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

Commit

Permalink
Resource State (#9632)
Browse files Browse the repository at this point in the history
* Start of Resource State

* licenses

* Start of resource manager hooks

* Resource hook refactoring

* useTexture for SkyboxComponent

* Start of unloading logic

* Include entity in useTexture

* Seperate logic by resource type

* Update Three cache

* Avatar loading system to resource manager

* Particle system to resource manager

* Per type metadata

* Include entity

* Unload assets when changed in editor, onUnload callback

* Image component resource manager hooks

* Don't use unloaded callback when the component is being unmounted

* Batch loader, loopAnimationComponent, AvatarAnimationSystem resource hooks

* Spawn point component to resource hooks, make sure unload only is called when unmounted

* Better typing

* onStart callback for setting initial metadata per type

* Update loaders to use abort controller signal

* license

* loader typings

* Portal texture resource hook

* Media component resource hooks, potential on gpu callback

* EnvmapComponent resource hooks

* rename abort controller function

* fix ordering

* Envmap and Hyperspace to resource hooks

* seperate resources by type, start of tests

* Update typing

* Remove references to three FileLoader, Loader

* Replace three loader reference

* Parse function is optional

* Custom loading manager with more hooks, tests

* license

* cleanup

* tests

* tests

* tests

* Remove materials on model unload

* Model component fix wip

* Track textures loaded by the GLTFLoader

* Update imports

* Resource manager track and manage materials, geometry

* Cleanup, no need to set asset to null when resources are disposed

* Track resources used by avatar preview, preliminary fixes for unloading assets race condition

* unload correctly in avatar preview, envmapcomponent, model component

* Update imports

* Start of variant resource manager

* Resource manager loads variants if exists

* hookstate fix

* Component typing

* Avatar preview animation fixes

* Mesh bvh hookstate and race condition fix, shadow system unmount fix

* Mesh bvh fixes, object layer component fixes

* Preliminary fix for avatar switching

* Fix errors when selecting a new avatar

* track textures by source uuid, remove dispose calls, mesh bvh hookstate fix, portal component reactivity fix

* debug change

* Update mesh bvh

* remove property

* test fix

* Fix for multiple avtars shown in avatar selection panel

* Avatar selection working

* bug fixes

* src ref

---------

Co-authored-by: HexaField <[email protected]>
Co-authored-by: Daniel Belmes <[email protected]>
  • Loading branch information
3 people authored Feb 14, 2024
1 parent 784e1d9 commit cd9908d
Show file tree
Hide file tree
Showing 49 changed files with 2,049 additions and 542 deletions.
24 changes: 9 additions & 15 deletions packages/client-core/src/common/components/AvatarPreview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { SxProps, Theme } from '@mui/material/styles'
import styles from './index.module.scss'

import { EntityUUID } from '@etherealengine/common/src/interfaces/EntityUUID'
import { hasComponent, removeComponent, setComponent } from '@etherealengine/ecs'
import { setComponent, UndefinedEntity } from '@etherealengine/ecs'
import { defaultAnimationPath, preloadedAnimations } from '@etherealengine/engine/src/avatar/animation/Util'
import { LoopAnimationComponent } from '@etherealengine/engine/src/avatar/components/LoopAnimationComponent'
import { AssetPreviewCameraComponent } from '@etherealengine/engine/src/camera/components/AssetPreviewCameraComponent'
Expand All @@ -55,6 +55,7 @@ import { UUIDComponent } from '@etherealengine/spatial/src/common/UUIDComponent'
import { ObjectLayerMaskComponent } from '@etherealengine/spatial/src/renderer/components/ObjectLayerComponent'
import { VisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent'
import { ObjectLayers } from '@etherealengine/spatial/src/renderer/constants/ObjectLayers'
import { EntityTreeComponent } from '@etherealengine/spatial/src/transform/components/EntityTree'
import { MathUtils } from 'three'

interface Props {
Expand All @@ -68,16 +69,10 @@ interface Props {
const AvatarPreview = ({ fill, avatarUrl, sx, onAvatarError, onAvatarLoaded }: Props) => {
const { t } = useTranslation()
const panelRef = useRef() as React.MutableRefObject<HTMLDivElement>

useRender3DPanelSystem(panelRef)

useEffect(() => {
loadAvatarPreview()
}, [avatarUrl])

const renderPanelState = getMutableState(PreviewPanelRendererState)

const loadAvatarPreview = () => {
useEffect(() => {
if (!avatarUrl) return

const renderPanelEntities = renderPanelState.entities[panelRef.current.id]
Expand All @@ -86,20 +81,19 @@ const AvatarPreview = ({ fill, avatarUrl, sx, onAvatarError, onAvatarLoaded }: P
setComponent(entity, UUIDComponent, uuid)
setComponent(entity, NameComponent, '3D Preview Entity')

if (hasComponent(entity, LoopAnimationComponent)) removeComponent(entity, LoopAnimationComponent)
if (hasComponent(entity, ModelComponent)) removeComponent(entity, ModelComponent)

setComponent(entity, VisibleComponent, true)
ObjectLayerMaskComponent.setLayer(entity, ObjectLayers.AssetPreview)
setComponent(entity, ModelComponent, { src: avatarUrl, convertToVRM: true })
setComponent(entity, LoopAnimationComponent, {
animationPack: defaultAnimationPath + preloadedAnimations.locomotion + '.glb',
activeClipIndex: 5
})
setComponent(entity, ModelComponent, { src: avatarUrl, convertToVRM: true })
setComponent(entity, EntityTreeComponent, { parentEntity: UndefinedEntity })

setComponent(entity, VisibleComponent, true)
ObjectLayerMaskComponent.setLayer(entity, ObjectLayers.AssetPreview)
setComponent(entity, EnvmapComponent, { type: EnvMapSourceType.Skybox })
const cameraEntity = renderPanelEntities[PanelEntities.camera].value
setComponent(cameraEntity, AssetPreviewCameraComponent, { targetModelEntity: entity })
}
}, [avatarUrl])

return (
<Box className={`${commonStyles.preview} ${fill ? styles.fill : ''}`} sx={sx}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@ import React, { useEffect } from 'react'
import { Euler, Quaternion, Vector3, WebGLRenderer } from 'three'

import {
Engine,
Entity,
PresentationSystemGroup,
UndefinedEntity,
createEntity,
defineQuery,
defineSystem,
getComponent,
getOptionalComponent,
removeComponent,
removeEntity,
setComponent
} from '@etherealengine/ecs'
import { defineState, getMutableState, none } from '@etherealengine/hyperflux'
import { NO_PROXY, defineState, getMutableState, none } from '@etherealengine/hyperflux'
import { DirectionalLightComponent, TransformComponent } from '@etherealengine/spatial'
import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent'
import {
Expand All @@ -49,6 +49,7 @@ import {
import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent'
import { InputSourceComponent } from '@etherealengine/spatial/src/input/components/InputSourceComponent'
import { addClientInputListeners } from '@etherealengine/spatial/src/input/systems/ClientInputSystem'
import { GroupComponent } from '@etherealengine/spatial/src/renderer/components/GroupComponent'
import {
ObjectLayerComponents,
ObjectLayerMaskComponent
Expand Down Expand Up @@ -158,6 +159,7 @@ export function useRender3DPanelSystem(panel: React.MutableRefObject<HTMLDivElem
getMutableState(ActiveOrbitCamera).set(UndefinedEntity)
const thisIdIndex = rendererState.ids.value.findIndex((value) => value === id)
rendererState.entities[id].set(none)
rendererState.renderers[id].get(NO_PROXY).dispose()
rendererState.renderers[id].set(none)
rendererState.ids[thisIdIndex].set(none)
}
Expand Down Expand Up @@ -193,21 +195,20 @@ export const render3DPanelSystem = defineSystem({
iterateEntityNode(previewEntity, (entity) => {
setComponent(entity, ObjectLayerComponents[ObjectLayers.AssetPreview])
})
const cameraComponent = getComponent(cameraEntity, CameraComponent)
// sync with view camera
const viewCamera = cameraComponent.cameras[0]
viewCamera.projectionMatrix.copy(cameraComponent.projectionMatrix)
viewCamera.quaternion.copy(cameraComponent.quaternion)
viewCamera.position.copy(cameraComponent.position)
viewCamera.layers.mask = getComponent(cameraEntity, ObjectLayerMaskComponent)
// hack to make the background transparent for the preview
const lastBackground = Engine.instance.scene.background
Engine.instance.scene.background = null
rendererState.renderers[id].value.render(Engine.instance.scene, viewCamera)
Engine.instance.scene.background = lastBackground
iterateEntityNode(previewEntity, (entity) => {
removeComponent(entity, ObjectLayerComponents[ObjectLayers.AssetPreview])
})
const group = getOptionalComponent(previewEntity, GroupComponent)
if (group && group[0]) {
const cameraComponent = getComponent(cameraEntity, CameraComponent)
// sync with view camera
const viewCamera = cameraComponent.cameras[0]
viewCamera.projectionMatrix.copy(cameraComponent.projectionMatrix)
viewCamera.quaternion.copy(cameraComponent.quaternion)
viewCamera.position.copy(cameraComponent.position)
viewCamera.layers.mask = getComponent(cameraEntity, ObjectLayerMaskComponent)
rendererState.renderers[id].value.render(group[0], viewCamera)
iterateEntityNode(previewEntity, (entity) => {
removeComponent(entity, ObjectLayerComponents[ObjectLayers.AssetPreview])
})
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/engine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"sift": "^17.0.1",
"simplex-noise": "^4.0.1",
"three": "0.158.0",
"three-mesh-bvh": "^0.6.8",
"three-mesh-bvh": "^0.7.1",
"three.quarks": "0.11.1",
"troika-three-text": "^0.49.0",
"ts-matches": "5.3.0",
Expand Down
21 changes: 6 additions & 15 deletions packages/engine/src/assets/classes/AssetLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import {
AudioLoader,
BufferAttribute,
BufferGeometry,
FileLoader,
Group,
LOD,
Material,
Expand All @@ -44,6 +43,8 @@ import {
TextureLoader
} from 'three'

import { FileLoader } from '../loaders/base/FileLoader'

import { getState } from '@etherealengine/hyperflux'

import { isClient } from '@etherealengine/common/src/utils/getEnvironment'
Expand Down Expand Up @@ -87,14 +88,6 @@ const onUploadDropBuffer = () =>
this.array = new this.array.constructor(1)
}

const onTextureUploadDropSource = () =>
function (this: Texture) {
// source.data can't be null because the WebGLRenderer checks for it
this.source.data = { width: this.source.data.width, height: this.source.data.height, __deleted: true }
this.mipmaps.map((b) => delete b.data)
this.mipmaps = []
}

export const cleanupAllMeshData = (child: Mesh, args: LoadingArgs) => {
if (getState(EngineState).isEditor || !child.isMesh) return
const geo = child.geometry as BufferGeometry
Expand All @@ -104,9 +97,6 @@ export const cleanupAllMeshData = (child: Mesh, args: LoadingArgs) => {
for (const name in attributes) (attributes[name] as BufferAttribute).onUploadCallback = onUploadDropBuffer()
if (geo.index) geo.index.onUploadCallback = onUploadDropBuffer()
}
Object.entries(mat)
.filter(([k, v]: [keyof typeof mat, Texture]) => v?.isTexture)
.map(([_, v]) => (v.onUpdate = onTextureUploadDropSource()))
}

const processModelAsset = (asset: Mesh, args: LoadingArgs): void => {
Expand Down Expand Up @@ -368,7 +358,7 @@ const assetLoadCallback =

const getAbsolutePath = (url) => (isAbsolutePath(url) ? url : getState(EngineState).publicPath + url)

type LoadingArgs = {
export type LoadingArgs = {
ignoreDisposeGeometry?: boolean
forceAssetType?: AssetType
assetRoot?: Entity
Expand All @@ -379,7 +369,8 @@ const load = async (
args: LoadingArgs,
onLoad = (response: any) => {},
onProgress = (request: ProgressEvent) => {},
onError = (event: ErrorEvent | Error) => {}
onError = (event: ErrorEvent | Error) => {},
signal?: AbortSignal
) => {
if (!_url) {
onError(new Error('URL is empty'))
Expand Down Expand Up @@ -430,7 +421,7 @@ const load = async (
const callback = assetLoadCallback(url, args, assetType, onLoad)

try {
return loader.load(url, callback, onProgress, onError)
return loader.load(url, callback, onProgress, onError, signal)
} catch (error) {
onError(error)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
import fetch from 'cross-fetch'
import draco3d from 'draco3dgltf'
import { MeshoptDecoder, MeshoptEncoder } from 'meshoptimizer'
import { FileLoader } from 'three'
import { FileLoader } from '../loaders/base/FileLoader'

import { EEMaterialExtension } from './extensions/EE_MaterialTransformer'
import { EEResourceIDExtension } from './extensions/EE_ResourceIDTransformer'
Expand Down
4 changes: 3 additions & 1 deletion packages/engine/src/assets/font/FontLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import { FileLoader, Loader, LoadingManager, ShapePath } from 'three'
import { LoadingManager, ShapePath } from 'three'

import { FileLoader } from '../loaders/base/FileLoader'
import { Loader } from '../loaders/base/Loader'
export class FontLoader extends Loader {
constructor(manager?: LoadingManager) {
super(manager)
Expand Down
Loading

0 comments on commit cd9908d

Please sign in to comment.