z_mirror/sdl-osu
git clone https://git.raiguard.me/z_mirror/sdl-osu
836a89c
trunk
1#include "hitobject.h" 2#include "audio.h" 3#include "beatmap.h" 4#include "beatmapreader.h" 5#include "graphics.h" 6#include "util.h" 7#include <SDL3/SDL.h> 8#include <stdio.h> 9#include <stdlib.h> 10 11SDL_EnumerationResult handleCountBeatmapHitObjects (char * section ,char * /*values*/ [],size_t /*valuesLen*/ ,void * userdata ) { 12if (strcmp (section ,"HitObjects" )== 0 ) { 13 (* (size_t * )userdata )++ ; 14 } 15 16return SDL_ENUM_CONTINUE ; 17} 18 19SDL_EnumerationResult handleBeatmapHitObjectCSVRow (char * section ,char * values [],size_t valuesLen ,void * userdata ) { 20if (strcmp (section ,"HitObjects" )!= 0 ) { 21return SDL_ENUM_CONTINUE ; 22 } 23 24struct Osu_HitObjects * data = userdata ; 25 26SDL_assert (valuesLen >=5 ); 27 28struct Osu_HitObject * hitObject = & data -> objects [data -> len ]; 29data -> len ++ ; 30 31// FIXME: Error checking 32hitObject -> x = strtof (values [0 ],NULL ); 33hitObject -> y = strtof (values [1 ],NULL ); 34hitObject -> time = strtol (values [2 ],NULL ,0 ); 35hitObject -> hitSounds = strtol (values [4 ],NULL ,0 )& 0b1111 ; 36 37Uint8 type = strtol (values [3 ],NULL ,0 ); 38if (type & (1 <<0 )) { 39hitObject -> type = OSU_HITOBJECT_TYPE_CIRCLE ; 40 }else if (type & (1 <<1 )) { 41hitObject -> type = OSU_HITOBJECT_TYPE_SLIDER ; 42 }else if (type & (1 <<3 )) { 43hitObject -> type = OSU_HITOBJECT_TYPE_SPINNER ; 44 } 45 46OSU_LOG_TRACEF ("%u at %.1f,%.1f at time %lu\n" ,hitObject -> type ,hitObject -> x ,hitObject -> y ,hitObject -> time ); 47 48return SDL_ENUM_CONTINUE ; 49} 50 51struct Osu_HitObjects * Osu_LoadHitObjectsFromBeatmap (struct Osu_Beatmap * beatmap ) { 52OSU_LOG_TRACEF ("Loading beatmap file: %s" ,beatmap -> filepath ); 53char * file = SDL_LoadFile (beatmap -> filepath ,NULL ); 54if (!file ) { 55OSU_LOG_TRACEF ("Unable to load file: %s" ,SDL_GetError ()); 56return NULL ; 57 } 58 59struct Osu_HitObjects * ho = malloc (sizeof (struct Osu_HitObjects )); 60SDL_zero (* ho ); 61 62size_t numHitObjects = 0 ; 63Osu_EnumerateBeatmapData (file ,NULL ,& handleCountBeatmapHitObjects ,& numHitObjects ); 64 65ho -> objects = calloc (numHitObjects ,sizeof (struct Osu_HitObject )); 66 67if (!Osu_EnumerateBeatmapData (file ,NULL ,& handleBeatmapHitObjectCSVRow ,ho )) { 68Osu_DestroyHitObjects (ho ); 69SDL_free (file ); 70return NULL ; 71 } 72 73OSU_LOG_TRACEF ("%zu hit objects\n" ,ho -> len ); 74 75SDL_free (file ); 76return ho ; 77} 78 79void Osu_DestroyHitObjects (struct Osu_HitObjects * ho ) { 80free (ho -> objects ); 81free (ho ); 82} 83 84void Osu_UpdateHitObjects (struct Osu_HitObjects * ho ,struct Osu_Game * game ) { 85if (ho -> cursor == ho -> len ) { 86return ; 87 } 88SDL_assert (ho -> cursor < ho -> len ); 89 90size_t cursor = ho -> cursor ; 91while (cursor < ho -> len ) { 92struct Osu_HitObject * obj = & ho -> objects [cursor ]; 93if (obj -> time + OSU_HITOBJECT_LEAD_OUT < game -> beatmapTick ) { 94break ; 95 } 96if (obj -> time == game -> beatmapTick ) { 97Osu_PlayHitSounds (game -> audio ,obj ); 98 } 99cursor ++ ; 100 } 101 102// TODO: Two cursors - one for the next object to hit and one for the next object to draw. 103while (ho -> cursor < ho -> len && ho -> objects [ho -> cursor ].time + OSU_HITOBJECT_LEAD_OUT < game -> beatmapTick ) { 104ho -> cursor ++ ; 105 } 106} 107 108float min (float a ,float b ) { 109return a < b ?a :b ; 110} 111 112void Osu_RenderHitObjects (struct Osu_HitObjects * ho ,struct Osu_Graphics * graphics ,Sint32 tick ) { 113if (ho -> cursor > ho -> len ) { 114return ; 115 } 116 117SDL_FRect bgRect = { 118 .x = 200 , 119 .y = 200 , 120 .w = 512 * 3 , 121 .h = 384 * 3 , 122 }; 123SDL_SetRenderDrawBlendMode (graphics -> renderer ,SDL_BLENDMODE_NONE ); 124SDL_SetRenderDrawColor (graphics -> renderer ,255 ,255 ,255 ,255 ); 125SDL_RenderRect (graphics -> renderer ,& bgRect ); 126 127SDL_FRect destRect = {}; 128SDL_GetTextureSize (graphics -> hitCircleTexture ,& destRect .w ,& destRect .h ); 129destRect .w *=1.5 ; 130destRect .h *=1.5 ; 131 132size_t cursor = ho -> cursor ; 133while (cursor < ho -> len && ho -> objects [cursor ].time - OSU_HITOBJECT_LEAD_IN <=tick ) { 134struct Osu_HitObject * obj = & ho -> objects [cursor ]; 135float alpha = 1.0f ; 136if (tick < obj -> time ) { 137alpha = min ((1.0f - (float )(obj -> time - tick ) /OSU_HITOBJECT_LEAD_IN )* 1.25 ,1.0f ); 138 }else if (tick > obj -> time ) { 139alpha = 1.0f - (float )(tick - obj -> time ) /OSU_HITOBJECT_LEAD_OUT ; 140 } 141 142destRect .x = obj -> x * 3 + 200 - destRect .w /2 ; 143destRect .y = obj -> y * 3 + 200 - destRect .h /2 ; 144 145SDL_Color color = {}; 146switch (obj -> type ) { 147case OSU_HITOBJECT_TYPE_CIRCLE : 148color = (SDL_Color ){26 ,116 ,242 ,255 }; 149break ; 150case OSU_HITOBJECT_TYPE_SLIDER : 151color = (SDL_Color ){164 ,32 ,240 ,255 }; 152break ; 153case OSU_HITOBJECT_TYPE_SPINNER : 154color = (SDL_Color ){37 ,185 ,239 ,255 }; 155break ; 156 } 157SDL_SetTextureColorMod (graphics -> hitCircleTexture ,color .r ,color .g ,color .b ); 158SDL_SetTextureAlphaModFloat (graphics -> hitCircleOverlayTexture ,alpha ); 159SDL_RenderTexture (graphics -> renderer ,graphics -> hitCircleTexture ,NULL ,& destRect ); 160SDL_RenderTexture (graphics -> renderer ,graphics -> hitCircleOverlayTexture ,NULL ,& destRect ); 161 162if (tick < obj -> time ) { 163SDL_FRect acRect = {}; 164SDL_GetTextureSize (graphics -> approachCircleTexture ,& acRect .w ,& acRect .h ); 165float ratio = (float )(obj -> time - tick ) /OSU_HITOBJECT_LEAD_IN ; 166float sizeRatio = 1.5f * (1.0f - ratio )+ (5.0f * ratio ); 167acRect .w *=sizeRatio ; 168acRect .h *=sizeRatio ; 169acRect .x = obj -> x * 3 + 200 - acRect .w /2 ; 170acRect .y = obj -> y * 3 + 200 - acRect .h /2 ; 171SDL_SetTextureColorMod (graphics -> approachCircleTexture ,color .r ,color .g ,color .b ); 172SDL_SetTextureAlphaModFloat (graphics -> approachCircleTexture ,alpha ); 173SDL_RenderTexture (graphics -> renderer ,graphics -> approachCircleTexture ,NULL ,& acRect ); 174 } 175 176cursor ++ ; 177 } 178}