1use crate::reflect::{ComponentKind, addable_components, present_components};
6use nightshade::ecs::animation::components::AnimationPlayer;
7use nightshade::ecs::camera::components::Camera;
8use nightshade::ecs::decal::components::Decal;
9use nightshade::ecs::primitives::{CameraCullingMask, CullingMask};
10use nightshade::ecs::text::components::TextProperties;
11use nightshade::plugins::audio::components::AudioSource;
12use nightshade::prelude::*;
13use nightshade::render::material::Material;
14use nightshade::render::particles::ParticleEmitter;
15use nightshade::render::render_layer::RenderLayer;
16use serde::{Deserialize, Serialize};
17
18pub fn quat_to_euler_xyz(quat: &Quat) -> (f32, f32, f32) {
21 let sinr = 2.0 * (quat.w * quat.i + quat.j * quat.k);
22 let cosr = 1.0 - 2.0 * (quat.i * quat.i + quat.j * quat.j);
23 let roll = sinr.atan2(cosr);
24
25 let sinp = 2.0 * (quat.w * quat.j - quat.k * quat.i);
26 let pitch = if sinp.abs() >= 1.0 {
27 std::f32::consts::FRAC_PI_2.copysign(sinp)
28 } else {
29 sinp.asin()
30 };
31
32 let siny = 2.0 * (quat.w * quat.k + quat.i * quat.j);
33 let cosy = 1.0 - 2.0 * (quat.j * quat.j + quat.k * quat.k);
34 let yaw = siny.atan2(cosy);
35
36 (roll, pitch, yaw)
37}
38
39pub fn euler_xyz_to_quat(roll: f32, pitch: f32, yaw: f32) -> Quat {
42 let cr = (roll * 0.5).cos();
43 let sr = (roll * 0.5).sin();
44 let cp = (pitch * 0.5).cos();
45 let sp = (pitch * 0.5).sin();
46 let cy = (yaw * 0.5).cos();
47 let sy = (yaw * 0.5).sin();
48
49 Quat::from_parts(
50 cr * cp * cy + sr * sp * sy,
51 nalgebra_glm::Vec3::new(
52 sr * cp * cy - cr * sp * sy,
53 cr * sp * cy + sr * cp * sy,
54 cr * cp * sy - sr * sp * cy,
55 ),
56 )
57}
58
59pub fn material_of(world: &World, entity: Entity) -> Option<Material> {
62 let material_ref = world.get::<nightshade::ecs::material::components::MaterialRef>(entity)?;
63 registry_entry_by_name(
64 &world.resources.assets.material_registry.registry,
65 &material_ref.name,
66 )
67 .cloned()
68}
69
70pub fn color(world: &World, entity: Entity) -> Option<[f32; 4]> {
73 material_of(world, entity).map(|material| material.base_color)
74}
75
76pub fn metallic_roughness(world: &World, entity: Entity) -> Option<(f32, f32)> {
78 material_of(world, entity).map(|material| (material.metallic, material.roughness))
79}
80
81pub fn emissive(world: &World, entity: Entity) -> Option<([f32; 3], f32)> {
83 material_of(world, entity)
84 .map(|material| (material.emissive_factor, material.emissive_strength))
85}
86
87pub fn unlit(world: &World, entity: Entity) -> Option<bool> {
89 material_of(world, entity).map(|material| material.unlit)
90}
91
92pub fn texture(world: &World, entity: Entity) -> Option<String> {
94 material_of(world, entity).and_then(|material| material.base_texture)
95}
96
97#[derive(Clone, PartialEq, Serialize, Deserialize)]
99pub struct AnimationView {
100 pub clips: Vec<String>,
101 pub current: Option<u32>,
102 pub playing: bool,
103 pub time: f32,
104 pub duration: f32,
105 pub speed: f32,
106 pub looping: bool,
107}
108
109pub fn animation_view(player: &AnimationPlayer) -> AnimationView {
111 let duration = player
112 .get_current_clip()
113 .map(|clip| clip.duration)
114 .unwrap_or(0.0);
115 AnimationView {
116 clips: player.clips.iter().map(|clip| clip.name.clone()).collect(),
117 current: player.current_clip.map(|index| index as u32),
118 playing: player.playing,
119 time: player.time,
120 duration,
121 speed: player.speed,
122 looping: player.looping,
123 }
124}
125
126#[derive(Clone, Serialize, Deserialize)]
131pub struct EntityView {
132 pub id: u32,
133 pub name: String,
134 pub translation: [f32; 3],
135 pub rotation: [f32; 3],
136 pub scale: [f32; 3],
137 pub mesh: Option<String>,
138 pub material_name: Option<String>,
139 pub tags: Vec<String>,
140 pub animation: Option<AnimationView>,
141 pub morph_weights: Option<Vec<f32>>,
142 pub visibility: Option<bool>,
143 pub casts_shadow: bool,
144 pub light: Option<Light>,
145 pub camera: Option<Camera>,
146 pub is_active_camera: bool,
147 pub rigid_body: Option<RigidBodyComponent>,
148 pub collider: Option<ColliderComponent>,
149 pub character_controller: Option<CharacterControllerComponent>,
150 pub navmesh_agent: Option<NavMeshAgent>,
151 pub particle_emitter: Option<Box<ParticleEmitter>>,
152 pub decal: Option<Decal>,
153 pub audio_source: Option<AudioSource>,
154 pub render_layer: Option<RenderLayer>,
155 pub culling_mask: Option<CullingMask>,
156 pub camera_culling_mask: Option<CameraCullingMask>,
157 pub ignore_parent_scale: bool,
158 pub text: Option<(String, TextProperties)>,
159 pub script: Option<String>,
160 pub present: Vec<ComponentKind>,
161 pub addable: Vec<ComponentKind>,
162}
163
164pub fn describe_entity(world: &World, entity: Entity) -> Option<EntityView> {
167 let transform = world
168 .get::<nightshade::ecs::transform::components::LocalTransform>(entity)
169 .copied()?;
170 let (roll, pitch, yaw) = quat_to_euler_xyz(&transform.rotation);
171
172 let name = world
173 .get::<nightshade::ecs::primitives::Name>(entity)
174 .map(|name| name.0.clone())
175 .filter(|name| !name.is_empty())
176 .unwrap_or_else(|| format!("Entity {}", entity.id));
177
178 let text = world
179 .get::<nightshade::ecs::text::components::Text>(entity)
180 .map(|text| {
181 let content = world
182 .resources
183 .text
184 .cache
185 .get_text(text.text_index)
186 .map(str::to_string)
187 .unwrap_or_default();
188 (content, text.properties.clone())
189 });
190
191 Some(EntityView {
192 id: entity.id,
193 name,
194 translation: [
195 transform.translation.x,
196 transform.translation.y,
197 transform.translation.z,
198 ],
199 rotation: [roll.to_degrees(), pitch.to_degrees(), yaw.to_degrees()],
200 scale: [transform.scale.x, transform.scale.y, transform.scale.z],
201 mesh: world
202 .get::<nightshade::ecs::mesh::components::RenderMesh>(entity)
203 .map(|mesh| mesh.name.clone()),
204 material_name: world
205 .get::<nightshade::ecs::material::components::MaterialRef>(entity)
206 .map(|reference| reference.name.clone()),
207 tags: world
208 .resources
209 .entities
210 .tags
211 .get(&entity)
212 .cloned()
213 .unwrap_or_default(),
214 animation: world
215 .get::<nightshade::ecs::animation::components::AnimationPlayer>(entity)
216 .map(animation_view),
217 morph_weights: world
218 .get::<nightshade::ecs::morph::components::MorphWeights>(entity)
219 .map(|weights| weights.weights.clone()),
220 visibility: world
221 .get::<nightshade::ecs::primitives::Visibility>(entity)
222 .map(|visibility| visibility.visible),
223 casts_shadow: world.has::<nightshade::ecs::primitives::CastsShadow>(entity),
224 light: world
225 .get::<nightshade::ecs::light::components::Light>(entity)
226 .cloned(),
227 camera: world
228 .get::<nightshade::ecs::camera::components::Camera>(entity)
229 .copied(),
230 is_active_camera: world.resources.active_camera == Some(entity),
231 rigid_body: world
232 .get::<nightshade::plugins::physics::components::RigidBodyComponent>(entity)
233 .cloned(),
234 collider: world
235 .get::<nightshade::plugins::physics::components::ColliderComponent>(entity)
236 .cloned(),
237 character_controller: world
238 .get::<nightshade::plugins::physics::components::CharacterControllerComponent>(entity)
239 .cloned(),
240 navmesh_agent: world
241 .get::<nightshade::plugins::navmesh::components::NavMeshAgent>(entity)
242 .cloned(),
243 particle_emitter: world
244 .get::<nightshade::render::particles::ParticleEmitter>(entity)
245 .cloned()
246 .map(Box::new),
247 decal: world
248 .get::<nightshade::ecs::decal::components::Decal>(entity)
249 .cloned(),
250 audio_source: world
251 .get::<nightshade::plugins::audio::components::AudioSource>(entity)
252 .cloned(),
253 render_layer: world
254 .get::<nightshade::render::render_layer::RenderLayer>(entity)
255 .copied(),
256 culling_mask: world
257 .get::<nightshade::ecs::primitives::CullingMask>(entity)
258 .copied(),
259 camera_culling_mask: world
260 .get::<nightshade::ecs::primitives::CameraCullingMask>(entity)
261 .copied(),
262 ignore_parent_scale: world
263 .has::<nightshade::ecs::transform::components::IgnoreParentScale>(entity),
264 text,
265 script: world
266 .get::<nightshade::ecs::script::components::Script>(entity)
267 .map(|script| script.source_text().to_string()),
268 present: present_components(world, entity),
269 addable: addable_components(world, entity),
270 })
271}