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',