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
#endif
struct LineMaterial {
unused: u32,
}
@group(2) @binding(100)
var<uniform> line_mat: LineMaterial;
@fragment
fn fragment(
in: VertexOutput,

View File

@@ -7,17 +7,19 @@ use crate::{
},
nodes::{
Node,
drawable::LinePoint,
spatial::{Spatial, SpatialNode},
},
};
use bevy::{
asset::{RenderAssetUsages, weak_handle},
asset::{AssetEvents, RenderAssetUsages, weak_handle},
pbr::{ExtendedMaterial, MaterialExtension},
prelude::*,
render::{
mesh::{Indices, MeshAabb, PrimitiveTopology},
primitives::Aabb,
render_resource::{AsBindGroup, ShaderRef},
view::VisibilitySystems,
},
};
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");
// No extra data needed for a simple holdout
#[derive(Default, Asset, AsBindGroup, TypePath, Debug, Clone)]
pub struct LineExtension {
#[uniform(100)]
unused: u32,
#[data(50, u32, binding_array(101))]
#[bindless(index_table(range(50..51), binding(100)))]
pub struct LineExtension {}
impl From<&LineExtension> for u32 {
fn from(_: &LineExtension) -> Self {
0
}
}
impl MaterialExtension for LineExtension {
fn fragment_shader() -> ShaderRef {
@@ -56,7 +62,14 @@ impl MaterialExtension for LineExtension {
pub struct LinesNodePlugin;
impl Plugin for LinesNodePlugin {
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(
LINE_SHADER_HANDLE.id(),
Shader::from_wgsl(
@@ -76,6 +89,7 @@ fn build_line_mesh(
mut cmds: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<LineMaterial>>,
query: Query<(&GlobalTransform, &InheritedVisibility)>,
) {
for lines in LINES_REGISTRY
.get_valid_contents()
@@ -88,6 +102,10 @@ fn build_line_mesh(
let mut vertex_colors = Vec::<[f32; 4]>::new();
let mut vertex_indices = Vec::<u32>::new();
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() {
*lines.bounds.lock() = Aabb::default();
match lines.entity.get() {
@@ -104,14 +122,26 @@ fn build_line_mesh(
let mut indices_set = 0;
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;
// 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 non-cyclic lines: handles endpoints with None values
let point_windows = {
let mut out = Vec::new();
let mut last = line.cyclic.then(|| line.points.last()).flatten();
let mut peekable = line.points.iter().peekable();
let mut last = line.cyclic.then(|| line_points.last()).flatten();
let mut peekable = line_points.iter().peekable();
while let Some(curr) = peekable.next() {
// Skip this point if it has the same position as the previous point
if let Some(prev) = last
@@ -128,7 +158,7 @@ fn build_line_mesh(
Some(v) => Some(*v),
None => {
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(),
..default()
},
extension: LineExtension { unused: 0 },
extension: LineExtension {},
})),
));
lines.spatial.set_entity(e.id());
_ = lines.entity.set(e.id().into());
e
}
@@ -236,7 +265,13 @@ fn build_line_mesh(
*lines.bounds.lock() = 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
#[derive(Default, Asset, AsBindGroup, TypePath, Debug, Clone)]
#[data(50, u32, binding_array(101))]
#[bindless(index_table(range(50..51), binding(100)))]
pub struct HoldoutExtension {}
impl From<&HoldoutExtension> for u32 {
fn from(_: &HoldoutExtension) -> Self {
0
}
}
impl MaterialExtension for HoldoutExtension {
fn fragment_shader() -> ShaderRef {
HOLDOUT_SHADER_HANDLE.into()

View File

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