Init repository
This commit is contained in:
1
.gitignore
vendored
Executable file
1
.gitignore
vendored
Executable file
@@ -0,0 +1 @@
|
|||||||
|
build/
|
||||||
6
build.bat
Executable file
6
build.bat
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cl.exe -Fe:visualize.exe ../src/visualize/vis_main.cpp -WX -W3 -wd4200 -diagnostics:column -nologo -FC -Z7 -GF -Gm- -Oi -Zo -D_CRT_SECURE_NO_WARNINGS -Od -D_DEBUG -MTd -EHa- -GR- -link -incremental:no -opt:ref -NODEFAULTLIB:LIBCMT
|
||||||
|
cd ..
|
||||||
607
src/core/arena.h
Executable file
607
src/core/arena.h
Executable file
@@ -0,0 +1,607 @@
|
|||||||
|
#ifndef MA_HEADER
|
||||||
|
#define MA_HEADER
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define MA_KIB(x) ((x##ull) * 1024ull)
|
||||||
|
#define MA_MIB(x) (MA_KIB(x) * 1024ull)
|
||||||
|
#define MA_GIB(x) (MA_MIB(x) * 1024ull)
|
||||||
|
#define MA_TIB(x) (MA_GIB(x) * 1024ull)
|
||||||
|
|
||||||
|
#ifndef MA_DEFAULT_RESERVE_SIZE
|
||||||
|
#define MA_DEFAULT_RESERVE_SIZE MA_GIB(1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_DEFAULT_ALIGNMENT
|
||||||
|
#define MA_DEFAULT_ALIGNMENT 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_COMMIT_ADD_SIZE
|
||||||
|
#define MA_COMMIT_ADD_SIZE MA_MIB(4)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_INHERIT_HOOK
|
||||||
|
#define MA_INHERIT_HOOK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_C_INHERIT_HOOK
|
||||||
|
#define MA_C_INHERIT_HOOK M_Allocator allocator;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_ZERO_IS_INITIALIZATION
|
||||||
|
#define MA_ZERO_IS_INITIALIZATION 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_INIT_HOOK
|
||||||
|
#define MA_INIT_HOOK(arena) \
|
||||||
|
arena->allocator.obj = (void *)arena; \
|
||||||
|
arena->allocator.p = (M_AllocatorProc *)MA_AllocatorProc;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_API
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define MA_API extern "C"
|
||||||
|
#else
|
||||||
|
#define MA_API
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_THREAD_LOCAL
|
||||||
|
#if defined(__cplusplus) && __cplusplus >= 201103L
|
||||||
|
#define MA_THREAD_LOCAL thread_local
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define MA_THREAD_LOCAL __thread
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define MA_THREAD_LOCAL __declspec(thread)
|
||||||
|
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
|
||||||
|
#define MA_THREAD_LOCAL _Thread_local
|
||||||
|
#elif defined(__TINYC__)
|
||||||
|
#define MA_THREAD_LOCAL _Thread_local
|
||||||
|
#else
|
||||||
|
#error Couldnt figure out thread local, needs to be provided manually
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum M_AllocatorOp {
|
||||||
|
M_AllocatorOp_Invalid,
|
||||||
|
M_AllocatorOp_Allocate,
|
||||||
|
M_AllocatorOp_Deallocate,
|
||||||
|
} M_AllocatorOp;
|
||||||
|
|
||||||
|
typedef struct MV_Memory MV_Memory;
|
||||||
|
typedef struct MA_Checkpoint MA_Checkpoint;
|
||||||
|
typedef struct MA_Arena MA_Arena;
|
||||||
|
typedef struct M_Allocator M_Allocator;
|
||||||
|
typedef void *M_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size);
|
||||||
|
|
||||||
|
struct M_Allocator {
|
||||||
|
void *obj;
|
||||||
|
void *(*p)(void *allocator, M_AllocatorOp kind, void *p, size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MV_Memory {
|
||||||
|
size_t commit;
|
||||||
|
size_t reserve;
|
||||||
|
uint8_t *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MA_Arena MA_INHERIT_HOOK {
|
||||||
|
MA_C_INHERIT_HOOK
|
||||||
|
MV_Memory memory;
|
||||||
|
int alignment;
|
||||||
|
int saved_alignment;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
size_t packed_array_element_size;
|
||||||
|
size_t packed_array_begin;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MA_Checkpoint {
|
||||||
|
MA_Arena *arena;
|
||||||
|
size_t pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MA_PushArrayNonZeroed(a, T, c) (T *)MA_PushSizeNonZeroed(a, sizeof(T) * (c))
|
||||||
|
#define MA_PushStructNonZeroed(a, T) (T *)MA_PushSizeNonZeroed(a, sizeof(T))
|
||||||
|
#define MA_PushStruct(a, T) (T *)MA_PushSize(a, sizeof(T))
|
||||||
|
#define MA_PushArray(a, T, c) (T *)MA_PushSize(a, sizeof(T) * (c))
|
||||||
|
#define MA_PushStructCopy(a, T, p) (T *)MA_PushCopy(a, (p), sizeof(T))
|
||||||
|
#define MA_BeginPackedArray(arena, T) (T *)MA__BeginPackedArray(arena, sizeof(T))
|
||||||
|
#define MA_CheckpointScope(name, InArena) for (MA_Checkpoint name = MA_Save(InArena); name.arena; (MA_Load(name), name.arena = 0))
|
||||||
|
|
||||||
|
#define M_AllocStruct(a, T) (T *)M_Alloc((a), sizeof(T))
|
||||||
|
#define M_AllocArray(a, T, c) (T *)M_Alloc((a), sizeof(T) * (c))
|
||||||
|
#define M_AllocStructCopy(a, T, p) (T *)M_PushCopy(a, (p), sizeof(T))
|
||||||
|
|
||||||
|
#define MA_IS_POW2(x) (((x) & ((x)-1)) == 0)
|
||||||
|
#define MA_MIN(x, y) ((x) <= (y) ? (x) : (y))
|
||||||
|
#define MA_MAX(x, y) ((x) >= (y) ? (x) : (y))
|
||||||
|
|
||||||
|
#define MA_CLAMP_TOP(x, max) ((x) >= (max) ? (max) : (x))
|
||||||
|
#define MA_CLAMP_BOT(x, min) ((x) <= (min) ? (min) : (x))
|
||||||
|
#define MA_CLAMP(x, min, max) ((x) >= (max) ? (max) : (x) <= (min) ? (min) \
|
||||||
|
: (x))
|
||||||
|
// clang-format off
|
||||||
|
MA_API void MA_MemoryZero(void *p, size_t size);
|
||||||
|
MA_API void MA_MemoryCopy(void *dst, void *src, size_t size);
|
||||||
|
MA_API size_t MA_GetAlignOffset(size_t size, size_t align);
|
||||||
|
MA_API size_t MA_AlignUp(size_t size, size_t align);
|
||||||
|
MA_API size_t MA_AlignDown(size_t size, size_t align);
|
||||||
|
MA_API void MA_DeallocateStub(MA_Arena *arena, void *p);
|
||||||
|
MA_API void MA_PopToPos(MA_Arena *arena, size_t pos);
|
||||||
|
MA_API void * MA_PopSize(MA_Arena *arena, size_t size);
|
||||||
|
MA_API void MA_DeallocateArena(MA_Arena *arena);
|
||||||
|
MA_API void MA_Reset(MA_Arena *arena);
|
||||||
|
MA_API void * MA__BeginPackedArray(MA_Arena *arena, size_t element_size);
|
||||||
|
MA_API int MA_EndPackedArray(MA_Arena *arena);
|
||||||
|
MA_API void MA_SetAlignment(MA_Arena *arena, int alignment);
|
||||||
|
MA_API uint8_t * MA_GetTop(MA_Arena *a);
|
||||||
|
MA_API void * MA_PushSizeNonZeroed(MA_Arena *a, size_t size);
|
||||||
|
MA_API void * MA_PushSize(MA_Arena *arena, size_t size);
|
||||||
|
MA_API void MA_InitEx(MA_Arena *a, size_t reserve);
|
||||||
|
MA_API void MA_Init(MA_Arena *a);
|
||||||
|
MA_API void MA_MakeSureInitialized(MA_Arena *a);
|
||||||
|
MA_API MA_Arena * MA_Bootstrap(void);
|
||||||
|
MA_API void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size);
|
||||||
|
MA_API MA_Arena MA_MakeFromBuffer(void *buffer, size_t size);
|
||||||
|
MA_API char * MA_PushStringCopy(MA_Arena *arena, char *p, size_t size);
|
||||||
|
MA_API void * MA_PushCopy(MA_Arena *arena, void *p, size_t size);
|
||||||
|
MA_API bool MA_IsPointerInside(MA_Arena *arena, void *p);
|
||||||
|
MA_API MA_Arena MA_PushArena(MA_Arena *arena, size_t size);
|
||||||
|
MA_API MA_Checkpoint MA_Save(MA_Arena *arena);
|
||||||
|
MA_API void MA_Load(MA_Checkpoint checkpoint);
|
||||||
|
MA_API MA_Checkpoint MA_GetScratchEx(MA_Arena **conflicts, int conflict_count);
|
||||||
|
MA_API MA_Checkpoint MA_GetScratch(void);
|
||||||
|
MA_API MA_Checkpoint MA_GetScratch1(MA_Arena *conflict);
|
||||||
|
MA_API void * MA_AllocatorProc(M_Allocator allocator, M_AllocatorOp kind, void *p, size_t size);
|
||||||
|
|
||||||
|
MA_API MV_Memory MV_Reserve(size_t size);
|
||||||
|
MA_API bool MV_Commit(MV_Memory *m, size_t commit);
|
||||||
|
MA_API void MV_Deallocate(MV_Memory *m);
|
||||||
|
MA_API bool MV_DecommitPos(MV_Memory *m, size_t pos);
|
||||||
|
|
||||||
|
MA_API void * M_AllocNonZeroed(M_Allocator allocator, size_t size);
|
||||||
|
MA_API void * M_Alloc(M_Allocator allocator, size_t size);
|
||||||
|
MA_API void * M_AllocCopy(M_Allocator allocator, void *p, size_t size);
|
||||||
|
MA_API void M_Dealloc(M_Allocator allocator, void *p);
|
||||||
|
MA_API M_Allocator M_GetSystemAllocator(void);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#ifdef MA_ENABLE_SCRATCH
|
||||||
|
extern MA_THREAD_LOCAL MA_Arena MA_ScratchArenaPool[];
|
||||||
|
#define MA_ScratchScope(x) for (MA_Checkpoint x = MA_GetScratch(); x.arena; (MA_ReleaseScratch(x), x.arena = 0))
|
||||||
|
#define MA_ReleaseScratch MA_Load
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
struct MA_Scratch {
|
||||||
|
MA_Checkpoint checkpoint;
|
||||||
|
MA_Scratch() { this->checkpoint = MA_GetScratch(); }
|
||||||
|
~MA_Scratch() { MA_Load(checkpoint); }
|
||||||
|
operator MA_Arena *() { return checkpoint.arena; }
|
||||||
|
|
||||||
|
private: // @Note: Disable copy constructors, cause its error prone
|
||||||
|
MA_Scratch(MA_Scratch &arena);
|
||||||
|
MA_Scratch(MA_Scratch &arena, MA_Scratch &a2);
|
||||||
|
};
|
||||||
|
#endif // __cplusplus
|
||||||
|
#endif // MA_ENABLE_SCRATCH
|
||||||
|
#endif // MA_HEADER
|
||||||
|
|
||||||
|
#ifdef MA_IMPLEMENTATION
|
||||||
|
|
||||||
|
#ifndef MA_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define MA_ASSERT(x) assert(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_MemoryZero
|
||||||
|
#include <string.h>
|
||||||
|
MA_API void MA_MemoryZero(void *p, size_t size) {
|
||||||
|
memset(p, 0, size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_MemoryCopy
|
||||||
|
#include <string.h>
|
||||||
|
MA_API void MA_MemoryCopy(void *dst, void *src, size_t size) {
|
||||||
|
memcpy(dst, src, size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_CMalloc
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define MA_CMalloc(x) malloc(x)
|
||||||
|
#define MA_CFree(x) free(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MA_FN
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define MA_FN __attribute__((unused)) static
|
||||||
|
#else
|
||||||
|
#define MA_FN static
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MA_Lengthof(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
|
||||||
|
MA_API size_t MA_GetAlignOffset(size_t size, size_t align) {
|
||||||
|
size_t mask = align - 1;
|
||||||
|
size_t val = size & mask;
|
||||||
|
if (val) {
|
||||||
|
val = align - val;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API size_t MA_AlignUp(size_t size, size_t align) {
|
||||||
|
size_t result = size + MA_GetAlignOffset(size, align);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API size_t MA_AlignDown(size_t size, size_t align) {
|
||||||
|
size += 1; // Make sure when align is 8 doesn't get rounded down to 0
|
||||||
|
size_t result = size - (align - MA_GetAlignOffset(size, align));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_FN uint8_t *MV__AdvanceCommit(MV_Memory *m, size_t *commit_size, size_t page_size) {
|
||||||
|
size_t aligned_up_commit = MA_AlignUp(*commit_size, page_size);
|
||||||
|
size_t to_be_total_commited_size = aligned_up_commit + m->commit;
|
||||||
|
size_t to_be_total_commited_size_clamped_to_reserve = MA_CLAMP_TOP(to_be_total_commited_size, m->reserve);
|
||||||
|
size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit;
|
||||||
|
MA_ASSERT(adjusted_to_boundary_commit && "Reached the virtual memory reserved boundary");
|
||||||
|
*commit_size = adjusted_to_boundary_commit;
|
||||||
|
|
||||||
|
if (adjusted_to_boundary_commit == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t *result = m->data + m->commit;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MA_DeallocateStub(MA_Arena *arena, void *p) {}
|
||||||
|
|
||||||
|
MA_API void MA_PopToPos(MA_Arena *arena, size_t pos) {
|
||||||
|
pos = MA_CLAMP_TOP(pos, arena->len);
|
||||||
|
arena->len = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void *MA_PopSize(MA_Arena *arena, size_t size) {
|
||||||
|
size = MA_CLAMP_TOP(size, arena->len);
|
||||||
|
arena->len -= size;
|
||||||
|
return arena->memory.data + arena->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MA_DeallocateArena(MA_Arena *arena) {
|
||||||
|
MV_Deallocate(&arena->memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MA_Reset(MA_Arena *arena) {
|
||||||
|
MA_PopToPos(arena, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_FN size_t MA__AlignLen(MA_Arena *a) {
|
||||||
|
size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
|
||||||
|
size_t aligned = a->len + align_offset;
|
||||||
|
return aligned;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void *MA__BeginPackedArray(MA_Arena *arena, size_t element_size) {
|
||||||
|
MA_ASSERT(arena->memory.data);
|
||||||
|
arena->len = MA__AlignLen(arena);
|
||||||
|
arena->saved_alignment = arena->alignment;
|
||||||
|
arena->alignment = 0;
|
||||||
|
arena->packed_array_begin = arena->len;
|
||||||
|
arena->packed_array_element_size = element_size;
|
||||||
|
void *result = arena->memory.data + arena->len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API int MA_EndPackedArray(MA_Arena *arena) {
|
||||||
|
arena->alignment = arena->saved_alignment;
|
||||||
|
size_t different = (arena->len - arena->packed_array_begin);
|
||||||
|
int result = (int)((arena->len - arena->packed_array_begin) / arena->packed_array_element_size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MA_SetAlignment(MA_Arena *arena, int alignment) {
|
||||||
|
arena->alignment = alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API uint8_t *MA_GetTop(MA_Arena *a) {
|
||||||
|
MA_ASSERT(a->memory.data);
|
||||||
|
return a->memory.data + a->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void *MA_PushSizeNonZeroed(MA_Arena *a, size_t size) {
|
||||||
|
size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
|
||||||
|
size_t aligned_len = a->len + align_offset;
|
||||||
|
size_t size_with_alignment = size + align_offset;
|
||||||
|
|
||||||
|
if (a->len + size_with_alignment > a->memory.commit) {
|
||||||
|
if (a->memory.reserve == 0) {
|
||||||
|
#if MA_ZERO_IS_INITIALIZATION
|
||||||
|
MA_Init(a);
|
||||||
|
#else
|
||||||
|
MA_ASSERT("Pushing on uninitialized arena");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
bool result = MV_Commit(&a->memory, size_with_alignment + MA_COMMIT_ADD_SIZE);
|
||||||
|
MA_ASSERT(result && "Failed to commit memory");
|
||||||
|
(void)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *result = a->memory.data + aligned_len;
|
||||||
|
a->len += size_with_alignment;
|
||||||
|
MA_ASSERT(a->len <= a->memory.commit);
|
||||||
|
return (void *)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void *MA_PushSize(MA_Arena *arena, size_t size) {
|
||||||
|
void *result = MA_PushSizeNonZeroed(arena, size);
|
||||||
|
MA_MemoryZero(result, size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MA_InitEx(MA_Arena *a, size_t reserve) {
|
||||||
|
a->memory = MV_Reserve(reserve);
|
||||||
|
a->alignment = MA_DEFAULT_ALIGNMENT;
|
||||||
|
MA_INIT_HOOK(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MA_Init(MA_Arena *a) {
|
||||||
|
MA_InitEx(a, MA_DEFAULT_RESERVE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MA_MakeSureInitialized(MA_Arena *a) {
|
||||||
|
if (a->memory.data == 0) {
|
||||||
|
MA_Init(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API MA_Arena *MA_Bootstrap(void) {
|
||||||
|
MA_Arena bootstrap_arena = {0};
|
||||||
|
MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena);
|
||||||
|
*arena = bootstrap_arena;
|
||||||
|
arena->allocator.obj = arena;
|
||||||
|
return arena;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size) {
|
||||||
|
arena->memory.data = (uint8_t *)buffer;
|
||||||
|
arena->memory.commit = size;
|
||||||
|
arena->memory.reserve = size;
|
||||||
|
arena->alignment = MA_DEFAULT_ALIGNMENT;
|
||||||
|
MA_INIT_HOOK(arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API MA_Arena MA_MakeFromBuffer(void *buffer, size_t size) {
|
||||||
|
MA_Arena arena;
|
||||||
|
MA_MemoryZero(&arena, sizeof(arena));
|
||||||
|
MA_InitFromBuffer(&arena, buffer, size);
|
||||||
|
return arena;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API char *MA_PushStringCopy(MA_Arena *arena, char *p, size_t size) {
|
||||||
|
char *copy_buffer = (char *)MA_PushSizeNonZeroed(arena, size + 1);
|
||||||
|
MA_MemoryCopy(copy_buffer, p, size);
|
||||||
|
copy_buffer[size] = 0;
|
||||||
|
return copy_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void *MA_PushCopy(MA_Arena *arena, void *p, size_t size) {
|
||||||
|
void *copy_buffer = MA_PushSizeNonZeroed(arena, size);
|
||||||
|
MA_MemoryCopy(copy_buffer, p, size);
|
||||||
|
return copy_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API bool MA_IsPointerInside(MA_Arena *arena, void *p) {
|
||||||
|
uintptr_t pointer = (uintptr_t)p;
|
||||||
|
uintptr_t start = (uintptr_t)arena->memory.data;
|
||||||
|
uintptr_t stop = start + (uintptr_t)arena->len;
|
||||||
|
bool result = pointer >= start && pointer < stop;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API MA_Arena MA_PushArena(MA_Arena *arena, size_t size) {
|
||||||
|
MA_Arena result;
|
||||||
|
MA_MemoryZero(&result, sizeof(result));
|
||||||
|
result.memory.data = MA_PushArrayNonZeroed(arena, uint8_t, size);
|
||||||
|
result.memory.commit = size;
|
||||||
|
result.memory.reserve = size;
|
||||||
|
result.alignment = arena->alignment;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API MA_Checkpoint MA_Save(MA_Arena *arena) {
|
||||||
|
MA_Checkpoint result;
|
||||||
|
result.pos = arena->len;
|
||||||
|
result.arena = arena;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MA_Load(MA_Checkpoint checkpoint) {
|
||||||
|
MA_PopToPos(checkpoint.arena, checkpoint.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void *M_AllocNonZeroed(M_Allocator allocator, size_t size) {
|
||||||
|
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void *M_Alloc(M_Allocator allocator, size_t size) {
|
||||||
|
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size);
|
||||||
|
MA_MemoryZero(p, size);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void *M_AllocCopy(M_Allocator allocator, void *p, size_t size) {
|
||||||
|
void *copy_buffer = M_AllocNonZeroed(allocator, size);
|
||||||
|
MA_MemoryCopy(copy_buffer, p, size);
|
||||||
|
return copy_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void M_Dealloc(M_Allocator allocator, void *p) {
|
||||||
|
allocator.p(allocator.obj, M_AllocatorOp_Deallocate, p, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_FN void *M_ClibAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size) {
|
||||||
|
if (kind == M_AllocatorOp_Allocate) {
|
||||||
|
return MA_CMalloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind == M_AllocatorOp_Deallocate) {
|
||||||
|
MA_CFree(p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_ASSERT("MA_Arena invalid codepath");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void *MA_AllocatorProc(M_Allocator allocator, M_AllocatorOp kind, void *p, size_t size) {
|
||||||
|
if (kind == M_AllocatorOp_Allocate) {
|
||||||
|
return MA_PushSizeNonZeroed((MA_Arena *)allocator.obj, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind == M_AllocatorOp_Deallocate) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_ASSERT("MA_Arena invalid codepath");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API M_Allocator M_GetSystemAllocator(void) {
|
||||||
|
M_Allocator a = {0};
|
||||||
|
a.p = M_ClibAllocatorProc;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MA_ENABLE_SCRATCH
|
||||||
|
MA_THREAD_LOCAL MA_Arena MA_ScratchArenaPool[4];
|
||||||
|
|
||||||
|
MA_API MA_Checkpoint MA_GetScratchEx(MA_Arena **conflicts, int conflict_count) {
|
||||||
|
MA_Arena *unoccupied = 0;
|
||||||
|
for (int i = 0; i < MA_Lengthof(MA_ScratchArenaPool); i += 1) {
|
||||||
|
MA_Arena *from_pool = MA_ScratchArenaPool + i;
|
||||||
|
unoccupied = from_pool;
|
||||||
|
for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) {
|
||||||
|
MA_Arena *from_conflict = conflicts[conflict_i];
|
||||||
|
if (from_pool == from_conflict) {
|
||||||
|
unoccupied = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unoccupied) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_ASSERT(unoccupied);
|
||||||
|
MA_Checkpoint result = MA_Save(unoccupied);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API MA_Checkpoint MA_GetScratch(void) {
|
||||||
|
MA_Checkpoint result = MA_Save(MA_ScratchArenaPool + 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API MA_Checkpoint MA_GetScratch1(MA_Arena *conflict) {
|
||||||
|
MA_Arena *conflicts[] = {conflict};
|
||||||
|
return MA_GetScratchEx(conflicts, 1);
|
||||||
|
}
|
||||||
|
#endif // MA_ENABLE_SCRATCH
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
const size_t MV__WIN32_PAGE_SIZE = 4096;
|
||||||
|
|
||||||
|
MA_API MV_Memory MV_Reserve(size_t size) {
|
||||||
|
MV_Memory result;
|
||||||
|
MA_MemoryZero(&result, sizeof(result));
|
||||||
|
size_t adjusted_size = MA_AlignUp(size, MV__WIN32_PAGE_SIZE);
|
||||||
|
result.data = (uint8_t *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
MA_ASSERT(result.data && "Failed to reserve virtual memory");
|
||||||
|
result.reserve = adjusted_size;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API bool MV_Commit(MV_Memory *m, size_t commit) {
|
||||||
|
uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__WIN32_PAGE_SIZE);
|
||||||
|
if (pointer) {
|
||||||
|
void *result = VirtualAlloc(pointer, commit, MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
MA_ASSERT(result && "Failed to commit more memory");
|
||||||
|
if (result) {
|
||||||
|
m->commit += commit;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MV_Deallocate(MV_Memory *m) {
|
||||||
|
BOOL result = VirtualFree(m->data, 0, MEM_RELEASE);
|
||||||
|
MA_ASSERT(result != 0 && "Failed to release MV_Memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API bool MV_DecommitPos(MV_Memory *m, size_t pos) {
|
||||||
|
size_t aligned = MA_AlignDown(pos, MV__WIN32_PAGE_SIZE);
|
||||||
|
size_t adjusted_pos = MA_CLAMP_TOP(aligned, m->commit);
|
||||||
|
size_t size_to_decommit = m->commit - adjusted_pos;
|
||||||
|
if (size_to_decommit) {
|
||||||
|
uint8_t *base_address = m->data + adjusted_pos;
|
||||||
|
BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT);
|
||||||
|
if (result) {
|
||||||
|
m->commit -= size_to_decommit;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif __unix__ // _WIN32
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#define MV__UNIX_PAGE_SIZE 4096
|
||||||
|
MA_API MV_Memory MV_Reserve(size_t size) {
|
||||||
|
MV_Memory result = {};
|
||||||
|
size_t size_aligned = MA_AlignUp(size, MV__UNIX_PAGE_SIZE);
|
||||||
|
result.data = (uint8_t *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
MA_ASSERT(result.data && "Failed to reserve memory using mmap!!");
|
||||||
|
if (result.data) {
|
||||||
|
result.reserve = size_aligned;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API bool MV_Commit(MV_Memory *m, size_t commit) {
|
||||||
|
uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__UNIX_PAGE_SIZE);
|
||||||
|
if (pointer) {
|
||||||
|
int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE);
|
||||||
|
MA_ASSERT(mprotect_result == 0 && "Failed to commit more memory using mmap");
|
||||||
|
if (mprotect_result == 0) {
|
||||||
|
m->commit += commit;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void MV_Deallocate(MV_Memory *m) {
|
||||||
|
int result = munmap(m->data, m->reserve);
|
||||||
|
MA_ASSERT(result == 0 && "Failed to release virtual memory using munmap");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error "unhandled arena platform"
|
||||||
|
#endif // __unix__
|
||||||
|
#endif // MA_IMPL
|
||||||
73
src/core/array.h
Executable file
73
src/core/array.h
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
#ifndef ARRAY_HEADER
|
||||||
|
#define ARRAY_HEADER
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifndef AUser_Assert
|
||||||
|
#include <assert.h>
|
||||||
|
#define AUser_Assert(x) assert(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Array_LongNames
|
||||||
|
#define Add A_Add
|
||||||
|
#define Pop A_Pop
|
||||||
|
#define Last A_Last
|
||||||
|
#define Reserve A_Reserve
|
||||||
|
#define SetLen A_SetLen
|
||||||
|
#define UnorderedRemove A_UnorderedRemove
|
||||||
|
#define OrderedRemove A_OrderedRemove
|
||||||
|
#define Insert A_Insert
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define Array(T) \
|
||||||
|
struct { \
|
||||||
|
T *data; \
|
||||||
|
int cap, len; \
|
||||||
|
}
|
||||||
|
typedef Array(void) Array_Void;
|
||||||
|
#define For(i, n) for (int i = 0; i < n; i += 1)
|
||||||
|
#define FoR(i, n) for (int i = n - 1; i >= 0; i -= 1)
|
||||||
|
|
||||||
|
#define A_ElementSize(a) sizeof(*(a)->data)
|
||||||
|
#define A_Add(a, e) (A__Grow((Array_Void *)a, A_ElementSize(a)), (a)->data[(a)->len++] = (e))
|
||||||
|
#define A_Pop(a) (AUser_Assert((a)->len > 0), (a)->data[--(a)->len])
|
||||||
|
#define A_Last(a) (AUser_Assert((a)->len > 0), (a)->data[(a)->len - 1])
|
||||||
|
#define A_Reserve(a, n) (A__Reserve((Array_Void *)(a), A_ElementSize(a), (n)))
|
||||||
|
#define A_SetLen(a, n) (A__Reserve((Array_Void *)(a), A_ElementSize(a), (n)), (a)->len = (n))
|
||||||
|
#define A_UnorderedRemove(a, n) ( \
|
||||||
|
AUser_Assert((n) >= 0 && (n) < (a)->len), \
|
||||||
|
(a)->data[(n)] = A_Pop(a))
|
||||||
|
#define A_OrderedRemoveN(a, i, n) ( \
|
||||||
|
AUser_Assert((n) >= 0 && (n) < (a)->len), \
|
||||||
|
memcpy((a)->data + i, (a)->data + (i) + (n), A_ElementSize(a) * ((a)->len - (i) - (n))), \
|
||||||
|
(a)->len -= (n))
|
||||||
|
#define A_OrderedRemove(a, i) A_OrderedRemoveN(a, i, 1)
|
||||||
|
|
||||||
|
#define A_InsertN(a, i, n) ( \
|
||||||
|
AUser_Assert((i) >= 0 && (i) < (a)->len), \
|
||||||
|
A__Grow((Array_Void *)a, A_ElementSize(a)), \
|
||||||
|
memcpy((a)->data + (i) + (n), (a)->data + i, (n)*A_ElementSize(a)), \
|
||||||
|
(a)->len += (n))
|
||||||
|
#define A_Insert(a, i, e) (A_InsertN(a, i, 1), (a)->data[i] = (e))
|
||||||
|
#define A_Free(a) (free((a)->data), memset(a, 0, sizeof(*a)))
|
||||||
|
|
||||||
|
static void A__Reserve(Array_Void *a, int element_size, int new_cap) {
|
||||||
|
if (new_cap > a->cap) {
|
||||||
|
a->cap = new_cap;
|
||||||
|
a->data = realloc(a->data, a->cap * element_size);
|
||||||
|
AUser_Assert(a->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void A__Grow(Array_Void *a, int element_size) {
|
||||||
|
if (a->cap == 0) {
|
||||||
|
a->cap = 16;
|
||||||
|
a->data = realloc(0, element_size * a->cap);
|
||||||
|
}
|
||||||
|
else if (a->len + 1 > a->cap) {
|
||||||
|
a->cap *= 2;
|
||||||
|
a->data = realloc(a->data, element_size * a->cap);
|
||||||
|
}
|
||||||
|
AUser_Assert(a->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ARRAY_HEADER
|
||||||
314
src/core/array.hpp
Executable file
314
src/core/array.hpp
Executable file
@@ -0,0 +1,314 @@
|
|||||||
|
// #define ARRAY_ALLOCATOR_TYPE Allocator
|
||||||
|
|
||||||
|
#ifndef ARRAY_PRIVATE_FUNCTION
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define ARRAY_PRIVATE_FUNCTION __attribute__((unused)) static
|
||||||
|
#else
|
||||||
|
#define ARRAY_PRIVATE_FUNCTION static
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_ALLOCATE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define ARRAY_ALLOCATE(allocator, size) malloc(size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_DEALLOCATE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define ARRAY_DEALLOCATE(allocator, p) free(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define ARRAY_ASSERT(x) assert(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_MemoryMove
|
||||||
|
#include <string.h>
|
||||||
|
#define ARRAY_MemoryMove(dst, src, size) memmove(dst, src, size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_SET_DEFAULT_ALLOCATOR
|
||||||
|
#define ARRAY_SET_DEFAULT_ALLOCATOR
|
||||||
|
// Example:
|
||||||
|
// #define ARRAY_SET_DEFAULT_ALLOCATOR if (!allocator) allocator = global_heap;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEFER_HEADER
|
||||||
|
#define ForArrayRemovable(a) for (int __i = 0; __i < (a).len; __i += 1)
|
||||||
|
#define ForArrayRemovablePrepare(a) \
|
||||||
|
auto &it = (a)[__i]; \
|
||||||
|
bool remove_it = false; \
|
||||||
|
defer { \
|
||||||
|
if (remove_it) { \
|
||||||
|
(a).ordered_remove(it); \
|
||||||
|
__i -= 1; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#define ForArrayRemovableDeclare() (remove_it = true)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(ARRAY_ALLOCATOR_CODE)
|
||||||
|
#if defined(ARRAY_ALLOCATOR_TYPE)
|
||||||
|
#define ARRAY_ALLOCATOR_CODE(x) x
|
||||||
|
#define ARRAY_ALLOCATOR_PARAM ARRAY_ALLOCATOR_TYPE *allocator,
|
||||||
|
#define ARRAY_ALLOCATOR_PASS allocator,
|
||||||
|
#define ARRAY__PASS(ctx, name) (ctx)->name,
|
||||||
|
#else
|
||||||
|
#define ARRAY_ALLOCATOR_CODE(x)
|
||||||
|
#define ARRAY_ALLOCATOR_PARAM
|
||||||
|
#define ARRAY_ALLOCATOR_PASS
|
||||||
|
#define ARRAY__PASS(ctx, name)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(For)
|
||||||
|
#define For2(a,it) for(auto &it : (a))
|
||||||
|
#define For(x) For2(x,it)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct Array {
|
||||||
|
ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator;)
|
||||||
|
T *data;
|
||||||
|
int cap, len;
|
||||||
|
|
||||||
|
T &operator[](int index) {
|
||||||
|
ARRAY_ASSERT(index >= 0 && index < len);
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
T &operator[](long long index) {
|
||||||
|
ARRAY_ASSERT(index >= 0 && index < len);
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_first(T &item) { return &item == first(); }
|
||||||
|
bool is_last(T &item) { return &item == last(); }
|
||||||
|
|
||||||
|
bool contains(T *item) {
|
||||||
|
bool result = item >= data && item < data + len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_index(T &item) {
|
||||||
|
ARRAY_ASSERT((data <= &item) && ((data + len) > &item));
|
||||||
|
size_t offset = &item - data;
|
||||||
|
return (int)offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(T item) {
|
||||||
|
try_growing();
|
||||||
|
data[len++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct needs to have 'value_to_sort_by' field
|
||||||
|
void sorted_insert_decreasing(T item) {
|
||||||
|
int insert_index = -1;
|
||||||
|
For(*this) {
|
||||||
|
if (it.value_to_sort_by <= item.value_to_sort_by) {
|
||||||
|
insert_index = get_index(it);
|
||||||
|
insert(item, insert_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insert_index == -1) {
|
||||||
|
add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bounded_add(T item) {
|
||||||
|
ARRAY_ASSERT(len + 1 <= cap);
|
||||||
|
try_growing(); // in case of error
|
||||||
|
data[len++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *alloc(const T &item) {
|
||||||
|
try_growing();
|
||||||
|
T *ref = data + len++;
|
||||||
|
*ref = item;
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *alloc() {
|
||||||
|
try_growing();
|
||||||
|
T *ref = data + len++;
|
||||||
|
*ref = {};
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *alloc_multiple(int size) {
|
||||||
|
try_growing_to_fit_item_count(size);
|
||||||
|
T *result = data + len;
|
||||||
|
len += size;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_array(T *items, int item_count) {
|
||||||
|
for (int i = 0; i < item_count; i += 1) {
|
||||||
|
add(items[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(int size) {
|
||||||
|
if (size > cap) {
|
||||||
|
ARRAY_SET_DEFAULT_ALLOCATOR;
|
||||||
|
|
||||||
|
void *p = ARRAY_ALLOCATE(allocator, size * sizeof(T));
|
||||||
|
ARRAY_ASSERT(p);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
ARRAY_MemoryMove(p, data, len * sizeof(T));
|
||||||
|
ARRAY_DEALLOCATE(allocator, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
data = (T *)p;
|
||||||
|
cap = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(ARRAY_ALLOCATOR_PARAM int size) {
|
||||||
|
len = 0;
|
||||||
|
cap = 0;
|
||||||
|
data = 0;
|
||||||
|
ARRAY_ALLOCATOR_CODE(this->allocator = allocator;)
|
||||||
|
reserve(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T pop() {
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
return data[--len];
|
||||||
|
}
|
||||||
|
|
||||||
|
void unordered_remove(T &item) { // DONT USE IN LOOPS !!!!
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
ARRAY_ASSERT(&item >= begin() && &item < end());
|
||||||
|
item = data[--len];
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_index(const T &item) {
|
||||||
|
ptrdiff_t index = (ptrdiff_t)(&item - data);
|
||||||
|
ARRAY_ASSERT(index >= 0 && index < len);
|
||||||
|
// ARRAY_ASSERT(index > INT_MIN && index < INT_MAX);
|
||||||
|
return (int)index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ordered_remove(T &item) { // DONT USE IN LOOPS !!!
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
ARRAY_ASSERT(&item >= begin() && &item < end());
|
||||||
|
int index = get_index(item);
|
||||||
|
ARRAY_ASSERT(index >= 0 && index < len);
|
||||||
|
|
||||||
|
int right_len = len - index - 1;
|
||||||
|
ARRAY_MemoryMove(data + index, data + index + 1, right_len * sizeof(T));
|
||||||
|
len -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(T item, int index) {
|
||||||
|
if (index == len) {
|
||||||
|
add(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARRAY_ASSERT(index < len);
|
||||||
|
ARRAY_ASSERT(index >= 0);
|
||||||
|
|
||||||
|
try_growing();
|
||||||
|
int right_len = len - index;
|
||||||
|
ARRAY_MemoryMove(data + index + 1, data + index, sizeof(T) * right_len);
|
||||||
|
data[index] = item;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dealloc() {
|
||||||
|
if (data) ARRAY_DEALLOCATE(allocator, data);
|
||||||
|
data = 0;
|
||||||
|
len = cap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<T> exact_copy(ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator)) {
|
||||||
|
Array result = {};
|
||||||
|
ARRAY_ALLOCATOR_CODE(result.allocator = allocator;)
|
||||||
|
result.reserve(cap);
|
||||||
|
|
||||||
|
ARRAY_MemoryMove(result.data, data, sizeof(T) * len);
|
||||||
|
result.len = len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<T> tight_copy(ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator)) {
|
||||||
|
Array result = {};
|
||||||
|
ARRAY_ALLOCATOR_CODE(result.allocator = allocator;)
|
||||||
|
result.reserve(len);
|
||||||
|
|
||||||
|
ARRAY_MemoryMove(result.data, data, sizeof(T) * len);
|
||||||
|
result.len = len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *first() {
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
T *last() {
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
return data + len - 1;
|
||||||
|
}
|
||||||
|
T *front() {
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
T *back() {
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
return data + len - 1;
|
||||||
|
}
|
||||||
|
T *begin() { return data; }
|
||||||
|
T *end() { return data + len; }
|
||||||
|
|
||||||
|
struct Reverse_Iter {
|
||||||
|
T *data;
|
||||||
|
Array<T> *arr;
|
||||||
|
|
||||||
|
Reverse_Iter operator++(int) {
|
||||||
|
Reverse_Iter ret = *this;
|
||||||
|
data -= 1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Reverse_Iter &operator++() {
|
||||||
|
data -= 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
T &operator*() { return data[0]; }
|
||||||
|
T *operator->() { return data; }
|
||||||
|
|
||||||
|
friend bool operator==(const Reverse_Iter &a, const Reverse_Iter &b) { return a.data == b.data; };
|
||||||
|
friend bool operator!=(const Reverse_Iter &a, const Reverse_Iter &b) { return a.data != b.data; };
|
||||||
|
|
||||||
|
Reverse_Iter begin() { return Reverse_Iter{arr->end() - 1, arr}; }
|
||||||
|
Reverse_Iter end() { return Reverse_Iter{arr->begin() - 1, arr}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
Reverse_Iter reverse() { return {end() - 1, this}; }
|
||||||
|
|
||||||
|
void try_growing() {
|
||||||
|
if (len + 1 > cap) {
|
||||||
|
int new_size = cap * 2;
|
||||||
|
if (new_size < 16) new_size = 16;
|
||||||
|
|
||||||
|
reserve(new_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void try_growing_to_fit_item_count(int item_count) {
|
||||||
|
if (len + item_count > cap) {
|
||||||
|
int new_size = (cap + item_count) * 2;
|
||||||
|
if (new_size < 16) new_size = 16;
|
||||||
|
reserve(new_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
438
src/core/bld.c
Executable file
438
src/core/bld.c
Executable file
@@ -0,0 +1,438 @@
|
|||||||
|
#include "core/core.c"
|
||||||
|
|
||||||
|
#define CL_Arena MA_Arena
|
||||||
|
#define CL_PushSize MA_PushSizeNonZeroed
|
||||||
|
#define CL_ASSERT IO_Assert
|
||||||
|
#define CL_VSNPRINTF stbsp_vsnprintf
|
||||||
|
#define CL_SNPRINTF stbsp_snprintf
|
||||||
|
#include "clexer.h"
|
||||||
|
|
||||||
|
MA_Arena G__Arena;
|
||||||
|
MA_Arena *G_Arena = &G__Arena;
|
||||||
|
|
||||||
|
S8_List BLD_DefaultMSVCCompileFlags;
|
||||||
|
S8_List BLD_DefaultMSVCDebugCompileFlags;
|
||||||
|
S8_List BLD_DefaultMSVCReleaseCompileFlags;
|
||||||
|
S8_List BLD_DefaultMSVCReleaseCompileFlagsWithoutCRT;
|
||||||
|
S8_List BLD_DefaultMSVCCompileFlagsWithSTD;
|
||||||
|
S8_List BLD_DefaultMSVCDebugCompileFlagsWithSTD;
|
||||||
|
S8_List BLD_DefaultMSVCReleaseCompileFlagsWithSTD;
|
||||||
|
S8_List BLD_BundledMSVCIncludeSearchPaths;
|
||||||
|
S8_List BLD_BundledMSVCLibrarySearchPaths;
|
||||||
|
S8_List BLD_DefaultMSVCLinkerFlags;
|
||||||
|
S8_List BLD_DefaultMSVCDebugLinkerFlags;
|
||||||
|
S8_List BLD_DefaultMSVCLinkerFlagsWithoutCRT;
|
||||||
|
S8_List BLD_CoreLibraryIncludeSearchPaths;
|
||||||
|
|
||||||
|
bool SRC_WasModified(S8_String file);
|
||||||
|
|
||||||
|
static char *BLD_CoreLibraryIncludes[] = {
|
||||||
|
BASE_PATH,
|
||||||
|
VENDOR_PATH "imgui",
|
||||||
|
};
|
||||||
|
|
||||||
|
CL_SearchPaths SRC_SearchPaths = {
|
||||||
|
BLD_CoreLibraryIncludes,
|
||||||
|
lengthof(BLD_CoreLibraryIncludes),
|
||||||
|
};
|
||||||
|
|
||||||
|
void BLD_Init() {
|
||||||
|
// COMPILER FLAGS
|
||||||
|
{
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-WX");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-W3");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-wd4200");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-diagnostics:column");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-nologo");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-FC");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-Z7");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-GF");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-Gm-");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-Oi");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-Zo");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-D_CRT_SECURE_NO_WARNINGS");
|
||||||
|
|
||||||
|
BLD_DefaultMSVCDebugCompileFlags = S8_CopyList(G_Arena, BLD_DefaultMSVCCompileFlags);
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCDebugCompileFlags, "-Od");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCDebugCompileFlags, "-D_DEBUG");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCDebugCompileFlags, "-MTd"); // Statically link the C library with debug symbols // required because std C++ calls some debug stuff
|
||||||
|
|
||||||
|
BLD_DefaultMSVCReleaseCompileFlags = S8_CopyList(G_Arena, BLD_DefaultMSVCCompileFlags);
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCReleaseCompileFlags, "-O2");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCReleaseCompileFlags, "-MT");
|
||||||
|
|
||||||
|
BLD_DefaultMSVCReleaseCompileFlagsWithoutCRT = S8_CopyList(G_Arena, BLD_DefaultMSVCCompileFlags);
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCReleaseCompileFlagsWithoutCRT, "-GS-");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCReleaseCompileFlagsWithoutCRT, "-Gs9999999");
|
||||||
|
|
||||||
|
BLD_DefaultMSVCCompileFlagsWithSTD = S8_CopyList(G_Arena, BLD_DefaultMSVCCompileFlags);
|
||||||
|
BLD_DefaultMSVCDebugCompileFlagsWithSTD = S8_CopyList(G_Arena, BLD_DefaultMSVCDebugCompileFlags);
|
||||||
|
BLD_DefaultMSVCReleaseCompileFlagsWithSTD = S8_CopyList(G_Arena, BLD_DefaultMSVCReleaseCompileFlags);
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlagsWithSTD, "-EHsc");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCDebugCompileFlagsWithSTD, "-EHsc");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCReleaseCompileFlagsWithSTD, "-EHsc");
|
||||||
|
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-EHa-");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCCompileFlags, "-GR-");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCDebugCompileFlags, "-EHa-");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCDebugCompileFlags, "-GR-");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCReleaseCompileFlags, "-EHa-");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCReleaseCompileFlags, "-GR-");
|
||||||
|
}
|
||||||
|
|
||||||
|
// SEARCH PATHS
|
||||||
|
{
|
||||||
|
S8_AddArrayWithPrefix(G_Arena, &BLD_CoreLibraryIncludeSearchPaths, "-I", BLD_CoreLibraryIncludes, lengthof(BLD_CoreLibraryIncludes));
|
||||||
|
|
||||||
|
S8_AddF(G_Arena, &BLD_BundledMSVCIncludeSearchPaths, "-I" MISC_PATH "MSVC/14.34.31933/include");
|
||||||
|
S8_AddF(G_Arena, &BLD_BundledMSVCIncludeSearchPaths, "-I" MISC_PATH "WindowsKits/Include/10.0.22000.0/ucrt");
|
||||||
|
S8_AddF(G_Arena, &BLD_BundledMSVCIncludeSearchPaths, "-I" MISC_PATH "WindowsKits/Include/10.0.22000.0/shared");
|
||||||
|
S8_AddF(G_Arena, &BLD_BundledMSVCIncludeSearchPaths, "-I" MISC_PATH "WindowsKits/Include/10.0.22000.0/um");
|
||||||
|
S8_AddArrayWithPrefix(G_Arena, &BLD_BundledMSVCIncludeSearchPaths, "-I", BLD_CoreLibraryIncludes, lengthof(BLD_CoreLibraryIncludes));
|
||||||
|
|
||||||
|
S8_AddF(G_Arena, &BLD_BundledMSVCLibrarySearchPaths, "-LIBPATH:" MISC_PATH "MSVC/14.34.31933/lib/x64");
|
||||||
|
S8_AddF(G_Arena, &BLD_BundledMSVCLibrarySearchPaths, "-LIBPATH:" MISC_PATH "WindowsKits/Lib/10.0.22000.0/um/x64");
|
||||||
|
S8_AddF(G_Arena, &BLD_BundledMSVCLibrarySearchPaths, "-LIBPATH:" MISC_PATH "WindowsKits/Lib/10.0.22000.0/ucrt/x64");
|
||||||
|
}
|
||||||
|
|
||||||
|
// LINKER
|
||||||
|
{
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCLinkerFlags, "-incremental:no");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCLinkerFlags, "-opt:ref");
|
||||||
|
|
||||||
|
BLD_DefaultMSVCDebugLinkerFlags = S8_CopyList(G_Arena, BLD_DefaultMSVCLinkerFlags);
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCDebugLinkerFlags, "-NODEFAULTLIB:LIBCMT");
|
||||||
|
|
||||||
|
BLD_DefaultMSVCLinkerFlagsWithoutCRT = S8_CopyList(G_Arena, BLD_DefaultMSVCLinkerFlags);
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCLinkerFlagsWithoutCRT, "-STACK:0x100000,0x100000");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCLinkerFlagsWithoutCRT, "-subsystem:windows");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCLinkerFlagsWithoutCRT, "gdi32.lib");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCLinkerFlagsWithoutCRT, "user32.lib");
|
||||||
|
S8_AddF(G_Arena, &BLD_DefaultMSVCLinkerFlagsWithoutCRT, "kernel32.lib");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct BLD_CompilerArgs BLD_CompilerArgs;
|
||||||
|
struct BLD_CompilerArgs {
|
||||||
|
bool compile_to_objects;
|
||||||
|
S8_String compiler_path;
|
||||||
|
S8_List include_paths;
|
||||||
|
S8_List library_paths;
|
||||||
|
|
||||||
|
S8_List files_to_compile_to_objects;
|
||||||
|
S8_List object_files_to_link;
|
||||||
|
S8_List libraries_to_link;
|
||||||
|
|
||||||
|
S8_List link_flags;
|
||||||
|
S8_List compile_flags;
|
||||||
|
|
||||||
|
S8_String output_filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
BLD_CompilerArgs BLD_DefaultCompilerArgs() {
|
||||||
|
BLD_CompilerArgs args = {0};
|
||||||
|
args.compiler_path = S8_Lit(CL_PATH);
|
||||||
|
args.include_paths = BLD_BundledMSVCIncludeSearchPaths;
|
||||||
|
args.library_paths = BLD_BundledMSVCLibrarySearchPaths;
|
||||||
|
args.compile_flags = BLD_DefaultMSVCDebugCompileFlags;
|
||||||
|
args.link_flags = BLD_DefaultMSVCDebugLinkerFlags;
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BLD_Compile(BLD_CompilerArgs a) {
|
||||||
|
S8_String include_search_paths = S8_MergeWithSeparator(G_Arena, a.include_paths, S8_Lit(" "));
|
||||||
|
S8_String library_paths = S8_MergeWithSeparator(G_Arena, a.library_paths, S8_Lit(" "));
|
||||||
|
S8_String files_to_compile_to_objects = S8_MergeWithSeparator(G_Arena, a.files_to_compile_to_objects, S8_Lit(" "));
|
||||||
|
S8_String object_files_to_link = S8_MergeWithSeparator(G_Arena, a.object_files_to_link, S8_Lit(" "));
|
||||||
|
S8_String libraries_to_link = S8_MergeWithSeparator(G_Arena, a.libraries_to_link, S8_Lit(" "));
|
||||||
|
|
||||||
|
S8_String link_flags = S8_MergeWithSeparator(G_Arena, a.link_flags, S8_Lit(" "));
|
||||||
|
S8_String compiler_flags = S8_MergeWithSeparator(G_Arena, a.compile_flags, S8_Lit(" "));
|
||||||
|
|
||||||
|
char *_c = "-c";
|
||||||
|
if (a.compile_to_objects == false) {
|
||||||
|
_c = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String output_filename = a.output_filename;
|
||||||
|
if (output_filename.len != 0) {
|
||||||
|
output_filename = S8_Format(G_Arena, "-Fe:%.*s", S8_Expand(output_filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = OS_SystemF("%.*s %s %.*s %.*s %.*s %.*s %.*s -link %.*s %.*s %.*s",
|
||||||
|
S8_Expand(a.compiler_path), _c, S8_Expand(output_filename), S8_Expand(files_to_compile_to_objects),
|
||||||
|
S8_Expand(object_files_to_link), S8_Expand(compiler_flags), S8_Expand(include_search_paths),
|
||||||
|
S8_Expand(library_paths), S8_Expand(link_flags), S8_Expand(libraries_to_link));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLD_AddDynamicSourceFile(S8_List *object_source_files, S8_List *object_files, S8_String source_filename, S8_String object_filename) {
|
||||||
|
if (SRC_WasModified(source_filename)) {
|
||||||
|
IO_Printf("RECOMPILING %.*s\n", S8_Expand(source_filename));
|
||||||
|
S8_AddNode(G_Arena, object_source_files, source_filename);
|
||||||
|
}
|
||||||
|
S8_AddNode(G_Arena, object_files, object_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLD_AddStaticSourceFile(S8_List *object_source_files, S8_List *object_files, S8_String source_filename, S8_String object_filename) {
|
||||||
|
if (OS_FileExists(object_filename) == false) {
|
||||||
|
S8_AddNode(G_Arena, object_source_files, source_filename);
|
||||||
|
IO_Printf("RECOMPILING %.*s\n", S8_Expand(source_filename));
|
||||||
|
}
|
||||||
|
S8_AddNode(G_Arena, object_files, object_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLD_AddRaylib(S8_List *libraries_to_link) {
|
||||||
|
S8_AddF(G_Arena, libraries_to_link, VENDOR_PATH "raylib/windows/raylibdll.lib");
|
||||||
|
OS_CopyFile(S8_Lit(VENDOR_PATH "raylib/windows/raylib.dll"), S8_Lit("raylib.dll"), false);
|
||||||
|
IO_Printf("cp %s %s\n", VENDOR_PATH "raylib/windows/raylib.dll", "raylib.dll");
|
||||||
|
}
|
||||||
|
|
||||||
|
const int BLD_Lib_Glad = 1;
|
||||||
|
const int BLD_Lib_StbImage = 2;
|
||||||
|
const int BLD_Lib_StbTruetype = 4;
|
||||||
|
const int BLD_Lib_ImGui = 8;
|
||||||
|
const int BLD_Lib_StbDs = 16;
|
||||||
|
const int BLD_Lib_RemoveCore = 32;
|
||||||
|
|
||||||
|
const int BLD_Lib_Raylib = 32; // Arg to build project
|
||||||
|
|
||||||
|
void BLD_AddLibsToBuild(S8_List *object_source_files, S8_List *object_files, int flags) {
|
||||||
|
if ((flags & BLD_Lib_RemoveCore) == 0) {
|
||||||
|
BLD_AddDynamicSourceFile(object_source_files, object_files, S8_Lit(BASE_PATH "core/core.c"), S8_Lit("encore.obj"));
|
||||||
|
}
|
||||||
|
if (flags & BLD_Lib_Glad) {
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "glad/glad.c"), S8_Lit("glad.obj"));
|
||||||
|
}
|
||||||
|
if (flags & BLD_Lib_StbImage) {
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "stb_image.c"), S8_Lit("stb_image.obj"));
|
||||||
|
}
|
||||||
|
if (flags & BLD_Lib_StbTruetype) {
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "stb_truetype.c"), S8_Lit("stb_truetype.obj"));
|
||||||
|
}
|
||||||
|
if (flags & BLD_Lib_ImGui) {
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "imgui/backends/imgui_impl_win32.cpp"), S8_Lit("imgui_impl_win32.obj"));
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "imgui/backends/imgui_impl_opengl3.cpp"), S8_Lit("imgui_impl_opengl3.obj"));
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "imgui/imgui.cpp"), S8_Lit("imgui.obj"));
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "imgui/imgui_widgets.cpp"), S8_Lit("imgui_widgets.obj"));
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "imgui/imgui_draw.cpp"), S8_Lit("imgui_draw.obj"));
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "imgui/imgui_tables.cpp"), S8_Lit("imgui_tables.obj"));
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "imgui/imgui_demo.cpp"), S8_Lit("imgui_demo.obj"));
|
||||||
|
}
|
||||||
|
if (flags & BLD_Lib_StbDs) {
|
||||||
|
BLD_AddStaticSourceFile(object_source_files, object_files, S8_Lit(VENDOR_PATH "stb_ds.c"), S8_Lit("stb_ds.obj"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BLD_PresetUnity(S8_String cfile, int libs_to_compile) {
|
||||||
|
S8_List object_files = {0};
|
||||||
|
|
||||||
|
// Incremental compile of libraries
|
||||||
|
{
|
||||||
|
S8_List object_source_files = {0};
|
||||||
|
BLD_AddLibsToBuild(&object_source_files, &object_files, libs_to_compile);
|
||||||
|
if (object_source_files.node_count) {
|
||||||
|
BLD_CompilerArgs args = BLD_DefaultCompilerArgs();
|
||||||
|
args.compile_to_objects = true;
|
||||||
|
args.files_to_compile_to_objects = object_source_files;
|
||||||
|
int result = BLD_Compile(args);
|
||||||
|
if (result != 0) {
|
||||||
|
IO_Printf("FAILED to compile libraries\n");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BLD_CompilerArgs args = BLD_DefaultCompilerArgs();
|
||||||
|
args.files_to_compile_to_objects = S8_MakeList(G_Arena, cfile);
|
||||||
|
|
||||||
|
S8_List libraries_to_link = {0};
|
||||||
|
if (libs_to_compile & BLD_Lib_Raylib) {
|
||||||
|
BLD_AddRaylib(&libraries_to_link);
|
||||||
|
}
|
||||||
|
args.libraries_to_link = libraries_to_link;
|
||||||
|
args.object_files_to_link = object_files;
|
||||||
|
int result = BLD_Compile(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct TokenReplacement TokenReplacement;
|
||||||
|
struct TokenReplacement {
|
||||||
|
S8_String replace;
|
||||||
|
S8_String with;
|
||||||
|
};
|
||||||
|
|
||||||
|
S8_String TokenReplace(MA_Arena *arena, CL_ArenaTuple *arena_tuple, S8_String content, TokenReplacement *replace, int replace_count) {
|
||||||
|
IO_Assert(content.str[content.len] == 0);
|
||||||
|
CL_LexResult *lex_result = CL_LexString(arena_tuple, "TokenReplace", content.str);
|
||||||
|
CL_Tokens tokens = lex_result->tokens;
|
||||||
|
|
||||||
|
S8_List out = {0};
|
||||||
|
S8_String c = content;
|
||||||
|
|
||||||
|
for (int i = 0; i < tokens.count; i += 1) {
|
||||||
|
CL_Token *it = tokens.data + i;
|
||||||
|
S8_String s = S8_Make(it->str, it->len);
|
||||||
|
S8_String replacement = {0};
|
||||||
|
|
||||||
|
bool match = false;
|
||||||
|
for (int ii = 0; ii < replace_count; ii += 1) {
|
||||||
|
if (S8_AreEqual(s, replace[ii].replace, 0)) {
|
||||||
|
match = true;
|
||||||
|
replacement = replace[ii].with;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
S8_String prefix = S8_GetPrefix(c, s.str - c.str);
|
||||||
|
S8_AddNode(arena_tuple->other, &out, prefix);
|
||||||
|
S8_AddNode(arena_tuple->other, &out, replacement);
|
||||||
|
c = S8_Skip(c, s.len + prefix.len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
S8_AddNode(arena_tuple->other, &out, c);
|
||||||
|
|
||||||
|
S8_String result = S8_Merge(arena, out);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Cache
|
||||||
|
//
|
||||||
|
|
||||||
|
double SRC_Time;
|
||||||
|
|
||||||
|
typedef struct SRC_CacheEntry SRC_CacheEntry;
|
||||||
|
struct SRC_CacheEntry {
|
||||||
|
uint64_t filepath_hash;
|
||||||
|
uint64_t file_hash;
|
||||||
|
uint64_t includes_hash;
|
||||||
|
uint64_t total_hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SRC_CACHE_ENTRY_COUNT 1024
|
||||||
|
typedef struct SRC_Cache SRC_Cache;
|
||||||
|
struct SRC_Cache {
|
||||||
|
int entry_cap;
|
||||||
|
int entry_len;
|
||||||
|
SRC_CacheEntry entries[SRC_CACHE_ENTRY_COUNT];
|
||||||
|
};
|
||||||
|
SRC_Cache *SRC_InMemoryCache;
|
||||||
|
SRC_Cache *SRC_FromFileCache;
|
||||||
|
CL_ArenaTuple SRC_ArenaTuple;
|
||||||
|
S8_String SRC_CacheFilename;
|
||||||
|
|
||||||
|
void SRC_InitCache(MA_Arena *arena, S8_String cachefilename) {
|
||||||
|
SRC_CacheFilename = cachefilename;
|
||||||
|
CL_InitDefaultTuple(&SRC_ArenaTuple);
|
||||||
|
|
||||||
|
SRC_InMemoryCache = MA_PushStruct(arena, SRC_Cache);
|
||||||
|
SRC_InMemoryCache->entry_cap = SRC_CACHE_ENTRY_COUNT;
|
||||||
|
|
||||||
|
SRC_FromFileCache = MA_PushStruct(arena, SRC_Cache);
|
||||||
|
SRC_FromFileCache->entry_cap = SRC_CACHE_ENTRY_COUNT;
|
||||||
|
|
||||||
|
S8_String cache = OS_ReadFile(arena, SRC_CacheFilename);
|
||||||
|
if (cache.len) MA_MemoryCopy(SRC_FromFileCache, cache.str, cache.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SRC_SaveCache() {
|
||||||
|
S8_String dump = S8_Make((char *)SRC_InMemoryCache, sizeof(SRC_Cache));
|
||||||
|
OS_WriteFile(SRC_CacheFilename, dump);
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_CacheEntry *SRC_AddHash(uint64_t filepath, uint64_t file, uint64_t includes) {
|
||||||
|
IO_Assert(SRC_InMemoryCache->entry_len + 1 < SRC_InMemoryCache->entry_cap);
|
||||||
|
SRC_CacheEntry *result = SRC_InMemoryCache->entries + SRC_InMemoryCache->entry_len++;
|
||||||
|
result->filepath_hash = filepath;
|
||||||
|
result->file_hash = file;
|
||||||
|
result->includes_hash = includes;
|
||||||
|
result->total_hash = HashBytes(result, sizeof(uint64_t) * 3);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_CacheEntry *SRC_FindCache(SRC_Cache *cache, uint64_t filepath_hash) {
|
||||||
|
for (int cache_i = 0; cache_i < cache->entry_len; cache_i += 1) {
|
||||||
|
SRC_CacheEntry *it = cache->entries + cache_i;
|
||||||
|
if (it->filepath_hash == filepath_hash) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_CacheEntry *SRC_HashFile(S8_String file, char *parent_file) {
|
||||||
|
char *resolved_file = CL_ResolveFilepath(G_Arena, &SRC_SearchPaths, file.str, parent_file, false);
|
||||||
|
if (!resolved_file) {
|
||||||
|
IO_Printf("Failed to resolve file: %s\n", file.str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t filepath_hash = HashBytes(resolved_file, S8_Length(resolved_file));
|
||||||
|
SRC_CacheEntry *entry = SRC_FindCache(SRC_InMemoryCache, filepath_hash);
|
||||||
|
if (entry) return entry;
|
||||||
|
|
||||||
|
CL_LexResult *first_lex = CL_LexFile(&SRC_ArenaTuple, resolved_file);
|
||||||
|
IO_Assert(first_lex);
|
||||||
|
uint64_t file_hash = HashBytes(first_lex->stream_begin, first_lex->stream - first_lex->stream_begin);
|
||||||
|
uint64_t includes_hash = 13;
|
||||||
|
|
||||||
|
CL_LexList list = CL_MakeLexList(first_lex);
|
||||||
|
for (CL_IncludeIter iter = CL_IterateIncludes(&list); iter.filename; CL_GetNextInclude(&iter)) {
|
||||||
|
if (iter.is_system_include) continue;
|
||||||
|
|
||||||
|
S8_String file_it = S8_MakeFromChar(iter.filename);
|
||||||
|
SRC_CacheEntry *cache = SRC_HashFile(file_it, resolved_file);
|
||||||
|
if (!cache) {
|
||||||
|
IO_Printf("Missing cache for: %s\n", file_it.str);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
includes_hash = HashMix(includes_hash, cache->total_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_CacheEntry *result = SRC_AddHash(filepath_hash, file_hash, includes_hash);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SRC_WasModified(S8_String file) {
|
||||||
|
double time_start = OS_GetTime();
|
||||||
|
|
||||||
|
if (OS_FileExists(file) == false) {
|
||||||
|
IO_Printf("FAILED File doesnt exist: %.*s\n", S8_Expand(file));
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if (OS_IsAbsolute(file) == false) {
|
||||||
|
file = OS_GetAbsolutePath(G_Arena, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String without_ext = S8_ChopLastPeriod(file);
|
||||||
|
S8_String name_only = S8_SkipToLastSlash(without_ext);
|
||||||
|
S8_String obj = S8_Format(G_Arena, "%.*s.obj", S8_Expand(name_only));
|
||||||
|
bool modified = OS_FileExists(obj) == false;
|
||||||
|
|
||||||
|
SRC_CacheEntry *in_memory = SRC_HashFile(file, 0);
|
||||||
|
IO_Assert(in_memory);
|
||||||
|
|
||||||
|
if (modified == false) {
|
||||||
|
SRC_CacheEntry *from_file = SRC_FindCache(SRC_FromFileCache, in_memory->filepath_hash);
|
||||||
|
if (from_file == 0 || (in_memory->total_hash != from_file->total_hash)) {
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_Time = SRC_Time + (OS_GetTime() - time_start);
|
||||||
|
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BLD_METAPROGRAM
|
||||||
|
int BLD_Main();
|
||||||
|
int main() {
|
||||||
|
BLD_Init();
|
||||||
|
SRC_InitCache(G_Arena, S8_Lit("main.cache"));
|
||||||
|
int result = BLD_Main();
|
||||||
|
if (result == 0) SRC_SaveCache();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
5
src/core/bld_paths.c
Executable file
5
src/core/bld_paths.c
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#define BASE_PATH "C:/Work/"
|
||||||
|
#define VENDOR_PATH BASE_PATH "vendor/"
|
||||||
|
#define MISC_PATH BASE_PATH "misc/"
|
||||||
|
#define BIN_PATH MISC_PATH "bin/"
|
||||||
|
#define CL_PATH MISC_PATH "MSVC/14.34.31933/bin/Hostx64/x64/cl.exe"
|
||||||
2411
src/core/clexer.h
Executable file
2411
src/core/clexer.h
Executable file
File diff suppressed because it is too large
Load Diff
21
src/core/core.c
Executable file
21
src/core/core.c
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#include "core.h"
|
||||||
|
#define UTF_IMPLEMENTATION
|
||||||
|
#define MA_IMPLEMENTATION
|
||||||
|
#define STB_SPRINTF_IMPLEMENTATION
|
||||||
|
#define S8_IMPLEMENTATION
|
||||||
|
#define RE_IMPLEMENTATION
|
||||||
|
#define CL_IMPLEMENTATION
|
||||||
|
#define CPP_IMPLEMENTATION
|
||||||
|
#define MU_IMPLEMENTATION
|
||||||
|
#define VIS_IMPLEMENTATION
|
||||||
|
#define IO_IMPLEMENTATION
|
||||||
|
#include "stb_sprintf.h"
|
||||||
|
#include "regex.h"
|
||||||
|
#include "unicode.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "arena.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "multimedia.h"
|
||||||
|
#include "hash.c"
|
||||||
|
#include "load_library.c"
|
||||||
|
#include "filesystem.c"
|
||||||
61
src/core/core.h
Executable file
61
src/core/core.h
Executable file
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "preproc_env.h"
|
||||||
|
#include "bld_paths.c"
|
||||||
|
#include "stb_sprintf.h"
|
||||||
|
|
||||||
|
#define IO_VSNPRINTF stbsp_vsnprintf
|
||||||
|
#define IO_SNPRINTF stbsp_snprintf
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
#define MA_ENABLE_SCRATCH
|
||||||
|
#define MA_ASSERT(x) IO_Assert(x)
|
||||||
|
#include "arena.h"
|
||||||
|
|
||||||
|
#define S8_VSNPRINTF stbsp_vsnprintf
|
||||||
|
#define S8_Allocator MA_Arena *
|
||||||
|
#define S8_ALLOCATE(allocator, size) MA_PushSize(allocator, size)
|
||||||
|
#define S8_ASSERT(x) IO_Assert(x)
|
||||||
|
#define S8_MemoryCopy MA_MemoryCopy
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#include "unicode.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "linked_list.h"
|
||||||
|
|
||||||
|
#define RE_PushSize MA_PushSize
|
||||||
|
#define RE_Arena MA_Arena
|
||||||
|
#define RE_ArenaFromBuffer MA_MakeFromBuffer
|
||||||
|
#define RE_ASSERT(x) IO_Assert(x)
|
||||||
|
#define RE__MemoryZero MA_MemoryZero
|
||||||
|
#include "regex.h"
|
||||||
|
|
||||||
|
#define MU_ASSERT IO_Assert
|
||||||
|
#include "multimedia.h"
|
||||||
|
|
||||||
|
#include "load_library.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include "defer.hpp"
|
||||||
|
#define TABLE_ASSERT IO_Assert
|
||||||
|
#include "table.hpp"
|
||||||
|
#define ARRAY_ASSERT IO_Assert
|
||||||
|
#include "array.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define lengthof(x) ((int64_t)((sizeof(x) / sizeof((x)[0]))))
|
||||||
|
#define Unused(x) (void)(x)
|
||||||
|
#define Transmute(T, x) (*(T *)&(x))
|
||||||
|
|
||||||
|
// #define CL_Arena MA_Arena
|
||||||
|
// #define CL_PushSize MA_PushSizeNonZeroed
|
||||||
|
// #define CL_ASSERT IO_Assert
|
||||||
|
// #define CL_VSNPRINTF stbsp_vsnprintf
|
||||||
|
// #define CL_SNPRINTF stbsp_snprintf
|
||||||
|
// #include "clexer.h"
|
||||||
|
// #include "cpreproc.h"
|
||||||
|
|
||||||
|
// #define VIS_MemoryCopy MA_MemoryCopy
|
||||||
|
// #define VIS_MemoryZero MA_MemoryZero
|
||||||
|
// #define VIS_ASSERT IO_Assert
|
||||||
|
// #include "visualize.h"
|
||||||
1056
src/core/cpreproc.h
Executable file
1056
src/core/cpreproc.h
Executable file
File diff suppressed because it is too large
Load Diff
21
src/core/defer.hpp
Executable file
21
src/core/defer.hpp
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#define DEFER_HEADER
|
||||||
|
template <typename T>
|
||||||
|
struct DEFER_ExitScope {
|
||||||
|
T lambda;
|
||||||
|
DEFER_ExitScope(T lambda) : lambda(lambda) {}
|
||||||
|
~DEFER_ExitScope() { lambda(); }
|
||||||
|
DEFER_ExitScope(const DEFER_ExitScope &i) : lambda(i.lambda){};
|
||||||
|
|
||||||
|
private:
|
||||||
|
DEFER_ExitScope &operator=(const DEFER_ExitScope &);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DEFER_ExitScopeHelp {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
DEFER_ExitScope<T> operator+(T t) { return t; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DEFER_CONCAT_INTERNAL(x, y) x##y
|
||||||
|
#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y)
|
||||||
|
#define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]()
|
||||||
508
src/core/filesystem.c
Executable file
508
src/core/filesystem.c
Executable file
@@ -0,0 +1,508 @@
|
|||||||
|
#include "filesystem.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
OS_API bool WIN_EnableTerminalColors(void) {
|
||||||
|
// Enable color terminal output
|
||||||
|
{
|
||||||
|
// Set output mode to handle virtual terminal sequences
|
||||||
|
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
if (hOut != INVALID_HANDLE_VALUE) {
|
||||||
|
DWORD dwMode = 0;
|
||||||
|
if (GetConsoleMode(hOut, &dwMode)) {
|
||||||
|
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||||
|
if (SetConsoleMode(hOut, dwMode)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_Printf("Failed to enable colored terminal output C\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_Printf("Failed to enable colored terminal output B\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_Printf("Failed to enable colored terminal output A\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API void DEV_SetWorkingDir(void) {
|
||||||
|
// Enable color terminal output
|
||||||
|
{
|
||||||
|
// Set output mode to handle virtual terminal sequences
|
||||||
|
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
if (hOut != INVALID_HANDLE_VALUE) {
|
||||||
|
DWORD dwMode = 0;
|
||||||
|
if (GetConsoleMode(hOut, &dwMode)) {
|
||||||
|
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||||
|
if (SetConsoleMode(hOut, dwMode)) {
|
||||||
|
// COLOR CODES ENABLED
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_Printf("Failed to enable colored terminal output C\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_Printf("Failed to enable colored terminal output B\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_Printf("Failed to enable colored terminal output A\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_Checkpoint scratch = MA_GetScratch();
|
||||||
|
S8_String working_dir = OS_GetWorkingDir(scratch.arena);
|
||||||
|
if (S8_EndsWith(working_dir, S8_Lit("build/"), S8_IGNORE_CASE)) {
|
||||||
|
OS_SetWorkingDir(S8_Lit(".."));
|
||||||
|
}
|
||||||
|
MA_ReleaseScratch(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API bool OS_IsAbsolute(S8_String path) {
|
||||||
|
bool result = path.len > 3 && CHAR_IsAlphabetic(path.str[0]) && path.str[1] == ':' && path.str[2] == '/';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API S8_String OS_GetExePath(MA_Arena *arena) {
|
||||||
|
wchar_t wbuffer[1024];
|
||||||
|
DWORD wsize = GetModuleFileNameW(0, wbuffer, lengthof(wbuffer));
|
||||||
|
IO_Assert(wsize != 0);
|
||||||
|
|
||||||
|
S8_String path = UTF_CreateStringFromWidechar(arena, wbuffer, wsize);
|
||||||
|
S8_NormalizePath(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API S8_String OS_GetExeDir(MA_Arena *arena) {
|
||||||
|
S8_String path = OS_GetExePath(arena);
|
||||||
|
path = S8_ChopLastSlash(path);
|
||||||
|
path.str[path.len] = 0;
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) {
|
||||||
|
wchar_t wbuffer[1024];
|
||||||
|
DWORD wsize = GetCurrentDirectoryW(lengthof(wbuffer), wbuffer);
|
||||||
|
IO_Assert(wsize != 0);
|
||||||
|
IO_Assert(wsize < 1022);
|
||||||
|
wbuffer[wsize++] = '/';
|
||||||
|
wbuffer[wsize] = 0;
|
||||||
|
|
||||||
|
S8_String path = UTF_CreateStringFromWidechar(arena, wbuffer, wsize);
|
||||||
|
S8_NormalizePath(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API void OS_SetWorkingDir(S8_String path) {
|
||||||
|
wchar_t wpath[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wpath, lengthof(wpath), path.str, path.len);
|
||||||
|
SetCurrentDirectoryW(wpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) {
|
||||||
|
wchar_t wpath[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wpath, lengthof(wpath), relative.str, relative.len);
|
||||||
|
wchar_t wpath_abs[1024];
|
||||||
|
DWORD written = GetFullPathNameW((wchar_t *)wpath, lengthof(wpath_abs), wpath_abs, 0);
|
||||||
|
if (written == 0)
|
||||||
|
return S8_MakeEmpty();
|
||||||
|
S8_String path = UTF_CreateStringFromWidechar(arena, wpath_abs, written);
|
||||||
|
S8_NormalizePath(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API bool OS_FileExists(S8_String path) {
|
||||||
|
wchar_t wbuff[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wbuff, lengthof(wbuff), path.str, path.len);
|
||||||
|
DWORD attribs = GetFileAttributesW(wbuff);
|
||||||
|
bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API bool OS_IsDir(S8_String path) {
|
||||||
|
wchar_t wbuff[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wbuff, lengthof(wbuff), path.str, path.len);
|
||||||
|
DWORD dwAttrib = GetFileAttributesW(wbuff);
|
||||||
|
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API bool OS_IsFile(S8_String path) {
|
||||||
|
wchar_t wbuff[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wbuff, lengthof(wbuff), path.str, path.len);
|
||||||
|
DWORD dwAttrib = GetFileAttributesW(wbuff);
|
||||||
|
bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0;
|
||||||
|
return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API double OS_GetTime(void) {
|
||||||
|
static int64_t counts_per_second;
|
||||||
|
if (counts_per_second == 0) {
|
||||||
|
LARGE_INTEGER freq;
|
||||||
|
QueryPerformanceFrequency(&freq);
|
||||||
|
counts_per_second = freq.QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
LARGE_INTEGER time;
|
||||||
|
QueryPerformanceCounter(&time);
|
||||||
|
double result = (double)time.QuadPart / (double)counts_per_second;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo: I think we want a list with both relative + absolute + other things
|
||||||
|
//
|
||||||
|
// Returns directories with slash at the end '/'
|
||||||
|
// By default returns absolute paths
|
||||||
|
OS_API S8_List OS_ListDir(MA_Arena *arena, S8_String path, unsigned flags) {
|
||||||
|
MA_Checkpoint scratch = MA_GetScratch1(arena);
|
||||||
|
S8_List dirs_to_read = S8_MakeEmptyList();
|
||||||
|
S8_List result = S8_MakeEmptyList();
|
||||||
|
S8_AddNode(scratch.arena, &dirs_to_read, path);
|
||||||
|
|
||||||
|
for (S8_Node *it = dirs_to_read.first; it; it = it->next) {
|
||||||
|
wchar_t wbuff[1024];
|
||||||
|
S8_String modified_path = S8_Format(scratch.arena, "%.*s\\*", (int)it->string.len, it->string.str);
|
||||||
|
IO_Assert(modified_path.len < lengthof(wbuff));
|
||||||
|
int64_t wsize = UTF_CreateWidecharFromChar(wbuff, lengthof(wbuff), modified_path.str, modified_path.len);
|
||||||
|
IO_Assert(wsize);
|
||||||
|
|
||||||
|
WIN32_FIND_DATAW ffd;
|
||||||
|
HANDLE handle = FindFirstFileW(wbuff, &ffd);
|
||||||
|
if (handle == INVALID_HANDLE_VALUE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Skip '.' and '..'
|
||||||
|
//
|
||||||
|
if (ffd.cFileName[0] == '.') {
|
||||||
|
if (ffd.cFileName[1] == '.') {
|
||||||
|
if (ffd.cFileName[2] == 0)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ffd.cFileName[1] == 0)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dir = ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
||||||
|
S8_String filename = UTF_CreateStringFromWidechar(scratch.arena, ffd.cFileName, S8_WideLength(ffd.cFileName));
|
||||||
|
S8_String rel_abs_path = S8_Format(scratch.arena, "%.*s/%.*s%s", it->string.len, it->string.str, filename.len, filename.str, dir ? "/" : "");
|
||||||
|
if (flags & OS_RELATIVE_PATHS) {
|
||||||
|
S8_AddNode(arena, &result, rel_abs_path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
S8_String abs_path = OS_GetAbsolutePath(arena, rel_abs_path);
|
||||||
|
S8_AddNode(arena, &result, abs_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir && flags & OS_RECURSIVE) {
|
||||||
|
S8_AddNode(scratch.arena, &dirs_to_read, rel_abs_path);
|
||||||
|
}
|
||||||
|
} while (FindNextFileW(handle, &ffd) != 0);
|
||||||
|
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
if (error != ERROR_NO_MORE_FILES) {
|
||||||
|
// Not sure what to do here hmmm
|
||||||
|
}
|
||||||
|
FindClose(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_ReleaseScratch(scratch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API OS_Result OS_MakeDir(S8_String path) {
|
||||||
|
wchar_t wpath[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wpath, lengthof(wpath), path.str, path.len);
|
||||||
|
BOOL success = CreateDirectoryW(wpath, NULL);
|
||||||
|
OS_Result result = OS_SUCCESS;
|
||||||
|
if (success == 0) {
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
if (error == ERROR_ALREADY_EXISTS) {
|
||||||
|
result = OS_ALREADY_EXISTS;
|
||||||
|
}
|
||||||
|
else if (error == ERROR_PATH_NOT_FOUND) {
|
||||||
|
result = OS_PATH_NOT_FOUND;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_Assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) {
|
||||||
|
wchar_t wfrom[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wfrom, lengthof(wfrom), from.str, from.len);
|
||||||
|
|
||||||
|
wchar_t wto[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wto, lengthof(wto), to.str, to.len);
|
||||||
|
|
||||||
|
BOOL fail_if_exists = !overwrite;
|
||||||
|
BOOL success = CopyFileW(wfrom, wto, fail_if_exists);
|
||||||
|
|
||||||
|
OS_Result result = OS_SUCCESS;
|
||||||
|
if (success == FALSE)
|
||||||
|
result = OS_FAILURE;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API OS_Result OS_DeleteFile(S8_String path) {
|
||||||
|
wchar_t wpath[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wpath, lengthof(wpath), path.str, path.len);
|
||||||
|
BOOL success = DeleteFileW(wpath);
|
||||||
|
OS_Result result = OS_SUCCESS;
|
||||||
|
if (success == 0)
|
||||||
|
result = OS_PATH_NOT_FOUND;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) {
|
||||||
|
if (flags & OS_RECURSIVE) {
|
||||||
|
MA_Checkpoint scratch = MA_GetScratch();
|
||||||
|
S8_List list = OS_ListDir(scratch.arena, path, OS_RECURSIVE);
|
||||||
|
S8_Node *dirs_to_remove = 0;
|
||||||
|
for (S8_Node *it = list.first; it; it = it->next) {
|
||||||
|
if (!S8_EndsWith(it->string, S8_Lit("/"), S8_IGNORE_CASE)) {
|
||||||
|
OS_DeleteFile(it->string);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
S8_Node *node = S8_CreateNode(scratch.arena, it->string);
|
||||||
|
SLL_STACK_ADD(dirs_to_remove, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (S8_Node *it = dirs_to_remove; it; it = it->next) {
|
||||||
|
OS_DeleteDir(it->string, OS_NO_FLAGS);
|
||||||
|
}
|
||||||
|
OS_Result result = OS_DeleteDir(path, OS_NO_FLAGS);
|
||||||
|
MA_ReleaseScratch(scratch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wchar_t wpath[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wpath, lengthof(wpath), path.str, path.len);
|
||||||
|
BOOL success = RemoveDirectoryW(wpath);
|
||||||
|
OS_Result result = OS_SUCCESS;
|
||||||
|
if (success == 0)
|
||||||
|
result = OS_PATH_NOT_FOUND;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static OS_Result OS__WriteFile(S8_String path, S8_String data, bool append) {
|
||||||
|
wchar_t wpath[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wpath, lengthof(wpath), path.str, path.len);
|
||||||
|
OS_Result result = OS_FAILURE;
|
||||||
|
|
||||||
|
DWORD access = GENERIC_WRITE;
|
||||||
|
DWORD creation_disposition = CREATE_ALWAYS;
|
||||||
|
if (append) {
|
||||||
|
access = FILE_APPEND_DATA;
|
||||||
|
creation_disposition = OPEN_ALWAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE handle = CreateFileW(wpath, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (handle != INVALID_HANDLE_VALUE) {
|
||||||
|
DWORD bytes_written = 0;
|
||||||
|
IO_Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files?
|
||||||
|
BOOL error = WriteFile(handle, data.str, (DWORD)data.len, &bytes_written, NULL);
|
||||||
|
if (error == TRUE) {
|
||||||
|
if (bytes_written != data.len) {
|
||||||
|
result = OS_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseHandle(handle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result = OS_PATH_NOT_FOUND;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) {
|
||||||
|
return OS__WriteFile(path, string, true);
|
||||||
|
} // @untested
|
||||||
|
|
||||||
|
OS_API OS_Result OS_WriteFile(S8_String path, S8_String string) {
|
||||||
|
return OS__WriteFile(path, string, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) {
|
||||||
|
bool success = false;
|
||||||
|
S8_String result = S8_MakeEmpty();
|
||||||
|
MA_Checkpoint checkpoint = MA_Save(arena);
|
||||||
|
|
||||||
|
wchar_t wpath[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wpath, lengthof(wpath), path.str, path.len);
|
||||||
|
HANDLE handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (handle != INVALID_HANDLE_VALUE) {
|
||||||
|
LARGE_INTEGER file_size;
|
||||||
|
if (GetFileSizeEx(handle, &file_size)) {
|
||||||
|
if (file_size.QuadPart != 0) {
|
||||||
|
result.len = (int64_t)file_size.QuadPart;
|
||||||
|
result.str = (char *)MA_PushSizeNonZeroed(arena, result.len + 1);
|
||||||
|
DWORD read;
|
||||||
|
if (ReadFile(handle, result.str, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files?
|
||||||
|
if (read == result.len) {
|
||||||
|
success = true;
|
||||||
|
result.str[result.len] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseHandle(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
result = S8_MakeEmpty();
|
||||||
|
MA_Load(checkpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API int OS_SystemF(const char *string, ...) {
|
||||||
|
MA_Checkpoint scratch = MA_GetScratch();
|
||||||
|
S8_FORMAT(scratch.arena, string, result);
|
||||||
|
IO_Printf("Executing: %s\n", result.str);
|
||||||
|
fflush(stdout);
|
||||||
|
int error_code = system(result.str);
|
||||||
|
MA_ReleaseScratch(scratch);
|
||||||
|
return error_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API int64_t OS_GetFileModTime(S8_String file) {
|
||||||
|
FILETIME time = {0};
|
||||||
|
WIN32_FIND_DATAW data;
|
||||||
|
|
||||||
|
wchar_t wpath[1024];
|
||||||
|
UTF_CreateWidecharFromChar(wpath, 1024, file.str, file.len);
|
||||||
|
HANDLE handle = FindFirstFileW(wpath, &data);
|
||||||
|
if (handle != INVALID_HANDLE_VALUE) {
|
||||||
|
FindClose(handle);
|
||||||
|
time = data.ftLastWriteTime;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API OS_Date OS_GetDate(void) {
|
||||||
|
SYSTEMTIME local;
|
||||||
|
GetLocalTime(&local);
|
||||||
|
|
||||||
|
OS_Date result = {0};
|
||||||
|
result.year = local.wYear;
|
||||||
|
result.month = local.wMonth;
|
||||||
|
result.day = local.wDay;
|
||||||
|
result.hour = local.wHour;
|
||||||
|
result.second = local.wSecond;
|
||||||
|
result.milliseconds = local.wMilliseconds;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
OS_API S8_String UTF_CreateStringFromWidechar(MA_Arena *arena, wchar_t *wstr, int64_t wsize) {
|
||||||
|
int64_t buffer_size = (wsize + 1) * 2;
|
||||||
|
char *buffer = (char *)MA_PushSizeNonZeroed(arena, buffer_size);
|
||||||
|
int64_t size = UTF_CreateCharFromWidechar(buffer, buffer_size, wstr, wsize);
|
||||||
|
IO_Assert(size < buffer_size);
|
||||||
|
return S8_Make(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API S8_List S8_SplitOnRegex(MA_Arena *arena, S8_String string, S8_String regex, unsigned flags) {
|
||||||
|
S8_List result = S8_MakeEmptyList();
|
||||||
|
int64_t index = 0;
|
||||||
|
|
||||||
|
char buff[4096];
|
||||||
|
RE_Regex *re = RE2_ParseBuff(buff, sizeof(buff), regex.str, regex.len);
|
||||||
|
for (RE_Match match = RE3_Find(re, string.str, string.len); match.pos != -1; match = RE3_Find(re, string.str, string.len)) {
|
||||||
|
S8_String before_match = S8_Make(string.str, match.pos);
|
||||||
|
S8_String the_match = S8_Make(string.str + match.pos, match.size);
|
||||||
|
if (before_match.len) S8_AddNode(arena, &result, before_match);
|
||||||
|
if (flags & S8_SPLIT_INCLUSIVE) {
|
||||||
|
if (the_match.len) S8_AddNode(arena, &result, the_match);
|
||||||
|
}
|
||||||
|
string = S8_Skip(string, match.pos + match.size);
|
||||||
|
}
|
||||||
|
S8_AddNode(arena, &result, string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API S8_List OS_ListDirRegex(MA_Arena *arena, S8_String path, unsigned flags, char *regex) {
|
||||||
|
S8_List result = S8_MakeEmptyList();
|
||||||
|
RE_Regex *re = RE1_Parse(arena, regex);
|
||||||
|
S8_List files = OS_ListDir(arena, path, flags);
|
||||||
|
for (S8_Node *it = files.first; it; it = it->next) {
|
||||||
|
if (RE3_AreEqual(re, it->string.str, it->string.len)) {
|
||||||
|
S8_AddNode(arena, &result, it->string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API S8_String OS_ListDirRegexAsString(MA_Arena *arena, S8_String path, unsigned flags, char *regex) {
|
||||||
|
S8_List files = OS_ListDirRegex(arena, path, flags, regex);
|
||||||
|
S8_String files_str = S8_MergeWithSeparator(arena, files, S8_Lit(" "));
|
||||||
|
return files_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath) {
|
||||||
|
S8_String c = OS_ReadFile(arena, filepath);
|
||||||
|
if (c.str == 0) return false;
|
||||||
|
S8_String path = S8_ChopLastSlash(filepath);
|
||||||
|
S8_String include = S8_Lit("#include \"");
|
||||||
|
for (;;) {
|
||||||
|
int64_t idx = -1;
|
||||||
|
if (S8_Find(c, include, 0, &idx)) {
|
||||||
|
S8_String str_to_add = S8_GetPrefix(c, idx);
|
||||||
|
S8_AddNode(arena, out, str_to_add);
|
||||||
|
S8_String save = c;
|
||||||
|
c = S8_Skip(c, idx + include.len);
|
||||||
|
|
||||||
|
S8_String filename = c;
|
||||||
|
filename.len = 0;
|
||||||
|
while (filename.str[filename.len] != '"' && filename.len < c.len) {
|
||||||
|
filename.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = S8_Skip(c, filename.len + 1);
|
||||||
|
S8_String inc_path = S8_Format(arena, "%.*s/%.*s", S8_Expand(path), S8_Expand(filename));
|
||||||
|
if (!OS_ExpandIncludesList(arena, out, inc_path)) {
|
||||||
|
S8_String s = S8_GetPrefix(save, save.len - c.len);
|
||||||
|
S8_AddNode(arena, out, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
S8_AddNode(arena, out, c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_API S8_String OS_ExpandIncludes(MA_Arena *arena, S8_String filepath) {
|
||||||
|
S8_List out = {0};
|
||||||
|
S8_String result = {0};
|
||||||
|
MA_ScratchScope(s) {
|
||||||
|
OS_ExpandIncludesList(s.arena, &out, filepath);
|
||||||
|
result = S8_Merge(arena, out);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
67
src/core/filesystem.h
Executable file
67
src/core/filesystem.h
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
#ifndef OS_FS_HEADER
|
||||||
|
#define OS_FS_HEADER
|
||||||
|
|
||||||
|
#ifndef OS_API
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define OS_API extern "C"
|
||||||
|
#else
|
||||||
|
#define OS_API
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum OS_Result OS_Result;
|
||||||
|
typedef struct OS_Date OS_Date;
|
||||||
|
|
||||||
|
enum OS_Result {
|
||||||
|
OS_SUCCESS,
|
||||||
|
OS_ALREADY_EXISTS,
|
||||||
|
OS_PATH_NOT_FOUND,
|
||||||
|
OS_FAILURE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
OS_NO_FLAGS = 0,
|
||||||
|
OS_RECURSIVE = 1,
|
||||||
|
OS_RELATIVE_PATHS = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OS_Date {
|
||||||
|
uint32_t year;
|
||||||
|
uint32_t month;
|
||||||
|
uint32_t day;
|
||||||
|
uint32_t hour;
|
||||||
|
uint32_t minute;
|
||||||
|
uint32_t second;
|
||||||
|
uint32_t milliseconds;
|
||||||
|
};
|
||||||
|
|
||||||
|
OS_API void DEV_SetWorkingDir(void);
|
||||||
|
OS_API bool OS_IsAbsolute(S8_String path);
|
||||||
|
OS_API S8_String OS_GetExePath(MA_Arena *arena);
|
||||||
|
OS_API S8_String OS_GetExeDir(MA_Arena *arena);
|
||||||
|
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena);
|
||||||
|
OS_API void OS_SetWorkingDir(S8_String path);
|
||||||
|
OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative);
|
||||||
|
OS_API bool OS_FileExists(S8_String path);
|
||||||
|
OS_API bool OS_IsDir(S8_String path);
|
||||||
|
OS_API bool OS_IsFile(S8_String path);
|
||||||
|
OS_API double OS_GetTime(void);
|
||||||
|
OS_API S8_List OS_ListDir(MA_Arena *arena, S8_String path, unsigned flags);
|
||||||
|
OS_API OS_Result OS_MakeDir(S8_String path);
|
||||||
|
OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite);
|
||||||
|
OS_API OS_Result OS_DeleteFile(S8_String path);
|
||||||
|
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags);
|
||||||
|
OS_API OS_Result OS_AppendFile(S8_String path, S8_String string);
|
||||||
|
OS_API OS_Result OS_WriteFile(S8_String path, S8_String string);
|
||||||
|
OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path);
|
||||||
|
OS_API int OS_SystemF(const char *string, ...);
|
||||||
|
OS_API int64_t OS_GetFileModTime(S8_String file);
|
||||||
|
OS_API OS_Date OS_GetDate(void);
|
||||||
|
OS_API S8_String UTF_CreateStringFromWidechar(MA_Arena *arena, wchar_t *wstr, int64_t wsize);
|
||||||
|
OS_API S8_List S8_SplitOnRegex(MA_Arena *arena, S8_String string, S8_String regex, unsigned flags);
|
||||||
|
OS_API S8_List OS_ListDirRegex(MA_Arena *arena, S8_String path, unsigned flags, char *regex);
|
||||||
|
OS_API S8_String OS_ListDirRegexAsString(MA_Arena *arena, S8_String path, unsigned flags, char *regex);
|
||||||
|
OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath);
|
||||||
|
OS_API S8_String OS_ExpandIncludes(MA_Arena *arena, S8_String filepath);
|
||||||
|
OS_API bool WIN_EnableTerminalColors(void);
|
||||||
|
#endif // OS_FS_HEADER
|
||||||
53
src/core/hash.c
Executable file
53
src/core/hash.c
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
// FNV HASH (1a?)
|
||||||
|
HASH_API_FUNCTION uint64_t HashBytes(void *data, uint64_t size) {
|
||||||
|
uint8_t *data8 = (uint8_t *)data;
|
||||||
|
uint64_t hash = (uint64_t)14695981039346656037ULL;
|
||||||
|
for (uint64_t i = 0; i < size; i++) {
|
||||||
|
hash = hash ^ (uint64_t)(data8[i]);
|
||||||
|
hash = hash * (uint64_t)1099511628211ULL;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
HASH_API_FUNCTION RandomSeed MakeRandomSeed(uint64_t value) {
|
||||||
|
RandomSeed result;
|
||||||
|
result.a = value;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
HASH_API_FUNCTION uint64_t GetRandomU64(RandomSeed *state) {
|
||||||
|
uint64_t x = state->a;
|
||||||
|
x ^= x << 13;
|
||||||
|
x ^= x >> 7;
|
||||||
|
x ^= x << 17;
|
||||||
|
return state->a = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
HASH_API_FUNCTION int GetRandomRangeI(RandomSeed *seed, int first, int last_included) {
|
||||||
|
uint64_t random = GetRandomU64(seed);
|
||||||
|
int range = (last_included - first + 1);
|
||||||
|
int mapped = random % range;
|
||||||
|
int result = mapped + first;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
HASH_API_FUNCTION double GetRandomNormal(RandomSeed *series) {
|
||||||
|
uint64_t rnd = GetRandomU64(series);
|
||||||
|
double result = (double)rnd / (double)UINT64_MAX;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
HASH_API_FUNCTION double GetRandomNormalRange(RandomSeed *seed, double min, double max) {
|
||||||
|
double value = GetRandomNormal(seed);
|
||||||
|
double result = value * (max - min) + min;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
HASH_API_FUNCTION uint64_t HashMix(uint64_t x, uint64_t y) {
|
||||||
|
x ^= y;
|
||||||
|
x *= 0xff51afd7ed558ccd;
|
||||||
|
x ^= x >> 32;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
33
src/core/hash.h
Executable file
33
src/core/hash.h
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef HASH_HEADER
|
||||||
|
#define HASH_HEADER
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef HASH_API_FUNCTION
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define HASH_API_FUNCTION extern "C"
|
||||||
|
#else
|
||||||
|
#define HASH_API_FUNCTION
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//@begin gen_structs
|
||||||
|
typedef struct RandomSeed RandomSeed;
|
||||||
|
//@end gen_structs
|
||||||
|
|
||||||
|
struct RandomSeed {
|
||||||
|
uint64_t a;
|
||||||
|
};
|
||||||
|
|
||||||
|
//@begin gen_api_funcs
|
||||||
|
HASH_API_FUNCTION uint64_t HashBytes(void *data, uint64_t size);
|
||||||
|
HASH_API_FUNCTION RandomSeed MakeRandomSeed(uint64_t value);
|
||||||
|
HASH_API_FUNCTION uint64_t GetRandomU64(RandomSeed *state);
|
||||||
|
HASH_API_FUNCTION int GetRandomRangeI(RandomSeed *seed, int first, int last_included);
|
||||||
|
HASH_API_FUNCTION double GetRandomNormal(RandomSeed *series);
|
||||||
|
HASH_API_FUNCTION double GetRandomNormalRange(RandomSeed *seed, double min, double max);
|
||||||
|
HASH_API_FUNCTION uint64_t HashMix(uint64_t x, uint64_t y);
|
||||||
|
//@end gen_api_funcs
|
||||||
|
|
||||||
|
#define WRAP_AROUND_POWER_OF_2(x, pow2) (((x) & ((pow2)-1llu)))
|
||||||
|
static inline float GetRandomNormalF(RandomSeed *series) { return (float)GetRandomNormal(series); }
|
||||||
|
#endif
|
||||||
295
src/core/io.h
Executable file
295
src/core/io.h
Executable file
@@ -0,0 +1,295 @@
|
|||||||
|
#ifndef IO_HEADER
|
||||||
|
#define IO_HEADER
|
||||||
|
#include <stdbool.h>
|
||||||
|
typedef enum IO_ErrorResult IO_ErrorResult;
|
||||||
|
#ifndef IO_API
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define IO_API extern "C"
|
||||||
|
#else
|
||||||
|
#define IO_API
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum IO_ErrorResult {
|
||||||
|
IO_ErrorResult_Continue,
|
||||||
|
IO_ErrorResult_Break,
|
||||||
|
IO_ErrorResult_Exit,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define IO_DebugBreak() (__debugbreak(), 0)
|
||||||
|
#else
|
||||||
|
#define IO_DebugBreak() (IO_Exit(1), 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern void (*IO_User_OutputMessage)(char *str, int len);
|
||||||
|
|
||||||
|
#define IO__STRINGIFY(x) #x
|
||||||
|
#define IO__TOSTRING(x) IO__STRINGIFY(x)
|
||||||
|
#define IO_LINE IO__TOSTRING(__LINE__)
|
||||||
|
|
||||||
|
#define IO_Assert(x) !(x) && IO__FatalError((char *)(__FILE__ "(" IO_LINE "): " \
|
||||||
|
"error: " #x "\n")) && \
|
||||||
|
IO_DebugBreak()
|
||||||
|
#define IO_FatalErrorf(...) \
|
||||||
|
do { \
|
||||||
|
bool result = IO__FatalErrorf(__FILE__, __LINE__, __VA_ARGS__); \
|
||||||
|
if (result) IO_DebugBreak(); \
|
||||||
|
} while (0)
|
||||||
|
#define IO_FatalError(...) \
|
||||||
|
do { \
|
||||||
|
bool result = IO__FatalError(__FILE__ "(" IO_LINE "): error - " __VA_ARGS__); \
|
||||||
|
if (result) IO_DebugBreak(); \
|
||||||
|
} while (0)
|
||||||
|
#define IO_Assertf(x, ...) \
|
||||||
|
do { \
|
||||||
|
if (!(x)) { \
|
||||||
|
bool result = IO__FatalErrorf(__FILE__, __LINE__, __VA_ARGS__); \
|
||||||
|
if (result) IO_DebugBreak(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define IO_InvalidElseIf(c) \
|
||||||
|
else if (c) { \
|
||||||
|
IO_InvalidCodepath(); \
|
||||||
|
}
|
||||||
|
#define IO_InvalidElse() \
|
||||||
|
else { \
|
||||||
|
IO_InvalidCodepath(); \
|
||||||
|
}
|
||||||
|
#define IO_InvalidCodepath() IO_FatalError("This codepath is invalid")
|
||||||
|
#define IO_InvalidDefaultCase() \
|
||||||
|
default: { \
|
||||||
|
IO_FatalError("Entered invalid switch statement case"); \
|
||||||
|
}
|
||||||
|
#define IO_Todo() IO_FatalError("This codepath is not implemented yet")
|
||||||
|
|
||||||
|
IO_API bool IO__FatalErrorf(const char *file, int line, const char *msg, ...);
|
||||||
|
IO_API void IO_Printf(const char *msg, ...);
|
||||||
|
IO_API bool IO__FatalError(char *msg);
|
||||||
|
IO_API void IO_Print(char *msg);
|
||||||
|
IO_API void IO_OutputMessage(char *str, int len);
|
||||||
|
IO_API IO_ErrorResult IO_OutputError(char *str, int len);
|
||||||
|
IO_API void IO_Exit(int error_code);
|
||||||
|
IO_API bool IO_IsDebuggerPresent(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IO_IMPLEMENTATION
|
||||||
|
#ifndef IO_SNPRINTF
|
||||||
|
#include <stdio.h>
|
||||||
|
#define IO_SNPRINTF snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IO_VSNPRINTF
|
||||||
|
#include <stdio.h>
|
||||||
|
#define IO_VSNPRINTF vsnprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IO_ALLOCATE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define IO_ALLOCATE(x) malloc(x)
|
||||||
|
#define IO_FREE(x) free(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IO_FN
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define IO_FN __attribute__((unused)) static
|
||||||
|
#else
|
||||||
|
#define IO_FN static
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IO_FN int IO_Strlen(char *string) {
|
||||||
|
int len = 0;
|
||||||
|
while (*string++ != 0) len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void (*IO_User_OutputMessage)(char *str, int len);
|
||||||
|
|
||||||
|
IO_API bool IO__FatalErrorf(const char *file, int line, const char *msg, ...) {
|
||||||
|
va_list args1;
|
||||||
|
va_list args2;
|
||||||
|
char buff[2048];
|
||||||
|
|
||||||
|
va_start(args1, msg);
|
||||||
|
va_copy(args2, args1);
|
||||||
|
int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2);
|
||||||
|
va_end(args2);
|
||||||
|
|
||||||
|
char *new_buffer = 0;
|
||||||
|
char *user_message = buff;
|
||||||
|
if (size >= sizeof(buff)) {
|
||||||
|
size += 4;
|
||||||
|
new_buffer = (char *)IO_ALLOCATE(size);
|
||||||
|
IO_VSNPRINTF(new_buffer, size, msg, args1);
|
||||||
|
user_message = new_buffer;
|
||||||
|
}
|
||||||
|
va_end(args1);
|
||||||
|
|
||||||
|
IO_ErrorResult ret = IO_ErrorResult_Continue;
|
||||||
|
{
|
||||||
|
char buff2[2048];
|
||||||
|
char *result = buff2;
|
||||||
|
char *b = 0;
|
||||||
|
int size = IO_SNPRINTF(buff2, sizeof(buff2), "%s(%d): error: %s \n", file, line, user_message);
|
||||||
|
if (size >= sizeof(buff2)) {
|
||||||
|
size += 4;
|
||||||
|
b = (char *)IO_ALLOCATE(size);
|
||||||
|
size = IO_SNPRINTF(b, size, "%s(%d): error: %s \n", file, line, user_message);
|
||||||
|
result = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = IO_OutputError(result, size);
|
||||||
|
if (ret == IO_ErrorResult_Exit) {
|
||||||
|
IO_Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b) {
|
||||||
|
IO_FREE(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_buffer) {
|
||||||
|
IO_FREE(new_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret == IO_ErrorResult_Break;
|
||||||
|
}
|
||||||
|
|
||||||
|
IO_API void IO_Printf(const char *msg, ...) {
|
||||||
|
va_list args1;
|
||||||
|
va_list args2;
|
||||||
|
char buff[2048];
|
||||||
|
|
||||||
|
va_start(args1, msg);
|
||||||
|
va_copy(args2, args1);
|
||||||
|
int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2);
|
||||||
|
va_end(args2);
|
||||||
|
|
||||||
|
char *new_buffer = 0;
|
||||||
|
char *result = buff;
|
||||||
|
if (size >= sizeof(buff)) {
|
||||||
|
size += 4;
|
||||||
|
new_buffer = (char *)IO_ALLOCATE(size);
|
||||||
|
IO_VSNPRINTF(new_buffer, size, msg, args1);
|
||||||
|
result = new_buffer;
|
||||||
|
}
|
||||||
|
va_end(args1);
|
||||||
|
|
||||||
|
if (IO_User_OutputMessage) {
|
||||||
|
IO_User_OutputMessage(result, size);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_OutputMessage(result, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_buffer) {
|
||||||
|
IO_FREE(new_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IO_API bool IO__FatalError(char *msg) {
|
||||||
|
int len = IO_Strlen(msg);
|
||||||
|
IO_ErrorResult result = IO_OutputError(msg, len);
|
||||||
|
if (result == IO_ErrorResult_Exit) {
|
||||||
|
IO_Exit(1);
|
||||||
|
}
|
||||||
|
return result == IO_ErrorResult_Break;
|
||||||
|
}
|
||||||
|
|
||||||
|
IO_API void IO_Print(char *msg) {
|
||||||
|
int len = IO_Strlen(msg);
|
||||||
|
if (IO_User_OutputMessage) {
|
||||||
|
IO_User_OutputMessage(msg, len);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_OutputMessage(msg, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "user32")
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
IO_API bool IO_IsDebuggerPresent(void) {
|
||||||
|
return IsDebuggerPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
IO_API void IO_OutputMessage(char *str, int len) {
|
||||||
|
printf("%.*s", len, str);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
IO_API IO_ErrorResult IO_OutputError(char *str, int len) {
|
||||||
|
IO_ErrorResult result = IO_ErrorResult_Continue;
|
||||||
|
IO_OutputMessage(str, len);
|
||||||
|
|
||||||
|
char *msg = str;
|
||||||
|
if (str[len] != 0) {
|
||||||
|
msg = (char *)IO_ALLOCATE(len + 1);
|
||||||
|
for (int i = 0; i < len; i += 1) msg[i] = str[i];
|
||||||
|
msg[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputDebugStringA(msg);
|
||||||
|
if (!IsDebuggerPresent()) {
|
||||||
|
|
||||||
|
// Limit size of error output message
|
||||||
|
char tmp = 0;
|
||||||
|
if (len > 4096) {
|
||||||
|
tmp = str[4096];
|
||||||
|
str[4096] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageBoxA(0, msg, "Error!", 0);
|
||||||
|
|
||||||
|
if (tmp != 0) {
|
||||||
|
str[4096] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = IO_ErrorResult_Exit;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = IO_ErrorResult_Break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg != str) {
|
||||||
|
IO_FREE(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
IO_API void IO_Exit(int error_code) {
|
||||||
|
ExitProcess(error_code);
|
||||||
|
}
|
||||||
|
#else // _WIN32 else // LIBC
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
IO_API IO_ErrorResult IO_OutputError(char *str, int len) {
|
||||||
|
fprintf(stderr, "%.*s", len, str);
|
||||||
|
return IO_ErrorResult_Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
IO_API void IO_OutputMessage(char *str, int len) {
|
||||||
|
fprintf(stdout, "%.*s", len, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
IO_API void IO_Exit(int error_code) {
|
||||||
|
exit(error_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
IO_API bool IO_IsDebuggerPresent(void) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // LIBC
|
||||||
|
#endif // IO_IMPLEMENTATION
|
||||||
129
src/core/linked_list.h
Executable file
129
src/core/linked_list.h
Executable file
@@ -0,0 +1,129 @@
|
|||||||
|
#ifndef LINKED_LIST_HEADER
|
||||||
|
#define LINKED_LIST_HEADER
|
||||||
|
|
||||||
|
#define SLL_QUEUE_ADD_MOD(f, l, n, next) \
|
||||||
|
do { \
|
||||||
|
(n)->next = 0; \
|
||||||
|
if ((f) == 0) { \
|
||||||
|
(f) = (l) = (n); \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(l) = (l)->next = (n); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next)
|
||||||
|
|
||||||
|
#define SLL_QUEUE_POP_FIRST_MOD(f, l, next) \
|
||||||
|
do { \
|
||||||
|
if ((f) == (l)) { \
|
||||||
|
(f) = (l) = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(f) = (f)->next; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define SLL_QUEUE_POP_FIRST(f, l) SLL_QUEUE_POP_FIRST_MOD(f, l, next)
|
||||||
|
|
||||||
|
#define SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) \
|
||||||
|
do { \
|
||||||
|
(new_stack_base)->next = (stack_base); \
|
||||||
|
(stack_base) = (new_stack_base); \
|
||||||
|
} while (0)
|
||||||
|
#define SLL_STACK_ADD(stack_base, new_stack_base) \
|
||||||
|
SLL_STACK_ADD_MOD(stack_base, new_stack_base, next)
|
||||||
|
|
||||||
|
#define SLL_STACK_POP_AND_STORE(stack_base, out_node) \
|
||||||
|
do { \
|
||||||
|
if (stack_base) { \
|
||||||
|
(out_node) = (stack_base); \
|
||||||
|
(stack_base) = (stack_base)->next; \
|
||||||
|
(out_node)->next = 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define DLL_QUEUE_ADD_MOD(f, l, node, next, prev) \
|
||||||
|
do { \
|
||||||
|
if ((f) == 0) { \
|
||||||
|
(f) = (l) = (node); \
|
||||||
|
(node)->prev = 0; \
|
||||||
|
(node)->next = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(l)->next = (node); \
|
||||||
|
(node)->prev = (l); \
|
||||||
|
(node)->next = 0; \
|
||||||
|
(l) = (node); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define DLL_QUEUE_ADD(f, l, node) DLL_QUEUE_ADD_MOD(f, l, node, next, prev)
|
||||||
|
#define DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) \
|
||||||
|
do { \
|
||||||
|
if ((first) == (last)) { \
|
||||||
|
IO_Assertf((node) == (first), "Not you are trying to remove is not in the list"); \
|
||||||
|
(first) = (last) = 0; \
|
||||||
|
} \
|
||||||
|
else if ((last) == (node)) { \
|
||||||
|
(last) = (last)->prev; \
|
||||||
|
(last)->next = 0; \
|
||||||
|
} \
|
||||||
|
else if ((first) == (node)) { \
|
||||||
|
(first) = (first)->next; \
|
||||||
|
(first)->prev = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(node)->prev->next = (node)->next; \
|
||||||
|
(node)->next->prev = (node)->prev; \
|
||||||
|
} \
|
||||||
|
if (node) { \
|
||||||
|
(node)->prev = 0; \
|
||||||
|
(node)->next = 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define DLL_QUEUE_REMOVE(first, last, node) DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev)
|
||||||
|
|
||||||
|
#define DLL_STACK_ADD_MOD(first, node, next, prev) \
|
||||||
|
do { \
|
||||||
|
(node)->next = (first); \
|
||||||
|
if ((first)) \
|
||||||
|
(first)->prev = (node); \
|
||||||
|
(first) = (node); \
|
||||||
|
(node)->prev = 0; \
|
||||||
|
} while (0)
|
||||||
|
#define DLL_STACK_ADD(first, node) DLL_STACK_ADD_MOD(first, node, next, prev)
|
||||||
|
#define DLL_STACK_REMOVE_MOD(first, node, next, prev) \
|
||||||
|
do { \
|
||||||
|
if ((node) == (first)) { \
|
||||||
|
(first) = (first)->next; \
|
||||||
|
if ((first)) \
|
||||||
|
(first)->prev = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(node)->prev->next = (node)->next; \
|
||||||
|
if ((node)->next) \
|
||||||
|
(node)->next->prev = (node)->prev; \
|
||||||
|
} \
|
||||||
|
if (node) { \
|
||||||
|
(node)->prev = 0; \
|
||||||
|
(node)->next = 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define DLL_STACK_REMOVE(first, node) DLL_STACK_REMOVE_MOD(first, node, next, prev)
|
||||||
|
|
||||||
|
#define DLL_INSERT_NEXT_MOD(base, new, next, prev) \
|
||||||
|
do { \
|
||||||
|
if ((base) == 0) { \
|
||||||
|
(base) = (new); \
|
||||||
|
(new)->next = 0; \
|
||||||
|
(new)->prev = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(new)->next = (base)->next; \
|
||||||
|
(base)->next = (new); \
|
||||||
|
(new)->prev = (base); \
|
||||||
|
if ((new)->next) (new)->next->prev = (new); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define DLL_INSERT_NEXT(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev)
|
||||||
|
#define DLL_INSERT_PREV(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev)
|
||||||
|
|
||||||
|
#endif
|
||||||
26
src/core/load_library.c
Executable file
26
src/core/load_library.c
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
#include "load_library.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
API LIB_Library LIB_LoadLibrary(char *str) {
|
||||||
|
HMODULE module = LoadLibraryA(str);
|
||||||
|
return (LIB_Library)module;
|
||||||
|
}
|
||||||
|
|
||||||
|
API void *LIB_LoadSymbol(LIB_Library lib, char *symbol) {
|
||||||
|
void *result = GetProcAddress((HMODULE)lib, symbol);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
API bool LIB_UnloadLibrary(LIB_Library lib) {
|
||||||
|
BOOL result = FreeLibrary((HMODULE)lib);
|
||||||
|
if (result == 0) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // _WIN32
|
||||||
14
src/core/load_library.h
Executable file
14
src/core/load_library.h
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef LIB_LOAD_HEADER
|
||||||
|
#define LIB_LOAD_HEADER
|
||||||
|
|
||||||
|
typedef void *LIB_Library;
|
||||||
|
|
||||||
|
API LIB_Library LIB_LoadLibrary(char *str);
|
||||||
|
API void *LIB_LoadSymbol(LIB_Library lib, char *symbol);
|
||||||
|
API bool LIB_UnloadLibrary(LIB_Library lib);
|
||||||
|
|
||||||
|
#ifndef LIB_EXPORT
|
||||||
|
#define LIB_EXPORT __declspec(dllexport)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
103
src/core/map.c
Executable file
103
src/core/map.c
Executable file
@@ -0,0 +1,103 @@
|
|||||||
|
#include "map.h"
|
||||||
|
|
||||||
|
API void Map_Reserve(Map *map, int size) {
|
||||||
|
Map old_map = *map;
|
||||||
|
|
||||||
|
map->len = 0;
|
||||||
|
map->cap = size;
|
||||||
|
if (map->cap < 32) {
|
||||||
|
map->cap = 32;
|
||||||
|
IO_Assert(map->allocator.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t byte_size = sizeof(Map_Entry) * map->cap;
|
||||||
|
map->entries = (Map_Entry *)M_Alloc(map->allocator, byte_size);
|
||||||
|
MA_MemoryZero(map->entries, byte_size);
|
||||||
|
|
||||||
|
if (old_map.entries) {
|
||||||
|
for (int i = 0; i < old_map.cap; i += 1) {
|
||||||
|
Map_Entry *it = old_map.entries + i;
|
||||||
|
if (it->key) Map_InsertEntry(map, it->key, it->value);
|
||||||
|
}
|
||||||
|
M_Dealloc(map->allocator, old_map.entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
API Map_Entry *Map_GetEntryBase(Map *map, uint64_t key) {
|
||||||
|
IO_Assert(key);
|
||||||
|
if (map->len * 2 >= map->cap) Map_Reserve(map, map->cap * 2);
|
||||||
|
|
||||||
|
uint64_t hash = HashBytes(&key, sizeof(key));
|
||||||
|
if (hash == 0) hash += 1;
|
||||||
|
uint64_t index = WRAP_AROUND_POWER_OF_2(hash, map->cap);
|
||||||
|
uint64_t i = index;
|
||||||
|
for (;;) {
|
||||||
|
Map_Entry *it = map->entries + i;
|
||||||
|
if (it->key == key || it->key == 0) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = WRAP_AROUND_POWER_OF_2(i + 1, map->cap);
|
||||||
|
if (i == index) return NULL;
|
||||||
|
}
|
||||||
|
IO_InvalidCodepath();
|
||||||
|
}
|
||||||
|
|
||||||
|
API bool Map_InsertWithoutReplace(Map *map, void *key, void *value) {
|
||||||
|
Map_Entry *entry = Map_GetEntryBase(map, (uint64_t)key);
|
||||||
|
if (entry->key != 0) return false;
|
||||||
|
|
||||||
|
map->len += 1;
|
||||||
|
entry->key = (uint64_t)key;
|
||||||
|
entry->value = (uint64_t)value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
API Map_Entry *Map_InsertEntry(Map *map, uint64_t key, uint64_t value) {
|
||||||
|
Map_Entry *entry = Map_GetEntryBase(map, key);
|
||||||
|
if (entry->key == key) {
|
||||||
|
entry->value = value;
|
||||||
|
}
|
||||||
|
if (entry->key == 0) {
|
||||||
|
entry->key = key;
|
||||||
|
entry->value = value;
|
||||||
|
map->len += 1;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
API Map_Entry *Map_GetEntry(Map *map, uint64_t key) {
|
||||||
|
Map_Entry *entry = Map_GetEntryBase(map, key);
|
||||||
|
if (entry && entry->key == key) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
API void Map_Insert(Map *map, S8_String keystr, void *value) {
|
||||||
|
uint64_t key = HashBytes(keystr.str, keystr.len);
|
||||||
|
Map_InsertEntry(map, key, (uint64_t)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
API void *Map_Get(Map *map, S8_String keystr) {
|
||||||
|
uint64_t key = HashBytes(keystr.str, keystr.len);
|
||||||
|
Map_Entry *r = Map_GetEntry(map, key);
|
||||||
|
return r ? (void *)r->value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
API void Map_InsertU64(Map *map, uint64_t key, void *value) {
|
||||||
|
Map_InsertEntry(map, key, (uint64_t)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
API void *Map_GetU64(Map *map, uint64_t key) {
|
||||||
|
Map_Entry *r = Map_GetEntry(map, key);
|
||||||
|
return r ? (void *)r->value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
API void *Map_GetP(Map *map, void *key) {
|
||||||
|
return Map_GetU64(map, (uint64_t)key);
|
||||||
|
}
|
||||||
|
|
||||||
|
API void Map_InsertP(Map *map, void *key, void *value) {
|
||||||
|
Map_InsertEntry(map, (uint64_t)key, (uint64_t)value);
|
||||||
|
}
|
||||||
27
src/core/map.h
Executable file
27
src/core/map.h
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
typedef struct Map_Entry Map_Entry;
|
||||||
|
typedef struct Map Map;
|
||||||
|
|
||||||
|
struct Map_Entry {
|
||||||
|
uint64_t key;
|
||||||
|
uint64_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Map {
|
||||||
|
M_Allocator allocator;
|
||||||
|
Map_Entry *entries;
|
||||||
|
int cap;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
API void Map_Reserve(Map *map, int size);
|
||||||
|
API Map_Entry *Map_GetEntryBase(Map *map, uint64_t key);
|
||||||
|
API Map_Entry *Map_InsertEntry(Map *map, uint64_t key, uint64_t value);
|
||||||
|
API Map_Entry *Map_GetEntry(Map *map, uint64_t key);
|
||||||
|
API void Map_Insert(Map *map, S8_String keystr, void *value);
|
||||||
|
API void *Map_Get(Map *map, S8_String keystr);
|
||||||
|
API void Map_InsertU64(Map *map, uint64_t keystr, void *value);
|
||||||
|
API void *Map_GetU64(Map *map, uint64_t keystr);
|
||||||
|
API void *Map_GetP(Map *map, void *key);
|
||||||
|
API void Map_InsertP(Map *map, void *key, void *value);
|
||||||
|
API void Map_Insert2P(Map *map, void *key, void *value);
|
||||||
2112
src/core/multimedia.h
Executable file
2112
src/core/multimedia.h
Executable file
File diff suppressed because it is too large
Load Diff
118
src/core/preproc_env.h
Executable file
118
src/core/preproc_env.h
Executable file
@@ -0,0 +1,118 @@
|
|||||||
|
#ifndef PREPROC_ENV_HEADER
|
||||||
|
#define PREPROC_ENV_HEADER
|
||||||
|
|
||||||
|
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && defined(__MACH__)
|
||||||
|
#define OS_MAC 1
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#define OS_WINDOWS 1
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#define OS_POSIX 1
|
||||||
|
#define OS_LINUX 1
|
||||||
|
#else
|
||||||
|
#error Unsupported platform
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define COMPILER_CLANG 1
|
||||||
|
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||||
|
#define COMPILER_GCC 1
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define COMPILER_MSVC 1
|
||||||
|
#elif defined(__TINYC__)
|
||||||
|
#define COMPILER_TCC 1
|
||||||
|
#else
|
||||||
|
#error Unsupported compiler
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define LANG_CPP 1
|
||||||
|
#else
|
||||||
|
#define LANG_C 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_MAC
|
||||||
|
#define OS_MAC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_WINDOWS
|
||||||
|
#define OS_WINDOWS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_LINUX
|
||||||
|
#define OS_LINUX 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_POSIX
|
||||||
|
#define OS_POSIX 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef COMPILER_MSVC
|
||||||
|
#define COMPILER_MSVC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef COMPILER_CLANG
|
||||||
|
#define COMPILER_CLANG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef COMPILER_GCC
|
||||||
|
#define COMPILER_GCC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef COMPILER_TCC
|
||||||
|
#define COMPILER_TCC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LANG_CPP
|
||||||
|
#define LANG_CPP 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LANG_C
|
||||||
|
#define LANG_C 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if COMPILER_MSVC
|
||||||
|
#define INLINE __forceinline
|
||||||
|
#elif COMPILER_GCC || COMPILER_CLANG
|
||||||
|
#define INLINE __attribute__((always_inline)) inline
|
||||||
|
#else
|
||||||
|
#define INLINE inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if COMPILER_GCC || COMPILER_CLANG
|
||||||
|
#define FUNC_INTERNAL __attribute__((unused)) static
|
||||||
|
#else
|
||||||
|
#define FUNC_INTERNAL static
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FN FUNC_INTERNAL
|
||||||
|
|
||||||
|
#if defined(__cplusplus) && __cplusplus >= 201103L
|
||||||
|
#define THREAD_LOCAL thread_local
|
||||||
|
#elif COMPILER_GCC || COMPILER_CLANG
|
||||||
|
#define THREAD_LOCAL __thread
|
||||||
|
#elif COMPILER_MSVC
|
||||||
|
#define THREAD_LOCAL __declspec(thread)
|
||||||
|
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
|
||||||
|
#define THREAD_LOCAL _Thread_local
|
||||||
|
#elif COMPILER_TCC
|
||||||
|
#define MA_THREAD_LOCAL _Thread_local
|
||||||
|
#else
|
||||||
|
#error Couldnt figure out thread local, needs to be provided manually
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if COMPILER_CLANG
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wmicrosoft-enum-forward-reference"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LANG_CPP
|
||||||
|
#define API extern "C"
|
||||||
|
#else
|
||||||
|
#define API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // PREPROC_ENV_HEADER
|
||||||
685
src/core/regex.h
Executable file
685
src/core/regex.h
Executable file
@@ -0,0 +1,685 @@
|
|||||||
|
#ifndef RE_HEADER
|
||||||
|
#define RE_HEADER
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifndef RE_Int
|
||||||
|
#define RE_Int int64_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RE_API
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define RE_API extern "C"
|
||||||
|
#else
|
||||||
|
#define RE_API
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RE_FN
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define RE_FN __attribute__((unused)) static
|
||||||
|
#else
|
||||||
|
#define RE_FN static
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RE_Arena
|
||||||
|
#define RE_Arena RE__Arena
|
||||||
|
typedef struct RE__Arena {
|
||||||
|
char *buff;
|
||||||
|
RE_Int len;
|
||||||
|
RE_Int cap;
|
||||||
|
} RE_Arena;
|
||||||
|
#define RE_PushSize(arena, size) RE__PushSize(arena, size)
|
||||||
|
#define RE_ArenaFromBuffer(buff, size) RE__ArenaFromBuffer(buff, size)
|
||||||
|
#else
|
||||||
|
#define RE_CUSTOM_ARENA_TYPE
|
||||||
|
#ifndef RE_PushSize
|
||||||
|
#error If you use a custom Arena type, you need to implement RE_PushSize macro
|
||||||
|
#endif
|
||||||
|
#ifndef RE_ArenaFromBuffer
|
||||||
|
#define RE_ArenaFromBuffer(buff, size) RE__ArenaFromBufferStub(buff, size)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct RE_String RE_String;
|
||||||
|
typedef struct RE_Utf32Result RE_Utf32Result;
|
||||||
|
typedef struct RE_Parser RE_Parser;
|
||||||
|
typedef enum RE_MatchKind RE_MatchKind;
|
||||||
|
typedef struct RE_Regex RE_Regex;
|
||||||
|
typedef struct RE_Match RE_Match;
|
||||||
|
|
||||||
|
/* @todo
|
||||||
|
Add \W \D \S oppsites
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum RE_MatchKind {
|
||||||
|
RE_MATCH_NULL,
|
||||||
|
RE_MATCH_FRONT,
|
||||||
|
RE_MATCH_BACK,
|
||||||
|
RE_MATCH_WORD,
|
||||||
|
RE_MATCH_OR,
|
||||||
|
RE_MATCH_GROUP,
|
||||||
|
RE_MATCH_SELECTED,
|
||||||
|
RE_MATCH_NOT_SELECTED,
|
||||||
|
RE_MATCH_RANGE,
|
||||||
|
RE_MATCH_ANY,
|
||||||
|
RE_MATCH_ANY_WORD,
|
||||||
|
RE_MATCH_ANY_DIGIT,
|
||||||
|
RE_MATCH_ANY_WHITESPACE,
|
||||||
|
RE_MATCH_ONE_OR_MORE,
|
||||||
|
RE_MATCH_ZERO_OR_MORE,
|
||||||
|
RE_MATCH_ZERO_OR_ONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RE_Regex {
|
||||||
|
RE_MatchKind kind;
|
||||||
|
RE_Regex *next;
|
||||||
|
RE_Regex *prev;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
char word_min;
|
||||||
|
char word_max;
|
||||||
|
};
|
||||||
|
char word;
|
||||||
|
uint32_t word32;
|
||||||
|
RE_Regex *child;
|
||||||
|
struct {
|
||||||
|
RE_Regex *left;
|
||||||
|
RE_Regex *right;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
RE_Regex *first;
|
||||||
|
RE_Regex *last;
|
||||||
|
} group;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RE_Match {
|
||||||
|
RE_Int pos;
|
||||||
|
RE_Int size;
|
||||||
|
};
|
||||||
|
|
||||||
|
RE_API bool RE1_AreEqual(char *regex, char *string);
|
||||||
|
RE_API bool RE2_AreEqual(RE_Regex *regex, char *string);
|
||||||
|
RE_API bool RE3_AreEqual(RE_Regex *regex, char *string, RE_Int len);
|
||||||
|
RE_API RE_Match RE1_Find(char *regex, char *string);
|
||||||
|
RE_API RE_Match RE2_Find(RE_Regex *regex, char *string);
|
||||||
|
RE_API RE_Match RE3_Find(RE_Regex *regex, char *string, RE_Int len);
|
||||||
|
RE_API RE_Match RE2_FindAgain(RE_Regex *regex, char *string, RE_Match prev_match);
|
||||||
|
RE_API RE_Match RE3_FindAgain(RE_Regex *regex, char *string, RE_Int len, RE_Match prev_match);
|
||||||
|
RE_API RE_Int RE3_MatchFront(RE_Regex *regex, char *string, RE_Int len, char *string_front);
|
||||||
|
RE_API RE_Regex *RE1_Parse(RE_Arena *arena, char *string);
|
||||||
|
RE_API RE_Regex *RE2_Parse(RE_Arena *arena, char *string, RE_Int len);
|
||||||
|
RE_API RE_Regex *RE1_ParseBuff(char *buff, RE_Int buffsize, char *string);
|
||||||
|
RE_API RE_Regex *RE2_ParseBuff(char *buff, RE_Int buffsize, char *string, RE_Int len);
|
||||||
|
|
||||||
|
#endif // RE_HEADER
|
||||||
|
|
||||||
|
#ifdef RE_IMPLEMENTATION
|
||||||
|
#ifndef RE_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define RE_ASSERT(x) assert(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RE_STRICT_ASSERT
|
||||||
|
#define RE_STRICT_ASSERT RE_ASSERT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RE__MemoryZero
|
||||||
|
#define RE__MemoryZero RE__MemoryZero
|
||||||
|
RE_FN void RE__MemoryZero(void *p, size_t size) {
|
||||||
|
uint8_t *p8 = (uint8_t *)p;
|
||||||
|
while (size--) *p8++ = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RE_CUSTOM_ARENA_TYPE
|
||||||
|
RE_FN void *RE__PushSize(RE_Arena *arena, RE_Int size) {
|
||||||
|
if (arena->len + size > arena->cap) {
|
||||||
|
RE_ASSERT(!"RE_Regex: Not enough memory passed for this regex");
|
||||||
|
}
|
||||||
|
void *result = arena->buff + arena->len;
|
||||||
|
arena->len += size;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_FN RE_Arena RE__ArenaFromBuffer(char *buff, RE_Int size) {
|
||||||
|
RE_Arena result;
|
||||||
|
result.len = 0;
|
||||||
|
result.cap = size;
|
||||||
|
result.buff = buff;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
RE_FN RE_Arena RE__ArenaFromBufferStub(char *buff, RE_Int size) {
|
||||||
|
RE_Arena result;
|
||||||
|
RE_ASSERT(!"RE_Regex: ArenaFromBuffer is not implemented");
|
||||||
|
RE__MemoryZero(&result, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct RE_String {
|
||||||
|
char *str;
|
||||||
|
RE_Int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
RE_FN RE_String RE_Skip(RE_String string, RE_Int len) {
|
||||||
|
if (len > string.len) len = string.len;
|
||||||
|
RE_Int remain = string.len - len;
|
||||||
|
RE_String result;
|
||||||
|
result.str = string.str + len;
|
||||||
|
result.len = remain;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_FN RE_Int RE_StringLength(char *string) {
|
||||||
|
RE_Int len = 0;
|
||||||
|
while (*string++ != 0) len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RE_Utf32Result {
|
||||||
|
uint32_t out_str;
|
||||||
|
int advance;
|
||||||
|
int error;
|
||||||
|
};
|
||||||
|
|
||||||
|
RE_FN RE_Utf32Result RE_ConvertUTF8ToUTF32(char *c, int max_advance) {
|
||||||
|
RE_Utf32Result result;
|
||||||
|
RE__MemoryZero(&result, sizeof(result));
|
||||||
|
|
||||||
|
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
|
||||||
|
if (max_advance >= 1) {
|
||||||
|
result.out_str = c[0];
|
||||||
|
result.advance = 1;
|
||||||
|
}
|
||||||
|
else result.error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((c[0] & 0xe0) == 0xc0) {
|
||||||
|
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
|
||||||
|
if (max_advance >= 2) {
|
||||||
|
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
|
||||||
|
result.advance = 2;
|
||||||
|
}
|
||||||
|
else result.error = 2;
|
||||||
|
}
|
||||||
|
else result.error = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((c[0] & 0xf0) == 0xe0) {
|
||||||
|
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||||
|
if (max_advance >= 3) {
|
||||||
|
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
|
||||||
|
result.advance = 3;
|
||||||
|
}
|
||||||
|
else result.error = 3;
|
||||||
|
}
|
||||||
|
else result.error = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((c[0] & 0xf8) == 0xf0) {
|
||||||
|
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
|
||||||
|
if (max_advance >= 4) {
|
||||||
|
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
|
||||||
|
result.advance = 4;
|
||||||
|
}
|
||||||
|
else result.error = 4;
|
||||||
|
}
|
||||||
|
else result.error = 4;
|
||||||
|
}
|
||||||
|
else result.error = 4;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RE_DLL_QUEUE_REMOVE(first, last, node) \
|
||||||
|
do { \
|
||||||
|
if ((first) == (last)) { \
|
||||||
|
(first) = (last) = 0; \
|
||||||
|
} \
|
||||||
|
else if ((last) == (node)) { \
|
||||||
|
(last) = (last)->prev; \
|
||||||
|
(last)->next = 0; \
|
||||||
|
} \
|
||||||
|
else if ((first) == (node)) { \
|
||||||
|
(first) = (first)->next; \
|
||||||
|
(first)->prev = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(node)->prev->next = (node)->next; \
|
||||||
|
(node)->next->prev = (node)->prev; \
|
||||||
|
} \
|
||||||
|
if (node) (node)->prev = 0; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define RE_DLL_QUEUE_ADD(f, l, node) \
|
||||||
|
do { \
|
||||||
|
if ((f) == 0) { \
|
||||||
|
(f) = (l) = (node); \
|
||||||
|
(node)->prev = 0; \
|
||||||
|
(node)->next = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(l)->next = (node); \
|
||||||
|
(node)->prev = (l); \
|
||||||
|
(node)->next = 0; \
|
||||||
|
(l) = (node); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static RE_Regex RE_NullRegex;
|
||||||
|
static char RE_NullChar;
|
||||||
|
|
||||||
|
struct RE_Parser {
|
||||||
|
RE_String string;
|
||||||
|
RE_Int i;
|
||||||
|
RE_Regex *first;
|
||||||
|
RE_Regex *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
RE_FN char *RE_GetP(RE_Parser *P) {
|
||||||
|
if (P->i >= P->string.len) return &RE_NullChar;
|
||||||
|
return P->string.str + P->i;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_FN char RE_Get(RE_Parser *P) {
|
||||||
|
if (P->i >= P->string.len) return 0;
|
||||||
|
return P->string.str[P->i];
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_FN char RE_Get1(RE_Parser *P) {
|
||||||
|
if ((P->i + 1) >= P->string.len || P->i >= P->string.len) return 0;
|
||||||
|
return P->string.str[P->i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_FN void RE_Advance(RE_Parser *P) {
|
||||||
|
if (P->i >= P->string.len) return;
|
||||||
|
P->i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_FN RE_Regex *RE_ParseSingle(RE_Parser *P, RE_Arena *arena, RE_Regex **first, RE_Regex **last) {
|
||||||
|
RE_Regex *regex = (RE_Regex *)RE_PushSize(arena, sizeof(RE_Regex));
|
||||||
|
RE__MemoryZero(regex, sizeof(*regex));
|
||||||
|
char *c = RE_GetP(P);
|
||||||
|
RE_Int size_left = P->string.len - P->i;
|
||||||
|
RE_Advance(P);
|
||||||
|
switch (*c) {
|
||||||
|
case ')': RE_STRICT_ASSERT(regex->kind != RE_MATCH_NULL && "Invalid regex syntax, ')' appeared without matching '('"); break;
|
||||||
|
case '\0': RE_STRICT_ASSERT(regex->kind != RE_MATCH_NULL && "Invalid regex syntax, reached end of string obruptly"); break;
|
||||||
|
case '.': regex->kind = RE_MATCH_ANY; break;
|
||||||
|
case '^': regex->kind = RE_MATCH_FRONT; break;
|
||||||
|
case '$': regex->kind = RE_MATCH_BACK; break;
|
||||||
|
|
||||||
|
case '*': {
|
||||||
|
if (*last) {
|
||||||
|
regex->kind = RE_MATCH_ZERO_OR_MORE;
|
||||||
|
RE_Regex *prev = *last;
|
||||||
|
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
|
||||||
|
regex->child = prev;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RE_STRICT_ASSERT(!"Invalid regex syntax, '*' is not attached to anything");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case '+': {
|
||||||
|
if (*last) {
|
||||||
|
regex->kind = RE_MATCH_ONE_OR_MORE;
|
||||||
|
RE_Regex *prev = *last;
|
||||||
|
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
|
||||||
|
regex->child = prev;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RE_STRICT_ASSERT(!"Invalid regex syntax, '+' is not attached to anything");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case '?': {
|
||||||
|
if (*last) {
|
||||||
|
regex->kind = RE_MATCH_ZERO_OR_ONE;
|
||||||
|
RE_Regex *prev = *last;
|
||||||
|
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
|
||||||
|
regex->child = prev;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RE_STRICT_ASSERT(!"Invalid regex syntax, '?' is not attached to anything");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case '[': {
|
||||||
|
regex->kind = RE_MATCH_SELECTED;
|
||||||
|
if (RE_Get(P) == '^') {
|
||||||
|
regex->kind = RE_MATCH_NOT_SELECTED;
|
||||||
|
RE_Advance(P);
|
||||||
|
}
|
||||||
|
while (RE_Get(P) != 0 && RE_Get(P) != ']') {
|
||||||
|
RE_Regex *r = RE_ParseSingle(P, arena, ®ex->group.first, ®ex->group.last);
|
||||||
|
if (r->kind == RE_MATCH_NULL) {
|
||||||
|
regex->kind = RE_MATCH_NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (r->kind == RE_MATCH_WORD && RE_Get(P) == '-') {
|
||||||
|
char word = RE_Get1(P);
|
||||||
|
if (word >= '!' && word <= '~') {
|
||||||
|
RE_Advance(P);
|
||||||
|
RE_Regex *right = RE_ParseSingle(P, arena, 0, 0);
|
||||||
|
if (right->kind == RE_MATCH_NULL) {
|
||||||
|
regex->kind = RE_MATCH_NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
RE_ASSERT(right->kind == RE_MATCH_WORD);
|
||||||
|
RE_ASSERT(right->word == word);
|
||||||
|
r->word_min = word > r->word ? r->word : word;
|
||||||
|
r->word_max = word > r->word ? word : r->word;
|
||||||
|
r->kind = RE_MATCH_RANGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RE_DLL_QUEUE_ADD(regex->group.first, regex->group.last, r);
|
||||||
|
}
|
||||||
|
RE_Advance(P);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case '(': {
|
||||||
|
regex->kind = RE_MATCH_GROUP;
|
||||||
|
while (RE_Get(P) != 0 && RE_Get(P) != ')') {
|
||||||
|
RE_Regex *r = RE_ParseSingle(P, arena, ®ex->group.first, ®ex->group.last);
|
||||||
|
if (r->kind == RE_MATCH_NULL) {
|
||||||
|
regex->kind = RE_MATCH_NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
RE_DLL_QUEUE_ADD(regex->group.first, regex->group.last, r);
|
||||||
|
}
|
||||||
|
RE_Advance(P);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case '|': {
|
||||||
|
if (*last) {
|
||||||
|
regex->kind = RE_MATCH_OR;
|
||||||
|
RE_Regex *left = *last;
|
||||||
|
RE_Regex *right = RE_ParseSingle(P, arena, first, last);
|
||||||
|
if (right->kind == RE_MATCH_NULL) {
|
||||||
|
regex->kind = RE_MATCH_NULL;
|
||||||
|
RE_STRICT_ASSERT(!"Invalid regex syntax, '|' appeared but it's right option is invalid");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RE_DLL_QUEUE_REMOVE(*first, *last, left);
|
||||||
|
regex->left = left;
|
||||||
|
regex->right = right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case '\\': {
|
||||||
|
regex->kind = RE_MATCH_WORD;
|
||||||
|
regex->word = RE_Get(P);
|
||||||
|
switch (regex->word) {
|
||||||
|
case 'n': regex->word = '\n'; break;
|
||||||
|
case 't': regex->word = '\t'; break;
|
||||||
|
case 'r': regex->word = '\r'; break;
|
||||||
|
case 'w': regex->kind = RE_MATCH_ANY_WORD; break;
|
||||||
|
case 'd': regex->kind = RE_MATCH_ANY_DIGIT; break;
|
||||||
|
case 's': regex->kind = RE_MATCH_ANY_WHITESPACE; break;
|
||||||
|
case '\0': {
|
||||||
|
regex->kind = RE_MATCH_NULL;
|
||||||
|
RE_STRICT_ASSERT(!"Invalid regex syntax, escape '\\' followed by end of string");
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
RE_Advance(P);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
regex->kind = RE_MATCH_WORD;
|
||||||
|
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(c, (int)size_left);
|
||||||
|
if (decode.error) {
|
||||||
|
regex->kind = RE_MATCH_NULL;
|
||||||
|
RE_STRICT_ASSERT(!"Invalid regex syntax, string is an invalid utf8");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
regex->word32 = decode.out_str;
|
||||||
|
for (int i = 0; i < decode.advance - 1; i += 1)
|
||||||
|
RE_Advance(P);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return regex;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_FN RE_Int RE_MatchSingle(RE_Regex *regex, RE_String string) {
|
||||||
|
switch (regex->kind) {
|
||||||
|
case RE_MATCH_ZERO_OR_MORE: {
|
||||||
|
RE_Int result = 0;
|
||||||
|
for (; string.len;) {
|
||||||
|
// @idea
|
||||||
|
// In this case (asd)*(asd) we just quit with 0
|
||||||
|
// when we meet asd
|
||||||
|
// Maybe this should be collapsed in parsing stage/
|
||||||
|
// asd should be combined with *asd etc. cause
|
||||||
|
// now it's a bit weird but I dont know why you would
|
||||||
|
// type that in the first place
|
||||||
|
if (RE_MatchSingle(regex->next, string) != -1) break;
|
||||||
|
RE_Int index = RE_MatchSingle(regex->child, string);
|
||||||
|
if (index == -1) break;
|
||||||
|
string = RE_Skip(string, index);
|
||||||
|
result += index;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_ONE_OR_MORE: {
|
||||||
|
RE_Int result = 0;
|
||||||
|
for (; string.len;) {
|
||||||
|
RE_Int index = RE_MatchSingle(regex->child, string);
|
||||||
|
if (index == -1) break;
|
||||||
|
string = RE_Skip(string, index);
|
||||||
|
result += index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == 0) return -1;
|
||||||
|
return result;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_OR: {
|
||||||
|
RE_Int right = RE_MatchSingle(regex->right, string);
|
||||||
|
RE_Int left = RE_MatchSingle(regex->left, string);
|
||||||
|
if (left > right) return left;
|
||||||
|
else return right;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_GROUP: {
|
||||||
|
RE_Int result = 0;
|
||||||
|
for (RE_Regex *it = regex->group.first; it; it = it->next) {
|
||||||
|
if (string.len == 0) return -1;
|
||||||
|
RE_Int index = RE_MatchSingle(it, string);
|
||||||
|
if (index == -1) return -1;
|
||||||
|
result += index;
|
||||||
|
string = RE_Skip(string, index);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_NOT_SELECTED: {
|
||||||
|
for (RE_Regex *it = regex->group.first; it; it = it->next) {
|
||||||
|
RE_Int index = RE_MatchSingle(it, string);
|
||||||
|
if (index != -1) return -1;
|
||||||
|
}
|
||||||
|
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(string.str, (int)string.len);
|
||||||
|
if (decode.error) return -1;
|
||||||
|
return decode.advance;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_SELECTED: {
|
||||||
|
for (RE_Regex *it = regex->group.first; it; it = it->next) {
|
||||||
|
RE_Int index = RE_MatchSingle(it, string);
|
||||||
|
if (index != -1) return index;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_RANGE: {
|
||||||
|
if (string.str[0] >= regex->word_min && string.str[0] <= regex->word_max)
|
||||||
|
return 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RE_MATCH_ANY_WORD: {
|
||||||
|
if ((string.str[0] >= 'a' && string.str[0] <= 'z') || (string.str[0] >= 'A' && string.str[0] <= 'Z'))
|
||||||
|
return 1;
|
||||||
|
return -1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_ANY_DIGIT: {
|
||||||
|
if (string.str[0] >= '0' && string.str[0] <= '9')
|
||||||
|
return 1;
|
||||||
|
return -1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_ANY_WHITESPACE: {
|
||||||
|
if (string.str[0] == ' ' || string.str[0] == '\n' || string.str[0] == '\t' || string.str[0] == '\r')
|
||||||
|
return 1;
|
||||||
|
return -1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_ANY: {
|
||||||
|
if (string.str[0] != '\n') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_ZERO_OR_ONE: {
|
||||||
|
RE_Int index = RE_MatchSingle(regex->child, string);
|
||||||
|
if (index == -1) index = 0;
|
||||||
|
return index;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_WORD: {
|
||||||
|
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(string.str, (int)string.len);
|
||||||
|
if (decode.error) return -1;
|
||||||
|
if (decode.out_str == regex->word32) return decode.advance;
|
||||||
|
return -1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RE_MATCH_BACK:
|
||||||
|
case RE_MATCH_NULL: return -1;
|
||||||
|
|
||||||
|
default: RE_ASSERT(!"Invalid codepath");
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API bool RE1_AreEqual(char *regex, char *string) {
|
||||||
|
char buff[4096];
|
||||||
|
RE_Arena arena = RE_ArenaFromBuffer(buff, sizeof(buff));
|
||||||
|
RE_Regex *re = RE1_Parse(&arena, regex);
|
||||||
|
bool result = RE3_AreEqual(re, string, RE_StringLength(string));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API bool RE2_AreEqual(RE_Regex *regex, char *string) {
|
||||||
|
return RE3_AreEqual(regex, string, RE_StringLength(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API bool RE3_AreEqual(RE_Regex *regex, char *string, RE_Int len) {
|
||||||
|
RE_Int result = RE3_MatchFront(regex, string, len, string);
|
||||||
|
return result == len ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API RE_Match RE1_Find(char *regex, char *string) {
|
||||||
|
char buff[4096];
|
||||||
|
RE_Arena arena = RE_ArenaFromBuffer(buff, sizeof(buff));
|
||||||
|
RE_Regex *re = RE1_Parse(&arena, regex);
|
||||||
|
RE_Match result = RE2_Find(re, string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API RE_Match RE2_Find(RE_Regex *regex, char *string) {
|
||||||
|
return RE3_Find(regex, string, RE_StringLength(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API RE_Match RE3_Find(RE_Regex *regex, char *string, RE_Int len) {
|
||||||
|
RE_Match result;
|
||||||
|
for (RE_Int i = 0; i < len; i += 1) {
|
||||||
|
result.size = RE3_MatchFront(regex, string + i, len - i, string);
|
||||||
|
if (result.size != -1) {
|
||||||
|
result.pos = i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.size = 0;
|
||||||
|
result.pos = -1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API RE_Match RE2_FindAgain(RE_Regex *regex, char *string, RE_Match prev_match) {
|
||||||
|
return RE2_Find(regex, string + prev_match.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API RE_Match RE3_FindAgain(RE_Regex *regex, char *string, RE_Int len, RE_Match prev_match) {
|
||||||
|
return RE3_Find(regex, string + prev_match.pos, len - prev_match.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API RE_Int RE3_MatchFront(RE_Regex *regex, char *string, RE_Int len, char *string_front) {
|
||||||
|
RE_String re_string;
|
||||||
|
re_string.str = string;
|
||||||
|
re_string.len = len;
|
||||||
|
RE_Int submatch_len = 0;
|
||||||
|
for (RE_Regex *it = regex; it; it = it->next) {
|
||||||
|
if (it->kind == RE_MATCH_FRONT) {
|
||||||
|
if (re_string.str == string_front)
|
||||||
|
continue;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (it->kind == RE_MATCH_BACK) {
|
||||||
|
if (re_string.len == 0)
|
||||||
|
continue;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_Int index = RE_MatchSingle(it, re_string);
|
||||||
|
if (index == -1) return -1;
|
||||||
|
re_string = RE_Skip(re_string, index);
|
||||||
|
submatch_len += index;
|
||||||
|
}
|
||||||
|
return submatch_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API RE_Regex *RE1_Parse(RE_Arena *arena, char *string) {
|
||||||
|
return RE2_Parse(arena, string, RE_StringLength(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API RE_Regex *RE2_Parse(RE_Arena *arena, char *string, RE_Int len) {
|
||||||
|
RE_Parser P;
|
||||||
|
RE__MemoryZero(&P, sizeof(P));
|
||||||
|
P.string.str = string;
|
||||||
|
P.string.len = len;
|
||||||
|
|
||||||
|
for (; P.i < P.string.len;) {
|
||||||
|
RE_Regex *regex = RE_ParseSingle(&P, arena, &P.first, &P.last);
|
||||||
|
RE_DLL_QUEUE_ADD(P.first, P.last, regex);
|
||||||
|
if (regex->kind == RE_MATCH_NULL) {
|
||||||
|
P.first = &RE_NullRegex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return P.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API RE_Regex *RE1_ParseBuff(char *buff, RE_Int buffsize, char *string) {
|
||||||
|
RE_Arena arena = RE_ArenaFromBuffer(buff, buffsize);
|
||||||
|
RE_Regex *result = RE1_Parse(&arena, string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_API RE_Regex *RE2_ParseBuff(char *buff, RE_Int buffsize, char *string, RE_Int len) {
|
||||||
|
RE_Arena arena = RE_ArenaFromBuffer(buff, buffsize);
|
||||||
|
RE_Regex *result = RE2_Parse(&arena, string, len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
21
src/core/stack.h
Executable file
21
src/core/stack.h
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
#define Stack_Lengthof(x) ((int)((sizeof(x) / sizeof((x)[0]))))
|
||||||
|
#define Stack(T, size) \
|
||||||
|
struct { \
|
||||||
|
int len; \
|
||||||
|
T data[size]; \
|
||||||
|
}
|
||||||
|
#define S_Add(s, val) ( \
|
||||||
|
((s).len + 1 > Stack_Lengthof((s).data) && IO_Assert("Reached stack capacity")), \
|
||||||
|
(s).data[(s).len++] = (val))
|
||||||
|
#define S_Alloc(s) ( \
|
||||||
|
((s).len + 1 > Stack_Lengthof((s).data) && IO_Assert("Reached stack capacity")), \
|
||||||
|
((s).data + (s).len++))
|
||||||
|
|
||||||
|
#define S_Pop(s) ( \
|
||||||
|
((s).len <= 0 && IO_Assert("Trying to pop stack of size 0")), \
|
||||||
|
(s).data[--(s).len])
|
||||||
|
#define S_Get(s, i) ( \
|
||||||
|
((i) >= (s).len && IO_Assert("Trying to index stack out of bounds")), \
|
||||||
|
(s).data + (i))
|
||||||
|
#define S_GetLast(s) S_Get(s, (s).len - 1)
|
||||||
1931
src/core/stb_sprintf.h
Executable file
1931
src/core/stb_sprintf.h
Executable file
File diff suppressed because it is too large
Load Diff
581
src/core/string.h
Executable file
581
src/core/string.h
Executable file
@@ -0,0 +1,581 @@
|
|||||||
|
#ifndef S8_HEADER
|
||||||
|
#define S8_HEADER
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// Preprocessor Input: ALLOCATOR_TYPE
|
||||||
|
|
||||||
|
#ifndef S8_FN
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define S8_FN __attribute__((unused)) static
|
||||||
|
#else
|
||||||
|
#define S8_FN static
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef S8_API
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define S8_API extern "C"
|
||||||
|
#else
|
||||||
|
#define S8_API
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef S8_Allocator
|
||||||
|
#define S8_Allocator void
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct S8_String S8_String;
|
||||||
|
typedef struct S8_Node S8_Node;
|
||||||
|
typedef struct S8_List S8_List;
|
||||||
|
|
||||||
|
struct S8_String {
|
||||||
|
char *str;
|
||||||
|
int64_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct S8_Node {
|
||||||
|
S8_Node *next;
|
||||||
|
S8_String string;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct S8_List {
|
||||||
|
int64_t node_count;
|
||||||
|
int64_t char_count;
|
||||||
|
S8_Node *first;
|
||||||
|
S8_Node *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
S8_NO_FLAGS = 0,
|
||||||
|
S8_IGNORE_CASE = 1,
|
||||||
|
S8_SPLIT_INCLUSIVE = 4,
|
||||||
|
S8_MATCH_FIND_LAST = 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define S8_Lit(string) S8_Make((char *)string, sizeof(string) - 1)
|
||||||
|
#define S8_ConstLit(string) \
|
||||||
|
{ string, sizeof(string) - 1 }
|
||||||
|
#define S8_Expand(string) (int)(string).len, (string).str
|
||||||
|
|
||||||
|
#define S8_FORMAT(allocator, str, result) \
|
||||||
|
va_list args1; \
|
||||||
|
va_start(args1, str); \
|
||||||
|
S8_String result = S8_FormatV(allocator, str, args1); \
|
||||||
|
va_end(args1)
|
||||||
|
|
||||||
|
#define S8_For(it, x) for (S8_Node *it = (x).first; it; it = it->next)
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case);
|
||||||
|
inline bool operator==(S8_String a, S8_String b) { return S8_AreEqual(a, b, false); }
|
||||||
|
inline S8_String operator""_s(const char *str, size_t size) { return {(char *)str, (int64_t)size}; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
S8_API char CHAR_ToLowerCase(char a);
|
||||||
|
S8_API char CHAR_ToUpperCase(char a);
|
||||||
|
S8_API bool CHAR_IsWhitespace(char w);
|
||||||
|
S8_API bool CHAR_IsAlphabetic(char a);
|
||||||
|
S8_API bool CHAR_IsDigit(char a);
|
||||||
|
S8_API bool CHAR_IsAlphanumeric(char a);
|
||||||
|
S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case);
|
||||||
|
S8_API bool S8_EndsWith(S8_String a, S8_String end, unsigned ignore_case);
|
||||||
|
S8_API bool S8_StartsWith(S8_String a, S8_String start, unsigned ignore_case);
|
||||||
|
S8_API S8_String S8_Make(char *str, int64_t len);
|
||||||
|
S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string);
|
||||||
|
S8_API void S8_NormalizePath(S8_String s);
|
||||||
|
S8_API S8_String S8_Chop(S8_String string, int64_t len);
|
||||||
|
S8_API S8_String S8_Skip(S8_String string, int64_t len);
|
||||||
|
S8_API S8_String S8_GetPostfix(S8_String string, int64_t len);
|
||||||
|
S8_API S8_String S8_GetPrefix(S8_String string, int64_t len);
|
||||||
|
S8_API S8_String S8_Slice(S8_String string, int64_t first_index, int64_t one_past_last_index);
|
||||||
|
S8_API S8_String S8_Trim(S8_String string);
|
||||||
|
S8_API S8_String S8_TrimEnd(S8_String string);
|
||||||
|
S8_API S8_String S8_ToLowerCase(S8_Allocator allocator, S8_String s);
|
||||||
|
S8_API S8_String S8_ToUpperCase(S8_Allocator allocator, S8_String s);
|
||||||
|
S8_API bool S8_Find(S8_String string, S8_String find, unsigned flags, int64_t *index_out);
|
||||||
|
S8_API S8_List S8_Split(S8_Allocator allocator, S8_String string, S8_String find, unsigned flags);
|
||||||
|
S8_API S8_String S8_MergeWithSeparator(S8_Allocator allocator, S8_List list, S8_String separator);
|
||||||
|
S8_API S8_String S8_Merge(S8_Allocator allocator, S8_List list);
|
||||||
|
S8_API S8_String S8_ReplaceAll(S8_Allocator allocator, S8_String string, S8_String replace, S8_String with, unsigned flags);
|
||||||
|
S8_API S8_List S8_FindAll(S8_Allocator allocator, S8_String string, S8_String find, unsigned flags);
|
||||||
|
S8_API S8_String S8_ChopLastSlash(S8_String s);
|
||||||
|
S8_API S8_String S8_ChopLastPeriod(S8_String s);
|
||||||
|
S8_API S8_String S8_SkipToLastSlash(S8_String s);
|
||||||
|
S8_API S8_String S8_SkipToLastPeriod(S8_String s);
|
||||||
|
S8_API int64_t S8_Length(char *string);
|
||||||
|
S8_API int64_t S8_WideLength(wchar_t *string);
|
||||||
|
S8_API S8_String S8_MakeFromChar(char *string);
|
||||||
|
S8_API S8_String S8_MakeEmpty(void);
|
||||||
|
S8_API S8_List S8_MakeEmptyList(void);
|
||||||
|
S8_API S8_String S8_FormatV(S8_Allocator allocator, const char *str, va_list args1);
|
||||||
|
S8_API S8_String S8_Format(S8_Allocator allocator, const char *str, ...);
|
||||||
|
S8_API S8_Node *S8_CreateNode(S8_Allocator allocator, S8_String string);
|
||||||
|
S8_API void S8_ReplaceNodeString(S8_List *list, S8_Node *node, S8_String new_string);
|
||||||
|
S8_API void S8_AddExistingNode(S8_List *list, S8_Node *node);
|
||||||
|
S8_API void S8_AddArray(S8_Allocator allocator, S8_List *list, char **array, int count);
|
||||||
|
S8_API void S8_AddArrayWithPrefix(S8_Allocator allocator, S8_List *list, char *prefix, char **array, int count);
|
||||||
|
S8_API S8_List S8_MakeList(S8_Allocator allocator, S8_String a);
|
||||||
|
S8_API S8_List S8_CopyList(S8_Allocator allocator, S8_List a);
|
||||||
|
S8_API S8_List S8_ConcatLists(S8_Allocator allocator, S8_List a, S8_List b);
|
||||||
|
S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String string);
|
||||||
|
S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...);
|
||||||
|
|
||||||
|
#endif // S8_HEADER
|
||||||
|
#ifdef S8_IMPLEMENTATION
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#ifndef S8_VSNPRINTF
|
||||||
|
#include <stdio.h>
|
||||||
|
#define S8_VSNPRINTF vsnprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef S8_ALLOCATE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define S8_ALLOCATE(allocator, size) malloc(size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef S8_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define S8_ASSERT(x) assert(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef S8_MemoryCopy
|
||||||
|
#include <string.h>
|
||||||
|
#define S8_MemoryCopy(dst, src, s) memcpy(dst, src, s)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
S8_FN int64_t S8__ClampTop(int64_t val, int64_t max) {
|
||||||
|
if (val > max) val = max;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API char CHAR_ToLowerCase(char a) {
|
||||||
|
if (a >= 'A' && a <= 'Z') a += 32;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API char CHAR_ToUpperCase(char a) {
|
||||||
|
if (a >= 'a' && a <= 'z') a -= 32;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API bool CHAR_IsWhitespace(char w) {
|
||||||
|
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API bool CHAR_IsAlphabetic(char a) {
|
||||||
|
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API bool CHAR_IsDigit(char a) {
|
||||||
|
bool result = a >= '0' && a <= '9';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API bool CHAR_IsAlphanumeric(char a) {
|
||||||
|
bool result = CHAR_IsDigit(a) || CHAR_IsAlphabetic(a);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case) {
|
||||||
|
if (a.len != b.len) return false;
|
||||||
|
for (int64_t i = 0; i < a.len; i++) {
|
||||||
|
char A = a.str[i];
|
||||||
|
char B = b.str[i];
|
||||||
|
if (ignore_case & S8_IGNORE_CASE) {
|
||||||
|
A = CHAR_ToLowerCase(A);
|
||||||
|
B = CHAR_ToLowerCase(B);
|
||||||
|
}
|
||||||
|
if (A != B)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API bool S8_EndsWith(S8_String a, S8_String end, unsigned ignore_case) {
|
||||||
|
S8_String a_end = S8_GetPostfix(a, end.len);
|
||||||
|
bool result = S8_AreEqual(end, a_end, ignore_case);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API bool S8_StartsWith(S8_String a, S8_String start, unsigned ignore_case) {
|
||||||
|
S8_String a_start = S8_GetPrefix(a, start.len);
|
||||||
|
bool result = S8_AreEqual(start, a_start, ignore_case);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_Make(char *str, int64_t len) {
|
||||||
|
S8_String result;
|
||||||
|
result.str = (char *)str;
|
||||||
|
result.len = len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string) {
|
||||||
|
char *copy = (char *)S8_ALLOCATE(allocator, sizeof(char) * (string.len + 1));
|
||||||
|
S8_MemoryCopy(copy, string.str, string.len);
|
||||||
|
copy[string.len] = 0;
|
||||||
|
S8_String result = S8_Make(copy, string.len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API void S8_NormalizePath(S8_String s) {
|
||||||
|
for (int64_t i = 0; i < s.len; i++) {
|
||||||
|
if (s.str[i] == '\\')
|
||||||
|
s.str[i] = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_Chop(S8_String string, int64_t len) {
|
||||||
|
len = S8__ClampTop(len, string.len);
|
||||||
|
S8_String result = S8_Make(string.str, string.len - len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_Skip(S8_String string, int64_t len) {
|
||||||
|
len = S8__ClampTop(len, string.len);
|
||||||
|
int64_t remain = string.len - len;
|
||||||
|
S8_String result = S8_Make(string.str + len, remain);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_GetPostfix(S8_String string, int64_t len) {
|
||||||
|
len = S8__ClampTop(len, string.len);
|
||||||
|
int64_t remain_len = string.len - len;
|
||||||
|
S8_String result = S8_Make(string.str + remain_len, len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_GetPrefix(S8_String string, int64_t len) {
|
||||||
|
len = S8__ClampTop(len, string.len);
|
||||||
|
S8_String result = S8_Make(string.str, len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_Slice(S8_String string, int64_t first_index, int64_t one_past_last_index) {
|
||||||
|
if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1;
|
||||||
|
if (first_index < 0) first_index = string.len + first_index;
|
||||||
|
S8_ASSERT(first_index < one_past_last_index && "S8_Slice, first_index is bigger then one_past_last_index");
|
||||||
|
S8_ASSERT(string.len > 0 && "Slicing string of length 0! Might be an error!");
|
||||||
|
S8_String result = string;
|
||||||
|
if (string.len > 0) {
|
||||||
|
if (one_past_last_index > first_index) {
|
||||||
|
first_index = S8__ClampTop(first_index, string.len - 1);
|
||||||
|
one_past_last_index = S8__ClampTop(one_past_last_index, string.len);
|
||||||
|
result.str += first_index;
|
||||||
|
result.len = one_past_last_index - first_index;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_Trim(S8_String string) {
|
||||||
|
if (string.len == 0)
|
||||||
|
return string;
|
||||||
|
|
||||||
|
int64_t whitespace_begin = 0;
|
||||||
|
for (; whitespace_begin < string.len; whitespace_begin++) {
|
||||||
|
if (!CHAR_IsWhitespace(string.str[whitespace_begin])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t whitespace_end = string.len;
|
||||||
|
for (; whitespace_end != whitespace_begin; whitespace_end--) {
|
||||||
|
if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (whitespace_begin == whitespace_end) {
|
||||||
|
string.len = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
string = S8_Slice(string, whitespace_begin, whitespace_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_TrimEnd(S8_String string) {
|
||||||
|
int64_t whitespace_end = string.len;
|
||||||
|
for (; whitespace_end != 0; whitespace_end--) {
|
||||||
|
if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String result = S8_GetPrefix(string, whitespace_end);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_ToLowerCase(S8_Allocator allocator, S8_String s) {
|
||||||
|
S8_String copy = S8_Copy(allocator, s);
|
||||||
|
for (int64_t i = 0; i < copy.len; i++) {
|
||||||
|
copy.str[i] = CHAR_ToLowerCase(copy.str[i]);
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_ToUpperCase(S8_Allocator allocator, S8_String s) {
|
||||||
|
S8_String copy = S8_Copy(allocator, s);
|
||||||
|
for (int64_t i = 0; i < copy.len; i++) {
|
||||||
|
copy.str[i] = CHAR_ToUpperCase(copy.str[i]);
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API bool S8_Find(S8_String string, S8_String find, unsigned flags, int64_t *index_out) {
|
||||||
|
bool result = false;
|
||||||
|
if (flags & S8_MATCH_FIND_LAST) {
|
||||||
|
for (int64_t i = string.len; i != 0; i--) {
|
||||||
|
int64_t index = i - 1;
|
||||||
|
S8_String substring = S8_Slice(string, index, index + find.len);
|
||||||
|
if (S8_AreEqual(substring, find, flags)) {
|
||||||
|
if (index_out)
|
||||||
|
*index_out = index;
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int64_t i = 0; i < string.len; i++) {
|
||||||
|
S8_String substring = S8_Slice(string, i, i + find.len);
|
||||||
|
if (S8_AreEqual(substring, find, flags)) {
|
||||||
|
if (index_out)
|
||||||
|
*index_out = i;
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_List S8_Split(S8_Allocator allocator, S8_String string, S8_String find, unsigned flags) {
|
||||||
|
S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||||
|
S8_List result = S8_MakeEmptyList();
|
||||||
|
int64_t index = 0;
|
||||||
|
while (S8_Find(string, find, flags, &index)) {
|
||||||
|
S8_String before_match = S8_Make(string.str, index);
|
||||||
|
S8_AddNode(allocator, &result, before_match);
|
||||||
|
if (flags & S8_SPLIT_INCLUSIVE) {
|
||||||
|
S8_String match = S8_Make(string.str + index, find.len);
|
||||||
|
S8_AddNode(allocator, &result, match);
|
||||||
|
}
|
||||||
|
string = S8_Skip(string, index + find.len);
|
||||||
|
}
|
||||||
|
S8_AddNode(allocator, &result, string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_MergeWithSeparator(S8_Allocator allocator, S8_List list, S8_String separator) {
|
||||||
|
if (list.node_count == 0) return S8_MakeEmpty();
|
||||||
|
if (list.char_count == 0) return S8_MakeEmpty();
|
||||||
|
// S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||||
|
int64_t base_size = (list.char_count + 1);
|
||||||
|
int64_t sep_size = (list.node_count - 1) * separator.len;
|
||||||
|
int64_t size = base_size + sep_size;
|
||||||
|
char *buff = (char *)S8_ALLOCATE(allocator, sizeof(char) * size);
|
||||||
|
S8_String string = S8_Make(buff, 0);
|
||||||
|
for (S8_Node *it = list.first; it; it = it->next) {
|
||||||
|
S8_ASSERT(string.len + it->string.len <= size);
|
||||||
|
S8_MemoryCopy(string.str + string.len, it->string.str, it->string.len);
|
||||||
|
string.len += it->string.len;
|
||||||
|
if (it != list.last) {
|
||||||
|
S8_MemoryCopy(string.str + string.len, separator.str, separator.len);
|
||||||
|
string.len += separator.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
S8_ASSERT(string.len == size - 1);
|
||||||
|
string.str[size] = 0;
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_Merge(S8_Allocator allocator, S8_List list) {
|
||||||
|
return S8_MergeWithSeparator(allocator, list, S8_Lit(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_ReplaceAll(S8_Allocator allocator, S8_String string, S8_String replace, S8_String with, unsigned flags) {
|
||||||
|
S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||||
|
S8_List list = S8_Split(allocator, string, replace, flags | S8_SPLIT_INCLUSIVE);
|
||||||
|
for (S8_Node *it = list.first; it; it = it->next) {
|
||||||
|
if (S8_AreEqual(it->string, replace, flags)) {
|
||||||
|
S8_ReplaceNodeString(&list, it, with);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
S8_String result = S8_Merge(allocator, list);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_List S8_FindAll(S8_Allocator allocator, S8_String string, S8_String find, unsigned flags) { // @untested
|
||||||
|
S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||||
|
S8_List result = S8_MakeEmptyList();
|
||||||
|
int64_t index = 0;
|
||||||
|
while (S8_Find(string, find, flags, &index)) {
|
||||||
|
S8_String match = S8_Make(string.str + index, find.len);
|
||||||
|
S8_AddNode(allocator, &result, match);
|
||||||
|
string = S8_Skip(string, index + find.len);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_ChopLastSlash(S8_String s) {
|
||||||
|
S8_String result = s;
|
||||||
|
S8_Find(s, S8_Lit("/"), S8_MATCH_FIND_LAST, &result.len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_ChopLastPeriod(S8_String s) {
|
||||||
|
S8_String result = s;
|
||||||
|
S8_Find(s, S8_Lit("."), S8_MATCH_FIND_LAST, &result.len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_SkipToLastSlash(S8_String s) {
|
||||||
|
int64_t pos;
|
||||||
|
S8_String result = s;
|
||||||
|
if (S8_Find(s, S8_Lit("/"), S8_MATCH_FIND_LAST, &pos)) {
|
||||||
|
result = S8_Skip(result, pos + 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_SkipToLastPeriod(S8_String s) {
|
||||||
|
int64_t pos;
|
||||||
|
S8_String result = s;
|
||||||
|
if (S8_Find(s, S8_Lit("."), S8_MATCH_FIND_LAST, &pos)) {
|
||||||
|
result = S8_Skip(result, pos + 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API int64_t S8_Length(char *string) {
|
||||||
|
int64_t len = 0;
|
||||||
|
while (*string++ != 0)
|
||||||
|
len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API int64_t S8_WideLength(wchar_t *string) {
|
||||||
|
int64_t len = 0;
|
||||||
|
while (*string++ != 0)
|
||||||
|
len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_MakeFromChar(char *string) {
|
||||||
|
S8_String result;
|
||||||
|
result.str = (char *)string;
|
||||||
|
result.len = S8_Length(string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_MakeEmpty(void) {
|
||||||
|
return S8_Make(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_List S8_MakeEmptyList(void) {
|
||||||
|
S8_List result;
|
||||||
|
result.first = 0;
|
||||||
|
result.last = 0;
|
||||||
|
result.char_count = 0;
|
||||||
|
result.node_count = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_FormatV(S8_Allocator allocator, const char *str, va_list args1) {
|
||||||
|
va_list args2;
|
||||||
|
va_copy(args2, args1);
|
||||||
|
int64_t len = S8_VSNPRINTF(0, 0, str, args2);
|
||||||
|
va_end(args2);
|
||||||
|
|
||||||
|
char *result = (char *)S8_ALLOCATE(allocator, sizeof(char) * (len + 1));
|
||||||
|
S8_VSNPRINTF(result, (int)(len + 1), str, args1);
|
||||||
|
S8_String res = S8_Make(result, len);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_Format(S8_Allocator allocator, const char *str, ...) {
|
||||||
|
S8_FORMAT(allocator, str, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_Node *S8_CreateNode(S8_Allocator allocator, S8_String string) {
|
||||||
|
S8_Node *result = (S8_Node *)S8_ALLOCATE(allocator, sizeof(S8_Node));
|
||||||
|
result->string = string;
|
||||||
|
result->next = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API void S8_ReplaceNodeString(S8_List *list, S8_Node *node, S8_String new_string) {
|
||||||
|
list->char_count -= node->string.len;
|
||||||
|
list->char_count += new_string.len;
|
||||||
|
node->string = new_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API void S8_AddExistingNode(S8_List *list, S8_Node *node) {
|
||||||
|
if (list->first) {
|
||||||
|
list->last->next = node;
|
||||||
|
list->last = list->last->next;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
list->first = list->last = node;
|
||||||
|
}
|
||||||
|
list->node_count += 1;
|
||||||
|
list->char_count += node->string.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API void S8_AddArray(S8_Allocator allocator, S8_List *list, char **array, int count) {
|
||||||
|
for (int i = 0; i < count; i += 1) {
|
||||||
|
S8_String s = S8_MakeFromChar(array[i]);
|
||||||
|
S8_AddNode(allocator, list, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API void S8_AddArrayWithPrefix(S8_Allocator allocator, S8_List *list, char *prefix, char **array, int count) {
|
||||||
|
for (int i = 0; i < count; i += 1) {
|
||||||
|
S8_AddF(allocator, list, "%s%s", prefix, array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_List S8_MakeList(S8_Allocator allocator, S8_String a) {
|
||||||
|
S8_List result = S8_MakeEmptyList();
|
||||||
|
S8_AddNode(allocator, &result, a);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_List S8_CopyList(S8_Allocator allocator, S8_List a) {
|
||||||
|
S8_List result = S8_MakeEmptyList();
|
||||||
|
for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_List S8_ConcatLists(S8_Allocator allocator, S8_List a, S8_List b) {
|
||||||
|
S8_List result = S8_MakeEmptyList();
|
||||||
|
for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
|
||||||
|
for (S8_Node *it = b.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String string) {
|
||||||
|
S8_Node *node = S8_CreateNode(allocator, string);
|
||||||
|
S8_AddExistingNode(list, node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...) {
|
||||||
|
S8_FORMAT(allocator, str, result);
|
||||||
|
S8_AddNode(allocator, list, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // S8_IMPLEMENTATION
|
||||||
209
src/core/table.hpp
Executable file
209
src/core/table.hpp
Executable file
@@ -0,0 +1,209 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
/*
|
||||||
|
Hash table implementation:
|
||||||
|
Pointers to values
|
||||||
|
Open adressing
|
||||||
|
Linear Probing
|
||||||
|
Power of 2
|
||||||
|
Robin Hood hashing
|
||||||
|
Resizes on high probe count (min max load factor)
|
||||||
|
|
||||||
|
Hash 0 is reserved for empty hash table entry
|
||||||
|
*/
|
||||||
|
|
||||||
|
// #define TABLE_ALLOCATOR_TYPE Allocator
|
||||||
|
|
||||||
|
#ifndef TABLE_PRIVATE_FUNCTION
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define TABLE_PRIVATE_FUNCTION __attribute__((unused)) static
|
||||||
|
#else
|
||||||
|
#define TABLE_PRIVATE_FUNCTION static
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TABLE_ALLOCATE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define TABLE_ALLOCATE(allocator, size) malloc(size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TABLE_DEALLOCATE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define TABLE_DEALLOCATE(allocator, p) free(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TABLE_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define TABLE_ASSERT(x) assert(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TABLE_SET_DEFAULT_ALLOCATOR
|
||||||
|
#define TABLE_SET_DEFAULT_ALLOCATOR
|
||||||
|
// Example:
|
||||||
|
// #define TABLE_SET_DEFAULT_ALLOCATOR if (!allocator) allocator = global_heap;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TABLE_HASH_BYTES
|
||||||
|
#define TABLE_HASH_BYTES TABLE__HashBytes
|
||||||
|
TABLE_PRIVATE_FUNCTION uint64_t TABLE__HashBytes(void *data, unsigned size) {
|
||||||
|
uint8_t *data8 = (uint8_t *)data;
|
||||||
|
uint64_t hash = (uint64_t)14695981039346656037ULL;
|
||||||
|
for (unsigned i = 0; i < size; i++) {
|
||||||
|
hash = hash ^ (uint64_t)(data8[i]);
|
||||||
|
hash = hash * (uint64_t)1099511628211ULL;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TABLE__WRAP_AROUND_POWER_OF_2(x, pow2) (((x) & ((pow2)-1llu)))
|
||||||
|
|
||||||
|
template <class Value>
|
||||||
|
struct Table {
|
||||||
|
struct Entry {
|
||||||
|
uint64_t hash;
|
||||||
|
uint64_t key;
|
||||||
|
size_t distance;
|
||||||
|
Value value;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool is_empty(Entry *entry) { return entry->hash == 0; }
|
||||||
|
inline bool is_occupied(Entry *entry) { return entry->hash != 0; }
|
||||||
|
|
||||||
|
void reserve(size_t size) {
|
||||||
|
TABLE_ASSERT(size > cap && "New size is smaller then original size");
|
||||||
|
TABLE_SET_DEFAULT_ALLOCATOR;
|
||||||
|
|
||||||
|
Entry *old_values = values;
|
||||||
|
size_t old_cap = cap;
|
||||||
|
|
||||||
|
values = (Entry *)TABLE_ALLOCATE(allocator, sizeof(Entry) * size);
|
||||||
|
cap = size;
|
||||||
|
|
||||||
|
TABLE_ASSERT(!(old_values == 0 && len != 0));
|
||||||
|
if (len == 0) {
|
||||||
|
if (old_values) TABLE_DEALLOCATE(allocator, old_values);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
for (size_t i = 0; i < old_cap; i += 1) {
|
||||||
|
Entry *it = old_values + i;
|
||||||
|
if (is_occupied(it)) {
|
||||||
|
insert(it->key, it->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TABLE_DEALLOCATE(allocator, old_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry *get_table_entry(uint64_t key) {
|
||||||
|
uint64_t hash = TABLE_HASH_BYTES(&key, sizeof(key));
|
||||||
|
if (hash == 0) hash += 1;
|
||||||
|
uint64_t index = TABLE__WRAP_AROUND_POWER_OF_2(hash, cap);
|
||||||
|
uint64_t i = index;
|
||||||
|
uint64_t distance = 0;
|
||||||
|
for (;;) {
|
||||||
|
Entry *it = values + i;
|
||||||
|
if (distance > it->distance) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->hash == hash && it->key == key) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
distance += 1;
|
||||||
|
i = TABLE__WRAP_AROUND_POWER_OF_2(i + 1, cap);
|
||||||
|
if (i == index) return 0;
|
||||||
|
}
|
||||||
|
TABLE_ASSERT(!"Invalid codepath");
|
||||||
|
}
|
||||||
|
|
||||||
|
// load factor calculation was rearranged
|
||||||
|
// to get rid of division:
|
||||||
|
//> 100 * len / cap = load_factor
|
||||||
|
//> len * 100 = load_factor * cap
|
||||||
|
inline bool reached_load_factor(size_t lfactor) {
|
||||||
|
return (len + 1) * 100 >= lfactor * cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(uint64_t key, const Value &value) {
|
||||||
|
if (reached_load_factor(max_load_factor)) {
|
||||||
|
if (cap == 0) cap = 16; // 32 cause cap*2
|
||||||
|
reserve(cap * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t hash = TABLE_HASH_BYTES(&key, sizeof(key));
|
||||||
|
if (hash == 0) hash += 1;
|
||||||
|
uint64_t index = TABLE__WRAP_AROUND_POWER_OF_2(hash, cap);
|
||||||
|
uint64_t i = index;
|
||||||
|
Entry to_insert = {hash, key, 0, value};
|
||||||
|
for (;;) {
|
||||||
|
Entry *it = values + i;
|
||||||
|
if (is_empty(it)) {
|
||||||
|
*it = to_insert;
|
||||||
|
len += 1;
|
||||||
|
// If we have more then 8 consecutive items we try to resize
|
||||||
|
if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) {
|
||||||
|
reserve(cap * 2);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (it->hash == hash && it->key == key) {
|
||||||
|
*it = to_insert;
|
||||||
|
// If we have more then 8 consecutive items we try to resize
|
||||||
|
if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) {
|
||||||
|
reserve(cap * 2);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Robin hood hashing
|
||||||
|
if (to_insert.distance > it->distance) {
|
||||||
|
Entry temp = to_insert;
|
||||||
|
to_insert = *it;
|
||||||
|
*it = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_insert.distance += 1;
|
||||||
|
i = TABLE__WRAP_AROUND_POWER_OF_2(i + 1, cap);
|
||||||
|
TABLE_ASSERT(i != index && "Did a full 360 through a hash table, no good :( that shouldnt be possible");
|
||||||
|
}
|
||||||
|
TABLE_ASSERT(!"Invalid codepath");
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(uint64_t key) {
|
||||||
|
Entry *entry = get_table_entry(key);
|
||||||
|
entry->hash = 0;
|
||||||
|
entry->distance = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *get(uint64_t key) {
|
||||||
|
Entry *v = get_table_entry(key);
|
||||||
|
if (!v) return 0;
|
||||||
|
return &v->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
len = 0;
|
||||||
|
for (size_t i = 0; i < cap; i += 1) {
|
||||||
|
Entry *it = values + i;
|
||||||
|
it->hash = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dealloc() {
|
||||||
|
COR_DEALLOCATE(allocator, values);
|
||||||
|
len = 0;
|
||||||
|
cap = 0;
|
||||||
|
values = 0;
|
||||||
|
}
|
||||||
|
static const size_t max_load_factor = 80;
|
||||||
|
static const size_t min_load_factor = 50;
|
||||||
|
static const size_t significant_distance = 8;
|
||||||
|
|
||||||
|
size_t len, cap;
|
||||||
|
Entry *values;
|
||||||
|
#ifdef TABLE_ALLOCATOR_TYPE
|
||||||
|
TABLE_ALLOCATOR_TYPE *allocator;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
278
src/core/unicode.h
Executable file
278
src/core/unicode.h
Executable file
@@ -0,0 +1,278 @@
|
|||||||
|
#ifndef UTF_HEADER
|
||||||
|
#define UTF_HEADER
|
||||||
|
#include <stdint.h>
|
||||||
|
typedef struct UTF32_Result UTF32_Result;
|
||||||
|
typedef struct UTF8_Result UTF8_Result;
|
||||||
|
typedef struct UTF16_Result UTF16_Result;
|
||||||
|
typedef struct UTF8_Iter UTF8_Iter;
|
||||||
|
|
||||||
|
#ifndef UTF_FN
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define UTF_FN __attribute__((unused)) static
|
||||||
|
#else
|
||||||
|
#define UTF_FN static
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef UTF_API
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define UTF_API extern "C"
|
||||||
|
#else
|
||||||
|
#define UTF_API
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct UTF32_Result {
|
||||||
|
uint32_t out_str;
|
||||||
|
int advance;
|
||||||
|
int error;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UTF8_Result {
|
||||||
|
uint8_t out_str[4];
|
||||||
|
int len;
|
||||||
|
int error;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UTF16_Result {
|
||||||
|
uint16_t out_str[2];
|
||||||
|
int len;
|
||||||
|
int error;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UTF8_Iter {
|
||||||
|
char *str;
|
||||||
|
int len;
|
||||||
|
int utf8_codepoint_byte_size;
|
||||||
|
int i;
|
||||||
|
uint32_t item;
|
||||||
|
};
|
||||||
|
|
||||||
|
UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance);
|
||||||
|
UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint);
|
||||||
|
UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance);
|
||||||
|
UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint);
|
||||||
|
UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen);
|
||||||
|
UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen);
|
||||||
|
UTF_API void UTF8_Advance(UTF8_Iter *iter);
|
||||||
|
UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len);
|
||||||
|
UTF_API UTF8_Iter UTF8_Iterate(char *str);
|
||||||
|
|
||||||
|
#endif // UTF_HEADER
|
||||||
|
#ifdef UTF_IMPLEMENTATION
|
||||||
|
|
||||||
|
UTF_FN int UTF__StringLength(char *string) {
|
||||||
|
int len = 0;
|
||||||
|
while (*string++ != 0)
|
||||||
|
len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF_FN void UTF__MemoryZero(void *p, size_t size) {
|
||||||
|
uint8_t *p8 = (uint8_t *)p;
|
||||||
|
while (size--) *p8++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance) {
|
||||||
|
UTF32_Result result;
|
||||||
|
UTF__MemoryZero(&result, sizeof(result));
|
||||||
|
if (max_advance >= 1) {
|
||||||
|
result.advance = 1;
|
||||||
|
result.out_str = c[0];
|
||||||
|
if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) {
|
||||||
|
if (max_advance >= 2) {
|
||||||
|
result.out_str = 0x10000;
|
||||||
|
result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF);
|
||||||
|
result.advance = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result.error = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.error = 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint) {
|
||||||
|
UTF8_Result result;
|
||||||
|
UTF__MemoryZero(&result, sizeof(result));
|
||||||
|
|
||||||
|
if (codepoint <= 0x7F) {
|
||||||
|
result.len = 1;
|
||||||
|
result.out_str[0] = (char)codepoint;
|
||||||
|
}
|
||||||
|
else if (codepoint <= 0x7FF) {
|
||||||
|
result.len = 2;
|
||||||
|
result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6));
|
||||||
|
result.out_str[1] = 0x80 | (0x3f & codepoint);
|
||||||
|
}
|
||||||
|
else if (codepoint <= 0xFFFF) { // 16 bit word
|
||||||
|
result.len = 3;
|
||||||
|
result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits
|
||||||
|
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||||
|
result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||||
|
}
|
||||||
|
else if (codepoint <= 0x10FFFF) { // 21 bit word
|
||||||
|
result.len = 4;
|
||||||
|
result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits
|
||||||
|
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits
|
||||||
|
result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||||
|
result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance) {
|
||||||
|
UTF32_Result result;
|
||||||
|
UTF__MemoryZero(&result, sizeof(result));
|
||||||
|
|
||||||
|
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
|
||||||
|
if (max_advance >= 1) {
|
||||||
|
result.out_str = c[0];
|
||||||
|
result.advance = 1;
|
||||||
|
}
|
||||||
|
else result.error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((c[0] & 0xe0) == 0xc0) {
|
||||||
|
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
|
||||||
|
if (max_advance >= 2) {
|
||||||
|
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
|
||||||
|
result.advance = 2;
|
||||||
|
}
|
||||||
|
else result.error = 2;
|
||||||
|
}
|
||||||
|
else result.error = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((c[0] & 0xf0) == 0xe0) {
|
||||||
|
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||||
|
if (max_advance >= 3) {
|
||||||
|
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
|
||||||
|
result.advance = 3;
|
||||||
|
}
|
||||||
|
else result.error = 3;
|
||||||
|
}
|
||||||
|
else result.error = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((c[0] & 0xf8) == 0xf0) {
|
||||||
|
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
|
||||||
|
if (max_advance >= 4) {
|
||||||
|
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
|
||||||
|
result.advance = 4;
|
||||||
|
}
|
||||||
|
else result.error = 4;
|
||||||
|
}
|
||||||
|
else result.error = 4;
|
||||||
|
}
|
||||||
|
else result.error = 4;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint) {
|
||||||
|
UTF16_Result result;
|
||||||
|
UTF__MemoryZero(&result, sizeof(result));
|
||||||
|
if (codepoint < 0x10000) {
|
||||||
|
result.out_str[0] = (uint16_t)codepoint;
|
||||||
|
result.out_str[1] = 0;
|
||||||
|
result.len = 1;
|
||||||
|
}
|
||||||
|
else if (codepoint <= 0x10FFFF) {
|
||||||
|
uint32_t code = (codepoint - 0x10000);
|
||||||
|
result.out_str[0] = (uint16_t)(0xD800 | (code >> 10));
|
||||||
|
result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF));
|
||||||
|
result.len = 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define UTF__HANDLE_DECODE_ERROR(question_mark) \
|
||||||
|
{ \
|
||||||
|
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) {
|
||||||
|
int64_t outlen = 0;
|
||||||
|
for (int64_t i = 0; i < inlen && in[i];) {
|
||||||
|
UTF32_Result decode = UTF_ConvertUTF16ToUTF32((uint16_t *)(in + i), (int)(inlen - i));
|
||||||
|
if (!decode.error) {
|
||||||
|
i += decode.advance;
|
||||||
|
UTF8_Result encode = UTF_ConvertUTF32ToUTF8(decode.out_str);
|
||||||
|
if (!encode.error) {
|
||||||
|
for (int64_t j = 0; j < encode.len; j++) {
|
||||||
|
if (outlen < buffer_size - 1) {
|
||||||
|
buffer[outlen++] = encode.out_str[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else UTF__HANDLE_DECODE_ERROR('?');
|
||||||
|
}
|
||||||
|
else UTF__HANDLE_DECODE_ERROR('?');
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[outlen] = 0;
|
||||||
|
return outlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
|
||||||
|
int64_t outlen = 0;
|
||||||
|
for (int64_t i = 0; i < inlen;) {
|
||||||
|
UTF32_Result decode = UTF_ConvertUTF8ToUTF32(in + i, (int)(inlen - i));
|
||||||
|
if (!decode.error) {
|
||||||
|
i += decode.advance;
|
||||||
|
UTF16_Result encode = UTF_ConvertUTF32ToUTF16(decode.out_str);
|
||||||
|
if (!encode.error) {
|
||||||
|
for (int64_t j = 0; j < encode.len; j++) {
|
||||||
|
if (outlen < buffer_size - 1) {
|
||||||
|
buffer[outlen++] = encode.out_str[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else UTF__HANDLE_DECODE_ERROR(0x003f);
|
||||||
|
}
|
||||||
|
else UTF__HANDLE_DECODE_ERROR(0x003f);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[outlen] = 0;
|
||||||
|
return outlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF_API void UTF8_Advance(UTF8_Iter *iter) {
|
||||||
|
iter->i += iter->utf8_codepoint_byte_size;
|
||||||
|
UTF32_Result r = UTF_ConvertUTF8ToUTF32(iter->str + iter->i, iter->len - iter->i);
|
||||||
|
if (r.error) {
|
||||||
|
iter->item = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter->utf8_codepoint_byte_size = r.advance;
|
||||||
|
iter->item = r.out_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len) {
|
||||||
|
UTF8_Iter result;
|
||||||
|
UTF__MemoryZero(&result, sizeof(result));
|
||||||
|
result.str = str;
|
||||||
|
result.len = len;
|
||||||
|
if (len) UTF8_Advance(&result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF_API UTF8_Iter UTF8_Iterate(char *str) {
|
||||||
|
return UTF8_IterateEx(str, UTF__StringLength(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UTF_IMPLEMENTATION
|
||||||
711
src/core/vmath.hpp
Executable file
711
src/core/vmath.hpp
Executable file
@@ -0,0 +1,711 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
constexpr float PI32 = 3.14159265359f;
|
||||||
|
constexpr float DEG2RAD = (PI32 / 180.f); // @Usage: degree * DEG2RAD = radians;
|
||||||
|
constexpr float RAD2DEG = (180.f / PI32);
|
||||||
|
|
||||||
|
union Vec2 {
|
||||||
|
struct {
|
||||||
|
float x, y;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
float w, h;
|
||||||
|
};
|
||||||
|
float e[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Vec3 {
|
||||||
|
struct {
|
||||||
|
float x, y, z;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
float r, g, b;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
float h, s, l;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Vec2 xy;
|
||||||
|
float floor;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
float _x1;
|
||||||
|
Vec2 yz;
|
||||||
|
};
|
||||||
|
float e[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Vec4 {
|
||||||
|
struct {
|
||||||
|
float x, y, z, w;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
float r, g, b, a;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
float h, s, l, a;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Vec3 rgb;
|
||||||
|
float _i1;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Vec3 xyz;
|
||||||
|
float _i2;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Vec2 xy;
|
||||||
|
Vec2 zw;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
float e[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Vec2I {
|
||||||
|
struct {
|
||||||
|
int x, y;
|
||||||
|
};
|
||||||
|
int e[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Vec3I {
|
||||||
|
struct {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int z;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
int r, g, b;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
int z_[2];
|
||||||
|
int floor;
|
||||||
|
};
|
||||||
|
int e[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Vec4I {
|
||||||
|
struct {
|
||||||
|
int x, y, z, w;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
int r, g, b, a;
|
||||||
|
};
|
||||||
|
int e[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rect2 {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
float x0;
|
||||||
|
float y0;
|
||||||
|
float x1;
|
||||||
|
float y1;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Vec2 min;
|
||||||
|
Vec2 max;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rect3 {
|
||||||
|
Vec3 min;
|
||||||
|
Vec3 max;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Right handed coordinate system +
|
||||||
|
struct M4x4 {
|
||||||
|
// Matrices are stored ROW MAJOR (But they follow notation and behaviour of column major math notation !)
|
||||||
|
// Matrix.e[row][column]
|
||||||
|
// Multiplication follows right to left = TRANSLATION * ROTATION * SCALE * V
|
||||||
|
// When sending to OpenGL need to transpose to column major !!!
|
||||||
|
//
|
||||||
|
// We want matrices to look and act exactly like in a textbook.
|
||||||
|
// When we store row major the C notation mimicks the textbook.
|
||||||
|
float e[4][4];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define COLOR_WHITE \
|
||||||
|
Vec4 { 1.f, 1.f, 1.f, 1.f }
|
||||||
|
#define COLOR_BLACK \
|
||||||
|
Vec4 { 0.f, 0.f, 0.f, 1.f }
|
||||||
|
#define COLOR_BLACK_NO_ALPHA \
|
||||||
|
Vec4 { 0.f, 0.f, 0.f, 0.f }
|
||||||
|
#define COLOR_GREY \
|
||||||
|
Vec4 { 0.5f, 0.5f, 0.5f, 1.f }
|
||||||
|
#define COLOR_RED \
|
||||||
|
Vec4 { 1.0f, 0.f, 0.f, 1.f }
|
||||||
|
#define COLOR_GREEN \
|
||||||
|
Vec4 { 0.0f, 1.f, 0.f, 1.f }
|
||||||
|
#define COLOR_BLUE \
|
||||||
|
Vec4 { 0.0f, 0.f, 1.f, 1.f }
|
||||||
|
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline T Maximum(T a, T b) {
|
||||||
|
if (a > b) return a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline T Minimum(T a, T b) {
|
||||||
|
if (a > b) return b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline void Swap(T &a, T& b) {
|
||||||
|
T temp = a;
|
||||||
|
a = b;
|
||||||
|
b = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec2 Vec2IToVec2(Vec2I a) { return { (float)a.x, (float)a.y, }; }
|
||||||
|
inline Vec3 Vec3IToVec3(Vec3I a) { return { (float)a.x, (float)a.y, (float)a.z, }; }
|
||||||
|
inline Vec4 Vec4IToVec4(Vec4I a) { return { (float)a.x, (float)a.y, (float)a.z, (float)a.w, }; }
|
||||||
|
inline Vec2 operator-(Vec2 a) { return { -a.x, -a.y }; }
|
||||||
|
inline Vec3 operator-(Vec3 a) { return { -a.x, -a.y ,-a.z }; }
|
||||||
|
inline Vec4 operator-(Vec4 a) { return { -a.x, -a.y ,-a.z ,-a.w }; }
|
||||||
|
inline Vec2 operator*(Vec2 a, Vec2 b) { return { a.x * b.x, a.y * b.y, }; }
|
||||||
|
inline Vec2 &operator*=(Vec2 &a, Vec2 b) { a = a * b; return a; }
|
||||||
|
inline Vec2 operator*(Vec2 a, float b) { return { a.x * b, a.y * b, }; }
|
||||||
|
inline Vec2 operator*(float b, Vec2 a) { return { a.x * b, a.y * b, }; }
|
||||||
|
inline Vec2 &operator*=(Vec2 &a, float b) { a = a * b; return a; }
|
||||||
|
inline Vec3 operator*(Vec3 a, Vec3 b) { return { a.x * b.x, a.y * b.y, a.z * b.z, }; }
|
||||||
|
inline Vec3 &operator*=(Vec3 &a, Vec3 b) { a = a * b; return a; }
|
||||||
|
inline Vec3 operator*(Vec3 a, float b) { return { a.x * b, a.y * b, a.z * b, }; }
|
||||||
|
inline Vec3 operator*(float b, Vec3 a) { return { a.x * b, a.y * b, a.z * b, }; }
|
||||||
|
inline Vec3 &operator*=(Vec3 &a, float b) { a = a * b; return a; }
|
||||||
|
inline Vec4 operator*(Vec4 a, Vec4 b) { return { a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w, }; }
|
||||||
|
inline Vec4 &operator*=(Vec4 &a, Vec4 b) { a = a * b; return a; }
|
||||||
|
inline Vec4 operator*(Vec4 a, float b) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
|
||||||
|
inline Vec4 operator*(float b, Vec4 a) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
|
||||||
|
inline Vec4 &operator*=(Vec4 &a, float b) { a = a * b; return a; }
|
||||||
|
inline Vec2 operator/(Vec2 a, Vec2 b) { return { a.x / b.x, a.y / b.y, }; }
|
||||||
|
inline Vec2 &operator/=(Vec2 &a, Vec2 b) { a = a / b; return a; }
|
||||||
|
inline Vec2 operator/(Vec2 a, float b) { return { a.x / b, a.y / b, }; }
|
||||||
|
inline Vec2 operator/(float b, Vec2 a) { return { b / a.x, b / a.y, }; }
|
||||||
|
inline Vec2 &operator/=(Vec2 &a, float b) { a = a / b; return a; }
|
||||||
|
inline Vec3 operator/(Vec3 a, Vec3 b) { return { a.x / b.x, a.y / b.y, a.z / b.z, }; }
|
||||||
|
inline Vec3 &operator/=(Vec3 &a, Vec3 b) { a = a / b; return a; }
|
||||||
|
inline Vec3 operator/(Vec3 a, float b) { return { a.x / b, a.y / b, a.z / b, }; }
|
||||||
|
inline Vec3 operator/(float b, Vec3 a) { return { b / a.x, b / a.y, b / a.z, }; }
|
||||||
|
inline Vec3 &operator/=(Vec3 &a, float b) { a = a / b; return a; }
|
||||||
|
inline Vec4 operator/(Vec4 a, Vec4 b) { return { a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w, }; }
|
||||||
|
inline Vec4 &operator/=(Vec4 &a, Vec4 b) { a = a / b; return a; }
|
||||||
|
inline Vec4 operator/(Vec4 a, float b) { return { a.x / b, a.y / b, a.z / b, a.w / b, }; }
|
||||||
|
inline Vec4 operator/(float b, Vec4 a) { return { b / a.x, b / a.y, b / a.z, b / a.w, }; }
|
||||||
|
inline Vec4 &operator/=(Vec4 &a, float b) { a = a / b; return a; }
|
||||||
|
inline Vec2 operator+(Vec2 a, Vec2 b) { return { a.x + b.x, a.y + b.y, }; }
|
||||||
|
inline Vec2 &operator+=(Vec2 &a, Vec2 b) { a = a + b; return a; }
|
||||||
|
inline Vec2 operator+(Vec2 a, float b) { return { a.x + b, a.y + b, }; }
|
||||||
|
inline Vec2 operator+(float b, Vec2 a) { return { a.x + b, a.y + b, }; }
|
||||||
|
inline Vec2 &operator+=(Vec2 &a, float b) { a = a + b; return a; }
|
||||||
|
inline Vec3 operator+(Vec3 a, Vec3 b) { return { a.x + b.x, a.y + b.y, a.z + b.z, }; }
|
||||||
|
inline Vec3 &operator+=(Vec3 &a, Vec3 b) { a = a + b; return a; }
|
||||||
|
inline Vec3 operator+(Vec3 a, float b) { return { a.x + b, a.y + b, a.z + b, }; }
|
||||||
|
inline Vec3 operator+(float b, Vec3 a) { return { a.x + b, a.y + b, a.z + b, }; }
|
||||||
|
inline Vec3 &operator+=(Vec3 &a, float b) { a = a + b; return a; }
|
||||||
|
inline Vec4 operator+(Vec4 a, Vec4 b) { return { a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w, }; }
|
||||||
|
inline Vec4 &operator+=(Vec4 &a, Vec4 b) { a = a + b; return a; }
|
||||||
|
inline Vec4 operator+(Vec4 a, float b) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
|
||||||
|
inline Vec4 operator+(float b, Vec4 a) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
|
||||||
|
inline Vec4 &operator+=(Vec4 &a, float b) { a = a + b; return a; }
|
||||||
|
inline Vec2 operator-(Vec2 a, Vec2 b) { return { a.x - b.x, a.y - b.y, }; }
|
||||||
|
inline Vec2 &operator-=(Vec2 &a, Vec2 b) { a = a - b; return a; }
|
||||||
|
inline Vec2 operator-(Vec2 a, float b) { return { a.x - b, a.y - b, }; }
|
||||||
|
inline Vec2 operator-(float b, Vec2 a) { return { b - a.x, b - a.y, }; }
|
||||||
|
inline Vec2 &operator-=(Vec2 &a, float b) { a = a - b; return a; }
|
||||||
|
inline Vec3 operator-(Vec3 a, Vec3 b) { return { a.x - b.x, a.y - b.y, a.z - b.z, }; }
|
||||||
|
inline Vec3 &operator-=(Vec3 &a, Vec3 b) { a = a - b; return a; }
|
||||||
|
inline Vec3 operator-(Vec3 a, float b) { return { a.x - b, a.y - b, a.z - b, }; }
|
||||||
|
inline Vec3 operator-(float b, Vec3 a) { return { b - a.x, b - a.y, b - a.z, }; }
|
||||||
|
inline Vec3 &operator-=(Vec3 &a, float b) { a = a - b; return a; }
|
||||||
|
inline Vec4 operator-(Vec4 a, Vec4 b) { return { a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w, }; }
|
||||||
|
inline Vec4 &operator-=(Vec4 &a, Vec4 b) { a = a - b; return a; }
|
||||||
|
inline Vec4 operator-(Vec4 a, float b) { return { a.x - b, a.y - b, a.z - b, a.w - b, }; }
|
||||||
|
inline Vec4 operator-(float b, Vec4 a) { return { b - a.x, b - a.y, b - a.z, b - a.w, }; }
|
||||||
|
inline Vec4 &operator-=(Vec4 &a, float b) { a = a - b; return a; }
|
||||||
|
|
||||||
|
inline Vec2I operator-(Vec2I a) { return { -a.x, -a.y }; }
|
||||||
|
inline Vec3I operator-(Vec3I a) { return { -a.x, -a.y ,-a.z }; }
|
||||||
|
inline Vec4I operator-(Vec4I a) { return { -a.x, -a.y ,-a.z ,-a.w }; }
|
||||||
|
inline Vec2I operator*(Vec2I a, Vec2I b) { return { a.x * b.x, a.y * b.y, }; }
|
||||||
|
inline Vec2I &operator*=(Vec2I &a, Vec2I b) { a = a * b; return a; }
|
||||||
|
inline Vec2I operator*(Vec2I a, int b) { return { a.x * b, a.y * b, }; }
|
||||||
|
inline Vec2I operator*(int b, Vec2I a) { return { a.x * b, a.y * b, }; }
|
||||||
|
inline Vec2I &operator*=(Vec2I &a, int b) { a = a * b; return a; }
|
||||||
|
inline Vec3I operator*(Vec3I a, Vec3I b) { return { a.x * b.x, a.y * b.y, a.z * b.z, }; }
|
||||||
|
inline Vec3I &operator*=(Vec3I &a, Vec3I b) { a = a * b; return a; }
|
||||||
|
inline Vec3I operator*(Vec3I a, int b) { return { a.x * b, a.y * b, a.z * b, }; }
|
||||||
|
inline Vec3I operator*(int b, Vec3I a) { return { a.x * b, a.y * b, a.z * b, }; }
|
||||||
|
inline Vec3I &operator*=(Vec3I &a, int b) { a = a * b; return a; }
|
||||||
|
inline Vec4I operator*(Vec4I a, Vec4I b) { return { a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w, }; }
|
||||||
|
inline Vec4I &operator*=(Vec4I &a, Vec4I b) { a = a * b; return a; }
|
||||||
|
inline Vec4I operator*(Vec4I a, int b) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
|
||||||
|
inline Vec4I operator*(int b, Vec4I a) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
|
||||||
|
inline Vec4I &operator*=(Vec4I &a, int b) { a = a * b; return a; }
|
||||||
|
inline Vec2I operator/(Vec2I a, Vec2I b) { return { a.x / b.x, a.y / b.y, }; }
|
||||||
|
inline Vec2I &operator/=(Vec2I &a, Vec2I b) { a = a / b; return a; }
|
||||||
|
inline Vec2I operator/(Vec2I a, int b) { return { a.x / b, a.y / b, }; }
|
||||||
|
inline Vec2I operator/(int b, Vec2I a) { return { b / a.x, b / a.y, }; }
|
||||||
|
inline Vec2I &operator/=(Vec2I &a, int b) { a = a / b; return a; }
|
||||||
|
inline Vec3I operator/(Vec3I a, Vec3I b) { return { a.x / b.x, a.y / b.y, a.z / b.z, }; }
|
||||||
|
inline Vec3I &operator/=(Vec3I &a, Vec3I b) { a = a / b; return a; }
|
||||||
|
inline Vec3I operator/(Vec3I a, int b) { return { a.x / b, a.y / b, a.z / b, }; }
|
||||||
|
inline Vec3I operator/(int b, Vec3I a) { return { b / a.x, b / a.y, b / a.z, }; }
|
||||||
|
inline Vec3I &operator/=(Vec3I &a, int b) { a = a / b; return a; }
|
||||||
|
inline Vec4I operator/(Vec4I a, Vec4I b) { return { a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w, }; }
|
||||||
|
inline Vec4I &operator/=(Vec4I &a, Vec4I b) { a = a / b; return a; }
|
||||||
|
inline Vec4I operator/(Vec4I a, int b) { return { a.x / b, a.y / b, a.z / b, a.w / b, }; }
|
||||||
|
inline Vec4I operator/(int b, Vec4I a) { return { b / a.x, b / a.y, b / a.z, b / a.w, }; }
|
||||||
|
inline Vec4I &operator/=(Vec4I &a, int b) { a = a / b; return a; }
|
||||||
|
inline Vec2I operator+(Vec2I a, Vec2I b) { return { a.x + b.x, a.y + b.y, }; }
|
||||||
|
inline Vec2I &operator+=(Vec2I &a, Vec2I b) { a = a + b; return a; }
|
||||||
|
inline Vec2I operator+(Vec2I a, int b) { return { a.x + b, a.y + b, }; }
|
||||||
|
inline Vec2I operator+(int b, Vec2I a) { return { a.x + b, a.y + b, }; }
|
||||||
|
inline Vec2I &operator+=(Vec2I &a, int b) { a = a + b; return a; }
|
||||||
|
inline Vec3I operator+(Vec3I a, Vec3I b) { return { a.x + b.x, a.y + b.y, a.z + b.z, }; }
|
||||||
|
inline Vec3I &operator+=(Vec3I &a, Vec3I b) { a = a + b; return a; }
|
||||||
|
inline Vec3I operator+(Vec3I a, int b) { return { a.x + b, a.y + b, a.z + b, }; }
|
||||||
|
inline Vec3I operator+(int b, Vec3I a) { return { a.x + b, a.y + b, a.z + b, }; }
|
||||||
|
inline Vec3I &operator+=(Vec3I &a, int b) { a = a + b; return a; }
|
||||||
|
inline Vec4I operator+(Vec4I a, Vec4I b) { return { a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w, }; }
|
||||||
|
inline Vec4I &operator+=(Vec4I &a, Vec4I b) { a = a + b; return a; }
|
||||||
|
inline Vec4I operator+(Vec4I a, int b) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
|
||||||
|
inline Vec4I operator+(int b, Vec4I a) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
|
||||||
|
inline Vec4I &operator+=(Vec4I &a, int b) { a = a + b; return a; }
|
||||||
|
inline Vec2I operator-(Vec2I a, Vec2I b) { return { a.x - b.x, a.y - b.y, }; }
|
||||||
|
inline Vec2I &operator-=(Vec2I &a, Vec2I b) { a = a - b; return a; }
|
||||||
|
inline Vec2I operator-(Vec2I a, int b) { return { a.x - b, a.y - b, }; }
|
||||||
|
inline Vec2I operator-(int b, Vec2I a) { return { b - a.x, b - a.y, }; }
|
||||||
|
inline Vec2I &operator-=(Vec2I &a, int b) { a = a - b; return a; }
|
||||||
|
inline Vec3I operator-(Vec3I a, Vec3I b) { return { a.x - b.x, a.y - b.y, a.z - b.z, }; }
|
||||||
|
inline Vec3I &operator-=(Vec3I &a, Vec3I b) { a = a - b; return a; }
|
||||||
|
inline Vec3I operator-(Vec3I a, int b) { return { a.x - b, a.y - b, a.z - b, }; }
|
||||||
|
inline Vec3I operator-(int b, Vec3I a) { return { b - a.x, b - a.y, b - a.z, }; }
|
||||||
|
inline Vec3I &operator-=(Vec3I &a, int b) { a = a - b; return a; }
|
||||||
|
inline Vec4I operator-(Vec4I a, Vec4I b) { return { a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w, }; }
|
||||||
|
inline Vec4I &operator-=(Vec4I &a, Vec4I b) { a = a - b; return a; }
|
||||||
|
inline Vec4I operator-(Vec4I a, int b) { return { a.x - b, a.y - b, a.z - b, a.w - b, }; }
|
||||||
|
inline Vec4I operator-(int b, Vec4I a) { return { b - a.x, b - a.y, b - a.z, b - a.w, }; }
|
||||||
|
inline Vec4I &operator-=(Vec4I &a, int b) { a = a - b; return a; }
|
||||||
|
|
||||||
|
inline Rect2 Rect2_Center(Vec2 center, Vec2 halfdim) { return {center.x - halfdim.x, center.y - halfdim.y, center.x + halfdim.x, center.y + halfdim.y}; }
|
||||||
|
inline Vec2 CalcSize(Rect2 r) { return { r.max.x - r.min.x, r.max.y - r.min.y }; }
|
||||||
|
inline Rect2 Rect2_Size(float x, float y, float w, float h) { return { x, y, x + w, y + h }; }
|
||||||
|
inline Rect2 Rect2_Size(Vec2 pos, Vec2 size) { return { pos.x, pos.y, pos.x + size.w, pos.y + size.h }; }
|
||||||
|
inline Rect2 Rect2_MinMax(Vec2 p0, Vec2 p1) { return {p0.x, p0.y, p1.x, p1.y }; }
|
||||||
|
inline Rect2 ShrinkByHalfSize(Rect2 rect, Vec2 half_size) { return {rect.min.x + half_size.x, rect.min.y + half_size.y, rect.max.x - half_size.x, rect.max.y - half_size.y}; }
|
||||||
|
inline Rect2 ExpandByHalfSize(Rect2 rect, Vec2 half_size) { return {rect.min.x - half_size.x, rect.min.y - half_size.y, rect.max.x + half_size.x, rect.max.y + half_size.y}; }
|
||||||
|
|
||||||
|
inline Rect2 Rect2_AdjustMinMax(Vec2 p0, Vec2 p1) {
|
||||||
|
Rect2 result = {
|
||||||
|
Minimum(p0.x, p1.x), Minimum(p0.y, p1.y),
|
||||||
|
Maximum(p0.x, p1.x), Maximum(p0.y, p1.y),
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float GetXSize(Rect2 r) { return r.max.x - r.min.x; }
|
||||||
|
inline float GetYSize(Rect2 r) { return r.max.y - r.min.y; }
|
||||||
|
inline Vec2 GetSize(Rect2 r) { return r.max - r.min; }
|
||||||
|
|
||||||
|
inline float Lerp(float from, float to, float t) {return (1-t)*from + t*to;}
|
||||||
|
inline Vec2 Lerp(Vec2 from, Vec2 to, float t) {return (1-t)*from + t*to;}
|
||||||
|
inline Vec3 Lerp(Vec3 from, Vec3 to, float t) {return (1-t)*from + t*to;}
|
||||||
|
inline Vec4 Lerp(Vec4 from, Vec4 to, float t) {return (1-t)*from + t*to;}
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
inline float SquareRoot(float a) { return sqrtf(a); }
|
||||||
|
inline Vec2 SquareRoot(Vec2 a) { return {SquareRoot(a.x), SquareRoot(a.y)}; }
|
||||||
|
inline Vec3 SquareRoot(Vec3 a) { return {SquareRoot(a.x), SquareRoot(a.y), SquareRoot(a.z)}; }
|
||||||
|
inline Vec4 SquareRoot(Vec4 a) { return {SquareRoot(a.x), SquareRoot(a.y), SquareRoot(a.z), SquareRoot(a.w)}; }
|
||||||
|
inline float Dot(Vec2 a, Vec2 b) { return a.x * b.x + a.y * b.y; }
|
||||||
|
inline float Dot(Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
|
||||||
|
inline float Dot(Vec4 a, Vec4 b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; }
|
||||||
|
inline float Length(Vec2 a) { return SquareRoot(Dot(a,a)); }
|
||||||
|
inline float Length(Vec3 a) { return SquareRoot(Dot(a,a)); }
|
||||||
|
inline float Length(Vec4 a) { return SquareRoot(Dot(a,a)); }
|
||||||
|
|
||||||
|
inline Vec2 Normalize(Vec2 a) {
|
||||||
|
Vec2 result = {};
|
||||||
|
float len = Length(a);
|
||||||
|
if (len != 0.f) {
|
||||||
|
float inv_len = 1.0f / len;
|
||||||
|
result.x = a.x * inv_len;
|
||||||
|
result.y = a.y * inv_len;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec3 Normalize(Vec3 a) {
|
||||||
|
Vec3 result = {};
|
||||||
|
float len = Length(a);
|
||||||
|
if (len != 0.f) {
|
||||||
|
float inv_len = 1.0f / len;
|
||||||
|
result.x = a.x * inv_len;
|
||||||
|
result.y = a.y * inv_len;
|
||||||
|
result.z = a.z * inv_len;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec4 Normalize(Vec4 a) {
|
||||||
|
Vec4 result = {};
|
||||||
|
float len = Length(a);
|
||||||
|
if (len != 0.f) {
|
||||||
|
float inv_len = 1.0f / len;
|
||||||
|
result.x = a.x * inv_len;
|
||||||
|
result.y = a.y * inv_len;
|
||||||
|
result.z = a.z * inv_len;
|
||||||
|
result.w = a.w * inv_len;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline M4x4 operator*(M4x4 a, M4x4 b) {
|
||||||
|
M4x4 result;
|
||||||
|
for (int y = 0; y < 4; y++) {
|
||||||
|
for (int x = 0; x < 4; x++) {
|
||||||
|
result.e[y][x] =
|
||||||
|
a.e[y][0] * b.e[0][x] + a.e[y][1] * b.e[1][x] + a.e[y][2] * b.e[2][x] + a.e[y][3] * b.e[3][x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matrix is stored ROW MAJOR in memory so that it mimicks the COLUMN MAJOR MATH NOTATION from textbooks.
|
||||||
|
// AMAZIN!
|
||||||
|
inline Vec4 operator*(M4x4 a, Vec4 b) {
|
||||||
|
Vec4 result;
|
||||||
|
for (int y = 0; y < 4; y++) {
|
||||||
|
result.e[y] = a.e[y][0] * b.e[0] + a.e[y][1] * b.e[1] + a.e[y][2] * b.e[2] + a.e[y][3] * b.e[3];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matrix is stored ROW MAJOR in memory so that it mimicks the COLUMN MAJOR MATH NOTATION from textbooks.
|
||||||
|
// AMAZIN!
|
||||||
|
inline Vec3 operator*(M4x4 a, Vec3 b) {
|
||||||
|
Vec4 result;
|
||||||
|
for (int y = 0; y < 4; y++)
|
||||||
|
result.e[y] = a.e[y][0] * b.e[0] + a.e[y][1] * b.e[1] + a.e[y][2] * b.e[2] + a.e[y][3] * 1;
|
||||||
|
return {result.x, result.y, result.z};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec3 GetBasisX(M4x4 a) {
|
||||||
|
Vec3 result = {a.e[0][0], a.e[0][1], a.e[0][2]};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec3 GetBasisY(M4x4 a) {
|
||||||
|
Vec3 result = {a.e[1][0], a.e[1][1], a.e[1][2]};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec3 GetBasisZ(M4x4 a) {
|
||||||
|
Vec3 result = {a.e[2][0], a.e[2][1], a.e[2][2]};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec3 GetTranslation(M4x4 a) {
|
||||||
|
Vec3 result = {a.e[0][3], a.e[1][3], a.e[2][3]};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row major or column major: Read struct M4x4
|
||||||
|
inline M4x4 M4_Perspective(float fov, float window_x, float window_y, float znear, float zfar) {
|
||||||
|
float a = window_x / window_y;
|
||||||
|
float c = 1.0f / tanf(DEG2RAD*fov/2.0f);
|
||||||
|
float f = zfar;
|
||||||
|
float n = znear;
|
||||||
|
M4x4 result = {
|
||||||
|
c/a , 0 , 0 , 0 ,
|
||||||
|
0 , c , 0 , 0 ,
|
||||||
|
0 , 0 , -(f+n)/(f-n) , -(2*f*n)/(f-n) ,
|
||||||
|
0 , 0 , -1 , 0 ,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row major or column major: Read struct M4x4
|
||||||
|
inline M4x4 M4_RotationX(float rotation) {
|
||||||
|
float s = sinf(rotation);
|
||||||
|
float c = cosf(rotation);
|
||||||
|
M4x4 result = {
|
||||||
|
1, 0, 0, 0,
|
||||||
|
0, c, - s, 0,
|
||||||
|
0, s, c, 0,
|
||||||
|
0, 0, 0, 1,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row major or column major: Read struct M4x4
|
||||||
|
inline M4x4 M4_RotationY(float rotation) {
|
||||||
|
float s = sinf(rotation);
|
||||||
|
float c = cosf(rotation);
|
||||||
|
M4x4 result = {
|
||||||
|
c, 0, s, 0,
|
||||||
|
0, 1, 0, 0,
|
||||||
|
-s, 0, c, 0,
|
||||||
|
0, 0, 0, 1,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row major or column major: Read struct M4x4
|
||||||
|
inline M4x4 M4_RotationZ(float rotation) {
|
||||||
|
float s = sinf(rotation);
|
||||||
|
float c = cosf(rotation);
|
||||||
|
M4x4 result = {
|
||||||
|
c, -s, 0, 0,
|
||||||
|
s, c, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
inline Vec3 Cross(Vec3 a, Vec3 b) {
|
||||||
|
Vec3 result;
|
||||||
|
result.x = a.y * b.z - a.z * b.y;
|
||||||
|
result.y = a.z * b.x - a.x * b.z;
|
||||||
|
result.z = a.x * b.y - a.y * b.x;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
inline M4x4 M4_Identity() {
|
||||||
|
M4x4 result = {
|
||||||
|
1, 0, 0, 0,
|
||||||
|
0, 1, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row major
|
||||||
|
inline M4x4 M4_Scale(Vec3 a) {
|
||||||
|
M4x4 result = {
|
||||||
|
a.x, 0, 0, 0,
|
||||||
|
0, a.y, 0, 0,
|
||||||
|
0, 0, a.z, 0,
|
||||||
|
0, 0, 0, 1,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row major
|
||||||
|
inline M4x4 M4_Translation(Vec3 a) {
|
||||||
|
return {
|
||||||
|
1, 0, 0, a.x,
|
||||||
|
0, 1, 0, a.y,
|
||||||
|
0, 0, 1, a.z,
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ?
|
||||||
|
// Why in the instance of changing the basis you need the vectors setup in this way
|
||||||
|
// but in camera transforms you have those vectors transposed??
|
||||||
|
inline M4x4 M4_ChangeOfBasis(Vec3 side, Vec3 up, Vec3 forward) {
|
||||||
|
M4x4 result = {
|
||||||
|
side.x , up.x , forward.x , 0 ,
|
||||||
|
side.y , up.y , forward.y , 0 ,
|
||||||
|
side.z , up.z , forward.z , 0 ,
|
||||||
|
0 , 0 , 0 , 1 ,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline M4x4 Transpose(M4x4 a) {
|
||||||
|
M4x4 result = a;
|
||||||
|
result.e[0][1] = result.e[1][0];
|
||||||
|
result.e[0][2] = result.e[2][0];
|
||||||
|
result.e[0][3] = result.e[3][0];
|
||||||
|
result.e[2][1] = result.e[1][2];
|
||||||
|
result.e[3][1] = result.e[1][3];
|
||||||
|
result.e[3][2] = result.e[2][3];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline T ClampTop(T t, T max) {
|
||||||
|
if (t > max) return max;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline T ClampBot(T t, T min) {
|
||||||
|
if (t < min) return min;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline T Clamp(T t, T min, T max) {
|
||||||
|
if (t > max) return max;
|
||||||
|
else if (t < min) return min;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float Clamp01(float t) {
|
||||||
|
return Clamp(t, 0.f, 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float Clamp01MapToRange(float t, float min, float max) {
|
||||||
|
float range = max - min;
|
||||||
|
float value = (t - min) / range;
|
||||||
|
float result = Clamp(value, 0.f, 1.f);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool AreColliding(Rect2 rect, Vec2 point) {
|
||||||
|
bool result = point.x > rect.min.x && point.x < rect.max.x && point.y > rect.min.y && point.y < rect.max.y;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool AreColliding(Rect2 a, Rect2 b) {
|
||||||
|
bool result = a.min.x < b.max.x && a.max.x > b.min.x &&a.min.y < b.max.y && a.max.y > b.min.y;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool AreColliding(Vec2 circle_pos, float radius, Vec2 point) {
|
||||||
|
Vec2 d = point - circle_pos;
|
||||||
|
float distance = Length(d);
|
||||||
|
bool result = distance <= radius;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts an RGB color value to HSL. Conversion formula
|
||||||
|
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||||
|
* also https://gist.github.com/ciembor/1494530
|
||||||
|
* alpha is ignored
|
||||||
|
*/
|
||||||
|
inline Vec3 RGBToHSL(Vec3 color) {
|
||||||
|
|
||||||
|
Vec3 result;
|
||||||
|
float max = Maximum(Maximum(color.r,color.g),color.b);
|
||||||
|
float min = Minimum(Minimum(color.r,color.g),color.b);
|
||||||
|
|
||||||
|
result.h = result.s = result.l = (max + min) / 2.f;
|
||||||
|
|
||||||
|
if (max == min) {
|
||||||
|
result.h = result.s = 0.f; // achromatic
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float d = max - min;
|
||||||
|
result.s = (result.l > 0.5f) ? d / (2.f - max - min) : d / (max + min);
|
||||||
|
|
||||||
|
if (max == color.r) {
|
||||||
|
result.h = (color.g - color.b) / d + (color.g < color.b ? 6.f : 0.f);
|
||||||
|
}
|
||||||
|
else if (max == color.g) {
|
||||||
|
result.h = (color.b - color.r) / d + 2.f;
|
||||||
|
}
|
||||||
|
else if (max == color.b) {
|
||||||
|
result.h = (color.r - color.g) / d + 4.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.h /= 6.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec4 RGBToHSL(Vec4 color) {
|
||||||
|
Vec3 result = RGBToHSL(color.rgb);
|
||||||
|
return {result.r, result.g, result.b, color.a};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts an HUE to r, g or b.
|
||||||
|
* returns float in the set [0, 1].
|
||||||
|
*/
|
||||||
|
inline float HueToRGB(float p, float q, float t) {
|
||||||
|
if (t < 0.f) t += 1.f;
|
||||||
|
if (t > 1.f) t -= 1.f;
|
||||||
|
if (t < 1.f/6.f) return p + (q - p) * 6.f * t;
|
||||||
|
if (t < 1.f/2.f) return q;
|
||||||
|
if (t < 2.f/3.f) return p + (q - p) * (2.f/3.f - t) * 6.f;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec3 HSLToRGB(Vec3 color) {
|
||||||
|
Vec3 result;
|
||||||
|
|
||||||
|
if(0 == color.s) {
|
||||||
|
result.r = result.g = result.b = color.l; // achromatic
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float q = color.l < 0.5f ? color.l * (1.f + color.s) : color.l + color.s - color.l * color.s;
|
||||||
|
float p = 2.f * color.l - q;
|
||||||
|
result.r = HueToRGB(p, q, color.h + 1.f/3.f);
|
||||||
|
result.g = HueToRGB(p, q, color.h);
|
||||||
|
result.b = HueToRGB(p, q, color.h - 1.f/3.f);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline Vec4 HSLToRGB(Vec4 color) {
|
||||||
|
Vec3 result = HSLToRGB(color.rgb);
|
||||||
|
return {result.r, result.g, result.b, color.a};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float PingPong(float x) {
|
||||||
|
return 1.0f - fabsf(1.f - fmodf(x,2));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float Absolute(float x) {return fabsf(x);}
|
||||||
|
inline Vec2 Absolute(Vec2 x) {return {fabsf(x.x), fabsf(x.y)};}
|
||||||
|
inline Vec4 SetAlpha(Vec4 color, float x) {
|
||||||
|
Vec4 result = color;
|
||||||
|
result.a = x;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float SmoothStep(float t) {
|
||||||
|
float result = t*t*(3-2*t);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float EaseOutBounce(float x) {
|
||||||
|
float n1 = 7.5625f;
|
||||||
|
float d1 = 2.75f;
|
||||||
|
|
||||||
|
if (x < 1.f / d1) {
|
||||||
|
return n1 * x * x;
|
||||||
|
} else if (x < 2.f / d1) {
|
||||||
|
return n1 * (x -= 1.5f / d1) * x + 0.75f;
|
||||||
|
} else if (x < 2.5f / d1) {
|
||||||
|
return n1 * (x -= 2.25f / d1) * x + 0.9375f;
|
||||||
|
} else {
|
||||||
|
return n1 * (x -= 2.625f / d1) * x + 0.984375f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float EaseOutElastic(float x) {
|
||||||
|
float c4 = (2 * PI32) / 3;
|
||||||
|
|
||||||
|
return x == 0.f
|
||||||
|
? 0
|
||||||
|
: x == 1.f
|
||||||
|
? 1
|
||||||
|
: powf(2.f, -10.f * x) * sinf((x * 10.f - 0.75f) * c4) + 1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float EaseInQuint(float x) {
|
||||||
|
return x * x * x * x * x;
|
||||||
|
}
|
||||||
|
inline float EaseInCubic(float x) {
|
||||||
|
return x * x * x;
|
||||||
|
}
|
||||||
306
src/core/vmath64.hpp
Executable file
306
src/core/vmath64.hpp
Executable file
@@ -0,0 +1,306 @@
|
|||||||
|
//
|
||||||
|
// 64 bit
|
||||||
|
//
|
||||||
|
|
||||||
|
union Vec2F64 {
|
||||||
|
struct {
|
||||||
|
double x, y;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
double w, h;
|
||||||
|
};
|
||||||
|
double e[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Vec3F64 {
|
||||||
|
struct {
|
||||||
|
double x, y, z;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
double r, g, b;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
double h, s, l;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Vec2F64 xy;
|
||||||
|
double floor;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
double _x1;
|
||||||
|
Vec2F64 yz;
|
||||||
|
};
|
||||||
|
double e[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Vec4F64 {
|
||||||
|
struct {
|
||||||
|
double x, y, z, w;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
double r, g, b, a;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
double h, s, l, a;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Vec3F64 rgb;
|
||||||
|
double _i1;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Vec3F64 xyz;
|
||||||
|
double _i2;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Vec2F64 xy;
|
||||||
|
Vec2F64 zw;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
double e[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Vec2I64 {
|
||||||
|
struct {
|
||||||
|
int64_t x, y;
|
||||||
|
};
|
||||||
|
int64_t e[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Vec3I64 {
|
||||||
|
struct {
|
||||||
|
int64_t x;
|
||||||
|
int64_t y;
|
||||||
|
int64_t z;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
int64_t r, g, b;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
int64_t z_[2];
|
||||||
|
int64_t floor;
|
||||||
|
};
|
||||||
|
int64_t e[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Vec4I64 {
|
||||||
|
struct {
|
||||||
|
int64_t x, y, z, w;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
int64_t r, g, b, a;
|
||||||
|
};
|
||||||
|
int64_t e[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rect2F64 {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
double x0;
|
||||||
|
double y0;
|
||||||
|
double x1;
|
||||||
|
double y1;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Vec2F64 min;
|
||||||
|
Vec2F64 max;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rect3F64 {
|
||||||
|
Vec3F64 min;
|
||||||
|
Vec3F64 max;
|
||||||
|
};
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
/*--#
|
||||||
|
|
||||||
|
for vec in ["Vec2F64", "Vec3F64", "Vec4F64"]:
|
||||||
|
v = vec.lower()
|
||||||
|
z = "(double)a.z," if vec == "Vec3F64" or vec == "Vec4F64" else ""
|
||||||
|
w = "(double)a.w," if vec == "Vec4F64" else ""
|
||||||
|
print(f"inline {vec} {vec}ITo{vec}({vec}I a) {{ return {{ (double)a.x, (double)a.y, {z} {w} }}; }}")
|
||||||
|
if z != "": z = ",-a.z"
|
||||||
|
if w != "": w = ",-a.w"
|
||||||
|
print(f"inline {vec} operator-({vec} a) {{ return {{ -a.x, -a.y {z} {w} }}; }}")
|
||||||
|
|
||||||
|
for op in ["*", "/", "+", "-"]:
|
||||||
|
for vec in ["Vec2F64", "Vec3F64", "Vec4F64"]:
|
||||||
|
x = f"a.x {op} b.x,"
|
||||||
|
y = f"a.y {op} b.y,"
|
||||||
|
z = f"a.z {op} b.z," if vec == "Vec3F64" or vec == "Vec4F64" else ""
|
||||||
|
w = f"a.w {op} b.w," if vec == "Vec4F64" else ""
|
||||||
|
|
||||||
|
print(f"inline {vec} operator{op}({vec} a, {vec} b) {{ return {{ {x} {y} {z} {w} }}; }}")
|
||||||
|
print(f"inline {vec} &operator{op}=({vec} &a, {vec} b) {{ a = a {op} b; return a; }}")
|
||||||
|
|
||||||
|
|
||||||
|
x = f"a.x {op} b,"
|
||||||
|
y = f"a.y {op} b,"
|
||||||
|
z = f"a.z {op} b," if vec == "Vec3F64" or vec == "Vec4F64" else ""
|
||||||
|
w = f"a.w {op} b," if vec == "Vec4F64" else ""
|
||||||
|
|
||||||
|
print(f"inline {vec} operator{op}({vec} a, double b) {{ return {{ {x} {y} {z} {w} }}; }}")
|
||||||
|
print(f"inline {vec} operator{op}(double b, {vec} a) {{ return {{ {x} {y} {z} {w} }}; }}")
|
||||||
|
print(f"inline {vec} &operator{op}=({vec} &a, double b) {{ a = a {op} b; return a; }}")
|
||||||
|
*/
|
||||||
|
inline Vec2F64 Vec2IToVec2(Vec2I64 a) { return { (double)a.x, (double)a.y, }; }
|
||||||
|
inline Vec3F64 Vec3IToVec3(Vec3I64 a) { return { (double)a.x, (double)a.y, (double)a.z, }; }
|
||||||
|
inline Vec4F64 Vec4IToVec4(Vec4I64 a) { return { (double)a.x, (double)a.y, (double)a.z, (double)a.w, }; }
|
||||||
|
inline Vec2F64 operator-(Vec2F64 a) { return { -a.x, -a.y }; }
|
||||||
|
inline Vec3F64 operator-(Vec3F64 a) { return { -a.x, -a.y ,-a.z }; }
|
||||||
|
inline Vec4F64 operator-(Vec4F64 a) { return { -a.x, -a.y ,-a.z ,-a.w }; }
|
||||||
|
inline Vec2F64 operator*(Vec2F64 a, Vec2F64 b) { return { a.x * b.x, a.y * b.y, }; }
|
||||||
|
inline Vec2F64 &operator*=(Vec2F64 &a, Vec2F64 b) { a = a * b; return a; }
|
||||||
|
inline Vec2F64 operator*(Vec2F64 a, double b) { return { a.x * b, a.y * b, }; }
|
||||||
|
inline Vec2F64 operator*(double b, Vec2F64 a) { return { a.x * b, a.y * b, }; }
|
||||||
|
inline Vec2F64 &operator*=(Vec2F64 &a, double b) { a = a * b; return a; }
|
||||||
|
inline Vec3F64 operator*(Vec3F64 a, Vec3F64 b) { return { a.x * b.x, a.y * b.y, a.z * b.z, }; }
|
||||||
|
inline Vec3F64 &operator*=(Vec3F64 &a, Vec3F64 b) { a = a * b; return a; }
|
||||||
|
inline Vec3F64 operator*(Vec3F64 a, double b) { return { a.x * b, a.y * b, a.z * b, }; }
|
||||||
|
inline Vec3F64 operator*(double b, Vec3F64 a) { return { a.x * b, a.y * b, a.z * b, }; }
|
||||||
|
inline Vec3F64 &operator*=(Vec3F64 &a, double b) { a = a * b; return a; }
|
||||||
|
inline Vec4F64 operator*(Vec4F64 a, Vec4F64 b) { return { a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w, }; }
|
||||||
|
inline Vec4F64 &operator*=(Vec4F64 &a, Vec4F64 b) { a = a * b; return a; }
|
||||||
|
inline Vec4F64 operator*(Vec4F64 a, double b) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
|
||||||
|
inline Vec4F64 operator*(double b, Vec4F64 a) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
|
||||||
|
inline Vec4F64 &operator*=(Vec4F64 &a, double b) { a = a * b; return a; }
|
||||||
|
inline Vec2F64 operator/(Vec2F64 a, Vec2F64 b) { return { a.x / b.x, a.y / b.y, }; }
|
||||||
|
inline Vec2F64 &operator/=(Vec2F64 &a, Vec2F64 b) { a = a / b; return a; }
|
||||||
|
inline Vec2F64 operator/(Vec2F64 a, double b) { return { a.x / b, a.y / b, }; }
|
||||||
|
inline Vec2F64 operator/(double b, Vec2F64 a) { return { b / a.x, b / a.y, }; }
|
||||||
|
inline Vec2F64 &operator/=(Vec2F64 &a, double b) { a = a / b; return a; }
|
||||||
|
inline Vec3F64 operator/(Vec3F64 a, Vec3F64 b) { return { a.x / b.x, a.y / b.y, a.z / b.z, }; }
|
||||||
|
inline Vec3F64 &operator/=(Vec3F64 &a, Vec3F64 b) { a = a / b; return a; }
|
||||||
|
inline Vec3F64 operator/(Vec3F64 a, double b) { return { a.x / b, a.y / b, a.z / b, }; }
|
||||||
|
inline Vec3F64 operator/(double b, Vec3F64 a) { return { b / a.x, b / a.y, b / a.z, }; }
|
||||||
|
inline Vec3F64 &operator/=(Vec3F64 &a, double b) { a = a / b; return a; }
|
||||||
|
inline Vec4F64 operator/(Vec4F64 a, Vec4F64 b) { return { a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w, }; }
|
||||||
|
inline Vec4F64 &operator/=(Vec4F64 &a, Vec4F64 b) { a = a / b; return a; }
|
||||||
|
inline Vec4F64 operator/(Vec4F64 a, double b) { return { a.x / b, a.y / b, a.z / b, a.w / b, }; }
|
||||||
|
inline Vec4F64 operator/(double b, Vec4F64 a) { return { b / a.x, b / a.y, b / a.z, b / a.w, }; }
|
||||||
|
inline Vec4F64 &operator/=(Vec4F64 &a, double b) { a = a / b; return a; }
|
||||||
|
inline Vec2F64 operator+(Vec2F64 a, Vec2F64 b) { return { a.x + b.x, a.y + b.y, }; }
|
||||||
|
inline Vec2F64 &operator+=(Vec2F64 &a, Vec2F64 b) { a = a + b; return a; }
|
||||||
|
inline Vec2F64 operator+(Vec2F64 a, double b) { return { a.x + b, a.y + b, }; }
|
||||||
|
inline Vec2F64 operator+(double b, Vec2F64 a) { return { a.x + b, a.y + b, }; }
|
||||||
|
inline Vec2F64 &operator+=(Vec2F64 &a, double b) { a = a + b; return a; }
|
||||||
|
inline Vec3F64 operator+(Vec3F64 a, Vec3F64 b) { return { a.x + b.x, a.y + b.y, a.z + b.z, }; }
|
||||||
|
inline Vec3F64 &operator+=(Vec3F64 &a, Vec3F64 b) { a = a + b; return a; }
|
||||||
|
inline Vec3F64 operator+(Vec3F64 a, double b) { return { a.x + b, a.y + b, a.z + b, }; }
|
||||||
|
inline Vec3F64 operator+(double b, Vec3F64 a) { return { a.x + b, a.y + b, a.z + b, }; }
|
||||||
|
inline Vec3F64 &operator+=(Vec3F64 &a, double b) { a = a + b; return a; }
|
||||||
|
inline Vec4F64 operator+(Vec4F64 a, Vec4F64 b) { return { a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w, }; }
|
||||||
|
inline Vec4F64 &operator+=(Vec4F64 &a, Vec4F64 b) { a = a + b; return a; }
|
||||||
|
inline Vec4F64 operator+(Vec4F64 a, double b) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
|
||||||
|
inline Vec4F64 operator+(double b, Vec4F64 a) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
|
||||||
|
inline Vec4F64 &operator+=(Vec4F64 &a, double b) { a = a + b; return a; }
|
||||||
|
inline Vec2F64 operator-(Vec2F64 a, Vec2F64 b) { return { a.x - b.x, a.y - b.y, }; }
|
||||||
|
inline Vec2F64 &operator-=(Vec2F64 &a, Vec2F64 b) { a = a - b; return a; }
|
||||||
|
inline Vec2F64 operator-(Vec2F64 a, double b) { return { a.x - b, a.y - b, }; }
|
||||||
|
inline Vec2F64 operator-(double b, Vec2F64 a) { return { b - a.x, b - a.y, }; }
|
||||||
|
inline Vec2F64 &operator-=(Vec2F64 &a, double b) { a = a - b; return a; }
|
||||||
|
inline Vec3F64 operator-(Vec3F64 a, Vec3F64 b) { return { a.x - b.x, a.y - b.y, a.z - b.z, }; }
|
||||||
|
inline Vec3F64 &operator-=(Vec3F64 &a, Vec3F64 b) { a = a - b; return a; }
|
||||||
|
inline Vec3F64 operator-(Vec3F64 a, double b) { return { a.x - b, a.y - b, a.z - b, }; }
|
||||||
|
inline Vec3F64 operator-(double b, Vec3F64 a) { return { b - a.x, b - a.y, b - a.z, }; }
|
||||||
|
inline Vec3F64 &operator-=(Vec3F64 &a, double b) { a = a - b; return a; }
|
||||||
|
inline Vec4F64 operator-(Vec4F64 a, Vec4F64 b) { return { a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w, }; }
|
||||||
|
inline Vec4F64 &operator-=(Vec4F64 &a, Vec4F64 b) { a = a - b; return a; }
|
||||||
|
inline Vec4F64 operator-(Vec4F64 a, double b) { return { a.x - b, a.y - b, a.z - b, a.w - b, }; }
|
||||||
|
inline Vec4F64 operator-(double b, Vec4F64 a) { return { b - a.x, b - a.y, b - a.z, b - a.w, }; }
|
||||||
|
inline Vec4F64 &operator-=(Vec4F64 &a, double b) { a = a - b; return a; }
|
||||||
|
/*END*/
|
||||||
|
|
||||||
|
inline Vec2I64 operator-(Vec2I64 a) { return { -a.x, -a.y }; }
|
||||||
|
inline Vec3I64 operator-(Vec3I64 a) { return { -a.x, -a.y ,-a.z }; }
|
||||||
|
inline Vec4I64 operator-(Vec4I64 a) { return { -a.x, -a.y ,-a.z ,-a.w }; }
|
||||||
|
inline Vec2I64 operator*(Vec2I64 a, Vec2I64 b) { return { a.x * b.x, a.y * b.y, }; }
|
||||||
|
inline Vec2I64 &operator*=(Vec2I64 &a, Vec2I64 b) { a = a * b; return a; }
|
||||||
|
inline Vec2I64 operator*(Vec2I64 a, int64_t b) { return { a.x * b, a.y * b, }; }
|
||||||
|
inline Vec2I64 operator*(int64_t b, Vec2I64 a) { return { a.x * b, a.y * b, }; }
|
||||||
|
inline Vec2I64 &operator*=(Vec2I64 &a, int64_t b) { a = a * b; return a; }
|
||||||
|
inline Vec3I64 operator*(Vec3I64 a, Vec3I64 b) { return { a.x * b.x, a.y * b.y, a.z * b.z, }; }
|
||||||
|
inline Vec3I64 &operator*=(Vec3I64 &a, Vec3I64 b) { a = a * b; return a; }
|
||||||
|
inline Vec3I64 operator*(Vec3I64 a, int64_t b) { return { a.x * b, a.y * b, a.z * b, }; }
|
||||||
|
inline Vec3I64 operator*(int64_t b, Vec3I64 a) { return { a.x * b, a.y * b, a.z * b, }; }
|
||||||
|
inline Vec3I64 &operator*=(Vec3I64 &a, int64_t b) { a = a * b; return a; }
|
||||||
|
inline Vec4I64 operator*(Vec4I64 a, Vec4I64 b) { return { a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w, }; }
|
||||||
|
inline Vec4I64 &operator*=(Vec4I64 &a, Vec4I64 b) { a = a * b; return a; }
|
||||||
|
inline Vec4I64 operator*(Vec4I64 a, int64_t b) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
|
||||||
|
inline Vec4I64 operator*(int64_t b, Vec4I64 a) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
|
||||||
|
inline Vec4I64 &operator*=(Vec4I64 &a, int64_t b) { a = a * b; return a; }
|
||||||
|
inline Vec2I64 operator/(Vec2I64 a, Vec2I64 b) { return { a.x / b.x, a.y / b.y, }; }
|
||||||
|
inline Vec2I64 &operator/=(Vec2I64 &a, Vec2I64 b) { a = a / b; return a; }
|
||||||
|
inline Vec2I64 operator/(Vec2I64 a, int64_t b) { return { a.x / b, a.y / b, }; }
|
||||||
|
inline Vec2I64 operator/(int64_t b, Vec2I64 a) { return { b / a.x, b / a.y, }; }
|
||||||
|
inline Vec2I64 &operator/=(Vec2I64 &a, int64_t b) { a = a / b; return a; }
|
||||||
|
inline Vec3I64 operator/(Vec3I64 a, Vec3I64 b) { return { a.x / b.x, a.y / b.y, a.z / b.z, }; }
|
||||||
|
inline Vec3I64 &operator/=(Vec3I64 &a, Vec3I64 b) { a = a / b; return a; }
|
||||||
|
inline Vec3I64 operator/(Vec3I64 a, int64_t b) { return { a.x / b, a.y / b, a.z / b, }; }
|
||||||
|
inline Vec3I64 operator/(int64_t b, Vec3I64 a) { return { b / a.x, b / a.y, b / a.z, }; }
|
||||||
|
inline Vec3I64 &operator/=(Vec3I64 &a, int64_t b) { a = a / b; return a; }
|
||||||
|
inline Vec4I64 operator/(Vec4I64 a, Vec4I64 b) { return { a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w, }; }
|
||||||
|
inline Vec4I64 &operator/=(Vec4I64 &a, Vec4I64 b) { a = a / b; return a; }
|
||||||
|
inline Vec4I64 operator/(Vec4I64 a, int64_t b) { return { a.x / b, a.y / b, a.z / b, a.w / b, }; }
|
||||||
|
inline Vec4I64 operator/(int64_t b, Vec4I64 a) { return { b / a.x, b / a.y, b / a.z, b / a.w, }; }
|
||||||
|
inline Vec4I64 &operator/=(Vec4I64 &a, int64_t b) { a = a / b; return a; }
|
||||||
|
inline Vec2I64 operator+(Vec2I64 a, Vec2I64 b) { return { a.x + b.x, a.y + b.y, }; }
|
||||||
|
inline Vec2I64 &operator+=(Vec2I64 &a, Vec2I64 b) { a = a + b; return a; }
|
||||||
|
inline Vec2I64 operator+(Vec2I64 a, int64_t b) { return { a.x + b, a.y + b, }; }
|
||||||
|
inline Vec2I64 operator+(int64_t b, Vec2I64 a) { return { a.x + b, a.y + b, }; }
|
||||||
|
inline Vec2I64 &operator+=(Vec2I64 &a, int64_t b) { a = a + b; return a; }
|
||||||
|
inline Vec3I64 operator+(Vec3I64 a, Vec3I64 b) { return { a.x + b.x, a.y + b.y, a.z + b.z, }; }
|
||||||
|
inline Vec3I64 &operator+=(Vec3I64 &a, Vec3I64 b) { a = a + b; return a; }
|
||||||
|
inline Vec3I64 operator+(Vec3I64 a, int64_t b) { return { a.x + b, a.y + b, a.z + b, }; }
|
||||||
|
inline Vec3I64 operator+(int64_t b, Vec3I64 a) { return { a.x + b, a.y + b, a.z + b, }; }
|
||||||
|
inline Vec3I64 &operator+=(Vec3I64 &a, int64_t b) { a = a + b; return a; }
|
||||||
|
inline Vec4I64 operator+(Vec4I64 a, Vec4I64 b) { return { a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w, }; }
|
||||||
|
inline Vec4I64 &operator+=(Vec4I64 &a, Vec4I64 b) { a = a + b; return a; }
|
||||||
|
inline Vec4I64 operator+(Vec4I64 a, int64_t b) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
|
||||||
|
inline Vec4I64 operator+(int64_t b, Vec4I64 a) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
|
||||||
|
inline Vec4I64 &operator+=(Vec4I64 &a, int64_t b) { a = a + b; return a; }
|
||||||
|
inline Vec2I64 operator-(Vec2I64 a, Vec2I64 b) { return { a.x - b.x, a.y - b.y, }; }
|
||||||
|
inline Vec2I64 &operator-=(Vec2I64 &a, Vec2I64 b) { a = a - b; return a; }
|
||||||
|
inline Vec2I64 operator-(Vec2I64 a, int64_t b) { return { a.x - b, a.y - b, }; }
|
||||||
|
inline Vec2I64 operator-(int64_t b, Vec2I64 a) { return { b - a.x, b - a.y, }; }
|
||||||
|
inline Vec2I64 &operator-=(Vec2I64 &a, int64_t b) { a = a - b; return a; }
|
||||||
|
inline Vec3I64 operator-(Vec3I64 a, Vec3I64 b) { return { a.x - b.x, a.y - b.y, a.z - b.z, }; }
|
||||||
|
inline Vec3I64 &operator-=(Vec3I64 &a, Vec3I64 b) { a = a - b; return a; }
|
||||||
|
inline Vec3I64 operator-(Vec3I64 a, int64_t b) { return { a.x - b, a.y - b, a.z - b, }; }
|
||||||
|
inline Vec3I64 operator-(int64_t b, Vec3I64 a) { return { b - a.x, b - a.y, b - a.z, }; }
|
||||||
|
inline Vec3I64 &operator-=(Vec3I64 &a, int64_t b) { a = a - b; return a; }
|
||||||
|
inline Vec4I64 operator-(Vec4I64 a, Vec4I64 b) { return { a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w, }; }
|
||||||
|
inline Vec4I64 &operator-=(Vec4I64 &a, Vec4I64 b) { a = a - b; return a; }
|
||||||
|
inline Vec4I64 operator-(Vec4I64 a, int64_t b) { return { a.x - b, a.y - b, a.z - b, a.w - b, }; }
|
||||||
|
inline Vec4I64 operator-(int64_t b, Vec4I64 a) { return { b - a.x, b - a.y, b - a.z, b - a.w, }; }
|
||||||
|
inline Vec4I64 &operator-=(Vec4I64 &a, int64_t b) { a = a - b; return a; }
|
||||||
|
|
||||||
|
inline Rect2F64 Rect2_Center(Vec2F64 center, Vec2F64 halfdim) { return {center.x - halfdim.x, center.y - halfdim.y, center.x + halfdim.x, center.y + halfdim.y}; }
|
||||||
|
inline Vec2F64 CalcSize(Rect2F64 r) { return { r.max.x - r.min.x, r.max.y - r.min.y }; }
|
||||||
|
inline Rect2F64 Rect2_Size(double x, double y, double w, double h) { return { x, y, x + w, y + h }; }
|
||||||
|
inline Rect2F64 Rect2_Size(Vec2F64 pos, Vec2F64 size) { return { pos.x, pos.y, pos.x + size.w, pos.y + size.h }; }
|
||||||
|
inline Rect2F64 Rect2_MinMax(Vec2F64 p0, Vec2F64 p1) { return {p0.x, p0.y, p1.x, p1.y }; }
|
||||||
|
inline Rect2F64 ShrinkByHalfSize(Rect2F64 rect, Vec2F64 half_size) { return {rect.min.x + half_size.x, rect.min.y + half_size.y, rect.max.x - half_size.x, rect.max.y - half_size.y}; }
|
||||||
|
inline Rect2F64 ExpandByHalfSize(Rect2F64 rect, Vec2F64 half_size) { return {rect.min.x - half_size.x, rect.min.y - half_size.y, rect.max.x + half_size.x, rect.max.y + half_size.y}; }
|
||||||
|
|
||||||
|
inline Rect2F64 Rect2_AdjustMinMax(Vec2F64 p0, Vec2F64 p1) {
|
||||||
|
Rect2F64 result = {
|
||||||
|
Minimum(p0.x, p1.x), Minimum(p0.y, p1.y),
|
||||||
|
Maximum(p0.x, p1.x), Maximum(p0.y, p1.y),
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double GetXSize(Rect2F64 r) { return r.max.x - r.min.x; }
|
||||||
|
inline double GetYSize(Rect2F64 r) { return r.max.y - r.min.y; }
|
||||||
|
inline Vec2F64 GetSize(Rect2F64 r) { return r.max - r.min; }
|
||||||
|
|
||||||
|
inline double Lerp(double from, double to, double t) {return (1-t)*from + t*to;}
|
||||||
|
inline Vec2F64 Lerp(Vec2F64 from, Vec2F64 to, double t) {return (1-t)*from + t*to;}
|
||||||
|
inline Vec3F64 Lerp(Vec3F64 from, Vec3F64 to, double t) {return (1-t)*from + t*to;}
|
||||||
|
inline Vec4F64 Lerp(Vec4F64 from, Vec4F64 to, double t) {return (1-t)*from + t*to;}
|
||||||
|
|
||||||
|
inline Vec2 Vec2F64ToVec2(Vec2F64 a) {return Vec2{(float)a.x, (float)a.y};}
|
||||||
|
inline Rect2 Rect2F64ToRect2(Rect2F64 a) { return Rect2{(float)a.min.x, (float)a.min.y, (float)a.max.x, (float)a.max.y}; }
|
||||||
1819
src/vendor/glad/glad.c
vendored
Executable file
1819
src/vendor/glad/glad.c
vendored
Executable file
File diff suppressed because it is too large
Load Diff
3655
src/vendor/glad/glad.h
vendored
Executable file
3655
src/vendor/glad/glad.h
vendored
Executable file
File diff suppressed because it is too large
Load Diff
311
src/vendor/glad/khrplatform.h
vendored
Executable file
311
src/vendor/glad/khrplatform.h
vendored
Executable file
@@ -0,0 +1,311 @@
|
|||||||
|
#ifndef __khrplatform_h_
|
||||||
|
#define __khrplatform_h_
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
** copy of this software and/or associated documentation files (the
|
||||||
|
** "Materials"), to deal in the Materials without restriction, including
|
||||||
|
** without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||||
|
** permit persons to whom the Materials are furnished to do so, subject to
|
||||||
|
** the following conditions:
|
||||||
|
**
|
||||||
|
** The above copyright notice and this permission notice shall be included
|
||||||
|
** in all copies or substantial portions of the Materials.
|
||||||
|
**
|
||||||
|
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Khronos platform-specific types and definitions.
|
||||||
|
*
|
||||||
|
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||||
|
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||||
|
* The last semantic modification to khrplatform.h was at commit ID:
|
||||||
|
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||||
|
*
|
||||||
|
* Adopters may modify this file to suit their platform. Adopters are
|
||||||
|
* encouraged to submit platform specific modifications to the Khronos
|
||||||
|
* group so that they can be included in future versions of this file.
|
||||||
|
* Please submit changes by filing pull requests or issues on
|
||||||
|
* the EGL Registry repository linked above.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* See the Implementer's Guidelines for information about where this file
|
||||||
|
* should be located on your system and for more details of its use:
|
||||||
|
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||||
|
*
|
||||||
|
* This file should be included as
|
||||||
|
* #include <KHR/khrplatform.h>
|
||||||
|
* by Khronos client API header files that use its types and defines.
|
||||||
|
*
|
||||||
|
* The types in khrplatform.h should only be used to define API-specific types.
|
||||||
|
*
|
||||||
|
* Types defined in khrplatform.h:
|
||||||
|
* khronos_int8_t signed 8 bit
|
||||||
|
* khronos_uint8_t unsigned 8 bit
|
||||||
|
* khronos_int16_t signed 16 bit
|
||||||
|
* khronos_uint16_t unsigned 16 bit
|
||||||
|
* khronos_int32_t signed 32 bit
|
||||||
|
* khronos_uint32_t unsigned 32 bit
|
||||||
|
* khronos_int64_t signed 64 bit
|
||||||
|
* khronos_uint64_t unsigned 64 bit
|
||||||
|
* khronos_intptr_t signed same number of bits as a pointer
|
||||||
|
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||||
|
* khronos_ssize_t signed size
|
||||||
|
* khronos_usize_t unsigned size
|
||||||
|
* khronos_float_t signed 32 bit floating point
|
||||||
|
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||||
|
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||||
|
* nanoseconds
|
||||||
|
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||||
|
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||||
|
* only be used as a base type when a client API's boolean type is
|
||||||
|
* an enum. Client APIs which use an integer or other type for
|
||||||
|
* booleans cannot use this as the base type for their boolean.
|
||||||
|
*
|
||||||
|
* Tokens defined in khrplatform.h:
|
||||||
|
*
|
||||||
|
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||||
|
*
|
||||||
|
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||||
|
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||||
|
*
|
||||||
|
* Calling convention macros defined in this file:
|
||||||
|
* KHRONOS_APICALL
|
||||||
|
* KHRONOS_APIENTRY
|
||||||
|
* KHRONOS_APIATTRIBUTES
|
||||||
|
*
|
||||||
|
* These may be used in function prototypes as:
|
||||||
|
*
|
||||||
|
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||||
|
* int arg1,
|
||||||
|
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||||
|
# define KHRONOS_STATIC 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* Definition of KHRONOS_APICALL
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
* This precedes the return type of the function in the function prototype.
|
||||||
|
*/
|
||||||
|
#if defined(KHRONOS_STATIC)
|
||||||
|
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||||
|
* header compatible with static linking. */
|
||||||
|
# define KHRONOS_APICALL
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
# define KHRONOS_APICALL __declspec(dllimport)
|
||||||
|
#elif defined (__SYMBIAN32__)
|
||||||
|
# define KHRONOS_APICALL IMPORT_C
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
# define KHRONOS_APICALL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* Definition of KHRONOS_APIENTRY
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
* This follows the return type of the function and precedes the function
|
||||||
|
* name in the function prototype.
|
||||||
|
*/
|
||||||
|
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||||
|
/* Win32 but not WinCE */
|
||||||
|
# define KHRONOS_APIENTRY __stdcall
|
||||||
|
#else
|
||||||
|
# define KHRONOS_APIENTRY
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* Definition of KHRONOS_APIATTRIBUTES
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
* This follows the closing parenthesis of the function prototype arguments.
|
||||||
|
*/
|
||||||
|
#if defined (__ARMCC_2__)
|
||||||
|
#define KHRONOS_APIATTRIBUTES __softfp
|
||||||
|
#else
|
||||||
|
#define KHRONOS_APIATTRIBUTES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* basic type definitions
|
||||||
|
*-----------------------------------------------------------------------*/
|
||||||
|
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Using <stdint.h>
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
typedef int32_t khronos_int32_t;
|
||||||
|
typedef uint32_t khronos_uint32_t;
|
||||||
|
typedef int64_t khronos_int64_t;
|
||||||
|
typedef uint64_t khronos_uint64_t;
|
||||||
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
/*
|
||||||
|
* To support platform where unsigned long cannot be used interchangeably with
|
||||||
|
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||||
|
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||||
|
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||||
|
* unsigned long long or similar (this results in different C++ name mangling).
|
||||||
|
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||||
|
* platforms where the size of a pointer is larger than the size of long.
|
||||||
|
*/
|
||||||
|
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||||
|
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||||
|
#define KHRONOS_USE_INTPTR_T
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(__VMS ) || defined(__sgi)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Using <inttypes.h>
|
||||||
|
*/
|
||||||
|
#include <inttypes.h>
|
||||||
|
typedef int32_t khronos_int32_t;
|
||||||
|
typedef uint32_t khronos_uint32_t;
|
||||||
|
typedef int64_t khronos_int64_t;
|
||||||
|
typedef uint64_t khronos_uint64_t;
|
||||||
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
|
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Win32
|
||||||
|
*/
|
||||||
|
typedef __int32 khronos_int32_t;
|
||||||
|
typedef unsigned __int32 khronos_uint32_t;
|
||||||
|
typedef __int64 khronos_int64_t;
|
||||||
|
typedef unsigned __int64 khronos_uint64_t;
|
||||||
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
|
#elif defined(__sun__) || defined(__digital__)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sun or Digital
|
||||||
|
*/
|
||||||
|
typedef int khronos_int32_t;
|
||||||
|
typedef unsigned int khronos_uint32_t;
|
||||||
|
#if defined(__arch64__) || defined(_LP64)
|
||||||
|
typedef long int khronos_int64_t;
|
||||||
|
typedef unsigned long int khronos_uint64_t;
|
||||||
|
#else
|
||||||
|
typedef long long int khronos_int64_t;
|
||||||
|
typedef unsigned long long int khronos_uint64_t;
|
||||||
|
#endif /* __arch64__ */
|
||||||
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
|
#elif 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hypothetical platform with no float or int64 support
|
||||||
|
*/
|
||||||
|
typedef int khronos_int32_t;
|
||||||
|
typedef unsigned int khronos_uint32_t;
|
||||||
|
#define KHRONOS_SUPPORT_INT64 0
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 0
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic fallback
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
typedef int32_t khronos_int32_t;
|
||||||
|
typedef uint32_t khronos_uint32_t;
|
||||||
|
typedef int64_t khronos_int64_t;
|
||||||
|
typedef uint64_t khronos_uint64_t;
|
||||||
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Types that are (so far) the same on all platforms
|
||||||
|
*/
|
||||||
|
typedef signed char khronos_int8_t;
|
||||||
|
typedef unsigned char khronos_uint8_t;
|
||||||
|
typedef signed short int khronos_int16_t;
|
||||||
|
typedef unsigned short int khronos_uint16_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||||
|
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||||
|
* to be the only LLP64 architecture in current use.
|
||||||
|
*/
|
||||||
|
#ifdef KHRONOS_USE_INTPTR_T
|
||||||
|
typedef intptr_t khronos_intptr_t;
|
||||||
|
typedef uintptr_t khronos_uintptr_t;
|
||||||
|
#elif defined(_WIN64)
|
||||||
|
typedef signed long long int khronos_intptr_t;
|
||||||
|
typedef unsigned long long int khronos_uintptr_t;
|
||||||
|
#else
|
||||||
|
typedef signed long int khronos_intptr_t;
|
||||||
|
typedef unsigned long int khronos_uintptr_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN64)
|
||||||
|
typedef signed long long int khronos_ssize_t;
|
||||||
|
typedef unsigned long long int khronos_usize_t;
|
||||||
|
#else
|
||||||
|
typedef signed long int khronos_ssize_t;
|
||||||
|
typedef unsigned long int khronos_usize_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if KHRONOS_SUPPORT_FLOAT
|
||||||
|
/*
|
||||||
|
* Float type
|
||||||
|
*/
|
||||||
|
typedef float khronos_float_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if KHRONOS_SUPPORT_INT64
|
||||||
|
/* Time types
|
||||||
|
*
|
||||||
|
* These types can be used to represent a time interval in nanoseconds or
|
||||||
|
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||||
|
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||||
|
* time the system booted). The Unadjusted System Time is an unsigned
|
||||||
|
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||||
|
* may be either signed or unsigned.
|
||||||
|
*/
|
||||||
|
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||||
|
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dummy value used to pad enum types to 32 bits.
|
||||||
|
*/
|
||||||
|
#ifndef KHRONOS_MAX_ENUM
|
||||||
|
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enumerated boolean type
|
||||||
|
*
|
||||||
|
* Values other than zero should be considered to be true. Therefore
|
||||||
|
* comparisons should not be made against KHRONOS_TRUE.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
KHRONOS_FALSE = 0,
|
||||||
|
KHRONOS_TRUE = 1,
|
||||||
|
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||||
|
} khronos_boolean_enum_t;
|
||||||
|
|
||||||
|
#endif /* __khrplatform_h_ */
|
||||||
2
src/vendor/stb_truetype.c
vendored
Executable file
2
src/vendor/stb_truetype.c
vendored
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
#include "stb_truetype.h"
|
||||||
5077
src/vendor/stb_truetype.h
vendored
Executable file
5077
src/vendor/stb_truetype.h
vendored
Executable file
File diff suppressed because it is too large
Load Diff
180
src/visualize/backup.cpp
Executable file
180
src/visualize/backup.cpp
Executable file
@@ -0,0 +1,180 @@
|
|||||||
|
#if 0
|
||||||
|
S8_String filename = S8_Lit("../assets/SkillCraft.csv");
|
||||||
|
PLOT_CSV = CSV_Load(G_Perm, filename);
|
||||||
|
UI_Notification(2.0f, "CSV: %s, loaded!", filename.str);
|
||||||
|
|
||||||
|
CSV_Column *gap_between_pacs = PLOT_CSV.columns + 12;
|
||||||
|
CSV_Column *apm = PLOT_CSV.columns + 5;
|
||||||
|
|
||||||
|
PLOT_X = gap_between_pacs->floats;
|
||||||
|
PLOT_XLabel = gap_between_pacs->name;
|
||||||
|
PLOT_Y = apm->floats;
|
||||||
|
PLOT_YLabel = apm->name;
|
||||||
|
PLOT_Title = PLOT_CSV.filename;
|
||||||
|
PLOT_DataSize = (int)PLOT_CSV.row_count;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
for (MU_DroppedFile *iter = Mu->window->first_dropped_file; iter; iter = iter->next) {
|
||||||
|
S8_String filename = S8_Make(iter->filename, iter->filename_size);
|
||||||
|
PLOT_CSV = CSV_Load(G_Perm, filename);
|
||||||
|
UI_Notification(2.0f, "CSV: %s, loaded!", filename.str);
|
||||||
|
|
||||||
|
CSV_Column *gap_between_pacs = PLOT_CSV.columns + 12;
|
||||||
|
CSV_Column *apm = PLOT_CSV.columns + 5;
|
||||||
|
|
||||||
|
PLOT_X = gap_between_pacs->floats;
|
||||||
|
PLOT_XLabel = gap_between_pacs->name;
|
||||||
|
PLOT_Y = apm->floats;
|
||||||
|
PLOT_YLabel = apm->name;
|
||||||
|
PLOT_Title = PLOT_CSV.filename;
|
||||||
|
PLOT_DataSize = (int)PLOT_CSV.row_count;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int SPREAD_MaxY;
|
||||||
|
int SPREAD_MaxX;
|
||||||
|
MAP_Map SPREAD_Map;
|
||||||
|
Vec2 SPREAD_Pos;
|
||||||
|
|
||||||
|
if (!PLOT_Initialized) {
|
||||||
|
SPREAD_Pos = Mu->window->sizef;
|
||||||
|
}
|
||||||
|
|
||||||
|
MAP_InsertEvent(&SPREAD_Map, {0 + current_series->id * 2, data_point_count}, iter.event);
|
||||||
|
MAP_InsertEvent(&SPREAD_Map, {1 + current_series->id * 2, data_point_count}, iter.event);
|
||||||
|
SPREAD_MaxY = data_point_count;
|
||||||
|
SPREAD_MaxX = 4;
|
||||||
|
|
||||||
|
|
||||||
|
Vec4 spreadsheet_background = COLOR_spreadsheet_background;
|
||||||
|
//
|
||||||
|
// SPREADSHEET
|
||||||
|
//
|
||||||
|
Vec2 spreadsheet_pos = SPREAD_Pos * G_Camera.zoom;
|
||||||
|
Rect2 spreadsheet_rect = {};
|
||||||
|
{
|
||||||
|
float xcell_size = 150.f * G_Camera.zoom;
|
||||||
|
float ycell_size = R2_RenderO.font->size * G_Camera.zoom;
|
||||||
|
float all_columns_size = xcell_size * SPREAD_MaxX;
|
||||||
|
Vec2 spreadsheet_size = {all_columns_size, -(SPREAD_MaxY + 1) * ycell_size};
|
||||||
|
Vec2 spreadcamoffset = spreadsheet_pos - G_Camera.pos;
|
||||||
|
spreadsheet_rect = Rect2Size(spreadcamoffset, spreadsheet_size);
|
||||||
|
if (spreadsheet_rect.min.y > spreadsheet_rect.max.y) {
|
||||||
|
Swap(spreadsheet_rect.min.y, spreadsheet_rect.max.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
int visible_row_begin = 0;
|
||||||
|
int visible_row_end = 0;
|
||||||
|
int visible_col_begin = 0;
|
||||||
|
int visible_col_end = 0;
|
||||||
|
{
|
||||||
|
float ystart = (-Mu->window->sizef.y + spreadcamoffset.y) / ycell_size;
|
||||||
|
float ysize = Mu->window->sizef.y / ycell_size;
|
||||||
|
visible_row_begin = Clamp((int)floorf(ystart) - 1, 0, SPREAD_MaxY);
|
||||||
|
visible_row_end = Clamp((int)(ceilf(ystart + ysize)), 0, SPREAD_MaxY);
|
||||||
|
|
||||||
|
float when_spreadcamoffset_incamera_offset = Clamp(Mu->window->sizef.x, 0.f, spreadcamoffset.x);
|
||||||
|
|
||||||
|
float xstart = (spreadcamoffset.x - when_spreadcamoffset_incamera_offset) / xcell_size;
|
||||||
|
float xsize = Mu->window->sizef.x / xcell_size;
|
||||||
|
visible_col_begin = Clamp((int)floorf(xstart) - 1, 0, SPREAD_MaxX);
|
||||||
|
visible_col_end = Clamp((int)(ceilf(xstart + xsize)), 0, SPREAD_MaxX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the background rect
|
||||||
|
R2_DrawRect(spreadsheet_rect, spreadsheet_background);
|
||||||
|
R2_DrawCircle(spreadcamoffset, 4.0f, {1, 0, 0, 1}); //@debug
|
||||||
|
|
||||||
|
// Draw label
|
||||||
|
{
|
||||||
|
Rect2 rect = spreadsheet_rect;
|
||||||
|
Vec2 size = GetSize(spreadsheet_rect);
|
||||||
|
float label_scale = G_Camera.zoom * 1.0f;
|
||||||
|
float label_t = 0;
|
||||||
|
if (G_Camera.zoom > 0.5f) {
|
||||||
|
label_t = 0.0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float t = ((1.f / G_Camera.zoom) - 2.0f) / 4.0f / 32.f;
|
||||||
|
label_t = Clamp01(Lerp(0, 1, t));
|
||||||
|
if (t <= 1.0f) {
|
||||||
|
label_scale = Lerp(label_scale, 1.0f, PingPong(t));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
label_scale = Lerp(0.5f, 1.0f, PingPong(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 plot_midtop = rect.max;
|
||||||
|
plot_midtop.x -= size.x / 2;
|
||||||
|
plot_midtop.y += 2;
|
||||||
|
|
||||||
|
Vec2 plot_mid = rect.min + (rect.max - rect.min) / 2.f;
|
||||||
|
|
||||||
|
// We want T that is 0 when zoom is 4 or less
|
||||||
|
|
||||||
|
Vec2 label_pos = Lerp(plot_midtop, plot_mid, label_t);
|
||||||
|
|
||||||
|
S8_String label_string = PLOT_Title;
|
||||||
|
Vec2 top_label_size = R2_GetStringSize(label_string, label_scale);
|
||||||
|
label_pos.x -= top_label_size.x / 2;
|
||||||
|
R2_DrawString(label_pos, label_string, text_color, label_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
float x_accumulator = 0;
|
||||||
|
if (should_render) {
|
||||||
|
for (int col_i = visible_col_begin; col_i < visible_col_end; col_i += 1) {
|
||||||
|
// Draw the first row, the label row
|
||||||
|
{
|
||||||
|
Vec2 cell_pos = Vec2{x_accumulator, -ycell_size * (float)(1.f)} + spreadcamoffset;
|
||||||
|
S8_String label = S8_Lit("");
|
||||||
|
if (col_i == 0) label = PLOT_XLabel;
|
||||||
|
if (col_i == 1) label = PLOT_YLabel;
|
||||||
|
|
||||||
|
Rect2 rect = R2_DrawString(cell_pos, label, disappearing_text_color, G_Camera.zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int row_i = visible_row_begin; row_i < visible_row_end; row_i += 1) {
|
||||||
|
VIS_Event *event = MAP_GetEvent(&SPREAD_Map, {col_i, row_i});
|
||||||
|
if (!event) continue;
|
||||||
|
float point = event->point[(col_i % 2)];
|
||||||
|
S8_String it = S8_Format(G_Frame, "%f", point);
|
||||||
|
|
||||||
|
// Draw the cell
|
||||||
|
// We start from row 2 (row 1 is reserved)
|
||||||
|
{
|
||||||
|
Vec2 cell_pos = Vec2{x_accumulator, -ycell_size * (float)(row_i + 2.f)} + spreadcamoffset;
|
||||||
|
Rect2 cell_rect = Rect2Size(cell_pos, Vec2{xcell_size, ycell_size});
|
||||||
|
R2_DrawRectOutline(cell_rect, cell_outline_color);
|
||||||
|
|
||||||
|
Vec2 string_size = R2_GetStringSize(it, G_Camera.zoom);
|
||||||
|
Vec2 string_pos = cell_pos + (GetSize(cell_rect) / 2 - string_size / 2);
|
||||||
|
R2_DrawString(string_pos, it, disappearing_text_color, G_Camera.zoom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x_accumulator += xcell_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Zoomed out view object moving
|
||||||
|
//
|
||||||
|
if (should_render == false) {
|
||||||
|
if (AreColliding(rect, Mu->window->mouse.posf)) {
|
||||||
|
R2_DrawRectOutline(rect, {1, 0, 0, 1});
|
||||||
|
|
||||||
|
if (Mu->window->mouse.right.down) {
|
||||||
|
PLOT_Pos += Mu->window->mouse.delta_pos_normalized * 1000.0f / G_Camera.zoom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (AreColliding(spreadsheet_rect, Mu->window->mouse.posf)) {
|
||||||
|
R2_DrawRectOutline(spreadsheet_rect, {1, 0, 0, 1});
|
||||||
|
if (Mu->window->mouse.right.down) {
|
||||||
|
SPREAD_Pos += Mu->window->mouse.delta_pos_normalized * 1000.0f / G_Camera.zoom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/visualize/globals.cpp
Executable file
18
src/visualize/globals.cpp
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
float COLOR_Tint = 0.0;
|
||||||
|
Vec4 COLOR_Caret = HSLToRGB({COLOR_Tint, 0.4f, 0.60f, 1.0f});
|
||||||
|
Vec4 COLOR_white = {1, 1, 1, 1};
|
||||||
|
Vec4 COLOR_plot_lines = COLOR_white;
|
||||||
|
Vec4 COLOR_on_hover_rect = HSLToRGB({COLOR_Tint, 0.2f, 0.96f, 1.0f});
|
||||||
|
Vec4 COLOR_cell_outline = COLOR_white;
|
||||||
|
Vec4 COLOR_background_plot = HSLToRGB({COLOR_Tint, 0.2f, 0.95f, 1.0f});
|
||||||
|
Vec4 COLOR_spreadsheet_background = COLOR_background_plot;
|
||||||
|
Vec4 COLOR_text = HSLToRGB({COLOR_Tint, 0.2f, 0.7f, 1.0f});
|
||||||
|
Vec4 COLOR_highlighted_text = HSLToRGB({COLOR_Tint, 0.4f, 0.9f, 1.0f});
|
||||||
|
Vec4 COLOR_data_point = HSLToRGB({COLOR_Tint, 0.4f, 0.60f, 1.0f});
|
||||||
|
Vec4 COLOR_highlighted_data_point = HSLToRGB({COLOR_Tint, 0.4f, 0.90f, 1.0f});
|
||||||
|
Vec4 COLOR_on_hover_outline_rect = COLOR_text;
|
||||||
|
|
||||||
|
Camera G_Camera = {
|
||||||
|
{1.0f, 1.0f}
|
||||||
|
};
|
||||||
|
int G_ProcessID;
|
||||||
48
src/visualize/prototype_audio.cpp
Executable file
48
src/visualize/prototype_audio.cpp
Executable file
@@ -0,0 +1,48 @@
|
|||||||
|
#include "visualize.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
|
||||||
|
MU_Context *Mu;
|
||||||
|
int Audio_Start;
|
||||||
|
int Audio_I;
|
||||||
|
float T;
|
||||||
|
void Audio_Callback(MU_Context *mu, uint16_t *buffer, uint32_t samples_to_fill) {
|
||||||
|
for (uint32_t i = 0; i < samples_to_fill; i += 2) {
|
||||||
|
float s = sinf(T * 0.3157f);
|
||||||
|
float ss = sinf(T * (0.3157f + 0.02f));
|
||||||
|
float comb = s + ss;
|
||||||
|
float value = comb * 500.0f;
|
||||||
|
buffer[i] = (int16_t)value;
|
||||||
|
buffer[i + 1] = (int16_t)value;
|
||||||
|
T += 0.1f;
|
||||||
|
|
||||||
|
Audio_Start += 1;
|
||||||
|
if (Audio_I < 48000) {
|
||||||
|
Audio_I += 1;
|
||||||
|
VIS_PlotKind(VIS_PlotKind_Scatter);
|
||||||
|
VIS_Series(0);
|
||||||
|
VIS_Point((float)Audio_I, s);
|
||||||
|
VIS_Series(1);
|
||||||
|
VIS_Point((float)Audio_I, ss);
|
||||||
|
VIS_Series(2);
|
||||||
|
VIS_Point((float)Audio_I, comb);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer[i] = 0;
|
||||||
|
buffer[i + 1] = 0;
|
||||||
|
MU_Quit(Mu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
VIS_Clear();
|
||||||
|
MU_Params params = {};
|
||||||
|
params.sound_callback = Audio_Callback;
|
||||||
|
Mu = MU_Start(params);
|
||||||
|
|
||||||
|
while (MU_Update(Mu)) {
|
||||||
|
if (Mu->window->key[MU_KEY_ESCAPE].down) {
|
||||||
|
MU_Quit(Mu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
328
src/visualize/prototype_data_handling.cpp
Executable file
328
src/visualize/prototype_data_handling.cpp
Executable file
@@ -0,0 +1,328 @@
|
|||||||
|
RandomSeed PLOT_RandomSeed = {13};
|
||||||
|
|
||||||
|
bool DH_Initialized;
|
||||||
|
|
||||||
|
struct R3_Vertex {
|
||||||
|
Vec3 pos;
|
||||||
|
Vec2 tex;
|
||||||
|
Vec3 color;
|
||||||
|
};
|
||||||
|
#define R3_VERTEX_BUFFER_SIZE 4096 * 8
|
||||||
|
|
||||||
|
enum R3_CommandKind {
|
||||||
|
R3_COMMAND_NONE,
|
||||||
|
R3_COMMAND_CUBE,
|
||||||
|
R3_COMMAND_QUAD,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R3_CommandCube {
|
||||||
|
R3_Vertex *vertices;
|
||||||
|
int vertex_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R3_Command {
|
||||||
|
R3_CommandKind kind;
|
||||||
|
R3_Command *next;
|
||||||
|
int64_t vertex_count;
|
||||||
|
R3_Vertex *vertices;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R3_Camera {
|
||||||
|
float pitch, roll, head;
|
||||||
|
Vec3 pos;
|
||||||
|
|
||||||
|
float old_roll;
|
||||||
|
float approach_speed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R3_Render {
|
||||||
|
MA_Arena arena;
|
||||||
|
|
||||||
|
unsigned atlas_id;
|
||||||
|
R3_Camera camera;
|
||||||
|
R3_Command *first_command;
|
||||||
|
R3_Command *last_command;
|
||||||
|
unsigned vbo, vao;
|
||||||
|
R2_Shader shader;
|
||||||
|
};
|
||||||
|
R3_Render R3;
|
||||||
|
|
||||||
|
unsigned R3_UploadTexture(void *data, int x, int y) {
|
||||||
|
unsigned id = 0;
|
||||||
|
glCreateTextures(GL_TEXTURE_2D, 1, &id);
|
||||||
|
glTextureParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTextureParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTextureParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTextureParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTextureStorage2D(id, 1, GL_RGBA8, x, y);
|
||||||
|
glTextureSubImage2D(id, 0, 0, 0, x, y, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R3_Init() {
|
||||||
|
MA_Init(&R3.arena);
|
||||||
|
R3.arena.alignment = 0;
|
||||||
|
|
||||||
|
// IO_Assert(MA_GetAlignOffset(sizeof(R3_Vertex), R3.arena.alignment) == 0);
|
||||||
|
{
|
||||||
|
glCreateBuffers(1, &R3.vbo);
|
||||||
|
glNamedBufferStorage(R3.vbo, R3_VERTEX_BUFFER_SIZE * sizeof(R3_Vertex), 0, GL_DYNAMIC_STORAGE_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
glCreateVertexArrays(1, &R3.vao);
|
||||||
|
|
||||||
|
GLint vbuf_index = 0;
|
||||||
|
glVertexArrayVertexBuffer(R3.vao, vbuf_index, R3.vbo, 0, sizeof(struct R3_Vertex));
|
||||||
|
|
||||||
|
GLint a_pos = 0;
|
||||||
|
glVertexArrayAttribFormat(R3.vao, a_pos, 3, GL_FLOAT, GL_FALSE, offsetof(struct R3_Vertex, pos));
|
||||||
|
glVertexArrayAttribBinding(R3.vao, a_pos, vbuf_index);
|
||||||
|
glEnableVertexArrayAttrib(R3.vao, a_pos);
|
||||||
|
|
||||||
|
GLint a_tex = 1;
|
||||||
|
glVertexArrayAttribFormat(R3.vao, a_tex, 2, GL_FLOAT, GL_FALSE, offsetof(struct R3_Vertex, tex));
|
||||||
|
glVertexArrayAttribBinding(R3.vao, a_tex, vbuf_index);
|
||||||
|
glEnableVertexArrayAttrib(R3.vao, a_tex);
|
||||||
|
|
||||||
|
GLint a_color = 2;
|
||||||
|
glVertexArrayAttribFormat(R3.vao, a_color, 3, GL_FLOAT, GL_FALSE, offsetof(struct R3_Vertex, color));
|
||||||
|
glVertexArrayAttribBinding(R3.vao, a_color, vbuf_index);
|
||||||
|
glEnableVertexArrayAttrib(R3.vao, a_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char *glsl_vshader =
|
||||||
|
"#version 450 core\n"
|
||||||
|
"#line " R2_STRINGIFY(__LINE__) "\n"
|
||||||
|
R"==(
|
||||||
|
|
||||||
|
layout (location=0) uniform mat4 UPerspective;
|
||||||
|
layout (location=0) in vec3 InPos;
|
||||||
|
layout (location=1) in vec2 InTex;
|
||||||
|
layout (location=2) in vec3 InColor;
|
||||||
|
|
||||||
|
|
||||||
|
out gl_PerVertex { vec4 gl_Position; }; // required because of ARB_separate_shader_objects
|
||||||
|
out vec2 OutTex;
|
||||||
|
out vec4 OutColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 pos = vec4(InPos, 1);
|
||||||
|
pos = UPerspective * pos;
|
||||||
|
|
||||||
|
gl_Position = pos;
|
||||||
|
OutTex = InTex;
|
||||||
|
OutColor = vec4(InColor, 1);
|
||||||
|
}
|
||||||
|
)==";
|
||||||
|
|
||||||
|
char *glsl_fshader =
|
||||||
|
"#version 450 core\n"
|
||||||
|
"#line " R2_STRINGIFY(__LINE__) "\n"
|
||||||
|
R"==(
|
||||||
|
|
||||||
|
in vec2 InTex;
|
||||||
|
in vec4 InColor;
|
||||||
|
|
||||||
|
layout (binding=0) uniform sampler2D Tex0;
|
||||||
|
layout (location=0) out vec4 OutColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
OutColor = InColor;
|
||||||
|
}
|
||||||
|
)==";
|
||||||
|
R3.shader = R2_CreateShader(glsl_vshader, glsl_fshader);
|
||||||
|
}
|
||||||
|
R3.last_command = R3.first_command = MA_PushStruct(&R3.arena, R3_Command);
|
||||||
|
}
|
||||||
|
|
||||||
|
R3_Command *R3_GetCommand(R3_CommandKind kind) {
|
||||||
|
R3_Command *cmd = R3.last_command;
|
||||||
|
if (cmd->kind == kind) {
|
||||||
|
if (cmd->vertex_count + 36 < R3_VERTEX_BUFFER_SIZE) {
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R3_Command *command = MA_PushStruct(&R3.arena, R3_Command);
|
||||||
|
command->kind = kind;
|
||||||
|
SLL_QUEUE_ADD(R3.first_command, R3.last_command, command);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R3_PushQuad(Rect3 rect, Vec3 color, bool colored = false) {
|
||||||
|
Vec3 top = color;
|
||||||
|
Vec3 side = color;
|
||||||
|
if (colored) {
|
||||||
|
side *= 0.8f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2 tex = {};
|
||||||
|
R3_Vertex vertices[] = {
|
||||||
|
// counter clockwise winding (opengl default)
|
||||||
|
{{rect.min.x, rect.min.y, rect.min.z}, {tex.min.x, tex.min.y}, side},
|
||||||
|
{{rect.min.x, rect.min.y, rect.max.z}, {tex.min.x, tex.max.y}, side},
|
||||||
|
{{rect.min.x, rect.max.y, rect.max.z}, {tex.max.x, tex.max.y}, side},
|
||||||
|
{{rect.min.x, rect.min.y, rect.min.z}, {tex.min.x, tex.min.y}, side},
|
||||||
|
{{rect.min.x, rect.max.y, rect.max.z}, {tex.max.x, tex.max.y}, side},
|
||||||
|
{{rect.min.x, rect.max.y, rect.min.z}, {tex.max.x, tex.min.y}, side},
|
||||||
|
{{rect.max.x, rect.max.y, rect.min.z}, {tex.max.x, tex.max.y}, side},
|
||||||
|
{{rect.min.x, rect.min.y, rect.min.z}, {tex.min.x, tex.min.y}, side},
|
||||||
|
{{rect.min.x, rect.max.y, rect.min.z}, {tex.min.x, tex.max.y}, side},
|
||||||
|
{{rect.max.x, rect.max.y, rect.min.z}, {tex.max.x, tex.max.y}, side},
|
||||||
|
{{rect.max.x, rect.min.y, rect.min.z}, {tex.max.x, tex.min.y}, side},
|
||||||
|
{{rect.min.x, rect.min.y, rect.min.z}, {tex.min.x, tex.min.y}, side},
|
||||||
|
{{rect.max.x, rect.min.y, rect.max.z}, {tex.max.x, tex.max.y}, side},
|
||||||
|
{{rect.min.x, rect.min.y, rect.min.z}, {tex.min.x, tex.min.y}, side},
|
||||||
|
{{rect.max.x, rect.min.y, rect.min.z}, {tex.max.x, tex.min.y}, side},
|
||||||
|
{{rect.max.x, rect.min.y, rect.max.z}, {tex.max.x, tex.max.y}, side},
|
||||||
|
{{rect.min.x, rect.min.y, rect.max.z}, {tex.min.x, tex.max.y}, side},
|
||||||
|
{{rect.min.x, rect.min.y, rect.min.z}, {tex.min.x, tex.min.y}, side},
|
||||||
|
{{rect.min.x, rect.max.y, rect.max.z}, {tex.min.x, tex.max.y}, side},
|
||||||
|
{{rect.min.x, rect.min.y, rect.max.z}, {tex.min.x, tex.min.y}, side},
|
||||||
|
{{rect.max.x, rect.min.y, rect.max.z}, {tex.max.x, tex.min.y}, side},
|
||||||
|
{{rect.max.x, rect.max.y, rect.max.z}, {tex.max.x, tex.max.y}, side},
|
||||||
|
{{rect.min.x, rect.max.y, rect.max.z}, {tex.min.x, tex.max.y}, side},
|
||||||
|
{{rect.max.x, rect.min.y, rect.max.z}, {tex.max.x, tex.min.y}, side},
|
||||||
|
{{rect.max.x, rect.max.y, rect.max.z}, {tex.max.x, tex.max.y}, side},
|
||||||
|
{{rect.max.x, rect.min.y, rect.min.z}, {tex.min.x, tex.min.y}, side},
|
||||||
|
{{rect.max.x, rect.max.y, rect.min.z}, {tex.max.x, tex.min.y}, side},
|
||||||
|
{{rect.max.x, rect.min.y, rect.min.z}, {tex.min.x, tex.min.y}, side},
|
||||||
|
{{rect.max.x, rect.max.y, rect.max.z}, {tex.max.x, tex.max.y}, side},
|
||||||
|
{{rect.max.x, rect.min.y, rect.max.z}, {tex.min.x, tex.max.y}, side},
|
||||||
|
{{rect.max.x, rect.max.y, rect.max.z}, {tex.max.x, tex.max.y}, top},
|
||||||
|
{{rect.max.x, rect.max.y, rect.min.z}, {tex.max.x, tex.min.y}, top},
|
||||||
|
{{rect.min.x, rect.max.y, rect.min.z}, {tex.min.x, tex.min.y}, top},
|
||||||
|
{{rect.max.x, rect.max.y, rect.max.z}, {tex.max.x, tex.max.y}, top},
|
||||||
|
{{rect.min.x, rect.max.y, rect.min.z}, {tex.min.x, tex.min.y}, top},
|
||||||
|
{{rect.min.x, rect.max.y, rect.max.z}, {tex.min.x, tex.max.y}, top},
|
||||||
|
};
|
||||||
|
R3_Command *command = R3_GetCommand(R3_COMMAND_CUBE);
|
||||||
|
void *result = MA_PushCopy(&R3.arena, vertices, sizeof(vertices));
|
||||||
|
if (command->vertices == 0) command->vertices = (R3_Vertex *)result;
|
||||||
|
command->vertex_count += lengthof(vertices);
|
||||||
|
}
|
||||||
|
|
||||||
|
void R3_EndFrame(Vec2 window_size, M4x4 matrix) {
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
|
||||||
|
glBindProgramPipeline(R3.shader.pipeline);
|
||||||
|
glProgramUniformMatrix4fv(R3.shader.vshader, 0, 1, GL_TRUE, (float *)matrix.e);
|
||||||
|
|
||||||
|
for (R3_Command *it = R3.first_command->next; it; it = it->next) {
|
||||||
|
switch (it->kind) {
|
||||||
|
case R3_COMMAND_CUBE: {
|
||||||
|
glNamedBufferSubData(R3.vbo, 0, it->vertex_count * sizeof(R3_Vertex), it->vertices);
|
||||||
|
glBindVertexArray(R3.vao);
|
||||||
|
GLint sampler_id = 0;
|
||||||
|
glBindTextureUnit(sampler_id, R3.atlas_id);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, (int)it->vertex_count);
|
||||||
|
} break;
|
||||||
|
case R3_COMMAND_QUAD: {
|
||||||
|
// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glNamedBufferSubData(R3.vbo, 0, it->vertex_count * sizeof(R3_Vertex), it->vertices);
|
||||||
|
glBindVertexArray(R3.vao);
|
||||||
|
GLint sampler_id = 0;
|
||||||
|
glBindTextureUnit(sampler_id, R3.atlas_id);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, (int)it->vertex_count);
|
||||||
|
} break;
|
||||||
|
case R3_COMMAND_NONE:
|
||||||
|
IO_InvalidDefaultCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_Reset(&R3.arena);
|
||||||
|
R3.last_command = R3.first_command = MA_PushStruct(&R3.arena, R3_Command);
|
||||||
|
}
|
||||||
|
|
||||||
|
M4x4 GetCameraTransform(R3_Camera camera) {
|
||||||
|
M4x4 result = M4_RotationZ(camera.old_roll) * M4_RotationX(camera.pitch) * M4_RotationY(camera.head) * M4_Translation(-camera.pos);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
M4x4 GetCameraRotation(R3_Camera camera) {
|
||||||
|
M4x4 result = M4_RotationZ(camera.old_roll) * M4_RotationX(camera.pitch) * M4_RotationY(camera.head);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String Serialize(MA_Arena *arena, R3_Camera *camera) {
|
||||||
|
S8_String result = S8_Format(arena, "Camera :: Pos:[%.02f %.02f %.02f] Pitch:%f Roll:%f Head:%f", camera->pos.x, camera->pos.y, camera->pos.z, camera->pitch, camera->roll, camera->head);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PROTOTYPE_DataHandling() {
|
||||||
|
if (!DH_Initialized) {
|
||||||
|
DH_Initialized = true;
|
||||||
|
R3_Init();
|
||||||
|
|
||||||
|
MU_ToggleFPSMode(Mu->window);
|
||||||
|
R3.camera.pitch = 0.70f;
|
||||||
|
R3.camera.head = -0.75f;
|
||||||
|
R3.camera.pos = {20, 20, 20};
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
R3_Camera &camera = R3.camera;
|
||||||
|
camera.pitch -= (float)Mu->window->mouse.delta_pos_normalized.y * 4;
|
||||||
|
camera.head += (float)Mu->window->mouse.delta_pos_normalized.x * 4;
|
||||||
|
M4x4 r = GetCameraRotation(camera);
|
||||||
|
Vec3 zdirection = GetBasisZ(r);
|
||||||
|
Vec3 xdirection = GetBasisX(r);
|
||||||
|
if (Mu->window->key[MU_KEY_W].down) {
|
||||||
|
camera.pos -= zdirection * 0.05f;
|
||||||
|
}
|
||||||
|
if (Mu->window->key[MU_KEY_S].down) {
|
||||||
|
camera.pos += zdirection * 0.05f;
|
||||||
|
}
|
||||||
|
if (Mu->window->key[MU_KEY_A].down) {
|
||||||
|
camera.pos -= xdirection * 0.05f;
|
||||||
|
}
|
||||||
|
if (Mu->window->key[MU_KEY_D].down) {
|
||||||
|
camera.pos += xdirection * 0.05f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float t1 = -0.1f;
|
||||||
|
float t2 = 0.1f;
|
||||||
|
float s = 15.f;
|
||||||
|
R3_PushQuad({t1, t1, t1, s, t2, t2}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1, t1, t1, t2, s, t2}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1, t1, t1, t2, t2, s}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1 + s, t1, t1, t2 + s, s, t2}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1, t1, t1 + s, t2, s, t2 + s}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1 + s, t1, t1 + s, t2 + s, s, t2 + s}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1 + s, t1, t1, t2 + s, t2, s}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1, t1, t1 + s, s, t2, t2 + s}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1, t1 + s, t1, s, t2 + s, t2}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1, t1 + s, t1, t2, t2 + s, s}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1 + s, t1 + s, t1, t2 + s, t2 + s, s}, {1, 0, 0});
|
||||||
|
R3_PushQuad({t1, t1 + s, t1 + s, s, t2 + s, t2 + s}, {1, 0, 0});
|
||||||
|
|
||||||
|
// R3_PushQuad({t1, t1 + 15.f, t1, 0.1f, 0.1f + 15.f, 15}, {1, 0, 0});
|
||||||
|
// for (float y = 0; y < 1.0f; y += 0.1f) {
|
||||||
|
// float of = y * 15.0f;
|
||||||
|
// R3_PushQuad({ + of, -0.1f, 0, 0.1f + of, 0.1f, 15.f}, {1, 0, 0});
|
||||||
|
// }
|
||||||
|
|
||||||
|
RandomSeed seed = {13};
|
||||||
|
for (float i = 0; i < 10; i += 1) {
|
||||||
|
Vec3 p = {
|
||||||
|
(float)GetRandomNormal(&seed) * 10.f,
|
||||||
|
(float)GetRandomNormal(&seed) * 10.f,
|
||||||
|
(float)GetRandomNormal(&seed) * 10.f,
|
||||||
|
};
|
||||||
|
|
||||||
|
float t = 0.25f;
|
||||||
|
Rect3 rect = {p.x - t, p.y - t, p.z - t, p.x + t, p.y + t, p.z + t};
|
||||||
|
R3_PushQuad(rect, COLOR_data_point.rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
M4x4 matrix = M4_Identity();
|
||||||
|
M4x4 camera_transform = GetCameraTransform(R3.camera);
|
||||||
|
matrix = M4_Perspective(65, Mu->window->sizef.x, Mu->window->sizef.y, 0.1f, 100.f) * camera_transform * matrix;
|
||||||
|
R3_EndFrame(Mu->window->sizef, matrix);
|
||||||
|
|
||||||
|
R2_DebugString("%s", Serialize(G_Frame, &R3.camera).str);
|
||||||
|
}
|
||||||
138
src/visualize/prototype_server_client.cpp
Executable file
138
src/visualize/prototype_server_client.cpp
Executable file
@@ -0,0 +1,138 @@
|
|||||||
|
bool SV_Initialized;
|
||||||
|
int DH_ProcessID = 0;
|
||||||
|
|
||||||
|
#define DEFAULT_BUFLEN 512
|
||||||
|
#define DEFAULT_PORT "27015"
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "Ws2_32.lib")
|
||||||
|
#pragma comment(lib, "Mswsock.lib")
|
||||||
|
#pragma comment(lib, "AdvApi32.lib")
|
||||||
|
|
||||||
|
// Server: https://learn.microsoft.com/en-us/windows/win32/winsock/complete-server-code
|
||||||
|
// Client: https://learn.microsoft.com/en-us/windows/win32/winsock/complete-client-code
|
||||||
|
|
||||||
|
void PROTOTYPE_ServerClient() {
|
||||||
|
if (!SV_Initialized) {
|
||||||
|
SV_Initialized = true;
|
||||||
|
|
||||||
|
int iResult;
|
||||||
|
WSADATA wsaData;
|
||||||
|
|
||||||
|
SOCKET ListenSocket = INVALID_SOCKET;
|
||||||
|
SOCKET ClientSocket = INVALID_SOCKET;
|
||||||
|
|
||||||
|
struct addrinfo *result = NULL;
|
||||||
|
struct addrinfo hints;
|
||||||
|
|
||||||
|
int iSendResult;
|
||||||
|
char recvbuf[DEFAULT_BUFLEN];
|
||||||
|
int recvbuflen = DEFAULT_BUFLEN;
|
||||||
|
|
||||||
|
// Initialize Winsock
|
||||||
|
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||||
|
if (iResult != 0) {
|
||||||
|
IO_FatalErrorf("WSAStartup failed with error: %d\n", iResult);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZeroMemory(&hints, sizeof(hints));
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_protocol = IPPROTO_TCP;
|
||||||
|
hints.ai_flags = AI_PASSIVE;
|
||||||
|
|
||||||
|
// Resolve the server address and port
|
||||||
|
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
|
||||||
|
if (iResult != 0) {
|
||||||
|
IO_FatalErrorf("getaddrinfo failed with error: %d\n", iResult);
|
||||||
|
WSACleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a SOCKET for the server to listen for client connections.
|
||||||
|
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
||||||
|
if (ListenSocket == INVALID_SOCKET) {
|
||||||
|
IO_FatalErrorf("socket failed with error: %ld\n", WSAGetLastError());
|
||||||
|
freeaddrinfo(result);
|
||||||
|
WSACleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the TCP listening socket
|
||||||
|
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
|
||||||
|
if (iResult == SOCKET_ERROR) {
|
||||||
|
IO_FatalErrorf("bind failed with error: %d\n", WSAGetLastError());
|
||||||
|
freeaddrinfo(result);
|
||||||
|
closesocket(ListenSocket);
|
||||||
|
WSACleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result);
|
||||||
|
|
||||||
|
iResult = listen(ListenSocket, SOMAXCONN);
|
||||||
|
if (iResult == SOCKET_ERROR) {
|
||||||
|
IO_FatalErrorf("listen failed with error: %d\n", WSAGetLastError());
|
||||||
|
closesocket(ListenSocket);
|
||||||
|
WSACleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept a client socket
|
||||||
|
ClientSocket = accept(ListenSocket, NULL, NULL);
|
||||||
|
if (ClientSocket == INVALID_SOCKET) {
|
||||||
|
IO_FatalErrorf("accept failed with error: %d\n", WSAGetLastError());
|
||||||
|
closesocket(ListenSocket);
|
||||||
|
WSACleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No longer need server socket
|
||||||
|
closesocket(ListenSocket);
|
||||||
|
|
||||||
|
// Receive until the peer shuts down the connection
|
||||||
|
do {
|
||||||
|
|
||||||
|
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
|
||||||
|
if (iResult > 0) {
|
||||||
|
printf("Bytes received: %d\n", iResult);
|
||||||
|
|
||||||
|
// Echo the buffer back to the sender
|
||||||
|
iSendResult = send(ClientSocket, recvbuf, iResult, 0);
|
||||||
|
if (iSendResult == SOCKET_ERROR) {
|
||||||
|
IO_FatalErrorf("send failed with error: %d\n", WSAGetLastError());
|
||||||
|
closesocket(ClientSocket);
|
||||||
|
WSACleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("Bytes sent: %d\n", iSendResult);
|
||||||
|
}
|
||||||
|
else if (iResult == 0)
|
||||||
|
printf("Connection closing...\n");
|
||||||
|
else {
|
||||||
|
IO_FatalErrorf("recv failed with error: %d\n", WSAGetLastError());
|
||||||
|
closesocket(ClientSocket);
|
||||||
|
WSACleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (iResult > 0);
|
||||||
|
|
||||||
|
// shutdown the connection since we're done
|
||||||
|
iResult = shutdown(ClientSocket, SD_SEND);
|
||||||
|
if (iResult == SOCKET_ERROR) {
|
||||||
|
IO_FatalErrorf("shutdown failed with error: %d\n", WSAGetLastError());
|
||||||
|
closesocket(ClientSocket);
|
||||||
|
WSACleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
closesocket(ClientSocket);
|
||||||
|
WSACleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
216
src/visualize/prototype_text_editor.cpp
Executable file
216
src/visualize/prototype_text_editor.cpp
Executable file
@@ -0,0 +1,216 @@
|
|||||||
|
|
||||||
|
bool TE_Initialized;
|
||||||
|
int64_t TE_Caret;
|
||||||
|
|
||||||
|
struct TE_Buffer {
|
||||||
|
S8_String string;
|
||||||
|
int64_t capacity;
|
||||||
|
};
|
||||||
|
char TE_Text[128 * 4];
|
||||||
|
TE_Buffer TE_MainBuffer = {{TE_Text}, lengthof(TE_Text)};
|
||||||
|
|
||||||
|
void TE_MemoryMove(char *dest, char *src, int64_t size) {
|
||||||
|
if (dest == src)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char *ends = src + size;
|
||||||
|
bool overlap_source_behind_dest = dest >= src && dest < ends;
|
||||||
|
if (overlap_source_behind_dest) {
|
||||||
|
char *endd = dest + size;
|
||||||
|
while (size--) {
|
||||||
|
*--endd = *--ends;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while (size--) {
|
||||||
|
*dest++ = *src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TE_MemoryCopy(char *dst, char *src, int64_t size) {
|
||||||
|
while (size--) *dst++ = *src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TE_Insert(TE_Buffer *buffer, S8_String string, int64_t pos) {
|
||||||
|
int64_t capacity_remaining = buffer->capacity - buffer->string.len;
|
||||||
|
pos = Clamp(pos, (int64_t)0, buffer->string.len);
|
||||||
|
string.len = Clamp(string.len, (int64_t)0, capacity_remaining);
|
||||||
|
|
||||||
|
char *address_to_insert_at = buffer->string.str + pos;
|
||||||
|
char *end_of_address_to_insert_at = address_to_insert_at + string.len;
|
||||||
|
int64_t existing_size_to_move = buffer->string.len - pos;
|
||||||
|
|
||||||
|
TE_MemoryMove(end_of_address_to_insert_at, address_to_insert_at, existing_size_to_move);
|
||||||
|
TE_MemoryMove(address_to_insert_at, string.str, string.len);
|
||||||
|
buffer->string.len += string.len;
|
||||||
|
|
||||||
|
IO_Assert(buffer->string.len <= buffer->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TE_Replace(TE_Buffer *buffer, S8_String replacement, int64_t pos_region_to_replace, int64_t size_region_to_replace) {
|
||||||
|
int64_t capacity_remaining = buffer->capacity - buffer->string.len;
|
||||||
|
int64_t start_pos_region_to_replace = Clamp(pos_region_to_replace, (int64_t)0, buffer->string.len);
|
||||||
|
int64_t end_pos_region_to_replace = Clamp(pos_region_to_replace + size_region_to_replace, (int64_t)0, buffer->string.len);
|
||||||
|
int64_t actual_size_region_to_replace = end_pos_region_to_replace - start_pos_region_to_replace;
|
||||||
|
|
||||||
|
IO_Assert(actual_size_region_to_replace >= 0);
|
||||||
|
|
||||||
|
char *start_of_region_to_replace = buffer->string.str + start_pos_region_to_replace;
|
||||||
|
char *end_of_region_to_replace = buffer->string.str + end_pos_region_to_replace;
|
||||||
|
char *new_home_of_old_memory = start_of_region_to_replace + replacement.len;
|
||||||
|
int64_t existing_size_to_move = buffer->string.len - end_pos_region_to_replace;
|
||||||
|
|
||||||
|
TE_MemoryMove(new_home_of_old_memory, end_of_region_to_replace, existing_size_to_move);
|
||||||
|
TE_MemoryMove(start_of_region_to_replace, replacement.str, replacement.len);
|
||||||
|
buffer->string.len = buffer->string.len - actual_size_region_to_replace + replacement.len;
|
||||||
|
|
||||||
|
IO_Assert(buffer->string.len <= buffer->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int TE_SearchFlags;
|
||||||
|
enum {
|
||||||
|
TE_SearchFlags_IgnoreCase = 1,
|
||||||
|
TE_SearchFlags_SearchLeft = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
int64_t TE_Search(S8_String string, S8_String seek_string, int64_t start_seek_pos, TE_SearchFlags flags) {
|
||||||
|
int64_t start_seek = Clamp(start_seek_pos, (int64_t)0, string.len - 1);
|
||||||
|
if (flags & TE_SearchFlags_SearchLeft) {
|
||||||
|
for (int64_t i = start_seek; i >= 0; i -= 1) {
|
||||||
|
int64_t seek_size = Clamp(seek_string.len, (int64_t)0, string.len - i);
|
||||||
|
S8_String to_try = S8_Make(string.str + i, seek_size);
|
||||||
|
if (S8_AreEqual(to_try, seek_string, flags)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int64_t i = start_seek; i < seek_string.len; i += 1) {
|
||||||
|
int64_t seek_size = Clamp(seek_string.len, (int64_t)0, string.len - i);
|
||||||
|
S8_String to_try = S8_Make(string.str + i, seek_size);
|
||||||
|
if (S8_AreEqual(to_try, seek_string, flags)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t CaretBackward() {
|
||||||
|
int64_t original_cursor = TE_Caret;
|
||||||
|
IO_Assert(TE_Caret <= TE_MainBuffer.string.len);
|
||||||
|
IO_Assert(TE_Caret >= 0);
|
||||||
|
while (TE_Caret) {
|
||||||
|
char it = TE_MainBuffer.string.str[--TE_Caret];
|
||||||
|
if ((it & 0b11000000) != 0b10000000) break; // We dont want to stop on continuation byte
|
||||||
|
}
|
||||||
|
TE_Caret = Clamp(TE_Caret, (int64_t)0, TE_MainBuffer.string.len);
|
||||||
|
|
||||||
|
int64_t step = original_cursor - TE_Caret;
|
||||||
|
return step;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CaretForward() {
|
||||||
|
IO_Assert(TE_Caret <= TE_MainBuffer.string.len);
|
||||||
|
IO_Assert(TE_Caret >= 0);
|
||||||
|
UTF32_Result decode = UTF_ConvertUTF8ToUTF32(TE_MainBuffer.string.str + TE_Caret, (int)(TE_MainBuffer.string.len - TE_Caret));
|
||||||
|
TE_Caret += decode.advance;
|
||||||
|
TE_Caret = Clamp(TE_Caret, (int64_t)0, TE_MainBuffer.string.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PROTOTYPE_TextEditor() {
|
||||||
|
if (!TE_Initialized) {
|
||||||
|
TE_Initialized = !TE_Initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mu->window->key[MU_KEY_LEFT].raw_press) {
|
||||||
|
if (Mu->window->key[MU_KEY_CONTROL].down) {
|
||||||
|
CaretBackward();
|
||||||
|
int64_t pos = TE_Search(TE_MainBuffer.string, S8_Lit(" "), TE_Caret, TE_SearchFlags_SearchLeft);
|
||||||
|
if (pos == -1) pos = 0;
|
||||||
|
TE_Caret = pos;
|
||||||
|
}
|
||||||
|
else CaretBackward();
|
||||||
|
}
|
||||||
|
if (Mu->window->key[MU_KEY_RIGHT].raw_press) {
|
||||||
|
if (Mu->window->key[MU_KEY_CONTROL].down) {
|
||||||
|
// CaretForward();
|
||||||
|
int64_t pos = TE_Search(TE_MainBuffer.string, S8_Lit(" "), TE_Caret, 0);
|
||||||
|
if (pos == -1) pos = TE_MainBuffer.string.len;
|
||||||
|
TE_Caret = pos;
|
||||||
|
}
|
||||||
|
else CaretForward();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UTF8_Iter iter = UTF8_IterateEx((char *)Mu->window->user_text8, Mu->window->user_text8_count); iter.item; UTF8_Advance(&iter)) {
|
||||||
|
uint32_t it = iter.item;
|
||||||
|
|
||||||
|
if (it == '\b') {
|
||||||
|
int64_t step = CaretBackward();
|
||||||
|
TE_Replace(&TE_MainBuffer, S8_Lit(""), TE_Caret, step);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (it == 127) {
|
||||||
|
// @warning: If I want to implement stuff like seek over all whitespace then it needs to be on search
|
||||||
|
// function level
|
||||||
|
int64_t pos = TE_Search(TE_MainBuffer.string, S8_Lit(" "), TE_Caret, TE_SearchFlags_SearchLeft);
|
||||||
|
if (pos == -1) pos = 0;
|
||||||
|
TE_Replace(&TE_MainBuffer, S8_Lit(""), pos, TE_Caret);
|
||||||
|
TE_Caret = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
TE_Insert(&TE_MainBuffer, S8_Make(iter.str + iter.i, iter.utf8_codepoint_byte_size), TE_Caret);
|
||||||
|
CaretForward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Vec2 window_middle = Mu->window->sizef / 2.0f;
|
||||||
|
Vec2 string_size = R2_GetStringSize(TE_MainBuffer.string);
|
||||||
|
|
||||||
|
Vec2 text_pos = window_middle - Vec2{string_size.x / 2, 0};
|
||||||
|
R2_StringParams params = R2_MakeStringParams(TE_MainBuffer.string, text_pos, COLOR_text, true);
|
||||||
|
params.rects_arena = G_Frame;
|
||||||
|
R2_StringMeasure measure = R2_BaseDrawString(params);
|
||||||
|
|
||||||
|
// Draw the caret
|
||||||
|
{
|
||||||
|
Rect2 caret_rect = {};
|
||||||
|
if (TE_Caret == 0) {
|
||||||
|
if (TE_MainBuffer.string.len) {
|
||||||
|
R2_RectNode *it = measure.first_rect;
|
||||||
|
caret_rect = {it->rect.min.x - 2, it->rect.min.y, it->rect.min.x + 2, it->rect.max.y};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
caret_rect = {window_middle.x - 2, window_middle.y, window_middle.x + 2, window_middle.y + R2.font->size / 2};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (TE_Caret == TE_MainBuffer.string.len) {
|
||||||
|
caret_rect = {measure.rect.max.x - 2, measure.rect.min.y, measure.rect.max.x + 2, measure.rect.max.y};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int glyph_i = 0;
|
||||||
|
for (R2_RectNode *it = measure.first_rect; it; it = it->next) {
|
||||||
|
if ((glyph_i + it->utf8_codepoint_byte_size) == TE_Caret) {
|
||||||
|
caret_rect = {it->rect.max.x - 2, it->rect.min.y, it->rect.max.x + 2, it->rect.max.y};
|
||||||
|
}
|
||||||
|
glyph_i += it->utf8_codepoint_byte_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R2_DrawRect(caret_rect, COLOR_Caret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t text_index = 0;
|
||||||
|
if (Mu->window->mouse.left.down) {
|
||||||
|
for (R2_RectNode *it = measure.first_rect; it; it = it->next) {
|
||||||
|
if (AreColliding(it->rect, Mu->window->mouse.posf)) {
|
||||||
|
TE_Caret = text_index;
|
||||||
|
|
||||||
|
Vec4 color = COLOR_Caret;
|
||||||
|
color.a = 0.2f;
|
||||||
|
R2_DrawRect(it->rect, color);
|
||||||
|
}
|
||||||
|
text_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
949
src/visualize/render2d.cpp
Executable file
949
src/visualize/render2d.cpp
Executable file
@@ -0,0 +1,949 @@
|
|||||||
|
// #include "vendor/stb_truetype.h"
|
||||||
|
// #include "vendor/glad/glad.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern MA_Arena *G_Frame;
|
||||||
|
|
||||||
|
struct Font_Glyph {
|
||||||
|
// Scaling transform applied to these
|
||||||
|
Vec2 size, offset;
|
||||||
|
float x_advance;
|
||||||
|
float left_side_bearing;
|
||||||
|
|
||||||
|
Rect2 atlas_bounding_box;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Font {
|
||||||
|
uint8_t *atlas;
|
||||||
|
Font_Glyph glyphs[96];
|
||||||
|
int glyph_count;
|
||||||
|
|
||||||
|
uint32_t first_char, last_char;
|
||||||
|
|
||||||
|
unsigned texture_id;
|
||||||
|
|
||||||
|
// This is for oversampling
|
||||||
|
// 0.5 = we draw the font as 2 times smaller then it is.
|
||||||
|
// Should lead to better quality result.
|
||||||
|
float scaling_transform;
|
||||||
|
|
||||||
|
// scaling transform is applied to these
|
||||||
|
float size;
|
||||||
|
float ascent;
|
||||||
|
float descent;
|
||||||
|
float line_gap;
|
||||||
|
|
||||||
|
// scaling factor not applied, not sure if these will be useful
|
||||||
|
float scale;
|
||||||
|
float em_scale;
|
||||||
|
|
||||||
|
Rect2 white_texture_bounding_box;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R2_Atlas {
|
||||||
|
uint8_t *bitmap;
|
||||||
|
Vec2I sizei;
|
||||||
|
Vec2 size;
|
||||||
|
Vec2 inverse_size;
|
||||||
|
Vec2I cursor;
|
||||||
|
int biggest_height;
|
||||||
|
Rect2 white_texture_bounding_box;
|
||||||
|
unsigned texture_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct File {
|
||||||
|
uint8_t *content;
|
||||||
|
uint64_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct R2_Vertex R2_Vertex;
|
||||||
|
struct R2_Vertex {
|
||||||
|
Vec2 pos;
|
||||||
|
Vec2 tex;
|
||||||
|
Vec4 color;
|
||||||
|
};
|
||||||
|
#define R2_Vertex_GLSL \
|
||||||
|
"layout(location=0) uniform Vec2 U_InvHalfScreenSize;\n" \
|
||||||
|
"layout(location=0) in Vec2 IN_Pos;\n" \
|
||||||
|
"layout(location=1) in Vec2 IN_Tex;\n" \
|
||||||
|
"layout(location=2) in Vec4 IN_Color;\n"
|
||||||
|
|
||||||
|
#define R2_STRINGIFY2(x) #x
|
||||||
|
#define R2_STRINGIFY(x) R2_STRINGIFY2(x)
|
||||||
|
#define R2_GLSL_HEADER(x) \
|
||||||
|
"#version 450 core\n" \
|
||||||
|
"#line " R2_STRINGIFY(x) "\n" \
|
||||||
|
"#define Vec2 vec2\n" \
|
||||||
|
"#define Vec3 vec3\n" \
|
||||||
|
"#define Vec4 vec4\n"
|
||||||
|
|
||||||
|
struct R2_VertexNode {
|
||||||
|
R2_VertexNode *next;
|
||||||
|
int count;
|
||||||
|
R2_Vertex vertices[1024 * 64];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R2_VertexList {
|
||||||
|
R2_VertexNode *first;
|
||||||
|
R2_VertexNode *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R2_Shader {
|
||||||
|
unsigned pipeline, fshader, vshader;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct V_Circle {
|
||||||
|
Vec2 pos;
|
||||||
|
float color_index;
|
||||||
|
};
|
||||||
|
const int R2_MaxCircleCount = 1024 * 64;
|
||||||
|
const int R2_CircleBufferSize = R2_MaxCircleCount * sizeof(V_Circle);
|
||||||
|
|
||||||
|
struct R2_Render {
|
||||||
|
bool initialized;
|
||||||
|
R2_VertexList base_vertex_list;
|
||||||
|
R2_VertexList ui_vertex_list;
|
||||||
|
|
||||||
|
R2_VertexList *vertex_list;
|
||||||
|
Font *font;
|
||||||
|
Font font_small;
|
||||||
|
Font font_medium;
|
||||||
|
Font font_large;
|
||||||
|
Font font_very_large;
|
||||||
|
R2_Atlas atlas;
|
||||||
|
unsigned vbo, vao, pipeline, fshader, vshader;
|
||||||
|
|
||||||
|
int64_t vertex_count; //@stats
|
||||||
|
MA_Arena arena;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
MA_Arena arena;
|
||||||
|
R2_Shader shader;
|
||||||
|
float radius;
|
||||||
|
|
||||||
|
Array<Vec4> colors;
|
||||||
|
int color_iter;
|
||||||
|
|
||||||
|
int instances_to_render;
|
||||||
|
int mesh_vertex_count;
|
||||||
|
int mesh_segment_count;
|
||||||
|
unsigned vbo_positions;
|
||||||
|
unsigned vbo_mesh;
|
||||||
|
unsigned vao;
|
||||||
|
} circle;
|
||||||
|
};
|
||||||
|
R2_Render R2;
|
||||||
|
|
||||||
|
struct R2_RectNode {
|
||||||
|
R2_RectNode *next;
|
||||||
|
Rect2 rect;
|
||||||
|
int utf8_codepoint_byte_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R2_StringMeasure {
|
||||||
|
R2_RectNode *first_rect;
|
||||||
|
R2_RectNode *last_rect;
|
||||||
|
Rect2 rect;
|
||||||
|
};
|
||||||
|
|
||||||
|
// @todo: Add multiple font support
|
||||||
|
// Need to add a way to output multiple draw calls instead of 1
|
||||||
|
// So a command buffer of sorts
|
||||||
|
///
|
||||||
|
|
||||||
|
File ReadFile(MA_Arena *arena, S8_String filename) {
|
||||||
|
IO_Assertf(filename.str[filename.len] == 0, "String was not null terminated");
|
||||||
|
|
||||||
|
File result = {};
|
||||||
|
FILE *f = fopen(filename.str, "r");
|
||||||
|
if (f) {
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long fsize = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
result.content = (uint8_t *)MA_PushSizeNonZeroed(arena, fsize + 1);
|
||||||
|
fread(result.content, fsize, 1, f);
|
||||||
|
|
||||||
|
result.size = fsize;
|
||||||
|
result.content[result.size] = 0;
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
R2_Atlas R2_CreateAtlas(MA_Arena *arena, Vec2I size) {
|
||||||
|
R2_Atlas result = {};
|
||||||
|
result.sizei = size;
|
||||||
|
result.size = Vec2IToVec2(size);
|
||||||
|
result.inverse_size.x = 1.f / result.size.x;
|
||||||
|
result.inverse_size.y = 1.f / result.size.y;
|
||||||
|
result.bitmap = MA_PushArray(arena, uint8_t, size.x * size.y);
|
||||||
|
|
||||||
|
// Add a whitebox first for rectangle rendering
|
||||||
|
for (int y = 0; y < 16; y++) {
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
uint8_t *dst = result.bitmap + x + y * result.sizei.x;
|
||||||
|
*dst = 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Skipping some pixels to avoid linear interpolation on edges
|
||||||
|
result.white_texture_bounding_box = {2.f * result.inverse_size.x, 2.f / result.size.y, 14.f * result.inverse_size.x, 14.f / result.size.y};
|
||||||
|
result.cursor.x += 16;
|
||||||
|
result.biggest_height += 16;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2 R2_PackBitmapInvertY(R2_Atlas *atlas, uint8_t *bitmap, int width, int height) {
|
||||||
|
// Packing into a texture atlas
|
||||||
|
// @Inefficient The algorithm is a simplest thing I had in mind, first we advance
|
||||||
|
// through the atlas in X packing consecutive glyphs. After we get to the end of the row
|
||||||
|
// we advance to the next row by the Y size of the biggest packed glyph. If we get to the
|
||||||
|
// end of atlas and fail to pack everything the app panics.
|
||||||
|
|
||||||
|
int spacing = 4;
|
||||||
|
if (atlas->cursor.x + width > atlas->sizei.x) {
|
||||||
|
if (atlas->cursor.y + height < atlas->sizei.y) {
|
||||||
|
atlas->cursor.x = 0;
|
||||||
|
atlas->cursor.y += atlas->biggest_height + spacing;
|
||||||
|
} else {
|
||||||
|
IO_Assert(!"Error while packing a font into atlas. Atlas size for this font scale is a bit too small");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the bitmap with inverted Y
|
||||||
|
uint8_t *src = bitmap;
|
||||||
|
for (int y = atlas->cursor.y + height - 1; y >= atlas->cursor.y; y--) {
|
||||||
|
for (int x = atlas->cursor.x; x < atlas->cursor.x + width; x++) {
|
||||||
|
uint8_t *dst = atlas->bitmap + x + y * atlas->sizei.x;
|
||||||
|
*dst = *src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 size = {(float)width * atlas->inverse_size.x, (float)height / atlas->size.y};
|
||||||
|
Vec2 pos = {(float)atlas->cursor.x * atlas->inverse_size.x, (float)atlas->cursor.y / atlas->size.y};
|
||||||
|
Rect2 result = {pos.x, pos.y, pos.x + size.x, pos.y + size.y};
|
||||||
|
|
||||||
|
atlas->cursor.x += width + spacing;
|
||||||
|
if (height > atlas->biggest_height) {
|
||||||
|
atlas->biggest_height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Font Font_Create(R2_Atlas *atlas, float size, S8_String path, float oversampling) {
|
||||||
|
MA_Scratch scratch;
|
||||||
|
File font_file = ReadFile(scratch, path);
|
||||||
|
|
||||||
|
Font result = {};
|
||||||
|
result.scaling_transform = 1.f / oversampling;
|
||||||
|
result.size = oversampling * size;
|
||||||
|
|
||||||
|
result.first_char = ' ';
|
||||||
|
result.last_char = '~';
|
||||||
|
stbtt_fontinfo stb_font;
|
||||||
|
if (font_file.content) {
|
||||||
|
int success = stbtt_InitFont(&stb_font, font_file.content, 0);
|
||||||
|
if (success) {
|
||||||
|
int ascent, descent, gap;
|
||||||
|
stbtt_GetFontVMetrics(&stb_font, &ascent, &descent, &gap);
|
||||||
|
result.scale = stbtt_ScaleForPixelHeight(&stb_font, result.size);
|
||||||
|
result.em_scale = stbtt_ScaleForMappingEmToPixels(&stb_font, result.size);
|
||||||
|
result.ascent = (float)ascent * result.scale;
|
||||||
|
result.descent = (float)descent * result.scale;
|
||||||
|
result.line_gap = (float)gap * result.scale;
|
||||||
|
result.white_texture_bounding_box = atlas->white_texture_bounding_box;
|
||||||
|
|
||||||
|
for (uint32_t ascii_symbol = result.first_char; ascii_symbol <= result.last_char; ascii_symbol++) {
|
||||||
|
int width, height, xoff, yoff;
|
||||||
|
uint8_t *bitmap = (uint8_t *)stbtt_GetCodepointBitmap(&stb_font, 0, result.scale, ascii_symbol, &width, &height, &xoff, &yoff);
|
||||||
|
defer { stbtt_FreeBitmap(bitmap, 0); };
|
||||||
|
|
||||||
|
int x_advance, left_side_bearing;
|
||||||
|
stbtt_GetCodepointHMetrics(&stb_font, ascii_symbol, &x_advance, &left_side_bearing);
|
||||||
|
|
||||||
|
Font_Glyph *g = result.glyphs + result.glyph_count++;
|
||||||
|
g->atlas_bounding_box = R2_PackBitmapInvertY(atlas, bitmap, width, height);
|
||||||
|
g->size = {(float)width, (float)height};
|
||||||
|
|
||||||
|
// Offset y needs to be inverted cause bitmap has inverted Y
|
||||||
|
g->offset = {(float)xoff, -(g->size.y + (float)yoff)};
|
||||||
|
g->x_advance = x_advance * result.scale;
|
||||||
|
g->left_side_bearing = left_side_bearing * result.scale;
|
||||||
|
|
||||||
|
// Apply scaling transform
|
||||||
|
g->offset *= result.scaling_transform;
|
||||||
|
g->x_advance *= result.scaling_transform;
|
||||||
|
g->left_side_bearing *= result.scaling_transform;
|
||||||
|
g->size *= result.scaling_transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.ascent *= result.scaling_transform;
|
||||||
|
result.descent *= result.scaling_transform;
|
||||||
|
result.size *= result.scaling_transform;
|
||||||
|
result.line_gap *= result.scaling_transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Font_Glyph &Font_GetGlyph(Font *font, uint32_t codepoint) {
|
||||||
|
bool is_in_range = codepoint >= font->first_char && codepoint <= font->last_char;
|
||||||
|
if (is_in_range) {
|
||||||
|
uint32_t index = codepoint - font->first_char;
|
||||||
|
return font->glyphs[index];
|
||||||
|
} else {
|
||||||
|
uint32_t index = '?' - font->first_char;
|
||||||
|
return font->glyphs[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user) {
|
||||||
|
IO_Printf("%s\n", message);
|
||||||
|
if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM) {
|
||||||
|
IO_Assert(!"OpenGL API usage error!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_Init(MU_Context *mu) {
|
||||||
|
int success = gladLoadGLLoader(mu->gl_get_proc_address);
|
||||||
|
IO_Assert(success);
|
||||||
|
glDebugMessageCallback(&GL_DebugCallback, NULL);
|
||||||
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||||
|
void R2_Init(R2_Render * R);
|
||||||
|
R2_Init(&R2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_Clear(Vec2I window_size, Vec4 clear_color = {0, 0, 0, 0}) {
|
||||||
|
glViewport(0, 0, window_size.x, window_size.y);
|
||||||
|
glClearColor(clear_color.r, clear_color.g, clear_color.b, clear_color.a);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
R2_Shader R2_CreateShader(char *glsl_vshader, char *glsl_fshader) {
|
||||||
|
R2_Shader result = {};
|
||||||
|
result.vshader = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &glsl_vshader);
|
||||||
|
result.fshader = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &glsl_fshader);
|
||||||
|
|
||||||
|
GLint linked;
|
||||||
|
glGetProgramiv(result.vshader, GL_LINK_STATUS, &linked);
|
||||||
|
if (!linked) {
|
||||||
|
char message[1024];
|
||||||
|
glGetProgramInfoLog(result.vshader, sizeof(message), NULL, message);
|
||||||
|
IO_FatalErrorf("[GL] Failed to create vertex shader! %s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
glGetProgramiv(result.fshader, GL_LINK_STATUS, &linked);
|
||||||
|
if (!linked) {
|
||||||
|
char message[1024];
|
||||||
|
glGetProgramInfoLog(result.fshader, sizeof(message), NULL, message);
|
||||||
|
IO_FatalErrorf("[GL] Failed to create fragment shader! %s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
glGenProgramPipelines(1, &result.pipeline);
|
||||||
|
glUseProgramStages(result.pipeline, GL_VERTEX_SHADER_BIT, result.vshader);
|
||||||
|
glUseProgramStages(result.pipeline, GL_FRAGMENT_SHADER_BIT, result.fshader);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 *MESH_GenerateCircle(MA_Arena *arena, float radius, int segment_count) {
|
||||||
|
int vertex_count = segment_count * 3;
|
||||||
|
Vec2 *circle_pos = MA_PushArray(arena, Vec2, vertex_count);
|
||||||
|
Vec2 *points = MA_PushArray(G_Frame, Vec2, segment_count + 1);
|
||||||
|
for (int i = 0; i < segment_count; i += 1) {
|
||||||
|
float radians = 2.0f * PI32 * float(i) / float(segment_count);
|
||||||
|
float x = radius * cosf(radians);
|
||||||
|
float y = radius * sinf(radians);
|
||||||
|
points[i] = {x, y};
|
||||||
|
}
|
||||||
|
points[segment_count] = points[0]; // wrap around
|
||||||
|
|
||||||
|
int point_i = 0;
|
||||||
|
int segment_i = 0;
|
||||||
|
for (; segment_i < vertex_count; segment_i += 3, point_i += 1) {
|
||||||
|
Vec2 *it = circle_pos + segment_i;
|
||||||
|
it[0] = {points[point_i]};
|
||||||
|
it[1] = {points[point_i + 1]};
|
||||||
|
it[2] = {};
|
||||||
|
}
|
||||||
|
return circle_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R2_Init(R2_Render *R) {
|
||||||
|
if (R2.initialized) return;
|
||||||
|
R2.vertex_list = &R2.base_vertex_list;
|
||||||
|
|
||||||
|
R2.initialized = true;
|
||||||
|
MA_Checkpoint atlas_memory = MA_Save(&R2.arena);
|
||||||
|
|
||||||
|
R2_Atlas atlas = R2_CreateAtlas(atlas_memory.arena, {2048, 2048});
|
||||||
|
R2.atlas = atlas;
|
||||||
|
|
||||||
|
R2.font_small = Font_Create(&atlas, 32.f, "C:/windows/fonts/Calibri.ttf"_s, 1.0f);
|
||||||
|
R2.font_medium = Font_Create(&atlas, 32.f, "C:/windows/fonts/Calibri.ttf"_s, 2.0f);
|
||||||
|
R2.font_large = Font_Create(&atlas, 32.f, "C:/windows/fonts/Calibri.ttf"_s, 4.0f);
|
||||||
|
R2.font_very_large = Font_Create(&atlas, 32.f, "C:/windows/fonts/Calibri.ttf"_s, 8.0f);
|
||||||
|
// Upload to GPU
|
||||||
|
{
|
||||||
|
glCreateTextures(GL_TEXTURE_2D, 1, &atlas.texture_id);
|
||||||
|
glTextureParameteri(atlas.texture_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTextureParameteri(atlas.texture_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTextureParameteri(atlas.texture_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTextureParameteri(atlas.texture_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTextureStorage2D(atlas.texture_id, 1, GL_R8, R2.atlas.sizei.x, R2.atlas.sizei.y);
|
||||||
|
glTextureSubImage2D(atlas.texture_id, 0, 0, 0, R2.atlas.sizei.x, R2.atlas.sizei.y, GL_RED, GL_UNSIGNED_BYTE, atlas.bitmap);
|
||||||
|
}
|
||||||
|
R2.font_medium.texture_id = atlas.texture_id;
|
||||||
|
R2.font_small.texture_id = atlas.texture_id;
|
||||||
|
R2.font_large.texture_id = atlas.texture_id;
|
||||||
|
R2.font_very_large.texture_id = atlas.texture_id;
|
||||||
|
R2.font = &R2.font_medium;
|
||||||
|
MA_Load(atlas_memory);
|
||||||
|
|
||||||
|
{
|
||||||
|
glCreateBuffers(1, &R2.vbo);
|
||||||
|
glNamedBufferStorage(R2.vbo, lengthof(R2_VertexNode::vertices) * sizeof(R2_Vertex), 0, GL_DYNAMIC_STORAGE_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
glCreateVertexArrays(1, &R2.vao);
|
||||||
|
|
||||||
|
GLint vbuf_index = 0;
|
||||||
|
glVertexArrayVertexBuffer(R2.vao, vbuf_index, R2.vbo, 0, sizeof(struct R2_Vertex));
|
||||||
|
|
||||||
|
GLint a_pos = 0;
|
||||||
|
glVertexArrayAttribFormat(R2.vao, a_pos, 2, GL_FLOAT, GL_FALSE, offsetof(struct R2_Vertex, pos));
|
||||||
|
glVertexArrayAttribBinding(R2.vao, a_pos, vbuf_index);
|
||||||
|
glEnableVertexArrayAttrib(R2.vao, a_pos);
|
||||||
|
|
||||||
|
GLint a_tex = 1;
|
||||||
|
glVertexArrayAttribFormat(R2.vao, a_tex, 2, GL_FLOAT, GL_FALSE, offsetof(struct R2_Vertex, tex));
|
||||||
|
glVertexArrayAttribBinding(R2.vao, a_tex, vbuf_index);
|
||||||
|
glEnableVertexArrayAttrib(R2.vao, a_tex);
|
||||||
|
|
||||||
|
GLint a_color = 2;
|
||||||
|
glVertexArrayAttribFormat(R2.vao, a_color, 4, GL_FLOAT, GL_FALSE, offsetof(struct R2_Vertex, color));
|
||||||
|
glVertexArrayAttribBinding(R2.vao, a_color, vbuf_index);
|
||||||
|
glEnableVertexArrayAttrib(R2.vao, a_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char *glsl_vshader = R2_GLSL_HEADER(__LINE__) R2_Vertex_GLSL R"==(
|
||||||
|
out gl_PerVertex { vec4 gl_Position; }; // required because of ARB_separate_shader_objects
|
||||||
|
out vec2 OUT_UV;
|
||||||
|
out vec4 OUT_Color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 pos = IN_Pos * U_InvHalfScreenSize;
|
||||||
|
pos -= vec2(1, 1);
|
||||||
|
|
||||||
|
gl_Position = vec4(pos, 0, 1);
|
||||||
|
OUT_UV = IN_Tex;
|
||||||
|
OUT_Color = IN_Color;
|
||||||
|
}
|
||||||
|
)==";
|
||||||
|
|
||||||
|
char *glsl_fshader = R2_GLSL_HEADER(__LINE__) R"==(
|
||||||
|
in vec2 IN_UV;
|
||||||
|
in vec4 IN_Color;
|
||||||
|
|
||||||
|
layout (binding=0) uniform sampler2D S_Texture;
|
||||||
|
layout (location=0) out vec4 OUT_Color;
|
||||||
|
|
||||||
|
vec3 lerp(vec3 v0, vec3 v1, float t) {
|
||||||
|
return (1 - t) * v0 + t * v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 c = IN_Color;
|
||||||
|
c.a *= texture(S_Texture, IN_UV).r;
|
||||||
|
OUT_Color = c;
|
||||||
|
}
|
||||||
|
)==";
|
||||||
|
R2_Shader shader = R2_CreateShader(glsl_vshader, glsl_fshader);
|
||||||
|
R2.vshader = shader.vshader;
|
||||||
|
R2.fshader = shader.fshader;
|
||||||
|
R2.pipeline = shader.pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Prepare circle rendering
|
||||||
|
//
|
||||||
|
// @note: Circle gets later updated before rendering
|
||||||
|
{
|
||||||
|
const int segment_count = 16;
|
||||||
|
const int vertex_count = segment_count * 3;
|
||||||
|
R2.circle.mesh_vertex_count = vertex_count;
|
||||||
|
R2.circle.mesh_segment_count = segment_count;
|
||||||
|
R2.circle.radius = 2.0f;
|
||||||
|
Vec2 *circle_pos = MESH_GenerateCircle(G_Frame, R2.circle.radius, segment_count);
|
||||||
|
|
||||||
|
glCreateBuffers(1, &R2.circle.vbo_mesh);
|
||||||
|
glNamedBufferStorage(R2.circle.vbo_mesh, sizeof(Vec2) * vertex_count, circle_pos, GL_DYNAMIC_STORAGE_BIT);
|
||||||
|
|
||||||
|
glCreateBuffers(1, &R2.circle.vbo_positions);
|
||||||
|
glNamedBufferStorage(R2.circle.vbo_positions, R2_CircleBufferSize, 0, GL_DYNAMIC_STORAGE_BIT);
|
||||||
|
|
||||||
|
{
|
||||||
|
unsigned vao = 0;
|
||||||
|
glCreateVertexArrays(1, &vao);
|
||||||
|
|
||||||
|
glVertexArrayVertexBuffer(vao, 0 /*vbuf_index*/, R2.circle.vbo_mesh, 0, sizeof(Vec2));
|
||||||
|
glVertexArrayVertexBuffer(vao, 1 /*vbuf_index*/, R2.circle.vbo_positions, 0, sizeof(V_Circle));
|
||||||
|
|
||||||
|
GLint IN_Pos = 0;
|
||||||
|
glVertexArrayAttribFormat(vao, IN_Pos, 2, GL_FLOAT, GL_FALSE, 0);
|
||||||
|
glVertexArrayAttribBinding(vao, IN_Pos, 0 /*vbuf_index*/);
|
||||||
|
glEnableVertexArrayAttrib(vao, IN_Pos);
|
||||||
|
|
||||||
|
GLint IN_Offset = 1;
|
||||||
|
glVertexArrayAttribFormat(vao, IN_Offset, 2, GL_FLOAT, GL_FALSE, 0);
|
||||||
|
glVertexArrayAttribBinding(vao, IN_Offset, 1 /*vbuf_index*/);
|
||||||
|
glEnableVertexArrayAttrib(vao, IN_Offset);
|
||||||
|
glVertexArrayBindingDivisor(vao, IN_Offset, 1);
|
||||||
|
|
||||||
|
GLint IN_ColorIndex = 2;
|
||||||
|
glVertexArrayAttribFormat(vao, IN_ColorIndex, 1, GL_FLOAT, GL_FALSE, offsetof(V_Circle, color_index));
|
||||||
|
glVertexArrayAttribBinding(vao, IN_ColorIndex, 1 /*vbuf_index*/);
|
||||||
|
glEnableVertexArrayAttrib(vao, IN_ColorIndex);
|
||||||
|
glVertexArrayBindingDivisor(vao, IN_ColorIndex, 1);
|
||||||
|
|
||||||
|
R2.circle.vao = vao;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *glsl_vshader = R2_GLSL_HEADER(__LINE__) R"==(
|
||||||
|
layout(location=0) uniform Vec2 U_InvHalfScreenSize;
|
||||||
|
layout(location=1) uniform Vec4 U_Colors[256]; // :Colors
|
||||||
|
|
||||||
|
layout(location=0) in Vec2 IN_Pos;
|
||||||
|
layout(location=1) in Vec2 IN_Offset;
|
||||||
|
layout(location=2) in float IN_ColorIndex;
|
||||||
|
|
||||||
|
out gl_PerVertex { vec4 gl_Position; }; // required because of ARB_separate_shader_objects
|
||||||
|
out vec4 OUT_Color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 pos = (IN_Pos+IN_Offset)*U_InvHalfScreenSize;
|
||||||
|
pos -= vec2(1, 1);
|
||||||
|
|
||||||
|
gl_Position = vec4(pos, 0, 1);
|
||||||
|
OUT_Color = U_Colors[int(IN_ColorIndex)];
|
||||||
|
}
|
||||||
|
)==";
|
||||||
|
|
||||||
|
char *glsl_fshader = R2_GLSL_HEADER(__LINE__) R"==(
|
||||||
|
in vec4 IN_Color;
|
||||||
|
|
||||||
|
layout (location=0) out vec4 OUT_Color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 c = IN_Color;
|
||||||
|
OUT_Color = c;
|
||||||
|
}
|
||||||
|
)==";
|
||||||
|
R2.circle.shader = R2_CreateShader(glsl_vshader, glsl_fshader);
|
||||||
|
MA_Init(&R2.circle.arena);
|
||||||
|
R2.circle.arena.alignment = 0;
|
||||||
|
|
||||||
|
// :Colors
|
||||||
|
RandomSeed seed = {193};
|
||||||
|
{
|
||||||
|
R2.circle.colors.reserve(256);
|
||||||
|
for (int i = 0; i < 256; i += 1) {
|
||||||
|
Vec4 hsl = {0, 0.4f, 0.60f, 1.0f};
|
||||||
|
hsl.r = (float)GetRandomNormal(&seed);
|
||||||
|
Vec4 rgb = HSLToRGB(hsl);
|
||||||
|
R2.circle.colors.add(rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec4 R2_GetColor(int *idx) {
|
||||||
|
int index = R2.circle.color_iter % 256;
|
||||||
|
R2.circle.color_iter += 1;
|
||||||
|
Vec4 result = R2.circle.colors[index];
|
||||||
|
*idx = index;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
R2_Vertex *R2_AllocVertex(MA_Arena *arena, R2_VertexList *list, int count) {
|
||||||
|
R2_VertexNode *node = list->last;
|
||||||
|
if (node == 0 || node->count + count > lengthof(node->vertices)) {
|
||||||
|
node = MA_PushStruct(arena, R2_VertexNode);
|
||||||
|
SLL_QUEUE_ADD(list->first, list->last, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
R2.vertex_count += count;
|
||||||
|
R2_Vertex *result = node->vertices + node->count;
|
||||||
|
node->count += count;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R2_PushVertex(MA_Arena *arena, R2_VertexList *list, R2_Vertex *vertices, int count) {
|
||||||
|
R2_VertexNode *node = list->last;
|
||||||
|
if (node == 0 || node->count + count > lengthof(node->vertices)) {
|
||||||
|
node = MA_PushStruct(arena, R2_VertexNode);
|
||||||
|
SLL_QUEUE_ADD(list->first, list->last, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
R2.vertex_count += count;
|
||||||
|
for (int i = 0; i < count; i += 1) {
|
||||||
|
node->vertices[node->count++] = vertices[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void R2_DrawCircle(Vec2 pos, float radius, Vec4 color) {
|
||||||
|
const int segment_count = 16;
|
||||||
|
const int vertex_count = segment_count * 3;
|
||||||
|
Vec2 points[segment_count + 1];
|
||||||
|
for (int i = 0; i < lengthof(points); i += 1) {
|
||||||
|
float radians = 2.0f * PI32 * float(i) / float(segment_count);
|
||||||
|
float x = radius * cosf(radians);
|
||||||
|
float y = radius * sinf(radians);
|
||||||
|
points[i] = {x, y};
|
||||||
|
}
|
||||||
|
points[segment_count] = points[0]; // wrap around
|
||||||
|
|
||||||
|
R2_Vertex *vertices = R2_AllocVertex(&R2.arena, R2.vertex_list, vertex_count);
|
||||||
|
int point_i = 0;
|
||||||
|
int segment_i = 0;
|
||||||
|
for (; segment_i < vertex_count; segment_i += 3, point_i += 1) {
|
||||||
|
Rect2 tex = R2.font->white_texture_bounding_box;
|
||||||
|
R2_Vertex *it = vertices + segment_i;
|
||||||
|
it[0].color = color;
|
||||||
|
it[1].color = color;
|
||||||
|
it[2].color = color;
|
||||||
|
it[0].tex = {tex.min.x, tex.max.y};
|
||||||
|
it[1].tex = {tex.max.x, tex.max.y};
|
||||||
|
it[2].tex = {tex.min.x, tex.min.y};
|
||||||
|
|
||||||
|
it[0].pos = {pos + points[point_i]};
|
||||||
|
it[1].pos = {pos + points[point_i + 1]};
|
||||||
|
it[2].pos = {pos};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void R2_PushQuad(MA_Arena *arena, R2_VertexList *list, Rect2 rect, Rect2 tex, Vec4 color, float rotation = 0.f, Vec2 rotation_point = {}) {
|
||||||
|
R2_Vertex *v = R2_AllocVertex(arena, list, 6);
|
||||||
|
v[0] = {
|
||||||
|
{rect.min.x, rect.max.y},
|
||||||
|
{ tex.min.x, tex.max.y},
|
||||||
|
color
|
||||||
|
};
|
||||||
|
v[1] = {
|
||||||
|
{rect.max.x, rect.max.y},
|
||||||
|
{ tex.max.x, tex.max.y},
|
||||||
|
color
|
||||||
|
};
|
||||||
|
v[2] = {
|
||||||
|
{rect.min.x, rect.min.y},
|
||||||
|
{ tex.min.x, tex.min.y},
|
||||||
|
color
|
||||||
|
};
|
||||||
|
v[3] = {
|
||||||
|
{rect.min.x, rect.min.y},
|
||||||
|
{ tex.min.x, tex.min.y},
|
||||||
|
color
|
||||||
|
};
|
||||||
|
v[4] = {
|
||||||
|
{rect.max.x, rect.max.y},
|
||||||
|
{ tex.max.x, tex.max.y},
|
||||||
|
color
|
||||||
|
};
|
||||||
|
v[5] = {
|
||||||
|
{rect.max.x, rect.min.y},
|
||||||
|
{ tex.max.x, tex.min.y},
|
||||||
|
color
|
||||||
|
};
|
||||||
|
if (rotation != 0.f) {
|
||||||
|
float s = sinf(rotation);
|
||||||
|
float c = cosf(rotation);
|
||||||
|
for (int i = 0; i < 6; i += 1) {
|
||||||
|
v[i].pos -= rotation_point;
|
||||||
|
v[i].pos = {v[i].pos.x * c + v[i].pos.y * (-s), v[i].pos.x * s + v[i].pos.y * c};
|
||||||
|
v[i].pos += rotation_point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void R2_DrawRectRounded(Rect2 rect, Vec4 color, float roundness = 0.3f) {
|
||||||
|
// Draw 5 rectangles, one in the middle, and for on sides, something like a cross
|
||||||
|
Vec2 size = CalcSize(rect);
|
||||||
|
float max_size = Minimum(size.x, size.y);
|
||||||
|
|
||||||
|
float shrink_by = max_size * roundness;
|
||||||
|
Rect2 inner_rect = ShrinkByHalfSize(rect, {shrink_by, shrink_by});
|
||||||
|
Rect2 left_rect = {rect.min.x, inner_rect.min.y, inner_rect.min.x, inner_rect.max.y};
|
||||||
|
Rect2 right_rect = {inner_rect.max.x, inner_rect.min.y, rect.max.x, inner_rect.max.y};
|
||||||
|
Rect2 up_rect = {inner_rect.min.x, inner_rect.max.y, inner_rect.max.x, rect.max.y};
|
||||||
|
Rect2 down_rect = {inner_rect.min.x, inner_rect.min.y, inner_rect.max.x, rect.min.y};
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, inner_rect, R2.font->white_texture_bounding_box, color);
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, left_rect, R2.font->white_texture_bounding_box, color);
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, right_rect, R2.font->white_texture_bounding_box, color);
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, up_rect, R2.font->white_texture_bounding_box, color);
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, down_rect, R2.font->white_texture_bounding_box, color);
|
||||||
|
|
||||||
|
// @optimize: we dont want to draw an entire circle lmao, just a 4th of it
|
||||||
|
Vec2 top_left = {inner_rect.min.x, inner_rect.max.y};
|
||||||
|
Vec2 top_right = {inner_rect.max.x, inner_rect.max.y};
|
||||||
|
Vec2 bottom_left = {inner_rect.min.x, inner_rect.min.y};
|
||||||
|
Vec2 bottom_right = {inner_rect.max.x, inner_rect.min.y};
|
||||||
|
|
||||||
|
R2_DrawCircle(top_left, shrink_by, color);
|
||||||
|
R2_DrawCircle(top_right, shrink_by, color);
|
||||||
|
R2_DrawCircle(bottom_left, shrink_by, color);
|
||||||
|
R2_DrawCircle(bottom_right, shrink_by, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void R2_DrawRect(Rect2 rect, Vec4 color) {
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, rect, R2.font->white_texture_bounding_box, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void R2_DrawLine(Vec2 p0, Vec2 p1, float thickness, Vec4 color) {
|
||||||
|
Rect2 tex = R2.font->white_texture_bounding_box;
|
||||||
|
float half_thickness = thickness / 2.f;
|
||||||
|
|
||||||
|
Vec2 from_p1_to_p0 = Normalize(p1 - p0);
|
||||||
|
Vec2 perp = {-from_p1_to_p0.y, from_p1_to_p0.x};
|
||||||
|
Vec2 offset = perp * thickness;
|
||||||
|
|
||||||
|
Vec2 p00 = p0 - offset;
|
||||||
|
Vec2 p01 = p0 + offset;
|
||||||
|
Vec2 p10 = p1 - offset;
|
||||||
|
Vec2 p11 = p1 + offset;
|
||||||
|
R2_Vertex *v = R2_AllocVertex(&R2.arena, R2.vertex_list, 6);
|
||||||
|
v[0].pos = {p00.x, p00.y};
|
||||||
|
v[0].tex = {tex.min.x, tex.max.y};
|
||||||
|
v[0].color = color;
|
||||||
|
v[1].pos = {p11.x, p11.y};
|
||||||
|
v[1].tex = {tex.max.x, tex.max.y};
|
||||||
|
v[1].color = color;
|
||||||
|
v[2].pos = {p10.x, p10.y};
|
||||||
|
v[2].tex = {tex.min.x, tex.min.y};
|
||||||
|
v[2].color = color;
|
||||||
|
v[3].pos = {p00.x, p00.y};
|
||||||
|
v[3].tex = {tex.min.x, tex.min.y};
|
||||||
|
v[3].color = color;
|
||||||
|
v[4].pos = {p11.x, p11.y};
|
||||||
|
v[4].tex = {tex.max.x, tex.max.y};
|
||||||
|
v[4].color = color;
|
||||||
|
v[5].pos = {p01.x, p01.y};
|
||||||
|
v[5].tex = {tex.max.x, tex.min.y};
|
||||||
|
v[5].color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R2_DrawRectOutline(Rect2 rect, Vec4 color, float thickness = 1.0f) {
|
||||||
|
Rect2 up = {rect.min.x, rect.max.y - thickness, rect.max.x, rect.max.y};
|
||||||
|
Rect2 down = {rect.min.x, rect.min.y, rect.max.x, rect.min.y + thickness};
|
||||||
|
Rect2 left = {rect.min.x, rect.min.y, rect.min.x + thickness, rect.max.y};
|
||||||
|
Rect2 right = {rect.max.x - thickness, rect.min.y, rect.max.x, rect.max.y};
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, up, R2.font->white_texture_bounding_box, color);
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, down, R2.font->white_texture_bounding_box, color);
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, left, R2.font->white_texture_bounding_box, color);
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, right, R2.font->white_texture_bounding_box, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct R2_StringParams {
|
||||||
|
Vec2 pos;
|
||||||
|
S8_String string;
|
||||||
|
Vec4 color;
|
||||||
|
|
||||||
|
float rotation;
|
||||||
|
float scale;
|
||||||
|
bool do_draw;
|
||||||
|
MA_Arena *rects_arena;
|
||||||
|
};
|
||||||
|
|
||||||
|
R2_StringMeasure R2_BaseDrawString(R2_StringParams params) {
|
||||||
|
R2_StringMeasure result = {};
|
||||||
|
// if (params.color.a < 0.00001f) params.do_draw = false;
|
||||||
|
|
||||||
|
S8_String string = params.string;
|
||||||
|
Vec2 original_pos = params.pos;
|
||||||
|
Vec2 max_pos = params.pos;
|
||||||
|
Vec2 pos = params.pos;
|
||||||
|
float scale = params.scale;
|
||||||
|
for (UTF8_Iter iter = UTF8_IterateEx(params.string.str, (int)params.string.len); iter.item; UTF8_Advance(&iter)) {
|
||||||
|
uint32_t it = iter.item;
|
||||||
|
if (it == '\n') {
|
||||||
|
pos.x = original_pos.x;
|
||||||
|
pos.y -= R2.font->size * scale;
|
||||||
|
if (pos.x > max_pos.x) max_pos.x = pos.x;
|
||||||
|
if (pos.y < max_pos.y) max_pos.y = pos.y; // @warning: min position y actually
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Font_Glyph &g = Font_GetGlyph(R2.font, it);
|
||||||
|
|
||||||
|
Vec2 sym_pos = pos;
|
||||||
|
pos.x += g.x_advance * scale;
|
||||||
|
if (pos.x > max_pos.x) max_pos.x = pos.x;
|
||||||
|
if (pos.y < max_pos.y) max_pos.y = pos.y; // @warning: min position y actually
|
||||||
|
|
||||||
|
sym_pos.x += g.offset.x * scale;
|
||||||
|
sym_pos.y += g.offset.y * scale;
|
||||||
|
|
||||||
|
Vec2 minp = {sym_pos.x, sym_pos.y};
|
||||||
|
Rect2 rect = Rect2_Size(minp, g.size * scale);
|
||||||
|
if (params.do_draw) {
|
||||||
|
R2_PushQuad(&R2.arena, R2.vertex_list, rect, g.atlas_bounding_box, params.color, params.rotation, original_pos);
|
||||||
|
}
|
||||||
|
if (params.rects_arena) {
|
||||||
|
R2_RectNode *node = MA_PushStruct(params.rects_arena, R2_RectNode);
|
||||||
|
node->rect = rect;
|
||||||
|
node->utf8_codepoint_byte_size = iter.utf8_codepoint_byte_size;
|
||||||
|
SLL_QUEUE_ADD(result.first_rect, result.last_rect, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.rect = {
|
||||||
|
original_pos.x, max_pos.y + R2.font->descent * scale, // @warning: min position y actually
|
||||||
|
max_pos.x, original_pos.y + R2.font->ascent * scale};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
R2_StringParams R2_MakeStringParams(S8_String string, Vec2 pos = {}, Vec4 color = {}, bool do_draw = false) {
|
||||||
|
R2_StringParams params = {};
|
||||||
|
params.pos = pos;
|
||||||
|
params.string = string;
|
||||||
|
params.color = color;
|
||||||
|
params.scale = 1.0f;
|
||||||
|
params.do_draw = do_draw;
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2 R2_DrawString(Vec2 pos, S8_String string, Vec4 color = {1, 1, 1, 1}, float scale = 1.0f, float rotation = 0.0f) {
|
||||||
|
R2_StringParams params = R2_MakeStringParams(string, pos, color, true);
|
||||||
|
params.scale = scale;
|
||||||
|
params.rotation = rotation;
|
||||||
|
R2_StringMeasure result = R2_BaseDrawString(params);
|
||||||
|
return result.rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2 R2_GetStringRect(Vec2 pos, S8_String string) {
|
||||||
|
R2_StringParams params = R2_MakeStringParams(string, pos);
|
||||||
|
R2_StringMeasure result = R2_BaseDrawString(params);
|
||||||
|
return result.rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 R2_GetStringSize(S8_String string, float scale = 1.0f) {
|
||||||
|
R2_StringParams params = R2_MakeStringParams(string);
|
||||||
|
params.scale = scale;
|
||||||
|
R2_StringMeasure result = R2_BaseDrawString(params);
|
||||||
|
return CalcSize(result.rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
R2_StringMeasure R2_GetStringMeasure(MA_Arena *arena, Vec2 pos, S8_String string) {
|
||||||
|
R2_StringParams params = R2_MakeStringParams(string, pos);
|
||||||
|
params.rects_arena = arena;
|
||||||
|
R2_StringMeasure result = R2_BaseDrawString(params);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float R2_DebugStringYValue;
|
||||||
|
Rect2 R2_DebugString(char *string, ...) {
|
||||||
|
S8_FORMAT(G_Frame, string, result);
|
||||||
|
|
||||||
|
R2.vertex_list = &R2.ui_vertex_list;
|
||||||
|
Vec2 pos = {0, R2_DebugStringYValue};
|
||||||
|
R2_DrawString(pos - 2, result, {0, 0, 0, 1});
|
||||||
|
Rect2 rect = R2_DrawString(pos, result, {1, 1, 1, 1});
|
||||||
|
R2_DebugStringYValue += R2.font->size;
|
||||||
|
R2.vertex_list = &R2.base_vertex_list;
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R2_ClearFrameData() {
|
||||||
|
MA_Reset(&R2.arena);
|
||||||
|
R2.ui_vertex_list.first = 0;
|
||||||
|
R2.ui_vertex_list.last = 0;
|
||||||
|
R2.base_vertex_list.first = 0;
|
||||||
|
R2.base_vertex_list.last = 0;
|
||||||
|
R2.vertex_count = 0;
|
||||||
|
|
||||||
|
MA_Reset(&R2.circle.arena);
|
||||||
|
R2.circle.instances_to_render = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R2_EndFrame(Vec2 window_size) {
|
||||||
|
R2_DebugStringYValue = 0;
|
||||||
|
R2.circle.color_iter = 0;
|
||||||
|
|
||||||
|
// Update the circle size
|
||||||
|
{
|
||||||
|
Vec2 *circle_pos = MESH_GenerateCircle(G_Frame, R2.circle.radius, R2.circle.mesh_segment_count);
|
||||||
|
glNamedBufferSubData(R2.circle.vbo_mesh, 0, R2.circle.mesh_vertex_count * sizeof(Vec2), circle_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
|
||||||
|
float x = 1.f / (window_size.x / 2.f);
|
||||||
|
float y = 1.f / (window_size.y / 2.f);
|
||||||
|
|
||||||
|
// Default draw using the font texture
|
||||||
|
{
|
||||||
|
glBindProgramPipeline(R2.pipeline);
|
||||||
|
glProgramUniform2f(R2.vshader, 0, x, y);
|
||||||
|
for (R2_VertexNode *it = R2.base_vertex_list.first; it; it = it->next) {
|
||||||
|
glNamedBufferSubData(R2.vbo, 0, it->count * sizeof(R2_Vertex), it->vertices);
|
||||||
|
glBindVertexArray(R2.vao);
|
||||||
|
GLint s_texture = 0; // texture unit that sampler2D will use in GLSL code
|
||||||
|
glBindTextureUnit(s_texture, R2.font->texture_id);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, it->count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw circles
|
||||||
|
{
|
||||||
|
glBindProgramPipeline(R2.circle.shader.pipeline);
|
||||||
|
glProgramUniform2f(R2.circle.shader.vshader, 0, x, y);
|
||||||
|
// :Colors
|
||||||
|
glProgramUniform4fv(R2.circle.shader.vshader, 1, R2.circle.colors.len, (float *)R2.circle.colors.data);
|
||||||
|
glBindVertexArray(R2.circle.vao);
|
||||||
|
|
||||||
|
V_Circle *circles = (V_Circle *)R2.circle.arena.memory.data;
|
||||||
|
int offset = 0;
|
||||||
|
int allocated_batches = (int)ceilf((float)R2.circle.instances_to_render / (float)R2_MaxCircleCount);
|
||||||
|
for (int batch_i = 0; batch_i < allocated_batches; batch_i += 1) {
|
||||||
|
int batch_size = ClampTop(R2.circle.instances_to_render - offset, R2_MaxCircleCount);
|
||||||
|
IO_Assert(batch_size >= 0);
|
||||||
|
|
||||||
|
glNamedBufferSubData(R2.circle.vbo_positions, 0, batch_size * sizeof(V_Circle), circles + offset);
|
||||||
|
glDrawArraysInstanced(GL_TRIANGLES, 0, R2.circle.mesh_vertex_count, batch_size);
|
||||||
|
|
||||||
|
offset += batch_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw UI
|
||||||
|
{
|
||||||
|
glBindProgramPipeline(R2.pipeline);
|
||||||
|
glProgramUniform2f(R2.vshader, 0, x, y);
|
||||||
|
for (R2_VertexNode *it = R2.ui_vertex_list.first; it; it = it->next) {
|
||||||
|
glNamedBufferSubData(R2.vbo, 0, it->count * sizeof(R2_Vertex), it->vertices);
|
||||||
|
glBindVertexArray(R2.vao);
|
||||||
|
GLint s_texture = 0; // texture unit that sampler2D will use in GLSL code
|
||||||
|
glBindTextureUnit(s_texture, R2.font->texture_id);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, it->count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
R2_ClearFrameData();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t G_PointCount;
|
||||||
|
|
||||||
|
void R2_DrawCircleFast(Vec2 p, int index) {
|
||||||
|
V_Circle *result = MA_PushStruct(&R2.circle.arena, V_Circle);
|
||||||
|
result->pos = p;
|
||||||
|
result->color_index = (float)index;
|
||||||
|
R2.circle.instances_to_render += 1;
|
||||||
|
G_PointCount += 1;
|
||||||
|
}
|
||||||
222
src/visualize/vis_main.cpp
Executable file
222
src/visualize/vis_main.cpp
Executable file
@@ -0,0 +1,222 @@
|
|||||||
|
|
||||||
|
#include "../core/vmath.hpp"
|
||||||
|
#define MU_Float2 Vec2
|
||||||
|
#define MU_Int2 Vec2I
|
||||||
|
|
||||||
|
#include "../core/core.c"
|
||||||
|
#include "../vendor/stb_truetype.c"
|
||||||
|
#include "../vendor/glad/glad.c"
|
||||||
|
|
||||||
|
#define VIS_MemoryCopy MA_MemoryCopy
|
||||||
|
#define VIS_MemoryZero MA_MemoryZero
|
||||||
|
#define VIS_ASSERT IO_Assert
|
||||||
|
#define VIS_IMPLEMENTATION
|
||||||
|
#include "visualize.h"
|
||||||
|
#include "../core/vmath64.hpp"
|
||||||
|
|
||||||
|
#include "vis_types.h"
|
||||||
|
#include "render2d.cpp"
|
||||||
|
|
||||||
|
MA_Arena G_PernamentArena;
|
||||||
|
MA_Arena G_FrameArena;
|
||||||
|
MA_Arena *G_Frame = &G_FrameArena;
|
||||||
|
MA_Arena *G_Perm = &G_PernamentArena;
|
||||||
|
MU_Context *Mu;
|
||||||
|
|
||||||
|
#include "globals.cpp"
|
||||||
|
#include "vis_ui.cpp"
|
||||||
|
#include "prototype_data_handling.cpp"
|
||||||
|
#include "vis_plot_canvas.cpp"
|
||||||
|
#include "prototype_text_editor.cpp"
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
- Add global hotkey to bring to forefront
|
||||||
|
- Output text to console
|
||||||
|
- Save event files and add history
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
DEV_SetWorkingDir();
|
||||||
|
for (int i = 1; i < argc; i += 1) {
|
||||||
|
S8_String arg = S8_MakeFromChar(argv[i]);
|
||||||
|
if (S8_AreEqual(arg, S8_Lit("1"), S8_NO_FLAGS)) G_ProcessID = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIS_Prototypes prototype = VIS_Prototypes_Plot;
|
||||||
|
MU_Params params = {};
|
||||||
|
params.enable_opengl = true;
|
||||||
|
params.window.size.x = 1280;
|
||||||
|
params.window.size.y = 720;
|
||||||
|
params.delta_time = 0.0166666;
|
||||||
|
Mu = MU_Start(params);
|
||||||
|
GL_Init(Mu);
|
||||||
|
|
||||||
|
while (MU_Update(Mu)) {
|
||||||
|
MA_Reset(G_Frame);
|
||||||
|
if (Mu->window->key[MU_KEY_ESCAPE].down) {
|
||||||
|
MU_Quit(Mu);
|
||||||
|
}
|
||||||
|
if (Mu->window->is_focused == false) {
|
||||||
|
|
||||||
|
uint8_t cmd = VIS_ReadCommand();
|
||||||
|
if (cmd) {
|
||||||
|
switch (cmd) {
|
||||||
|
case VIS_COMMAND_CONNECTED: {
|
||||||
|
U_Notification(4.0f, "Your program has successfully written to the shared memory!");
|
||||||
|
} break;
|
||||||
|
case VIS_COMMAND_CLEAR: {
|
||||||
|
VIS__Clear();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
VIS_IncrementReadCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mu->window->should_render = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PROTOTYPE TRANSITIONS
|
||||||
|
//
|
||||||
|
static float t_transition;
|
||||||
|
static int transition_phase;
|
||||||
|
|
||||||
|
if (Mu->window->key[MU_KEY_F1].press) {
|
||||||
|
transition_phase = 1;
|
||||||
|
t_transition = 0;
|
||||||
|
}
|
||||||
|
if (transition_phase == 1) {
|
||||||
|
t_transition += Mu->time.deltaf * 5.0f;
|
||||||
|
if (t_transition >= 1) {
|
||||||
|
prototype = (prototype + 1) % VIS_Prototypes_Count;
|
||||||
|
transition_phase = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (transition_phase == 2) {
|
||||||
|
t_transition -= Mu->time.deltaf * 5.0f;
|
||||||
|
if (t_transition <= 0) {
|
||||||
|
transition_phase = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// CAMERA
|
||||||
|
//
|
||||||
|
{
|
||||||
|
Mu->window->change_cursor_on_mouse_hold = true;
|
||||||
|
const float zoom_penalty = 0.4f;
|
||||||
|
const Vec2 target_wheel = {};
|
||||||
|
|
||||||
|
bool one_zoom_to_far = (G_Camera.zoom.y > 1000.0f || G_Camera.zoom.x > 1000.0f);
|
||||||
|
if (Mu->window->mouse.delta_wheel) {
|
||||||
|
bool invalid = one_zoom_to_far && Mu->window->mouse.delta_wheel > 0.0f;
|
||||||
|
if (!invalid) {
|
||||||
|
// We are adding the G_Camera.zoom so it scales nicely,
|
||||||
|
// When big zoom it zoomes faster and when small zoom it zooms slower
|
||||||
|
G_Camera.wheel = Mu->window->mouse.delta_wheel * G_Camera.zoom * zoom_penalty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mu->window->key[MU_KEY_SPACE].down) {
|
||||||
|
if (Mu->window->key[MU_KEY_CONTROL].down) {
|
||||||
|
G_Camera.wheel.x = -0.5f * G_Camera.zoom.x * zoom_penalty;
|
||||||
|
} else {
|
||||||
|
G_Camera.wheel.x = 0.5f * G_Camera.zoom.x * zoom_penalty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 distance = G_Camera.wheel - target_wheel;
|
||||||
|
Vec2 wheel_step = distance * Mu->time.deltaf * 10.f;
|
||||||
|
G_Camera.wheel -= wheel_step; //
|
||||||
|
|
||||||
|
Vec2 zoom_to_point = Mu->window->mouse.posf;
|
||||||
|
Vec2 change_in_zoom = wheel_step;
|
||||||
|
Vec2 new_zoom = G_Camera.zoom + change_in_zoom;
|
||||||
|
|
||||||
|
bool stepped_any = Absolute(wheel_step.x) >= 0.0000001f && Absolute(wheel_step.y) >= 0.00000001f;
|
||||||
|
bool zoom_is_big_enough = new_zoom.y > 0.01f && new_zoom.x > 0.01f;
|
||||||
|
bool zoom_is_small_enough = new_zoom.x < 1200.f && new_zoom.y < 1200.f;
|
||||||
|
// if (stepped_any && zoom_is_small_enough && zoom_is_big_enough) {
|
||||||
|
if (1) {
|
||||||
|
Vec2 t = new_zoom / G_Camera.zoom;
|
||||||
|
|
||||||
|
M4x4 matrix = M4_Identity();
|
||||||
|
matrix = M4_Translation({zoom_to_point.x, zoom_to_point.y, 0}) * matrix;
|
||||||
|
matrix = M4_Scale({t.x, t.y, 1}) * matrix;
|
||||||
|
matrix = M4_Translation({-zoom_to_point.x, -zoom_to_point.y, 0}) * matrix;
|
||||||
|
|
||||||
|
Vec4 pos = {G_Camera.pos.x, G_Camera.pos.y, 1, 1};
|
||||||
|
pos = matrix * pos;
|
||||||
|
G_Camera.pos = {pos.x, pos.y};
|
||||||
|
G_Camera.zoom = new_zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_Camera.zoom.y > 4.0f) {
|
||||||
|
R2.font = &R2.font_very_large;
|
||||||
|
} else if (G_Camera.zoom.y > 0.25f)
|
||||||
|
R2.font = &R2.font_medium;
|
||||||
|
else {
|
||||||
|
R2.font = &R2.font_small;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mu->window->mouse.left.down) {
|
||||||
|
Vec2 pan_delta = Mu->window->mouse.delta_pos_normalized * 1000;
|
||||||
|
G_Camera.pos -= pan_delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
float speed = 8.0f;
|
||||||
|
if (Mu->window->key[MU_KEY_SHIFT].down) {
|
||||||
|
speed *= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 multiplier = G_Camera.zoom;
|
||||||
|
multiplier.x = Clamp(multiplier.x, 1.0f, 4.f);
|
||||||
|
multiplier.y = Clamp(multiplier.y, 1.0f, 4.f);
|
||||||
|
if (Mu->window->key[MU_KEY_W].down) {
|
||||||
|
G_Camera.pos.y += speed * multiplier.y;
|
||||||
|
}
|
||||||
|
if (Mu->window->key[MU_KEY_S].down) {
|
||||||
|
G_Camera.pos.y -= speed * multiplier.y;
|
||||||
|
}
|
||||||
|
if (Mu->window->key[MU_KEY_D].down) {
|
||||||
|
G_Camera.pos.x += speed * multiplier.x;
|
||||||
|
}
|
||||||
|
if (Mu->window->key[MU_KEY_A].down) {
|
||||||
|
G_Camera.pos.x -= speed * multiplier.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_Clear(Mu->window->size, {1, 1, 1, 1});
|
||||||
|
|
||||||
|
switch (prototype) {
|
||||||
|
case VIS_Prototypes_Plot: {
|
||||||
|
PROTOTYPE_Plot();
|
||||||
|
} break;
|
||||||
|
case VIS_Prototypes_TextEditor: {
|
||||||
|
PROTOTYPE_TextEditor();
|
||||||
|
} break;
|
||||||
|
case VIS_Prototypes_DataHandling: {
|
||||||
|
PROTOTYPE_DataHandling();
|
||||||
|
} break;
|
||||||
|
IO_InvalidDefaultCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI_EndFrame();
|
||||||
|
U_EndFrame();
|
||||||
|
#if 1
|
||||||
|
R2_DebugString("Camera_Pos: %f %f", G_Camera.pos.x, G_Camera.pos.y);
|
||||||
|
R2_DebugString("Zoom: %f %f", G_Camera.zoom.x, G_Camera.zoom.y);
|
||||||
|
R2_DebugString("update: %f", Mu->time.update);
|
||||||
|
R2_DebugString("points: %llu", G_PointCount);
|
||||||
|
G_PointCount = 0;
|
||||||
|
// R2_DebugString("loading_globals: %f", T_LoadingGlobals);
|
||||||
|
// R2_DebugString("draw_routines: %f", T_PlotDrawRoutines);
|
||||||
|
// R2_DebugString("series_processing: %f", T_PlotSeriesProcessing);
|
||||||
|
// R2_DebugString("total_events: %f", T_PlotTotal);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
R2_DrawCircle(Mu->window->sizef / 2, Mu->window->size.x / 2.f * t_transition, HSLToRGB({0.7861f, 0.2f, 0.95f, 1.0f}));
|
||||||
|
R2_EndFrame({(float)Mu->window->size.x, (float)Mu->window->size.y});
|
||||||
|
}
|
||||||
|
}
|
||||||
600
src/visualize/vis_plot_canvas.cpp
Executable file
600
src/visualize/vis_plot_canvas.cpp
Executable file
@@ -0,0 +1,600 @@
|
|||||||
|
bool PLOT_Initialized;
|
||||||
|
|
||||||
|
struct PLOT_Point {
|
||||||
|
Vec2 render_pos;
|
||||||
|
Vec2F64 pos;
|
||||||
|
Vec4 color;
|
||||||
|
bool was_selected;
|
||||||
|
uint32_t name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PLOT_Series {
|
||||||
|
PLOT_Series *next;
|
||||||
|
VIS_Node event;
|
||||||
|
PLOT_Point *points;
|
||||||
|
int64_t point_count;
|
||||||
|
|
||||||
|
Vec4 color;
|
||||||
|
int color_index;
|
||||||
|
|
||||||
|
Rect2F64 range;
|
||||||
|
int *bins;
|
||||||
|
};
|
||||||
|
|
||||||
|
// We make a full copy of the data structures, afterwards during a frame
|
||||||
|
// we don't access the shared memory stuff. The reason for that is let's
|
||||||
|
// for example say user decides to add some new data after we have computed
|
||||||
|
// value ranges or even better after we have assigned colors to series.
|
||||||
|
// Then we could access for example a data point that is not accounted for etc.
|
||||||
|
struct PLOT_Plot {
|
||||||
|
PLOT_Plot *next;
|
||||||
|
VIS_Node event;
|
||||||
|
|
||||||
|
Vec2F64 pos;
|
||||||
|
|
||||||
|
int64_t series_count;
|
||||||
|
PLOT_Series *first;
|
||||||
|
PLOT_Series *last;
|
||||||
|
|
||||||
|
int64_t point_count;
|
||||||
|
Rect2F64 range;
|
||||||
|
};
|
||||||
|
MA_Arena PLOT_Arena;
|
||||||
|
PLOT_Plot *PLOT_First;
|
||||||
|
PLOT_Plot *PLOT_Last;
|
||||||
|
RandomSeed PLOT_FrameSeriesColorSeed;
|
||||||
|
|
||||||
|
void PLOT_DrawBackgroundAndTitle(Rect2 rect, S8_String title) {
|
||||||
|
R2_DrawRect(rect, COLOR_background_plot);
|
||||||
|
|
||||||
|
// Draw label
|
||||||
|
// We also need to handle here the behaviour of centering
|
||||||
|
// in zoomed out view.
|
||||||
|
{
|
||||||
|
float label_scale = G_Camera.zoom.y * 1.0f;
|
||||||
|
float label_t = 0;
|
||||||
|
if (G_Camera.zoom.y > 0.5f) {
|
||||||
|
label_t = 0.0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float t = ((1.f / G_Camera.zoom.y) - 2.0f) / 4.0f;
|
||||||
|
label_t = Clamp01(Lerp(0, 1, t));
|
||||||
|
if (t <= 1.0f) {
|
||||||
|
label_scale = Lerp(label_scale, 1.0f, PingPong(t));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
label_scale = Lerp(0.5f, 1.0f, PingPong(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 plot_midtop = rect.max;
|
||||||
|
plot_midtop.x -= GetXSize(rect) / 2;
|
||||||
|
plot_midtop.y += 2;
|
||||||
|
|
||||||
|
Vec2 plot_mid = rect.min + (rect.max - rect.min) / 2.f;
|
||||||
|
|
||||||
|
// We want T that is 0 when zoom is 4 or less
|
||||||
|
|
||||||
|
Vec2 label_pos = Lerp(plot_midtop, plot_mid, label_t);
|
||||||
|
|
||||||
|
S8_String label_string = title;
|
||||||
|
Vec2 top_label_size = R2_GetStringSize(label_string, label_scale);
|
||||||
|
label_pos.x -= top_label_size.x / 2;
|
||||||
|
R2.vertex_list = &R2.ui_vertex_list;
|
||||||
|
R2_DrawString(label_pos, label_string, COLOR_text, label_scale);
|
||||||
|
R2.vertex_list = &R2.base_vertex_list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PLOT_DrawMetrics(Rect2 rect, S8_String xlabel, S8_String ylabel, Rect2F64 range, Vec4 text_color) {
|
||||||
|
float axis_number_scale = G_Camera.zoom.y * 0.5f;
|
||||||
|
Vec2 plot_size = CalcSize(rect);
|
||||||
|
|
||||||
|
// X and Y at 0,0
|
||||||
|
{
|
||||||
|
{
|
||||||
|
S8_String x_value_string = S8_Format(G_Frame, "%.02f", range.min.x);
|
||||||
|
Vec2 string_size = R2_GetStringSize(x_value_string, axis_number_scale);
|
||||||
|
Vec2 xpos = {rect.min.x - string_size.x / 2, rect.min.y - string_size.y};
|
||||||
|
R2_DrawString(xpos, x_value_string, text_color, axis_number_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
S8_String y_value_string = S8_Format(G_Frame, "%.02f", range.min.y);
|
||||||
|
Vec2 string_size = R2_GetStringSize(y_value_string, axis_number_scale);
|
||||||
|
Vec2 ypos = {rect.min.x - string_size.x + R2.font->descent * G_Camera.zoom.y, rect.min.y};
|
||||||
|
R2_DrawString(ypos, y_value_string, text_color, axis_number_scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 rect_size = plot_size;
|
||||||
|
float subdivisions = 10.f;
|
||||||
|
float start_value = 1.f / subdivisions;
|
||||||
|
float step_value = 1.f / subdivisions;
|
||||||
|
float line_half_size = rect_size.y * 0.001f;
|
||||||
|
|
||||||
|
// X Numbers
|
||||||
|
for (double it = start_value; it < 1.1f; it += step_value) {
|
||||||
|
double x_value = it * GetXSize(range);
|
||||||
|
Rect2 line = {rect.min.x, rect.min.y, rect.min.x, rect.max.y};
|
||||||
|
line.min.x += (float)it * rect_size.x;
|
||||||
|
line.max.x += (float)it * rect_size.x;
|
||||||
|
line.min.x -= line_half_size;
|
||||||
|
line.max.x += line_half_size;
|
||||||
|
R2_DrawRect(line, COLOR_plot_lines);
|
||||||
|
|
||||||
|
S8_String x_value_string = S8_Format(G_Frame, "%.02f", x_value);
|
||||||
|
Vec2 string_size = R2_GetStringSize(x_value_string, axis_number_scale);
|
||||||
|
R2_DrawString({line.min.x - string_size.x / 2, line.min.y - string_size.y}, x_value_string, text_color, axis_number_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Y Numbers
|
||||||
|
float y_label_max_x_string_size = 0;
|
||||||
|
for (float it = start_value; it < 1.1f; it += step_value) {
|
||||||
|
double y_value = it * GetYSize(range);
|
||||||
|
|
||||||
|
Rect2 line = {rect.min.x, rect.min.y, rect.max.x, rect.min.y};
|
||||||
|
line.min.y += it * rect_size.y;
|
||||||
|
line.max.y += it * rect_size.y;
|
||||||
|
line.min.y -= line_half_size;
|
||||||
|
line.max.y += line_half_size;
|
||||||
|
R2_DrawRect(line, COLOR_plot_lines);
|
||||||
|
|
||||||
|
S8_String y_value_string = S8_Format(G_Frame, "%.02f", y_value);
|
||||||
|
Vec2 string_size = R2_GetStringSize(y_value_string, axis_number_scale);
|
||||||
|
y_label_max_x_string_size = Maximum(y_label_max_x_string_size, string_size.x);
|
||||||
|
R2_DrawString({line.min.x - string_size.x + R2.font->descent, line.min.y}, y_value_string, text_color, axis_number_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// X label
|
||||||
|
{
|
||||||
|
S8_String s = xlabel;
|
||||||
|
Vec2 pos = rect.min + Vec2{GetXSize(rect) / 2.f, 0};
|
||||||
|
Vec2 ssize = R2_GetStringSize(s, axis_number_scale * 2.0f);
|
||||||
|
|
||||||
|
pos.y -= ssize.y * 2.0f;
|
||||||
|
pos.x -= ssize.x / 2.0f;
|
||||||
|
R2_DrawString(pos, s, text_color, axis_number_scale * 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Y label
|
||||||
|
{
|
||||||
|
S8_String s = ylabel;
|
||||||
|
Vec2 pos = rect.min + Vec2{0, GetYSize(rect) / 2.f};
|
||||||
|
Vec2 ssize = R2_GetStringSize(s, axis_number_scale * 2.0f);
|
||||||
|
|
||||||
|
pos.x -= y_label_max_x_string_size * 2;
|
||||||
|
pos.y -= ssize.y / 2.0f;
|
||||||
|
R2_DrawString(pos, s, text_color, axis_number_scale * 2.0f, PI32 * 0.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PROTOTYPE_Plot() {
|
||||||
|
PLOT_FrameSeriesColorSeed = {4};
|
||||||
|
|
||||||
|
// Initialization, Initialized gets switched at end of frame
|
||||||
|
if (!PLOT_Initialized) {
|
||||||
|
VIS_CreateSharedMemory();
|
||||||
|
U_Notification(60.0f * 60.0f * 48.0f, "No program connected ... ");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool generate_3plots_data = false;
|
||||||
|
|
||||||
|
S8_String events_dump_name = S8_Lit("events.bin");
|
||||||
|
|
||||||
|
// Generate data
|
||||||
|
if (generate_3plots_data) {
|
||||||
|
|
||||||
|
VIS_Plot(0);
|
||||||
|
VIS_Title("Different application metrics ");
|
||||||
|
VIS_XLabel("Frame number");
|
||||||
|
VIS_YLabel("Time in miliseconds");
|
||||||
|
|
||||||
|
VIS_Series(0);
|
||||||
|
VIS_SeriesName("Update without sleep");
|
||||||
|
VIS_Point((double)Mu->frame, (double)Mu->time.update);
|
||||||
|
|
||||||
|
VIS_Series(1);
|
||||||
|
VIS_SeriesName("Update with sleep");
|
||||||
|
VIS_Point((double)Mu->frame, (double)Mu->time.update_total);
|
||||||
|
|
||||||
|
VIS_Series(2);
|
||||||
|
VIS_SeriesName("Delta time");
|
||||||
|
VIS_Point((double)Mu->frame, (double)Mu->time.deltaf);
|
||||||
|
|
||||||
|
VIS_Plot(1);
|
||||||
|
VIS_PlotKind(VIS_PlotKind_Bar);
|
||||||
|
VIS_NamedValue("Allocated", (double)PLOT_Arena.len);
|
||||||
|
VIS_NamedValue("Reserved", (double)PLOT_Arena.memory.reserve);
|
||||||
|
VIS_NamedValue("Commited", (double)PLOT_Arena.memory.commit);
|
||||||
|
|
||||||
|
double random = GetRandomNormal(&PLOT_RandomSeed);
|
||||||
|
uint64_t hash = HashBytes(&random, sizeof(random));
|
||||||
|
|
||||||
|
VIS_Plot(2);
|
||||||
|
VIS_PlotKind(VIS_PlotKind_Histogram);
|
||||||
|
VIS_Title("XOR Shift random distribution");
|
||||||
|
VIS_Value((double)random);
|
||||||
|
|
||||||
|
VIS_Plot(3);
|
||||||
|
VIS_PlotKind(VIS_PlotKind_Histogram);
|
||||||
|
VIS_Title("FNV Hash distribution");
|
||||||
|
VIS_Value((double)hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static bool menu_open;
|
||||||
|
|
||||||
|
U_PushLayout({
|
||||||
|
U_GrowthRule_Down | U_SizeRule_MatchText | U_AlignRule_Center,
|
||||||
|
{UI_RectPadding.x, Mu->window->sizef.y - UI_RectPadding.y - 35},
|
||||||
|
{ 75, 35},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (U_Checkbox(&menu_open, "Menu")) {
|
||||||
|
U_PushLayoutIndent(0);
|
||||||
|
|
||||||
|
if (U_Button("Close menu")) {
|
||||||
|
menu_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
U_PopLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
U_PopLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context menu
|
||||||
|
{
|
||||||
|
|
||||||
|
static bool ui_context_menu_open;
|
||||||
|
static Vec2 ui_context_menu_pos;
|
||||||
|
if (Mu->window->mouse.right.press) {
|
||||||
|
if (ui_context_menu_open == false) ui_context_menu_open = true;
|
||||||
|
ui_context_menu_pos = Mu->window->mouse.posf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ui_context_menu_open) {
|
||||||
|
U_PushLayout({
|
||||||
|
U_GrowthRule_Down | U_SizeRule_Exact | U_AlignRule_Left | U_AnimationRule_Off,
|
||||||
|
ui_context_menu_pos,
|
||||||
|
{400, 35},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (U_ClickedOutsideUI || U_ClickedUnpress) ui_context_menu_open = false;
|
||||||
|
|
||||||
|
if (U_Button("Post notification")) {
|
||||||
|
U_Notification(4.0f, "Very cool text and stuff!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (U_Button("Reset camera")) {
|
||||||
|
G_Camera.zoom = {1, 1};
|
||||||
|
G_Camera.pos = {0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (U_Button("Generate constant data")) {
|
||||||
|
generate_3plots_data = !generate_3plots_data;
|
||||||
|
}
|
||||||
|
if (U_Button("Generate sine wave")) {
|
||||||
|
double t = Mu->time.totalf * 8;
|
||||||
|
double step = 32.0f / 48000.f;
|
||||||
|
VIS_Plot(0);
|
||||||
|
VIS_Title("Sine wave time");
|
||||||
|
VIS_SeriesName("Sine wave");
|
||||||
|
for (int i = 0; i < 48000; i += 1) {
|
||||||
|
VIS_Point((double)i, sin(t));
|
||||||
|
t += step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (U_Button("Save events to file")) {
|
||||||
|
S8_String shared_memory = S8_Make((char *)VIS_HeaderPointer, VIS_BUFFER_SIZE);
|
||||||
|
OS_WriteFile(events_dump_name, shared_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (U_Button("Load events from file")) {
|
||||||
|
S8_String events_dump = OS_ReadFile(G_Frame, events_dump_name);
|
||||||
|
MA_MemoryCopy(VIS_HeaderPointer, events_dump.str, events_dump.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
U_PopLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float alpha_for_zoom = EaseInCubic(Clamp01(G_Camera.zoom.y * 4.0f));
|
||||||
|
|
||||||
|
MA_Reset(&PLOT_Arena);
|
||||||
|
PLOT_First = 0;
|
||||||
|
PLOT_Last = 0;
|
||||||
|
|
||||||
|
// int MEASURE_ID = 2;
|
||||||
|
// VIS_BeginMeasure(MEASURE_ID);
|
||||||
|
// VIS_EndMeasure(MEASURE_ID);
|
||||||
|
|
||||||
|
Vec2F64 PLOT_Pos = {Mu->window->sizef.x / 2, Mu->window->sizef.y / 2};
|
||||||
|
for (VIS_Node *P = VIS_GetNode(VIS_HeaderPointer->first_plot); P; P = VIS_GetNode(P->next)) {
|
||||||
|
PLOT_Plot *it_plot = MA_PushStruct(&PLOT_Arena, PLOT_Plot);
|
||||||
|
{
|
||||||
|
it_plot->event = *P;
|
||||||
|
it_plot->range = {INFINITY, INFINITY, 0, 0};
|
||||||
|
it_plot->pos = PLOT_Pos;
|
||||||
|
PLOT_Pos.y -= 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (VIS_Node *S = VIS_GetNode(P->plot.first_series); S; S = VIS_GetNode(S->next)) {
|
||||||
|
PLOT_Series *it_series = MA_PushStruct(&PLOT_Arena, PLOT_Series);
|
||||||
|
{
|
||||||
|
it_plot->series_count += 1;
|
||||||
|
it_series->event = *S;
|
||||||
|
it_series->range = {INFINITY, INFINITY, 0, 0};
|
||||||
|
it_series->color = R2_GetColor(&it_series->color_index);
|
||||||
|
SLL_QUEUE_ADD(it_plot->first, it_plot->last, it_series);
|
||||||
|
// R2.circle.colors[color_index].a = alpha_for_zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (P->plot.plot_kind == VIS_PlotKind_Histogram) {
|
||||||
|
it_series->bins = MA_PushArray(&PLOT_Arena, int, P->plot.histogram_bin_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
it_series->points = MA_BeginPackedArray(&PLOT_Arena, PLOT_Point);
|
||||||
|
for (VIS_Node *E = VIS_GetNode(S->series.first_event); E; E = VIS_GetNode(E->next)) {
|
||||||
|
double x = E->point[0];
|
||||||
|
double y = E->point[1];
|
||||||
|
it_series->range.min.x = Minimum(it_series->range.min.x, x);
|
||||||
|
it_series->range.min.y = Minimum(it_series->range.min.y, y);
|
||||||
|
it_series->range.max.x = Maximum(it_series->range.max.x, x);
|
||||||
|
it_series->range.max.y = Maximum(it_series->range.max.y, y);
|
||||||
|
|
||||||
|
PLOT_Point *point = MA_PushStruct(&PLOT_Arena, PLOT_Point);
|
||||||
|
point->pos = {x, y};
|
||||||
|
if (E->kind == VIS_Kind_NamedPoint) {
|
||||||
|
point->name = E->named_value.string;
|
||||||
|
}
|
||||||
|
it_series->point_count += 1;
|
||||||
|
it_plot->point_count += 1;
|
||||||
|
}
|
||||||
|
int point_count = MA_EndPackedArray(&PLOT_Arena);
|
||||||
|
IO_Assert(it_series->point_count == point_count);
|
||||||
|
|
||||||
|
double offset = 0.0001f;
|
||||||
|
if (it_series->range.min.x == it_series->range.max.x) {
|
||||||
|
it_series->range.min.x -= offset;
|
||||||
|
it_series->range.max.x += offset;
|
||||||
|
}
|
||||||
|
if (it_series->range.min.y == it_series->range.max.y) {
|
||||||
|
it_series->range.min.y -= offset;
|
||||||
|
it_series->range.max.y += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
it_plot->range.min.x = Minimum(it_series->range.min.x, it_plot->range.min.x);
|
||||||
|
it_plot->range.min.y = Minimum(it_series->range.min.y, it_plot->range.min.y);
|
||||||
|
it_plot->range.max.x = Maximum(it_series->range.max.x, it_plot->range.max.x);
|
||||||
|
it_plot->range.max.y = Maximum(it_series->range.max.y, it_plot->range.max.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec4 background_plot_color = COLOR_background_plot;
|
||||||
|
Vec4 text_color = COLOR_text;
|
||||||
|
text_color.a = alpha_for_zoom;
|
||||||
|
|
||||||
|
bool should_render = (alpha_for_zoom > 0.1f);
|
||||||
|
|
||||||
|
Rect2F64 range = it_plot->range;
|
||||||
|
Rect2 rect = Rect2_Center(Vec2F64ToVec2(it_plot->pos), {300, 300});
|
||||||
|
{
|
||||||
|
rect.min *= G_Camera.zoom;
|
||||||
|
rect.max *= G_Camera.zoom;
|
||||||
|
rect.min -= G_Camera.pos;
|
||||||
|
rect.max -= G_Camera.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2 window_rect = {0, 0, Mu->window->sizef.x, Mu->window->sizef.y};
|
||||||
|
if (AreColliding(rect, window_rect)) {
|
||||||
|
|
||||||
|
Vec2 plot_size = CalcSize(rect);
|
||||||
|
S8_String title = VIS_GetString(P->plot.title);
|
||||||
|
PLOT_DrawBackgroundAndTitle(rect, title);
|
||||||
|
|
||||||
|
Rect2F64 range = it_plot->range;
|
||||||
|
if (P->plot.plot_kind == VIS_PlotKind_Histogram) {
|
||||||
|
range = {it_plot->range.min.y, 0, it_plot->range.max.y, (double)it_plot->point_count};
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String xlabel = VIS_GetString(P->plot.xlabel);
|
||||||
|
S8_String ylabel = VIS_GetString(P->plot.ylabel);
|
||||||
|
PLOT_DrawMetrics(rect, xlabel, ylabel, range, text_color);
|
||||||
|
|
||||||
|
if (should_render) {
|
||||||
|
|
||||||
|
switch (P->plot.plot_kind) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// HISTOGRAM
|
||||||
|
//
|
||||||
|
case VIS_PlotKind_Histogram: {
|
||||||
|
|
||||||
|
double range_step = 1.0 / (double)P->plot.histogram_bin_count;
|
||||||
|
double bar_x_size = range_step * plot_size.x;
|
||||||
|
float series_alpha = 1.0f;
|
||||||
|
float alpha_step = 1.0f / (float)it_plot->series_count;
|
||||||
|
|
||||||
|
for (PLOT_Series *it = it_plot->first; it; it = it->next) {
|
||||||
|
if (it->point_count == 0) continue;
|
||||||
|
VIS_Node *S = &it->event;
|
||||||
|
Vec4 color = it->color;
|
||||||
|
color.a = alpha_for_zoom * series_alpha;
|
||||||
|
series_alpha -= alpha_step;
|
||||||
|
|
||||||
|
for (int64_t point_i = 0; point_i < it->point_count; point_i += 1) {
|
||||||
|
PLOT_Point *point = it->points + point_i;
|
||||||
|
double y = point->pos.y;
|
||||||
|
Rect2F64 r = it->range;
|
||||||
|
double normy = (y - r.min.y) / (r.max.y - r.min.y);
|
||||||
|
|
||||||
|
double bin_indexf = normy / range_step;
|
||||||
|
int bin_index = (int)bin_indexf;
|
||||||
|
|
||||||
|
// Special case: Only one value in entire set
|
||||||
|
if (bin_index == P->plot.histogram_bin_count) {
|
||||||
|
for (int i = 0; i < P->plot.histogram_bin_count; i += 1) {
|
||||||
|
it->bins[i] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bin_index < 0 || bin_index >= P->plot.histogram_bin_count) continue;
|
||||||
|
it->bins[bin_index] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool already_selected = false;
|
||||||
|
for (int i = 0; i < P->plot.histogram_bin_count; i += 1) {
|
||||||
|
Vec4 bar_color = color;
|
||||||
|
|
||||||
|
int bin_it = it->bins[i];
|
||||||
|
double height_normalized = (double)bin_it / (double)it->point_count;
|
||||||
|
double height_in_plot_sizing = height_normalized * plot_size.y;
|
||||||
|
|
||||||
|
double i_normalized = (double)i / (double)P->plot.histogram_bin_count;
|
||||||
|
double bar_position_in_plot_sizing = i_normalized * plot_size.x;
|
||||||
|
|
||||||
|
Rect2F64 bar_rect_f64 = {
|
||||||
|
bar_position_in_plot_sizing,
|
||||||
|
0,
|
||||||
|
bar_position_in_plot_sizing + bar_x_size,
|
||||||
|
height_in_plot_sizing,
|
||||||
|
};
|
||||||
|
Rect2 bar_rect = Rect2F64ToRect2(bar_rect_f64);
|
||||||
|
bar_rect.min += rect.min;
|
||||||
|
bar_rect.max += rect.min;
|
||||||
|
|
||||||
|
if (already_selected == false && AreColliding(bar_rect, Mu->window->mouse.posf)) {
|
||||||
|
bar_color = COLOR_highlighted_data_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
R2_DrawRect(bar_rect, bar_color);
|
||||||
|
|
||||||
|
if (already_selected == false && AreColliding(bar_rect, Mu->window->mouse.posf)) {
|
||||||
|
S8_String name = VIS_GetString(it->event.series.name);
|
||||||
|
|
||||||
|
U_Popup(Mu->window->mouse.posf, "%d. %.*s\ndata points in range :: %d(%f%%)", it->event.id, S8_Expand(name), bin_it, height_normalized);
|
||||||
|
already_selected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Bar graphs
|
||||||
|
//
|
||||||
|
case VIS_PlotKind_Bar: {
|
||||||
|
float range_step = 1.0f / it_plot->series_count;
|
||||||
|
float xrange_min = 0.0f;
|
||||||
|
for (PLOT_Series *it_series = it_plot->first; it_series; it_series = it_series->next, xrange_min += range_step) {
|
||||||
|
Rect2 bar_rect = {xrange_min, 0, xrange_min + range_step, 0};
|
||||||
|
|
||||||
|
// Sort points, smaller first
|
||||||
|
for (int64_t point_i = 0; point_i < it_series->point_count - 1; point_i += 1) {
|
||||||
|
for (int64_t point_j = 0; point_j < it_series->point_count - 1; point_j += 1) {
|
||||||
|
PLOT_Point *it_point = it_series->points + point_j;
|
||||||
|
PLOT_Point *next_point = it_series->points + point_j + 1;
|
||||||
|
if (it_point->pos.y > next_point->pos.y) {
|
||||||
|
Swap(*it_point, *next_point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float point_alpha = 1.0f;
|
||||||
|
float alpha_step = 1.0f / (float)it_series->point_count;
|
||||||
|
|
||||||
|
double y_cursor = 0;
|
||||||
|
for (int64_t point_i = 0; point_i < it_series->point_count; point_i += 1) {
|
||||||
|
Vec4 color = it_series->color;
|
||||||
|
color.a = alpha_for_zoom * point_alpha;
|
||||||
|
point_alpha -= alpha_step;
|
||||||
|
|
||||||
|
PLOT_Point *it_point = it_series->points + point_i;
|
||||||
|
|
||||||
|
Rect2 point_rect = bar_rect;
|
||||||
|
double height = (it_point->pos.y - it_plot->range.min.y) / (it_plot->range.max.y - it_plot->range.min.y);
|
||||||
|
{
|
||||||
|
if (height == 0) height += 0.0001f;
|
||||||
|
point_rect.min.y = (float)y_cursor;
|
||||||
|
point_rect.max.y = (float)height;
|
||||||
|
y_cursor = height;
|
||||||
|
|
||||||
|
point_rect.min = point_rect.min * plot_size;
|
||||||
|
point_rect.max = point_rect.max * plot_size;
|
||||||
|
point_rect.min = point_rect.min + rect.min;
|
||||||
|
point_rect.max = point_rect.max + rect.min;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AreColliding(point_rect, Mu->window->mouse.posf)) {
|
||||||
|
S8_String series_name = VIS_GetString(it_series->event.series.name);
|
||||||
|
S8_String value_name = VIS_GetString(it_point->name);
|
||||||
|
U_Popup(Mu->window->mouse.posf, "%d%.*s :: %.*s %f(%f%%)", it_series->event.id, S8_Expand(series_name), S8_Expand(value_name), it_point->pos.y, height);
|
||||||
|
color += 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
R2_DrawRect(point_rect, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// SCATTER
|
||||||
|
//
|
||||||
|
case VIS_PlotKind_Line:
|
||||||
|
case VIS_PlotKind_Scatter: {
|
||||||
|
|
||||||
|
float circle_size = 2.0f * G_Camera.zoom.y;
|
||||||
|
R2.circle.radius = 2.0f * G_Camera.zoom.y;
|
||||||
|
float line_thickness = 0.2f * G_Camera.zoom.y;
|
||||||
|
Vec4 highlighted_data_point_color = COLOR_highlighted_data_point;
|
||||||
|
|
||||||
|
for (PLOT_Series *it_series = it_plot->first; it_series; it_series = it_series->next) {
|
||||||
|
VIS_Node *S = &it_series->event;
|
||||||
|
for (int64_t point_i = 0; point_i < it_series->point_count; point_i += 1) {
|
||||||
|
PLOT_Point *pit = it_series->points + point_i;
|
||||||
|
Vec2 norm_pos = Vec2F64ToVec2((pit->pos - it_plot->range.min) / (it_plot->range.max - it_plot->range.min));
|
||||||
|
Vec2 point_scaled_to_plot_size = norm_pos * plot_size; // <0, plot_size>
|
||||||
|
Vec2 point_moved_to_plot_pos = point_scaled_to_plot_size + rect.min; // Move to rect poit_seriesion
|
||||||
|
|
||||||
|
R2_DrawCircleFast(point_moved_to_plot_pos, it_series->color_index);
|
||||||
|
|
||||||
|
pit->color = it_series->color;
|
||||||
|
pit->render_pos = point_moved_to_plot_pos;
|
||||||
|
if (AreColliding(point_moved_to_plot_pos, circle_size, Mu->window->mouse.posf)) {
|
||||||
|
pit->was_selected = true;
|
||||||
|
pit->color = highlighted_data_point_color;
|
||||||
|
S8_String xlabel = VIS_GetString(P->plot.xlabel);
|
||||||
|
S8_String ylabel = VIS_GetString(P->plot.ylabel);
|
||||||
|
S8_String name = VIS_GetString(S->series.name);
|
||||||
|
U_Popup(Mu->window->mouse.posf, "%d.%.*s\n%.*s :: %f\n%.*s :: %f", (int)S->id, S8_Expand(name), S8_Expand(xlabel), pit->pos.x, S8_Expand(ylabel), pit->pos.y);
|
||||||
|
|
||||||
|
R2.vertex_list = &R2.ui_vertex_list;
|
||||||
|
R2_DrawCircle(point_moved_to_plot_pos, R2.circle.radius, pit->color);
|
||||||
|
R2.vertex_list = &R2.base_vertex_list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (P->plot.plot_kind == VIS_PlotKind_Line) {
|
||||||
|
for (PLOT_Series *it_series = it_plot->first; it_series; it_series = it_series->next) {
|
||||||
|
for (int64_t plot_i = 0; plot_i < it_series->point_count; plot_i += 1) {
|
||||||
|
PLOT_Point *pit = it_series->points + plot_i;
|
||||||
|
if (plot_i < it_series->point_count - 1) {
|
||||||
|
PLOT_Point *next_pit = it_series->points + plot_i + 1;
|
||||||
|
Vec4 line_color = pit->color;
|
||||||
|
if (next_pit->was_selected) line_color = next_pit->color;
|
||||||
|
R2_DrawLine(pit->render_pos, next_pit->render_pos, line_thickness, line_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
IO_InvalidDefaultCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PLOT_Initialized) PLOT_Initialized = !PLOT_Initialized;
|
||||||
|
}
|
||||||
72
src/visualize/vis_types.h
Executable file
72
src/visualize/vis_types.h
Executable file
@@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
typedef struct Camera Camera;
|
||||||
|
struct Camera {
|
||||||
|
Vec2 zoom;
|
||||||
|
Vec2 pos;
|
||||||
|
Vec2 target_pos;
|
||||||
|
Vec2 wheel;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int VIS_Prototypes;
|
||||||
|
enum {
|
||||||
|
VIS_Prototypes_Plot,
|
||||||
|
VIS_Prototypes_TextEditor,
|
||||||
|
VIS_Prototypes_DataHandling,
|
||||||
|
VIS_Prototypes_Audio,
|
||||||
|
VIS_Prototypes_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MAP_Entry {
|
||||||
|
MAP_Entry *next;
|
||||||
|
void *pointer;
|
||||||
|
uint64_t hash;
|
||||||
|
uint64_t key;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MAP_Map {
|
||||||
|
MAP_Entry *entries;
|
||||||
|
int entry_count;
|
||||||
|
int occupied_entry_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
MAP_Entry *MAP_GetOrInsert(MA_Arena *arena, MAP_Map *map, uint64_t key) {
|
||||||
|
if (map->entries == 0) {
|
||||||
|
map->entry_count = 4096;
|
||||||
|
map->entries = MA_PushArray(arena, MAP_Entry, map->entry_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t hash = HashBytes(&key, sizeof(key));
|
||||||
|
if (hash == 0) hash += 1;
|
||||||
|
|
||||||
|
int index = WRAP_AROUND_POWER_OF_2(hash, map->entry_count);
|
||||||
|
MAP_Entry *it = map->entries + index;
|
||||||
|
for (;;) {
|
||||||
|
if (it->hash == 0) {
|
||||||
|
it->key = key;
|
||||||
|
it->hash = hash;
|
||||||
|
map->occupied_entry_count += 1;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
if (it->hash == hash && it->key == key) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
if (it->next == 0) {
|
||||||
|
if (arena) {
|
||||||
|
it->next = MA_PushStruct(arena, MAP_Entry);
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String VIS_GetString(uint32_t offset) {
|
||||||
|
S8_String result = S8_MakeEmpty();
|
||||||
|
VIS_String *str = (VIS_String *)VIS_Get(offset);
|
||||||
|
if (str) {
|
||||||
|
result.str = str->str;
|
||||||
|
result.len = str->len;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
392
src/visualize/vis_ui.cpp
Executable file
392
src/visualize/vis_ui.cpp
Executable file
@@ -0,0 +1,392 @@
|
|||||||
|
|
||||||
|
const int U_GrowthRule_Down = 1;
|
||||||
|
const int U_GrowthRule_Up = 2;
|
||||||
|
const int U_SizeRule_Exact = 4;
|
||||||
|
const int U_SizeRule_MatchText = 8;
|
||||||
|
const int U_AlignRule_Left = 16;
|
||||||
|
const int U_AlignRule_Center = 32;
|
||||||
|
const int U_AnimationRule_Off = 64;
|
||||||
|
const int U_LayoutRule_Absolute = 128;
|
||||||
|
|
||||||
|
struct U_Layout {
|
||||||
|
int rule_flags;
|
||||||
|
Vec2 pos;
|
||||||
|
Vec2 size;
|
||||||
|
Vec2 pos_iter;
|
||||||
|
uint64_t di;
|
||||||
|
};
|
||||||
|
|
||||||
|
const int U_Action_Button = 1;
|
||||||
|
const int U_Action_Slider = 2;
|
||||||
|
|
||||||
|
struct U_Action {
|
||||||
|
int flags;
|
||||||
|
bool pressed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct U_Widget {
|
||||||
|
S8_String string;
|
||||||
|
Vec2 pos;
|
||||||
|
Vec2 size;
|
||||||
|
Vec2 string_pos;
|
||||||
|
U_Action action;
|
||||||
|
float t;
|
||||||
|
|
||||||
|
float running_t; // @retained
|
||||||
|
float max_t; // @retained
|
||||||
|
U_Layout layout; // @absolute
|
||||||
|
|
||||||
|
uint64_t di;
|
||||||
|
uint64_t hash;
|
||||||
|
uint64_t last_touched_frame_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum U_EventKind {
|
||||||
|
U_EventKind_None,
|
||||||
|
U_EventKind_Widget,
|
||||||
|
U_EventKind_PushLayout,
|
||||||
|
U_EventKind_PopLayout,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct U_Event {
|
||||||
|
U_EventKind kind;
|
||||||
|
union {
|
||||||
|
U_Widget *widget;
|
||||||
|
U_Layout *layout;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
MA_Arena U_Arena;
|
||||||
|
Array<U_Layout *> U_FreeLayouts;
|
||||||
|
Array<U_Layout *> U_LayoutStack;
|
||||||
|
|
||||||
|
Array<U_Widget> U_RetainedWidgets;
|
||||||
|
Array<U_Event> U_Events;
|
||||||
|
Array<U_Widget *> U_WidgetCache;
|
||||||
|
Array<U_Widget *> U_FreeWidgets;
|
||||||
|
Array<U_Widget *> U_WidgetsToDraw;
|
||||||
|
|
||||||
|
U_Widget *U_Hot;
|
||||||
|
U_Widget *U_InteractingWith;
|
||||||
|
uint64_t U_DebugID;
|
||||||
|
|
||||||
|
// One frame delayed utility variables
|
||||||
|
bool U_InteractionEnded;
|
||||||
|
bool U_ClickedUnpress;
|
||||||
|
bool U_ClickedOutsideUI;
|
||||||
|
bool U_DrawDebug;
|
||||||
|
|
||||||
|
U_Widget U_NullWidget;
|
||||||
|
|
||||||
|
U_Widget *U_CreateWidget(S8_String string, int action_flags) {
|
||||||
|
// Hash the string up to '::' so that varying numbers are ok
|
||||||
|
int64_t string_to_hash_len = string.len;
|
||||||
|
for (int64_t i = 0; i < string.len; i += 1) {
|
||||||
|
if (string.str[i] == ':' && string.str[i + 1] == ':') {
|
||||||
|
string_to_hash_len = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint64_t hash = HashBytes(string.str, string_to_hash_len);
|
||||||
|
// string = S8_ReplaceAll(G_Frame, string, S8_Lit("##"), S8_Lit(""), 0);
|
||||||
|
|
||||||
|
U_Widget *widget = 0;
|
||||||
|
bool found_in_cache = false;
|
||||||
|
For(U_WidgetCache) {
|
||||||
|
if (it->hash == hash) {
|
||||||
|
widget = it;
|
||||||
|
found_in_cache = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget && widget->last_touched_frame_index == Mu->frame) {
|
||||||
|
return &U_NullWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget == 0 && U_FreeWidgets.len) widget = U_FreeWidgets.pop();
|
||||||
|
if (widget == 0) widget = MA_PushStruct(&U_Arena, U_Widget);
|
||||||
|
|
||||||
|
if (!found_in_cache) {
|
||||||
|
U_WidgetCache.add(widget);
|
||||||
|
*widget = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// @string_lifetime:
|
||||||
|
// I think we dont need to worry about the string lifetime because
|
||||||
|
// it should get updated on every frame
|
||||||
|
//
|
||||||
|
// Will see how this plays out when adding notifications
|
||||||
|
// maybe that should also be called on every frame etc.
|
||||||
|
widget->string = string;
|
||||||
|
widget->hash = hash;
|
||||||
|
widget->last_touched_frame_index = Mu->frame;
|
||||||
|
widget->di = ++U_DebugID;
|
||||||
|
widget->action.flags = action_flags;
|
||||||
|
|
||||||
|
U_Events.add({U_EventKind_Widget, widget});
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void U_PushLayout(U_Layout layout) {
|
||||||
|
layout.di = ++U_DebugID;
|
||||||
|
|
||||||
|
U_Layout *l = 0;
|
||||||
|
if (U_FreeLayouts.len) {
|
||||||
|
l = U_FreeLayouts.pop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
l = MA_PushStruct(&U_Arena, U_Layout);
|
||||||
|
}
|
||||||
|
*l = layout;
|
||||||
|
|
||||||
|
U_LayoutStack.add(l);
|
||||||
|
auto e = U_Events.alloc();
|
||||||
|
e->kind = U_EventKind_PushLayout;
|
||||||
|
e->layout = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
void U_PushLayoutIndent(float indent) {
|
||||||
|
U_Layout **l = U_LayoutStack.last();
|
||||||
|
U_Layout layout = **l;
|
||||||
|
layout.pos.x += indent;
|
||||||
|
U_PushLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void U_PopLayout() {
|
||||||
|
U_Layout *l = U_LayoutStack.pop();
|
||||||
|
auto e = U_Events.alloc();
|
||||||
|
e->kind = U_EventKind_PopLayout;
|
||||||
|
e->layout = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool U_Button(char *str, ...) {
|
||||||
|
S8_FORMAT(G_Frame, str, string_result);
|
||||||
|
U_Widget *w = U_CreateWidget(string_result, U_Action_Button);
|
||||||
|
return w->action.pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool U_Checkbox(bool *result, char *str, ...) {
|
||||||
|
S8_FORMAT(G_Frame, str, string_result);
|
||||||
|
U_Widget *w = U_CreateWidget(string_result, U_Action_Button);
|
||||||
|
if (w->action.pressed) *result = !*result;
|
||||||
|
return *result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void U_Popup(Vec2 pos, char *str, ...) {
|
||||||
|
S8_FORMAT(G_Frame, str, string_result);
|
||||||
|
U_Widget *w = U_CreateWidget(string_result, 0);
|
||||||
|
w->layout = {};
|
||||||
|
w->layout.pos = pos;
|
||||||
|
w->layout.rule_flags = U_LayoutRule_Absolute | U_AlignRule_Center | U_GrowthRule_Down | U_SizeRule_MatchText;
|
||||||
|
}
|
||||||
|
|
||||||
|
void U_Notification(float max_t, char *str, ...) {
|
||||||
|
S8_FORMAT(G_Frame, str, string_result);
|
||||||
|
U_Widget *w = U_RetainedWidgets.alloc();
|
||||||
|
w->max_t = max_t;
|
||||||
|
w->string = string_result;
|
||||||
|
w->string.str = (char *)malloc(string_result.len + 1);
|
||||||
|
w->di = ++U_DebugID;
|
||||||
|
MA_MemoryCopy(w->string.str, string_result.str, string_result.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 UI_RectPadding = {32, 8.f};
|
||||||
|
float UI_DrawBox(float t, Rect2 animated_popup_rect) {
|
||||||
|
float bounce_t = EaseOutElastic(Clamp01(t));
|
||||||
|
animated_popup_rect = ShrinkByHalfSize(animated_popup_rect, (1 - bounce_t) * CalcSize(animated_popup_rect) / 2);
|
||||||
|
Rect2 popup_rect = ExpandByHalfSize(animated_popup_rect, UI_RectPadding);
|
||||||
|
Rect2 outline_popup_rect = ExpandByHalfSize(popup_rect, {1.f, 1.f});
|
||||||
|
|
||||||
|
R2_DrawRectRounded(popup_rect, COLOR_on_hover_rect, 0.1f);
|
||||||
|
return bounce_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void U_EndFrame() {
|
||||||
|
R2.font = &R2.font_medium;
|
||||||
|
R2.vertex_list = &R2.ui_vertex_list;
|
||||||
|
IO_Assert(U_LayoutStack.len == 0);
|
||||||
|
|
||||||
|
// We move all of the "inline" function logic into the event list
|
||||||
|
// and then afterwards we only render those things that got updated
|
||||||
|
// in this frame (which got it's index updated meaning that they dont
|
||||||
|
// get evicted from cache.
|
||||||
|
|
||||||
|
For(U_Events) {
|
||||||
|
if (it.kind == U_EventKind_PushLayout) {
|
||||||
|
U_LayoutStack.add(it.layout);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (it.kind == U_EventKind_PopLayout) {
|
||||||
|
U_LayoutStack.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_Assert(it.kind == U_EventKind_Widget);
|
||||||
|
}
|
||||||
|
U_Widget *widget = it.widget;
|
||||||
|
|
||||||
|
U_Layout *layout = 0;
|
||||||
|
if (U_LayoutStack.len) layout = *U_LayoutStack.last();
|
||||||
|
if (widget->layout.rule_flags & U_LayoutRule_Absolute) layout = &widget->layout;
|
||||||
|
|
||||||
|
Vec2 string_size = R2_GetStringSize(widget->string);
|
||||||
|
|
||||||
|
if (layout->rule_flags & U_SizeRule_Exact) {
|
||||||
|
widget->size = layout->size;
|
||||||
|
}
|
||||||
|
else if (layout->rule_flags & U_SizeRule_MatchText) {
|
||||||
|
widget->size = string_size;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_InvalidCodepath();
|
||||||
|
}
|
||||||
|
|
||||||
|
widget->pos = layout->pos + layout->pos_iter;
|
||||||
|
if (layout->rule_flags & U_GrowthRule_Up) {
|
||||||
|
layout->pos_iter.y += widget->size.y;
|
||||||
|
}
|
||||||
|
else if (layout->rule_flags & U_GrowthRule_Down) {
|
||||||
|
layout->pos_iter.y -= widget->size.y;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_InvalidCodepath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout->rule_flags & U_AnimationRule_Off) {
|
||||||
|
widget->t = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2 string_rect = R2_GetStringRect(widget->pos, widget->string);
|
||||||
|
widget->string_pos = widget->pos;
|
||||||
|
// align font (aligned to baseline) to top left of rectangle
|
||||||
|
widget->string_pos.y = widget->pos.y + (widget->pos.y - string_rect.min.y);
|
||||||
|
|
||||||
|
if (layout->rule_flags & U_AlignRule_Left) {
|
||||||
|
// widget->string_pos = widget->pos + (widget->size - string_size) / 2;
|
||||||
|
widget->string_pos.y += (widget->size.y - string_size.y) / 2;
|
||||||
|
widget->string_pos.x += 32;
|
||||||
|
}
|
||||||
|
else if (layout->rule_flags & U_AlignRule_Center) {
|
||||||
|
widget->string_pos += (widget->size - string_size) / 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
IO_InvalidCodepath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float retained_y_iter = 0;
|
||||||
|
ForArrayRemovable(U_RetainedWidgets) {
|
||||||
|
ForArrayRemovablePrepare(U_RetainedWidgets);
|
||||||
|
|
||||||
|
it.t = Clamp01(it.running_t);
|
||||||
|
float completion_rate = it.running_t / it.max_t;
|
||||||
|
if (completion_rate > 1.05) {
|
||||||
|
free(it.string.str);
|
||||||
|
ForArrayRemovableDeclare();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (completion_rate > 0.8f) {
|
||||||
|
float eigth = 0.8f * it.max_t;
|
||||||
|
it.t = it.t - (it.running_t - eigth);
|
||||||
|
}
|
||||||
|
|
||||||
|
it.running_t += Mu->time.deltaf;
|
||||||
|
|
||||||
|
Vec2 string_size = R2_GetStringSize(it.string);
|
||||||
|
|
||||||
|
it.size = string_size + UI_RectPadding;
|
||||||
|
it.pos = UI_RectPadding;
|
||||||
|
it.pos.y += retained_y_iter;
|
||||||
|
retained_y_iter += string_size.y + UI_RectPadding.y * 2;
|
||||||
|
|
||||||
|
// @copy_paste: from layouting, might need to collapse it in the future
|
||||||
|
Rect2 string_rect = R2_GetStringRect(it.pos, it.string);
|
||||||
|
it.string_pos = it.pos;
|
||||||
|
// align font (aligned to baseline) to top left of rectangle
|
||||||
|
it.string_pos.y = it.pos.y + (it.pos.y - string_rect.min.y);
|
||||||
|
// align center
|
||||||
|
it.string_pos += (it.size - string_size) / 2;
|
||||||
|
U_WidgetsToDraw.add(&it);
|
||||||
|
}
|
||||||
|
|
||||||
|
ForArrayRemovable(U_WidgetCache) {
|
||||||
|
ForArrayRemovablePrepare(U_WidgetCache);
|
||||||
|
if (it->last_touched_frame_index != Mu->frame) {
|
||||||
|
ForArrayRemovableDeclare();
|
||||||
|
U_FreeWidgets.add(it);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
U_WidgetsToDraw.add(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
U_InteractionEnded = false;
|
||||||
|
U_ClickedUnpress = false;
|
||||||
|
U_ClickedOutsideUI = false;
|
||||||
|
U_Hot = 0;
|
||||||
|
For(U_WidgetsToDraw) {
|
||||||
|
bool activated = false;
|
||||||
|
Rect2 rect = Rect2_Size(it->pos, it->size);
|
||||||
|
|
||||||
|
if (AreColliding(rect, Mu->window->mouse.posf)) {
|
||||||
|
U_Hot = it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (U_Hot == it) {
|
||||||
|
if (Mu->window->mouse.left.press) {
|
||||||
|
U_InteractingWith = it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (U_InteractingWith) {
|
||||||
|
if (Mu->window->mouse.left.unpress) {
|
||||||
|
if (U_Hot == it) {
|
||||||
|
activated = true;
|
||||||
|
}
|
||||||
|
U_InteractionEnded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->action.flags & U_Action_Button) {
|
||||||
|
it->action.pressed = false;
|
||||||
|
if (activated) it->action.pressed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec4 text_color = COLOR_text;
|
||||||
|
if (it->action.flags & U_Action_Button) {
|
||||||
|
if (U_Hot == it) text_color = COLOR_highlighted_text;
|
||||||
|
if (U_InteractingWith == it) text_color = {1, 0, 0, 1};
|
||||||
|
if (activated) text_color = {0, 1, 0, 1};
|
||||||
|
}
|
||||||
|
text_color.a = UI_DrawBox(it->t, rect);
|
||||||
|
R2_DrawString(it->string_pos, it->string, text_color);
|
||||||
|
it->t += Mu->time.deltaf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mu->window->mouse.left.unpress) {
|
||||||
|
U_ClickedUnpress = true;
|
||||||
|
U_InteractingWith = 0;
|
||||||
|
}
|
||||||
|
if (Mu->window->mouse.left.press && U_InteractingWith == 0) {
|
||||||
|
U_ClickedOutsideUI = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (U_DrawDebug) {
|
||||||
|
R2_DebugString("FreeWidgets: %d", U_FreeWidgets.len);
|
||||||
|
R2_DebugString("CachedWidgets: %d", U_WidgetCache.len);
|
||||||
|
R2_DebugString("Events: %d", U_Events.len);
|
||||||
|
R2_DebugString("FreeLayouts: %d", U_FreeLayouts.len);
|
||||||
|
R2_DebugString("RetainedElements: %d", U_RetainedWidgets.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
For(U_Events) {
|
||||||
|
if (it.kind == U_EventKind_PushLayout) {
|
||||||
|
U_FreeLayouts.add(it.layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
U_Events.reset();
|
||||||
|
U_WidgetsToDraw.reset();
|
||||||
|
R2.vertex_list = &R2.base_vertex_list;
|
||||||
|
}
|
||||||
485
src/visualize/visualize.h
Executable file
485
src/visualize/visualize.h
Executable file
@@ -0,0 +1,485 @@
|
|||||||
|
#ifndef VIS_HEADER
|
||||||
|
#define VIS_HEADER
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifndef VIS_MemoryZero
|
||||||
|
#include <string.h>
|
||||||
|
#define VIS_MemoryZero(p, s) memset(p, 0, s)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef VIS_MemoryCopy
|
||||||
|
#include <string.h>
|
||||||
|
#define VIS_MemoryCopy(dst, src, size) memcpy(dst, src, size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef VIS_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define VIS_ASSERT assert
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define VIS_BUFFER_SIZE (1024 * 1024 * 32)
|
||||||
|
#define VIS_BUFFER_NAME L"Hello, inter process communication!"
|
||||||
|
#define VIS_DATA_VERSION 3
|
||||||
|
#define VIS_ROLLING_COMMAND_COUNT 8
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define VIS_FUNCTION extern "C"
|
||||||
|
#else
|
||||||
|
#define VIS_FUNCTION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct VIS_Header VIS_Header;
|
||||||
|
struct VIS_Header { // @on_update: update VIS__Clear!
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t written;
|
||||||
|
uint32_t strings_written; //@debug_info
|
||||||
|
|
||||||
|
uint32_t synchronization_counter_write;
|
||||||
|
uint32_t synchronization_counter_read;
|
||||||
|
uint8_t rolling_commands[VIS_ROLLING_COMMAND_COUNT];
|
||||||
|
|
||||||
|
// @cache
|
||||||
|
// For caching, we keep track of names of the
|
||||||
|
// plots. We don't want to send a ton of wasteful string data
|
||||||
|
uint32_t first_plot;
|
||||||
|
uint32_t last_plot;
|
||||||
|
uint32_t current_plot;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" VIS_Header *VIS_HeaderPointer;
|
||||||
|
#else
|
||||||
|
extern VIS_Header *VIS_HeaderPointer;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum {
|
||||||
|
VIS_PlotKind_Line,
|
||||||
|
VIS_PlotKind_Scatter,
|
||||||
|
VIS_PlotKind_Histogram,
|
||||||
|
VIS_PlotKind_Bar,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef uint16_t VIS_Kind;
|
||||||
|
enum {
|
||||||
|
VIS_Kind_Null,
|
||||||
|
VIS_Kind_Plot,
|
||||||
|
VIS_Kind_Series,
|
||||||
|
VIS_Kind_Point,
|
||||||
|
VIS_Kind_NamedPoint,
|
||||||
|
VIS_Kind_Measure,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct VIS_String VIS_String;
|
||||||
|
struct VIS_String {
|
||||||
|
int32_t len;
|
||||||
|
char str[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// @cache
|
||||||
|
// For caching, we keep track of names of the
|
||||||
|
// plots. We don't want to send a ton of wasteful string data
|
||||||
|
typedef struct VIS_PlotData VIS_PlotData;
|
||||||
|
struct VIS_PlotData {
|
||||||
|
uint32_t plot_kind;
|
||||||
|
uint32_t xlabel;
|
||||||
|
uint32_t ylabel;
|
||||||
|
uint32_t title;
|
||||||
|
|
||||||
|
uint64_t xlabel_hash;
|
||||||
|
uint64_t ylabel_hash;
|
||||||
|
uint64_t title_hash;
|
||||||
|
|
||||||
|
int32_t histogram_bin_count;
|
||||||
|
uint32_t first_series;
|
||||||
|
uint32_t last_series;
|
||||||
|
uint32_t current_series;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct VIS_SeriesData VIS_SeriesData;
|
||||||
|
struct VIS_SeriesData {
|
||||||
|
uint32_t first_event;
|
||||||
|
uint32_t last_event;
|
||||||
|
uint32_t name;
|
||||||
|
uint64_t name_hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define VIS_BASIC_NODE_SIZE (sizeof(uint64_t)) // @todo: maybe remove kind and id from here
|
||||||
|
typedef struct VIS_Node VIS_Node;
|
||||||
|
struct VIS_Node {
|
||||||
|
uint32_t next;
|
||||||
|
VIS_Kind kind;
|
||||||
|
uint16_t id; // @todo: maybe remove kind and id from here
|
||||||
|
|
||||||
|
union {
|
||||||
|
double point[2];
|
||||||
|
VIS_PlotData plot;
|
||||||
|
VIS_SeriesData series;
|
||||||
|
struct {
|
||||||
|
double point[2];
|
||||||
|
uint64_t string_hash;
|
||||||
|
uint32_t string;
|
||||||
|
} named_value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct VIS_Current VIS_Current;
|
||||||
|
struct VIS_Current {
|
||||||
|
VIS_Node *plot;
|
||||||
|
VIS_Node *series;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
VIS_COMMAND_NONE,
|
||||||
|
VIS_COMMAND_CONNECTED,
|
||||||
|
VIS_COMMAND_CLEAR,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define VISH_Cast8(base) ((uint8_t *)(base))
|
||||||
|
#define VISH_Get(base, offset) ((offset) == 0 ? 0 : (VISH_Cast8(base) + (offset)))
|
||||||
|
#define VISH_GetStruct(base, offset, T) ((T *)(VISH_Get(base, offset)))
|
||||||
|
#define VISH_CalcOffset(base, pointer) ((uint32_t)((uint8_t *)(pointer)-VISH_Cast8(base)))
|
||||||
|
|
||||||
|
#define VIS_Get(offset) VISH_Get(VIS_HeaderPointer, offset)
|
||||||
|
#define VIS_CalcOffset(pointer) VISH_CalcOffset(VIS_HeaderPointer, pointer)
|
||||||
|
#define VIS_GetNode(offset) VISH_GetStruct(VIS_HeaderPointer, offset, VIS_Node)
|
||||||
|
|
||||||
|
VIS_FUNCTION void VIS_WriteCommand(uint8_t command);
|
||||||
|
VIS_FUNCTION uint8_t VIS_ReadCommand();
|
||||||
|
VIS_FUNCTION void VIS_IncrementReadCommand();
|
||||||
|
VIS_FUNCTION void VIS_Synchronize();
|
||||||
|
VIS_FUNCTION void VIS_Connect();
|
||||||
|
VIS_FUNCTION void VIS_CreateSharedMemory();
|
||||||
|
|
||||||
|
static inline int32_t VIS_GetStringLength(char *string) {
|
||||||
|
int32_t len = 0;
|
||||||
|
while (*string++ != 0)
|
||||||
|
len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline VIS_Header *VIS_TryConnecting() {
|
||||||
|
if (!VIS_HeaderPointer) VIS_Connect();
|
||||||
|
return VIS_HeaderPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t VIS_HashBytes(void *data, int32_t size) {
|
||||||
|
uint8_t *data8 = (uint8_t *)data;
|
||||||
|
uint64_t hash = (uint64_t)14695981039346656037ULL;
|
||||||
|
for (int32_t i = 0; i < size; i++) {
|
||||||
|
hash = hash ^ (uint64_t)(data8[i]);
|
||||||
|
hash = hash * (uint64_t)1099511628211ULL;
|
||||||
|
}
|
||||||
|
if (hash == 0) hash += 1;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VIS_PushStruct(T) ((T *)VIS_Push(sizeof(T)))
|
||||||
|
#define VIS_PushArray(T, c) ((T *)VIS_Push((c) * sizeof(T)))
|
||||||
|
static inline void *VIS_Push(uint32_t size) {
|
||||||
|
if (VIS_HeaderPointer->written + size <= VIS_BUFFER_SIZE) {
|
||||||
|
void *result = VIS_Get(VIS_HeaderPointer->written);
|
||||||
|
VIS_HeaderPointer->written += size;
|
||||||
|
VIS_MemoryZero(result, size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline VIS_Node *VIS_PushBasicNode(VIS_Kind kind) {
|
||||||
|
VIS_Node *result = (VIS_Node *)VIS_Push(VIS_BASIC_NODE_SIZE);
|
||||||
|
if (result) result->kind = kind;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VIS_SLL_QUEUE_ADD(first, last, n) \
|
||||||
|
do { \
|
||||||
|
(n)->next = 0; \
|
||||||
|
if ((last) == 0) { \
|
||||||
|
(last) = VIS_CalcOffset((n)); \
|
||||||
|
(first) = (last); \
|
||||||
|
} else { \
|
||||||
|
VIS_Node *the_last = VIS_GetNode((last)); \
|
||||||
|
uint32_t the_next = VIS_CalcOffset((n)); \
|
||||||
|
the_last->next = the_next; \
|
||||||
|
(last) = the_next; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static inline VIS_Current VIS_GetCurrent() {
|
||||||
|
VIS_Current current;
|
||||||
|
current.plot = VIS_GetNode(VIS_HeaderPointer->current_plot);
|
||||||
|
current.series = VIS_GetNode(current.plot->plot.current_series);
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_AddNode(VIS_Node *event) {
|
||||||
|
if (event == 0) return;
|
||||||
|
VIS_Current current = VIS_GetCurrent();
|
||||||
|
VIS_SLL_QUEUE_ADD(current.series->series.first_event, current.series->series.last_event, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t VIS_PushString(char *in_string, int32_t in_len) {
|
||||||
|
int32_t *length = VIS_PushStruct(int32_t);
|
||||||
|
char *string = VIS_PushArray(char, in_len + 1);
|
||||||
|
if (length && string) {
|
||||||
|
length[0] = in_len;
|
||||||
|
VIS_MemoryCopy(string, in_string, in_len + 1);
|
||||||
|
VIS_HeaderPointer->strings_written += 1;
|
||||||
|
}
|
||||||
|
return VIS_CalcOffset(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_PushPoint(double x, double y) {
|
||||||
|
double *value = VIS_PushArray(double, 2);
|
||||||
|
if (value) {
|
||||||
|
value[0] = x;
|
||||||
|
value[1] = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_Point(double x, double y) {
|
||||||
|
if (!VIS_TryConnecting()) return;
|
||||||
|
VIS_Node *event = VIS_PushBasicNode(VIS_Kind_Point);
|
||||||
|
VIS_PushPoint(x, y);
|
||||||
|
VIS_AddNode(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_Value(double y) {
|
||||||
|
VIS_Point(0, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_NamedPoint(char *str, double x, double y) {
|
||||||
|
if (!VIS_TryConnecting()) return;
|
||||||
|
VIS_ASSERT(str && "Passed in string is null");
|
||||||
|
|
||||||
|
int32_t len = VIS_GetStringLength(str);
|
||||||
|
VIS_Current current = VIS_GetCurrent();
|
||||||
|
uint64_t hash = VIS_HashBytes(str, len);
|
||||||
|
|
||||||
|
// Try updating the node
|
||||||
|
for (VIS_Node *it = VIS_GetNode(current.series->series.first_event); it; it = VIS_GetNode(it->next)) {
|
||||||
|
if (it->kind == VIS_Kind_NamedPoint && it->named_value.string_hash == hash) {
|
||||||
|
it->named_value.point[0] = x;
|
||||||
|
it->named_value.point[1] = y;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node not found, new node
|
||||||
|
VIS_Node *event = VIS_PushBasicNode(VIS_Kind_NamedPoint);
|
||||||
|
VIS_PushPoint(x, y);
|
||||||
|
|
||||||
|
uint64_t *hash_space = VIS_PushStruct(uint64_t);
|
||||||
|
uint32_t *string_space = VIS_PushStruct(uint32_t);
|
||||||
|
if (hash_space) *hash_space = hash;
|
||||||
|
if (string_space) *string_space = VIS_PushString(str, len);
|
||||||
|
|
||||||
|
VIS_AddNode(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_NamedValue(char *str, double y) {
|
||||||
|
VIS_ASSERT(str && "Passed in string is null");
|
||||||
|
VIS_NamedPoint(str, 0, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_Title(char *title) {
|
||||||
|
if (!VIS_TryConnecting()) return;
|
||||||
|
VIS_ASSERT(title && "Passed in string is null");
|
||||||
|
int32_t len = VIS_GetStringLength(title);
|
||||||
|
VIS_Node *id = VIS_GetNode(VIS_HeaderPointer->current_plot);
|
||||||
|
uint64_t hash = VIS_HashBytes(title, len);
|
||||||
|
if (id->plot.title_hash == 0 || id->plot.title_hash != hash) {
|
||||||
|
id->plot.title = VIS_PushString(title, len);
|
||||||
|
id->plot.title_hash = hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_YLabel(char *ylabel) {
|
||||||
|
if (!VIS_TryConnecting()) return;
|
||||||
|
VIS_ASSERT(ylabel && "Passed in string is null");
|
||||||
|
int32_t len = VIS_GetStringLength(ylabel);
|
||||||
|
VIS_Node *id = VIS_GetNode(VIS_HeaderPointer->current_plot);
|
||||||
|
uint64_t hash = VIS_HashBytes(ylabel, len);
|
||||||
|
if (id->plot.ylabel_hash != hash) {
|
||||||
|
id->plot.ylabel = VIS_PushString(ylabel, len);
|
||||||
|
id->plot.ylabel_hash = hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_XLabel(char *xlabel) {
|
||||||
|
if (!VIS_TryConnecting()) return;
|
||||||
|
VIS_ASSERT(xlabel && "Passed in string is null");
|
||||||
|
int32_t len = VIS_GetStringLength(xlabel);
|
||||||
|
VIS_Node *id = VIS_GetNode(VIS_HeaderPointer->current_plot);
|
||||||
|
uint64_t hash = VIS_HashBytes(xlabel, len);
|
||||||
|
if (id->plot.xlabel_hash != hash) {
|
||||||
|
id->plot.xlabel = VIS_PushString(xlabel, len);
|
||||||
|
id->plot.xlabel_hash = hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline VIS_Node *VIS_AddGetSeries(VIS_Node *plot, uint16_t id) {
|
||||||
|
for (VIS_Node *it = VIS_GetNode(plot->plot.first_series); it; it = VIS_GetNode(it->next)) {
|
||||||
|
if (it->id == id) return it;
|
||||||
|
}
|
||||||
|
VIS_Node *series = VIS_PushStruct(VIS_Node);
|
||||||
|
series->id = id;
|
||||||
|
VIS_SLL_QUEUE_ADD(plot->plot.first_series, plot->plot.last_series, series);
|
||||||
|
return series;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline VIS_Node *VIS_AddGetPlot(uint16_t id) {
|
||||||
|
for (VIS_Node *it = VIS_GetNode(VIS_HeaderPointer->first_plot); it; it = VIS_GetNode(it->next)) {
|
||||||
|
if (it->id == id) return it;
|
||||||
|
}
|
||||||
|
VIS_Node *it = VIS_PushStruct(VIS_Node);
|
||||||
|
it->id = id;
|
||||||
|
it->plot.current_series = VIS_CalcOffset(VIS_AddGetSeries(it, 0));
|
||||||
|
it->plot.histogram_bin_count = 10;
|
||||||
|
|
||||||
|
VIS_SLL_QUEUE_ADD(VIS_HeaderPointer->first_plot, VIS_HeaderPointer->last_plot, it);
|
||||||
|
if (VIS_HeaderPointer->current_plot == 0) VIS_HeaderPointer->current_plot = VIS_CalcOffset(it);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_Series(uint16_t id) {
|
||||||
|
if (!VIS_TryConnecting()) return;
|
||||||
|
VIS_Node *plot = VIS_GetNode(VIS_HeaderPointer->current_plot);
|
||||||
|
VIS_Node *series = VIS_AddGetSeries(plot, id);
|
||||||
|
plot->plot.current_series = VIS_CalcOffset(series);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_PlotKind(uint16_t kind_id) {
|
||||||
|
if (!VIS_TryConnecting()) return;
|
||||||
|
VIS_Node *id = VIS_GetNode(VIS_HeaderPointer->current_plot);
|
||||||
|
id->plot.plot_kind = kind_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_Plot(uint16_t id) {
|
||||||
|
if (!VIS_TryConnecting()) return;
|
||||||
|
VIS_Node *plot = VIS_AddGetPlot(id);
|
||||||
|
VIS_HeaderPointer->current_plot = VIS_CalcOffset(plot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_SeriesName(char *name) {
|
||||||
|
if (!VIS_TryConnecting()) return;
|
||||||
|
VIS_ASSERT(name && "Passed in string is null");
|
||||||
|
|
||||||
|
int32_t len = VIS_GetStringLength(name);
|
||||||
|
VIS_Current c = VIS_GetCurrent();
|
||||||
|
uint64_t hash = VIS_HashBytes(name, len);
|
||||||
|
|
||||||
|
if (c.series->series.name_hash != hash) {
|
||||||
|
c.series->series.name = VIS_PushString(name, len);
|
||||||
|
c.series->series.name_hash = hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS_Clear() {
|
||||||
|
if (!VIS_TryConnecting()) return;
|
||||||
|
VIS_WriteCommand(VIS_COMMAND_CLEAR);
|
||||||
|
VIS_Synchronize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void VIS__Clear() {
|
||||||
|
VIS_HeaderPointer->strings_written = 0;
|
||||||
|
VIS_HeaderPointer->first_plot = 0;
|
||||||
|
VIS_HeaderPointer->last_plot = 0;
|
||||||
|
VIS_HeaderPointer->current_plot = 0;
|
||||||
|
VIS_HeaderPointer->written = sizeof(VIS_Header);
|
||||||
|
VIS_HeaderPointer->version = VIS_DATA_VERSION;
|
||||||
|
VIS_AddGetPlot(0);
|
||||||
|
}
|
||||||
|
#endif // VIS_HEADER
|
||||||
|
|
||||||
|
#ifdef VIS_IMPLEMENTATION
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
VIS_Header *VIS_HeaderPointer;
|
||||||
|
|
||||||
|
VIS_FUNCTION void VIS_WriteCommand(uint8_t command) {
|
||||||
|
uint32_t i = VIS_HeaderPointer->synchronization_counter_write % VIS_ROLLING_COMMAND_COUNT;
|
||||||
|
VIS_ASSERT(i >= 0 && i < VIS_ROLLING_COMMAND_COUNT);
|
||||||
|
VIS_HeaderPointer->rolling_commands[i] = command;
|
||||||
|
InterlockedIncrement(&VIS_HeaderPointer->synchronization_counter_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
VIS_FUNCTION uint8_t VIS_ReadCommand() {
|
||||||
|
if (!VIS_HeaderPointer) return 0;
|
||||||
|
if (VIS_HeaderPointer->synchronization_counter_write == VIS_HeaderPointer->synchronization_counter_read) return 0;
|
||||||
|
uint32_t i = VIS_HeaderPointer->synchronization_counter_read % VIS_ROLLING_COMMAND_COUNT;
|
||||||
|
VIS_ASSERT(i >= 0 && i < VIS_ROLLING_COMMAND_COUNT);
|
||||||
|
uint8_t result = VIS_HeaderPointer->rolling_commands[i];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIS_FUNCTION void VIS_IncrementReadCommand() {
|
||||||
|
InterlockedIncrement(&VIS_HeaderPointer->synchronization_counter_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
VIS_FUNCTION void VIS_Synchronize() {
|
||||||
|
while (VIS_HeaderPointer->synchronization_counter_write != VIS_HeaderPointer->synchronization_counter_read) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VIS_FUNCTION void VIS_Connect() {
|
||||||
|
HANDLE handle = OpenFileMappingW(
|
||||||
|
FILE_MAP_ALL_ACCESS, // read/write access
|
||||||
|
FALSE, // do not inherit the name
|
||||||
|
VIS_BUFFER_NAME);
|
||||||
|
|
||||||
|
if (handle == NULL) {
|
||||||
|
// VIS_ASSERT(!"Failed to open shared memory. Windows error:%d", GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIS_HeaderPointer = (VIS_Header *)MapViewOfFile(handle, // handle to map object
|
||||||
|
FILE_MAP_ALL_ACCESS, // read/write permission
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
VIS_BUFFER_SIZE);
|
||||||
|
|
||||||
|
if (VIS_HeaderPointer == NULL) {
|
||||||
|
// VIS_ASSERT(!"Failed to map shared memory to a buffer");
|
||||||
|
CloseHandle(handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIS_WriteCommand(VIS_COMMAND_CONNECTED);
|
||||||
|
VIS_Synchronize();
|
||||||
|
}
|
||||||
|
|
||||||
|
VIS_FUNCTION void VIS_CreateSharedMemory() {
|
||||||
|
HANDLE VIS_SharedMemoryHandle = CreateFileMappingW(
|
||||||
|
INVALID_HANDLE_VALUE, // use paging file
|
||||||
|
NULL, // default security
|
||||||
|
PAGE_READWRITE, // read/write access
|
||||||
|
0, // maximum object size (high-order DWORD)
|
||||||
|
VIS_BUFFER_SIZE, // maximum object size (low-order DWORD)
|
||||||
|
VIS_BUFFER_NAME); // name of mapping object
|
||||||
|
|
||||||
|
if (VIS_SharedMemoryHandle == NULL) {
|
||||||
|
VIS_ASSERT(!"Failed to create shared memory.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIS_HeaderPointer = (VIS_Header *)MapViewOfFile(VIS_SharedMemoryHandle, // handle to map object
|
||||||
|
FILE_MAP_ALL_ACCESS, // read/write permission
|
||||||
|
0, 0, VIS_BUFFER_SIZE);
|
||||||
|
|
||||||
|
if (VIS_HeaderPointer == NULL) {
|
||||||
|
VIS_ASSERT(!"Failed to create named shader memory buffer.");
|
||||||
|
CloseHandle(VIS_SharedMemoryHandle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIS__Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // VIS_IMPLEMENTATION
|
||||||
BIN
visualize_plot_zooms.mp4
Normal file
BIN
visualize_plot_zooms.mp4
Normal file
Binary file not shown.
Reference in New Issue
Block a user