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