maptalks 开发GIS地图(9)maptalks.three.02 animation

1. 说明: 本demo主要是加载了一个具有动画效果的三维机器人,然后通过鼠标选择菜单控制机器人的动作和表情。

  其实这个机器人,本身就已经具有动画和动作表情效果了,只不过是使用threejs的接口,把它加载到地图上,

  然后再调用API操控机器人。

2. 先来看一下  ../threelayer/demo/data/RobotExpressive.glb 这个文件。通过Windows10下面自带的 3D查看器软件打开。

    这是一个glb文件,通过3dmax软件导出后,已经具有了动画效果。而且还包括了多种动画效果。

3. 新建一个 maptalks 地图

关于maptalks 地图的功能,这里就不多讲了,可以参考前面的文章。

 1         var map = new maptalks.Map("map", {
 2             center: [19.06325670775459, 42.16842479475318],
 3             zoom: 9,
 4             pitch: 60,
 5             // bearing: 180,
 6 
 7             centerCross: true,
 8             doubleClickZoom: false,
 9             baseLayer: new maptalks.TileLayer('tile', {
10                 urlTemplate: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
11                 subdomains: ['a', 'b', 'c', 'd'],
12                 attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
13             })
14         });

4. 新建一个 ThreeLayer 图层,并设置三维三要素,光源、场景、镜头。

 1  // the ThreeLayer to draw buildings
 2         var threeLayer = new maptalks.ThreeLayer('t', {
 3             forceRenderOnMoving: true,
 4             forceRenderOnRotating: true
 5             // animation: true
 6         });
 7         threeLayer.prepareToDraw = function (gl, scene, camera) {
 8             var light = new THREE.DirectionalLight(0xffffff);
 9             light.position.set(0, -10, 10).normalize();
10             scene.add(light);
11             camera.add(new THREE.PointLight('#fff', 4));
12 
13             addGltf();
14 
15         };
16         threeLayer.addTo(map);

5. 添加三维模型对象

这里使用了 THREE.GLTFLoader 对象,此函数包含在 GLTFLoader.js 中,需要调用前添加引用。

其中 model 对象是获取了三维模型中的场景,然后将三维模型进行旋转45°  model.rotation , 模型比例设为 1:1 , scale.set , 模型的位置设在地图中心点。

使用 addMesh 添加三维模型到对应的threeLayer 图层,完成了三维模型对象在地图上的添加。

 1   function addGltf() {
 2             clock = new THREE.Clock();
 3             stats = new Stats();
 4             map.getContainer().appendChild(stats.dom);
 5             var loader = new THREE.GLTFLoader();
 6             loader.load('./data/RobotExpressive.glb', function (gltf) {
 7 
 8                 model = gltf.scene;
 9                 model.rotation.x = Math.PI / 2;
10                 model.scale.set(100, 100, 100);
11                 model.position.copy(threeLayer.coordinateToVector3(map.getCenter()));
12                 threeLayer.addMesh(model);
13 
14                 createGUI(model, gltf.animations);
15                 animate();
16 
17             }, undefined, function (e) {
18 
19                 console.error(e);
20 
21             });
22         }

6. 创建GUI用以控制机器人

这就是前面那篇文章说的使用了 dat.gui.min.js 中相关的内容。

 1 function createGUI(model, animations) {
 2 
 3             var states = ['Idle', 'Walking', 'Running', 'Dance', 'Death', 'Sitting', 'Standing'];
 4             var emotes = ['Jump', 'Yes', 'No', 'Wave', 'Punch', 'ThumbsUp'];
 5 
 6             gui = new dat.GUI();
 7 
 8             mixer = new THREE.AnimationMixer(model);
 9 
10             actions = {};
11 
12             for (var i = 0; i < animations.length; i++) {
13 
14                 var clip = animations[i];
15                 var action = mixer.clipAction(clip);
16                 actions[clip.name] = action;
17 
18                 if (emotes.indexOf(clip.name) >= 0 || states.indexOf(clip.name) >= 4) {
19 
20                     action.clampWhenFinished = true;
21                     action.loop = THREE.LoopOnce;
22 
23                 }
24 
25             }
26 
27             // states
28 
29             var statesFolder = gui.addFolder('States');
30 
31             var clipCtrl = statesFolder.add(api, 'state').options(states);
32 
33             clipCtrl.onChange(function () {
34 
35                 fadeToAction(api.state, 0.5);
36 
37             });
38 
39             statesFolder.open();
40 
41             // emotes
42 
43             var emoteFolder = gui.addFolder('Emotes');
44 
45             function createEmoteCallback(name) {
46 
47                 api[name] = function () {
48 
49                     fadeToAction(name, 0.2);
50 
51                     mixer.addEventListener('finished', restoreState);
52 
53                 };
54 
55                 emoteFolder.add(api, name);
56 
57             }
58 
59             function restoreState() {
60 
61                 mixer.removeEventListener('finished', restoreState);
62 
63                 fadeToAction(api.state, 0.2);
64 
65             }
66 
67             for (var i = 0; i < emotes.length; i++) {
68 
69                 createEmoteCallback(emotes[i]);
70 
71             }
72 
73             emoteFolder.open();
74 
75             // expressions
76 
77             face = model.getObjectByName('Head_2');
78 
79             var expressions = Object.keys(face.morphTargetDictionary);
80             var expressionFolder = gui.addFolder('Expressions');
81 
82             for (var i = 0; i < expressions.length; i++) {
83 
84                 expressionFolder.add(face.morphTargetInfluences, i, 0, 1, 0.01).name(expressions[i]);
85 
86             }
87 
88             activeAction = actions['Walking'];
89             activeAction.play();
90 
91             expressionFolder.open();
92 
93         }

7. 重新绘制webgl的状态,满足动画更新需要。

 1         function animate() {
 2             var dt = clock.getDelta();
 3             if (mixer) mixer.update(dt);
 4             requestAnimationFrame(animate);
 5             stats.update();
 6             // threeLayer._needsUpdate = !threeLayer._needsUpdate;
 7             if (threeLayer._needsUpdate) {
 8                 threeLayer.renderScene();
 9             }
10 
11         }

8. 在 maptalks 地图中的效果。

9.  源码地址

https://github.com/WhatGIS/maptalkMap/tree/main/threelayer/demo

原文地址:https://www.cnblogs.com/googlegis/p/14721778.html