1
0
Эх сурвалжийг харах

docs: add Tweakpane integration for interactive 3D controls

alvarosabu 1 долоо хоног өмнө
parent
commit
0e9755c609

+ 111 - 0
docs/app/components/examples/tweakpane/TweakpaneDemo.vue

@@ -0,0 +1,111 @@
+<script setup lang="ts">
+import { onMounted, onUnmounted, ref } from 'vue'
+import { OrbitControls } from '@tresjs/cientos'
+import { Pane } from 'tweakpane'
+
+const props = defineProps<{
+  paneContainer: HTMLElement | undefined
+}>()
+
+const meshRef = ref()
+const pane = ref<Pane>()
+
+const controls = ref({
+  positionX: 0,
+  positionY: 0,
+  positionZ: 0,
+  rotationX: 0,
+  rotationY: 0,
+  rotationZ: 0,
+  scale: 1,
+  color: '#ff6b6b',
+  wireframe: false,
+  material: 'basic',
+})
+
+onMounted(() => {
+  if (!props.paneContainer) {
+    return
+  }
+
+  pane.value = new Pane({
+    title: 'TresJS Controls',
+    expanded: true,
+    container: props.paneContainer,
+  })
+
+  // Position controls
+  const positionFolder = pane.value.addFolder({ title: 'Position' })
+  positionFolder.addBinding(controls.value, 'positionX', { min: -3, max: 3, step: 0.1 })
+  positionFolder.addBinding(controls.value, 'positionY', { min: -3, max: 3, step: 0.1 })
+  positionFolder.addBinding(controls.value, 'positionZ', { min: -3, max: 3, step: 0.1 })
+
+  // Rotation controls
+  const rotationFolder = pane.value.addFolder({ title: 'Rotation' })
+  rotationFolder.addBinding(controls.value, 'rotationX', { min: -Math.PI, max: Math.PI, step: 0.01 })
+  rotationFolder.addBinding(controls.value, 'rotationY', { min: -Math.PI, max: Math.PI, step: 0.01 })
+  rotationFolder.addBinding(controls.value, 'rotationZ', { min: -Math.PI, max: Math.PI, step: 0.01 })
+
+  // Material controls
+  const materialFolder = pane.value.addFolder({ title: 'Material' })
+  materialFolder.addBinding(controls.value, 'scale', { min: 0.1, max: 3, step: 0.1 })
+  materialFolder.addBinding(controls.value, 'color')
+  materialFolder.addBinding(controls.value, 'wireframe')
+  materialFolder.addBinding(controls.value, 'material', {
+    options: {
+      Basic: 'basic',
+      Normal: 'normal',
+      Standard: 'standard',
+    },
+  })
+
+  // Reset button
+  pane.value.addButton({ title: 'Reset All' }).on('click', () => {
+    controls.value.positionX = 0
+    controls.value.positionY = 0
+    controls.value.positionZ = 0
+    controls.value.rotationX = 0
+    controls.value.rotationY = 0
+    controls.value.rotationZ = 0
+    controls.value.scale = 1
+    controls.value.color = '#ff6b6b'
+    controls.value.wireframe = false
+    controls.value.material = 'basic'
+  })
+})
+
+onUnmounted(() => {
+  pane.value?.dispose()
+})
+</script>
+
+<template>
+  <OrbitControls />
+
+  <TresMesh
+    ref="meshRef"
+    :position="[controls.positionX, controls.positionY, controls.positionZ]"
+    :rotation="[controls.rotationX, controls.rotationY, controls.rotationZ]"
+    :scale="controls.scale"
+  >
+    <TresBoxGeometry />
+    <TresMeshBasicMaterial
+      v-if="controls.material === 'basic'"
+      :color="controls.color"
+      :wireframe="controls.wireframe"
+    />
+    <TresMeshNormalMaterial
+      v-else-if="controls.material === 'normal'"
+      :wireframe="controls.wireframe"
+    />
+    <TresMeshStandardMaterial
+      v-else-if="controls.material === 'standard'"
+      :color="controls.color"
+      :wireframe="controls.wireframe"
+    />
+  </TresMesh>
+
+  <TresGridHelper :args="[10, 10]" />
+  <TresAmbientLight :intensity="0.5" />
+  <TresDirectionalLight :position="[0, 0, 5]" :intensity="0.5" />
+</template>

+ 17 - 0
docs/app/components/examples/tweakpane/index.vue

@@ -0,0 +1,17 @@
+<script setup lang="ts">
+import { ref } from 'vue'
+import { TresCanvas } from '@tresjs/core'
+import TweakpaneDemo from './TweakpaneDemo.vue'
+
+const paneContainer = ref<HTMLElement>()
+</script>
+
+<template>
+  <SceneWrapper>
+    <div ref="paneContainer" class="absolute top-4 right-4 z-10"></div>
+    <TresCanvas clear-color="#82DBC5">
+      <TresPerspectiveCamera :position="[3, 3, 3]" />
+      <TweakpaneDemo :pane-container="paneContainer" />
+    </TresCanvas>
+  </SceneWrapper>
+</template>

+ 171 - 0
docs/content/4.cookbook/5.tweakpane.md

@@ -0,0 +1,171 @@
+---
+title: Tweakpane
+description: Learn how to integrate Tweakpane with TresJS for interactive 3D controls
+thumbnail: /recipes/tweakpane.png
+---
+
+[Tweakpane](https://tweakpane.github.io/docs/) is a compact GUI library that provides an excellent way to create interactive controls for your 3D scenes. This recipe shows you how to integrate Tweakpane with TresJS to create dynamic, real-time controls for your 3D objects and scenes.
+
+::examples-tweakpane
+::
+
+## Installation
+
+First, install Tweakpane v4 in your project:
+
+```bash
+npm install tweakpane@^4.0.0
+```
+
+::tip
+Make sure to use Tweakpane v4 or higher, as this recipe uses the latest API which has breaking changes from v3.
+::
+
+## Basic Setup
+
+Here's how to set up Tweakpane with a basic TresJS scene:
+
+```vue
+<script setup lang="ts">
+import { ref, onMounted, onUnmounted } from 'vue'
+import { Pane } from 'tweakpane'
+import { OrbitControls } from '@tresjs/cientos'
+
+const meshRef = ref()
+const pane = ref<Pane>()
+const paneContainer = ref<HTMLElement>()
+
+// Reactive properties that will be controlled by Tweakpane
+const controls = ref({
+  positionX: 0,
+  positionY: 0,
+  positionZ: 0,
+  rotationX: 0,
+  rotationY: 0,
+  rotationZ: 0,
+  scale: 1,
+  color: '#ff6b6b',
+  wireframe: false,
+  material: 'basic',
+})
+
+onMounted(() => {
+  if (!paneContainer.value) return
+
+  // Create Tweakpane instance with container
+  pane.value = new Pane({
+    title: 'Scene Controls',
+    container: paneContainer.value
+  })
+
+  // Add position controls
+  const positionFolder = pane.value.addFolder({ title: 'Position' })
+  positionFolder.addBinding(controls.value, 'positionX', { min: -3, max: 3, step: 0.1 })
+  positionFolder.addBinding(controls.value, 'positionY', { min: -3, max: 3, step: 0.1 })
+  positionFolder.addBinding(controls.value, 'positionZ', { min: -3, max: 3, step: 0.1 })
+
+  // Add rotation controls
+  const rotationFolder = pane.value.addFolder({ title: 'Rotation' })
+  rotationFolder.addBinding(controls.value, 'rotationX', { min: -Math.PI, max: Math.PI, step: 0.01 })
+  rotationFolder.addBinding(controls.value, 'rotationY', { min: -Math.PI, max: Math.PI, step: 0.01 })
+  rotationFolder.addBinding(controls.value, 'rotationZ', { min: -Math.PI, max: Math.PI, step: 0.01 })
+
+  // Add material controls
+  const materialFolder = pane.value.addFolder({ title: 'Material' })
+  materialFolder.addBinding(controls.value, 'scale', { min: 0.1, max: 3, step: 0.1 })
+  materialFolder.addBinding(controls.value, 'color')
+  materialFolder.addBinding(controls.value, 'wireframe')
+  materialFolder.addBinding(controls.value, 'material', {
+    options: {
+      Basic: 'basic',
+      Normal: 'normal',
+      Standard: 'standard',
+    },
+  })
+
+  // Add reset button
+  pane.value.addButton({ title: 'Reset All' }).on('click', () => {
+    Object.assign(controls.value, {
+      positionX: 0, positionY: 0, positionZ: 0,
+      rotationX: 0, rotationY: 0, rotationZ: 0,
+      scale: 1, color: '#ff6b6b', wireframe: false, material: 'basic'
+    })
+  })
+})
+
+// Clean up on unmount
+onUnmounted(() => {
+  pane.value?.dispose()
+})
+</script>
+
+<template>
+  <div class="relative w-full h-full">
+    <!-- Tweakpane container positioned outside canvas -->
+    <div ref="paneContainer" class="absolute top-4 right-4 z-10" />
+
+    <TresCanvas clear-color="#82DBC5">
+      <TresPerspectiveCamera :position="[3, 3, 3]" />
+      <OrbitControls />
+
+      <TresMesh
+        ref="meshRef"
+        :position="[controls.positionX, controls.positionY, controls.positionZ]"
+        :rotation="[controls.rotationX, controls.rotationY, controls.rotationZ]"
+        :scale="controls.scale"
+      >
+        <TresBoxGeometry />
+        <TresMeshBasicMaterial
+          v-if="controls.material === 'basic'"
+          :color="controls.color"
+          :wireframe="controls.wireframe"
+        />
+        <TresMeshNormalMaterial
+          v-else-if="controls.material === 'normal'"
+          :wireframe="controls.wireframe"
+        />
+        <TresMeshStandardMaterial
+          v-else-if="controls.material === 'standard'"
+          :color="controls.color"
+          :wireframe="controls.wireframe"
+        />
+      </TresMesh>
+
+      <TresGridHelper :args="[10, 10]" />
+      <TresAmbientLight :intensity="0.5" />
+      <TresDirectionalLight :position="[0, 0, 5]" :intensity="0.5" />
+    </TresCanvas>
+  </div>
+</template>
+```
+
+### Monitoring Values
+
+You can also monitor values without making them editable:
+
+```ts
+const stats = ref({
+  triangles: 0,
+  fps: 0,
+})
+
+const statsFolder = pane.value.addFolder({ title: 'Statistics' })
+statsFolder.addMonitor(stats.value, 'triangles')
+statsFolder.addMonitor(stats.value, 'fps', { interval: 100 })
+```
+
+## Cleanup
+
+Don't forget to dispose of the pane when the component unmounts:
+
+```ts
+import { onUnmounted } from 'vue'
+
+onUnmounted(() => {
+  pane.value?.dispose()
+})
+```
+
+::: tip
+Always dispose of the pane instance to prevent memory leaks, especially in SPAs where components are frequently mounted/unmounted.
+:::

+ 1 - 0
docs/package.json

@@ -24,6 +24,7 @@
     "better-sqlite3": "^12.2.0",
     "nuxt": "^4.0.3",
     "nuxt-llms": "0.1.3",
+    "tweakpane": "^4.0.5",
     "vue": "^3.5.18",
     "vue-router": "^4.5.1"
   },

+ 3 - 0
pnpm-lock.yaml

@@ -186,6 +186,9 @@ importers:
       nuxt-llms:
         specifier: 0.1.3
         version: 0.1.3(magicast@0.3.5)
+      tweakpane:
+        specifier: ^4.0.5
+        version: 4.0.5
       vue:
         specifier: ^3.5.21
         version: 3.5.21(typescript@5.9.2)