* feat: planeGeometry 支持地形(地形贴图偏移)

* style: lint style

* feat: 增加 terrain clip

* style: lint sytle

* style: lint style
This commit is contained in:
YiQianYao 2022-03-25 17:43:16 +08:00 committed by GitHub
parent 5746ad3227
commit 23595672c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 279 additions and 87 deletions

View File

@ -5,6 +5,7 @@ import {
IModelUniform,
ITexture2D,
} from '@antv/l7-core';
import { Version } from '@antv/l7-maps';
import { getMask, isMini } from '@antv/l7-utils';
// import { mat4, vec3 } from 'gl-matrix';
import BaseModel from '../../core/BaseModel';
@ -12,61 +13,72 @@ import { IGeometryLayerStyleOptions } from '../../core/interface';
import planeFrag from '../shaders/plane_frag.glsl';
import planeVert from '../shaders/plane_vert.glsl';
function initPlane(
width = 1,
height = 1,
widthSegments = 1,
heightSegments = 1,
lng = 120,
lat = 30,
) {
// https://github.com/mrdoob/three.js/blob/dev/src/geometries/PlaneGeometry.js
const widthHalf = width / 2;
const heightHalf = height / 2;
const gridX = Math.floor(widthSegments);
const gridY = Math.floor(heightSegments);
const gridX1 = gridX + 1;
const gridY1 = gridY + 1;
const segmentWidth = width / gridX;
const segmentHeight = height / gridY;
const indices = [];
const positions = [];
for (let iy = 0; iy < gridY1; iy++) {
const y = iy * segmentHeight - heightHalf;
for (let ix = 0; ix < gridX1; ix++) {
const x = ix * segmentWidth - widthHalf;
positions.push(x + lng, -y + lat, 0);
positions.push(ix / gridX);
positions.push(1 - iy / gridY);
}
}
for (let iy = 0; iy < gridY; iy++) {
for (let ix = 0; ix < gridX; ix++) {
const a = ix + gridX1 * iy;
const b = ix + gridX1 * (iy + 1);
const c = ix + 1 + gridX1 * (iy + 1);
const d = ix + 1 + gridX1 * iy;
indices.push(a, b, d);
indices.push(b, c, d);
}
}
return { indices, positions };
}
export default class PlaneModel extends BaseModel {
protected texture: ITexture2D;
protected mapTexture: string | undefined;
protected positions: number[];
protected indices: number[];
public initPlane(
width = 1,
height = 1,
widthSegments = 1,
heightSegments = 1,
lng = 120,
lat = 30,
) {
// https://github.com/mrdoob/three.js/blob/dev/src/geometries/PlaneGeometry.js
const widthHalf = width / 2;
const heightHalf = height / 2;
const gridX = Math.floor(widthSegments);
const gridY = Math.floor(heightSegments);
const gridX1 = gridX + 1;
const gridY1 = gridY + 1;
const segmentWidth = width / gridX;
const segmentHeight = height / gridY;
const indices = [];
const positions = [];
for (let iy = 0; iy < gridY1; iy++) {
const y = iy * segmentHeight - heightHalf;
for (let ix = 0; ix < gridX1; ix++) {
const x = ix * segmentWidth - widthHalf;
if (this.mapService.version === Version['GAODE2.x']) {
// @ts-ignore
const [a, b] = this.mapService.lngLatToCoord([x + lng, -y + lat]) as [
number,
number,
];
positions.push(a, b, 0);
} else {
positions.push(x + lng, -y + lat, 0);
}
positions.push(ix / gridX);
positions.push(1 - iy / gridY);
}
}
for (let iy = 0; iy < gridY; iy++) {
for (let ix = 0; ix < gridX; ix++) {
const a = ix + gridX1 * iy;
const b = ix + gridX1 * (iy + 1);
const c = ix + 1 + gridX1 * (iy + 1);
const d = ix + 1 + gridX1 * iy;
indices.push(a, b, d);
indices.push(b, c, d);
}
}
return { indices, positions };
}
public planeGeometryTriangulation = () => {
const {
width = 1,
@ -74,25 +86,44 @@ export default class PlaneModel extends BaseModel {
widthSegments = 1,
heightSegments = 1,
center = [120, 30],
terrainTexture,
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
const { indices, positions } = initPlane(
const { indices, positions } = this.initPlane(
width,
height,
widthSegments,
heightSegments,
...center,
);
this.positions = positions;
this.indices = indices;
if (terrainTexture) {
// 存在地形贴图的时候会根据地形贴图对顶点进行偏移
this.loadTerrainTexture();
}
return {
vertices: positions,
indices,
size: 5,
};
};
public planeGeometryUpdateTriangulation = () => {
return {
vertices: this.positions,
indices: this.indices,
size: 5,
};
};
public getUninforms(): IModelUniform {
const {
opacity,
mapTexture,
terrainClipHeight = 0,
terrainTexture,
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
if (this.mapTexture !== mapTexture) {
this.mapTexture = mapTexture;
@ -102,7 +133,7 @@ export default class PlaneModel extends BaseModel {
return {
u_opacity: opacity || 1,
u_mapFlag: mapTexture ? 1 : 0,
u_terrainClipHeight: terrainTexture ? terrainClipHeight : -1,
u_texture: this.texture,
// u_ModelMatrix: mat4.translate(mat4.create(), mat4.create(), [1, 0, 0])
// u_ModelMatrix: mat4.rotateZ(mat4.create(), mat4.create(), 10)
@ -111,17 +142,6 @@ export default class PlaneModel extends BaseModel {
};
}
// public rotateZ(): mat4 {
// const res = mat4.create()
// const roZero = mat4.translate(mat4.create(), mat4.create(), [-120, 0, -30])
// const rotate = mat4.rotateZ(mat4.create(), mat4.create(), 10)
// const roOrigin = mat4.translate(mat4.create(), mat4.create(), [120, 0, 30])
// mat4.multiply(res, res, roZero)
// mat4.multiply(res, res, rotate)
// mat4.multiply(res, res, roOrigin)
// return res
// }
public clearModels(): void {
this.texture.destroy();
}
@ -149,13 +169,89 @@ export default class PlaneModel extends BaseModel {
fragmentShader: planeFrag,
triangulation: this.planeGeometryTriangulation,
primitive: gl.TRIANGLES,
// primitive: gl.POINTS,
depth: { enable: false },
depth: { enable: true },
blend: this.getBlend(),
stencil: getMask(mask, maskInside),
}),
];
}
public getImageData(img: HTMLImageElement) {
const canvas: HTMLCanvasElement = document.createElement('canvas');
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
const { width, height } = img;
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height);
return imageData;
}
/**
* load terrain texture & offset attribute z
*/
public loadTerrainTexture(): void {
const {
mask = false,
maskInside = true,
widthSegments = 1,
heightSegments = 1,
terrainTexture,
rgb2height = (r: number, g: number, b: number) => r + g + b,
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
const terrainImage = new Image();
terrainImage.crossOrigin = 'anonymous';
terrainImage.onload = () => {
const imgWidth = terrainImage.width;
const imgHeight = terrainImage.height;
const imageData = this.getImageData(terrainImage).data;
const gridX = Math.floor(widthSegments);
const gridY = Math.floor(heightSegments);
const gridX1 = gridX + 1;
const gridY1 = gridY + 1;
const widthStep = imgWidth / gridX;
const heihgtStep = imgHeight / gridY;
for (let iy = 0; iy < gridY1; iy++) {
const imgIndexY = Math.floor(iy * heihgtStep);
const imgLen = imgIndexY * imgWidth;
for (let ix = 0; ix < gridX1; ix++) {
const imgIndexX = Math.floor(ix * widthStep);
const imgDataIndex = (imgLen + imgIndexX) * 4;
const r = imageData[imgDataIndex];
const g = imageData[imgDataIndex + 1];
const b = imageData[imgDataIndex + 2];
const z = (iy * gridX1 + ix) * 5 + 2;
this.positions[z] = rgb2height(r, g, b);
}
}
this.layer.models = [
this.layer.buildLayerModel({
moduleName: 'geometry_plane',
vertexShader: planeVert,
fragmentShader: planeFrag,
triangulation: this.planeGeometryUpdateTriangulation,
primitive: gl.TRIANGLES,
depth: { enable: true },
blend: this.getBlend(),
stencil: getMask(mask, maskInside),
}),
];
this.layerService.renderLayers();
};
terrainImage.src = terrainTexture as string;
}
public buildModels() {
return this.initModels();
}

View File

@ -5,6 +5,7 @@ uniform float u_opacity;
varying vec3 v_Color;
varying vec2 v_uv;
varying float v_clip;
#pragma include "picking"
void main() {
@ -12,10 +13,10 @@ void main() {
if(u_mapFlag > 0.0) {
gl_FragColor = texture2D(u_texture, vec2(v_uv.x, 1.0 - v_uv.y));
gl_FragColor.a *= u_opacity;
} else {
// gl_FragColor = vec4(v_uv, 0.0, u_opacity);
gl_FragColor = vec4(v_Color, u_opacity);
}
gl_FragColor.a *= v_clip;
gl_FragColor = filterColor(gl_FragColor);
}

View File

@ -2,6 +2,8 @@ precision highp float;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
uniform float u_opacity;
uniform float u_terrainClipHeight;
attribute vec3 a_Position;
attribute vec2 a_Uv;
@ -9,6 +11,7 @@ attribute vec3 a_Color;
varying vec3 v_Color;
varying vec2 v_uv;
varying float v_clip;
#pragma include "projection"
#pragma include "picking"
@ -17,6 +20,12 @@ void main() {
v_uv = a_Uv;
vec4 project_pos = project_position(vec4(a_Position, 1.0));
v_clip = 1.0;
if(a_Position.z < u_terrainClipHeight) {
v_clip = 0.0;
}
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
// float x = 1.0;
@ -35,12 +44,10 @@ void main() {
// );
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(project_pos.xy, 0., 1.0));
gl_Position = u_Mvp * (vec4(project_pos.xy, a_Position.z, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy, 0., 1.0));
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy, a_Position.z, 1.0));
}
setPickingColor(a_PickingColor);
gl_PointSize = 10.0;
}

View File

@ -106,6 +106,7 @@ export interface IGeometryLayerStyleOptions {
maskInside?: boolean;
mapTexture?: string;
terrainTexture?: string;
// planeGeometry
center?: [number, number];
@ -114,6 +115,9 @@ export interface IGeometryLayerStyleOptions {
widthSegments?: number;
heightSegments?: number;
terrainClipHeight?: number;
rgb2height?: (r: number, g: number, b: number) => number;
}
export enum CanvasUpdateType {

View File

@ -180,7 +180,7 @@ export default class DataMappingPlugin implements ILayerPlugin {
// TODO: 避免经纬度被重复计算导致坐标位置偏移
.filter((d) => !d.originCoordinates)
.map((d) => {
d.version = 'GAODE2.x';
d.version = Version['GAODE2.x'];
// @ts-ignore
d.originCoordinates = cloneDeep(d.coordinates); // 为了兼容高德1.x 需要保存一份原始的经纬度坐标数据(许多上层逻辑依赖经纬度数据)
// @ts-ignore
@ -193,7 +193,7 @@ export default class DataMappingPlugin implements ILayerPlugin {
// TODO: 避免经纬度被重复计算导致坐标位置偏移
.filter((d) => !d.originCoordinates)
.map((d) => {
d.version = 'GAODE2.x';
d.version = Version['GAODE2.x'];
// @ts-ignore
d.originCoordinates = cloneDeep(d.coordinates); // 为了兼容高德1.x 需要保存一份原始的经纬度坐标数据(许多上层逻辑依赖经纬度数据)
// @ts-ignore

View File

@ -1,5 +1,5 @@
import { GeometryLayer, Scene, IMapService } from '@antv/l7';
import { GaodeMap, Mapbox } from '@antv/l7-maps';
import { GaodeMap, GaodeMapV2, Mapbox } from '@antv/l7-maps';
import * as React from 'react';
export default class Demo extends React.Component {
@ -13,7 +13,8 @@ export default class Demo extends React.Component {
public async componentDidMount() {
const scene = new Scene({
id: 'map',
map: new GaodeMap({
// map: new GaodeMap({
map: new GaodeMapV2({
// map: new Mapbox({
pitch: 0,
// style: 'dark',
@ -49,16 +50,16 @@ export default class Demo extends React.Component {
scene.on('loaded', () => {
scene.addLayer(layer);
setTimeout(() => {
// layer.style({
// mapTexture: ""
// })
layer.style({
mapTexture:
'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*GJhASbfQTK8AAAAAAAAAAAAAARQnAQ',
});
scene.render();
}, 2000);
// setTimeout(() => {
// // layer.style({
// // mapTexture: ""
// // })
// layer.style({
// mapTexture:
// 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*GJhASbfQTK8AAAAAAAAAAAAAARQnAQ',
// });
// scene.render();
// }, 2000);
});
}

View File

@ -0,0 +1,81 @@
import { GeometryLayer, Scene, IMapService } from '@antv/l7';
import { GaodeMap, Mapbox, GaodeMapV2 } 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: 60,
center: [120.1025, 30.2594],
rotation: 220,
zoom: 14,
}),
});
this.scene = scene;
let layer = new GeometryLayer()
.style({
width: 0.074,
height: 0.061,
center: [120.1025, 30.2594],
widthSegments: 200,
heightSegments: 200,
terrainClipHeight: 1,
mapTexture:
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*gA0NRbuOF5cAAAAAAAAAAAAAARQnAQ',
terrainTexture:
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*eYFaRYlnnOUAAAAAAAAAAAAAARQnAQ',
rgb2height: (r: number, g: number, b: number) => {
let h =
-10000.0 +
(r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) * 0.1;
h = h / 20 - 127600;
h = Math.max(0, h);
return h;
},
})
.color('#ff0');
scene.on('loaded', () => {
scene.addLayer(layer);
// setTimeout(() => {
// // layer.style({
// // mapTexture: ""
// // })
// layer.style({
// mapTexture:
// 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*GJhASbfQTK8AAAAAAAAAAAAAARQnAQ',
// });
// scene.render();
// }, 2000);
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -6,6 +6,7 @@ import Taifong from './components/taifeng'
import Radar from './components/radar';
import CanvasDemo from './components/canvas';
import Plane from './components/plane';
import PlaneTerrain from './components/planeTerrain';
storiesOf('Object', module)
.add('water', () => <Water />)
@ -13,4 +14,5 @@ storiesOf('Object', module)
.add('Taifong', () => <Taifong />)
.add('Radar', () => <Radar/>)
.add('CanvasDemo', () => <CanvasDemo/>)
.add('Plane', () => <Plane/>)
.add('Plane', () => <Plane/>)
.add('PlaneTerrain', () => <PlaneTerrain/>)