Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,45 @@
- Global Vitest setup mocks were removed; tests now mock dependencies locally per file
- Shared test mock modules like `src/test/mocks/three.ts` and `src/test/mocks/three-spritetext.ts` were removed; tests should inline only the mocks they actually need
- Old global-mock cleanup can leave behind no-op local shims like `vi.mock('three', () => importActual('three'))`; remove them when a test does not override Three behavior
- After the WebGPU import migration, tests must mock `three/webgpu` when the production module imports from `three/webgpu`; mocking `three` does not affect those modules
- `TransformTool` must add and traverse `TransformControls.getHelper()`, not the `TransformControls` instance itself, because current Three typings and runtime treat the control as non-`Object3D`
- Local mocks for `three/examples/jsm/*` should use the exact runtime specifier including the `.js` suffix when the source import does
- `ARQuickLook` tests must mock `@shopware-ag/dive/assetloader` and `@shopware-ag/dive/assetexporter` in addition to `AssetConverter`, because `new AssetLoader()` and `new AssetExporter()` are evaluated before the mocked `AssetConverter` constructor runs
- `DIVEGizmo` tests should mock child gizmo classes as real `Object3D` instances with spied methods to avoid `THREE.Object3D.add` warnings from plain-object stand-ins
- `DIVEPrimitive` tests are more stable with real `Box3` plus per-test spies on `Box3.prototype`/`Raycaster`, instead of mocking the full `three` module surface
- `OrientationDisplayAxes` tests should locally stub `three-spritetext` because jsdom does not implement the canvas text context that the real package needs
- `DIVERoot` should detach both legacy scene-level `TransformControls` objects and modern `TransformControlsRoot.controls` helper roots when cleaning up transform controls
- `DIVERoot` POV update/delete coverage requires manually seeding a matching `Object3D` in tests because `addSceneObject` intentionally skips creating POV scene nodes
- Plugins live in `src/plugins/<name>/` and are auto-discovered by looking for `index.ts` in subdirectories
- Plugins are exported as subpath exports: `@shopware-ag/dive/<plugin-name>` (e.g. `@shopware-ag/dive/shader`, `@shopware-ag/dive/state`)
- The shader plugin (`src/plugins/shader/`) exports `DIVEShaderMaterial` (extends three.js `ShaderMaterial`) and `DIVEShaderLib`
- The shader plugin (`src/plugins/shader/`) now exposes node-based building blocks like `GridNode` and `GridNodeUniforms`; legacy `DIVEShaderLib`/`DIVEShaderMaterial` shader-lib wrappers are being removed
- `DIVEGrid` custom shader code must use TSL/node materials for WebGPU; plain `ShaderMaterial` triggers `THREE.NodeMaterial: Material "ShaderMaterial" is not compatible`
- `DIVEGrid` owns its `MeshBasicNodeMaterial` setup and creates its grid uniform nodes locally; `GridNode` only provides the TSL output-node implementation
- `DIVEGrid` component uses the shader plugin; it is imported transitively via `Scene` → `Grid` → `@shopware-ag/dive/shader`
- Shader plugin public docs must describe the new node-based API: `GridNode` plus `GridNodeUniforms`; legacy `DIVEShaderLib`/`DIVEShaderMaterial` docs are outdated
- Tests that mock `@shopware-ag/dive/shader` must provide a `GridNode` constructor stub after the shader plugin migration; legacy `DIVEShaderLib`-only mocks break transitive imports
- Most tests do not need to mock `@shopware-ag/dive/shader` at all; after the WebGPU migration the only current direct need is `src/components/grid/__test__/Grid.test.ts`, which asserts `DIVEGrid` constructs `GridNode`
- When partially mocking `three/webgpu`, base the mock on `importOriginal<typeof import('three/webgpu')>()`; using `vi.importActual('three')` drops WebGPU-only exports like `Node` and breaks transitive shader imports
- `DIVEGrid` tests or other `MeshBasicNodeMaterial` mocks must preserve the constructor `outputNode` param because production code passes `new GridNode(uniforms)` directly into material creation
- `GridNode` unit tests are best written with local `three/tsl` and `three/webgpu` mocks plus `vi.hoisted(...)`; plain top-level mock helpers break because `vi.mock(...)` factories are hoisted
- `GridNode` returns the final `vec4(...)` TSL node from its constructor while still naming the underlying base `Node` instance `GridNode`
- In `GridNode` tests, keep a raw mock-uniform object separate from the `GridNodeUniforms` cast; casting too early hides Vitest `.mock` metadata from TypeScript
- `DIVEEnvironment` no longer applies HDR state from the constructor alone; tests should wait for the async HDR load and call `env.init()` before asserting environment/background updates
- `DIVEEnvironment` concurrent-load cleanup is best covered by spying on the private `loadHDRImage` method and resolving overlapping promises out of order; stale textures should be disposed
- `DIVE` tests must mock `mainView.renderer.initialized` and `mainView.renderer.init()` because `DIVE.start()` now guards rendering via renderer initialization
- On the v3 branch, deprecated compatibility APIs should not be kept alive just to satisfy tests; remove the matching legacy test coverage instead of restoring `DIVE.QuickView()`, `engine`, `createView()`, `disposeView()`, `AnimationSystem.animate()`, `Toolbox.useTool()`, `Toolbox.getActiveTool()`, or old environment no-op methods
- `DIVERenderer` tests must mock `three/webgpu` `WebGPURenderer`; old `three` `WebGLRenderer` expectations are outdated
- `DIVERenderer` stale-init behavior after `setCanvas()` is best tested with a deferred first `init()` promise; the old renderer must not trigger a second environment init after the swap
- `MediaCreator` fallback coverage is easiest by overriding test canvas `width`/`height` to `undefined` and `writable: true`, then letting `drawCanvas()` fall back through `clientWidth` to the renderer canvas dimensions
- `MediaCreator.drawCanvas()` must restore the previous WebGPU render target and camera layer mask before awaiting `readRenderTargetPixelsAsync()`; otherwise the live `View.tick()` render can keep drawing into the offscreen target and trigger `WebGPUTextureUtils: Texture already initialized.`
- Demo fixture `/Users/f.frank/Public/Repos/dive-demo/public/model_reverse_animation_order_long_name_blank_name.glb` is used for animation edge cases; it contains a blank clip name, an overlong clip name, and a `Walk` clip that now hard-fails loading via an invalid animation accessor reference
- `yarn build` can still exit successfully while `vite-plugin-dts` reports TypeScript API migration errors, so WebGPU refactors need explicit grep/type-review and not just a green build exit code
- `DIVE.start()` is now a fire-and-forget wrapper around `startAsync()`, so tests that need renderer readiness should await `startAsync()` or a microtask before asserting downstream effects
- `DIVE.dispose()` must dispose the `DIVEClock` before tearing down views/renderers, and `startAsync()` must bail out after late renderer init if the instance was disposed; otherwise demo route switches can leave stale RAF ticks calling `DIVEView.tick()` on dead WebGPU renderers
- Demo views in `/Users/f.frank/Public/Repos/dive-demo/src/views/` that create `QuickView` instances must dispose them in `onUnmounted`; missing route-leave cleanup leaves old WebGPU render loops alive across example switches
- Deprecated `BaseTool` coverage was removed entirely; if `src/plugins/toolbox/src/BaseTool.ts` is gone in a future major, delete the legacy suite instead of recreating the class for tests
- `MediaCreator` screenshot generation is async under WebGPU and uses `RenderTarget` plus `readRenderTargetPixelsAsync`; it no longer swaps `renderer.domElement`
- `DIVEXRLightRoot` currently guards `XREstimatedLight` off under WebGPU and falls back to the existing scene light until a dedicated WebGPU-compatible light-estimation path exists
- Library builds must externalize `three` with a pattern that also matches subpaths like `three/webgpu`, `three/tsl`, and `three/examples/jsm/*`; externalizing only bare `three` bundles a second Three runtime into `build/` and triggers `THREE.WARNING: Multiple instances of Three.js being imported.` in consumers
- State action migrations must use `AnimationSystem.fromTargets(...).play()` and `Toolbox.enableTool()`; lingering `animate()` or `useTool()` calls can still let `yarn build` exit 0 while `vite-plugin-dts` reports TS2339 API drift
- When swapping canvases under WebGPU, `DIVEEnvironment.setRenderer()` must run before disposing the previous `WebGPURenderer`; disposing the old renderer first can crash `PMREMGenerator.dispose()` inside Three's `NodeManager.delete` with `usedTimes` access errors
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@shopware-ag/dive",
"version": "2.3.9",
"version": "3.0.0",
"description": "Shopware Spatial Framework",
"type": "module",
"main": "build/dive.cjs",
Expand Down Expand Up @@ -107,7 +107,7 @@
"> 1%, not dead, not ie 11, not op_mini all"
],
"dependencies": {
"three": "^0.163.0",
"three": "^0.183.0",
"@tweenjs/tween.js": "^25.0.0",
"three-spritetext": "^1.8.2",
"lodash": "^4.17.21",
Expand All @@ -119,7 +119,7 @@
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.12",
"@types/node": "^20.12.7",
"@types/three": "^0.163.0",
"@types/three": "^0.183.0",
"@vitest/coverage-v8": "3.1.2",
"acorn": "^8.14.1",
"acorn-walk": "^8.3.4",
Expand Down Expand Up @@ -153,10 +153,11 @@
"lint": "eslint",
"prettier:check": "prettier --check .",
"prettier:fix": "prettier --write .",
"typecheck": "tsc --noEmit --project tsconfig.json",
"unit": "vitest --run",
"coverage": "vitest --coverage --run",
"docs": "yarn docs:actions",
"docs:actions": "tsx docs/generators/generate-actions-docs.ts",
"ci": "yarn && yarn lint && yarn prettier:check && yarn coverage && yarn build"
"ci": "yarn && yarn lint && yarn prettier:check && yarn typecheck && yarn coverage && yarn build"
}
}
12 changes: 7 additions & 5 deletions scripts/build/vite/vite-plugin-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ interface pluginRegistration {
buildPath: string; // Path in the build output
}

const externalDependencies = [
/^three(?:\/.*)?$/,
'@tweenjs/tween.js',
'three-spritetext',
];

// Function to update package.json exports
function updatePackageJsonExports(registrations: pluginRegistration[]): void {
const packageJsonPath = pathResolve(process.cwd(), 'package.json');
Expand Down Expand Up @@ -142,11 +148,7 @@ export default function pluginBuildPlugin(): Plugin {
exports: 'named',
},
],
external: [
'three',
'@tweenjs/tween.js',
'three-spritetext',
],
external: externalDependencies,
},
},
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/boundingbox/BoundingBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
MeshBasicMaterial,
SphereGeometry,
ColorRepresentation,
} from 'three';
} from 'three/webgpu';
import { DIVENode } from '../node/Node.ts';

/**
Expand Down
2 changes: 1 addition & 1 deletion src/components/boundingbox/__test__/BoundingBox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
MeshBasicMaterial,
BoxGeometry,
SphereGeometry,
} from 'three';
} from 'three/webgpu';

describe('BoundingBox', () => {
let mockObject: Object3D;
Expand Down
2 changes: 1 addition & 1 deletion src/components/floor/Floor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
Mesh,
MeshStandardMaterial,
PlaneGeometry,
} from 'three';
} from 'three/webgpu';
import { PRODUCT_LAYER_MASK } from '../../constants/VisibilityLayerMask.ts';

/**
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/Gizmo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Euler, Object3D, Vector3 } from 'three';
import { Euler, Object3D, Vector3 } from 'three/webgpu';
import { DIVERotateGizmo } from './rotate/RotateGizmo.ts';
import { DIVETranslateGizmo } from './translate/TranslateGizmo.ts';
import { OrbitController } from '@shopware-ag/dive/orbitcontroller';
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/__test__/Gizmo.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import { DIVEGizmo, DIVEGizmoMode } from '../Gizmo.ts';
import { OrbitController } from '@shopware-ag/dive/orbitcontroller';
import { Object3D, Vector3, Euler } from 'three';
import { Object3D, Vector3, Euler } from 'three/webgpu';
import { DIVESelectable } from '../../../interfaces/Selectable.ts';

// Mock the OrbitController
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/handles/AxisHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
MeshBasicMaterial,
Object3D,
Vector3,
} from 'three';
} from 'three/webgpu';
import { UI_LAYER_MASK } from '../../../constants/VisibilityLayerMask.ts';
import { DIVEHoverable } from '../../../interfaces/Hoverable.ts';
import { DIVETranslateGizmo } from '../translate/TranslateGizmo.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/handles/RadialHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Object3D,
TorusGeometry,
Vector3,
} from 'three';
} from 'three/webgpu';
import { UI_LAYER_MASK } from '../../../constants/VisibilityLayerMask.ts';
import { DIVEHoverable } from '../../../interfaces/Hoverable.ts';
import { DraggableEvent } from '@shopware-ag/dive/toolbox';
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/handles/ScaleHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
MeshBasicMaterial,
Object3D,
Vector3,
} from 'three';
} from 'three/webgpu';
import { UI_LAYER_MASK } from '../../../constants/VisibilityLayerMask.ts';
import { DIVEHoverable } from '../../../interfaces/Hoverable.ts';
import { DIVEScaleGizmo } from '../scale/ScaleGizmo.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/handles/__test__/AxisHandle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import { DIVEAxisHandle } from '../AxisHandle.ts';
import { DIVETranslateGizmo } from '../../translate/TranslateGizmo.ts';
import { DraggableEvent } from '@shopware-ag/dive/toolbox';
import { Vector3, Color } from 'three';
import { Vector3, Color } from 'three/webgpu';

// Mock the TranslateGizmo
vi.mock('../../translate/TranslateGizmo', () => ({
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/handles/__test__/RadialHandle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import { DIVERadialHandle } from '../RadialHandle.ts';
import { DIVERotateGizmo } from '../../rotate/RotateGizmo.ts';
import { DraggableEvent } from '@shopware-ag/dive/toolbox';
import { Vector3, Color } from 'three';
import { Vector3, Color } from 'three/webgpu';

// Mock the RotateGizmo
vi.mock('../../rotate/RotateGizmo', () => ({
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/handles/__test__/ScaleHandle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach } from 'vitest';
import { DIVEScaleHandle } from '../ScaleHandle.ts';
import { DIVEScaleGizmo } from '../../scale/ScaleGizmo.ts';
import { DraggableEvent } from '@shopware-ag/dive/toolbox';
import { Vector3 } from 'three';
import { Vector3 } from 'three/webgpu';
import { vi } from 'vitest';

vi.mock('../../../constants/VisibilityLayerMask', () => ({ UI_LAYER_MASK: 1 }));
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/plane/GizmoPlane.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Mesh, MeshBasicMaterial, Object3D, PlaneGeometry } from 'three';
import { Mesh, MeshBasicMaterial, Object3D, PlaneGeometry } from 'three/webgpu';
import { UI_LAYER_MASK } from '../../../constants/VisibilityLayerMask.ts';
import { DIVEGizmoAxis, DIVEGizmoMode } from '../Gizmo.ts';

Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/rotate/RotateGizmo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Euler, Object3D, Vector3 } from 'three';
import { Euler, Object3D, Vector3 } from 'three/webgpu';
import {
AxesColorBlue,
AxesColorGreen,
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/rotate/__test__/RotateGizmo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { OrbitController } from '@shopware-ag/dive/orbitcontroller';
import { DIVERadialHandle } from '../../handles/RadialHandle.ts';
import { DIVEGizmo } from '../../Gizmo.ts';
import { DraggableEvent } from '@shopware-ag/dive/toolbox';
import { Vector3, Euler } from 'three';
import { Vector3, Euler } from 'three/webgpu';
import { DIVEMath } from '../../../../helpers/math/index.ts';

// Mock the OrbitController
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/scale/ScaleGizmo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Object3D, Vector3 } from 'three';
import { Object3D, Vector3 } from 'three/webgpu';
import {
AxesColorBlue,
AxesColorGreen,
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/scale/__test__/ScaleGizmo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { OrbitController } from '@shopware-ag/dive/orbitcontroller';
import { DIVEScaleHandle } from '../../handles/ScaleHandle.ts';
import { DIVEGizmo } from '../../Gizmo.ts';
import { DraggableEvent } from '@shopware-ag/dive/toolbox';
import { Vector3 } from 'three';
import { Vector3 } from 'three/webgpu';

// Mock the OrbitController
vi.mock('@shopware-ag/dive/orbitcontroller', () => ({
Expand Down
2 changes: 1 addition & 1 deletion src/components/gizmo/translate/TranslateGizmo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Object3D, Vector3 } from 'three';
import { Object3D, Vector3 } from 'three/webgpu';
import {
AxesColorBlue,
AxesColorGreen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { OrbitController } from '@shopware-ag/dive/orbitcontroller';
import { DIVEAxisHandle } from '../../handles/AxisHandle.ts';
import { DIVEGizmo } from '../../Gizmo.ts';
import { DraggableEvent } from '@shopware-ag/dive/toolbox';
import { Vector3 } from 'three';
import { Vector3 } from 'three/webgpu';

// Mock the OrbitController
vi.mock('@shopware-ag/dive/orbitcontroller', () => ({
Expand Down
37 changes: 18 additions & 19 deletions src/components/grid/Grid.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
DIVEShaderLib,
DIVEShaderMaterial,
GridShader,
} from '@shopware-ag/dive/shader';
import { GridNode, type GridNodeUniforms } from '@shopware-ag/dive/shader';
import {
GRID_MINOR_LINE_COLOR,
GRID_MAJOR_LINE_COLOR,
Expand All @@ -12,10 +8,11 @@ import {
Color,
DoubleSide,
Mesh,
MeshBasicNodeMaterial,
Object3D,
PlaneGeometry,
ShaderMaterial,
} from 'three';
} from 'three/webgpu';
import { uniform } from 'three/tsl';

const PLANE_SIZE = 50;
const GRID_SIZE = 1;
Expand All @@ -35,7 +32,8 @@ export interface DIVEGridSettings {
*/
export class DIVEGrid extends Object3D {
private _mesh: Mesh;
private _material: ShaderMaterial;
private _material: MeshBasicNodeMaterial;
private _uniforms: GridNodeUniforms;
private _gridSize: number;

constructor(settings?: DIVEGridSettings) {
Expand All @@ -48,18 +46,19 @@ export class DIVEGrid extends Object3D {
const geometry = new PlaneGeometry(PLANE_SIZE, PLANE_SIZE);
geometry.rotateX(-Math.PI / 2);

this._material = new DIVEShaderMaterial<GridShader>({
...DIVEShaderLib.grid,
uniforms: {
uGridSize: { value: this._gridSize },
uMajorLineEvery: { value: majorLineEvery },
uMinorLineColor: { value: new Color(GRID_MINOR_LINE_COLOR) },
uMajorLineColor: { value: new Color(GRID_MAJOR_LINE_COLOR) },
uFadeDistance: { value: PLANE_SIZE / 2 },
},
this._uniforms = {
uGridSize: uniform(this._gridSize),
uMajorLineEvery: uniform(majorLineEvery),
uMinorLineColor: uniform(new Color(GRID_MINOR_LINE_COLOR)),
uMajorLineColor: uniform(new Color(GRID_MAJOR_LINE_COLOR)),
uFadeDistance: uniform(PLANE_SIZE / 2),
};

this._material = new MeshBasicNodeMaterial({
transparent: true,
depthWrite: false,
side: DoubleSide,
outputNode: new GridNode(this._uniforms),
});

this._mesh = new Mesh(geometry, this._material);
Expand All @@ -83,11 +82,11 @@ export class DIVEGrid extends Object3D {

public setGridSize(size: number): void {
this._gridSize = size;
this._material.uniforms.uGridSize.value = size;
this._uniforms.uGridSize.value = size;
}

public setMajorLineEvery(n: number): void {
this._material.uniforms.uMajorLineEvery.value = n;
this._uniforms.uMajorLineEvery.value = n;
}

public dispose(): void {
Expand Down
Loading
Loading