1073 lines
38 KiB
C++
1073 lines
38 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 "smol-atlas.h"
|
|
#include "defer.h"
|
|
#include "log.h"
|
|
#include "m_string.h"
|
|
#include "math_graphics.h"
|
|
#include "load_entire_file.h"
|
|
#include "stb_image.h"
|
|
#include "../assets/shader/basic_vertex_shader.h"
|
|
#include "../assets/shader/basic_pixel_shader.h"
|
|
|
|
using namespace M;
|
|
|
|
SDL_GPUDevice *device;
|
|
SDL_Window *window;
|
|
|
|
SDL_GPUShader *basic_vertex_shader;
|
|
SDL_GPUShader *basic_pixel_shader;
|
|
|
|
SDL_GPUGraphicsPipeline *basic_graphics_pipeline;
|
|
SDL_GPUSampler *point_sampler;
|
|
|
|
SDL_GPUBuffer *vertex_buffer;
|
|
SDL_GPUBuffer *index_buffer;
|
|
SDL_GPUBuffer *tiles_instance_buffer;
|
|
SDL_GPUBuffer *player_instance_buffer;
|
|
|
|
Sint32 window_width;
|
|
Sint32 window_height;
|
|
|
|
bool Running = true;
|
|
|
|
#define view_width 16
|
|
#define view_height 9
|
|
|
|
struct Vertex {
|
|
V4 pos;
|
|
V2 uv0uv1;
|
|
};
|
|
|
|
Vertex vertices[] = {
|
|
{{ -1, 1, 1, 1 }, {0, 0}},
|
|
{{ 1, -1, 1, 1 }, {1, 1}},
|
|
{{ -1, -1, 1, 1 }, {0, 1}},
|
|
{{ 1, 1, 1, 1 }, {1, 0}},
|
|
};
|
|
|
|
Uint16 indices[] = {
|
|
0, 1, 2,
|
|
0, 3, 1,
|
|
};
|
|
|
|
struct Instance {
|
|
V4 pos_size;
|
|
Uint32 tile_type;
|
|
V4 uv0uv1;
|
|
};
|
|
|
|
Instance tiles_instances[view_width * view_height] = {
|
|
|
|
};
|
|
|
|
Instance player_instance = { { 0.5f + 0.5f / view_width, 0.5f, 1.0f / view_width, 1.0f / view_height}, 0, {0, 0, 1, 1}};
|
|
|
|
Uint32 map_width = view_width;
|
|
Uint32 map_height = view_height;
|
|
|
|
Uint32* map_tiles;
|
|
|
|
struct Player {
|
|
Sint64 pos_x;
|
|
Sint64 pos_y;
|
|
};
|
|
|
|
Player player = {
|
|
.pos_x = 8,
|
|
.pos_y = 6,
|
|
};
|
|
|
|
struct PerFrame {
|
|
float aspect_ratio;
|
|
float empty[3];
|
|
};
|
|
|
|
PerFrame per_frame = {1};
|
|
|
|
typedef struct {
|
|
Uint32 type;
|
|
const char *asset_path;
|
|
|
|
smol_atlas_item_t *atlas_item;
|
|
} TileInfo;
|
|
|
|
TileInfo tile_infos[] = {
|
|
{ 0x0001, "../assets/tiles/error.png" },
|
|
{ 0x0000, "../assets/tiles/empty.png" },
|
|
{ 0x0100, "../assets/tiles/grass_1.png" },
|
|
{ 0x0101, "../assets/tiles/grass_2.png" },
|
|
{ 0x0102, "../assets/tiles/grass_3.png" },
|
|
{ 0x0200, "../assets/tiles/ground_1.png" },
|
|
{ 0x0300, "../assets/tiles/water_1.png" },
|
|
{ 0x0301, "../assets/tiles/water_2.png" },
|
|
};
|
|
|
|
int tile_atlas_size = 256;
|
|
smol_atlas_t *tile_atlas;
|
|
|
|
Sint32 selected_tile = -1;
|
|
|
|
void save_map() {
|
|
log("Save file is under construction.");
|
|
FILE* file = fopen("../assets/map/map.sv", "wb");
|
|
|
|
if (!file) {
|
|
log_error("Save file creation has failed.");
|
|
return;
|
|
}
|
|
defer(fclose(file));
|
|
|
|
if (fwrite(&map_width, sizeof(Uint32), 1, file) != 1) {
|
|
log_error("fwrite for map_width has failed.");
|
|
return;
|
|
}
|
|
|
|
if (fwrite(&map_height, sizeof(Uint32), 1, file) != 1) {
|
|
log_error("fwrite for map_height has failed.");
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < map_width * map_height; i++) {
|
|
Uint32 type = tile_infos[map_tiles[i]].type;
|
|
if (fwrite(&type, sizeof(Uint32), 1, file) != 1) {
|
|
log_error("fwrite for tile_type at tile number %d has failed.", i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
log("Saving map was successful.");
|
|
}
|
|
|
|
void load_map() {
|
|
log("Load save file.");
|
|
String file = load_entire_file("../assets/map/map.sv");
|
|
if (!file.length) {
|
|
log_error("Loading save file has failed.");
|
|
return;
|
|
}
|
|
|
|
map_width = read<Uint32>(file);
|
|
map_height = read<Uint32>(file);
|
|
if (file.length != map_width * map_height * sizeof(Uint32)) {
|
|
log_error("Incorrect file.length.");
|
|
return;
|
|
}
|
|
|
|
if (map_tiles)
|
|
free(map_tiles);
|
|
|
|
map_tiles = (Uint32*)malloc(map_width * map_height * sizeof(Uint32));
|
|
|
|
for (int i = 0; i < map_width * map_height; i++) {
|
|
Uint32 type = read<Uint32>(file);
|
|
Uint32 kind = 0;
|
|
for (int i = 0; i < SDL_arraysize(tile_infos); i++) {
|
|
if (tile_infos[i].type == type) {
|
|
kind = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
map_tiles[i] = kind;
|
|
}
|
|
|
|
log("Loading map was successful.");
|
|
}
|
|
|
|
void change_map_size(char direction, int amount) {
|
|
Uint32* old_map = map_tiles;
|
|
auto old_map_width = map_width;
|
|
auto 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)] = 0;
|
|
}
|
|
}
|
|
|
|
player.pos_x = clamp(0, player.pos_x, map_width - 1);
|
|
player.pos_y = clamp(0, player.pos_y, map_height - 1);
|
|
|
|
free(old_map);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
SDL_GPUTexture *create_shader_texture(const char *path) {
|
|
int width = 0, height = 0, channels = 0;
|
|
stbi_uc *data = stbi_load(path, &width, &height, &channels, 0);
|
|
if (!data) {
|
|
log_error("Failed to load texture (\"%s\").", path);
|
|
return NULL;
|
|
}
|
|
|
|
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;
|
|
|
|
if (format == SDL_GPU_TEXTUREFORMAT_INVALID) {
|
|
log_error("Failed to find texture format for texture (\"%s\").", path);
|
|
stbi_image_free(data);
|
|
return NULL;
|
|
}
|
|
|
|
SDL_PropertiesID properties = SDL_CreateProperties();
|
|
if (properties) SDL_SetStringProperty(properties, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING, path);
|
|
|
|
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());
|
|
stbi_image_free(data);
|
|
return NULL;
|
|
}
|
|
if (properties) SDL_DestroyProperties(properties);
|
|
|
|
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);
|
|
stbi_image_free(data);
|
|
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);
|
|
stbi_image_free(data);
|
|
return NULL;
|
|
}
|
|
memcpy(transfer_data, data, upload_size);
|
|
SDL_UnmapGPUTransferBuffer(device, transfer_buffer);
|
|
stbi_image_free(data);
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
void blit(char *dst, Sint32 dst_width, Sint32 dst_x, Sint32 dst_y, char *src, Sint32 width, Sint32 height, int components = 4) {
|
|
for (Sint32 y = 0; y < height; y++)
|
|
memmove(&dst[((dst_y + y) * dst_width + dst_x) * components], &src[y * width * components], width * components);
|
|
}
|
|
|
|
bool SelectableImage(const char *label, bool selected, SDL_GPUTextureSamplerBinding *image, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1)) {
|
|
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->AddImage((ImTextureID)image, min + padding, max - padding, uv0, uv1);
|
|
|
|
return pressed;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) {
|
|
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, SDL_GPU_PRESENTMODE_VSYNC);
|
|
|
|
SDL_GPUShaderCreateInfo basic_vertex_shader_info = {
|
|
.code_size = SDL_arraysize(SPIRV_basic_vertex_shader),
|
|
.code = SPIRV_basic_vertex_shader,
|
|
.entrypoint = "main",
|
|
.format = SDL_GPU_SHADERFORMAT_SPIRV,
|
|
.stage = SDL_GPU_SHADERSTAGE_VERTEX,
|
|
|
|
.num_uniform_buffers = 1,
|
|
};
|
|
|
|
basic_vertex_shader = SDL_CreateGPUShader(device, &basic_vertex_shader_info);
|
|
if (!basic_vertex_shader) {
|
|
log_error("Failed to create basic vertex shader. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
SDL_GPUShaderCreateInfo basic_pixel_shader_info = {
|
|
.code_size = SDL_arraysize(SPIRV_basic_pixel_shader),
|
|
.code = SPIRV_basic_pixel_shader,
|
|
.entrypoint = "main",
|
|
.format = SDL_GPU_SHADERFORMAT_SPIRV,
|
|
.stage = SDL_GPU_SHADERSTAGE_FRAGMENT,
|
|
|
|
.num_samplers = 1,
|
|
};
|
|
|
|
basic_pixel_shader = SDL_CreateGPUShader(device, &basic_pixel_shader_info);
|
|
if (!basic_pixel_shader) {
|
|
log_error("Failed to create basic pixel shader. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
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_FLOAT4,
|
|
.offset = 0,
|
|
},
|
|
{
|
|
.location = 1,
|
|
.buffer_slot = 0,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
|
|
.offset = 16,
|
|
},
|
|
{
|
|
.location = 2,
|
|
.buffer_slot = 1,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4,
|
|
.offset = 0,
|
|
},
|
|
{
|
|
.location = 3,
|
|
.buffer_slot = 1,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_UINT,
|
|
.offset = 16,
|
|
},
|
|
{
|
|
.location = 4,
|
|
.buffer_slot = 1,
|
|
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4,
|
|
.offset = 20,
|
|
},
|
|
};
|
|
|
|
SDL_GPUColorTargetDescription color_target_descriptions[] = {
|
|
{
|
|
.format = SDL_GetGPUSwapchainTextureFormat(device, window),
|
|
.blend_state = {
|
|
.src_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE,
|
|
.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,
|
|
},
|
|
},
|
|
};
|
|
|
|
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,
|
|
},
|
|
.target_info = {
|
|
.color_target_descriptions = color_target_descriptions,
|
|
.num_color_targets = SDL_arraysize(color_target_descriptions),
|
|
},
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
for (int y = 0; y < view_height; y++) {
|
|
for (int x = 0; x < view_width; x++) {
|
|
tiles_instances[x + y * view_width].pos_size.zw = { 1.0f / view_width, 1.0f / view_height };
|
|
tiles_instances[x + y * view_width].pos_size.xy = { (float)x / view_width, (float)y / (float)view_height };
|
|
tiles_instances[x + y * view_width].pos_size.xy += tiles_instances[x + y * view_width].pos_size.zw * 0.5f;
|
|
tiles_instances[x + y * view_width].uv0uv1 = { 0, 0, 1, 1 };
|
|
}
|
|
}
|
|
|
|
SDL_GPUTexture *player_texture = create_shader_texture("../assets/decorations/strawberry.png");
|
|
if (!player_texture) {
|
|
log_error("Failed to create shader texture. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
tile_atlas = sma_atlas_create(tile_atlas_size, tile_atlas_size);
|
|
char *tile_atlas_texture_cpu = (char *)calloc(1, tile_atlas_size * tile_atlas_size * 4);
|
|
for (int i = 0; i < SDL_arraysize(tile_infos); i++) {
|
|
int width = 0, height = 0;
|
|
stbi_uc *data = stbi_load(tile_infos[i].asset_path, &width, &height, NULL, 4);
|
|
if (!data) {
|
|
log_error("Failed to load texture (\"%s\"). Exiting.", tile_infos[i].asset_path);
|
|
return 1;
|
|
}
|
|
|
|
tile_infos[i].atlas_item = sma_item_add(tile_atlas, width, height);
|
|
if (!tile_infos[i].atlas_item) {
|
|
log_error("Failed to add tile texture to atlas. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
blit(tile_atlas_texture_cpu, tile_atlas_size, sma_item_x(tile_infos[i].atlas_item), sma_item_y(tile_infos[i].atlas_item), (char *)data, width, height);
|
|
stbi_image_free(data);
|
|
}
|
|
SDL_GPUTexture *tile_atlas_texture = create_shader_texture("tile_atlas", tile_atlas_texture_cpu, tile_atlas_size, tile_atlas_size, 4);
|
|
free(tile_atlas_texture_cpu);
|
|
if (!tile_atlas_texture) {
|
|
log_error("Failed to create tile atlas texture. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
SDL_GPUSamplerCreateInfo point_sampler_info = {
|
|
.min_filter = SDL_GPU_FILTER_NEAREST,
|
|
.mag_filter = SDL_GPU_FILTER_NEAREST,
|
|
.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST,
|
|
|
|
.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,
|
|
};
|
|
|
|
point_sampler = SDL_CreateGPUSampler(device, &point_sampler_info);
|
|
|
|
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;
|
|
}
|
|
|
|
tiles_instance_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_VERTEX, sizeof(tiles_instances), tiles_instances, "tiles_instance_buffer");
|
|
if (!tiles_instance_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;
|
|
}
|
|
|
|
load_map();
|
|
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImGuiIO &io = ImGui::GetIO();
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
|
|
|
ImGui::StyleColorsDark();
|
|
|
|
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);
|
|
|
|
bool show_demo_window = true;
|
|
bool first_frame = true;
|
|
|
|
|
|
SDL_GetWindowSizeInPixels(window, &window_width, &window_height);
|
|
|
|
// MSG Message;
|
|
while (Running) {
|
|
ImGui_ImplSDLGPU3_NewFrame();
|
|
ImGui_ImplSDL3_NewFrame();
|
|
ImGui::NewFrame();
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(0, window_height), ImVec2(window_width, window_height));
|
|
ImGui::SetNextWindowSize(ImVec2(0.2 * window_width, window_height), ImGuiCond_FirstUseEver);
|
|
if (ImGui::Begin("Tile Picker", NULL, ImGuiWindowFlags_NoFocusOnAppearing)) {
|
|
if (SelectableImage("##tile", selected_tile == -1, NULL, ImVec2(32, 32))) {
|
|
selected_tile = -1;
|
|
}
|
|
|
|
for (int i = 0; i < SDL_arraysize(tile_infos); i++) {
|
|
ImGui::PushID(i);
|
|
SDL_GPUTextureSamplerBinding texture_binding = { .texture = tile_atlas_texture, .sampler = point_sampler };
|
|
|
|
ImVec2 uv0 = ImVec2(sma_item_x(tile_infos[i].atlas_item) / (float)tile_atlas_size, sma_item_y(tile_infos[i].atlas_item) / (float)tile_atlas_size);
|
|
ImVec2 uv1 = ImVec2(uv0.x + sma_item_width(tile_infos[i].atlas_item) / (float)tile_atlas_size, uv0.y + sma_item_height(tile_infos[i].atlas_item) / (float)tile_atlas_size);
|
|
|
|
float available = ImGui::GetContentRegionAvail().x - ImGui::GetItemRectMax().x;
|
|
if (available >= 32)
|
|
ImGui::SameLine();
|
|
|
|
if (SelectableImage("##tile", selected_tile == i, &texture_binding, ImVec2(32, 32), uv0, uv1)) {
|
|
selected_tile = i;
|
|
}
|
|
|
|
ImGui::PopID();
|
|
}
|
|
}
|
|
ImGui::End();
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&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;
|
|
|
|
if (event.key.key == SDLK_UP || event.key.key == SDLK_W) {
|
|
player.pos_y = clamp(0, player.pos_y - 1, map_height - 1);
|
|
}
|
|
|
|
if (event.key.key == SDLK_LEFT || event.key.key == SDLK_A) {
|
|
player.pos_x = clamp(0, player.pos_x - 1, map_width - 1);
|
|
}
|
|
|
|
if (event.key.key == SDLK_DOWN || event.key.key == SDLK_S) {
|
|
player.pos_y = clamp(0, player.pos_y + 1, map_height - 1);
|
|
}
|
|
|
|
if (event.key.key == SDLK_RIGHT || event.key.key == SDLK_D) {
|
|
player.pos_x = clamp(0, player.pos_x + 1, map_width - 1);
|
|
}
|
|
|
|
if (event.key.key == SDLK_F1) {
|
|
save_map();
|
|
}
|
|
|
|
if (event.key.key == SDLK_F4) {
|
|
load_map();
|
|
}
|
|
} break;
|
|
|
|
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
|
|
if (io.WantCaptureMouse)
|
|
continue;
|
|
|
|
if (selected_tile == -1)
|
|
continue;
|
|
|
|
float mouse_x = event.button.x;
|
|
float mouse_y = event.button.y;
|
|
|
|
V2 X = V2{ -1, 1 };
|
|
V2 Y = V2{ -1, 1 };
|
|
|
|
float correction_factor = (window_width / (float)window_height) / (16.0 / 9.0);
|
|
if (correction_factor < 1)
|
|
Y *= correction_factor;
|
|
else
|
|
X /= correction_factor;
|
|
|
|
float m_x = remap(0, window_width, -1, 1, mouse_x);
|
|
float m_y = remap(0, window_height, -1, 1, mouse_y);
|
|
|
|
mouse_x = remap(X.x, X.y, 0, window_width, m_x);
|
|
mouse_y = remap(Y.x, Y.y, 0, window_height, m_y);
|
|
|
|
float tile_width = window_width / (float) view_width;
|
|
float tile_height = window_height / (float) view_height;
|
|
|
|
int tile_x = (int)(mouse_x / tile_width) + player.pos_x - (view_width / 2);
|
|
int tile_y = (int)(mouse_y / tile_height) + player.pos_y - (view_height / 2);
|
|
|
|
if (mouse_x < 0)
|
|
tile_x = -1;
|
|
if (mouse_y< 0)
|
|
tile_y = -1;
|
|
|
|
if(0 <= tile_x && tile_x < map_width &&
|
|
0 <= tile_y && tile_y < map_height)
|
|
map_tiles[tile_x + map_width * tile_y] = selected_tile;
|
|
|
|
SDL_Keymod modifiers = SDL_GetModState();
|
|
if (modifiers & SDL_KMOD_SHIFT && tile_x == -1) {
|
|
if(modifiers & SDL_KMOD_CTRL)
|
|
change_map_size('W', -1);
|
|
else
|
|
change_map_size('W', 2);
|
|
}
|
|
|
|
if (modifiers & SDL_KMOD_SHIFT && tile_x == map_width) {
|
|
if (modifiers & SDL_KMOD_CTRL)
|
|
change_map_size('E', -1);
|
|
else
|
|
change_map_size('E', 2);
|
|
}
|
|
|
|
if (modifiers & SDL_KMOD_SHIFT && tile_y == -1) {
|
|
if (modifiers & SDL_KMOD_CTRL)
|
|
change_map_size('N', -1);
|
|
else
|
|
change_map_size('N', 2);
|
|
}
|
|
|
|
if (modifiers & SDL_KMOD_SHIFT && tile_y == map_height) {
|
|
if (modifiers & SDL_KMOD_CTRL)
|
|
change_map_size('S', -1);
|
|
else
|
|
change_map_size('S', 2);
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
//Tiles updaten
|
|
for (int y = 0; y < view_height; y++) {
|
|
for (int x = 0; x < view_width; x++) {
|
|
if (x + player.pos_x - view_width / 2 >= 0 && x + player.pos_x - view_width / 2 < map_width &&
|
|
y + player.pos_y - view_height / 2 >= 0 && y + player.pos_y - view_height / 2 < map_height) {
|
|
tiles_instances[x + y * view_width].tile_type = map_tiles[(x + player.pos_x - view_width / 2) + (y + player.pos_y - view_height / 2) * map_width];
|
|
} else {
|
|
tiles_instances[x + y * view_width].tile_type = 0;
|
|
}
|
|
|
|
Uint32 type = tiles_instances[x + y * view_width].tile_type;
|
|
|
|
float x0 = sma_item_x(tile_infos[type].atlas_item);
|
|
float y0 = sma_item_y(tile_infos[type].atlas_item);
|
|
float x1 = x0 + sma_item_width(tile_infos[type].atlas_item);
|
|
float y1 = y0 + sma_item_height(tile_infos[type].atlas_item);
|
|
|
|
tiles_instances[x + y * view_width].uv0uv1 = { x0 / tile_atlas_size, y0 / tile_atlas_size, x1 / tile_atlas_size, y1 / tile_atlas_size };
|
|
}
|
|
}
|
|
|
|
if (!swapchain_texture) {
|
|
SDL_CancelGPUCommandBuffer(command_buffer);
|
|
ImGui::Render();
|
|
ImDrawData *draw_data = ImGui::GetDrawData();
|
|
continue;
|
|
}
|
|
|
|
per_frame.aspect_ratio = (float) window_width / (float) window_height;
|
|
SDL_PushGPUVertexUniformData(command_buffer, 0, &per_frame, sizeof(per_frame));
|
|
|
|
if (!update_buffer(tiles_instance_buffer, 0, sizeof(tiles_instances), tiles_instances)) {
|
|
log_error("Failed to update buffer. Exiting.");
|
|
return 1;
|
|
}
|
|
|
|
SDL_GPUColorTargetInfo color_target_info = {
|
|
.texture = swapchain_texture,
|
|
.clear_color = { .r = 1.0f, .g = 0.0f, .b = 1.0f, .a = 1.0f },
|
|
.load_op = SDL_GPU_LOADOP_CLEAR,
|
|
.store_op = SDL_GPU_STOREOP_STORE,
|
|
};
|
|
|
|
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
|
|
SDL_GPUBufferBinding index_buffer_binding = { .buffer = index_buffer, .offset = 0 };
|
|
SDL_GPUBufferBinding vertex_buffers[] = {
|
|
{ .buffer = vertex_buffer, .offset = 0 },
|
|
{ .buffer = tiles_instance_buffer, .offset = 0 },
|
|
};
|
|
SDL_GPUTextureSamplerBinding texture_bindings[] = {
|
|
{ .texture = tile_atlas_texture, .sampler = point_sampler },
|
|
};
|
|
|
|
SDL_BindGPUGraphicsPipeline(render_pass, basic_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_BindGPUFragmentSamplers(render_pass, 0, texture_bindings, SDL_arraysize(texture_bindings));
|
|
SDL_DrawGPUIndexedPrimitives(render_pass, 6, view_width * view_height, 0, 0, 0);
|
|
}
|
|
|
|
{ // 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 = point_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);
|
|
}
|
|
SDL_EndGPURenderPass(render_pass);
|
|
|
|
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);
|
|
|
|
if (!SDL_SubmitGPUCommandBuffer(command_buffer)) {
|
|
log_error("Failed to submit gpu command buffer (%s). Exiting.", SDL_GetError());
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|