Skip to content

Commit

Permalink
feat(ScreenSizer): add component, playground, docs (#535)
Browse files Browse the repository at this point in the history
* feat(ScreenSizer): add component, playground, docs

* chore: added demo to routes

---------

Co-authored-by: alvarosabu <[email protected]>
  • Loading branch information
andretchen0 and alvarosabu authored Dec 4, 2024
1 parent e43f112 commit ee5af96
Show file tree
Hide file tree
Showing 8 changed files with 140 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 @@ -78,6 +78,7 @@ export default defineConfig({
{ text: 'Edges', link: '/guide/abstractions/edges' },
{ text: 'PositionalAudio', link: '/guide/abstractions/positional-audio' },
{ text: 'AnimatedSprite', link: '/guide/abstractions/animated-sprite' },
{ text: 'ScreenSizer', link: '/guide/abstractions/screen-sizer' },
{ text: 'ScreenSpace', link: '/guide/abstractions/screen-space' },
{ text: 'Outline', link: '/guide/abstractions/outline' },
{ text: 'Image', link: '/guide/abstractions/image' },
Expand Down
22 changes: 22 additions & 0 deletions docs/.vitepress/theme/components/ScreenSizerDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { OrbitControls, ScreenSizer } from '@tresjs/cientos'
</script>

<template>
<TresCanvas clear-color="#3f3f3f">
<TresPerspectiveCamera :position="[10, 10, 10]" />
<OrbitControls />
<ScreenSizer>
<TresMesh>
<TresBoxGeometry :args="[100, 100, 100]" />
<TresMeshNormalMaterial />
</TresMesh>
</ScreenSizer>
<TresMesh :position-x="5">
<TresBoxGeometry />
<TresMeshNormalMaterial />
</TresMesh>
<TresGridHelper :args="[10, 10]" />
</TresCanvas>
</template>
17 changes: 17 additions & 0 deletions docs/guide/abstractions/screen-sizer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# ScreenSizer

<DocsDemo>
<ScreenSizerDemo />
</DocsDemo>

Adds a `<TresObject3D />` wrapper that scales to "screen space". By default `1` THREE world unit will be translated to 1 screen pixel.

E.g. a BoxGeometry with a height, width, and depth of 100 each, will be scaled to 100 screen pixels in each dimension.

## Usage

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

## Props

Inherits all props from `THREE.Object3D`.
23 changes: 23 additions & 0 deletions playground/vue/src/pages/abstractions/ScreenSizerDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { OrbitControls, ScreenSizer } from '@tresjs/cientos'
</script>

<template>
<TresCanvas>
<TresPerspectiveCamera :position="[11, 11, 11]" />
<OrbitControls />
<ScreenSizer>
<TresMesh>
<TresBoxGeometry :args="[100, 100, 100]" />
<TresMeshNormalMaterial />
</TresMesh>
</ScreenSizer>
<TresMesh :position-x="2">
<TresBoxGeometry />
<TresMeshNormalMaterial />
</TresMesh>
<TresGridHelper :args="[10, 10]" />
<TresAmbientLight :intensity="1" />
</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 @@ -84,4 +84,9 @@ export const abstractionsRoutes = [
name: 'Billboard',
component: () => import('../../pages/abstractions/BillboardDemo.vue'),
},
{
path: '/abstractions/screen-sizer',
name: 'ScreenSizer',
component: () => import('../../pages/abstractions/ScreenSizerDemo.vue'),
},
]
30 changes: 30 additions & 0 deletions src/core/abstractions/ScreenSizer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup lang="ts">
import { Vector3 } from 'three'
import { calculateScaleFactor } from '../../utils/calculateScaleFactor'
import { computed, shallowRef } from 'vue'
import { useLoop, useTres } from '@tresjs/core'
const worldPos = new Vector3()
const outerRef = shallowRef()
const innerRef = shallowRef()
const sizes = useTres().sizes
const size = computed(() => ({ width: sizes.width.value, height: sizes.height.value }))
useLoop().onBeforeRender(({ camera }) => {
const obj = innerRef.value
if (!obj) { return }
const sf = calculateScaleFactor(obj.getWorldPosition(worldPos), 1, camera, size.value)
obj.scale.setScalar(sf)
})
defineExpose({ instance: outerRef })
</script>

<template>
<TresObject3D ref="outerRef">
<TresObject3D ref="innerRef">
<slot></slot>
</TresObject3D>
</TresObject3D>
</template>
2 changes: 2 additions & 0 deletions src/core/abstractions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Text3D from './Text3D.vue'
import { useAnimations } from './useAnimations'
import Fbo from './useFBO/component.vue'
import Sampler from './useSurfaceSampler/component.vue'
import ScreenSizer from './ScreenSizer.vue'
import Edges from './Edges.vue'

export * from '../staging/useEnvironment'
Expand All @@ -32,6 +33,7 @@ export {
PositionalAudio,
Reflector,
Sampler,
ScreenSizer,
ScreenSpace,
Text3D,
useAnimations,
Expand Down
40 changes: 40 additions & 0 deletions src/utils/calculateScaleFactor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as THREE from 'three'

// NOTE: Source
// https://github.com/pmndrs/drei/blob/f8e5653f7f60d3782301c13e781c9966370b8fda/src/core/calculateScaleFactor.ts#L24

const tV0 = new THREE.Vector3()
const tV1 = new THREE.Vector3()
const tV2 = new THREE.Vector3()

interface Size {
width: number
height: number
}

const getPoint2 = (point3: THREE.Vector3, camera: THREE.Camera, size: Size) => {
const widthHalf = size.width / 2
const heightHalf = size.height / 2
camera.updateMatrixWorld(false)
const vector = point3.project(camera)
vector.x = vector.x * widthHalf + widthHalf
vector.y = -(vector.y * heightHalf) + heightHalf
return vector
}

const getPoint3 = (point2: THREE.Vector3, camera: THREE.Camera, size: Size, zValue: number = 1) => {
const vector = tV0.set((point2.x / size.width) * 2 - 1, -(point2.y / size.height) * 2 + 1, zValue)
vector.unproject(camera)
return vector
}

export const calculateScaleFactor = (point3: THREE.Vector3, radiusPx: number, camera: THREE.Camera, size: Size) => {
const point2 = getPoint2(tV2.copy(point3), camera, size)
let scale = 0
for (let i = 0; i < 2; ++i) {
const point2off = tV1.copy(point2).setComponent(i, point2.getComponent(i) + radiusPx)
const point3off = getPoint3(point2off, camera, size, point2off.z)
scale = Math.max(scale, point3.distanceTo(point3off))
}
return scale
}

0 comments on commit ee5af96

Please sign in to comment.