Make @theatre/r3f play well with different vs of react,three,r3f #177

This commit is contained in:
Aria 2022-05-27 21:59:51 +02:00 committed by GitHub
parent e8c440f357
commit 9b4aa4b0e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 796 additions and 145 deletions

View file

@ -15,6 +15,7 @@
"@testing-library/user-event": "^12.1.10",
"@theatre/core": "^0.0.1-COMPAT.1",
"@theatre/studio": "^0.0.1-COMPAT.1",
"@theatre/r3f": "^0.0.1-COMPAT.1",
"react-scripts": "^5.0.1",
"three": ">0.132.0",
"web-vitals": "^1.0.1"

View file

@ -1,6 +1,154 @@
import React from 'react'
import {getProject} from '@theatre/core'
import * as THREE from 'three'
import React, {useState, useEffect, useRef} from 'react'
import {useFrame, Canvas} from '@react-three/fiber'
import {Shadow, softShadows} from '@react-three/drei'
import studio from '@theatre/studio'
import {editable as e, SheetProvider} from '@theatre/r3f'
import extension from '@theatre/r3f/dist/extension'
export default function App({obj}) {
console.log(obj)
return <div>hi</div>
if (process.env.NODE_ENV === 'development') {
studio.extend(extension)
studio.initialize()
}
// Soft shadows are expensive, comment and refresh when it's too slow
softShadows()
// credit: https://codesandbox.io/s/camera-pan-nsb7f
function Button() {
const vec = new THREE.Vector3()
const light = useRef(undefined)
const [active, setActive] = useState(false)
const [zoom, set] = useState(true)
useEffect(
() => void (document.body.style.cursor = active ? 'pointer' : 'auto'),
[active],
)
useFrame((state) => {
const step = 0.1
const camera = state.camera
camera.fov = THREE.MathUtils.lerp(camera.fov, zoom ? 10 : 42, step)
camera.position.lerp(
vec.set(zoom ? 25 : 10, zoom ? 1 : 5, zoom ? 0 : 10),
step,
)
state.camera.lookAt(0, 0, 0)
state.camera.updateProjectionMatrix()
light.current.position.lerp(
vec.set(zoom ? 4 : 0, zoom ? 3 : 8, zoom ? 3 : 5),
step,
)
})
return (
<e.mesh
receiveShadow
castShadow
onClick={() => set(!zoom)}
onPointerOver={() => setActive(true)}
onPointerOut={() => setActive(false)}
uniqueName="The Button"
>
<sphereBufferGeometry args={[0.75, 64, 64]} />
<meshPhysicalMaterial
color={active ? 'purple' : '#e7b056'}
clearcoat={1}
clearcoatRoughness={0}
/>
<Shadow
position-y={-0.79}
rotation-x={-Math.PI / 2}
opacity={0.6}
scale={[0.8, 0.8, 1]}
/>
<directionalLight
ref={light}
castShadow
intensity={1.5}
shadow-camera-far={70}
/>
</e.mesh>
)
}
function Plane({color, uniqueName, ...props}) {
return (
<e.mesh receiveShadow castShadow {...props} uniqueName={uniqueName}>
<boxBufferGeometry />
<meshStandardMaterial color={color} />
</e.mesh>
)
}
function App() {
return (
<div>
<Canvas
// @ts-ignore
shadowMap
gl={{preserveDrawingBuffer: true}}
linear
frameloop="demand"
dpr={[1.5, 2]}
>
<SheetProvider
sheet={getProject('Playground - R3F').sheet('R3F-Canvas')}
>
{/* @ts-ignore */}
<e.perspectiveCamera makeDefault uniqueName="Camera" />
<ambientLight intensity={0.4} />
<e.pointLight
position={[-10, -10, 5]}
intensity={2}
color="#ff20f0"
uniqueName="Light 1"
/>
<e.pointLight
position={[0, 0.5, -1]}
distance={1}
intensity={2}
color="#e4be00"
uniqueName="Light 2"
/>
<group position={[0, -0.9, -3]}>
<Plane
color="hotpink"
rotation-x={-Math.PI / 2}
position-z={2}
scale={[4, 20, 0.2]}
uniqueName="plane1"
/>
<Plane
color="#e4be00"
rotation-x={-Math.PI / 2}
position-y={1}
scale={[4.2, 0.2, 4]}
uniqueName="plane2"
/>
<Plane
color="#736fbd"
rotation-x={-Math.PI / 2}
position={[-1.7, 1, 3.5]}
scale={[0.5, 4, 4]}
uniqueName="plane3"
/>
<Plane
color="white"
rotation-x={-Math.PI / 2}
position={[0, 4.5, 3]}
scale={[2, 0.03, 4]}
uniqueName="plane4"
/>
</group>
<Button />
</SheetProvider>
</Canvas>
</div>
)
}
export default App

View file

@ -0,0 +1,3 @@
/.cache
/dist
/package-lock.json

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Theatre.js Example - DOM</title>
</head>
<body>
<div id="root"></div>
<script src="./src/index.js"></script>
</body>
</html>

View file

@ -0,0 +1,17 @@
{
"name": "@compat/parcel1-react18",
"scripts": {
"dev": "parcel serve ./index.html"
},
"dependencies": {
"parcel-bundler": "^1.12.5",
"@react-three/drei": "^7.3.1",
"@react-three/fiber": "^7.0.6",
"@theatre/core": "^0.0.1-COMPAT.1",
"@theatre/r3f": "^0.0.1-COMPAT.1",
"@theatre/studio": "^0.0.1-COMPAT.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"three": "^0.137.0"
}
}

View file

@ -0,0 +1,154 @@
import {getProject} from '@theatre/core'
import * as THREE from 'three'
import React, {useState, useEffect, useRef} from 'react'
import {useFrame, Canvas} from '@react-three/fiber'
import {Shadow, softShadows} from '@react-three/drei'
import studio from '@theatre/studio'
import {editable as e, SheetProvider} from '@theatre/r3f'
import extension from '@theatre/r3f/dist/extension'
if (process.env.NODE_ENV === 'development') {
studio.extend(extension)
studio.initialize()
}
// Soft shadows are expensive, comment and refresh when it's too slow
softShadows()
// credit: https://codesandbox.io/s/camera-pan-nsb7f
function Button() {
const vec = new THREE.Vector3()
const light = useRef(undefined)
const [active, setActive] = useState(false)
const [zoom, set] = useState(true)
useEffect(
() => void (document.body.style.cursor = active ? 'pointer' : 'auto'),
[active],
)
useFrame((state) => {
const step = 0.1
const camera = state.camera
camera.fov = THREE.MathUtils.lerp(camera.fov, zoom ? 10 : 42, step)
camera.position.lerp(
vec.set(zoom ? 25 : 10, zoom ? 1 : 5, zoom ? 0 : 10),
step,
)
state.camera.lookAt(0, 0, 0)
state.camera.updateProjectionMatrix()
light.current.position.lerp(
vec.set(zoom ? 4 : 0, zoom ? 3 : 8, zoom ? 3 : 5),
step,
)
})
return (
<e.mesh
receiveShadow
castShadow
onClick={() => set(!zoom)}
onPointerOver={() => setActive(true)}
onPointerOut={() => setActive(false)}
uniqueName="The Button"
>
<sphereBufferGeometry args={[0.75, 64, 64]} />
<meshPhysicalMaterial
color={active ? 'purple' : '#e7b056'}
clearcoat={1}
clearcoatRoughness={0}
/>
<Shadow
position-y={-0.79}
rotation-x={-Math.PI / 2}
opacity={0.6}
scale={[0.8, 0.8, 1]}
/>
<directionalLight
ref={light}
castShadow
intensity={1.5}
shadow-camera-far={70}
/>
</e.mesh>
)
}
function Plane({color, uniqueName, ...props}) {
return (
<e.mesh receiveShadow castShadow {...props} uniqueName={uniqueName}>
<boxBufferGeometry />
<meshStandardMaterial color={color} />
</e.mesh>
)
}
function App() {
return (
<div>
<Canvas
// @ts-ignore
shadowMap
gl={{preserveDrawingBuffer: true}}
linear
frameloop="demand"
dpr={[1.5, 2]}
>
<SheetProvider
sheet={getProject('Playground - R3F').sheet('R3F-Canvas')}
>
{/* @ts-ignore */}
<e.perspectiveCamera makeDefault uniqueName="Camera" />
<ambientLight intensity={0.4} />
<e.pointLight
position={[-10, -10, 5]}
intensity={2}
color="#ff20f0"
uniqueName="Light 1"
/>
<e.pointLight
position={[0, 0.5, -1]}
distance={1}
intensity={2}
color="#e4be00"
uniqueName="Light 2"
/>
<group position={[0, -0.9, -3]}>
<Plane
color="hotpink"
rotation-x={-Math.PI / 2}
position-z={2}
scale={[4, 20, 0.2]}
uniqueName="plane1"
/>
<Plane
color="#e4be00"
rotation-x={-Math.PI / 2}
position-y={1}
scale={[4.2, 0.2, 4]}
uniqueName="plane2"
/>
<Plane
color="#736fbd"
rotation-x={-Math.PI / 2}
position={[-1.7, 1, 3.5]}
scale={[0.5, 4, 4]}
uniqueName="plane3"
/>
<Plane
color="white"
rotation-x={-Math.PI / 2}
position={[0, 4.5, 3]}
scale={[2, 0.03, 4]}
uniqueName="plane4"
/>
</group>
<Button />
</SheetProvider>
</Canvas>
</div>
)
}
export default App

View file

@ -0,0 +1,13 @@
import ReactDOM from 'react-dom'
import '@theatre/studio'
import React from 'react'
import App from './App'
console.log('hj')
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
)

View file

@ -8,10 +8,14 @@
"preview": "vite preview"
},
"dependencies": {
"@react-three/drei": "^9.11.3",
"@react-three/fiber": "^8.0.19",
"@theatre/core": "^0.0.1-COMPAT.1",
"@theatre/r3f": "^0.0.1-COMPAT.1",
"@theatre/studio": "^0.0.1-COMPAT.1",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react-dom": "^18.0.0",
"three": "^0.141.0"
},
"devDependencies": {
"@types/react": "^18.0.0",

View file

@ -1,6 +1,154 @@
import React from 'react'
import {getProject} from '@theatre/core'
import * as THREE from 'three'
import React, {useState, useEffect, useRef} from 'react'
import {useFrame, Canvas} from '@react-three/fiber'
import {Shadow, softShadows} from '@react-three/drei'
import studio from '@theatre/studio'
import {editable as e, SheetProvider} from '@theatre/r3f'
import extension from '@theatre/r3f/dist/extension'
export default function App({obj}) {
console.log(obj)
return <div>hi</div>
if (process.env.NODE_ENV === 'development') {
studio.extend(extension)
studio.initialize()
}
// Soft shadows are expensive, comment and refresh when it's too slow
softShadows()
// credit: https://codesandbox.io/s/camera-pan-nsb7f
function Button() {
const vec = new THREE.Vector3()
const light = useRef(undefined)
const [active, setActive] = useState(false)
const [zoom, set] = useState(true)
useEffect(
() => void (document.body.style.cursor = active ? 'pointer' : 'auto'),
[active],
)
useFrame((state) => {
const step = 0.1
const camera = state.camera
camera.fov = THREE.MathUtils.lerp(camera.fov, zoom ? 10 : 42, step)
camera.position.lerp(
vec.set(zoom ? 25 : 10, zoom ? 1 : 5, zoom ? 0 : 10),
step,
)
state.camera.lookAt(0, 0, 0)
state.camera.updateProjectionMatrix()
light.current.position.lerp(
vec.set(zoom ? 4 : 0, zoom ? 3 : 8, zoom ? 3 : 5),
step,
)
})
return (
<e.mesh
receiveShadow
castShadow
onClick={() => set(!zoom)}
onPointerOver={() => setActive(true)}
onPointerOut={() => setActive(false)}
uniqueName="The Button"
>
<sphereBufferGeometry args={[0.75, 64, 64]} />
<meshPhysicalMaterial
color={active ? 'purple' : '#e7b056'}
clearcoat={1}
clearcoatRoughness={0}
/>
<Shadow
position-y={-0.79}
rotation-x={-Math.PI / 2}
opacity={0.6}
scale={[0.8, 0.8, 1]}
/>
<directionalLight
ref={light}
castShadow
intensity={1.5}
shadow-camera-far={70}
/>
</e.mesh>
)
}
function Plane({color, uniqueName, ...props}) {
return (
<e.mesh receiveShadow castShadow {...props} uniqueName={uniqueName}>
<boxBufferGeometry />
<meshStandardMaterial color={color} />
</e.mesh>
)
}
function App() {
return (
<div>
<Canvas
// @ts-ignore
shadowMap
gl={{preserveDrawingBuffer: true}}
linear
frameloop="demand"
dpr={[1.5, 2]}
>
<SheetProvider
sheet={getProject('Playground - R3F').sheet('R3F-Canvas')}
>
{/* @ts-ignore */}
<e.perspectiveCamera makeDefault uniqueName="Camera" />
<ambientLight intensity={0.4} />
<e.pointLight
position={[-10, -10, 5]}
intensity={2}
color="#ff20f0"
uniqueName="Light 1"
/>
<e.pointLight
position={[0, 0.5, -1]}
distance={1}
intensity={2}
color="#e4be00"
uniqueName="Light 2"
/>
<group position={[0, -0.9, -3]}>
<Plane
color="hotpink"
rotation-x={-Math.PI / 2}
position-z={2}
scale={[4, 20, 0.2]}
uniqueName="plane1"
/>
<Plane
color="#e4be00"
rotation-x={-Math.PI / 2}
position-y={1}
scale={[4.2, 0.2, 4]}
uniqueName="plane2"
/>
<Plane
color="#736fbd"
rotation-x={-Math.PI / 2}
position={[-1.7, 1, 3.5]}
scale={[0.5, 4, 4]}
uniqueName="plane3"
/>
<Plane
color="white"
rotation-x={-Math.PI / 2}
position={[0, 4.5, 3]}
scale={[2, 0.03, 4]}
uniqueName="plane4"
/>
</group>
<Button />
</SheetProvider>
</Canvas>
</div>
)
}
export default App

View file

@ -1,19 +1,11 @@
// import ReactDOM from 'react-dom'
import ReactDOM from 'react-dom'
import '@theatre/studio'
import React from 'react'
import App from './App'
import studio from '@theatre/studio'
import {getProject} from '@theatre/core'
// import React from 'react'
// import App from './App'
studio.initialize({usePersistentStorage: false})
const project = getProject('Project')
const sheet = project.sheet('Sheet')
const obj = sheet.object('Obj', {str: 'some string', num: 0})
// ReactDOM.render(
// <React.StrictMode>
// <App obj={obj} />
// </React.StrictMode>,
// document.getElementById('root'),
// )
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
)

View file

@ -1,11 +1,11 @@
import {getProject} from '@theatre/core'
import * as THREE from 'three'
import {useState, useEffect, useRef} from 'react'
import React, {useState, useEffect, useRef} from 'react'
import {useFrame, Canvas} from '@react-three/fiber'
import {Shadow, softShadows} from '@react-three/drei'
import React from 'react'
import studio from '@theatre/studio'
import {editable as e, SheetProvider, extension} from '@theatre/r3f'
import {editable as e, SheetProvider} from '@theatre/r3f'
import extension from '@theatre/r3f/dist/extension'
if (process.env.NODE_ENV === 'development') {
studio.extend(extension)
@ -90,6 +90,10 @@ function App() {
<Canvas
// @ts-ignore
shadowMap
gl={{preserveDrawingBuffer: true}}
linear
frameloop="demand"
dpr={[1.5, 2]}
>
<SheetProvider
sheet={getProject('Playground - R3F').sheet('R3F-Canvas')}

View file

@ -8,7 +8,7 @@ import App from './App'
ReactDOM.render(
<React.StrictMode>
<App project={getProject('CRA project')} />
<App />
</React.StrictMode>,
document.getElementById('root'),
)

View file

@ -2,7 +2,7 @@ import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import studio from '@theatre/studio'
import {extension} from '@theatre/r3f'
import extension from '@theatre/r3f/dist/extension'
studio.extend(extension)
studio.initialize()

View file

@ -2,7 +2,7 @@ import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import studio from '@theatre/studio'
import {extension} from '@theatre/r3f'
import extension from '@theatre/r3f/dist/extension'
studio.extend(extension)
studio.initialize()

View file

@ -0,0 +1,74 @@
import path = require('path')
import {build} from 'esbuild'
const definedGlobals = {
'process.env.version': JSON.stringify(require('../package.json').version),
'process.env.NODE_ENV': JSON.stringify('production'),
}
createBundles()
async function createBundles() {
createMainBundle()
createExtensionBundle()
async function createMainBundle() {
const pathToEntry = path.join(__dirname, '../src/index.ts')
const esbuildConfig: Parameters<typeof build>[0] = {
entryPoints: [pathToEntry],
target: ['es6'],
loader: {'.svg': 'text', '.png': 'dataurl'},
bundle: true,
sourcemap: true,
define: {...definedGlobals},
external: [
'@theatre/core',
'@theatre/dataverse',
'@theatre/react',
'@theatre/studio',
'react',
'react-dom',
'three',
'@react-three/fiber',
],
platform: 'browser',
mainFields: ['browser', 'module', 'main'],
conditions: ['browser', 'node'],
outfile: path.join(__dirname, '../dist/index.js'),
format: 'cjs',
metafile: true,
}
const result = await build(esbuildConfig)
}
async function createExtensionBundle() {
const pathToEntry = path.join(__dirname, '../src/extension/index.ts')
const esbuildConfig: Parameters<typeof build>[0] = {
entryPoints: [pathToEntry],
target: 'es6',
loader: {'.svg': 'text', '.png': 'dataurl'},
bundle: true,
sourcemap: true,
define: {...definedGlobals},
external: [
'@theatre/core',
'@theatre/studio',
'@theatre/dataverse',
'@theatre/r3f',
// 'three',
// '@react-three/fiber',
// '@react-three/drei',
// 'three-stdlib',
],
platform: 'browser',
mainFields: ['browser', 'module', 'main'],
conditions: ['browser'],
outfile: path.join(__dirname, '../dist/extension/index.js'),
format: 'cjs',
metafile: true,
}
const result = await build(esbuildConfig)
}
}

View file

@ -0,0 +1,3 @@
{
}

View file

@ -21,31 +21,35 @@
},
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"sideEffects": false,
"files": [
"dist/**/*"
],
"exports": {
".": "./dist/index.js",
"./extension": "./dist/extension/index.js"
},
"scripts": {
"prepack": "yarn run build",
"typecheck": "yarn run build",
"build": "tsc --build ./tsconfig.json",
"build": "run-s build:ts build:js",
"build:js": "node -r esbuild-register devEnv/bundle.ts",
"build:ts": "tsc --build ./tsconfig.json",
"prepublish": "yarn run build",
"clean": "rm -rf ./dist && rm -f tsconfig.tsbuildinfo"
},
"devDependencies": {
"@react-three/drei": "^7.3.1",
"@theatre/react": "workspace:*",
"@types/jest": "^26.0.23",
"@types/lodash-es": "^4.17.4",
"@types/node": "^15.6.2",
"@types/react": "^17.0.9",
"@types/styled-components": "^5.1.9",
"npm-run-all": "^4.1.5",
"typescript": "^4.4.2"
},
"dependencies": {
"@react-three/drei": "^7.3.1",
"@theatre/react": "workspace:*",
"esbuild": "^0.12.15",
"esbuild-register": "^2.5.0",
"lodash-es": "^4.17.21",
"npm-run-all": "^4.1.5",
"polished": "^4.1.3",
"react-icons": "^4.2.0",
"react-merge-refs": "^1.1.0",
@ -53,14 +57,15 @@
"react-use-measure": "^2.0.4",
"reakit": "^1.3.8",
"styled-components": "^5.3.0",
"typescript": "^4.4.2",
"zustand": "^3.5.1"
},
"peerDependencies": {
"@react-three/fiber": "^7.0.6",
"@react-three/fiber": ">=7.0.6",
"@theatre/core": "*",
"@theatre/studio": "*",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"three": "^0.131.3"
"react": ">=17.0.2",
"react-dom": ">=17.0.2",
"three": ">=0.131.3"
}
}

View file

@ -1,22 +0,0 @@
import type {ComponentProps, ElementType} from 'react'
import React from 'react'
import {useEditorStore} from '../store'
import {createPortal} from '@react-three/fiber'
export type EditorHelperProps<T extends ElementType> = {
component: T
} & ComponentProps<T>
const EditorHelper = <T extends ElementType>({
component: Component,
...props
}: EditorHelperProps<T>) => {
const helpersRoot = useEditorStore((state) => state.helpersRoot)
if (process.env.NODE_ENV === 'development') {
return <>{createPortal(<Component {...props} />, helpersRoot)}</>
} else {
return null
}
}
export default EditorHelper

View file

@ -0,0 +1,11 @@
module.exports = {
rules: {
'no-restricted-syntax': [
'error',
{
selector: `ImportDeclaration[importKind!='type'][source.value=/\\u002Fmain\\u002F/]`,
message: `The extension should not be able to import the internals of main. If you need to use this API, expose it as __private_api from @theatre/r3f/src/index.ts`,
},
],
},
}

View file

@ -2,17 +2,17 @@ import type {Object3D} from 'three'
import type {VFC} from 'react'
import React, {useEffect, useLayoutEffect, useMemo, useState} from 'react'
import {Sphere, Html} from '@react-three/drei'
import {useEditorStore} from '../store'
import shallow from 'zustand/shallow'
import studio from '@theatre/studio'
import {useSelected} from './useSelected'
import {useVal} from '@theatre/react'
import {getEditorSheetObject} from './editorStuff'
import {getEditorSheetObject} from '../editorStuff'
import type {IconID} from '../icons'
import icons from '../icons'
import type {Helper} from '../editableFactoryConfigUtils'
import type {Helper} from '../../main/editableFactoryConfigUtils'
import {invalidate, useFrame, useThree} from '@react-three/fiber'
import {useDragDetector} from './DragDetector'
import useExtensionStore from '../useExtensionStore'
export interface EditableProxyProps {
storeKey: string
@ -21,7 +21,7 @@ export interface EditableProxyProps {
const EditableProxy: VFC<EditableProxyProps> = ({storeKey, object}) => {
const editorObject = getEditorSheetObject()
const [setSnapshotProxyObject, editables] = useEditorStore(
const [setSnapshotProxyObject, editables] = useExtensionStore(
(state) => [state.setSnapshotProxyObject, state.editables],
shallow,
)
@ -122,7 +122,7 @@ const EditableProxy: VFC<EditableProxyProps> = ({storeKey, object}) => {
e.stopPropagation()
const theatreObject =
useEditorStore.getState().editables[storeKey].sheetObject
useExtensionStore.getState().editables[storeKey].sheetObject
if (!theatreObject) {
console.log('no theatre object for', storeKey)
@ -169,7 +169,7 @@ const EditableProxy: VFC<EditableProxyProps> = ({storeKey, object}) => {
if (e.delta < 2) {
e.stopPropagation()
const theatreObject =
useEditorStore.getState().editables[storeKey].sheetObject
useExtensionStore.getState().editables[storeKey].sheetObject
if (!theatreObject) {
console.log('no theatre object for', storeKey)

View file

@ -1,7 +1,6 @@
import type {VFC} from 'react'
import React, {useLayoutEffect, useMemo, useRef, useState} from 'react'
import type {Editable} from '../store'
import {useEditorStore} from '../store'
import type {Editable} from '../../main/store'
import {createPortal} from '@react-three/fiber'
import EditableProxy from './EditableProxy'
import type {OrbitControls} from 'three-stdlib'
@ -13,7 +12,8 @@ import type {IScrub} from '@theatre/studio'
import studio from '@theatre/studio'
import {useSelected} from './useSelected'
import {useVal} from '@theatre/react'
import {getEditorSheetObject} from './editorStuff'
import {getEditorSheetObject} from '../editorStuff'
import useExtensionStore from '../useExtensionStore'
export interface ProxyManagerProps {
orbitControlsRef: React.MutableRefObject<OrbitControls | null>
@ -28,7 +28,7 @@ type IEditableProxy<T> = {
const ProxyManager: VFC<ProxyManagerProps> = ({orbitControlsRef}) => {
const isBeingEdited = useRef(false)
const editorObject = getEditorSheetObject()
const [sceneSnapshot, editables] = useEditorStore(
const [sceneSnapshot, editables] = useExtensionStore(
(state) => [state.sceneSnapshot, state.editables],
shallow,
)

View file

@ -1,20 +1,27 @@
import type {VFC} from 'react'
import {useMemo} from 'react'
import React, {useEffect, useLayoutEffect, useRef} from 'react'
import {useEditorStore} from '../../store'
import shallow from 'zustand/shallow'
import type {WebGLRenderer} from 'three'
import useMeasure from 'react-use-measure'
import styled, {keyframes} from 'styled-components'
import {TiWarningOutline} from 'react-icons/ti'
// This is ugly, but pure TS doesn't let you do bundler-stuff
import noiseImageUrl from './noiseImage'
import noiseImageUrl from './noise-transparent.png'
import useExtensionStore from '../../useExtensionStore'
import {useVal} from '@theatre/react'
import {getEditorSheetObject} from '../../editorStuff'
import studio from '@theatre/studio'
const Container = styled.div`
position: relative;
width: fit-content;
height: fit-content;
border-radius: 4px;
pointer-events: auto;
cursor: pointer;
overflow: hidden;
&.hidden {
border-radius: 8px;
}
`
const Canvas = styled.canvas`
@ -64,14 +71,27 @@ const Warning = styled.div`
opacity: 0.8;
`
const Dot = styled.div`
width: 8px;
height: 8px;
border-radius: 50%;
background-color: red;
pointer-events: auto;
cursor: pointer;
`
interface ReferenceWindowProps {
height: number
maxHeight: number
maxWidth: number
}
const ReferenceWindow: VFC<ReferenceWindowProps> = ({height}) => {
const ReferenceWindow: VFC<ReferenceWindowProps> = ({maxHeight, maxWidth}) => {
const canvasRef = useRef<HTMLCanvasElement>(null)
const [gl] = useEditorStore((state) => [state.gl], shallow)
const visible =
useVal(getEditorSheetObject()?.props.viewport.showReferenceWindow) ?? true
const [gl] = useExtensionStore((state) => [state.gl], shallow)
const [ref, bounds] = useMeasure()
const preserveDrawingBuffer =
@ -85,7 +105,18 @@ const ReferenceWindow: VFC<ReferenceWindowProps> = ({height}) => {
}
}, [gl, ref])
const [width, height] = useMemo(() => {
if (!gl) return [0, 0]
const aspectRatio = gl.domElement.width / gl.domElement.height
const width = Math.min(aspectRatio * maxHeight, maxWidth)
const height = width / aspectRatio
return [width, height]
}, [gl, maxWidth, maxHeight])
useEffect(() => {
// if (!visible) return
let animationHandle: number
const draw = (gl: WebGLRenderer) => () => {
animationHandle = requestAnimationFrame(draw(gl))
@ -95,8 +126,6 @@ const ReferenceWindow: VFC<ReferenceWindowProps> = ({height}) => {
return
}
const width = (gl.domElement.width / gl.domElement.height) * height
const ctx = canvasRef.current!.getContext('2d')!
// https://stackoverflow.com/questions/17861447/html5-canvas-drawimage-how-to-apply-antialiasing
@ -115,20 +144,29 @@ const ReferenceWindow: VFC<ReferenceWindowProps> = ({height}) => {
return () => {
cancelAnimationFrame(animationHandle)
}
}, [gl, height, preserveDrawingBuffer])
}, [gl, width, height, preserveDrawingBuffer])
const toggleVisibility = () => {
studio.transaction(({set}) => {
set(getEditorSheetObject()!.props.viewport.showReferenceWindow, !visible)
})
}
// if (!visible) {
// return <Dot onClick={toggleVisibility} />
// }
return (
<Container>
<Container
onClick={toggleVisibility}
className={`${visible ? 'visible' : 'hidden'}`}
style={{
width: (visible ? width : 12) + 'px',
height: (visible ? height : 12) + 'px',
}}
>
{gl?.domElement && preserveDrawingBuffer ? (
<Canvas
ref={canvasRef}
width={
((bounds.width || gl.domElement.width) /
(bounds.height || gl.domElement.height)) *
height
}
height={height}
/>
<Canvas ref={canvasRef} width={width} height={height} />
) : (
<Static>
<Warning>

View file

@ -1,8 +1,8 @@
import {useCallback, useEffect, useLayoutEffect, useMemo, useState} from 'react'
import React from 'react'
import {Canvas, useThree} from '@react-three/fiber'
import type {BaseSheetObjectType} from '../store'
import {allRegisteredObjects, useEditorStore} from '../store'
import type {BaseSheetObjectType} from '../../main/store'
import {__private_allRegisteredObjects as allRegisteredObjects} from '@theatre/r3f'
import shallow from 'zustand/shallow'
import root from 'react-shadow/styled-components'
import ProxyManager from './ProxyManager'
@ -11,11 +11,13 @@ import {useVal} from '@theatre/react'
import styled, {createGlobalStyle, StyleSheetManager} from 'styled-components'
import type {ISheet} from '@theatre/core'
import useSnapshotEditorCamera from './useSnapshotEditorCamera'
import {getEditorSheet, getEditorSheetObject} from './editorStuff'
import {getEditorSheet, getEditorSheetObject} from '../editorStuff'
import type {$IntentionalAny} from '@theatre/shared/utils/types'
import {InfiniteGridHelper} from '../InfiniteGridHelper'
import {DragDetectorProvider} from './DragDetector'
import ReferenceWindow from './ReferenceWindow/ReferenceWindow'
import useExtensionStore from '../useExtensionStore'
import useMeasure from 'react-use-measure'
const GlobalStyle = createGlobalStyle`
:host {
@ -62,7 +64,7 @@ const EditorScene: React.FC<{snapshotEditorSheet: ISheet; paneId: string}> = ({
}
}, [gl, viewportLighting, scene, camera])
const helpersRoot = useEditorStore((state) => state.helpersRoot, shallow)
const helpersRoot = useExtensionStore((state) => state.helpersRoot, shallow)
const showGrid = useVal(editorObject?.props.viewport.showGrid) ?? true
const showAxes = useVal(editorObject?.props.viewport.showAxes) ?? true
@ -128,11 +130,9 @@ const SnapshotEditor: React.FC<{paneId: string}> = (props) => {
const snapshotEditorSheet = getEditorSheet()
const paneId = props.paneId
const editorObject = getEditorSheetObject()
const [ref, bounds] = useMeasure()
const showReferenceWindow =
useVal(editorObject?.props.viewport.showReferenceWindow) ?? true
const [sceneSnapshot, createSnapshot] = useEditorStore(
const [sceneSnapshot, createSnapshot] = useExtensionStore(
(state) => [state.sceneSnapshot, state.createSnapshot],
shallow,
)
@ -184,16 +184,17 @@ const SnapshotEditor: React.FC<{paneId: string}> = (props) => {
<Wrapper>
<Overlay>
<Tools ref={setToolsContainer} />
{showReferenceWindow && (
<ReferenceWindowContainer>
<ReferenceWindow height={120} />
<ReferenceWindow
maxHeight={Math.min(bounds.height * 0.3, 120)}
maxWidth={Math.min(bounds.width * 0.4, 200)}
/>
</ReferenceWindowContainer>
)}
</Overlay>
{sceneSnapshot ? (
<>
<CanvasWrapper>
<CanvasWrapper ref={ref}>
<Canvas
onCreated={({gl}) => {
gl.setClearColor('white')

View file

@ -1,9 +1,11 @@
import {useLayoutEffect, useRef, useState} from 'react'
import {allRegisteredObjects} from '../store'
import {
__private_allRegisteredObjects as allRegisteredObjects,
__private_makeStoreKey as makeStoreKey,
} from '@theatre/r3f'
import studio from '@theatre/studio'
import type {ISheetObject} from '@theatre/core'
import type {$IntentionalAny} from '../types'
import {makeStoreKey} from '../utils'
import type {$IntentionalAny} from '../../types'
export function useSelected(): undefined | string {
const [state, set] = useState<string | undefined>(undefined)

View file

@ -1,11 +1,11 @@
import SnapshotEditor from './components/SnapshotEditor'
import type {IExtension} from '@theatre/studio'
import {prism, Ticker, val} from '@theatre/dataverse'
import {getEditorSheetObject} from './components/editorStuff'
import {useEditorStore} from './store'
import {getEditorSheetObject} from './editorStuff'
import ReactDOM from 'react-dom'
import React from 'react'
import type {ToolsetConfig} from '@theatre/studio'
import useExtensionStore from './useExtensionStore'
const io5CameraOutline = `<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><title>Camera</title><path d="M350.54 148.68l-26.62-42.06C318.31 100.08 310.62 96 302 96h-92c-8.62 0-16.31 4.08-21.92 10.62l-26.62 42.06C155.85 155.23 148.62 160 140 160H80a32 32 0 00-32 32v192a32 32 0 0032 32h352a32 32 0 0032-32V192a32 32 0 00-32-32h-59c-8.65 0-16.85-4.77-22.46-11.32z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/><circle cx="256" cy="272" r="80" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="32"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M124 158v-22h-24v22"/></svg>`
const gameIconMove = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 34.47l-90.51 90.51h67.883v108.393H124.98V165.49L34.47 256l90.51 90.51v-67.883h108.393V387.02H165.49L256 477.53l90.51-90.51h-67.883V278.627H387.02v67.883L477.53 256l-90.51-90.51v67.883H278.627V124.98h67.883L256 34.47z"/></svg>`
@ -40,7 +40,7 @@ const r3fExtension: IExtension = {
})
},
'snapshot-editor': (set, studio) => {
const {createSnapshot} = useEditorStore.getState()
const {createSnapshot} = useExtensionStore.getState()
const calc = prism<ToolsetConfig>(() => {
const editorObject = getEditorSheetObject()

View file

@ -0,0 +1,6 @@
import {____private_editorStore} from '@theatre/r3f'
import create from 'zustand'
const useExtensionStore = create(____private_editorStore)
export default useExtensionStore

View file

@ -1,3 +1,7 @@
declare module '*.txt' {
export default string
}
declare module '*.png' {
export default string
}

25
packages/r3f/src/index.ts Normal file
View file

@ -0,0 +1,25 @@
export {default as editable} from './main/editable'
export type {EditableState, BindFunction} from './main/store'
/**
* This is a private API that's exported so that `@theatre/r3f/extension`
* and `@theatre/r3f` can talk to one another. This API _could_ change
* between patch releases, so please don't build on it :)
*
* @internal
*/
export {
editorStore as ____private_editorStore,
allRegisteredObjects as __private_allRegisteredObjects,
} from './main/store'
/**
* This is a private API that's exported so that `@theatre/r3f/extension`
* and `@theatre/r3f` can talk to one another. This API _could_ change
* between patch releases, so please don't build on it :)
*
* @internal
*/
export {makeStoreKey as __private_makeStoreKey} from './main/utils'
export {default as SheetProvider, useCurrentSheet} from './main/SheetProvider'
export {refreshSnapshot} from './main/utils'
export {default as RefreshSnapshot} from './main/RefreshSnapshot'

View file

@ -1,8 +0,0 @@
export {default as extension} from './extension'
export {default as EditorHelper} from './components/EditorHelper'
export type {EditorHelperProps} from './components/EditorHelper'
export {default as editable} from './components/editable'
export type {EditableState, BindFunction} from './store'
export {default as SheetProvider, useCurrentSheet} from './SheetProvider'
export {refreshSnapshot} from './utils'
export {default as RefreshSnapshot} from './components/RefreshSnapshot'

View file

@ -0,0 +1,11 @@
module.exports = {
rules: {
'no-restricted-syntax': [
'error',
{
selector: `ImportDeclaration[importKind!='type'][source.value=/\\u002Fextension\\u002F/]`,
message: `The main bundle should not be able to import the internals of extension.`,
},
],
},
}

View file

@ -1,5 +1,5 @@
import React, {useEffect} from 'react'
import {refreshSnapshot} from '../utils'
import {refreshSnapshot} from './utils'
/**
* Putting this element in a suspense tree makes sure the snapshot editor

View file

@ -1,14 +1,14 @@
import type {ComponentProps, ComponentType, RefAttributes} from 'react'
import React, {forwardRef, useLayoutEffect, useRef, useState} from 'react'
import {allRegisteredObjects, useEditorStore} from '../store'
import {allRegisteredObjects, editorStore} from './store'
import mergeRefs from 'react-merge-refs'
import type {$FixMe} from '@theatre/shared/utils/types'
import type {ISheetObject} from '@theatre/core'
import useInvalidate from './useInvalidate'
import {useCurrentSheet} from '../SheetProvider'
import defaultEditableFactoryConfig from '../defaultEditableFactoryConfig'
import type {EditableFactoryConfig} from '../editableFactoryConfigUtils'
import {makeStoreKey} from '../utils'
import {useCurrentSheet} from './SheetProvider'
import defaultEditableFactoryConfig from './defaultEditableFactoryConfig'
import type {EditableFactoryConfig} from './editableFactoryConfigUtils'
import {makeStoreKey} from './utils'
import type {$FixMe} from '../types'
const createEditable = <Keys extends keyof JSX.IntrinsicElements>(
config: EditableFactoryConfig,
@ -78,7 +78,7 @@ const createEditable = <Keys extends keyof JSX.IntrinsicElements>(
if (objRef) objRef!.current = sheetObject
useEditorStore.getState().addEditable(storeKey, {
editorStore.getState().addEditable(storeKey, {
type: actualType,
sheetObject,
visibleOnlyInEditor: visible === 'editor',

View file

@ -1,7 +1,7 @@
import type {UnknownShorthandCompoundProps} from '@theatre/core'
import {types} from '@theatre/core'
import type {Object3D} from 'three'
import type {IconID} from './icons'
import type {IconID} from '../extension/icons'
import {Color} from 'three'
export type Helper = Object3D & {

View file

@ -1,5 +1,5 @@
import type {StateCreator} from 'zustand'
import create from 'zustand'
import create from 'zustand/vanilla'
import type {Object3D, Scene, WebGLRenderer} from 'three'
import {Group} from 'three'
import type {ISheetObject} from '@theatre/core'
@ -102,7 +102,7 @@ const config: StateCreator<EditorStore> = (set) => {
}
}
export const useEditorStore = create<EditorStore>(config)
export const editorStore = create<EditorStore>(config)
export type BindFunction = (options: {
allowImplicitInstancing?: boolean
@ -111,6 +111,6 @@ export type BindFunction = (options: {
}) => void
export const bindToCanvas: BindFunction = ({gl, scene}) => {
const init = useEditorStore.getState().init
const init = editorStore.getState().init
init(scene, gl)
}

View file

@ -1,7 +1,7 @@
import {useEditorStore} from './store'
import {editorStore} from './store'
import type {ISheet} from '@theatre/core'
export const refreshSnapshot = useEditorStore.getState().createSnapshot
export const refreshSnapshot = editorStore.getState().createSnapshot
export const makeStoreKey = (sheet: ISheet, name: string) =>
`${sheet.address.sheetId}:${sheet.address.sheetInstanceId}:${name}`

View file

@ -5,6 +5,7 @@
"lib": ["ESNext", "DOM"],
"rootDir": "src",
"types": ["jest", "node"],
"emitDeclarationOnly": true,
"composite": true
},
"references": [{"path": "../../theatre"}],

View file

@ -24,7 +24,8 @@
"@theatre/shared/*": ["./theatre/shared/src/*"],
"@theatre/dataverse": ["./packages/dataverse/src/index.ts"],
"@theatre/react": ["./packages/react/src/index.ts"],
"@theatre/r3f": ["./packages/r3f/src/index.tsx"],
"@theatre/r3f": ["./packages/r3f/src/index.ts"],
"@theatre/r3f/dist/extension": ["./packages/r3f/src/extension/index.ts"],
"@theatre/dataverse-experiments": [
"./packages/dataverse-experiments/src/index.ts"
]

View file

@ -6605,6 +6605,8 @@ __metadata:
"@types/node": ^15.6.2
"@types/react": ^17.0.9
"@types/styled-components": ^5.1.9
esbuild: ^0.12.15
esbuild-register: ^2.5.0
lodash-es: ^4.17.21
npm-run-all: ^4.1.5
polished: ^4.1.3
@ -6617,12 +6619,12 @@ __metadata:
typescript: ^4.4.2
zustand: ^3.5.1
peerDependencies:
"@react-three/fiber": ^7.0.6
"@react-three/fiber": ">=7.0.6"
"@theatre/core": "*"
"@theatre/studio": "*"
react: ^17.0.2
react-dom: ^17.0.2
three: ^0.131.3
react: ">=17.0.2"
react-dom: ">=17.0.2"
three: ">=0.131.3"
languageName: unknown
linkType: soft