fix: properly implement lines by only transforming points, not the mesh, also make lines and holdout bindless

Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
Schmarni
2025-10-26 00:01:40 +02:00
parent 8bfb01808a
commit 23f0b5f880
4 changed files with 56 additions and 18 deletions

View File

@@ -18,13 +18,6 @@
#import bevy_core_pipeline::oit::oit_draw #import bevy_core_pipeline::oit::oit_draw
#endif #endif
struct LineMaterial {
unused: u32,
}
@group(2) @binding(100)
var<uniform> line_mat: LineMaterial;
@fragment @fragment
fn fragment( fn fragment(
in: VertexOutput, in: VertexOutput,

View File

@@ -7,17 +7,19 @@ use crate::{
}, },
nodes::{ nodes::{
Node, Node,
drawable::LinePoint,
spatial::{Spatial, SpatialNode}, spatial::{Spatial, SpatialNode},
}, },
}; };
use bevy::{ use bevy::{
asset::{RenderAssetUsages, weak_handle}, asset::{AssetEvents, RenderAssetUsages, weak_handle},
pbr::{ExtendedMaterial, MaterialExtension}, pbr::{ExtendedMaterial, MaterialExtension},
prelude::*, prelude::*,
render::{ render::{
mesh::{Indices, MeshAabb, PrimitiveTopology}, mesh::{Indices, MeshAabb, PrimitiveTopology},
primitives::Aabb, primitives::Aabb,
render_resource::{AsBindGroup, ShaderRef}, render_resource::{AsBindGroup, ShaderRef},
view::VisibilitySystems,
}, },
}; };
use glam::Vec3; use glam::Vec3;
@@ -31,9 +33,13 @@ type LineMaterial = ExtendedMaterial<BevyMaterial, LineExtension>;
const LINE_SHADER_HANDLE: Handle<Shader> = weak_handle!("7d28aa5a-3abd-43bb-b0e9-0de8b81b650d"); const LINE_SHADER_HANDLE: Handle<Shader> = weak_handle!("7d28aa5a-3abd-43bb-b0e9-0de8b81b650d");
// No extra data needed for a simple holdout // No extra data needed for a simple holdout
#[derive(Default, Asset, AsBindGroup, TypePath, Debug, Clone)] #[derive(Default, Asset, AsBindGroup, TypePath, Debug, Clone)]
pub struct LineExtension { #[data(50, u32, binding_array(101))]
#[uniform(100)] #[bindless(index_table(range(50..51), binding(100)))]
unused: u32, pub struct LineExtension {}
impl From<&LineExtension> for u32 {
fn from(_: &LineExtension) -> Self {
0
}
} }
impl MaterialExtension for LineExtension { impl MaterialExtension for LineExtension {
fn fragment_shader() -> ShaderRef { fn fragment_shader() -> ShaderRef {
@@ -56,7 +62,14 @@ impl MaterialExtension for LineExtension {
pub struct LinesNodePlugin; pub struct LinesNodePlugin;
impl Plugin for LinesNodePlugin { impl Plugin for LinesNodePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Update, build_line_mesh); app.add_systems(
PostUpdate,
build_line_mesh
.after(TransformSystem::TransformPropagate)
.before(AssetEvents)
.after(VisibilitySystems::VisibilityPropagate)
.before(VisibilitySystems::CheckVisibility),
);
app.world_mut().resource_mut::<Assets<Shader>>().insert( app.world_mut().resource_mut::<Assets<Shader>>().insert(
LINE_SHADER_HANDLE.id(), LINE_SHADER_HANDLE.id(),
Shader::from_wgsl( Shader::from_wgsl(
@@ -76,6 +89,7 @@ fn build_line_mesh(
mut cmds: Commands, mut cmds: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<LineMaterial>>, mut materials: ResMut<Assets<LineMaterial>>,
query: Query<(&GlobalTransform, &InheritedVisibility)>,
) { ) {
for lines in LINES_REGISTRY for lines in LINES_REGISTRY
.get_valid_contents() .get_valid_contents()
@@ -88,6 +102,10 @@ fn build_line_mesh(
let mut vertex_colors = Vec::<[f32; 4]>::new(); let mut vertex_colors = Vec::<[f32; 4]>::new();
let mut vertex_indices = Vec::<u32>::new(); let mut vertex_indices = Vec::<u32>::new();
let lines_data = lines.data.lock(); let lines_data = lines.data.lock();
let Some((transform, visibil)) = lines.spatial.get_entity().and_then(|e| query.get(e).ok())
else {
continue;
};
if lines_data.is_empty() { if lines_data.is_empty() {
*lines.bounds.lock() = Aabb::default(); *lines.bounds.lock() = Aabb::default();
match lines.entity.get() { match lines.entity.get() {
@@ -104,14 +122,26 @@ fn build_line_mesh(
let mut indices_set = 0; let mut indices_set = 0;
for line in lines_data.iter() { for line in lines_data.iter() {
// yes this alloc is suboptimal, but good enough for now
let line_points = line
.points
.iter()
.map(|p: &LinePoint| LinePoint {
// point: transform.transform_point(p.point.into()).into(),
point: transform.transform_point(p.point.into()).into(),
thickness: p.thickness,
color: p.color,
})
.collect::<Vec<_>>();
let start_set = indices_set; let start_set = indices_set;
// Create a sliding window of points to process each segment of the line // Create a sliding window of points to process each segment of the line
// For cyclic lines: wraps around by connecting last point back to first // For cyclic lines: wraps around by connecting last point back to first
// For non-cyclic lines: handles endpoints with None values // For non-cyclic lines: handles endpoints with None values
let point_windows = { let point_windows = {
let mut out = Vec::new(); let mut out = Vec::new();
let mut last = line.cyclic.then(|| line.points.last()).flatten(); let mut last = line.cyclic.then(|| line_points.last()).flatten();
let mut peekable = line.points.iter().peekable(); let mut peekable = line_points.iter().peekable();
while let Some(curr) = peekable.next() { while let Some(curr) = peekable.next() {
// Skip this point if it has the same position as the previous point // Skip this point if it has the same position as the previous point
if let Some(prev) = last if let Some(prev) = last
@@ -128,7 +158,7 @@ fn build_line_mesh(
Some(v) => Some(*v), Some(v) => Some(*v),
None => { None => {
end = true; end = true;
line.cyclic.then(|| line.points.first()).flatten() line.cyclic.then(|| line_points.first()).flatten()
} }
}; };
@@ -224,10 +254,9 @@ fn build_line_mesh(
emissive: Color::linear_rgba(0.25, 0.25, 0.25, 1.0).into(), emissive: Color::linear_rgba(0.25, 0.25, 0.25, 1.0).into(),
..default() ..default()
}, },
extension: LineExtension { unused: 0 }, extension: LineExtension {},
})), })),
)); ));
lines.spatial.set_entity(e.id());
_ = lines.entity.set(e.id().into()); _ = lines.entity.set(e.id().into());
e e
} }
@@ -236,7 +265,13 @@ fn build_line_mesh(
*lines.bounds.lock() = aabb; *lines.bounds.lock() = aabb;
entity.insert(aabb); entity.insert(aabb);
} }
entity.insert(Mesh3d(meshes.add(mesh))); entity
.insert(Mesh3d(meshes.add(mesh)))
.insert(*visibil)
.insert(match visibil.get() {
true => Visibility::Visible,
false => Visibility::Hidden,
});
} }
} }

View File

@@ -67,7 +67,14 @@ impl Plugin for ModelNodePlugin {
// No extra data needed for a simple holdout // No extra data needed for a simple holdout
#[derive(Default, Asset, AsBindGroup, TypePath, Debug, Clone)] #[derive(Default, Asset, AsBindGroup, TypePath, Debug, Clone)]
#[data(50, u32, binding_array(101))]
#[bindless(index_table(range(50..51), binding(100)))]
pub struct HoldoutExtension {} pub struct HoldoutExtension {}
impl From<&HoldoutExtension> for u32 {
fn from(_: &HoldoutExtension) -> Self {
0
}
}
impl MaterialExtension for HoldoutExtension { impl MaterialExtension for HoldoutExtension {
fn fragment_shader() -> ShaderRef { fn fragment_shader() -> ShaderRef {
HOLDOUT_SHADER_HANDLE.into() HOLDOUT_SHADER_HANDLE.into()

View File

@@ -194,6 +194,9 @@ impl Spatial {
child.mark_dirty(); child.mark_dirty();
} }
} }
pub fn get_entity(&self) -> Option<Entity> {
self.entity.read().as_ref().map(|v| v.0)
}
pub fn add_to( pub fn add_to(
node: &Arc<Node>, node: &Arc<Node>,
parent: Option<Arc<Spatial>>, parent: Option<Arc<Spatial>>,