# 节点

元素虽然有很多派生,但本质上可以分为两类——3D节点和非3D节点(简称节点)。节点Node对应的标签是xr-node,它是场景中用于渲染的元素的基础,本质上是在基础元素的基础上默认添加了Transform变换组件

# 变换组件

变换组件提供了渲染最基础的位置position、旋转rotation和缩放scale能力,原理上来讲,整个3D场景的渲染就是由很多个变换组件的级联实现的,最终构成一颗场景节点树。

要使用变换组件,我们有两种方式,一种是直接作为组件使用:

<xr-element transform="position: 1 1 1;nodeId=node1">
  <xr-element transform="position: 1 2 1"></xr-element>
</xr-element>

这个例子中,我们创建了两个带有变换组件的元素,并将它们级联了起来。而通过使用节点元素,我们可以将其等价地写成:

<xr-node node-id="node1" position="1 1 1">
  <xr-node position="1 2 1"></xr-node>
</xr-node>

可见,节点元素其实就是将变换组件的属性做了一下映射。我们还可以注意到nodeId这个数据,它和元素的id不同,是专门给变换组件用的一个索引。在组件数据解析一章最后,有一种transform类型的内置数据类型的值,就是这个nodeId。此外,我们还可用以下方法来手动获取变换组件的引用:

const trs = scene.getNodeById(nodeId);

// 修改位置
trs.position.x = 10;
trs.position.setValue(10, 10, 10);
trs.position.setFromArray([1, 1, 1]);
trs.position.set(new xrFrameSystem.Vector3());

// 欧拉角、缩放、四元素也是类似的
trs.rotation.y = 10;
trs.scale.z = 2;
trs.quaternion.w = 1;

更多属性请看API文档。

# 派生

节点作为一类特殊的元素的基础,很多元素都是派生自它的,比如灯光、相机等等,所以它的默认组件集和数据映射表非常常见,一般来讲要派生自节点,我们需要这么做:

import XrFrame from 'XrFrame';
const xrFrameSystem = wx.getXrFrameSystem();

const CustomDefaultComponents: XrFrame.IEntityComponents = Object.assign({
  'custom-component': {
    customData: value
  }
}, xrFrameSystem.NodeDefaultComponents);

const CustomStarDataMapping: {[key: string]: string[];} = Object.assign({
  'custom-data': ['custom-component', 'customData']
}, xrFrameSystem.NodeDataMapping);

xrFrameSystem.registerElement('custom', class XRCustom extends xrFrameSystem.Element {
  public readonly defaultComponents: XrFrame.IEntityComponents = CustomStarDefaultComponents;
  public readonly dataMapping: {[key: string]: string[];} = CustomStarDataMapping;
});

关键在于对xrFrameSystem.NodeDefaultComponentsxrFrameSystem.NodeDataMapping的合并。

# 可见性与图层

对于节点和派生自节点的元素(也就是挂载了Transform组件的元素),都有visiblelayer两个属性可选。这两个属性用于控制节点自身以及其子孙结点的可见性

当我们需要需要一个开关直截了当得让某个节点之下的所有模型或者灯光都显示或者不显示,最方便的方法就是使用visible

<xr-node visible="false">
  <xr-mesh visible geometry="cube" />
</xr-node>

比如这个例子,所有节点的visible默认都是true,但当父节点设置为false时,即便子节点显式定义了true,也会被隐藏。

除了这种场景,在另外一些场景我们还会有更复杂的需求,比如只是想针对性得剔除掉某一类模型或是灯光,而非按照层级结构,这时候图层layer就派上用场了,让我么来看个例子:

<xr-node layer="1">
  <xr-mesh geometry="sphere" uniforms="u_baseColorFactor:0.937 0.176 0.368 1" />
  <xr-node layer="2">
    <xr-mesh geometry="cylinder" uniforms="u_baseColorFactor:1 0.776 0.364 1" />
  </xr-node>
</xr-node>
<xr-camera
  position="0 1.6 0" clear-color="0.925 0.925 0.925 1"
  cull-mask="0b01"
/>

对于两个xr-node节点,我们给第一层指定了layer=1,第二层指定了layer=2,然后将xr-camere节点的cull-mask属性(对应于Camera组件的cullMask数据)设置为0b01。如此设置的效果应该是第一个mesh被渲染,第二个不被渲染。

开发者看到这里应该不难发现图层的规则:layer可以设置1~32共32个值,而相机的cullMask是一个32位无符号整数,每一位都代表一个layer。只有当一个节点从顶层到自身路径中的所有节点都通过了这个mask的测试,才会被显示出来。这也适用于灯光Light组件。