1use khora_core::ecs::entity::EntityId;
39use khora_core::math::{Aabb, Vec2, Vec3};
40use khora_core::renderer::api::{
41 pipeline::{PrimitiveTopology, VertexAttributeDescriptor, VertexFormat},
42 scene::Mesh,
43};
44use khora_data::ecs::{GlobalTransform, Transform};
45
46use crate::GameWorld;
47
48pub struct Vessel<'a> {
58 world: &'a mut GameWorld,
59 entity: EntityId,
60 transform: Transform,
61}
62
63impl<'a> Vessel<'a> {
64 pub fn new(world: &'a mut GameWorld) -> Self {
68 let transform = Transform::identity();
69 let global = GlobalTransform::new(transform.to_mat4());
70 let entity = world.spawn((transform, global));
71
72 Self {
73 world,
74 entity,
75 transform,
76 }
77 }
78
79 pub fn at(world: &'a mut GameWorld, position: Vec3) -> Self {
81 let transform = Transform::from_translation(position);
82 let global = GlobalTransform::new(transform.to_mat4());
83 let entity = world.spawn((transform, global));
84
85 Self {
86 world,
87 entity,
88 transform,
89 }
90 }
91
92 pub fn with_transform(mut self, transform: Transform) -> Self {
94 self.transform = transform;
95 self
96 }
97
98 pub fn at_position(mut self, position: Vec3) -> Self {
100 self.transform.translation = position;
101 self
102 }
103
104 pub fn with_rotation(mut self, rotation: khora_core::math::Quaternion) -> Self {
106 self.transform.rotation = rotation;
107 self
108 }
109
110 pub fn with_scale(mut self, scale: Vec3) -> Self {
112 self.transform.scale = scale;
113 self
114 }
115
116 pub fn with_component<C: khora_data::ecs::Component>(self, component: C) -> Self {
122 self.world.add_component(self.entity, component);
123 self
124 }
125
126 pub fn entity(&self) -> EntityId {
128 self.entity
129 }
130
131 pub fn build(self) -> EntityId {
135 if let Some(existing_transform) = self.world.get_component_mut::<Transform>(self.entity) {
137 *existing_transform = self.transform;
138 }
139
140 let global = GlobalTransform::new(self.transform.to_mat4());
142 if let Some(existing_global) = self.world.get_component_mut::<GlobalTransform>(self.entity)
143 {
144 *existing_global = global;
145 }
146
147 self.entity
148 }
149}
150
151pub fn spawn_plane<'a>(world: &'a mut GameWorld, size: f32, y: f32) -> Vessel<'a> {
153 let mesh = create_plane(size, y);
154 let handle = world.add_mesh(mesh);
155 Vessel::new(world).with_component(handle)
156}
157
158pub fn spawn_cube_at<'a>(world: &'a mut GameWorld, position: Vec3, size: f32) -> Vessel<'a> {
160 let mesh = create_cube(size);
161 let handle = world.add_mesh(mesh);
162 Vessel::at(world, position).with_component(handle)
163}
164
165pub fn spawn_sphere<'a>(
167 world: &'a mut GameWorld,
168 radius: f32,
169 segments: u32,
170 rings: u32,
171) -> Vessel<'a> {
172 let mesh = create_sphere(radius, segments, rings);
173 let handle = world.add_mesh(mesh);
174 Vessel::new(world).with_component(handle)
175}
176
177fn create_plane(size: f32, y: f32) -> Mesh {
183 let half = size / 2.0;
184
185 let positions = vec![
186 Vec3::new(-half, y, -half),
187 Vec3::new(half, y, -half),
188 Vec3::new(half, y, half),
189 Vec3::new(-half, y, half),
190 ];
191
192 let normals = vec![
193 Vec3::new(0.0, 1.0, 0.0),
194 Vec3::new(0.0, 1.0, 0.0),
195 Vec3::new(0.0, 1.0, 0.0),
196 Vec3::new(0.0, 1.0, 0.0),
197 ];
198
199 let tex_coords = vec![
200 Vec2::new(0.0, 0.0),
201 Vec2::new(1.0, 0.0),
202 Vec2::new(1.0, 1.0),
203 Vec2::new(0.0, 1.0),
204 ];
205
206 let indices = vec![0u32, 1, 2, 0, 2, 3];
207
208 let vertex_layout = vec![
210 VertexAttributeDescriptor {
211 shader_location: 0,
212 format: VertexFormat::Float32x3,
213 offset: 0,
214 },
215 VertexAttributeDescriptor {
216 shader_location: 1,
217 format: VertexFormat::Float32x3,
218 offset: 12,
219 },
220 VertexAttributeDescriptor {
221 shader_location: 2,
222 format: VertexFormat::Float32x2,
223 offset: 24,
224 },
225 ];
226
227 Mesh {
228 positions,
229 normals: Some(normals),
230 tex_coords: Some(tex_coords),
231 tangents: None,
232 colors: None,
233 indices: Some(indices),
234 primitive_type: PrimitiveTopology::TriangleList,
235 bounding_box: Aabb::from_min_max(Vec3::new(-half, y, -half), Vec3::new(half, y, half)),
236 vertex_layout,
237 }
238}
239
240fn create_cube(size: f32) -> Mesh {
242 let half = size / 2.0;
243
244 let positions = vec![
246 Vec3::new(-half, -half, half),
248 Vec3::new(half, -half, half),
249 Vec3::new(half, half, half),
250 Vec3::new(-half, half, half),
251 Vec3::new(half, -half, -half),
253 Vec3::new(-half, -half, -half),
254 Vec3::new(-half, half, -half),
255 Vec3::new(half, half, -half),
256 Vec3::new(half, -half, half),
258 Vec3::new(half, -half, -half),
259 Vec3::new(half, half, -half),
260 Vec3::new(half, half, half),
261 Vec3::new(-half, -half, -half),
263 Vec3::new(-half, -half, half),
264 Vec3::new(-half, half, half),
265 Vec3::new(-half, half, -half),
266 Vec3::new(-half, half, half),
268 Vec3::new(half, half, half),
269 Vec3::new(half, half, -half),
270 Vec3::new(-half, half, -half),
271 Vec3::new(-half, -half, -half),
273 Vec3::new(half, -half, -half),
274 Vec3::new(half, -half, half),
275 Vec3::new(-half, -half, half),
276 ];
277
278 let normals = vec![
279 Vec3::new(0.0, 0.0, 1.0),
281 Vec3::new(0.0, 0.0, 1.0),
282 Vec3::new(0.0, 0.0, 1.0),
283 Vec3::new(0.0, 0.0, 1.0),
284 Vec3::new(0.0, 0.0, -1.0),
286 Vec3::new(0.0, 0.0, -1.0),
287 Vec3::new(0.0, 0.0, -1.0),
288 Vec3::new(0.0, 0.0, -1.0),
289 Vec3::new(1.0, 0.0, 0.0),
291 Vec3::new(1.0, 0.0, 0.0),
292 Vec3::new(1.0, 0.0, 0.0),
293 Vec3::new(1.0, 0.0, 0.0),
294 Vec3::new(-1.0, 0.0, 0.0),
296 Vec3::new(-1.0, 0.0, 0.0),
297 Vec3::new(-1.0, 0.0, 0.0),
298 Vec3::new(-1.0, 0.0, 0.0),
299 Vec3::new(0.0, 1.0, 0.0),
301 Vec3::new(0.0, 1.0, 0.0),
302 Vec3::new(0.0, 1.0, 0.0),
303 Vec3::new(0.0, 1.0, 0.0),
304 Vec3::new(0.0, -1.0, 0.0),
306 Vec3::new(0.0, -1.0, 0.0),
307 Vec3::new(0.0, -1.0, 0.0),
308 Vec3::new(0.0, -1.0, 0.0),
309 ];
310
311 let tex_coords = vec![
312 [0.0, 0.0],
313 [1.0, 0.0],
314 [1.0, 1.0],
315 [0.0, 1.0],
316 [0.0, 0.0],
317 [1.0, 0.0],
318 [1.0, 1.0],
319 [0.0, 1.0],
320 [0.0, 0.0],
321 [1.0, 0.0],
322 [1.0, 1.0],
323 [0.0, 1.0],
324 [0.0, 0.0],
325 [1.0, 0.0],
326 [1.0, 1.0],
327 [0.0, 1.0],
328 [0.0, 0.0],
329 [1.0, 0.0],
330 [1.0, 1.0],
331 [0.0, 1.0],
332 [0.0, 0.0],
333 [1.0, 0.0],
334 [1.0, 1.0],
335 [0.0, 1.0],
336 ]
337 .into_iter()
338 .map(|uv| Vec2::new(uv[0], uv[1]))
339 .collect();
340
341 let indices = vec![
343 0u32, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23,
350 ];
351
352 let vertex_layout = vec![
354 VertexAttributeDescriptor {
355 shader_location: 0,
356 format: VertexFormat::Float32x3,
357 offset: 0,
358 },
359 VertexAttributeDescriptor {
360 shader_location: 1,
361 format: VertexFormat::Float32x3,
362 offset: 12,
363 },
364 VertexAttributeDescriptor {
365 shader_location: 2,
366 format: VertexFormat::Float32x2,
367 offset: 24,
368 },
369 ];
370
371 Mesh {
372 positions,
373 normals: Some(normals),
374 tex_coords: Some(tex_coords),
375 tangents: None,
376 colors: None,
377 indices: Some(indices),
378 primitive_type: PrimitiveTopology::TriangleList,
379 bounding_box: Aabb::from_min_max(
380 Vec3::new(-half, -half, -half),
381 Vec3::new(half, half, half),
382 ),
383 vertex_layout,
384 }
385}
386
387fn create_sphere(radius: f32, segments: u32, rings: u32) -> Mesh {
389 let mut positions = Vec::new();
390 let mut normals = Vec::new();
391 let mut tex_coords = Vec::new();
392
393 for ring in 0..=rings {
395 let phi = std::f32::consts::PI * (ring as f32 / rings as f32);
396 let y = radius * phi.cos();
397 let ring_radius = radius * phi.sin();
398
399 for segment in 0..=segments {
400 let theta = 2.0 * std::f32::consts::PI * (segment as f32 / segments as f32);
401 let x = ring_radius * theta.cos();
402 let z = ring_radius * theta.sin();
403
404 positions.push(Vec3::new(x, y, z));
405 normals.push(Vec3::new(x / radius, y / radius, z / radius));
406 tex_coords.push(Vec2::new(
407 segment as f32 / segments as f32,
408 ring as f32 / rings as f32,
409 ));
410 }
411 }
412
413 let mut indices = Vec::new();
415 for ring in 0..rings {
416 for segment in 0..segments {
417 let current = ring * (segments + 1) + segment;
418 let next = current + segments + 1;
419
420 indices.push(current);
422 indices.push(next);
423 indices.push(current + 1);
424
425 indices.push(current + 1);
426 indices.push(next);
427 indices.push(next + 1);
428 }
429 }
430
431 let vertex_layout = vec![
433 VertexAttributeDescriptor {
434 shader_location: 0,
435 format: VertexFormat::Float32x3,
436 offset: 0,
437 },
438 VertexAttributeDescriptor {
439 shader_location: 1,
440 format: VertexFormat::Float32x3,
441 offset: 12,
442 },
443 VertexAttributeDescriptor {
444 shader_location: 2,
445 format: VertexFormat::Float32x2,
446 offset: 24,
447 },
448 ];
449
450 Mesh {
451 positions,
452 normals: Some(normals),
453 tex_coords: Some(tex_coords),
454 tangents: None,
455 colors: None,
456 indices: Some(indices),
457 primitive_type: PrimitiveTopology::TriangleList,
458 bounding_box: Aabb::from_min_max(
459 Vec3::new(-radius, -radius, -radius),
460 Vec3::new(radius, radius, radius),
461 ),
462 vertex_layout,
463 }
464}