2041 lines
80 KiB
C++
2041 lines
80 KiB
C++
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <SDL3/SDL.h>
|
|
#include <imgui.h>
|
|
#include <imgui_internal.h>
|
|
#include <imgui_impl_sdl3.h>
|
|
#include <imgui_impl_sdlgpu3.h>
|
|
#include <tracy/Tracy.hpp>
|
|
#include <tracy/TracyC.h>
|
|
|
|
#include "defer.h"
|
|
#include "log.h"
|
|
#include "math_graphics.h"
|
|
#include "stb_image.h"
|
|
|
|
#include "../assets/shader/basic.h"
|
|
#include "../assets/shader/world.h"
|
|
#include "../assets/shader/gui_tile.h"
|
|
#include "../assets/shader/grid.h"
|
|
|
|
using namespace M;
|
|
|
|
#define ASSETS_PATH "../assets/"
|
|
|
|
#define NEAR_PLANE (0.01f)
|
|
#define TILE_SIZE (32)
|
|
|
|
static SDL_GPUDevice *device;
|
|
static SDL_Window *window;
|
|
|
|
static SDL_GPUGraphicsPipeline *basic_graphics_pipeline;
|
|
static SDL_GPUGraphicsPipeline *world_graphics_pipeline;
|
|
static SDL_GPUGraphicsPipeline *grid_graphics_pipeline;
|
|
static SDL_GPUGraphicsPipeline *gui_tile_graphics_pipeline;
|
|
static SDL_GPUSampler *pixel_sampler;
|
|
|
|
SDL_GPUTexture *tile_textures_array;
|
|
|
|
static SDL_GPUBuffer *vertex_buffer;
|
|
static SDL_GPUBuffer *index_buffer;
|
|
static SDL_GPUBuffer *grid_vertex_buffer;
|
|
static SDL_GPUBuffer *grid_index_buffer;
|
|
static SDL_GPUBuffer *player_instance_buffer;
|
|
static SDL_GPUBuffer *tile_infos_buffer;
|
|
|
|
static Sint32 window_width;
|
|
static Sint32 window_height;
|
|
|
|
static Sint32 msaa_texture_width;
|
|
static Sint32 msaa_texture_height;
|
|
static SDL_GPUTexture *msaa_texture;
|
|
|
|
static bool Running = true;
|
|
|
|
static float camera_fovy_degrees = 31.0f;
|
|
static float camera_tilt = 25.5f;
|
|
static float camera_distance = 13.5f;
|
|
|
|
static M4x4 view_matrix = view (V3_(0.0f, 0.0f, 0.0f), radians(camera_tilt), camera_distance);
|
|
static M4x4 inverse_view_matrix = inverse_view(V3_(0.0f, 0.0f, 0.0f), radians(camera_tilt), camera_distance);
|
|
|
|
static M4x4 projection_matrix = projection (radians(camera_fovy_degrees), 16.0f / 9.0f, NEAR_PLANE);
|
|
static M4x4 inverse_projection_matrix = inverse_projection(radians(camera_fovy_degrees), 16.0f / 9.0f, NEAR_PLANE);
|
|
|
|
static SDL_Time time;
|
|
|
|
static bool use_actual_time = true;
|
|
static SDL_DateTime calendar_time;
|
|
|
|
static V2 mouse_pos;
|
|
|
|
#define MAX_TINT_TIMES 32
|
|
static int num_used_tint_times = 4;
|
|
static int time_tints_times[MAX_TINT_TIMES][3] = {
|
|
{ 4, 0, 0 },
|
|
{ 9, 0, 0 },
|
|
{ 19, 0, 0 },
|
|
{ 21, 0, 0 },
|
|
};
|
|
|
|
static V3 time_tints[MAX_TINT_TIMES] = {
|
|
V3_(0.314f, 0.369f, 0.455f),
|
|
V3_(1.0f, 0.891f, 0.868f),
|
|
V3_(1.0f, 0.465f, 0.373f),
|
|
V3_(0.314f, 0.369f, 0.455f),
|
|
};
|
|
|
|
struct Vertex {
|
|
V3 pos;
|
|
};
|
|
|
|
static Vertex vertices[] = {
|
|
{{ -0.5f, 0.5f }},
|
|
{{ 0.5f, 0.5f }},
|
|
{{ 0.5f, -0.5f }},
|
|
{{ -0.5f, -0.5f }},
|
|
};
|
|
|
|
static Uint16 indices[] = {
|
|
0, 1, 2,
|
|
0, 2, 3,
|
|
};
|
|
|
|
#define grid_line_size (1.0f / 64.0f)
|
|
static Vertex grid_vertices[] = {
|
|
// LEFT
|
|
{{ -0.5f, 0.5f }},
|
|
{{ -0.5f + grid_line_size, 0.5f }},
|
|
{{ -0.5f + grid_line_size, -0.5f + grid_line_size }},
|
|
{{ -0.5f, -0.5f + grid_line_size }},
|
|
|
|
// TOP
|
|
{{ -0.5f + grid_line_size, 0.5f }},
|
|
{{ 0.5f, 0.5f }},
|
|
{{ 0.5f, 0.5f - grid_line_size }},
|
|
{{ -0.5f + grid_line_size, 0.5f - grid_line_size }},
|
|
|
|
// RIGHT
|
|
{{ 0.5f - grid_line_size, 0.5f - grid_line_size }},
|
|
{{ 0.5f, 0.5f - grid_line_size }},
|
|
{{ 0.5f, -0.5f }},
|
|
{{ 0.5f - grid_line_size, -0.5f }},
|
|
|
|
// BOTTOM
|
|
{{ -0.5f, -0.5f + grid_line_size }},
|
|
{{ 0.5f - grid_line_size, -0.5f + grid_line_size }},
|
|
{{ 0.5f - grid_line_size, -0.5f }},
|
|
{{ -0.5f, -0.5f }},
|
|
};
|
|
|
|
static Uint16 grid_indices[] = {
|
|
// LEFT
|
|
0, 1, 2,
|
|
0, 2, 3,
|
|
|
|
// TOP
|
|
4, 5, 6,
|
|
4, 6, 7,
|
|
|
|
// RIGHT
|
|
8, 9, 10,
|
|
8, 10, 11,
|
|
|
|
// BOTTOM
|
|
12, 13, 14,
|
|
12, 14, 15,
|
|
};
|
|
|
|
struct Instance {
|
|
V2 pos;
|
|
V4 uv0uv1;
|
|
V4 uv2uv3;
|
|
};
|
|
|
|
static Instance player_instance = { { 0.0f, 0.0f }, { 0, 0, 1, 0 }, { 1, 1, 0, 1 }};
|
|
|
|
struct Map {
|
|
Sint32 width;
|
|
Sint32 height;
|
|
|
|
Uint32 *tiles;
|
|
|
|
char *name;
|
|
SDL_GPUBuffer *gpu_buffer;
|
|
};
|
|
|
|
static Map current_map;
|
|
|
|
struct Player {
|
|
Sint32 pos_x;
|
|
Sint32 pos_y;
|
|
};
|
|
|
|
static Player player;
|
|
|
|
struct PerFrame {
|
|
Sint32 drag_start[2];
|
|
Sint32 mouse[2];
|
|
Sint32 map_width;
|
|
};
|
|
|
|
static PerFrame per_frame = {};
|
|
|
|
typedef struct {
|
|
Uint16 type;
|
|
const char *asset_path;
|
|
|
|
V2 uv_min;
|
|
V2 uv_max;
|
|
} TileInfo;
|
|
|
|
static TileInfo tile_infos[] = {
|
|
{ 0x0001, "tiles/error.png" },
|
|
{ 0x0000, "tiles/empty.png" },
|
|
{ 0x0100, "tiles/grass_1.png" },
|
|
{ 0x0101, "tiles/grass_2.png" },
|
|
{ 0x0102, "tiles/grass_3.png" },
|
|
{ 0x0103, "tiles/grass_4.png" },
|
|
{ 0x0200, "tiles/ground_1.png" },
|
|
{ 0x0201, "tiles/ground_2.png" },
|
|
{ 0x0202, "tiles/ground_3.png" },
|
|
{ 0x0300, "tiles/water_1.png" },
|
|
{ 0x0301, "tiles/water_2.png" },
|
|
{ 0x0400, "tiles/grass_ground_1.png" },
|
|
{ 0x0401, "tiles/grass_ground_2.png" },
|
|
{ 0x0402, "tiles/grass_ground_3.png" },
|
|
{ 0x0410, "tiles/grass_ground_outer_corner.png" },
|
|
{ 0x0411, "tiles/grass_ground_outer_corner_2.png" },
|
|
{ 0x0420, "tiles/grass_ground_inner_corner.png" },
|
|
{ 0x0421, "tiles/grass_ground_inner_corner_2.png" },
|
|
{ 0x0422, "tiles/grass_ground_inner_corner_3.png" },
|
|
};
|
|
|
|
static V4 cpu_tile_infos_buffer[SDL_arraysize(tile_infos)];
|
|
|
|
static Sint32 selected_tile = -1;
|
|
static Sint32 selected_rotation = 0;
|
|
|
|
static bool dragging_tile_change = false;
|
|
static Sint32 drag_start_pos[2];
|
|
|
|
static bool update_buffer(SDL_GPUBuffer *buffer, Uint32 offset, Uint32 num_bytes, void *data) {
|
|
SDL_GPUTransferBufferCreateInfo transfer_buffer_info = {
|
|
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
|
|
.size = num_bytes,
|
|
};
|
|
|
|
SDL_GPUTransferBuffer *transfer_buffer = SDL_CreateGPUTransferBuffer(device, &transfer_buffer_info);
|
|
if (!transfer_buffer) {
|
|
log_error("Failed to create gpu transfer buffer (%s).", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
void *transfer_data = SDL_MapGPUTransferBuffer(device, transfer_buffer, true);
|
|
if (!transfer_data) {
|
|
log_error("Failed to map gpu transfer buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
|
|
return false;
|
|
}
|
|
memcpy(transfer_data, data, num_bytes);
|
|
SDL_UnmapGPUTransferBuffer(device, transfer_buffer);
|
|
|
|
SDL_GPUCommandBuffer *command_buffer = SDL_AcquireGPUCommandBuffer(device);
|
|
if (!command_buffer) {
|
|
log_error("Failed to acquire gpu command buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
|
|
return false;
|
|
}
|
|
|
|
SDL_GPUTransferBufferLocation transfer_info = {
|
|
.transfer_buffer = transfer_buffer,
|
|
.offset = 0,
|
|
};
|
|
|
|
SDL_GPUBufferRegion buffer_region = {
|
|
.buffer = buffer,
|
|
.offset = offset,
|
|
.size = num_bytes,
|
|
};
|
|
|
|
SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(command_buffer);
|
|
SDL_UploadToGPUBuffer(copy_pass, &transfer_info, &buffer_region, true);
|
|
SDL_EndGPUCopyPass(copy_pass);
|
|
|
|
if (!SDL_SubmitGPUCommandBuffer(command_buffer)) {
|
|
log_error("Failed to submit gpu command buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
|
|
SDL_CancelGPUCommandBuffer(command_buffer);
|
|
return false;
|
|
}
|
|
|
|
SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
|
|
return true;
|
|
}
|
|
|
|
static SDL_GPUBuffer *create_buffer(SDL_GPUBufferUsageFlags usage, Uint32 num_bytes, void *data = NULL, const char *name = NULL) {
|
|
SDL_PropertiesID properties = 0;
|
|
if (name) {
|
|
properties = SDL_CreateProperties();
|
|
if (properties) SDL_SetStringProperty(properties, SDL_PROP_GPU_BUFFER_CREATE_NAME_STRING, name);
|
|
}
|
|
|
|
SDL_GPUBufferCreateInfo buffer_info = {
|
|
.usage = usage,
|
|
.size = num_bytes,
|
|
|
|
.props = properties,
|
|
};
|
|
|
|
SDL_GPUBuffer *buffer = SDL_CreateGPUBuffer(device, &buffer_info);
|
|
if (properties) SDL_DestroyProperties(properties);
|
|
|
|
if (data) {
|
|
if (!update_buffer(buffer, 0, num_bytes, data)) {
|
|
log_error("Failed to update buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUBuffer(device, buffer);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static bool save_map(Map map) {
|
|
char path[256] = ASSETS_PATH "maps/";
|
|
SDL_strlcat(path, map.name, SDL_arraysize(path));
|
|
|
|
SDL_IOStream *file = SDL_IOFromFile(path, "wb");
|
|
if (!file) {
|
|
log_error("Failed to open map file for writing.");
|
|
return false;
|
|
}
|
|
defer(SDL_CloseIO(file));
|
|
|
|
if (!SDL_WriteS32LE(file, map.width)) {
|
|
log_error("Failed to write width to map file.");
|
|
return false;
|
|
}
|
|
|
|
if (!SDL_WriteS32LE(file, map.height)) {
|
|
log_error("Failed to write height to map file.");
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < map.width * map.height; i++) {
|
|
Uint32 type = tile_infos[map.tiles[i] & 0xffff].type;
|
|
Uint32 orientation = map.tiles[i] & 0x30000;
|
|
|
|
Uint32 to_write = orientation | type;
|
|
if (!SDL_WriteU32LE(file, to_write)) {
|
|
log_error("Failed to write tile to map file.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(!SDL_FlushIO(file)) {
|
|
log_error("Failed to flush data to map file.");
|
|
return false;
|
|
};
|
|
|
|
log("Saved map file.");
|
|
return true;
|
|
}
|
|
|
|
static bool load_map(const char *name, Map *result) {
|
|
char path[256] = ASSETS_PATH "maps/";
|
|
SDL_strlcat(path, name, SDL_arraysize(path));
|
|
|
|
SDL_IOStream *file = SDL_IOFromFile(path, "rb");
|
|
if (!file) {
|
|
log_error("Failed to open map file for reading.");
|
|
return false;
|
|
}
|
|
defer(SDL_CloseIO(file));
|
|
result->name = SDL_strdup(name);
|
|
|
|
if (!SDL_ReadS32LE(file, &result->width)) {
|
|
log_error("Failed read width from map file.");
|
|
return false;
|
|
}
|
|
|
|
if (!SDL_ReadS32LE(file, &result->height)) {
|
|
log_error("Failed read height from map file.");
|
|
return false;
|
|
}
|
|
|
|
result->tiles = (Uint32*)malloc(result->width * result->height * sizeof(Uint32));
|
|
|
|
for (int i = 0; i < result->width * result->height; i++) {
|
|
Uint32 tile = 0;
|
|
if (!SDL_ReadU32LE(file, &tile)) {
|
|
free(result->tiles);
|
|
return false;
|
|
}
|
|
|
|
Uint32 type = tile & 0xffff;
|
|
Uint32 orientation = tile & 0x30000;
|
|
|
|
Uint32 kind = 0;
|
|
for (int i = 0; i < SDL_arraysize(tile_infos); i++) {
|
|
if (tile_infos[i].type == type) {
|
|
kind = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
result->tiles[i] = orientation | kind;
|
|
}
|
|
|
|
char buffer_name[256] = "Map ";
|
|
SDL_strlcat(buffer_name, result->name, SDL_arraysize(buffer_name));
|
|
|
|
result->gpu_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_VERTEX, result->width * result->height * 4, result->tiles, buffer_name);
|
|
if (!result->gpu_buffer) {
|
|
log_error("Failed to create buffer. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
log("Loaded map file.");
|
|
return true;
|
|
}
|
|
|
|
|
|
static void unload_map(Map *map) {
|
|
map->width = 0;
|
|
map->height = 0;
|
|
free(map->tiles);
|
|
SDL_free(map->name);
|
|
SDL_ReleaseGPUBuffer(device, map->gpu_buffer);
|
|
}
|
|
|
|
|
|
static void change_map_size(Map *map, char direction, int amount) {
|
|
SDL_GPUBuffer *old_gpu_buffer = map->gpu_buffer;
|
|
Uint32 *old_map = map->tiles;
|
|
Sint32 old_map_width = map->width;
|
|
Sint32 old_map_height = map->height;
|
|
|
|
Sint32 new_x_offset = 0;
|
|
Sint32 new_y_offset = 0;
|
|
Sint32 old_x_offset = 0;
|
|
Sint32 old_y_offset = 0;
|
|
|
|
Sint32 to_fill_width = map->width;
|
|
Sint32 to_fill_height = map->height;
|
|
Sint32 to_fill_x_offset = 0;
|
|
Sint32 to_fill_y_offset = 0;
|
|
|
|
if (direction == 'W') {
|
|
player.pos_x = player.pos_x + amount;
|
|
map->width += amount;
|
|
to_fill_width = amount;
|
|
|
|
if (amount < 0)
|
|
old_x_offset = -amount;
|
|
else
|
|
new_x_offset = amount;
|
|
}
|
|
|
|
if (direction == 'N') {
|
|
player.pos_y = player.pos_y + amount;
|
|
map->height += amount;
|
|
to_fill_height = amount;
|
|
|
|
if (amount < 0)
|
|
old_y_offset = -amount;
|
|
else
|
|
new_y_offset = amount;
|
|
}
|
|
|
|
if (direction == 'E') {
|
|
map->width += amount;
|
|
to_fill_width = amount;
|
|
to_fill_x_offset = old_map_width;
|
|
}
|
|
|
|
if (direction == 'S') {
|
|
map->height += amount;
|
|
to_fill_height = amount;
|
|
to_fill_y_offset = old_map_height;
|
|
}
|
|
|
|
map->tiles = (Uint32*)malloc(map->width * map->height * sizeof(Uint32));
|
|
|
|
for (int y = 0; y < min(old_map_height, map->height); y++) {
|
|
for (int x = 0; x < min(old_map_width, map->width); x++) {
|
|
map->tiles[(y + new_y_offset) * map->width + (x + new_x_offset)] = old_map[(y + old_y_offset) * old_map_width + (x + old_x_offset)];
|
|
}
|
|
}
|
|
|
|
for (int y = 0; y < to_fill_height; y++) {
|
|
for (int x = 0; x < to_fill_width; x++) {
|
|
map->tiles[(y + to_fill_y_offset) * map->width + (x + to_fill_x_offset)] = 1;
|
|
}
|
|
}
|
|
|
|
player.pos_x = clamp(0, player.pos_x, map->width - 1);
|
|
player.pos_y = clamp(0, player.pos_y, map->height - 1);
|
|
|
|
map->gpu_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_VERTEX, map->width * map->height * 4, map->tiles, "world_buffer");
|
|
if (!map->gpu_buffer) {
|
|
log_error("Failed to create buffer. Exiting.");
|
|
exit(1);
|
|
}
|
|
|
|
free(old_map);
|
|
SDL_ReleaseGPUBuffer(device, old_gpu_buffer);
|
|
}
|
|
|
|
static SDL_GPUTexture *create_shader_texture(const char *name, const char *data, int width, int height, int channels) {
|
|
SDL_GPUTextureFormat format = SDL_GPU_TEXTUREFORMAT_INVALID;
|
|
if (channels == 1) format = SDL_GPU_TEXTUREFORMAT_A8_UNORM;
|
|
if (channels == 4) format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB;
|
|
|
|
if (format == SDL_GPU_TEXTUREFORMAT_INVALID) {
|
|
log_error("Failed to find texture format for texture.");
|
|
return NULL;
|
|
}
|
|
|
|
SDL_PropertiesID properties = SDL_CreateProperties();
|
|
if (properties) SDL_SetStringProperty(properties, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING, name);
|
|
|
|
SDL_GPUTextureCreateInfo texture_info = {
|
|
.type = SDL_GPU_TEXTURETYPE_2D,
|
|
.format = format,
|
|
.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER,
|
|
.width = (Uint32)width,
|
|
.height = (Uint32)height,
|
|
|
|
.layer_count_or_depth = 1,
|
|
.num_levels = 1,
|
|
|
|
.props = properties,
|
|
};
|
|
|
|
SDL_GPUTexture *texture = SDL_CreateGPUTexture(device, &texture_info);
|
|
if (!texture) {
|
|
log_error("Failed to create gpu texture (%s).", SDL_GetError());
|
|
return NULL;
|
|
}
|
|
if (properties) SDL_DestroyProperties(properties);
|
|
|
|
if (data) {
|
|
Uint32 upload_size = width * height * channels;
|
|
SDL_GPUTransferBufferCreateInfo transfer_buffer_info = {
|
|
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
|
|
.size = upload_size,
|
|
};
|
|
|
|
SDL_GPUTransferBuffer *transfer_buffer = SDL_CreateGPUTransferBuffer(device, &transfer_buffer_info);
|
|
if (!transfer_buffer) {
|
|
log_error("Failed to create gpu transfer buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTexture(device, texture);
|
|
return NULL;
|
|
}
|
|
|
|
void *transfer_data = SDL_MapGPUTransferBuffer(device, transfer_buffer, false);
|
|
if (!transfer_data) {
|
|
log_error("Failed to map gpu transfer buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
|
|
SDL_ReleaseGPUTexture(device, texture);
|
|
return NULL;
|
|
}
|
|
memcpy(transfer_data, data, upload_size);
|
|
SDL_UnmapGPUTransferBuffer(device, transfer_buffer);
|
|
|
|
SDL_GPUTextureTransferInfo transfer_info = {
|
|
.transfer_buffer = transfer_buffer,
|
|
.pixels_per_row = (Uint32)width,
|
|
};
|
|
|
|
SDL_GPUTextureRegion tetxure_region = {
|
|
.texture = texture,
|
|
.w = (Uint32)width,
|
|
.h = (Uint32)height,
|
|
.d = 1,
|
|
};
|
|
|
|
SDL_GPUCommandBuffer *command_buffer = SDL_AcquireGPUCommandBuffer(device);
|
|
if (!command_buffer) {
|
|
log_error("Failed to acquire gpu command buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
|
|
SDL_ReleaseGPUTexture(device, texture);
|
|
return NULL;
|
|
}
|
|
|
|
SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(command_buffer);
|
|
SDL_UploadToGPUTexture(copy_pass, &transfer_info, &tetxure_region, false);
|
|
SDL_EndGPUCopyPass(copy_pass);
|
|
|
|
if (!SDL_SubmitGPUCommandBuffer(command_buffer)) {
|
|
log_error("Failed to submit gpu command buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
|
|
SDL_ReleaseGPUTexture(device, texture);
|
|
SDL_CancelGPUCommandBuffer(command_buffer);
|
|
return NULL;
|
|
}
|
|
|
|
SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
|
|
}
|
|
|
|
return texture;
|
|
}
|
|
|
|
static SDL_GPUTexture *create_shader_texture(const char *path) {
|
|
char path_to_load[256] = ASSETS_PATH;
|
|
SDL_strlcat(path_to_load, path, SDL_arraysize(path_to_load));
|
|
|
|
int width = 0, height = 0, channels = 0;
|
|
stbi_uc *data = stbi_load(path_to_load, &width, &height, &channels, 0);
|
|
if (!data) {
|
|
log_error("Failed to load texture (\"%s\").", path_to_load);
|
|
return NULL;
|
|
}
|
|
|
|
SDL_GPUTexture *result = create_shader_texture(path, (char *)data, width, height, channels);
|
|
if (!result) {
|
|
log_error("Failed to load texture (\"%s\").", path_to_load);
|
|
stbi_image_free(data);
|
|
return NULL;
|
|
}
|
|
stbi_image_free(data);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void blit(char *dst, Sint32 dst_pitch, Sint32 dst_x, Sint32 dst_y, char *src, Sint32 src_pitch, Sint32 width, Sint32 height, int components = 4) {
|
|
for (Sint32 y = 0; y < height; y++)
|
|
memmove(&dst[((dst_y + y) * dst_pitch + dst_x) * components], &src[y * src_pitch * components], width * components);
|
|
}
|
|
|
|
static void imgui_tiles_draw_callback(const ImDrawList *parent_list, const ImDrawCmd *cmd) {
|
|
ImGui_ImplSDLGPU3_RenderState *render_state = (ImGui_ImplSDLGPU3_RenderState *)ImGui::GetPlatformIO().Renderer_RenderState;
|
|
SDL_BindGPUGraphicsPipeline(render_state->render_pass, gui_tile_graphics_pipeline);
|
|
|
|
Uint32 tile_index = *(Uint32 *)cmd->UserCallbackData;
|
|
SDL_PushGPUFragmentUniformData(render_state->command_buffer, 0, &tile_index, sizeof(Uint32));
|
|
}
|
|
|
|
static bool SelectableTile(const char *label, bool selected, SDL_GPUTextureSamplerBinding *image, Uint32 tile_index, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), Uint8 orientation = 0) {
|
|
const ImGuiContext *context = ImGui::GetCurrentContext();
|
|
const ImVec2 padding = context->Style.FramePadding;
|
|
|
|
bool pressed = ImGui::Selectable(label, selected, 0, image_size + padding * 2.0f);
|
|
|
|
ImVec2 min = ImGui::GetItemRectMin();
|
|
ImVec2 max = ImGui::GetItemRectMax();
|
|
|
|
if (image) {
|
|
context->CurrentWindow->DrawList->AddCallback(imgui_tiles_draw_callback, (void *)&tile_index, sizeof(Uint32));
|
|
switch (orientation) {
|
|
case 0: context->CurrentWindow->DrawList->AddImageQuad((ImTextureID)image, min + padding, ImVec2(max.x - padding.x, min.y + padding.y), max - padding, ImVec2(min.x + padding.x, max.y - padding.y), ImVec2(uv0.x, uv0.y), ImVec2(uv1.x, uv0.y), ImVec2(uv1.x, uv1.y), ImVec2(uv0.x, uv1.y)); break;
|
|
case 1: context->CurrentWindow->DrawList->AddImageQuad((ImTextureID)image, min + padding, ImVec2(max.x - padding.x, min.y + padding.y), max - padding, ImVec2(min.x + padding.x, max.y - padding.y), ImVec2(uv1.x, uv0.y), ImVec2(uv1.x, uv1.y), ImVec2(uv0.x, uv1.y), ImVec2(uv0.x, uv0.y)); break;
|
|
case 2: context->CurrentWindow->DrawList->AddImageQuad((ImTextureID)image, min + padding, ImVec2(max.x - padding.x, min.y + padding.y), max - padding, ImVec2(min.x + padding.x, max.y - padding.y), ImVec2(uv1.x, uv1.y), ImVec2(uv0.x, uv1.y), ImVec2(uv0.x, uv0.y), ImVec2(uv1.x, uv0.y)); break;
|
|
case 3: context->CurrentWindow->DrawList->AddImageQuad((ImTextureID)image, min + padding, ImVec2(max.x - padding.x, min.y + padding.y), max - padding, ImVec2(min.x + padding.x, max.y - padding.y), ImVec2(uv0.x, uv1.y), ImVec2(uv0.x, uv0.y), ImVec2(uv1.x, uv0.y), ImVec2(uv1.x, uv1.y)); break;
|
|
default: SDL_assert_always(false); break;
|
|
}
|
|
context->CurrentWindow->DrawList->AddCallback(ImDrawCallback_ResetRenderState, NULL);
|
|
}
|
|
|
|
return pressed;
|
|
}
|
|
|
|
static ImVec4 linear_to_sRGB(ImVec4 linear) {
|
|
float red = linear.x <= 0.0031308f ? 12.92f * linear.x : 1.055f * powf(linear.x, 1.0f / 2.4f) - 0.055;
|
|
float green = linear.y <= 0.0031308f ? 12.92f * linear.y : 1.055f * powf(linear.y, 1.0f / 2.4f) - 0.055;
|
|
float blue = linear.z <= 0.0031308f ? 12.92f * linear.z : 1.055f * powf(linear.z, 1.0f / 2.4f) - 0.055;
|
|
|
|
return ImVec4(red, green, blue, linear.w);
|
|
}
|
|
|
|
static ImVec4 sRGB_to_linear(ImVec4 linear) {
|
|
float red = linear.x <= 0.0031308f ? linear.x / 12.92f : powf((linear.x + 0.055) / 1.055, 2.4f);
|
|
float green = linear.y <= 0.0031308f ? linear.y / 12.92f : powf((linear.y + 0.055) / 1.055, 2.4f);
|
|
float blue = linear.z <= 0.0031308f ? linear.z / 12.92f : powf((linear.z + 0.055) / 1.055, 2.4f);
|
|
|
|
return ImVec4(red, green, blue, linear.w);
|
|
}
|
|
|
|
static V3 Unproject(V3 screen_pos) {
|
|
V4 result = inverse_view_matrix * inverse_projection_matrix * V4_(screen_pos, 1.0f);
|
|
result.xyz /= result.w;
|
|
|
|
return result.xyz;
|
|
}
|
|
|
|
static V2 get_floor_intersection_of_mouse(V2 mouse_pos) {
|
|
V2 mouse = remap(V2{ 0, 0 }, V2{ (float)window_width, (float)window_height }, V2{ -1, 1 }, V2{ 1, -1 }, V2{ mouse_pos.x, mouse_pos.y });
|
|
V3 camera_position = (inverse_view_matrix * V4_(0, 0, 0, 1)).xyz;
|
|
|
|
V3 probe = Unproject(V3_(mouse, .5));
|
|
V3 ray_dir = normalize(probe - camera_position);
|
|
|
|
float t = -camera_position.z / ray_dir.z;
|
|
V3 floor_intersection = camera_position + (t * ray_dir);
|
|
|
|
floor_intersection.xy += V2_(0.5f, 0.5f);
|
|
|
|
return floor_intersection.xy;
|
|
}
|
|
|
|
#ifdef TRACY_ENABLE
|
|
static SDL_malloc_func sdl_malloc = NULL;
|
|
static SDL_calloc_func sdl_calloc = NULL;
|
|
static SDL_realloc_func sdl_realloc = NULL;
|
|
static SDL_free_func sdl_free = NULL;
|
|
|
|
static void setup_memory_functions() {
|
|
SDL_GetMemoryFunctions(&sdl_malloc, &sdl_calloc, &sdl_realloc, &sdl_free);
|
|
SDL_SetMemoryFunctions(
|
|
[](size_t size) -> void * {
|
|
void *result = sdl_malloc(size);
|
|
TracyAllocN(result, size, "SDL");
|
|
return result;
|
|
},
|
|
[](size_t nmemb, size_t size) -> void * {
|
|
void *result = sdl_calloc(nmemb, size);
|
|
TracyAllocN(result, nmemb * size, "SDL");
|
|
return result;
|
|
},
|
|
[](void *mem, size_t size) -> void * {
|
|
void *result = sdl_realloc(mem, size);
|
|
TracyFreeN(mem, "SDL");
|
|
TracyAllocN(result, size, "SDL");
|
|
return result;
|
|
},
|
|
[](void *mem) {
|
|
TracyFreeN(mem, "SDL");
|
|
sdl_free(mem);
|
|
}
|
|
);
|
|
}
|
|
#else
|
|
static void setup_memory_functions() {}
|
|
#endif
|
|
|
|
static bool enable_msaa = true;
|
|
static SDL_GPUSampleCount highest_supported_sample_count = SDL_GPU_SAMPLECOUNT_1;
|
|
|
|
static bool recreate_graphics_pipelines() {
|
|
SDL_GPUColorTargetDescription color_target_descriptions[] = {
|
|
{
|
|
.format = SDL_GetGPUSwapchainTextureFormat(device, window),
|
|
.blend_state = {
|
|
.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA,
|
|
.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
|
|
.color_blend_op = SDL_GPU_BLENDOP_ADD,
|
|
|
|
.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE,
|
|
.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
|
|
.alpha_blend_op = SDL_GPU_BLENDOP_ADD,
|
|
|
|
.enable_blend = true,
|
|
},
|
|
},
|
|
};
|
|
|
|
{ // basic_graphics_pipeline
|
|
SDL_GPUShaderCreateInfo basic_vertex_shader_info = {
|
|
.code_size = SDL_arraysize(SPIRV_basic),
|
|
.code = SPIRV_basic,
|
|
.entrypoint = "main_vertex",
|
|
.format = SDL_GPU_SHADERFORMAT_SPIRV,
|
|
.stage = SDL_GPU_SHADERSTAGE_VERTEX,
|
|
|
|
.num_uniform_buffers = 2,
|
|
};
|
|
|
|
SDL_GPUShader *basic_vertex_shader = SDL_CreateGPUShader(device, &basic_vertex_shader_info);
|
|
if (!basic_vertex_shader) {
|
|
log_error("Failed to create basic vertex shader. Exiting.");
|
|
return false;
|
|
}
|
|
|
|
SDL_GPUShaderCreateInfo basic_pixel_shader_info = {
|
|
.code_size = SDL_arraysize(SPIRV_basic),
|
|
.code = SPIRV_basic,
|
|
.entrypoint = "main_fragment",
|
|
.format = SDL_GPU_SHADERFORMAT_SPIRV,
|
|
.stage = SDL_GPU_SHADERSTAGE_FRAGMENT,
|
|
|
|
.num_samplers = 1,
|
|
.num_uniform_buffers = 1,
|
|
};
|
|
|
|
SDL_GPUShader *basic_pixel_shader = SDL_CreateGPUShader(device, &basic_pixel_shader_info);
|
|
if (!basic_pixel_shader) {
|
|
log_error("Failed to create basic pixel shader. Exiting.");
|
|
return false;
|
|
}
|
|
|
|
SDL_GPUVertexBufferDescription vertex_buffer_descriptions[] = {
|
|
{
|
|
.slot = 0,
|
|
.pitch = sizeof(Vertex),
|
|
.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,
|
|
},
|
|
{
|
|
.slot = 1,
|
|
.pitch = sizeof(Instance),
|
|
.input_rate = SDL_GPU_VERTEXINPUTRATE_INSTANCE,
|
|
.instance_step_rate = 1,
|
|
},
|
|
};
|
|
|
|
SDL_GPUVertexAttribute vertex_attributes[] = {
|
|
{
|
|
.location = 0,
|
|
.buffer_slot = 0,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3,
|
|
.offset = offsetof(Vertex, pos),
|
|
},
|
|
{
|
|
.location = 1,
|
|
.buffer_slot = 1,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
|
|
.offset = offsetof(Instance, pos),
|
|
},
|
|
{
|
|
.location = 2,
|
|
.buffer_slot = 1,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4,
|
|
.offset = offsetof(Instance, uv0uv1),
|
|
},
|
|
{
|
|
.location = 3,
|
|
.buffer_slot = 1,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4,
|
|
.offset = offsetof(Instance, uv2uv3),
|
|
},
|
|
};
|
|
|
|
SDL_GPUGraphicsPipelineCreateInfo basic_graphics_pipeline_info = {
|
|
.vertex_shader = basic_vertex_shader,
|
|
.fragment_shader = basic_pixel_shader,
|
|
.vertex_input_state = {
|
|
.vertex_buffer_descriptions = vertex_buffer_descriptions,
|
|
.num_vertex_buffers = SDL_arraysize(vertex_buffer_descriptions),
|
|
.vertex_attributes = vertex_attributes,
|
|
.num_vertex_attributes = SDL_arraysize(vertex_attributes),
|
|
},
|
|
.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
|
|
.rasterizer_state = {
|
|
.fill_mode = SDL_GPU_FILLMODE_FILL,
|
|
.cull_mode = SDL_GPU_CULLMODE_BACK,
|
|
.front_face = SDL_GPU_FRONTFACE_CLOCKWISE,
|
|
.enable_depth_clip = true,
|
|
},
|
|
.multisample_state = {
|
|
.sample_count = enable_msaa ? highest_supported_sample_count : SDL_GPU_SAMPLECOUNT_1,
|
|
},
|
|
.target_info = {
|
|
.color_target_descriptions = color_target_descriptions,
|
|
.num_color_targets = SDL_arraysize(color_target_descriptions),
|
|
},
|
|
};
|
|
|
|
if (basic_graphics_pipeline) {
|
|
SDL_ReleaseGPUGraphicsPipeline(device, basic_graphics_pipeline);
|
|
basic_graphics_pipeline = NULL;
|
|
}
|
|
|
|
basic_graphics_pipeline = SDL_CreateGPUGraphicsPipeline(device, &basic_graphics_pipeline_info);
|
|
if (!basic_graphics_pipeline) {
|
|
log_error("Failed to create basic graphics pipeline. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
SDL_ReleaseGPUShader(device, basic_vertex_shader);
|
|
SDL_ReleaseGPUShader(device, basic_pixel_shader);
|
|
}
|
|
|
|
{ // world_graphics_pipeline
|
|
SDL_GPUShaderCreateInfo world_vertex_shader_info = {
|
|
.code_size = SDL_arraysize(SPIRV_world),
|
|
.code = SPIRV_world,
|
|
.entrypoint = "main_vertex",
|
|
.format = SDL_GPU_SHADERFORMAT_SPIRV,
|
|
.stage = SDL_GPU_SHADERSTAGE_VERTEX,
|
|
|
|
.num_storage_buffers = 1,
|
|
.num_uniform_buffers = 2,
|
|
};
|
|
|
|
SDL_GPUShader *world_vertex_shader = SDL_CreateGPUShader(device, &world_vertex_shader_info);
|
|
if (!world_vertex_shader) {
|
|
log_error("Failed to create world vertex shader. Exiting.");
|
|
return false;
|
|
}
|
|
|
|
SDL_GPUShaderCreateInfo world_pixel_shader_info = {
|
|
.code_size = SDL_arraysize(SPIRV_world),
|
|
.code = SPIRV_world,
|
|
.entrypoint = "main_fragment",
|
|
.format = SDL_GPU_SHADERFORMAT_SPIRV,
|
|
.stage = SDL_GPU_SHADERSTAGE_FRAGMENT,
|
|
|
|
.num_samplers = 1,
|
|
.num_uniform_buffers = 1,
|
|
};
|
|
|
|
SDL_GPUShader *world_pixel_shader = SDL_CreateGPUShader(device, &world_pixel_shader_info);
|
|
if (!world_pixel_shader) {
|
|
log_error("Failed to create world pixel shader. Exiting.");
|
|
return false;
|
|
}
|
|
|
|
SDL_GPUVertexBufferDescription vertex_buffer_descriptions[] = {
|
|
{
|
|
.slot = 0,
|
|
.pitch = sizeof(Vertex),
|
|
.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,
|
|
},
|
|
{
|
|
.slot = 1,
|
|
.pitch = sizeof(Uint32),
|
|
.input_rate = SDL_GPU_VERTEXINPUTRATE_INSTANCE,
|
|
.instance_step_rate = 1,
|
|
},
|
|
};
|
|
|
|
SDL_GPUVertexAttribute vertex_attributes[] = {
|
|
{
|
|
.location = 0,
|
|
.buffer_slot = 0,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3,
|
|
.offset = offsetof(Vertex, pos),
|
|
},
|
|
{
|
|
.location = 1,
|
|
.buffer_slot = 1,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_UINT,
|
|
.offset = 0,
|
|
},
|
|
};
|
|
|
|
SDL_GPUGraphicsPipelineCreateInfo world_graphics_pipeline_info = {
|
|
.vertex_shader = world_vertex_shader,
|
|
.fragment_shader = world_pixel_shader,
|
|
.vertex_input_state = {
|
|
.vertex_buffer_descriptions = vertex_buffer_descriptions,
|
|
.num_vertex_buffers = SDL_arraysize(vertex_buffer_descriptions),
|
|
.vertex_attributes = vertex_attributes,
|
|
.num_vertex_attributes = SDL_arraysize(vertex_attributes),
|
|
},
|
|
.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
|
|
.rasterizer_state = {
|
|
.fill_mode = SDL_GPU_FILLMODE_FILL,
|
|
.cull_mode = SDL_GPU_CULLMODE_BACK,
|
|
.front_face = SDL_GPU_FRONTFACE_CLOCKWISE,
|
|
.enable_depth_clip = true,
|
|
},
|
|
.multisample_state = {
|
|
.sample_count = enable_msaa ? highest_supported_sample_count : SDL_GPU_SAMPLECOUNT_1,
|
|
},
|
|
.target_info = {
|
|
.color_target_descriptions = color_target_descriptions,
|
|
.num_color_targets = SDL_arraysize(color_target_descriptions),
|
|
},
|
|
};
|
|
|
|
if (world_graphics_pipeline) {
|
|
SDL_ReleaseGPUGraphicsPipeline(device, world_graphics_pipeline);
|
|
world_graphics_pipeline = NULL;
|
|
}
|
|
|
|
world_graphics_pipeline = SDL_CreateGPUGraphicsPipeline(device, &world_graphics_pipeline_info);
|
|
if (!world_graphics_pipeline) {
|
|
log_error("Failed to create world graphics pipeline. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
SDL_ReleaseGPUShader(device, world_vertex_shader);
|
|
SDL_ReleaseGPUShader(device, world_pixel_shader);
|
|
}
|
|
|
|
{ // gui_tile_graphics_pipeline
|
|
SDL_GPUShaderCreateInfo gui_tile_vertex_shader_info = {
|
|
.code_size = SDL_arraysize(SPIRV_gui_tile),
|
|
.code = SPIRV_gui_tile,
|
|
.entrypoint = "main_vertex",
|
|
.format = SDL_GPU_SHADERFORMAT_SPIRV,
|
|
.stage = SDL_GPU_SHADERSTAGE_VERTEX,
|
|
|
|
.num_uniform_buffers = 1,
|
|
};
|
|
|
|
SDL_GPUShader *gui_tile_vertex_shader = SDL_CreateGPUShader(device, &gui_tile_vertex_shader_info);
|
|
if (!gui_tile_vertex_shader) {
|
|
log_error("Failed to create gui_tile vertex shader. Exiting.");
|
|
return false;
|
|
}
|
|
|
|
SDL_GPUShaderCreateInfo gui_tile_pixel_shader_info = {
|
|
.code_size = SDL_arraysize(SPIRV_gui_tile),
|
|
.code = SPIRV_gui_tile,
|
|
.entrypoint = "main_fragment",
|
|
.format = SDL_GPU_SHADERFORMAT_SPIRV,
|
|
.stage = SDL_GPU_SHADERSTAGE_FRAGMENT,
|
|
|
|
.num_samplers = 1,
|
|
.num_uniform_buffers = 1,
|
|
};
|
|
|
|
SDL_GPUShader *gui_tile_pixel_shader = SDL_CreateGPUShader(device, &gui_tile_pixel_shader_info);
|
|
if (!gui_tile_pixel_shader) {
|
|
log_error("Failed to create gui_tile pixel shader. Exiting.");
|
|
return false;
|
|
}
|
|
|
|
SDL_GPUVertexBufferDescription vertex_buffer_descriptions[] = {
|
|
{
|
|
.slot = 0,
|
|
.pitch = sizeof(ImDrawVert),
|
|
.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,
|
|
},
|
|
{
|
|
.slot = 1,
|
|
.pitch = sizeof(Uint32),
|
|
.input_rate = SDL_GPU_VERTEXINPUTRATE_INSTANCE,
|
|
.instance_step_rate = 1,
|
|
},
|
|
};
|
|
|
|
SDL_GPUVertexAttribute vertex_attributes[] = {
|
|
{
|
|
.location = 0,
|
|
.buffer_slot = 0,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
|
|
.offset = offsetof(ImDrawVert, pos),
|
|
},
|
|
{
|
|
.location = 1,
|
|
.buffer_slot = 0,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
|
|
.offset = offsetof(ImDrawVert, uv),
|
|
},
|
|
{
|
|
.location = 2,
|
|
.buffer_slot = 0,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM,
|
|
.offset = offsetof(ImDrawVert, col),
|
|
},
|
|
};
|
|
|
|
SDL_GPUGraphicsPipelineCreateInfo gui_tile_graphics_pipeline_info = {
|
|
.vertex_shader = gui_tile_vertex_shader,
|
|
.fragment_shader = gui_tile_pixel_shader,
|
|
.vertex_input_state = {
|
|
.vertex_buffer_descriptions = vertex_buffer_descriptions,
|
|
.num_vertex_buffers = SDL_arraysize(vertex_buffer_descriptions),
|
|
.vertex_attributes = vertex_attributes,
|
|
.num_vertex_attributes = SDL_arraysize(vertex_attributes),
|
|
},
|
|
.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
|
|
.rasterizer_state = {
|
|
.fill_mode = SDL_GPU_FILLMODE_FILL,
|
|
.cull_mode = SDL_GPU_CULLMODE_NONE,
|
|
.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE,
|
|
},
|
|
.target_info = {
|
|
.color_target_descriptions = color_target_descriptions,
|
|
.num_color_targets = SDL_arraysize(color_target_descriptions),
|
|
},
|
|
};
|
|
|
|
if (gui_tile_graphics_pipeline) {
|
|
SDL_ReleaseGPUGraphicsPipeline(device, gui_tile_graphics_pipeline);
|
|
gui_tile_graphics_pipeline = NULL;
|
|
}
|
|
|
|
gui_tile_graphics_pipeline = SDL_CreateGPUGraphicsPipeline(device, &gui_tile_graphics_pipeline_info);
|
|
if (!gui_tile_graphics_pipeline) {
|
|
log_error("Failed to create world graphics pipeline. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
SDL_ReleaseGPUShader(device, gui_tile_vertex_shader);
|
|
SDL_ReleaseGPUShader(device, gui_tile_pixel_shader);
|
|
}
|
|
|
|
{ // grid_graphics_pipeline
|
|
SDL_GPUShaderCreateInfo grid_vertex_shader_info = {
|
|
.code_size = SDL_arraysize(SPIRV_grid),
|
|
.code = SPIRV_grid,
|
|
.entrypoint = "main_vertex",
|
|
.format = SDL_GPU_SHADERFORMAT_SPIRV,
|
|
.stage = SDL_GPU_SHADERSTAGE_VERTEX,
|
|
|
|
.num_uniform_buffers = 2,
|
|
};
|
|
|
|
SDL_GPUShader *grid_vertex_shader = SDL_CreateGPUShader(device, &grid_vertex_shader_info);
|
|
if (!grid_vertex_shader) {
|
|
log_error("Failed to create grid vertex shader. Exiting.");
|
|
return false;
|
|
}
|
|
|
|
SDL_GPUShaderCreateInfo grid_pixel_shader_info = {
|
|
.code_size = SDL_arraysize(SPIRV_grid),
|
|
.code = SPIRV_grid,
|
|
.entrypoint = "main_fragment",
|
|
.format = SDL_GPU_SHADERFORMAT_SPIRV,
|
|
.stage = SDL_GPU_SHADERSTAGE_FRAGMENT,
|
|
|
|
.num_uniform_buffers = 1,
|
|
};
|
|
|
|
SDL_GPUShader *grid_pixel_shader = SDL_CreateGPUShader(device, &grid_pixel_shader_info);
|
|
if (!grid_pixel_shader) {
|
|
log_error("Failed to create grid pixel shader. Exiting.");
|
|
return false;
|
|
}
|
|
|
|
SDL_GPUVertexBufferDescription vertex_buffer_descriptions[] = {
|
|
{
|
|
.slot = 0,
|
|
.pitch = sizeof(Vertex),
|
|
.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,
|
|
},
|
|
};
|
|
|
|
SDL_GPUVertexAttribute vertex_attributes[] = {
|
|
{
|
|
.location = 0,
|
|
.buffer_slot = 0,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3,
|
|
.offset = offsetof(Vertex, pos),
|
|
},
|
|
};
|
|
|
|
SDL_GPUGraphicsPipelineCreateInfo grid_graphics_pipeline_info = {
|
|
.vertex_shader = grid_vertex_shader,
|
|
.fragment_shader = grid_pixel_shader,
|
|
.vertex_input_state = {
|
|
.vertex_buffer_descriptions = vertex_buffer_descriptions,
|
|
.num_vertex_buffers = SDL_arraysize(vertex_buffer_descriptions),
|
|
.vertex_attributes = vertex_attributes,
|
|
.num_vertex_attributes = SDL_arraysize(vertex_attributes),
|
|
},
|
|
.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
|
|
.rasterizer_state = {
|
|
.fill_mode = SDL_GPU_FILLMODE_FILL,
|
|
.cull_mode = SDL_GPU_CULLMODE_BACK,
|
|
.front_face = SDL_GPU_FRONTFACE_CLOCKWISE,
|
|
.enable_depth_clip = true,
|
|
},
|
|
.multisample_state = {
|
|
.sample_count = enable_msaa ? highest_supported_sample_count : SDL_GPU_SAMPLECOUNT_1,
|
|
},
|
|
.target_info = {
|
|
.color_target_descriptions = color_target_descriptions,
|
|
.num_color_targets = SDL_arraysize(color_target_descriptions),
|
|
},
|
|
};
|
|
|
|
if (grid_graphics_pipeline) {
|
|
SDL_ReleaseGPUGraphicsPipeline(device, grid_graphics_pipeline);
|
|
grid_graphics_pipeline = NULL;
|
|
}
|
|
|
|
grid_graphics_pipeline = SDL_CreateGPUGraphicsPipeline(device, &grid_graphics_pipeline_info);
|
|
if (!grid_graphics_pipeline) {
|
|
log_error("Failed to create grid graphics pipeline. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
SDL_ReleaseGPUShader(device, grid_vertex_shader);
|
|
SDL_ReleaseGPUShader(device, grid_pixel_shader);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool recreate_tile_textures() {
|
|
SDL_PropertiesID tile_textures_array_properties = SDL_CreateProperties();
|
|
defer(SDL_DestroyProperties(tile_textures_array_properties));
|
|
|
|
SDL_SetStringProperty(tile_textures_array_properties, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING, "tile_textures_array");
|
|
|
|
SDL_GPUTextureCreateInfo tile_textures_array_info = {
|
|
.type = SDL_GPU_TEXTURETYPE_2D_ARRAY,
|
|
.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB,
|
|
.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER,
|
|
.width = TILE_SIZE,
|
|
.height = TILE_SIZE,
|
|
|
|
.layer_count_or_depth = SDL_arraysize(tile_infos),
|
|
.num_levels = 1,
|
|
|
|
.props = tile_textures_array_properties,
|
|
};
|
|
|
|
tile_textures_array = SDL_CreateGPUTexture(device, &tile_textures_array_info);
|
|
if (!tile_textures_array) {
|
|
log_error("Failed to create gpu tiles_texture (%s).", SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
Uint32 transfer_buffer_size = TILE_SIZE * TILE_SIZE * 4 * SDL_arraysize(tile_infos);
|
|
SDL_GPUTransferBufferCreateInfo transfer_buffer_info = {
|
|
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
|
|
.size = transfer_buffer_size,
|
|
};
|
|
|
|
SDL_GPUTransferBuffer *transfer_buffer = SDL_CreateGPUTransferBuffer(device, &transfer_buffer_info);
|
|
if (!transfer_buffer) {
|
|
log_error("Failed to create gpu transfer buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTexture(device, tile_textures_array);
|
|
return false;
|
|
}
|
|
defer(SDL_ReleaseGPUTransferBuffer(device, transfer_buffer));
|
|
|
|
SDL_GPUCommandBuffer *command_buffer = SDL_AcquireGPUCommandBuffer(device);
|
|
if (!command_buffer) {
|
|
log_error("Failed to acquire gpu command buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTexture(device, tile_textures_array);
|
|
return false;
|
|
}
|
|
|
|
SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(command_buffer);
|
|
|
|
void *transfer_data = SDL_MapGPUTransferBuffer(device, transfer_buffer, false);
|
|
if (!transfer_data) {
|
|
log_error("Failed to map gpu transfer buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTexture(device, tile_textures_array);
|
|
return false;
|
|
}
|
|
|
|
for (Uint32 i = 0; i < SDL_arraysize(tile_infos); i++) {
|
|
char path[256] = ASSETS_PATH;
|
|
SDL_strlcat(path, tile_infos[i].asset_path, SDL_arraysize(path));
|
|
|
|
int width = 0, height = 0;
|
|
stbi_uc *data = stbi_load(path, &width, &height, NULL, 4);
|
|
if (!data) {
|
|
log_error("Failed to load texture (\"%s\"). Exiting.", path);
|
|
SDL_ReleaseGPUTexture(device, tile_textures_array);
|
|
SDL_CancelGPUCommandBuffer(command_buffer);
|
|
return false;
|
|
}
|
|
|
|
SDL_assert_always(width == TILE_SIZE);
|
|
SDL_assert_always(height == TILE_SIZE);
|
|
|
|
tile_infos[i].uv_min = V2_(0, 0);
|
|
tile_infos[i].uv_max = V2_(1, 1);
|
|
|
|
cpu_tile_infos_buffer[i] = V4_(tile_infos[i].uv_min, tile_infos[i].uv_max);
|
|
|
|
memmove((char *)transfer_data + i * width * height * 4, data, width * height * 4);
|
|
stbi_image_free(data);
|
|
|
|
SDL_GPUTextureTransferInfo transfer_info = {
|
|
.transfer_buffer = transfer_buffer,
|
|
.offset = i * width * height * 4,
|
|
.pixels_per_row = (Uint32)width,
|
|
};
|
|
|
|
SDL_GPUTextureRegion tetxure_region = {
|
|
.texture = tile_textures_array,
|
|
.layer = i,
|
|
.w = (Uint32)width,
|
|
.h = (Uint32)height,
|
|
.d = 1,
|
|
};
|
|
|
|
SDL_UploadToGPUTexture(copy_pass, &transfer_info, &tetxure_region, false);
|
|
}
|
|
|
|
SDL_UnmapGPUTransferBuffer(device, transfer_buffer);
|
|
SDL_EndGPUCopyPass(copy_pass);
|
|
|
|
if (!SDL_SubmitGPUCommandBuffer(command_buffer)) {
|
|
log_error("Failed to submit gpu command buffer (%s).", SDL_GetError());
|
|
SDL_ReleaseGPUTexture(device, tile_textures_array);
|
|
SDL_CancelGPUCommandBuffer(command_buffer);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int real_mod(int a, int b) {
|
|
int result = a % b;
|
|
return result >= 0 ? result : result + b;
|
|
}
|
|
|
|
bool imgui_time_picker(const char *label, int time[3]) {
|
|
bool result = ImGui::DragScalarN(label, ImGuiDataType_S32, time, 3);
|
|
|
|
time[1] += time[2] >= 0 ? time[2] / 60 : -1;
|
|
time[0] += time[1] >= 0 ? time[1] / 60 : -1;
|
|
|
|
time[2] = real_mod(time[2], 60);
|
|
time[1] = real_mod(time[1], 60);
|
|
time[0] = real_mod(time[0], 24);
|
|
|
|
return result;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
setup_memory_functions();
|
|
|
|
#ifdef SDL_PLATFORM_LINUX
|
|
if (!getenv("ENABLE_VULKAN_RENDERDOC_CAPTURE"))
|
|
SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "wayland,x11");
|
|
#endif
|
|
|
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMEPAD)) {
|
|
log_error("Failed to initialize SDL (%s). Exiting.", SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV, true, NULL);
|
|
if (!device) {
|
|
log_error("Failed to create gpu device (%s). Exiting.", SDL_GetError());
|
|
return 1;
|
|
}
|
|
SDL_SetGPUAllowedFramesInFlight(device, 1);
|
|
|
|
window = SDL_CreateWindow("Mikemon", 1280, 720, SDL_WINDOW_RESIZABLE);
|
|
if (!window) {
|
|
log_error("Failed to create window (%s). Exiting.", SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
if (!SDL_ClaimWindowForGPUDevice(device, window)) {
|
|
log_error("Failed to claim window for gpu device (%s). Exiting.", SDL_GetError());
|
|
return 1;
|
|
}
|
|
SDL_SetGPUSwapchainParameters(device, window, SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR, SDL_GPU_PRESENTMODE_VSYNC);
|
|
|
|
SDL_GPUTextureFormat swapchain_format = SDL_GetGPUSwapchainTextureFormat(device, window);
|
|
highest_supported_sample_count = SDL_GPUTextureSupportsSampleCount(device, swapchain_format, SDL_GPU_SAMPLECOUNT_2) ? SDL_GPU_SAMPLECOUNT_2 : highest_supported_sample_count;
|
|
highest_supported_sample_count = SDL_GPUTextureSupportsSampleCount(device, swapchain_format, SDL_GPU_SAMPLECOUNT_4) ? SDL_GPU_SAMPLECOUNT_4 : highest_supported_sample_count;
|
|
highest_supported_sample_count = SDL_GPUTextureSupportsSampleCount(device, swapchain_format, SDL_GPU_SAMPLECOUNT_8) ? SDL_GPU_SAMPLECOUNT_8 : highest_supported_sample_count;
|
|
|
|
if (!recreate_graphics_pipelines()) {
|
|
log_error("Failed to create graphics pipelines. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
if (!load_map("map.sv", ¤t_map)) {
|
|
log_error("Failed to load initial map. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
SDL_GPUTexture *player_texture = create_shader_texture("decorations/strawberry.png");
|
|
if (!player_texture) {
|
|
log_error("Failed to create shader texture. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
if (!recreate_tile_textures()) {
|
|
log_error("Failed to create tile textures. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
SDL_GPUSamplerCreateInfo pixel_sampler_info = {
|
|
.min_filter = SDL_GPU_FILTER_LINEAR,
|
|
.mag_filter = SDL_GPU_FILTER_LINEAR,
|
|
.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR,
|
|
|
|
.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
|
.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
|
.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
|
|
|
.max_anisotropy = 16.0f,
|
|
.enable_anisotropy = true,
|
|
};
|
|
|
|
pixel_sampler = SDL_CreateGPUSampler(device, &pixel_sampler_info);
|
|
SDL_GPUTextureSamplerBinding tile_atlas_texture_binding = { .texture = tile_textures_array, .sampler = pixel_sampler };
|
|
|
|
vertex_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_VERTEX, sizeof(vertices), vertices, "vertex_buffer");
|
|
if (!vertex_buffer) {
|
|
log_error("Failed to create buffer. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
index_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_INDEX, sizeof(indices), indices, "index_buffer");
|
|
if (!index_buffer) {
|
|
log_error("Failed to create buffer. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
grid_vertex_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_VERTEX, sizeof(grid_vertices), grid_vertices, "grid_vertex_buffer");
|
|
if (!grid_vertex_buffer) {
|
|
log_error("Failed to create buffer. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
grid_index_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_INDEX, sizeof(grid_indices), grid_indices, "grid_index_buffer");
|
|
if (!grid_index_buffer) {
|
|
log_error("Failed to create buffer. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
player_instance_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_VERTEX, sizeof(player_instance), &player_instance, "player_instance_buffer");
|
|
if (!player_instance_buffer) {
|
|
log_error("Failed to create buffer. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
tile_infos_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ, SDL_arraysize(cpu_tile_infos_buffer) * sizeof(*cpu_tile_infos_buffer), cpu_tile_infos_buffer, "tile_infos_buffer");
|
|
if (!tile_infos_buffer) {
|
|
log_error("Failed to create buffer. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
IMGUI_CHECKVERSION();
|
|
ImGuiContext *imgui_context = ImGui::CreateContext();
|
|
ImGuiIO &io = ImGui::GetIO();
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
|
io.ConfigFlags |= ImGuiConfigFlags_IsSRGB;
|
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
|
|
|
ImGui::StyleColorsDark();
|
|
|
|
if (io.ConfigFlags & ImGuiConfigFlags_IsSRGB){
|
|
for (int i = 0; i < IM_ARRAYSIZE(imgui_context->Style.Colors); i++) {
|
|
imgui_context->Style.Colors[i] = sRGB_to_linear(imgui_context->Style.Colors[i]);
|
|
}
|
|
}
|
|
|
|
ImGui_ImplSDL3_InitForSDLGPU(window);
|
|
ImGui_ImplSDLGPU3_InitInfo imgui_init_info = {
|
|
.Device = device,
|
|
.ColorTargetFormat = SDL_GetGPUSwapchainTextureFormat(device, window),
|
|
.MSAASamples = SDL_GPU_SAMPLECOUNT_1,
|
|
};
|
|
ImGui_ImplSDLGPU3_Init(&imgui_init_info);
|
|
|
|
ImGuiSettingsHandler time_tints_settings_handler = {};
|
|
time_tints_settings_handler.TypeName = "TimeTints";
|
|
time_tints_settings_handler.TypeHash = ImHashStr("TimeTints");
|
|
time_tints_settings_handler.ReadOpenFn = [](ImGuiContext *context, ImGuiSettingsHandler *handler, const char *name) -> void * {
|
|
if (strcmp(name, "num") == 0)
|
|
return (void *)-1;
|
|
int num = atoi(name) + 1;
|
|
return (void *)(Sint64)num;
|
|
};
|
|
time_tints_settings_handler.ReadLineFn = [](ImGuiContext *context, ImGuiSettingsHandler *handler, void *entry, const char *line) {
|
|
if (entry == (void *)-1) {
|
|
sscanf(line, "num=%d", &num_used_tint_times);
|
|
return;
|
|
}
|
|
|
|
if (strncmp(line, "time", 4) == 0) {
|
|
sscanf(line, "time=%d %d %d", &time_tints_times[(size_t)entry - 1][0], &time_tints_times[(size_t)entry - 1][1], &time_tints_times[(size_t)entry - 1][2]);
|
|
} else if(strncmp(line, "color", 5) == 0) {
|
|
sscanf(line, "color=%g %g %g", &time_tints[(size_t)entry - 1][0], &time_tints[(size_t)entry - 1][1], &time_tints[(size_t)entry - 1][2]);
|
|
}
|
|
};
|
|
time_tints_settings_handler.WriteAllFn = [](ImGuiContext *context, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buffer) {
|
|
buffer->append("[TimeTints][num]\n");
|
|
buffer->appendf("num=%d\n\n", num_used_tint_times);
|
|
|
|
for (int i = 0; i < num_used_tint_times; i++) {
|
|
buffer->appendf("[TimeTints][%d]\n", i);
|
|
buffer->appendf("time=%d %d %d\n", time_tints_times[i][0], time_tints_times[i][1], time_tints_times[i][2]);
|
|
buffer->appendf("color=%g %g %g\n\n", time_tints[i][0], time_tints[i][1], time_tints[i][2]);
|
|
}
|
|
};
|
|
ImGui::AddSettingsHandler(&time_tints_settings_handler);
|
|
|
|
bool first_frame = true;
|
|
bool show_demo_window = false;
|
|
bool show_tile_picker = false;
|
|
bool show_settings = false;
|
|
|
|
SDL_GetWindowSizeInPixels(window, &window_width, &window_height);
|
|
|
|
// MSG Message;
|
|
while (Running) {
|
|
ZoneScopedN("Loop");
|
|
ImGui_ImplSDLGPU3_NewFrame();
|
|
ImGui_ImplSDL3_NewFrame();
|
|
ImGui::NewFrame();
|
|
|
|
SDL_GetCurrentTime(&time);
|
|
|
|
if (use_actual_time)
|
|
SDL_TimeToDateTime(time, &calendar_time, true);
|
|
|
|
calendar_time.minute += calendar_time.second >= 0 ? calendar_time.second / 60 : calendar_time.second / 60 - 1;
|
|
calendar_time.hour += calendar_time.minute >= 0 ? calendar_time.minute / 60 : calendar_time.minute / 60 - 1;
|
|
|
|
calendar_time.second = real_mod(calendar_time.second, 60);
|
|
calendar_time.minute = real_mod(calendar_time.minute, 60);
|
|
calendar_time.hour = real_mod(calendar_time.hour, 24);
|
|
|
|
if (ImGui::BeginMainMenuBar()) {
|
|
if (ImGui::BeginMenu("File")) {
|
|
ImGui::MenuItem("Settings", NULL, &show_settings);
|
|
ImGui::MenuItem("Demo Window", NULL, &show_demo_window);
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Exit")) {
|
|
Running = false;
|
|
}
|
|
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::BeginMenu("Edit")) {
|
|
ImGui::MenuItem("Tile Picker", NULL, &show_tile_picker);
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
ImGui::EndMainMenuBar();
|
|
}
|
|
|
|
ImGuiID main_viewport_dock = ImGui::GetID("main_viewport_dock");
|
|
if (!ImGui::DockBuilderGetNode(main_viewport_dock)) {
|
|
ImGui::DockBuilderAddNode (main_viewport_dock, (ImGuiDockNodeFlags)ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingOverCentralNode);
|
|
ImGui::DockBuilderSetNodePos (main_viewport_dock, ImGui::GetMainViewport()->WorkPos);
|
|
ImGui::DockBuilderSetNodeSize(main_viewport_dock, ImGui::GetMainViewport()->WorkSize);
|
|
|
|
ImGuiID left_dock = ImGui::DockBuilderSplitNode(main_viewport_dock, ImGuiDir_Left, 0.2f, NULL, NULL);
|
|
ImGui::DockBuilderDockWindow("Tile Picker", left_dock);
|
|
ImGui::DockBuilderFinish(main_viewport_dock);
|
|
}
|
|
ImGui::DockSpaceOverViewport(main_viewport_dock, ImGui::GetMainViewport(), ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingOverCentralNode);
|
|
|
|
if (show_settings) {
|
|
ImGui::SetNextWindowSize(ImVec2(400, 0), ImGuiCond_FirstUseEver);
|
|
if (ImGui::Begin("Settings", &show_settings)) {
|
|
ImGui::DragFloat("fovy", &camera_fovy_degrees);
|
|
ImGui::DragFloat("camera_distance", &camera_distance, 0.25f, 1.0f, INFINITY);
|
|
ImGui::DragFloat("camera_tilt", &camera_tilt, 0.25f, 0.0f, 89.0f);
|
|
|
|
if (ImGui::Checkbox("MSAA", &enable_msaa)) {
|
|
recreate_graphics_pipelines();
|
|
if (msaa_texture) {
|
|
SDL_ReleaseGPUTexture(device, msaa_texture);
|
|
msaa_texture = NULL;
|
|
}
|
|
}
|
|
|
|
ImGui::NewLine();
|
|
ImGui::BeginDisabled(use_actual_time);
|
|
ImGui::DragScalarN("Time", ImGuiDataType_S32, &calendar_time.hour, 3);
|
|
ImGui::EndDisabled();
|
|
ImGui::Checkbox("use actual time", &use_actual_time);
|
|
|
|
ImGui::NewLine();
|
|
for (int i = 0; i < num_used_tint_times; i++) {
|
|
ImGui::PushID(i);
|
|
imgui_time_picker("##time", time_tints_times[i]);
|
|
ImGui::PopID();
|
|
}
|
|
|
|
if (ImGui::Button("Add")) num_used_tint_times = clamp(1, num_used_tint_times + 1, MAX_TINT_TIMES);
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Remove")) num_used_tint_times = clamp(1, num_used_tint_times - 1, MAX_TINT_TIMES);
|
|
|
|
ImGui::NewLine();
|
|
for (int i = 0; i < num_used_tint_times; i++) {
|
|
ImGui::PushID(i);
|
|
ImGui::ColorEdit3("##color", time_tints[i].E);
|
|
ImGui::PopID();
|
|
}
|
|
|
|
if (!ImGui::IsAnyItemActive()) {
|
|
for (int j = 0; j < num_used_tint_times; j++) {
|
|
for (int i = 0; i < num_used_tint_times - 1; i++) {
|
|
if (time_tints_times[i][0] > time_tints_times[i + 1][0] ||
|
|
time_tints_times[i][0] == time_tints_times[i + 1][0] && time_tints_times[i][1] > time_tints_times[i + 1][1] ||
|
|
time_tints_times[i][0] == time_tints_times[i + 1][0] && time_tints_times[i][1] == time_tints_times[i + 1][1] && time_tints_times[i][2] > time_tints_times[i + 1][2]) {
|
|
|
|
int temp_time[3] = { time_tints_times[i][0], time_tints_times[i][1], time_tints_times[i][2] };
|
|
V3 temp_color = time_tints[i];
|
|
|
|
time_tints_times[i][0] = time_tints_times[i + 1][0];
|
|
time_tints_times[i][1] = time_tints_times[i + 1][1];
|
|
time_tints_times[i][2] = time_tints_times[i + 1][2];
|
|
|
|
time_tints[i] = time_tints[i + 1];
|
|
|
|
time_tints_times[i + 1][0] = temp_time[0];
|
|
time_tints_times[i + 1][1] = temp_time[1];
|
|
time_tints_times[i + 1][2] = temp_time[2];
|
|
|
|
time_tints[i + 1] = temp_color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
if (show_tile_picker) {
|
|
if (ImGui::Begin("Tile Picker", &show_tile_picker, ImGuiWindowFlags_NoFocusOnAppearing)) {
|
|
if (SelectableTile("##tile", selected_tile == -1, NULL, 0, ImVec2(32, 32))) {
|
|
selected_tile = -1;
|
|
}
|
|
|
|
for (int i = 0; i < SDL_arraysize(tile_infos); i++) {
|
|
ImGui::PushID(i);
|
|
|
|
ImVec2 uv0 = ImVec2(tile_infos[i].uv_min.x, tile_infos[i].uv_min.y);
|
|
ImVec2 uv1 = ImVec2(tile_infos[i].uv_max.x, tile_infos[i].uv_max.y);
|
|
|
|
float available = ImGui::GetContentRegionAvail().x - ImGui::GetItemRectMax().x;
|
|
if (available >= 32)
|
|
ImGui::SameLine();
|
|
|
|
if (SelectableTile("##tile", selected_tile == i, &tile_atlas_texture_binding, i, ImVec2(32, 32), uv0, uv1, SDL_max(selected_rotation, 0)))
|
|
selected_tile = i;
|
|
|
|
ImGui::PopID();
|
|
}
|
|
|
|
if (selected_tile != -1) {
|
|
ImGui::Text("Rotation:");
|
|
|
|
ImVec2 uv0 = ImVec2(tile_infos[selected_tile].uv_min.x, tile_infos[selected_tile].uv_min.y);
|
|
ImVec2 uv1 = ImVec2(tile_infos[selected_tile].uv_max.x, tile_infos[selected_tile].uv_max.y);
|
|
|
|
if (SelectableTile("##None", selected_rotation == 0, &tile_atlas_texture_binding, selected_tile, ImVec2(32, 32), uv0, uv1, 0))
|
|
selected_rotation = 0;
|
|
|
|
if (ImGui::GetContentRegionAvail().x - ImGui::GetItemRectMax().x >= 32)
|
|
ImGui::SameLine();
|
|
|
|
if (SelectableTile("##90", selected_rotation == 1, &tile_atlas_texture_binding, selected_tile, ImVec2(32, 32), uv0, uv1, 1))
|
|
selected_rotation = 1;
|
|
|
|
if (ImGui::GetContentRegionAvail().x - ImGui::GetItemRectMax().x >= 32)
|
|
ImGui::SameLine();
|
|
|
|
if (SelectableTile("##180", selected_rotation == 2, &tile_atlas_texture_binding, selected_tile, ImVec2(32, 32), uv0, uv1, 2))
|
|
selected_rotation = 2;
|
|
|
|
if (ImGui::GetContentRegionAvail().x - ImGui::GetItemRectMax().x >= 32)
|
|
ImGui::SameLine();
|
|
|
|
if (SelectableTile("##270", selected_rotation == 3, &tile_atlas_texture_binding, selected_tile, ImVec2(32, 32), uv0, uv1, 3))
|
|
selected_rotation = 3;
|
|
|
|
if (ImGui::Selectable("Random", selected_rotation == -1))
|
|
selected_rotation = -1;
|
|
}
|
|
|
|
if (selected_tile != -1 && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_R, false)) {
|
|
if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) {
|
|
selected_rotation = (selected_rotation - 1) & 3;
|
|
} else {
|
|
selected_rotation = (selected_rotation + 1) & 3;
|
|
}
|
|
}
|
|
}
|
|
ImGui::End();
|
|
} else {
|
|
selected_tile = -1;
|
|
}
|
|
|
|
if (show_demo_window)
|
|
ImGui::ShowDemoWindow(&show_demo_window);
|
|
|
|
if (first_frame) {
|
|
ImGui::SetWindowFocus(NULL);
|
|
first_frame = false;
|
|
}
|
|
|
|
SDL_GPUCommandBuffer *command_buffer = SDL_AcquireGPUCommandBuffer(device);
|
|
if (!command_buffer) {
|
|
log_error("Failed to acquire gpu command buffer (%s). Exiting.", SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
TracyCZoneN(tracy_AcquireSwapchainTexture, "AcquireSwapchainTexture", true);
|
|
Uint32 width = 0, height = 0;
|
|
SDL_GPUTexture *swapchain_texture = NULL;
|
|
if (!SDL_WaitAndAcquireGPUSwapchainTexture(command_buffer, window, &swapchain_texture, &width, &height)) {
|
|
log_error("Failed to acquire swapchain texture (%s). Exiting.", SDL_GetError());
|
|
SDL_CancelGPUCommandBuffer(command_buffer);
|
|
return 1;
|
|
}
|
|
TracyCZoneEnd(tracy_AcquireSwapchainTexture);
|
|
|
|
{
|
|
ZoneScopedN("Events");
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event)) {
|
|
ZoneScopedN("Event");
|
|
ImGui_ImplSDL3_ProcessEvent(&event);
|
|
|
|
switch (event.type) {
|
|
case SDL_EVENT_QUIT: {
|
|
Running = false;
|
|
} break;
|
|
|
|
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: {
|
|
window_width = event.window.data1;
|
|
window_height = event.window.data2;
|
|
} break;
|
|
|
|
case SDL_EVENT_KEY_DOWN: {
|
|
if (io.WantCaptureKeyboard)
|
|
continue;
|
|
|
|
SDL_Keymod modifiers = SDL_GetModState();
|
|
|
|
if (event.key.key == SDLK_UP || event.key.key == SDLK_W) {
|
|
player.pos_y = clamp(0, player.pos_y + 1, current_map.height - 2);
|
|
}
|
|
|
|
if (event.key.key == SDLK_LEFT || event.key.key == SDLK_A) {
|
|
player.pos_x = clamp(0, player.pos_x - 1, current_map.width - 2);
|
|
}
|
|
|
|
if (event.key.key == SDLK_DOWN || event.key.key == SDLK_S) {
|
|
player.pos_y = clamp(0, player.pos_y - 1, current_map.height - 2);
|
|
}
|
|
|
|
if (event.key.key == SDLK_RIGHT || event.key.key == SDLK_D) {
|
|
player.pos_x = clamp(0, player.pos_x + 1, current_map.width - 2);
|
|
}
|
|
|
|
if (event.key.key == SDLK_F1) {
|
|
save_map(current_map);
|
|
}
|
|
|
|
if (event.key.key == SDLK_F4) {
|
|
char *map_path = SDL_strdup(current_map.name);
|
|
unload_map(¤t_map);
|
|
load_map(map_path, ¤t_map);
|
|
SDL_free(map_path);
|
|
}
|
|
|
|
if (event.key.key == SDLK_R) {
|
|
if (selected_tile != -1 && selected_rotation != -1) {
|
|
if (modifiers & SDL_KMOD_SHIFT) {
|
|
selected_rotation = (selected_rotation - 1) & 3;
|
|
} else {
|
|
selected_rotation = (selected_rotation + 1) & 3;
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
|
|
if (io.WantCaptureMouse)
|
|
continue;
|
|
|
|
V2 floor_intersection = get_floor_intersection_of_mouse(V2_(event.button.x, event.button.y));
|
|
|
|
Sint32 tile_x = roundf(floor_intersection.x);
|
|
Sint32 tile_y = roundf(floor_intersection.y);
|
|
|
|
if(0 <= tile_x && tile_x < current_map.width && 0 <= tile_y && tile_y < current_map.height) {
|
|
dragging_tile_change = true;
|
|
|
|
drag_start_pos[0] = tile_x;
|
|
drag_start_pos[1] = tile_y;
|
|
}
|
|
|
|
SDL_Keymod modifiers = SDL_GetModState();
|
|
if (modifiers & SDL_KMOD_SHIFT && tile_x <= -1) {
|
|
if(modifiers & SDL_KMOD_CTRL)
|
|
change_map_size(¤t_map, 'W', -1);
|
|
else
|
|
change_map_size(¤t_map, 'W', 1);
|
|
}
|
|
|
|
if (modifiers & SDL_KMOD_SHIFT && tile_x == current_map.width) {
|
|
if (modifiers & SDL_KMOD_CTRL)
|
|
change_map_size(¤t_map, 'E', -1);
|
|
else
|
|
change_map_size(¤t_map, 'E', 1);
|
|
}
|
|
|
|
if (modifiers & SDL_KMOD_SHIFT && tile_y <= -1) {
|
|
if (modifiers & SDL_KMOD_CTRL)
|
|
change_map_size(¤t_map, 'N', -1);
|
|
else
|
|
change_map_size(¤t_map, 'N', 1);
|
|
}
|
|
|
|
if (modifiers & SDL_KMOD_SHIFT && tile_y == current_map.height) {
|
|
if (modifiers & SDL_KMOD_CTRL)
|
|
change_map_size(¤t_map, 'S', -1);
|
|
else
|
|
change_map_size(¤t_map, 'S', 1);
|
|
}
|
|
} break;
|
|
|
|
case SDL_EVENT_MOUSE_BUTTON_UP: {
|
|
if (io.WantCaptureMouse)
|
|
continue;
|
|
|
|
if (selected_tile != -1 && dragging_tile_change) {
|
|
V2 floor_intersection = get_floor_intersection_of_mouse(V2_(event.button.x, event.button.y));
|
|
|
|
Sint32 tile_x = clamp(0, (Sint32)roundf(floor_intersection.x), current_map.width - 1);
|
|
Sint32 tile_y = clamp(0, (Sint32)roundf(floor_intersection.y), current_map.height - 1);
|
|
|
|
Sint32 start_x = min(tile_x, drag_start_pos[0]);
|
|
Sint32 start_y = min(tile_y, drag_start_pos[1]);
|
|
|
|
Sint32 end_x = max(tile_x, drag_start_pos[0]);
|
|
Sint32 end_y = max(tile_y, drag_start_pos[1]);
|
|
|
|
for (Sint32 y = start_y; y <= end_y; y++) {
|
|
for (Sint32 x = start_x; x <= end_x; x++) {
|
|
if (selected_rotation == -1) {
|
|
Sint32 rotation = SDL_rand(4);
|
|
current_map.tiles[x + current_map.width * y] = ((rotation & 3) << 16) | selected_tile;
|
|
} else {
|
|
current_map.tiles[x + current_map.width * y] = ((selected_rotation & 3) << 16) | selected_tile;
|
|
}
|
|
}
|
|
}
|
|
|
|
update_buffer(current_map.gpu_buffer, 0, current_map.width * current_map.height * sizeof(Uint32), current_map.tiles);
|
|
}
|
|
|
|
dragging_tile_change = false;
|
|
} break;
|
|
|
|
case SDL_EVENT_MOUSE_MOTION: {
|
|
mouse_pos = V2_(event.motion.x, event.motion.y);
|
|
} break;
|
|
|
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: {
|
|
if (io.WantCaptureKeyboard)
|
|
continue;
|
|
|
|
if (event.gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_UP) {
|
|
player.pos_y = clamp(0, player.pos_y + 1, current_map.height - 2);
|
|
}
|
|
|
|
if (event.gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_LEFT) {
|
|
player.pos_x = clamp(0, player.pos_x - 1, current_map.width - 2);
|
|
}
|
|
|
|
if (event.gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_DOWN) {
|
|
player.pos_y = clamp(0, player.pos_y - 1, current_map.height - 2);
|
|
}
|
|
|
|
if (event.gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
|
|
player.pos_x = clamp(0, player.pos_x + 1, current_map.width - 2);
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!swapchain_texture) {
|
|
SDL_CancelGPUCommandBuffer(command_buffer);
|
|
ImGui::Render();
|
|
ImDrawData *draw_data = ImGui::GetDrawData();
|
|
SDL_DelayNS(16 * SDL_NS_PER_MS);
|
|
continue;
|
|
}
|
|
|
|
if (enable_msaa && (!msaa_texture || msaa_texture_width != width || msaa_texture_height != height)) {
|
|
if (msaa_texture) {
|
|
SDL_ReleaseGPUTexture(device, msaa_texture);
|
|
msaa_texture = NULL;
|
|
}
|
|
|
|
SDL_GPUTextureCreateInfo texture_info = {
|
|
.type = SDL_GPU_TEXTURETYPE_2D,
|
|
.format = swapchain_format,
|
|
.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET,
|
|
.width = (Uint32)width,
|
|
.height = (Uint32)height,
|
|
|
|
.layer_count_or_depth = 1,
|
|
.num_levels = 1,
|
|
.sample_count = highest_supported_sample_count,
|
|
};
|
|
|
|
msaa_texture = SDL_CreateGPUTexture(device, &texture_info);
|
|
if (!msaa_texture) {
|
|
log_error("Failed to create gpu texture (%s).", SDL_GetError());
|
|
return 1;
|
|
}
|
|
msaa_texture_width = width;
|
|
msaa_texture_height = height;
|
|
}
|
|
|
|
{
|
|
ZoneScopedN("Update");
|
|
{
|
|
ZoneScopedN("player_instance_buffer");
|
|
player_instance.pos.x = player.pos_x;
|
|
player_instance.pos.y = player.pos_y;
|
|
|
|
update_buffer(player_instance_buffer, 0, sizeof(player_instance), &player_instance);
|
|
}
|
|
|
|
{
|
|
ZoneScopedN("per_frame");
|
|
|
|
float aspect_ratio = ((float) window_width / (float) window_height);
|
|
|
|
view_matrix = view (V3_((float)player.pos_x, (float)player.pos_y, 0), radians(camera_tilt), camera_distance);
|
|
inverse_view_matrix = inverse_view(V3_((float)player.pos_x, (float)player.pos_y, 0), radians(camera_tilt), camera_distance);
|
|
|
|
projection_matrix = projection (radians(camera_fovy_degrees), aspect_ratio, NEAR_PLANE);
|
|
inverse_projection_matrix = inverse_projection(radians(camera_fovy_degrees), aspect_ratio, NEAR_PLANE);
|
|
|
|
M4x4 view_projection_matrix = projection_matrix * view_matrix;
|
|
SDL_PushGPUVertexUniformData(command_buffer, 0, &view_projection_matrix, sizeof(view_projection_matrix));
|
|
|
|
V2 floor_intersection = get_floor_intersection_of_mouse(mouse_pos);
|
|
Sint32 tile_x = roundf(floor_intersection.x);
|
|
Sint32 tile_y = roundf(floor_intersection.y);
|
|
|
|
per_frame.map_width = current_map.width;
|
|
per_frame.mouse[0] = tile_x;
|
|
per_frame.mouse[1] = tile_y;
|
|
|
|
if (dragging_tile_change) {
|
|
per_frame.drag_start[0] = drag_start_pos[0];
|
|
per_frame.drag_start[1] = drag_start_pos[1];
|
|
} else {
|
|
per_frame.drag_start[0] = tile_x;
|
|
per_frame.drag_start[1] = tile_y;
|
|
}
|
|
|
|
SDL_PushGPUVertexUniformData(command_buffer, 1, &per_frame, sizeof(per_frame));
|
|
}
|
|
|
|
{
|
|
ZoneScopedN("tint color");
|
|
Sint64 tint_times_ns[MAX_TINT_TIMES];
|
|
for (int i = 0; i < num_used_tint_times; i++)
|
|
tint_times_ns[i] = (time_tints_times[i][0] * 60 * 60 + time_tints_times[i][1] * 60 + time_tints_times[i][2]) * SDL_NS_PER_SECOND;
|
|
tint_times_ns[num_used_tint_times] = (24 * 60 * 60 + 60 * 60 + 60) * SDL_NS_PER_SECOND + tint_times_ns[0];
|
|
|
|
Sint64 calendar_time_ns = (calendar_time.hour * 60 * 60 + calendar_time.minute * 60 + calendar_time.second) * SDL_NS_PER_SECOND + (Sint64)calendar_time.nanosecond;
|
|
|
|
int last_time_index = num_used_tint_times - 1;
|
|
for (int i = 0; i < num_used_tint_times; i++) {
|
|
if (calendar_time_ns > tint_times_ns[i])
|
|
last_time_index = i;
|
|
}
|
|
if (calendar_time_ns <= tint_times_ns[0]) calendar_time_ns += (24 * 60 * 60 + 60 * 60 + 60) * SDL_NS_PER_SECOND;
|
|
|
|
Sint64 v = calendar_time_ns - tint_times_ns[last_time_index];
|
|
Sint64 time_between = tint_times_ns[last_time_index + 1] - tint_times_ns[last_time_index];
|
|
|
|
double t = v / (double)time_between;
|
|
|
|
V3 tint_color = lerp(time_tints[last_time_index], time_tints[(last_time_index + 1) % num_used_tint_times], t);
|
|
SDL_PushGPUFragmentUniformData(command_buffer, 0, &tint_color, sizeof(tint_color));
|
|
}
|
|
}
|
|
|
|
SDL_GPUColorTargetInfo color_target_info = {
|
|
.texture = enable_msaa ? msaa_texture : swapchain_texture,
|
|
.clear_color = { .r = 0.01f, .g = 0.01f, .b = 0.01f, .a = 1.0f },
|
|
.load_op = SDL_GPU_LOADOP_CLEAR,
|
|
.store_op = enable_msaa ? SDL_GPU_STOREOP_RESOLVE : SDL_GPU_STOREOP_STORE,
|
|
.resolve_texture = swapchain_texture,
|
|
.cycle = true,
|
|
};
|
|
|
|
SDL_GPURenderPass *render_pass = SDL_BeginGPURenderPass(command_buffer, &color_target_info, 1, NULL);
|
|
if (!render_pass) {
|
|
log_error("Failed to begin render pass. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
{ // Draw Map
|
|
ZoneScopedN("Draw Map");
|
|
SDL_GPUBufferBinding index_buffer_binding = { .buffer = index_buffer, .offset = 0 };
|
|
SDL_GPUBufferBinding vertex_buffers[] = {
|
|
{ .buffer = vertex_buffer, .offset = 0 },
|
|
{ .buffer = current_map.gpu_buffer, .offset = 0 },
|
|
};
|
|
SDL_GPUTextureSamplerBinding texture_bindings[] = {
|
|
{ .texture = tile_textures_array, .sampler = pixel_sampler },
|
|
};
|
|
|
|
SDL_BindGPUGraphicsPipeline(render_pass, world_graphics_pipeline);
|
|
SDL_BindGPUIndexBuffer(render_pass, &index_buffer_binding, SDL_GPU_INDEXELEMENTSIZE_16BIT);
|
|
SDL_BindGPUVertexBuffers(render_pass, 0, vertex_buffers, SDL_arraysize(vertex_buffers));
|
|
SDL_BindGPUVertexStorageBuffers(render_pass, 0, &tile_infos_buffer, 1);
|
|
SDL_BindGPUFragmentSamplers(render_pass, 0, texture_bindings, SDL_arraysize(texture_bindings));
|
|
SDL_DrawGPUIndexedPrimitives(render_pass, 6, current_map.height * current_map.width, 0, 0, 0);
|
|
}
|
|
|
|
{ // Draw Player
|
|
ZoneScopedN("Draw Player");
|
|
SDL_GPUBufferBinding vertex_buffers[] = {
|
|
{ .buffer = vertex_buffer, .offset = 0 },
|
|
{ .buffer = player_instance_buffer, .offset = 0 },
|
|
};
|
|
SDL_GPUTextureSamplerBinding texture_bindings[] = {
|
|
{ .texture = player_texture, .sampler = pixel_sampler },
|
|
};
|
|
|
|
SDL_BindGPUGraphicsPipeline(render_pass, basic_graphics_pipeline);
|
|
SDL_BindGPUVertexBuffers(render_pass, 0, vertex_buffers, SDL_arraysize(vertex_buffers));
|
|
SDL_BindGPUFragmentSamplers(render_pass, 0, texture_bindings, SDL_arraysize(texture_bindings));
|
|
SDL_DrawGPUIndexedPrimitives(render_pass, 6, 1, 0, 0, 0);
|
|
}
|
|
|
|
if (show_tile_picker) { // Draw Grid
|
|
ZoneScopedN("Draw Grid");
|
|
SDL_GPUBufferBinding index_buffer_binding = { .buffer = grid_index_buffer, .offset = 0 };
|
|
SDL_GPUBufferBinding vertex_buffers[] = {
|
|
{ .buffer = grid_vertex_buffer, .offset = 0 },
|
|
};
|
|
|
|
V4 tints[2] = {
|
|
{ 1.0f, 1.0f, 1.0f, 0.1f },
|
|
{ 1.0f, 0.0f, 1.0f, 1.0f },
|
|
};
|
|
|
|
SDL_BindGPUGraphicsPipeline(render_pass, grid_graphics_pipeline);
|
|
SDL_BindGPUIndexBuffer(render_pass, &index_buffer_binding, SDL_GPU_INDEXELEMENTSIZE_16BIT);
|
|
SDL_BindGPUVertexBuffers(render_pass, 0, vertex_buffers, SDL_arraysize(vertex_buffers));
|
|
SDL_PushGPUFragmentUniformData(command_buffer, 0, &tints, sizeof(tints));
|
|
SDL_DrawGPUIndexedPrimitives(render_pass, SDL_arraysize(grid_indices), current_map.height * current_map.width, 0, 0, 0);
|
|
}
|
|
|
|
SDL_EndGPURenderPass(render_pass);
|
|
|
|
{
|
|
ZoneScopedN("ImGui Render");
|
|
ImGui::Render();
|
|
ImDrawData *draw_data = ImGui::GetDrawData();
|
|
Imgui_ImplSDLGPU3_PrepareDrawData(draw_data, command_buffer);
|
|
|
|
SDL_GPUColorTargetInfo imgui_color_target_info = {
|
|
.texture = swapchain_texture,
|
|
.load_op = SDL_GPU_LOADOP_LOAD,
|
|
.store_op = SDL_GPU_STOREOP_STORE,
|
|
};
|
|
|
|
SDL_GPURenderPass *imgui_render_pass = SDL_BeginGPURenderPass(command_buffer, &imgui_color_target_info, 1, NULL);
|
|
ImGui_ImplSDLGPU3_RenderDrawData(draw_data, command_buffer, imgui_render_pass);
|
|
SDL_EndGPURenderPass(imgui_render_pass);
|
|
}
|
|
|
|
{
|
|
ZoneScopedN("SubmitGPUCommandBuffer");
|
|
if (!SDL_SubmitGPUCommandBuffer(command_buffer)) {
|
|
log_error("Failed to submit gpu command buffer (%s). Exiting.", SDL_GetError());
|
|
return 1;
|
|
}
|
|
}
|
|
FrameMark;
|
|
}
|
|
|
|
ImGui_ImplSDLGPU3_Shutdown();
|
|
ImGui_ImplSDL3_Shutdown();
|
|
ImGui::DestroyContext();
|
|
|
|
return 0;
|
|
}
|