From 8d3d37671a13b2b61daa02a234fcfee011c4528e Mon Sep 17 00:00:00 2001 From: HexaField Date: Mon, 24 Jun 2024 13:30:23 +1000 Subject: [PATCH] add scene placement and updates --- src/examples/multipleScenes.tsx | 85 +++++++++++++++++++++++++------- src/examples/scenePlacement.tsx | 72 +++++++++++++++++++++++++++ src/examples/utils/transform.tsx | 46 +++++++++++++++++ src/examplesRoute.tsx | 6 +++ src/sceneRoute.tsx | 2 +- 5 files changed, 193 insertions(+), 18 deletions(-) create mode 100644 src/examples/scenePlacement.tsx create mode 100644 src/examples/utils/transform.tsx diff --git a/src/examples/multipleScenes.tsx b/src/examples/multipleScenes.tsx index db411aa..31fea14 100644 --- a/src/examples/multipleScenes.tsx +++ b/src/examples/multipleScenes.tsx @@ -1,5 +1,13 @@ import { useLoadEngineWithScene, useNetwork } from '@etherealengine/client-core/src/components/World/EngineHooks' -import { Engine, EntityUUID, createEntity, getComponent, getMutableComponent, setComponent } from '@etherealengine/ecs' +import { + Engine, + EntityUUID, + UndefinedEntity, + 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' @@ -10,14 +18,11 @@ import { InputComponent } from '@etherealengine/spatial/src/input/components/Inp 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 { computeTransformMatrix } from '@etherealengine/spatial/src/transform/systems/TransformSystem' 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: '' -} +import { Cache, Color, Euler, Matrix4, Quaternion, Vector3 } from 'three' +import { Transform } from './utils/transform' // create scene with a rigidbody loaded offset from the origin const createSceneGLTF = (id: string): GLTF.IGLTF => ({ @@ -54,8 +59,14 @@ const createSceneGLTF = (id: string): GLTF.IGLTF => ({ extensionsUsed: ['EE_uuid', 'EE_visible', 'EE_rigidbody', 'EE_collider', 'EE_primitive_geometry'] }) -const SceneReactor = (props: { coord: Vector3 }) => { - const { coord } = props +const SceneReactor = (props: { + coord: Vector3 + transform: { position: Vector3; rotation: Quaternion; scale: Vector3 } +}) => { + const { coord, transform } = props + + const gltfEntityState = useHookstate(UndefinedEntity) + useEffect(() => { const sceneID = `scene-${coord.x}-${coord.z}` const gltf = createSceneGLTF(sceneID) @@ -65,25 +76,56 @@ const SceneReactor = (props: { coord: Vector3 }) => { 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) + gltfEntityState.set(gltfEntity) + return () => { GLTFSourceState.unload(gltfEntity) getMutableState(GLTFAssetState)[sceneURL].set(gltfEntity) } }, []) + useEffect(() => { + const gltfEntity = gltfEntityState.value + + // reset transform + setComponent(gltfEntity, TransformComponent, { + position: coord + .clone() + .sub(new Vector3(0.5, 0, 0.5)) + .multiplyScalar(gridSpacing), + rotation: new Quaternion(), + scale: new Vector3(0.5, 0.5, 0.5) + }) + + // apply transform state + const transformComponent = getComponent(gltfEntity, TransformComponent) + const mat4 = new Matrix4() + transformComponent.matrix.multiply(mat4.compose(transform.position, transform.rotation, transform.scale)) + transformComponent.matrix.decompose( + transformComponent.position, + transformComponent.rotation, + transformComponent.scale + ) + computeTransformMatrix(gltfEntity) + + console.log('position', transformComponent.position.x, transformComponent.position.y, transformComponent.position.z) + console.log( + 'rotation', + transformComponent.rotation.x, + transformComponent.rotation.y, + transformComponent.rotation.z, + transformComponent.rotation.w + ) + console.log('scale', transformComponent.scale.x, transformComponent.scale.y, transformComponent.scale.z) + }, [transform.position, transform.rotation, transform.scale]) + return null } -const gridCount = 10 +const gridCount = 5 const gridSpacing = 10 export default function MultipleScenesEntry() { @@ -118,11 +160,20 @@ export default function MultipleScenesEntry() { coordsState.set(coords) }, []) + const transformState = useHookstate({ + position: new Vector3(), + rotation: new Quaternion(), + scale: new Vector3(1, 1, 1) + }) + return ( <> {coordsState.value.map((coord) => ( - + ))} +
+ +
) } diff --git a/src/examples/scenePlacement.tsx b/src/examples/scenePlacement.tsx new file mode 100644 index 0000000..75e9452 --- /dev/null +++ b/src/examples/scenePlacement.tsx @@ -0,0 +1,72 @@ +import { useLoadEngineWithScene, useNetwork } from '@etherealengine/client-core/src/components/World/EngineHooks' +import { Engine, getComponent, setComponent } from '@etherealengine/ecs' +import { getMutableState, useHookstate, useImmediateEffect } from '@etherealengine/hyperflux' +import { TransformComponent } from '@etherealengine/spatial' +import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent' +import { CameraOrbitComponent } from '@etherealengine/spatial/src/camera/components/CameraOrbitComponent' +import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent' +import { RendererState } from '@etherealengine/spatial/src/renderer/RendererState' +import { updateWorldOriginFromScenePlacement } from '@etherealengine/spatial/src/transform/updateWorldOrigin' +import { XRState } from '@etherealengine/spatial/src/xr/XRState' +import React, { useEffect } from 'react' +import { Quaternion, Vector3 } from 'three' +import { useRouteScene } from '../sceneRoute' +import { Transform } from './utils/transform' + +export default function ScenePlacement() { + const sceneEntity = useRouteScene('default-project', 'public/scenes/apartment.gltf') + useNetwork({ online: false }) + useLoadEngineWithScene() + + useImmediateEffect(() => { + getMutableState(RendererState).gridVisibility.set(true) + getMutableState(RendererState).physicsDebug.set(true) + const entity = Engine.instance.viewerEntity + setComponent(entity, CameraOrbitComponent) + setComponent(entity, InputComponent) + getComponent(entity, CameraComponent).position.set(0, 3, 4) + }, []) + + /** Origin Transform */ + const transformState = useHookstate({ + position: new Vector3(), + rotation: new Quaternion(), + scale: new Vector3(1, 1, 1) + }) + + useEffect(() => { + const xrState = getMutableState(XRState) + xrState.scenePosition.value.copy(transformState.position.value) + xrState.sceneRotation.value.copy(transformState.rotation.value) + xrState.sceneScale.set(transformState.scale.value.x) + updateWorldOriginFromScenePlacement() + }, [transformState.position, transformState.rotation, transformState.scale]) + + /** Scene Transform */ + const transformState2 = useHookstate({ + position: new Vector3(2, 0, 2), + rotation: new Quaternion(), + scale: new Vector3().setScalar(0.1) + }) + + useEffect(() => { + if (!sceneEntity.value) return + setComponent(sceneEntity.value, TransformComponent, { + position: transformState2.position.value, + rotation: transformState2.rotation.value, + scale: transformState2.scale.value + }) + }, [sceneEntity, transformState2.position, transformState2.rotation, transformState2.scale]) + + return ( +
+ + +
+ ) +} + +/** + * Scene placement + * - + */ diff --git a/src/examples/utils/transform.tsx b/src/examples/utils/transform.tsx new file mode 100644 index 0000000..745cc8e --- /dev/null +++ b/src/examples/utils/transform.tsx @@ -0,0 +1,46 @@ +import { State } from '@etherealengine/hyperflux' +import EulerInput from '@etherealengine/ui/src/components/editor/input/Euler' +import InputGroup from '@etherealengine/ui/src/components/editor/input/Group' +import Vector3Input from '@etherealengine/ui/src/components/editor/input/Vector3' +import PropertyGroup from '@etherealengine/ui/src/components/editor/properties/group' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { Euler, Quaternion, Vector3 } from 'three' + +export const Transform = (props: { + title?: string + transformState: State<{ position: Vector3; rotation: Quaternion; scale: Vector3 }> +}) => { + const { transformState } = props + const { t } = useTranslation() + + const { position, rotation, scale } = transformState.value + + const onChangePosition = (value: Vector3) => transformState.position.set(new Vector3().copy(value)) + const onChangeRotation = (value: Euler) => transformState.rotation.set(new Quaternion().setFromEuler(value)) + const onChangeScale = (value: Vector3) => transformState.scale.set(new Vector3().copy(value)) + + return ( + + + + + + + + + + + + ) +} diff --git a/src/examplesRoute.tsx b/src/examplesRoute.tsx index 9f4a1ed..0a641d1 100644 --- a/src/examplesRoute.tsx +++ b/src/examplesRoute.tsx @@ -7,6 +7,7 @@ import ComponentExamplesRoute, { subComponentExamples } from './examples/compone import GLTFViewer from './examples/gltf' import Routes, { RouteData } from './sceneRoute' import MultipleScenesEntry from './examples/multipleScenes' +import ScenePlacement from './examples/scenePlacement' export const examples: RouteData[] = [ { @@ -38,6 +39,11 @@ export const examples: RouteData[] = [ name: 'Multiple Scenes', description: 'multiple scenes example', entry: MultipleScenesEntry + }, + { + name: 'Scene Placement', + description: 'place scenes relative to the origin example', + entry: ScenePlacement } ] diff --git a/src/sceneRoute.tsx b/src/sceneRoute.tsx index a9a0b47..aa123af 100644 --- a/src/sceneRoute.tsx +++ b/src/sceneRoute.tsx @@ -209,8 +209,8 @@ const Routes = (props: { routes: RouteData[]; header: string }) => {
+ {Entry && }
- {Entry && } ) }