From c20d1fdb9d83f09af3faaf4a3251a431e2301590 Mon Sep 17 00:00:00 2001 From: Josh Field Date: Tue, 31 Oct 2023 21:07:33 +1100 Subject: [PATCH] Remove studio obj3d type (#9179) * mvp for removing objs * license * i18n --- packages/client-core/i18n/en/editor.json | 6 +- .../components/InstanceChat/index.module.scss | 3 +- .../UserMediaWindow/index.module.scss | 3 +- packages/client/src/pages/styles.scss | 3 +- .../editor/src/classes/EditorCameraState.ts | 4 +- .../editor/src/components/EditorContainer.tsx | 6 + .../hierarchy/HeirarchyTreeWalker.ts | 23 +- .../hierarchy/HierarchyPanelContainer.tsx | 189 ++----- .../hierarchy/HierarchyTreeNode.tsx | 65 +-- .../src/components/inputs/MaterialInput.tsx | 13 +- .../components/inputs/SceneObjectInput.tsx | 70 --- .../components/materials/MaterialEditor.tsx | 82 ++- .../materials/MaterialLibraryPanel.tsx | 29 +- .../materials/MaterialLibraryState.ts | 33 ++ .../components/properties/CoreNodeEditor.tsx | 5 +- .../components/properties/NameInputGroup.tsx | 4 +- .../src/components/properties/NodeEditor.tsx | 5 +- .../properties/Object3DNodeEditor.tsx | 392 -------------- .../properties/PropertiesPanelContainer.tsx | 105 ++-- .../properties/TransformPropertyGroup.tsx | 12 +- .../editor/src/components/properties/Util.ts | 19 +- .../properties/three.quarks/BehaviorInput.tsx | 6 +- .../src/components/toolbar/styles.module.scss | 3 +- .../src/functions/EditorControlFunctions.ts | 482 +++++++----------- .../src/functions/cancelGrabOrPlacement.ts | 3 +- .../src/functions/filterParentEntities.ts | 6 +- .../src/functions/getDetachedObjectsRoots.ts | 58 +-- .../src/functions/getIntersectingNode.ts | 7 +- .../editor/src/functions/traverseEarlyOut.ts | 8 +- .../editor/src/services/EditorServices.ts | 1 - .../editor/src/services/SelectionServices.ts | 12 +- .../src/systems/EditorControlSystem.tsx | 56 +- .../engine/src/assets/classes/XRELoader.ts | 3 +- .../src/ecs/functions/EntityTree.test.ts | 48 +- .../engine/src/ecs/functions/EntityTree.ts | 27 +- 35 files changed, 472 insertions(+), 1319 deletions(-) delete mode 100644 packages/editor/src/components/inputs/SceneObjectInput.tsx create mode 100644 packages/editor/src/components/materials/MaterialLibraryState.ts delete mode 100755 packages/editor/src/components/properties/Object3DNodeEditor.tsx diff --git a/packages/client-core/i18n/en/editor.json b/packages/client-core/i18n/en/editor.json index 6cbde8f2be..d722fc8b15 100755 --- a/packages/client-core/i18n/en/editor.json +++ b/packages/client-core/i18n/en/editor.json @@ -181,9 +181,13 @@ "info": "Takes a screenshot of your scene at the current view." } }, + "materialProperties": { + "title": "Material", + "info": "Access and edit detailed information about materials in the scene." + }, "properties": { "title": "Properties", - "info": "Propeties let you access and edit detailed information about objects in your scene.", + "info": "Propeties let you access and edit detailed information about objects in the scene.", "lbl-visible": "Visible", "lbl-preventBake": "Prevent Bake", "lbl-dynamicLoad": "Load Children Dynamically", diff --git a/packages/client-core/src/components/InstanceChat/index.module.scss b/packages/client-core/src/components/InstanceChat/index.module.scss index 82fa9b1c43..9db3f0d49e 100755 --- a/packages/client-core/src/components/InstanceChat/index.module.scss +++ b/packages/client-core/src/components/InstanceChat/index.module.scss @@ -218,8 +218,7 @@ flex-direction: column; padding: 0 10px; margin-bottom: 5px; - overflow-y: auto; - overflow-x: clip; + overflow: clip auto; @media (max-width: 1024px) { margin: 15px 15px 20px; diff --git a/packages/client-core/src/components/UserMediaWindow/index.module.scss b/packages/client-core/src/components/UserMediaWindow/index.module.scss index 693fdbec4c..58e4e2543e 100755 --- a/packages/client-core/src/components/UserMediaWindow/index.module.scss +++ b/packages/client-core/src/components/UserMediaWindow/index.module.scss @@ -190,8 +190,7 @@ padding: 8px; border-radius: 50px; display: flex; - justify-content: center; - align-content: center; + place-content: center center; width: 30px; } } diff --git a/packages/client/src/pages/styles.scss b/packages/client/src/pages/styles.scss index 63c825bc21..9f87c4564c 100755 --- a/packages/client/src/pages/styles.scss +++ b/packages/client/src/pages/styles.scss @@ -275,8 +275,7 @@ body { overflow: hidden; color: var(--textColor); - overscroll-behavior-y: contain; - overscroll-behavior-x: none; + overscroll-behavior: none contain; background-color: var(--mainBackground); } diff --git a/packages/editor/src/classes/EditorCameraState.ts b/packages/editor/src/classes/EditorCameraState.ts index ab132ba624..124632b199 100644 --- a/packages/editor/src/classes/EditorCameraState.ts +++ b/packages/editor/src/classes/EditorCameraState.ts @@ -25,14 +25,14 @@ Ethereal Engine. All Rights Reserved. import { Vector3 } from 'three' -import { EntityOrObjectUUID } from '@etherealengine/engine/src/ecs/functions/EntityTree' +import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' import { defineState } from '@etherealengine/hyperflux' export const EditorCameraState = defineState({ name: 'EditorCameraState', initial: { zoomDelta: 0, - focusedObjects: [] as EntityOrObjectUUID[], + focusedObjects: [] as Entity[], isPanning: false, cursorDeltaX: 0, cursorDeltaY: 0, diff --git a/packages/editor/src/components/EditorContainer.tsx b/packages/editor/src/components/EditorContainer.tsx index bc1ae914bc..4008642abe 100755 --- a/packages/editor/src/components/EditorContainer.tsx +++ b/packages/editor/src/components/EditorContainer.tsx @@ -79,6 +79,7 @@ import { GraphPanelTitle } from './graph/GraphPanelTitle' import HierarchyPanelContainer from './hierarchy/HierarchyPanelContainer' import { HierarchyPanelTitle } from './hierarchy/HierarchyPanelTitle' import { PanelDragContainer, PanelIcon, PanelTitle } from './layout/Panel' +import MaterialProperties, { MaterialPropertyTitle } from './materials/MaterialEditor' import MaterialLibraryPanel from './materials/MaterialLibraryPanel' import { MaterialLibraryPanelTitle } from './materials/MaterialLibraryPanelTitle' import PropertiesPanelContainer from './properties/PropertiesPanelContainer' @@ -491,6 +492,11 @@ const defaultLayout: LayoutData = { id: 'graphPanel', title: , content: + }, + { + id: 'graphPanel', + title: , + content: } ] } diff --git a/packages/editor/src/components/hierarchy/HeirarchyTreeWalker.ts b/packages/editor/src/components/hierarchy/HeirarchyTreeWalker.ts index 21d9240448..8f3624b53d 100644 --- a/packages/editor/src/components/hierarchy/HeirarchyTreeWalker.ts +++ b/packages/editor/src/components/hierarchy/HeirarchyTreeWalker.ts @@ -23,22 +23,15 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ -import { Object3D } from 'three' - import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' import { getComponent, hasComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' -import { EntityOrObjectUUID, EntityTreeComponent } from '@etherealengine/engine/src/ecs/functions/EntityTree' +import { EntityTreeComponent } from '@etherealengine/engine/src/ecs/functions/EntityTree' export type HeirarchyTreeNodeType = { depth: number - entityNode: EntityOrObjectUUID + entity: Entity childIndex: number lastChild: boolean - /** - * @param obj3d is used for exploding models, it will eventually be replaced when - * the scene graph is implemented on the ECS instead of threejs - */ - obj3d?: Object3D isLeaf?: boolean isCollapsed?: boolean selected?: boolean @@ -53,18 +46,18 @@ export type HeirarchyTreeCollapsedNodeType = { [key: number]: boolean } * @param {entityNode} collapsedNodes */ export function* heirarchyTreeWalker( - treeNode: EntityOrObjectUUID, - selectedEntities: (Entity | string)[], + treeNode: Entity, + selectedEntities: Entity[], collapsedNodes: HeirarchyTreeCollapsedNodeType ): Generator { if (!treeNode) return const stack = [] as HeirarchyTreeNodeType[] - stack.push({ depth: 0, entityNode: treeNode, childIndex: 0, lastChild: true }) + stack.push({ depth: 0, entity: treeNode, childIndex: 0, lastChild: true }) while (stack.length !== 0) { - const { depth, entityNode, childIndex, lastChild } = stack.pop() as HeirarchyTreeNodeType + const { depth, entity: entityNode, childIndex, lastChild } = stack.pop() as HeirarchyTreeNodeType const isCollapsed = collapsedNodes[entityNode] const entityTreeComponent = getComponent(entityNode as Entity, EntityTreeComponent) @@ -73,7 +66,7 @@ export function* heirarchyTreeWalker( isLeaf: entityTreeComponent.children.length === 0, isCollapsed, depth, - entityNode, + entity: entityNode, selected: selectedEntities.includes(entityNode), active: selectedEntities.length > 0 && entityNode === selectedEntities[selectedEntities.length - 1], childIndex, @@ -87,7 +80,7 @@ export function* heirarchyTreeWalker( if (node) { stack.push({ depth: depth + 1, - entityNode: entityTreeComponent.children[i], + entity: entityTreeComponent.children[i], childIndex: i, lastChild: i === 0 }) diff --git a/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx b/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx index 101697e85c..872a5b9f6d 100644 --- a/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx +++ b/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx @@ -29,27 +29,16 @@ import Hotkeys from 'react-hot-keys' import { useTranslation } from 'react-i18next' import AutoSizer from 'react-virtualized-auto-sizer' import { FixedSizeList, areEqual } from 'react-window' -import { Object3D } from 'three' import { AllFileTypes } from '@etherealengine/engine/src/assets/constants/fileTypes' import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' import { SceneState } from '@etherealengine/engine/src/ecs/classes/Scene' -import { - getComponent, - getOptionalComponent, - hasComponent -} from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' -import { - EntityTreeComponent, - getEntityNodeArrayFromEntities, - traverseEntityNode -} from '@etherealengine/engine/src/ecs/functions/EntityTree' +import { getComponent, getOptionalComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' +import { EntityTreeComponent, traverseEntityNode } from '@etherealengine/engine/src/ecs/functions/EntityTree' import { GroupComponent } from '@etherealengine/engine/src/scene/components/GroupComponent' -import { ModelComponent } from '@etherealengine/engine/src/scene/components/ModelComponent' import { NameComponent } from '@etherealengine/engine/src/scene/components/NameComponent' import { getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' -import { Checkbox } from '@mui/material' import MenuItem from '@mui/material/MenuItem' import { PopoverPosition } from '@mui/material/Popover' @@ -69,13 +58,7 @@ import { PropertiesPanelButton } from '../inputs/Button' import { ContextMenu } from '../layout/ContextMenu' import { updateProperties } from '../properties/Util' import { HeirarchyTreeCollapsedNodeType, HeirarchyTreeNodeType, heirarchyTreeWalker } from './HeirarchyTreeWalker' -import { - HierarchyTreeNode, - HierarchyTreeNodeData, - HierarchyTreeNodeProps, - RenameNodeData, - getNodeElId -} from './HierarchyTreeNode' +import { HierarchyTreeNode, HierarchyTreeNodeProps, RenameNodeData, getNodeElId } from './HierarchyTreeNode' import styles from './styles.module.scss' /** @@ -88,62 +71,6 @@ const uploadOptions = { accepts: AllFileTypes } -/** - * getNodeKey function used to get object id at given index. - * - * @param {number} index [index of the node to get object id] - * @param {object} data - * @return {string} - */ -function getNodeKey(index: number, data: HierarchyTreeNodeData) { - return index //data.nodes[index].entityNode ? data.nodes[index].entityNode.entity : data.nodes[index].toString() -} - -function traverseWithDepth(obj3d: Object3D, depth: number, cb: (obj: Object3D, depth: number) => void) { - cb(obj3d, depth) - for (const obj of obj3d.children) { - traverseWithDepth(obj, depth + 1, cb) - } -} - -function getModelNodesFromTreeWalker( - inputNodes: HeirarchyTreeNodeType[], - collapsedNodes: HeirarchyTreeCollapsedNodeType, - showObject3Ds: boolean -): HeirarchyTreeNodeType[] { - const outputNodes = [] as HeirarchyTreeNodeType[] - const selected = new Set( - getState(SelectionState).selectedEntities.filter((ent) => typeof ent === 'string') as string[] - ) - for (const node of inputNodes) { - outputNodes.push(node) - const isCollapsed = collapsedNodes[node.entityNode] - if (showObject3Ds && hasComponent(node.entityNode as Entity, ModelComponent)) { - const group = getOptionalComponent(node.entityNode as Entity, GroupComponent) as Object3D[] - if (!group?.length) continue - node.isLeaf = false - if (isCollapsed) continue - let childIndex = node.childIndex - for (const obj3d of group) - traverseWithDepth(obj3d, node.depth, (obj, depth) => { - if (group.includes(obj)) return - outputNodes.push({ - depth, - obj3d: obj, - entityNode: null!, - childIndex: childIndex++, - lastChild: false, - isLeaf: true, //!obj.children.length, // todo, store collapsed state on obj3d - isCollapsed: node.isCollapsed, - selected: selected.has(obj.uuid), - active: false - }) - }) - } - } - return outputNodes -} - /** * HierarchyPanel function component provides view for hierarchy tree. * @@ -155,7 +82,6 @@ export default function HierarchyPanel() { const [anchorPosition, setAnchorPosition] = React.useState(undefined) const [anchorEl, setAnchorEl] = React.useState(null) const [prevClickedNode, setPrevClickedNode] = useState(null) - const open = Boolean(anchorEl) const onUpload = useUpload(uploadOptions) const selectionState = useHookstate(getMutableState(SelectionState)) const [renamingNode, setRenamingNode] = useState(null) @@ -163,16 +89,15 @@ export default function HierarchyPanel() { const [nodes, setNodes] = useState([]) const nodeSearch: HeirarchyTreeNodeType[] = [] const [selectedNode, _setSelectedNode] = useState(null) - const editorState = useHookstate(getMutableState(EditorState)) + const lockPropertiesPanel = useHookstate(getMutableState(EditorState).lockPropertiesPanel) const [searchHierarchy, setSearchHierarchy] = useState('') - const showObject3DInHierarchy = editorState.showObject3DInHierarchy const activeScene = useHookstate(getMutableState(SceneState).activeScene) const entities = useHookstate(UUIDComponent.entitiesByUUIDState) const MemoTreeNode = memo( (props: HierarchyTreeNodeProps) => ( - + ), areEqual ) @@ -180,11 +105,7 @@ export default function HierarchyPanel() { if (searchHierarchy.length > 0) { const condition = new RegExp(searchHierarchy.toLowerCase()) nodes.forEach((node) => { - if ( - (node.entityNode && - condition.test(getComponent(node.entityNode as Entity, NameComponent)?.toLowerCase() ?? '')) || - (node.obj3d && condition.test(node.obj3d.name?.toLowerCase() ?? '')) - ) + if (node.entity && condition.test(getComponent(node.entity as Entity, NameComponent)?.toLowerCase() ?? '')) nodeSearch.push(node) }) } @@ -192,35 +113,29 @@ export default function HierarchyPanel() { useEffect(() => { if (!activeScene.value) return setNodes( - getModelNodesFromTreeWalker( - Array.from( - heirarchyTreeWalker( - SceneState.getRootEntity(getState(SceneState).activeScene!), - selectionState.selectedEntities.value, - collapsedNodes - ) - ), - collapsedNodes, - showObject3DInHierarchy.value + Array.from( + heirarchyTreeWalker( + SceneState.getRootEntity(getState(SceneState).activeScene!), + selectionState.selectedEntities.value, + collapsedNodes + ) ) ) - }, [collapsedNodes, activeScene, showObject3DInHierarchy, selectionState.selectedEntities, entities]) + }, [collapsedNodes, activeScene, selectionState.selectedEntities, entities]) - const setSelectedNode = (selection) => !editorState.lockPropertiesPanel.value && _setSelectedNode(selection) + const setSelectedNode = (selection) => !lockPropertiesPanel.value && _setSelectedNode(selection) /* Expand & Collapse Functions */ const expandNode = useCallback( (node: HeirarchyTreeNodeType) => { - if (node.obj3d) return // todo - setCollapsedNodes({ ...collapsedNodes, [node.entityNode]: false }) + setCollapsedNodes({ ...collapsedNodes, [node.entity]: false }) }, [collapsedNodes] ) const collapseNode = useCallback( (node: HeirarchyTreeNodeType) => { - if (node.obj3d) return // todo - setCollapsedNodes({ ...collapsedNodes, [node.entityNode]: true }) + setCollapsedNodes({ ...collapsedNodes, [node.entity]: true }) }, [collapsedNodes] ) @@ -228,9 +143,7 @@ export default function HierarchyPanel() { const expandChildren = useCallback( (node: HeirarchyTreeNodeType) => { handleClose() - - if (node.obj3d) return // todo - traverseEntityNode(node.entityNode as Entity, (child) => (collapsedNodes[child] = false)) + traverseEntityNode(node.entity as Entity, (child) => (collapsedNodes[child] = false)) setCollapsedNodes({ ...collapsedNodes }) }, [collapsedNodes] @@ -239,9 +152,7 @@ export default function HierarchyPanel() { const collapseChildren = useCallback( (node: HeirarchyTreeNodeType) => { handleClose() - - if (node.obj3d) return // todo - traverseEntityNode(node.entityNode as Entity, (child) => (collapsedNodes[child] = true)) + traverseEntityNode(node.entity as Entity, (child) => (collapsedNodes[child] = true)) setCollapsedNodes({ ...collapsedNodes }) }, [collapsedNodes] @@ -252,17 +163,17 @@ export default function HierarchyPanel() { (e: MouseEvent, node: HeirarchyTreeNodeType) => { if (e.detail === 1) { if (e.ctrlKey) { - EditorControlFunctions.toggleSelection([node.entityNode ?? node.obj3d!.uuid]) + EditorControlFunctions.toggleSelection([node.entity]) setSelectedNode(null) } else if (e.shiftKey && prevClickedNode) { - const startIndex = nodes.findIndex((n) => n.entityNode === prevClickedNode.entityNode) - const endIndex = nodes.findIndex((n) => n.entityNode === node.entityNode) + const startIndex = nodes.findIndex((n) => n.entity === prevClickedNode.entity) + const endIndex = nodes.findIndex((n) => n.entity === node.entity) const range = nodes.slice(Math.min(startIndex, endIndex), Math.max(startIndex, endIndex) + 1) - const entityUuids = range.filter((n) => n.entityNode).map((n) => n.entityNode!) + const entityUuids = range.filter((n) => n.entity).map((n) => n.entity!) EditorControlFunctions.replaceSelection(entityUuids) setSelectedNode(node) } else if (!node.selected) { - EditorControlFunctions.replaceSelection([node.entityNode ?? node.obj3d!.uuid]) + EditorControlFunctions.replaceSelection([node.entity]) setSelectedNode(node) } setPrevClickedNode(node) @@ -290,18 +201,16 @@ export default function HierarchyPanel() { } const onClick = useCallback((e: MouseEvent, node: HeirarchyTreeNodeType) => { - if (node.obj3d) return // todo if (e.detail === 2) { const editorCameraState = getMutableState(EditorCameraState) - editorCameraState.focusedObjects.set([node.entityNode]) + editorCameraState.focusedObjects.set([node.entity]) editorCameraState.refocus.set(true) } }, []) const onToggle = useCallback( (_, node: HeirarchyTreeNodeType) => { - if (node.obj3d) return // todo - if (collapsedNodes[node.entityNode as Entity]) expandNode(node) + if (collapsedNodes[node.entity as Entity]) expandNode(node) else collapseNode(node) }, [collapsedNodes, expandNode, collapseNode] @@ -310,7 +219,7 @@ export default function HierarchyPanel() { const onKeyDown = useCallback( (e: KeyboardEvent, node: HeirarchyTreeNodeType) => { const nodeIndex = nodes.indexOf(node) - const entityTree = getComponent(node.entityNode as Entity, EntityTreeComponent) + const entityTree = getComponent(node.entity as Entity, EntityTreeComponent) switch (e.key) { case 'ArrowDown': { e.preventDefault() @@ -319,7 +228,7 @@ export default function HierarchyPanel() { if (!nextNode) return if (e.shiftKey) { - EditorControlFunctions.addToSelection([nextNode.entityNode ?? nextNode.obj3d!.uuid]) + EditorControlFunctions.addToSelection([nextNode.entity]) } const nextNodeEl = document.getElementById(getNodeElId(nextNode)) @@ -336,7 +245,7 @@ export default function HierarchyPanel() { if (!prevNode) return if (e.shiftKey) { - EditorControlFunctions.addToSelection([prevNode.entityNode ?? prevNode.obj3d!.uuid]) + EditorControlFunctions.addToSelection([prevNode.entity]) } const prevNodeEl = document.getElementById(getNodeElId(prevNode)) @@ -348,7 +257,6 @@ export default function HierarchyPanel() { case 'ArrowLeft': if (entityTree && (!entityTree.children || entityTree.children.length === 0)) return - if (node.obj3d && (!node.obj3d.children || node.obj3d.children.length === 0)) return if (e.shiftKey) collapseChildren(node) else collapseNode(node) @@ -356,7 +264,6 @@ export default function HierarchyPanel() { case 'ArrowRight': if (entityTree && (!entityTree.children || entityTree.children.length === 0)) return - if (node.obj3d && (!node.obj3d.children || node.obj3d.children.length === 0)) return if (e.shiftKey) expandChildren(node) else expandNode(node) @@ -364,10 +271,10 @@ export default function HierarchyPanel() { case 'Enter': if (e.shiftKey) { - EditorControlFunctions.toggleSelection([node.entityNode ?? node.obj3d!.uuid]) + EditorControlFunctions.toggleSelection([node.entity]) setSelectedNode(null) } else { - EditorControlFunctions.replaceSelection([node.entityNode ?? node.obj3d!.uuid]) + EditorControlFunctions.replaceSelection([node.entity]) setSelectedNode(node) } break @@ -384,27 +291,21 @@ export default function HierarchyPanel() { const onDeleteNode = useCallback((node: HeirarchyTreeNodeType) => { handleClose() - let objs = node.selected - ? getEntityNodeArrayFromEntities(selectionState.selectedEntities.value) - : [node.entityNode ?? node.obj3d!.uuid] + const objs = node.selected ? selectionState.selectedEntities.value : [node.entity] EditorControlFunctions.removeObject(objs) }, []) const onDuplicateNode = useCallback((node: HeirarchyTreeNodeType) => { handleClose() - let objs = node.selected - ? getEntityNodeArrayFromEntities(selectionState.selectedEntities.value) - : [node.entityNode ?? node.obj3d!.uuid] + const objs = node.selected ? selectionState.selectedEntities.value : [node.entity] EditorControlFunctions.duplicateObject(objs) }, []) const onGroupNodes = useCallback((node: HeirarchyTreeNodeType) => { handleClose() - const objs = node.selected - ? getEntityNodeArrayFromEntities(selectionState.selectedEntities.value) - : [node.entityNode ?? node.obj3d!.uuid] + const objs = node.selected ? selectionState.selectedEntities.value : [node.entity] EditorControlFunctions.groupObjects(objs) }, []) @@ -414,8 +315,8 @@ export default function HierarchyPanel() { const onRenameNode = useCallback((node: HeirarchyTreeNodeType) => { handleClose() - if (node.entityNode) { - const entity = node.entityNode as Entity + if (node.entity) { + const entity = node.entity as Entity setRenamingNode({ entity, name: getComponent(entity, NameComponent) }) } else { // todo @@ -423,14 +324,14 @@ export default function HierarchyPanel() { }, []) const onChangeName = useCallback( - (node: HeirarchyTreeNodeType, name: string) => setRenamingNode({ entity: node.entityNode as Entity, name }), + (node: HeirarchyTreeNodeType, name: string) => setRenamingNode({ entity: node.entity as Entity, name }), [] ) const onRenameSubmit = useCallback((node: HeirarchyTreeNodeType, name: string) => { if (name) { - if (!node.obj3d) updateProperties(NameComponent, name, [node.entityNode]) - const groups = getOptionalComponent(node.entityNode as Entity, GroupComponent) + updateProperties(NameComponent, name, [node.entity]) + const groups = getOptionalComponent(node.entity as Entity, GroupComponent) if (groups) for (const obj of groups) if (obj) obj.name = name } @@ -503,7 +404,7 @@ export default function HierarchyPanel() { onToggle, onUpload }} - itemKey={getNodeKey} + itemKey={(index) => index} outerRef={treeContainerDropTarget} innerElementType="ul" > @@ -517,16 +418,6 @@ export default function HierarchyPanel() { <>
-
- {t('editor:hierarchy.lbl-explode')} - getMutableState(EditorState).showObject3DInHierarchy.set(value)} - /> -
{Engine.instance.scene && ( @@ -549,7 +440,7 @@ export default function HierarchyPanel() { {t('editor:hierarchy.lbl-addEntity')}
- + onRenameNode(contextSelectedItem!)}>{t('editor:hierarchy.lbl-rename')} { - return 'hierarchy-node-' + (node.obj3d ? node.obj3d.uuid : node.entityNode) + return 'hierarchy-node-' + node.entity } export type RenameNodeData = { @@ -98,13 +93,9 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => { const data = props.data const selectionState = useHookstate(getMutableState(SelectionState)) - const nodeName = node.obj3d - ? node.obj3d.name ?? node.obj3d.uuid - : hasComponent(node.entityNode as Entity, NameComponent) - ? useComponent(node.entityNode as Entity, NameComponent).value - : '' + const nodeName = useComponent(node.entity, NameComponent).value - const errors = node.entityNode ? useOptionalComponent(node.entityNode as Entity, ErrorComponent) : undefined + const errors = node.entity ? useOptionalComponent(node.entity as Entity, ErrorComponent) : undefined const firstError = errors?.keys[0] const onClickToggle = useCallback( @@ -145,7 +136,7 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => { return { type: ItemTypes.Node, multiple, - value: multiple ? getEntityNodeArrayFromEntities(selectedEntities) : selectedEntities[0] + value: multiple ? selectedEntities : selectedEntities[0] } }, canDrag() { @@ -159,24 +150,22 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => { }) const dropItem = (node: HeirarchyTreeNodeType, place: 'On' | 'Before' | 'After') => { - const isObj3D = !node.entityNode && node.obj3d - if (isObj3D) return let parentNode: Entity let beforeNode: Entity if (place === 'Before') { - const entityTreeComponent = getComponent(node.entityNode as Entity, EntityTreeComponent) + const entityTreeComponent = getComponent(node.entity as Entity, EntityTreeComponent) parentNode = entityTreeComponent?.parentEntity! - beforeNode = node.entityNode as Entity + beforeNode = node.entity as Entity } else if (place === 'After') { - const entityTreeComponent = getComponent(node.entityNode as Entity, EntityTreeComponent) + const entityTreeComponent = getComponent(node.entity as Entity, EntityTreeComponent) parentNode = entityTreeComponent?.parentEntity! const parentTreeComponent = getComponent(entityTreeComponent?.parentEntity!, EntityTreeComponent) if (!node.lastChild && parentNode && parentTreeComponent?.children.length > node.childIndex + 1) { beforeNode = parentTreeComponent.children[node.childIndex + 1] } } else { - parentNode = node.entityNode as Entity + parentNode = node.entity as Entity } if (!parentNode) @@ -219,33 +208,22 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => { } } - const canDropItem = (entityNode: Entity | Object3D, dropOn?: boolean) => { + const canDropItem = (entityNode: Entity, dropOn?: boolean) => { return (item, monitor): boolean => { //check if monitor is over or object is not parent element if (!monitor.isOver()) return false - const isObject3D = typeof entityNode === 'object' - const eNode = entityNode as Entity - const obj3d = entityNode as Object3D - if (!dropOn && !isObject3D) { - const entityTreeComponent = getComponent(eNode, EntityTreeComponent) + if (!dropOn) { + const entityTreeComponent = getComponent(entityNode, EntityTreeComponent) if (!entityTreeComponent) return false } - if (dropOn && isObject3D && !obj3d.parent) return false if (item.type === ItemTypes.Node) { - const entityTreeComponent = getComponent(eNode, EntityTreeComponent) + const entityTreeComponent = getComponent(entityNode, EntityTreeComponent) return ( (dropOn || !!entityTreeComponent.parentEntity) && !(item.multiple - ? item.value.some((otherObject) => isAncestor(otherObject, eNode)) - : isAncestor(item.value, eNode)) - ) - } else if (isObject3D) { - return ( - (dropOn || !!obj3d.parent) && - !(item.multiple - ? item.value.some((otherObject) => typeof otherObject !== 'string' || isAncestor(otherObject, obj3d.uuid)) - : isAncestor(item.value, obj3d.uuid)) + ? item.value.some((otherObject) => isAncestor(otherObject, entityNode)) + : isAncestor(item.value, entityNode)) ) } return true @@ -255,7 +233,7 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => { const [{ canDropBefore, isOverBefore }, beforeDropTarget] = useDrop({ accept: [ItemTypes.Node, ItemTypes.File, ItemTypes.Component, ...SupportedFileTypes], drop: dropItem(node, 'Before'), - canDrop: canDropItem((node.entityNode as Entity) ?? node.obj3d!), + canDrop: canDropItem(node.entity), collect: (monitor) => ({ canDropBefore: monitor.canDrop(), isOverBefore: monitor.isOver() @@ -265,7 +243,7 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => { const [{ canDropAfter, isOverAfter }, afterDropTarget] = useDrop({ accept: [ItemTypes.Node, ItemTypes.File, ItemTypes.Component, ...SupportedFileTypes], drop: dropItem(node, 'After'), - canDrop: canDropItem((node.entityNode as Entity) ?? node.obj3d!), + canDrop: canDropItem(node.entity), collect: (monitor) => ({ canDropAfter: monitor.canDrop(), isOverAfter: monitor.isOver() @@ -275,7 +253,7 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => { const [{ canDropOn, isOverOn }, onDropTarget] = useDrop({ accept: [ItemTypes.Node, ItemTypes.File, ItemTypes.Component, ...SupportedFileTypes], drop: dropItem(node, 'On'), - canDrop: canDropItem((node.entityNode as Entity) ?? node.obj3d!, true), + canDrop: canDropItem(node.entity, true), collect: (monitor) => ({ canDropOn: monitor.canDrop(), isOverOn: monitor.isOver() @@ -287,13 +265,13 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => { }, [preview]) const editors = - typeof node.entityNode === 'number' && entityExists(node.entityNode as Entity) - ? getAllComponents(node.entityNode as Entity) + typeof node.entity === 'number' && entityExists(node.entity as Entity) + ? getAllComponents(node.entity as Entity) .map((c) => EntityNodeEditor.get(c)!) .filter((c) => !!c) : [] const IconComponent = editors.length && editors[editors.length - 1].iconComponent - const renaming = data.renamingNode && data.renamingNode.entity === node.entityNode + const renaming = data.renamingNode && data.renamingNode.entity === node.entity const marginLeft = node.depth > 0 ? node.depth * 8 + 20 : 0 return ( @@ -305,7 +283,6 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => { onKeyDown={onNodeKeyDown} className={ styles.treeNodeContainer + - (node.obj3d ? ' ' + styles.obj3d : '') + (node.depth === 0 ? ' ' + styles.rootNode : '') + (node.selected ? ' ' + styles.selected : '') + (node.active ? ' ' + styles.active : '') diff --git a/packages/editor/src/components/inputs/MaterialInput.tsx b/packages/editor/src/components/inputs/MaterialInput.tsx index f1d0c3cc94..55907f5613 100644 --- a/packages/editor/src/components/inputs/MaterialInput.tsx +++ b/packages/editor/src/components/inputs/MaterialInput.tsx @@ -26,17 +26,18 @@ Ethereal Engine. All Rights Reserved. import React from 'react' import { DropTargetMonitor, useDrop } from 'react-dnd' -import { EntityOrObjectUUID } from '@etherealengine/engine/src/ecs/functions/EntityTree' - +import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' import { ItemTypes } from '../../constants/AssetTypes' import { ControlledStringInput } from './StringInput' -export function MaterialInput< - T extends { value: EntityOrObjectUUID; onChange: (val: EntityOrObjectUUID) => any; [key: string]: any } ->({ value, onChange, ...rest }: T) { +export function MaterialInput any; [key: string]: any }>({ + value, + onChange, + ...rest +}: T) { function onDrop(item, monitor: DropTargetMonitor) { const value = item.value - let element = value as EntityOrObjectUUID | EntityOrObjectUUID[] | undefined + let element = value as Entity | Entity[] | undefined if (typeof element === 'undefined') return if (Array.isArray(value)) { element = element[0] diff --git a/packages/editor/src/components/inputs/SceneObjectInput.tsx b/packages/editor/src/components/inputs/SceneObjectInput.tsx deleted file mode 100644 index bba36d3163..0000000000 --- a/packages/editor/src/components/inputs/SceneObjectInput.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/* -CPAL-1.0 License - -The contents of this file are subject to the Common Public Attribution License -Version 1.0. (the "License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at -https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. -The License is based on the Mozilla Public License Version 1.1, but Sections 14 -and 15 have been added to cover use of software over a computer network and -provide for limited attribution for the Original Developer. In addition, -Exhibit A has been modified to be consistent with Exhibit B. - -Software distributed under the License is distributed on an "AS IS" basis, -WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the -specific language governing rights and limitations under the License. - -The Original Code is Ethereal Engine. - -The Original Developer is the Initial Developer. The Initial Developer of the -Original Code is the Ethereal Engine team. - -All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 -Ethereal Engine. All Rights Reserved. -*/ - -import React from 'react' -import { DropTargetMonitor, useDrop } from 'react-dnd' - -import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' -import { getComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' -import { EntityOrObjectUUID } from '@etherealengine/engine/src/ecs/functions/EntityTree' -import { UUIDComponent } from '@etherealengine/engine/src/scene/components/UUIDComponent' - -import { ItemTypes } from '../../constants/AssetTypes' -import { ControlledStringInput } from './StringInput' - -export function SceneObjectInput< - T extends { value: EntityOrObjectUUID; onChange: (val: EntityOrObjectUUID) => any; [key: string]: any } ->({ value, onChange, ...rest }: T) { - function onDrop(item, monitor: DropTargetMonitor) { - const value = item.value - let element = value as EntityOrObjectUUID | EntityOrObjectUUID[] | undefined - if (typeof element === 'string' || typeof element === 'undefined') return - if (Array.isArray(value)) { - element = element[0] - } - onChange(getComponent(element as Entity, UUIDComponent)) - } - - const [{ canDrop, isOver }, dropRef] = useDrop({ - accept: [ItemTypes.Node], - drop: onDrop, - collect: (monitor) => ({ - canDrop: monitor.canDrop(), - isOver: monitor.isOver() - }) - }) - - return ( - <> - - - ) -} diff --git a/packages/editor/src/components/materials/MaterialEditor.tsx b/packages/editor/src/components/materials/MaterialEditor.tsx index 9e57d706f3..986548a1fe 100644 --- a/packages/editor/src/components/materials/MaterialEditor.tsx +++ b/packages/editor/src/components/materials/MaterialEditor.tsx @@ -24,32 +24,35 @@ Ethereal Engine. All Rights Reserved. */ import React, { useCallback, useEffect } from 'react' -import { Material, Texture } from 'three' +import { Texture } from 'three' import styles from '@etherealengine/editor/src/components/layout/styles.module.scss' import { AssetLoader } from '@etherealengine/engine/src/assets/classes/AssetLoader' import createReadableTexture from '@etherealengine/engine/src/assets/functions/createReadableTexture' +import { MaterialLibraryState } from '@etherealengine/engine/src/renderer/materials/MaterialLibrary' import { LibraryEntryType } from '@etherealengine/engine/src/renderer/materials/constants/LibraryEntry' import { changeMaterialPrototype, entryId, - materialFromId, - prototypeFromId + materialFromId } from '@etherealengine/engine/src/renderer/materials/functions/MaterialLibraryFunctions' import { removeMaterialPlugin } from '@etherealengine/engine/src/renderer/materials/functions/MaterialPluginFunctions' -import { MaterialLibraryState } from '@etherealengine/engine/src/renderer/materials/MaterialLibrary' -import { getMutableState, getState, none, State, useState } from '@etherealengine/hyperflux' +import { State, getMutableState, none, useHookstate, useState } from '@etherealengine/hyperflux' +import MaterialLibraryIcon from '@mui/icons-material/Yard' import { Box, Divider, Stack } from '@mui/material' +import { useTranslation } from 'react-i18next' import { EditorControlFunctions } from '../../functions/EditorControlFunctions' -import { SelectionState } from '../../services/SelectionServices' import { Button } from '../inputs/Button' import { InputGroup } from '../inputs/InputGroup' import ParameterInput from '../inputs/ParameterInput' import SelectInput from '../inputs/SelectInput' import StringInput from '../inputs/StringInput' import PaginatedList from '../layout/PaginatedList' +import { PanelDragContainer, PanelIcon, PanelTitle } from '../layout/Panel' +import { InfoTooltip } from '../layout/Tooltip' +import { MaterialSelectionState } from './MaterialLibraryState' type ThumbnailData = { src: string @@ -64,11 +67,12 @@ const toBlobs = (thumbnails: Record): Record +export function MaterialEditor(props: { materialID: string }) { + const { materialID } = props const materialLibrary = useState(getMutableState(MaterialLibraryState)) - const materialComponent = materialLibrary.materials[material.uuid] - let prototypeComponent = materialLibrary.prototypes[materialComponent.prototype.value].value + const materialComponent = materialLibrary.materials[materialID] + const prototypeComponent = materialLibrary.prototypes.value[materialComponent.prototype.value] + const material = materialFromId(materialID).material const loadingData = useState(false) const prototypes = Object.values(materialLibrary.prototypes.value).map((prototype) => ({ label: prototype.prototypeId, @@ -145,7 +149,7 @@ export default function MaterialEditor({ material, ...rest }: { material: Materi const selectedPlugin = useState('vegetation') return ( -
+
@@ -178,7 +182,7 @@ export default function MaterialEditor({ material, ...rest }: { material: Materi onChange={(protoId) => { const nuMat = changeMaterialPrototype(material, protoId) materialComponent.set(materialFromId(nuMat!.uuid)) - prototypeComponent = prototypeFromId(materialComponent.prototype.value) + // prototypeComponent = prototypeFromId(materialComponent.prototype.value) }} /> @@ -203,7 +207,7 @@ export default function MaterialEditor({ material, ...rest }: { material: Materi prop = val } EditorControlFunctions.modifyMaterial( - getState(SelectionState).selectedEntities.filter((val) => typeof val === 'string') as string[], + [materialID], entryId(materialComponent.value, LibraryEntryType.MATERIAL), [{ [k]: prop }] ) @@ -269,3 +273,55 @@ export default function MaterialEditor({ material, ...rest }: { material: Materi
) } + +export const MaterialPropertyTitle = () => { + const { t } = useTranslation() + + return ( +
+ + + + + {t('editor:materialProperties.title')} + + + +
+ ) +} + +export const MaterialProperties = () => { + const { t } = useTranslation() + + const selectedMaterialID = useHookstate(getMutableState(MaterialSelectionState).selectedMaterial) + + const material = selectedMaterialID.value ? materialFromId(selectedMaterialID.value) : undefined + + return ( +
+ {material && selectedMaterialID.value ? ( + + ) : ( +
+ {t('editor:properties.noNodeSelected')} +
+ )} +
+ ) +} + +export default MaterialProperties diff --git a/packages/editor/src/components/materials/MaterialLibraryPanel.tsx b/packages/editor/src/components/materials/MaterialLibraryPanel.tsx index 98ce60eafe..104519c9b9 100644 --- a/packages/editor/src/components/materials/MaterialLibraryPanel.tsx +++ b/packages/editor/src/components/materials/MaterialLibraryPanel.tsx @@ -43,16 +43,15 @@ import { getMutableState, getState, useHookstate, useState } from '@etherealengi import { Stack } from '@mui/material' import { uploadProjectFiles } from '../../functions/assetFunctions' -import { EditorControlFunctions } from '../../functions/EditorControlFunctions' import { EditorState } from '../../services/EditorServices' -import { SelectionState } from '../../services/SelectionServices' import styles from '../hierarchy/styles.module.scss' import { Button } from '../inputs/Button' import MaterialLibraryEntry, { MaterialLibraryEntryType } from './MaterialLibraryEntry' +import { MaterialSelectionState } from './MaterialLibraryState' export default function MaterialLibraryPanel() { const editorState = useHookstate(getMutableState(EditorState)) - const selectionState = useHookstate(getMutableState(SelectionState)) + const selectedMaterial = useHookstate(getMutableState(MaterialSelectionState).selectedMaterial) const materialLibrary = useHookstate(getMutableState(MaterialLibraryState)) const MemoMatLibEntry = memo(MaterialLibraryEntry, areEqual) const nodeChanges = useState(0) @@ -77,10 +76,8 @@ export default function MaterialLibraryPanel() { uuid, type: LibraryEntryType.MATERIAL_SOURCE, entry: srcComp, - selected: selectionState.selectedEntities.value.some( - (entity) => typeof entity === 'string' && entity === uuid - ), - active: selectionState.selectedEntities.value.at(-1) === uuid, + selected: selectedMaterial.value === uuid, + active: selectedMaterial.value === uuid, isCollapsed }, ...(isCollapsed @@ -92,22 +89,20 @@ export default function MaterialLibraryPanel() { uuid, type: LibraryEntryType.MATERIAL, entry: materialFromId(uuid), - selected: selectionState.selectedEntities.value.some( - (entity) => typeof entity === 'string' && entity === uuid - ), - active: selectionState.selectedEntities.value.at(-1) === uuid + selected: selectedMaterial.value === uuid, + active: selectedMaterial.value === uuid } })) ] }) return result - }, [nodeChanges, srcs, selectionState.selectedEntities]) + }, [nodeChanges, srcs, selectedMaterial]) const nodes = useState(createNodes()) const onClick = useCallback((e: MouseEvent, node: MaterialLibraryEntryType) => { if (!editorState.lockPropertiesPanel.get()) { - EditorControlFunctions.replaceSelection([entryId(node.entry, node.type)]) + if (getState(MaterialLibraryState).materials[node.uuid]) selectedMaterial.set(node.uuid) } }, []) @@ -129,7 +124,7 @@ export default function MaterialLibraryPanel() { useEffect(() => { nodes.set(createNodes()) - }, [nodeChanges, selectionState.selectedEntities, srcs]) + }, [nodeChanges, selectedMaterial, srcs]) return ( <> @@ -169,11 +164,7 @@ export default function MaterialLibraryPanel() { - {/* animations */} - {isMesh && ( - <> - - - - - - - - {materialIds.value?.length > 0 && ( - <> - - { - currentMaterialId.set(materialIds.value.findIndex(({ value }) => value === nuVal)) - }} - /> - - { - if (materialLibrary.materials[nuId]) { - if (Array.isArray(mesh.material)) { - mesh.material[currentMaterialId.value] = materialFromId('' + nuId).material - } else { - mesh.material = materialFromId('' + nuId).material - mesh.material.needsUpdate = true - } - } - }} - /> - - - )} - - - )} - - {isInstancedMesh && ( - - {instancedMesh?.count > 0 && ( - <> - -
- -
-
- { - const transform = new Matrix4() - instancedMesh.getMatrixAt(i, transform) - const position = new Vector3() - const rotation = new Quaternion() - const scale = new Vector3() - transform.decompose(position, rotation, scale) - - const euler = new Euler() - euler.setFromQuaternion(rotation) - return ( - - - - - - - - - - - - ) - }} - /> - - )} -
- )} -
-

userData

- { - EditorControlFunctions.modifyObject3d( - selectionState.value.selectedEntities.filter((val) => typeof val === 'string') as string[], - [{ userData: edit.updated_src }] - ) - obj3d.userData = edit.updated_src - }} - onAdd={(add) => { - obj3d.userData = add.updated_src - }} - onDelete={(_delete) => { - obj3d.userData = _delete.updated_src - }} - onSelect={() => {}} - theme="monokai" - src={obj3d.userData} - /> -
- - ) -} - -Object3DNodeEditor.iconComponent = AxisIcon - -export default Object3DNodeEditor diff --git a/packages/editor/src/components/properties/PropertiesPanelContainer.tsx b/packages/editor/src/components/properties/PropertiesPanelContainer.tsx index 7605035927..0b8e02f594 100755 --- a/packages/editor/src/components/properties/PropertiesPanelContainer.tsx +++ b/packages/editor/src/components/properties/PropertiesPanelContainer.tsx @@ -26,9 +26,7 @@ Ethereal Engine. All Rights Reserved. import { useHookstate } from '@hookstate/core' import React from 'react' import { useTranslation } from 'react-i18next' -import { Object3D } from 'three' -import { Engine } from '@etherealengine/engine/src/ecs/classes/Engine' import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' import { ComponentJSONIDMap, @@ -36,10 +34,8 @@ import { hasComponent, useOptionalComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' -import { MaterialComponentType } from '@etherealengine/engine/src/renderer/materials/components/MaterialComponent' -import { MaterialLibraryState } from '@etherealengine/engine/src/renderer/materials/MaterialLibrary' import { UUIDComponent } from '@etherealengine/engine/src/scene/components/UUIDComponent' -import { getMutableState, getState } from '@etherealengine/hyperflux' +import { getMutableState } from '@etherealengine/hyperflux' import { useDrop } from 'react-dnd' import { ItemTypes } from '../../constants/AssetTypes' @@ -47,42 +43,17 @@ import { EntityNodeEditor } from '../../functions/ComponentEditors' import { EditorControlFunctions } from '../../functions/EditorControlFunctions' import { EditorState } from '../../services/EditorServices' import { SelectionState } from '../../services/SelectionServices' -import MaterialEditor from '../materials/MaterialEditor' import { CoreNodeEditor } from './CoreNodeEditor' -import Object3DNodeEditor from './Object3DNodeEditor' - -/** - * PropertiesPanelContent used as container element contains content of editor view. - * @type {Style} - */ -const propertiesPanelContentStyle: React.CSSProperties = { - overflowY: 'auto', - height: '100%' -} -/** - * NoNodeSelectedMessage used to show the message when no selected no is there. - * - * @type {Style} - */ -const noNodeSelectedMessageStyle: React.CSSProperties = { - height: '100%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - color: 'var(--textColor)' -} const EntityComponentEditor = (props: { entity; component; multiEdit }) => { const { entity, component, multiEdit } = props - const componentMounted = useOptionalComponent(entity as Entity, component) + const componentMounted = useOptionalComponent(entity, component) const Editor = EntityNodeEditor.get(component)! if (!componentMounted) return null // nodeEntity is used as key here to signal to React when the entity has changed, // and to prevent state from being recycled between editor instances, which // can cause hookstate to throw errors. - return ( - - ) + return } const EntityEditor = (props: { entity: Entity; multiEdit: boolean }) => { @@ -111,7 +82,7 @@ const EntityEditor = (props: { entity: Entity; multiEdit: boolean }) => { if (!uuid) return null - const components = getAllComponents(entity as Entity).filter((c) => EntityNodeEditor.has(c)) + const components = getAllComponents(entity).filter((c) => EntityNodeEditor.has(c)) return (
{ const selectedEntities = selectionState.selectedEntities.value const { t } = useTranslation() - const materialLibrary = getState(MaterialLibraryState) - - //rendering editor views for customization of element properties - let content - const lockedNode = editorState.lockPropertiesPanel.value const multiEdit = selectedEntities.length > 1 - const nodeEntity = lockedNode + const entity = lockedNode ? UUIDComponent.entitiesByUUID[lockedNode] ?? lockedNode : selectedEntities[selectedEntities.length - 1] - const isMaterial = - typeof nodeEntity === 'string' && - (!!materialLibrary.materials[nodeEntity] || - Object.values(materialLibrary.materials) - .map(({ material }) => material.uuid) - .includes(nodeEntity)) - - const isObject3D = typeof nodeEntity === 'string' && !isMaterial - - const node = isMaterial - ? materialLibrary.materials[nodeEntity as string] ?? - Object.values(materialLibrary.materials).find(({ material }) => material.uuid === nodeEntity) - : isObject3D - ? Engine.instance.scene.getObjectByProperty('uuid', nodeEntity as string) - : nodeEntity - - if (!nodeEntity || !node) { - content =
{t('editor:properties.noNodeSelected')}
- } else if (isObject3D) { - content = ( -
- {/* @todo these types are incorrect */} - -
- ) - } else if (isMaterial) { - content = ( -
- -
- ) - } else { - const entity = nodeEntity as Entity - content = - } - - return
{content}
+ return ( +
+ {entity ? ( + + ) : ( +
+ {t('editor:properties.noNodeSelected')} +
+ )} +
+ ) } export default PropertiesPanelContainer diff --git a/packages/editor/src/components/properties/TransformPropertyGroup.tsx b/packages/editor/src/components/properties/TransformPropertyGroup.tsx index 6b391ed42a..990e916eca 100755 --- a/packages/editor/src/components/properties/TransformPropertyGroup.tsx +++ b/packages/editor/src/components/properties/TransformPropertyGroup.tsx @@ -27,7 +27,6 @@ import React from 'react' import { useTranslation } from 'react-i18next' import { Euler, Vector3 } from 'three' -import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' import { defineQuery, getComponent, @@ -35,7 +34,6 @@ import { useComponent, useOptionalComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' -import { getEntityNodeArrayFromEntities } from '@etherealengine/engine/src/ecs/functions/EntityTree' import { SceneDynamicLoadTagComponent } from '@etherealengine/engine/src/scene/components/SceneDynamicLoadTagComponent' import { TransformGizmoComponent } from '@etherealengine/engine/src/scene/components/TransformGizmo' import { TransformSpace } from '@etherealengine/engine/src/scene/constants/transformConstants' @@ -72,14 +70,12 @@ export const TransformPropertyGroup: EditorComponentType = (props) => { } const onChangeDynamicLoad = (value) => { - const nodes = getEntityNodeArrayFromEntities(getMutableState(SelectionState).selectedEntities.value).filter( - (n) => typeof n !== 'string' - ) as Entity[] + const nodes = getMutableState(SelectionState).selectedEntities.value EditorControlFunctions.addOrRemoveComponent(nodes, SceneDynamicLoadTagComponent, value) } const onChangePosition = (value: Vector3) => { - const nodes = getEntityNodeArrayFromEntities(getMutableState(SelectionState).selectedEntities.value) + const nodes = getMutableState(SelectionState).selectedEntities.value EditorControlFunctions.positionObject(nodes, [value]) LocalTransformComponent.stateMap[props.entity]!.set(LocalTransformComponent.valueMap[props.entity]) @@ -94,7 +90,7 @@ export const TransformPropertyGroup: EditorComponentType = (props) => { } const onChangeRotation = (value: Euler) => { - const nodes = getEntityNodeArrayFromEntities(getMutableState(SelectionState).selectedEntities.value) + const nodes = getMutableState(SelectionState).selectedEntities.value EditorControlFunctions.rotateObject(nodes, [value]) LocalTransformComponent.stateMap[props.entity]!.set(LocalTransformComponent.valueMap[props.entity]) @@ -112,7 +108,7 @@ export const TransformPropertyGroup: EditorComponentType = (props) => { if (useGlobalTransformComponent.value) { transformComponent.scale.set(value) } - const nodes = getEntityNodeArrayFromEntities(getMutableState(SelectionState).selectedEntities.value) + const nodes = getMutableState(SelectionState).selectedEntities.value EditorControlFunctions.scaleObject(nodes, [value], TransformSpace.Local, true) LocalTransformComponent.stateMap[props.entity]!.set(LocalTransformComponent.valueMap[props.entity]) } diff --git a/packages/editor/src/components/properties/Util.ts b/packages/editor/src/components/properties/Util.ts index 6a60d1372c..f3fff1b249 100644 --- a/packages/editor/src/components/properties/Util.ts +++ b/packages/editor/src/components/properties/Util.ts @@ -30,11 +30,7 @@ import { SerializedComponentType, updateComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' -import { - EntityOrObjectUUID, - getEntityNodeArrayFromEntities, - iterateEntityNode -} from '@etherealengine/engine/src/ecs/functions/EntityTree' +import { iterateEntityNode } from '@etherealengine/engine/src/ecs/functions/EntityTree' import { UUIDComponent } from '@etherealengine/engine/src/scene/components/UUIDComponent' import { getMutableState } from '@etherealengine/hyperflux' @@ -55,7 +51,7 @@ export type EditorComponentType = React.FC & { export const updateProperty = >( component: C, propName: K, - nodes?: EntityOrObjectUUID[] + nodes?: Entity[] ) => { return (value: SerializedComponentType[K]) => { updateProperties(component, { [propName]: value } as any, nodes) @@ -65,7 +61,7 @@ export const updateProperty = ( component: C, properties: Partial>, - nodes?: EntityOrObjectUUID[] + nodes?: Entity[] ) => { const editorState = getMutableState(EditorState) const selectionState = getMutableState(SelectionState) @@ -74,8 +70,7 @@ export const updateProperties = ( ? nodes : editorState.lockPropertiesPanel.value ? [UUIDComponent.entitiesByUUID[editorState.lockPropertiesPanel.value]] - : (getEntityNodeArrayFromEntities(selectionState.selectedEntities.value) as EntityOrObjectUUID[]) - + : selectionState.selectedEntities.value for (let i = 0; i < affectedNodes.length; i++) { const node = affectedNodes[i] if (typeof node === 'string') continue @@ -86,7 +81,7 @@ export const updateProperties = ( export const commitProperty = >( component: C, propName: K, - nodes?: EntityOrObjectUUID[] + nodes?: Entity[] ) => { return (value: SerializedComponentType[K]) => { commitProperties(component, { [propName]: value } as any, nodes) @@ -96,7 +91,7 @@ export const commitProperty = ( component: C, properties: Partial>, - nodes?: EntityOrObjectUUID[] + nodes?: Entity[] ) => { const editorState = getMutableState(EditorState) const selectionState = getMutableState(SelectionState) @@ -105,7 +100,7 @@ export const commitProperties = ( ? nodes : editorState.lockPropertiesPanel.value ? [UUIDComponent.entitiesByUUID[editorState.lockPropertiesPanel.value]] - : (getEntityNodeArrayFromEntities(selectionState.selectedEntities.value) as EntityOrObjectUUID[]) + : selectionState.selectedEntities.value EditorControlFunctions.modifyProperty(affectedNodes, component, properties) } diff --git a/packages/editor/src/components/properties/three.quarks/BehaviorInput.tsx b/packages/editor/src/components/properties/three.quarks/BehaviorInput.tsx index 679c3a1908..3549dd6cb8 100644 --- a/packages/editor/src/components/properties/three.quarks/BehaviorInput.tsx +++ b/packages/editor/src/components/properties/three.quarks/BehaviorInput.tsx @@ -56,7 +56,6 @@ import BooleanInput from '../../inputs/BooleanInput' import { Button } from '../../inputs/Button' import InputGroup from '../../inputs/InputGroup' import NumericInputGroup from '../../inputs/NumericInputGroup' -import { SceneObjectInput } from '../../inputs/SceneObjectInput' import SelectInput from '../../inputs/SelectInput' import TexturePreviewInput from '../../inputs/TexturePreviewInput' import Vector3Input from '../../inputs/Vector3Input' @@ -329,10 +328,11 @@ export default function BehaviorInput({ return ( <> - + /> */} ) diff --git a/packages/editor/src/components/toolbar/styles.module.scss b/packages/editor/src/components/toolbar/styles.module.scss index 752fe43db4..9e0da6136f 100644 --- a/packages/editor/src/components/toolbar/styles.module.scss +++ b/packages/editor/src/components/toolbar/styles.module.scss @@ -32,8 +32,7 @@ } & > ul { - margin-block-start: 2px; - margin-block-end: 2px; + margin-block: 2px 2px; padding-inline-start: 4px; & > li { diff --git a/packages/editor/src/functions/EditorControlFunctions.ts b/packages/editor/src/functions/EditorControlFunctions.ts index df70c792a0..92eeba64db 100644 --- a/packages/editor/src/functions/EditorControlFunctions.ts +++ b/packages/editor/src/functions/EditorControlFunctions.ts @@ -41,15 +41,9 @@ import { updateComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' import { createEntity, removeEntity } from '@etherealengine/engine/src/ecs/functions/EntityFunctions' -import { - EntityOrObjectUUID, - EntityTreeComponent, - getEntityNodeArrayFromEntities, - traverseEntityNode -} from '@etherealengine/engine/src/ecs/functions/EntityTree' +import { EntityTreeComponent, traverseEntityNode } from '@etherealengine/engine/src/ecs/functions/EntityTree' import { materialFromId } from '@etherealengine/engine/src/renderer/materials/functions/MaterialLibraryFunctions' import { MaterialLibraryState } from '@etherealengine/engine/src/renderer/materials/MaterialLibrary' -import { GroupComponent } from '@etherealengine/engine/src/scene/components/GroupComponent' import { UUIDComponent } from '@etherealengine/engine/src/scene/components/UUIDComponent' import { TransformSpace } from '@etherealengine/engine/src/scene/constants/transformConstants' import { createNewEditorNode } from '@etherealengine/engine/src/scene/systems/SceneLoadingSystem' @@ -58,10 +52,6 @@ import { LocalTransformComponent, TransformComponent } from '@etherealengine/engine/src/transform/components/TransformComponent' -import { - computeLocalTransformMatrix, - computeTransformMatrix -} from '@etherealengine/engine/src/transform/systems/TransformSystem' import { dispatchAction, getMutableState, getState } from '@etherealengine/hyperflux' import { ComponentJson } from '@etherealengine/common/src/interfaces/SceneInterface' @@ -70,6 +60,10 @@ import { NameComponent } from '@etherealengine/engine/src/scene/components/NameC import { SceneObjectComponent } from '@etherealengine/engine/src/scene/components/SceneObjectComponent' import { VisibleComponent } from '@etherealengine/engine/src/scene/components/VisibleComponent' import { serializeEntity } from '@etherealengine/engine/src/scene/functions/serializeWorld' +import { + computeLocalTransformMatrix, + computeTransformMatrix +} from '@etherealengine/engine/src/transform/systems/TransformSystem' import { EditorHistoryAction, EditorHistoryState } from '../services/EditorHistory' import { SelectionState } from '../services/SelectionServices' import { cancelGrabOrPlacement } from './cancelGrabOrPlacement' @@ -77,11 +71,7 @@ import { filterParentEntities } from './filterParentEntities' import { getDetachedObjectsRoots } from './getDetachedObjectsRoots' import { getSpaceMatrix } from './getSpaceMatrix' -const addOrRemoveComponent = >( - nodes: EntityOrObjectUUID[], - component: C, - add: boolean -) => { +const addOrRemoveComponent = >(entities: Entity[], component: C, add: boolean) => { const sceneComponentID = component.jsonID if (!sceneComponentID) return @@ -89,9 +79,7 @@ const addOrRemoveComponent = >( const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() - for (let i = 0; i < nodes.length; i++) { - const entity = nodes[i] - if (typeof entity === 'string') continue + for (const entity of entities) { const entityUUID = getComponent(entity, UUIDComponent) const componentData = newSnapshot.data.scene.entities[entityUUID].components @@ -112,14 +100,13 @@ const addOrRemoveComponent = >( dispatchAction(EditorHistoryAction.createSnapshot(newSnapshot)) } -const modifyName = (nodes: EntityOrObjectUUID[], name: string) => { +const modifyName = (entities: Entity[], name: string) => { cancelGrabOrPlacement() const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() - for (const node of nodes) { - if (typeof node === 'string') continue - const entityUUID = getComponent(node, UUIDComponent) + for (const entity of entities) { + const entityUUID = getComponent(entity, UUIDComponent) const entityData = newSnapshot.data.scene.entities[entityUUID] if (!entityData) continue entityData.name = name @@ -132,7 +119,7 @@ const modifyName = (nodes: EntityOrObjectUUID[], name: string) => { * Updates each property specified in 'properties' on the component for each of the specified entity nodes */ const modifyProperty = >( - nodes: EntityOrObjectUUID[], + entities: Entity[], component: C, properties: Partial> ) => { @@ -140,9 +127,8 @@ const modifyProperty = >( const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() - for (const node of nodes) { - if (typeof node === 'string') continue - const entityUUID = getComponent(node, UUIDComponent) + for (const entity of entities) { + const entityUUID = getComponent(entity, UUIDComponent) const componentData = newSnapshot.data.scene.entities[entityUUID].components.find( (c) => c.name === component.jsonID ) @@ -266,78 +252,57 @@ const createObjectFromSceneElement = ( /** * @todo copying an object should be rooted to which object is currently selected */ -const duplicateObject = (nodes: EntityOrObjectUUID[]) => { +const duplicateObject = (entities: Entity[]) => { cancelGrabOrPlacement() - const parents = [] as EntityOrObjectUUID[] + const parents = [] as Entity[] - for (const o of nodes) { - if (typeof o === 'string') { - const obj3d = obj3dFromUuid(o) - if (!obj3d.parent) throw new Error('Parent is not defined') - const parent = obj3d.parent - parents.push(parent.uuid) - } else { - if (!hasComponent(o, EntityTreeComponent)) throw new Error('Parent is not defined') - const parent = getComponent(o, EntityTreeComponent).parentEntity - if (!parent) throw new Error('Parent is not defined') - parents.push(parent) - } + for (const entity of entities) { + if (!hasComponent(entity, EntityTreeComponent)) throw new Error('Parent is not defined') + const parent = getComponent(entity, EntityTreeComponent).parentEntity + if (!parent) throw new Error('Parent is not defined') + parents.push(parent) } const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() - const rootObjects = getDetachedObjectsRoots(nodes) + const rootEntities = getDetachedObjectsRoots(entities) const copyMap = {} as { [entityUUID: EntityUUID | string]: EntityUUID | string } - for (let i = 0; i < rootObjects.length; i++) { - const object = rootObjects[i] - if (typeof object !== 'string') { - traverseEntityNode(object, (entity) => { - const entityUUID = getComponent(entity, UUIDComponent) - const entityData = newSnapshot.data.scene.entities[entityUUID] - if (!entityData) return /** @todo entity may be loaded in via GLTF **/ + for (const rootEntity of rootEntities) { + traverseEntityNode(rootEntity, (entity) => { + const entityUUID = getComponent(entity, UUIDComponent) + const entityData = newSnapshot.data.scene.entities[entityUUID] + if (!entityData) return /** @todo entity may be loaded in via GLTF **/ - const entityDataClone = JSON.parse(JSON.stringify(entityData)) - const newUUID = MathUtils.generateUUID() as EntityUUID - copyMap[entityUUID] = newUUID + const entityDataClone = JSON.parse(JSON.stringify(entityData)) + const newUUID = MathUtils.generateUUID() as EntityUUID + copyMap[entityUUID] = newUUID - const parentEntity = getComponent(entity, EntityTreeComponent).parentEntity! - const parentEntityUUID = getComponent(parentEntity, UUIDComponent) + const parentEntity = getComponent(entity, EntityTreeComponent).parentEntity! + const parentEntityUUID = getComponent(parentEntity, UUIDComponent) - if (copyMap[parentEntityUUID]) { - entityDataClone.parent = copyMap[parentEntityUUID] - } + if (copyMap[parentEntityUUID]) { + entityDataClone.parent = copyMap[parentEntityUUID] + } - newSnapshot.data.scene.entities[newUUID] = entityDataClone - - if (object === entity) { - /** update index of parent with new entity */ - const parentEntityTreeComponent = getComponent(parentEntity, EntityTreeComponent) - const index = parentEntityTreeComponent.children.indexOf(entity) - if (index) { - for (const [entityUUID, data] of Object.entries(newSnapshot.data.scene.entities)) { - if (typeof data.index !== 'number') continue - if (data.parent === parentEntityUUID) { - if (data.index > index) data.index++ - } + newSnapshot.data.scene.entities[newUUID] = entityDataClone + + if (rootEntity === entity) { + /** update index of parent with new entity */ + const parentEntityTreeComponent = getComponent(parentEntity, EntityTreeComponent) + const index = parentEntityTreeComponent.children.indexOf(entity) + if (index) { + for (const [entityUUID, data] of Object.entries(newSnapshot.data.scene.entities)) { + if (typeof data.index !== 'number') continue + if (data.parent === parentEntityUUID) { + if (data.index > index) data.index++ } } } - }) - } else { - // @todo check this is implemented correctly - const parent = (parents.length ? parents[i] ?? parents[0] : Engine.instance.scene.uuid) as string - // let before = befores.length ? befores[i] ?? befores[0] : undefined - - const pObj3d = obj3dFromUuid(parent) - - const newObject = obj3dFromUuid(object).clone() - copyMap[object] = newObject.uuid - - newObject.parent = copyMap[parent] ? obj3dFromUuid(copyMap[parent] as string) : pObj3d - } + } + }) } dispatchAction(EditorHistoryAction.createSnapshot(newSnapshot)) @@ -347,7 +312,7 @@ const tempMatrix = new Matrix4() const tempVector = new Vector3() const positionObject = ( - nodes: EntityOrObjectUUID[], + nodes: Entity[], positions: Vector3[], space: TransformSpace = TransformSpace.Local, addToPosition?: boolean @@ -356,52 +321,30 @@ const positionObject = ( const node = nodes[i] const pos = positions[i] ?? positions[0] - const isObj3d = typeof node === 'string' - - if (isObj3d) { - const obj3d = obj3dFromUuid(node) - if (space === TransformSpace.Local) { - if (addToPosition) obj3d.position.add(pos) - else obj3d.position.copy(pos) - } else { - obj3d.updateMatrixWorld() - if (addToPosition) { - tempVector.setFromMatrixPosition(obj3d.matrixWorld) - tempVector.add(pos) - } + const transform = getComponent(node, TransformComponent) + const localTransform = getOptionalComponent(node, LocalTransformComponent) ?? transform + const targetComponent = hasComponent(node, LocalTransformComponent) ? LocalTransformComponent : TransformComponent - const _spaceMatrix = space === TransformSpace.World ? obj3d.parent!.matrixWorld : getSpaceMatrix() - tempMatrix.copy(_spaceMatrix).invert() - tempVector.applyMatrix4(tempMatrix) - obj3d.position.copy(tempVector) - } - obj3d.updateMatrix() + if (space === TransformSpace.Local) { + if (addToPosition) localTransform.position.add(pos) + else localTransform.position.copy(pos) } else { - const transform = getComponent(node, TransformComponent) - const localTransform = getOptionalComponent(node, LocalTransformComponent) ?? transform - const targetComponent = hasComponent(node, LocalTransformComponent) ? LocalTransformComponent : TransformComponent + const entityTreeComponent = getComponent(node, EntityTreeComponent) + const parentTransform = entityTreeComponent.parentEntity + ? getComponent(entityTreeComponent.parentEntity, TransformComponent) + : transform - if (space === TransformSpace.Local) { - if (addToPosition) localTransform.position.add(pos) - else localTransform.position.copy(pos) - } else { - const entityTreeComponent = getComponent(node, EntityTreeComponent) - const parentTransform = entityTreeComponent.parentEntity - ? getComponent(entityTreeComponent.parentEntity, TransformComponent) - : transform - - if (addToPosition) { - tempVector.setFromMatrixPosition(transform.matrix) - tempVector.add(pos) - } + if (addToPosition) { + tempVector.setFromMatrixPosition(transform.matrix) + tempVector.add(pos) + } - const _spaceMatrix = space === TransformSpace.World ? parentTransform.matrix : getSpaceMatrix() - tempMatrix.copy(_spaceMatrix).invert() - tempVector.applyMatrix4(tempMatrix) + const _spaceMatrix = space === TransformSpace.World ? parentTransform.matrix : getSpaceMatrix() + tempMatrix.copy(_spaceMatrix).invert() + tempVector.applyMatrix4(tempMatrix) - localTransform.position.copy(tempVector) - updateComponent(node, targetComponent, { position: localTransform.position }) - } + localTransform.position.copy(tempVector) + updateComponent(node, targetComponent, { position: localTransform.position }) } } } @@ -409,96 +352,64 @@ const positionObject = ( const T_QUAT_1 = new Quaternion() const T_QUAT_2 = new Quaternion() -const rotateObject = ( - nodes: EntityOrObjectUUID[], - rotations: Euler[], - space: TransformSpace = TransformSpace.Local -) => { +const rotateObject = (nodes: Entity[], rotations: Euler[], space: TransformSpace = TransformSpace.Local) => { for (let i = 0; i < nodes.length; i++) { const node = nodes[i] - if (typeof node === 'string') { - const obj3d = obj3dFromUuid(node) - T_QUAT_1.setFromEuler(rotations[i] ?? rotations[0]) - if (space === TransformSpace.Local) { - obj3d.quaternion.copy(T_QUAT_1) - obj3d.updateMatrix() - } else { - obj3d.updateMatrixWorld() - const _spaceMatrix = space === TransformSpace.World ? obj3d.parent!.matrixWorld : getSpaceMatrix() - - const inverseParentWorldQuaternion = T_QUAT_2.setFromRotationMatrix(_spaceMatrix).invert() - const newLocalQuaternion = inverseParentWorldQuaternion.multiply(T_QUAT_1) - obj3d.quaternion.copy(newLocalQuaternion) - } - } else { - const transform = getComponent(node, TransformComponent) - const localTransform = getComponent(node, LocalTransformComponent) || transform - const targetComponent = getComponent(node, LocalTransformComponent) ? TransformComponent : LocalTransformComponent + const transform = getComponent(node, TransformComponent) + const localTransform = getComponent(node, LocalTransformComponent) || transform + const targetComponent = getComponent(node, LocalTransformComponent) ? TransformComponent : LocalTransformComponent - T_QUAT_1.setFromEuler(rotations[i] ?? rotations[0]) + T_QUAT_1.setFromEuler(rotations[i] ?? rotations[0]) - if (space === TransformSpace.Local) { - localTransform.rotation.copy(T_QUAT_1) - } else { - const entityTreeComponent = getComponent(node, EntityTreeComponent) - const parentTransform = entityTreeComponent.parentEntity - ? getComponent(entityTreeComponent.parentEntity, TransformComponent) - : transform + if (space === TransformSpace.Local) { + localTransform.rotation.copy(T_QUAT_1) + } else { + const entityTreeComponent = getComponent(node, EntityTreeComponent) + const parentTransform = entityTreeComponent.parentEntity + ? getComponent(entityTreeComponent.parentEntity, TransformComponent) + : transform - const _spaceMatrix = space === TransformSpace.World ? parentTransform.matrix : getSpaceMatrix() + const _spaceMatrix = space === TransformSpace.World ? parentTransform.matrix : getSpaceMatrix() - const inverseParentWorldQuaternion = T_QUAT_2.setFromRotationMatrix(_spaceMatrix).invert() - const newLocalQuaternion = inverseParentWorldQuaternion.multiply(T_QUAT_1) + const inverseParentWorldQuaternion = T_QUAT_2.setFromRotationMatrix(_spaceMatrix).invert() + const newLocalQuaternion = inverseParentWorldQuaternion.multiply(T_QUAT_1) - updateComponent(node, targetComponent, { rotation: newLocalQuaternion }) - computeLocalTransformMatrix(node) - computeTransformMatrix(node) - } + updateComponent(node, targetComponent, { rotation: newLocalQuaternion }) + computeLocalTransformMatrix(node) + computeTransformMatrix(node) } } } -const rotateAround = (nodes: EntityOrObjectUUID[], axis: Vector3, angle: number, pivot: Vector3) => { +const rotateAround = (entities: Entity[], axis: Vector3, angle: number, pivot: Vector3) => { const pivotToOriginMatrix = new Matrix4().makeTranslation(-pivot.x, -pivot.y, -pivot.z) const originToPivotMatrix = new Matrix4().makeTranslation(pivot.x, pivot.y, pivot.z) const rotationMatrix = new Matrix4().makeRotationAxis(axis, angle) - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i] - if (typeof node === 'string') { - const obj3d = obj3dFromUuid(node) - const matrixWorld = new Matrix4() - .copy(obj3d.matrixWorld) - .premultiply(pivotToOriginMatrix) - .premultiply(rotationMatrix) - .premultiply(originToPivotMatrix) - .premultiply(obj3d.parent!.matrixWorld.clone().invert()) - obj3d.matrixWorld.copy(matrixWorld) - } else { - const transform = getComponent(node, TransformComponent) - const localTransform = getComponent(node, LocalTransformComponent) || transform - const entityTreeComponent = getComponent(node, EntityTreeComponent) - const parentTransform = entityTreeComponent.parentEntity - ? getComponent(entityTreeComponent.parentEntity, TransformComponent) - : transform - const targetComponent = hasComponent(node, LocalTransformComponent) ? LocalTransformComponent : TransformComponent - - new Matrix4() - .copy(transform.matrix) - .premultiply(pivotToOriginMatrix) - .premultiply(rotationMatrix) - .premultiply(originToPivotMatrix) - .premultiply(parentTransform.matrixInverse) - .decompose(localTransform.position, localTransform.rotation, localTransform.scale) - - updateComponent(node, targetComponent, { rotation: localTransform.rotation }) - } + for (const entity of entities) { + const transform = getComponent(entity, TransformComponent) + const localTransform = getComponent(entity, LocalTransformComponent) || transform + const entityTreeComponent = getComponent(entity, EntityTreeComponent) + const parentTransform = entityTreeComponent.parentEntity + ? getComponent(entityTreeComponent.parentEntity, TransformComponent) + : transform + const targetComponent = hasComponent(entity, LocalTransformComponent) ? LocalTransformComponent : TransformComponent + + new Matrix4() + .copy(transform.matrix) + .premultiply(pivotToOriginMatrix) + .premultiply(rotationMatrix) + .premultiply(originToPivotMatrix) + .premultiply(parentTransform.matrixInverse) + .decompose(localTransform.position, localTransform.rotation, localTransform.scale) + + updateComponent(entity, targetComponent, { rotation: localTransform.rotation }) } } const scaleObject = ( - nodes: EntityOrObjectUUID[], + entities: Entity[], scales: Vector3[], space: TransformSpace = TransformSpace.Local, overrideScale = false @@ -508,19 +419,13 @@ const scaleObject = ( return } - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i] + for (let i = 0; i < entities.length; i++) { + const entity = entities[i] const scale = scales[i] ?? scales[0] - const transformComponent = - typeof node === 'string' - ? obj3dFromUuid(node) - : getComponent(node, LocalTransformComponent) ?? getComponent(node, TransformComponent) + const transformComponent = getComponent(entity, LocalTransformComponent) ?? getComponent(entity, TransformComponent) - const componentType = - typeof node != 'string' && getComponent(node, LocalTransformComponent) - ? LocalTransformComponent - : TransformComponent + const componentType = hasComponent(entity, LocalTransformComponent) ? LocalTransformComponent : TransformComponent if (overrideScale) { transformComponent.scale.copy(scale) @@ -534,76 +439,61 @@ const scaleObject = ( transformComponent.scale.z === 0 ? Number.EPSILON : transformComponent.scale.z ) - updateComponent(node as Entity, componentType, { scale: transformComponent.scale }) + updateComponent(entity as Entity, componentType, { scale: transformComponent.scale }) } } -const reparentObject = ( - nodes: EntityOrObjectUUID[], - before?: Entity | null, - parent?: Entity | null, - updateSelection = true -) => { +const reparentObject = (entities: Entity[], before?: Entity | null, parent?: Entity | null, updateSelection = true) => { parent = parent ?? SceneState.getRootEntity() cancelGrabOrPlacement() const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i] - if (typeof node !== 'string') { - if (node === parent) continue - - const currentParentEntity = getComponent(node, EntityTreeComponent).parentEntity! - const currentParentEntityUUID = getComponent(currentParentEntity, UUIDComponent) - const parentEntityTreeComponent = getComponent(currentParentEntity, EntityTreeComponent) - const currentIndex = parentEntityTreeComponent.children.indexOf(node) - - const newParentEntityTreeComponent = getComponent(parent, EntityTreeComponent) - const newIndex = before - ? newParentEntityTreeComponent.children.indexOf(before as Entity) - : newParentEntityTreeComponent.children.length - - const entityData = newSnapshot.data.scene.entities[getComponent(node, UUIDComponent)] - entityData.parent = getComponent(parent, UUIDComponent) - entityData.index = newIndex - - for (const [entityUUID, data] of Object.entries(newSnapshot.data.scene.entities)) { - if (typeof data.index !== 'number') continue - if (entityUUID === getComponent(node, UUIDComponent)) continue - - /** update indexes for old sibling entities */ - if (data.parent === currentParentEntityUUID) { - if (data.index > currentIndex) data.index-- - } + for (let i = 0; i < entities.length; i++) { + const entity = entities[i] + if (entity === parent) continue - /** update indexes for new sibling entities */ - if (newIndex) { - if (data.parent === getComponent(parent, UUIDComponent)) { - if (data.index >= newIndex) data.index++ - } - } + const currentParentEntity = getComponent(entity, EntityTreeComponent).parentEntity! + const currentParentEntityUUID = getComponent(currentParentEntity, UUIDComponent) + const parentEntityTreeComponent = getComponent(currentParentEntity, EntityTreeComponent) + const currentIndex = parentEntityTreeComponent.children.indexOf(entity) + + const newParentEntityTreeComponent = getComponent(parent, EntityTreeComponent) + const newIndex = before + ? newParentEntityTreeComponent.children.indexOf(before as Entity) + : newParentEntityTreeComponent.children.length + + const entityData = newSnapshot.data.scene.entities[getComponent(entity, UUIDComponent)] + entityData.parent = getComponent(parent, UUIDComponent) + entityData.index = newIndex + + for (const [entityUUID, data] of Object.entries(newSnapshot.data.scene.entities)) { + if (typeof data.index !== 'number') continue + if (entityUUID === getComponent(entity, UUIDComponent)) continue + + /** update indexes for old sibling entities */ + if (data.parent === currentParentEntityUUID) { + if (data.index > currentIndex) data.index-- } - } else { - const _parent = typeof parent === 'string' ? obj3dFromUuid(parent) : getComponent(parent, GroupComponent)[0] - const obj3d = obj3dFromUuid(node) - const oldWorldTransform = obj3d.parent?.matrixWorld ?? new Matrix4() - obj3d.removeFromParent() - _parent.add(obj3d) - obj3d.applyMatrix4(_parent.matrixWorld.clone().invert().multiply(oldWorldTransform)) + /** update indexes for new sibling entities */ + if (newIndex) { + if (data.parent === getComponent(parent, UUIDComponent)) { + if (data.index >= newIndex) data.index++ + } + } } } if (updateSelection) { - EditorControlFunctions.replaceSelection(nodes) + EditorControlFunctions.replaceSelection(entities) } dispatchAction(EditorHistoryAction.createSnapshot(newSnapshot)) } /** @todo - grouping currently doesnt take into account parentEntity or beforeEntity */ -const groupObjects = (nodes: EntityOrObjectUUID[]) => { +const groupObjects = (entities: Entity[]) => { cancelGrabOrPlacement() const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() @@ -636,23 +526,21 @@ const groupObjects = (nodes: EntityOrObjectUUID[]) => { let count = 0 - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i] - if (typeof node === 'string') continue - - const currentParentEntity = getComponent(node, EntityTreeComponent).parentEntity! + for (let i = 0; i < entities.length; i++) { + const entity = entities[i] + const currentParentEntity = getComponent(entity, EntityTreeComponent).parentEntity! const currentParentEntityUUID = getComponent(currentParentEntity, UUIDComponent) const parentEntityTreeComponent = getComponent(currentParentEntity, EntityTreeComponent) - const currentIndex = parentEntityTreeComponent.children.indexOf(node) + const currentIndex = parentEntityTreeComponent.children.indexOf(entity) - const entityData = newSnapshot.data.scene.entities[getComponent(node, UUIDComponent)] + const entityData = newSnapshot.data.scene.entities[getComponent(entity, UUIDComponent)] entityData.parent = groupEntityUUID entityData.index = count++ for (const [entityUUID, data] of Object.entries(newSnapshot.data.scene.entities)) { if (typeof data.index !== 'number') continue - if (entityUUID === getComponent(node, UUIDComponent)) continue + if (entityUUID === getComponent(entity, UUIDComponent)) continue /** update indexes for old sibling entities */ if (data.parent === currentParentEntityUUID) { @@ -666,7 +554,7 @@ const groupObjects = (nodes: EntityOrObjectUUID[]) => { dispatchAction(EditorHistoryAction.createSnapshot(newSnapshot)) } -const removeObject = (nodes: EntityOrObjectUUID[]) => { +const removeObject = (entities: Entity[]) => { cancelGrabOrPlacement() /** we have to manually set this here or it will cause react errors when entities are removed */ @@ -674,18 +562,12 @@ const removeObject = (nodes: EntityOrObjectUUID[]) => { const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() - const removedParentNodes = getEntityNodeArrayFromEntities(filterParentEntities(nodes, undefined, true, false)) - const scene = Engine.instance.scene + const removedParentNodes = filterParentEntities(entities, undefined, true, false) for (let i = 0; i < removedParentNodes.length; i++) { - const node = removedParentNodes[i] - if (typeof node === 'string') { - const obj = scene.getObjectByProperty('uuid', node) - obj?.removeFromParent() - } else { - const entityTreeComponent = getComponent(node, EntityTreeComponent) - if (!entityTreeComponent.parentEntity) continue - delete newSnapshot.data.scene.entities[getComponent(node, UUIDComponent)] - } + const entity = removedParentNodes[i] + const entityTreeComponent = getComponent(entity, EntityTreeComponent) + if (!entityTreeComponent.parentEntity) continue + delete newSnapshot.data.scene.entities[getComponent(entity, UUIDComponent)] } newSnapshot.selectedEntities = [] @@ -693,13 +575,13 @@ const removeObject = (nodes: EntityOrObjectUUID[]) => { dispatchAction(EditorHistoryAction.createSnapshot(newSnapshot)) } -const replaceSelection = (nodes: EntityOrObjectUUID[]) => { +const replaceSelection = (entities: Entity[]) => { const current = getMutableState(SelectionState).selectedEntities.value - if (nodes.length === current.length) { + if (entities.length === current.length) { let same = true - for (let i = 0; i < nodes.length; i++) { - if (!current.includes(nodes[i])) { + for (let i = 0; i < entities.length; i++) { + if (!current.includes(entities[i])) { same = false break } @@ -708,69 +590,59 @@ const replaceSelection = (nodes: EntityOrObjectUUID[]) => { } const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() - newSnapshot.selectedEntities = nodes - .map((node) => { - if (typeof node === 'string') return - return getComponent(node, UUIDComponent) - }) + newSnapshot.selectedEntities = entities + .map((node) => getComponent(node, UUIDComponent)) .filter(Boolean) as EntityUUID[] - SelectionState.updateSelection(nodes) + SelectionState.updateSelection(entities) // dispatchAction(EditorHistoryAction.createSnapshot(newSnapshot)) } -const toggleSelection = (nodes: EntityOrObjectUUID[]) => { +const toggleSelection = (entities: Entity[]) => { const selectedEntities = getMutableState(SelectionState).selectedEntities.value.slice(0) - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i] - const index = selectedEntities.indexOf(node) + for (let i = 0; i < entities.length; i++) { + const entity = entities[i] + const index = selectedEntities.indexOf(entity) if (index > -1) { selectedEntities.splice(index, 1) } else { - selectedEntities.push(node) + selectedEntities.push(entity) } } const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() newSnapshot.selectedEntities = selectedEntities - .map((node) => { - if (typeof node === 'string') return - return getComponent(node, UUIDComponent) - }) + .map((node) => getComponent(node, UUIDComponent)) .filter(Boolean) as EntityUUID[] - SelectionState.updateSelection(nodes) + SelectionState.updateSelection(entities) // dispatchAction(EditorHistoryAction.createSnapshot(newSnapshot)) } -const addToSelection = (nodes: EntityOrObjectUUID[]) => { +const addToSelection = (entities: Entity[]) => { const selectedEntities = getMutableState(SelectionState).selectedEntities.value.slice(0) - for (let i = 0; i < nodes.length; i++) { - const object = nodes[i] + for (let i = 0; i < entities.length; i++) { + const object = entities[i] if (selectedEntities.includes(object)) continue selectedEntities.push(object) } const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() newSnapshot.selectedEntities = selectedEntities - .map((node) => { - if (typeof node === 'string') return - return getComponent(node, UUIDComponent) - }) + .map((node) => getComponent(node, UUIDComponent)) .filter(Boolean) as EntityUUID[] - SelectionState.updateSelection(nodes) + SelectionState.updateSelection(entities) // dispatchAction(EditorHistoryAction.createSnapshot(newSnapshot)) } -const commitTransformSave = (nodes: EntityOrObjectUUID[]) => { +const commitTransformSave = (entities: Entity[]) => { const newSnapshot = EditorHistoryState.cloneCurrentSnapshot() - for (let i = 0; i < nodes.length; i++) { - const entity = nodes[i] - if (typeof entity === 'string') continue + for (let i = 0; i < entities.length; i++) { + const entity = entities[i] LocalTransformComponent.stateMap[entity]!.set(LocalTransformComponent.valueMap[entity]) const entityData = newSnapshot.data.scene.entities[getComponent(entity, UUIDComponent)] const component = entityData.components.find((c) => c.name === LocalTransformComponent.jsonID)! diff --git a/packages/editor/src/functions/cancelGrabOrPlacement.ts b/packages/editor/src/functions/cancelGrabOrPlacement.ts index fd952ddbd1..57cbfc0242 100644 --- a/packages/editor/src/functions/cancelGrabOrPlacement.ts +++ b/packages/editor/src/functions/cancelGrabOrPlacement.ts @@ -23,7 +23,6 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ -import { getEntityNodeArrayFromEntities } from '@etherealengine/engine/src/ecs/functions/EntityTree' import { TransformMode } from '@etherealengine/engine/src/scene/constants/transformConstants' import { getState } from '@etherealengine/hyperflux' @@ -39,6 +38,6 @@ export const cancelGrabOrPlacement = () => { // if (EditorHistory.grabCheckPoint) revertHistory(EditorHistory.grabCheckPoint) } else if (editorHelperState.transformMode === TransformMode.Placement) { setTransformMode(editorHelperState.transformModeOnCancel) - EditorControlFunctions.removeObject(getEntityNodeArrayFromEntities(getState(SelectionState).selectedEntities)) + EditorControlFunctions.removeObject(getState(SelectionState).selectedEntities) } } diff --git a/packages/editor/src/functions/filterParentEntities.ts b/packages/editor/src/functions/filterParentEntities.ts index 603622a835..7e3791da82 100644 --- a/packages/editor/src/functions/filterParentEntities.ts +++ b/packages/editor/src/functions/filterParentEntities.ts @@ -40,11 +40,11 @@ import { TransformComponent } from '@etherealengine/engine/src/transform/compone * @returns List of parent entities */ export const filterParentEntities = ( - entityList: (Entity | string)[], - parentEntityList: (Entity | string)[] = [], + entityList: Entity[], + parentEntityList: Entity[] = [], filterUnremovable = true, filterUntransformable = true -): (Entity | string)[] => { +): Entity[] => { parentEntityList.length = 0 // Recursively find the nodes in the tree with the lowest depth diff --git a/packages/editor/src/functions/getDetachedObjectsRoots.ts b/packages/editor/src/functions/getDetachedObjectsRoots.ts index 8cd694ad40..c8820c125b 100755 --- a/packages/editor/src/functions/getDetachedObjectsRoots.ts +++ b/packages/editor/src/functions/getDetachedObjectsRoots.ts @@ -23,25 +23,13 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ -import { Object3D } from 'three' - -import { SceneState } from '@etherealengine/engine/src/ecs/classes/Scene' -import { getOptionalComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' -import { - EntityOrObjectUUID, - EntityTreeComponent, - findIndexOfEntityNode -} from '@etherealengine/engine/src/ecs/functions/EntityTree' -import { Object3DWithEntity } from '@etherealengine/engine/src/scene/components/GroupComponent' -import obj3dFromUuid from '@etherealengine/engine/src/scene/util/obj3dFromUuid' +import { findIndexOfEntityNode } from '@etherealengine/engine/src/ecs/functions/EntityTree' +import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' import traverseEarlyOut from './traverseEarlyOut' // Returns an array of objects that are not ancestors of any other objects in the array. -export function getDetachedObjectsRoots( - objects: EntityOrObjectUUID[], - target: EntityOrObjectUUID[] = [] -): EntityOrObjectUUID[] { +export function getDetachedObjectsRoots(objects: Entity[], target: Entity[] = []): Entity[] { // Initially all objects are candidates for (let i = 0; i < objects.length; i++) target.push(objects[i]) @@ -69,46 +57,8 @@ export function getDetachedObjectsRoots( return target } -export const isAncestor = (parent: EntityOrObjectUUID, potentialChild: EntityOrObjectUUID): boolean => { +export const isAncestor = (parent: Entity, potentialChild: Entity): boolean => { if (!potentialChild) return false if (parent === potentialChild) return false - let parentNode: EntityOrObjectUUID - let childNode: EntityOrObjectUUID - if (typeof parent === 'string' && typeof potentialChild === 'string') { - parentNode = getEntityNode(parent) - childNode = getEntityNode(potentialChild) - if (parentNode !== childNode) return isAncestor(parentNode, childNode) - //iterate to root for child node, checking for parent - let walker: Object3D | null = obj3dFromUuid(potentialChild) - let target = obj3dFromUuid(parent) - while (walker) { - if (walker === target) return true - walker = walker.parent - } - return false - } else if (typeof parent === 'string') { - //child is implicitly an EntityTree entity - parentNode = getEntityNode(parent) - return isAncestor(parentNode, potentialChild) - } else if (typeof potentialChild === 'string') { - //parent is implicitly an EntityTree entity - childNode = getEntityNode(potentialChild) - return isAncestor(parent, childNode) - } - if (parent === potentialChild) return false return traverseEarlyOut(parent, (child) => child === potentialChild) } - -const getEntityNode = (uuid: string) => { - const sceneEntity = SceneState.getRootEntity() - let obj3d = obj3dFromUuid(uuid) as Object3DWithEntity - while (obj3d) { - if ( - obj3d.entity !== undefined && - getOptionalComponent(obj3d.entity, EntityTreeComponent)?.rootEntity === sceneEntity - ) - return obj3d.entity - obj3d = obj3d.parent as Object3DWithEntity - } - throw new Error('no Entity Node found') -} diff --git a/packages/editor/src/functions/getIntersectingNode.ts b/packages/editor/src/functions/getIntersectingNode.ts index b7feadface..bbe6fb1bf7 100755 --- a/packages/editor/src/functions/getIntersectingNode.ts +++ b/packages/editor/src/functions/getIntersectingNode.ts @@ -28,7 +28,6 @@ import { Camera, Intersection, Object3D, Raycaster, Vector2 } from 'three' import { Engine } from '@etherealengine/engine/src/ecs/classes/Engine' import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' import { getComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' -import { EntityOrObjectUUID, getEntityNodeArrayFromEntities } from '@etherealengine/engine/src/ecs/functions/EntityTree' import { GroupComponent, Object3DWithEntity } from '@etherealengine/engine/src/scene/components/GroupComponent' import { ObjectLayers } from '@etherealengine/engine/src/scene/constants/ObjectLayers' import { getState } from '@etherealengine/hyperflux' @@ -38,7 +37,7 @@ import { SelectionState } from '../services/SelectionServices' type RaycastIntersectionNode = Intersection & { obj3d: Object3DWithEntity - node?: EntityOrObjectUUID + node?: Entity } function getParentEntity(obj: Object3DWithEntity): Object3DWithEntity { @@ -61,14 +60,14 @@ export function getIntersectingNode(results: Intersection[]) const parentNode = getParentEntity(obj) if (!parentNode) continue //skip obj3ds that are not children of EntityNodes if (!obj.entity && parentNode && !selected.has(parentNode.entity)) { - result.node = getEntityNodeArrayFromEntities([parentNode.entity])[0] + result.node = parentNode.entity result.obj3d = getComponent(parentNode.entity, GroupComponent)[0] as Object3DWithEntity return result } if (obj && (obj as Object3D) !== Engine.instance.scene) { result.obj3d = obj - result.node = obj.entity ?? obj.uuid + result.node = obj.entity //if(result.node && hasComponent(result.node.entity, GroupComponent)) //result.obj3d = result.object //result.node = result.object.uuid diff --git a/packages/editor/src/functions/traverseEarlyOut.ts b/packages/editor/src/functions/traverseEarlyOut.ts index a10aa4e415..2168e9c937 100755 --- a/packages/editor/src/functions/traverseEarlyOut.ts +++ b/packages/editor/src/functions/traverseEarlyOut.ts @@ -25,14 +25,14 @@ Ethereal Engine. All Rights Reserved. import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' import { getComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' -import { EntityOrObjectUUID, EntityTreeComponent } from '@etherealengine/engine/src/ecs/functions/EntityTree' +import { EntityTreeComponent } from '@etherealengine/engine/src/ecs/functions/EntityTree' -export default function traverseEarlyOut(node: EntityOrObjectUUID, cb: (node: EntityOrObjectUUID) => boolean): boolean { - let stopTravel = cb(node) +export default function traverseEarlyOut(entity: Entity, cb: (entity: Entity) => boolean): boolean { + let stopTravel = cb(entity) if (stopTravel) return stopTravel - const entityTreeComponent = getComponent(node as Entity, EntityTreeComponent) + const entityTreeComponent = getComponent(entity as Entity, EntityTreeComponent) const children = entityTreeComponent.children if (!children) return stopTravel diff --git a/packages/editor/src/services/EditorServices.ts b/packages/editor/src/services/EditorServices.ts index 07cc3af63b..889f433bb1 100644 --- a/packages/editor/src/services/EditorServices.ts +++ b/packages/editor/src/services/EditorServices.ts @@ -32,7 +32,6 @@ export const EditorState = defineState({ projectName: null as string | null, sceneName: null as string | null, sceneModified: false, - showObject3DInHierarchy: false, lockPropertiesPanel: '' as EntityUUID }) }) diff --git a/packages/editor/src/services/SelectionServices.ts b/packages/editor/src/services/SelectionServices.ts index cd659f53f9..b62a6e0fb8 100644 --- a/packages/editor/src/services/SelectionServices.ts +++ b/packages/editor/src/services/SelectionServices.ts @@ -25,11 +25,11 @@ Ethereal Engine. All Rights Reserved. import { removeComponent, setComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' import { entityExists } from '@etherealengine/engine/src/ecs/functions/EntityFunctions' -import { EntityOrObjectUUID } from '@etherealengine/engine/src/ecs/functions/EntityTree' import { defineSystem } from '@etherealengine/engine/src/ecs/functions/SystemFunctions' import { SelectTagComponent } from '@etherealengine/engine/src/scene/components/SelectTagComponent' import { defineState, getMutableState, useHookstate } from '@etherealengine/hyperflux' +import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity' import { useEffect } from 'react' import { cancelGrabOrPlacement } from '../functions/cancelGrabOrPlacement' import { filterParentEntities } from '../functions/filterParentEntities' @@ -37,10 +37,10 @@ import { filterParentEntities } from '../functions/filterParentEntities' export const SelectionState = defineState({ name: 'SelectionState', initial: { - selectedEntities: [] as EntityOrObjectUUID[], - selectedParentEntities: [] as EntityOrObjectUUID[] + selectedEntities: [] as Entity[], + selectedParentEntities: [] as Entity[] }, - updateSelection: (selectedEntities: EntityOrObjectUUID[]) => { + updateSelection: (selectedEntities: Entity[]) => { getMutableState(SelectionState).merge({ selectedEntities: selectedEntities, selectedParentEntities: filterParentEntities(selectedEntities) @@ -55,13 +55,13 @@ const reactor = () => { cancelGrabOrPlacement() const entities = [...selectedEntities.value] for (const entity of entities) { - if (typeof entity !== 'number' || !entityExists(entity)) continue + if (!entityExists(entity)) continue setComponent(entity, SelectTagComponent) } return () => { for (const entity of entities) { - if (typeof entity !== 'number' || !entityExists(entity)) continue + if (!entityExists(entity)) continue removeComponent(entity, SelectTagComponent) } } diff --git a/packages/editor/src/systems/EditorControlSystem.tsx b/packages/editor/src/systems/EditorControlSystem.tsx index b4898a655c..7c190f14e9 100644 --- a/packages/editor/src/systems/EditorControlSystem.tsx +++ b/packages/editor/src/systems/EditorControlSystem.tsx @@ -52,10 +52,7 @@ import { useQuery } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions' import { createEntity } from '@etherealengine/engine/src/ecs/functions/EntityFunctions' -import { - EntityTreeComponent, - getEntityNodeArrayFromEntities -} from '@etherealengine/engine/src/ecs/functions/EntityTree' +import { EntityTreeComponent } from '@etherealengine/engine/src/ecs/functions/EntityTree' import { defineSystem } from '@etherealengine/engine/src/ecs/functions/SystemFunctions' import { InputComponent } from '@etherealengine/engine/src/input/components/InputComponent' import { InputSourceComponent } from '@etherealengine/engine/src/input/components/InputSourceComponent' @@ -145,9 +142,9 @@ const isMacOS = /Mac|iPod|iPhone|iPad/.test(navigator.platform) let lastZoom = 0 let prevRotationAngle = 0 -let selectedEntities: (Entity | string)[] -let selectedParentEntities: (Entity | string)[] -let lastSelectedEntities = [] as (Entity | string)[] +let selectedEntities: Entity[] +let selectedParentEntities: Entity[] +let lastSelectedEntities = [] as Entity[] // let gizmoObj: TransformGizmo let transformMode: TransformModeType let transformPivot: TransformPivotType @@ -158,7 +155,7 @@ let transformSpaceChanged = false let dragging = false const onKeyQ = () => { - const nodes = getEntityNodeArrayFromEntities(getState(SelectionState).selectedEntities) + const nodes = getState(SelectionState).selectedEntities const gizmoTransform = getComponent(gizmoEntity, TransformComponent) const editorHelperState = getState(EditorHelperState) EditorControlFunctions.rotateAround( @@ -170,7 +167,7 @@ const onKeyQ = () => { } const onKeyE = () => { - const nodes = getEntityNodeArrayFromEntities(getState(SelectionState).selectedEntities) + const nodes = getState(SelectionState).selectedEntities const gizmoTransform = getComponent(gizmoEntity, TransformComponent) const editorHelperState = getState(EditorHelperState) EditorControlFunctions.rotateAround( @@ -198,7 +195,7 @@ const onEscape = () => { const onKeyF = () => { const editorCameraState = getMutableState(EditorCameraState) - editorCameraState.focusedObjects.set(getEntityNodeArrayFromEntities(selectedEntities)) + editorCameraState.focusedObjects.set(selectedEntities) editorCameraState.refocus.set(true) } @@ -246,7 +243,7 @@ const onMinus = () => { } const onDelete = () => { - EditorControlFunctions.removeObject(getEntityNodeArrayFromEntities(getState(SelectionState).selectedEntities)) + EditorControlFunctions.removeObject(getState(SelectionState).selectedEntities) } function copy(event) { @@ -318,13 +315,8 @@ const getRaycastPosition = (coords: Vector2, target: Vector3, snapAmount = 0): v const excludeObjects = [] as Object3D[] const selectionState = getState(SelectionState) for (const e of selectionState.selectedParentEntities) { - if (typeof e === 'string') { - const obj = scene.getObjectByProperty('uuid', e) - if (obj) excludeObjects.push(obj) - } else { - const group = getComponent(e, GroupComponent) - if (group) excludeObjects.push(...group) - } + const group = getComponent(e, GroupComponent) + if (group) excludeObjects.push(...group) } findIntersectObjects(Engine.instance.scene, excludeObjects, raycastIgnoreLayers) @@ -384,11 +376,8 @@ const execute = () => { if (hasComponent(gizmoEntity, VisibleComponent)) removeComponent(gizmoEntity, VisibleComponent) } else { const lastSelection = selectedEntities[selectedEntities.length - 1] - const isUuid = typeof lastSelection === 'string' - const lastSelectedTransform = isUuid - ? Engine.instance.scene.getObjectByProperty('uuid', lastSelection) - : getOptionalComponent(lastSelection as Entity, TransformComponent) + const lastSelectedTransform = getOptionalComponent(lastSelection as Entity, TransformComponent) if (lastSelectedTransform) { const isChanged = !compareArrays(lastSelectedEntities, selectionState.selectedEntities) || transformModeChanged @@ -403,12 +392,7 @@ const execute = () => { for (let i = 0; i < selectedParentEntities.length; i++) { const parentEnt = selectedParentEntities[i] - const isUuid = typeof parentEnt === 'string' - if (isUuid) { - box.expandByObject(Engine.instance.scene.getObjectByProperty('uuid', parentEnt)!) - } else { - box.expandByPoint(getComponent(parentEnt, TransformComponent).position) - } + box.expandByPoint(getComponent(parentEnt, TransformComponent).position) } box.getCenter(gizmoObj.position) if (transformPivot === TransformPivot.Bottom) { @@ -430,10 +414,7 @@ const execute = () => { const localTransform = getComponent(lastSelection as Entity, LocalTransformComponent) if (localTransform) gizmoObj.quaternion.copy(localTransform.rotation) } else { - if (lastSelectedTransform) - gizmoObj.quaternion.copy( - 'quaternion' in lastSelectedTransform ? lastSelectedTransform.quaternion : lastSelectedTransform.rotation - ) + if (lastSelectedTransform) gizmoObj.quaternion.copy(lastSelectedTransform.rotation) } inverseGizmoQuaternion.copy(gizmoObj.quaternion).invert() @@ -538,9 +519,6 @@ const execute = () => { ) } - const nodes = getEntityNodeArrayFromEntities(getState(SelectionState).selectedEntities) - EditorControlFunctions.positionObject(nodes, [translationVector], transformSpace, true) - // if (isGrabbing && transformMode === TransformMode.Grab) { // EditorHistory.grabCheckPoint = (selectedEntities?.find((ent) => typeof ent !== 'string') ?? 0) as Entity // } @@ -563,7 +541,7 @@ const execute = () => { const relativeRotationAngle = rotationAngle - prevRotationAngle prevRotationAngle = rotationAngle - const nodes = getEntityNodeArrayFromEntities(getState(SelectionState).selectedEntities) + const nodes = getState(SelectionState).selectedEntities EditorControlFunctions.rotateAround(nodes, planeNormal, relativeRotationAngle, gizmoObj.position) const selectedAxisInfo = gizmoObj.selectedAxisObj?.axisInfo! @@ -637,7 +615,7 @@ const execute = () => { scaleVector.copy(curScale).divide(prevScale) prevScale.copy(curScale) - const nodes = getEntityNodeArrayFromEntities(getState(SelectionState).selectedEntities) + const nodes = getState(SelectionState).selectedEntities EditorControlFunctions.scaleObject(nodes, [scaleVector], transformSpace) } } @@ -658,7 +636,7 @@ const execute = () => { if (selectStartPosition.distanceTo(cursorPosition) < SELECT_SENSITIVITY) { const result = getIntersectingNodeOnScreen(raycaster, cursorPosition) if (result) { - if (result.node && (typeof result.node === 'string' || hasComponent(result.node, SceneObjectComponent))) { + if (result.node && hasComponent(result.node, SceneObjectComponent)) { if (shift) { EditorControlFunctions.toggleSelection([result.node]) } else { @@ -675,7 +653,7 @@ const execute = () => { if (dragging) { // commit transform changes upon releasing the gizmo - const nodes = getEntityNodeArrayFromEntities(getState(SelectionState).selectedEntities) + const nodes = getState(SelectionState).selectedEntities EditorControlFunctions.commitTransformSave(nodes) } } diff --git a/packages/engine/src/assets/classes/XRELoader.ts b/packages/engine/src/assets/classes/XRELoader.ts index c1cbd4a5b5..605fc7b1a8 100644 --- a/packages/engine/src/assets/classes/XRELoader.ts +++ b/packages/engine/src/assets/classes/XRELoader.ts @@ -29,10 +29,9 @@ import { gltfToSceneJson } from '@etherealengine/engine/src/scene/functions/GLTF import { loadECSData } from '@etherealengine/engine/src/scene/systems/SceneLoadingSystem' import { Entity } from '../../ecs/classes/Entity' -import { EntityOrObjectUUID } from '../../ecs/functions/EntityTree' import { AssetLoader } from './AssetLoader' -export type OnLoadType = (response: EntityOrObjectUUID[]) => EntityOrObjectUUID[] | void +export type OnLoadType = (response: Entity[]) => Entity[] | void export class XRELoader { fileLoader: FileLoader diff --git a/packages/engine/src/ecs/functions/EntityTree.test.ts b/packages/engine/src/ecs/functions/EntityTree.test.ts index 8cafdeb35c..f225ef6ac5 100644 --- a/packages/engine/src/ecs/functions/EntityTree.test.ts +++ b/packages/engine/src/ecs/functions/EntityTree.test.ts @@ -43,7 +43,6 @@ import { EntityTreeComponent, destroyEntityTree, findIndexOfEntityNode, - getEntityNodeArrayFromEntities, iterateEntityNode, removeFromEntityTree, traverseEntityNode, @@ -352,37 +351,6 @@ describe('EntityTreeFunctions', () => { }) }) - describe('getEntityNodeArrayFromEntities function', () => { - it('will return entity node array from passed entities', () => { - const nodes = [] as Entity[] - for (let i = 0; i < 4; i++) { - nodes[i] = createEntity() - setComponent(nodes[i], EntityTreeComponent, { parentEntity: root }) - } - - const entities = [nodes[0], nodes[2]] - - const retrivedNodes = getEntityNodeArrayFromEntities(entities) as Entity[] - - retrivedNodes.forEach((node) => assert(entities.includes(node))) - }) - - it('will remove entity for which there is no node', () => { - const nodes = [] as Entity[] - for (let i = 0; i < 4; i++) { - nodes[i] = createEntity() - setComponent(nodes[i], EntityTreeComponent, { parentEntity: root }) - } - - const fakeEntity = createEntity() - const entities = [nodes[0], nodes[2], fakeEntity] - - const retrivedNodes = getEntityNodeArrayFromEntities(entities) - - retrivedNodes.forEach((node) => assert.notEqual(node, fakeEntity)) - }) - }) - describe('findIndexOfEntityNode function', () => { it('will return index of passed entity node', () => { const testNode = createEntity() @@ -391,13 +359,7 @@ describe('EntityTreeFunctions', () => { setComponent(testNode, EntityTreeComponent, { parentEntity: root }) setComponent(createEntity(), EntityTreeComponent, { parentEntity: root }) - assert.equal( - findIndexOfEntityNode( - getEntityNodeArrayFromEntities(getComponent(root, EntityTreeComponent).children), - testNode - ), - 2 - ) + assert.equal(findIndexOfEntityNode(getComponent(root, EntityTreeComponent).children, testNode), 2) }) it('will return -1 if it can not find the index of the passed node', () => { @@ -406,13 +368,7 @@ describe('EntityTreeFunctions', () => { setComponent(createEntity(), EntityTreeComponent, { parentEntity: root }) setComponent(createEntity(), EntityTreeComponent, { parentEntity: root }) - assert.equal( - findIndexOfEntityNode( - getEntityNodeArrayFromEntities(getComponent(root, EntityTreeComponent).children), - testNode - ), - -1 - ) + assert.equal(findIndexOfEntityNode(getComponent(root, EntityTreeComponent).children, testNode), -1) }) }) }) diff --git a/packages/engine/src/ecs/functions/EntityTree.ts b/packages/engine/src/ecs/functions/EntityTree.ts index 1b26974df7..9b118573ff 100644 --- a/packages/engine/src/ecs/functions/EntityTree.ts +++ b/packages/engine/src/ecs/functions/EntityTree.ts @@ -139,8 +139,6 @@ export const EntityTreeComponent = defineComponent({ roots: hookstate({} as Record) }) -export type EntityOrObjectUUID = Entity | string - /** * Recursively destroys all the children entities of the passed entity */ @@ -268,25 +266,6 @@ export function traverseEntityNodeParent(entity: Entity, cb: (parent: Entity) => } } -/** - * Creates Entity Tree Node array from passed Entity array - * @param entities Entity Array to get Entity node from - * @param tree Entity Tree object - * @returns Entity Tree node array obtained from passed Entities. - */ -export function getEntityNodeArrayFromEntities(entities: EntityOrObjectUUID[]) { - const arr = [] as EntityOrObjectUUID[] - const scene = Engine.instance.scene - for (const entity of entities) { - if (typeof entity === 'string') { - scene.getObjectByProperty('uuid', entity) && arr.push(entity) - continue - } - if (hasComponent(entity, EntityTreeComponent)) arr.push(entity) - } - return arr -} - /** * Finds the index of an entity tree node using entity. * This function is useful for node which is not contained in array but can have same entity as one of array elements @@ -294,12 +273,10 @@ export function getEntityNodeArrayFromEntities(entities: EntityOrObjectUUID[]) { * @param node Node to find index of * @returns index of the node if found -1 oterhwise. */ -export function findIndexOfEntityNode(arr: EntityOrObjectUUID[], obj: EntityOrObjectUUID): number { +export function findIndexOfEntityNode(arr: Entity[], obj: Entity): number { for (let i = 0; i < arr.length; i++) { const elt = arr[i] - if (typeof elt !== typeof obj) continue - if (typeof obj === 'string' || (typeof obj === 'number' && obj === elt)) return i + if (obj === elt) return i } - return -1 }