particles.glsl 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. #[compute]
  2. #version 450
  3. #VERSION_DEFINES
  4. layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
  5. #define SAMPLER_NEAREST_CLAMP 0
  6. #define SAMPLER_LINEAR_CLAMP 1
  7. #define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
  8. #define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
  9. #define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
  10. #define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
  11. #define SAMPLER_NEAREST_REPEAT 6
  12. #define SAMPLER_LINEAR_REPEAT 7
  13. #define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
  14. #define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
  15. #define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
  16. #define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
  17. #define SDF_MAX_LENGTH 16384.0
  18. /* SET 0: GLOBAL DATA */
  19. layout(set = 0, binding = 1) uniform sampler material_samplers[12];
  20. layout(set = 0, binding = 2, std430) restrict readonly buffer GlobalShaderUniformData {
  21. vec4 data[];
  22. }
  23. global_shader_uniforms;
  24. /* Set 1: FRAME AND PARTICLE DATA */
  25. // a frame history is kept for trail deterministic behavior
  26. #define MAX_ATTRACTORS 32
  27. #define ATTRACTOR_TYPE_SPHERE 0
  28. #define ATTRACTOR_TYPE_BOX 1
  29. #define ATTRACTOR_TYPE_VECTOR_FIELD 2
  30. struct Attractor {
  31. mat4 transform;
  32. vec3 extents; //exents or radius
  33. uint type;
  34. uint texture_index; //texture index for vector field
  35. float strength;
  36. float attenuation;
  37. float directionality;
  38. };
  39. #define MAX_COLLIDERS 32
  40. #define COLLIDER_TYPE_SPHERE 0
  41. #define COLLIDER_TYPE_BOX 1
  42. #define COLLIDER_TYPE_SDF 2
  43. #define COLLIDER_TYPE_HEIGHT_FIELD 3
  44. #define COLLIDER_TYPE_2D_SDF 4
  45. struct Collider {
  46. mat4 transform;
  47. vec3 extents; //exents or radius
  48. uint type;
  49. uint texture_index; //texture index for vector field
  50. float scale;
  51. uint pad[2];
  52. };
  53. struct FrameParams {
  54. bool emitting;
  55. float system_phase;
  56. float prev_system_phase;
  57. uint cycle;
  58. float explosiveness;
  59. float randomness;
  60. float time;
  61. float delta;
  62. uint frame;
  63. uint pad0;
  64. uint pad1;
  65. uint pad2;
  66. uint random_seed;
  67. uint attractor_count;
  68. uint collider_count;
  69. float particle_size;
  70. mat4 emission_transform;
  71. Attractor attractors[MAX_ATTRACTORS];
  72. Collider colliders[MAX_COLLIDERS];
  73. };
  74. layout(set = 1, binding = 0, std430) restrict buffer FrameHistory {
  75. FrameParams data[];
  76. }
  77. frame_history;
  78. #define PARTICLE_FLAG_ACTIVE uint(1)
  79. #define PARTICLE_FLAG_STARTED uint(2)
  80. #define PARTICLE_FLAG_TRAILED uint(4)
  81. #define PARTICLE_FRAME_MASK uint(0xFFFF)
  82. #define PARTICLE_FRAME_SHIFT uint(16)
  83. struct ParticleData {
  84. mat4 xform;
  85. vec3 velocity;
  86. uint flags;
  87. vec4 color;
  88. vec4 custom;
  89. #ifdef USERDATA1_USED
  90. vec4 userdata1;
  91. #endif
  92. #ifdef USERDATA2_USED
  93. vec4 userdata2;
  94. #endif
  95. #ifdef USERDATA3_USED
  96. vec4 userdata3;
  97. #endif
  98. #ifdef USERDATA4_USED
  99. vec4 userdata4;
  100. #endif
  101. #ifdef USERDATA5_USED
  102. vec4 userdata5;
  103. #endif
  104. #ifdef USERDATA6_USED
  105. vec4 userdata6;
  106. #endif
  107. };
  108. layout(set = 1, binding = 1, std430) restrict buffer Particles {
  109. ParticleData data[];
  110. }
  111. particles;
  112. #define EMISSION_FLAG_HAS_POSITION 1
  113. #define EMISSION_FLAG_HAS_ROTATION_SCALE 2
  114. #define EMISSION_FLAG_HAS_VELOCITY 4
  115. #define EMISSION_FLAG_HAS_COLOR 8
  116. #define EMISSION_FLAG_HAS_CUSTOM 16
  117. struct ParticleEmission {
  118. mat4 xform;
  119. vec3 velocity;
  120. uint flags;
  121. vec4 color;
  122. vec4 custom;
  123. };
  124. layout(set = 1, binding = 2, std430) restrict buffer SourceEmission {
  125. int particle_count;
  126. uint pad0;
  127. uint pad1;
  128. uint pad2;
  129. ParticleEmission data[];
  130. }
  131. src_particles;
  132. layout(set = 1, binding = 3, std430) restrict buffer DestEmission {
  133. int particle_count;
  134. int particle_max;
  135. uint pad1;
  136. uint pad2;
  137. ParticleEmission data[];
  138. }
  139. dst_particles;
  140. /* SET 2: COLLIDER/ATTRACTOR TEXTURES */
  141. #define MAX_3D_TEXTURES 7
  142. layout(set = 2, binding = 0) uniform texture3D sdf_vec_textures[MAX_3D_TEXTURES];
  143. layout(set = 2, binding = 1) uniform texture2D height_field_texture;
  144. /* SET 3: MATERIAL */
  145. #ifdef MATERIAL_UNIFORMS_USED
  146. layout(set = 3, binding = 0, std140) uniform MaterialUniforms{
  147. #MATERIAL_UNIFORMS
  148. } material;
  149. #endif
  150. layout(push_constant, std430) uniform Params {
  151. float lifetime;
  152. bool clear;
  153. uint total_particles;
  154. uint trail_size;
  155. bool use_fractional_delta;
  156. bool sub_emitter_mode;
  157. bool can_emit;
  158. bool trail_pass;
  159. }
  160. params;
  161. uint hash(uint x) {
  162. x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
  163. x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
  164. x = (x >> uint(16)) ^ x;
  165. return x;
  166. }
  167. bool emit_subparticle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom, uint p_flags) {
  168. if (!params.can_emit) {
  169. return false;
  170. }
  171. bool valid = false;
  172. int dst_index = atomicAdd(dst_particles.particle_count, 1);
  173. if (dst_index >= dst_particles.particle_max) {
  174. atomicAdd(dst_particles.particle_count, -1);
  175. return false;
  176. }
  177. dst_particles.data[dst_index].xform = p_xform;
  178. dst_particles.data[dst_index].velocity = p_velocity;
  179. dst_particles.data[dst_index].color = p_color;
  180. dst_particles.data[dst_index].custom = p_custom;
  181. dst_particles.data[dst_index].flags = p_flags;
  182. return true;
  183. }
  184. vec3 safe_normalize(vec3 direction) {
  185. const float EPSILON = 0.001;
  186. if (length(direction) < EPSILON) {
  187. return vec3(0.0);
  188. }
  189. return normalize(direction);
  190. }
  191. #GLOBALS
  192. void main() {
  193. uint particle = gl_GlobalInvocationID.x;
  194. if (params.trail_size > 1) {
  195. if (params.trail_pass) {
  196. if (particle >= params.total_particles * (params.trail_size - 1)) {
  197. return;
  198. }
  199. particle += (particle / (params.trail_size - 1)) + 1;
  200. } else {
  201. if (particle >= params.total_particles) {
  202. return;
  203. }
  204. particle *= params.trail_size;
  205. }
  206. }
  207. if (particle >= params.total_particles * params.trail_size) {
  208. return; //discard
  209. }
  210. uint index = particle / params.trail_size;
  211. uint frame = (particle % params.trail_size);
  212. #define FRAME frame_history.data[frame]
  213. #define PARTICLE particles.data[particle]
  214. bool apply_forces = true;
  215. bool apply_velocity = true;
  216. float local_delta = FRAME.delta;
  217. float mass = 1.0;
  218. bool restart = false;
  219. bool restart_position = false;
  220. bool restart_rotation_scale = false;
  221. bool restart_velocity = false;
  222. bool restart_color = false;
  223. bool restart_custom = false;
  224. if (params.clear) {
  225. PARTICLE.color = vec4(1.0);
  226. PARTICLE.custom = vec4(0.0);
  227. PARTICLE.velocity = vec3(0.0);
  228. PARTICLE.flags = 0;
  229. PARTICLE.xform = mat4(
  230. vec4(1.0, 0.0, 0.0, 0.0),
  231. vec4(0.0, 1.0, 0.0, 0.0),
  232. vec4(0.0, 0.0, 1.0, 0.0),
  233. vec4(0.0, 0.0, 0.0, 1.0));
  234. }
  235. //clear started flag if set
  236. if (params.trail_pass) {
  237. //trail started
  238. uint src_idx = index * params.trail_size;
  239. if (bool(particles.data[src_idx].flags & PARTICLE_FLAG_STARTED)) {
  240. //save start conditions for trails
  241. PARTICLE.color = particles.data[src_idx].color;
  242. PARTICLE.custom = particles.data[src_idx].custom;
  243. PARTICLE.velocity = particles.data[src_idx].velocity;
  244. PARTICLE.flags = PARTICLE_FLAG_TRAILED | ((frame_history.data[0].frame & PARTICLE_FRAME_MASK) << PARTICLE_FRAME_SHIFT); //mark it as trailed, save in which frame it will start
  245. PARTICLE.xform = particles.data[src_idx].xform;
  246. }
  247. if (!bool(particles.data[src_idx].flags & PARTICLE_FLAG_ACTIVE)) {
  248. // Disable the entire trail if the parent is no longer active.
  249. PARTICLE.flags = 0;
  250. return;
  251. }
  252. if (bool(PARTICLE.flags & PARTICLE_FLAG_TRAILED) && ((PARTICLE.flags >> PARTICLE_FRAME_SHIFT) == (FRAME.frame & PARTICLE_FRAME_MASK))) { //check this is trailed and see if it should start now
  253. // we just assume that this is the first frame of the particle, the rest is deterministic
  254. PARTICLE.flags = PARTICLE_FLAG_ACTIVE | (particles.data[src_idx].flags & (PARTICLE_FRAME_MASK << PARTICLE_FRAME_SHIFT));
  255. return; //- this appears like it should be correct, but it seems not to be.. wonder why.
  256. }
  257. } else {
  258. PARTICLE.flags &= ~PARTICLE_FLAG_STARTED;
  259. }
  260. bool collided = false;
  261. vec3 collision_normal = vec3(0.0);
  262. float collision_depth = 0.0;
  263. vec3 attractor_force = vec3(0.0);
  264. #if !defined(DISABLE_VELOCITY)
  265. if (bool(PARTICLE.flags & PARTICLE_FLAG_ACTIVE)) {
  266. PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta;
  267. }
  268. #endif
  269. if (!params.trail_pass && params.sub_emitter_mode) {
  270. if (!bool(PARTICLE.flags & PARTICLE_FLAG_ACTIVE)) {
  271. int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
  272. if (src_index >= 0) {
  273. PARTICLE.flags = (PARTICLE_FLAG_ACTIVE | PARTICLE_FLAG_STARTED | (FRAME.cycle << PARTICLE_FRAME_SHIFT));
  274. restart = true;
  275. if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) {
  276. PARTICLE.xform[3] = src_particles.data[src_index].xform[3];
  277. } else {
  278. PARTICLE.xform[3] = vec4(0, 0, 0, 1);
  279. restart_position = true;
  280. }
  281. if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) {
  282. PARTICLE.xform[0] = src_particles.data[src_index].xform[0];
  283. PARTICLE.xform[1] = src_particles.data[src_index].xform[1];
  284. PARTICLE.xform[2] = src_particles.data[src_index].xform[2];
  285. } else {
  286. PARTICLE.xform[0] = vec4(1, 0, 0, 0);
  287. PARTICLE.xform[1] = vec4(0, 1, 0, 0);
  288. PARTICLE.xform[2] = vec4(0, 0, 1, 0);
  289. restart_rotation_scale = true;
  290. }
  291. if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) {
  292. PARTICLE.velocity = src_particles.data[src_index].velocity;
  293. } else {
  294. PARTICLE.velocity = vec3(0);
  295. restart_velocity = true;
  296. }
  297. if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) {
  298. PARTICLE.color = src_particles.data[src_index].color;
  299. } else {
  300. PARTICLE.color = vec4(1);
  301. restart_color = true;
  302. }
  303. if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) {
  304. PARTICLE.custom = src_particles.data[src_index].custom;
  305. } else {
  306. PARTICLE.custom = vec4(0);
  307. restart_custom = true;
  308. }
  309. }
  310. }
  311. } else if (FRAME.emitting) {
  312. float restart_phase = float(index) / float(params.total_particles);
  313. if (FRAME.randomness > 0.0) {
  314. uint seed = FRAME.cycle;
  315. if (restart_phase >= FRAME.system_phase) {
  316. seed -= uint(1);
  317. }
  318. seed *= uint(params.total_particles);
  319. seed += uint(index);
  320. float random = float(hash(seed) % uint(65536)) / 65536.0;
  321. restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
  322. }
  323. restart_phase *= (1.0 - FRAME.explosiveness);
  324. if (FRAME.system_phase > FRAME.prev_system_phase) {
  325. // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
  326. if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
  327. restart = true;
  328. if (params.use_fractional_delta) {
  329. local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
  330. }
  331. }
  332. } else if (FRAME.delta > 0.0) {
  333. if (restart_phase >= FRAME.prev_system_phase) {
  334. restart = true;
  335. if (params.use_fractional_delta) {
  336. local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
  337. }
  338. } else if (restart_phase < FRAME.system_phase) {
  339. restart = true;
  340. if (params.use_fractional_delta) {
  341. local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
  342. }
  343. }
  344. }
  345. if (params.trail_pass) {
  346. restart = false;
  347. }
  348. if (restart) {
  349. PARTICLE.flags = FRAME.emitting ? (PARTICLE_FLAG_ACTIVE | PARTICLE_FLAG_STARTED | (FRAME.cycle << PARTICLE_FRAME_SHIFT)) : 0;
  350. restart_position = true;
  351. restart_rotation_scale = true;
  352. restart_velocity = true;
  353. restart_color = true;
  354. restart_custom = true;
  355. }
  356. }
  357. bool particle_active = bool(PARTICLE.flags & PARTICLE_FLAG_ACTIVE);
  358. uint particle_number = (PARTICLE.flags >> PARTICLE_FRAME_SHIFT) * uint(params.total_particles) + index;
  359. if (restart && particle_active) {
  360. #CODE : START
  361. }
  362. if (particle_active) {
  363. for (uint i = 0; i < FRAME.attractor_count; i++) {
  364. vec3 dir;
  365. float amount;
  366. vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.attractors[i].transform[3].xyz;
  367. vec3 local_pos = rel_vec * mat3(FRAME.attractors[i].transform);
  368. switch (FRAME.attractors[i].type) {
  369. case ATTRACTOR_TYPE_SPHERE: {
  370. dir = safe_normalize(rel_vec);
  371. float d = length(local_pos) / FRAME.attractors[i].extents.x;
  372. if (d > 1.0) {
  373. continue;
  374. }
  375. amount = max(0.0, 1.0 - d);
  376. } break;
  377. case ATTRACTOR_TYPE_BOX: {
  378. dir = safe_normalize(rel_vec);
  379. vec3 abs_pos = abs(local_pos / FRAME.attractors[i].extents);
  380. float d = max(abs_pos.x, max(abs_pos.y, abs_pos.z));
  381. if (d > 1.0) {
  382. continue;
  383. }
  384. amount = max(0.0, 1.0 - d);
  385. } break;
  386. case ATTRACTOR_TYPE_VECTOR_FIELD: {
  387. vec3 uvw_pos = (local_pos / FRAME.attractors[i].extents + 1.0) * 0.5;
  388. if (any(lessThan(uvw_pos, vec3(0.0))) || any(greaterThan(uvw_pos, vec3(1.0)))) {
  389. continue;
  390. }
  391. vec3 s = texture(sampler3D(sdf_vec_textures[FRAME.attractors[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).xyz * -2.0 + 1.0;
  392. dir = mat3(FRAME.attractors[i].transform) * safe_normalize(s); //revert direction
  393. amount = length(s);
  394. } break;
  395. }
  396. amount = pow(amount, FRAME.attractors[i].attenuation);
  397. dir = safe_normalize(mix(dir, FRAME.attractors[i].transform[2].xyz, FRAME.attractors[i].directionality));
  398. attractor_force -= amount * dir * FRAME.attractors[i].strength;
  399. }
  400. float particle_size = FRAME.particle_size;
  401. #ifdef USE_COLLISION_SCALE
  402. particle_size *= dot(vec3(length(PARTICLE.xform[0].xyz), length(PARTICLE.xform[1].xyz), length(PARTICLE.xform[2].xyz)), vec3(0.33333333333));
  403. #endif
  404. if (FRAME.collider_count == 1 && FRAME.colliders[0].type == COLLIDER_TYPE_2D_SDF) {
  405. //2D collision
  406. vec2 pos = PARTICLE.xform[3].xy;
  407. vec4 to_sdf_x = FRAME.colliders[0].transform[0];
  408. vec4 to_sdf_y = FRAME.colliders[0].transform[1];
  409. vec2 sdf_pos = vec2(dot(vec4(pos, 0, 1), to_sdf_x), dot(vec4(pos, 0, 1), to_sdf_y));
  410. vec4 sdf_to_screen = vec4(FRAME.colliders[0].extents, FRAME.colliders[0].scale);
  411. vec2 uv_pos = sdf_pos * sdf_to_screen.xy + sdf_to_screen.zw;
  412. if (all(greaterThan(uv_pos, vec2(0.0))) && all(lessThan(uv_pos, vec2(1.0)))) {
  413. vec2 pos2 = pos + vec2(0, particle_size);
  414. vec2 sdf_pos2 = vec2(dot(vec4(pos2, 0, 1), to_sdf_x), dot(vec4(pos2, 0, 1), to_sdf_y));
  415. float sdf_particle_size = distance(sdf_pos, sdf_pos2);
  416. float d = texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos).r * SDF_MAX_LENGTH;
  417. d -= sdf_particle_size;
  418. if (d < 0.0) {
  419. const float EPSILON = 0.001;
  420. vec2 n = normalize(vec2(
  421. texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos + vec2(EPSILON, 0.0)).r - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos - vec2(EPSILON, 0.0)).r,
  422. texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos + vec2(0.0, EPSILON)).r - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos - vec2(0.0, EPSILON)).r));
  423. collided = true;
  424. sdf_pos2 = sdf_pos + n * d;
  425. pos2 = vec2(dot(vec4(sdf_pos2, 0, 1), FRAME.colliders[0].transform[2]), dot(vec4(sdf_pos2, 0, 1), FRAME.colliders[0].transform[3]));
  426. n = pos - pos2;
  427. collision_normal = normalize(vec3(n, 0.0));
  428. collision_depth = length(n);
  429. }
  430. }
  431. } else {
  432. for (uint i = 0; i < FRAME.collider_count; i++) {
  433. vec3 normal;
  434. float depth;
  435. bool col = false;
  436. vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.colliders[i].transform[3].xyz;
  437. vec3 local_pos = rel_vec * mat3(FRAME.colliders[i].transform);
  438. switch (FRAME.colliders[i].type) {
  439. case COLLIDER_TYPE_SPHERE: {
  440. float d = length(rel_vec) - (particle_size + FRAME.colliders[i].extents.x);
  441. if (d < 0.0) {
  442. col = true;
  443. depth = -d;
  444. normal = normalize(rel_vec);
  445. }
  446. } break;
  447. case COLLIDER_TYPE_BOX: {
  448. vec3 abs_pos = abs(local_pos);
  449. vec3 sgn_pos = sign(local_pos);
  450. if (any(greaterThan(abs_pos, FRAME.colliders[i].extents))) {
  451. //point outside box
  452. vec3 closest = min(abs_pos, FRAME.colliders[i].extents);
  453. vec3 rel = abs_pos - closest;
  454. depth = length(rel) - particle_size;
  455. if (depth < 0.0) {
  456. col = true;
  457. normal = mat3(FRAME.colliders[i].transform) * (normalize(rel) * sgn_pos);
  458. depth = -depth;
  459. }
  460. } else {
  461. //point inside box
  462. vec3 axis_len = FRAME.colliders[i].extents - abs_pos;
  463. // there has to be a faster way to do this?
  464. if (all(lessThan(axis_len.xx, axis_len.yz))) {
  465. normal = vec3(1, 0, 0);
  466. } else if (all(lessThan(axis_len.yy, axis_len.xz))) {
  467. normal = vec3(0, 1, 0);
  468. } else {
  469. normal = vec3(0, 0, 1);
  470. }
  471. col = true;
  472. depth = dot(normal * axis_len, vec3(1)) + particle_size;
  473. normal = mat3(FRAME.colliders[i].transform) * (normal * sgn_pos);
  474. }
  475. } break;
  476. case COLLIDER_TYPE_SDF: {
  477. vec3 apos = abs(local_pos);
  478. float extra_dist = 0.0;
  479. if (any(greaterThan(apos, FRAME.colliders[i].extents))) { //outside
  480. vec3 mpos = min(apos, FRAME.colliders[i].extents);
  481. extra_dist = distance(mpos, apos);
  482. }
  483. if (extra_dist > particle_size) {
  484. continue;
  485. }
  486. vec3 uvw_pos = (local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5;
  487. float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).r;
  488. s *= FRAME.colliders[i].scale;
  489. s += extra_dist;
  490. if (s < particle_size) {
  491. col = true;
  492. depth = particle_size - s;
  493. const float EPSILON = 0.001;
  494. normal = mat3(FRAME.colliders[i].transform) *
  495. normalize(
  496. vec3(
  497. texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r,
  498. texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, EPSILON, 0.0)).r,
  499. texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, 0.0, EPSILON)).r));
  500. }
  501. } break;
  502. case COLLIDER_TYPE_HEIGHT_FIELD: {
  503. vec3 local_pos_bottom = local_pos;
  504. local_pos_bottom.y -= particle_size;
  505. if (any(greaterThan(abs(local_pos_bottom), FRAME.colliders[i].extents))) {
  506. continue;
  507. }
  508. const float DELTA = 1.0 / 8192.0;
  509. vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5;
  510. float y = 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz).r;
  511. if (y > uvw_pos.y) {
  512. //inside heightfield
  513. vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
  514. vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
  515. vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents;
  516. normal = normalize(cross(pos1 - pos2, pos1 - pos3));
  517. float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y;
  518. col = true;
  519. depth = dot(normal, pos1) - dot(normal, local_pos_bottom);
  520. }
  521. } break;
  522. }
  523. if (col) {
  524. if (!collided) {
  525. collided = true;
  526. collision_normal = normal;
  527. collision_depth = depth;
  528. } else {
  529. vec3 c = collision_normal * collision_depth;
  530. c += normal * max(0.0, depth - dot(normal, c));
  531. collision_normal = normalize(c);
  532. collision_depth = length(c);
  533. }
  534. }
  535. }
  536. }
  537. }
  538. if (particle_active) {
  539. #CODE : PROCESS
  540. }
  541. PARTICLE.flags &= ~PARTICLE_FLAG_ACTIVE;
  542. if (particle_active) {
  543. PARTICLE.flags |= PARTICLE_FLAG_ACTIVE;
  544. }
  545. }