From 56518bdd66783927be17e4b263040a35fefd2f44 Mon Sep 17 00:00:00 2001 From: PS Date: Tue, 22 Mar 2022 19:13:06 +0100 Subject: [PATCH] Day 1 --- .gitignore | 6 +- build/build.sh | 294 ++ build/build_app_msvc_win32_debug.bat | 2 +- project.4coder | 9 +- run_tree/text.txt | Bin 0 -> 37 bytes run_tree/webgl/debug/loader.html | 9 + run_tree/webgl/debug/loader.js | 130 + run_tree/webgl/debug/lumenarium.wasm | Bin 0 -> 633 bytes .../intel/debug/lumenarium_first_win32.obj | Bin 0 -> 108086 bytes .../platform_win32/win32_foldhaus_fileio.h | 406 +-- .../platform_win32/win32_foldhaus_timing.h | 40 +- src_v2/editor/lumenarium_editor.cpp | 25 + src_v2/editor/lumenarium_editor_renderer.cpp | 20 + src_v2/engine/lumenarium_engine.cpp | 25 + src_v2/engine/lumenarium_engine_assembly.h | 58 + src_v2/libs/HandmadeMath.h | 3089 +++++++++++++++++ src_v2/lumenarium_first.cpp | 87 + src_v2/lumenarium_first.h | 48 + src_v2/lumenarium_input.cpp | 81 + src_v2/lumenarium_memory.cpp | 226 ++ src_v2/lumenarium_string.cpp | 226 ++ src_v2/lumenarium_tests.cpp | 46 + src_v2/lumenarium_types.h | 106 + src_v2/platform/lumenarium_compiler_flags.h | 17 + src_v2/platform/lumenarium_platform.h | 294 ++ .../lumenarium_platform_common_includes.h | 36 + src_v2/platform/osx/lumenarium_first_osx.cpp | 28 + src_v2/platform/osx/lumenarium_osx_memory.cpp | 30 + .../platform/webgl/lumenarium_first_webgl.cpp | 18 + .../platform/win32/lumenarium_first_win32.cpp | 249 ++ .../platform/win32/lumenarium_win32_file.cpp | 258 ++ .../win32/lumenarium_win32_memory.cpp | 43 + .../win32/lumenarium_win32_thread.cpp | 93 + .../platform/win32/lumenarium_win32_time.cpp | 48 + .../win32/lumenarium_win32_window.cpp | 203 ++ 35 files changed, 6024 insertions(+), 226 deletions(-) create mode 100644 build/build.sh create mode 100644 run_tree/text.txt create mode 100644 run_tree/webgl/debug/loader.html create mode 100644 run_tree/webgl/debug/loader.js create mode 100644 run_tree/webgl/debug/lumenarium.wasm create mode 100644 run_tree/win32/intel/debug/lumenarium_first_win32.obj create mode 100644 src_v2/editor/lumenarium_editor.cpp create mode 100644 src_v2/editor/lumenarium_editor_renderer.cpp create mode 100644 src_v2/engine/lumenarium_engine.cpp create mode 100644 src_v2/engine/lumenarium_engine_assembly.h create mode 100644 src_v2/libs/HandmadeMath.h create mode 100644 src_v2/lumenarium_first.cpp create mode 100644 src_v2/lumenarium_first.h create mode 100644 src_v2/lumenarium_input.cpp create mode 100644 src_v2/lumenarium_memory.cpp create mode 100644 src_v2/lumenarium_string.cpp create mode 100644 src_v2/lumenarium_tests.cpp create mode 100644 src_v2/lumenarium_types.h create mode 100644 src_v2/platform/lumenarium_compiler_flags.h create mode 100644 src_v2/platform/lumenarium_platform.h create mode 100644 src_v2/platform/lumenarium_platform_common_includes.h create mode 100644 src_v2/platform/osx/lumenarium_first_osx.cpp create mode 100644 src_v2/platform/osx/lumenarium_osx_memory.cpp create mode 100644 src_v2/platform/webgl/lumenarium_first_webgl.cpp create mode 100644 src_v2/platform/win32/lumenarium_first_win32.cpp create mode 100644 src_v2/platform/win32/lumenarium_win32_file.cpp create mode 100644 src_v2/platform/win32/lumenarium_win32_memory.cpp create mode 100644 src_v2/platform/win32/lumenarium_win32_thread.cpp create mode 100644 src_v2/platform/win32/lumenarium_win32_time.cpp create mode 100644 src_v2/platform/win32/lumenarium_win32_window.cpp diff --git a/.gitignore b/.gitignore index 67d2a76..1335d3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ app_run_tree/ meta_run_tree/ +*.exe +*.pdb +*.o process/ reference/ working_data/ -nssm_log.log \ No newline at end of file +nssm_log.log +sysroot/ \ No newline at end of file diff --git a/build/build.sh b/build/build.sh new file mode 100644 index 0000000..6c61fa8 --- /dev/null +++ b/build/build.sh @@ -0,0 +1,294 @@ +#!/bin/bash + +# -------------------------------------------- +# Usage + +print_usage () { + echo + echo Build Command Syntax: + echo " $0 [mode] [platform] [arch]" + echo + echo "Release Mode Options:" + echo " debug" + echo " prod" + echo + echo "Platform Options:" + echo " win32" + echo " osx" + echo " webgl" + echo + echo "Arch Options: (architecture)" + echo " intel (valid with Platform Win32 and OSX) (default)" + echo " arm64 (only valid for Platform OSX)" +} + +# -------------------------------------------- +# Arguments +MODE=$1 +PLATFORM=$2 +ARCH=$3 +PACKAGE=$4 + +if [ "${MODE}" == "" ] | [ "${PLATFORM}" == "" ] +then + print_usage + exit 0 +fi + +# Default to Intel architecture if none provided +if [ "${ARCH}" == "" ] +then + ARCH="intel" +fi + +if [ "${ARCH}" != "intel" ] && [ "${ARCH}" != "arm64" ] +then + echo "Uknown target architecture: ${ARCH}" + print_usage + exit 0 + +fi + +# -------------------------------------------- +# Utilities + +pushdir () { + command pushd "$@" > /dev/null +} + +popdir () { + command popd "$@" > /dev/null +} + +# -------------------------------------------- +# Getting Project Path +# +# Project is stored in PROJECT_PATH + +SCRIPT_REL_DIR=$(dirname "${BASH_SOURCE[0]}") +pushdir $SCRIPT_REL_DIR +pushdir .. +PROJECT_PATH=$(pwd) +popdir +popdir + +# -------------------------------------------- +# Platform/Mode Specific Variables + +# Compiler Selection + +Compiler_win32="cl" +Compiler_osx="clang++" +WasiSdk="/c/drive/apps/wasi-sdk" +Compiler_webgl="$WasiSdk/bin/clang++" +Compiler_linux="clang++" + +# Platform Entry Points + +PlatformEntry_win32="src_v2/platform/win32/lumenarium_first_win32.cpp" +PlatformEntry_osx="src_v2/platform/osx/lumenarium_first_osx.cpp" +PlatformEntry_webgl="src_v2/platform/webgl/lumenarium_first_webgl.cpp" +PlatformEntry_linux="src_v2/platform/linux/lumenarium_first_linux.cpp" + +# Intermediate Outputs + +CompilerOutput_win32="lumenarium.o" +CompilerOutput_osx="lumenarium" +CompilerOutput_webgl="lumenarium.wasm" +CompilerOutput_linux="" + +# Executables + +LinkerOutput_win32="lumenarium.exe" +LinkerOutput_osx="lumenarium" +LinkerOutput_webgl="lumenarium.wasm" +LinkerOutput_linux="" + +# Wasm Sys Root +WasmSysRoot="${PROJECT_PATH}/src_v2/platform/webgl/sysroot/" + +# Compiler Flags + +CompilerFlags_win32="-nologo -FC -WX -W4 -Z7 -Oi -MTd -fp:fast" +CompilerFlags_win32="$CompilerFlags_win32 -wd4505 -wd4100 -wd4189" + +CompilerFlags_osx="" + +CompilerFlags_webgl="-target wasm32 --sysroot ${WasmSysRoot} -s -fvisibility=hidden -fno-builtin -fno-exceptions -fno-threadsafe-statics" + +# CompilerFlags_webgl="-nostartfiles -fno-exceptions -fno-entry -strip-all -s -import-memory -fvisibility=hidden --sysroot ${WasmSysRoot}" + +CompilerFlags_linux="" + +CompilerFlags_DEBUG_win32="-Od -Zi -DDEBUG" +CompilerFlags_DEBUG="-O0 -g -DDEBUG" +CompilerFlags_PROD="-O3" + +# Compiler flags that no matter what, we want to define +# for the most part these pass the build parameters into the executable +CompilerFlags_common="-DPLATFORM=${PLATFORM} -DMODE=${MODE} -DARCH=${ARCH}" + +# Linker Flags + +LinkerFlags_win32="-NOLOGO -incremental:no -subsystem:windows -entry:WinMain -opt:ref" +LinkerFlags_osx="" +# LinkerFlags_webgl="--no-entry --export-dynamic -allow-undefined -import-memory -export=__wasm_call_ctors -export=malloc -export=free -export=main" +LinkerFlags_webgl="--no-entry --export-dynamic --unresolved-symbols=import-functions" +LinkerFlags_linux="" + +LinkerFlags_DEBUG="-debug" +LinkerFlags_PROD="" + +# Linker Libs + +LinkerLibs_win32="user32.lib kernel32.lib gdi32.lib opengl32.lib" +# winmm.lib gdi32.lib dsound.lib Ws2_32.lib Comdlg32.lib Winspool.lib" +LinkerLibs_osx="-framework OpenGL -framework Cocoa" +LinkerLibs_webgl="" +LinkerLibs_linux="" + +# -------------------------------------------- +# Varible Selection + +# Select Platform Variables + +if [ "${PLATFORM}" == "win32" ] +then + Compiler=$Compiler_win32 + PlatformEntry=$PlatformEntry_win32 + CompilerFlags=$CompilerFlags_win32 + CompilerOutput=$CompilerOutput_win32 + LinkerOutput=$LinkerOutput_win32 + LinkerFlags=$LinkerFlags_win32 + LinkerLibs=$LinkerLibs_win32 + +elif [ "${PLATFORM}" == "osx" ] +then + Compiler=$Compiler_osx + PlatformEntry=$PlatformEntry_osx + CompilerFlags=$CompilerFlags_osx + CompilerOutput=$CompilerOutput_osx + LinkerOutput=$LinkerOutput_osx + LinkerFlags=$LinkerFlags_osx + LinkerLibs=$LinkerLibs_osx + +elif [ "${PLATFORM}" == "webgl" ] +then + Compiler=$Compiler_webgl + PlatformEntry=$PlatformEntry_webgl + CompilerFlags=$CompilerFlags_webgl + CompilerOutput=$CompilerOutput_webgl + LinkerOutput=$LinkerOutput_webgl + LinkerFlags=$LinkerFlags_webgl + LinkerLibs=$LinkerLibs_webgl + +elif [ "${PLATFORM}" == "linux" ] +then + Compiler=$Compiler_linux + PlatformEntry=$PlatformEntry_linux + CompilerFlags=$CompilerFlags_linux + CompilerOutput=$CompilerOutput_linux + LinkerOutput=$LinkerOutput_linux + LinkerFlags=$LinkerFlags_linux + LinkerLibs=$LinkerLibs_linux + +else + echo "Attempting to build for an unknown platform: ${PLATFORM}" + print_usage + exit 0 + +fi + +# Select Release Mode Variables + +if [ "${MODE}" == "debug" ] +then + if [ $PLATFORM == "win32" ] + then + CompilerFlags="${CompilerFlags} ${CompilerFlags_DEBUG_win32}" + else + CompilerFlags="${CompilerFlags} ${CompilerFlags_DEBUG}" + fi + + LinkerFlags="${LinkerFlags} ${LinkerFlags_DEBUG}" + +elif [ "${MODE}" == "prod" ] +then + CompilerFlags="${CompilerFlags} ${CompilerFlags_PROD}" + LinkerFlags="${LinkerFlags} ${LinkerFlags_PROD}" + +else + echo "Attempting to build for an unknown release mode: ${MODE}" + print_usage + exit 0 + +fi + +# Common Flags +CompilerFlags="${CompilerFlags} ${CompilerFlags_common}" + +# -------------------------------------------- +# Build Path Construction +# +# This determines where the generated executable will +# be located. In general, it can be found at +# project_path/run_tree/platform/arch/release_mode/lumenarium.exe +# +# This section also ensures that the path requested actually exists + +BuildDir="${PROJECT_PATH}/run_tree/${PLATFORM}/${ARCH}/${MODE}" +EntryPath="${PROJECT_PATH}/${PlatformEntry}" + +# Exception for webgl, which doesn't care about cpu architecture +if [ $PLATFORM == "webgl" ] +then + BuildDir="${PROJECT_PATH}/run_tree/${PLATFORM}/${MODE}" + +fi + +# Make the build directory, +# "-p" flag makes it make the entire tree, and not emit errors if it +# exists. +mkdir -p "${BuildDir}" + +# -------------------------------------------- +# Compilation + +echo "Building To: ${BuildDir}/${LinkerOutput}" +echo +pushdir $BuildDir + +rm ${CompilerOutput} 2> /dev/null +rm ${LinkerOutput} 2> /dev/null + +echo "COMPILING..." +if [ $PLATFORM == "win32" ] +then + $Compiler $CompilerFlags $EntryPath \ + -link $LinkerFlags $LinkerLibs -OUT:${LinkerOutput} + +elif [ $PLATFORM == "webgl" ] +then + + LD="$WasiSdk/bin/wasm-ld" + echo $Compiler + CFlags="-O3 -flto --target=wasm32-unknown-wasi" + LDFlags="--no-entry --export-dynamic --allow-undefined --lto-O3 --import-memory" + + $Compiler \ + -Wno-writable-strings \ + --target=wasm32 \ + -nostdlib \ + -Wl,--export-all \ + -Wl,--no-entry \ + -Wl,--allow-undefined \ + -o lumenarium.wasm \ + $EntryPath \ + +else + $Compiler -o $LinkerOutput $CompilerFlags $EntryPath $LinkerLibs + +fi + +echo "Finished..." +popdir \ No newline at end of file diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index 1997c89..9f523a3 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -20,7 +20,7 @@ del *.pdb > NUL 2> NUL echo WAITING FOR PDB TO WRITE > lock.tmp -cl %CommonCompilerFlags% %SourceCodePath%\foldhaus_app.cpp /Fefoldhaus.dll /LD /link %CommonLinkerFlags% %DLLExports% +cl %CommonCompilerFlags% %SourceCodePath%\foldhaus_app.cpp /Fefoldhaus.dll /LD /link %CommoLinkerFlags% %DLLExports% SET LastError=%ERRORLEVEL% del lock.tmp diff --git a/project.4coder b/project.4coder index 830f111..8596496 100644 --- a/project.4coder +++ b/project.4coder @@ -14,6 +14,7 @@ blacklist_patterns = { }; load_paths_base = { { "src", .relative = true, .recursive = true, }, + { "src_v2", .relative = true, .recursive = true, }, { "meta", .relative = true, .recursive = true, }, { "gs_libs", .relative = true, .recursive = true, }, }; @@ -22,6 +23,7 @@ load_paths = { { load_paths_base, .os = "linux", }, { load_paths_base, .os = "mac", }, }; +enable_virtual_whitespace=true; command_list = { { .name = "build_application", @@ -34,6 +36,11 @@ command_list = { .cmd = { { "build\build_meta_msvc_win32_debug.bat" , .os = "win" }, { "./build_meta.sh", .os = "linux" }, { "./build_meta.sh", .os = "mac" }, }, }, + { .name = "build_v2", + .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, + .cmd = { { "bash build\build.sh debug win32 intel" , .os = "win" }, + { "./build_meta.sh", .os = "linux" }, + { "./build_meta.sh", .os = "mac" }, }, }, }; -fkey_command[1] = "build_application"; +fkey_command[1] = "build_v2"; fkey_command[2] = "build_meta"; diff --git a/run_tree/text.txt b/run_tree/text.txt new file mode 100644 index 0000000000000000000000000000000000000000..ccfbc2907e133e5420b8c0917ace2b28be40e2bd GIT binary patch literal 37 UcmYexhl3 + + + + + + + + \ No newline at end of file diff --git a/run_tree/webgl/debug/loader.js b/run_tree/webgl/debug/loader.js new file mode 100644 index 0000000..7e8633a --- /dev/null +++ b/run_tree/webgl/debug/loader.js @@ -0,0 +1,130 @@ + +let memory = null; +let memory_u8 = null; + +// TODO(PS): you need to figure out how to get text to convert to a string +function print (text) +{ + console.log(i, memory_u8.length, text); +} + +async function load_webassembly_module () +{ + const path = "lumenarium.wasm"; + const promise = fetch(path); + const module = await WebAssembly.compileStreaming(promise); + memory = new WebAssembly.Memory({ initial: 2 }); + memory_u8 = new Uint8Array(memory.buffer); + const env = { + memory, + print, + }; + const result = await WebAssembly.instantiate(module, { env }); + console.log(result); + + result.exports.main(); + +} + +window.addEventListener("load", load_webassembly_module) + + +/* +function load_webassembly_module (wa) +{ + fetch(wa.module) + .then(res => res.arrayBuffer()) + .then((wasm_bytes) => { + "use strict"; + wasm_bytes = new Uint8Array(wasm_bytes); + + // find start of heap and calc initial mem requirements + var wasm_heap_base = 65536; + + // see https://webassembly.org/docs/binary-encoding/ + for (let i = 8, section_end, type, length; + i < wasm_bytes.length; + i = section_end + ){ + + function get_b8() + { + return wasm_bytes[i++]; + } + + function get_leb() + { + for (var s = i, r = 0, n = 128; n & 128; i++) + { + r |= ((n = wasm_bytes[i]) & 127) << ((i - s) * 7); + } + return r; // TODO(PS): this might belong in the for loop? + } + + type = get_leb(); + length = get_leb(); + section_end = i + length; + + if (type < 0 || type > 11 || length <= 0 || section_end > wasm_bytes.length) + { + break; + } + + if (type == 6) + { + //Section 6 'Globals', llvm places the heap base pointer into the first value here + let count = get_leb(); + let gtype = get_b8(); + let mutable = get_b8(); + let opcode = get_leb(); + let offset = get_leb(); + let endcode = get_leb(); + wasm_heap_base = offset; + break; + } + } + + // Set the wasm memory size to [DATA] + [STACK] + [256KB HEAP] + // (This loader does not support memory growing so it stays at this size) + var wasm_mem_init = ((wasm_heap_base + 65535) >> 16 << 16) + (256 * 1024); + var env = { + env: { + memory: new WebAssembly.Memory( + { + initial: wasm_mem_init >> 16 + } + ), + }, + }; + + // Instantiate the wasm module by passing the prepared environment + WebAssembly.instantiate(wasm_bytes, env) + .then(function (output) + { + // Store the list of the functions exported by the wasm module in WA.asm + wa.asm = output.instance.exports; + + // If function '__wasm_call_ctors' (global C++ constructors) exists, call it + if (wa.asm.__wasm_call_ctors) wa.asm.__wasm_call_ctors(); + + // If function 'main' exists, call it with dummy arguments + if (wa.asm.main) wa.asm.main(0, 0); + + // If the outer HTML file supplied a 'started' callback, call it + if (wa.started) wa.started(); + }) + .catch(function (err) + { + // On an exception, if the err is 'abort' the error was already processed in the abort function above + if (err !== 'abort') { + let stack = err.stack ? "\n" + err.stack : ""; + console.error('BOOT: WASM instiantate error: ' + err + stack); + return; + } + }); + }); +} + +load_webassembly_module(web_assembly) + +*/ \ No newline at end of file diff --git a/run_tree/webgl/debug/lumenarium.wasm b/run_tree/webgl/debug/lumenarium.wasm new file mode 100644 index 0000000000000000000000000000000000000000..85b7240938f3633b8d0825ba916b1a189b08a033 GIT binary patch literal 633 zcmZWm!H&}~5S_7;X1D38WpCUZ!2yJ}P1{wMa?6bmKuDZiCw3d}CQg*NMSDucX(cXv z4_}9ya01I3&zt9Yw%&kfO9TMC^>*074&26WN4gF>a)VTvlV3O5SODr#B-Hbx_PGl5r}e|InQOcVvrY}K@YQ_ibK zAG8q#FNHQhy#Ame?=e4BS^{~^xi(TaDzkilG?~?v;Z~%DLYQza+luGH$U;#ZJ1DH+ z%1BCXI6o9s>cmsgDtb}Pm8b{gId=HlfPJUbYYuQU)|5c_1+Tg{XSX1NWELRf|de&oB3k)ubCQNV(sgZa~NirCG5^rB8_8iu#nn;CxA z%;u_5(HP*HXGE!R<-srw{NF(GuYa_+FK}{>X|2efcwM0 je#}<;_+Co2TBl0I`*bZHWW3pmZMux_H&SdLw(tJ{iIS`- literal 0 HcmV?d00001 diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj new file mode 100644 index 0000000000000000000000000000000000000000..551d7f3ebd5810ccb57900a2b4a1312569983160 GIT binary patch literal 108086 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 = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh); - Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime); - Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime); - Info->IsDirectory = HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY); - - // NOTE(Peter): String Storage - // Storing the string in the final storage means we don't have to copy the string later, and all - // strings will be continguous in memory at the calling site, though they will be before the array - gs_string FileName = PushString(Storage, SearchPath.Length + FileNameLength + 2); - PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName); - if (Info->IsDirectory) - { - AppendPrintF(&FileName, "\\"); - } - NullTerminate(&FileName); - - Info->Path = FileName.ConstString; + u32 FileNameLength = CharArrayLength(FindFileData.cFileName); + + Info->FileSize = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh); + Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime); + Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime); + Info->IsDirectory = HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY); + + // NOTE(Peter): String Storage + // Storing the string in the final storage means we don't have to copy the string later, and all + // strings will be continguous in memory at the calling site, though they will be before the array + gs_string FileName = PushString(Storage, SearchPath.Length + FileNameLength + 2); + PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName); + if (Info->IsDirectory) + { + AppendPrintF(&FileName, "\\"); + } + NullTerminate(&FileName); + + Info->Path = FileName.ConstString; } internal u32 Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* TempList, gs_const_string Path, gs_memory_arena* Storage, b32 Flags) { - u32 FilesCount = 0; - Assert(Path.Str[Path.Length - 1] != '\\' && - Path.Str[Path.Length - 1] != '/'); - gs_const_string SearchPath = Path; - - gs_const_string SearchPathDir = SearchPath; - s64 LastSlash = FindLastFromSet(SearchPath, "\\/"); - if (LastSlash >= 0) + u32 FilesCount = 0; + Assert(Path.Str[Path.Length - 1] != '\\' && + Path.Str[Path.Length - 1] != '/'); + gs_const_string SearchPath = Path; + + gs_const_string SearchPathDir = SearchPath; + s64 LastSlash = FindLastFromSet(SearchPath, "\\/"); + if (LastSlash >= 0) + { + SearchPathDir = Substring(SearchPath, 0, LastSlash + 1); + } + + WIN32_FIND_DATA FindFileData; + HANDLE SearchHandle = FindFirstFile(Path.Str, &FindFileData); + if (SearchHandle != INVALID_HANDLE_VALUE) + { + do { - SearchPathDir = Substring(SearchPath, 0, LastSlash + 1); - } - - WIN32_FIND_DATA FindFileData; - HANDLE SearchHandle = FindFirstFile(Path.Str, &FindFileData); - if (SearchHandle != INVALID_HANDLE_VALUE) - { - do + b32 AddFile = true; + + if (HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY)) + { + gs_const_string SubDirName = ConstString(FindFileData.cFileName); + bool IsNav = (StringsEqual(SubDirName, ConstString(".")) || + StringsEqual(SubDirName, ConstString(".."))); + + if (HasFlag(Flags, EnumerateDirectory_Recurse)) { - b32 AddFile = true; - - if (HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY)) - { - gs_const_string SubDirName = ConstString(FindFileData.cFileName); - bool IsNav = (StringsEqual(SubDirName, ConstString(".")) || - StringsEqual(SubDirName, ConstString(".."))); - - if (HasFlag(Flags, EnumerateDirectory_Recurse)) - { - if (!IsNav) - { - gs_string SubDirectoryPath = PushString(FileHandler.Transient, SearchPath.Length + SubDirName.Length + 3); - PrintF(&SubDirectoryPath, "%S%S/*\0", SearchPath, SubDirName); - FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString, - Storage, Flags); - } - } - - AddFile = HasFlag(Flags, EnumerateDirectory_IncludeDirectories); - } - - if (AddFile) - { - temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry); - *File = {0}; - Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPathDir, Storage); - SLLPushOrInit(TempList->First, TempList->Last, File); - FilesCount += 1; - } - }while(FindNextFile(SearchHandle, &FindFileData)); - } - else - { - PrintLastError(); - } - - return FilesCount; + if (!IsNav) + { + gs_string SubDirectoryPath = PushString(FileHandler.Transient, SearchPath.Length + SubDirName.Length + 3); + PrintF(&SubDirectoryPath, "%S%S/*\0", SearchPath, SubDirName); + FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString, + Storage, Flags); + } + } + + AddFile = HasFlag(Flags, EnumerateDirectory_IncludeDirectories); + } + + if (AddFile) + { + temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry); + *File = {0}; + Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPathDir, Storage); + SLLPushOrInit(TempList->First, TempList->Last, File); + FilesCount += 1; + } + }while(FindNextFile(SearchHandle, &FindFileData)); + } + else + { + PrintLastError(); + } + + return FilesCount; } ENUMERATE_DIRECTORY(Win32EnumerateDirectory) { - Assert(IsNullTerminated(Path)); - gs_file_info_array Result = {}; - - temp_file_list TempList = {}; - Result.MaxCount = Win32EnumerateDirectoryIntoTempList(FileHandler, &TempList, Path, Storage, Flags); - - Result.Values = PushArray(Storage, gs_file_info, Result.MaxCount); - for (temp_file_list_entry* FileAt = TempList.First; - FileAt != 0; - FileAt = FileAt->Next) - { - // NOTE(Peter): We don't copy the file name here because its already in Storage. - // See String Storage note above ^^ - Result.Values[Result.Count++] = FileAt->Info; - } - - return Result; + Assert(IsNullTerminated(Path)); + gs_file_info_array Result = {}; + + temp_file_list TempList = {}; + Result.MaxCount = Win32EnumerateDirectoryIntoTempList(FileHandler, &TempList, Path, Storage, Flags); + + Result.Values = PushArray(Storage, gs_file_info, Result.MaxCount); + for (temp_file_list_entry* FileAt = TempList.First; + FileAt != 0; + FileAt = FileAt->Next) + { + // NOTE(Peter): We don't copy the file name here because its already in Storage. + // See String Storage note above ^^ + Result.Values[Result.Count++] = FileAt->Info; + } + + return Result; } #define WIN32_FOLDHAUS_FILEIO_H diff --git a/src/app/platform_win32/win32_foldhaus_timing.h b/src/app/platform_win32/win32_foldhaus_timing.h index 6d04a31..dee60bf 100644 --- a/src/app/platform_win32/win32_foldhaus_timing.h +++ b/src/app/platform_win32/win32_foldhaus_timing.h @@ -11,33 +11,33 @@ internal s64 GetPerformanceFrequency () { - LARGE_INTEGER Frequency; - if (!QueryPerformanceFrequency(&Frequency)) - { - s32 Error = GetLastError(); - // TODO(Peter): I'm waiting to see an error actually occur here - // to know what it could possibly be. - InvalidCodePath; - } - return (s64)Frequency.QuadPart; + LARGE_INTEGER Frequency; + if (!QueryPerformanceFrequency(&Frequency)) + { + s32 Error = GetLastError(); + // TODO(Peter): I'm waiting to see an error actually occur here + // to know what it could possibly be. + InvalidCodePath; + } + return (s64)Frequency.QuadPart; } internal s64 GetWallClock () { #if 0 - s64 Result = __rdtsc(); - return Result; + s64 Result = __rdtsc(); + return Result; #else - LARGE_INTEGER Time; - if (!QueryPerformanceCounter(&Time)) - { - s32 Error = GetLastError(); - // TODO(Peter): I'm waiting to see an error actually occur here - // to know what it could possibly be. - InvalidCodePath; - } - return (s64)Time.QuadPart; + LARGE_INTEGER Time; + if (!QueryPerformanceCounter(&Time)) + { + s32 Error = GetLastError(); + // TODO(Peter): I'm waiting to see an error actually occur here + // to know what it could possibly be. + InvalidCodePath; + } + return (s64)Time.QuadPart; #endif } diff --git a/src_v2/editor/lumenarium_editor.cpp b/src_v2/editor/lumenarium_editor.cpp new file mode 100644 index 0000000..8fc0d0e --- /dev/null +++ b/src_v2/editor/lumenarium_editor.cpp @@ -0,0 +1,25 @@ + +internal void +ed_init(App_State* state) +{ + +} + +internal void +ed_frame_prepare(App_State* state) +{ + +} + +internal void +ed_frame(App_State* state) +{ + edr_render(state); +} + +internal void +ed_cleanup(App_State* state) +{ + +} + diff --git a/src_v2/editor/lumenarium_editor_renderer.cpp b/src_v2/editor/lumenarium_editor_renderer.cpp new file mode 100644 index 0000000..452471c --- /dev/null +++ b/src_v2/editor/lumenarium_editor_renderer.cpp @@ -0,0 +1,20 @@ + +internal void +edr_render(App_State* state) +{ + glMatrixMode(GL_TEXTURE_2D); + glLoadIdentity(); + + glClearColor(0.1f, 0.1f, 0.1f, 1); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDisable(GL_TEXTURE_2D); + + glViewport(0, 0, 1600, 900); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} \ No newline at end of file diff --git a/src_v2/engine/lumenarium_engine.cpp b/src_v2/engine/lumenarium_engine.cpp new file mode 100644 index 0000000..718c899 --- /dev/null +++ b/src_v2/engine/lumenarium_engine.cpp @@ -0,0 +1,25 @@ + +internal void +en_init(App_State* state) +{ + +} + +internal void +en_frame_prepare(App_State* state) +{ + +} + +internal void +en_frame(App_State* state) +{ + +} + +internal void +en_cleanup(App_State* state) +{ + +} + diff --git a/src_v2/engine/lumenarium_engine_assembly.h b/src_v2/engine/lumenarium_engine_assembly.h new file mode 100644 index 0000000..72d0271 --- /dev/null +++ b/src_v2/engine/lumenarium_engine_assembly.h @@ -0,0 +1,58 @@ +/* date = March 22nd 2022 6:40 pm */ + +#ifndef LUMENARIUM_ENGINE_ASSEMBLY_H +#define LUMENARIUM_ENGINE_ASSEMBLY_H + +struct Assembly_Handle +{ + u32 value; +}; + +union Assembly_Pixel +{ + struct { + u8 r; + u8 g; + u8 b; + }; + u8 channels[3]; +}; + +struct Assembly_Pixel_Buffer +{ + u32 cap; + u32 len; + Assembly_Pixel* pixels; + v4* positions; +}; + +struct Assembly_Strip +{ + u32 pixels_cap; + u32* pixels; +}; + +struct Assembly_Strip_Array +{ + u32 cap; + Assembly_Strip* strips; +}; + +struct Assembly_Array +{ + u32 cap; + u32 len; + String* names; + Assembly_Pixel_Buffer* pixel_buffers; + Assembly_Strip_Array* strip_arrays; + + Allocator* allocator; +}; + +Assembly_Handle assembly_add(Assembly_Array* a, String name, u64 pixels_cap, u64 strips_cap); +void assembly_rem(Assembly_Array* a, Assembly_Handle h); +Assembly_Strip* assembly_add_strip(Assembly_Array* a, Assembly_Handle h); +void assembly_add_led(Assembly_Array* a, Assembly_Handle h, Assembly_Strip* strip, v4 position); + + +#endif //LUMENARIUM_ENGINE_ASSEMBLY_H diff --git a/src_v2/libs/HandmadeMath.h b/src_v2/libs/HandmadeMath.h new file mode 100644 index 0000000..ef54521 --- /dev/null +++ b/src_v2/libs/HandmadeMath.h @@ -0,0 +1,3089 @@ +/* + HandmadeMath.h v1.2.0 + + This is a single header file with a bunch of useful functions for + basic game math operations. + + ============================================================================= + + You MUST + + #define HANDMADE_MATH_IMPLEMENTATION + + in EXACTLY one C or C++ file that includes this header, BEFORE the + include, like this: + + #define HANDMADE_MATH_IMPLEMENTATION + #include "HandmadeMath.h" + + All other files should just #include "HandmadeMath.h" without the #define. + + ============================================================================= + + For overloaded and operator overloaded versions of the base C functions, + you MUST + + #define HANDMADE_MATH_CPP_MODE + + in EXACTLY one C or C++ file that includes this header, BEFORE the + include, like this: + + #define HANDMADE_MATH_IMPLEMENTATION + #define HANDMADE_MATH_CPP_MODE + #include "HandmadeMath.h" + + All other files should just #include "HandmadeMath.h" without the #define. + + ============================================================================= + + To disable SSE intrinsics, you MUST + + #define HANDMADE_MATH_NO_SSE + + in EXACTLY one C or C++ file that includes this header, BEFORE the + include, like this: + + #define HANDMADE_MATH_IMPLEMENTATION + #define HANDMADE_MATH_CPP_MODE + #define HANDMADE_MATH_NO_SSE + #include "HandmadeMath.h" + + or + + #define HANDMADE_MATH_IMPLEMENTATION + #define HANDMADE_MATH_NO_SSE + #include "HandmadeMath.h" + + ============================================================================= + + To disable inlining functions, you MUST + + #define HANDMADE_MATH_NO_INLINE + + in EXACTLY one C or C++ file that includes this header, BEFORE the + include, like this: + + #define HANDMADE_MATH_IMPLEMENTATION + #define HANDMADE_MATH_CPP_MODE + #define HANDMADE_MATH_NO_INLINE + #include "HandmadeMath.h" + + All other files should just #include "HandmadeMath.h" without the #define. + + ============================================================================= + + To use HandmadeMath without the CRT, you MUST + + #define HMM_SINF MySinF + #define HMM_COSF MyCosF + #define HMM_TANF MyTanF + #define HMM_SQRTF MySqrtF + #define HMM_EXPF MyExpF + #define HMM_LOGF MyLogF + #define HMM_ACOSF MyACosF + #define HMM_ATANF MyATanF + #define HMM_ATAN2F MYATan2F + + Provide your own implementations of SinF, CosF, TanF, ACosF, ATanF, ATan2F, + ExpF, and LogF in EXACTLY one C or C++ file that includes this header, + BEFORE the include, like this: + + #define HMM_SINF MySinF + #define HMM_COSF MyCosF + #define HMM_TANF MyTanF + #define HMM_SQRTF MySqrtF + #define HMM_EXPF MyExpF + #define HMM_LOGF MyLogF + #define HMM_ACOSF MyACosF + #define HMM_ATANF MyATanF + #define HMM_ATAN2F MyATan2F + #define HANDMADE_MATH_IMPLEMENTATION + #define HANDMADE_MATH_CPP_MODE + #include "HandmadeMath.h" + + If you do not define all five of these, HandmadeMath.h will use the + versions of these functions that are provided by the CRT. + + ============================================================================= + + Version History: + 0.2 (*) Updated documentation + (*) Better C compliance + (*) Prefix all handmade math functions + (*) Better operator overloading + 0.2a + (*) Prefixed Macros + 0.2b + (*) Disabled warning 4201 on MSVC as it is legal is C11 + (*) Removed the f at the end of HMM_PI to get 64bit precision + 0.3 + (*) Added +=, -=, *=, /= for hmm_vec2, hmm_vec3, hmm_vec4 + 0.4 + (*) SSE Optimized HMM_SqrtF + (*) SSE Optimized HMM_RSqrtF + (*) Removed CRT + 0.5 + (*) Added scalar multiplication and division for vectors + and matrices + (*) Added matrix subtraction and += for hmm_mat4 + (*) Reconciled all headers and implementations + (*) Tidied up, and filled in a few missing operators + 0.5.1 + (*) Ensured column-major order for matrices throughout + (*) Fixed HMM_Translate producing row-major matrices + 0.5.2 + (*) Fixed SSE code in HMM_SqrtF + (*) Fixed SSE code in HMM_RSqrtF + 0.6 + (*) Added Unit testing + (*) Made HMM_Power faster + (*) Fixed possible efficiency problem with HMM_Normalize + (*) RENAMED HMM_LengthSquareRoot to HMM_LengthSquared + (*) RENAMED HMM_RSqrtF to HMM_RSquareRootF + (*) RENAMED HMM_SqrtF to HMM_SquareRootF + (*) REMOVED Inner function (user should use Dot now) + (*) REMOVED HMM_FastInverseSquareRoot function declaration + 0.7 + (*) REMOVED HMM_LengthSquared in HANDMADE_MATH_IMPLEMENTATION (should + use HMM_LengthSquaredVec3, or HANDMADE_MATH_CPP_MODE for function + overloaded version) + (*) REMOVED HMM_Length in HANDMADE_MATH_IMPLEMENTATION (should use + HMM_LengthVec3, HANDMADE_MATH_CPP_MODE for function + overloaded version) + (*) REMOVED HMM_Normalize in HANDMADE_MATH_IMPLEMENTATION (should use + HMM_NormalizeVec3, or HANDMADE_MATH_CPP_MODE for function + overloaded version) + (*) Added HMM_LengthSquaredVec2 + (*) Added HMM_LengthSquaredVec4 + (*) Addd HMM_LengthVec2 + (*) Added HMM_LengthVec4 + (*) Added HMM_NormalizeVec2 + (*) Added HMM_NormalizeVec4 + 1.0 + (*) Lots of testing! + 1.1 + (*) Quaternion support + (*) Added type hmm_quaternion + (*) Added HMM_Quaternion + (*) Added HMM_QuaternionV4 + (*) Added HMM_AddQuaternion + (*) Added HMM_SubtractQuaternion + (*) Added HMM_MultiplyQuaternion + (*) Added HMM_MultiplyQuaternionF + (*) Added HMM_DivideQuaternionF + (*) Added HMM_InverseQuaternion + (*) Added HMM_DotQuaternion + (*) Added HMM_NormalizeQuaternion + (*) Added HMM_Slerp + (*) Added HMM_QuaternionToMat4 + (*) Added HMM_QuaternionFromAxisAngle + 1.1.1 + (*) Resolved compiler warnings on gcc and g++ + 1.1.2 + (*) Fixed invalid HMMDEF's in the function definitions + 1.1.3 + (*) Fixed compile error in C mode + 1.1.4 + (*) Fixed SSE being included on platforms that don't support it + (*) Fixed divide-by-zero errors when normalizing zero vectors. + 1.1.5 + (*) Add Width and Height to HMM_Vec2 + (*) Made it so you can supply your own SqrtF + 1.2.0 + (*) Added equality functions for HMM_Vec2, HMM_Vec3, and HMM_Vec4. + (*) Added HMM_EqualsVec2, HMM_EqualsVec3, and HMM_EqualsVec4 + (*) Added C++ overloaded HMM_Equals for all three + (*) Added C++ == and != operators for all three + (*) SSE'd HMM_MultiplyMat4 (this is _WAY_ faster) + (*) SSE'd HMM_Transpose + + LICENSE + + This software is in the public domain. Where that dedication is not + recognized, you are granted a perpetual, irrevocable license to copy, + distribute, and modify this file as you see fit. + + CREDITS + + Written by Zakary Strange (zak@handmade.network && @strangezak) + + Functionality: + Matt Mascarenhas (@miblo_) + Aleph + FieryDrake (@fierydrake) + Gingerbill (@TheGingerBill) + Ben Visness (@bvisness) + Trinton Bullard (@Peliex_Dev) + + Fixes: + Jeroen van Rijn (@J_vanRijn) + Kiljacken (@Kiljacken) + Insofaras (@insofaras) + Daniel Gibson (@DanielGibson) +*/ + + +/* let's figure out if SSE is really available (unless disabled anyway) + (it isn't on non-x86/x86_64 platforms or even x86 without explicit SSE support) + => only use "#ifdef HANDMADE_MATH__USE_SSE" to check for SSE support below this block! */ +#ifndef HANDMADE_MATH_NO_SSE + +# ifdef _MSC_VER +/* MSVC supports SSE in amd64 mode or _M_IX86_FP >= 1 (2 means SSE2) */ +# if defined(_M_AMD64) || ( defined(_M_IX86_FP) && _M_IX86_FP >= 1 ) +# define HANDMADE_MATH__USE_SSE 1 +# endif +# else /* not MSVC, probably GCC, clang, icc or something that doesn't support SSE anyway */ +# ifdef __SSE__ /* they #define __SSE__ if it's supported */ +# define HANDMADE_MATH__USE_SSE 1 +# endif /* __SSE__ */ +# endif /* not _MSC_VER */ + +#endif /* #ifndef HANDMADE_MATH_NO_SSE */ + +#include // This is for types + +#ifdef HANDMADE_MATH__USE_SSE +#include +#endif + +#ifndef HANDMADE_MATH_H +#define HANDMADE_MATH_H + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef HANDMADE_MATH_STATIC +#define HMMDEF static +#else +#define HMMDEF extern +#endif + +#ifdef HANDMADE_MATH_NO_INLINE +#define HINLINE +#elif _MSC_VER && !__INTEL_COMPILER +#define HINLINE __inline +#else +#define HINLINE inline +#endif + +#if !defined(HMM_SINF) || !defined(HMM_COSF) || !defined(HMM_TANF) || \ +!defined(HMM_SQRTF) || !defined(HMM_EXPF) || !defined(HMM_LOGF) || \ +!defined(HMM_ACOSF) || !defined(HMM_ATANF)|| !defined(HMM_ATAN2F) +#include +#endif + +#ifndef HMM_SINF +#define HMM_SINF sinf +#endif + +#ifndef HMM_COSF +#define HMM_COSF cosf +#endif + +#ifndef HMM_TANF +#define HMM_TANF tanf +#endif + +#ifndef HMM_SQRTF +#define HMM_SQRTF sqrtf +#endif + +#ifndef HMM_EXPF +#define HMM_EXPF expf +#endif + +#ifndef HMM_LOGF +#define HMM_LOGF logf +#endif + +#ifndef HMM_ACOSF +#define HMM_ACOSF acosf +#endif + +#ifndef HMM_ATANF +#define HMM_ATANF atanf +#endif + +#ifndef HMM_ATAN2F +#define HMM_ATAN2F atan2f +#endif + +#define HMM_PI32 3.14159265359f +#define HMM_PI 3.14159265358979323846 + +#define HMM_MIN(a, b) (a) > (b) ? (b) : (a) +#define HMM_MAX(a, b) (a) < (b) ? (b) : (a) +#define HMM_ABS(a) ((a) > 0 ? (a) : -(a)) +#define HMM_MOD(a, m) ((a) % (m)) >= 0 ? ((a) % (m)) : (((a) % (m)) + (m)) +#define HMM_SQUARE(x) ((x) * (x)) + + typedef union hmm_vec2 + { + struct + { + float X, Y; + }; + struct + { + float x, y; + }; + struct + { + float U, V; + }; + + struct + { + float Left, Right; + }; + + struct + { + float Width, Height; + }; + + float Elements[2]; + } hmm_vec2; + + typedef union hmm_vec3 + { + struct + { + float X, Y, Z; + }; + + struct + { + float x, y, z; + }; + + struct + { + float U, V, W; + }; + + struct + { + float R, G, B; + }; + + struct + { + hmm_vec2 XY; + float Ignored0_; + }; + + struct + { + float Ignored1_; + hmm_vec2 YZ; + }; + + struct + { + hmm_vec2 UV; + float Ignored2_; + }; + + struct + { + float Ignored3_; + hmm_vec2 VW; + }; + + float Elements[3]; + } hmm_vec3; + + typedef union hmm_vec4 + { + struct + { + union + { + hmm_vec3 XYZ; + struct + { + float X, Y, Z; + }; + }; + + float W; + }; + struct + { + union + { + hmm_vec3 xyz; + struct + { + float x, y, z; + }; + }; + + float w; + }; + struct + { + union + { + hmm_vec3 RGB; + struct + { + float R, G, B; + }; + }; + + float A; + }; + + struct + { + hmm_vec2 XY; + float Ignored0_; + float Ignored1_; + }; + + struct + { + float Ignored2_; + hmm_vec2 YZ; + float Ignored3_; + }; + + struct + { + float Ignored4_; + float Ignored5_; + hmm_vec2 ZW; + }; + + float Elements[4]; + } hmm_vec4; + + typedef union hmm_mat4 + { + float Elements[4][4]; + + +#ifdef HANDMADE_MATH__USE_SSE + __m128 Rows[4]; +#endif + } hmm_mat4; + + typedef union hmm_quaternion + { + struct + { + union + { + hmm_vec3 XYZ; + struct + { + float X, Y, Z; + }; + }; + + float W; + }; + + float Elements[4]; + } hmm_quaternion; + + typedef struct + { + hmm_vec2 ValueMin; + hmm_vec2 ValueMax; + } hmm_vec2r; + + typedef struct + { + hmm_vec3 ValueMin; + hmm_vec3 ValueMax; + } hmm_vec3r; + + typedef struct + { + hmm_vec4 ValueMin; + hmm_vec4 ValueMax; + } hmm_vec4r; + + typedef int32_t hmm_bool; + + typedef hmm_vec2 hmm_v2; + typedef hmm_vec3 hmm_v3; + typedef hmm_vec4 hmm_v4; + typedef hmm_mat4 hmm_m4; + typedef hmm_vec2r hmm_v2r; + typedef hmm_vec3r hmm_v3r; + typedef hmm_vec4r hmm_v4r; + + HMMDEF float HMM_SinF(float Angle); + HMMDEF float HMM_TanF(float Angle); + HMMDEF float HMM_ATanF(float Theta); + HMMDEF float HMM_ATan2F(float Theta, float Theta2); + HMMDEF float HMM_CosF(float Angle); + HMMDEF float HMM_ACosF(float Theta); + HMMDEF float HMM_ExpF(float Float); + HMMDEF float HMM_LogF(float Float); + + HMMDEF float HMM_ToRadians(float Degrees); + HMMDEF float HMM_SquareRootF(float Float); + HMMDEF float HMM_RSquareRootF(float Float); + + HMMDEF float HMM_LengthSquaredVec2(hmm_vec2 A); + HMMDEF float HMM_LengthSquaredVec3(hmm_vec3 A); + HMMDEF float HMM_LengthSquaredVec4(hmm_vec4 A); + + HMMDEF float HMM_LengthVec2(hmm_vec2 A); + HMMDEF float HMM_LengthVec3(hmm_vec3 A); + HMMDEF float HMM_LengthVec4(hmm_vec4 A); + + HMMDEF float HMM_Power(float Base, int Exponent); + HMMDEF float HMM_PowerF(float Base, float Exponent); + HMMDEF float HMM_Lerp(float A, float Time, float B); + HMMDEF float HMM_Clamp(float Min, float Value, float Max); + + HMMDEF hmm_vec2 HMM_NormalizeVec2(hmm_vec2 A); + HMMDEF hmm_vec3 HMM_NormalizeVec3(hmm_vec3 A); + HMMDEF hmm_vec4 HMM_NormalizeVec4(hmm_vec4 A); + + HMMDEF float HMM_DotVec2(hmm_vec2 VecOne, hmm_vec2 VecTwo); + HMMDEF float HMM_DotVec3(hmm_vec3 VecOne, hmm_vec3 VecTwo); + HMMDEF float HMM_DotVec4(hmm_vec4 VecOne, hmm_vec4 VecTwo); + + HMMDEF hmm_vec3 HMM_Cross(hmm_vec3 VecOne, hmm_vec3 VecTwo); + + HMMDEF hmm_vec2 HMM_Vec2(float X, float Y); + HMMDEF hmm_vec2 HMM_Vec2i(int X, int Y); + HMMDEF hmm_vec3 HMM_Vec3(float X, float Y, float Z); + HMMDEF hmm_vec3 HMM_Vec3i(int X, int Y, int Z); + HMMDEF hmm_vec4 HMM_Vec4(float X, float Y, float Z, float W); + HMMDEF hmm_vec4 HMM_Vec4i(int X, int Y, int Z, int W); + HMMDEF hmm_vec4 HMM_Vec4v(hmm_vec3 Vector, float W); + + HMMDEF hmm_vec2 HMM_AddVec2(hmm_vec2 Left, hmm_vec2 Right); + HMMDEF hmm_vec3 HMM_AddVec3(hmm_vec3 Left, hmm_vec3 Right); + HMMDEF hmm_vec4 HMM_AddVec4(hmm_vec4 Left, hmm_vec4 Right); + + HMMDEF hmm_vec2 HMM_SubtractVec2(hmm_vec2 Left, hmm_vec2 Right); + HMMDEF hmm_vec3 HMM_SubtractVec3(hmm_vec3 Left, hmm_vec3 Right); + HMMDEF hmm_vec4 HMM_SubtractVec4(hmm_vec4 Left, hmm_vec4 Right); + + HMMDEF hmm_vec2 HMM_MultiplyVec2(hmm_vec2 Left, hmm_vec2 Right); + HMMDEF hmm_vec2 HMM_MultiplyVec2f(hmm_vec2 Left, float Right); + HMMDEF hmm_vec3 HMM_MultiplyVec3(hmm_vec3 Left, hmm_vec3 Right); + HMMDEF hmm_vec3 HMM_MultiplyVec3f(hmm_vec3 Left, float Right); + HMMDEF hmm_vec4 HMM_MultiplyVec4(hmm_vec4 Left, hmm_vec4 Right); + HMMDEF hmm_vec4 HMM_MultiplyVec4f(hmm_vec4 Left, float Right); + + HMMDEF hmm_vec2 HMM_DivideVec2(hmm_vec2 Left, hmm_vec2 Right); + HMMDEF hmm_vec2 HMM_DivideVec2f(hmm_vec2 Left, float Right); + HMMDEF hmm_vec3 HMM_DivideVec3(hmm_vec3 Left, hmm_vec3 Right); + HMMDEF hmm_vec3 HMM_DivideVec3f(hmm_vec3 Left, float Right); + HMMDEF hmm_vec4 HMM_DivideVec4(hmm_vec4 Left, hmm_vec4 Right); + HMMDEF hmm_vec4 HMM_DivideVec4f(hmm_vec4 Left, float Right); + + HMMDEF hmm_bool HMM_EqualsVec2(hmm_vec2 Left, hmm_vec2 Right); + HMMDEF hmm_bool HMM_EqualsVec3(hmm_vec3 Left, hmm_vec3 Right); + HMMDEF hmm_bool HMM_EqualsVec4(hmm_vec4 Left, hmm_vec4 Right); + + HMMDEF hmm_mat4 HMM_Mat4(void); + HMMDEF hmm_mat4 HMM_Mat4d(float Diagonal); + HMMDEF hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right); + HMMDEF hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right); + +#ifdef HANDMADE_MATH__USE_SSE + HMMDEF __m128 HMM_LinearCombineSSE(__m128 Left, hmm_mat4 Right); +#endif + + HMMDEF hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right); + HMMDEF hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar); + HMMDEF hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector); + HMMDEF hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar); + + HMMDEF hmm_mat4 HMM_Transpose(hmm_mat4 Matrix); + + HMMDEF hmm_mat4 HMM_Orthographic(float Left, float Right, float Bottom, float Top, float Near, float Far); + HMMDEF hmm_mat4 HMM_Perspective(float FOV, float AspectRatio, float Near, float Far); + + HMMDEF hmm_mat4 HMM_Translate(hmm_vec3 Translation); + HMMDEF hmm_mat4 HMM_Rotate(float Angle, hmm_vec3 Axis); + HMMDEF hmm_mat4 HMM_Scale(hmm_vec3 Scale); + + HMMDEF hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up); + + HMMDEF hmm_quaternion HMM_Quaternion(float X, float Y, float Z, float W); + HMMDEF hmm_quaternion HMM_QuaternionV4(hmm_vec4 Vector); + HMMDEF hmm_quaternion HMM_AddQuaternion(hmm_quaternion Left, hmm_quaternion Right); + HMMDEF hmm_quaternion HMM_SubtractQuaternion(hmm_quaternion Left, hmm_quaternion Right); + HMMDEF hmm_quaternion HMM_MultiplyQuaternion(hmm_quaternion Left, hmm_quaternion Right); + HMMDEF hmm_quaternion HMM_MultiplyQuaternionF(hmm_quaternion Left, float Multiplicative); + HMMDEF hmm_quaternion HMM_DivideQuaternionF(hmm_quaternion Left, float Dividend); + HMMDEF hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left); + HMMDEF float HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right); + HMMDEF hmm_quaternion HMM_NormalizeQuaternion(hmm_quaternion Left); + HMMDEF hmm_quaternion HMM_NLerp(hmm_quaternion Left, float Time, hmm_quaternion Right); + HMMDEF hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right); + HMMDEF hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left); + HMMDEF hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotation); + +#ifdef __cplusplus +} +#endif + +#ifdef HANDMADE_MATH_CPP_MODE + +HMMDEF float HMM_Length(hmm_vec2 A); +HMMDEF float HMM_Length(hmm_vec3 A); +HMMDEF float HMM_Length(hmm_vec4 A); + +HMMDEF float HMM_LengthSquared(hmm_vec2 A); +HMMDEF float HMM_LengthSquared(hmm_vec3 A); +HMMDEF float HMM_LengthSquared(hmm_vec4 A); + +HMMDEF hmm_vec2 HMM_Normalize(hmm_vec2 A); +HMMDEF hmm_vec3 HMM_Normalize(hmm_vec3 A); +HMMDEF hmm_vec4 HMM_Normalize(hmm_vec4 A); +HMMDEF hmm_quaternion HMM_Normalize(hmm_quaternion A); + +HMMDEF float HMM_Dot(hmm_vec2 VecOne, hmm_vec2 VecTwo); +HMMDEF float HMM_Dot(hmm_vec3 VecOne, hmm_vec3 VecTwo); +HMMDEF float HMM_Dot(hmm_vec4 VecOne, hmm_vec4 VecTwo); +HMMDEF float HMM_Dot(hmm_quaternion QuatOne, hmm_quaternion QuatTwo); + +HMMDEF hmm_vec2 HMM_Add(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_vec3 HMM_Add(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_vec4 HMM_Add(hmm_vec4 Left, hmm_vec4 Right); +HMMDEF hmm_mat4 HMM_Add(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_quaternion HMM_Add(hmm_quaternion Left, hmm_quaternion Right); + +HMMDEF hmm_vec2 HMM_Subtract(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_vec3 HMM_Subtract(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_vec4 HMM_Subtract(hmm_vec4 Left, hmm_vec4 Right); +HMMDEF hmm_mat4 HMM_Subtract(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_quaternion HMM_Subtract(hmm_quaternion Left, hmm_quaternion Right); + +HMMDEF hmm_vec2 HMM_Multiply(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_vec2 HMM_Multiply(hmm_vec2 Left, float Right); +HMMDEF hmm_vec3 HMM_Multiply(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_vec3 HMM_Multiply(hmm_vec3 Left, float Right); +HMMDEF hmm_vec4 HMM_Multiply(hmm_vec4 Left, hmm_vec4 Right); +HMMDEF hmm_vec4 HMM_Multiply(hmm_vec4 Left, float Right); +HMMDEF hmm_mat4 HMM_Multiply(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_mat4 HMM_Multiply(hmm_mat4 Left, float Right); +HMMDEF hmm_vec4 HMM_Multiply(hmm_mat4 Matrix, hmm_vec4 Vector); +HMMDEF hmm_quaternion HMM_Multiply(hmm_quaternion Left, hmm_quaternion Right); +HMMDEF hmm_quaternion HMM_Multiply(hmm_quaternion Left, float Right); + +HMMDEF hmm_vec2 HMM_Divide(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_vec2 HMM_Divide(hmm_vec2 Left, float Right); +HMMDEF hmm_vec3 HMM_Divide(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_vec3 HMM_Divide(hmm_vec3 Left, float Right); +HMMDEF hmm_vec4 HMM_Divide(hmm_vec4 Left, hmm_vec4 Right); +HMMDEF hmm_vec4 HMM_Divide(hmm_vec4 Left, float Right); +HMMDEF hmm_mat4 HMM_Divide(hmm_mat4 Left, float Right); +HMMDEF hmm_quaternion HMM_Divide(hmm_quaternion Left, hmm_quaternion Right); +HMMDEF hmm_quaternion HMM_Divide(hmm_quaternion Left, float Right); + +HMMDEF hmm_bool HMM_Equals(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_bool HMM_Equals(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_bool HMM_Equals(hmm_vec4 Left, hmm_vec4 Right); + +HMMDEF hmm_vec2 operator+(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_vec3 operator+(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_vec4 operator+(hmm_vec4 Left, hmm_vec4 Right); +HMMDEF hmm_mat4 operator+(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_quaternion operator+(hmm_quaternion Left, hmm_quaternion Right); + +HMMDEF hmm_vec2 operator-(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_vec3 operator-(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_vec4 operator-(hmm_vec4 Left, hmm_vec4 Right); +HMMDEF hmm_mat4 operator-(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_quaternion operator-(hmm_quaternion Left, hmm_quaternion Right); + +HMMDEF hmm_vec2 operator*(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_vec3 operator*(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_vec4 operator*(hmm_vec4 Left, hmm_vec4 Right); +HMMDEF hmm_mat4 operator*(hmm_mat4 Left, hmm_mat4 Right); +HMMDEF hmm_quaternion operator*(hmm_quaternion Left, hmm_quaternion Right); + +HMMDEF hmm_vec2 operator*(hmm_vec2 Left, float Right); +HMMDEF hmm_vec3 operator*(hmm_vec3 Left, float Right); +HMMDEF hmm_vec4 operator*(hmm_vec4 Left, float Right); +HMMDEF hmm_mat4 operator*(hmm_mat4 Left, float Right); +HMMDEF hmm_quaternion operator*(hmm_quaternion Left, float Right); + +HMMDEF hmm_vec2 operator*(float Left, hmm_vec2 Right); +HMMDEF hmm_vec3 operator*(float Left, hmm_vec3 Right); +HMMDEF hmm_vec4 operator*(float Left, hmm_vec4 Right); +HMMDEF hmm_mat4 operator*(float Left, hmm_mat4 Right); +HMMDEF hmm_quaternion operator*(float Left, hmm_quaternion Right); + +HMMDEF hmm_vec4 operator*(hmm_mat4 Matrix, hmm_vec4 Vector); + +HMMDEF hmm_vec2 operator/(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_vec3 operator/(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_vec4 operator/(hmm_vec4 Left, hmm_vec4 Right); + +HMMDEF hmm_vec2 operator/(hmm_vec2 Left, float Right); +HMMDEF hmm_vec3 operator/(hmm_vec3 Left, float Right); +HMMDEF hmm_vec4 operator/(hmm_vec4 Left, float Right); +HMMDEF hmm_mat4 operator/(hmm_mat4 Left, float Right); +HMMDEF hmm_quaternion operator/(hmm_quaternion Left, float Right); + +HMMDEF hmm_vec2 &operator+=(hmm_vec2 &Left, hmm_vec2 Right); +HMMDEF hmm_vec3 &operator+=(hmm_vec3 &Left, hmm_vec3 Right); +HMMDEF hmm_vec4 &operator+=(hmm_vec4 &Left, hmm_vec4 Right); +HMMDEF hmm_mat4 &operator+=(hmm_mat4 &Left, hmm_mat4 Right); +HMMDEF hmm_quaternion &operator+=(hmm_quaternion &Left, hmm_quaternion Right); + +HMMDEF hmm_vec2 &operator-=(hmm_vec2 &Left, hmm_vec2 Right); +HMMDEF hmm_vec3 &operator-=(hmm_vec3 &Left, hmm_vec3 Right); +HMMDEF hmm_vec4 &operator-=(hmm_vec4 &Left, hmm_vec4 Right); +HMMDEF hmm_mat4 &operator-=(hmm_mat4 &Left, hmm_mat4 Right); +HMMDEF hmm_quaternion &operator-=(hmm_quaternion &Left, hmm_quaternion Right); + +HMMDEF hmm_vec2 &operator*=(hmm_vec2 &Left, hmm_vec2 Right); +HMMDEF hmm_vec3 &operator*=(hmm_vec3 &Left, hmm_vec3 Right); +HMMDEF hmm_vec4 &operator*=(hmm_vec4 &Left, hmm_vec4 Right); + +HMMDEF hmm_vec2 &operator*=(hmm_vec2 &Left, float Right); +HMMDEF hmm_vec3 &operator*=(hmm_vec3 &Left, float Right); +HMMDEF hmm_vec4 &operator*=(hmm_vec4 &Left, float Right); +HMMDEF hmm_mat4 &operator*=(hmm_mat4 &Left, float Right); +HMMDEF hmm_quaternion &operator*=(hmm_quaternion &Left, float Right); + +HMMDEF hmm_vec2 &operator/=(hmm_vec2 &Left, hmm_vec2 Right); +HMMDEF hmm_vec3 &operator/=(hmm_vec3 &Left, hmm_vec3 Right); +HMMDEF hmm_vec4 &operator/=(hmm_vec4 &Left, hmm_vec4 Right); + +HMMDEF hmm_vec2 &operator/=(hmm_vec2 &Left, float Right); +HMMDEF hmm_vec3 &operator/=(hmm_vec3 &Left, float Right); +HMMDEF hmm_vec4 &operator/=(hmm_vec4 &Left, float Right); +HMMDEF hmm_mat4 &operator/=(hmm_mat4 &Left, float Right); +HMMDEF hmm_quaternion &operator/=(hmm_quaternion &Left, float Right); + +HMMDEF hmm_bool operator==(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_bool operator==(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_bool operator==(hmm_vec4 Left, hmm_vec4 Right); + +HMMDEF hmm_bool operator!=(hmm_vec2 Left, hmm_vec2 Right); +HMMDEF hmm_bool operator!=(hmm_vec3 Left, hmm_vec3 Right); +HMMDEF hmm_bool operator!=(hmm_vec4 Left, hmm_vec4 Right); + +HMMDEF hmm_mat4 HMM_Mat4Identity(); +HMMDEF hmm_vec2 HMM_LerpV2(hmm_vec2 A, float T, hmm_vec2 B); +HMMDEF hmm_vec3 HMM_LerpV3(hmm_vec3 A, float T, hmm_vec3 B); +HMMDEF hmm_vec4 HMM_LerpV4(hmm_vec4 A, float T, hmm_vec4 B); +HMMDEF hmm_vec4 HMM_AverageVec3(hmm_vec4* Arr, unsigned int Len); +HMMDEF hmm_vec4 HMM_AverageVec4(hmm_vec4* Arr, unsigned int Len); +HMMDEF bool HMM_SameSide(hmm_vec3 P1, hmm_vec3 P2, hmm_vec3 A, hmm_vec3 B); +HMMDEF bool HMM_PointInTriangle(hmm_vec3 P, hmm_vec3 T0, hmm_vec3 T1, hmm_vec3 T2); + +#endif /* HANDMADE_MATH_CPP */ + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif /* HANDMADE_MATH_H */ + +#ifdef HANDMADE_MATH_IMPLEMENTATION + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#pragma clang diagnostic ignored "-Wmissing-braces" +#endif + +HINLINE float +HMM_SinF(float Angle) +{ + float Result = 0.0f; + + Result = HMM_SINF(Angle); + return (Result); +} + +HINLINE float +HMM_CosF(float Angle) +{ + float Result = 0.0f; + + Result = HMM_COSF(Angle); + return (Result); +} + +HINLINE float +HMM_TanF(float Radians) +{ + float Result = 0.0f; + + Result = HMM_TANF(Radians); + return (Result); +} + +HINLINE float +HMM_ACosF(float Radians) +{ + float Result = 0.0f; + + Result = HMM_ACOSF(Radians); + return (Result); +} + +HINLINE float +HMM_ATanF(float Radians) +{ + float Result = 0.0f; + + Result = HMM_ATANF(Radians); + return (Result); +} + +HINLINE float +HMM_Atan2F(float Left, float Right) +{ + float Result = 0.0f; + + Result = HMM_ATAN2F(Left, Right); + return (Result); +} + +HINLINE float +HMM_ExpF(float Float) +{ + float Result = 0.0f; + + Result = HMM_EXPF(Float); + return (Result); +} + +HINLINE float +HMM_LogF(float Float) +{ + float Result = 0.0f; + + Result = HMM_LOGF(Float); + return (Result); +} + +HINLINE float +HMM_ToRadians(float Degrees) +{ + float Result = 0.0f; + + Result = Degrees * (HMM_PI32 / 180.0f); + return (Result); +} + +HINLINE float +HMM_SquareRootF(float Value) +{ + float Result = 0.0f; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 In = _mm_set_ss(Value); + __m128 Out = _mm_sqrt_ss(In); + Result = _mm_cvtss_f32(Out); +#else + Result = HMM_SQRTF(Value); +#endif + + return(Result); +} + +HINLINE float +HMM_RSquareRootF(float Value) +{ + float Result = 0.0f; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 In = _mm_set_ss(Value); + __m128 Out = _mm_rsqrt_ss(In); + Result = _mm_cvtss_f32(Out); +#else + Result = 1.0f/HMM_SquareRootF(Value); +#endif + + return(Result); +} + +HINLINE float +HMM_LengthSquaredVec2(hmm_vec2 A) +{ + float Result = 0.0f; + + Result = HMM_DotVec2(A, A); + + return(Result); +} + +HINLINE float +HMM_LengthSquaredVec3(hmm_vec3 A) +{ + float Result = 0.0f; + + Result = HMM_DotVec3(A, A); + + return (Result); +} + +HINLINE float +HMM_LengthSquaredVec4(hmm_vec4 A) +{ + float Result = 0.0f; + + Result = HMM_DotVec4(A, A); + + return(Result); +} + +HINLINE float +HMM_LengthVec2(hmm_vec2 A) +{ + float Result = 0.0f; + + Result = HMM_SquareRootF(HMM_LengthSquaredVec2(A)); + + return(Result); +} + +HINLINE float +HMM_LengthVec3(hmm_vec3 A) +{ + float Result = 0.0f; + + Result = HMM_SquareRootF(HMM_LengthSquaredVec3(A)); + + return (Result); +} + +HINLINE float +HMM_LengthVec4(hmm_vec4 A) +{ + float Result = 0.0f; + + Result = HMM_SquareRootF(HMM_LengthSquaredVec4(A)); + + return(Result); +} + +HINLINE float +HMM_Power(float Base, int Exponent) +{ + float Result = 1.0f; + float Mul = Exponent < 0 ? 1.f / Base : Base; + int X = Exponent < 0 ? -Exponent : Exponent; + while (X) + { + if (X & 1) + { + Result *= Mul; + } + + Mul *= Mul; + X >>= 1; + } + + return (Result); +} + +HINLINE float +HMM_PowerF(float Base, float Exponent) +{ + return HMM_EXPF(Exponent * HMM_LOGF(Base)); +} + +HINLINE float +HMM_Lerp(float A, float Time, float B) +{ + float Result = 0; + + Result = (1.0f - Time) * A + Time * B; + return (Result); +} + +HINLINE float +HMM_Clamp(float Min, float Value, float Max) +{ + float Result = Value; + + if(Result < Min) + { + Result = Min; + } + else if(Result > Max) + { + Result = Max; + } + + return (Result); +} + +HINLINE hmm_vec2 +HMM_NormalizeVec2(hmm_vec2 A) +{ + hmm_vec2 Result = {0}; + + float VectorLength = HMM_LengthVec2(A); + + /* NOTE(kiljacken): We need a zero check to not divide-by-zero */ + if (VectorLength != 0.0f) + { + Result.X = A.X * (1.0f / VectorLength); + Result.Y = A.Y * (1.0f / VectorLength); + } + + return (Result); +} + +HINLINE hmm_vec3 +HMM_NormalizeVec3(hmm_vec3 A) +{ + hmm_vec3 Result = {0}; + + float VectorLength = HMM_LengthVec3(A); + + /* NOTE(kiljacken): We need a zero check to not divide-by-zero */ + if (VectorLength != 0.0f) + { + Result.X = A.X * (1.0f / VectorLength); + Result.Y = A.Y * (1.0f / VectorLength); + Result.Z = A.Z * (1.0f / VectorLength); + } + + return (Result); +} + +HINLINE hmm_vec4 +HMM_NormalizeVec4(hmm_vec4 A) +{ + hmm_vec4 Result = {0}; + + float VectorLength = HMM_LengthVec4(A); + + /* NOTE(kiljacken): We need a zero check to not divide-by-zero */ + if (VectorLength != 0.0f) + { + Result.X = A.X * (1.0f / VectorLength); + Result.Y = A.Y * (1.0f / VectorLength); + Result.Z = A.Z * (1.0f / VectorLength); + Result.W = A.W * (1.0f / VectorLength); + } + + return (Result); +} + +HINLINE float +HMM_DotVec2(hmm_vec2 VecOne, hmm_vec2 VecTwo) +{ + float Result = 0.0f; + + Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y); + + return (Result); +} + +HINLINE float +HMM_DotVec3(hmm_vec3 VecOne, hmm_vec3 VecTwo) +{ + float Result = 0.0f; + + Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z); + + return (Result); +} + +HINLINE float +HMM_DotVec4(hmm_vec4 VecOne, hmm_vec4 VecTwo) +{ + float Result = 0.0f; + + Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z) + (VecOne.W * VecTwo.W); + + return (Result); +} + +HINLINE hmm_vec3 +HMM_Cross(hmm_vec3 VecOne, hmm_vec3 VecTwo) +{ + hmm_vec3 Result = {0}; + + Result.X = (VecOne.Y * VecTwo.Z) - (VecOne.Z * VecTwo.Y); + Result.Y = (VecOne.Z * VecTwo.X) - (VecOne.X * VecTwo.Z); + Result.Z = (VecOne.X * VecTwo.Y) - (VecOne.Y * VecTwo.X); + + return (Result); +} + +HINLINE hmm_vec2 +HMM_Vec2(float X, float Y) +{ + hmm_vec2 Result = {0}; + + Result.X = X; + Result.Y = Y; + + return (Result); +} + +HINLINE hmm_vec2 +HMM_Vec2i(int X, int Y) +{ + hmm_vec2 Result = {0}; + + Result.X = (float)X; + Result.Y = (float)Y; + + return (Result); +} + +HINLINE hmm_vec3 +HMM_Vec3(float X, float Y, float Z) +{ + hmm_vec3 Result = {0}; + + Result.X = X; + Result.Y = Y; + Result.Z = Z; + + return (Result); +} + +HINLINE hmm_vec3 +HMM_Vec3i(int X, int Y, int Z) +{ + hmm_vec3 Result = {0}; + + Result.X = (float)X; + Result.Y = (float)Y; + Result.Z = (float)Z; + + return (Result); +} + +HINLINE hmm_vec4 +HMM_Vec4(float X, float Y, float Z, float W) +{ + hmm_vec4 Result = {0}; + + Result.X = X; + Result.Y = Y; + Result.Z = Z; + Result.W = W; + + return (Result); +} + +HINLINE hmm_vec4 +HMM_Vec4i(int X, int Y, int Z, int W) +{ + hmm_vec4 Result = {0}; + + Result.X = (float)X; + Result.Y = (float)Y; + Result.Z = (float)Z; + Result.W = (float)W; + + return (Result); +} + +HINLINE hmm_vec4 +HMM_Vec4v(hmm_vec3 Vector, float W) +{ + hmm_vec4 Result = {0}; + + Result.XYZ = Vector; + Result.W = W; + + return (Result); +} + +HINLINE hmm_vec2 +HMM_AddVec2(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + + return (Result); +} + +HINLINE hmm_vec3 +HMM_AddVec3(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + Result.Z = Left.Z + Right.Z; + + return (Result); +} + +HINLINE hmm_vec4 +HMM_AddVec4(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + Result.Z = Left.Z + Right.Z; + Result.W = Left.W + Right.W; + + return (Result); +} + +HINLINE hmm_vec2 +HMM_SubtractVec2(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + + return (Result); +} + +HINLINE hmm_vec3 +HMM_SubtractVec3(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + Result.Z = Left.Z - Right.Z; + + return (Result); +} + +HINLINE hmm_vec4 +HMM_SubtractVec4(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + Result.Z = Left.Z - Right.Z; + Result.W = Left.W - Right.W; + + return (Result); +} + +HINLINE hmm_vec2 +HMM_MultiplyVec2(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result.X = Left.X * Right.X; + Result.Y = Left.Y * Right.Y; + + return (Result); +} + +HINLINE hmm_vec2 +HMM_MultiplyVec2f(hmm_vec2 Left, float Right) +{ + hmm_vec2 Result = {0}; + + Result.X = Left.X * Right; + Result.Y = Left.Y * Right; + + return (Result); +} + +HINLINE hmm_vec3 +HMM_MultiplyVec3(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result.X = Left.X * Right.X; + Result.Y = Left.Y * Right.Y; + Result.Z = Left.Z * Right.Z; + + return (Result); +} + +HINLINE hmm_vec3 +HMM_MultiplyVec3f(hmm_vec3 Left, float Right) +{ + hmm_vec3 Result = {0}; + + Result.X = Left.X * Right; + Result.Y = Left.Y * Right; + Result.Z = Left.Z * Right; + + return (Result); +} + +HINLINE hmm_vec4 +HMM_MultiplyVec4(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result.X = Left.X * Right.X; + Result.Y = Left.Y * Right.Y; + Result.Z = Left.Z * Right.Z; + Result.W = Left.W * Right.W; + + return (Result); +} + +HINLINE hmm_vec4 +HMM_MultiplyVec4f(hmm_vec4 Left, float Right) +{ + hmm_vec4 Result = {0}; + + Result.X = Left.X * Right; + Result.Y = Left.Y * Right; + Result.Z = Left.Z * Right; + Result.W = Left.W * Right; + + return (Result); +} + +HINLINE hmm_vec2 +HMM_DivideVec2(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result.X = Left.X / Right.X; + Result.Y = Left.Y / Right.Y; + + return (Result); +} + +HINLINE hmm_vec2 +HMM_DivideVec2f(hmm_vec2 Left, float Right) +{ + hmm_vec2 Result = {0}; + + Result.X = Left.X / Right; + Result.Y = Left.Y / Right; + + return (Result); +} + +HINLINE hmm_vec3 +HMM_DivideVec3(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result.X = Left.X / Right.X; + Result.Y = Left.Y / Right.Y; + Result.Z = Left.Z / Right.Z; + + return (Result); +} + +HINLINE hmm_vec3 +HMM_DivideVec3f(hmm_vec3 Left, float Right) +{ + hmm_vec3 Result = {0}; + + Result.X = Left.X / Right; + Result.Y = Left.Y / Right; + Result.Z = Left.Z / Right; + + return (Result); +} + +HINLINE hmm_vec4 +HMM_DivideVec4(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result.X = Left.X / Right.X; + Result.Y = Left.Y / Right.Y; + Result.Z = Left.Z / Right.Z; + Result.W = Left.W / Right.W; + + return (Result); +} + +HINLINE hmm_vec4 +HMM_DivideVec4f(hmm_vec4 Left, float Right) +{ + hmm_vec4 Result = {0}; + + Result.X = Left.X / Right; + Result.Y = Left.Y / Right; + Result.Z = Left.Z / Right; + Result.W = Left.W / Right; + + return (Result); +} + +HINLINE hmm_bool +HMM_EqualsVec2(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_bool Result = 0; + + Result = (Left.X == Right.X && Left.Y == Right.Y); + + return (Result); +} + +HINLINE hmm_bool +HMM_EqualsVec3(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_bool Result = 0; + + Result = (Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z); + + return (Result); +} + +HINLINE hmm_bool +HMM_EqualsVec4(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_bool Result = 0; + + Result = (Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z && Left.W == Right.W); + + return (Result); +} + +HINLINE hmm_mat4 +HMM_Mat4(void) +{ + hmm_mat4 Result = {0}; + + return (Result); +} + +HINLINE hmm_mat4 +HMM_Mat4d(float Diagonal) +{ + hmm_mat4 Result = HMM_Mat4(); + + Result.Elements[0][0] = Diagonal; + Result.Elements[1][1] = Diagonal; + Result.Elements[2][2] = Diagonal; + Result.Elements[3][3] = Diagonal; + + return (Result); +} + +HINLINE hmm_mat4 +HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right) +{ + hmm_mat4 Result = HMM_Mat4(); + + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] + Right.Elements[Columns][Rows]; + } + } + + return (Result); +} + +HINLINE hmm_mat4 +HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right) +{ + hmm_mat4 Result = HMM_Mat4(); + + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] - Right.Elements[Columns][Rows]; + } + } + + return (Result); +} + +#ifdef HANDMADE_MATH__USE_SSE +HINLINE __m128 +HMM_LinearCombineSSE(__m128 Left, hmm_mat4 Right) +{ + __m128 Result = {}; + Result = _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0x00), Right.Rows[0]); + Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0x55), Right.Rows[1])); + Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0xaa), Right.Rows[2])); + Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0xff), Right.Rows[3])); + + return(Result); +} +#endif + +HINLINE hmm_mat4 +HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right) +{ + hmm_mat4 Result = HMM_Mat4(); + +#ifdef HANDMADE_MATH__USE_SSE + + hmm_mat4 TransposedLeft = HMM_Transpose(Left); + hmm_mat4 TransposedRight = HMM_Transpose(Right); + + Result.Rows[0] = HMM_LinearCombineSSE(TransposedLeft.Rows[0], TransposedRight); + Result.Rows[1] = HMM_LinearCombineSSE(TransposedLeft.Rows[1], TransposedRight); + Result.Rows[2] = HMM_LinearCombineSSE(TransposedLeft.Rows[2], TransposedRight); + Result.Rows[3] = HMM_LinearCombineSSE(TransposedLeft.Rows[3], TransposedRight); + + Result = HMM_Transpose(Result); + +#else + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + float Sum = 0; + int CurrentMatrice; + for(CurrentMatrice = 0; CurrentMatrice < 4; ++CurrentMatrice) + { + Sum += Left.Elements[CurrentMatrice][Rows] * Right.Elements[Columns][CurrentMatrice]; + } + + Result.Elements[Columns][Rows] = Sum; + } + } +#endif + return (Result); +} + +HINLINE hmm_mat4 +HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar) +{ + hmm_mat4 Result = HMM_Mat4(); + + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] * Scalar; + } + } + + return (Result); +} + +HINLINE hmm_vec4 +HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector) +{ + hmm_vec4 Result = {0}; + + int Columns, Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + float Sum = 0; + for(Columns = 0; Columns < 4; ++Columns) + { + Sum += Matrix.Elements[Columns][Rows] * Vector.Elements[Columns]; + } + + Result.Elements[Rows] = Sum; + } + + return (Result); +} + +HINLINE hmm_mat4 +HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar) +{ + hmm_mat4 Result = HMM_Mat4(); + + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] / Scalar; + } + } + + return (Result); +} + +HINLINE hmm_mat4 +HMM_Transpose(hmm_mat4 Matrix) +{ + hmm_mat4 Result = HMM_Mat4(); + +#ifdef HANDMADE_MATH__USE_SSE + Result = Matrix; + + _MM_TRANSPOSE4_PS(Result.Rows[0], Result.Rows[1], Result.Rows[2], Result.Rows[3]); +#else + int Columns; + for(Columns = 0; Columns < 4; ++Columns) + { + int Rows; + for(Rows = 0; Rows < 4; ++Rows) + { + Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows]; + } + } +#endif + + return (Result); +} + +HINLINE hmm_mat4 +HMM_Orthographic(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + hmm_mat4 Result = HMM_Mat4d(1.0f); + + Result.Elements[0][0] = 2.0f / (Right - Left); + Result.Elements[1][1] = 2.0f / (Top - Bottom); + Result.Elements[2][2] = 2.0f / (Near - Far); + + Result.Elements[3][0] = (Left + Right) / (Left - Right); + Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top); + Result.Elements[3][2] = (Far + Near) / (Near - Far); + + return (Result); +} + +HINLINE hmm_mat4 +HMM_Perspective(float FOV, float AspectRatio, float Near, float Far) +{ + hmm_mat4 Result = HMM_Mat4d(1.0f); + + float TanThetaOver2 = HMM_TanF(FOV * (HMM_PI32 / 360.0f)); + + Result.Elements[0][0] = 1.0f / TanThetaOver2; + Result.Elements[1][1] = AspectRatio / TanThetaOver2; + Result.Elements[2][3] = -1.0f; + Result.Elements[2][2] = (Near + Far) / (Near - Far); + Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far); + Result.Elements[3][3] = 0.0f; + + return (Result); +} + +HINLINE hmm_mat4 +HMM_Translate(hmm_vec3 Translation) +{ + hmm_mat4 Result = HMM_Mat4d(1.0f); + + Result.Elements[3][0] = Translation.X; + Result.Elements[3][1] = Translation.Y; + Result.Elements[3][2] = Translation.Z; + + return (Result); +} + +HINLINE hmm_mat4 +HMM_Rotate(float Angle, hmm_vec3 Axis) +{ + hmm_mat4 Result = HMM_Mat4d(1.0f); + + Axis = HMM_NormalizeVec3(Axis); + + float SinTheta = HMM_SinF(HMM_ToRadians(Angle)); + float CosTheta = HMM_CosF(HMM_ToRadians(Angle)); + float CosValue = 1.0f - CosTheta; + + Result.Elements[0][0] = (Axis.X * Axis.X * CosValue) + CosTheta; + Result.Elements[0][1] = (Axis.X * Axis.Y * CosValue) + (Axis.Z * SinTheta); + Result.Elements[0][2] = (Axis.X * Axis.Z * CosValue) - (Axis.Y * SinTheta); + + Result.Elements[1][0] = (Axis.Y * Axis.X * CosValue) - (Axis.Z * SinTheta); + Result.Elements[1][1] = (Axis.Y * Axis.Y * CosValue) + CosTheta; + Result.Elements[1][2] = (Axis.Y * Axis.Z * CosValue) + (Axis.X * SinTheta); + + Result.Elements[2][0] = (Axis.Z * Axis.X * CosValue) + (Axis.Y * SinTheta); + Result.Elements[2][1] = (Axis.Z * Axis.Y * CosValue) - (Axis.X * SinTheta); + Result.Elements[2][2] = (Axis.Z * Axis.Z * CosValue) + CosTheta; + + return (Result); +} + +HINLINE hmm_mat4 +HMM_Scale(hmm_vec3 Scale) +{ + hmm_mat4 Result = HMM_Mat4d(1.0f); + + Result.Elements[0][0] = Scale.X; + Result.Elements[1][1] = Scale.Y; + Result.Elements[2][2] = Scale.Z; + + return (Result); +} + +HINLINE hmm_mat4 +HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up) +{ + hmm_mat4 Result = {0}; + + hmm_vec3 F = HMM_NormalizeVec3(HMM_SubtractVec3(Center, Eye)); + hmm_vec3 S = HMM_NormalizeVec3(HMM_Cross(F, Up)); + hmm_vec3 U = HMM_Cross(S, F); + + Result.Elements[0][0] = S.X; + Result.Elements[0][1] = U.X; + Result.Elements[0][2] = -F.X; + + Result.Elements[1][0] = S.Y; + Result.Elements[1][1] = U.Y; + Result.Elements[1][2] = -F.Y; + + Result.Elements[2][0] = S.Z; + Result.Elements[2][1] = U.Z; + Result.Elements[2][2] = -F.Z; + + Result.Elements[3][0] = -HMM_DotVec3(S, Eye); + Result.Elements[3][1] = -HMM_DotVec3(U, Eye); + Result.Elements[3][2] = HMM_DotVec3(F, Eye); + Result.Elements[3][3] = 1.0f; + + return (Result); +} + + +HINLINE hmm_quaternion +HMM_Quaternion(float X, float Y, float Z, float W) +{ + hmm_quaternion Result = {0}; + + Result.X = X; + Result.Y = Y; + Result.Z = Z; + Result.W = W; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_QuaternionV4(hmm_vec4 Vector) +{ + hmm_quaternion Result = {0}; + + Result.X = Vector.X; + Result.Y = Vector.Y; + Result.Z = Vector.Z; + Result.W = Vector.W; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_AddQuaternion(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + Result.Z = Left.Z + Right.Z; + Result.W = Left.W + Right.W; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_SubtractQuaternion(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + Result.Z = Left.Z - Right.Z; + Result.W = Left.W - Right.W; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_MultiplyQuaternion(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result.X = (Left.X * Right.W) + (Left.Y * Right.Z) - (Left.Z * Right.Y) + (Left.W * Right.X); + Result.Y = (-Left.X * Right.Z) + (Left.Y * Right.W) + (Left.Z * Right.X) + (Left.W * Right.Y); + Result.Z = (Left.X * Right.Y) - (Left.Y * Right.X) + (Left.Z * Right.W) + (Left.W * Right.Z); + Result.W = (-Left.X * Right.X) - (Left.Y * Right.Y) - (Left.Z * Right.Z) + (Left.W * Right.W); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_MultiplyQuaternionF(hmm_quaternion Left, float Multiplicative) +{ + hmm_quaternion Result = {0}; + + Result.X = Left.X * Multiplicative; + Result.Y = Left.Y * Multiplicative; + Result.Z = Left.Z * Multiplicative; + Result.W = Left.W * Multiplicative; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_DivideQuaternionF(hmm_quaternion Left, float Dividend) +{ + hmm_quaternion Result = {0}; + + Result.X = Left.X / Dividend; + Result.Y = Left.Y / Dividend; + Result.Z = Left.Z / Dividend; + Result.W = Left.W / Dividend; + + return(Result); +} + +HINLINE hmm_quaternion +HMM_InverseQuaternion(hmm_quaternion Left) +{ + hmm_quaternion Conjugate = {0}; + hmm_quaternion Result = {0}; + float Norm = 0; + float NormSquared = 0; + + Conjugate.X = -Left.X; + Conjugate.Y = -Left.Y; + Conjugate.Z = -Left.Z; + Conjugate.W = Left.W; + + Norm = HMM_SquareRootF(HMM_DotQuaternion(Left, Left)); + NormSquared = Norm * Norm; + + Result.X = Conjugate.X / NormSquared; + Result.Y = Conjugate.Y / NormSquared; + Result.Z = Conjugate.Z / NormSquared; + Result.W = Conjugate.W / NormSquared; + + return(Result); +} + +HINLINE float +HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right) +{ + float Result = 0.0f; + + Result = (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z) + (Left.W * Right.W); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_NormalizeQuaternion(hmm_quaternion Left) +{ + hmm_quaternion Result; + + float Length = HMM_SquareRootF(HMM_DotQuaternion(Left, Left)); + Result = HMM_DivideQuaternionF(Left, Length); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_NLerp(hmm_quaternion Left, float Time, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result.X = HMM_Lerp(Left.X, Time, Right.X); + Result.Y = HMM_Lerp(Left.Y, Time, Right.Y); + Result.Z = HMM_Lerp(Left.Z, Time, Right.Z); + Result.W = HMM_Lerp(Left.W, Time, Right.W); + + Result = HMM_NormalizeQuaternion(Result); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right) +{ + hmm_quaternion Result; + hmm_quaternion QuaternionLeft; + hmm_quaternion QuaternionRight; + + float Cos_Theta = HMM_DotQuaternion(Left, Right); + float Angle = HMM_ACosF(Cos_Theta); + + float S1 = HMM_SinF((1.0f - Time) * Angle); + float S2 = HMM_SinF(Time * Angle); + float Is = 1.0f / HMM_SinF(Angle); + + QuaternionLeft = HMM_MultiplyQuaternionF(Left, S1); + QuaternionRight = HMM_MultiplyQuaternionF(Right, S2); + + Result = HMM_AddQuaternion(QuaternionLeft, QuaternionRight); + Result = HMM_MultiplyQuaternionF(Result, Is); + + return(Result); +} + +HINLINE hmm_mat4 +HMM_QuaternionToMat4(hmm_quaternion Left) +{ + hmm_mat4 Result; + Result = HMM_Mat4d(1); + + hmm_quaternion NormalizedQuaternion = HMM_NormalizeQuaternion(Left); + + float XX, YY, ZZ, + XY, XZ, YZ, + WX, WY, WZ; + + XX = NormalizedQuaternion.X * NormalizedQuaternion.X; + YY = NormalizedQuaternion.Y * NormalizedQuaternion.Y; + ZZ = NormalizedQuaternion.Z * NormalizedQuaternion.Z; + XY = NormalizedQuaternion.X * NormalizedQuaternion.Y; + XZ = NormalizedQuaternion.X * NormalizedQuaternion.Z; + YZ = NormalizedQuaternion.Y * NormalizedQuaternion.Z; + WX = NormalizedQuaternion.W * NormalizedQuaternion.X; + WY = NormalizedQuaternion.W * NormalizedQuaternion.Y; + WZ = NormalizedQuaternion.W * NormalizedQuaternion.Z; + + Result.Elements[0][0] = 1.0f - 2.0f * (YY + ZZ); + Result.Elements[0][1] = 2.0f * (XY + WZ); + Result.Elements[0][2] = 2.0f * (XZ - WY); + + Result.Elements[1][0] = 2.0f * (XY - WZ); + Result.Elements[1][1] = 1.0f - 2.0f * (XX + ZZ); + Result.Elements[1][2] = 2.0f * (YZ + WX); + + Result.Elements[2][0] = 2.0f * (XZ + WY); + Result.Elements[2][1] = 2.0f * (YZ - WX); + Result.Elements[2][2] = 1.0f - 2.0f * (XX + YY); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotation) +{ + hmm_quaternion Result = {0}; + float AxisNorm = 0; + float SineOfRotation = 0; + hmm_vec3 RotatedVector; + + AxisNorm = HMM_SquareRootF(HMM_DotVec3(Axis, Axis)); + SineOfRotation = HMM_SinF(AngleOfRotation / 2.0f); + RotatedVector = HMM_MultiplyVec3f(Axis, SineOfRotation); + + Result.W = HMM_CosF(AngleOfRotation / 2.0f); + Result.XYZ = HMM_DivideVec3f(RotatedVector, AxisNorm); + + return(Result); +} + +// Extensions + +HINLINE hmm_mat4 +HMM_Mat4Identity() +{ + hmm_mat4 Result = {}; + + Result.Elements[0][0] = 1; + Result.Elements[0][1] = 0; + Result.Elements[0][2] = 0; + Result.Elements[0][3] = 0; + + Result.Elements[1][0] = 0; + Result.Elements[1][1] = 1; + Result.Elements[1][2] = 0; + Result.Elements[1][3] = 0; + + Result.Elements[2][0] = 0; + Result.Elements[2][1] = 0; + Result.Elements[2][2] = 1; + Result.Elements[2][3] = 0; + + Result.Elements[3][0] = 0; + Result.Elements[3][1] = 0; + Result.Elements[3][2] = 0; + Result.Elements[3][3] = 1; + + return Result; +} + +#define HMMToV2(v) (v2){ (v).X, (v).Y } +#define HMMToV3(v) (v3){ (v).X, (v).Y, (v).Z } +#define HMMToV4(v) (v4){ (v).X, (v).Y, (v).Z, (v).W } + +#define ToHMMV2(v) (hmm_vec2){ (v).x, (v).y } +#define ToHMMV3(v) (hmm_vec3){ (v).x, (v).y, (v).z } +#define ToHMMV4(v) (hmm_vec4){ (v).x, (v).y, (v).z, (v).w } + +HINLINE hmm_vec2 +HMM_LerpV2(hmm_vec2 A, float T, hmm_vec2 B) +{ + hmm_vec2 Result = {}; + Result.X = HMM_Lerp(A.X, T, B.X); + Result.Y = HMM_Lerp(A.Y, T, B.Y); + return Result; +} +HINLINE hmm_vec3 +HMM_LerpV3(hmm_vec3 A, float T, hmm_vec3 B) +{ + hmm_vec3 Result = {}; + Result.X = HMM_Lerp(A.X, T, B.X); + Result.Y = HMM_Lerp(A.Y, T, B.Y); + Result.Z = HMM_Lerp(A.Z, T, B.Z); + return Result; +} +HINLINE hmm_vec4 +HMM_LerpV4(hmm_vec4 A, float T, hmm_vec4 B) +{ + hmm_vec4 Result = {}; + Result.X = HMM_Lerp(A.X, T, B.X); + Result.Y = HMM_Lerp(A.Y, T, B.Y); + Result.Z = HMM_Lerp(A.Z, T, B.Z); + Result.W = HMM_Lerp(A.W, T, B.W); + return Result; +} + +// Range + +static bool +HMM_V2RContains(hmm_v2r Range, hmm_v2 P) +{ + return (Range.ValueMin.x <= P.x && + Range.ValueMin.y <= P.y && + Range.ValueMax.x >= P.x && + Range.ValueMax.y >= P.y); +} + +static hmm_v2 +HMM_V2RSize(hmm_v2r Range) +{ + hmm_v2 Result = {}; + Result.x = Range.ValueMax.x - Range.ValueMin.x; + Result.y = Range.ValueMax.y - Range.ValueMin.y; + return Result; +} + +#define HMM_V2RWidth(r) HMM_V2RSize(r).x +#define HMM_V2RHeight(r) HMM_V2RSize(r).y + +static hmm_v2r +HMM_V2ROffset(hmm_v2r Range, hmm_v2 Offset) +{ + hmm_v2r Result = {}; + Result.ValueMin = HMM_AddVec2(Range.ValueMin, Offset); + Result.ValueMax = HMM_AddVec2(Range.ValueMax, Offset); + return Result; +} + +// Geometry + +static hmm_vec3 +HMM_AverageVec3(hmm_vec3* Arr, int Len) +{ + hmm_vec3 Total = {}; + for (int i = 0; i < Len; i++) + { + Total += HMM_AddVec3(Total, Arr[i]); + } + + hmm_vec3 Result = {}; + Result.x = Total.x / Len; + Result.y = Total.y / Len; + Result.z = Total.z / Len; + + return Result; +} + +static hmm_vec4 +HMM_AverageVec4(hmm_vec4* Arr, int Len) +{ + hmm_vec4 Total = {}; + for (int i = 0; i < Len; i++) + { + Total = HMM_AddVec4(Total, Arr[i]); + } + + hmm_vec4 Result = {}; + Result.x = Total.x / Len; + Result.y = Total.y / Len; + Result.z = Total.z / Len; + Result.w = Total.w / Len; + return Result; +} + +// Point In Triangle From: https://blackpawn.com/texts/pointinpoly/default.html +static bool +HMM_SameSide(hmm_vec3 P1, hmm_vec3 P2, hmm_vec3 A, hmm_vec3 B) +{ + hmm_vec3 BA = HMM_SubtractVec3(B, A); + hmm_vec3 P1A = HMM_SubtractVec3(P1, A); + hmm_vec3 P2A = HMM_SubtractVec3(P2, A); + hmm_vec3 CP1 = HMM_Cross(BA, P1A); + hmm_vec3 CP2 = HMM_Cross(BA, P2A); + return (HMM_DotVec3(CP1, CP2) >= 0); +} + +static bool +HMM_PointInTriangle(hmm_vec3 P, hmm_vec3 T0, hmm_vec3 T1, hmm_vec3 T2) +{ + return (HMM_SameSide(P, T0, T1, T2) && + HMM_SameSide(P, T1, T0, T2) && + HMM_SameSide(P, T2, T0, T1)); +} + +#ifdef HANDMADE_MATH_CPP_MODE + +HINLINE float +HMM_Length(hmm_vec2 A) +{ + float Result = 0.0f; + + Result = HMM_LengthVec2(A); + + return(Result); +} + +HINLINE float +HMM_Length(hmm_vec3 A) +{ + float Result = 0.0f; + + Result = HMM_LengthVec3(A); + + return(Result); +} + +HINLINE float +HMM_Length(hmm_vec4 A) +{ + float Result = 0.0f; + + Result = HMM_LengthVec4(A); + + return(Result); +} + +HINLINE float +HMM_LengthSquared(hmm_vec2 A) +{ + float Result = 0.0f; + + Result = HMM_LengthSquaredVec2(A); + + return(Result); +} + +HINLINE float +HMM_LengthSquared(hmm_vec3 A) +{ + float Result = 0.0f; + + Result = HMM_LengthSquaredVec3(A); + + return(Result); +} + +HINLINE float +HMM_LengthSquared(hmm_vec4 A) +{ + float Result = 0.0f; + + Result = HMM_LengthSquaredVec4(A); + + return(Result); +} + +HINLINE hmm_vec2 +HMM_Normalize(hmm_vec2 A) +{ + hmm_vec2 Result = {0}; + + Result = HMM_NormalizeVec2(A); + + return(Result); +} + +HINLINE hmm_vec3 +HMM_Normalize(hmm_vec3 A) +{ + hmm_vec3 Result = {0}; + + Result = HMM_NormalizeVec3(A); + + return(Result); +} + +HINLINE hmm_vec4 +HMM_Normalize(hmm_vec4 A) +{ + hmm_vec4 Result = {0}; + + Result = HMM_NormalizeVec4(A); + + return(Result); +} + +HINLINE hmm_quaternion +HMM_Normalize(hmm_quaternion A) +{ + hmm_quaternion Result = {0}; + + Result = HMM_NormalizeQuaternion(A); + + return(Result); +} + +HINLINE float +HMM_Dot(hmm_vec2 VecOne, hmm_vec2 VecTwo) +{ + float Result = 0; + + Result = HMM_DotVec2(VecOne, VecTwo); + + return(Result); +} + +HINLINE float +HMM_Dot(hmm_vec3 VecOne, hmm_vec3 VecTwo) +{ + float Result = 0; + + Result = HMM_DotVec3(VecOne, VecTwo); + + return(Result); +} + +HINLINE float +HMM_Dot(hmm_vec4 VecOne, hmm_vec4 VecTwo) +{ + float Result = 0; + + Result = HMM_DotVec4(VecOne, VecTwo); + + return(Result); +} + +HINLINE float +HMM_Dot(hmm_quaternion QuatOne, hmm_quaternion QuatTwo) +{ + float Result = 0; + + Result = HMM_DotQuaternion(QuatOne, QuatTwo); + + return(Result); +} + +HINLINE hmm_vec2 +HMM_Add(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_AddVec2(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +HMM_Add(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_AddVec3(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +HMM_Add(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_AddVec4(Left, Right); + return (Result); +} + +HINLINE hmm_mat4 +HMM_Add(hmm_mat4 Left, hmm_mat4 Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_AddMat4(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +HMM_Add(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_AddQuaternion(Left, Right); + return(Result); +} + +HINLINE hmm_vec2 +HMM_Subtract(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_SubtractVec2(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +HMM_Subtract(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_SubtractVec3(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +HMM_Subtract(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_SubtractVec4(Left, Right); + return (Result); +} + +HINLINE hmm_mat4 +HMM_Subtract(hmm_mat4 Left, hmm_mat4 Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_SubtractMat4(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +HMM_Subtract(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_SubtractQuaternion(Left, Right); + return (Result); +} + +HINLINE hmm_vec2 +HMM_Multiply(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_MultiplyVec2(Left, Right); + return (Result); +} + +HINLINE hmm_vec2 +HMM_Multiply(hmm_vec2 Left, float Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_MultiplyVec2f(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +HMM_Multiply(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_MultiplyVec3(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +HMM_Multiply(hmm_vec3 Left, float Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_MultiplyVec3f(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +HMM_Multiply(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_MultiplyVec4(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +HMM_Multiply(hmm_vec4 Left, float Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_MultiplyVec4f(Left, Right); + return (Result); +} + +HINLINE hmm_mat4 +HMM_Multiply(hmm_mat4 Left, hmm_mat4 Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_MultiplyMat4(Left, Right); + return (Result); +} + +HINLINE hmm_mat4 +HMM_Multiply(hmm_mat4 Left, float Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_MultiplyMat4f(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +HMM_Multiply(hmm_mat4 Matrix, hmm_vec4 Vector) +{ + hmm_vec4 Result = {0}; + + Result = HMM_MultiplyMat4ByVec4(Matrix, Vector); + return (Result); +} + +HINLINE hmm_quaternion +HMM_Multiply(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_MultiplyQuaternion(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +HMM_Multiply(hmm_quaternion Left, float Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_MultiplyQuaternionF(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +HMM_Multiply(float Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_MultiplyQuaternionF(Right, Left); + return (Result); +} + +HINLINE hmm_vec2 +HMM_Divide(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_DivideVec2(Left, Right); + return (Result); +} + +HINLINE hmm_vec2 +HMM_Divide(hmm_vec2 Left, float Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_DivideVec2f(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +HMM_Divide(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_DivideVec3(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +HMM_Divide(hmm_vec3 Left, float Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_DivideVec3f(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +HMM_Divide(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_DivideVec4(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +HMM_Divide(hmm_vec4 Left, float Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_DivideVec4f(Left, Right); + return (Result); +} + +HINLINE hmm_mat4 +HMM_Divide(hmm_mat4 Left, float Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_DivideMat4f(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +HMM_Divide(hmm_quaternion Left, float Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_DivideQuaternionF(Left, Right); + return (Result); +} + +HINLINE hmm_bool +HMM_Equals(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_bool Result = 0; + + Result = HMM_EqualsVec2(Left, Right); + return (Result); +} + +HINLINE hmm_bool +HMM_Equals(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_bool Result = 0; + + Result = HMM_EqualsVec3(Left, Right); + return (Result); +} + +HINLINE hmm_bool +HMM_Equals(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_bool Result = 0; + + Result = HMM_EqualsVec4(Left, Right); + return (Result); +} + +HINLINE hmm_vec2 +operator+(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_Add(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +operator+(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_Add(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +operator+(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_Add(Left, Right); + return (Result); +} + +HINLINE hmm_mat4 +operator+(hmm_mat4 Left, hmm_mat4 Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_Add(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +operator+(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Add(Left, Right); + return (Result); +} + +HINLINE hmm_vec2 +operator-(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_Subtract(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +operator-(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_Subtract(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +operator-(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_Subtract(Left, Right); + return (Result); +} + +HINLINE hmm_mat4 +operator-(hmm_mat4 Left, hmm_mat4 Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_Subtract(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +operator-(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Subtract(Left, Right); + return (Result); +} + +HINLINE hmm_vec2 +operator*(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +operator*(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +operator*(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = HMM_Multiply(Left, Right); + + return (Result); +} + +HINLINE hmm_vec2 +operator*(hmm_vec2 Left, float Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +operator*(hmm_vec3 Left, float Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +operator*(hmm_vec4 Left, float Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_mat4 +operator*(hmm_mat4 Left, float Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_vec2 +operator*(float Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_Multiply(Right, Left); + return (Result); +} + +HINLINE hmm_vec3 +operator*(float Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_Multiply(Right, Left); + return (Result); +} + +HINLINE hmm_vec4 +operator*(float Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_Multiply(Right, Left); + return (Result); +} + +HINLINE hmm_mat4 +operator*(float Left, hmm_mat4 Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_Multiply(Right, Left); + return (Result); +} + +HINLINE hmm_mat4 +operator*(hmm_mat4 Left, hmm_mat4 Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +operator*(hmm_mat4 Matrix, hmm_vec4 Vector) +{ + hmm_vec4 Result = {0}; + + Result = HMM_Multiply(Matrix, Vector); + return (Result); +} + +HINLINE hmm_quaternion +operator*(hmm_quaternion Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +operator*(hmm_quaternion Left, float Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Multiply(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +operator*(float Left, hmm_quaternion Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Multiply(Right, Left); + return (Result); +} + +HINLINE hmm_vec2 +operator/(hmm_vec2 Left, hmm_vec2 Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_Divide(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +operator/(hmm_vec3 Left, hmm_vec3 Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_Divide(Left, Right); + + return (Result); +} + +HINLINE hmm_vec4 +operator/(hmm_vec4 Left, hmm_vec4 Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_Divide(Left, Right); + return (Result); +} + +HINLINE hmm_vec2 +operator/(hmm_vec2 Left, float Right) +{ + hmm_vec2 Result = {0}; + + Result = HMM_Divide(Left, Right); + return (Result); +} + +HINLINE hmm_vec3 +operator/(hmm_vec3 Left, float Right) +{ + hmm_vec3 Result = {0}; + + Result = HMM_Divide(Left, Right); + return (Result); +} + +HINLINE hmm_vec4 +operator/(hmm_vec4 Left, float Right) +{ + hmm_vec4 Result = {0}; + + Result = HMM_Divide(Left, Right); + return (Result); +} + +HINLINE hmm_mat4 +operator/(hmm_mat4 Left, float Right) +{ + hmm_mat4 Result = {0}; + + Result = HMM_Divide(Left, Right); + return (Result); +} + +HINLINE hmm_quaternion +operator/(hmm_quaternion Left, float Right) +{ + hmm_quaternion Result = {0}; + + Result = HMM_Divide(Left, Right); + return (Result); +} + +HINLINE hmm_vec2 & +operator+=(hmm_vec2 &Left, hmm_vec2 Right) +{ + return (Left = Left + Right); +} + +HINLINE hmm_vec3 & +operator+=(hmm_vec3 &Left, hmm_vec3 Right) +{ + return (Left = Left + Right); +} + +HINLINE hmm_vec4 & +operator+=(hmm_vec4 &Left, hmm_vec4 Right) +{ + return (Left = Left + Right); +} + +HINLINE hmm_mat4 & +operator+=(hmm_mat4 &Left, hmm_mat4 Right) +{ + return (Left = Left + Right); +} + +HINLINE hmm_quaternion & +operator+=(hmm_quaternion &Left, hmm_quaternion Right) +{ + return (Left = Left + Right); +} + +HINLINE hmm_vec2 & +operator-=(hmm_vec2 &Left, hmm_vec2 Right) +{ + return (Left = Left - Right); +} + +HINLINE hmm_vec3 & +operator-=(hmm_vec3 &Left, hmm_vec3 Right) +{ + return (Left = Left - Right); +} + +HINLINE hmm_vec4 & +operator-=(hmm_vec4 &Left, hmm_vec4 Right) +{ + return (Left = Left - Right); +} + +HINLINE hmm_mat4 & +operator-=(hmm_mat4 &Left, hmm_mat4 Right) +{ + return (Left = Left - Right); +} + +HINLINE hmm_quaternion & +operator-=(hmm_quaternion &Left, hmm_quaternion Right) +{ + return (Left = Left - Right); +} + +HINLINE hmm_vec2 & +operator/=(hmm_vec2 &Left, hmm_vec2 Right) +{ + return (Left = Left / Right); +} + +HINLINE hmm_vec3 & +operator/=(hmm_vec3 &Left, hmm_vec3 Right) +{ + return (Left = Left / Right); +} + +HINLINE hmm_vec4 & +operator/=(hmm_vec4 &Left, hmm_vec4 Right) +{ + return (Left = Left / Right); +} + +HINLINE hmm_vec2 & +operator/=(hmm_vec2 &Left, float Right) +{ + return (Left = Left / Right); +} + +HINLINE hmm_vec3 & +operator/=(hmm_vec3 &Left, float Right) +{ + return (Left = Left / Right); +} + +HINLINE hmm_vec4 & +operator/=(hmm_vec4 &Left, float Right) +{ + return (Left = Left / Right); +} + +HINLINE hmm_mat4 & +operator/=(hmm_mat4 &Left, float Right) +{ + return (Left = Left / Right); +} + +HINLINE hmm_quaternion & +operator/=(hmm_quaternion &Left, float Right) +{ + return (Left = Left / Right); +} + +HINLINE hmm_vec2 & +operator*=(hmm_vec2 &Left, hmm_vec2 Right) +{ + return (Left = Left * Right); +} + +HINLINE hmm_vec3 & +operator*=(hmm_vec3 &Left, hmm_vec3 Right) +{ + return (Left = Left * Right); +} + +HINLINE hmm_vec4 & +operator*=(hmm_vec4 &Left, hmm_vec4 Right) +{ + return (Left = Left * Right); +} + +HINLINE hmm_vec2 & +operator*=(hmm_vec2 &Left, float Right) +{ + return (Left = Left * Right); +} + +HINLINE hmm_vec3 & +operator*=(hmm_vec3 &Left, float Right) +{ + return (Left = Left * Right); +} + +HINLINE hmm_vec4 & +operator*=(hmm_vec4 &Left, float Right) +{ + return (Left = Left * Right); +} + +HINLINE hmm_mat4 & +operator*=(hmm_mat4 &Left, float Right) +{ + return (Left = Left * Right); +} + +HINLINE hmm_quaternion & +operator*=(hmm_quaternion &Left, float Right) +{ + return (Left = Left * Right); +} + +HINLINE hmm_bool +operator==(hmm_vec2 Left, hmm_vec2 Right) +{ + return HMM_EqualsVec2(Left, Right); +} + +HINLINE hmm_bool +operator==(hmm_vec3 Left, hmm_vec3 Right) +{ + return HMM_EqualsVec3(Left, Right); +} + +HINLINE hmm_bool +operator==(hmm_vec4 Left, hmm_vec4 Right) +{ + return HMM_EqualsVec4(Left, Right); +} + + +HINLINE hmm_bool +operator!=(hmm_vec2 Left, hmm_vec2 Right) +{ + return !HMM_EqualsVec2(Left, Right); +} + +HINLINE hmm_bool +operator!=(hmm_vec3 Left, hmm_vec3 Right) +{ + return !HMM_EqualsVec3(Left, Right); +} + +HINLINE hmm_bool +operator!=(hmm_vec4 Left, hmm_vec4 Right) +{ + return !HMM_EqualsVec4(Left, Right); +} + +#endif /* HANDMADE_MATH_CPP_MODE */ + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif /* HANDMADE_MATH_IMPLEMENTATION */ diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.cpp new file mode 100644 index 0000000..e81ff6a --- /dev/null +++ b/src_v2/lumenarium_first.cpp @@ -0,0 +1,87 @@ +#include "lumenarium_first.h" + +internal App_State* +lumenarium_init() +{ + permanent = bump_allocator_create_reserve(MB(64)); + scratch = bump_allocator_create_reserve(MB(64)); + + run_tests(); + + App_State* state = allocator_alloc_struct(permanent, App_State); + add_flag(state->flags, AppState_IsRunning); + + state->input_state = input_state_create(); + + en_init(state); + if (!has_flag(state->flags, AppState_NoEditor)) + { + ed_init(state); + } + + return state; +} + +internal void +lumenarium_frame_prepare(App_State* state) +{ + allocator_clear(scratch); + input_state_swap_frames(&state->input_state); + + en_frame_prepare(state); + if (!has_flag(state->flags, AppState_NoEditor)) + { + ed_frame_prepare(state); + } +} + +internal void +lumenarium_frame(App_State* state) +{ + en_frame(state); + if (!has_flag(state->flags, AppState_NoEditor)) + { + ed_frame(state); + } +} + +internal void +lumenarium_event(Platform_Window_Event evt, App_State* state) +{ + Input_Frame* frame = state->input_state.frame_hot; + switch (evt.kind) + { + case WindowEvent_MouseScroll: + { + frame->mouse_scroll = evt.scroll_amt; + } break; + + case WindowEvent_ButtonDown: + case WindowEvent_ButtonUp: + { + frame->key_flags[evt.key_code] = evt.key_flags; + } break; + + case WindowEvent_Char: + { + frame->string_input[frame->string_input_len++] = evt.char_value; + } break; + + case WindowEvent_WindowClosed: + { + rem_flag(state->flags, AppState_IsRunning); + } break; + + invalid_default_case; + } +} + +internal void +lumenarium_cleanup(App_State* state) +{ + en_cleanup(state); + if (!has_flag(state->flags, AppState_NoEditor)) + { + ed_cleanup(state); + } +} diff --git a/src_v2/lumenarium_first.h b/src_v2/lumenarium_first.h new file mode 100644 index 0000000..f6d93c0 --- /dev/null +++ b/src_v2/lumenarium_first.h @@ -0,0 +1,48 @@ +/* date = March 22nd 2022 2:29 am */ + +#ifndef LUMENARIUM_FIRST_H +#define LUMENARIUM_FIRST_H + +typedef struct App_State App_State; + +#include "lumenarium_memory.cpp" +#include "lumenarium_string.cpp" +#include "lumenarium_input.cpp" + +#include "engine/lumenarium_engine_assembly.h" +#include "engine/lumenarium_engine.cpp" + +#include "editor/lumenarium_editor_renderer.cpp" +#include "editor/lumenarium_editor.cpp" + +////////////////////////////////////////////// +// Lumenarium Runtime Environment + +global Allocator* permanent; +global Allocator* scratch; // gets reset at frame boundaries + +#if defined(DEBUG) +# include "lumenarium_tests.cpp" +#else +# define run_tests() +#endif + +////////////////////////////////////////////// +// Lumenarium State + +typedef b32 App_State_Flags; +enum +{ + AppState_None = 0, + AppState_IsRunning = 1, + AppState_NoEditor = 2, +}; + +struct App_State +{ + App_State_Flags flags; + + Input_State input_state; +}; + +#endif //LUMENARIUM_FIRST_H diff --git a/src_v2/lumenarium_input.cpp b/src_v2/lumenarium_input.cpp new file mode 100644 index 0000000..8937018 --- /dev/null +++ b/src_v2/lumenarium_input.cpp @@ -0,0 +1,81 @@ + +#define INPUT_FRAME_STRING_LENGTH 32 +struct Input_Frame +{ + Platform_Key_Flags key_flags[KeyCode_Count]; + + char string_input[INPUT_FRAME_STRING_LENGTH]; + u32 string_input_len; + + v2 mouse_pos; + s32 mouse_scroll; +}; + +struct Input_State +{ + Input_Frame frames[2]; + Input_Frame* frame_hot; + Input_Frame* frame_cold; + + // cross frame state tracking + v2 mouse_pos_delta; + v2 mouse_pos_down; +}; + +#define key_was_down(key_flags) has_flag((key_flags), KeyFlag_State_WasDown) +#define key_is_down(key_flags) has_flag((key_flags), KeyFlag_State_IsDown) +#define key_was_up(key_flags) (!has_flag((key_flags), KeyFlag_State_WasDown) +#define key_is_up(key_flags) (!has_flag((key_flags), KeyFlag_State_IsDown) + +internal Input_State +input_state_create() +{ + Input_State result = {}; + result.frame_hot = result.frames + 0; + result.frame_cold = result.frames + 1; + return result; +} + +internal void +input_state_swap_frames(Input_State* input_state) +{ + Input_Frame* next_hot = input_state->frame_cold; + input_state->frame_cold = input_state->frame_hot; + input_state->frame_hot = next_hot; + + // Clear the new hot input frame + Platform_Key_Flags* hot_key_flags = input_state->frame_hot->key_flags; + Platform_Key_Flags* cold_key_flags = input_state->frame_cold->key_flags; + for (u32 i = 0; i < KeyCode_Count; i++) hot_key_flags[i] = cold_key_flags[i]; + input_state->frame_hot->string_input_len = 0; +} + +// Key State Queries + +internal bool +input_key_is_down(Input_State* input_state, Platform_Key_Code key) +{ + Platform_Key_Flags flags = input_state->frame_hot->key_flags[key]; + return key_is_down(flags); +} + +internal bool +input_key_went_down(Input_State* input_state, Platform_Key_Code key) +{ + Platform_Key_Flags flags = input_state->frame_hot->key_flags[key]; + return key_is_down(flags) && !key_was_down(flags); +} + +internal bool +input_key_is_up(Input_State* input_state, Platform_Key_Code key) +{ + Platform_Key_Flags flags = input_state->frame_hot->key_flags[key]; + return !key_is_down(flags); +} + +internal bool +input_key_went_up(Input_State* input_state, Platform_Key_Code key) +{ + Platform_Key_Flags flags = input_state->frame_hot->key_flags[key]; + return !key_is_down(flags) && key_was_down(flags); +} diff --git a/src_v2/lumenarium_memory.cpp b/src_v2/lumenarium_memory.cpp new file mode 100644 index 0000000..6fa8123 --- /dev/null +++ b/src_v2/lumenarium_memory.cpp @@ -0,0 +1,226 @@ + +///////////////////////////////////////// +// Memory Functions + +void +memory_zero_no_simd(u8* base, u64 size) +{ + for (u64 i = 0; i < size; i++) base[i] = 0; +} + +void +memory_copy_no_simd(u8* from, u8* to, u64 size) +{ + for (u64 i = 0; i < size; i++) to[i] = from[i]; +} + +#if defined(PLATFORM_HAS_SIMD) + +// TODO(PS): +// TODO(PS): +// TODO(PS): + +void +memory_zero_simd(u8* base, u64 size) +{ + memory_zero_no_simd(base, size); +} + +void +memory_copy_simd(u8* from, u8* to, u64 size) +{ + memory_copy_no_simd(from, to, size); +} + +# define memory_zero(b,s) memory_zero_simd((b),(s)) +# define memory_copy(f,t,s) memory_copy_simd((f),(t),(s)) + +#else +# define memory_zero(b,s) memory_zero_no_simd((b),(s)) +# define memory_copy(f,t,s) memory_copy_no_simd((f),(t),(s)) + +#endif // defined(PLATFORM_HAS_SIMD) + +#define zero_struct(s) memory_zero((u8*)(&s), sizeof(s)) + +u64 +size_to_pages(u64 size) +{ + u64 page_size = platform_page_size(); + u64 rem = size % page_size; + if (rem != 0 || size < page_size) + { + u64 grow = page_size - rem; + size += grow; + } + return size; +} + +///////////////////////////////////////// +// Allocator +// +// A generic interface for any memory-providing construct +// +// To implement a complete allocator, all that is really required +// is to create its Allocator_Alloc function + +typedef struct Allocator Allocator; + +typedef u8* Allocator_Alloc(Allocator* allocator, u64 size); +typedef void Allocator_Free(Allocator* allocator, u8* base, u64 size); +typedef u8* Allocator_Realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size); +typedef void Allocator_Clear(Allocator* allocator); + +struct Allocator +{ + Allocator_Alloc* alloc; + Allocator_Free* free; + Allocator_Realloc* realloc; + Allocator_Clear* clear; + + Allocator* parent; + + u8* allocator_data; +}; + +#define allocator_alloc(a,s) (a)->alloc((a),(s)) +#define allocator_alloc_struct(a,t) (t*)(a)->alloc((a),sizeof(t)) +#define allocator_alloc_array(a,t,c) (t*)(a)->alloc((a),sizeof(t)*(c)) + +#define allocator_free(a,b,s) (a)->free((a),(b),(s)) +#define allocator_free_struct(a,b,t) (a)->free((a),(b),sizeof(t)) +#define allocator_free_array(a,b,t,c) (a)->free((a),(b),sizeof(t)*(c)) + +#define allocator_realloc(a,b,os,ns) (a)->realloc((a),(b),(os),(ns)) +#define allocator_realloc_array(a,b,t,oc,nc) (t*)(a)->realloc((a),(b),sizeof(t)*(oc),sizeof(t)*(nc)) + +#define allocator_clear(a) (a)->clear(a) + +///////////////////////////////////////// +// Bump Allocator + +struct Allocator_Bump +{ + u8* base; + u64 at; + u64 size_committed; + u64 size_reserved; +}; + +internal u8* +bump_allocator_alloc(Allocator* allocator, u64 size) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; + + u64 at_after = bump->at + size; + // TODO(PS): align up to 8 bytes + + if (at_after >= bump->size_committed) + { + // determine new size of the arena + u64 new_size = bump->size_committed * 2; + if (new_size == 0) new_size = platform_page_size(); + if (new_size < at_after) new_size = size_to_pages(at_after); + + if (allocator->parent) + { + bump->base = allocator_realloc( + allocator->parent, + bump->base, + bump->size_committed, + new_size + ); + if (bump->base != 0) + { + bump->size_reserved = new_size; + bump->size_committed = new_size; + } + } + else + { + if (new_size < bump->size_reserved) + { + u64 next_page = size_to_pages(bump->at); + u64 commit_amt = new_size - next_page; + bump->base = platform_mem_commit(bump->base + next_page, commit_amt); + } + else + { + invalid_code_path; // out of reserved memory + } + } + } + + u8* result = bump->base; + bump->at = at_after; + + return result; +} + +internal u8* +bump_allocator_realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size) +{ + u8* result = bump_allocator_alloc(allocator, new_size); + memory_copy(base, result, old_size); + return result; +} + +internal void +bump_allocator_clear(Allocator* allocator) +{ + if (!allocator->allocator_data) return; + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; + bump->at = 0; +} + +internal Allocator* +bump_allocator_create_() +{ + u64 size_needed = sizeof(Allocator) + sizeof(Allocator_Bump); + + u8* base = platform_mem_reserve(size_needed); + base = platform_mem_commit(base, size_needed); + + Allocator* result = (Allocator*)base; + zero_struct(*result); + + Allocator_Bump* bump = (Allocator_Bump*)base + sizeof(Allocator); + zero_struct(*bump); + + result->alloc = bump_allocator_alloc; + result->realloc = bump_allocator_realloc; + result->clear = bump_allocator_clear; + result->allocator_data = (u8*)bump; + + return result; +} + +internal Allocator* +bump_allocator_create_reserve(u64 reserve_size) +{ + Allocator* result = bump_allocator_create_(); + Allocator_Bump* bump = (Allocator_Bump*)result->allocator_data; + + u64 reserve_pages = size_to_pages(reserve_size); + bump->base = platform_mem_reserve(reserve_pages); + if (bump->base != 0) bump->size_reserved = reserve_pages; + + return result; +} + +internal Allocator* +bump_allocator_create_child(Allocator* parent, u64 init_size) +{ + Allocator* result = bump_allocator_create_(); + result->parent = parent; + + Allocator_Bump* bump = (Allocator_Bump*)result->allocator_data; + bump->base = allocator_alloc(result->parent, init_size); + if (bump->base != 0) + { + bump->size_reserved = init_size; + bump->size_committed = init_size; + } + + return result; +} diff --git a/src_v2/lumenarium_string.cpp b/src_v2/lumenarium_string.cpp new file mode 100644 index 0000000..b7850a0 --- /dev/null +++ b/src_v2/lumenarium_string.cpp @@ -0,0 +1,226 @@ + +internal u64 +c_str_len(char* s) +{ + u64 result = 0; + for (; s[result] != 0; result++) {} + return result; +} + +#define str_varg(str) (int)(str).len, (char*)(str).str +#define lit_str(s) String{ (u8*)(s), (u64)sizeof(s)-1, (u64)sizeof(s)-1 } + +internal String +allocator_alloc_string(Allocator* a, u64 cap) +{ + String result = {}; + result.str = allocator_alloc_array(a, u8, cap); + result.cap = cap; + return result; +} + +///////////////////////////////////// +// Char Operations + +bool +char_is_space(u8 c) +{ + return c == ' ' || c == '\r' || c == '\t' || c == '\f' || c == '\v' || c == '\n'; +} + +u8 +char_to_upper(u8 c) +{ + return (c >= 'a' && c <= 'z') ? ('A' + (c - 'a')) : c; +} + +u8 +char_to_lower(u8 c) +{ + return (c >= 'A' && c <= 'Z') ? ('a' + (c - 'A')) : c; +} + +u8 +char_to_forward_slash(u8 c) +{ + return (c == '\\' ? '/' : c); +} + + +///////////////////////////////////// +// String Operations +// +// Note that these don't actually modify any memory +// just return structures that let you view it differently + +internal String +string_substring(String s, u64 min, u64 max) +{ + if (max > s.len) max = s.len; + if (min > s.len) min = s.len; + if (min > max) { + u64 t = min; + min = max; + max = min; + } + String result = {}; + result.str = s.str + min; + result.len = max - min; + result.cap = result.len; + return result; +} + +internal String +string_skip(String s, u64 min) +{ + return string_substring(s, min, s.len); +} + +internal String +string_chop(String s, u64 nmax) +{ + return string_substring(s, 0, s.len - nmax); +} + +internal String +string_get_prefix(String s, u64 max) +{ + return string_substring(s, 0, max); +} + +internal String +string_get_suffix(String s, u64 nmax) +{ + return string_substring(s, s.len - nmax, s.len); +} + +typedef u32 String_Match_Flags; +enum +{ + StringMatch_FindLast = 1, + StringMatch_CaseInsensitive = 2, + StringMatch_SlashInsensitive = 4, +}; + +internal bool +string_match(String a, String b, String_Match_Flags flags) +{ + bool result = false; + if (a.len == b.len) + { + result = true; + for (u64 i = 0; i < a.len; i++) + { + bool match = a.str[i] == b.str[i]; + if(flags & StringMatch_CaseInsensitive) + { + match |= (char_to_lower(a.str[i]) == char_to_lower(b.str[i])); + } + if(flags & StringMatch_SlashInsensitive) + { + match |= (char_to_forward_slash(a.str[i]) == char_to_forward_slash(b.str[i])); + } + if(match == 0) + { + result = 0; + break; + } + } + } + return result; +} + +internal u64 +string_find_substring(String s, String substr, u64 start_pos, String_Match_Flags flags) +{ + bool found = false; + u64 found_i = s.len; + for (u64 i = start_pos; i < s.len; i++) + { + if (i + substr.len <= s.len) + { + String at = string_substring(s, i, i + substr.len); + if (string_match(at, substr, flags)) + { + found = true; + found_i = i; + if (!(flags & StringMatch_FindLast)) break; + } + } + } + return found_i; +} + +///////////////////////////////////// +// Path Operations + +// good for removing extensions +internal String +string_chop_last_period(String s) +{ + u64 period_pos = string_find_substring(s, lit_str("."), 0, StringMatch_FindLast); + if(period_pos < s.len) + { + s.len = period_pos; + s.cap = s.len; + } + return s; +} + +// get the filename +internal String +string_skip_last_slash(String s) +{ + u64 slash_pos = string_find_substring(s, lit_str("/"), 0, StringMatch_FindLast | StringMatch_SlashInsensitive); + if(slash_pos < s.len) + { + s.str += slash_pos + 1; + s.len -= slash_pos + 1; + s.cap = s.len; + } + return s; +} + +// get the extension +internal String +string_skip_last_period(String s) +{ + u64 period_pos = string_find_substring(s, lit_str("."), 0, StringMatch_FindLast); + if(period_pos < s.len) + { + s.str += period_pos + 1; + s.len -= period_pos + 1; + s.cap = s.len; + } + return s; +} + +// good for getting the path to a file +internal String +string_chop_last_slash(String s) +{ + u64 slash_pos = string_find_substring(s, lit_str("/"), 0, StringMatch_FindLast | StringMatch_SlashInsensitive); + if(slash_pos < s.len) + { + s.len = slash_pos; + s.cap = s.len; + } + return s; +} + + +///////////////////////////////////// +// String Modifications + +internal String +string_copy(String s, Allocator* a) +{ + u64 size = s.cap; + if (s.str[s.cap] != 0) size += 1; + String result = allocator_alloc_string(a, size); + memory_copy(s.str, result.str, s.cap); + result.str[size] = 0; + result.len = size; + return result; +} + diff --git a/src_v2/lumenarium_tests.cpp b/src_v2/lumenarium_tests.cpp new file mode 100644 index 0000000..3bdfa5c --- /dev/null +++ b/src_v2/lumenarium_tests.cpp @@ -0,0 +1,46 @@ + +Platform_Thread_Result +thread_proc(Platform_Thread_Data* td) +{ + return {}; +} + +internal void +run_tests() +{ + // testing strings and exe path + String exe_file_path = platform_get_exe_path(scratch); + assert(exe_file_path.str != 0); + u64 run_tree_start = string_find_substring(exe_file_path, lit_str("run_tree"), 0, StringMatch_FindLast); + u64 run_tree_end = run_tree_start + lit_str("run_tree").len; + assert(run_tree_start < exe_file_path.len); + String run_tree_path = string_get_prefix(exe_file_path, run_tree_end); + String run_tree_path_nullterm = string_copy(run_tree_path, scratch); + assert(run_tree_path_nullterm.len > 0); + assert(platform_pwd_set(run_tree_path_nullterm)); + + // testing file io + Platform_File_Handle f = platform_file_open(lit_str("text.txt"), FileAccess_Read | FileAccess_Write, FileCreate_OpenExisting); + Platform_File_Info i = platform_file_get_info(f, scratch); + + Data d0 = platform_file_read_all(f, scratch); + assert(d0.size > 0); + + String s = lit_str("foooooooooobbbbbbaaaarrrrrr"); + Data d1 = { s.str, s.len }; + bool r = platform_file_write_all(f, d1); + assert(r); + + // testing threads + Platform_Thread_Handle threads[8]; + for (u32 j = 0; j < 8; j++) + { + threads[j] = platform_thread_begin(thread_proc, 0); + } + for (u32 j = 0; j < 8; j++) + { + platform_thread_end(threads[j]); + } + + allocator_clear(scratch); +} \ No newline at end of file diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h new file mode 100644 index 0000000..29b96c9 --- /dev/null +++ b/src_v2/lumenarium_types.h @@ -0,0 +1,106 @@ +/* date = March 22nd 2022 2:08 am */ + +#ifndef LUMENARIUM_TYPES_H +#define LUMENARIUM_TYPES_H + +#define internal static +#define local_persist static +#define global static +#define local_const static const +#define global_const static const +#define external extern "C" + +#if defined(GUESS_INTS) +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#else +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +#endif + +typedef s8 b8; +typedef s32 b32; +typedef s64 b64; + +typedef float r32; +typedef double r64; + +struct Data +{ + u8* base; + u64 size; +}; + +#define Bytes(x) (x) +#define KB(x) ((x) << 10) +#define MB(x) ((x) << 20) +#define GB(x) ((x) << 30) +#define TB(x) (((u64)x) << 40) + +#define has_flag(data, flag) (((data) & (flag)) != 0) +#define has_flag_only(data, flag) (((data) & (flag)) == (data)) +#define add_flag(data, flag) ((data) |= (flag)) +#define rem_flag(data, flag) ((data) &= (~(flag))) + +////////////////////////////////////////////// +// Assert + +// this assert works by simply trying to write to an invalid address +// (in this case, 0x0), which will crash in most debuggers +#define assert_always (*((volatile s32*)0) = 0xFFFF) + +#ifdef USE_ASSERTS +# define assert(c) if (!(c)) { assert_always; } + +// useful for catching cases that you aren't sure you'll hit, but +// want to be alerted when they happen +# define invalid_code_path assert_always + +// useful for switch statements on enums that might grow. You'll +// break in the debugger the first time the default case is hit +// with a new enum value +# define invalid_default_case default: { assert_always; } break; + +#else +# define assert(c) +# define invalid_code_path +# define invalid_default_case default: { } break; +#endif + +////////////////////////////////////////////// +// List Helper Macros + +#define sll_push(first,last,ele) \ +if (!(first)) { (first) = (ele); }\ +else { (last)->next = (ele); } \ +(last) = (ele); (ele)->next = 0; + +// TODO(PS): Stack, Queue, DLL ops + +////////////////////////////////////////////// +// String + +// NOTE(PS): even though this has a len and cap, it should always be +// null terminated +struct String +{ + u8* str; + u64 len; + u64 cap; +}; + +typedef struct Allocator Allocator; + +#endif //LUMENARIUM_TYPES_H diff --git a/src_v2/platform/lumenarium_compiler_flags.h b/src_v2/platform/lumenarium_compiler_flags.h new file mode 100644 index 0000000..c137adf --- /dev/null +++ b/src_v2/platform/lumenarium_compiler_flags.h @@ -0,0 +1,17 @@ +/* date = March 22nd 2022 2:09 am */ + +#ifndef LUMENARIUM_COMPILER_FLAGS_H +#define LUMENARIUM_COMPILER_FLAGS_H + +#if defined(__clang__) +# pragma GCC diagnostic ignored "-Wunused-value" +# pragma GCC diagnostic ignored "-Wvarargs" +# pragma GCC diagnostic ignored "-Wwritable-strings" +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +#ifdef DEBUG +# define USE_ASSERTS 1 +#endif + +#endif //LUMENARIUM_COMPILER_FLAGS_H diff --git a/src_v2/platform/lumenarium_platform.h b/src_v2/platform/lumenarium_platform.h new file mode 100644 index 0000000..4a74d1e --- /dev/null +++ b/src_v2/platform/lumenarium_platform.h @@ -0,0 +1,294 @@ +/* date = March 22nd 2022 2:05 am */ + +#ifndef LUMENARIUM_PLATFORM_H +#define LUMENARIUM_PLATFORM_H + +// This is a file that defines the things that every platform +// must expose to the entire program + +/////////////////////////////////////// +// Memory + +u64 platform_page_size(); + +u8* platform_mem_reserve(u64 size); +u8* platform_mem_commit(u8* base, u64 size); +bool platform_mem_decommit(u8* base, u64 size); +bool platform_mem_release(u8* base, u64 size); + +/////////////////////////////////////// +// File I/O + +struct Platform_File_Handle +{ + u64 value; +}; + +typedef u32 Platform_File_Access_Flags; +enum +{ + FileAccess_None = 0, + FileAccess_Read = 1, + FileAccess_Write = 2, +}; + +typedef u32 Platform_File_Create_Flags; +enum +{ + // these match https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + FileCreate_None = 0, + FileCreate_New = 1, + FileCreate_CreateAlways = 2, + FileCreate_OpenExisting = 3, + FileCreate_OpenAlways = 4, +}; + +typedef u32 Platform_File_Flags; +enum +{ + FileFlag_IsFile = 0, + FileFlag_IsDir = 1, +}; + +struct Platform_File_Info +{ + String path; + String path_abs; + u64 size; + u64 time_created; + u64 time_last_write; + Platform_File_Flags flags; +}; + +struct Platform_File_Info_List_Ele +{ + Platform_File_Info info; + Platform_File_Info_List_Ele* next; +}; + +struct Platform_File_Info_List +{ + Platform_File_Info_List_Ele* first; + Platform_File_Info_List_Ele* last; +}; + +Platform_File_Handle platform_file_open(String path, Platform_File_Access_Flags flags_access, Platform_File_Create_Flags flags_create); +void platform_file_close(Platform_File_Handle file_handle); +Platform_File_Info platform_file_get_info(Platform_File_Handle file_handle, Allocator* allocator); +Data platform_file_read_all(Platform_File_Handle file_handle, Allocator* allocator); +bool platform_file_write_all(Platform_File_Handle file_handle, Data file_data); + +typedef u32 Platform_Enum_Dir_Flags; +enum +{ + EnumDir_Recursive = 1, + EnumDir_IncludeDirectories = 2, +}; + +Platform_File_Info_List platform_dir_enum(String path, Platform_Enum_Dir_Flags flags, Allocator* allocator); + +String platform_get_exe_path(Allocator* allocator); +bool platform_pwd_set(String path); + +/////////////////////////////////////// +// Windows & Events + +enum Platform_Window_Event_Kind +{ + WindowEvent_Invalid = 0, + + WindowEvent_MouseScroll, + WindowEvent_ButtonDown, + WindowEvent_ButtonUp, + WindowEvent_Char, + WindowEvent_WindowClosed, + + WindowEvent_Count, +}; + +typedef u32 Platform_Key_Code; +enum +{ + KeyCode_Invalid = 0, + + KeyCode_Esc, + + KeyCode_Space, + KeyCode_Tab, + KeyCode_CapsLock, + KeyCode_LeftShift, KeyCode_RightShift, + KeyCode_LeftCtrl, KeyCode_RightCtrl, + KeyCode_Fn, + KeyCode_Alt, + KeyCode_PageUp, KeyCode_PageDown, + KeyCode_Backspace, KeyCode_Delete, + KeyCode_Enter, + + // Function Keys + KeyCode_F0, KeyCode_F1, KeyCode_F2, KeyCode_F3, KeyCode_F4, KeyCode_F5, KeyCode_F6, KeyCode_F7, + KeyCode_F8, KeyCode_F9, KeyCode_F10, KeyCode_F11, KeyCode_F12, + + // Letters + KeyCode_a, KeyCode_b, KeyCode_c, KeyCode_d, KeyCode_e, KeyCode_f, KeyCode_g, KeyCode_h, + KeyCode_i, KeyCode_j, KeyCode_k, KeyCode_l, KeyCode_m, KeyCode_n, KeyCode_o, KeyCode_p, + KeyCode_q, KeyCode_r, KeyCode_s, KeyCode_t, KeyCode_u, KeyCode_v, KeyCode_w, KeyCode_x, + KeyCode_y, KeyCode_z, + + KeyCode_A, KeyCode_B, KeyCode_C, KeyCode_D, KeyCode_E, KeyCode_F, KeyCode_G, KeyCode_H, + KeyCode_I, KeyCode_J, KeyCode_K, KeyCode_L, KeyCode_M, KeyCode_N, KeyCode_O, KeyCode_P, + KeyCode_Q, KeyCode_R, KeyCode_S, KeyCode_T, KeyCode_U, KeyCode_V, KeyCode_W, KeyCode_X, + KeyCode_Y, KeyCode_Z, + + // Numbers + KeyCode_0, KeyCode_1, KeyCode_2, KeyCode_3, KeyCode_4, KeyCode_5, KeyCode_6, KeyCode_7, + KeyCode_8, KeyCode_9, + + KeyCode_Num0, KeyCode_Num1, KeyCode_Num2, KeyCode_Num3, KeyCode_Num4, KeyCode_Num5, + KeyCode_Num6, KeyCode_Num7, KeyCode_Num8, KeyCode_Num9, + + // Symbols + KeyCode_Bang, KeyCode_At, KeyCode_Pound, KeyCode_Dollar, KeyCode_Percent, KeyCode_Carrot, + KeyCode_Ampersand, KeyCode_Star, KeyCode_LeftParen, KeyCode_RightParen, KeyCode_Minus, KeyCode_Plus, + KeyCode_Equals, KeyCode_Underscore, KeyCode_LeftBrace, KeyCode_RightBrace, KeyCode_LeftBracket, + KeyCode_RightBracket, KeyCode_Colon, KeyCode_SemiColon, KeyCode_SingleQuote, KeyCode_DoubleQuote, + KeyCode_ForwardSlash, KeyCode_Backslash, KeyCode_Pipe, KeyCode_Comma, KeyCode_Period, + KeyCode_QuestionMark, KeyCode_LessThan, KeyCode_GreaterThan, KeyCode_Tilde, KeyCode_BackQuote, + + // Arrows + KeyCode_UpArrow, + KeyCode_DownArrow, + KeyCode_LeftArrow, + KeyCode_RightArrow, + + // Mouse + // NOTE(Peter): Including this here so we can utilize the same KeyDown, KeyUp etc. functions + KeyCode_MouseLeftButton, + KeyCode_MouseMiddleButton, + KeyCode_MouseRightButton, + + KeyCode_Count, +}; + +typedef u8 Platform_Key_Flags; +enum +{ + KeyFlag_None = 0, + + KeyFlag_Mod_Shift = 1, + KeyFlag_Mod_Ctrl = 2, + KeyFlag_Mod_Alt = 4, + KeyFlag_Mod_Sys = 8, + + KeyFlag_State_WasDown = 16, + KeyFlag_State_IsDown = 32, +}; + +struct Platform_Window_Event +{ + Platform_Window_Event_Kind kind; + Platform_Key_Code key_code; + Platform_Key_Flags key_flags; + s32 mouse_x; + s32 mouse_y; + s32 scroll_amt; + char char_value; +}; + +enum Platform_Cursor_Kind +{ + Cursor_Arrow, + Cursor_Pointer, + Cursor_Loading, + Cursor_HArrows, + Cursor_VArrows, + Cursor_DTopLeftArrows, + Cursor_DTopRightArrows, + Cursor_Count, +}; + +/////////////////////////////////////// +// Time + +global r64 target_seconds_per_frame = 1.0 / 30.0f; + +struct Platform_Ticks +{ + s64 value; +}; + +Platform_Ticks platform_get_ticks(); +r64 platform_ticks_to_seconds(Platform_Ticks ticks); + +Platform_Ticks +get_ticks_elapsed(Platform_Ticks start, Platform_Ticks end) +{ + Platform_Ticks result = {}; + result.value = end.value - start.value; + return result; +} + +r64 +get_seconds_elapsed(Platform_Ticks start, Platform_Ticks end) +{ + Platform_Ticks diff = get_ticks_elapsed(start, end); + return platform_ticks_to_seconds(diff); +} + +// TODO(PS): we have some stuff in v1 around system time, probably +// for timestamps etc. + +/////////////////////////////////////// +// Threads + +struct Platform_Thread_Handle +{ + u64 value; +}; + +struct Platform_Thread_Result +{ + u32 code; +}; + +typedef struct Platform_Thread_Data Platform_Thread_Data; +typedef Platform_Thread_Result Platform_Thread_Proc(Platform_Thread_Data* thread_data); + +struct Platform_Thread_Data +{ + Platform_Thread_Handle thread_handle; + u32 thread_id; + Platform_Thread_Proc* thread_proc; + Allocator* thread_memory; + u8* user_data; +}; + +Platform_Thread_Handle platform_thread_begin(Platform_Thread_Proc* proc, u8* user_data); +void platform_thread_end(Platform_Thread_Handle thread_handle); + +u32 platform_interlocked_increment(volatile u32* value); +u32 platform_interlocked_cmp_exchg(volatile u32* dest, u32 new_value, u32 old_value); + +/////////////////////////////////////// +// Network Access + +// TODO(PS): + +struct Platform_Socket_Handle +{ + u64 value; +}; + +Platform_Socket_Handle platform_socket_create(); +bool platform_socket_bind(); +bool platform_socket_connect(); +bool platform_socket_close(); +Data platform_socket_recv(); +s32 platform_Socket_set_listening(); +s32 platform_Socket_send(); +s32 platform_Socket_send_to(); +s32 platform_Socket_set_opt(); + +/////////////////////////////////////// +// Graphics Integration + +#endif //LUMENARIUM_PLATFORM_H diff --git a/src_v2/platform/lumenarium_platform_common_includes.h b/src_v2/platform/lumenarium_platform_common_includes.h new file mode 100644 index 0000000..46e6c47 --- /dev/null +++ b/src_v2/platform/lumenarium_platform_common_includes.h @@ -0,0 +1,36 @@ +/* date = March 22nd 2022 2:12 am */ + +#ifndef LUMENARIUM_PLATFORM_COMMON_INCLUDES_H +#define LUMENARIUM_PLATFORM_COMMON_INCLUDES_H + +#include + +#if !defined(GUESS_INTS) +# include +#endif // !defined(GUESS_INTS) + +#include + +#if 0 +#define HMM_SINF sin +#define HMM_COSF cos +#define HMM_TANF tan +#define HMM_SQRTF sqrt +#define HMM_EXPF exp +#define HMM_LOGF log +#define HMM_ACOSF acos +#define HMM_ATANF atan +#define HMM_ATAN2F atan2 +#endif + +#define HANDMADE_MATH_IMPLEMENTATION +#define HANDMADE_MATH_CPP_MODE +#define HANDMADE_MATH_STATIC +#include "../libs/HandmadeMath.h" + +typedef hmm_v2 v2; +typedef hmm_v3 v3; +typedef hmm_v4 v4; +typedef hmm_mat4 m44; + +#endif //LUMENARIUM_PLATFORM_COMMON_INCLUDES_H diff --git a/src_v2/platform/osx/lumenarium_first_osx.cpp b/src_v2/platform/osx/lumenarium_first_osx.cpp new file mode 100644 index 0000000..49d4820 --- /dev/null +++ b/src_v2/platform/osx/lumenarium_first_osx.cpp @@ -0,0 +1,28 @@ + +#include "../lumenarium_compiler_flags.h" +#include "../lumenarium_platform_common_includes.h" + +#include "../../lumenarium_types.h" +#include "../lumenarium_platform.h" +#include "../../lumenarium_first.cpp" + +#include +#include + +#include "lumenarium_osx_memory.cpp" + +int main (int arg_count, char** args) +{ + App_State* state = lumenarium_init(); + + while (has_flag(state->flags, AppState_IsRunning)) + { + // TODO(PS): event processing + + lumenarium_update(state); + } + + lumenarium_cleanup(state); + + return 0; +} \ No newline at end of file diff --git a/src_v2/platform/osx/lumenarium_osx_memory.cpp b/src_v2/platform/osx/lumenarium_osx_memory.cpp new file mode 100644 index 0000000..0ae2d54 --- /dev/null +++ b/src_v2/platform/osx/lumenarium_osx_memory.cpp @@ -0,0 +1,30 @@ +#define OSX_PAGE_SIZE KB(4) // TODO(PS): look this up + +u64 platform_page_size() { return OSX_PAGE_SIZE; } + +u8* +platform_mem_reserve(u64 size) +{ + size_t size_cvt = (size_t)size_to_pages(size); + u8* result = (u8*)malloc(size); + return result; +} + +u8* +platform_mem_commit(u8* base, u64 size) +{ + return base; +} + +bool +platform_mem_decommit(u8* base, u64 size) +{ + return true; +} + +bool +platform_mem_release(u8* base, u64 size) +{ + free(base); + return true; +} diff --git a/src_v2/platform/webgl/lumenarium_first_webgl.cpp b/src_v2/platform/webgl/lumenarium_first_webgl.cpp new file mode 100644 index 0000000..a6814e1 --- /dev/null +++ b/src_v2/platform/webgl/lumenarium_first_webgl.cpp @@ -0,0 +1,18 @@ +#define WEBGL_EXPORT __attribute__((visibility("default"))) +#define WEBGL_EXTERN extern "C" + +#define EXTERN_C_BEGIN extern "C" { +#define EXTERN_C_END } + +WEBGL_EXTERN void print(char* text); + +EXTERN_C_BEGIN; + +WEBGL_EXPORT int +main(void) +{ + print("Hi there!\n"); + return 5; +} + +EXTERN_C_END; \ No newline at end of file diff --git a/src_v2/platform/win32/lumenarium_first_win32.cpp b/src_v2/platform/win32/lumenarium_first_win32.cpp new file mode 100644 index 0000000..4eed08d --- /dev/null +++ b/src_v2/platform/win32/lumenarium_first_win32.cpp @@ -0,0 +1,249 @@ + +#include "../lumenarium_compiler_flags.h" +#include "../lumenarium_platform_common_includes.h" + +#include "windows.h" +#include + +#include "../../lumenarium_types.h" +#include "../lumenarium_platform.h" +#include "../../lumenarium_first.cpp" + +global DWORD win32_last_error = 0; +void +win32_get_last_error() +{ + win32_last_error = GetLastError(); +} + +#include "lumenarium_win32_memory.cpp" +#include "lumenarium_win32_window.cpp" +#include "lumenarium_win32_time.cpp" +#include "lumenarium_win32_file.cpp" +#include "lumenarium_win32_thread.cpp" + +internal Platform_Key_Flags +win32_get_key_flags_mod() +{ + Platform_Key_Flags result = 0; + if (GetKeyState(VK_SHIFT) & 0x8000) add_flag(result, KeyFlag_Mod_Shift); + if (GetKeyState(VK_MENU) & 0x8000) add_flag(result, KeyFlag_Mod_Alt); + if (GetKeyState(VK_CONTROL) & 0x8000) add_flag(result, KeyFlag_Mod_Ctrl); + return result; +} + +internal void +win32_mouse_capture(Win32_Window* win) +{ + // NOTE(Peter): We capture events when the mouse goes down so that + // if the user drags outside the window, we still get the mouse up + // event and can process it. Otherwise, we can get into cases where + // an event was started, didn't end, but the user can click again and + // try to start the event again. + // We relase event capture on mouse up. + SetCapture(win->window_handle); +} + +internal void +win32_mouse_release(Win32_Window* win) +{ + ReleaseCapture(); +} + +internal Platform_Window_Event +win32_button_event(Platform_Key_Code key, bool is_down, bool was_down) +{ + Platform_Window_Event evt = {}; + evt.kind = WindowEvent_ButtonDown; + evt.key_code = key; + evt.key_flags = win32_get_key_flags_mod(); + if (is_down) add_flag(evt.key_flags, KeyFlag_State_IsDown); + if (was_down) add_flag(evt.key_flags, KeyFlag_State_WasDown); + return evt; +} + +internal void +win32_window_handle_event(MSG msg, Win32_Window* win, App_State* state) +{ + switch (msg.message) + { + case WM_MOUSEWHEEL: + { + Platform_Window_Event evt = {}; + evt.kind = WindowEvent_MouseScroll; + evt.scroll_amt = GET_WHEEL_DELTA_WPARAM(msg.wParam); + lumenarium_event(evt, state); + }break; + + case WM_LBUTTONDOWN: + { + Platform_Window_Event evt = win32_button_event( + KeyCode_MouseLeftButton, + true, false + ); + lumenarium_event(evt, state); + win32_mouse_capture(win); + }break; + + case WM_MBUTTONDOWN: + { + Platform_Window_Event evt = win32_button_event( + KeyCode_MouseMiddleButton, + true, false + ); + lumenarium_event(evt, state); + win32_mouse_capture(win); + }break; + + case WM_RBUTTONDOWN: + { + Platform_Window_Event evt = win32_button_event( + KeyCode_MouseRightButton, + true, false + ); + lumenarium_event(evt, state); + win32_mouse_capture(win); + }break; + + case WM_LBUTTONUP: + { + Platform_Window_Event evt = win32_button_event( + KeyCode_MouseLeftButton, + false, true + ); + lumenarium_event(evt, state); + win32_mouse_release(win); + }break; + + case WM_MBUTTONUP: + { + Platform_Window_Event evt = win32_button_event( + KeyCode_MouseMiddleButton, + false, true + ); + lumenarium_event(evt, state); + win32_mouse_release(win); + }break; + + case WM_RBUTTONUP: + { + Platform_Window_Event evt = win32_button_event( + KeyCode_MouseRightButton, + false, true + ); + lumenarium_event(evt, state); + win32_mouse_release(win); + }break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + { + Platform_Key_Code key = 0; + b32 was_down = (msg.lParam & (1 << 30)) != 0; + b32 is_down = (msg.lParam & (1 << 31)) == 0; + Platform_Window_Event evt = win32_button_event(key, is_down, was_down); + lumenarium_event(evt, state); + TranslateMessage(&msg); + DispatchMessage(&msg); + }break; + + case WM_CHAR: + { + Platform_Window_Event evt = {}; + evt.kind = WindowEvent_Char; + evt.char_value = (char)msg.wParam; + lumenarium_event(evt, state); + }break; + + default: + { + TranslateMessage(&msg); + DispatchMessage(&msg); + }break; + } +} + +INT WINAPI +WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + PSTR lpCmdLine, + INT nCmdShow) +{ + + App_State* state = lumenarium_init(); + if (!has_flag(state->flags, AppState_IsRunning)) return 0; + + // Window Setup + win32_main_window = win32_window_create( + hInstance, "Lumenarium", 1600, 900, win32_window_event_handler + ); + win32_window_opengl_ctx_create(&win32_main_window, { 32, 8, 0 }); + + win32_time_init(); + win32_files_init(); + win32_threads_init(); + + Platform_Ticks ticks_start = platform_get_ticks(); + while (has_flag(state->flags, AppState_IsRunning)) + { + win32_threads_reclaim(); + lumenarium_frame_prepare(state); + + // Potentially pass the window closed event to the runtime + if (win32_window_event_flags & WindowEventFlag_CloseRequested) + { + Platform_Window_Event evt = { + WindowEvent_WindowClosed, + }; + lumenarium_event(evt, state); + } + + // Pass Window Events to the runtime + MSG window_msg; + while (PeekMessageA( + &window_msg, + win32_main_window.window_handle, + 0, + 0, + PM_REMOVE) + ){ + win32_window_handle_event(window_msg, &win32_main_window, state); + } + + lumenarium_frame(state); + + // Swap Render Buffers + SwapBuffers(win32_main_window.dc); + + //////////////////////////////////////// + // Maintain Frame Rate + + Platform_Ticks ticks_end = platform_get_ticks(); + r64 seconds_elapsed = get_seconds_elapsed(ticks_start, ticks_end); + while (seconds_elapsed < target_seconds_per_frame) + { + u32 sleep_time = (u32)(1000.0f * (target_seconds_per_frame - seconds_elapsed)); + Sleep(sleep_time); + + ticks_end = platform_get_ticks(); + seconds_elapsed = get_seconds_elapsed(ticks_start, ticks_end); + } + ticks_start = ticks_end; + } + + lumenarium_cleanup(state); + + + // threads cleanup + for (u32 i = 1; i < win32_threads_cap; i++) + { + if (win32_threads[i] == INVALID_HANDLE_VALUE) continue; + TerminateThread(win32_threads[i], 0); + } + + ExitProcess(0); +} + diff --git a/src_v2/platform/win32/lumenarium_win32_file.cpp b/src_v2/platform/win32/lumenarium_win32_file.cpp new file mode 100644 index 0000000..344e785 --- /dev/null +++ b/src_v2/platform/win32/lumenarium_win32_file.cpp @@ -0,0 +1,258 @@ +#define win32_open_files_cap 32 +char win32_open_file_paths[win32_open_files_cap][MAX_PATH]; +u64 win32_open_files_len = 1; // zero is invalid +HANDLE win32_open_files[win32_open_files_cap]; + +void +win32_files_init() +{ + for (u32 i = 0; i < win32_open_files_cap; i++) + { + win32_open_files[i] = INVALID_HANDLE_VALUE; + } +} + +HANDLE +win32_get_open_file_handle(Platform_File_Handle file) +{ + return win32_open_files[file.value]; +} + +internal u64 +win32_high_low_to_u64(u32 low_part, u32 high_part) +{ + ULARGE_INTEGER Time = {}; + Time.LowPart = low_part; + Time.HighPart = high_part; + u64 result = Time.QuadPart; + return result; +} + +internal u64 +win32_file_time_to_u64(FILETIME file_time) +{ + return win32_high_low_to_u64(file_time.dwLowDateTime, file_time.dwHighDateTime); +} + +Platform_File_Handle +platform_file_open(String path, Platform_File_Access_Flags flags_access, Platform_File_Create_Flags flags_create) +{ + Platform_File_Handle result = {}; + + DWORD flags_create_ = OPEN_EXISTING; + + HANDLE file_handle = CreateFileA( + (char*)path.str, + (DWORD)flags_access, + 0, // share mode + NULL, // security attributes + (DWORD)flags_create, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + + if (file_handle != INVALID_HANDLE_VALUE) + { + if (win32_open_files_len < win32_open_files_cap) + { + result.value = win32_open_files_len++; + } + else + { + // search for emtpy index + for (u32 i = 1; i < win32_open_files_cap; i++) + { + if (win32_open_files[i] == INVALID_HANDLE_VALUE) + { + result.value = i; + } + } + } + + assert(result.value != 0); + win32_open_files[result.value] = file_handle; + + memory_copy(path.str, (u8*)win32_open_file_paths[result.value], path.len); + win32_open_file_paths[result.value][path.len] = 0; // null term + } + else + { + win32_get_last_error(); + } + + return result; +} + +void +platform_file_close(Platform_File_Handle file_handle) +{ + assert(file_handle.value < win32_open_files_len); + CloseHandle(win32_get_open_file_handle(file_handle)); + win32_open_files[file_handle.value] = INVALID_HANDLE_VALUE; +} + +u64 +win32_get_file_size(HANDLE h) +{ + DWORD size_low, size_high; + size_low = GetFileSize(h, &size_high); + if (size_low == INVALID_FILE_SIZE) + { + win32_get_last_error(); + return 0; + } + LARGE_INTEGER win32_size; + win32_size.LowPart = size_low; + win32_size.HighPart = size_high; + return (u64)win32_size.QuadPart; +} + +Platform_File_Info +platform_file_get_info(Platform_File_Handle file_handle, Allocator* allocator) +{ + Platform_File_Info result = {}; + HANDLE h = win32_get_open_file_handle(file_handle); + if (h == INVALID_HANDLE_VALUE) return result; + + // File Size + u64 win32_size = win32_get_file_size(h); + if (win32_size == 0 && win32_last_error != 0) return result; + + // File Times + FILETIME time_created, time_last_write; + if (!GetFileTime(h, &time_created, (LPFILETIME)0, &time_last_write)) + { + win32_get_last_error(); + return result; + } + + // File Path + // call GetFullPathName with empty dest just to get the length needed + DWORD file_name_len_needed = GetFullPathName( + win32_open_file_paths[file_handle.value], 0, 0, 0 + ); + if (!file_name_len_needed) + { + win32_get_last_error(); + return result; + } + + result.path = allocator_alloc_string(allocator, (u64)file_name_len_needed); + result.path.len = (u64)GetFullPathName( + win32_open_file_paths[file_handle.value], + (DWORD)result.path.cap, + (char*)result.path.str, + 0 + ); + result.path_abs = result.path; + + // File Attributes + DWORD file_attrs = GetFileAttributesA((char*)result.path.str); + if (!file_attrs) + { + win32_get_last_error(); + return result; + } + + result.size = win32_size; + result.time_created = win32_file_time_to_u64(time_created); + result.time_last_write = win32_file_time_to_u64(time_last_write); + + if (has_flag(file_attrs, FILE_ATTRIBUTE_DIRECTORY)) + { + add_flag(result.flags, FileFlag_IsDir); + } + + return result; +} + +Data +platform_file_read_all(Platform_File_Handle file_handle, Allocator* allocator) +{ + Data result = {}; + + HANDLE h = win32_get_open_file_handle(file_handle); + if (h == INVALID_HANDLE_VALUE) return result; + + u64 file_size = win32_get_file_size(h); + if (file_size == 0 && win32_last_error != 0) return result; + + result.base = allocator_alloc(allocator, file_size + 1); + result.size = file_size + 1; + + DWORD bytes_read = 0; + if (ReadFile(h, (void*)result.base, (DWORD)result.size, &bytes_read, NULL)) + { + result.base[result.size - 1] = 0; + } + else + { + win32_get_last_error(); + } + + return result; +} + +bool +platform_file_write_all(Platform_File_Handle file_handle, Data file_data) +{ + bool result = false; + + HANDLE h = win32_get_open_file_handle(file_handle); + if (h == INVALID_HANDLE_VALUE) return result; + + // Set file pointer to beginning + SetFilePointer(h, 0, 0, FILE_BEGIN); + + DWORD bytes_written = 0; + if (WriteFile(h, file_data.base, (DWORD)file_data.size, &bytes_written, NULL)) + { + result = (bytes_written == file_data.size); + } + + return result; +} + +void +platform_dir_enum_(String path, Platform_Enum_Dir_Flags flags, Allocator* allocator, Platform_File_Info_List* list) +{ + WIN32_FIND_DATA ffd; + HANDLE search_handle = FindFirstFile((char*)path.str, &ffd); + if (search_handle != INVALID_HANDLE_VALUE) + { + do + { + Platform_File_Info_List_Ele* ele = allocator_alloc_struct( + allocator, Platform_File_Info_List_Ele + ); + + sll_push(list->first, list->last, ele); + } while (FindNextFile(search_handle, &ffd)); + } +} + +Platform_File_Info_List +platform_dir_enum(String path, Platform_Enum_Dir_Flags flags, Allocator* allocator) +{ + Platform_File_Info_List result = {}; + platform_dir_enum_(path, flags, allocator, &result); + return result; +} + +String +platform_get_exe_path(Allocator* allocator) +{ + String result = allocator_alloc_string(allocator, (u64)MAX_PATH); + result.len = (u64)GetModuleFileName(NULL, (char*)result.str, (DWORD)result.cap); + if (!result.len) return result; + + return result; +} + +bool +platform_pwd_set(String path) +{ + bool result = SetCurrentDirectory((char*)path.str); + if (!result) win32_get_last_error(); + return result; +} diff --git a/src_v2/platform/win32/lumenarium_win32_memory.cpp b/src_v2/platform/win32/lumenarium_win32_memory.cpp new file mode 100644 index 0000000..8a82089 --- /dev/null +++ b/src_v2/platform/win32/lumenarium_win32_memory.cpp @@ -0,0 +1,43 @@ +#define WIN32_PAGE_SIZE KB(4) + +u64 platform_page_size() { return WIN32_PAGE_SIZE; } + +u8* +platform_mem_reserve(u64 size) +{ + size_t size_cvt = (size_t)size_to_pages(size); + DWORD alloc_type = MEM_RESERVE; + DWORD protection = PAGE_READWRITE; + u8* result = (u8*)VirtualAlloc(0, size_cvt, alloc_type, protection); + if (!result) win32_get_last_error(); + return result; +} + +u8* +platform_mem_commit(u8* base, u64 size) +{ + size_t size_cvt = (size_t)size_to_pages(size); + DWORD alloc_type = MEM_COMMIT; + DWORD protection = PAGE_READWRITE; + u8* result = (u8*)VirtualAlloc(base, size_cvt, alloc_type, protection); + if (!result) win32_get_last_error(); + return result; +} + +bool +platform_mem_decommit(u8* base, u64 size) +{ + DWORD free_type = MEM_DECOMMIT; + bool result = VirtualFree(base, (size_t)size, free_type); + if (!result) win32_get_last_error(); + return result; +} + +bool +platform_mem_release(u8* base, u64 size) +{ + DWORD free_type = MEM_RELEASE; + bool result = VirtualFree(base, size, free_type); + if (!result) win32_get_last_error(); + return result; +} diff --git a/src_v2/platform/win32/lumenarium_win32_thread.cpp b/src_v2/platform/win32/lumenarium_win32_thread.cpp new file mode 100644 index 0000000..2285b4c --- /dev/null +++ b/src_v2/platform/win32/lumenarium_win32_thread.cpp @@ -0,0 +1,93 @@ + +#define win32_threads_cap 9 +global u32 win32_threads_len = 1; +global HANDLE win32_threads[win32_threads_cap]; +global Platform_Thread_Data win32_threads_data[win32_threads_cap]; + +DWORD WINAPI +win32_thread_wrapper(void* d) +{ + Platform_Thread_Result result = {}; + Platform_Thread_Data* thread_data = (Platform_Thread_Data*)d; + thread_data->thread_id = GetCurrentThreadId(); + if (thread_data->thread_proc) + { + result = thread_data->thread_proc(thread_data); + } + return result.code; +} + +void +win32_threads_init() +{ + for (u32 i = 1; i < win32_threads_cap; i++) win32_threads[i] = INVALID_HANDLE_VALUE; +} + +void +win32_threads_reclaim() +{ + u32 highest_valid = 0; + for (u32 i = 1; i < win32_threads_cap; i++) + { + if (win32_threads[i] != INVALID_HANDLE_VALUE) + { + highest_valid = i; + } + } + + win32_threads_len = highest_valid + 1; +} + +Platform_Thread_Handle +platform_thread_begin(Platform_Thread_Proc* proc, u8* user_data) +{ + Platform_Thread_Handle result = {}; + if (win32_threads_len < win32_threads_cap) + { + result.value = win32_threads_len++; + } + else + { + for (u32 i = 1; i < win32_threads_cap; i++) + { + if (win32_threads[i] == INVALID_HANDLE_VALUE) + { + result.value = i; + break; + } + } + } + assert(result.value != 0); + + Platform_Thread_Data* thread_data = &win32_threads_data[result.value]; + thread_data->thread_handle = result; + thread_data->thread_proc = proc; + thread_data->user_data = user_data; + + HANDLE thread_handle = CreateThread( + 0, 0, win32_thread_wrapper, (void*)thread_data, 0, 0 + ); + win32_threads[result.value] = thread_handle; + + return result; +} + +void +platform_thread_end(Platform_Thread_Handle thread) +{ + HANDLE thread_handle = win32_threads[thread.value]; + TerminateThread(thread_handle, 0); + win32_threads[thread.value] = INVALID_HANDLE_VALUE; +} + +u32 +platform_interlocked_increment(volatile u32* value) +{ + return InterlockedIncrement((LONG volatile*)value); +} + +u32 +platform_interlocked_cmp_exchg(volatile u32* dest, u32 new_value, u32 old_value) +{ + return InterlockedCompareExchange((LONG volatile*)dest, new_value, old_value); +} diff --git a/src_v2/platform/win32/lumenarium_win32_time.cpp b/src_v2/platform/win32/lumenarium_win32_time.cpp new file mode 100644 index 0000000..6bd090c --- /dev/null +++ b/src_v2/platform/win32/lumenarium_win32_time.cpp @@ -0,0 +1,48 @@ + +// set by calling win32_get_performance_frequency() +global s64 win32_performance_counter_freq_s64 = 0; +global r64 win32_performance_counter_freq_r64 = 0; + +s64 +win32_get_performance_frequency() +{ + LARGE_INTEGER freq; + if (!QueryPerformanceFrequency(&freq)) + { + win32_get_last_error(); + // TODO(Peter): I'm waiting to see an error actually occur here + // to know what it could possibly be. + invalid_code_path; + } + return (s64)freq.QuadPart; +} + +void +win32_time_init() +{ + win32_performance_counter_freq_s64 = win32_get_performance_frequency(); + win32_performance_counter_freq_r64 = (r64)win32_performance_counter_freq_s64; +} + +Platform_Ticks +platform_get_ticks() +{ + Platform_Ticks result = {}; + LARGE_INTEGER time; + if (!QueryPerformanceCounter(&time)) + { + win32_get_last_error(); + // TODO(Peter): I'm waiting to see an error actually occur here + // to know what it could possibly be. + invalid_code_path; + } + result.value = (s64)time.QuadPart; + return result; +} + +r64 +platform_ticks_to_seconds(Platform_Ticks ticks) +{ + r64 result = (r64)ticks.value / win32_performance_counter_freq_r64; + return result; +} \ No newline at end of file diff --git a/src_v2/platform/win32/lumenarium_win32_window.cpp b/src_v2/platform/win32/lumenarium_win32_window.cpp new file mode 100644 index 0000000..2241d0e --- /dev/null +++ b/src_v2/platform/win32/lumenarium_win32_window.cpp @@ -0,0 +1,203 @@ +typedef u32 Win32_Window_Event_Flags; +enum +{ + WindowEventFlag_None = 0, + WindowEventFlag_CloseRequested = 1, + WindowEventFlag_WindowIsActive = 2, +}; + +struct Win32_Window_OpenGL_Info +{ + BYTE bits_color; + BYTE bits_alpha; + BYTE bits_depth; + + HGLRC rc; +}; + +struct Win32_Window +{ + char* name; + char* class_name; + s32 width; + s32 height; + + WNDCLASS window_class; + WNDPROC window_event_handler; + HWND window_handle; + HDC dc; + + Win32_Window_OpenGL_Info opengl_info; +}; + +////////////////////////////////////////// +// Main Window +// +// At the moment, we only need one window, so this is easier to +// track globally. Replace this if we need more windows + +global Win32_Window_Event_Flags win32_window_event_flags = 0; +global Win32_Window win32_main_window = {}; + +////////////////////////////////////////// +// + +internal Win32_Window +win32_window_create( + HINSTANCE hinstance, + char* window_name, + s32 width, + s32 height, + WNDPROC window_event_handler + ) +{ + Win32_Window result = {}; + result.name = window_name; + result.class_name = window_name; + result.width = width; + result.height = height; + result.window_event_handler = window_event_handler; + + result.window_class = {}; + result.window_class.style = CS_HREDRAW | CS_VREDRAW; + result.window_class.lpfnWndProc = window_event_handler; + result.window_class.hInstance = hinstance; + result.window_class.lpszClassName = window_name; + + if (RegisterClass(&result.window_class)) + { + result.window_handle = CreateWindowEx( + 0, + result.window_class.lpszClassName, + window_name, + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, + CW_USEDEFAULT, + width, + height, + 0, + 0, + hinstance, + 0 + ); + result.dc = GetDC(result.window_handle); + } + + return result; +} + +internal void +win32_window_update_dim(Win32_Window* win) +{ + RECT client_rect; + GetClientRect(win->window_handle, &client_rect); + win->width = client_rect.right - client_rect.left; + win->height = client_rect.bottom - client_rect.top; +} + +LRESULT CALLBACK +win32_window_event_handler(HWND window_handle, UINT msg, WPARAM wparam, LPARAM lparam) +{ + LRESULT result = 0; + + switch (msg) + { + case WM_SIZE: + { + win32_window_update_dim(&win32_main_window); + }break; + + case WM_CLOSE: + { + result = DefWindowProc(window_handle, msg, wparam, lparam); + add_flag(win32_window_event_flags, WindowEventFlag_CloseRequested); + }break; + + case WM_DESTROY: + { + }break; + + case WM_PAINT: + { + PAINTSTRUCT paint_struct; + HDC device_ctx; + b32 paint_result; + + device_ctx = BeginPaint(window_handle, &paint_struct); + paint_result = EndPaint(window_handle, &paint_struct); + }break; + + case WM_ACTIVATE: + { + bool WindowIsActive = ( + LOWORD(wparam) == WA_ACTIVE || LOWORD(wparam) == WA_CLICKACTIVE + ); + if (WindowIsActive) + { + add_flag(win32_window_event_flags, WindowEventFlag_WindowIsActive); + } + else + { + rem_flag(win32_window_event_flags, WindowEventFlag_WindowIsActive); + } + }break; + + default: + { + result = DefWindowProc(window_handle, msg, wparam, lparam); + } + } + + return result; +} + +internal void +win32_window_opengl_ctx_create(Win32_Window* win, Win32_Window_OpenGL_Info info) +{ + // Setup pixel format + { + PIXELFORMATDESCRIPTOR pixel_format_desc = { 0 }; + // TODO: Program seems to work perfectly fine without all other params except dwFlags. + // Can we skip other params for the sake of brevity? + pixel_format_desc.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pixel_format_desc.nVersion = 1; + pixel_format_desc.dwFlags = ( + PFD_SUPPORT_OPENGL | + PFD_DRAW_TO_WINDOW | + PFD_DOUBLEBUFFER + ); + pixel_format_desc.cColorBits = info.bits_color; + pixel_format_desc.cAlphaBits = info.bits_alpha; + pixel_format_desc.cDepthBits = info.bits_depth; + + // TODO(Peter): include these in win32_opengl_window_info? + pixel_format_desc.iPixelType = PFD_TYPE_RGBA; + pixel_format_desc.dwLayerMask = PFD_MAIN_PLANE; + + s32 pixel_fmt = ChoosePixelFormat(win->dc, &pixel_format_desc); + if (!pixel_fmt) { invalid_code_path; } + if (!SetPixelFormat(win->dc, pixel_fmt, &pixel_format_desc)) + { + invalid_code_path; + } + } + + // Create rendering context + { + // TODO: Create "proper" context? + // https://www.opengl.org/wiki/Creating_an_OpenGL_Context_(WGL)#Proper_Context_Creation + + info.rc = wglCreateContext(win->dc); + wglMakeCurrent(win->dc, info.rc); + + // TODO(Peter): do we want this? + /* + glGetIntegerv(GL_MAJOR_VERSION, ); + glGetIntegerv(GL_MINOR_VERSION, ); + (char*)glGetString(GL_VENDOR); + (char*)glGetString(GL_RENDERER); + */ + } + + win->opengl_info = info; +}