mirror of https://gitee.com/antv-l7/antv-l7
commit
58448178d5
|
@ -103,6 +103,7 @@
|
|||
"rollup-pluginutils": "^2.8.2",
|
||||
"sass-loader": "^7.1.0",
|
||||
"style-loader": "^1.0.0",
|
||||
"three": "0.115.0",
|
||||
"styled-components": "^3.4.6",
|
||||
"stylelint": "^9.5.0",
|
||||
"stylelint-config-recommended": "^2.1.0",
|
||||
|
|
|
@ -24,8 +24,6 @@ const bytesPerElementMap = {
|
|||
[gl.UNSIGNED_SHORT]: 2,
|
||||
};
|
||||
|
||||
let counter = 0;
|
||||
|
||||
/**
|
||||
* 每个 Layer 都拥有一个,用于管理样式属性的注册和更新
|
||||
*/
|
||||
|
@ -41,11 +39,8 @@ export default class StyleAttributeService implements IStyleAttributeService {
|
|||
private readonly rendererService: IRendererService;
|
||||
|
||||
private attributes: IStyleAttribute[] = [];
|
||||
|
||||
private triangulation: Triangulation;
|
||||
|
||||
private c = counter++;
|
||||
|
||||
private featureLayout: {
|
||||
sizePerElement: number;
|
||||
elements: Array<{
|
||||
|
@ -186,7 +181,7 @@ export default class StyleAttributeService implements IStyleAttributeService {
|
|||
|
||||
public createAttributesAndIndices(
|
||||
features: IEncodeFeature[],
|
||||
triangulation?: Triangulation,
|
||||
triangulation: Triangulation,
|
||||
): {
|
||||
attributes: {
|
||||
[attributeName: string]: IAttribute;
|
||||
|
|
|
@ -10,7 +10,11 @@ export interface IPoint {
|
|||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface IMercator {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
}
|
||||
export interface IStatusOptions {
|
||||
showIndoorMap: boolean;
|
||||
resizeEnable: boolean;
|
||||
|
@ -73,6 +77,14 @@ export interface IMapService<RawMap = {}> {
|
|||
lngLatToPixel(lnglat: Point): IPoint;
|
||||
containerToLngLat(pixel: Point): ILngLat;
|
||||
lngLatToContainer(lnglat: Point): IPoint;
|
||||
lngLatToMercator(lnglat: [number, number], altitude: number): IMercator;
|
||||
getModelMatrix(
|
||||
lnglat: [number, number],
|
||||
altitude: number,
|
||||
rotate: [number, number, number],
|
||||
scale: [number, number, number],
|
||||
origin: IMercator,
|
||||
): number[];
|
||||
exportMap(type: 'jpg' | 'png'): string;
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,13 @@ export interface IRendererService {
|
|||
): void;
|
||||
getViewportSize(): { width: number; height: number };
|
||||
getContainer(): HTMLElement | null;
|
||||
getCanvas(): HTMLCanvasElement | null;
|
||||
getGLContext(): WebGLRenderingContext;
|
||||
viewport(size: { x: number; y: number; width: number; height: number }): void;
|
||||
readPixels(options: IReadPixelsOptions): Uint8Array;
|
||||
setBaseState(): void;
|
||||
setCustomLayerDefaults(): void;
|
||||
setDirty(flag: boolean): void;
|
||||
getDirty(): boolean;
|
||||
destroy(): void;
|
||||
}
|
||||
|
|
|
@ -79,6 +79,8 @@ export interface ITexture2DInitializationOptions {
|
|||
}
|
||||
|
||||
export interface ITexture2D {
|
||||
get(): unknown;
|
||||
update(): void;
|
||||
resize(options: { width: number; height: number }): void;
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,6 +5,7 @@ import { IRenderConfig } from '../renderer/IRendererService';
|
|||
|
||||
export interface ISceneService {
|
||||
destroyed: boolean;
|
||||
loaded: boolean;
|
||||
on(type: string, handle: (...args: any[]) => void): void;
|
||||
off(type: string, handle: (...args: any[]) => void): void;
|
||||
removeAllListeners(event?: string): this;
|
||||
|
|
|
@ -34,6 +34,8 @@ import { ISceneService } from './ISceneService';
|
|||
export default class Scene extends EventEmitter implements ISceneService {
|
||||
public destroyed: boolean = false;
|
||||
|
||||
public loaded: boolean = false;
|
||||
|
||||
@inject(TYPES.SceneID)
|
||||
private readonly id: string;
|
||||
/**
|
||||
|
@ -219,6 +221,7 @@ export default class Scene extends EventEmitter implements ISceneService {
|
|||
this.logger.info(' render inited');
|
||||
this.layerService.initLayers();
|
||||
this.controlService.addControls();
|
||||
this.loaded = true;
|
||||
this.emit('loaded');
|
||||
this.inited = true;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"dependencies": {
|
||||
"@antv/l7": "^2.2.0",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"@turf/turf": "^5.1.6",
|
||||
"@turf/circle": "^6.0.1",
|
||||
"@turf/distance": "^6.0.1",
|
||||
"@turf/helpers": "^6.1.4",
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
"inversify": "^5.0.1",
|
||||
"lodash": "^4.17.15",
|
||||
"merge-json-schemas": "1.0.0",
|
||||
"polyline-miter-util": "^1.0.1",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -5,6 +5,8 @@ import {
|
|||
gl,
|
||||
IActiveOption,
|
||||
IAnimateOption,
|
||||
ICameraService,
|
||||
ICoordinateSystemService,
|
||||
IDataState,
|
||||
IEncodeFeature,
|
||||
IFontService,
|
||||
|
@ -118,6 +120,10 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
@lazyInject(TYPES.IShaderModuleService)
|
||||
protected readonly shaderModuleService: IShaderModuleService;
|
||||
|
||||
protected cameraService: ICameraService;
|
||||
|
||||
protected coordinateService: ICoordinateSystemService;
|
||||
|
||||
protected iconService: IIconService;
|
||||
|
||||
protected fontService: IFontService;
|
||||
|
@ -252,6 +258,12 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
TYPES.IInteractionService,
|
||||
);
|
||||
this.mapService = this.container.get<IMapService>(TYPES.IMapService);
|
||||
this.cameraService = this.container.get<ICameraService>(
|
||||
TYPES.ICameraService,
|
||||
);
|
||||
this.coordinateService = this.container.get<ICoordinateSystemService>(
|
||||
TYPES.ICoordinateSystemService,
|
||||
);
|
||||
this.postProcessingPassFactory = this.container.get(
|
||||
TYPES.IFactoryPostProcessingPass,
|
||||
);
|
||||
|
|
|
@ -20,6 +20,9 @@ export default class ImageModel extends BaseModel {
|
|||
|
||||
public getUninforms(): IModelUniform {
|
||||
const { opacity } = this.layer.getLayerConfig() as IImageLayerStyleOptions;
|
||||
if (this.rendererService.getDirty()) {
|
||||
this.texture.update();
|
||||
}
|
||||
return {
|
||||
u_opacity: opacity || 1.0,
|
||||
u_texture: this.texture,
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
ILogService,
|
||||
IMapConfig,
|
||||
IMapService,
|
||||
IMercator,
|
||||
IPoint,
|
||||
IStatusOptions,
|
||||
IViewport,
|
||||
|
@ -18,6 +19,7 @@ import {
|
|||
TYPES,
|
||||
} from '@antv/l7-core';
|
||||
import { DOM } from '@antv/l7-utils';
|
||||
import { mat4, vec2, vec3 } from 'gl-matrix';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { IAMapEvent, IAMapInstance } from '../../typings/index';
|
||||
import './logo.css';
|
||||
|
@ -241,6 +243,45 @@ export default class AMapService
|
|||
};
|
||||
}
|
||||
|
||||
public lngLatToMercator(
|
||||
lnglat: [number, number],
|
||||
altitude: number,
|
||||
): IMercator {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
};
|
||||
}
|
||||
|
||||
public getModelMatrix(
|
||||
lnglat: [number, number],
|
||||
altitude: number,
|
||||
rotate: [number, number, number],
|
||||
scale: [number, number, number] = [1, 1, 1],
|
||||
origin: IMercator = { x: 0, y: 0, z: 0 },
|
||||
): number[] {
|
||||
const flat = this.viewport.projectFlat(lnglat);
|
||||
// @ts-ignore
|
||||
const modelMatrix = mat4.create();
|
||||
|
||||
mat4.translate(
|
||||
modelMatrix,
|
||||
modelMatrix,
|
||||
vec3.fromValues(flat[0], flat[1], altitude),
|
||||
);
|
||||
mat4.scale(
|
||||
modelMatrix,
|
||||
modelMatrix,
|
||||
vec3.fromValues(scale[0], scale[1], scale[2]),
|
||||
);
|
||||
|
||||
mat4.rotateX(modelMatrix, modelMatrix, rotate[0]);
|
||||
mat4.rotateY(modelMatrix, modelMatrix, rotate[1]);
|
||||
mat4.rotateZ(modelMatrix, modelMatrix, rotate[2]);
|
||||
|
||||
return (modelMatrix as unknown) as number[];
|
||||
}
|
||||
public async init(): Promise<void> {
|
||||
const {
|
||||
id,
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
ILogService,
|
||||
IMapConfig,
|
||||
IMapService,
|
||||
IMercator,
|
||||
IPoint,
|
||||
IStatusOptions,
|
||||
IViewport,
|
||||
|
@ -18,6 +19,7 @@ import {
|
|||
TYPES,
|
||||
} from '@antv/l7-core';
|
||||
import { DOM } from '@antv/l7-utils';
|
||||
import { mat4, vec2, vec3 } from 'gl-matrix';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import mapboxgl, { IControl, Map } from 'mapbox-gl';
|
||||
|
||||
|
@ -234,6 +236,53 @@ export default class MapboxService
|
|||
public lngLatToContainer(lnglat: [number, number]): IPoint {
|
||||
return this.map.project(lnglat);
|
||||
}
|
||||
public lngLatToMercator(
|
||||
lnglat: [number, number],
|
||||
altitude: number,
|
||||
): IMercator {
|
||||
const { x = 0, y = 0, z = 0 } = mapboxgl.MercatorCoordinate.fromLngLat(
|
||||
lnglat,
|
||||
altitude,
|
||||
);
|
||||
return { x, y, z };
|
||||
}
|
||||
public getModelMatrix(
|
||||
lnglat: [number, number],
|
||||
altitude: number,
|
||||
rotate: [number, number, number],
|
||||
scale: [number, number, number] = [1, 1, 1],
|
||||
origin: IMercator = { x: 0, y: 0, z: 0 },
|
||||
): number[] {
|
||||
const modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat(
|
||||
lnglat,
|
||||
altitude,
|
||||
);
|
||||
// @ts-ignore
|
||||
const meters = modelAsMercatorCoordinate.meterInMercatorCoordinateUnits();
|
||||
const modelMatrix = mat4.create();
|
||||
|
||||
mat4.translate(
|
||||
modelMatrix,
|
||||
modelMatrix,
|
||||
vec3.fromValues(
|
||||
modelAsMercatorCoordinate.x - origin.x,
|
||||
modelAsMercatorCoordinate.y - origin.y,
|
||||
modelAsMercatorCoordinate.z || 0 - origin.z,
|
||||
),
|
||||
);
|
||||
|
||||
mat4.scale(
|
||||
modelMatrix,
|
||||
modelMatrix,
|
||||
vec3.fromValues(meters * scale[0], -meters * scale[1], meters * scale[2]),
|
||||
);
|
||||
|
||||
mat4.rotateX(modelMatrix, modelMatrix, rotate[0]);
|
||||
mat4.rotateY(modelMatrix, modelMatrix, rotate[1]);
|
||||
mat4.rotateZ(modelMatrix, modelMatrix, rotate[2]);
|
||||
|
||||
return (modelMatrix as unknown) as number[];
|
||||
}
|
||||
|
||||
public async init(): Promise<void> {
|
||||
const {
|
||||
|
|
|
@ -15,6 +15,8 @@ import {
|
|||
*/
|
||||
export default class ReglTexture2D implements ITexture2D {
|
||||
private texture: regl.Texture2D;
|
||||
private width: number;
|
||||
private height: number;
|
||||
|
||||
constructor(reGl: regl.Regl, options: ITexture2DInitializationOptions) {
|
||||
const {
|
||||
|
@ -34,6 +36,8 @@ export default class ReglTexture2D implements ITexture2D {
|
|||
min = gl.NEAREST,
|
||||
colorSpace = gl.BROWSER_DEFAULT_WEBGL,
|
||||
} = options;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
const textureOptions: regl.Texture2DOptions = {
|
||||
width,
|
||||
|
@ -69,9 +73,15 @@ export default class ReglTexture2D implements ITexture2D {
|
|||
public get() {
|
||||
return this.texture;
|
||||
}
|
||||
public update() {
|
||||
// @ts-ignore
|
||||
this.texture._texture.bind();
|
||||
}
|
||||
|
||||
public resize({ width, height }: { width: number; height: number }): void {
|
||||
this.texture.resize(width, height);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
|
|
|
@ -36,6 +36,9 @@ import ReglTexture2D from './ReglTexture2D';
|
|||
export default class ReglRendererService implements IRendererService {
|
||||
private gl: regl.Regl;
|
||||
private $container: HTMLDivElement | null;
|
||||
private width: number;
|
||||
private height: number;
|
||||
private isDirty: boolean;
|
||||
|
||||
public async init(
|
||||
$container: HTMLDivElement,
|
||||
|
@ -146,6 +149,8 @@ export default class ReglRendererService implements IRendererService {
|
|||
renderCanvas.style.height = height / 2 + 'px';
|
||||
}
|
||||
this.gl._gl.viewport(x, y, width, height);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.gl._refresh();
|
||||
};
|
||||
|
||||
|
@ -174,6 +179,47 @@ export default class ReglRendererService implements IRendererService {
|
|||
return this.$container;
|
||||
};
|
||||
|
||||
public getCanvas = () => {
|
||||
return this.$container?.getElementsByTagName('canvas')[0] || null;
|
||||
};
|
||||
|
||||
public getGLContext = () => {
|
||||
return this.gl._gl;
|
||||
};
|
||||
|
||||
public setBaseState() {
|
||||
this.gl({
|
||||
cull: {
|
||||
enable: false,
|
||||
face: 'back',
|
||||
},
|
||||
viewport: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
height: this.width,
|
||||
width: this.height,
|
||||
},
|
||||
blend: {
|
||||
enable: false,
|
||||
equation: 'add',
|
||||
},
|
||||
framebuffer: null,
|
||||
});
|
||||
this.gl._refresh();
|
||||
}
|
||||
public setCustomLayerDefaults() {
|
||||
const gl = this.getGLContext();
|
||||
gl.disable(gl.CULL_FACE);
|
||||
}
|
||||
|
||||
public setDirty(flag: boolean): void {
|
||||
this.isDirty = flag;
|
||||
}
|
||||
|
||||
public getDirty(): boolean {
|
||||
return this.isDirty;
|
||||
}
|
||||
|
||||
public destroy = () => {
|
||||
// @see https://github.com/regl-project/regl/blob/gh-pages/API.md#clean-up
|
||||
this.gl.destroy();
|
||||
|
|
|
@ -103,6 +103,9 @@ class Scene
|
|||
|
||||
this.initControl();
|
||||
}
|
||||
public getServiceContainer(): Container {
|
||||
return this.container;
|
||||
}
|
||||
public getSize(): [number, number] {
|
||||
return this.mapService.getSize();
|
||||
}
|
||||
|
@ -133,6 +136,18 @@ class Scene
|
|||
return this.sceneService.exportPng(type);
|
||||
}
|
||||
|
||||
public registerRenderService(render: any) {
|
||||
if (this.sceneService.loaded) {
|
||||
const renderSerivce = new render(this);
|
||||
renderSerivce.init();
|
||||
} else {
|
||||
this.on('loaded', () => {
|
||||
const renderSerivce = new render(this);
|
||||
renderSerivce.init();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get map() {
|
||||
return this.mapService.map;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# `three`
|
||||
|
||||
> L7 ThreeJS
|
||||
通过ThreeJS 扩展加载3D模型
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
import three = require('l7-three');
|
||||
|
||||
```
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"name": "@antv/l7-three",
|
||||
"version": "2.1.15",
|
||||
"description": "three for L7 ",
|
||||
"keywords": ["3D", "L7", "three"],
|
||||
"author": "thinkinggis <lzx199065@gmail.com>",
|
||||
"license": "ISC",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"unpkg": "dist/l7-tree.js",
|
||||
"types": "es/index.d.ts",
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "__tests__"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"lib",
|
||||
"es",
|
||||
"README.md"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/antvis/L7.git"
|
||||
},
|
||||
"scripts": {
|
||||
"tsc": "tsc --project tsconfig.build.json",
|
||||
"clean": "rimraf dist; rimraf es; rimraf lib;",
|
||||
"build": "run-p build:*",
|
||||
"build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --out-dir lib --source-maps --extensions .ts,.tsx --delete-dir-on-start --no-comments",
|
||||
"build:esm": "BABEL_ENV=esm babel src --root-mode upward --out-dir es --source-maps --extensions .ts,.tsx --delete-dir-on-start --no-comments",
|
||||
"watch": "BABEL_ENV=cjs babel src --watch --root-mode upward --out-dir lib --source-maps --extensions .ts,.tsx --delete-dir-on-start --no-comments",
|
||||
"build:cdn": "node_modules/.bin/rollup -c",
|
||||
"lint:ts": "run-p -c lint:ts-*",
|
||||
"test": "jest"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/antvis/L7/issues"
|
||||
},
|
||||
"homepage": "https://github.com/antvis/L7#readme",
|
||||
"dependencies": {
|
||||
"@antv/l7": "2.2.0",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"rollup": "^2.3.3",
|
||||
"rollup-plugin-less": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"three": "^0.115.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
import pkg from './package.json';
|
||||
import typescript from 'rollup-plugin-typescript';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import buble from 'rollup-plugin-buble';
|
||||
import postcss from 'rollup-plugin-postcss';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import url from 'postcss-url';
|
||||
export default {
|
||||
input: './src/index.ts',
|
||||
plugins: [
|
||||
// less(),
|
||||
typescript({
|
||||
exclude: 'node_modules/**',
|
||||
typescript: require('typescript')
|
||||
}),
|
||||
resolve({
|
||||
preferBuiltins: false
|
||||
}),
|
||||
postcss({
|
||||
plugins: [
|
||||
url({ url: 'inline' })
|
||||
]
|
||||
}),
|
||||
commonjs({
|
||||
namedExports: {
|
||||
eventemitter3: [ 'EventEmitter' ],
|
||||
lodash: [ 'merge' ]
|
||||
}
|
||||
}),
|
||||
buble({
|
||||
transforms: { generator: false }
|
||||
}),
|
||||
terser()
|
||||
],
|
||||
external: [
|
||||
'@antv/l7'
|
||||
],
|
||||
output: [
|
||||
{
|
||||
format: 'umd',
|
||||
name: 'L7-Three',
|
||||
file: pkg.unpkg,
|
||||
sourcemap: true,
|
||||
globals: {
|
||||
'@antv/l7': 'L7'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
import { BaseLayer, ILayer, IMercator } from '@antv/l7';
|
||||
import {
|
||||
AnimationMixer,
|
||||
Camera,
|
||||
Matrix4,
|
||||
PCFSoftShadowMap,
|
||||
PerspectiveCamera,
|
||||
Scene,
|
||||
WebGLRenderer,
|
||||
} from 'three';
|
||||
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;
|
||||
}>
|
||||
implements IThreeJSLayer {
|
||||
public type: string = 'custom';
|
||||
protected threeRenderService: IThreeRenderService;
|
||||
private scene: Scene = new Scene();
|
||||
private renderer: WebGLRenderer;
|
||||
private animateMixer: AnimationMixer[] = [];
|
||||
// 地图中点墨卡托坐标
|
||||
private center: IMercator;
|
||||
|
||||
// 初始状态相机变换矩阵
|
||||
|
||||
/**
|
||||
* 根据模型
|
||||
*/
|
||||
public getModelMatrix(
|
||||
lnglat: [number, number],
|
||||
altitude: number = 0,
|
||||
rotation: [number, number, number] = [0, 0, 0],
|
||||
scale: [number, number, number] = [1, 1, 1],
|
||||
): Matrix4 {
|
||||
return new Matrix4().fromArray(
|
||||
this.mapService.getModelMatrix(
|
||||
lnglat,
|
||||
altitude,
|
||||
rotation,
|
||||
scale,
|
||||
this.threeRenderService.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public buildModels() {
|
||||
this.threeRenderService = this.getContainer().get<IThreeRenderService>(
|
||||
ThreeRenderServiceType,
|
||||
);
|
||||
const config = this.getLayerConfig();
|
||||
if (config && config.onAddMeshes) {
|
||||
config.onAddMeshes(this.scene, this);
|
||||
}
|
||||
}
|
||||
public renderModels() {
|
||||
const gl = this.rendererService.getGLContext();
|
||||
this.rendererService.setCustomLayerDefaults();
|
||||
const cullFace =
|
||||
this.mapService.constructor.name === 'AMapService' ? gl.BACK : gl.FRONT;
|
||||
gl.cullFace(cullFace);
|
||||
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());
|
||||
});
|
||||
this.rendererService.setBaseState();
|
||||
this.rendererService.setDirty(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public renderAMapModels() {
|
||||
const gl = this.rendererService.getGLContext();
|
||||
// gl.frontFace(gl.CCW);
|
||||
// gl.enable(gl.CULL_FACE);
|
||||
// gl.cullFace(gl.BACK);
|
||||
this.rendererService.setCustomLayerDefaults();
|
||||
const renderer = this.threeRenderService.renderer;
|
||||
renderer.state.reset();
|
||||
renderer.autoClear = false;
|
||||
renderer.render(this.scene, this.threeRenderService.getRenderCamera());
|
||||
this.animateMixer.forEach((mixer: AnimationMixer) => {
|
||||
mixer.update(this.getTime());
|
||||
});
|
||||
this.rendererService.setBaseState();
|
||||
this.rendererService.setDirty(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public addAnimateMixer(mixer: AnimationMixer) {
|
||||
this.animateMixer.push(mixer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { Scene } from '@antv/l7';
|
||||
import {
|
||||
AnimationMixer,
|
||||
Camera,
|
||||
Matrix4,
|
||||
PCFSoftShadowMap,
|
||||
PerspectiveCamera,
|
||||
Scene as ThreeScene,
|
||||
WebGLRenderer,
|
||||
} from 'three';
|
||||
import {
|
||||
IThreeRenderService,
|
||||
ThreeRenderService,
|
||||
ThreeRenderServiceType,
|
||||
} from './threeRenderService';
|
||||
|
||||
export default class ThreeRender {
|
||||
private threeRenderService: IThreeRenderService;
|
||||
constructor(scene: Scene) {
|
||||
const sceneContainer = scene.getServiceContainer();
|
||||
sceneContainer
|
||||
.bind<IThreeRenderService>(ThreeRenderServiceType)
|
||||
.to(ThreeRenderService)
|
||||
.inSingletonScope();
|
||||
|
||||
this.threeRenderService = sceneContainer.get<IThreeRenderService>(
|
||||
ThreeRenderServiceType,
|
||||
);
|
||||
}
|
||||
public init() {
|
||||
this.threeRenderService.init();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
import { IMapService, IMercator, IRendererService, TYPES } from '@antv/l7';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import {
|
||||
AnimationMixer,
|
||||
Camera,
|
||||
Matrix4,
|
||||
PCFSoftShadowMap,
|
||||
PerspectiveCamera,
|
||||
Scene as ThreeScene,
|
||||
WebGLRenderer,
|
||||
} from 'three';
|
||||
const DEG2RAD = Math.PI / 180;
|
||||
export interface IThreeRenderService {
|
||||
renderer: WebGLRenderer;
|
||||
camera: Camera;
|
||||
center: IMercator;
|
||||
init(): void;
|
||||
getRenderCamera(): Camera;
|
||||
}
|
||||
export const ThreeRenderServiceType = Symbol.for('ThreeJSRenderService');
|
||||
@injectable()
|
||||
export class ThreeRenderService implements IThreeRenderService {
|
||||
public renderer: WebGLRenderer;
|
||||
public camera: Camera;
|
||||
public center: IMercator;
|
||||
private scene: ThreeScene;
|
||||
|
||||
// 初始状态相机变换矩阵
|
||||
private cameraTransform: Matrix4;
|
||||
|
||||
@inject(TYPES.IRendererService)
|
||||
private readonly rendererService: IRendererService;
|
||||
|
||||
@inject(TYPES.IMapService)
|
||||
private readonly mapService: IMapService;
|
||||
|
||||
public init() {
|
||||
const canvas = this.rendererService.getCanvas() as HTMLCanvasElement;
|
||||
const gl = this.rendererService.getGLContext();
|
||||
if (canvas && gl) {
|
||||
const center = this.mapService.getCenter();
|
||||
this.center = this.mapService.lngLatToMercator(
|
||||
[center.lng, center.lat],
|
||||
0,
|
||||
);
|
||||
}
|
||||
const { x, y, z } = this.center;
|
||||
this.cameraTransform = new Matrix4().makeTranslation(x, y, z);
|
||||
this.renderer = new WebGLRenderer({
|
||||
canvas,
|
||||
context: gl,
|
||||
antialias: true,
|
||||
});
|
||||
|
||||
this.renderer.autoClear = false;
|
||||
// 是否需要 gamma correction?
|
||||
this.renderer.gammaFactor = 2.2;
|
||||
this.renderer.shadowMap.enabled = true;
|
||||
// this.renderer.shadowMap.type = PCFSoftShadowMap;
|
||||
|
||||
this.scene = new ThreeScene();
|
||||
this.camera = new PerspectiveCamera(45, 1, 1, 2000000);
|
||||
}
|
||||
public getRenderCamera(): Camera {
|
||||
return this.mapService.constructor.name === 'AMapService'
|
||||
? this.AMapCamera()
|
||||
: this.mapboxCamera();
|
||||
}
|
||||
|
||||
private mapboxCamera(): Camera {
|
||||
const mercatorMatrix = new Matrix4().fromArray(
|
||||
// @ts-ignore
|
||||
this.mapService.map.transform.customLayerMatrix(),
|
||||
);
|
||||
this.camera.projectionMatrix = mercatorMatrix.multiply(
|
||||
this.cameraTransform,
|
||||
);
|
||||
return this.camera;
|
||||
}
|
||||
|
||||
private AMapCamera(): Camera {
|
||||
// @ts-ignore
|
||||
const mapCamera = this.mapService.map.getCameraState();
|
||||
const camera = this.camera;
|
||||
let { pitch, rotation } = mapCamera;
|
||||
const { fov, near, far, height, aspect } = mapCamera;
|
||||
pitch *= DEG2RAD;
|
||||
rotation *= DEG2RAD;
|
||||
// @ts-ignore
|
||||
camera.fov = (180 * fov) / Math.PI;
|
||||
// @ts-ignore
|
||||
camera.aspect = aspect;
|
||||
// @ts-ignore
|
||||
camera.near = near;
|
||||
// @ts-ignore
|
||||
camera.far = far;
|
||||
// @ts-ignore
|
||||
camera.updateProjectionMatrix();
|
||||
camera.position.z = height * Math.cos(pitch);
|
||||
camera.position.x = height * Math.sin(pitch) * Math.sin(rotation);
|
||||
camera.position.y = -height * Math.sin(pitch) * Math.cos(rotation);
|
||||
camera.up.x = -Math.cos(pitch) * Math.sin(rotation);
|
||||
camera.up.y = Math.cos(pitch) * Math.cos(rotation);
|
||||
camera.up.z = Math.sin(pitch);
|
||||
camera.lookAt(0, 0, 0);
|
||||
camera.position.x += mapCamera.position.x;
|
||||
camera.position.y += -mapCamera.position.y;
|
||||
return camera;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import ThreeLayer from './core/baseLayer';
|
||||
import ThreeRender from './core/threeRender';
|
||||
export { ThreeLayer, ThreeRender };
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"extends": "../../tsconfig.build.json",
|
||||
"compilerOptions": {
|
||||
"declarationDir": "./es",
|
||||
"rootDir": "./src",
|
||||
"baseUrl": "./"
|
||||
},
|
||||
"include": ["./src"]
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
import { Scene } from '@antv/l7';
|
||||
import { GaodeMap, Mapbox } from '@antv/l7-maps';
|
||||
import { ThreeLayer, ThreeRender } 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 GaodeMap({
|
||||
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) => {
|
||||
// 根据 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 轴缩放比例
|
||||
),
|
||||
);
|
||||
const animations = gltf.animations;
|
||||
if (animations && animations.length) {
|
||||
const mixer = new THREE.AnimationMixer(gltfScene);
|
||||
// @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(gltfScene);
|
||||
});
|
||||
// 重绘图层
|
||||
layer.render();
|
||||
},
|
||||
);
|
||||
},
|
||||
})
|
||||
.source({
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
properties: {},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [111.4453125, 32.84267363195431],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.animate(true);
|
||||
scene.addLayer(threeJSLayer);
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
import { PolygonLayer, Scene } from '@antv/l7';
|
||||
import { GaodeMap, Mapbox } from '@antv/l7-maps';
|
||||
import { ThreeLayer, ThreeRender } 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 scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [112, 35.39847],
|
||||
pitch: 45,
|
||||
rotation: 30,
|
||||
zoom: 5,
|
||||
}),
|
||||
});
|
||||
this.scene = scene;
|
||||
scene.registerRenderService(ThreeRender);
|
||||
scene.on('loaded', async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json',
|
||||
);
|
||||
const data = await response.json();
|
||||
const polygonlayer = new PolygonLayer({
|
||||
name: '01',
|
||||
});
|
||||
|
||||
polygonlayer
|
||||
.source(data)
|
||||
.color('name', [
|
||||
'#2E8AE6',
|
||||
'#69D1AB',
|
||||
'#DAF291',
|
||||
'#FFD591',
|
||||
'#FF7A45',
|
||||
'#CF1D49',
|
||||
])
|
||||
.shape('fill')
|
||||
.select(true)
|
||||
.style({
|
||||
opacity: 1.0,
|
||||
});
|
||||
scene.addLayer(polygonlayer);
|
||||
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) => {
|
||||
// 根据 GeoJSON 数据放置模型
|
||||
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 轴缩放比例
|
||||
),
|
||||
);
|
||||
|
||||
const animations = gltf.animations;
|
||||
if (animations && animations.length) {
|
||||
const mixer = new THREE.AnimationMixer(gltfScene);
|
||||
// @ts-ignore
|
||||
for (let i = 0; i < animations.length; i++) {
|
||||
const animation = animations[i];
|
||||
|
||||
// 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(gltfScene);
|
||||
});
|
||||
// 重绘图层
|
||||
layer.render();
|
||||
},
|
||||
);
|
||||
},
|
||||
})
|
||||
.source({
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
properties: {},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [112, 35.39847],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.animate(true);
|
||||
scene.addLayer(threeJSLayer);
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
import { PointLayer, Scene } from '@antv/l7';
|
||||
import { GaodeMap, Mapbox } from '@antv/l7-maps';
|
||||
import { ThreeLayer, ThreeRender } 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 Mapbox({
|
||||
center: [121.4, 31.258134],
|
||||
pitch: 45,
|
||||
rotation: 30,
|
||||
zoom: 15,
|
||||
}),
|
||||
});
|
||||
scene.registerRenderService(ThreeRender);
|
||||
this.scene = scene;
|
||||
scene.on('loaded', async () => {
|
||||
// scene.registerRenderService(ThreeRender);
|
||||
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',
|
||||
},
|
||||
})
|
||||
.shape('name', ['00', '01', '02'])
|
||||
// .shape('triangle')
|
||||
// .color('red')
|
||||
.active(true)
|
||||
.size(20);
|
||||
scene.addLayer(imageLayer);
|
||||
|
||||
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) => {
|
||||
// 根据 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 轴缩放比例
|
||||
),
|
||||
);
|
||||
const animations = gltf.animations;
|
||||
if (animations && animations.length) {
|
||||
const mixer = new THREE.AnimationMixer(gltfScene);
|
||||
// @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(gltfScene);
|
||||
});
|
||||
// 重绘图层
|
||||
layer.render();
|
||||
},
|
||||
);
|
||||
},
|
||||
})
|
||||
.source({
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
properties: {},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [121.4, 31.258134],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.animate(true);
|
||||
scene.addLayer(threeJSLayer);
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import { storiesOf } from '@storybook/react';
|
||||
import * as React from 'react';
|
||||
import AMapModel from './Components/amap_three';
|
||||
import MapboxModel from './Components/mapbox_three';
|
||||
import ThreeRender from './Components/threeRender';
|
||||
|
||||
storiesOf('3D 模型', module)
|
||||
.add('ThreeJS Render', () => <ThreeRender />, {})
|
||||
.add('高德模型', () => <AMapModel />, {})
|
||||
.add('Mapbox模型', () => <MapboxModel />, {});
|
|
@ -2,6 +2,7 @@ import { RasterLayer, Scene } from '@antv/l7';
|
|||
import { Mapbox } from '@antv/l7-maps';
|
||||
import * as dat from 'dat.gui';
|
||||
// @ts-ignore
|
||||
// tslint:disable-next-line:no-submodule-imports
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
import * as React from 'react';
|
||||
import { colorScales } from '../lib/colorscales';
|
||||
|
|
27
yarn.lock
27
yarn.lock
|
@ -202,6 +202,19 @@
|
|||
resolved "https://registry.npmjs.org/@antv/gl-matrix/-/gl-matrix-2.7.1.tgz#acb8e37f7ab3df01345aba4372d7942be42eba14"
|
||||
integrity sha512-oOWcVNlpELIKi9x+Mm1Vwbz8pXfkbJKykoCIOJ/dNK79hSIANbpXJ5d3Rra9/wZqK6MC961B7sybFhPlLraT3Q==
|
||||
|
||||
"@antv/l7@2.1.15":
|
||||
version "2.1.15"
|
||||
resolved "https://registry.npmjs.org/@antv/l7/-/l7-2.1.15.tgz#27121b8f22838b5ad17114a1b2895389b6b0a800"
|
||||
integrity sha512-goR3M1e2YXXEo5jp/QYd1VF9rKoAy/DlKrJ5KLE/jhzuqgw9Kp0iHSOaGE+M7tFIyL8hCoG8JXHmAK1tVyeaIw==
|
||||
dependencies:
|
||||
"@antv/l7-component" "^2.1.15"
|
||||
"@antv/l7-core" "^2.1.15"
|
||||
"@antv/l7-layers" "^2.1.15"
|
||||
"@antv/l7-maps" "^2.1.15"
|
||||
"@antv/l7-scene" "^2.1.15"
|
||||
"@antv/l7-utils" "^2.1.15"
|
||||
"@babel/runtime" "^7.7.7"
|
||||
|
||||
"@antv/scale@~0.1.1":
|
||||
version "0.1.5"
|
||||
resolved "https://registry.npmjs.org/@antv/scale/-/scale-0.1.5.tgz#243266e8b9047cf64b2fdfc40f9834cf0846496e"
|
||||
|
@ -12647,7 +12660,7 @@ gl-matrix@^3.0.0, gl-matrix@^3.1.0:
|
|||
resolved "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.3.0.tgz#232eef60b1c8b30a28cbbe75b2caf6c48fd6358b"
|
||||
integrity sha512-COb7LDz+SXaHtl/h4LeaFcNdJdAQSDeVqjiIihSXNrkWObZLhDI4hIkZC11Aeqp7bcE72clzB0BnDXr2SmslRA==
|
||||
|
||||
gl-vec2@^1.3.0:
|
||||
gl-vec2@^1.0.0, gl-vec2@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/gl-vec2/-/gl-vec2-1.3.0.tgz#83d472ed46034de8e09cbc857123fb6c81c51199"
|
||||
integrity sha512-YiqaAuNsheWmUV0Sa8k94kBB0D6RWjwZztyO+trEYS8KzJ6OQB/4686gdrf59wld4hHFIvaxynO3nRxpk1Ij/A==
|
||||
|
@ -19002,6 +19015,13 @@ polished@^3.3.1:
|
|||
dependencies:
|
||||
"@babel/runtime" "^7.8.7"
|
||||
|
||||
polyline-miter-util@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/polyline-miter-util/-/polyline-miter-util-1.0.1.tgz#b693f2389ea0ded36a6bcf5ecd2ece4b6917d957"
|
||||
integrity sha1-tpPyOJ6g3tNqa89ezS7OS2kX2Vc=
|
||||
dependencies:
|
||||
gl-vec2 "^1.0.0"
|
||||
|
||||
popper.js@^1.14.4, popper.js@^1.14.7:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
|
||||
|
@ -24042,6 +24062,11 @@ threads@^1.3.1:
|
|||
optionalDependencies:
|
||||
tiny-worker ">= 2"
|
||||
|
||||
three@0.115.0, three@^0.115.0:
|
||||
version "0.115.0"
|
||||
resolved "https://registry.npmjs.org/three/-/three-0.115.0.tgz#540d800c381b9da2334c024f0fbe4d23f84eb05e"
|
||||
integrity sha512-mAV2Ky3RdcbdSbR9capI+tKLvRldWYxd4151PZTT/o7+U2jh9Is3a4KmnYwzyUAhB2ZA3pXSgCd2DOY4Tj5kow==
|
||||
|
||||
throat@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
|
||||
|
|
Loading…
Reference in New Issue