Merging with new base, dumping obj files to raw binary and loading
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,3 +13,4 @@ data.txt
|
||||
*.vcxproj*
|
||||
*.rdbg
|
||||
*.sublime*
|
||||
*.bin
|
||||
454
base_string.cpp
Normal file
454
base_string.cpp
Normal file
@@ -0,0 +1,454 @@
|
||||
|
||||
function U8
|
||||
to_lower_case(U8 a) {
|
||||
if (a >= 'A' && a <= 'Z')
|
||||
a += 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
function U8
|
||||
to_upper_case(U8 a) {
|
||||
if (a >= 'a' && a <= 'z')
|
||||
a -= 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
function U8
|
||||
char_to_lower(U8 c){
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
c += 32;
|
||||
return c;
|
||||
}
|
||||
|
||||
function U8
|
||||
char_to_upper(U8 c){
|
||||
if(c >= 'a' && c <= 'z')
|
||||
c -= 32;
|
||||
return c;
|
||||
}
|
||||
|
||||
function B32
|
||||
is_whitespace(U8 w) {
|
||||
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||
return result;
|
||||
}
|
||||
|
||||
function B32
|
||||
is_alphabetic(U8 a) {
|
||||
if ((a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function B32
|
||||
is_number(U8 a) {
|
||||
B32 result = a >= '0' && a <= '9';
|
||||
return result;
|
||||
}
|
||||
|
||||
function B32
|
||||
is_alphanumeric(U8 a) {
|
||||
B32 result = is_number(a) || is_alphabetic(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
function B32
|
||||
string_compare(String a, String b, B32 ignore_case = false) {
|
||||
if (a.len != b.len)
|
||||
return false;
|
||||
for (S64 i = 0; i < a.len; i++) {
|
||||
U8 A = a.str[i];
|
||||
U8 B = b.str[i];
|
||||
if (ignore_case) {
|
||||
A = to_lower_case(A);
|
||||
B = to_lower_case(B);
|
||||
}
|
||||
if (A != B)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function B32
|
||||
operator==(String a, String b){
|
||||
return string_compare(a,b);
|
||||
}
|
||||
|
||||
function String
|
||||
string_copy(Allocator *a, String string){
|
||||
U8 *copy = exp_alloc_array(a, U8, string.len+1);
|
||||
memory_copy(copy, string.str, string.len);
|
||||
copy[string.len] = 0;
|
||||
return (String){copy, string.len};
|
||||
}
|
||||
|
||||
function String
|
||||
string_fmtv(Allocator *a, const char *str, va_list args1) {
|
||||
va_list args2;
|
||||
va_copy(args2, args1);
|
||||
S64 len = stbsp_vsnprintf(0, 0, str, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *result = exp_alloc_array(a, char, len + 1);
|
||||
stbsp_vsnprintf(result, len + 1, str, args1);
|
||||
|
||||
String res = {(U8 *)result, len};
|
||||
return res;
|
||||
}
|
||||
|
||||
#define STRING_FMT(alloc, str, result) \
|
||||
va_list args1; \
|
||||
va_start(args1, str); \
|
||||
String result = string_fmtv(alloc, str, args1); \
|
||||
va_end(args1)
|
||||
|
||||
function String
|
||||
string_fmt(Allocator *a, const char *str, ...) {
|
||||
STRING_FMT(a, str, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// String builder
|
||||
//-----------------------------------------------------------------------------
|
||||
struct String_Builder_Block{
|
||||
String_Builder_Block *next;
|
||||
S64 cap;
|
||||
S64 len;
|
||||
U8 data[0];
|
||||
};
|
||||
|
||||
struct String_Builder{
|
||||
Allocator *allocator;
|
||||
String_Builder_Block *first;
|
||||
String_Builder_Block *last;
|
||||
|
||||
void push_block(SizeU size){
|
||||
auto *block = (String_Builder_Block *)exp_alloc(allocator, sizeof(String_Builder_Block) + size);
|
||||
memory_zero(block, sizeof(String_Builder_Block)+1); // Also clear first byte of character data
|
||||
block->cap = size;
|
||||
SLLQueuePush(first, last, block);
|
||||
}
|
||||
|
||||
void init(S64 size = 4096){
|
||||
assert(allocator);
|
||||
push_block(size);
|
||||
}
|
||||
|
||||
void append_data(void *data, S64 size){
|
||||
if(first == 0){
|
||||
init();
|
||||
}
|
||||
|
||||
S64 remaining_cap = last->cap - last->len;
|
||||
if(size > remaining_cap){
|
||||
S64 new_block_size = max(last->cap*2, size*2);
|
||||
push_block(new_block_size);
|
||||
}
|
||||
|
||||
U8 *write_address = last->data + last->len;
|
||||
last->len += size;
|
||||
memory_copy(write_address, data, size);
|
||||
}
|
||||
};
|
||||
|
||||
function String_Builder
|
||||
string_builder_make(Allocator *a, S64 first_block_size = 4096){
|
||||
String_Builder sb = {a};
|
||||
sb.init(first_block_size);
|
||||
return sb;
|
||||
}
|
||||
|
||||
function void
|
||||
appendf(String_Builder *b, const char *str, ...){
|
||||
if(b->first == 0){
|
||||
b->init();
|
||||
}
|
||||
va_list args, args2;
|
||||
va_start(args, str);
|
||||
retry:{
|
||||
String_Builder_Block *block = b->last;
|
||||
int block_size = block->cap - block->len;
|
||||
char *write_address = (char *)block->data + block->len;
|
||||
|
||||
va_copy(args2, args);
|
||||
int written = vsnprintf(write_address, block_size, str, args2);
|
||||
va_end(args2);
|
||||
|
||||
if(written > block_size){
|
||||
int new_block_size = max(4096, (written+1)*2);
|
||||
b->push_block(new_block_size);
|
||||
goto retry;
|
||||
}
|
||||
block->len += written;
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
enum String_Builder_Flag{
|
||||
String_Builder_Flag_None = 0,
|
||||
String_Builder_Flag_AddSize = 0,
|
||||
};
|
||||
|
||||
function String
|
||||
string_flatten(String_Builder *b, String_Builder_Flag flags = String_Builder_Flag_None){
|
||||
// @Note(Krzosa): Only single block, no need to flatten, vsnprintf null terminates too
|
||||
if(b->first == b->last){
|
||||
String result = {b->first->data, b->first->len};
|
||||
return result;
|
||||
}
|
||||
|
||||
// @Note(Krzosa): Compute size to allocate
|
||||
S64 size = 1;
|
||||
if(is_flag_set(flags, String_Builder_Flag_AddSize)) size += sizeof(SizeU);
|
||||
IterList(b){
|
||||
size += it->len;
|
||||
}
|
||||
|
||||
String result = {};
|
||||
result.str = (U8 *)exp_alloc(b->allocator, size);
|
||||
if(is_flag_set(flags, String_Builder_Flag_AddSize)) {
|
||||
memory_copy(result.str + result.len, &size, sizeof(S64));
|
||||
result.len += sizeof(S64);
|
||||
}
|
||||
|
||||
// @Note(Krzosa): Copy the content of each block into the string
|
||||
IterList(b){
|
||||
memory_copy(result.str + result.len, it->data, it->len);
|
||||
result.len += it->len;
|
||||
}
|
||||
|
||||
result.str[result.len] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
function void
|
||||
test_string_builder(){
|
||||
Scratch scratch;
|
||||
String_Builder sb = string_builder_make(scratch, 4);
|
||||
appendf(&sb, "Thing, %d", 242252);
|
||||
String f = string_flatten(&sb);
|
||||
assert(string_compare(f, "Thing, 242252"_s));
|
||||
appendf(&sb, "-%f %f %f", 23.0, 42.29, 2925.2);
|
||||
f = string_flatten(&sb);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
function void
|
||||
string_path_normalize(String s) {
|
||||
for (S64 i = 0; i < s.len; i++) {
|
||||
if (s.str[i] == '\\')
|
||||
s.str[i] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
function String
|
||||
string_make(char *str, S64 len) {
|
||||
String result;
|
||||
result.str = (U8 *)str;
|
||||
result.len = len;
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_make(U8 *str, S64 len) {
|
||||
return string_make((char*)str, len);
|
||||
}
|
||||
|
||||
function String
|
||||
string_chop(String string, S64 len) {
|
||||
len = clamp_top(len, string.len);
|
||||
String result = string_make(string.str, string.len - len);
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_skip(String string, S64 len) {
|
||||
len = clamp_top(len, string.len);
|
||||
S64 remain = string.len - len;
|
||||
String result = string_make(string.str + len, remain);
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_get_postfix(String string, S64 len) {
|
||||
len = clamp_top(len, string.len);
|
||||
S64 remain_len = string.len - len;
|
||||
String result = string_make(string.str + remain_len, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_get_prefix(String string, S64 len) {
|
||||
len = clamp_top(len, string.len);
|
||||
String result = string_make(string.str, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_slice(String string, S64 first_index, S64 one_past_last_index) {
|
||||
assert_msg(first_index < one_past_last_index, "string_slice, first_index is bigger then one_past_last_index");
|
||||
assert_msg(string.len > 0, "Slicing string of length 0! Might be an error!");
|
||||
String result = string;
|
||||
if (string.len > 0) {
|
||||
if (one_past_last_index > first_index) {
|
||||
first_index = clamp_top(first_index, string.len - 1);
|
||||
one_past_last_index = clamp_top(one_past_last_index, string.len);
|
||||
result.str += first_index;
|
||||
result.len = one_past_last_index - first_index;
|
||||
}
|
||||
else {
|
||||
result.len = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_trim(String string) {
|
||||
if (string.len == 0) return string;
|
||||
|
||||
|
||||
S64 whitespace_begin = 0;
|
||||
for (; whitespace_begin < string.len; whitespace_begin++) {
|
||||
if (!is_whitespace(string.str[whitespace_begin])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
S64 whitespace_end = string.len;
|
||||
for (; whitespace_end != whitespace_begin; whitespace_end--) {
|
||||
if (!is_whitespace(string.str[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (whitespace_begin == whitespace_end) {
|
||||
string.len = 0;
|
||||
}
|
||||
else {
|
||||
string = string_slice(string, whitespace_begin, whitespace_end);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
function String
|
||||
string_trim_end(String string) {
|
||||
S64 whitespace_end = string.len;
|
||||
for (; whitespace_end != 0; whitespace_end--) {
|
||||
if (!is_whitespace(string.str[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String result = string_get_prefix(string, whitespace_end);
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_to_lower_case(Allocator *arena, String s) {
|
||||
String copy = string_copy(arena, s);
|
||||
for (U64 i = 0; i < copy.len; i++) {
|
||||
copy.str[i] = to_lower_case(copy.str[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
function String
|
||||
string_to_upper_case(Allocator *arena, String s) {
|
||||
String copy = string_copy(arena, s);
|
||||
for (U64 i = 0; i < copy.len; i++) {
|
||||
copy.str[i] = to_upper_case(copy.str[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
typedef U64 MatchFlag;
|
||||
enum {
|
||||
MatchFlag_None=0,
|
||||
MatchFlag_FindLast=1,
|
||||
MatchFlag_IgnoreCase=2,
|
||||
};
|
||||
|
||||
function B32
|
||||
string_find(String string, String find, MatchFlag flags, S64 *index_out) {
|
||||
B32 result = false;
|
||||
if (flags & MatchFlag_FindLast) {
|
||||
for (S64 i = string.len; i != 0; i--) {
|
||||
S64 index = i - 1;
|
||||
String substring = string_slice(string, index, index + find.len);
|
||||
if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) {
|
||||
if (index_out)
|
||||
*index_out = index;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (S64 i = 0; i < string.len; i++) {
|
||||
String substring = string_slice(string, i, i + find.len);
|
||||
if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) {
|
||||
if (index_out)
|
||||
*index_out = i;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_chop_last_slash(String s) {
|
||||
String result = s;
|
||||
string_find(s, "/"_s, MatchFlag_FindLast, &result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_chop_last_period(String s) {
|
||||
String result = s;
|
||||
string_find(s, "."_s, MatchFlag_FindLast, &result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_skip_to_last_slash(String s) {
|
||||
S64 pos;
|
||||
String result = s;
|
||||
if (string_find(s, "/"_s, MatchFlag_FindLast, &pos)) {
|
||||
result = string_skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function String
|
||||
string_skip_to_last_period(String s) {
|
||||
S64 pos;
|
||||
String result = s;
|
||||
if (string_find(s, "."_s, MatchFlag_FindLast, &pos)) {
|
||||
result = string_skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
function void
|
||||
test_stb_sprintf(){
|
||||
char buff[1024];
|
||||
const char *asd = "adfag";
|
||||
int len = snprintf(buff, 1024, "Thing %s", asd);
|
||||
assert(string_make(buff,len) == "Thing adfag"_s);
|
||||
|
||||
String thing = "Thing12312412412412412"_s;
|
||||
thing.len = 5;
|
||||
len = snprintf(buff, 1024, "%Q %Q", thing, string_make(thing.str + 5, 5));
|
||||
__debugbreak();
|
||||
}
|
||||
#endif
|
||||
4
build.bat
Normal file
4
build.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
|
||||
|
||||
clang main.cpp -Wall -Wno-unused-function -Wno-missing-braces -fno-exceptions -fdiagnostics-absolute-paths -g -I"C:/base" -I".." -o main.exe -Wl,user32.lib
|
||||
899
kpl_multimedia.cpp
Normal file
899
kpl_multimedia.cpp
Normal file
@@ -0,0 +1,899 @@
|
||||
#include <Windows.h>
|
||||
#include <shellscalingapi.h>
|
||||
#include <GL/Gl.h>
|
||||
#include <intrin.h>
|
||||
|
||||
#pragma comment(linker, "/subsystem:windows")
|
||||
#pragma comment(lib, "gdi32.lib")
|
||||
#pragma comment(lib, "Shcore.lib")
|
||||
#pragma comment(lib, "opengl32.lib")
|
||||
|
||||
// Symbols taken from GLFW
|
||||
//
|
||||
// Executables (but not DLLs) exporting this symbol with this value will be
|
||||
// automatically directed to the high-performance GPU on Nvidia Optimus systems
|
||||
// with up-to-date drivers
|
||||
//
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
|
||||
|
||||
// Executables (but not DLLs) exporting this symbol with this value will be
|
||||
// automatically directed to the high-performance GPU on AMD PowerXpress systems
|
||||
// with up-to-date drivers
|
||||
//
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
|
||||
|
||||
|
||||
OS os;
|
||||
#include "math.h"
|
||||
|
||||
typedef HRESULT tSetProcessDpiAwareness(PROCESS_DPI_AWARENESS);
|
||||
typedef MMRESULT TimeBeginPeriod(MMRESULT);
|
||||
constexpr DWORD window_style_simplified = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
|
||||
constexpr DWORD window_style_resizable = WS_OVERLAPPEDWINDOW;
|
||||
constexpr DWORD window_style_borderless = WS_POPUP;
|
||||
|
||||
struct Win32Bitmap {
|
||||
Bitmap bitmap;
|
||||
HDC dc;
|
||||
HBITMAP dib;
|
||||
};
|
||||
|
||||
struct Win32FontCtx {
|
||||
HFONT font;
|
||||
Win32Bitmap bitmap;
|
||||
TEXTMETRIC text_metric;
|
||||
Font result;
|
||||
};
|
||||
|
||||
struct OS_Win32 {
|
||||
HWND window;
|
||||
HDC window_dc;
|
||||
Win32Bitmap screen;
|
||||
HINSTANCE instance;
|
||||
int show_cmd;
|
||||
char *cmd_line;
|
||||
bool good_scheduling;
|
||||
void *main_fiber;
|
||||
void *msg_fiber;
|
||||
U8 text_buff[32];
|
||||
Vec2I prev_window_size;
|
||||
Vec2I prev_mouse_pos;
|
||||
};
|
||||
static_assert(sizeof(OS_Win32) < 256, "Too big");
|
||||
#define w32(a) (*(OS_Win32 *)(a).platform)
|
||||
|
||||
api Bitmap bitmap(U32 *pixels, Vec2I size, Vec2 align_in_pixels = {}) {
|
||||
Bitmap result;
|
||||
result.pixels = pixels;
|
||||
result.size = size;
|
||||
result.align = align_in_pixels;
|
||||
return result;
|
||||
}
|
||||
|
||||
function Win32Bitmap win32_create_bitmap(Vec2I size) {
|
||||
Win32Bitmap result;
|
||||
result.bitmap.size = size;
|
||||
if (result.bitmap.size.y < 0)
|
||||
result.bitmap.size.y = -result.bitmap.size.y;
|
||||
|
||||
HDC hdc = GetDC(0);
|
||||
BITMAPINFO bminfo = {};
|
||||
bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader);
|
||||
bminfo.bmiHeader.biWidth = (LONG)size.x;
|
||||
bminfo.bmiHeader.biHeight = (LONG)-size.y;
|
||||
bminfo.bmiHeader.biPlanes = 1;
|
||||
bminfo.bmiHeader.biBitCount = 32;
|
||||
bminfo.bmiHeader.biCompression = BI_RGB; // AA RR GG BB
|
||||
bminfo.bmiHeader.biXPelsPerMeter = 1;
|
||||
bminfo.bmiHeader.biYPelsPerMeter = 1;
|
||||
|
||||
void *mem = 0;
|
||||
result.dib = CreateDIBSection(hdc, &bminfo, DIB_RGB_COLORS, (void **)&mem, 0, 0);
|
||||
assert_msg(mem != 0, "Failed to create win32 bitmap");
|
||||
result.dc = CreateCompatibleDC(hdc);
|
||||
result.bitmap.pixels = (U32 *)mem;
|
||||
return result;
|
||||
}
|
||||
|
||||
function void win32_destroy_bitmap(Win32Bitmap *b) {
|
||||
if (b->bitmap.pixels) {
|
||||
b->bitmap.pixels = 0;
|
||||
DeleteDC(b->dc);
|
||||
DeleteObject(b->dib);
|
||||
}
|
||||
}
|
||||
|
||||
function LRESULT
|
||||
CALLBACK win32_window_proc(HWND window, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
EventKind kind = EventKind_None;
|
||||
Key key = Key_None;
|
||||
LRESULT result = 0;
|
||||
switch (uMsg) {
|
||||
case WM_CLOSE:
|
||||
DestroyWindow(window);
|
||||
os.quit = 1;
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
os.quit = 1;
|
||||
break;
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_KEYDOWN: {
|
||||
kind = EventKind_KeyDown;
|
||||
switch (wParam) {
|
||||
|
||||
#define X(button, win) \
|
||||
case win: \
|
||||
key = Key_##button; \
|
||||
break;
|
||||
|
||||
KEY_MAPPING
|
||||
}
|
||||
} break;
|
||||
case WM_SYSKEYUP:
|
||||
case WM_KEYUP: {
|
||||
kind = EventKind_KeyUp;
|
||||
switch (wParam) {
|
||||
KEY_MAPPING
|
||||
#undef X
|
||||
}
|
||||
} break;
|
||||
case WM_TIMER: {
|
||||
SwitchToFiber(w32(os).main_fiber);
|
||||
} break;
|
||||
case WM_ENTERMENULOOP:
|
||||
case WM_ENTERSIZEMOVE: {
|
||||
SetTimer(w32(os).window, 0, 1, 0);
|
||||
} break;
|
||||
case WM_EXITMENULOOP:
|
||||
case WM_EXITSIZEMOVE: {
|
||||
KillTimer(w32(os).window, 0);
|
||||
} break;
|
||||
case WM_MOUSEMOVE: {
|
||||
POINT p;
|
||||
GetCursorPos(&p);
|
||||
ScreenToClient(w32(os).window, &p);
|
||||
os.mouse_pos = vec2i(p.x, p.y);
|
||||
os.mouse_pos.y = os.window_size.y - os.mouse_pos.y;
|
||||
kind = EventKind_MouseMove;
|
||||
} break;
|
||||
case WM_LBUTTONDOWN: {
|
||||
kind = EventKind_KeyDown;
|
||||
key = Key_MouseLeft;
|
||||
} break;
|
||||
case WM_LBUTTONUP: {
|
||||
kind = EventKind_KeyUp;
|
||||
key = Key_MouseLeft;
|
||||
} break;
|
||||
case WM_RBUTTONDOWN: {
|
||||
kind = EventKind_KeyDown;
|
||||
key = Key_MouseRight;
|
||||
} break;
|
||||
case WM_RBUTTONUP: {
|
||||
kind = EventKind_KeyUp;
|
||||
key = Key_MouseRight;
|
||||
} break;
|
||||
case WM_MBUTTONDOWN: {
|
||||
kind = EventKind_KeyDown;
|
||||
key = Key_MouseMiddle;
|
||||
} break;
|
||||
case WM_MBUTTONUP: {
|
||||
kind = EventKind_KeyUp;
|
||||
key = Key_MouseMiddle;
|
||||
} break;
|
||||
case WM_MOUSEWHEEL: {
|
||||
if ((int)wParam > 0)
|
||||
os.mouse_wheel = 1;
|
||||
else
|
||||
os.mouse_wheel = -1;
|
||||
kind = EventKind_MouseWheel;
|
||||
} break;
|
||||
case WM_CHAR: {
|
||||
// @Todo: Can this overflow?
|
||||
//kind = EventKind_KeyboardText;
|
||||
//U32 codepoint = '?';
|
||||
//utf16_to_utf32((U16 *)&wParam, &codepoint);
|
||||
//os.text.len += utf32_to_utf8(codepoint, w32(os).text_buff + os.text.len);
|
||||
} break;
|
||||
default:
|
||||
result = DefWindowProcW(window, uMsg, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
|
||||
if (kind) {
|
||||
if (kind == EventKind_KeyDown) {
|
||||
if (os.key[key].down == 0)
|
||||
os.key[key].pressed = 1;
|
||||
os.key[key].down = 1;
|
||||
} else if (kind == EventKind_KeyUp) {
|
||||
os.key[key].released = 1;
|
||||
os.key[key].down = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function void
|
||||
CALLBACK _os_fiber_event_proc(void *data) {
|
||||
unused(data);
|
||||
for (;;) {
|
||||
MSG msg;
|
||||
while (PeekMessageW(&msg, w32(os).window, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
SwitchToFiber(w32(os).main_fiber);
|
||||
}
|
||||
}
|
||||
|
||||
function Vec2I
|
||||
get_window_size(HWND window) {
|
||||
Vec2I result;
|
||||
RECT window_rect;
|
||||
GetClientRect(window, &window_rect);
|
||||
result.x = window_rect.right - window_rect.left;
|
||||
result.y = window_rect.bottom - window_rect.top;
|
||||
return result;
|
||||
}
|
||||
|
||||
function Vec2I
|
||||
get_window_size_with_border(HWND window) {
|
||||
RECT ClientRect;
|
||||
GetWindowRect(window, &ClientRect);
|
||||
Vec2I draw_area;
|
||||
draw_area.x = (ClientRect.right - ClientRect.left);
|
||||
draw_area.y = (ClientRect.bottom - ClientRect.top);
|
||||
return draw_area;
|
||||
}
|
||||
|
||||
function Vec2I
|
||||
get_border_size(HWND window) {
|
||||
Vec2I client = get_window_size(window);
|
||||
Vec2I wind_size = get_window_size_with_border(window);
|
||||
Vec2I result = vec2i(wind_size.x - client.x, wind_size.y - client.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
api void os_quit() { os.quit = 1; }
|
||||
api void os_set_fps(F64 fps) { os.ms_per_frame = 1 / fps; }
|
||||
api void os_set_ms_per_frame(F64 ms) { os.ms_per_frame = ms; }
|
||||
|
||||
api void os_show_cursor(B32 status) {
|
||||
ShowCursor(status);
|
||||
os.cursor_visible = status;
|
||||
}
|
||||
|
||||
api void os_set_window_title(String title) {
|
||||
Scratch scratch;
|
||||
BOOL result = SetWindowTextA(w32(os).window, (char *)title.str);
|
||||
assert_msg(result != 0, "Failed to set window title");
|
||||
os.window_title = title;
|
||||
}
|
||||
|
||||
api void os_set_window_size(S32 x, S32 y) {
|
||||
Vec2I border = get_border_size(w32(os).window);
|
||||
int actual_width = (int)(x + border.x);
|
||||
int actual_height = (int)(y + border.y);
|
||||
bool result =
|
||||
SetWindowPos(w32(os).window, 0, 0, 0, actual_width, actual_height, SWP_NOMOVE | SWP_NOOWNERZORDER);
|
||||
assert_msg(result, "SetWindowPos returned invalid value");
|
||||
os.window_size = get_window_size(w32(os).window);
|
||||
}
|
||||
|
||||
api void os_pull_state() {
|
||||
os.window_was_resized = false;
|
||||
os.window_size = get_window_size(w32(os).window);
|
||||
if (os.window_size != w32(os).prev_window_size) {
|
||||
os.window_was_resized = true;
|
||||
w32(os).prev_window_size = os.window_size;
|
||||
}
|
||||
os.monitor_size = vec2i(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
|
||||
|
||||
// @Note: Client relative
|
||||
POINT point = {0, 0};
|
||||
ClientToScreen(w32(os).window, &point);
|
||||
os.window_pos.x = point.x;
|
||||
os.window_pos.y = point.y;
|
||||
|
||||
// @Note: Get DPI scale
|
||||
UINT dpi = GetDpiForWindow(w32(os).window);
|
||||
assert_msg(dpi != 0, "Failed to get dpi for window");
|
||||
os.dpi_scale = (F32)dpi / 96.f;
|
||||
|
||||
// @Note: Reset text
|
||||
os.text.len = 0;
|
||||
// @Note: Reset keys
|
||||
for (int i = 0; i < Key_Count; i++) {
|
||||
os.key[i].released = 0;
|
||||
os.key[i].pressed = 0;
|
||||
}
|
||||
os.mouse_wheel = 0;
|
||||
SwitchToFiber(w32(os).msg_fiber);
|
||||
if (!os.quit) {
|
||||
os.delta_mouse_pos = w32(os).prev_mouse_pos - os.mouse_pos;
|
||||
w32(os).prev_mouse_pos = os.mouse_pos;
|
||||
|
||||
// @Note: Resize
|
||||
if (os.render_backend == RenderBackend_Software) {
|
||||
if (os.window_size != w32(os).screen.bitmap.size && os.window_size.x != 0 && os.window_size.y != 0) {
|
||||
win32_destroy_bitmap(&w32(os).screen);
|
||||
w32(os).screen = win32_create_bitmap(vec2i(os.window_size.x, -(os.window_size.y)));
|
||||
os.screen = &w32(os).screen.bitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
api void os_init_software_render() { os.render_backend = RenderBackend_Software; }
|
||||
|
||||
api B32 os_init_opengl() {
|
||||
PIXELFORMATDESCRIPTOR p = {};
|
||||
p.nSize = sizeof(p);
|
||||
p.nVersion = 1;
|
||||
p.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
||||
p.iPixelType = PFD_TYPE_RGBA;
|
||||
p.cColorBits = 32;
|
||||
p.cDepthBits = 24;
|
||||
p.cStencilBits = 8;
|
||||
p.iLayerType = PFD_MAIN_PLANE;
|
||||
|
||||
S32 pixel_format = ChoosePixelFormat(w32(os).window_dc, &p);
|
||||
if (pixel_format != 0) {
|
||||
if (SetPixelFormat(w32(os).window_dc, pixel_format, &p)) {
|
||||
HGLRC gl_ctx = wglCreateContext(w32(os).window_dc);
|
||||
if (gl_ctx != NULL) {
|
||||
if (wglMakeCurrent(w32(os).window_dc, gl_ctx)) {
|
||||
// Success
|
||||
}
|
||||
else {
|
||||
log_error("Failed on wglMakeCurrent!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_error("Failed on wglCreateContext!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_error("Failed on SetPixelFormat!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_error("Failed on ChoosePixelFormat!");
|
||||
return false;
|
||||
}
|
||||
|
||||
os.opengl.vendor = (char *)glGetString(GL_VENDOR);
|
||||
os.opengl.renderer = (char *)glGetString(GL_RENDERER);
|
||||
os.opengl.version = (char *)glGetString(GL_VERSION);
|
||||
os.opengl.extensions = (char *)glGetString(GL_EXTENSIONS);
|
||||
os.render_backend = RenderBackend_OpenGL1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
global S64 Global_counts_per_second;
|
||||
api F64 os_time() {
|
||||
if (Global_counts_per_second == 0) {
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
Global_counts_per_second = freq.QuadPart;
|
||||
}
|
||||
|
||||
LARGE_INTEGER time;
|
||||
QueryPerformanceCounter(&time);
|
||||
F64 result = (F64)time.QuadPart / (F64)Global_counts_per_second;
|
||||
return result;
|
||||
}
|
||||
|
||||
api B32 os_init() {
|
||||
HMODULE shcore = LoadLibraryA("Shcore.dll");
|
||||
if (shcore) {
|
||||
tSetProcessDpiAwareness *set_dpi_awr =
|
||||
(tSetProcessDpiAwareness *)GetProcAddress(shcore, "SetProcessDpiAwareness");
|
||||
if (set_dpi_awr) {
|
||||
HRESULT hr = set_dpi_awr(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
assert_msg(SUCCEEDED(hr), "Failed to set dpi awareness");
|
||||
}
|
||||
}
|
||||
|
||||
HMODULE winmm = LoadLibraryA("winmm.dll");
|
||||
if (winmm) {
|
||||
TimeBeginPeriod *timeBeginPeriod = (TimeBeginPeriod *)GetProcAddress(winmm, "timeBeginPeriod");
|
||||
if (timeBeginPeriod) {
|
||||
if (timeBeginPeriod(1) == TIMERR_NOERROR) {
|
||||
w32(os).good_scheduling = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DWORD window_style_chosen = window_style_resizable;
|
||||
if (!os.window_resizable)
|
||||
window_style_chosen = window_style_simplified;
|
||||
|
||||
os.app_start_time = os_time();
|
||||
os.frame_start_time = os.app_start_time;
|
||||
os.update_begin_cycles = __rdtsc();
|
||||
|
||||
WNDCLASSW wc = {}; // @Todo(Krzosa): UTF
|
||||
wc.lpfnWndProc = win32_window_proc;
|
||||
wc.hInstance = w32(os).instance;
|
||||
// @Todo(Krzosa): UTF conversions
|
||||
wc.lpszClassName = L"Have a good day"; // @Todo(Krzosa): Cant set window title
|
||||
wc.hCursor = LoadCursor(0, IDC_ARROW);
|
||||
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
|
||||
if (!RegisterClassW(&wc)) { // @Todo(Krzosa): UTF
|
||||
log_error("Failed to create window class!");
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT window_rect;
|
||||
window_rect.left = (LONG)os.window_pos.x;
|
||||
window_rect.top = (LONG)os.window_pos.y;
|
||||
window_rect.right = (LONG)os.window_size.x + window_rect.left;
|
||||
window_rect.bottom = (LONG)os.window_size.y + window_rect.top;
|
||||
AdjustWindowRectEx(&window_rect, window_style_chosen, false, 0);
|
||||
|
||||
// @Todo(Krzosa): UTF
|
||||
w32(os).window = CreateWindowW(wc.lpszClassName, wc.lpszClassName, window_style_chosen, window_rect.left, window_rect.top, window_rect.right - window_rect.left,window_rect.bottom - window_rect.top, NULL, NULL, w32(os).instance, NULL);
|
||||
if (w32(os).window == 0) {
|
||||
log_error("Failed to create window!");
|
||||
return false;
|
||||
}
|
||||
|
||||
ShowWindow(w32(os).window, w32(os).show_cmd);
|
||||
UpdateWindow(w32(os).window);
|
||||
w32(os).window_dc = GetDC(w32(os).window);
|
||||
|
||||
w32(os).main_fiber = ConvertThreadToFiber(0);
|
||||
assert_msg(w32(os).main_fiber, "Failed to create main fiber");
|
||||
w32(os).msg_fiber = CreateFiber(0, _os_fiber_event_proc, 0);
|
||||
assert_msg(w32(os).msg_fiber, "Failed to create message fiber");
|
||||
|
||||
if (os.cursor_visible == false)
|
||||
os_show_cursor(false);
|
||||
|
||||
switch (os.render_backend) {
|
||||
case RenderBackend_Software: {
|
||||
os_init_software_render();
|
||||
} break;
|
||||
case RenderBackend_OpenGL1: {
|
||||
os_init_opengl();
|
||||
} break;
|
||||
default: assert_msg(0, "Invalid value for render backend");
|
||||
break;
|
||||
}
|
||||
|
||||
os_pull_state();
|
||||
os.initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
api bool os_game_loop() {
|
||||
assert_msg(os.initialized, "Platform is not initialized! Please call os_init");
|
||||
switch (os.render_backend) {
|
||||
case RenderBackend_Software: {
|
||||
if (os.screen) { // @Note: Draw screen
|
||||
U32 *p = os.screen->pixels;
|
||||
for (int y = 0; y < os.screen->y; y++) {
|
||||
for (int x = 0; x < os.screen->x; x++) {
|
||||
*p = ((*p & 0xff000000)) | ((*p & 0x00ff0000) >> 16) | ((*p & 0x0000ff00)) |
|
||||
((*p & 0x000000ff) << 16);
|
||||
p += 1;
|
||||
}
|
||||
}
|
||||
|
||||
HDC hdc = w32(os).window_dc;
|
||||
SelectObject(w32(os).screen.dc, w32(os).screen.dib);
|
||||
BitBlt(hdc, 0, 0, (LONG)os.screen->size.x, (LONG)os.screen->size.y, w32(os).screen.dc, 0, 0, SRCCOPY);
|
||||
}
|
||||
} break;
|
||||
case RenderBackend_OpenGL1: {
|
||||
SwapBuffers(w32(os).window_dc);
|
||||
} break;
|
||||
default:
|
||||
assert_msg(0, "Please select a rendering backend!");
|
||||
break;
|
||||
}
|
||||
arena_clear(os.frame_arena);
|
||||
os.update_time = os_time() - os.frame_start_time;
|
||||
os.update_end_cycles = __rdtsc();
|
||||
F64 frame_time = os.update_time;
|
||||
F64 ms_per_frame = os.ms_per_frame;
|
||||
if (frame_time < ms_per_frame) {
|
||||
if (w32(os).good_scheduling) {
|
||||
// @Todo: I have no idea if letting over sleep is bad or not
|
||||
// Busy waiting is chugging cpu alot more, not sure what to do
|
||||
F64 time_to_sleep = (ms_per_frame - frame_time) * 1000;
|
||||
if (time_to_sleep > 0) {
|
||||
Sleep((DWORD)time_to_sleep);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
frame_time = os_time() - os.frame_start_time;
|
||||
} while (frame_time < ms_per_frame);
|
||||
}
|
||||
os.frame++;
|
||||
os.delta_time = frame_time;
|
||||
os.fps = 1 / os.delta_time;
|
||||
os.time += os.delta_time;
|
||||
os.frame_start_time = os_time();
|
||||
os.update_begin_cycles = __rdtsc();
|
||||
os_pull_state();
|
||||
return !os.quit;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv);
|
||||
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmd_line, int show_cmd) {
|
||||
thread_ctx_init();
|
||||
w32(os).instance = instance;
|
||||
w32(os).show_cmd = show_cmd;
|
||||
w32(os).cmd_line = cmd_line;
|
||||
|
||||
Arena frame_arena = {};
|
||||
arena_init(&frame_arena, "Frame arena"_s);
|
||||
os.perm_arena = &pernament_arena;
|
||||
os.frame_arena = &frame_arena;
|
||||
|
||||
os.dpi_scale = 1;
|
||||
os.text.str = w32(os).text_buff;
|
||||
os.window_title = "Have a good day!"_s;
|
||||
os.window_pos.x = 0;
|
||||
os.window_pos.y = 50;
|
||||
os.window_size.x = 1280;
|
||||
os.window_size.y = 720;
|
||||
os.ms_per_frame = 1.f / 60.f;
|
||||
os.cursor_visible = true;
|
||||
|
||||
if(AttachConsole(-1)) {
|
||||
freopen("CONIN$", "r",stdin);
|
||||
freopen("CONOUT$", "w",stdout);
|
||||
freopen("CONOUT$", "w",stderr);
|
||||
}
|
||||
|
||||
return main(__argc, __argv);
|
||||
}
|
||||
|
||||
//////////////
|
||||
// @Note: Font API
|
||||
function FontGlyph extract_glyph(Allocator *arena, Win32FontCtx *ctx, wchar_t glyph) {
|
||||
SIZE size;
|
||||
GetTextExtentPoint32W(ctx->bitmap.dc, &glyph, 1, &size);
|
||||
TextOutW(ctx->bitmap.dc, 0, 0, &glyph, 1);
|
||||
|
||||
// @Note: Find bitmap edges
|
||||
int minx = 100000;
|
||||
int miny = 100000;
|
||||
int maxx = -100000;
|
||||
int maxy = -100000;
|
||||
for (int y = 499; y >= 500 - size.cy; y--) {
|
||||
for (int x = 0; x < size.cx; x++) {
|
||||
if (ctx->bitmap.bitmap.pixels[x + y * (int)ctx->bitmap.bitmap.size.x] != 0) {
|
||||
if (minx > x)
|
||||
minx = x;
|
||||
if (miny > y)
|
||||
miny = y;
|
||||
if (maxx < x)
|
||||
maxx = x;
|
||||
if (maxy < y)
|
||||
maxy = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(minx >= 0 && miny >= 0);
|
||||
int bwidth = maxx - minx + 1;
|
||||
int bheight = maxy - miny + 1;
|
||||
U32 *cropped =
|
||||
(U32 *)exp_alloc(arena, sizeof(U32) * (U32)(bwidth) * (U32)(bheight));
|
||||
for (int y = miny; y <= maxy; y++) {
|
||||
for (int x = minx; x <= maxx; x++) {
|
||||
U32 value = ctx->bitmap.bitmap.pixels[x + y * (int)ctx->bitmap.bitmap.size.x];
|
||||
U32 *dst = cropped + ((size_t)(x - minx) + (size_t)(y - miny) * bwidth);
|
||||
#if 1 // Premultiplied alpha
|
||||
F32 alpha = (F32)((value & 0x000000ff) >> 0);
|
||||
F32 rgb = (F32)0xff*(alpha/255.f);
|
||||
U8 val = (U8)(rgb + 0.5f);
|
||||
*dst = (((U32)(alpha+0.5f) << 24) | val << 16 | val << 8 | val);
|
||||
#else
|
||||
U8 grey = (value & 0x000000ff);
|
||||
*dst = (grey << 24 | 0xff << 16 | 0xff << 8 | 0xff);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
exp_alloc(arena, sizeof(U32) * (U32)bwidth);
|
||||
|
||||
// @Note: Calculate char metrics
|
||||
int glyph_descent = (499 - size.cy) - miny;
|
||||
|
||||
FontGlyph result;
|
||||
INT width;
|
||||
GetCharWidth32W(ctx->bitmap.dc, glyph, glyph, &width);
|
||||
result.xadvance = (F32)width;
|
||||
result.bitmap = bitmap(cropped, vec2i(bwidth, bheight),
|
||||
vec2((F32)-minx, (F32)ctx->text_metric.tmDescent + (F32)glyph_descent));
|
||||
return result;
|
||||
}
|
||||
|
||||
function Win32FontCtx begin_font_extraction(char *filename, char *font_name, S64 height) {
|
||||
assert_msg(height < 500, "Height of font over 500");
|
||||
Win32FontCtx ctx = {};
|
||||
if (filename) {
|
||||
int fonts_added = AddFontResourceExA(filename, FR_PRIVATE, 0);
|
||||
assert_msg(fonts_added != 0, "AddFontResourceEx added 0 fonts");
|
||||
}
|
||||
|
||||
ctx.bitmap = win32_create_bitmap(vec2i(500, -500));
|
||||
ctx.font =
|
||||
CreateFontA((S32)height, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
|
||||
CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, font_name);
|
||||
assert_msg(ctx.font != NULL, "CreateFont returned a 0 pointer");
|
||||
SelectObject(ctx.bitmap.dc, ctx.bitmap.dib);
|
||||
SelectObject(ctx.bitmap.dc, ctx.font);
|
||||
GetTextMetrics(ctx.bitmap.dc, &ctx.text_metric);
|
||||
ctx.result.height = (F32)height;
|
||||
ctx.result.descent = (F32)ctx.text_metric.tmDescent;
|
||||
ctx.result.ascent = (F32)ctx.text_metric.tmAscent;
|
||||
ctx.result.line_advance = (F32)(height + ctx.text_metric.tmExternalLeading);
|
||||
SetBkColor(ctx.bitmap.dc, RGB(0, 0, 0));
|
||||
SetTextColor(ctx.bitmap.dc, RGB(255, 255, 255));
|
||||
return ctx;
|
||||
}
|
||||
|
||||
function Font end_font_extraction(Win32FontCtx *ctx) {
|
||||
win32_destroy_bitmap(&ctx->bitmap);
|
||||
DeleteObject(ctx->font);
|
||||
return ctx->result;
|
||||
}
|
||||
|
||||
api Font os_load_font(Allocator *arena, S32 height, const char *font_name, const char *filename) {
|
||||
FontGlyph *glyphs = exp_alloc_array(arena, FontGlyph, 96, AF_ZeroMemory);
|
||||
Win32FontCtx font_ctx = begin_font_extraction((char *)filename, (char *)font_name, height);
|
||||
for (U32 i = '!'; i <= '~'; i++) {
|
||||
glyphs[i - '!'] = extract_glyph(arena, &font_ctx, (wchar_t)i);
|
||||
}
|
||||
Font font = end_font_extraction(&font_ctx);
|
||||
font.glyphs = glyphs;
|
||||
font.glyphs_len = '~' - '!';
|
||||
return font;
|
||||
}
|
||||
|
||||
function void set_window_style(HWND window, DWORD style) {
|
||||
// @Todo: Need to readup on this
|
||||
SetWindowLongPtrW(window, GWL_STYLE, style);
|
||||
SetWindowPos(window, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
|
||||
ShowWindow(window, SW_SHOW);
|
||||
}
|
||||
|
||||
#undef w32
|
||||
///////////////////////////////////////
|
||||
// @Section Audio
|
||||
#include <objbase.h>
|
||||
#include <audioclient.h>
|
||||
#include <audiopolicy.h>
|
||||
#include <mmdeviceapi.h>
|
||||
|
||||
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
|
||||
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
|
||||
const IID IID_IAudioClient = __uuidof(IAudioClient);
|
||||
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
|
||||
|
||||
// NOTE: typedefines for the functions which are goint to be loaded
|
||||
typedef HRESULT CoCreateInstanceFunction(REFCLSID rclsid, LPUNKNOWN *pUnkOuter, DWORD dwClsContext,
|
||||
REFIID riid, LPVOID *ppv);
|
||||
typedef HRESULT CoInitializeExFunction(LPVOID pvReserved, DWORD dwCoInit);
|
||||
|
||||
// NOTE: empty functions(stubs) which are used when library fails to load
|
||||
HRESULT CoCreateInstanceStub(REFCLSID rclsid, LPUNKNOWN *pUnkOuter, DWORD dwClsContext, REFIID riid,
|
||||
LPVOID *ppv) {
|
||||
unused(rclsid);
|
||||
unused(pUnkOuter);
|
||||
unused(dwClsContext);
|
||||
unused(riid);
|
||||
unused(ppv);
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT CoInitializeExStub(LPVOID pvReserved, DWORD dwCoInit) {
|
||||
unused(pvReserved);
|
||||
unused(dwCoInit);
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
// NOTE: pointers to the functions from the dll
|
||||
CoCreateInstanceFunction *CoCreateInstanceFunctionPointer = CoCreateInstanceStub;
|
||||
CoInitializeExFunction *CoInitializeExFunctionPointer = CoInitializeExStub;
|
||||
|
||||
// NOTE: Number of REFERENCE_TIME units per second
|
||||
// One unit is equal to 100 nano seconds
|
||||
#define REF_TIMES_PER_SECOND 10000000
|
||||
#define REF_TIMES_PER_MSECOND 10000
|
||||
#define w32(a) (*(Audio_Win32 *)(a)->platform)
|
||||
|
||||
struct Audio_Win32 {
|
||||
IMMDevice *device;
|
||||
IAudioClient *audio_client;
|
||||
|
||||
IMMDeviceEnumerator *device_enum;
|
||||
IAudioRenderClient *audio_render_client;
|
||||
IAudioCaptureClient *audio_capture_client;
|
||||
};
|
||||
static_assert(sizeof(Audio::platform) > sizeof(Audio_Win32),
|
||||
"Audio::platform is too small to hold Audio_Win32 struct");
|
||||
|
||||
// Load COM Library functions dynamically,
|
||||
// this way sound is not necessary to run the game
|
||||
function B32 win32_load_com() {
|
||||
B32 result = true;
|
||||
HMODULE ole32Library = LoadLibraryA("ole32.dll");
|
||||
if (ole32Library) {
|
||||
CoCreateInstanceFunctionPointer =
|
||||
(CoCreateInstanceFunction *)GetProcAddress(ole32Library, "CoCreateInstance");
|
||||
if (!CoCreateInstanceFunctionPointer) {
|
||||
CoCreateInstanceFunctionPointer = CoCreateInstanceStub;
|
||||
log_error("CoCreateInstance failed to load");
|
||||
result = false;
|
||||
}
|
||||
CoInitializeExFunctionPointer = (CoInitializeExFunction *)GetProcAddress(ole32Library, "CoInitializeEx");
|
||||
if (!CoInitializeExFunctionPointer) {
|
||||
CoInitializeExFunctionPointer = CoInitializeExStub;
|
||||
log_error("CoInitializeEx failed to load");
|
||||
result = false;
|
||||
}
|
||||
} else {
|
||||
CoCreateInstanceFunctionPointer = CoCreateInstanceStub;
|
||||
CoInitializeExFunctionPointer = CoInitializeExStub;
|
||||
log_error("Failed to load OLE32.dll");
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
api void os_clean_audio(Audio *audio) {
|
||||
if (w32(audio).audio_client)
|
||||
w32(audio).audio_client->Stop();
|
||||
if (w32(audio).device_enum)
|
||||
w32(audio).device_enum->Release();
|
||||
if (w32(audio).device)
|
||||
w32(audio).device->Release();
|
||||
if (w32(audio).audio_client)
|
||||
w32(audio).audio_client->Release();
|
||||
if (w32(audio).audio_render_client)
|
||||
w32(audio).audio_render_client->Release();
|
||||
audio->initialized = false;
|
||||
}
|
||||
|
||||
function DWORD win32_audio_thread(void *parameter) {
|
||||
Audio *audio = (Audio *)parameter;
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
||||
HANDLE buffer_ready_event = CreateEvent(0, 0, 0, 0);
|
||||
if (!buffer_ready_event) {
|
||||
return -1;
|
||||
}
|
||||
if (FAILED(w32(audio).audio_client->SetEventHandle(buffer_ready_event))) {
|
||||
return -1;
|
||||
}
|
||||
U32 buffer_frame_count;
|
||||
if (FAILED(w32(audio).audio_client->GetBufferSize(&buffer_frame_count))) {
|
||||
return -1;
|
||||
}
|
||||
// U32 buffer_sample_count = buffer_frame_count * audio->number_of_channels;
|
||||
if (FAILED(w32(audio).audio_client->Start())) {
|
||||
w32(audio).audio_client->Stop();
|
||||
return -1;
|
||||
}
|
||||
for (;;) {
|
||||
if (WaitForSingleObject(buffer_ready_event, INFINITE) != WAIT_OBJECT_0) {
|
||||
w32(audio).audio_client->Stop();
|
||||
return -1;
|
||||
}
|
||||
U32 padding_frame_count;
|
||||
if (FAILED(w32(audio).audio_client->GetCurrentPadding(&padding_frame_count))) {
|
||||
w32(audio).audio_client->Stop();
|
||||
return -1;
|
||||
}
|
||||
U32 *samples;
|
||||
U32 fill_frame_count = buffer_frame_count - padding_frame_count;
|
||||
if (FAILED(w32(audio).audio_render_client->GetBuffer(fill_frame_count, (BYTE **)&samples))) {
|
||||
w32(audio).audio_client->Stop();
|
||||
return -1;
|
||||
}
|
||||
audio->callback(audio, samples, fill_frame_count);
|
||||
if (FAILED(w32(audio).audio_render_client->ReleaseBuffer(fill_frame_count, 0))) {
|
||||
w32(audio).audio_client->Stop();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function AUDIO_CALLBACK(default_audio_callback) {
|
||||
memory_zero(buffer, (U64)frames_to_fill * (U64)audio->samples_per_second);
|
||||
}
|
||||
|
||||
api B32 os_init_audio(Audio *audio) {
|
||||
audio->bits_per_sample = 16;
|
||||
if (audio->number_of_channels == 0)
|
||||
audio->number_of_channels = 2;
|
||||
if (audio->samples_per_second == 0)
|
||||
audio->samples_per_second = 44100;
|
||||
if (audio->callback == 0)
|
||||
audio->callback = default_audio_callback;
|
||||
|
||||
B32 success = win32_load_com();
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
if (FAILED(CoInitializeExFunctionPointer(0, COINITBASE_MULTITHREADED))) {
|
||||
log_error("Failed to initialize COM, CoInitializeEx");
|
||||
return false;
|
||||
}
|
||||
if (FAILED(CoCreateInstanceFunctionPointer(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
|
||||
IID_IMMDeviceEnumerator, (LPVOID *)&w32(audio).device_enum))) {
|
||||
log_error("Failed to initialize COM, CoCreateInstance");
|
||||
return false;
|
||||
}
|
||||
if (FAILED(w32(audio).device_enum->GetDefaultAudioEndpoint(eRender, eConsole, &w32(audio).device))) {
|
||||
os_clean_audio(audio);
|
||||
log_error("Failed to initialize WASAPI, GetDefaultAudioEndpoint");
|
||||
return false;
|
||||
}
|
||||
if (FAILED(
|
||||
w32(audio).device->Activate(IID_IAudioClient, CLSCTX_ALL, 0, (void **)&w32(audio).audio_client))) {
|
||||
os_clean_audio(audio);
|
||||
log_error("Failed to initialize WASAPI, "
|
||||
"w32(audio).device->Activate(IID_IAudioClient,");
|
||||
return false;
|
||||
}
|
||||
|
||||
WAVEFORMATEX waveFormat = {};
|
||||
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
waveFormat.nChannels = audio->number_of_channels;
|
||||
waveFormat.nSamplesPerSec = audio->samples_per_second;
|
||||
waveFormat.wBitsPerSample = audio->bits_per_sample;
|
||||
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
|
||||
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
|
||||
REFERENCE_TIME requestedBufferDuration = REF_TIMES_PER_MSECOND * 40;
|
||||
if (FAILED(w32(audio).audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_RATEADJUST |
|
||||
AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
|
||||
requestedBufferDuration, 0, &waveFormat, 0))) {
|
||||
os_clean_audio(audio);
|
||||
log_error("Failed to initialize WASAPI, w32(audio).audio_client->Initialize");
|
||||
return false;
|
||||
}
|
||||
if (FAILED(w32(audio).audio_client->GetService(IID_IAudioRenderClient,
|
||||
(void **)&w32(audio).audio_render_client))) {
|
||||
os_clean_audio(audio);
|
||||
log_error("Failed to initialize WASAPI, "
|
||||
"w32(audio).audio_client->GetService(IID_IAudioRenderClient");
|
||||
return false;
|
||||
}
|
||||
if (FAILED(w32(audio).audio_client->GetBufferSize(&audio->buffer_frame_count))) {
|
||||
os_clean_audio(audio);
|
||||
log_error("Failed to initialize WASAPI, w32(audio).audio_client->GetBufferSize");
|
||||
return false;
|
||||
}
|
||||
HANDLE thread_handle = CreateThread(0, 0, win32_audio_thread, audio, 0, 0);
|
||||
if (!thread_handle) {
|
||||
os_clean_audio(audio);
|
||||
log_error("Failed to initialize WASAPI, CreateThread returned 0 in handle");
|
||||
return false;
|
||||
}
|
||||
if (thread_handle == INVALID_HANDLE_VALUE) {
|
||||
os_clean_audio(audio);
|
||||
log_error("Failed to initialize WASAPI, CreateThread for "
|
||||
"audio failed with INVALID HANDLE VALUE");
|
||||
return false;
|
||||
}
|
||||
CloseHandle(thread_handle);
|
||||
audio->initialized = true;
|
||||
#undef w32
|
||||
return true;
|
||||
}
|
||||
|
||||
230
kpl_multimedia.h
Normal file
230
kpl_multimedia.h
Normal file
@@ -0,0 +1,230 @@
|
||||
#pragma once
|
||||
|
||||
struct Bitmap {
|
||||
union {
|
||||
U32 *pixels;
|
||||
U64 id;
|
||||
};
|
||||
union {
|
||||
Vec2I size;
|
||||
struct {
|
||||
S32 x, y;
|
||||
};
|
||||
};
|
||||
Vec2 align;
|
||||
};
|
||||
|
||||
enum EventKind {
|
||||
EventKind_None,
|
||||
EventKind_KeyDown,
|
||||
EventKind_KeyUp,
|
||||
EventKind_MouseMove,
|
||||
EventKind_MouseWheel,
|
||||
EventKind_KeyboardText,
|
||||
};
|
||||
|
||||
enum Key {
|
||||
Key_None,
|
||||
Key_Up,
|
||||
Key_Down,
|
||||
Key_Left,
|
||||
Key_Right,
|
||||
Key_Escape,
|
||||
Key_F1,
|
||||
Key_F2,
|
||||
Key_F3,
|
||||
Key_F4,
|
||||
Key_F5,
|
||||
Key_F6,
|
||||
Key_F7,
|
||||
Key_F8,
|
||||
Key_F9,
|
||||
Key_F10,
|
||||
Key_F11,
|
||||
Key_F12,
|
||||
Key_MouseLeft,
|
||||
Key_MouseRight,
|
||||
Key_MouseMiddle,
|
||||
Key_0 = '0',
|
||||
Key_1,
|
||||
Key_2,
|
||||
Key_3,
|
||||
Key_4,
|
||||
Key_5,
|
||||
Key_6,
|
||||
Key_7,
|
||||
Key_8,
|
||||
Key_9 = '9',
|
||||
Key_A = 'a',
|
||||
Key_B,
|
||||
Key_C,
|
||||
Key_D,
|
||||
Key_E,
|
||||
Key_F,
|
||||
Key_G,
|
||||
Key_H,
|
||||
Key_I,
|
||||
Key_J,
|
||||
Key_K,
|
||||
Key_L,
|
||||
Key_M,
|
||||
Key_N,
|
||||
Key_O,
|
||||
Key_P,
|
||||
Key_Q,
|
||||
Key_R,
|
||||
Key_S,
|
||||
Key_T,
|
||||
Key_U,
|
||||
Key_V,
|
||||
Key_W,
|
||||
Key_X,
|
||||
Key_Y,
|
||||
Key_Z = 'z',
|
||||
Key_Count = 256,
|
||||
};
|
||||
|
||||
#define KEY_MAPPING \
|
||||
X(Up, VK_UP) \
|
||||
X(Down, VK_DOWN) \
|
||||
X(Left, VK_LEFT) \
|
||||
X(Right, VK_RIGHT) \
|
||||
X(Escape, VK_ESCAPE) \
|
||||
X(F1, VK_F1) \
|
||||
X(F2, VK_F2) \
|
||||
X(F3, VK_F3) \
|
||||
X(F4, VK_F4) \
|
||||
X(F5, VK_F5) \
|
||||
X(F6, VK_F6) \
|
||||
X(F7, VK_F7) \
|
||||
X(F8, VK_F8) \
|
||||
X(F9, VK_F9) \
|
||||
X(F10, VK_F10) \
|
||||
X(F11, VK_F11) \
|
||||
X(F12, VK_F12) \
|
||||
X(A, 65) \
|
||||
X(B, 66) \
|
||||
X(C, 67) \
|
||||
X(D, 68) \
|
||||
X(E, 69) \
|
||||
X(F, 70) \
|
||||
X(G, 71) \
|
||||
X(H, 72) \
|
||||
X(I, 73) \
|
||||
X(J, 74) \
|
||||
X(K, 75) \
|
||||
X(L, 76) \
|
||||
X(M, 77) \
|
||||
X(N, 78) \
|
||||
X(O, 79) \
|
||||
X(P, 80) \
|
||||
X(Q, 81) \
|
||||
X(R, 82) \
|
||||
X(S, 83) \
|
||||
X(T, 84) \
|
||||
X(U, 85) \
|
||||
X(V, 86) \
|
||||
X(W, 87) \
|
||||
X(X, 88) \
|
||||
X(Y, 89) \
|
||||
X(Z, 90) \
|
||||
X(0, 48) \
|
||||
X(1, 49) \
|
||||
X(2, 50) \
|
||||
X(3, 51) \
|
||||
X(4, 52) \
|
||||
X(5, 53) \
|
||||
X(6, 54) \
|
||||
X(7, 55) \
|
||||
X(8, 56) \
|
||||
X(9, 57)
|
||||
|
||||
struct DigitalKey {
|
||||
bool pressed;
|
||||
bool down;
|
||||
bool released;
|
||||
};
|
||||
|
||||
enum RenderBackend {
|
||||
RenderBackend_Software,
|
||||
RenderBackend_OpenGL1,
|
||||
};
|
||||
|
||||
struct OS {
|
||||
bool quit;
|
||||
bool initialized;
|
||||
Arena *frame_arena;
|
||||
Arena *perm_arena;
|
||||
|
||||
F64 ms_per_frame;
|
||||
bool window_resizable;
|
||||
bool window_was_resized;
|
||||
String window_title;
|
||||
Vec2I window_size;
|
||||
Vec2I window_pos;
|
||||
RenderBackend render_backend;
|
||||
|
||||
Bitmap *screen;
|
||||
Vec2I monitor_size;
|
||||
F32 dpi_scale;
|
||||
|
||||
F64 fps;
|
||||
F64 delta_time;
|
||||
F64 time;
|
||||
U64 frame;
|
||||
F64 update_time;
|
||||
U64 update_begin_cycles;
|
||||
U64 update_end_cycles;
|
||||
F64 frame_start_time;
|
||||
F64 app_start_time;
|
||||
|
||||
B32 cursor_visible;
|
||||
DigitalKey key[Key_Count];
|
||||
F32 mouse_wheel;
|
||||
Vec2I mouse_pos;
|
||||
Vec2I delta_mouse_pos;
|
||||
|
||||
String text;
|
||||
|
||||
char platform[256];
|
||||
struct {
|
||||
char *vendor;
|
||||
char *version;
|
||||
char *renderer;
|
||||
char *extensions;
|
||||
} opengl;
|
||||
};
|
||||
|
||||
struct FontGlyph {
|
||||
F32 xadvance;
|
||||
U32 codepoint;
|
||||
Bitmap bitmap;
|
||||
};
|
||||
|
||||
struct Font {
|
||||
F32 line_advance;
|
||||
F32 ascent;
|
||||
F32 descent;
|
||||
F32 height;
|
||||
FontGlyph *glyphs;
|
||||
U32 glyphs_len;
|
||||
};
|
||||
|
||||
struct Audio;
|
||||
#define AUDIO_CALLBACK(name) void name(Audio *audio, U32* buffer, U32 frames_to_fill)
|
||||
typedef AUDIO_CALLBACK(AudioCallback);
|
||||
|
||||
struct Audio {
|
||||
AudioCallback *callback;
|
||||
B32 initialized;
|
||||
U32 samples_per_second;
|
||||
U32 number_of_channels;
|
||||
|
||||
// NOTE: one frame is 2 samples (left, right) 32 bits if
|
||||
// one sample is equal 16 bit
|
||||
U32 buffer_frame_count;
|
||||
U32 latency_frame_count;
|
||||
S32 bits_per_sample;
|
||||
|
||||
char platform[128];
|
||||
};
|
||||
243
main.cpp
243
main.cpp
@@ -25,6 +25,8 @@
|
||||
/// - [x] FPS Camera
|
||||
/// - [ ] Quarternions for rotations
|
||||
/// - [x] Reading OBJ models
|
||||
/// - [x] Dumping raw obj files
|
||||
/// - [x] Loading raw obj files, big startup speedup!
|
||||
/// - [ ] Reading more OBJ formats
|
||||
/// - [x] Reading OBJ .mtl files
|
||||
/// - [x] Loading materials
|
||||
@@ -77,13 +79,10 @@
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define PREMULTIPLIED_ALPHA_BLENDING 1
|
||||
|
||||
#define PLATFORM
|
||||
#include "kpl.h"
|
||||
#include "base.cpp"
|
||||
#include "kpl_multimedia.h"
|
||||
#include "kpl_multimedia.cpp"
|
||||
#include "profile.cpp"
|
||||
#include "math.h"
|
||||
|
||||
@@ -110,9 +109,9 @@ struct R_Render {
|
||||
F32 *depth320;
|
||||
};
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
#include "obj_parser.cpp"
|
||||
#include <float.h>
|
||||
|
||||
enum Scene {
|
||||
Scene_F22,
|
||||
@@ -120,24 +119,22 @@ enum Scene {
|
||||
Scene_Count,
|
||||
};
|
||||
|
||||
global F32 light_rotation = 0;
|
||||
global F32 zfar_value = 100000.f;
|
||||
|
||||
GLOBAL F32 light_rotation = 0;
|
||||
GLOBAL F32 zfar_value = 100000.f;
|
||||
|
||||
|
||||
FUNCTION
|
||||
function
|
||||
Vec4 srgb_to_almost_linear(Vec4 a) {
|
||||
Vec4 result = {a.r*a.r, a.g*a.g, a.b*a.b, a.a};
|
||||
return result; // @Note: Linear would be to power of 2.2
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
function
|
||||
Vec4 almost_linear_to_srgb(Vec4 a) {
|
||||
Vec4 result = { sqrtf(a.r), sqrtf(a.g), sqrtf(a.b), a.a };
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
function
|
||||
Vec4 premultiplied_alpha(Vec4 dst, Vec4 src) {
|
||||
Vec4 result;
|
||||
result.r = src.r + ((1-src.a) * dst.r);
|
||||
@@ -147,12 +144,12 @@ Vec4 premultiplied_alpha(Vec4 dst, Vec4 src) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
function
|
||||
void r_draw_rect(Bitmap* dst, F32 X, F32 Y, F32 w, F32 h, Vec4 color) {
|
||||
int max_x = (int)(MIN(X + w, dst->x) + 0.5f);
|
||||
int max_y = (int)(MIN(Y + h, dst->y) + 0.5f);
|
||||
int min_x = (int)(MAX(0, X) + 0.5f);
|
||||
int min_y = (int)(MAX(0, Y) + 0.5f);
|
||||
int max_x = (int)(min(X + w, (F32)dst->x) + 0.5f);
|
||||
int max_y = (int)(min(Y + h, (F32)dst->y) + 0.5f);
|
||||
int min_x = (int)(max(0.f, X) + 0.5f);
|
||||
int min_y = (int)(max(0.f, Y) + 0.5f);
|
||||
|
||||
color.rgb *= color.a;
|
||||
color = srgb_to_almost_linear(color);
|
||||
@@ -167,16 +164,16 @@ void r_draw_rect(Bitmap* dst, F32 X, F32 Y, F32 w, F32 h, Vec4 color) {
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
function
|
||||
void r_draw_bitmap(Bitmap* dst, Bitmap* src, Vec2 pos, Vec2 size=vec2(F32MAX, F32MAX)) {
|
||||
I64 minx = (I64)(pos.x + 0.5);
|
||||
I64 miny = (I64)(pos.y + 0.5);
|
||||
S64 minx = (S64)(pos.x + 0.5);
|
||||
S64 miny = (S64)(pos.y + 0.5);
|
||||
|
||||
if (size.x == F32MAX || size.y == F32MAX) {
|
||||
I64 maxx = minx + src->x;
|
||||
I64 maxy = miny + src->y;
|
||||
I64 offsetx = 0;
|
||||
I64 offsety = 0;
|
||||
S64 maxx = minx + src->x;
|
||||
S64 maxy = miny + src->y;
|
||||
S64 offsetx = 0;
|
||||
S64 offsety = 0;
|
||||
|
||||
if (maxx > dst->x) {
|
||||
maxx = dst->x;
|
||||
@@ -192,10 +189,10 @@ void r_draw_bitmap(Bitmap* dst, Bitmap* src, Vec2 pos, Vec2 size=vec2(F32MAX, F3
|
||||
offsety = -miny;
|
||||
miny = 0;
|
||||
}
|
||||
for (I64 y = miny; y < maxy; y++) {
|
||||
for (I64 x = minx; x < maxx; x++) {
|
||||
I64 tx = x - minx + offsetx;
|
||||
I64 ty = y - miny + offsety;
|
||||
for (S64 y = miny; y < maxy; y++) {
|
||||
for (S64 x = minx; x < maxx; x++) {
|
||||
S64 tx = x - minx + offsetx;
|
||||
S64 ty = y - miny + offsety;
|
||||
U32 *dst_pixel = dst->pixels + (x + y * dst->x);
|
||||
U32 *pixel = src->pixels + (tx + ty * src->x);
|
||||
Vec4 result_color = srgb_to_almost_linear(vec4abgr(*pixel));
|
||||
@@ -208,10 +205,10 @@ void r_draw_bitmap(Bitmap* dst, Bitmap* src, Vec2 pos, Vec2 size=vec2(F32MAX, F3
|
||||
}
|
||||
}
|
||||
else {
|
||||
I64 maxx = minx + (I64)(size.x + 0.5f);
|
||||
I64 maxy = miny + (I64)(size.y + 0.5f);
|
||||
I64 offsetx = 0;
|
||||
I64 offsety = 0;
|
||||
S64 maxx = minx + (S64)(size.x + 0.5f);
|
||||
S64 maxy = miny + (S64)(size.y + 0.5f);
|
||||
S64 offsetx = 0;
|
||||
S64 offsety = 0;
|
||||
|
||||
if (maxx > dst->x) {
|
||||
maxx = dst->x;
|
||||
@@ -229,12 +226,12 @@ void r_draw_bitmap(Bitmap* dst, Bitmap* src, Vec2 pos, Vec2 size=vec2(F32MAX, F3
|
||||
}
|
||||
F32 distx = (F32)(maxx - minx);
|
||||
F32 disty = (F32)(maxy - miny);
|
||||
for (I64 y = miny; y < maxy; y++) {
|
||||
for (I64 x = minx; x < maxx; x++) {
|
||||
for (S64 y = miny; y < maxy; y++) {
|
||||
for (S64 x = minx; x < maxx; x++) {
|
||||
F32 u = (F32)(x - minx) / distx;
|
||||
F32 v = (F32)(y - miny) / disty;
|
||||
I64 tx = (I64)(u * src->x + 0.5f);
|
||||
I64 ty = (I64)(v * src->y + 0.5f);
|
||||
S64 tx = (S64)(u * src->x + 0.5f);
|
||||
S64 ty = (S64)(v * src->y + 0.5f);
|
||||
U32 *dst_pixel = dst->pixels + (x + y * dst->x);
|
||||
U32 *pixel = src->pixels + (tx + ty * src->x);
|
||||
Vec4 result_color = srgb_to_almost_linear(vec4abgr(*pixel));
|
||||
@@ -249,8 +246,8 @@ void r_draw_bitmap(Bitmap* dst, Bitmap* src, Vec2 pos, Vec2 size=vec2(F32MAX, F3
|
||||
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
Vec4 r_base_string(Bitmap *dst, Font *font, S8 word, Vec2 pos, B32 draw) {
|
||||
function
|
||||
Vec4 r_base_string(Bitmap *dst, Font *font, String word, Vec2 pos, B32 draw) {
|
||||
Vec2 og_position = pos;
|
||||
F32 max_x = pos.x;
|
||||
for (U64 i = 0; i < word.len; i++) {
|
||||
@@ -274,36 +271,36 @@ Vec4 r_base_string(Bitmap *dst, Font *font, S8 word, Vec2 pos, B32 draw) {
|
||||
return rect;
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
Vec4 r_draw_string(Bitmap *dst, Font *font, S8 word, Vec2 pos) {
|
||||
function
|
||||
Vec4 r_draw_string(Bitmap *dst, Font *font, String word, Vec2 pos) {
|
||||
return r_base_string(dst, font, word, pos, true);
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
Vec4 r_get_string_rect(Font *font, S8 word, Vec2 pos) {
|
||||
function
|
||||
Vec4 r_get_string_rect(Font *font, String word, Vec2 pos) {
|
||||
return r_base_string(0, font, word, pos, false);
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
function
|
||||
F32 edge_function(Vec4 vecp0, Vec4 vecp1, Vec4 p) {
|
||||
F32 result = (vecp1.y - vecp0.y) * (p.x - vecp0.x) - (vecp1.x - vecp0.x) * (p.y - vecp0.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
function
|
||||
void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, Vec3 light_direction,
|
||||
Vec4 p0, Vec4 p1, Vec4 p2,
|
||||
Vec2 tex0, Vec2 tex1, Vec2 tex2,
|
||||
Vec3 norm0, Vec3 norm1, Vec3 norm2) {
|
||||
if(os.frame > 60) PROFILE_BEGIN(draw_triangle);
|
||||
F32 min_x1 = (F32)(MIN(p0.x, MIN(p1.x, p2.x)));
|
||||
F32 min_y1 = (F32)(MIN(p0.y, MIN(p1.y, p2.y)));
|
||||
F32 max_x1 = (F32)(MAX(p0.x, MAX(p1.x, p2.x)));
|
||||
F32 max_y1 = (F32)(MAX(p0.y, MAX(p1.y, p2.y)));
|
||||
I64 min_x = (I64)MAX(0, floor(min_x1));
|
||||
I64 min_y = (I64)MAX(0, floor(min_y1));
|
||||
I64 max_x = (I64)MIN(dst->x, ceil(max_x1));
|
||||
I64 max_y = (I64)MIN(dst->y, ceil(max_y1));
|
||||
F32 min_x1 = (F32)(min(p0.x, min(p1.x, p2.x)));
|
||||
F32 min_y1 = (F32)(min(p0.y, min(p1.y, p2.y)));
|
||||
F32 max_x1 = (F32)(max(p0.x, max(p1.x, p2.x)));
|
||||
F32 max_y1 = (F32)(max(p0.y, max(p1.y, p2.y)));
|
||||
S64 min_x = (S64)max(0.f, floor(min_x1));
|
||||
S64 min_y = (S64)max(0.f, floor(min_y1));
|
||||
S64 max_x = (S64)min((F32)dst->x, ceil(max_x1));
|
||||
S64 max_y = (S64)min((F32)dst->y, ceil(max_y1));
|
||||
|
||||
F32 dy10 = (p1.y - p0.y);
|
||||
F32 dy21 = (p2.y - p1.y);
|
||||
@@ -323,11 +320,11 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, Vec3 lig
|
||||
|
||||
U32 *destination = dst->pixels + dst->x*min_y;
|
||||
F32 area = (p1.y - p0.y) * (p2.x - p0.x) - (p1.x - p0.x) * (p2.y - p0.y);
|
||||
for (I64 y = min_y; y < max_y; y++) {
|
||||
for (S64 y = min_y; y < max_y; y++) {
|
||||
F32 Cx0 = Cy0;
|
||||
F32 Cx1 = Cy1;
|
||||
F32 Cx2 = Cy2;
|
||||
for (I64 x = min_x; x < max_x; x++) {
|
||||
for (S64 x = min_x; x < max_x; x++) {
|
||||
if (Cx0 >= 0 && Cx1 >= 0 && Cx2 >= 0) {
|
||||
F32 w1 = Cx1 / area;
|
||||
F32 w2 = Cx2 / area;
|
||||
@@ -356,10 +353,10 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, Vec3 lig
|
||||
u = u * (src->x - 1);
|
||||
v = v * (src->y - 1);
|
||||
}
|
||||
I64 ui = (I64)(u);
|
||||
I64 vi = (I64)(v);
|
||||
F32 udiff = u - (F32)ui;
|
||||
F32 vdiff = v - (F32)vi;
|
||||
S64 ui = (S64)(u);
|
||||
S64 vi = (S64)(v);
|
||||
//F32 udiff = u - (F32)ui;
|
||||
//F32 vdiff = v - (F32)vi;
|
||||
// Origin UV (0,0) is in bottom left
|
||||
U32 *dst_pixel = destination + x;
|
||||
U32 *pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x);
|
||||
@@ -390,7 +387,7 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, Vec3 lig
|
||||
Vec3 light_color = vec3(0.8,0.8,1);
|
||||
constexpr F32 ambient_strength = 0.1f; {
|
||||
Vec3 ambient = ambient_strength * light_color;
|
||||
Vec3 diffuse = CLAMP_BOT(0, -dot(norm, light_direction)) * light_color;
|
||||
Vec3 diffuse = clamp_bot(0.f, -dot(norm, light_direction)) * light_color;
|
||||
result_color.rgb *= (ambient+diffuse);
|
||||
}
|
||||
|
||||
@@ -420,22 +417,22 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, Vec3 lig
|
||||
if(os.frame > 60) PROFILE_END(draw_triangle);
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
function
|
||||
void draw_triangle_bilinear(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 light,
|
||||
Vec4 p0, Vec4 p1, Vec4 p2,
|
||||
Vec2 tex0, Vec2 tex1, Vec2 tex2) {
|
||||
F32 min_x1 = (F32)(MIN(p0.x, MIN(p1.x, p2.x)));
|
||||
F32 min_y1 = (F32)(MIN(p0.y, MIN(p1.y, p2.y)));
|
||||
F32 max_x1 = (F32)(MAX(p0.x, MAX(p1.x, p2.x)));
|
||||
F32 max_y1 = (F32)(MAX(p0.y, MAX(p1.y, p2.y)));
|
||||
I64 min_x = (I64)MAX(0, floor(min_x1));
|
||||
I64 min_y = (I64)MAX(0, floor(min_y1));
|
||||
I64 max_x = (I64)MIN(dst->x, ceil(max_x1));
|
||||
I64 max_y = (I64)MIN(dst->y, ceil(max_y1));
|
||||
F32 min_x1 = (F32)(min(p0.x, min(p1.x, p2.x)));
|
||||
F32 min_y1 = (F32)(min(p0.y, min(p1.y, p2.y)));
|
||||
F32 max_x1 = (F32)(max(p0.x, max(p1.x, p2.x)));
|
||||
F32 max_y1 = (F32)(max(p0.y, max(p1.y, p2.y)));
|
||||
S64 min_x = (S64)max(0.f, floor(min_x1));
|
||||
S64 min_y = (S64)max(0.f, floor(min_y1));
|
||||
S64 max_x = (S64)min((F32)dst->x, ceil(max_x1));
|
||||
S64 max_y = (S64)min((F32)dst->y, ceil(max_y1));
|
||||
|
||||
F32 area = edge_function(p0, p1, p2);
|
||||
for (I64 y = min_y; y < max_y; y++) {
|
||||
for (I64 x = min_x; x < max_x; x++) {
|
||||
for (S64 y = min_y; y < max_y; y++) {
|
||||
for (S64 x = min_x; x < max_x; x++) {
|
||||
F32 edge0 = edge_function(p0, p1, { (F32)x,(F32)y });
|
||||
F32 edge1 = edge_function(p1, p2, { (F32)x,(F32)y });
|
||||
F32 edge2 = edge_function(p2, p0, { (F32)x,(F32)y });
|
||||
@@ -458,8 +455,8 @@ void draw_triangle_bilinear(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 lig
|
||||
*depth = interpolated_w;
|
||||
u = u * (src->x - 2);
|
||||
v = v * (src->y - 2);
|
||||
I64 ui = (I64)(u);
|
||||
I64 vi = (I64)(v);
|
||||
S64 ui = (S64)(u);
|
||||
S64 vi = (S64)(v);
|
||||
F32 udiff = u - (F32)ui;
|
||||
F32 vdiff = v - (F32)vi;
|
||||
// Origin UV (0,0) is in bottom left
|
||||
@@ -495,10 +492,10 @@ void draw_triangle_bilinear(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 lig
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
void r_scatter_plot(Bitmap *dst, F64 *data, I64 data_len) {
|
||||
function
|
||||
void r_scatter_plot(Bitmap *dst, F64 *data, S64 data_len) {
|
||||
F64 min = F32MAX;
|
||||
F64 max = FLT_MIN;
|
||||
F64 max = F32MIN;
|
||||
F64 step = dst->x / (F64)data_len;
|
||||
for (U32 i = 0; i < data_len; i++) {
|
||||
if (min > data[i]) min = data[i];
|
||||
@@ -516,13 +513,13 @@ void r_scatter_plot(Bitmap *dst, F64 *data, I64 data_len) {
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION
|
||||
void r_draw_mesh(R_Render *r, S8 scene_name, OBJMaterial *materials, ObjMesh *mesh, Vec3 *vertices, Vec2 *tex_coords, Vec3 *normals) {
|
||||
function
|
||||
void r_draw_mesh(R_Render *r, String scene_name, Obj_Material *materials, Obj_Mesh *mesh, Vec3 *vertices, Vec2 *tex_coords, Vec3 *normals) {
|
||||
for (int i = 0; i < mesh->indices.len; i++) {
|
||||
ObjIndex *index = mesh->indices.e + i;
|
||||
Obj_Index *index = mesh->indices.data + i;
|
||||
Bitmap *image = &r->img;
|
||||
if(index->material_id != -1) {
|
||||
OBJMaterial *material = materials + index->material_id;
|
||||
Obj_Material *material = materials + index->material_id;
|
||||
// @Todo: No size info from OBJ things, this stuff needs a bit of refactor
|
||||
// Need to figure out how to accomodate multiple possible formats of input etc.
|
||||
if(material->texture_ambient.pixels) {
|
||||
@@ -584,7 +581,7 @@ void r_draw_mesh(R_Render *r, S8 scene_name, OBJMaterial *materials, ObjMesh *me
|
||||
B32 vertex_is_outside = false;
|
||||
Vec3 zfar_normal = vec3(0, 0, -1);
|
||||
Vec3 zfar_pos = vec3(0, 0, zfar_value);
|
||||
for (I32 j = 0; j < 3; j++) {
|
||||
for (S32 j = 0; j < 3; j++) {
|
||||
// @Note: Camera
|
||||
vert[j].pos = r->camera * vert[j].pos;
|
||||
// @Note: Skip triangle if even one vertex gets outside the clipping plane
|
||||
@@ -607,7 +604,7 @@ void r_draw_mesh(R_Render *r, S8 scene_name, OBJMaterial *materials, ObjMesh *me
|
||||
Vec2 tex;
|
||||
Vec3 norm;
|
||||
} in[4];
|
||||
I32 in_count = 0;
|
||||
S32 in_count = 0;
|
||||
|
||||
R_Vertex *prev = vert + 2;
|
||||
R_Vertex *curr = vert;
|
||||
@@ -634,7 +631,7 @@ void r_draw_mesh(R_Render *r, S8 scene_name, OBJMaterial *materials, ObjMesh *me
|
||||
continue;
|
||||
}
|
||||
|
||||
for(I64 j = 0; j < in_count; j++) {
|
||||
for(S64 j = 0; j < in_count; j++) {
|
||||
//@Note: Perspective
|
||||
in[j].pos = r->projection * in[j].pos;
|
||||
in[j].pos.x = in[j].pos.x / in[j].pos.w;
|
||||
@@ -653,7 +650,7 @@ void r_draw_mesh(R_Render *r, S8 scene_name, OBJMaterial *materials, ObjMesh *me
|
||||
}
|
||||
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
ProfileScope *scope = profile_scopes + ProfileScopeName_draw_triangle;
|
||||
LOCAL_PERSIST B32 profile_flag;
|
||||
if (!profile_flag && scope->i > 2000) {
|
||||
@@ -665,15 +662,17 @@ void r_draw_mesh(R_Render *r, S8 scene_name, OBJMaterial *materials, ObjMesh *me
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "ui.cpp"
|
||||
|
||||
F32 speed = 100.f;
|
||||
F32 rotation = 0;
|
||||
Obj f22;
|
||||
Obj sponza;
|
||||
Obj *obj;
|
||||
R_Render r = {};
|
||||
GLOBAL Scene scene = Scene_Sponza;
|
||||
#include "ui.cpp"
|
||||
global F32 speed = 100.f;
|
||||
global F32 rotation = 0;
|
||||
global Obj f22;
|
||||
global Obj *sponza;
|
||||
global Obj *obj;
|
||||
global R_Render r = {};
|
||||
global Scene scene = Scene_Sponza;
|
||||
|
||||
function
|
||||
UI_SIGNAL_CALLBACK(scene_callback) {
|
||||
switch(scene) {
|
||||
case Scene_F22: {
|
||||
@@ -684,49 +683,37 @@ UI_SIGNAL_CALLBACK(scene_callback) {
|
||||
case Scene_Sponza: {
|
||||
speed = 100;
|
||||
r.camera_pos = vec3(0,0,-2);
|
||||
obj = &sponza;
|
||||
obj = sponza;
|
||||
} break;
|
||||
case Scene_Count:
|
||||
INVALID_DEFAULT_CASE;
|
||||
invalid_default_case;
|
||||
}
|
||||
scene = (Scene)(((int)scene + 1) % Scene_Count);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int main(int argc, char **argv) {
|
||||
os.window_size.x = 320*2;
|
||||
os.window_size.y = 180*2;
|
||||
os.window_resizable = 1;
|
||||
os_init().error_is_fatal();
|
||||
S8List list = {};
|
||||
string_push(os.frame_arena, &list, LIT("main.cpp"));
|
||||
generate_documentation(list, LIT("README.md"));
|
||||
Font font = os_load_font(os.perm_arena, 24, "Arial");
|
||||
for (U32 i = 0; i < font.glyphs_len; i++) {
|
||||
FontGlyph *g = font.glyphs + i;
|
||||
U32 *pointer = g->bitmap.pixels;
|
||||
for (I32 y = 0; y < g->bitmap.y; y++) {
|
||||
for (I32 x = 0; x < g->bitmap.x; x++) {
|
||||
Vec4 color = vec4abgr(*pointer);
|
||||
color.rgb *= color.a;
|
||||
*pointer++ = vec4_to_u32abgr(color);
|
||||
}
|
||||
}
|
||||
assert(os_init());
|
||||
Font font = os_load_font(os.perm_arena, 24, "Arial", 0);
|
||||
|
||||
}
|
||||
f22 = load_obj(&os_process_heap, "assets/f22.obj"_s);
|
||||
//Obj sponza_obj = load_obj(&os_process_heap, "assets/sponza/sponza.obj"_s);
|
||||
//sponza = &sponza_obj;
|
||||
//dump_obj_to_file(sponza);
|
||||
sponza = load_obj_dump(os.perm_arena, "sponza.bin"_s);
|
||||
|
||||
|
||||
f22 = load_obj(os.perm_arena, LIT("assets/f22.obj"));
|
||||
sponza = load_obj(os.perm_arena, LIT("assets/sponza/sponza.obj"));
|
||||
scene_callback();
|
||||
|
||||
int screen_x = 1280/2;
|
||||
int screen_y = 720/2;
|
||||
|
||||
r.camera_pos = {0,0,-2};
|
||||
r.screen320 = {(U32 *)PUSH_SIZE(os.perm_arena, screen_x*screen_y*sizeof(U32)), screen_x, screen_y};
|
||||
r.plot = {(U32 *)PUSH_SIZE(os.perm_arena, 1280*720*sizeof(U32)), 1280, 720};
|
||||
r.depth320 = (F32 *)PUSH_SIZE(os.perm_arena, sizeof(F32) * screen_x * screen_y);
|
||||
r.img = load_image("assets/bricksx64.png");
|
||||
r.screen320 = {(U32 *)arena_push_size(os.perm_arena, screen_x*screen_y*sizeof(U32)), screen_x, screen_y};
|
||||
r.plot = {(U32 *)arena_push_size(os.perm_arena, 1280*720*sizeof(U32)), 1280, 720};
|
||||
r.depth320 = (F32 *)arena_push_size(os.perm_arena, sizeof(F32) * screen_x * screen_y);
|
||||
r.img = load_image("assets/bricksx64.png"_s);
|
||||
|
||||
/* @Note: Transparent texture */ {
|
||||
#if 0
|
||||
@@ -747,13 +734,13 @@ int main() {
|
||||
|
||||
|
||||
|
||||
S8 frame_data = {};
|
||||
String frame_data = {};
|
||||
UISetup setup[] = {
|
||||
UI_SIGNAL(LIT("Change scene"), scene_callback),
|
||||
UI_SIGNAL("Change scene"_s, scene_callback),
|
||||
UI_IMAGE(&r.plot),
|
||||
UI_LABEL(&frame_data),
|
||||
};
|
||||
UI ui = ui_make(os.perm_arena, setup, ARRAY_CAP(setup));
|
||||
UI ui = ui_make(setup, buff_cap(setup));
|
||||
B32 ui_mouse_lock = true;
|
||||
|
||||
while (os_game_loop()) {
|
||||
@@ -804,11 +791,11 @@ int main() {
|
||||
r.transform = mat4_rotation_z(rotation);
|
||||
r.transform = r.transform * mat4_rotation_y(rotation);
|
||||
for (int i = 0; i < obj->mesh.len; i++) {
|
||||
Vec2* tex_coords = (Vec2*)obj->texture_coordinates.e;
|
||||
Vec3 *normals = (Vec3 *)obj->normals.e;
|
||||
ObjMesh *mesh = obj->mesh.e;
|
||||
Vec3* vertices = (Vec3 *)obj->vertices.e;
|
||||
r_draw_mesh(&r, obj->name, obj->materials.e, mesh+i, vertices, tex_coords, normals);
|
||||
Vec2* tex_coords = (Vec2*)obj->texture_coordinates.data;
|
||||
Vec3 *normals = (Vec3 *)obj->normals.data;
|
||||
Obj_Mesh *mesh = obj->mesh.data;
|
||||
Vec3* vertices = (Vec3 *)obj->vertices.data;
|
||||
r_draw_mesh(&r, obj->name, obj->materials.data, mesh+i, vertices, tex_coords, normals);
|
||||
}
|
||||
|
||||
|
||||
@@ -824,7 +811,7 @@ int main() {
|
||||
}
|
||||
}
|
||||
ui_end_frame(os.screen, &ui, &font);
|
||||
frame_data = string_format(os.frame_arena, "FPS:%f dt:%f frame:%u", os.fps, os.delta_time, os.frame);
|
||||
frame_data = string_fmt(os.frame_arena, "FPS:%f dt:%f frame:%u", os.fps, os.delta_time, os.frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
484
obj_parser.cpp
484
obj_parser.cpp
@@ -1,73 +1,66 @@
|
||||
template<class T>
|
||||
struct DynamicArray {
|
||||
T *e;
|
||||
U64 cap, len;
|
||||
T *push_empty(int element_count = 1) {
|
||||
if (cap == 0) {
|
||||
cap = 8;
|
||||
e = (T*)malloc(sizeof(T)*cap);
|
||||
}
|
||||
else if (len + element_count > cap) {
|
||||
U64 new_size = (len + element_count) * 2;
|
||||
void *ptr = realloc(e, new_size*sizeof(T));
|
||||
if (!ptr) FATAL_ERROR("Ran out of memory! Cant allocate more.");
|
||||
e = (T *)ptr;
|
||||
cap = new_size;
|
||||
}
|
||||
|
||||
T *result = e + len;
|
||||
len += element_count;
|
||||
return result;
|
||||
}
|
||||
void push(T element) {
|
||||
T *result = push_empty();
|
||||
*result = element;
|
||||
}
|
||||
};
|
||||
|
||||
struct ObjIndex {
|
||||
struct Obj_Index {
|
||||
int vertex[3];
|
||||
int tex[3];
|
||||
int normal[3];
|
||||
I32 material_id;
|
||||
I32 smoothing_group_id;
|
||||
S32 material_id;
|
||||
S32 smoothing_group_id;
|
||||
};
|
||||
|
||||
struct ObjMesh {
|
||||
struct Obj_Mesh {
|
||||
char name[64];
|
||||
DynamicArray<ObjIndex> indices;
|
||||
Array<Obj_Index> indices;
|
||||
};
|
||||
|
||||
struct OBJMaterial {
|
||||
struct Obj_Material {
|
||||
char name[64];
|
||||
U32 name_len;
|
||||
Bitmap texture_ambient; // map_Ka
|
||||
Bitmap texture_diffuse; // map_Kd
|
||||
Bitmap texture_dissolve; // map_d
|
||||
Bitmap texture_displacment; // map_Disp
|
||||
Bitmap texture_displacement; // map_Disp
|
||||
F32 non_transparency; // d
|
||||
F32 transparency; // Tr
|
||||
F32 optical_density; // Ni
|
||||
F32 shininess; // Ns
|
||||
I32 illumination_model; // illum
|
||||
S32 illumination_model; // illum
|
||||
Vec3 ambient_color; // Ka
|
||||
Vec3 diffuse_color; // Kd
|
||||
Vec3 specular_color; // Ks
|
||||
};
|
||||
|
||||
struct Obj {
|
||||
S8 name;
|
||||
DynamicArray<Vec3> vertices;
|
||||
DynamicArray<Vec2> texture_coordinates;
|
||||
DynamicArray<Vec3> normals;
|
||||
DynamicArray<ObjMesh> mesh;
|
||||
DynamicArray<OBJMaterial> materials;
|
||||
String name;
|
||||
Array<Vec3> vertices;
|
||||
Array<Vec2> texture_coordinates;
|
||||
Array<Vec3> normals;
|
||||
Array<Obj_Mesh> mesh;
|
||||
Array<Obj_Material> materials;
|
||||
};
|
||||
|
||||
FUNCTION
|
||||
Bitmap load_image(const char* path) {
|
||||
enum class Obj_Token_Type {
|
||||
none, word, number, whitespace, end
|
||||
};
|
||||
|
||||
struct Obj_Token {
|
||||
Obj_Token_Type type;
|
||||
double number;
|
||||
union {
|
||||
struct {
|
||||
char* s;
|
||||
int len;
|
||||
};
|
||||
String s8;
|
||||
};
|
||||
};
|
||||
|
||||
function Bitmap
|
||||
load_image(String path) {
|
||||
Scratch scratch;
|
||||
String file = os_read_file(scratch, path);
|
||||
|
||||
int x, y, n;
|
||||
unsigned char* data = stbi_load(path, &x, &y, &n, 4);
|
||||
unsigned char* data = stbi_load_from_memory(file.str, file.len, &x, &y, &n, 4);
|
||||
Bitmap result = { (U32*)data, x, y };
|
||||
#if PREMULTIPLIED_ALPHA_BLENDING
|
||||
if(data) {
|
||||
@@ -86,36 +79,20 @@ Bitmap load_image(const char* path) {
|
||||
return result;
|
||||
}
|
||||
|
||||
enum class OBJTokenType {
|
||||
none, word, number, whitespace, end
|
||||
};
|
||||
|
||||
struct OBJToken {
|
||||
OBJTokenType type;
|
||||
double number;
|
||||
union {
|
||||
struct {
|
||||
char* s;
|
||||
int len;
|
||||
};
|
||||
S8 s8;
|
||||
};
|
||||
};
|
||||
|
||||
FUNCTION OBJToken next_token_raw(char** data) {
|
||||
OBJToken result = {};
|
||||
function Obj_Token next_token_raw(char** data) {
|
||||
Obj_Token result = {};
|
||||
result.s = *data;
|
||||
*data += 1;
|
||||
|
||||
if (is_alphabetic(*result.s)) {
|
||||
result.type = OBJTokenType::word;
|
||||
result.type = Obj_Token_Type::word;
|
||||
while (!is_whitespace(**data)) {
|
||||
*data += 1;
|
||||
}
|
||||
result.len = (int)(*data - result.s);
|
||||
}
|
||||
else if (is_number(*result.s) || *result.s == '-') {
|
||||
result.type = OBJTokenType::number;
|
||||
result.type = Obj_Token_Type::number;
|
||||
while (is_number(**data) || **data == '.' || **data == 'e' || **data == '-') {
|
||||
*data += 1;
|
||||
}
|
||||
@@ -127,198 +104,206 @@ FUNCTION OBJToken next_token_raw(char** data) {
|
||||
result = next_token_raw(data);
|
||||
}
|
||||
else if (is_whitespace(*result.s)) {
|
||||
result.type = OBJTokenType::whitespace;
|
||||
result.type = Obj_Token_Type::whitespace;
|
||||
while (is_whitespace(**data)) *data += 1;
|
||||
result.len = (int)(*data - result.s);
|
||||
}
|
||||
else if (*result.s == 0) {
|
||||
result.type = OBJTokenType::end;
|
||||
result.type = Obj_Token_Type::end;
|
||||
}
|
||||
else if (*result.s >= '!') {
|
||||
result.type = (OBJTokenType)*result.s;
|
||||
result.type = (Obj_Token_Type)*result.s;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION OBJToken next_token(char** data) {
|
||||
OBJToken result;
|
||||
function Obj_Token next_token(char** data) {
|
||||
Obj_Token result;
|
||||
do {
|
||||
result = next_token_raw(data);
|
||||
} while (result.type == OBJTokenType::whitespace);
|
||||
} while (result.type == Obj_Token_Type::whitespace);
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION double expect_number(char** data) {
|
||||
OBJToken t = next_token(data);
|
||||
TASSERT(t.type == OBJTokenType::number); // @Todo: Error handling, error flag
|
||||
function double expect_number(char** data) {
|
||||
Obj_Token t = next_token(data);
|
||||
assert(t.type == Obj_Token_Type::number); // @Todo: Error handling, error flag
|
||||
return t.number;
|
||||
}
|
||||
|
||||
FUNCTION void expect_token(char** data, char token) {
|
||||
OBJToken t = next_token(data);
|
||||
TASSERT(t.type == (OBJTokenType)token); // @Todo: Error handling, error flag
|
||||
function void expect_token(char** data, char token) {
|
||||
Obj_Token t = next_token(data);
|
||||
assert(t.type == (Obj_Token_Type)token); // @Todo: Error handling, error flag
|
||||
}
|
||||
|
||||
FUNCTION void debug_expect_raw(char** data, OBJTokenType type) {
|
||||
function void debug_expect_raw(char** data, Obj_Token_Type type) {
|
||||
char* data_temp = *data;
|
||||
OBJToken t = next_token_raw(&data_temp);
|
||||
TASSERT(t.type == type);
|
||||
Obj_Token t = next_token_raw(&data_temp);
|
||||
assert(t.type == type);
|
||||
}
|
||||
|
||||
FUNCTION void parse_mtl(Arena *arena, Obj* obj, S8 path_obj_folder, S8 mtl_file) {
|
||||
function void
|
||||
parse_mtl(Obj* obj, String path_obj_folder, String mtl_file) {
|
||||
Scratch scratch;
|
||||
char *data = (char *)mtl_file.str;
|
||||
OBJMaterial *m = 0;
|
||||
Obj_Material *m = 0;
|
||||
|
||||
for (;;) {
|
||||
OBJToken token = next_token(&data);
|
||||
if (token.type == OBJTokenType::end) break;
|
||||
else if (token.type == OBJTokenType::word) {
|
||||
if (string_compare(token.s8, LIT("newmtl"))) {
|
||||
Obj_Token token = next_token(&data);
|
||||
if (token.type == Obj_Token_Type::end) break;
|
||||
else if (token.type == Obj_Token_Type::word) {
|
||||
if (string_compare(token.s8, "newmtl"_s)) {
|
||||
token = next_token(&data);
|
||||
m = obj->materials.push_empty();
|
||||
ZERO_STRUCT(m);
|
||||
m->name_len = CLAMP_TOP(token.len, 64);
|
||||
memory_copy(token.s8.str, m->name, m->name_len);
|
||||
m = obj->materials.push_empty_zero();
|
||||
m->name_len = clamp_top(token.len, 64);
|
||||
memory_copy(m->name, token.s8.str, m->name_len);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("Ns"))) {
|
||||
else if (string_compare(token.s8, "Ns"_s)) {
|
||||
m->shininess = expect_number(&data);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("Ka"))) {
|
||||
else if (string_compare(token.s8, "Ka"_s)) {
|
||||
m->ambient_color.x = expect_number(&data);
|
||||
m->ambient_color.y = expect_number(&data);
|
||||
m->ambient_color.z = expect_number(&data);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("Kd"))) {
|
||||
else if (string_compare(token.s8, "Kd"_s)) {
|
||||
m->diffuse_color.x = expect_number(&data);
|
||||
m->diffuse_color.y = expect_number(&data);
|
||||
m->diffuse_color.z = expect_number(&data);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("Ks"))) {
|
||||
else if (string_compare(token.s8, "Ks"_s)) {
|
||||
m->specular_color.x = expect_number(&data);
|
||||
m->specular_color.y = expect_number(&data);
|
||||
m->specular_color.z = expect_number(&data);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("Ni"))) {
|
||||
else if (string_compare(token.s8, "Ni"_s)) {
|
||||
m->optical_density = expect_number(&data);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("d"))) {
|
||||
else if (string_compare(token.s8, "d"_s)) {
|
||||
m->non_transparency = expect_number(&data);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("illum"))) {
|
||||
m->illumination_model = (I32)expect_number(&data);
|
||||
else if (string_compare(token.s8, "illum"_s)) {
|
||||
m->illumination_model = (S32)expect_number(&data);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("map_Kd"))) {
|
||||
OBJToken t = next_token(&data);
|
||||
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
|
||||
m->texture_diffuse = load_image((const char *)path.str);
|
||||
else if (string_compare(token.s8, "map_Kd"_s)) {
|
||||
Obj_Token t = next_token(&data);
|
||||
String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8);
|
||||
m->texture_diffuse = load_image(path);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("map_Ka"))) {
|
||||
OBJToken t = next_token(&data);
|
||||
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
|
||||
m->texture_ambient = load_image((const char *)path.str);
|
||||
else if (string_compare(token.s8, "map_Ka"_s)) {
|
||||
Obj_Token t = next_token(&data);
|
||||
String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8);
|
||||
m->texture_ambient = load_image(path);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("map_d"))) {
|
||||
OBJToken t = next_token(&data);
|
||||
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
|
||||
m->texture_dissolve = load_image((const char *)path.str);
|
||||
else if (string_compare(token.s8, "map_d"_s)) {
|
||||
Obj_Token t = next_token(&data);
|
||||
String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8);
|
||||
m->texture_dissolve = load_image(path);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("map_Disp"))) {
|
||||
OBJToken t = next_token(&data);
|
||||
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
|
||||
m->texture_displacment = load_image((const char *)path.str);
|
||||
else if (string_compare(token.s8, "map_Disp"_s)) {
|
||||
Obj_Token t = next_token(&data);
|
||||
String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8);
|
||||
m->texture_displacement = load_image(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION Obj parse(Arena *arena, char* data, S8 path_obj_folder) {
|
||||
function Obj
|
||||
parse(Allocator *allocator, char* data, String path_obj_folder) {
|
||||
Scratch mtl_scratch;
|
||||
Obj result = {};
|
||||
result.vertices.init(allocator, 160000);
|
||||
result.texture_coordinates.init(allocator, 160000);
|
||||
result.normals.init(allocator, 160000);
|
||||
result.mesh.init(allocator, 64);
|
||||
result.materials.init(allocator, 64);
|
||||
int smoothing = 0;
|
||||
ObjMesh *mesh = result.mesh.push_empty();
|
||||
ZERO_STRUCT(mesh);
|
||||
|
||||
Obj_Mesh *mesh = result.mesh.push_empty_zero();
|
||||
mesh->indices.init(allocator);
|
||||
int material_id = -1;
|
||||
|
||||
for (;; ) {
|
||||
OBJToken token = next_token(&data);
|
||||
if (token.type == OBJTokenType::end) break;
|
||||
else if (token.type == OBJTokenType::word) {
|
||||
if (string_compare(token.s8, LIT("v"))) {
|
||||
Vec3 *vertex = result.vertices.push_empty();
|
||||
S64 debug_i = 0;
|
||||
for (;;debug_i++){
|
||||
Obj_Token token = next_token(&data);
|
||||
if (token.type == Obj_Token_Type::end) break;
|
||||
else if (token.type == Obj_Token_Type::word) {
|
||||
if (string_compare(token.s8, "v"_s)) {
|
||||
Vec3 *vertex = result.vertices.push_empty_zero();
|
||||
vertex->x = (float)expect_number(&data);
|
||||
vertex->y = (float)expect_number(&data);
|
||||
vertex->z = (float)expect_number(&data);
|
||||
debug_expect_raw(&data, OBJTokenType::whitespace);
|
||||
debug_expect_raw(&data, Obj_Token_Type::whitespace);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("vt"))) {
|
||||
Vec2 *tex = result.texture_coordinates.push_empty();
|
||||
else if (string_compare(token.s8, "vt"_s)) {
|
||||
Vec2 *tex = result.texture_coordinates.push_empty_zero();
|
||||
tex->x = (float)expect_number(&data);
|
||||
tex->y = (float)expect_number(&data);
|
||||
debug_expect_raw(&data, OBJTokenType::whitespace);
|
||||
debug_expect_raw(&data, Obj_Token_Type::whitespace);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("vn"))) {
|
||||
Vec3 *norm = result.normals.push_empty();
|
||||
else if (string_compare(token.s8, "vn"_s)) {
|
||||
Vec3 *norm = result.normals.push_empty_zero();
|
||||
norm->x = (float)expect_number(&data);
|
||||
norm->y = (float)expect_number(&data);
|
||||
norm->z = (float)expect_number(&data);
|
||||
debug_expect_raw(&data, OBJTokenType::whitespace);
|
||||
debug_expect_raw(&data, Obj_Token_Type::whitespace);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("mtllib"))) {
|
||||
OBJToken t = next_token(&data);
|
||||
S8 path = string_format(mtl_scratch, "%s/%s", path_obj_folder, t.s8);
|
||||
Result<S8> mtl_file = os_read_file(mtl_scratch, path);
|
||||
if(mtl_file.no_error()) {
|
||||
PUSH_SIZE(mtl_scratch, 1);
|
||||
parse_mtl(arena, &result, path_obj_folder, mtl_file.result);
|
||||
else if (string_compare(token.s8, "mtllib"_s)) {
|
||||
Obj_Token t = next_token(&data);
|
||||
String path = string_fmt(mtl_scratch, "%Q/%Q", path_obj_folder, t.s8);
|
||||
String mtl_file = os_read_file(mtl_scratch, path);
|
||||
if(mtl_file.str) {
|
||||
parse_mtl(&result, path_obj_folder, mtl_file);
|
||||
}
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("usemtl"))) {
|
||||
OBJToken t = next_token(&data);
|
||||
TASSERT(t.type == OBJTokenType::word);
|
||||
else if (string_compare(token.s8, "usemtl"_s)) {
|
||||
Obj_Token t = next_token(&data);
|
||||
assert(t.type == Obj_Token_Type::word);
|
||||
for(U64 i = 0; i < result.materials.len; i++) {
|
||||
OBJMaterial *m = result.materials.e + i;
|
||||
if(string_compare(string_make((U8 *)m->name, m->name_len), t.s8)) {
|
||||
Obj_Material *m = result.materials.data + i;
|
||||
if(string_compare({(U8 *)m->name, m->name_len}, t.s8)) {
|
||||
material_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("o"))) {
|
||||
OBJToken t = next_token(&data);
|
||||
TASSERT(t.type == OBJTokenType::word);
|
||||
else if (string_compare(token.s8, "o"_s)) {
|
||||
Obj_Token t = next_token(&data);
|
||||
assert(t.type == Obj_Token_Type::word);
|
||||
if (mesh->indices.len != 0) {
|
||||
mesh = result.mesh.push_empty();
|
||||
ZERO_STRUCT(mesh);
|
||||
mesh = result.mesh.push_empty_zero();
|
||||
mesh->indices.init(allocator);
|
||||
}
|
||||
else {
|
||||
U64 len = CLAMP_TOP(t.len, 64);
|
||||
memory_copy(t.s, mesh->name, len);
|
||||
U64 len = clamp_top(t.len, 64);
|
||||
memory_copy(mesh->name, t.s, len);
|
||||
}
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("s"))) {
|
||||
OBJToken t = next_token(&data);
|
||||
if (t.type == OBJTokenType::number) {
|
||||
else if (string_compare(token.s8, "s"_s)) {
|
||||
Obj_Token t = next_token(&data);
|
||||
if (t.type == Obj_Token_Type::number) {
|
||||
smoothing = (int)t.number;
|
||||
}
|
||||
else {
|
||||
TASSERT(t.type == OBJTokenType::word);
|
||||
if (string_compare(t.s8, LIT("on"))) {
|
||||
assert(t.type == Obj_Token_Type::word);
|
||||
if (string_compare(t.s8, "on"_s)) {
|
||||
smoothing = 1;
|
||||
}
|
||||
else if (string_compare(t.s8, LIT("off"))) {
|
||||
else if (string_compare(t.s8, "off"_s)) {
|
||||
smoothing = 0;
|
||||
}
|
||||
else INVALID_CODEPATH;
|
||||
else invalid_codepath;
|
||||
}
|
||||
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("g"))) {
|
||||
OBJToken t = next_token(&data);
|
||||
TASSERT(t.type == OBJTokenType::word);
|
||||
else if (string_compare(token.s8, "g"_s)) {
|
||||
Obj_Token t = next_token(&data);
|
||||
assert(t.type == Obj_Token_Type::word);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("f"))) {
|
||||
ObjIndex *i = mesh->indices.push_empty();
|
||||
else if (string_compare(token.s8, "f"_s)) {
|
||||
Obj_Index *i = mesh->indices.push_empty_zero();
|
||||
i->smoothing_group_id = smoothing;
|
||||
i->material_id = material_id;
|
||||
i->vertex[0] = (int)expect_number(&data);
|
||||
@@ -338,41 +323,194 @@ FUNCTION Obj parse(Arena *arena, char* data, S8 path_obj_folder) {
|
||||
i->tex[2] = (int)expect_number(&data);
|
||||
expect_token(&data, '/');
|
||||
i->normal[2] = (int)expect_number(&data);
|
||||
//debug_expect_raw(&data, OBJTokenType::whitespace);
|
||||
//debug_expect_raw(&data, Obj_Token_Type::whitespace);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION void test_lex() {
|
||||
function void
|
||||
test_lex() {
|
||||
const char* d = "v 0.885739 0.001910 -0.380334";
|
||||
char* dd = (char *)d;
|
||||
TASSERT(next_token(&dd).type == OBJTokenType::word);
|
||||
OBJToken t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number > 0.8857);
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number > 0.0019);
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number < -0.38);
|
||||
assert(next_token(&dd).type == Obj_Token_Type::word);
|
||||
Obj_Token t = next_token(&dd); assert(t.type == Obj_Token_Type::number && t.number > 0.8857);
|
||||
t = next_token(&dd); assert(t.type == Obj_Token_Type::number && t.number > 0.0019);
|
||||
t = next_token(&dd); assert(t.type == Obj_Token_Type::number && t.number < -0.38);
|
||||
d = "# Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'\n"
|
||||
"# www.blender.org\n"
|
||||
"mtllib f-22.mtl\n"
|
||||
"o F-22\n";
|
||||
dd = (char *)d;
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("mtllib")));
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("f-22.mtl")));
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("o")));
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("F-22")));
|
||||
t = next_token(&dd); assert(t.type == Obj_Token_Type::word && string_compare(t.s8, "mtllib"_s));
|
||||
t = next_token(&dd); assert(t.type == Obj_Token_Type::word && string_compare(t.s8, "f-22.mtl"_s));
|
||||
t = next_token(&dd); assert(t.type == Obj_Token_Type::word && string_compare(t.s8, "o"_s));
|
||||
t = next_token(&dd); assert(t.type == Obj_Token_Type::word && string_compare(t.s8, "F-22"_s));
|
||||
}
|
||||
|
||||
void test() {
|
||||
test_lex();
|
||||
}
|
||||
|
||||
FUNCTION Obj load_obj(Arena *arena, S8 file) {
|
||||
function Obj
|
||||
load_obj(Allocator *arena, String file) {
|
||||
Scratch scratch;
|
||||
S8 data = os_read_file(scratch, file).error_is_fatal();
|
||||
PUSH_SIZE(scratch, 1);
|
||||
S8 path = string_chop_last_slash(file);
|
||||
String data = os_read_file(scratch, file);
|
||||
assert(data.str);
|
||||
|
||||
String path = string_chop_last_slash(file);
|
||||
Obj result = parse(arena, (char *)data.str, path);
|
||||
result.name = file;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T> void
|
||||
dump_array(String_Builder *sb, Array<T> *arr){
|
||||
sb->append_data(arr, sizeof(*arr));
|
||||
sb->append_data(arr->data, sizeof(T)*arr->len);
|
||||
}
|
||||
|
||||
function void
|
||||
dump_bitmap_image(String_Builder *sb, Bitmap *bm){
|
||||
sb->append_data(bm->pixels, sizeof(U32)*bm->x*bm->y);
|
||||
}
|
||||
|
||||
function B32
|
||||
_os_write_file(String file, String data, B32 append = false) {
|
||||
B32 result = false;
|
||||
DWORD access = GENERIC_WRITE;
|
||||
DWORD creation_disposition = CREATE_ALWAYS;
|
||||
if (append) {
|
||||
access = FILE_APPEND_DATA;
|
||||
creation_disposition = OPEN_ALWAYS;
|
||||
}
|
||||
|
||||
Scratch scratch; // @Todo(Krzosa): Unicode
|
||||
HANDLE handle = CreateFileA((const char *)file.str, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
DWORD bytes_written = 0;
|
||||
// @Todo: can only read 32 byte size files?
|
||||
assert_msg(data.len == (U32)data.len,
|
||||
"Max data size os_write can handle is 32 bytes, data to write is "
|
||||
"larger then 32 bytes!");
|
||||
B32 error = WriteFile(handle, data.str, (U32)data.len, &bytes_written, NULL);
|
||||
if (error == false) {
|
||||
log_error("Failed to write to file: %Q", file);
|
||||
}
|
||||
else {
|
||||
if (bytes_written != data.len) {
|
||||
log_error("Failed to write to file: %Q, mismatch between length requested to write and length written", file);
|
||||
}
|
||||
else{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
CloseHandle(handle);
|
||||
}
|
||||
else {
|
||||
log_error("File not found when trying to write: %Q", file);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function B32
|
||||
os_write_file(String file, String data) {
|
||||
return _os_write_file(file, data, false);
|
||||
}
|
||||
function B32
|
||||
os_append_file(String file, String data) {
|
||||
return _os_write_file(file, data, true);
|
||||
}
|
||||
|
||||
struct Stream{
|
||||
U8 *cursor;
|
||||
U8 *end;
|
||||
};
|
||||
|
||||
#define stream_read_array(s,T,c) (T *)stream_read(s,sizeof(T)*(c))
|
||||
#define stream_read_struct(s,T) stream_read_array(s,T,1)
|
||||
function void *
|
||||
stream_read(Stream *s, SizeU size){
|
||||
U8 *result = s->cursor;
|
||||
s->cursor += size;
|
||||
assert(s->end >= s->cursor);
|
||||
return result;
|
||||
}
|
||||
|
||||
function void
|
||||
dump_obj_to_file(Obj *obj){
|
||||
obj->vertices.allocator = 0;
|
||||
obj->vertices.cap = obj->vertices.len;
|
||||
|
||||
obj->texture_coordinates.allocator = 0;
|
||||
obj->texture_coordinates.cap = obj->texture_coordinates.len;
|
||||
|
||||
obj->normals.allocator = 0;
|
||||
obj->normals.cap = obj->normals.len;
|
||||
|
||||
obj->mesh.allocator = 0;
|
||||
obj->mesh.cap = obj->mesh.len;
|
||||
|
||||
obj->materials.allocator = 0;
|
||||
obj->materials.cap = obj->materials.len;
|
||||
|
||||
Iter(obj->mesh){
|
||||
it->indices.allocator = 0;
|
||||
it->indices.cap = it->indices.len;
|
||||
}
|
||||
|
||||
Scratch arena;
|
||||
String_Builder sb = string_builder_make(arena, mib(4));
|
||||
sb.append_data(obj, sizeof(Obj));
|
||||
sb.append_data(obj->name.str, obj->name.len);
|
||||
sb.append_data(obj->vertices.data, obj->vertices.len*sizeof(Vec3));
|
||||
sb.append_data(obj->texture_coordinates.data, obj->texture_coordinates.len*sizeof(Vec2));
|
||||
sb.append_data(obj->normals.data, obj->normals.len*sizeof(Vec3));
|
||||
sb.append_data(obj->mesh.data, obj->mesh.len*sizeof(Obj_Mesh));
|
||||
sb.append_data(obj->materials.data, obj->materials.len*sizeof(Obj_Material));
|
||||
|
||||
Iter(obj->mesh){
|
||||
sb.append_data(it->indices.data, sizeof(Obj_Index)*it->indices.len);
|
||||
}
|
||||
|
||||
Iter(obj->materials){
|
||||
sb.append_data(it, sizeof(Obj_Material));
|
||||
dump_bitmap_image(&sb, &it->texture_ambient);
|
||||
dump_bitmap_image(&sb, &it->texture_diffuse);
|
||||
dump_bitmap_image(&sb, &it->texture_dissolve);
|
||||
dump_bitmap_image(&sb, &it->texture_displacement);
|
||||
}
|
||||
|
||||
String result = string_flatten(&sb);
|
||||
os_write_file("sponza.bin"_s, result);
|
||||
}
|
||||
|
||||
function Obj *
|
||||
load_obj_dump(Allocator *allocator, String filename){
|
||||
String string = os_read_file(allocator, filename);
|
||||
|
||||
Obj *obj = (Obj *)string.str;
|
||||
Stream stream = {(U8 *)(obj+1), string.str + string.len};
|
||||
obj->name.str = stream_read_array(&stream, U8, obj->name.len);
|
||||
obj->vertices.data = stream_read_array(&stream, Vec3, obj->vertices.len);
|
||||
obj->texture_coordinates.data = stream_read_array(&stream, Vec2, obj->texture_coordinates.len);
|
||||
obj->normals.data = stream_read_array(&stream, Vec3, obj->normals.len);
|
||||
obj->mesh.data = stream_read_array(&stream, Obj_Mesh, obj->mesh.len);
|
||||
obj->materials.data = stream_read_array(&stream, Obj_Material, obj->materials.len);
|
||||
|
||||
Iter(obj->mesh){
|
||||
it->indices.data = stream_read_array(&stream, Obj_Index, it->indices.len);
|
||||
}
|
||||
|
||||
Iter(obj->materials){
|
||||
it->texture_ambient.pixels = stream_read_array(&stream, U32, it->texture_ambient.x*it->texture_ambient.y);
|
||||
it->texture_diffuse.pixels = stream_read_array(&stream, U32, it->texture_diffuse.x*it->texture_diffuse.y);
|
||||
it->texture_dissolve.pixels = stream_read_array(&stream, U32, it->texture_dissolve.x*it->texture_dissolve.y);
|
||||
it->texture_displacement.pixels = stream_read_array(&stream, U32, it->texture_displacement.x*it->texture_displacement.y);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
23
profile.cpp
23
profile.cpp
@@ -5,10 +5,10 @@ enum ProfileScopeName {
|
||||
|
||||
struct ProfileScope {
|
||||
U64 samples[5096];
|
||||
I64 i;
|
||||
S64 i;
|
||||
};
|
||||
|
||||
GLOBAL ProfileScope profile_scopes[ProfileScopeName_Count];
|
||||
global ProfileScope profile_scopes[ProfileScopeName_Count];
|
||||
|
||||
#define PROFILE_BEGIN(name) do { \
|
||||
ProfileScope *__profile_scope = profile_scopes + ProfileScopeName_##name; \
|
||||
@@ -22,9 +22,9 @@ GLOBAL ProfileScope profile_scopes[ProfileScopeName_Count];
|
||||
}while (0)
|
||||
|
||||
|
||||
FN void save_profile_data(ProfileScope *scope, S8 scenario_name, S8 scope_name) {
|
||||
/*for (I64 si = 1; si < scope->i; si++) {
|
||||
for (I64 sj = 1; sj < scope->i; sj++) {
|
||||
function void save_profile_data(ProfileScope *scope, S8 scenario_name, S8 scope_name) {
|
||||
/*for (S64 si = 1; si < scope->i; si++) {
|
||||
for (S64 sj = 1; sj < scope->i; sj++) {
|
||||
if (scope->samples[sj] < scope->samples[sj - 1]) {
|
||||
F64 temp = scope->samples[sj];
|
||||
scope->samples[sj] = scope->samples[sj-1];
|
||||
@@ -33,20 +33,21 @@ FN void save_profile_data(ProfileScope *scope, S8 scenario_name, S8 scope_name)
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
Scratch scratch;
|
||||
scenario_name = string_chop_last_period(scenario_name);
|
||||
scenario_name = string_skip_to_last_slash(scenario_name);
|
||||
U8 *string_pointer = string_begin(scratch);
|
||||
S8 build_name = BUILD_NAME;
|
||||
string_format(scratch, "%s %s\n", build_name, scenario_name);
|
||||
I64 one_past_last = scope->i;
|
||||
for (I64 si = 0; si < one_past_last; si++) {
|
||||
string_format(scratch, "%u\n", scope->samples[si]);
|
||||
string_fmt(scratch, "%s %s\n", build_name, scenario_name);
|
||||
S64 one_past_last = scope->i;
|
||||
for (S64 si = 0; si < one_past_last; si++) {
|
||||
string_fmt(scratch, "%u\n", scope->samples[si]);
|
||||
}
|
||||
|
||||
S8 data = string_end(scratch, string_pointer);
|
||||
Date date = os_date();
|
||||
os_make_dir(LIT("stats"));
|
||||
S8 name = string_format(scratch, "stats/%s_%s_%s_%u_%u_%u_%u_%u_%u.txt", scope_name, build_name, scenario_name, date.year, date.month, date.day, date.hour, date.minute, date.second);
|
||||
S8 name = string_fmt(scratch, "stats/%s_%s_%s_%u_%u_%u_%u_%u_%u.txt", scope_name, build_name, scenario_name, date.year, date.month, date.day, date.hour, date.minute, date.second);
|
||||
os_append_file(name, data);
|
||||
*/
|
||||
}
|
||||
9
project.4c
Normal file
9
project.4c
Normal file
@@ -0,0 +1,9 @@
|
||||
project_name='software_render'
|
||||
whitelist=['*.cpp' '*.c' '*.h' '*.4c']
|
||||
blacklist=[]
|
||||
F1='build.bat'
|
||||
F5='build.bat && remedybg start-debugging'
|
||||
F10='build.bat && remedybg run-to-cursor {file} {line}'
|
||||
F11='remedybg main.exe'
|
||||
include=['{project}']
|
||||
// includes_recursive='false'
|
||||
@@ -1,2 +0,0 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
60
ui.cpp
60
ui.cpp
@@ -12,20 +12,20 @@ enum UIWidgetKind {
|
||||
typedef UI_SIGNAL_CALLBACK(UISignalCallback);
|
||||
struct UISetup {
|
||||
UIWidgetKind kind;
|
||||
S8 text;
|
||||
String text;
|
||||
union {
|
||||
void *v;
|
||||
Bitmap *image;
|
||||
B32 *b32;
|
||||
S8 *label;
|
||||
I32 *option;
|
||||
String *label;
|
||||
S32 *option;
|
||||
UISignalCallback *signal_callback;
|
||||
};
|
||||
I32 option_max;
|
||||
S32 option_max;
|
||||
};
|
||||
#define UI_BOOL(text, x) {UIWidgetKind_Boolean,text,(void*)(x)}
|
||||
#define UI_IMAGE(x) {UIWidgetKind_Image,string_null,(void*)(x)}
|
||||
#define UI_LABEL(x) {UIWidgetKind_Label,string_null,(void*)(x)}
|
||||
#define UI_IMAGE(x) {UIWidgetKind_Image,{},(void*)(x)}
|
||||
#define UI_LABEL(x) {UIWidgetKind_Label,{},(void*)(x)}
|
||||
#define UI_OPTION(text,x,option_max){UIWidgetKind_Option,text,(void*)(x),(option_max)}
|
||||
#define UI_SIGNAL(text,x){UIWidgetKind_Signal,text,(void*)(x)}
|
||||
|
||||
@@ -36,14 +36,14 @@ struct UIWidget {
|
||||
UIWidget *first_child;
|
||||
UIWidget *last_child;
|
||||
|
||||
S8 text;
|
||||
String text;
|
||||
Vec2 size;
|
||||
I32 option_max;
|
||||
S32 option_max;
|
||||
union {
|
||||
Bitmap *image;
|
||||
B32 *b32;
|
||||
S8 *label;
|
||||
I32 *option;
|
||||
String *label;
|
||||
S32 *option;
|
||||
UISignalCallback *signal_callback;
|
||||
} ptr;
|
||||
};
|
||||
@@ -55,23 +55,23 @@ struct UI : UIWidget {
|
||||
UIWidget *active;
|
||||
};
|
||||
|
||||
FUNCTION UIWidget *ui_new_widget(Arena *arena, UIWidgetKind kind) {
|
||||
UIWidget *result = PUSH_STRUCT(arena, UIWidget);
|
||||
function UIWidget *ui_new_widget(Allocator *arena, UIWidgetKind kind) {
|
||||
UIWidget *result = exp_alloc_type(arena, UIWidget);
|
||||
result->kind = kind;
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION void ui_push_child(UIWidget *widget, UIWidget *child) {
|
||||
DLL_QUEUE_PUSH(widget->first_child, widget->last_child, child);
|
||||
function void ui_push_child(UIWidget *widget, UIWidget *child) {
|
||||
//DLL_QUEUE_PUSH(widget->first_child, widget->last_child, child);
|
||||
}
|
||||
|
||||
FUNCTION UIWidget *ui_push_child(Arena *arena, UIWidget *widget, UIWidgetKind kind) {
|
||||
function UIWidget *ui_push_child(Arena *arena, UIWidget *widget, UIWidgetKind kind) {
|
||||
UIWidget *result = ui_new_widget(arena, kind);
|
||||
ui_push_child(widget, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION UIWidget *ui_push_image(Arena *arena, UIWidget *widget, Bitmap *img) {
|
||||
function UIWidget *ui_push_image(Arena *arena, UIWidget *widget, Bitmap *img) {
|
||||
UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Image);
|
||||
result->ptr.image = img;
|
||||
|
||||
@@ -81,20 +81,20 @@ FUNCTION UIWidget *ui_push_image(Arena *arena, UIWidget *widget, Bitmap *img) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION UIWidget *ui_push_bool(Arena *arena, UIWidget *widget, S8 string, B32 *b32) {
|
||||
function UIWidget *ui_push_bool(Arena *arena, UIWidget *widget, String string, B32 *b32) {
|
||||
UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Boolean);
|
||||
result->text = string;
|
||||
result->ptr.b32 = b32;
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION UIWidget *ui_push_string(Arena *arena, UIWidget *widget, S8 *string) {
|
||||
function UIWidget *ui_push_string(Arena *arena, UIWidget *widget, String *string) {
|
||||
UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Label);
|
||||
result->ptr.label = string;
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION UIWidget *ui_push_option(Arena *arena, UIWidget *widget, S8 string, I32 *option, I32 option_max) {
|
||||
function UIWidget *ui_push_option(Arena *arena, UIWidget *widget, String string, S32 *option, S32 option_max) {
|
||||
UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Option);
|
||||
result->text = string;
|
||||
result->ptr.option = option;
|
||||
@@ -102,16 +102,16 @@ FUNCTION UIWidget *ui_push_option(Arena *arena, UIWidget *widget, S8 string, I32
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION UIWidget *ui_push_signal(Arena *arena, UIWidget *widget, S8 string, UISignalCallback *callback) {
|
||||
function UIWidget *ui_push_signal(Arena *arena, UIWidget *widget, String string, UISignalCallback *callback) {
|
||||
UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Signal);
|
||||
result->text = string;
|
||||
result->ptr.signal_callback = callback;
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) {
|
||||
function UI ui_make(UISetup *setup, U64 len) {
|
||||
UI result = {};
|
||||
result.arena = arena_sub(arena, MiB(16));
|
||||
arena_init(&result.arena, "UI_Arena"_s);
|
||||
UIWidget *parent = &result;
|
||||
for (UISetup *s = setup; s != (setup+len); s++) {
|
||||
switch (s->kind) {
|
||||
@@ -130,13 +130,13 @@ FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) {
|
||||
case UIWidgetKind_Signal: {
|
||||
ui_push_signal(&result.arena, parent, s->text, s->signal_callback);
|
||||
} break;
|
||||
INVALID_DEFAULT_CASE;
|
||||
invalid_default_case;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION B32 ui_mouse_test(UI *ui, UIWidget *w, Vec4 rect) {
|
||||
function B32 ui_mouse_test(UI *ui, UIWidget *w, Vec4 rect) {
|
||||
B32 result = false;
|
||||
if (os.mouse_pos.x > rect.x && os.mouse_pos.x < rect.x + rect.width &&
|
||||
os.mouse_pos.y > rect.y && os.mouse_pos.y < rect.y + rect.height) {
|
||||
@@ -160,7 +160,7 @@ FUNCTION B32 ui_mouse_test(UI *ui, UIWidget *w, Vec4 rect) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
|
||||
function void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
|
||||
Scratch scratch;
|
||||
Vec2 pos = vec2(0, (F32)dst->y);
|
||||
for (UIWidget *w = ui->first_child; w; w = w->next) {
|
||||
@@ -170,7 +170,7 @@ FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
|
||||
pos.y -= w->size.y;
|
||||
rect = vec4(pos, w->size);
|
||||
ui_mouse_test(ui, w, rect);
|
||||
S8 string = string_format(scratch, "%d %d", w->ptr.image->x, w->ptr.image->y);
|
||||
String string = string_fmt(scratch, "%d %d", w->ptr.image->x, w->ptr.image->y);
|
||||
r_draw_string(dst, font, string, pos);
|
||||
r_draw_bitmap(dst, w->ptr.image, pos, w->size);
|
||||
if (ui->active == w) {
|
||||
@@ -185,7 +185,7 @@ FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
|
||||
case UIWidgetKind_Boolean: {
|
||||
pos.y -= font->height;
|
||||
Vec4 color = vec4(0, 0, 0, 1);
|
||||
S8 string = string_format(scratch, "%s %d", w->text, *w->ptr.b32);
|
||||
String string = string_fmt(scratch, "%s %d", w->text, *w->ptr.b32);
|
||||
rect = r_get_string_rect(font, string, pos);
|
||||
B32 clicked = ui_mouse_test(ui, w, rect);
|
||||
if (clicked) *w->ptr.b32 = !*w->ptr.b32;
|
||||
@@ -205,7 +205,7 @@ FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
|
||||
case UIWidgetKind_Option: {
|
||||
pos.y -= font->height;
|
||||
Vec4 color = vec4(0, 0, 0, 1);
|
||||
S8 string = string_format(scratch, "%s %d", w->text, *w->ptr.option);
|
||||
String string = string_fmt(scratch, "%s %d", w->text, *w->ptr.option);
|
||||
rect = r_get_string_rect(font, string, pos);
|
||||
B32 clicked = ui_mouse_test(ui, w, rect);
|
||||
if (clicked) {
|
||||
@@ -222,7 +222,7 @@ FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
|
||||
case UIWidgetKind_Signal: {
|
||||
pos.y -= font->height;
|
||||
Vec4 color = vec4(0, 0, 0, 1);
|
||||
S8 string = string_format(scratch, "%s", w->text);
|
||||
String string = string_fmt(scratch, "%s", w->text);
|
||||
rect = r_get_string_rect(font, string, pos);
|
||||
B32 clicked = ui_mouse_test(ui, w, rect);
|
||||
if (clicked) {
|
||||
@@ -236,7 +236,7 @@ FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
|
||||
rect = r_draw_string(dst, font, string, pos);
|
||||
pos.y -= rect.height - font->height;
|
||||
} break;
|
||||
INVALID_DEFAULT_CASE;
|
||||
invalid_default_case;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user