@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
172 lines (155 loc) • 5.7 kB
text/typescript
import {Camera, WebGLRenderer} from 'three';
import {PolyScene} from '../../../engine/scene/PolyScene';
import {CameraAttribute} from '../CoreCamera';
import {
WebXRControllerMountFunction,
WebXRControllerUnmountFunction,
WEBXR_REFERENCE_SPACE_TYPES,
} from '../../webXR/Common';
import {WebXRVRFeature, WEBXR_VR_FEATURES} from '../../webXR/webXRVR/CommonVR';
import {BaseCoreWebXRController} from '../../webXR/_BaseCoreWebXRController';
import {WebXRARFeature, WEBXR_AR_FEATURES} from '../../webXR/webXRAR/CommonAR';
import {coreObjectClassFactory} from '../../geometry/CoreObjectFactory';
interface WebXRControllerOptions {
camera: Camera;
scene: PolyScene;
renderer: WebGLRenderer;
canvas: HTMLCanvasElement;
}
export interface CoreCameraWebXRControllerConfig {
mountFunction: WebXRControllerMountFunction;
unmountFunction: WebXRControllerUnmountFunction;
}
function getFeatures<F extends WebXRARFeature | WebXRVRFeature>(
camera: Camera,
attribName: string,
existingFeatures: F[]
): F[] {
const features: F[] = [];
const featuresStr = coreObjectClassFactory(camera).attribValue(camera, attribName) as string | null;
const featuresStrings = featuresStr?.split(' ');
if (featuresStrings) {
for (const featuresString of featuresStrings) {
if (existingFeatures.includes(featuresString as F)) {
features.push(featuresString as F);
}
}
}
return features;
}
interface FeatureAttribNames {
optional: CameraAttribute;
required: CameraAttribute;
}
function _getRequiredAndOptionalFeatures<E extends WebXRARFeature | WebXRVRFeature>(
camera: Camera,
existingFeatures: E[],
attribNames: FeatureAttribNames
) {
// features
const optionalFeatures = getFeatures<E>(camera, attribNames.optional, existingFeatures);
const requiredFeatures = getFeatures<E>(camera, attribNames.required, existingFeatures);
return {requiredFeatures, optionalFeatures};
}
interface SpaceTypeAttribNames {
type: CameraAttribute;
override: CameraAttribute;
}
function _getReferenceSpaceType(camera: Camera, options: SpaceTypeAttribNames) {
// referenceSpaceType
const coreObjectClass = coreObjectClassFactory(camera);
let overrideReferenceSpaceType = coreObjectClass.attribValue(camera, options.override) as boolean | null;
let referenceSpaceType: string | null | undefined = coreObjectClass.attribValue(camera, options.type) as
| string
| null;
if (!(referenceSpaceType && WEBXR_REFERENCE_SPACE_TYPES.includes(referenceSpaceType as XRReferenceSpaceType))) {
overrideReferenceSpaceType = false;
referenceSpaceType = undefined;
}
return {overrideReferenceSpaceType, referenceSpaceType};
}
export class CoreCameraWebXRController {
static process(options: WebXRControllerOptions): CoreCameraWebXRControllerConfig {
const {camera, scene, renderer, canvas} = options;
const controllers: BaseCoreWebXRController[] = [];
const subMountFunctions: WebXRControllerMountFunction[] = [];
const subUnmountFunctions: WebXRControllerUnmountFunction[] = [];
const mountFunction: WebXRControllerMountFunction = () => {
for (const subFunc of subMountFunctions) {
subFunc();
}
};
const unmountFunction: WebXRControllerUnmountFunction = () => {
for (const subFunc of subUnmountFunctions) {
subFunc();
}
};
const coreObjectClass = coreObjectClassFactory(camera);
//
// AR
//
const isWebAR = coreObjectClass.attribValue(camera, CameraAttribute.WEBXR_AR) as boolean | null;
if (isWebAR == true) {
const createFunction = scene.webXR.ARControllerCreateFunction();
if (createFunction) {
const {overrideReferenceSpaceType, referenceSpaceType} = _getReferenceSpaceType(camera, {
type: CameraAttribute.WEBXR_AR_OVERRIDE_REFERENCE_SPACE_TYPE,
override: CameraAttribute.WEBXR_AR_REFERENCE_SPACE_TYPE,
});
const {requiredFeatures, optionalFeatures} = _getRequiredAndOptionalFeatures<WebXRARFeature>(
camera,
WEBXR_AR_FEATURES,
{
optional: CameraAttribute.WEBXR_AR_FEATURES_OPTIONAL,
required: CameraAttribute.WEBXR_AR_FEATURES_REQUIRED,
}
);
// createFunction
const controller = createFunction(renderer, camera, canvas, {
overrideReferenceSpaceType: overrideReferenceSpaceType || false,
referenceSpaceType: referenceSpaceType as XRReferenceSpaceType | undefined,
requiredFeatures,
optionalFeatures,
});
controllers.push(controller);
}
}
//
// VR
//
const isWebVR = coreObjectClass.attribValue(camera, CameraAttribute.WEBXR_VR) as boolean | null;
if (isWebVR == true) {
const createFunction = scene.webXR.VRControllerCreateFunction();
if (createFunction) {
const {overrideReferenceSpaceType, referenceSpaceType} = _getReferenceSpaceType(camera, {
type: CameraAttribute.WEBXR_VR_OVERRIDE_REFERENCE_SPACE_TYPE,
override: CameraAttribute.WEBXR_VR_REFERENCE_SPACE_TYPE,
});
const {requiredFeatures, optionalFeatures} = _getRequiredAndOptionalFeatures<WebXRVRFeature>(
camera,
WEBXR_VR_FEATURES,
{
optional: CameraAttribute.WEBXR_VR_FEATURES_OPTIONAL,
required: CameraAttribute.WEBXR_VR_FEATURES_REQUIRED,
}
);
// createFunction
const controller = createFunction(renderer, camera, canvas, {
overrideReferenceSpaceType: overrideReferenceSpaceType || false,
referenceSpaceType: referenceSpaceType as XRReferenceSpaceType | undefined,
requiredFeatures,
optionalFeatures,
});
controllers.push(controller);
}
}
//
//
//
for (const controller of controllers) {
subMountFunctions.push(() => controller.mount());
subUnmountFunctions.push(() => controller.unmount());
}
return {mountFunction, unmountFunction};
}
}