particles.glsl 17 KB

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