Skip to content

Commit

Permalink
chore(3D): add presence demo
Browse files Browse the repository at this point in the history
  • Loading branch information
stipsan committed Oct 29, 2022
1 parent a2c05aa commit f6c80a8
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 1 deletion.
4 changes: 3 additions & 1 deletion dev/test-studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
},
"dependencies": {
"@portabletext/react": "^1.0.6",
"@react-three/cannon": "^6.4.0",
"@react-three/drei": "^9.38.1",
"@react-three/fiber": "^8.8.10",
"@sanity/block-tools": "3.0.0-dev-preview.22",
Expand Down Expand Up @@ -45,7 +46,8 @@
"sanity": "3.0.0-dev-preview.22",
"sanity-plugin-mux-input": "2.0.0-v3-studio.20",
"styled-components": "^5.2.0",
"three": "^0.146.0"
"three": "^0.146.0",
"three-stdlib": "^2.17.3"
},
"devDependencies": {
"@types/history": "^4.7.9",
Expand Down
215 changes: 215 additions & 0 deletions dev/test-studio/plugins/presence/Presence3D.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable react/no-unknown-property */
// Fork of https://codesandbox.io/s/object-clump-ssbdsw

import React, {memo, useState} from 'react'

import {Physics, useCylinder, useSphere} from '@react-three/cannon'
import {Effects as EffectComposer, Environment, Sky, useTexture} from '@react-three/drei'
import {Canvas, extend, useFrame, useThree} from '@react-three/fiber'
import * as THREE from 'three'
import {SSAOPass} from 'three-stdlib'
import {useGlobalPresence, useUserColor, useWorkspace} from 'sanity'
import styled from 'styled-components'
import {Card, Box, Text} from '@sanity/ui'

extend({SSAOPass})

const rfs = THREE.MathUtils.randFloatSpread
const sphereGeometry = new THREE.SphereGeometry(1, 32, 32)
const baubleMaterial = new THREE.MeshStandardMaterial({
color: 'red',
roughness: 0,
envMapIntensity: 0.2,
emissive: '#370037',
})

export const initial = {
count: 40,
color: 'red',
roughness: 0,
emissive: '#370037',
}

function Clump({
mat = new THREE.Matrix4(),
vec = new THREE.Vector3(),
count,
extra,
...props
}: any) {
const texture = useTexture('/static/cross.jpeg')
const [ref, api] = useSphere(() => ({
args: [1],
mass: 1,
angularDamping: 0.1,
linearDamping: 0.65,
position: [rfs(20), rfs(20), rfs(20)],
}))
useFrame((state) => {
for (let i = 0; i < count; i++) {
// Get current whereabouts of the instanced sphere
;(ref.current as any).getMatrixAt(i, mat)
// Normalize the position and multiply by a negative force.
// This is enough to drive it towards the center-point.
api
.at(i)
.applyForce(
vec.setFromMatrixPosition(mat).normalize().multiplyScalar(-50).toArray(),
[0, 0, 0]
)
}
})
return (
<instancedMesh
ref={ref as any}
castShadow
receiveShadow
args={[null, null, count] as any}
geometry={sphereGeometry}
material={baubleMaterial}
material-map={texture}
/>
)
}

function UserClump({
mat = new THREE.Matrix4(),
vec = new THREE.Vector3(),
imageUrl,
userId,
i: start,
...props
}: any) {
const color = useUserColor(userId)
const [sphereGeometry] = useState(() => new THREE.CylinderGeometry(1.25, 1.25, 0.1, 64))
const [baubleMaterial] = useState(
() =>
new THREE.MeshStandardMaterial({
// color: presence?.color?.border,
color: color?.border,
roughness: 0,
envMapIntensity: 0.2,
// emissive: presence?.color?.text,
emissive: color?.text,
})
)

// TODO: error recovery if image fails to load
const texture = useTexture(imageUrl)

const [ref, api] = useCylinder(() => ({
args: [1],
mass: 1,
angularDamping: 0.1,
linearDamping: 0.65,
position: [rfs(20), rfs(20), rfs(20)],
}))
useFrame((state) => {
// Get current whereabouts of the instanced sphere
;(ref.current as any).getMatrixAt(0, mat)
// Normalize the position and multiply by a negative force.
// This is enough to drive it towards the center-point.
api
.at(0)
.applyForce(
vec.setFromMatrixPosition(mat).normalize().multiplyScalar(-50).toArray(),
[0, 0, 0]
)
})
return (
<instancedMesh
ref={ref as any}
castShadow
receiveShadow
args={[null, null, 1] as any}
geometry={sphereGeometry}
material={baubleMaterial}
material-map={texture}
/>
)
}

function Pointer() {
const viewport = useThree((state) => state.viewport)
const [, api] = useSphere(() => ({type: 'Kinematic', args: [3], position: [0, 0, 0]}))
return useFrame((state) =>
api.position.set((state.mouse.x * viewport.width) / 2, (state.mouse.y * viewport.height) / 2, 0)
)
}

function Effects(props: any) {
const {scene, camera} = useThree()
return (
<EffectComposer {...props}>
{/* @ts-expect-error -- @types/react doesn't know about this JSX Element */}
<sSAOPass args={[scene, camera, 100, 100]} kernelRadius={1.2} kernelSize={0} />
</EffectComposer>
)
}

const Container = styled(Box)`
height: 100%;
align-items: center;
justify-content: center;
flex-direction: column;
`
const Frame = styled(Card)`
height: 640px;
width: 640px;
max-height: 100%;
max-width: 100%;
`

export default memo(function Presence3D() {
const globalPresence = useGlobalPresence()
const {currentUser} = useWorkspace()
const count = globalPresence?.length + 3

return (
<Container sizing="border" display="flex">
<Box padding={4}>
<Text>The more people present the more fun! 🥳</Text>
</Box>
<Frame height="fill" overflow="hidden" radius={4} border>
<Canvas shadows dpr={[1, 2]} camera={{position: [0, 0, 20], fov: 35, near: 1, far: 40}}>
<ambientLight intensity={0.25} />
<spotLight
intensity={1}
angle={0.2}
penumbra={1}
position={[30, 30, 30]}
castShadow
shadow-mapSize={[512, 512]}
/>
<directionalLight intensity={5} position={[-10, -10, -10]} color="purple" />
<Physics gravity={[0, 2, 0]} iterations={10}>
<Pointer />
<Clump key={`c-${count}`} count={count} />
<UserClump
key="currentUser"
i={0}
userId={currentUser!.id}
imageUrl={currentUser?.profileImage}
color={'#f00'}
emissive={'#f0f'}
/>
{globalPresence.map((presence, i) => {
return (
<UserClump
key={`presence-${i}`}
userId={presence.user.id}
imageUrl={presence.user.imageUrl}
i={i + globalPresence.length}
/>
)
})}
</Physics>
<Environment files="/static/adamsbridge.hdr" />
<Effects />
<Sky />
</Canvas>
</Frame>
</Container>
)
})
1 change: 1 addition & 0 deletions dev/test-studio/plugins/presence/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './plugin'
27 changes: 27 additions & 0 deletions dev/test-studio/plugins/presence/plugin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {lazy} from 'react'
import {createPlugin} from 'sanity'
import {JoystickIcon} from '@sanity/icons'
import {route} from 'sanity/router'
import {PresenceToolConfig} from './types'

const Presence3D = lazy(() => import('./Presence3D'))

/**
* Presence playground plugin
*/
export const presenceTool = createPlugin<PresenceToolConfig | void>((options) => {
const {name, title, icon} = options || {}

return {
name: '@local/presence',
tools: [
{
name: name || 'presence',
title: title || 'Presence',
icon: icon || JoystickIcon,
component: Presence3D,
router: route.create('/*'),
},
],
}
})
7 changes: 7 additions & 0 deletions dev/test-studio/plugins/presence/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type {ComponentType} from 'react'

export interface PresenceToolConfig {
name?: string
title?: string
icon?: ComponentType
}
2 changes: 2 additions & 0 deletions dev/test-studio/sanity.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {languageFilter} from './plugins/language-filter'
import {schemaTypes} from './schema'
import {defaultDocumentNode, newDocumentOptions, structure} from './structure'
import {workshopTool} from './workshop'
import {presenceTool} from './plugins/presence'
import {
CustomLogo,
CustomLayout,
Expand Down Expand Up @@ -71,6 +72,7 @@ const sharedSettings = createPlugin({
}),
// eslint-disable-next-line camelcase
muxInput({mp4_support: 'standard'}),
presenceTool(),
],
})

Expand Down
Binary file added dev/test-studio/static/adamsbridge.hdr
Binary file not shown.
Binary file added dev/test-studio/static/cross.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2951,6 +2951,11 @@
schema-utils "^3.0.0"
source-map "^0.7.3"

"@pmndrs/cannon-worker-api@^2.1.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@pmndrs/cannon-worker-api/-/cannon-worker-api-2.2.0.tgz#f1fefff3572fe77728ac8e3095676318e4121a38"
integrity sha512-IS1Ewx4PPkz/mAAzfiKy0HtEIECtkwDsAvErEQdEn0EbVUAEnh5f0Pdnv3/1k4afZfuDj0AO+87JlbBcUUpRTw==

"@popperjs/core@^2.11.6":
version "2.11.6"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
Expand Down Expand Up @@ -3237,6 +3242,15 @@
resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.5.5.tgz#c8e94f1b9232ca7cb9d860ea67762ec401b1de14"
integrity sha512-7I/qY8H7Enwasxr4jU6WmtNK+RZ4Z/XvSlDvjXFVe7ii1x0MoSlkw6pD7xuac8qrHQRm9BTcbZNyeeKApYsvCg==

"@react-three/cannon@^6.4.0":
version "6.4.0"
resolved "https://registry.yarnpkg.com/@react-three/cannon/-/cannon-6.4.0.tgz#89aee95752c0f02caac04fa97675ccec59bd9b61"
integrity sha512-hrT7ploQOCKyP/vVSm9RWN2r2WPxjpTHDNnkxzwa/doK3IyREJ+40Ghor7E7pf76sjRgJFBrypVUSpKDcescTA==
dependencies:
"@pmndrs/cannon-worker-api" "^2.1.0"
cannon-es "^0.20.0"
cannon-es-debugger "^1.0.0"

"@react-three/drei@^9.38.1":
version "9.38.1"
resolved "https://registry.yarnpkg.com/@react-three/drei/-/drei-9.38.1.tgz#d9f124d8859725b3ea57a74cb6772326d51949f5"
Expand Down Expand Up @@ -6162,6 +6176,16 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001406, can
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001423.tgz#57176d460aa8cd85ee1a72016b961eb9aca55d91"
integrity sha512-09iwWGOlifvE1XuHokFMP7eR38a0JnajoyL3/i87c8ZjRWRrdKo1fqjNfugfBD0UDBIOz0U+jtNhJ0EPm1VleQ==

cannon-es-debugger@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/cannon-es-debugger/-/cannon-es-debugger-1.0.0.tgz#28c6679b30dcf608e978225239f447a8fe55ccb4"
integrity sha512-sE9lDOBAYFKlh+0w+cvWKwUhJef8HYnUSVPWPL0jD15MAuVRQKno4QYZSGxgOoJkMR3mQqxL4bxys2b3RSWH8g==

cannon-es@^0.20.0:
version "0.20.0"
resolved "https://registry.yarnpkg.com/cannon-es/-/cannon-es-0.20.0.tgz#bf2f62a674701f37e3d861c90668a32d4d32f082"
integrity sha512-eZhWTZIkFOnMAJOgfXJa9+b3kVlvG+FX4mdkpePev/w/rP5V8NRquGyEozcjPfEoXUlb+p7d9SUcmDSn14prOA==

capture-stack-trace@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz#1c43f6b059d4249e7f3f8724f15f048b927d3a8a"
Expand Down

0 comments on commit f6c80a8

Please sign in to comment.