Skip to content

Commit

Permalink
feat(Billboard): add component, playground, docs (#527)
Browse files Browse the repository at this point in the history
Co-authored-by: alvarosabu <[email protected]>
  • Loading branch information
andretchen0 and alvarosabu authored Dec 3, 2024
1 parent 7a23019 commit 418e6ae
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export default defineConfig({
{ text: 'useSurfaceSampler', link: '/guide/abstractions/use-surface-sampler' },
{ text: 'Sampler', link: '/guide/abstractions/sampler' },
{ text: 'AnimatedSprite', link: '/guide/abstractions/animated-sprite' },
{ text: 'Billboard', link: '/guide/abstractions/billboard' },
],
},
{
Expand Down
26 changes: 26 additions & 0 deletions docs/.vitepress/theme/components/BillboardDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script setup lang="ts">
import { Billboard, Box, OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { Vector3 } from 'three'
const COUNT = 5 * 5
const positions = Array.from({ length: COUNT }).fill(0).map((_, i) => {
return new Vector3(
i % 5 - 2,
Math.floor(i / 5) - 2,
0,
)
})
</script>

<template>
<TresCanvas clear-color="#333333">
<OrbitControls />
<TresPerspectiveCamera :position="[0, 0, 10]" />
<Billboard v-for="position, i of positions" :key="i" :position="position">
<Box :scale="[0.5, 0.5, 0.001]">
<TresMeshNormalMaterial />
</Box>
</Billboard>
</TresCanvas>
</template>
1 change: 1 addition & 0 deletions docs/component-list/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default [
},
{ text: 'Sampler', link: '/guide/abstractions/sampler' },
{ text: 'PositionalAudio', link: '/guide/abstractions/positional-audio' },
{ text: 'Billboard', link: '/guide/abstractions/billboard' },
],
},
{
Expand Down
20 changes: 20 additions & 0 deletions docs/guide/abstractions/billboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Billboard

<DocsDemo>
<BillboardDemo />
</DocsDemo>

Adds a `THREE.Group` that faces the camera.

## Usage

<<< @/.vitepress/theme/components/BillboardDemo.vue

## Props

| Prop | Description | Default |
| :--------------- | :--------------------------------------------------- | ------------- |
| `autoUpdate` | Whether the `<Billboard />` should face the camera automatically on every frame. | `true` |
| `lockX` | Whether to lock the x-axis. | `false` |
| `lockY` | Whether to lock the y-axis. | `false` |
| `lockZ` | Whether to lock the z-axis. | `false` |
27 changes: 27 additions & 0 deletions playground/vue/src/pages/abstractions/BillboardDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script setup lang="ts">
import { Billboard, Box, OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { Vector3 } from 'three'
import '@tresjs/leches/styles'
const COUNT = 5 * 5
const positions = Array.from({ length: COUNT }).fill(0).map((_, i) => {
return new Vector3(
i % 5 - 2,
Math.floor(i / 5) - 2,
0,
)
})
</script>

<template>
<TresCanvas clear-color="#333333">
<OrbitControls />
<TresPerspectiveCamera :position="[0, 0, 10]" />
<Billboard v-for="position, i of positions" :key="i" :position="position">
<Box :scale="[0.5, 0.5, 0.001]">
<TresMeshNormalMaterial />
</Box>
</Billboard>
</TresCanvas>
</template>
5 changes: 5 additions & 0 deletions playground/vue/src/router/routes/abstractions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,9 @@ export const abstractionsRoutes = [
name: 'AnimatedSprite',
component: () => import('../../pages/abstractions/AnimatedSpriteDemo.vue'),
},
{
path: '/abstractions/billboard',
name: 'Billboard',
component: () => import('../../pages/abstractions/BillboardDemo.vue'),
},
]
75 changes: 75 additions & 0 deletions src/core/abstractions/Billboard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<script setup lang="ts">
import type { Camera } from 'three'
import { Euler, Group, Quaternion } from 'three'
import { shallowRef } from 'vue'
import { useLoop, useTres } from '@tresjs/core'
export interface BillboardProps {
/**
* Whether the Billboard should face the camera automatically on every frame.
*/
autoUpdate?: boolean
/**
* Whether to lock the x-axis.
*/
lockX?: boolean
/**
* Whether to lock the y-axis.
*/
lockY?: boolean
/**
* Whether to lock the z-axis.
*/
lockZ?: boolean
}
const props = withDefaults(defineProps<BillboardProps>(), {
autoUpdate: true,
lockX: false,
lockY: false,
lockZ: false,
})
const outerRef = shallowRef(new Group())
const innerRef = shallowRef(new Group())
const q = new Quaternion()
const r = new Euler()
function update(camera?: Camera) {
if (!outerRef.value) { return }
if (!camera) {
const t = useTres()
camera = t.camera.value
if (!camera) { return }
}
// NOTE: Save current rotation in case we're locking an axis
innerRef.value.rotation.copy(r)
// NOTE: Face the camera
outerRef.value.updateMatrix()
outerRef.value.updateWorldMatrix(false, false)
outerRef.value.getWorldQuaternion(q)
camera.getWorldQuaternion(innerRef.value.quaternion).premultiply(q.invert())
// NOTE: Overwrite locked axes
if (props.lockX) { innerRef.value.rotation.x = r.x }
if (props.lockY) { innerRef.value.rotation.y = r.y }
if (props.lockZ) { innerRef.value.rotation.z = r.z }
}
useLoop().onBeforeRender(({ camera }) => {
if (props.autoUpdate) { update(camera) }
})
defineExpose({ instance: outerRef, update })
</script>

<template>
<TresGroup ref="outerRef">
<TresGroup ref="innerRef">
<slot></slot>
</TresGroup>
</TresGroup>
</template>
2 changes: 2 additions & 0 deletions src/core/abstractions/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import AnimatedSprite from './AnimatedSprite/component.vue'
import Billboard from './Billboard.vue'
import { GlobalAudio } from './GlobalAudio'
import Lensflare from './Lensflare/component.vue'
import Levioso from './Levioso.vue'
Expand All @@ -15,6 +16,7 @@ export * from './useFBO/'
export * from './useSurfaceSampler'
export {
AnimatedSprite,
Billboard,
Fbo,
GlobalAudio,
Lensflare,
Expand Down

0 comments on commit 418e6ae

Please sign in to comment.