Skip to content

Commit f2ade7e

Browse files
committed
.
1 parent 5ae5558 commit f2ade7e

36 files changed

Lines changed: 905 additions & 235 deletions

crates/processing_pyo3/examples/field_animated.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def setup():
4848
positions.extend([x - 4.5, y - 4.5, z - 4.5])
4949

5050
field_obj = Field(capacity=capacity, attributes=[Attribute.position()])
51-
pos_buf = field_obj.pbuffer(Attribute.position())
51+
pos_buf = field_obj.buffer(Attribute.position())
5252
pos_buf.write(positions)
5353

5454
mat = Material(roughness=0.4)

crates/processing_pyo3/examples/field_basic.py

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,13 @@ def setup():
2121
attributes=[Attribute.position(), Attribute.uv(), Attribute.color()],
2222
)
2323

24-
# Read uvs back, build per-particle colors, write to color PBuffer.
25-
color_buf = field_obj.pbuffer(Attribute.color())
26-
uv_buf = field_obj.pbuffer(Attribute.uv())
24+
uv_buf = field_obj.buffer(Attribute.uv())
25+
color_buf = field_obj.buffer(Attribute.color())
26+
2727
colors = []
2828
for uv in uv_buf.read():
29-
u = uv[0]
30-
h = u * 6.0
31-
c = h - int(h)
32-
if h < 1:
33-
colors.append([1.0, c, 0.0, 1.0])
34-
elif h < 2:
35-
colors.append([1.0 - c, 1.0, 0.0, 1.0])
36-
elif h < 3:
37-
colors.append([0.0, 1.0, c, 1.0])
38-
elif h < 4:
39-
colors.append([0.0, 1.0 - c, 1.0, 1.0])
40-
elif h < 5:
41-
colors.append([c, 0.0, 1.0, 1.0])
42-
else:
43-
colors.append([1.0, 0.0, 1.0 - c, 1.0])
29+
c = hsva(uv[0] * 360.0, 0.85, 1.0)
30+
colors.append([c.r, c.g, c.b, 1.0])
4431
color_buf.write(colors)
4532

4633
particle = Geometry.sphere(0.18, 10, 8)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from mewnala import *
2+
import math
3+
4+
field_obj = None
5+
sphere = None
6+
mat = None
7+
frame = 0
8+
9+
10+
def setup():
11+
global field_obj, sphere, mat
12+
13+
size(900, 700)
14+
mode_3d()
15+
16+
sphere = Geometry.sphere(0.08, 8, 6)
17+
18+
capacity = 2000
19+
field_obj = Field(
20+
capacity=capacity,
21+
attributes=[Attribute.position(), Attribute.color()],
22+
)
23+
24+
# Push unemitted slots far off-screen so they don't all render at the
25+
# origin while the ring buffer is still filling.
26+
pos_buf = field_obj.buffer(Attribute.position())
27+
pos_buf.write([1.0e6] * (capacity * 3))
28+
29+
color_buf = field_obj.buffer(Attribute.color())
30+
mat = Material.field_color(color_buf)
31+
32+
33+
def draw():
34+
global frame
35+
camera_position(0.0, 4.0, 14.0)
36+
camera_look_at(0.0, 0.0, 0.0)
37+
background(15, 15, 20)
38+
39+
use_material(mat)
40+
draw_field(field_obj, sphere)
41+
42+
# Emit 4 particles per frame in an outward-spiraling ring; once the ring
43+
# buffer fills (~500 frames at 4/frame for capacity 2000), oldest get
44+
# overwritten and the swirl continues without bound.
45+
burst = 4
46+
positions = []
47+
colors = []
48+
for k in range(burst):
49+
i = frame * burst + k
50+
t = i * 0.05
51+
radius = 1.5 + min(t * 0.02, 3.0)
52+
height = math.sin(t * 0.1) * 2.0
53+
positions.extend([math.cos(t) * radius, height, math.sin(t) * radius])
54+
c = hsva((i * 4.32) % 360.0, 0.85, 1.0)
55+
colors.extend([c.r, c.g, c.b, 1.0])
56+
57+
field_obj.emit(burst, position=positions, color=colors)
58+
frame += 1
59+
60+
61+
run()
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
from mewnala import *
2+
import math
3+
4+
field_obj = None
5+
particle = None
6+
mat = None
7+
spawn = None
8+
motion = None
9+
10+
CAPACITY = 40000
11+
BURST = 120
12+
DT = 1.0 / 60.0
13+
TTL = 2.5
14+
GRAVITY = 9.8
15+
SPEED = 5.0
16+
17+
SPAWN_SHADER = """
18+
struct Spawn {
19+
pos: vec4<f32>,
20+
speed: vec4<f32>,
21+
}
22+
23+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
24+
@group(0) @binding(1) var<storage, read_write> velocity: array<f32>;
25+
@group(0) @binding(2) var<storage, read_write> color: array<f32>;
26+
@group(0) @binding(3) var<storage, read_write> scale: array<f32>;
27+
@group(0) @binding(4) var<storage, read_write> age: array<f32>;
28+
@group(0) @binding(5) var<storage, read_write> dead: array<f32>;
29+
@group(0) @binding(6) var<uniform> spawn: Spawn;
30+
@group(0) @binding(7) var<uniform> emit_range: vec4<f32>;
31+
32+
fn hash(n: u32) -> u32 {
33+
var x = n;
34+
x = (x ^ 61u) ^ (x >> 16u);
35+
x = x + (x << 3u);
36+
x = x ^ (x >> 4u);
37+
x = x * 0x27d4eb2du;
38+
x = x ^ (x >> 15u);
39+
return x;
40+
}
41+
42+
fn hash_unit(n: u32) -> f32 {
43+
return f32(hash(n)) / f32(0xffffffffu);
44+
}
45+
46+
@compute @workgroup_size(64)
47+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
48+
let local_i = gid.x;
49+
if local_i >= u32(emit_range.y) { return; }
50+
let base = u32(emit_range.x);
51+
let cap = u32(emit_range.z);
52+
let slot = (base + local_i) % cap;
53+
54+
let seed = base + local_i;
55+
56+
let theta = hash_unit(seed) * 6.2831853;
57+
let r = sqrt(hash_unit(seed * 2u + 1u));
58+
let dirxz = vec2<f32>(cos(theta), sin(theta)) * r;
59+
let dy = 0.7 + 0.3 * hash_unit(seed * 3u + 7u);
60+
let v = vec3<f32>(dirxz.x, dy, dirxz.y) * spawn.speed.x;
61+
62+
position[slot * 3u + 0u] = spawn.pos.x;
63+
position[slot * 3u + 1u] = spawn.pos.y;
64+
position[slot * 3u + 2u] = spawn.pos.z;
65+
66+
velocity[slot * 3u + 0u] = v.x;
67+
velocity[slot * 3u + 1u] = v.y;
68+
velocity[slot * 3u + 2u] = v.z;
69+
70+
let h = fract(hash_unit(seed * 5u + 11u));
71+
color[slot * 4u + 0u] = 0.5 + 0.5 * sin(h * 6.28);
72+
color[slot * 4u + 1u] = 0.5 + 0.5 * sin(h * 6.28 + 2.094);
73+
color[slot * 4u + 2u] = 0.5 + 0.5 * sin(h * 6.28 + 4.189);
74+
color[slot * 4u + 3u] = 1.0;
75+
76+
scale[slot * 3u + 0u] = 1.0;
77+
scale[slot * 3u + 1u] = 1.0;
78+
scale[slot * 3u + 2u] = 1.0;
79+
80+
age[slot] = 0.0;
81+
dead[slot] = 0.0;
82+
}
83+
"""
84+
85+
MOTION_SHADER = """
86+
struct Params {
87+
dt: f32,
88+
ttl: f32,
89+
gravity: f32,
90+
_pad: f32,
91+
}
92+
93+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
94+
@group(0) @binding(1) var<storage, read_write> velocity: array<f32>;
95+
@group(0) @binding(2) var<storage, read_write> scale: array<f32>;
96+
@group(0) @binding(3) var<storage, read_write> age: array<f32>;
97+
@group(0) @binding(4) var<storage, read_write> dead: array<f32>;
98+
@group(0) @binding(5) var<uniform> params: Params;
99+
100+
@compute @workgroup_size(64)
101+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
102+
let i = gid.x;
103+
let count = arrayLength(&age);
104+
if i >= count { return; }
105+
if dead[i] != 0.0 { return; }
106+
107+
age[i] = age[i] + params.dt;
108+
109+
velocity[i * 3u + 1u] = velocity[i * 3u + 1u] - params.gravity * params.dt;
110+
111+
position[i * 3u + 0u] = position[i * 3u + 0u] + velocity[i * 3u + 0u] * params.dt;
112+
position[i * 3u + 1u] = position[i * 3u + 1u] + velocity[i * 3u + 1u] * params.dt;
113+
position[i * 3u + 2u] = position[i * 3u + 2u] + velocity[i * 3u + 2u] * params.dt;
114+
115+
let life = clamp(1.0 - age[i] / params.ttl, 0.0, 1.0);
116+
let s = life * life;
117+
scale[i * 3u + 0u] = s;
118+
scale[i * 3u + 1u] = s;
119+
scale[i * 3u + 2u] = s;
120+
121+
if age[i] > params.ttl { dead[i] = 1.0; }
122+
}
123+
"""
124+
125+
126+
def setup():
127+
global field_obj, particle, mat, spawn, motion
128+
129+
size(900, 700)
130+
mode_3d()
131+
132+
create_directional_light((0.95, 0.9, 0.85), 800.0)
133+
134+
particle = Geometry.sphere(0.12, 8, 6)
135+
136+
velocity_attr = Attribute("velocity", AttributeFormat.Float3)
137+
age_attr = Attribute("age", AttributeFormat.Float)
138+
139+
field_obj = Field(
140+
capacity=CAPACITY,
141+
attributes=[
142+
Attribute.position(),
143+
Attribute.color(),
144+
Attribute.scale(),
145+
Attribute.dead(),
146+
velocity_attr,
147+
age_attr,
148+
],
149+
)
150+
151+
# Mark all unemitted slots dead so they don't render at origin.
152+
dead_buf = field_obj.buffer(Attribute.dead())
153+
dead_buf.write([1.0] * CAPACITY)
154+
155+
color_buf = field_obj.buffer(Attribute.color())
156+
mat = Material.field_pbr(color_buf)
157+
158+
spawn = Compute(Shader(SPAWN_SHADER))
159+
motion = Compute(Shader(MOTION_SHADER))
160+
161+
162+
def draw():
163+
camera_position(0.0, 4.0, 16.0)
164+
camera_look_at(0.0, 2.0, 0.0)
165+
background(10, 10, 18)
166+
167+
use_material(mat)
168+
draw_field(field_obj, particle)
169+
170+
# Animate spawn point in a small circle so the fountain meanders.
171+
t = elapsed_time
172+
sx = math.cos(t) * 0.4
173+
sz = math.sin(t) * 0.4
174+
spawn.set(pos=[sx, 7.0, sz, 0.0], speed=[SPEED, 0.0, 0.0, 0.0])
175+
field_obj.emit_gpu(BURST, spawn)
176+
177+
motion.set(dt=DT, ttl=TTL, gravity=GRAVITY)
178+
field_obj.apply(motion)
179+
180+
181+
run()
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from mewnala import *
2+
3+
field_obj = None
4+
particle = None
5+
mat = None
6+
7+
8+
def setup():
9+
global field_obj, particle, mat
10+
11+
size(900, 700)
12+
mode_3d()
13+
14+
create_directional_light((0.95, 0.9, 0.85), 200.0)
15+
16+
# Source mesh whose vertices become the particle positions. UVs come along
17+
# for free and we'll use them to paint each particle a unique color.
18+
source = Geometry.sphere(5.0, 32, 24)
19+
field_obj = Field(
20+
geometry=source,
21+
attributes=[Attribute.position(), Attribute.uv(), Attribute.color()],
22+
)
23+
24+
uv_buf = field_obj.buffer(Attribute.uv())
25+
color_buf = field_obj.buffer(Attribute.color())
26+
27+
colors = []
28+
for uv in uv_buf.read():
29+
c = hsva(uv[0] * 360.0, 0.85, 1.0)
30+
colors.append([c.r, c.g, c.b, 1.0])
31+
color_buf.write(colors)
32+
33+
particle = Geometry.sphere(0.18, 10, 8)
34+
mat = Material.field_pbr(color_buf)
35+
36+
37+
def draw():
38+
camera_position(0.0, 4.0, 18.0)
39+
camera_look_at(0.0, 0.0, 0.0)
40+
background(15, 15, 20)
41+
42+
use_material(mat)
43+
draw_field(field_obj, particle)
44+
45+
46+
run()

0 commit comments

Comments
 (0)