This commit is contained in:
2912401452 2021-10-09 11:12:30 +08:00
commit d1af7b023b
6 changed files with 251 additions and 149 deletions

View File

@ -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,
};
}

View File

@ -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,
};
}

View File

@ -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);
if (isGlobel) {
// TODO: 在地球模式下需要将传入 shader 的经纬度转化成对应的 xyz 坐标
return lglt2xyz([coordinates[0], coordinates[1]]) as [
number,
number,
number,
];
} else {
return [coordinates[0], coordinates[1], 0];
}
},
},
});

View File

@ -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);
}

View File

@ -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);
}

View File

@ -258,6 +258,9 @@ export default class ScaleComponent extends React.Component {
.source(
d,
// [
// {"lng":120,"lat":30}
// ],
// [
// {"lng":10,"lat":0},
// {"lng":20,"lat":0},
// {"lng":30,"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);
});