Skip to content

Commit 1c3a88a

Browse files
committed
alpha.3 fixes
1 parent 37982e8 commit 1c3a88a

10 files changed

Lines changed: 115 additions & 25 deletions

File tree

‎Cargo.lock‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "processing"
3-
version = "0.0.9-alpha.3"
3+
version = "0.0.9-alpha.4"
44
edition = "2024"
55
autoexamples = false
66

‎crates/processing_pyo3/Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "processing_pyo3"
3-
version = "0.0.9-alpha.3"
3+
version = "0.0.9-alpha.4"
44
edition = "2024"
55

66
[lints]

‎crates/processing_pyo3/src/gltf.rs‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use bevy::prelude::Entity;
22
use processing::prelude::*;
33
use pyo3::{exceptions::PyRuntimeError, prelude::*};
44

5-
use crate::graphics::{Geometry, Light};
5+
use crate::graphics::{Geometry, Image, Light};
66
use crate::material::Material;
77

88
#[pyclass(unsendable)]
@@ -35,6 +35,13 @@ impl Gltf {
3535
Ok(Material { entity })
3636
}
3737

38+
/// The texture on the named mesh, e.g. for `LOOKUP`.
39+
pub fn texture(&self, name: &str) -> PyResult<Image> {
40+
let entity = gltf_texture(self.entity, name)
41+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))?;
42+
Ok(Image::wrap(entity))
43+
}
44+
3845
pub fn mesh_names(&self) -> PyResult<Vec<String>> {
3946
gltf_mesh_names(self.entity).map_err(|e| PyRuntimeError::new_err(format!("{e}")))
4047
}

‎crates/processing_render/src/gltf.rs‎

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use bevy::{
88
},
99
camera::visibility::RenderLayers,
1010
ecs::system::RunSystemOnce,
11-
gltf::{Gltf, GltfMaterial, GltfMeshName},
11+
gltf::{Gltf, GltfMaterial, GltfMaterialName, GltfMeshName},
1212
pbr::ExtendedMaterial,
1313
prelude::*,
1414
world_serialization::WorldInstanceSpawner,
@@ -138,21 +138,7 @@ pub fn geometry(
138138
let instance_id = gltf_handle.instance_id;
139139

140140
let (mesh_handle, global_transform) = {
141-
let spawner = world.resource::<WorldInstanceSpawner>();
142-
143-
// find the mesh with the given name component that bevy added post-spawn
144-
// name is derived from gltf node or computed
145-
let mesh_entity = spawner
146-
.iter_instance_entities(instance_id)
147-
.find(|&e| {
148-
world
149-
.get::<GltfMeshName>(e)
150-
.map(|n| n.0 == name)
151-
.unwrap_or(false)
152-
})
153-
.ok_or_else(|| {
154-
ProcessingError::GltfLoadError(format!("Mesh '{}' not found in GLTF scene", name))
155-
})?;
141+
let mesh_entity = find_mesh_entity(world, instance_id, &name)?;
156142

157143
let mesh3d = world.get::<Mesh3d>(mesh_entity).ok_or_else(|| {
158144
ProcessingError::GltfLoadError(format!(
@@ -182,6 +168,64 @@ pub fn geometry(
182168
Ok(entity)
183169
}
184170

171+
/// The scene entity bevy spawned for a mesh, by the name it gave that mesh (from the
172+
/// glTF node, or computed).
173+
fn find_mesh_entity(
174+
world: &World,
175+
instance_id: bevy::world_serialization::InstanceId,
176+
name: &str,
177+
) -> Result<Entity> {
178+
world
179+
.resource::<WorldInstanceSpawner>()
180+
.iter_instance_entities(instance_id)
181+
.find(|&e| world.get::<GltfMeshName>(e).is_some_and(|n| n.0 == name))
182+
.ok_or_else(|| {
183+
ProcessingError::GltfLoadError(format!("Mesh '{}' not found in GLTF scene", name))
184+
})
185+
}
186+
187+
/// The base color texture of a mesh's material, as an [`Image`](crate::image::Image).
188+
pub fn texture(In((gltf_entity, name)): In<(Entity, String)>, world: &mut World) -> Result<Entity> {
189+
let gltf_handle = world
190+
.get::<GltfHandle>(gltf_entity)
191+
.ok_or(ProcessingError::InvalidEntity)?;
192+
let (instance_id, handle) = (gltf_handle.instance_id, gltf_handle.handle.clone());
193+
194+
let mesh_entity = find_mesh_entity(world, instance_id, &name)?;
195+
let material_name = world
196+
.get::<GltfMaterialName>(mesh_entity)
197+
.ok_or_else(|| {
198+
ProcessingError::GltfLoadError(format!("Mesh '{}' has no material", name))
199+
})?
200+
.0
201+
.clone();
202+
203+
let texture = {
204+
let gltf = world
205+
.resource::<Assets<Gltf>>()
206+
.get(&handle)
207+
.ok_or_else(|| ProcessingError::GltfLoadError("GLTF asset not found".into()))?;
208+
let material = gltf
209+
.named_materials
210+
.get(material_name.as_str())
211+
.and_then(|h| world.resource::<Assets<GltfMaterial>>().get(h))
212+
.ok_or_else(|| {
213+
ProcessingError::GltfLoadError(format!(
214+
"Material '{}' not found in GLTF",
215+
material_name
216+
))
217+
})?;
218+
material.base_color_texture.clone().ok_or_else(|| {
219+
ProcessingError::GltfLoadError(format!("Mesh '{}' has no texture", name))
220+
})?
221+
};
222+
223+
block_on_load(world, |w| w.get_asset_server().load_state(&texture))?;
224+
world
225+
.run_system_once_with(crate::image::from_handle, texture)
226+
.unwrap()
227+
}
228+
185229
/// Translate a bevy [`GltfMaterial`] into the [`StandardMaterial`] base of a
186230
/// [`ProcessingExtendedMaterial`]. `GltfMaterial` mirrors `StandardMaterial`'s
187231
/// fields, so we copy across the ones that drive shading and let the rest fall

‎crates/processing_render/src/lib.rs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2290,6 +2290,15 @@ pub fn gltf_material(gltf_entity: Entity, name: &str) -> error::Result<Entity> {
22902290
})
22912291
}
22922292

2293+
#[cfg(not(target_arch = "wasm32"))]
2294+
pub fn gltf_texture(gltf_entity: Entity, name: &str) -> error::Result<Entity> {
2295+
app_mut(|app| {
2296+
app.world_mut()
2297+
.run_system_cached_with(gltf::texture, (gltf_entity, name.to_string()))
2298+
.unwrap()
2299+
})
2300+
}
2301+
22932302
#[cfg(not(target_arch = "wasm32"))]
22942303
pub fn gltf_mesh_names(gltf_entity: Entity) -> error::Result<Vec<String>> {
22952304
app_mut(|app| {

‎crates/processing_render/src/particles/emit.rs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ pub fn particles_emit_gpu(
2323
return Ok(());
2424
}
2525

26+
let required: Vec<Entity> = app_mut(|app| {
27+
Ok(app
28+
.world()
29+
.get::<KernelRequires>(compute_entity)
30+
.map(|r| r.0.clone())
31+
.unwrap_or_default())
32+
})?;
33+
for attr_entity in required {
34+
particles_ensure_attribute(particles_entity, attr_entity)?;
35+
}
36+
2637
let (capacity, head, buffers) = app_mut(|app| {
2738
let world = app.world();
2839
let field = world

‎crates/processing_render/src/particles/kernels/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{compute_create, compute_set, shader_load};
1515
#[derive(Component, Default, Clone)]
1616
pub struct KernelRequires(pub Vec<Entity>);
1717

18-
fn set_requires(compute: Entity, names: &[&str]) -> error::Result<()> {
18+
pub(crate) fn set_requires(compute: Entity, names: &[&str]) -> error::Result<()> {
1919
app_mut(|app| {
2020
let world = app.world_mut();
2121
let attrs: Vec<Entity> = {

‎crates/processing_render/src/particles/kernels/scatter_surface.wgsl‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ struct EmitRange {
2121
@group(0) @binding(6) var<storage, read_write> life: array<f32>;
2222
@group(0) @binding(7) var<uniform> params: Params;
2323
@group(0) @binding(8) var<uniform> emit_range: EmitRange;
24+
@group(0) @binding(9) var<storage, read> source_uv: array<f32>;
25+
@group(0) @binding(10) var<storage, read_write> uv: array<f32>;
2426

2527
fn hash(n: u32) -> u32 {
2628
var x = n;
@@ -91,6 +93,13 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
9193
position[slot * 3u + 0u] = p.x;
9294
position[slot * 3u + 1u] = p.y;
9395
position[slot * 3u + 2u] = p.z;
96+
// texture coordinate at the same spot, for LOOKUP
97+
let t0 = vec2<f32>(source_uv[i0 * 2u], source_uv[i0 * 2u + 1u]);
98+
let t1 = vec2<f32>(source_uv[i1 * 2u], source_uv[i1 * 2u + 1u]);
99+
let t2 = vec2<f32>(source_uv[i2 * 2u], source_uv[i2 * 2u + 1u]);
100+
let t = (1.0 - u - v) * t0 + u * t1 + v * t2;
101+
uv[slot * 2u + 0u] = t.x;
102+
uv[slot * 2u + 1u] = t.y;
94103
scale[slot * 3u + 0u] = 1.0;
95104
scale[slot * 3u + 1u] = 1.0;
96105
scale[slot * 3u + 2u] = 1.0;

‎crates/processing_render/src/particles/scatter.rs‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,27 @@ use processing_core::app_mut;
55
use processing_core::error::{self, ProcessingError, Result};
66

77
use crate::geometry::Geometry;
8+
use crate::particles::kernels::set_requires;
89
use crate::shader_value::ShaderValue;
910
use crate::{
1011
buffer_create_with_data, compute_create, compute_set, geometry_attribute_position, shader_load,
1112
};
1213

1314
pub fn particles_scatter_create(source_geometry: Entity) -> error::Result<Entity> {
14-
let (cdf_bytes, indices_bytes, face_count) = app_mut(|app| {
15+
let (cdf_bytes, indices_bytes, uv_bytes, face_count) = app_mut(|app| {
1516
app.world_mut()
1617
.run_system_cached_with(prepare_scatter_source, source_geometry)
1718
.unwrap()
1819
})?;
1920

2021
let cdf_buf = buffer_create_with_data(cdf_bytes)?;
2122
let idx_buf = buffer_create_with_data(indices_bytes)?;
23+
let uv_buf = buffer_create_with_data(uv_bytes)?;
2224

2325
let shader =
2426
shader_load("embedded://processing_render/particles/kernels/scatter_surface.wgsl")?;
2527
let scatter = compute_create(shader)?;
28+
set_requires(scatter, &["position", "scale", "age", "life", "uv"])?;
2629

2730
let position_attr = geometry_attribute_position();
2831
compute_set(
@@ -32,6 +35,7 @@ pub fn particles_scatter_create(source_geometry: Entity) -> error::Result<Entity
3235
)?;
3336
compute_set(scatter, "source_indices", ShaderValue::Buffer(idx_buf))?;
3437
compute_set(scatter, "cdf", ShaderValue::Buffer(cdf_buf))?;
38+
compute_set(scatter, "source_uv", ShaderValue::Buffer(uv_buf))?;
3539
compute_set(scatter, "face_count", ShaderValue::UInt(face_count))?;
3640
compute_set(scatter, "seed", ShaderValue::UInt(0xc0ffeeu32))?;
3741

@@ -119,7 +123,7 @@ pub fn prepare_scatter_source(
119123
In(geom_entity): In<Entity>,
120124
geometries: Query<&Geometry>,
121125
mut meshes: ResMut<Assets<Mesh>>,
122-
) -> Result<(Vec<u8>, Vec<u8>, u32)> {
126+
) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>, u32)> {
123127
let geom = geometries
124128
.get(geom_entity)
125129
.map_err(|_| ProcessingError::GeometryNotFound)?;
@@ -130,6 +134,11 @@ pub fn prepare_scatter_source(
130134

131135
let (positions, dense_indices) = extract_scatter_geometry(mesh)?;
132136
let face_count = (dense_indices.len() / 3) as u32;
137+
// a mesh without UVs scatters with uv (0, 0)
138+
let uvs = match mesh.attribute(Mesh::ATTRIBUTE_UV_0) {
139+
Some(VertexAttributeValues::Float32x2(uv)) => uv.clone(),
140+
_ => vec![[0.0, 0.0]; positions.len()],
141+
};
133142

134143
let mut cum = Vec::with_capacity(face_count as usize);
135144
let mut total = 0.0_f32;
@@ -156,7 +165,8 @@ pub fn prepare_scatter_source(
156165

157166
let cdf_bytes: Vec<u8> = cum.iter().flat_map(|f| f.to_le_bytes()).collect();
158167
let indices_bytes: Vec<u8> = dense_indices.iter().flat_map(|i| i.to_le_bytes()).collect();
159-
Ok((cdf_bytes, indices_bytes, face_count))
168+
let uv_bytes: Vec<u8> = uvs.iter().flatten().flat_map(|f| f.to_le_bytes()).collect();
169+
Ok((cdf_bytes, indices_bytes, uv_bytes, face_count))
160170
}
161171

162172
pub fn prepare_scatter_volume_source(

0 commit comments

Comments
 (0)