diff --git a/packages/gl/src/layer/raycaster/RayCaster.js b/packages/gl/src/layer/raycaster/RayCaster.js index 0322153a59..566c921845 100644 --- a/packages/gl/src/layer/raycaster/RayCaster.js +++ b/packages/gl/src/layer/raycaster/RayCaster.js @@ -66,6 +66,7 @@ export default class RayCaster { _testMesh(mesh, ray, map, positions, altitudes, indices, dim, matrix, count) { const coordinates = []; + const glRes = map.getGLRes(); for (let j = 0; j < indices.length; j += 3) { if (j > mesh.properties.skirtOffset) { break; @@ -73,22 +74,41 @@ export default class RayCaster { const a = indices[j]; const b = indices[j + 1]; const c = indices[j + 2]; - const positionsA = vec3.set(pA_VEC, positions[a * dim], positions[a * dim + 1], positions[a * dim + 2]); - const pA = this._toWorldPosition(POS_A, map, positionsA, altitudes[a] / 100, matrix); - const positionsB = vec3.set(pB_VEC, positions[b * dim], positions[b * dim + 1], positions[b * dim + 2]); - const pB = this._toWorldPosition(POS_B, map, positionsB, altitudes[b] / 100, matrix); - const positionsC = vec3.set(pC_VEC, positions[c * dim], positions[c * dim + 1], positions[c * dim + 2]); - const pC = this._toWorldPosition(POS_C, map, positionsC, altitudes[c] / 100, matrix); + const aindex = a * dim, bindex = b * dim, cindex = c * dim; + //高频操作,尽可能的减少function(vec3.set) call, + pA_VEC[0] = positions[aindex]; + pA_VEC[1] = positions[aindex + 1]; + pA_VEC[2] = positions[aindex + 2]; + + pB_VEC[0] = positions[bindex]; + pB_VEC[1] = positions[bindex + 1]; + pB_VEC[2] = positions[bindex + 2]; + + pC_VEC[0] = positions[cindex]; + pC_VEC[1] = positions[cindex + 1]; + pC_VEC[2] = positions[cindex + 2]; + + + const pA = this._toWorldPosition(POS_A, map, pA_VEC, altitudes[a] / 100, matrix); + const pB = this._toWorldPosition(POS_B, map, pB_VEC, altitudes[b] / 100, matrix); + const pC = this._toWorldPosition(POS_C, map, pC_VEC, altitudes[c] / 100, matrix); + + // const positionsA = vec3.set(pA_VEC, positions[a * dim], positions[a * dim + 1], positions[a * dim + 2]); + // const pA = this._toWorldPosition(POS_A, map, positionsA, altitudes[a] / 100, matrix); + // const positionsB = vec3.set(pB_VEC, positions[b * dim], positions[b * dim + 1], positions[b * dim + 2]); + // const pB = this._toWorldPosition(POS_B, map, positionsB, altitudes[b] / 100, matrix); + // const positionsC = vec3.set(pC_VEC, positions[c * dim], positions[c * dim + 1], positions[c * dim + 2]); + // const pC = this._toWorldPosition(POS_C, map, positionsC, altitudes[c] / 100, matrix); const triangle = vec3.set(TRIANGLE, pA, pB, pC); const vAB = vec3.sub(TEMP_VEC_AB, pA, pB); const vAC = vec3.sub(TEMP_VEC_AC, pA, pC); const intersectPoint = this._testIntersection(INTERSECT_POINT, triangle, ray); if (intersectPoint) { - const altitude = map.pointAtResToAltitude(intersectPoint[2], map.getGLRes()); + const altitude = map.pointAtResToAltitude(intersectPoint[2], glRes); TEMP_POINT.x = intersectPoint[0]; TEMP_POINT.y = intersectPoint[1]; - const coord = map.pointAtResToCoordinate(TEMP_POINT, map.getGLRes()); + const coord = map.pointAtResToCoordinate(TEMP_POINT, glRes); coord.z = altitude; coordinates.push({ coordinate: coord, diff --git a/packages/gl/src/layer/terrain/TerrainLitPainter.js b/packages/gl/src/layer/terrain/TerrainLitPainter.js index 8ff88518c9..6e678b975d 100644 --- a/packages/gl/src/layer/terrain/TerrainLitPainter.js +++ b/packages/gl/src/layer/terrain/TerrainLitPainter.js @@ -1,6 +1,6 @@ import * as reshader from '../../reshader'; import TerrainPainter from './TerrainPainter'; -import { extend, isNil } from '../util/util'; +import { extend, isNil } from '../util/util'; import * as ContextUtil from '../util/context'; const { getIBLResOnCanvas, getPBRUniforms, loginIBLResOnCanvas, logoutIBLResOnCanvas } = reshader.pbr.PBRUtils; @@ -52,7 +52,7 @@ class TerrainLitPainter extends TerrainPainter { createTerrainMesh(tileInfo, terrainImage) { const { mesh: terrainGeo, image: heightTexture } = terrainImage; - const { positions, texcoords, triangles, leftSkirtIndex, rightSkirtIndex, bottomSkirtIndex, numVerticesWithoutSkirts } = terrainGeo; + const { positions, texcoords, triangles, leftSkirtIndex, rightSkirtIndex, bottomSkirtIndex, numVerticesWithoutSkirts, tangents } = terrainGeo; const normals = new Int8Array(positions.length); for (let i = 2; i < normals.length; i += 3) { if (i < numVerticesWithoutSkirts * 3) { @@ -72,15 +72,12 @@ class TerrainLitPainter extends TerrainPainter { aPosition: positions, aTexCoord: texcoords, aNormal: normals - }, - triangles, - 0); - + }, triangles, 0); // 共用端点时,法线值会出现错误,造成视觉上不连续,所以需要唯一化 // 唯一化后,三角形数量不变,但端点数组大概会膨胀5倍以上 // geo.buildUniqueVertex(); // geo.createNormal(); - geo.createTangent(); + geo.createTangent('aTangent', tangents); delete geo.data.aNormal; geo.generateBuffers(this.graphics); @@ -161,7 +158,7 @@ class TerrainLitPainter extends TerrainPainter { extend(uniforms, { viewMatrix: map.viewMatrix, projMatrix: map.projMatrix, - projViewMatrix : map.projViewMatrix, + projViewMatrix: map.projViewMatrix, outSize: [canvas.width, canvas.height], polygonFill: [1, 1, 1, 1], terrainHeightMapResolution: [tileSize, tileSize], diff --git a/packages/gl/src/layer/terrain/worker/index.js b/packages/gl/src/layer/terrain/worker/index.js index d746fc3a48..a0db94fa52 100644 --- a/packages/gl/src/layer/terrain/worker/index.js +++ b/packages/gl/src/layer/terrain/worker/index.js @@ -3,6 +3,11 @@ import "./zlib.min"; import { vec2, vec3 } from 'gl-matrix'; import { createMartiniData } from '../util/martini'; import { ColorIn } from 'colorin'; +import { buildTangents } from '@maptalks/tbn-packer'; + +function isNumber(val) { + return (typeof val === 'number') && !isNaN(val); +} // 保存当前的workerId,用于告知主线程结果回传给哪个worker let workerId; @@ -764,6 +769,41 @@ function createColorsTexture(data, colors, tileSize) { } +/** + * create terrain geometry tangents by worker for perf + * @param {*} terrainMesh + * @returns + */ +function createTerrainGeometryTangents(terrainMesh) { + if (!terrainMesh) { + return; + } + const { positions, texcoords, triangles, leftSkirtIndex, rightSkirtIndex, bottomSkirtIndex, numVerticesWithoutSkirts } = terrainMesh; + if (!positions || !texcoords || !triangles) { + return; + } + if (!isNumber(leftSkirtIndex) || !isNumber(rightSkirtIndex) || !isNumber(bottomSkirtIndex) || !isNumber(numVerticesWithoutSkirts)) { + return; + } + const normals = new Int8Array(positions.length); + for (let i = 2; i < normals.length; i += 3) { + if (i < numVerticesWithoutSkirts * 3) { + normals[i] = 1; + } else if (i < leftSkirtIndex / 2 * 3) { + normals[i - 2] = -1; + } else if (i < rightSkirtIndex / 2 * 3) { + normals[i - 2] = 1; + } else if (i < bottomSkirtIndex / 2 * 3) { + normals[i - 1] = -1; + } else { + // top + normals[i - 1] = 1; + } + } + const tangents = buildTangents(positions, normals, texcoords, triangles); + return new Float32Array(tangents); +} + export const onmessage = function (message, postResponse) { const data = message.data; if (data.command === 'addLayer' || data.command === 'removeLayer') { @@ -775,12 +815,20 @@ export const onmessage = function (message, postResponse) { const colors = (data.params || {}).colors; const tileSize = (data.params || {}).tileSize; loadTerrain(data.params, (data, transferables) => { + transferables = transferables || []; + //create terrain colors texture const texture = createColorsTexture(data, colors, tileSize); if (texture) { data.colorsTexture = texture; - transferables = transferables || []; transferables.push(texture); } + + //create terrain geometry tangents attribute + const tangents = createTerrainGeometryTangents(data.mesh); + if (tangents && data.mesh) { + data.mesh.tangents = tangents; + transferables.push(tangents.buffer); + } postResponse(data.error, data, transferables); }); } else if (data.command === 'abortTerrain') { diff --git a/packages/gl/src/reshader/Geometry.ts b/packages/gl/src/reshader/Geometry.ts index f17d5afa70..12859d05cd 100644 --- a/packages/gl/src/reshader/Geometry.ts +++ b/packages/gl/src/reshader/Geometry.ts @@ -88,7 +88,7 @@ export default class Geometry { } } - static padGPUBufferAlignment(array:TypedArray, vertexCount: number): TypedArray { + static padGPUBufferAlignment(array: TypedArray, vertexCount: number): TypedArray { const itemBytes = array.byteLength / vertexCount; if (itemBytes % 4 === 0) { return array; @@ -226,7 +226,7 @@ export default class Geometry { if (!id) { id = attribute.array.buffer['__id'] = GUID(); } - this.data[attr] = { + this.data[attr] = { buffer: id, offset: attribute.byteOffset, stride: attribute.byteStride, @@ -467,9 +467,15 @@ export default class Geometry { } else { ctor = this.elements.constructor; } - this.indices = new ctor(elements.length); - for (let i = 0; i < elements.length; i++) { - this.indices[i] = elements[i]; + if (elements instanceof ctor) { + //2x faster by new TypeArray(typearray) + //https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array + this.indices = new ctor(elements); + } else { + this.indices = new ctor(elements.length); + for (let i = 0; i < elements.length; i++) { + this.indices[i] = elements[i]; + } } } this.elements = this.elements.destroy ? this.elements : Geometry.createElementBuffer(device, this.elements); @@ -495,7 +501,7 @@ export default class Geometry { if (isArray(data)) { // 因为data可能被转成regl buffer,需要保存到this._vertexCount // 在 updateData时再更新 - this._vertexCount = Math.ceil(data.length / positionSize); + this._vertexCount = Math.ceil(data.length / positionSize); } else if (data && data.count !== undefined) { this._vertexCount = data.count; } @@ -600,6 +606,7 @@ export default class Geometry { return this; } + _updateGPUBuffer(buffer : GPUBuffer, data : AttributeData, offset: number, byteLength: number) { if (Array.isArray(data)) { data = new Float32Array(data); @@ -899,7 +906,7 @@ export default class Geometry { //对于POSITION数据,为避免updateBBox时频繁创建临时数组,采用缓存tempPosArray的策略获取interleavedArray, //对于非POSITION的数据,直接readInterleavedArray读取即可 if (name === this.desc.positionAttribute) { - if (!this._tempPosArray || (this._tempPosArray && this._tempPosArray.length < size *count)) { + if (!this._tempPosArray || (this._tempPosArray && this._tempPosArray.length < size * count)) { this._tempPosArray = new ctor(size * count); return gltf.GLTFLoader.readInterleavedArray(this._tempPosArray, attribute, count, size, stride, offset, componentType); } @@ -915,18 +922,23 @@ export default class Geometry { } } - createTangent(name = 'aTangent') { + createTangent(name = 'aTangent', tangentsDataArray?: Float32Array | Array) { this._incrVersion(); //TODO data 可能是含stride的interleaved类型 const { normalAttribute, positionAttribute, uv0Attribute } = this.desc; const normals = this._getAttributeData(normalAttribute); const positions = this._getAttributeData(positionAttribute); - const tangents = buildTangents( - positions, - normals, - this.data[uv0Attribute], - this.elements - ); + let tangents; + if (tangentsDataArray && tangentsDataArray.length) { + tangents = tangentsDataArray; + } else { + tangents = buildTangents( + positions, + normals, + this.data[uv0Attribute], + this.elements + ); + } const aTangent = this.data[name] = new Float32Array(tangents.length); const t: vec4 = [0, 0, 0, 0], n: vec3 = [0, 0, 0], q: vec4 = [0, 0, 0, 0]; for (let i = 0; i < tangents.length; i += 4) { @@ -982,7 +994,7 @@ export default class Geometry { const oldData = {}; let pos = data[this.desc.positionAttribute]; - pos = pos.length ? pos : pos.array; //存在两种结构 array或者 { array } + pos = pos.length ? pos : pos.array; //存在两种结构 array或者 { array } if (!isArray(pos)) { throw new Error(this.desc.positionAttribute + ' must be array to build unique vertex.'); } @@ -1016,7 +1028,7 @@ export default class Geometry { indices[i] = cursor++; } pos = this.data[this.desc.positionAttribute]; - this._vertexCount = Math.ceil(pos.length / this.desc.positionSize); + this._vertexCount = Math.ceil(pos.length / this.desc.positionSize); delete this._reglData; } @@ -1048,7 +1060,7 @@ export default class Geometry { //@internal _forEachBuffer(fn: (buffer: any) => void) { - if (this.elements && this.elements.destroy) { + if (this.elements && this.elements.destroy) { fn(this.elements); } for (const p in this.data) { @@ -1283,7 +1295,7 @@ function createGPUBuffer(device, data, usage, label) { data = new Float32Array(data); } const ctor = data.constructor; - // f32 in default + // f32 in default const byteLength = data.byteLength; // mappedAtCreation requires size is a multiplier of 4 // https://github.com/gpuweb/gpuweb/issues/5105 diff --git a/packages/maptalks/src/map/Map.DomEvents.ts b/packages/maptalks/src/map/Map.DomEvents.ts index 8eb31eae99..e484b0370d 100644 --- a/packages/maptalks/src/map/Map.DomEvents.ts +++ b/packages/maptalks/src/map/Map.DomEvents.ts @@ -404,7 +404,7 @@ Map.include(/** @lends Map.prototype */ { //@internal _wrapTerrainData(eventParam: MapEventDataType) { - if (this.options['queryTerrainInMapEvents'] && eventParam.containerPoint && !eventParam.terrain) { + if (this.options['queryTerrainInMapEvents'] && eventParam.containerPoint && !eventParam.terrain && !this.isInteracting()) { eventParam.terrain = this._queryTerrainInfo(eventParam.containerPoint); } }, diff --git a/packages/vt/src/layer/layer/VectorTileLayer.ts b/packages/vt/src/layer/layer/VectorTileLayer.ts index 68a19864e0..a40fde8aae 100644 --- a/packages/vt/src/layer/layer/VectorTileLayer.ts +++ b/packages/vt/src/layer/layer/VectorTileLayer.ts @@ -386,23 +386,22 @@ class VectorTileLayer extends maptalks.TileLayer { getWorkerOptions(): Record { const isWebGPU = this.getMap().getRenderer().isWebGPU(); const renderer = this.getRenderer(); + const options = this.options; return { - debug: (this.options as any)["debug"], - debugTile: (this.options as any)["debugTile"], - altitudeProperty: (this.options as any)["altitudeProperty"], + debug: options.debug, + debugTile: options.debugTile, + altitudeProperty: options.altitudeProperty, tileSize: this.getTileSize().width, //default render时,this._vtStyle有可能被default render设值 style: this.isDefaultRender() ? { style: [], featureStyle: [] } : this._getComputedStyle(), - features: - (this.options as any).debugTileData || (this.options as any).features, - schema: (this.options as any).schema, - pickingGeometry: (this.options as any)["pickingGeometry"], + features: options.debugTileData || options.features, + schema: options.schema, + pickingGeometry: options.pickingGeometry, projectionCode: this.getSpatialReference().getProjection().code, - workerGlyph: - (this.options as any)["workerGlyph"] && !this.getURLModifier(), - featureIdProperty: (this.options as any)["featureIdProperty"], + workerGlyph: options.workerGlyph && !this.getURLModifier(), + featureIdProperty: options.featureIdProperty, isWebGPU, isWebGL1: renderer.gl && (renderer.gl instanceof WebGLRenderingContext) }; @@ -547,23 +546,23 @@ class VectorTileLayer extends maptalks.TileLayer { this._loadStyle(style); }); } else { - this._loadStyle(style); + this._loadStyle(style); } } _parseStyle(style) { const background = style.background || {}; return { - originFeatureStyle: style["featureStyle"] || [], - featureStyle: parseFeatureStyle(style["featureStyle"]), - style: style["style"] || [], - background: { - enable: background.enable || false, - color: unitColor(background.color) || [0, 0, 0, 0], - opacity: getOrDefault(background.opacity, 1), - patternFile: background.patternFile, - depthRange: background.depthRange, - } + originFeatureStyle: style["featureStyle"] || [], + featureStyle: parseFeatureStyle(style["featureStyle"]), + style: style["style"] || [], + background: { + enable: background.enable || false, + color: unitColor(background.color) || [0, 0, 0, 0], + opacity: getOrDefault(background.opacity, 1), + patternFile: background.patternFile, + depthRange: background.depthRange, + } }; } @@ -577,7 +576,7 @@ class VectorTileLayer extends maptalks.TileLayer { this.validateStyle(); if (this._replacer) { - this._parseStylePath(); + this._parseStylePath(); } this._compileStyle(); /** @@ -590,8 +589,8 @@ class VectorTileLayer extends maptalks.TileLayer { * @property {Object|Object[]} style - style to set */ this.fire("setstyle", { - style: this.getStyle(), - computedStyle: this.getComputedStyle(), + style: this.getStyle(), + computedStyle: this.getComputedStyle(), }); } @@ -2026,6 +2025,7 @@ export type VectorTileLayerOptionsType = { enableAltitude?: true, debugTileData?: boolean, + debugTile?: boolean; altitudeQueryTimeLimitPerFrame?: number, workerGlyph?: boolean,