Fix sample hydration rendering regressions
This commit is contained in:
parent
06010c5922
commit
d52cc2e3b7
@ -287,6 +287,42 @@
|
|||||||
),
|
),
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
id: ("607864bf-fa77-4f9e-acc9-2c0906d34163"),
|
||||||
|
path: "assets/levels/samples/material_lab.scn.ron",
|
||||||
|
label: "material_lab.scn",
|
||||||
|
kind_tag: "Level",
|
||||||
|
import_settings: (
|
||||||
|
scale: 1.0,
|
||||||
|
generate_collider: true,
|
||||||
|
lod0_only: true,
|
||||||
|
placement_mode: StaticAsset,
|
||||||
|
hierarchy_mode: SingleActor,
|
||||||
|
material_policy: SourceMaterials,
|
||||||
|
static_mesh_manifest_path: None,
|
||||||
|
animation_manifest_path: None,
|
||||||
|
default_animation_clip_id: None,
|
||||||
|
),
|
||||||
|
dependencies: [],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
id: ("bca13177-afa9-4691-9b40-288059c7a57e"),
|
||||||
|
path: "assets/levels/samples/brush_blockout.scn.ron",
|
||||||
|
label: "brush_blockout.scn",
|
||||||
|
kind_tag: "Level",
|
||||||
|
import_settings: (
|
||||||
|
scale: 1.0,
|
||||||
|
generate_collider: true,
|
||||||
|
lod0_only: true,
|
||||||
|
placement_mode: StaticAsset,
|
||||||
|
hierarchy_mode: SingleActor,
|
||||||
|
material_policy: SourceMaterials,
|
||||||
|
static_mesh_manifest_path: None,
|
||||||
|
animation_manifest_path: None,
|
||||||
|
default_animation_clip_id: None,
|
||||||
|
),
|
||||||
|
dependencies: [],
|
||||||
|
),
|
||||||
(
|
(
|
||||||
id: ("5a2307d0-48e5-40d3-a5c9-527c0cfd30f4"),
|
id: ("5a2307d0-48e5-40d3-a5c9-527c0cfd30f4"),
|
||||||
path: "assets/levels/editor_scene.scn.ron",
|
path: "assets/levels/editor_scene.scn.ron",
|
||||||
@ -395,6 +431,24 @@
|
|||||||
),
|
),
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
id: ("e42185de-cda2-4a9e-9df0-df67ccd86123"),
|
||||||
|
path: "assets/samples/editor_samples.ron",
|
||||||
|
label: "editor_samples",
|
||||||
|
kind_tag: "Level",
|
||||||
|
import_settings: (
|
||||||
|
scale: 1.0,
|
||||||
|
generate_collider: true,
|
||||||
|
lod0_only: true,
|
||||||
|
placement_mode: StaticAsset,
|
||||||
|
hierarchy_mode: SingleActor,
|
||||||
|
material_policy: SourceMaterials,
|
||||||
|
static_mesh_manifest_path: None,
|
||||||
|
animation_manifest_path: None,
|
||||||
|
default_animation_clip_id: None,
|
||||||
|
),
|
||||||
|
dependencies: [],
|
||||||
|
),
|
||||||
(
|
(
|
||||||
id: ("04a4e00e-4732-43db-8e99-9cd99b94667b"),
|
id: ("04a4e00e-4732-43db-8e99-9cd99b94667b"),
|
||||||
path: "assets/animations/generated/113f74df-e39c-41d4-9b5b-e48efe541f7f.animation.ron",
|
path: "assets/animations/generated/113f74df-e39c-41d4-9b5b-e48efe541f7f.animation.ron",
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
use bevy::material::OpaqueRendererMethod;
|
||||||
use bevy::mesh::MeshVertexBufferLayoutRef;
|
use bevy::mesh::MeshVertexBufferLayoutRef;
|
||||||
use bevy::pbr::{
|
use bevy::pbr::{
|
||||||
ExtendedMaterial, MaterialExtension, MaterialExtensionKey, MaterialExtensionPipeline,
|
ExtendedMaterial, MaterialExtension, MaterialExtensionKey, MaterialExtensionPipeline,
|
||||||
@ -393,6 +394,9 @@ fn build_terrain_layer_material(
|
|||||||
base: StandardMaterial {
|
base: StandardMaterial {
|
||||||
base_color: Color::WHITE,
|
base_color: Color::WHITE,
|
||||||
perceptual_roughness: 1.0,
|
perceptual_roughness: 1.0,
|
||||||
|
// Terrain is intentionally absent from the Solari acceleration structure until its
|
||||||
|
// ray-tracing evaluator exists, so keep the layer blend in the raster forward pass.
|
||||||
|
opaque_render_method: OpaqueRendererMethod::Forward,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
extension,
|
extension,
|
||||||
@ -1079,6 +1083,10 @@ mod tests {
|
|||||||
assert_eq!(material.extension.uniform.uv_scales.y, 10.0);
|
assert_eq!(material.extension.uniform.uv_scales.y, 10.0);
|
||||||
assert_eq!(material.extension.uniform.properties[0].w, 1.0);
|
assert_eq!(material.extension.uniform.properties[0].w, 1.0);
|
||||||
assert_eq!(material.extension.uniform.properties[1].w, 1.0);
|
assert_eq!(material.extension.uniform.properties[1].w, 1.0);
|
||||||
|
assert_eq!(
|
||||||
|
material.base.opaque_render_method,
|
||||||
|
OpaqueRendererMethod::Forward
|
||||||
|
);
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
material.extension.uniform.base_colors[0],
|
material.extension.uniform.base_colors[0],
|
||||||
material.extension.uniform.base_colors[1]
|
material.extension.uniform.base_colors[1]
|
||||||
|
|||||||
@ -2171,6 +2171,8 @@ fn load_composed_subscenes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn finalize_scene_load(world: &mut World) {
|
fn finalize_scene_load(world: &mut World) {
|
||||||
|
shared::initialize_level_object_visibility_hierarchy(world);
|
||||||
|
|
||||||
let rendering = world
|
let rendering = world
|
||||||
.resource::<settings::ProjectSettings>()
|
.resource::<settings::ProjectSettings>()
|
||||||
.rendering
|
.rendering
|
||||||
@ -2490,12 +2492,20 @@ mod tests {
|
|||||||
.world_mut()
|
.world_mut()
|
||||||
.spawn((
|
.spawn((
|
||||||
LevelObject,
|
LevelObject,
|
||||||
|
EditorVisibility { visible: false },
|
||||||
BrushDesc::default(),
|
BrushDesc::default(),
|
||||||
ColliderDesc::static_cuboid(Vec3::ONE),
|
ColliderDesc::static_cuboid(Vec3::ONE),
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
|
|
||||||
finalize_scene_load(app.world_mut());
|
finalize_scene_load(app.world_mut());
|
||||||
|
assert_eq!(
|
||||||
|
app.world().get::<Visibility>(actor),
|
||||||
|
Some(&Visibility::Hidden),
|
||||||
|
"scene actors must receive authored visibility before Update hydration"
|
||||||
|
);
|
||||||
|
assert!(app.world().get::<InheritedVisibility>(actor).is_some());
|
||||||
|
assert!(app.world().get::<ViewVisibility>(actor).is_some());
|
||||||
assert!(
|
assert!(
|
||||||
app.world().get::<Children>(actor).is_none(),
|
app.world().get::<Children>(actor).is_none(),
|
||||||
"scene finalization must not create collider children ahead of Update"
|
"scene finalization must not create collider children ahead of Update"
|
||||||
@ -2511,6 +2521,7 @@ mod tests {
|
|||||||
.world()
|
.world()
|
||||||
.get::<avian3d::prelude::ColliderConstructor>(first_child)
|
.get::<avian3d::prelude::ColliderConstructor>(first_child)
|
||||||
.is_some());
|
.is_some());
|
||||||
|
assert!(app.world().get::<InheritedVisibility>(actor).is_some());
|
||||||
|
|
||||||
app.update();
|
app.update();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -2522,6 +2533,57 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn brush_sample_parents_own_visibility_before_generated_meshes_attach() {
|
||||||
|
let project_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||||
|
.join("../..")
|
||||||
|
.canonicalize()
|
||||||
|
.unwrap();
|
||||||
|
let sample_path = project_root.join("assets/levels/samples/brush_blockout.scn.ron");
|
||||||
|
let mut app = App::new();
|
||||||
|
app.add_plugins(MinimalPlugins)
|
||||||
|
.add_plugins(AssetPlugin::default())
|
||||||
|
.add_plugins(bevy::pbr::MaterialPlugin::<StandardMaterial>::default())
|
||||||
|
.init_asset::<Mesh>()
|
||||||
|
.add_plugins(shared::SharedTypesPlugin);
|
||||||
|
app.world_mut()
|
||||||
|
.insert_resource(settings::ProjectSettings::default());
|
||||||
|
app.world_mut()
|
||||||
|
.insert_resource(crate::project_io::ProjectWorkspace {
|
||||||
|
root: project_root.display().to_string(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
app.world_mut()
|
||||||
|
.insert_resource(crate::ui::hierarchy_state::HierarchyPanelState::default());
|
||||||
|
|
||||||
|
load_level(app.world_mut(), &sample_path).unwrap();
|
||||||
|
|
||||||
|
let brush_actors: Vec<Entity> = app
|
||||||
|
.world_mut()
|
||||||
|
.query_filtered::<Entity, (With<LevelObject>, With<BrushDesc>)>()
|
||||||
|
.iter(app.world())
|
||||||
|
.collect();
|
||||||
|
assert!(!brush_actors.is_empty());
|
||||||
|
for actor in &brush_actors {
|
||||||
|
assert!(app.world().get::<InheritedVisibility>(*actor).is_some());
|
||||||
|
assert!(app.world().get::<ViewVisibility>(*actor).is_some());
|
||||||
|
assert!(app.world().get::<Children>(*actor).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.update();
|
||||||
|
|
||||||
|
for actor in brush_actors {
|
||||||
|
let generated_meshes = app
|
||||||
|
.world()
|
||||||
|
.get::<Children>(actor)
|
||||||
|
.expect("brush hydration should attach generated mesh children");
|
||||||
|
assert!(generated_meshes
|
||||||
|
.iter()
|
||||||
|
.any(|child| app.world().get::<Mesh3d>(child).is_some()));
|
||||||
|
assert!(app.world().get::<InheritedVisibility>(actor).is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scene_serialization_persists_composition_without_composed_members() {
|
fn scene_serialization_persists_composition_without_composed_members() {
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
|
|||||||
@ -40,16 +40,17 @@ use static_meshes::{
|
|||||||
};
|
};
|
||||||
pub use terrain::HydratedTerrainChunk;
|
pub use terrain::HydratedTerrainChunk;
|
||||||
use terrain::{cleanup_removed_terrain, hydrate_terrain};
|
use terrain::{cleanup_removed_terrain, hydrate_terrain};
|
||||||
|
pub use visibility::initialize_level_object_visibility_hierarchy;
|
||||||
use visibility::{
|
use visibility::{
|
||||||
ensure_level_object_visibility_hierarchy, init_editor_visibility_on_spawn,
|
ensure_level_object_visibility_hierarchy, init_editor_visibility_on_spawn,
|
||||||
sync_editor_visibility, visibility_from_editor,
|
sync_editor_visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
authoring_component_active, AuthoringComponentStates, BrushDesc, ColliderDesc,
|
authoring_component_active, AuthoringComponentStates, BrushDesc, ColliderDesc, InspectorOrder,
|
||||||
EditorVisibility, InspectorOrder, LevelObject, MaterialDesc, MaterialOverride, Primitive,
|
LevelObject, MaterialDesc, MaterialOverride, Primitive, StaticMeshRenderer,
|
||||||
StaticMeshRenderer, COMPONENT_BRUSH_DESC, COMPONENT_COLLIDER_DESC, COMPONENT_LIGHT_DESC,
|
COMPONENT_BRUSH_DESC, COMPONENT_COLLIDER_DESC, COMPONENT_LIGHT_DESC, COMPONENT_MATERIAL_DESC,
|
||||||
COMPONENT_MATERIAL_DESC, COMPONENT_PRIMITIVE, COMPONENT_STATIC_MESH_RENDERER,
|
COMPONENT_PRIMITIVE, COMPONENT_STATIC_MESH_RENDERER,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Registers hydration systems in deterministic order.
|
/// Registers hydration systems in deterministic order.
|
||||||
@ -115,6 +116,8 @@ pub fn flush_level_object_hydration(world: &mut World) {
|
|||||||
world.insert_resource(StaticMeshArtifactCache::default());
|
world.insert_resource(StaticMeshArtifactCache::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialize_level_object_visibility_hierarchy(world);
|
||||||
|
|
||||||
let primitives: Vec<(Entity, Primitive)> = world
|
let primitives: Vec<(Entity, Primitive)> = world
|
||||||
.query_filtered::<(
|
.query_filtered::<(
|
||||||
Entity,
|
Entity,
|
||||||
@ -250,12 +253,6 @@ pub fn flush_level_object_hydration(world: &mut World) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let visibility_targets: Vec<(Entity, EditorVisibility)> = world
|
|
||||||
.query_filtered::<(Entity, &EditorVisibility), With<LevelObject>>()
|
|
||||||
.iter(world)
|
|
||||||
.map(|(entity, vis)| (entity, *vis))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let rendering = world
|
let rendering = world
|
||||||
.get_resource::<settings::ProjectSettings>()
|
.get_resource::<settings::ProjectSettings>()
|
||||||
.map(|s| s.rendering.clone())
|
.map(|s| s.rendering.clone())
|
||||||
@ -348,25 +345,6 @@ pub fn flush_level_object_hydration(world: &mut World) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.apply(world);
|
state.apply(world);
|
||||||
|
|
||||||
for (entity, editor) in visibility_targets {
|
|
||||||
let visibility = visibility_from_editor(editor);
|
|
||||||
if let Some(mut vis) = world.get_mut::<Visibility>(entity) {
|
|
||||||
*vis = visibility;
|
|
||||||
} else if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
|
|
||||||
entity_mut.insert(visibility);
|
|
||||||
}
|
|
||||||
if world.get::<InheritedVisibility>(entity).is_none() {
|
|
||||||
if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
|
|
||||||
entity_mut.insert(InheritedVisibility::default());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if world.get::<ViewVisibility>(entity).is_none() {
|
|
||||||
if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
|
|
||||||
entity_mut.insert(ViewVisibility::default());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@ -3,6 +3,24 @@
|
|||||||
use crate::{EditorVisibility, LevelObject};
|
use crate::{EditorVisibility, LevelObject};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
/// Establishes the runtime visibility hierarchy synchronously for all authored actors.
|
||||||
|
///
|
||||||
|
/// Scene loading can occur between scheduled hydration sets, so callers that materialize a scene
|
||||||
|
/// must run this before generated render children can be attached in the same frame.
|
||||||
|
pub fn initialize_level_object_visibility_hierarchy(world: &mut World) {
|
||||||
|
let targets: Vec<(Entity, EditorVisibility)> = world
|
||||||
|
.query_filtered::<(Entity, Option<&EditorVisibility>), With<LevelObject>>()
|
||||||
|
.iter(world)
|
||||||
|
.map(|(entity, visibility)| (entity, visibility.copied().unwrap_or_default()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for (entity, editor_visibility) in targets {
|
||||||
|
if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
|
||||||
|
entity_mut.insert((editor_visibility, visibility_from_editor(editor_visibility)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[expect(
|
#[expect(
|
||||||
clippy::type_complexity,
|
clippy::type_complexity,
|
||||||
reason = "the query mirrors Bevy's three visibility hierarchy components"
|
reason = "the query mirrors Bevy's three visibility hierarchy components"
|
||||||
@ -81,7 +99,9 @@ pub fn visibility_from_editor(editor: EditorVisibility) -> Visibility {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::ensure_level_object_visibility_hierarchy;
|
use super::{
|
||||||
|
ensure_level_object_visibility_hierarchy, initialize_level_object_visibility_hierarchy,
|
||||||
|
};
|
||||||
use crate::{EditorVisibility, LevelObject};
|
use crate::{EditorVisibility, LevelObject};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
@ -102,4 +122,34 @@ mod tests {
|
|||||||
assert!(world.get::<InheritedVisibility>(entity).is_some());
|
assert!(world.get::<InheritedVisibility>(entity).is_some());
|
||||||
assert!(world.get::<ViewVisibility>(entity).is_some());
|
assert!(world.get::<ViewVisibility>(entity).is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn immediate_initializer_establishes_hierarchy_before_scheduled_hydration() {
|
||||||
|
let mut world = World::new();
|
||||||
|
let entity = world
|
||||||
|
.spawn((LevelObject, EditorVisibility { visible: false }))
|
||||||
|
.id();
|
||||||
|
|
||||||
|
initialize_level_object_visibility_hierarchy(&mut world);
|
||||||
|
|
||||||
|
assert_eq!(world.get::<Visibility>(entity), Some(&Visibility::Hidden));
|
||||||
|
assert!(world.get::<InheritedVisibility>(entity).is_some());
|
||||||
|
assert!(world.get::<ViewVisibility>(entity).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn immediate_initializer_migrates_missing_authored_visibility() {
|
||||||
|
let mut world = World::new();
|
||||||
|
let entity = world.spawn(LevelObject).id();
|
||||||
|
|
||||||
|
initialize_level_object_visibility_hierarchy(&mut world);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
world.get::<EditorVisibility>(entity),
|
||||||
|
Some(&EditorVisibility::default())
|
||||||
|
);
|
||||||
|
assert_eq!(world.get::<Visibility>(entity), Some(&Visibility::Visible));
|
||||||
|
assert!(world.get::<InheritedVisibility>(entity).is_some());
|
||||||
|
assert!(world.get::<ViewVisibility>(entity).is_some());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,10 +20,10 @@ pub use actor::{infer_actor_kind, validate_actor, ActorValidationError};
|
|||||||
pub use animation::*;
|
pub use animation::*;
|
||||||
pub use components::*;
|
pub use components::*;
|
||||||
pub use hydration::{
|
pub use hydration::{
|
||||||
cascade_config_from_rendering, flush_level_object_hydration, material_from_desc,
|
cascade_config_from_rendering, flush_level_object_hydration,
|
||||||
strip_hydrated, strip_hydrated_entity, HydratedModelRoot, HydratedPrefabMember,
|
initialize_level_object_visibility_hierarchy, material_from_desc, strip_hydrated,
|
||||||
HydratedPrefabReady, HydratedSkinnedMeshRoot, HydratedTerrainChunk, HydrationPlugin,
|
strip_hydrated_entity, HydratedModelRoot, HydratedPrefabMember, HydratedPrefabReady,
|
||||||
PrefabHydrationBlocked,
|
HydratedSkinnedMeshRoot, HydratedTerrainChunk, HydrationPlugin, PrefabHydrationBlocked,
|
||||||
};
|
};
|
||||||
pub use material_asset::{
|
pub use material_asset::{
|
||||||
load_resolved_material_from_path, MaterialAlphaMode, MaterialAsset, MaterialInstanceAsset,
|
load_resolved_material_from_path, MaterialAlphaMode, MaterialAsset, MaterialInstanceAsset,
|
||||||
|
|||||||
@ -22,7 +22,8 @@ weights through `Mesh::ATTRIBUTE_COLOR`.
|
|||||||
`blacksite_surface` owns the terrain-specific `ExtendedMaterial` and blends each resolved layer's
|
`blacksite_surface` owns the terrain-specific `ExtendedMaterial` and blends each resolved layer's
|
||||||
albedo, tangent-space normal, metallic, and roughness inputs. Invalid or unavailable references leave
|
albedo, tangent-space normal, metallic, and roughness inputs. Invalid or unavailable references leave
|
||||||
the existing visible terrain fallback in place and emit diagnostics. Terrain remains excluded from
|
the existing visible terrain fallback in place and emit diagnostics. Terrain remains excluded from
|
||||||
Solari geometry until a matching ray-tracing evaluator exists.
|
Solari geometry until a matching ray-tracing evaluator exists, and its opaque material is therefore
|
||||||
|
forced through Bevy's forward raster path even when the project default is deferred.
|
||||||
|
|
||||||
Editor paint strokes materialize the implicit default map only when needed. Each pointer stroke is a
|
Editor paint strokes materialize the implicit default map only when needed. Each pointer stroke is a
|
||||||
single reflected `TerrainDesc` transaction; cancel restores the exact original descriptor.
|
single reflected `TerrainDesc` transaction; cancel restores the exact original descriptor.
|
||||||
|
|||||||
@ -59,9 +59,10 @@ references deliberately validate terrain dependency resolution and layer transpo
|
|||||||
fallback rather than missing geometry.
|
fallback rather than missing geometry.
|
||||||
- Generated `HydratedTerrainChunk` children own mesh, optional trimesh collider, and shadow state.
|
- Generated `HydratedTerrainChunk` children own mesh, optional trimesh collider, and shadow state.
|
||||||
They are runtime-only and never serialized as authored actors.
|
They are runtime-only and never serialized as authored actors.
|
||||||
- Foundation chunks are explicitly excluded from Solari submission and remain raster-visible while
|
- Foundation chunks are explicitly excluded from Solari submission and force their layer blend
|
||||||
Auto/Solari is active. Raster material-layer parity is shipped; matching Solari terrain evaluation
|
through Bevy's forward opaque raster path while Auto/Solari is active. Raster material-layer
|
||||||
remains explicit future work, and the editor never substitutes a semantically different proxy.
|
parity is shipped; matching Solari terrain evaluation remains explicit future work, and the editor
|
||||||
|
never substitutes a semantically different proxy.
|
||||||
|
|
||||||
The terrain foundation is recorded in [ADR 0039](../adr/0039-inline-height-grid-terrain-foundation.md),
|
The terrain foundation is recorded in [ADR 0039](../adr/0039-inline-height-grid-terrain-foundation.md),
|
||||||
and layer weight persistence/render transport in
|
and layer weight persistence/render transport in
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user