- 最后登录
- 2013-9-29
- 注册时间
- 2012-8-20
- 阅读权限
- 90
- 积分
- 6371
- 纳金币
- 6372
- 精华
- 0
|
项目的基本技术要求,就是能捕捉用户在舞台上点击的3D坐标。
首先这涉及侦听MouseDown事件,如果Scene来侦听,当舞台上没东西时,是不会触发鼠标事件的,因此只能用Stage来侦听,而stage侦听的鼠标坐标是舞台的相对坐标,这里就需要一系列换算。
基本思路就是:将舞台坐标先换算为View3D的坐标,再通过Camera与该点的连线与坐标平面相交来计算点。
舞台坐标换View坐标可以以View的MouseX,MouseY属性为参数,调用Camera的unproject来计算。计算相交这部分代码不用自己写,Away3D的Plane3D对象封装了相关函数。因此代码很简洁:
?
1
private function onmd(e:MouseEvent):void<BR> {<BR> var pllane3D = new Plane3D();<BR> pl.fromNormalAndPoint(new Vector3D(0, 0, -1), new Vector3D());<BR> var v:Vector3D = camera.unproject(view.mouseX, view.mouseY)<BR> v=v.add(camera.position);<BR> var p:Vector3D = pl.getIntersectionLineNumbers(v, camera.position);<BR> }<BR>
pl.fromNormalAndPoint是创建一个虚拟平面,第一个参数是平面法向,(0,0,-1)就是面向屏幕的面,第二个参数是平面上一点,直接默认在零点即可。
camera.unproject是调用Camera对象的投影方法得到一个坐标值,但这个值是相对于Camera的坐标,所以再加上Camera的坐标就成了绝对坐标。
pl.getIntersectionLineNumbers,函数名字有点长,参数是两个点,两点确定一条直线,返回的就是虚拟平面上的点了。
还有一种简单粗暴但有效的办法确定点,那就是创建一个真实的平面做坐标平面,然后监听平面的MouseDown事件,通过事件参数里的SceneX,SceneY,SceneZ来得到空间坐标,这种办法绝对不会错,唯一的问题就是如果平面被别的物体挡住了,MouseDown事件就失效了。
通过实验比较发现,这两种办法确定下来的点的坐标很相似,但后者正好比前者的坐标大了整1.2倍。造成这个诡异误差的原因不明,所以只好手工把前者产生的坐标扩大1.2倍使用了。
?
1
p.scaleBy(1.2);<BR>
然后我试图从坐标原点画一条线到鼠标点击位置:
?
1
var lineSegment = new LineSegment();<BR>l.end = new Vertex(0,0,p.z);<BR>l.start = new Vertex(p.x, p.y,p.z);<BR>l.material = new WireframeMaterial(0xff0000);<BR>scene.addChild(l);<BR>
问题更诡异了,终点居然比我鼠标点的位置还要长,重新检查两种方法得到的坐标值,发现那个倍数已经变成了1.15。
为了确认那个倍数到底是不是常数,于是我调整Camera位置,focus,zoom值继续实验。发现引起误差的原因来自于focus值,解决办法是让focus无限接近0,为了弥补focus过小导致物体太远,再把zoom放大相应倍数。Away3D里focus默认是100,zoom默认是10。这里我设focus为0.1,zoom就是10000。
在实际应用中,要获得某个物体的某个面上的点除了用传统的监听MouseDown外,也可以利用直线和面交点的办法,在创建Plane3D对象时,将物体上某个面的法向量和一个点作为参数。法向量和点用如下方法获取:
?
1
2
view.hitManager.elementVO as FaceVO).face.normal;
view.hitManager.elementVO as FaceVO).face.v0.position;
View.hitManage用于获取当前点击的一系列属性,类似于MouseEvent3D对象,用于非MouseEvent3D事件。上面两行代码第一行获取法向量,第二行获取被点击面上的一个点,v0是vertexs类,position是vector3D类。 |
|