Skip to content

Commit f759344

Browse files
authored
feat: WebcamVideoTexture and ScreenVideoTexture (#2240)
* 1st commit * ScreenVideoTexture * doc * doc * src/web
1 parent 56b7182 commit f759344

7 files changed

+195
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as THREE from 'three'
2+
import * as React from 'react'
3+
import { Suspense } from 'react'
4+
import { Meta, StoryObj } from '@storybook/react'
5+
6+
import { Setup } from '../Setup'
7+
8+
import { Plane, ScreenVideoTexture } from '../../src'
9+
10+
export default {
11+
title: 'Misc/ScreenVideoTexture',
12+
component: ScreenVideoTexture,
13+
decorators: [
14+
(Story) => (
15+
<Setup>
16+
<Story />
17+
</Setup>
18+
),
19+
],
20+
} satisfies Meta<typeof ScreenVideoTexture>
21+
22+
type Story = StoryObj<typeof ScreenVideoTexture>
23+
24+
function ScreenVideoTextureScene(props: React.ComponentProps<typeof ScreenVideoTexture>) {
25+
return (
26+
<Plane args={[4, 2.25]}>
27+
<Suspense fallback={<meshBasicMaterial color="gray" />}>
28+
<ScreenVideoTexture {...props}>
29+
{(texture) => <meshBasicMaterial side={THREE.DoubleSide} map={texture} toneMapped={false} />}
30+
</ScreenVideoTexture>
31+
</Suspense>
32+
</Plane>
33+
)
34+
}
35+
36+
export const ScreenVideoTextureSt = {
37+
render: (args) => <ScreenVideoTextureScene {...args} />,
38+
name: 'Default',
39+
} satisfies Story
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as THREE from 'three'
2+
import * as React from 'react'
3+
import { Suspense } from 'react'
4+
import { Meta, StoryObj } from '@storybook/react'
5+
6+
import { Setup } from '../Setup'
7+
8+
import { Plane, WebcamVideoTexture } from '../../src'
9+
10+
export default {
11+
title: 'Misc/WebcamVideoTexture',
12+
component: WebcamVideoTexture,
13+
decorators: [
14+
(Story) => (
15+
<Setup>
16+
<Story />
17+
</Setup>
18+
),
19+
],
20+
} satisfies Meta<typeof WebcamVideoTexture>
21+
22+
type Story = StoryObj<typeof WebcamVideoTexture>
23+
24+
function WebcamVideoTextureScene(props: React.ComponentProps<typeof WebcamVideoTexture>) {
25+
return (
26+
<Plane args={[4, 2.25]}>
27+
<Suspense fallback={<meshBasicMaterial color="gray" />}>
28+
<WebcamVideoTexture {...props}>
29+
{(texture) => <meshBasicMaterial side={THREE.DoubleSide} map={texture} toneMapped={false} />}
30+
</WebcamVideoTexture>
31+
</Suspense>
32+
</Plane>
33+
)
34+
}
35+
36+
export const WebcamVideoTextureSt = {
37+
render: (args) => <WebcamVideoTextureScene {...args} />,
38+
name: 'Default',
39+
} satisfies Story

docs/loaders/screen-video-texture.mdx

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
title: ScreenVideoTexture
3+
sourcecode: src/web/ScreenVideoTexture.tsx
4+
---
5+
6+
[![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/misc-screenvideotexture) ![](https://img.shields.io/badge/-suspense-brightgreen)
7+
8+
<Intro>Create a video texture from [`getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia)</Intro>
9+
10+
```tsx
11+
export type ScreenVideoTextureProps = Omit<VideoTextureProps, 'src'> & {
12+
options?: DisplayMediaStreamOptions
13+
}
14+
```
15+
16+
```jsx
17+
<ScreenVideoTexture>
18+
{(texture) => <meshBasicMaterial map={texture} />}
19+
```
20+
21+
or exposed via `ref`:
22+
23+
```jsx
24+
const textureRef = useRef()
25+
<ScreenVideoTexture ref={textureRef} />
26+
```

docs/loaders/webcam-video-texture.mdx

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
title: WebcamVideoTexture
3+
sourcecode: src/web/WebcamVideoTexture.tsx
4+
---
5+
6+
[![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/misc-webcamvideotexture) ![](https://img.shields.io/badge/-suspense-brightgreen)
7+
8+
<Intro>Create a video texture from [`getUserMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)</Intro>
9+
10+
```tsx
11+
export type WebcamVideoTextureProps = Omit<VideoTextureProps, 'src'> & {
12+
constraints?: MediaStreamConstraints
13+
}
14+
```
15+
16+
```jsx
17+
<WebcamVideoTexture>
18+
{(texture) => <meshBasicMaterial map={texture} />}
19+
```
20+
21+
or exposed via `ref`:
22+
23+
```jsx
24+
const textureRef = useRef()
25+
<WebcamVideoTexture ref={textureRef} />
26+
```

src/web/ScreenVideoTexture.tsx

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import * as React from 'react'
2+
import { forwardRef, useEffect } from 'react'
3+
import { suspend, clear } from 'suspend-react'
4+
import { VideoTexture, VideoTextureProps } from '..'
5+
6+
export type ScreenVideoTextureProps = Omit<VideoTextureProps, 'src'> & {
7+
options?: DisplayMediaStreamOptions
8+
}
9+
10+
/**
11+
* Create a video texture from [`getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia)
12+
*/
13+
export const ScreenVideoTexture = /* @__PURE__ */ forwardRef<THREE.VideoTexture, ScreenVideoTextureProps>(
14+
({ options = { video: true }, ...props }, fref) => {
15+
const mediaStream = suspend(() => navigator.mediaDevices.getDisplayMedia(options), [])
16+
17+
useEffect(() => {
18+
return () => {
19+
mediaStream?.getTracks().forEach((track) => track.stop())
20+
clear([])
21+
}
22+
}, [mediaStream])
23+
24+
return <VideoTexture ref={fref} {...props} src={mediaStream} />
25+
}
26+
)

src/web/WebcamVideoTexture.tsx

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as React from 'react'
2+
import { forwardRef, useEffect } from 'react'
3+
import { suspend, clear } from 'suspend-react'
4+
import { VideoTexture, VideoTextureProps } from '..'
5+
6+
export type WebcamVideoTextureProps = Omit<VideoTextureProps, 'src'> & {
7+
constraints?: MediaStreamConstraints
8+
}
9+
10+
/**
11+
* Create a video texture from [`getUserMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)
12+
*/
13+
export const WebcamVideoTexture = /* @__PURE__ */ forwardRef<THREE.VideoTexture, WebcamVideoTextureProps>(
14+
(
15+
{
16+
constraints = {
17+
audio: false,
18+
video: { facingMode: 'user' },
19+
},
20+
...props
21+
},
22+
fref
23+
) => {
24+
const mediaStream = suspend(() => navigator.mediaDevices.getUserMedia(constraints), [])
25+
26+
useEffect(() => {
27+
return () => {
28+
mediaStream?.getTracks().forEach((track) => track.stop())
29+
clear([])
30+
}
31+
}, [mediaStream])
32+
33+
return <VideoTexture ref={fref} {...props} src={mediaStream} />
34+
}
35+
)

src/web/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export * from './View'
1414
// Gizmos
1515
export * from './pivotControls'
1616

17+
// Loaders
18+
export * from './ScreenVideoTexture'
19+
export * from './WebcamVideoTexture'
20+
1721
// Controls
1822
export * from './FaceControls'
1923
export { DragControls } from './DragControls'

0 commit comments

Comments
 (0)