diff --git a/platform/lumin-runtime/component-mapping.js b/platform/lumin-runtime/component-mapping.js index 0421544e..b42a01fe 100644 --- a/platform/lumin-runtime/component-mapping.js +++ b/platform/lumin-runtime/component-mapping.js @@ -1,6 +1,7 @@ // Copyright (c) 2019 Magic Leap, Inc. All Rights Reserved import { ControllerBuilder } from './controllers/builders/controller-builder.js'; +import { WebGlController } from './controllers/webgl-controller.js'; // import { UiNodeBuilder } from './elements/builders/ui-node-builder.js'; import { ButtonBuilder } from './elements/builders/button-builder.js'; @@ -40,6 +41,7 @@ import { LineBuilder } from './elements/builders/line-builder.js'; import { ModelBuilder } from './elements/builders/model-builder.js'; import { QuadBuilder } from './elements/builders/quad-builder.js'; import { VideoBuilder } from './elements/builders/video-builder.js'; +// import { WebGLBuilder } from './elements/builders/webgl-builder.js'; export default { @@ -47,6 +49,7 @@ export default { platform: 'lumin-runtime', controllers: { 'scene': () => new ControllerBuilder(), + 'webgl': () => new WebGlController(), }, elements: { 'view': () => new GroupBuilder(), @@ -90,5 +93,6 @@ export default { 'audio': () => new AudioBuilder(), 'light': () => new LightBuilder(), 'line' : () => new LineBuilder() + // 'webgl': () => new WebGLBuilder() } }; diff --git a/platform/lumin-runtime/controllers/webgl-controller.js b/platform/lumin-runtime/controllers/webgl-controller.js new file mode 100644 index 00000000..4be609c1 --- /dev/null +++ b/platform/lumin-runtime/controllers/webgl-controller.js @@ -0,0 +1,199 @@ +// Copyright (c) 2019 Magic Leap, Inc. All Rights Reserved + +import { PrismController } from 'lumin'; +import egl from 'egl'; +import gl from 'gl'; +import png from 'png'; +import jpeg from 'jpeg'; + +import { QuadBuilder } from '../elements/builders/quad-builder.js'; + +import { ControlPose3DofInputEventData } from '../types/event-data/control-pose-3dof-Input-event-data.js'; +import { ControlPose6DofInputEventData } from '../types/event-data/control-pose-6dof-input-event-data.js'; +import { ControlTouchPadInputEventData } from '../types/event-data/control-touch-pad-input-event-data.js'; +import { EyeTrackingEventData } from '../types/event-data/eye-tracking-event-data.js'; +import { GestureInputEventData } from '../types/event-data/gesture-input-event-data.js'; +import { InputEventData } from '../types/event-data/input-event-data.js'; +import { KeyInputEventData } from '../types/event-data/key-input-event-data.js'; +import { PrismEventData } from '../types/event-data/prism-event-data.js'; +import { SelectionEventData } from '../types/event-data/selection-event-data.js'; +import { UiEventData } from '../types/event-data/ui-event-data.js'; +import { VideoEventData } from '../types/event-data/video-event-data.js'; + +export class WebGlController extends PrismController { + constructor(properties) { + super(properties.name); + + this._children = []; + this._controllers = []; + + this._initialProperties = {...properties}; + + this._eventHandlers = { + onPreAttachPrism: [], + onAttachPrism: [], + onDetachPrism: [], + onEvent:[], + onUpdate: [] + }; + + this._eventTypes = [ + ControlPose3DofInputEventData, + ControlPose6DofInputEventData, + ControlTouchPadInputEventData, + EyeTrackingEventData, + GestureInputEventData, + InputEventData, + KeyInputEventData, + SelectionEventData, + UiEventData, + VideoEventData + ]; + } + + addChild(child) { + const root = this.getRoot(); + if (root === undefined || root === null) { + this._children.push(child); + } else { + root.addChild(child); + } + } + + addChildController(controller) { + // It will receive all onEvent callbacks received by this controller (the parent). + // If a prism is already attached, + // the child controller will also receive an onAttachPrism callback. + const root = this.getRoot(); + if (root === undefined || root === null) { + this._controllers.push(controller); + } else { + super.addChildController(controller); + this.addChild(controller.getRoot()); + } + } + + addListener(eventName, eventHandler) { + const handlers = this._eventHandlers[eventName]; + if (handlers !== undefined) { + handlers.push(eventHandler); + } else { + throw TypeError(`Event ${eventName} is not supported by the controller`); + } + } + + removeListener(eventName, eventHandler) { + const handlers = this._eventHandlers[eventName]; + if (handlers !== undefined) { + const index = handlers.indexOf(eventHandler); + handlers.splice(index, 1); + } else { + throw TypeError(`Event ${eventName} is not supported by the controller`); + } + } + + clearListeners() { + this._eventHandlers = { + onPreAttachPrism: [], + onAttachPrism: [], + onDetachPrism: [], + onEvent:[], + onUpdate: [] + }; + } + + _getQuadNode(prism) { + const root = this.getRoot(); + const [width, height] = prism.getSize(); + + // create planar resource + const id = prism.createPlanarEGLResourceId(1024, 1024); + const quad = this.quad = prism.createQuadNode(id); + quad.setLocalScale([width, height, 1]); + quad.setLocalPosition([-width / 2, -height / 2, 0]); + quad.setBackFaceCulls(false); + quad.setIsOpaque(false); + + root.addChild(quad); + + const resource = this.resource = prism.getResource(id); + const surface = this.surface = resource.getEGLSurface(); + const context = this.context = resource.getEGLContext(); + egl.makeCurrent(surface, surface, context); + + this.start = Date.now(); + + globalThis.requestAnimationFrame = callback => { + this.cb = callback; + }; + + if (cb) { + this.cb = cb; + } + // if (window.onload) { window.onload(); } + + return quad; + } + + onPreAttachPrism(prism) { + this._eventHandlers.onPreAttachPrism.forEach(handler => handler(new PrismEventData(prism))); + } + + onAttachPrism(prism) { + const quad = this._getQuadNode(prism); + + if (this._initialProperties !== undefined) { + const builder = new QuadBuilder(); + builder.update(quad, undefined, this._initialProperties); + + this._initialProperties = undefined; + } + + if (this._controllers !== undefined) { + this._controllers.forEach(controller => { + super.addChildController(controller); + quad.addChild(controller.getRoot()); + }); + this._controllers = undefined; + } + + if (this._children !== undefined) { + this._children.forEach(child => quad.addChild(child)); + this._children = undefined; + } + + this._eventHandlers.onAttachPrism.forEach(handler => handler(new PrismEventData(prism))); + } + + onDetachPrism(prism) { + this._eventHandlers.onDetachPrism.forEach(handler => handler(new PrismEventData(prism))); + } + + _getEventDataByEventType(eventData) { + + const eventConstructor = this._eventTypes + .find( eventType => eventType.isSupported(eventData) ); + + return eventConstructor === undefined + ? eventData + : new eventConstructor(eventData); + } + + onEvent(event) { + const eventData = this._getEventDataByEventType(event); + this._eventHandlers.onEvent.forEach(handler => handler(eventData)); + return false; + } + + onUpdate(delta) { + this._eventHandlers.onUpdate.forEach(handler => handler(delta)); + + if (this.cb) { + gl.clear(gl.COLOR_BUFFER_BIT); + const fn = this.cb; + this.cb = null; + fn(Date.now() - this.start); + egl.swapBuffers(this.surface); + } + } +} \ No newline at end of file diff --git a/platform/lumin-runtime/elements/builders/loading-spinner-builder.js b/platform/lumin-runtime/elements/builders/loading-spinner-builder.js index 0acab241..178013c0 100644 --- a/platform/lumin-runtime/elements/builders/loading-spinner-builder.js +++ b/platform/lumin-runtime/elements/builders/loading-spinner-builder.js @@ -7,6 +7,7 @@ import { UiNodeBuilder } from './ui-node-builder.js'; import { ArrayProperty } from '../properties/array-property.js'; import { PrimitiveTypeProperty } from '../properties/primitive-type-property.js'; import { PropertyDescriptor } from '../properties/property-descriptor.js'; +import { LoadingSpinnerType } from '../../types/loading-spinner-type.js'; import { validator } from '../../utilities/validator.js'; @@ -14,7 +15,7 @@ export class LoadingSpinnerBuilder extends UiNodeBuilder { constructor(){ super(); - this._propertyDescriptors['size'] = new ArrayProperty('color', 'setSize', true, 'vec2'); + this._propertyDescriptors['size'] = new ArrayProperty('size', 'setSize', true, 'vec2'); this._propertyDescriptors['value'] = new PrimitiveTypeProperty('value', 'setValue', true, 'number'); } @@ -23,9 +24,10 @@ export class LoadingSpinnerBuilder extends UiNodeBuilder { this.validate(undefined, undefined, properties); - const type = properties.type; - const id = this.getPropertyValue('resourceId', 0, properties); + const type = LoadingSpinnerType[this.getPropertyValue('type', 'sprite-animation', properties)]; + const id = this.getPropertyValue('resourceId', 0, properties); const path = this.getPropertyValue('resourcePath', '', properties); + const height = this.getPropertyValue('height', 0, properties); const element = ui.UiLoadingSpinner.Create(prism, type, id, path, height); diff --git a/platform/lumin-runtime/elements/builders/webgl-builder.js b/platform/lumin-runtime/elements/builders/webgl-builder.js new file mode 100644 index 00000000..d3b5766b --- /dev/null +++ b/platform/lumin-runtime/elements/builders/webgl-builder.js @@ -0,0 +1,245 @@ +import { ElementBuilder } from './element-builder.js'; +import { ArrayProperty } from '../properties/array-property.js'; +import { EnumProperty } from '../properties/enum-property.js'; +import { PrimitiveTypeProperty } from '../properties/primitive-type-property.js'; + +import { CursorHoverState } from '../../types/cursor-hover-state.js'; + +import { validator } from '../../utilities/validator.js'; + +export class WebGLBuilder extends ElementBuilder { + constructor(){ + super(); + + // TransformNode properties + this._propertyDescriptors['name'] = new PrimitiveTypeProperty('name', 'setName', false, 'string'); + this._propertyDescriptors['parentedBoneName'] = new PrimitiveTypeProperty('parentedBoneName', 'setParentedBoneName', false, 'string'); + this._propertyDescriptors['skipRaycast'] = new PrimitiveTypeProperty('skipRaycast', 'setSkipRaycast', false, 'boolean'); + this._propertyDescriptors['triggerable'] = new PrimitiveTypeProperty('triggerable', 'setTriggerable', false, 'boolean'); + this._propertyDescriptors['visible'] = new PrimitiveTypeProperty('visible', 'setVisible', false, 'boolean'); + this._propertyDescriptors['visibilityInherited'] = new PrimitiveTypeProperty('visibilityInherited', 'setVisibilityInherited', false, 'boolean'); + this._propertyDescriptors['anchorPosition'] = new ArrayProperty('anchorPosition', 'setAnchorPosition', false, 'vec3'); + this._propertyDescriptors['localPosition'] = new ArrayProperty('localPosition', 'setLocalPosition', false, 'vec3'); + this._propertyDescriptors['localRotation'] = new ArrayProperty('localRotation', 'setLocalRotation', false, 'quat'); + this._propertyDescriptors['localScale'] = new ArrayProperty('localScale', 'setLocalScale', false, 'vec3'); + this._propertyDescriptors['localTransform'] = new ArrayProperty('localTransform', 'setLocalTransform', false, 'mat4'); + this._propertyDescriptors['cursorHoverState'] = new EnumProperty('cursorHoverState', 'setCursorHoverState', false, CursorHoverState, 'CursorHoverState'); + + this._propertyDescriptors['offset'] = new ArrayProperty('offset', 'setOffset', false, 'vec3'); + + // RenderNode properties + this._propertyDescriptors['bloomStrength'] = new PrimitiveTypeProperty('bloomStrength', 'setBloomStrength', false, 'number'); + this._propertyDescriptors['color'] = new ArrayProperty('color', 'setColor', false, 'vec4'); + this._propertyDescriptors['drmContent'] = new PrimitiveTypeProperty('drmContent', 'setDrmContent', false, 'boolean'); + this._propertyDescriptors['shader'] = new EnumProperty('shader', 'setShader', false, ShaderType, 'ShaderType'); + this._propertyDescriptors['renderingLayer'] = new EnumProperty('renderingLayer', 'setRenderingLayer', false, RenderingLayer, 'RenderingLayer'); + + this._addRenderStateProperty('backFaceCulls', 'setBackFaceCulls'); + this._addRenderStateProperty('blooms', 'setBlooms'); + this._addRenderStateProperty('castsShadows', 'setCastsShadows'); + this._addRenderStateProperty('frontFaceCulls', 'setFrontFaceCulls'); + this._addRenderStateProperty('isOpaque', 'setIsOpaque'); + this._addRenderStateProperty('isUI', 'setIsUI'); + this._addRenderStateProperty('pushesStencil', 'setPushesStencil'); + this._addRenderStateProperty('readsClip', 'setReadsClip'); + this._addRenderStateProperty('readsDepth', 'setReadsDepth'); + this._addRenderStateProperty('receivesLight', 'setReceivesLight'); + this._addRenderStateProperty('receivesShadows', 'setReceivesShadows'); + this._addRenderStateProperty('writesColor', 'setWritesColor'); + this._addRenderStateProperty('writesDepth', 'setWritesDepth'); + this._addRenderStateProperty('writesStencil', 'setWritesStencil'); + + // QuadNode properties + this._propertyDescriptors['renderResourceId'] = new PrimitiveTypeProperty('renderResourceId', 'setRenderResource', false, 'number'); + this._propertyDescriptors['texCoords'] = new ArrayProperty('texCoords', 'setTexCoords', false, 'vec2'); + this._propertyDescriptors['viewMode'] = new EnumProperty('viewMode', 'setViewMode', false, ViewMode, 'ViewMode'); + this._propertyDescriptors['size'] = new ArrayProperty('size', 'setSize', false, 'vec2'); + + // WebGLNode properties + this._propertyDescriptors['width'] = new PrimitiveTypeProperty('width', 'setWidth', false, 'number'); + this._propertyDescriptors['height'] = new PrimitiveTypeProperty('height', 'setHeight', false, 'number'); + } + + _addRenderStateProperty(propertyName, setFunction) { + this._propertyDescriptors[propertyName] = this._getClassProperty(propertyName, setFunction); + this[setFunction] = this._getSetFunction(propertyName, setFunction); + } + + _getClassProperty(propertyName, setFunction) { + const properties = [ + new PrimitiveTypeProperty('on', undefined, undefined, 'boolean'), + new PrimitiveTypeProperty('renderStateIndex', undefined, undefined, 'number') + ]; + + return new ClassProperty(propertyName, setFunction, false, properties); + } + + _getSetFunction(propertyName, setFunction) { + const builder = this; + + return function(element, oldProperties, newProperties) { + const on = newProperties[propertyName].on; + + if (on !== undefined) { + const renderStateIndex = builder.getPropertyValue('renderStateIndex', -1, newProperties[propertyName]); + builder[setFunction](element, on, renderStateIndex); + } + } + } + + create(prism, properties) { + print("WebGLBuilder.create has not been implemented yet"); + return {}; + } + + validate(element, oldProperties, newProperties) { + print("WebGLBuilder.validate has not been implemented yet"); + } + + update(element, oldProperties, newProperties) { + print("WebGLBuilder.update has not been implemented yet"); + } + + setName(element, oldProperties, newProperties) { + print("WebGLBuilder.setName has not been implemented yet"); + } + + setParentedBoneName(element, oldProperties, newProperties) { + print("WebGLBuilder.setParentedBoneName has not been implemented yet"); + } + + setSkipRaycast(element, oldProperties, newProperties) { + print("WebGLBuilder.setSkipRaycast has not been implemented yet"); + } + + setTriggerable(element, oldProperties, newProperties) { + print("WebGLBuilder.setTriggerable has not been implemented yet"); + } + + setVisible(element, oldProperties, newProperties) { + print("WebGLBuilder.setVisible has not been implemented yet"); + } + + setVisibilityInherited(element, oldProperties, newProperties) { + print("WebGLBuilder.setVisibilityInherited has not been implemented yet"); + } + + setAnchorPosition(element, oldProperties, newProperties) { + print("WebGLBuilder.setAnchorPosition has not been implemented yet"); + } + + setLocalPosition(element, oldProperties, newProperties) { + print("WebGLBuilder.setLocalPosition has not been implemented yet"); + } + + setLocalRotation(element, oldProperties, newProperties) { + print("WebGLBuilder.setLocalRotation has not been implemented yet"); + } + + setLocalScale(element, oldProperties, newProperties) { + print("WebGLBuilder.setLocalScale has not been implemented yet"); + } + + setLocalTransform(element, oldProperties, newProperties) { + print("WebGLBuilder.setLocalTransform has not been implemented yet"); + } + + setCursorHoverState(element, oldProperties, newProperties) { + print("WebGLBuilder.setCursorHoverState has not been implemented yet"); + } + + setOffset(element, oldProperties, newProperties) { + const offset = newProperties.offset; + if (offset !== undefined) { + Object.defineProperty(element, 'offset', { + enumerable: true, + writable: true, + configurable: false, + value: offset + }); + } + } + + setBloomStrength(element, oldProperties, newProperties) { + print("WebGLBuilder.setBloomStrength has not been implemented yet"); + } + + setColor(element, oldProperties, newProperties) { + print("WebGLBuilder.setColor has not been implemented yet"); + } + + setDrmContent(element, oldProperties, newProperties) { + print("WebGLBuilder.setDrmContent has not been implemented yet"); + } + + setShader(element, oldProperties, newProperties) { + print("WebGLBuilder.setShader has not been implemented yet"); + } + + setRenderingLayer(element, oldProperties, newProperties) { + print("WebGLBuilder.setRenderingLayer has not been implemented yet"); + } + + setBackFaceCulls(element, on, renderStateIndex) { + print("WebGLBuilder.setBackFaceCulls has not been implemented yet"); + } + + setBlooms(element, on, renderStateIndex) { + print("WebGLBuilder.setBlooms has not been implemented yet"); + } + + setCastsShadows(element, on, renderStateIndex) { + print("WebGLBuilder.setCastsShadows has not been implemented yet"); + } + + setFrontFaceCulls(element, on, renderStateIndex) { + print("WebGLBuilder.setFrontFaceCulls has not been implemented yet"); + } + + setIsOpaque(element, on, renderStateIndex) { + print("WebGLBuilder.setIsOpaque has not been implemented yet"); + } + + setIsUI(element, on, renderStateIndex) { + print("WebGLBuilder.setIsUI has not been implemented yet"); + } + + setPushesStencil(element, on, renderStateIndex) { + print("WebGLBuilder.setPushesStencil has not been implemented yet"); + } + + setReadsClip(element, on, renderStateIndex) { + print("WebGLBuilder.setReadsClip has not been implemented yet"); + } + + setReadsDepth(element, on, renderStateIndex) { + print("WebGLBuilder.setReadsDepth has not been implemented yet"); + } + + setReceivesLight(element, on, renderStateIndex) { + print("WebGLBuilder.setReceivesLight has not been implemented yet"); + } + + setReceivesShadows(element, on, renderStateIndex) { + print("WebGLBuilder.setReceivesShadows has not been implemented yet"); + } + + setWritesColor(element, on, renderStateIndex) { + print("WebGLBuilder.setWritesColor has not been implemented yet"); + } + + setWritesDepth(element, on, renderStateIndex) { + print("WebGLBuilder.setWritesDepth has not been implemented yet"); + } + + setWritesStencil(element, on, renderStateIndex) { + print("WebGLBuilder.setWritesStencil has not been implemented yet"); + } + + setWidth(element, oldProperties, newProperties) { + print("WebGLBuilder.setWidth has not been implemented yet"); + } + setHeight(element, oldProperties, newProperties) { + print("WebGLBuilder.setHeight has not been implemented yet"); + } +} diff --git a/platform/lumin-runtime/utilities/validator.js b/platform/lumin-runtime/utilities/validator.js index 773401ac..8028e2f1 100644 --- a/platform/lumin-runtime/utilities/validator.js +++ b/platform/lumin-runtime/utilities/validator.js @@ -60,7 +60,7 @@ export const validator = { validateHandGestureKeypointName: value => validate(value, HandGestureKeypointName), validateLabelDisplayMode: value => validate(value, LabelDisplayMode), validateLightType: value => validate(value, LightType), - validateLoadinSpinnerType: value => validate(value, LoadingSpinnerType), + validateLoadingSpinnerType: value => validate(value, LoadingSpinnerType), validatePanelEdgeConstraintLevel: value => validate(value, PanelEdgeConstraintLevel), validatePanelCursorTransitionType: value => validate(value, PanelCursorTransitionType), validatePortalIconSize: value => validate(value, PortalIconSize),