@treasuryspatial/map-kit 0.1.3 → 0.1.5

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.
@@ -13,6 +13,7 @@ export type MapThreeLayerOptions = {
13
13
  id: string;
14
14
  zOffsetMeters?: number;
15
15
  onInit?: (scene: THREE.Scene, renderer: THREE.WebGLRenderer, camera: THREE.Camera) => void;
16
+ debug?: boolean;
16
17
  };
17
18
  export declare function createMapThreeLayer(options: MapThreeLayerOptions): MapThreeLayer;
18
19
  //# sourceMappingURL=mapThreeLayer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mapThreeLayer.d.ts","sourceRoot":"","sources":["../src/mapThreeLayer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,SAAS,EAAE,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IACjE,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC;CAC5F,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAoJhF"}
1
+ {"version":3,"file":"mapThreeLayer.d.ts","sourceRoot":"","sources":["../src/mapThreeLayer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,SAAS,EAAE,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IACjE,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC;IAC3F,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CA+OhF"}
@@ -1,7 +1,7 @@
1
1
  import mapboxgl from 'mapbox-gl';
2
2
  import * as THREE from 'three';
3
3
  export function createMapThreeLayer(options) {
4
- const { id, zOffsetMeters = 0.02, onInit } = options;
4
+ const { id, zOffsetMeters = 0.02, onInit, debug = false } = options;
5
5
  let mapRef = null;
6
6
  let scene = null;
7
7
  let camera = null;
@@ -18,6 +18,55 @@ export function createMapThreeLayer(options) {
18
18
  let zOffset = zOffsetMeters;
19
19
  let targetWidth = 0;
20
20
  let targetHeight = 0;
21
+ let renderDisabled = false;
22
+ let errorCount = 0;
23
+ let hasRenderable = false;
24
+ const log = (...args) => {
25
+ if (!debug)
26
+ return;
27
+ console.info(`[map-kit:${id}]`, ...args);
28
+ };
29
+ const logError = (message, err) => {
30
+ console.error(`[map-kit:${id}] ${message}`, err);
31
+ };
32
+ const logGlError = (gl, label) => {
33
+ if (!debug)
34
+ return;
35
+ const error = gl.getError();
36
+ if (error !== gl.NO_ERROR) {
37
+ log(`GL error (${label})`, error);
38
+ }
39
+ };
40
+ const checkFramebuffer = (gl, label) => {
41
+ if (!debug)
42
+ return;
43
+ const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
44
+ if (status !== gl.FRAMEBUFFER_COMPLETE) {
45
+ log(`Framebuffer incomplete (${label})`, status);
46
+ }
47
+ };
48
+ const updateRenderable = (group) => {
49
+ if (!group) {
50
+ hasRenderable = false;
51
+ return;
52
+ }
53
+ let found = false;
54
+ group.traverse((child) => {
55
+ if (found)
56
+ return;
57
+ const mesh = child;
58
+ if (!mesh.isMesh)
59
+ return;
60
+ if (mesh.visible === false)
61
+ return;
62
+ const geometry = mesh.geometry;
63
+ const position = geometry?.getAttribute?.('position');
64
+ if (position && position.count > 0) {
65
+ found = true;
66
+ }
67
+ });
68
+ hasRenderable = found;
69
+ };
21
70
  const layer = {
22
71
  id,
23
72
  type: 'custom',
@@ -46,6 +95,7 @@ export function createMapThreeLayer(options) {
46
95
  });
47
96
  renderTarget.texture.colorSpace = THREE.SRGBColorSpace;
48
97
  renderTarget.texture.premultiplyAlpha = true;
98
+ log('renderTarget', { width: targetWidth, height: targetHeight });
49
99
  screenScene = new THREE.Scene();
50
100
  screenCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
51
101
  screenMaterial = new THREE.MeshBasicMaterial({
@@ -66,41 +116,89 @@ export function createMapThreeLayer(options) {
66
116
  onInit(scene, renderer, camera);
67
117
  },
68
118
  prerender: (_gl, matrix) => {
119
+ if (renderDisabled)
120
+ return;
69
121
  if (!renderer || !scene || !camera || !rootGroup || !renderTarget)
70
122
  return;
71
- camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);
72
- if (mercatorOrigin) {
73
- const translate = new THREE.Matrix4().makeTranslation(mercatorOrigin.x, mercatorOrigin.y, (mercatorOrigin.z ?? 0) + zOffset * meterScale);
74
- const scale = new THREE.Matrix4().makeScale(meterScale, -meterScale, meterScale);
75
- rootGroup.matrix = new THREE.Matrix4().multiplyMatrices(translate, scale);
76
- rootGroup.matrixWorldNeedsUpdate = true;
123
+ if (!hasRenderable)
124
+ return;
125
+ try {
126
+ camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);
127
+ if (mercatorOrigin) {
128
+ const translate = new THREE.Matrix4().makeTranslation(mercatorOrigin.x, mercatorOrigin.y, (mercatorOrigin.z ?? 0) + zOffset * meterScale);
129
+ const scale = new THREE.Matrix4().makeScale(meterScale, -meterScale, meterScale);
130
+ rootGroup.matrix = new THREE.Matrix4().multiplyMatrices(translate, scale);
131
+ rootGroup.matrixWorldNeedsUpdate = true;
132
+ }
133
+ const canvas = mapRef?.getCanvas();
134
+ if (canvas && (canvas.width !== targetWidth || canvas.height !== targetHeight)) {
135
+ targetWidth = canvas.width;
136
+ targetHeight = canvas.height;
137
+ renderTarget.setSize(targetWidth, targetHeight);
138
+ log('renderTarget resized', { width: targetWidth, height: targetHeight });
139
+ }
140
+ renderer.setRenderTarget(renderTarget);
141
+ renderer.setClearColor(0x000000, 0);
142
+ renderer.setClearAlpha(0);
143
+ renderer.clear(true, true, true);
144
+ const gl = renderer.getContext();
145
+ checkFramebuffer(gl, 'prerender');
146
+ renderer.render(scene, camera);
147
+ logGlError(gl, 'prerender');
148
+ renderer.setRenderTarget(null);
77
149
  }
78
- const canvas = mapRef?.getCanvas();
79
- if (canvas && (canvas.width !== targetWidth || canvas.height !== targetHeight)) {
80
- targetWidth = canvas.width;
81
- targetHeight = canvas.height;
82
- renderTarget.setSize(targetWidth, targetHeight);
150
+ catch (err) {
151
+ errorCount += 1;
152
+ logError('prerender failed', err);
153
+ if (errorCount > 2) {
154
+ renderDisabled = true;
155
+ log('render disabled after repeated errors');
156
+ }
83
157
  }
84
- renderer.setRenderTarget(renderTarget);
85
- renderer.clear(true, true, true);
86
- renderer.render(scene, camera);
87
- renderer.setRenderTarget(null);
88
158
  },
89
159
  render: (gl) => {
160
+ if (renderDisabled)
161
+ return;
90
162
  if (!renderer || !screenScene || !screenCamera)
91
163
  return;
92
- renderer.resetState();
93
- gl.disable(gl.DEPTH_TEST);
94
- gl.enable(gl.BLEND);
95
- gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
96
- renderer.render(screenScene, screenCamera);
97
- mapRef?.triggerRepaint();
164
+ if (!hasRenderable)
165
+ return;
166
+ try {
167
+ renderer.resetState();
168
+ gl.disable(gl.DEPTH_TEST);
169
+ gl.depthMask(false);
170
+ gl.disable(gl.STENCIL_TEST);
171
+ gl.disable(gl.SCISSOR_TEST);
172
+ gl.colorMask(true, true, true, true);
173
+ gl.enable(gl.BLEND);
174
+ gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
175
+ const canvas = mapRef?.getCanvas();
176
+ if (canvas) {
177
+ renderer.setViewport(0, 0, canvas.width, canvas.height);
178
+ }
179
+ renderer.render(screenScene, screenCamera);
180
+ logGlError(gl, 'render');
181
+ renderer.resetState();
182
+ mapRef?.triggerRepaint();
183
+ }
184
+ catch (err) {
185
+ errorCount += 1;
186
+ logError('render failed', err);
187
+ if (errorCount > 2) {
188
+ renderDisabled = true;
189
+ log('render disabled after repeated errors');
190
+ }
191
+ }
98
192
  },
99
193
  };
100
194
  return {
101
195
  layer,
102
196
  setGroup: (group) => {
103
197
  meshGroup = group;
198
+ updateRenderable(group);
199
+ if (debug) {
200
+ log('group set', { hasRenderable });
201
+ }
104
202
  if (rootGroup) {
105
203
  rootGroup.clear();
106
204
  if (meshGroup)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treasuryspatial/map-kit",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "main": "./dist/index.js",