Block saving invalid brushes
Some checks are pending
CI / Format, lint, test, build (push) Waiting to run
Some checks are pending
CI / Format, lint, test, build (push) Waiting to run
This commit is contained in:
parent
015ae6da63
commit
11b0df2b5f
@ -404,6 +404,9 @@ fn format_actor_validation(err: ActorValidationError) -> String {
|
|||||||
ActorValidationError::BrushHasModelRef => {
|
ActorValidationError::BrushHasModelRef => {
|
||||||
"Save failed: Brush actor cannot have ModelRef".into()
|
"Save failed: Brush actor cannot have ModelRef".into()
|
||||||
}
|
}
|
||||||
|
ActorValidationError::InvalidBrushGeometry(message) => {
|
||||||
|
format!("Save failed: invalid brush geometry: {message}")
|
||||||
|
}
|
||||||
ActorValidationError::StaticMeshMissingPrimitive => {
|
ActorValidationError::StaticMeshMissingPrimitive => {
|
||||||
"Save failed: StaticMesh actor requires Primitive or StaticMeshRenderer with a mesh slot"
|
"Save failed: StaticMesh actor requires Primitive or StaticMeshRenderer with a mesh slot"
|
||||||
.into()
|
.into()
|
||||||
|
|||||||
@ -4,9 +4,9 @@ use bevy::ecs::world::EntityRef;
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ActorKind, BrushDesc, LevelObject, LightDesc, ModelRef, ObjectiveMarker, PlayerSpawn,
|
brush_math::validate_brush, ActorKind, BrushDesc, LevelObject, LightDesc, ModelRef,
|
||||||
PostProcessVolumeDesc, PrefabInstance, PrefabRef, Primitive, StaticMeshRenderer, TeamSpawn,
|
ObjectiveMarker, PlayerSpawn, PostProcessVolumeDesc, PrefabInstance, PrefabRef, Primitive,
|
||||||
TriggerVolume, WeaponSpawn,
|
StaticMeshRenderer, TeamSpawn, TriggerVolume, WeaponSpawn,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// One-shot deterministic kind from authoring components (scene migration only).
|
/// One-shot deterministic kind from authoring components (scene migration only).
|
||||||
@ -59,6 +59,7 @@ pub enum ActorValidationError {
|
|||||||
BrushHasStaticMeshRenderer,
|
BrushHasStaticMeshRenderer,
|
||||||
BrushHasLight,
|
BrushHasLight,
|
||||||
BrushHasModelRef,
|
BrushHasModelRef,
|
||||||
|
InvalidBrushGeometry(String),
|
||||||
StaticMeshMissingPrimitive,
|
StaticMeshMissingPrimitive,
|
||||||
StaticMeshHasLight,
|
StaticMeshHasLight,
|
||||||
StaticMeshHasModelRef,
|
StaticMeshHasModelRef,
|
||||||
@ -99,9 +100,9 @@ pub fn validate_actor(entity: EntityRef<'_>) -> Result<(), ActorValidationError>
|
|||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
ActorKind::Brush => {
|
ActorKind::Brush => {
|
||||||
if entity.get::<BrushDesc>().is_none() {
|
let Some(brush) = entity.get::<BrushDesc>() else {
|
||||||
return Err(ActorValidationError::BrushMissingDesc);
|
return Err(ActorValidationError::BrushMissingDesc);
|
||||||
}
|
};
|
||||||
if entity.get::<Primitive>().is_some() {
|
if entity.get::<Primitive>().is_some() {
|
||||||
return Err(ActorValidationError::BrushHasPrimitive);
|
return Err(ActorValidationError::BrushHasPrimitive);
|
||||||
}
|
}
|
||||||
@ -114,6 +115,18 @@ pub fn validate_actor(entity: EntityRef<'_>) -> Result<(), ActorValidationError>
|
|||||||
if entity.get::<ModelRef>().is_some() {
|
if entity.get::<ModelRef>().is_some() {
|
||||||
return Err(ActorValidationError::BrushHasModelRef);
|
return Err(ActorValidationError::BrushHasModelRef);
|
||||||
}
|
}
|
||||||
|
let report = validate_brush(brush);
|
||||||
|
if !report.is_valid() {
|
||||||
|
let message = report
|
||||||
|
.diagnostics
|
||||||
|
.into_iter()
|
||||||
|
.find(|diagnostic| {
|
||||||
|
diagnostic.severity == crate::brush_math::BrushDiagnosticSeverity::Error
|
||||||
|
})
|
||||||
|
.map(|diagnostic| diagnostic.message)
|
||||||
|
.unwrap_or_else(|| "Brush geometry is invalid.".to_string());
|
||||||
|
return Err(ActorValidationError::InvalidBrushGeometry(message));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ActorKind::StaticMesh => {
|
ActorKind::StaticMesh => {
|
||||||
let has_static_mesh_renderer = entity
|
let has_static_mesh_renderer = entity
|
||||||
@ -262,6 +275,20 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_brush_rejects_invalid_geometry() {
|
||||||
|
let mut world = World::new();
|
||||||
|
let mut brush = BrushDesc::default();
|
||||||
|
brush.faces[0].vertices.clear();
|
||||||
|
let e = level_entity(&mut world, (ActorKind::Brush, brush));
|
||||||
|
assert_eq!(
|
||||||
|
validate_actor(world.entity(e)),
|
||||||
|
Err(ActorValidationError::InvalidBrushGeometry(
|
||||||
|
"Face has fewer than three vertices.".into()
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn validate_post_process_volume_ok() {
|
fn validate_post_process_volume_ok() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|||||||
@ -10,6 +10,7 @@ Brushes are persisted blockout geometry stored as `ActorKind::Brush + BrushDesc`
|
|||||||
- Brush actors validate separately from primitives and imported/static mesh actors.
|
- Brush actors validate separately from primitives and imported/static mesh actors.
|
||||||
- The Actor Inspector Add Component shelf can add a **Brush** component. The Brush card exposes kind, face count, shadow flags, and cube reset.
|
- The Actor Inspector Add Component shelf can add a **Brush** component. The Brush card exposes kind, face count, shadow flags, and cube reset.
|
||||||
- The Brush card runs shared validation and reports invalid faces inline. Fatal geometry errors prevent hydration; warnings call out authoring issues that can still render. **Reset Cube Brush** is the MVP repair path.
|
- The Brush card runs shared validation and reports invalid faces inline. Fatal geometry errors prevent hydration; warnings call out authoring issues that can still render. **Reset Cube Brush** is the MVP repair path.
|
||||||
|
- Scene save runs the same fatal brush validation and blocks writing unrecoverable invalid brush geometry.
|
||||||
- Draw Brush mode (`B`, toolbar pencil, or command `brush.draw`) creates additive prism brushes from snapped floor points. LMB places points, Backspace removes the last point, Enter commits through undo history, and Esc/right-click cancels without scene mutation.
|
- Draw Brush mode (`B`, toolbar pencil, or command `brush.draw`) creates additive prism brushes from snapped floor points. LMB places points, Backspace removes the last point, Enter commits through undo history, and Esc/right-click cancels without scene mutation.
|
||||||
|
|
||||||
## Element Modes
|
## Element Modes
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user