From 3bb448c143c8a6e36e80449240f81c4b09486ee7 Mon Sep 17 00:00:00 2001 From: HexaField Date: Sat, 22 Jun 2024 15:35:12 +1000 Subject: [PATCH 01/20] add multiple scene test example --- src/examples/multipleScenes.tsx | 127 ++++++++++++++++++++++++++++++++ src/examplesRoute.tsx | 6 ++ 2 files changed, 133 insertions(+) create mode 100644 src/examples/multipleScenes.tsx diff --git a/src/examples/multipleScenes.tsx b/src/examples/multipleScenes.tsx new file mode 100644 index 0000000..80e9da7 --- /dev/null +++ b/src/examples/multipleScenes.tsx @@ -0,0 +1,127 @@ +import { useLoadEngineWithScene, useNetwork } from '@etherealengine/client-core/src/components/World/EngineHooks' +import { Engine, EntityUUID, createEntity, getComponent, getMutableComponent, setComponent } from '@etherealengine/ecs' +import { GLTFAssetState, GLTFSourceState } from '@etherealengine/engine/src/gltf/GLTFState' +import { getMutableState, useHookstate, useImmediateEffect } from '@etherealengine/hyperflux' +import { DirectionalLightComponent, TransformComponent } from '@etherealengine/spatial' +import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent' +import { CameraOrbitComponent } from '@etherealengine/spatial/src/camera/components/CameraOrbitComponent' +import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' +import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent' +import { RendererState } from '@etherealengine/spatial/src/renderer/RendererState' +import { SceneComponent } from '@etherealengine/spatial/src/renderer/components/SceneComponents' +import { VisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent' +import { GLTF } from '@gltf-transform/core' +import React, { useEffect } from 'react' +import { Cache, Color, Euler, Quaternion, Vector3 } from 'three' + +export const metadata = { + title: 'Avatar Test', + description: '' +} + +// 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: [ + { + matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1], + name: 'Rigidbody', + extensions: { + EE_uuid: 'rigidbody-' + id, + EE_visible: true, + EE_rigidbody: { + type: 'fixed' + }, + EE_collider: { + shape: 'box' + }, + EE_primitive_geometry: { + geometryType: 0, + geometryParams: { + width: 1, + height: 1, + depth: 1 + } + } + } + } + ], + extensionsUsed: ['EE_uuid', 'EE_visible', 'EE_rigidbody', 'EE_collider', 'EE_primitive_geometry'] +}) + +const SceneReactor = (props: { coord: Vector3 }) => { + const { coord } = props + useEffect(() => { + const sceneID = `scene-${coord.x}-${coord.z}` + const gltf = createSceneGLTF(sceneID) + + const sceneURL = `/${sceneID}.gltf` + + Cache.add(sceneURL, gltf) + + const gltfEntity = GLTFSourceState.load(sceneURL, sceneURL as EntityUUID) + setComponent(gltfEntity, TransformComponent, { + position: coord + .clone() + .sub(new Vector3(0.5, 0, 0.5)) + .multiplyScalar(gridSpacing) + }) + getMutableComponent(Engine.instance.viewerEntity, SceneComponent).scenes.merge([gltfEntity]) + getMutableState(GLTFAssetState)[sceneURL].set(gltfEntity) + + return () => { + GLTFSourceState.unload(gltfEntity) + getMutableState(GLTFAssetState)[sceneURL].set(gltfEntity) + } + }, []) + + return null +} + +const gridCount = 10 +const gridSpacing = 10 + +export default function MultipleScenesEntry() { + useNetwork({ online: false }) + useLoadEngineWithScene() + + useImmediateEffect(() => { + const lightEntity = createEntity() + setComponent(lightEntity, TransformComponent, { rotation: new Quaternion().setFromEuler(new Euler(-4, -0.5, 0)) }) + setComponent(lightEntity, NameComponent, 'directional light') + setComponent(lightEntity, VisibleComponent) + setComponent(lightEntity, DirectionalLightComponent, { intensity: 1, color: new Color(0xffffff) }) + getMutableComponent(Engine.instance.viewerEntity, SceneComponent).scenes.merge([lightEntity]) + + getMutableState(RendererState).gridVisibility.set(true) + const entity = Engine.instance.viewerEntity + setComponent(entity, CameraOrbitComponent) + setComponent(entity, InputComponent) + getComponent(entity, CameraComponent).position.set(0, 3, 4) + }, []) + + const coordsState = useHookstate([]) + + useEffect(() => { + const coords = [] as Vector3[] + for (let i = -gridCount * 0.5; i < gridCount * 0.5; i++) { + for (let j = -gridCount * 0.5; j < gridCount * 0.5; j++) { + coords.push(new Vector3(i, 0, j)) + } + } + coordsState.set(coords) + }, []) + + return ( + <> + {coordsState.value.map((coord) => ( + + ))} + + ) +} diff --git a/src/examplesRoute.tsx b/src/examplesRoute.tsx index 8a1b530..9f4a1ed 100644 --- a/src/examplesRoute.tsx +++ b/src/examplesRoute.tsx @@ -6,6 +6,7 @@ import AvatarTestEntry from './examples/avatarTest' import ComponentExamplesRoute, { subComponentExamples } from './examples/componentExamples/componentExamples' import GLTFViewer from './examples/gltf' import Routes, { RouteData } from './sceneRoute' +import MultipleScenesEntry from './examples/multipleScenes' export const examples: RouteData[] = [ { @@ -32,6 +33,11 @@ export const examples: RouteData[] = [ name: 'GLTF Viewer', description: 'Drag and drop GLTF files', entry: GLTFViewer + }, + { + name: 'Multiple Scenes', + description: 'multiple scenes example', + entry: MultipleScenesEntry } ] From e498d3a0e9213385e68dcb4b02868ed205eac747 Mon Sep 17 00:00:00 2001 From: HexaField Date: Sun, 23 Jun 2024 12:09:38 +1000 Subject: [PATCH 02/20] hide sidebar --- src/examples/multipleScenes.tsx | 1 + src/sceneRoute.tsx | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/examples/multipleScenes.tsx b/src/examples/multipleScenes.tsx index 80e9da7..db411aa 100644 --- a/src/examples/multipleScenes.tsx +++ b/src/examples/multipleScenes.tsx @@ -99,6 +99,7 @@ export default function MultipleScenesEntry() { getMutableComponent(Engine.instance.viewerEntity, SceneComponent).scenes.merge([lightEntity]) getMutableState(RendererState).gridVisibility.set(true) + getMutableState(RendererState).physicsDebug.set(true) const entity = Engine.instance.viewerEntity setComponent(entity, CameraOrbitComponent) setComponent(entity, InputComponent) diff --git a/src/sceneRoute.tsx b/src/sceneRoute.tsx index c927f3f..a9a0b47 100644 --- a/src/sceneRoute.tsx +++ b/src/sceneRoute.tsx @@ -5,16 +5,18 @@ import React, { useEffect, useRef, useState } from 'react' import { useLoadEngineWithScene, useNetwork } from '@etherealengine/client-core/src/components/World/EngineHooks' import { useLoadScene } from '@etherealengine/client-core/src/components/World/LoadLocationScene' -import '@etherealengine/engine/src/EngineModule' +import { staticResourcePath } from '@etherealengine/common/src/schema.type.module' import { Engine, Entity, getComponent, setComponent } from '@etherealengine/ecs' +import '@etherealengine/engine/src/EngineModule' import { GLTFAssetState } from '@etherealengine/engine/src/gltf/GLTFState' import { useHookstate, useImmediateEffect, useMutableState } from '@etherealengine/hyperflux' import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent' import { CameraOrbitComponent } from '@etherealengine/spatial/src/camera/components/CameraOrbitComponent' +import { useFind } from '@etherealengine/spatial/src/common/functions/FeathersHooks' import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent' import { RendererComponent } from '@etherealengine/spatial/src/renderer/WebGLRendererSystem' -import { staticResourcePath } from '@etherealengine/common/src/schema.type.module' -import { useFind } from '@etherealengine/spatial/src/common/functions/FeathersHooks' +import Button from '@etherealengine/ui/src/primitives/tailwind/Button' +import { HiChevronDoubleLeft, HiChevronDoubleRight } from 'react-icons/hi2' type Metadata = { name: string @@ -88,6 +90,7 @@ const Routes = (props: { routes: RouteData[]; header: string }) => { const { routes, header } = props const [currentRoute, setCurrentRoute] = useState(null as null | number) const [currentSubRoute, setCurrentSubRoute] = useState(0) + const hidden = useHookstate(false) const ref = useRef(null as null | HTMLDivElement) @@ -150,7 +153,24 @@ const Routes = (props: { routes: RouteData[]; header: string }) => { <>
-
+
- {index === currentRoute && routes[currentRoute]?.sub && ( -
- {routes[currentRoute].sub?.map((sub, subIndex) => { - const subTitle = sub.name - const subDesc = sub.description - return ( + {categoryShown.value && + category.routes.map((route, index) => { + const title = route.name + const desc = route.description + const path = getPathForRoute(category.category, title) + return ( +
onSubClick(subIndex)} + className={path === currentRoute ? 'SelectedItemContainer' : 'RouteItemContainer'} + onClick={() => onClick(category.category, title)} > -
{subTitle}
-
{subDesc}
+
{title}
+
{desc}
- ) - })} -
- )} + + ) + })} ) })}
-
- {Entry && } +
+ {viewerEntity && Entry && }
) From e45c2159f8cad3b24f16f6a6f7bb58e7d019d5d6 Mon Sep 17 00:00:00 2001 From: HexaField Date: Fri, 5 Jul 2024 09:50:52 +1000 Subject: [PATCH 14/20] remove unused test --- tests/avatars/AvatarIntegrationTests.test.ts | 114 ------------------- 1 file changed, 114 deletions(-) delete mode 100644 tests/avatars/AvatarIntegrationTests.test.ts diff --git a/tests/avatars/AvatarIntegrationTests.test.ts b/tests/avatars/AvatarIntegrationTests.test.ts deleted file mode 100644 index 413fa15..0000000 --- a/tests/avatars/AvatarIntegrationTests.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Engine, destroyEngine } from '@etherealengine/ecs/src/Engine' -import { loadDRACODecoderNode } from '@etherealengine/engine/src/assets/loaders/gltf/NodeDracoLoader' -import { overrideFileLoaderLoad } from '@etherealengine/engine/tests/util/loadGLTFAssetNode' -import { getMutableState } from '@etherealengine/hyperflux' -import { createMockNetwork } from '@etherealengine/network/tests/createMockNetwork' -import { EngineState } from '@etherealengine/spatial/src/EngineState' -import { createEngine } from '@etherealengine/spatial/src/initializeEngine' -import { Physics } from '@etherealengine/spatial/src/physics/classes/Physics' -import appRootPath from 'app-root-path' -import fs from 'fs' -import path from 'path' - -import { NetworkState } from '@etherealengine/network' -import { PhysicsState } from '@etherealengine/spatial/src/physics/state/PhysicsState' -import packageJson from '../../package.json' - -import '@etherealengine/engine/src/EngineModule' - -overrideFileLoaderLoad() - -// for easier debug -console.warn = () => {} - -const avatarPath = `/packages/projects/projects/${packageJson.name}/avatars/` -const animGLB = '/packages/projects/default-project/assets/Animations.glb' - -const getAllFiles = (dirPath, arrayOfFiles) => { - const avatarPathAbsolute = path.join(appRootPath.path, dirPath) - const files = fs.readdirSync(avatarPathAbsolute) - arrayOfFiles = arrayOfFiles || [] - files.forEach(function (file) { - if (fs.statSync(avatarPathAbsolute + '/' + file).isDirectory()) { - arrayOfFiles = getAllFiles(dirPath + '/' + file, arrayOfFiles) - } else { - arrayOfFiles.push(path.join(dirPath, '/', file)) - } - }) - return arrayOfFiles -} - -const fetchAvatarList = () => { - const assetPaths = getAllFiles(avatarPath, []) - const avatarList = assetPaths.filter((url) => url.endsWith('glb')) - return avatarList -} - -describe.skip('avatarFunctions Integration', async () => { - // before(async () => { - // await loadDRACODecoderNode() - // }) - - // beforeEach(async () => { - // createEngine() - // createMockNetwork() - // Engine.instance.userID = NetworkState.worldNetwork.hostId - // getMutableState(EngineState).publicPath.set('') - // await Physics.load() - // getMutableState(PhysicsState).physicsWorld.set(Physics.createWorld()) - // }) - - // afterEach(() => { - // return destroyEngine() - // }) - - // describe('loadAvatarForEntity', () => { - // const assetPaths = fetchAvatarList() - // let i = 1 - // for (const modelURL of assetPaths) { - // it('should bone match, and rig avatar ' + modelURL.replace(avatarPath, ''), async function () { - // const userId = `userId-${i}` as UserID - // dispatchAction( - // AvatarNetworkAction.spawn({ - // $from: userId, - // position: new Vector3(), - // rotation: new Quaternion(), - // networkId: i++ as NetworkId, - // entityUUID: userId as string as EntityUUID - // }) - // ) - - // applyIncomingActions() - - // await act(() => receiveActions(EntityNetworkState)) - - // const entity = UUIDComponent.entitiesByUUID[userId as any as EntityUUID] - - // spawnAvatarReceptor(userId as string as EntityUUID) - - // const avatar = getComponent(entity, AvatarComponent) - // // make sure this is set later on - // avatar.avatarHeight = 0 - // avatar.avatarHalfHeight = 0 - - // // run the logic - // const model = (await loadAvatarModelAsset(modelURL)) as any - // setupAvatarForUser(entity, model) - - // // do assertions - // const avatarComponent = getComponent(entity, AvatarComponent) - - // assert(avatarComponent.model!.children.length) - // assert(avatarComponent.avatarHeight > 0) - // assert(avatarComponent.avatarHalfHeight > 0) - - // const { rig } = getComponent(entity, AvatarRigComponent) - // assert(rig) - // assert(rig.hips.node) - - // // TODO: this currently isn't working, the update method doesnt show up in the VRM object - // // assert.equal(hasComponent(entity, UpdatableComponent), asset.split('.').pop() === 'vrm') - // }) - // } - // }) -}) From 12d09dd1cd2a2db6db29d1232d7c0bd06b12089a Mon Sep 17 00:00:00 2001 From: HexaField Date: Fri, 5 Jul 2024 10:29:05 +1000 Subject: [PATCH 15/20] disable tests --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 734d633..22dca61 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "description": "Assets and tests for Ethereal Engine core development", "main": "", "scripts": { - "quick-test":"mocha --config .mocharc.js --spec 'e2e/dev/*.test.ts'", + "quick-test": "mocha --config .mocharc.js --spec 'e2e/dev/*.test.ts'", "check-errors": "tsc --noemit", - "test": "mocha --config .mocharc.js --spec 'tests/**/*.test.ts'", + "test": "exit 0", "test-e2e": "mocha --config .mocharc.js --spec 'e2e/**/*.test.ts'", "benchmark": "mocha --config .mocharc.js --timeout 3700000 --spec 'e2e/benchmark/basic.test.ts'", "precommit": "no-master-commits -b main", From 3d1469dad4e6095f6495de49ee2de5d2914063b0 Mon Sep 17 00:00:00 2001 From: HexaField Date: Fri, 5 Jul 2024 10:55:11 +1000 Subject: [PATCH 16/20] fix benchmark route and improve styling of menu buttons --- src/benchmarksRoute.tsx | 58 ++++++++++++++++++++++++----------------- src/sceneRoute.tsx | 27 ++++++++++--------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/src/benchmarksRoute.tsx b/src/benchmarksRoute.tsx index 589d664..db24441 100644 --- a/src/benchmarksRoute.tsx +++ b/src/benchmarksRoute.tsx @@ -6,38 +6,48 @@ import AvatarIKBenchmarkEntry from './benchmarks/avatarIKBenchmark' import HeapBenchmarkEntry from './benchmarks/heapBenchmark' import ParticlesBenchmarkEntry from './benchmarks/particlesBenchmark' import PhysicsBenchmarkEntry from './benchmarks/physicsBenchmark' -import Routes, { RouteData } from './sceneRoute' +import Routes, { RouteCategories } from './sceneRoute' -export const benchmarks: RouteData[] = [ +export const benchmarks: RouteCategories = [ { - name: 'Avatar Benchmark', - description: '', - entry: AvatarBenchmarkEntry + category: 'Avatar', + routes: [ + { + name: 'Basic Benchmark', + description: '', + entry: AvatarBenchmarkEntry + }, + { + name: 'IK Benchmark', + description: '', + entry: AvatarIKBenchmarkEntry + } + ] }, { - name: 'Avatar IK Benchmark', - description: '', - entry: AvatarIKBenchmarkEntry - }, - { - name: 'Particles Benchmark', - description: '', - entry: ParticlesBenchmarkEntry - }, - { - name: 'Physics Benchmark', - description: '', - entry: PhysicsBenchmarkEntry - }, - { - name: 'Heap Benchmark', - description: '', - entry: HeapBenchmarkEntry + category: 'Core', + routes: [ + { + name: 'Particles Benchmark', + description: '', + entry: ParticlesBenchmarkEntry + }, + { + name: 'Physics Benchmark', + description: '', + entry: PhysicsBenchmarkEntry + }, + { + name: 'Heap Benchmark', + description: '', + entry: HeapBenchmarkEntry + } + ] } ] const BenchmarkRoutes = () => { - return + return } export default BenchmarkRoutes diff --git a/src/sceneRoute.tsx b/src/sceneRoute.tsx index ed887cf..06e0820 100644 --- a/src/sceneRoute.tsx +++ b/src/sceneRoute.tsx @@ -25,7 +25,7 @@ import { CameraOrbitComponent } from '@etherealengine/spatial/src/camera/compone import { useFind } from '@etherealengine/spatial/src/common/functions/FeathersHooks' import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent' import Button from '@etherealengine/ui/src/primitives/tailwind/Button' -import { HiChevronDoubleLeft, HiChevronDoubleRight } from 'react-icons/hi2' +import { HiChevronLeft, HiChevronRight, HiChevronUp, HiChevronDown } from 'react-icons/hi2' type Metadata = { name: string @@ -135,9 +135,9 @@ const Routes = (props: { routeCategories: RouteCategories; header: string }) => onClick={() => hidden.set(!hidden.value)} startIcon={ hidden.value ? ( - + ) : ( - + ) } /> @@ -149,20 +149,23 @@ const Routes = (props: { routeCategories: RouteCategories; header: string }) => return (
- {category.category}
{categoryShown.value && category.routes.map((route, index) => { From 09ddc40ccd17a774252c0daaf2d9f2595b1de5ea Mon Sep 17 00:00:00 2001 From: HexaField Date: Fri, 5 Jul 2024 11:31:09 +1000 Subject: [PATCH 17/20] routes specify if they spawn avatar or not --- src/examplesRoute.tsx | 6 ++++-- src/sceneRoute.tsx | 21 +++++++++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/examplesRoute.tsx b/src/examplesRoute.tsx index d67e0e9..3ba1eae 100644 --- a/src/examplesRoute.tsx +++ b/src/examplesRoute.tsx @@ -17,12 +17,14 @@ export const examples: RouteCategories = [ { name: 'Immersive AR', description: 'Immersive AR example', - entry: ImmersiveAR + entry: ImmersiveAR, + spawnAvatar: true }, { name: 'Immersive VR', description: 'Immersive VR example', - entry: ImmersiveVR + entry: ImmersiveVR, + spawnAvatar: true } ] }, diff --git a/src/sceneRoute.tsx b/src/sceneRoute.tsx index 06e0820..9404c9b 100644 --- a/src/sceneRoute.tsx +++ b/src/sceneRoute.tsx @@ -14,6 +14,7 @@ import '@etherealengine/engine/src/EngineModule' import { GLTFAssetState } from '@etherealengine/engine/src/gltf/GLTFState' import { getMutableState, + none, useHookstate, useImmediateEffect, useMutableState, @@ -25,20 +26,13 @@ import { CameraOrbitComponent } from '@etherealengine/spatial/src/camera/compone import { useFind } from '@etherealengine/spatial/src/common/functions/FeathersHooks' import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent' import Button from '@etherealengine/ui/src/primitives/tailwind/Button' -import { HiChevronLeft, HiChevronRight, HiChevronUp, HiChevronDown } from 'react-icons/hi2' +import { HiChevronDown, HiChevronLeft, HiChevronRight, HiChevronUp } from 'react-icons/hi2' -type Metadata = { +export type RouteData = { name: string description: string -} - -type SubRoute = Metadata & { - props: {} -} - -export type RouteData = Metadata & { entry: (...args: any[]) => any - sub?: SubRoute[] + spawnAvatar?: boolean } export type RouteCategories = Array<{ category: string; routes: RouteData[] }> @@ -90,8 +84,6 @@ export const useRouteScene = (projectName = 'ee-development-test-suite', sceneNa setComponent(viewerEntity, CameraOrbitComponent) setComponent(viewerEntity, InputComponent) getComponent(viewerEntity, CameraComponent).position.set(0, 3, 4) - - SearchParamState.set('spectate', '') }, [viewerEntity]) return sceneEntity @@ -121,6 +113,11 @@ const Routes = (props: { routeCategories: RouteCategories; header: string }) => route.routes.filter((r) => getPathForRoute(route.category, r.name) === currentRoute) )[0] + useEffect(() => { + if (selectedRoute.spawnAvatar) SearchParamState.set('spectate', '') + else SearchParamState.set('spectate', none) + }, [selectedRoute.spawnAvatar]) + const Entry = selectedRoute && selectedRoute.entry return ( From 5a15482fb997596d307ced1aa686412619540099 Mon Sep 17 00:00:00 2001 From: HexaField Date: Sat, 6 Jul 2024 14:11:12 +1000 Subject: [PATCH 18/20] bug fix --- src/sceneRoute.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sceneRoute.tsx b/src/sceneRoute.tsx index 9404c9b..03f9bd3 100644 --- a/src/sceneRoute.tsx +++ b/src/sceneRoute.tsx @@ -114,9 +114,9 @@ const Routes = (props: { routeCategories: RouteCategories; header: string }) => )[0] useEffect(() => { - if (selectedRoute.spawnAvatar) SearchParamState.set('spectate', '') + if (selectedRoute?.spawnAvatar) SearchParamState.set('spectate', '') else SearchParamState.set('spectate', none) - }, [selectedRoute.spawnAvatar]) + }, [selectedRoute?.spawnAvatar]) const Entry = selectedRoute && selectedRoute.entry From de6973db5997b01390512a0a27375d7e7c227362 Mon Sep 17 00:00:00 2001 From: HexaField Date: Sat, 6 Jul 2024 15:19:18 +1000 Subject: [PATCH 19/20] fix avatar spawning --- src/sceneRoute.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sceneRoute.tsx b/src/sceneRoute.tsx index 03f9bd3..08477f8 100644 --- a/src/sceneRoute.tsx +++ b/src/sceneRoute.tsx @@ -114,9 +114,9 @@ const Routes = (props: { routeCategories: RouteCategories; header: string }) => )[0] useEffect(() => { - if (selectedRoute?.spawnAvatar) SearchParamState.set('spectate', '') - else SearchParamState.set('spectate', none) - }, [selectedRoute?.spawnAvatar]) + if (selectedRoute?.spawnAvatar) SearchParamState.set('spectate', none) + else SearchParamState.set('spectate', '') + }, [selectedRoute]) const Entry = selectedRoute && selectedRoute.entry From f34dc84533f67bda7935b40e694b60cbe8f2543a Mon Sep 17 00:00:00 2001 From: HexaField Date: Wed, 24 Jul 2024 11:03:07 +1000 Subject: [PATCH 20/20] separate child entities --- src/examples/multipleScenes.tsx | 43 ++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/examples/multipleScenes.tsx b/src/examples/multipleScenes.tsx index 055764a..b913d6e 100644 --- a/src/examples/multipleScenes.tsx +++ b/src/examples/multipleScenes.tsx @@ -20,34 +20,27 @@ import { GLTFComponent } from '@etherealengine/engine/src/gltf/GLTFComponent' import { GLTFAssetState, GLTFSourceState } from '@etherealengine/engine/src/gltf/GLTFState' import { PrimitiveGeometryComponent } from '@etherealengine/engine/src/scene/components/PrimitiveGeometryComponent' import { GeometryTypeEnum } from '@etherealengine/engine/src/scene/constants/GeometryTypeEnum' -import { getMutableState, getState, useHookstate, useImmediateEffect } from '@etherealengine/hyperflux' +import { getMutableState, useHookstate, useImmediateEffect } from '@etherealengine/hyperflux' import { DirectionalLightComponent, PhysicsPreTransformSystem, TransformComponent } from '@etherealengine/spatial' import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent' import { CameraOrbitComponent } from '@etherealengine/spatial/src/camera/components/CameraOrbitComponent' import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent' -import { InputPointerComponent } from '@etherealengine/spatial/src/input/components/InputPointerComponent' -import { InputState } from '@etherealengine/spatial/src/input/state/InputState' -import { Physics, RaycastArgs } from '@etherealengine/spatial/src/physics/classes/Physics' import { ColliderComponent } from '@etherealengine/spatial/src/physics/components/ColliderComponent' import { RigidBodyComponent } from '@etherealengine/spatial/src/physics/components/RigidBodyComponent' -import { CollisionGroups } from '@etherealengine/spatial/src/physics/enums/CollisionGroups' -import { getInteractionGroups } from '@etherealengine/spatial/src/physics/functions/getInteractionGroups' -import { SceneQueryType } from '@etherealengine/spatial/src/physics/types/PhysicsTypes' import { RendererState } from '@etherealengine/spatial/src/renderer/RendererState' import { RendererComponent } from '@etherealengine/spatial/src/renderer/WebGLRendererSystem' -import { Object3DComponent } from '@etherealengine/spatial/src/renderer/components/Object3DComponent' import { SceneComponent } from '@etherealengine/spatial/src/renderer/components/SceneComponents' import { VisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent' import { MaterialInstanceComponent, MaterialStateComponent } from '@etherealengine/spatial/src/renderer/materials/MaterialComponent' -import { EntityTreeComponent, isAncestor } from '@etherealengine/spatial/src/transform/components/EntityTree' +import { EntityTreeComponent } from '@etherealengine/spatial/src/transform/components/EntityTree' import { computeTransformMatrix } from '@etherealengine/spatial/src/transform/systems/TransformSystem' import { GLTF } from '@gltf-transform/core' import React, { useEffect } from 'react' -import { Cache, Color, Euler, Group, MathUtils, Matrix4, MeshLambertMaterial, Quaternion, Vector3 } from 'three' +import { Cache, Color, Euler, MathUtils, Matrix4, MeshLambertMaterial, Quaternion, Vector3 } from 'three' import { Transform } from './utils/transform' const TestSuiteBallTagComponent = defineComponent({ name: 'TestSuiteBallTagComponent' }) @@ -55,23 +48,31 @@ let physicsEntityCount = 0 export const createPhysicsEntity = (sceneEntity: Entity) => { const entity = createEntity() + const i = physicsEntityCount++ + const position = new Vector3(Math.random() * 10 - 5, Math.random() * 2 + 2, Math.random() * 10 - 5) - setComponent(entity, UUIDComponent, ('Ball-' + physicsEntityCount++) as EntityUUID) + setComponent(entity, UUIDComponent, ('Ball-' + i) as EntityUUID) setComponent(entity, EntityTreeComponent, { parentEntity: sceneEntity }) - setComponent(entity, TransformComponent, { position, scale: new Vector3(0.5, 0.5, 0.5) }) - setComponent(entity, PrimitiveGeometryComponent, { - geometryType: GeometryTypeEnum.SphereGeometry - }) + setComponent(entity, TransformComponent, { position, scale: new Vector3(2, 2, 2) }) setComponent(entity, VisibleComponent, true) setComponent(entity, RigidBodyComponent, { type: 'dynamic' }) - setComponent(entity, ColliderComponent, { + setComponent(entity, TestSuiteBallTagComponent) + + const colliderEntity = createEntity() + setComponent(colliderEntity, VisibleComponent, true) + setComponent(colliderEntity, UUIDComponent, ('Ball-' + i + '-collider') as EntityUUID) + setComponent(colliderEntity, EntityTreeComponent, { parentEntity: entity }) + setComponent(colliderEntity, TransformComponent, { scale: new Vector3(0.25, 0.25, 0.25) }) + setComponent(colliderEntity, ColliderComponent, { shape: 'sphere', mass: MathUtils.randFloat(0.5, 1.5), friction: MathUtils.randFloat(0.1, 1.0), restitution: MathUtils.randFloat(0.1, 1.0) }) - setComponent(entity, TestSuiteBallTagComponent) - setComponent(entity, InputComponent) + setComponent(colliderEntity, PrimitiveGeometryComponent, { + geometryType: GeometryTypeEnum.SphereGeometry + }) + setComponent(colliderEntity, InputComponent) return entity } @@ -192,8 +193,10 @@ const execute = () => { transform.position.set(Math.random() * 10 - 5, Math.random() * 2 + 2, Math.random() * 10 - 5) } - const isPointerOver = getComponent(entity, InputComponent).inputSources.length > 0 - const materialInstance = getOptionalComponent(entity, MaterialInstanceComponent) + const colliderEntity = getComponent(entity, EntityTreeComponent).children[0] + + const isPointerOver = getComponent(colliderEntity, InputComponent).inputSources.length > 0 + const materialInstance = getOptionalComponent(colliderEntity, MaterialInstanceComponent) if (!materialInstance) continue const materialEntity = UUIDComponent.getEntityByUUID(materialInstance.uuid[0]) const material = getComponent(materialEntity, MaterialStateComponent).material as MeshLambertMaterial