diff --git a/src/examples/GLTFs.tsx b/src/examples/GLTFs.tsx index c194cff..64e9cad 100644 --- a/src/examples/GLTFs.tsx +++ b/src/examples/GLTFs.tsx @@ -157,6 +157,11 @@ export const gltfRoutes = [ description: 'Ethereal Engine Material Extension', /** @todo currently relies on eepro advanced materials project - replace asset with one that has base custom material */ entry: () => + }, + { + name: 'VRM', + description: 'VRM Avatar', + entry: () => } ] as RouteData[] diff --git a/src/examples/avatarSimple.tsx b/src/examples/avatarSimple.tsx new file mode 100644 index 0000000..d8881d4 --- /dev/null +++ b/src/examples/avatarSimple.tsx @@ -0,0 +1,100 @@ +import { dispatchAction, getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' +import { GLTF } from '@gltf-transform/core' +import { useEffect } from 'react' +import { Cache, Color, Euler, Quaternion } from 'three' + +import { AvatarID } from '@etherealengine/common/src/schema.type.module' +import { + Engine, + EntityUUID, + UUIDComponent, + UndefinedEntity, + createEntity, + getComponent, + getMutableComponent, + setComponent, + useOptionalComponent +} from '@etherealengine/ecs' +import { AvatarNetworkAction } from '@etherealengine/engine/src/avatar/state/AvatarNetworkActions' +import { GLTFComponent } from '@etherealengine/engine/src/gltf/GLTFComponent' +import { GLTFAssetState, GLTFSourceState } from '@etherealengine/engine/src/gltf/GLTFState' +import { AmbientLightComponent, DirectionalLightComponent, TransformComponent } from '@etherealengine/spatial' +import { EngineState } from '@etherealengine/spatial/src/EngineState' +import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' +import { RendererComponent } from '@etherealengine/spatial/src/renderer/WebGLRendererSystem' +import { SceneComponent } from '@etherealengine/spatial/src/renderer/components/SceneComponents' +import { VisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent' +import { EntityTreeComponent } from '@etherealengine/spatial/src/transform/components/EntityTree' + +// create scene with a rigidbody loaded offset from the origin +const createSceneGLTF = (id: string): GLTF.IGLTF => ({ + asset: { + version: '2.0', + generator: 'iR Engine' + }, + scenes: [{ nodes: [0] }], + scene: 0, + nodes: [ + { + name: 'Ground Plane', + extensions: { + EE_uuid: 'ground-plane', + EE_visible: true, + EE_ground_plane: {} + } + } + ], + extensionsUsed: ['EE_uuid', 'EE_visible', 'EE_ground_plane'] +}) + +export default function AvatarSimpleEntry() { + const entity = useHookstate(UndefinedEntity) + const gltfComponent = useOptionalComponent(entity.value, GLTFComponent) + + useEffect(() => { + const lightEntity = createEntity() + setComponent(lightEntity, UUIDComponent, 'directional light' as EntityUUID) + setComponent(lightEntity, NameComponent, 'Directional Light') + setComponent(lightEntity, TransformComponent, { rotation: new Quaternion().setFromEuler(new Euler(2, 5, 3)) }) + setComponent(lightEntity, EntityTreeComponent, { parentEntity: getState(EngineState).originEntity }) + setComponent(lightEntity, VisibleComponent, true) + setComponent(lightEntity, DirectionalLightComponent, { color: new Color('white'), intensity: 0.5 }) + setComponent(lightEntity, AmbientLightComponent, { color: new Color('white'), intensity: 0.5 }) + + const sceneID = `scene` + const gltf = createSceneGLTF(sceneID) + + const sceneURL = `/${sceneID}.gltf` + + Cache.add(sceneURL, gltf) + + const gltfEntity = GLTFSourceState.load(sceneURL, sceneURL as EntityUUID) + getMutableComponent(Engine.instance.viewerEntity, RendererComponent).scenes.merge([gltfEntity]) + setComponent(gltfEntity, SceneComponent) + getMutableState(GLTFAssetState)[sceneURL].set(gltfEntity) + + entity.set(gltfEntity) + + return () => { + GLTFSourceState.unload(gltfEntity) + getMutableState(GLTFAssetState)[sceneURL].set(gltfEntity) + } + }, []) + + useEffect(() => { + if (gltfComponent?.progress?.value !== 100) return + + const parentUUID = getComponent(entity.value, UUIDComponent) + const entityUUID = Engine.instance.userID + dispatchAction( + AvatarNetworkAction.spawn({ + parentUUID, + avatarID: '9cc0a253-aeec-45d6-86cb-ab08f094c09d' as AvatarID, + entityUUID: (entityUUID + '_avatar') as EntityUUID, + name: 'avatar' + }) + ) + }, [gltfComponent?.progress?.value]) + + return null +} diff --git a/src/examples/gltfViewer.tsx b/src/examples/gltfViewer.tsx index 26295a6..eb90bca 100644 --- a/src/examples/gltfViewer.tsx +++ b/src/examples/gltfViewer.tsx @@ -29,12 +29,13 @@ export const metadata = { } const loadOldModel = false +const defaultSource = config.client.fileServer + '/projects/default-project/assets/apartment.glb' // const defaultSource = config.client.fileServer + '/projects/ee-development-test-suite/assets/GLTF/Duck/basic/Duck.gltf' // const defaultSource = config.client.fileServer + '/projects/ee-development-test-suite/assets/GLTF/Duck/binary/Duck.glb' // const defaultSource = config.client.fileServer + '/projects/ee-development-test-suite/assets/GLTF/Duck/draco/Duck.gltf' // const defaultSource = config.client.fileServer + '/projects/ee-development-test-suite/assets/GLTF/Duck/embedded/Duck.gltf' // const defaultSource = config.client.fileServer + '/projects/ee-development-test-suite/assets/GLTF/Duck/quantized/Duck.gltf' -const defaultSource = 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/main/2.0/UnlitTest/glTF-Binary/UnlitTest.glb' +// const defaultSource = 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/main/2.0/UnlitTest/glTF-Binary/UnlitTest.glb' const GLTF = () => { diff --git a/src/examplesRoute.tsx b/src/examplesRoute.tsx index 69d377f..fb12cdb 100644 --- a/src/examplesRoute.tsx +++ b/src/examplesRoute.tsx @@ -1,7 +1,10 @@ import React from 'react' +import { useEngineInjection } from '@etherealengine/client-core/src/components/World/EngineHooks' import '@etherealengine/engine/src/EngineModule' +import { gltfRoutes } from './examples/GLTFs' import AvatarMocapEntry from './examples/avatarMocap' +import AvatarSimpleEntry from './examples/avatarSimple' import AvatarTestEntry from './examples/avatarTest' import ComponentExamplesRoute, { subComponentExamples } from './examples/componentExamples/componentExamples' import GLTFViewer from './examples/gltfViewer' @@ -9,8 +12,6 @@ import ImmersiveAR from './examples/immersiveAR' import ImmersiveVR from './examples/immersiveVR' import MultipleScenesEntry from './examples/multipleScenes' import Routes, { RouteCategories } from './sceneRoute' -import { gltfRoutes } from './examples/GLTFs' -import { useEngineInjection } from '@etherealengine/client-core/src/components/World/EngineHooks' export const examples: RouteCategories = [ { @@ -41,6 +42,11 @@ export const examples: RouteCategories = [ { category: 'Avatar', routes: [ + { + name: 'Simple', + description: 'Avatar simple example', + entry: AvatarSimpleEntry + }, { name: 'Mocap', description: 'Avatar mocap example',