From 1ba17ee43c6954396118bd8298436af3e2a70510 Mon Sep 17 00:00:00 2001 From: Alvaro Saburido Date: Thu, 30 May 2024 08:32:43 +0200 Subject: [PATCH] feat: release v4 (#490) * feat: 474 vue chrome devtools plugin (#479) * feat: vue chrome devtools * feat: editable scenes from devtools * chore(lint): fix lint errors * feat: highlight material * chore(lint): fix * chore: release v4.0.0-next.0 * feat: update to three `v160` and vue `v3.4` (#488) * fix(types): added `Object3DEventMap` to `Object3D` generics for point event handling (#491) * feat: 140 on demand rendering (#497) * feat: conditional rendering * chore: remove subscribe system * feat: on-demand automatic invalidation with prop changes * feat: invalidate once first when is `renderMode !== 'always'` * docs: performance page, on-demand rendering * chore: fix windowsize issue * chore(lint): fix maximum line length issues * feat: invalidate on-demand on window resize * feat: add advance method for manual mode * feat: fix manual first render with advance * docs: performance manual mode * docs: add badge with version * chore: correct typos and PR suggestions * chore: tell dont ask fix * feat: render state instead of internal * feat: remove default camera warning (#499) * feat: remove annoying defautl camera warning * chore: remove logWarning * feat: 492 set tone mapping default to acesfilmictonemapping (#498) * feat: set ACESFilmicToneMapping as default toneMapping * chore: usage of nullish coealescing operator instead of ternaries * feat: 516 localstate for custom renderer node instances instead of userdata (#522) * feat: conditional rendering * chore: remove subscribe system * feat: on-demand automatic invalidation with prop changes * feat: invalidate once first when is `renderMode !== 'always'` * docs: performance page, on-demand rendering * chore: fix windowsize issue * chore(lint): fix maximum line length issues * feat: invalidate on-demand on window resize * feat: add advance method for manual mode * feat: fix manual first render with advance * docs: performance manual mode * docs: add badge with version * chore: correct typos and PR suggestions * chore: tell dont ask fix * feat: render state instead of internal * feat: add __tres local state to nodeOps instances * feat: add context to root on instances localstate * feat: camera registration ops from node local state ctx * feat: event handling registration from localState of nodes * feature: disposable flag on node localstate * feat: remove userData from types * chore: remove unused import * fix(test): fake localstate `.__tres` on tests * fix(types): fix nodeOps instances localstate type * fix: camera aspect * Update orthographic camera aspect when screen size updates * Give user a "manual" flag to keep Tres from updating camera * feat: 503 conditional rendering of primitives (#514) * feat(nodeOps): switch instance logic for reactive `object` prop * chore: playground primitives with models * chore: fix linter * chore: fix tests and linters, primitive object is now reactive * chore: refactor instance swaping logic to overwrite set and copy properties * chore: tests * chore: remove console.log * chore: remove unused import watch * feat: add primitive conditional to patch object prop * fix: `nodeOps` is now a function (#579) * fix: `nodeOps` is now a function * chore(test): updated tests for `nodeOps` * chore: next package json version * chore: release v4.0.0-next.1 * fix: refactor nodeOps to return methods at the end of the function (#602) * fix: refactor nodeOps to return methods at the end of the function * chore: fix lint * chore: internal playground organisation (#601) * chore: new internal playground org and testing pages * chore: fix lint * chore: better styling of playground landing page * chore: lint * chore: deps update * chore: internal primitive model test playground * chore: fix lint * chore: release v4.0.0-next.2 * chore: misc routes * fix: do not change pierced props case (#608) * chore: lint fix * chore: problem with package version * chore: fix lint * chore: rebuild pnpm-lock * test(nodeOps): clean up tests * test(nodeOps): organize tests * test: add coverage plugin * test: add coverage to package.json script * test(nodeOps): improve test coverage * feat: devtools renderer improvements (#614) * feat: renderer programs when selecting scene on devtools * feat: renderer.info * chore: fix lint * docs: devtools update * chore: fix lint issues * feat(events)!: pointerevents manager and state (#529) * new file: playground/src/components/Box.vue new file: playground/src/pages/raycaster/Propogation.vue * Started work on interactive Event Propogation playground example modified: src/components/TresCanvas.vue * Import and use `useEventStore` * defineEmits for all expected pointer events so we may emit propogated events off of the canvasa modified: src/composables/index.ts new file: src/composables/useEventStore/index.ts * Started work on an event store. I'm not sure this counts as a store just yet * Wired up majority of pointer events * Added event propogation * Does not require using userData scene props or nodeOps for registering objects to scene modified: src/composables/useRaycaster/index.ts * Added new event listeners to power newly supported pointer events. We now check whole scene/children when calling intersectObjects. * Created new EventHooks for new events * Added `forceUpdate` function that allows for pointer-move events to work without mouth movement (good for when camera is moving but mouse is not) modified: src/core/nodeOps.ts * Added supported events to array so they don't get received as props * (temporarily) unhook current pointer event solution to iterate on useEventStore modified: src/utils/index.ts * Added Camel-to-kebab case util * Support multiple event listeners, add support for .stop event modifier * Set stopProgation variable to false by default, whoops * fix typo * fix: remove `createGlobalState` from `useEventStore`, allowing events to work while multiple TresCanvas' are being used * fix(perf): remove extraneous intersectObjects/getIntersects calls by moving intersects into a ref that is updated on pointer-move * chore(lint): fix lint issues * feat: enhance events manager to include duplicates checking, pointer-missed support, and forced updating Per file changelog: modified: playground/src/components/Box.vue * Added a pointer-missed handler for testing modified: playground/src/pages/TheBasic.vue * uses forceUpdate from EventManager to fire events even when the mouse hasn't moved modified: playground/src/pages/raycaster/Propagation.vue * Didn't mean to undo the lint changes, adds a pointer-missed event on the canvas for extra testing modified: src/components/TresCanvas.vue * Adds `pointer-missed` as possible event for canvas emits modified: src/composables/index.ts * Update export deleted: src/composables/useEventStore/index.ts * Rename `useEventStore` to `useTresEventManager` modified: src/composables/useRaycaster/index.ts * Check for empty intersects on hit test, wire up pointerMissed events eventHook * Fix forceUpdate to call onPointerMove instead of triggering an EventHook modified: src/composables/useTresContextProvider/index.ts * Add TresEventManager type new file: src/composables/useTresEventManager/index.ts * add onPointerMissed * create (de)registerPointerMissedObj methods so we can track objects in the scene listening to this event * Note: These are passed to nodeOps via TresContext * Implement duplicates checking for eventPropogation modified: src/core/nodeOps.ts * register/deregister pointerMissed objects * chore: lint * docs: new event docs * chore: fix lint * feat: enhance event object details and use in Box example to change material color. Add ability to force event system updates even when mouse hasn't moved. Enhance pointer-enter/leave events. Update types Box.vue * Added pointer-missed handler * set the materials flash color using the object coming off of the event instead of a ref UseRaycaster * Flesh out event details to include * all mouse event properties * intersections * tres camera * camera raycaster * source event * mouse position delta * stopPropagating stub * and unprojectedPoint (this needs work, cant get the math to work) UseTresContextProvider * Add TresEventManager type to TresContext useTresEventManager * Add forceUpdate method to allow apps to force an event system update even when the mouse hasnt moved * Add pointerMissed event * Properly implement pointer-enter/pointer-leave events * Before now, pointer-enter | leave were only called on first object in intersection, now we execute the events for all entered/left objects * Use stopPropagating property included on event object * chore: lint * chore: fix lint issues --------- Co-authored-by: alvarosabu * feat: 499 better memory management (#606) * chore: memory management playground * feat: recursively free cpu and gpu memory allocation on remove * chore: clumsy attempt to dispose on unmount * chore: lint fix * feat: remove scene root on disposal * chore: fix lint * docs: added disposal guide on `performance` docs * chore: fix lint * chore: type issues (#663) * fix: fix some internal types * chore: fix linters * fix: typescript issues on event manager * chore: release v4.0.0-rc.0 * fix: make on* callbacks settable (#672) * fix: make on- callbacks settable * test: test setting not calling * feat: 633 use loop (#673) * feat: createRenderLoop unique to context * feat: onLoop returns current state * feat: ensuring callback excecution with index order * feat: take control of render loop logic * docs: updated composable docs * feat: change error to deprecation warning towards v5 * chore: add link to new composable docs on deprecation warning * chore: remove depcreation warning of existing useRenderLoop * feat: `useFrame` and `useRender` instead of `onLoop` * chore: fix lint * feat: applied useFrame to directives * chore: fix lint * feat: `useUpdate` instead of `useFrame` and useRender pausing. * chore: testing fbo * feat: reserve index 1 for late-updates * chore: fix lint * feat: useLoop composable for the win * chore: change onLoop name for register * chore: unit tests for loop * chore: change order for registration to make index optional * chore: fix lint * feat: pauseRender and resumeRender * docs: useLoop guide * docs: updated basic animations recipe to `useLoop` * docs: correct pause render methods on docs * Update docs/api/composables.md Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * Update docs/api/composables.md Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * Update docs/api/composables.md Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * Update docs/api/composables.md Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * Update docs/api/composables.md Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * Update docs/api/composables.md Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * Update docs/api/composables.md Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * chore: refactor subscribers to `priorityEventHooks` * Update docs/api/composables.md Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * feat: just return `off` on the loop registration methods * docs: update docs to add `off` unregister callback method * feat: remove `v-rotate` * docs: added context warning for `v-always-look-at` * Update docs/api/composables.md Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * Update docs/api/composables.md Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * chore: remove leftover of isntance.provide * chore: remove subscribers from context * chore: abstract `wrapCallback` and move render loop register to `useRender` * chore: fix lint * chore: testing off * Revert "chore: abstract `wrapCallback` and move render loop register to `useRender`" This reverts commit 24cec651df56aedd16835144986c3c7260b3e374. * chore: return bound `off` method and use createPriorityEvent for render with defaultFn fallback * feat: deprecate and remove `vAlwaysLookAt` and `vRotate` BREAKING_CHANGE: Directives `vAlwaysLookAt` and `vRotate` due incompatibility with new `useLoop` and the refactor of the render loop logic. * feat: set context to loop to avoid wrapping the callbacks * feat: dispose render hook before taking over --------- Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> * chore(playground): adding missing import and removing the directives that were deprecated * chore(playground): use new composable on animations * fix(utils): reorder object disposal to avoid issue with Helper `dispose` methods (#683) * chore: updated deps * chore: release v4.0.0-rc.1 * fix: manual rendering blank (#685) * fix: increate time to advance on manual mode * chore: correct playground * fix: 686 useloop callback state missing controls (#687) * fix(loop): take plain snapshots of ctx * fix: types for useloop * chore: lint * docs: add RectAreaLightHelper to vLightHelper docs * chore(deps): update deps 24-0-2024 * chore: release v4.0.0-rc.2 * fix: start loop if user calls useRenderLoop (#695) * docs: change motivation * chore(deps): last update before release --------- Co-authored-by: Peter Co-authored-by: Garrett Walker Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com> Co-authored-by: Jaime Torrealba Co-authored-by: Jaime A Torrealba C <63722373+JaimeTorrealba@users.noreply.github.com> --- CHANGELOG.md | 106 +- docs/.vitepress/config/en.ts | 5 +- .../theme/components/BlenderCube.vue | 17 + .../.vitepress/theme/components/GraphPane.vue | 101 + .../theme/components/OnDemandRendering.vue | 37 + .../theme/components/RenderingLogger.vue | 23 + docs/.vitepress/theme/composables/state.ts | 11 + docs/advanced/performance.md | 157 + docs/api/composables.md | 293 +- docs/api/events.md | 64 +- docs/api/tres-canvas.md | 5 +- docs/components.d.ts | 6 +- docs/cookbook/basic-animations.md | 70 +- docs/debug/devtools.md | 6 + docs/directives/v-always-look-at.md | 6 +- docs/directives/v-light-helper.md | 1 + docs/directives/v-rotate.md | 6 +- docs/guide/index.md | 23 +- docs/package.json | 4 +- docs/public/devtools-v4.png | Bin 0 -> 62379 bytes docs/public/logo.svg | 32 +- package.json | 62 +- playground/components.d.ts | 41 +- playground/package.json | 8 +- playground/public/logo.svg | 27 + playground/src/App.vue | 13 +- playground/src/components/AkuAku.vue | 17 + .../components/AnimatedObjectUseUpdate.vue | 95 + playground/src/components/BlenderCube.vue | 29 + playground/src/components/Box.vue | 44 + .../src/components/DirectiveSubComponent.vue | 7 + playground/src/components/DynamicModel.vue | 22 + playground/src/components/FBOCube.vue | 27 + playground/src/components/GraphPane.vue | 101 + playground/src/components/MultipleCanvas.vue | 98 - .../src/components/TakeOverLoopExperience.vue | 57 + playground/src/components/TheExperience.vue | 38 +- playground/src/components/TheSphere.vue | 16 +- playground/src/composables/state.ts | 11 + playground/src/composables/useFBO.ts | 88 + playground/src/main.ts | 1 - playground/src/pages/MultipleCanvas.vue | 7 - playground/src/pages/advanced/FBO.vue | 31 + playground/src/pages/advanced/Memory.vue | 65 + .../src/pages/advanced/TakeOverLoop.vue | 24 + .../src/pages/{perf => advanced}/index.vue | 0 .../src/pages/advanced/manual/experience.vue | 28 + .../src/pages/advanced/manual/index.vue | 24 + .../pages/advanced/on-demand/experience.vue | 36 + .../src/pages/advanced/on-demand/index.vue | 23 + .../Conditional.vue} | 0 .../pages/{TheGroups.vue => basic/Groups.vue} | 0 .../pages/{lights.vue => basic/Lights.vue} | 4 +- playground/src/pages/basic/Multiple.vue | 60 + playground/src/pages/basic/OnCallbacks.vue | 72 + playground/src/pages/basic/PiercedProps.vue | 67 + playground/src/pages/basic/Primitives.vue | 138 + .../src/pages/{ => basic}/Responsiveness.vue | 2 +- playground/src/pages/basic/example.vue | 11 + .../pages/{TheBasic.vue => basic/index.vue} | 21 +- .../pages/cameras/{Cameras.vue => index.vue} | 18 +- playground/src/pages/click-blocking-box.vue | 42 - playground/src/pages/empty.vue | 2 +- playground/src/pages/events/Propagation.vue | 188 + playground/src/pages/events/index.vue | 94 + playground/src/pages/index.vue | 87 +- playground/src/pages/misc/Directives.vue | 25 + playground/src/pages/misc/Text3DDemo.vue | 11 +- .../src/pages/models/PrimitivesModel.vue | 59 + playground/src/pages/perf/AkuAku.vue | 50 - playground/src/pages/raycaster/TheEvents.vue | 77 - playground/src/{router.ts => router/index.ts} | 35 +- playground/src/router/routes/advanced.ts | 22 + playground/src/router/routes/basic.ts | 47 + playground/src/router/routes/cameras.ts | 17 + playground/src/router/routes/events.ts | 7 + playground/src/router/routes/index.ts | 15 + playground/src/router/routes/misc.ts | 12 + playground/src/router/routes/models.ts | 7 + playground/vite.config.ts | 9 +- pnpm-lock.yaml | 3166 +++++++---------- src/components/TresCanvas.vue | 64 +- src/composables/index.ts | 4 +- src/composables/useCamera/index.ts | 36 +- src/composables/useLoop/index.ts | 49 + .../usePointerEventHandler/index.ts | 71 +- src/composables/useRaycaster/index.ts | 174 +- src/composables/useRenderLoop/index.ts | 27 +- src/composables/useRenderer/const.ts | 6 +- src/composables/useRenderer/index.ts | 72 +- .../useTresContextProvider/index.ts | 183 +- src/composables/useTresEventManager/index.ts | 194 + src/core/loop.test.ts | 120 + src/core/loop.ts | 161 + src/core/nodeOps.test.ts | 460 +++ src/core/nodeOps.ts | 264 +- src/core/nodeOpts.test.ts | 184 - src/core/renderer.ts | 12 - src/devtools/highlight.ts | 2 +- src/devtools/plugin.ts | 65 +- src/devtools/utils.ts | 4 + src/directives/index.ts | 4 +- src/directives/vAlwaysLookAt.ts | 21 - src/directives/vRotate.ts | 40 - src/index.ts | 3 + src/types/index.ts | 70 +- src/utils/createPriorityEventHook.test.ts | 263 ++ src/utils/createPriorityEventHook.ts | 73 + src/utils/index.ts | 58 +- src/utils/test-utils.ts | 3 +- 110 files changed, 6039 insertions(+), 3026 deletions(-) create mode 100644 docs/.vitepress/theme/components/BlenderCube.vue create mode 100644 docs/.vitepress/theme/components/GraphPane.vue create mode 100644 docs/.vitepress/theme/components/OnDemandRendering.vue create mode 100644 docs/.vitepress/theme/components/RenderingLogger.vue create mode 100644 docs/.vitepress/theme/composables/state.ts create mode 100644 docs/advanced/performance.md create mode 100644 docs/public/devtools-v4.png create mode 100644 playground/public/logo.svg create mode 100644 playground/src/components/AkuAku.vue create mode 100644 playground/src/components/AnimatedObjectUseUpdate.vue create mode 100644 playground/src/components/BlenderCube.vue create mode 100644 playground/src/components/Box.vue create mode 100644 playground/src/components/DirectiveSubComponent.vue create mode 100644 playground/src/components/DynamicModel.vue create mode 100644 playground/src/components/FBOCube.vue create mode 100644 playground/src/components/GraphPane.vue delete mode 100644 playground/src/components/MultipleCanvas.vue create mode 100644 playground/src/components/TakeOverLoopExperience.vue create mode 100644 playground/src/composables/state.ts create mode 100644 playground/src/composables/useFBO.ts delete mode 100644 playground/src/pages/MultipleCanvas.vue create mode 100644 playground/src/pages/advanced/FBO.vue create mode 100644 playground/src/pages/advanced/Memory.vue create mode 100644 playground/src/pages/advanced/TakeOverLoop.vue rename playground/src/pages/{perf => advanced}/index.vue (100%) create mode 100644 playground/src/pages/advanced/manual/experience.vue create mode 100644 playground/src/pages/advanced/manual/index.vue create mode 100644 playground/src/pages/advanced/on-demand/experience.vue create mode 100644 playground/src/pages/advanced/on-demand/index.vue rename playground/src/pages/{TheConditional.vue => basic/Conditional.vue} (100%) rename playground/src/pages/{TheGroups.vue => basic/Groups.vue} (100%) rename playground/src/pages/{lights.vue => basic/Lights.vue} (90%) create mode 100644 playground/src/pages/basic/Multiple.vue create mode 100644 playground/src/pages/basic/OnCallbacks.vue create mode 100644 playground/src/pages/basic/PiercedProps.vue create mode 100644 playground/src/pages/basic/Primitives.vue rename playground/src/pages/{ => basic}/Responsiveness.vue (87%) create mode 100644 playground/src/pages/basic/example.vue rename playground/src/pages/{TheBasic.vue => basic/index.vue} (80%) rename playground/src/pages/cameras/{Cameras.vue => index.vue} (76%) delete mode 100644 playground/src/pages/click-blocking-box.vue create mode 100644 playground/src/pages/events/Propagation.vue create mode 100644 playground/src/pages/events/index.vue create mode 100644 playground/src/pages/misc/Directives.vue create mode 100644 playground/src/pages/models/PrimitivesModel.vue delete mode 100644 playground/src/pages/perf/AkuAku.vue delete mode 100644 playground/src/pages/raycaster/TheEvents.vue rename playground/src/{router.ts => router/index.ts} (70%) create mode 100644 playground/src/router/routes/advanced.ts create mode 100644 playground/src/router/routes/basic.ts create mode 100644 playground/src/router/routes/cameras.ts create mode 100644 playground/src/router/routes/events.ts create mode 100644 playground/src/router/routes/index.ts create mode 100644 playground/src/router/routes/misc.ts create mode 100644 playground/src/router/routes/models.ts create mode 100644 src/composables/useLoop/index.ts create mode 100644 src/composables/useTresEventManager/index.ts create mode 100644 src/core/loop.test.ts create mode 100644 src/core/loop.ts create mode 100644 src/core/nodeOps.test.ts delete mode 100644 src/core/nodeOpts.test.ts delete mode 100644 src/core/renderer.ts delete mode 100644 src/directives/vAlwaysLookAt.ts delete mode 100644 src/directives/vRotate.ts create mode 100644 src/utils/createPriorityEventHook.test.ts create mode 100644 src/utils/createPriorityEventHook.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 0728bfa42..d65ea2776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,83 @@ +## [4.0.0-rc.2](https://github.com/Tresjs/tres/compare/4.0.0-rc.1...4.0.0-rc.2) (2024-05-24) + + +### Bug Fixes + +* 686 useloop callback state missing controls ([#687](https://github.com/Tresjs/tres/issues/687)) ([a41f532](https://github.com/Tresjs/tres/commit/a41f532b0c8d717e4bc3ec11fa73bd58df871fa8)) +* manual rendering blank ([#685](https://github.com/Tresjs/tres/issues/685)) ([0720d18](https://github.com/Tresjs/tres/commit/0720d186e92ca9faa9e5f4e51a3269504bed2a09)) + +## [4.0.0-rc.1](https://github.com/Tresjs/tres/compare/4.0.0-rc.0...4.0.0-rc.1) (2024-05-15) + + +### Features + +* 633 use loop ([#673](https://github.com/Tresjs/tres/issues/673)) ([1b2fa70](https://github.com/Tresjs/tres/commit/1b2fa70e9999eb64395b3e7e9f2489ceab035a7a)) + + +### Bug Fixes + +* make on* callbacks settable ([#672](https://github.com/Tresjs/tres/issues/672)) ([ac152df](https://github.com/Tresjs/tres/commit/ac152dfa91c6ba347cbe0566fb4afbe19f50dd2b)) +* **utils:** reorder object disposal to avoid issue with Helper `dispose` methods ([#683](https://github.com/Tresjs/tres/issues/683)) ([e5a2cef](https://github.com/Tresjs/tres/commit/e5a2cef0e450196abaa6d18380a5aadbc9cd057d)) + +## [4.0.0-rc.0](https://github.com/Tresjs/tres/compare/3.9.0...4.0.0-rc.0) (2024-04-25) + + +### ⚠ BREAKING CHANGES + +* **events:** pointerevents manager and state (#529) + +### Features + +* 499 better memory management ([#606](https://github.com/Tresjs/tres/issues/606)) ([e98ca6d](https://github.com/Tresjs/tres/commit/e98ca6dea15973b3a00e4b485199d9906eb772eb)) +* devtools renderer improvements ([#614](https://github.com/Tresjs/tres/issues/614)) ([cdf6b6f](https://github.com/Tresjs/tres/commit/cdf6b6fefbd58dbf1dfbe396f219ac6f7c6fc92d)) +* **events:** pointerevents manager and state ([#529](https://github.com/Tresjs/tres/issues/529)) ([b536ab1](https://github.com/Tresjs/tres/commit/b536ab19d1f4082c2db926e24d8c52f92949964b)) + + +### Bug Fixes + +* do not change pierced props case ([#608](https://github.com/Tresjs/tres/issues/608)) ([906f2e1](https://github.com/Tresjs/tres/commit/906f2e157aab7aa6daef5682c3282cf6e84fa891)) + +## [4.0.0-next.2](https://github.com/Tresjs/tres/compare/3.9.0...4.0.0-rc.0) (2024-03-27) + + +### Bug Fixes + +* refactor nodeOps to return methods at the end of the function ([#602](https://github.com/Tresjs/tres/issues/602)) ([cd0c3bc](https://github.com/Tresjs/tres/commit/cd0c3bcd891f019cf91f30e5fdd547630332a065)) + +## [4.0.0-next.1](https://github.com/Tresjs/tres/compare/3.9.0...4.0.0-rc.0) (2024-03-18) + + +### Features + +* 140 on demand rendering ([#497](https://github.com/Tresjs/tres/issues/497)) ([f688c64](https://github.com/Tresjs/tres/commit/f688c6447be887c4675a57ecabb5182d8b7d02cf)) +* 492 set tone mapping default to acesfilmictonemapping ([#498](https://github.com/Tresjs/tres/issues/498)) ([c4547f9](https://github.com/Tresjs/tres/commit/c4547f92615a43b7b56b34c0e1ee9f4b78a2230b)) +* 503 conditional rendering of primitives ([#514](https://github.com/Tresjs/tres/issues/514)) ([79d8a76](https://github.com/Tresjs/tres/commit/79d8a762da6b6e23771a20314f7902eff4635acf)) +* 516 localstate for custom renderer node instances instead of userdata ([#522](https://github.com/Tresjs/tres/issues/522)) ([08717ef](https://github.com/Tresjs/tres/commit/08717efd0f631c085340b1fea4eb6c154c63608b)) +* remove default camera warning ([#499](https://github.com/Tresjs/tres/issues/499)) ([8bbafde](https://github.com/Tresjs/tres/commit/8bbafde48a33753f0b6560da36a4d128aaa83cc6)) +* update to three `v160` and vue `v3.4` ([#488](https://github.com/Tresjs/tres/issues/488)) ([5fad3b8](https://github.com/Tresjs/tres/commit/5fad3b8095c09cfe758e2553da3df49b29b1ce1a)) + + +### Bug Fixes + +* `nodeOps` is now a function ([#579](https://github.com/Tresjs/tres/issues/579)) ([ddc229e](https://github.com/Tresjs/tres/commit/ddc229e6e492b9e7887add0fcc679a9ae4e47f5c)) +* camera aspect ([52dad5c](https://github.com/Tresjs/tres/commit/52dad5c98271f80f4d454bbcce1bb5844960f943)) +* **types:** added `Object3DEventMap` to `Object3D` generics for point event handling ([#491](https://github.com/Tresjs/tres/issues/491)) ([a63eb90](https://github.com/Tresjs/tres/commit/a63eb9099fcaf97b1c96abe5667ee71ca2fd611f)) + +## [4.0.0-next.0](https://github.com/Tresjs/tres/compare/3.9.0...4.0.0-rc.0) (2023-12-22) + + +### Features + +* 474 vue chrome devtools plugin ([#479](https://github.com/Tresjs/tres/issues/479)) ([224ab06](https://github.com/Tresjs/tres/commit/224ab06a4404e2ae5a0cbd2f43041961862b09fd)) + +## [4.0.0-next.2](https://github.com/Tresjs/tres/compare/4.0.0-next.1...4.0.0-next.2) (2024-03-27) + +## [4.0.0-next.1](https://github.com/Tresjs/tres/compare/3.7.0...4.0.0-next.1) (2024-03-18) +* correct type exporting issues ([#625](https://github.com/Tresjs/tres/issues/625)) ([8e52cf1](https://github.com/Tresjs/tres/commit/8e52cf1935d7b725b87c9a41e44ba61e33bd3e85)) + + ## [3.9.0](https://github.com/Tresjs/tres/compare/3.8.1...3.9.0) (2024-04-24) @@ -17,7 +95,8 @@ ### Bug Fixes -* correct type exporting issues ([#625](https://github.com/Tresjs/tres/issues/625)) ([8e52cf1](https://github.com/Tresjs/tres/commit/8e52cf1935d7b725b87c9a41e44ba61e33bd3e85)) +* refactor nodeOps to return methods at the end of the function ([#602](https://github.com/Tresjs/tres/issues/602)) ([cd0c3bc](https://github.com/Tresjs/tres/commit/cd0c3bcd891f019cf91f30e5fdd547630332a065)) + ## [3.8.0](https://github.com/Tresjs/tres/compare/3.7.0...3.8.0) (2024-04-03) @@ -38,6 +117,31 @@ ### Features +* 140 on demand rendering ([#497](https://github.com/Tresjs/tres/issues/497)) ([f688c64](https://github.com/Tresjs/tres/commit/f688c6447be887c4675a57ecabb5182d8b7d02cf)) +* 492 set tone mapping default to acesfilmictonemapping ([#498](https://github.com/Tresjs/tres/issues/498)) ([c4547f9](https://github.com/Tresjs/tres/commit/c4547f92615a43b7b56b34c0e1ee9f4b78a2230b)) +* 503 conditional rendering of primitives ([#514](https://github.com/Tresjs/tres/issues/514)) ([79d8a76](https://github.com/Tresjs/tres/commit/79d8a762da6b6e23771a20314f7902eff4635acf)) +* 516 localstate for custom renderer node instances instead of userdata ([#522](https://github.com/Tresjs/tres/issues/522)) ([08717ef](https://github.com/Tresjs/tres/commit/08717efd0f631c085340b1fea4eb6c154c63608b)) +* remove default camera warning ([#499](https://github.com/Tresjs/tres/issues/499)) ([8bbafde](https://github.com/Tresjs/tres/commit/8bbafde48a33753f0b6560da36a4d128aaa83cc6)) +* update to three `v160` and vue `v3.4` ([#488](https://github.com/Tresjs/tres/issues/488)) ([5fad3b8](https://github.com/Tresjs/tres/commit/5fad3b8095c09cfe758e2553da3df49b29b1ce1a)) + + +### Bug Fixes + +* `nodeOps` is now a function ([#579](https://github.com/Tresjs/tres/issues/579)) ([ddc229e](https://github.com/Tresjs/tres/commit/ddc229e6e492b9e7887add0fcc679a9ae4e47f5c)) +* camera aspect ([52dad5c](https://github.com/Tresjs/tres/commit/52dad5c98271f80f4d454bbcce1bb5844960f943)) +* **types:** added `Object3DEventMap` to `Object3D` generics for point event handling ([#491](https://github.com/Tresjs/tres/issues/491)) ([a63eb90](https://github.com/Tresjs/tres/commit/a63eb9099fcaf97b1c96abe5667ee71ca2fd611f)) + +## [4.0.0-next.0](https://github.com/Tresjs/tres/compare/3.7.0...4.0.0-next.1) (2023-12-22) + + +### Features + +* 474 vue chrome devtools plugin ([#479](https://github.com/Tresjs/tres/issues/479)) ([224ab06](https://github.com/Tresjs/tres/commit/224ab06a4404e2ae5a0cbd2f43041961862b09fd)) + +## [3.7.0](https://github.com/Tresjs/tres/compare/3.6.1...3.7.0) (2024-01-29) + +### Features + * 474 vue chrome devtools plugin ([#526](https://github.com/Tresjs/tres/issues/526)) ([0185bfa](https://github.com/Tresjs/tres/commit/0185bfa6f04faff5eabbc526616713ef7747ebeb)) * 524 feat add directives to core ([#525](https://github.com/Tresjs/tres/issues/525)) ([5268e9f](https://github.com/Tresjs/tres/commit/5268e9f13bf65c61d5ddfe7153b71b335449b81d)) diff --git a/docs/.vitepress/config/en.ts b/docs/.vitepress/config/en.ts index d1122645c..6d0e285be 100644 --- a/docs/.vitepress/config/en.ts +++ b/docs/.vitepress/config/en.ts @@ -43,7 +43,8 @@ export const enConfig: LocaleSpecificConfig = { items: [ { text: 'Extending', link: '/advanced/extending' }, - { text: 'Primitive', link: '/advanced/primitive' }, + { text: 'Primitives', link: '/advanced/primitive' }, + { text: 'Scaling Performance 🚀', link: '/advanced/performance' }, { text: 'Caveats', link: '/advanced/caveats', @@ -77,9 +78,7 @@ export const enConfig: LocaleSpecificConfig = { items: [ { text: 'v-log', link: '/directives/v-log' }, { text: 'v-light-helper', link: '/directives/v-light-helper' }, - { text: 'v-always-look-at', link: '/directives/v-always-look-at' }, { text: 'v-distance-to', link: '/directives/v-distance-to' }, - { text: 'v-rotate', link: '/directives/v-rotate' }, ], }, { diff --git a/docs/.vitepress/theme/components/BlenderCube.vue b/docs/.vitepress/theme/components/BlenderCube.vue new file mode 100644 index 000000000..349fee298 --- /dev/null +++ b/docs/.vitepress/theme/components/BlenderCube.vue @@ -0,0 +1,17 @@ + + + diff --git a/docs/.vitepress/theme/components/GraphPane.vue b/docs/.vitepress/theme/components/GraphPane.vue new file mode 100644 index 000000000..79110e0f2 --- /dev/null +++ b/docs/.vitepress/theme/components/GraphPane.vue @@ -0,0 +1,101 @@ + + + diff --git a/docs/.vitepress/theme/components/OnDemandRendering.vue b/docs/.vitepress/theme/components/OnDemandRendering.vue new file mode 100644 index 000000000..c739c11e6 --- /dev/null +++ b/docs/.vitepress/theme/components/OnDemandRendering.vue @@ -0,0 +1,37 @@ + + + diff --git a/docs/.vitepress/theme/components/RenderingLogger.vue b/docs/.vitepress/theme/components/RenderingLogger.vue new file mode 100644 index 000000000..0fca78844 --- /dev/null +++ b/docs/.vitepress/theme/components/RenderingLogger.vue @@ -0,0 +1,23 @@ + + + diff --git a/docs/.vitepress/theme/composables/state.ts b/docs/.vitepress/theme/composables/state.ts new file mode 100644 index 000000000..227164b7c --- /dev/null +++ b/docs/.vitepress/theme/composables/state.ts @@ -0,0 +1,11 @@ +import { reactive, toRefs } from 'vue' + +const state = reactive({ + renderingTimes: 0, +}) +export function useState() { + return { + ...toRefs(state), + + } +} diff --git a/docs/advanced/performance.md b/docs/advanced/performance.md new file mode 100644 index 000000000..1925eacd1 --- /dev/null +++ b/docs/advanced/performance.md @@ -0,0 +1,157 @@ +# Scaling performance 🚀 + +> Quick guide with tips to improve performance of your Tres.js application. + +We are running WebGL on the browser, which can be quite expensive and it will depend on how powerful the user's device is. To make 3D accessible to everyone, we need to make sure our applications are optimized to run also on low-end devices. This guide will provide some tips to improve the performance of your Tres.js application. + +## On-demand rendering + +By default, Tres.js will render your scene on every frame. This is great for most applications, but if you are building a game or a complex application, you might want to control when the scene is rendered. + +Otherwise it might drain your device battery 🔋 🔜 đŸĒĢ and make your computer sound like an airplane đŸ›Ģ. + +Ideally, you only want to **render the scene when necessary**, for example when the user interacts with the scene and the camera moves, or when objects in the scene are animated. + +You can do that by setting the `renderMode` prop to `on-demand` or `manual`: + +### Mode `on-demand` + + +
+ +
+
+ +```vue + + + +``` + +#### Automatic Invalidation + +When using `render-mode="on-demand"`, Tres.js will automatically invalidate the current frame by observing component props and lifecycle hooks like `onMounted` and `onUnmounted`. It will also invalidate the frame when resizing the window or changing any prop from the `` component like `clearColor` or `antialias`. + +The code below updates TresMesh's position-x prop every second, triggering a new render. + +```vue + + + +``` + +#### Manual Invalidation + +Since it is not really possible to observe all the possible changes in your application, you can also manually invalidate the frame by calling the `invalidate()` method from the [`useTresContext` composable](../api/composables.md#usetrescontext): + +::: code-group + +```vue [App.vue] + + + +``` + +```vue [Scene.vue] + + + +``` + +::: + +### Mode `always` + +In this rendering mode, Tres will continously render the scene on every frame. This is the default mode and the easiest to use, but it's also the most resource expensive one. + +### Mode `manual` + +If you want to have full control of when the scene is rendered, you can set the `render-mode` prop to `manual`: + +```vue + + + +``` + +In this mode, Tres will not render the scene automatically. You will need to call the `advance()` method from the [`useTresContext` composable](../api/composables.md#usetrescontext) to render the scene: + +```vue + +``` + +## Dispose resources `dispose()` + +When you are done with a resource, like a texture, geometry, or material, you should dispose of it to free up memory. This is especially important when you are creating and destroying resources frequently, like in a game. + +TresJS will automatically dispose of resources recursively when the component is unmounted, but you can also perform this manually by calling the `dispose()` directly from the package: + +::: warning +To avoid errors and unwanted sideeffects, resources created programatically with the use of `primitives` need to be manually disposed. +::: + +```html {2,12} + + + +``` diff --git a/docs/api/composables.md b/docs/api/composables.md index e4beb5fe4..acfa67314 100644 --- a/docs/api/composables.md +++ b/docs/api/composables.md @@ -4,73 +4,213 @@ Vue 3 [Composition API](https://vuejs.org/guide/extras/composition-api-faq.html# **TresJS** takes huge advantage of this API to create a set of composable functions that can be used to create animations, interact with the scene and more. It also allows you to create more complex scenes that might not be possible using just the Vue Components (Textures, Loaders, etc.). -The core of **TresJS** uses these composables internally, so you would be using the same API that the core uses. For instance, components that need to updated on the internal render loop use the `useRenderLoop` composable to register a callback that will be called every time the renderer updates the scene. +The core of **TresJS** uses these composables internally, so you would be using the same API that the core uses. -## useRenderLoop - -The `useRenderLoop` composable is the core of **TresJS** animations. It allows you to register a callback that will be called on native refresh rate. This is the most important composable in **TresJS**. +## useTresContext +This composable aims to provide access to the state model which contains multiple useful properties. ```ts -const { onLoop, resume } = useRenderLoop() +const { camera, renderer, camera, cameras } = useTresContext() +``` -onLoop(({ delta, elapsed, clock }) => { - // I will run at every frame ~60FPS (depending of your monitor) -}) +::: warning +`useTresContext` can be only be used inside of a `TresCanvas` since this component acts as the provider for the context data. +::: + +::: code-group + +```vue [App.vue] + + + ``` +```vue [SubComponent.vue] + +``` + +::: + +### Properties of context +| Property | Description | +| --- | --- | +| **camera** | The currently active camera | +| **cameras** | The cameras that exist in the scene | +| **controls** | The controls of your scene | +| **deregisterCamera** | A method to deregister a camera. This is only required if you manually create a camera. Cameras in the template are deregistered automatically. | +| **extend** | Extends the component catalogue. See [extending](/advanced/extending) | +| **raycaster** | the global raycaster used for pointer events | +| **registerCamera** | a method to register a camera. This is only required if you manually create a camera. Cameras in the template are registered automatically. | +| **renderer** | the [WebGLRenderer](https://threejs.org/docs/#api/en/renderers/WebGLRenderer) of your scene | +| **scene** | the [scene](https://threejs.org/docs/?q=sce#api/en/scenes/Scene). | +| **setCameraActive** | a method to set a camera active | +| **sizes** | contains width, height and aspect ratio of your canvas | +| **invalidate** | a method to invalidate the render loop. This is only required if you set the `render-mode` prop to `on-demand`. | +| **advance** | a method to advance the render loop. This is only required if you set the `render-mode` prop to `manual`. | +| **loop** | the renderer loop | + +### useLoop + +This composable allows you to execute a callback on every rendered frame, similar to `useRenderLoop` but unique to each `TresCanvas` instance and with access to the [context](#usetrescontext). + ::: warning -Be mindful of the performance implications of using this composable. It will run at every frame, so if you have a lot of logic in your callback, it might impact the performance of your app. Specially if you are updating reactive states or references. +`useLoop` can be only be used inside of a `TresCanvas` since this component acts as the provider for the context data. ::: -The `onLoop` callback receives an object with the following properties based on the [THREE clock](https://threejs.org/docs/?q=clock#api/en/core/Clock): +#### Register update callbacks -- `delta`: The delta time between the current and the last frame. This is the time in seconds since the last frame. -- `elapsed`: The elapsed time since the start of the render loop. +The user can register update callbacks (such as animations, fbo, etc) using the `onBeforeRender` -This composable is based on `useRafFn` from [vueuse](https://vueuse.org/core/useRafFn/). Thanks to [@wheatjs](https://github.com/wheatjs) for the amazing contribution. +::: code-group -### Before and after render +```vue [App.vue] + -You can also register a callback that will be called before and after the renderer updates the scene. This is useful if you add a profiler to measure the FPS for example. + +``` + +```vue [AnimatedBox.vue] + + + +``` + +::: + +Your callback function will be triggered just before a frame is rendered and it will be deregistered automatically when the component is destroyed. + +#### Take over the render loop + +You can take over the render call by using the `render` method. ```ts -const { onBeforeLoop, onAfterLoop } = useRenderLoop() +const { render } = useLoop() -onBeforeLoop(({ delta, elapsed }) => { - // I will run before the renderer updates the scene - fps.begin() +render(({ renderer, scene, camera }) => { + renderer.render(scene, camera) }) +``` -onAfterLoop(({ delta, elapsed }) => { - // I will run after the renderer updates the scene - fps.end() +::: warning +Consider that if you take over the render loop, you will need to manually render the scene and take care of features like the conditional rendering yourself. +::: + +#### Register after render callbacks (ex physics calculations) + +You can also register callbacks which are invoked after rendring by using the `onAfterRender` method. + +```ts +const { onAfterRender } = useLoop() + +onAfterRender(({ renderer }) => { + // Calculations }) ``` -### Pause and resume +#### Render priority -You can pause and resume the render loop using the exposed `pause` and `resume` methods. +Both useBeforeRender and useAfteRender provide an optional priority number. This number could be anything from `Number.NEGATIVE_INFINITY` to `Number.POSITIVE_INFINITY` being the 0 by default. The lower the number, the earlier the callback will be executed. ```ts -const { pause, resume } = useRenderLoop() +onBeforeRender(() => { + console.count('triggered first') +}, -1) -// Pause the render loop -pause() +onBeforeRender(() => { + console.count('triggered second') +}, 1) +``` -// Resume the render loop -resume() +#### Params of the callback + +All callbacks receive an object with the following properties: + +- `delta`: The delta time between the current and the last frame. This is the time in miliseconds since the last frame. +- `elapsed`: The elapsed time since the start of the render loop. +- `clock`: The [THREE clock](https://threejs.org/docs/?q=clock#api/en/core/Clock) instance. +- `renderer`: The [WebGLRenderer](https://threejs.org/docs/#api/en/renderers/WebGLRenderer) of your scene. +- `scene`: The [scene](https://threejs.org/docs/?q=sce#api/en/scenes/Scene) of your scene. +- `camera`: The currently active camera. +- `raycaster`: The global raycaster used for pointer events. +- `controls`: The controls of your scene. +- `invalidate`: A method to invalidate the render loop. This is only required if you set the `render-mode` prop to `on-demand`. +- `advance`: A method to advance the render loop. This is only required if you set the `render-mode` prop to `manual`. + +#### Pausing and resuming the update loop + +You can use `pause` and `resume` methods: + +```ts +const { onBeforeRender, pause, resume } = useLoop() + +onBeforeRender(({ elapsed }) => { + sphereRef.value.position.y += Math.sin(elapsed) * 0.01 +}) + +pause() // This will pause the loop +resume() // This will resume the loop ``` -Also you can get the active state of the render loop using the `isActive` property. +#### Pausing and resuming the render + +You can use `pauseRender` and `resumeRender` methods: ```ts -const { resume, isActive } = useRenderLoop() +const { pauseRender, resumeRender } = useLoop() -console.log(isActive) // false +onBeforeRender(({ elapse }) => { + sphereRef.value.position.y += Math.sin(elapsed) * 0.01 +}) -resume() +pauseRender() // This will pause the renderer +resumeRender() // This will resume the renderer +``` -console.log(isActive) // true +#### Unregistering callbacks + +You can unregister a callback by calling the method `off` returned by the `onBeforeRender` or `onAfterRender` method. + +```ts +const { onBeforeRender } = useLoop() + +const { off } = onBeforeRender(({ elapsed }) => { + sphereRef.value.position.y += Math.sin(elapsed) * 0.01 +}) ``` ## useLoader @@ -197,44 +337,73 @@ watch(character, ({ model }) => { }) ``` -## useTresContext -This composable aims to provide access to the state model which contains multiple useful properties. +## useRenderLoop + +The `useRenderLoop` composable can be use for animations that don't require access to the [context](#usetrescontext). It allows you to register a callback that will be called on native refresh rate. + +::: warning + Since v4.0.0, `useRenderLoop` is no longer used internally to control the rendering, if you want to use conditional rendering, multiple canvases or need access to state please `useLoop` instead. [Read why](#useloop) +::: ```ts -const { camera, renderer, camera, cameras } = useTresContext() +const { onLoop, resume } = useRenderLoop() + +onLoop(({ delta, elapsed, clock }) => { + // I will run at every frame ~60FPS (depending of your monitor) +}) ``` ::: warning -`useTresContext` can be only be used inside of a `TresCanvas` since `TresCanvas` acts as the provider for the context data. Use [the context exposed by TresCanvas](tres-canvas#exposed-public-properties) if you find yourself needing it in parent components of TresCanvas. +Be mindful of the performance implications of using this composable. It will run at every frame, so if you have a lot of logic in your callback, it might impact the performance of your app. Specially if you are updating reactive states or references. ::: -```vue - - - +The `onLoop` callback receives an object with the following properties based on the [THREE clock](https://threejs.org/docs/?q=clock#api/en/core/Clock): + +- `delta`: The delta time between the current and the last frame. This is the time in milliseconds since the last frame. +- `elapsed`: The elapsed time since the start of the render loop. + +This composable is based on `useRafFn` from [vueuse](https://vueuse.org/core/useRafFn/). Thanks to [@wheatjs](https://github.com/wheatjs) for the amazing contribution. + +### Before and after render + +You can also register a callback that will be called before and after the renderer updates the scene. This is useful if you add a profiler to measure the FPS for example. + +```ts +const { onBeforeLoop, onAfterLoop } = useRenderLoop() + +onBeforeLoop(({ delta, elapsed }) => { + // I will run before the renderer updates the scene + fps.begin() +}) + +onAfterLoop(({ delta, elapsed }) => { + // I will run after the renderer updates the scene + fps.end() +}) ``` -```vue -// MyModel.vue +### Pause and resume - +```ts +const { pause, resume } = useRenderLoop() + +// Pause the render loop +pause() + +// Resume the render loop +resume() ``` -### Properties of context -| Property | Description | -| --- | --- | -| **camera** | The currently active camera | -| **cameras** | The cameras that exist in the scene | -| **controls** | The controls of your scene | -| **deregisterCamera** | A method to deregister a camera. This is only required if you manually create a camera. Cameras in the template are deregistered automatically. | -| **extend** | Extends the component catalogue. See [extending](/advanced/extending) | -| **raycaster** | The global raycaster used for pointer events | -| **registerCamera** | A method to register a camera. This is only required if you manually create a camera. Cameras in the template are registered automatically. | -| **renderer** | The [WebGLRenderer](https://threejs.org/docs/#api/en/renderers/WebGLRenderer) of your scene | -| **scene** | The [scene](https://threejs.org/docs/?q=sce#api/en/scenes/Scene). | -| **setCameraActive** | A method to set a camera active | -| **sizes** | Contains width, height and aspect ratio of your canvas | +Also you can get the active state of the render loop using the `isActive` property. + +```ts +const { resume, isActive } = useRenderLoop() + +console.log(isActive.value) // false + +resume() + +console.log(isActive.value) // true +``` diff --git a/docs/api/events.md b/docs/api/events.md index c83019df8..b2e874981 100644 --- a/docs/api/events.md +++ b/docs/api/events.md @@ -6,22 +6,62 @@ ## Pointer Events +The following pointer events are available on `v3` and previous: + +- `click` +- `pointer-move` +- `pointer-enter` +- `pointer-leave` + +From `v4.x` on, the following pointer events are been added to the list: + +- `context-menu` (right click) +- `double-click` +- `pointer-down` +- `pointer-up` +- `wheel` +- `pointer-missed` + ```html ``` -| Event | fires when ... | Event Handler Parameter Type(s) | -| ------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| click | ... the events pointerdown and pointerup fired on the same object one after the other | [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16), [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | -| pointer-move | ... the pointer is moving above the object | [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16), [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | -| pointer-enter | ... the pointer is entering the object | [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16), [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | -| pointer-leave | ... the pointer is leaves the object | [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | +|
Event
| fires when ... | Event Handler Parameter Type(s) | +| ---------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| click | the events pointerdown and pointerup fired on the same object one after the other | [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16), [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | +| contextMenu | the user triggers a context menu, often by right-clicking | [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | +| double-click | the user clicks the mouse button twice in quick succession on the same object | [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16), [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | +| wheel | the mouse wheel or similar device is rotated | [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent) | +| pointer-down | the pointer is pressed down over the object | [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16), [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | +| pointer-up | the pointer is released over the object | [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16), [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | +| pointer-leave | the pointer is leaves the object | [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | +| pointer-move | the pointer is moving above the object | [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16), [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | +| pointer-missed | the pointer interaction is attempted but misses the object | [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) | + +## Event Propagation (Bubbling đŸĢ§) + +Propagation of events on 3D scenes works differently than in the DOM because objects can **occlude each other in 3D**. The `intersections` array contains all the objects that the raycaster intersects with, sorted by distance from the camera. The first object in the array is the closest one to the camera. -The returned [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16) includes the [Object3D](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D) that triggered the event. You can access it via `intersection.object`. +When an event is triggered, the event is propagated to the closest object in the `intersections` array. If the event is not handled by the object, it will be propagated to the next object in the array. -By default, objects positioned in front of others with event handlers do not prevent those events from being triggered. This behavior can be achieved by using the prop `blocks-pointer-events`. +`event.stopPropagation()` can be used to stop the event from propagating to the next object in the array, stoping the event from bubbling up and reaching to farther objects (the oens behind the first one). + +```html + +``` diff --git a/docs/api/tres-canvas.md b/docs/api/tres-canvas.md index 6aa8fa612..71677b7e4 100644 --- a/docs/api/tres-canvas.md +++ b/docs/api/tres-canvas.md @@ -77,12 +77,13 @@ renderer.shadowMap.type = PCFSoftShadowMap | **clearColor** | The color the renderer will use to clear the canvas. | `#000000` | | **context** | This can be used to attach the renderer to an existing [RenderingContext](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext) | | | **depth** | Whether the drawing buffer has a [depth buffer](https://en.wikipedia.org/wiki/Z-buffering) of at least 16 bits. | `true` | +| **renderMode** | Render mode, can be `always`, `on-demand` or `manual`. See [Performance](../advanced/performance) | `always` | | **disableRender** | Disable render on requestAnimationFrame, useful for PostProcessing | `false` | | **failIfMajorPerformanceCaveat** | Whether the renderer creation will fail upon low performance is detected. See [WebGL spec](https://registry.khronos.org/webgl/specs/latest/1.0/#5.2) for details. | `false` | | **logarithmicDepthBuffer** | Whether to use a logarithmic depth buffer. It may be necessary to use this if dealing with huge differences in scale in a single scene. Note that this setting uses gl_FragDepth if available which disables the [Early Fragment Test](https://www.khronos.org/opengl/wiki/Early_Fragment_Test) optimization and can cause a decrease in performance. | `false` | | **outputColorSpace** | Defines the output encoding | `LinearEncoding` | -| **powerPreference** | Provides a hint to the user agent indicating what configuration of GPU is suitable for this WebGL context. Can be "high-performance", "low-power" or "default". | `default` | -| **precision** | Shader precision. Can be "highp", "mediump" or "lowp". | "highp" if supported by the device | +| **powerPreference** | Provides a hint to the user agent indicating what configuration of GPU is suitable for this WebGL context. Can be `high-performance`, `low-power` or `default`. | `default` | +| **precision** | Shader precision. Can be `highp`, `mediump` or `lowp`. | "highp" if supported by the device | | **premultipliedAlpha** | Whether the renderer will assume that colors have [premultiplied alpha](https://en.wikipedia.org/wiki/Glossary_of_computer_graphics#premultiplied_alpha). | `true` | | **preserveDrawingBuffer** | Whether to preserve the buffers until manually cleared or overwritten.. | `false` | | **shadows** | Enable shadows in the renderer | `false` | diff --git a/docs/components.d.ts b/docs/components.d.ts index 4368e2a4d..a67752554 100644 --- a/docs/components.d.ts +++ b/docs/components.d.ts @@ -1,21 +1,25 @@ /* eslint-disable */ -/* prettier-ignore */ // @ts-nocheck // Generated by unplugin-vue-components // Read more: https://github.com/vuejs/core/pull/3399 export {} +/* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + BlenderCube: typeof import('./.vitepress/theme/components/BlenderCube.vue')['default'] Cookbook: typeof import('./.vitepress/theme/components/Cookbook.vue')['default'] DonutExample: typeof import('./.vitepress/theme/components/DonutExample.vue')['default'] EmbedExperiment: typeof import('./.vitepress/theme/components/EmbedExperiment.vue')['default'] ExtendExample: typeof import('./.vitepress/theme/components/ExtendExample.vue')['default'] FirstScene: typeof import('./.vitepress/theme/components/FirstScene.vue')['default'] FirstSceneLightToon: typeof import('./.vitepress/theme/components/FirstSceneLightToon.vue')['default'] + GraphPane: typeof import('./.vitepress/theme/components/GraphPane.vue')['default'] HomeSponsors: typeof import('./.vitepress/theme/components/HomeSponsors.vue')['default'] LocalOrbitControls: typeof import('./.vitepress/theme/components/LocalOrbitControls.vue')['default'] LoveVueThreeJS: typeof import('./.vitepress/theme/components/LoveVueThreeJS.vue')['default'] + OnDemandRendering: typeof import('./.vitepress/theme/components/OnDemandRendering.vue')['default'] + RenderingLogger: typeof import('./.vitepress/theme/components/RenderingLogger.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] SandboxDemo: typeof import('./.vitepress/theme/components/SandboxDemo.vue')['default'] diff --git a/docs/cookbook/basic-animations.md b/docs/cookbook/basic-animations.md index 79e1f31c9..2b2d81c9a 100644 --- a/docs/cookbook/basic-animations.md +++ b/docs/cookbook/basic-animations.md @@ -12,54 +12,68 @@ This guide will help you get started with basic animations in TresJS. We will build a simple scene with a cube. We will then animate the cube to rotate around the Y and Z axis. - + -## useRenderLoop + -The `useRenderLoop` composable is the core of TresJS animations. It allows you to register a callback that will be called every time the renderer updates the scene with the browser's refresh rate. +## useLoop -To see a detailed explanation of how it works, please refer to the [useRenderLoop](/api/composables#userenderloop) documentation. +The `useLoop` composable is the core of TresJS updates, which includes: **animations**. It allows you to register a callback that will be called every time the renderer updates the scene with the browser's refresh rate. + +To see a detailed explanation of how it works, please refer to the [useRenderLoop](/api/composables#useloop) documentation. ```ts -const { onLoop } = useRenderLoop() +const { onBeforeRender } = useLoop() -onLoop(({ delta, elapsed }) => { +onBeforeRender(({ delta, elapsed }) => { // I will run at every frame ~ 60FPS (depending of your monitor) }) ``` ## Getting the reference to the cube -To animate the cube, we need to get a reference to it. We can do it by passing a [Template Ref](https://vuejs.org/guide/essentials/template-refs.html) using `ref` prop to the `TresMesh` component. This will return the THREE instance. +To animate the cube, we need to get a reference to it. We can do it by passing a [Template Ref](https://vuejs.org/guide/essentials/template-refs.html) using `ref` prop to the `TresMesh` component. This will return the plain `THREE instance`. -To improve the performance, we will use a [Shallow Ref](https://v3.vuejs.org/guide/reactivity-fundamentals.html#shallow-reactivity) to store the reference instead of a regular Ref. See why [here](../advanced/caveats.md#reactivity) +::: code-group -```vue - + + +``` -const boxRef: ShallowRef = shallowRef(null) +```vue [App.vue] + ``` +::: ## Animating the cube -Now that we have a reference to the cube, we can animate it. We will use the `onLoop` callback to update the cube's rotation. +Now that we have a reference to the cube, we can animate it. We will use the `onBeforeRender` method to update the cube's rotation. ```ts -onLoop(({ delta, elapsed }) => { +const { onBeforeRender } = useLoop() + +onBeforeRender(({ delta, elapsed }) => { if (boxRef.value) { boxRef.value.rotation.y += delta boxRef.value.rotation.z = elapsed * 0.2 @@ -73,18 +87,14 @@ You can also use the `delta` from the internal [THREE clock](https://threejs.org You might be wondering why we are not using reactivity to animate the cube. The answer is simple, performance. -```vue +```ts // This is a bad idea ❌ - ``` We can be tempted to use reactivity to animate the cube. But it would be a bad idea. diff --git a/docs/debug/devtools.md b/docs/debug/devtools.md index cc9b854e1..53fcecd41 100644 --- a/docs/debug/devtools.md +++ b/docs/debug/devtools.md @@ -24,3 +24,9 @@ From we are introducing the TresJS Devtools, a customize ![](/devtools-scene-inspector.png) Enjoy the new Devtools and let us know what you think! 🎉 + +## Renderer info + +From `v4` it's possible to see the renderer information in the Devtools when inspecting the root object (Scene). This is useful to know what renderer is being used and its properties including the programs (shaders) and the capabilities of the renderer. + +![](/devtools-v4.png) diff --git a/docs/directives/v-always-look-at.md b/docs/directives/v-always-look-at.md index fb0cb272b..b0c97e1f2 100644 --- a/docs/directives/v-always-look-at.md +++ b/docs/directives/v-always-look-at.md @@ -1,4 +1,8 @@ -# v-always-look-at 👀 +# v-always-look-at 👀 + +::: warning +This directive has been removed on the `v4` due incompatibility with the new renderer loop. +::: With the new directive v-always-look-at provided by **TresJS**, you can add easily command an [Object3D](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D) to always look at a specific position, this could be passed as a Vector3 or an Array. diff --git a/docs/directives/v-light-helper.md b/docs/directives/v-light-helper.md index e3193764e..b3d266edb 100644 --- a/docs/directives/v-light-helper.md +++ b/docs/directives/v-light-helper.md @@ -7,6 +7,7 @@ The following lights are supported: - PointLight - SpotLight - HemisphereLight +- RectAreaLightHelper ## Usage diff --git a/docs/directives/v-rotate.md b/docs/directives/v-rotate.md index 9f71795eb..a313ff71f 100644 --- a/docs/directives/v-rotate.md +++ b/docs/directives/v-rotate.md @@ -1,4 +1,8 @@ -# v-rotate +# v-rotate + +::: warning +This directive has been removed on the `v4` due incompatibility with the new renderer loop. +::: ## Problem diff --git a/docs/guide/index.md b/docs/guide/index.md index 13a2b2049..a42f418ec 100644 --- a/docs/guide/index.md +++ b/docs/guide/index.md @@ -87,25 +87,8 @@ We also have a showcase lab of examples made with TresJS. Check it out [here](ht The React ecosystem has an impressive **custom render** solution called [React-three-fiber](https://docs.pmnd.rs/react-three-fiber) that allows you build your scenes declaratively with re-usable, self-contained components that react to state. -In my search for something similar in the VueJS ecosystem, I found this amazing library called [Lunchbox](https://github.com/breakfast-studio/lunchboxjs), which works with the same concept as R3F, it provides a [custom Vue3 Renderer](https://vuejs.org/api/custom-renderer.html). I'm also contributing to improve this library so it gets as mature and feature-rich as R3F. +In my search for something similar in the VueJS ecosystem, I found this amazing library called [Lunchbox](https://github.com/breakfast-studio/lunchboxjs), which works with the same concept as R3F, it provides a [custom Vue3 Renderer](https://vuejs.org/api/custom-renderer.html). -The only issue with this is, mixing compilers renderers in Vue 3 is something the Vue community is still working on - see [here](https://github.com/vuejs/vue-loader/pull/1645) for more information. +But none of them was actively maintained or had the same level of abstraction as R3F. -```ts -// Example Vite setup -import { createApp } from 'vue' -import { createApp as createLunchboxApp } from 'lunchboxjs' -import App from './App.vue' -import LunchboxApp from './LunchboxApp.vue' - -// html app -const app = createApp(App) -app.mount('#app') - -// lunchbox app -const lunchboxApp = createLunchboxApp(LunchboxApp) -// assuming there's an element with ID `lunchbox` in your HTML app -lunchboxApp.mount('#lunchbox') -``` - -So I was inspired by both libraries to create a Vue custom renderer for ThreeJS. That's **TresJS v2**. +So I was inspired by both libraries to create a Vue custom renderer for ThreeJS. That's **TresJS**. diff --git a/docs/package.json b/docs/package.json index 33675a58c..33bde1079 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,12 +9,12 @@ "preview": "vitepress preview" }, "dependencies": { - "@tresjs/core": "workspace:*" + "@tresjs/core": "workspace:^" }, "devDependencies": { "@iconify-json/logos": "^1.1.42", "@iconify-json/mdi": "^1.1.66", - "unocss": "^0.59.4", + "unocss": "^0.60.3", "vite-svg-loader": "^5.1.0" } } diff --git a/docs/public/devtools-v4.png b/docs/public/devtools-v4.png new file mode 100644 index 0000000000000000000000000000000000000000..36b79be870f76bb0caf5419335e036308d182a06 GIT binary patch literal 62379 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 - - - - + + + + + + + + + \ No newline at end of file diff --git a/package.json b/package.json index 7fb905aa1..01c1ede05 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@tresjs/core", "type": "module", - "version": "3.9.0", + "version": "4.0.0-rc.2", "packageManager": "pnpm@8.15.6", "description": "Declarative ThreeJS using Vue Components", "author": "Alvaro Saburido (https://github.com/alvarosabu/)", @@ -50,7 +50,7 @@ "playground": "cd playground && npm run dev", "test": "vitest", "test:ci": "vitest run", - "test:ui": "vitest --ui", + "test:ui": "vitest --ui --coverage.enabled=true", "release": "release-it", "coverage": "vitest run --coverage", "lint": "eslint .", @@ -64,52 +64,52 @@ }, "peerDependencies": { "three": ">=0.133", - "vue": ">=3.3" + "vue": ">=3.4" }, "dependencies": { - "@alvarosabu/utils": "^3.1.1", + "@alvarosabu/utils": "^3.2.0", "@vue/devtools-api": "^6.6.1", - "@vueuse/core": "^10.7.0" + "@vueuse/core": "^10.10.0" }, "devDependencies": { "@release-it/conventional-changelog": "^8.0.1", - "@stackblitz/sdk": "^1.9.0", - "@tresjs/cientos": "3.8.0", - "@tresjs/eslint-config": "^1.0.0", - "@tresjs/eslint-config-vue": "^0.2.1", - "@types/three": "^0.163.0", - "@typescript-eslint/eslint-plugin": "^7.7.1", - "@typescript-eslint/parser": "^7.7.1", + "@stackblitz/sdk": "^1.10.0", + "@tresjs/cientos": "3.9.0", + "@tresjs/eslint-config": "^1.1.0", + "@types/three": "^0.164.1", + "@typescript-eslint/eslint-plugin": "^7.11.0", + "@typescript-eslint/parser": "^7.11.0", "@vitejs/plugin-vue": "^5.0.4", "@vitest/coverage-c8": "^0.33.0", - "@vitest/ui": "^1.5.0", - "@vue/test-utils": "^2.4.3", - "eslint": "^9.1.1", - "eslint-plugin-vue": "^9.25.0", - "esno": "^4.0.0", - "gsap": "^3.12.3", + "@vitest/coverage-v8": "^1.6.0", + "@vitest/ui": "^1.6.0", + "@vue/test-utils": "^2.4.6", + "eslint": "^9.3.0", + "eslint-plugin-vue": "^9.26.0", + "esno": "^4.7.0", + "gsap": "^3.12.5", "husky": "^9.0.11", - "jsdom": "^24.0.0", + "jsdom": "^24.1.0", "kolorist": "^1.8.0", "ohmyfetch": "^0.4.21", - "pathe": "^1.1.1", - "release-it": "^17.2.0", + "pathe": "^1.1.2", + "release-it": "^17.3.0", "rollup-plugin-analyzer": "^4.0.0", "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-visualizer": "^5.11.0", - "three": "^0.163.0", - "unocss": "^0.59.4", + "rollup-plugin-visualizer": "^5.12.0", + "three": "^0.164.1", + "unocss": "^0.60.3", "unplugin": "^1.10.1", - "unplugin-vue-components": "^0.26.0", - "vite": "^5.2.10", + "unplugin-vue-components": "^0.27.0", + "vite": "^5.2.12", "vite-plugin-banner": "^0.7.1", - "vite-plugin-dts": "3.9.0", + "vite-plugin-dts": "3.9.1", "vite-plugin-inspect": "^0.8.4", "vite-plugin-require-transform": "^1.0.21", "vite-svg-loader": "^5.1.0", - "vitepress": "1.1.3", - "vitest": "^1.5.0", - "vue": "^3.4.24", - "vue-demi": "^0.14.6" + "vitepress": "1.2.2", + "vitest": "^1.6.0", + "vue": "^3.4.27", + "vue-demi": "^0.14.7" } } diff --git a/playground/components.d.ts b/playground/components.d.ts index 33c0bcd9c..4e1478c2a 100644 --- a/playground/components.d.ts +++ b/playground/components.d.ts @@ -1,49 +1,28 @@ /* eslint-disable */ -/* prettier-ignore */ // @ts-nocheck // Generated by unplugin-vue-components // Read more: https://github.com/vuejs/core/pull/3399 export {} +/* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { - AnimatedModel: typeof import('./src/components/AnimatedModel.vue')['default'] - CameraOperator: typeof import('./src/components/CameraOperator.vue')['default'] - Cameras: typeof import('./src/components/Cameras.vue')['default'] - copy: typeof import('./src/components/TheBasic copy.vue')['default'] - DanielTest: typeof import('./src/components/DanielTest.vue')['default'] - DebugUI: typeof import('./src/components/DebugUI.vue')['default'] - DeleteMe: typeof import('./src/components/DeleteMe.vue')['default'] - FBXModels: typeof import('./src/components/FBXModels.vue')['default'] - Gltf: typeof import('./src/components/gltf/index.vue')['default'] + AkuAku: typeof import('./src/components/AkuAku.vue')['default'] + AnimatedObjectUseUpdate: typeof import('./src/components/AnimatedObjectUseUpdate.vue')['default'] + BlenderCube: typeof import('./src/components/BlenderCube.vue')['default'] + Box: typeof import('./src/components/Box.vue')['default'] + DirectiveSubComponent: typeof import('./src/components/DirectiveSubComponent.vue')['default'] + DynamicModel: typeof import('./src/components/DynamicModel.vue')['default'] + FBOCube: typeof import('./src/components/FBOCube.vue')['default'] + GraphPane: typeof import('./src/components/GraphPane.vue')['default'] LocalOrbitControls: typeof import('./src/components/LocalOrbitControls.vue')['default'] - MeshWobbleMaterial: typeof import('./src/components/meshWobbleMaterial/index.vue')['default'] - MultipleCanvas: typeof import('./src/components/MultipleCanvas.vue')['default'] - PortalJourney: typeof import('./src/components/portal-journey/index.vue')['default'] - Responsiveness: typeof import('./src/components/Responsiveness.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] - ShadersExperiment: typeof import('./src/components/shaders-experiment/index.vue')['default'] + TakeOverLoopExperience: typeof import('./src/components/TakeOverLoopExperience.vue')['default'] TestSphere: typeof import('./src/components/TestSphere.vue')['default'] Text3D: typeof import('./src/components/Text3D.vue')['default'] - TheBasic: typeof import('./src/components/TheBasic.vue')['default'] TheCameraOperator: typeof import('./src/components/TheCameraOperator.vue')['default'] - TheConditional: typeof import('./src/components/TheConditional.vue')['default'] - TheEnvironment: typeof import('./src/components/TheEnvironment.vue')['default'] - TheEvents: typeof import('./src/components/TheEvents.vue')['default'] TheExperience: typeof import('./src/components/TheExperience.vue')['default'] - TheFireFlies: typeof import('./src/components/portal-journey/TheFireFlies.vue')['default'] - TheFirstScene: typeof import('./src/components/TheFirstScene.vue')['default'] - TheGizmos: typeof import('./src/components/TheGizmos.vue')['default'] - TheGroups: typeof import('./src/components/TheGroups.vue')['default'] - TheModel: typeof import('./src/components/gltf/TheModel.vue')['default'] - TheParticles: typeof import('./src/components/TheParticles.vue')['default'] - ThePortal: typeof import('./src/components/portal-journey/ThePortal.vue')['default'] - TheSmallExperience: typeof import('./src/components/TheSmallExperience.vue')['default'] TheSphere: typeof import('./src/components/TheSphere.vue')['default'] - TheUSDZModel: typeof import('./src/components/udsz/TheUSDZModel.vue')['default'] - TresLechesTest: typeof import('./src/components/TresLechesTest.vue')['default'] - Udsz: typeof import('./src/components/udsz/index.vue')['default'] - VectorSetProps: typeof import('./src/components/VectorSetProps.vue')['default'] } } diff --git a/playground/package.json b/playground/package.json index c2676be52..49ab2186f 100644 --- a/playground/package.json +++ b/playground/package.json @@ -5,11 +5,11 @@ "private": true, "scripts": { "dev": "vite --host", - "build": "vue-tsc && vite build", + "build": "vite build", "preview": "vite preview" }, "dependencies": { - "@tresjs/cientos": "3.8.0", + "@tresjs/cientos": "3.9.0", "@tresjs/core": "workspace:^", "vue-router": "^4.3.2" }, @@ -19,7 +19,7 @@ "unplugin-auto-import": "^0.17.2", "vite-plugin-glsl": "^1.2.1", "vite-plugin-qrcode": "^0.2.3", - "vite-plugin-vue-devtools": "7.1.2", - "vue-tsc": "^2.0.14" + "vite-plugin-vue-devtools": "7.2.1", + "vue-tsc": "^2.0.19" } } diff --git a/playground/public/logo.svg b/playground/public/logo.svg new file mode 100644 index 000000000..035ffd955 --- /dev/null +++ b/playground/public/logo.svg @@ -0,0 +1,27 @@ + + + + + + + + + \ No newline at end of file diff --git a/playground/src/App.vue b/playground/src/App.vue index c0482986b..1db6204f5 100644 --- a/playground/src/App.vue +++ b/playground/src/App.vue @@ -1,6 +1,17 @@ diff --git a/playground/src/components/AkuAku.vue b/playground/src/components/AkuAku.vue new file mode 100644 index 000000000..da35e1437 --- /dev/null +++ b/playground/src/components/AkuAku.vue @@ -0,0 +1,17 @@ + + + diff --git a/playground/src/components/AnimatedObjectUseUpdate.vue b/playground/src/components/AnimatedObjectUseUpdate.vue new file mode 100644 index 000000000..c775616c1 --- /dev/null +++ b/playground/src/components/AnimatedObjectUseUpdate.vue @@ -0,0 +1,95 @@ + + + + diff --git a/playground/src/components/BlenderCube.vue b/playground/src/components/BlenderCube.vue new file mode 100644 index 000000000..66b7630b7 --- /dev/null +++ b/playground/src/components/BlenderCube.vue @@ -0,0 +1,29 @@ + + + diff --git a/playground/src/components/Box.vue b/playground/src/components/Box.vue new file mode 100644 index 000000000..02f1a097e --- /dev/null +++ b/playground/src/components/Box.vue @@ -0,0 +1,44 @@ + + + diff --git a/playground/src/components/DirectiveSubComponent.vue b/playground/src/components/DirectiveSubComponent.vue new file mode 100644 index 000000000..ec4b9a4ff --- /dev/null +++ b/playground/src/components/DirectiveSubComponent.vue @@ -0,0 +1,7 @@ + + + diff --git a/playground/src/components/DynamicModel.vue b/playground/src/components/DynamicModel.vue new file mode 100644 index 000000000..bb99cee45 --- /dev/null +++ b/playground/src/components/DynamicModel.vue @@ -0,0 +1,22 @@ + + + diff --git a/playground/src/components/FBOCube.vue b/playground/src/components/FBOCube.vue new file mode 100644 index 000000000..f6ba89fe4 --- /dev/null +++ b/playground/src/components/FBOCube.vue @@ -0,0 +1,27 @@ + + + + diff --git a/playground/src/components/GraphPane.vue b/playground/src/components/GraphPane.vue new file mode 100644 index 000000000..79110e0f2 --- /dev/null +++ b/playground/src/components/GraphPane.vue @@ -0,0 +1,101 @@ + + + diff --git a/playground/src/components/MultipleCanvas.vue b/playground/src/components/MultipleCanvas.vue deleted file mode 100644 index 5006e17d6..000000000 --- a/playground/src/components/MultipleCanvas.vue +++ /dev/null @@ -1,98 +0,0 @@ - - - diff --git a/playground/src/components/TakeOverLoopExperience.vue b/playground/src/components/TakeOverLoopExperience.vue new file mode 100644 index 000000000..5acf39670 --- /dev/null +++ b/playground/src/components/TakeOverLoopExperience.vue @@ -0,0 +1,57 @@ + + + diff --git a/playground/src/components/TheExperience.vue b/playground/src/components/TheExperience.vue index 6a6d42e4d..b579e613a 100644 --- a/playground/src/components/TheExperience.vue +++ b/playground/src/components/TheExperience.vue @@ -1,34 +1,26 @@ @@ -39,23 +31,27 @@ watchEffect(() => { v-bind="gl" ref="canvas" class="awiwi" - :style="{ background: '#008080' }" > + @@ -66,22 +62,20 @@ watchEffect(() => { /> - - + + - + + diff --git a/playground/src/components/TheSphere.vue b/playground/src/components/TheSphere.vue index 9c1761fcc..e5d6e2107 100644 --- a/playground/src/components/TheSphere.vue +++ b/playground/src/components/TheSphere.vue @@ -1,7 +1,21 @@ - + +