Skip to content
This repository was archived by the owner on Aug 21, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/client-core/src/networking/AvatarSpawnSystem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import {
getComponent,
getOptionalComponent,
PresentationSystemGroup,
useComponent,
useQuery,
UUIDComponent
} from '@etherealengine/ecs'
Expand All @@ -56,7 +55,7 @@ import { AuthState } from '../user/services/AuthService'
export const AvatarSpawnReactor = (props: { sceneEntity: Entity }) => {
if (!isClient) return null
const { sceneEntity } = props
const gltfLoaded = useComponent(sceneEntity, GLTFComponent).progress.value === 100
const gltfLoaded = GLTFComponent.useSceneLoaded(sceneEntity)
const searchParams = useMutableState(SearchParamState)

const spawnAvatar = useHookstate(false)
Expand Down
4 changes: 2 additions & 2 deletions packages/client-core/src/systems/LoadingUISystem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ export const LoadingUISystemState = defineState({

const LoadingReactor = (props: { sceneEntity: Entity }) => {
const { sceneEntity } = props
const gltfComponent = useComponent(props.sceneEntity, GLTFComponent)
const gltfComponent = useComponent(sceneEntity, GLTFComponent)
const loadingProgress = gltfComponent.progress.value
const sceneLoaded = loadingProgress === 100
const sceneLoaded = GLTFComponent.useSceneLoaded(sceneEntity)
const locationState = useMutableState(LocationState)
const state = useMutableState(LoadingUISystemState)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import { ComponentType, getComponent, hasComponent } from '@etherealengine/ecs/src/ComponentFunctions'
import { Entity } from '@etherealengine/ecs/src/Entity'
import { getComponent, hasComponent } from '@etherealengine/ecs/src/ComponentFunctions'
import { Entity, EntityUUID } from '@etherealengine/ecs/src/Entity'
import { entityExists } from '@etherealengine/ecs/src/EntityFunctions'
import { SourceComponent } from '@etherealengine/engine/src/scene/components/SourceComponent'
import { getState } from '@etherealengine/hyperflux'
Expand Down Expand Up @@ -68,7 +68,7 @@ function buildHierarchyTree(
sceneID: string,
showModelChildren: boolean
) {
const uuid = node.extensions && (node.extensions[UUIDComponent.jsonID] as ComponentType<typeof UUIDComponent>)
const uuid = node.extensions && (node.extensions[UUIDComponent.jsonID] as EntityUUID)
const entity = UUIDComponent.getEntityByUUID(uuid!)
if (!entity || !entityExists(entity)) return

Expand Down
7 changes: 3 additions & 4 deletions packages/editor/src/systems/ClickPlacementSystem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import {
getOptionalComponent,
removeComponent,
setComponent,
useComponent,
useOptionalComponent
} from '@etherealengine/ecs'
import { GLTFComponent } from '@etherealengine/engine/src/gltf/GLTFComponent'
Expand Down Expand Up @@ -115,7 +114,7 @@ const ClickPlacementReactor = (props: { parentEntity: Entity }) => {
const { parentEntity } = props
const clickState = useState(getMutableState(ClickPlacementState))
const editorState = useState(getMutableState(EditorHelperState))
const gltfComponent = useComponent(parentEntity, GLTFComponent)
const sceneLoaded = GLTFComponent.useSceneLoaded(parentEntity)
const errors = useEntityErrors(clickState.placementEntity.value, ModelComponent)

// const renderers = defineQuery([RendererComponent])
Expand All @@ -132,7 +131,7 @@ const ClickPlacementReactor = (props: { parentEntity: Entity }) => {
// }, [editorState.placementMode])

useEffect(() => {
if (gltfComponent.progress.value < 100) return
if (!sceneLoaded) return
if (editorState.placementMode.value === PlacementMode.CLICK) {
SelectionState.updateSelection([])
if (clickState.placementEntity.value) return
Expand All @@ -146,7 +145,7 @@ const ClickPlacementReactor = (props: { parentEntity: Entity }) => {
clickState.placementEntity.set(UndefinedEntity)
SelectionState.updateSelection(selectedEntities)
}
}, [editorState.placementMode, gltfComponent.progress])
}, [editorState.placementMode, sceneLoaded])

useEffect(() => {
if (!clickState.placementEntity.value) return
Expand Down
156 changes: 137 additions & 19 deletions packages/engine/src/gltf/GLTFComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import React, { useEffect } from 'react'

import { parseStorageProviderURLs } from '@etherealengine/common/src/utils/parseSceneJSON'
import {
Component,
ComponentJSONIDMap,
defineComponent,
Entity,
EntityUUID,
Expand All @@ -44,13 +46,36 @@ import { dispatchAction, getState, useHookstate } from '@etherealengine/hyperflu

import { FileLoader } from '../assets/loaders/base/FileLoader'
import { BINARY_EXTENSION_HEADER_MAGIC, EXTENSIONS, GLTFBinaryExtension } from '../assets/loaders/gltf/GLTFExtensions'
import { ModelComponent } from '../scene/components/ModelComponent'
import { SourceComponent } from '../scene/components/SourceComponent'
import { SceneJsonType } from '../scene/types/SceneTypes'
import { migrateSceneJSONToGLTF } from './convertJsonToGLTF'
import { GLTFDocumentState, GLTFSnapshotAction } from './GLTFDocumentState'
import { ResourcePendingComponent } from './ResourcePendingComponent'

const loadDependencies = {
['EE_model']: ['scene']
} as Record<string, string[]>

type ComponentDependencies = Record<EntityUUID, Component[]>

const buildComponentDependencies = (json: GLTF.IGLTF) => {
const dependencies = {} as ComponentDependencies
if (!json.nodes) return dependencies
for (const node of json.nodes) {
if (!node.extensions || !node.extensions[UUIDComponent.jsonID]) continue
const uuid = node.extensions[UUIDComponent.jsonID] as EntityUUID
const extensions = Object.keys(node.extensions)
for (const extension of extensions) {
if (loadDependencies[extension]) {
if (!dependencies[uuid]) dependencies[uuid] = []
dependencies[uuid].push(ComponentJSONIDMap.get(extension)!)
}
}
}

return dependencies
}

export const GLTFComponent = defineComponent({
name: 'GLTFComponent',

Expand All @@ -59,49 +84,70 @@ export const GLTFComponent = defineComponent({
src: '',
// internals
extensions: {},
progress: 0
progress: 0,
dependencies: undefined as ComponentDependencies | undefined
}
},

onSet(entity, component, json) {
if (typeof json?.src === 'string') component.src.set(json.src)
},

useDependenciesLoaded(entity: Entity) {
const dependencies = useComponent(entity, GLTFComponent).dependencies
return !!(dependencies.value && !dependencies.keys?.length)
},

useSceneLoaded(entity: Entity) {
const gltfComponent = useComponent(entity, GLTFComponent)
const dependencies = gltfComponent.dependencies
const progress = gltfComponent.progress.value
return !!(dependencies.value && !dependencies.keys?.length) && progress === 100
},

isSceneLoaded(entity: Entity) {
const gltfComponent = getComponent(entity, GLTFComponent)
const dependencies = gltfComponent.dependencies
const progress = gltfComponent.progress
return !!(dependencies && !Object.keys(dependencies).length) && progress === 100
},

reactor: () => {
const entity = useEntityContext()
const gltfComponent = useComponent(entity, GLTFComponent)
const dependencies = gltfComponent.dependencies

useGLTFDocument(gltfComponent.src.value, entity)

const documentID = useComponent(entity, SourceComponent).value

return <ResourceReactor documentID={documentID} entity={entity} />
return (
<>
<ResourceReactor documentID={documentID} entity={entity} />
{dependencies.value && dependencies.keys?.length ? (
<DependencyReactor
key={entity}
gltfComponentEntity={entity}
dependencies={dependencies.value as ComponentDependencies}
/>
) : null}
</>
)
}
})

const ResourceReactor = (props: { documentID: string; entity: Entity }) => {
const dependenciesLoaded = GLTFComponent.useDependenciesLoaded(props.entity)
const resourceQuery = useQuery([SourceComponent, ResourcePendingComponent])
const sourceEntities = useHookstate(SourceComponent.entitiesBySourceState[props.documentID])

useEffect(() => {
if (getComponent(props.entity, GLTFComponent).progress === 100) return
if (!getState(GLTFDocumentState)[props.documentID]) return
const document = getState(GLTFDocumentState)[props.documentID]
const modelNodes = document.nodes?.filter((node) => !!node.extensions?.[ModelComponent.jsonID])
if (modelNodes) {
for (const node of modelNodes) {
//check if an entity exists for this node, and has a model component
const uuid = node.extensions![UUIDComponent.jsonID] as EntityUUID
if (!UUIDComponent.entitiesByUUIDState[uuid]) return
const entity = UUIDComponent.entitiesByUUIDState[uuid].value
const model = getOptionalComponent(entity, ModelComponent)
//ensure that model contents have been loaded into the scene
if (!model?.scene) return
}
}

const entities = resourceQuery.filter((e) => getComponent(e, SourceComponent) === props.documentID)
if (!entities.length) {
getMutableComponent(props.entity, GLTFComponent).progress.set(100)
if (dependenciesLoaded) getMutableComponent(props.entity, GLTFComponent).progress.set(100)
return
}

Expand All @@ -121,14 +167,83 @@ const ResourceReactor = (props: { documentID: string; entity: Entity }) => {

const progress = resources.reduce((acc, resource) => acc + resource.progress, 0)
const total = resources.reduce((acc, resource) => acc + resource.total, 0)
if (!total) return

const percentage = total === 0 ? 100 : (progress / total) * 100
const percentage = Math.floor(Math.min((progress / total) * 100, dependenciesLoaded ? 100 : 99))
getMutableComponent(props.entity, GLTFComponent).progress.set(percentage)
}, [resourceQuery, sourceEntities])
}, [resourceQuery, sourceEntities, dependenciesLoaded])

return null
}

const ComponentReactor = (props: { gltfComponentEntity: Entity; entity: Entity; component: Component }) => {
const { gltfComponentEntity, entity, component } = props
const dependencies = loadDependencies[component.jsonID!]
const comp = useComponent(entity, component)

useEffect(() => {
const compValue = comp.value
for (const key of dependencies) {
if (!compValue[key]) return
}

// console.log(`All dependencies loaded for entity: ${entity} on component: ${component.jsonID}`)

const gltfComponent = getMutableComponent(gltfComponentEntity, GLTFComponent)
const uuid = getComponent(entity, UUIDComponent)
gltfComponent.dependencies.set((prev) => {
const dependencyArr = prev![uuid] as Component[]
const index = dependencyArr.findIndex((compItem) => compItem.jsonID === component.jsonID)
dependencyArr.splice(index, 1)
if (!dependencyArr.length) {
delete prev![uuid]
}
return prev
})
}, [...dependencies.map((key) => comp[key])])

return null
}

const DependencyEntryReactor = (props: { gltfComponentEntity: Entity; uuid: string; components: Component[] }) => {
const { gltfComponentEntity, uuid, components } = props
const entity = UUIDComponent.useEntityByUUID(uuid as EntityUUID) as Entity | undefined
return entity ? (
<>
{components.map((component) => {
return (
<ComponentReactor
key={component.jsonID}
gltfComponentEntity={gltfComponentEntity}
entity={entity}
component={component}
/>
)
})}
</>
) : null
}

const DependencyReactor = (props: { gltfComponentEntity: Entity; dependencies: ComponentDependencies }) => {
const { gltfComponentEntity, dependencies } = props
const entries = Object.entries(dependencies)

return (
<>
{entries.map(([uuid, components]) => {
return (
<DependencyEntryReactor
key={uuid}
gltfComponentEntity={gltfComponentEntity}
uuid={uuid}
components={components}
/>
)
})}
</>
)
}

const onError = (error: ErrorEvent) => {
// console.error(error)
}
Expand Down Expand Up @@ -187,6 +302,9 @@ const useGLTFDocument = (url: string, entity: Entity) => {
json = migrateSceneJSONToGLTF(json)
}

const dependencies = buildComponentDependencies(json)
state.dependencies.set(dependencies)

dispatchAction(
GLTFSnapshotAction.createSnapshot({
source: getComponent(entity, SourceComponent),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,7 @@ export const ParticleSystemComponent = defineComponent({
const metadata = useHookstate({ textures: {}, geometries: {}, materials: {} } as ParticleSystemMetadata)
const sceneID = useOptionalComponent(entity, SourceComponent)?.value
const rootEntity = useHookstate(getMutableState(GLTFSourceState))[sceneID ?? ''].value
const rootGLTF = useOptionalComponent(rootEntity, GLTFComponent)
const sceneLoaded = GLTFComponent.useSceneLoaded(rootEntity)
const refreshed = useHookstate(false)

const [geoDependency] = useGLTF(componentState.value.systemParameters.instancingGeometry!, entity, (url) => {
Expand All @@ -890,15 +890,15 @@ export const ParticleSystemComponent = defineComponent({
})
//@todo: this is a hack to make trail rendering mode work correctly. We need to find out why an additional snapshot is needed
useEffect(() => {
if (rootGLTF?.value?.progress !== 100) return
if (!sceneLoaded) return
if (refreshed.value) return

//if (componentState.systemParameters.renderMode.value === RenderMode.Trail) {
const snapshot = GLTFSnapshotState.cloneCurrentSnapshot(sceneID!)
dispatchAction(GLTFSnapshotAction.createSnapshot(snapshot))
//}
refreshed.set(true)
}, [rootGLTF?.value?.progress])
}, [sceneLoaded])

useEffect(() => {
//add dud material
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ export const VisualScriptComponent = defineComponent({
})

const LoadReactor = (props: { entity: Entity; gltfAncestor: Entity }) => {
const gltfComponent = useComponent(props.gltfAncestor, GLTFComponent)
const loaded = gltfComponent.progress.value === 100
const loaded = GLTFComponent.useSceneLoaded(props.gltfAncestor)

useEffect(() => {
setComponent(props.entity, VisualScriptComponent, { run: true })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export type PluginType = PluginObjectType | typeof Material.prototype.onBeforeCo
/**@deprecated Use setPlugin instead */
export function addOBCPlugin(material: Material, plugin: PluginType): void {
material.onBeforeCompile = plugin as any
console.log(material.onBeforeCompile)
material.needsUpdate = true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ const ViewportDnD = ({ children }: { children: React.ReactNode }) => {
const SceneLoadingProgress = ({ rootEntity }) => {
const { t } = useTranslation()
const progress = useComponent(rootEntity, GLTFComponent).progress.value
const loaded = GLTFComponent.useSceneLoaded(rootEntity)
const resourcePendingQuery = useQuery([ResourcePendingComponent])
const root = getComponent(rootEntity, SourceComponent)
const sceneModified = useHookstate(getMutableState(GLTFModifiedState)[root]).value
Expand All @@ -142,12 +143,13 @@ const SceneLoadingProgress = ({ rootEntity }) => {
}
}, [sceneModified])

if (progress === 100) return null
if (loaded) return null

return (
<LoadingView
fullSpace
className="block h-12 w-12"
containerClassname="absolute bg-black bg-opacity-70"
title={t('editor:loadingScenesWithProgress', { progress, assetsLeft: resourcePendingQuery.length })}
/>
)
Expand Down Expand Up @@ -183,8 +185,8 @@ const ViewPortPanelContainer = () => {
{sceneName.value ? <GizmoTool viewportRef={ref} toolbarRef={toolbarRef} /> : null}
{sceneName.value ? (
<>
{rootEntity.value && <SceneLoadingProgress key={rootEntity.value} rootEntity={rootEntity.value} />}
<div id="engine-renderer-canvas-container" ref={ref} className="absolute h-full w-full" />
{rootEntity.value && <SceneLoadingProgress key={rootEntity.value} rootEntity={rootEntity.value} />}
</>
) : (
<div className="flex h-full w-full flex-col justify-center gap-2">
Expand Down