Skip to content

Commit d4cac1b

Browse files
committed
observable component mvp
1 parent 6261b08 commit d4cac1b

File tree

3 files changed

+173
-5
lines changed

3 files changed

+173
-5
lines changed

assets/GLTF/basic_door.glb

47 KB
Binary file not shown.

resources.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@
105105
"thumbnailKey": "projects/ir-engine/ir-development-test-suite/public/thumbnails/assets_hall.glb-thumbnail.png",
106106
"thumbnailMode": "automatic"
107107
},
108+
"assets/GLTF/basic_door.glb": {
109+
"type": "file",
110+
"tags": [
111+
"Model"
112+
],
113+
"dependencies": [],
114+
"name": "basic_door.glb"
115+
},
108116
"assets/GLTF/double-mat-test_data.bin": {
109117
"type": "file",
110118
"tags": [

src/examples/componentExamples/componentExamples.tsx

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ import {
1414
} from '@ir-engine/ecs'
1515
import { LoopAnimationComponent } from '@ir-engine/engine/src/avatar/components/LoopAnimationComponent'
1616
import { GLTFComponent } from '@ir-engine/engine/src/gltf/GLTFComponent'
17-
import { NodeIDComponent } from '@ir-engine/engine/src/gltf/NodeIDComponent'
17+
import { NodeID, NodeIDComponent } from '@ir-engine/engine/src/gltf/NodeIDComponent'
1818
import {
1919
InteractableComponent,
2020
XRUIActivationType
2121
} from '@ir-engine/engine/src/interaction/components/InteractableComponent'
2222
import { ImageComponent } from '@ir-engine/engine/src/scene/components/ImageComponent'
2323
import { LinkComponent } from '@ir-engine/engine/src/scene/components/LinkComponent'
2424
import { MediaComponent } from '@ir-engine/engine/src/scene/components/MediaComponent'
25+
import { ObservableComponent } from '@ir-engine/engine/src/scene/components/ObservableComponent'
2526
import { ParticleSystemComponent } from '@ir-engine/engine/src/scene/components/ParticleSystemComponent'
2627
import { PrimitiveGeometryComponent } from '@ir-engine/engine/src/scene/components/PrimitiveGeometryComponent'
2728
import { SDFComponent } from '@ir-engine/engine/src/scene/components/SDFComponent'
@@ -39,16 +40,22 @@ import { useHookstate } from '@ir-engine/hyperflux'
3940
import { TransformComponent } from '@ir-engine/spatial'
4041
import { CallbackComponent } from '@ir-engine/spatial/src/common/CallbackComponent'
4142
import { NameComponent } from '@ir-engine/spatial/src/common/NameComponent'
42-
import { Q_IDENTITY, Q_Y_180 } from '@ir-engine/spatial/src/common/constants/MathConstants'
43+
import { PI, Q_IDENTITY, Q_Y_180, Vector3_Up } from '@ir-engine/spatial/src/common/constants/MathConstants'
4344
import { InputComponent } from '@ir-engine/spatial/src/input/components/InputComponent'
44-
import { setVisibleComponent } from '@ir-engine/spatial/src/renderer/components/VisibleComponent'
45+
import { VisibleComponent, setVisibleComponent } from '@ir-engine/spatial/src/renderer/components/VisibleComponent'
4546
import { ObjectLayerMasks } from '@ir-engine/spatial/src/renderer/constants/ObjectLayers'
47+
import {
48+
MaterialInstanceComponent,
49+
MaterialStateComponent
50+
} from '@ir-engine/spatial/src/renderer/materials/MaterialComponent'
4651
import React, { useEffect } from 'react'
47-
import { MathUtils } from 'three'
52+
import { MathUtils, MeshLambertMaterial, Quaternion, Vector3 } from 'three'
4853
import { useAvatars } from '../../engine/TestUtils'
4954
import { useExampleEntity } from '../utils/common/entityUtils'
5055
import ComponentNamesUI from './ComponentNamesUI'
5156

57+
const BehaviorComponent = ObservableComponent
58+
5259
export const metadata = {
5360
title: 'Components Examples',
5461
description: 'Components examples'
@@ -442,6 +449,157 @@ export const subComponentExamples = [
442449
onLoad(entity)
443450
}, [callback])
444451

452+
return null
453+
}
454+
},
455+
{
456+
name: 'Behavior',
457+
description: 'Add arbitrary behaviors to objects',
458+
Reactor: (props: { parent: Entity; onLoad: (entity: Entity) => void }) => {
459+
const { parent, onLoad } = props
460+
const doorEntity = useExampleEntity(parent)
461+
const buttonEntity = useExampleEntity(parent)
462+
const pedastalEntity = useExampleEntity(parent)
463+
464+
useEffect(() => {
465+
setComponent(doorEntity, TransformComponent, { position: new Vector3(6.5, 0, 2.5) })
466+
setComponent(doorEntity, NameComponent, 'Door')
467+
setComponent(doorEntity, GLTFComponent, {
468+
src: config.client.fileServer + '/projects/ir-engine/ir-development-test-suite/assets/GLTF/basic_door.glb'
469+
})
470+
setComponent(doorEntity, VisibleComponent)
471+
472+
setComponent(pedastalEntity, TransformComponent, { position: new Vector3(0, 0.5, 0) })
473+
setComponent(pedastalEntity, PrimitiveGeometryComponent, {
474+
geometryType: GeometryTypeEnum.BoxGeometry,
475+
geometryParams: {
476+
width: 1,
477+
height: 1,
478+
depth: 1,
479+
widthSegments: 1,
480+
heightSegments: 1,
481+
depthSegments: 1
482+
}
483+
})
484+
setComponent(pedastalEntity, NameComponent, 'Pedestal')
485+
const lightpurpleHex = 0x9b59b6
486+
setComponent(pedastalEntity, MaterialStateComponent, {
487+
material: new MeshLambertMaterial({ color: lightpurpleHex })
488+
})
489+
setComponent(pedastalEntity, MaterialInstanceComponent, { uuid: [getComponent(pedastalEntity, UUIDComponent)] })
490+
setComponent(pedastalEntity, VisibleComponent, true)
491+
492+
setComponent(buttonEntity, TransformComponent, {
493+
position: new Vector3(0, 1.025, 0)
494+
})
495+
setComponent(buttonEntity, PrimitiveGeometryComponent, {
496+
geometryType: GeometryTypeEnum.BoxGeometry,
497+
geometryParams: {
498+
width: 0.2,
499+
height: 0.05,
500+
depth: 0.2,
501+
widthSegments: 1,
502+
heightSegments: 1,
503+
depthSegments: 1
504+
}
505+
})
506+
setComponent(buttonEntity, NameComponent, 'Button')
507+
setComponent(buttonEntity, MaterialStateComponent, { material: new MeshLambertMaterial({ color: 'red' }) })
508+
setComponent(buttonEntity, MaterialInstanceComponent, { uuid: [getComponent(buttonEntity, UUIDComponent)] })
509+
setComponent(buttonEntity, VisibleComponent, true)
510+
511+
setComponent(buttonEntity, InputComponent, { highlight: true, grow: true })
512+
setComponent(buttonEntity, InteractableComponent, {
513+
label: '',
514+
clickInteract: true,
515+
uiActivationType: XRUIActivationType.proximity,
516+
activationDistance: 2,
517+
highlighted: true,
518+
callbacks: [
519+
{
520+
callbackID: 'Button Click Callback',
521+
target: getComponent(buttonEntity, NodeIDComponent)
522+
},
523+
{
524+
callbackID: 'Button Click Callback 2',
525+
target: getComponent(buttonEntity, NodeIDComponent)
526+
}
527+
]
528+
})
529+
530+
const doorNodeID = '5' as NodeID // the door inside the door model
531+
const Q_Y_120 = new Quaternion().setFromAxisAngle(Vector3_Up, PI * (120 / 180))
532+
533+
setComponent(buttonEntity, BehaviorComponent, {
534+
observers: [
535+
{
536+
conditions: [
537+
{
538+
type: 'callback',
539+
nodeID: getComponent(buttonEntity, NodeIDComponent),
540+
callback: 'Button Click Callback'
541+
},
542+
// ensure door is not already open by getting the rotation
543+
{
544+
type: 'entity',
545+
nodeID: doorNodeID,
546+
sourceNodeID: getComponent(doorEntity, NodeIDComponent),
547+
component: TransformComponent.jsonID,
548+
property: 'rotation.y',
549+
value: 0,
550+
condition: 'equal'
551+
}
552+
],
553+
effects: [
554+
{
555+
type: 'transition',
556+
nodeID: doorNodeID,
557+
sourceNodeID: getComponent(doorEntity, NodeIDComponent),
558+
jsonID: TransformComponent.jsonID,
559+
propertyPath: 'rotation',
560+
value: Q_Y_120,
561+
duration: 1000,
562+
easing: Easing.exponential.inOut.path
563+
}
564+
],
565+
networked: true
566+
},
567+
{
568+
conditions: [
569+
{
570+
type: 'callback',
571+
nodeID: getComponent(buttonEntity, NodeIDComponent),
572+
callback: 'Button Click Callback 2'
573+
},
574+
// ensure door is not already closed by getting the rotation
575+
{
576+
type: 'entity',
577+
nodeID: doorNodeID,
578+
sourceNodeID: getComponent(doorEntity, NodeIDComponent),
579+
component: TransformComponent.jsonID,
580+
property: 'rotation.y',
581+
value: 0,
582+
condition: 'notEqual'
583+
}
584+
],
585+
effects: [
586+
{
587+
type: 'transition',
588+
nodeID: doorNodeID,
589+
sourceNodeID: getComponent(doorEntity, NodeIDComponent),
590+
jsonID: TransformComponent.jsonID,
591+
propertyPath: 'rotation',
592+
value: Q_IDENTITY,
593+
duration: 1000,
594+
easing: Easing.exponential.inOut.path
595+
}
596+
],
597+
networked: true
598+
}
599+
]
600+
})
601+
}, [])
602+
445603
return null
446604
}
447605
}
@@ -494,6 +652,8 @@ export const ComponentExamples = (props: {
494652
const xrui = useHookstate({ entity: UndefinedEntity })
495653

496654
useEffect(() => {
655+
if (!xrui.entity.value) return
656+
497657
const componentNamesUIEntity = createEntity()
498658
setComponent(componentNamesUIEntity, UUIDComponent, generateEntityUUID())
499659
setComponent(componentNamesUIEntity, EntityTreeComponent, { parentEntity: sceneEntity })
@@ -504,7 +664,7 @@ export const ComponentExamples = (props: {
504664
return () => {
505665
removeEntity(componentNamesUIEntity)
506666
}
507-
}, [Reactor])
667+
}, [Reactor, xrui.entity.value])
508668

509669
return <Reactor parent={sceneEntity} onLoad={xrui.entity.set} />
510670
}

0 commit comments

Comments
 (0)