@3driseai/3drise-viewer 0.1.15 → 0.1.17

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.
Files changed (126) hide show
  1. package/README.md +4 -3
  2. package/dist/components/ChladniGrid.d.ts +15 -0
  3. package/dist/components/ChladniGrid.d.ts.map +1 -0
  4. package/dist/components/ChladniGrid.js +62 -0
  5. package/dist/components/CustomPrimitive.d.ts +2 -0
  6. package/dist/components/CustomPrimitive.d.ts.map +1 -1
  7. package/dist/components/CustomPrimitive.js +10 -16
  8. package/dist/components/EffectsGenerator.d.ts.map +1 -1
  9. package/dist/components/EffectsGenerator.js +26 -2
  10. package/dist/components/FlowNetGrid.d.ts +15 -0
  11. package/dist/components/FlowNetGrid.d.ts.map +1 -0
  12. package/dist/components/FlowNetGrid.js +61 -0
  13. package/dist/components/GirihGrid.d.ts +15 -0
  14. package/dist/components/GirihGrid.d.ts.map +1 -0
  15. package/dist/components/GirihGrid.js +62 -0
  16. package/dist/components/GravWaveGrid.d.ts +15 -0
  17. package/dist/components/GravWaveGrid.d.ts.map +1 -0
  18. package/dist/components/GravWaveGrid.js +62 -0
  19. package/dist/components/GridsGenerator.d.ts +2 -2
  20. package/dist/components/GridsGenerator.d.ts.map +1 -1
  21. package/dist/components/GridsGenerator.js +21 -0
  22. package/dist/components/HyperbolicGrid.d.ts +15 -0
  23. package/dist/components/HyperbolicGrid.d.ts.map +1 -0
  24. package/dist/components/HyperbolicGrid.js +61 -0
  25. package/dist/components/LayoutInstances.d.ts +13 -0
  26. package/dist/components/LayoutInstances.d.ts.map +1 -0
  27. package/dist/components/LayoutInstances.js +39 -0
  28. package/dist/components/ModelBoundary.d.ts +28 -0
  29. package/dist/components/ModelBoundary.d.ts.map +1 -0
  30. package/dist/components/ModelBoundary.js +25 -0
  31. package/dist/components/ModelPrimitive.d.ts +40 -0
  32. package/dist/components/ModelPrimitive.d.ts.map +1 -0
  33. package/dist/components/ModelPrimitive.js +52 -0
  34. package/dist/components/ObjectNode.d.ts.map +1 -1
  35. package/dist/components/ObjectNode.js +36 -8
  36. package/dist/components/ObjectNodeApi.d.ts.map +1 -1
  37. package/dist/components/ObjectNodeApi.js +6 -1
  38. package/dist/components/ProjectLoader.d.ts.map +1 -1
  39. package/dist/components/ProjectLoader.js +1 -0
  40. package/dist/components/ReactionDiffusionGrid.d.ts +15 -0
  41. package/dist/components/ReactionDiffusionGrid.d.ts.map +1 -0
  42. package/dist/components/ReactionDiffusionGrid.js +376 -0
  43. package/dist/components/WallpaperGrid.d.ts +15 -0
  44. package/dist/components/WallpaperGrid.d.ts.map +1 -0
  45. package/dist/components/WallpaperGrid.js +555 -0
  46. package/dist/components/cursor/CursorController.d.ts.map +1 -1
  47. package/dist/components/effects/BlackHoleEffect.d.ts +3 -3
  48. package/dist/components/effects/BlackHoleEffect.d.ts.map +1 -1
  49. package/dist/components/effects/BlackHoleEffect.js +293 -163
  50. package/dist/components/effects/DandelionSeedsEffect.d.ts +13 -0
  51. package/dist/components/effects/DandelionSeedsEffect.d.ts.map +1 -0
  52. package/dist/components/effects/DandelionSeedsEffect.js +510 -0
  53. package/dist/components/effects/FallingLeavesEffect.d.ts +13 -0
  54. package/dist/components/effects/FallingLeavesEffect.d.ts.map +1 -0
  55. package/dist/components/effects/FallingLeavesEffect.js +446 -0
  56. package/dist/components/effects/FieldLinesEffect.d.ts +11 -0
  57. package/dist/components/effects/FieldLinesEffect.d.ts.map +1 -0
  58. package/dist/components/effects/FieldLinesEffect.js +260 -0
  59. package/dist/components/effects/FireflySwarmEffect.d.ts +13 -0
  60. package/dist/components/effects/FireflySwarmEffect.d.ts.map +1 -0
  61. package/dist/components/effects/FireflySwarmEffect.js +408 -0
  62. package/dist/components/effects/GalaxyEffect.d.ts +13 -0
  63. package/dist/components/effects/GalaxyEffect.d.ts.map +1 -0
  64. package/dist/components/effects/GalaxyEffect.js +374 -0
  65. package/dist/components/effects/HarmonicShellEffect.d.ts +11 -0
  66. package/dist/components/effects/HarmonicShellEffect.d.ts.map +1 -0
  67. package/dist/components/effects/HarmonicShellEffect.js +209 -0
  68. package/dist/components/effects/IceCrystalsEffect.d.ts +3 -3
  69. package/dist/components/effects/IceCrystalsEffect.d.ts.map +1 -1
  70. package/dist/components/effects/IceCrystalsEffect.js +286 -166
  71. package/dist/components/effects/IceHaloEffect.d.ts +13 -0
  72. package/dist/components/effects/IceHaloEffect.d.ts.map +1 -0
  73. package/dist/components/effects/IceHaloEffect.js +306 -0
  74. package/dist/components/effects/LichtenbergEffect.d.ts +13 -0
  75. package/dist/components/effects/LichtenbergEffect.d.ts.map +1 -0
  76. package/dist/components/effects/LichtenbergEffect.js +517 -0
  77. package/dist/components/effects/MoleculesEffect.d.ts +3 -3
  78. package/dist/components/effects/MoleculesEffect.d.ts.map +1 -1
  79. package/dist/components/effects/MoleculesEffect.js +322 -164
  80. package/dist/components/effects/MurmurationEffect.d.ts +13 -0
  81. package/dist/components/effects/MurmurationEffect.d.ts.map +1 -0
  82. package/dist/components/effects/MurmurationEffect.js +539 -0
  83. package/dist/components/effects/PlanetaryRingsEffect.d.ts +13 -0
  84. package/dist/components/effects/PlanetaryRingsEffect.d.ts.map +1 -0
  85. package/dist/components/effects/PlanetaryRingsEffect.js +458 -0
  86. package/dist/components/effects/PortalEffect.d.ts +3 -3
  87. package/dist/components/effects/PortalEffect.d.ts.map +1 -1
  88. package/dist/components/effects/PortalEffect.js +240 -163
  89. package/dist/components/effects/ShockwaveEffect.d.ts +5 -3
  90. package/dist/components/effects/ShockwaveEffect.d.ts.map +1 -1
  91. package/dist/components/effects/ShockwaveEffect.js +538 -142
  92. package/dist/components/effects/SmokePlumeEffect.d.ts +3 -3
  93. package/dist/components/effects/SmokePlumeEffect.d.ts.map +1 -1
  94. package/dist/components/effects/SmokePlumeEffect.js +180 -116
  95. package/dist/components/effects/SmokeRingEffect.d.ts +3 -3
  96. package/dist/components/effects/SmokeRingEffect.d.ts.map +1 -1
  97. package/dist/components/effects/SmokeRingEffect.js +260 -135
  98. package/dist/components/effects/StrangeAttractorEffect.d.ts +11 -0
  99. package/dist/components/effects/StrangeAttractorEffect.d.ts.map +1 -0
  100. package/dist/components/effects/StrangeAttractorEffect.js +229 -0
  101. package/dist/components/effects/VolumetricFogEffect.d.ts +5 -3
  102. package/dist/components/effects/VolumetricFogEffect.d.ts.map +1 -1
  103. package/dist/components/effects/VolumetricFogEffect.js +211 -93
  104. package/dist/components/effects/WaterCausticsEffect.d.ts +13 -0
  105. package/dist/components/effects/WaterCausticsEffect.d.ts.map +1 -0
  106. package/dist/components/effects/WaterCausticsEffect.js +308 -0
  107. package/dist/components/effects/index.d.ts +9 -0
  108. package/dist/components/effects/index.d.ts.map +1 -1
  109. package/dist/components/effects/index.js +9 -0
  110. package/dist/components/index.d.ts +18 -0
  111. package/dist/components/index.d.ts.map +1 -1
  112. package/dist/components/index.js +10 -0
  113. package/dist/constants.d.ts +1 -1
  114. package/dist/constants.d.ts.map +1 -1
  115. package/dist/constants.js +1 -1
  116. package/dist/hooks/index.d.ts +1 -0
  117. package/dist/hooks/index.d.ts.map +1 -1
  118. package/dist/hooks/index.js +1 -0
  119. package/dist/hooks/useModelObject.d.ts +30 -0
  120. package/dist/hooks/useModelObject.d.ts.map +1 -0
  121. package/dist/hooks/useModelObject.js +86 -0
  122. package/dist/types/gridConfigs.d.ts +98 -0
  123. package/dist/types/gridConfigs.d.ts.map +1 -1
  124. package/dist/types/index.d.ts +1 -1
  125. package/dist/types/index.d.ts.map +1 -1
  126. package/package.json +2 -2
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useRef } from 'react';
3
+ import * as THREE from 'three';
4
+ import { useObjectReady } from '../hooks/useObjectReady.js';
5
+ import { useGridSurface } from '../hooks/useGridSurface.js';
6
+ import { HyperbolicGridMaterial } from '@3driseai/3drise-core';
7
+ // Side-effect import registers <hyperbolicGridMaterial> via extend().
8
+ void HyperbolicGridMaterial;
9
+ /** HyperbolicGridConfig defaults. Must match the catalog entry in grid-defaults.json exactly. */
10
+ export const defaultHyperbolicGridConfig = {
11
+ cellSize: 22,
12
+ size: 50,
13
+ flat: true,
14
+ segments: undefined,
15
+ lineMode: 1,
16
+ cellMode: 2,
17
+ lineWidth: 1.4,
18
+ sectionSize: 3,
19
+ sectionWidth: 1,
20
+ animSpeed: 1,
21
+ animScale: 1.2,
22
+ animIntensity: 0.7,
23
+ fadeDistance: undefined,
24
+ fadeStrength: 0.2,
25
+ displace: 0,
26
+ sides: 7,
27
+ valence: 3,
28
+ drift: 0.5,
29
+ lineColor: '#c9a4ff',
30
+ sectionColor: '#ffd9f2',
31
+ cellColor: '#2a1446',
32
+ glowColor: '#b07cff',
33
+ bgColor: '#07030c',
34
+ bgOpacity: 0,
35
+ followMouse: false,
36
+ reveal: 1,
37
+ revealDuration: 0,
38
+ };
39
+ /**
40
+ * An Escher-style {p,q} tiling of the Poincare disk. A slow Mobius translation glides the tiling through itself, and the tiles shrink without end toward the rim.
41
+ */
42
+ export function HyperbolicGrid({ config = defaultHyperbolicGridConfig, groupProps, onObjectReady, onObjectDispose }) {
43
+ const matRef = useRef(null);
44
+ const rootRef = useRef(null);
45
+ const meshRef = useRef(null);
46
+ useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
47
+ const { cellSize, size, segments, flat, lineMode, cellMode, lineWidth, sectionSize, sectionWidth, animSpeed, animScale, animIntensity, fadeDistance, fadeStrength, displace, sides, valence, drift, lineColor, sectionColor, cellColor, glowColor, bgColor, bgOpacity, followMouse, reveal, revealDuration, } = config;
48
+ // the ripple is fine-grained relative to the disk
49
+ const segs = segments ?? (displace > 0 ? Math.min(512, Math.max(96, Math.ceil(size / Math.max(cellSize, 1e-4)) * 64)) : 1);
50
+ const fade = fadeDistance ?? size * 0.5;
51
+ const colors = useMemo(() => ({
52
+ line: new THREE.Color(lineColor),
53
+ section: new THREE.Color(sectionColor),
54
+ cell: new THREE.Color(cellColor),
55
+ glow: new THREE.Color(glowColor),
56
+ bg: new THREE.Color(bgColor),
57
+ }), [lineColor, sectionColor, cellColor, glowColor, bgColor]);
58
+ useGridSurface(matRef, meshRef, { followMouse, reveal, revealDuration });
59
+ return (_jsx("group", { ref: rootRef, ...groupProps, children: _jsxs("mesh", { ref: meshRef, rotation: flat ? [-Math.PI / 2, 0, 0] : [0, 0, 0], children: [_jsx("planeGeometry", { args: [size, size, segs, segs] }), _jsx("hyperbolicGridMaterial", { ref: matRef, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, side: THREE.DoubleSide, u_cellSize: cellSize, u_lineWidth: lineWidth, u_sectionSize: sectionSize, u_sectionWidth: sectionWidth, u_lineMode: lineMode, u_cellMode: cellMode, u_animSpeed: animSpeed, u_animScale: animScale, u_animIntensity: animIntensity, u_fadeDistance: fade, u_fadeStrength: fadeStrength, u_displace: displace, u_sides: sides, u_valence: valence, u_drift: drift, u_lineColor: colors.line, u_sectionColor: colors.section, u_cellColor: colors.cell, u_glowColor: colors.glow, u_bgColor: colors.bg, u_bgOpacity: bgOpacity }, HyperbolicGridMaterial.key)] }) }));
60
+ }
61
+ export default HyperbolicGrid;
@@ -0,0 +1,13 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Object3D } from 'three';
3
+ import type { ThreeEvent } from '@react-three/fiber';
4
+ import type { CreatedObjectSettings, ObjectLayoutSettings } from '@3driseai/3drise-core';
5
+ export interface LayoutInstancesProps {
6
+ createdObject: CreatedObjectSettings;
7
+ layout: ObjectLayoutSettings;
8
+ render: (slot: number) => ReactNode;
9
+ handleObjectClick?: (createdObject: CreatedObjectSettings, ref: Object3D, event: ThreeEvent<MouseEvent>) => void;
10
+ }
11
+ declare const LayoutInstances: import("react").MemoExoticComponent<({ createdObject, layout, render, handleObjectClick }: LayoutInstancesProps) => import("react").JSX.Element>;
12
+ export default LayoutInstances;
13
+ //# sourceMappingURL=LayoutInstances.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LayoutInstances.d.ts","sourceRoot":"","sources":["../../src/components/LayoutInstances.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAS,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,qBAAqB,EAAc,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAErG,MAAM,WAAW,oBAAoB;IACjC,aAAa,EAAE,qBAAqB,CAAC;IACrC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;IACpC,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CACpH;AAmCD,QAAA,MAAM,eAAe,6FAA+D,oBAAoB,iCAmBtG,CAAC;AAGH,eAAe,eAAe,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ // =============================================================================
3
+ // LayoutInstances — repeat one scene object across a gallery layout
4
+ // =============================================================================
5
+ //
6
+ // ObjectNode hands this the object's own renderer; each slot is a plain group at the
7
+ // transform LayoutCreator computed for it, and the renderer output goes inside.
8
+ //
9
+ // Rules (see the project doc claude/object-layout-plan.md):
10
+ // - Only slot 0 reports readiness. The render callback receives the slot index and
11
+ // ObjectNode passes no ready callbacks for the others, so one object still reports
12
+ // once and ObjectsRenderer's aggregate count stays right.
13
+ // - Slot groups never set userData.id, so findById(id) keeps returning the
14
+ // CreatedObject group that owns the transform.
15
+ // - Clicks are per slot: zoom / glow / vanish target the copy that was clicked, the
16
+ // same way gallery frames behave.
17
+ // - Motion stays on the CreatedObject group, so the whole layout animates as one.
18
+ import { memo, useMemo, useRef } from 'react';
19
+ import { generateLayoutSlots } from '@3driseai/3drise-core';
20
+ const LayoutSlotGroup = ({ slot, index, createdObject, handleObjectClick, children }) => {
21
+ const ref = useRef(null);
22
+ const onClick = handleObjectClick
23
+ ? (e) => {
24
+ if (!ref.current)
25
+ return;
26
+ e.stopPropagation();
27
+ handleObjectClick(createdObject, ref.current, e);
28
+ }
29
+ : undefined;
30
+ return (_jsx("group", { ref: ref, name: `layout-slot-${index}`, position: slot.position, rotation: slot.rotation ?? [0, 0, 0], scale: slot.scale ?? [1, 1, 1], userData: { layoutSlot: index }, onClick: onClick, children: children }));
31
+ };
32
+ const LayoutInstances = memo(({ createdObject, layout, render, handleObjectClick }) => {
33
+ // ObjectNode normalises the block on every render, so key on its contents.
34
+ const layoutKey = JSON.stringify(layout);
35
+ const slots = useMemo(() => generateLayoutSlots(layout.count, layout), [layoutKey]);
36
+ return (_jsx(_Fragment, { children: slots.map((slot, index) => (_jsx(LayoutSlotGroup, { slot: slot, index: index, createdObject: createdObject, handleObjectClick: handleObjectClick, children: render(index) }, index))) }));
37
+ });
38
+ LayoutInstances.displayName = 'LayoutInstances';
39
+ export default LayoutInstances;
@@ -0,0 +1,28 @@
1
+ import { Component } from 'react';
2
+ import type { ErrorInfo, ReactNode } from 'react';
3
+ interface ModelBoundaryProps {
4
+ children: ReactNode;
5
+ /** Rendered while the model's GLTF is in flight. Null keeps the scene from jumping. */
6
+ fallback?: ReactNode;
7
+ /** Identifies the offending object in the console when a load fails. */
8
+ label?: string;
9
+ }
10
+ interface ModelBoundaryState {
11
+ failed: boolean;
12
+ }
13
+ /**
14
+ * Suspense + error boundary around a single model leaf.
15
+ *
16
+ * Both halves are required. The cache re-throws a rejected load on every subsequent read,
17
+ * and a throw that reaches a bare `<Suspense>` unmounts everything under it — one model
18
+ * behind a dead URL would blank the rest of the scene. Here a failed model renders nothing
19
+ * and its siblings carry on.
20
+ */
21
+ declare class ModelBoundary extends Component<ModelBoundaryProps, ModelBoundaryState> {
22
+ state: ModelBoundaryState;
23
+ static getDerivedStateFromError(): ModelBoundaryState;
24
+ componentDidCatch(error: Error, info: ErrorInfo): void;
25
+ render(): ReactNode;
26
+ }
27
+ export default ModelBoundary;
28
+ //# sourceMappingURL=ModelBoundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelBoundary.d.ts","sourceRoot":"","sources":["../../src/components/ModelBoundary.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,SAAS,EAAY,MAAM,OAAO,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElD,UAAU,kBAAkB;IACxB,QAAQ,EAAE,SAAS,CAAC;IACpB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,kBAAkB;IACxB,MAAM,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,cAAM,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IACzE,KAAK,EAAE,kBAAkB,CAAqB;IAE9C,MAAM,CAAC,wBAAwB,IAAI,kBAAkB;IAIrD,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAQtD,MAAM,IAAI,SAAS;CAItB;AAED,eAAe,aAAa,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Component, Suspense } from 'react';
3
+ /**
4
+ * Suspense + error boundary around a single model leaf.
5
+ *
6
+ * Both halves are required. The cache re-throws a rejected load on every subsequent read,
7
+ * and a throw that reaches a bare `<Suspense>` unmounts everything under it — one model
8
+ * behind a dead URL would blank the rest of the scene. Here a failed model renders nothing
9
+ * and its siblings carry on.
10
+ */
11
+ class ModelBoundary extends Component {
12
+ state = { failed: false };
13
+ static getDerivedStateFromError() {
14
+ return { failed: true };
15
+ }
16
+ componentDidCatch(error, info) {
17
+ console.error(`[ModelBoundary] Model failed to load${this.props.label ? `: ${this.props.label}` : ''}`, error, info.componentStack);
18
+ }
19
+ render() {
20
+ if (this.state.failed)
21
+ return null;
22
+ return _jsx(Suspense, { fallback: this.props.fallback ?? null, children: this.props.children });
23
+ }
24
+ }
25
+ export default ModelBoundary;
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import type { ReactNode } from 'react';
3
+ import type { CreatedObjectSettings } from '@3driseai/3drise-core';
4
+ import type { ObjectReadyProps } from '../types/objectReady';
5
+ interface ModelPrimitiveProps extends ObjectReadyProps {
6
+ createdObject: CreatedObjectSettings;
7
+ /** Layout slot. Slot 0 is the object itself; the rest are unregistered clones. */
8
+ slot?: number;
9
+ children?: ReactNode;
10
+ }
11
+ /**
12
+ * Mounts the Object3D behind a `type: 'model'` scene object, and hands it to the host
13
+ * application.
14
+ *
15
+ * Split out of CustomPrimitive because this is the component that suspends: it must sit
16
+ * under its own boundary so one slow or broken model does not hold up its siblings.
17
+ *
18
+ * ## Readiness
19
+ *
20
+ * `onObjectReady` fires here, with the loaded model root - that Object3D is the whole point
21
+ * of the object API, so it has to arrive on both paths: preloaded (mounts in the same
22
+ * commit as its parent) and suspended (mounts whenever the GLTF lands, which may be several
23
+ * frames later).
24
+ *
25
+ * CustomPrimitive cannot report it any more. When this component suspends, CustomPrimitive
26
+ * commits first with its ref still empty, its layout effect finds nothing to report, and it
27
+ * never re-renders once the asset resolves. Its `meshRef` now stays null for a model, so its
28
+ * own call is a no-op rather than a second, conflicting report.
29
+ *
30
+ * The report is driven by a callback ref rather than a layout effect: R3F runs it the moment
31
+ * it attaches the object and again with null when it detaches, so the pair fires exactly on
32
+ * attach/detach no matter how many renders the suspension cost.
33
+ *
34
+ * `dispose={null}` is not optional. Geometry and textures here are shared with the cache
35
+ * master and every other instance of the same URL; letting R3F free them on unmount breaks
36
+ * all of them.
37
+ */
38
+ declare const ModelPrimitive: React.FC<ModelPrimitiveProps>;
39
+ export default ModelPrimitive;
40
+ //# sourceMappingURL=ModelPrimitive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelPrimitive.d.ts","sourceRoot":"","sources":["../../src/components/ModelPrimitive.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,UAAU,mBAAoB,SAAQ,gBAAgB;IAClD,aAAa,EAAE,qBAAqB,CAAC;IACrC,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA8BhD,CAAC;AAIH,eAAe,cAAc,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { memo, useCallback, useRef } from 'react';
3
+ import { useModelObject } from '../hooks/useModelObject.js';
4
+ import { useObjectReadyReporter } from '../hooks/useObjectReady.js';
5
+ /**
6
+ * Mounts the Object3D behind a `type: 'model'` scene object, and hands it to the host
7
+ * application.
8
+ *
9
+ * Split out of CustomPrimitive because this is the component that suspends: it must sit
10
+ * under its own boundary so one slow or broken model does not hold up its siblings.
11
+ *
12
+ * ## Readiness
13
+ *
14
+ * `onObjectReady` fires here, with the loaded model root - that Object3D is the whole point
15
+ * of the object API, so it has to arrive on both paths: preloaded (mounts in the same
16
+ * commit as its parent) and suspended (mounts whenever the GLTF lands, which may be several
17
+ * frames later).
18
+ *
19
+ * CustomPrimitive cannot report it any more. When this component suspends, CustomPrimitive
20
+ * commits first with its ref still empty, its layout effect finds nothing to report, and it
21
+ * never re-renders once the asset resolves. Its `meshRef` now stays null for a model, so its
22
+ * own call is a no-op rather than a second, conflicting report.
23
+ *
24
+ * The report is driven by a callback ref rather than a layout effect: R3F runs it the moment
25
+ * it attaches the object and again with null when it detaches, so the pair fires exactly on
26
+ * attach/detach no matter how many renders the suspension cost.
27
+ *
28
+ * `dispose={null}` is not optional. Geometry and textures here are shared with the cache
29
+ * master and every other instance of the same URL; letting R3F free them on unmount breaks
30
+ * all of them.
31
+ */
32
+ const ModelPrimitive = memo(({ createdObject, slot = 0, onObjectReady, onObjectDispose, children }) => {
33
+ const attachedRef = useRef(null);
34
+ const object = useModelObject(createdObject, slot);
35
+ const { report, reportDisposed } = useObjectReadyReporter(createdObject, onObjectReady, onObjectDispose);
36
+ const setObjectRef = useCallback((instance) => {
37
+ if (instance) {
38
+ attachedRef.current = instance;
39
+ report(instance);
40
+ return;
41
+ }
42
+ if (attachedRef.current) {
43
+ reportDisposed(attachedRef.current);
44
+ attachedRef.current = null;
45
+ }
46
+ }, [report, reportDisposed]);
47
+ if (!object)
48
+ return null;
49
+ return (_jsx("primitive", { ref: setObjectRef, name: createdObject.name, object: object, dispose: null, children: children }, `${createdObject.id}:${slot}`));
50
+ });
51
+ ModelPrimitive.displayName = 'ModelPrimitive';
52
+ export default ModelPrimitive;
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectNode.d.ts","sourceRoot":"","sources":["../../src/components/ObjectNode.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAe,qBAAqB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,KAAK,eAAe,GAAG,gBAAgB,GAAG;IACtC,IAAI,EAAE,cAAc,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CAC7G,CAAC;AAgBF,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAqHzC,CAAC;AACF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ObjectNode.d.ts","sourceRoot":"","sources":["../../src/components/ObjectNode.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAe,qBAAqB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAiBrD,KAAK,eAAe,GAAG,gBAAgB,GAAG;IACtC,IAAI,EAAE,cAAc,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CAC7G,CAAC;AAgBF,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAiIzC,CAAC;AACF,eAAe,UAAU,CAAC"}
@@ -15,7 +15,23 @@ import Galaxy from "./Galaxy.js";
15
15
  import GridGenerator from "./GridsGenerator.js";
16
16
  import OceanBuilder from "./OceanBuilder.js";
17
17
  import LandscapeObject from "./LandscapeObject.js";
18
+ import LayoutInstances from "./LayoutInstances.js";
18
19
  import { SELF_MANAGED_MATERIAL } from "../constants.js";
20
+ import { LAYOUT_CAPABLE_TYPES, getObjectLayoutSupport, normalizeObjectLayout } from "@3driseai/3drise-core";
21
+ // Slots after the first render with no ready callbacks: one object reports once.
22
+ const NO_READY = {};
23
+ // A point light's shadow is six extra renders; only slot 0 keeps it. Cached per settings
24
+ // object so the copies do not re-run their shadow effect on every render.
25
+ const noShadowCache = new WeakMap();
26
+ const withoutShadow = (obj) => {
27
+ let copy = noShadowCache.get(obj);
28
+ if (!copy) {
29
+ const config = (obj.config ?? {});
30
+ copy = { ...obj, config: { ...config, shadow: { ...config.shadow, enabled: false } } };
31
+ noShadowCache.set(obj, copy);
32
+ }
33
+ return copy;
34
+ };
19
35
  // const EffectsLayer: React.FC<{ effects: CreatedObjectSettings['effects'] }> = ({ effects }) =>
20
36
  // effects?.length ? (
21
37
  // <>
@@ -40,7 +56,9 @@ const ObjectNode = ({ node, onObjectReady, onObjectDispose, handleObjectClick })
40
56
  // Specialized renderers for each object type
41
57
  const renderers = useMemo(() => ({
42
58
  group: () => null,
43
- mesh: (obj, onObjectReady) => (_jsx(CustomPrimitive, { createdObject: obj, onObjectReady: onObjectReady, onObjectDispose: onObjectDispose, handleObjectClick: handleObjectClick }, obj.id)),
59
+ // mesh / model / effect / light take a layout slot as the third argument
60
+ // (see LayoutInstances); slot 0 is also the no-layout case.
61
+ mesh: (obj, _onObjectReady, slot = 0) => (_jsx(CustomPrimitive, { createdObject: obj, ...(slot === 0 ? { onObjectReady, onObjectDispose } : NO_READY), handleObjectClick: handleObjectClick }, obj.id)),
44
62
  // water: (obj: CreatedObjectSettings, cameraSettings: CameraState, onObjectReady?: (object: CreatedObjectSettings, ref: Object3D) => void) => (
45
63
  // <Ocean
46
64
  // settings={obj}
@@ -56,7 +74,7 @@ const ObjectNode = ({ node, onObjectReady, onObjectDispose, handleObjectClick })
56
74
  // settings={obj}
57
75
  // />
58
76
  // ),
59
- model: (obj, onObjectReady) => (_jsx(CustomPrimitive, { createdObject: obj, onObjectReady: onObjectReady, onObjectDispose: onObjectDispose, handleObjectClick: handleObjectClick }, obj.id)),
77
+ model: (obj, _onObjectReady, slot = 0) => (_jsx(CustomPrimitive, { createdObject: obj, slot: slot, ...(slot === 0 ? { onObjectReady, onObjectDispose } : NO_READY), handleObjectClick: handleObjectClick }, obj.id)),
60
78
  // custom_primitive: (obj: CreatedObjectSettings, cameraSettings: CameraState, onObjectReady?: (object: CreatedObjectSettings, ref: Object3D) => void) => (
61
79
  // <CustomPrimitive
62
80
  // key={obj.id}
@@ -87,11 +105,11 @@ const ObjectNode = ({ node, onObjectReady, onObjectDispose, handleObjectClick })
87
105
  // LandscapeConnector instead; this one is a diorama, a floating island or
88
106
  // a game tile, and gets its transform from the CreatedObject wrapper.
89
107
  landscape: (obj, onObjectReady) => (_jsx(LandscapeObject, { settings: obj, ...relabel(obj) }, obj.id)),
90
- effect: (obj) => (_jsx(EffectsGenerator, { settings: {
108
+ effect: (obj, _onObjectReady, slot = 0) => (_jsx(EffectsGenerator, { settings: {
91
109
  config: obj.config,
92
- }, ...relabel(obj) }, obj.id)),
110
+ }, ...(slot === 0 ? relabel(obj) : NO_READY) }, obj.id)),
93
111
  gallery: (obj, onObjectReady) => (_jsx(GalleryLayout, { createdObject: obj, handleObjectClick: handleObjectClick, ...relabel(obj) })),
94
- light: (obj, onObjectReady) => (_jsx(LightObject, { createdObject: obj, ...relabel(obj) })),
112
+ light: (obj, _onObjectReady, slot = 0) => (_jsx(LightObject, { createdObject: slot === 0 ? obj : withoutShadow(obj), ...(slot === 0 ? relabel(obj) : NO_READY) })),
95
113
  environment: (obj, onObjectReady) => (_jsx(EnvironmentObject, { createdObject: obj, onObjectReady: onObjectReady, onObjectDispose: onObjectDispose })),
96
114
  space: (obj, onObjectReady) => (_jsx(Galaxy, { createdObject: obj, onObjectReady: onObjectReady, onObjectDispose: onObjectDispose })),
97
115
  // rain: (obj: CreatedObjectSettings, cameraSettings: CameraState, onObjectReady?: (object: CreatedObjectSettings, ref: Object3D) => void) => (
@@ -115,10 +133,20 @@ const ObjectNode = ({ node, onObjectReady, onObjectDispose, handleObjectClick })
115
133
  // />
116
134
  // ),
117
135
  }), [handleObjectClick, onObjectReady, onObjectDispose, relabel]);
118
- const renderObject = useCallback((obj, handleObjectClick) => {
136
+ const renderObject = useCallback((obj, _handleObjectClick) => {
119
137
  const renderer = renderers[obj.type];
120
- return renderer?.(obj, onObjectReady) ?? null;
121
- }, [onObjectReady, renderers]);
138
+ if (!renderer)
139
+ return null;
140
+ // meshSettings.layout: repeat the renderer output across a gallery layout.
141
+ // Absent on every object saved before layouts existed, which renders as before.
142
+ const layout = LAYOUT_CAPABLE_TYPES.has(obj.type) ? normalizeObjectLayout(obj.meshSettings?.layout) : null;
143
+ const support = layout?.enabled ? getObjectLayoutSupport(obj) : null;
144
+ if (layout && support?.allowed) {
145
+ const capped = layout.count > support.maxCount ? { ...layout, count: support.maxCount } : layout;
146
+ return (_jsx(LayoutInstances, { createdObject: obj, layout: capped, handleObjectClick: handleObjectClick, render: (slot) => renderer(obj, onObjectReady, slot) }));
147
+ }
148
+ return renderer(obj, onObjectReady) ?? null;
149
+ }, [onObjectReady, renderers, handleObjectClick]);
122
150
  if (!createdObject) {
123
151
  return null;
124
152
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectNodeApi.d.ts","sourceRoot":"","sources":["../../src/components/ObjectNodeApi.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAe,qBAAqB,EAAkB,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,KAAK,kBAAkB,GAAG,gBAAgB,GAAG;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CAC7G,CAAC;AAGF,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAc/C,CAAC;AACF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"ObjectNodeApi.d.ts","sourceRoot":"","sources":["../../src/components/ObjectNodeApi.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAe,qBAAqB,EAAkB,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,KAAK,kBAAkB,GAAG,gBAAgB,GAAG;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CAC7G,CAAC;AAGF,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAmB/C,CAAC;AACF,eAAe,aAAa,CAAC"}
@@ -9,11 +9,16 @@ const ObjectNodeApi = ({ objectId, onObjectReady, onObjectDispose, handleObjectC
9
9
  // too — SceneBuilder is not in this tree to do it.
10
10
  useRegisterScene();
11
11
  const [node, setNode] = useState(null);
12
+ // No asset pre-pass here on purpose. Every model leaf under this tree loads itself
13
+ // through the shared URL cache under its own Suspense boundary (see useModelObject),
14
+ // however deeply it is nested, so this component only has to fetch the tree.
12
15
  useEffect(() => {
13
16
  fetch(`${dataCenterServer}scene-objects/${objectId}/tree`)
14
17
  .then(res => res.ok ? res.json() : Promise.reject(res.status))
15
18
  .then(({ tree }) => setNode(tree))
16
- .catch(() => { });
19
+ .catch(error => {
20
+ console.error(`[ObjectNodeApi] Failed to load object tree ${objectId}:`, error);
21
+ });
17
22
  }, [objectId]);
18
23
  if (!node)
19
24
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectLoader.d.ts","sourceRoot":"","sources":["../../src/components/ProjectLoader.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGhF,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,KAAK,YAAY,GAAG,gBAAgB,GAAG;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACnC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IACtG,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IACvE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC,CAAC;AAGF,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAqDzC,CAAC;AACF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"ProjectLoader.d.ts","sourceRoot":"","sources":["../../src/components/ProjectLoader.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGhF,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,KAAK,YAAY,GAAG,gBAAgB,GAAG;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACnC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IACtG,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IACvE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC,CAAC;AAGF,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAsDzC,CAAC;AACF,eAAe,aAAa,CAAC"}
@@ -22,6 +22,7 @@ const ProjectLoader = ({ projectId, onObjectClick, onObjectHover, onElementClick
22
22
  "Content-Type": "application/json",
23
23
  },
24
24
  });
25
+ console.log(response);
25
26
  if (!response.ok) {
26
27
  if (response.status === 404) {
27
28
  throw new Error("Project not found");
@@ -0,0 +1,15 @@
1
+ import type { ThreeElements } from '@react-three/fiber';
2
+ import type { ObjectReadyProps } from '../types/objectReady';
3
+ import type { ReactionDiffusionGridConfig } from '../types/gridConfigs';
4
+ export interface ReactionDiffusionGridProps extends ObjectReadyProps {
5
+ config?: ReactionDiffusionGridConfig;
6
+ groupProps?: ThreeElements['group'];
7
+ }
8
+ /** ReactionDiffusionGridConfig defaults. Must match the catalog entry in grid-defaults.json exactly. */
9
+ export declare const defaultReactionDiffusionGridConfig: ReactionDiffusionGridConfig;
10
+ /**
11
+ * A Gray-Scott reaction-diffusion field simulated on the GPU and sampled by the plane: coral, labyrinths and dividing spots grow, merge and keep evolving, wrapped seamlessly as a torus.
12
+ */
13
+ export declare function ReactionDiffusionGrid({ config, groupProps, onObjectReady, onObjectDispose }: ReactionDiffusionGridProps): import("react").JSX.Element;
14
+ export default ReactionDiffusionGrid;
15
+ //# sourceMappingURL=ReactionDiffusionGrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactionDiffusionGrid.d.ts","sourceRoot":"","sources":["../../src/components/ReactionDiffusionGrid.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAGxE,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAChE,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,UAAU,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CACvC;AAKD,wGAAwG;AACxG,eAAO,MAAM,kCAAkC,EAAE,2BA6BhD,CAAC;AAuTF;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,MAA2C,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,EAAE,0BAA0B,+BAgF5J;AAED,eAAe,qBAAqB,CAAC"}