Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
fixing interactable XRUI (interact ui) and making it's background rea…
Browse files Browse the repository at this point in the history
…ctive to handle all sizing. fixing font rasterization mismatch issue with xrui by sticking to default font. Updating label still won't properly update reactively until leaving/re-entering playmode however (#10954)
  • Loading branch information
SamMazerIR authored Aug 14, 2024
1 parent 65bdc5a commit 0f5fb7e
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 63 deletions.
33 changes: 15 additions & 18 deletions packages/engine/src/interaction/components/InteractableComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import matches from 'ts-matches'
import { isClient } from '@etherealengine/common/src/utils/getEnvironment'
import {
ECSState,
Engine,
Entity,
EntityUUID,
getComponent,
Expand Down Expand Up @@ -70,10 +69,12 @@ import {
DistanceFromCameraComponent,
DistanceFromLocalClientComponent
} from '@etherealengine/spatial/src/transform/components/DistanceComponents'
import { useXRUIState } from '@etherealengine/spatial/src/xrui/functions/useXRUIState'
import { useEffect } from 'react'
import { AvatarComponent } from '../../avatar/components/AvatarComponent'
import { createUI } from '../functions/createUI'
import { inFrustum, InteractableState, InteractableTransitions } from '../functions/interactableFunctions'
import { InteractiveModalState } from '../ui/InteractiveModalView'

/**
* Visibility override for XRUI, none is default behavior, on or off forces that state
Expand Down Expand Up @@ -131,7 +132,7 @@ export const updateInteractableUI = (entity: Entity) => {
xruiTransform.position.z = center.z
xruiTransform.position.y = MathUtils.lerp(xruiTransform.position.y, center.y + 0.7 * size.y, alpha)

const cameraTransform = getComponent(Engine.instance.viewerEntity, TransformComponent)
const cameraTransform = getComponent(getState(EngineState).viewerEntity, TransformComponent)
xruiTransform.rotation.copy(cameraTransform.rotation)
}

Expand All @@ -145,7 +146,7 @@ export const updateInteractableUI = (entity: Entity) => {
const transition = InteractableTransitions.get(entity)!
let activateUI = false

const inCameraFrustum = inFrustum(entity)
const inCameraFrustum = inFrustum(interactable.uiEntity)
let hovering = false

if (inCameraFrustum) {
Expand Down Expand Up @@ -204,9 +205,9 @@ const addInteractableUI = (entity: Entity) => {

const uiEntity = createUI(entity, interactable.label, interactable.uiInteractable).entity
getMutableComponent(entity, InteractableComponent).uiEntity.set(uiEntity)
setComponent(uiEntity, EntityTreeComponent, { parentEntity: Engine.instance.originEntity })
setComponent(uiEntity, EntityTreeComponent, { parentEntity: getState(EngineState).originEntity })
setComponent(uiEntity, ComputedTransformComponent, {
referenceEntities: [entity, Engine.instance.viewerEntity],
referenceEntities: [entity, getState(EngineState).viewerEntity],
computeFunction: () => updateInteractableUI(entity)
})

Expand Down Expand Up @@ -297,29 +298,19 @@ export const InteractableComponent = defineComponent({
const entity = useEntityContext()
const interactableComponent = useComponent(entity, InteractableComponent)
const isEditing = useMutableState(EngineState).isEditing
const modalState = useXRUIState<InteractiveModalState>()

useImmediateEffect(() => {
setComponent(entity, DistanceFromCameraComponent)
setComponent(entity, DistanceFromLocalClientComponent)

setComponent(entity, BoundingBoxComponent)
return () => {
removeComponent(entity, DistanceFromCameraComponent)
removeComponent(entity, DistanceFromLocalClientComponent)
removeComponent(entity, BoundingBoxComponent)
}
}, [])

useImmediateEffect(() => {
if (
interactableComponent.uiActivationType.value === XRUIActivationType.hover ||
interactableComponent.clickInteract.value
) {
setComponent(entity, BoundingBoxComponent)
return () => {
removeComponent(entity, BoundingBoxComponent)
}
}
}, [interactableComponent.uiActivationType, interactableComponent.clickInteract])

InputComponent.useExecuteWithInput(
() => {
const buttons = InputComponent.getMergedButtons(entity)
Expand Down Expand Up @@ -348,6 +339,12 @@ export const InteractableComponent = defineComponent({
}
}
}, [isEditing.value])

useEffect(() => {
//const xrUI = getMutableComponent(interactableComponent.uiEntity, XRUIComponent)
const msg = interactableComponent.label?.value ?? ''
modalState.interactMessage?.set(msg)
}, [interactableComponent.label]) //TODO just nuke the whole XRUI and recreate....
return null
}
})
Expand Down
36 changes: 0 additions & 36 deletions packages/engine/src/interaction/functions/createUI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ import { TransformComponent } from '@etherealengine/spatial/src/transform/compon
import { XRUIComponent } from '@etherealengine/spatial/src/xrui/components/XRUIComponent'
import { WebLayer3D } from '@etherealengine/xrui'

import { createEntity } from '@etherealengine/ecs'
import { addObjectToGroup } from '@etherealengine/spatial/src/renderer/components/GroupComponent'
import { MeshComponent } from '@etherealengine/spatial/src/renderer/components/MeshComponent'
import { VisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent'
import { EntityTreeComponent } from '@etherealengine/spatial/src/transform/components/EntityTree'
import { Color, DoubleSide, Mesh, MeshPhysicalMaterial, Shape, ShapeGeometry, Vector3 } from 'three'
import { createModalView } from '../ui/InteractiveModalView'

/**
Expand All @@ -48,22 +42,6 @@ import { createModalView } from '../ui/InteractiveModalView'
export function createUI(entity: Entity, uiMessage: string, isInteractable = true) {
const ui = createModalView(entity, uiMessage, isInteractable)

const blurMat = new MeshPhysicalMaterial({
color: new Color('#B9B9B9'),
transmission: 1,
roughness: 0.5,
opacity: 1,
transparent: true,
side: DoubleSide
})

const backgroundEid = createEntity()
const mesh = new Mesh(roundedRect(-(100 / 1000) / 2, -(100 / 1000) / 2, 100 / 1000, 100 / 1000, 0.01), blurMat)
setComponent(backgroundEid, EntityTreeComponent, { parentEntity: ui.entity })
setComponent(backgroundEid, MeshComponent, mesh)
setComponent(backgroundEid, VisibleComponent)
const backgroundTransform = setComponent(backgroundEid, TransformComponent, { position: new Vector3(0, 0, -0.001) })
addObjectToGroup(backgroundEid, mesh) // TODO: this should be managed by the MeshComponent
const nameComponent = getComponent(entity, NameComponent)
setComponent(ui.entity, NameComponent, 'interact-ui-' + uiMessage + '-' + nameComponent)

Expand All @@ -77,17 +55,3 @@ export function createUI(entity: Entity, uiMessage: string, isInteractable = tru

return ui
}

function roundedRect(x: number, y: number, width: number, height: number, radius: number): ShapeGeometry {
const shape = new Shape()
shape.moveTo(x, y + radius)
shape.lineTo(x, y + height - radius)
shape.quadraticCurveTo(x, y + height, x + radius, y + height)
shape.lineTo(x + width - radius, y + height)
shape.quadraticCurveTo(x + width, y + height, x + width, y + height - radius)
shape.lineTo(x + width, y + radius)
shape.quadraticCurveTo(x + width, y, x + width - radius, y)
shape.lineTo(x + radius, y)
shape.quadraticCurveTo(x, y, x, y + radius)
return new ShapeGeometry(shape)
}
77 changes: 69 additions & 8 deletions packages/engine/src/interaction/ui/InteractiveModalView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,91 @@ Ethereal Engine. All Rights Reserved.
import React from 'react'

import { isClient } from '@etherealengine/common/src/utils/getEnvironment'
import { createEntity, setComponent } from '@etherealengine/ecs'
import { Entity } from '@etherealengine/ecs/src/Entity'
import { hookstate } from '@etherealengine/hyperflux'
import { TransformComponent } from '@etherealengine/spatial'
import { addObjectToGroup } from '@etherealengine/spatial/src/renderer/components/GroupComponent'
import { MeshComponent } from '@etherealengine/spatial/src/renderer/components/MeshComponent'
import { VisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent'
import { EntityTreeComponent } from '@etherealengine/spatial/src/transform/components/EntityTree'
import { createXRUI } from '@etherealengine/spatial/src/xrui/functions/createXRUI'
import { useXRUIState } from '@etherealengine/spatial/src/xrui/functions/useXRUIState'
import { Color, DoubleSide, Mesh, MeshPhysicalMaterial, Shape, ShapeGeometry, Vector3 } from 'three'

export interface InteractiveModalState {
interactMessage: string
}

export const createModalView = (entity: Entity, interactMessage: string, isInteractable = true) => {
const uiEntity = createEntity()
const ui = createXRUI(
InteractiveModalView,
() => InteractiveModalView(uiEntity),
hookstate({
interactMessage
} as InteractiveModalState),
{ interactable: isInteractable }
{ interactable: isInteractable },
uiEntity
)
return ui
}

export const InteractiveModalView = () => {
function createBackground(parentEntity: Entity, width: number, height: number): Entity {
const blurMat = new MeshPhysicalMaterial({
color: new Color('#B9B9B9'),
transmission: 1,
roughness: 0.5,
opacity: 1,
transparent: true,
side: DoubleSide
})

const backgroundEid = createEntity()
const calcWidth = width + 30 // 30 accounts for padding and border radius in the Element styling
const calcHeight = height + 30
const mesh = new Mesh(
roundedRect(-(calcWidth / 1000) / 2, -(calcHeight / 1000) / 2, calcWidth / 1000, calcHeight / 1000, 0.01),
blurMat
)
setComponent(backgroundEid, EntityTreeComponent, { parentEntity: parentEntity })
setComponent(backgroundEid, MeshComponent, mesh)
setComponent(backgroundEid, VisibleComponent)
const backgroundTransform = setComponent(backgroundEid, TransformComponent, { position: new Vector3(0, 0, -0.001) })
addObjectToGroup(backgroundEid, mesh) // TODO: this should be managed by the MeshComponent
return backgroundEid
}

function roundedRect(x: number, y: number, width: number, height: number, radius: number): ShapeGeometry {
const shape = new Shape()
shape.moveTo(x, y + radius)
shape.lineTo(x, y + height - radius)
shape.quadraticCurveTo(x, y + height, x + radius, y + height)
shape.lineTo(x + width - radius, y + height)
shape.quadraticCurveTo(x + width, y + height, x + width, y + height - radius)
shape.lineTo(x + width, y + radius)
shape.quadraticCurveTo(x + width, y, x + width - radius, y)
shape.lineTo(x + radius, y)
shape.quadraticCurveTo(x, y, x, y + radius)
return new ShapeGeometry(shape)
}

export const InteractiveModalView: React.FC = (entity: Entity) => {
const modalState = useXRUIState<InteractiveModalState>()
const rootElement = React.useRef<HTMLDivElement>(null)

if (!isClient) return <></>

React.useLayoutEffect(() => {
if (rootElement.current) {
createBackground(entity, rootElement.current.clientWidth, rootElement.current.clientHeight)
}
}, [rootElement.current]) //TODO this isn't firing, not calculating size to add BG

return (
<div className={'modal'}>
E
<div className={'modal'} ref={rootElement}>
{modalState.interactMessage.value && modalState.interactMessage.value !== ''
? modalState.interactMessage.value
: 'E'}
<link href="https://fonts.googleapis.com/css?family=Lato:400" rel="stylesheet" type="text/css" />
<style>
{`
Expand All @@ -61,14 +120,16 @@ export const InteractiveModalView = () => {
align-items: center;
font-size: 60px;
color: #e7e7e7;
font-family: 'Lato', sans-serif;
font-family: sans-serif;
font-weight: 400;
border: 4px solid #e7e7e7;
border-radius: 10px;
padding: 10px;
margin: 60px;
width: 50px;
height: 50px;
width: fit-content;
height: fit-content;
min-width: 50px;
min-height: 50px;
text-align: center;
vertical-align: center;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ export const InteractableComponentNodeEditor: EditorComponentType = (props) => {
targets.set(options)
}, [])

const updateLabel = (value: string) => {
commitProperty(InteractableComponent, 'label')(value)
//this might be useful later, but xrui is not updating properly
// const msg = value ?? ''
// modalState.interactMessage?.set(msg)
}
const addCallback = () => {
const label = ''
const callbacks = [
Expand Down Expand Up @@ -137,7 +143,7 @@ export const InteractableComponentNodeEditor: EditorComponentType = (props) => {
<StringInput
value={interactableComponent.label.value!}
onChange={updateProperty(InteractableComponent, 'label')}
onRelease={commitProperty(InteractableComponent, 'label')}
onRelease={(value) => updateLabel(value)}
/>
</InputGroup>
<InputGroup name="ActivationDistance" label={t('editor:properties.interactable.lbl-activationDistance')}>
Expand Down

0 comments on commit 0f5fb7e

Please sign in to comment.