注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Mississippi的博客

学习使人进步

 
 
 

日志

 
 

WOW引擎与Alternativa3D  

2009-10-12 00:16:25|  分类: FLEX学习 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

WOW是一个纯数据结构的物理引擎,用于模拟实现世界的真实运动(重力、弹力、摩擦力等)。由于它是纯数据结构的(非可视对象),所以它可以使用在任何3D引擎中,通过这个数据化的虚拟系统来计算各种物体的位置和旋转值,然后将几何变换值赋予3D引擎中的显示对象,从而实现了物理学运动模拟。

WOW能与绝大部分3D引擎配合使用,如AWAY3D等。本文主要说明在Alternativa3D中的使用方法。

WOW需要两个类库:“de”和“fr”。de提供了必须的数据结构,fr包含WOW的主类。

 

基本用法

要想在3D场景中实现物理学运动,首先必须创建一个虚拟的WOW系统(所谓虚拟,是指WOW及其子对象都是非可视的)。如:

var mywow:WOWEngine;

mywow = new WOWEngine(0.3);//创建引擎;

mywow.collisionResponseMode = mywow.SELECTIVE;//碰撞模式;

mywow.addMasslessForce(new WVector(0, 0, -50));//设置一个无质量的力;用它可模拟重力。

注意:WOW的坐标系统与Alternativa3D相同。上面那个new WVector(0, 0, -50)表示重力方向为指向Z轴负向。

然后,再创建一个WOW地平面(是一个虚拟的无限大平面)——用于模拟实现环境中的地面,如:

var ground:WOWPlane = new WOWPlane(0,0,0,-90,0,0); //右手系确定旋转

ground.elasticity = 0.6;//弹力系数

ground.friction = 0.3;//摩擦力系数;

mywow.addParticle(ground);//添加进WOW系统;

注意:默认创建的地平面(new WOWPlane(0,0,0)),位于Alternativa3DXZ平面,法线为(0,-1,0)——指向Y轴负向,如图:

WOW引擎与Alternativa3D - mississi - mississi的博客

因此,如果希望WOW平面与3D场景的地平面匹配的话,需要旋转WOWPlane。——WOW系统的旋转遵循右手法则,为了使法线指向Z轴正向,则应该在X轴上旋转-90度,故:在创建WOW平面时,用new WOWPlane(0,0,0,-90,0,0)

这样,创建的平面如下图:

 WOW引擎与Alternativa3D - mississi - mississi的博客


下面,创建一个虚拟测试球:

var wowSphere:WSphere;

wowSphere = new WSphere(0,0,1000,200,false,0.1,0.5,0.02);

//参数解释:0,0,1000坐标,将球放在地平面的上面1000处;200是球半径;false是固定与否标记,0.1是质量;0.5是弹力(0-1之间,0无弹力,1成为永动机),0.02是摩擦力。——用于表现物体的质地。

然后。

mywow.addParticle(wowSphere);//将测试球添加进WOW系统。

OK,最后在帧事件中(EnterFrame),添加进WOW处理过程:

mywow.step();//每帧进行运算,则测试球会按设置的属性进行真实的物理运动。

 

由于WOW仅仅是在数值上计算了物体的运动和碰撞,却无法观察到,因此,需要将该计算结果链接到(赋值给)3D场景中的可视对象,并且让这些可视对象每帧都更新其几何变换(位置,旋转)。如:

预先定义一个可视球:

sphere = new Sphere(radius);

sphere.cloneMaterialToAllSurfaces(new WireMaterial(1,0xff0000));

mis3d.scene.root.addChild(sphere);

然后在帧事件中,更新这个球的位置:

sphere.coords = new Point3D(wowSphere.px,wowSphere.py,wowSphere.pz);

则该球即可表现出合理的物理运动。

 

 

弹性约束

在两个质点(或者W球)之间设置一种弹性关联(约束),使它们能保持一定的距离。如果为一个平面网格的相邻顶点两两设置了约束(横向和纵向),则可表现织物(如悬挂的毛巾)。代码如下(假设已经有3D场景和WOW):

 

先定义两个常量及一些公共变量:

private const _PLANSEGMENTW:int = 4;

private const _PLANSEGMENTH:int = 4;

private var pmt:alternativa.types.Matrix3D;

private var ipmt:alternativa.types.Matrix3D;

private var pv:Array;//顶点数组

private var parr:Array;//质点数组

 

 

然后:

var p:Plane = new Plane(1000,1000,_PLANSEGMENTW, _PLANSEGMENTH);//创建一个3D平面网格;

p.cloneMaterialToAllSurfaces(new WireMaterial(1,0xff0000));                       

mis3d.scene.root.addChild(p);

p.coords = new Point3D(0,0,2000);//上升一个高度,

p.rotationY = MathUtils.toRadian(45);

获得平面p的Matrix3D和它的逆矩阵:

pmt = p.transformation;

ipmt = alternativa.types.Matrix3D.inverseMatrix(pmt.clone());

//这两个矩阵是用来计算顶点位置的。(注意:Alternativa3D的vertices值并不随Mesh的几何变换而改变,因此要正确获得顶点位置,必须使用它的矩阵来计算)。

 

由于Plane的顶点ID号已经表示了其网格的纵横关系,如3_2表示第四行第三列的顶点,因此可方便地用此进行设置。下面为p网格每个顶点创建一个虚拟质点或者W球:

pv = [];

parr = [];

for(var i:int=0; i<=_PLANSEGMENTW; i++){//行

       for(var j:int=0; j<=_PLANSEGMENTH; j++){//列

              var v:Vertex = p.vertices[i+"_"+j]; // i+"_"+j是顶点ID;

              pv.push(p.vertices[i+"_"+j]);

             

              var newp:Point3D = new Point3D(v.x, v.y, v.z);

newp.transform(pmt);//必须变换,否则错位

var spt:WSphere = new WSphere(newp.x, newp.y, newp.z, 10, false, 0.1, 0.2, 1);//创建W球,也可使用质点(WParticle),但似乎质点不能检测碰撞。

parr.push(spt);

mywow.addParticle(spt);

       }

}

这样,为每个顶点关联了一个质点。以后,计算质点的物理运动状态,再改变相关联的顶点位置即可。

找到网格中心,然后固定它:

var center:int = (_PLANSEGMENTW + 1) * (_PLANSEGMENTH + 1) * 0.5;

(parr[center] as WSphere).fixed = true;

或者固定网格的边界:

for(var q:int=0;q<=_PLANSEGMENTW;q++){

       parr[q].fixed = true;

       parr[q + (_PLANSEGMENTW+1)*_PLANSEGMENTH ].fixed = true;

}

 

下面,设置两两相邻质点之间的弹性约束:

for(var k:int=0; k<parr.length-1; k++){

       //横向:

       var index:int = k % (_PLANSEGMENTW + 1);

       if(index<_PLANSEGMENTW){

              var scv:WSpringConstraint = new WSpringConstraint(parr[k], parr[k+1], 0.1);

              scv.massAffected = false;

              mywow.addConstraint(scv);

       }

                                  

       //纵向:

       if(k < (_PLANSEGMENTW + 1)*_PLANSEGMENTH){

              var sch:WSpringConstraint = new WSpringConstraint(parr[k], parr[k+_PLANSEGMENTW], 0.1);

              sch.massAffected = false;

              mywow.addConstraint(sch);

       }

}

OK,一切就绪。最后在在帧事件中,更新网格顶点的位置:

mywow.step();

for(var pi:int=0; pi<parr.length; pi++){                                  

       var cp:Point3D = new Point3D(parr[pi].px, parr[pi].py, parr[pi].pz);

       cp.transform(ipmt);//通过逆矩阵计算位置

 

       pv[pi].x = cp.x;

       pv[pi].y = cp.y;

       pv[pi].z = cp.z;

}

效果:

WOW引擎与Alternativa3D - mississi - mississi的博客   WOW引擎与Alternativa3D - mississi - mississi的博客

 

 

BoundArea范围域

一个由六个WOWPlane组成的BOX(所有法线均指向内部),可将物体限定在此区域内,如制作桌球游戏。

var ba:WBoundArea = new WBoundArea(1000,1500,2000);//创建一个长方体限定域;

注意它添加进WOW系统的方法:不能直接用mywow.addParticle(ba);这样添加无效。

必须:

var ps:Array = ba.getPlanes();//首先获得它的六个面;

for(var i:int=0; i<ps.length; i++){

       ps[i].elasticity = 0.1;//可以为每个面设置弹性系数

       mywow.addParticle(ps[i] as WOWPlane);//分别将平面添加进WOW系统;

}

 

它的外形如:

var box:Box = new Box(1000,1500,2000);//注意参数匹配位置

 

WPlane和WOWPlane

WPlane类用于对一个平面的数学描述,由四个参数创建(前三个定义平面的法线,第四个描述平面到原点的距离)。

WOWPlane的数据模型是WPlane,它内置4个顶点,构成一个正三角形及其质心。下图是在X轴旋转了-90度时的顶点位置:

WOW引擎与Alternativa3D - mississi - mississi的博客

 红色为全局坐标系(与Alternativa匹配),绿色为本地坐标系,蓝色为场景坐标系。法线为(0,0,1)——全局坐标系,指向Z轴正向。

 

Force力

WOW引擎支持两种力,普通力(Force)和无质量力(MasslessForce)。可通过以下方法设置力(这是全局力,所有WOW系统中的质点都会受影响):

mywow.addMasslessForce(new WVector(0,0,-200));//力的方向和大小,匹配全局坐标系(这个参数可用来模拟重力)。

或者给属性赋值,mywow.masslessForce= new WVector(0,0,-200);

注意上两用法的区别:前者力会累加,后者纯粹设置值。

如果要取消力,可用mywow.masslessForce= new WVector(0,0,0);

能否为不同的物体设置不同的力?

答案是可以。如:系统中有一个W球wowSphere,则用:

wowSphere.addForce(new WVector(500,0,0));

则W球会在X轴正向前进一段距离。注意,物体自身设置的力存在衰减(由于摩擦阻力)所以运动一段时间会停止,如果希望它再次运动,必须重新add一次。

此外,全局力和物体身体动力可以并存且同时产生影响。

还需注意,wowSphere.addForce是添加了一个有质量的力,因此,如果物体质量远大于力的话,可能物体不能运动。——如,质量为100的物体,至少需要5000以上的力才能推动。

 

WBox

由九个W球构成的刚性立方体(八个顶点球加一个质心球),所谓刚性是指碰撞后会恢复外形(不是不能变形,而是在弹性约束的作用下能恢复外形)。如:

wbox = new WBox(0,0,400,500, mywow);//创建一个W方体;

参数解释:0,0,400是方体中心的坐标(遵循全局坐标系)。500是尺寸,因有一个参数,似乎W方体只能创建正方体。mywow是当前的WOW系统。

WBox不需要额外使用ADD语句添进WOW系统,因为在传入的参数中包括了mywow,在创建时,它已经ADD进去了。

创建后,可从属性值中分别获得九个W球(可重新设置它们的属性,以及获得它们的位置),如下面用Alternatia的BOX来渲染WBOX:

wbox = new WBox(0,0,400,500, mywow);//创建一个W方体;

var cs:WSphere = wbox.centerSphere;//获得质心球;

var c:Sphere = new Sphere(50);

c.cloneMaterialToAllSurfaces(new WireMaterial(1,0x0000ff));

c.coords = new Point3D(cs.position.x, cs. position.y, cs.position.z);

mis3d.scene.root.addChild(c);

 

var match:Array = ["0_1_0","1_1_0","1_0_0","0_0_0","0_1_1","1_1_1","1_0_1","0_0_1"];//BOX顶点与WBOX匹配;

var box:Box = new Box();//创建一个渲染BOX

box.cloneMaterialToAllSurfaces(new WireMaterial(1,0xff0000));

mis3d.scene.root.addChild(box);

 

var mm:int=0;

for(var i:int=0;i<2;i++){

       for(var j:int=0;j<4;j++){

              var posi:WVector = wbox["coin"+i+"_"+j].position;//获得八个顶点球

              box.vertices[match[mm]].x = posi.x;

              box.vertices[match[mm]].y = posi.y;

              box.vertices[match[mm]].z = posi.z;

              mm++;                                    

       }

}

以后,在帧事件处理中,更新网格顶点位置,即可看到BOX的运动效果。

 

质心球可用来移动整个BOX,两种方法,一是给质心球施加一个外力(推荐);二是直接将质心球移动到需要的位置。 注意,默认创建时,W球的质量都是0.5,太轻会出现物体颤动不止的现象,可以在创建后重新设置该值。

通常,用WBOX来模拟计算移动的交通工具如车辆坦克等。——可以获得位置和跟随地面起伏的旋转状态(WBOX不能通过代码修改旋转,仅有springRotation属性能获得约束的旋转值(只读)。可通过此值来计算物体的旋转状态)。 

WOW引擎与Alternativa3D - mississi - mississi的博客

 

 

使用WOWPolygon构建地形

WOWPloygon可以创建自定义的地面。由三个顶点创建表面,顶点次序决定法线方向(顺时针),而法线方向决定是否碰撞。如:

poly = new WOWPolygon(v1,v2,v3);

WOW引擎与Alternativa3D - mississi - mississi的博客

每个polygon可分别设置属性(弹性、摩擦等)。可以创建离散的polygon。尽量避免出现尖锐的顶点(否则会有意外的结果)。尽量保证物体的运动在polygons区域内(避免出现无限下落情形)

通常导入3DS作为地形模型。以下为代码示例:

首先在C4D等3D软件中制作一个地形网格,导出保存为一个3DS文件。然后,

var load:Loader3DS = new Loader3DS();

load.addEventListener(Event.COMPLETE, loaded);

load.load("assets/wowTest.3ds");

在模型加载完成处理中:

private function loaded(event:Event):void{

         mesh3DS = new Mesh();

         for (var o:* in event.target.content.children) {

                  //weldVerticesAndFaces(o);//不能焊接优化,否则三角面可能会变成多边形面;以保证每个面的顶点数为3,WOW只支持三角面;

                  mesh3DS = MeshUtils.uniteMeshes(mesh3DS, o);//用Mesh工具合并分离的mesh;

}

//将获得的mesh赋予材质及其他调整,并且显示在场景中;

mesh3DS.cloneMaterialToAllSurfaces(new WireMaterial(1,0x0000ff));

mesh3DS.coords = new Point3D(0,0,-200);//通常在C4D中确定网格位置,但也可在此调整;

mesh3DS.rotationX = MathUtils.toRadian(-5);//旋转一下,如果需要的话;                               

mis3d.scene.root.addChild(mesh3DS);//添加进场景;

 

//接下来,依据3DS的网格顶点,构建WOWPloygon;

for each(var f:Face in mesh3DS.faces){//找出mesh的每个面;

         if(f.verticesCount == 3){//保证顶点数为3;

                  var vps:Array = [];

                  for each(var v:Vertex in f.vertices){//找出该面的每个顶点;

                           var vp:Point3D = new Point3D(v.x, v.y, v.z);

                           vp.transform(mesh3DS.transformation);//注意,顶点位置值不随网格几何变换而改变,因此需要用Matrix3D进行正确定位;

                           vps.push(vp);//获得顶点数组;

}

         }

 

//构建WOW顶点;

var v0:WVertex = new WVertex(vps[0].x, vps[0].y, vps[0].z);

var v1:WVertex = new WVertex(vps[1].x, vps[1].y, vps[1].z);

var v2:WVertex = new WVertex(vps[2].x, vps[2].y, vps[2].z);

 

var poly:WOWPolygon = new WOWPolygon(v2,v1,v0);//创建WOWPolygon,注意参数的次序,与Alternativa3D的次序正好相反。

 

//设置面的WOW属性;

poly.fixed = true;

poly.collidable = true;

poly.elasticity = 0.2;

poly.friction = 0.8;

 

mywow.addParticle(poly);//加进WOW系统;

}

 

//至此,将3DS网格转换为WOW网格已经完成;

//如果想,可放置一个W球进行测试;

}

WOW引擎与Alternativa3D - mississi - mississi的博客

  评论这张
 
阅读(2185)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017