main.c 107 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226
  1. #include <gs.h>
  2. #include "render_pass/blur_pass.h"
  3. #include "render_pass/bright_filter_pass.h"
  4. #include "render_pass/composite_pass.h"
  5. #include "font/font.h"
  6. #include "font/font_data.c"
  7. #if (defined GS_PLATFORM_APPLE)
  8. _global const s32 g_window_width = 800;
  9. _global const s32 g_window_height = 600;
  10. #else
  11. _global const s32 g_window_width = 1258;
  12. _global const s32 g_window_height = 848;
  13. #endif
  14. _global const s32 g_texture_width = 1258 / 2;
  15. _global const s32 g_texture_height = 848 / 2;
  16. // 32 bit color structure
  17. typedef struct color_t
  18. {
  19. u8 r;
  20. u8 g;
  21. u8 b;
  22. u8 a;
  23. } color_t;
  24. typedef struct particle_t
  25. {
  26. u8 id;
  27. f32 life_time;
  28. gs_vec2 velocity;
  29. color_t color;
  30. b32 has_been_updated_this_frame;
  31. } particle_t;
  32. // Should have a hash map of glyph character to glyph metric
  33. // Globals
  34. _global gs_resource( gs_vertex_buffer ) g_vbo = {0};
  35. _global gs_resource( gs_index_buffer ) g_ibo = {0};
  36. _global gs_resource( gs_command_buffer ) g_cb = {0};
  37. _global gs_resource( gs_shader ) g_shader = {0};
  38. _global gs_resource( gs_uniform ) u_tex = {0};
  39. _global gs_resource( gs_uniform ) u_flip_y = {0};
  40. _global gs_resource( gs_texture ) g_tex = {0};
  41. _global gs_resource( gs_texture ) g_tex_ui = {0};
  42. _global gs_resource( gs_texture ) g_rt = {0};
  43. _global gs_resource( gs_frame_buffer ) g_fb = {0};
  44. _global blur_pass_t g_blur_pass = {0};
  45. _global bright_filter_pass_t g_bright_pass = {0};
  46. _global composite_pass_t g_composite_pass = {0};
  47. _global font_t g_font = {0};
  48. _global b32 g_app_running = true;
  49. // For now, all particle information will simply be a value to determine its material id
  50. #define mat_id_empty (u8)0
  51. #define mat_id_sand (u8)1
  52. #define mat_id_water (u8)2
  53. #define mat_id_salt (u8)3
  54. #define mat_id_wood (u8)4
  55. #define mat_id_fire (u8)5
  56. #define mat_id_smoke (u8)6
  57. #define mat_id_ember (u8)7
  58. #define mat_id_steam (u8)8
  59. #define mat_id_gunpowder (u8)9
  60. #define mat_id_oil (u8)10
  61. #define mat_id_lava (u8)11
  62. #define mat_id_stone (u8)12
  63. #define mat_id_acid (u8)13
  64. // Colors
  65. #define mat_col_empty (color_t){ 0, 0, 0, 0}
  66. #define mat_col_sand (color_t){ 150, 100, 50, 255 }
  67. #define mat_col_salt (color_t){ 200, 180, 190, 255 }
  68. #define mat_col_water (color_t){ 20, 100, 170, 200 }
  69. #define mat_col_stone (color_t){ 120, 110, 120, 255 }
  70. #define mat_col_wood (color_t){ 60, 40, 20, 255 }
  71. #define mat_col_fire (color_t){ 150, 20, 0, 255 }
  72. #define mat_col_smoke (color_t){ 50, 50, 50, 255 }
  73. #define mat_col_ember (color_t){ 200, 120, 20, 255 }
  74. #define mat_col_steam (color_t){ 220, 220, 250, 255 }
  75. #define mat_col_gunpowder (color_t){ 60, 60, 60, 255 }
  76. #define mat_col_oil (color_t){ 80, 70, 60, 255 }
  77. #define mat_col_lava (color_t){ 200, 50, 0, 255 }
  78. #define mat_col_acid (color_t){ 90, 200, 60, 255 }
  79. typedef enum material_selection
  80. {
  81. mat_sel_sand = 0x00,
  82. mat_sel_water,
  83. mat_sel_salt,
  84. mat_sel_wood,
  85. mat_sel_fire,
  86. mat_sel_smoke,
  87. mat_sel_steam,
  88. mat_sel_gunpowder,
  89. mat_sel_oil,
  90. mat_sel_lava,
  91. mat_sel_stone,
  92. mat_sel_acid
  93. } material_selection;
  94. // Material selection for "painting" / default to sand
  95. _global material_selection g_material_selection = mat_sel_sand;
  96. // World update processing structure
  97. _global u8* g_world_process_update_structure = {0}; // Every particle has id associated with it? Jeezuz...
  98. // World particle data structure
  99. _global particle_t* g_world_particle_data = {0};
  100. // Texture buffers
  101. _global color_t* g_texture_buffer = {0};
  102. // UI texture buffer
  103. _global color_t* g_ui_buffer = {0};
  104. // Frame counter
  105. _global u32 g_frame_counter = 0;
  106. // World physics settings
  107. _global f32 gravity = 10.f;
  108. _global f32 g_selection_radius = 10.f;
  109. _global b32 g_show_material_selection_panel = true;
  110. _global b32 g_run_simulation = true;
  111. _global b32 g_show_frame_count = true;
  112. _global b32 g_use_post_processing = true;
  113. // Handle for main window
  114. _global gs_resource_handle g_window;
  115. const char* v_src = "\n"
  116. "#version 330 core\n"
  117. "layout( location = 0 ) in vec2 a_pos;\n"
  118. "layout( location = 1 ) in vec2 a_texCoord;\n"
  119. "uniform int u_flip_y;"
  120. "out vec2 texCoord;\n"
  121. "void main()\n"
  122. "{\n"
  123. " gl_Position = vec4(a_pos, 0.0, 1.0);\n"
  124. " texCoord = vec2(a_texCoord.x, bool(u_flip_y) ? 1.0 - a_texCoord.y : a_texCoord.y);\n"
  125. "}";
  126. const char* f_src = "\n"
  127. "#version 330 core\n"
  128. "in vec2 texCoord;\n"
  129. "out vec4 frag_color;\n"
  130. "uniform sampler2D u_tex;\n"
  131. "void main()\n"
  132. "{\n"
  133. " frag_color = texture(u_tex, texCoord);\n"
  134. "}";
  135. // Forward Decls.
  136. gs_result app_init();
  137. gs_result app_update();
  138. gs_result app_shutdown();
  139. particle_t particle_empty();
  140. particle_t particle_sand();
  141. particle_t particle_water();
  142. particle_t particle_salt();
  143. particle_t particle_wood();
  144. particle_t particle_fire();
  145. particle_t particle_lava();
  146. particle_t particle_smoke();
  147. particle_t particle_ember();
  148. particle_t particle_steam();
  149. particle_t particle_gunpowder();
  150. particle_t particle_oil();
  151. particle_t particle_stone();
  152. particle_t particle_acid();
  153. void update_input();
  154. b32 update_ui();
  155. // Particle updates
  156. void update_particle_sim();
  157. void update_sand( u32 x, u32 y );
  158. void update_water( u32 x, u32 y );
  159. void update_salt( u32 x, u32 y );
  160. void update_fire( u32 x, u32 y );
  161. void update_smoke( u32 x, u32 y );
  162. void update_ember( u32 x, u32 y );
  163. void update_steam( u32 x, u32 y );
  164. void update_gunpowder( u32 x, u32 y );
  165. void update_oil( u32 x, u32 y );
  166. void update_lava( u32 x, u32 y );
  167. void update_acid( u32 x, u32 y );
  168. void update_default( u32 x, u32 y );
  169. // Utilities for writing data into color buffer
  170. void write_data( u32 idx, particle_t );
  171. // Rendering
  172. void render_scene();
  173. // Font methods
  174. void construct_font_data();
  175. font_glyph_t get_glyph( font_t* f, char c );
  176. gs_vec2 calculate_mouse_position()
  177. {
  178. gs_vec2 ws = gs_engine_instance()->ctx.platform->window_size( g_window );
  179. gs_vec2 pmp = gs_engine_instance()->ctx.platform->mouse_position();
  180. // Need to place mouse into frame
  181. f32 x_scale = pmp.x / (f32)ws.x;
  182. f32 y_scale = pmp.y / (f32)ws.y;
  183. return (gs_vec2){ x_scale * (f32)g_texture_width, y_scale * (f32)g_texture_height };
  184. }
  185. s32 random_val( s32 lower, s32 upper )
  186. {
  187. if ( upper < lower ) {
  188. s32 tmp = lower;
  189. lower = upper;
  190. upper = tmp;
  191. }
  192. return ( rand() % (upper - lower + 1) + lower );
  193. }
  194. s32 compute_idx( s32 x, s32 y )
  195. {
  196. return ( y * g_texture_width + x );
  197. }
  198. b32 in_bounds( s32 x, s32 y )
  199. {
  200. if ( x < 0 || x > (g_texture_width - 1) || y < 0 || y > (g_texture_height - 1) ) return false;
  201. return true;
  202. }
  203. b32 is_empty( s32 x, s32 y )
  204. {
  205. return ( in_bounds( x, y ) && g_world_particle_data[ compute_idx( x, y ) ].id == mat_id_empty );
  206. }
  207. particle_t get_particle_at( s32 x, s32 y )
  208. {
  209. return g_world_particle_data[ compute_idx( x, y ) ];
  210. }
  211. b32 completely_surrounded( s32 x, s32 y )
  212. {
  213. // Top
  214. if ( in_bounds( x, y - 1 ) && !is_empty( x, y - 1 ) ) {
  215. return false;
  216. }
  217. // Bottom
  218. if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) ) {
  219. return false;
  220. }
  221. // Left
  222. if ( in_bounds( x - 1, y ) && !is_empty( x - 1, y ) ) {
  223. return false;
  224. }
  225. // Right
  226. if ( in_bounds( x + 1, y ) && !is_empty( x + 1, y ) ) {
  227. return false;
  228. }
  229. // Top Left
  230. if ( in_bounds( x - 1, y - 1 ) && !is_empty( x - 1, y - 1 ) ) {
  231. return false;
  232. }
  233. // Top Right
  234. if ( in_bounds( x + 1, y - 1 ) && !is_empty( x + 1, y - 1 ) ) {
  235. return false;
  236. }
  237. // Bottom Left
  238. if ( in_bounds( x - 1, y + 1 ) && !is_empty( x - 1, y + 1 ) ) {
  239. return false;
  240. }
  241. // Bottom Right
  242. if ( in_bounds( x + 1, y + 1 ) && !is_empty( x + 1, y + 1 ) ) {
  243. return false;
  244. }
  245. return true;
  246. }
  247. b32 is_in_liquid( s32 x, s32 y, s32* lx, s32* ly )
  248. {
  249. if ( in_bounds( x, y ) && (get_particle_at( x, y ).id == mat_id_water || get_particle_at( x, y ).id == mat_id_oil) ) {
  250. *lx = x; *ly = y;
  251. return true;
  252. }
  253. if ( in_bounds( x, y - 1 ) && (get_particle_at( x, y - 1 ).id == mat_id_water || get_particle_at( x, y - 1 ).id == mat_id_oil) ) {
  254. *lx = x; *ly = y - 1;
  255. return true;
  256. }
  257. if ( in_bounds( x, y + 1 ) && (get_particle_at( x, y + 1 ).id == mat_id_water || get_particle_at( x, y + 1 ).id == mat_id_oil) ) {
  258. *lx = x; *ly = y + 1;
  259. return true;
  260. }
  261. if ( in_bounds( x - 1, y ) && (get_particle_at( x - 1, y ).id == mat_id_water || get_particle_at( x - 1, y ).id == mat_id_oil) ) {
  262. *lx = x - 1; *ly = y;
  263. return true;
  264. }
  265. if ( in_bounds( x - 1, y - 1 ) && (get_particle_at( x - 1, y - 1 ).id == mat_id_water || get_particle_at( x - 1, y - 1 ).id == mat_id_oil) ) {
  266. *lx = x - 1; *ly = y - 1;
  267. return true;
  268. }
  269. if ( in_bounds( x - 1, y + 1 ) && (get_particle_at( x - 1, y + 1 ).id == mat_id_water || get_particle_at( x - 1, y + 1 ).id == mat_id_oil) ) {
  270. *lx = x - 1; *ly = y + 1;
  271. return true;
  272. }
  273. if ( in_bounds( x + 1, y ) && (get_particle_at( x + 1, y ).id == mat_id_water || get_particle_at( x + 1, y ).id == mat_id_oil) ) {
  274. *lx = x + 1; *ly = y;
  275. return true;
  276. }
  277. if ( in_bounds( x + 1, y - 1 ) && (get_particle_at( x + 1, y - 1 ).id == mat_id_water || get_particle_at( x + 1, y - 1 ).id == mat_id_oil) ) {
  278. *lx = x + 1; *ly = y - 1;
  279. return true;
  280. }
  281. if ( in_bounds( x + 1, y + 1 ) && (get_particle_at( x + 1, y + 1 ).id == mat_id_water || get_particle_at( x + 1, y + 1 ).id == mat_id_oil) ) {
  282. *lx = x + 1; *ly = y + 1;
  283. return true;
  284. }
  285. return false;
  286. }
  287. b32 is_in_water( s32 x, s32 y, s32* lx, s32* ly )
  288. {
  289. if ( in_bounds( x, y ) && (get_particle_at( x, y ).id == mat_id_water) ) {
  290. *lx = x; *ly = y;
  291. return true;
  292. }
  293. if ( in_bounds( x, y - 1 ) && (get_particle_at( x, y - 1 ).id == mat_id_water) ) {
  294. *lx = x; *ly = y - 1;
  295. return true;
  296. }
  297. if ( in_bounds( x, y + 1 ) && (get_particle_at( x, y + 1 ).id == mat_id_water) ) {
  298. *lx = x; *ly = y + 1;
  299. return true;
  300. }
  301. if ( in_bounds( x - 1, y ) && (get_particle_at( x - 1, y ).id == mat_id_water) ) {
  302. *lx = x - 1; *ly = y;
  303. return true;
  304. }
  305. if ( in_bounds( x - 1, y - 1 ) && (get_particle_at( x - 1, y - 1 ).id == mat_id_water) ) {
  306. *lx = x - 1; *ly = y - 1;
  307. return true;
  308. }
  309. if ( in_bounds( x - 1, y + 1 ) && (get_particle_at( x - 1, y + 1 ).id == mat_id_water) ) {
  310. *lx = x - 1; *ly = y + 1;
  311. return true;
  312. }
  313. if ( in_bounds( x + 1, y ) && (get_particle_at( x + 1, y ).id == mat_id_water) ) {
  314. *lx = x + 1; *ly = y;
  315. return true;
  316. }
  317. if ( in_bounds( x + 1, y - 1 ) && (get_particle_at( x + 1, y - 1 ).id == mat_id_water) ) {
  318. *lx = x + 1; *ly = y - 1;
  319. return true;
  320. }
  321. if ( in_bounds( x + 1, y + 1 ) && (get_particle_at( x + 1, y + 1 ).id == mat_id_water) ) {
  322. *lx = x + 1; *ly = y + 1;
  323. return true;
  324. }
  325. return false;
  326. }
  327. int main( int argc, char** argv )
  328. {
  329. gs_application_desc app = {0};
  330. app.window_title = "SandSim";
  331. app.window_width = g_window_width;
  332. app.window_height = g_window_height;
  333. app.init = &app_init;
  334. app.update = &app_update;
  335. app.shutdown = &app_shutdown;
  336. app.frame_rate = 60;
  337. app.enable_vsync = false;
  338. // Construct internal instance of our engine
  339. gs_engine* engine = gs_engine_construct( app );
  340. // Run the internal engine loop until completion
  341. gs_result res = engine->run();
  342. // Check result of engine after exiting loop
  343. if ( res != gs_result_success )
  344. {
  345. gs_println( "Error: Engine did not successfully finish running." );
  346. return -1;
  347. }
  348. gs_println( "Gunslinger exited successfully." );
  349. return 0;
  350. }
  351. typedef struct hsv_t
  352. {
  353. f32 h;
  354. f32 s;
  355. f32 v;
  356. } hsv_t;
  357. // From on: https://gist.github.com/fairlight1337/4935ae72bcbcc1ba5c72
  358. hsv_t rgb_to_hsv( color_t c )
  359. {
  360. gs_vec3 cv = (gs_vec3){ (f32)c.r / 255.f, (f32)c.g / 255.f, (f32)c.b / 255.f };
  361. f32 fR = cv.x, fG = cv.y, fB = cv.z;
  362. f32 fCMax = gs_max(gs_max(fR, fG), fB);
  363. f32 fCMin = gs_min(gs_min(fR, fG), fB);
  364. f32 fDelta = fCMax - fCMin;
  365. hsv_t hsv;
  366. if(fDelta > 0) {
  367. if(fCMax == fR) {
  368. hsv.h = 60 * (fmod(((fG - fB) / fDelta), 6));
  369. } else if(fCMax == fG) {
  370. hsv.h = 60 * (((fB - fR) / fDelta) + 2);
  371. } else if(fCMax == fB) {
  372. hsv.h = 60 * (((fR - fG) / fDelta) + 4);
  373. }
  374. if(fCMax > 0) {
  375. hsv.s = fDelta / fCMax;
  376. } else {
  377. hsv.s = 0;
  378. }
  379. hsv.v = fCMax;
  380. } else {
  381. hsv.h = 0;
  382. hsv.s = 0;
  383. hsv.v = fCMax;
  384. }
  385. if(hsv.h < 0) {
  386. hsv.h = 360 + hsv.h;
  387. }
  388. return hsv;
  389. }
  390. // Implemented from: https://stackoverflow.com/questions/27374550/how-to-compare-color-object-and-get-closest-color-in-an-color
  391. // distance between two hues:
  392. f32 hue_dist( f32 h1, f32 h2 )
  393. {
  394. f32 d = fabsf( h1 - h2 );
  395. return d > 180.f ? 360.f - d : d;
  396. }
  397. // color brightness as perceived:
  398. f32 brightness( color_t c )
  399. {
  400. return ( (f32)c.r * 0.299f + (f32)c.g * 0.587f + (f32)c.b *0.114f ) / 256.f;
  401. }
  402. f32 color_num( color_t c )
  403. {
  404. const f32 bright_factor = 100.0f;
  405. const f32 sat_factor = 0.1f;
  406. hsv_t hsv = rgb_to_hsv( c );
  407. return hsv.s * sat_factor + brightness( c ) * bright_factor;
  408. }
  409. #define __check_hsv(c0, c1, p_func)\
  410. do {\
  411. hsv_t hsv0 = rgb_to_hsv( c0 );\
  412. hsv_t hsv1 = rgb_to_hsv( c1 );\
  413. f32 d = abs( color_num( c0 ) - color_num( c1 ) ) + hue_dist( hsv0.h, hsv1.h );\
  414. if ( d < min_dist ) {\
  415. min_dist = d;\
  416. p = p_func();\
  417. }\
  418. } while ( 0 )
  419. #define __check_dist_euclidean(c0, c1, p_func)\
  420. do {\
  421. gs_vec4 c0_vec = (gs_vec4){ (f32)c0.r, c0.g, c0.b, 255.f };\
  422. gs_vec4 c1_vec = (gs_vec4){ (f32)c1.r, c1.g, c1.b, 255.f };\
  423. f32 d = gs_vec4_dist( c0_vec, c1_vec );\
  424. if ( d < min_dist ) {\
  425. min_dist = d;\
  426. p = p_func();\
  427. }\
  428. } while ( 0 )
  429. #define __check_dist( c0, c1, p_func )\
  430. do\
  431. {\
  432. f32 rd = (f32)c0.r - (f32)c1.r;\
  433. f32 gd = (f32)c0.g - (f32)c1.g;\
  434. f32 bd = (f32)c0.b - (f32)c1.b;\
  435. f32 sd = rd * rd + gd * gd + bd * bd;\
  436. f32 d = pow(rd * 0.299, 2) + pow(gd * 0.587, 2) + pow(bd * 0.114, 2);\
  437. if (d < min_dist) {\
  438. min_dist = d;\
  439. p = p_func();\
  440. }\
  441. } while ( 0 )
  442. particle_t get_closest_particle_from_color( color_t c )
  443. {
  444. particle_t p = particle_empty();
  445. f32 min_dist = f32_max;
  446. gs_vec4 c_vec = (gs_vec4){ (f32)c.r, (f32)c.g, (f32)c.b, (f32)c.a };
  447. u8 id = mat_id_empty;
  448. __check_dist_euclidean( c, mat_col_sand, particle_sand );
  449. __check_dist_euclidean( c, mat_col_water, particle_water );
  450. __check_dist_euclidean( c, mat_col_salt, particle_salt );
  451. __check_dist_euclidean( c, mat_col_wood, particle_wood );
  452. __check_dist_euclidean( c, mat_col_fire, particle_fire );
  453. __check_dist_euclidean( c, mat_col_smoke, particle_smoke );
  454. __check_dist_euclidean( c, mat_col_steam, particle_steam );
  455. __check_dist_euclidean( c, mat_col_gunpowder, particle_gunpowder );
  456. __check_dist_euclidean( c, mat_col_oil, particle_oil );
  457. __check_dist_euclidean( c, mat_col_lava, particle_lava );
  458. __check_dist_euclidean( c, mat_col_stone, particle_stone );
  459. __check_dist_euclidean( c, mat_col_acid, particle_acid );
  460. return p;
  461. }
  462. void drop_file_callback( void* platform_window, s32 count, const char** file_paths )
  463. {
  464. if ( count < 1 ) return;
  465. // Just do first one for now...
  466. if ( count > 1 ) count = 1;
  467. gs_graphics_i* gfx = gs_engine_instance()->ctx.graphics;
  468. // We'll place at the mouse position as well, for shiggles
  469. gs_vec2 mp = calculate_mouse_position();
  470. for ( s32 i = 0; i < count; ++i )
  471. {
  472. // Need to verify this IS an image first.
  473. char temp_file_extension_buffer[ 16 ] = {0};
  474. gs_util_get_file_extension( temp_file_extension_buffer, sizeof( temp_file_extension_buffer ), file_paths[ 0 ] );
  475. if ( gs_string_compare_equal(temp_file_extension_buffer, "png" ) ||
  476. gs_string_compare_equal(temp_file_extension_buffer, "jpg" ) ||
  477. gs_string_compare_equal(temp_file_extension_buffer, "jpeg") ||
  478. gs_string_compare_equal(temp_file_extension_buffer, "bmp" ) )
  479. {
  480. // Load texture into memory
  481. s32 _w, _h, _n;
  482. void* texture_data = NULL;
  483. // Force texture data to 3 components
  484. texture_data = gfx->load_texture_data_from_file( file_paths[ i ], false, gs_texture_format_rgb8, &_w, &_h, &_n );
  485. _n = 3;
  486. // Not sure what the format should be, so this is ...blah. Need to find a way to determine this beforehand.
  487. u8* data = (u8*)texture_data;
  488. s32 sx = ( g_texture_width - _w ) / 2;
  489. s32 sy = ( g_texture_height - _h ) / 2;
  490. // Now we need to process the data and place it into our particle/color buffers
  491. for ( u32 h = 0; h < _h; ++h )
  492. {
  493. for ( u32 w = 0; w < _w; ++w )
  494. {
  495. color_t c =
  496. {
  497. data[ (h * _w + w) * _n + 0 ],
  498. data[ (h * _w + w) * _n + 1 ],
  499. data[ (h * _w + w) * _n + 2 ],
  500. 255
  501. };
  502. // Get color of this pixel in the image
  503. particle_t p = get_closest_particle_from_color( c );
  504. // Let's place this thing in the center instead...
  505. if ( in_bounds( sx + w, sy + h ) ) {
  506. u32 idx = compute_idx( sx + w, sy + h );
  507. write_data( idx, p );
  508. }
  509. }
  510. }
  511. // Free texture data
  512. gs_free( texture_data );
  513. }
  514. }
  515. }
  516. void app_close_window_callback( void* window )
  517. {
  518. g_app_running = false;
  519. }
  520. // Here, we'll initialize all of our application data, which in this case is our graphics resources
  521. gs_result app_init()
  522. {
  523. // Cache instance of api contexts from engine
  524. gs_graphics_i* gfx = gs_engine_instance()->ctx.graphics;
  525. gs_platform_i* platform = gs_engine_instance()->ctx.platform;
  526. // Set callback for when window close button is pressed
  527. platform->set_window_close_callback( platform->main_window(), &app_close_window_callback );
  528. // Construct command buffer ( the command buffer is used to allow for immediate drawing at any point in our program )
  529. g_cb = gfx->construct_command_buffer();
  530. // Construct shader from our source above
  531. g_shader = gfx->construct_shader( v_src, f_src );
  532. // Construct uniform for shader
  533. u_tex = gfx->construct_uniform( g_shader, "u_tex", gs_uniform_type_sampler2d );
  534. u_flip_y = gfx->construct_uniform( g_shader, "u_flip_y", gs_uniform_type_int );
  535. // Vertex data layout for our mesh (for this shader, it's a single float2 attribute for position)
  536. gs_vertex_attribute_type layout[] =
  537. {
  538. gs_vertex_attribute_float2,
  539. gs_vertex_attribute_float2
  540. };
  541. // Count of our vertex attribute array
  542. u32 layout_count = sizeof( layout ) / sizeof( gs_vertex_attribute_type );
  543. // Vertex data for triangle
  544. f32 v_data[] =
  545. {
  546. // Positions UVs
  547. -1.0f, -1.0f, 0.0f, 0.0f, // Top Left
  548. 1.0f, -1.0f, 1.0f, 0.0f, // Top Right
  549. -1.0f, 1.0f, 0.0f, 1.0f, // Bottom Left
  550. 1.0f, 1.0f, 1.0f, 1.0f // Bottom Right
  551. };
  552. u32 i_data[] =
  553. {
  554. 0, 2, 3,
  555. 3, 1, 0
  556. };
  557. // Construct vertex buffer
  558. g_vbo = gfx->construct_vertex_buffer( layout, layout_count, v_data, sizeof(v_data) );
  559. // Construct index buffer
  560. g_ibo = gfx->construct_index_buffer( i_data, sizeof(i_data) );
  561. // Construct world data ( for now, it'll just be the size of the screen )
  562. g_world_particle_data = gs_malloc( g_texture_width * g_texture_height * sizeof(particle_t) );
  563. // Construct texture buffer data
  564. g_texture_buffer = gs_malloc( g_texture_width * g_texture_height * sizeof(color_t) );
  565. g_ui_buffer = gs_malloc( g_texture_width * g_texture_height * sizeof(color_t) );
  566. // Set buffers to 0
  567. memset( g_texture_buffer, 0, g_texture_width * g_texture_height * sizeof(color_t) );
  568. memset( g_world_particle_data, 0, g_texture_width * g_texture_height );
  569. memset( g_ui_buffer, 0, g_texture_width * g_texture_height );
  570. // Construct texture resource from GPU
  571. gs_texture_parameter_desc t_desc = gs_texture_parameter_desc_default();
  572. t_desc.texture_format = gs_texture_format_rgba8;
  573. t_desc.mag_filter = gs_nearest;
  574. t_desc.min_filter = gs_nearest;
  575. t_desc.generate_mips = false;
  576. t_desc.width = g_texture_width;
  577. t_desc.height = g_texture_height;
  578. t_desc.num_comps = 4;
  579. t_desc.data = g_texture_buffer;
  580. g_tex = gfx->construct_texture( t_desc );
  581. // Construct texture resource from GPU
  582. t_desc = gs_texture_parameter_desc_default();
  583. t_desc.texture_format = gs_texture_format_rgba8;
  584. t_desc.mag_filter = gs_nearest;
  585. t_desc.min_filter = gs_nearest;
  586. t_desc.generate_mips = false;
  587. t_desc.width = g_texture_width;
  588. t_desc.height = g_texture_height;
  589. t_desc.num_comps = 4;
  590. t_desc.data = g_ui_buffer;
  591. g_tex_ui = gfx->construct_texture( t_desc );
  592. // Construct target for offscreen rendering
  593. t_desc.data = NULL;
  594. g_rt = gfx->construct_texture( t_desc );
  595. // Construct frame buffer
  596. g_fb = gfx->construct_frame_buffer( g_rt );
  597. // Cache window handle from platform
  598. g_window = gs_engine_instance()->ctx.platform->main_window();
  599. // Initialize render passes
  600. g_blur_pass = blur_pass_ctor();
  601. g_bright_pass = bright_filter_pass_ctor();
  602. g_composite_pass = composite_pass_ctor();
  603. // Load UI font texture data from file
  604. construct_font_data();
  605. // Set up callback for dropping them files, yo.
  606. platform->set_dropped_files_callback( platform->main_window(), &drop_file_callback );
  607. return gs_result_success;
  608. }
  609. gs_result app_update()
  610. {
  611. // Grab global instance of engine
  612. gs_engine* engine = gs_engine_instance();
  613. // If we press the escape key, exit the application
  614. if ( engine->ctx.platform->key_pressed( gs_keycode_esc ) || !g_app_running )
  615. {
  616. return gs_result_success;
  617. }
  618. // Why not print this elsewhere, yo?
  619. gs_timed_action( 60, {
  620. gs_println( "frame: %.5f ms", engine->ctx.platform->time.frame );
  621. });
  622. // All application updates
  623. b32 ui_interaction = update_ui();
  624. if ( !ui_interaction ) {
  625. update_input();
  626. }
  627. if ( g_run_simulation ) {
  628. update_particle_sim();
  629. }
  630. /*===============
  631. // Render scene
  632. ================*/
  633. render_scene();
  634. // Update frame counter
  635. g_frame_counter = (g_frame_counter + 1) % u32_max;
  636. return gs_result_in_progress;
  637. }
  638. gs_result app_shutdown()
  639. {
  640. gs_println( "Goodbye, Gunslinger." );
  641. return gs_result_success;
  642. }
  643. void construct_font_data()
  644. {
  645. gs_platform_i* platform = gs_engine_instance()->ctx.platform;
  646. gs_graphics_i* gfx = gs_engine_instance()->ctx.graphics;
  647. g_font.data = g_font_data;
  648. g_font.width = 90;
  649. g_font.height = 42;
  650. g_font.num_comps = 3;
  651. // Set up metrics
  652. g_font.glyph_advance = 1;
  653. // Construct glyph information
  654. const s32 glyph_width = 5, glyph_height = 7;
  655. for ( u32 r = 0; r < 6; ++r )
  656. {
  657. for ( u32 c = 0; c < 18; ++c )
  658. {
  659. u32 idx = r * 18 + c;
  660. g_font.glyphs[ idx ] = (font_glyph_t){ c * 5, r * 7, 5, 7 };
  661. }
  662. }
  663. }
  664. font_glyph_t get_glyph( font_t* f, char c )
  665. {
  666. switch ( c )
  667. {
  668. case ' ': return g_font.glyphs[ 0 ];
  669. case '!': return g_font.glyphs[ 1 ];
  670. case '"': return g_font.glyphs[ 2 ];
  671. case '#': return g_font.glyphs[ 3 ];
  672. case '$': return g_font.glyphs[ 4 ];
  673. case '%': return g_font.glyphs[ 5 ];
  674. case '&': return g_font.glyphs[ 6 ];
  675. case '\'': return g_font.glyphs[ 7 ];
  676. case '(': return g_font.glyphs[ 8 ];
  677. case ')': return g_font.glyphs[ 9 ];
  678. case '*': return g_font.glyphs[ 10 ];
  679. case '+': return g_font.glyphs[ 11 ];
  680. case ',': return g_font.glyphs[ 12 ];
  681. case '-': return g_font.glyphs[ 13 ];
  682. case '.': return g_font.glyphs[ 14 ];
  683. case '/': return g_font.glyphs[ 15 ];
  684. case '0': return g_font.glyphs[ 16 ];
  685. case '1': return g_font.glyphs[ 17 ];
  686. case '2': return g_font.glyphs[ 18 ];
  687. case '3': return g_font.glyphs[ 19 ];
  688. case '4': return g_font.glyphs[ 20 ];
  689. case '5': return g_font.glyphs[ 21 ];
  690. case '6': return g_font.glyphs[ 22 ];
  691. case '7': return g_font.glyphs[ 23 ];
  692. case '8': return g_font.glyphs[ 24 ];
  693. case '9': return g_font.glyphs[ 25 ];
  694. case ':': return g_font.glyphs[ 26 ];
  695. case ';': return g_font.glyphs[ 27 ];
  696. case '<': return g_font.glyphs[ 28 ];
  697. case '=': return g_font.glyphs[ 29 ];
  698. case '>': return g_font.glyphs[ 30 ];
  699. case '?': return g_font.glyphs[ 31 ];
  700. case '@': return g_font.glyphs[ 32 ];
  701. case 'A': return g_font.glyphs[ 33 ];
  702. case 'B': return g_font.glyphs[ 34 ];
  703. case 'C': return g_font.glyphs[ 35 ];
  704. case 'D': return g_font.glyphs[ 36 ];
  705. case 'E': return g_font.glyphs[ 37 ];
  706. case 'F': return g_font.glyphs[ 38 ];
  707. case 'G': return g_font.glyphs[ 39 ];
  708. case 'H': return g_font.glyphs[ 40 ];
  709. case 'I': return g_font.glyphs[ 41 ];
  710. case 'J': return g_font.glyphs[ 42 ];
  711. case 'K': return g_font.glyphs[ 43 ];
  712. case 'L': return g_font.glyphs[ 44 ];
  713. case 'M': return g_font.glyphs[ 45 ];
  714. case 'N': return g_font.glyphs[ 46 ];
  715. case 'O': return g_font.glyphs[ 47 ];
  716. case 'P': return g_font.glyphs[ 48 ];
  717. case 'Q': return g_font.glyphs[ 49 ];
  718. case 'R': return g_font.glyphs[ 50 ];
  719. case 'S': return g_font.glyphs[ 51 ];
  720. case 'T': return g_font.glyphs[ 52 ];
  721. case 'U': return g_font.glyphs[ 53 ];
  722. case 'V': return g_font.glyphs[ 54 ];
  723. case 'W': return g_font.glyphs[ 55 ];
  724. case 'X': return g_font.glyphs[ 56 ];
  725. case 'Y': return g_font.glyphs[ 57 ];
  726. case 'Z': return g_font.glyphs[ 58 ];
  727. case '[': return g_font.glyphs[ 59 ];
  728. case '\\': return g_font.glyphs[ 60 ];
  729. case ']': return g_font.glyphs[ 61 ];
  730. case '^': return g_font.glyphs[ 62 ];
  731. case '_': return g_font.glyphs[ 63 ];
  732. case '`': return g_font.glyphs[ 64 ];
  733. case 'a': return g_font.glyphs[ 65 ];
  734. case 'b': return g_font.glyphs[ 66 ];
  735. case 'c': return g_font.glyphs[ 67 ];
  736. case 'd': return g_font.glyphs[ 68 ];
  737. case 'e': return g_font.glyphs[ 69 ];
  738. case 'f': return g_font.glyphs[ 70 ];
  739. case 'g': return g_font.glyphs[ 71 ];
  740. case 'h': return g_font.glyphs[ 72 ];
  741. case 'i': return g_font.glyphs[ 73 ];
  742. case 'j': return g_font.glyphs[ 74 ];
  743. case 'k': return g_font.glyphs[ 75 ];
  744. case 'l': return g_font.glyphs[ 76 ];
  745. case 'm': return g_font.glyphs[ 77 ];
  746. case 'n': return g_font.glyphs[ 78 ];
  747. case 'o': return g_font.glyphs[ 79 ];
  748. case 'p': return g_font.glyphs[ 80 ];
  749. case 'q': return g_font.glyphs[ 81 ];
  750. case 'r': return g_font.glyphs[ 82 ];
  751. case 's': return g_font.glyphs[ 83 ];
  752. case 't': return g_font.glyphs[ 84 ];
  753. case 'u': return g_font.glyphs[ 85 ];
  754. case 'v': return g_font.glyphs[ 86 ];
  755. case 'w': return g_font.glyphs[ 87 ];
  756. case 'x': return g_font.glyphs[ 88 ];
  757. case 'y': return g_font.glyphs[ 89 ];
  758. case 'z': return g_font.glyphs[ 90 ];
  759. case '{': return g_font.glyphs[ 91 ];
  760. case '|': return g_font.glyphs[ 92 ];
  761. case '}': return g_font.glyphs[ 93 ];
  762. case '~': return g_font.glyphs[ 94 ];
  763. // For anything not supported, just return empty space
  764. default: {
  765. return (font_glyph_t){0};
  766. } break;
  767. }
  768. }
  769. void putpixel( int x, int y ) {
  770. if ( in_bounds( x, y ) ) {
  771. g_ui_buffer[ compute_idx( x, y ) ] = (color_t){ 255, 255, 255, 255 };
  772. }
  773. }
  774. // Function to put pixels
  775. // at subsequence points
  776. void drawCircle(int xc, int yc, int x, int y)
  777. {
  778. putpixel(xc+x, yc+y);
  779. putpixel(xc-x, yc+y);
  780. putpixel(xc+x, yc-y);
  781. putpixel(xc-x, yc-y);
  782. putpixel(xc+y, yc+x);
  783. putpixel(xc-y, yc+x);
  784. putpixel(xc+y, yc-x);
  785. putpixel(xc-y, yc-x);
  786. }
  787. // Function for circle-generation
  788. // using Bresenham's algorithm
  789. void circleBres(int xc, int yc, int r)
  790. {
  791. int x = 0, y = r;
  792. int d = 3 - 2 * r;
  793. drawCircle(xc, yc, x, y);
  794. while (y >= x)
  795. {
  796. // For each pixel we will
  797. // draw all eight pixels
  798. x++;
  799. // Check for decision parameter
  800. // and correspondingly
  801. // update d, x, y
  802. if (d > 0)
  803. {
  804. y--;
  805. d = d + 4 * (x - y) + 10;
  806. }
  807. else
  808. d = d + 4 * x + 6;
  809. drawCircle(xc, yc, x, y);
  810. }
  811. }
  812. void update_input()
  813. {
  814. gs_platform_i* platform = gs_engine_instance()->ctx.platform;
  815. if ( platform->key_pressed( gs_keycode_i ) ) {
  816. g_show_material_selection_panel = !g_show_material_selection_panel;
  817. }
  818. if ( platform->key_pressed( gs_keycode_f ) ) {
  819. g_show_frame_count = !g_show_frame_count;
  820. }
  821. if ( platform->key_pressed( gs_keycode_b ) ) {
  822. g_use_post_processing = !g_use_post_processing;
  823. }
  824. f32 wx = 0, wy = 0;
  825. platform->mouse_wheel( &wx, &wy );
  826. if ( platform->key_pressed( gs_keycode_lbracket ) || wy < 0.f ) {
  827. g_selection_radius = gs_clamp( g_selection_radius - 1.f, 1.f, 100.f );
  828. }
  829. if ( platform->key_pressed( gs_keycode_rbracket ) || wy > 0.f ) {
  830. g_selection_radius = gs_clamp( g_selection_radius + 1.f, 1.f, 100.f );
  831. }
  832. if ( platform->key_pressed( gs_keycode_p ) ) {
  833. g_run_simulation = !g_run_simulation;
  834. }
  835. // Clear data
  836. if ( platform->key_pressed( gs_keycode_c ) ) {
  837. memset( g_texture_buffer, 0, sizeof(color_t) * g_texture_width * g_texture_height );
  838. memset( g_world_particle_data, 0, sizeof(particle_t) * g_texture_width * g_texture_height );
  839. }
  840. // Mouse input for testing
  841. if ( platform->mouse_down( gs_mouse_lbutton ) )
  842. {
  843. gs_vec2 mp = calculate_mouse_position();
  844. f32 mp_x = gs_clamp( mp.x, 0.f, (f32)g_texture_width - 1.f );
  845. f32 mp_y = gs_clamp( mp.y, 0.f, (f32)g_texture_height - 1.f );
  846. u32 max_idx = (g_texture_width * g_texture_height) - 1;
  847. s32 r_amt = random_val( 1, 10000 );
  848. const f32 R = g_selection_radius;
  849. // Spawn in a circle around the mouse
  850. for ( u32 i = 0; i < r_amt; ++i )
  851. {
  852. f32 ran = (f32)random_val(0, 100) / 100.f;
  853. f32 r = R * sqrt(ran);
  854. f32 theta = (f32)random_val(0, 100)/100.f * 2.f * gs_pi;
  855. f32 rx = cos((f32)theta) * r;
  856. f32 ry = sin((f32)theta) * r;
  857. s32 mpx = (s32)gs_clamp( mp_x + (f32)rx, 0.f, (f32)g_texture_width - 1.f );
  858. s32 mpy = (s32)gs_clamp( mp_y + (f32)ry, 0.f, (f32)g_texture_height - 1.f );
  859. s32 idx = mpy * (s32)g_texture_width + mpx;
  860. idx = gs_clamp( idx, 0, max_idx );
  861. if ( is_empty( mpx, mpy ) )
  862. {
  863. particle_t p = {0};
  864. switch ( g_material_selection ) {
  865. case mat_sel_sand: p = particle_sand(); break;;
  866. case mat_sel_water: p = particle_water(); break;;
  867. case mat_sel_salt: p = particle_salt(); break;;
  868. case mat_sel_wood: p = particle_wood(); break;;
  869. case mat_sel_fire: p = particle_fire(); break;
  870. case mat_sel_smoke: p = particle_smoke(); break;
  871. case mat_sel_steam: p = particle_steam(); break;
  872. case mat_sel_gunpowder: p = particle_gunpowder(); break;
  873. case mat_sel_oil: p = particle_oil(); break;
  874. case mat_sel_lava: p = particle_lava(); break;
  875. case mat_sel_stone: p = particle_stone(); break;
  876. case mat_sel_acid: p = particle_acid(); break;
  877. }
  878. p.velocity = (gs_vec2){ random_val( -1, 1 ), random_val( -2, 5 ) };
  879. write_data( idx, p );
  880. }
  881. }
  882. }
  883. // Solid Erase
  884. if ( platform->mouse_down( gs_mouse_rbutton ) )
  885. {
  886. gs_vec2 mp = calculate_mouse_position( );
  887. f32 mp_x = gs_clamp( mp.x, 0.f, (f32)g_texture_width - 1.f );
  888. f32 mp_y = gs_clamp( mp.y, 0.f, (f32)g_texture_height - 1.f );
  889. u32 max_idx = (g_texture_width * g_texture_height) - 1;
  890. const f32 R = g_selection_radius;
  891. // Erase in a circle pattern
  892. for ( s32 i = -R ; i < R; ++i )
  893. {
  894. for ( s32 j = -R ; j < R; ++j )
  895. {
  896. s32 rx = ((s32)mp_x + j);
  897. s32 ry = ((s32)mp_y + i);
  898. gs_vec2 r = (gs_vec2){ rx, ry };
  899. if ( in_bounds( rx, ry ) && gs_vec2_dist( mp, r ) <= R ) {
  900. write_data( compute_idx( rx, ry ), particle_empty() );
  901. }
  902. }
  903. }
  904. }
  905. // Need to detect if mouse has entered the screen with payload...
  906. }
  907. void update_particle_sim()
  908. {
  909. // Cache engine subsystem interfaces
  910. gs_graphics_i* gfx = gs_engine_instance()->ctx.graphics;
  911. gs_platform_i* platform = gs_engine_instance()->ctx.platform;
  912. // Update frame counter ( loop back to 0 if we roll past u32 max )
  913. b32 frame_counter_even = ((g_frame_counter % 2) == 0);
  914. s32 ran = frame_counter_even ? 0 : 1;
  915. const f32 dt = platform->time.delta;
  916. // Rip through read data and update write buffer
  917. // Note(John): We update "bottom up", since all the data is edited "in place". Double buffering all data would fix this
  918. // issue, however it requires double all of the data.
  919. for ( u32 y = g_texture_height - 1; y > 0; --y )
  920. {
  921. for ( u32 x = ran ? 0 : g_texture_width - 1; ran ? x < g_texture_width : x > 0; ran ? ++x : --x )
  922. {
  923. // Current particle idx
  924. u32 read_idx = compute_idx( x, y );
  925. // Get material of particle at point
  926. u8 mat_id = get_particle_at( x, y ).id;
  927. // Update particle's lifetime (I guess just use frames)? Or should I have sublife?
  928. g_world_particle_data[ read_idx ].life_time += 1.f * dt;
  929. switch ( mat_id ) {
  930. case mat_id_sand: update_sand( x, y ); break;
  931. case mat_id_water: update_water( x, y ); break;
  932. case mat_id_salt: update_salt( x, y ); break;
  933. case mat_id_fire: update_fire( x, y ); break;
  934. case mat_id_smoke: update_smoke( x, y ); break;
  935. case mat_id_ember: update_ember( x, y ); break;
  936. case mat_id_steam: update_steam( x, y ); break;
  937. case mat_id_gunpowder: update_gunpowder( x, y ); break;
  938. case mat_id_oil: update_oil( x, y ); break;
  939. case mat_id_lava: update_lava( x, y ); break;
  940. case mat_id_acid: update_acid( x, y ); break;
  941. // Do nothing for empty or default case
  942. default:
  943. case mat_id_empty:
  944. {
  945. // update_default( w, h, i );
  946. } break;
  947. }
  948. }
  949. }
  950. // Can remove this loop later on by keeping update structure and setting that for the particle as it moves,
  951. // then at the end of frame just memsetting the entire structure to 0.
  952. for ( u32 y = g_texture_height - 1; y > 0; --y ) {
  953. for ( u32 x = ran ? 0 : g_texture_width - 1; ran ? x < g_texture_width : x > 0; ran ? ++x : --x ) {
  954. // Set particle's update to false for next frame
  955. g_world_particle_data[ compute_idx( x, y ) ].has_been_updated_this_frame = false;
  956. }
  957. }
  958. }
  959. void draw_glyph_at( font_t* f, color_t* buffer, s32 x, s32 y, char c, color_t col )
  960. {
  961. u8* font_data = (u8*)f->data;
  962. font_glyph_t g = get_glyph( f, c );
  963. // How to accurately place? I have width and height of glyph in texture, but need to convert this to RGBA data for ui buffer
  964. for ( s32 h = 0; h < g.height; ++h )
  965. {
  966. for ( s32 w = 0; w < g.width; ++w )
  967. {
  968. s32 _w = w + g.x;
  969. s32 _h = h + g.y;
  970. u8 a = font_data[ ( _h * f->width + _w ) * f->num_comps + 0 ] == 0 ? 0 : 255;
  971. color_t c = {
  972. font_data[ ( _h * f->width + _w ) * f->num_comps + 0 ],
  973. font_data[ ( _h * f->width + _w ) * f->num_comps + 1 ],
  974. font_data[ ( _h * f->width + _w ) * f->num_comps + 2 ],
  975. a
  976. };
  977. if ( in_bounds( x + w, y + h ) && a ) {
  978. buffer[ compute_idx( x + w, y + h ) ] = col;
  979. }
  980. }
  981. }
  982. }
  983. void draw_string_at( font_t* f, color_t* buffer, s32 x, s32 y, const char* str, usize len, color_t col )
  984. {
  985. u8* font_data = (u8*)f->data;
  986. for ( u32 i = 0; i < len; ++i )
  987. {
  988. font_glyph_t g = get_glyph( f, str[i] );
  989. draw_glyph_at( f, buffer, x, y, str[i], col );
  990. x += g.width + f->glyph_advance; // Move by glyph width + advance
  991. }
  992. }
  993. b32 in_rect ( gs_vec2 p, gs_vec2 ro, gs_vec2 rd )
  994. {
  995. if ( p.x < ro.x || p.x > ro.x + rd.x || p.y < ro.y || p.y > ro.y + rd.y ) return false;
  996. return true;
  997. }
  998. b32 gui_rect( color_t* buffer, s32 _x, s32 _y, s32 _w, s32 _h, color_t c )
  999. {
  1000. gs_vec2 mp = calculate_mouse_position();
  1001. for ( u32 h = 0; h < _h; ++ h )
  1002. {
  1003. for ( u32 w = 0; w < _w; ++w )
  1004. {
  1005. if ( in_bounds( _x + w, _y + h ) ) {
  1006. buffer[ compute_idx( _x + w, _y + h ) ] = c;
  1007. }
  1008. }
  1009. }
  1010. b32 clicked = gs_engine_instance()->ctx.platform->mouse_pressed( gs_mouse_lbutton );
  1011. return in_rect( mp, (gs_vec2){ _x, _y }, (gs_vec2){ _w, _h } ) && clicked ;
  1012. }
  1013. #define __gui_interaction( x, y, w, h, c, str, id )\
  1014. do {\
  1015. if ( (id) == g_material_selection ) {\
  1016. const s32 b = 2;\
  1017. gui_rect( g_ui_buffer, x - b / 2, y - b / 2, w + b, h + b, (color_t){ 200, 150, 20, 255 } );\
  1018. }\
  1019. gs_vec2 mp = calculate_mouse_position();\
  1020. if ( in_rect( mp, (gs_vec2){ (x), (y) }, (gs_vec2){ (w), (h) })) {\
  1021. interaction |= true;\
  1022. char _str[] = (str);\
  1023. color_t col = (color_t){ 255, 255, 255, 255 };\
  1024. color_t s_col = (color_t){ 10, 10, 10, 255 };\
  1025. color_t r_col = (color_t){ 5, 5, 5, 170 };\
  1026. /*Draw rect around text as well for easier viewing*/\
  1027. gui_rect(g_ui_buffer, g_texture_width / 2 - 50, 15, 100, 20, r_col);\
  1028. draw_string_at(&g_font, g_ui_buffer, g_texture_width / 2 + 1 - (sizeof(str) * 5) / 2, 20 - 1, _str, sizeof(_str), s_col);\
  1029. draw_string_at(&g_font, g_ui_buffer, g_texture_width / 2 - (sizeof(str) * 5) / 2, 20, _str, sizeof(_str), col);\
  1030. }\
  1031. if ( gui_rect( g_ui_buffer, x, y, w, h, c ) ) {\
  1032. g_material_selection = id;\
  1033. }\
  1034. } while ( 0 )
  1035. b32 update_ui()
  1036. {
  1037. b32 interaction = false;
  1038. gs_graphics_i* gfx = gs_engine_instance()->ctx.graphics;
  1039. gs_platform_i* platform = gs_engine_instance()->ctx.platform;
  1040. // Cache transformed mouse position
  1041. gs_vec2 mp = calculate_mouse_position();
  1042. // Do ui stuff
  1043. memset( g_ui_buffer, 0, g_texture_width * g_texture_height * sizeof(color_t) );
  1044. // Material selection panel gui
  1045. if ( g_show_material_selection_panel )
  1046. {
  1047. const s32 offset = 12;
  1048. s32 xoff = 20;
  1049. s32 base = 10;
  1050. // Sand Selection
  1051. __gui_interaction(g_texture_width - xoff, base + offset * 0, 10, 10, mat_col_sand, "Sand", mat_sel_sand );
  1052. __gui_interaction(g_texture_width - xoff, base + offset * 1, 10, 10, mat_col_water, "Water", mat_sel_water );
  1053. __gui_interaction(g_texture_width - xoff, base + offset * 2, 10, 10, mat_col_smoke, "Smoke", mat_sel_smoke );
  1054. __gui_interaction(g_texture_width - xoff, base + offset * 3, 10, 10, mat_col_fire, "Fire", mat_sel_fire );
  1055. __gui_interaction(g_texture_width - xoff, base + offset * 4, 10, 10, mat_col_steam, "Steam", mat_sel_steam );
  1056. __gui_interaction(g_texture_width - xoff, base + offset * 5, 10, 10, mat_col_oil, "Oil", mat_sel_oil );
  1057. __gui_interaction(g_texture_width - xoff, base + offset * 6, 10, 10, mat_col_salt, "Salt", mat_sel_salt );
  1058. __gui_interaction(g_texture_width - xoff, base + offset * 7, 10, 10, mat_col_wood, "Wood", mat_sel_wood );
  1059. __gui_interaction(g_texture_width - xoff, base + offset * 8, 10, 10, mat_col_stone, "Stone", mat_sel_stone );
  1060. __gui_interaction(g_texture_width - xoff, base + offset * 9, 10, 10, mat_col_lava, "Lava", mat_sel_lava );
  1061. __gui_interaction(g_texture_width - xoff, base + offset * 10, 10, 10, mat_col_gunpowder, "GunPowder", mat_sel_gunpowder );
  1062. __gui_interaction(g_texture_width - xoff, base + offset * 11, 10, 10, mat_col_acid, "Acid", mat_sel_acid );
  1063. }
  1064. if ( g_show_frame_count ) {
  1065. char frame_time_str[256];
  1066. gs_snprintf (frame_time_str, sizeof(frame_time_str), "frame: %.2f ms", platform->time.frame );
  1067. draw_string_at( &g_font, g_ui_buffer, 10, 10, frame_time_str, strlen(frame_time_str), (color_t){ 255, 255, 255, 255 } );
  1068. char sim_state_str[256];
  1069. gs_snprintf (sim_state_str, sizeof(sim_state_str), "state: %s", g_run_simulation ? "running" : "paused" );
  1070. draw_string_at( &g_font, g_ui_buffer, 10, 20, sim_state_str, strlen(sim_state_str), (color_t){ 255, 255, 255, 255 } );
  1071. }
  1072. // Draw circle around mouse pointer
  1073. s32 R = g_selection_radius;
  1074. circleBres((s32)mp.x, (s32)mp.y, R);
  1075. // Upload our updated texture data to GPU
  1076. gs_texture_parameter_desc t_desc = gs_texture_parameter_desc_default();
  1077. t_desc.mag_filter = gs_nearest;
  1078. t_desc.min_filter = gs_nearest;
  1079. t_desc.generate_mips = false;
  1080. t_desc.width = g_texture_width;
  1081. t_desc.height = g_texture_height;
  1082. t_desc.num_comps = 4;
  1083. t_desc.data = g_ui_buffer;
  1084. gfx->update_texture_data( g_tex_ui, t_desc ) ;
  1085. return interaction;
  1086. }
  1087. void render_scene()
  1088. {
  1089. // Graphics api instance
  1090. gs_graphics_i* gfx = gs_engine_instance()->ctx.graphics;
  1091. // Platform api instance
  1092. gs_platform_i* platform = gs_engine_instance()->ctx.platform;
  1093. // Upload our updated texture data to GPU
  1094. gs_texture_parameter_desc t_desc = gs_texture_parameter_desc_default();
  1095. t_desc.mag_filter = gs_nearest;
  1096. t_desc.min_filter = gs_nearest;
  1097. t_desc.generate_mips = false;
  1098. t_desc.width = g_texture_width;
  1099. t_desc.height = g_texture_height;
  1100. t_desc.num_comps = 4;
  1101. t_desc.data = g_texture_buffer;
  1102. gfx->update_texture_data( g_tex, t_desc );
  1103. gs_vec2 ws = platform->window_size( g_window );
  1104. gs_vec2 fbs = platform->frame_buffer_size( g_window );
  1105. b32 flip_y = false;
  1106. // Default state set up
  1107. gfx->set_depth_enabled( g_cb, false );
  1108. gfx->set_face_culling( g_cb, gs_face_culling_disabled );
  1109. // Bind our render target and render offscreen
  1110. gfx->bind_frame_buffer( g_cb, g_fb );
  1111. {
  1112. // Bind frame buffer attachment for rendering
  1113. gfx->set_frame_buffer_attachment( g_cb, g_rt, 0 );
  1114. // Set clear color and clear screen
  1115. f32 clear_color[4] = { 0.1f, 0.1f, 0.1f, 1.f };
  1116. gfx->set_view_clear( g_cb, clear_color );
  1117. // This is to handle mac's retina high dpi for now until I fix that internally.
  1118. gfx->set_view_port( g_cb, g_texture_width, g_texture_height );
  1119. gfx->bind_shader( g_cb, g_shader );
  1120. gfx->bind_uniform( g_cb, u_flip_y, &flip_y );
  1121. gfx->bind_vertex_buffer( g_cb, g_vbo );
  1122. gfx->bind_index_buffer( g_cb, g_ibo );
  1123. gfx->bind_texture( g_cb, u_tex, g_tex, 0 );
  1124. gfx->draw_indexed( g_cb, 6, 0 );
  1125. }
  1126. // Unbind offscreen buffer
  1127. gfx->unbind_frame_buffer( g_cb );
  1128. // Bind frame buffer for post processing
  1129. gfx->bind_frame_buffer( g_cb, g_fb );
  1130. {
  1131. // Brightness pass
  1132. {
  1133. bright_filter_pass_parameters_t params = (bright_filter_pass_parameters_t){ g_rt };
  1134. render_pass_i* p = gs_cast( render_pass_i, &g_bright_pass );
  1135. p->pass( g_cb, p, &params );
  1136. }
  1137. // Blur pass
  1138. {
  1139. blur_pass_parameters_t params = (blur_pass_parameters_t){ g_bright_pass.data.render_target };
  1140. render_pass_i* p = gs_cast( render_pass_i, &g_blur_pass );
  1141. p->pass( g_cb, p, &params );
  1142. }
  1143. // composite pass w/ gamma correction
  1144. {
  1145. composite_pass_parameters_t params = (composite_pass_parameters_t){ g_rt, g_blur_pass.data.blur_render_target_b };
  1146. render_pass_i* p = gs_cast( render_pass_i, &g_composite_pass );
  1147. p->pass( g_cb, p, &params );
  1148. }
  1149. }
  1150. gfx->unbind_frame_buffer( g_cb );
  1151. // Back buffer Presentation
  1152. {
  1153. // Set clear color and clear screen
  1154. f32 clear_color[4] = { 0.2f, 0.2f, 0.2f, 1.f };
  1155. gfx->set_view_clear( g_cb, clear_color );
  1156. gfx->set_depth_enabled( g_cb, false );
  1157. // This is to handle mac's retina high dpi for now until I fix that internally.
  1158. gfx->set_view_port( g_cb, fbs.x, fbs.y );
  1159. f32 t = gs_engine_instance()->ctx.platform->elapsed_time() * gs_engine_instance()->ctx.platform->time.delta * 0.001f;
  1160. flip_y = true;
  1161. gfx->bind_shader( g_cb, g_shader );
  1162. gfx->bind_uniform( g_cb, u_flip_y, &flip_y );
  1163. gfx->bind_vertex_buffer( g_cb, g_vbo );
  1164. gfx->bind_index_buffer( g_cb, g_ibo );
  1165. // Draw final composited image
  1166. if (g_use_post_processing) {
  1167. gfx->bind_texture( g_cb, u_tex, g_composite_pass.data.render_target, 0 );
  1168. } else {
  1169. gfx->bind_texture( g_cb, u_tex, g_rt, 0 );
  1170. }
  1171. gfx->draw_indexed( g_cb, 6, 0 );
  1172. // Draw UI on top
  1173. gfx->bind_texture( g_cb, u_tex, g_tex_ui, 0 );
  1174. gfx->draw_indexed( g_cb, 6, 0);
  1175. }
  1176. // Submit command buffer for rendering
  1177. gfx->submit_command_buffer( g_cb );
  1178. }
  1179. void write_data( u32 idx, particle_t p )
  1180. {
  1181. // Write into particle data for id value
  1182. g_world_particle_data[ idx ] = p;
  1183. g_texture_buffer[ idx ] = p.color;
  1184. }
  1185. void update_salt( u32 x, u32 y )
  1186. {
  1187. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  1188. u32 read_idx = compute_idx( x, y );
  1189. particle_t* p = &g_world_particle_data[ read_idx ];
  1190. u32 write_idx = read_idx;
  1191. u32 fall_rate = 2;
  1192. u32 spread_rate = 5;
  1193. s32 lx, ly;
  1194. p->velocity.y = gs_clamp( p->velocity.y + (gravity * dt), -10.f, 10.f );
  1195. p->has_been_updated_this_frame = true;
  1196. // If in liquid, chance to dissolve itself.
  1197. if ( is_in_liquid( x, y, &lx, &ly ) ) {
  1198. if ( random_val( 0, 1000 ) == 0 ) {
  1199. write_data( read_idx, particle_empty() );
  1200. return;
  1201. }
  1202. }
  1203. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  1204. // if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) && get_particle_at( x, y + 1 ).id != mat_id_water ) {
  1205. if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) ) {
  1206. p->velocity.y /= 2.f;
  1207. }
  1208. s32 r = 1;
  1209. s32 l = -r;
  1210. s32 u = fall_rate;
  1211. s32 v_idx = compute_idx ( x + (s32)p->velocity.x, y + (s32)p->velocity.y );
  1212. s32 b_idx = compute_idx( x, y + u );
  1213. s32 bl_idx = compute_idx( x + l, y + u );
  1214. s32 br_idx = compute_idx( x + r, y + u );
  1215. s32 l_idx = compute_idx( x + l, y );
  1216. s32 r_idx = compute_idx( x + r, y );
  1217. s32 vx = (s32)p->velocity.x, vy = (s32)p->velocity.y;
  1218. if ( in_bounds( x + vx, y + vy ) && (is_empty( x + vx, y + vy ) ) ) {
  1219. write_data( v_idx, *p );
  1220. write_data( read_idx, particle_empty() );
  1221. }
  1222. else if ( is_in_liquid( x, y, &lx, &ly ) && random_val( 0, 10 ) == 0 ) {
  1223. particle_t tmp_b = get_particle_at( lx, ly );
  1224. write_data( compute_idx( lx, ly ), *p );
  1225. write_data( read_idx, tmp_b );
  1226. }
  1227. // Simple falling, changing the velocity here ruins everything. I need to redo this entire simulation.
  1228. else if ( in_bounds( x, y + 1 ) && (( is_empty( x, y + 1 ) ) ) ) {
  1229. u32 idx = compute_idx( x, y + 1 );
  1230. p->velocity.y += (gravity * dt );
  1231. particle_t tmp_a = g_world_particle_data[ read_idx ];
  1232. particle_t tmp_b = g_world_particle_data[ idx ];
  1233. write_data( idx, tmp_a );
  1234. write_data( read_idx, tmp_b );
  1235. }
  1236. else if ( in_bounds( x - 1, y + 1 ) && ( is_empty( x - 1, y + 1 ) ) ) {
  1237. u32 idx = compute_idx( x - 1, y + 1 );
  1238. p->velocity.x = random_val( 0, 1 ) == 0 ? -1.2f : 1.2f;
  1239. p->velocity.y += (gravity * dt );
  1240. particle_t tmp_a = g_world_particle_data[ read_idx ];
  1241. particle_t tmp_b = g_world_particle_data[ idx ];
  1242. write_data( idx, tmp_a );
  1243. write_data( read_idx, tmp_b );
  1244. }
  1245. else if ( in_bounds( x + 1, y + 1 ) && ( is_empty( x + 1, y + 1 )) ) {
  1246. u32 idx = compute_idx( x + 1, y + 1 );
  1247. p->velocity.x = random_val( 0, 1 ) == 0 ? -1.2f : 1.2f;
  1248. p->velocity.y += (gravity * dt );
  1249. particle_t tmp_a = g_world_particle_data[ read_idx ];
  1250. particle_t tmp_b = g_world_particle_data[ idx ];
  1251. write_data( idx, tmp_a );
  1252. write_data( read_idx, tmp_b );
  1253. }
  1254. }
  1255. void update_sand( u32 x, u32 y )
  1256. {
  1257. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  1258. // For water, same as sand, but we'll check immediate left and right as well
  1259. u32 read_idx = compute_idx( x, y );
  1260. particle_t* p = &g_world_particle_data[ read_idx ];
  1261. u32 write_idx = read_idx;
  1262. u32 fall_rate = 4;
  1263. p->velocity.y = gs_clamp( p->velocity.y + (gravity * dt), -10.f, 10.f );
  1264. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  1265. if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) && get_particle_at( x, y + 1 ).id != mat_id_water ) {
  1266. p->velocity.y /= 2.f;
  1267. }
  1268. s32 vi_x = x + (s32)p->velocity.x;
  1269. s32 vi_y = y + (s32)p->velocity.y;
  1270. // Check to see if you can swap first with other element below you
  1271. u32 b_idx = compute_idx( x, y + 1 );
  1272. u32 br_idx = compute_idx( x + 1, y + 1 );
  1273. u32 bl_idx = compute_idx( x - 1, y + 1 );
  1274. s32 lx, ly;
  1275. particle_t tmp_a = g_world_particle_data[ read_idx ];
  1276. // Physics (using velocity)
  1277. if ( in_bounds( vi_x, vi_y ) && (( is_empty( vi_x, vi_y ) ||
  1278. ((( g_world_particle_data[ compute_idx( vi_x, vi_y ) ].id == mat_id_water ) &&
  1279. !g_world_particle_data[ compute_idx( vi_x, vi_y ) ].has_been_updated_this_frame &&
  1280. gs_vec2_len(g_world_particle_data[compute_idx(vi_x, vi_y)].velocity) - gs_vec2_len(tmp_a.velocity) > 10.f ) ) ) ) ) {
  1281. particle_t tmp_b = g_world_particle_data[ compute_idx( vi_x, vi_y ) ];
  1282. // Try to throw water out
  1283. if ( tmp_b.id == mat_id_water ) {
  1284. s32 rx = random_val( -2, 2 );
  1285. tmp_b.velocity = (gs_vec2){ rx, -4.f };
  1286. write_data( compute_idx( vi_x, vi_y ), tmp_a );
  1287. for( s32 i = -10; i < 0; ++i ) {
  1288. for ( s32 j = -10; j < 10; ++j ) {
  1289. if ( is_empty( vi_x + j, vi_y + i ) ) {
  1290. write_data( compute_idx( vi_x + j, vi_y + i ), tmp_b );
  1291. break;
  1292. }
  1293. }
  1294. }
  1295. // Couldn't write there, so, uh, destroy it.
  1296. write_data( read_idx, particle_empty() );
  1297. }
  1298. else if ( is_empty( vi_x, vi_y ) ) {
  1299. write_data( compute_idx( vi_x, vi_y ), tmp_a );
  1300. write_data( read_idx, tmp_b );
  1301. }
  1302. }
  1303. // Simple falling, changing the velocity here ruins everything. I need to redo this entire simulation.
  1304. else if ( in_bounds( x, y + 1 ) && (( is_empty( x, y + 1 ) || ( g_world_particle_data[ b_idx ].id == mat_id_water ) ) ) ) {
  1305. p->velocity.y += (gravity * dt );
  1306. particle_t tmp_b = get_particle_at( x, y + 1 );
  1307. write_data( b_idx, *p );
  1308. write_data( read_idx, tmp_b );
  1309. }
  1310. else if ( in_bounds( x - 1, y + 1 ) && (( is_empty( x - 1, y + 1 ) || g_world_particle_data[ bl_idx ].id == mat_id_water )) ) {
  1311. p->velocity.x = is_in_liquid( x, y, &lx, &ly ) ? 0.f : random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  1312. p->velocity.y += (gravity * dt );
  1313. particle_t tmp_b = get_particle_at( x - 1, y + 1 );
  1314. write_data( bl_idx, *p );
  1315. write_data( read_idx, tmp_b );
  1316. }
  1317. else if ( in_bounds( x + 1, y + 1 ) && (( is_empty( x + 1, y + 1 ) || g_world_particle_data[ br_idx ].id == mat_id_water )) ) {
  1318. p->velocity.x = is_in_liquid( x, y, &lx, &ly ) ? 0.f : random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  1319. p->velocity.y += (gravity * dt );
  1320. particle_t tmp_b = get_particle_at( x + 1, y + 1 );
  1321. write_data( br_idx, *p );
  1322. write_data( read_idx, tmp_b );
  1323. }
  1324. else if ( is_in_liquid( x, y, &lx, &ly ) && random_val( 0, 10 ) == 0 ) {
  1325. particle_t tmp_b = get_particle_at( lx, ly );
  1326. write_data( compute_idx( lx, ly ), *p );
  1327. write_data( read_idx, tmp_b );
  1328. }
  1329. }
  1330. void update_gunpowder( u32 x, u32 y )
  1331. {
  1332. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  1333. // For water, same as sand, but we'll check immediate left and right as well
  1334. u32 read_idx = compute_idx( x, y );
  1335. particle_t* p = &g_world_particle_data[ read_idx ];
  1336. u32 write_idx = read_idx;
  1337. u32 fall_rate = 4;
  1338. p->velocity.y = gs_clamp( p->velocity.y + (gravity * dt), -10.f, 10.f );
  1339. // p->velocity.x = gs_clamp( p->velocity.x, -5.f, 5.f );
  1340. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  1341. if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) && get_particle_at( x, y + 1 ).id != mat_id_water ) {
  1342. p->velocity.y /= 2.f;
  1343. // p->velocity.x /= 1.2f;
  1344. }
  1345. s32 vi_x = x + (s32)p->velocity.x;
  1346. s32 vi_y = y + (s32)p->velocity.y;
  1347. // Check to see if you can swap first with other element below you
  1348. u32 b_idx = compute_idx( x, y + 1 );
  1349. u32 br_idx = compute_idx( x + 1, y + 1 );
  1350. u32 bl_idx = compute_idx( x - 1, y + 1 );
  1351. s32 lx, ly;
  1352. particle_t tmp_a = g_world_particle_data[ read_idx ];
  1353. // Physics (using velocity)
  1354. if ( in_bounds( vi_x, vi_y ) && (( is_empty( vi_x, vi_y ) ||
  1355. ((( g_world_particle_data[ compute_idx( vi_x, vi_y ) ].id == mat_id_water ) &&
  1356. !g_world_particle_data[ compute_idx( vi_x, vi_y ) ].has_been_updated_this_frame &&
  1357. gs_vec2_len(g_world_particle_data[compute_idx(vi_x, vi_y)].velocity) - gs_vec2_len(tmp_a.velocity) > 10.f ) ) ) ) ) {
  1358. particle_t tmp_b = g_world_particle_data[ compute_idx( vi_x, vi_y ) ];
  1359. // Try to throw water out
  1360. if ( tmp_b.id == mat_id_water ) {
  1361. s32 rx = random_val( -2, 2 );
  1362. tmp_b.velocity = (gs_vec2){ rx, -4.f };
  1363. write_data( compute_idx( vi_x, vi_y ), tmp_a );
  1364. for( s32 i = -10; i < 0; ++i ) {
  1365. for ( s32 j = -10; j < 10; ++j ) {
  1366. if ( is_empty( vi_x + j, vi_y + i ) ) {
  1367. write_data( compute_idx( vi_x + j, vi_y + i ), tmp_b );
  1368. break;
  1369. }
  1370. }
  1371. }
  1372. // Couldn't write there, so, uh, destroy it.
  1373. write_data( read_idx, particle_empty() );
  1374. }
  1375. else if ( is_empty( vi_x, vi_y ) ) {
  1376. write_data( compute_idx( vi_x, vi_y ), tmp_a );
  1377. write_data( read_idx, tmp_b );
  1378. }
  1379. }
  1380. // Simple falling, changing the velocity here ruins everything. I need to redo this entire simulation.
  1381. else if ( in_bounds( x, y + 1 ) && (( is_empty( x, y + 1 ) || ( g_world_particle_data[ b_idx ].id == mat_id_water ) ) ) ) {
  1382. p->velocity.y += (gravity * dt );
  1383. particle_t tmp_b = get_particle_at( x, y + 1 );
  1384. write_data( b_idx, *p );
  1385. write_data( read_idx, tmp_b );
  1386. }
  1387. else if ( in_bounds( x - 1, y + 1 ) && (( is_empty( x - 1, y + 1 ) || g_world_particle_data[ bl_idx ].id == mat_id_water )) ) {
  1388. p->velocity.x = is_in_liquid( x, y, &lx, &ly ) ? 0.f : random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  1389. p->velocity.y += (gravity * dt );
  1390. particle_t tmp_b = get_particle_at( x - 1, y + 1 );
  1391. write_data( bl_idx, *p );
  1392. write_data( read_idx, tmp_b );
  1393. }
  1394. else if ( in_bounds( x + 1, y + 1 ) && (( is_empty( x + 1, y + 1 ) || g_world_particle_data[ br_idx ].id == mat_id_water )) ) {
  1395. p->velocity.x = is_in_liquid( x, y, &lx, &ly ) ? 0.f : random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  1396. p->velocity.y += (gravity * dt );
  1397. particle_t tmp_b = get_particle_at( x + 1, y + 1 );
  1398. write_data( br_idx, *p );
  1399. write_data( read_idx, tmp_b );
  1400. }
  1401. else if ( is_in_liquid( x, y, &lx, &ly ) && random_val( 0, 10 ) == 0 ) {
  1402. particle_t tmp_b = get_particle_at( lx, ly );
  1403. write_data( compute_idx( lx, ly ), *p );
  1404. write_data( read_idx, tmp_b );
  1405. }
  1406. }
  1407. void update_steam( u32 x, u32 y )
  1408. {
  1409. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  1410. // For water, same as sand, but we'll check immediate left and right as well
  1411. u32 read_idx = compute_idx( x, y );
  1412. particle_t* p = &g_world_particle_data[ read_idx ];
  1413. u32 write_idx = read_idx;
  1414. u32 fall_rate = 4;
  1415. if ( p->life_time > 10.f ) {
  1416. write_data( read_idx, particle_empty() );
  1417. return;
  1418. }
  1419. if ( p->has_been_updated_this_frame ) {
  1420. return;
  1421. }
  1422. p->has_been_updated_this_frame = true;
  1423. // Smoke rises over time. This might cause issues, actually...
  1424. p->velocity.y = gs_clamp( p->velocity.y - (gravity * dt ), -2.f, 10.f );
  1425. p->velocity.x = gs_clamp( p->velocity.x + (f32)random_val( -100, 100 ) / 100.f, -1.f, 1.f );
  1426. // Change color based on life_time
  1427. p->color.r = (u8)( gs_clamp( (gs_interp_linear( 0.f, 10.f, p->life_time ) / 10.f) * 255.f, 150.f, 255.f ) );
  1428. p->color.g = (u8)( gs_clamp( (gs_interp_linear( 0.f, 10.f, p->life_time ) / 10.f) * 255.f, 150.f, 255.f ) );
  1429. p->color.b = (u8)( gs_clamp( (gs_interp_linear( 0.f, 10.f, p->life_time ) / 10.f) * 255.f, 150.f, 255.f ) );
  1430. p->color.a = (u8)( gs_clamp( (gs_interp_linear( 10.f, 0.f, p->life_time ) / 10.f) * 255.f, 10.f, 255.f ) );
  1431. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  1432. if ( in_bounds( x, y - 1 ) && !is_empty( x, y - 1 ) && get_particle_at( x, y - 1 ).id != mat_id_water ) {
  1433. p->velocity.y /= 2.f;
  1434. }
  1435. s32 vi_x = x + (s32)p->velocity.x;
  1436. s32 vi_y = y + (s32)p->velocity.y;
  1437. if ( in_bounds( vi_x, vi_y ) && ( (is_empty( vi_x, vi_y ) || get_particle_at( vi_x, vi_y ).id == mat_id_water || get_particle_at( vi_x, vi_y ).id == mat_id_fire ) ) ) {
  1438. particle_t tmp_b = g_world_particle_data[ compute_idx( vi_x, vi_y ) ];
  1439. // Try to throw water out
  1440. if ( tmp_b.id == mat_id_water ) {
  1441. tmp_b.has_been_updated_this_frame = true;
  1442. s32 rx = random_val( -2, 2 );
  1443. tmp_b.velocity = (gs_vec2){ rx, -3.f };
  1444. write_data( compute_idx( vi_x, vi_y ), *p );
  1445. write_data( read_idx, tmp_b );
  1446. }
  1447. else if ( is_empty( vi_x, vi_y ) ) {
  1448. write_data( compute_idx( vi_x, vi_y ), *p );
  1449. write_data( read_idx, tmp_b );
  1450. }
  1451. }
  1452. // Simple falling, changing the velocity here ruins everything. I need to redo this entire simulation.
  1453. else if ( in_bounds( x, y - 1 ) && (( is_empty( x, y - 1 ) || ( get_particle_at( x, y - 1 ).id == mat_id_water ) || get_particle_at(x,y-1).id == mat_id_fire ) ) ) {
  1454. p->velocity.y -= (gravity * dt );
  1455. particle_t tmp_b = get_particle_at( x, y - 1 );
  1456. write_data( compute_idx( x, y - 1 ), *p );
  1457. write_data( read_idx, tmp_b );
  1458. }
  1459. else if ( in_bounds( x - 1, y - 1 ) && (( is_empty( x - 1, y - 1 ) || get_particle_at( x - 1, y - 1 ).id == mat_id_water ) || get_particle_at(x-1, y-1).id == mat_id_fire) ) {
  1460. p->velocity.x = random_val( 0, 1 ) == 0 ? -1.2f : 1.2f;
  1461. p->velocity.y -= (gravity * dt );
  1462. particle_t tmp_b = get_particle_at( x - 1, y - 1 );
  1463. write_data( compute_idx( x - 1, y - 1 ), *p );
  1464. write_data( read_idx, tmp_b );
  1465. }
  1466. else if ( in_bounds( x + 1, y - 1 ) && (( is_empty( x + 1, y - 1 ) || get_particle_at( x + 1, y - 1 ).id == mat_id_water ) || get_particle_at(x+1, y-1).id == mat_id_fire) ) {
  1467. p->velocity.x = random_val( 0, 1 ) == 0 ? -1.2f : 1.2f;
  1468. p->velocity.y -= (gravity * dt );
  1469. particle_t tmp_b = get_particle_at( x + 1, y - 1 );
  1470. write_data( compute_idx( x + 1, y - 1 ), *p );
  1471. write_data( read_idx, tmp_b );
  1472. }
  1473. // Can move if in liquid
  1474. else if ( in_bounds( x + 1, y ) && ( get_particle_at( x + 1, y ).id == mat_id_water ) ) {
  1475. u32 idx = compute_idx( x + 1, y );
  1476. particle_t tmp_b = g_world_particle_data[ idx ];
  1477. write_data( idx, *p );
  1478. write_data( read_idx, tmp_b );
  1479. }
  1480. else if ( in_bounds( x - 1, y ) && ( g_world_particle_data[ compute_idx( x - 1, y ) ].id == mat_id_water ) ) {
  1481. u32 idx = compute_idx( x - 1, y );
  1482. particle_t tmp_b = g_world_particle_data[ idx ];
  1483. write_data( idx, *p );
  1484. write_data( read_idx, tmp_b );
  1485. }
  1486. else {
  1487. write_data( read_idx, *p );
  1488. }
  1489. }
  1490. void update_smoke( u32 x, u32 y )
  1491. {
  1492. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  1493. // For water, same as sand, but we'll check immediate left and right as well
  1494. u32 read_idx = compute_idx( x, y );
  1495. particle_t* p = &g_world_particle_data[ read_idx ];
  1496. u32 write_idx = read_idx;
  1497. u32 fall_rate = 4;
  1498. if ( p->life_time > 10.f ) {
  1499. write_data( read_idx, particle_empty() );
  1500. return;
  1501. }
  1502. if ( p->has_been_updated_this_frame ) {
  1503. return;
  1504. }
  1505. p->has_been_updated_this_frame = true;
  1506. // Smoke rises over time. This might cause issues, actually...
  1507. p->velocity.y = gs_clamp( p->velocity.y - (gravity * dt ), -2.f, 10.f );
  1508. p->velocity.x = gs_clamp( p->velocity.x + (f32)random_val( -100, 100 ) / 100.f, -1.f, 1.f );
  1509. // Change color based on life_time
  1510. p->color.r = (u8)( gs_clamp( (gs_interp_linear( 10.f, 0.f, p->life_time * 0.5f ) / 10.f) * 150.f, 0.f, 150.f ) );
  1511. p->color.g = (u8)( gs_clamp( (gs_interp_linear( 10.f, 0.f, p->life_time * 0.5f ) / 10.f) * 120.f, 0.f, 120.f ) );
  1512. p->color.b = (u8)( gs_clamp( (gs_interp_linear( 10.f, 0.f, p->life_time * 0.5f ) / 10.f) * 100.f, 0.f, 100.f ) );
  1513. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  1514. if ( in_bounds( x, y - 1 ) && !is_empty( x, y - 1 ) && get_particle_at( x, y - 1 ).id != mat_id_water ) {
  1515. p->velocity.y /= 2.f;
  1516. }
  1517. s32 vi_x = x + (s32)p->velocity.x;
  1518. s32 vi_y = y + (s32)p->velocity.y;
  1519. // if ( in_bounds( vi_x, vi_y ) && ( (is_empty( vi_x, vi_y ) || get_particle_at( vi_x, vi_y ).id == mat_id_water || get_particle_at( vi_x, vi_y ).id == mat_id_fire ) ) ) {
  1520. if ( in_bounds( vi_x, vi_y ) && get_particle_at( vi_x, vi_y ).id != mat_id_smoke ) {
  1521. particle_t tmp_b = g_world_particle_data[ compute_idx( vi_x, vi_y ) ];
  1522. // Try to throw water out
  1523. if ( tmp_b.id == mat_id_water ) {
  1524. tmp_b.has_been_updated_this_frame = true;
  1525. s32 rx = random_val( -2, 2 );
  1526. tmp_b.velocity = (gs_vec2){ rx, -3.f };
  1527. write_data( compute_idx( vi_x, vi_y ), *p );
  1528. write_data( read_idx, tmp_b );
  1529. }
  1530. else if ( is_empty( vi_x, vi_y ) ) {
  1531. write_data( compute_idx( vi_x, vi_y ), *p );
  1532. write_data( read_idx, tmp_b );
  1533. }
  1534. }
  1535. // Simple falling, changing the velocity here ruins everything. I need to redo this entire simulation.
  1536. else if ( in_bounds( x, y - 1 ) && get_particle_at( x, y - 1 ).id != mat_id_smoke &&
  1537. get_particle_at( x, y - 1 ).id != mat_id_wood &&
  1538. get_particle_at( x, y - 1 ).id != mat_id_stone ) {
  1539. p->velocity.y -= (gravity * dt );
  1540. particle_t tmp_b = get_particle_at( x, y - 1 );
  1541. write_data( compute_idx( x, y - 1 ), *p );
  1542. write_data( read_idx, tmp_b );
  1543. }
  1544. else if ( in_bounds( x - 1, y - 1 ) && get_particle_at( x - 1, y - 1 ).id != mat_id_smoke &&
  1545. get_particle_at( x - 1, y - 1 ).id != mat_id_wood &&
  1546. get_particle_at( x - 1, y - 1 ).id != mat_id_stone ) {
  1547. p->velocity.x = random_val( 0, 1 ) == 0 ? -1.2f : 1.2f;
  1548. p->velocity.y -= (gravity * dt );
  1549. particle_t tmp_b = get_particle_at( x - 1, y - 1 );
  1550. write_data( compute_idx( x - 1, y - 1 ), *p );
  1551. write_data( read_idx, tmp_b );
  1552. }
  1553. else if ( in_bounds( x + 1, y - 1 ) && get_particle_at( x + 1, y - 1 ).id != mat_id_smoke &&
  1554. get_particle_at( x + 1, y - 1 ).id != mat_id_wood &&
  1555. get_particle_at( x + 1, y - 1 ).id != mat_id_stone ) {
  1556. p->velocity.x = random_val( 0, 1 ) == 0 ? -1.2f : 1.2f;
  1557. p->velocity.y -= (gravity * dt );
  1558. particle_t tmp_b = get_particle_at( x + 1, y - 1 );
  1559. write_data( compute_idx( x + 1, y - 1 ), *p );
  1560. write_data( read_idx, tmp_b );
  1561. }
  1562. // Can move if in liquid
  1563. else if ( in_bounds( x + 1, y ) && get_particle_at( x + 1, y ).id != mat_id_smoke &&
  1564. get_particle_at( x + 1, y ).id != mat_id_wood &&
  1565. get_particle_at( x + 1, y ).id != mat_id_stone ) {
  1566. u32 idx = compute_idx( x + 1, y );
  1567. particle_t tmp_b = g_world_particle_data[ idx ];
  1568. write_data( idx, *p );
  1569. write_data( read_idx, tmp_b );
  1570. }
  1571. else if ( in_bounds( x - 1, y ) && get_particle_at( x - 1, y ).id != mat_id_smoke &&
  1572. get_particle_at( x - 1, y ).id != mat_id_wood &&
  1573. get_particle_at( x - 1, y ).id != mat_id_stone ) {
  1574. u32 idx = compute_idx( x - 1, y );
  1575. particle_t tmp_b = g_world_particle_data[ idx ];
  1576. write_data( idx, *p );
  1577. write_data( read_idx, tmp_b );
  1578. }
  1579. else {
  1580. write_data( read_idx, *p );
  1581. }
  1582. }
  1583. void update_ember( u32 x, u32 y )
  1584. {
  1585. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  1586. // For water, same as sand, but we'll check immediate left and right as well
  1587. u32 read_idx = compute_idx( x, y );
  1588. particle_t* p = &g_world_particle_data[ read_idx ];
  1589. u32 write_idx = read_idx;
  1590. u32 fall_rate = 4;
  1591. if ( p->life_time > 0.5f ) {
  1592. write_data( read_idx, particle_empty() );
  1593. return;
  1594. }
  1595. if ( p->has_been_updated_this_frame ) {
  1596. return;
  1597. }
  1598. p->has_been_updated_this_frame = true;
  1599. p->velocity.y = gs_clamp( p->velocity.y - (gravity * dt ), -2.f, 10.f );
  1600. p->velocity.x = gs_clamp( p->velocity.x + (f32)random_val( -100, 100 ) / 100.f, -1.f, 1.f );
  1601. // If directly on top of some wall, then replace it
  1602. if ( in_bounds( x, y + 1 ) && get_particle_at( x, y + 1 ).id == mat_id_wood && random_val( 0, 200 ) == 0 ) {
  1603. write_data( compute_idx( x, y + 1 ), particle_fire() );
  1604. }
  1605. else if ( in_bounds( x + 1, y + 1 ) && get_particle_at( x + 1, y + 1 ).id == mat_id_wood && random_val( 0, 200 ) == 0 ) {
  1606. write_data( compute_idx( x + 1, y + 1 ), particle_fire() );
  1607. }
  1608. else if ( in_bounds( x - 1, y + 1 ) && get_particle_at( x - 1, y + 1 ).id == mat_id_wood && random_val( 0, 200 ) == 0 ) {
  1609. write_data( compute_idx( x - 1, y + 1 ), particle_fire() );
  1610. }
  1611. else if ( in_bounds( x - 1, y ) && get_particle_at( x - 1, y ).id == mat_id_wood && random_val( 0, 200 ) == 0 ) {
  1612. write_data( compute_idx( x - 1, y ), particle_fire() );
  1613. }
  1614. else if ( in_bounds( x + 1, y ) && get_particle_at( x + 1, y ).id == mat_id_wood && random_val( 0, 200 ) == 0 ) {
  1615. write_data( compute_idx( x + 1, y ), particle_fire() );
  1616. }
  1617. else if ( in_bounds( x + 1, y - 1 ) && get_particle_at( x + 1, y - 1 ).id == mat_id_wood && random_val( 0, 200 ) == 0 ) {
  1618. write_data( compute_idx( x + 1, y - 1 ), particle_fire() );
  1619. }
  1620. else if ( in_bounds( x - 1, y - 1 ) && get_particle_at( x - 1, y - 1 ).id == mat_id_wood && random_val( 0, 200 ) == 0 ) {
  1621. write_data( compute_idx( x - 1, y - 1 ), particle_fire() );
  1622. }
  1623. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  1624. if ( in_bounds( x, y - 1 ) && !is_empty( x, y - 1 ) && get_particle_at( x, y - 1 ).id != mat_id_water ) {
  1625. p->velocity.y /= 2.f;
  1626. }
  1627. s32 vi_x = x + (s32)p->velocity.x;
  1628. s32 vi_y = y + (s32)p->velocity.y;
  1629. if ( in_bounds( vi_x, vi_y ) && ( is_empty( vi_x, vi_y ) ||
  1630. get_particle_at( vi_x, vi_y ).id == mat_id_water ||
  1631. get_particle_at( vi_x, vi_y ).id == mat_id_fire ||
  1632. get_particle_at( vi_x, vi_y ).id == mat_id_smoke ||
  1633. get_particle_at( vi_x, vi_y ).id == mat_id_ember ) ) {
  1634. particle_t tmp_b = g_world_particle_data[ compute_idx( vi_x, vi_y ) ];
  1635. // Try to throw water out
  1636. if ( tmp_b.id == mat_id_water ) {
  1637. tmp_b.has_been_updated_this_frame = true;
  1638. s32 rx = random_val( -2, 2 );
  1639. tmp_b.velocity = (gs_vec2){ rx, -3.f };
  1640. write_data( compute_idx( vi_x, vi_y ), *p );
  1641. write_data( read_idx, tmp_b );
  1642. }
  1643. else if ( is_empty( vi_x, vi_y ) ) {
  1644. write_data( compute_idx( vi_x, vi_y ), *p );
  1645. write_data( read_idx, tmp_b );
  1646. }
  1647. }
  1648. // Simple falling, changing the velocity here ruins everything. I need to redo this entire simulation.
  1649. else if ( in_bounds( x, y - 1 ) && (( is_empty( x, y - 1 ) || ( get_particle_at( x, y - 1 ).id == mat_id_water ) || get_particle_at(x,y-1).id == mat_id_fire ) ) ) {
  1650. p->velocity.y -= (gravity * dt );
  1651. particle_t tmp_b = get_particle_at( x, y - 1 );
  1652. write_data( compute_idx( x, y - 1 ), *p );
  1653. write_data( read_idx, tmp_b );
  1654. }
  1655. else if ( in_bounds( x - 1, y - 1 ) && (( is_empty( x - 1, y - 1 ) || get_particle_at( x - 1, y - 1 ).id == mat_id_water ) || get_particle_at(x-1, y-1).id == mat_id_fire) ) {
  1656. p->velocity.x = random_val( 0, 1 ) == 0 ? -1.2f : 1.2f;
  1657. p->velocity.y -= (gravity * dt );
  1658. particle_t tmp_b = get_particle_at( x - 1, y - 1 );
  1659. write_data( compute_idx( x - 1, y - 1 ), *p );
  1660. write_data( read_idx, tmp_b );
  1661. }
  1662. else if ( in_bounds( x + 1, y - 1 ) && (( is_empty( x + 1, y - 1 ) || get_particle_at( x + 1, y - 1 ).id == mat_id_water ) || get_particle_at(x+1, y+1).id == mat_id_fire) ) {
  1663. p->velocity.x = random_val( 0, 1 ) == 0 ? -1.2f : 1.2f;
  1664. p->velocity.y -= (gravity * dt );
  1665. particle_t tmp_b = get_particle_at( x + 1, y - 1 );
  1666. write_data( compute_idx( x + 1, y - 1 ), *p );
  1667. write_data( read_idx, tmp_b );
  1668. }
  1669. // Can move if in liquid
  1670. else if ( in_bounds( x + 1, y ) && ( is_empty( x + 1, y ) || get_particle_at( x + 1, y ).id == mat_id_fire ) ) {
  1671. u32 idx = compute_idx( x + 1, y );
  1672. particle_t tmp_b = g_world_particle_data[ idx ];
  1673. write_data( idx, *p );
  1674. write_data( read_idx, tmp_b );
  1675. }
  1676. else if ( in_bounds( x - 1, y ) && ( is_empty( x - 1, y ) || get_particle_at( x - 1, y ).id == mat_id_fire ) ) {
  1677. u32 idx = compute_idx( x - 1, y );
  1678. particle_t tmp_b = g_world_particle_data[ idx ];
  1679. write_data( idx, *p );
  1680. write_data( read_idx, tmp_b );
  1681. }
  1682. else {
  1683. write_data( read_idx, *p );
  1684. }
  1685. }
  1686. void update_fire( u32 x, u32 y )
  1687. {
  1688. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  1689. // For water, same as sand, but we'll check immediate left and right as well
  1690. u32 read_idx = compute_idx( x, y );
  1691. particle_t* p = &g_world_particle_data[ read_idx ];
  1692. u32 write_idx = read_idx;
  1693. u32 fall_rate = 4;
  1694. if ( p->has_been_updated_this_frame ) {
  1695. return;
  1696. }
  1697. p->has_been_updated_this_frame = true;
  1698. if ( p->life_time > 0.2f ) {
  1699. if ( random_val( 0, 100 ) == 0 ) {
  1700. write_data( read_idx, particle_empty() );
  1701. return;
  1702. }
  1703. }
  1704. f32 st = sin(gs_engine_instance()->ctx.platform->elapsed_time());
  1705. // f32 grav_mul = random_val( 0, 10 ) == 0 ? 2.f : 1.f;
  1706. p->velocity.y = gs_clamp( p->velocity.y - ((gravity * dt)) * 0.2f , -5.0f, 0.f );
  1707. // p->velocity.x = gs_clamp( st, -1.f, 1.f );
  1708. p->velocity.x = gs_clamp( p->velocity.x + (f32)random_val( -100, 100 ) / 200.f, -0.5f, 0.5f );
  1709. // Change color based on life_time
  1710. if ( random_val( 0, (s32)(p->life_time * 100.f) ) % 200 == 0 ) {
  1711. s32 ran = random_val( 0, 3 );
  1712. switch ( ran ) {
  1713. case 0: p->color = (color_t){ 255, 80, 20, 255 }; break;
  1714. case 1: p->color = (color_t){ 250, 150, 10, 255 }; break;
  1715. case 2: p->color = (color_t){ 200, 150, 0, 255 }; break;
  1716. case 3: p->color = (color_t){ 100, 50, 2, 255 }; break;
  1717. }
  1718. }
  1719. if ( p->life_time < 0.02f ) {
  1720. p->color.r = 200;
  1721. } else {
  1722. p->color.r = 255;
  1723. }
  1724. // In water, so create steam and DIE
  1725. // Should also kill the water...
  1726. s32 lx, ly;
  1727. if ( is_in_water( x, y, &lx, &ly ) ) {
  1728. if ( random_val( 0, 1 ) == 0 ) {
  1729. s32 ry = random_val( -5, -1 );
  1730. s32 rx = random_val( -5, 5 );
  1731. for ( s32 i = ry; i > -5; --i ) {
  1732. for ( s32 j = rx; j < 5; ++j ) {
  1733. particle_t p = particle_steam();
  1734. if ( in_bounds( x + j, y + i ) && is_empty( x + j, y + i ) ) {
  1735. particle_t p = particle_steam();
  1736. write_data( compute_idx( x + j, y + i ), p );
  1737. }
  1738. }
  1739. }
  1740. particle_t p = particle_steam();
  1741. write_data( read_idx, particle_empty() );
  1742. write_data( read_idx, p );
  1743. write_data( compute_idx( lx, ly ), particle_empty() );
  1744. return;
  1745. }
  1746. }
  1747. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  1748. if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) && ( get_particle_at( x, y + 1 ).id != mat_id_water || get_particle_at( x, y + 1 ).id != mat_id_smoke ) ) {
  1749. p->velocity.y /= 2.f;
  1750. }
  1751. if ( random_val( 0, 10 ) == 0 ) {
  1752. // p->velocity.x = gs_clamp( p->velocity.x + (f32)random_val( -1, 1 ) / 2.f, -1.f, 1.f );
  1753. }
  1754. // p->velocity.x = gs_clamp( p->velocity.x, -0.5f, 0.5f );
  1755. // Kill fire underneath
  1756. if ( in_bounds( x, y + 3 ) && get_particle_at(x, y + 3).id == mat_id_fire && random_val(0, 100) == 0 ) {
  1757. write_data( compute_idx(x, y +3 ), *p);
  1758. write_data( read_idx, particle_empty() );
  1759. return;
  1760. }
  1761. // Chance to kick itself up ( to simulate flames )
  1762. if ( in_bounds( x, y + 1 ) && get_particle_at( x, y + 1 ).id == mat_id_fire &&
  1763. in_bounds( x, y - 1 ) && get_particle_at( x, y - 1 ).id == mat_id_empty ) {
  1764. if ( random_val( 0, 10 ) == 0 * p->life_time < 10.f && p->life_time > 1.f ) {
  1765. s32 r = random_val(0, 1);
  1766. s32 rh = random_val(-10, -1);
  1767. s32 spread = 3;
  1768. for ( s32 i = rh; i < 0; ++i ) {
  1769. for ( s32 j = r ? -spread : spread; r ? j < spread : j > -spread; r ? ++j : --j ) {
  1770. s32 rx = j, ry = i;
  1771. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  1772. write_data( compute_idx( x + rx, y + ry ), *p );
  1773. write_data( read_idx, particle_empty() );
  1774. break;
  1775. }
  1776. }
  1777. }
  1778. }
  1779. return;
  1780. }
  1781. s32 vi_x = x + (s32)p->velocity.x;
  1782. s32 vi_y = y + (s32)p->velocity.y;
  1783. // Check to see if you can swap first with other element below you
  1784. u32 b_idx = compute_idx( x, y + 1 );
  1785. u32 br_idx = compute_idx( x + 1, y + 1 );
  1786. u32 bl_idx = compute_idx( x - 1, y + 1 );
  1787. const s32 wood_chance = 100;
  1788. const s32 gun_powder_chance = 1;
  1789. const s32 oil_chance = 5;
  1790. // Chance to spawn smoke above
  1791. for ( u32 i = 0; i < random_val( 1, 10 ); ++i ) {
  1792. if ( random_val( 0, 500 ) == 0 ) {
  1793. if ( in_bounds( x, y - 1 ) && is_empty( x, y - 1 ) ) {
  1794. write_data( compute_idx( x, y - 1 ), particle_smoke() );
  1795. }
  1796. else if ( in_bounds( x + 1, y - 1 ) && is_empty( x + 1, y - 1 ) ) {
  1797. write_data( compute_idx( x + 1, y - 1 ), particle_smoke() );
  1798. }
  1799. else if ( in_bounds( x - 1, y - 1 ) && is_empty( x - 1, y - 1 ) ) {
  1800. write_data( compute_idx( x - 1, y - 1 ), particle_smoke() );
  1801. }
  1802. }
  1803. }
  1804. // Spawn embers
  1805. if ( random_val( 0, 250 ) == 0 && p->life_time < 3.f ) {
  1806. for ( u32 i = 0; i < random_val(1, 100); ++i ) {
  1807. if ( in_bounds( x, y - 1 ) && is_empty( x, y - 1 ) ) {
  1808. particle_t e = particle_ember();
  1809. e.velocity = (gs_vec2){ (f32)random_val(-5, 5) / 5.f, -(f32)random_val(2, 10) / 10.f };
  1810. write_data( compute_idx( x, y - 1 ), e );
  1811. }
  1812. else if ( in_bounds( x + 1, y - 1 ) && is_empty( x + 1, y - 1 ) ) {
  1813. particle_t e = particle_ember();
  1814. e.velocity = (gs_vec2){ (f32)random_val(-5, 5) / 5.f, -(f32)random_val(2, 10) / 10.f };
  1815. write_data( compute_idx( x + 1, y - 1 ), e );
  1816. }
  1817. else if ( in_bounds( x - 1, y - 1 ) && is_empty( x - 1, y - 1 ) ) {
  1818. particle_t e = particle_ember();
  1819. e.velocity = (gs_vec2){ (f32)random_val(-5, 5) / 5.f, -(f32)random_val(2, 10) / 10.f };
  1820. write_data( compute_idx( x - 1, y - 1 ), e );
  1821. }
  1822. }
  1823. }
  1824. // If directly on top of some wall, then replace it
  1825. if ( in_bounds( x, y + 1 ) && ((get_particle_at( x, y + 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  1826. (get_particle_at( x, y + 1 ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  1827. (get_particle_at( x, y + 1 ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  1828. )) {
  1829. write_data( compute_idx( x, y + 1 ), particle_fire() );
  1830. if ( random_val( 0, 5 ) == 0 ) {
  1831. s32 r = random_val(0, 1);
  1832. for ( s32 i = -3; i < 2; ++i ) {
  1833. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  1834. s32 rx = j, ry = i;
  1835. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  1836. // particle_t fp = particle_fire();
  1837. p->life_time += 0.1f;
  1838. write_data( compute_idx( x + rx, y + ry ), *p );
  1839. write_data( read_idx, particle_empty() );
  1840. break;
  1841. }
  1842. }
  1843. }
  1844. }
  1845. }
  1846. else if ( in_bounds( x + 1, y + 1 ) && ((get_particle_at( x + 1, y + 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  1847. (get_particle_at( x + 1, y + 1 ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  1848. (get_particle_at( x + 1, y + 1 ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  1849. )) {
  1850. write_data( compute_idx( x + 1, y + 1 ), particle_fire() );
  1851. if ( random_val( 0, 5 ) == 0 ) {
  1852. s32 r = random_val(0, 1);
  1853. for ( s32 i = -3; i < 2; ++i ) {
  1854. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  1855. s32 rx = j, ry = i;
  1856. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  1857. // particle_t fp = particle_fire();
  1858. p->life_time += 0.1f;
  1859. write_data( compute_idx( x + rx, y + ry ), *p );
  1860. write_data( read_idx, particle_empty() );
  1861. break;
  1862. }
  1863. }
  1864. }
  1865. }
  1866. }
  1867. else if ( in_bounds( x - 1, y + 1 ) && ((get_particle_at( x - 1, y + 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  1868. (get_particle_at( x - 1, y + 1 ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  1869. (get_particle_at( x - 1, y + 1 ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  1870. )) {
  1871. write_data( compute_idx( x - 1, y + 1 ), particle_fire() );
  1872. if ( random_val( 0, 5 ) == 0 ) {
  1873. s32 r = random_val(0, 1);
  1874. for ( s32 i = -3; i < 2; ++i ) {
  1875. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  1876. s32 rx = j, ry = i;
  1877. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  1878. // particle_t fp = particle_fire();
  1879. p->life_time += 0.1f;
  1880. write_data( compute_idx( x + rx, y + ry ), *p );
  1881. write_data( read_idx, particle_empty() );
  1882. break;
  1883. }
  1884. }
  1885. }
  1886. }
  1887. }
  1888. else if ( in_bounds( x - 1, y ) && ((get_particle_at( x - 1, y ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  1889. (get_particle_at( x - 1, y ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  1890. (get_particle_at( x - 1, y ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  1891. )) {
  1892. write_data( compute_idx( x - 1, y ), particle_fire() );
  1893. if ( random_val( 0, 5 ) == 0 ) {
  1894. s32 r = random_val(0, 1);
  1895. for ( s32 i = -3; i < 2; ++i ) {
  1896. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  1897. s32 rx = j, ry = i;
  1898. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  1899. // particle_t fp = particle_fire();
  1900. p->life_time += 0.1f;
  1901. write_data( compute_idx( x + rx, y + ry ), *p );
  1902. write_data( read_idx, particle_empty() );
  1903. break;
  1904. }
  1905. }
  1906. }
  1907. }
  1908. }
  1909. else if ( in_bounds( x + 1, y ) && ((get_particle_at( x + 1, y ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  1910. (get_particle_at( x + 1, y ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  1911. (get_particle_at( x + 1, y ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  1912. )) {
  1913. write_data( compute_idx( x + 1, y ), particle_fire() );
  1914. if ( random_val( 0, 5 ) == 0 ) {
  1915. s32 r = random_val(0, 1);
  1916. for ( s32 i = -3; i < 2; ++i ) {
  1917. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  1918. s32 rx = j, ry = i;
  1919. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  1920. // particle_t fp = particle_fire();
  1921. p->life_time += 0.1f;
  1922. write_data( compute_idx( x + rx, y + ry ), *p );
  1923. write_data( read_idx, particle_empty() );
  1924. break;
  1925. }
  1926. }
  1927. }
  1928. }
  1929. }
  1930. else if ( in_bounds( x + 1, y - 1 ) && ((get_particle_at( x + 1, y - 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  1931. (get_particle_at( x + 1, y - 1 ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  1932. (get_particle_at( x + 1, y - 1 ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  1933. )) {
  1934. write_data( compute_idx( x + 1, y - 1 ), particle_fire() );
  1935. if ( random_val( 0, 5 ) == 0 ) {
  1936. s32 r = random_val(0, 1);
  1937. for ( s32 i = -3; i < 2; ++i ) {
  1938. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  1939. s32 rx = j, ry = i;
  1940. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  1941. // particle_t fp = particle_fire();
  1942. p->life_time += 0.1f;
  1943. write_data( compute_idx( x + rx, y + ry ), *p );
  1944. write_data( read_idx, particle_empty() );
  1945. break;
  1946. }
  1947. }
  1948. }
  1949. }
  1950. }
  1951. else if ( in_bounds( x - 1, y - 1 ) && ((get_particle_at( x - 1, y - 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  1952. (get_particle_at( x - 1, y - 1 ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  1953. (get_particle_at( x - 1, y - 1 ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  1954. )) {
  1955. write_data( compute_idx( x - 1, y - 1 ), particle_fire() );
  1956. if ( random_val( 0, 5 ) == 0 ) {
  1957. s32 r = random_val(0, 1);
  1958. for ( s32 i = -3; i < 2; ++i ) {
  1959. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  1960. s32 rx = j, ry = i;
  1961. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  1962. // particle_t fp = particle_fire();
  1963. p->life_time += 0.1f;
  1964. write_data( compute_idx( x + rx, y + ry ), *p );
  1965. write_data( read_idx, particle_empty() );
  1966. break;
  1967. }
  1968. }
  1969. }
  1970. }
  1971. }
  1972. else if ( in_bounds( x, y - 1 ) && is_empty( x, y - 1 ) ) {
  1973. if ( random_val( 0, 50 ) == 0 ) {
  1974. write_data( read_idx, particle_empty() );
  1975. return;
  1976. }
  1977. }
  1978. if ( in_bounds( vi_x, vi_y ) && (is_empty(vi_x, vi_y) ||
  1979. get_particle_at(vi_x, vi_y).id == mat_id_fire ||
  1980. get_particle_at(vi_x, vi_y).id == mat_id_smoke))
  1981. {
  1982. // p->velocity.y -= (gravity * dt );
  1983. particle_t tmp_b = g_world_particle_data[ compute_idx(vi_x, vi_y) ];
  1984. write_data( compute_idx(vi_x, vi_y), *p );
  1985. write_data( read_idx, tmp_b );
  1986. }
  1987. // Simple falling, changing the velocity here ruins everything. I need to redo this entire simulation.
  1988. else if ( in_bounds( x, y + 1 ) && (( is_empty( x, y + 1 ) || ( g_world_particle_data[ b_idx ].id == mat_id_water ) ) ) ) {
  1989. // p->velocity.y -= (gravity * dt );
  1990. // p->velocity.x = random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  1991. particle_t tmp_b = g_world_particle_data[ b_idx ];
  1992. write_data( b_idx, *p );
  1993. write_data( read_idx, tmp_b );
  1994. }
  1995. else if ( in_bounds( x - 1, y + 1 ) && (( is_empty( x - 1, y + 1 ) || g_world_particle_data[ bl_idx ].id == mat_id_water )) ) {
  1996. // p->velocity.x = random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  1997. // p->velocity.y -= (gravity * dt );
  1998. particle_t tmp_b = g_world_particle_data[ bl_idx ];
  1999. write_data( bl_idx, *p );
  2000. write_data( read_idx, tmp_b );
  2001. }
  2002. else if ( in_bounds( x + 1, y + 1 ) && (( is_empty( x + 1, y + 1 ) || g_world_particle_data[ br_idx ].id == mat_id_water )) ) {
  2003. // p->velocity.x = random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  2004. // p->velocity.y -= (gravity * dt );
  2005. particle_t tmp_b = g_world_particle_data[ br_idx ];
  2006. write_data( br_idx, *p );
  2007. write_data( read_idx, tmp_b );
  2008. }
  2009. else if ( in_bounds( x - 1, y - 1 ) && ( g_world_particle_data[ compute_idx( x - 1, y - 1 ) ].id == mat_id_water ) ) {
  2010. u32 idx = compute_idx( x - 1, y - 1 );
  2011. // p->velocity.x = random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  2012. particle_t tmp_b = g_world_particle_data[ idx ];
  2013. write_data( idx, *p );
  2014. write_data( read_idx, tmp_b );
  2015. }
  2016. else if ( in_bounds( x + 1, y - 1 ) && ( g_world_particle_data[ compute_idx( x + 1, y - 1 ) ].id == mat_id_water ) ) {
  2017. u32 idx = compute_idx( x + 1, y - 1 );
  2018. // p->velocity.x = random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  2019. particle_t tmp_b = g_world_particle_data[ idx ];
  2020. write_data( idx, *p );
  2021. write_data( read_idx, tmp_b );
  2022. }
  2023. else if ( in_bounds( x, y - 1 ) && ( g_world_particle_data[ compute_idx( x, y - 1 ) ].id == mat_id_water ) ) {
  2024. u32 idx = compute_idx( x, y - 1 );
  2025. // p->velocity.x = random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  2026. particle_t tmp_b = g_world_particle_data[ idx ];
  2027. write_data( idx, *p );
  2028. write_data( read_idx, tmp_b );
  2029. }
  2030. else {
  2031. // p->velocity.x = random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  2032. write_data( read_idx, *p );
  2033. }
  2034. }
  2035. void update_lava( u32 x, u32 y )
  2036. {
  2037. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  2038. // For water, same as sand, but we'll check immediate left and right as well
  2039. u32 read_idx = compute_idx( x, y );
  2040. particle_t* p = &g_world_particle_data[ read_idx ];
  2041. u32 write_idx = read_idx;
  2042. u32 fall_rate = 4;
  2043. if ( p->has_been_updated_this_frame ) {
  2044. return;
  2045. }
  2046. p->has_been_updated_this_frame = true;
  2047. p->velocity.y = gs_clamp( p->velocity.y + ((gravity * dt)), -10.f, 10.f );
  2048. // Change color based on life_time
  2049. if ( random_val( 0, (s32)(p->life_time * 100.f) ) % 200 == 0 ) {
  2050. s32 ran = random_val( 0, 3 );
  2051. switch ( ran ) {
  2052. case 0: p->color = (color_t){ 255, 80, 20, 255 }; break;
  2053. case 1: p->color = (color_t){ 255, 100, 10, 255 }; break;
  2054. case 2: p->color = (color_t){ 255, 50, 0, 255 }; break;
  2055. case 3: p->color = (color_t){ 200, 50, 2, 255 }; break;
  2056. }
  2057. }
  2058. // In water, so create steam and DIE
  2059. // Should also kill the water...
  2060. s32 lx, ly;
  2061. if ( is_in_water( x, y, &lx, &ly ) ) {
  2062. if ( random_val( 0, 1 ) == 0 ) {
  2063. s32 ry = random_val( -5, -1 );
  2064. s32 rx = random_val( -5, 5 );
  2065. for ( s32 i = ry; i > -5; --i ) {
  2066. for ( s32 j = rx; j < 5; ++j ) {
  2067. particle_t p = particle_steam();
  2068. if ( in_bounds( x + j, y + i ) && is_empty( x + j, y + i ) ) {
  2069. particle_t p = particle_steam();
  2070. write_data( compute_idx( x + j, y + i ), p );
  2071. }
  2072. }
  2073. }
  2074. particle_t p = particle_steam();
  2075. write_data( read_idx, particle_empty() );
  2076. write_data( read_idx, p );
  2077. write_data( compute_idx( lx, ly ), particle_stone() );
  2078. return;
  2079. }
  2080. }
  2081. // Otherwise destroy anything that isn't fire or lava...eventually...
  2082. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  2083. if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) && ( get_particle_at( x, y + 1 ).id != mat_id_water || get_particle_at( x, y + 1 ).id != mat_id_smoke ) ) {
  2084. p->velocity.y /= 2.f;
  2085. }
  2086. s32 vi_x = x + (s32)p->velocity.x;
  2087. s32 vi_y = y + (s32)p->velocity.y;
  2088. const s32 spread_rate = 1;
  2089. s32 ran = random_val( 0, 1 );
  2090. s32 r = spread_rate;
  2091. s32 l = -r;
  2092. s32 u = fall_rate;
  2093. s32 v_idx = compute_idx ( x + (s32)p->velocity.x, y + (s32)p->velocity.y );
  2094. s32 b_idx = compute_idx( x, y + u );
  2095. s32 bl_idx = compute_idx( x + l, y + u );
  2096. s32 br_idx = compute_idx( x + r, y + u );
  2097. s32 l_idx = compute_idx( x + l, y );
  2098. s32 r_idx = compute_idx( x + r, y );
  2099. s32 vx = (s32)p->velocity.x, vy = (s32)p->velocity.y;
  2100. const s32 wood_chance = 200;
  2101. const s32 gun_powder_chance = 0;
  2102. const s32 oil_chance = 5;
  2103. // Chance to spawn smoke above
  2104. for ( u32 i = 0; i < random_val( 1, 10 ); ++i ) {
  2105. if ( random_val( 0, 500 ) == 0 ) {
  2106. if ( in_bounds( x, y - 1 ) && is_empty( x, y - 1 ) ) {
  2107. write_data( compute_idx( x, y - 1 ), particle_smoke() );
  2108. }
  2109. else if ( in_bounds( x + 1, y - 1 ) && is_empty( x + 1, y - 1 ) ) {
  2110. write_data( compute_idx( x + 1, y - 1 ), particle_smoke() );
  2111. }
  2112. else if ( in_bounds( x - 1, y - 1 ) && is_empty( x - 1, y - 1 ) ) {
  2113. write_data( compute_idx( x - 1, y - 1 ), particle_smoke() );
  2114. }
  2115. }
  2116. }
  2117. // Spawn embers
  2118. if ( random_val( 0, 250 ) == 0 && p->life_time < 3.f ) {
  2119. for ( u32 i = 0; i < random_val(1, 100); ++i ) {
  2120. if ( in_bounds( x, y - 1 ) && is_empty( x, y - 1 ) ) {
  2121. particle_t e = particle_ember();
  2122. e.velocity = (gs_vec2){ (f32)random_val(-5, 5) / 5.f, -(f32)random_val(2, 10) / 2.f };
  2123. write_data( compute_idx( x, y - 1 ), e );
  2124. }
  2125. else if ( in_bounds( x + 1, y - 1 ) && is_empty( x + 1, y - 1 ) ) {
  2126. particle_t e = particle_ember();
  2127. e.velocity = (gs_vec2){ (f32)random_val(-5, 5) / 5.f, -(f32)random_val(2, 10) / 2.f };
  2128. write_data( compute_idx( x + 1, y - 1 ), e );
  2129. }
  2130. else if ( in_bounds( x - 1, y - 1 ) && is_empty( x - 1, y - 1 ) ) {
  2131. particle_t e = particle_ember();
  2132. e.velocity = (gs_vec2){ (f32)random_val(-5, 5) / 5.f, -(f32)random_val(2, 10) / 2.f };
  2133. write_data( compute_idx( x - 1, y - 1 ), e );
  2134. }
  2135. }
  2136. }
  2137. // If directly on top of some wall, then replace it
  2138. if ( in_bounds( x, y + 1 ) && ((get_particle_at( x, y + 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2139. (get_particle_at( x, y + 1 ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  2140. (get_particle_at( x, y + 1 ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  2141. )) {
  2142. write_data( compute_idx( x, y + 1 ), particle_fire() );
  2143. if ( random_val( 0, 5 ) == 0 ) {
  2144. s32 r = random_val(0, 1);
  2145. for ( s32 i = -3; i < 2; ++i ) {
  2146. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  2147. s32 rx = j, ry = i;
  2148. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  2149. particle_t fp = particle_fire();
  2150. p->life_time += 0.1f;
  2151. write_data( compute_idx( x + rx, y + ry ), fp );
  2152. // write_data( read_idx, particle_empty() );
  2153. break;
  2154. }
  2155. }
  2156. }
  2157. }
  2158. }
  2159. else if ( in_bounds( x + 1, y + 1 ) && ((get_particle_at( x + 1, y + 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2160. (get_particle_at( x + 1, y + 1 ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  2161. (get_particle_at( x + 1, y + 1 ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  2162. )) {
  2163. write_data( compute_idx( x + 1, y + 1 ), particle_fire() );
  2164. if ( random_val( 0, 5 ) == 0 ) {
  2165. s32 r = random_val(0, 1);
  2166. for ( s32 i = -3; i < 2; ++i ) {
  2167. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  2168. s32 rx = j, ry = i;
  2169. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  2170. particle_t fp = particle_fire();
  2171. p->life_time += 0.1f;
  2172. write_data( compute_idx( x + rx, y + ry ), fp );
  2173. break;
  2174. }
  2175. }
  2176. }
  2177. }
  2178. }
  2179. else if ( in_bounds( x - 1, y + 1 ) && ((get_particle_at( x - 1, y + 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2180. (get_particle_at( x - 1, y + 1 ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  2181. (get_particle_at( x - 1, y + 1 ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  2182. )) {
  2183. write_data( compute_idx( x - 1, y + 1 ), particle_fire() );
  2184. if ( random_val( 0, 5 ) == 0 ) {
  2185. s32 r = random_val(0, 1);
  2186. for ( s32 i = -3; i < 2; ++i ) {
  2187. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  2188. s32 rx = j, ry = i;
  2189. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  2190. particle_t fp = particle_fire();
  2191. p->life_time += 0.1f;
  2192. write_data( compute_idx( x + rx, y + ry ), fp );
  2193. break;
  2194. }
  2195. }
  2196. }
  2197. }
  2198. }
  2199. else if ( in_bounds( x - 1, y ) && ((get_particle_at( x - 1, y ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2200. (get_particle_at( x - 1, y ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  2201. (get_particle_at( x - 1, y ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  2202. )) {
  2203. write_data( compute_idx( x - 1, y ), particle_fire() );
  2204. if ( random_val( 0, 5 ) == 0 ) {
  2205. s32 r = random_val(0, 1);
  2206. for ( s32 i = -3; i < 2; ++i ) {
  2207. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  2208. s32 rx = j, ry = i;
  2209. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  2210. particle_t fp = particle_fire();
  2211. p->life_time += 0.1f;
  2212. write_data( compute_idx( x + rx, y + ry ), fp );
  2213. break;
  2214. }
  2215. }
  2216. }
  2217. }
  2218. }
  2219. else if ( in_bounds( x + 1, y ) && ((get_particle_at( x + 1, y ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2220. (get_particle_at( x + 1, y ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  2221. (get_particle_at( x + 1, y ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  2222. )) {
  2223. write_data( compute_idx( x + 1, y ), particle_fire() );
  2224. if ( random_val( 0, 5 ) == 0 ) {
  2225. s32 r = random_val(0, 1);
  2226. for ( s32 i = -3; i < 2; ++i ) {
  2227. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  2228. s32 rx = j, ry = i;
  2229. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  2230. particle_t fp = particle_fire();
  2231. p->life_time += 0.1f;
  2232. write_data( compute_idx( x + rx, y + ry ), fp );
  2233. break;
  2234. }
  2235. }
  2236. }
  2237. }
  2238. }
  2239. else if ( in_bounds( x + 1, y - 1 ) && ((get_particle_at( x + 1, y - 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2240. (get_particle_at( x + 1, y - 1 ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  2241. (get_particle_at( x + 1, y - 1 ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  2242. )) {
  2243. write_data( compute_idx( x + 1, y - 1 ), particle_fire() );
  2244. if ( random_val( 0, 5 ) == 0 ) {
  2245. s32 r = random_val(0, 1);
  2246. for ( s32 i = -3; i < 2; ++i ) {
  2247. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  2248. s32 rx = j, ry = i;
  2249. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  2250. particle_t fp = particle_fire();
  2251. p->life_time += 0.1f;
  2252. write_data( compute_idx( x + rx, y + ry ), fp );
  2253. break;
  2254. }
  2255. }
  2256. }
  2257. }
  2258. }
  2259. else if ( in_bounds( x - 1, y - 1 ) && ((get_particle_at( x - 1, y - 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2260. (get_particle_at( x - 1, y - 1 ).id == mat_id_gunpowder && random_val(0, gun_powder_chance) == 0) ||
  2261. (get_particle_at( x - 1, y - 1 ).id == mat_id_oil && random_val(0, oil_chance) == 0)
  2262. )) {
  2263. write_data( compute_idx( x - 1, y - 1 ), particle_fire() );
  2264. if ( random_val( 0, 5 ) == 0 ) {
  2265. s32 r = random_val(0, 1);
  2266. for ( s32 i = -3; i < 2; ++i ) {
  2267. for ( s32 j = r ? -3 : 2; r ? j < 2 : j > -3; r ? ++j : --j ) {
  2268. s32 rx = j, ry = i;
  2269. if ( in_bounds( x + rx, y + ry ) && is_empty( x + rx, y + ry ) ) {
  2270. particle_t fp = particle_fire();
  2271. p->life_time += 0.1f;
  2272. write_data( compute_idx( x + rx, y + ry ), fp );
  2273. break;
  2274. }
  2275. }
  2276. }
  2277. }
  2278. }
  2279. // If in water, then need to float upwards
  2280. // s32 lx, ly;
  2281. // if ( is_in_liquid( x, y, &lx, &ly ) && in_bounds( x, y - 1 ) && get_particle_at( x, y - 1 ).id == mat_id_water ) {
  2282. // particle_t tmp = get_particle_at( x, y - 1 );
  2283. // write_data( compute_idx( x, y - 1 ), *p );
  2284. // write_data( read_idx, tmp );
  2285. // // return;
  2286. // }
  2287. if ( in_bounds( x + vx, y + vy ) && (is_empty( x + vx, y + vy ) ) ) {
  2288. write_data( v_idx, *p );
  2289. write_data( read_idx, particle_empty() );
  2290. }
  2291. else if ( is_empty( x, y + u ) ) {
  2292. write_data( b_idx, *p );
  2293. write_data( read_idx, particle_empty() );
  2294. }
  2295. else if ( is_empty( x + r, y + u ) ) {
  2296. write_data( br_idx, *p );
  2297. write_data( read_idx, particle_empty() );
  2298. }
  2299. else if ( is_empty( x + l, y + u ) ) {
  2300. write_data( bl_idx, *p );
  2301. write_data( read_idx, particle_empty() );
  2302. }
  2303. else {
  2304. particle_t tmp = *p;
  2305. b32 found = false;
  2306. for ( u32 i = 0; i < fall_rate; ++i ) {
  2307. for ( s32 j = spread_rate; j > 0; --j )
  2308. {
  2309. if ( is_empty( x - j, y + i ) ) {
  2310. write_data( compute_idx( x - j, y + i ), *p );
  2311. write_data( read_idx, particle_empty() );
  2312. found = true;
  2313. break;
  2314. }
  2315. else if ( is_empty( x + j, y + i ) ) {
  2316. write_data( compute_idx( x + j, y + i ), *p );
  2317. write_data( read_idx, particle_empty() );
  2318. found = true;
  2319. break;
  2320. }
  2321. }
  2322. }
  2323. if ( !found ) {
  2324. write_data( read_idx, tmp );
  2325. }
  2326. }
  2327. }
  2328. void update_oil( u32 x, u32 y )
  2329. {
  2330. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  2331. u32 read_idx = compute_idx( x, y );
  2332. particle_t* p = &g_world_particle_data[ read_idx ];
  2333. u32 write_idx = read_idx;
  2334. u32 fall_rate = 2;
  2335. u32 spread_rate = 4;
  2336. p->velocity.y = gs_clamp( p->velocity.y + (gravity * dt), -10.f, 10.f );
  2337. p->has_been_updated_this_frame = true;
  2338. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  2339. // if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) && get_particle_at( x, y + 1 ).id != mat_id_water ) {
  2340. if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) ) {
  2341. p->velocity.y /= 2.f;
  2342. }
  2343. // Change color depending on pressure? Pressure would dictate how "deep" the water is, I suppose.
  2344. if ( random_val( 0, (s32)(p->life_time * 100.f) ) % 20 == 0 ) {
  2345. f32 r = (f32)(random_val( 0, 1 )) / 2.f;
  2346. p->color.r = (u8)(gs_interp_linear(0.2f, 0.25f, r) * 255.f);
  2347. p->color.g = (u8)(gs_interp_linear(0.2f, 0.25f, r) * 255.f);
  2348. p->color.b = (u8)(gs_interp_linear(0.2f, 0.25f, r) * 255.f);
  2349. }
  2350. s32 ran = random_val( 0, 1 );
  2351. s32 r = ran ? spread_rate : -spread_rate;
  2352. s32 l = -r;
  2353. s32 u = fall_rate;
  2354. s32 v_idx = compute_idx ( x + (s32)p->velocity.x, y + (s32)p->velocity.y );
  2355. s32 b_idx = compute_idx( x, y + u );
  2356. s32 bl_idx = compute_idx( x + l, y + u );
  2357. s32 br_idx = compute_idx( x + r, y + u );
  2358. s32 l_idx = compute_idx( x + l, y );
  2359. s32 r_idx = compute_idx( x + r, y );
  2360. s32 vx = (s32)p->velocity.x, vy = (s32)p->velocity.y;
  2361. // If in water, then need to float upwards
  2362. // s32 lx, ly;
  2363. // if ( is_in_liquid( x, y, &lx, &ly ) && in_bounds( x, y - 1 ) && get_particle_at( x, y - 1 ).id == mat_id_water ) {
  2364. // particle_t tmp = get_particle_at( x, y - 1 );
  2365. // write_data( compute_idx( x, y - 1 ), *p );
  2366. // write_data( read_idx, tmp );
  2367. // // return;
  2368. // }
  2369. if ( in_bounds( x + vx, y + vy ) && (is_empty( x + vx, y + vy ) ) ) {
  2370. write_data( v_idx, *p );
  2371. write_data( read_idx, particle_empty() );
  2372. }
  2373. else if ( is_empty( x, y + u ) ) {
  2374. write_data( b_idx, *p );
  2375. write_data( read_idx, particle_empty() );
  2376. }
  2377. else if ( is_empty( x + r, y + u ) ) {
  2378. write_data( br_idx, *p );
  2379. write_data( read_idx, particle_empty() );
  2380. }
  2381. else if ( is_empty( x + l, y + u ) ) {
  2382. write_data( bl_idx, *p );
  2383. write_data( read_idx, particle_empty() );
  2384. }
  2385. else {
  2386. particle_t tmp = *p;
  2387. b32 found = false;
  2388. for ( u32 i = 0; i < fall_rate; ++i ) {
  2389. for ( s32 j = spread_rate; j > 0; --j )
  2390. {
  2391. if ( is_empty( x - j, y + i ) ) {
  2392. write_data( compute_idx( x - j, y + i ), *p );
  2393. write_data( read_idx, particle_empty() );
  2394. found = true;
  2395. break;
  2396. }
  2397. else if ( is_empty( x + j, y + i ) ) {
  2398. write_data( compute_idx( x + j, y + i ), *p );
  2399. write_data( read_idx, particle_empty() );
  2400. found = true;
  2401. break;
  2402. }
  2403. }
  2404. }
  2405. if ( !found ) {
  2406. write_data( read_idx, tmp );
  2407. }
  2408. }
  2409. }
  2410. void update_acid( u32 x, u32 y )
  2411. {
  2412. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  2413. u32 read_idx = compute_idx( x, y );
  2414. particle_t* p = &g_world_particle_data[ read_idx ];
  2415. u32 write_idx = read_idx;
  2416. u32 fall_rate = 2;
  2417. u32 spread_rate = 5;
  2418. s32 lx, ly;
  2419. p->velocity.y = gs_clamp( p->velocity.y + (gravity * dt), -10.f, 10.f );
  2420. p->has_been_updated_this_frame = true;
  2421. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  2422. // if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) && get_particle_at( x, y + 1 ).id != mat_id_water ) {
  2423. if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) ) {
  2424. p->velocity.y /= 2.f;
  2425. }
  2426. // Change color depending on pressure? Pressure would dictate how "deep" the water is, I suppose.
  2427. if ( random_val( 0, (s32)(p->life_time * 100.f) ) % 20 == 0 ) {
  2428. f32 r = (f32)(random_val( 0, 1 )) / 2.f;
  2429. p->color.r = (u8)(gs_interp_linear(0.05f, 0.06f, r) * 255.f);
  2430. p->color.g = (u8)(gs_interp_linear(0.8f, 0.85f, r) * 255.f);
  2431. p->color.b = (u8)(gs_interp_linear(0.1f, 0.12f, r) * 255.f);
  2432. }
  2433. const s32 wood_chance = 100;
  2434. const s32 stone_chance = 300;
  2435. const s32 sand_chance = 50;
  2436. const s32 salt_chance = 20;
  2437. // Random chance to die if in water
  2438. if ( is_in_water( x, y, &lx, &ly ) && random_val( 0, 250 ) == 0 ) {
  2439. write_data( read_idx, particle_empty() );
  2440. return;
  2441. }
  2442. // If directly on top of some wall, then replace it
  2443. if ( in_bounds( x, y + 1 ) && ((get_particle_at( x, y + 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2444. (get_particle_at( x, y + 1 ).id == mat_id_stone && random_val(0, stone_chance) == 0)
  2445. || (get_particle_at( x, y + 1 ).id == mat_id_sand && random_val(0, sand_chance) == 0)
  2446. || (get_particle_at( x, y + 1 ).id == mat_id_salt && random_val(0, salt_chance) == 0)
  2447. ))
  2448. {
  2449. write_data( compute_idx( x, y + 1 ), *p );
  2450. write_data( read_idx, particle_empty() );
  2451. }
  2452. else if ( in_bounds( x + 1, y + 1 ) && ((get_particle_at( x + 1, y + 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2453. (get_particle_at( x + 1, y + 1 ).id == mat_id_stone && random_val(0, stone_chance) == 0)
  2454. || (get_particle_at( x + 1, y + 1 ).id == mat_id_sand && random_val(0, sand_chance) == 0)
  2455. || (get_particle_at( x + 1, y + 1 ).id == mat_id_salt && random_val(0, salt_chance) == 0)
  2456. ))
  2457. {
  2458. write_data( compute_idx( x + 1, y + 1 ), *p );
  2459. write_data( read_idx, particle_empty() );
  2460. }
  2461. else if ( in_bounds( x - 1, y + 1 ) && ((get_particle_at( x - 1, y + 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2462. (get_particle_at( x - 1, y + 1 ).id == mat_id_stone && random_val(0, stone_chance) == 0)
  2463. || (get_particle_at( x - 1, y + 1 ).id == mat_id_sand && random_val(0, sand_chance) == 0)
  2464. || (get_particle_at( x - 1, y + 1 ).id == mat_id_salt && random_val(0, salt_chance) == 0)
  2465. ))
  2466. {
  2467. write_data( compute_idx( x - 1, y + 1 ), *p );
  2468. write_data( read_idx, particle_empty() );
  2469. }
  2470. else if ( in_bounds( x - 1, y ) && ((get_particle_at( x - 1, y ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2471. (get_particle_at( x - 1, y ).id == mat_id_stone && random_val(0, stone_chance) == 0)
  2472. || (get_particle_at( x - 1, y ).id == mat_id_sand && random_val(0, sand_chance) == 0)
  2473. || (get_particle_at( x - 1, y ).id == mat_id_salt && random_val(0, salt_chance) == 0)
  2474. ))
  2475. {
  2476. write_data( compute_idx( x - 1, y ), *p );
  2477. write_data( read_idx, particle_empty() );
  2478. }
  2479. else if ( in_bounds( x + 1, y ) && ((get_particle_at( x + 1, y ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2480. (get_particle_at( x + 1, y ).id == mat_id_stone && random_val(0, stone_chance) == 0)
  2481. || (get_particle_at( x + 1, y ).id == mat_id_sand && random_val(0, sand_chance) == 0)
  2482. || (get_particle_at( x + 1, y ).id == mat_id_salt && random_val(0, sand_chance) == 0)
  2483. ))
  2484. {
  2485. write_data( compute_idx( x + 1, y ), *p );
  2486. write_data( read_idx, particle_empty() );
  2487. }
  2488. else if ( in_bounds( x + 1, y - 1 ) && ((get_particle_at( x + 1, y - 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2489. (get_particle_at( x + 1, y - 1 ).id == mat_id_stone && random_val(0, stone_chance) == 0)
  2490. || (get_particle_at( x + 1, y - 1 ).id == mat_id_sand && random_val(0, sand_chance) == 0)
  2491. || (get_particle_at( x + 1, y - 1 ).id == mat_id_salt && random_val(0, salt_chance) == 0)
  2492. ))
  2493. {
  2494. write_data( compute_idx( x + 1, y - 1 ), *p );
  2495. write_data( read_idx, particle_empty() );
  2496. }
  2497. else if ( in_bounds( x - 1, y - 1 ) && ((get_particle_at( x - 1, y - 1 ).id == mat_id_wood && random_val( 0, wood_chance ) == 0) ||
  2498. (get_particle_at( x - 1, y - 1 ).id == mat_id_stone && random_val(0, stone_chance) == 0)
  2499. || (get_particle_at( x - 1, y - 1 ).id == mat_id_sand && random_val(0, sand_chance) == 0)
  2500. || (get_particle_at( x - 1, y - 1 ).id == mat_id_salt && random_val(0, salt_chance) == 0)
  2501. ))
  2502. {
  2503. write_data( compute_idx( x - 1, y - 1 ), *p );
  2504. write_data( read_idx, particle_empty() );
  2505. }
  2506. s32 ran = random_val( 0, 1 );
  2507. s32 r = ran ? spread_rate : -spread_rate;
  2508. s32 l = -r;
  2509. s32 u = fall_rate;
  2510. s32 v_idx = compute_idx ( x + (s32)p->velocity.x, y + (s32)p->velocity.y );
  2511. s32 b_idx = compute_idx( x, y + u );
  2512. s32 bl_idx = compute_idx( x + l, y + u );
  2513. s32 br_idx = compute_idx( x + r, y + u );
  2514. s32 l_idx = compute_idx( x + l, y );
  2515. s32 r_idx = compute_idx( x + r, y );
  2516. s32 vx = (s32)p->velocity.x, vy = (s32)p->velocity.y;
  2517. // If touching wood or stone, destroy it
  2518. if ( in_bounds( x + vx, y + vy ) && (is_empty( x + vx, y + vy ) ) ) {
  2519. write_data( v_idx, *p );
  2520. write_data( read_idx, particle_empty() );
  2521. }
  2522. else if ( is_empty( x, y + u ) ) {
  2523. write_data( b_idx, *p );
  2524. write_data( read_idx, particle_empty() );
  2525. }
  2526. else if ( is_empty( x + r, y + u ) ) {
  2527. write_data( br_idx, *p );
  2528. write_data( read_idx, particle_empty() );
  2529. }
  2530. else if ( is_empty( x + l, y + u ) ) {
  2531. write_data( bl_idx, *p );
  2532. write_data( read_idx, particle_empty() );
  2533. }
  2534. // Simple falling, changing the velocity here ruins everything. I need to redo this entire simulation.
  2535. else if ( in_bounds( x, y + u ) && (( is_empty( x, y + u ) ) ) ) {
  2536. p->velocity.y += (gravity * dt );
  2537. particle_t tmp_b = get_particle_at( x, y + u );
  2538. write_data( b_idx, *p );
  2539. write_data( read_idx, tmp_b );
  2540. }
  2541. else if ( in_bounds( x + l, y + u ) && (( is_empty( x + l, y + u ) )) ) {
  2542. p->velocity.x = is_in_liquid( x, y, &lx, &ly ) ? 0.f : random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  2543. p->velocity.y += (gravity * dt );
  2544. particle_t tmp_b = get_particle_at( x + l, y + u );
  2545. write_data( bl_idx, *p );
  2546. write_data( read_idx, tmp_b );
  2547. }
  2548. else if ( in_bounds( x + r, y + u ) && (( is_empty( x + r, y + u ) )) ) {
  2549. p->velocity.x = is_in_liquid( x, y, &lx, &ly ) ? 0.f : random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  2550. p->velocity.y += (gravity * dt );
  2551. particle_t tmp_b = get_particle_at( x + r, y + u );
  2552. write_data( br_idx, *p );
  2553. write_data( read_idx, tmp_b );
  2554. }
  2555. else if ( is_in_liquid( x, y, &lx, &ly ) && random_val( 0, 10 ) == 0 ) {
  2556. particle_t tmp_b = get_particle_at( lx, ly );
  2557. write_data( compute_idx( lx, ly ), *p );
  2558. write_data( read_idx, tmp_b );
  2559. }
  2560. else {
  2561. particle_t tmp = *p;
  2562. b32 found = false;
  2563. // Don't try to spread if something is directly above you?
  2564. if ( completely_surrounded( x, y ) ) {
  2565. write_data( read_idx, tmp );
  2566. return;
  2567. }
  2568. else {
  2569. for ( u32 i = 0; i < fall_rate; ++i ) {
  2570. for ( s32 j = spread_rate; j > 0; --j )
  2571. {
  2572. if ( in_bounds( x - j, y + i ) && (is_empty( x - j, y + i ) || get_particle_at( x - j, y + i ).id == mat_id_oil ) ) {
  2573. particle_t tmp = get_particle_at( x - j, y + i );
  2574. write_data( compute_idx( x - j, y + i ), *p );
  2575. write_data( read_idx, tmp );
  2576. found = true;
  2577. break;
  2578. }
  2579. if ( in_bounds( x + j, y + i ) && (is_empty( x + j, y + i ) || get_particle_at( x + j, y + i ).id == mat_id_oil ) ) {
  2580. particle_t tmp = get_particle_at( x + j, y + i );
  2581. write_data( compute_idx( x + j, y + i ), *p );
  2582. write_data( read_idx, tmp );
  2583. found = true;
  2584. break;
  2585. }
  2586. }
  2587. }
  2588. if ( !found ) {
  2589. write_data( read_idx, tmp );
  2590. }
  2591. }
  2592. }
  2593. }
  2594. void update_water( u32 x, u32 y )
  2595. {
  2596. f32 dt = gs_engine_instance()->ctx.platform->time.delta;
  2597. u32 read_idx = compute_idx( x, y );
  2598. particle_t* p = &g_world_particle_data[ read_idx ];
  2599. u32 write_idx = read_idx;
  2600. u32 fall_rate = 2;
  2601. u32 spread_rate = 5;
  2602. p->velocity.y = gs_clamp( p->velocity.y + (gravity * dt), -10.f, 10.f );
  2603. p->has_been_updated_this_frame = true;
  2604. // Just check if you can move directly beneath you. If not, then reset your velocity. God, this is going to blow.
  2605. // if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) && get_particle_at( x, y + 1 ).id != mat_id_water ) {
  2606. if ( in_bounds( x, y + 1 ) && !is_empty( x, y + 1 ) ) {
  2607. p->velocity.y /= 2.f;
  2608. }
  2609. // Change color depending on pressure? Pressure would dictate how "deep" the water is, I suppose.
  2610. if ( random_val( 0, (s32)(p->life_time * 100.f) ) % 20 == 0 ) {
  2611. f32 r = (f32)(random_val( 0, 1 )) / 2.f;
  2612. p->color.r = (u8)(gs_interp_linear(0.1f, 0.15f, r) * 255.f);
  2613. p->color.g = (u8)(gs_interp_linear(0.3f, 0.35f, r) * 255.f);
  2614. p->color.b = (u8)(gs_interp_linear(0.7f, 0.8f, r) * 255.f);
  2615. }
  2616. s32 ran = random_val( 0, 1 );
  2617. s32 r = ran ? spread_rate : -spread_rate;
  2618. s32 l = -r;
  2619. s32 u = fall_rate;
  2620. s32 v_idx = compute_idx ( x + (s32)p->velocity.x, y + (s32)p->velocity.y );
  2621. s32 b_idx = compute_idx( x, y + u );
  2622. s32 bl_idx = compute_idx( x + l, y + u );
  2623. s32 br_idx = compute_idx( x + r, y + u );
  2624. s32 l_idx = compute_idx( x + l, y );
  2625. s32 r_idx = compute_idx( x + r, y );
  2626. s32 vx = (s32)p->velocity.x, vy = (s32)p->velocity.y;
  2627. s32 lx, ly;
  2628. if ( in_bounds( x + vx, y + vy ) && (is_empty( x + vx, y + vy ) ) ) {
  2629. write_data( v_idx, *p );
  2630. write_data( read_idx, particle_empty() );
  2631. }
  2632. else if ( is_empty( x, y + u ) ) {
  2633. write_data( b_idx, *p );
  2634. write_data( read_idx, particle_empty() );
  2635. }
  2636. else if ( is_empty( x + r, y + u ) ) {
  2637. write_data( br_idx, *p );
  2638. write_data( read_idx, particle_empty() );
  2639. }
  2640. else if ( is_empty( x + l, y + u ) ) {
  2641. write_data( bl_idx, *p );
  2642. write_data( read_idx, particle_empty() );
  2643. }
  2644. // Simple falling, changing the velocity here ruins everything. I need to redo this entire simulation.
  2645. else if ( in_bounds( x, y + u ) && (( is_empty( x, y + u ) || ( g_world_particle_data[ b_idx ].id == mat_id_oil ) ) ) ) {
  2646. p->velocity.y += (gravity * dt );
  2647. particle_t tmp_b = get_particle_at( x, y + u );
  2648. write_data( b_idx, *p );
  2649. write_data( read_idx, tmp_b );
  2650. }
  2651. else if ( in_bounds( x + l, y + u ) && (( is_empty( x + l, y + u ) || g_world_particle_data[ bl_idx ].id == mat_id_oil )) ) {
  2652. p->velocity.x = is_in_liquid( x, y, &lx, &ly ) ? 0.f : random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  2653. p->velocity.y += (gravity * dt );
  2654. particle_t tmp_b = get_particle_at( x + l, y + u );
  2655. write_data( bl_idx, *p );
  2656. write_data( read_idx, tmp_b );
  2657. }
  2658. else if ( in_bounds( x + r, y + u ) && (( is_empty( x + r, y + u ) || g_world_particle_data[ br_idx ].id == mat_id_oil )) ) {
  2659. p->velocity.x = is_in_liquid( x, y, &lx, &ly ) ? 0.f : random_val( 0, 1 ) == 0 ? -1.f : 1.f;
  2660. p->velocity.y += (gravity * dt );
  2661. particle_t tmp_b = get_particle_at( x + r, y + u );
  2662. write_data( br_idx, *p );
  2663. write_data( read_idx, tmp_b );
  2664. }
  2665. else if ( is_in_liquid( x, y, &lx, &ly ) && random_val( 0, 10 ) == 0 ) {
  2666. particle_t tmp_b = get_particle_at( lx, ly );
  2667. write_data( compute_idx( lx, ly ), *p );
  2668. write_data( read_idx, tmp_b );
  2669. }
  2670. else {
  2671. particle_t tmp = *p;
  2672. b32 found = false;
  2673. // Don't try to spread if something is directly above you?
  2674. if ( completely_surrounded( x, y ) ) {
  2675. write_data( read_idx, tmp );
  2676. return;
  2677. }
  2678. else {
  2679. for ( u32 i = 0; i < fall_rate; ++i ) {
  2680. for ( s32 j = spread_rate; j > 0; --j )
  2681. {
  2682. if ( in_bounds( x - j, y + i ) && (is_empty( x - j, y + i ) || get_particle_at( x - j, y + i ).id == mat_id_oil ) ) {
  2683. particle_t tmp = get_particle_at( x - j, y + i );
  2684. write_data( compute_idx( x - j, y + i ), *p );
  2685. write_data( read_idx, tmp );
  2686. found = true;
  2687. break;
  2688. }
  2689. if ( in_bounds( x + j, y + i ) && (is_empty( x + j, y + i ) || get_particle_at( x + j, y + i ).id == mat_id_oil ) ) {
  2690. particle_t tmp = get_particle_at( x + j, y + i );
  2691. write_data( compute_idx( x + j, y + i ), *p );
  2692. write_data( read_idx, tmp );
  2693. found = true;
  2694. break;
  2695. }
  2696. }
  2697. }
  2698. if ( !found ) {
  2699. write_data( read_idx, tmp );
  2700. }
  2701. }
  2702. }
  2703. }
  2704. void update_default( u32 w, u32 h )
  2705. {
  2706. u32 read_idx = compute_idx( w, h );
  2707. write_data( read_idx, get_particle_at( w, h ) );
  2708. }
  2709. particle_t particle_empty()
  2710. {
  2711. particle_t p = {0};
  2712. p.id = mat_id_empty;
  2713. p.color = mat_col_empty;
  2714. return p;
  2715. }
  2716. particle_t particle_sand()
  2717. {
  2718. particle_t p = {0};
  2719. p.id = mat_id_sand;
  2720. // Random sand color
  2721. f32 r = (f32)(random_val( 0, 10 )) / 10.f;
  2722. p.color.r = (u8)(gs_interp_linear(0.8f, 1.f, r) * 255.f);
  2723. p.color.g = (u8)(gs_interp_linear(0.5f, 0.6f, r) * 255.f);
  2724. p.color.b = (u8)(gs_interp_linear(0.2f, 0.25f, r) * 255.f);
  2725. p.color.a = 255;
  2726. return p;
  2727. }
  2728. particle_t particle_water()
  2729. {
  2730. particle_t p = {0};
  2731. p.id = mat_id_water;
  2732. f32 r = (f32)(random_val( 0, 1 )) / 2.f;
  2733. p.color.r = (u8)(gs_interp_linear(0.1f, 0.15f, r) * 255.f);
  2734. p.color.g = (u8)(gs_interp_linear(0.3f, 0.35f, r) * 255.f);
  2735. p.color.b = (u8)(gs_interp_linear(0.7f, 0.8f, r) * 255.f);
  2736. p.color.a = 255;
  2737. return p;
  2738. }
  2739. particle_t particle_salt()
  2740. {
  2741. particle_t p = {0};
  2742. p.id = mat_id_salt;
  2743. f32 r = (f32)(random_val( 0, 1 )) / 2.f;
  2744. p.color.r = (u8)(gs_interp_linear(0.9f, 1.0f, r) * 255.f);
  2745. p.color.g = (u8)(gs_interp_linear(0.8f, 0.85f, r) * 255.f);
  2746. p.color.b = (u8)(gs_interp_linear(0.8f, 0.9f, r) * 255.f);
  2747. p.color.a = 255;
  2748. return p;
  2749. }
  2750. particle_t particle_wood()
  2751. {
  2752. particle_t p = {0};
  2753. p.id = mat_id_wood;
  2754. f32 r = (f32)(random_val( 0, 1 )) / 2.f;
  2755. p.color.r = (u8)(gs_interp_linear(0.23f, 0.25f, r) * 255.f);
  2756. p.color.g = (u8)(gs_interp_linear(0.15f, 0.18f, r) * 255.f);
  2757. p.color.b = (u8)(gs_interp_linear(0.02f, 0.03f, r) * 255.f);
  2758. p.color.a = 255;
  2759. return p;
  2760. }
  2761. particle_t particle_gunpowder()
  2762. {
  2763. particle_t p = {0};
  2764. p.id = mat_id_gunpowder;
  2765. f32 r = (f32)(random_val( 0, 1 )) / 2.f;
  2766. p.color.r = (u8)(gs_interp_linear(0.15f, 0.2f, r) * 255.f);
  2767. p.color.g = (u8)(gs_interp_linear(0.15f, 0.2f, r) * 255.f);
  2768. p.color.b = (u8)(gs_interp_linear(0.15f, 0.2f, r) * 255.f);
  2769. p.color.a = 255;
  2770. return p;
  2771. }
  2772. particle_t particle_oil()
  2773. {
  2774. particle_t p = {0};
  2775. p.id = mat_id_oil;
  2776. f32 r = (f32)(random_val( 0, 1 )) / 2.f;
  2777. p.color.r = (u8)(gs_interp_linear(0.12f, 0.15f, r) * 255.f);
  2778. p.color.g = (u8)(gs_interp_linear(0.10f, 0.12f, r) * 255.f);
  2779. p.color.b = (u8)(gs_interp_linear(0.08f, 0.10f, r) * 255.f);
  2780. p.color.a = 255;
  2781. return p;
  2782. }
  2783. particle_t particle_fire()
  2784. {
  2785. particle_t p = {0};
  2786. p.id = mat_id_fire;
  2787. p.color = mat_col_fire;
  2788. return p;
  2789. }
  2790. particle_t particle_lava()
  2791. {
  2792. particle_t p = {0};
  2793. p.id = mat_id_lava;
  2794. p.color = mat_col_fire;
  2795. return p;
  2796. }
  2797. particle_t particle_ember()
  2798. {
  2799. particle_t p = {0};
  2800. p.id = mat_id_ember;
  2801. p.color = mat_col_ember;
  2802. return p;
  2803. }
  2804. particle_t particle_smoke()
  2805. {
  2806. particle_t p = {0};
  2807. p.id = mat_id_smoke;
  2808. p.color = mat_col_smoke;
  2809. return p;
  2810. }
  2811. particle_t particle_steam()
  2812. {
  2813. particle_t p = {0};
  2814. p.id = mat_id_steam;
  2815. p.color = mat_col_steam;
  2816. return p;
  2817. }
  2818. particle_t particle_stone()
  2819. {
  2820. particle_t p = {0};
  2821. p.id = mat_id_stone;
  2822. f32 r = (f32)(random_val( 0, 1 )) / 2.f;
  2823. p.color.r = (u8)(gs_interp_linear(0.5f, 0.65f, r) * 255.f);
  2824. p.color.g = (u8)(gs_interp_linear(0.5f, 0.65f, r) * 255.f);
  2825. p.color.b = (u8)(gs_interp_linear(0.5f, 0.65f, r) * 255.f);
  2826. p.color.a = 255;
  2827. return p;
  2828. }
  2829. particle_t particle_acid()
  2830. {
  2831. particle_t p = {0};
  2832. p.id = mat_id_acid;
  2833. f32 r = (f32)(random_val( 0, 1 )) / 2.f;
  2834. p.color.r = (u8)(gs_interp_linear(0.05f, 0.06f, r) * 255.f);
  2835. p.color.g = (u8)(gs_interp_linear(0.8f, 0.85f, r) * 255.f);
  2836. p.color.b = (u8)(gs_interp_linear(0.1f, 0.12f, r) * 255.f);
  2837. p.color.a = 200;
  2838. return p;
  2839. }