mirror of https://gitee.com/antv-l7/antv-l7
feat: 3D model
This commit is contained in:
parent
e42149bdb2
commit
377ae5c036
|
@ -183,8 +183,5 @@
|
|||
"tnpm": {
|
||||
"mode": "yarn"
|
||||
},
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@turf/turf": "^5.1.6"
|
||||
}
|
||||
"version": "0.0.0"
|
||||
}
|
||||
|
|
|
@ -60,5 +60,7 @@ export interface IRendererService {
|
|||
getGLContext(): WebGLRenderingContext;
|
||||
viewport(size: { x: number; y: number; width: number; height: number }): void;
|
||||
readPixels(options: IReadPixelsOptions): Uint8Array;
|
||||
setBaseState(): void;
|
||||
setCustomLayerDefaults(): void;
|
||||
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;
|
||||
/**
|
||||
|
@ -228,6 +230,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;
|
||||
}
|
||||
|
|
|
@ -36,9 +36,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@antv/l7": "2.1.14",
|
||||
"@antv/l7-component": "2.1.14",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"@turf/circle": "^6.0.1",
|
||||
"@turf/turf": "^5.1.6",
|
||||
"@turf/distance": "^6.0.1",
|
||||
"@turf/helpers": "^6.1.4",
|
||||
"@turf/midpoint": "^5.1.5",
|
||||
|
|
|
@ -20,6 +20,7 @@ export default class ImageModel extends BaseModel {
|
|||
|
||||
public getUninforms(): IModelUniform {
|
||||
const { opacity } = this.layer.getLayerConfig() as IImageLayerStyleOptions;
|
||||
this.texture.update();
|
||||
return {
|
||||
u_opacity: opacity || 1.0,
|
||||
u_texture: this.texture,
|
||||
|
|
|
@ -4,17 +4,27 @@
|
|||
* @see https://github.com/peterqliu/threebox/blob/master/examples/Object3D.html
|
||||
*/
|
||||
import { IMercator } from '@antv/l7-core';
|
||||
import { Camera, Matrix4, Scene, WebGLRenderer } from 'three';
|
||||
import {
|
||||
AnimationMixer,
|
||||
Camera,
|
||||
Matrix4,
|
||||
PCFSoftShadowMap,
|
||||
PerspectiveCamera,
|
||||
Scene,
|
||||
WebGLRenderer,
|
||||
} from 'three';
|
||||
import BaseLayer from '../core/BaseLayer';
|
||||
|
||||
const DEG2RAD = Math.PI / 180;
|
||||
export default class ThreeJSLayer extends BaseLayer<{
|
||||
onAddMeshes: (threeScene: Scene, layer: ThreeJSLayer) => void;
|
||||
}> {
|
||||
public name: string = 'ThreeJSLayer';
|
||||
public type: string = 'custom';
|
||||
|
||||
private scene: Scene;
|
||||
private camera: Camera;
|
||||
private renderer: WebGLRenderer;
|
||||
private animateMixer: AnimationMixer[] = [];
|
||||
|
||||
// 地图中点墨卡托坐标
|
||||
private center: IMercator;
|
||||
|
@ -63,12 +73,12 @@ export default class ThreeJSLayer extends BaseLayer<{
|
|||
// L7 负责 clear
|
||||
this.renderer.autoClear = false;
|
||||
// 是否需要 gamma correction?
|
||||
this.renderer.gammaOutput = true;
|
||||
this.renderer.gammaFactor = 2.2;
|
||||
this.renderer.shadowMap.enabled = true;
|
||||
// this.renderer.shadowMap.type = PCFSoftShadowMap;
|
||||
|
||||
this.scene = new Scene();
|
||||
// 后续同步 L7 相机
|
||||
this.camera = new Camera();
|
||||
this.camera = new PerspectiveCamera(45, 1, 1, 2000000);
|
||||
|
||||
const config = this.getLayerConfig();
|
||||
if (config && config.onAddMeshes) {
|
||||
|
@ -76,10 +86,14 @@ export default class ThreeJSLayer extends BaseLayer<{
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public renderModels() {
|
||||
const { width, height } = this.rendererService.getViewportSize();
|
||||
this.renderer.setSize(width, height, false);
|
||||
return this.mapService.constructor.name === 'AMapService'
|
||||
? this.renderAMapModels()
|
||||
: this.renderMapboxModels();
|
||||
}
|
||||
public renderMapboxModels() {
|
||||
// const { width, height } = this.rendererService.getViewportSize();
|
||||
// this.renderer.setSize(width, height, false);
|
||||
|
||||
const gl = this.rendererService.getGLContext();
|
||||
gl.frontFace(gl.CCW);
|
||||
|
@ -97,9 +111,57 @@ export default class ThreeJSLayer extends BaseLayer<{
|
|||
);
|
||||
this.renderer.state.reset();
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
this.rendererService.setBaseState();
|
||||
this.animateMixer.forEach((mixer: AnimationMixer) => {
|
||||
mixer.update(this.getTime());
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public renderAMapModels() {
|
||||
const gl = this.rendererService.getGLContext();
|
||||
gl.frontFace(gl.CCW);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
gl.cullFace(gl.BACK);
|
||||
|
||||
// @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;
|
||||
this.renderer.state.reset();
|
||||
this.renderer.autoClear = false;
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
this.animateMixer.forEach((mixer: AnimationMixer) => {
|
||||
mixer.update(this.getTime());
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public addAnimateMixer(mixer: AnimationMixer) {
|
||||
this.animateMixer.push(mixer);
|
||||
}
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
properties: {
|
||||
|
|
|
@ -258,10 +258,29 @@ export default class AMapService
|
|||
lnglat: [number, number],
|
||||
altitude: number,
|
||||
rotate: [number, number, number],
|
||||
scale: [number, number, number],
|
||||
origin: IMercator,
|
||||
scale: [number, number, number] = [1, 1, 1],
|
||||
origin: IMercator = { x: 0, y: 0, z: 0 },
|
||||
): number[] {
|
||||
return (mat4.create() as unknown) as 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 {
|
||||
|
|
|
@ -240,13 +240,12 @@ export default class MapboxService
|
|||
lnglat: [number, number],
|
||||
altitude: number,
|
||||
): IMercator {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
};
|
||||
const { x = 0, y = 0, z = 0 } = mapboxgl.MercatorCoordinate.fromLngLat(
|
||||
lnglat,
|
||||
altitude,
|
||||
);
|
||||
return { x, y, z };
|
||||
}
|
||||
|
||||
public getModelMatrix(
|
||||
lnglat: [number, number],
|
||||
altitude: number,
|
||||
|
|
|
@ -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,8 @@ import ReglTexture2D from './ReglTexture2D';
|
|||
export default class ReglRendererService implements IRendererService {
|
||||
private gl: regl.Regl;
|
||||
private $container: HTMLDivElement | null;
|
||||
private width: number;
|
||||
private height: number;
|
||||
|
||||
public async init(
|
||||
$container: HTMLDivElement,
|
||||
|
@ -146,6 +148,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();
|
||||
};
|
||||
|
||||
|
@ -182,6 +186,31 @@ export default class ReglRendererService implements IRendererService {
|
|||
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 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,48 @@
|
|||
{
|
||||
"name": "@antv/l7-three",
|
||||
"version": "2.1.14",
|
||||
"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": {
|
||||
"three": "^0.115.0",
|
||||
"@antv/l7": "2.1.14",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"rollup": "^2.3.3",
|
||||
"rollup-plugin-less": "^1.1.2"
|
||||
}
|
||||
}
|
|
@ -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,108 @@
|
|||
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();
|
||||
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();
|
||||
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.applyMatrix(
|
||||
// 生成模型矩阵
|
||||
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 { Scene, PolygonLayer, ThreeJSLayer } 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.applyMatrix(
|
||||
// 生成模型矩阵
|
||||
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.applyMatrix(
|
||||
// 生成模型矩阵
|
||||
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 />, {});
|
|
@ -7,6 +7,7 @@ import {
|
|||
DirectionalLight,
|
||||
Mesh,
|
||||
MeshLambertMaterial,
|
||||
Matrix4,
|
||||
Scene as ThreeScene,
|
||||
} from 'three';
|
||||
// @ts-ignore
|
||||
|
@ -71,7 +72,11 @@ export default class ThreeJSLayerComponent extends React.Component {
|
|||
});
|
||||
const cube = new Mesh(geometry, redMaterial);
|
||||
cube.applyMatrix(
|
||||
layer.getModelMatrix([120.19382669582967, 30.258134], 10, [0, 0, 0]),
|
||||
layer.getModelMatrix([120.19382669582967, 30.258134], 10, [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]) as Matrix4,
|
||||
);
|
||||
cube.frustumCulled = false;
|
||||
threeScene.add(cube);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Scene, ThreeJSLayer } from '@antv/l7';
|
||||
import { Mapbox } from '@antv/l7-maps';
|
||||
import * as React from 'react';
|
||||
import { DirectionalLight, Scene as ThreeScene } from 'three';
|
||||
import { DirectionalLight, Matrix4, Scene as ThreeScene } from 'three';
|
||||
// tslint:disable-next-line:no-submodule-imports
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||
|
||||
|
@ -25,7 +25,7 @@ export default class GlTFThreeJSDemo extends React.Component {
|
|||
center: [121.434765, 31.256735],
|
||||
pitch: 45,
|
||||
rotation: 30,
|
||||
zoom: 16,
|
||||
zoom: 18,
|
||||
}),
|
||||
});
|
||||
this.scene = scene;
|
||||
|
@ -46,7 +46,8 @@ export default class GlTFThreeJSDemo extends React.Component {
|
|||
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://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf',
|
||||
'https://gw.alipayobjects.com/os/antvdemo/assets/gltf/radar/34M_17.gltf',
|
||||
// 'https://gw.alipayobjects.com/os/bmw-prod/3ca0a546-92d8-4ba0-a89c-017c218d5bea.gltf',
|
||||
(gltf) => {
|
||||
// 根据 GeoJSON 数据放置模型
|
||||
layer.getSource().data.dataArray.forEach(({ coordinates }) => {
|
||||
|
|
Loading…
Reference in New Issue