diff --git a/package.json b/package.json index 8a093da63a..5ef325b786 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "lint-staged": "^9.2.4", "node-sass": "^4.12.0", "npm-run-all": "^4.1.5", + "popmotion": "^9.4.2", "postcss": "^7.0.18", "postcss-plugin": "^1.0.0", "postcss-url": "^8.0.0", diff --git a/packages/maps/src/amap/map.ts b/packages/maps/src/amap/map.ts index 4cc64e9a16..dee66c084f 100644 --- a/packages/maps/src/amap/map.ts +++ b/packages/maps/src/amap/map.ts @@ -275,6 +275,12 @@ export default class AMapService }; } + public lngLatToCoord(lnglat: [number, number]): any { + // @ts-ignore + const { x, y } = this.map.lngLatToGeodeticCoord(lnglat); + return [x, -y]; + } + public lngLatToMercator( lnglat: [number, number], altitude: number, diff --git a/packages/maps/src/amap2/map.ts b/packages/maps/src/amap2/map.ts index 547b1914ec..664694fe5d 100644 --- a/packages/maps/src/amap2/map.ts +++ b/packages/maps/src/amap2/map.ts @@ -370,7 +370,9 @@ export default class AMapService scale: [number, number, number] = [1, 1, 1], origin: IMercator = { x: 0, y: 0, z: 0 }, ): number[] { - const flat = this.viewport.projectFlat(lnglat); + // const flat = this.viewport.projectFlat(lnglat); + // @ts-ignore + const flat = this.map.customCoords.lngLatToCoord(lnglat); // @ts-ignore const modelMatrix = mat4.create(); @@ -379,6 +381,7 @@ export default class AMapService modelMatrix, vec3.fromValues(flat[0], flat[1], altitude), ); + mat4.scale( modelMatrix, modelMatrix, diff --git a/packages/maps/src/mapbox/map.ts b/packages/maps/src/mapbox/map.ts index 3073ac8c35..f7d9875421 100644 --- a/packages/maps/src/mapbox/map.ts +++ b/packages/maps/src/mapbox/map.ts @@ -236,6 +236,21 @@ export default class MapboxService public lngLatToContainer(lnglat: [number, number]): IPoint { return this.map.project(lnglat); } + + /** + * 将经纬度转成墨卡托坐标 + * @param lnglat + * @returns + */ + public lngLatToCoord( + lnglat: [number, number], + origin: IMercator = { x: 0, y: 0, z: 0 }, + ) { + // @ts-ignore + const { x, y } = this.lngLatToMercator(lnglat, 0); + return [x - origin.x, y - origin.y] as [number, number]; + } + public lngLatToMercator( lnglat: [number, number], altitude: number, diff --git a/packages/three/src/core/IThreeJSLayer.ts b/packages/three/src/core/IThreeJSLayer.ts new file mode 100644 index 0000000000..702cdb0710 --- /dev/null +++ b/packages/three/src/core/IThreeJSLayer.ts @@ -0,0 +1,29 @@ +import { ILayer } from '@antv/l7'; +import { AnimationMixer, Matrix4, Object3D } from 'three'; + +export type ILngLat = [number, number]; + +export interface IThreeJSLayer extends ILayer { + // 获取对应地图的经纬度模型矩阵 + getModelMatrix( + lnglat: ILngLat, + altitude: number, + rotation: [number, number, number], + scale: [number, number, number], + ): Matrix4; + + // 获取对应地图的经纬度平移矩阵 + getTranslateMatrix(lnglat: ILngLat, altitude?: number): Matrix4; + + // 设置模型对应地图在经纬度和高度方向的平移 + setObjectLngLat(object: Object3D, lnglat: ILngLat, altitude?: number): void; + + // 根据经纬度设置模型对应地图的平移 + setObjectLngLat(object: Object3D, lnglat: ILngLat, altitude?: number): void; + + // 返回物体在场景中的经纬度 + getObjectLngLat(object: Object3D): ILngLat; + + // 增加加载模型的动画混合器 + addAnimateMixer(mixer: AnimationMixer): void; +} diff --git a/packages/three/src/core/baseLayer.ts b/packages/three/src/core/baseLayer.ts index 53bdbedd30..34a9dd5faa 100644 --- a/packages/three/src/core/baseLayer.ts +++ b/packages/three/src/core/baseLayer.ts @@ -3,25 +3,19 @@ import { AnimationMixer, Camera, Matrix4, + Object3D, PCFSoftShadowMap, PerspectiveCamera, Scene, + Vector3, WebGLRenderer, } from 'three'; +import { ILngLat, IThreeJSLayer } from './IThreeJSLayer'; import { IThreeRenderService, ThreeRenderServiceType, } from './threeRenderService'; const DEG2RAD = Math.PI / 180; -interface IThreeJSLayer extends ILayer { - getModelMatrix( - lnglat: [number, number], - altitude: number, - rotation: [number, number, number], - scale: [number, number, number], - ): Matrix4; - addAnimateMixer(mixer: AnimationMixer): void; -} export default class ThreeJSLayer extends BaseLayer<{ onAddMeshes: (threeScene: Scene, layer: ThreeJSLayer) => void; @@ -29,16 +23,15 @@ export default class ThreeJSLayer implements IThreeJSLayer { public type: string = 'custom'; protected threeRenderService: IThreeRenderService; + // 构建 threejs 的 scene private scene: Scene = new Scene(); private renderer: WebGLRenderer; private animateMixer: AnimationMixer[] = []; // 地图中点墨卡托坐标 private center: IMercator; - // 初始状态相机变换矩阵 - /** - * 根据模型 + * 根据数据计算对应地图的模型矩阵 不同地图主要是点位偏移不同 */ public getModelMatrix( lnglat: [number, number], @@ -57,7 +50,100 @@ export default class ThreeJSLayer ); } + /** + * 获取平移矩阵 + * @param lnglat + * @param altitude + * @returns + */ + public getTranslateMatrix(lnglat: ILngLat, altitude: number = 0) { + return this.getModelMatrix(lnglat, altitude, [0, 0, 0], [1, 1, 1]); + } + + /** + * 设置当前物体往经纬度和高度方向的移动 + * @param object + * @param lnglat + * @param altitude + */ + public applyObjectLngLat(object: Object3D, lnglat: ILngLat, altitude = 0) { + const positionMatrix = this.getTranslateMatrix(lnglat, altitude); + object.applyMatrix4(positionMatrix); + } + + /** + * 设置物体当前的经纬度和高度 + * @param object + * @param lnglat + * @param altitude + */ + public setObjectLngLat(object: Object3D, lnglat: ILngLat, altitude = 0) { + // @ts-ignore + const [x, y] = this.lnglatToCoord(lnglat); + object.position.set(x, y, altitude); + } + + /** + * 将经纬度转为 three 世界坐标 + * @param lnglat + * @returns + */ + public lnglatToCoord(lnglat: ILngLat) { + // @ts-ignore + const [x, y] = this.mapService?.lngLatToCoord( + lnglat, + // @ts-ignore + this.threeRenderService.center, + ); + return [x, y]; + } + + /** + * 获取 + * @param object + * @returns + */ + public getObjectLngLat(object: Object3D) { + const coord = [object.position.x, object.position.y]; + return [0, 0] as ILngLat; + } + + /** + * 设置网格适配到地图坐标系 + * @param object + */ + public adjustMeshToMap(object: Object3D) { + object.up = new Vector3(0, 0, 1); + const defaultLngLat = this.mapService.getCenter(); + const modelMatrix = this.getModelMatrix( + [defaultLngLat.lng, defaultLngLat.lat], // 经纬度坐标 + 0, // 高度,单位米/ + [Math.PI / 2, -Math.PI, 0], // 沿 XYZ 轴旋转角度 + [1, 1, 1], // 沿 XYZ 轴缩放比例 + ); + object.applyMatrix4(modelMatrix); + } + + /** + * 设置网格的缩放 (主要是抹平 mapbox 底图时的差异,若是高德底图则可以直接设置网格的 scale 属性/方法) + * @param object + * @param x + * @param y + * @param z + */ + public setMeshScale( + object: Object3D, + x: number = 1, + y: number = 1, + z: number = 1, + ) { + const scaleMatrix = new Matrix4(); + scaleMatrix.scale(new Vector3(x, y, z)); + object.applyMatrix4(scaleMatrix); + } + public buildModels() { + // @ts-ignore this.threeRenderService = this.getContainer().get( ThreeRenderServiceType, ); @@ -67,16 +153,23 @@ export default class ThreeJSLayer } } public renderModels() { + // 获取到 L7 的 gl const gl = this.rendererService.getGLContext(); this.rendererService.setCustomLayerDefaults(); const cullFace = this.mapService.constructor.name === 'AMapService' ? gl.BACK : gl.FRONT; gl.cullFace(cullFace); + + // threejs 的 renderer const renderer = this.threeRenderService.renderer; renderer.state.reset(); renderer.autoClear = false; + + // 获取相机 (不同的地图获取对应的方式不同) const camera = this.threeRenderService.getRenderCamera(); + renderer.render(this.scene, camera); + this.rendererService.setBaseState(); this.animateMixer.forEach((mixer: AnimationMixer) => { mixer.update(this.getTime()); diff --git a/packages/three/src/core/threeRenderService.ts b/packages/three/src/core/threeRenderService.ts index 283dece4a1..19f3da57b1 100644 --- a/packages/three/src/core/threeRenderService.ts +++ b/packages/three/src/core/threeRenderService.ts @@ -25,6 +25,7 @@ export class ThreeRenderService implements IThreeRenderService { public renderer: WebGLRenderer; public camera: Camera; public center: IMercator; + public aspect: number; private scene: ThreeScene; // 初始状态相机变换矩阵 @@ -37,6 +38,7 @@ export class ThreeRenderService implements IThreeRenderService { private readonly mapService: IMapService; public init() { + // 从 L7 的 renderer 中获取可视化层的 canvas/gl const canvas = this.rendererService.getCanvas() as HTMLCanvasElement; const gl = this.rendererService.getGLContext(); if (canvas && gl) { @@ -48,6 +50,8 @@ export class ThreeRenderService implements IThreeRenderService { } const { x, y, z } = this.center; this.cameraTransform = new Matrix4().makeTranslation(x, y, z); + + // 根据 L7 的 canvas/gl 构建 threejs 的 renderer this.renderer = new WebGLRenderer({ canvas, context: gl, @@ -61,12 +65,30 @@ export class ThreeRenderService implements IThreeRenderService { // this.renderer.shadowMap.type = PCFSoftShadowMap; this.scene = new ThreeScene(); - this.camera = new PerspectiveCamera(45, 1, 1, 2000000); + + this.aspect = gl.drawingBufferWidth / gl.drawingBufferHeight; + this.camera = new PerspectiveCamera(45, this.aspect, 1, 20000000); } public getRenderCamera(): Camera { - return this.mapService.constructor.name === 'AMapService' - ? this.AMapCamera() - : this.mapboxCamera(); + /** + * map version + * GAODE1.x + * GAODE2.x + * MAPBOX + */ + switch (this.mapService.version) { + case 'GAODE1.x': + return this.AMapCamera(); + case 'GAODE2.x': + return this.AMap2Camera(); + case 'MAPBOX': + return this.mapboxCamera(); + default: + return this.AMapCamera(); + } + // return this.mapService.constructor.name === 'AMapService' + // ? this.AMapCamera() + // : this.mapboxCamera(); } private mapboxCamera(): Camera { @@ -109,4 +131,36 @@ export class ThreeRenderService implements IThreeRenderService { camera.position.y += -mapCamera.position.y; return camera; } + + private AMap2Camera(): Camera { + // @ts-ignore + const customCoords = this.mapService.map.customCoords; + customCoords.getCenter(); + + const camera = this.camera; + const { + near, + far, + fov, + up, + lookAt, + position, + } = customCoords.getCameraParams(); + // @ts-ignore + camera.near = near; + // @ts-ignore + camera.far = far; + // @ts-ignore + camera.fov = fov; + // @ts-ignore + camera.position.set(...position); + // @ts-ignore + camera.up.set(...up); + // @ts-ignore + camera.lookAt(...lookAt); + // @ts-ignore + camera.updateProjectionMatrix(); + + return camera; + } } diff --git a/packages/three/src/index.ts b/packages/three/src/index.ts index eeb2447453..6eb46c17fc 100644 --- a/packages/three/src/index.ts +++ b/packages/three/src/index.ts @@ -1,3 +1,5 @@ +import { Object3D } from 'three'; import ThreeLayer from './core/baseLayer'; +import { ILngLat, IThreeJSLayer } from './core/IThreeJSLayer'; import ThreeRender from './core/threeRender'; -export { ThreeLayer, ThreeRender }; +export { ThreeLayer, ThreeRender, IThreeJSLayer, ILngLat, Object3D }; diff --git a/stories/3D_Model/Components/amap2_three.tsx b/stories/3D_Model/Components/amap2_three.tsx new file mode 100644 index 0000000000..fc4780dbd2 --- /dev/null +++ b/stories/3D_Model/Components/amap2_three.tsx @@ -0,0 +1,148 @@ +import { Scene } from '@antv/l7'; +import { GaodeMap, GaodeMapV2, Mapbox } from '@antv/l7-maps'; +import { ThreeLayer, ThreeRender, ILngLat, Object3D } from '@antv/l7-three'; +import * as React from 'react'; +// import { DirectionalLight, Scene as ThreeScene } from 'three'; +import * as THREE from 'three'; +// tslint:disable-next-line:no-submodule-imports +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; + +export default class GlTFThreeJSDemo extends React.Component { + // @ts-ignore + private scene: Scene; + + public componentWillUnmount() { + this.scene.destroy(); + } + + public async componentDidMount() { + const response = await fetch( + 'https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json', + ); + const pointsData = await response.json(); + + const scene = new Scene({ + id: 'map', + map: new GaodeMapV2({ + center: [111.4453125, 32.84267363195431], + pitch: 45, + rotation: 30, + zoom: 13, + }), + }); + this.scene = scene; + scene.registerRenderService(ThreeRender); + scene.on('loaded', () => { + const threeJSLayer = new ThreeLayer({ + enableMultiPassRenderer: false, + onAddMeshes: (threeScene: THREE.Scene, layer: ThreeLayer) => { + threeScene.add(new THREE.AmbientLight(0xffffff)); + + const sunlight = new THREE.DirectionalLight(0xffffff, 0.25); + sunlight.position.set(0, 80000000, 100000000); + sunlight.matrixWorldNeedsUpdate = true; + threeScene.add(sunlight); + + // 使用 Three.js glTFLoader 加载模型 + const loader = new GLTFLoader(); + loader.load( + // 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf', + // 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/AnimatedCube/glTF/AnimatedCube.gltf', + // 'https://gw.alipayobjects.com/os/antvdemo/assets/gltf/radar/34M_17.gltf', + // 'https://gw.alipayobjects.com/os/antvdemo/assets/gltf/duck/Duck.gltf', // duck + // 'https://gw.alipayobjects.com/os/antvdemo/assets/gltf/truck/CesiumMilkTruck.gltf', // Truck + // 'https://gw.alipayobjects.com/os/antvdemo/assets/gltf/man/CesiumMan.gltf', + 'https://gw.alipayobjects.com/os/bmw-prod/3ca0a546-92d8-4ba0-a89c-017c218d5bea.gltf', + (gltf) => { + const model: Object3D = gltf.scene; + + layer.getSource().data.dataArray.forEach(({ coordinates }) => { + layer.adjustMeshToMap(model); + // model.scale.set(100, 100, 100) + layer.setMeshScale(model, 100, 100, 100); + + const animations = gltf.animations; + if (animations && animations.length) { + const mixer = new THREE.AnimationMixer(model); + // @ts-ignore + // for (let i = 0; i < 1; i++) { + const animation = animations[2]; + + // There's .3333 seconds junk at the tail of the Monster animation that + // keeps it from looping cleanly. Clip it at 3 seconds + + const action = mixer.clipAction(animation); + + action.play(); + // } + layer.addAnimateMixer(mixer); + } + }); + // 向场景中添加模型 + threeScene.add(model); + + let lnglat = [121.107, 30.267069] as [number, number]; + let altitude = 0; + let center = scene.getCenter(); + // layer.setObjectLngLat(model, lnglat, altitude) + // console.log() + // layer.setObjectLngLat(model, [center.lng + 0.05, center.lat] as ILngLat, 0) + // layer.setObjectLngLat(model, [center.lng + 0.05, center.lat] as ILngLat, 0) + + layer.setObjectLngLat( + model, + [center.lng + 0.05, center.lat] as ILngLat, + 0, + ); + + let t = 0; + setInterval(() => { + t += 0.01; + layer.setObjectLngLat( + model, + [center.lng, center.lat + Math.sin(t) * 0.1] as ILngLat, + 0, + ); + // layer.setObjectLngLat(model, [center.lng + 0.2, center.lat], 0) + }, 16); + + // 重绘图层 + layer.render(); + }, + ); + }, + }) + .source({ + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [111.4453125, 32.84267363195431], + // coordinates: [121.107, 30.267069], // 该坐标点在钱塘江入海口附近 + }, + }, + ], + }) + .animate(true); + scene.addLayer(threeJSLayer); + }); + } + + public render() { + return ( +
+ ); + } +} diff --git a/stories/3D_Model/Components/amap_three.tsx b/stories/3D_Model/Components/amap_three.tsx index c63957acd3..df466d718e 100644 --- a/stories/3D_Model/Components/amap_three.tsx +++ b/stories/3D_Model/Components/amap_three.tsx @@ -1,6 +1,6 @@ import { Scene } from '@antv/l7'; import { GaodeMap, Mapbox } from '@antv/l7-maps'; -import { ThreeLayer, ThreeRender } from '@antv/l7-three'; +import { ThreeLayer, ThreeRender, ILngLat } from '@antv/l7-three'; import * as React from 'react'; // import { DirectionalLight, Scene as ThreeScene } from 'three'; import * as THREE from 'three'; @@ -41,6 +41,19 @@ export default class GlTFThreeJSDemo extends React.Component { sunlight.position.set(0, 80000000, 100000000); sunlight.matrixWorldNeedsUpdate = true; threeScene.add(sunlight); + + let center = scene.getCenter(); + + let cubeGeometry = new THREE.BoxBufferGeometry(10000, 10000, 10000); + let cubeMaterial = new THREE.MeshNormalMaterial(); + let cube = new THREE.Mesh(cubeGeometry, cubeMaterial); + layer.setObjectLngLat( + cube, + [center.lng + 0.05, center.lat] as ILngLat, + 0, + ); + threeScene.add(cube); + // 使用 Three.js glTFLoader 加载模型 const loader = new GLTFLoader(); loader.load( @@ -55,15 +68,17 @@ export default class GlTFThreeJSDemo extends React.Component { // 根据 GeoJSON 数据放置模型 layer.getSource().data.dataArray.forEach(({ coordinates }) => { const gltfScene = gltf.scene; - gltfScene.applyMatrix4( - // 生成模型矩阵 - layer.getModelMatrix( - [coordinates[0], coordinates[1]], // 经纬度坐标 - 0, // 高度,单位米/ - [Math.PI / 2, -Math.PI, 0], // 沿 XYZ 轴旋转角度 - [100, 100, 100], // 沿 XYZ 轴缩放比例 - ), + + layer.adjustMeshToMap(gltfScene); + // gltfScene.scale.set(1000, 1000, 1000) + layer.setMeshScale(gltfScene, 1000, 1000, 1000); + + layer.setObjectLngLat( + gltfScene, + [coordinates[0] + 0.02, coordinates[1]], + 0, ); + const animations = gltf.animations; if (animations && animations.length) { const mixer = new THREE.AnimationMixer(gltfScene); @@ -80,6 +95,16 @@ export default class GlTFThreeJSDemo extends React.Component { // } layer.addAnimateMixer(mixer); } + // layer.setObjectLngLat(gltfScene, [center.lng + 0.05, center.lat] as ILngLat, 0) + let t = 0; + setInterval(() => { + t += 0.01; + layer.setObjectLngLat( + gltfScene, + [center.lng, center.lat + Math.sin(t) * 0.1] as ILngLat, + 0, + ); + }, 16); // 向场景中添加模型 threeScene.add(gltfScene); diff --git a/stories/3D_Model/Components/mapbox_three.tsx b/stories/3D_Model/Components/mapbox_three.tsx index 5d07990def..9336365d3e 100644 --- a/stories/3D_Model/Components/mapbox_three.tsx +++ b/stories/3D_Model/Components/mapbox_three.tsx @@ -1,11 +1,65 @@ import { PolygonLayer, Scene } from '@antv/l7'; import { GaodeMap, Mapbox } from '@antv/l7-maps'; -import { ThreeLayer, ThreeRender } from '@antv/l7-three'; +import { ThreeLayer, ThreeRender, ILngLat } from '@antv/l7-three'; import * as React from 'react'; // import { DirectionalLight, Scene as ThreeScene } from 'three'; import * as THREE from 'three'; // tslint:disable-next-line:no-submodule-imports import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { animate, easeInOut } from 'popmotion'; +import { Object3D, Vector3 } from 'three'; + +let isTravel = false; + +function travel( + mesh: Object3D, + path: Vector3[], + duration: number, + callback?: () => any, +) { + if (path.length < 2 || isTravel) return; + isTravel = true; + let startIndex = 0, + len = path.length; + let currentP = path[0], + nextP = path[1]; + let t = duration / len; + + move(currentP, nextP); + function move(currentP: Vector3, nextP: Vector3) { + animate({ + from: { + x: currentP.x, + y: currentP.y, + z: currentP.z, + }, + to: { + x: nextP.x, + y: nextP.y, + z: nextP.z, + }, + ease: easeInOut, + duration: t, + repeatType: 'loop', + onUpdate: (o) => { + mesh.position.set(o.x, o.y, o.z); + }, + onComplete: () => { + startIndex++; + if (startIndex < len - 1) { + let currentP = path[startIndex], + nextP = path[startIndex + 1]; + mesh.lookAt(nextP); + + move(currentP, nextP); + } else { + isTravel = false; + callback && callback(); + } + }, + }); + } +} export default class GlTFThreeJSDemo extends React.Component { // @ts-ignore @@ -61,6 +115,66 @@ export default class GlTFThreeJSDemo extends React.Component { sunlight.position.set(0, 80000000, 100000000); sunlight.matrixWorldNeedsUpdate = true; threeScene.add(sunlight); + + let lineData: ILngLat[] = [ + [116.71874999999999, 26.745610382199022], + [117.3779296875, 28.8831596093235], + [115.75195312499999, 31.466153715024294], + [113.466796875, 33.32134852669881], + [113.9501953125, 35.85343961959182], + [115.400390625, 38.272688535980976], + [116.5869140625, 40.3130432088809], + [115.6201171875, 42.261049162113856], + [112.236328125, 42.94033923363181], + [109.3798828125, 41.04621681452063], + [103.84277343749999, 39.80853604144591], + [98.9208984375, 39.842286020743394], + [95.2294921875, 40.713955826286046], + [91.7138671875, 39.87601941962116], + [90.8349609375, 37.125286284966805], + [90.3076171875, 35.88905007936091], + [90.703125, 33.284619968887675], + [92.94433593749999, 31.98944183792288], + [96.2841796875, 32.21280106801518], + [98.87695312499999, 32.0639555946604], + [102.919921875, 28.459033019728043], + [107.9736328125, 28.497660832963472], + [108.10546875, 24.206889622398023], + [109.072265625, 23.039297747769726], + [112.763671875, 24.44714958973082], + [116.54296874999999, 25.958044673317843], + ]; + + let lineCoordData = lineData.map((d: ILngLat) => { + return layer.lnglatToCoord(d); + }); + // console.log(lineCoordData) + + var material = new THREE.LineBasicMaterial({ + color: 0x0000ff, + }); + + var rawPoints: THREE.Vector3[] = []; + lineCoordData.map((d) => { + rawPoints.push(new THREE.Vector3(d[0], d[1], 0)); + }); + var curve = new THREE.CatmullRomCurve3(rawPoints); + var points = curve.getPoints(200); + var geometry = new THREE.BufferGeometry().setFromPoints(points); + + var material = new THREE.LineBasicMaterial({ color: 0xff0000 }); + + var line = new THREE.LineLoop(geometry, material); + threeScene.add(line); + + // console.log(line) + // animate({ + // from: 0, + // to: 100, + // duration: 3000, + // onUpdate: latest => console.log(latest) + // }) + // 使用 Three.js glTFLoader 加载模型 const loader = new GLTFLoader(); loader.load( @@ -73,17 +187,12 @@ export default class GlTFThreeJSDemo extends React.Component { // 'https://gw.alipayobjects.com/os/bmw-prod/3ca0a546-92d8-4ba0-a89c-017c218d5bea.gltf', (gltf) => { // 根据 GeoJSON 数据放置模型 + const gltfScene = gltf.scene.clone(); layer.getSource().data.dataArray.forEach(({ coordinates }) => { - const gltfScene = gltf.scene.clone(); - gltfScene.applyMatrix4( - // 生成模型矩阵 - layer.getModelMatrix( - [coordinates[0], coordinates[1]], // 经纬度坐标 - 0, // 高度,单位米 - [Math.PI / 2, 0, 0], // 沿 XYZ 轴旋转角度 - [100000, 100000, 100000], // 沿 XYZ 轴缩放比例 - ), - ); + layer.adjustMeshToMap(gltfScene); + gltfScene.scale.set(500000, 500000, 500000); + + // gltfScene.rotation.y = Math.PI const animations = gltf.animations; if (animations && animations.length) { @@ -105,6 +214,24 @@ export default class GlTFThreeJSDemo extends React.Component { // 向场景中添加模型 threeScene.add(gltfScene); }); + + let center = scene.getCenter(); + // layer.setObjectLngLat(gltfScene, [center.lng + 0.05, center.lat] as ILngLat, 0) + + // let t = 0 + // setInterval(() => { + // t += 0.01 + // layer.setObjectLngLat(gltfScene, [center.lng, center.lat + Math.sin(t) * 10] as ILngLat, 0) + // // layer.setObjectLngLat(model, [center.lng + 0.2, center.lat], 0) + // }, 16) + + // travel(gltfScene, points, 5000) + travelLoop(); + function travelLoop() { + travel(gltfScene, points, 5000, () => { + travelLoop(); + }); + } // 重绘图层 layer.render(); }, diff --git a/stories/3D_Model/Components/threeRender.tsx b/stories/3D_Model/Components/threeRender.tsx index 43f7cce41f..40744b13c2 100644 --- a/stories/3D_Model/Components/threeRender.tsx +++ b/stories/3D_Model/Components/threeRender.tsx @@ -37,33 +37,28 @@ export default class GlTFThreeJSDemo extends React.Component { const response = await fetch( 'https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json', ); - scene.addImage( - '00', - 'https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*Rq6tQ5b4_JMAAAAAAAAAAABkARQnAQ', - ); - scene.addImage( - '01', - 'https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*0D0SQ6AgkRMAAAAAAAAAAABkARQnAQ', - ); - scene.addImage( - '02', - 'https://gw.alipayobjects.com/zos/rmsportal/xZXhTxbglnuTmZEwqQrE.png', - ); const data = await response.json(); const imageLayer = new PointLayer() - .source(data, { - parser: { - type: 'json', - x: 'longitude', - y: 'latitude', + .source( + // [{ + // longitude: 120, + // latitude: 30 + // }] + data, + { + parser: { + type: 'json', + x: 'longitude', + y: 'latitude', + }, }, - }) - // .shape('name', ['00', '01', '02']) + ) .shape('triangle') .color('red') .active(true) - .size(20); - scene.addLayer(imageLayer); + .size(20) + .animate(true); + // scene.addLayer(imageLayer); const threeJSLayer = new ThreeLayer({ enableMultiPassRenderer: false, @@ -74,6 +69,9 @@ export default class GlTFThreeJSDemo extends React.Component { sunlight.matrixWorldNeedsUpdate = true; threeScene.add(sunlight); // 使用 Three.js glTFLoader 加载模型 + + let center = scene.getCenter(); + const loader = new GLTFLoader(); loader.load( // 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf', @@ -87,15 +85,10 @@ export default class GlTFThreeJSDemo extends React.Component { // 根据 GeoJSON 数据放置模型 layer.getSource().data.dataArray.forEach(({ coordinates }) => { const gltfScene = gltf.scene; - gltfScene.applyMatrix4( - // 生成模型矩阵 - layer.getModelMatrix( - [coordinates[0], coordinates[1]], // 经纬度坐标 - 0, // 高度,单位米/ - [Math.PI / 2, -Math.PI, 0], // 沿 XYZ 轴旋转角度 - [10, 10, 10], // 沿 XYZ 轴缩放比例 - ), - ); + + layer.adjustMeshToMap(gltfScene); + layer.setMeshScale(gltfScene, 10, 10, 10); + const animations = gltf.animations; if (animations && animations.length) { const mixer = new THREE.AnimationMixer(gltfScene); @@ -115,6 +108,17 @@ export default class GlTFThreeJSDemo extends React.Component { // 向场景中添加模型 threeScene.add(gltfScene); + + // layer.setObjectLngLat(gltfScene, [120, 30], 0) + // @ts-ignore + // console.log(layer.mapService.lngLatToCoord([121.4, 31.258134])) + + // let t = 0 + // setInterval(() => { + // t += 0.01 + // layer.setObjectLngLat(gltfScene, [center.lng, center.lat + Math.sin(t) * 0.005] as ILngLat, 0) + // // layer.setObjectLngLat(model, [center.lng + 0.2, center.lat], 0) + // }, 16) }); // 重绘图层 layer.render(); diff --git a/stories/3D_Model/model.stories.tsx b/stories/3D_Model/model.stories.tsx index b031ad6727..3a335f8e01 100644 --- a/stories/3D_Model/model.stories.tsx +++ b/stories/3D_Model/model.stories.tsx @@ -1,10 +1,12 @@ import { storiesOf } from '@storybook/react'; import * as React from 'react'; import AMapModel from './Components/amap_three'; +import AMap2Model from './Components/amap2_three'; import MapboxModel from './Components/mapbox_three'; import ThreeRender from './Components/threeRender'; storiesOf('3D 模型', module) .add('ThreeJS Render', () => , {}) .add('高德模型1.x', () => , {}) + .add('高德模型2.x', () => , {}) .add('Mapbox模型', () => , {});