首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在mapbox场景中使用"WASD“键移动3D对象

如何在mapbox场景中使用"WASD“键移动3D对象
EN

Stack Overflow用户
提问于 2021-01-22 21:20:18
回答 1查看 251关注 0票数 0

我正在尝试让我的3D对象在我的Mapbox创建的场景中移动。我使用一个自定义图层通过Three.js在地图上创建我的对象,如以下代码所示:

代码语言:javascript
复制
var map = action.map;

        var orign = {
            lon: -8.34,
            lat: 41.21
        }

        var camera, scene;

        var fromLL = function (lon, lat) {

            var extent = 20037508.34;

            var x = lon * extent / 180;
            var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
            y = y * extent / 180;

            return [(x + extent) / (2 * extent), 1 - ((y + extent) / (2 * extent))];
        }

        var translate = fromLL(orign.lon, orign.lat);

        const transform = {
            translateX: translate[0],
            translateY: translate[1],
            translateZ: 0,
            rotateX: Math.PI / 2,
            rotateY: 0,
            rotateZ: 0,
            scale: 5.41843220338983e-6
        }

        // configuration of the custom layer for a 3D model per the CustomLayerInterface

        var customLayer = {
            id: '3d-model' + uuid(),
            type: 'custom',
            renderingMode: '3d',

            
            onAdd: function (map, gl) {
                this.camera = new THREE.Camera();
                this.scene = new THREE.Scene();

                // create two three.js lights to illuminate the model
                var directionalLight = new THREE.DirectionalLight(0xffffff);
                directionalLight.position.set(0, -70, 100).normalize();
                this.scene.add(directionalLight);

                var directionalLight2 = new THREE.DirectionalLight(0xffffff);
                directionalLight2.position.set(0, 70, 100).normalize();
                this.scene.add(directionalLight2);

                // use the three.js GLTF loader to add the 3D model to the three.js scene
                var loader = new GLTFLoader();
                const bus = 'https://threejsfundamentals.org/threejs/resources/models/animals/Horse.gltf';
                loader.load(
                    bus,
                    (gltf) => {
                        this.scene.add(gltf.scene);
                    },
                    (xhr) => {
                        console.log(`${(xhr.loaded / xhr.total * 100)}% loaded`);
                    },
                    (error) => {
                        // called when loading has errors
                        console.error('An error happened', error);
                    }
                );

                this.map = map;

                // use the Mapbox GL JS map canvas for three.js
                this.renderer = new THREE.WebGLRenderer({
                    canvas: map.getCanvas(),
                    context: gl,
                    antialias: true
                });

                this.renderer.autoClear = false;
            },

            render: function (gl, matrix) {
                const rotationX = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(1, 0, 0), transform.rotateX);
                const rotationY = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0, 1, 0), transform.rotateY);
                const rotationZ = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0, 0, 1), transform.rotateZ);

                const m = new THREE.Matrix4().fromArray(matrix);
                const l = new THREE.Matrix4().makeTranslation(transform.translateX, transform.translateY, transform.translateZ)
                    .scale(new THREE.Vector3(transform.scale, -transform.scale, transform.scale))
                    .multiply(rotationX)
                    .multiply(rotationY)
                    .multiply(rotationZ);

                this.camera.projectionMatrix.elements = matrix;
                this.camera.projectionMatrix = m.multiply(l);
                this.renderer.state.reset();
                this.renderer.render(this.scene, this.camera);
                this.map.triggerRepaint();

            }
        }

在将对象放入场景后,我要采取的下一步是使用Mapbox提供的方法控制相机。我的疑问是如何让"setInterval“函数通过"WASD”键更改我之前渲染的对象的位置值,而不仅仅是地图上的相机。下面是我正在尝试调整的代码,以适应我的对象的移动:

代码语言:javascript
复制
map.on('load', function () {

            map.addLayer(customLayer);

            var keys = {};

            window.onkeyup = function (e) { keys[e.keyCode] = false; }
            window.onkeydown = function (e) { keys[e.keyCode] = true; }

            var heading = 180;

            setInterval(function () {
                var speed = 0;

                if (keys[68]) {
                    heading += 2;
                }
                if (keys[65]) {
                    heading -= 2;
                }
                if (keys[87]) {
                    speed = 0.0002;
                }
                if (keys[83]) {
                    speed = -0.0002;
                }

                var rad = heading * 0.0174532925;

                orign.lat += Math.cos(rad) * speed;
                orign.lon += Math.sin(rad) * speed;

                map.setBearing(heading);
                map.setCenter([orign.lon, orign.lat]);
            }, 1000 / 60);
        });

到目前为止,我所拥有的是:3D Object in map

EN

回答 1

Stack Overflow用户

发布于 2021-02-02 19:02:52

如果你还对这个感兴趣..。这是the example I have built based on your request of a WASD game-like demo in Mapbox。在这个示例中,我使用了threebox,它简化了与three.js和mapbox的所有交互,避免了您处理相机、矩阵、透视图和其他更多内容。

我还在你的问题中添加了一些额外的功能,比如惯性,加速度参数和一个填充挤压层,当你用卡车与建筑物相撞时,它会将建筑物涂成红色。

相关代码如下:

代码语言:javascript
复制
        mapboxgl.accessToken = "PASTE YOUR TOKEN HERE";

        let minZoom = 12;
        let mapConfig = {
          map: {
            center: [-122.4301905, 37.7298202],
            zoom: 20,
            pitch: 60,
            bearing: 38
          },
          truck: {
            origin: [-122.4301905, 37.7298202, 0],
            type: 'mtl',
            model: 'https://unpkg.com/threebox-plugin/examples/models/Truck',
            rotation: {
              x: 90,
              y: 0,
              z: 0
            },
            scale: 3,
            startRotation: {
              x: 0,
              y: 0,
              z: -38
            },
            date: new Date(2020, 6, 19, 23)
          },
          names: {
            compositeSource: "composite",
            compositeSourceLayer: "building",
            compositeLayer: "3d-buildings"
          }
        }

        let map = new mapboxgl.Map({
          container: 'map',
          style: 'mapbox://styles/mapbox/satellite-streets-v11',
          zoom: mapConfig.map.zoom,
          center: mapConfig.map.center,
          pitch: mapConfig.map.pitch,
          bearing: mapConfig.map.bearing,
          antialias: true // create the gl context with MSAA antialiasing, so custom layers are antialiased
        });

        window.tb = new Threebox(
          map,
          map.getCanvas().getContext('webgl'), {
            realSunlight: true,
            enableSelectingObjects: true,
            enableDraggingObjects: true,
            enableRotatingObjects: true,
            enableTooltips: true
          }
        );

        tb.setSunlight(new Date(2020, 6, 19, 23), map.getCenter());

        // parameters to ensure the model is georeferenced correctly on the map
        let truck;

        function createCustomLayer(layerName) {
          let model;
          //create the layer
          let customLayer3D = {
            id: layerName,
            type: 'custom',
            renderingMode: '3d',
            onAdd: function(map, gl) {

              let options = {
                type: mapConfig.truck.type, //model type
                obj: mapConfig.truck.model + '.obj', //model .obj url
                mtl: mapConfig.truck.model + '.mtl', //model .mtl url
                units: 'meters', // in meters
                scale: mapConfig.truck.scale, //x3 times is real size for this model
                rotation: mapConfig.truck.rotation, //default rotation
                anchor: 'top'
              }
              tb.loadObj(options, function(model) {
                truck = model.setCoords(mapConfig.truck.origin);
                truck.setRotation(mapConfig.truck.startRotation); //turn it to the initial street way
                truck.addTooltip("Drive with WASD keys", true, truck.anchor, true, 2);
                truck.castShadow = true;
                truck.selected = true;
                truck.addEventListener('ObjectChanged', onObjectChanged, false);

                tb.add(truck);
                init();

              });


            },
            render: function(gl, matrix) {
              tb.update();
            }
          };
          return customLayer3D;

        };

        function easing(t) {
          return t * (2 - t);
        }

        let velocity = 0.0,
          speed = 0.0,
          ds = 0.01;
        let keys;

        map.on('style.load', function() {
          let l = mapConfig.names.compositeLayer;
          if (api.buildings) {
            if (!map.getLayer(l)) {
              map.addLayer(createCompositeLayer(l));
            }
          }
          map.addLayer(createCustomLayer('3d-model'), 'waterway-label');

          map.getCanvas().focus();

        });

        function createCompositeLayer(layerId) {
          let layer = {
            'id': layerId,
            'source': mapConfig.names.compositeSource,
            'source-layer': mapConfig.names.compositeSourceLayer,
            'filter': ['==', 'extrude', 'true'],
            'type': 'fill-extrusion',
            'minzoom': minZoom,
            'paint': {
              'fill-extrusion-color': [
                'case',
                ['boolean', ['feature-state', 'select'], false],
                "red",
                ['boolean', ['feature-state', 'hover'], false],
                "lightblue",
                '#aaa'
              ],

              // use an 'interpolate' expression to add a smooth transition effect to the
              // buildings as the user zooms in
              'fill-extrusion-height': [
                'interpolate',
                ['linear'],
                ['zoom'],
                minZoom,
                0,
                minZoom + 0.05,
                ['get', 'height']
              ],
              'fill-extrusion-base': [
                'interpolate',
                ['linear'],
                ['zoom'],
                minZoom,
                0,
                minZoom + 0.05,
                ['get', 'min_height']
              ],
              'fill-extrusion-opacity': 0.9
            }
          };
          return layer;
        }

        let api = {
          buildings: true,
          acceleration: 5,
          inertia: 3
        };

        function init() {

          keys = {
            a: false,
            s: false,
            d: false,
            w: false
          };

          document.body.addEventListener('keydown', function(e) {

            const key = e.code.replace('Key', '').toLowerCase();
            if (keys[key] !== undefined)
              keys[key] = true;
          });
          document.body.addEventListener('keyup', function(e) {

            const key = e.code.replace('Key', '').toLowerCase();
            if (keys[key] !== undefined)
              keys[key] = false;
          });

          animate();

        }

        function animate() {

          requestAnimationFrame(animate);
          speed = 0.0;

          if (!(keys.w || keys.s)) {
            if (velocity > 0) {
              speed = -api.inertia * ds
            } else if (velocity < 0) {
              speed = api.inertia * ds
            }
            if (velocity > -0.0008 && velocity < 0.0008) {
              speed = velocity = 0.0;
              return;
            }
          }

          if (keys.w)
            speed = api.acceleration * ds;
          else if (keys.s)
            speed = -api.acceleration * ds;

          velocity += (speed - velocity) * api.acceleration * ds;
          if (speed == 0.0) {
            velocity = 0;
            return;
          }

          truck.set({
            worldTranslate: new THREE.Vector3(0, -velocity, 0)
          });

          let options = {
            center: truck.coordinates,
            bearing: map.getBearing(),
            easing: easing
          };

          function toDeg(rad) {
            return rad / Math.PI * 180;
          }

          function toRad(deg) {
            return deg * Math.PI / 180;
          }

          let deg = 1;
          let rad = toRad(deg);
          let zAxis = new THREE.Vector3(0, 0, 1);

          if (keys.a || keys.d) {
            rad *= (keys.d ? -1 : 1);
            truck.set({
              quaternion: [zAxis, truck.rotation.z + rad]
            });
            options.bearing = -toDeg(truck.rotation.z);
          }

          map.jumpTo(options);
          tb.map.update = true;

        }

        function onObjectChanged(e) {
          let model = e.detail.object; //here's the object already modified
          if (api.buildings) {
            let c = model.coordinates;
            let point = map.project(c);
            let features = map.queryRenderedFeatures(point, {
              layers: [mapConfig.names.compositeLayer]
            });
            if (features.length > 0) {
              light(features[0]); // crash!
            }
          }
        }

        function light(feature) {
          fHover = feature;
          map.setFeatureState({
            source: fHover.source,
            sourceLayer: fHover.sourceLayer,
            id: fHover.id
          }, {
            select: true
          });
        }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65845977

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档