1#[cfg(feature = "protocol-agent")]
5mod agent;
6#[cfg(feature = "protocol-agent")]
7pub use agent::*;
8
9use crate::command::Command;
10use nightshade::ecs::camera::components::Camera;
11use nightshade::ecs::decal::components::Decal;
12use nightshade::ecs::event::Event;
13use nightshade::ecs::light::components::Light;
14use nightshade::ecs::scene::{SceneChunkConfig, SceneLayerConfig};
15use nightshade::ecs::text::components::TextProperties;
16use nightshade::ecs::vfx::components::{Beam, LightningBolt, Trail, VfxAnimator};
17use nightshade::ecs::water::components::Water;
18use nightshade::plugins::audio::components::AudioSource;
19use nightshade::plugins::gizmos::GizmoMode;
20pub use nightshade::prelude::{
21 Atmosphere, CharacterControllerComponent, ColliderComponent, NavMeshAgent, RigidBodyComponent,
22};
23use nightshade::render::material::Material;
24use nightshade::render::particles::ParticleEmitter;
25use serde::{Deserialize, Serialize};
26
27pub use nightshade::ecs::camera::components::{
28 Camera as CameraComponent, OrthographicCamera, PerspectiveCamera, Projection,
29};
30pub use nightshade::ecs::decal::components::Decal as DecalComponent;
31pub use nightshade::ecs::light::components::{AreaLightShape, Light as LightComponent, LightType};
32pub use nightshade::ecs::primitives::{CameraCullingMask, CullingMask};
33pub use nightshade::ecs::text::components::TextProperties as TextPropertiesComponent;
34pub use nightshade::ecs::vfx::components::{
35 Beam as BeamComponent, LightningBolt as LightningBoltComponent, Trail as TrailComponent,
36 VfxAnimator as VfxAnimatorComponent,
37};
38pub use nightshade::ecs::water::components::Water as WaterComponent;
39pub use nightshade::plugins::audio::components::AudioSource as AudioSourceComponent;
40pub use nightshade::plugins::gizmos::GizmoMode as GizmoModeKind;
41pub use nightshade::plugins::physics::components::ColliderShape;
42pub use nightshade::plugins::physics::types::RigidBodyType;
43pub use nightshade::prelude::{Atmosphere as AtmosphereKind, Vec2, Vec3, Vec4};
44pub use nightshade::render::material::AlphaMode;
45pub use nightshade::render::material::Material as MaterialComponent;
46pub use nightshade::render::particles::ParticleEmitter as ParticleEmitterComponent;
47pub use nightshade::render::render_layer::RenderLayer;
48
49#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
51#[cfg_attr(
52 feature = "protocol-agent",
53 derive(enum2schema::Schema),
54 schema(string_enum)
55)]
56pub enum ShapeKind {
57 Cube,
58 Plane,
59 Cylinder,
60 Cone,
61 Sphere,
62 Torus,
63}
64
65#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
67#[cfg_attr(
68 feature = "protocol-agent",
69 derive(enum2schema::Schema),
70 schema(string_enum)
71)]
72pub enum LightKind {
73 Point,
74 Spot,
75 Directional,
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
80#[cfg_attr(
81 feature = "protocol-agent",
82 derive(enum2schema::Schema),
83 schema(string_enum)
84)]
85pub enum GeneratorKind {
86 Building,
87 Room,
88 Tower,
89 Stairs,
90 Columns,
91 Perimeter,
92 CityBlock,
93 Courtyard,
94 Wfc,
95 WfcCity,
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
100#[cfg_attr(
101 feature = "protocol-agent",
102 derive(enum2schema::Schema),
103 schema(string_enum)
104)]
105pub enum PolyhavenCategory {
106 Hdris,
107 Models,
108}
109
110pub use crate::reflect::{ComponentKind, ComponentPatch};
111
112#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
114#[cfg_attr(feature = "protocol-agent", derive(enum2schema::Schema))]
115pub enum AnimationCommand {
116 Play { index: u32 },
117 PlayAll,
118 Pause,
119 Resume,
120 Stop,
121 Seek { time: f32 },
122 SetSpeed { speed: f32 },
123 SetLooping { looping: bool },
124}
125
126#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
129#[cfg_attr(feature = "protocol-agent", derive(enum2schema::Schema))]
130pub struct GenerationSettings {
131 pub footprint_x: f32,
132 pub footprint_z: f32,
133 pub stories: u32,
134 pub story_height: f32,
135 pub wall_thickness: f32,
136 pub floor_thickness: f32,
137 pub window_spacing: f32,
138 pub seed: u32,
139 pub variation: f32,
140}
141
142#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
145pub struct RenderSettingsState {
146 pub ambient_light: [f32; 4],
147 pub bloom_enabled: bool,
148 pub bloom_intensity: f32,
149 pub bloom_threshold: f32,
150 pub bloom_knee: f32,
151 pub bloom_filter_radius: f32,
152 pub ssao_enabled: bool,
153 pub ssao_radius: f32,
154 pub ssao_intensity: f32,
155 pub ssao_bias: f32,
156 pub ssao_sample_count: u32,
157 pub ssao_visualization: bool,
158 pub ssgi_enabled: bool,
159 pub ssgi_radius: f32,
160 pub ssgi_intensity: f32,
161 pub ssgi_max_steps: u32,
162 pub ssr_enabled: bool,
163 pub ssr_max_steps: u32,
164 pub ssr_thickness: f32,
165 pub ssr_max_distance: f32,
166 pub ssr_stride: f32,
167 pub ssr_fade_start: f32,
168 pub ssr_fade_end: f32,
169 pub ssr_intensity: f32,
170 pub fog_enabled: bool,
171 pub fog_color: [f32; 3],
172 pub fog_start: f32,
173 pub fog_end: f32,
174 pub dof_enabled: bool,
175 pub dof_focus_distance: f32,
176 pub dof_focus_range: f32,
177 pub dof_max_blur_radius: f32,
178 pub dof_bokeh_threshold: f32,
179 pub dof_bokeh_intensity: f32,
180 pub dof_quality: u32,
181 pub taa_enabled: bool,
182 pub render_scale: f32,
183 pub ibl_blend_factor: f32,
184 pub pbr_debug_index: u32,
185 pub unlit_mode: bool,
186 pub selection_outline_enabled: bool,
187 pub selection_outline_color: [f32; 4],
188 pub exposure: f32,
189 pub saturation: f32,
190 pub contrast: f32,
191 pub brightness: f32,
192 pub gamma: f32,
193 pub show_grid: bool,
194 pub show_sky: bool,
195 pub show_normals: bool,
196 pub navmesh_debug: bool,
197 pub atmosphere: Atmosphere,
198}
199
200#[derive(Clone, PartialEq, Serialize, Deserialize)]
202pub struct EditorFlags {
203 pub gizmo_mode: GizmoMode,
204 pub snap_enabled: bool,
205 pub snap_translation_step: f32,
206 pub snap_rotation_step_degrees: f32,
207 pub snap_scale_step: f32,
208 pub greybox_enabled: bool,
209 pub greybox_show_final: bool,
210 pub placement_active: bool,
211 pub placement_shape: ShapeKind,
212 pub placement_size: [f32; 3],
213 pub placement_orient: bool,
214 pub placement_status: String,
215 pub push_pull_active: bool,
216 pub scripting_active: bool,
217 pub playground_active: bool,
218 pub fly_mode: bool,
219 pub viewer_mode: bool,
220 pub frame_on_load: bool,
221 pub day_night_hour: f32,
222 pub day_night_auto: bool,
223 pub skeleton_view: bool,
224 pub rotation_speed: f32,
225 pub active_palette: usize,
226 pub edit_mode_target: Option<u32>,
227 pub generation: GenerationSettings,
228 pub global_scripts: Vec<nightshade::ecs::script::components::GlobalScript>,
229}
230
231#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
233#[cfg_attr(feature = "protocol-agent", derive(enum2schema::Schema))]
234pub enum GlobalScriptCommand {
235 Add,
236 AddTemplate { name: String, source: String },
237 Remove(u32),
238 MoveUp(u32),
239 MoveDown(u32),
240 SetEnabled { index: u32, enabled: bool },
241 SetSource { index: u32, source: String },
242 Rename { index: u32, name: String },
243}
244
245#[derive(Clone, PartialEq, Serialize, Deserialize)]
247pub struct TreeRow {
248 pub id: u32,
249 pub name: String,
250 pub depth: u32,
251 pub has_children: bool,
252 pub camera: bool,
253 pub light: bool,
254 pub mesh: bool,
255}
256
257#[derive(Clone, PartialEq, Serialize, Deserialize)]
259pub struct PaletteEntry {
260 pub label: String,
261 pub color: [f32; 3],
262}
263
264#[derive(Clone, PartialEq, Serialize, Deserialize)]
266pub struct PrefabLink {
267 pub name: String,
268 pub source_path: Option<String>,
269}
270
271#[derive(Clone, PartialEq, Serialize, Deserialize)]
274pub struct MorphMeshInfo {
275 pub id: u32,
276 pub name: String,
277 pub weights: Vec<f32>,
278}
279
280#[derive(Clone, PartialEq, Serialize, Deserialize)]
282pub struct AnimationInfo {
283 pub clips: Vec<String>,
284 pub current: Option<u32>,
285 pub playing: bool,
286 pub time: f32,
287 pub duration: f32,
288 pub speed: f32,
289 pub looping: bool,
290}
291
292#[derive(Clone, Serialize, Deserialize)]
294pub struct SelectedEntity {
295 pub id: u32,
296 pub revision: u32,
299 pub name: String,
300 pub translation: [f32; 3],
301 pub rotation: [f32; 3],
302 pub scale: [f32; 3],
303 pub mesh: Option<String>,
304 pub material_name: Option<String>,
305 pub tags: Vec<String>,
306 pub layer: Option<u32>,
307 pub chunk: Option<u32>,
308 pub prefab: Option<PrefabLink>,
309 pub animation: Option<AnimationInfo>,
310 pub morph_weights: Option<Vec<f32>>,
311 pub selected_count: u32,
312 pub visibility: Option<bool>,
313 pub casts_shadow: bool,
314 pub light: Option<Light>,
315 pub camera: Option<Camera>,
316 pub is_active_camera: bool,
317 pub rigid_body: Option<RigidBodyComponent>,
318 pub collider: Option<ColliderComponent>,
319 pub character_controller: Option<CharacterControllerComponent>,
320 pub navmesh_agent: Option<NavMeshAgent>,
321 pub particle_emitter: Option<Box<ParticleEmitter>>,
322 pub decal: Option<Decal>,
323 pub water: Option<Water>,
324 pub audio_source: Option<AudioSource>,
325 pub render_layer: Option<RenderLayer>,
326 pub culling_mask: Option<CullingMask>,
327 pub camera_culling_mask: Option<CameraCullingMask>,
328 pub ignore_parent_scale: bool,
329 pub text: Option<(String, TextProperties)>,
330 pub script: Option<String>,
331 pub beam: Option<Box<Beam>>,
332 pub lightning_bolt: Option<Box<LightningBolt>>,
333 pub trail: Option<Box<Trail>>,
334 pub vfx_animator: Option<Box<VfxAnimator>>,
335 pub present: Vec<ComponentKind>,
336 pub addable: Vec<ComponentKind>,
337}
338
339#[derive(Clone, Serialize, Deserialize)]
341pub struct MaterialEntry {
342 pub name: String,
343 pub material: Material,
344}
345
346#[derive(Clone, PartialEq, Serialize, Deserialize)]
348pub struct KhronosEntry {
349 pub label: String,
350 pub thumbnail: Option<String>,
351}
352
353#[derive(Clone, PartialEq, Serialize, Deserialize)]
355pub struct PolyhavenEntry {
356 pub slug: String,
357 pub name: String,
358 pub thumbnail: String,
359}
360
361#[derive(Clone, PartialEq, Serialize, Deserialize)]
363pub struct SceneStructure {
364 pub layers: Vec<SceneLayerConfig>,
365 pub chunks: Vec<SceneChunkConfig>,
366}
367
368#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
371#[cfg_attr(feature = "protocol-agent", derive(enum2schema::Schema))]
372pub enum EditorAction {
373 ToggleGroundGrid,
374 ToggleSky,
375 ToggleLitUnlit,
376 ToggleViewerMode,
377 SetViewerMode(bool),
378 FrameScene,
379 ToggleFrameOnLoad,
380 ClearScene,
381 SnapSelectionToFloor,
382 BakeNavmesh,
383 ToggleNavmeshDebug,
384 SetAtmosphere(Atmosphere),
385 SetGizmoMode(GizmoMode),
386 RandomizeBoth,
387 SelectEntity(u32),
388 ToggleSelectEntity(u32),
389 ClearSelection,
390 AddEntityChild(u32),
391 ViewCamera(u32),
392 ToggleSnap,
393 ToggleSkeletonView,
394 SetMaterialVariant(Option<String>),
395 ToggleCameraGizmos,
396 ToggleShowNormals,
397 ToggleDayNightCycle,
398 SetDayNightHour(f32),
399 LoadKhronosByIndex(usize),
400 LoadPolyhavenByIndex {
401 category: PolyhavenCategory,
402 index: usize,
403 },
404 SetPolyhavenResolution(u32),
405 RefreshBrowsers,
406 DeepLink(String),
407 SpawnLines,
408 SpawnMeshes,
409 Spawn3DText,
410 SpawnTextLattice,
411 ClothSponza,
412 NewProject,
413 LoadExampleProject,
414 SaveProject,
415 ExportGlb,
416 SaveSelectedAsPrefab,
417 RefreshSelectedPrefab,
418 BreakSelectedPrefabLink,
419 SpawnPrefab(String),
420 Undo,
421 Redo,
422 DeleteEntity(u32),
423 DeleteSelectedEntity,
424 DuplicateEntity(u32),
425 DuplicateSelectedEntity,
426 AddShape(ShapeKind),
427 AddInstancedShape(ShapeKind),
428 ConvertSimilarToInstanced,
429 AddEmpty,
430 AddCamera,
431 AddPlayerSpawn,
432 AddSavePoint,
433 AddPatrolPoint,
434 AddTriggerVolume,
435 StampDecalAtCursor,
436 AddLight(LightKind),
437 AddTagToSelected(String),
438 RemoveTagFromSelected(String),
439 ToggleGreyboxMode,
440 ApplyGreyboxToSelection,
441 SnapSelectionToGrid,
442 MarkSelectionAsBrush,
443 ToggleShowFinal,
444 ToggleScripting,
445 SetScripting(bool),
448 SetPlayground(bool),
452 LoadLesson(String),
454 GlobalScript(GlobalScriptCommand),
455 TogglePushPull,
456 SetPlacementActive(bool),
457 SetPlacementShape(ShapeKind),
458 SetPlacementSize([f32; 3]),
459 SetOrientToNormal(bool),
460 RunGenerator(GeneratorKind),
461 SetGenerationSettings(GenerationSettings),
462 SetPaletteIndex(usize),
463 SetSnapEnabled(bool),
464 SetSnapSteps {
465 translation: f32,
466 rotation_degrees: f32,
467 scale: f32,
468 },
469 SetRotationSpeed(f32),
470 SetEntityLayer {
471 id: u32,
472 layer: Option<u32>,
473 },
474 SetEntityChunk {
475 id: u32,
476 chunk: Option<u32>,
477 },
478 AddSceneLayer,
479 RemoveSceneLayer(u32),
480 UpdateSceneLayer(SceneLayerConfig),
481 AddSceneChunk,
482 RemoveSceneChunk(u32),
483 UpdateSceneChunk(SceneChunkConfig),
484}
485
486#[derive(Clone, Serialize, Deserialize)]
488pub struct CameraEntry {
489 pub id: u32,
490 pub name: String,
491}
492
493#[derive(Clone, Copy, Serialize, Deserialize)]
496pub struct ViewportTile {
497 pub camera: u32,
498 pub x: f32,
499 pub y: f32,
500 pub width: f32,
501 pub height: f32,
502}
503
504#[derive(Clone, Serialize, Deserialize)]
508pub enum ClientMessage {
509 SetViewportTiles(Vec<ViewportTile>),
512 Action(Box<EditorAction>),
513 SetTransform {
514 id: u32,
515 translation: [f32; 3],
516 rotation: [f32; 3],
517 scale: [f32; 3],
518 committed: bool,
519 },
520 SetEntityName {
521 id: u32,
522 name: String,
523 },
524 SetComponent {
525 id: u32,
526 payload: Box<ComponentPatch>,
527 committed: bool,
528 },
529 AddComponent {
530 id: u32,
531 kind: ComponentKind,
532 },
533 RemoveComponent {
534 id: u32,
535 kind: ComponentKind,
536 },
537 Animation {
538 id: u32,
539 command: AnimationCommand,
540 },
541 UpdateMaterial {
542 name: String,
543 material: Box<Material>,
544 committed: bool,
545 },
546 SetRenderSettings(Box<RenderSettingsState>),
547 DropAsset {
551 name: String,
552 },
553 LoadGltfBundle {
556 name: String,
557 },
558 UpgradeBrush {
561 name: String,
562 },
563 SubmitCommand {
568 command: String,
569 },
570 LoadScriptAsset {
576 name: String,
577 url: String,
578 },
579 #[cfg(feature = "protocol-agent")]
582 Agent(Box<AgentRequest>),
583}
584
585#[derive(Clone, Serialize, Deserialize)]
588pub enum WorkerMessage {
589 Camera {
591 right: [f32; 3],
592 up: [f32; 3],
593 forward: [f32; 3],
594 },
595 ScriptAssetLoaded {
598 name: String,
599 },
600 ScriptAssetFailed {
602 name: String,
603 error: String,
604 },
605 Scene {
606 rows: Vec<TreeRow>,
607 },
608 Selected {
609 detail: Option<Box<SelectedEntity>>,
610 },
611 Cameras {
614 entries: Vec<CameraEntry>,
615 },
616 Animation {
618 time: f32,
619 duration: f32,
620 playing: bool,
621 clip: Option<u32>,
622 },
623 MorphWeights {
626 id: u32,
627 weights: Vec<f32>,
628 },
629 MorphMeshes {
632 entries: Vec<MorphMeshInfo>,
633 },
634 MaterialVariants {
637 names: Vec<String>,
638 active: Option<String>,
639 },
640 RenderSettings(Box<RenderSettingsState>),
641 Flags(Box<EditorFlags>),
642 Materials {
643 entries: Vec<MaterialEntry>,
644 },
645 Tags {
646 entries: Vec<(String, u32)>,
647 },
648 Structure(SceneStructure),
649 Palette {
650 entries: Vec<PaletteEntry>,
651 },
652 KhronosList {
653 entries: Vec<KhronosEntry>,
654 },
655 PolyhavenList {
656 category: PolyhavenCategory,
657 entries: Vec<PolyhavenEntry>,
658 },
659 Loading {
660 active: bool,
661 label: String,
662 },
663 Project {
665 name: String,
666 modified: bool,
667 model: Option<String>,
668 mode: String,
669 },
670 SaveFile {
673 name: String,
674 },
675 PointerLock {
677 locked: bool,
678 },
679 FocusEntity {
681 id: u32,
682 },
683 UndoState {
684 can_undo: bool,
685 can_redo: bool,
686 },
687 Toast {
688 message: String,
689 error: bool,
690 },
691 CommandLog {
694 entries: Vec<CommandLogEntry>,
695 },
696 #[cfg(feature = "protocol-agent")]
699 Agent(Box<AgentResponse>),
700}
701
702#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
705pub enum CommandLogKind {
706 Command,
707 Event,
708 Error,
709}
710
711#[derive(Clone, Debug, Serialize, Deserialize)]
714pub struct CommandLogEntry {
715 pub cycle: u64,
716 pub kind: CommandLogKind,
717 pub label: String,
718 pub detail: String,
719 pub entities: Vec<u32>,
722}
723
724impl CommandLogEntry {
725 pub fn from_event(cycle: u64, event: &Event) -> Self {
728 Self {
729 cycle,
730 kind: CommandLogKind::Event,
731 label: event_label(event).to_string(),
732 detail: event_detail(event),
733 entities: event_entities(event),
734 }
735 }
736
737 pub fn from_command(cycle: u64, command: &Command) -> Self {
742 use enum2schema::serde_json::Value;
743 let value: Value = enum2schema::serde_json::to_value(command).unwrap_or(Value::Null);
744 let detail = value
745 .as_object()
746 .and_then(|object| object.values().next())
747 .map(|inner| inner.to_string())
748 .unwrap_or_else(|| value.to_string());
749 let mut entities = Vec::new();
750 collect_entity_ids(&value, &mut entities);
751 Self {
752 cycle,
753 kind: CommandLogKind::Command,
754 label: command.name().to_string(),
755 detail,
756 entities,
757 }
758 }
759
760 pub fn from_error(cycle: u64, message: String) -> Self {
764 Self {
765 cycle,
766 kind: CommandLogKind::Error,
767 label: "error".to_string(),
768 detail: message,
769 entities: Vec::new(),
770 }
771 }
772}
773
774fn event_label(event: &Event) -> &'static str {
775 match event {
776 Event::Collision { .. } => "Collision",
777 Event::Despawned { .. } => "Despawned",
778 Event::AnimationFinished { .. } => "AnimationFinished",
779 Event::AnimationEvent { .. } => "AnimationEvent",
780 Event::NavigationArrived { .. } => "NavigationArrived",
781 }
782}
783
784fn event_detail(event: &Event) -> String {
785 match event {
786 Event::Collision {
787 a,
788 b,
789 sensor,
790 started,
791 } => format!(
792 "#{} and #{}, {}{}",
793 a.id,
794 b.id,
795 if *started { "started" } else { "ended" },
796 if *sensor { ", sensor" } else { "" }
797 ),
798 Event::AnimationEvent { entity, name } => format!("#{} {}", entity.id, name),
799 Event::Despawned { entity }
800 | Event::AnimationFinished { entity }
801 | Event::NavigationArrived { entity } => format!("#{}", entity.id),
802 }
803}
804
805fn event_entities(event: &Event) -> Vec<u32> {
806 match event {
807 Event::Collision { a, b, .. } => vec![a.id, b.id],
808 Event::AnimationEvent { entity, .. } => vec![entity.id],
809 Event::Despawned { entity }
810 | Event::AnimationFinished { entity }
811 | Event::NavigationArrived { entity } => vec![entity.id],
812 }
813}
814
815fn collect_entity_ids(value: &enum2schema::serde_json::Value, ids: &mut Vec<u32>) {
818 use enum2schema::serde_json::Value;
819 match value {
820 Value::Object(map) => {
821 if let Some(id) = map.get("Existing").and_then(Value::as_u64) {
822 ids.push(id as u32);
823 }
824 if let Some(id) = map
825 .get("Entity")
826 .and_then(Value::as_object)
827 .and_then(|entity| entity.get("id"))
828 .and_then(Value::as_u64)
829 {
830 ids.push(id as u32);
831 }
832 for nested in map.values() {
833 collect_entity_ids(nested, ids);
834 }
835 }
836 Value::Array(array) => {
837 for nested in array {
838 collect_entity_ids(nested, ids);
839 }
840 }
841 _ => {}
842 }
843}
844
845#[cfg(all(test, feature = "protocol-agent"))]
846mod schema_tests {
847 use super::EditorAction;
848 use enum2schema::Schema;
849 use enum2schema::serde_json::Value;
850
851 fn assert_no_array_items(value: &Value) {
856 match value {
857 Value::Object(object) => {
858 if let Some(items) = object.get("items") {
859 assert!(
860 !items.is_array(),
861 "array-valued `items` is invalid in draft 2020-12, use `prefixItems`: {object:?}"
862 );
863 }
864 for nested in object.values() {
865 assert_no_array_items(nested);
866 }
867 }
868 Value::Array(array) => {
869 for nested in array {
870 assert_no_array_items(nested);
871 }
872 }
873 _ => {}
874 }
875 }
876
877 #[test]
878 fn editor_action_schema_is_draft_2020_12() {
879 assert_no_array_items(&EditorAction::schema());
880 }
881}