浏览代码

Merge pull request #57 from Tresjs/feature/56-buffergeometry-and-bufferattribute-support

feat(core): added camel keys to set attributes
Alvaro Saburido 2 年之前
父节点
当前提交
e4d8644a24
共有 2 个文件被更改,包括 170 次插入68 次删除
  1. 85 0
      packages/tres/src/components/TheParticles.vue
  2. 85 68
      packages/tres/src/core/useInstanceCreator/index.ts

+ 85 - 0
packages/tres/src/components/TheParticles.vue

@@ -0,0 +1,85 @@
+<script setup lang="ts">
+import { reactive } from 'vue'
+import { OrbitControls } from '@tresjs/cientos/src'
+import { TresCanvas } from '../core/useRenderer/component'
+import { AdditiveBlending } from 'three'
+import { useRenderLoop } from '../core/useRenderLoop'
+/* import { OrbitControls, GLTFModel } from '@tresjs/cientos' */
+
+const state = reactive({
+  clearColor: 'teal',
+  shadows: true,
+  alpha: false,
+  physicallyCorrectLights: true,
+})
+
+const shader = {
+  transparent: true,
+  blending: AdditiveBlending,
+  depthWrite: false,
+
+  vertexShader: `
+  uniform float uPixelRatio;
+  uniform float uSize;
+  uniform float uTime;
+  attribute float aScale;
+
+  void main()
+  {
+      vec4 modelPosition = modelMatrix * vec4(position, 1.0);
+      modelPosition.y += sin(uTime + modelPosition.x * 100.0) * aScale * 0.2;
+      vec4 viewPosition = viewMatrix * modelPosition;
+      vec4 projectionPosition = projectionMatrix * viewPosition;
+
+      gl_Position = projectionPosition;
+      gl_PointSize = aScale * uSize * uPixelRatio;
+      gl_PointSize *= (1.0 / - viewPosition.z);
+  }
+  `,
+  fragmentShader: `
+  void main()
+    {
+      float distanceToCenter = distance(gl_PointCoord, vec2(0.5));
+      float strength = 0.05 / distanceToCenter - 0.1;
+
+      gl_FragColor = vec4(1.0, 1.0, 1.0, strength);
+    }
+  `,
+  uniforms: {
+    uSize: { value: 100 },
+    uPixelRatio: { value: Math.min(window.devicePixelRatio, 2) },
+    uTime: { value: 0 },
+  },
+}
+
+const firefliesCount = 30
+const positionArray = new Float32Array(firefliesCount * 3)
+const scaleArray = new Float32Array(firefliesCount)
+
+for (let i = 0; i < firefliesCount; i++) {
+  positionArray[i * 3 + 0] = Math.random() * 4
+  positionArray[i * 3 + 1] = Math.random() * 4
+  positionArray[i * 3 + 2] = Math.random() * 4
+  scaleArray[i] = Math.random()
+}
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ elapsed }) => {
+  shader.uniforms.uTime.value = elapsed
+})
+</script>
+<template>
+  <TresCanvas v-bind="state">
+    <TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :near="0.1" :far="1000" :look-at="[-8, 3, -3]" />
+    <TresScene>
+      <OrbitControls />
+      <TresAmbientLight :intensity="0.5" />
+      <TresPoints>
+        <TresBufferGeometry :position="[positionArray, 3]" :a-scale="[scaleArray, 1]" />
+        <TresShaderMaterial v-bind="shader" />
+      </TresPoints>
+      <TresDirectionalLight :position="[0, 2, 4]" :intensity="1" cast-shadow />
+    </TresScene>
+  </TresCanvas>
+</template>

+ 85 - 68
packages/tres/src/core/useInstanceCreator/index.ts

@@ -1,6 +1,6 @@
 /* eslint-disable new-cap */
 /* eslint-disable @typescript-eslint/no-empty-function */
-import { FogBase, OrthographicCamera, PerspectiveCamera, Scene } from 'three'
+import { BufferAttribute, FogBase, OrthographicCamera, PerspectiveCamera, Scene } from 'three'
 import { defineComponent, inject, Ref } from 'vue'
 import { isArray, isDefined, isFunction } from '@alvarosabu/utils'
 import { normalizeVectorFlexibleParam } from '/@/utils/normalize'
@@ -13,6 +13,16 @@ const VECTOR3_PROPS = ['rotation', 'scale', 'position']
 export function useInstanceCreator(prefix: string) {
   const { logMessage, logError } = useLogger()
 
+  function processSetAttributes(props: Record<string, any>, instance: TresInstance) {
+    if (!isDefined(props)) return
+    if (!isDefined(instance)) return
+
+    Object.entries(props).forEach(([key, value]) => {
+      const camelKey = key.replace(/(-\w)/g, m => m[1].toUpperCase())
+      instance.setAttribute(camelKey, new BufferAttribute(...(value as ConstructorParameters<typeof BufferAttribute>)))
+    })
+  }
+
   function processProps(props: Record<string, any>, instance: TresInstance) {
     if (!isDefined(props)) return
     if (!isDefined(instance)) return
@@ -91,7 +101,11 @@ export function useInstanceCreator(prefix: string) {
       // check if props is defined on the vnode
       if (vnode?.props) {
         // if props is defined, process the props and pass the internalInstance to update its properties
-        processProps(vnode.props, internalInstance)
+        if (vNodeType === 'BufferGeometry') {
+          processSetAttributes(vnode.props, internalInstance)
+        } else {
+          processProps(vnode.props, internalInstance)
+        }
       }
 
       return internalInstance
@@ -120,76 +134,79 @@ export function useInstanceCreator(prefix: string) {
   }
 
   function createComponentInstances(catalogue: Ref<TresCatalogue>) {
-    return Object.entries(catalogue.value)
-      .filter(([_key, value]) => (value as { prototype: any })?.prototype?.constructor?.toString().includes('class'))
-      .map(([key, threeObj]) => {
-        const name = `${prefix}${key}`
-        const cmp = defineComponent({
-          name,
-          setup(props, { slots, attrs, ...ctx }) {
-            const { scene: fallback } = useScene()
-            const scene = inject<Ref<Scene>>('local-scene') || fallback
-            const catalogue = inject<Ref<TresCatalogue>>('catalogue')
-            const { pushCamera } = useCamera()
-
-            let instance = createInstance(threeObj, attrs, slots)
-            processProps(attrs, instance)
-            // If the instance is a camera, push it to the camera stack
-            if (instance instanceof PerspectiveCamera || instance instanceof OrthographicCamera) {
-              pushCamera(instance)
-            }
-
-            // If the instance is a valid Object3D, add it to the scene
-            if (instance.isObject3D) {
-              scene?.value.add(instance)
-            }
-
-            if (scene?.value && instance.isFog) {
-              scene.value.fog = instance as unknown as FogBase
-            }
-
-            if (import.meta.hot) {
-              import.meta.hot.on('vite:beforeUpdate', () => {
-                scene.value.remove(instance)
-              })
+    return (
+      Object.entries(catalogue.value)
+        // eslint-disable-next-line @typescript-eslint/no-unused-vars
+        .filter(([_key, value]) => (value as { prototype: any })?.prototype?.constructor?.toString().includes('class'))
+        .map(([key, threeObj]) => {
+          const name = `${prefix}${key}`
+          const cmp = defineComponent({
+            name,
+            setup(props, { slots, attrs, ...ctx }) {
+              const { scene: fallback } = useScene()
+              const scene = inject<Ref<Scene>>('local-scene') || fallback
+              const catalogue = inject<Ref<TresCatalogue>>('catalogue')
+              const { pushCamera } = useCamera()
+
+              let instance = createInstance(threeObj, attrs, slots)
+              processProps(attrs, instance)
+              // If the instance is a camera, push it to the camera stack
+              if (instance instanceof PerspectiveCamera || instance instanceof OrthographicCamera) {
+                pushCamera(instance)
+              }
+
+              // If the instance is a valid Object3D, add it to the scene
+              if (instance.isObject3D) {
+                scene?.value.add(instance)
+              }
+
+              if (scene?.value && instance.isFog) {
+                scene.value.fog = instance as unknown as FogBase
+              }
+
+              if (import.meta.hot) {
+                import.meta.hot.on('vite:beforeUpdate', () => {
+                  scene.value.remove(instance)
+                })
 
-              import.meta.hot.on('vite:afterUpdate', () => {
-                instance = createInstance(threeObj, attrs, slots)
-                processProps(attrs, instance)
-
-                if (instance.isObject3D) {
-                  scene?.value.add(instance)
-                }
-
-                logMessage(name, {
-                  instance,
-                  sceneuuid: scene?.value.uuid,
-                  catalogue: catalogue?.value.uuid,
-                  props,
-                  slots: slots.default ? slots.default() : undefined,
-                  attrs,
-                  ctx,
-                  scene,
+                import.meta.hot.on('vite:afterUpdate', () => {
+                  instance = createInstance(threeObj, attrs, slots)
+                  processProps(attrs, instance)
+
+                  if (instance.isObject3D) {
+                    scene?.value.add(instance)
+                  }
+
+                  logMessage(name, {
+                    instance,
+                    sceneuuid: scene?.value.uuid,
+                    catalogue: catalogue?.value.uuid,
+                    props,
+                    slots: slots.default ? slots.default() : undefined,
+                    attrs,
+                    ctx,
+                    scene,
+                  })
                 })
+              }
+
+              ctx.expose(instance)
+              logMessage(name, {
+                sceneuuid: scene?.value.uuid,
+                catalogue: catalogue?.value.uuid,
+                props,
+                slots: slots.default ? slots.default() : undefined,
+                attrs,
+                ctx,
+                scene,
               })
-            }
-
-            ctx.expose(instance)
-            logMessage(name, {
-              sceneuuid: scene?.value.uuid,
-              catalogue: catalogue?.value.uuid,
-              props,
-              slots: slots.default ? slots.default() : undefined,
-              attrs,
-              ctx,
-              scene,
-            })
-            return () => {}
-          },
-        })
+              return () => {}
+            },
+          })
 
-        return [name, cmp]
-      })
+          return [name, cmp]
+        })
+    )
   }
 
   return {