Skip to content

Commit 8342871

Browse files
ibesoragithub-actions[bot]
authored andcommitted
Model scope fixes (internal-8073)
GitOrigin-RevId: 1cd6807807f3e72e33590ec863e4f3b0b9b02ed9
1 parent e5af912 commit 8342871

File tree

5 files changed

+109
-16
lines changed

5 files changed

+109
-16
lines changed

3d-style/render/draw_model.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import type VertexBuffer from '../../src/gl/vertex_buffer';
3939
import type {CutoffParams} from '../../src/render/cutoff';
4040
import type {LUT} from "../../src/util/lut";
4141
import type {ModelUniformsType, ModelDepthUniformsType} from '../render/program/model_program';
42-
import type {Source} from '../../src/source/source';
4342

4443
export default drawModels;
4544

@@ -261,23 +260,19 @@ function drawMesh(sortedMesh: SortedMesh, painter: Painter, layer: ModelStyleLay
261260
undefined, dynamicBuffers);
262261
}
263262

264-
function getScope(source: Source, layer: ModelStyleLayer, importedAsBasemap: boolean) {
265-
// When using geojson layers, models are always set in the root scope to avoid model duplication
266-
// But when the root style is defined as a fragment or has an schema, we use the well-known "basemap"
267-
// scope to load the style so models will be set there.
268-
return source.type === 'vector' ? layer.scope : importedAsBasemap ? "basemap" : "";
263+
function getScope(painter: Painter, layer: ModelStyleLayer) {
264+
return painter.style._importedAsBasemap ? "basemap" : layer.scope;
269265
}
270266

271267
export function prepare(layer: ModelStyleLayer, sourceCache: SourceCache, painter: Painter) {
272268
const modelSource = sourceCache.getSource();
273269
if (!modelSource.loaded()) return;
274270

275271
if (modelSource.type === 'vector' || modelSource.type === 'geojson') {
276-
const scope = getScope(modelSource, layer, painter.style._importedAsBasemap);
277272
if (painter.modelManager) {
278273
// Do it here, to prevent modelManager handling in Painter.
279274
// geojson models are always set in the root scope to avoid model duplication
280-
painter.modelManager.upload(painter, scope);
275+
painter.modelManager.upload(painter, getScope(painter, layer));
281276
}
282277
return;
283278
}
@@ -448,8 +443,7 @@ function drawModels(painter: Painter, sourceCache: SourceCache, layer: ModelStyl
448443
}
449444

450445
if (modelSource.type === 'vector' || modelSource.type === 'geojson') {
451-
const scope = getScope(modelSource, layer, painter.style._importedAsBasemap);
452-
drawVectorLayerModels(painter, sourceCache, layer, coords, scope);
446+
drawVectorLayerModels(painter, sourceCache, layer, coords, getScope(painter, layer));
453447
cleanup();
454448
return;
455449
}

src/style/style.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,8 +2044,12 @@ class Style extends Evented<MapEvents> {
20442044
return this._availableImages.slice();
20452045
}
20462046

2047+
getActualScope() {
2048+
return this._importedAsBasemap ? "basemap" : this.scope;
2049+
}
2050+
20472051
addModelURLs(models: ModelsSpecification): this {
2048-
this.modelManager.addModelURLs(models, this.scope);
2052+
this.modelManager.addModelURLs(models, this.getActualScope());
20492053
this._updateWorkerModels();
20502054
this.fire(new Event('data', {dataType: 'style'}));
20512055
return this;
@@ -2055,13 +2059,13 @@ class Style extends Evented<MapEvents> {
20552059
this._checkLoaded();
20562060
if (this._validate(validateModel, `models.${id}`, url, null, options)) return this;
20572061

2058-
this.modelManager.addModel(id, url, this.scope);
2062+
this.modelManager.addModel(id, url, this.getActualScope());
20592063
this.fire(new Event('data', {dataType: 'style'}));
20602064
return this;
20612065
}
20622066

20632067
hasModel(id: string): boolean {
2064-
return this.modelManager.hasModel(id, this.scope);
2068+
return this.modelManager.hasModel(id, this.getActualScope());
20652069
}
20662070

20672071
removeModel(id: string): this {
@@ -2070,14 +2074,14 @@ class Style extends Evented<MapEvents> {
20702074
}
20712075
const keepModelURI = false;
20722076
const forceRemoval = true;
2073-
this.modelManager.removeModel(id, this.scope, keepModelURI, forceRemoval);
2077+
this.modelManager.removeModel(id, this.getActualScope(), keepModelURI, forceRemoval);
20742078
this.fire(new Event('data', {dataType: 'style'}));
20752079
return this;
20762080
}
20772081

20782082
listModels(): Array<string> {
20792083
this._checkLoaded();
2080-
return this.modelManager.listModels(this.scope);
2084+
return this.modelManager.listModels(this.getActualScope());
20812085
}
20822086

20832087
addSource(id: string, source: (SourceSpecification | CustomSourceInterface<unknown>) & {collectResourceTiming?: boolean}, options: StyleSetterOptions = {}): void {
79.8 KB
Loading
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"version": 8,
3+
"metadata": {
4+
"test": {
5+
"height": 512,
6+
"allowed": 0.0015,
7+
"operations": [
8+
["addSource",
9+
"trees",
10+
{
11+
"type": "geojson",
12+
"data": "local://data/trees.geojson"
13+
}],
14+
["addLayer", {
15+
"id": "tree-layer-diffuse",
16+
"type": "model",
17+
"source": "trees",
18+
"layout": {
19+
"model-id": "tree-diffuse"
20+
},
21+
"paint": {
22+
"model-scale": [ 70.0, 50.0, 50.0],
23+
"model-translation": [0, 0, 100]
24+
}
25+
}
26+
],
27+
["addModel", "tree-diffuse", "local://models/tree-no-material.glb"],
28+
["wait"]
29+
]
30+
}
31+
},
32+
"sources": {
33+
},
34+
"transition": {
35+
"duration": 0
36+
},
37+
"models": {
38+
},
39+
"pitch": 60,
40+
"bearing": 0,
41+
"zoom": 15,
42+
"schema": {},
43+
"center": [
44+
-122.40784,
45+
37.78432
46+
],
47+
"light": {
48+
"intensity": 1,
49+
"position": [1, 110, 90],
50+
"anchor": "map"
51+
},
52+
"layers": [
53+
]
54+
}

test/unit/render/model_manager.test.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ describe('ModelManager', () => {
7878
});
7979

8080
// eslint-disable-next-line @typescript-eslint/require-await
81-
test("#addModel with different ids but with same URL increases it's number of references but doesn't load it again", async () => {
81+
test("#addModel with different ids but with same URL and scope increases it's number of references but doesn't load it again", async () => {
8282
const {modelManager, eventedParent} = createModelManager();
8383

8484
eventedParent.on('error', ({error}) => {
@@ -96,6 +96,25 @@ describe('ModelManager', () => {
9696
expect(modelManager.models['basemap']['model'].numReferences).toBe(2);
9797
});
9898

99+
// eslint-disable-next-line @typescript-eslint/require-await
100+
test("#addModel with different ids and scope but with same URL increases it's number of references but doesn't load it again", async () => {
101+
const {modelManager, eventedParent} = createModelManager();
102+
103+
eventedParent.on('error', ({error}) => {
104+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
105+
expect.unreachable(error.message);
106+
});
107+
108+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
109+
modelManager.loadModel = vi.fn((id, url) => Promise.resolve({id, url}));
110+
111+
modelManager.addModel('model', 'https://www.example.com/', 'basemap');
112+
modelManager.addModel('model2', 'https://www.example.com/', '');
113+
114+
expect(modelManager.loadModel).toHaveBeenCalledOnce();
115+
expect(modelManager.models['basemap']['model'].numReferences).toBe(2);
116+
});
117+
99118
// eslint-disable-next-line @typescript-eslint/require-await
100119
test('#removeModel', async () => {
101120
const {modelManager, eventedParent} = createModelManager();
@@ -188,6 +207,28 @@ describe('ModelManager', () => {
188207
modelManager.addModelsFromBucket(['https://www.example.com/1', 'https://www.example.com/2', 'https://www.example.com/3'], 'basemap');
189208
});
190209

210+
// eslint-disable-next-line @typescript-eslint/require-await
211+
test('#addModelsFromBucket doesn\t reload existing models', async () => {
212+
const {modelManager, eventedParent} = createModelManager();
213+
214+
eventedParent.on('error', ({error}) => {
215+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
216+
expect.unreachable(error.message);
217+
});
218+
219+
vi.spyOn(modelManager, 'loadModel').mockImplementation(
220+
(id, url) => url.startsWith('https://www.example.com/') ? Promise.resolve({id, url}) : Promise.reject(new Error('Not found'))
221+
);
222+
223+
eventedParent.once('data', () => {
224+
modelManager.addModelsFromBucket(['https://www.example.com/1'], 'basemap');
225+
expect(modelManager.models['basemap']['https://www.example.com/1'].numReferences).toBe(2);
226+
});
227+
228+
modelManager.addModelsFromBucket(['https://www.example.com/1', 'https://www.example.com/2', 'https://www.example.com/3'], 'basemap');
229+
230+
});
231+
191232
test('#reloadModels', () => {
192233
const {modelManager, eventedParent} = createModelManager();
193234

0 commit comments

Comments
 (0)