@mesh3d/cesium-vectortile-gl 0.4.6 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,7 @@
13
13
  - ✅ **GPU 精准剔除**:利用 FBO + RTT 技术生成瓦片 ID 纹理,在 GPU 中高效剔除不可见矢量片段
14
14
  - ✅ **高度可扩展**:提供清晰接口,轻松接入新数据源(如 TopoJSON)或自定义图层类型(如热力图、流线)
15
15
  - ✅ **文字符号自动避让**:使用 `maplibre-gl` `GridIndex`类,结合 Cesium.Label 屏幕空间位置和包围盒计算API,实现符号碰撞检测(自动避让)
16
+ - ✅ **样式动态修改**:提供`setLayoutProperty`、`setPaintProperty`、`setFilter`等`样式修改API`,可动态控制图层`显隐`、`颜色`、`透明度`
16
17
 
17
18
  #### 🧠 技术栈亮点
18
19
 
@@ -83,6 +84,34 @@ const tileset = new VectorTileset({
83
84
  viewer.scene.primitives.add(tileset)
84
85
  ```
85
86
 
87
+ ### 样式修改示例
88
+
89
+ ```js
90
+ tileset.readyEvent.addEventListener(() => {
91
+ // 1、修改绘制属性(颜色、透明度),不会触发强制刷新,实时变化
92
+ tileset.setPaintProperty('background', 'background-color', 'skyblue')
93
+ tileset.setPaintProperty('geolines', 'line-color', 'green')
94
+ tileset.setPaintProperty('us_states:fill', 'fill-color', 'red')
95
+ tileset.setPaintProperty('countries-label', 'text-color', 'blue')
96
+
97
+ // 2、修改布局属性,除 visibility 外,都会触发强制刷新
98
+ // 2.1、布局属性修改:图层显隐,不会触发强制刷新
99
+ tileset.setLayoutProperty('coastline', 'visibility', 'none')
100
+ // 2.2、布局属性修改(非visibility):字体大小修改,会触发强制刷新
101
+ tileset.setLayoutProperty('countries-label', 'text-size', {
102
+ stops: [
103
+ [2, 12],
104
+ [4, 16],
105
+ [6, 20]
106
+ ]
107
+ })
108
+
109
+ // 3、修改过滤器。
110
+ //和setLayoutProperty一样(visibility例外)会触发强制刷新
111
+ tileset.setFilter('geolines', null)
112
+ })
113
+ ```
114
+
86
115
  ### 可选:Web Worker 多线程瓦片处理
87
116
 
88
117
  将瓦片解析、坐标转换、几何构建放到子线程,减轻主线程卡顿。不传 `workerUrl` 时仍走主线程。
@@ -33,6 +33,7 @@ export class VectorTileset {
33
33
  this._tilesToRender = []
34
34
  /**@type {StyleLayer[]} */
35
35
  this._styleLayers = []
36
+ this._styleLayerIndexMap = new Map()
36
37
  /**@type {VectorTileRenderList} */
37
38
  this._renderList = new VectorTileRenderList(this._styleLayers)
38
39
  this.numLoading = 0
@@ -95,6 +96,7 @@ export class VectorTileset {
95
96
  //初始化样式图层
96
97
  for (let i = 0; i < style.layers.length; i++) {
97
98
  this._styleLayers[i] = new StyleLayer(style.layers[i])
99
+ this._styleLayerIndexMap.set(style.layers[i].id, i)
98
100
  }
99
101
 
100
102
  //创建顶级瓦片LOD
@@ -257,6 +259,65 @@ export class VectorTileset {
257
259
  }
258
260
  }
259
261
 
262
+ //样式编辑API
263
+
264
+ setLayoutProperty(layerId, name, value) {
265
+ const styleLayerIndexMap = this._styleLayerIndexMap
266
+ if (!styleLayerIndexMap.has(layerId)) {
267
+ warnOnce(`不存在图层:${layerId}`)
268
+ return false
269
+ }
270
+ const layerIndex = styleLayerIndexMap.get(layerId)
271
+ const styleLayer = this._styleLayers[layerIndex]
272
+ const changed = styleLayer.setLayoutProperty(name, value)
273
+ //强制更新
274
+ if (changed && name !== 'visibility') {
275
+ this._forceUpdate()
276
+ }
277
+ return changed
278
+ }
279
+
280
+ setPaintProperty(layerId, name, value) {
281
+ const styleLayerIndexMap = this._styleLayerIndexMap
282
+ if (!styleLayerIndexMap.has(layerId)) {
283
+ warnOnce(`不存在图层:${layerId}`)
284
+ return false
285
+ }
286
+ const layerIndex = styleLayerIndexMap.get(layerId)
287
+ const styleLayer = this._styleLayers[layerIndex]
288
+ return styleLayer.setPaintProperty(name, value)
289
+ }
290
+
291
+ setFilter(layerId, filter) {
292
+ const styleLayerIndexMap = this._styleLayerIndexMap
293
+ if (!styleLayerIndexMap.has(layerId)) {
294
+ warnOnce(`不存在图层:${layerId}`)
295
+ return false
296
+ }
297
+ const layerIndex = styleLayerIndexMap.get(layerId)
298
+ const styleLayer = this._styleLayers[layerIndex]
299
+ const changed = styleLayer.setFilter(filter)
300
+ if (changed) {
301
+ this._forceUpdate()
302
+ }
303
+ return changed
304
+ }
305
+
306
+ //强制更新
307
+ _forceUpdate() {
308
+ for (const cacheTile of this._cacheTiles) {
309
+ cacheTile.unload()
310
+ }
311
+ for (const cacheTile of this._tilesToRender) {
312
+ cacheTile.unload()
313
+ }
314
+ for (const cacheTile of this._tilesToUpdate) {
315
+ cacheTile.unload()
316
+ }
317
+ this._tilesToRender.length = 0
318
+ this._tilesToUpdate.length = 0
319
+ }
320
+
260
321
  destroy() {
261
322
  const scene = this.scene
262
323
  const rootTiles = this._rootTiles
@@ -80,6 +80,21 @@ export class BackgroundRenderLayer extends IRenderLayer {
80
80
 
81
81
  frameState.commandList = preCommandList
82
82
  }
83
+ if (this.primitive && this.paintNeedsUpdate) {
84
+ const style = this.style,
85
+ tile = this.tile
86
+ const color = style.convertColor(
87
+ style.paint.getDataConstValue('background-color', tile.z)
88
+ )
89
+ const opacity = style.paint.getDataConstValue(
90
+ 'background-opacity',
91
+ tile.z
92
+ )
93
+ color.alpha *= opacity
94
+ this.primitive.appearance.material.uniforms.color = color
95
+
96
+ this.paintNeedsUpdate = false
97
+ }
83
98
  }
84
99
 
85
100
  destroy() {
@@ -4,13 +4,47 @@ import { registerRenderLayer } from './registerRenderLayer'
4
4
  import { FillLayerVisualizer } from './visualizers/FillLayerVisualizer'
5
5
 
6
6
  export class FillRenderLayer extends IRenderLayer {
7
+ constructor(sourceFeatures, styleLayer, tile) {
8
+ super(sourceFeatures, styleLayer, tile)
9
+ this.outlineOffsets = []
10
+ this.outlineCounts = []
11
+ }
7
12
  /**
8
13
  * @param {Cesium.FrameState} frameState
9
14
  * @param {VectorTileset} tileset
10
15
  */
11
16
  update(frameState, tileset) {
12
17
  //可以在这里实现同步样式,动态更新图层颜色等样式
18
+ if (this.paintNeedsUpdate) {
19
+ const style = this.style,
20
+ tile = this.tile,
21
+ batchTable = this._batchTable
22
+
23
+ for (const feature of this.features) {
24
+ const fillColor = style.convertColor(
25
+ style.paint.getDataValue('fill-color', tile.z, feature)
26
+ )
27
+ const fillOpacity = style.paint.getDataValue(
28
+ 'fill-opacity',
29
+ tile.z,
30
+ feature
31
+ )
32
+ feature.fillColor = fillColor
33
+ feature.fillOpacity = fillOpacity
34
+
35
+ const batchId = feature.batchId
36
+ const colorBytes = fillColor.toBytes()
37
+ colorBytes[3] = Math.floor(colorBytes[3] * fillOpacity)
38
+ batchTable.setBatchedAttribute(batchId, 0, {
39
+ x: colorBytes[0],
40
+ y: colorBytes[1],
41
+ z: colorBytes[2],
42
+ w: colorBytes[3]
43
+ })
44
+ }
13
45
 
46
+ this.paintNeedsUpdate = false
47
+ }
14
48
  super.update(frameState, tileset)
15
49
  }
16
50
  }
@@ -82,6 +82,10 @@ export class IRenderLayer {
82
82
  * @type {'none'|'done'|'error'}
83
83
  */
84
84
  this.state = 'none'
85
+ /**
86
+ * 绘制(paint)属性版本号,用于监听绘制属性变化
87
+ */
88
+ this.paintVersion = styleLayer.paintVersion
85
89
  }
86
90
 
87
91
  get id() {
@@ -92,6 +96,15 @@ export class IRenderLayer {
92
96
  return this.style.type
93
97
  }
94
98
 
99
+ get paintNeedsUpdate() {
100
+ return this.paintVersion !== this.style.paintVersion
101
+ }
102
+ set paintNeedsUpdate(val) {
103
+ if (!val) {
104
+ this.paintVersion = this.style.paintVersion
105
+ }
106
+ }
107
+
95
108
  /**
96
109
  * 更渲染图层,可在该方法内实现绘图命令构建、动态样式更新等图层渲染准备相关功能。该方法可以被子类重写或复用
97
110
  * @param {Cesium.FrameState} frameState
@@ -5,6 +5,7 @@ import { IRenderLayer } from './IRenderLayer'
5
5
  import { registerRenderLayer } from './registerRenderLayer'
6
6
  import { VectorTileLOD } from '../VectorTileLOD'
7
7
  import { LineLayerVisualizer } from './visualizers/LineLayerVisualizer'
8
+ import { warnOnce } from 'maplibre-gl/src/util/util'
8
9
 
9
10
  export class LineRenderLayer extends IRenderLayer {
10
11
  /**
@@ -91,7 +92,49 @@ export class LineRenderLayer extends IRenderLayer {
91
92
  // }
92
93
  // if (this.primitive && this.primitive.length) {
93
94
  // this.primitive.update(frameState)
95
+ // this._batchTable = primitive._batchTable
94
96
  // }
97
+ if (this.paintNeedsUpdate) {
98
+ const style = this.style,
99
+ tile = this.tile,
100
+ batchTable = this._batchTable
101
+
102
+ for (const feature of this.features) {
103
+ const lineWidth = style.paint.getDataValue(
104
+ 'line-width',
105
+ tile.z,
106
+ feature
107
+ )
108
+ const lineColor = style.convertColor(
109
+ style.paint.getDataValue('line-color', tile.z, feature)
110
+ )
111
+ const lineOpacity = style.paint.getDataValue(
112
+ 'line-opacity',
113
+ tile.z,
114
+ feature
115
+ )
116
+
117
+ if (lineWidth !== feature.lineWidth) {
118
+ warnOnce('不支持动态修改 line 图层样式属性:line-width')
119
+ }
120
+
121
+ feature.lineColor = lineColor
122
+ feature.lineOpacity = lineOpacity
123
+ feature.lineWidth = lineWidth
124
+
125
+ const batchId = feature.batchId
126
+ const colorBytes = lineColor.toBytes()
127
+ colorBytes[3] = Math.floor(colorBytes[3] * lineOpacity)
128
+ batchTable.setBatchedAttribute(batchId, 0, {
129
+ x: colorBytes[0],
130
+ y: colorBytes[1],
131
+ z: colorBytes[2],
132
+ w: colorBytes[3]
133
+ })
134
+ }
135
+
136
+ this.paintNeedsUpdate = false
137
+ }
95
138
  super.update(frameState, tileset)
96
139
  }
97
140
 
@@ -22,6 +22,41 @@ export class SymbolRenderLayer extends IRenderLayer {
22
22
  */
23
23
  update(frameState, tileset) {
24
24
  //TODO:动态更新符号样式
25
+ if (this.paintNeedsUpdate) {
26
+ const style = this.style,
27
+ tile = this.tile
28
+
29
+ for (let i = 0; i < this.features.length; i++) {
30
+ const feature = this.features[i]
31
+ const label = this.labels[i]
32
+
33
+ const textColor = style.convertColor(
34
+ style.paint.getDataValue('text-color', tile.z, feature)
35
+ )
36
+ const outlineColor = style.convertColor(
37
+ style.paint.getDataValue('text-halo-color', tile.z, feature)
38
+ )
39
+ const outlineWidth = style.paint.getDataValue(
40
+ 'text-halo-width',
41
+ tile.z,
42
+ feature
43
+ )
44
+
45
+ feature.textColor = textColor
46
+ feature.outlineColor = outlineColor
47
+ feature.outlineWidth = outlineWidth
48
+
49
+ label.fillColor = textColor
50
+ label.style = outlineWidth && Cesium.LabelStyle.FILL_AND_OUTLINE
51
+ label.outlineWidth = outlineWidth * feature.textSize
52
+ label.outlineColor = outlineColor
53
+
54
+ label._baseFillColor = label.fillColor.clone()
55
+ label._baseOutlineColor = label.outlineColor.clone()
56
+ }
57
+
58
+ this.paintNeedsUpdate = false
59
+ }
25
60
 
26
61
  super.update(frameState, tileset)
27
62
  }
@@ -22,6 +22,7 @@ export class FillLayerVisualizer extends ILayerVisualizer {
22
22
  super(layers, tile)
23
23
 
24
24
  this.geometryInstances = []
25
+ this.outlineGeometryInstances = []
25
26
  this.primitive = null
26
27
  this.commandsReady = false
27
28
  }
@@ -67,6 +68,11 @@ export class FillLayerVisualizer extends ILayerVisualizer {
67
68
  sourceFeature
68
69
  )
69
70
 
71
+ const fillOutlineColor =
72
+ style.convertColor(
73
+ style.paint.getDataValue('fill-outline-color', tile.z, sourceFeature)
74
+ ) || fillColor
75
+
70
76
  //关键:对投影坐标细分,而不是使用cesium内置的细分
71
77
  const vtCoords = loadGeometry(sourceFeature)
72
78
  const polygons = classifyRings(vtCoords)
@@ -84,6 +90,7 @@ export class FillLayerVisualizer extends ILayerVisualizer {
84
90
  featureId,
85
91
  fillColor,
86
92
  fillOpacity,
93
+ fillOutlineColor,
87
94
  properties,
88
95
  //保存原始数据的要素id,后续可以用来支持 featureState 表达式,这个表达式可以实现选定要素高亮显示
89
96
  id: sourceFeatureId,
@@ -92,6 +99,7 @@ export class FillLayerVisualizer extends ILayerVisualizer {
92
99
  }
93
100
 
94
101
  scope.addFeature(fillFeature, granularity)
102
+ layer.features.push(fillFeature)
95
103
 
96
104
  featureId++
97
105
  }
@@ -186,9 +194,12 @@ export class FillLayerVisualizer extends ILayerVisualizer {
186
194
  */
187
195
  addFeature(feature, granularity) {
188
196
  const geometryInstances = this.geometryInstances
189
- const { coordinates, fillColor, fillOpacity } = feature
197
+ const outlineGeometryInstances = this.outlineGeometryInstances
198
+ const { coordinates, fillColor, fillOpacity, fillOutlineColor } = feature
190
199
  const colorBytes = fillColor.toBytes()
191
200
  colorBytes[3] = Math.floor(colorBytes[3] * fillOpacity)
201
+ const outlineColorBytes = fillOutlineColor.toBytes()
202
+ outlineColorBytes[3] = Math.floor(outlineColorBytes[3] * fillOpacity)
192
203
 
193
204
  // 使用 maplibre-gl 的 subdividePolygon 基于投影坐标进行细分,
194
205
  // 而不是在转成世界坐标后再使用 Cesium.PolygonGeometry 构建,这样才能避免出现自相交、破面等现象。
@@ -198,7 +209,7 @@ export class FillLayerVisualizer extends ILayerVisualizer {
198
209
  coordinates,
199
210
  this.tile,
200
211
  granularity,
201
- false
212
+ true
202
213
  )
203
214
  const verticesFlattened = subdivisionRes.verticesFlattened
204
215
  const coordDeg = [0, 0],
@@ -240,6 +251,14 @@ export class FillLayerVisualizer extends ILayerVisualizer {
240
251
  : Uint8Array
241
252
  )(subdivisionRes.indicesTriangles)
242
253
 
254
+ const outlineIndices = new (
255
+ vertCount > 65535
256
+ ? Uint32Array
257
+ : vertCount > 255
258
+ ? Uint16Array
259
+ : Uint8Array
260
+ )(subdivisionRes.indicesLineList.flat(3))
261
+
243
262
  const geometry = new Cesium.Geometry({
244
263
  attributes: {
245
264
  position: {
@@ -265,6 +284,19 @@ export class FillLayerVisualizer extends ILayerVisualizer {
265
284
  indices: indices,
266
285
  boundingSphere: Cesium.BoundingSphere.fromVertices(positions)
267
286
  })
287
+ const outlineGeometry = new Cesium.Geometry({
288
+ attributes: {
289
+ position: {
290
+ componentDatatype: Cesium.ComponentDatatype.DOUBLE,
291
+ componentsPerAttribute: 3,
292
+ normalize: false,
293
+ values: positions
294
+ }
295
+ },
296
+ primitiveType: Cesium.PrimitiveType.LINES,
297
+ indices: outlineIndices,
298
+ boundingSphere: Cesium.BoundingSphere.fromVertices(positions)
299
+ })
268
300
 
269
301
  const cartographic = Cesium.Cartographic.fromCartesian(
270
302
  geometry.boundingSphere.center
@@ -294,6 +326,25 @@ export class FillLayerVisualizer extends ILayerVisualizer {
294
326
  })
295
327
  })
296
328
  geometryInstances.push(instance)
329
+
330
+ const outlineInstance = new Cesium.GeometryInstance({
331
+ geometry: outlineGeometry,
332
+ attributes: {
333
+ color: new Cesium.GeometryInstanceAttribute({
334
+ componentDatatype: Cesium.ComponentDatatype.UNSIGNED_BYTE,
335
+ componentsPerAttribute: 4,
336
+ normalize: true,
337
+ value: outlineColorBytes
338
+ })
339
+ },
340
+ //通过entity的形式暴露给Cesium pickEntity,这样点击时系统自带的inforbox可以弹出
341
+ id: new Cesium.Entity({
342
+ position: center,
343
+ id: feature.id,
344
+ properties: feature.properties
345
+ })
346
+ })
347
+ outlineGeometryInstances.push(outlineInstance)
297
348
  }
298
349
 
299
350
  createPrimitive() {
@@ -427,6 +478,143 @@ void main()
427
478
  */
428
479
  onBatchTableCreated(batchTable) {
429
480
  this._batchTable = batchTable
481
+ for (const layer of this.layers) {
482
+ layer._batchTable = batchTable
483
+ }
484
+ }
485
+
486
+ createOutlinePrimitive() {
487
+ const primitive = new Cesium.Primitive({
488
+ geometryInstances: this.outlineGeometryInstances,
489
+ asynchronous: !(
490
+ this.outlineGeometryInstances[0].geometry instanceof Cesium.Geometry
491
+ ),
492
+ appearance: new Cesium.PerInstanceColorAppearance({
493
+ flat: true,
494
+ translucent: false,
495
+ renderState: {
496
+ //这里设置是没有用的,只要 translucent 为 false,
497
+ //Cesium 内部都会覆盖成 true,所以我们需要在 DrawCommand 创建完成后再设置
498
+ depthMask: false
499
+ },
500
+ fragmentShaderSource: /*glsl*/ `
501
+ in vec4 v_color;
502
+
503
+ uniform vec4 tileId;
504
+ uniform sampler2D tileIdTexture;
505
+
506
+ void main()
507
+ {
508
+ vec2 id_st = gl_FragCoord.xy / czm_viewport.zw;
509
+ vec4 bgId = texture(tileIdTexture, id_st);
510
+ if (!all(equal(bgId, tileId)))
511
+ {
512
+ discard;
513
+ }
514
+ out_FragColor = v_color;
515
+ }
516
+ `
517
+ })
518
+ })
519
+
520
+ //通过定义 Primitive 私有变量 _geometries、_batchTable 的 setter 和 getter,
521
+ //监听合批几何体和批次表的创建:
522
+ //1、几何体创建完成后,根据 batchId 和 featureId,计算每个图层几何体的起始索引(offset)和索引数量(count)
523
+ //2、批次表创建完成后,保存备用
524
+ let scope = this
525
+ Object.defineProperties(primitive, {
526
+ _geometries: {
527
+ get() {
528
+ return this._geometries_
529
+ },
530
+ set(geometries) {
531
+ this._geometries_ = geometries
532
+ if (geometries) {
533
+ scope.onOutlineGeometriesLoaded(geometries)
534
+ } else {
535
+ scope = null
536
+ }
537
+ }
538
+ },
539
+ _batchTable: {
540
+ get() {
541
+ return this._batchTable_
542
+ },
543
+ set(batchTable) {
544
+ this._batchTable_ = batchTable
545
+ if (batchTable) {
546
+ scope.onOutlineBatchTableCreated(batchTable)
547
+ }
548
+ }
549
+ }
550
+ })
551
+
552
+ this.outlinePrimitive = primitive
553
+ }
554
+ /**
555
+ * 根据 batchId 和 featureId,计算每个图层几何体的起始索引(offset)和索引数量(count)
556
+ * @param {Cesium.Geometry[]} geometries
557
+ */
558
+ onOutlineGeometriesLoaded(geometries) {
559
+ //Cesium 几何体合批结果可能是多个几何体,对应多个 DrawCommand
560
+ for (let pass = 0; pass < geometries.length; pass++) {
561
+ const batches = {}
562
+ const geometry = geometries[pass]
563
+ const batchIds = geometry.attributes.batchId.values
564
+ const indices = geometry.indices
565
+
566
+ //提取每个批次的起始和结束索引
567
+ let currBatchId = -1
568
+ let currBatch = null
569
+ for (let i = 0; i < indices.length; i++) {
570
+ const vertIndex = indices[i]
571
+ const batchId = batchIds[vertIndex]
572
+ if (currBatchId !== batchId) {
573
+ currBatchId = batchId
574
+ currBatch = batches[currBatchId] = {
575
+ begin: i,
576
+ end: i
577
+ }
578
+ }
579
+ currBatch.end = i
580
+ }
581
+
582
+ //根据图层批次范围,提取图层几何体索引范围,即起始索引(offset)和索引数量(count)
583
+ for (const layer of this.layers) {
584
+ const { firstBatchId, lastBatchId } = layer
585
+ if (firstBatchId === -1 || lastBatchId === -1) {
586
+ continue
587
+ }
588
+
589
+ let begin = -1,
590
+ end = -1
591
+ for (let batchId = firstBatchId; batchId <= lastBatchId; batchId++) {
592
+ const batch = batches[batchId]
593
+ if (batch) {
594
+ if (begin === -1) begin = batch.begin
595
+ end = batch.end
596
+ }
597
+ }
598
+
599
+ if (begin === -1 || end === -1) {
600
+ continue
601
+ }
602
+
603
+ //起始和结束索引,索引数量需要加1
604
+ layer.outlineOffsets[pass] = begin
605
+ layer.outlineCounts[pass] = end - begin + 1
606
+ }
607
+ }
608
+ }
609
+ /**
610
+ * 保存 Cesium Primitive 创建的批次表。图层样式变化时,通过更新批次表传递到GPU,同步更新渲染效果
611
+ * @param {Cesium.BatchTable} batchTable
612
+ */
613
+ onOutlineBatchTableCreated(batchTable) {
614
+ this._outlineBatchTable = batchTable
615
+ for (const layer of this.layers) {
616
+ layer._outlineBatchTable = batchTable
617
+ }
430
618
  }
431
619
 
432
620
  /**
@@ -452,14 +640,29 @@ void main()
452
640
  for (let i = 0; i < this.layers.length; i++) {
453
641
  const layer = this.layers[i]
454
642
  const layerCommandList = (layer.commandList = [])
643
+ const passes = {
644
+ fill: 0,
645
+ outline: 0
646
+ }
455
647
 
456
- for (let pass = 0; pass < batchedCommandList.length; pass++) {
457
- const offset = layer.offsets[pass],
458
- count = layer.counts[pass]
648
+ for (let i = 0; i < batchedCommandList.length; i++) {
649
+ const command = batchedCommandList[i]
650
+ let pass, offsets, counts
651
+ if (command.primitiveType == Cesium.PrimitiveType.LINES) {
652
+ pass = passes.outline++
653
+ offsets = layer.outlineOffsets
654
+ counts = layer.outlineCounts
655
+ } else {
656
+ pass = passes.fill++
657
+ offsets = layer.offsets
658
+ counts = layer.counts
659
+ }
660
+
661
+ const offset = offsets[pass],
662
+ count = counts[pass]
459
663
  if (typeof offset !== 'number' || typeof count !== 'number') {
460
664
  continue
461
665
  }
462
- const command = batchedCommandList[pass]
463
666
  command.uniformMap.tileIdTexture = function () {
464
667
  return tileset.tileIdTexture
465
668
  }
@@ -493,6 +696,7 @@ void main()
493
696
 
494
697
  if (!this.primitive && this.geometryInstances.length) {
495
698
  this.createPrimitive()
699
+ this.createOutlinePrimitive()
496
700
  }
497
701
 
498
702
  if (this.primitive && this.state !== 'done' && this.state !== 'error') {
@@ -504,13 +708,18 @@ void main()
504
708
  //执行 primitive 的 update ,直到生成了合批后的drawCommand
505
709
  try {
506
710
  this.primitive.update(frameState)
711
+ this.outlinePrimitive.update(frameState)
507
712
  } catch (err) {
508
713
  //如果报错,下一帧就不要再执行 update 了,以免重复打印错误信息
509
714
  this.geometryInstances = []
715
+ this.outlineGeometryInstances = []
510
716
  this.setState('error')
511
717
  if (err.stack) console.trace(err.stack)
512
718
  else console.error(err)
513
719
  return
720
+ } finally {
721
+ //恢复系统的 commandList
722
+ frameState.commandList = preCommandList
514
723
  }
515
724
 
516
725
  //使用合批后的 drawCommand 创建副本,为渲染图层分配 drawCommand
@@ -518,14 +727,22 @@ void main()
518
727
  this.createLayerCommands(batchedCommandList, tileset)
519
728
  }
520
729
 
521
- if (this.primitive._state === Cesium.PrimitiveState.FAILED) {
730
+ if (
731
+ this.primitive._state === Cesium.PrimitiveState.FAILED ||
732
+ this.outlinePrimitive._state === Cesium.PrimitiveState.FAILED
733
+ ) {
522
734
  this.setState('error')
523
735
  }
524
736
 
525
- //恢复系统的 commandList
526
- frameState.commandList = preCommandList
527
737
  this.geometryInstances = []
528
738
  }
739
+
740
+ if (this._batchTable && this._batchTable._batchValuesDirty) {
741
+ this._batchTable.update(frameState)
742
+ }
743
+ if (this._outlineBatchTable && this._outlineBatchTable._batchValuesDirty) {
744
+ this._outlineBatchTable.update(frameState)
745
+ }
529
746
  }
530
747
 
531
748
  destroy() {