Skip to content

Commit

Permalink
fix issues with runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
yorkie committed Aug 15, 2024
1 parent 02172a0 commit 04b10eb
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 32 deletions.
2 changes: 1 addition & 1 deletion fixtures/spatial-externalmesh-glb.xsml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<xsml>
<head>
<title>External Mesh Example(Glb)</title>
<link id="my" rel="mesh" href="model/rokid-jungle.glb" />
<link id="my" rel="mesh" href="http://ar.rokidcdn.com/web-assets/pages/models/dog.glb" />
<style>
space {
rotation: 0 0 0;
Expand Down
3 changes: 2 additions & 1 deletion fixtures/spatial-lion.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ let isBlowing = false;
let targetX = 0;
let targetY = 0;

const frameDuration = 1000 / 45;
(function render() {
fan.isBlowing = isBlowing;
fan.update(targetX, targetY);
Expand All @@ -257,7 +258,7 @@ let targetY = 0;
} else {
lion.lookAt(targetX, targetY);
}
setTimeout(render, 16);
setTimeout(render, frameDuration);
})();

async function createAudioPlayer(name) {
Expand Down
1 change: 1 addition & 0 deletions fixtures/spatial-lion.xsml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<xsml version="1.0">
<head>
<title>Spatial Lion Example</title>
<meta name="viewport" content="bounding-size=0.3" />
<style type="text/scss">
@material yellow {
diffuse-color: #fdd276;
Expand Down
11 changes: 2 additions & 9 deletions pages/impl-babylonjs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference path="node_modules/@types/wicg-file-system-access/index.d.ts" />

import 'babylonjs';
import {
SpatialDocumentImpl,
DOMParser,
Expand All @@ -17,12 +18,10 @@ import {
XRFeature,
XRSessionBackendInit,
} from '../src';
import 'babylonjs';

import { canParseURL } from '../src/living/helpers/url';
import type ImageDataImpl from '../src/living/image/ImageData';
import { JSARInputEvent } from '../src/input-event';
import { SPATIAL_OBJECT_GUID_SYMBOL } from '../src/symbols';
import { WebXRDefaultExperience } from './xr/DefaultExperience';

interface EngineOnBabylonjs extends BABYLON.Engine, EventTarget { }
Expand Down Expand Up @@ -257,6 +256,7 @@ class NativeDocumentOnBabylonjs extends EventTarget implements NativeDocument {

const scene = this._scene = new BABYLON.Scene(this.engine);
this._scene.clearColor = new BABYLON.Color4(0.1, 0.1, 0.1, 1);
this._scene.useRightHandedSystem = false

const hdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(
'https://assets.babylonjs.com/environments/environmentSpecular.env', this._scene);
Expand All @@ -276,7 +276,6 @@ class NativeDocumentOnBabylonjs extends EventTarget implements NativeDocument {
camera.wheelDeltaPercentage = 0.01;

camera.setPosition(new BABYLON.Vector3(0, 1, -5));
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, false, true);

const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 2, -5), this._scene);
Expand Down Expand Up @@ -500,7 +499,6 @@ async function requestXRExperience(): Promise<XRSession> {
optionalFeatures: [],
}, xrHelper.renderTarget);
// Just moving the object space to the front of the camera
currentDom.document.space.position.z = 1.5;
console.log('entered WebXR session', xrHelper);
return xrHelper.baseExperience.sessionManager.session;
} else {
Expand Down Expand Up @@ -622,13 +620,8 @@ document.addEventListener('DOMContentLoaded', async () => {
});
nativeDocument.addClientCdpTransport(transport);

const spaceNode = currentDom.document.space.asNativeType<BABYLON.TransformNode>();
spaceNode.setEnabled(false);

await currentDom.waitForSpaceReady();
{
fitSpaceWithScene(spaceNode, 2.5);

// scene.debugLayer.show();
// Show panels
panels = await Promise.all([
Expand Down
20 changes: 18 additions & 2 deletions src/impl-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,25 @@ export interface NativeDocument extends EventTarget {
getContainerPose(): XRPose;

/**
* It returns a `XRPose` which represents the pose of the viewer in space.
* It returns the `XRSession` instance created by the implementator. In some of WebXR runtimes, there is a default XRSession will
* be created once the document is requested. This method will return the XRSession instance for those runtimes.
*/
getViewerPose?(): XRPose;
getXRSession?(): XRSession | null;

/**
* It returns a number which represents the recommeneded size of the bounding box of the document. It works as a hint for the native
* engine how to fit the spatial document, but not a strict constraint.
*
* It only works when the document has a viewport <meta> tag such as:
*
* ```
* <meta name="viewport" content="bouding-size=0.5, initial-scale=1">
* ```
*
* With the above tag, this document will be fit into a bounding box with a size of 0.5 * getRecommendedBoudingSize(), that means the
* bounding-size is not an absolute value in meters, but a percentage of the recommended size.
*/
getRecommendedBoudingSize?(): number;

/**
* It returns a map of preloaded meshes.
Expand Down
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'babylonjs';
import './living/helpers/babylonjs/patches';
import './living/helpers/babylonjs/loaders/gLTF/index';
import viewportParser from 'metaviewport-parser';
Expand Down Expand Up @@ -51,7 +50,7 @@ export class JSARDOM<T extends NativeDocument> {
}

get document(): SpatialDocumentImpl<T> {
return this[windowSymbol].document as SpatialDocumentImpl<T>;
return this[windowSymbol].document as unknown as SpatialDocumentImpl<T>;
}

get nativeDocument(): T {
Expand Down
22 changes: 14 additions & 8 deletions src/living/helpers/babylonjs/patches/rewrite-draco-compression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,18 @@ function decodeMesh(decoderModule, dataView, attributes, onIndicesData, onAttrib
}

// Check if the browser env
const decoderModulePending = Promise.all([
import('./draco_decoder_gltf.cjs'),
import('./draco_decoder_gltf.wasm.cjs'),
]).then(([DracoDecoderModule, wasmBinary]) => {
return DracoDecoderModule.default(wasmBinary.default);
});
let decoderModulePending: Promise<any> = null;
function loadDecoderModule(): Promise<any> {
if (decoderModulePending == null) {
decoderModulePending = Promise.all([
import('./draco_decoder_gltf.cjs'),
import('./draco_decoder_gltf.wasm.cjs'),
]).then(([DracoDecoderModule, wasmBinary]) => {
return DracoDecoderModule.default(wasmBinary.default);
});
}
return decoderModulePending;
}

(BABYLON.DracoCompression as any)._Default = {
dispose() {
Expand All @@ -120,7 +126,7 @@ const decoderModulePending = Promise.all([
* @returns a promise that resolves when ready
*/
whenReadyAsync() {
return decoderModulePending;
return loadDecoderModule();
},
/**
* Decode Draco compressed mesh data to vertex data.
Expand All @@ -131,7 +137,7 @@ const decoderModulePending = Promise.all([
*/
decodeMeshAsync(data: ArrayBuffer | ArrayBufferView, attributes, dividers) {
const dataView = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
return decoderModulePending.then((decoderModule) => {
return loadDecoderModule().then((decoderModule) => {
const vertexData = new BABYLON.VertexData();
decodeMesh(decoderModule, dataView, attributes, (indices) => {
vertexData.indices = indices;
Expand Down
10 changes: 7 additions & 3 deletions src/living/helpers/gui2d/control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,11 @@ export class Control2D {
private _parseLengthStr(input: string): LengthPercentageDimension | 'auto' {
if (input.endsWith('px')) {
return parseFloat(input);
} else if (input.endsWith('%')) {
return input;
} else {
return 'auto';
}
return input;
}

private _initializeLayoutStyle() {
Expand All @@ -250,8 +253,9 @@ export class Control2D {
height: this._overwriteHeight || 'auto',
width: this._overwriteWidth || 'auto',
};
if (this._style) {
const inputStyle = this._style;

const inputStyle = this._style;
if (inputStyle) {
if (inputStyle.height) {
layoutStyle.height = this._parseLengthStr(inputStyle.height);
}
Expand Down
3 changes: 2 additions & 1 deletion src/living/nodes/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export class ElementImpl extends NodeImpl implements Element {
ariaColIndex: string;
ariaColSpan: string;
ariaCurrent: string;
ariaDescription: string;
ariaDisabled: string;
ariaExpanded: string;
ariaHasPopup: string;
Expand Down Expand Up @@ -198,7 +199,7 @@ export class ElementImpl extends NodeImpl implements Element {
}

get ownerDocument(): Document {
return this._ownerDocument;
return this._ownerDocument as unknown as Document;
}

get prefix() {
Expand Down
1 change: 1 addition & 0 deletions src/living/nodes/HTMLContentElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export class HTMLContentElement extends HTMLElementImpl {
);
}
});
this._control.updateLayoutStyle();
}

_attrModified(name: string, value: string, oldValue: string): void {
Expand Down
3 changes: 2 additions & 1 deletion src/living/nodes/HTMLElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class HTMLElementImpl extends ElementImpl implements HTMLElement {
showPopover(): void {
throw new Error('The method "HTMLElement.prototype.showPopover()" not implemented.');
}
togglePopover(force?: boolean): void {
togglePopover(force?: boolean): boolean {
throw new Error('The method "HTMLElement.prototype.togglePopover()" not implemented.');
}

Expand All @@ -71,6 +71,7 @@ export class HTMLElementImpl extends ElementImpl implements HTMLElement {
onanimationstart: (this: GlobalEventHandlers, ev: AnimationEvent) => any;
onauxclick: (this: GlobalEventHandlers, ev: MouseEvent) => any;
onbeforeinput: (this: GlobalEventHandlers, ev: InputEvent) => any;
onbeforetoggle: (this: GlobalEventHandlers, ev: Event) => any;
onblur: (this: GlobalEventHandlers, ev: FocusEvent) => any;
oncancel: (this: GlobalEventHandlers, ev: Event) => any;
oncanplay: (this: GlobalEventHandlers, ev: Event) => any;
Expand Down
1 change: 1 addition & 0 deletions src/living/nodes/HTMLLinkElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ export default class HTMLLinkElementImpl extends HTMLElementImpl implements HTML
resolve(true);

} catch (err) {
console.log(err.stack);
reject(new DOMException(`Failed to load spatial model(${this.href}): ${err.message}`, 'INVALID_STATE_ERR'));
}
})
Expand Down
85 changes: 83 additions & 2 deletions src/living/nodes/HTMLMetaElement.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,53 @@
import { NativeDocument } from '../../impl-interfaces';
import { HTMLElementImpl } from './HTMLElement';

class ViewportMeta {
width: number;
height: number;
initialScale: number;
maximumScale: number;
minimumScale: number;
userScalable: boolean;
// Added by XSML
depth: number;
boundingSize: number;

static Parse(input: string): ViewportMeta {
const viewport = new ViewportMeta();
const parts = input.split(',');
for (const part of parts) {
const [key, value] = part.split('=');
switch (key.trim()) {
case 'width':
viewport.width = parseFloat(value);
break;
case 'height':
viewport.height = parseFloat(value);
break;
case 'initial-scale':
viewport.initialScale = parseFloat(value);
break;
case 'maximum-scale':
viewport.maximumScale = parseFloat(value);
break;
case 'minimum-scale':
viewport.minimumScale = parseFloat(value);
break;
case 'user-scalable':
viewport.userScalable = value === 'yes';
break;
case 'bounding-size':
viewport.boundingSize = parseFloat(value);
break;
case 'depth':
viewport.depth = parseFloat(value);
break;
}
}
return viewport;
}
}

export default class HTMLMetaElementImpl extends HTMLElementImpl implements HTMLMetaElement {
content: string;
httpEquiv: string;
Expand All @@ -11,10 +58,44 @@ export default class HTMLMetaElementImpl extends HTMLElementImpl implements HTML
constructor(
nativeDocument: NativeDocument,
args,
privateData: {} = null
_privateData: {} = null
) {
super(nativeDocument, args, {
localName: 'meta',
});
}
}

_attach(): void {
super._attach();
this.content = this.getAttribute('content');
this.httpEquiv = this.getAttribute('http-equiv');
this.media = this.getAttribute('media');
this.name = this.getAttribute('name');
this.scheme = this.getAttribute('scheme');

if (this.name === 'viewport') {
this.#updateForViewport(this.content);
}
}

#updateForViewport(input: string) {
if (!input) {
return;
}
const viewportConfig = ViewportMeta.Parse(input);
this._ownerDocument.addEventListener('load', () => {
const spaceTransform = this._ownerDocument.space.asNativeType<BABYLON.TransformNode>();
const recommendedContentSize = this._hostObject.getRecommendedBoudingSize?.() || 1.0;
const targetSize = viewportConfig.boundingSize * recommendedContentSize;
this.#fitTo(spaceTransform, targetSize);
});
}

#fitTo(node: BABYLON.TransformNode, targetSize: number) {
const boundingVectors = node.getHierarchyBoundingVectors(true);
const totalSize = boundingVectors.max.subtract(boundingVectors.min);
const scalingFactor = Math.min(targetSize / totalSize.x, targetSize / totalSize.y, targetSize / totalSize.z);
node.scaling.multiplyInPlace(new BABYLON.Vector3(scalingFactor, scalingFactor, scalingFactor));
console.log('Fitting to', targetSize, totalSize, scalingFactor);
}
}
17 changes: 15 additions & 2 deletions src/living/nodes/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ export class NodeImpl extends EventTarget implements Node {
_registeredObserverList: Array<ObserverItem> = [];
_memoizedQueries = {};
_ceState: string;
_rootNode: Node;
_rootNodeWithComposed: Node;

/**
* The followings are used by inspector protocol
Expand Down Expand Up @@ -292,7 +294,7 @@ export class NodeImpl extends EventTarget implements Node {
}

get ownerDocument(): Document {
return this.nodeType === NodeTypes.DOCUMENT_NODE ? null : this._ownerDocument;
return this.nodeType === NodeTypes.DOCUMENT_NODE ? null : this._ownerDocument as unknown as Document;
}

get nextSibling(): ChildNode | null {
Expand Down Expand Up @@ -494,7 +496,18 @@ export class NodeImpl extends EventTarget implements Node {
return isInclusiveAncestor(this, other);
}
getRootNode(options?: GetRootNodeOptions): Node {
return options?.composed ? shadowIncludingRoot(this) : nodeRoot(this);
const isComposed = options?.composed === true;
if (isComposed) {
if (!this._rootNodeWithComposed) {
this._rootNodeWithComposed = shadowIncludingRoot(this);
}
return this._rootNodeWithComposed;
} else {
if (!this._rootNode) {
this._rootNode = nodeRoot(this);
}
return this._rootNode;
}
}
hasChildNodes(): boolean {
return domSymbolTree.hasChildren(this);
Expand Down
Loading

0 comments on commit 04b10eb

Please sign in to comment.