implemented tilemap rendering

This commit is contained in:
Sara 2023-10-01 15:22:17 +02:00
parent 124d379a3a
commit 4ebe759030
29 changed files with 3377 additions and 8223 deletions

View file

@ -1,6 +1,8 @@
#include "assets.h"
#include "debug.h"
#include <stdlib.h>
#include <memory.h>
#include <stdio.h>
typedef
struct AssetData {
@ -9,11 +11,29 @@ struct AssetData {
AssetDestructor destructor;
} AssetData;
cJSON* g_levels_json;
static AssetData* _assets = NULL;
static size_t _assets_len = 0;
static size_t _assets_cap = 0;
static asset_id _next_id = 0;
static
size_t file_length(FILE* fp) {
size_t start = ftell(fp);
fseek(fp, 0, SEEK_END);
size_t r = ftell(fp);
fseek(fp, start, SEEK_SET);
return r;
}
static
void read_file(FILE* fp, char* out_buffer, size_t out_size) {
size_t start = ftell(fp);
fread(out_buffer, 1, out_size, fp);
fseek(fp, start, SEEK_SET);
}
static
void _resize_if_needed(size_t requested_len) {
if(requested_len < _assets_cap) {
@ -32,8 +52,45 @@ void _resize_if_needed(size_t requested_len) {
}
void assets_init() {
LOG_INFO("assets_init");
_assets = malloc(_assets_cap * sizeof(AssetData));
_next_id = 1;
g_levels_json = load_json_from_file("assets/levels/levels.ldtk");
}
// clear all assets without shrinking the assets array.
static
void _empty_assets() {
AssetData* start = _assets;
AssetData* end = _assets + _assets_len;
for(AssetData* asset = start; asset < end; ++asset) {
asset->destructor(asset->asset);
}
_assets_len = 0;
}
void assets_clean() {
_empty_assets();
free(_assets);
_assets_len = 0;
_assets_cap = 0;
_assets = NULL;
cJSON_Delete(g_levels_json);
g_levels_json = NULL;
}
void assets_reset() {
_empty_assets();
size_t desired = 8;
AssetData* new = realloc(_assets, desired * sizeof(AssetData));
if(new == NULL)
return;
_assets_cap = desired;
_assets = new;
}
asset_id store_asset(void* asset, AssetDestructor destructor) {
@ -96,5 +153,40 @@ void free_asset(asset_id id) {
AssetData* next = found + 1;
size_t dif = next - (_assets + _assets_len);
memmove(found, next, dif);
--_assets_len;
}
cJSON* load_json_from_file(const char* filename) {
FILE* fp = fopen(filename, "r");
if(fp == NULL) {
return NULL;
}
size_t len = file_length(fp);
char* buffer = malloc(len + 1);
if(buffer == NULL) {
return NULL;
}
read_file(fp, buffer, len);
cJSON* out = cJSON_Parse(buffer);
free(buffer);
if(out == NULL) {
return NULL;
}
return out;
}
size_t json_array_len(cJSON* json) {
if(!cJSON_IsArray(json)) {
return 0;
}
size_t len = 0;
cJSON* itr;
cJSON_ArrayForEach(itr, json) {
++len;
}
return len;
}

View file

@ -1,18 +1,21 @@
#ifndef _fencer_assets_h
#define _fencer_assets_h
#include "vmath.h"
#include <cjson/cJSON.h>
#include <stdint.h>
typedef void(*AssetDestructor)(void*);
typedef uintptr_t asset_id;
extern cJSON* g_levels_json;
// Prepare asset management.
// Other asset management functions are invalid until assets_init is called.
void assets_init();
// Clean up and shut down assets management.
void assets_clean();
// Clear all assets and reset to initial state.
// Clear all loaded assets
void assets_reset();
// Submit an object to be managed as an asset. If the destructor is NULL, free will be used.
// Returns zero if asset could not be stored.
@ -24,5 +27,23 @@ void* get_asset(asset_id id);
asset_id get_asset_id(void* asset);
// Free an asset managed by the asset manager using it's destructor, or free(..) if NULL.
void free_asset(asset_id id);
// load a file's contents into json
cJSON* load_json_from_file(const char* filepath);
// Get the length of a cJSON array
size_t json_array_len(cJSON* array);
static inline
Vector json_array_to_vector(cJSON* array) {
return (Vector) {
cJSON_GetArrayItem(array, 0)->valuedouble,
cJSON_GetArrayItem(array, 1)->valuedouble,
};
}
static inline
IVector json_array_to_ivector(cJSON* array) {
return (IVector) {
cJSON_GetArrayItem(array, 0)->valueint,
cJSON_GetArrayItem(array, 1)->valueint
};
}
#endif // !_fencer_assets_h

View file

@ -1,9 +1,11 @@
#include "camera.h"
#include "debug.h"
#include "render.h"
Camera g_camera;
void camera_init() {
LOG_INFO("camera_init");
g_camera.centre = ZeroVector;
g_camera.width = 10;
}
@ -14,28 +16,16 @@ float _camera_height(Camera* self) {
}
static inline
float _one() {
float _cam_to_world_conversion() {
return g_render_resolution.x / g_camera.width;
}
SDL_FRect camera_world_to_screen_space(Camera* self, SDL_FRect* world_space) {
float unit = _one();
float unit = _cam_to_world_conversion();
return (SDL_FRect) {
.x = (world_space->x - g_camera.centre.x + g_camera.width/2.0) * unit,
.y = (world_space->y - g_camera.centre.y + _camera_height(&g_camera)/2.0) * unit,
.x = (g_camera.centre.x - world_space->x + g_camera.width/2.0) * unit,
.y = (g_camera.centre.y - world_space->y + _camera_height(&g_camera)/2.0) * unit,
.w = world_space->w * unit,
.h = world_space->h * unit
};
}
SDL_FRect camera_calculate_unit_rect() {
// If the camera's width is 1, this rectangle should fill the width of the screen.
// If the camera's width is 2, this rectangle should fill half the width of the screen.
float unit = _one();
return (SDL_FRect) {
.x = (-g_camera.centre.x + g_camera.width/2) * unit,
.y = (-g_camera.centre.y + _camera_height(&g_camera)/2) * unit,
.w = unit,
.h = unit
};
}

View file

@ -15,9 +15,6 @@ typedef struct Camera Camera;
extern Camera g_camera;
extern void camera_init();
// generate a screen-space rectangle that is exactly 1x1 in world units.
// With it's centre on the world origin.
extern SDL_FRect camera_calculate_world_unit_rect(Camera* self);
extern SDL_FRect camera_screen_to_world_space(Camera* self, SDL_FRect* camera_space);
extern SDL_FRect camera_world_to_screen_space(Camera* self, SDL_FRect* world_space);

7
src/debug.c Normal file
View file

@ -0,0 +1,7 @@
#include "debug.h"
#if NDEBUG
int g_debug_error_abort = 0;
#else
int g_debug_error_abort = 1;
#endif

25
src/debug.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef _fencer_debug_h
#define _fencer_debug_h
#include <stdio.h>
extern int g_debug_error_abort;
#define LOG_INFO(...) do {\
printf("[%s:%d] ", __FILE__, __LINE__);\
printf("INFO | ");\
printf(__VA_ARGS__);\
printf("\n");\
fflush(stdout);\
} while(0)
#define LOG_ERROR(...) do {\
printf("[%s:%d] ", __FILE__, __LINE__);\
printf("ERROR | ");\
printf(__VA_ARGS__);\
printf("\n");\
fflush(stdout);\
if(g_debug_error_abort != 0) abort();\
} while(0)
#endif // !_fencer_debug_h

130
src/level.c Normal file
View file

@ -0,0 +1,130 @@
#include "level.h"
#include "assets.h"
#include "debug.h"
#include "tilemap.h"
#include <cjson/cJSON.h>
#include <stdio.h>
#define LEVEL_EXT ".ldtkl"
#define WORLD_EXT ".ldtk"
#define LEVEL_DIR "assets/levels/"
struct Level {
asset_id asset_id;
size_t level_iid;
Tilemap** tilemaps;
size_t tilemaps_len;
Vector player_start;
};
static
void _deallocate_level(void* self_void) {
Level* self = self_void;
free(self);
}
static
size_t _count_layers(cJSON* layers, size_t* entities, size_t* automap) {
size_t ent_ = 0, auto_ = 0, tot_ = 0;
cJSON* layer;
cJSON_ArrayForEach(layer, layers) {
cJSON* type = cJSON_GetObjectItem(layer, "__type");
if(type == NULL) {
continue;
} else if(strcmp(type->valuestring, "AutoLayer") == 0) {
auto_++;
} else if(strcmp(type->valuestring, "Entities") == 0) {
ent_++;
}
tot_++;
}
if(entities != NULL)
*entities = ent_;
if(automap != NULL)
*automap = auto_;
return tot_;
}
Level* level_from_json(cJSON* json) {
// allocate a new level struct
Level* self = malloc(sizeof(Level));
// ensure allocations succeeded and initialize
if(self == NULL) {
LOG_ERROR("Failed to allocate level");
return NULL;
}
*self = (Level) {
.asset_id = store_asset(self, &_deallocate_level)
};
// fetch the instance id
cJSON* iid = cJSON_GetObjectItem(json, "iid");
if(iid != NULL && cJSON_IsNumber(iid))
self->level_iid = iid->valueint;
// get the level's layers
cJSON* layers = cJSON_GetObjectItem(json, "layerInstances");
// count the different kinds of layers
size_t entity_layer_count;
_count_layers(layers, &entity_layer_count, &self->tilemaps_len);
// allocate tilemap array
self->tilemaps = malloc(self->tilemaps_len * sizeof(Tilemap*));
Tilemap** next_tilemap = self->tilemaps;
if(layers != NULL && cJSON_IsArray(layers)) {
cJSON* layer;
cJSON_ArrayForEach(layer, layers) {
cJSON* type = cJSON_GetObjectItem(layer, "__type");
if(strcmp(type->valuestring, "AutoLayer") == 0) {
// load a tilemap as an autolayer
LOG_INFO("loading autolayer");
*next_tilemap = tilemap_from_autolayer(layer);
++next_tilemap;
} else if(strcmp(type->valuestring, "Entities")) {
// load entities
}
}
}
return self;
}
Level* level_load(const char* level_id) {
// Convert the level ID to a filepath
size_t filename_len = strlen(LEVEL_DIR) + strlen(level_id) + strlen(LEVEL_EXT) + 1;
char* filename = malloc(filename_len);
sprintf(filename, "%s%s%s", LEVEL_DIR, level_id, LEVEL_EXT);
LOG_INFO("loading level %s at %s", level_id, filename);
cJSON* level = load_json_from_file(filename);
if(level == NULL) {
LOG_ERROR("Failed to load level file %s", filename);
free(filename);
return NULL;
}
free(filename);
return level_from_json(level);
}
void level_destroy(Level* self) {
free_asset(self->asset_id);
}
void level_draw(Level* self) {
Tilemap** end = self->tilemaps + self->tilemaps_len;
for(Tilemap** map = self->tilemaps; map != end; ++map) {
tilemap_draw(*map);
}
}

19
src/level.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef _fencer_level_h
#define _fencer_level_h
#include "sprite.h"
#include <SDL2/SDL_render.h>
#include <cjson/cJSON.h>
typedef struct Level Level;
extern void world_init();
extern void world_close();
extern Level* level_from_json(cJSON* json);
extern Level* level_load(const char* level_id);
extern void level_destroy(Level* self);
extern void level_draw(Level* self);
#endif // !_fencer_level_h

View file

@ -1,5 +1,6 @@
#include "render.h"
#include "program.h"
#include "debug.h"
#include <SDL2/SDL_pixels.h>
#include <SDL2/SDL_render.h>
@ -9,6 +10,7 @@ SDL_Rect g_render_area;
IVector g_render_resolution;
void render_init(SDL_Window* window, const struct ProgramSettings* settings) {
LOG_INFO("render_init");
// create renderer, needs to be able to target textures, preferably hardware accelerated
g_renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
// create render target texture

View file

@ -1,5 +1,6 @@
#include "sprite.h"
#include "camera.h"
#include "debug.h"
#include "render.h"
#include "spritesheet.h"
#include <SDL2/SDL_image.h>
@ -7,7 +8,7 @@
struct Sprite {
// The animation sheet to sample sprites from.
Spritesheet* sheet;
Spritesheet* spritesheet;
// The current frame of animation.
size_t current_frame;
@ -15,17 +16,20 @@ struct Sprite {
float current_frame_time;
// The local transformation of this sprite.
Transform transform;
Vector origin;
};
Sprite* sprite_from_spritesheet(Spritesheet* sheet) {
Sprite* sprite_from_spritesheet(Spritesheet* sheet, size_t initial_frame) {
Sprite* self = malloc(sizeof(Sprite));
self->sheet = sheet;
self->transform = IdentityTransform;
if(self == NULL) {
LOG_ERROR("Failed to allocate memory for new sprite");
return NULL;
}
self->spritesheet = sheet;
self->origin = (Vector){0.5f, 1.0f};
self->current_frame = 0;
self->current_frame = initial_frame;
// TODO: replace with a getter for the current game time.
self->current_frame_time = 0;
@ -37,12 +41,10 @@ void sprite_destroy(Sprite* self) {
}
void sprite_draw(Sprite* self, Transform transform) {
SDL_Texture* texture = spritesheet_get_texture(self->sheet);
SDL_Rect source = spritesheet_get_frame_rect(self->sheet, self->current_frame);
transform = transform_apply(transform, self->transform);
SDL_Texture* texture = spritesheet_get_texture(self->spritesheet);
SDL_Rect source = spritesheet_get_frame_rect(self->spritesheet, self->current_frame);
Vector left_top = vinvf(transform_point(&transform, self->origin));
printf("lt: %f %f\n", left_top.x, left_top.y);
printf("or: %f %f\n", self->origin.x, self->origin.y);
SDL_FRect destination = (SDL_FRect) {
left_top.x, left_top.y,
@ -51,9 +53,32 @@ void sprite_draw(Sprite* self, Transform transform) {
destination = camera_world_to_screen_space(&g_camera, &destination);
Vector origin = vmulf(self->origin, vmulff(transform.scale, 0.5f));
self->transform.rotation += 0.01f;
Vector origin = vmulff(vmulf(self->origin, transform.scale), 0.5f);
SDL_RenderCopyExF(g_renderer, texture, &source, &destination,
transform.rotation * 57.2958, &origin, SDL_FLIP_NONE);
}
Vector sprite_get_origin(Sprite* self) {
return self->origin;
}
void sprite_set_origin(Sprite* self, Vector origin) {
self->origin = origin;
}
size_t sprite_get_frame(const Sprite* self) {
return self->current_frame;
}
void sprite_set_frame(Sprite* self, size_t size) {
self->current_frame = size;
}
Spritesheet* sprite_get_spritesheet(const Sprite* self) {
return self->spritesheet;
}
void sprite_set_spritesheet(Sprite* self, Spritesheet* spritesheet) {
self->spritesheet = spritesheet;
}

View file

@ -10,16 +10,18 @@
// Forward declaration of the private sprite struct
typedef struct Sprite Sprite;
extern Sprite* sprite_from_spritesheet(Spritesheet* sheet);
extern Sprite* sprite_from_spritesheet(Spritesheet* sheet, size_t initial_frame);
extern void sprite_destroy(Sprite* sprite);
extern void sprite_draw(Sprite* self, Transform transform);
extern void sprite_set_position(Sprite* self, Vector position);
extern void sprite_translate(Sprite* self, Vector delta_position);
extern void sprite_set_scale(Sprite* self, Vector scale);
extern void sprite_scale(Sprite* self, Vector scale);
extern void sprite_set_rotation(Sprite* self, float rotation);
extern void sprite_rotate(Sprite* self, float delta_rotation);
extern Vector sprite_get_origin(Sprite* self);
extern void sprite_set_origin(Sprite* self, Vector origin);
extern size_t sprite_get_frame(const Sprite* self);
extern void sprite_set_frame(Sprite* self, size_t size);
extern Spritesheet* sprite_get_spritesheet(const Sprite* self);
extern void sprite_set_spritesheet(Sprite* self, Spritesheet* spritesheet);
#endif // !_fencer_sprite_h

View file

@ -5,6 +5,8 @@
#include <SDL2/SDL_image.h>
struct Spritesheet {
asset_id asset_id;
// The texture to sample.
SDL_Texture* texture;
// The resolution of the texture.
@ -28,17 +30,25 @@ void _spritesheet_destroy_asset(void* self_void) {
free(self_void);
}
Spritesheet* spritesheet_from_texture(const char* texture_name, IVector frame_resolution) {
Spritesheet* spritesheet_load(const char* texture_name, IVector frame_resolution) {
SDL_Texture* texture = IMG_LoadTexture(g_renderer, texture_name);
if(texture == NULL)
return NULL;
else
return spritesheet_from_texture(texture, frame_resolution);
}
Spritesheet* spritesheet_from_texture(SDL_Texture* texture, IVector frame_resolution) {
Spritesheet* self = malloc(sizeof(Spritesheet));
if(self == NULL) {
return NULL;
}
store_asset(self, _spritesheet_destroy_asset);
self->asset_id = store_asset(self, _spritesheet_destroy_asset);
// Load the texture image and query it's size
self->texture = IMG_LoadTexture(g_renderer, texture_name);
self->texture = texture;
SDL_QueryTexture(self->texture, NULL, NULL, &self->resolution.x, &self->resolution.y);
self->animation_type = ANIMTYPE_ONCE;
@ -51,10 +61,7 @@ Spritesheet* spritesheet_from_texture(const char* texture_name, IVector frame_re
}
void spritesheet_destroy(Spritesheet* self) {
asset_id id = get_asset_id(self);
if(id != 0) {
free_asset(id);
}
free_asset(self->asset_id);
}
SDL_Texture* spritesheet_get_texture(const Spritesheet* self) {
@ -69,4 +76,8 @@ SDL_Rect spritesheet_get_frame_rect(const Spritesheet* self, size_t index) {
tile_coord.x, tile_coord.y,
self->frame_size.x, self->frame_size.y
};
}
IVector spritesheet_get_resolution(const Spritesheet* self) {
return self->resolution;
}

View file

@ -12,10 +12,12 @@ enum AnimationType {
typedef struct Spritesheet Spritesheet;
extern Spritesheet* spritesheet_from_texture(const char* texture_name, IVector frame_resolution);
extern Spritesheet* spritesheet_load(const char* texture_name, IVector frame_resolution);
extern Spritesheet* spritesheet_from_texture(SDL_Texture* texture, IVector frame_resolution);
extern void spritesheet_destroy(Spritesheet* self);
extern SDL_Texture* spritesheet_get_texture(const Spritesheet* self);
extern SDL_Rect spritesheet_get_frame_rect(const Spritesheet* self, size_t index);
extern IVector spritesheet_get_resolution(const Spritesheet* self);
#endif // !_fencer_spritesheet_h

View file

@ -1,109 +1,114 @@
#include "tilemap.h"
#include "debug.h"
#include "tileset.h"
#include "assets.h"
#include "camera.h"
#include "program.h"
#include <stdio.h>
#include "level.h"
#include "render.h"
#include "transform.h"
#include <SDL2/SDL_image.h>
#include <cjson/cJSON.h>
#define XML(__str) (const xmlChar*)__str
struct TileInstance {
Transform transform;
TileDef* tiledef;
};
struct Tilemap tilemap_load(const char* tilemap_file) {
struct Tilemap self = {
.dimensions = {0,0},
.tiles = NULL
};
return self;
}
struct Tilemap {
Transform transform;
static inline
int is_path_char(int n) {
return isalnum(n) || n == '/';
}
Tileset* set;
struct Tileset tileset_load(const char* filename) {
char* texture_path = NULL;
char* writer = NULL;
char num_buffer[6];
num_buffer[5] = '\0';
int n = 0;
struct Tileset self = {
.texture = NULL,
.tile_size = {0,0}
};
size_t len = 1;
TileInstance* map;
size_t map_num;
IVector map_size;
};
FILE* fs = fopen(filename, "r");
do {
n = fgetc(fs);
if(is_path_char(n)) {
++len;
}
} while(n != ',');
texture_path = malloc(len * sizeof(char));
rewind(fs);
writer = texture_path;
do {
n = fgetc(fs);
if(is_path_char(n)) {
*writer = n;
++writer;
} else if(n == ',') {
*writer = '\0';
}
} while(n != ',');
self.texture = IMG_LoadTexture(g_renderer, texture_path);
SDL_QueryTexture(self.texture, NULL, NULL, &self.texture_resolution.x, &self.texture_resolution.y);
self.shear = floor((float)self.texture_resolution.x / self.tile_size.x);
writer = num_buffer;
do {
n = fgetc(fs);
if(isdigit(n) || n == '-') {
*writer= n;
++writer;
} else if(n == ',' || feof(fs) || n == '\n') {
*writer = '\0';
writer = num_buffer;
if(self.tile_size.x == 0) {
self.tile_size.x = atoi(num_buffer);
} else {
self.tile_size.y = atoi(num_buffer);
}
}
} while(!feof(fs));
fclose(fs);
return self;
}
SDL_Rect tileset_index_to_rect(struct Tileset* self, size_t index) {
SDL_Rect rect = {0,0, self->tile_size.x, self->tile_size.y};
rect.x = (index % self->shear) * rect.w;
rect.y = (index / self->shear) * rect.h;
return rect;
}
void tilemap_render(struct Tilemap* self) {
size_t num_tiles = self->dimensions.x * self->dimensions.y;
SDL_Rect source_rect = {0, 0, self->tileset.tile_size.x, self->tileset.tile_size.y};
SDL_FRect world_dest_rect = { .w = 1, .h = 1 };
for(int i = 0; i < num_tiles; ++i) {
source_rect = tileset_index_to_rect(&self->tileset, self->tiles[i]);
world_dest_rect.x = (i % self->dimensions.x) * world_dest_rect.w;
world_dest_rect.y = (float)floor((float)i / self->dimensions.y) * world_dest_rect.h;
SDL_FRect camera_dest_rect = camera_world_to_screen_space(&g_camera, &world_dest_rect);
SDL_RenderCopyF(g_renderer, self->tileset.texture, &source_rect, &camera_dest_rect);
Tilemap* tilemap_from_autolayer(cJSON* json) {
cJSON* const tileset = cJSON_GetObjectItem(json, "__tilesetDefUid");
if(tileset == NULL || !cJSON_IsNumber(tileset)) {
// no tileset means we cannot point at tile definitions later
LOG_ERROR("Could not find __tilesetDefUid while loading tilemap");
return NULL;
}
}
cJSON* const tiles = cJSON_GetObjectItem(json, "autoLayerTiles");
if(tiles == NULL || !cJSON_IsArray(tiles)) {
LOG_ERROR("Could not find autoLayerTiles while loading tilemap");
return NULL;
}
cJSON* const wid = cJSON_GetObjectItem(json, "__cWid");
cJSON* const hei = cJSON_GetObjectItem(json, "__cHei");
if(wid == NULL || hei == NULL || !cJSON_IsNumber(wid) || !cJSON_IsNumber(hei)) {
// no width and height mean we cannot allocate the tile map
LOG_ERROR("Could not find __cWid or __cHei while loading tilemap");
return NULL;
}
cJSON* const grid = cJSON_GetObjectItem(json, "__gridSize");
if(grid == NULL || !cJSON_IsNumber(grid)) {
// can't know how to scale sprites
LOG_ERROR("Could not find __gridSize while loading tilemap");
return NULL;
}
// the minimum requirements for a valid tilemap are met
// allocate a tilemap and load the tileset
Tilemap* self = malloc(sizeof(Tilemap));
self->transform = IdentityTransform;
self->set = tileset_load(tileset->valueint);
self->map_size = (IVector) {
.x = wid->valueint,
.y = hei->valueint
};
self->map_num = json_array_len(tiles);
self->map = malloc(self->map_num * sizeof(TileInstance));
if(self->map == NULL) {
tileset_destroy(self->set);
free(self);
LOG_ERROR("Failed to allocate map memory");
return NULL;
}
const double px_to_ws = 1.0 / grid->valuedouble;
cJSON* tile; TileInstance* writer = self->map;
cJSON_ArrayForEach(tile, tiles) {
cJSON* t = cJSON_GetObjectItem(tile, "t");
writer->tiledef = tileset_get_tiledef(self->set, t->valueint);
writer->transform = IdentityTransform;
cJSON* px = cJSON_GetObjectItem(tile, "px");
writer->transform.position = vmulff(json_array_to_vector(px), px_to_ws);
// sprite_get_spritesheet(tiledef_get_sprite(writer->tiledef));
// LOG_INFO("Loading tile");
// LOG_INFO("tid = %d", t->valueint);
// LOG_INFO("transform =");
// LOG_INFO(".position = %f %f", writer->transform.position.x, writer->transform.position.y);
// LOG_INFO(".scale = %f %f", writer->transform.scale.x, writer->transform.scale.y);
++writer;
}
return self;
}
void tilemap_draw(Tilemap* self) {
Transform tiletrans = self->transform;
TileInstance* tile;
for(int i = 0; i < self->map_num; ++i) {
tile = self->map + i;
tiletrans = transform_apply(self->transform, tile->transform);
Sprite* sprite = tiledef_get_sprite(tile->tiledef);
// LOG_INFO("sprite: %p", sprite);
// LOG_INFO("trans: %f %f ; %f %f ; %f",
// tiletrans.position.x, tiletrans.position.y, tiletrans.scale.x, tiletrans.scale.y, tiletrans.rotation);
// LOG_INFO("self->transform: %f %f ; %f %f ; %f",
// self->transform.position.x, self->transform.position.y, self->transform.scale.x, self->transform.scale.y, self->transform.rotation);
if(sprite != NULL)
sprite_draw(sprite, tiletrans);
}
}

View file

@ -1,27 +1,19 @@
#ifndef _fencer_tilemap_h
#define _fencer_tilemap_h
#include "vmath.h"
#include "render.h"
#include <SDL2/SDL.h>
#include "spritesheet.h"
#include "tileset.h"
#include "level.h"
struct Tileset {
SDL_Texture* texture;
IVector tile_size;
IVector texture_resolution;
int shear;
};
typedef struct TileInstance TileInstance;
typedef struct Tilemap Tilemap;
struct Tilemap {
IVector dimensions;
int* tiles;
struct Tileset tileset;
};
extern struct Tilemap tilemap_load(const char* tilemap_file);
extern struct Tileset tileset_load(const char* filename);
extern SDL_Rect tileset_index_to_rect(struct Tileset* self, size_t index);
extern Tilemap* tilemap_from_autolayer(cJSON* json);
extern void tilemap_destroy(Tilemap* self);
extern void tilemap_render(struct Tilemap* map);
extern void tilemap_set_tileset(Tilemap* self, Tileset* set);
extern void tilemap_draw(Tilemap* tilemap);
#endif // !_fencer_tilemap_h

184
src/tileset.c Normal file
View file

@ -0,0 +1,184 @@
#include "tileset.h"
#include "debug.h"
#include "spritesheet.h"
#include "assets.h"
#include "render.h"
#include "SDL2/SDL_image.h"
struct TileDef {
size_t tid;
Sprite* sprite;
// Shape* collision;
};
struct Tileset {
size_t uid;
Spritesheet* atlas;
TileDef* tiledefs;
size_t tiledefs_len;
};
typedef struct TilesetStore {
Tileset* set;
size_t iid;
} TilesetStore;
static TilesetStore* _tilesets = NULL;
static size_t _tilesets_len = 0;
static size_t _tilesets_cap = 0;
static
int _tilesets_resize_if_needed(size_t required_cap) {
if(required_cap < _tilesets_cap) {
return 1;
}
size_t needed = _tilesets_cap ? _tilesets_cap : 2;
while(needed < required_cap) {
needed *= 2;
}
TilesetStore* new = realloc(_tilesets, needed * sizeof(TilesetStore));
if(new == NULL) {
LOG_ERROR("Failed to resize tileset store array.");
return 0;
}
_tilesets_cap = needed;
_tilesets = new;
return 1;
}
static
void _tileset_deallocate(void* self_void) {
Tileset* self = self_void;
TilesetStore* store = _tilesets;
for(; store < _tilesets + _tilesets_len; ++store) {
if(store->set == self) {
break;
}
}
size_t diff = _tilesets_len - (size_t)(store - _tilesets);
memmove(store, store+1, diff-1);
--_tilesets_len;
spritesheet_destroy(self->atlas);
free(self->tiledefs);
}
static
void _tileset_store(Tileset* self) {
_tilesets_resize_if_needed(_tilesets_len + 1);
_tilesets[_tilesets_len].set = self;
_tilesets[_tilesets_len].iid = self->uid;
}
Tileset* tileset_from_json(cJSON* json) {
cJSON* uid = cJSON_GetObjectItem(json, "uid");
if(uid == NULL || !cJSON_IsNumber(uid)) {
LOG_ERROR("Failed to find uid while loading tileset");
return NULL;
}
cJSON* path = cJSON_GetObjectItem(json, "relPath");
if(path == NULL || !cJSON_IsString(path)) {
LOG_ERROR("Failed to find relPath while loading tileset");
return NULL;
}
cJSON* gridsize = cJSON_GetObjectItem(json, "tileGridSize");
if(gridsize == NULL || !cJSON_IsNumber(gridsize)) {
LOG_ERROR("Failed to find tileGridSize while loading tileset");
return NULL;
}
SDL_Surface* atlas_surface = IMG_Load(path->valuestring);
if(atlas_surface == NULL) {
LOG_ERROR("Failed to load atlas image while loading tileset");
return NULL;
}
Tileset* self = malloc(sizeof(Tileset));
SDL_Texture* texture = SDL_CreateTextureFromSurface(g_renderer, atlas_surface);
IVector grid = {gridsize->valueint, gridsize->valueint};
if(self == NULL) {
LOG_ERROR("Failed to allocate space for Tileset");
return NULL;
}
_tileset_store(self);
*self = (Tileset) {
.uid = uid->valueint,
.atlas = spritesheet_from_texture(texture, grid),
};
IVector resolution = spritesheet_get_resolution(self->atlas);
self->tiledefs_len = resolution.x / grid.x * resolution.y / grid.y;
self->tiledefs = malloc(self->tiledefs_len * sizeof(TileDef));
LOG_INFO("tiledefs_len: %zu", self->tiledefs_len);
LOG_INFO("resolution: %d, %d", resolution.x, resolution.y);
LOG_INFO("grid: %d, %d", grid.x, grid.y);
for(size_t tid = 0; tid < self->tiledefs_len; ++tid) {
self->tiledefs[tid] = (TileDef) {
.tid = tid,
.sprite = sprite_from_spritesheet(self->atlas, tid)
};
// TODO: generate/read collision information
}
SDL_FreeSurface(atlas_surface);
return self;
}
Tileset* tileset_load(size_t uid) {
for(TilesetStore* store = _tilesets; store < _tilesets + _tilesets_len; ++store) {
if(store->iid == uid) {
return store->set;
}
}
cJSON* defs = cJSON_GetObjectItem(g_levels_json, "defs");
if(defs == NULL) {
LOG_ERROR("Failed to find defs element in levels json");
return NULL;
}
cJSON* tilesets = cJSON_GetObjectItem(defs, "tilesets");
if(tilesets == NULL) {
LOG_ERROR("Failed to find tilesets def region in levels json");
return NULL;
}
cJSON* tileset = NULL;
cJSON_ArrayForEach(tileset, tilesets) {
cJSON* tileset_uid = cJSON_GetObjectItem(tileset, "uid");
if(tileset_uid == NULL) continue;
LOG_INFO("finding uid: %d", tileset_uid->valueint);
if(uid == tileset_uid->valueint) {
break;
}
}
if(tileset == NULL) {
LOG_ERROR("Failed to find a tileset with the uid matching %zu", uid);
return NULL;
}
return tileset_from_json(tileset);
}
void tileset_destroy(Tileset* self) {
_tileset_deallocate(self);
}
TileDef* tileset_get_tiledef(Tileset* self, size_t t) {
return &self->tiledefs[t];
}
Sprite* tiledef_get_sprite(const TileDef* self) {
return self->sprite;
}

17
src/tileset.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef _fencer_tileset_h
#define _fencer_tileset_h
#include "cjson/cJSON.h"
#include "sprite.h"
typedef struct TileDef TileDef;
typedef struct Tileset Tileset;
extern Tileset* tileset_from_json(cJSON* json);
extern Tileset* tileset_load(size_t uid);
extern TileDef* tileset_get_tiledef(Tileset* self, size_t t);
extern void tileset_destroy(Tileset* self);
extern Sprite* tiledef_get_sprite(const TileDef* self);
#endif // !_fencer_tileset_h

View file

@ -12,12 +12,17 @@ struct Transform {
#define IdentityTransform (Transform){ZeroVector, OneVector, 0.0f}
static inline Transform transform_apply(Transform parent, Transform child);
static inline Transform transform_invert(Transform a);
static inline Vector transform_direction(Transform* self, Vector direction);
static inline Vector transform_point(Transform* self, Vector point);
static inline
Transform transform_apply(Transform a, Transform b) {
Transform transform_apply(Transform parent, Transform child) {
return (Transform) {
.position = vaddf(a.position, b.position),
.scale = vmulf(a.scale, b.scale),
.rotation = a.rotation + b.rotation
.position = transform_point(&parent, child.position),
.scale = vmulf(parent.scale, child.scale),
.rotation = parent.rotation + child.rotation
};
}