From a0422c325945531c8179f6f6f05cd1e0a731b964 Mon Sep 17 00:00:00 2001 From: Jiatong Yao Date: Mon, 8 Apr 2024 11:07:57 -0500 Subject: [PATCH 1/4] add prefab ui and popup list --- packages/client-core/i18n/en/editor.json | 2 + .../hierarchy/HierarchyPanelContainer.tsx | 36 ++- .../src/components/hierarchy/PrefabList.tsx | 271 ++++++++++++++++++ 3 files changed, 306 insertions(+), 3 deletions(-) create mode 100644 packages/editor/src/components/hierarchy/PrefabList.tsx diff --git a/packages/client-core/i18n/en/editor.json b/packages/client-core/i18n/en/editor.json index 23909eb1693..a44b533cc89 100755 --- a/packages/client-core/i18n/en/editor.json +++ b/packages/client-core/i18n/en/editor.json @@ -1000,6 +1000,8 @@ "copyURL": "Copy URL", "openInNewTab": "Open URL in New Tab", "deleteAsset": "Delete Asset", + "prefab": "Prefab", + "prefab-search" : "Search Prefabs ...", "components": "Components", "components-search": "Search Components ...", "component-detail": { diff --git a/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx b/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx index 1a0df8491b5..660c5fb038b 100644 --- a/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx +++ b/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx @@ -38,7 +38,7 @@ import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' import { EntityTreeComponent, traverseEntityNode } from '@etherealengine/spatial/src/transform/components/EntityTree' import MenuItem from '@mui/material/MenuItem' -import { PopoverPosition } from '@mui/material/Popover' +import Popover, { PopoverPosition } from '@mui/material/Popover' import { NotificationService } from '@etherealengine/client-core/src/common/services/NotificationService' import { Engine, EntityUUID, UUIDComponent } from '@etherealengine/ecs' @@ -56,10 +56,12 @@ import { EditorState } from '../../services/EditorServices' import { SelectionState } from '../../services/SelectionServices' import Search from '../Search/Search' import useUpload from '../assets/useUpload' +import { PopoverContext } from '../element/PopoverContext' import { PropertiesPanelButton } from '../inputs/Button' import { ContextMenu } from '../layout/ContextMenu' import { HeirarchyTreeNodeType, heirarchyTreeWalker } from './HeirarchyTreeWalker' import { HierarchyTreeNode, HierarchyTreeNodeProps, RenameNodeData, getNodeElId } from './HierarchyTreeNode' +import PrefabList from './PrefabList' import styles from './styles.module.scss' /** @@ -463,7 +465,8 @@ function HierarchyPanelContents({ rootEntityUUID }: { rootEntityUUID: EntityUUID {MemoTreeNode} ) - + const anchorElement = useHookstate(null) + const open = !!anchorElement.value return ( <>
@@ -473,6 +476,7 @@ function HierarchyPanelContents({ rootEntityUUID }: { rootEntityUUID: EntityUUID
{HierarchyList}
+ EditorControlFunctions.createObjectFromSceneElement()} + onClick={(event) => { + anchorElement.set(event.currentTarget) + }} > {t('editor:hierarchy.lbl-addEntity')}
+ { + anchorElement.set(null) + } + }} + > + anchorElement.set(null)} + anchorOrigin={{ + vertical: 'center', + horizontal: 'left' + }} + transformOrigin={{ + vertical: 'center', + horizontal: 'right' + }} + > + + + onRenameNode(contextSelectedItem!)}>{t('editor:hierarchy.lbl-rename')} { + return { + Audio: ['Audio'], + Image: ['Image'], + Ground: ['Ground'], + Shape: ['Shape'], + Spawn: ['Spawn'], + Collider: ['Collider'], + Light: ['Spot Light', 'Ambient Light', 'Point Light', 'Directional Light', 'Hemisphere Light'], + + '3D Text': ['3D Text'], + Model: ['Model'], + Video: ['Video'], + Volumetric: ['Volumetric'], + Chair: ['Chair'], + Link: ['Link'], + Particles: ['Particles'], + Settings: ['Settings'], + 'Preview Camera': ['Preview Camera'], + Spline: ['Spline'], + 'Effect Volume': ['SDF'] + } as Record + } +}) + +const PrefabListItem = ({ item }: { item: string }) => { + const { t } = useTranslation() + const Icon = getState(ComponentEditorsState)[item]?.iconComponent ?? PlaceHolderIcon + const handleClosePopover = usePopoverContextClose() + return ( + { + let componentJsons: ComponentJsonType[] = [] + PrefabShelfCategories[item].forEach((component) => { + componentJsons.push({ name: component.jsonID }) + }) + // EditorControlFunctions.createObjectFromSceneElement([ + // { name: PrefabShelfCategories[item][0].jsonID } + + // ]) + EditorControlFunctions.createObjectFromSceneElement(componentJsons) + handleClosePopover() + }} + > + + + + + {startCase(item.replace('-', ' ').toLowerCase())} + + } + secondary={ + + {t(`editor:layout.assetGrid.component-detail.${item}`)} + + } + /> + + ) +} +const ScenePrefabListItem = ({ + categoryTitle, + categoryItems, + isCollapsed +}: { + categoryTitle: string + categoryItems: string[] + isCollapsed: boolean +}) => { + const open = useHookstate(categoryTitle === 'Misc') + return ( + <> + open.set((prev) => !prev)} + style={{ + backgroundColor: 'var(--dockBackground)', + cursor: 'pointer', + color: 'var(--textColor)', + display: 'flex', + justifyContent: 'space-between', + width: '100%' + }} + > + {categoryTitle} + + + + + {categoryItems.map((item) => ( + + ))} + + + + ) +} + +const usePrefabNameShelfCategories = (search: string) => { + useHookstate(getMutableState(PrefabNameShelfCategoriesState)).value + + if (!search) { + return Object.entries(getState(PrefabNameShelfCategoriesState)) + } + + const searchRegExp = new RegExp(search, 'gi') + + return Object.entries(getState(PrefabNameShelfCategoriesState)) + .map(([category, items]) => { + const filteredItems = items.filter((item) => item.match(searchRegExp)?.length) + return [category, filteredItems] as [string, string[]] + }) + .filter(([_, items]) => !!items.length) +} + +export function PrefabList() { + const { t } = useTranslation() + const search = useHookstate({ local: '', query: '' }) + const searchTimeout = useRef | null>(null) + const shelves = usePrefabNameShelfCategories(search.query.value) + + const onSearch = (text: string) => { + search.local.set(text) + if (searchTimeout.current) clearTimeout(searchTimeout.current) + searchTimeout.current = setTimeout(() => { + search.query.set(text) + }, 50) + } + + return ( + + + {t('editor:layout.assetGrid.prefab')} + + onSearch(e.target.value)} + /> + + } + > + {shelves.map(([category, items]) => ( + + ))} + + ) +} + +export default PrefabList From 8bcfd8e3bdc856f205c2eae7e50c54badb9c495b Mon Sep 17 00:00:00 2001 From: Jiatong Yao Date: Mon, 15 Apr 2024 16:48:51 -0500 Subject: [PATCH 2/4] update prefab list and add studio mode toggle --- packages/client-core/i18n/en/editor.json | 6 + .../hierarchy/HierarchyPanelContainer.tsx | 6 +- .../components/hierarchy/PrefabEditors.tsx | 61 ++++++ .../src/components/hierarchy/PrefabList.tsx | 176 +++--------------- .../editor/src/components/toolbar/ToolBar.tsx | 2 + .../toolbar/tools/StudioModeTool.tsx | 86 +++++++++ .../editor/src/services/EditorHelperState.ts | 3 + .../src/scene/constants/transformConstants.ts | 5 + 8 files changed, 195 insertions(+), 150 deletions(-) create mode 100644 packages/editor/src/components/hierarchy/PrefabEditors.tsx create mode 100644 packages/editor/src/components/toolbar/tools/StudioModeTool.tsx diff --git a/packages/client-core/i18n/en/editor.json b/packages/client-core/i18n/en/editor.json index a44b533cc89..51bbc2beb4a 100755 --- a/packages/client-core/i18n/en/editor.json +++ b/packages/client-core/i18n/en/editor.json @@ -177,6 +177,12 @@ "lbl-nodeHelpers": "Toggle Node Helpers", "info-nodeHelpers": "Helper geometry that helps components have visibility in the scene when inactive." }, + "studioModeToggle":{ + "lbl-simple": "Simple", + "info-simple": "simple mode", + "lbl-advanced": "Advanced", + "info-advanced": "advanced mode" + }, "sceneScreenshot": { "lbl": "Screenshot", "info": "Takes a screenshot of your scene at the current view." diff --git a/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx b/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx index 660c5fb038b..fa538965929 100644 --- a/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx +++ b/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx @@ -52,6 +52,7 @@ import { EditorControlFunctions } from '../../functions/EditorControlFunctions' import { addMediaNode } from '../../functions/addMediaNode' import { isAncestor } from '../../functions/getDetachedObjectsRoots' import { cmdOrCtrlString } from '../../functions/utils' +import { EditorHelperState } from '../../services/EditorHelperState' import { EditorState } from '../../services/EditorServices' import { SelectionState } from '../../services/SelectionServices' import Search from '../Search/Search' @@ -489,6 +490,9 @@ function HierarchyPanelContents({ rootEntityUUID }: { rootEntityUUID: EntityUUID }} onClick={(event) => { anchorElement.set(event.currentTarget) + if (getMutableState(EditorHelperState).studioMode.value == 'Advanced') { + EditorControlFunctions.createObjectFromSceneElement() + } }} > {t('editor:hierarchy.lbl-addEntity')} @@ -503,7 +507,7 @@ function HierarchyPanelContents({ rootEntityUUID }: { rootEntityUUID: EntityUUID > anchorElement.set(null)} anchorOrigin={{ diff --git a/packages/editor/src/components/hierarchy/PrefabEditors.tsx b/packages/editor/src/components/hierarchy/PrefabEditors.tsx new file mode 100644 index 00000000000..5277177b1f5 --- /dev/null +++ b/packages/editor/src/components/hierarchy/PrefabEditors.tsx @@ -0,0 +1,61 @@ +/* +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 { Component } from '@etherealengine/ecs/src/ComponentFunctions' +import { LoopAnimationComponent } from '@etherealengine/engine/src/avatar/components/LoopAnimationComponent' +import { EnvmapComponent } from '@etherealengine/engine/src/scene/components/EnvmapComponent' +import { ModelComponent } from '@etherealengine/engine/src/scene/components/ModelComponent' +import { PostProcessingComponent } from '@etherealengine/engine/src/scene/components/PostProcessingComponent' +import { ShadowComponent } from '@etherealengine/engine/src/scene/components/ShadowComponent' +import { SkyboxComponent } from '@etherealengine/engine/src/scene/components/SkyboxComponent' +import { VariantComponent } from '@etherealengine/engine/src/scene/components/VariantComponent' +import { defineState } from '@etherealengine/hyperflux' +import { ColliderComponent } from '@etherealengine/spatial/src/physics/components/ColliderComponent' +import { RigidBodyComponent } from '@etherealengine/spatial/src/physics/components/RigidBodyComponent' +import { TriggerComponent } from '@etherealengine/spatial/src/physics/components/TriggerComponent' +import { AmbientLightComponent } from '@etherealengine/spatial/src/renderer/components/AmbientLightComponent' +import { DirectionalLightComponent } from '@etherealengine/spatial/src/renderer/components/DirectionalLightComponent' +import { FogSettingsComponent } from '@etherealengine/spatial/src/renderer/components/FogSettingsComponent' +import { HemisphereLightComponent } from '@etherealengine/spatial/src/renderer/components/HemisphereLightComponent' +import { PointLightComponent } from '@etherealengine/spatial/src/renderer/components/PointLightComponent' +import { SpotLightComponent } from '@etherealengine/spatial/src/renderer/components/SpotLightComponent' + +export const PrefabShelfCategories = defineState({ + name: 'ee.editor.PrefabShelfCategories', + initial: () => { + return { + 'Light Prefab': [ + AmbientLightComponent, + DirectionalLightComponent, + HemisphereLightComponent, + PointLightComponent, + SpotLightComponent + ], + 'Physics Prefab': [ColliderComponent, RigidBodyComponent, TriggerComponent], + 'Model Prefab': [ModelComponent, LoopAnimationComponent, VariantComponent, EnvmapComponent, ShadowComponent], + 'Setting Prefab': [SkyboxComponent, FogSettingsComponent, PostProcessingComponent, FogSettingsComponent] + //will continue to add more prefabs + } as Record + } +}) diff --git a/packages/editor/src/components/hierarchy/PrefabList.tsx b/packages/editor/src/components/hierarchy/PrefabList.tsx index c6a4b8c8702..1f929f10e6c 100644 --- a/packages/editor/src/components/hierarchy/PrefabList.tsx +++ b/packages/editor/src/components/hierarchy/PrefabList.tsx @@ -27,128 +27,34 @@ import { startCase } from 'lodash' import React, { useRef } from 'react' import { useTranslation } from 'react-i18next' -import { PositionalAudioComponent } from '@etherealengine/engine/src/audio/components/PositionalAudioComponent' -import { GroundPlaneComponent } from '@etherealengine/engine/src/scene/components/GroundPlaneComponent' -import { ImageComponent } from '@etherealengine/engine/src/scene/components/ImageComponent' -import { ModelComponent } from '@etherealengine/engine/src/scene/components/ModelComponent' -import { ParticleSystemComponent } from '@etherealengine/engine/src/scene/components/ParticleSystemComponent' -import { SDFComponent } from '@etherealengine/engine/src/scene/components/SDFComponent' -import { ScenePreviewCameraComponent } from '@etherealengine/engine/src/scene/components/ScenePreviewCamera' -import { SkyboxComponent } from '@etherealengine/engine/src/scene/components/SkyboxComponent' -import { SpawnPointComponent } from '@etherealengine/engine/src/scene/components/SpawnPointComponent' -import { SplineComponent } from '@etherealengine/engine/src/scene/components/SplineComponent' -import { SplineTrackComponent } from '@etherealengine/engine/src/scene/components/SplineTrackComponent' -import { VariantComponent } from '@etherealengine/engine/src/scene/components/VariantComponent' -import { VideoComponent } from '@etherealengine/engine/src/scene/components/VideoComponent' -import { VolumetricComponent } from '@etherealengine/engine/src/scene/components/VolumetricComponent' -import { defineState, getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' -import { ColliderComponent } from '@etherealengine/spatial/src/physics/components/ColliderComponent' -import { AmbientLightComponent } from '@etherealengine/spatial/src/renderer/components/AmbientLightComponent' -import { DirectionalLightComponent } from '@etherealengine/spatial/src/renderer/components/DirectionalLightComponent' -import { HemisphereLightComponent } from '@etherealengine/spatial/src/renderer/components/HemisphereLightComponent' -import { PointLightComponent } from '@etherealengine/spatial/src/renderer/components/PointLightComponent' -import { SpotLightComponent } from '@etherealengine/spatial/src/renderer/components/SpotLightComponent' +import { getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' import PlaceHolderIcon from '@mui/icons-material/GroupAddOutlined' -import { Collapse, List, ListItemButton, ListItemIcon, ListItemText } from '@mui/material' +import { List, ListItemButton, ListItemIcon, ListItemText } from '@mui/material' import InputText from '@etherealengine/client-core/src/common/components/InputText' -import { LoopAnimationComponent } from '@etherealengine/engine/src/avatar/components/LoopAnimationComponent' -import { CameraSettingsComponent } from '@etherealengine/engine/src/scene/components/CameraSettingsComponent' -import { EnvmapComponent } from '@etherealengine/engine/src/scene/components/EnvmapComponent' -import { LinkComponent } from '@etherealengine/engine/src/scene/components/LinkComponent' -import { MediaSettingsComponent } from '@etherealengine/engine/src/scene/components/MediaSettingsComponent' -import { MountPointComponent } from '@etherealengine/engine/src/scene/components/MountPointComponent' -import { PostProcessingComponent } from '@etherealengine/engine/src/scene/components/PostProcessingComponent' -import { RenderSettingsComponent } from '@etherealengine/engine/src/scene/components/RenderSettingsComponent' -import { SceneSettingsComponent } from '@etherealengine/engine/src/scene/components/SceneSettingsComponent' -import { ShadowComponent } from '@etherealengine/engine/src/scene/components/ShadowComponent' -import { TextComponent } from '@etherealengine/engine/src/scene/components/TextComponent' +import { Component } from '@etherealengine/ecs/src/ComponentFunctions' import { ComponentJsonType } from '@etherealengine/engine/src/scene/types/SceneTypes' -import { RigidBodyComponent } from '@etherealengine/spatial/src/physics/components/RigidBodyComponent' -import { FogSettingsComponent } from '@etherealengine/spatial/src/renderer/components/FogSettingsComponent' -import Icon from '@etherealengine/ui/src/primitives/mui/Icon' import Typography from '@etherealengine/ui/src/primitives/mui/Typography' -import { PrimitiveGeometryComponent } from '../../../../engine/src/scene/components/PrimitiveGeometryComponent' import { ComponentEditorsState } from '../../functions/ComponentEditors' import { EditorControlFunctions } from '../../functions/EditorControlFunctions' import { usePopoverContextClose } from '../element/PopoverContext' - -export const PrefabShelfCategories = { - Audio: [PositionalAudioComponent], - Image: [ImageComponent], - Ground: [GroundPlaneComponent], - Shape: [PrimitiveGeometryComponent], - Spawn: [SpawnPointComponent], - Collider: [RigidBodyComponent, ColliderComponent], - 'Spot Light': [SpotLightComponent], - 'Ambient Light': [AmbientLightComponent], - PointLight: [PointLightComponent], - 'Directional Light': [DirectionalLightComponent], - 'Hemisphere Light': [HemisphereLightComponent], - '3D Text': [TextComponent], - Model: [ModelComponent, VariantComponent, ShadowComponent, LoopAnimationComponent, EnvmapComponent], - Video: [VideoComponent], - Volumetric: [VolumetricComponent], - Chair: [MountPointComponent], - Link: [LinkComponent], - Particles: [ParticleSystemComponent], - Settings: [ - MediaSettingsComponent, - RenderSettingsComponent, - SceneSettingsComponent, - CameraSettingsComponent, - PostProcessingComponent, - FogSettingsComponent, - SkyboxComponent - ], - 'Preview Camera': [ScenePreviewCameraComponent], - Spline: [SplineTrackComponent, SplineComponent], - 'Effect Volume': [SDFComponent] -} -export const PrefabNameShelfCategoriesState = defineState({ - name: 'ee.editor.PrefabNameShelfCategories', - initial: () => { - return { - Audio: ['Audio'], - Image: ['Image'], - Ground: ['Ground'], - Shape: ['Shape'], - Spawn: ['Spawn'], - Collider: ['Collider'], - Light: ['Spot Light', 'Ambient Light', 'Point Light', 'Directional Light', 'Hemisphere Light'], - - '3D Text': ['3D Text'], - Model: ['Model'], - Video: ['Video'], - Volumetric: ['Volumetric'], - Chair: ['Chair'], - Link: ['Link'], - Particles: ['Particles'], - Settings: ['Settings'], - 'Preview Camera': ['Preview Camera'], - Spline: ['Spline'], - 'Effect Volume': ['SDF'] - } as Record - } -}) +import { PrefabShelfCategories } from './PrefabEditors' const PrefabListItem = ({ item }: { item: string }) => { const { t } = useTranslation() const Icon = getState(ComponentEditorsState)[item]?.iconComponent ?? PlaceHolderIcon const handleClosePopover = usePopoverContextClose() + return ( { - let componentJsons: ComponentJsonType[] = [] - PrefabShelfCategories[item].forEach((component) => { - componentJsons.push({ name: component.jsonID }) + const PrefabNameShelfCategories = getState(PrefabShelfCategories) + const componentJsons: ComponentJsonType[] = [] + PrefabNameShelfCategories[item].forEach((component) => { + componentJsons.push({ name: component.jsonID as string }) }) - // EditorControlFunctions.createObjectFromSceneElement([ - // { name: PrefabShelfCategories[item][0].jsonID } - - // ]) EditorControlFunctions.createObjectFromSceneElement(componentJsons) handleClosePopover() }} @@ -171,56 +77,31 @@ const PrefabListItem = ({ item }: { item: string }) => { ) } -const ScenePrefabListItem = ({ - categoryTitle, - categoryItems, - isCollapsed -}: { - categoryTitle: string - categoryItems: string[] - isCollapsed: boolean -}) => { - const open = useHookstate(categoryTitle === 'Misc') +const ScenePrefabListItem = ({ categoryItems }: { categoryItems: string[]; isCollapsed: boolean }) => { return ( <> - open.set((prev) => !prev)} - style={{ - backgroundColor: 'var(--dockBackground)', - cursor: 'pointer', - color: 'var(--textColor)', - display: 'flex', - justifyContent: 'space-between', - width: '100%' - }} - > - {categoryTitle} - - - - - {categoryItems.map((item) => ( - - ))} - - + + {categoryItems.map((item) => ( + + ))} + ) } -const usePrefabNameShelfCategories = (search: string) => { - useHookstate(getMutableState(PrefabNameShelfCategoriesState)).value +const usePrefabShelfCategories = (search: string) => { + useHookstate(getMutableState(PrefabShelfCategories)).value if (!search) { - return Object.entries(getState(PrefabNameShelfCategoriesState)) + return Object.entries(getState(PrefabShelfCategories)) } const searchRegExp = new RegExp(search, 'gi') - return Object.entries(getState(PrefabNameShelfCategoriesState)) + return Object.entries(getState(PrefabShelfCategories)) .map(([category, items]) => { - const filteredItems = items.filter((item) => item.match(searchRegExp)?.length) - return [category, filteredItems] as [string, string[]] + const filteredcategory = category.match(searchRegExp)?.length ? category : '' + return [filteredcategory, items] as [string, Component[]] }) .filter(([_, items]) => !!items.length) } @@ -229,7 +110,11 @@ export function PrefabList() { const { t } = useTranslation() const search = useHookstate({ local: '', query: '' }) const searchTimeout = useRef | null>(null) - const shelves = usePrefabNameShelfCategories(search.query.value) + const shelves = usePrefabShelfCategories(search.query.value) + const shelveslist: string[] = [] + shelves.map(([category, items]) => { + shelveslist.push(category) + }) const onSearch = (text: string) => { search.local.set(text) @@ -256,14 +141,7 @@ export function PrefabList() { } > - {shelves.map(([category, items]) => ( - - ))} + ) } diff --git a/packages/editor/src/components/toolbar/ToolBar.tsx b/packages/editor/src/components/toolbar/ToolBar.tsx index b754bb62b1c..d69953862dc 100755 --- a/packages/editor/src/components/toolbar/ToolBar.tsx +++ b/packages/editor/src/components/toolbar/ToolBar.tsx @@ -39,6 +39,7 @@ import PublishLocation from './tools/PublishLocation' import RenderModeTool from './tools/RenderModeTool' import SceneScreenshot from './tools/SceneScreenshot' import StatsTool from './tools/StatsTool' +import StudioModeTool from './tools/StudioModeTool' import TransformPivotTool from './tools/TransformPivotTool' import TransformSnapTool from './tools/TransformSnapTool' import TransformSpaceTool from './tools/TransformSpaceTool' @@ -65,6 +66,7 @@ export const ToolBar = (props: ToolBarProps) => { + diff --git a/packages/editor/src/components/toolbar/tools/StudioModeTool.tsx b/packages/editor/src/components/toolbar/tools/StudioModeTool.tsx new file mode 100644 index 00000000000..c8eaa840a0f --- /dev/null +++ b/packages/editor/src/components/toolbar/tools/StudioModeTool.tsx @@ -0,0 +1,86 @@ +/* +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 { useHookstate } from '@hookstate/core' +import React from 'react' + +import { getMutableState } from '@etherealengine/hyperflux' + +import AdjustIcon from '@mui/icons-material/Adjust' + +import { StudioMode, StudioModeType } from '@etherealengine/engine/src/scene/constants/transformConstants' +import { t } from 'i18next' +import { useTranslation } from 'react-i18next' +import { EditorHelperState } from '../../../services/EditorHelperState' +import SelectInput from '../../inputs/SelectInput' +import { InfoTooltip } from '../../layout/Tooltip' +import * as styles from '../styles.module.scss' + +const modeToggleOptions = [ + { + label: t('editor:toolbar.studioModeToggle.lbl-advanced'), + info: t('editor:toolbar.studioModeToggle.info-advanced'), + value: StudioMode.Advanced + }, + { + label: t('editor:toolbar.studioModeToggle.lbl-simple'), + info: t('editor:toolbar.studioModeToggle.info-simple'), + value: StudioMode.Simple + } +] + +const studioModeToggleTool = () => { + const { t } = useTranslation() + + const editorHelperState = useHookstate(getMutableState(EditorHelperState)) + const settoggleMode = (mode: StudioModeType): void => { + getMutableState(EditorHelperState).studioMode.set(mode) + } + const toggleMode = () => { + getMutableState(EditorHelperState).studioMode.set((mode) => + mode === StudioMode.Advanced ? StudioMode.Simple : StudioMode.Advanced + ) + } + return ( +
+ + + + +
+ ) +} + +export default studioModeToggleTool diff --git a/packages/editor/src/services/EditorHelperState.ts b/packages/editor/src/services/EditorHelperState.ts index 3fd24aa8210..3b912e3879f 100644 --- a/packages/editor/src/services/EditorHelperState.ts +++ b/packages/editor/src/services/EditorHelperState.ts @@ -26,6 +26,8 @@ Ethereal Engine. All Rights Reserved. import { SnapMode, SnapModeType, + StudioMode, + StudioModeType, TransformMode, TransformModeType, TransformPivot, @@ -43,6 +45,7 @@ export const EditorHelperState = defineState({ transformSpace: TransformSpace.world as TransformSpaceType, transformPivot: TransformPivot.Selection as TransformPivotType, gridSnap: SnapMode.Grid as SnapModeType, + studioMode: StudioMode.Advanced as StudioModeType, translationSnap: 0.5, rotationSnap: 10, scaleSnap: 0.1 diff --git a/packages/engine/src/scene/constants/transformConstants.ts b/packages/engine/src/scene/constants/transformConstants.ts index 8027681785f..486d56865e5 100644 --- a/packages/engine/src/scene/constants/transformConstants.ts +++ b/packages/engine/src/scene/constants/transformConstants.ts @@ -36,6 +36,10 @@ export const TransformMode = { rotate: 'rotate' as const, scale: 'scale' as const } +export const StudioMode = { + Advanced: 'Advanced' as const, + Simple: 'Simple' as const +} export const TransformAxis = { X: 'X' as const, @@ -78,3 +82,4 @@ export type TransformSpaceType = (typeof TransformSpace)[keyof typeof TransformS export type TransformPivotType = (typeof TransformPivot)[keyof typeof TransformPivot] export type TransformAxisType = (typeof TransformAxis)[keyof typeof TransformAxis] export type SnapModeType = (typeof SnapMode)[keyof typeof SnapMode] +export type StudioModeType = (typeof StudioMode)[keyof typeof StudioMode] From 842489c56ab1d3cee6492d06c49ac6645501b407 Mon Sep 17 00:00:00 2001 From: Jiatong Yao Date: Mon, 22 Apr 2024 15:40:17 -0500 Subject: [PATCH 3/4] update for front end --- .../components/element/PropertiesPanelContainer.tsx | 10 +++++++--- .../components/hierarchy/HierarchyPanelContainer.tsx | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/editor/src/components/element/PropertiesPanelContainer.tsx b/packages/editor/src/components/element/PropertiesPanelContainer.tsx index a277308eefa..dbdbb36972d 100755 --- a/packages/editor/src/components/element/PropertiesPanelContainer.tsx +++ b/packages/editor/src/components/element/PropertiesPanelContainer.tsx @@ -35,6 +35,7 @@ import { EntityUUID } from '@etherealengine/ecs' import { MaterialSelectionState } from '@etherealengine/engine/src/scene/materials/MaterialLibraryState' import { Popover } from '@mui/material' import { ComponentEditorsState } from '../../functions/ComponentEditors' +import { EditorHelperState } from '../../services/EditorHelperState' import { EditorState } from '../../services/EditorServices' import { SelectionState } from '../../services/SelectionServices' import { PropertiesPanelButton } from '../inputs/Button' @@ -62,7 +63,7 @@ const EntityEditor = (props: { entityUUID: EntityUUID; multiEdit: boolean }) => const entity = UUIDComponent.getEntityByUUID(entityUUID) useHookstate(getMutableState(ComponentEditorsState).keys).value const components = useAllComponents(entity).filter((c) => !!getState(ComponentEditorsState)[c.name]) - + const studioMode = useHookstate(getMutableState(EditorHelperState).studioMode) const open = !!anchorEl.value return ( @@ -74,13 +75,16 @@ const EntityEditor = (props: { entityUUID: EntityUUID; multiEdit: boolean }) => }} >
- anchorEl.set(event.currentTarget)}> + anchorEl.set(event.currentTarget)} + style={{ display: studioMode.value === 'Simple' ? 'none' : 'block' }} + > {t('editor:properties.lbl-addComponent')}
anchorEl.set(null)} anchorOrigin={{ diff --git a/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx b/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx index fa538965929..4caead8770e 100644 --- a/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx +++ b/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx @@ -96,6 +96,7 @@ function HierarchyPanelContents({ rootEntityUUID }: { rootEntityUUID: EntityUUID const rootEntity = UUIDComponent.useEntityByUUID(rootEntityUUID) const sceneID = useHookstate(getMutableState(EditorState).sceneID) const index = SceneSnapshotState.useSnapshotIndex(sceneID.value!) + const studioMode = useHookstate(getMutableState(EditorHelperState).studioMode) const MemoTreeNode = useCallback( (props: HierarchyTreeNodeProps) => ( @@ -490,7 +491,7 @@ function HierarchyPanelContents({ rootEntityUUID }: { rootEntityUUID: EntityUUID }} onClick={(event) => { anchorElement.set(event.currentTarget) - if (getMutableState(EditorHelperState).studioMode.value == 'Advanced') { + if (studioMode.value == 'Advanced') { EditorControlFunctions.createObjectFromSceneElement() } }} @@ -507,7 +508,7 @@ function HierarchyPanelContents({ rootEntityUUID }: { rootEntityUUID: EntityUUID > anchorElement.set(null)} anchorOrigin={{ From e94e3dcb216606cec367db99cb61e8e91eef587c Mon Sep 17 00:00:00 2001 From: Jiatong Yao Date: Mon, 22 Apr 2024 17:26:57 -0500 Subject: [PATCH 4/4] fix conditional statement --- .../src/components/element/PropertiesPanelContainer.tsx | 2 +- .../src/components/hierarchy/HierarchyPanelContainer.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/editor/src/components/element/PropertiesPanelContainer.tsx b/packages/editor/src/components/element/PropertiesPanelContainer.tsx index dbdbb36972d..cbbab50deb8 100755 --- a/packages/editor/src/components/element/PropertiesPanelContainer.tsx +++ b/packages/editor/src/components/element/PropertiesPanelContainer.tsx @@ -84,7 +84,7 @@ const EntityEditor = (props: { entityUUID: EntityUUID; multiEdit: boolean }) => anchorEl.set(null)} anchorOrigin={{ diff --git a/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx b/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx index 4caead8770e..e38ca791edc 100644 --- a/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx +++ b/packages/editor/src/components/hierarchy/HierarchyPanelContainer.tsx @@ -491,7 +491,7 @@ function HierarchyPanelContents({ rootEntityUUID }: { rootEntityUUID: EntityUUID }} onClick={(event) => { anchorElement.set(event.currentTarget) - if (studioMode.value == 'Advanced') { + if (studioMode.value === 'Advanced') { EditorControlFunctions.createObjectFromSceneElement() } }} @@ -508,7 +508,7 @@ function HierarchyPanelContents({ rootEntityUUID }: { rootEntityUUID: EntityUUID > anchorElement.set(null)} anchorOrigin={{