Skip to content

Commit

Permalink
feat: add ScreenSizer
Browse files Browse the repository at this point in the history
  • Loading branch information
RodrigoHamuy committed Dec 19, 2023
1 parent cc8ea56 commit e0fafd8
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 36 deletions.
65 changes: 65 additions & 0 deletions .storybook/stories/ScreenSizer.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as React from 'react'
import { Vector3 } from 'three'
import { Box, Html, ScreenSizer } from '../../src'

import { Setup } from '../Setup'

export default {
title: 'Abstractions/ScreenSizer',
component: ScreenSizer,
decorators: [(storyFn) => <Setup cameraPosition={new Vector3(0, 0, 10)}>{storyFn()}</Setup>],
}

export const ScreenSizerStory = ({ scale }) => (
<>
<Box args={[1, 1, 1]} position={[-1, 0, 0]}>
<meshPhysicalMaterial color="#69d2e7" />
<Html
center
sprite
style={{
textAlign: 'center',
background: 'rgba(255,255,255,0.5)',
pointerEvents: 'none',
boxShadow: '0px 0px 10px 10px rgba(255,255,255, 0.5)',
}}
>
Normal Box
</Html>
</Box>
<ScreenSizer scale={scale} position={[1, 0, 0]}>
<Box args={[100, 100, 100]}>
<meshPhysicalMaterial color="#f38630" />
<Html
center
sprite
style={{
textAlign: 'center',
background: 'rgba(255,255,255,0.5)',
pointerEvents: 'none',
boxShadow: '0px 0px 10px 10px rgba(255,255,255, 0.5)',
}}
>
Box wrapped in ScreenSizer
</Html>
</Box>
</ScreenSizer>
<Html
center
sprite
position={[0, -3, 0]}
style={{
textAlign: 'center',
background: 'rgba(255,255,255,0.5)',
pointerEvents: 'none',
width: '10rem',
}}
>
Zoom in/out to see the difference
</Html>
</>
)

ScreenSizerStory.args = {
scale: 1,
}
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
<li><a href="#positionalaudio">PositionalAudio</a></li>
<li><a href="#billboard">Billboard</a></li>
<li><a href="#screenspace">ScreenSpace</a></li>
<li><a href="#screensizer">ScreenSizer</a></li>
<li><a href="#effects">Effects</a></li>
<li><a href="#gradienttexture">GradientTexture</a></li>
<li><a href="#edges">Edges</a></li>
Expand Down Expand Up @@ -1467,6 +1468,22 @@ Adds a `<group />` that aligns objects to screen space.
</ScreenSpace>
```

#### ScreenSizer

[![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/abstractions-screensizer--screen-sizer-story)

Adds a `<object3D />` that scales objects to screen space.

```jsx
<ScreenSizer
scale={1} // scale factor
>
<Box
args={[100, 100, 100]} // will render roughly as a 100px box
/>
</ScreenSizer>
```

#### GradientTexture

<p>
Expand Down
43 changes: 43 additions & 0 deletions src/core/ScreenSizer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Object3DProps, useFrame } from '@react-three/fiber'
import { ForwardRefComponent } from 'helpers/ts-utils'
import * as React from 'react'
import { forwardRef, useRef } from 'react'
import mergeRefs from 'react-merge-refs'
import { Object3D, Vector3 } from 'three'
import { calculateScaleFactor } from './calculateScaleFactor'

const worldPos = /* @__PURE__ */ new Vector3()

export interface ScreenSizerProps extends Object3DProps {
/** Scale factor. Defaults to 1, which equals 1 pixel size. */
scale: number
}

/**
* Wraps children in an `Object3D` and attempts to scale from
* world units to screen units * scale factor.
*
* For example, this will render a box of roughly 1x1 pixel size,
* independently of how far the camera is.
*
* ```jsx
* <ScreenSizer>
* <Box />
* </ScreenSizer>
* ```
*/
export const ScreenSizer: ForwardRefComponent<ScreenSizerProps, Object3D> = /* @__PURE__ */ forwardRef<
Object3D,
ScreenSizerProps
>(({ scale = 1, ...props }, ref) => {
const container = useRef<Object3D>(null)

useFrame((state) => {
const obj = container.current
if (!obj) return
const sf = calculateScaleFactor(obj.getWorldPosition(worldPos), scale, state.camera, state.size)
obj.scale.setScalar(sf * scale)
})

return <object3D ref={mergeRefs([container, ref])} {...props} />
})
33 changes: 33 additions & 0 deletions src/core/calculateScaleFactor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Size } from '@react-three/fiber'
import * as THREE from 'three'

const tV0 = /* @__PURE__ */ new THREE.Vector3()
const tV1 = /* @__PURE__ */ new THREE.Vector3()
const tV2 = /* @__PURE__ */ new THREE.Vector3()

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
}
4 changes: 4 additions & 0 deletions src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Abstractions
export * from './Billboard'
export * from './ScreenSpace'
export * from './ScreenSizer'
export * from './QuadraticBezierLine'
export * from './CubicBezierLine'
export * from './CatmullRomLine'
Expand Down Expand Up @@ -145,3 +146,6 @@ export * from './Mask'
export * from './Hud'
export * from './Fisheye'
export * from './MeshPortalMaterial'

// Others
export * from './calculateScaleFactor'
42 changes: 6 additions & 36 deletions src/web/pivotControls/index.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,13 @@
import * as THREE from 'three'
import { useFrame, useThree } from '@react-three/fiber'
import * as React from 'react'
import { Size, extend, useFrame, useThree } from '@react-three/fiber'
import * as THREE from 'three'

import { ForwardRefComponent } from '../../helpers/ts-utils'
import { AxisArrow } from './AxisArrow'
import { PlaneSlider } from './PlaneSlider'
import { AxisRotator } from './AxisRotator'
import { context, OnDragStartProps } from './context'
import { ForwardRefComponent } from '../../helpers/ts-utils'

const tV0 = /* @__PURE__ */ new THREE.Vector3()
const tV1 = /* @__PURE__ */ new THREE.Vector3()
const tV2 = /* @__PURE__ */ new THREE.Vector3()

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
}
import { PlaneSlider } from './PlaneSlider'
import { OnDragStartProps, context } from './context'
import { calculateScaleFactor } from '../../core/calculateScaleFactor'

const mL0 = /* @__PURE__ */ new THREE.Matrix4()
const mW0 = /* @__PURE__ */ new THREE.Matrix4()
Expand Down

0 comments on commit e0fafd8

Please sign in to comment.