• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

iTowns / itowns / 10635241580

30 Aug 2024 03:26PM UTC coverage: 86.966% (-2.8%) from 89.766%
10635241580

push

github

jailln
feat(3dtiles): add new OGC3DTilesLayer using 3d-tiles-renderer-js

Deprecate C3DTilesLayer (replaced by OGC3DTilesLayer).
Add new iGLTFLoader that loads gltf 1.0 and 2.0 files.

2791 of 3694 branches covered (75.55%)

Branch coverage included in aggregate %.

480 of 644 new or added lines in 8 files covered. (74.53%)

2144 existing lines in 111 files now uncovered.

24319 of 27479 relevant lines covered (88.5%)

1024.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.04
/src/Core/MainLoop.js
1
import { EventDispatcher } from 'three';
1✔
2

1✔
3
export const RENDERING_PAUSED = 0;
1✔
4
export const RENDERING_SCHEDULED = 1;
1✔
5

1✔
6
/**
1✔
7
 * MainLoop's update events list that are fired using
1✔
8
 * {@link View#execFrameRequesters}.
1✔
9
 *
1✔
10
 * @property UPDATE_START {string} fired at the start of the update
1✔
11
 * @property BEFORE_CAMERA_UPDATE {string} fired before the camera update
1✔
12
 * @property AFTER_CAMERA_UPDATE {string} fired after the camera update
1✔
13
 * @property BEFORE_LAYER_UPDATE {string} fired before the layer update
1✔
14
 * @property AFTER_LAYER_UPDATE {string} fired after the layer update
1✔
15
 * @property BEFORE_RENDER {string} fired before the render
1✔
16
 * @property AFTER_RENDER {string} fired after the render
1✔
17
 * @property UPDATE_END {string} fired at the end of the update
1✔
18
 */
1✔
19

1✔
20
export const MAIN_LOOP_EVENTS = {
1✔
21
    UPDATE_START: 'update_start',
1✔
22
    BEFORE_CAMERA_UPDATE: 'before_camera_update',
1✔
23
    AFTER_CAMERA_UPDATE: 'after_camera_update',
1✔
24
    BEFORE_LAYER_UPDATE: 'before_layer_update',
1✔
25
    AFTER_LAYER_UPDATE: 'after_layer_update',
1✔
26
    BEFORE_RENDER: 'before_render',
1✔
27
    AFTER_RENDER: 'after_render',
1✔
28
    UPDATE_END: 'update_end',
1✔
29
};
1✔
30

1✔
31
function updateElements(context, geometryLayer, elements) {
6✔
32
    if (!elements) {
6!
UNCOV
33
        return;
×
UNCOV
34
    }
×
35
    for (const element of elements) {
6✔
36
        // update element
4✔
37
        // TODO find a way to notify attachedLayers when geometryLayer deletes some elements
4✔
38
        // and then update Debug.js:addGeometryLayerDebugFeatures
4✔
39
        const newElementsToUpdate = geometryLayer.update(context, geometryLayer, element);
4✔
40

4✔
41
        const sub = geometryLayer.getObjectToUpdateForAttachedLayers(element);
4✔
42

4✔
43
        if (sub) {
4!
44
            if (sub.element) {
×
45
                if (__DEBUG__) {
×
46
                    if (!(sub.element.isObject3D)) {
×
47
                        throw new Error(`
×
48
                            Invalid object for attached layer to update.
×
49
                            Must be a THREE.Object and have a THREE.Material`);
×
50
                    }
×
51
                }
×
52
                // update attached layers
×
53
                for (const attachedLayer of geometryLayer.attachedLayers) {
×
54
                    if (attachedLayer.ready) {
×
55
                        attachedLayer.update(context, attachedLayer, sub.element, sub.parent);
×
56
                        attachedLayer.cache.flush();
×
57
                    }
×
58
                }
×
59
            } else if (sub.elements) {
×
60
                for (let i = 0; i < sub.elements.length; i++) {
×
61
                    if (!(sub.elements[i].isObject3D)) {
×
62
                        throw new Error(`
×
UNCOV
63
                            Invalid object for attached layer to update.
×
UNCOV
64
                            Must be a THREE.Object and have a THREE.Material`);
×
UNCOV
65
                    }
×
UNCOV
66
                    // update attached layers
×
UNCOV
67
                    for (const attachedLayer of geometryLayer.attachedLayers) {
×
UNCOV
68
                        if (attachedLayer.ready) {
×
UNCOV
69
                            attachedLayer.update(context, attachedLayer, sub.elements[i], sub.parent);
×
UNCOV
70
                            attachedLayer.cache.flush();
×
UNCOV
71
                        }
×
UNCOV
72
                    }
×
UNCOV
73
                }
×
UNCOV
74
            }
×
UNCOV
75
        }
×
76
        updateElements(context, geometryLayer, newElementsToUpdate);
4✔
77
    }
4✔
78
}
6✔
79

1✔
80
function filterChangeSources(updateSources, geometryLayer) {
2✔
81
    let fullUpdate = false;
2✔
82
    const filtered = new Set();
2✔
83
    updateSources.forEach((src) => {
2✔
84
        if (src === geometryLayer || src.isCamera) {
2!
85
            geometryLayer.info.clear();
2✔
86
            fullUpdate = true;
2✔
87
        } else if (src.layer === geometryLayer) {
2!
UNCOV
88
            filtered.add(src);
×
UNCOV
89
        }
×
90
    });
2✔
91
    return fullUpdate ? new Set([geometryLayer]) : filtered;
2!
92
}
2✔
93

1✔
94

1✔
95
class MainLoop extends EventDispatcher {
44✔
96
    #needsRedraw = false;
44✔
97
    #updateLoopRestarted = true;
44✔
98
    #lastTimestamp = 0;
44✔
99
    constructor(scheduler, engine) {
44✔
100
        super();
44✔
101
        this.renderingState = RENDERING_PAUSED;
44✔
102
        this.scheduler = scheduler;
44✔
103
        this.gfxEngine = engine; // TODO: remove me
44✔
104
    }
44✔
105

44✔
106
    scheduleViewUpdate(view, forceRedraw) {
44✔
107
        this.#needsRedraw |= forceRedraw;
195✔
108

195✔
109
        if (this.renderingState !== RENDERING_SCHEDULED) {
195✔
110
            this.renderingState = RENDERING_SCHEDULED;
39✔
111

39✔
112
            if (__DEBUG__) {
39✔
113
                document.title += ' ⌛';
39✔
114
            }
39✔
115

39✔
116
            // TODO Fix asynchronization between xr and MainLoop render loops.
39✔
117
            // WebGLRenderer#setAnimationLoop must be used for WebXR projects.
39✔
118
            // (see WebXR#initializeWebXR).
39✔
119
            if (!this.gfxEngine.renderer.xr.isPresenting) {
39✔
120
                requestAnimationFrame((timestamp) => { this.step(view, timestamp); });
39✔
121
            }
39✔
122
        }
39✔
123
    }
195✔
124

44✔
125
    #update(view, updateSources, dt) {
44✔
126
        const context = {
2✔
127
            camera: view.camera,
2✔
128
            engine: this.gfxEngine,
2✔
129
            scheduler: this.scheduler,
2✔
130
            view,
2✔
131
        };
2✔
132

2✔
133
        // replace layer with their parent where needed
2✔
134
        updateSources.forEach((src) => {
2✔
135
            const layer = src.layer || src;
2✔
136
            if (layer.isLayer && layer.parent) {
2!
UNCOV
137
                updateSources.add(layer.parent);
×
UNCOV
138
            }
×
139
        });
2✔
140

2✔
141
        for (const geometryLayer of view.getLayers((x, y) => !y)) {
2✔
142
            context.geometryLayer = geometryLayer;
2✔
143
            if (geometryLayer.ready && geometryLayer.visible && !geometryLayer.frozen) {
2✔
144
                view.execFrameRequesters(MAIN_LOOP_EVENTS.BEFORE_LAYER_UPDATE, dt, this.#updateLoopRestarted, geometryLayer);
2✔
145

2✔
146
                // Filter updateSources that are relevant for the geometryLayer
2✔
147
                const srcs = filterChangeSources(updateSources, geometryLayer);
2✔
148
                if (srcs.size > 0) {
2✔
149
                    // pre update attached layer
2✔
150
                    for (const attachedLayer of geometryLayer.attachedLayers) {
2!
UNCOV
151
                        if (attachedLayer.ready && attachedLayer.preUpdate) {
×
UNCOV
152
                            attachedLayer.preUpdate(context, srcs);
×
UNCOV
153
                        }
×
UNCOV
154
                    }
×
155
                    // `preUpdate` returns an array of elements to update
2✔
156
                    const elementsToUpdate = geometryLayer.preUpdate(context, srcs);
2✔
157
                    // `update` is called in `updateElements`.
2✔
158
                    updateElements(context, geometryLayer, elementsToUpdate);
2✔
159
                    // `postUpdate` is called when this geom layer update process is finished
2✔
160
                    geometryLayer.postUpdate(context, geometryLayer, updateSources);
2✔
161
                }
2✔
162

2✔
163
                // Clear the cache of expired resources
2✔
164
                geometryLayer.cache.flush();
2✔
165

2✔
166
                view.execFrameRequesters(MAIN_LOOP_EVENTS.AFTER_LAYER_UPDATE, dt, this.#updateLoopRestarted, geometryLayer);
2✔
167
            }
2✔
168
        }
2✔
169
    }
2✔
170

44✔
171
    step(view, timestamp) {
44✔
172
        const dt = timestamp - this.#lastTimestamp;
2✔
173
        view._executeFrameRequestersRemovals();
2✔
174

2✔
175
        view.execFrameRequesters(MAIN_LOOP_EVENTS.UPDATE_START, dt, this.#updateLoopRestarted);
2✔
176

2✔
177
        const willRedraw = this.#needsRedraw;
2✔
178
        this.#lastTimestamp = timestamp;
2✔
179

2✔
180
        // Reset internal state before calling _update (so future calls to View.notifyChange()
2✔
181
        // can properly change it)
2✔
182
        this.#needsRedraw = false;
2✔
183
        this.renderingState = RENDERING_PAUSED;
2✔
184
        const updateSources = new Set(view._changeSources);
2✔
185
        view._changeSources.clear();
2✔
186

2✔
187
        // update camera
2✔
188
        const dim = this.gfxEngine.getWindowSize();
2✔
189

2✔
190
        view.execFrameRequesters(MAIN_LOOP_EVENTS.BEFORE_CAMERA_UPDATE, dt, this.#updateLoopRestarted);
2✔
191
        view.camera.update(dim.x, dim.y);
2✔
192
        view.execFrameRequesters(MAIN_LOOP_EVENTS.AFTER_CAMERA_UPDATE, dt, this.#updateLoopRestarted);
2✔
193

2✔
194
        // Disable camera's matrix auto update to make sure the camera's
2✔
195
        // world matrix is never updated mid-update.
2✔
196
        // Otherwise inconsistencies can appear because object visibility
2✔
197
        // testing and object drawing could be performed using different
2✔
198
        // camera matrixWorld.
2✔
199
        // Note: this is required at least because WEBGLRenderer calls
2✔
200
        // camera.updateMatrixWorld()
2✔
201
        const oldAutoUpdate = view.camera3D.matrixAutoUpdate;
2✔
202
        view.camera3D.matrixAutoUpdate = false;
2✔
203

2✔
204
        // update data-structure
2✔
205
        this.#update(view, updateSources, dt);
2✔
206

2✔
207
        if (this.scheduler.commandsWaitingExecutionCount() == 0) {
2✔
208
            this.dispatchEvent({ type: 'command-queue-empty' });
2✔
209
        }
2✔
210

2✔
211
        // Redraw *only* if needed.
2✔
212
        // (redraws only happen when this.#needsRedraw is true, which in turn only happens when
2✔
213
        // view.notifyChange() is called with redraw=true)
2✔
214
        // As such there's no continuous update-loop, instead we use a ad-hoc update/render
2✔
215
        // mechanism.
2✔
216
        if (willRedraw) {
2✔
217
            this.#renderView(view, dt);
1✔
218
        }
1✔
219

2✔
220
        // next time, we'll consider that we've just started the loop if we are still PAUSED now
2✔
221
        this.#updateLoopRestarted = this.renderingState === RENDERING_PAUSED;
2✔
222

2✔
223
        if (__DEBUG__) {
2✔
224
            document.title = document.title.substr(0, document.title.length - 2);
2✔
225
        }
2✔
226

2✔
227
        view.camera3D.matrixAutoUpdate = oldAutoUpdate;
2✔
228

2✔
229
        view.execFrameRequesters(MAIN_LOOP_EVENTS.UPDATE_END, dt, this.#updateLoopRestarted);
2✔
230
    }
2✔
231

44✔
232
    #renderView(view, dt) {
44✔
233
        view.execFrameRequesters(MAIN_LOOP_EVENTS.BEFORE_RENDER, dt, this.#updateLoopRestarted);
1✔
234

1✔
235
        if (view.render) {
1!
UNCOV
236
            view.render();
×
237
        } else {
1✔
238
            // use default rendering method
1✔
239
            this.gfxEngine.renderView(view);
1✔
240
        }
1✔
241

1✔
242
        view.execFrameRequesters(MAIN_LOOP_EVENTS.AFTER_RENDER, dt, this.#updateLoopRestarted);
1✔
243
    }
1✔
244
}
44✔
245

1✔
246
export default MainLoop;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc