render.c (9123B)
1 #include "engine/render.h" 2 3 #include <math.h> 4 #include <stdio.h> 5 #include <string.h> 6 7 #include "config.h" 8 9 #if PH_USE_SDL 10 static float 11 ph_world_pixel(float position, float camera) 12 { 13 return floorf(position + 0.5f) - floorf(camera + 0.5f); 14 } 15 16 static SDL_Texture * 17 ph_load_sprite_sheet(SDL_Renderer *renderer, const PhAssetDef *def) 18 { 19 SDL_Surface *surface; 20 SDL_Texture *texture; 21 22 surface = SDL_LoadBMP(def->path); 23 if (!surface) { 24 fprintf(stderr, "SDL_LoadBMP failed for %s: %s\n", 25 def->path, SDL_GetError()); 26 return NULL; 27 } 28 29 SDL_SetSurfaceColorKey(surface, true, SDL_MapSurfaceRGB(surface, 30 def->key.r, def->key.g, def->key.b)); 31 texture = SDL_CreateTextureFromSurface(renderer, surface); 32 SDL_DestroySurface(surface); 33 if (!texture) { 34 fprintf(stderr, "SDL_CreateTextureFromSurface failed: %s\n", SDL_GetError()); 35 } 36 return texture; 37 } 38 39 int 40 ph_render_assets_load(PhRenderAssets *assets, SDL_Renderer *renderer, 41 const PhAssetDef *defs, int def_count) 42 { 43 int i; 44 45 memset(assets, 0, sizeof(*assets)); 46 if (!renderer || def_count < 0 || def_count > PH_MAX_ASSETS || 47 (def_count > 0 && !defs)) return -1; 48 assets->texture_count = def_count; 49 for (i = 0; i < def_count; ++i) { 50 if (!defs[i].path) continue; 51 assets->textures[i] = ph_load_sprite_sheet(renderer, &defs[i]); 52 if (!assets->textures[i]) { 53 ph_render_assets_destroy(assets); 54 return -1; 55 } 56 } 57 return 0; 58 } 59 60 void 61 ph_render_assets_destroy(PhRenderAssets *assets) 62 { 63 int i; 64 65 for (i = 0; i < assets->texture_count; ++i) 66 SDL_DestroyTexture(assets->textures[i]); 67 memset(assets, 0, sizeof(*assets)); 68 } 69 70 SDL_Texture * 71 ph_render_asset(const PhRenderAssets *assets, int asset_id) 72 { 73 return asset_id >= 0 && asset_id < assets->texture_count ? 74 assets->textures[asset_id] : NULL; 75 } 76 77 void 78 ph_render_sprite(SDL_Renderer *renderer, SDL_Texture *texture, 79 int sprite_tile_x, int sprite_tile_y, SDL_FRect dst, SDL_FlipMode flip) 80 { 81 SDL_FRect src = { 82 .x = (float)(sprite_tile_x * PH_TILE_SIZE), 83 .y = (float)(sprite_tile_y * PH_TILE_SIZE), 84 .w = (float)PH_TILE_SIZE, 85 .h = (float)PH_TILE_SIZE, 86 }; 87 88 SDL_RenderTextureRotated(renderer, texture, &src, &dst, 0.0, NULL, flip); 89 } 90 91 void 92 ph_render_entity_frame(const PhEntityDef *def, const PhEntity *entity, 93 int *sprite_tile_x, int *sprite_tile_y, SDL_FlipMode *flip) 94 { 95 int direction = entity->motion.facing_y > 0 ? PH_SPRITE_DOWN : 96 entity->motion.facing_y < 0 ? PH_SPRITE_UP : PH_SPRITE_SIDE; 97 int frame = ph_entity_animation_frame(def, entity); 98 99 *sprite_tile_x = def->sprite_tiles[direction][frame][0]; 100 *sprite_tile_y = def->sprite_tiles[direction][frame][1]; 101 *flip = direction == PH_SPRITE_SIDE && entity->motion.facing_x > 0 ? 102 SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; 103 } 104 105 static void 106 ph_draw_area(SDL_Renderer *renderer, const PhRenderAssets *assets, const PhWorld *world) 107 { 108 int tx; 109 int ty; 110 111 for (ty = (int)(world->camera.pos.y / PH_TILE_SIZE); 112 ty < world->area.height && (float)(ty * PH_TILE_SIZE) < 113 world->camera.pos.y + world->camera.viewport_h; ++ty) { 114 for (tx = (int)(world->camera.pos.x / PH_TILE_SIZE); 115 tx < world->area.width && (float)(tx * PH_TILE_SIZE) < 116 world->camera.pos.x + world->camera.viewport_w; ++tx) { 117 const PhTileDef *def = ph_area_tile_def(&world->area, tx, ty); 118 SDL_Texture *texture; 119 SDL_FRect rect = { 120 .x = ph_world_pixel((float)(tx * PH_TILE_SIZE), world->camera.pos.x), 121 .y = ph_world_pixel((float)(ty * PH_TILE_SIZE), world->camera.pos.y), 122 .w = (float)PH_TILE_SIZE, 123 .h = (float)PH_TILE_SIZE, 124 }; 125 SDL_FRect src; 126 int sprite_tile_x; 127 int sprite_tile_y; 128 129 if (!def) continue; 130 if (def->background_asset_id) { 131 SDL_Texture *background = ph_render_asset(assets, 132 def->background_asset_id); 133 SDL_FRect background_src = { 134 .x = (float)(def->background_sprite_tile_x * PH_TILE_SIZE), 135 .y = (float)(def->background_sprite_tile_y * PH_TILE_SIZE), 136 .w = (float)PH_TILE_SIZE, 137 .h = (float)PH_TILE_SIZE, 138 }; 139 140 if (background) 141 SDL_RenderTexture(renderer, background, &background_src, &rect); 142 } 143 texture = ph_render_asset(assets, def->asset_id); 144 if (!texture) continue; 145 sprite_tile_x = def->sprite_tile_x; 146 sprite_tile_y = def->sprite_tile_y; 147 if (def->autotile == PH_TILE_AUTOTILE_3X3) { 148 const PhTileDef *north = ph_area_tile_def(&world->area, tx, ty - 1); 149 const PhTileDef *east = ph_area_tile_def(&world->area, tx + 1, ty); 150 const PhTileDef *south = ph_area_tile_def(&world->area, tx, ty + 1); 151 const PhTileDef *west = ph_area_tile_def(&world->area, tx - 1, ty); 152 int joins_north = north && north->symbol == def->symbol; 153 int joins_east = east && east->symbol == def->symbol; 154 int joins_south = south && south->symbol == def->symbol; 155 int joins_west = west && west->symbol == def->symbol; 156 157 sprite_tile_x += joins_west == joins_east ? 1 : joins_west ? 2 : 0; 158 sprite_tile_y += joins_north == joins_south ? 1 : joins_north ? 2 : 0; 159 } else if (def->autotile == PH_TILE_AUTOTILE_FIXED && 160 def->autotile_width > 0 && def->autotile_height > 0) { 161 const PhTileDef *neighbor; 162 int offset_x = 0; 163 int offset_y = 0; 164 165 while (offset_x < def->autotile_width - 1 && 166 (neighbor = ph_area_tile_def(&world->area, 167 tx - offset_x - 1, ty)) && 168 neighbor->autotile == def->autotile && 169 neighbor->asset_id == def->asset_id && 170 neighbor->sprite_tile_x == def->sprite_tile_x && 171 neighbor->sprite_tile_y == def->sprite_tile_y && 172 neighbor->autotile_width == def->autotile_width && 173 neighbor->autotile_height == def->autotile_height) ++offset_x; 174 while (offset_y < def->autotile_height - 1 && 175 (neighbor = ph_area_tile_def(&world->area, 176 tx, ty - offset_y - 1)) && 177 neighbor->autotile == def->autotile && 178 neighbor->asset_id == def->asset_id && 179 neighbor->sprite_tile_x == def->sprite_tile_x && 180 neighbor->sprite_tile_y == def->sprite_tile_y && 181 neighbor->autotile_width == def->autotile_width && 182 neighbor->autotile_height == def->autotile_height) ++offset_y; 183 sprite_tile_x += offset_x; 184 sprite_tile_y += offset_y; 185 } 186 src = (SDL_FRect){ 187 .x = (float)(sprite_tile_x * PH_TILE_SIZE), 188 .y = (float)(sprite_tile_y * PH_TILE_SIZE), 189 .w = (float)PH_TILE_SIZE, 190 .h = (float)PH_TILE_SIZE, 191 }; 192 193 SDL_RenderTexture(renderer, texture, &src, &rect); 194 } 195 } 196 } 197 198 static void 199 ph_draw_items(SDL_Renderer *renderer, const PhRenderAssets *assets, const PhWorld *world) 200 { 201 int i; 202 203 for (i = 0; i < world->ground_item_count; ++i) { 204 const PhGroundItem *drop = &world->ground_items[i]; 205 const PhItemDef *def = ph_world_item_def(world, drop->item_id); 206 SDL_Texture *texture = NULL; 207 SDL_FRect rect; 208 209 if (!drop->active || drop->area_id != world->area_id) { 210 continue; 211 } 212 213 rect.x = ph_world_pixel(drop->pos.x, world->camera.pos.x) - 3.0f; 214 rect.y = ph_world_pixel(drop->pos.y, world->camera.pos.y) - 3.0f; 215 rect.w = 6.0f; 216 rect.h = 6.0f; 217 218 if (def) texture = ph_render_asset(assets, def->asset_id); 219 if (texture && def->sprite_tile_x >= 0 && def->sprite_tile_y >= 0) { 220 rect.x = ph_world_pixel(drop->pos.x, world->camera.pos.x) - 221 PH_TILE_SIZE * 0.5f; 222 rect.y = ph_world_pixel(drop->pos.y, world->camera.pos.y) - 223 PH_TILE_SIZE * 0.5f; 224 rect.w = PH_TILE_SIZE; 225 rect.h = PH_TILE_SIZE; 226 ph_render_sprite(renderer, texture, 227 def->sprite_tile_x, def->sprite_tile_y, rect, SDL_FLIP_NONE); 228 } else { 229 SDL_SetRenderDrawColor(renderer, PH_ITEM_COLOR); 230 SDL_RenderFillRect(renderer, &rect); 231 } 232 } 233 } 234 235 static void 236 ph_draw_entities(SDL_Renderer *renderer, const PhRenderAssets *assets, const PhWorld *world) 237 { 238 int i; 239 240 for (i = 0; i < world->entity_count; ++i) { 241 const PhEntity *entity = &world->entities[i]; 242 const PhEntityDef *def = ph_world_entity_def(world, entity->type_id); 243 SDL_Texture *texture = NULL; 244 SDL_FRect rect; 245 int sprite_tile_x; 246 int sprite_tile_y; 247 SDL_FlipMode flip; 248 249 if (!entity->active || entity->area_id != world->area_id || !def) { 250 continue; 251 } 252 253 texture = ph_render_asset(assets, def->asset_id); 254 ph_render_entity_frame(def, entity, &sprite_tile_x, &sprite_tile_y, &flip); 255 if (texture && sprite_tile_x >= 0 && sprite_tile_y >= 0) { 256 257 rect.x = ph_world_pixel(entity->pos.x, world->camera.pos.x) - 258 PH_TILE_SIZE * 0.5f; 259 rect.y = ph_world_pixel(entity->pos.y, world->camera.pos.y) - 260 PH_TILE_SIZE * 0.75f; 261 rect.w = PH_TILE_SIZE; 262 rect.h = PH_TILE_SIZE; 263 264 ph_render_sprite(renderer, texture, sprite_tile_x, sprite_tile_y, rect, flip); 265 } else { 266 rect.x = ph_world_pixel(entity->pos.x, world->camera.pos.x) - 7.0f; 267 rect.y = ph_world_pixel(entity->pos.y, world->camera.pos.y) - 12.0f; 268 rect.w = 14.0f; 269 rect.h = 18.0f; 270 271 if (def->kind == PH_ENTITY_MONSTER) { 272 SDL_SetRenderDrawColor(renderer, PH_MONSTER_COLOR); 273 } else { 274 SDL_SetRenderDrawColor(renderer, PH_NPC_COLOR); 275 } 276 SDL_RenderFillRect(renderer, &rect); 277 } 278 } 279 } 280 void 281 ph_render_world(SDL_Renderer *renderer, const PhRenderAssets *assets, 282 const PhWorld *world) 283 { 284 ph_draw_area(renderer, assets, world); 285 ph_draw_items(renderer, assets, world); 286 ph_draw_entities(renderer, assets, world); 287 } 288 #endif