- {showOverlayIcons && (
+ {(showOverlayIcons ||
+ (editable.objectConfig.dimensionless &&
+ selected !== uniqueName)) && (
- {icon}
+ {icons[editable.objectConfig.icon as IconID]}
)}
- {dimensionless.includes(editableType) && (
+ {editable.objectConfig.dimensionless && (
{
if (e.delta < 2) {
e.stopPropagation()
const theatreObject =
- useEditorStore.getState().sheetObjects[uniqueName]
+ useEditorStore.getState().editables[uniqueName].sheetObject
if (!theatreObject) {
console.log('no theatre object for', uniqueName)
diff --git a/packages/r3f/src/components/ProxyManager.tsx b/packages/r3f/src/components/ProxyManager.tsx
index d86ba75..578fcae 100644
--- a/packages/r3f/src/components/ProxyManager.tsx
+++ b/packages/r3f/src/components/ProxyManager.tsx
@@ -6,7 +6,8 @@ import React, {
useRef,
useState,
} from 'react'
-import {useEditorStore} from '../store'
+import type {Editable} from '../store';
+import { useEditorStore} from '../store'
import {createPortal} from '@react-three/fiber'
import EditableProxy from './EditableProxy'
import type {OrbitControls} from 'three-stdlib'
@@ -15,8 +16,6 @@ import shallow from 'zustand/shallow'
import type {Material, Mesh, Object3D} from 'three'
import {MeshBasicMaterial, MeshPhongMaterial} from 'three'
import studio from '@theatre/studio'
-import type {ISheetObject} from '@theatre/core'
-import type {$FixMe} from '../types'
import {useSelected} from './useSelected'
import {useVal} from '@theatre/react'
import useInvalidate from './useInvalidate'
@@ -26,17 +25,17 @@ export interface ProxyManagerProps {
orbitControlsRef: React.MutableRefObject
}
-type IEditableProxy = {
+type IEditableProxy = {
portal: ReturnType
object: Object3D
- sheetObject: ISheetObject<$FixMe>
+ editable: Editable
}
const ProxyManager: VFC = ({orbitControlsRef}) => {
const isBeingEdited = useRef(false)
const editorObject = getEditorSheetObject()
- const [sceneSnapshot, sheetObjects] = useEditorStore(
- (state) => [state.sceneSnapshot, state.sheetObjects],
+ const [sceneSnapshot, editables] = useEditorStore(
+ (state) => [state.sceneSnapshot, state.editables],
shallow,
)
const transformControlsMode =
@@ -51,7 +50,7 @@ const ProxyManager: VFC = ({orbitControlsRef}) => {
const sceneProxy = useMemo(() => sceneSnapshot?.clone(), [sceneSnapshot])
const [editableProxies, setEditableProxies] = useState<
{
- [name in string]?: IEditableProxy
+ [name in string]?: IEditableProxy
}
>({})
@@ -63,7 +62,7 @@ const ProxyManager: VFC = ({orbitControlsRef}) => {
return
}
- const editableProxies: {[name: string]: IEditableProxy} = {}
+ const editableProxies: {[name: string]: IEditableProxy} = {}
sceneProxy.traverse((object) => {
if (object.userData.__editable) {
@@ -77,13 +76,12 @@ const ProxyManager: VFC = ({orbitControlsRef}) => {
portal: createPortal(
,
object.parent!,
),
object: object,
- sheetObject: sheetObjects[uniqueName]!,
+ editable: editables[uniqueName]!,
}
}
}
@@ -94,25 +92,22 @@ const ProxyManager: VFC = ({orbitControlsRef}) => {
const selected = useSelected()
const editableProxyOfSelected = selected && editableProxies[selected]
+ const editable = selected ? editables[selected] : undefined
// subscribe to external changes
useEffect(() => {
- if (!editableProxyOfSelected) return
+ if (!editableProxyOfSelected || !editable) return
const object = editableProxyOfSelected.object
- const sheetObject = editableProxyOfSelected.sheetObject
+ const sheetObject = editableProxyOfSelected.editable.sheetObject
+ const objectConfig = editable.objectConfig
const setFromTheatre = (newValues: any) => {
- object.position.set(
- newValues.position.x,
- newValues.position.y,
- newValues.position.z,
- )
- object.rotation.set(
- newValues.rotation.x,
- newValues.rotation.y,
- newValues.rotation.z,
- )
- object.scale.set(newValues.scale.x, newValues.scale.y, newValues.scale.z)
+ // @ts-ignore
+ Object.entries(objectConfig.props).forEach(([key, value]) => {
+ // @ts-ignore
+ return value.apply(newValues[key], object)
+ })
+ objectConfig.updateObject?.(object)
invalidate()
}
@@ -229,39 +224,43 @@ const ProxyManager: VFC = ({orbitControlsRef}) => {
return (
<>
- {selected && editableProxyOfSelected && (
- {
- const sheetObject = editableProxyOfSelected.sheetObject
- const obj = editableProxyOfSelected.object
+ {selected &&
+ editableProxyOfSelected &&
+ editable &&
+ editable.objectConfig.useTransformControls && (
+ {
+ const sheetObject = editableProxyOfSelected.editable.sheetObject
+ const obj = editableProxyOfSelected.object
- scrub.capture(({set}) => {
- set(sheetObject.props, {
- position: {
- x: obj.position.x,
- y: obj.position.y,
- z: obj.position.z,
- },
- rotation: {
- x: obj.rotation.x,
- y: obj.rotation.y,
- z: obj.rotation.z,
- },
- scale: {
- x: obj.scale.x,
- y: obj.scale.y,
- z: obj.scale.z,
- },
+ scrub.capture(({set}) => {
+ set(sheetObject.props, {
+ ...sheetObject.value,
+ position: {
+ x: obj.position.x,
+ y: obj.position.y,
+ z: obj.position.z,
+ },
+ rotation: {
+ x: obj.rotation.x,
+ y: obj.rotation.y,
+ z: obj.rotation.z,
+ },
+ scale: {
+ x: obj.scale.x,
+ y: obj.scale.y,
+ z: obj.scale.z,
+ },
+ })
})
- })
- }}
- onDraggingChange={(event) => (isBeingEdited.current = event.value)}
- />
- )}
+ }}
+ onDraggingChange={(event) => (isBeingEdited.current = event.value)}
+ />
+ )}
{Object.values(editableProxies).map(
(editableProxy) => editableProxy!.portal,
)}
diff --git a/packages/r3f/src/components/editable.tsx b/packages/r3f/src/components/editable.tsx
index a25cb8d..7c9e4e9 100644
--- a/packages/r3f/src/components/editable.tsx
+++ b/packages/r3f/src/components/editable.tsx
@@ -1,222 +1,178 @@
import type {ComponentProps, ComponentType, RefAttributes} from 'react'
import React, {forwardRef, useLayoutEffect, useRef, useState} from 'react'
-import type {
- DirectionalLight,
- Group,
- Mesh,
- OrthographicCamera,
- PerspectiveCamera,
- PointLight,
- SpotLight,
-} from 'three'
-import {Vector3} from 'three'
-import type {EditableType} from '../store'
-import {allRegisteredObjects} from '../store'
-import {baseSheetObjectType} from '../store'
-import {useEditorStore} from '../store'
+import {allRegisteredObjects, useEditorStore} 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'
-interface Elements {
- group: Group
- mesh: Mesh
- spotLight: SpotLight
- directionalLight: DirectionalLight
- perspectiveCamera: PerspectiveCamera
- orthographicCamera: OrthographicCamera
- pointLight: PointLight
-}
-
-const editable = <
- T extends ComponentType | EditableType | 'primitive',
- U extends T extends EditableType ? T : EditableType,
->(
- Component: T,
- type: T extends 'primitive' ? null : U,
+const createEditable = (
+ config: EditableFactoryConfig,
) => {
- type Props = Omit, 'visible'> & {
- uniqueName: string
- visible?: boolean | 'editor'
- additionalProps?: $FixMe
- objRef?: $FixMe
- } & (T extends 'primitive'
- ? {
- editableType: U
- }
- : {}) &
- RefAttributes
+ const editable = <
+ T extends ComponentType | Keys | 'primitive',
+ U extends T extends Keys ? T : Keys,
+ >(
+ Component: T,
+ type: T extends 'primitive' ? null : U,
+ ) => {
+ type Props = Omit, 'visible'> & {
+ uniqueName: string
+ visible?: boolean | 'editor'
+ additionalProps?: $FixMe
+ objRef?: $FixMe
+ } & (T extends 'primitive'
+ ? {
+ editableType: U
+ }
+ : {}) &
+ RefAttributes
- return forwardRef(
- (
- {
- uniqueName,
- visible,
- editableType,
- additionalProps,
- objRef,
- ...props
- }: Props,
- ref,
- ) => {
- const objectRef = useRef()
+ return forwardRef(
+ (
+ {
+ uniqueName,
+ visible,
+ editableType,
+ additionalProps,
+ objRef,
+ ...props
+ }: Props,
+ ref,
+ ) => {
+ const actualType = type ?? editableType
- const sheet = useCurrentSheet()
+ const objectRef = useRef()
- const [sheetObject, setSheetObject] = useState<
- undefined | ISheetObject<$FixMe>
- >(undefined)
+ const sheet = useCurrentSheet()
- const invalidate = useInvalidate()
+ const [sheetObject, setSheetObject] = useState<
+ undefined | ISheetObject<$FixMe>
+ >(undefined)
- useLayoutEffect(() => {
- if (!sheet) return
- const sheetObject = sheet.object(uniqueName, {
- ...baseSheetObjectType,
- ...additionalProps,
- })
- allRegisteredObjects.add(sheetObject)
- setSheetObject(sheetObject)
+ const invalidate = useInvalidate()
- if (objRef) objRef!.current = sheetObject
+ useLayoutEffect(() => {
+ if (!sheet) return
+ const sheetObject = sheet.object(
+ uniqueName,
+ Object.assign(
+ {
+ ...additionalProps,
+ },
+ // @ts-ignore
+ ...Object.values(config[actualType].props).map(
+ // @ts-ignore
+ (value) => value.type,
+ ),
+ ),
+ )
+ allRegisteredObjects.add(sheetObject)
+ setSheetObject(sheetObject)
- useEditorStore
- .getState()
- .setSheetObject(uniqueName, sheetObject as $FixMe)
- }, [sheet, uniqueName])
+ if (objRef) objRef!.current = sheetObject
- const transformDeps: string[] = []
+ useEditorStore.getState().addEditable(uniqueName, {
+ type: actualType,
+ sheetObject,
+ visibleOnlyInEditor: visible === 'editor',
+ // @ts-ignore
+ objectConfig: config[actualType],
+ })
+ }, [sheet, uniqueName])
- ;['x', 'y', 'z'].forEach((axis) => {
- transformDeps.push(
- props[`position-${axis}` as any],
- props[`rotation-${axis}` as any],
- props[`scale-${axis}` as any],
+ // store initial values of props
+ useLayoutEffect(() => {
+ if (!sheetObject) return
+ sheetObject!.initialValue = Object.fromEntries(
+ // @ts-ignore
+ Object.entries(config[actualType].props).map(
+ // @ts-ignore
+ ([key, value]) => [key, value.parse(props)],
+ ),
+ )
+ }, [
+ uniqueName,
+ sheetObject,
+ // @ts-ignore
+ ...Object.keys(config[actualType].props).map(
+ // @ts-ignore
+ (key) => props[key],
+ ),
+ ])
+
+ // subscribe to prop changes from theatre
+ useLayoutEffect(() => {
+ if (!sheetObject) return
+
+ const object = objectRef.current!
+
+ const setFromTheatre = (newValues: any) => {
+ // @ts-ignore
+ Object.entries(config[actualType].props).forEach(
+ // @ts-ignore
+ ([key, value]) => value.apply(newValues[key], object),
+ )
+ // @ts-ignore
+ config[actualType].updateObject?.(object)
+ invalidate()
+ }
+
+ setFromTheatre(sheetObject.value)
+
+ const untap = sheetObject.onValuesChange(setFromTheatre)
+
+ return () => {
+ untap()
+ }
+ }, [sheetObject])
+
+ return (
+ // @ts-ignore
+
)
- })
+ },
+ )
+ }
- // store initial values of props
- useLayoutEffect(() => {
- if (!sheetObject) return
- // calculate initial properties before adding the editable
- const position: Vector3 = props.position
- ? Array.isArray(props.position)
- ? new Vector3(...(props.position as any))
- : props.position
- : new Vector3()
- const rotation: Vector3 = props.rotation
- ? Array.isArray(props.rotation)
- ? new Vector3(...(props.rotation as any))
- : props.rotation
- : new Vector3()
- const scale: Vector3 = props.scale
- ? Array.isArray(props.scale)
- ? new Vector3(...(props.scale as any))
- : props.scale
- : new Vector3(1, 1, 1)
-
- ;['x', 'y', 'z'].forEach((axis, index) => {
- if (props[`position-${axis}` as any])
- position.setComponent(index, props[`position-${axis}` as any])
- if (props[`rotation-${axis}` as any])
- rotation.setComponent(index, props[`rotation-${axis}` as any])
- if (props[`scale-${axis}` as any])
- scale.setComponent(index, props[`scale-${axis}` as any])
- })
-
- const initial = {
- position: {
- x: position.x,
- y: position.y,
- z: position.z,
- },
- rotation: {
- x: rotation.x,
- y: rotation.y,
- z: rotation.z,
- },
- scale: {
- x: scale.x,
- y: scale.y,
- z: scale.z,
- },
- }
- sheetObject!.initialValue = initial
- }, [
- uniqueName,
- sheetObject,
- props.position,
- props.rotation,
- props.scale,
-
- ...transformDeps,
- ])
-
- // subscribe to prop changes from theatre
- useLayoutEffect(() => {
- if (!sheetObject) return
-
- const object = objectRef.current!
-
- const setFromTheatre = (newValues: any) => {
- object.position.set(
- newValues.position.x,
- newValues.position.y,
- newValues.position.z,
- )
- object.rotation.set(
- newValues.rotation.x,
- newValues.rotation.y,
- newValues.rotation.z,
- )
- object.scale.set(
- newValues.scale.x,
- newValues.scale.y,
- newValues.scale.z,
- )
- invalidate()
- }
-
- setFromTheatre(sheetObject.value)
-
- const untap = sheetObject.onValuesChange(setFromTheatre)
-
- return () => {
- untap()
- }
- }, [sheetObject])
-
- return (
+ const extensions = {
+ ...Object.fromEntries(
+ Object.keys(config).map((key) => [
+ key,
// @ts-ignore
-
- )
- },
- )
+ editable(key, key),
+ ]),
+ ),
+ primitive: editable('primitive', null),
+ } as unknown as {
+ [Property in Keys]: React.ForwardRefExoticComponent<
+ React.PropsWithoutRef<
+ Omit & {
+ uniqueName: string
+ visible?: boolean | 'editor'
+ additionalProps?: $FixMe
+ objRef?: $FixMe
+ } & React.RefAttributes
+ >
+ >
+ }
+
+ return Object.assign(editable, extensions)
}
-const createEditable = (type: T) =>
- // @ts-ignore
- editable(type, type)
-
-editable.primitive = editable('primitive', null)
-editable.group = createEditable('group')
-editable.mesh = createEditable('mesh')
-editable.spotLight = createEditable('spotLight')
-editable.directionalLight = createEditable('directionalLight')
-editable.pointLight = createEditable('pointLight')
-editable.perspectiveCamera = createEditable('perspectiveCamera')
-editable.orthographicCamera = createEditable('orthographicCamera')
+const editable = createEditable(
+ defaultEditableFactoryConfig,
+)
export default editable
diff --git a/packages/r3f/src/defaultEditableFactoryConfig.ts b/packages/r3f/src/defaultEditableFactoryConfig.ts
new file mode 100644
index 0000000..cdabff9
--- /dev/null
+++ b/packages/r3f/src/defaultEditableFactoryConfig.ts
@@ -0,0 +1,109 @@
+import type {EditableFactoryConfig} from './editableFactoryConfigUtils'
+import {
+ createNumberPropConfig,
+ createVector,
+ createVectorPropConfig,
+ extendObjectProps,
+} from './editableFactoryConfigUtils'
+import type {
+ DirectionalLight,
+ Object3D,
+ OrthographicCamera,
+ PerspectiveCamera,
+ PointLight,
+ SpotLight,
+} from 'three'
+import {
+ BoxHelper,
+ CameraHelper,
+ DirectionalLightHelper,
+ PointLightHelper,
+ SpotLightHelper,
+} from 'three'
+
+const baseObjectConfig = {
+ props: {
+ position: createVectorPropConfig('position'),
+ rotation: createVectorPropConfig('rotation'),
+ scale: createVectorPropConfig('scale', createVector([1, 1, 1])),
+ },
+ useTransformControls: true,
+ icon: 'cube' as const,
+ createHelper: (object: Object3D) => new BoxHelper(object, selectionColor),
+}
+
+const baseLightConfig = {
+ ...extendObjectProps(baseObjectConfig, {
+ intensity: createNumberPropConfig('intensity', 1),
+ distance: createNumberPropConfig('distance'),
+ decay: createNumberPropConfig('decay'),
+ }),
+ dimensionless: true,
+}
+
+const baseCameraConfig = {
+ ...extendObjectProps(baseObjectConfig, {
+ near: createNumberPropConfig('near', 0.1, {nudgeMultiplier: 0.1}),
+ far: createNumberPropConfig('far', 2000, {nudgeMultiplier: 0.1}),
+ }),
+ updateObject: (camera: PerspectiveCamera | OrthographicCamera) => {
+ camera.updateProjectionMatrix()
+ },
+ icon: 'camera' as const,
+ dimensionless: true,
+ createHelper: (camera: PerspectiveCamera) => new CameraHelper(camera),
+}
+
+const selectionColor = '#40AAA4'
+
+const defaultEditableFactoryConfig = {
+ group: {
+ ...baseObjectConfig,
+ icon: 'collection' as const,
+ createHelper: (object: Object3D) => new BoxHelper(object, selectionColor),
+ },
+ mesh: {
+ ...baseObjectConfig,
+ icon: 'cube' as const,
+ createHelper: (object: Object3D) => new BoxHelper(object, selectionColor),
+ },
+ spotLight: {
+ ...extendObjectProps(baseLightConfig, {
+ angle: createNumberPropConfig('angle', 0, {nudgeMultiplier: 0.001}),
+ penumbra: createNumberPropConfig('penumbra', 0, {nudgeMultiplier: 0.001}),
+ }),
+ icon: 'spotLight' as const,
+ createHelper: (light: SpotLight) =>
+ new SpotLightHelper(light, selectionColor),
+ },
+ directionalLight: {
+ ...extendObjectProps(baseObjectConfig, {
+ intensity: createNumberPropConfig('intensity', 1),
+ }),
+ icon: 'sun' as const,
+ dimensionless: true,
+ createHelper: (light: DirectionalLight) =>
+ new DirectionalLightHelper(light, 1, selectionColor),
+ },
+ pointLight: {
+ ...baseLightConfig,
+ icon: 'lightBulb' as const,
+ createHelper: (light: PointLight) =>
+ new PointLightHelper(light, 1, selectionColor),
+ },
+ perspectiveCamera: extendObjectProps(baseCameraConfig, {
+ fov: createNumberPropConfig('fov', 50, {nudgeMultiplier: 0.1}),
+ zoom: createNumberPropConfig('zoom', 1),
+ }),
+ orthographicCamera: baseCameraConfig,
+ points: baseObjectConfig,
+ line: baseObjectConfig,
+ lineLoop: baseObjectConfig,
+ lineSegments: baseObjectConfig,
+}
+
+// Assert that the config is indeed of EditableFactoryConfig without actually
+// forcing it to that type so that we can pass the real type to the editable factory
+defaultEditableFactoryConfig as EditableFactoryConfig
+
+export default defaultEditableFactoryConfig
diff --git a/packages/r3f/src/editableFactoryConfigUtils.ts b/packages/r3f/src/editableFactoryConfigUtils.ts
new file mode 100644
index 0000000..9a0a2b3
--- /dev/null
+++ b/packages/r3f/src/editableFactoryConfigUtils.ts
@@ -0,0 +1,98 @@
+import type {IShorthandCompoundProps} from '@theatre/core'
+import {types} from '@theatre/core'
+import type {Object3D} from 'three'
+import type {IconID} from './icons'
+
+export type Helper = Object3D & {
+ update?: () => void
+}
+type PropConfig = {
+ parse: (props: Record) => T
+ apply: (value: T, object: any) => void
+ type: IShorthandCompoundProps
+}
+type Props = Record>
+type Meta = {
+ useTransformControls: boolean
+ updateObject?: (object: T) => void
+ icon: IconID
+ dimensionless?: boolean
+ createHelper: (object: T) => Helper
+}
+export type ObjectConfig = {props: Props} & Meta
+export type EditableFactoryConfig = Partial<
+ Record>
+>
+
+type Vector3 = {
+ x: number
+ y: number
+ z: number
+}
+
+export const createVector = (components?: [number, number, number]) => {
+ return components
+ ? {x: components[0], y: components[1], z: components[2]}
+ : {
+ x: 0,
+ y: 0,
+ z: 0,
+ }
+}
+
+export const createVectorPropConfig = (
+ key: string,
+ defaultValue = createVector(),
+ {nudgeMultiplier = 0.01} = {},
+): PropConfig => ({
+ parse: (props) => {
+ const vector = props[key]
+ ? Array.isArray(props[key])
+ ? createVector(props[key] as any)
+ : {
+ x: props[key].x,
+ y: props[key].y,
+ z: props[key].z,
+ }
+ : defaultValue
+ ;(['x', 'y', 'z'] as const).forEach((axis) => {
+ if (props[`${key}-${axis}` as any])
+ vector[axis] = props[`${key}-${axis}` as any]
+ })
+ return vector
+ },
+ apply: (value, object) => {
+ object[key].set(value.x, value.y, value.z)
+ },
+ type: {
+ [key]: {
+ x: types.number(defaultValue.x, {nudgeMultiplier}),
+ y: types.number(defaultValue.y, {nudgeMultiplier}),
+ z: types.number(defaultValue.z, {nudgeMultiplier}),
+ },
+ },
+})
+
+export const createNumberPropConfig = (
+ key: string,
+ defaultValue: number = 0,
+ {nudgeMultiplier = 0.01} = {},
+): PropConfig => ({
+ parse: (props) => {
+ return props[key] ?? defaultValue ?? 0
+ },
+ apply: (value, object) => {
+ object[key] = value
+ },
+ type: {
+ [key]: types.number(defaultValue, {nudgeMultiplier}),
+ },
+})
+
+export const extendObjectProps = (
+ objectConfig: T,
+ extension: Props,
+) => ({
+ ...objectConfig,
+ props: {...objectConfig.props, ...extension},
+})
diff --git a/packages/r3f/src/icons.tsx b/packages/r3f/src/icons.tsx
new file mode 100644
index 0000000..1a54e96
--- /dev/null
+++ b/packages/r3f/src/icons.tsx
@@ -0,0 +1,17 @@
+import {BsCameraVideoFill, BsFillCollectionFill} from 'react-icons/bs'
+import {GiCube, GiLightBulb, GiLightProjector} from 'react-icons/gi'
+import {BiSun} from 'react-icons/bi'
+import React from 'react'
+
+const icons = {
+ collection: ,
+ cube: ,
+ lightBulb: ,
+ spotLight: ,
+ sun: ,
+ camera: ,
+}
+
+export type IconID = keyof typeof icons
+
+export default icons
diff --git a/packages/r3f/src/store.ts b/packages/r3f/src/store.ts
index 0de458b..ca97b09 100644
--- a/packages/r3f/src/store.ts
+++ b/packages/r3f/src/store.ts
@@ -3,172 +3,61 @@ import create from 'zustand'
import type {Object3D, Scene, WebGLRenderer} from 'three'
import {Group} from 'three'
import type {ISheetObject} from '@theatre/core'
-import {types} from '@theatre/core'
-
-export type EditableType =
- | 'group'
- | 'mesh'
- | 'spotLight'
- | 'directionalLight'
- | 'pointLight'
- | 'perspectiveCamera'
- | 'orthographicCamera'
+import type {ObjectConfig} from './editableFactoryConfigUtils'
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})
-
-export const baseSheetObjectType = {
- position: {
- x: positionComp,
- y: positionComp,
- z: positionComp,
- },
- rotation: {
- x: rotationComp,
- y: rotationComp,
- z: rotationComp,
- },
- scale: {
- x: scaleComp,
- y: scaleComp,
- z: scaleComp,
- },
-}
-
-export type BaseSheetObjectType = ISheetObject
+export type BaseSheetObjectType = ISheetObject
export const allRegisteredObjects = new WeakSet()
-export interface AbstractEditable {
- type: T
- role: 'active' | 'removed'
- sheetObject?: ISheetObject
+export interface Editable {
+ type: string
+ sheetObject: ISheetObject
+ objectConfig: ObjectConfig
+ visibleOnlyInEditor: boolean
}
-// 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 = {
+export type EditableSnapshot = Editable> = {
proxyObject?: Object3D | null
} & T
-export interface AbstractSerializedEditable {
- type: T
+export interface SerializedEditable {
+ type: string
}
-export interface SerializedEditableGroup
- extends AbstractSerializedEditable<'group'> {}
-
-export interface SerializedEditableMesh
- extends AbstractSerializedEditable<'mesh'> {}
-
-export interface SerializedEditableSpotLight
- extends AbstractSerializedEditable<'spotLight'> {}
-
-export interface SerializedEditableDirectionalLight
- extends AbstractSerializedEditable<'directionalLight'> {}
-
-export interface SerializedEditablePointLight
- extends AbstractSerializedEditable<'pointLight'> {}
-
-export interface SerializedEditablePerspectiveCamera
- extends AbstractSerializedEditable<'perspectiveCamera'> {}
-
-export interface SerializedEditableOrthographicCamera
- extends AbstractSerializedEditable<'orthographicCamera'> {}
-
-export type SerializedEditable =
- | SerializedEditableGroup
- | SerializedEditableMesh
- | SerializedEditableSpotLight
- | SerializedEditableDirectionalLight
- | SerializedEditablePointLight
- | SerializedEditablePerspectiveCamera
- | SerializedEditableOrthographicCamera
-
export interface EditableState {
editables: Record
}
export type EditorStore = {
- sheetObjects: {[uniqueName in string]?: BaseSheetObjectType}
scene: Scene | null
gl: WebGLRenderer | null
- allowImplicitInstancing: boolean
helpersRoot: Group
- editables: Record
+ editables: Record>
// this will come in handy when we start supporting multiple canvases
canvasName: string
sceneSnapshot: Scene | null
editablesSnapshot: Record | null
- init: (
- scene: Scene,
- gl: WebGLRenderer,
- allowImplicitInstancing: boolean,
- ) => void
+ init: (scene: Scene, gl: WebGLRenderer) => void
- addEditable: (type: T, uniqueName: string) => void
- removeEditable: (uniqueName: string) => void
+ addEditable: (uniqueName: string, editable: Editable) => void
createSnapshot: () => void
- setSheetObject: (uniqueName: string, sheetObject: BaseSheetObjectType) => void
setSnapshotProxyObject: (
proxyObject: Object3D | null,
uniqueName: string,
) => void
}
-const config: StateCreator = (set, get) => {
+const config: StateCreator = (set) => {
return {
sheet: null,
editorObject: null,
- sheetObjects: {},
scene: null,
gl: null,
- allowImplicitInstancing: false,
helpersRoot: new Group(),
editables: {},
canvasName: 'default',
@@ -176,65 +65,18 @@ const config: StateCreator = (set, get) => {
editablesSnapshot: null,
initialEditorCamera: {},
- init: (scene, gl, allowImplicitInstancing) => {
+ init: (scene, gl) => {
set({
scene,
gl,
- allowImplicitInstancing,
})
},
- 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',
- },
- },
- }
- }),
-
- removeEditable: (name) =>
- set((state) => {
- const {[name]: removed, ...rest} = state.editables
- return {
- editables: {
- ...rest,
- [name]: {...removed, role: 'removed'},
- },
- }
- }),
- setSheetObject: (uniqueName, sheetObject) => {
+ addEditable: (uniqueName, editable) => {
set((state) => ({
- sheetObjects: {
- ...state.sheetObjects,
- [uniqueName]: sheetObject,
+ editables: {
+ ...state.editables,
+ [uniqueName]: editable,
},
}))
},
@@ -245,6 +87,7 @@ const config: StateCreator = (set, get) => {
editablesSnapshot: state.editables,
}))
},
+
setSnapshotProxyObject: (proxyObject, uniqueName) => {
set((state) => ({
editablesSnapshot: {
@@ -267,11 +110,7 @@ export type BindFunction = (options: {
scene: Scene
}) => void
-export const bindToCanvas: BindFunction = ({
- allowImplicitInstancing = false,
- gl,
- scene,
-}) => {
+export const bindToCanvas: BindFunction = ({gl, scene}) => {
const init = useEditorStore.getState().init
- init(scene, gl, allowImplicitInstancing)
+ init(scene, gl)
}
diff --git a/theatre/core/src/index.ts b/theatre/core/src/index.ts
index 2faf3ca..5b36ad2 100644
--- a/theatre/core/src/index.ts
+++ b/theatre/core/src/index.ts
@@ -12,6 +12,7 @@ export type {
export type {ISequence} from '@theatre/core/sequences/TheatreSequence'
export type {ISheetObject} from '@theatre/core/sheetObjects/TheatreSheetObject'
export type {ISheet} from '@theatre/core/sheets/TheatreSheet'
+export type {IShorthandCompoundProps} from '@theatre/core/propTypes'
import * as globalVariableNames from '@theatre/shared/globalVariableNames'
import type StudioBundle from '@theatre/studio/StudioBundle'
import CoreBundle from './CoreBundle'
diff --git a/theatre/core/src/propTypes/index.ts b/theatre/core/src/propTypes/index.ts
index 80b5a8b..b3a1cdf 100644
--- a/theatre/core/src/propTypes/index.ts
+++ b/theatre/core/src/propTypes/index.ts
@@ -726,3 +726,5 @@ export type PropTypeConfig =
| PropTypeConfig_AllSimples
| PropTypeConfig_Compound<$IntentionalAny>
| PropTypeConfig_Enum
+
+export type {IShorthandCompoundProps}