Explorar el Código

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
Alvaro Saburido hace 1 año
padre
commit
a6578a7b9a

+ 27 - 5
docs/public/logo.svg

@@ -1,5 +1,27 @@
-<svg width="44" height="10" viewBox="0 0 44 10" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M5.14255 1.42916C5.53095 0.781817 6.46913 0.781816 6.85753 1.42915L11.0913 8.4855C11.4913 9.15203 11.0111 10 10.2338 10H1.76623C0.988935 10 0.508822 9.15203 0.908736 8.4855L5.14255 1.42916Z" fill="#82DBC5"/>
-<rect x="19" y="1" width="9" height="9" rx="1" fill="#4F4F4F"/>
-<circle cx="39.5" cy="5.5" r="4.5" fill="#EFAC35"/>
-</svg>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   width="440"
+   height="102.67172"
+   version="1.1"
+   id="svg154"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs158" />
+  <g
+     id="g442"
+     transform="matrix(7.3336915,0,0,7.3336915,-0.02148824,0)">
+    <path
+       fill="#82dbc5"
+       d="m 6.768,0.75 c 0.6,-1 2.05,-1 2.65,0 l 6.546,10.909 A 1.546,1.546 0 0 1 14.638,14 H 1.548 A 1.546,1.546 0 0 1 0.223,11.659 Z"
+       id="path148" />
+    <path
+       d="M 25.556,0 H 36.444 C 37.306,0 38,0.694 38,1.556 V 12.444 C 38,13.306 37.306,14 36.444,14 H 25.556 A 1.552,1.552 0 0 1 24,12.444 V 1.556 C 24,0.694 24.694,0 25.556,0 Z"
+       style="fill:#4f4f4f;fill-opacity:1"
+       id="path150" />
+    <path
+       d="m 60,7 a 7,7 0 0 1 -7,7 7,7 0 0 1 -7,-7 7,7 0 0 1 7,-7 7,7 0 0 1 7,7 z"
+       style="fill:#efac35"
+       id="path152" />
+  </g>
+</svg>

+ 3 - 0
playground/components.d.ts

@@ -8,6 +8,7 @@ export {}
 declare module 'vue' {
   export interface GlobalComponents {
     AnimatedModel: typeof import('./src/components/AnimatedModel.vue')['default']
+    BlenderCube: typeof import('./src/components/BlenderCube.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']
@@ -17,10 +18,12 @@ declare module 'vue' {
     DynamicModel: typeof import('./src/components/DynamicModel.vue')['default']
     FBXModels: typeof import('./src/components/FBXModels.vue')['default']
     Gltf: typeof import('./src/components/gltf/index.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']
+    RenderingLogger: typeof import('./src/components/RenderingLogger.vue')['default']
     Responsiveness: typeof import('./src/components/Responsiveness.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']

+ 27 - 0
playground/public/logo.svg

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   width="440"
+   height="102.67172"
+   version="1.1"
+   id="svg154"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs158" />
+  <g
+     id="g442"
+     transform="matrix(7.3336915,0,0,7.3336915,-0.02148824,0)">
+    <path
+       fill="#82dbc5"
+       d="m 6.768,0.75 c 0.6,-1 2.05,-1 2.65,0 l 6.546,10.909 A 1.546,1.546 0 0 1 14.638,14 H 1.548 A 1.546,1.546 0 0 1 0.223,11.659 Z"
+       id="path148" />
+    <path
+       d="M 25.556,0 H 36.444 C 37.306,0 38,0.694 38,1.556 V 12.444 C 38,13.306 37.306,14 36.444,14 H 25.556 A 1.552,1.552 0 0 1 24,12.444 V 1.556 C 24,0.694 24.694,0 25.556,0 Z"
+       style="fill:#4f4f4f;fill-opacity:1"
+       id="path150" />
+    <path
+       d="m 60,7 a 7,7 0 0 1 -7,7 7,7 0 0 1 -7,-7 7,7 0 0 1 7,-7 7,7 0 0 1 7,7 z"
+       style="fill:#efac35"
+       id="path152" />
+  </g>
+</svg>

+ 12 - 2
playground/src/App.vue

@@ -1,7 +1,17 @@
 <script setup lang="ts">
+import { watch } from 'vue'
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+function setBodyClass(routeName: string) {
+  document.title = `Core Playground - ${routeName}`
+  document.body.className = routeName
+}
+watch([route], () => setBodyClass(route.name?.toString() ?? ''))
 </script>
 
 <template>
-  <router-view />
+  <Suspense>
+    <router-view />
+  </Suspense>
 </template>
-

+ 18 - 0
playground/src/components/BlenderCube.vue

@@ -0,0 +1,18 @@
+<script setup lang="ts">
+import { useTresContext } from '@tresjs/core'
+import { useGLTF } from '@tresjs/cientos'
+
+const { nodes } = await useGLTF('https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/blender-cube.glb', 
+  { draco: true })
+const model = nodes.Cube
+
+model.position.set(0, 1, 0)
+
+const state = useTresContext()
+
+state.invalidate()
+</script>
+
+<template>
+  <primitive :object="model" />
+</template>

+ 101 - 0
playground/src/components/GraphPane.vue

@@ -0,0 +1,101 @@
+<script lang="ts" setup>
+import { ref } from 'vue'
+import { useRafFn } from '@vueuse/core'
+import { useState } from '../composables/state'
+
+const width = 160
+const height = 40
+const strokeWidth = 2
+const updateInterval = 100 // Update interval in milliseconds
+const topOffset = 0 // Offset from the top
+
+const points = ref('')
+const frameTimes = ref([])
+const maxFrames = ref(width / strokeWidth)
+
+let lastUpdateTime = performance.now()
+
+const { renderingTimes } = useState()
+
+useRafFn(({ timestamp }) => {
+  if (timestamp - lastUpdateTime >= updateInterval) {
+    lastUpdateTime = timestamp
+
+    frameTimes.value.push(renderingTimes?.value)
+    renderingTimes.value = 0
+
+    if (frameTimes.value.length > maxFrames.value) {
+      frameTimes.value.shift()
+    }
+
+    points.value = frameTimes.value
+      .map(
+        (value, index) =>
+          `${index * strokeWidth},${
+            height + topOffset - strokeWidth / 2 - (value * (height + topOffset - strokeWidth)) / 2
+          }`,
+      )
+      .join(' ')
+  }
+})
+</script>
+
+<template>
+  <div
+    class="absolute
+      right-2
+      top-2
+      flex
+      px-4
+      py-1
+      justify-between
+      gap-4
+      items-center
+      mb-2
+      z-10
+      bg-white
+      dark:bg-dark
+      shadow-xl
+      rounded 
+      border-4 
+      border-solid 
+      bg-primary 
+      border-primary 
+      pointer-events-none
+      overflow-hidden"
+  >
+    <label class="text-secondary text-xs w-1/3">Rendering Activity</label>
+
+    <div
+      class="
+        bg-gray-100
+        dark:bg-gray-600
+        relative
+        w-2/3
+        p-1
+        rounded
+        text-right
+        text-xs
+        focus:border-gray-200
+        outline-none
+        border-none
+        font-sans
+      "
+    >
+      <svg
+        :width="width"
+        :height="height"
+        xmlns="http://www.w3.org/2000/svg"
+        fill="none"
+      >
+        <polyline
+          :points="points"
+          stroke="lightgray"
+          :stroke-width="strokeWidth"
+          stroke-linecap="round"
+          stroke-linejoin="round"
+        />
+      </svg>
+    </div>
+  </div>
+</template>

+ 24 - 0
playground/src/components/RenderingLogger.vue

@@ -0,0 +1,24 @@
+<script setup lang="ts">
+import { useRenderLoop, useTresContext } from '@tresjs/core'
+import { OrbitControls } from '@tresjs/cientos'
+import { onMounted } from 'vue'
+import { useState } from '../composables/state'
+
+const { renderingTimes } = useState()
+
+const state = useTresContext()
+
+function manualInvalidate() {
+  state.invalidate()
+}
+
+onMounted(() => {
+  manualInvalidate()
+})
+</script>
+
+<template>
+  <OrbitControls
+    @change="manualInvalidate"
+  />
+</template>

+ 11 - 0
playground/src/composables/state.ts

@@ -0,0 +1,11 @@
+import { reactive, toRefs } from 'vue'
+
+const state = reactive({
+  renderingTimes: 0,
+})
+export function useState() {
+  return {
+    ...toRefs(state),
+    
+  }
+}

+ 0 - 0
playground/src/pages/TheConditional.vue → playground/src/pages/basic/Conditional.vue


+ 0 - 0
playground/src/pages/TheGroups.vue → playground/src/pages/basic/Groups.vue


+ 0 - 0
playground/src/pages/lights.vue → playground/src/pages/basic/Lights.vue


+ 0 - 0
playground/src/pages/multiple.vue → playground/src/pages/basic/Multiple.vue


+ 0 - 0
playground/src/pages/primitives.vue → playground/src/pages/basic/Primitives.vue


+ 1 - 1
playground/src/pages/Responsiveness.vue → playground/src/pages/basic/Responsiveness.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import TheBasic from './TheBasic.vue'
+import TheBasic from '../basic/index.vue'
 </script>
 
 <template>

+ 0 - 0
playground/src/pages/TheBasic.vue → playground/src/pages/basic/index.vue


+ 0 - 0
playground/src/pages/cameras/Cameras.vue → playground/src/pages/cameras/index.vue


+ 0 - 41
playground/src/pages/click-blocking-box.vue

@@ -1,41 +0,0 @@
-<script lang="ts" setup>
-import { TresCanvas } from '@tresjs/core'
-import { OrbitControls } from '@tresjs/cientos'
-
-const log = (text: string) => {
-  console.log(text)
-}
-
-const boxOneBlocksPointerEvents = ref(false)
-</script>
-
-<template>
-  <div>
-    <TresCanvas window-size>
-      <TresPerspectiveCamera :look-at="[0, 4, 0]" />
-      <TresMesh
-        :position="[0, 1, 0]"
-        :blocks-pointer-events="boxOneBlocksPointerEvents"
-      >
-        <TresBoxGeometry :args="[1, 1, 1]" />
-        <TresMeshNormalMaterial />
-      </TresMesh>
-      <TresMesh
-        :position="[-2, 0, -2]"
-        name="box 2"
-        @click="log('box 2')"
-      >
-        <TresBoxGeometry :args="[1, 1, 1]" />
-        <TresMeshNormalMaterial />
-      </TresMesh>
-      <OrbitControls />
-      <TresGridHelper />
-      <TresAmbientLight :intensity="1" />
-    </TresCanvas>
-    <input
-      v-model="boxOneBlocksPointerEvents"
-      type="checkbox"
-      style="position: fixed; top:10px; left: 10px;"
-    >
-  </div>
-</template>

+ 0 - 0
playground/src/pages/raycaster/TheEvents.vue → playground/src/pages/events/index.vue


+ 81 - 2
playground/src/pages/index.vue

@@ -1,5 +1,84 @@
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import {
+  basicRoutes,
+  perfRoutes,
+  eventsRoutes,
+  cameraRoutes,
+} from '../router/routes'
+
+const sections = [
+  { icon: '📦', title: 'Basic', routes: basicRoutes },
+  { icon: '🏎️', title: 'Perf', routes: perfRoutes },
+  { icon: '📣', title: 'Events', routes: eventsRoutes },
+  { icon: '📷', title: 'Camera', routes: cameraRoutes },
+]
+</script>
 
 <template>
-  <TheExperience />
+  <div
+    class="
+  container mx-auto max-w-3xl
+  font-sans text-xs color-gray
+  bg-white 
+  "
+  >
+    <div class="mx-4">
+      <div
+        class="
+    mt-24 mb-12 text-center align-baseline items-center gap-6
+    sm:mt-16 sm:mb-6 sm:text-left sm:flex sm:flex-row-reverse sm:justify-left
+    "
+      >
+        <div>
+          <img
+            src="/logo.svg"
+            alt="TresJS logo"
+            class="max-w-24 sm:max-w-48 align-baseline"
+          >
+        </div>
+        <div class="sm:w-2/3">
+          <h1
+            class="
+        w-auto max-w-75 mx-auto text-5xl text-zinc-700 mb-3
+        sm:mx-none sm:w-1/2 sm:max-w-72
+        "
+          >
+            <span class="text-tres-primary">TresJS</span> Playground
+          </h1>
+          <p class="text-lg">
+            Testing zone for TresJS/core components
+          </p>
+        </div>
+      </div>
+      <div class="text-center sm:text-left sm:grid sm:grid-cols-2 md:grid-cols-3 gap-4">
+        <div
+          v-for="{ title, routes, icon } in sections"
+          :key="title"
+          class="
+          p-4 my-4 leading-normal size-m weight-600 bg-zinc-50 rounded
+          sm:my-0
+          "
+        >
+          <div class="inline-block p-2 p-x-3 m-b-3 text-2xl bg-zinc-200 rounded">
+            {{ icon }}
+          </div>
+          <h2 class="text-sm p-0 m-0 mb-1.5 font-semibold text-zinc-600">
+            {{ title }}
+          </h2>
+          <div
+            v-for="route in routes"
+            :key="route.name"
+            class="link-wrapper"
+          >
+            <router-link
+              class="no-underline text-zinc-700 visited:text-zinc-400 hover:text-cientos-blue"
+              :to="route.path"
+            >
+              <span>{{ route.name }} </span>
+            </router-link>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
 </template>

+ 0 - 49
playground/src/pages/perf/AkuAku.vue

@@ -1,49 +0,0 @@
-<script setup lang="ts">
-import { useGLTF } from '@tresjs/cientos'
-import { useControls } from '@tresjs/leches'
-
-const { nodes } = await useGLTF(
-  'https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/aku-aku/AkuAku.gltf',
-  { draco: true },
-)
-
-const model = nodes.AkuAku
-
-/* useControls({
-  button: {
-    label: 'Manual dispose',
-    type: 'button',
-    onClick() {
-      disposeModel()
-    },
-  },
-}) */
-
-function disposeModel() {
-  console.log('disposingModel')
-  model.traverse((child) => {
-    if (child.isMesh) {
-      // Dispose of the material
-      if (child.material) {
-        child.material.dispose()
-      }
-        
-      // Dispose of the geometry
-      if (child.geometry) {
-        child.geometry.dispose()
-      }
-    }
-  })
-  console.log('disposingModel Finished')
-}
-
-model.traverse((child) => {
-  if (child.material) {
-    console.log('child.material', child.material.uuid)
-  }
-})
-</script>
-
-<template>
-  <primitive :object="model" />
-</template>

+ 40 - 0
playground/src/pages/perf/OnDemand.vue

@@ -0,0 +1,40 @@
+<script setup lang="ts">
+import { TresCanvas } from '@tresjs/core'
+import { BasicShadowMap, SRGBColorSpace, NoToneMapping } from 'three'
+import { OrbitControls } from '@tresjs/cientos'
+import { useState } from '../../composables/state'
+import BlenderCube from '../../components/BlenderCube.vue'
+import GraphPane from '../../components/GraphPane.vue'
+import RenderingLogger from '../../components/RenderingLogger.vue'
+
+const { renderingTimes } = useState()
+
+function onRender() {
+  renderingTimes.value = 1
+
+}
+</script>
+
+<template>
+  <GraphPane />
+  <TresCanvas
+    render-mode="on-demand"
+    clear-color="#82DBC5"
+    @render="onRender"
+  >
+    <TresPerspectiveCamera
+      :position="[5, 5, 5]"
+      :look-at="[0, 0, 0]"
+    />
+    <Suspense>
+      <BlenderCube />
+    </Suspense>
+    <TresGridHelper />
+    <RenderingLogger />
+    <TresAmbientLight :intensity="1" />
+    <TresDirectionalLight
+      :position="[0, 8, 4]"
+      :intensity="0.7"
+    />
+  </TresCanvas>
+</template>

+ 0 - 21
playground/src/pages/rendering-modes/index.vue

@@ -1,21 +0,0 @@
-<script setup lang="ts">
-import { TresCanvas } from '@tresjs/core'
-
-import Scene from './scene.vue'
-
-const clearColor = ref('#82DBC5')
-
-setTimeout(() => {
-  clearColor.value = '#000000'
-}, 3000)
-</script>
-
-<template>
-  <TresCanvas
-    :clear-color="clearColor"
-    render-mode="on-demand"
-    @render="() => console.log('onRender')"
-  >
-    <Scene />
-  </TresCanvas>
-</template>

+ 0 - 32
playground/src/pages/rendering-modes/scene.vue

@@ -1,32 +0,0 @@
-<script setup lang="ts">
-import { useRenderLoop, useTres } from '@tresjs/core'
-import { OrbitControls } from '@tresjs/cientos'
-
-const { invalidate, advance } = useTres()
-
-function onControlChange() {
-  invalidate()
-}
-
-const positionX = ref(0)
-const showMesh = ref(true)
-
-setTimeout(() => {
-  positionX.value = 1
-  /*   showMesh.value = false */
-
-}, 3000)
-</script>
-
-<template>
-  <OrbitControls @change="onControlChange" />
-  <TresGridHelper />
-  <TresMesh
-    v-if="showMesh"
-    :position-x="positionX"
-  >
-    <TresBoxGeometry />
-    <TresMeshNormalMaterial />
-  </TresMesh>
-  <TresAmbientLight :intensity="1" />
-</template>

+ 17 - 1
playground/src/router.ts → playground/src/router/index.ts

@@ -1,7 +1,18 @@
 import { createRouter, createWebHistory } from 'vue-router'
+import { basicRoutes } from './routes/basic'
+import { cameraRoutes, eventsRoutes, perfRoutes } from './routes'
 
 const routes = [
   {
+    path: '/',
+    name: 'Home',
+    component: () => import('../pages/index.vue'),
+  },
+  ...basicRoutes,
+  ...perfRoutes,
+  ...eventsRoutes,
+  ...cameraRoutes,
+/*   {
     path: '/',
     name: 'Home',
     component: () => import('./pages/index.vue'),
@@ -91,12 +102,17 @@ const routes = [
     name: 'Rendering Modes',
     component: () => import('./pages/rendering-modes/index.vue'),
   },
+  {
+    path: '/on-demand-rendering',
+    name: 'On Demand Rendering',
+    component: () => import('./pages/on-demand-rendering.vue'),
+  },
   {
     path: '/empty',
     name: 'empty',
     component: () => import('./pages/empty.vue'),
   },
-  
+   */
 ]
 export const router = createRouter({
   history: createWebHistory(),

+ 37 - 0
playground/src/router/routes/basic.ts

@@ -0,0 +1,37 @@
+export const basicRoutes = [
+  {
+    path: '/basic',
+    name: 'Basic',
+    component: () => import('../../pages/basic/index.vue'),
+  },
+  {
+    path: '/basic/lights',
+    name: 'Lights',
+    component: () => import('../../pages/basic/Lights.vue'),
+  },
+  {
+    path: '/basic/groups',
+    name: 'Groups',
+    component: () => import('../../pages/basic/Groups.vue'),
+  },
+  {
+    path: '/basic/conditional',
+    name: 'Conditional',
+    component: () => import('../../pages/basic/Conditional.vue'),
+  },
+  {
+    path: '/Primitives',
+    name: 'Primitives',
+    component: () => import('../../pages/basic/Primitives.vue'),
+  },
+  {
+    path: '/basic/multiple',
+    name: 'Multiple',
+    component: () => import('../../pages/basic/Multiple.vue'),
+  },
+  {
+    path: '/basic/responsive',
+    name: 'Responsiveness',
+    component: () => import('../../pages/basic/Responsiveness.vue'),
+  },
+]

+ 17 - 0
playground/src/router/routes/cameras.ts

@@ -0,0 +1,17 @@
+export const cameraRoutes = [
+  {
+    path: '/cameras',
+    name: 'Cameras',
+    component: () => import('../../pages/cameras/index.vue'),
+  },
+  {
+    path: '/cameras/no-camera',
+    name: 'No Camera',
+    component: () => import('../../pages/cameras/index.vue'),
+  },
+  {
+    path: '/cameras/multiple-cameras',
+    name: 'Multiple Cameras',
+    component: () => import('../../pages/cameras/MultipleCameras.vue'),
+  },
+]

+ 7 - 0
playground/src/router/routes/events.ts

@@ -0,0 +1,7 @@
+export const eventsRoutes = [
+  {
+    path: '/events',
+    name: 'Events',
+    component: () => import('../../pages/events/index.vue'),
+  },
+]

+ 11 - 0
playground/src/router/routes/index.ts

@@ -0,0 +1,11 @@
+import { cameraRoutes } from './cameras'
+import { eventsRoutes } from './events'
+import { basicRoutes } from './basic'
+import { perfRoutes } from './performance'
+
+export {
+  basicRoutes,
+  perfRoutes,
+  eventsRoutes,
+  cameraRoutes,
+}

+ 7 - 0
playground/src/router/routes/performance.ts

@@ -0,0 +1,7 @@
+export const perfRoutes = [
+  {
+    path: '/perf/on-demand',
+    name: 'On Demand',
+    component: () => import('../../pages/perf/OnDemand.vue'),
+  },
+]

+ 5 - 0
playground/vite.config.ts

@@ -32,6 +32,11 @@ export default defineConfig({
     }),
     UnoCSS({
       /* options */
+      theme: {
+        colors: {
+          'tres-primary': '#82dbc5',
+        },
+      },
     }),
     qrcode(), // only applies in dev mode
   ],