Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
Optimize non 3d webpages from rendering canvas (#10421)
Browse files Browse the repository at this point in the history
* initial work to separate engine canvas as requirement

* studio works

* fixes

* studio fixes

* fix cache busting for admin panel

* license

* fix preview panels, various bug fixes

* fix checks and tests

* visual script, particles

* fix

* add dispose to mock renderer

* skip visual script tests

* console log

* add note

* fix error with renderer system test, don't start time for tests

* format

* fix regressions

* fix destroying spatial entities

* fix zindex for viewport toolbar

* client input system was still running if no viewer was available

* revert

---------

Co-authored-by: Daniel Belmes <[email protected]>
  • Loading branch information
HexaField and DanielBelmes authored Jun 25, 2024
1 parent 06a06b9 commit db09aee
Show file tree
Hide file tree
Showing 68 changed files with 680 additions and 526 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { PopoverState } from '@etherealengine/client-core/src/common/services/Po
import { AvatarService } from '@etherealengine/client-core/src/user/services/AvatarService'
import { THUMBNAIL_HEIGHT, THUMBNAIL_WIDTH } from '@etherealengine/common/src/constants/AvatarConstants'
import { AvatarType } from '@etherealengine/common/src/schema.type.module'
import { cleanURL } from '@etherealengine/common/src/utils/cleanURL'
import { AssetsPreviewPanel } from '@etherealengine/editor/src/components/assets/AssetsPreviewPanel'
import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes'
import { useHookstate } from '@etherealengine/hyperflux'
Expand Down Expand Up @@ -118,12 +119,12 @@ export default function AddEditAvatarModal({ avatar }: { avatar?: AvatarType })
avatarFile = avatarAssets.model.value
avatarThumbnail = avatarAssets.thumbnail.value
} else {
const modelName = avatarAssets.modelURL.value.split('/').pop()!
const avatarData = await fetch(avatarAssets.modelURL.value)
const modelName = cleanURL(avatarAssets.modelURL.value).split('/').pop()!
avatarFile = new File([await avatarData.blob()], modelName)

const thumbnailData = await fetch(avatarAssets.thumbnailURL.value)
const thumbnailName = avatarAssets.thumbnailURL.value.split('/').pop()!
const thumbnailName = cleanURL(avatarAssets.thumbnailURL.value).split('/').pop()!
avatarThumbnail = new File([await thumbnailData.blob()], thumbnailName)
}

Expand Down Expand Up @@ -165,12 +166,13 @@ export default function AddEditAvatarModal({ avatar }: { avatar?: AvatarType })

useEffect(() => {
if (!avatarAssets.modelURL.value || avatarAssets.source.value !== 'url') return
const modelName = avatarAssets.modelURL.value.split('/').pop()
const modelType = avatarAssets.modelURL.value.split('.').pop()
const modelURL = cleanURL(avatarAssets.modelURL.value)
const modelName = modelURL.split('/').pop()
const modelType = modelURL.split('.').pop()
if (!modelName || !modelType) return
;(previewPanelRef as any).current?.onSelectionChanged({
name: modelName,
resourceUrl: avatarAssets.modelURL.value,
resourceUrl: modelURL,
contentType: `model/${modelType}`
})
}, [avatarAssets.modelURL])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { useTranslation } from 'react-i18next'

import { PopoverState } from '@etherealengine/client-core/src/common/services/PopoverState'
import { StaticResourceType, uploadAssetPath } from '@etherealengine/common/src/schema.type.module'
import { cleanURL } from '@etherealengine/common/src/utils/cleanURL'
import { AssetsPreviewPanel } from '@etherealengine/editor/src/components/assets/AssetsPreviewPanel'
import {
AssetTypeToMimeType,
Expand Down Expand Up @@ -110,12 +111,13 @@ export default function CreateResourceModal({ selectedResource }: { selectedReso
contentType: MimeTypeToAssetType[state.mimeType.value]
})
} else {
getNameAndType(state.resourceURL.value).then(({ name, mimeType, assetType }) => {
const url = cleanURL(state.resourceURL.value)
getNameAndType(url).then(({ name, mimeType, assetType }) => {
state.name.set(name)
state.mimeType.set(mimeType)
;(previewPanelRef as any).current?.onSelectionChanged({
name: name,
resourceUrl: state.resourceURL.value,
resourceUrl: url,
contentType: assetType
})
})
Expand Down Expand Up @@ -145,8 +147,9 @@ export default function CreateResourceModal({ selectedResource }: { selectedReso
})
} else if (state.source.value === 'url' && state.resourceURL.value) {
const response = await fetch(state.resourceURL.value)
const url = cleanURL(state.resourceURL.value)
const blob = await response.blob()
const resourceFile = new File([blob], state.resourceURL.value.split('/').pop()!, {
const resourceFile = new File([blob], url.split('/').pop()!, {
type: state.mimeType.value
})
await uploadToFeathersService(uploadAssetPath, [resourceFile], {
Expand Down
79 changes: 79 additions & 0 deletions packages/client-core/src/hooks/useEngineCanvas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
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 { getComponent } from '@etherealengine/ecs'
import { getState, useHookstate } from '@etherealengine/hyperflux'
import { EngineState } from '@etherealengine/spatial/src/EngineState'
import { destroySpatialEngine, initializeSpatialEngine } from '@etherealengine/spatial/src/initializeEngine'
import { RendererComponent } from '@etherealengine/spatial/src/renderer/WebGLRendererSystem'
import { useEffect } from 'react'

export const useEngineCanvas = (ref: React.RefObject<HTMLElement>) => {
const lastRef = useHookstate(ref.current)

useEffect(() => {
if (ref.current !== lastRef.value) {
lastRef.set(ref.current)
}
}, [ref.current])

useEffect(() => {
if (!lastRef.value) return

const parent = lastRef.value as HTMLElement

const canvas = document.getElementById('engine-renderer-canvas') as HTMLCanvasElement
const originalParent = canvas.parentElement
initializeSpatialEngine(canvas)
parent.appendChild(canvas)

const observer = new ResizeObserver(() => {
getComponent(getState(EngineState).viewerEntity, RendererComponent).needsResize = true
})

observer.observe(parent)

return () => {
destroySpatialEngine()
observer.disconnect()
parent.removeChild(canvas)
originalParent?.appendChild(canvas)
}
}, [lastRef.value])
}

export const useRemoveEngineCanvas = () => {
useEffect(() => {
const canvas = document.getElementById('engine-renderer-canvas')!
const parent = canvas.parentElement
parent?.removeChild(canvas)

return () => {
parent?.appendChild(canvas)
}
}, [])

return null
}
7 changes: 5 additions & 2 deletions packages/client-core/src/systems/AvatarUISystem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import { TransformComponent } from '@etherealengine/spatial/src/transform/compon
import { TransformSystem } from '@etherealengine/spatial/src/transform/systems/TransformSystem'
import { XRUIComponent } from '@etherealengine/spatial/src/xrui/components/XRUIComponent'

import { EngineState } from '@etherealengine/spatial/src/EngineState'
import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent'
import AvatarContextMenu from '../user/components/UserMenu/menus/AvatarContextMenu'
import { PopupMenuState } from '../user/components/UserMenu/PopupMenuService'
Expand Down Expand Up @@ -159,9 +160,11 @@ const onSecondaryClick = () => {
}

const execute = () => {
const viewerEntity = getState(EngineState).viewerEntity
if (!viewerEntity) return

const ecsState = getState(ECSState)

const viewerEntity = Engine.instance.viewerEntity
const buttons = InputComponent.getMergedButtons(viewerEntity)

// const buttons = InputSourceComponent.getMergedButtons()
Expand Down Expand Up @@ -190,7 +193,7 @@ const execute = () => {
AvatarUI.set(userEntity, ui)
}

const cameraTransform = getComponent(Engine.instance.cameraEntity, TransformComponent)
const cameraTransform = getComponent(viewerEntity, TransformComponent)

const immersiveMedia = getState(MediaSettingsState).immersiveMedia
const mediaNetwork = NetworkState.mediaNetwork
Expand Down
51 changes: 34 additions & 17 deletions packages/client-core/src/systems/LoadingUISystem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Ethereal Engine. All Rights Reserved.
import React, { useEffect } from 'react'
import { BackSide, Color, Mesh, MeshBasicMaterial, SphereGeometry, Vector2 } from 'three'

import { Entity } from '@etherealengine/ecs'
import { Entity, UndefinedEntity } from '@etherealengine/ecs'
import {
getComponent,
getMutableComponent,
Expand Down Expand Up @@ -69,9 +69,10 @@ import { XRUIComponent } from '@etherealengine/spatial/src/xrui/components/XRUIC
import { ObjectFitFunctions } from '@etherealengine/spatial/src/xrui/functions/ObjectFitFunctions'
import type { WebLayer3D } from '@etherealengine/xrui'

import { EngineState } from '@etherealengine/spatial/src/EngineState'
import { AdminClientSettingsState } from '../admin/services/Setting/ClientSettingService'
import { AppThemeState, getAppTheme } from '../common/services/AppThemeState'
import { useRemoveEngineCanvas } from '../hooks/useRemoveEngineCanvas'
import { useRemoveEngineCanvas } from '../hooks/useEngineCanvas'
import { LocationState } from '../social/services/LocationService'
import { AuthState } from '../user/services/AuthService'
import { LoadingSystemState } from './state/LoadingState'
Expand All @@ -85,6 +86,20 @@ export const LoadingUISystemState = defineState({
name: 'LoadingUISystemState',
initial: () => {
const transition = createTransitionState(transitionPeriodSeconds, 'IN')
return {
ui: null as null | ReturnType<typeof createLoaderDetailView>,
colors: {
main: '',
background: '',
alternate: ''
},
meshEntity: UndefinedEntity,
transition,
ready: false
}
},

createLoadingUI: () => {
const ui = createLoaderDetailView()
getMutableComponent(ui.entity, InputComponent).grow.set(false)
setComponent(ui.entity, NameComponent, 'Loading XRUI')
Expand Down Expand Up @@ -117,17 +132,10 @@ export const LoadingUISystemState = defineState({

getComponent(meshEntity, TransformComponent).scale.set(-1, 1, -1)

return {
getMutableState(LoadingUISystemState).merge({
ui,
colors: {
main: '',
background: '',
alternate: ''
},
meshEntity,
transition,
ready: false
}
meshEntity
})
}
})

Expand All @@ -140,7 +148,12 @@ const LoadingReactor = (props: { sceneEntity: Entity }) => {
const state = useMutableState(LoadingUISystemState)

useEffect(() => {
state.ui.get(NO_PROXY).state.progress.set(loadingProgress)
if (!state.ui.value) LoadingUISystemState.createLoadingUI()
}, [])

useEffect(() => {
const ui = state.ui.get(NO_PROXY)!
ui.state.progress.set(loadingProgress)
}, [loadingProgress])

/** Scene is loading */
Expand All @@ -160,7 +173,8 @@ const LoadingReactor = (props: { sceneEntity: Entity }) => {
}, [sceneLoaded])

useEffect(() => {
const xrui = getComponent(state.ui.entity.value, XRUIComponent)
const ui = state.ui.get(NO_PROXY)!
const xrui = getComponent(ui.entity!, XRUIComponent)
const progressBar = xrui.getObjectByName('progress-container') as WebLayer3D | undefined
if (!progressBar) return

Expand Down Expand Up @@ -261,7 +275,7 @@ const defaultColor = new Color()

const execute = () => {
const { transition, ui, meshEntity, colors, ready } = getState(LoadingUISystemState)
if (!transition) return
if (!ui) return

const ecsState = getState(ECSState)

Expand Down Expand Up @@ -322,7 +336,7 @@ const execute = () => {
setVisibleComponent(ui.entity, isReady)
}

const reactor = () => {
const Reactor = () => {
const themeState = useMutableState(AppThemeState)
const themeModes = useHookstate(getMutableState(AuthState).user?.userSetting?.ornull?.themeModes)
const clientSettings = useHookstate(
Expand Down Expand Up @@ -353,5 +367,8 @@ export const LoadingUISystem = defineSystem({
uuid: 'ee.client.LoadingUISystem',
insert: { before: TransformSystem },
execute,
reactor
reactor: () => {
if (!useMutableState(EngineState).viewerEntity.value) return null
return <Reactor />
}
})
30 changes: 18 additions & 12 deletions packages/client-core/src/systems/WarningUISystem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,15 @@ import { removeEntity } from '@etherealengine/ecs/src/EntityFunctions'
import { defineSystem } from '@etherealengine/ecs/src/SystemFunctions'
import { PresentationSystemGroup } from '@etherealengine/ecs/src/SystemGroups'
import { defineState, getMutableState, getState, useMutableState } from '@etherealengine/hyperflux'
import { EngineState } from '@etherealengine/spatial/src/EngineState'
import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent'
import { createTransitionState } from '@etherealengine/spatial/src/common/functions/createTransitionState'
import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent'
import { setVisibleComponent, VisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent'
import { createTransitionState } from '@etherealengine/spatial/src/common/functions/createTransitionState'
import { VisibleComponent, setVisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent'
import { ComputedTransformComponent } from '@etherealengine/spatial/src/transform/components/ComputedTransformComponent'
import { XRUIComponent } from '@etherealengine/spatial/src/xrui/components/XRUIComponent'
import { createXRUI } from '@etherealengine/spatial/src/xrui/functions/createXRUI'
import { ObjectFitFunctions } from '@etherealengine/spatial/src/xrui/functions/ObjectFitFunctions'
import { createXRUI } from '@etherealengine/spatial/src/xrui/functions/createXRUI'
import type { WebLayer3D } from '@etherealengine/xrui'

export const WarningUIState = defineState({
Expand Down Expand Up @@ -151,12 +152,8 @@ export const WarningUISystemState = defineState({
const transitionPeriodSeconds = 0.2
const transition = createTransitionState(transitionPeriodSeconds, 'OUT')

const ui = createXRUI(WarningSystemXRUI)
removeComponent(ui.entity, VisibleComponent)
setComponent(ui.entity, NameComponent, 'Warning XRUI')

return {
ui,
ui: null as null | ReturnType<typeof createXRUI>,
transition
}
}
Expand All @@ -179,8 +176,10 @@ function TransitionReactor() {
let accumulator = 0

const execute = () => {
const state = getState(WarningUIState)
const { transition, ui } = getState(WarningUISystemState)
if (!ui) return

const state = getState(WarningUIState)

const deltaSeconds = getState(ECSState).deltaSeconds

Expand Down Expand Up @@ -226,10 +225,14 @@ const execute = () => {
})
}

const reactor = () => {
const Reactor = () => {
useEffect(() => {
const ui = createXRUI(WarningSystemXRUI)
removeComponent(ui.entity, VisibleComponent)
setComponent(ui.entity, NameComponent, 'Warning XRUI')
getMutableState(WarningUISystemState).ui.set(ui)

return () => {
const ui = getState(WarningUISystemState).ui
removeEntity(ui.entity)
}
}, [])
Expand All @@ -240,5 +243,8 @@ export const WarningUISystem = defineSystem({
uuid: 'ee.client.WarningUISystem',
insert: { after: PresentationSystemGroup },
execute,
reactor
reactor: () => {
if (!useMutableState(EngineState).viewerEntity.value) return null
return <Reactor />
}
})
Loading

0 comments on commit db09aee

Please sign in to comment.