@volter/editor-game 0.5.66 → 0.5.68
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/contributions/audio-unlock.service.ts +2 -2
- package/contributions/autoplay.service.ts +1 -1
- package/contributions/bridge.command.ts +3 -3
- package/contributions/build-player.document.tsx +95 -0
- package/contributions/canvas/component-board.service.ts +16 -0
- package/contributions/canvas/design-time-mount.service.ts +45 -0
- package/contributions/canvas-story-capture.service.ts +12 -0
- package/contributions/edit-mode-audio.service.ts +2 -2
- package/contributions/edit-mode-networking.service.ts +1 -1
- package/contributions/gameplay.command.ts +4 -4
- package/contributions/generation.service.ts +1 -1
- package/contributions/godot.style.ts +26 -6
- package/contributions/godot.view.ts +6 -0
- package/contributions/ingest.service.ts +2 -2
- package/contributions/instances.command.ts +1 -1
- package/contributions/navmesh.menu.ts +1 -1
- package/contributions/network-observer.service.ts +14 -0
- package/contributions/play.command.ts +10 -4
- package/contributions/react/component-board.service.ts +1 -1
- package/contributions/react/design-time-mount.service.ts +1 -1
- package/contributions/scene-document.service.ts +3 -3
- package/contributions/state-watch.menu.ts +1 -1
- package/contributions/state-watch.utility.tsx +1 -1
- package/contributions/team-playtest.service.ts +4 -4
- package/contributions/three/component-board.service.ts +1 -1
- package/contributions/three/component-verbs.command.ts +6 -6
- package/contributions/three/three-authoring.service.ts +8 -5
- package/contributions/three-story-capture.service.ts +12 -0
- package/contributions/unity.style.ts +17 -7
- package/contributions/unity.view.ts +6 -0
- package/contributions/unreal.style.ts +10 -2
- package/contributions/unreal.view.ts +5 -0
- package/package.json +21 -10
- package/src/asset-budget/AssetBudgetPanel.tsx +1 -1
- package/src/asset-budget/asset-budget-model.ts +1 -1
- package/src/audio/AudioDebuggerPanel.tsx +1 -1
- package/src/bridge/dispatch.ts +14 -14
- package/src/bridge/live-frames.ts +1 -1
- package/src/bridge/screenshot.ts +3 -3
- package/src/build/BuildProfilesPanel.tsx +14 -3
- package/src/build/build-session.ts +35 -0
- package/src/canvas/canvas-board/CanvasBoardDocument.tsx +749 -0
- package/src/canvas/canvas-board/canvas-board-model.ts +407 -0
- package/src/canvas/canvas-board/canvas-component-board.ts +56 -0
- package/src/canvas/canvas-design-mount.ts +524 -0
- package/src/canvas/design-time-canvas-mount.ts +79 -0
- package/src/coverage/live-authoring-surface.ts +5 -5
- package/src/coverage/live-project-verbs.ts +1 -1
- package/src/coverage/native-system-coverage.ts +5 -5
- package/src/coverage/root-coverage.ts +1 -1
- package/src/coverage/session-coverage.ts +3 -3
- package/src/design-system-stories/ApplicationChrome.stories.tsx +5 -5
- package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +7 -7
- package/src/edit-mode/edit-mode-audio.ts +4 -4
- package/src/edit-mode/edit-mode-networking.ts +2 -2
- package/src/game-document/GameCaptureFrameButton.tsx +1 -1
- package/src/game-document/GameDocument.tsx +2 -2
- package/src/game-document/GamePanel.tsx +4 -4
- package/src/game-document/InstanceInspectorPicker.tsx +2 -2
- package/src/game-document/crowd-debug.ts +1 -1
- package/src/game-document/device-preview.ts +10 -11
- package/src/game-document/physics-debug.ts +1 -1
- package/src/generation/GenerationActivity.tsx +3 -3
- package/src/generation/generation-documents.tsx +5 -5
- package/src/generation/generation-jobs.ts +1 -1
- package/src/host/adapter-runtime-bindings.ts +96 -5
- package/src/host/authoring/babylon-authoring-adapter.ts +6 -6
- package/src/host/authoring/gesture-persist.ts +1 -1
- package/src/host/authoring/ingest-data-writer.ts +1 -1
- package/src/host/authoring/ingest-source-persistence.ts +4 -4
- package/src/host/authoring/mounted-authoring.ts +4 -4
- package/src/host/authoring/phaser-live-authoring-adapter.ts +4 -4
- package/src/host/authoring/pixi-authoring-adapter.ts +178 -31
- package/src/host/authoring/pixi-creatable-kinds.ts +62 -0
- package/src/host/authoring/pixi-creation-site-write-target.ts +2 -2
- package/src/host/authoring/pixi-live-write-target.ts +4 -4
- package/src/host/authoring/pixi-source-identity.ts +3 -3
- package/src/host/authoring/pixi-source-write-target.ts +1709 -0
- package/src/host/authoring/pixi-still-presentation.ts +1 -1
- package/src/host/authoring/pixi-structure-history.ts +2 -2
- package/src/host/authoring/pixi-transform-channels.ts +16 -14
- package/src/host/authoring/source-persistence-backend.ts +3 -3
- package/src/host/authoring/struct-write-pipe.ts +1 -1
- package/src/host/binding-resolver.ts +8 -9
- package/src/host/browser-transpile.ts +1 -1
- package/src/host/canvas-entry-runtime.ts +58 -47
- package/src/host/canvas-preview-frames.ts +482 -0
- package/src/host/components/CameraAuthoringOverlay.tsx +1 -1
- package/src/host/components/HeaderTelemetry.tsx +4 -4
- package/src/host/components/PixiIsolationSceneContent.tsx +11 -11
- package/src/host/components/ThreeIsolationSceneContent.tsx +3 -3
- package/src/host/components/frame-debugger-model.ts +2 -2
- package/src/host/components/header-telemetry-model.ts +2 -2
- package/src/host/components/scene-document.tsx +14 -14
- package/src/host/components/utility-view-state.ts +1 -1
- package/src/host/components/world-root-stage-binding.tsx +12 -12
- package/src/host/components/world-root-stage.ts +70 -49
- package/src/host/coverage/system-adapter-coverage.ts +3 -4
- package/src/host/design-system-stories/fixtures/editor-runtime.tsx +9 -11
- package/src/host/document-preview-three.ts +1 -1
- package/src/host/entry-adjudication.ts +6 -6
- package/src/host/game-css-scope-transform.ts +4 -0
- package/src/host/game-realm-page.ts +1 -1
- package/src/host/gameplay-export.ts +25 -14
- package/src/host/gameplay-recording.ts +5 -5
- package/src/host/gated-globals.ts +2 -2
- package/src/host/history/json-history-resource.ts +1 -1
- package/src/host/projection/pixi.ts +24 -2
- package/src/host/r3f-entry-runtime.ts +65 -34
- package/src/host/react-mount-runtime.ts +7 -48
- package/src/host/realm-services.ts +1 -1
- package/src/host/roots/canvas-root.tsx +373 -0
- package/src/host/roots/r3f-root.tsx +473 -0
- package/src/host/roots/react-root.ts +9 -43
- package/src/host/served-bundle-runtime-modules.ts +3 -19
- package/src/host/server-log-bridge.ts +2 -2
- package/src/host/stories/mounted-story-viewport-source.ts +1 -1
- package/src/host/stories/pixi-story-model.ts +30 -0
- package/src/host/stories/story-media-captures.ts +46 -0
- package/src/host/stories/story-media-presence.ts +3 -3
- package/src/host/stories/story-pixi-preview.ts +408 -0
- package/src/host/stories/story-three-preview.ts +806 -0
- package/src/host/stories/three-story-captures.ts +35 -0
- package/src/host/story-three-preview-runtime.ts +56 -0
- package/src/host/use-active-performance-source.ts +2 -2
- package/src/host/viewport-pose-memory.ts +1 -1
- package/src/host/viewport-root-presentation.ts +6 -5
- package/src/ingest/active-ingest.ts +1 -1
- package/src/ingest/authoring/ingest-dom-surface-authoring.ts +4 -4
- package/src/ingest/authoring/ingest-root-adapter.ts +8 -8
- package/src/ingest/deferred-ingest-play.ts +6 -6
- package/src/ingest/discovery-public-ingest.ts +2 -2
- package/src/ingest/ingest-boot-viewport.ts +2 -2
- package/src/ingest/ingest-canvas-scene-document.tsx +9 -9
- package/src/ingest/ingest-canvas-scene.ts +3 -3
- package/src/ingest/ingest-evidence-hook.ts +1 -1
- package/src/ingest/ingest-frame-snapshot.ts +1 -1
- package/src/ingest/ingest-play-control.ts +1 -1
- package/src/ingest/ingest-render-debug.ts +10 -10
- package/src/ingest/ingest-siblings.ts +13 -25
- package/src/ingest/module-mode.ts +14 -14
- package/src/ingest/mount-canvas-ingest-root.ts +21 -21
- package/src/ingest/mount-coverage.ts +2 -2
- package/src/ingest/mount-dom-ingest-root.ts +11 -11
- package/src/ingest/mount-ingest-root.ts +8 -8
- package/src/ingest/mount-three-ingest-root.ts +8 -8
- package/src/ingest/resolve-canvas.ts +1 -1
- package/src/ingest/served-html-boot.ts +1 -1
- package/src/ingest/surface-canvas.ts +1 -1
- package/src/ingest/unmount-ingest-root.ts +5 -5
- package/src/navmesh/navmesh-handler.ts +24 -16
- package/src/network/NetworkInspectorPanel.tsx +939 -37
- package/src/network/network-inspector-model.ts +20 -2
- package/src/play/play-log-events.ts +1 -1
- package/src/play/play-mode.ts +76 -133
- package/src/play/play-recording.ts +1 -1
- package/src/play/react-play-live-authoring.ts +3 -3
- package/src/play/run-selection.ts +93 -0
- package/src/play-bar/PlayBar.tsx +20 -39
- package/src/profiler/FrameDebuggerPanel.tsx +1 -1
- package/src/profiler/PerformancePanel.tsx +3 -3
- package/src/react/design-time-react-mount.ts +23 -65
- package/src/react/dom-authoring-adapter.ts +9 -9
- package/src/react/react-inspector-section.tsx +5 -5
- package/src/react/react-world-authoring-adapter.ts +11 -11
- package/src/react/story-documents/story-documents.tsx +9 -9
- package/src/react/ui-board-document.tsx +7 -7
- package/src/react/ui-component-board.ts +2 -2
- package/src/runtime/adapter/audio-meter.ts +21 -0
- package/src/runtime/adapter/first-party-audio-system.ts +230 -0
- package/src/runtime/adapter/ingest/contract-debug-adapter.ts +114 -0
- package/src/runtime/adapter/ingest/contract-system-adapters.ts +256 -0
- package/src/runtime/adapter/ingest/merge-debug-adapters.ts +197 -0
- package/src/runtime/adapter/ingest/observation-debug-adapter.ts +162 -0
- package/src/runtime/adapter/ingest/upstream-pin.ts +51 -0
- package/src/runtime/adapter/native-debug-module.ts +498 -0
- package/src/runtime/audio/bus-mixer.ts +161 -0
- package/src/runtime/audio/pose-guard.ts +80 -0
- package/src/runtime/core/frame-pacing.ts +126 -0
- package/src/runtime/core/game-loop.ts +225 -0
- package/src/runtime/core/game-scoped-slot.ts +28 -0
- package/src/runtime/core/seeded-random.ts +162 -0
- package/src/runtime/core/sim-clock.ts +391 -0
- package/src/runtime/core/system-runner.ts +269 -0
- package/src/runtime/core/types.ts +104 -0
- package/src/runtime/create-runtime.ts +1128 -0
- package/src/runtime/debug-bridge.ts +570 -0
- package/src/runtime/debug-registry.ts +899 -0
- package/src/runtime/dev/chrome-trace.ts +153 -0
- package/src/runtime/dev/instruments.ts +403 -0
- package/src/runtime/dev/logger.ts +119 -0
- package/src/runtime/dev/performance-profiler.ts +367 -0
- package/src/runtime/dev/register-render-vitals.ts +276 -0
- package/src/runtime/dev/render-census.ts +354 -0
- package/src/runtime/dev/render-debug-adapter.ts +218 -0
- package/src/runtime/dev/render-memory.ts +226 -0
- package/src/runtime/dev/render-vitals.ts +338 -0
- package/src/runtime/dev/static-batch-advisor.ts +188 -0
- package/src/runtime/dev/webgl-frame-capture.ts +366 -0
- package/src/runtime/dev/webgl-gpu-timer.ts +53 -0
- package/src/runtime/dev-build.ts +47 -0
- package/src/runtime/game.ts +1636 -0
- package/src/runtime/gameplay-rng-trap.ts +135 -0
- package/src/runtime/host-context.ts +64 -0
- package/src/runtime/input-router.ts +182 -0
- package/src/runtime/mount-manifest.ts +480 -0
- package/src/runtime/pixi/authoring.ts +706 -0
- package/src/runtime/pixi/ingest.ts +116 -0
- package/src/runtime/pixi/physics-registry.ts +49 -0
- package/src/runtime/pixi/render-pass-bracket.ts +117 -0
- package/src/runtime/pixi/scene-capture.ts +179 -0
- package/src/runtime/pixi/system-adapters.ts +69 -0
- package/src/runtime/playtest.ts +22 -0
- package/src/runtime/presentation.ts +141 -0
- package/src/runtime/render-control.ts +642 -0
- package/src/runtime/render-seed.ts +77 -0
- package/src/runtime/run-ticks-settled.ts +73 -0
- package/src/runtime/setup/setup-audio.ts +72 -0
- package/src/services/audio-pose-guard.ts +2 -2
- package/src/services/game-audio.ts +152 -0
- package/src/services/game-network.ts +767 -0
- package/src/services/game-physics.ts +334 -0
- package/src/state-watch/StateWatchPanel.tsx +1 -1
- package/src/three/authoring/camera-runtime-inspector-section.tsx +3 -2
- package/src/three/authoring/constraint-inspector-section.tsx +6 -5
- package/src/three/authoring/model-asset-inspector-section.tsx +7 -6
- package/src/three/authoring/oid-source-persistence.ts +7 -7
- package/src/three/authoring/r3f-design-session.ts +58 -54
- package/src/three/authoring/r3f-source-authoring-adapter.ts +95 -91
- package/src/three/authoring/reflection-probe-inspector-section.tsx +3 -2
- package/src/three/authoring/three-authoring-adapter.ts +29 -29
- package/src/three/component-verbs/extract-menu.ts +7 -6
- package/src/three/component-verbs/fork-menu.ts +7 -6
- package/src/three/component-verbs/internals-menu.ts +2 -2
- package/src/three/story-documents/three-story-documents.tsx +13 -13
- package/src/three/three-board/ThreeBoardDocument.tsx +13 -12
- package/src/three/three-board/board-scene.ts +6 -6
- package/src/three/three-board/three-component-board.ts +2 -2
- package/src/services/game-audio-unlock.ts +0 -48
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
AuthoringAdapter2D,
|
|
35
35
|
type CanvasIdentity,
|
|
36
36
|
type Transform2DValue,
|
|
37
|
-
} from '
|
|
37
|
+
} from '../../runtime/pixi/authoring';
|
|
38
38
|
import type {
|
|
39
39
|
AssetDropContext,
|
|
40
40
|
AssetDropProvider,
|
|
@@ -70,9 +70,9 @@ import type {
|
|
|
70
70
|
import type { Container, Graphics, Matrix, PointData, Sprite, Text, Texture } from 'pixi.js';
|
|
71
71
|
import * as shellPixi from 'pixi.js';
|
|
72
72
|
import type { CanvasPixiNamespace } from '../canvas-entry-runtime';
|
|
73
|
-
import { componentStatesProvider } from '@volter/editor-
|
|
73
|
+
import { componentStatesProvider } from '@volter/editor-sdk/kit/component-states-registry';
|
|
74
74
|
import { editorConsole } from '@volter/editor-sdk/kit/editor-console';
|
|
75
|
-
import type { EditorShellStore } from '@volter/editor-
|
|
75
|
+
import type { EditorShellStore } from '@volter/editor-threejs/kit/editor-shell-store';
|
|
76
76
|
import type { JournalSubject } from '../history/json-history-resource';
|
|
77
77
|
import { PixiProjector } from '../projection/pixi';
|
|
78
78
|
import { creationSiteRelated } from './creation-site-related';
|
|
@@ -82,8 +82,10 @@ import {
|
|
|
82
82
|
readContainerAuthoringLabel,
|
|
83
83
|
} from './pixi-source-identity';
|
|
84
84
|
import { CanvasStructureHistory } from './pixi-structure-history';
|
|
85
|
+
import { PIXI_CREATABLE_KINDS } from './pixi-creatable-kinds';
|
|
85
86
|
import { fromNeutralTransform, toNeutralTransform } from './pixi-transform-channels';
|
|
86
87
|
import { resolvesLiveOnly, runWritePipe } from '@volter/editor-sdk/kit/write-pipe';
|
|
88
|
+
import { editorHost } from '@volter/editor-sdk/host';
|
|
87
89
|
|
|
88
90
|
/**
|
|
89
91
|
* What a write target is handed at construction — the live view it edits
|
|
@@ -302,12 +304,19 @@ export interface PixiAuthoringOptions {
|
|
|
302
304
|
* `Texture.WHITE` and a size; a graphics gets a drawn rect): a node the author
|
|
303
305
|
* cannot see is indistinguishable from a create that silently failed.
|
|
304
306
|
*/
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
307
|
+
/** The project-local section holding each canvas world's locked and grouped label paths. */
|
|
308
|
+
const EDIT_LOCKS_SECTION = 'canvasEditLocks';
|
|
309
|
+
type EditLocks = Record<string, { readonly locked: readonly string[]; readonly grouped: readonly string[] }>;
|
|
310
|
+
|
|
311
|
+
function readEditLocks(): EditLocks {
|
|
312
|
+
return editorHost().projectLocalState.read<EditLocks>(EDIT_LOCKS_SECTION) ?? {};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function writeEditLocks(world: string, locks: EditLocks[string]): void {
|
|
316
|
+
editorHost().projectLocalState.write(EDIT_LOCKS_SECTION, { ...readEditLocks(), [world]: locks });
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const CANVAS_CREATABLE_KINDS = PIXI_CREATABLE_KINDS;
|
|
311
320
|
|
|
312
321
|
/** What a canvas world can take from the asset browser: an image becomes a
|
|
313
322
|
* Sprite. A model or an audio file has no display object to become here. */
|
|
@@ -347,7 +356,7 @@ const ORIGIN_UNWRITABLE_REASON =
|
|
|
347
356
|
* from the one this prebuilt shell bundles — so a real `Sprite` from the world
|
|
348
357
|
* fails `instanceof Sprite` here and every selected sprite would silently read
|
|
349
358
|
* as a pivot node. The engine's own 2D authoring walk already refuses class
|
|
350
|
-
* identity for the same reason (`@volter/game
|
|
359
|
+
* identity for the same reason (`@volter/editor-game/runtime/pixi/authoring`'s `kindOf` keys on the
|
|
351
360
|
* constructor NAME); asking for the two fields the anchor question is actually
|
|
352
361
|
* about is the same move without depending on a name a minifier may mangle.
|
|
353
362
|
*
|
|
@@ -417,6 +426,19 @@ function createDisplayObject(pixi: CanvasPixiNamespace, kind: string): Container
|
|
|
417
426
|
return new pixi.Text({ text: 'Text', style: { fill: 0xffffff, fontSize: 24 } });
|
|
418
427
|
case 'graphics':
|
|
419
428
|
return new pixi.Graphics().rect(0, 0, 100, 100).fill(0xffffff);
|
|
429
|
+
case 'animatedSprite': {
|
|
430
|
+
const animated = new pixi.AnimatedSprite([pixi.Texture.WHITE]);
|
|
431
|
+
animated.setSize(64, 64);
|
|
432
|
+
return animated;
|
|
433
|
+
}
|
|
434
|
+
case 'tilingSprite':
|
|
435
|
+
return new pixi.TilingSprite({ texture: pixi.Texture.WHITE, width: 100, height: 100 });
|
|
436
|
+
case 'nineSliceSprite':
|
|
437
|
+
return new pixi.NineSliceSprite({ texture: pixi.Texture.WHITE, width: 100, height: 100 });
|
|
438
|
+
case 'bitmapText':
|
|
439
|
+
return new pixi.BitmapText({ text: 'BitmapText', style: { fill: 0xffffff, fontSize: 24 } });
|
|
440
|
+
case 'htmlText':
|
|
441
|
+
return new pixi.HTMLText({ text: 'HTMLText', style: { fill: 0xffffff, fontSize: 24 } });
|
|
420
442
|
default:
|
|
421
443
|
return null;
|
|
422
444
|
}
|
|
@@ -490,6 +512,35 @@ interface CanvasBoxEditSession {
|
|
|
490
512
|
readonly id: string;
|
|
491
513
|
readonly transform: Transform;
|
|
492
514
|
readonly rect: { x: number; y: number; width: number; height: number };
|
|
515
|
+
/** The parent's world matrix's linear part (Pixi's a, b, c, d), when the node has a parent. */
|
|
516
|
+
readonly parentLinear?: { a: number; b: number; c: number; d: number };
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* The local turn, in degrees, that turns the node's own x axis by `degrees` on screen. Godot measures
|
|
521
|
+
* a rotate drag in the parent's space; a turn added straight to the local rotation goes backwards
|
|
522
|
+
* under a mirrored parent and by the wrong amount under an unevenly scaled one.
|
|
523
|
+
*/
|
|
524
|
+
function localTurn(session: CanvasBoxEditSession, degrees: number): number {
|
|
525
|
+
const m = session.parentLinear;
|
|
526
|
+
if (!m) return degrees;
|
|
527
|
+
const det = m.a * m.d - m.b * m.c;
|
|
528
|
+
if (Math.abs(det) < 1e-9) return degrees;
|
|
529
|
+
const [, , z, w] = session.transform.rotation;
|
|
530
|
+
const r = 2 * Math.atan2(z, w);
|
|
531
|
+
// The node's x axis on screen, turned by the drag, taken back into the parent's space.
|
|
532
|
+
const vx = m.a * Math.cos(r) + m.c * Math.sin(r);
|
|
533
|
+
const vy = m.b * Math.cos(r) + m.d * Math.sin(r);
|
|
534
|
+
const t = (degrees * Math.PI) / 180;
|
|
535
|
+
const wx = vx * Math.cos(t) - vy * Math.sin(t);
|
|
536
|
+
const wy = vx * Math.sin(t) + vy * Math.cos(t);
|
|
537
|
+
const ux = (m.d * wx - m.c * wy) / det;
|
|
538
|
+
const uy = (-m.b * wx + m.a * wy) / det;
|
|
539
|
+
// A long drag passes half a turn: keep the answer beside the turn's own direction in that space.
|
|
540
|
+
const base = det < 0 ? -t : t;
|
|
541
|
+
const raw = Math.atan2(uy, ux) - r - base;
|
|
542
|
+
const wrapped = Math.atan2(Math.sin(raw), Math.cos(raw));
|
|
543
|
+
return ((base + wrapped) * 180) / Math.PI;
|
|
493
544
|
}
|
|
494
545
|
|
|
495
546
|
function boxPatchChannels(patch: Record<string, number>): TransformChannel[] {
|
|
@@ -538,7 +589,7 @@ function transformForBoxPatch(
|
|
|
538
589
|
scale: [...session.transform.scale],
|
|
539
590
|
};
|
|
540
591
|
const rotate = patch['rotate'];
|
|
541
|
-
if (rotate !== undefined) next.rotation = rotateTransformZ(session.transform, rotate);
|
|
592
|
+
if (rotate !== undefined) next.rotation = rotateTransformZ(session.transform, localTurn(session, rotate));
|
|
542
593
|
const width = patch['width'];
|
|
543
594
|
if (width !== undefined && session.rect.width > 0) {
|
|
544
595
|
next.scale[0] = session.transform.scale[0] * Math.max(0.0001, width / session.rect.width);
|
|
@@ -669,7 +720,17 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
669
720
|
| undefined;
|
|
670
721
|
private listeners = new Set<() => void>();
|
|
671
722
|
private boxEditSessions = new Map<string, CanvasBoxEditSession>();
|
|
672
|
-
|
|
723
|
+
/**
|
|
724
|
+
* Lock (a click passes through the node) and Godot's Group (a click inside a grouped node selects
|
|
725
|
+
* the group), held by each node's label path in this world so they survive the remount a source
|
|
726
|
+
* write causes and the next session. Kept per checkout, as Unity keeps scene pickability per
|
|
727
|
+
* user; Godot writes them into the scene file instead.
|
|
728
|
+
*/
|
|
729
|
+
private lockedPaths = new Set<string>();
|
|
730
|
+
private groupedPaths = new Set<string>();
|
|
731
|
+
private readonly editLockWorld: string;
|
|
732
|
+
/** Each node's label path, cleared whenever the tree is re-projected. */
|
|
733
|
+
private pathKeys = new Map<string, string>();
|
|
673
734
|
|
|
674
735
|
constructor(
|
|
675
736
|
private readonly root: Container,
|
|
@@ -721,13 +782,18 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
721
782
|
const surface = opts.surface;
|
|
722
783
|
if (surface) {
|
|
723
784
|
this.pickable = {
|
|
724
|
-
pick: (clientX, clientY) =>
|
|
725
|
-
this.projector.pick(clientX, clientY, (id) => this.isPickLocked(id))
|
|
726
|
-
|
|
727
|
-
|
|
785
|
+
pick: (clientX, clientY) => {
|
|
786
|
+
const hit = this.projector.pick(clientX, clientY, (id) => this.isPickLocked(id));
|
|
787
|
+
return hit ? this.groupOf(hit) : hit;
|
|
788
|
+
},
|
|
789
|
+
candidates: (clientX, clientY, options) =>
|
|
790
|
+
this.projector.candidates(clientX, clientY, (id) =>
|
|
791
|
+
options?.includeLocked ? false : this.isPickLocked(id),
|
|
792
|
+
),
|
|
728
793
|
};
|
|
729
794
|
this.rects = {
|
|
730
795
|
rect: (id) => this.projector.rect(id),
|
|
796
|
+
frame: (id) => this.projector.frame(id),
|
|
731
797
|
contextRects: (id) => this.projector.contextRects(id),
|
|
732
798
|
};
|
|
733
799
|
}
|
|
@@ -744,7 +810,18 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
744
810
|
this.target.provenance.source === 'source-code' && this.target.persistence !== undefined,
|
|
745
811
|
};
|
|
746
812
|
this.target.bind({ a2d: this.a2d, store, journal: opts.journal, notify: () => this.notify() });
|
|
747
|
-
this.
|
|
813
|
+
this.editLockWorld = opts.journal.id;
|
|
814
|
+
const saved = readEditLocks()[this.editLockWorld];
|
|
815
|
+
this.lockedPaths = new Set(saved?.locked ?? []);
|
|
816
|
+
this.groupedPaths = new Set(saved?.grouped ?? []);
|
|
817
|
+
const structure = this.target.structure ?? this.liveStructure;
|
|
818
|
+
// A placed creation reaches the structure in the new node's PARENT's own space: the target
|
|
819
|
+
// writes `x`/`y` as authored, and only this adapter knows the frame `rects` answer in.
|
|
820
|
+
this.structure = {
|
|
821
|
+
...structure,
|
|
822
|
+
create: (kind, parentId, at) =>
|
|
823
|
+
structure.create(kind, parentId, at ? this.pointInParent(parentId ?? null, at) : undefined),
|
|
824
|
+
};
|
|
748
825
|
this.assetDrop =
|
|
749
826
|
this.target.assetDrop ??
|
|
750
827
|
({
|
|
@@ -767,7 +844,7 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
767
844
|
if (!opts.stories && this.target.componentIdentity) {
|
|
768
845
|
this.stories = componentStatesProvider(
|
|
769
846
|
'canvas',
|
|
770
|
-
store,
|
|
847
|
+
store.shell,
|
|
771
848
|
(id) => this.target.componentIdentity?.(id) ?? null,
|
|
772
849
|
);
|
|
773
850
|
}
|
|
@@ -845,10 +922,11 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
845
922
|
queueMicrotask(() => {
|
|
846
923
|
this.structureNotifyQueued = false;
|
|
847
924
|
this.projector.reproject();
|
|
925
|
+
this.pathKeys.clear();
|
|
848
926
|
this.watchStructure(this.root);
|
|
849
927
|
this.target.onReindex();
|
|
850
928
|
this.notify();
|
|
851
|
-
this.store.notifyIngestEdit();
|
|
929
|
+
this.store.shell.notifyIngestEdit();
|
|
852
930
|
});
|
|
853
931
|
};
|
|
854
932
|
|
|
@@ -914,9 +992,9 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
914
992
|
}
|
|
915
993
|
|
|
916
994
|
readonly selection: SelectionProvider = {
|
|
917
|
-
get: () => [...this.store.selectedEntityIds],
|
|
995
|
+
get: () => [...this.store.shell.selectedEntityIds],
|
|
918
996
|
set: (ids) => {
|
|
919
|
-
this.store.selectMultiple(ids);
|
|
997
|
+
this.store.shell.selectMultiple(ids);
|
|
920
998
|
this.notify();
|
|
921
999
|
},
|
|
922
1000
|
};
|
|
@@ -945,7 +1023,7 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
945
1023
|
// degrade — a half-answer would be a menu item that silently does nothing.
|
|
946
1024
|
|
|
947
1025
|
private readonly liveStructure: StructureProvider = {
|
|
948
|
-
create: (kind, parentId) => this.createStructuralNode(kind, parentId ?? null),
|
|
1026
|
+
create: (kind, parentId, at) => this.createStructuralNode(kind, parentId ?? null, at),
|
|
949
1027
|
remove: (id) => this.removeStructuralNodes([id]),
|
|
950
1028
|
removeMany: (ids) => this.removeStructuralNodes(ids),
|
|
951
1029
|
duplicate: (id) => this.duplicateStructuralNode(id),
|
|
@@ -1009,10 +1087,11 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
1009
1087
|
*/
|
|
1010
1088
|
private afterStructuralChange(): void {
|
|
1011
1089
|
this.projector.reproject();
|
|
1090
|
+
this.pathKeys.clear();
|
|
1012
1091
|
this.watchStructure(this.root);
|
|
1013
1092
|
this.target.onReindex();
|
|
1014
1093
|
this.notify();
|
|
1015
|
-
this.store.notifyIngestEdit();
|
|
1094
|
+
this.store.shell.notifyIngestEdit();
|
|
1016
1095
|
}
|
|
1017
1096
|
|
|
1018
1097
|
private idOf(object: Container): string {
|
|
@@ -1025,7 +1104,22 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
1025
1104
|
* same turn. The live-only report is the other half of the answer and rides
|
|
1026
1105
|
* back in `ack` (see `StructuralIdWrite`) instead of being fired `void`.
|
|
1027
1106
|
*/
|
|
1028
|
-
|
|
1107
|
+
/** A point in the `rects` frame, in the space of the container a new child of `parentId` joins. */
|
|
1108
|
+
private pointInParent(
|
|
1109
|
+
parentId: string | null,
|
|
1110
|
+
at: { readonly x: number; readonly y: number },
|
|
1111
|
+
): { x: number; y: number } | undefined {
|
|
1112
|
+
const parent = this.containerFor(parentId);
|
|
1113
|
+
if (!parent || typeof parent.updateLocalTransform !== 'function') return undefined;
|
|
1114
|
+
const local = this.localToRootMatrix(parent).clone().invert().apply({ x: at.x, y: at.y });
|
|
1115
|
+
return { x: local.x, y: local.y };
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
private createStructuralNode(
|
|
1119
|
+
kind: string,
|
|
1120
|
+
parentId: string | null,
|
|
1121
|
+
at?: { readonly x: number; readonly y: number },
|
|
1122
|
+
): StructuralIdWrite {
|
|
1029
1123
|
const parent = this.containerFor(parentId);
|
|
1030
1124
|
if (!parent) {
|
|
1031
1125
|
return {
|
|
@@ -1041,6 +1135,7 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
1041
1135
|
};
|
|
1042
1136
|
}
|
|
1043
1137
|
node.label = defaultLabelFor(kind);
|
|
1138
|
+
if (at) node.position.set(at.x, at.y);
|
|
1044
1139
|
const label = `Create ${node.label}`;
|
|
1045
1140
|
this.structureHistory.track(parent);
|
|
1046
1141
|
this.structureHistory.track(node);
|
|
@@ -1274,7 +1369,13 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
1274
1369
|
this.boxEditSessions.delete(id);
|
|
1275
1370
|
return;
|
|
1276
1371
|
}
|
|
1277
|
-
|
|
1372
|
+
const parent = this.projector.object(id)?.parent?.worldTransform;
|
|
1373
|
+
this.boxEditSessions.set(id, {
|
|
1374
|
+
id,
|
|
1375
|
+
rect,
|
|
1376
|
+
transform: this.transforms.get(id),
|
|
1377
|
+
...(parent ? { parentLinear: { a: parent.a, b: parent.b, c: parent.c, d: parent.d } } : {}),
|
|
1378
|
+
});
|
|
1278
1379
|
this.transforms.beginEdit?.(id);
|
|
1279
1380
|
},
|
|
1280
1381
|
apply: (id, patch) => this.applyBoxEdit(id, patch),
|
|
@@ -1504,12 +1605,23 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
1504
1605
|
properties: (id) => [
|
|
1505
1606
|
...this.target.properties(id),
|
|
1506
1607
|
{ path: 'locked', label: 'Locked', type: 'boolean', group: 'Visibility' },
|
|
1608
|
+
{ path: 'grouped', label: 'Grouped', type: 'boolean', group: 'Visibility' },
|
|
1507
1609
|
],
|
|
1508
|
-
get: (id, path) =>
|
|
1610
|
+
get: (id, path) =>
|
|
1611
|
+
path === 'locked'
|
|
1612
|
+
? this.lockedPaths.has(this.pathKeyOf(id))
|
|
1613
|
+
: path === 'grouped'
|
|
1614
|
+
? this.groupedPaths.has(this.pathKeyOf(id))
|
|
1615
|
+
: this.target.get(id, path),
|
|
1509
1616
|
set: (id, path, value) => {
|
|
1510
|
-
if (path === 'locked') {
|
|
1511
|
-
|
|
1512
|
-
|
|
1617
|
+
if (path === 'locked' || path === 'grouped') {
|
|
1618
|
+
const set = path === 'locked' ? this.lockedPaths : this.groupedPaths;
|
|
1619
|
+
if (value === true) set.add(this.pathKeyOf(id));
|
|
1620
|
+
else set.delete(this.pathKeyOf(id));
|
|
1621
|
+
writeEditLocks(this.editLockWorld, {
|
|
1622
|
+
locked: [...this.lockedPaths],
|
|
1623
|
+
grouped: [...this.groupedPaths],
|
|
1624
|
+
});
|
|
1513
1625
|
this.notify();
|
|
1514
1626
|
return;
|
|
1515
1627
|
}
|
|
@@ -1519,10 +1631,45 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
1519
1631
|
remove: (id, path) => this.target.remove?.(id, path),
|
|
1520
1632
|
};
|
|
1521
1633
|
|
|
1634
|
+
/** `id`'s label path from its world's root: each step the authored label and its place among
|
|
1635
|
+
* same-labelled siblings, so it holds across line edits and reloads (a rename drops it). */
|
|
1636
|
+
private pathKeyOf(id: string): string {
|
|
1637
|
+
const known = this.pathKeys.get(id);
|
|
1638
|
+
if (known !== undefined) return known;
|
|
1639
|
+
const labelOf = (nodeId: string): string => this.projector.object(nodeId)?.label ?? '';
|
|
1640
|
+
const segments: string[] = [];
|
|
1641
|
+
let current: string | null = id;
|
|
1642
|
+
while (current) {
|
|
1643
|
+
const node = this.projector.node(current);
|
|
1644
|
+
if (!node) break;
|
|
1645
|
+
const siblings = node.parentId
|
|
1646
|
+
? (this.projector.node(node.parentId)?.childIds ?? [])
|
|
1647
|
+
: this.projector.roots().map((root) => root.id);
|
|
1648
|
+
const label = labelOf(current);
|
|
1649
|
+
const place = siblings.filter((sibling) => labelOf(sibling) === label).indexOf(current);
|
|
1650
|
+
segments.unshift(`${label}#${Math.max(0, place)}`);
|
|
1651
|
+
current = node.parentId ?? null;
|
|
1652
|
+
}
|
|
1653
|
+
const key = segments.join('/');
|
|
1654
|
+
this.pathKeys.set(id, key);
|
|
1655
|
+
return key;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
/** The outermost grouped node that contains `id`, or `id` itself when none does. */
|
|
1659
|
+
private groupOf(id: string): string {
|
|
1660
|
+
let group = id;
|
|
1661
|
+
let current: string | null = id;
|
|
1662
|
+
while (current) {
|
|
1663
|
+
if (this.groupedPaths.has(this.pathKeyOf(current))) group = current;
|
|
1664
|
+
current = this.projector.node(current)?.parentId ?? null;
|
|
1665
|
+
}
|
|
1666
|
+
return group;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1522
1669
|
private isPickLocked(id: string): boolean {
|
|
1523
1670
|
let current: string | null = id;
|
|
1524
1671
|
while (current) {
|
|
1525
|
-
if (this.
|
|
1672
|
+
if (this.lockedPaths.has(this.pathKeyOf(current))) return true;
|
|
1526
1673
|
current = this.projector.node(current)?.parentId ?? null;
|
|
1527
1674
|
}
|
|
1528
1675
|
return false;
|
|
@@ -1541,7 +1688,7 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
1541
1688
|
|
|
1542
1689
|
dispose(): void {
|
|
1543
1690
|
this.boxEditSessions.clear();
|
|
1544
|
-
this.
|
|
1691
|
+
this.pathKeys.clear();
|
|
1545
1692
|
for (const object of this.watchedForStructure) {
|
|
1546
1693
|
object.off('childAdded', this.onStructureChanged);
|
|
1547
1694
|
object.off('childRemoved', this.onStructureChanged);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The display objects a canvas world creates, as its create dialog lists them: Pixi's own class
|
|
3
|
+
* tree (every one of them is a Container) and a line on what each is, from Pixi's API reference.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CreatableKind } from '@volter/editor-project/adapter';
|
|
7
|
+
|
|
8
|
+
export const PIXI_CREATABLE_KINDS: readonly CreatableKind[] = [
|
|
9
|
+
{
|
|
10
|
+
kind: 'container',
|
|
11
|
+
label: 'Container',
|
|
12
|
+
description: 'A general-purpose display object that holds children, and transforms and draws them together.',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
kind: 'sprite',
|
|
16
|
+
label: 'Sprite',
|
|
17
|
+
extends: 'container',
|
|
18
|
+
description: 'Draws one texture: an image, or a frame of a spritesheet.',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
kind: 'animatedSprite',
|
|
22
|
+
label: 'AnimatedSprite',
|
|
23
|
+
extends: 'sprite',
|
|
24
|
+
description: 'Plays a list of textures as frames at an animation speed.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
kind: 'tilingSprite',
|
|
28
|
+
label: 'TilingSprite',
|
|
29
|
+
extends: 'container',
|
|
30
|
+
description: 'Repeats one texture across its width and height, scrollable by its tile position.',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
kind: 'nineSliceSprite',
|
|
34
|
+
label: 'NineSliceSprite',
|
|
35
|
+
extends: 'container',
|
|
36
|
+
description: 'Stretches a texture without stretching its corners: the nine-slice panel of a UI.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
kind: 'text',
|
|
40
|
+
label: 'Text',
|
|
41
|
+
extends: 'container',
|
|
42
|
+
description: 'Draws a string with a text style (font, size, fill, wrapping).',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
kind: 'bitmapText',
|
|
46
|
+
label: 'BitmapText',
|
|
47
|
+
extends: 'container',
|
|
48
|
+
description: 'Draws a string from a bitmap font: fast to change every frame.',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
kind: 'htmlText',
|
|
52
|
+
label: 'HTMLText',
|
|
53
|
+
extends: 'container',
|
|
54
|
+
description: 'Draws a string of HTML and CSS markup.',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
kind: 'graphics',
|
|
58
|
+
label: 'Graphics',
|
|
59
|
+
extends: 'container',
|
|
60
|
+
description: 'Draws vector shapes (rectangles, circles, paths) from its draw callback.',
|
|
61
|
+
},
|
|
62
|
+
];
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
* place a fallback could quietly pretend to persist.
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
import type { PhysicsAdapter2D } from '
|
|
31
|
-
import type { HistoryService } from '@volter/editor-
|
|
30
|
+
import type { PhysicsAdapter2D } from '../../runtime/pixi/system-adapters';
|
|
31
|
+
import type { HistoryService } from '@volter/editor-sdk/kit/history/history-service';
|
|
32
32
|
import type { IngestSourcePersistence } from './ingest-source-persistence';
|
|
33
33
|
import type { CanvasWriteTarget } from './pixi-authoring-adapter';
|
|
34
34
|
import { createLiveCanvasWriteTarget } from './pixi-live-write-target';
|
|
@@ -34,8 +34,8 @@ import type {
|
|
|
34
34
|
AuthoringAdapter2D,
|
|
35
35
|
Overlay2D,
|
|
36
36
|
Transform2DValue,
|
|
37
|
-
} from '
|
|
38
|
-
import type { PhysicsAdapter2D } from '
|
|
37
|
+
} from '../../runtime/pixi/authoring';
|
|
38
|
+
import type { PhysicsAdapter2D } from '../../runtime/pixi/system-adapters';
|
|
39
39
|
import type {
|
|
40
40
|
ComponentInstanceApplyResult,
|
|
41
41
|
ComponentInstanceDescription,
|
|
@@ -48,7 +48,7 @@ import type {
|
|
|
48
48
|
TransformEditability,
|
|
49
49
|
} from '@volter/editor-project/adapter';
|
|
50
50
|
import type { Container } from 'pixi.js';
|
|
51
|
-
import type { ChannelValue, CreationSiteLiteralReport } from '@volter/editor-
|
|
51
|
+
import type { ChannelValue, CreationSiteLiteralReport } from '@volter/editor-sdk/kit/creation-site-edit';
|
|
52
52
|
import { creationSiteAnchor, instancesAtSite } from '@volter/editor-sdk/kit/creation-site-registry';
|
|
53
53
|
import { editorConsole } from '@volter/editor-sdk/kit/editor-console';
|
|
54
54
|
import { JsonHistoryResource } from '../history/json-history-resource';
|
|
@@ -750,7 +750,7 @@ export function createLiveCanvasWriteTarget(
|
|
|
750
750
|
bind(context: CanvasWriteContext): void {
|
|
751
751
|
a2d = context.a2d;
|
|
752
752
|
notify = context.notify;
|
|
753
|
-
const history = context.store.projectHistory;
|
|
753
|
+
const history = context.store.shell.projectHistory;
|
|
754
754
|
historyResource = history
|
|
755
755
|
? new JsonHistoryResource({
|
|
756
756
|
history,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The OID half of the canvas authoring surface's IDENTITY axis
|
|
3
|
-
* (`@volter/game
|
|
3
|
+
* (`@volter/editor-game/runtime/pixi/authoring`'s {@link CanvasIdentity}) — what a row is called
|
|
4
4
|
* when the display tree was authored as first-party `@pixi/react` TSX, and the
|
|
5
5
|
* resolution from such an id back to the source tag it addresses.
|
|
6
6
|
*
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
* selection survives one.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
import type { CanvasIdentity } from '
|
|
37
|
+
import type { CanvasIdentity } from '../../runtime/pixi/authoring';
|
|
38
38
|
import type { Container } from 'pixi.js';
|
|
39
|
-
import { occurrenceId } from '@volter/editor-
|
|
39
|
+
import { occurrenceId } from '@volter/editor-threejs/kit/authoring/component-instance-root';
|
|
40
40
|
|
|
41
41
|
const PREFIX = 'pixi';
|
|
42
42
|
|