1format: tdn
2version: '2.0'
3build: null
4generator: Embody/6.0.16
5td_build: 099.2025.32820
6source_file: Embody-6.16.toe
7exported_at: '2026-06-12T00:27:58Z'
8network_path: /specimen_lab/murmuration
9options:
10 include_dat_content: true
11 include_storage: true
12type_defaults:
13 annotateCOMP:
14 parameters:
15 order: =me.digits or 0
16 layerzone: =0 if hasattr(me, 'EncloseOPs') and me.EncloseOPs else 1
17 flags:
18 - display
19 color: [0.45, 0.45, 0.45]
20 glslPOP:
21 parameters:
22 outputattrs: '*'
23 size: [130, 90]
24 color: [0.67, 0.67, 0.67]
25 infoDAT:
26 flags:
27 - viewer
28 size: [130, 90]
29 color: [0.67, 0.67, 0.67]
30 textDAT:
31 parameters:
32 language: glsl
33 flags:
34 - viewer
35 size: [130, 90]
36 color: [0.67, 0.67, 0.67]
37par_templates:
38 about:
39 - default: '1.0'
40 help: Annotate COMP default setup version.
41 name: Version
42 readOnly: true
43 startSection: true
44 style: Str
45 - help: Click to open help page.
46 name: Help
47 style: Pulse
48 text:
49 - default: Annotate
50 help: Text in the title bar.
51 label: Title Text
52 name: Titletext
53 startSection: true
54 style: Str
55 - clampMin: true
56 default: 30
57 help: Height of the title bar. Title font height adjusts automatically to fill.
58 label: Title Height
59 name: Titleheight
60 normMax: 100.0
61 normMin: 5.0
62 style: Int
63 - default: left
64 help: Alignment of title text.
65 label: Title Align
66 menuLabels:
67 - Left
68 - Center
69 - Right
70 menuNames:
71 - left
72 - center
73 - right
74 name: Titlealign
75 style: Menu
76 - help: Text in the body area. Use an expression for newlines etc.
77 label: Body Text
78 name: Bodytext
79 startSection: true
80 style: Str
81 - clampMin: true
82 default: 10
83 help: Size of text in the body area.
84 label: Body Font Size
85 name: Bodyfontsize
86 normMax: 100.0
87 normMin: 8.0
88 style: Int
89 - help: Limit the width of the text area.
90 label: Limit Body Text Width
91 name: Bodylimitwidth
92 style: Toggle
93 - clampMin: true
94 default: 1000
95 help: Width limit that will cause wraparound or cut-off. Measured in Panel units.
96 label: Max Body Text Width
97 name: Bodymaxwidth
98 normMax: 2000.0
99 normMin: 100.0
100 style: Int
101 settings:
102 - default: comment
103 help: 'Switch between Comment, Network Box, and Annotate Modes. '
104 menuLabels:
105 - Comment
106 - Network Box
107 - Annotate
108 menuNames:
109 - comment
110 - networkbox
111 - annotate
112 name: Mode
113 style: Menu
114 - help: Converts quotes, ellipsis, and dashes to more typographically nice unicode versions.
115 label: Smart Quote
116 name: Smartquote
117 startSection: true
118 style: Toggle
119 - default: true
120 help: Wrap body text when it extends past right bound.
121 label: Body Word Wrap
122 name: Bodywordwrap
123 style: Toggle
124 - default: 1
125 help: Background color base.
126 label: Back Color
127 name: Backcolorr
128 startSection: true
129 style: RGBA
130 - clampMax: true
131 clampMin: true
132 default: 1
133 help: Back color alpha.
134 label: Back Color Alpha
135 name: Backcoloralpha
136 style: Float
137 - clampMax: true
138 clampMin: true
139 default: 1
140 help: Opacity of the entire Annotate.
141 label: Annotate Opacity
142 name: Opacity
143 style: Float
144 op_viewer:
145 - help: Turn the visibility of the viewer specified in the OP parameter below on or off.
146 label: Viewer Display
147 name: Opviewerdisplay
148 style: Toggle
149 - help: The operator whose viewer is displayed in the Annotate.
150 label: OP
151 name: Opviewer
152 style: OP
153 - help: Allow interaction with the OP viewer.
154 label: OP Viewer Interactive
155 name: Opviewerinteractive
156 style: Toggle
157 - default: 'False'
158 help: Use the Size/Aspect Override to control viewer's size in the background.
159 label: Size/Aspect Override
160 menuLabels:
161 - Natural
162 - Specify
163 - Auto-Fit
164 menuNames:
165 - natural
166 - specify
167 - autofit
168 name: Opvieweroversize
169 startSection: true
170 style: Menu
171 - clampMin: true
172 default: 800
173 help: Diplay viewer as-if it were being displayed at this resolution. This is particularly useful for zooming into operators that don't have a built-in resolution, like CHOPs, SOPs, and DATs.
174 label: Size/Aspect
175 name: Opviewersize
176 normMax: 1000.0
177 normMin: 1.0
178 style: WH
179 - default: 1
180 help: Scale the viewer by this factor.
181 label: Scale
182 name: Opviewerscale
183 normMax: 2.0
184 startSection: true
185 style: Float
186 - help: Move the border of the viewer towards left edge of Annotate when negative or towards right edge when positive.
187 label: Justify X
188 name: Opviewerjustifyx
189 normMin: -1.0
190 style: Float
191 - label: Justify Y
192 name: Opviewerjustifyy
193 normMin: -1.0
194 style: Float
195 - help: When True, allow viewer to display in the Annotate title area as well as body.
196 label: Cover Body and Title
197 name: Opviewerfillbodytitle
198 style: Toggle
199 - clampMin: true
200 default: 1
201 help: Zoom the viewer by this scale factor without increasing the size of its display area in the Annotate.
202 label: OP Viewer Zoom
203 min: 0.001
204 name: Opviewerzoom
205 normMax: 5.0
206 normMin: 1.0
207 startSection: true
208 style: Float
209 - help: Offsets the displayed area within the viewer. Combined with OP Viewer Zoom, this lets you display a specific area of a viewer, such as a CHOP channel or table cell.
210 label: OP Viewer Offset
211 name: Opvieweroffsetx
212 style: XYZW
213 - clampMax: true
214 clampMin: true
215 default: 1
216 help: Alpha value of the background area in the OP Viewer.
217 label: Fill Alpha
218 name: Opviewerfillalpha
219 startSection: true
220 style: Float
221type: baseCOMP
222custom_pars:
223 Murmuration:
224 - name: Cohesion
225 style: Float
226 startSection: true
227 default: 0.04
228 max: 2.0
229 clampMin: true
230 normMax: 0.5
231 help: Steer toward the local flock centroid (Reynolds cohesion).
232 - name: Alignment
233 style: Float
234 default: 0.55
235 max: 3.0
236 clampMin: true
237 normMax: 2.0
238 help: Match the mean heading of neighbors (Reynolds alignment).
239 - name: Separation
240 style: Float
241 default: 0.9
242 max: 5.0
243 clampMin: true
244 normMax: 3.0
245 help: Inverse-square repulsion from close neighbors. Prevents collapse - the anti-blob force.
246 - name: Attractor
247 style: Float
248 default: 0.15
249 max: 2.0
250 clampMin: true
251 help: Pull toward the slow wandering roost attractor.
252 - name: Spiral
253 style: Float
254 default: 0.55
255 max: 3.0
256 clampMin: true
257 normMax: 2.0
258 help: Tangential swirl around the attractor (vortex shear / split-reform).
259 - name: Curl
260 style: Float
261 default: 0.4
262 max: 3.0
263 clampMin: true
264 normMax: 2.0
265 help: Curl-noise wander for organic break-up.
266 - name: Bodyradius
267 style: Float
268 label: Body Radius
269 startSection: true
270 default: 2.4
271 min: 0.5
272 max: 6.0
273 clampMin: true
274 normMin: 1.0
275 normMax: 4.0
276 help: Soft containment radius - the size of the coherent body.
277 - name: Morphspeed
278 style: Float
279 label: Morph Speed
280 default: 1
281 max: 4.0
282 clampMin: true
283 normMax: 3.0
284 help: Overall evolution rate (attractor drift + curl). 0 freezes the motion.
285 - name: Maxparticles
286 style: Int
287 label: Max Particles
288 startSection: true
289 default: 5000
290 min: 100.0
291 clampMin: true
292 normMin: 1000.0
293 normMax: 40000.0
294 help: Maximum live particle pool.
295 - name: Birthrate
296 style: Float
297 label: Birth Rate
298 default: 2000
299 clampMin: true
300 normMax: 8000.0
301 help: Particles born per second.
302color: [0.67, 0.67, 0.67]
303operators:
304- name: out1
305 type: outTOP
306 parameters:
307 label: =me.name
308 flags:
309 - viewer
310 position: [2900, 0]
311 size: [130, 90]
312 color: [0.67, 0.67, 0.67]
313 inputs:
314 - bloom_glow
315- name: null_sim
316 type: nullPOP
317 flags:
318 - display
319 - render
320 position: [1400, 0]
321 size: [130, 90]
322 color: [0.67, 0.67, 0.67]
323 inputs:
324 - glsl_flock
325- name: annotate1
326 type: annotateCOMP
327 sequences:
328 ext:
329 - object: op.TDAnnotate.mod.AnnotateExt.AnnotateExt(me)
330 promote: true
331 custom_pars:
332 Text:
333 $t: text
334 Titletext: Source & Emission
335 Bodytext: Sphere POP seeds start positions; Particle POP births + integrates (timeintegration ON). Its targetpop is null_sim - the downstream feedback target that closes the GPU sim loop each frame.
336 Settings:
337 $t: settings
338 Mode: annotate
339 Backcolorr: [0.12, 0.14, 0.2]
340 Opacity: 0.55
341 OP Viewer:
342 $t: op_viewer
343 Opvieweroversize: natural
344 About:
345 $t: about
346 position: [-70, -70]
347 size: [570, 330]
348 palette_clone: true
349- name: annotate2
350 type: annotateCOMP
351 sequences:
352 ext:
353 - object: op.TDAnnotate.mod.AnnotateExt.AnnotateExt(me)
354 promote: true
355 custom_pars:
356 Text:
357 $t: text
358 Titletext: Flocking Brain - GPU Reynolds + Feedback
359 Bodytext: Neighbor POP emits a per-point neighbor INDEX list (Nebr). The GLSL POP compute shader iterates those real neighbors for true Reynolds steering - cohesion, alignment, and inverse-square separation - plus a slow moving attractor, curl-noise wander, soft containment and drag. It writes PartForce; null_sim feeds the result back to the Particle POP.
360 Settings:
361 $t: settings
362 Mode: annotate
363 Backcolorr: [0.1, 0.16, 0.16]
364 Opacity: 0.55
365 OP Viewer:
366 $t: op_viewer
367 Opvieweroversize: natural
368 About:
369 $t: about
370 position: [730, -240]
371 size: [870, 500]
372 palette_clone: true
373- name: annotate3
374 type: annotateCOMP
375 sequences:
376 ext:
377 - object: op.TDAnnotate.mod.AnnotateExt.AnnotateExt(me)
378 promote: true
379 custom_pars:
380 Text:
381 $t: text
382 Titletext: Color, Sprite Render & Bloom
383 Bodytext: A side-branch GLSL POP maps speed -> a 3-stop dusk color ramp (deep blue-violet / ice-cyan / warm amber) and a PointScale. The Point Sprite MAT renders the swarm as additive soft round dots (sprite_dot drives the sprite shape); Bloom adds the luminous glow; out1 is the output TOP.
384 Settings:
385 $t: settings
386 Mode: annotate
387 Backcolorr: [0.18, 0.13, 0.16]
388 Opacity: 0.55
389 OP Viewer:
390 $t: op_viewer
391 Opvieweroversize: natural
392 About:
393 $t: about
394 position: [1870, -470]
395 size: [1230, 730]
396 palette_clone: true
397- name: bloom_glow
398 type: bloomTOP
399 position: [2600, 0]
400 size: [130, 90]
401 color: [0.67, 0.67, 0.67]
402 inputs:
403 - render_swarm
404- name: glsl_color
405 type: glslPOP
406 parameters:
407 computedat: glsl_color_compute
408 sequences:
409 attr:
410 - customname: Color
411 numcomps: '4'
412 value0: 0.5
413 value1: 0.6
414 value2: 1
415 - customname: PointScale
416 value0: 1
417 position: [2000, 0]
418 inputs:
419 - null_sim
420- name: glsl_flock
421 type: glslPOP
422 parameters:
423 computedat: glsl_flock_compute
424 sequences:
425 vec:
426 - name: uWeights
427 type: vec4
428 valuex: =parent().par.Cohesion.eval()
429 valuey: =parent().par.Alignment.eval()
430 valuez: =parent().par.Separation.eval()
431 valuew: =parent().par.Attractor.eval()
432 - name: uDynamics
433 type: vec4
434 valuex: =parent().par.Bodyradius.eval()
435 valuey: 3.5
436 valuez: =parent().par.Curl.eval()
437 valuew: =absTime.seconds*0.3*parent().par.Morphspeed.eval()
438 - name: uAttractor
439 type: vec4
440 valuex: =math.sin(absTime.seconds*0.11*parent().par.Morphspeed.eval())*1.8 + math.sin(absTime.seconds*0.067*parent().par.Morphspeed.eval())*0.9
441 valuey: =0.3 + math.sin(absTime.seconds*0.09*parent().par.Morphspeed.eval())*1.1
442 valuez: =math.cos(absTime.seconds*0.13*parent().par.Morphspeed.eval())*1.8 + math.cos(absTime.seconds*0.051*parent().par.Morphspeed.eval())*0.9
443 valuew: =parent().par.Spiral.eval()
444 position: [1100, 0]
445 inputs:
446 - neighbor_analysis
447- name: mat_sprite
448 type: pointspriteMAT
449 parameters:
450 colormap: sprite_dot
451 pointsize: 9
452 skelrootpath: =parent()
453 blending: true
454 destblend: one
455 depthtest: false
456 depthwriting: false
457 position: [2300, -230]
458 size: [130, 90]
459 color: [0.67, 0.67, 0.67]
460- name: sprite_dot
461 type: glslTOP
462 parameters:
463 pixeldat: sprite_dot_pixel
464 computedat: sprite_dot_compute
465 outputresolution: custom
466 resolutionw: 64
467 resolutionh: 64
468 position: [2140, -230]
469 size: [130, 90]
470 color: [0.67, 0.67, 0.67]
471- name: particle_pop
472 type: particlePOP
473 parameters:
474 targetpop: null_sim
475 maxparticles: =parent().par.Maxparticles.eval()
476 birthrate: =parent().par.Birthrate.eval()
477 life: 8
478 position: [300, 0]
479 size: [130, 90]
480 color: [0.67, 0.67, 0.67]
481 inputs:
482 - source_points
483- name: render_swarm
484 type: rendersimpleTOP
485 parameters:
486 camdistance: 7.5
487 bgcolorr: 0.01
488 bgcolorg: 0.012
489 bgcolorb: 0.03
490 bgcolora: 1
491 pop: glsl_color
492 materialsource: matnode
493 mat: mat_sprite
494 position: [2300, 0]
495 size: [130, 90]
496 color: [0.67, 0.67, 0.67]
497- name: source_points
498 type: spherePOP
499 size: [130, 90]
500 color: [0.67, 0.67, 0.67]
501- name: glsl_color_info
502 type: infoDAT
503 parameters:
504 op: glsl_color
505 position: [2110, -170]
506 dock: glsl_color
507 dat_read_only: true
508- name: glsl_flock_info
509 type: infoDAT
510 parameters:
511 op: glsl_flock
512 position: [1210, -170]
513 dock: glsl_flock
514 dat_read_only: true
515- name: sprite_dot_info
516 type: infoDAT
517 parameters:
518 op: sprite_dot
519 position: [2260, -400]
520 dock: sprite_dot
521 dat_read_only: true
522- name: sprite_dot_pixel
523 type: textDAT
524 parameters:
525 extension: frag
526 position: [2130, -400]
527 dock: sprite_dot
528 dat_content: |-
529 out vec4 fragColor;
530 void main(){
531 vec2 uv = vUV.st * 2.0 - 1.0;
532 float r = length(uv);
533 float a = smoothstep(1.0, 0.15, r); // bright full core, soft round edge
534 fragColor = vec4(vec3(a), 1.0);
535 }
536 dat_content_format: text
537- name: neighbor_analysis
538 type: neighborPOP
539 parameters:
540 maxdistance: 0.2
541 numhashbuckets: 8192
542 maxneighbors: 16
543 maxnebrsavg: 16
544 incquerypt: false
545 addprefix: true
546 position: [800, 0]
547 size: [130, 90]
548 color: [0.67, 0.67, 0.67]
549 inputs:
550 - particle_pop
551- name: glsl_color_compute
552 type: textDAT
553 position: [1940, -170]
554 dock: glsl_color
555 dat_content: |
556 // Murmuration - speed -> dusk color ramp + point size (render side branch)
557 void main(){
558 uint id = TDIndex();
559 if(id >= TDNumElements()) return;
560 vec3 V = TDIn_PartVel(0, id);
561 float sp = length(V);
562 float t = clamp((sp - 0.12) / 0.55, 0.0, 1.0);
563 vec3 violet = vec3(0.16, 0.09, 0.55); // slow - deep blue-violet
564 vec3 cyan = vec3(0.55, 0.92, 1.00); // mid - ice cyan/white
565 vec3 amber = vec3(1.00, 0.68, 0.26); // fast - warm amber/gold
566 vec3 col = (t < 0.5) ? mix(violet, cyan, t * 2.0)
567 : mix(cyan, amber, (t - 0.5) * 2.0);
568 Color[id] = vec4(col * 0.7, 1.0);
569 PointScale[id] = 1.0 + t * 0.9;
570 P[id] = TDIn_P(0, id);
571 }
572 dat_content_format: text
573- name: glsl_flock_compute
574 type: textDAT
575 position: [1040, -170]
576 dock: glsl_flock
577 dat_content: |
578 // Murmuration - GPU flocking compute (GLSL POP), TRUE per-neighbor Reynolds.
579 // Iterates the Neighbor POP index list (Nebr) to compute cohesion, alignment,
580 // and inverse-square separation directly - the separation is dominated by the
581 // closest neighbor so it never cancels in a symmetric clump (even spacing).
582 // uWeights = (cohesion, alignment, separation, attractor)
583 // uDynamics = (bodyRadius, maxForce, curl, time)
584 // uAttractor = (ax, ay, az, spiral)
585
586 vec3 hash33(vec3 p){
587 p = vec3(dot(p, vec3(127.1,311.7,74.7)), dot(p, vec3(269.5,183.3,246.1)), dot(p, vec3(113.5,271.9,124.6)));
588 return fract(sin(p) * 43758.5453) * 2.0 - 1.0;
589 }
590 float vnoise(vec3 p){
591 vec3 i=floor(p); vec3 f=fract(p); vec3 u=f*f*(3.0-2.0*f);
592 return mix(mix(mix(dot(hash33(i+vec3(0,0,0)),f-vec3(0,0,0)), dot(hash33(i+vec3(1,0,0)),f-vec3(1,0,0)),u.x),
593 mix(dot(hash33(i+vec3(0,1,0)),f-vec3(0,1,0)), dot(hash33(i+vec3(1,1,0)),f-vec3(1,1,0)),u.x),u.y),
594 mix(mix(dot(hash33(i+vec3(0,0,1)),f-vec3(0,0,1)), dot(hash33(i+vec3(1,0,1)),f-vec3(1,0,1)),u.x),
595 mix(dot(hash33(i+vec3(0,1,1)),f-vec3(0,1,1)), dot(hash33(i+vec3(1,1,1)),f-vec3(1,1,1)),u.x),u.y),u.z);
596 }
597 vec3 snoiseVec3(vec3 x){ return vec3(vnoise(x), vnoise(x+vec3(123.4,0,0)), vnoise(x+vec3(0,234.5,0))); }
598 vec3 curlNoise(vec3 p){
599 const float e=0.1; vec3 dx=vec3(e,0,0), dy=vec3(0,e,0), dz=vec3(0,0,e);
600 vec3 px0=snoiseVec3(p-dx),px1=snoiseVec3(p+dx),py0=snoiseVec3(p-dy),py1=snoiseVec3(p+dy),pz0=snoiseVec3(p-dz),pz1=snoiseVec3(p+dz);
601 float x=(py1.z-py0.z)-(pz1.y-pz0.y), y=(pz1.x-pz0.x)-(px1.z-px0.z), z=(px1.y-px0.y)-(py1.x-py0.x);
602 return normalize(vec3(x,y,z)/(2.0*e) + 1e-6);
603 }
604
605 void main(){
606 uint id = TDIndex();
607 if(id >= TDNumElements()) return;
608
609 vec3 P0 = TDIn_P(0, id);
610 vec3 V0 = TDIn_PartVel(0, id);
611 int nN = int(TDIn_NumNebrs(0, id));
612
613 vec3 sumPos = vec3(0.0);
614 vec3 sumVel = vec3(0.0);
615 vec3 sep = vec3(0.0);
616 int cnt = 0;
617 const int MAXN = 16;
618 for(int i = 0; i < MAXN; i++){
619 if(i >= nN) break;
620 uint nIdx = TDIn_Nebr(0, id, i);
621 vec3 nP = TDIn_P(0, uint(nIdx));
622 vec3 nV = TDIn_PartVel(0, uint(nIdx));
623 vec3 diff = P0 - nP;
624 float dist = length(diff) + 1e-5;
625 sumPos += nP;
626 sumVel += nV;
627 float push = clamp(0.03 / (dist*dist), 0.0, 6.0); // inverse-square-capped: closest neighbor
628 sep += (diff / dist) * push; // closest neighbor dominates (no cancel)
629 cnt++;
630 }
631
632 vec3 force = vec3(0.0);
633 if(cnt > 0){
634 vec3 nbrCenter = sumPos / float(cnt);
635 vec3 avgVel = sumVel / float(cnt);
636 force += uWeights.x * (nbrCenter - P0); // cohesion
637 force += uWeights.y * (avgVel - V0); // alignment
638 force += uWeights.z * sep; // separation: summed per-neighbor push (crowded => stronger)
639 }
640
641 // moving attractor - slow radial pull + tangential spiral
642 vec3 toA = uAttractor.xyz - P0;
643 float dA = length(toA) + 1e-5;
644 vec3 dirA = toA / dA;
645 force += uWeights.w * dirA;
646 vec3 tang = normalize(cross(vec3(0.0,1.0,0.0), dirA) + 1e-6);
647 force += uAttractor.w * tang;
648
649 // curl wander
650 vec3 cp = P0 * 1.6 + vec3(0.0, uDynamics.w, 0.0);
651 force += uDynamics.z * curlNoise(cp);
652
653 // clamp the steering forces
654 float maxF = uDynamics.y;
655 float fl = length(force);
656 if(fl > maxF) force *= maxF / fl;
657
658 // containment + drag AFTER the clamp
659 float R = uDynamics.x;
660 if(dA > R) force += dirA * (dA - R) * 1.5;
661 force -= V0 * 0.7;
662
663 PartForce[id] = force;
664 P[id] = P0;
665 }
666 dat_content_format: text
667- name: sprite_dot_compute
668 type: textDAT
669 position: [2000, -400]
670 dock: sprite_dot
671annotations:
672- name: annotate1
673 mode: annotate
674 title: Source & Emission
675 text: Sphere POP seeds start positions; Particle POP births + integrates (timeintegration ON). Its targetpop is null_sim - the downstream feedback target that closes the GPU sim loop each frame.
676 position: [-70, -70]
677 size: [570, 330]
678 color: [0.12, 0.14, 0.2]
679 opacity: 0.55
680- name: annotate2
681 mode: annotate
682 title: Flocking Brain - GPU Reynolds + Feedback
683 text: Neighbor POP emits a per-point neighbor INDEX list (Nebr). The GLSL POP compute shader iterates those real neighbors for true Reynolds steering - cohesion, alignment, and inverse-square separation - plus a slow moving attractor, curl-noise wander, soft containment and drag. It writes PartForce; null_sim feeds the result back to the Particle POP.
684 position: [730, -240]
685 size: [870, 500]
686 color: [0.1, 0.16, 0.16]
687 opacity: 0.55
688- name: annotate3
689 mode: annotate
690 title: Color, Sprite Render & Bloom
691 text: A side-branch GLSL POP maps speed -> a 3-stop dusk color ramp (deep blue-violet / ice-cyan / warm amber) and a PointScale. The Point Sprite MAT renders the swarm as additive soft round dots (sprite_dot drives the sprite shape); Bloom adds the luminous glow; out1 is the output TOP.
692 position: [1870, -470]
693 size: [1230, 730]
694 color: [0.18, 0.13, 0.16]
695 opacity: 0.55