关于tuo-scene中使用绑定3D点的说明

所谓绑定3D点是指一个点具备独立的坐标值,但时它在实际应用的时候,我们需要把它看成是在某个相对坐标系下的坐标。这种应用场景出现在TuoObject2D中计算投射2D点时引用了某个3D对象上的某个点的情况。

TuoObject2D对象不能像原生的three.js的Object3D那样直接作为一个子对象添加到3D场景中去,但有时候我们却需要在计算其坐标时把它当作某个Object3D对象的子对象。在这种场景下,我提出一个解决方案,就是给创建TuoObject2D对象时应用的3D点point添加一个_bindObject对象。到要实际应用point的时候,采取如下方法获取point的真实坐标值:

let absPoint = new THREE.Vector3()
if(point._buildObject)
  absPoint.copy(point).applyMatrix4(point._buildObject.matrixWorld)

tuo-scene的viewSpecifyMap特征

tuo-scene是一个单场景多视图的架构,在有一些特殊场景下,我们需要添加一个指定视图的对象。即该对象虽然被添加进入了场景,但是该对象只在指定的一个视图中渲染。这样的对象在CoordTransform和RefTransform插件中都有应用。

为了支持此类型的对象,我对TuoScene的add函数做了一些修改。其原型改为如下的形式:

TuoScene.add(object, viewName)

其中第2个参数为视图的名称。目前在使用视图指定对象时还需要注意以下几个条件。

  1. 此对象必须是一个3D对象(目前viewSpecifyMap暂不支持TuoObject2D)
  2. 此对象的可视性不允许外部修改,否则可能会导致该对象的可视性不符合预期效果。

tuo-scene的TuoObject2D对象

由于之前对3维附属2维对象和3维附属DOM对象的设计不太满意,由其是面向单场景多视图的架构时,会导致构造2维渲染的逻辑特别复杂。因此,我们构造了TuoObject2D对像来实现tuo-scene系统的2维渲染。在整个系统中,TuoObject2D是与Object3D平等的存在,TuoObject2D对象也能够独立的加入到TuoScene场景中。即通过TuoScene.add和TuoScene.remove来添加和移除。

每一个TuoObject2D对象必须实现一个自己的create函数,此函数返回一个如下架构的对象:

{
twoObject: Object, //two.js对象
dom: Object, //dom元素
project2D: Object, //TuoFunctionNode对象
}

TuoFunctionNode对象的apply函数是非常重要的,在这个函数中必须做2个事情,第一更新相应towObject中的坐标数据;第二,更新dom的位置。

create函数接收1个参数:view。这个参数由当前场景拥有的各个视图来传递给它。

TuoScene对TuoObject2D对象的反应

TuoScene只需要在两个场合下对TuoObject2D对象作出反应即可:

  1. 调用TuoScene.add函数添加TuoObject2D对象时,TuoScene会把当前的TuoObject2D对象存如object2DSet中。并发布如下格式的消息
    {
    type: 'tuoObject2DAdded',
    payload: {
    object: TuoObject2D
    }
    }
  2. 调用TuoScene.remove函数移除TuoObject2D对象时,TuoScene会把指定的TuoObject2D对象从object2DSet中移除。并对该对象调用TuoObject2D.dispose()函数。

TuoView对TuoObject2D对象的反应

TuoView需要在3个场合下对TuoObject2D对象作出反应

  1. TuoView被创建后第一次调用render函数做渲染时。此时TuoView需要检查相关TuoScene中的object2DSet是否为空,如果不为空,则需要对每一个TuoObject2D对象调用wrapCreate函数,获取create函数的返回值,并将返回值的数据,按对应关系,添加到自己的twoScene, controlDiv和project2DRoot中去。
  2. 当收到相对应的TuoScene发布的tuoObject2DAdded消息时,应该调用该消息中TuoObject2D的wrapCreate函数,获取create函数的返回值,并将返回值的数据,按对应关系,添加到自己的twoScene, controlDiv和project2DRoot中去。
  3. 在自己的每一次render函数渲染中,需要按以下顺序分别执行渲染运算:1. 先调用一遍所有project2DRoot的子元素的apply函数;2. 渲染twoScene;

tuo-scene的3维附属DOM对象设计

此方案已经废弃,转为TuoObject2D的方案

tuo-scene将会支持更好的dom对象的应用,以此来增强2D和3D的交互能力。3D对象的附属DOM实现步骤如下:

3D对象嵌入DOM的创建方法

在3D对象的userData中添加如下的结构

userData: {
domCreator: Function
}

domCreator函数接收一个dom元素作为parent,在domCreator中,必须将新创建的dom元素添加到该参数的dom元素中去,并对新创建的dom的样式和回调函数都配置妥当。需要注意的是,dom元素在创建时候的一些数据可以与2D或3D的一些坐标相关联。由其注意,此函数还需要将新创建的dom元素返回。

TuoScene对domCreator的处理

TuoScene在接收到满足以上条件的3D对象时会做以下处理:

  1. 将接收到的domCreator函数放入domCreatorMap中去,此map以3D对象的uuid为key,以domCreator函数为value
  2. 发布'domAdded'消息,此消息的具体结构如下:{type: 'domAdded', payload: {id: uuid}}。消息中payload的id属性为3D对象的uuid
  3. 给3D对象添加'dispose'消息的监听函数,在该监听函数内负责移除domCreatorMap中的相对应数据,并向TuoScene发布'domRemoved'消息。此消息的具体结构如下:{type: 'domRemoved', payload: {id: uuid}}

TuoView对domCreator的处理

对于TuoView而言,主要是处理两个时机。一个是调用domCreator创建dom元素的时机,一个是移除dom元素的时机。

  1. 创建dom元素。TuoView会监听TuoScene的'domAdded'事件,当监听到此事件时,立即给自己的needsAddDomSet中添加消息中的uuid。然后,在下一次的render函数调用时,会检测needsAddDomSet中的uuid,并根据uuid来调用scene中domCreatorMap所保存的domCreator函数来创建dom。并将dom对象保存到domMap中去。
  2. 移除dom元素。相比于创建dom元素,移除就要简单一些。TuoView会监听TuoScene的'domRemoved'事件,当监听到此事件时,立即从domMap中检索到相对应的dom,并从ui和domMap中移除此dom的所有信息。

tuo-scene的3维附属2维对象设计

此方案已经废弃,转为TuoObject2D的方案

tuo-scene相对于tuo-three2的一个重要改变,就是更好的去支持单场景多视图的结构下,2D和3D的混合渲染。tuo-scene以two.js为基础来描述2D对象,每一个TuoView的渲染函数在渲染3D对象以外,还专门渲染2D对象。而在我们的应用中,很多2D对象是依赖于3D坐标而渲染的,因此tuo-scene设计了project投射机制,用于在渲染时,针对每个视图来单独做投射运算。

3维附属的2维对象的渲染实现步骤如下:

3D对象的数据中插入2D对象数据

附带了2D对象的3D对象的userData结构如下:

userData = {
twoObject: Object2D, //object of two.js
project2D: Function
}

其中,Object2D是two.js的对象,project2D函数每执行以此都会对Object2D对象内部的坐标信息更新。用户必须很好的来实现这个project2D函数。

TuoScene对附属2D对象数据的处理

当TuoScene接到一个满足上述条件的3D对象时,它会做以下4件事情:

  1. 把3D对象添加到threeScene中去
  2. 把2D对象(Object2D)添加到twoScene中去
  3. 把project2D对象添加到project2DMap中去,此map以3D对象的uuid为key,以project2D对象为value
  4. 给3D对象添加一个监听'dispose'事件的函数,在这个函数中,将会从twoScene和project2DMap中把该3D对象对应的2D对象和project2D函数移除。

tuo-scene对摄像头的重新封装TuoCamera

一般的3d渲染引擎只关注摄像头比较基本和纯粹的物理参数,在实用时稍显得有些麻烦。tuo-scene工程考虑到多个应用案例,对摄像头进行的重新封装。封装后的TuoCamera主要有以下特点:

  1. 支持更方便的镜头切换,一个TuoCamera对象支持切换摄像头类型“Perspective|Orthographic”,在切换时因为替换调了底层摄像头,而触发底层摄像头发布的dispose事件的发布。(这里要注意,发布dispose消息的是底层的camera,如果有动画依赖需要监听依赖的,需要监听底层camera的依赖)
  2. 重新定义了camera的位姿基本元素,由camera的position,target和up来定义。这里的up指的是摄像头的上方,target指的是镜头对准的目标,position为镜头的位置。其中,若无特殊指定,up的绝大多数情况都由y方向(0,1,0)和position与target来计算精确值,特殊情况,例如position和target正好与y方向平行时,则以z或者-z方向来确定up的精确值。如果有特殊指定,则按特殊指定方向和position与target的值来确定up的精确值。

其中,采用2的方式来定义基本元素后,如需自动调整镜头,可以通过调整target和position为主,特殊情况下,可以指定up的模糊方向来确定up的精确方向。这样带来的好处是,可以比较无缝的在自动与手动控制镜头之间切换。因为像trackball和orbit之类的手动控制镜头的插件,都需要有一个target来配合。

tuo-scene的插件类别

tuo-scene的插件分两种类别,一种是基于视图的插件ViewPlugin,一种是基于场景的插件ScenePlugin。

ViewPlugin是属于独立视图的插件,它基本上只访问视图的资源,例如图层,摄像头等。而且ViewPlugin必须实现clone函数,以此来满足在做视图的克隆操作时,能把插件也克隆过去。

ScenePlugin是属于场景的插件,它不仅仅能访问场景的api,还能访问视图的api。我们知道tuo-scene是一场景,多视图的结构。ScenePlugin访问视图api时,会做到对所有视图都一视同仁。

tuo-scene的配置系统

tuo-scene的配置系统将支持对场景中各类典型对象的配置,增加和删除能力。除此以外,可配置的东西还包括各类插件的参数配置。这里,可以配置的参数应该都是可实时配置的参数。

tuo-scene的插件应该提供满足统一的配置接口的函数,以供配置交互时统一调用。此接口继承自tuo-three2项目,采用一样的方式。

核心组件支持的配置操作

1.场景配置

  1. 场景的背景颜色
  2. 场景的环境灯光,这里泛指父结点为scene的灯光。对灯光的配置要支持添加,移除和修改参数。
  3. 摄像头的配置,这里指的是tuo-scene环境下的摄像头。摄像头的配置支持添加,移除和修改参数。

2. 视图的配置

  1. 支持与视图所绑定的摄像头的选择。

tuo-scene核心组件的概念

场景的概念

  1. 场景负责管理对象和视图
  2. 一个场景可以拥有多个视图
  3. 场景中的对象包括3D对象,依赖3D对象的2D对象和依赖3D对象的dom组件。

视图的概念

  1. 具备图层的概念。一个视图是由多个canvas图层叠加在一起呈现。
  2. 一个视图只能属于一个场景。
  3. 一个视图必须绑定一个摄像头。
  4. 视图至少具备2个图层,最低层3D渲染图层,紧跟着的2D渲染图层。有的渲染效果的插件可能会在2D渲染图层之上再添加图层。
  5. 在所有的渲染图层的上面,有一个控制层的存在,用于接受各种手动的输入事件(鼠标事件,触屏事件)。
  6. 视图的渲染函数中将执行依赖解析算法,以此来引导two.js正确的渲染依赖3D对象的2D对象;以及引导浏览器来正确的渲染依赖3D对象的dom组件。

摄像头的概念

  1. 摄像头属于场景中特殊的3D对象,为了满足更高的可配置性要求,tuo-scene对原始的three.js的camera做了一层封装。允许更方便的配置摄像头类型,摄像头参数,摄像头灯光,摄像头helper等。

摄像头手动控制插件的概念

  1. 摄像头插件和普通的插件一样,属于管理控制效果的插件,但由于摄像头是场景中很重要的对象,因此把摄像头的手动控制插件定义为tuo-scene的核心组件。