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

behave graph fixes, camera fade to black uses sphere #9084

Merged
merged 5 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions packages/editor/src/components/graph/BehaveFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ import {
setComponent
} from '@etherealengine/engine/src/ecs/functions/ComponentFunctions'
import { NO_PROXY, getMutableState, useHookstate } from '@etherealengine/hyperflux'
import { isEqual } from 'lodash'
import React, { useEffect } from 'react'
import AutoSizer from 'react-virtualized-auto-sizer'
import 'reactflow/dist/style.css'
import { SelectionState } from '../../services/SelectionServices'
import { PropertiesPanelButton } from '../inputs/Button'
import { commitProperty } from '../properties/Util'
import { Flow } from './ee-flow'
import './ee-flow/styles.css'

Expand Down Expand Up @@ -81,13 +81,10 @@ const BehaveFlow = () => {
)}
{validEntity && (
<Flow
initialGraph={graphComponent?.value?.graph}
initialGraph={graphComponent?.get(NO_PROXY)?.graph}
examples={{}}
registry={behaveGraphState.registries.get(NO_PROXY)[graphComponent?.domain.value]}
onChangeGraph={(newGraph) => {
if (!graphComponent.graph || isEqual(graphComponent.graph.get(NO_PROXY), newGraph)) return
graphComponent.graph.set(JSON.parse(JSON.stringify(newGraph)))
}}
onChangeGraph={commitProperty(BehaveGraphComponent, 'graph')}
/>
)}
</div>
Expand Down
21 changes: 11 additions & 10 deletions packages/editor/src/components/graph/ee-flow/components/Flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { Background, BackgroundVariant, ReactFlow } from 'reactflow'
import { GraphJSON, IRegistry } from '@behave-graph/core'

import { useGraphRunner } from '@etherealengine/engine/src/behave-graph/functions/useGraphRunner.js'
import _ from 'lodash'
import { useHookstate } from '@hookstate/core'
import { useBehaveGraphFlow } from '../hooks/useBehaveGraphFlow.js'
import { useFlowHandlers } from '../hooks/useFlowHandlers.js'
import { useNodeSpecGenerator } from '../hooks/useNodeSpecGenerator.js'
Expand All @@ -49,6 +49,9 @@ export const Flow: React.FC<FlowProps> = ({ initialGraph: graph, examples, regis

const flowRef = useRef(null)

const dragging = useHookstate(false)
const mouseOver = useHookstate(false)

const { nodes, edges, onNodesChange, onEdgesChange, graphJson, setGraphJson, nodeTypes } = useBehaveGraphFlow({
initialGraphJson: graph,
specGenerator
Expand Down Expand Up @@ -77,30 +80,28 @@ export const Flow: React.FC<FlowProps> = ({ initialGraph: graph, examples, regis
registry
})

const debouncedOnChangeGraph = _.debounce(() => {
onChangeGraph(graphJson ?? graph)
}, 2000)

useEffect(() => {
debouncedOnChangeGraph()
return () => {
debouncedOnChangeGraph.cancel()
}
}, [graphJson])
if (dragging.value || !mouseOver.value) return
onChangeGraph(graphJson ?? graph)
}, [dragging, graphJson])

return (
<ReactFlow
ref={flowRef}
nodeTypes={nodeTypes}
nodes={nodes}
edges={edges}
onNodeDragStart={() => dragging.set(true)}
onNodeDragStop={() => dragging.set(false)}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
// @ts-ignore
onConnectStart={handleStartConnect}
// @ts-ignore
onConnectEnd={handleStopConnect}
onPaneMouseEnter={() => mouseOver.set(true)}
onPaneMouseLeave={() => mouseOver.set(false)}
fitView
fitViewOptions={{ maxZoom: 1 }}
onPaneClick={handlePaneClick}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export const Node: React.FC<NodeProps> = ({ id, data, spec, selected, specGenera
<InputSocket
{...input}
specGenerator={specGenerator}
value={data[input.name] ?? input.defaultValue}
value={data.values?.[input.name] ?? input.defaultValue}
onChange={handleChange}
connected={isHandleConnected(edges, id, input.name, 'target')}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ export const useChangeNodeData = (id: string) => {
...n,
data: {
...n.data,
[key]: value
values: {
...n.data.values,
[key]: value
}
}
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import { GraphJSON, NodeConfigurationJSON } from '@behave-graph/core'
import { Edge, Node } from 'reactflow'
import { v4 as uuidv4 } from 'uuid'

import { GraphJSON } from '@behave-graph/core'

export const behaveToFlow = (graph: GraphJSON): [Node[], Edge[]] => {
const nodes: Node[] = []
const edges: Edge[] = []
Expand All @@ -40,11 +39,20 @@ export const behaveToFlow = (graph: GraphJSON): [Node[], Edge[]] => {
x: nodeJSON.metadata?.positionX ? Number(nodeJSON.metadata?.positionX) : 0,
y: nodeJSON.metadata?.positionY ? Number(nodeJSON.metadata?.positionY) : 0
},
data: {} as { [key: string]: any }
data: {
configuration: {} as NodeConfigurationJSON,
values: {} as { [key: string]: any }
}
}

nodes.push(node)

if (nodeJSON.configuration) {
for (const [inputKey, input] of Object.entries(nodeJSON.configuration)) {
node.data.configuration[inputKey] = input
}
}

if (nodeJSON.parameters) {
for (const [inputKey, input] of Object.entries(nodeJSON.parameters)) {
if ('link' in input && input.link !== undefined) {
Expand All @@ -57,7 +65,7 @@ export const behaveToFlow = (graph: GraphJSON): [Node[], Edge[]] => {
})
}
if ('value' in input) {
node.data[inputKey] = input.value
node.data.values[inputKey] = input.value
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export const flowToBehave = (nodes: Node[], edges: Edge[], specGenerator: NodeSp

const nodeSpec = specGenerator.getNodeSpec(node.type, node.data.configuration)
if (nodeSpec === undefined) return

const behaveNode: NodeJSON = {
id: node.id,
type: node.type,
Expand All @@ -47,22 +46,19 @@ export const flowToBehave = (nodes: Node[], edges: Edge[], specGenerator: NodeSp
positionY: String(node.position.y)
}
}
if (node.data.configuration) {
Object.entries(node.data.configuration).forEach(([key, value]) => {
if (behaveNode.configuration === undefined) {
behaveNode.configuration = {}
}
behaveNode.configuration[key] = value as ValueJSON
})
}
if (node.data.values) {
Object.entries(node.data.values).forEach(([key, value]) => {
if (behaveNode.parameters === undefined) {
behaveNode.parameters = {}
}
behaveNode.parameters[key] = { value: value as string }
})
}
Object.entries(node.data.configuration).forEach(([key, value]) => {
if (behaveNode.configuration === undefined) {
behaveNode.configuration = {}
}
behaveNode.configuration[key] = value as ValueJSON
})

Object.entries(node.data.values).forEach(([key, value]) => {
if (behaveNode.parameters === undefined) {
behaveNode.parameters = {}
}
behaveNode.parameters[key] = { value: value as string }
})

edges
.filter((edge) => edge.target === node.id)
Expand Down
17 changes: 8 additions & 9 deletions packages/engine/src/avatar/systems/AvatarTeleportSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,14 @@ const execute = () => {
getState(AvatarTeleportSystemState)

if (fadeBackInAccumulator >= 0) {
/** @todo fix camera fade transition shader - for now just teleport instantly */
// fadeBackInAccumulator += getState(EngineState).deltaSeconds
// if (fadeBackInAccumulator > 0.25) {
fadeBackInAccumulator = -1
teleportAvatar(Engine.instance.localClientEntity, guideCursor.position)
dispatchAction(CameraActions.fadeToBlack({ in: false }))
dispatchAction(XRAction.vibrateController({ handedness: 'left', value: 0.5, duration: 100 }))
dispatchAction(XRAction.vibrateController({ handedness: 'right', value: 0.5, duration: 100 }))
// }
fadeBackInAccumulator += getState(EngineState).deltaSeconds
if (fadeBackInAccumulator > 0.25) {
fadeBackInAccumulator = -1
teleportAvatar(Engine.instance.localClientEntity, guideCursor.position)
dispatchAction(CameraActions.fadeToBlack({ in: false }))
dispatchAction(XRAction.vibrateController({ handedness: 'left', value: 0.5, duration: 100 }))
dispatchAction(XRAction.vibrateController({ handedness: 'right', value: 0.5, duration: 100 }))
}
}
for (const entity of avatarTeleportQuery.exit()) {
visibleSegments = 1
Expand Down
96 changes: 27 additions & 69 deletions packages/engine/src/camera/systems/CameraFadeBlackEffectSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,76 +23,44 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import { Color, Mesh, PlaneGeometry, ShaderMaterial, Texture, Uniform, Vector2, Vector3 } from 'three'
import { Color, DoubleSide, Mesh, MeshBasicMaterial, SphereGeometry, Texture } from 'three'

import { defineActionQueue, defineState, getMutableState, getState, useHookstate } from '@etherealengine/hyperflux'
import { defineActionQueue, defineState, getState } from '@etherealengine/hyperflux'

import { useEffect } from 'react'
import { AssetLoader } from '../../assets/classes/AssetLoader'
import { Engine } from '../../ecs/classes/Engine'
import { EngineState } from '../../ecs/classes/EngineState'
import { removeComponent, setComponent } from '../../ecs/functions/ComponentFunctions'
import { getComponent, removeComponent } from '../../ecs/functions/ComponentFunctions'
import { createEntity } from '../../ecs/functions/EntityFunctions'
import { EntityTreeComponent } from '../../ecs/functions/EntityTree'
import { defineSystem } from '../../ecs/functions/SystemFunctions'
import { addObjectToGroup } from '../../scene/components/GroupComponent'
import { setVisibleComponent } from '../../scene/components/VisibleComponent'
import { ObjectLayers } from '../../scene/constants/ObjectLayers'
import { setObjectLayers } from '../../scene/functions/setObjectLayers'
import { LocalTransformComponent } from '../../transform/components/TransformComponent'
import {
ComputedTransformComponent,
setComputedTransformComponent
} from '../../transform/components/ComputedTransformComponent'
import { TransformComponent } from '../../transform/components/TransformComponent'
import { createTransitionState } from '../../xrui/functions/createTransitionState'
import { CameraActions } from '../CameraState'

const VERTEX_SHADER = `
precision highp float;

void main() {
vec3 newPosition = position * 2.0;
gl_Position = vec4(newPosition, 1.0);
}`

const FRAGMENT_SHADER = `
precision highp float;

uniform vec2 resolution;
uniform vec3 color;
uniform float intensity;
#ifdef USE_GRAPHIC
uniform sampler2D graphicTexture;
#endif
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec4 baseColor = vec4(color, intensity);
#ifdef USE_GRAPHIC
if (uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0) {
vec4 graphicColor = texture2D(graphicTexture, uv);
baseColor = graphicColor * baseColor;
} else {
baseColor = vec4(0.0, 0.0, 0.0, 1.0);
}
#endif
gl_FragColor = baseColor;
}`

const fadeToBlackQueue = defineActionQueue(CameraActions.fadeToBlack.matches)

const CameraFadeBlackEffectSystemState = defineState({
name: 'CameraFadeBlackEffectSystemState',
initial: () => {
const geometry = new PlaneGeometry(1, 1)
const material = new ShaderMaterial({
vertexShader: VERTEX_SHADER,
fragmentShader: FRAGMENT_SHADER,
const geometry = new SphereGeometry(10)
const material = new MeshBasicMaterial({
transparent: true,
depthTest: false,
uniforms: {
color: { value: new Color('black') },
intensity: { value: 0 },
resolution: { value: new Vector2(window.outerWidth, window.outerHeight) }
}
side: DoubleSide,
depthWrite: true,
depthTest: false
})

const mesh = new Mesh(geometry, material)
mesh.scale.set(-1, 1, -1)
mesh.renderOrder = 1
mesh.name = 'Camera Fade Transition'
const entity = createEntity()
addObjectToGroup(entity, mesh)
Expand All @@ -112,43 +80,33 @@ const execute = () => {
for (const action of fadeToBlackQueue()) {
transition.setState(action.in ? 'IN' : 'OUT')
if (action.in) {
setComponent(entity, LocalTransformComponent, {
position: new Vector3(0, 0, -0.1)
setComputedTransformComponent(entity, Engine.instance.cameraEntity, () => {
getComponent(entity, TransformComponent).position.copy(
getComponent(Engine.instance.cameraEntity, TransformComponent).position
)
})
setComponent(entity, EntityTreeComponent, { parentEntity: Engine.instance.cameraEntity })
} else removeComponent(entity, LocalTransformComponent)
} else removeComponent(entity, ComputedTransformComponent)
if (action.graphicTexture) {
AssetLoader.load(action.graphicTexture, {}, (texture: Texture) => {
mesh.material.uniforms.graphicTexture = new Uniform(texture)
mesh.material.uniforms.color = new Uniform(new Color('white'))
mesh.material.defines.USE_GRAPHIC = true
mesh.material.color = new Color('white')
mesh.material.map = texture
mesh.material.needsUpdate = true
})
} else {
delete mesh.material.defines.USE_GRAPHIC
mesh.material.uniforms.color = new Uniform(new Color('black'))
mesh.material.color = new Color('black')
mesh.material.map = null
mesh.material.needsUpdate = true
}
}

const deltaSeconds = getState(EngineState).deltaSeconds
transition.update(deltaSeconds, (alpha) => {
mesh.material.uniforms.intensity.value = alpha
mesh.material.opacity = alpha
setVisibleComponent(entity, alpha > 0)
})
}

const reactor = () => {
const outerWidth = useHookstate(window.outerWidth)
const outerHeight = useHookstate(window.outerHeight)
const { mesh } = useHookstate(getMutableState(CameraFadeBlackEffectSystemState))
useEffect(() => {
mesh.material.uniforms.resolution.nested('value').set([outerWidth.value, outerHeight.value])
}, [outerWidth, outerHeight])
return null
}

export const CameraFadeBlackEffectSystem = defineSystem({
uuid: 'ee.engine.CameraFadeBlackEffectSystem',
execute,
reactor
execute
})