feat: 新增公告牌 geometry (#1091)

* chore: update version 2.8.30 -> 2.8.31

* feat: 新增公告牌图层

* style: lint style
This commit is contained in:
YiQianYao 2022-05-05 10:32:01 +08:00 committed by GitHub
parent bfe95589e2
commit 3e7efa04a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 435 additions and 2 deletions

View File

@ -31,6 +31,7 @@ export default class GeometryLayer extends BaseLayer<
const defaultConfig = {
plane: {},
sprite: {},
billboard: {},
};
return defaultConfig[type];
}
@ -44,6 +45,8 @@ export default class GeometryLayer extends BaseLayer<
return 'plane';
} else if (shape === 'sprite') {
return 'sprite';
} else if (shape === 'billboard') {
return 'billboard';
} else {
return 'plane';
}

View File

@ -0,0 +1,217 @@
import {
AttributeType,
gl,
IAttrubuteAndElements,
IEncodeFeature,
IModelUniform,
ITexture2D,
} from '@antv/l7-core';
import { getMask, isMini } from '@antv/l7-utils';
import BaseModel from '../../core/BaseModel';
import { IGeometryLayerStyleOptions } from '../../core/interface';
import planeFrag from '../shaders/billboard_frag.glsl';
import planeVert from '../shaders/billboard_vert.glsl';
export default class BillBoardModel extends BaseModel {
protected texture: ITexture2D;
protected terrainImage: HTMLImageElement;
protected terrainImageLoaded: boolean = false;
protected mapTexture: string | undefined;
private radian: number = 0; // 旋转的弧度
public planeGeometryTriangulation = () => {
const {
center = [120, 30],
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
return {
size: 4,
indices: [0, 1, 2, 2, 3, 0],
vertices: [
...center,
...[1, 1],
...center,
...[0, 1],
...center,
...[0, 0],
...center,
...[1, 0],
],
};
};
public getUninforms(): IModelUniform {
const {
opacity,
width = 1,
height = 1,
raisingHeight = 0,
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
/**
* rotateFlag
* L7MAP 1
* MAPBOX 1
* GAODE2.x -1
* GAODE1.x -1
*/
let rotateFlag = 1;
if (
this.mapService.version === 'GAODE2.x' ||
this.mapService.version === 'GAODE1.x'
) {
rotateFlag = -1;
}
// 控制图标的旋转角度(绕 Z 轴旋转)
this.radian =
(rotateFlag * Math.PI * (this.mapService.getRotation() % 360)) / 180;
return {
u_raisingHeight: Number(raisingHeight),
u_RotateMatrix: new Float32Array([
// z
Math.cos(this.radian),
Math.sin(this.radian),
-Math.sin(this.radian),
Math.cos(this.radian),
]),
u_opacity: opacity || 1,
u_texture: this.texture,
u_size: [width, height],
};
}
public clearModels(): void {
// @ts-ignore
this.terrainImage = null;
this.texture?.destroy();
}
public initModels() {
const {
mask = false,
maskInside = true,
mapTexture,
drawCanvas,
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
this.mapTexture = mapTexture;
const { createTexture2D } = this.rendererService;
this.texture = createTexture2D({
height: 0,
width: 0,
});
if (drawCanvas) {
this.updateTexture(drawCanvas);
}
return [
this.layer.buildLayerModel({
moduleName: 'geometry_billboard',
vertexShader: planeVert,
fragmentShader: planeFrag,
triangulation: this.planeGeometryTriangulation,
primitive: gl.TRIANGLES,
// primitive: gl.LINES,
depth: { enable: true },
blend: this.getBlend(),
stencil: getMask(mask, maskInside),
}),
];
}
public buildModels() {
return this.initModels();
}
public updateTexture(drawCanvas: (canvas: HTMLCanvasElement) => void): void {
const { createTexture2D } = this.rendererService;
const {
canvasWidth = 1,
canvasHeight = 1,
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
const canvas = document.createElement('canvas');
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const ctx = canvas.getContext('2d');
if (ctx) {
drawCanvas(canvas);
this.texture = createTexture2D({
data: canvas,
width: canvas.width,
height: canvas.height,
wrapS: gl.CLAMP_TO_EDGE,
wrapT: gl.CLAMP_TO_EDGE,
});
this.layerService.updateLayerRenderList();
this.layerService.renderLayers();
}
}
protected getConfigSchema() {
return {
properties: {
opacity: {
type: 'number',
minimum: 0,
maximum: 1,
},
},
};
}
protected registerBuiltinAttributes() {
this.styleAttributeService.registerStyleAttribute({
name: 'extrude',
type: AttributeType.Attribute,
descriptor: {
name: 'a_Extrude',
buffer: {
// give the WebGL driver a hint that this buffer may change
usage: gl.DYNAMIC_DRAW,
data: [],
type: gl.FLOAT,
},
size: 3,
update: (
feature: IEncodeFeature,
featureIdx: number,
vertex: number[],
attributeIdx: number,
) => {
const extrude = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0];
const extrudeIndex = (attributeIdx % 4) * 3;
return [
extrude[extrudeIndex],
extrude[extrudeIndex + 1],
extrude[extrudeIndex + 2],
];
},
},
});
// point layer size;
this.styleAttributeService.registerStyleAttribute({
name: 'uv',
type: AttributeType.Attribute,
descriptor: {
name: 'a_Uv',
buffer: {
// give the WebGL driver a hint that this buffer may change
usage: gl.DYNAMIC_DRAW,
data: [],
type: gl.FLOAT,
},
size: 2,
update: (
feature: IEncodeFeature,
featureIdx: number,
vertex: number[],
attributeIdx: number,
) => {
return [vertex[2], vertex[3]];
},
},
});
}
}

View File

@ -1,9 +1,11 @@
import BillBoardModel from './billboard';
import PlaneModel from './plane';
import SpriteModel from './sprite';
export type GeometryModelType = 'plane' | 'sprite';
export type GeometryModelType = 'plane' | 'sprite' | 'billboard';
const GeometryModels: { [key in GeometryModelType]: any } = {
plane: PlaneModel,
sprite: SpriteModel,
billboard: BillBoardModel,
};
export default GeometryModels;

View File

@ -0,0 +1,13 @@
uniform sampler2D u_texture;
uniform float u_opacity;
varying vec3 v_Color;
varying vec2 v_uv;
#pragma include "picking"
void main() {
gl_FragColor = texture2D(u_texture, vec2(v_uv.x, 1.0 - v_uv.y));
gl_FragColor.a *= u_opacity;
gl_FragColor = filterColor(gl_FragColor);
}

View File

@ -0,0 +1,50 @@
precision highp float;
uniform mat4 u_ModelMatrix;
uniform float u_raisingHeight: 0.0;
uniform mat4 u_Mvp;
uniform float u_opacity;
uniform vec2 u_size: [1.0, 1.0];
uniform mat2 u_RotateMatrix;
attribute vec3 a_Extrude;
attribute vec3 a_Position;
attribute vec2 a_Uv;
attribute vec3 a_Color;
varying vec3 v_Color;
varying vec2 v_uv;
#pragma include "projection"
#pragma include "picking"
void main() {
vec3 extrude = a_Extrude;
v_Color = a_Color;
v_uv = a_Uv;
float raiseHeight = u_raisingHeight;
if(u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT || u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET) {
float mapboxZoomScale = 4.0/pow(2.0, 21.0 - u_Zoom);
raiseHeight = u_raisingHeight * mapboxZoomScale;
}
// 计算经纬度点位坐标
vec4 project_pos = project_position(vec4(a_Position.xy, 0.0, 1.0));
// 计算绕 z 轴旋转后的偏移
vec2 offsetXY = project_pixel(u_RotateMatrix * vec2(extrude.x * u_size.x, 0.0));
// 绕 z 轴旋转
float x = project_pos.x + offsetXY.x;
float y = project_pos.y + offsetXY.y;
// z 轴不参与旋转
float z = project_pixel(extrude.y * u_size.y + raiseHeight);
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
// gl_Position = u_Mvp * (vec4(project_pos.xy, a_Position.z, 1.0));
gl_Position = u_Mvp * (vec4(x, y, z, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(x , y, z , 1.0));
}
setPickingColor(a_PickingColor);
}

View File

@ -144,6 +144,12 @@ export interface IGeometryLayerStyleOptions {
terrainClipHeight?: number;
rgb2height?: (r: number, g: number, b: number) => number;
// billboard
raisingHeight?: number; // 抬升高度
canvasWidth?: number;
canvasHeight?: number;
drawCanvas?: (canvas: HTMLCanvasElement) => void;
// sprite
spriteAnimate?: string;
spriteRadius?: number;

View File

@ -2,7 +2,7 @@ import { container, ILayerPlugin, TYPES } from '@antv/l7-core';
import CanvasLayer from './canvas';
import CityBuildingLayer from './citybuliding/building';
import BaseLayer from './core/BaseLayer';
import GeometryLayer from './Geometry';
import GeometryLayer from './Geometry'; // 逐步替换为 Geometry
import './glsl.d';
import HeatmapLayer from './heatmap';
import ImageLayer from './image';

View File

@ -0,0 +1,140 @@
import { GeometryLayer, Scene, IMapService, PointLayer } from '@antv/l7';
import { GaodeMap, GaodeMapV2, Mapbox } from '@antv/l7-maps';
import * as React from 'react';
export default class Demo 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({
// map: new GaodeMapV2({
// map: new Mapbox({
pitch: 80,
style: 'dark',
center: [120, 30],
zoom: 5,
}),
});
this.scene = scene;
let point = new PointLayer()
.source(
[
{
lng: 120,
lat: 30,
},
],
{
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
},
)
.shape('circle')
.size(80)
.animate(true)
.color('#0ff');
let pointBar = new PointLayer({ depth: false })
.source(
[
{
lng: 120,
lat: 30,
},
],
{
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
},
)
.shape('cylinder')
.size([5, 5, 60])
.color('#0ff')
.style({
opacityLinear: {
enable: true,
dir: 'up',
},
opacity: 0.6,
heightFixed: true,
});
scene.on('loaded', () => {
scene.addLayer(point);
scene.addLayer(pointBar);
const img = new Image();
img.crossOrigin = '';
img.onload = () => {
let billboard = new GeometryLayer().shape('billboard').style({
width: 90,
height: 30,
canvasWidth: 360,
canvasHeight: 120,
center: [120, 30],
drawCanvas: (canvas: HTMLCanvasElement) => {
let { width, height } = canvas;
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
ctx.globalAlpha = 0.5;
ctx.drawImage(
img,
0,
0,
img.width,
img.height,
0,
0,
width,
height,
);
ctx.globalAlpha = 1;
ctx.fillStyle = '#0ff';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = '36px Georgia';
ctx.fillText('Hello World! 蚂蚁', width / 2, height / 2);
},
raisingHeight: 100,
});
billboard.active({
color: '#0ff',
mix: 0.5,
});
scene.addLayer(billboard);
};
img.src =
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*zMw0T6gEIZYAAAAAAAAAAAAAARQnAQ';
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -10,6 +10,7 @@ import Sprite from './components/sprite';
import PlaneTerrain from './components/planeTerrain';
import Cursor from './components/cursor';
import Arrow from './components/arrow';
import BillBoard from './components/billboard';
storiesOf('Object', module)
.add('water', () => <Water />)
@ -18,6 +19,7 @@ storiesOf('Object', module)
.add('Radar', () => <Radar/>)
.add('CanvasDemo', () => <CanvasDemo/>)
.add('Plane', () => <Plane/>)
.add('BillBoard', () => <BillBoard/>)
.add('Sprite', () => <Sprite/>)
.add('PlaneTerrain', () => <PlaneTerrain/>)
.add('Cursor', () => <Cursor/>)