@pmndrs/xr
Version:
VR/AR for threejs
80 lines (79 loc) • 3.19 kB
JavaScript
import { DefaultAssetBasePath } from '../index.js';
import { syncAsync } from './utils.js';
const DefaultDefaultControllerProfileId = 'generic-trigger';
export class XRControllerLayoutLoader {
baseAssetPath;
defaultProfileId;
//cache
profilesListCache;
profileCacheMap = new Map();
constructor(options) {
this.baseAssetPath = options?.baseAssetPath ?? DefaultAssetBasePath;
this.defaultProfileId = options?.defaultControllerProfileId ?? DefaultDefaultControllerProfileId;
}
load(inputSourceProfileIds, handedness) {
return syncAsync(
//load profile
() => this.loadProfile(inputSourceProfileIds),
//get controller layout from profile
(profile) => {
for (const key in profile.layouts) {
if (!key.includes(handedness)) {
continue;
}
return profile.layouts[key];
}
throw new Error(`No matching layout for "${handedness}", in profile ${profile.profileId} with layouts ${Object.keys(profile.layouts).join(', ')}.`);
});
}
//alias for Loader compatibility
loadAsync = this.load;
loadProfile(inputSourceProfileIds) {
return syncAsync(
//load profiles list
() => this.profilesListCache ??
fetchJson(new URL('profilesList.json', this.baseAssetPath).href).then((profilesList) => (this.profilesListCache = profilesList)),
//load profile
(profilesList) => {
const length = inputSourceProfileIds.length;
let profileInfo;
for (let i = 0; i < length; i++) {
profileInfo = profilesList[inputSourceProfileIds[i]];
if (profileInfo != null) {
break;
}
}
profileInfo ??= profilesList[this.defaultProfileId];
if (profileInfo == null) {
throw new Error(`no matching profile found for profiles "${inputSourceProfileIds.join(', ')}" in profile list ${JSON.stringify(profilesList)}`);
}
return this.loadProfileFromPath(profileInfo.path);
});
}
loadProfileFromPath(relativeProfilePath) {
const result = this.profileCacheMap.get(relativeProfilePath);
if (result != null) {
return result;
}
const absoluteProfilePath = new URL(relativeProfilePath, this.baseAssetPath).href;
return fetchJson(absoluteProfilePath).then((profile) => {
//overwrite the relative assetPath into an absolute path
for (const key in profile.layouts) {
const layout = profile.layouts[key];
if (layout == null) {
continue;
}
layout.assetPath = new URL(layout.assetPath, absoluteProfilePath).href;
}
this.profileCacheMap.set(relativeProfilePath, profile);
return profile;
});
}
}
async function fetchJson(url) {
let response = await fetch(url);
if (!response.ok) {
return Promise.reject(new Error(response.statusText));
}
return response.json();
}