Cesium 实战教程 - 三种方式(CZML、nodeTransformations)修改模型节点组件属性(比例、旋转、移动等)
- 核心代码
- 完整代码
- 在线示例
关于 Cesium 设置模型组件的动作,之前是通过 CZML + articulations
来实现的,最近在官网发现一个示例,可以直接操作模型的 node
节点,来实现模型组件的动作。
本文介绍几种给模型组件设置动作的方法,包括 CZML articulations
、CZML nodeTransformations
以及代码(model.nodeTransformations
) 实现模型动作。
三种设置方式,效果是一致的,但是操作难度略有差别。
CZML articulations 需要先给模型添加自定义的关节动作,才可以通过代码来实现动作。
CZML nodeTransformations 相对比较方便,只需要知道节点(node)名称就可以实现动作,只是设置参数略有不同。
model.nodeTransformations 完全是由代码来实现的,比较自由,但是需要对相关代码比较熟悉。
三种方式各有优势,使用哪种,取决于项目需求。
本文包含核心代码、完整代码以及在线示例三部分。
核心代码
1. CZML articulations 关节动作
// 自定义动作,需要模型存在自定义关节属性
"articulations": {
// 导弹组件向前移动
// MoveZ 为 glTF 中自定义的关节名称
"missiles MoveZ": {
// 开始移动时刻
"epoch": "2023-06-14T10:10:00Z",
// 设置移动参数
"number": [
// 当前 epoch 时刻,第 0 秒的时,Z 轴移动的距离(米)
0, 0,
// 当前 epoch 时刻,第 300 秒的时,Z 轴移动的距离(米)
// 与模型行进方向相反
300, -60
]
},
}
详情请参考:Cesium 实战 - AGI_articulations 扩展:模型自定义关节动作
2. CZML nodeTransformations 实现节点动作
// 设置火焰显示隐藏
"nodeTransformations": {
"SRB_Flame4": {
// 旋转(按照模型设置的中心点)
"scale": {
"epoch": "2023-06-14T10:00:20Z",
// 设置时刻与比例
// 0 比例表示隐藏,1 比例表示显示
"cartesian": [
// 0 秒、x 比例、y 比例、z 比例
0.0, 0, 0, 0,
// 5 秒、x 比例、y 比例、z 比例
5.0, 1.0, 1.0, 1.0,
800.0, 1.0, 1.0, 1.0,
1600.0, 1.0, 1.0, 1.0,
],
// nextTime: 500,
},
},
}
2. model.nodeTransformations 代码实现动作
// 获取模型实体
const entity = dataSource.entities.getById("Vulcan");
// 模型视角跟随
viewer.trackedEntity = entity;
console.log(entity.model.nodeTransformations);
// 定义比例节点转换,用于显示隐藏
const transformation = new Cesium.NodeTransformationProperty({
scale: new Cesium.Cartesian3(1.0, 1.0, 1.0)
});
// 记录当前时间
const current = viewer.clock.currentTime.secondsOfDay;
// 模型实体添加节点控制
entity.model.nodeTransformations.addProperty('SRB_Flame2', transformation);
// 时间轴监听事件
let onTickEvent;
// 时间轴监听回调
function adjust() {
// 200 秒后隐藏 SRB_Flame2 组件
if (viewer.clock.currentTime.secondsOfDay > current + 200) {
const transformation = new Cesium.NodeTransformationProperty({
scale: new Cesium.Cartesian3(0.0, 0.0, 0.0)
});
entity.model.nodeTransformations.SRB_Flame2 = transformation;
onTickEvent();
}
}
onTickEvent = viewer.clock.onTick.addEventListener(adjust);
// 设置观察角度
entity.viewFrom = new Cesium.Cartesian3(-1000, 100,-1000);
完整代码
<style>
@import url(../templates/bucket.css);
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<span>
<h2>请调整至合适视角!<h4/><br/>
开始阶段:SRB_Flame2 (通过 model.nodeTransformations 控制)尾焰开启<br/><br/>
和 BoosterFlames (通过 czml articulations控制)尾焰开启!<br/><br/>
viewer时钟 100 秒之后,BoosterFlames 尾焰关闭,<br/><br/>
SRB_Flame4 和 SRB_Flame6(通过 czml nodeTransformations 控制) 开启!<br/><br/>
</span>
</div>
// 火箭模拟发射数据
const czml = [
{
"id": "document",
"name": "SpaceX",
"version": "1.0",
"clock": {
"interval": "2023-06-14T10:00:00Z/2023-06-14T10:17:33Z",
"currentTime": "2023-06-14T10:00:00Z",
"multiplier": 10,
"range": "CLAMPED",
"step": "SYSTEM_CLOCK_MULTIPLIER"
}
},
{
"id": "Vulcan",
"availability": "2023-06-14T10:00:00Z/2023-06-14T10:17:33Z",
"name": "Vulcan",
"billboard": {
"show": true,
"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAjSURBVChTYyAa/EcDUGEIgIphAKg0XRSAAFQMDqDChAADAwDC13+BJ+0oDwAAAABJRU5ErkJgggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"scale": 1,
"pixelOffset": {
"cartesian2": [
0, 0
]
},
"eyeOffset": {
"cartesian": [
0, 0, 0
]
},
"horizontalOrigin": "CENTER",
"verticalOrigin": "CENTER",
"color": [
{
"interval": "2023-06-14T10:00:00Z/2023-06-14T10:10:00Z",
"rgba": [
0, 255, 0, 255
]
},
{
"interval": "2023-06-14T10:10:00Z/2023-06-14T10:13:20Z",
"rgba": [
255, 255, 0, 255
]
},
{
"interval": "2023-06-14T10:13:20Z/9999-12-31T23:59:59.9999999Z",
"rgba": [
255, 0, 255, 255
]
}
]
},
"label": {
"show": false,
"text": "Vulcan",
"font": "21pt Lucida Console",
"style": "FILL_AND_OUTLINE",
"scale": 0.5,
"pixelOffset": {
"cartesian2": [
5, -4
]
},
"horizontalOrigin": "LEFT",
"verticalOrigin": "CENTER",
"fillColor": [
{
"interval": "2023-06-14T10:00:00Z/2023-06-14T10:10:00Z",
"rgba": [
0, 255, 0, 255
]
},
{
"interval": "2023-06-14T10:10:00Z/2023-06-14T10:13:20Z",
"rgba": [
255, 255, 0, 255
]
},
{
"interval": "2023-06-14T10:13:20Z/9999-12-31T23:59:59.9999999Z",
"rgba": [
255, 0, 255, 255
]
}
],
"outlineColor": {
"rgba": [
0, 0, 0, 255
]
},
"outlineWidth": 2
},
"path": {
"show": [
{
"interval": "2023-06-14T10:00:00Z/2023-06-14T10:17:33Z",
"boolean": true
}
],
"width": 5,
"resolution": 1,
"leadTime": [
{
"interval": "2023-06-14T10:00:00Z/2023-06-14T10:17:33Z",
"epoch": "2023-06-14T10:00:00Z",
"number": [
0, 1053,
1053, 0
]
}
],
"trailTime": [
{
"interval": "2023-06-14T10:00:00Z/2023-06-14T10:17:33Z",
"epoch": "2023-06-14T10:00:00Z",
"number": [
0, 0,
1053, 1053
]
}
],
"material": {
polylineGlow: {
color: [{
"interval": "2023-06-14T10:00:00Z/2023-06-14T10:10:00Z",
rgba: [255, 0, 0, 255],
},
{
"interval": "2023-06-14T10:10:00Z/2023-06-14T10:13:20Z",
rgba: [0, 0, 255, 255],
},
{
"interval": "2023-06-14T10:13:20Z/9999-12-31T23:59:59.9999999Z",
rgba: [255, 0, 255, 255],
}
],
glowPower: 0.25,
// taperPower: 0.5,
},
},
},
"model": {
"show": true,
"gltf": [
{
"interval": "2023-06-14T10:00:00Z/9999-12-31T23:59:59.9999999Z",
"uri": "https://cesium.com/public/SandcastleSampleData/launchvehicle.glb"
}
],
"minimumPixelSize": 512,
"scale": 15,
"runAnimations": false,
// 自定义的关节动作设置火焰
"articulations": {
// 火箭中间火焰
"BoosterFlames Size": {
"epoch": "2023-06-14T10:00:00Z",
"number": [
0, 1,
49, 1,
50, 0,
9999, 0
]
},
},
// nodeTransformations 设置火焰显示隐藏
"nodeTransformations": {
// 第六个火焰节点名称
"SRB_Flame6": {
// 比例(按照模型设置的中心点)
"scale": {
"epoch": "2023-06-14T10:00:00Z",
"cartesian": [
0.0, 0, 0, 0,
120.0, 0, 0, 0,
121.0, 1.0, 1.0, 1.0,
200.0, 1.0, 1.0, 1.0,
],
},
},
// 第五个火焰节点名称
"SRB_Flame4": {
// 比例(按照模型设置的中心点)
"scale": {
"epoch": "2023-06-14T10:00:00Z",
"cartesian": [
0.0, 0, 0, 0,
150.0, 0, 0, 0,
151.0, 1.0, 1.0, 1.0,
200.0, 1.0, 1.0, 1.0,
],
},
},
},
},
"position": {
"interpolationAlgorithm": "LAGRANGE",
"interpolationDegree": 2,
"referenceFrame": "FIXED",
"epoch": "2023-06-14T10:00:00Z",
"cartesian": [
0.000, -2174195.199042614, 4389988.019058515, 4070606.7900844226,
250.000, -2828836.74243954, 4527941.384141082, 4322899.592600973,
750.000, -3812052.7676919936, 3910960.7948377975, 4168079.0591541207,
1053.000, -3929362.40133975, 3277792.786767426, 3795371.463871257,
]
},
"orientation": {
"velocityReference": "#position"
},
},
];
const viewer = new Cesium.Viewer("cesiumContainer", {
shouldAnimate: true,
});
const dataSourcePromise = viewer.dataSources.add(
Cesium.CzmlDataSource.load(czml)
);
dataSourcePromise
.then(function (dataSource) {
// 获取模型实体
const entity = dataSource.entities.getById("Vulcan");
// 模型视角跟随
viewer.trackedEntity = entity;
console.log(entity.model.nodeTransformations);
// 定义比例节点转换,用于显示隐藏
const transformation = new Cesium.NodeTransformationProperty({
scale: new Cesium.Cartesian3(1.0, 1.0, 1.0)
});
// 记录当前时间
const current = viewer.clock.currentTime.secondsOfDay;
// 模型实体添加节点控制
entity.model.nodeTransformations.addProperty('SRB_Flame2', transformation);
// 时间轴监听事件
let onTickEvent;
// 时间轴监听回调
function adjust() {
// 200 秒后隐藏 SRB_Flame2 组件
if (viewer.clock.currentTime.secondsOfDay > current + 100) {
console.log('SRB_Flame2 尾焰关闭!');
// 重新定义火焰比例
const transformation = new Cesium.NodeTransformationProperty({
scale: new Cesium.Cartesian3(0.0, 0.0, 0.0)
});
entity.model.nodeTransformations.SRB_Flame2 = transformation;
onTickEvent();
}
}
onTickEvent = viewer.clock.onTick.addEventListener(adjust);
// 设置观察角度
entity.viewFrom = new Cesium.Cartesian3(-1000, 100,-1000);
})
.catch(function (error) {
console.error(error);
});
在线示例
三种方式(CZML、nodeTransformations)修改模型节点组件属性(修改比例、旋转、移动等)
参考博客:
[1]: 【Cesium】计算模型的朝向四元数,实现模型运动中调整朝向
[2]: czml api
[3]: CZML Model - Node Transformations.html
[4]: Transformations 代码设置节点旋转
[5]: cesium-CZML描述动态场景