lecodes-viewer 0.19.2 → 0.20.2
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/dist-embed/VERSION.json +3 -3
- package/dist-embed/assets/{createViewer2d-D12Wy3VF.js → createViewer2d-Dx-COLjO.js} +1 -1
- package/dist-embed/assets/createViewerLite-B91Sj6p_.js +852 -0
- package/dist-embed/assets/host-DZ6302Ho.js +168 -0
- package/dist-embed/assets/{index-UwRci5mq.js → index-D10DoNfS.js} +1 -1
- package/dist-embed/assets/{loadCreatorUiNode-UAdhjRTd.js → loadCreatorUiNode-D87bZHrH.js} +1 -1
- package/dist-embed/assets/mapViewImpl-B2JcES8l.js +810 -0
- package/dist-embed/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
- package/dist-embed/assets/{particles-DxYDmI1O.filamat → particles-Cgp1L6RQ.filamat} +0 -0
- package/dist-embed/assets/{particles-quad-DwYo9yCK.filamat → particles-quad-CgrEJN4M.filamat} +0 -0
- package/dist-embed/embed.js +9 -9
- package/package.json +7 -3
- package/src/createViewer.ts +2 -0
- package/src/host.ts +3 -0
- package/src/ui/nativeViewSystem.ts +2 -91
- package/src/ui/presentPlayer.ts +2 -156
- package/src/utils/imageNode.ts +5 -0
- package/src/viewer/createViewer.ts +26 -2
- package/src/viewer/particles-quad.filamat +0 -0
- package/src/viewer/particles.filamat +0 -0
- package/dist-embed/assets/createViewerLite-CinItTpO.js +0 -707
- package/dist-embed/assets/host-Vzbyo0SX.js +0 -162
- package/src/assets/banner.png +0 -0
- package/src/assets/image2.png +0 -0
- package/src/assets/landing.mp4 +0 -0
- package/src/assets/neutral_ibl256.ktx +0 -0
- package/src/assets/output.mp4 +0 -0
- package/src/assets/output2.mp4 +0 -0
- package/src/assets/texture.png +0 -0
- package/src/assets/vue.svg +0 -1
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lecodes-viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.2",
|
|
4
4
|
"main": "src/createViewer.ts",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"lecodes-renderer": "0.
|
|
6
|
+
"lecodes-renderer": "0.20.2",
|
|
7
7
|
"ogl": "^1.0.11",
|
|
8
|
-
"lecodes-viewer-lite": "0.
|
|
8
|
+
"lecodes-viewer-lite": "0.20.2"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"dev": "vite",
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"type": "module",
|
|
26
26
|
"files": [
|
|
27
27
|
"src",
|
|
28
|
+
"!src/assets/*.mp4",
|
|
29
|
+
"!src/assets/*.png",
|
|
30
|
+
"!src/assets/*.svg",
|
|
31
|
+
"!src/assets/neutral_ibl256.ktx",
|
|
28
32
|
"dist-embed",
|
|
29
33
|
"embed.html",
|
|
30
34
|
"README.md"
|
package/src/createViewer.ts
CHANGED
|
@@ -18,6 +18,8 @@ export { getBuffer } from './utils/fetch'
|
|
|
18
18
|
export { decodeKtx2Image, isKtx2Bytes } from 'lecodes-viewer-lite/src/gl/decoders'
|
|
19
19
|
export type { CreatorMethods, EngineMode, Host, HostOptions, Run, RunOptions, UIFactory } from './host'
|
|
20
20
|
export type { PreviewError, MappedFrame } from './sourcemapError'
|
|
21
|
+
// The render tree the canvas UI hands back (`_creatorUI.renderTree()`) — the embedder's verify gates walk it.
|
|
22
|
+
export type { RenderNode } from 'lecodes-renderer'
|
|
21
23
|
export { createCanvasUIRenderer } from './ui/mountCanvasUI'
|
|
22
24
|
export { createBareUI } from './ui/createBareUI'
|
|
23
25
|
export type { SafeAreaEdge, SafeAreaSpec } from './ui/safeAreaSpec'
|
package/src/host.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { createCanvasHost } from './canvas/createCanvasHost'
|
|
|
13
13
|
import { putBuffer, getBuffer } from './utils/fetch'
|
|
14
14
|
import { createNavHostBridge } from 'lecodes-viewer-lite/src/gl/core/navBridge'
|
|
15
15
|
import { createInputHost } from "lecodes-viewer-lite/src/utils/input"
|
|
16
|
+
import { registerWebPlugins } from "lecodes-viewer-lite/src/plugins"
|
|
16
17
|
import { createUtils } from './utils/createUtils'
|
|
17
18
|
import { ensureFontsLoaded, loadBootFonts, type BootFont } from './utils/fonts'
|
|
18
19
|
import type { SafeAreaSpec } from './ui/safeAreaSpec'
|
|
@@ -217,6 +218,8 @@ export const createHost = async (root: HTMLElement, options: HostOptions = {}) =
|
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
const _creatorUI = options.ui ? await options.ui(root, canvasHost) : undefined
|
|
221
|
+
// The web twins of the first-party NativeView plugins (camera, map) — shared with the lite host.
|
|
222
|
+
registerWebPlugins(_creatorUI)
|
|
220
223
|
const _creatorUtils = createUtils({ launchUrl: options.launchUrl, onOrientation: options.onOrientation })
|
|
221
224
|
_creatorUI?._setCreator(_creator)
|
|
222
225
|
// Before the ResizeObserver's initial fire, so the first applied layout already has the insets.
|
|
@@ -1,91 +1,2 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
// host._creatorUI._registerView("map", (params, channel) => {
|
|
5
|
-
// const el = document.createElement("div")
|
|
6
|
-
// return { el, call: (method, args) => {...}, destroy: () => {...} }
|
|
7
|
-
// })
|
|
8
|
-
//
|
|
9
|
-
// One SDK NativeView instance = one live DOM instance (keyed by its _viewId), reused across
|
|
10
|
-
// embed/fullscreen/pager mounts — that reuse IS the promotion semantics. An unregistered
|
|
11
|
-
// name renders a labeled placeholder so a bundle stays inspectable on hosts without the plugin.
|
|
12
|
-
|
|
13
|
-
export type WebViewChannel = { emit(event: string, data?: any): void }
|
|
14
|
-
export type WebViewInstanceHandle = {
|
|
15
|
-
el: HTMLElement
|
|
16
|
-
call?(method: string, args: any[]): any | Promise<any>
|
|
17
|
-
destroy?(): void
|
|
18
|
-
}
|
|
19
|
-
export type WebViewFactory = (params: any, channel: WebViewChannel) => WebViewInstanceHandle
|
|
20
|
-
|
|
21
|
-
// The SDK-side NativeView element/descriptor surface the system needs (see sdk NativeView.ts).
|
|
22
|
-
type SdkNativeView = {
|
|
23
|
-
viewName: string
|
|
24
|
-
params: any
|
|
25
|
-
_viewId: number
|
|
26
|
-
_emitViewEvent(event: string, dataJson?: string): void
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const createNativeViewSystem = () => {
|
|
30
|
-
const factories = new Map<string, WebViewFactory>()
|
|
31
|
-
const instances = new Map<number, WebViewInstanceHandle>()
|
|
32
|
-
|
|
33
|
-
const registerView = (name: string, factory: WebViewFactory) => { factories.set(name, factory) }
|
|
34
|
-
const isViewSupported = (name: string) => factories.has(name)
|
|
35
|
-
|
|
36
|
-
const placeholder = (name: string): WebViewInstanceHandle => {
|
|
37
|
-
const el = document.createElement("div")
|
|
38
|
-
el.style.cssText = "display:flex;align-items:center;justify-content:center;background:#333;color:#999;font:12px sans-serif"
|
|
39
|
-
el.textContent = `NativeView("${name}") — no web factory registered`
|
|
40
|
-
return { el }
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** Resolve (or lazily create) the live instance for an SDK NativeView. */
|
|
44
|
-
const ensureInstance = (view: SdkNativeView): WebViewInstanceHandle => {
|
|
45
|
-
let instance = instances.get(view._viewId)
|
|
46
|
-
if (!instance) {
|
|
47
|
-
const factory = factories.get(view.viewName)
|
|
48
|
-
instance = factory
|
|
49
|
-
? factory(view.params, { emit: (event, data) => view._emitViewEvent(event, data === undefined ? undefined : JSON.stringify(data)) })
|
|
50
|
-
: placeholder(view.viewName)
|
|
51
|
-
instances.set(view._viewId, instance)
|
|
52
|
-
}
|
|
53
|
-
return instance
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** The `_creatorUI.viewCall` bridge — JSON in/out, promise-friendly. */
|
|
57
|
-
const viewCall = (viewId: number, method: string, args: string, onComplete: (result?: string) => void, onError: (err: string) => void) => {
|
|
58
|
-
const instance = instances.get(viewId)
|
|
59
|
-
if (!instance?.call) {
|
|
60
|
-
onError(`NativeView call "${method}": no live instance or no call handler (viewId ${viewId})`)
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
Promise.resolve(instance.call(method, JSON.parse(args))).then(
|
|
65
|
-
(result) => onComplete(result === undefined ? undefined : JSON.stringify(result)),
|
|
66
|
-
(e) => onError(e instanceof Error ? e.message : String(e)),
|
|
67
|
-
)
|
|
68
|
-
} catch (e) {
|
|
69
|
-
onError(e instanceof Error ? e.message : String(e))
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** Mount the live instance into a layout cell (the "native" node's DOM element). A view lives
|
|
74
|
-
* in ONE place: mounting here steals it from wherever it was (promotion works the same way
|
|
75
|
-
* in reverse — openView steals it from the cell, close returns it via re-mount). */
|
|
76
|
-
const mountInto = (view: SdkNativeView, cell: HTMLElement) => {
|
|
77
|
-
const { el } = ensureInstance(view)
|
|
78
|
-
el.style.position = "absolute"
|
|
79
|
-
el.style.inset = "0"
|
|
80
|
-
cell.appendChild(el)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const destroy = () => {
|
|
84
|
-
for (const i of instances.values()) i.destroy?.()
|
|
85
|
-
instances.clear()
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return { registerView, isViewSupported, ensureInstance, viewCall, mountInto, destroy }
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export type NativeViewSystem = ReturnType<typeof createNativeViewSystem>
|
|
1
|
+
// Moved to viewer-lite (the NativeView registry + channel are shared by both web hosts); re-export shim.
|
|
2
|
+
export * from "lecodes-viewer-lite/src/ui/nativeViewSystem"
|
package/src/ui/presentPlayer.ts
CHANGED
|
@@ -1,156 +1,2 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
// so a custom spec from the SDK needs no extra machinery.
|
|
4
|
-
//
|
|
5
|
-
// The static rule (mirrors the Android SurfaceView constraint so apps feel the same): a side
|
|
6
|
-
// marked `static` (the scene canvas) never animates — only the screen side moves. Screens are
|
|
7
|
-
// position:absolute so they always paint above the in-flow canvas, which makes the degradations
|
|
8
|
-
// coherent: entering a scene slides the old screen away to reveal it; leaving a scene slides the
|
|
9
|
-
// new screen in over it.
|
|
10
|
-
|
|
11
|
-
export type SpecTransform = { x?: number | string, y?: number | string, scale?: number, opacity?: number }
|
|
12
|
-
export type TransitionSpecLike = {
|
|
13
|
-
duration?: number
|
|
14
|
-
easing?: [number, number, number, number]
|
|
15
|
-
incoming?: { from?: SpecTransform }
|
|
16
|
-
outgoing?: { to?: SpecTransform }
|
|
17
|
-
dim?: number
|
|
18
|
-
onTop?: "incoming" | "outgoing"
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const STACK_EASING: [number, number, number, number] = [0.2, 0, 0, 1]
|
|
22
|
-
|
|
23
|
-
const NAMED: Record<string, TransitionSpecLike> = {
|
|
24
|
-
"none": {},
|
|
25
|
-
"fade": { duration: 280, incoming: { from: { opacity: 0 } } },
|
|
26
|
-
"slide-from-right": { duration: 400, incoming: { from: { x: "100%" } }, outgoing: { to: { x: "-30%" } } },
|
|
27
|
-
"slide-from-left": { duration: 400, incoming: { from: { x: "-100%" } }, outgoing: { to: { x: "30%" } } },
|
|
28
|
-
"slide-from-top": { duration: 400, incoming: { from: { y: "-100%" } }, outgoing: { to: { y: "30%" } } },
|
|
29
|
-
"slide-from-bottom": { duration: 400, incoming: { from: { y: "100%" } }, outgoing: { to: { y: "-30%" } } },
|
|
30
|
-
"zoom": { duration: 400, easing: [0.33, 1.2, 0.62, 1], incoming: { from: { scale: 0.985 } } },
|
|
31
|
-
"zoom-in": { duration: 400, easing: [0.33, 1.2, 0.62, 1], incoming: { from: { scale: 1.08, opacity: 0 } }, onTop: "incoming" },
|
|
32
|
-
"zoom-out": { duration: 420, outgoing: { to: { scale: 0.92, opacity: 0 } }, onTop: "outgoing" },
|
|
33
|
-
"push": { duration: 350, easing: STACK_EASING, incoming: { from: { x: "100%" } }, outgoing: { to: { x: "-25%" } }, dim: 0.3, onTop: "incoming" },
|
|
34
|
-
"pop": { duration: 350, easing: STACK_EASING, incoming: { from: { x: "-25%" } }, outgoing: { to: { x: "100%" } }, dim: 0.3, onTop: "outgoing" },
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const toTransform = (t: SpecTransform): string => {
|
|
38
|
-
const parts: string[] = []
|
|
39
|
-
if (t.x !== undefined || t.y !== undefined) {
|
|
40
|
-
const px = (v: number | string | undefined) => v === undefined ? "0px" : typeof v === "number" ? v + "px" : v
|
|
41
|
-
parts.push(`translate(${px(t.x)}, ${px(t.y)})`)
|
|
42
|
-
}
|
|
43
|
-
if (t.scale !== undefined) parts.push(`scale(${t.scale})`)
|
|
44
|
-
return parts.length > 0 ? parts.join(" ") : "none"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const poseKeyframe = (t: SpecTransform): Keyframe => {
|
|
48
|
-
const kf: Keyframe = { transform: toTransform(t) }
|
|
49
|
-
if (t.opacity !== undefined) kf.opacity = t.opacity
|
|
50
|
-
return kf
|
|
51
|
-
}
|
|
52
|
-
const IDENTITY: Keyframe = { transform: "none", opacity: 1 }
|
|
53
|
-
|
|
54
|
-
// The exit pose for an outgoing side when the incoming one is static (a scene): mirror the
|
|
55
|
-
// incoming entry so the reveal keeps the transition's direction (push's 100%-from-right becomes
|
|
56
|
-
// a full slide-out to the left); pure opacity entries become a fade-out.
|
|
57
|
-
const mirrorExit = (from: SpecTransform): SpecTransform => {
|
|
58
|
-
const neg = (v: number | string | undefined) =>
|
|
59
|
-
v === undefined ? undefined : typeof v === "number" ? -v : v.startsWith("-") ? v.slice(1) : "-" + v
|
|
60
|
-
const out: SpecTransform = {}
|
|
61
|
-
if (from.x !== undefined) out.x = neg(from.x)
|
|
62
|
-
if (from.y !== undefined) out.y = neg(from.y)
|
|
63
|
-
if (from.scale !== undefined) out.scale = 2 - from.scale
|
|
64
|
-
if (from.opacity !== undefined || Object.keys(out).length === 0) out.opacity = 0
|
|
65
|
-
return out
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
type Side = { el: HTMLElement, static?: boolean }
|
|
69
|
-
|
|
70
|
-
export const createPresentPlayer = (container: HTMLElement) => {
|
|
71
|
-
let finishActive: (() => void) | null = null
|
|
72
|
-
|
|
73
|
-
/** Play `transition` between the mounted `incoming` and the still-mounted `outgoing`; calls
|
|
74
|
-
* `onDone` exactly once (after the animation, or immediately for none/unsupported) — the
|
|
75
|
-
* caller physically unmounts the outgoing side there. A new play() snaps the previous
|
|
76
|
-
* transition to its end first. */
|
|
77
|
-
const play = (opts: {
|
|
78
|
-
incoming?: Side
|
|
79
|
-
outgoing?: Side
|
|
80
|
-
transition: string | TransitionSpecLike | null | undefined
|
|
81
|
-
onDone: () => void
|
|
82
|
-
}) => {
|
|
83
|
-
finishActive?.()
|
|
84
|
-
|
|
85
|
-
const spec = typeof opts.transition === "string" ? NAMED[opts.transition] : opts.transition ?? undefined
|
|
86
|
-
const inSide = opts.incoming && !opts.incoming.static ? opts.incoming.el : null
|
|
87
|
-
const outSide = opts.outgoing && !opts.outgoing.static ? opts.outgoing.el : null
|
|
88
|
-
|
|
89
|
-
let inFrom = spec?.incoming?.from
|
|
90
|
-
let outTo = spec?.outgoing?.to
|
|
91
|
-
|
|
92
|
-
// Static-side degradations (see header). A poseless spec ("none") stays instant — the
|
|
93
|
-
// degradations only reshape transitions that actually wanted to move something.
|
|
94
|
-
if (opts.incoming?.static && outSide && (spec?.incoming || spec?.outgoing)) {
|
|
95
|
-
// The incoming scene can't animate: unless this is an exit-shaped transition (onTop:
|
|
96
|
-
// "outgoing" — pop/zoom-out already fully remove the old side), the outgoing screen leaves
|
|
97
|
-
// by the mirror of the entry it was supposed to be covered by.
|
|
98
|
-
if (spec?.onTop !== "outgoing") outTo = inFrom ? mirrorExit(inFrom) : (outTo ?? { opacity: 0 })
|
|
99
|
-
inFrom = undefined
|
|
100
|
-
}
|
|
101
|
-
if (opts.outgoing?.static && inSide && !inFrom && spec && Object.keys(spec).length > 0) {
|
|
102
|
-
inFrom = { opacity: 0 } // something must move; fade the screen in over the scene
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const hasWork = (inSide && inFrom) || (outSide && outTo)
|
|
106
|
-
if (!spec || !hasWork || typeof (inSide ?? outSide)?.animate !== "function") {
|
|
107
|
-
opts.onDone()
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const duration = spec.duration ?? 300
|
|
112
|
-
const easing = spec.easing ? `cubic-bezier(${spec.easing.join(",")})` : "cubic-bezier(0.25, 0.1, 0.25, 1)"
|
|
113
|
-
const animations: Animation[] = []
|
|
114
|
-
|
|
115
|
-
// z-order for the duration of the transition (screens only — the static canvas stays in
|
|
116
|
-
// flow at the bottom). Dim sits between the two.
|
|
117
|
-
const onTop = spec.onTop ?? "incoming"
|
|
118
|
-
const topEl = onTop === "incoming" ? inSide : outSide
|
|
119
|
-
const bottomEl = onTop === "incoming" ? outSide : inSide
|
|
120
|
-
if (topEl) topEl.style.zIndex = "3"
|
|
121
|
-
if (bottomEl) bottomEl.style.zIndex = "1"
|
|
122
|
-
|
|
123
|
-
let dimEl: HTMLElement | null = null
|
|
124
|
-
if (spec.dim && spec.dim > 0) {
|
|
125
|
-
dimEl = document.createElement("div")
|
|
126
|
-
dimEl.style.cssText = "position:absolute;inset:0;background:#000;pointer-events:none;z-index:2"
|
|
127
|
-
container.appendChild(dimEl)
|
|
128
|
-
// Entering (push): the under layer darkens; exiting (pop): it un-darkens.
|
|
129
|
-
const [a, b] = onTop === "incoming" ? [0, spec.dim] : [spec.dim, 0]
|
|
130
|
-
animations.push(dimEl.animate([{ opacity: a }, { opacity: b }], { duration, easing }))
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (inSide && inFrom) {
|
|
134
|
-
animations.push(inSide.animate([poseKeyframe(inFrom), IDENTITY], { duration, easing }))
|
|
135
|
-
}
|
|
136
|
-
if (outSide && outTo) {
|
|
137
|
-
animations.push(outSide.animate([IDENTITY, poseKeyframe(outTo)], { duration, easing, fill: "forwards" }))
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
let done = false
|
|
141
|
-
const cleanup = () => {
|
|
142
|
-
if (done) return
|
|
143
|
-
done = true
|
|
144
|
-
finishActive = null
|
|
145
|
-
for (const a of animations) a.cancel() // drops fill:forwards so a detached-not-freed screen restores clean
|
|
146
|
-
if (topEl) topEl.style.zIndex = ""
|
|
147
|
-
if (bottomEl) bottomEl.style.zIndex = ""
|
|
148
|
-
dimEl?.remove()
|
|
149
|
-
opts.onDone()
|
|
150
|
-
}
|
|
151
|
-
finishActive = cleanup
|
|
152
|
-
Promise.all(animations.map(a => a.finished)).then(cleanup, cleanup)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return { play, finish: () => finishActive?.() }
|
|
156
|
-
}
|
|
1
|
+
// Moved to viewer-lite (both web hosts play the same destination transitions); re-export shim.
|
|
2
|
+
export * from "lecodes-viewer-lite/src/ui/presentPlayer"
|
package/src/utils/imageNode.ts
CHANGED
|
@@ -99,6 +99,11 @@ function configureImageSrc(host: ImageHost, el: HTMLElement, src: ImageSrc, tint
|
|
|
99
99
|
;(el as any)._canvasSurface = surfaceId
|
|
100
100
|
el.removeAttribute("data-viewbox")
|
|
101
101
|
el.src = host.canvasHost?.surfaceToDataUrl(surfaceId) ?? ""
|
|
102
|
+
} else if (typeof src === "object" && "scene2d" in src) {
|
|
103
|
+
// UIImage(scene2d): a live 2D scene drawn into the box — desktop only so far (parity
|
|
104
|
+
// ui-scene2d-image); a blank box here rather than a bogus blob url.
|
|
105
|
+
el.removeAttribute("data-viewbox")
|
|
106
|
+
el.removeAttribute("src")
|
|
102
107
|
} else {
|
|
103
108
|
el.removeAttribute("data-viewbox")
|
|
104
109
|
el.src = typeof src === "object" ? getUrl(src._id) : src
|
|
@@ -440,6 +440,17 @@ export const createViewer = async (canvas: HTMLCanvasElement, options: ViewerOpt
|
|
|
440
440
|
// Screen inputs are logical px; scale to backing px for the native (viewport-space) pick.
|
|
441
441
|
raycastView: (offsetX: number, offsetY: number) => module._raycastView(offsetX * resFactor, offsetY * resFactor),
|
|
442
442
|
setCulling: module._setCulling,
|
|
443
|
+
// Draw order / per-instance depth state (Mesh.renderPriority, Material.depthTest / depthWrite) —
|
|
444
|
+
// only once the wasm carries the exports; the SDK feature-detects them.
|
|
445
|
+
setRenderPriority: module._setRenderPriority,
|
|
446
|
+
setMaterialDepthTest: module._setMaterialDepthTest ? (id: number, on: boolean) => module._setMaterialDepthTest(id, on ? 1 : 0) : undefined,
|
|
447
|
+
setMaterialDepthWrite: module._setMaterialDepthWrite ? (id: number, on: boolean) => module._setMaterialDepthWrite(id, on ? 1 : 0) : undefined,
|
|
448
|
+
setMaterialCulling: module._setMaterialCulling,
|
|
449
|
+
setMaterialStencil: module._setMaterialStencil
|
|
450
|
+
? (id: number, write: boolean, test: number, ref: number, onPass: number, onFail: number, onDepthFail: number, readMask: number, writeMask: number) =>
|
|
451
|
+
module._setMaterialStencil(id, write ? 1 : 0, test, ref, onPass, onFail, onDepthFail, readMask, writeMask)
|
|
452
|
+
: undefined,
|
|
453
|
+
setSceneStencil: module._setSceneStencil ? (sceneId: number, on: boolean) => module._setSceneStencil(sceneId, on ? 1 : 0) : undefined,
|
|
443
454
|
setGlbCulling: module._setGlbCulling,
|
|
444
455
|
// Culling default for animated GLBs (Model.load): only once the wasm carries the export — an older
|
|
445
456
|
// binary leaves the SDK on the always-draw default (see bridges.d.ts).
|
|
@@ -615,7 +626,7 @@ export const createViewer = async (canvas: HTMLCanvasElement, options: ViewerOpt
|
|
|
615
626
|
createMaterial: (systemId: number) => {
|
|
616
627
|
return module._createMaterialInstance(compileMaterial(systemId))
|
|
617
628
|
},
|
|
618
|
-
setMesh(entityId: number, materialId: number, vertices: Float32Array, normals: Float32Array, indices: Uint16Array, uv: Float32Array, meshType: number, uv1?: Float32Array) {
|
|
629
|
+
setMesh(entityId: number, materialId: number, vertices: Float32Array, normals: Float32Array, indices: Uint16Array, uv: Float32Array, meshType: number, uv1?: Float32Array, colors?: Uint8Array) {
|
|
619
630
|
const ptrVertices = module._malloc(vertices.byteLength)
|
|
620
631
|
const ptrNormals = module._malloc(normals.byteLength)
|
|
621
632
|
const ptrIndices = module._malloc(indices.byteLength)
|
|
@@ -625,6 +636,15 @@ export const createViewer = async (canvas: HTMLCanvasElement, options: ViewerOpt
|
|
|
625
636
|
module.HEAPU16.set(indices, ptrIndices/2)
|
|
626
637
|
module.HEAPF32.set(uv, ptrUv/4)
|
|
627
638
|
|
|
639
|
+
// the vertex colours ride the newest export (createMeshC); a wasm without it draws white
|
|
640
|
+
if (colors && module._createMeshC) {
|
|
641
|
+
const ptrUv1 = uv1 ? module._malloc(uv1.byteLength) : 0
|
|
642
|
+
if (uv1 && ptrUv1) module.HEAPF32.set(uv1, ptrUv1 / 4)
|
|
643
|
+
const ptrColors = module._malloc(colors.byteLength)
|
|
644
|
+
module.HEAPU8.set(colors, ptrColors)
|
|
645
|
+
module._createMeshC(entityId, materialId, ptrVertices, ptrNormals, ptrIndices, ptrUv, vertices.length / 3, indices.length, meshType, ptrUv1, ptrColors)
|
|
646
|
+
return
|
|
647
|
+
}
|
|
628
648
|
// the lightmap UV set rides the newer export; a wasm without it duplicates uv into UV1
|
|
629
649
|
if (uv1 && module._createMeshEx) {
|
|
630
650
|
const ptrUv1 = module._malloc(uv1.byteLength)
|
|
@@ -832,7 +852,11 @@ export const createViewer = async (canvas: HTMLCanvasElement, options: ViewerOpt
|
|
|
832
852
|
return out
|
|
833
853
|
},
|
|
834
854
|
getGlbClipSet: (entityId: number) => module._getGlbClipSet(entityId),
|
|
835
|
-
|
|
855
|
+
deriveClip: (clipSetId: number, clip: number, mirror: boolean, start: number, end: number) => {
|
|
856
|
+
if (typeof module._deriveClip === "function") return module._deriveClip(clipSetId, clip, mirror ? 1 : 0, start, end)
|
|
857
|
+
if (mirror) { console.warn("creator-gl: this wasm cannot mirror clips — rebuild it"); return 0 } // a wasm from before 2026-09-06
|
|
858
|
+
return module._sliceClip(clipSetId, clip, start < 0 ? 0 : start, end < 0 ? 1e9 : end)
|
|
859
|
+
},
|
|
836
860
|
destroyClipSet: (clipSetId: number) => module._destroyClipSet(clipSetId),
|
|
837
861
|
animatorCreate: (entityId: number) => module._animatorCreate(entityId),
|
|
838
862
|
animatorDestroy: (animatorId: number) => module._animatorDestroy(animatorId),
|
|
Binary file
|
|
Binary file
|