theatre/packages/plugin-r3f/src/store.ts

281 lines
7.6 KiB
TypeScript
Raw Normal View History

2021-06-18 13:05:06 +02:00
import type {StateCreator} from 'zustand'
import create from 'zustand'
2021-06-29 15:08:02 +02:00
import type {Object3D, Scene, WebGLRenderer} from 'three'
2021-07-03 15:16:18 +02:00
import {Group} from 'three'
2021-06-18 13:05:06 +02:00
import type {ISheet, ISheetObject} from '@theatre/core'
2021-08-08 22:25:48 +02:00
import {types} from '@theatre/core'
2021-06-18 13:05:06 +02:00
export type EditableType =
| 'group'
| 'mesh'
| 'spotLight'
| 'directionalLight'
| 'pointLight'
| 'perspectiveCamera'
| 'orthographicCamera'
export type TransformControlsMode = 'translate' | 'rotate' | 'scale'
export type TransformControlsSpace = 'world' | 'local'
export type ViewportShading = 'wireframe' | 'flat' | 'solid' | 'rendered'
const positionComp = types.number(1, {nudgeMultiplier: 0.1})
const rotationComp = types.number(1, {nudgeMultiplier: 0.02})
const scaleComp = types.number(1, {nudgeMultiplier: 0.1})
2021-07-03 15:36:00 +02:00
export const baseSheetObjectType = types.compound({
position: types.compound({
x: positionComp,
y: positionComp,
z: positionComp,
2021-06-29 15:47:27 +02:00
}),
2021-07-03 15:36:00 +02:00
rotation: types.compound({
x: rotationComp,
y: rotationComp,
z: rotationComp,
2021-07-03 15:36:00 +02:00
}),
scale: types.compound({
x: scaleComp,
y: scaleComp,
z: scaleComp,
2021-07-03 15:36:00 +02:00
}),
})
2021-06-18 13:05:06 +02:00
2021-07-03 15:36:00 +02:00
export type BaseSheetObjectType = ISheetObject<typeof baseSheetObjectType>
2021-06-18 13:05:06 +02:00
export interface AbstractEditable<T extends EditableType> {
type: T
role: 'active' | 'removed'
sheetObject?: ISheetObject<any>
}
// all these identical types are to prepare for a future in which different object types have different properties
export interface EditableGroup extends AbstractEditable<'group'> {
sheetObject?: BaseSheetObjectType
}
export interface EditableMesh extends AbstractEditable<'mesh'> {
sheetObject?: BaseSheetObjectType
}
export interface EditableSpotLight extends AbstractEditable<'spotLight'> {
sheetObject?: BaseSheetObjectType
}
export interface EditableDirectionalLight
extends AbstractEditable<'directionalLight'> {
sheetObject?: BaseSheetObjectType
}
export interface EditablePointLight extends AbstractEditable<'pointLight'> {
sheetObject?: BaseSheetObjectType
}
export interface EditablePerspectiveCamera
extends AbstractEditable<'perspectiveCamera'> {
sheetObject?: BaseSheetObjectType
}
export interface EditableOrthographicCamera
extends AbstractEditable<'orthographicCamera'> {
sheetObject?: BaseSheetObjectType
}
export type Editable =
| EditableGroup
| EditableMesh
| EditableSpotLight
| EditableDirectionalLight
| EditablePointLight
| EditablePerspectiveCamera
| EditableOrthographicCamera
export type EditableSnapshot<T extends Editable = Editable> = {
proxyObject?: Object3D | null
} & T
export interface AbstractSerializedEditable<T extends EditableType> {
type: T
}
export interface SerializedEditableGroup
2021-06-29 15:08:02 +02:00
extends AbstractSerializedEditable<'group'> {}
2021-06-18 13:05:06 +02:00
export interface SerializedEditableMesh
2021-06-29 15:08:02 +02:00
extends AbstractSerializedEditable<'mesh'> {}
2021-06-18 13:05:06 +02:00
export interface SerializedEditableSpotLight
2021-06-29 15:08:02 +02:00
extends AbstractSerializedEditable<'spotLight'> {}
2021-06-18 13:05:06 +02:00
export interface SerializedEditableDirectionalLight
2021-06-29 15:08:02 +02:00
extends AbstractSerializedEditable<'directionalLight'> {}
2021-06-18 13:05:06 +02:00
export interface SerializedEditablePointLight
2021-06-29 15:08:02 +02:00
extends AbstractSerializedEditable<'pointLight'> {}
2021-06-18 13:05:06 +02:00
export interface SerializedEditablePerspectiveCamera
2021-06-29 15:08:02 +02:00
extends AbstractSerializedEditable<'perspectiveCamera'> {}
2021-06-18 13:05:06 +02:00
export interface SerializedEditableOrthographicCamera
2021-06-29 15:08:02 +02:00
extends AbstractSerializedEditable<'orthographicCamera'> {}
2021-06-18 13:05:06 +02:00
export type SerializedEditable =
| SerializedEditableGroup
| SerializedEditableMesh
| SerializedEditableSpotLight
| SerializedEditableDirectionalLight
| SerializedEditablePointLight
| SerializedEditablePerspectiveCamera
| SerializedEditableOrthographicCamera
export interface EditableState {
editables: Record<string, SerializedEditable>
}
export type EditorStore = {
sheet: ISheet | null
sheetObjects: {[uniqueName in string]?: BaseSheetObjectType}
scene: Scene | null
gl: WebGLRenderer | null
allowImplicitInstancing: boolean
helpersRoot: Group
editables: Record<string, Editable>
// this will come in handy when we start supporting multiple canvases
canvasName: string
sceneSnapshot: Scene | null
editablesSnapshot: Record<string, EditableSnapshot> | null
init: (
scene: Scene,
gl: WebGLRenderer,
allowImplicitInstancing: boolean,
sheet: ISheet,
) => void
addEditable: <T extends EditableType>(type: T, uniqueName: string) => void
removeEditable: (uniqueName: string) => void
createSnapshot: () => void
setSheetObject: (uniqueName: string, sheetObject: BaseSheetObjectType) => void
setSnapshotProxyObject: (
proxyObject: Object3D | null,
uniqueName: string,
) => void
}
const config: StateCreator<EditorStore> = (set, get) => {
return {
sheet: null,
editorObject: null,
2021-06-18 13:05:06 +02:00
sheetObjects: {},
scene: null,
gl: null,
allowImplicitInstancing: false,
helpersRoot: new Group(),
editables: {},
canvasName: 'default',
sceneSnapshot: null,
editablesSnapshot: null,
initialEditorCamera: {},
2021-08-08 22:25:48 +02:00
init: (scene, gl, allowImplicitInstancing, sheet) => {
2021-06-18 13:05:06 +02:00
set({
scene,
gl,
allowImplicitInstancing,
sheet,
})
},
addEditable: (type, uniqueName) =>
set((state) => {
if (state.editables[uniqueName]) {
if (
state.editables[uniqueName].type !== type &&
process.env.NODE_ENV === 'development'
) {
console.error(`Warning: There is a mismatch between the serialized type of ${uniqueName} and the one set when adding it to the scene.
Serialized: ${state.editables[uniqueName].type}.
Current: ${type}.
This might have happened either because you changed the type of an object, in which case a re-export will solve the issue, or because you re-used the uniqueName for an object of a different type, which is an error.`)
}
if (
state.editables[uniqueName].role === 'active' &&
!state.allowImplicitInstancing
) {
throw Error(
`Scene already has an editable object named ${uniqueName}.
If this is intentional, please set the allowImplicitInstancing prop of EditableManager to true.`,
)
} else {
}
}
return {
editables: {
...state.editables,
[uniqueName]: {
type: type as EditableType,
role: 'active',
},
},
}
}),
2021-06-18 13:05:06 +02:00
removeEditable: (name) =>
set((state) => {
const {[name]: removed, ...rest} = state.editables
return {
editables: {
...rest,
[name]: {...removed, role: 'removed'},
},
}
}),
setSheetObject: (uniqueName, sheetObject) => {
set((state) => ({
sheetObjects: {
...state.sheetObjects,
[uniqueName]: sheetObject,
},
}))
},
2021-07-03 13:20:40 +02:00
2021-06-18 13:05:06 +02:00
createSnapshot: () => {
set((state) => ({
sceneSnapshot: state.scene?.clone() ?? null,
editablesSnapshot: state.editables,
}))
},
setSnapshotProxyObject: (proxyObject, uniqueName) => {
set((state) => ({
editablesSnapshot: {
...state.editablesSnapshot,
[uniqueName]: {
...state.editablesSnapshot![uniqueName],
proxyObject,
},
},
}))
},
}
}
export const useEditorStore = create<EditorStore>(config)
export type BindFunction = (options: {
allowImplicitInstancing?: boolean
sheet: ISheet
2021-08-08 22:25:48 +02:00
gl: WebGLRenderer
scene: Scene
}) => void
2021-06-29 15:39:47 +02:00
export const bindToCanvas: BindFunction = ({
allowImplicitInstancing = false,
sheet,
2021-08-08 22:25:48 +02:00
gl,
scene,
2021-06-29 15:39:47 +02:00
}) => {
2021-08-08 22:25:48 +02:00
const init = useEditorStore.getState().init
init(scene, gl, allowImplicitInstancing, sheet)
2021-06-18 13:05:06 +02:00
}