tuo-fair-core参数调整器数据结构说明

1.基本数据结构

{
   [key]: {
      type: String, // 类型
      value: Any,  // 当前的值
      defaultValue: Any, // 默认值
      label: String, // 展示在界面上的参数名称
      options: Object, // 其他属性
   }
}

2. 具体参数调整器的options说明

2.1 数字输入类

{
  type: 'adjustorNumber',
  value: Number,
  defaultValue: Number,
  label: String,
  options: {
      precision: Number, // 小数点精度,表示保留几位小数
      step: Number, // 点击梯度变化按钮时的增减梯度,是正数
      hideStep: Boolean, // 是否隐藏梯度变化按钮,默认是false
      min: Number, // 最小值
      max: Number // 最大值
  }
}

2.2 图形选择枚举对象类

这类参数是指用一张图片来示意此参数值达成的效果,然后用一个对象来作为这个参数的实际值。此类参数主要用在需要用图片才能表达参数值的效果的情况。例如,用来调整材质的参数,这时候就需要提供一张图片来表示当前参数值能达成的渲染效果。此类参数的数据结构如下:

{
  type: 'imageEnumObject',
  value: Number,
  defaultValue: Number,
  label: String,
  options: {
      images: [<url_to_images>, ...]
      objects: [Any,...]
  }
}

2.3 图片输入类

{
   type: 'imageInput',
   value:  String, // 图片的 url
   defaultValue: String, // 图片的 url
   label: String,
   options: {
     width: Number,
     height: Number,
     aspect: Number, //optional
     texture: {
       wrapT: oneOf(['RepeatWrapping', 'MirroredRepeatWrapping', 'ClampToEdgeWrapping' ]), //default: 'ClampToEdgeWrapping',
       wrapS: oneOf(['RepeatWrapping', 'MirroredRepeatWrapping', 'ClampToEdgeWrapping' ]), //default: 'ClampToEdgeWrapping'
     }
   }
 }

图片输入类参数options中的aspect如果存在的,则表示输入的图片需要先进行一步变形。这里的aspect值是指uv值应用到3维对象上后,3维空间上u的长度比上v的长度的比值。例如,下面杯子所示:

如果杯子外壁上的uv展开值是铺满的,那么这里的aspect就应该满足如下的情况:

aspect = 外壁的周长 / 外壁的高度

2.4 一般选择类

{
  type: 'enum',
  value: ,
  defaultValue: ,
  label: String,
  options: {
     mutiple: Boolean, // 是否多选
     options: ArrayOf(String) | ArrayOf({lable: String, value: String}), // 若是String数组,则每个元素都必须唯一;若是Object数组则每个元素的value必须唯一
  }
}

2.5 人像选择类

{
   type: 'faceEnum',
   value:  String || null, // face3d的id
   defaultValue: null,
   label: String
 }

tuo-ui数据结构说明

以下是基本数据结构说明,具体实现时可能会根据实际需要增加一些属性。

1.字符输入类

{

value: String, // 默认初始值为‘’

onChange: (value) => {},

}

2.数字输入类

{

value: Number | undefined, // 默认初始值为undefined

onChange: (value) => {},

precision: Number, // 小数点精度,表示保留几位小数

step: Number, // 点击梯度变化按钮时的增减梯度,是正数

hideStep: Boolean, // 是否隐藏梯度变化按钮,默认是false

}

3.选项(Option)

{

value: String, // 必须唯一

title: String,

…...

}

或者

Option: String // 每个选项必须唯一

4.一般选择类

{

value: String,

onChange: (value, option: Option) => {}, // 所选值发生变化时调用

onSelect: (option: Option) => {}, // 进行选择操作时,调用(值不一定变化)

}

4.弹窗式选择类

{

visible: Boolean, // 弹窗是否可见

value: Any,

onClose: () => {}, // 点击关闭时调用

onOk: (value) => {}, // 点击确定时调用

onCancel: () => {} // 点击取消时调用

}

5.其他

5.1 adjustorNumber

{

value: Number,

onChange: (value) => {},

name: String,

min: Number,

max: Number

step: Number,

type: EnumOf(['continuous', 'discrete']),

couple: Boolean

}

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-fair的阻塞概念

tuo-fair的阻塞源自于tuo-fair的异步计算的引用。在执行一些动作前,我们可能希望该动作发生在异步计算之后,因此而引入了阻塞的概念。目前阻塞主要用在动画的执行过程中。动画步骤的执行顺序有时需要执行在异步计算完成之前或者之后。

tuo-fair-core的document.js里面创立了两个函数:wrapBlockBefore和wrapBlockAfter。这两个函数都是接受tuo-engine的控制单元为输入,然后在输出一个tuo-engine的控制单元。wrapBlockBefore函数输出的控制单元能确保输入的控制单元执行之前异步运算已经结束,也就是阻塞在执行单元之前。相反wrapBlockAfter是能够确保阻塞在执行单元之后。

用户添加动画时,可以通过调用addAnimator函数时,传递第二个参数来控制此执行单元是否要封装成阻塞体。addAnimator的第2个参数为undefined时没有阻塞,'before'和'after'分别对应阻塞在控制单元之前还是之后。

阻塞的应用除了用户在调用addAnimator函数时可以手动控制之外,还应用在启动动画时的自动应用。例如在启动动画控制的时候,tuo-fair-core会给刚刚启动的那个控制单元封装成阻塞在控制单元之前的阻塞体。

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时,会做到对所有视图都一视同仁。