Skip to content

Commit b8656e9

Browse files
committed
Lights addable as components via inspector; auto-place positional ones at mesh AABB center
Directional / Point / Spot light inspector entries now wire add_fn + remove_fn so they appear in the inspector's 'Add Component' menu under Lighting. DirectionalLight adds directly (it's positionless). PointLight and SpotLight spawn on a CHILD entity placed at the parent's local AABB center. Many imported GLBs bake world position into vertex data while keeping the entity's Transform at (0,0,0); attaching a positional light to such a mesh would emit from world origin, not from where the bulb visibly is. The child-on-AABB-center pattern puts the emitter where the artist expects. UX trade-off: the inspector pretends it adds to the selected entity but the component actually lands on a child. Users find/edit the light by expanding the parent in the hierarchy. Worth a follow-up to either teach the inspector to follow children for editing or expose this through a dedicated 'Add Light to Mesh' affordance.
1 parent 0921dc8 commit b8656e9

1 file changed

Lines changed: 64 additions & 6 deletions

File tree

crates/renzora_editor/src/bevy_inspectors.rs

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! regardless of which engine plugins are loaded.
55
66
use bevy::prelude::*;
7+
use bevy::camera::primitives::Aabb;
78
use bevy::pbr::Lightmap;
89
use bevy::light::{EnvironmentMapLight, IrradianceVolume, LightProbe, VolumetricFog, VolumetricLight};
910
use egui_phosphor::regular;
@@ -392,8 +393,12 @@ fn directional_light_entry() -> InspectorEntry {
392393
icon: regular::SUN,
393394
category: "lighting",
394395
has_fn: |world, entity| world.get::<DirectionalLight>(entity).is_some(),
395-
add_fn: None,
396-
remove_fn: None,
396+
add_fn: Some(|world, entity| {
397+
world.entity_mut(entity).insert(DirectionalLight::default());
398+
}),
399+
remove_fn: Some(|world, entity| {
400+
world.entity_mut(entity).remove::<DirectionalLight>();
401+
}),
397402
is_enabled_fn: None,
398403
set_enabled_fn: None,
399404
fields: vec![
@@ -462,8 +467,14 @@ fn point_light_entry() -> InspectorEntry {
462467
icon: regular::LIGHTBULB,
463468
category: "lighting",
464469
has_fn: |world, entity| world.get::<PointLight>(entity).is_some(),
465-
add_fn: None,
466-
remove_fn: None,
470+
add_fn: Some(|world, entity| {
471+
spawn_light_child(world, entity, "Point Light", |child| {
472+
child.insert(PointLight::default());
473+
});
474+
}),
475+
remove_fn: Some(|world, entity| {
476+
world.entity_mut(entity).remove::<PointLight>();
477+
}),
467478
is_enabled_fn: None,
468479
set_enabled_fn: None,
469480
fields: vec![
@@ -552,8 +563,14 @@ fn spot_light_entry() -> InspectorEntry {
552563
icon: regular::FLASHLIGHT,
553564
category: "lighting",
554565
has_fn: |world, entity| world.get::<SpotLight>(entity).is_some(),
555-
add_fn: None,
556-
remove_fn: None,
566+
add_fn: Some(|world, entity| {
567+
spawn_light_child(world, entity, "Spot Light", |child| {
568+
child.insert(SpotLight::default());
569+
});
570+
}),
571+
remove_fn: Some(|world, entity| {
572+
world.entity_mut(entity).remove::<SpotLight>();
573+
}),
557574
is_enabled_fn: None,
558575
set_enabled_fn: None,
559576
fields: vec![
@@ -1182,3 +1199,44 @@ fn volumetric_fog_entry() -> InspectorEntry {
11821199
custom_ui_fn: None,
11831200
}
11841201
}
1202+
1203+
/// Add a positional light (PointLight/SpotLight) to an entity such that the
1204+
/// emitter sits at the visible center of the entity's geometry rather than
1205+
/// at its `Transform` origin.
1206+
///
1207+
/// Many imported GLBs (sketchfab scenes especially) have meshes whose local
1208+
/// `Transform` is `(0,0,0)` with all the world position baked into the
1209+
/// vertex data. A `PointLight` attached directly reads the entity's
1210+
/// `GlobalTransform` — `(0,0,0)` — and emits from world origin, nowhere
1211+
/// near where the light bulb actually appears.
1212+
///
1213+
/// To put the light where the artist expects, we spawn it on a CHILD
1214+
/// entity with `Transform.translation` set to the parent's local AABB
1215+
/// center. The child's `GlobalTransform` then composes into the AABB world
1216+
/// center, which is what the user clicked on. Light moves with the parent
1217+
/// when reparented or transformed, same as any normal child.
1218+
///
1219+
/// If the entity has no `Aabb` (typical for empty entities the user
1220+
/// created themselves to hold a light), the child still gets created but
1221+
/// at the parent's origin — equivalent to attaching the light directly,
1222+
/// just one level of indirection.
1223+
fn spawn_light_child(
1224+
world: &mut World,
1225+
parent: Entity,
1226+
name: &str,
1227+
insert: impl FnOnce(&mut bevy::ecs::world::EntityWorldMut),
1228+
) {
1229+
let center = world
1230+
.get::<Aabb>(parent)
1231+
.map(|a| Vec3::from(a.center))
1232+
.unwrap_or(Vec3::ZERO);
1233+
1234+
let mut child_cmd = world.spawn((
1235+
Name::new(name.to_string()),
1236+
Transform::from_translation(center),
1237+
Visibility::default(),
1238+
));
1239+
insert(&mut child_cmd);
1240+
let child = child_cmd.id();
1241+
world.entity_mut(parent).add_child(child);
1242+
}

0 commit comments

Comments
 (0)