z_mirror/sdl-osu

git clone https://git.raiguard.me/z_mirror/sdl-osu

Caleb HeuerSwitch to four-space indents and inline braces836a89c

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