Add control state synchronization and effect application to RuntimeHost
This commit is contained in:
@@ -38,6 +38,15 @@ import type { LoadedImageAsset } from "../assets/image-assets";
|
|||||||
import type { LoadedAudioAsset } from "../assets/audio-assets";
|
import type { LoadedAudioAsset } from "../assets/audio-assets";
|
||||||
import type { ProjectAssetRecord } from "../assets/project-assets";
|
import type { ProjectAssetRecord } from "../assets/project-assets";
|
||||||
import type { BoxBrush } from "../document/brushes";
|
import type { BoxBrush } from "../document/brushes";
|
||||||
|
import {
|
||||||
|
applyControlEffectToResolvedState,
|
||||||
|
createInteractionLinkResolvedControlSource,
|
||||||
|
type ControlEffect,
|
||||||
|
type InteractionControlTargetRef,
|
||||||
|
type LightControlTargetRef,
|
||||||
|
type RuntimeResolvedControlChannelValue,
|
||||||
|
type RuntimeResolvedDiscreteControlState
|
||||||
|
} from "../controls/control-surface";
|
||||||
import { buildBoxBrushDerivedMeshData } from "../geometry/box-brush-mesh";
|
import { buildBoxBrushDerivedMeshData } from "../geometry/box-brush-mesh";
|
||||||
import {
|
import {
|
||||||
createStarterMaterialSignature,
|
createStarterMaterialSignature,
|
||||||
@@ -85,6 +94,7 @@ import type {
|
|||||||
} from "./navigation-controller";
|
} from "./navigation-controller";
|
||||||
import { RapierCollisionWorld } from "./rapier-collision-world";
|
import { RapierCollisionWorld } from "./rapier-collision-world";
|
||||||
import {
|
import {
|
||||||
|
type InteractionLink,
|
||||||
RuntimeInteractionSystem,
|
RuntimeInteractionSystem,
|
||||||
type RuntimeInteractionDispatcher,
|
type RuntimeInteractionDispatcher,
|
||||||
type RuntimeInteractionPrompt
|
type RuntimeInteractionPrompt
|
||||||
@@ -128,6 +138,7 @@ interface CachedMaterialTexture {
|
|||||||
|
|
||||||
interface LocalLightRenderObjects {
|
interface LocalLightRenderObjects {
|
||||||
group: Group;
|
group: Group;
|
||||||
|
light: PointLight | SpotLight;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RuntimeWaterContactUniformBinding {
|
interface RuntimeWaterContactUniformBinding {
|
||||||
@@ -464,6 +475,7 @@ export class RuntimeHost {
|
|||||||
});
|
});
|
||||||
this.applyWorld();
|
this.applyWorld();
|
||||||
this.rebuildLocalLights(runtimeScene.localLights);
|
this.rebuildLocalLights(runtimeScene.localLights);
|
||||||
|
this.syncResolvedControlStateToRuntime(runtimeScene.control.resolved);
|
||||||
this.rebuildBrushMeshes(runtimeScene.brushes);
|
this.rebuildBrushMeshes(runtimeScene.brushes);
|
||||||
this.rebuildModelRenderObjects(
|
this.rebuildModelRenderObjects(
|
||||||
runtimeScene.modelInstances,
|
runtimeScene.modelInstances,
|
||||||
@@ -926,13 +938,13 @@ export class RuntimeHost {
|
|||||||
for (const pointLight of localLights.pointLights) {
|
for (const pointLight of localLights.pointLights) {
|
||||||
const renderObjects = this.createPointLightRuntimeObjects(pointLight);
|
const renderObjects = this.createPointLightRuntimeObjects(pointLight);
|
||||||
this.localLightGroup.add(renderObjects.group);
|
this.localLightGroup.add(renderObjects.group);
|
||||||
this.localLightObjects.set(pointLight.entityId, renderObjects.group);
|
this.localLightObjects.set(pointLight.entityId, renderObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const spotLight of localLights.spotLights) {
|
for (const spotLight of localLights.spotLights) {
|
||||||
const renderObjects = this.createSpotLightRuntimeObjects(spotLight);
|
const renderObjects = this.createSpotLightRuntimeObjects(spotLight);
|
||||||
this.localLightGroup.add(renderObjects.group);
|
this.localLightGroup.add(renderObjects.group);
|
||||||
this.localLightObjects.set(spotLight.entityId, renderObjects.group);
|
this.localLightObjects.set(spotLight.entityId, renderObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.applyShadowState();
|
this.applyShadowState();
|
||||||
@@ -957,7 +969,8 @@ export class RuntimeHost {
|
|||||||
group.add(light);
|
group.add(light);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
group
|
group,
|
||||||
|
light
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -995,10 +1008,145 @@ export class RuntimeHost {
|
|||||||
group.add(light.target);
|
group.add(light.target);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
group
|
group,
|
||||||
|
light
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private syncResolvedControlStateToRuntime(
|
||||||
|
resolved: RuntimeSceneDefinition["control"]["resolved"]
|
||||||
|
) {
|
||||||
|
for (const state of resolved.discrete) {
|
||||||
|
this.applyResolvedDiscreteControlState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const channelValue of resolved.channels) {
|
||||||
|
this.applyResolvedControlChannelValue(channelValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyResolvedDiscreteControlState(
|
||||||
|
state: RuntimeResolvedDiscreteControlState
|
||||||
|
) {
|
||||||
|
switch (state.type) {
|
||||||
|
case "lightEnabled":
|
||||||
|
this.applyLightEnabledControl(state.target, state.value);
|
||||||
|
return;
|
||||||
|
case "interactionEnabled":
|
||||||
|
this.applyInteractionEnabledControl(state.target, state.value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyResolvedControlChannelValue(
|
||||||
|
channelValue: RuntimeResolvedControlChannelValue
|
||||||
|
) {
|
||||||
|
switch (channelValue.type) {
|
||||||
|
case "lightIntensity":
|
||||||
|
this.applyLightIntensityControl(
|
||||||
|
channelValue.descriptor.target,
|
||||||
|
channelValue.value
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyLightEnabledControl(
|
||||||
|
target: LightControlTargetRef,
|
||||||
|
enabled: boolean
|
||||||
|
) {
|
||||||
|
const renderObjects = this.localLightObjects.get(target.entityId);
|
||||||
|
|
||||||
|
if (renderObjects === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderObjects.group.visible = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyLightIntensityControl(
|
||||||
|
target: LightControlTargetRef,
|
||||||
|
intensity: number
|
||||||
|
) {
|
||||||
|
const renderObjects = this.localLightObjects.get(target.entityId);
|
||||||
|
|
||||||
|
if (renderObjects === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderObjects.light.intensity = intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyInteractionEnabledControl(
|
||||||
|
target: InteractionControlTargetRef,
|
||||||
|
enabled: boolean
|
||||||
|
) {
|
||||||
|
if (this.runtimeScene === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.interactionKind === "interactable") {
|
||||||
|
const interactable =
|
||||||
|
this.runtimeScene.entities.interactables.find(
|
||||||
|
(candidate) => candidate.entityId === target.entityId
|
||||||
|
) ?? null;
|
||||||
|
|
||||||
|
if (interactable !== null) {
|
||||||
|
interactable.interactionEnabled = enabled;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sceneExit =
|
||||||
|
this.runtimeScene.entities.sceneExits.find(
|
||||||
|
(candidate) => candidate.entityId === target.entityId
|
||||||
|
) ?? null;
|
||||||
|
|
||||||
|
if (sceneExit !== null) {
|
||||||
|
sceneExit.interactionEnabled = enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyControlEffect(effect: ControlEffect, link: InteractionLink) {
|
||||||
|
switch (effect.type) {
|
||||||
|
case "playModelAnimation":
|
||||||
|
this.applyPlayAnimationAction(
|
||||||
|
effect.target.modelInstanceId,
|
||||||
|
effect.clipName,
|
||||||
|
effect.loop
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "stopModelAnimation":
|
||||||
|
this.applyStopAnimationAction(effect.target.modelInstanceId);
|
||||||
|
break;
|
||||||
|
case "playSound":
|
||||||
|
this.audioSystem.playSound(effect.target.entityId, link);
|
||||||
|
break;
|
||||||
|
case "stopSound":
|
||||||
|
this.audioSystem.stopSound(effect.target.entityId);
|
||||||
|
break;
|
||||||
|
case "setInteractionEnabled":
|
||||||
|
this.applyInteractionEnabledControl(effect.target, effect.enabled);
|
||||||
|
break;
|
||||||
|
case "setLightEnabled":
|
||||||
|
this.applyLightEnabledControl(effect.target, effect.enabled);
|
||||||
|
break;
|
||||||
|
case "setLightIntensity":
|
||||||
|
this.applyLightIntensityControl(effect.target, effect.intensity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.runtimeScene === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.runtimeScene.control.resolved = applyControlEffectToResolvedState(
|
||||||
|
this.runtimeScene.control.resolved,
|
||||||
|
effect,
|
||||||
|
createInteractionLinkResolvedControlSource(link.id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private rebuildBrushMeshes(brushes: RuntimeBoxBrushInstance[]) {
|
private rebuildBrushMeshes(brushes: RuntimeBoxBrushInstance[]) {
|
||||||
this.clearBrushMeshes();
|
this.clearBrushMeshes();
|
||||||
const volumeRenderPaths: ResolvedBoxVolumeRenderPaths =
|
const volumeRenderPaths: ResolvedBoxVolumeRenderPaths =
|
||||||
@@ -2327,6 +2475,9 @@ export class RuntimeHost {
|
|||||||
},
|
},
|
||||||
stopSound: (soundEmitterId) => {
|
stopSound: (soundEmitterId) => {
|
||||||
this.audioSystem.stopSound(soundEmitterId);
|
this.audioSystem.stopSound(soundEmitterId);
|
||||||
|
},
|
||||||
|
dispatchControlEffect: (effect, link) => {
|
||||||
|
this.applyControlEffect(effect, link);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user