@webspatial/core-sdk 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/iife/index.d.ts +164 -12
- package/dist/iife/index.global.js +11 -11
- package/dist/iife/index.global.js.map +1 -1
- package/dist/index.d.ts +164 -12
- package/dist/index.js +403 -101
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/JSBCommand.ts +7 -2
- package/src/Spatial.ts +1 -1
- package/src/SpatialSession.ts +4 -2
- package/src/SpatializedElement.ts +10 -10
- package/src/SpatializedElementCreator.ts +5 -2
- package/src/SpatializedStatic3DElement.test.ts +15 -1
- package/src/SpatializedStatic3DElement.ts +167 -8
- package/src/WebMsgCommand.ts +22 -0
- package/src/scene-polyfill.manifest.test.ts +402 -0
- package/src/scene-polyfill.test.ts +108 -18
- package/src/scene-polyfill.ts +275 -29
- package/src/types/global.d.ts +3 -0
- package/src/types/types.ts +71 -0
- package/src/utils.ts +21 -0
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
(function(){
|
|
3
3
|
if(typeof window === 'undefined') return;
|
|
4
4
|
if(!window.__webspatialsdk__) window.__webspatialsdk__ = {}
|
|
5
|
-
window.__webspatialsdk__['core-sdk-version'] = "1.
|
|
5
|
+
window.__webspatialsdk__['core-sdk-version'] = "1.6.0"
|
|
6
6
|
})()
|
|
7
7
|
|
|
8
8
|
var __defProp = Object.defineProperty;
|
|
@@ -824,6 +824,16 @@ function composeSRT(position, rotation, scale) {
|
|
|
824
824
|
m = m.scale(sx, sy, sz);
|
|
825
825
|
return m;
|
|
826
826
|
}
|
|
827
|
+
function deepCloneJSON(value) {
|
|
828
|
+
const sc = globalThis.structuredClone;
|
|
829
|
+
if (typeof sc === "function") {
|
|
830
|
+
try {
|
|
831
|
+
return sc(value);
|
|
832
|
+
} catch {
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
return JSON.parse(JSON.stringify(value));
|
|
836
|
+
}
|
|
827
837
|
var init_utils = __esm({
|
|
828
838
|
"src/utils.ts"() {
|
|
829
839
|
"use strict";
|
|
@@ -1015,14 +1025,16 @@ var init_JSBCommand = __esm({
|
|
|
1015
1025
|
}
|
|
1016
1026
|
};
|
|
1017
1027
|
CreateSpatializedStatic3DElementCommand = class extends JSBCommand {
|
|
1018
|
-
constructor(modelURL) {
|
|
1028
|
+
constructor(modelURL, sources) {
|
|
1019
1029
|
super();
|
|
1020
1030
|
this.modelURL = modelURL;
|
|
1031
|
+
this.sources = sources;
|
|
1021
1032
|
this.modelURL = modelURL;
|
|
1033
|
+
this.sources = sources;
|
|
1022
1034
|
}
|
|
1023
1035
|
commandType = "CreateSpatializedStatic3DElement";
|
|
1024
1036
|
getParams() {
|
|
1025
|
-
return { modelURL: this.modelURL };
|
|
1037
|
+
return { modelURL: this.modelURL, sources: this.sources };
|
|
1026
1038
|
}
|
|
1027
1039
|
};
|
|
1028
1040
|
CreateSpatializedDynamic3DElementCommand = class extends JSBCommand {
|
|
@@ -1554,6 +1566,69 @@ var CubeInfo = class {
|
|
|
1554
1566
|
}
|
|
1555
1567
|
};
|
|
1556
1568
|
|
|
1569
|
+
// src/scene-polyfill.ts
|
|
1570
|
+
init_utils();
|
|
1571
|
+
|
|
1572
|
+
// src/physicalMetrics.ts
|
|
1573
|
+
var physicalMetrics_exports = {};
|
|
1574
|
+
__export(physicalMetrics_exports, {
|
|
1575
|
+
getValue: () => getValue,
|
|
1576
|
+
physicalToPoint: () => physicalToPoint,
|
|
1577
|
+
pointToPhysical: () => pointToPhysical,
|
|
1578
|
+
subscribe: () => subscribe
|
|
1579
|
+
});
|
|
1580
|
+
init_SpatialWebEvent();
|
|
1581
|
+
var snapshot = {
|
|
1582
|
+
meterToPtUnscaled: 1360,
|
|
1583
|
+
meterToPtScaled: 1360
|
|
1584
|
+
};
|
|
1585
|
+
function getWorldScalingCompensation(options) {
|
|
1586
|
+
return options?.worldScalingCompensation ?? "scaled";
|
|
1587
|
+
}
|
|
1588
|
+
function pointToPhysical(point, options) {
|
|
1589
|
+
updateValue();
|
|
1590
|
+
const compensation = getWorldScalingCompensation(options);
|
|
1591
|
+
if (compensation === "unscaled") {
|
|
1592
|
+
return point / snapshot.meterToPtUnscaled;
|
|
1593
|
+
}
|
|
1594
|
+
return point / snapshot.meterToPtScaled;
|
|
1595
|
+
}
|
|
1596
|
+
function physicalToPoint(physical, options) {
|
|
1597
|
+
updateValue();
|
|
1598
|
+
const compensation = getWorldScalingCompensation(options);
|
|
1599
|
+
if (compensation === "unscaled") {
|
|
1600
|
+
return physical * snapshot.meterToPtUnscaled;
|
|
1601
|
+
}
|
|
1602
|
+
return physical * snapshot.meterToPtScaled;
|
|
1603
|
+
}
|
|
1604
|
+
function updateValue() {
|
|
1605
|
+
if (typeof window === "undefined") return;
|
|
1606
|
+
const src = window.__webspatialsdk__?.physicalMetrics;
|
|
1607
|
+
if (!src) return;
|
|
1608
|
+
const next = {
|
|
1609
|
+
meterToPtScaled: src.meterToPtScaled ?? snapshot.meterToPtScaled,
|
|
1610
|
+
meterToPtUnscaled: src.meterToPtUnscaled ?? snapshot.meterToPtUnscaled
|
|
1611
|
+
};
|
|
1612
|
+
if (next.meterToPtScaled !== snapshot.meterToPtScaled || next.meterToPtUnscaled !== snapshot.meterToPtUnscaled) {
|
|
1613
|
+
snapshot = next;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
function getValue() {
|
|
1617
|
+
updateValue();
|
|
1618
|
+
return snapshot;
|
|
1619
|
+
}
|
|
1620
|
+
function subscribe(cb) {
|
|
1621
|
+
if (typeof window === "undefined") return () => {
|
|
1622
|
+
};
|
|
1623
|
+
const handler = () => {
|
|
1624
|
+
cb();
|
|
1625
|
+
};
|
|
1626
|
+
SpatialWebEvent.addEventReceiver("window", handler);
|
|
1627
|
+
return () => {
|
|
1628
|
+
SpatialWebEvent.removeEventReceiver("window");
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1557
1632
|
// src/scene-polyfill.ts
|
|
1558
1633
|
var defaultSceneConfig = {
|
|
1559
1634
|
defaultSize: {
|
|
@@ -1563,15 +1638,47 @@ var defaultSceneConfig = {
|
|
|
1563
1638
|
};
|
|
1564
1639
|
var defaultSceneConfigVolume = {
|
|
1565
1640
|
defaultSize: {
|
|
1566
|
-
width: 0.
|
|
1567
|
-
height: 0.
|
|
1568
|
-
depth: 0.
|
|
1641
|
+
width: "0.94m",
|
|
1642
|
+
height: "0.94m",
|
|
1643
|
+
depth: "0.94m"
|
|
1569
1644
|
}
|
|
1570
1645
|
};
|
|
1646
|
+
var xr_window_defaults = {
|
|
1647
|
+
...defaultSceneConfig
|
|
1648
|
+
};
|
|
1649
|
+
var xr_volume_defaults = {
|
|
1650
|
+
...defaultSceneConfigVolume
|
|
1651
|
+
};
|
|
1571
1652
|
var INTERNAL_SCHEMA_PREFIX = "webspatial://";
|
|
1653
|
+
function deepMergePlain(base, over) {
|
|
1654
|
+
if (!over) return { ...base || {} };
|
|
1655
|
+
const out = { ...base || {} };
|
|
1656
|
+
for (const k of Object.keys(over)) {
|
|
1657
|
+
const bv = out[k];
|
|
1658
|
+
const ov = over[k];
|
|
1659
|
+
if (ov && typeof ov === "object" && !Array.isArray(ov) && bv && typeof bv === "object" && !Array.isArray(bv)) {
|
|
1660
|
+
out[k] = deepMergePlain(bv, ov);
|
|
1661
|
+
} else {
|
|
1662
|
+
out[k] = ov;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
return out;
|
|
1666
|
+
}
|
|
1667
|
+
function normalizeXRDefaultsToSceneOptions(src) {
|
|
1668
|
+
const out = { ...src || {} };
|
|
1669
|
+
const ds = src.defaultSize !== void 0 ? src.defaultSize : src.default_size;
|
|
1670
|
+
if (ds !== void 0) {
|
|
1671
|
+
out.defaultSize = ds;
|
|
1672
|
+
}
|
|
1673
|
+
if ("default_size" in out) {
|
|
1674
|
+
delete out.default_size;
|
|
1675
|
+
}
|
|
1676
|
+
return out;
|
|
1677
|
+
}
|
|
1572
1678
|
var SceneManager = class _SceneManager {
|
|
1573
1679
|
originalOpen;
|
|
1574
1680
|
static instance;
|
|
1681
|
+
manifestReady = null;
|
|
1575
1682
|
static getInstance() {
|
|
1576
1683
|
if (!_SceneManager.instance) {
|
|
1577
1684
|
_SceneManager.instance = new _SceneManager();
|
|
@@ -1579,15 +1686,23 @@ var SceneManager = class _SceneManager {
|
|
|
1579
1686
|
return _SceneManager.instance;
|
|
1580
1687
|
}
|
|
1581
1688
|
init(window2) {
|
|
1689
|
+
this.manifestReady = this.setupManifest();
|
|
1582
1690
|
this.originalOpen = window2.open.bind(window2);
|
|
1583
1691
|
window2.open = this.open;
|
|
1584
1692
|
}
|
|
1693
|
+
// Stores the latest formatted config used by the platform (per scene name).
|
|
1694
|
+
// This object contains normalized values and is safe for internal consumption.
|
|
1585
1695
|
configMap = {};
|
|
1586
|
-
// name
|
|
1696
|
+
// Stores the raw callback return value (per scene name) to feed into the next initScene call as `pre`.
|
|
1697
|
+
// We keep this unformatted so developers receive exactly what they last returned.
|
|
1698
|
+
callbackReturnMap = {};
|
|
1587
1699
|
getConfig(name) {
|
|
1588
1700
|
if (name === void 0 || !this.configMap[name]) return void 0;
|
|
1589
1701
|
return this.configMap[name];
|
|
1590
1702
|
}
|
|
1703
|
+
waitManifest() {
|
|
1704
|
+
return this.manifestReady ?? Promise.resolve();
|
|
1705
|
+
}
|
|
1591
1706
|
// Ensure URL is absolute; only convert when a relative path is provided
|
|
1592
1707
|
// - Keep external and special schemes untouched (http, https, data, blob, about, file, mailto, etc.)
|
|
1593
1708
|
// - Handle protocol-relative URLs (//example.com/path)
|
|
@@ -1606,14 +1721,41 @@ var SceneManager = class _SceneManager {
|
|
|
1606
1721
|
return raw;
|
|
1607
1722
|
}
|
|
1608
1723
|
}
|
|
1724
|
+
async setupManifest() {
|
|
1725
|
+
const manifest = await this.getPWAManifest();
|
|
1726
|
+
try {
|
|
1727
|
+
const xr = manifest?.xr_spatial_scene;
|
|
1728
|
+
if (!xr || typeof xr !== "object") return;
|
|
1729
|
+
const { overrides, ...topLevel } = xr;
|
|
1730
|
+
const windowRaw = deepMergePlain(topLevel, overrides?.window_scene);
|
|
1731
|
+
const volumeRaw = deepMergePlain(topLevel, overrides?.volume_scene);
|
|
1732
|
+
const windowNext = normalizeXRDefaultsToSceneOptions(windowRaw);
|
|
1733
|
+
const volumeNext = normalizeXRDefaultsToSceneOptions(volumeRaw);
|
|
1734
|
+
if (windowNext && Object.keys(windowNext).length > 0) {
|
|
1735
|
+
xr_window_defaults = windowNext;
|
|
1736
|
+
}
|
|
1737
|
+
if (volumeNext && Object.keys(volumeNext).length > 0) {
|
|
1738
|
+
xr_volume_defaults = volumeNext;
|
|
1739
|
+
}
|
|
1740
|
+
} catch (error) {
|
|
1741
|
+
console.warn(
|
|
1742
|
+
"SceneManager.setupManifest failed; using built-in defaults.",
|
|
1743
|
+
error?.message || error
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1609
1747
|
open = (url, target, features) => {
|
|
1610
1748
|
if (url?.startsWith(INTERNAL_SCHEMA_PREFIX)) {
|
|
1611
|
-
if (url.includes("createSpatialized2DElement")) {
|
|
1612
|
-
const token =
|
|
1749
|
+
if (url.includes("createSpatialized2DElement") || url.includes("createAttachment")) {
|
|
1750
|
+
const token = (
|
|
1751
|
+
//@ts-ignore
|
|
1752
|
+
(window.webSpatial || window.__webspatialShell__)?.genToken?.()
|
|
1753
|
+
);
|
|
1613
1754
|
if (token) {
|
|
1755
|
+
const command = url.includes("createAttachment") ? "createAttachment" : "createSpatialized2DElement";
|
|
1614
1756
|
const host = window.location.host;
|
|
1615
1757
|
const protocol = window.location.protocol;
|
|
1616
|
-
const finalURL = `${protocol}//${host}/${token}/?command
|
|
1758
|
+
const finalURL = `${protocol}//${host}/${token}/?command=${command}`;
|
|
1617
1759
|
const rid = new URL(url).searchParams.get("rid");
|
|
1618
1760
|
const final = new URL(finalURL);
|
|
1619
1761
|
if (rid) final.searchParams.set("rid", rid);
|
|
@@ -1627,7 +1769,12 @@ var SceneManager = class _SceneManager {
|
|
|
1627
1769
|
const newWindow = this.originalOpen(url, target, features);
|
|
1628
1770
|
return newWindow;
|
|
1629
1771
|
}
|
|
1630
|
-
|
|
1772
|
+
let cfg = target ? this.getConfig(target) : void 0;
|
|
1773
|
+
if (cfg === void 0) {
|
|
1774
|
+
const preFormatted = deepCloneJSON(getSceneDefaultConfig("window"));
|
|
1775
|
+
const [ans] = formatSceneConfig(preFormatted, "window");
|
|
1776
|
+
cfg = { ...ans, type: "window" };
|
|
1777
|
+
}
|
|
1631
1778
|
const cmd = new createSpatialSceneCommand(url, cfg, target, features);
|
|
1632
1779
|
const result = cmd.executeSync();
|
|
1633
1780
|
const id = result.data?.id;
|
|
@@ -1639,23 +1786,116 @@ var SceneManager = class _SceneManager {
|
|
|
1639
1786
|
};
|
|
1640
1787
|
initScene(name, callback, options) {
|
|
1641
1788
|
const sceneType = options?.type ?? "window";
|
|
1642
|
-
const
|
|
1643
|
-
const
|
|
1644
|
-
|
|
1789
|
+
const defaultConfigRaw = getSceneDefaultConfig(sceneType);
|
|
1790
|
+
const previousOrDefault = this.callbackReturnMap[name] ?? (() => {
|
|
1791
|
+
const cloned = deepCloneJSON(defaultConfigRaw);
|
|
1792
|
+
return cloned;
|
|
1793
|
+
})();
|
|
1794
|
+
const rawReturnVal = callback(previousOrDefault);
|
|
1795
|
+
const sanitizedReturnVal = sanitizeSceneOptionsUnits(
|
|
1796
|
+
deepCloneJSON(rawReturnVal)
|
|
1797
|
+
);
|
|
1798
|
+
const clonedForFormat = deepCloneJSON(sanitizedReturnVal);
|
|
1799
|
+
const baseDefaults = deepCloneJSON(getSceneDefaultConfig(sceneType));
|
|
1800
|
+
const mergedForFormat = deepMergePlain(baseDefaults, clonedForFormat);
|
|
1801
|
+
const [formattedConfig, errors] = formatSceneConfig(
|
|
1802
|
+
mergedForFormat,
|
|
1803
|
+
sceneType
|
|
1804
|
+
);
|
|
1645
1805
|
if (errors.length > 0) {
|
|
1646
1806
|
console.warn(`initScene ${name} with errors: ${errors.join(", ")}`);
|
|
1647
1807
|
}
|
|
1808
|
+
this.callbackReturnMap[name] = sanitizedReturnVal;
|
|
1648
1809
|
this.configMap[name] = {
|
|
1649
1810
|
...formattedConfig,
|
|
1650
1811
|
type: sceneType
|
|
1651
1812
|
};
|
|
1652
1813
|
}
|
|
1814
|
+
/**
|
|
1815
|
+
* Resolve and load a PWA manifest as JSON:
|
|
1816
|
+
* 1) Determine href:
|
|
1817
|
+
* - Prefer explicit manifestUrl if provided;
|
|
1818
|
+
* - Fallback to <link rel="manifest">, preferring the raw attribute over computed href.
|
|
1819
|
+
* 2) Normalize href to absolute using ensureAbsoluteUrl (respects <base href>).
|
|
1820
|
+
* 3) Handle data URLs inline:
|
|
1821
|
+
* - data:...;base64,... → atob then JSON.parse
|
|
1822
|
+
* - data:...,... → decodeURIComponent then JSON.parse
|
|
1823
|
+
* 4) Fetch with credentials same-origin first; if that fails (e.g., CORS), attempt unauthenticated fetch.
|
|
1824
|
+
* 5) Parse as JSON; if response body is text, parse the text as JSON.
|
|
1825
|
+
*/
|
|
1826
|
+
async getPWAManifest(manifestUrl) {
|
|
1827
|
+
let href = manifestUrl;
|
|
1828
|
+
if (!href) {
|
|
1829
|
+
const el = document.querySelector(
|
|
1830
|
+
'link[rel="manifest"]'
|
|
1831
|
+
);
|
|
1832
|
+
href = el?.getAttribute("href") || el?.href;
|
|
1833
|
+
}
|
|
1834
|
+
if (!href) return;
|
|
1835
|
+
href = this.ensureAbsoluteUrl(href);
|
|
1836
|
+
if (!href) return;
|
|
1837
|
+
if (href.startsWith("data:")) {
|
|
1838
|
+
try {
|
|
1839
|
+
const comma = href.indexOf(",");
|
|
1840
|
+
if (comma < 0) return;
|
|
1841
|
+
const meta = href.slice(5, comma);
|
|
1842
|
+
const data = href.slice(comma + 1);
|
|
1843
|
+
const isBase64 = /;base64/i.test(meta);
|
|
1844
|
+
const decoded = isBase64 ? atob(data) : decodeURIComponent(data);
|
|
1845
|
+
return JSON.parse(decoded);
|
|
1846
|
+
} catch {
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
try {
|
|
1851
|
+
const res = await fetch(href, { credentials: "same-origin" });
|
|
1852
|
+
if (!res.ok) throw new Error(String(res.status));
|
|
1853
|
+
try {
|
|
1854
|
+
return await res.json();
|
|
1855
|
+
} catch {
|
|
1856
|
+
const t = await res.text();
|
|
1857
|
+
return JSON.parse(t);
|
|
1858
|
+
}
|
|
1859
|
+
} catch {
|
|
1860
|
+
try {
|
|
1861
|
+
const res = await fetch(href);
|
|
1862
|
+
if (!res.ok) return;
|
|
1863
|
+
try {
|
|
1864
|
+
return await res.json();
|
|
1865
|
+
} catch {
|
|
1866
|
+
const t = await res.text();
|
|
1867
|
+
return JSON.parse(t);
|
|
1868
|
+
}
|
|
1869
|
+
} catch {
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1653
1874
|
};
|
|
1875
|
+
function sanitizeSceneOptionsUnits(val) {
|
|
1876
|
+
if (val?.defaultSize) {
|
|
1877
|
+
const keys = ["width", "height", "depth"];
|
|
1878
|
+
for (const k of keys) {
|
|
1879
|
+
if (k in val.defaultSize && !isValidSceneUnit(val.defaultSize[k])) {
|
|
1880
|
+
delete val.defaultSize[k];
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
if (val?.resizability) {
|
|
1885
|
+
const keys = ["minWidth", "minHeight", "maxWidth", "maxHeight"];
|
|
1886
|
+
for (const k of keys) {
|
|
1887
|
+
if (k in val.resizability && !isValidSceneUnit(val.resizability[k])) {
|
|
1888
|
+
delete val.resizability[k];
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
return val;
|
|
1893
|
+
}
|
|
1654
1894
|
function pxToMeter(px) {
|
|
1655
|
-
return px
|
|
1895
|
+
return pointToPhysical(px);
|
|
1656
1896
|
}
|
|
1657
1897
|
function meterToPx(meter) {
|
|
1658
|
-
return meter
|
|
1898
|
+
return physicalToPoint(meter);
|
|
1659
1899
|
}
|
|
1660
1900
|
function formatToNumber(str, targetUnit, defaultUnit) {
|
|
1661
1901
|
if (typeof str === "number") {
|
|
@@ -1705,11 +1945,10 @@ function formatSceneConfig(config, sceneType) {
|
|
|
1705
1945
|
config.defaultSize[k] = formatToNumber(
|
|
1706
1946
|
config.defaultSize[k],
|
|
1707
1947
|
isWindow ? "px" : "m",
|
|
1708
|
-
|
|
1948
|
+
"px"
|
|
1709
1949
|
);
|
|
1710
1950
|
} else {
|
|
1711
|
-
;
|
|
1712
|
-
config.defaultSize[k] = defaultSceneConfig2.defaultSize[k];
|
|
1951
|
+
delete config.defaultSize[k];
|
|
1713
1952
|
errors.push(`defaultSize.${k}`);
|
|
1714
1953
|
}
|
|
1715
1954
|
}
|
|
@@ -1723,11 +1962,10 @@ function formatSceneConfig(config, sceneType) {
|
|
|
1723
1962
|
config.resizability[k] = formatToNumber(
|
|
1724
1963
|
config.resizability[k],
|
|
1725
1964
|
"px",
|
|
1726
|
-
|
|
1965
|
+
"px"
|
|
1727
1966
|
);
|
|
1728
1967
|
} else {
|
|
1729
|
-
;
|
|
1730
|
-
config.resizability[k] = void 0;
|
|
1968
|
+
delete config.resizability[k];
|
|
1731
1969
|
errors.push(`resizability.${k}`);
|
|
1732
1970
|
}
|
|
1733
1971
|
}
|
|
@@ -1791,7 +2029,7 @@ function handleATag(event) {
|
|
|
1791
2029
|
}
|
|
1792
2030
|
}
|
|
1793
2031
|
function getSceneDefaultConfig(sceneType) {
|
|
1794
|
-
return sceneType === "window" ? defaultSceneConfig : defaultSceneConfigVolume;
|
|
2032
|
+
return sceneType === "window" ? xr_window_defaults || defaultSceneConfig : xr_volume_defaults || defaultSceneConfigVolume;
|
|
1795
2033
|
}
|
|
1796
2034
|
async function injectScenePolyfill() {
|
|
1797
2035
|
if (!window.opener) return;
|
|
@@ -1805,13 +2043,14 @@ async function injectScenePolyfill() {
|
|
|
1805
2043
|
}
|
|
1806
2044
|
}
|
|
1807
2045
|
onContentLoaded(async () => {
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
);
|
|
1811
|
-
|
|
2046
|
+
await SceneManager.getInstance().waitManifest();
|
|
2047
|
+
const sceneType = window.xrCurrentSceneType ?? "window";
|
|
2048
|
+
const rawDefault = getSceneDefaultConfig(sceneType);
|
|
2049
|
+
const pre = deepCloneJSON(rawDefault);
|
|
2050
|
+
let cfg = pre;
|
|
1812
2051
|
if (typeof window.xrCurrentSceneDefaults === "function") {
|
|
1813
2052
|
try {
|
|
1814
|
-
cfg = await window.xrCurrentSceneDefaults?.(
|
|
2053
|
+
cfg = await window.xrCurrentSceneDefaults?.(pre);
|
|
1815
2054
|
} catch (error) {
|
|
1816
2055
|
console.error(error);
|
|
1817
2056
|
}
|
|
@@ -1821,17 +2060,18 @@ async function injectScenePolyfill() {
|
|
|
1821
2060
|
resolve(null);
|
|
1822
2061
|
}, 1e3);
|
|
1823
2062
|
});
|
|
1824
|
-
const
|
|
1825
|
-
const [formattedConfig, errors] = formatSceneConfig(
|
|
2063
|
+
const mergedCfg = deepMergePlain(deepCloneJSON(rawDefault), cfg);
|
|
2064
|
+
const [formattedConfig, errors] = formatSceneConfig(mergedCfg, sceneType);
|
|
1826
2065
|
if (errors.length > 0) {
|
|
1827
2066
|
console.warn(
|
|
1828
2067
|
`window.xrCurrentSceneDefaults with errors: ${errors.join(", ")}`
|
|
1829
2068
|
);
|
|
1830
2069
|
}
|
|
1831
|
-
|
|
2070
|
+
const finalCfg = {
|
|
1832
2071
|
...formattedConfig,
|
|
1833
2072
|
type: sceneType
|
|
1834
|
-
}
|
|
2073
|
+
};
|
|
2074
|
+
await SpatialScene.getInstance().updateSceneCreationConfig(finalCfg);
|
|
1835
2075
|
});
|
|
1836
2076
|
}
|
|
1837
2077
|
function injectSceneHook() {
|
|
@@ -2082,10 +2322,12 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
|
|
|
2082
2322
|
* Registers the element to receive spatial events.
|
|
2083
2323
|
* @param id Unique identifier for this element
|
|
2084
2324
|
* @param modelURL URL of the 3D model
|
|
2325
|
+
* @param sources Optional fallback model sources
|
|
2085
2326
|
*/
|
|
2086
|
-
constructor(id, modelURL) {
|
|
2327
|
+
constructor(id, modelURL, sources) {
|
|
2087
2328
|
super(id);
|
|
2088
2329
|
this.modelURL = modelURL;
|
|
2330
|
+
this.sources = sources;
|
|
2089
2331
|
}
|
|
2090
2332
|
/**
|
|
2091
2333
|
* Promise resolver for the ready state.
|
|
@@ -2097,6 +2339,17 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
|
|
|
2097
2339
|
* Used to reset the ready promise when the model URL changes.
|
|
2098
2340
|
*/
|
|
2099
2341
|
modelURL;
|
|
2342
|
+
/**
|
|
2343
|
+
* Caches the last sources array to detect changes.
|
|
2344
|
+
*/
|
|
2345
|
+
sources;
|
|
2346
|
+
/**
|
|
2347
|
+
* The model URL that was successfully loaded by the native runtime.
|
|
2348
|
+
*/
|
|
2349
|
+
_currentSrc = "";
|
|
2350
|
+
get currentSrc() {
|
|
2351
|
+
return this._currentSrc;
|
|
2352
|
+
}
|
|
2100
2353
|
/**
|
|
2101
2354
|
* Creates a new promise for tracking the ready state of the model.
|
|
2102
2355
|
* @returns Promise that resolves when the model is loaded (true) or fails to load (false)
|
|
@@ -2119,17 +2372,100 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
|
|
|
2119
2372
|
* @returns Promise resolving when the update is complete
|
|
2120
2373
|
*/
|
|
2121
2374
|
async updateProperties(properties) {
|
|
2375
|
+
let needsReadyReset = false;
|
|
2122
2376
|
if (properties.modelURL !== void 0) {
|
|
2123
2377
|
if (this.modelURL !== properties.modelURL) {
|
|
2124
2378
|
this.modelURL = properties.modelURL;
|
|
2125
|
-
|
|
2379
|
+
needsReadyReset = true;
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
if (properties.sources !== void 0) {
|
|
2383
|
+
const prevJson = JSON.stringify(this.sources);
|
|
2384
|
+
const nextJson = JSON.stringify(properties.sources);
|
|
2385
|
+
if (prevJson !== nextJson) {
|
|
2386
|
+
this.sources = properties.sources;
|
|
2387
|
+
needsReadyReset = true;
|
|
2126
2388
|
}
|
|
2127
2389
|
}
|
|
2390
|
+
if (needsReadyReset) {
|
|
2391
|
+
this.ready = this.createReadyPromise();
|
|
2392
|
+
}
|
|
2393
|
+
if (properties.autoplay !== void 0) {
|
|
2394
|
+
this._autoplay = properties.autoplay;
|
|
2395
|
+
}
|
|
2396
|
+
if (properties.loop !== void 0) {
|
|
2397
|
+
this._loop = properties.loop;
|
|
2398
|
+
}
|
|
2399
|
+
if (properties.playbackRate !== void 0) {
|
|
2400
|
+
this._playbackRate = properties.playbackRate;
|
|
2401
|
+
}
|
|
2128
2402
|
return new UpdateSpatializedStatic3DElementProperties(
|
|
2129
2403
|
this,
|
|
2130
2404
|
properties
|
|
2131
2405
|
).execute();
|
|
2132
2406
|
}
|
|
2407
|
+
/**
|
|
2408
|
+
* Total animation duration in seconds, synced from native.
|
|
2409
|
+
*/
|
|
2410
|
+
_duration = 0;
|
|
2411
|
+
/**
|
|
2412
|
+
* Returns the total animation duration in seconds.
|
|
2413
|
+
*/
|
|
2414
|
+
get duration() {
|
|
2415
|
+
return this._duration;
|
|
2416
|
+
}
|
|
2417
|
+
/**
|
|
2418
|
+
* Playback speed multiplier.
|
|
2419
|
+
*/
|
|
2420
|
+
_playbackRate = 1;
|
|
2421
|
+
/**
|
|
2422
|
+
* Returns the current playback rate.
|
|
2423
|
+
*/
|
|
2424
|
+
get playbackRate() {
|
|
2425
|
+
return this._playbackRate;
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Sets the playback rate and sends it to native.
|
|
2429
|
+
*/
|
|
2430
|
+
set playbackRate(value) {
|
|
2431
|
+
this.updateProperties({ playbackRate: value });
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* Whether the animation is currently paused.
|
|
2435
|
+
*/
|
|
2436
|
+
_paused = true;
|
|
2437
|
+
/**
|
|
2438
|
+
* Returns whether the animation is currently paused.
|
|
2439
|
+
*/
|
|
2440
|
+
get paused() {
|
|
2441
|
+
return this._paused;
|
|
2442
|
+
}
|
|
2443
|
+
/**
|
|
2444
|
+
* Callback for animation state changes.
|
|
2445
|
+
*/
|
|
2446
|
+
_onAnimationStateChangeCallback;
|
|
2447
|
+
/**
|
|
2448
|
+
* Sets the callback for animation state changes.
|
|
2449
|
+
*/
|
|
2450
|
+
set onAnimationStateChangeCallback(callback) {
|
|
2451
|
+
this._onAnimationStateChangeCallback = callback;
|
|
2452
|
+
}
|
|
2453
|
+
/**
|
|
2454
|
+
* Starts or resumes animation playback.
|
|
2455
|
+
* @returns Promise resolving when the command is sent
|
|
2456
|
+
*/
|
|
2457
|
+
async play() {
|
|
2458
|
+
this._paused = false;
|
|
2459
|
+
await this.updateProperties({ animationPaused: false });
|
|
2460
|
+
}
|
|
2461
|
+
/**
|
|
2462
|
+
* Pauses animation playback.
|
|
2463
|
+
* @returns Promise resolving when the command is sent
|
|
2464
|
+
*/
|
|
2465
|
+
async pause() {
|
|
2466
|
+
this._paused = true;
|
|
2467
|
+
await this.updateProperties({ animationPaused: true });
|
|
2468
|
+
}
|
|
2133
2469
|
/**
|
|
2134
2470
|
* Processes events received from the WebSpatial environment.
|
|
2135
2471
|
* Handles model loading events in addition to base spatial events.
|
|
@@ -2137,15 +2473,40 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
|
|
|
2137
2473
|
*/
|
|
2138
2474
|
onReceiveEvent(data) {
|
|
2139
2475
|
if (data.type === "modelloaded" /* modelloaded */) {
|
|
2476
|
+
this._currentSrc = data.detail?.src ?? this.modelURL ?? "";
|
|
2140
2477
|
this._onLoadCallback?.();
|
|
2141
2478
|
this._readyResolve?.(true);
|
|
2142
2479
|
} else if (data.type === "modelloadfailed" /* modelloadfailed */) {
|
|
2143
2480
|
this._onLoadFailureCallback?.();
|
|
2144
2481
|
this._readyResolve?.(false);
|
|
2482
|
+
} else if (data.type === "animationstatechange" /* animationstatechange */) {
|
|
2483
|
+
this._paused = data.detail.paused;
|
|
2484
|
+
this._duration = data.detail.duration;
|
|
2485
|
+
this._onAnimationStateChangeCallback?.(data.detail);
|
|
2145
2486
|
} else {
|
|
2146
2487
|
super.onReceiveEvent(data);
|
|
2147
2488
|
}
|
|
2148
2489
|
}
|
|
2490
|
+
/**
|
|
2491
|
+
* Whether the model should automatically play its first animation on load.
|
|
2492
|
+
*/
|
|
2493
|
+
_autoplay = false;
|
|
2494
|
+
/**
|
|
2495
|
+
* Returns whether autoplay is enabled for this element.
|
|
2496
|
+
*/
|
|
2497
|
+
get autoplay() {
|
|
2498
|
+
return this._autoplay;
|
|
2499
|
+
}
|
|
2500
|
+
/**
|
|
2501
|
+
* Whether the model animation should loop continuously.
|
|
2502
|
+
*/
|
|
2503
|
+
_loop = false;
|
|
2504
|
+
/**
|
|
2505
|
+
* Returns whether loop is enabled for this element.
|
|
2506
|
+
*/
|
|
2507
|
+
get loop() {
|
|
2508
|
+
return this._loop;
|
|
2509
|
+
}
|
|
2149
2510
|
/**
|
|
2150
2511
|
* Callback function for successful model loading.
|
|
2151
2512
|
*/
|
|
@@ -2219,15 +2580,16 @@ async function createSpatialized2DElement() {
|
|
|
2219
2580
|
return new Spatialized2DElement(id, windowProxy);
|
|
2220
2581
|
}
|
|
2221
2582
|
}
|
|
2222
|
-
async function createSpatializedStatic3DElement(modelURL) {
|
|
2583
|
+
async function createSpatializedStatic3DElement(modelURL, sources) {
|
|
2223
2584
|
const result = await new CreateSpatializedStatic3DElementCommand(
|
|
2224
|
-
modelURL
|
|
2585
|
+
modelURL,
|
|
2586
|
+
sources
|
|
2225
2587
|
).execute();
|
|
2226
2588
|
if (!result.success) {
|
|
2227
2589
|
throw new Error("createSpatializedStatic3DElement failed");
|
|
2228
2590
|
} else {
|
|
2229
2591
|
const { id } = result.data;
|
|
2230
|
-
return new SpatializedStatic3DElement(id, modelURL);
|
|
2592
|
+
return new SpatializedStatic3DElement(id, modelURL, sources);
|
|
2231
2593
|
}
|
|
2232
2594
|
}
|
|
2233
2595
|
async function createSpatializedDynamic3DElement() {
|
|
@@ -2675,8 +3037,8 @@ var SpatialSession = class {
|
|
|
2675
3037
|
* @param modelURL Optional URL to the 3D model to load
|
|
2676
3038
|
* @returns Promise resolving to a new SpatializedStatic3DElement instance
|
|
2677
3039
|
*/
|
|
2678
|
-
createSpatializedStatic3DElement(modelURL) {
|
|
2679
|
-
return createSpatializedStatic3DElement(modelURL);
|
|
3040
|
+
createSpatializedStatic3DElement(modelURL, sources) {
|
|
3041
|
+
return createSpatializedStatic3DElement(modelURL, sources);
|
|
2680
3042
|
}
|
|
2681
3043
|
/**
|
|
2682
3044
|
* Initializes the spatial scene with custom configuration.
|
|
@@ -2811,7 +3173,7 @@ var Spatial = class {
|
|
|
2811
3173
|
* @returns True if running in a spatial web environment, false otherwise
|
|
2812
3174
|
*/
|
|
2813
3175
|
runInSpatialWeb() {
|
|
2814
|
-
if (navigator.userAgent.indexOf("WebSpatial/")
|
|
3176
|
+
if (navigator.userAgent.indexOf("WebSpatial/") >= 0) {
|
|
2815
3177
|
return true;
|
|
2816
3178
|
}
|
|
2817
3179
|
return false;
|
|
@@ -2855,70 +3217,10 @@ var Spatial = class {
|
|
|
2855
3217
|
* @returns Client SDK version string in format "x.x.x"
|
|
2856
3218
|
*/
|
|
2857
3219
|
getClientVersion() {
|
|
2858
|
-
return "1.
|
|
3220
|
+
return "1.6.0";
|
|
2859
3221
|
}
|
|
2860
3222
|
};
|
|
2861
3223
|
|
|
2862
|
-
// src/physicalMetrics.ts
|
|
2863
|
-
var physicalMetrics_exports = {};
|
|
2864
|
-
__export(physicalMetrics_exports, {
|
|
2865
|
-
getValue: () => getValue,
|
|
2866
|
-
physicalToPoint: () => physicalToPoint,
|
|
2867
|
-
pointToPhysical: () => pointToPhysical,
|
|
2868
|
-
subscribe: () => subscribe
|
|
2869
|
-
});
|
|
2870
|
-
init_SpatialWebEvent();
|
|
2871
|
-
var snapshot = {
|
|
2872
|
-
meterToPtUnscaled: 1360,
|
|
2873
|
-
meterToPtScaled: 1360
|
|
2874
|
-
};
|
|
2875
|
-
function getWorldScalingCompensation(options) {
|
|
2876
|
-
return options?.worldScalingCompensation ?? "scaled";
|
|
2877
|
-
}
|
|
2878
|
-
function pointToPhysical(point, options) {
|
|
2879
|
-
updateValue();
|
|
2880
|
-
const compensation = getWorldScalingCompensation(options);
|
|
2881
|
-
if (compensation === "unscaled") {
|
|
2882
|
-
return point / snapshot.meterToPtUnscaled;
|
|
2883
|
-
}
|
|
2884
|
-
return point / snapshot.meterToPtScaled;
|
|
2885
|
-
}
|
|
2886
|
-
function physicalToPoint(physical, options) {
|
|
2887
|
-
updateValue();
|
|
2888
|
-
const compensation = getWorldScalingCompensation(options);
|
|
2889
|
-
if (compensation === "unscaled") {
|
|
2890
|
-
return physical * snapshot.meterToPtUnscaled;
|
|
2891
|
-
}
|
|
2892
|
-
return physical * snapshot.meterToPtScaled;
|
|
2893
|
-
}
|
|
2894
|
-
function updateValue() {
|
|
2895
|
-
if (typeof window === "undefined") return;
|
|
2896
|
-
const src = window.__webspatialsdk__?.physicalMetrics;
|
|
2897
|
-
if (!src) return;
|
|
2898
|
-
const next = {
|
|
2899
|
-
meterToPtScaled: src.meterToPtScaled ?? snapshot.meterToPtScaled,
|
|
2900
|
-
meterToPtUnscaled: src.meterToPtUnscaled ?? snapshot.meterToPtUnscaled
|
|
2901
|
-
};
|
|
2902
|
-
if (next.meterToPtScaled !== snapshot.meterToPtScaled || next.meterToPtUnscaled !== snapshot.meterToPtUnscaled) {
|
|
2903
|
-
snapshot = next;
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
function getValue() {
|
|
2907
|
-
updateValue();
|
|
2908
|
-
return snapshot;
|
|
2909
|
-
}
|
|
2910
|
-
function subscribe(cb) {
|
|
2911
|
-
if (typeof window === "undefined") return () => {
|
|
2912
|
-
};
|
|
2913
|
-
const handler = () => {
|
|
2914
|
-
cb();
|
|
2915
|
-
};
|
|
2916
|
-
SpatialWebEvent.addEventReceiver("window", handler);
|
|
2917
|
-
return () => {
|
|
2918
|
-
SpatialWebEvent.removeEventReceiver("window");
|
|
2919
|
-
};
|
|
2920
|
-
}
|
|
2921
|
-
|
|
2922
3224
|
// src/index.ts
|
|
2923
3225
|
init_ssr_polyfill();
|
|
2924
3226
|
|