Home Reference Source

viewer/geometrycache.js

/**
 * Keeps track of GeometryData that is potentially reused. There are three fases:
 * - toload (this data has yet to start loading)
 * - loading (data has been requested} from the server but not yet returned)
 * - loaded (data has arrived and is processed)
 */
export class GeometryCache {
	constructor(renderLayer) {
		this.renderLayer = renderLayer;
		
		// GeometryData ID -> geometry
		this.loaded = new Map();

		// GeometryData ID -> Set of BimserverGeometryLoader
		this.toload = new Map();

		// GeometryData ID -> Set of BimserverGeometryLoader
		this.loading = new Map();
	}

	integrate2(geometryDataId, loader, gpuBufferManager, geometryInfoIds, geometryLoader) {
		var info = {
			loader: loader,
			gpuBufferManager: gpuBufferManager,
			geometryInfoIds: geometryInfoIds,
			geometryLoader: geometryLoader
		};
		if (this.loaded.has(geometryDataId)) {
			for (const geometryInfoId of geometryInfoIds) {
				this.renderLayer.addGeometryToObject(geometryDataId, geometryInfoId, info.loader, info.gpuBufferManager);
			}
			info.geometryLoader.geometryDataIdResolved(geometryDataId);
			return;
		}
		if (this.loading.has(geometryDataId)) {
			var set = this.loading.get(geometryDataId);
			set.add(info);
			return;
		}
		var set = this.toload.get(geometryDataId);
		if (set == null) {
			set = new Set();
			this.toload.set(geometryDataId, set);
		}
		set.add(info);
	}
	
	/*
	 * Calling this method will either:
	 * - Store the geometryDataId as toload if it's not already loaded and also not loading
	 * - If the geometryDataId is already loading, it will add the given `info` to the list to be triggered when it is loaded
	 * - If it's already loaded, the addGeometryToObject will be triggered right away
	 */
	integrate(geometryDataId, info) {
		if (this.loaded.has(geometryDataId)) {
			this.renderLayer.addGeometryToObject(geometryDataId, info.geometryInfoId, info.loader, info.gpuBufferManager);
			info.geometryLoader.geometryDataIdResolved(geometryDataId);
			return;
		}
		if (this.loading.has(geometryDataId)) {
			var set = this.loading.get(geometryDataId);
			set.add(info);
			return;
		}
		var set = this.toload.get(geometryDataId);
		if (set == null) {
			set = new Set();
			this.toload.set(geometryDataId, set);
		}
		set.add(info);
	}
	
	/*
	 * Whenever this method is called, all objects in the toload state are moved to the loading state. The IDs of the objecst are returned as an array
	 */
	pullToLoad() {
		var ids = Array.from(this.toload.keys());
		for (var id of ids) {
			this.loading.set(id, this.toload.get(id));
			this.toload.delete(id);
		}
		return ids;
	}

	has(geometryDataId) {
		return this.loaded.has(geometryDataId);
	}
	
	get(geometryDataId) {
		return this.loaded.get(geometryDataId);
	}

	/*
	 * Stores a piece of geometry
	 */
	set(geometryDataId, geometry) {
		if (this.loaded.has(geometryDataId)) {
			console.error("Already loaded", geometryDataId);
		}
		this.loaded.set(geometryDataId, geometry);
		var geometryInfos = this.loading.get(geometryDataId);
		if (geometryInfos != null) {
			for (var info of geometryInfos.values()) {
				for (const geometryInfoId of info.geometryInfoIds) {
					this.renderLayer.addGeometryToObject(geometryDataId, geometryInfoId, info.loader, info.gpuBufferManager);
				}
			}
			// TODO in a lot of cases, this is 4000x the same geomtryLoader, which after 1 invocation has already cleaned-up...
			for (var info of geometryInfos.values()) {
				info.geometryLoader.geometryDataIdResolved(geometryDataId);
			}
		}
		this.loading.delete(geometryDataId);
	}
	
	isEmpty() {
		return this.toload.size == 0;
	}
}