mirror of https://gitee.com/antv-l7/antv-l7
commit
a4c974261c
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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<IThreeRenderService>(
|
||||
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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
},
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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', () => <ThreeRender />, {})
|
||||
.add('高德模型1.x', () => <AMapModel />, {})
|
||||
.add('高德模型2.x', () => <AMap2Model />, {})
|
||||
.add('Mapbox模型', () => <MapboxModel />, {});
|
||||
|
|
Loading…
Reference in New Issue