{
"name": "embers",
"attributes": [
{
"name": "position",
"components": 3
},
{
"name": "velocity",
"components": 3
},
{
"name": "age",
"components": 1
},
{
"name": "life",
"components": 1
},
{
"name": "size",
"components": 1
},
{
"name": "size0",
"components": 1
},
{
"name": "color",
"components": 4
},
{
"name": "phase",
"components": 1
},
{
"name": "seed",
"components": 1
}
],
"groups": [],
"init": {
"nodes": [
{
"id": 18,
"type": "builtin",
"parameters": {
"id": 2
},
"x": 413.5,
"y": 375
},
{
"id": 19,
"type": "add",
"parameters": {},
"x": 803.5,
"y": 432.0000216484863
},
{
"id": 20,
"type": "setAttribute",
"parameters": {
"name": "position"
},
"x": 1097,
"y": 432.0000216484863
},
{
"id": 21,
"type": "const",
"parameters": {
"value": [
0,
2.6,
0
]
},
"x": 727,
"y": 39.5
},
{
"id": 22,
"type": "random",
"parameters": {
"components": 3
},
"x": 63.5,
"y": 0
},
{
"id": 23,
"type": "mad",
"parameters": {},
"x": 440.5,
"y": 114
},
{
"id": 24,
"type": "const",
"parameters": {
"value": [
2
]
},
"x": 61,
"y": 131
},
{
"id": 25,
"type": "const",
"parameters": {
"value": [
-1
]
},
"x": 61,
"y": 262
},
{
"id": 26,
"type": "scale",
"parameters": {},
"x": 804,
"y": 170.5
},
{
"id": 27,
"type": "const",
"parameters": {
"value": [
1.15
]
},
"x": 424.5,
"y": 244
},
{
"id": 28,
"type": "add",
"parameters": {},
"x": 1129.5,
"y": 96.5
},
{
"id": 29,
"type": "setAttribute",
"parameters": {
"name": "velocity"
},
"x": 1379,
"y": 96.5
},
{
"id": 30,
"type": "random",
"parameters": {
"components": 1
},
"x": 63.5,
"y": 637.000017418334
},
{
"id": 31,
"type": "mad",
"parameters": {},
"x": 440.5,
"y": 751.000017418334
},
{
"id": 32,
"type": "const",
"parameters": {
"value": [
0.032
]
},
"x": 61,
"y": 768.000017418334
},
{
"id": 33,
"type": "const",
"parameters": {
"value": [
0.018
]
},
"x": 61,
"y": 899.000017418334
},
{
"id": 36,
"type": "random",
"parameters": {
"components": 1
},
"x": 63.5,
"y": 1161.000017418334
},
{
"id": 37,
"type": "mad",
"parameters": {},
"x": 440.5,
"y": 1275.000017418334
},
{
"id": 38,
"type": "const",
"parameters": {
"value": [
1.2
]
},
"x": 61,
"y": 1292.000017418334
},
{
"id": 39,
"type": "const",
"parameters": {
"value": [
0.4
]
},
"x": 61,
"y": 1423.000017418334
},
{
"id": 40,
"type": "setAttribute",
"parameters": {
"name": "life"
},
"x": 771,
"y": 1275.000017418334
},
{
"id": 41,
"type": "setAttribute",
"parameters": {
"name": "size0"
},
"x": 771,
"y": 620.0000216484864
},
{
"id": 42,
"type": "setAttribute",
"parameters": {
"name": "size"
},
"x": 771,
"y": 817.000008709167
},
{
"id": 43,
"type": "setAttribute",
"parameters": {
"name": "color"
},
"x": 428,
"y": 1554.000017418334
},
{
"id": 44,
"type": "const",
"parameters": {
"value": [
1,
1,
1,
0
]
},
"x": 0,
"y": 1554.000017418334
},
{
"id": 45,
"type": "random",
"parameters": {
"components": 1
},
"x": 63.5,
"y": 1685.000017418334
},
{
"id": 46,
"type": "mad",
"parameters": {},
"x": 440.5,
"y": 1799.000017418334
},
{
"id": 47,
"type": "const",
"parameters": {
"value": [
6.28
]
},
"x": 61,
"y": 1816.000017418334
},
{
"id": 48,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 61,
"y": 1947.000017418334
},
{
"id": 49,
"type": "setAttribute",
"parameters": {
"name": "phase"
},
"x": 771,
"y": 1799.000017418334
},
{
"id": 50,
"type": "random",
"parameters": {
"components": 1
},
"x": 63.5,
"y": 2078.000017418334
},
{
"id": 51,
"type": "mad",
"parameters": {},
"x": 440.5,
"y": 2192.000017418334
},
{
"id": 52,
"type": "const",
"parameters": {
"value": [
64
]
},
"x": 61,
"y": 2209.000017418334
},
{
"id": 53,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 61,
"y": 2340.000017418334
},
{
"id": 54,
"type": "setAttribute",
"parameters": {
"name": "seed"
},
"x": 771,
"y": 2192.000017418334
},
{
"id": 55,
"type": "sampleSphereVolume",
"parameters": {},
"x": 411,
"y": 506
},
{
"id": 56,
"type": "random",
"parameters": {
"components": 3
},
"x": 63.5,
"y": 506.000017418334
},
{
"id": 35,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 61,
"y": 1030.000017418334
},
{
"id": 34,
"type": "setAttribute",
"parameters": {
"name": "age"
},
"x": 428,
"y": 1030.000017418334
}
],
"connections": [
{
"from": 18,
"fromPort": "out",
"to": 19,
"toPort": "a"
},
{
"from": 19,
"fromPort": "out",
"to": 20,
"toPort": "value"
},
{
"from": 22,
"fromPort": "out",
"to": 23,
"toPort": "a"
},
{
"from": 24,
"fromPort": "out",
"to": 23,
"toPort": "b"
},
{
"from": 25,
"fromPort": "out",
"to": 23,
"toPort": "c"
},
{
"from": 23,
"fromPort": "out",
"to": 26,
"toPort": "v"
},
{
"from": 27,
"fromPort": "out",
"to": 26,
"toPort": "s"
},
{
"from": 21,
"fromPort": "out",
"to": 28,
"toPort": "a"
},
{
"from": 26,
"fromPort": "out",
"to": 28,
"toPort": "b"
},
{
"from": 28,
"fromPort": "out",
"to": 29,
"toPort": "value"
},
{
"from": 30,
"fromPort": "out",
"to": 31,
"toPort": "a"
},
{
"from": 32,
"fromPort": "out",
"to": 31,
"toPort": "b"
},
{
"from": 33,
"fromPort": "out",
"to": 31,
"toPort": "c"
},
{
"from": 36,
"fromPort": "out",
"to": 37,
"toPort": "a"
},
{
"from": 38,
"fromPort": "out",
"to": 37,
"toPort": "b"
},
{
"from": 39,
"fromPort": "out",
"to": 37,
"toPort": "c"
},
{
"from": 37,
"fromPort": "out",
"to": 40,
"toPort": "value"
},
{
"from": 31,
"fromPort": "out",
"to": 41,
"toPort": "value"
},
{
"from": 31,
"fromPort": "out",
"to": 42,
"toPort": "value"
},
{
"from": 44,
"fromPort": "out",
"to": 43,
"toPort": "value"
},
{
"from": 45,
"fromPort": "out",
"to": 46,
"toPort": "a"
},
{
"from": 47,
"fromPort": "out",
"to": 46,
"toPort": "b"
},
{
"from": 48,
"fromPort": "out",
"to": 46,
"toPort": "c"
},
{
"from": 46,
"fromPort": "out",
"to": 49,
"toPort": "value"
},
{
"from": 50,
"fromPort": "out",
"to": 51,
"toPort": "a"
},
{
"from": 52,
"fromPort": "out",
"to": 51,
"toPort": "b"
},
{
"from": 53,
"fromPort": "out",
"to": 51,
"toPort": "c"
},
{
"from": 51,
"fromPort": "out",
"to": 54,
"toPort": "value"
},
{
"from": 56,
"fromPort": "out",
"to": 55,
"toPort": "uvw"
},
{
"from": 55,
"fromPort": "out",
"to": 19,
"toPort": "b"
},
{
"from": 35,
"fromPort": "out",
"to": 34,
"toPort": "value"
}
]
},
"update": {
"nodes": [
{
"id": 0,
"type": "builtin",
"parameters": {
"id": 0
},
"x": 7.5,
"y": 832
},
{
"id": 1,
"type": "attribute",
"parameters": {
"name": "age"
},
"x": 0,
"y": 1039
},
{
"id": 2,
"type": "add",
"parameters": {},
"x": 231.25,
"y": 1039
},
{
"id": 3,
"type": "setAttribute",
"parameters": {
"name": "age"
},
"x": 455,
"y": 1039
},
{
"id": 4,
"type": "attribute",
"parameters": {
"name": "life"
},
"x": 227.5,
"y": 1263.5
},
{
"id": 5,
"type": "div",
"parameters": {},
"x": 470,
"y": 1247.5
},
{
"id": 6,
"type": "compare",
"parameters": {
"op": "ge"
},
"x": 720,
"y": 1704
},
{
"id": 7,
"type": "const",
"parameters": {
"value": [
1
]
},
"x": 481.25,
"y": 1720
},
{
"id": 8,
"type": "kill",
"parameters": {},
"x": 955,
"y": 1704
},
{
"id": 9,
"type": "const",
"parameters": {
"value": [
0,
3.4,
0
]
},
"x": 966.25,
"y": 245
},
{
"id": 10,
"type": "const",
"parameters": {
"value": [
0,
-1.9,
0
]
},
"x": 966.25,
"y": 339
},
{
"id": 11,
"type": "mul",
"parameters": {},
"x": 720,
"y": 433
},
{
"id": 12,
"type": "const",
"parameters": {
"value": [
1.6
]
},
"x": 481.25,
"y": 433
},
{
"id": 13,
"type": "saturate",
"parameters": {},
"x": 955,
"y": 433
},
{
"id": 14,
"type": "mix",
"parameters": {},
"x": 1190,
"y": 284
},
{
"id": 15,
"type": "builtin",
"parameters": {
"id": 7
},
"x": 7.5,
"y": 188
},
{
"id": 16,
"type": "mul",
"parameters": {},
"x": 231.25,
"y": 730.5
},
{
"id": 17,
"type": "const",
"parameters": {
"value": [
-0.5
]
},
"x": 15,
"y": 457
},
{
"id": 18,
"type": "attribute",
"parameters": {
"name": "seed"
},
"x": 227.5,
"y": 314.5
},
{
"id": 19,
"type": "vec3",
"parameters": {},
"x": 470,
"y": 675.5
},
{
"id": 20,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 242.5,
"y": 840.5
},
{
"id": 21,
"type": "attribute",
"parameters": {
"name": "position"
},
"x": 0,
"y": 0
},
{
"id": 22,
"type": "scale",
"parameters": {},
"x": 231.25,
"y": 28.5
},
{
"id": 23,
"type": "const",
"parameters": {
"value": [
1.3
]
},
"x": 15,
"y": 94
},
{
"id": 24,
"type": "add",
"parameters": {},
"x": 720,
"y": 637
},
{
"id": 25,
"type": "const",
"parameters": {
"value": [
0,
0,
0
]
},
"x": 731.25,
"y": 543
},
{
"id": 26,
"type": "add",
"parameters": {},
"x": 955,
"y": 582
},
{
"id": 27,
"type": "simplex3",
"parameters": {},
"x": 1190,
"y": 582
},
{
"id": 28,
"type": "const",
"parameters": {
"value": [
17.3,
5.1,
31.7
]
},
"x": 731.25,
"y": 755
},
{
"id": 29,
"type": "add",
"parameters": {},
"x": 955,
"y": 692
},
{
"id": 30,
"type": "simplex3",
"parameters": {},
"x": 1190,
"y": 692
},
{
"id": 31,
"type": "const",
"parameters": {
"value": [
73.9,
41.3,
11.5
]
},
"x": 731.25,
"y": 857
},
{
"id": 32,
"type": "add",
"parameters": {},
"x": 955,
"y": 841
},
{
"id": 33,
"type": "simplex3",
"parameters": {},
"x": 1190,
"y": 841
},
{
"id": 34,
"type": "vec3",
"parameters": {},
"x": 1425,
"y": 661
},
{
"id": 35,
"type": "scale",
"parameters": {},
"x": 1660,
"y": 771
},
{
"id": 36,
"type": "const",
"parameters": {
"value": [
1.9
]
},
"x": 1436.25,
"y": 787
},
{
"id": 37,
"type": "add",
"parameters": {},
"x": 1895,
"y": 651
},
{
"id": 38,
"type": "attribute",
"parameters": {
"name": "velocity"
},
"x": 2115,
"y": 198
},
{
"id": 39,
"type": "scale",
"parameters": {},
"x": 2118.75,
"y": 677.5
},
{
"id": 40,
"type": "add",
"parameters": {},
"x": 2342.5,
"y": 622.5
},
{
"id": 41,
"type": "setAttribute",
"parameters": {
"name": "velocity"
},
"x": 2562.5,
"y": 622.5
},
{
"id": 42,
"type": "attribute",
"parameters": {
"name": "position"
},
"x": 2573.75,
"y": 10.5
},
{
"id": 43,
"type": "scale",
"parameters": {},
"x": 2577.5,
"y": 104.5
},
{
"id": 44,
"type": "add",
"parameters": {},
"x": 2812.5,
"y": 49.5
},
{
"id": 45,
"type": "setAttribute",
"parameters": {
"name": "position"
},
"x": 3032.5,
"y": 49.5
},
{
"id": 46,
"type": "attribute",
"parameters": {
"name": "size0"
},
"x": 716.25,
"y": 1461
},
{
"id": 47,
"type": "curveLiteral",
"parameters": {
"curve": {
"keys": [
{
"time": 0,
"value": 1,
"inTangent": 0,
"outTangent": 0
},
{
"time": 0.75,
"value": 0.9,
"inTangent": 0,
"outTangent": 0
},
{
"time": 1,
"value": 0.15,
"inTangent": 0,
"outTangent": 0
}
]
}
},
"x": 705,
"y": 1610
},
{
"id": 48,
"type": "mul",
"parameters": {},
"x": 955,
"y": 1555
},
{
"id": 49,
"type": "setAttribute",
"parameters": {
"name": "size"
},
"x": 1175,
"y": 1555
},
{
"id": 50,
"type": "builtin",
"parameters": {
"id": 7
},
"x": 7.5,
"y": 1188
},
{
"id": 51,
"type": "mul",
"parameters": {},
"x": 231.25,
"y": 1357.5
},
{
"id": 52,
"type": "const",
"parameters": {
"value": [
11
]
},
"x": 15,
"y": 1422
},
{
"id": 53,
"type": "attribute",
"parameters": {
"name": "phase"
},
"x": 227.5,
"y": 1467.5
},
{
"id": 54,
"type": "add",
"parameters": {},
"x": 470,
"y": 1357.5
},
{
"id": 55,
"type": "sin",
"parameters": {},
"x": 720,
"y": 1265
},
{
"id": 56,
"type": "mul",
"parameters": {},
"x": 955,
"y": 1257
},
{
"id": 57,
"type": "mad",
"parameters": {},
"x": 1190,
"y": 1343
},
{
"id": 58,
"type": "const",
"parameters": {
"value": [
2
]
},
"x": 966.25,
"y": 1367
},
{
"id": 59,
"type": "const",
"parameters": {
"value": [
1
]
},
"x": 966.25,
"y": 1461
},
{
"id": 60,
"type": "gradientRGBA",
"parameters": {
"stops": [
{
"position": 0,
"color": [
1,
0.86,
0.52,
0
]
},
{
"position": 0.06,
"color": [
1,
0.72,
0.28,
0.8
]
},
{
"position": 0.55,
"color": [
1,
0.42,
0.1,
0.55
]
},
{
"position": 1,
"color": [
0.7,
0.1,
0.02,
0
]
}
]
},
"x": 1175,
"y": 1108
},
{
"id": 61,
"type": "mul",
"parameters": {},
"x": 1425,
"y": 1247.5
},
{
"id": 62,
"type": "setAttribute",
"parameters": {
"name": "color"
},
"x": 1645,
"y": 1247.5
},
{
"id": 63,
"type": "wind",
"parameters": {},
"x": 2160,
"y": 882.5
},
{
"id": 64,
"type": "const",
"parameters": {
"value": [
1
]
},
"x": 2160,
"y": 1002.5
},
{
"id": 65,
"type": "scale",
"parameters": {},
"x": 2460,
"y": 882.5
},
{
"id": 66,
"type": "add",
"parameters": {},
"x": 2720,
"y": 822.5
}
],
"connections": [
{
"from": 1,
"fromPort": "out",
"to": 2,
"toPort": "a"
},
{
"from": 0,
"fromPort": "out",
"to": 2,
"toPort": "b"
},
{
"from": 2,
"fromPort": "out",
"to": 3,
"toPort": "value"
},
{
"from": 2,
"fromPort": "out",
"to": 5,
"toPort": "a"
},
{
"from": 4,
"fromPort": "out",
"to": 5,
"toPort": "b"
},
{
"from": 5,
"fromPort": "out",
"to": 6,
"toPort": "a"
},
{
"from": 7,
"fromPort": "out",
"to": 6,
"toPort": "b"
},
{
"from": 6,
"fromPort": "out",
"to": 8,
"toPort": "condition"
},
{
"from": 12,
"fromPort": "out",
"to": 11,
"toPort": "a"
},
{
"from": 5,
"fromPort": "out",
"to": 11,
"toPort": "b"
},
{
"from": 11,
"fromPort": "out",
"to": 13,
"toPort": "x"
},
{
"from": 9,
"fromPort": "out",
"to": 14,
"toPort": "a"
},
{
"from": 10,
"fromPort": "out",
"to": 14,
"toPort": "b"
},
{
"from": 13,
"fromPort": "out",
"to": 14,
"toPort": "t"
},
{
"from": 15,
"fromPort": "out",
"to": 16,
"toPort": "a"
},
{
"from": 17,
"fromPort": "out",
"to": 16,
"toPort": "b"
},
{
"from": 18,
"fromPort": "out",
"to": 19,
"toPort": "x"
},
{
"from": 16,
"fromPort": "out",
"to": 19,
"toPort": "y"
},
{
"from": 20,
"fromPort": "out",
"to": 19,
"toPort": "z"
},
{
"from": 21,
"fromPort": "out",
"to": 22,
"toPort": "v"
},
{
"from": 23,
"fromPort": "out",
"to": 22,
"toPort": "s"
},
{
"from": 22,
"fromPort": "out",
"to": 24,
"toPort": "a"
},
{
"from": 19,
"fromPort": "out",
"to": 24,
"toPort": "b"
},
{
"from": 24,
"fromPort": "out",
"to": 26,
"toPort": "a"
},
{
"from": 25,
"fromPort": "out",
"to": 26,
"toPort": "b"
},
{
"from": 26,
"fromPort": "out",
"to": 27,
"toPort": "p"
},
{
"from": 24,
"fromPort": "out",
"to": 29,
"toPort": "a"
},
{
"from": 28,
"fromPort": "out",
"to": 29,
"toPort": "b"
},
{
"from": 29,
"fromPort": "out",
"to": 30,
"toPort": "p"
},
{
"from": 24,
"fromPort": "out",
"to": 32,
"toPort": "a"
},
{
"from": 31,
"fromPort": "out",
"to": 32,
"toPort": "b"
},
{
"from": 32,
"fromPort": "out",
"to": 33,
"toPort": "p"
},
{
"from": 27,
"fromPort": "out",
"to": 34,
"toPort": "x"
},
{
"from": 30,
"fromPort": "out",
"to": 34,
"toPort": "y"
},
{
"from": 33,
"fromPort": "out",
"to": 34,
"toPort": "z"
},
{
"from": 34,
"fromPort": "out",
"to": 35,
"toPort": "v"
},
{
"from": 36,
"fromPort": "out",
"to": 35,
"toPort": "s"
},
{
"from": 14,
"fromPort": "out",
"to": 37,
"toPort": "a"
},
{
"from": 35,
"fromPort": "out",
"to": 37,
"toPort": "b"
},
{
"from": 37,
"fromPort": "out",
"to": 39,
"toPort": "v"
},
{
"from": 0,
"fromPort": "out",
"to": 39,
"toPort": "s"
},
{
"from": 38,
"fromPort": "out",
"to": 40,
"toPort": "a"
},
{
"from": 39,
"fromPort": "out",
"to": 40,
"toPort": "b"
},
{
"from": 40,
"fromPort": "out",
"to": 41,
"toPort": "value"
},
{
"from": 66,
"fromPort": "out",
"to": 43,
"toPort": "v"
},
{
"from": 0,
"fromPort": "out",
"to": 43,
"toPort": "s"
},
{
"from": 42,
"fromPort": "out",
"to": 44,
"toPort": "a"
},
{
"from": 43,
"fromPort": "out",
"to": 44,
"toPort": "b"
},
{
"from": 44,
"fromPort": "out",
"to": 45,
"toPort": "value"
},
{
"from": 5,
"fromPort": "out",
"to": 47,
"toPort": "t"
},
{
"from": 46,
"fromPort": "out",
"to": 48,
"toPort": "a"
},
{
"from": 47,
"fromPort": "out",
"to": 48,
"toPort": "b"
},
{
"from": 48,
"fromPort": "out",
"to": 49,
"toPort": "value"
},
{
"from": 50,
"fromPort": "out",
"to": 51,
"toPort": "a"
},
{
"from": 52,
"fromPort": "out",
"to": 51,
"toPort": "b"
},
{
"from": 51,
"fromPort": "out",
"to": 54,
"toPort": "a"
},
{
"from": 53,
"fromPort": "out",
"to": 54,
"toPort": "b"
},
{
"from": 54,
"fromPort": "out",
"to": 55,
"toPort": "x"
},
{
"from": 55,
"fromPort": "out",
"to": 56,
"toPort": "a"
},
{
"from": 55,
"fromPort": "out",
"to": 56,
"toPort": "b"
},
{
"from": 56,
"fromPort": "out",
"to": 57,
"toPort": "a"
},
{
"from": 58,
"fromPort": "out",
"to": 57,
"toPort": "b"
},
{
"from": 59,
"fromPort": "out",
"to": 57,
"toPort": "c"
},
{
"from": 5,
"fromPort": "out",
"to": 60,
"toPort": "t"
},
{
"from": 60,
"fromPort": "out",
"to": 61,
"toPort": "a"
},
{
"from": 57,
"fromPort": "out",
"to": 61,
"toPort": "b"
},
{
"from": 61,
"fromPort": "out",
"to": 62,
"toPort": "value"
},
{
"from": 42,
"fromPort": "out",
"to": 63,
"toPort": "position"
},
{
"from": 63,
"fromPort": "out",
"to": 65,
"toPort": "v"
},
{
"from": 64,
"fromPort": "out",
"to": 65,
"toPort": "s"
},
{
"from": 40,
"fromPort": "out",
"to": 66,
"toPort": "a"
},
{
"from": 65,
"fromPort": "out",
"to": 66,
"toPort": "b"
}
]
},
"emitter": {
"spawn_rate": 42,
"prewarm": 2,
"blend": 1,
"projection": 0,
"lighting": false,
"soft_depth": false,
"sort": false,
"cull": false,
"texture": null,
"flipbook": [
1,
1
],
"render": {
"position": "position",
"size": "size",
"color": "color"
}
},
"source": "graph",
"assembly": "",
"optimize": true
}
{
"name": "flame",
"attributes": [
{
"name": "position",
"components": 3
},
{
"name": "velocity",
"components": 3
},
{
"name": "age",
"components": 1
},
{
"name": "life",
"components": 1
},
{
"name": "size",
"components": 1
},
{
"name": "size0",
"components": 1
},
{
"name": "color",
"components": 4
},
{
"name": "spin",
"components": 1
},
{
"name": "seed",
"components": 1
},
{
"name": "orientation",
"components": 4
}
],
"groups": [],
"init": {
"nodes": [
{
"id": 0,
"type": "sqrt",
"parameters": {},
"x": 989,
"y": 472
},
{
"id": 1,
"type": "random",
"parameters": {
"components": 1
},
"x": 700,
"y": 472
},
{
"id": 2,
"type": "scale",
"parameters": {},
"x": 1283.5,
"y": 432.5
},
{
"id": 3,
"type": "mul",
"parameters": {},
"x": 440.5,
"y": 376
},
{
"id": 4,
"type": "random",
"parameters": {
"components": 1
},
"x": 63.5,
"y": 262
},
{
"id": 5,
"type": "const",
"parameters": {
"value": [
6.283185307179586
]
},
"x": 61,
"y": 393
},
{
"id": 6,
"type": "cos",
"parameters": {},
"x": 731.5,
"y": 219.25
},
{
"id": 7,
"type": "sin",
"parameters": {},
"x": 734.5,
"y": 376
},
{
"id": 8,
"type": "vec2",
"parameters": {},
"x": 984,
"y": 359
},
{
"id": 9,
"type": "scale",
"parameters": {},
"x": 1536,
"y": 480.5
},
{
"id": 10,
"type": "const",
"parameters": {
"value": [
0.34
]
},
"x": 1247,
"y": 545.5
},
{
"id": 11,
"type": "swizzle",
"parameters": {
"pattern": [
0
]
},
"x": 1769,
"y": 219.25
},
{
"id": 12,
"type": "swizzle",
"parameters": {
"pattern": [
1
]
},
"x": 1769,
"y": 544
},
{
"id": 13,
"type": "random",
"parameters": {
"components": 1
},
"x": 63.5,
"y": 0
},
{
"id": 14,
"type": "mul",
"parameters": {},
"x": 440.5,
"y": 114
},
{
"id": 15,
"type": "const",
"parameters": {
"value": [
0.18
]
},
"x": 61,
"y": 131
},
{
"id": 16,
"type": "vec3",
"parameters": {},
"x": 2065,
"y": 219.25
},
{
"id": 17,
"type": "builtin",
"parameters": {
"id": 2
},
"x": 2041,
"y": 87.75
},
{
"id": 18,
"type": "add",
"parameters": {},
"x": 2428.5,
"y": 144.75
},
{
"id": 19,
"type": "setAttribute",
"parameters": {
"name": "position"
},
"x": 2722,
"y": 144.75
},
{
"id": 20,
"type": "vec3",
"parameters": {},
"x": 2065,
"y": 463.5
},
{
"id": 21,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 1752,
"y": 671
},
{
"id": 22,
"type": "scale",
"parameters": {},
"x": 2429,
"y": 520
},
{
"id": 23,
"type": "const",
"parameters": {
"value": [
1.6
]
},
"x": 2052,
"y": 593.5
},
{
"id": 24,
"type": "random",
"parameters": {
"components": 3
},
"x": 63.5,
"y": 655
},
{
"id": 25,
"type": "mad",
"parameters": {},
"x": 427,
"y": 786
},
{
"id": 26,
"type": "const",
"parameters": {
"value": [
2
]
},
"x": 61,
"y": 786
},
{
"id": 27,
"type": "const",
"parameters": {
"value": [
-1
]
},
"x": 61,
"y": 917
},
{
"id": 28,
"type": "scale",
"parameters": {},
"x": 734,
"y": 842.5
},
{
"id": 29,
"type": "const",
"parameters": {
"value": [
0.45
]
},
"x": 411,
"y": 916
},
{
"id": 30,
"type": "const",
"parameters": {
"value": [
0,
1.5,
0
]
},
"x": 2352,
"y": 266.75
},
{
"id": 31,
"type": "add",
"parameters": {},
"x": 2754.5,
"y": 437
},
{
"id": 32,
"type": "add",
"parameters": {},
"x": 3004,
"y": 563
},
{
"id": 33,
"type": "setAttribute",
"parameters": {
"name": "velocity"
},
"x": 3221,
"y": 563
},
{
"id": 34,
"type": "setAttribute",
"parameters": {
"name": "age"
},
"x": 414.5,
"y": 524
},
{
"id": 35,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 61,
"y": 524
},
{
"id": 36,
"type": "random",
"parameters": {
"components": 1
},
"x": 63.5,
"y": 1048
},
{
"id": 37,
"type": "mad",
"parameters": {},
"x": 427,
"y": 1179
},
{
"id": 38,
"type": "const",
"parameters": {
"value": [
0.5
]
},
"x": 61,
"y": 1179
},
{
"id": 39,
"type": "const",
"parameters": {
"value": [
0.5
]
},
"x": 61,
"y": 1310
},
{
"id": 40,
"type": "setAttribute",
"parameters": {
"name": "life"
},
"x": 701,
"y": 1179
},
{
"id": 41,
"type": "random",
"parameters": {
"components": 1
},
"x": 63.5,
"y": 1441
},
{
"id": 42,
"type": "mad",
"parameters": {},
"x": 427,
"y": 1572
},
{
"id": 43,
"type": "const",
"parameters": {
"value": [
0.4
]
},
"x": 61,
"y": 1572
},
{
"id": 44,
"type": "const",
"parameters": {
"value": [
0.1
]
},
"x": 61,
"y": 1703
},
{
"id": 45,
"type": "setAttribute",
"parameters": {
"name": "size0"
},
"x": 701,
"y": 1481
},
{
"id": 46,
"type": "setAttribute",
"parameters": {
"name": "size"
},
"x": 701,
"y": 1677.5
},
{
"id": 47,
"type": "setAttribute",
"parameters": {
"name": "color"
},
"x": 414.5,
"y": 1834
},
{
"id": 48,
"type": "const",
"parameters": {
"value": [
1,
1,
1,
0
]
},
"x": 0,
"y": 1834
},
{
"id": 49,
"type": "random",
"parameters": {
"components": 1
},
"x": 63.5,
"y": 1965
},
{
"id": 50,
"type": "mad",
"parameters": {},
"x": 427,
"y": 2096
},
{
"id": 51,
"type": "const",
"parameters": {
"value": [
6.28
]
},
"x": 61,
"y": 2096
},
{
"id": 52,
"type": "const",
"parameters": {
"value": [
-3.14
]
},
"x": 61,
"y": 2227
},
{
"id": 53,
"type": "setAttribute",
"parameters": {
"name": "spin"
},
"x": 701,
"y": 2096
},
{
"id": 54,
"type": "random",
"parameters": {
"components": 1
},
"x": 63.5,
"y": 2358
},
{
"id": 55,
"type": "mad",
"parameters": {},
"x": 427,
"y": 2489
},
{
"id": 56,
"type": "const",
"parameters": {
"value": [
64
]
},
"x": 61,
"y": 2489
},
{
"id": 57,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 61,
"y": 2620
},
{
"id": 58,
"type": "setAttribute",
"parameters": {
"name": "seed"
},
"x": 701,
"y": 2489
},
{
"id": 59,
"type": "const",
"parameters": {
"value": [
0,
0,
1
]
},
"x": 201,
"y": 2760
},
{
"id": 60,
"type": "quatFromAxisAngle",
"parameters": {},
"x": 451,
"y": 2740
},
{
"id": 61,
"type": "setAttribute",
"parameters": {
"name": "orientation"
},
"x": 701,
"y": 2740
}
],
"connections": [
{
"from": 1,
"fromPort": "out",
"to": 0,
"toPort": "x"
},
{
"from": 4,
"fromPort": "out",
"to": 3,
"toPort": "a"
},
{
"from": 5,
"fromPort": "out",
"to": 3,
"toPort": "b"
},
{
"from": 3,
"fromPort": "out",
"to": 6,
"toPort": "x"
},
{
"from": 3,
"fromPort": "out",
"to": 7,
"toPort": "x"
},
{
"from": 6,
"fromPort": "out",
"to": 8,
"toPort": "x"
},
{
"from": 7,
"fromPort": "out",
"to": 8,
"toPort": "y"
},
{
"from": 8,
"fromPort": "out",
"to": 2,
"toPort": "v"
},
{
"from": 0,
"fromPort": "out",
"to": 2,
"toPort": "s"
},
{
"from": 2,
"fromPort": "out",
"to": 9,
"toPort": "v"
},
{
"from": 10,
"fromPort": "out",
"to": 9,
"toPort": "s"
},
{
"from": 9,
"fromPort": "out",
"to": 11,
"toPort": "x"
},
{
"from": 9,
"fromPort": "out",
"to": 12,
"toPort": "x"
},
{
"from": 13,
"fromPort": "out",
"to": 14,
"toPort": "a"
},
{
"from": 15,
"fromPort": "out",
"to": 14,
"toPort": "b"
},
{
"from": 11,
"fromPort": "out",
"to": 16,
"toPort": "x"
},
{
"from": 14,
"fromPort": "out",
"to": 16,
"toPort": "y"
},
{
"from": 12,
"fromPort": "out",
"to": 16,
"toPort": "z"
},
{
"from": 17,
"fromPort": "out",
"to": 18,
"toPort": "a"
},
{
"from": 16,
"fromPort": "out",
"to": 18,
"toPort": "b"
},
{
"from": 18,
"fromPort": "out",
"to": 19,
"toPort": "value"
},
{
"from": 11,
"fromPort": "out",
"to": 20,
"toPort": "x"
},
{
"from": 21,
"fromPort": "out",
"to": 20,
"toPort": "y"
},
{
"from": 12,
"fromPort": "out",
"to": 20,
"toPort": "z"
},
{
"from": 20,
"fromPort": "out",
"to": 22,
"toPort": "v"
},
{
"from": 23,
"fromPort": "out",
"to": 22,
"toPort": "s"
},
{
"from": 24,
"fromPort": "out",
"to": 25,
"toPort": "a"
},
{
"from": 26,
"fromPort": "out",
"to": 25,
"toPort": "b"
},
{
"from": 27,
"fromPort": "out",
"to": 25,
"toPort": "c"
},
{
"from": 25,
"fromPort": "out",
"to": 28,
"toPort": "v"
},
{
"from": 29,
"fromPort": "out",
"to": 28,
"toPort": "s"
},
{
"from": 30,
"fromPort": "out",
"to": 31,
"toPort": "a"
},
{
"from": 22,
"fromPort": "out",
"to": 31,
"toPort": "b"
},
{
"from": 31,
"fromPort": "out",
"to": 32,
"toPort": "a"
},
{
"from": 28,
"fromPort": "out",
"to": 32,
"toPort": "b"
},
{
"from": 32,
"fromPort": "out",
"to": 33,
"toPort": "value"
},
{
"from": 35,
"fromPort": "out",
"to": 34,
"toPort": "value"
},
{
"from": 36,
"fromPort": "out",
"to": 37,
"toPort": "a"
},
{
"from": 38,
"fromPort": "out",
"to": 37,
"toPort": "b"
},
{
"from": 39,
"fromPort": "out",
"to": 37,
"toPort": "c"
},
{
"from": 37,
"fromPort": "out",
"to": 40,
"toPort": "value"
},
{
"from": 41,
"fromPort": "out",
"to": 42,
"toPort": "a"
},
{
"from": 43,
"fromPort": "out",
"to": 42,
"toPort": "b"
},
{
"from": 44,
"fromPort": "out",
"to": 42,
"toPort": "c"
},
{
"from": 42,
"fromPort": "out",
"to": 45,
"toPort": "value"
},
{
"from": 42,
"fromPort": "out",
"to": 46,
"toPort": "value"
},
{
"from": 48,
"fromPort": "out",
"to": 47,
"toPort": "value"
},
{
"from": 49,
"fromPort": "out",
"to": 50,
"toPort": "a"
},
{
"from": 51,
"fromPort": "out",
"to": 50,
"toPort": "b"
},
{
"from": 52,
"fromPort": "out",
"to": 50,
"toPort": "c"
},
{
"from": 50,
"fromPort": "out",
"to": 53,
"toPort": "value"
},
{
"from": 54,
"fromPort": "out",
"to": 55,
"toPort": "a"
},
{
"from": 56,
"fromPort": "out",
"to": 55,
"toPort": "b"
},
{
"from": 57,
"fromPort": "out",
"to": 55,
"toPort": "c"
},
{
"from": 55,
"fromPort": "out",
"to": 58,
"toPort": "value"
},
{
"from": 59,
"fromPort": "out",
"to": 60,
"toPort": "axis"
},
{
"from": 50,
"fromPort": "out",
"to": 60,
"toPort": "angle"
},
{
"from": 60,
"fromPort": "quat",
"to": 61,
"toPort": "value"
}
]
},
"update": {
"nodes": [
{
"id": 0,
"type": "builtin",
"parameters": {
"id": 0
},
"x": 0,
"y": 1203
},
{
"id": 1,
"type": "attribute",
"parameters": {
"name": "age"
},
"x": 14.5,
"y": 1072
},
{
"id": 2,
"type": "add",
"parameters": {},
"x": 387.5,
"y": 1072
},
{
"id": 3,
"type": "setAttribute",
"parameters": {
"name": "age"
},
"x": 684.5,
"y": 1072
},
{
"id": 4,
"type": "attribute",
"parameters": {
"name": "life"
},
"x": 355,
"y": 1525
},
{
"id": 5,
"type": "div",
"parameters": {},
"x": 716,
"y": 1508
},
{
"id": 6,
"type": "compare",
"parameters": {
"op": "ge"
},
"x": 1054,
"y": 2165.75
},
{
"id": 7,
"type": "const",
"parameters": {
"value": [
1
]
},
"x": 681,
"y": 2182.75
},
{
"id": 8,
"type": "kill",
"parameters": {},
"x": 1389.5,
"y": 2165.75
},
{
"id": 9,
"type": "builtin",
"parameters": {
"id": 7
},
"x": 0,
"y": 262
},
{
"id": 10,
"type": "mul",
"parameters": {},
"x": 381,
"y": 319
},
{
"id": 11,
"type": "const",
"parameters": {
"value": [
-1.4
]
},
"x": 11,
"y": 393
},
{
"id": 12,
"type": "attribute",
"parameters": {
"name": "seed"
},
"x": 355,
"y": 170
},
{
"id": 13,
"type": "vec3",
"parameters": {},
"x": 694,
"y": 301.75
},
{
"id": 14,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 351.5,
"y": 441
},
{
"id": 15,
"type": "attribute",
"parameters": {
"name": "position"
},
"x": 14.5,
"y": 0
},
{
"id": 16,
"type": "scale",
"parameters": {},
"x": 388,
"y": 57
},
{
"id": 17,
"type": "const",
"parameters": {
"value": [
3.4
]
},
"x": 11,
"y": 131
},
{
"id": 18,
"type": "add",
"parameters": {},
"x": 1062.5,
"y": 170.75
},
{
"id": 19,
"type": "const",
"parameters": {
"value": [
0,
0,
0
]
},
"x": 986,
"y": 39.75
},
{
"id": 20,
"type": "add",
"parameters": {},
"x": 1404.5,
"y": 80
},
{
"id": 21,
"type": "simplex3",
"parameters": {},
"x": 1654,
"y": 80
},
{
"id": 22,
"type": "const",
"parameters": {
"value": [
17.3,
5.1,
31.7
]
},
"x": 986,
"y": 292.25
},
{
"id": 23,
"type": "add",
"parameters": {},
"x": 1404.5,
"y": 227.25
},
{
"id": 24,
"type": "simplex3",
"parameters": {},
"x": 1654,
"y": 227.25
},
{
"id": 25,
"type": "const",
"parameters": {
"value": [
73.9,
41.3,
11.5
]
},
"x": 986,
"y": 423.25
},
{
"id": 26,
"type": "add",
"parameters": {},
"x": 1404.5,
"y": 406.25
},
{
"id": 27,
"type": "simplex3",
"parameters": {},
"x": 1654,
"y": 406.25
},
{
"id": 28,
"type": "vec3",
"parameters": {},
"x": 1939,
"y": 210.25
},
{
"id": 29,
"type": "mul",
"parameters": {},
"x": 1056,
"y": 810
},
{
"id": 30,
"type": "const",
"parameters": {
"value": [
9
]
},
"x": 681,
"y": 810
},
{
"id": 31,
"type": "scale",
"parameters": {},
"x": 2279,
"y": 822.25
},
{
"id": 32,
"type": "attribute",
"parameters": {
"name": "position"
},
"x": 14.5,
"y": 524
},
{
"id": 33,
"type": "builtin",
"parameters": {
"id": 2
},
"x": 0,
"y": 655
},
{
"id": 34,
"type": "sub",
"parameters": {},
"x": 380.5,
"y": 581
},
{
"id": 35,
"type": "mul",
"parameters": {},
"x": 710.5,
"y": 628.75
},
{
"id": 36,
"type": "const",
"parameters": {
"value": [
1,
0,
1
]
},
"x": 311,
"y": 711
},
{
"id": 37,
"type": "mul",
"parameters": {},
"x": 1056,
"y": 941
},
{
"id": 38,
"type": "const",
"parameters": {
"value": [
-1.7
]
},
"x": 681,
"y": 941
},
{
"id": 39,
"type": "scale",
"parameters": {},
"x": 1405,
"y": 628.75
},
{
"id": 40,
"type": "const",
"parameters": {
"value": [
0,
4.2,
0
]
},
"x": 2202,
"y": 691.25
},
{
"id": 41,
"type": "add",
"parameters": {},
"x": 2572,
"y": 748.25
},
{
"id": 42,
"type": "add",
"parameters": {},
"x": 2789,
"y": 708.5
},
{
"id": 43,
"type": "attribute",
"parameters": {
"name": "velocity"
},
"x": 3006,
"y": 1282.25
},
{
"id": 44,
"type": "scale",
"parameters": {},
"x": 3039,
"y": 1413.25
},
{
"id": 45,
"type": "add",
"parameters": {},
"x": 3288,
"y": 1339.25
},
{
"id": 46,
"type": "setAttribute",
"parameters": {
"name": "velocity"
},
"x": 3505,
"y": 1339.25
},
{
"id": 47,
"type": "attribute",
"parameters": {
"name": "position"
},
"x": 3505,
"y": 1055
},
{
"id": 48,
"type": "scale",
"parameters": {},
"x": 3538,
"y": 1186
},
{
"id": 49,
"type": "add",
"parameters": {},
"x": 3787,
"y": 1112
},
{
"id": 50,
"type": "setAttribute",
"parameters": {
"name": "position"
},
"x": 4004,
"y": 1112
},
{
"id": 51,
"type": "attribute",
"parameters": {
"name": "size0"
},
"x": 1030,
"y": 1690.75
},
{
"id": 52,
"type": "curveLiteral",
"parameters": {
"curve": {
"keys": [
{
"time": 0,
"value": 0.662916438992136,
"inTangent": 2.2471691826185145,
"outTangent": 1.2269055440364782
},
{
"time": 0.2192864853655495,
"value": 0.9319602436194028,
"inTangent": 0,
"outTangent": 0
},
{
"time": 0.44971337926304744,
"value": 0.9319602436194028,
"inTangent": 0.061155585117630966,
"outTangent": 0.061155585117630966
},
{
"time": 0.6960317830845106,
"value": 0.961115881253485,
"inTangent": 0,
"outTangent": 0
},
{
"time": 1,
"value": 0.010069238351883891,
"inTangent": -3.1287700159981378,
"outTangent": -2.46508031364683
}
]
}
},
"x": 970,
"y": 1821.75
},
{
"id": 53,
"type": "mul",
"parameters": {},
"x": 1398,
"y": 1747.75
},
{
"id": 54,
"type": "setAttribute",
"parameters": {
"name": "size"
},
"x": 1655.5,
"y": 1747.75
},
{
"id": 55,
"type": "gradientRGBA",
"parameters": {
"stops": [
{
"position": 0,
"color": [
1,
0.95,
0.8,
0
]
},
{
"position": 0.06,
"color": [
1,
0.8,
0.45,
0.2
]
},
{
"position": 0.28,
"color": [
1,
0.52,
0.14,
0.16
]
},
{
"position": 0.62,
"color": [
0.9,
0.22,
0.03,
0.08
]
},
{
"position": 1,
"color": [
0.35,
0.04,
0.01,
0
]
}
]
},
"x": 970,
"y": 1492.75
},
{
"id": 56,
"type": "setAttribute",
"parameters": {
"name": "color"
},
"x": 1372,
"y": 1492.75
},
{
"id": 57,
"type": "wind",
"parameters": {},
"x": 3113,
"y": 1599.25
},
{
"id": 58,
"type": "const",
"parameters": {
"value": [
0.5
]
},
"x": 3113,
"y": 1719.25
},
{
"id": 59,
"type": "scale",
"parameters": {},
"x": 3413,
"y": 1599.25
},
{
"id": 60,
"type": "add",
"parameters": {},
"x": 3673,
"y": 1539.25
}
],
"connections": [
{
"from": 1,
"fromPort": "out",
"to": 2,
"toPort": "a"
},
{
"from": 0,
"fromPort": "out",
"to": 2,
"toPort": "b"
},
{
"from": 2,
"fromPort": "out",
"to": 3,
"toPort": "value"
},
{
"from": 2,
"fromPort": "out",
"to": 5,
"toPort": "a"
},
{
"from": 4,
"fromPort": "out",
"to": 5,
"toPort": "b"
},
{
"from": 5,
"fromPort": "out",
"to": 6,
"toPort": "a"
},
{
"from": 7,
"fromPort": "out",
"to": 6,
"toPort": "b"
},
{
"from": 6,
"fromPort": "out",
"to": 8,
"toPort": "condition"
},
{
"from": 9,
"fromPort": "out",
"to": 10,
"toPort": "a"
},
{
"from": 11,
"fromPort": "out",
"to": 10,
"toPort": "b"
},
{
"from": 12,
"fromPort": "out",
"to": 13,
"toPort": "x"
},
{
"from": 10,
"fromPort": "out",
"to": 13,
"toPort": "y"
},
{
"from": 14,
"fromPort": "out",
"to": 13,
"toPort": "z"
},
{
"from": 15,
"fromPort": "out",
"to": 16,
"toPort": "v"
},
{
"from": 17,
"fromPort": "out",
"to": 16,
"toPort": "s"
},
{
"from": 16,
"fromPort": "out",
"to": 18,
"toPort": "a"
},
{
"from": 13,
"fromPort": "out",
"to": 18,
"toPort": "b"
},
{
"from": 18,
"fromPort": "out",
"to": 20,
"toPort": "a"
},
{
"from": 19,
"fromPort": "out",
"to": 20,
"toPort": "b"
},
{
"from": 20,
"fromPort": "out",
"to": 21,
"toPort": "p"
},
{
"from": 18,
"fromPort": "out",
"to": 23,
"toPort": "a"
},
{
"from": 22,
"fromPort": "out",
"to": 23,
"toPort": "b"
},
{
"from": 23,
"fromPort": "out",
"to": 24,
"toPort": "p"
},
{
"from": 18,
"fromPort": "out",
"to": 26,
"toPort": "a"
},
{
"from": 25,
"fromPort": "out",
"to": 26,
"toPort": "b"
},
{
"from": 26,
"fromPort": "out",
"to": 27,
"toPort": "p"
},
{
"from": 21,
"fromPort": "out",
"to": 28,
"toPort": "x"
},
{
"from": 24,
"fromPort": "out",
"to": 28,
"toPort": "y"
},
{
"from": 27,
"fromPort": "out",
"to": 28,
"toPort": "z"
},
{
"from": 30,
"fromPort": "out",
"to": 29,
"toPort": "a"
},
{
"from": 5,
"fromPort": "out",
"to": 29,
"toPort": "b"
},
{
"from": 28,
"fromPort": "out",
"to": 31,
"toPort": "v"
},
{
"from": 29,
"fromPort": "out",
"to": 31,
"toPort": "s"
},
{
"from": 32,
"fromPort": "out",
"to": 34,
"toPort": "a"
},
{
"from": 33,
"fromPort": "out",
"to": 34,
"toPort": "b"
},
{
"from": 34,
"fromPort": "out",
"to": 35,
"toPort": "a"
},
{
"from": 36,
"fromPort": "out",
"to": 35,
"toPort": "b"
},
{
"from": 38,
"fromPort": "out",
"to": 37,
"toPort": "a"
},
{
"from": 5,
"fromPort": "out",
"to": 37,
"toPort": "b"
},
{
"from": 35,
"fromPort": "out",
"to": 39,
"toPort": "v"
},
{
"from": 37,
"fromPort": "out",
"to": 39,
"toPort": "s"
},
{
"from": 40,
"fromPort": "out",
"to": 41,
"toPort": "a"
},
{
"from": 31,
"fromPort": "out",
"to": 41,
"toPort": "b"
},
{
"from": 41,
"fromPort": "out",
"to": 42,
"toPort": "a"
},
{
"from": 39,
"fromPort": "out",
"to": 42,
"toPort": "b"
},
{
"from": 42,
"fromPort": "out",
"to": 44,
"toPort": "v"
},
{
"from": 0,
"fromPort": "out",
"to": 44,
"toPort": "s"
},
{
"from": 43,
"fromPort": "out",
"to": 45,
"toPort": "a"
},
{
"from": 44,
"fromPort": "out",
"to": 45,
"toPort": "b"
},
{
"from": 45,
"fromPort": "out",
"to": 46,
"toPort": "value"
},
{
"from": 60,
"fromPort": "out",
"to": 48,
"toPort": "v"
},
{
"from": 0,
"fromPort": "out",
"to": 48,
"toPort": "s"
},
{
"from": 47,
"fromPort": "out",
"to": 49,
"toPort": "a"
},
{
"from": 48,
"fromPort": "out",
"to": 49,
"toPort": "b"
},
{
"from": 49,
"fromPort": "out",
"to": 50,
"toPort": "value"
},
{
"from": 5,
"fromPort": "out",
"to": 52,
"toPort": "t"
},
{
"from": 51,
"fromPort": "out",
"to": 53,
"toPort": "a"
},
{
"from": 52,
"fromPort": "out",
"to": 53,
"toPort": "b"
},
{
"from": 53,
"fromPort": "out",
"to": 54,
"toPort": "value"
},
{
"from": 5,
"fromPort": "out",
"to": 55,
"toPort": "t"
},
{
"from": 55,
"fromPort": "out",
"to": 56,
"toPort": "value"
},
{
"from": 47,
"fromPort": "out",
"to": 57,
"toPort": "position"
},
{
"from": 57,
"fromPort": "out",
"to": 59,
"toPort": "v"
},
{
"from": 58,
"fromPort": "out",
"to": 59,
"toPort": "s"
},
{
"from": 45,
"fromPort": "out",
"to": 60,
"toPort": "a"
},
{
"from": 59,
"fromPort": "out",
"to": 60,
"toPort": "b"
}
]
},
"emitter": {
"spawn_rate": 560,
"prewarm": 1.2,
"blend": 1,
"projection": 0,
"lighting": false,
"soft_depth": true,
"sort": false,
"cull": false,
"texture": null,
"flipbook": [
1,
1
],
"render": {
"position": "position",
"size": "size",
"color": "color",
"orientation": "orientation"
}
},
"source": "graph",
"assembly": "",
"optimize": true
}
{
"name": "smoke",
"attributes": [
{
"name": "position",
"components": 3
},
{
"name": "velocity",
"components": 3
},
{
"name": "age",
"components": 1
},
{
"name": "life",
"components": 1
},
{
"name": "size",
"components": 1
},
{
"name": "size0",
"components": 1
},
{
"name": "color",
"components": 4
},
{
"name": "spin",
"components": 1
},
{
"name": "spin_rate",
"components": 1
},
{
"name": "seed",
"components": 1
},
{
"name": "orientation",
"components": 4
}
],
"groups": [],
"init": {
"nodes": [
{
"id": 0,
"type": "sqrt",
"parameters": {},
"x": 705,
"y": 457.5
},
{
"id": 1,
"type": "random",
"parameters": {
"components": 1
},
"x": 477.5,
"y": 457.5
},
{
"id": 2,
"type": "scale",
"parameters": {},
"x": 940,
"y": 358
},
{
"id": 3,
"type": "mul",
"parameters": {},
"x": 220,
"y": 316.5
},
{
"id": 4,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 277.5
},
{
"id": 5,
"type": "const",
"parameters": {
"value": [
6.283185307179586
]
},
"x": 3.75,
"y": 371.5
},
{
"id": 6,
"type": "cos",
"parameters": {},
"x": 470,
"y": 206
},
{
"id": 7,
"type": "sin",
"parameters": {},
"x": 470,
"y": 363.5
},
{
"id": 8,
"type": "vec2",
"parameters": {},
"x": 705,
"y": 308.5
},
{
"id": 9,
"type": "scale",
"parameters": {},
"x": 1175,
"y": 452
},
{
"id": 10,
"type": "const",
"parameters": {
"value": [
0.34
]
},
"x": 951.25,
"y": 515
},
{
"id": 11,
"type": "swizzle",
"parameters": {
"pattern": [
0
]
},
"x": 1395,
"y": 36.5
},
{
"id": 12,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 81.5
},
{
"id": 13,
"type": "mad",
"parameters": {},
"x": 220,
"y": 73.5
},
{
"id": 14,
"type": "const",
"parameters": {
"value": [
0.35
]
},
"x": 3.75,
"y": 175.5
},
{
"id": 15,
"type": "const",
"parameters": {
"value": [
0.9
]
},
"x": 3.75,
"y": 183.5
},
{
"id": 16,
"type": "swizzle",
"parameters": {
"pattern": [
1
]
},
"x": 1395,
"y": 452
},
{
"id": 17,
"type": "vec3",
"parameters": {},
"x": 1615,
"y": 94
},
{
"id": 18,
"type": "builtin",
"parameters": {
"id": 2
},
"x": 1618.75,
"y": 0
},
{
"id": 19,
"type": "add",
"parameters": {},
"x": 1835,
"y": 2.5
},
{
"id": 20,
"type": "setAttribute",
"parameters": {
"name": "position"
},
"x": 2055,
"y": 2.5
},
{
"id": 21,
"type": "const",
"parameters": {
"value": [
0,
1.35,
0
]
},
"x": 481.25,
"y": 551.5
},
{
"id": 22,
"type": "random",
"parameters": {
"components": 3
},
"x": 0,
"y": 559.5
},
{
"id": 23,
"type": "mad",
"parameters": {},
"x": 220,
"y": 645.5
},
{
"id": 24,
"type": "const",
"parameters": {
"value": [
2
]
},
"x": 3.75,
"y": 661.5
},
{
"id": 25,
"type": "const",
"parameters": {
"value": [
-1
]
},
"x": 3.75,
"y": 755.5
},
{
"id": 26,
"type": "scale",
"parameters": {},
"x": 470,
"y": 711
},
{
"id": 27,
"type": "const",
"parameters": {
"value": [
0.22
]
},
"x": 231.25,
"y": 771.5
},
{
"id": 28,
"type": "add",
"parameters": {},
"x": 705,
"y": 695
},
{
"id": 29,
"type": "setAttribute",
"parameters": {
"name": "velocity"
},
"x": 925,
"y": 695
},
{
"id": 30,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 959.5
},
{
"id": 31,
"type": "mad",
"parameters": {},
"x": 220,
"y": 1076.5
},
{
"id": 32,
"type": "const",
"parameters": {
"value": [
0.25000000000000006
]
},
"x": 3.75,
"y": 1053.5
},
{
"id": 33,
"type": "const",
"parameters": {
"value": [
0.3
]
},
"x": 3.75,
"y": 1147.5
},
{
"id": 34,
"type": "setAttribute",
"parameters": {
"name": "age"
},
"x": 205,
"y": 465.5
},
{
"id": 35,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 3.75,
"y": 465.5
},
{
"id": 36,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 1241.5
},
{
"id": 37,
"type": "mad",
"parameters": {},
"x": 220,
"y": 1358.5
},
{
"id": 38,
"type": "const",
"parameters": {
"value": [
2.1999999999999997
]
},
"x": 3.75,
"y": 1335.5
},
{
"id": 39,
"type": "const",
"parameters": {
"value": [
3.4
]
},
"x": 3.75,
"y": 1429.5
},
{
"id": 40,
"type": "setAttribute",
"parameters": {
"name": "life"
},
"x": 455,
"y": 1358.5
},
{
"id": 41,
"type": "setAttribute",
"parameters": {
"name": "size0"
},
"x": 455,
"y": 951.5
},
{
"id": 42,
"type": "setAttribute",
"parameters": {
"name": "size"
},
"x": 455,
"y": 1123.5
},
{
"id": 43,
"type": "setAttribute",
"parameters": {
"name": "color"
},
"x": 205,
"y": 865.5
},
{
"id": 44,
"type": "const",
"parameters": {
"value": [
1,
1,
1,
0
]
},
"x": 3.75,
"y": 865.5
},
{
"id": 45,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 1523.5
},
{
"id": 46,
"type": "mad",
"parameters": {},
"x": 220,
"y": 1640.5
},
{
"id": 47,
"type": "const",
"parameters": {
"value": [
6.28
]
},
"x": 3.75,
"y": 1617.5
},
{
"id": 48,
"type": "const",
"parameters": {
"value": [
-3.14
]
},
"x": 3.75,
"y": 1711.5
},
{
"id": 49,
"type": "setAttribute",
"parameters": {
"name": "spin"
},
"x": 455,
"y": 1640.5
},
{
"id": 50,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 1805.5
},
{
"id": 51,
"type": "mad",
"parameters": {},
"x": 220,
"y": 1922.5
},
{
"id": 52,
"type": "const",
"parameters": {
"value": [
1
]
},
"x": 3.75,
"y": 1899.5
},
{
"id": 53,
"type": "const",
"parameters": {
"value": [
-0.5
]
},
"x": 3.75,
"y": 1993.5
},
{
"id": 54,
"type": "setAttribute",
"parameters": {
"name": "spin_rate"
},
"x": 455,
"y": 1922.5
},
{
"id": 55,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 2087.5
},
{
"id": 56,
"type": "mad",
"parameters": {},
"x": 220,
"y": 2204.5
},
{
"id": 57,
"type": "const",
"parameters": {
"value": [
64
]
},
"x": 3.75,
"y": 2181.5
},
{
"id": 58,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 3.75,
"y": 2275.5
},
{
"id": 59,
"type": "setAttribute",
"parameters": {
"name": "seed"
},
"x": 455,
"y": 2204.5
},
{
"id": 60,
"type": "const",
"parameters": {
"value": [
0,
0,
1
]
},
"x": -45,
"y": 2415.5
},
{
"id": 61,
"type": "quatFromAxisAngle",
"parameters": {},
"x": 205,
"y": 2395.5
},
{
"id": 62,
"type": "setAttribute",
"parameters": {
"name": "orientation"
},
"x": 455,
"y": 2395.5
}
],
"connections": [
{
"from": 1,
"fromPort": "out",
"to": 0,
"toPort": "x"
},
{
"from": 4,
"fromPort": "out",
"to": 3,
"toPort": "a"
},
{
"from": 5,
"fromPort": "out",
"to": 3,
"toPort": "b"
},
{
"from": 3,
"fromPort": "out",
"to": 6,
"toPort": "x"
},
{
"from": 3,
"fromPort": "out",
"to": 7,
"toPort": "x"
},
{
"from": 6,
"fromPort": "out",
"to": 8,
"toPort": "x"
},
{
"from": 7,
"fromPort": "out",
"to": 8,
"toPort": "y"
},
{
"from": 8,
"fromPort": "out",
"to": 2,
"toPort": "v"
},
{
"from": 0,
"fromPort": "out",
"to": 2,
"toPort": "s"
},
{
"from": 2,
"fromPort": "out",
"to": 9,
"toPort": "v"
},
{
"from": 10,
"fromPort": "out",
"to": 9,
"toPort": "s"
},
{
"from": 9,
"fromPort": "out",
"to": 11,
"toPort": "x"
},
{
"from": 12,
"fromPort": "out",
"to": 13,
"toPort": "a"
},
{
"from": 14,
"fromPort": "out",
"to": 13,
"toPort": "b"
},
{
"from": 15,
"fromPort": "out",
"to": 13,
"toPort": "c"
},
{
"from": 9,
"fromPort": "out",
"to": 16,
"toPort": "x"
},
{
"from": 11,
"fromPort": "out",
"to": 17,
"toPort": "x"
},
{
"from": 13,
"fromPort": "out",
"to": 17,
"toPort": "y"
},
{
"from": 16,
"fromPort": "out",
"to": 17,
"toPort": "z"
},
{
"from": 18,
"fromPort": "out",
"to": 19,
"toPort": "a"
},
{
"from": 17,
"fromPort": "out",
"to": 19,
"toPort": "b"
},
{
"from": 19,
"fromPort": "out",
"to": 20,
"toPort": "value"
},
{
"from": 22,
"fromPort": "out",
"to": 23,
"toPort": "a"
},
{
"from": 24,
"fromPort": "out",
"to": 23,
"toPort": "b"
},
{
"from": 25,
"fromPort": "out",
"to": 23,
"toPort": "c"
},
{
"from": 23,
"fromPort": "out",
"to": 26,
"toPort": "v"
},
{
"from": 27,
"fromPort": "out",
"to": 26,
"toPort": "s"
},
{
"from": 21,
"fromPort": "out",
"to": 28,
"toPort": "a"
},
{
"from": 26,
"fromPort": "out",
"to": 28,
"toPort": "b"
},
{
"from": 28,
"fromPort": "out",
"to": 29,
"toPort": "value"
},
{
"from": 30,
"fromPort": "out",
"to": 31,
"toPort": "a"
},
{
"from": 32,
"fromPort": "out",
"to": 31,
"toPort": "b"
},
{
"from": 33,
"fromPort": "out",
"to": 31,
"toPort": "c"
},
{
"from": 35,
"fromPort": "out",
"to": 34,
"toPort": "value"
},
{
"from": 36,
"fromPort": "out",
"to": 37,
"toPort": "a"
},
{
"from": 38,
"fromPort": "out",
"to": 37,
"toPort": "b"
},
{
"from": 39,
"fromPort": "out",
"to": 37,
"toPort": "c"
},
{
"from": 37,
"fromPort": "out",
"to": 40,
"toPort": "value"
},
{
"from": 31,
"fromPort": "out",
"to": 41,
"toPort": "value"
},
{
"from": 31,
"fromPort": "out",
"to": 42,
"toPort": "value"
},
{
"from": 44,
"fromPort": "out",
"to": 43,
"toPort": "value"
},
{
"from": 45,
"fromPort": "out",
"to": 46,
"toPort": "a"
},
{
"from": 47,
"fromPort": "out",
"to": 46,
"toPort": "b"
},
{
"from": 48,
"fromPort": "out",
"to": 46,
"toPort": "c"
},
{
"from": 46,
"fromPort": "out",
"to": 49,
"toPort": "value"
},
{
"from": 50,
"fromPort": "out",
"to": 51,
"toPort": "a"
},
{
"from": 52,
"fromPort": "out",
"to": 51,
"toPort": "b"
},
{
"from": 53,
"fromPort": "out",
"to": 51,
"toPort": "c"
},
{
"from": 51,
"fromPort": "out",
"to": 54,
"toPort": "value"
},
{
"from": 55,
"fromPort": "out",
"to": 56,
"toPort": "a"
},
{
"from": 57,
"fromPort": "out",
"to": 56,
"toPort": "b"
},
{
"from": 58,
"fromPort": "out",
"to": 56,
"toPort": "c"
},
{
"from": 56,
"fromPort": "out",
"to": 59,
"toPort": "value"
},
{
"from": 60,
"fromPort": "out",
"to": 61,
"toPort": "axis"
},
{
"from": 46,
"fromPort": "out",
"to": 61,
"toPort": "angle"
},
{
"from": 61,
"fromPort": "quat",
"to": 62,
"toPort": "value"
}
]
},
"update": {
"nodes": [
{
"id": 0,
"type": "builtin",
"parameters": {
"id": 0
},
"x": 7.5,
"y": 595.5
},
{
"id": 1,
"type": "attribute",
"parameters": {
"name": "age"
},
"x": 0,
"y": 432
},
{
"id": 2,
"type": "add",
"parameters": {},
"x": 231.25,
"y": 432
},
{
"id": 3,
"type": "setAttribute",
"parameters": {
"name": "age"
},
"x": 455,
"y": 432
},
{
"id": 4,
"type": "attribute",
"parameters": {
"name": "life"
},
"x": 227.5,
"y": 758
},
{
"id": 5,
"type": "div",
"parameters": {},
"x": 470,
"y": 742
},
{
"id": 6,
"type": "compare",
"parameters": {
"op": "ge"
},
"x": 720,
"y": 571
},
{
"id": 7,
"type": "const",
"parameters": {
"value": [
1
]
},
"x": 481.25,
"y": 338
},
{
"id": 8,
"type": "kill",
"parameters": {},
"x": 970,
"y": 571
},
{
"id": 9,
"type": "builtin",
"parameters": {
"id": 7
},
"x": 7.5,
"y": 283
},
{
"id": 10,
"type": "mul",
"parameters": {},
"x": 231.25,
"y": 330
},
{
"id": 11,
"type": "const",
"parameters": {
"value": [
-0.3
]
},
"x": 15,
"y": 385
},
{
"id": 12,
"type": "attribute",
"parameters": {
"name": "seed"
},
"x": 227.5,
"y": 236
},
{
"id": 13,
"type": "vec3",
"parameters": {},
"x": 470,
"y": 212
},
{
"id": 14,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 242.5,
"y": 291
},
{
"id": 15,
"type": "attribute",
"parameters": {
"name": "position"
},
"x": 0,
"y": 87
},
{
"id": 16,
"type": "scale",
"parameters": {},
"x": 231.25,
"y": 126
},
{
"id": 17,
"type": "const",
"parameters": {
"value": [
0.95
]
},
"x": 15,
"y": 181
},
{
"id": 18,
"type": "add",
"parameters": {},
"x": 720,
"y": 110
},
{
"id": 19,
"type": "const",
"parameters": {
"value": [
0,
0,
0
]
},
"x": 731.25,
"y": 16
},
{
"id": 20,
"type": "add",
"parameters": {},
"x": 970,
"y": 0
},
{
"id": 21,
"type": "simplex3",
"parameters": {},
"x": 1220,
"y": 0
},
{
"id": 22,
"type": "const",
"parameters": {
"value": [
17.3,
5.1,
31.7
]
},
"x": 731.25,
"y": 220
},
{
"id": 23,
"type": "add",
"parameters": {},
"x": 970,
"y": 157
},
{
"id": 24,
"type": "simplex3",
"parameters": {},
"x": 1220,
"y": 157
},
{
"id": 25,
"type": "const",
"parameters": {
"value": [
73.9,
41.3,
11.5
]
},
"x": 731.25,
"y": 322
},
{
"id": 26,
"type": "add",
"parameters": {},
"x": 970,
"y": 306
},
{
"id": 27,
"type": "simplex3",
"parameters": {},
"x": 1220,
"y": 306
},
{
"id": 28,
"type": "vec3",
"parameters": {},
"x": 1455,
"y": 94
},
{
"id": 29,
"type": "scale",
"parameters": {},
"x": 1675,
"y": 130.5
},
{
"id": 30,
"type": "const",
"parameters": {
"value": [
1.7
]
},
"x": 1466.25,
"y": 220
},
{
"id": 31,
"type": "const",
"parameters": {
"value": [
0.28,
0.1,
-0.12
]
},
"x": 1686.25,
"y": 36.5
},
{
"id": 32,
"type": "add",
"parameters": {},
"x": 1895,
"y": 75.5
},
{
"id": 33,
"type": "attribute",
"parameters": {
"name": "velocity"
},
"x": 2115,
"y": 571
},
{
"id": 34,
"type": "scale",
"parameters": {},
"x": 2118.75,
"y": 665
},
{
"id": 35,
"type": "add",
"parameters": {},
"x": 2342.5,
"y": 610
},
{
"id": 37,
"type": "mul",
"parameters": {},
"x": 231.25,
"y": 1009
},
{
"id": 38,
"type": "const",
"parameters": {
"value": [
0.55
]
},
"x": 15,
"y": 1009
},
{
"id": 39,
"type": "saturate",
"parameters": {},
"x": 2342.5,
"y": 1009
},
{
"id": 40,
"type": "mix",
"parameters": {},
"x": 2562.5,
"y": 763
},
{
"id": 41,
"type": "setAttribute",
"parameters": {
"name": "velocity"
},
"x": 2782.5,
"y": 763
},
{
"id": 42,
"type": "attribute",
"parameters": {
"name": "position"
},
"x": 2793.75,
"y": 400
},
{
"id": 43,
"type": "scale",
"parameters": {},
"x": 2797.5,
"y": 494
},
{
"id": 44,
"type": "add",
"parameters": {},
"x": 3032.5,
"y": 439
},
{
"id": 45,
"type": "setAttribute",
"parameters": {
"name": "position"
},
"x": 3252.5,
"y": 439
},
{
"id": 46,
"type": "attribute",
"parameters": {
"name": "size0"
},
"x": 716.25,
"y": 1070
},
{
"id": 47,
"type": "curveLiteral",
"parameters": {
"curve": {
"keys": [
{
"time": 0,
"value": 0.8,
"inTangent": 0,
"outTangent": 0
},
{
"time": 0.35,
"value": 1.7,
"inTangent": 0,
"outTangent": 0
},
{
"time": 1,
"value": 3.1,
"inTangent": 0,
"outTangent": 0
}
]
}
},
"x": 705,
"y": 836
},
{
"id": 48,
"type": "mul",
"parameters": {},
"x": 970,
"y": 1070
},
{
"id": 49,
"type": "setAttribute",
"parameters": {
"name": "size"
},
"x": 1205,
"y": 1070
},
{
"id": 50,
"type": "attribute",
"parameters": {
"name": "spin_rate"
},
"x": 0,
"y": 821
},
{
"id": 51,
"type": "attribute",
"parameters": {
"name": "spin"
},
"x": 0,
"y": 915
},
{
"id": 52,
"type": "mad",
"parameters": {},
"x": 231.25,
"y": 852
},
{
"id": 53,
"type": "setAttribute",
"parameters": {
"name": "spin"
},
"x": 455,
"y": 852
},
{
"id": 54,
"type": "gradientRGBA",
"parameters": {
"stops": [
{
"position": 0,
"color": [
0.32,
0.27,
0.22,
0
]
},
{
"position": 0.07,
"color": [
0.34,
0.3,
0.25,
0.2
]
},
{
"position": 0.24,
"color": [
0.46,
0.44,
0.41,
0.17
]
},
{
"position": 0.7,
"color": [
0.58,
0.58,
0.58,
0.1
]
},
{
"position": 1,
"color": [
0.62,
0.62,
0.62,
0
]
}
]
},
"x": 705,
"y": 742
},
{
"id": 55,
"type": "setAttribute",
"parameters": {
"name": "color"
},
"x": 955,
"y": 742
},
{
"id": 56,
"type": "const",
"parameters": {
"value": [
0,
0,
1
]
},
"x": -45,
"y": 1210
},
{
"id": 57,
"type": "quatFromAxisAngle",
"parameters": {},
"x": 205,
"y": 1190
},
{
"id": 58,
"type": "setAttribute",
"parameters": {
"name": "orientation"
},
"x": 455,
"y": 1190
},
{
"id": 59,
"type": "wind",
"parameters": {},
"x": 2353.75,
"y": 720
}
],
"connections": [
{
"from": 1,
"fromPort": "out",
"to": 2,
"toPort": "a"
},
{
"from": 0,
"fromPort": "out",
"to": 2,
"toPort": "b"
},
{
"from": 2,
"fromPort": "out",
"to": 3,
"toPort": "value"
},
{
"from": 2,
"fromPort": "out",
"to": 5,
"toPort": "a"
},
{
"from": 4,
"fromPort": "out",
"to": 5,
"toPort": "b"
},
{
"from": 5,
"fromPort": "out",
"to": 6,
"toPort": "a"
},
{
"from": 7,
"fromPort": "out",
"to": 6,
"toPort": "b"
},
{
"from": 6,
"fromPort": "out",
"to": 8,
"toPort": "condition"
},
{
"from": 9,
"fromPort": "out",
"to": 10,
"toPort": "a"
},
{
"from": 11,
"fromPort": "out",
"to": 10,
"toPort": "b"
},
{
"from": 12,
"fromPort": "out",
"to": 13,
"toPort": "x"
},
{
"from": 10,
"fromPort": "out",
"to": 13,
"toPort": "y"
},
{
"from": 14,
"fromPort": "out",
"to": 13,
"toPort": "z"
},
{
"from": 15,
"fromPort": "out",
"to": 16,
"toPort": "v"
},
{
"from": 17,
"fromPort": "out",
"to": 16,
"toPort": "s"
},
{
"from": 16,
"fromPort": "out",
"to": 18,
"toPort": "a"
},
{
"from": 13,
"fromPort": "out",
"to": 18,
"toPort": "b"
},
{
"from": 18,
"fromPort": "out",
"to": 20,
"toPort": "a"
},
{
"from": 19,
"fromPort": "out",
"to": 20,
"toPort": "b"
},
{
"from": 20,
"fromPort": "out",
"to": 21,
"toPort": "p"
},
{
"from": 18,
"fromPort": "out",
"to": 23,
"toPort": "a"
},
{
"from": 22,
"fromPort": "out",
"to": 23,
"toPort": "b"
},
{
"from": 23,
"fromPort": "out",
"to": 24,
"toPort": "p"
},
{
"from": 18,
"fromPort": "out",
"to": 26,
"toPort": "a"
},
{
"from": 25,
"fromPort": "out",
"to": 26,
"toPort": "b"
},
{
"from": 26,
"fromPort": "out",
"to": 27,
"toPort": "p"
},
{
"from": 21,
"fromPort": "out",
"to": 28,
"toPort": "x"
},
{
"from": 24,
"fromPort": "out",
"to": 28,
"toPort": "y"
},
{
"from": 27,
"fromPort": "out",
"to": 28,
"toPort": "z"
},
{
"from": 28,
"fromPort": "out",
"to": 29,
"toPort": "v"
},
{
"from": 30,
"fromPort": "out",
"to": 29,
"toPort": "s"
},
{
"from": 31,
"fromPort": "out",
"to": 32,
"toPort": "a"
},
{
"from": 29,
"fromPort": "out",
"to": 32,
"toPort": "b"
},
{
"from": 32,
"fromPort": "out",
"to": 34,
"toPort": "v"
},
{
"from": 0,
"fromPort": "out",
"to": 34,
"toPort": "s"
},
{
"from": 33,
"fromPort": "out",
"to": 35,
"toPort": "a"
},
{
"from": 34,
"fromPort": "out",
"to": 35,
"toPort": "b"
},
{
"from": 38,
"fromPort": "out",
"to": 37,
"toPort": "a"
},
{
"from": 0,
"fromPort": "out",
"to": 37,
"toPort": "b"
},
{
"from": 37,
"fromPort": "out",
"to": 39,
"toPort": "x"
},
{
"from": 35,
"fromPort": "out",
"to": 40,
"toPort": "a"
},
{
"from": 59,
"fromPort": "out",
"to": 40,
"toPort": "b"
},
{
"from": 39,
"fromPort": "out",
"to": 40,
"toPort": "t"
},
{
"from": 40,
"fromPort": "out",
"to": 41,
"toPort": "value"
},
{
"from": 40,
"fromPort": "out",
"to": 43,
"toPort": "v"
},
{
"from": 0,
"fromPort": "out",
"to": 43,
"toPort": "s"
},
{
"from": 42,
"fromPort": "out",
"to": 44,
"toPort": "a"
},
{
"from": 43,
"fromPort": "out",
"to": 44,
"toPort": "b"
},
{
"from": 44,
"fromPort": "out",
"to": 45,
"toPort": "value"
},
{
"from": 5,
"fromPort": "out",
"to": 47,
"toPort": "t"
},
{
"from": 46,
"fromPort": "out",
"to": 48,
"toPort": "a"
},
{
"from": 47,
"fromPort": "out",
"to": 48,
"toPort": "b"
},
{
"from": 48,
"fromPort": "out",
"to": 49,
"toPort": "value"
},
{
"from": 50,
"fromPort": "out",
"to": 52,
"toPort": "a"
},
{
"from": 0,
"fromPort": "out",
"to": 52,
"toPort": "b"
},
{
"from": 51,
"fromPort": "out",
"to": 52,
"toPort": "c"
},
{
"from": 52,
"fromPort": "out",
"to": 53,
"toPort": "value"
},
{
"from": 5,
"fromPort": "out",
"to": 54,
"toPort": "t"
},
{
"from": 54,
"fromPort": "out",
"to": 55,
"toPort": "value"
},
{
"from": 56,
"fromPort": "out",
"to": 57,
"toPort": "axis"
},
{
"from": 52,
"fromPort": "out",
"to": 57,
"toPort": "angle"
},
{
"from": 57,
"fromPort": "quat",
"to": 58,
"toPort": "value"
},
{
"from": 42,
"fromPort": "out",
"to": 59,
"toPort": "position"
}
]
},
"emitter": {
"spawn_rate": 40,
"prewarm": 4.5,
"blend": 0,
"projection": 0,
"lighting": true,
"soft_depth": true,
"sort": false,
"cull": false,
"texture": null,
"flipbook": [
1,
1
],
"render": {
"position": "position",
"size": "size",
"color": "color",
"orientation": "orientation"
}
},
"source": "graph",
"assembly": "",
"optimize": true
}
{
"name": "soot",
"attributes": [
{
"name": "position",
"components": 3
},
{
"name": "velocity",
"components": 3
},
{
"name": "age",
"components": 1
},
{
"name": "life",
"components": 1
},
{
"name": "size",
"components": 1
},
{
"name": "size0",
"components": 1
},
{
"name": "color",
"components": 4
},
{
"name": "seed",
"components": 1
},
{
"name": "orientation",
"components": 4
},
{
"name": "tumble",
"components": 3
}
],
"groups": [],
"init": {
"nodes": [
{
"id": 18,
"type": "builtin",
"parameters": {
"id": 2
},
"x": 1524.266787905249,
"y": -95.60801223873777
},
{
"id": 19,
"type": "add",
"parameters": {},
"x": 1835,
"y": 2.5
},
{
"id": 20,
"type": "setAttribute",
"parameters": {
"name": "position"
},
"x": 2055,
"y": 2.5
},
{
"id": 21,
"type": "const",
"parameters": {
"value": [
0,
1.9,
0
]
},
"x": 481.25,
"y": 551.5
},
{
"id": 22,
"type": "random",
"parameters": {
"components": 3
},
"x": 0,
"y": 559.5
},
{
"id": 23,
"type": "mad",
"parameters": {},
"x": 220,
"y": 645.5
},
{
"id": 24,
"type": "const",
"parameters": {
"value": [
2
]
},
"x": 3.75,
"y": 661.5
},
{
"id": 25,
"type": "const",
"parameters": {
"value": [
-1
]
},
"x": 3.75,
"y": 755.5
},
{
"id": 26,
"type": "scale",
"parameters": {},
"x": 470,
"y": 711
},
{
"id": 27,
"type": "const",
"parameters": {
"value": [
0.7
]
},
"x": 231.25,
"y": 771.5
},
{
"id": 28,
"type": "add",
"parameters": {},
"x": 705,
"y": 695
},
{
"id": 29,
"type": "setAttribute",
"parameters": {
"name": "velocity"
},
"x": 925,
"y": 695
},
{
"id": 30,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 959.5
},
{
"id": 31,
"type": "mad",
"parameters": {},
"x": 220,
"y": 1076.5
},
{
"id": 32,
"type": "const",
"parameters": {
"value": [
0.03
]
},
"x": 3.75,
"y": 1053.5
},
{
"id": 33,
"type": "const",
"parameters": {
"value": [
0.02
]
},
"x": 3.75,
"y": 1147.5
},
{
"id": 34,
"type": "setAttribute",
"parameters": {
"name": "age"
},
"x": 205,
"y": 465.5
},
{
"id": 35,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 3.75,
"y": 465.5
},
{
"id": 36,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 1241.5
},
{
"id": 37,
"type": "mad",
"parameters": {},
"x": 220,
"y": 1358.5
},
{
"id": 38,
"type": "const",
"parameters": {
"value": [
2.6
]
},
"x": 3.75,
"y": 1335.5
},
{
"id": 39,
"type": "const",
"parameters": {
"value": [
2.6
]
},
"x": 3.75,
"y": 1429.5
},
{
"id": 40,
"type": "setAttribute",
"parameters": {
"name": "life"
},
"x": 455,
"y": 1358.5
},
{
"id": 41,
"type": "setAttribute",
"parameters": {
"name": "size0"
},
"x": 455,
"y": 951.5
},
{
"id": 42,
"type": "setAttribute",
"parameters": {
"name": "size"
},
"x": 455,
"y": 1123.5
},
{
"id": 43,
"type": "setAttribute",
"parameters": {
"name": "color"
},
"x": 205,
"y": 865.5
},
{
"id": 44,
"type": "const",
"parameters": {
"value": [
1,
1,
1,
0
]
},
"x": 3.75,
"y": 865.5
},
{
"id": 50,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 1805.5
},
{
"id": 51,
"type": "mad",
"parameters": {},
"x": 220,
"y": 1922.5
},
{
"id": 52,
"type": "const",
"parameters": {
"value": [
14
]
},
"x": 3.75,
"y": 1899.5
},
{
"id": 53,
"type": "const",
"parameters": {
"value": [
-7
]
},
"x": 3.75,
"y": 1993.5
},
{
"id": 55,
"type": "random",
"parameters": {
"components": 1
},
"x": 0,
"y": 2087.5
},
{
"id": 56,
"type": "mad",
"parameters": {},
"x": 220,
"y": 2204.5
},
{
"id": 57,
"type": "const",
"parameters": {
"value": [
64
]
},
"x": 3.75,
"y": 2181.5
},
{
"id": 58,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 3.75,
"y": 2275.5
},
{
"id": 59,
"type": "setAttribute",
"parameters": {
"name": "seed"
},
"x": 455,
"y": 2204.5
},
{
"id": 0,
"type": "sampleSphereVolume",
"parameters": {},
"x": 1580.274926377338,
"y": 95.7984954592288
},
{
"id": 1,
"type": "random",
"parameters": {
"components": 3
},
"x": 1288.9516890851849,
"y": 82.30089373140704
},
{
"id": 62,
"type": "setAttribute",
"parameters": {
"name": "orientation"
},
"x": 455,
"y": 1640.5
},
{
"id": 63,
"type": "random",
"parameters": {
"components": 3
},
"x": -45,
"y": 1640.5
},
{
"id": 64,
"type": "sampleRotation",
"parameters": {},
"x": 205,
"y": 1640.5
},
{
"id": 65,
"type": "random",
"parameters": {
"components": 2
},
"x": -295,
"y": 2042.5
},
{
"id": 66,
"type": "sampleSphereSurface",
"parameters": {},
"x": -45,
"y": 2042.5
},
{
"id": 67,
"type": "scale",
"parameters": {},
"x": 205,
"y": 1922.5
},
{
"id": 68,
"type": "setAttribute",
"parameters": {
"name": "tumble"
},
"x": 455,
"y": 1922.5
}
],
"connections": [
{
"from": 18,
"fromPort": "out",
"to": 19,
"toPort": "a"
},
{
"from": 19,
"fromPort": "out",
"to": 20,
"toPort": "value"
},
{
"from": 22,
"fromPort": "out",
"to": 23,
"toPort": "a"
},
{
"from": 24,
"fromPort": "out",
"to": 23,
"toPort": "b"
},
{
"from": 25,
"fromPort": "out",
"to": 23,
"toPort": "c"
},
{
"from": 23,
"fromPort": "out",
"to": 26,
"toPort": "v"
},
{
"from": 27,
"fromPort": "out",
"to": 26,
"toPort": "s"
},
{
"from": 21,
"fromPort": "out",
"to": 28,
"toPort": "a"
},
{
"from": 26,
"fromPort": "out",
"to": 28,
"toPort": "b"
},
{
"from": 28,
"fromPort": "out",
"to": 29,
"toPort": "value"
},
{
"from": 30,
"fromPort": "out",
"to": 31,
"toPort": "a"
},
{
"from": 32,
"fromPort": "out",
"to": 31,
"toPort": "b"
},
{
"from": 33,
"fromPort": "out",
"to": 31,
"toPort": "c"
},
{
"from": 35,
"fromPort": "out",
"to": 34,
"toPort": "value"
},
{
"from": 36,
"fromPort": "out",
"to": 37,
"toPort": "a"
},
{
"from": 38,
"fromPort": "out",
"to": 37,
"toPort": "b"
},
{
"from": 39,
"fromPort": "out",
"to": 37,
"toPort": "c"
},
{
"from": 37,
"fromPort": "out",
"to": 40,
"toPort": "value"
},
{
"from": 31,
"fromPort": "out",
"to": 41,
"toPort": "value"
},
{
"from": 31,
"fromPort": "out",
"to": 42,
"toPort": "value"
},
{
"from": 44,
"fromPort": "out",
"to": 43,
"toPort": "value"
},
{
"from": 50,
"fromPort": "out",
"to": 51,
"toPort": "a"
},
{
"from": 52,
"fromPort": "out",
"to": 51,
"toPort": "b"
},
{
"from": 53,
"fromPort": "out",
"to": 51,
"toPort": "c"
},
{
"from": 55,
"fromPort": "out",
"to": 56,
"toPort": "a"
},
{
"from": 57,
"fromPort": "out",
"to": 56,
"toPort": "b"
},
{
"from": 58,
"fromPort": "out",
"to": 56,
"toPort": "c"
},
{
"from": 56,
"fromPort": "out",
"to": 59,
"toPort": "value"
},
{
"from": 0,
"fromPort": "out",
"to": 19,
"toPort": "b"
},
{
"from": 1,
"fromPort": "out",
"to": 0,
"toPort": "uvw"
},
{
"from": 63,
"fromPort": "out",
"to": 64,
"toPort": "uvw"
},
{
"from": 64,
"fromPort": "quat",
"to": 62,
"toPort": "value"
},
{
"from": 65,
"fromPort": "out",
"to": 66,
"toPort": "uv"
},
{
"from": 66,
"fromPort": "out",
"to": 67,
"toPort": "v"
},
{
"from": 51,
"fromPort": "out",
"to": 67,
"toPort": "s"
},
{
"from": 67,
"fromPort": "out",
"to": 68,
"toPort": "value"
}
]
},
"update": {
"nodes": [
{
"id": 0,
"type": "builtin",
"parameters": {
"id": 0
},
"x": 0,
"y": 1070.5
},
{
"id": 1,
"type": "attribute",
"parameters": {
"name": "age"
},
"x": 12,
"y": 1334.5
},
{
"id": 2,
"type": "add",
"parameters": {},
"x": 347,
"y": 1334.5
},
{
"id": 3,
"type": "setAttribute",
"parameters": {
"name": "age"
},
"x": 601,
"y": 1334.5
},
{
"id": 4,
"type": "attribute",
"parameters": {
"name": "life"
},
"x": 312,
"y": 1685.75
},
{
"id": 5,
"type": "div",
"parameters": {},
"x": 635,
"y": 1668.75
},
{
"id": 6,
"type": "compare",
"parameters": {
"op": "ge"
},
"x": 973,
"y": 2038.75
},
{
"id": 7,
"type": "const",
"parameters": {
"value": [
1
]
},
"x": 600,
"y": 2055.75
},
{
"id": 8,
"type": "kill",
"parameters": {},
"x": 1352.5,
"y": 2038.75
},
{
"id": 9,
"type": "const",
"parameters": {
"value": [
0,
2.6,
0
]
},
"x": 1291,
"y": 303
},
{
"id": 10,
"type": "const",
"parameters": {
"value": [
0,
-2.4,
0
]
},
"x": 1291,
"y": 434
},
{
"id": 11,
"type": "mul",
"parameters": {},
"x": 975,
"y": 565
},
{
"id": 12,
"type": "const",
"parameters": {
"value": [
2.4
]
},
"x": 600,
"y": 565
},
{
"id": 13,
"type": "saturate",
"parameters": {},
"x": 1361,
"y": 565
},
{
"id": 14,
"type": "mix",
"parameters": {},
"x": 1696,
"y": 386
},
{
"id": 15,
"type": "builtin",
"parameters": {
"id": 7
},
"x": 0,
"y": 262
},
{
"id": 16,
"type": "mul",
"parameters": {},
"x": 340.5,
"y": 922.75
},
{
"id": 17,
"type": "const",
"parameters": {
"value": [
-0.35
]
},
"x": 11,
"y": 590.5
},
{
"id": 18,
"type": "attribute",
"parameters": {
"name": "seed"
},
"x": 312,
"y": 408.75
},
{
"id": 19,
"type": "vec3",
"parameters": {},
"x": 613,
"y": 848.75
},
{
"id": 20,
"type": "const",
"parameters": {
"value": [
0
]
},
"x": 311,
"y": 1035.75
},
{
"id": 21,
"type": "attribute",
"parameters": {
"name": "position"
},
"x": 12,
"y": 0
},
{
"id": 22,
"type": "scale",
"parameters": {},
"x": 347.5,
"y": 65.75
},
{
"id": 23,
"type": "const",
"parameters": {
"value": [
0.9
]
},
"x": 11,
"y": 131
},
{
"id": 24,
"type": "add",
"parameters": {},
"x": 981.5,
"y": 809
},
{
"id": 25,
"type": "const",
"parameters": {
"value": [
0,
0,
0
]
},
"x": 905,
"y": 678
},
{
"id": 26,
"type": "add",
"parameters": {},
"x": 1367.5,
"y": 735
},
{
"id": 27,
"type": "simplex3",
"parameters": {},
"x": 1662,
"y": 735
},
{
"id": 28,
"type": "const",
"parameters": {
"value": [
17.3,
5.1,
31.7
]
},
"x": 905,
"y": 930.5
},
{
"id": 29,
"type": "add",
"parameters": {},
"x": 1367.5,
"y": 865.5
},
{
"id": 30,
"type": "simplex3",
"parameters": {},
"x": 1662,
"y": 865.5
},
{
"id": 31,
"type": "const",
"parameters": {
"value": [
73.9,
41.3,
11.5
]
},
"x": 905,
"y": 1061.5
},
{
"id": 32,
"type": "add",
"parameters": {},
"x": 1367.5,
"y": 1044.5
},
{
"id": 33,
"type": "simplex3",
"parameters": {},
"x": 1662,
"y": 1044.5
},
{
"id": 34,
"type": "vec3",
"parameters": {},
"x": 1961,
"y": 850.5
},
{
"id": 35,
"type": "scale",
"parameters": {},
"x": 2237,
"y": 963.5
},
{
"id": 36,
"type": "const",
"parameters": {
"value": [
1.2
]
},
"x": 1948,
"y": 980.5
},
{
"id": 37,
"type": "add",
"parameters": {},
"x": 2453,
"y": 840
},
{
"id": 38,
"type": "attribute",
"parameters": {
"name": "velocity"
},
"x": 2670,
"y": 237.5
},
{
"id": 39,
"type": "scale",
"parameters": {},
"x": 2705.5,
"y": 902.25
},
{
"id": 40,
"type": "add",
"parameters": {},
"x": 2957,
"y": 828.25
},
{
"id": 41,
"type": "setAttribute",
"parameters": {
"name": "velocity"
},
"x": 3174,
"y": 828.25
},
{
"id": 42,
"type": "attribute",
"parameters": {
"name": "position"
},
"x": 3174,
"y": 10.25
},
{
"id": 43,
"type": "scale",
"parameters": {},
"x": 3209.5,
"y": 141.25
},
{
"id": 44,
"type": "add",
"parameters": {},
"x": 3461,
"y": 103.5
},
{
"id": 45,
"type": "setAttribute",
"parameters": {
"name": "position"
},
"x": 3678,
"y": 103.5
},
{
"id": 46,
"type": "attribute",
"parameters": {
"name": "size0"
},
"x": 946.5,
"y": 1266
},
{
"id": 47,
"type": "curveLiteral",
"parameters": {
"curve": {
"keys": [
{
"time": 0,
"value": 1,
"inTangent": 0,
"outTangent": 0
},
{
"time": 1,
"value": 0.85,
"inTangent": 0,
"outTangent": 0
}
]
}
},
"x": 889,
"y": 1496.75
},
{
"id": 48,
"type": "mul",
"parameters": {},
"x": 1361,
"y": 1323
},
{
"id": 49,
"type": "setAttribute",
"parameters": {
"name": "size"
},
"x": 1661,
"y": 1323
},
{
"id": 54,
"type": "gradientRGBA",
"parameters": {
"stops": [
{
"position": 0,
"color": [
0.06,
0.05,
0.045,
0
]
},
{
"position": 0.12,
"color": [
0.05,
0.045,
0.04,
0.85
]
},
{
"position": 0.45,
"color": [
0.04,
0.04,
0.04,
0.8
]
},
{
"position": 0.85,
"color": [
0.035,
0.035,
0.035,
0.55
]
},
{
"position": 1,
"color": [
0.035,
0.035,
0.035,
0
]
}
]
},
"x": 889,
"y": 1840.75
},
{
"id": 55,
"type": "setAttribute",
"parameters": {
"name": "color"
},
"x": 1332.5,
"y": 1840.75
},
{
"id": 58,
"type": "setAttribute",
"parameters": {
"name": "orientation"
},
"x": 601,
"y": 1514
},
{
"id": 59,
"type": "attribute",
"parameters": {
"name": "orientation"
},
"x": -149,
"y": 1454
},
{
"id": 60,
"type": "attribute",
"parameters": {
"name": "tumble"
},
"x": -149,
"y": 1554
},
{
"id": 61,
"type": "quatIntegrate",
"parameters": {},
"x": 301,
"y": 1514
},
{
"id": 62,
"type": "wind",
"parameters": {},
"x": 2783.25,
"y": 1088.25
},
{
"id": 63,
"type": "const",
"parameters": {
"value": [
1
]
},
"x": 2783.25,
"y": 1208.25
},
{
"id": 64,
"type": "scale",
"parameters": {},
"x": 3083.25,
"y": 1088.25
},
{
"id": 65,
"type": "add",
"parameters": {},
"x": 3343.25,
"y": 1028.25
}
],
"connections": [
{
"from": 1,
"fromPort": "out",
"to": 2,
"toPort": "a"
},
{
"from": 0,
"fromPort": "out",
"to": 2,
"toPort": "b"
},
{
"from": 2,
"fromPort": "out",
"to": 3,
"toPort": "value"
},
{
"from": 2,
"fromPort": "out",
"to": 5,
"toPort": "a"
},
{
"from": 4,
"fromPort": "out",
"to": 5,
"toPort": "b"
},
{
"from": 5,
"fromPort": "out",
"to": 6,
"toPort": "a"
},
{
"from": 7,
"fromPort": "out",
"to": 6,
"toPort": "b"
},
{
"from": 6,
"fromPort": "out",
"to": 8,
"toPort": "condition"
},
{
"from": 12,
"fromPort": "out",
"to": 11,
"toPort": "a"
},
{
"from": 5,
"fromPort": "out",
"to": 11,
"toPort": "b"
},
{
"from": 11,
"fromPort": "out",
"to": 13,
"toPort": "x"
},
{
"from": 9,
"fromPort": "out",
"to": 14,
"toPort": "a"
},
{
"from": 10,
"fromPort": "out",
"to": 14,
"toPort": "b"
},
{
"from": 13,
"fromPort": "out",
"to": 14,
"toPort": "t"
},
{
"from": 15,
"fromPort": "out",
"to": 16,
"toPort": "a"
},
{
"from": 17,
"fromPort": "out",
"to": 16,
"toPort": "b"
},
{
"from": 18,
"fromPort": "out",
"to": 19,
"toPort": "x"
},
{
"from": 16,
"fromPort": "out",
"to": 19,
"toPort": "y"
},
{
"from": 20,
"fromPort": "out",
"to": 19,
"toPort": "z"
},
{
"from": 21,
"fromPort": "out",
"to": 22,
"toPort": "v"
},
{
"from": 23,
"fromPort": "out",
"to": 22,
"toPort": "s"
},
{
"from": 22,
"fromPort": "out",
"to": 24,
"toPort": "a"
},
{
"from": 19,
"fromPort": "out",
"to": 24,
"toPort": "b"
},
{
"from": 24,
"fromPort": "out",
"to": 26,
"toPort": "a"
},
{
"from": 25,
"fromPort": "out",
"to": 26,
"toPort": "b"
},
{
"from": 26,
"fromPort": "out",
"to": 27,
"toPort": "p"
},
{
"from": 24,
"fromPort": "out",
"to": 29,
"toPort": "a"
},
{
"from": 28,
"fromPort": "out",
"to": 29,
"toPort": "b"
},
{
"from": 29,
"fromPort": "out",
"to": 30,
"toPort": "p"
},
{
"from": 24,
"fromPort": "out",
"to": 32,
"toPort": "a"
},
{
"from": 31,
"fromPort": "out",
"to": 32,
"toPort": "b"
},
{
"from": 32,
"fromPort": "out",
"to": 33,
"toPort": "p"
},
{
"from": 27,
"fromPort": "out",
"to": 34,
"toPort": "x"
},
{
"from": 30,
"fromPort": "out",
"to": 34,
"toPort": "y"
},
{
"from": 33,
"fromPort": "out",
"to": 34,
"toPort": "z"
},
{
"from": 34,
"fromPort": "out",
"to": 35,
"toPort": "v"
},
{
"from": 36,
"fromPort": "out",
"to": 35,
"toPort": "s"
},
{
"from": 14,
"fromPort": "out",
"to": 37,
"toPort": "a"
},
{
"from": 35,
"fromPort": "out",
"to": 37,
"toPort": "b"
},
{
"from": 37,
"fromPort": "out",
"to": 39,
"toPort": "v"
},
{
"from": 0,
"fromPort": "out",
"to": 39,
"toPort": "s"
},
{
"from": 38,
"fromPort": "out",
"to": 40,
"toPort": "a"
},
{
"from": 39,
"fromPort": "out",
"to": 40,
"toPort": "b"
},
{
"from": 40,
"fromPort": "out",
"to": 41,
"toPort": "value"
},
{
"from": 65,
"fromPort": "out",
"to": 43,
"toPort": "v"
},
{
"from": 0,
"fromPort": "out",
"to": 43,
"toPort": "s"
},
{
"from": 42,
"fromPort": "out",
"to": 44,
"toPort": "a"
},
{
"from": 43,
"fromPort": "out",
"to": 44,
"toPort": "b"
},
{
"from": 44,
"fromPort": "out",
"to": 45,
"toPort": "value"
},
{
"from": 5,
"fromPort": "out",
"to": 47,
"toPort": "t"
},
{
"from": 46,
"fromPort": "out",
"to": 48,
"toPort": "a"
},
{
"from": 47,
"fromPort": "out",
"to": 48,
"toPort": "b"
},
{
"from": 48,
"fromPort": "out",
"to": 49,
"toPort": "value"
},
{
"from": 5,
"fromPort": "out",
"to": 54,
"toPort": "t"
},
{
"from": 54,
"fromPort": "out",
"to": 55,
"toPort": "value"
},
{
"from": 59,
"fromPort": "out",
"to": 61,
"toPort": "quat"
},
{
"from": 60,
"fromPort": "out",
"to": 61,
"toPort": "omega"
},
{
"from": 0,
"fromPort": "out",
"to": 61,
"toPort": "dt"
},
{
"from": 61,
"fromPort": "quat",
"to": 58,
"toPort": "value"
},
{
"from": 42,
"fromPort": "out",
"to": 62,
"toPort": "position"
},
{
"from": 62,
"fromPort": "out",
"to": 64,
"toPort": "v"
},
{
"from": 63,
"fromPort": "out",
"to": 64,
"toPort": "s"
},
{
"from": 40,
"fromPort": "out",
"to": 65,
"toPort": "a"
},
{
"from": 64,
"fromPort": "out",
"to": 65,
"toPort": "b"
}
]
},
"emitter": {
"spawn_rate": 75,
"prewarm": 3,
"blend": 0,
"projection": 4,
"lighting": true,
"soft_depth": true,
"sort": false,
"cull": false,
"texture": null,
"flipbook": [
1,
1
],
"render": {
"position": "position",
"size": "size",
"color": "color",
"orientation": "orientation"
}
},
"source": "graph",
"assembly": "",
"optimize": true
}
import { ParticleEffect } from "@woosh/meep-engine/src/engine/graphics/ecs/particles/ParticleEffect.js";
import { ParticleEffectDocument } from "@woosh/meep-engine/editor/particles/effect/ParticleEffectDocument.js";
import { PARTICLE_BUILTIN_GROUPS } from "@woosh/meep-engine/src/shade/renderer/particles/graph/groups/particle_builtin_groups.js";
import {
get_particle_group,
register_particle_group,
} from "@woosh/meep-engine/src/shade/renderer/particles/graph/particle_group_library.js";
import flame_json from "./effects/flame.json" with { type: "json" };
import smoke_json from "./effects/smoke.json" with { type: "json" };
import embers_json from "./effects/embers.json" with { type: "json" };
import soot_json from "./effects/soot.json" with { type: "json" };
// Ensure the standard node groups (simplex3, sampleSphereVolume, quatIntegrate, ...)
// are registered even when a production bundler tree-shakes side-effect-only imports.
for (const group of PARTICLE_BUILTIN_GROUPS) {
if (get_particle_group(group.name) === undefined) {
register_particle_group(group);
}
}
/**
* A bonfire composed of four saved particle effects: flame, smoke, embers and soot.
*
* The four effects read as one fire because they are authored to fit together: the smoke starts
* where the flame stops being bright, the embers leave from inside the flame core and outlive it,
* and the soot rides the same plume as the smoke before falling out of it.
*
* ## Saved effect documents
*
* Each effect is a {@link ParticleEffectDocument} saved as JSON in `effects/`: the per-particle
* attribute declarations, the INIT and UPDATE node graphs, and the emitter settings.
*
* Every effect follows the same structure:
*
* - **INIT** places the particle, gives it an initial velocity, and rolls its lifetime, base size
* and personal `seed`. Age and lifetime are ordinary declared attributes rather than built-ins.
* - **UPDATE** advances `age`, kills the particle when `age >= life`, integrates forces, and
* drives the drawn `size` and `color` from `age / life` through curve and colour-gradient ramps
* compiled directly into VM instructions.
*
* ## Wind and turbulence
*
* All four effects sample the scene's wind at each particle's world position through the `wind`
* node (`SAMPLE_WIND`). Flame, embers and soot integrate `velocity + gain · wind` into their
* positions (with gain 0.5 for flame so its tongues lean without blowing off the logs, and 1.0 for
* embers and soot), while smoke relaxes its velocity toward the wind through drag so the rising
* plume bends downwind.
*
* Turbulence in all four effects is three decorrelated `simplex3` samples at a point that drifts
* with time so the field scrolls through the rising column.
*
* ## Lighting
*
* Smoke and soot opt into scene lighting (`lighting: true`), so their colour gradients are diffuse
* albedo — tar-brown paling to grey for smoke, near-black for soot — lit by the warm point light in
* the fire below and the cool moonlight above. Flame and embers are additive and unlit: they are
* the light source.
*/
/**
* The four saved effect JSON payloads, keyed by effect name.
*
* @type {Object<string, object>}
*/
export const BONFIRE_EFFECT_JSON = Object.freeze({
flame: flame_json,
smoke: smoke_json,
embers: embers_json,
soot: soot_json,
});
/**
* Effect names in display order.
*
* @type {string[]}
*/
export const BONFIRE_EFFECT_NAMES = Object.freeze(["flame", "smoke", "embers", "soot"]);
/**
* Load one of the saved effects into a {@link ParticleEffectDocument}.
*
* @param {string} name see {@link BONFIRE_EFFECT_NAMES}
* @param {object} [json]
* @returns {ParticleEffectDocument}
*/
export function bonfire_effect_document(name, json = BONFIRE_EFFECT_JSON[name]) {
return new ParticleEffectDocument().fromJSON(json);
}
/**
* Compile a {@link ParticleEffectDocument} into an ECS {@link ParticleEffect} component.
*
* Returns `null` if the document fails to compile.
*
* @param {ParticleEffectDocument} document
* @param {string|null} [texture] fallback sprite URL when the document itself names none
* @returns {ParticleEffect|null}
*/
export function bonfire_effect_component(document, texture = null) {
const compilation = document.compile();
if (compilation.program === null || compilation.layout === null) {
return null;
}
const config = document.emitterConfig(compilation);
return ParticleEffect.from({ ...config, texture: config.texture ?? texture });
}
/**
* Build all four {@link ParticleEffect} components, ready to attach to entities.
*
* @param {object} [textures] sprite URLs keyed by sprite name (`null` for untextured quads)
* @param {string|null} [textures.flame]
* @param {string|null} [textures.smoke]
* @param {string|null} [textures.ember]
* @param {string|null} [textures.soot]
* @returns {{flame: ParticleEffect, smoke: ParticleEffect, embers: ParticleEffect, soot: ParticleEffect}}
*/
export function bonfire_effects(textures = {}) {
const sprites = bonfire_sprite_urls(textures);
return Object.fromEntries(BONFIRE_EFFECT_NAMES.map(name => {
const component = bonfire_effect_component(bonfire_effect_document(name), sprites[name]);
if (component === null) {
throw new Error(`The saved '${name}' effect does not compile — see effects/${name}.json`);
}
return [name, component];
}));
}
/**
* Map the sprite set produced by `bonfire_sprites()` (where `ember` is singular) to effect names.
*
* @param {object} [textures]
* @returns {Object<string, string|null>}
*/
export function bonfire_sprite_urls(textures = {}) {
return {
flame: textures.flame ?? null,
smoke: textures.smoke ?? null,
embers: textures.ember ?? null,
soot: textures.soot ?? null,
};
}
/**
* Procedural sprites for the four bonfire particle effects, generated as PNG `data:` URLs.
*
* Returning `data:` URLs keeps the example self-contained while exercising the normal texture
* pipeline: each `ParticleEffect` names a URL, `AssetManager` decodes it, and
* `GPUParticleEmitterSystem` packs the images into the scene's shared particle sprite atlas.
*
* Noise generation is seeded so every run produces the same sprites.
*/
/** Size of the flame and smoke sprites, in texels. */
const LARGE = 128;
/** Size of the ember and soot sprites, in texels. */
const SMALL = 32;
/**
* @typedef {object} BonfireSprites
* @property {string} flame
* @property {string} smoke
* @property {string} ember
* @property {string} soot
*/
/**
* Generate the four particle sprite data URLs.
*
* @returns {BonfireSprites}
*/
export function bonfire_sprites() {
return {
flame: flame_sprite(),
smoke: smoke_sprite(),
ember: ember_sprite(),
soot: soot_sprite(),
};
}
/**
* Soft round puff with a bright core for additive flame licks.
*
* @returns {string} PNG data URL
*/
function flame_sprite() {
return draw(LARGE, (x, y) => {
const r = Math.hypot(x, y);
if (r >= 1) {
return 0;
}
const falloff = (1 - r) * (1 - r);
return falloff * (0.55 + 0.45 * (1 - r));
});
}
/**
* Cloudy puff: a radial falloff modulated by tiling multi-octave value noise so overlapping smoke
* particles read as a continuous plume rather than a stack of discs.
*
* @returns {string} PNG data URL
*/
function smoke_sprite() {
const noise = value_noise_field(LARGE, 0x51ce);
return draw(LARGE, (x, y, ix, iy) => {
const r = Math.hypot(x, y);
if (r >= 1) {
return 0;
}
const mask = smoothstep(1, 0.05, r);
const detail = 0.18 + 0.82 * noise[iy * LARGE + ix];
return mask * mask * detail;
});
}
/**
* Tight bright dot with a faint halo for glowing cinders.
*
* @returns {string} PNG data URL
*/
function ember_sprite() {
return draw(SMALL, (x, y) => {
const r = Math.hypot(x, y);
if (r >= 1) {
return 0;
}
const core = smoothstep(0.45, 0.0, r);
const halo = (1 - r) * (1 - r) * 0.35;
return Math.min(1, core + halo);
});
}
/**
* Irregular five-lobed flake with soft edges so tumbling soot particles visibly change silhouette.
*
* @returns {string} PNG data URL
*/
function soot_sprite() {
const random = seeded_random(0x50075);
const lobes = Array.from({ length: 5 }, () => 0.55 + random() * 0.45);
return draw(SMALL, (x, y) => {
const r = Math.hypot(x, y);
const angle = Math.atan2(y, x);
const t = (angle / (Math.PI * 2) + 1) % 1 * lobes.length;
const i = Math.floor(t);
const edge = lerp(lobes[i], lobes[(i + 1) % lobes.length], t - i);
return smoothstep(edge, edge * 0.55, r);
});
}
/**
* Rasterize an alpha function into a white RGBA PNG data URL.
*
* Sprites are white because each effect drives its own `color` channel from a gradient over age.
*
* @param {number} size
* @param {function(number, number, number, number): number} alpha
* @returns {string} PNG data URL
*/
function draw(size, alpha) {
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
const context = canvas.getContext("2d");
const image = context.createImageData(size, size);
const data = image.data;
for (let iy = 0; iy < size; iy++) {
for (let ix = 0; ix < size; ix++) {
const x = (ix + 0.5) / size * 2 - 1;
const y = (iy + 0.5) / size * 2 - 1;
const offset = (iy * size + ix) * 4;
data[offset] = 255;
data[offset + 1] = 255;
data[offset + 2] = 255;
data[offset + 3] = Math.round(clamp(alpha(x, y, ix, iy), 0, 1) * 255);
}
}
context.putImageData(image, 0, 0);
return canvas.toDataURL("image/png");
}
/**
* Three octaves of tiling value noise in `[0, 1]`.
*
* @param {number} size
* @param {number} seed
* @returns {Float32Array}
*/
function value_noise_field(size, seed) {
const field = new Float32Array(size * size);
let amplitude = 1;
let total = 0;
for (const period of [4, 8, 16]) {
const lattice = lattice_values(period, seed + period);
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
field[y * size + x] += amplitude * sample_lattice(
lattice, period, x / size * period, y / size * period
);
}
}
total += amplitude;
amplitude *= 0.55;
}
for (let i = 0; i < field.length; i++) {
field[i] /= total;
}
return field;
}
/**
* @param {number} period
* @param {number} seed
* @returns {Float32Array}
*/
function lattice_values(period, seed) {
const random = seeded_random(seed);
const values = new Float32Array(period * period);
for (let i = 0; i < values.length; i++) {
values[i] = random();
}
return values;
}
/**
* Smooth bilinear lookup wrapping at `period`.
*
* @param {Float32Array} lattice
* @param {number} period
* @param {number} x
* @param {number} y
* @returns {number}
*/
function sample_lattice(lattice, period, x, y) {
const x0 = Math.floor(x);
const y0 = Math.floor(y);
const fx = smootherstep(x - x0);
const fy = smootherstep(y - y0);
const at = (ix, iy) => lattice[((iy % period) + period) % period * period + ((ix % period) + period) % period];
return lerp(
lerp(at(x0, y0), at(x0 + 1, y0), fx),
lerp(at(x0, y0 + 1), at(x0 + 1, y0 + 1), fx),
fy
);
}
/**
* Deterministic `mulberry32` PRNG.
*
* @param {number} seed
* @returns {function(): number} uniform in `[0, 1)`
*/
function seeded_random(seed) {
let state = seed >>> 0;
return () => {
state = (state + 0x6d2b79f5) >>> 0;
let t = state;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
/**
* @param {number} edge0
* @param {number} edge1
* @param {number} x
* @returns {number}
*/
function smoothstep(edge0, edge1, x) {
const t = clamp((x - edge0) / (edge1 - edge0), 0, 1);
return t * t * (3 - 2 * t);
}
/**
* @param {number} t
* @returns {number}
*/
function smootherstep(t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
/**
* @param {number} a
* @param {number} b
* @param {number} t
* @returns {number}
*/
function lerp(a, b, t) {
return a + (b - a) * t;
}
/**
* @param {number} value
* @param {number} low
* @param {number} high
* @returns {number}
*/
function clamp(value, low, high) {
return value < low ? low : (value > high ? high : value);
}
import { DEG_TO_RAD } from "@woosh/meep-engine/src/core/math/DEG_TO_RAD.js";
import { GlobalFluidEffector } from "@woosh/meep-engine/src/engine/physics/fluid/effector/GlobalFluidEffector.js";
/**
* The weather over the fire: a steady wind plus a time-varying gust, written onto the scene's
* published wind field as its far-field velocity `v_far(t)`.
*
* Every `GPUSceneContext` publishes a wind field. With no local fluid cascades, every particle
* reading `SAMPLE_WIND` reads `v_far` directly. The gust uses `GlobalFluidEffector`'s three-axis
* sinusoid model, advanced each frame and added to the steady wind vector before calling
* `wind_field.set_far_velocity`.
*/
export class BonfireWind {
/**
* Steady wind speed, m/s.
* @type {number}
*/
speed = 2;
/**
* Steady wind heading, degrees about +Y from +X toward +Z. The default blows across the
* opening camera view from left to right.
* @type {number}
*/
heading_degrees = -38;
/**
* Peak gust speed on top of the steady wind, m/s per axis.
* @type {number}
*/
gust = 1.5;
/**
* @type {GlobalFluidEffector}
*/
#weather = new GlobalFluidEffector();
/** @type {Float64Array} */
#gust_now = new Float64Array(3);
/**
* `v_far(t)` as last written.
* @type {Float64Array}
*/
far_velocity = new Float64Array(3);
constructor() {
this.#weather.gust_period = 3;
}
/**
* Advance the gust by `dt` and write the combined wind velocity onto the scene's wind field.
*
* @param {number} dt seconds
* @param {import("@woosh/meep-engine/src/shade/renderer/wind/GPUSceneWind.js").GPUSceneWind} wind_field
*/
blow(dt, wind_field) {
const weather = this.#weather;
const gust = this.#gust_now;
weather.gust = this.gust;
weather.advance(dt);
weather.compute_gust(gust);
const heading = this.heading_degrees * DEG_TO_RAD;
const far = this.far_velocity;
far[0] = this.speed * Math.cos(heading) + gust[0];
far[1] = gust[1];
far[2] = this.speed * Math.sin(heading) + gust[2];
wind_field.set_far_velocity(far);
}
}
import { Color } from "@woosh/meep-engine/src/core/color/Color.js";
import Quaternion from "@woosh/meep-engine/src/core/geom/Quaternion.js";
import Vector3 from "@woosh/meep-engine/src/core/geom/Vector3.js";
import { Entity } from "@woosh/meep-engine/src/engine/ecs/Entity.js";
import { Transform64 } from "@woosh/meep-engine/src/engine/ecs/transform/Transform64.js";
import { Light } from "@woosh/meep-engine/src/engine/graphics/ecs/light/Light.js";
import { LightType } from "@woosh/meep-engine/src/engine/graphics/ecs/light/LightType.js";
import { ShadedGeometry } from "@woosh/meep-engine/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js";
import { MeshletGeometry } from "@woosh/meep-engine/src/shade/renderer/geometry/MeshletGeometry.js";
import { MeshoptClusterizer } from "@woosh/meep-engine/src/shade/renderer/geometry/meshlet/build/meshoptimizer/meshopt_clusterizer.js";
import { meshlet_geometry_build_from_geometry } from "@woosh/meep-engine/src/shade/renderer/geometry/meshlet_geometry_build_from_geometry.js";
import { make_box_geometry } from "@woosh/meep-engine/src/shade/renderer/geometry/primitives/make_box_geometry.js";
import { make_cylinder_geometry } from "@woosh/meep-engine/src/shade/renderer/geometry/primitives/make_cylinder_geometry.js";
import { make_plane_geometry } from "@woosh/meep-engine/src/shade/renderer/geometry/primitives/make_plane_geometry.js";
import { ShadeDrawSide } from "@woosh/meep-engine/src/shade/renderer/material/ShadeDrawSide.js";
import { StandardShadeMaterial } from "@woosh/meep-engine/src/shade/renderer/material/StandardShadeMaterial.js";
import { TransparencyMode } from "@woosh/meep-engine/src/shade/renderer/material/TransparencyMode.js";
import { bonfire_effects } from "./bonfire_effects.js";
import { MOON_DIRECTION } from "./moonlit_environment.js";
/**
* The bonfire scene, built entirely as ECS entities.
*
* Every visible object in the set is an entity carrying a `Transform64` and one more component:
* the ground, stones and logs carry `ShadedGeometry`, the moon and firelight carry `Light`, and
* the flame, smoke, embers and soot carry `ParticleEffect`. Three systems observe those pairs and
* populate one `Scene`. Nothing here touches the scene graph directly.
*/
/** World-space origin of the fire pit. */
export const FIRE_POSITION = new Vector3(0, 0, 0);
/** Radius of the stone ring, in metres. */
const PIT_RADIUS = 0.85;
/** Baseline intensity of the fire point light, before flicker. */
const FIRELIGHT_INTENSITY = 3.5;
/**
* @typedef {object} BonfireWorld
* @property {number} flame entity id
* @property {number} smoke entity id
* @property {number} embers entity id
* @property {number} soot entity id
* @property {import("@woosh/meep-engine/src/engine/graphics/ecs/particles/ParticleEffect.js").ParticleEffect[]} effects
* the four components in flame / smoke / embers / soot order
* @property {Light} firelight the point light inside the fire, flickered by {@link animate_bonfire}
* @property {number[]} entities all created entity ids
*/
/**
* Build the ground, stone ring, logs, lights and four particle emitters.
*
* Async because the meshlet clusterizer runs on a WASM instance that initializes asynchronously.
*
* @param {import("@woosh/meep-engine/src/engine/ecs/EntityComponentDataset.js").EntityComponentDataset} dataset
* @param {object} [options]
* @param {import("./bonfire_sprites.js").BonfireSprites} [options.sprites] sprite data URLs for the
* four effects; when omitted, particles draw as untextured quads
* @returns {Promise<BonfireWorld>}
*/
export async function build_bonfire_world(dataset, { sprites = {} } = {}) {
await MeshoptClusterizer.ready;
/** @type {number[]} */
const entities = [];
/**
* @param {Entity} builder
* @returns {number}
*/
const build = builder => {
builder.build(dataset);
entities.push(builder.id);
return builder.id;
};
// --- Ground, stone ring and logs -----------------------------------------------------------
const ground_material = material(new Color(0.12, 0.113, 0.10, 1), { roughness: 0.95 });
const stone_material = material(new Color(0.14, 0.14, 0.15, 1), { roughness: 0.8 });
const log_material = material(new Color(0.17, 0.10, 0.065, 1), { roughness: 0.85 });
build(new Entity()
.add(placed(new Vector3(0, 0, 0), Quaternion.fromAxisAngle(Vector3.right, -Math.PI / 2)))
.add(shaded(meshlets(make_plane_geometry(200, 200, 1, 1)), ground_material)));
const stone = meshlets(make_box_geometry(0.34, 0.26, 0.28));
for (let i = 0; i < 11; i++) {
const angle = (i / 11) * Math.PI * 2;
const wobble = 0.9 + ((i * 37) % 7) / 24;
build(new Entity()
.add(placed(
new Vector3(
FIRE_POSITION.x + Math.cos(angle) * PIT_RADIUS * wobble,
0.1,
FIRE_POSITION.z + Math.sin(angle) * PIT_RADIUS * wobble
),
Quaternion.fromAxisAngle(Vector3.up, angle + wobble)
))
.add(shaded(stone, stone_material)));
}
// Five logs leaning inward in a teepee, plus two spent logs lying across the pit.
const log = meshlets(make_cylinder_geometry(0.075, 0.1, 1.5, 8, 1));
for (let i = 0; i < 5; i++) {
const angle = (i / 5) * Math.PI * 2 + 0.4;
const lean = 0.42;
build(new Entity()
.add(placed(
new Vector3(
FIRE_POSITION.x + Math.cos(angle) * 0.34,
0.62,
FIRE_POSITION.z + Math.sin(angle) * 0.34
),
Quaternion.fromAxisAngle(
new Vector3(Math.sin(angle), 0, -Math.cos(angle)),
lean
)
))
.add(shaded(log, log_material)));
}
for (const [offset, angle] of [[new Vector3(0.1, 0.09, -0.55), 0.7], [new Vector3(-0.35, 0.09, 0.4), 2.3]]) {
build(new Entity()
.add(placed(
new Vector3(FIRE_POSITION.x + offset.x, offset.y, FIRE_POSITION.z + offset.z),
Quaternion.fromAxisAngle(new Vector3(Math.sin(angle), 0, -Math.cos(angle)), Math.PI / 2)
))
.add(shaded(log, log_material)));
}
// --- Lights --------------------------------------------------------------------------------
const moon = new Light();
moon.type.set(LightType.DIRECTION);
moon.color.setRGB(0.62, 0.72, 1.0);
moon.intensity.set(1.1);
// Half-degree angular radius softens the terminator and shadow edge.
moon.radius.set(0.0087);
moon.castShadow.set(true);
build(new Entity()
.add(placed(new Vector3(0, 12, 0), look_along(MOON_DIRECTION.clone().negate())))
.add(moon));
// Warm point light inside the flame, flickered each frame by `animate_bonfire`.
const firelight = new Light();
firelight.type.set(LightType.POINT);
firelight.color.setRGB(1.0, 0.52, 0.19);
firelight.intensity.set(FIRELIGHT_INTENSITY);
firelight.distance.set(18);
firelight.radius.set(0.35);
build(new Entity()
.add(placed(new Vector3(FIRE_POSITION.x, 0.55, FIRE_POSITION.z)))
.add(firelight));
// --- Particle emitters ---------------------------------------------------------------------
const effects = bonfire_effects(sprites);
/**
* @param {import("@woosh/meep-engine/src/engine/graphics/ecs/particles/ParticleEffect.js").ParticleEffect} effect
* @param {number} height
* @returns {number}
*/
const emitter = (effect, height) => build(new Entity()
.add(placed(new Vector3(FIRE_POSITION.x, FIRE_POSITION.y + height, FIRE_POSITION.z)))
.add(effect));
return {
flame: emitter(effects.flame, 0.16),
smoke: emitter(effects.smoke, 0.16),
embers: emitter(effects.embers, 0.16),
soot: emitter(effects.soot, 0.16),
effects: [effects.flame, effects.smoke, effects.embers, effects.soot],
firelight,
entities,
};
}
/**
* Flicker the firelight using three incommensurable sine waves so the light on the stones and logs
* dances along with the flame particles.
*
* @param {BonfireWorld} world
* @param {number} time seconds since start
*/
export function animate_bonfire(world, time) {
const wobble =
Math.sin(time * 8.7) * 0.13
+ Math.sin(time * 3.1 + 1.7) * 0.09
+ Math.sin(time * 17.3 + 0.4) * 0.05;
world.firelight.intensity.set(FIRELIGHT_INTENSITY * (1 + wobble));
}
// -------------------------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------------------------
/**
* @param {Vector3} position
* @param {Quaternion} [rotation]
* @returns {Transform64}
*/
function placed(position, rotation = new Quaternion()) {
const transform = new Transform64();
transform.setTranslation(position.x, position.y, position.z);
transform.setRotation(rotation.x, rotation.y, rotation.z, rotation.w);
transform.updateMatrix();
return transform;
}
/**
* Rotation that points local forward (+Z) along `direction`, which is how a directional `Light`
* entity takes its direction.
*
* @param {Vector3} direction unit vector
* @returns {Quaternion}
*/
function look_along(direction) {
const q = new Quaternion();
q.fromUnitVectors(Vector3.forward, direction);
q.normalize();
return q;
}
/**
* @param {MeshletGeometry} geometry
* @param {StandardShadeMaterial} material_instance
* @returns {ShadedGeometry}
*/
function shaded(geometry, material_instance) {
const component = new ShadedGeometry();
component.geometry = geometry;
component.material = material_instance;
return component;
}
/**
* @param {object} geometry
* @returns {MeshletGeometry}
*/
function meshlets(geometry) {
const built = new MeshletGeometry();
meshlet_geometry_build_from_geometry(geometry, built);
return built;
}
/**
* @param {Color} diffuse
* @param {object} [options]
* @param {number} [options.roughness]
* @returns {StandardShadeMaterial}
*/
function material(diffuse, { roughness = 0.8 } = {}) {
const result = new StandardShadeMaterial();
result.diffuse_color.copy(diffuse);
result.roughness_factor = roughness;
result.metallic_factor = 0;
result.transparency_mode = TransparencyMode.Opaque;
result.draw_side = ShadeDrawSide.Double;
return result;
}
import ConcurrentExecutor from "@woosh/meep-engine/src/core/process/executor/ConcurrentExecutor.js";
import Vector3 from "@woosh/meep-engine/src/core/geom/Vector3.js";
import { DEG_TO_RAD } from "@woosh/meep-engine/src/core/math/DEG_TO_RAD.js";
import { AssetManager } from "@woosh/meep-engine/src/engine/asset/AssetManager.js";
import { GameAssetType } from "@woosh/meep-engine/src/engine/asset/GameAssetType.js";
import { ImageRGBADataLoader } from "@woosh/meep-engine/src/engine/asset/loaders/image/ImageRGBADataLoader.js";
import { EntityComponentDataset } from "@woosh/meep-engine/src/engine/ecs/EntityComponentDataset.js";
import { EntityManager } from "@woosh/meep-engine/src/engine/ecs/EntityManager.js";
import { GPUParticleEmitterSystem } from "@woosh/meep-engine/src/engine/graphics3/GPUParticleEmitterSystem.js";
import { GraphicsEngine } from "@woosh/meep-engine/src/engine/graphics3/GraphicsEngine.js";
import { LightSystem } from "@woosh/meep-engine/src/engine/graphics3/LightSystem.js";
import { ShadedGeometrySystem } from "@woosh/meep-engine/src/engine/graphics3/ShadedGeometrySystem.js";
import { OrbitalCameraController } from "@woosh/meep-engine/src/shade/renderer/camera/orbital/OrbitalCameraController.js";
import { create_frame_loop } from "@woosh/meep-engine/src/shade/renderer/create_frame_loop.js";
import { Scene } from "@woosh/meep-engine/src/shade/renderer/scene/Scene.js";
import { ShadeTransparencyMode } from "@woosh/meep-engine/src/shade/renderer/ShadeTransparencyMode.js";
import { BONFIRE_EFFECT_NAMES } from "./bonfire_effects.js";
import { bonfire_sprites } from "./bonfire_sprites.js";
import { BonfireWind } from "./bonfire_wind.js";
import { animate_bonfire, build_bonfire_world } from "./bonfire_world.js";
import { moonlit_environment } from "./moonlit_environment.js";
/**
* A moonlit bonfire built with an `EntityManager`, three rendering systems, and an entity per
* object in the scene.
*
* The fire is four entities, each carrying a `Transform64` and a `ParticleEffect`.
* `GPUParticleEmitterSystem` makes them burn the same way `ShadedGeometrySystem` makes the logs
* visible and `LightSystem` makes the moon shine:
*
* ```js
* new Entity()
* .add(new Transform64())
* .add(ParticleEffect.from({ ...effect, spawn_rate: 560 }))
* .build(dataset);
* ```
*/
/** Particles one press of "Stoke the fire" requests from the ember emitter. */
const STOKE_EMBERS = 900;
/** …and from the flame emitter, so the fire flares as it throws sparks. */
const STOKE_FLAME = 400;
/** How often GPU particle counters are copied back for the HUD overlay. */
const STATS_INTERVAL_FRAMES = 20;
/**
* Longest simulation step per frame (seconds), so a backgrounded tab resuming after several
* seconds does not advance the fire in one giant jump.
*/
const MAX_SIMULATION_STEP = 0.05;
/** Sliding window length (frames) used for the HUD frame-rate readout. */
const FPS_WINDOW_FRAMES = 60;
const CAMERA = {
position: new Vector3(2.9, 1.35, 3.7),
target: new Vector3(0, 1.0, 0),
fov_degrees: 48,
near: 0.05,
far: 300,
};
const element = id => document.getElementById(id);
const view_size = () => [Math.max(1, window.innerWidth), Math.max(1, window.innerHeight)];
async function main() {
// ---- World & ECS setup --------------------------------------------------------------------
// Entities and components are plain data, built before the WebGPU device starts.
const graphics = new GraphicsEngine();
const scene = new Scene();
// Assign the night sky before any system calls `GraphicsEngine.set_scene`, which otherwise
// installs a default daylight environment that would wash out the fire.
scene.lights.environment = moonlit_environment();
element("viewport").appendChild(graphics.viewport.el);
const [initial_width, initial_height] = view_size();
graphics.viewport.size.set(initial_width, initial_height);
const assets = new AssetManager({ context: {}, executor: new ConcurrentExecutor() });
await assets.registerLoader(GameAssetType.Image, new ImageRGBADataLoader());
assets.startup();
const dataset = new EntityComponentDataset();
const entities = new EntityManager();
const particles = new GPUParticleEmitterSystem(graphics, scene, assets);
entities.addSystem(new ShadedGeometrySystem(graphics, scene));
entities.addSystem(new LightSystem(graphics, scene));
entities.addSystem(particles);
entities.attachDataset(dataset);
await new Promise((resolve, reject) => entities.startup(resolve, reject));
const sprites = bonfire_sprites();
const world = await build_bonfire_world(dataset, { sprites });
// ---- Camera & UI controls -----------------------------------------------------------------
const camera = graphics.camera.camera;
camera.near = CAMERA.near;
camera.far = CAMERA.far;
camera.fov = CAMERA.fov_degrees * DEG_TO_RAD;
// Attach to `document.body` (which has `tabindex="0"`) so keyboard movement receives key events.
const controller = new OrbitalCameraController(camera, document.body);
controller.look(CAMERA.position, CAMERA.target);
controller.movement_speed_scale = 2;
document.body.focus();
for (let i = 0; i < BONFIRE_EFFECT_NAMES.length; i++) {
const id = BONFIRE_EFFECT_NAMES[i];
const checkbox = element(id);
checkbox.addEventListener("change", () => {
// Toggle an effect by writing `emitting` on its component. `emitting` gates the
// authored `spawn_rate` without clearing it, and the system picks up the change on the
// next frame while already-alive particles finish their lifetimes naturally.
world.effects[i].emitting = checkbox.checked;
});
}
element("stoke").addEventListener("click", () => {
// Queue a one-shot burst on an entity. The request is handed to the GPU on the next frame
// through the same spawn path as continuous emission.
particles.burst(world.embers, STOKE_EMBERS);
particles.burst(world.flame, STOKE_FLAME);
});
const weather = new BonfireWind();
// ---- WebGPU device & renderer -------------------------------------------------------------
graphics.on.contextFailed.add(failure => show_message(
"There will be no rendering.\n\n" + failure.message
+ "\n\nThe page needs WebGPU with subgroups and immediate data, over http://localhost — "
+ "a secure context, and not file://."
));
try {
await graphics.start();
} catch (e) {
console.error(e);
return;
}
const renderer = graphics.renderer;
// GPU particles render inside the AVBOIT transparency pipeline alongside transparent meshes.
renderer.transparency_mode = ShadeTransparencyMode.AVBOIT;
// Keep exposure fixed so auto-exposure does not chase the flickering fire against the dark sky.
renderer.feature_automatic_exposure_enabled = false;
renderer.exposure_compensation = 0;
const device = renderer.graphics.device;
/** @type {(() => void)|null} */
let stop_loop = null;
device.lost?.then(info => {
stop_loop?.();
show_message("The GPU device was lost.\n\n" + (info?.message ?? ""));
});
// ---- Frame loop ---------------------------------------------------------------------------
let stats_pending = false;
let frame_index = 0;
let time = 0;
/**
* @param {number} raw_dt unclamped frame delta in seconds
*/
function frame(raw_dt) {
record_frame_time(raw_dt);
const dt = Math.min(raw_dt, MAX_SIMULATION_STEP);
time += dt;
const [width, height] = view_size();
if (width !== graphics.viewport.size.x || height !== graphics.viewport.size.y) {
graphics.viewport.size.set(width, height);
}
animate_bonfire(world, time);
controller.update();
// Tick every ECS system. `GPUParticleEmitterSystem` syncs each entity's `ParticleEffect`
// component with its GPU emitter.
entities.update(dt);
// Advance the gust and write the wind velocity onto the scene's wind field before rendering.
weather.blow(dt, renderer.scenes.obtain(scene).wind);
graphics.render(dt);
frame_index++;
if (!stats_pending && frame_index % STATS_INTERVAL_FRAMES === 0) {
capture_stats();
}
}
/**
* Read the GPU particle counters back asynchronously for the HUD overlay.
*/
function capture_stats() {
const system = renderer.particles(renderer.scenes.obtain(scene));
stats_pending = true;
system.read_stats().then(stats => {
stats_pending = false;
write_hud({
fps: current_fps(),
alive: stats.alive,
dead: stats.dead,
emitters: particles.emitters.length,
});
}).catch(e => {
stats_pending = false;
console.error(e);
});
}
/**
* Run one frame, stopping the loop on the first unhandled error so a lost device does not
* spam the console on every subsequent animation frame.
*
* @param {number} dt
*/
function guarded_frame(dt) {
try {
frame(dt);
} catch (e) {
stop_loop?.();
console.error(e);
show_message("The frame failed and the loop has stopped.\n\n" + e.message);
}
}
stop_loop = create_frame_loop(dt => guarded_frame(dt));
}
// ---- HUD & status helpers ---------------------------------------------------------------------
const frame_times = new Float64Array(FPS_WINDOW_FRAMES);
let frame_time_count = 0;
let frame_time_cursor = 0;
/**
* Record the unclamped frame interval so the FPS readout reflects actual frame times.
* @param {number} raw_dt seconds
*/
function record_frame_time(raw_dt) {
if (!(raw_dt > 0) || !Number.isFinite(raw_dt)) {
return;
}
frame_times[frame_time_cursor] = raw_dt;
frame_time_cursor = (frame_time_cursor + 1) % frame_times.length;
frame_time_count = Math.min(frame_time_count + 1, frame_times.length);
}
/**
* Reciprocal of the mean frame time over the sliding window.
* @returns {number}
*/
function current_fps() {
if (frame_time_count === 0) {
return 0;
}
let total = 0;
for (let i = 0; i < frame_time_count; i++) {
total += frame_times[i];
}
return frame_time_count / total;
}
/**
* @param {{fps: number, alive: number, dead: number, emitters: number}} stats
*/
function write_hud({ fps, alive, dead, emitters }) {
const put = (id, value) => {
const node = element(id);
if (node !== null) {
node.textContent = value;
}
};
put("stat-fps", fps.toFixed(0));
put("stat-alive", alive.toLocaleString());
put("stat-dead", dead.toLocaleString());
put("stat-emitters", String(emitters));
}
function show_message(text) {
const node = element("message");
if (node !== null) {
node.textContent = text;
}
}
main().catch(e => {
console.error(e);
show_message("The bonfire example failed to start — see the console.\n\n" + e.message);
});
import Vector3 from "@woosh/meep-engine/src/core/geom/Vector3.js";
import { Sampler2D } from "@woosh/meep-engine/src/engine/graphics/texture/sampler/Sampler2D.js";
import { sampler2d_to_f16 } from "@woosh/meep-engine/src/engine/graphics/texture/sampler/sampler2d_to_f16.js";
import { octahedral_z_pole_uv_to_direction } from "@woosh/meep-engine/src/shade/renderer/light/environment/octahedral_z_pole_uv_to_direction.js";
import { sampler2d_octahedral_skirt_fill } from "@woosh/meep-engine/src/shade/renderer/light/environment/sampler2d_octahedral_skirt_fill.js";
import { ColorSpace } from "@woosh/meep-engine/src/shade/renderer/texture/ColorSpace.js";
import { ShadeImage } from "@woosh/meep-engine/src/shade/renderer/texture/source/ShadeImage.js";
import { ShadeTexture } from "@woosh/meep-engine/src/shade/renderer/texture/ShadeTexture.js";
/**
* Procedural moonlit night sky in octahedral projection for `scene.lights.environment`.
*
* A dark night sky roughly three orders of magnitude dimmer than daylight gives the bonfire room
* to be the brightest source in the frame, while tinting the upper smoke plume cool blue once it
* rises above the warm reach of the firelight.
*/
/** Side length of the octahedral map in texels, including its one-texel skirt. */
const RESOLUTION = 128;
/**
* Unit direction toward the moon (+Y up). The directional moon light in `bonfire_world.js` shines
* along the opposite vector so shadows fall away from the moon in the sky.
*
* @type {Vector3}
*/
export const MOON_DIRECTION = new Vector3(-0.42, 0.62, -0.66).normalize();
/** Angular radius of the moon disc in radians. */
const MOON_RADIUS = 0.075;
/** @type {Vector3} */
const SCRATCH_DIRECTION = new Vector3();
/**
* Linear RGB sky radiance arriving from unit direction `(x, y, z)`.
*
* @param {number[]} out rgb
* @param {number} x
* @param {number} y +Y is up
* @param {number} z
*/
function moonlit_radiance(out, x, y, z) {
const alignment = x * MOON_DIRECTION.x + y * MOON_DIRECTION.y + z * MOON_DIRECTION.z;
const angle = Math.acos(Math.min(1, Math.max(-1, alignment)));
if (y >= 0) {
// Upper hemisphere: deep zenith lifting toward the horizon.
const horizon = Math.pow(1 - y, 4);
out[0] = 0.0035 + horizon * 0.011;
out[1] = 0.0052 + horizon * 0.014;
out[2] = 0.0115 + horizon * 0.020;
} else {
// Lower hemisphere: dim ground bounce that softly fills the underside of the smoke.
const t = Math.pow(1 + y, 2);
out[0] = 0.0016 + t * 0.0042;
out[1] = 0.0017 + t * 0.0044;
out[2] = 0.0022 + t * 0.0055;
}
// Atmospheric halo around the moon, plus the soft-edged lunar disc.
const halo = Math.exp(-angle * 5.5) * 0.05;
out[0] += halo * 0.75;
out[1] += halo * 0.82;
out[2] += halo;
if (angle < MOON_RADIUS) {
const disc = 1 - Math.pow(angle / MOON_RADIUS, 3);
out[0] += disc * 5.2;
out[1] += disc * 5.6;
out[2] += disc * 6.4;
}
}
/**
* Build the octahedral half-float environment texture for `scene.lights.environment`.
*
* @returns {ShadeTexture}
*/
export function moonlit_environment() {
const sampler = Sampler2D.float32(4, RESOLUTION, RESOLUTION);
const data = sampler.data;
const rgb = [0, 0, 0];
const interior = RESOLUTION - 2;
for (let v = 1; v <= interior; v++) {
for (let u = 1; u <= interior; u++) {
octahedral_z_pole_uv_to_direction(
SCRATCH_DIRECTION,
(u - 0.5) / interior,
(v - 0.5) / interior
);
moonlit_radiance(rgb, SCRATCH_DIRECTION.x, SCRATCH_DIRECTION.y, SCRATCH_DIRECTION.z);
const offset = (v * RESOLUTION + u) * 4;
data[offset] = rgb[0];
data[offset + 1] = rgb[1];
data[offset + 2] = rgb[2];
data[offset + 3] = 1;
}
}
sampler2d_octahedral_skirt_fill(sampler);
const image = ShadeImage.fromSampler2D(sampler2d_to_f16(sampler));
image.color_space = ColorSpace.LinearSRGB;
return ShadeTexture.from(image);
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
<title>Bonfire · Meep</title>
<style>
:root {
color-scheme: dark;
--panel: rgba(14, 15, 20, 0.78);
--edge: rgba(255, 255, 255, 0.1);
--text: #e7e4de;
--muted: #9a978f;
--ember: #ff9a3c;
}
* {
box-sizing: border-box;
}
html, body {
margin: 0;
height: 100%;
overflow: hidden;
background: #05060a;
color: var(--text);
font: 13px/1.5 ui-sans-serif, system-ui, "Segoe UI", Roboto, sans-serif;
}
#viewport {
position: fixed;
inset: 0;
overflow: hidden;
}
#viewport canvas {
display: block;
}
.panel {
position: fixed;
background: var(--panel);
border: 1px solid var(--edge);
border-radius: 10px;
padding: 12px 14px;
backdrop-filter: blur(8px);
}
#controls {
top: 14px;
left: 14px;
width: 220px;
}
#stats {
top: 14px;
right: 14px;
min-width: 190px;
font-variant-numeric: tabular-nums;
}
#hint {
bottom: 14px;
left: 14px;
margin: 0;
color: var(--muted);
max-width: 460px;
}
h1 {
margin: 0 0 2px;
font-size: 13px;
font-weight: 600;
letter-spacing: 0.02em;
}
h1 span {
color: var(--ember);
}
p.sub {
margin: 0 0 10px;
color: var(--muted);
font-size: 12px;
}
label.row {
display: flex;
align-items: center;
gap: 8px;
padding: 3px 0;
cursor: pointer;
}
label.row input {
accent-color: var(--ember);
margin: 0;
}
label.row span {
flex: 1;
}
button {
width: 100%;
margin-top: 10px;
padding: 7px 10px;
font: inherit;
font-weight: 600;
color: #1a1206;
background: var(--ember);
border: 0;
border-radius: 7px;
cursor: pointer;
}
button:hover {
filter: brightness(1.1);
}
dl {
display: grid;
grid-template-columns: auto 1fr;
gap: 2px 12px;
margin: 0;
}
dt {
color: var(--muted);
}
dd {
margin: 0;
text-align: right;
}
#message {
position: fixed;
inset: 0;
display: grid;
place-content: center;
padding: 40px;
text-align: center;
white-space: pre-wrap;
color: #ffb4a4;
pointer-events: none;
}
kbd {
padding: 1px 5px;
border: 1px solid var(--edge);
border-radius: 4px;
background: rgba(255, 255, 255, 0.06);
font: inherit;
font-size: 11px;
}
</style>
</head>
<body tabindex="0">
<div id="viewport"></div>
<section class="panel" id="controls">
<h1>Bonfire · <span>ECS particles</span></h1>
<p class="sub">Four <code>ParticleEffect</code> entities</p>
<label class="row"><input type="checkbox" id="flame" checked><span>Flame</span></label>
<label class="row"><input type="checkbox" id="smoke" checked><span>Smoke</span></label>
<label class="row"><input type="checkbox" id="embers" checked><span>Embers</span></label>
<label class="row"><input type="checkbox" id="soot" checked><span>Soot</span></label>
<button id="stoke">Stoke the fire</button>
</section>
<section class="panel hud" id="stats">
<dl>
<dt>fps</dt>
<dd id="stat-fps">—</dd>
<dt>alive</dt>
<dd id="stat-alive">—</dd>
<dt>free</dt>
<dd id="stat-dead">—</dd>
<dt>emitters</dt>
<dd id="stat-emitters">—</dd>
</dl>
</section>
<p class="panel" id="hint">
Drag to orbit, <kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> to move, wheel to zoom. The
checkboxes write <code>emitting</code> on a component; the button asks an entity for a burst.
Both go through the ECS — nothing here touches the scene graph.
</p>
<div id="message"></div>
<script type="module" src="./src/main.js"></script>
</body>
</html>
{
"name": "@meep-examples/bonfire",
"version": "0.1.0",
"private": true,
"type": "module",
"description": "A moonlit bonfire built from four GPU ParticleEffect entities in a gusty wind.",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@woosh/meep-engine": "3.57.0"
},
"devDependencies": {
"@rollup/plugin-strip": "^3.0.4",
"vite": "^8.0.13"
}
}
# Bonfire — GPU particles as ECS entities
A moonlit fire pit demonstrating GPU `ParticleEffect` entities in the ECS. Every visible object in
the scene is an entity carrying a `Transform64` and one additional component:
- **flame, smoke, embers, soot** — `ParticleEffect`
- **ground, logs, stones** — `ShadedGeometry`
- **moon, firelight** — `Light`
Three systems (`ShadedGeometrySystem`, `LightSystem`, and `GPUParticleEmitterSystem`) observe those
component pairs and populate one `Scene`:
```js
new Entity()
.add(new Transform64())
.add(ParticleEffect.from({ ...effect, spawn_rate: 560 }))
.build(dataset);
```
## Run
```bash
npm install
npm run dev
```
Open `/demo.html` in a WebGPU-capable browser. `npm run build` writes the production bundle to
`../../public/examples/bonfire/`.
## What is on screen
| Entity | Component | Role |
|---|---|---|
| `flame` | `ParticleEffect` | Additive billboards, short-lived, stirred by simplex noise and pinched inward so the column narrows |
| `smoke` | `ParticleEffect` | Alpha-blended, soft-depth, expanding, firelit at the base and moonlit higher up (`spawn_rate: 40`) |
| `embers` | `ParticleEffect` | Small additive cinders, buoyant then falling, flickering on their own clock |
| `soot` | `ParticleEffect` | Dark `FACING_WORLD` flakes that ride the plume and tumble through the firelight via `sampleRotation` and `quatIntegrate` |
| ground, logs, stones | `ShadedGeometry` | Geometry for the firelight to illuminate and for `soft_depth` smoke to fade against |
| `moon` | `Light` (directional) | Cool directional light aligned with the moon in the procedural sky |
| `firelight` | `Light` (point) | Warm point light inside the flame, flickered every frame |
The night sky (`src/moonlit_environment.js`) and the four particle sprites (`src/bonfire_sprites.js`)
are generated procedurally at startup, and the four particle effects live as saved effect documents
in `src/effects/*.json` loaded by `src/bonfire_effects.js`.
## Controls
- **Effect checkboxes** write `effect.emitting` directly on each entity's `ParticleEffect`
component. `emitting` gates `spawn_rate` without clearing it, and already-alive particles finish
their lifetimes naturally.
- **"Stoke the fire"** calls `particles.burst(entity, count)` on the ember and flame entities to
queue a one-shot GPU burst.
## Wind and lighting
`src/bonfire_wind.js` combines a steady breeze with a `GlobalFluidEffector` gust and publishes the
result to the scene's wind field each frame via `GPUSceneWind#set_far_velocity`. All four effects
sample the wind at each particle's position using the `wind` (`SAMPLE_WIND`) node:
- **Flame** integrates half the wind into its position so the tongues lean without blowing off the
logs.
- **Embers and soot** integrate the full wind so they stream downwind and scatter in gusts.
- **Smoke** relaxes its velocity toward the wind through drag so the column leaves the fire upright
and bends over as it rises.
Smoke and soot enable `lighting: true` (`EMITTER_FLAG.LIGHTING`), treating their colour gradients as
diffuse albedo illuminated by the froxel lighting volume (warm firelight below, cool moonlight
above). Flame and embers are unlit and additive.
import { defineConfig } from "vite";
import { copyFileSync, existsSync, mkdirSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { resolve, dirname } from "node:path";
import strip from "@rollup/plugin-strip";
const __dirname = dirname(fileURLToPath(import.meta.url));
export default defineConfig({
root: __dirname,
publicDir: false,
// Keep the engine's imported asset URLs intact during development.
optimizeDeps: {
exclude: ["@woosh/meep-engine"],
include: ["dat.gui", "opentype.js"],
},
plugins: [
{
name: "copy-thumbnail",
apply: "build",
closeBundle() {
const thumb = resolve(__dirname, "thumbnail.png");
const dst = resolve(__dirname, "../../public/examples/bonfire");
if (existsSync(thumb)) {
mkdirSync(dst, { recursive: true });
copyFileSync(thumb, resolve(dst, "thumbnail.png"));
}
},
},
],
base: "./",
build: {
outDir: resolve(__dirname, "../../public/examples/bonfire"),
emptyOutDir: false,
rollupOptions: {
input: resolve(__dirname, "demo.html"),
plugins: [
{
// Remove assert statements from the production build.
...strip(),
apply: "build",
},
],
},
target: "es2022",
},
});
thumbnail.png · 190.8 KB