diff --git a/packages/layers/src/core/triangulation.ts b/packages/layers/src/core/triangulation.ts index 5b87370196..5ad994618f 100644 --- a/packages/layers/src/core/triangulation.ts +++ b/packages/layers/src/core/triangulation.ts @@ -3,6 +3,12 @@ import { aProjectFlat, lngLatToMeters } from '@antv/l7-utils'; import earcut from 'earcut'; // @ts-ignore import { mat4, vec3 } from 'gl-matrix'; +import { + EARTH_RADIUS, + EARTH_SEGMENTS, + lglt2xyz, + primitiveSphere, +} from '../earth/utils'; import ExtrudePolyline from '../utils/extrude_polyline'; import { calculateCentroid } from '../utils/geo'; import extrudePolygon, { @@ -22,9 +28,6 @@ interface IGeometryCache { } const GeometryCache: IGeometryCache = {}; -// 地球网格半径 -const EARTH_RADIUS = 100; -const EARTH_SEGMENTS = 36; /** * 计算2D 填充点图顶点 * @param feature 映射feature @@ -52,33 +55,12 @@ export function GlobelPointFillTriangulation(feature: IEncodeFeature) { }; } -function torad(deg: number) { - return (deg / 180) * Math.acos(-1); -} -/** - * 经纬度转xyz - * @param longitude 经度 - * @param latitude 纬度 - * @param radius 半径 - */ -function lglt2xyz(lnglat: [number, number]) { - // TODO: + Math.PI/2 是为了对齐坐标 - const lng = torad(lnglat[0]) + Math.PI / 2; - const lat = torad(lnglat[1]); - - const z = EARTH_RADIUS * Math.cos(lat) * Math.cos(lng); - const x = EARTH_RADIUS * Math.cos(lat) * Math.sin(lng); - const y = EARTH_RADIUS * Math.sin(lat); - return [x, y, z]; -} - /** * 计算3D 拉伸点图 * @param feature 映射feature */ export function PointExtrudeTriangulation(feature: IEncodeFeature) { const { shape } = feature; - // console.log('PointExtrudeTriangulation', feature) const { positions, index, normals } = getGeometry( shape as ShapeType3D, false, @@ -329,7 +311,6 @@ function getGeometry(shape: ShapeType3D, needFlat = false): IExtrudeGeomety { : geometryShape.cylinder(); const geometry = extrude_PolygonNormal([path], needFlat); GeometryCache[shape] = geometry; - // console.log('geometry', geometry) return geometry; } @@ -425,8 +406,8 @@ function addDir(dirX: number, dirY: number) { * @returns */ export function earthTriangulation() { - const mesh = primitiveSphere(EARTH_RADIUS, { segments: EARTH_SEGMENTS }); - const { positionsArr, indicesArr, normalArr } = mesh; + const earthmesh = primitiveSphere(EARTH_RADIUS, { segments: EARTH_SEGMENTS }); + const { positionsArr, indicesArr, normalArr } = earthmesh; return { vertices: positionsArr, indices: indicesArr, @@ -434,119 +415,3 @@ export function earthTriangulation() { normals: normalArr, }; } - -/** - * 构建地球球体网格 - * @param radius - * @param opt - * @returns - */ -function primitiveSphere( - radius: number, - opt: { - segments: number; - }, -) { - const matRotY = mat4.create(); - const matRotZ = mat4.create(); - const up = vec3.fromValues(0, 1, 0); - const tmpVec3 = vec3.fromValues(0, 0, 0); - - opt = opt || {}; - radius = typeof radius !== 'undefined' ? radius : 1; - const segments = typeof opt.segments !== 'undefined' ? opt.segments : 32; - - const totalZRotationSteps = 2 + segments; - const totalYRotationSteps = 2 * totalZRotationSteps; - - const indices = []; - const indicesArr = []; - const positions = []; - const positionsArr = []; - const normals = []; - const normalArr = []; - const uvs = []; - - for ( - let zRotationStep = 0; - zRotationStep <= totalZRotationSteps; - zRotationStep++ - ) { - const normalizedZ = zRotationStep / totalZRotationSteps; - const angleZ = normalizedZ * Math.PI; - - for ( - let yRotationStep = 0; - yRotationStep <= totalYRotationSteps; - yRotationStep++ - ) { - const normalizedY = yRotationStep / totalYRotationSteps; - const angleY = normalizedY * Math.PI * 2; - - mat4.identity(matRotZ); - mat4.rotateZ(matRotZ, matRotZ, -angleZ); - - mat4.identity(matRotY); - mat4.rotateY(matRotY, matRotY, angleY); - - vec3.transformMat4(tmpVec3, up, matRotZ); - vec3.transformMat4(tmpVec3, tmpVec3, matRotY); - - vec3.scale(tmpVec3, tmpVec3, -radius); - - positions.push(tmpVec3.slice()); - positionsArr.push(...tmpVec3.slice()); - - vec3.normalize(tmpVec3, tmpVec3); - normals.push(tmpVec3.slice()); - normalArr.push(...tmpVec3.slice()); - - uvs.push([normalizedY, 1 - normalizedZ]); - - // position 和 uv 一起存储 - positionsArr.push(normalizedY, 1 - normalizedZ); - } - - if (zRotationStep > 0) { - const verticesCount = positions.length; - let firstIndex = verticesCount - 2 * (totalYRotationSteps + 1); - for ( - ; - firstIndex + totalYRotationSteps + 2 < verticesCount; - firstIndex++ - ) { - indices.push([ - firstIndex, - firstIndex + 1, - firstIndex + totalYRotationSteps + 1, - ]); - - indicesArr.push( - firstIndex, - firstIndex + 1, - firstIndex + totalYRotationSteps + 1, - ); - indices.push([ - firstIndex + totalYRotationSteps + 1, - firstIndex + 1, - firstIndex + totalYRotationSteps + 2, - ]); - indicesArr.push( - firstIndex + totalYRotationSteps + 1, - firstIndex + 1, - firstIndex + totalYRotationSteps + 2, - ); - } - } - } - - return { - cells: indices, - positions, - normals, - uvs, - positionsArr, - indicesArr, - normalArr, - }; -} diff --git a/packages/layers/src/earth/utils.ts b/packages/layers/src/earth/utils.ts new file mode 100644 index 0000000000..43b87524cc --- /dev/null +++ b/packages/layers/src/earth/utils.ts @@ -0,0 +1,147 @@ +import { mat4, vec3 } from 'gl-matrix'; +// 该文件专门记录地球模式的数值 + +// 地球网格半径 +export const EARTH_RADIUS = 100; +export const EARTH_SEGMENTS = 36; + +/** + * 角度转弧度 + * @param deg + * @returns + */ +function torad(deg: number) { + return (deg / 180) * Math.acos(-1); +} +/** + * 经纬度转xyz + * @param longitude 经度 + * @param latitude 纬度 + * @param radius 半径 + */ +export function lglt2xyz(lnglat: [number, number]) { + // TODO: + Math.PI/2 是为了对齐坐标 + const lng = torad(lnglat[0]) + Math.PI / 2; + const lat = torad(lnglat[1]); + + const z = EARTH_RADIUS * Math.cos(lat) * Math.cos(lng); + const x = EARTH_RADIUS * Math.cos(lat) * Math.sin(lng); + const y = EARTH_RADIUS * Math.sin(lat); + return [x, y, z]; +} + +/** + * 构建地球球体网格 + * @param radius + * @param opt + * @returns + */ +export function primitiveSphere( + radius: number, + opt: { + segments: number; + }, +) { + const matRotY = mat4.create(); + const matRotZ = mat4.create(); + const up = vec3.fromValues(0, 1, 0); + const tmpVec3 = vec3.fromValues(0, 0, 0); + + opt = opt || {}; + radius = typeof radius !== 'undefined' ? radius : 1; + const segments = typeof opt.segments !== 'undefined' ? opt.segments : 32; + + const totalZRotationSteps = 2 + segments; + const totalYRotationSteps = 2 * totalZRotationSteps; + + const indices = []; + const indicesArr = []; + const positions = []; + const positionsArr = []; + const normals = []; + const normalArr = []; + const uvs = []; + + for ( + let zRotationStep = 0; + zRotationStep <= totalZRotationSteps; + zRotationStep++ + ) { + const normalizedZ = zRotationStep / totalZRotationSteps; + const angleZ = normalizedZ * Math.PI; + + for ( + let yRotationStep = 0; + yRotationStep <= totalYRotationSteps; + yRotationStep++ + ) { + const normalizedY = yRotationStep / totalYRotationSteps; + const angleY = normalizedY * Math.PI * 2; + + mat4.identity(matRotZ); + mat4.rotateZ(matRotZ, matRotZ, -angleZ); + + mat4.identity(matRotY); + mat4.rotateY(matRotY, matRotY, angleY); + + vec3.transformMat4(tmpVec3, up, matRotZ); + vec3.transformMat4(tmpVec3, tmpVec3, matRotY); + + vec3.scale(tmpVec3, tmpVec3, -radius); + + positions.push(tmpVec3.slice()); + positionsArr.push(...tmpVec3.slice()); + + vec3.normalize(tmpVec3, tmpVec3); + normals.push(tmpVec3.slice()); + normalArr.push(...tmpVec3.slice()); + + uvs.push([normalizedY, 1 - normalizedZ]); + + // position 和 uv 一起存储 + positionsArr.push(normalizedY, 1 - normalizedZ); + } + + if (zRotationStep > 0) { + const verticesCount = positions.length; + let firstIndex = verticesCount - 2 * (totalYRotationSteps + 1); + for ( + ; + firstIndex + totalYRotationSteps + 2 < verticesCount; + firstIndex++ + ) { + indices.push([ + firstIndex, + firstIndex + 1, + firstIndex + totalYRotationSteps + 1, + ]); + + indicesArr.push( + firstIndex, + firstIndex + 1, + firstIndex + totalYRotationSteps + 1, + ); + indices.push([ + firstIndex + totalYRotationSteps + 1, + firstIndex + 1, + firstIndex + totalYRotationSteps + 2, + ]); + indicesArr.push( + firstIndex + totalYRotationSteps + 1, + firstIndex + 1, + firstIndex + totalYRotationSteps + 2, + ); + } + } + } + + return { + cells: indices, + positions, + normals, + uvs, + positionsArr, + indicesArr, + normalArr, + }; +} diff --git a/packages/layers/src/point/models/extrude.ts b/packages/layers/src/point/models/extrude.ts index a59978c241..9146bcb567 100644 --- a/packages/layers/src/point/models/extrude.ts +++ b/packages/layers/src/point/models/extrude.ts @@ -2,6 +2,7 @@ import { AttributeType, gl, IEncodeFeature, IModel } from '@antv/l7-core'; import { isNumber } from 'lodash'; import BaseModel, { styleOffset, styleSingle } from '../../core/BaseModel'; import { PointExtrudeTriangulation } from '../../core/triangulation'; +import { lglt2xyz } from '../../earth/utils'; import { calculateCentroid } from '../../utils/geo'; import pointExtrudeFrag from '../shaders/extrude_frag.glsl'; import pointExtrudeVert from '../shaders/extrude_vert.glsl'; @@ -51,6 +52,9 @@ export default class ExtrudeModel extends BaseModel { }); } return { + // TODO: 判断当前的点图层的模型是普通地图模式还是地球模式 + u_globel: this.mapService.version === 'GLOBEL' ? 1 : 0, + u_dataTexture: this.dataTexture, // 数据纹理 - 有数据映射的时候纹理中带数据,若没有任何数据映射时纹理是 [1] u_cellTypeLayout: this.getCellTypeLayout(), // u_opacity: opacity || 1.0, @@ -70,6 +74,7 @@ export default class ExtrudeModel extends BaseModel { fragmentShader: pointExtrudeFrag, triangulation: PointExtrudeTriangulation, blend: this.getBlend(), + // primitive: gl.POINTS, }), ]; } @@ -77,6 +82,8 @@ export default class ExtrudeModel extends BaseModel { this.dataTexture?.destroy(); } protected registerBuiltinAttributes() { + // TODO: 判断当前的点图层的模型是普通地图模式还是地球模式 + const isGlobel = this.mapService.version === 'GLOBEL'; // point layer size; this.styleAttributeService.registerStyleAttribute({ name: 'size', @@ -104,7 +111,7 @@ export default class ExtrudeModel extends BaseModel { size.length === 2 ? [size[0], size[0], size[1]] : size; } if (!Array.isArray(size)) { - buffersize = [size]; + buffersize = [size, size, size]; } return buffersize; } else { @@ -152,7 +159,16 @@ export default class ExtrudeModel extends BaseModel { size: 3, update: (feature: IEncodeFeature, featureIdx: number) => { const coordinates = calculateCentroid(feature.coordinates); - return [coordinates[0], coordinates[1], 0]; + if (isGlobel) { + // TODO: 在地球模式下需要将传入 shader 的经纬度转化成对应的 xyz 坐标 + return lglt2xyz([coordinates[0], coordinates[1]]) as [ + number, + number, + number, + ]; + } else { + return [coordinates[0], coordinates[1], 0]; + } }, }, }); diff --git a/packages/layers/src/point/shaders/extrude_vert.glsl b/packages/layers/src/point/shaders/extrude_vert.glsl index 320e17e91a..8b4fb17da2 100644 --- a/packages/layers/src/point/shaders/extrude_vert.glsl +++ b/packages/layers/src/point/shaders/extrude_vert.glsl @@ -1,5 +1,6 @@ precision highp float; +#define pi 3.1415926535 #define ambientRatio 0.5 #define diffuseRatio 0.3 #define specularRatio 0.2 @@ -10,6 +11,7 @@ attribute vec4 a_Color; attribute vec3 a_Size; attribute vec3 a_Normal; +uniform float u_globel; uniform mat4 u_ModelMatrix; uniform mat4 u_Mvp; varying vec4 v_color; @@ -25,6 +27,22 @@ varying mat4 styleMappingMat; // 用于将在顶点着色器中计算好的样 #pragma include "light" #pragma include "picking" +float getYRadian(float x, float z) { + if(x > 0.0 && z > 0.0) { + return atan(x/z); + } else if(x > 0.0 && z <= 0.0){ + return atan(-z/x) + pi/2.0; + } else if(x <= 0.0 && z <= 0.0) { + return pi + atan(x/z); //atan(x/z) + + } else { + return atan(z/-x) + pi*3.0/2.0; + } +} + +float getXRadian(float y, float r) { + return atan(y/r); +} + void main() { // cal style mapping - 数据纹理映射部分的计算 @@ -70,5 +88,32 @@ void main() { } else { gl_Position = project_common_position_to_clipspace(pos); } + + if(u_globel > 0.0) { + // 在地球模式下,将原本垂直于 xy 平面的圆柱调整姿态到适应圆的角度 + //旋转矩阵mx,创建绕x轴旋转矩阵 + float r = sqrt(a_Pos.z*a_Pos.z + a_Pos.x*a_Pos.x); + float xRadian = getXRadian(a_Pos.y, r); + float xcos = cos(xRadian);//求解旋转角度余弦值 + float xsin = sin(xRadian);//求解旋转角度正弦值 + mat4 mx = mat4( + 1,0,0,0, + 0,xcos,-xsin,0, + 0,xsin,xcos,0, + 0,0,0,1); + + //旋转矩阵my,创建绕y轴旋转矩阵 + float yRadian = getYRadian(a_Pos.x, a_Pos.z); + float ycos = cos(yRadian);//求解旋转角度余弦值 + float ysin = sin(yRadian);//求解旋转角度正弦值 + mat4 my = mat4( + ycos,0,-ysin,0, + 0,1,0,0, + ysin,0,ycos,0, + 0,0,0,1); + + gl_Position = u_ViewProjectionMatrix * vec4(( my * mx * vec4(a_Position * a_Size, 1.0)).xyz + a_Pos, 1.0); + } + setPickingColor(a_PickingColor); } diff --git a/packages/layers/src/point/shaders/fill_frag.glsl b/packages/layers/src/point/shaders/fill_frag.glsl index 2ca23d81a7..37faf08849 100644 --- a/packages/layers/src/point/shaders/fill_frag.glsl +++ b/packages/layers/src/point/shaders/fill_frag.glsl @@ -93,6 +93,9 @@ void main() { gl_FragColor = vec4(gl_FragColor.xyz, intensity); } + // TODO: 避免多余片元绘制,同时也能避免有用片元在透明且重叠的情况下无法写入 + if(gl_FragColor.a <= 0.0) discard; + gl_FragColor = filterColor(gl_FragColor); } diff --git a/stories/customMap/components/earth.tsx b/stories/customMap/components/earth.tsx index 91c5de503b..948f8f1bcc 100644 --- a/stories/customMap/components/earth.tsx +++ b/stories/customMap/components/earth.tsx @@ -257,6 +257,9 @@ export default class ScaleComponent extends React.Component { let pointlayer = new PointLayer() .source( d, + // [ + // {"lng":120,"lat":30} + // ], // [ // {"lng":10,"lat":0}, // {"lng":20,"lat":0}, @@ -310,6 +313,26 @@ export default class ScaleComponent extends React.Component { // {"lng":0,"lat":80}, // {"lng":0,"lat":90}, + // {"lng":0,"lat":100}, + // {"lng":0,"lat":110}, + // {"lng":0,"lat":120}, + // {"lng":0,"lat":130}, + // {"lng":0,"lat":140}, + // {"lng":0,"lat":150}, + // {"lng":0,"lat":160}, + // {"lng":0,"lat":170}, + // {"lng":0,"lat":180}, + + // {"lng":0,"lat":190}, + // {"lng":0,"lat":200}, + // {"lng":0,"lat":210}, + // {"lng":0,"lat":220}, + // {"lng":0,"lat":230}, + // {"lng":0,"lat":240}, + // {"lng":0,"lat":250}, + // {"lng":0,"lat":260}, + // {"lng":0,"lat":270}, + // {"lng":0,"lat":-10}, // {"lng":0,"lat":-20}, // {"lng":0,"lat":-30}, @@ -328,13 +351,15 @@ export default class ScaleComponent extends React.Component { }, }, ) - .shape('circle') - // .shape('cylinder') + // .shape('circle') + .shape('cylinder') .color('#f00') - .size(10) + .size('', () => [1, 1, 10]) + // .size(20) .style({ - opacity: 0.6, + // opacity: 0.6, }) + .animate(true) .active(true); // scene.addLayer(pointlayer); @@ -387,6 +412,7 @@ export default class ScaleComponent extends React.Component { scene.on('loaded', () => { scene.addLayer(earthlayer); scene.addLayer(pointlayer); + // console.log(pointlayer) earthlayer.setEarthTime(4.0); });