Skip to main content

concinnity_core/components/
mod.rs

1//! Every component type the runtime can store: the ones an authored world
2//! declares and the ones only the runtime mints, plus the resources the cook
3//! compiles into the blob's resource stream.
4//!
5//! Each module here is one asset, both halves together: the authored data
6//! schema a world.jsonl declares and whatever runtime behavior it needs -- a
7//! runtime struct distinct from its authored args, an extension trait, a
8//! build-time `SourceBacked` binding, or a helper the generated `Component`
9//! impl can't express. Most components are pure data whose impl is generated
10//! from the registry (see `cn_impl_components!` in `ecs::registry`).
11//!
12//! The authoring-only vocabulary -- the types a world declares and the cook
13//! expands away, and the authored args schemas that diverge from the component
14//! they bake into -- is not named here. It lives in
15//! [`cook`], and the registry half of it in
16//! `concinnity_cook::authoring::registry::build_only`.
17//!
18//! Systems are not components: every system is internal code (see
19//! `World::start`), driven by the presence of the components defined here. The
20//! client re-exports this module under the historical `crate::components::*`
21//! paths.
22
23// Component data types.
24mod animation;
25mod animation_graph;
26mod animation_params;
27mod app_config;
28mod audio_bus;
29mod audio_clip;
30mod audio_command;
31mod audio_cue;
32mod audio_emitter;
33mod audio_occlusion_probe;
34mod behavior;
35mod block_type;
36mod camera3d;
37mod camera_probe;
38mod character_rig;
39mod character_shape;
40mod color_lut;
41mod contact_event;
42mod controls_command;
43mod cubemap_texture;
44mod debug_hud;
45mod decal;
46mod despawn_request;
47mod directional_light;
48mod engine_defaults;
49mod entity_target;
50mod environment_map;
51mod file;
52mod font;
53mod fps_counter;
54mod frame_input;
55mod gamepad_button;
56mod gamepad_map;
57mod geometry;
58mod glass_panel;
59mod graphics_config;
60mod ground_probes;
61mod hit_region;
62mod input_key;
63mod instanced_prop;
64mod interact_event;
65mod key_binding;
66mod layout_container;
67mod lifetime;
68mod loading_overlay;
69mod material;
70mod mesh;
71mod model;
72mod nav_direction;
73mod particle_emitter;
74mod physics_config;
75mod physics_joint;
76mod play_cue;
77mod point_light;
78mod post_process_config;
79pub mod procedural_mesh;
80mod prop;
81mod prop_body;
82mod rect_area_light;
83mod reflection_probe;
84mod reparent_request;
85mod rigid_body;
86mod room;
87mod root_motion_event;
88mod scene;
89mod scene_command;
90mod screen;
91mod screen_command;
92mod screen_shown;
93mod scroll_panel;
94pub mod sdf_volume;
95mod setting_command;
96pub mod shader;
97mod skeleton_pose;
98mod skinned_mesh;
99mod spawn_request;
100mod spawner;
101mod spot_light;
102mod sprite;
103mod stat_hud;
104mod story;
105mod story_command;
106mod streaming_config;
107mod text_input;
108mod text_label;
109mod texture;
110mod trigger_volume;
111mod variables;
112mod visibility_request;
113mod volume_event;
114mod volumetric_fog;
115mod voxel_chunk;
116mod voxel_world;
117mod water_surface;
118mod window;
119
120// Per-instance components an entity is composed from: its placement, render
121// description, collision, hierarchy, and gameplay tags.
122mod body_dynamics;
123mod children;
124mod collider;
125mod global_transform;
126mod held;
127mod hidden;
128mod interactable;
129mod mesh_renderer;
130mod model_renderer;
131mod parent;
132mod pickup;
133mod prop_instance;
134mod render_handle;
135mod scene_member;
136mod transform;
137
138pub mod cook;
139pub mod stored;
140pub mod validate;
141
142// Serde / default / round-trip coverage for the generated data-only
143// components, gathered here after their per-type modules were removed.
144#[cfg(test)]
145mod component_tests;
146
147pub use animation::{Animation, AnimationTrack, Keyframe, MorphKey};
148pub use animation_graph::{
149    AnimationBlend, AnimationBlendPoint, AnimationCondition, AnimationGraph, AnimationIkChain,
150    AnimationParam, AnimationState, AnimationTransition,
151};
152pub use animation_params::AnimationParams;
153pub use app_config::AppConfig;
154pub use audio_bus::AudioBus;
155pub use audio_clip::AudioClip;
156pub use audio_command::{AudioCommand, AudioTarget};
157pub use audio_cue::AudioCue;
158pub use audio_cue::CueKind;
159pub use audio_emitter::AudioEmitter;
160pub use audio_emitter::Rolloff;
161pub use audio_occlusion_probe::AudioOcclusionProbe;
162pub use behavior::Behavior;
163pub use behavior::BehaviorExpr;
164pub use behavior::BehaviorLiteral;
165pub use behavior::BehaviorLocal;
166pub use behavior::BehaviorNode;
167pub use behavior::BehaviorQuery;
168pub use behavior::BehaviorSource;
169pub use block_type::BlockType;
170pub use camera_probe::CameraProbe;
171pub use camera3d::Camera3D;
172pub use camera3d::CameraController;
173pub use camera3d::FollowController;
174pub use camera3d::FollowDrive;
175pub use character_rig::CharacterRig;
176pub use character_shape::CharacterShape;
177pub use character_shape::JointProportion;
178pub use character_shape::ResolvedSliders;
179pub use character_shape::ShapeSlider;
180pub use color_lut::ColorLut;
181pub use contact_event::ContactEvent;
182pub use controls_command::ControlsCommand;
183pub use cubemap_texture::CubemapTexture;
184pub use decal::Decal;
185pub use despawn_request::DespawnRequest;
186pub use directional_light::DirectionalLight;
187pub use engine_defaults::EngineDefaults;
188pub use entity_target::EntityTarget;
189pub use environment_map::EnvironmentMap;
190pub use file::File;
191pub use file::FileKind;
192pub use font::Font;
193pub use frame_input::FrameInput;
194pub use gamepad_button::GamepadButton;
195pub use gamepad_map::{GamepadAction, GamepadMap};
196pub use geometry::{
197    GlassPanelGeometry, InstancedPropGeometry, RectAreaLightGeometry, SPOT_MAX_ANGLE_DEG,
198    SpotLightGeometry,
199};
200pub use glass_panel::GlassPanel;
201pub use graphics_config::GraphicsConfig;
202pub use graphics_config::ShadowUpdate;
203pub use ground_probes::{GroundProbe, GroundProbes};
204pub use hit_region::HitRegion;
205pub use input_key::InputKey;
206pub use instanced_prop::InstanceTransform;
207pub use instanced_prop::InstancedProp;
208pub use interact_event::InteractEvent;
209pub use key_binding::KeyBinding;
210pub use layout_container::Justify;
211pub use layout_container::LabelBox;
212pub use layout_container::LabelPlacement;
213pub use layout_container::LayoutContainer;
214pub use layout_container::LayoutRow;
215pub use lifetime::Lifetime;
216pub use loading_overlay::LoadingOverlay;
217pub use material::Material;
218pub use mesh::Mesh;
219pub use mesh::VertexData;
220pub use model::Model;
221pub use model::SubMeshRef;
222pub use nav_direction::NavDirection;
223pub use particle_emitter::ParticleEmitter;
224pub use physics_config::PhysicsConfig;
225pub use physics_joint::PhysicsJoint;
226pub use physics_joint::PhysicsJointKind;
227pub use play_cue::PlayCue;
228pub use point_light::PointLight;
229pub use post_process_config::AaMode;
230pub use post_process_config::IndirectLighting;
231pub use post_process_config::PostProcessConfig;
232pub use post_process_config::PostProcessResolve;
233pub use post_process_config::ReflectionBlurResolution;
234pub use post_process_config::SsgiResolution;
235pub use post_process_config::UpscaleQuality;
236pub use post_process_config::UpscalerBackend;
237pub use procedural_mesh::ProceduralMesh;
238pub use prop::Prop;
239pub use prop::PropCollider;
240pub use prop_body::PropBody;
241pub use rect_area_light::RectAreaLight;
242pub use reflection_probe::ReflectionProbe;
243pub use reparent_request::ReparentRequest;
244pub use rigid_body::RigidBody;
245pub use room::Room;
246pub use root_motion_event::RootMotionEvent;
247pub use scene::Scene;
248pub use scene_command::SceneCommand;
249pub use screen::Screen;
250pub use screen::ScreenInput;
251pub use screen_command::ScreenCommand;
252pub use screen_shown::ScreenShown;
253pub use scroll_panel::ScrollGroup;
254pub use scroll_panel::ScrollPanel;
255pub use scroll_panel::ScrollRow;
256pub use sdf_volume::SdfVolume;
257pub use setting_command::{SettingCommand, SettingOp};
258pub use shader::{Shader, ShaderKind, ShaderPayload, StageSource};
259pub use skeleton_pose::SkeletonPose;
260pub use skinned_mesh::CharacterCapsule;
261pub use skinned_mesh::MorphDelta;
262pub use skinned_mesh::SkeletonJoint;
263pub use skinned_mesh::SkinnedMesh;
264pub use skinned_mesh::SkinnedVertexData;
265pub use skinned_mesh::{SkinnedMeshGeometry, build_skeleton_from_joint_defs};
266pub use spawn_request::SpawnRequest;
267pub use spawner::Spawner;
268pub use spot_light::SpotLight;
269pub use sprite::Sprite;
270pub use sprite::SpriteFit;
271pub use story::Story;
272pub use story::StoryChoice;
273pub use story::StoryCompareOp;
274pub use story::StoryCondition;
275pub use story::StoryGate;
276pub use story::StoryImage;
277pub use story::StoryNode;
278pub use story::StoryOp;
279pub use story::StoryPage;
280pub use story::StoryPlayback;
281pub use story::StoryReload;
282pub use story::StoryScaffold;
283pub use story::StorySpeaker;
284pub use story::StoryStage;
285pub use story_command::StoryCommand;
286pub use streaming_config::StreamingConfig;
287pub use text_input::TextInput;
288pub use text_label::TextAlign;
289pub use text_label::TextLabel;
290pub use texture::Texture;
291pub use trigger_volume::TriggerFilter;
292pub use trigger_volume::TriggerVolume;
293pub use variables::VariableDecl;
294pub use variables::Variables;
295pub use visibility_request::VisibilityRequest;
296pub use volume_event::VolumeEvent;
297pub use volumetric_fog::VolumetricFog;
298pub use voxel_chunk::VoxelChunk;
299pub use voxel_world::VoxelWorld;
300pub use water_surface::MAX_WATER_WAVES;
301pub use water_surface::WaterSurface;
302pub use water_surface::WaterWave;
303pub use window::Window;
304pub use window::WindowMode;
305
306// Per-instance components an entity is composed from.
307pub use body_dynamics::BodyDynamics;
308pub use children::Children;
309pub use collider::Collider;
310pub use global_transform::GlobalTransform;
311pub use held::Held;
312pub use hidden::Hidden;
313pub use interactable::Interactable;
314pub use mesh_renderer::MeshRenderer;
315pub use model_renderer::ModelRenderer;
316pub use parent::Parent;
317pub use pickup::Pickup;
318pub use prop_instance::PropInstance;
319pub use render_handle::RenderHandle;
320pub use scene_member::SceneMember;
321pub use transform::Transform;
322
323// HUD-overlay request components; their behavior lives in the client crate.
324pub use debug_hud::DebugHud;
325pub use fps_counter::FpsCounter;
326pub use stat_hud::StatHud;
327
328// The file-name extension of a path (the chars after the last `.` of its final
329// component), or `None` when that component has no extension. A pure, no_std
330// stand-in for `Path::new(p).extension().and_then(|e| e.to_str())` over the
331// asset-relative source strings the asset types carry.
332pub(crate) fn path_extension(path: &str) -> Option<&str> {
333    let name = path.rsplit(['/', '\\']).next().unwrap_or(path);
334    match name.rsplit_once('.') {
335        Some((stem, ext)) if !stem.is_empty() => Some(ext),
336        _ => None,
337    }
338}
339
340// Bounds and capacities the engine reads off the schema. Not vocabulary: they
341// declare nothing, so they stay out of both namespaces.
342pub use post_process_config::{DEFAULT_SSGI_RAYS, DEFAULT_SSGI_STEPS};
343#[cfg(test)]
344mod tests {
345    // Uniform, low-level checks over the small data-only asset types: their
346    // derive impls, custom Defaults, arg round-trips, injection hooks,
347    // source_path branches, and cross-reference declarations. Kept in one place
348    // because the checks are identical in shape across many one-file components.
349    use super::*;
350    use crate::ecs::asset_id::AssetId;
351    use crate::ecs::{Component, PayloadLocator};
352
353    // Round-trip an asset's defaults through its baked form and the Component
354    // hooks. One call executes the type's Default, serialization, `from_baked`,
355    // `inject_name`, `inject_locator`, and the frame-exactness check.
356    fn exercise<C: Component + Default + serde::Serialize>() {
357        let bytes = postcard::to_allocvec(&C::default()).expect("default serializes");
358        let mut comp = C::from_baked(&bytes).expect("baked bytes deserialize");
359        comp.inject_name(AssetId::default());
360        comp.inject_locator(PayloadLocator {
361            blob_index: 0,
362            offset: 0,
363            len: 0,
364        });
365
366        // A record written by a schema carrying a field this build no longer
367        // reads leaves the tail of its frame unread. `from_baked` takes the
368        // whole frame or fails.
369        let mut widened = bytes.clone();
370        widened.push(0);
371        assert!(
372            C::from_baked(&widened).is_err(),
373            "{} accepted a record with an unread trailing byte",
374            core::any::type_name::<C>()
375        );
376    }
377
378    #[test]
379    fn path_extension_matches_std_path_semantics() {
380        assert_eq!(path_extension("foo.metal"), Some("metal"));
381        assert_eq!(path_extension("shaders/pbr.hlsl"), Some("hlsl"));
382        assert_eq!(path_extension("a.b.glsl"), Some("glsl"));
383        // No extension, a dotfile, and a dotted directory with an extensionless
384        // file all resolve to None, matching `Path::extension`.
385        assert_eq!(path_extension("plain"), None);
386        assert_eq!(path_extension(".bashrc"), None);
387        assert_eq!(path_extension("dir.v2/plain"), None);
388    }
389
390    #[test]
391    fn simple_assets_round_trip_defaults() {
392        exercise::<Scene>();
393        exercise::<Model>();
394        exercise::<ProceduralMesh>();
395        exercise::<WaterSurface>();
396        exercise::<Decal>();
397        exercise::<CharacterShape>();
398        exercise::<ParticleEmitter>();
399        exercise::<VoxelWorld>();
400        exercise::<VoxelChunk>();
401    }
402}