private async _createTileMesh()

in packages/layers/xyz-aoi-tile/src/AOILayer.ts [576:804]


	private async _createTileMesh(
		geojson: any,
		projection: Projection,
		polaris: AbstractPolaris,
		key?: string
	): Promise<TileRenderables | undefined> {
		if (!geojson.type || geojson.type !== 'FeatureCollection') {
			return
		}

		const mesh = new Mesh({
			name: key ? key : 'aois',
			extras: { isAOI: true },
		})
		mesh.renderOrder = this.getProp('renderOrder')

		// styles
		const featureIdKey = this.getProp('featureIdKey')
		const baseAlt = this.getProp('baseAlt')
		const featureFilter = this.getProp('featureFilter')
		const getColor = functionlize(this.getProp('getColor'))
		const getOpacity = functionlize(this.getProp('getOpacity'))
		const lineHeight = this.getProp('indicatorLinesHeight')
		const pickable = this.getProp('pickable')

		// caches
		const meshFeatures: any[] = []
		const tileRenderables: Mesh[] = []
		const idLineRangeMap: Map<number | string, { offset: number; count: number }[]> = new Map()

		// attrs
		const positions: number[] = []
		const colors: number[] = []
		const indices: number[] = []
		const linePosArr: number[][] = []
		let linePosOffset = 0
		let offset = 0

		const features = geojson.features as any[]
		const loadPromises = features.map(async (feature) => {
			if (!feature.geometry) {
				return
			}
			const geometry = feature.geometry

			// apply filter
			if (featureFilter) {
				const filterResult = featureFilter(feature)
				if (filterResult === undefined || filterResult === false) {
					return
				}
			}

			// add 'index' prop to feature
			feature.index = this._featureCount
			this._featureCount++

			const id = feature.properties[featureIdKey] as number | string

			if (id === undefined || id === null) {
				console.error(`AOILayer - No feature id prop found, skip`)
				return
			}

			if (geometry.type === 'Polygon' || geometry.type === 'MultiPolygon') {
				// polygon triangles generation
				// use workers if available

				const result = this._workerManager
					? await this._workerManager.execute({
							data: {
								task: 'getFeatureTriangles',
								feature,
								projectionDesc: projection.toDesc(),
								baseAlt,
							},
							transferables: [],
					  })
					: getFeatureTriangles(feature, projection, baseAlt)

				const { positions: featPositions, indices: featIndices } = result

				const indexStart = indices.length
				// const indexRange = new Uint32Array([indices.length, 0])
				// const colorRange = new Uint32Array([offset * 4, 0])

				for (let i = 0; i < featPositions.length; i += 3) {
					positions.push(featPositions[i + 0], featPositions[i + 1], featPositions[i + 2])
				}
				for (let i = 0; i < featIndices.length; i++) {
					indices.push(featIndices[i] + offset)
				}

				const count = featPositions.length / 3
				const offset4 = offset * 4
				const color = new Color(getColor(feature))
				const alpha = getOpacity(feature) ?? 1.0
				const colorUint = colorToUint8Array(color, alpha)
				for (let i = 0; i < count; i++) {
					const i4 = offset4 + i * 4
					colors[i4 + 0] = colorUint[0]
					colors[i4 + 1] = colorUint[1]
					colors[i4 + 2] = colorUint[2]
					colors[i4 + 3] = colorUint[3]
				}

				offset += count

				// Store index range for feature
				const indexEnd = indices.length - 1
				// indexRange[1] = indices.length - 1

				// // Store feature vert range
				// colorRange[1] = offset * 4

				this._featureIndexRangeMap.set(feature, createRangeArray(indexStart, indexEnd))
			} else if (
				pickable &&
				(geometry.type === 'LineString' || geometry.type === 'MultiLineString')
			) {
				// use workers if available
				// const linePositions: Float32Array[] = this._workerManager
				// 	? (
				// 			await this._workerManager.execute({
				// 				data: {
				// 					task: 'featureToLinePositions',
				// 					feature,
				// 					projectionDesc: projection.toDesc(),
				// 					baseAlt: baseAlt + lineHeight,
				// 				},
				// 				transferables: [],
				// 			})
				// 	  ).linePositions
				// 	: featureToLinePositions(feature, projection, baseAlt + lineHeight)

				// outline positions generation
				const linePositions = featureToLinePositions(feature, projection, baseAlt + lineHeight)

				if (!linePositions) return

				for (let i = 0; i < linePositions.length; i++) {
					const linePos = linePositions[i]
					const count = linePos.length / 3
					const arr: number[] = []
					for (let j = 0, jl = linePos.length; j < jl; j += 3) {
						arr.push(linePos[j + 0], linePos[j + 1], linePos[j + 2])
					}
					linePosArr.push(arr)

					const range = {
						offset: linePosOffset,
						count,
					}

					const lineRanges = idLineRangeMap.get(id)
					if (lineRanges) {
						lineRanges.push(range)
					} else {
						idLineRangeMap.set(id, [range])
					}

					// update offset
					linePosOffset += count
				}
			}

			meshFeatures.push(feature)
		})

		const results = await Promise.all(loadPromises)

		const posAttr = new Attr(new Float32Array(positions), 3)
		const colorAttr = new Attr(new Uint8Array(colors), 4)
		const indicesArray = offset > 65535 ? new Uint32Array(indices) : new Uint16Array(indices)
		const indicesAttr = new Attr(indicesArray, 1)

		const geom = new Geom({
			mode: 'TRIANGLES',
			attributes: {
				position: posAttr,
				aColor: colorAttr,
			},
			indices: indicesAttr,
		})

		computeBSphere(geom)
		computeBBox(geom)

		mesh.geometry = geom
		mesh.material = this.matr

		// geom.boundingSphere = new Sphere(new Vector3(), Infinity)
		// geom.boundingBox = new Box3(
		// 	new Vector3(-Infinity, -Infinity, -Infinity),
		// 	new Vector3(Infinity, Infinity, Infinity)
		// )

		if (this.getProp('debug') && geom.boundingSphere) {
			console.warn('debug unimplemented')

			// TODO: gen wire frame is a GSI processor now
			// const wireframe = new Mesh({
			// 	name: 'bsphere-wireframe',
			// 	geometry: genBSphereWireframe(geom.boundingSphere),
			// 	// geometry: genBBoxWireframe(geom.boundingBox),
			// 	material: new UnlitMaterial({ baseColorFactor: { r: 1, g: 0, b: 1 } }),
			// })
			// mesh.add(wireframe)
		}

		this._renderableFeatureMap.set(mesh, meshFeatures)

		// LineIndicators
		if (pickable && linePosArr.length > 0) {
			const { hoverIndicator, selectIndicator } = this._genLineIndicators(polaris, linePosArr)
			tileRenderables.push(hoverIndicator.gline, selectIndicator.gline)
			this._indicators.add(hoverIndicator)
			this._indicators.add(selectIndicator)
			this._cacheIndicatorRanges(idLineRangeMap, hoverIndicator, selectIndicator)
			idLineRangeMap.forEach((ranges, id) => {
				if (this._hoveredIds.has(id)) this._setStyleById(id, 'hover')
				if (this._selectedIds.has(id)) this._setStyleById(id, 'select')
			})
		}

		tileRenderables.push(mesh)

		return { meshes: tileRenderables, layers: [] }
	}