From 64879c0e2dc6b95c46aa4dfb6259b4f23731f105 Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 28 Feb 2021 15:47:05 -0800 Subject: [PATCH 001/151] cleanup --- README.md | 2 +- src/app/engine/assembly/foldhaus_assembly_debug.h | 5 ----- src/app/foldhaus_app.cpp | 3 ++- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ddbba99..46e3eee 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Windows - Debug 1. Just run `win32_msvc\debug\win32_foldhaus.exe` ## Debug Lumenarium -###Windows +### Windows Building in debug mode outputs pdb file info that can be read by Visual Studio or RemedyBG (preferred debugging solution, but ymmv). You can just open the exe in either application and it'll find the pdb file in the same directory ## What Is Lumenarium? diff --git a/src/app/engine/assembly/foldhaus_assembly_debug.h b/src/app/engine/assembly/foldhaus_assembly_debug.h index 07a3685..2bf6f40 100644 --- a/src/app/engine/assembly/foldhaus_assembly_debug.h +++ b/src/app/engine/assembly/foldhaus_assembly_debug.h @@ -201,11 +201,6 @@ AssemblyDebug_OverrideOutput(assembly_debug_state State, assembly_array Assembli InvalidDefaultCase; } - - if (State.Override ) - { - - } } diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 47590c0..13f5dc5 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -57,12 +57,13 @@ INITIALIZE_APPLICATION(InitializeApplication) State->LedSystem = LedSystem_Create(Context.ThreadContext.Allocator, 128); State->AssemblyDebugState = AssemblyDebug_Create(&State->Permanent); State->AssemblyDebugState.Brightness = 255; - State->AssemblyDebugState.Override = ADS_Override_AllRed; + State->AssemblyDebugState.Override = ADS_Override_AllBlue; GlobalDebugServices->Interface.RenderSculpture = true; PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); { + // NOTE(pjs): This just sets up the default panel layout panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context); SplitPanel(RootPanel, .25f, PanelSplit_Horizontal, &State->PanelSystem, State, Context); From f8d0f904ce9600e69490019553758f7c32d54723 Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 28 Feb 2021 16:58:22 -0800 Subject: [PATCH 002/151] Blumen lumen now reads packet headers before attempting to interpret them --- src/app/blumen_lumen.cpp | 60 ++++++++++++++++++++++++++++------------ src/app/blumen_lumen.h | 19 +++++++++++-- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/src/app/blumen_lumen.cpp b/src/app/blumen_lumen.cpp index 323b8ae..c34e4e7 100644 --- a/src/app/blumen_lumen.cpp +++ b/src/app/blumen_lumen.cpp @@ -5,6 +5,7 @@ // #ifndef BLUMEN_LUMEN_CPP + internal void BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) { @@ -192,25 +193,48 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) while (BLState->MicPacketBuffer.ReadHead != BLState->MicPacketBuffer.WriteHead) { gs_data PacketData = BLState->MicPacketBuffer.Values[BLState->MicPacketBuffer.ReadHead++]; - microphone_packet Packet = *(microphone_packet*)PacketData.Memory; - u32 NameLen = CStringLength(Packet.AnimationFileName); - if (StringEqualsCharArray(BlueString.ConstString, Packet.AnimationFileName, NameLen)) - { - State->AnimationSystem.ActiveAnimationIndex = 0; - } - else if (StringEqualsCharArray(GreenString.ConstString, Packet.AnimationFileName, NameLen)) - { - State->AnimationSystem.ActiveAnimationIndex = 1; - } - else if (StringEqualsCharArray(ILoveYouString.ConstString, Packet.AnimationFileName, NameLen)) - { - State->AnimationSystem.ActiveAnimationIndex = 2; - } - - if (BLState->MicPacketBuffer.ReadHead >= PACKETS_MAX) - { - BLState->MicPacketBuffer.ReadHead = 0; + u8 PacketType = PacketData.Memory[0]; + switch (PacketType) { + case PacketType_PatternCommand: + { + microphone_packet Packet = *(microphone_packet*)(PacketData.Memory + 1); + + u32 NameLen = CStringLength(Packet.AnimationFileName); + if (StringEqualsCharArray(BlueString.ConstString, Packet.AnimationFileName, NameLen)) + { + State->AnimationSystem.ActiveAnimationIndex = 0; + } + else if (StringEqualsCharArray(GreenString.ConstString, Packet.AnimationFileName, NameLen)) + { + State->AnimationSystem.ActiveAnimationIndex = 1; + } + else if (StringEqualsCharArray(ILoveYouString.ConstString, Packet.AnimationFileName, NameLen)) + { + State->AnimationSystem.ActiveAnimationIndex = 2; + } + + if (BLState->MicPacketBuffer.ReadHead >= PACKETS_MAX) + { + BLState->MicPacketBuffer.ReadHead = 0; + } + + OutputDebugStringA("Received Pattern Packet"); + }break; + + case PacketType_MotorState: + { + motor_packet Packet = *(motor_packet*)(PacketData.Memory + 1); + OutputDebugStringA("Received Motor Packet"); + }break; + + case PacketType_Temperature: + { + temp_packet Packet = *(temp_packet*)(PacketData.Memory + 1); + OutputDebugStringA("Received Temperature Packet"); + }break; + + InvalidDefaultCase; } } diff --git a/src/app/blumen_lumen.h b/src/app/blumen_lumen.h index 0ea9db5..61e6026 100644 --- a/src/app/blumen_lumen.h +++ b/src/app/blumen_lumen.h @@ -5,13 +5,21 @@ // #ifndef BLUMEN_LUMEN_H +enum bl_python_packet_type +{ + PacketType_Invalid = 0, + PacketType_PatternCommand = 1, + PacketType_MotorState = 2, + PacketType_Temperature = 3, +}; + +#pragma pack(push, 1) typedef struct motor_packet { u8 FlowerPositions[3]; } motor_packet; -#pragma pack(push, 1) -struct microphone_packet +typedef struct microphone_packet { b8 ChangeAnimation; char AnimationFileName[32]; @@ -21,7 +29,12 @@ struct microphone_packet b8 SetLayerParamColor; char LayerParamColor[7]; r32 OverrideDuration; -}; +} microphone_packet; + +typedef struct temp_packet +{ + s8 Temperature; +} temp_packet; #pragma pack(pop) #define BLUMEN_MESSAGE_QUEUE_COUNT 32 From c68cb80ca48318b2fb0b74945c96acafb8d36a44 Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 28 Feb 2021 17:13:51 -0800 Subject: [PATCH 003/151] Printing out based on messages received --- src/app/blumen_lumen.cpp | 36 ++++++++++++++++++++++++++---------- src/app/blumen_lumen.h | 3 ++- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/app/blumen_lumen.cpp b/src/app/blumen_lumen.cpp index c34e4e7..68aa25a 100644 --- a/src/app/blumen_lumen.cpp +++ b/src/app/blumen_lumen.cpp @@ -56,6 +56,8 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) u32 Port = WeathermanPort; s32 Flags = 0; SocketSend(Data->SocketManager, Data->ListenSocket, Address, Port, Msg, Flags); + + OutputDebugString("Sending Motor Packet\n"); } } } @@ -124,7 +126,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; BLState->MicListenJobData.ListenSocket = CreateSocket(Context.SocketManager, "127.0.0.1", "20185"); - //BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); + BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); gs_const_string SculpturePath = ConstString("data/test_blumen.fold"); LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); @@ -214,31 +216,45 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) State->AnimationSystem.ActiveAnimationIndex = 2; } - if (BLState->MicPacketBuffer.ReadHead >= PACKETS_MAX) - { - BLState->MicPacketBuffer.ReadHead = 0; - } - - OutputDebugStringA("Received Pattern Packet"); + OutputDebugStringA("Received Pattern Packet\n"); }break; case PacketType_MotorState: { motor_packet Packet = *(motor_packet*)(PacketData.Memory + 1); - OutputDebugStringA("Received Motor Packet"); + BLState->LastKnownMotorState = Packet; + + gs_string Temp = PushStringF(State->Transient, 256, "Received Motor States: %d %d %d\n", + Packet.FlowerPositions[0], + Packet.FlowerPositions[1], + Packet.FlowerPositions[2]); + NullTerminate(&Temp); + + OutputDebugStringA(Temp.Str); }break; case PacketType_Temperature: { temp_packet Packet = *(temp_packet*)(PacketData.Memory + 1); - OutputDebugStringA("Received Temperature Packet"); + + gs_string Temp = PushStringF(State->Transient, 256, "Temperature: %d\n", + Packet.Temperature); + NullTerminate(&Temp); + + OutputDebugStringA(Temp.Str); }break; InvalidDefaultCase; } + + + if (BLState->MicPacketBuffer.ReadHead >= PACKETS_MAX) + { + BLState->MicPacketBuffer.ReadHead = 0; + } } - if (MotorTimeElapsed > 60) + if (false && MotorTimeElapsed > 0) { // NOTE(pjs): MotorTimeElapsed = 0; diff --git a/src/app/blumen_lumen.h b/src/app/blumen_lumen.h index 61e6026..2c47b22 100644 --- a/src/app/blumen_lumen.h +++ b/src/app/blumen_lumen.h @@ -66,9 +66,10 @@ struct blumen_lumen_state temp_job_req JobReq; - platform_thread_handle MicListenThread; mic_listen_job_data MicListenJobData; + + motor_packet LastKnownMotorState; }; From b91a7a9a0216e415355e5ed8ba2fb3bcd0be8f57 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sat, 6 Mar 2021 18:17:00 -0800 Subject: [PATCH 004/151] sanity tests, path handling, file lister update and began working on saving files --- build/build_app_msvc_win32_debug.bat | 5 ++ src/app/blumen_lumen.cpp | 2 + .../foldhaus_panel_animation_timeline.h | 33 ++++++++- .../editor/panels/foldhaus_panel_file_view.h | 68 +++++++++++++------ .../editor/panels/foldhaus_panel_hierarchy.h | 2 +- src/app/foldhaus_app.cpp | 4 +- src/app/foldhaus_platform.h | 1 + src/app/platform_win32/win32_foldhaus.cpp | 9 ++- .../platform_win32/win32_foldhaus_fileio.h | 46 ++++++++----- .../platform_win32/win32_foldhaus_serial.h | 5 ++ src/sculpture_gen/gen_blumen_lumen.cpp | 4 +- 11 files changed, 132 insertions(+), 47 deletions(-) diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index d69d5db..663ae28 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -34,6 +34,11 @@ cl %CommonCompilerFlags% %ProjectDevPath%\src\serial_monitor\first.cpp /Feserial cl %CommonCompilerFlags% %ProjectDevPath%\src\sculpture_gen\gen_blumen_lumen.cpp /Fegen_blumen_lumen.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib +REM COMPILE AND RUN TESTS +cl %CommonCompilerFlags% %ProjectDevPath%\src\tests\sanity_tests.cpp /Fesanity_tests.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib + +sanity_tests.exe + popd call %MyPath%\_postbuild_win32.bat \ No newline at end of file diff --git a/src/app/blumen_lumen.cpp b/src/app/blumen_lumen.cpp index 68aa25a..4f9791c 100644 --- a/src/app/blumen_lumen.cpp +++ b/src/app/blumen_lumen.cpp @@ -126,7 +126,9 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; BLState->MicListenJobData.ListenSocket = CreateSocket(Context.SocketManager, "127.0.0.1", "20185"); +#if 0 BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); +#endif gs_const_string SculpturePath = ConstString("data/test_blumen.fold"); LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 92ee12c..6464628 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -792,6 +792,29 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c } } +internal void +AnimInfoView_SaveAnimFile (gs_const_string Path, app_state* State, context Context) +{ + u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex; + animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex]; + + gs_string FileText = AnimSerializer_Serialize(ActiveAnimation, State->Patterns, State->Transient); + + if (!WriteEntireFile(Context.ThreadContext.FileHandler, Path, StringToData(FileText))) + { + InvalidCodePath; + } +} + +PANEL_MODAL_OVERRIDE_CALLBACK(AnimInfoView_SaveAnimFileCallback) +{ + Assert(ReturningFrom->TypeIndex == PanelType_FileView); + file_view_state* FileViewState = Panel_GetStateStruct(ReturningFrom, file_view_state); + gs_file_info FileInfo = FileViewState->SelectedFile; + + AnimInfoView_SaveAnimFile(FileInfo.Path, State, Context); +} + internal void AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { @@ -833,10 +856,14 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel // need to use the file browser to create a file u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex; animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex]; - gs_string FileText = AnimSerializer_Serialize(ActiveAnimation, State->Patterns, State->Transient); - if (WriteEntireFile(Context.ThreadContext.FileHandler, ActiveAnimation.FileInfo.Path, StringToData(FileText))) + + if (!ActiveAnimation.FileInfo.Path.Str) { - InvalidCodePath; + panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context); + FileView_SetMode(FileBrowser, FileViewMode_Save); + Panel_PushModalOverride(Panel, FileBrowser, AnimInfoView_SaveAnimFileCallback); + } else { + AnimInfoView_SaveAnimFile(ActiveAnimation.FileInfo.Path, State, Context); } } if (ui_Button(Interface, MakeString("Load"))) diff --git a/src/app/editor/panels/foldhaus_panel_file_view.h b/src/app/editor/panels/foldhaus_panel_file_view.h index eaf5013..aee1c19 100644 --- a/src/app/editor/panels/foldhaus_panel_file_view.h +++ b/src/app/editor/panels/foldhaus_panel_file_view.h @@ -49,30 +49,31 @@ s32 FileView_CommandsCount = 0; internal void FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_state* State, context Context) { + // NOTE(pjs): make sure we're only passing valid directory paths to the + // function + char LastChar = WorkingDirectory.Str[WorkingDirectory.Length - 1]; + Assert(LastChar == '\\' || LastChar == '/'); ClearArena(&State->FileNamesArena); - gs_const_string SanitizedDirectory = WorkingDirectory; - - u32 LastSlashIndex = FindLast(SanitizedDirectory, '\\'); - gs_const_string LastDir = Substring(SanitizedDirectory, LastSlashIndex + 1, SanitizedDirectory.Length); - if (StringsEqual(LastDir, ConstString(".."))) + gs_string SanitizedDir = PushString(Context.ThreadContext.Transient, WorkingDirectory.Length + 2); + SanitizePath(WorkingDirectory, &SanitizedDir, Context.ThreadContext.Transient); + if (SanitizedDir.Str[SanitizedDir.Length - 1] != '\\') { - u32 SecondLastSlashIndex = FindLast(SanitizedDirectory, LastSlashIndex - 1, '\\'); - SanitizedDirectory = Substring(SanitizedDirectory, 0, SecondLastSlashIndex); - } - else if (StringsEqual(LastDir, ConstString(".")) && LastDir.Length > 1) - { - SanitizedDirectory = Substring(SanitizedDirectory, 0, LastSlashIndex); + AppendPrintF(&SanitizedDir, "\\"); } - gs_file_info NewWorkingDirectory = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDirectory); - if (NewWorkingDirectory.IsDirectory) + gs_file_info NewWorkingDir = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDir.ConstString); + if (NewWorkingDir.IsDirectory) { + AppendPrintF(&SanitizedDir, "*"); + NullTerminate(&SanitizedDir); + + State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, SanitizedDir.ConstString, EnumerateDirectory_IncludeDirectories); + // NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory // in some cases. Shouldn't be a problem but it is unnecessary - PrintF(&State->WorkingDirectory, "%S", WorkingDirectory); + PrintF(&State->WorkingDirectory, "%S", SanitizedDir.ConstString); - State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, State->WorkingDirectory.ConstString, EnumerateDirectory_IncludeDirectories); } } @@ -88,7 +89,7 @@ FileView_Init(panel* Panel, app_state* State, context Context) // TODO(pjs): this shouldn't be stored in permanent FileViewState->WorkingDirectory = PushString(&State->Permanent, 256); - FileView_UpdateWorkingDirectory(ConstString("."), FileViewState, Context); + FileView_UpdateWorkingDirectory(ConstString(".\\"), FileViewState, Context); } GSMetaTag(panel_cleanup); @@ -105,14 +106,26 @@ internal void FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state); - Assert(FileViewState->Mode == FileViewMode_Save); ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout")); { - if (ui_Button(&State->Interface, MakeString("Exit"))) + + ui_BeginRow(&State->Interface, 3); { - FileView_Exit_(Panel, State, Context); + if (FileViewState->Mode == FileViewMode_Save) + { + if (ui_Button(&State->Interface, MakeString("Save"))) + { + FileView_Exit_(Panel, State, Context); + } + } + + if (ui_Button(&State->Interface, MakeString("Exit"))) + { + FileView_Exit_(Panel, State, Context); + } } + ui_EndRow(&State->Interface); // Header if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->WorkingDirectory)) @@ -137,7 +150,7 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu { gs_file_info File = FileViewState->FileNames.Values[i]; - u32 LastSlashIndex = FindLast(File.Path, '\\'); + u32 LastSlashIndex = FindLast(File.Path, File.Path.Length - 2, '\\'); gs_const_string FileName = Substring(File.Path, LastSlashIndex + 1, File.Path.Length); gs_string PathString = PushString(State->Transient, FileName.Length); PrintF(&PathString, "%S", FileName); @@ -150,8 +163,19 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu } else { - FileViewState->SelectedFile = File; - FileView_Exit_(Panel, State, Context); + switch (FileViewState->Mode) + { + FileViewState->SelectedFile = File; + case FileViewMode_Load: + { + FileView_Exit_(Panel, State, Context); + } break; + + case FileViewMode_Save: + { + + } break; + } } } } diff --git a/src/app/editor/panels/foldhaus_panel_hierarchy.h b/src/app/editor/panels/foldhaus_panel_hierarchy.h index 13d45fe..7979512 100644 --- a/src/app/editor/panels/foldhaus_panel_hierarchy.h +++ b/src/app/editor/panels/foldhaus_panel_hierarchy.h @@ -69,7 +69,7 @@ HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren if (ui_Button(&State->Interface, MakeString("+ Add Assembly"))) { panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context); - FileView_SetMode(FileBrowser, FileViewMode_Save); + FileView_SetMode(FileBrowser, FileViewMode_Load); Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback); } ui_EndRow(&State->Interface); diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 13f5dc5..9112a93 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -57,7 +57,7 @@ INITIALIZE_APPLICATION(InitializeApplication) State->LedSystem = LedSystem_Create(Context.ThreadContext.Allocator, 128); State->AssemblyDebugState = AssemblyDebug_Create(&State->Permanent); State->AssemblyDebugState.Brightness = 255; - State->AssemblyDebugState.Override = ADS_Override_AllBlue; + State->AssemblyDebugState.Override = ADS_Override_None; GlobalDebugServices->Interface.RenderSculpture = true; @@ -80,7 +80,7 @@ INITIALIZE_APPLICATION(InitializeApplication) Panel_SetType(Profiler, &State->PanelSystem, PanelType_ProfilerView, State, Context); panel* Hierarchy = LeftPanel->Left; - Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_HierarchyView, State, Context); + Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, Context); } diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 7f10ab0..35a539d 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -11,6 +11,7 @@ #include "..\gs_libs\gs_types.h" #include "..\gs_libs\gs_types.cpp" +#include "..\gs_libs\gs_path.h" struct handle { diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index df968c8..e959107 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -523,6 +523,8 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) return Result; } +#include "../../gs_libs/gs_path.h" + int WINAPI WinMain ( HINSTANCE HInstance, @@ -533,6 +535,11 @@ WinMain ( { gs_thread_context ThreadContext = Win32CreateThreadContext(); + gs_file_info A = GetFileInfo(ThreadContext.FileHandler, ConstString("C:\\projects\\Lumenarium")); + + gs_file_info B = GetFileInfo(ThreadContext.FileHandler, ConstString("C:\\projects\\Lumenarium\\")); + + if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); @@ -629,7 +636,7 @@ WinMain ( Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); - bool Multithread = false; + bool Multithread = true; if (Multithread) { for (addressed_data_buffer* At = OutputData.Root; diff --git a/src/app/platform_win32/win32_foldhaus_fileio.h b/src/app/platform_win32/win32_foldhaus_fileio.h index 95163ea..a4f3edf 100644 --- a/src/app/platform_win32/win32_foldhaus_fileio.h +++ b/src/app/platform_win32/win32_foldhaus_fileio.h @@ -178,28 +178,39 @@ Win32SetFileInfoFromFindFileData(gs_file_info* Info, WIN32_FIND_DATA FindFileDat { u32 FileNameLength = CharArrayLength(FindFileData.cFileName); - // 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 + 1); - PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName); - NullTerminate(&FileName); - Info->FileSize = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh); Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime); Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime); - Info->Path = FileName.ConstString; 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; - s64 IndexOfLastSlash = FindLastFromSet(Path, "\\/"); - Assert(IndexOfLastSlash >= 0); - gs_const_string SearchPath = Substring(Path, 0, IndexOfLastSlash + 1); + 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); @@ -211,15 +222,18 @@ Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* 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)) { - gs_const_string SubDirName = ConstString(FindFileData.cFileName); - if (!StringsEqual(SubDirName, ConstString(".")) && - !StringsEqual(SubDirName, ConstString(".."))) + 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); + FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString, + Storage, Flags); } } @@ -230,7 +244,7 @@ Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* { temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry); *File = {0}; - Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPath, Storage); + Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPathDir, Storage); SLLPushOrInit(TempList->First, TempList->Last, File); FilesCount += 1; } diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index 71b4c99..7f21539 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -146,6 +146,11 @@ Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer) // ie. the usb stick was removed }break; + case ERROR_ACCESS_DENIED: + { + // ?? + }break; + case ERROR_INVALID_HANDLE: InvalidDefaultCase; } diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index dce5c4a..d1f4462 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -191,7 +191,7 @@ int main(int ArgCount, char** Args) u32 BloomInnerChannels[] = { FSC(0, 0), FSC(0, 1), FSC(0, 2), FSC(0, 3), FSC(0, 4), FSC(0, 5) }; flower_desc F0 = {}; F0.Pos = v3{-1, 0, 0}; - F0.ComPort = "\\\\.\\COM4"; + F0.ComPort = "\\\\.\\COM11"; F0.FlowerTagValue = "left"; F0.StemChannels = StemChannels; F0.BloomOuterChannels = BloomOuterChannels; @@ -200,7 +200,7 @@ int main(int ArgCount, char** Args) flower_desc F1 = {}; F1.Pos = v3{0, 0, 0}; - F1.ComPort = "\\\\.\\COM5"; + F1.ComPort = "\\\\.\\COM12"; F1.FlowerTagValue = "center"; F1.StemChannels = StemChannels; F1.BloomInnerChannels = BloomInnerChannels; From bb9c9b3e261c09614b9f4947432b186927fa1fd4 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sat, 6 Mar 2021 18:17:00 -0800 Subject: [PATCH 005/151] sanity tests, path handling, file lister update and began working on saving files --- build/build_app_msvc_win32_debug.bat | 5 + src/app/blumen_lumen.cpp | 2 + .../foldhaus_panel_animation_timeline.h | 33 +++++- .../editor/panels/foldhaus_panel_file_view.h | 68 +++++++---- .../editor/panels/foldhaus_panel_hierarchy.h | 2 +- src/app/foldhaus_app.cpp | 4 +- src/app/foldhaus_platform.h | 1 + src/app/platform_win32/win32_foldhaus.cpp | 9 +- .../platform_win32/win32_foldhaus_fileio.h | 46 +++++--- .../platform_win32/win32_foldhaus_serial.h | 5 + src/gs_libs/gs_path.h | 106 ++++++++++++++++++ src/gs_libs/gs_tests.h | 72 ++++++++++++ src/sculpture_gen/gen_blumen_lumen.cpp | 4 +- src/tests/sanity_tests.cpp | 43 +++++++ 14 files changed, 353 insertions(+), 47 deletions(-) create mode 100644 src/gs_libs/gs_path.h create mode 100644 src/gs_libs/gs_tests.h create mode 100644 src/tests/sanity_tests.cpp diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index d69d5db..663ae28 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -34,6 +34,11 @@ cl %CommonCompilerFlags% %ProjectDevPath%\src\serial_monitor\first.cpp /Feserial cl %CommonCompilerFlags% %ProjectDevPath%\src\sculpture_gen\gen_blumen_lumen.cpp /Fegen_blumen_lumen.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib +REM COMPILE AND RUN TESTS +cl %CommonCompilerFlags% %ProjectDevPath%\src\tests\sanity_tests.cpp /Fesanity_tests.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib + +sanity_tests.exe + popd call %MyPath%\_postbuild_win32.bat \ No newline at end of file diff --git a/src/app/blumen_lumen.cpp b/src/app/blumen_lumen.cpp index 68aa25a..4f9791c 100644 --- a/src/app/blumen_lumen.cpp +++ b/src/app/blumen_lumen.cpp @@ -126,7 +126,9 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; BLState->MicListenJobData.ListenSocket = CreateSocket(Context.SocketManager, "127.0.0.1", "20185"); +#if 0 BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); +#endif gs_const_string SculpturePath = ConstString("data/test_blumen.fold"); LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 92ee12c..6464628 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -792,6 +792,29 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c } } +internal void +AnimInfoView_SaveAnimFile (gs_const_string Path, app_state* State, context Context) +{ + u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex; + animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex]; + + gs_string FileText = AnimSerializer_Serialize(ActiveAnimation, State->Patterns, State->Transient); + + if (!WriteEntireFile(Context.ThreadContext.FileHandler, Path, StringToData(FileText))) + { + InvalidCodePath; + } +} + +PANEL_MODAL_OVERRIDE_CALLBACK(AnimInfoView_SaveAnimFileCallback) +{ + Assert(ReturningFrom->TypeIndex == PanelType_FileView); + file_view_state* FileViewState = Panel_GetStateStruct(ReturningFrom, file_view_state); + gs_file_info FileInfo = FileViewState->SelectedFile; + + AnimInfoView_SaveAnimFile(FileInfo.Path, State, Context); +} + internal void AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { @@ -833,10 +856,14 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel // need to use the file browser to create a file u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex; animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex]; - gs_string FileText = AnimSerializer_Serialize(ActiveAnimation, State->Patterns, State->Transient); - if (WriteEntireFile(Context.ThreadContext.FileHandler, ActiveAnimation.FileInfo.Path, StringToData(FileText))) + + if (!ActiveAnimation.FileInfo.Path.Str) { - InvalidCodePath; + panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context); + FileView_SetMode(FileBrowser, FileViewMode_Save); + Panel_PushModalOverride(Panel, FileBrowser, AnimInfoView_SaveAnimFileCallback); + } else { + AnimInfoView_SaveAnimFile(ActiveAnimation.FileInfo.Path, State, Context); } } if (ui_Button(Interface, MakeString("Load"))) diff --git a/src/app/editor/panels/foldhaus_panel_file_view.h b/src/app/editor/panels/foldhaus_panel_file_view.h index eaf5013..aee1c19 100644 --- a/src/app/editor/panels/foldhaus_panel_file_view.h +++ b/src/app/editor/panels/foldhaus_panel_file_view.h @@ -49,30 +49,31 @@ s32 FileView_CommandsCount = 0; internal void FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_state* State, context Context) { + // NOTE(pjs): make sure we're only passing valid directory paths to the + // function + char LastChar = WorkingDirectory.Str[WorkingDirectory.Length - 1]; + Assert(LastChar == '\\' || LastChar == '/'); ClearArena(&State->FileNamesArena); - gs_const_string SanitizedDirectory = WorkingDirectory; - - u32 LastSlashIndex = FindLast(SanitizedDirectory, '\\'); - gs_const_string LastDir = Substring(SanitizedDirectory, LastSlashIndex + 1, SanitizedDirectory.Length); - if (StringsEqual(LastDir, ConstString(".."))) + gs_string SanitizedDir = PushString(Context.ThreadContext.Transient, WorkingDirectory.Length + 2); + SanitizePath(WorkingDirectory, &SanitizedDir, Context.ThreadContext.Transient); + if (SanitizedDir.Str[SanitizedDir.Length - 1] != '\\') { - u32 SecondLastSlashIndex = FindLast(SanitizedDirectory, LastSlashIndex - 1, '\\'); - SanitizedDirectory = Substring(SanitizedDirectory, 0, SecondLastSlashIndex); - } - else if (StringsEqual(LastDir, ConstString(".")) && LastDir.Length > 1) - { - SanitizedDirectory = Substring(SanitizedDirectory, 0, LastSlashIndex); + AppendPrintF(&SanitizedDir, "\\"); } - gs_file_info NewWorkingDirectory = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDirectory); - if (NewWorkingDirectory.IsDirectory) + gs_file_info NewWorkingDir = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDir.ConstString); + if (NewWorkingDir.IsDirectory) { + AppendPrintF(&SanitizedDir, "*"); + NullTerminate(&SanitizedDir); + + State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, SanitizedDir.ConstString, EnumerateDirectory_IncludeDirectories); + // NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory // in some cases. Shouldn't be a problem but it is unnecessary - PrintF(&State->WorkingDirectory, "%S", WorkingDirectory); + PrintF(&State->WorkingDirectory, "%S", SanitizedDir.ConstString); - State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, State->WorkingDirectory.ConstString, EnumerateDirectory_IncludeDirectories); } } @@ -88,7 +89,7 @@ FileView_Init(panel* Panel, app_state* State, context Context) // TODO(pjs): this shouldn't be stored in permanent FileViewState->WorkingDirectory = PushString(&State->Permanent, 256); - FileView_UpdateWorkingDirectory(ConstString("."), FileViewState, Context); + FileView_UpdateWorkingDirectory(ConstString(".\\"), FileViewState, Context); } GSMetaTag(panel_cleanup); @@ -105,14 +106,26 @@ internal void FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state); - Assert(FileViewState->Mode == FileViewMode_Save); ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout")); { - if (ui_Button(&State->Interface, MakeString("Exit"))) + + ui_BeginRow(&State->Interface, 3); { - FileView_Exit_(Panel, State, Context); + if (FileViewState->Mode == FileViewMode_Save) + { + if (ui_Button(&State->Interface, MakeString("Save"))) + { + FileView_Exit_(Panel, State, Context); + } + } + + if (ui_Button(&State->Interface, MakeString("Exit"))) + { + FileView_Exit_(Panel, State, Context); + } } + ui_EndRow(&State->Interface); // Header if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->WorkingDirectory)) @@ -137,7 +150,7 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu { gs_file_info File = FileViewState->FileNames.Values[i]; - u32 LastSlashIndex = FindLast(File.Path, '\\'); + u32 LastSlashIndex = FindLast(File.Path, File.Path.Length - 2, '\\'); gs_const_string FileName = Substring(File.Path, LastSlashIndex + 1, File.Path.Length); gs_string PathString = PushString(State->Transient, FileName.Length); PrintF(&PathString, "%S", FileName); @@ -150,8 +163,19 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu } else { - FileViewState->SelectedFile = File; - FileView_Exit_(Panel, State, Context); + switch (FileViewState->Mode) + { + FileViewState->SelectedFile = File; + case FileViewMode_Load: + { + FileView_Exit_(Panel, State, Context); + } break; + + case FileViewMode_Save: + { + + } break; + } } } } diff --git a/src/app/editor/panels/foldhaus_panel_hierarchy.h b/src/app/editor/panels/foldhaus_panel_hierarchy.h index 13d45fe..7979512 100644 --- a/src/app/editor/panels/foldhaus_panel_hierarchy.h +++ b/src/app/editor/panels/foldhaus_panel_hierarchy.h @@ -69,7 +69,7 @@ HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren if (ui_Button(&State->Interface, MakeString("+ Add Assembly"))) { panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context); - FileView_SetMode(FileBrowser, FileViewMode_Save); + FileView_SetMode(FileBrowser, FileViewMode_Load); Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback); } ui_EndRow(&State->Interface); diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 13f5dc5..9112a93 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -57,7 +57,7 @@ INITIALIZE_APPLICATION(InitializeApplication) State->LedSystem = LedSystem_Create(Context.ThreadContext.Allocator, 128); State->AssemblyDebugState = AssemblyDebug_Create(&State->Permanent); State->AssemblyDebugState.Brightness = 255; - State->AssemblyDebugState.Override = ADS_Override_AllBlue; + State->AssemblyDebugState.Override = ADS_Override_None; GlobalDebugServices->Interface.RenderSculpture = true; @@ -80,7 +80,7 @@ INITIALIZE_APPLICATION(InitializeApplication) Panel_SetType(Profiler, &State->PanelSystem, PanelType_ProfilerView, State, Context); panel* Hierarchy = LeftPanel->Left; - Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_HierarchyView, State, Context); + Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, Context); } diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 7f10ab0..35a539d 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -11,6 +11,7 @@ #include "..\gs_libs\gs_types.h" #include "..\gs_libs\gs_types.cpp" +#include "..\gs_libs\gs_path.h" struct handle { diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index df968c8..e959107 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -523,6 +523,8 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) return Result; } +#include "../../gs_libs/gs_path.h" + int WINAPI WinMain ( HINSTANCE HInstance, @@ -533,6 +535,11 @@ WinMain ( { gs_thread_context ThreadContext = Win32CreateThreadContext(); + gs_file_info A = GetFileInfo(ThreadContext.FileHandler, ConstString("C:\\projects\\Lumenarium")); + + gs_file_info B = GetFileInfo(ThreadContext.FileHandler, ConstString("C:\\projects\\Lumenarium\\")); + + if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); @@ -629,7 +636,7 @@ WinMain ( Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); - bool Multithread = false; + bool Multithread = true; if (Multithread) { for (addressed_data_buffer* At = OutputData.Root; diff --git a/src/app/platform_win32/win32_foldhaus_fileio.h b/src/app/platform_win32/win32_foldhaus_fileio.h index 95163ea..a4f3edf 100644 --- a/src/app/platform_win32/win32_foldhaus_fileio.h +++ b/src/app/platform_win32/win32_foldhaus_fileio.h @@ -178,28 +178,39 @@ Win32SetFileInfoFromFindFileData(gs_file_info* Info, WIN32_FIND_DATA FindFileDat { u32 FileNameLength = CharArrayLength(FindFileData.cFileName); - // 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 + 1); - PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName); - NullTerminate(&FileName); - Info->FileSize = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh); Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime); Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime); - Info->Path = FileName.ConstString; 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; - s64 IndexOfLastSlash = FindLastFromSet(Path, "\\/"); - Assert(IndexOfLastSlash >= 0); - gs_const_string SearchPath = Substring(Path, 0, IndexOfLastSlash + 1); + 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); @@ -211,15 +222,18 @@ Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* 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)) { - gs_const_string SubDirName = ConstString(FindFileData.cFileName); - if (!StringsEqual(SubDirName, ConstString(".")) && - !StringsEqual(SubDirName, ConstString(".."))) + 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); + FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString, + Storage, Flags); } } @@ -230,7 +244,7 @@ Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* { temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry); *File = {0}; - Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPath, Storage); + Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPathDir, Storage); SLLPushOrInit(TempList->First, TempList->Last, File); FilesCount += 1; } diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index 71b4c99..7f21539 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -146,6 +146,11 @@ Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer) // ie. the usb stick was removed }break; + case ERROR_ACCESS_DENIED: + { + // ?? + }break; + case ERROR_INVALID_HANDLE: InvalidDefaultCase; } diff --git a/src/gs_libs/gs_path.h b/src/gs_libs/gs_path.h new file mode 100644 index 0000000..ebcbb7b --- /dev/null +++ b/src/gs_libs/gs_path.h @@ -0,0 +1,106 @@ +// +// File: gs_path.h +// Author: Peter Slattery +// Creation Date: 2021-03-06 +// +#ifndef GS_PATH_H + +internal gs_const_string +ClearString() +{ + gs_const_string R = {}; + R.Str = 0; + R.Length = 0; + return R; +} + +internal void +SanitizePath (gs_const_string Path, gs_string* Dest, gs_memory_arena* Scratch) +{ + Dest->Length = 0; + + // Put all slashes in the same format + s32 SlashCount = 0; + for (u64 i = 0; i < Path.Length; i++) + { + char At = Path.Str[i]; + if (At == '\\' || At == '/') { + SlashCount += 1; + } + } + + // we add one to slash count in case the last element is a file or + // doesn't end in a slash (even if it should) + u32 PathEleCountMax = SlashCount + 1; + u32 PathEleCount = 0; + gs_const_string* PathEle = PushArray(Scratch, gs_const_string, PathEleCountMax); + + u64 OnePastLastEleEnd = 0; + for (s64 i = 0; i < (s64)Path.Length; i++) + { + char At = Path.Str[i]; + if (At == '\\' || At == '/') + { + gs_const_string* NewEle = PathEle + PathEleCount++; + *NewEle = Substring(Path, OnePastLastEleEnd, i + 1); + OnePastLastEleEnd = i + 1; + } + } + + if (OnePastLastEleEnd != Path.Length) + { + gs_const_string* NewEle = PathEle + PathEleCount++; + *NewEle = Substring(Path, OnePastLastEleEnd, Path.Length); + OnePastLastEleEnd = Path.Length; + } + + // Remove redundant elements + for (u32 i = 0; i < PathEleCount; i++) + { + gs_const_string* At = PathEle + i; + bool ShouldRemove = false; + if (i != 0) + { + if (StringsEqual(*At, ConstString(".\\")) || + StringsEqual(*At, ConstString("./"))) + { + *At = ClearString(); + } + else if (StringsEqual(*At, ConstString("..\\")) || + StringsEqual(*At, ConstString("../"))) + { + PathEle[i - 1] = ClearString(); + if (i != 1) { + PathEle[i] = ClearString(); + } + } + } + } + + for (u32 i = 0; i < PathEleCount; i++) + { + if (PathEle[i].Str) { + AppendPrintF(Dest, "%S", PathEle[i]); + } + } + + // Put all slashes in the same format + for (u64 i = 0; i < Dest->Length; i++) + { + if (Dest->Str[i] == '/') { + Dest->Str[i] = '\\'; + } + } +} + +internal gs_const_string +SanitizePath (gs_const_string Path, gs_memory_arena* Scratch) +{ + gs_string Result = PushString(Scratch, Path.Length); + SanitizePath(Path, &Result, Scratch); + return Result.ConstString; +} + + +#define GS_PATH_H +#endif // GS_PATH_H \ No newline at end of file diff --git a/src/gs_libs/gs_tests.h b/src/gs_libs/gs_tests.h new file mode 100644 index 0000000..7a38fb5 --- /dev/null +++ b/src/gs_libs/gs_tests.h @@ -0,0 +1,72 @@ +// +// File: gs_tests.h +// Author: Peter Slattery +// Creation Date: 2021-03-06 +// +#ifndef GS_TESTS_H + +int CStringLen(char* Str) +{ + char* At = Str; + while (*At != 0) { At++; } + int Result = At - Str; + return Result; +} + +struct test_ctx +{ + int TestsCount; + int TestsPassedCount; +}; + +static test_ctx TestCtx = {0}; + +static void +BeginTest(char* Name) +{ + int Length = CStringLen(Name); + int Spaces = 25 - Length; + if(Spaces < 0) + { + Spaces = 0; + } + printf("\"%s\" %.*s [", Name, Spaces, "------------------------------"); + TestCtx.TestsCount = 0; + TestCtx.TestsPassedCount = 0; +} + +static void +TestResult(bool Result) +{ + TestCtx.TestsCount += 1; + if (Result) { + TestCtx.TestsPassedCount += 1; + } + printf(Result ? "." : "X"); +} + +static void +EndTest(void) +{ + int Spaces = 10 - TestCtx.TestsCount; + if(Spaces < 0) { Spaces = 0; } + printf("]%.*s ", Spaces, " "); + printf("[%i/%i] %i passed, %i tests, ", + TestCtx.TestsPassedCount, TestCtx.TestsCount, + TestCtx.TestsPassedCount, TestCtx.TestsCount); + if(TestCtx.TestsCount == TestCtx.TestsPassedCount) + { + printf("SUCCESS ( )"); + } + else + { + printf("FAILED (X)"); + } + printf("\n"); +} + +#define Test(name) for(int _i_ = (BeginTest(name), 0); !_i_; _i_ += 1, EndTest()) + + +#define GS_TESTS_H +#endif // GS_TESTS_H \ No newline at end of file diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index dce5c4a..d1f4462 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -191,7 +191,7 @@ int main(int ArgCount, char** Args) u32 BloomInnerChannels[] = { FSC(0, 0), FSC(0, 1), FSC(0, 2), FSC(0, 3), FSC(0, 4), FSC(0, 5) }; flower_desc F0 = {}; F0.Pos = v3{-1, 0, 0}; - F0.ComPort = "\\\\.\\COM4"; + F0.ComPort = "\\\\.\\COM11"; F0.FlowerTagValue = "left"; F0.StemChannels = StemChannels; F0.BloomOuterChannels = BloomOuterChannels; @@ -200,7 +200,7 @@ int main(int ArgCount, char** Args) flower_desc F1 = {}; F1.Pos = v3{0, 0, 0}; - F1.ComPort = "\\\\.\\COM5"; + F1.ComPort = "\\\\.\\COM12"; F1.FlowerTagValue = "center"; F1.StemChannels = StemChannels; F1.BloomInnerChannels = BloomInnerChannels; diff --git a/src/tests/sanity_tests.cpp b/src/tests/sanity_tests.cpp new file mode 100644 index 0000000..281c1af --- /dev/null +++ b/src/tests/sanity_tests.cpp @@ -0,0 +1,43 @@ +// +// File: sanity_tests.cpp +// Author: Peter Slattery +// Creation Date: 2021-03-06 +// +#ifndef SANITY_TESTS_CPP + +#include +#include "../gs_libs/gs_types.h" +#include "../gs_libs/gs_types.cpp" +#include "../gs_libs/gs_tests.h" + +#include "../gs_libs/gs_path.h" + +gs_memory_arena Scratch = {}; +void* Alloc(u64 Size, u64* ResultSize) { *ResultSize = Size; return malloc(Size); } +void Free(void* Ptr, u64 Size) { return free(Ptr); } + +bool PathTest (char* In, char* Out) { + return StringsEqual(SanitizePath(ConstString(In), &Scratch), ConstString(Out)); +} + +int main (int ArgCount, char** Args) +{ + Scratch = CreateMemoryArena(CreateAllocator(Alloc, Free)); + + Test("gs_path.h") + { + TestResult(PathTest(".", ".")); + TestResult(PathTest(".\\", ".\\")); + TestResult(PathTest("./", ".\\")); + TestResult(PathTest("./../", "..\\")); + TestResult(PathTest("C:/users/pslattery\\test.foo", "C:\\users\\pslattery\\test.foo")); + TestResult(PathTest("./test/../foo.bar", ".\\foo.bar")); + TestResult(PathTest("C:\\hello\\world\\.\\test", "C:\\hello\\world\\test")); + } + + return 0; +} + + +#define SANITY_TESTS_CPP +#endif // SANITY_TESTS_CPP \ No newline at end of file From 59cb48c9f07e152240a72ccb44ed89bfba97074a Mon Sep 17 00:00:00 2001 From: PS Date: Wed, 17 Mar 2021 22:15:37 -0700 Subject: [PATCH 006/151] Implemented Crossfading between animations --- src/app/blumen_lumen.cpp | 25 +- src/app/blumen_lumen.h | 5 + .../foldhaus_panel_animation_timeline.h | 382 ++++++++---------- .../editor/panels/foldhaus_panel_file_view.h | 23 +- src/app/engine/animation/foldhaus_animation.h | 183 +++++++-- .../animation/foldhaus_animation_renderer.cpp | 272 ++++++++----- src/app/engine/assembly/foldhaus_assembly.h | 40 ++ src/app/foldhaus_app.cpp | 17 +- .../platform_win32/win32_foldhaus_socket.h | 4 + 9 files changed, 594 insertions(+), 357 deletions(-) diff --git a/src/app/blumen_lumen.cpp b/src/app/blumen_lumen.cpp index 4f9791c..d85e3bc 100644 --- a/src/app/blumen_lumen.cpp +++ b/src/app/blumen_lumen.cpp @@ -144,7 +144,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) Animation_AddBlock(&Anim0, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(15), 0); - AnimationArray_Push(&State->AnimationSystem.Animations, Anim0); + BLState->AnimHandles[0] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim0); animation Anim1 = {0}; Anim1.Name = PushStringF(&State->Permanent, 256, "test_anim_one"); @@ -156,7 +156,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) Animation_AddBlock(&Anim1, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(12), 0); - AnimationArray_Push(&State->AnimationSystem.Animations, Anim1); + BLState->AnimHandles[1] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim1); animation Anim2 = {0}; Anim2.Name = PushStringF(&State->Permanent, 256, "i_love_you"); @@ -166,10 +166,12 @@ BlumenLumen_CustomInit(app_state* State, context Context) Anim2.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(10), 0); + Animation_AddBlock(&Anim2, 0, 100, Patterns_IndexToHandle(5), 0); + Animation_AddBlock(&Anim2, 50, Anim0.PlayableRange.Max, Patterns_IndexToHandle(10), 0); - AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); + BLState->AnimHandles[2] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); + State->AnimationSystem.ActiveFadeGroup.From = BLState->AnimHandles[2]; State->AnimationSystem.TimelineShouldAdvance = true; } // End Animation Playground @@ -189,6 +191,15 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; MotorTimeElapsed += Context->DeltaTime; + BLState->TimeElapsed += Context->DeltaTime; + + if (BLState->TimeElapsed > 5) + { + u32 NextIndex = ++BLState->CurrAnim % 3; + animation_handle Next = BLState->AnimHandles[NextIndex]; + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, Next, 5); + BLState->TimeElapsed = 0; + } gs_string BlueString = MakeString("blue"); gs_string GreenString = MakeString("green"); @@ -207,15 +218,15 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) u32 NameLen = CStringLength(Packet.AnimationFileName); if (StringEqualsCharArray(BlueString.ConstString, Packet.AnimationFileName, NameLen)) { - State->AnimationSystem.ActiveAnimationIndex = 0; + State->AnimationSystem.ActiveFadeGroup.From.Index = 0; } else if (StringEqualsCharArray(GreenString.ConstString, Packet.AnimationFileName, NameLen)) { - State->AnimationSystem.ActiveAnimationIndex = 1; + State->AnimationSystem.ActiveFadeGroup.From.Index = 1; } else if (StringEqualsCharArray(ILoveYouString.ConstString, Packet.AnimationFileName, NameLen)) { - State->AnimationSystem.ActiveAnimationIndex = 2; + State->AnimationSystem.ActiveFadeGroup.From.Index = 2; } OutputDebugStringA("Received Pattern Packet\n"); diff --git a/src/app/blumen_lumen.h b/src/app/blumen_lumen.h index 2c47b22..56ad6b6 100644 --- a/src/app/blumen_lumen.h +++ b/src/app/blumen_lumen.h @@ -70,6 +70,11 @@ struct blumen_lumen_state mic_listen_job_data MicListenJobData; motor_packet LastKnownMotorState; + + r64 TimeElapsed; + + animation_handle AnimHandles[3]; + u32 CurrAnim; }; diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 6464628..9b350c9 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -13,6 +13,7 @@ struct animation_timeline_state { frame_range VisibleRange; handle SelectedBlockHandle; + animation_handle EditingAnimationHandle; u32 SelectedAnimationLayer; }; @@ -33,22 +34,13 @@ GetXPositionFromFrameInAnimationPanel (u32 Frame, rect2 PanelBounds, frame_range return XPositionAtFrame; } -internal handle -AddAnimationBlockAtCurrentTime (animation_pattern_handle AnimationProcHandle, u32 LayerHandle, animation_system* System) -{ - u32 NewBlockStart = System->CurrentFrame; - u32 NewBlockEnd = NewBlockStart + SecondsToFrames(3, *System); - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - handle AnimHandle = Animation_AddBlock(ActiveAnim, NewBlockStart, NewBlockEnd, AnimationProcHandle, LayerHandle); - return AnimHandle; -} - FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand) { animation_timeline_state* PanelState = Panel_GetStateStruct(Panel, animation_timeline_state); handle SelectedBlockHandle = PanelState->SelectedBlockHandle; - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + animation* ActiveAnim = AnimationArray_GetSafe(State->AnimationSystem.Animations, PanelState->EditingAnimationHandle); + if(SelectedBlockHandle.Index < ActiveAnim->Blocks_.Count && ActiveAnim->Blocks_.Generations[SelectedBlockHandle.Index] == SelectedBlockHandle.Generation) { @@ -112,6 +104,7 @@ StartDragTimeMarker(rect2 TimelineBounds, frame_range VisibleFrames, app_state* OPERATION_STATE_DEF(drag_animation_block_state) { rect2 TimelineBounds; + animation_handle EditingAnimationHandle; handle BlockHandle; frame_range VisibleRange; frame_range ClipRange; @@ -133,7 +126,9 @@ OPERATION_RENDER_PROC(UpdateDragAnimationBlock) { drag_animation_block_state* OpState = (drag_animation_block_state*)Operation.OpStateMemory; - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + animation_array Animations = State->AnimationSystem.Animations; + animation_handle Handle = OpState->EditingAnimationHandle; + animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle); r32 ClipInitialStartFrameXPercent = FrameToPercentRange(OpState->ClipRange.Min, OpState->VisibleRange); u32 ClipInitialStartFrameXPosition = LerpR32(ClipInitialStartFrameXPercent, @@ -237,7 +232,10 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle { TimelineState->SelectedBlockHandle = BlockHandle; - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + animation_handle Handle = TimelineState->EditingAnimationHandle; + animation_array Animations = State->AnimationSystem.Animations; + animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle); + operation_mode* DragAnimationBlockMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationBlockCommands, UpdateDragAnimationBlock); animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle); @@ -245,29 +243,39 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle &State->Modes, drag_animation_block_state); OpState->TimelineBounds = TimelineBounds; + OpState->EditingAnimationHandle = Handle; OpState->BlockHandle = BlockHandle; OpState->VisibleRange = VisibleRange; OpState->ClipRange = SelectedBlock->Range; } // ------------------- -FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand) +internal void +AnimationTimeline_AddAnimationBlockCommand(animation_timeline_state* TimelineState, app_state* State, context Context) { - animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state); + animation_handle Handle = TimelineState->EditingAnimationHandle; + animation_array Animations = State->AnimationSystem.Animations; + animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle); - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - - frame_range Range = ActiveAnim->PlayableRange; - u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, Panel->Bounds, Range); - - animation_pattern_handle PatternHandle = Patterns_IndexToHandle(4); - handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), PatternHandle, TimelineState->SelectedAnimationLayer); - TimelineState->SelectedBlockHandle = NewBlockHandle; + s32 StartFrame = State->AnimationSystem.CurrentFrame; + s32 EndFrame = StartFrame + SecondsToFrames(3, State->AnimationSystem); + EndFrame = Clamp(0, EndFrame, ActiveAnim->PlayableRange.Max); + if ((EndFrame - StartFrame) > 0) + { + animation_pattern_handle PatternHandle = Patterns_IndexToHandle(0); + u32 Layer = TimelineState->SelectedAnimationLayer; + + handle NewBlockHandle = Animation_AddBlock(ActiveAnim, StartFrame, EndFrame, PatternHandle, Layer); + + TimelineState->SelectedBlockHandle = NewBlockHandle; + } else { + // TODO(pjs): we don't want to create a block of zero frames + // since you won't be able to delete it. What do we do here?? + } } input_command AnimationTimeline_Commands[] = { { KeyCode_X, KeyCode_Invalid, Command_Began, DeleteAnimationBlockCommand }, - { KeyCode_A, KeyCode_Invalid, Command_Began, AddAnimationBlockCommand }, }; s32 AnimationTimeline_CommandsCount = 2; @@ -276,10 +284,17 @@ GSMetaTag(panel_type_animation_timeline); internal void AnimationTimeline_Init(panel* Panel, app_state* State, context Context) { + animation_handle Handle = State->AnimationSystem.ActiveFadeGroup.From; + // TODO: :FreePanelMemory - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); animation_timeline_state* TimelineState = PushStruct(&State->Permanent, animation_timeline_state); - TimelineState->VisibleRange = ActiveAnim->PlayableRange; + TimelineState->EditingAnimationHandle = Handle; + + if (IsValid(Handle)) { + animation_array Animations = State->AnimationSystem.Animations; + animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle); + TimelineState->VisibleRange = ActiveAnim->PlayableRange; + } Panel->StateMemory = StructToData(TimelineState, animation_timeline_state); } @@ -481,92 +496,6 @@ DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, frame_range V return BlockBounds; } -internal handle -DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_state* TimelineState, rect2 PanelBounds, handle SelectedBlockHandle, ui_interface* Interface, app_state* State) -{ - gs_string Tempgs_string = PushString(State->Transient, 256); - handle Result = SelectedBlockHandle; - - animation CurrAnimation = *AnimationSystem_GetActiveAnimation(AnimationSystem); - - rect2 LayerMenuBounds, TimelineBounds; - RectVSplitAtDistanceFromLeft(PanelBounds, 256, &LayerMenuBounds, &TimelineBounds); - - // In Top To Bottom Order - rect2 TimelineFrameBarBounds; - rect2 TimelineBlockDisplayBounds; - rect2 TimelineRangeBarBounds; - RectHSplitAtDistanceFromTop(TimelineBounds, 32, &TimelineFrameBarBounds, &TimelineBounds); - RectHSplitAtDistanceFromBottom(TimelineBounds, 24, &TimelineBlockDisplayBounds, &TimelineRangeBarBounds); - - DrawLayerMenu(AnimationSystem, CurrAnimation, *Interface, LayerMenuBounds, &TimelineState->SelectedAnimationLayer); - - frame_range AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, CurrAnimation, TimelineState, *Interface, TimelineRangeBarBounds); - - DrawFrameBar(AnimationSystem, *Interface, AdjustedViewRange, TimelineFrameBarBounds, State); - - ui_FillRect(Interface, TimelineBlockDisplayBounds, v4{.25f, .25f, .25f, 1.0f}); - - // Animation Blocks - b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState); - handle DragBlockHandle = {0}; - for (u32 i = 0; i < CurrAnimation.Blocks_.Count; i++) - { - animation_block* AnimationBlockAt = CurrAnimation.Blocks_.Values + i; - - // If either end is in the range, we should draw it - b32 RangeIsVisible = (FrameIsInRange(AdjustedViewRange, AnimationBlockAt->Range.Min) || - FrameIsInRange(AdjustedViewRange, AnimationBlockAt->Range.Max)); - // If neither end is in the range, but the ends surround the visible range, - // we should still draw it. - RangeIsVisible |= (AnimationBlockAt->Range.Min <= AdjustedViewRange.Min && - AnimationBlockAt->Range.Max>= AdjustedViewRange.Max); - if (RangeIsVisible) - { - v4 BlockColor = BlackV4; - if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == CurrAnimation.Blocks_.Generations[i]) - { - BlockColor = PinkV4; - } - rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, AdjustedViewRange, TimelineBounds, Interface->RenderBuffer); - if (PointIsInRect(BlockBounds, Interface->Mouse.Pos)) - { - DragBlockHandle.Index = i; - DragBlockHandle.Generation = CurrAnimation.Blocks_.Generations[i]; - } - } - } - - if (MouseDownAndNotHandled && Handle_IsValid(DragBlockHandle)) - { - MouseDownAndNotHandled = false; - SelectAndBeginDragAnimationBlock(TimelineState, DragBlockHandle, AdjustedViewRange, TimelineBounds, State); - } - - // Time Slider - if (FrameIsInRange(AdjustedViewRange, AnimationSystem->CurrentFrame)) - { - r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, AdjustedViewRange); - r32 SliderX = LerpR32(FrameAtPercentVisibleRange, TimelineBounds.Min.x, TimelineBounds.Max.x); - rect2 SliderBounds = { - v2{ SliderX, TimelineBounds.Min.y }, - v2{ SliderX + 1, TimelineBounds.Max.y } - }; - ui_FillRect(Interface, SliderBounds, TimeSliderColor); - } - - ui_OutlineRect(Interface, TimelineRangeBarBounds, 1.f, RedV4); - ui_OutlineRect(Interface, TimelineFrameBarBounds, 1.f, RedV4); - ui_OutlineRect(Interface, TimelineBlockDisplayBounds, 1.f, RedV4); - - if (MouseDownAndNotHandled && PointIsInRect(TimelineBounds, Interface->Mouse.Pos)) - { - TimelineState->SelectedBlockHandle = {0}; - } - - return Result; -} - PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) { Assert(ReturningFrom->TypeIndex == PanelType_FileView); @@ -581,28 +510,11 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) animation NewAnim = AnimParser_Parse(AnimFileString, State->AnimationSystem.Storage, State->Patterns); NewAnim.FileInfo = AnimFile.FileInfo; - u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); - State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex; + animation_handle NewAnimHandle = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); + State->AnimationSystem.ActiveFadeGroup.From = NewAnimHandle; } } -internal void -DrawAnimationPatternList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem, animation_pattern_array Patterns) -{ - ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("AnimClips Layout")); - for (u32 i = 0; i < Patterns.Count; i++) - { - animation_pattern Pattern = Patterns.Values[i]; - gs_string PatternName = MakeString(Pattern.Name, Pattern.NameLength); - if (ui_Button(Interface, PatternName)) - { - animation_pattern_handle PatternHandle = Patterns_IndexToHandle(i); - AddAnimationBlockAtCurrentTime(PatternHandle, SelectedAnimationLayerHandle, AnimationSystem); - } - } - ui_PopLayout(Interface, MakeString("AnimClips Layout")); -} - internal void PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { @@ -634,14 +546,18 @@ PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Pan } internal void -FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) +FrameCount_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { ui_interface* Interface = &State->Interface; gs_string TempString = PushString(State->Transient, 256); + // :FrameRange // frame_range VisibleFrames = TimelineState->VisibleRange; - animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - frame_range VisibleFrames = ActiveAnim.PlayableRange; + + frame_range VisibleFrames = {}; + if (ActiveAnim) { + VisibleFrames = ActiveAnim->PlayableRange; + } s32 VisibleFrameCount = VisibleFrames.Max - VisibleFrames.Min; @@ -687,10 +603,9 @@ FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_ } internal void -LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) +LayerList_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { ui_interface* Interface = &State->Interface; - animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem); ui_FillRect(Interface, Bounds, Interface->Style.PanelBG); @@ -698,38 +613,45 @@ LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* P rect2 LayerBounds = {0}; LayerBounds.Min = Bounds.Min; LayerBounds.Max = LayerBounds.Min + LayerDim; - for (u32 i = 0; i < ActiveAnim.Layers.Count; i++) + + if (ActiveAnim) { - anim_layer* Layer = ActiveAnim.Layers.Values + i; - - if (ui_MouseClickedRect(*Interface, LayerBounds)) + for (u32 i = 0; i < ActiveAnim->Layers.Count; i++) { - TimelineState->SelectedAnimationLayer = i; + anim_layer* Layer = ActiveAnim->Layers.Values + i; + + if (ui_MouseClickedRect(*Interface, LayerBounds)) + { + TimelineState->SelectedAnimationLayer = i; + } + + v2 LayerTextPos = { LayerBounds.Min.x + 6, LayerBounds.Max.y - 16}; + if (TimelineState->SelectedAnimationLayer == i) + { + PushRenderBoundingBox2D(Interface->RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4); + } + DrawString(Interface->RenderBuffer, Layer->Name, Interface->Style.Font, LayerTextPos, WhiteV4); + + LayerBounds = Rect2TranslateY(LayerBounds, LayerDim.y); } - - v2 LayerTextPos = { LayerBounds.Min.x + 6, LayerBounds.Max.y - 16}; - if (TimelineState->SelectedAnimationLayer == i) - { - PushRenderBoundingBox2D(Interface->RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4); - } - DrawString(Interface->RenderBuffer, Layer->Name, Interface->Style.Font, LayerTextPos, WhiteV4); - - LayerBounds = Rect2TranslateY(LayerBounds, LayerDim.y); } } internal void -TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) +TimeRange_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { ui_interface* Interface = &State->Interface; - animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem); // TODO(pjs): setting the timeline to show the entire range // of the current animation until I reimplement the range // slider bars // :FrameRange // frame_range ViewRange = TimelineState->VisibleRange; - frame_range ViewRange = ActiveAnim.PlayableRange; + frame_range ViewRange = {}; + if (ActiveAnim) + { + ViewRange = ActiveAnim->PlayableRange; + } handle SelectedBlockHandle = TimelineState->SelectedBlockHandle; s32 CurrentFrame = State->AnimationSystem.CurrentFrame; @@ -737,30 +659,33 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c // Animation Blocks b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState); handle DragBlockHandle = {0}; - for (u32 i = 0; i < ActiveAnim.Blocks_.Count; i++) + if (ActiveAnim) { - animation_block* AnimationBlockAt = ActiveAnim.Blocks_.Values + i; - - // If either end is in the range, we should draw it - b32 RangeIsVisible = (FrameIsInRange(ViewRange, AnimationBlockAt->Range.Min) || - FrameIsInRange(ViewRange, AnimationBlockAt->Range.Max)); - // If neither end is in the range, but the ends surround the visible range, - // we should still draw it. - RangeIsVisible |= (AnimationBlockAt->Range.Min <= ViewRange.Min && - AnimationBlockAt->Range.Max>= ViewRange.Max); - if (RangeIsVisible) + for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++) { - v4 BlockColor = BlackV4; - if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == ActiveAnim.Blocks_.Generations[i]) - { - BlockColor = PinkV4; - } - rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, ViewRange, Bounds, Interface->RenderBuffer); + animation_block* AnimationBlockAt = ActiveAnim->Blocks_.Values + i; - if (PointIsInRect(BlockBounds, Interface->Mouse.Pos)) + // If either end is in the range, we should draw it + b32 RangeIsVisible = (FrameIsInRange(ViewRange, AnimationBlockAt->Range.Min) || + FrameIsInRange(ViewRange, AnimationBlockAt->Range.Max)); + // If neither end is in the range, but the ends surround the visible range, + // we should still draw it. + RangeIsVisible |= (AnimationBlockAt->Range.Min <= ViewRange.Min && + AnimationBlockAt->Range.Max>= ViewRange.Max); + if (RangeIsVisible) { - DragBlockHandle.Index = i; - DragBlockHandle.Generation = ActiveAnim.Blocks_.Generations[i]; + v4 BlockColor = BlackV4; + if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == ActiveAnim->Blocks_.Generations[i]) + { + BlockColor = PinkV4; + } + rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, ViewRange, Bounds, Interface->RenderBuffer); + + if (PointIsInRect(BlockBounds, Interface->Mouse.Pos)) + { + DragBlockHandle.Index = i; + DragBlockHandle.Generation = ActiveAnim->Blocks_.Generations[i]; + } } } } @@ -795,10 +720,10 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c internal void AnimInfoView_SaveAnimFile (gs_const_string Path, app_state* State, context Context) { - u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex; - animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex]; + animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From; + animation ActiveAnim = *AnimationArray_GetSafe(State->AnimationSystem.Animations, ActiveAnimHandle); - gs_string FileText = AnimSerializer_Serialize(ActiveAnimation, State->Patterns, State->Transient); + gs_string FileText = AnimSerializer_Serialize(ActiveAnim, State->Patterns, State->Transient); if (!WriteEntireFile(Context.ThreadContext.FileHandler, Path, StringToData(FileText))) { @@ -816,24 +741,41 @@ PANEL_MODAL_OVERRIDE_CALLBACK(AnimInfoView_SaveAnimFileCallback) } internal void -AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) +AnimationTimeline_SetActiveAnimation (animation_handle Handle, animation_timeline_state* TimelineState, + animation_system* System) +{ + System->ActiveFadeGroup.From = Handle; + TimelineState->EditingAnimationHandle = Handle; +} + +internal void +AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { animation_system* AnimSystem = &State->AnimationSystem; - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(AnimSystem); ui_interface* Interface = &State->Interface; ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout")); ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBG); - if (ui_BeginLabeledDropdown(Interface, MakeString("Active Animation"), ActiveAnim->Name)) + gs_string AnimName = {}; + if (ActiveAnim) + { + AnimName = ActiveAnim->Name; + } else { + AnimName = MakeString("[None]"); + } + + if (ui_BeginLabeledDropdown(Interface, MakeString("Active Animation"), AnimName)) { for (u32 i = 0; i < AnimSystem->Animations.Count; i++) { animation Animation = AnimSystem->Animations.Values[i]; if (ui_Button(Interface, Animation.Name)) { - AnimSystem->ActiveAnimationIndex = i; + animation_handle NewHandle = {}; + NewHandle.Index = i; + AnimationTimeline_SetActiveAnimation(NewHandle, TimelineState, AnimSystem); } } } @@ -846,16 +788,16 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel animation NewAnim = {}; NewAnim.Name = PushString(State->AnimationSystem.Storage, 256); - u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); - State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex; + animation_handle NewAnimHandle = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); + State->AnimationSystem.ActiveFadeGroup.From = NewAnimHandle; } - if (ui_Button(Interface, MakeString("Save"))) + if (ActiveAnim && ui_Button(Interface, MakeString("Save"))) { // Save Animation File // TODO(pjs): If you created the animation via the "new" button, there won't be a file attached. // need to use the file browser to create a file - u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex; - animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex]; + animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From; + animation ActiveAnimation = *AnimationArray_GetSafe(State->AnimationSystem.Animations, ActiveAnimHandle); if (!ActiveAnimation.FileInfo.Path.Str) { @@ -875,39 +817,46 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel } ui_EndRow(Interface); - ui_TextEntry(Interface, MakeString("Anim Name"), &ActiveAnim->Name); - - ui_Label(Interface, MakeString("Frame Range")); - ui_BeginRow(Interface, 3); + if (ActiveAnim) { - ActiveAnim->PlayableRange.Min = ui_TextEntryU64(Interface, MakeString("StartFrame"), ActiveAnim->PlayableRange.Min); - ActiveAnim->PlayableRange.Max = ui_TextEntryU64(Interface, MakeString("EndFrame"), ActiveAnim->PlayableRange.Max); - - } - ui_EndRow(Interface); - - animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, TimelineState->SelectedBlockHandle); - if (SelectedBlock) - { - animation_pattern BlockPattern = Patterns_GetPattern(State->Patterns, SelectedBlock->AnimationProcHandle); + ui_TextEntry(Interface, MakeString("Anim Name"), &ActiveAnim->Name); + ui_Label(Interface, MakeString("Frame Range")); ui_BeginRow(Interface, 3); - ui_Label(Interface, MakeString("Selected Pattern")); - //if (ui_BeginLabeledDropdown(Interface, MakeString("Selected Pattern"), MakeString(BlockPattern.Name, BlockPattern.NameLength))) - if (ui_BeginDropdown(Interface, MakeString(BlockPattern.Name, BlockPattern.NameLength))) { - for (u32 i = 0; i < State->Patterns.Count; i++) + ActiveAnim->PlayableRange.Min = ui_TextEntryU64(Interface, MakeString("StartFrame"), ActiveAnim->PlayableRange.Min); + ActiveAnim->PlayableRange.Max = ui_TextEntryU64(Interface, MakeString("EndFrame"), ActiveAnim->PlayableRange.Max); + + } + ui_EndRow(Interface); + + animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, TimelineState->SelectedBlockHandle); + if (SelectedBlock) + { + animation_pattern BlockPattern = Patterns_GetPattern(State->Patterns, SelectedBlock->AnimationProcHandle); + + ui_BeginRow(Interface, 3); + ui_Label(Interface, MakeString("Selected Pattern")); + //if (ui_BeginLabeledDropdown(Interface, MakeString("Selected Pattern"), MakeString(BlockPattern.Name, BlockPattern.NameLength))) + if (ui_BeginDropdown(Interface, MakeString(BlockPattern.Name, BlockPattern.NameLength))) { - animation_pattern Pattern = State->Patterns.Values[i]; - if (ui_Button(Interface, MakeString(Pattern.Name, Pattern.NameLength))) + for (u32 i = 0; i < State->Patterns.Count; i++) { - SelectedBlock->AnimationProcHandle = Patterns_IndexToHandle(i); + animation_pattern Pattern = State->Patterns.Values[i]; + if (ui_Button(Interface, MakeString(Pattern.Name, Pattern.NameLength))) + { + SelectedBlock->AnimationProcHandle = Patterns_IndexToHandle(i); + } } } + ui_EndLabeledDropdown(Interface); + } + + if (ui_Button(Interface, MakeString("+ Add Block"))) + { + AnimationTimeline_AddAnimationBlockCommand(TimelineState, State, Context); } - ui_EndLabeledDropdown(Interface); } - ui_PopLayout(Interface, MakeString("AnimInfo Layout")); } @@ -924,6 +873,15 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* { animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state); + animation* ActiveAnim = 0; + animation_handle Handle = State->AnimationSystem.ActiveFadeGroup.From; + TimelineState->EditingAnimationHandle = Handle; + if (IsValid(Handle)) + { + animation_array Animations = State->AnimationSystem.Animations; + ActiveAnim = AnimationArray_GetSafe(Animations, Handle); + } + ui_FillRect(&State->Interface, PanelBounds, v4{.1f,.1f,.1f,1.f}); rect2 TimelineBounds, InfoBounds; @@ -940,10 +898,10 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RectHSplitAtDistanceFromTop(TimeRangePanelBounds, TitleBarHeight, &FrameCountBounds, &TimeRangeBounds); PlayBar_Render(TimelineState, PlayBarBounds, Panel, RenderBuffer, State, Context); - FrameCount_Render(TimelineState, FrameCountBounds, RenderBuffer, State, Context); - LayerList_Render(TimelineState, LayersBounds, Panel, RenderBuffer, State, Context); - TimeRange_Render(TimelineState, TimeRangeBounds, RenderBuffer, State, Context); - AnimInfoView_Render(TimelineState, InfoBounds, Panel, RenderBuffer, State, Context); + FrameCount_Render(TimelineState, ActiveAnim, FrameCountBounds, RenderBuffer, State, Context); + LayerList_Render(TimelineState, ActiveAnim, LayersBounds, Panel, RenderBuffer, State, Context); + TimeRange_Render(TimelineState, ActiveAnim, TimeRangeBounds, RenderBuffer, State, Context); + AnimInfoView_Render(TimelineState, ActiveAnim, InfoBounds, Panel, RenderBuffer, State, Context); } #define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H diff --git a/src/app/editor/panels/foldhaus_panel_file_view.h b/src/app/editor/panels/foldhaus_panel_file_view.h index aee1c19..0910e66 100644 --- a/src/app/editor/panels/foldhaus_panel_file_view.h +++ b/src/app/editor/panels/foldhaus_panel_file_view.h @@ -16,6 +16,8 @@ struct file_view_state file_view_mode Mode; gs_string WorkingDirectory; + gs_string DisplayDirectory; + gs_memory_arena FileNamesArena; gs_file_info_array FileNames; @@ -55,6 +57,7 @@ FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_stat Assert(LastChar == '\\' || LastChar == '/'); ClearArena(&State->FileNamesArena); + gs_string SanitizedDir = PushString(Context.ThreadContext.Transient, WorkingDirectory.Length + 2); SanitizePath(WorkingDirectory, &SanitizedDir, Context.ThreadContext.Transient); if (SanitizedDir.Str[SanitizedDir.Length - 1] != '\\') @@ -62,6 +65,8 @@ FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_stat AppendPrintF(&SanitizedDir, "\\"); } + gs_const_string SanitizedDisplayDir = SanitizedDir.ConstString; + gs_file_info NewWorkingDir = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDir.ConstString); if (NewWorkingDir.IsDirectory) { @@ -73,7 +78,7 @@ FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_stat // NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory // in some cases. Shouldn't be a problem but it is unnecessary PrintF(&State->WorkingDirectory, "%S", SanitizedDir.ConstString); - + PrintF(&State->DisplayDirectory, "%S", SanitizedDisplayDir); } } @@ -88,7 +93,9 @@ FileView_Init(panel* Panel, app_state* State, context Context) FileViewState->FileNamesArena = CreateMemoryArena(Context.ThreadContext.Allocator); // TODO(pjs): this shouldn't be stored in permanent - FileViewState->WorkingDirectory = PushString(&State->Permanent, 256); + FileViewState->DisplayDirectory = PushString(&State->Permanent, 1024); + FileViewState->WorkingDirectory = PushString(&State->Permanent, 1024); + FileView_UpdateWorkingDirectory(ConstString(".\\"), FileViewState, Context); } @@ -109,13 +116,17 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout")); { - ui_BeginRow(&State->Interface, 3); { if (FileViewState->Mode == FileViewMode_Save) { if (ui_Button(&State->Interface, MakeString("Save"))) { + if (!FileViewState->SelectedFile.Path.Str) + { + FileViewState->SelectedFile.Path = FileViewState->DisplayDirectory.ConstString; + } + FileView_Exit_(Panel, State, Context); } } @@ -128,11 +139,11 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu ui_EndRow(&State->Interface); // Header - if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->WorkingDirectory)) + if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->DisplayDirectory)) { // if last character is a slash, update pwd, and clear the filter string // otherwise update the filter string - gs_string Pwd = FileViewState->WorkingDirectory; + gs_string Pwd = FileViewState->DisplayDirectory; char LastChar = Pwd.Str[Pwd.Length - 1]; if (LastChar == '\\' || LastChar == '/') { @@ -163,9 +174,9 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu } else { + FileViewState->SelectedFile = File; switch (FileViewState->Mode) { - FileViewState->SelectedFile = File; case FileViewMode_Load: { FileView_Exit_(Panel, State, Context); diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index d93f3f1..85e541a 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -46,9 +46,7 @@ enum blend_mode BlendMode_Count, }; -// TODO(pjs): Add Opacity to this -typedef pixel led_blend_proc(pixel PixelA, pixel PixelB); - +// TODO(pjs): This really doesn't belong here global gs_const_string BlendModeStrings[] = { ConstString("Overwrite"), ConstString("Add"), @@ -86,6 +84,18 @@ struct animation gs_file_info FileInfo; }; +struct animation_handle +{ + s32 Index; +}; + +internal bool IsValid (animation_handle H) { return H.Index >= 0; } +internal void Clear (animation_handle* H) { H->Index = -1; } +internal bool AnimHandlesAreEqual (animation_handle A, animation_handle B) +{ + return A.Index == B.Index; +} + struct animation_array { animation* Values; @@ -101,7 +111,9 @@ struct animation_layer_frame animation_block NextHot; bool HasNextHot; - r32 HotOpacity; + r32 NextHotOpacity; + + blend_mode BlendMode; }; // NOTE(pjs): This is an evaluated frame - across all layers in an @@ -112,6 +124,27 @@ struct animation_frame u32 LayersCount; }; +enum animation_repeat_mode +{ + AnimationRepeat_Single, + AnimationRepeat_Loop, + AnimationRepeat_Invalid, +}; + +global gs_const_string AnimationRepeatModeStrings[] = { + ConstString("Repeat Single"), + ConstString("Loop"), + ConstString("Invalid"), +}; + +struct animation_fade_group +{ + animation_handle From; + animation_handle To; + r32 FadeElapsed; + r32 FadeDuration; +}; + #define ANIMATION_SYSTEM_LAYERS_MAX 128 #define ANIMATION_SYSTEM_BLOCKS_MAX 128 struct animation_system @@ -119,10 +152,14 @@ struct animation_system gs_memory_arena* Storage; animation_array Animations; + animation_repeat_mode RepeatMode; + // NOTE(Peter): The frame currently being displayed/processed. you // can see which frame you're on by looking at the time slider on the timeline // panel - u32 ActiveAnimationIndex; + animation_handle ActiveAnimationHandle_; + animation_fade_group ActiveFadeGroup; + s32 CurrentFrame; s32 LastUpdatedFrame; r32 SecondsPerFrame; @@ -214,7 +251,7 @@ Patterns_Create(gs_memory_arena* Arena, s32 CountMax) return Result; } -#define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc))) +#define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)) - 1) internal void Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength) { @@ -341,13 +378,33 @@ AnimationArray_Create(gs_memory_arena* Storage, u32 CountMax) return Result; } -internal u32 +internal animation_handle AnimationArray_Push(animation_array* Array, animation Value) { Assert(Array->Count < Array->CountMax); - u32 Index = Array->Count++; - Array->Values[Index] = Value; - return Index; + animation_handle Result = {0}; + Result.Index = Array->Count++; + Array->Values[Result.Index] = Value; + return Result; +} + +internal animation* +AnimationArray_Get(animation_array Array, animation_handle Handle) +{ + animation* Result = 0; + if (IsValid(Handle) && Handle.Index < (s32)Array.Count) + { + Result = Array.Values + Handle.Index; + } + return Result; +} + +internal animation* +AnimationArray_GetSafe(animation_array Array, animation_handle Handle) +{ + Assert(IsValid(Handle)); + Assert(Handle.Index < (s32)Array.Count); + return AnimationArray_Get(Array, Handle); } ////////////////////////// @@ -482,6 +539,49 @@ ClampFrameToRange(s32 Frame, frame_range Range) // Layers +// Fade Group + +internal bool +AnimationFadeGroup_ShouldRender (animation_fade_group FadeGroup) +{ + return IsValid(FadeGroup.From); +} + +internal void +AnimationFadeGroup_Advance(animation_fade_group* Group) +{ + Group->From = Group->To; + Clear(&Group->To); + Group->FadeElapsed = 0; + Group->FadeDuration = 0; +} + +internal void +AnimationFadeGroup_Update(animation_fade_group* Group, r32 DeltaTime) +{ + if (IsValid(Group->To)) + { + Group->FadeElapsed += DeltaTime; + if (Group->FadeElapsed >= Group->FadeDuration) + { + AnimationFadeGroup_Advance(Group); + } + } +} + +internal void +AnimationFadeGroup_FadeTo(animation_fade_group* Group, animation_handle To, r32 Duration) +{ + // complete current fade if there is one in progress + if (IsValid(Group->To)) + { + AnimationFadeGroup_Advance(Group); + } + + Group->To = To; + Group->FadeDuration = Duration; +} + // System struct animation_system_desc @@ -499,29 +599,38 @@ AnimationSystem_Init(animation_system_desc Desc) Result.Animations = AnimationArray_Create(Result.Storage, Desc.AnimArrayCount); Result.SecondsPerFrame = Desc.SecondsPerFrame; + Clear(&Result.ActiveFadeGroup.From); + Clear(&Result.ActiveFadeGroup.To); + Result.ActiveFadeGroup.FadeElapsed = 0; + return Result; } internal animation* AnimationSystem_GetActiveAnimation(animation_system* System) { - // TODO(pjs): need a way to specify the active animation - return System->Animations.Values + System->ActiveAnimationIndex; + return AnimationArray_Get(System->Animations, System->ActiveFadeGroup.From); } internal animation_frame -AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_arena* Arena) +AnimationSystem_CalculateAnimationFrame(animation_system* System, + animation* Animation, + gs_memory_arena* Arena) { - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - animation_frame Result = {0}; - Result.LayersCount = ActiveAnim->Layers.Count; + Result.LayersCount = Animation->Layers.Count; Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount); ZeroArray(Result.Layers, animation_layer_frame, Result.LayersCount); - for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++) + for (u32 l = 0; l < Animation->Layers.Count; l++) { - animation_block Block = ActiveAnim->Blocks_.Values[i]; + animation_layer_frame* Layer = Result.Layers + l; + Layer->BlendMode = Animation->Layers.Values[l].BlendMode; + } + + for (u32 i = 0; i < Animation->Blocks_.Count; i++) + { + animation_block Block = Animation->Blocks_.Values[i]; if (FrameIsInRange(Block.Range, System->CurrentFrame)) { @@ -548,12 +657,12 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren frame_range BlendRange = {}; BlendRange.Min = Layer->NextHot.Range.Min; BlendRange.Max = Layer->Hot.Range.Max; - Layer->HotOpacity = 1.0f - FrameToPercentRange(System->CurrentFrame, BlendRange); + Layer->NextHotOpacity = FrameToPercentRange(System->CurrentFrame, BlendRange); } else { Layer->Hot = Block; - Layer->HotOpacity = 1.0f; + Layer->NextHotOpacity = 0.0f; Layer->HasHot = true; } } @@ -563,18 +672,34 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren } internal void -AnimationSystem_Update(animation_system* System) +AnimationSystem_Update(animation_system* System, r32 DeltaTime) { + if (!System->TimelineShouldAdvance) { return; } + if (!AnimationFadeGroup_ShouldRender(System->ActiveFadeGroup)) { return; } + + AnimationFadeGroup_Update(&System->ActiveFadeGroup, DeltaTime); + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - if (System->TimelineShouldAdvance) { - // TODO(Peter): Revisit this. This implies that the framerate of the animation system - // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure - System->CurrentFrame += 1; - - // Loop back to the beginning - if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) + // TODO(Peter): Revisit this. This implies that the framerate of the animation system + // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure + System->CurrentFrame += 1; + + // Loop back to the beginning + if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) + { + switch (System->RepeatMode) { - System->CurrentFrame = 0; + case AnimationRepeat_Single: + { + System->CurrentFrame = 0; + }break; + + case AnimationRepeat_Loop: + { + // TODO(pjs): + }break; + + InvalidDefaultCase; } } } diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index 8915d59..16072ad 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -6,25 +6,27 @@ #ifndef FOLDHAUS_ANIMATION_RENDERER_CPP internal pixel -LedBlend_Overwrite(pixel PixelA, pixel PixelB) +LedBlend_Overwrite(pixel PixelA, pixel PixelB, u8* UserData) { return PixelB; } internal pixel -LedBlend_Overwrite(pixel PixelA, pixel PixelB, r32 Opacity) +LedBlend_Lerp(pixel PixelA, pixel PixelB, u8* UserData) { + r32 BOpacity = *(r32*)UserData; + pixel Result = {}; - r32 BOpacity = 1.0f - Opacity; - Result.R = (u8)((PixelA.R * Opacity) + (PixelB.R * BOpacity)); - Result.G = (u8)((PixelA.G * Opacity) + (PixelB.G * BOpacity)); - Result.B = (u8)((PixelA.B * Opacity) + (PixelB.B * BOpacity)); + r32 AOpacity = 1.0f - BOpacity; + Result.R = (u8)((PixelA.R * AOpacity) + (PixelB.R * BOpacity)); + Result.G = (u8)((PixelA.G * AOpacity) + (PixelB.G * BOpacity)); + Result.B = (u8)((PixelA.B * AOpacity) + (PixelB.B * BOpacity)); return Result; } internal pixel -LedBlend_Add(pixel PixelA, pixel PixelB) +LedBlend_Add(pixel PixelA, pixel PixelB, u8* UserData) { pixel Result = {}; @@ -40,7 +42,7 @@ LedBlend_Add(pixel PixelA, pixel PixelB) } internal pixel -LedBlend_Multiply(pixel PixelA, pixel PixelB) +LedBlend_Multiply(pixel PixelA, pixel PixelB, u8* UserData) { pixel Result = {}; @@ -60,7 +62,7 @@ LedBlend_Multiply(pixel PixelA, pixel PixelB) } internal pixel -LedBlend_Overlay(pixel PixelA, pixel PixelB) +LedBlend_Overlay(pixel PixelA, pixel PixelB, u8* UserData) { pixel Result = {}; return Result; @@ -80,15 +82,110 @@ LedBlend_GetProc(blend_mode BlendMode) return Result; } +struct pattern_args +{ + assembly Assembly; + gs_memory_arena* Transient; + u8* UserData; +}; + internal void -AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, assembly Assembly, animation_pattern_array Patterns, gs_memory_arena* Transient, - u8* UserData) +AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, animation_pattern_array Patterns, pattern_args PatternArgs) { u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min; r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame; animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle); - Pattern.Proc(Buffer, Assembly, SecondsIntoBlock, Transient, UserData); + Pattern.Proc(Buffer, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData); +} + +// NOTE(pjs): This mirrors animation_layer_frame to account +// for overlapping +struct layer_led_buffer +{ + led_buffer HotBuffer; + led_buffer NextHotBuffer; +}; + +internal led_buffer +RenderAnimationToLedBuffer (animation_system* System, + pattern_args PatternArgs, + animation_frame CurrFrame, + layer_led_buffer* LayerBuffers, + led_buffer* AssemblyLedBuffer, + animation_pattern_array Patterns, + gs_memory_arena* Transient) +{ + led_buffer AccBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); + + // Create the LayerLEDBuffers + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + layer_led_buffer TempBuffer = {}; + + if (CurrFrame.Layers[Layer].HasHot) + { + TempBuffer.HotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); + + if (CurrFrame.Layers[Layer].HasNextHot) + { + TempBuffer.NextHotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); + } + } + + LayerBuffers[Layer] = TempBuffer; + } + + // Render Each layer's block to the appropriate temp buffer + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; + if (LayerFrame.HasHot) + { + led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer; + animation_block Block = LayerFrame.Hot; + AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs); + } + + if (LayerFrame.HasNextHot) + { + led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer; + animation_block Block = LayerFrame.NextHot; + AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs); + } + } + + // Blend together any layers that have a hot and next hot buffer + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; + layer_led_buffer LayerBuffer = LayerBuffers[Layer]; + if (LayerFrame.HasNextHot) + { + LedBuffer_Blend(LayerBuffer.HotBuffer, + LayerBuffer.NextHotBuffer, + &LayerBuffer.HotBuffer, + LedBlend_Lerp, + (u8*)&LayerFrame.NextHotOpacity); + } + } + + // Consolidate Temp Buffers back into AssemblyLedBuffer + // We do this in reverse order so that they go from top to bottom + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + if (CurrFrame.Layers[Layer].HasHot) + { + led_blend_proc* Blend = LedBlend_GetProc(CurrFrame.Layers[Layer].BlendMode); + LedBuffer_Blend(AccBuffer, + LayerBuffers[Layer].HotBuffer, + &AccBuffer, + Blend, + 0); + } + } + + return AccBuffer; } internal void @@ -101,100 +198,85 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse s32 CurrentFrame = System->CurrentFrame; r32 FrameTime = CurrentFrame * System->SecondsPerFrame; - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, Transient); +#if 1 + animation_array Animations = System->Animations; + animation_fade_group FadeGroup = System->ActiveFadeGroup; - // NOTE(pjs): This mirrors animation_layer_frame to account - // for overlapping - struct layer_led_buffer + animation* FromAnim = AnimationArray_Get(Animations, FadeGroup.From); + animation_frame FromFrame = AnimationSystem_CalculateAnimationFrame(System, FromAnim, Transient); + layer_led_buffer* FromLayerBuffers = PushArray(Transient, layer_led_buffer, FromFrame.LayersCount); + + animation* ToAnim = AnimationArray_Get(Animations, FadeGroup.To); + animation_frame ToFrame = {0}; + layer_led_buffer* ToLayerBuffers = 0; + if (ToAnim) { - led_buffer HotBuffer; - led_buffer NextHotBuffer; - }; - - layer_led_buffer* LayerBuffers = PushArray(Transient, layer_led_buffer, CurrFrame.LayersCount); + ToFrame = AnimationSystem_CalculateAnimationFrame(System, ToAnim, Transient); + ToLayerBuffers = PushArray(Transient, layer_led_buffer, ToFrame.LayersCount); + } for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) { - assembly* Assembly = &Assemblies.Values[AssemblyIndex]; - led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex); + assembly Assembly = Assemblies.Values[AssemblyIndex]; + led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); - // Create the LayerLEDBuffers - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) - { - layer_led_buffer TempBuffer = {}; - if (CurrFrame.Layers[Layer].HasHot) - { - TempBuffer.HotBuffer.LedCount = AssemblyLedBuffer->LedCount; - TempBuffer.HotBuffer.Positions = AssemblyLedBuffer->Positions; - TempBuffer.HotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount); - LedBuffer_ClearToBlack(&TempBuffer.HotBuffer); - } + pattern_args PatternArgs = {}; + PatternArgs.Assembly = Assembly; + PatternArgs.Transient = Transient; + PatternArgs.UserData = UserData; + + led_buffer FromBuffer = RenderAnimationToLedBuffer(System, + PatternArgs, + FromFrame, + FromLayerBuffers, + AssemblyLedBuffer, + Patterns, + Transient); + led_buffer ConsolidatedBuffer = FromBuffer; + + if (ToAnim) { + led_buffer ToBuffer = RenderAnimationToLedBuffer(System, + PatternArgs, + ToFrame, + ToLayerBuffers, + AssemblyLedBuffer, + Patterns, + Transient); - if (CurrFrame.Layers[Layer].HasNextHot) - { - TempBuffer.NextHotBuffer.LedCount = AssemblyLedBuffer->LedCount; - TempBuffer.NextHotBuffer.Positions = AssemblyLedBuffer->Positions; - TempBuffer.NextHotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount); - LedBuffer_ClearToBlack(&TempBuffer.NextHotBuffer); - } - - LayerBuffers[Layer] = TempBuffer; + r32 BlendPercent = FadeGroup.FadeElapsed / FadeGroup.FadeDuration; + LedBuffer_Blend(FromBuffer, ToBuffer, &ConsolidatedBuffer, LedBlend_Lerp, (u8*)&BlendPercent); } - // Render Each layer's block to the appropriate temp buffer - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) - { - animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; - if (LayerFrame.HasHot) - { - led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer; - animation_block Block = LayerFrame.Hot; - AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient, UserData); - } - - if (LayerFrame.HasNextHot) - { - led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer; - animation_block Block = LayerFrame.NextHot; - AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient, UserData); - } - } - - // Blend together any layers that have a hot and next hot buffer - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) - { - animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; - layer_led_buffer LayerBuffer = LayerBuffers[Layer]; - if (LayerFrame.HasNextHot) - { - for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) - { - pixel A = LayerBuffer.HotBuffer.Colors[LED]; - pixel B = LayerBuffer.NextHotBuffer.Colors[LED]; - LayerBuffer.HotBuffer.Colors[LED] = LedBlend_Overwrite(A, B, LayerFrame.HotOpacity); - } - } - } - - // Consolidate Temp Buffers - // We do this in reverse order so that they go from top to bottom - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) - { - if (CurrFrame.Layers[Layer].HasHot) - { - led_blend_proc* Blend = LedBlend_GetProc(ActiveAnim->Layers.Values[Layer].BlendMode); - Assert(Blend != 0); - for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) - { - pixel A = AssemblyLedBuffer->Colors[LED]; - pixel B = LayerBuffers[Layer].HotBuffer.Colors[LED]; - AssemblyLedBuffer->Colors[LED] = Blend(A, B); - } - } - } + LedBuffer_Copy(ConsolidatedBuffer, AssemblyLedBuffer); } +#else + + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); + animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, ActiveAnim, Transient); + + for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) + { + assembly Assembly = Assemblies.Values[AssemblyIndex]; + led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); + + pattern_args PatternArgs = {}; + PatternArgs.Assembly = Assembly; + PatternArgs.Transient = Transient; + PatternArgs.UserData = UserData; + + led_buffer AccBuffer = RenderAnimationToLedBuffer(System, + PatternArgs, + CurrFrame, + LayerBuffers, + AssemblyLedBuffer, + Patterns, + Transient); + LedBuffer_Copy(AccBuffer, AssemblyLedBuffer); + } + +#endif + System->LastUpdatedFrame = System->CurrentFrame; } diff --git a/src/app/engine/assembly/foldhaus_assembly.h b/src/app/engine/assembly/foldhaus_assembly.h index 677d4ff..a14268e 100644 --- a/src/app/engine/assembly/foldhaus_assembly.h +++ b/src/app/engine/assembly/foldhaus_assembly.h @@ -160,6 +160,8 @@ struct assembly_array assembly* Values; }; +typedef pixel led_blend_proc(pixel A, pixel B, u8* UserData); + internal led_buffer* LedSystemGetBuffer(led_system* System, u32 Index) { @@ -178,6 +180,44 @@ LedBuffer_ClearToBlack(led_buffer* Buffer) } } +internal void +LedBuffer_Copy(led_buffer From, led_buffer* To) +{ + Assert(From.LedCount == To->LedCount); + u32 LedCount = To->LedCount; + for (u32 i = 0; i < LedCount; i++) + { + To->Colors[i] = From.Colors[i]; + } +} + +internal void +LedBuffer_Blend(led_buffer A, led_buffer B, led_buffer* Dest, led_blend_proc* BlendProc, u8* UserData) +{ + Assert(A.LedCount == B.LedCount); + Assert(Dest->LedCount == A.LedCount); + Assert(BlendProc); + + u32 LedCount = Dest->LedCount; + for (u32 i = 0; i < LedCount; i++) + { + pixel PA = A.Colors[i]; + pixel PB = B.Colors[i]; + Dest->Colors[i] = BlendProc(PA, PB, UserData); + } +} + +internal led_buffer +LedBuffer_CreateCopyCleared (led_buffer Buffer, gs_memory_arena* Arena) +{ + led_buffer Result = {}; + Result.LedCount = Buffer.LedCount; + Result.Positions = Buffer.Positions; + Result.Colors = PushArray(Arena, pixel, Buffer.LedCount); + LedBuffer_ClearToBlack(&Result); + return Result; +} + internal u32 StripGenData_CountLeds(strip_gen_data Data) { diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 9112a93..df1df6a 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -62,6 +62,14 @@ INITIALIZE_APPLICATION(InitializeApplication) GlobalDebugServices->Interface.RenderSculpture = true; PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); + + State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext); + + State->UserSpaceDesc = BlumenLumen_UserSpaceCreate(); + + ReloadStaticData(Context, GlobalDebugServices); + US_CustomInit(&State->UserSpaceDesc, State, Context); + { // NOTE(pjs): This just sets up the default panel layout panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context); @@ -83,13 +91,6 @@ INITIALIZE_APPLICATION(InitializeApplication) Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, Context); } - - State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext); - - State->UserSpaceDesc = BlumenLumen_UserSpaceCreate(); - - ReloadStaticData(Context, GlobalDebugServices); - US_CustomInit(&State->UserSpaceDesc, State, Context); } UPDATE_AND_RENDER(UpdateAndRender) @@ -105,7 +106,7 @@ UPDATE_AND_RENDER(UpdateAndRender) Editor_Update(State, Context, InputQueue); - AnimationSystem_Update(&State->AnimationSystem); + AnimationSystem_Update(&State->AnimationSystem, Context->DeltaTime); if (AnimationSystem_NeedsRender(State->AnimationSystem)) { AnimationSystem_RenderToLedBuffers(&State->AnimationSystem, diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index b42c8c9..1a51d46 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -260,6 +260,10 @@ Win32SocketPeek(platform_socket* Socket) { }break; + case WSAECONNRESET: + { + }break; + InvalidDefaultCase; } } From 01d960ca8f0ced962f1e7af8856e8bb2bd220ef7 Mon Sep 17 00:00:00 2001 From: PS Date: Wed, 17 Mar 2021 22:48:55 -0700 Subject: [PATCH 007/151] Added ui for adding layers --- src/app/blumen_lumen.cpp | 2 + .../foldhaus_panel_animation_timeline.h | 54 +++++++++++++++---- src/app/engine/animation/foldhaus_animation.h | 10 ++-- .../foldhaus_animation_serializer.cpp | 4 +- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/app/blumen_lumen.cpp b/src/app/blumen_lumen.cpp index d85e3bc..09cf12d 100644 --- a/src/app/blumen_lumen.cpp +++ b/src/app/blumen_lumen.cpp @@ -191,6 +191,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; MotorTimeElapsed += Context->DeltaTime; +#if 0 BLState->TimeElapsed += Context->DeltaTime; if (BLState->TimeElapsed > 5) @@ -200,6 +201,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, Next, 5); BLState->TimeElapsed = 0; } +#endif gs_string BlueString = MakeString("blue"); gs_string GreenString = MakeString("green"); diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 9b350c9..974dc04 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -602,6 +602,28 @@ FrameCount_Render(animation_timeline_state* TimelineState, animation* ActiveAnim } } +internal bool +LayerList_DrawLayerButton (ui_interface* Interface, gs_string Name, rect2 Bounds, bool Selected) +{ + bool Result = ui_MouseClickedRect(*Interface, Bounds); + v2 TextPos = { Bounds.Min.x + 6, Bounds.Max.y - 16}; + + v4 BoxColor = WhiteV4; + bool DrawBox = Selected; + if (PointIsInRect(Bounds, Interface->Mouse.Pos)) + { + DrawBox = true; + BoxColor = PinkV4; + } + + if (DrawBox) + { + PushRenderBoundingBox2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, 1, BoxColor); + } + DrawString(Interface->RenderBuffer, Name, Interface->Style.Font, TextPos, WhiteV4); + return Result; +} + internal void LayerList_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { @@ -616,24 +638,23 @@ LayerList_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, if (ActiveAnim) { + v2 LayerTextPos = {}; for (u32 i = 0; i < ActiveAnim->Layers.Count; i++) { anim_layer* Layer = ActiveAnim->Layers.Values + i; - if (ui_MouseClickedRect(*Interface, LayerBounds)) + bool Selected = (TimelineState->SelectedAnimationLayer == i); + if (LayerList_DrawLayerButton(Interface, Layer->Name, LayerBounds, Selected)) { TimelineState->SelectedAnimationLayer = i; } - - v2 LayerTextPos = { LayerBounds.Min.x + 6, LayerBounds.Max.y - 16}; - if (TimelineState->SelectedAnimationLayer == i) - { - PushRenderBoundingBox2D(Interface->RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4); - } - DrawString(Interface->RenderBuffer, Layer->Name, Interface->Style.Font, LayerTextPos, WhiteV4); - LayerBounds = Rect2TranslateY(LayerBounds, LayerDim.y); } + + if (LayerList_DrawLayerButton(Interface, MakeString("+ Add Layer"), LayerBounds, false)) + { + u32 NewLayer = Animation_AddLayer(ActiveAnim, MakeString("[New Layer]"), BlendMode_Add, &State->AnimationSystem); + } } } @@ -830,6 +851,21 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn } ui_EndRow(Interface); + u32 LayerIndex = TimelineState->SelectedAnimationLayer; + anim_layer* SelectedLayer = ActiveAnim->Layers.Values + LayerIndex; + gs_string BlendStr = BlendModeStrings[SelectedLayer->BlendMode]; + if (ui_BeginLabeledDropdown(Interface, MakeString("Blend Mode"), BlendStr)) + { + for (u32 i = 0; i < BlendMode_Count; i++) + { + if (ui_Button(Interface, BlendModeStrings[i])) + { + SelectedLayer->BlendMode = (blend_mode)i; + } + } + } + ui_EndLabeledDropdown(Interface); + animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, TimelineState->SelectedBlockHandle); if (SelectedBlock) { diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 85e541a..0c33c8b 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -47,11 +47,11 @@ enum blend_mode }; // TODO(pjs): This really doesn't belong here -global gs_const_string BlendModeStrings[] = { - ConstString("Overwrite"), - ConstString("Add"), - ConstString("Multiply"), - ConstString("Count"), +global gs_string BlendModeStrings[] = { + MakeString("Overwrite"), + MakeString("Add"), + MakeString("Multiply"), + MakeString("Count"), }; struct anim_layer diff --git a/src/app/engine/animation/foldhaus_animation_serializer.cpp b/src/app/engine/animation/foldhaus_animation_serializer.cpp index 7e688f2..effbcda 100644 --- a/src/app/engine/animation/foldhaus_animation_serializer.cpp +++ b/src/app/engine/animation/foldhaus_animation_serializer.cpp @@ -32,7 +32,7 @@ AnimSerializer_Serialize(animation Anim, animation_pattern_array Patterns, gs_me Serializer_OpenStruct(&Serializer, AnimField_Layer); { Serializer_WriteStringValue(&Serializer, AnimField_LayerName, LayerAt.Name.ConstString); - Serializer_WriteStringValue(&Serializer, AnimField_LayerBlendMode, BlendModeStrings[LayerAt.BlendMode]); + Serializer_WriteStringValue(&Serializer, AnimField_LayerBlendMode, BlendModeStrings[LayerAt.BlendMode].ConstString); } Serializer_CloseStruct(&Serializer); } @@ -116,7 +116,7 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array gs_string BlendModeName = Parser_ReadStringValue(&Parser, AnimField_LayerBlendMode); for (u32 i = 0; i < BlendMode_Count; i++) { - if (StringsEqual(BlendModeName.ConstString, BlendModeStrings[i])) + if (StringsEqual(BlendModeName, BlendModeStrings[i])) { Layer.BlendMode = (blend_mode)i; break; From bf72a521424cd6388af119989d74a4e07919e8ed Mon Sep 17 00:00:00 2001 From: PS Date: Wed, 17 Mar 2021 22:56:52 -0700 Subject: [PATCH 008/151] Fixed backspacing condition in interface.h and added ui for renaming layers. --- src/app/editor/panels/foldhaus_panel_animation_timeline.h | 6 ++++++ src/app/interface.h | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 974dc04..9209ef0 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -851,8 +851,12 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn } ui_EndRow(Interface); + ui_Label(Interface, MakeString("Layer")); + u32 LayerIndex = TimelineState->SelectedAnimationLayer; anim_layer* SelectedLayer = ActiveAnim->Layers.Values + LayerIndex; + + ui_TextEntry(Interface, MakeString("Layer Name"), &SelectedLayer->Name); gs_string BlendStr = BlendModeStrings[SelectedLayer->BlendMode]; if (ui_BeginLabeledDropdown(Interface, MakeString("Blend Mode"), BlendStr)) { @@ -866,6 +870,8 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn } ui_EndLabeledDropdown(Interface); + ui_Label(Interface, MakeString("Pattern")); + animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, TimelineState->SelectedBlockHandle); if (SelectedBlock) { diff --git a/src/app/interface.h b/src/app/interface.h index f0942a7..a54c669 100644 --- a/src/app/interface.h +++ b/src/app/interface.h @@ -1015,10 +1015,12 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds) for (u32 i = 0; i < Interface->TempInputString.Length; i++) { - if (Interface->TempInputString.Str[i] == '\b' && - State->EditString.Length > 0) + if (Interface->TempInputString.Str[i] == '\b') { - State->EditString.Length -= 1; + if (State->EditString.Length > 0) + { + State->EditString.Length -= 1; + } } else { From 3a04aab4fd88c6d10ddd3bb5fcc7e729dddafb78 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 18 Mar 2021 00:18:58 -0700 Subject: [PATCH 009/151] Updated tests for strings, and added cursor to widget strings if the widget is currently being edited --- src/app/editor/foldhaus_editor_draw.h | 24 +++- .../foldhaus_panel_animation_timeline.h | 12 +- .../panels/foldhaus_panel_sculpture_view.h | 2 +- src/app/foldhaus_renderer.h | 7 + src/app/interface.h | 100 ++++--------- src/gs_libs/gs_types.cpp | 134 ++++++++++++++---- src/tests/sanity_tests.cpp | 62 ++++++++ 7 files changed, 231 insertions(+), 110 deletions(-) diff --git a/src/app/editor/foldhaus_editor_draw.h b/src/app/editor/foldhaus_editor_draw.h index 00c7949..065bc21 100644 --- a/src/app/editor/foldhaus_editor_draw.h +++ b/src/app/editor/foldhaus_editor_draw.h @@ -6,12 +6,12 @@ #ifndef FOLDHAUS_EDITOR_DRAW_H internal void -Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ClippingBox, v4 Color) +Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ClippingBox, v4 Color, s32 CursorPosition) { gs_string Temp = PushString(State->Transient, 256); PrintF(&Temp, "%d", Widget.Id.Id); render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, - Widget.String.Length, + Widget.String.Length + 1, State->Interface.Style.Font->BitmapMemory, State->Interface.Style.Font->BitmapTextureHandle, State->Interface.Style.Font->BitmapWidth, @@ -25,7 +25,8 @@ Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffe { case Align_Left: { - RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color); + RegisterPosition = DrawStringLeftAligned(RenderBuffer, + &BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color, CursorPosition, GreenV4); }break; case Align_Right: @@ -82,6 +83,8 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren rect2 WidgetParentUnion = Widget.Bounds; WidgetParentUnion = Rect2Union(Widget.Bounds, ParentClipBounds); + bool IsActiveWidget = ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget); + ; if (!Widget.Parent || (Rect2Area(WidgetParentUnion) > 0)) { if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground)) @@ -101,7 +104,13 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0) { v4 Color = State->Interface.Style.TextColor; - Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, Color); + s32 CursorPosition = -1; + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_Typable) && IsActiveWidget) + { + CursorPosition = State->Interface.CursorPosition; + } + + Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, Color, CursorPosition); } if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill) || @@ -122,11 +131,14 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren { // TODO(pjs): add this color to the style v4 TextColor = BlackV4; - Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, ClippedFillBounds, TextColor); + Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, ClippedFillBounds, TextColor, -1); } } - if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline)) + bool DrawOutline = ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline); + DrawOutline |= ui_WidgetIsFlagSet(Widget, UIWidgetFlag_Typable) && IsActiveWidget; + + if (DrawOutline) { // TODO(pjs): replace these with values from the style r32 Thickness = 1.0f; diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 9209ef0..886b1a3 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -335,7 +335,7 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames); r32 FrameX = LerpR32(FramePercent, BarBounds.Min.x, BarBounds.Max.x); v2 FrameTextPos = v2{FrameX, BarBounds.Min.y + 2}; - DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, FrameTextPos, WhiteV4); + DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, FrameTextPos, WhiteV4, -1, GreenV4); } // Time Slider @@ -352,7 +352,7 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r v2 HeadMin = v2{SliderX - SliderHalfWidth, BarBounds.Min.y}; v2 HeadMax = v2{SliderX + SliderHalfWidth, BarBounds.Max.y}; PushRenderQuad2D(Interface.RenderBuffer, HeadMin, HeadMax, TimeSliderColor); - DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, HeadMin + v2{6, 4}, WhiteV4); + DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, HeadMin + v2{6, 4}, WhiteV4, -1, GreenV4); } } @@ -465,7 +465,7 @@ DrawLayerMenu(animation_system* AnimationSystem, animation ActiveAnim, ui_interf { PushRenderBoundingBox2D(Interface.RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4); } - DrawString(Interface.RenderBuffer, Layer->Name, Interface.Style.Font, LayerTextPos, WhiteV4); + DrawString(Interface.RenderBuffer, Layer->Name, Interface.Style.Font, LayerTextPos, WhiteV4, -1, GreenV4); } } @@ -573,7 +573,7 @@ FrameCount_Render(animation_timeline_state* TimelineState, animation* ActiveAnim r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames); r32 FrameX = LerpR32(FramePercent, Bounds.Min.x, Bounds.Max.x); v2 FrameTextPos = v2{FrameX, Bounds.Min.y + 2}; - DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, FrameTextPos, WhiteV4); + DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, FrameTextPos, WhiteV4, -1, GreenV4); } // Time Slider @@ -591,7 +591,7 @@ FrameCount_Render(animation_timeline_state* TimelineState, animation* ActiveAnim v2 HeadMin = v2{SliderX - SliderHalfWidth, Bounds.Min.y}; v2 HeadMax = v2{SliderX + SliderHalfWidth, Bounds.Max.y}; PushRenderQuad2D(Interface->RenderBuffer, HeadMin, HeadMax, TimeSliderColor); - DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, HeadMin + v2{6, 4}, WhiteV4); + DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, HeadMin + v2{6, 4}, WhiteV4, -1, GreenV4); } // Interaction @@ -620,7 +620,7 @@ LayerList_DrawLayerButton (ui_interface* Interface, gs_string Name, rect2 Bounds { PushRenderBoundingBox2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, 1, BoxColor); } - DrawString(Interface->RenderBuffer, Name, Interface->Style.Font, TextPos, WhiteV4); + DrawString(Interface->RenderBuffer, Name, Interface->Style.Font, TextPos, WhiteV4, -1, GreenV4); return Result; } diff --git a/src/app/editor/panels/foldhaus_panel_sculpture_view.h b/src/app/editor/panels/foldhaus_panel_sculpture_view.h index 6d4c803..ad51493 100644 --- a/src/app/editor/panels/foldhaus_panel_sculpture_view.h +++ b/src/app/editor/panels/foldhaus_panel_sculpture_view.h @@ -234,7 +234,7 @@ SculptureView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren gs_string Tempgs_string = PushString(State->Transient, 256); PrintF(&Tempgs_string, "Hot Id: %u, ZIndex: %u | Active Id: %u", State->Interface.HotWidget.Id, State->Interface.HotWidget.ZIndex,State->Interface.ActiveWidget.Id); - DrawString(RenderBuffer, Tempgs_string, State->Interface.Style.Font, v2{PanelBounds.Min.x + 100, PanelBounds.Max.y - 200}, WhiteV4); + DrawString(RenderBuffer, Tempgs_string, State->Interface.Style.Font, v2{PanelBounds.Min.x + 100, PanelBounds.Max.y - 200}, WhiteV4, -1, GreenV4); } Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); diff --git a/src/app/foldhaus_renderer.h b/src/app/foldhaus_renderer.h index a40c0f4..7e25346 100644 --- a/src/app/foldhaus_renderer.h +++ b/src/app/foldhaus_renderer.h @@ -487,6 +487,13 @@ PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, v2 Min, v2 Max, v v2{0, 0}, v2{1, 1}, Color); } +internal void +PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, rect2 Rect, v4 Color) +{ + PushQuad2DOnBatch(Constructor, v2{Rect.Min.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Max.y}, v2{Rect.Min.x, Rect.Max.y}, + v2{0, 0}, v2{1, 1}, Color); +} + internal void PushLine2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, r32 Thickness, v4 Color) { diff --git a/src/app/interface.h b/src/app/interface.h index a54c669..566b1ad 100644 --- a/src/app/interface.h +++ b/src/app/interface.h @@ -99,17 +99,35 @@ DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char return PointAfterCharacter; } +internal void +DrawCursor (render_command_buffer* RenderBuffer, v2 RegisterPosition, bitmap_font* Font, v4 Color) +{ + rect2 CursorRect = {}; + CursorRect.Min = RegisterPosition; + CursorRect.Max = CursorRect.Min + v2{5, (r32)Font->Ascent}; + PushRenderQuad2D(RenderBuffer, CursorRect, Color); +} + internal v2 -DrawStringLeftAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color) +DrawStringLeftAligned (render_command_buffer* RenderBuffer, render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color, s32 CursorBeforeIndex, v4 CursorColor) { v2 RegisterPosition = InitialRegisterPosition; char* C = gs_string; for (s32 i = 0; i < Length; i++) { + if (i == CursorBeforeIndex) + { + DrawCursor(RenderBuffer, RegisterPosition, Font, CursorColor); + } + v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color); RegisterPosition.x = PositionAfterCharacter.x; C++; } + if (CursorBeforeIndex == Length) + { + DrawCursor(RenderBuffer, RegisterPosition, Font, CursorColor); + } return RegisterPosition; } @@ -128,12 +146,12 @@ DrawStringRightAligned (render_quad_batch_constructor* BatchConstructor, s32 Len } internal v2 -DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* Font, v2 Position, v4 Color, gs_string_alignment Alignment = Align_Left) +DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* Font, v2 Position, v4 Color, s32 CursorPosition, v4 CursorColor, gs_string_alignment Alignment = Align_Left) { DEBUG_TRACK_FUNCTION; v2 LowerRight = Position; - render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length, + render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length + 1, Font->BitmapMemory, Font->BitmapTextureHandle, Font->BitmapWidth, @@ -152,7 +170,7 @@ DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* F v2 RegisterPosition = Position; if (Alignment == Align_Left) { - RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color); + RegisterPosition = DrawStringLeftAligned(RenderBuffer, &BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color, CursorPosition, CursorColor); } else if (Alignment == Align_Right) { @@ -359,6 +377,7 @@ struct ui_interface // A per-frame string of the characters which have been typed gs_const_string TempInputString; + u64 CursorPosition; render_command_buffer* RenderBuffer; @@ -980,6 +999,11 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds) { Result.Clicked = true; Interface->ActiveWidget = Widget->Id; + + if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable)) + { + Interface->CursorPosition = Widget->String.Length; + } } } @@ -1013,11 +1037,12 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds) { ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + Interface->CursorPosition = Clamp(0, Interface->CursorPosition, State->EditString.Length); for (u32 i = 0; i < Interface->TempInputString.Length; i++) { if (Interface->TempInputString.Str[i] == '\b') { - if (State->EditString.Length > 0) + if (Interface->CursorPosition > 0) { State->EditString.Length -= 1; } @@ -1030,71 +1055,6 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds) } } -#if 0 - // if you can click it - if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Clickable)) - { - // updating hot widget, and handling mouse clicks - if (PointIsInRect(Widget->Parent->Bounds, Interface->Mouse.Pos) && - PointIsInRect(Widget->Bounds, Interface->Mouse.Pos)) - { - if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id) && MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState)) - { - Result.Clicked = true; - Interface->ActiveWidget = Widget->Id; - } - - Interface->HotWidget = Widget->Id; - } - - // click and drag - if (MouseButtonHeldDown(Interface->Mouse.LeftButtonState) && - PointIsInRect(Widget->Bounds, Interface->Mouse.DownPos)) - { - Result.Held = true; - Result.DragDelta = Interface->Mouse.Pos - Interface->Mouse.DownPos; - } - - // if this is the active widget (its been clicked) - if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id)) - { - // if you can select it - if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Selectable)) - { - // - if (MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState) && - !PointIsInRect(Widget->Bounds, Interface->Mouse.Pos)) - { - Interface->ActiveWidget = {}; - } - - if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable) && - Interface->TempInputString.Length > 0) - { - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); - - // TODO(pjs): Backspace? - for (u32 i = 0; i < Interface->TempInputString.Length; i++) - { - if (Interface->TempInputString.Str[i] == '\b') - { - State->EditString.Length -= 1; - } - else - { - OutChar(&State->EditString, Interface->TempInputString.Str[i]); - } - } - } - } - else if (MouseButtonTransitionedUp(Interface->Mouse.LeftButtonState)) - { - Interface->ActiveWidget = {}; - } - } - } -#endif - Assert(Widget->Parent != 0); return Result; } diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index 197c270..cbd0678 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -1522,54 +1522,91 @@ Substring(gs_const_string String, u64 First, u64 Last) Result.Length = Min(Last - First, String.Length); return Result; } -internal u64 +internal gs_const_string +Substring(gs_string String, u64 First, u64 Last) +{ + return Substring(String.ConstString, First, Last); +} + +internal s64 FindFirst(gs_const_string String, u64 StartIndex, char C) { - u64 Result = StartIndex; - for(; Result < String.Length && C != String.Str[Result]; Result++); + s64 Result = -1; + for(u64 i = StartIndex; i < String.Length; i++) + { + if (String.Str[i] == C) { + Result = (s64)i; + break; + } + } return Result; } -internal u64 +internal s64 FindFirst(gs_const_string String, char C) { return FindFirst(String, 0, C); } - -internal u64 -FindLast(gs_const_string String, u64 StartIndex, char C) +internal s64 +FindFirst(gs_string String, u64 StartIndex, char C) { - s64 Result = StartIndex; - for(; Result >= 0 && C != String.Str[Result]; Result--); - return (u64)Result; + return FindFirst(String.ConstString, StartIndex, C); +} +internal s64 +FindFirst(gs_string String, char C) +{ + return FindFirst(String.ConstString, 0, C); } -internal u64 +internal s64 +FindLast(gs_const_string String, u64 StartIndex, char C) +{ + s64 Result = -1; + for(s64 i= StartIndex; i >= 0; i--) + { + if (String.Str[i] == C) { + Result = i; + break; + } + } + return (u64)Result; +} +internal s64 FindLast(gs_const_string String, char C) { return FindLast(String, String.Length - 1, C); } +internal s64 +FindLast(gs_string String, u64 StartIndex, char C) +{ + return FindLast(String.ConstString, StartIndex, C); +} +internal s64 +FindLast(gs_string String, char C) +{ + return FindLast(String.ConstString, String.Length - 1, C); +} -internal u64 +internal s64 FindFirstFromSet(gs_const_string String, char* SetArray) { gs_const_string Set = ConstString(SetArray); - u64 Result = String.Length - 1; - for(u64 At = 0; At < String.Length; At++) + s64 Result = -1; + + s64 CurrMin = String.Length; + for (u64 SetAt = 0; SetAt < Set.Length; SetAt++) { - char CharAt = String.Str[At]; - for (u64 SetAt = 0; SetAt < Set.Length; SetAt++) + s64 Index = FindFirst(String, Set.Str[SetAt]); + if (Index >= 0 && Index < CurrMin) { - if (CharAt == Set.Str[SetAt]) - { - Result = At; - // NOTE(Peter): The alternative to this goto is a break in the inner loop - // followed by an if check in the outer loop, that must be evaluated - // every character you check. This is more efficient - goto find_last_from_set_complete; - } + CurrMin = Index; } } - find_last_from_set_complete: + + if (CurrMin < (s64)String.Length) + { + Result = CurrMin; + } + return Result; } @@ -1648,6 +1685,12 @@ StringEqualsCharArray(gs_const_string A, char* B, u64 Length) return StringsEqual(A, BStr); } internal bool +StringEqualsCharArray(gs_const_string A, char* B) +{ + u64 Length = CStringLength(B); + return StringEqualsCharArray(A, B, Length); +} +internal bool StringsEqualUpToLength(gs_string A, gs_string B, u64 Length) { return StringsEqualUpToLength(A.ConstString, B.ConstString, Length); @@ -1660,8 +1703,12 @@ StringsEqual(gs_string A, gs_string B) internal bool StringEqualsCharArray(gs_string A, char* B, u64 Length) { - gs_string BStr = MakeString(B, Length); - return StringsEqual(A, BStr); + return StringEqualsCharArray(A.ConstString, B, Length); +} +internal bool +StringEqualsCharArray(gs_string A, char* B) +{ + return StringEqualsCharArray(A.ConstString, B); } internal u64 @@ -1907,6 +1954,39 @@ AppendString(gs_string* Base, gs_string Appendix) { return AppendString(Base, Appendix.ConstString); } + +internal void +InsertAt(gs_string* Str, u64 Index, char C) +{ + if (Str->Length > Index) + { + for (u64 i = Str->Length; i > Index; i--) + { + Str->Str[i] = Str->Str[i - 1]; + } + } + + if (Index < Str->Size) + { + Str->Str[Index] = C; + Str->Length += 1; + Assert(Str->Length < Str->Size); + } +} + +internal void +RemoveAt(gs_string* Str, u64 Index) +{ + if (Str->Length > 0 && Index < Str->Length) + { + for (u64 i = Index; i < Str->Length - 1; i++) + { + Str->Str[i] = Str->Str[i + 1]; + } + Str->Length -= 1; + } +} + internal void NullTerminate(gs_string* String) { diff --git a/src/tests/sanity_tests.cpp b/src/tests/sanity_tests.cpp index 281c1af..6130f5d 100644 --- a/src/tests/sanity_tests.cpp +++ b/src/tests/sanity_tests.cpp @@ -16,6 +16,15 @@ gs_memory_arena Scratch = {}; void* Alloc(u64 Size, u64* ResultSize) { *ResultSize = Size; return malloc(Size); } void Free(void* Ptr, u64 Size) { return free(Ptr); } +bool StringTest (gs_const_string StrA, gs_const_string StrB) +{ + return StringsEqual(StrA, StrB); +} +bool StringTest (gs_string StrA, gs_string StrB) +{ + return StringsEqual(StrA, StrB); +} + bool PathTest (char* In, char* Out) { return StringsEqual(SanitizePath(ConstString(In), &Scratch), ConstString(Out)); } @@ -24,6 +33,59 @@ int main (int ArgCount, char** Args) { Scratch = CreateMemoryArena(CreateAllocator(Alloc, Free)); + Test("gs_string") + { + gs_string TestString = PushStringF(&Scratch, 256, "Hello there, Sailor!"); + + NullTerminate(&TestString); + TestResult(IsNullTerminated(TestString)); + + TestResult(StringTest(GetStringPrefix(TestString.ConstString, 5), ConstString("Hello"))); + TestResult(StringTest(GetStringPostfix(TestString.ConstString, 5), ConstString("ilor!"))); + TestResult(StringTest(GetStringAfter(TestString.ConstString, 13), ConstString("Sailor!"))); + TestResult(StringTest(GetStringBefore(TestString.ConstString, 5), ConstString("Hello"))); + TestResult(StringTest(Substring(TestString.ConstString, 5, 11), ConstString(" there"))); + + TestResult(FindFirst(TestString, 5, 'l') == 16); + TestResult(FindFirst(TestString, 0, 'k') == -1); + TestResult(FindLast(TestString, 10, 'l') == 3); + TestResult(FindLast(TestString, 'k') == -1); + + TestResult(FindFirstFromSet(TestString.ConstString, "re") == 1); + TestResult(FindFirstFromSet(TestString.ConstString, "er") == 1); + TestResult(FindFirstFromSet(TestString.ConstString, "bk") == -1); + TestResult(FindFirstFromSet(TestString.ConstString, "ek") == 1); + + TestResult(FindLastFromSet(TestString.ConstString, "re") == 18); + TestResult(FindLastFromSet(TestString.ConstString, "er") == 18); + TestResult(FindLastFromSet(TestString.ConstString, "bk") == -1); + TestResult(FindLastFromSet(TestString.ConstString, "rk") == 18); + + TestResult(StringContains(TestString.ConstString, ',')); + TestResult(!StringContains(TestString.ConstString, '@')); + TestResult(StringsEqual(TestString, TestString)); + + TestResult(StringEqualsCharArray(TestString, "Hello there, Sailor!")); + TestResult(!StringEqualsCharArray(TestString, "Hello there, Sailor")); + TestResult(!StringEqualsCharArray(TestString, "Foobar")); + + ReverseStringInPlace(&TestString); + TestResult(StringTest(TestString, MakeString("!roliaS ,ereht olleH"))); + ReverseStringInPlace(&TestString); + + TestResult(ParseUInt(ConstString("532")) == 532); + TestResult(ParseInt(ConstString("-1234567890")) == -1234567890); + TestResult(ParseFloat(ConstString("-12345.6789")) == -12345.6789); + + TestString.Length = 0; + U64ToASCII(&TestString, 53298, 10); + TestResult(StringTest(TestString.ConstString, ConstString("53298"))); + + TestString.Length = 0; + R64ToASCII(&TestString, 145732.321, 2); + TestResult(StringTest(TestString.ConstString, ConstString("145732.32"))); + } + Test("gs_path.h") { TestResult(PathTest(".", ".")); From 4f199ee1c6cf3bda338e1c3e654a48d8da7389a0 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 18 Mar 2021 00:26:47 -0700 Subject: [PATCH 010/151] Fixed a problem with r64 parsing --- src/gs_libs/gs_types.cpp | 10 +++++++--- src/tests/sanity_tests.cpp | 6 ++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index cbd0678..3f444fa 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -1885,12 +1885,16 @@ ValidateAndParseFloat(gs_const_string String) if (StringIsValid) { - u64 DecimalIndex = FindFirst(String, '.'); + s64 DecimalIndex = FindFirst(String, '.'); u64 TempParsedLength = 0; u64 PlacesAfterPoint = 0; gs_const_string IntegerString = GetStringBefore(String, DecimalIndex); - gs_const_string DecimalString = GetStringAfter(String, DecimalIndex + 1); + gs_const_string DecimalString = {}; + if (DecimalIndex >= 0) + { + DecimalString = GetStringAfter(String, DecimalIndex + 1); + } r32 Polarity = 1; if (IntegerString.Str[0] == '-') @@ -1910,7 +1914,7 @@ ValidateAndParseFloat(gs_const_string String) } Result.ParsedLength = TempParsedLength + PlacesAfterPoint; - if (DecimalIndex < String.Length) { Result.ParsedLength += 1; } + if (DecimalIndex < (s64)String.Length) { Result.ParsedLength += 1; } Result.Success = true; } diff --git a/src/tests/sanity_tests.cpp b/src/tests/sanity_tests.cpp index 6130f5d..3d37c1a 100644 --- a/src/tests/sanity_tests.cpp +++ b/src/tests/sanity_tests.cpp @@ -76,14 +76,16 @@ int main (int ArgCount, char** Args) TestResult(ParseUInt(ConstString("532")) == 532); TestResult(ParseInt(ConstString("-1234567890")) == -1234567890); TestResult(ParseFloat(ConstString("-12345.6789")) == -12345.6789); + TestResult(ParseFloat(ConstString("-1")) == -1); + TestResult(ParseFloat(ConstString("-.035")) == -.035); TestString.Length = 0; U64ToASCII(&TestString, 53298, 10); TestResult(StringTest(TestString.ConstString, ConstString("53298"))); TestString.Length = 0; - R64ToASCII(&TestString, 145732.321, 2); - TestResult(StringTest(TestString.ConstString, ConstString("145732.32"))); + R64ToASCII(&TestString, -145732.321, 2); + TestResult(StringTest(TestString.ConstString, ConstString("-145732.32"))); } Test("gs_path.h") From c054a0e6b650933e4c71fad47bb11779c8c53d7f Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 18 Mar 2021 02:19:35 -0700 Subject: [PATCH 011/151] Memory profiling & codebase cleanup --- compile.bat | 2 + msdev.bat | 3 - src/app/editor/foldhaus_editor.cpp | 4 +- src/app/editor/foldhaus_editor_draw.h | 36 +- src/app/{ => editor}/interface.h | 2 +- .../editor/panels/foldhaus_panel_file_view.h | 2 +- .../editor/panels/foldhaus_panel_profiler.h | 71 +- src/app/engine/assembly/foldhaus_assembly.cpp | 2 +- src/app/engine/foldhaus_addressed_data.h | 2 +- src/app/foldhaus_app.cpp | 14 +- src/app/foldhaus_app.h | 8 +- src/app/foldhaus_debug.h | 10 +- src/app/handmade_math.h | 3239 ----------------- src/app/platform_win32/win32_foldhaus.cpp | 8 +- .../platform_win32/win32_foldhaus_serial.h | 6 +- .../win32_foldhaus_work_queue.h | 2 +- .../{ => ss_blumen_lumen}/blumen_lumen.cpp | 3 - src/app/{ => ss_blumen_lumen}/blumen_lumen.h | 0 src/app/sse_mathfun.h | 711 ---- src/app/sse_mathfun_extension.h | 360 -- src/gs_libs/gs_types.cpp | 141 +- src/gs_libs/gs_types.h | 43 +- src/{app => tests}/interface_test.cpp | 0 src/tests/sanity_tests.cpp | 2 +- src/{app => tests}/test_patterns.h | 0 25 files changed, 272 insertions(+), 4399 deletions(-) create mode 100644 compile.bat delete mode 100644 msdev.bat rename src/app/{ => editor}/interface.h (99%) delete mode 100644 src/app/handmade_math.h rename src/app/{ => ss_blumen_lumen}/blumen_lumen.cpp (98%) rename src/app/{ => ss_blumen_lumen}/blumen_lumen.h (100%) delete mode 100644 src/app/sse_mathfun.h delete mode 100644 src/app/sse_mathfun_extension.h rename src/{app => tests}/interface_test.cpp (100%) rename src/{app => tests}/test_patterns.h (100%) diff --git a/compile.bat b/compile.bat new file mode 100644 index 0000000..b623ea0 --- /dev/null +++ b/compile.bat @@ -0,0 +1,2 @@ +@echo off +build\build_app_msvc_win32_debug.bat \ No newline at end of file diff --git a/msdev.bat b/msdev.bat deleted file mode 100644 index 31ca5aa..0000000 --- a/msdev.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off - -remedybg build\win32_foldhaus.rdbg \ No newline at end of file diff --git a/src/app/editor/foldhaus_editor.cpp b/src/app/editor/foldhaus_editor.cpp index 6d71ca3..d6abf2e 100644 --- a/src/app/editor/foldhaus_editor.cpp +++ b/src/app/editor/foldhaus_editor.cpp @@ -144,8 +144,8 @@ Editor_Render(app_state* State, context* Context, render_command_buffer* RenderB // Draw the Interface if (State->Interface.DrawOrderRoot != 0) { - ui_widget Widget = *State->Interface.DrawOrderRoot; - Editor_DrawWidget(State, Context, RenderBuffer, Widget, Context->WindowBounds); + ui_widget* Widget = State->Interface.DrawOrderRoot; + Editor_DrawWidgetList(State, Context, RenderBuffer, Widget, Context->WindowBounds); } Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext); diff --git a/src/app/editor/foldhaus_editor_draw.h b/src/app/editor/foldhaus_editor_draw.h index 065bc21..f4a4edc 100644 --- a/src/app/editor/foldhaus_editor_draw.h +++ b/src/app/editor/foldhaus_editor_draw.h @@ -77,12 +77,11 @@ Editor_GetWidgetFillBounds(ui_widget Widget) return Result; } +internal void Editor_DrawWidgetList(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ParentClipBounds); + internal void -Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ParentClipBounds) +Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 WidgetParentUnion) { - rect2 WidgetParentUnion = Widget.Bounds; - WidgetParentUnion = Rect2Union(Widget.Bounds, ParentClipBounds); - bool IsActiveWidget = ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget); ; if (!Widget.Parent || (Rect2Area(WidgetParentUnion) > 0)) @@ -146,18 +145,27 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren PushRenderBoundingBox2D(RenderBuffer, WidgetParentUnion.Min, WidgetParentUnion.Max, Thickness, Color); } } - - if (Widget.ChildrenRoot) - { - Editor_DrawWidget(State, Context, RenderBuffer, *Widget.ChildrenRoot, WidgetParentUnion); - } - - if (Widget.Next) - { - Editor_DrawWidget(State, Context, RenderBuffer, *Widget.Next, ParentClipBounds); - } } +internal void Editor_DrawWidgetList(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget* Widget, rect2 ParentClipBounds) +{ + ui_widget* WidgetAt = Widget; + while (WidgetAt) + { + rect2 WidgetParentUnion = WidgetAt->Bounds; + WidgetParentUnion = Rect2Union(WidgetAt->Bounds, ParentClipBounds); + + Editor_DrawWidget(State, Context, RenderBuffer, *WidgetAt, WidgetParentUnion); + + if (WidgetAt->ChildrenRoot) + { + Editor_DrawWidgetList(State, Context, RenderBuffer, WidgetAt->ChildrenRoot, WidgetParentUnion); + } + + WidgetAt = WidgetAt->Next; + } +} + #define FOLDHAUS_EDITOR_DRAW_H #endif // FOLDHAUS_EDITOR_DRAW_H \ No newline at end of file diff --git a/src/app/interface.h b/src/app/editor/interface.h similarity index 99% rename from src/app/interface.h rename to src/app/editor/interface.h index 566b1ad..6b91bef 100644 --- a/src/app/interface.h +++ b/src/app/editor/interface.h @@ -1582,7 +1582,7 @@ ui_InterfaceCreate(context Context, interface_config Style, gs_memory_arena* Per Result.WidgetsCountMax = 4096; Result.Widgets = PushArray(Permanent, ui_widget, Result.WidgetsCountMax); Result.PerFrameMemory = PushStruct(Permanent, gs_memory_arena); - *Result.PerFrameMemory = CreateMemoryArena(Context.ThreadContext.Allocator); + *Result.PerFrameMemory = CreateMemoryArena(Context.ThreadContext.Allocator, "Interface Per Frame Memory Arena", KB(32)); InterfaceAssert(Result.PerFrameMemory); Result.Permanent = Permanent; diff --git a/src/app/editor/panels/foldhaus_panel_file_view.h b/src/app/editor/panels/foldhaus_panel_file_view.h index 0910e66..050bd0a 100644 --- a/src/app/editor/panels/foldhaus_panel_file_view.h +++ b/src/app/editor/panels/foldhaus_panel_file_view.h @@ -90,7 +90,7 @@ FileView_Init(panel* Panel, app_state* State, context Context) // TODO: :FreePanelMemory file_view_state* FileViewState = PushStruct(&State->Permanent, file_view_state); Panel->StateMemory = StructToData(FileViewState, file_view_state); - FileViewState->FileNamesArena = CreateMemoryArena(Context.ThreadContext.Allocator); + FileViewState->FileNamesArena = CreateMemoryArena(Context.ThreadContext.Allocator, "File View - File Names Arena"); // TODO(pjs): this shouldn't be stored in permanent FileViewState->DisplayDirectory = PushString(&State->Permanent, 1024); diff --git a/src/app/editor/panels/foldhaus_panel_profiler.h b/src/app/editor/panels/foldhaus_panel_profiler.h index 405b50f..82e7d6f 100644 --- a/src/app/editor/panels/foldhaus_panel_profiler.h +++ b/src/app/editor/panels/foldhaus_panel_profiler.h @@ -163,6 +163,44 @@ RenderProfiler_ListVisualization(ui_interface* Interface, ui_widget* Layout, deb ui_EndList(Interface); } +internal void +RenderProfiler_MemoryView(ui_interface* Interface, ui_widget* Layout, app_state* State, context Context, gs_memory_arena* Memory) +{ + gs_allocator_debug Debug = *Context.ThreadContext.Allocator.Debug; + gs_string TempString = PushString(State->Transient, 256); + + u64 MemFootprint = Debug.TotalAllocSize; + u64 AllocCount = Debug.AllocationsCount; + PrintF(&TempString, "Total Memory Size: %lld | Allocations: %lld", MemFootprint, AllocCount); + ui_Label(Interface, TempString); + + ui_column_spec ColumnWidths[] = { + { UIColumnSize_Fill, 0 }, + { UIColumnSize_Fixed,256 }, + }; + ui_BeginRow(Interface, 2, &ColumnWidths[0]); + { + ui_Label(Interface, MakeString("Location")); + ui_Label(Interface, MakeString("Alloc Size")); + } + ui_EndRow(Interface); + + ui_BeginList(Interface, MakeString("Alloc List"), 10, Debug.AllocationsCount); + ui_BeginRow(Interface, 2, &ColumnWidths[0]); + for (s32 n = 0; n < Debug.AllocationsCount; n++) + { + gs_debug_allocation A = Debug.Allocations[n]; + + PrintF(&TempString, "%S", A.Location); + ui_Label(Interface, TempString); + + PrintF(&TempString, "%lld bytes", A.Size); + ui_Label(Interface, TempString); + } + ui_EndRow(Interface); + ui_EndList(Interface); +} + GSMetaTag(panel_render); GSMetaTag(panel_type_profiler); internal void @@ -234,24 +272,39 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend ui_BeginRow(&State->Interface, 8); { - if (ui_Button(&State->Interface, MakeString("Scope View"))) + if (ui_Button(&State->Interface, MakeString("Profiler"))) { - GlobalDebugServices->Interface.FrameView = FRAME_VIEW_PROFILER; + GlobalDebugServices->Interface.FrameView = DebugUI_Profiler; } if (ui_Button(&State->Interface, MakeString("List View"))) { - GlobalDebugServices->Interface.FrameView = FRAME_VIEW_SCOPE_LIST; + GlobalDebugServices->Interface.FrameView = DebugUI_ScopeList; + } + if (ui_Button(&State->Interface, MakeString("Memory"))) + { + GlobalDebugServices->Interface.FrameView = DebugUI_MemoryView; } } ui_EndRow(&State->Interface); - if (GlobalDebugServices->Interface.FrameView == FRAME_VIEW_PROFILER) + switch (GlobalDebugServices->Interface.FrameView) { - RenderProfiler_ScopeVisualization(&State->Interface, Layout, VisibleFrame, Memory); - } - else - { - RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory); + case DebugUI_Profiler: + { + RenderProfiler_ScopeVisualization(&State->Interface, Layout, VisibleFrame, Memory); + }break; + + case DebugUI_ScopeList: + { + RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory); + }break; + + case DebugUI_MemoryView: + { + RenderProfiler_MemoryView(&State->Interface, Layout, State, Context, Memory); + }break; + + InvalidDefaultCase; } ui_PopLayout(&State->Interface, MakeString("Profiler Layout")); diff --git a/src/app/engine/assembly/foldhaus_assembly.cpp b/src/app/engine/assembly/foldhaus_assembly.cpp index 4ae1a6c..1a8cb6f 100644 --- a/src/app/engine/assembly/foldhaus_assembly.cpp +++ b/src/app/engine/assembly/foldhaus_assembly.cpp @@ -206,7 +206,7 @@ LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena gs_const_string FileName = Substring(Path, IndexOfLastSlash + 1, Path.Length); assembly* NewAssembly = AssemblyArray_Take(Assemblies); - NewAssembly->Arena = CreateMemoryArena(Context.ThreadContext.Allocator); + NewAssembly->Arena = CreateMemoryArena(Context.ThreadContext.Allocator, "Assembly Arena"); parser AssemblyParser = ParseAssemblyFile(NewAssembly, FileName, AssemblyFileText, Scratch); if (AssemblyParser.Success) diff --git a/src/app/engine/foldhaus_addressed_data.h b/src/app/engine/foldhaus_addressed_data.h index c27bd59..8339b6e 100644 --- a/src/app/engine/foldhaus_addressed_data.h +++ b/src/app/engine/foldhaus_addressed_data.h @@ -100,7 +100,7 @@ AddressedDataBufferList_Create(gs_thread_context TC) { addressed_data_buffer_list Result = {}; Result.Arena = AllocatorAllocStruct(TC.Allocator, gs_memory_arena); - *Result.Arena = CreateMemoryArena(TC.Allocator); + *Result.Arena = CreateMemoryArena(TC.Allocator, "Addressed Data Buffer List Arena"); return Result; } diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index df1df6a..e91070d 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -24,7 +24,7 @@ INITIALIZE_APPLICATION(InitializeApplication) app_state* State = (app_state*)Context.MemoryBase; *State = {}; - State->Permanent = CreateMemoryArena(Context.ThreadContext.Allocator); + State->Permanent = CreateMemoryArena(Context.ThreadContext.Allocator, "Permanent"); State->Transient = Context.ThreadContext.Transient; State->Assemblies = AssemblyArray_Create(8, &State->Permanent); @@ -91,6 +91,8 @@ INITIALIZE_APPLICATION(InitializeApplication) Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, Context); } + + State->RunEditor = true; } UPDATE_AND_RENDER(UpdateAndRender) @@ -104,7 +106,10 @@ UPDATE_AND_RENDER(UpdateAndRender) // incorrect to clear the arena, and then access the memory later. ClearArena(State->Transient); - Editor_Update(State, Context, InputQueue); + if (State->RunEditor) + { + Editor_Update(State, Context, InputQueue); + } AnimationSystem_Update(&State->AnimationSystem, Context->DeltaTime); if (AnimationSystem_NeedsRender(State->AnimationSystem)) @@ -123,7 +128,10 @@ UPDATE_AND_RENDER(UpdateAndRender) State->Assemblies, State->LedSystem); - Editor_Render(State, Context, RenderBuffer); + if (State->RunEditor) + { + Editor_Render(State, Context, RenderBuffer); + } // NOTE(pjs): Building data buffers to be sent out to the sculpture // This array is used on the platform side to actually send the information diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index 43ded68..f18ea3f 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -13,7 +13,7 @@ #include "../gs_libs/gs_font.h" #include "foldhaus_log.h" -#include "interface.h" +#include "editor/interface.h" #include "engine/foldhaus_network_ordering.h" @@ -42,7 +42,7 @@ typedef struct panel panel; #include "engine/animation/foldhaus_animation_renderer.cpp" #include "engine/user_space.h" -#include "blumen_lumen.h" +#include "ss_blumen_lumen/blumen_lumen.h" struct app_state { @@ -72,6 +72,8 @@ struct app_state panel* HotPanel; user_space_desc UserSpaceDesc; + + bool RunEditor; }; internal void OpenColorPicker(app_state* State, v4* Address); @@ -81,7 +83,7 @@ internal void OpenColorPicker(app_state* State, v4* Address); #include "engine/user_space.cpp" #include "patterns/blumen_patterns.h" -#include "blumen_lumen.cpp" +#include "ss_blumen_lumen/blumen_lumen.cpp" internal void EndCurrentOperationMode(app_state* State) diff --git a/src/app/foldhaus_debug.h b/src/app/foldhaus_debug.h index 7acdfcc..c6b7133 100644 --- a/src/app/foldhaus_debug.h +++ b/src/app/foldhaus_debug.h @@ -64,8 +64,14 @@ struct debug_frame collated_scope_record* CollatedScopes; }; -#define FRAME_VIEW_PROFILER 0 -#define FRAME_VIEW_SCOPE_LIST 1 +enum debug_ui_view +{ + DebugUI_Profiler, + DebugUI_ScopeList, + DebugUI_MemoryView, + + DebugUI_Count, +}; struct debug_interface { diff --git a/src/app/handmade_math.h b/src/app/handmade_math.h deleted file mode 100644 index 049dfdb..0000000 --- a/src/app/handmade_math.h +++ /dev/null @@ -1,3239 +0,0 @@ -/* - HandmadeMath.h v1.11.0 - - This is a single header file with a bunch of useful functions for game and - graphics 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. - - ============================================================================= - - 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_NO_SSE - #include "HandmadeMath.h" - - ============================================================================= - - If you would prefer not to use the HMM_ prefix on function names, you can - - #define HMM_PREFIX - - To use a custom prefix instead, you can - - #define HMM_PREFIX(name) YOUR_PREFIX_##name - - ============================================================================= - - 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 - #include "HandmadeMath.h" - - If you do not define all of these, HandmadeMath.h will use the - versions of these functions that are provided by the CRT. - - ============================================================================= - - 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 (strangezak@gmail.com && @strangezak) - - Functionality: - Matt Mascarenhas (@miblo_) - Aleph - FieryDrake (@fierydrake) - Gingerbill (@TheGingerBill) - Ben Visness (@bvisness) - Trinton Bullard (@Peliex_Dev) - @AntonDan - - Fixes: - Jeroen van Rijn (@J_vanRijn) - Kiljacken (@Kiljacken) - Insofaras (@insofaras) - Daniel Gibson (@DanielGibson) -*/ - -// Dummy macros for when test framework is not present. -#ifndef COVERAGE -#define COVERAGE(a, b) -#endif - -#ifndef ASSERT_COVERED -#define ASSERT_COVERED(a) -#endif - -/* 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 */ - -#ifdef HANDMADE_MATH__USE_SSE -#include -#endif - -#ifndef HANDMADE_MATH_H -#define HANDMADE_MATH_H - -#ifdef _MSC_VER -#pragma warning(disable:4201) -#endif - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#if defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ < 8) -#pragma GCC diagnostic ignored "-Wmissing-braces" -#endif -#ifdef __clang__ -#pragma GCC diagnostic ignored "-Wgnu-anonymous-struct" -#endif -#endif - -#if defined(__GNUC__) || defined(__clang__) -#define HMM_DEPRECATED(msg) __attribute__((deprecated(msg))) -#elif defined(_MSC_VER) -#define HMM_DEPRECATED(msg) __declspec(deprecated(msg)) -#else -#define HMM_DEPRECATED(msg) -#endif - -#ifdef __cplusplus -extern "C" -{ -#endif - -#define HMM_INLINE static inline -#define HMM_EXTERN extern - -#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)) - -#ifndef HMM_PREFIX -#define HMM_PREFIX(name) HMM_##name -#endif - - typedef union hmm_vec2 - { - struct - { - float X, Y; - }; - - struct - { - float U, V; - }; - - struct - { - float Left, Right; - }; - - struct - { - float Width, Height; - }; - - float Elements[2]; - -#ifdef __cplusplus - inline float &operator[](const int &Index) - { - return Elements[Index]; - } -#endif - } hmm_vec2; - - typedef union hmm_vec3 - { - 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]; - -#ifdef __cplusplus - inline float &operator[](const int &Index) - { - return Elements[Index]; - } -#endif - } hmm_vec3; - - typedef union hmm_vec4 - { - 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]; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 InternalElementsSSE; -#endif - -#ifdef __cplusplus - inline float &operator[](const int &Index) - { - return Elements[Index]; - } -#endif - } hmm_vec4; - - typedef union hmm_mat4 - { - float Elements[4][4]; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 Columns[4]; - - HMM_DEPRECATED("Our matrices are column-major, so this was named incorrectly. Use Columns instead.") - __m128 Rows[4]; -#endif - -#ifdef __cplusplus - inline hmm_vec4 operator[](const int &Index) - { - float* col = Elements[Index]; - - hmm_vec4 result; - result.Elements[0] = col[0]; - result.Elements[1] = col[1]; - result.Elements[2] = col[2]; - result.Elements[3] = col[3]; - - return result; - } -#endif - } hmm_mat4; - - typedef union hmm_quaternion - { - struct - { - union - { - hmm_vec3 XYZ; - struct - { - float X, Y, Z; - }; - }; - - float W; - }; - - float Elements[4]; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 InternalElementsSSE; -#endif - } hmm_quaternion; - - typedef signed int hmm_bool; - - typedef hmm_vec2 hmm_v2; - typedef hmm_vec3 hmm_v3; - typedef hmm_vec4 hmm_v4; - typedef hmm_mat4 hmm_m4; - - - /* - * Floating-point math functions - */ - - COVERAGE(HMM_SinF, 1) - HMM_INLINE float HMM_PREFIX(SinF)(float Radians) - { - ASSERT_COVERED(HMM_SinF); - - float Result = HMM_SINF(Radians); - - return (Result); - } - - COVERAGE(HMM_CosF, 1) - HMM_INLINE float HMM_PREFIX(CosF)(float Radians) - { - ASSERT_COVERED(HMM_CosF); - - float Result = HMM_COSF(Radians); - - return (Result); - } - - COVERAGE(HMM_TanF, 1) - HMM_INLINE float HMM_PREFIX(TanF)(float Radians) - { - ASSERT_COVERED(HMM_TanF); - - float Result = HMM_TANF(Radians); - - return (Result); - } - - COVERAGE(HMM_ACosF, 1) - HMM_INLINE float HMM_PREFIX(ACosF)(float Radians) - { - ASSERT_COVERED(HMM_ACosF); - - float Result = HMM_ACOSF(Radians); - - return (Result); - } - - COVERAGE(HMM_ATanF, 1) - HMM_INLINE float HMM_PREFIX(ATanF)(float Radians) - { - ASSERT_COVERED(HMM_ATanF); - - float Result = HMM_ATANF(Radians); - - return (Result); - } - - COVERAGE(HMM_ATan2F, 1) - HMM_INLINE float HMM_PREFIX(ATan2F)(float Left, float Right) - { - ASSERT_COVERED(HMM_ATan2F); - - float Result = HMM_ATAN2F(Left, Right); - - return (Result); - } - - COVERAGE(HMM_ExpF, 1) - HMM_INLINE float HMM_PREFIX(ExpF)(float Float) - { - ASSERT_COVERED(HMM_ExpF); - - float Result = HMM_EXPF(Float); - - return (Result); - } - - COVERAGE(HMM_LogF, 1) - HMM_INLINE float HMM_PREFIX(LogF)(float Float) - { - ASSERT_COVERED(HMM_LogF); - - float Result = HMM_LOGF(Float); - - return (Result); - } - - COVERAGE(HMM_SquareRootF, 1) - HMM_INLINE float HMM_PREFIX(SquareRootF)(float Float) - { - ASSERT_COVERED(HMM_SquareRootF); - - float Result; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 In = _mm_set_ss(Float); - __m128 Out = _mm_sqrt_ss(In); - Result = _mm_cvtss_f32(Out); -#else - Result = HMM_SQRTF(Float); -#endif - - return(Result); - } - - COVERAGE(HMM_RSquareRootF, 1) - HMM_INLINE float HMM_PREFIX(RSquareRootF)(float Float) - { - ASSERT_COVERED(HMM_RSquareRootF); - - float Result; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 In = _mm_set_ss(Float); - __m128 Out = _mm_rsqrt_ss(In); - Result = _mm_cvtss_f32(Out); -#else - Result = 1.0f/HMM_PREFIX(SquareRootF)(Float); -#endif - - return(Result); - } - - HMM_EXTERN float HMM_PREFIX(Power)(float Base, int Exponent); - - COVERAGE(HMM_PowerF, 1) - HMM_INLINE float HMM_PREFIX(PowerF)(float Base, float Exponent) - { - ASSERT_COVERED(HMM_PowerF); - - float Result = HMM_EXPF(Exponent * HMM_LOGF(Base)); - - return (Result); - } - - - /* - * Utility functions - */ - - COVERAGE(HMM_ToRadians, 1) - HMM_INLINE float HMM_PREFIX(ToRadians)(float Degrees) - { - ASSERT_COVERED(HMM_ToRadians); - - float Result = Degrees * (HMM_PI32 / 180.0f); - - return (Result); - } - - COVERAGE(HMM_Lerp, 1) - HMM_INLINE float HMM_PREFIX(Lerp)(float A, float Time, float B) - { - ASSERT_COVERED(HMM_Lerp); - - float Result = (1.0f - Time) * A + Time * B; - - return (Result); - } - - COVERAGE(HMM_Clamp, 1) - HMM_INLINE float HMM_PREFIX(Clamp)(float Min, float Value, float Max) - { - ASSERT_COVERED(HMM_Clamp); - - float Result = Value; - - if(Result < Min) - { - Result = Min; - } - else if(Result > Max) - { - Result = Max; - } - - return (Result); - } - - - /* - * Vector initialization - */ - - COVERAGE(HMM_Vec2, 1) - HMM_INLINE hmm_vec2 HMM_PREFIX(Vec2)(float X, float Y) - { - ASSERT_COVERED(HMM_Vec2); - - hmm_vec2 Result; - - Result.X = X; - Result.Y = Y; - - return (Result); - } - - COVERAGE(HMM_Vec2i, 1) - HMM_INLINE hmm_vec2 HMM_PREFIX(Vec2i)(int X, int Y) - { - ASSERT_COVERED(HMM_Vec2i); - - hmm_vec2 Result; - - Result.X = (float)X; - Result.Y = (float)Y; - - return (Result); - } - - COVERAGE(HMM_Vec3, 1) - HMM_INLINE hmm_vec3 HMM_PREFIX(Vec3)(float X, float Y, float Z) - { - ASSERT_COVERED(HMM_Vec3); - - hmm_vec3 Result; - - Result.X = X; - Result.Y = Y; - Result.Z = Z; - - return (Result); - } - - COVERAGE(HMM_Vec3i, 1) - HMM_INLINE hmm_vec3 HMM_PREFIX(Vec3i)(int X, int Y, int Z) - { - ASSERT_COVERED(HMM_Vec3i); - - hmm_vec3 Result; - - Result.X = (float)X; - Result.Y = (float)Y; - Result.Z = (float)Z; - - return (Result); - } - - COVERAGE(HMM_Vec4, 1) - HMM_INLINE hmm_vec4 HMM_PREFIX(Vec4)(float X, float Y, float Z, float W) - { - ASSERT_COVERED(HMM_Vec4); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = _mm_setr_ps(X, Y, Z, W); -#else - Result.X = X; - Result.Y = Y; - Result.Z = Z; - Result.W = W; -#endif - - return (Result); - } - - COVERAGE(HMM_Vec4i, 1) - HMM_INLINE hmm_vec4 HMM_PREFIX(Vec4i)(int X, int Y, int Z, int W) - { - ASSERT_COVERED(HMM_Vec4i); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = _mm_setr_ps((float)X, (float)Y, (float)Z, (float)W); -#else - Result.X = (float)X; - Result.Y = (float)Y; - Result.Z = (float)Z; - Result.W = (float)W; -#endif - - return (Result); - } - - COVERAGE(HMM_Vec4v, 1) - HMM_INLINE hmm_vec4 HMM_PREFIX(Vec4v)(hmm_vec3 Vector, float W) - { - ASSERT_COVERED(HMM_Vec4v); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = _mm_setr_ps(Vector.X, Vector.Y, Vector.Z, W); -#else - Result.XYZ = Vector; - Result.W = W; -#endif - - return (Result); - } - - - /* - * Binary vector operations - */ - - COVERAGE(HMM_AddVec2, 1) - HMM_INLINE hmm_vec2 HMM_PREFIX(AddVec2)(hmm_vec2 Left, hmm_vec2 Right) - { - ASSERT_COVERED(HMM_AddVec2); - - hmm_vec2 Result; - - Result.X = Left.X + Right.X; - Result.Y = Left.Y + Right.Y; - - return (Result); - } - - COVERAGE(HMM_AddVec3, 1) - HMM_INLINE hmm_vec3 HMM_PREFIX(AddVec3)(hmm_vec3 Left, hmm_vec3 Right) - { - ASSERT_COVERED(HMM_AddVec3); - - hmm_vec3 Result; - - Result.X = Left.X + Right.X; - Result.Y = Left.Y + Right.Y; - Result.Z = Left.Z + Right.Z; - - return (Result); - } - - COVERAGE(HMM_AddVec4, 1) - HMM_INLINE hmm_vec4 HMM_PREFIX(AddVec4)(hmm_vec4 Left, hmm_vec4 Right) - { - ASSERT_COVERED(HMM_AddVec4); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = _mm_add_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); -#else - Result.X = Left.X + Right.X; - Result.Y = Left.Y + Right.Y; - Result.Z = Left.Z + Right.Z; - Result.W = Left.W + Right.W; -#endif - - return (Result); - } - - COVERAGE(HMM_SubtractVec2, 1) - HMM_INLINE hmm_vec2 HMM_PREFIX(SubtractVec2)(hmm_vec2 Left, hmm_vec2 Right) - { - ASSERT_COVERED(HMM_SubtractVec2); - - hmm_vec2 Result; - - Result.X = Left.X - Right.X; - Result.Y = Left.Y - Right.Y; - - return (Result); - } - - COVERAGE(HMM_SubtractVec3, 1) - HMM_INLINE hmm_vec3 HMM_PREFIX(SubtractVec3)(hmm_vec3 Left, hmm_vec3 Right) - { - ASSERT_COVERED(HMM_SubtractVec3); - - hmm_vec3 Result; - - Result.X = Left.X - Right.X; - Result.Y = Left.Y - Right.Y; - Result.Z = Left.Z - Right.Z; - - return (Result); - } - - COVERAGE(HMM_SubtractVec4, 1) - HMM_INLINE hmm_vec4 HMM_PREFIX(SubtractVec4)(hmm_vec4 Left, hmm_vec4 Right) - { - ASSERT_COVERED(HMM_SubtractVec4); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = _mm_sub_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); -#else - Result.X = Left.X - Right.X; - Result.Y = Left.Y - Right.Y; - Result.Z = Left.Z - Right.Z; - Result.W = Left.W - Right.W; -#endif - - return (Result); - } - - COVERAGE(HMM_MultiplyVec2, 1) - HMM_INLINE hmm_vec2 HMM_PREFIX(MultiplyVec2)(hmm_vec2 Left, hmm_vec2 Right) - { - ASSERT_COVERED(HMM_MultiplyVec2); - - hmm_vec2 Result; - - Result.X = Left.X * Right.X; - Result.Y = Left.Y * Right.Y; - - return (Result); - } - - COVERAGE(HMM_MultiplyVec2f, 1) - HMM_INLINE hmm_vec2 HMM_PREFIX(MultiplyVec2f)(hmm_vec2 Left, float Right) - { - ASSERT_COVERED(HMM_MultiplyVec2f); - - hmm_vec2 Result; - - Result.X = Left.X * Right; - Result.Y = Left.Y * Right; - - return (Result); - } - - COVERAGE(HMM_MultiplyVec3, 1) - HMM_INLINE hmm_vec3 HMM_PREFIX(MultiplyVec3)(hmm_vec3 Left, hmm_vec3 Right) - { - ASSERT_COVERED(HMM_MultiplyVec3); - - hmm_vec3 Result; - - Result.X = Left.X * Right.X; - Result.Y = Left.Y * Right.Y; - Result.Z = Left.Z * Right.Z; - - return (Result); - } - - COVERAGE(HMM_MultiplyVec3f, 1) - HMM_INLINE hmm_vec3 HMM_PREFIX(MultiplyVec3f)(hmm_vec3 Left, float Right) - { - ASSERT_COVERED(HMM_MultiplyVec3f); - - hmm_vec3 Result; - - Result.X = Left.X * Right; - Result.Y = Left.Y * Right; - Result.Z = Left.Z * Right; - - return (Result); - } - - COVERAGE(HMM_MultiplyVec4, 1) - HMM_INLINE hmm_vec4 HMM_PREFIX(MultiplyVec4)(hmm_vec4 Left, hmm_vec4 Right) - { - ASSERT_COVERED(HMM_MultiplyVec4); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = _mm_mul_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); -#else - Result.X = Left.X * Right.X; - Result.Y = Left.Y * Right.Y; - Result.Z = Left.Z * Right.Z; - Result.W = Left.W * Right.W; -#endif - - return (Result); - } - - COVERAGE(HMM_MultiplyVec4f, 1) - HMM_INLINE hmm_vec4 HMM_PREFIX(MultiplyVec4f)(hmm_vec4 Left, float Right) - { - ASSERT_COVERED(HMM_MultiplyVec4f); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 Scalar = _mm_set1_ps(Right); - Result.InternalElementsSSE = _mm_mul_ps(Left.InternalElementsSSE, Scalar); -#else - Result.X = Left.X * Right; - Result.Y = Left.Y * Right; - Result.Z = Left.Z * Right; - Result.W = Left.W * Right; -#endif - - return (Result); - } - - COVERAGE(HMM_DivideVec2, 1) - HMM_INLINE hmm_vec2 HMM_PREFIX(DivideVec2)(hmm_vec2 Left, hmm_vec2 Right) - { - ASSERT_COVERED(HMM_DivideVec2); - - hmm_vec2 Result; - - Result.X = Left.X / Right.X; - Result.Y = Left.Y / Right.Y; - - return (Result); - } - - COVERAGE(HMM_DivideVec2f, 1) - HMM_INLINE hmm_vec2 HMM_PREFIX(DivideVec2f)(hmm_vec2 Left, float Right) - { - ASSERT_COVERED(HMM_DivideVec2f); - - hmm_vec2 Result; - - Result.X = Left.X / Right; - Result.Y = Left.Y / Right; - - return (Result); - } - - COVERAGE(HMM_DivideVec3, 1) - HMM_INLINE hmm_vec3 HMM_PREFIX(DivideVec3)(hmm_vec3 Left, hmm_vec3 Right) - { - ASSERT_COVERED(HMM_DivideVec3); - - hmm_vec3 Result; - - Result.X = Left.X / Right.X; - Result.Y = Left.Y / Right.Y; - Result.Z = Left.Z / Right.Z; - - return (Result); - } - - COVERAGE(HMM_DivideVec3f, 1) - HMM_INLINE hmm_vec3 HMM_PREFIX(DivideVec3f)(hmm_vec3 Left, float Right) - { - ASSERT_COVERED(HMM_DivideVec3f); - - hmm_vec3 Result; - - Result.X = Left.X / Right; - Result.Y = Left.Y / Right; - Result.Z = Left.Z / Right; - - return (Result); - } - - COVERAGE(HMM_DivideVec4, 1) - HMM_INLINE hmm_vec4 HMM_PREFIX(DivideVec4)(hmm_vec4 Left, hmm_vec4 Right) - { - ASSERT_COVERED(HMM_DivideVec4); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = _mm_div_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); -#else - Result.X = Left.X / Right.X; - Result.Y = Left.Y / Right.Y; - Result.Z = Left.Z / Right.Z; - Result.W = Left.W / Right.W; -#endif - - return (Result); - } - - COVERAGE(HMM_DivideVec4f, 1) - HMM_INLINE hmm_vec4 HMM_PREFIX(DivideVec4f)(hmm_vec4 Left, float Right) - { - ASSERT_COVERED(HMM_DivideVec4f); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 Scalar = _mm_set1_ps(Right); - Result.InternalElementsSSE = _mm_div_ps(Left.InternalElementsSSE, Scalar); -#else - Result.X = Left.X / Right; - Result.Y = Left.Y / Right; - Result.Z = Left.Z / Right; - Result.W = Left.W / Right; -#endif - - return (Result); - } - - COVERAGE(HMM_EqualsVec2, 1) - HMM_INLINE hmm_bool HMM_PREFIX(EqualsVec2)(hmm_vec2 Left, hmm_vec2 Right) - { - ASSERT_COVERED(HMM_EqualsVec2); - - hmm_bool Result = (Left.X == Right.X && Left.Y == Right.Y); - - return (Result); - } - - COVERAGE(HMM_EqualsVec3, 1) - HMM_INLINE hmm_bool HMM_PREFIX(EqualsVec3)(hmm_vec3 Left, hmm_vec3 Right) - { - ASSERT_COVERED(HMM_EqualsVec3); - - hmm_bool Result = (Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z); - - return (Result); - } - - COVERAGE(HMM_EqualsVec4, 1) - HMM_INLINE hmm_bool HMM_PREFIX(EqualsVec4)(hmm_vec4 Left, hmm_vec4 Right) - { - ASSERT_COVERED(HMM_EqualsVec4); - - hmm_bool Result = (Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z && Left.W == Right.W); - - return (Result); - } - - COVERAGE(HMM_DotVec2, 1) - HMM_INLINE float HMM_PREFIX(DotVec2)(hmm_vec2 VecOne, hmm_vec2 VecTwo) - { - ASSERT_COVERED(HMM_DotVec2); - - float Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y); - - return (Result); - } - - COVERAGE(HMM_DotVec3, 1) - HMM_INLINE float HMM_PREFIX(DotVec3)(hmm_vec3 VecOne, hmm_vec3 VecTwo) - { - ASSERT_COVERED(HMM_DotVec3); - - float Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z); - - return (Result); - } - - COVERAGE(HMM_DotVec4, 1) - HMM_INLINE float HMM_PREFIX(DotVec4)(hmm_vec4 VecOne, hmm_vec4 VecTwo) - { - ASSERT_COVERED(HMM_DotVec4); - - float Result; - - // NOTE(zak): IN the future if we wanna check what version SSE is support - // we can use _mm_dp_ps (4.3) but for now we will use the old way. - // Or a r = _mm_mul_ps(v1, v2), r = _mm_hadd_ps(r, r), r = _mm_hadd_ps(r, r) for SSE3 -#ifdef HANDMADE_MATH__USE_SSE - __m128 SSEResultOne = _mm_mul_ps(VecOne.InternalElementsSSE, VecTwo.InternalElementsSSE); - __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1)); - SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); - SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3)); - SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); - _mm_store_ss(&Result, SSEResultOne); -#else - Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z) + (VecOne.W * VecTwo.W); -#endif - - return (Result); - } - - COVERAGE(HMM_Cross, 1) - HMM_INLINE hmm_vec3 HMM_PREFIX(Cross)(hmm_vec3 VecOne, hmm_vec3 VecTwo) - { - ASSERT_COVERED(HMM_Cross); - - hmm_vec3 Result; - - 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); - } - - - /* - * Unary vector operations - */ - - COVERAGE(HMM_LengthSquaredVec2, 1) - HMM_INLINE float HMM_PREFIX(LengthSquaredVec2)(hmm_vec2 A) - { - ASSERT_COVERED(HMM_LengthSquaredVec2); - - float Result = HMM_PREFIX(DotVec2)(A, A); - - return (Result); - } - - COVERAGE(HMM_LengthSquaredVec3, 1) - HMM_INLINE float HMM_PREFIX(LengthSquaredVec3)(hmm_vec3 A) - { - ASSERT_COVERED(HMM_LengthSquaredVec3); - - float Result = HMM_PREFIX(DotVec3)(A, A); - - return (Result); - } - - COVERAGE(HMM_LengthSquaredVec4, 1) - HMM_INLINE float HMM_PREFIX(LengthSquaredVec4)(hmm_vec4 A) - { - ASSERT_COVERED(HMM_LengthSquaredVec4); - - float Result = HMM_PREFIX(DotVec4)(A, A); - - return (Result); - } - - COVERAGE(HMM_LengthVec2, 1) - HMM_INLINE float HMM_PREFIX(LengthVec2)(hmm_vec2 A) - { - ASSERT_COVERED(HMM_LengthVec2); - - float Result = HMM_PREFIX(SquareRootF)(HMM_PREFIX(LengthSquaredVec2)(A)); - - return (Result); - } - - COVERAGE(HMM_LengthVec3, 1) - HMM_INLINE float HMM_PREFIX(LengthVec3)(hmm_vec3 A) - { - ASSERT_COVERED(HMM_LengthVec3); - - float Result = HMM_PREFIX(SquareRootF)(HMM_PREFIX(LengthSquaredVec3)(A)); - - return (Result); - } - - COVERAGE(HMM_LengthVec4, 1) - HMM_INLINE float HMM_PREFIX(LengthVec4)(hmm_vec4 A) - { - ASSERT_COVERED(HMM_LengthVec4); - - float Result = HMM_PREFIX(SquareRootF)(HMM_PREFIX(LengthSquaredVec4)(A)); - - return(Result); - } - - COVERAGE(HMM_NormalizeVec2, 2) - HMM_INLINE hmm_vec2 HMM_PREFIX(NormalizeVec2)(hmm_vec2 A) - { - ASSERT_COVERED(HMM_NormalizeVec2); - - hmm_vec2 Result = {0}; - - float VectorLength = HMM_PREFIX(LengthVec2)(A); - - /* NOTE(kiljacken): We need a zero check to not divide-by-zero */ - if (VectorLength != 0.0f) - { - ASSERT_COVERED(HMM_NormalizeVec2); - - Result.X = A.X * (1.0f / VectorLength); - Result.Y = A.Y * (1.0f / VectorLength); - } - - return (Result); - } - - COVERAGE(HMM_NormalizeVec3, 2) - HMM_INLINE hmm_vec3 HMM_PREFIX(NormalizeVec3)(hmm_vec3 A) - { - ASSERT_COVERED(HMM_NormalizeVec3); - - hmm_vec3 Result = {0}; - - float VectorLength = HMM_PREFIX(LengthVec3)(A); - - /* NOTE(kiljacken): We need a zero check to not divide-by-zero */ - if (VectorLength != 0.0f) - { - ASSERT_COVERED(HMM_NormalizeVec3); - - Result.X = A.X * (1.0f / VectorLength); - Result.Y = A.Y * (1.0f / VectorLength); - Result.Z = A.Z * (1.0f / VectorLength); - } - - return (Result); - } - - COVERAGE(HMM_NormalizeVec4, 2) - HMM_INLINE hmm_vec4 HMM_PREFIX(NormalizeVec4)(hmm_vec4 A) - { - ASSERT_COVERED(HMM_NormalizeVec4); - - hmm_vec4 Result = {0}; - - float VectorLength = HMM_PREFIX(LengthVec4)(A); - - /* NOTE(kiljacken): We need a zero check to not divide-by-zero */ - if (VectorLength != 0.0f) - { - ASSERT_COVERED(HMM_NormalizeVec4); - - float Multiplier = 1.0f / VectorLength; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 SSEMultiplier = _mm_set1_ps(Multiplier); - Result.InternalElementsSSE = _mm_mul_ps(A.InternalElementsSSE, SSEMultiplier); -#else - Result.X = A.X * Multiplier; - Result.Y = A.Y * Multiplier; - Result.Z = A.Z * Multiplier; - Result.W = A.W * Multiplier; -#endif - } - - return (Result); - } - - COVERAGE(HMM_FastNormalizeVec2, 1) - HMM_INLINE hmm_vec2 HMM_PREFIX(FastNormalizeVec2)(hmm_vec2 A) - { - ASSERT_COVERED(HMM_FastNormalizeVec2); - - return HMM_PREFIX(MultiplyVec2f)(A, HMM_PREFIX(RSquareRootF)(HMM_PREFIX(DotVec2)(A, A))); - } - - COVERAGE(HMM_FastNormalizeVec3, 1) - HMM_INLINE hmm_vec3 HMM_PREFIX(FastNormalizeVec3)(hmm_vec3 A) - { - ASSERT_COVERED(HMM_FastNormalizeVec3); - - return HMM_PREFIX(MultiplyVec3f)(A, HMM_PREFIX(RSquareRootF)(HMM_PREFIX(DotVec3)(A, A))); - } - - COVERAGE(HMM_FastNormalizeVec4, 1) - HMM_INLINE hmm_vec4 HMM_PREFIX(FastNormalizeVec4)(hmm_vec4 A) - { - ASSERT_COVERED(HMM_FastNormalizeVec4); - - return HMM_PREFIX(MultiplyVec4f)(A, HMM_PREFIX(RSquareRootF)(HMM_PREFIX(DotVec4)(A, A))); - } - - - /* - * SSE stuff - */ - -#ifdef HANDMADE_MATH__USE_SSE - COVERAGE(HMM_LinearCombineSSE, 1) - HMM_INLINE __m128 HMM_PREFIX(LinearCombineSSE)(__m128 Left, hmm_mat4 Right) - { - ASSERT_COVERED(HMM_LinearCombineSSE); - - __m128 Result; - Result = _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0x00), Right.Columns[0]); - Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0x55), Right.Columns[1])); - Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0xaa), Right.Columns[2])); - Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0xff), Right.Columns[3])); - - return (Result); - } -#endif - - - /* - * Matrix functions - */ - - COVERAGE(HMM_Mat4, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(Mat4)(void) - { - ASSERT_COVERED(HMM_Mat4); - - hmm_mat4 Result = {0}; - - return (Result); - } - - COVERAGE(HMM_Mat4d, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(Mat4d)(float Diagonal) - { - ASSERT_COVERED(HMM_Mat4d); - - hmm_mat4 Result = HMM_PREFIX(Mat4)(); - - Result.Elements[0][0] = Diagonal; - Result.Elements[1][1] = Diagonal; - Result.Elements[2][2] = Diagonal; - Result.Elements[3][3] = Diagonal; - - return (Result); - } - -#ifdef HANDMADE_MATH__USE_SSE - COVERAGE(HMM_Transpose, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(Transpose)(hmm_mat4 Matrix) - { - ASSERT_COVERED(HMM_Transpose); - - hmm_mat4 Result = Matrix; - - _MM_TRANSPOSE4_PS(Result.Columns[0], Result.Columns[1], Result.Columns[2], Result.Columns[3]); - - return (Result); - } -#else - HMM_EXTERN hmm_mat4 HMM_PREFIX(Transpose)(hmm_mat4 Matrix); -#endif - -#ifdef HANDMADE_MATH__USE_SSE - COVERAGE(HMM_AddMat4, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(AddMat4)(hmm_mat4 Left, hmm_mat4 Right) - { - ASSERT_COVERED(HMM_AddMat4); - - hmm_mat4 Result; - - Result.Columns[0] = _mm_add_ps(Left.Columns[0], Right.Columns[0]); - Result.Columns[1] = _mm_add_ps(Left.Columns[1], Right.Columns[1]); - Result.Columns[2] = _mm_add_ps(Left.Columns[2], Right.Columns[2]); - Result.Columns[3] = _mm_add_ps(Left.Columns[3], Right.Columns[3]); - - return (Result); - } -#else - HMM_EXTERN hmm_mat4 HMM_PREFIX(AddMat4)(hmm_mat4 Left, hmm_mat4 Right); -#endif - -#ifdef HANDMADE_MATH__USE_SSE - COVERAGE(HMM_SubtractMat4, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(SubtractMat4)(hmm_mat4 Left, hmm_mat4 Right) - { - ASSERT_COVERED(HMM_SubtractMat4); - - hmm_mat4 Result; - - Result.Columns[0] = _mm_sub_ps(Left.Columns[0], Right.Columns[0]); - Result.Columns[1] = _mm_sub_ps(Left.Columns[1], Right.Columns[1]); - Result.Columns[2] = _mm_sub_ps(Left.Columns[2], Right.Columns[2]); - Result.Columns[3] = _mm_sub_ps(Left.Columns[3], Right.Columns[3]); - - return (Result); - } -#else - HMM_EXTERN hmm_mat4 HMM_PREFIX(SubtractMat4)(hmm_mat4 Left, hmm_mat4 Right); -#endif - - HMM_EXTERN hmm_mat4 HMM_PREFIX(MultiplyMat4)(hmm_mat4 Left, hmm_mat4 Right); - -#ifdef HANDMADE_MATH__USE_SSE - COVERAGE(HMM_MultiplyMat4f, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(MultiplyMat4f)(hmm_mat4 Matrix, float Scalar) - { - ASSERT_COVERED(HMM_MultiplyMat4f); - - hmm_mat4 Result; - - __m128 SSEScalar = _mm_set1_ps(Scalar); - Result.Columns[0] = _mm_mul_ps(Matrix.Columns[0], SSEScalar); - Result.Columns[1] = _mm_mul_ps(Matrix.Columns[1], SSEScalar); - Result.Columns[2] = _mm_mul_ps(Matrix.Columns[2], SSEScalar); - Result.Columns[3] = _mm_mul_ps(Matrix.Columns[3], SSEScalar); - - return (Result); - } -#else - HMM_EXTERN hmm_mat4 HMM_PREFIX(MultiplyMat4f)(hmm_mat4 Matrix, float Scalar); -#endif - - HMM_EXTERN hmm_vec4 HMM_PREFIX(MultiplyMat4ByVec4)(hmm_mat4 Matrix, hmm_vec4 Vector); - -#ifdef HANDMADE_MATH__USE_SSE - COVERAGE(HMM_DivideMat4f, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(DivideMat4f)(hmm_mat4 Matrix, float Scalar) - { - ASSERT_COVERED(HMM_DivideMat4f); - - hmm_mat4 Result; - - __m128 SSEScalar = _mm_set1_ps(Scalar); - Result.Columns[0] = _mm_div_ps(Matrix.Columns[0], SSEScalar); - Result.Columns[1] = _mm_div_ps(Matrix.Columns[1], SSEScalar); - Result.Columns[2] = _mm_div_ps(Matrix.Columns[2], SSEScalar); - Result.Columns[3] = _mm_div_ps(Matrix.Columns[3], SSEScalar); - - return (Result); - } -#else - HMM_EXTERN hmm_mat4 HMM_PREFIX(DivideMat4f)(hmm_mat4 Matrix, float Scalar); -#endif - - - /* - * Common graphics transformations - */ - - COVERAGE(HMM_Orthographic, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(Orthographic)(float Left, float Right, float Bottom, float Top, float Near, float Far) - { - ASSERT_COVERED(HMM_Orthographic); - - hmm_mat4 Result = HMM_PREFIX(Mat4)(); - - 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][3] = 1.0f; - - 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); - } - - COVERAGE(HMM_Perspective, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(Perspective)(float FOV, float AspectRatio, float Near, float Far) - { - ASSERT_COVERED(HMM_Perspective); - - hmm_mat4 Result = HMM_PREFIX(Mat4)(); - - // See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml - - float Cotangent = 1.0f / HMM_PREFIX(TanF)(FOV * (HMM_PI32 / 360.0f)); - - Result.Elements[0][0] = Cotangent / AspectRatio; - Result.Elements[1][1] = Cotangent; - 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); - } - - COVERAGE(HMM_Translate, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(Translate)(hmm_vec3 Translation) - { - ASSERT_COVERED(HMM_Translate); - - hmm_mat4 Result = HMM_PREFIX(Mat4d)(1.0f); - - Result.Elements[3][0] = Translation.X; - Result.Elements[3][1] = Translation.Y; - Result.Elements[3][2] = Translation.Z; - - return (Result); - } - - HMM_EXTERN hmm_mat4 HMM_PREFIX(Rotate)(float Angle, hmm_vec3 Axis); - - COVERAGE(HMM_Scale, 1) - HMM_INLINE hmm_mat4 HMM_PREFIX(Scale)(hmm_vec3 Scale) - { - ASSERT_COVERED(HMM_Scale); - - hmm_mat4 Result = HMM_PREFIX(Mat4d)(1.0f); - - Result.Elements[0][0] = Scale.X; - Result.Elements[1][1] = Scale.Y; - Result.Elements[2][2] = Scale.Z; - - return (Result); - } - - HMM_EXTERN hmm_mat4 HMM_PREFIX(LookAt)(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up); - - - /* - * Quaternion operations - */ - - COVERAGE(HMM_Quaternion, 1) - HMM_INLINE hmm_quaternion HMM_PREFIX(Quaternion)(float X, float Y, float Z, float W) - { - ASSERT_COVERED(HMM_Quaternion); - - hmm_quaternion Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = _mm_setr_ps(X, Y, Z, W); -#else - Result.X = X; - Result.Y = Y; - Result.Z = Z; - Result.W = W; -#endif - - return (Result); - } - - COVERAGE(HMM_QuaternionV4, 1) - HMM_INLINE hmm_quaternion HMM_PREFIX(QuaternionV4)(hmm_vec4 Vector) - { - ASSERT_COVERED(HMM_QuaternionV4); - - hmm_quaternion Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = Vector.InternalElementsSSE; -#else - Result.X = Vector.X; - Result.Y = Vector.Y; - Result.Z = Vector.Z; - Result.W = Vector.W; -#endif - - return (Result); - } - - COVERAGE(HMM_AddQuaternion, 1) - HMM_INLINE hmm_quaternion HMM_PREFIX(AddQuaternion)(hmm_quaternion Left, hmm_quaternion Right) - { - ASSERT_COVERED(HMM_AddQuaternion); - - hmm_quaternion Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = _mm_add_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); -#else - - Result.X = Left.X + Right.X; - Result.Y = Left.Y + Right.Y; - Result.Z = Left.Z + Right.Z; - Result.W = Left.W + Right.W; -#endif - - return (Result); - } - - COVERAGE(HMM_SubtractQuaternion, 1) - HMM_INLINE hmm_quaternion HMM_PREFIX(SubtractQuaternion)(hmm_quaternion Left, hmm_quaternion Right) - { - ASSERT_COVERED(HMM_SubtractQuaternion); - - hmm_quaternion Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = _mm_sub_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); -#else - - Result.X = Left.X - Right.X; - Result.Y = Left.Y - Right.Y; - Result.Z = Left.Z - Right.Z; - Result.W = Left.W - Right.W; -#endif - - return (Result); - } - - COVERAGE(HMM_MultiplyQuaternion, 1) - HMM_INLINE hmm_quaternion HMM_PREFIX(MultiplyQuaternion)(hmm_quaternion Left, hmm_quaternion Right) - { - ASSERT_COVERED(HMM_MultiplyQuaternion); - - hmm_quaternion Result; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.InternalElementsSSE, Left.InternalElementsSSE, _MM_SHUFFLE(0, 0, 0, 0)), _mm_setr_ps(0.f, -0.f, 0.f, -0.f)); - __m128 SSEResultTwo = _mm_shuffle_ps(Right.InternalElementsSSE, Right.InternalElementsSSE, _MM_SHUFFLE(0, 1, 2, 3)); - __m128 SSEResultThree = _mm_mul_ps(SSEResultTwo, SSEResultOne); - - SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.InternalElementsSSE, Left.InternalElementsSSE, _MM_SHUFFLE(1, 1, 1, 1)) , _mm_setr_ps(0.f, 0.f, -0.f, -0.f)); - SSEResultTwo = _mm_shuffle_ps(Right.InternalElementsSSE, Right.InternalElementsSSE, _MM_SHUFFLE(1, 0, 3, 2)); - SSEResultThree = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne)); - - SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.InternalElementsSSE, Left.InternalElementsSSE, _MM_SHUFFLE(2, 2, 2, 2)), _mm_setr_ps(-0.f, 0.f, 0.f, -0.f)); - SSEResultTwo = _mm_shuffle_ps(Right.InternalElementsSSE, Right.InternalElementsSSE, _MM_SHUFFLE(2, 3, 0, 1)); - SSEResultThree = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne)); - - SSEResultOne = _mm_shuffle_ps(Left.InternalElementsSSE, Left.InternalElementsSSE, _MM_SHUFFLE(3, 3, 3, 3)); - SSEResultTwo = _mm_shuffle_ps(Right.InternalElementsSSE, Right.InternalElementsSSE, _MM_SHUFFLE(3, 2, 1, 0)); - Result.InternalElementsSSE = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne)); -#else - 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); -#endif - - return (Result); - } - - COVERAGE(HMM_MultiplyQuaternionF, 1) - HMM_INLINE hmm_quaternion HMM_PREFIX(MultiplyQuaternionF)(hmm_quaternion Left, float Multiplicative) - { - ASSERT_COVERED(HMM_MultiplyQuaternionF); - - hmm_quaternion Result; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 Scalar = _mm_set1_ps(Multiplicative); - Result.InternalElementsSSE = _mm_mul_ps(Left.InternalElementsSSE, Scalar); -#else - Result.X = Left.X * Multiplicative; - Result.Y = Left.Y * Multiplicative; - Result.Z = Left.Z * Multiplicative; - Result.W = Left.W * Multiplicative; -#endif - - return (Result); - } - - COVERAGE(HMM_DivideQuaternionF, 1) - HMM_INLINE hmm_quaternion HMM_PREFIX(DivideQuaternionF)(hmm_quaternion Left, float Dividend) - { - ASSERT_COVERED(HMM_DivideQuaternionF); - - hmm_quaternion Result; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 Scalar = _mm_set1_ps(Dividend); - Result.InternalElementsSSE = _mm_div_ps(Left.InternalElementsSSE, Scalar); -#else - Result.X = Left.X / Dividend; - Result.Y = Left.Y / Dividend; - Result.Z = Left.Z / Dividend; - Result.W = Left.W / Dividend; -#endif - - return (Result); - } - - HMM_EXTERN hmm_quaternion HMM_PREFIX(InverseQuaternion)(hmm_quaternion Left); - - COVERAGE(HMM_DotQuaternion, 1) - HMM_INLINE float HMM_PREFIX(DotQuaternion)(hmm_quaternion Left, hmm_quaternion Right) - { - ASSERT_COVERED(HMM_DotQuaternion); - - float Result; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 SSEResultOne = _mm_mul_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); - __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1)); - SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); - SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3)); - SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); - _mm_store_ss(&Result, SSEResultOne); -#else - Result = (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z) + (Left.W * Right.W); -#endif - - return (Result); - } - - COVERAGE(HMM_NormalizeQuaternion, 1) - HMM_INLINE hmm_quaternion HMM_PREFIX(NormalizeQuaternion)(hmm_quaternion Left) - { - ASSERT_COVERED(HMM_NormalizeQuaternion); - - hmm_quaternion Result; - - float Length = HMM_PREFIX(SquareRootF)(HMM_PREFIX(DotQuaternion)(Left, Left)); - Result = HMM_PREFIX(DivideQuaternionF)(Left, Length); - - return (Result); - } - - COVERAGE(HMM_NLerp, 1) - HMM_INLINE hmm_quaternion HMM_PREFIX(NLerp)(hmm_quaternion Left, float Time, hmm_quaternion Right) - { - ASSERT_COVERED(HMM_NLerp); - - hmm_quaternion Result; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 ScalarLeft = _mm_set1_ps(1.0f - Time); - __m128 ScalarRight = _mm_set1_ps(Time); - __m128 SSEResultOne = _mm_mul_ps(Left.InternalElementsSSE, ScalarLeft); - __m128 SSEResultTwo = _mm_mul_ps(Right.InternalElementsSSE, ScalarRight); - Result.InternalElementsSSE = _mm_add_ps(SSEResultOne, SSEResultTwo); -#else - Result.X = HMM_PREFIX(Lerp)(Left.X, Time, Right.X); - Result.Y = HMM_PREFIX(Lerp)(Left.Y, Time, Right.Y); - Result.Z = HMM_PREFIX(Lerp)(Left.Z, Time, Right.Z); - Result.W = HMM_PREFIX(Lerp)(Left.W, Time, Right.W); -#endif - Result = HMM_PREFIX(NormalizeQuaternion)(Result); - - return (Result); - } - - HMM_EXTERN hmm_quaternion HMM_PREFIX(Slerp)(hmm_quaternion Left, float Time, hmm_quaternion Right); - HMM_EXTERN hmm_mat4 HMM_PREFIX(QuaternionToMat4)(hmm_quaternion Left); - HMM_EXTERN hmm_quaternion HMM_PREFIX(Mat4ToQuaternion)(hmm_mat4 Left); - HMM_EXTERN hmm_quaternion HMM_PREFIX(QuaternionFromAxisAngle)(hmm_vec3 Axis, float AngleOfRotation); - -#ifdef __cplusplus -} -#endif - -#ifdef __cplusplus - -COVERAGE(HMM_LengthVec2CPP, 1) -HMM_INLINE float HMM_PREFIX(Length)(hmm_vec2 A) -{ - ASSERT_COVERED(HMM_LengthVec2CPP); - - float Result = HMM_PREFIX(LengthVec2)(A); - - return (Result); -} - -COVERAGE(HMM_LengthVec3CPP, 1) -HMM_INLINE float HMM_PREFIX(Length)(hmm_vec3 A) -{ - ASSERT_COVERED(HMM_LengthVec3CPP); - - float Result = HMM_PREFIX(LengthVec3)(A); - - return (Result); -} - -COVERAGE(HMM_LengthVec4CPP, 1) -HMM_INLINE float HMM_PREFIX(Length)(hmm_vec4 A) -{ - ASSERT_COVERED(HMM_LengthVec4CPP); - - float Result = HMM_PREFIX(LengthVec4)(A); - - return (Result); -} - -COVERAGE(HMM_LengthSquaredVec2CPP, 1) -HMM_INLINE float HMM_PREFIX(LengthSquared)(hmm_vec2 A) -{ - ASSERT_COVERED(HMM_LengthSquaredVec2CPP); - - float Result = HMM_PREFIX(LengthSquaredVec2)(A); - - return (Result); -} - -COVERAGE(HMM_LengthSquaredVec3CPP, 1) -HMM_INLINE float HMM_PREFIX(LengthSquared)(hmm_vec3 A) -{ - ASSERT_COVERED(HMM_LengthSquaredVec3CPP); - - float Result = HMM_PREFIX(LengthSquaredVec3)(A); - - return (Result); -} - -COVERAGE(HMM_LengthSquaredVec4CPP, 1) -HMM_INLINE float HMM_PREFIX(LengthSquared)(hmm_vec4 A) -{ - ASSERT_COVERED(HMM_LengthSquaredVec4CPP); - - float Result = HMM_PREFIX(LengthSquaredVec4)(A); - - return (Result); -} - -COVERAGE(HMM_NormalizeVec2CPP, 1) -HMM_INLINE hmm_vec2 HMM_PREFIX(Normalize)(hmm_vec2 A) -{ - ASSERT_COVERED(HMM_NormalizeVec2CPP); - - hmm_vec2 Result = HMM_PREFIX(NormalizeVec2)(A); - - return (Result); -} - -COVERAGE(HMM_NormalizeVec3CPP, 1) -HMM_INLINE hmm_vec3 HMM_PREFIX(Normalize)(hmm_vec3 A) -{ - ASSERT_COVERED(HMM_NormalizeVec3CPP); - - hmm_vec3 Result = HMM_PREFIX(NormalizeVec3)(A); - - return (Result); -} - -COVERAGE(HMM_NormalizeVec4CPP, 1) -HMM_INLINE hmm_vec4 HMM_PREFIX(Normalize)(hmm_vec4 A) -{ - ASSERT_COVERED(HMM_NormalizeVec4CPP); - - hmm_vec4 Result = HMM_PREFIX(NormalizeVec4)(A); - - return (Result); -} - -COVERAGE(HMM_FastNormalizeVec2CPP, 1) -HMM_INLINE hmm_vec2 HMM_PREFIX(FastNormalize)(hmm_vec2 A) -{ - ASSERT_COVERED(HMM_FastNormalizeVec2CPP); - - hmm_vec2 Result = HMM_PREFIX(FastNormalizeVec2)(A); - - return (Result); -} - -COVERAGE(HMM_FastNormalizeVec3CPP, 1) -HMM_INLINE hmm_vec3 HMM_PREFIX(FastNormalize)(hmm_vec3 A) -{ - ASSERT_COVERED(HMM_FastNormalizeVec3CPP); - - hmm_vec3 Result = HMM_PREFIX(FastNormalizeVec3)(A); - - return (Result); -} - -COVERAGE(HMM_FastNormalizeVec4CPP, 1) -HMM_INLINE hmm_vec4 HMM_PREFIX(FastNormalize)(hmm_vec4 A) -{ - ASSERT_COVERED(HMM_FastNormalizeVec4CPP); - - hmm_vec4 Result = HMM_PREFIX(FastNormalizeVec4)(A); - - return (Result); -} - -COVERAGE(HMM_NormalizeQuaternionCPP, 1) -HMM_INLINE hmm_quaternion HMM_PREFIX(Normalize)(hmm_quaternion A) -{ - ASSERT_COVERED(HMM_NormalizeQuaternionCPP); - - hmm_quaternion Result = HMM_PREFIX(NormalizeQuaternion)(A); - - return (Result); -} - -COVERAGE(HMM_DotVec2CPP, 1) -HMM_INLINE float HMM_PREFIX(Dot)(hmm_vec2 VecOne, hmm_vec2 VecTwo) -{ - ASSERT_COVERED(HMM_DotVec2CPP); - - float Result = HMM_PREFIX(DotVec2)(VecOne, VecTwo); - - return (Result); -} - -COVERAGE(HMM_DotVec3CPP, 1) -HMM_INLINE float HMM_PREFIX(Dot)(hmm_vec3 VecOne, hmm_vec3 VecTwo) -{ - ASSERT_COVERED(HMM_DotVec3CPP); - - float Result = HMM_PREFIX(DotVec3)(VecOne, VecTwo); - - return (Result); -} - -COVERAGE(HMM_DotVec4CPP, 1) -HMM_INLINE float HMM_PREFIX(Dot)(hmm_vec4 VecOne, hmm_vec4 VecTwo) -{ - ASSERT_COVERED(HMM_DotVec4CPP); - - float Result = HMM_PREFIX(DotVec4)(VecOne, VecTwo); - - return (Result); -} - -COVERAGE(HMM_DotQuaternionCPP, 1) -HMM_INLINE float HMM_PREFIX(Dot)(hmm_quaternion QuatOne, hmm_quaternion QuatTwo) -{ - ASSERT_COVERED(HMM_DotQuaternionCPP); - - float Result = HMM_PREFIX(DotQuaternion)(QuatOne, QuatTwo); - - return (Result); -} - -COVERAGE(HMM_AddVec2CPP, 1) -HMM_INLINE hmm_vec2 HMM_PREFIX(Add)(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_AddVec2CPP); - - hmm_vec2 Result = HMM_PREFIX(AddVec2)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_AddVec3CPP, 1) -HMM_INLINE hmm_vec3 HMM_PREFIX(Add)(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_AddVec3CPP); - - hmm_vec3 Result = HMM_PREFIX(AddVec3)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_AddVec4CPP, 1) -HMM_INLINE hmm_vec4 HMM_PREFIX(Add)(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_AddVec4CPP); - - hmm_vec4 Result = HMM_PREFIX(AddVec4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_AddMat4CPP, 1) -HMM_INLINE hmm_mat4 HMM_PREFIX(Add)(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_AddMat4CPP); - - hmm_mat4 Result = HMM_PREFIX(AddMat4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_AddQuaternionCPP, 1) -HMM_INLINE hmm_quaternion HMM_PREFIX(Add)(hmm_quaternion Left, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_AddQuaternionCPP); - - hmm_quaternion Result = HMM_PREFIX(AddQuaternion)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_SubtractVec2CPP, 1) -HMM_INLINE hmm_vec2 HMM_PREFIX(Subtract)(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_SubtractVec2CPP); - - hmm_vec2 Result = HMM_PREFIX(SubtractVec2)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_SubtractVec3CPP, 1) -HMM_INLINE hmm_vec3 HMM_PREFIX(Subtract)(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_SubtractVec3CPP); - - hmm_vec3 Result = HMM_PREFIX(SubtractVec3)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_SubtractVec4CPP, 1) -HMM_INLINE hmm_vec4 HMM_PREFIX(Subtract)(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_SubtractVec4CPP); - - hmm_vec4 Result = HMM_PREFIX(SubtractVec4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_SubtractMat4CPP, 1) -HMM_INLINE hmm_mat4 HMM_PREFIX(Subtract)(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_SubtractMat4CPP); - - hmm_mat4 Result = HMM_PREFIX(SubtractMat4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_SubtractQuaternionCPP, 1) -HMM_INLINE hmm_quaternion HMM_PREFIX(Subtract)(hmm_quaternion Left, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_SubtractQuaternionCPP); - - hmm_quaternion Result = HMM_PREFIX(SubtractQuaternion)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec2CPP, 1) -HMM_INLINE hmm_vec2 HMM_PREFIX(Multiply)(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec2CPP); - - hmm_vec2 Result = HMM_PREFIX(MultiplyVec2)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec2fCPP, 1) -HMM_INLINE hmm_vec2 HMM_PREFIX(Multiply)(hmm_vec2 Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyVec2fCPP); - - hmm_vec2 Result = HMM_PREFIX(MultiplyVec2f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec3CPP, 1) -HMM_INLINE hmm_vec3 HMM_PREFIX(Multiply)(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec3CPP); - - hmm_vec3 Result = HMM_PREFIX(MultiplyVec3)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec3fCPP, 1) -HMM_INLINE hmm_vec3 HMM_PREFIX(Multiply)(hmm_vec3 Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyVec3fCPP); - - hmm_vec3 Result = HMM_PREFIX(MultiplyVec3f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec4CPP, 1) -HMM_INLINE hmm_vec4 HMM_PREFIX(Multiply)(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec4CPP); - - hmm_vec4 Result = HMM_PREFIX(MultiplyVec4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec4fCPP, 1) -HMM_INLINE hmm_vec4 HMM_PREFIX(Multiply)(hmm_vec4 Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyVec4fCPP); - - hmm_vec4 Result = HMM_PREFIX(MultiplyVec4f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyMat4CPP, 1) -HMM_INLINE hmm_mat4 HMM_PREFIX(Multiply)(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_MultiplyMat4CPP); - - hmm_mat4 Result = HMM_PREFIX(MultiplyMat4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyMat4fCPP, 1) -HMM_INLINE hmm_mat4 HMM_PREFIX(Multiply)(hmm_mat4 Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyMat4fCPP); - - hmm_mat4 Result = HMM_PREFIX(MultiplyMat4f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyMat4ByVec4CPP, 1) -HMM_INLINE hmm_vec4 HMM_PREFIX(Multiply)(hmm_mat4 Matrix, hmm_vec4 Vector) -{ - ASSERT_COVERED(HMM_MultiplyMat4ByVec4CPP); - - hmm_vec4 Result = HMM_PREFIX(MultiplyMat4ByVec4)(Matrix, Vector); - - return (Result); -} - -COVERAGE(HMM_MultiplyQuaternionCPP, 1) -HMM_INLINE hmm_quaternion HMM_PREFIX(Multiply)(hmm_quaternion Left, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_MultiplyQuaternionCPP); - - hmm_quaternion Result = HMM_PREFIX(MultiplyQuaternion)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyQuaternionFCPP, 1) -HMM_INLINE hmm_quaternion HMM_PREFIX(Multiply)(hmm_quaternion Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyQuaternionFCPP); - - hmm_quaternion Result = HMM_PREFIX(MultiplyQuaternionF)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec2CPP, 1) -HMM_INLINE hmm_vec2 HMM_PREFIX(Divide)(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_DivideVec2CPP); - - hmm_vec2 Result = HMM_PREFIX(DivideVec2)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec2fCPP, 1) -HMM_INLINE hmm_vec2 HMM_PREFIX(Divide)(hmm_vec2 Left, float Right) -{ - ASSERT_COVERED(HMM_DivideVec2fCPP); - - hmm_vec2 Result = HMM_PREFIX(DivideVec2f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec3CPP, 1) -HMM_INLINE hmm_vec3 HMM_PREFIX(Divide)(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_DivideVec3CPP); - - hmm_vec3 Result = HMM_PREFIX(DivideVec3)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec3fCPP, 1) -HMM_INLINE hmm_vec3 HMM_PREFIX(Divide)(hmm_vec3 Left, float Right) -{ - ASSERT_COVERED(HMM_DivideVec3fCPP); - - hmm_vec3 Result = HMM_PREFIX(DivideVec3f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec4CPP, 1) -HMM_INLINE hmm_vec4 HMM_PREFIX(Divide)(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_DivideVec4CPP); - - hmm_vec4 Result = HMM_PREFIX(DivideVec4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec4fCPP, 1) -HMM_INLINE hmm_vec4 HMM_PREFIX(Divide)(hmm_vec4 Left, float Right) -{ - ASSERT_COVERED(HMM_DivideVec4fCPP); - - hmm_vec4 Result = HMM_PREFIX(DivideVec4f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideMat4fCPP, 1) -HMM_INLINE hmm_mat4 HMM_PREFIX(Divide)(hmm_mat4 Left, float Right) -{ - ASSERT_COVERED(HMM_DivideMat4fCPP); - - hmm_mat4 Result = HMM_PREFIX(DivideMat4f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideQuaternionFCPP, 1) -HMM_INLINE hmm_quaternion HMM_PREFIX(Divide)(hmm_quaternion Left, float Right) -{ - ASSERT_COVERED(HMM_DivideQuaternionFCPP); - - hmm_quaternion Result = HMM_PREFIX(DivideQuaternionF)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_EqualsVec2CPP, 1) -HMM_INLINE hmm_bool HMM_PREFIX(Equals)(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_EqualsVec2CPP); - - hmm_bool Result = HMM_PREFIX(EqualsVec2)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_EqualsVec3CPP, 1) -HMM_INLINE hmm_bool HMM_PREFIX(Equals)(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_EqualsVec3CPP); - - hmm_bool Result = HMM_PREFIX(EqualsVec3)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_EqualsVec4CPP, 1) -HMM_INLINE hmm_bool HMM_PREFIX(Equals)(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_EqualsVec4CPP); - - hmm_bool Result = HMM_PREFIX(EqualsVec4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_AddVec2Op, 1) -HMM_INLINE hmm_vec2 operator+(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_AddVec2Op); - - hmm_vec2 Result = HMM_PREFIX(AddVec2)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_AddVec3Op, 1) -HMM_INLINE hmm_vec3 operator+(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_AddVec3Op); - - hmm_vec3 Result = HMM_PREFIX(AddVec3)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_AddVec4Op, 1) -HMM_INLINE hmm_vec4 operator+(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_AddVec4Op); - - hmm_vec4 Result = HMM_PREFIX(AddVec4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_AddMat4Op, 1) -HMM_INLINE hmm_mat4 operator+(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_AddMat4Op); - - hmm_mat4 Result = HMM_PREFIX(AddMat4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_AddQuaternionOp, 1) -HMM_INLINE hmm_quaternion operator+(hmm_quaternion Left, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_AddQuaternionOp); - - hmm_quaternion Result = HMM_PREFIX(AddQuaternion)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_SubtractVec2Op, 1) -HMM_INLINE hmm_vec2 operator-(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_SubtractVec2Op); - - hmm_vec2 Result = HMM_PREFIX(SubtractVec2)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_SubtractVec3Op, 1) -HMM_INLINE hmm_vec3 operator-(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_SubtractVec3Op); - - hmm_vec3 Result = HMM_PREFIX(SubtractVec3)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_SubtractVec4Op, 1) -HMM_INLINE hmm_vec4 operator-(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_SubtractVec4Op); - - hmm_vec4 Result = HMM_PREFIX(SubtractVec4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_SubtractMat4Op, 1) -HMM_INLINE hmm_mat4 operator-(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_SubtractMat4Op); - - hmm_mat4 Result = HMM_PREFIX(SubtractMat4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_SubtractQuaternionOp, 1) -HMM_INLINE hmm_quaternion operator-(hmm_quaternion Left, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_SubtractQuaternionOp); - - hmm_quaternion Result = HMM_PREFIX(SubtractQuaternion)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec2Op, 1) -HMM_INLINE hmm_vec2 operator*(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec2Op); - - hmm_vec2 Result = HMM_PREFIX(MultiplyVec2)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec3Op, 1) -HMM_INLINE hmm_vec3 operator*(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec3Op); - - hmm_vec3 Result = HMM_PREFIX(MultiplyVec3)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec4Op, 1) -HMM_INLINE hmm_vec4 operator*(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec4Op); - - hmm_vec4 Result = HMM_PREFIX(MultiplyVec4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyMat4Op, 1) -HMM_INLINE hmm_mat4 operator*(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_MultiplyMat4Op); - - hmm_mat4 Result = HMM_PREFIX(MultiplyMat4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyQuaternionOp, 1) -HMM_INLINE hmm_quaternion operator*(hmm_quaternion Left, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_MultiplyQuaternionOp); - - hmm_quaternion Result = HMM_PREFIX(MultiplyQuaternion)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec2fOp, 1) -HMM_INLINE hmm_vec2 operator*(hmm_vec2 Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyVec2fOp); - - hmm_vec2 Result = HMM_PREFIX(MultiplyVec2f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec3fOp, 1) -HMM_INLINE hmm_vec3 operator*(hmm_vec3 Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyVec3fOp); - - hmm_vec3 Result = HMM_PREFIX(MultiplyVec3f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec4fOp, 1) -HMM_INLINE hmm_vec4 operator*(hmm_vec4 Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyVec4fOp); - - hmm_vec4 Result = HMM_PREFIX(MultiplyVec4f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyMat4fOp, 1) -HMM_INLINE hmm_mat4 operator*(hmm_mat4 Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyMat4fOp); - - hmm_mat4 Result = HMM_PREFIX(MultiplyMat4f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyQuaternionFOp, 1) -HMM_INLINE hmm_quaternion operator*(hmm_quaternion Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyQuaternionFOp); - - hmm_quaternion Result = HMM_PREFIX(MultiplyQuaternionF)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec2fOpLeft, 1) -HMM_INLINE hmm_vec2 operator*(float Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec2fOpLeft); - - hmm_vec2 Result = HMM_PREFIX(MultiplyVec2f)(Right, Left); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec3fOpLeft, 1) -HMM_INLINE hmm_vec3 operator*(float Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec3fOpLeft); - - hmm_vec3 Result = HMM_PREFIX(MultiplyVec3f)(Right, Left); - - return (Result); -} - -COVERAGE(HMM_MultiplyVec4fOpLeft, 1) -HMM_INLINE hmm_vec4 operator*(float Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec4fOpLeft); - - hmm_vec4 Result = HMM_PREFIX(MultiplyVec4f)(Right, Left); - - return (Result); -} - -COVERAGE(HMM_MultiplyMat4fOpLeft, 1) -HMM_INLINE hmm_mat4 operator*(float Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_MultiplyMat4fOpLeft); - - hmm_mat4 Result = HMM_PREFIX(MultiplyMat4f)(Right, Left); - - return (Result); -} - -COVERAGE(HMM_MultiplyQuaternionFOpLeft, 1) -HMM_INLINE hmm_quaternion operator*(float Left, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_MultiplyQuaternionFOpLeft); - - hmm_quaternion Result = HMM_PREFIX(MultiplyQuaternionF)(Right, Left); - - return (Result); -} - -COVERAGE(HMM_MultiplyMat4ByVec4Op, 1) -HMM_INLINE hmm_vec4 operator*(hmm_mat4 Matrix, hmm_vec4 Vector) -{ - ASSERT_COVERED(HMM_MultiplyMat4ByVec4Op); - - hmm_vec4 Result = HMM_PREFIX(MultiplyMat4ByVec4)(Matrix, Vector); - - return (Result); -} - -COVERAGE(HMM_DivideVec2Op, 1) -HMM_INLINE hmm_vec2 operator/(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_DivideVec2Op); - - hmm_vec2 Result = HMM_PREFIX(DivideVec2)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec3Op, 1) -HMM_INLINE hmm_vec3 operator/(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_DivideVec3Op); - - hmm_vec3 Result = HMM_PREFIX(DivideVec3)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec4Op, 1) -HMM_INLINE hmm_vec4 operator/(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_DivideVec4Op); - - hmm_vec4 Result = HMM_PREFIX(DivideVec4)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec2fOp, 1) -HMM_INLINE hmm_vec2 operator/(hmm_vec2 Left, float Right) -{ - ASSERT_COVERED(HMM_DivideVec2fOp); - - hmm_vec2 Result = HMM_PREFIX(DivideVec2f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec3fOp, 1) -HMM_INLINE hmm_vec3 operator/(hmm_vec3 Left, float Right) -{ - ASSERT_COVERED(HMM_DivideVec3fOp); - - hmm_vec3 Result = HMM_PREFIX(DivideVec3f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideVec4fOp, 1) -HMM_INLINE hmm_vec4 operator/(hmm_vec4 Left, float Right) -{ - ASSERT_COVERED(HMM_DivideVec4fOp); - - hmm_vec4 Result = HMM_PREFIX(DivideVec4f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideMat4fOp, 1) -HMM_INLINE hmm_mat4 operator/(hmm_mat4 Left, float Right) -{ - ASSERT_COVERED(HMM_DivideMat4fOp); - - hmm_mat4 Result = HMM_PREFIX(DivideMat4f)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_DivideQuaternionFOp, 1) -HMM_INLINE hmm_quaternion operator/(hmm_quaternion Left, float Right) -{ - ASSERT_COVERED(HMM_DivideQuaternionFOp); - - hmm_quaternion Result = HMM_PREFIX(DivideQuaternionF)(Left, Right); - - return (Result); -} - -COVERAGE(HMM_AddVec2Assign, 1) -HMM_INLINE hmm_vec2 &operator+=(hmm_vec2 &Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_AddVec2Assign); - - return (Left = Left + Right); -} - -COVERAGE(HMM_AddVec3Assign, 1) -HMM_INLINE hmm_vec3 &operator+=(hmm_vec3 &Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_AddVec3Assign); - - return (Left = Left + Right); -} - -COVERAGE(HMM_AddVec4Assign, 1) -HMM_INLINE hmm_vec4 &operator+=(hmm_vec4 &Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_AddVec4Assign); - - return (Left = Left + Right); -} - -COVERAGE(HMM_AddMat4Assign, 1) -HMM_INLINE hmm_mat4 &operator+=(hmm_mat4 &Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_AddMat4Assign); - - return (Left = Left + Right); -} - -COVERAGE(HMM_AddQuaternionAssign, 1) -HMM_INLINE hmm_quaternion &operator+=(hmm_quaternion &Left, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_AddQuaternionAssign); - - return (Left = Left + Right); -} - -COVERAGE(HMM_SubtractVec2Assign, 1) -HMM_INLINE hmm_vec2 &operator-=(hmm_vec2 &Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_SubtractVec2Assign); - - return (Left = Left - Right); -} - -COVERAGE(HMM_SubtractVec3Assign, 1) -HMM_INLINE hmm_vec3 &operator-=(hmm_vec3 &Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_SubtractVec3Assign); - - return (Left = Left - Right); -} - -COVERAGE(HMM_SubtractVec4Assign, 1) -HMM_INLINE hmm_vec4 &operator-=(hmm_vec4 &Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_SubtractVec4Assign); - - return (Left = Left - Right); -} - -COVERAGE(HMM_SubtractMat4Assign, 1) -HMM_INLINE hmm_mat4 &operator-=(hmm_mat4 &Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_SubtractMat4Assign); - - return (Left = Left - Right); -} - -COVERAGE(HMM_SubtractQuaternionAssign, 1) -HMM_INLINE hmm_quaternion &operator-=(hmm_quaternion &Left, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_SubtractQuaternionAssign); - - return (Left = Left - Right); -} - -COVERAGE(HMM_MultiplyVec2Assign, 1) -HMM_INLINE hmm_vec2 &operator*=(hmm_vec2 &Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec2Assign); - - return (Left = Left * Right); -} - -COVERAGE(HMM_MultiplyVec3Assign, 1) -HMM_INLINE hmm_vec3 &operator*=(hmm_vec3 &Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec3Assign); - - return (Left = Left * Right); -} - -COVERAGE(HMM_MultiplyVec4Assign, 1) -HMM_INLINE hmm_vec4 &operator*=(hmm_vec4 &Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_MultiplyVec4Assign); - - return (Left = Left * Right); -} - -COVERAGE(HMM_MultiplyVec2fAssign, 1) -HMM_INLINE hmm_vec2 &operator*=(hmm_vec2 &Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyVec2fAssign); - - return (Left = Left * Right); -} - -COVERAGE(HMM_MultiplyVec3fAssign, 1) -HMM_INLINE hmm_vec3 &operator*=(hmm_vec3 &Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyVec3fAssign); - - return (Left = Left * Right); -} - -COVERAGE(HMM_MultiplyVec4fAssign, 1) -HMM_INLINE hmm_vec4 &operator*=(hmm_vec4 &Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyVec4fAssign); - - return (Left = Left * Right); -} - -COVERAGE(HMM_MultiplyMat4fAssign, 1) -HMM_INLINE hmm_mat4 &operator*=(hmm_mat4 &Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyMat4fAssign); - - return (Left = Left * Right); -} - -COVERAGE(HMM_MultiplyQuaternionFAssign, 1) -HMM_INLINE hmm_quaternion &operator*=(hmm_quaternion &Left, float Right) -{ - ASSERT_COVERED(HMM_MultiplyQuaternionFAssign); - - return (Left = Left * Right); -} - -COVERAGE(HMM_DivideVec2Assign, 1) -HMM_INLINE hmm_vec2 &operator/=(hmm_vec2 &Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_DivideVec2Assign); - - return (Left = Left / Right); -} - -COVERAGE(HMM_DivideVec3Assign, 1) -HMM_INLINE hmm_vec3 &operator/=(hmm_vec3 &Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_DivideVec3Assign); - - return (Left = Left / Right); -} - -COVERAGE(HMM_DivideVec4Assign, 1) -HMM_INLINE hmm_vec4 &operator/=(hmm_vec4 &Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_DivideVec4Assign); - - return (Left = Left / Right); -} - -COVERAGE(HMM_DivideVec2fAssign, 1) -HMM_INLINE hmm_vec2 &operator/=(hmm_vec2 &Left, float Right) -{ - ASSERT_COVERED(HMM_DivideVec2fAssign); - - return (Left = Left / Right); -} - -COVERAGE(HMM_DivideVec3fAssign, 1) -HMM_INLINE hmm_vec3 &operator/=(hmm_vec3 &Left, float Right) -{ - ASSERT_COVERED(HMM_DivideVec3fAssign); - - return (Left = Left / Right); -} - -COVERAGE(HMM_DivideVec4fAssign, 1) -HMM_INLINE hmm_vec4 &operator/=(hmm_vec4 &Left, float Right) -{ - ASSERT_COVERED(HMM_DivideVec4fAssign); - - return (Left = Left / Right); -} - -COVERAGE(HMM_DivideMat4fAssign, 1) -HMM_INLINE hmm_mat4 &operator/=(hmm_mat4 &Left, float Right) -{ - ASSERT_COVERED(HMM_DivideMat4fAssign); - - return (Left = Left / Right); -} - -COVERAGE(HMM_DivideQuaternionFAssign, 1) -HMM_INLINE hmm_quaternion &operator/=(hmm_quaternion &Left, float Right) -{ - ASSERT_COVERED(HMM_DivideQuaternionFAssign); - - return (Left = Left / Right); -} - -COVERAGE(HMM_EqualsVec2Op, 1) -HMM_INLINE hmm_bool operator==(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_EqualsVec2Op); - - return HMM_PREFIX(EqualsVec2)(Left, Right); -} - -COVERAGE(HMM_EqualsVec3Op, 1) -HMM_INLINE hmm_bool operator==(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_EqualsVec3Op); - - return HMM_PREFIX(EqualsVec3)(Left, Right); -} - -COVERAGE(HMM_EqualsVec4Op, 1) -HMM_INLINE hmm_bool operator==(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_EqualsVec4Op); - - return HMM_PREFIX(EqualsVec4)(Left, Right); -} - -COVERAGE(HMM_EqualsVec2OpNot, 1) -HMM_INLINE hmm_bool operator!=(hmm_vec2 Left, hmm_vec2 Right) -{ - ASSERT_COVERED(HMM_EqualsVec2OpNot); - - return !HMM_PREFIX(EqualsVec2)(Left, Right); -} - -COVERAGE(HMM_EqualsVec3OpNot, 1) -HMM_INLINE hmm_bool operator!=(hmm_vec3 Left, hmm_vec3 Right) -{ - ASSERT_COVERED(HMM_EqualsVec3OpNot); - - return !HMM_PREFIX(EqualsVec3)(Left, Right); -} - -COVERAGE(HMM_EqualsVec4OpNot, 1) -HMM_INLINE hmm_bool operator!=(hmm_vec4 Left, hmm_vec4 Right) -{ - ASSERT_COVERED(HMM_EqualsVec4OpNot); - - return !HMM_PREFIX(EqualsVec4)(Left, Right); -} - -#endif /* __cplusplus */ - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif - -#endif /* HANDMADE_MATH_H */ - -#ifdef HANDMADE_MATH_IMPLEMENTATION - -COVERAGE(HMM_Power, 2) -float HMM_PREFIX(Power)(float Base, int Exponent) -{ - ASSERT_COVERED(HMM_Power); - - float Result = 1.0f; - float Mul = Exponent < 0 ? 1.f / Base : Base; - int X = Exponent < 0 ? -Exponent : Exponent; - while (X) - { - if (X & 1) - { - ASSERT_COVERED(HMM_Power); - - Result *= Mul; - } - - Mul *= Mul; - X >>= 1; - } - - return (Result); -} - -#ifndef HANDMADE_MATH__USE_SSE -COVERAGE(HMM_Transpose, 1) -hmm_mat4 HMM_PREFIX(Transpose)(hmm_mat4 Matrix) -{ - ASSERT_COVERED(HMM_Transpose); - - hmm_mat4 Result; - - int Columns; - for(Columns = 0; Columns < 4; ++Columns) - { - int Rows; - for(Rows = 0; Rows < 4; ++Rows) - { - Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows]; - } - } - - return (Result); -} -#endif - -#ifndef HANDMADE_MATH__USE_SSE -COVERAGE(HMM_AddMat4, 1) -hmm_mat4 HMM_PREFIX(AddMat4)(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_AddMat4); - - hmm_mat4 Result; - - 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); -} -#endif - -#ifndef HANDMADE_MATH__USE_SSE -COVERAGE(HMM_SubtractMat4, 1) -hmm_mat4 HMM_PREFIX(SubtractMat4)(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_SubtractMat4); - - hmm_mat4 Result; - - 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); -} -#endif - -COVERAGE(HMM_MultiplyMat4, 1) -hmm_mat4 HMM_PREFIX(MultiplyMat4)(hmm_mat4 Left, hmm_mat4 Right) -{ - ASSERT_COVERED(HMM_MultiplyMat4); - - hmm_mat4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.Columns[0] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[0], Left); - Result.Columns[1] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[1], Left); - Result.Columns[2] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[2], Left); - Result.Columns[3] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[3], Left); -#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); -} - -#ifndef HANDMADE_MATH__USE_SSE -COVERAGE(HMM_MultiplyMat4f, 1) -hmm_mat4 HMM_PREFIX(MultiplyMat4f)(hmm_mat4 Matrix, float Scalar) -{ - ASSERT_COVERED(HMM_MultiplyMat4f); - - hmm_mat4 Result; - - 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); -} -#endif - -COVERAGE(HMM_MultiplyMat4ByVec4, 1) -hmm_vec4 HMM_PREFIX(MultiplyMat4ByVec4)(hmm_mat4 Matrix, hmm_vec4 Vector) -{ - ASSERT_COVERED(HMM_MultiplyMat4ByVec4); - - hmm_vec4 Result; - -#ifdef HANDMADE_MATH__USE_SSE - Result.InternalElementsSSE = HMM_PREFIX(LinearCombineSSE)(Vector.InternalElementsSSE, Matrix); -#else - 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; - } -#endif - - return (Result); -} - -#ifndef HANDMADE_MATH__USE_SSE -COVERAGE(HMM_DivideMat4f, 1); -hmm_mat4 HMM_PREFIX(DivideMat4f)(hmm_mat4 Matrix, float Scalar) -{ - ASSERT_COVERED(HMM_DivideMat4f); - - hmm_mat4 Result; - - 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); -} -#endif - -COVERAGE(HMM_Rotate, 1) -hmm_mat4 HMM_PREFIX(Rotate)(float Angle, hmm_vec3 Axis) -{ - ASSERT_COVERED(HMM_Rotate); - - hmm_mat4 Result = HMM_PREFIX(Mat4d)(1.0f); - - Axis = HMM_PREFIX(NormalizeVec3)(Axis); - - float SinTheta = HMM_PREFIX(SinF)(HMM_PREFIX(ToRadians)(Angle)); - float CosTheta = HMM_PREFIX(CosF)(HMM_PREFIX(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); -} - -COVERAGE(HMM_LookAt, 1) -hmm_mat4 HMM_PREFIX(LookAt)(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up) -{ - ASSERT_COVERED(HMM_LookAt); - - hmm_mat4 Result; - - hmm_vec3 F = HMM_PREFIX(NormalizeVec3)(HMM_PREFIX(SubtractVec3)(Center, Eye)); - hmm_vec3 S = HMM_PREFIX(NormalizeVec3)(HMM_PREFIX(Cross)(F, Up)); - hmm_vec3 U = HMM_PREFIX(Cross)(S, F); - - Result.Elements[0][0] = S.X; - Result.Elements[0][1] = U.X; - Result.Elements[0][2] = -F.X; - Result.Elements[0][3] = 0.0f; - - Result.Elements[1][0] = S.Y; - Result.Elements[1][1] = U.Y; - Result.Elements[1][2] = -F.Y; - Result.Elements[1][3] = 0.0f; - - Result.Elements[2][0] = S.Z; - Result.Elements[2][1] = U.Z; - Result.Elements[2][2] = -F.Z; - Result.Elements[2][3] = 0.0f; - - Result.Elements[3][0] = -HMM_PREFIX(DotVec3)(S, Eye); - Result.Elements[3][1] = -HMM_PREFIX(DotVec3)(U, Eye); - Result.Elements[3][2] = HMM_PREFIX(DotVec3)(F, Eye); - Result.Elements[3][3] = 1.0f; - - return (Result); -} - -COVERAGE(HMM_InverseQuaternion, 1) -hmm_quaternion HMM_PREFIX(InverseQuaternion)(hmm_quaternion Left) -{ - ASSERT_COVERED(HMM_InverseQuaternion); - - hmm_quaternion Conjugate; - hmm_quaternion Result; - float Norm = 0; - float NormSquared = 0; - - Conjugate.X = -Left.X; - Conjugate.Y = -Left.Y; - Conjugate.Z = -Left.Z; - Conjugate.W = Left.W; - - Norm = HMM_PREFIX(SquareRootF)(HMM_PREFIX(DotQuaternion)(Left, Left)); - NormSquared = Norm * Norm; - - Result = HMM_PREFIX(DivideQuaternionF)(Conjugate, NormSquared); - - return (Result); -} - -COVERAGE(HMM_Slerp, 1) -hmm_quaternion HMM_PREFIX(Slerp)(hmm_quaternion Left, float Time, hmm_quaternion Right) -{ - ASSERT_COVERED(HMM_Slerp); - - hmm_quaternion Result; - hmm_quaternion QuaternionLeft; - hmm_quaternion QuaternionRight; - - float Cos_Theta = HMM_PREFIX(DotQuaternion)(Left, Right); - float Angle = HMM_PREFIX(ACosF)(Cos_Theta); - - float S1 = HMM_PREFIX(SinF)((1.0f - Time) * Angle); - float S2 = HMM_PREFIX(SinF)(Time * Angle); - float Is = 1.0f / HMM_PREFIX(SinF)(Angle); - - QuaternionLeft = HMM_PREFIX(MultiplyQuaternionF)(Left, S1); - QuaternionRight = HMM_PREFIX(MultiplyQuaternionF)(Right, S2); - - Result = HMM_PREFIX(AddQuaternion)(QuaternionLeft, QuaternionRight); - Result = HMM_PREFIX(MultiplyQuaternionF)(Result, Is); - - return (Result); -} - -COVERAGE(HMM_QuaternionToMat4, 1) -hmm_mat4 HMM_PREFIX(QuaternionToMat4)(hmm_quaternion Left) -{ - ASSERT_COVERED(HMM_QuaternionToMat4); - - hmm_mat4 Result; - - hmm_quaternion NormalizedQuaternion = HMM_PREFIX(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[0][3] = 0.0f; - - 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[1][3] = 0.0f; - - 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); - Result.Elements[2][3] = 0.0f; - - Result.Elements[3][0] = 0.0f; - Result.Elements[3][1] = 0.0f; - Result.Elements[3][2] = 0.0f; - Result.Elements[3][3] = 1.0f; - - return (Result); -} - -// This method taken from Mike Day at Insomniac Games. -// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf -// -// Note that as mentioned at the top of the paper, the paper assumes the matrix -// would be *post*-multiplied to a vector to rotate it, meaning the matrix is -// the transpose of what we're dealing with. But, because our matrices are -// stored in column-major order, the indices *appear* to match the paper. -// -// For example, m12 in the paper is row 1, column 2. We need to transpose it to -// row 2, column 1. But, because the column comes first when referencing -// elements, it looks like M.Elements[1][2]. -// -// Don't be confused! Or if you must be confused, at least trust this -// comment. :) -COVERAGE(HMM_Mat4ToQuaternion, 4) -hmm_quaternion HMM_PREFIX(Mat4ToQuaternion)(hmm_mat4 M) -{ - float T; - hmm_quaternion Q; - - if (M.Elements[2][2] < 0.0f) { - if (M.Elements[0][0] > M.Elements[1][1]) { - ASSERT_COVERED(HMM_Mat4ToQuaternion); - - T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2]; - Q = HMM_PREFIX(Quaternion)( - T, - M.Elements[0][1] + M.Elements[1][0], - M.Elements[2][0] + M.Elements[0][2], - M.Elements[1][2] - M.Elements[2][1] - ); - } else { - ASSERT_COVERED(HMM_Mat4ToQuaternion); - - T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2]; - Q = HMM_PREFIX(Quaternion)( - M.Elements[0][1] + M.Elements[1][0], - T, - M.Elements[1][2] + M.Elements[2][1], - M.Elements[2][0] - M.Elements[0][2] - ); - } - } else { - if (M.Elements[0][0] < -M.Elements[1][1]) { - ASSERT_COVERED(HMM_Mat4ToQuaternion); - - T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2]; - Q = HMM_PREFIX(Quaternion)( - M.Elements[2][0] + M.Elements[0][2], - M.Elements[1][2] + M.Elements[2][1], - T, - M.Elements[0][1] - M.Elements[1][0] - ); - } else { - ASSERT_COVERED(HMM_Mat4ToQuaternion); - - T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2]; - Q = HMM_PREFIX(Quaternion)( - M.Elements[1][2] - M.Elements[2][1], - M.Elements[2][0] - M.Elements[0][2], - M.Elements[0][1] - M.Elements[1][0], - T - ); - } - } - - Q = HMM_PREFIX(MultiplyQuaternionF)(Q, 0.5f / HMM_PREFIX(SquareRootF)(T)); - - return Q; -} - -COVERAGE(HMM_QuaternionFromAxisAngle, 1) -hmm_quaternion HMM_PREFIX(QuaternionFromAxisAngle)(hmm_vec3 Axis, float AngleOfRotation) -{ - ASSERT_COVERED(HMM_QuaternionFromAxisAngle); - - hmm_quaternion Result; - - hmm_vec3 AxisNormalized = HMM_PREFIX(NormalizeVec3)(Axis); - float SineOfRotation = HMM_PREFIX(SinF)(AngleOfRotation / 2.0f); - - Result.XYZ = HMM_PREFIX(MultiplyVec3f)(AxisNormalized, SineOfRotation); - Result.W = HMM_PREFIX(CosF)(AngleOfRotation / 2.0f); - - return (Result); -} - -#endif /* HANDMADE_MATH_IMPLEMENTATION */ \ No newline at end of file diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index e959107..765af09 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -535,6 +535,12 @@ WinMain ( { gs_thread_context ThreadContext = Win32CreateThreadContext(); + gs_allocator_debug AllocDebug = {}; + AllocDebug.AllocationsCountMax = 4096; + AllocDebug.Allocations = (gs_debug_allocation*)Win32Alloc(sizeof(gs_debug_allocation) * AllocDebug.AllocationsCountMax, 0); + + ThreadContext.Allocator.Debug = &AllocDebug; + gs_file_info A = GetFileInfo(ThreadContext.FileHandler, ConstString("C:\\projects\\Lumenarium")); gs_file_info B = GetFileInfo(ThreadContext.FileHandler, ConstString("C:\\projects\\Lumenarium\\")); @@ -556,7 +562,7 @@ WinMain ( Context.MemorySize = MB(64); Context.MemoryBase = (u8*)Win32Alloc(Context.MemorySize, 0); - gs_memory_arena PlatformPermanent = CreateMemoryArena(Context.ThreadContext.Allocator); + gs_memory_arena PlatformPermanent = CreateMemoryArena(Context.ThreadContext.Allocator, "Platform Memory"); s64 PerformanceCountFrequency = GetPerformanceFrequency(); s64 LastFrameEnd = GetWallClock(); diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index 7f21539..c3dcfb1 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -204,9 +204,13 @@ Win32SerialArray_Create(gs_thread_context Context) Win32SerialPortNames = AllocatorAllocArray(Context.Allocator, gs_string, Win32SerialHandlesCountMax); Win32SerialPortFilled = AllocatorAllocArray(Context.Allocator, s32, Win32SerialHandlesCountMax); + u64 PortNameSize = 256; + u64 PortNameBufferSize = PortNameSize * Win32SerialHandlesCountMax; + char* PortNameBuffer = AllocatorAllocArray(Context.Allocator, char, PortNameBufferSize); for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) { - Win32SerialPortNames[i] = AllocatorAllocString(Context.Allocator, 256); + char* NameBase = PortNameBuffer + (PortNameSize * i); + Win32SerialPortNames[i] = MakeString(NameBase, 0, PortNameSize); Win32SerialPortFilled[i] = 0; } } diff --git a/src/app/platform_win32/win32_foldhaus_work_queue.h b/src/app/platform_win32/win32_foldhaus_work_queue.h index b3a2a36..8d18986 100644 --- a/src/app/platform_win32/win32_foldhaus_work_queue.h +++ b/src/app/platform_win32/win32_foldhaus_work_queue.h @@ -48,7 +48,7 @@ Win32CreateThreadContext(gs_memory_arena* Transient = 0) else { Result.Transient = (gs_memory_arena*)AllocatorAlloc(Result.Allocator, sizeof(gs_memory_arena)).Memory; - *Result.Transient = CreateMemoryArena(Result.Allocator); + *Result.Transient = CreateMemoryArena(Result.Allocator, "Tctx Transient"); } Result.FileHandler = CreateFileHandler(Win32GetFileInfo, Win32ReadEntireFile, diff --git a/src/app/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp similarity index 98% rename from src/app/blumen_lumen.cpp rename to src/app/ss_blumen_lumen/blumen_lumen.cpp index 09cf12d..698451f 100644 --- a/src/app/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -24,10 +24,8 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) while (*Data->Running) { -#if 1 if (SocketQueryStatus(Data->SocketManager, Data->ListenSocket)) { - // TODO(pjs): Removing this block for now - nothing is wrong with it except that SocketPeek is still blocking for some reason if (SocketPeek(Data->SocketManager, Data->ListenSocket)) { // TODO(pjs): Make this a peek operation @@ -41,7 +39,6 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) } } } -#endif while (Data->OutgoingMsgQueue->ReadHead != Data->OutgoingMsgQueue->WriteHead) { diff --git a/src/app/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h similarity index 100% rename from src/app/blumen_lumen.h rename to src/app/ss_blumen_lumen/blumen_lumen.h diff --git a/src/app/sse_mathfun.h b/src/app/sse_mathfun.h deleted file mode 100644 index 16b22e9..0000000 --- a/src/app/sse_mathfun.h +++ /dev/null @@ -1,711 +0,0 @@ -/* SIMD (SSE1+MMX or SSE2) implementation of sin, cos, exp and log - - Inspired by Intel Approximate Math library, and based on the - corresponding algorithms of the cephes math library - - The default is to use the SSE1 version. If you define USE_SSE2 the - the SSE2 intrinsics will be used in place of the MMX intrinsics. Do - not expect any significant performance improvement with SSE2. -*/ - -/* Copyright (C) 2007 Julien Pommier - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - (this is the zlib license) -*/ - -#include - -/* yes I know, the top of this file is quite ugly */ - -#ifdef _MSC_VER /* visual c++ */ -# define ALIGN16_BEG __declspec(align(16)) -# define ALIGN16_END -#else /* gcc or icc */ -# define ALIGN16_BEG -# define ALIGN16_END __attribute__((aligned(16))) -#endif - -/* __m128 is ugly to write */ -typedef __m128 v4sf; // vector of 4 float (sse1) - -#ifdef USE_SSE2 -# include -typedef __m128i v4si; // vector of 4 int (sse2) -#else -typedef __m64 v2si; // vector of 2 int (mmx) -#endif - -/* declare some SSE constants -- why can't I figure a better way to do that? */ -#define _PS_CONST(Name, Val) \ -static const ALIGN16_BEG float _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val } -#define _PI32_CONST(Name, Val) \ -static const ALIGN16_BEG int _pi32_##Name[4] ALIGN16_END = { Val, Val, Val, Val } -#define _PS_CONST_TYPE(Name, Type, Val) \ -static const ALIGN16_BEG Type _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val } - -_PS_CONST(1 , 1.0f); -_PS_CONST(0p5, 0.5f); -/* the smallest non denormalized float number */ -_PS_CONST_TYPE(min_norm_pos, int, 0x00800000); -_PS_CONST_TYPE(mant_mask, int, 0x7f800000); -_PS_CONST_TYPE(inv_mant_mask, int, ~0x7f800000); - -_PS_CONST_TYPE(sign_mask, int, (int)0x80000000); -_PS_CONST_TYPE(inv_sign_mask, int, ~0x80000000); - -_PI32_CONST(1, 1); -_PI32_CONST(inv1, ~1); -_PI32_CONST(2, 2); -_PI32_CONST(4, 4); -_PI32_CONST(0x7f, 0x7f); - -_PS_CONST(cephes_SQRTHF, 0.707106781186547524); -_PS_CONST(cephes_log_p0, 7.0376836292E-2); -_PS_CONST(cephes_log_p1, - 1.1514610310E-1); -_PS_CONST(cephes_log_p2, 1.1676998740E-1); -_PS_CONST(cephes_log_p3, - 1.2420140846E-1); -_PS_CONST(cephes_log_p4, + 1.4249322787E-1); -_PS_CONST(cephes_log_p5, - 1.6668057665E-1); -_PS_CONST(cephes_log_p6, + 2.0000714765E-1); -_PS_CONST(cephes_log_p7, - 2.4999993993E-1); -_PS_CONST(cephes_log_p8, + 3.3333331174E-1); -_PS_CONST(cephes_log_q1, -2.12194440e-4); -_PS_CONST(cephes_log_q2, 0.693359375); - -#ifndef USE_SSE2 -typedef union xmm_mm_union { - __m128 xmm; - __m64 mm[2]; -} xmm_mm_union; - -#define COPY_XMM_TO_MM(xmm_, mm0_, mm1_) { \ - xmm_mm_union u; u.xmm = xmm_; \ - mm0_ = u.mm[0]; \ - mm1_ = u.mm[1]; \ -} - -#define COPY_MM_TO_XMM(mm0_, mm1_, xmm_) { \ - xmm_mm_union u; u.mm[0]=mm0_; u.mm[1]=mm1_; xmm_ = u.xmm; \ -} - -#endif // USE_SSE2 - -/* natural logarithm computed for 4 simultaneous float - return NaN for x <= 0 -*/ -v4sf log_ps(v4sf x) { -#ifdef USE_SSE2 - v4si emm0; -#else - v2si mm0, mm1; -#endif - v4sf one = *(v4sf*)_ps_1; - - v4sf invalid_mask = _mm_cmple_ps(x, _mm_setzero_ps()); - - x = _mm_max_ps(x, *(v4sf*)_ps_min_norm_pos); /* cut off denormalized stuff */ - -#ifndef USE_SSE2 - /* part 1: x = frexpf(x, &e); */ - COPY_XMM_TO_MM(x, mm0, mm1); - mm0 = _mm_srli_pi32(mm0, 23); - mm1 = _mm_srli_pi32(mm1, 23); -#else - emm0 = _mm_srli_epi32(_mm_castps_si128(x), 23); -#endif - /* keep only the fractional part */ - x = _mm_and_ps(x, *(v4sf*)_ps_inv_mant_mask); - x = _mm_or_ps(x, *(v4sf*)_ps_0p5); - -#ifndef USE_SSE2 - /* now e=mm0:mm1 contain the really base-2 exponent */ - mm0 = _mm_sub_pi32(mm0, *(v2si*)_pi32_0x7f); - mm1 = _mm_sub_pi32(mm1, *(v2si*)_pi32_0x7f); - v4sf e = _mm_cvtpi32x2_ps(mm0, mm1); - _mm_empty(); /* bye bye mmx */ -#else - emm0 = _mm_sub_epi32(emm0, *(v4si*)_pi32_0x7f); - v4sf e = _mm_cvtepi32_ps(emm0); -#endif - - e = _mm_add_ps(e, one); - - /* part2: - if( x < SQRTHF ) { - e -= 1; - x = x + x - 1.0; - } else { x = x - 1.0; } - */ - v4sf mask = _mm_cmplt_ps(x, *(v4sf*)_ps_cephes_SQRTHF); - v4sf tmp = _mm_and_ps(x, mask); - x = _mm_sub_ps(x, one); - e = _mm_sub_ps(e, _mm_and_ps(one, mask)); - x = _mm_add_ps(x, tmp); - - - v4sf z = _mm_mul_ps(x,x); - - v4sf y = *(v4sf*)_ps_cephes_log_p0; - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p1); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p2); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p3); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p4); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p5); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p6); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p7); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p8); - y = _mm_mul_ps(y, x); - - y = _mm_mul_ps(y, z); - - - tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q1); - y = _mm_add_ps(y, tmp); - - - tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5); - y = _mm_sub_ps(y, tmp); - - tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q2); - x = _mm_add_ps(x, y); - x = _mm_add_ps(x, tmp); - x = _mm_or_ps(x, invalid_mask); // negative arg will be NAN - return x; -} - -_PS_CONST(exp_hi, 88.3762626647949f); -_PS_CONST(exp_lo, -88.3762626647949f); - -_PS_CONST(cephes_LOG2EF, 1.44269504088896341); -_PS_CONST(cephes_exp_C1, 0.693359375); -_PS_CONST(cephes_exp_C2, -2.12194440e-4); - -_PS_CONST(cephes_exp_p0, 1.9875691500E-4); -_PS_CONST(cephes_exp_p1, 1.3981999507E-3); -_PS_CONST(cephes_exp_p2, 8.3334519073E-3); -_PS_CONST(cephes_exp_p3, 4.1665795894E-2); -_PS_CONST(cephes_exp_p4, 1.6666665459E-1); -_PS_CONST(cephes_exp_p5, 5.0000001201E-1); - -v4sf exp_ps(v4sf x) { - v4sf tmp = _mm_setzero_ps(), fx; -#ifdef USE_SSE2 - v4si emm0; -#else - v2si mm0, mm1; -#endif - v4sf one = *(v4sf*)_ps_1; - - x = _mm_min_ps(x, *(v4sf*)_ps_exp_hi); - x = _mm_max_ps(x, *(v4sf*)_ps_exp_lo); - - /* express exp(x) as exp(g + n*log(2)) */ - fx = _mm_mul_ps(x, *(v4sf*)_ps_cephes_LOG2EF); - fx = _mm_add_ps(fx, *(v4sf*)_ps_0p5); - - /* how to perform a floorf with SSE: just below */ -#ifndef USE_SSE2 - /* step 1 : cast to int */ - tmp = _mm_movehl_ps(tmp, fx); - mm0 = _mm_cvttps_pi32(fx); - mm1 = _mm_cvttps_pi32(tmp); - /* step 2 : cast back to float */ - tmp = _mm_cvtpi32x2_ps(mm0, mm1); -#else - emm0 = _mm_cvttps_epi32(fx); - tmp = _mm_cvtepi32_ps(emm0); -#endif - /* if greater, substract 1 */ - v4sf mask = _mm_cmpgt_ps(tmp, fx); - mask = _mm_and_ps(mask, one); - fx = _mm_sub_ps(tmp, mask); - - tmp = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C1); - v4sf z = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C2); - x = _mm_sub_ps(x, tmp); - x = _mm_sub_ps(x, z); - - z = _mm_mul_ps(x,x); - - v4sf y = *(v4sf*)_ps_cephes_exp_p0; - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p1); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p2); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p3); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p4); - y = _mm_mul_ps(y, x); - y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p5); - y = _mm_mul_ps(y, z); - y = _mm_add_ps(y, x); - y = _mm_add_ps(y, one); - - /* build 2^n */ -#ifndef USE_SSE2 - z = _mm_movehl_ps(z, fx); - mm0 = _mm_cvttps_pi32(fx); - mm1 = _mm_cvttps_pi32(z); - mm0 = _mm_add_pi32(mm0, *(v2si*)_pi32_0x7f); - mm1 = _mm_add_pi32(mm1, *(v2si*)_pi32_0x7f); - mm0 = _mm_slli_pi32(mm0, 23); - mm1 = _mm_slli_pi32(mm1, 23); - - v4sf pow2n; - COPY_MM_TO_XMM(mm0, mm1, pow2n); - _mm_empty(); -#else - emm0 = _mm_cvttps_epi32(fx); - emm0 = _mm_add_epi32(emm0, *(v4si*)_pi32_0x7f); - emm0 = _mm_slli_epi32(emm0, 23); - v4sf pow2n = _mm_castsi128_ps(emm0); -#endif - y = _mm_mul_ps(y, pow2n); - return y; -} - -_PS_CONST(minus_cephes_DP1, -0.78515625); -_PS_CONST(minus_cephes_DP2, -2.4187564849853515625e-4); -_PS_CONST(minus_cephes_DP3, -3.77489497744594108e-8); -_PS_CONST(sincof_p0, -1.9515295891E-4); -_PS_CONST(sincof_p1, 8.3321608736E-3); -_PS_CONST(sincof_p2, -1.6666654611E-1); -_PS_CONST(coscof_p0, 2.443315711809948E-005); -_PS_CONST(coscof_p1, -1.388731625493765E-003); -_PS_CONST(coscof_p2, 4.166664568298827E-002); -_PS_CONST(cephes_FOPI, 1.27323954473516); // 4 / M_PI - - -/* evaluation of 4 sines at onces, using only SSE1+MMX intrinsics so - it runs also on old athlons XPs and the pentium III of your grand - mother. - - The code is the exact rewriting of the cephes sinf function. - Precision is excellent as long as x < 8192 (I did not bother to - take into account the special handling they have for greater values - -- it does not return garbage for arguments over 8192, though, but - the extra precision is missing). - - Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the - surprising but correct result. - - Performance is also surprisingly good, 1.33 times faster than the - macos vsinf SSE2 function, and 1.5 times faster than the - __vrs4_sinf of amd's ACML (which is only available in 64 bits). Not - too bad for an SSE1 function (with no special tuning) ! - However the latter libraries probably have a much better handling of NaN, - Inf, denormalized and other special arguments.. - - On my core 1 duo, the execution of this function takes approximately 95 cycles. - - From what I have observed on the experiments with Intel AMath lib, switching to an - SSE2 version would improve the perf by only 10%. - - Since it is based on SSE intrinsics, it has to be compiled at -O2 to - deliver full speed. -*/ -v4sf sin_ps(v4sf x) { // any x - v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y; - -#ifdef USE_SSE2 - v4si emm0, emm2; -#else - v2si mm0, mm1, mm2, mm3; -#endif - sign_bit = x; - /* take the absolute value */ - x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask); - /* extract the sign bit (upper one) */ - sign_bit = _mm_and_ps(sign_bit, *(v4sf*)_ps_sign_mask); - - /* scale by 4/Pi */ - y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI); - -#ifdef USE_SSE2 - /* store the integer part of y in mm0 */ - emm2 = _mm_cvttps_epi32(y); - /* j=(j+1) & (~1) (see the cephes sources) */ - emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1); - emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1); - y = _mm_cvtepi32_ps(emm2); - - /* get the swap sign flag */ - emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4); - emm0 = _mm_slli_epi32(emm0, 29); - /* get the polynom selection mask - there is one polynom for 0 <= x <= Pi/4 - and another one for Pi/4> precision OK for the tan_ps <<- - -checking tan on [-0.49*Pi, 0.49*Pi] -max deviation from tanf(x): 3.8147e-06 at -0.490000009841*Pi, max deviation from cephes_tan(x): -9.53674e-07 - ->> precision OK for the tan_ps <<- - -checking cot on [0.2*Pi, 0.7*Pi] -max deviation from cotf(x): 1.19209e-07 at 0.204303119606*Pi, max deviation from cephes_cot(x): -1.19209e-07 - ->> precision OK for the cot_ps <<- - -checking cot on [0.01*Pi, 0.99*Pi] -max deviation from cotf(x): 3.8147e-06 at 0.987876517942*Pi, max deviation from cephes_cot(x): -9.53674e-07 - ->> precision OK for the cot_ps <<- - -With atan_ps and atan2_ps you get pretty good precision, atan_ps max deviation is < 2e-7 and -atan2_ps max deviation is < 2.5e-7 -*/ - -/* Copyright (C) 2016 Tolga Mizrak - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - (this is the zlib license) -*/ - -#pragma once - -#ifndef _SSE_MATHFUN_EXTENSION_H_INCLUDED_ -#define _SSE_MATHFUN_EXTENSION_H_INCLUDED_ - -#ifndef USE_SSE2 -#error sse1 & mmx version not implemented -#endif - -#ifdef _MSC_VER -#pragma warning( push ) -/* warning C4838: conversion from 'double' to 'const float' requires a narrowing conversion */ -#pragma warning( disable : 4838 ) -/* warning C4305: 'initializing': truncation from 'double' to 'const float' */ -#pragma warning( disable : 4305 ) -#endif - -#include "sse_mathfun.h" - -_PS_CONST( 0, 0 ); -_PS_CONST( 2, 2 ); -_PI32_CONST( neg1, 1 ); - -_PS_CONST( tancof_p0, 9.38540185543E-3 ); -_PS_CONST( tancof_p1, 3.11992232697E-3 ); -_PS_CONST( tancof_p2, 2.44301354525E-2 ); -_PS_CONST( tancof_p3, 5.34112807005E-2 ); -_PS_CONST( tancof_p4, 1.33387994085E-1 ); -_PS_CONST( tancof_p5, 3.33331568548E-1 ); - -_PS_CONST( tancot_eps, 1.0e-4 ); - -v4sf tancot_ps( v4sf x, int cotFlag ) -{ - v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y; - -#ifdef USE_SSE2 - v4si emm2; -#else -#endif - sign_bit = x; - /* take the absolute value */ - x = _mm_and_ps( x, *(v4sf*)_ps_inv_sign_mask ); - /* extract the sign bit (upper one) */ - sign_bit = _mm_and_ps( sign_bit, *(v4sf*)_ps_sign_mask ); - - /* scale by 4/Pi */ - y = _mm_mul_ps( x, *(v4sf*)_ps_cephes_FOPI ); - -#ifdef USE_SSE2 - /* store the integer part of y in mm0 */ - emm2 = _mm_cvttps_epi32( y ); - /* j=(j+1) & (~1) (see the cephes sources) */ - emm2 = _mm_add_epi32( emm2, *(v4si*)_pi32_1 ); - emm2 = _mm_and_si128( emm2, *(v4si*)_pi32_inv1 ); - y = _mm_cvtepi32_ps( emm2 ); - - emm2 = _mm_and_si128( emm2, *(v4si*)_pi32_2 ); - emm2 = _mm_cmpeq_epi32( emm2, _mm_setzero_si128() ); - - v4sf poly_mask = _mm_castsi128_ps( emm2 ); -#else -#endif - /* The magic pass: "Extended precision modular arithmetic" - x = ((x - y * DP1) - y * DP2) - y * DP3; */ - xmm1 = *(v4sf*)_ps_minus_cephes_DP1; - xmm2 = *(v4sf*)_ps_minus_cephes_DP2; - xmm3 = *(v4sf*)_ps_minus_cephes_DP3; - xmm1 = _mm_mul_ps( y, xmm1 ); - xmm2 = _mm_mul_ps( y, xmm2 ); - xmm3 = _mm_mul_ps( y, xmm3 ); - v4sf z = _mm_add_ps( x, xmm1 ); - z = _mm_add_ps( z, xmm2 ); - z = _mm_add_ps( z, xmm3 ); - - v4sf zz = _mm_mul_ps( z, z ); - - y = *(v4sf*)_ps_tancof_p0; - y = _mm_mul_ps( y, zz ); - y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p1 ); - y = _mm_mul_ps( y, zz ); - y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p2 ); - y = _mm_mul_ps( y, zz ); - y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p3 ); - y = _mm_mul_ps( y, zz ); - y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p4 ); - y = _mm_mul_ps( y, zz ); - y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p5 ); - y = _mm_mul_ps( y, zz ); - y = _mm_mul_ps( y, z ); - y = _mm_add_ps( y, z ); - - v4sf y2; - if( cotFlag ) { - y2 = _mm_xor_ps( y, *(v4sf*)_ps_sign_mask ); - /* y = _mm_rcp_ps( y ); */ - /* using _mm_rcp_ps here loses on way too much precision, better to do a div */ - y = _mm_div_ps( *(v4sf*)_ps_1, y ); - } else { - /* y2 = _mm_rcp_ps( y ); */ - /* using _mm_rcp_ps here loses on way too much precision, better to do a div */ - y2 = _mm_div_ps( *(v4sf*)_ps_1, y ); - y2 = _mm_xor_ps( y2, *(v4sf*)_ps_sign_mask ); - } - - /* select the correct result from the two polynoms */ - xmm3 = poly_mask; - y = _mm_and_ps( xmm3, y ); - y2 = _mm_andnot_ps( xmm3, y2 ); - y = _mm_or_ps( y, y2 ); - - /* update the sign */ - y = _mm_xor_ps( y, sign_bit ); - - return y; -} - -v4sf tan_ps( v4sf x ) { return tancot_ps( x, 0 ); } - -v4sf cot_ps( v4sf x ) { return tancot_ps( x, 1 ); } - -_PS_CONST( atanrange_hi, 2.414213562373095 ); -_PS_CONST( atanrange_lo, 0.4142135623730950 ); -const float PIF = 3.141592653589793238; -const float PIO2F = 1.5707963267948966192; -_PS_CONST( cephes_PIF, 3.141592653589793238 ); -_PS_CONST( cephes_PIO2F, 1.5707963267948966192 ); -_PS_CONST( cephes_PIO4F, 0.7853981633974483096 ); - -_PS_CONST( atancof_p0, 8.05374449538e-2 ); -_PS_CONST( atancof_p1, 1.38776856032E-1 ); -_PS_CONST( atancof_p2, 1.99777106478E-1 ); -_PS_CONST( atancof_p3, 3.33329491539E-1 ); - -v4sf atan_ps( v4sf x ) -{ - v4sf sign_bit, y; - - sign_bit = x; - /* take the absolute value */ - x = _mm_and_ps( x, *(v4sf*)_ps_inv_sign_mask ); - /* extract the sign bit (upper one) */ - sign_bit = _mm_and_ps( sign_bit, *(v4sf*)_ps_sign_mask ); - - /* range reduction, init x and y depending on range */ -#ifdef USE_SSE2 - /* x > 2.414213562373095 */ - v4sf cmp0 = _mm_cmpgt_ps( x, *(v4sf*)_ps_atanrange_hi ); - /* x > 0.4142135623730950 */ - v4sf cmp1 = _mm_cmpgt_ps( x, *(v4sf*)_ps_atanrange_lo ); - - /* x > 0.4142135623730950 && !( x > 2.414213562373095 ) */ - v4sf cmp2 = _mm_andnot_ps( cmp0, cmp1 ); - - /* -( 1.0/x ) */ - v4sf y0 = _mm_and_ps( cmp0, *(v4sf*)_ps_cephes_PIO2F ); - v4sf x0 = _mm_div_ps( *(v4sf*)_ps_1, x ); - x0 = _mm_xor_ps( x0, *(v4sf*)_ps_sign_mask ); - - v4sf y1 = _mm_and_ps( cmp2, *(v4sf*)_ps_cephes_PIO4F ); - /* (x-1.0)/(x+1.0) */ - v4sf x1_o = _mm_sub_ps( x, *(v4sf*)_ps_1 ); - v4sf x1_u = _mm_add_ps( x, *(v4sf*)_ps_1 ); - v4sf x1 = _mm_div_ps( x1_o, x1_u ); - - v4sf x2 = _mm_and_ps( cmp2, x1 ); - x0 = _mm_and_ps( cmp0, x0 ); - x2 = _mm_or_ps( x2, x0 ); - cmp1 = _mm_or_ps( cmp0, cmp2 ); - x2 = _mm_and_ps( cmp1, x2 ); - x = _mm_andnot_ps( cmp1, x ); - x = _mm_or_ps( x2, x ); - - y = _mm_or_ps( y0, y1 ); -#else -#error sse1 & mmx version not implemented -#endif - - v4sf zz = _mm_mul_ps( x, x ); - v4sf acc = *(v4sf*)_ps_atancof_p0; - acc = _mm_mul_ps( acc, zz ); - acc = _mm_sub_ps( acc, *(v4sf*)_ps_atancof_p1 ); - acc = _mm_mul_ps( acc, zz ); - acc = _mm_add_ps( acc, *(v4sf*)_ps_atancof_p2 ); - acc = _mm_mul_ps( acc, zz ); - acc = _mm_sub_ps( acc, *(v4sf*)_ps_atancof_p3 ); - acc = _mm_mul_ps( acc, zz ); - acc = _mm_mul_ps( acc, x ); - acc = _mm_add_ps( acc, x ); - y = _mm_add_ps( y, acc ); - - /* update the sign */ - y = _mm_xor_ps( y, sign_bit ); - - return y; -} - -v4sf atan2_ps( v4sf y, v4sf x ) -{ - v4sf x_eq_0 = _mm_cmpeq_ps( x, *(v4sf*)_ps_0 ); - v4sf x_gt_0 = _mm_cmpgt_ps( x, *(v4sf*)_ps_0 ); - v4sf x_le_0 = _mm_cmple_ps( x, *(v4sf*)_ps_0 ); - v4sf y_eq_0 = _mm_cmpeq_ps( y, *(v4sf*)_ps_0 ); - v4sf x_lt_0 = _mm_cmplt_ps( x, *(v4sf*)_ps_0 ); - v4sf y_lt_0 = _mm_cmplt_ps( y, *(v4sf*)_ps_0 ); - - v4sf zero_mask = _mm_and_ps( x_eq_0, y_eq_0 ); - v4sf zero_mask_other_case = _mm_and_ps( y_eq_0, x_gt_0 ); - zero_mask = _mm_or_ps( zero_mask, zero_mask_other_case ); - - v4sf pio2_mask = _mm_andnot_ps( y_eq_0, x_eq_0 ); - v4sf pio2_mask_sign = _mm_and_ps( y_lt_0, *(v4sf*)_ps_sign_mask ); - v4sf pio2_result = *(v4sf*)_ps_cephes_PIO2F; - pio2_result = _mm_xor_ps( pio2_result, pio2_mask_sign ); - pio2_result = _mm_and_ps( pio2_mask, pio2_result ); - - v4sf pi_mask = _mm_and_ps( y_eq_0, x_le_0 ); - v4sf pi = *(v4sf*)_ps_cephes_PIF; - v4sf pi_result = _mm_and_ps( pi_mask, pi ); - - v4sf swap_sign_mask_offset = _mm_and_ps( x_lt_0, y_lt_0 ); - swap_sign_mask_offset = _mm_and_ps( swap_sign_mask_offset, *(v4sf*)_ps_sign_mask ); - - v4sf offset0 = _mm_setzero_ps(); - v4sf offset1 = *(v4sf*)_ps_cephes_PIF; - offset1 = _mm_xor_ps( offset1, swap_sign_mask_offset ); - - v4sf offset = _mm_andnot_ps( x_lt_0, offset0 ); - offset = _mm_and_ps( x_lt_0, offset1 ); - - v4sf arg = _mm_div_ps( y, x ); - v4sf atan_result = atan_ps( arg ); - atan_result = _mm_add_ps( atan_result, offset ); - - /* select between zero_result, pio2_result and atan_result */ - - v4sf result = _mm_andnot_ps( zero_mask, pio2_result ); - atan_result = _mm_andnot_ps( pio2_mask, atan_result ); - atan_result = _mm_andnot_ps( pio2_mask, atan_result); - result = _mm_or_ps( result, atan_result ); - result = _mm_or_ps( result, pi_result ); - - return result; -} - -/* for convenience of calling simd sqrt */ -float sqrt_ps( float x ) -{ - v4sf sse_value = _mm_set_ps1( x ); - sse_value = _mm_sqrt_ps( sse_value ); - return _mm_cvtss_f32( sse_value ); -} -float rsqrt_ps( float x ) -{ - v4sf sse_value = _mm_set_ps1( x ); - sse_value = _mm_rsqrt_ps( sse_value ); - return _mm_cvtss_f32( sse_value ); -} - -/* atan2 implementation using atan, used as a reference to implement atan2_ps */ -float atan2_ref( float y, float x ) -{ - if( x == 0.0f ) { - if( y == 0.0f ) { - return 0.0f; - } - float result = _ps_cephes_PIO2F[0]; - if( y < 0.0f ) { - result = -result; - } - return result; - } - - if( y == 0.0f ) { - if( x > 0.0f ) { - return 0.0f; - } - return PIF; - } - - float offset = 0; - if( x < 0.0f ) { - offset = PIF; - if( y < 0.0f ) { - offset = -offset; - } - } - - v4sf val = _mm_set_ps1( y / x ); - val = atan_ps( val ); - return offset + _mm_cvtss_f32( val ); -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index 3f444fa..1b59488 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -2424,12 +2424,37 @@ CreateAllocator_(allocator_allocate* Alloc, allocator_free* Free) } #define CreateAllocator(a, f) CreateAllocator_((allocator_allocate*)(a), (allocator_free*)(f)) +internal void +AllocatorDebug_PushAlloc(gs_allocator_debug* Debug, u64 Size, char* Location) +{ + // NOTE(pjs): I don't want this debug procedure to be the reason the + // application crashes. + if (Debug->AllocationsCount < Debug->AllocationsCountMax) + { + gs_debug_allocation Allocation = {}; + + gs_const_string L = ConstString(Location); + + s64 LastSlash = FindLastFromSet(L, "\\/"); + if (LastSlash < 0) LastSlash = 0; + Allocation.Location = GetStringAfter(L, LastSlash); + Allocation.Size = Size; + + Debug->Allocations[Debug->AllocationsCount++] = Allocation; + } + Debug->TotalAllocSize += Size; +} + internal gs_data AllocatorAlloc_(gs_allocator Allocator, u64 Size, char* Location) { // TODO(Peter): Memory Profiling with Location u64 SizeResult = 0; void* Memory = Allocator.Alloc(Size, &SizeResult); + if (Allocator.Debug) + { + AllocatorDebug_PushAlloc(Allocator.Debug, Size, Location); + } return CreateData((u8*)Memory, SizeResult); } internal void @@ -2439,6 +2464,13 @@ AllocatorFree_(gs_allocator Allocator, void* Base, u64 Size, char* Location) if (Base != 0 && Size != 0) { Allocator.Free(Base, Size); + if (Allocator.Debug) + { + // NOTE(pjs): There's no reason we should be going negative + // ie. Freeing more memory than we allocated + Assert(Allocator.Debug->TotalAllocSize >= Size); + Allocator.Debug->TotalAllocSize -= Size; + } } } @@ -2526,30 +2558,37 @@ FreeCursorListEntry(gs_allocator Allocator, gs_memory_cursor_list* CursorEntry) } internal gs_memory_arena -CreateMemoryArena_(arena_type ArenaType, gs_allocator Allocator, u64 ChunkSize, u64 Alignment, gs_memory_arena* ParentArena) +CreateMemoryArena_(arena_type ArenaType, gs_allocator Allocator, u64 ChunkSize, u64 Alignment, gs_memory_arena* ParentArena, char* Name) { // we only want a parent arena if the type is Arena_SubArena Assert(((ArenaType == Arena_BaseArena) && (ParentArena == 0)) || ((ArenaType == Arena_SubArena) && (ParentArena != 0))); gs_memory_arena Arena = {}; + Arena.ArenaName = Name; Arena.Type = ArenaType; Arena.Allocator = Allocator; Arena.Parent = ParentArena; + +#if MEMORY_CURSOR_STATIC_ARRAY + Arena.CursorsCountMax = 4096; + Arena.Cursors = AllocatorAllocArray(Allocator, gs_memory_cursor_list, Arena.CursorsCountMax); +#endif + Arena.MemoryChunkSize = ChunkSize; Arena.MemoryAlignment = Alignment; return Arena; } internal gs_memory_arena -CreateMemoryArena(gs_allocator Allocator, u64 ChunkSize = KB(32), u64 Alignment = Bytes(8)) +CreateMemoryArena(gs_allocator Allocator, char* Name, u64 ChunkSize = KB(32), u64 Alignment = Bytes(8)) { - return CreateMemoryArena_(Arena_BaseArena, Allocator, ChunkSize, Alignment, 0); + return CreateMemoryArena_(Arena_BaseArena, Allocator, ChunkSize, Alignment, 0, Name); } internal gs_memory_arena -CreateMemorySubArena(gs_memory_arena* Parent, u64 ChunkSize = KB(32), u64 Alignment = Bytes(8)) +CreateMemorySubArena(gs_memory_arena* Parent, char* Name, u64 ChunkSize = KB(32), u64 Alignment = Bytes(8)) { - return CreateMemoryArena_(Arena_SubArena, Parent->Allocator, ChunkSize, Alignment, Parent); + return CreateMemoryArena_(Arena_SubArena, Parent->Allocator, ChunkSize, Alignment, Parent, Name); } internal gs_data PushSize_(gs_memory_arena* Arena, u64 Size, char* Location); @@ -2557,6 +2596,7 @@ internal gs_data PushSize_(gs_memory_arena* Arena, u64 Size, char* Location); internal void FreeCursorList(gs_memory_cursor_list* List, gs_allocator Allocator) { +#if !MEMORY_CURSOR_STATIC_ARRAY gs_memory_cursor_list* CursorAt = List; while (CursorAt != 0) { @@ -2564,13 +2604,18 @@ FreeCursorList(gs_memory_cursor_list* List, gs_allocator Allocator) FreeCursorListEntry(Allocator, CursorAt); CursorAt = Prev; } +#endif } internal gs_memory_cursor_list* MemoryArenaNewCursor(gs_memory_arena* Arena, u64 MinSize, char* Location) { +#if MEMORY_CURSOR_STATIC_ARRAY + u64 AllocSize = Max(MinSize, Arena->MemoryChunkSize); +#else // Allocate enough spcae for the minimum size needed + sizeo for the cursor list u64 AllocSize = Max(MinSize, Arena->MemoryChunkSize) + sizeof(gs_memory_cursor_list); +#endif gs_data Data = {0}; switch (Arena->Type) @@ -2588,6 +2633,11 @@ MemoryArenaNewCursor(gs_memory_arena* Arena, u64 MinSize, char* Location) InvalidDefaultCase; } +#if MEMORY_CURSOR_STATIC_ARRAY + Assert(Arena->CursorsCount < Arena->CursorsCountMax); + gs_memory_cursor_list* Result = Arena->Cursors + Arena->CursorsCount++; + Result->Cursor = CreateMemoryCursor(Data.Memory, Data.Size); +#else // Fit the memory cursor into the region allocated Assert(MinSize + sizeof(gs_memory_cursor_list) <= Data.Size); gs_memory_cursor_list* Result = (gs_memory_cursor_list*)Data.Memory; @@ -2599,9 +2649,14 @@ MemoryArenaNewCursor(gs_memory_arena* Arena, u64 MinSize, char* Location) Result->Next = 0; if (Arena->CursorList != 0) { + if (Arena->CursorList->Next != 0) + { + Result->Next = Arena->CursorList->Next; + } Arena->CursorList->Next = Result; } Arena->CursorList = Result; +#endif return Result; } @@ -2611,6 +2666,27 @@ PushSize_(gs_memory_arena* Arena, u64 Size, char* Location) gs_data Result = {0}; if (Size > 0) { +#if MEMORY_CURSOR_STATIC_ARRAY + gs_memory_cursor_list* CursorEntry = 0; + for (u64 i = 0; + i < Arena->CursorsCount; + i++) + { + gs_memory_cursor_list* At = Arena->Cursors + i; + if (CursorHasRoom(At->Cursor, Size)) + { + CursorEntry = At; + break; + } + } + if (!CursorEntry) + { + CursorEntry = MemoryArenaNewCursor(Arena, Size, Location); + } + Assert(CursorEntry); + Assert(CursorHasRoom(CursorEntry->Cursor, Size)); +#else + gs_memory_cursor_list* CursorEntry = Arena->CursorList; if (CursorEntry == 0) { @@ -2627,6 +2703,7 @@ PushSize_(gs_memory_arena* Arena, u64 Size, char* Location) CursorEntry = MemoryArenaNewCursor(Arena, Size, Location); } } +#endif Assert(CursorEntry != 0); Result = PushSizeOnCursor_(&CursorEntry->Cursor, Size, Location); Assert(Result.Memory != 0); @@ -2651,44 +2728,19 @@ PushSize_(gs_memory_arena* Arena, u64 Size, char* Location) return Result; } -internal void -PopSize(gs_memory_arena* Arena, u64 Size) -{ - gs_allocator Allocator = Arena->Allocator; - gs_memory_cursor_list* CursorEntry = Arena->CursorList; - for (gs_memory_cursor_list* Prev = 0; - CursorEntry != 0 && Size != 0; - CursorEntry = Prev) - { - Prev = CursorEntry->Prev; - if (Size >= CursorEntry->Cursor.Position) - { - Size -= CursorEntry->Cursor.Position; - FreeCursorListEntry(Allocator, CursorEntry); - } - else - { - PopSizeOnCursor(&CursorEntry->Cursor, Size); - break; - } - } - Arena->CursorList = CursorEntry; -} internal void FreeMemoryArena(gs_memory_arena* Arena) { - gs_allocator Allocator = Arena->Allocator; - gs_memory_cursor_list* CursorEntry = Arena->CursorList; - for (gs_memory_cursor_list* Prev = 0; - CursorEntry != 0; - CursorEntry = Prev) +#if MEMORY_CURSOR_STATIC_ARRAY + for (u32 i = 0; i < Arena->CursorsCount; i++) { - Prev = CursorEntry->Prev; - if (CursorEntry != 0) - { - FreeCursorListEntry(Allocator, CursorEntry); - } + gs_memory_cursor_list E = Arena->Cursors[i]; + AllocatorFree(Arena->Allocator, E.Cursor.Data.Memory, E.Cursor.Data.Size); } + AllocatorFreeArray(Arena->Allocator, Arena->Cursors, gs_memory_cursor_list, Arena->CursorsCountMax); +#else + FreeCursorList(Arena->CursorList, Arena->Allocator); +#endif } #define PushSizeToData(arena, size) PushSize_((arena), (size), FileNameAndLineNumberString) @@ -2726,6 +2778,12 @@ PushStringCopy(gs_memory_arena* Arena, gs_const_string String) internal void ClearArena(gs_memory_arena* Arena) { +#if MEMORY_CURSOR_STATIC_ARRAY + for (u32 i = 0; i < Arena->CursorsCount; i++) + { + Arena->Cursors[i].Cursor.Position = 0; + } +#else gs_memory_cursor_list* First = 0; for (gs_memory_cursor_list* CursorEntry = Arena->CursorList; CursorEntry != 0; @@ -2735,12 +2793,13 @@ ClearArena(gs_memory_arena* Arena) CursorEntry->Cursor.Position = 0; } Arena->CursorList = First; +#endif } internal void FreeArena(gs_memory_arena* Arena) { - FreeCursorList(Arena->CursorList, Arena->Allocator); + FreeMemoryArena(Arena); } /////////////////////////// @@ -2789,14 +2848,14 @@ CreateDynarrayWithStorage(gs_memory_arena Storage, u32 ElementSize, u32 Elements internal gs_dynarray CreateDynarray_(gs_allocator Allocator, u32 ElementSize, u32 ElementsPerBuffer) { - gs_memory_arena Storage = CreateMemoryArena(Allocator, ElementSize * ElementsPerBuffer); + gs_memory_arena Storage = CreateMemoryArena(Allocator, "Dynarray Arena", ElementSize * ElementsPerBuffer); return CreateDynarrayWithStorage(Storage, ElementSize, ElementsPerBuffer); }; internal gs_dynarray CreateDynarray_(gs_memory_arena* Arena, u32 ElementSize, u32 ElementsPerBuffer) { - gs_memory_arena Storage = CreateMemorySubArena(Arena, ElementSize * ElementsPerBuffer); + gs_memory_arena Storage = CreateMemorySubArena(Arena, "Dynarray Sub Arena", ElementSize * ElementsPerBuffer); return CreateDynarrayWithStorage(Storage, ElementSize, ElementsPerBuffer); }; diff --git a/src/gs_libs/gs_types.h b/src/gs_libs/gs_types.h index e6ab857..792f2de 100644 --- a/src/gs_libs/gs_types.h +++ b/src/gs_libs/gs_types.h @@ -247,7 +247,7 @@ enum { \ #define DontCompile ImAfraidICantDoThat #define LineNumberString Stringify(__LINE__) -#define FileNameAndLineNumberString_ __FILE__ ":" LineNumberString ":" +#define FileNameAndLineNumberString_ __FILE__ ":" LineNumberString ":" __FUNCTION__ #define FileNameAndLineNumberString (char*)FileNameAndLineNumberString_ // @@ -633,10 +633,27 @@ typedef ALLOCATOR_ALLOC(allocator_allocate); #define ALLOCATOR_FREE(name) void name(void* Ptr, u64 Size) typedef ALLOCATOR_FREE(allocator_free); +struct gs_debug_allocation +{ + gs_const_string Location; + u64 Size; +}; + +struct gs_allocator_debug +{ + u64 TotalAllocSize; + + u64 AllocationsCount; + u64 AllocationsCountMax; + gs_debug_allocation* Allocations; +}; + struct gs_allocator { allocator_allocate* Alloc; allocator_free* Free; + + gs_allocator_debug* Debug; }; struct gs_memory_cursor @@ -645,11 +662,26 @@ struct gs_memory_cursor u64 Position; }; +/* TODO(pjs): Setting MEMORY_CURSOR_STATIC_ARRAY will still compile, + However, it introduces a bug that I haven't fully diagnosed. +The problem seems to occur when trying to push to a cleared memory arena +Where the FirstCursor doesn't have enough room for the allocation, but +also FirstCursor->Next points to a valid cursor. The new cursor is put +in the middle however we seem to continually keep allocating new +cursors forever and losing old ones. +The problem in Lumenarium is found in the OutputData structure + +Leaving this in a simplified state for now +*/ +#define MEMORY_CURSOR_STATIC_ARRAY 1 + struct gs_memory_cursor_list { gs_memory_cursor Cursor; +#if !MEMORY_CURSOR_STATIC_ARRAY gs_memory_cursor_list* Next; gs_memory_cursor_list* Prev; +#endif }; enum arena_type @@ -664,9 +696,18 @@ struct gs_memory_arena gs_allocator Allocator; gs_memory_arena* Parent; +#if MEMORY_CURSOR_STATIC_ARRAY + gs_memory_cursor_list* Cursors; + u64 CursorsCount; + u64 CursorsCountMax; +#else gs_memory_cursor_list* CursorList; +#endif + u64 MemoryChunkSize; u64 MemoryAlignment; + + char* ArenaName; }; struct gs_memory_arena_array diff --git a/src/app/interface_test.cpp b/src/tests/interface_test.cpp similarity index 100% rename from src/app/interface_test.cpp rename to src/tests/interface_test.cpp diff --git a/src/tests/sanity_tests.cpp b/src/tests/sanity_tests.cpp index 3d37c1a..437fc4d 100644 --- a/src/tests/sanity_tests.cpp +++ b/src/tests/sanity_tests.cpp @@ -31,7 +31,7 @@ bool PathTest (char* In, char* Out) { int main (int ArgCount, char** Args) { - Scratch = CreateMemoryArena(CreateAllocator(Alloc, Free)); + Scratch = CreateMemoryArena(CreateAllocator(Alloc, Free), "Scratch"); Test("gs_string") { diff --git a/src/app/test_patterns.h b/src/tests/test_patterns.h similarity index 100% rename from src/app/test_patterns.h rename to src/tests/test_patterns.h From 9fc984d6f2af76e103503185f8479a008211c93b Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 20 Mar 2021 15:15:35 -0700 Subject: [PATCH 012/151] Message sending to weatherman --- src/app/foldhaus_app.cpp | 2 + src/app/foldhaus_platform.h | 17 + src/app/patterns/blumen_patterns.h | 336 +++++++++++++++++- src/app/platform_win32/win32_foldhaus.cpp | 41 +++ .../platform_win32/win32_foldhaus_serial.h | 4 + src/app/ss_blumen_lumen/blumen_lumen.cpp | 235 ++++++++---- src/app/ss_blumen_lumen/blumen_lumen.h | 60 +++- src/sculpture_gen/gen_blumen_lumen.cpp | 3 + 8 files changed, 615 insertions(+), 83 deletions(-) diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index e91070d..5016e27 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -133,12 +133,14 @@ UPDATE_AND_RENDER(UpdateAndRender) Editor_Render(State, Context, RenderBuffer); } +#if SEND_DATA // NOTE(pjs): Building data buffers to be sent out to the sculpture // This array is used on the platform side to actually send the information assembly_array SACNAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaSACN, State->Transient); assembly_array UARTAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaUART, State->Transient); SACN_BuildOutputData(&State->SACN, OutputData, SACNAssemblies, &State->LedSystem); UART_BuildOutputData(OutputData, UARTAssemblies, &State->LedSystem, State->Transient); +#endif } CLEANUP_APPLICATION(CleanupApplication) diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 35a539d..efe8ff8 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -189,6 +189,20 @@ GetSecondsElapsed (s64 Start, s64 End, s64 PerformanceCountFrequency) return Result; } +typedef struct system_time +{ + u64 NanosSinceEpoch; + + s32 Year; + s32 Month; + s32 Day; + s32 Hour; // [0:23] + s32 Minute; + s32 Second; +} system_time; + +#define STATUS_PACKET_FREQ_SECONDS 5 + struct context { gs_thread_context ThreadContext; @@ -218,6 +232,9 @@ struct context platform_draw_font_codepoint* PlatformDrawFontCodepoint; platform_get_socket_handle* PlatformGetSocketHandle; + + system_time SystemTime_Last; + system_time SystemTime_Current; }; #define FOLDHAUS_PLATFORM_H diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 4345524..0b0d2d2 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -7,6 +7,242 @@ #define FLOWER_COLORS_COUNT 12 +internal r32 +Smoothstep(r32 T) +{ + r32 Result = (T * T * (3 - (2 * T))); + return Result; +} +internal r32 +Smoothstep(r32 T, r32 A, r32 B) +{ + return LerpR32(Smoothstep(T), A, B); +} + +internal v2 +FloorV2(v2 P) +{ + v2 Result = {}; + Result.x = FloorR32(P.x); + Result.y = FloorR32(P.y); + return Result; +} +internal v3 +FloorV3(v3 P) +{ + v3 Result = {}; + Result.x = FloorR32(P.x); + Result.y = FloorR32(P.y); + Result.z = FloorR32(P.z); + return Result; +} + +internal v2 +FractV2(v2 P) +{ + v2 Result = {}; + Result.x = FractR32(P.x); + Result.y = FractR32(P.y); + return Result; +} +internal v3 +FractV3(v3 P) +{ + v3 Result = {}; + Result.x = FractR32(P.x); + Result.y = FractR32(P.y); + Result.z = FractR32(P.z); + return Result; +} + +internal v2 +SinV2(v2 P) +{ + v2 Result = {}; + Result.x = SinR32(P.x); + Result.y = SinR32(P.y); + return Result; +} +internal v3 +SinV3(v3 P) +{ + v3 Result = {}; + Result.x = SinR32(P.x); + Result.y = SinR32(P.y); + Result.y = SinR32(P.z); + return Result; +} + +internal r32 +Hash1(v2 P) +{ + v2 Result = FractV2( P * 0.3183099f ) * 50.f; + return FractR32(P.x * P.y * (P.x + P.y)); +} + +internal r32 +Hash1(r32 N) +{ + return FractR32(N * 17.0f * FractR32(N * 0.3183099f)); +} + +internal v2 +Hash2(r32 N) +{ + v2 P = V2MultiplyPairwise(SinV2(v2{N,N+1.0f}), v2{43758.5453123f,22578.1459123f}); + return FractV2(P); +} + +internal v2 +Hash2(v2 P) +{ + v2 K = v2{ 0.3183099f, 0.3678794f }; + v2 Kp = v2{K.y, K.x}; + v2 R = V2MultiplyPairwise(P, K) + Kp; + return FractV2( K * 16.0f * FractR32( P.x * P.y * (P.x + P.y))); +} + +internal v3 +Hash3(v2 P) +{ + v3 Q = v3{}; + Q.x = V2Dot(P, v2{127.1f, 311.7f}); + Q.y = V2Dot(P, v2{267.5f, 183.3f}); + Q.z = V2Dot(P, v2{419.2f, 371.9f}); + return FractV3(SinV3(Q) * 43758.5453f); +} + +internal r32 +Random(v2 N) +{ + v2 V = v2{12.9898f, 4.1414f}; + return FractR32(SinR32(V2Dot(N, V)) * 43758.5453); +} + +internal r32 +Noise2D(v2 P) +{ + v2 IP = FloorV2(P); + v2 U = FractV2(P); + U = V2MultiplyPairwise(U, U); + U = V2MultiplyPairwise(U, ((U * 2.0f) + v2{-3, -3})); + + r32 A = LerpR32(U.x, Random(IP), Random(IP + v2{1.0f, 0})); + r32 B = LerpR32(U.x, Random(IP + v2{0, 1}), Random(IP + v2{1, 1})); + r32 Res = LerpR32(U.y, A, B); + + return Res * Res; +} + +internal r32 +Noise3D(v3 Pp) +{ + v3 P = FloorV3(Pp); + v3 W = FractV3(Pp); + + //v3 U = W * W * W * (W * (W * 6.0f - 15.0f) + 10.0f); + v3 U = V3MultiplyPairwise(W, W * 6.0f - v3{15, 15, 15}); + U = U + v3{10, 10, 10}; + U = V3MultiplyPairwise(U, W); + U = V3MultiplyPairwise(U, W); + U = V3MultiplyPairwise(U, W); + + r32 N = P.x + 317.0f * P.y + 157.0f * P.z; + + r32 A = Hash1(N + 0.0f); + r32 B = Hash1(N + 1.0f); + r32 C = Hash1(N + 317.0f); + r32 D = Hash1(N + 317.0f); + r32 E = Hash1(N + 157.0f); + r32 F = Hash1(N + 158.0f); + r32 G = Hash1(N + 474.0f); + r32 H = Hash1(N + 475.0f); + + r32 K0 = A; + r32 K1 = B - A; + r32 K2 = C - A; + r32 K3 = E - A; + r32 K4 = A - B - C + D; + r32 K5 = A - C - E + G; + r32 K6 = A - B - E + F; + r32 K7 = A + B + C - D + E - F - G + H; + + return -1.0f + 2.0f * (K0 + + K1 * U.x + + K2 * U.y + + K3 * U.z + + K4 * U.x * U.y + + K5 * U.y + U.z + + K6 * U.z * U.x + + K7 * U.x * U.y * U.z); +} + +internal r32 +Fbm2D(v2 P) +{ + r32 R = 0; + r32 Amp = 1.0; + r32 Freq = 1.0; + for (u32 i = 0; i < 3; i++) + { + R += Amp * Noise2D(P * Freq); + Amp *= 0.5f; + Freq *= 1.0f / 0.5f; + } + return R; +} + +global m44 M3 = m44{ + 0.00f, 0.80f, 0.60f, 0, + -0.80f, 0.36f, -0.48f, 0, + -0.60f, -0.48f, 0.64f, 0, + 0, 0, 0, 1 +}; + +internal r32 +Fbm3D(v3 P) +{ + v3 X = P; + r32 F = 2.0f; + r32 S = 0.5f; + r32 A = 0.0f; + r32 B = 0.5f; + for (u32 i = 0; i < 4; i++) + { + r32 N = Noise3D(X); + A += B * N; + B *= S; + v4 Xp = M3 * ToV4Point(X); + X = Xp.xyz * F; + } + + return A; +} + +internal r32 +Voronoise(v2 P, r32 U, r32 V) +{ + r32 K = 1.0f + 63.0f + PowR32(1.0f - V, 6.0f); + + v2 I = FloorV2(P); + v2 F = FractV2(P); + + v2 A = v2{0, 0}; + for (s32 y = -2; y <= 2; y++) + { + for (s32 x = -2; x <= 2; x++) + { + v2 G = v2{(r32)x, (r32)y}; + v3 O = V3MultiplyPairwise(Hash3(I + G), v3{U, U, 1.0f}); + v2 D = G - F + O.xy; + r32 W = PowR32(1.0f - Smoothstep(V2Mag(D), 0.0f, 1.414f), K); + A += v2{O.z * W, W}; + } + } + + return A.x / A.y; +} + pixel FlowerAColors[FLOWER_COLORS_COUNT] = { { 232,219,88 }, { 232,219,88 }, @@ -507,13 +743,6 @@ Pattern_LighthouseRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memo } } -internal r32 -Smoothstep(r32 T) -{ - r32 Result = (T * T * (3 - (2 * T))); - return Result; -} - internal void Pattern_SmoothGrowRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { @@ -726,5 +955,98 @@ Pattern_BasicFlowers(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_ar } } } + +internal void +Pattern_Wavy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + +} + +internal void +Pattern_Patchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + { + v4 P = Leds->Positions[LedIndex]; + + v3 Pp = P.xyz; + + r32 Noise = Fbm3D((Pp / 1000) + (v3{Time, -Time, SinR32(Time)} * 0.1f)); + Noise = RemapR32(Noise, -1, 1, 0, 1); + Noise = Smoothstep(Noise, 0, 1); + u8 NV = (u8)(Noise * 255); + + v3 BSeed = v3{P.z, P.x, P.y}; + r32 BNoise = 1.0f; //Fbm3D(BSeed / 50); + + pixel C = GetColor(&FlowerAColors[0], FLOWER_COLORS_COUNT, Noise); + C.R = (u8)((r32)C.R * BNoise); + C.G = (u8)((r32)C.G * BNoise); + C.B = (u8)((r32)C.B * BNoise); + + Leds->Colors[LedIndex] = C; + //Leds->Colors[LedIndex] = pixel{NV, NV, NV}; + } +} + +internal void +Pattern_Leafy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + pixel* Colors = &FlowerBColors[0]; + + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + { + v4 P = Leds->Positions[LedIndex]; + r32 RefPos = P.y + Noise2D(v2{P.x, P.z} * 50); + + r32 B = 0; + + pixel C = {}; + + r32 BandWidth = 5; + r32 TransitionPeriod = 5.0f; + u32 BandCount = 10; + for (u32 Band = 0; Band < BandCount; Band++) + { + r32 BandSeed = RemapR32(Hash1((r32)Band), -1, 1, 0, 1); + r32 BandDelay = BandSeed * TransitionPeriod; + r32 BandTransitionDuration = RemapR32(Hash1((r32)Band * 3.413f), -1, 1, 0, 1) * TransitionPeriod; + r32 BandPercent = Smoothstep(ModR32(Time + BandDelay, BandTransitionDuration) / TransitionPeriod, 0, 1); + r32 BandHeight = 150 - BandPercent * 250; + + r32 BandDist = Abs(RefPos - BandHeight); + + //B += Max(0, BandWidth - BandDist); + B = Max(0, BandWidth - BandDist); + + + { + //pixel BandColor = GetColor(Colors, FLOWER_COLORS_COUNT, BandSeed); + pixel BandColor = Colors[Band % FLOWER_COLORS_COUNT]; + C.R = C.R + (BandColor.R * B); + C.G = C.G + (BandColor.G * B); + C.B = C.B + (BandColor.B * B); + } + + } + + u8 V = (u8)(B * 255); + //pixel C = { V, V, V }; + Leds->Colors[LedIndex] = C; + } +} + +internal void +Pattern_LeafyPatchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + +} + +internal void +Pattern_WavyPatchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + +} + #define BLUMEN_PATTERNS_H #endif // BLUMEN_PATTERNS_H \ No newline at end of file diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 765af09..c34f3f4 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -620,6 +620,47 @@ WinMain ( } DEBUG_TRACK_SCOPE(MainLoop); + { + // update system time + SYSTEMTIME WinLocalTime; + GetLocalTime(&WinLocalTime); + + SYSTEMTIME WinSysTime; + FILETIME WinSysFileTime; + GetSystemTime(&WinSysTime); + if (!SystemTimeToFileTime((const SYSTEMTIME*)&WinSysTime, &WinSysFileTime)) + { + u32 Error = GetLastError(); + InvalidCodePath; + } + ULARGE_INTEGER SysTime = {}; + SysTime.LowPart = WinSysFileTime.dwLowDateTime; + SysTime.HighPart = WinSysFileTime.dwHighDateTime; + + Context.SystemTime_Last = Context.SystemTime_Current; + + Context.SystemTime_Current.NanosSinceEpoch = SysTime.QuadPart; + Context.SystemTime_Current.Year = WinLocalTime.wYear; + Context.SystemTime_Current.Month = WinLocalTime.wMonth; + Context.SystemTime_Current.Day = WinLocalTime.wDay; + Context.SystemTime_Current.Hour = WinLocalTime.wHour; + Context.SystemTime_Current.Minute = WinLocalTime.wMinute; + Context.SystemTime_Current.Second = WinLocalTime.wSecond; + +#define PRINT_SYSTEM_TIME 0 +#if PRINT_SYSTEM_TIME + gs_string T = PushStringF(Context.ThreadContext.Transient, + 256, + "%d %d %d - %lld\n", + Context.SystemTime_Current.Hour, + Context.SystemTime_Current.Minute, + Context.SystemTime_Current.Second, + Context.SystemTime_Current.NanosSinceEpoch); + NullTerminate(&T); + OutputDebugStringA(T.Str); +#endif + } + ResetInputQueue(&InputQueue); ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false); diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index c3dcfb1..820da09 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -151,6 +151,10 @@ Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer) // ?? }break; + case ERROR_NO_SUCH_DEVICE: + { + }break; + case ERROR_INVALID_HANDLE: InvalidDefaultCase; } diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 698451f..370341d 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -5,6 +5,67 @@ // #ifndef BLUMEN_LUMEN_CPP +internal bool +MessageQueue_CanRead(blumen_network_msg_queue* Queue) +{ + bool Result = (Queue->ReadHead != Queue->WriteHead); + return Result; +} + +internal gs_data +MessageQueue_Read(blumen_network_msg_queue* Queue) +{ + gs_data Result = {}; + u32 ReadIndex = Queue->ReadHead++; + if (Queue->ReadHead >= BLUMEN_MESSAGE_QUEUE_COUNT) + { + Queue->ReadHead = 0; + } + Result = Queue->Buffers[ReadIndex]; + return Result; +} + +// KB(1) is just bigger than any packet we send. Good for now +#define DEFAULT_QUEUE_ENTRY_SIZE KB(1) + +internal void +MessageQueue_Init(blumen_network_msg_queue* Queue, gs_memory_arena* Arena) +{ + for (u32 i = 0; i < BLUMEN_MESSAGE_QUEUE_COUNT; i++) + { + Queue->Buffers[i] = PushSizeToData(Arena, DEFAULT_QUEUE_ENTRY_SIZE); + } +} + +internal gs_data* +MessageQueue_GetWrite(blumen_network_msg_queue* Queue) +{ + u32 Index = Queue->WriteHead++; + gs_data* Result = &Queue->Buffers[Index]; + Assert(Result->Size > 0); + + if (Queue->WriteHead >= PACKETS_MAX) + { + Queue->WriteHead = 0; + } + return Result; +} + +internal bool +MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) +{ + gs_data* Dest = MessageQueue_GetWrite(Queue); + Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE); + CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size); +} + +internal bool +MessageQueue_CanWrite(blumen_network_msg_queue Queue) +{ + bool Result = ((Queue.WriteHead >= Queue.ReadHead) || + (Queue.WriteHead < Queue.ReadHead)); + return Result; +} internal void BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) @@ -32,23 +93,13 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) Msg = SocketRecieve(Data->SocketManager, Data->ListenSocket, Ctx->Transient); if (Msg.Size > 0) { - Data->MicPacketBuffer->Values[Data->MicPacketBuffer->WriteHead++] = Msg; - if (Data->MicPacketBuffer->WriteHead >= PACKETS_MAX) - { - Data->MicPacketBuffer->WriteHead = 0; - } + MessageQueue_Write(Data->IncomingMsgQueue, Msg); } } - while (Data->OutgoingMsgQueue->ReadHead != Data->OutgoingMsgQueue->WriteHead) + while (MessageQueue_CanRead(Data->OutgoingMsgQueue)) { - u32 ReadIndex = Data->OutgoingMsgQueue->ReadHead++; - if (Data->OutgoingMsgQueue->ReadHead >= BLUMEN_MESSAGE_QUEUE_COUNT) - { - Data->OutgoingMsgQueue->ReadHead = 0; - } - - Msg = Data->OutgoingMsgQueue->Buffers[ReadIndex]; + Msg = MessageQueue_Read(Data->OutgoingMsgQueue); u32 Address = WeathermanIPV4; u32 Port = WeathermanPort; s32 Flags = 0; @@ -88,6 +139,9 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_FlowerColors); Patterns_PushPattern(Patterns, Pattern_FlowerColorToWhite); Patterns_PushPattern(Patterns, Pattern_BasicFlowers); + // 15 + Patterns_PushPattern(Patterns, Pattern_Patchy); + Patterns_PushPattern(Patterns, Pattern_Leafy); } internal pixel @@ -116,10 +170,13 @@ BlumenLumen_CustomInit(app_state* State, context Context) blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory; BLState->Running = true; + BLState->BrightnessPercent = 1; + MessageQueue_Init(&BLState->IncomingMsgQueue, &State->Permanent); + MessageQueue_Init(&BLState->OutgoingMsgQueue, &State->Permanent); BLState->MicListenJobData.Running = &BLState->Running; BLState->MicListenJobData.SocketManager = Context.SocketManager; - BLState->MicListenJobData.MicPacketBuffer = &BLState->MicPacketBuffer; + BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue; BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; BLState->MicListenJobData.ListenSocket = CreateSocket(Context.SocketManager, "127.0.0.1", "20185"); @@ -163,8 +220,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) Anim2.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim2, 0, 100, Patterns_IndexToHandle(5), 0); - Animation_AddBlock(&Anim2, 50, Anim0.PlayableRange.Max, Patterns_IndexToHandle(10), 0); + Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(17), 0); BLState->AnimHandles[2] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); @@ -204,26 +260,26 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) gs_string GreenString = MakeString("green"); gs_string ILoveYouString = MakeString("i_love_you"); - while (BLState->MicPacketBuffer.ReadHead != BLState->MicPacketBuffer.WriteHead) + while (MessageQueue_CanRead(&BLState->IncomingMsgQueue)) { - gs_data PacketData = BLState->MicPacketBuffer.Values[BLState->MicPacketBuffer.ReadHead++]; + gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); - u8 PacketType = PacketData.Memory[0]; - switch (PacketType) { + blumen_packet Packet = *(blumen_packet*)PacketData.Memory; + switch (Packet.Type) { case PacketType_PatternCommand: { - microphone_packet Packet = *(microphone_packet*)(PacketData.Memory + 1); + microphone_packet Mic = Packet.MicPacket; - u32 NameLen = CStringLength(Packet.AnimationFileName); - if (StringEqualsCharArray(BlueString.ConstString, Packet.AnimationFileName, NameLen)) + u32 NameLen = CStringLength(Mic.AnimationFileName); + if (StringEqualsCharArray(BlueString.ConstString, Mic.AnimationFileName, NameLen)) { State->AnimationSystem.ActiveFadeGroup.From.Index = 0; } - else if (StringEqualsCharArray(GreenString.ConstString, Packet.AnimationFileName, NameLen)) + else if (StringEqualsCharArray(GreenString.ConstString, Mic.AnimationFileName, NameLen)) { State->AnimationSystem.ActiveFadeGroup.From.Index = 1; } - else if (StringEqualsCharArray(ILoveYouString.ConstString, Packet.AnimationFileName, NameLen)) + else if (StringEqualsCharArray(ILoveYouString.ConstString, Mic.AnimationFileName, NameLen)) { State->AnimationSystem.ActiveFadeGroup.From.Index = 2; } @@ -233,13 +289,13 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) case PacketType_MotorState: { - motor_packet Packet = *(motor_packet*)(PacketData.Memory + 1); - BLState->LastKnownMotorState = Packet; + motor_packet Motor = Packet.MotorPacket; + BLState->LastKnownMotorState = Motor; gs_string Temp = PushStringF(State->Transient, 256, "Received Motor States: %d %d %d\n", - Packet.FlowerPositions[0], - Packet.FlowerPositions[1], - Packet.FlowerPositions[2]); + Motor.FlowerPositions[0], + Motor.FlowerPositions[1], + Motor.FlowerPositions[2]); NullTerminate(&Temp); OutputDebugStringA(Temp.Str); @@ -247,66 +303,97 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) case PacketType_Temperature: { - temp_packet Packet = *(temp_packet*)(PacketData.Memory + 1); + temp_packet Temp = Packet.TempPacket; - gs_string Temp = PushStringF(State->Transient, 256, "Temperature: %d\n", - Packet.Temperature); - NullTerminate(&Temp); + if (Temp.Temperature > 21) + { + BLState->BrightnessPercent = .5f; + } + else + { + BLState->BrightnessPercent = 1.f; + } - OutputDebugStringA(Temp.Str); + gs_string TempStr = PushStringF(State->Transient, 256, "Temperature: %d\n", + Temp.Temperature); + NullTerminate(&TempStr); + OutputDebugStringA(TempStr.Str); }break; InvalidDefaultCase; } - - - if (BLState->MicPacketBuffer.ReadHead >= PACKETS_MAX) + } + + + // Open / Close the Motor + + if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue)) + { + for (u32 i = 0; i < MotorOpenTimesCount; i++) { - BLState->MicPacketBuffer.ReadHead = 0; + time_range Range = MotorOpenTimes[i]; + + bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); + bool LastTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Last, Range); + + if (CurrTimeInRange && !LastTimeInRange) + { + OutputDebugString("Open\n"); + gs_data* Msg = MessageQueue_GetWrite(&BLState->OutgoingMsgQueue); + + blumen_packet* Packet = (blumen_packet*)Msg->Memory; + Packet->Type = PacketType_MotorState; + Packet->MotorPacket.FlowerPositions[0] = 2; + Packet->MotorPacket.FlowerPositions[1] = 2; + Packet->MotorPacket.FlowerPositions[2] = 2; + } + else if (!CurrTimeInRange && LastTimeInRange) + { + OutputDebugString("Close\n"); + gs_data* Msg = MessageQueue_GetWrite(&BLState->OutgoingMsgQueue); + + blumen_packet* Packet = (blumen_packet*)Msg->Memory; + Packet->Type = PacketType_MotorState; + Packet->MotorPacket.FlowerPositions[0] = 1; + Packet->MotorPacket.FlowerPositions[1] = 1; + Packet->MotorPacket.FlowerPositions[2] = 1; + } } } - if (false && MotorTimeElapsed > 0) + // Dim the leds based on temp data + for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) { - // NOTE(pjs): - MotorTimeElapsed = 0; - u8 Position = LastPosition; - if (LastPosition == 2) + led_buffer Buffer = State->LedSystem.Buffers[i]; + for (u32 j = 0; j < Buffer.LedCount; j++) { - LastPosition = 1; + pixel* Color = Buffer.Colors + j; + Color->R = Color->R * BLState->BrightnessPercent; + Color->G = Color->G * BLState->BrightnessPercent; + Color->B = Color->B * BLState->BrightnessPercent; } - else + } + + // Send Status Packet + { + system_time LastSendTime = BLState->LastStatusUpdateTime; + s64 NanosSinceLastSend = ((s64)Context->SystemTime_Current.NanosSinceEpoch - (s64)LastSendTime.NanosSinceEpoch); + s64 SecondsSinceLastSend = NanosSinceLastSend * 1000000000; + if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) { - LastPosition = 2; - } - - if ((BLState->OutgoingMsgQueue.WriteHead >= BLState->OutgoingMsgQueue.ReadHead) || - (BLState->OutgoingMsgQueue.WriteHead < BLState->OutgoingMsgQueue.ReadHead)) - { - u32 WriteIndex = BLState->OutgoingMsgQueue.WriteHead; + BLState->LastStatusUpdateTime = Context->SystemTime_Current; + gs_data* Msg = MessageQueue_GetWrite(&BLState->OutgoingMsgQueue); - gs_data* Msg = BLState->OutgoingMsgQueue.Buffers + WriteIndex; - if (Msg->Size == 0) - { - *Msg = PushSizeToData(&State->Permanent, sizeof(motor_packet)); - } - motor_packet* Packet = (motor_packet*)Msg->Memory; - Packet->FlowerPositions[0] = Position; - Packet->FlowerPositions[1] = Position; - Packet->FlowerPositions[2] = Position; + OutputDebugString("Sending Status\n"); - // NOTE(pjs): We increment the write head AFTER we've written so that - // the network thread doesn't think the buffer is ready to send before - // the data is set. We want to avoid the case of: - // 1. Main Thread increments write head to 1 - // 2. Network Thread thinks theres a new message to send at 0 - // 3. Network Thread sends the message at 0 - // 4. Main Thread sets the message at 0 - BLState->OutgoingMsgQueue.WriteHead += 1; - if (BLState->OutgoingMsgQueue.WriteHead >= BLUMEN_MESSAGE_QUEUE_COUNT) - { - BLState->OutgoingMsgQueue.WriteHead = 0; - } + blumen_packet* Packet = (blumen_packet*)Msg->Memory; + Packet->Type = PacketType_LumenariumStatus; + Packet->StatusPacket.NextMotorEventType = 0; + Packet->StatusPacket.NextEventTime = 0; + + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + CopyMemoryTo(ActiveAnim->Name.Str, Packet->StatusPacket.AnimFileName, + Min(ActiveAnim->Name.Length, 32)); } } } diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 56ad6b6..5009dac 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -11,6 +11,7 @@ enum bl_python_packet_type PacketType_PatternCommand = 1, PacketType_MotorState = 2, PacketType_Temperature = 3, + PacketType_LumenariumStatus = 4, }; #pragma pack(push, 1) @@ -35,6 +36,32 @@ typedef struct temp_packet { s8 Temperature; } temp_packet; + +enum motor_event_type +{ + MotorEvent_Close = 0, + MotorEvent_Open = 1, +}; + +typedef struct status_packet +{ + u8 NextMotorEventType; + u32 NextEventTime; + char AnimFileName[32]; +} status_packet; + +typedef struct blumen_packet +{ + bl_python_packet_type Type; + union + { + motor_packet MotorPacket; + microphone_packet MicPacket; + temp_packet TempPacket; + status_packet StatusPacket; + }; +} blumen_packet; + #pragma pack(pop) #define BLUMEN_MESSAGE_QUEUE_COUNT 32 @@ -51,17 +78,41 @@ struct mic_listen_job_data bool* Running; platform_socket_manager* SocketManager; - packet_ringbuffer* MicPacketBuffer; + blumen_network_msg_queue* IncomingMsgQueue; platform_socket_handle_ ListenSocket; blumen_network_msg_queue* OutgoingMsgQueue; }; +typedef struct time_range +{ + s32 StartHour; + s32 StartMinute; + + s32 EndHour; + s32 EndMinute; +} time_range; + +internal bool +SystemTimeIsInTimeRange(system_time SysTime, time_range Range) +{ + bool Result = (SysTime.Hour >= Range.StartHour && + SysTime.Minute >= Range.StartMinute && + SysTime.Hour <= Range.EndHour && + SysTime.Minute <= Range.EndMinute); + return Result; +} + +global time_range MotorOpenTimes[] = { + { 14, 28, 14, 29 } +}; +global u32 MotorOpenTimesCount = 1; + struct blumen_lumen_state { bool Running; - packet_ringbuffer MicPacketBuffer; + blumen_network_msg_queue IncomingMsgQueue; blumen_network_msg_queue OutgoingMsgQueue; temp_job_req JobReq; @@ -75,6 +126,11 @@ struct blumen_lumen_state animation_handle AnimHandles[3]; u32 CurrAnim; + + // NOTE(pjs): Based on temperature data from weatherman + // dim the leds. + r32 BrightnessPercent; + system_time LastStatusUpdateTime; }; diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index d1f4462..7beea9d 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -219,6 +219,9 @@ int main(int ArgCount, char** Args) printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str); //printf("%d\n", StripCount); + + + return 0; } From f6baf2290797f8bf61ed27697bb478d55eab7dc6 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sat, 20 Mar 2021 18:06:04 -0700 Subject: [PATCH 013/151] Message logging --- .../panels/foldhaus_panel_assembly_debug.h | 2 - src/app/foldhaus_app.cpp | 3 +- src/app/foldhaus_platform.h | 2 +- .../platform_win32/win32_foldhaus_socket.h | 28 +++++ src/app/ss_blumen_lumen/blumen_lumen.cpp | 113 ++++++++++-------- src/app/ss_blumen_lumen/blumen_lumen.h | 21 +++- 6 files changed, 113 insertions(+), 56 deletions(-) diff --git a/src/app/editor/panels/foldhaus_panel_assembly_debug.h b/src/app/editor/panels/foldhaus_panel_assembly_debug.h index 2f4f1a2..ee75876 100644 --- a/src/app/editor/panels/foldhaus_panel_assembly_debug.h +++ b/src/app/editor/panels/foldhaus_panel_assembly_debug.h @@ -107,8 +107,6 @@ AssemblyDebug_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren }break; } - ui_RangeSlider(Interface, MakeString("Test"), .5f, 0, 1); - ui_PopLayout(Interface, MakeString("Assembly Debug Layout")); } diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 5016e27..e5c1fed 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -133,7 +133,8 @@ UPDATE_AND_RENDER(UpdateAndRender) Editor_Render(State, Context, RenderBuffer); } -#if SEND_DATA +#define SEND_DATA +#ifdef SEND_DATA // NOTE(pjs): Building data buffers to be sent out to the sculpture // This array is used on the platform side to actually send the information assembly_array SACNAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaSACN, State->Transient); diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index efe8ff8..8a30d92 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -201,7 +201,7 @@ typedef struct system_time s32 Second; } system_time; -#define STATUS_PACKET_FREQ_SECONDS 5 +#define STATUS_PACKET_FREQ_SECONDS 2 struct context { diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index 1a51d46..6c83a0a 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -264,6 +264,11 @@ Win32SocketPeek(platform_socket* Socket) { }break; + case WSAECONNABORTED: + { + Win32CloseSocket(Socket); + }break; + InvalidDefaultCase; } } @@ -302,6 +307,14 @@ Win32SocketReceive(platform_socket* Socket, gs_memory_arena* Storage) #endif } + +typedef struct status_packet_foo +{ + u8 NextMotorEventType; + u32 NextEventTime; + char AnimFileName[32]; +} status_packet; + internal s32 Win32SocketSend(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags) { @@ -312,8 +325,11 @@ Win32SocketSend(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s3 SockAddress.sin_port = HostToNetU16(Port); SockAddress.sin_addr.s_addr = HostToNetU32(Address); + status_packet_foo* Foo = (status_packet_foo*)Data.Memory; + s32 LengthSent = sendto(*Win32Sock, (char*)Data.Memory, Data.Size, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); + OutputDebugString("Attempting To Send Network Data: "); if (LengthSent == SOCKET_ERROR) { s32 Error = WSAGetLastError(); @@ -330,6 +346,12 @@ Win32SocketSend(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s3 // TODO(Peter): :ErrorLogging InvalidCodePath; } + + OutputDebugString("Error\n"); + } + else + { + OutputDebugString("Sent\n"); } return LengthSent; @@ -368,9 +390,11 @@ Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, c s32 LengthSent = sendto(Socket->Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); + OutputDebugString("Attempting To Send Network Data: "); if (LengthSent == SOCKET_ERROR) { s32 Error = WSAGetLastError(); + OutputDebugString("Error\n"); if (Error == 10051) { } @@ -380,6 +404,10 @@ Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, c InvalidCodePath; } } + else + { + OutputDebugString("Sent\n"); + } return LengthSent; } diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 370341d..b1e29cb 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -37,26 +37,21 @@ MessageQueue_Init(blumen_network_msg_queue* Queue, gs_memory_arena* Arena) } } -internal gs_data* -MessageQueue_GetWrite(blumen_network_msg_queue* Queue) -{ - u32 Index = Queue->WriteHead++; - gs_data* Result = &Queue->Buffers[Index]; - Assert(Result->Size > 0); - - if (Queue->WriteHead >= PACKETS_MAX) - { - Queue->WriteHead = 0; - } - return Result; -} - internal bool MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) { - gs_data* Dest = MessageQueue_GetWrite(Queue); Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE); + + u32 Index = Queue->WriteHead; + gs_data* Dest = Queue->Buffers + Index; CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size); + Dest->Size = Msg.Size; + + // NOTE(pjs): We increment write head at the end of writing so that + // a reader thread doesn't pull the message off before we've finished + // filling it out + Queue->WriteHead++; + return true; } internal bool @@ -104,8 +99,6 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) u32 Port = WeathermanPort; s32 Flags = 0; SocketSend(Data->SocketManager, Data->ListenSocket, Address, Port, Msg, Flags); - - OutputDebugString("Sending Motor Packet\n"); } } } @@ -180,7 +173,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; BLState->MicListenJobData.ListenSocket = CreateSocket(Context.SocketManager, "127.0.0.1", "20185"); -#if 0 +#if 1 BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); #endif @@ -284,18 +277,30 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) State->AnimationSystem.ActiveFadeGroup.From.Index = 2; } - OutputDebugStringA("Received Pattern Packet\n"); + OutputDebugStringA("\nReceived Pattern Packet\n"); }break; case PacketType_MotorState: { motor_packet Motor = Packet.MotorPacket; + + // NOTE(pjs): Python sends multi-byte integers in little endian + // order. Have to unpack + u8* T = (u8*)&Motor.Temperature; + Motor.Temperature = (T[0] << 8 | + T[1] << 0); + BLState->LastKnownMotorState = Motor; - gs_string Temp = PushStringF(State->Transient, 256, "Received Motor States: %d %d %d\n", + gs_string Temp = PushStringF(State->Transient, 256, "\nReceived Motor States: \n\tPos: %d %d %d\n\tErr: %d %d %d\n\tTemp: %d\n\n", Motor.FlowerPositions[0], Motor.FlowerPositions[1], - Motor.FlowerPositions[2]); + Motor.FlowerPositions[2], + Motor.MotorStatus[0], + Motor.MotorStatus[1], + Motor.MotorStatus[2], + (u32)Motor.Temperature + ); NullTerminate(&Temp); OutputDebugStringA(Temp.Str); @@ -307,14 +312,14 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (Temp.Temperature > 21) { - BLState->BrightnessPercent = .5f; + BLState->BrightnessPercent = .25f; } else { BLState->BrightnessPercent = 1.f; } - gs_string TempStr = PushStringF(State->Transient, 256, "Temperature: %d\n", + gs_string TempStr = PushStringF(State->Transient, 256, "\nTemperature: %d\n", Temp.Temperature); NullTerminate(&TempStr); OutputDebugStringA(TempStr.Str); @@ -334,29 +339,37 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) time_range Range = MotorOpenTimes[i]; bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); + bool LastTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Last, Range); - if (CurrTimeInRange && !LastTimeInRange) + bool SendOpen = CurrTimeInRange && !LastTimeInRange; + + r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)BLState->LastSendTime.NanosSinceEpoch); + r64 SecondsSinceLastSend = NanosSinceLastSend / PowR32(10, 8); + + SendOpen = SecondsSinceLastSend > 2; + if (SendOpen) { - OutputDebugString("Open\n"); - gs_data* Msg = MessageQueue_GetWrite(&BLState->OutgoingMsgQueue); - - blumen_packet* Packet = (blumen_packet*)Msg->Memory; - Packet->Type = PacketType_MotorState; - Packet->MotorPacket.FlowerPositions[0] = 2; - Packet->MotorPacket.FlowerPositions[1] = 2; - Packet->MotorPacket.FlowerPositions[2] = 2; + BLState->LastSendTime = Context->SystemTime_Current; + OutputDebugString("Motors: Open\n"); + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket.FlowerPositions[0] = 2; + Packet.MotorPacket.FlowerPositions[1] = 2; + Packet.MotorPacket.FlowerPositions[2] = 2; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); } else if (!CurrTimeInRange && LastTimeInRange) { - OutputDebugString("Close\n"); - gs_data* Msg = MessageQueue_GetWrite(&BLState->OutgoingMsgQueue); - - blumen_packet* Packet = (blumen_packet*)Msg->Memory; - Packet->Type = PacketType_MotorState; - Packet->MotorPacket.FlowerPositions[0] = 1; - Packet->MotorPacket.FlowerPositions[1] = 1; - Packet->MotorPacket.FlowerPositions[2] = 1; + OutputDebugString("Motors: Close\n"); + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket.FlowerPositions[0] = 1; + Packet.MotorPacket.FlowerPositions[1] = 1; + Packet.MotorPacket.FlowerPositions[2] = 1; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); } } } @@ -377,23 +390,25 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) // Send Status Packet { system_time LastSendTime = BLState->LastStatusUpdateTime; - s64 NanosSinceLastSend = ((s64)Context->SystemTime_Current.NanosSinceEpoch - (s64)LastSendTime.NanosSinceEpoch); - s64 SecondsSinceLastSend = NanosSinceLastSend * 1000000000; + r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)LastSendTime.NanosSinceEpoch); + r64 SecondsSinceLastSend = NanosSinceLastSend / PowR32(10, 8); if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) { BLState->LastStatusUpdateTime = Context->SystemTime_Current; - gs_data* Msg = MessageQueue_GetWrite(&BLState->OutgoingMsgQueue); - OutputDebugString("Sending Status\n"); - blumen_packet* Packet = (blumen_packet*)Msg->Memory; - Packet->Type = PacketType_LumenariumStatus; - Packet->StatusPacket.NextMotorEventType = 0; - Packet->StatusPacket.NextEventTime = 0; + blumen_packet Packet = {}; + Packet.Type = PacketType_LumenariumStatus; + Packet.StatusPacket.NextMotorEventType = 0; + Packet.StatusPacket.NextEventTime = 0; animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - CopyMemoryTo(ActiveAnim->Name.Str, Packet->StatusPacket.AnimFileName, + CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName, Min(ActiveAnim->Name.Length, 32)); + Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0; + + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); } } } diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 5009dac..b29ea6c 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -18,6 +18,14 @@ enum bl_python_packet_type typedef struct motor_packet { u8 FlowerPositions[3]; + /* +u8 Motor1Pos; +u8 Motor2Pos; +u8 Motor3Pos; +*/ + u8 MotorStatus[3]; + u16 Temperature; + } motor_packet; typedef struct microphone_packet @@ -46,13 +54,15 @@ enum motor_event_type typedef struct status_packet { u8 NextMotorEventType; + // u16 Padding; u32 NextEventTime; + char AnimFileName[32]; } status_packet; typedef struct blumen_packet { - bl_python_packet_type Type; + u8 Type; union { motor_packet MotorPacket; @@ -104,9 +114,12 @@ SystemTimeIsInTimeRange(system_time SysTime, time_range Range) } global time_range MotorOpenTimes[] = { - { 14, 28, 14, 29 } + { 17, 56, 17, 56 }, + { 17, 58, 17, 56 }, + { 18, 00, 18, 00 }, + }; -global u32 MotorOpenTimesCount = 1; +global u32 MotorOpenTimesCount = 3; struct blumen_lumen_state { @@ -131,6 +144,8 @@ struct blumen_lumen_state // dim the leds. r32 BrightnessPercent; system_time LastStatusUpdateTime; + + system_time LastSendTime; }; From d81b8099701a0d5c2a2e2154a09e1e63ac5d03a8 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 20 Mar 2021 19:10:12 -0700 Subject: [PATCH 014/151] Patterns --- src/app/engine/assembly/foldhaus_assembly.h | 1 + src/app/patterns/blumen_patterns.h | 212 ++++++++++-------- .../platform_win32/win32_foldhaus_socket.h | 39 ++-- src/app/ss_blumen_lumen/blumen_lumen.cpp | 10 +- 4 files changed, 147 insertions(+), 115 deletions(-) diff --git a/src/app/engine/assembly/foldhaus_assembly.h b/src/app/engine/assembly/foldhaus_assembly.h index a14268e..d1a2562 100644 --- a/src/app/engine/assembly/foldhaus_assembly.h +++ b/src/app/engine/assembly/foldhaus_assembly.h @@ -142,6 +142,7 @@ struct assembly r32 Scale; v3 Center; + v3 MinLedPos, MaxLedPos; s32 LedCountTotal; u32 LedBufferIndex; diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 0b0d2d2..4eb7230 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -243,49 +243,59 @@ Voronoise(v2 P, r32 U, r32 V) return A.x / A.y; } -pixel FlowerAColors[FLOWER_COLORS_COUNT] = { - { 232,219,88 }, - { 232,219,88 }, - { 232,219,88 }, - { 147,75,176 }, - { 193,187,197 }, - { 223,190,49 }, - { 198,76,65 }, - { 198,76,65 }, - { 198,76,65 }, - { 226,200,17 }, - { 116,126,39 }, - { 61,62,31 } +v4 FlowerAColors[FLOWER_COLORS_COUNT] = { + { 232 / 255.f, 219 / 255.f, 88 / 255.f }, + { 232 / 255.f, 219 / 255.f, 88 / 255.f }, + { 232 / 255.f, 219 / 255.f, 88 / 255.f }, + { 147 / 255.f, 75 / 255.f, 176 / 255.f }, + { 193 / 255.f, 187 / 255.f, 197 / 255.f }, + { 223 / 255.f, 190 / 255.f, 49 / 255.f }, + { 198 / 255.f, 76 / 255.f, 65 / 255.f }, + { 198 / 255.f, 76 / 255.f, 65 / 255.f }, + { 198 / 255.f, 76 / 255.f, 65 / 255.f }, + { 226 / 255.f, 200 / 255.f, 17 / 255.f }, + { 116 / 255.f, 126 / 255.f, 39 / 255.f }, + { 61 / 255.f, 62 / 255.f, 31 / 255.f } }; -pixel FlowerBColors[FLOWER_COLORS_COUNT] = { - { 62,56,139 }, - { 93,87,164 }, - { 93,87,164 }, - { 93,87,164 }, - { 155,140,184 }, - { 191,201,204 }, - { 45,31,116 }, - { 201,196,156 }, - { 191,175,109 }, - { 186,176,107 }, - { 89,77,17 }, - { 47,49,18 }, +v4 FlowerBColors[FLOWER_COLORS_COUNT] = { + { 62 / 255.f, 56 / 255.f, 139 / 255.f }, + { 93 / 255.f, 87 / 255.f, 164 / 255.f }, + { 93 / 255.f, 87 / 255.f, 164 / 255.f }, + { 93 / 255.f, 87 / 255.f, 164 / 255.f }, + { 155 / 255.f, 140 / 255.f, 184 / 255.f }, + { 191 / 255.f, 201 / 255.f, 204 / 255.f }, + { 45 / 255.f, 31 / 255.f, 116 / 255.f }, + { 201 / 255.f, 196 / 255.f, 156 / 255.f }, + { 191 / 255.f, 175 / 255.f, 109 / 255.f }, + { 186 / 255.f, 176 / 255.f, 107 / 255.f }, + { 89 / 255.f, 77 / 255.f, 17 / 255.f }, + { 47 / 255.f, 49 / 255.f, 18 / 255.f }, }; -pixel FlowerCColors[FLOWER_COLORS_COUNT] = { - { 220,217,210 }, - { 220,217,210 }, - { 220,217,210 }, - { 225,193,110 }, - { 225,193,110 }, - { 227,221,214 }, - { 227,221,214 }, - { 230,218,187 }, - { 230,218,187 }, - { 172,190,211 }, - { 172,190,211 }, - { 172,190,211 }, +v4 FlowerCColors[FLOWER_COLORS_COUNT] = { + { 220 / 255.f, 217 / 255.f, 210 / 255.f }, + { 220 / 255.f, 217 / 255.f, 210 / 255.f }, + { 220 / 255.f, 217 / 255.f, 210 / 255.f }, + { 225 / 255.f, 193 / 255.f, 110 / 255.f }, + { 225 / 255.f, 193 / 255.f, 110 / 255.f }, + { 227 / 255.f, 221 / 255.f, 214 / 255.f }, + { 227 / 255.f, 221 / 255.f, 214 / 255.f }, + { 230 / 255.f, 218 / 255.f, 187 / 255.f }, + { 230 / 255.f, 218 / 255.f, 187 / 255.f }, + { 172 / 255.f, 190 / 255.f, 211 / 255.f }, + { 172 / 255.f, 190 / 255.f, 211 / 255.f }, + { 172 / 255.f, 190 / 255.f, 211 / 255.f }, }; +internal pixel +V4ToRGBPixel(v4 C) +{ + pixel Result = {}; + Result.R = (u8)(C.x * 255); + Result.G = (u8)(C.y * 255); + Result.B = (u8)(C.z * 255); + return Result; +} + internal pixel PixelMix(r32 T, pixel A, pixel B) { @@ -298,7 +308,15 @@ PixelMix(r32 T, pixel A, pixel B) } internal pixel -GetColor(pixel* Colors, u32 ColorsCount, r32 Percent) +PixelMix(r32 T, v4 A, v4 B) +{ + v4 Result = V4Lerp(T, A, B); + pixel P = V4ToRGBPixel(Result); + return P; +} + +internal v4 +GetColor(v4* Colors, u32 ColorsCount, r32 Percent) { Percent = Clamp01(Percent); @@ -311,7 +329,7 @@ GetColor(pixel* Colors, u32 ColorsCount, r32 Percent) r32 StepPercent = 1.f / (r32)ColorsCount; r32 PercentLower = (Percent - LowerPercent) / StepPercent; - pixel Result = PixelMix(PercentLower, Colors[LowerIndex], Colors[HigherIndex]); + v4 Result = V4Lerp(PercentLower, Colors[LowerIndex], Colors[HigherIndex]); return Result; } @@ -345,8 +363,8 @@ Pattern_FlowerColors(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_ar r32 CycleTime = 10; r32 CyclePercent = ModR32(Time, CycleTime) / CycleTime; - pixel CA = GetColor(FlowerAColors, FLOWER_COLORS_COUNT, CyclePercent); - pixel CB = GetColor(FlowerAColors, FLOWER_COLORS_COUNT, 1.0f - CyclePercent); + v4 CA = GetColor(FlowerAColors, FLOWER_COLORS_COUNT, CyclePercent); + v4 CB = GetColor(FlowerAColors, FLOWER_COLORS_COUNT, 1.0f - CyclePercent); for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) { @@ -637,16 +655,6 @@ Pattern_HueShift(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* } } -internal pixel -V4ToRGBPixel(v4 C) -{ - pixel Result = {}; - Result.R = (u8)(C.x * 255); - Result.G = (u8)(C.y * 255); - Result.B = (u8)(C.z * 255); - return Result; -} - internal void Pattern_HueFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { @@ -876,7 +884,7 @@ Pattern_FlowerColorToWhite(led_buffer* Leds, assembly Assembly, r32 Time, gs_mem FlowerOffset *= FlowerSpread; #else // Each flower different - pixel* Colors = &FlowerAColors[0]; + v4* Colors = &FlowerAColors[0]; r32 FlowerOffset = 0; if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "center")) { @@ -896,30 +904,30 @@ Pattern_FlowerColorToWhite(led_buffer* Leds, assembly Assembly, r32 Time, gs_mem u32 LedIndex = Strip.LedLUT[i]; v4 P = Leds->Positions[LedIndex]; - pixel FinalColor = {}; + v4 FinalColor = {}; r32 B = RemapR32(SinR32((P.y / 15.f) + (PercentCycle * TauR32)), -1, 1, .5f, 1.f); r32 HNoise = RemapR32(SinR32((P.y / 31.f) + (PercentCycle * TauR32)), -1, 1, 0.f, 1.f); - pixel BottomColor = GetColor(Colors, FLOWER_COLORS_COUNT, (PercentCycle + HNoise) / 2); + v4 BottomColor = GetColor(Colors, FLOWER_COLORS_COUNT, (PercentCycle + HNoise) / 2); FinalColor = BottomColor; - Leds->Colors[LedIndex] = FinalColor; + Leds->Colors[LedIndex] = V4ToRGBPixel(FinalColor); } } } r32 TLastFrame = 0; -pixel* FAC = &FlowerAColors[0]; -pixel* FBC = &FlowerBColors[0]; -pixel* FCC = &FlowerCColors[0]; +v4* FAC = &FlowerAColors[0]; +v4* FBC = &FlowerBColors[0]; +v4* FCC = &FlowerCColors[0]; internal void Pattern_BasicFlowers(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { if (TLastFrame > Time) { - pixel* Temp = FAC; + v4 * Temp = FAC; FAC = FBC; FBC = FCC; FCC = Temp; @@ -931,7 +939,7 @@ Pattern_BasicFlowers(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_ar v2_strip Strip = Assembly.Strips[StripIndex]; // Each flower different - pixel* Colors = FAC; + v4 * Colors = FAC; if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "center")) { Colors = FBC; @@ -951,7 +959,8 @@ Pattern_BasicFlowers(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_ar r32 T = ModR32(P.y + CycleT, 200) / 200.f; T = Clamp01(T); - Leds->Colors[LedIndex] = GetColor(Colors, FLOWER_COLORS_COUNT, T); + v4 Color = GetColor(Colors, FLOWER_COLORS_COUNT, T); + Leds->Colors[LedIndex] = V4ToRGBPixel(Color); } } } @@ -979,12 +988,10 @@ Pattern_Patchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* T v3 BSeed = v3{P.z, P.x, P.y}; r32 BNoise = 1.0f; //Fbm3D(BSeed / 50); - pixel C = GetColor(&FlowerAColors[0], FLOWER_COLORS_COUNT, Noise); - C.R = (u8)((r32)C.R * BNoise); - C.G = (u8)((r32)C.G * BNoise); - C.B = (u8)((r32)C.B * BNoise); + v4 C = GetColor(&FlowerAColors[0], FLOWER_COLORS_COUNT, Noise); + C = C * BNoise; - Leds->Colors[LedIndex] = C; + Leds->Colors[LedIndex] = V4ToRGBPixel(C); //Leds->Colors[LedIndex] = pixel{NV, NV, NV}; } } @@ -992,48 +999,71 @@ Pattern_Patchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* T internal void Pattern_Leafy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { - pixel* Colors = &FlowerBColors[0]; + v4* Colors = &FlowerBColors[0]; +#if 1 for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) { v4 P = Leds->Positions[LedIndex]; - r32 RefPos = P.y + Noise2D(v2{P.x, P.z} * 50); + r32 RefPos = P.y; // + Noise2D(v2{P.x, P.z} * 10); + v4 C = {}; r32 B = 0; - pixel C = {}; - r32 BandWidth = 5; - r32 TransitionPeriod = 5.0f; + r32 TransitionPeriod = 30.0f; u32 BandCount = 10; for (u32 Band = 0; Band < BandCount; Band++) { r32 BandSeed = RemapR32(Hash1((r32)Band), -1, 1, 0, 1); r32 BandDelay = BandSeed * TransitionPeriod; - r32 BandTransitionDuration = RemapR32(Hash1((r32)Band * 3.413f), -1, 1, 0, 1) * TransitionPeriod; - r32 BandPercent = Smoothstep(ModR32(Time + BandDelay, BandTransitionDuration) / TransitionPeriod, 0, 1); - r32 BandHeight = 150 - BandPercent * 250; + r32 BandTransitionPeriod = RemapR32(Hash1((r32)Band * 3.413f), -1, 1, 0, 1) * TransitionPeriod; + r32 BandOffset = Time + BandDelay; + r32 BandPercent = ModR32(BandOffset, BandTransitionPeriod) / BandTransitionPeriod; + r32 BandSmoothed = Smoothstep(BandPercent, 0, 1); - r32 BandDist = Abs(RefPos - BandHeight); + r32 BandHeight = 125 - BandPercent * 250; - //B += Max(0, BandWidth - BandDist); - B = Max(0, BandWidth - BandDist); - - - { - //pixel BandColor = GetColor(Colors, FLOWER_COLORS_COUNT, BandSeed); - pixel BandColor = Colors[Band % FLOWER_COLORS_COUNT]; - C.R = C.R + (BandColor.R * B); - C.G = C.G + (BandColor.G * B); - C.B = C.B + (BandColor.B * B); - } + r32 BandDist = Abs(RefPos - (BandHeight + BandSeed * 5)); + B += Max(0, BandWidth - BandDist); } + B = Clamp(0, B, 1); - u8 V = (u8)(B * 255); - //pixel C = { V, V, V }; - Leds->Colors[LedIndex] = C; + r32 BandCP = (P.y + 100) / 200; + BandCP = 0.8f; + v4 BandC = GetColor(&FlowerBColors[0], FLOWER_COLORS_COUNT, BandCP); + + v4 GradientC = GetColor(&FlowerBColors[0], FLOWER_COLORS_COUNT, 0); + r32 GradientB = RemapR32(P.y, 200, 0, 1, 0); + GradientB = Clamp(0, GradientB, 1); + + C = (GradientC * GradientB) + (BandC * B); + //C *= B; + + Leds->Colors[LedIndex] = V4ToRGBPixel(C); } +#else + + r32 FadeTop = 150; + r32 FadeBottom = -50; + + v4 C = GetColor(&FlowerAColors[0], FLOWER_COLORS_COUNT, 0); + + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + { + v4 P = Leds->Positions[LedIndex]; + + r32 Brightness = RemapR32(P.y, 200, 0, 1, 0); + + Brightness = Clamp(Brightness, 0, 1); + u8 B = (u8)(Brightness * 255); + + v4 CB = C * Brightness; + + Leds->Colors[LedIndex] = V4ToRGBPixel(CB); + } +#endif } internal void diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index 6c83a0a..00bceec 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -308,13 +308,6 @@ Win32SocketReceive(platform_socket* Socket, gs_memory_arena* Storage) } -typedef struct status_packet_foo -{ - u8 NextMotorEventType; - u32 NextEventTime; - char AnimFileName[32]; -} status_packet; - internal s32 Win32SocketSend(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags) { @@ -325,26 +318,32 @@ Win32SocketSend(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s3 SockAddress.sin_port = HostToNetU16(Port); SockAddress.sin_addr.s_addr = HostToNetU32(Address); - status_packet_foo* Foo = (status_packet_foo*)Data.Memory; - s32 LengthSent = sendto(*Win32Sock, (char*)Data.Memory, Data.Size, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); OutputDebugString("Attempting To Send Network Data: "); if (LengthSent == SOCKET_ERROR) { s32 Error = WSAGetLastError(); - if (Error == 10051) + switch (Error) { - } - if (Error == 10053) - { - // TODO(pjs): WSAECONNABORTED - InvalidCodePath; - } - else - { - // TODO(Peter): :ErrorLogging - InvalidCodePath; + case WSAECONNABORTED: + { + }break; + + case WSAENETUNREACH: + { + }break; + + case WSAECONNRESET: + { + + }break; + + case WSAENOTCONN: + { + }break; + + InvalidDefaultCase; } OutputDebugString("Error\n"); diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index b1e29cb..cfc8b54 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -137,17 +137,17 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_Leafy); } -internal pixel -TEMP_Saturate(pixel P) +internal v4 +TEMP_Saturate(v4 P) { - v4 CRGB = v4{ (r32)P.R / 255.f, (r32)P.G / 255.f, (r32)P.B / 255.f, 1.f }; + v4 CRGB = P; v4 CHSV = RGBToHSV(CRGB); if (CHSV.g > .3f) { CHSV.g = 1; CRGB = HSVToRGB(CHSV); } - return V4ToRGBPixel(CRGB); + return CRGB; } internal gs_data @@ -375,6 +375,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } // Dim the leds based on temp data +#if DIM_LED_BRIGHTNESS for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) { led_buffer Buffer = State->LedSystem.Buffers[i]; @@ -386,6 +387,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Color->B = Color->B * BLState->BrightnessPercent; } } +#endif // Send Status Packet { From e6042b7a01f83e2ac394b034635bb7579bee8b50 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sat, 20 Mar 2021 21:49:02 -0700 Subject: [PATCH 015/151] Patterns --- src/app/engine/animation/foldhaus_animation.h | 3 +- src/app/patterns/blumen_patterns.h | 50 ++++------ src/app/platform_win32/win32_foldhaus.cpp | 6 ++ src/app/ss_blumen_lumen/blumen_lumen.cpp | 97 ++++++++++++------- src/app/ss_blumen_lumen/blumen_lumen.h | 25 ++++- 5 files changed, 116 insertions(+), 65 deletions(-) diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 0c33c8b..33d466a 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -157,7 +157,6 @@ struct animation_system // NOTE(Peter): The frame currently being displayed/processed. you // can see which frame you're on by looking at the time slider on the timeline // panel - animation_handle ActiveAnimationHandle_; animation_fade_group ActiveFadeGroup; s32 CurrentFrame; @@ -561,7 +560,9 @@ AnimationFadeGroup_Update(animation_fade_group* Group, r32 DeltaTime) { if (IsValid(Group->To)) { + r32 FadeBefore = Group->FadeElapsed; Group->FadeElapsed += DeltaTime; + if (Group->FadeElapsed >= Group->FadeDuration) { AnimationFadeGroup_Advance(Group); diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 4eb7230..18d267e 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -980,7 +980,7 @@ Pattern_Patchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* T v3 Pp = P.xyz; - r32 Noise = Fbm3D((Pp / 1000) + (v3{Time, -Time, SinR32(Time)} * 0.1f)); + r32 Noise = Fbm3D((Pp / 1000) + (v3{Time, -Time, Time} * 0.01f)); Noise = RemapR32(Noise, -1, 1, 0, 1); Noise = Smoothstep(Noise, 0, 1); u8 NV = (u8)(Noise * 255); @@ -991,6 +991,7 @@ Pattern_Patchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* T v4 C = GetColor(&FlowerAColors[0], FLOWER_COLORS_COUNT, Noise); C = C * BNoise; + //Leds->Colors[LedIndex] = V4ToRGBPixel(v4{Noise, Noise, Noise, 1}); Leds->Colors[LedIndex] = V4ToRGBPixel(C); //Leds->Colors[LedIndex] = pixel{NV, NV, NV}; } @@ -1001,11 +1002,13 @@ Pattern_Leafy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Tr { v4* Colors = &FlowerBColors[0]; -#if 1 + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) { v4 P = Leds->Positions[LedIndex]; - r32 RefPos = P.y; // + Noise2D(v2{P.x, P.z} * 10); + +#if 0 + r32 RefPos = P.y + Noise2D(v2{P.x, P.z} * 10); v4 C = {}; r32 B = 0; @@ -1015,18 +1018,19 @@ Pattern_Leafy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Tr u32 BandCount = 10; for (u32 Band = 0; Band < BandCount; Band++) { - r32 BandSeed = RemapR32(Hash1((r32)Band), -1, 1, 0, 1); - r32 BandDelay = BandSeed * TransitionPeriod; + r32 BandSeed = Hash1((r32)Band); + r32 BandSeedPos = RemapR32(BandSeed, -1, 1, 0, 1); + r32 BandDelay = BandSeedPos * TransitionPeriod; r32 BandTransitionPeriod = RemapR32(Hash1((r32)Band * 3.413f), -1, 1, 0, 1) * TransitionPeriod; r32 BandOffset = Time + BandDelay; r32 BandPercent = ModR32(BandOffset, BandTransitionPeriod) / BandTransitionPeriod; r32 BandSmoothed = Smoothstep(BandPercent, 0, 1); - r32 BandHeight = 125 - BandPercent * 250; + r32 BandHeight = -125 + BandPercent * 250; - r32 BandDist = Abs(RefPos - (BandHeight + BandSeed * 5)); + r32 BandDist = Abs(RefPos - BandHeight); - B += Max(0, BandWidth - BandDist); + B += Max(0, (BandWidth + BandSeed * 2.5) - BandDist); } B = Clamp(0, B, 1); @@ -1039,31 +1043,17 @@ Pattern_Leafy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Tr GradientB = Clamp(0, GradientB, 1); C = (GradientC * GradientB) + (BandC * B); +#endif + //v4 C = GetColor(&FlowerBColors[0], FLOWER_COLORS_COUNT, 0); + v4 C = v4{ 255, 100, 3 }; + C /= 255.f; + //r32 B = Fbm3D(P.xyz / 200); //C *= B; - + if (P.y < 75) { + C = v4{ 139 / 255.f, 69 / 255.f, 19 / 255.f, 1.0f} * .25f; + } Leds->Colors[LedIndex] = V4ToRGBPixel(C); } -#else - - r32 FadeTop = 150; - r32 FadeBottom = -50; - - v4 C = GetColor(&FlowerAColors[0], FLOWER_COLORS_COUNT, 0); - - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) - { - v4 P = Leds->Positions[LedIndex]; - - r32 Brightness = RemapR32(P.y, 200, 0, 1, 0); - - Brightness = Clamp(Brightness, 0, 1); - u8 B = (u8)(Brightness * 255); - - v4 CB = C * Brightness; - - Leds->Colors[LedIndex] = V4ToRGBPixel(CB); - } -#endif } internal void diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index c34f3f4..ab6faac 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -681,6 +681,12 @@ WinMain ( RenderBuffer.ViewHeight = MainWindow.Height; Context.DeltaTime = LastFrameSecondsElapsed; +#if 0 + gs_string T = PushStringF(Context.ThreadContext.Transient, 256, "%f\n", Context.DeltaTime); + NullTerminate(&T); + OutputDebugStringA(T.Str); +#endif + Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); bool Multithread = true; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index cfc8b54..2d9b842 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -223,9 +223,9 @@ BlumenLumen_CustomInit(app_state* State, context Context) for (u32 i = 0; i < FLOWER_COLORS_COUNT; i++) { - FlowerAColors[i] = TEMP_Saturate(FlowerAColors[i]); - FlowerBColors[i] = TEMP_Saturate(FlowerBColors[i]); - FlowerCColors[i] = TEMP_Saturate(FlowerCColors[i]); + //FlowerAColors[i] = TEMP_Saturate(FlowerAColors[i]); + //FlowerBColors[i] = TEMP_Saturate(FlowerBColors[i]); + //FlowerCColors[i] = TEMP_Saturate(FlowerCColors[i]); } return Result; @@ -236,8 +236,11 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; - MotorTimeElapsed += Context->DeltaTime; + bool SendMotorCommand = false; + blumen_packet MotorCommand = {}; + #if 0 + MotorTimeElapsed += Context->DeltaTime; BLState->TimeElapsed += Context->DeltaTime; if (BLState->TimeElapsed > 5) @@ -249,10 +252,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } #endif - gs_string BlueString = MakeString("blue"); - gs_string GreenString = MakeString("green"); - gs_string ILoveYouString = MakeString("i_love_you"); - while (MessageQueue_CanRead(&BLState->IncomingMsgQueue)) { gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); @@ -262,27 +261,47 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) case PacketType_PatternCommand: { microphone_packet Mic = Packet.MicPacket; - u32 NameLen = CStringLength(Mic.AnimationFileName); - if (StringEqualsCharArray(BlueString.ConstString, Mic.AnimationFileName, NameLen)) - { - State->AnimationSystem.ActiveFadeGroup.From.Index = 0; - } - else if (StringEqualsCharArray(GreenString.ConstString, Mic.AnimationFileName, NameLen)) - { - State->AnimationSystem.ActiveFadeGroup.From.Index = 1; - } - else if (StringEqualsCharArray(ILoveYouString.ConstString, Mic.AnimationFileName, NameLen)) - { - State->AnimationSystem.ActiveFadeGroup.From.Index = 2; - } - OutputDebugStringA("\nReceived Pattern Packet\n"); + for (u32 i = 0; i < PhraseToAnimMapCount; i++) + { + gs_const_string PhraseStr = ConstString(PhraseToAnimMap[i].Phrase); + u32 PhraseIndex = PhraseToAnimMap[i].PatternIndex; + if (StringEqualsCharArray(PhraseStr, Mic.AnimationFileName, NameLen)) + { + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + animation_handle{(s32)PhraseIndex}, + 3.0f); + OutputDebugStringA("\nReceived Pattern Packet\n"); + + { + // DEBUG CODE + u8 MotorState = BLState->LastKnownMotorState.FlowerPositions[0]; + if (MotorState == 2) { + OutputDebugStringA("Sending 1\n"); + MotorState = 1; + } + else + { + OutputDebugStringA("Sending 1\n"); + MotorState = 2; + } + + blumen_packet MPacket = {}; + MPacket.Type = PacketType_MotorState; + MPacket.MotorPacket.FlowerPositions[0] = MotorState; + MPacket.MotorPacket.FlowerPositions[1] = MotorState; + MPacket.MotorPacket.FlowerPositions[2] = MotorState; + MotorCommand = MPacket; + SendMotorCommand = true; + } + } + } }break; case PacketType_MotorState: { - motor_packet Motor = Packet.MotorPacket; + motor_status_packet Motor = Packet.MotorStatusPacket; // NOTE(pjs): Python sends multi-byte integers in little endian // order. Have to unpack @@ -290,12 +309,12 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Motor.Temperature = (T[0] << 8 | T[1] << 0); - BLState->LastKnownMotorState = Motor; + BLState->LastKnownMotorState = Motor.Pos; gs_string Temp = PushStringF(State->Transient, 256, "\nReceived Motor States: \n\tPos: %d %d %d\n\tErr: %d %d %d\n\tTemp: %d\n\n", - Motor.FlowerPositions[0], - Motor.FlowerPositions[1], - Motor.FlowerPositions[2], + Motor.Pos.FlowerPositions[0], + Motor.Pos.FlowerPositions[1], + Motor.Pos.FlowerPositions[2], Motor.MotorStatus[0], Motor.MotorStatus[1], Motor.MotorStatus[2], @@ -334,6 +353,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue)) { +#if 0 for (u32 i = 0; i < MotorOpenTimesCount; i++) { time_range Range = MotorOpenTimes[i]; @@ -347,33 +367,42 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)BLState->LastSendTime.NanosSinceEpoch); r64 SecondsSinceLastSend = NanosSinceLastSend / PowR32(10, 8); - SendOpen = SecondsSinceLastSend > 2; + //SendOpen = SecondsSinceLastSend > 2; if (SendOpen) { + SendMotorCommand = true; + BLState->LastSendTime = Context->SystemTime_Current; OutputDebugString("Motors: Open\n"); + blumen_packet Packet = {}; Packet.Type = PacketType_MotorState; Packet.MotorPacket.FlowerPositions[0] = 2; Packet.MotorPacket.FlowerPositions[1] = 2; Packet.MotorPacket.FlowerPositions[2] = 2; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + MotorCommand = Packet; } else if (!CurrTimeInRange && LastTimeInRange) { + SendMotorCommand = true; OutputDebugString("Motors: Close\n"); + blumen_packet Packet = {}; Packet.Type = PacketType_MotorState; Packet.MotorPacket.FlowerPositions[0] = 1; Packet.MotorPacket.FlowerPositions[1] = 1; Packet.MotorPacket.FlowerPositions[2] = 1; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + MotorCommand = Packet; } } +#endif + + if (SendMotorCommand) + { + gs_data Msg = StructToData(&MotorCommand, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + } } - // Dim the leds based on temp data #if DIM_LED_BRIGHTNESS for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) @@ -387,6 +416,8 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Color->B = Color->B * BLState->BrightnessPercent; } } + + // TODO(pjs): dim stem to 50% #endif // Send Status Packet diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index b29ea6c..30b975a 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -18,6 +18,11 @@ enum bl_python_packet_type typedef struct motor_packet { u8 FlowerPositions[3]; +} motor_packet; + +typedef struct motor_status_packet +{ + motor_packet Pos; /* u8 Motor1Pos; u8 Motor2Pos; @@ -26,7 +31,7 @@ u8 Motor3Pos; u8 MotorStatus[3]; u16 Temperature; -} motor_packet; +} motor_status_packet; typedef struct microphone_packet { @@ -66,6 +71,7 @@ typedef struct blumen_packet union { motor_packet MotorPacket; + motor_status_packet MotorStatusPacket; microphone_packet MicPacket; temp_packet TempPacket; status_packet StatusPacket; @@ -121,6 +127,23 @@ global time_range MotorOpenTimes[] = { }; global u32 MotorOpenTimesCount = 3; +struct phrase_string_to_anim_file +{ + char* Phrase; + u32 PatternIndex; +}; + +phrase_string_to_anim_file PhraseToAnimMap[] = { + { "begonia", 0}, + { "hyacinth", 1 }, + { "tulip", 1 }, + { "calla lilly", 0 }, + { "sunflower", 1 }, + { "salvia", 2 }, + { "freesia", 2 }, +}; +u32 PhraseToAnimMapCount = sizeof(PhraseToAnimMap) / sizeof(PhraseToAnimMap[0]); + struct blumen_lumen_state { bool Running; From ef4eb84a83f6863c9a3baeb571b4cea2ed7ba3b2 Mon Sep 17 00:00:00 2001 From: PS Date: Mon, 22 Mar 2021 19:09:30 -0700 Subject: [PATCH 016/151] More pattern work --- project.4coder | 40 +++--- src/app/patterns/blumen_patterns.h | 122 +++++++++++++++++- src/app/platform_win32/win32_foldhaus.cpp | 2 +- .../platform_win32/win32_foldhaus_serial.h | 68 ++++++---- src/app/ss_blumen_lumen/blumen_lumen.cpp | 4 +- 5 files changed, 189 insertions(+), 47 deletions(-) diff --git a/project.4coder b/project.4coder index 51d1801..830f111 100644 --- a/project.4coder +++ b/project.4coder @@ -1,37 +1,37 @@ version(1); project_name = "main.exe"; patterns = { -"*.c", -"*.cpp", -"*.h", -"*.m", -"*.bat", -"*.sh", -"*.4coder", + "*.c", + "*.cpp", + "*.h", + "*.m", + "*.bat", + "*.sh", + "*.4coder", }; blacklist_patterns = { -".*", + ".*", }; load_paths_base = { - { "src", .relative = true, .recursive = true, }, - { "meta", .relative = true, .recursive = true, }, - { "gs_libs", .relative = true, .recursive = true, }, + { "src", .relative = true, .recursive = true, }, + { "meta", .relative = true, .recursive = true, }, + { "gs_libs", .relative = true, .recursive = true, }, }; load_paths = { - { load_paths_base, .os = "win", }, - { load_paths_base, .os = "linux", }, - { load_paths_base, .os = "mac", }, + { load_paths_base, .os = "win", }, + { load_paths_base, .os = "linux", }, + { load_paths_base, .os = "mac", }, }; command_list = { - { .name = "build_application", - .out = "*app compilation*", .footer_panel = false, .save_dirty_files = true, - .cmd = { { "build\build_app_msvc_win32_debug.bat" , .os = "win" }, + { .name = "build_application", + .out = "*compilation*", .footer_panel = false, .save_dirty_files = true, + .cmd = { { "build\build_app_msvc_win32_debug.bat" , .os = "win" }, { "./build.sh", .os = "linux" }, { "./build.sh", .os = "mac" }, }, }, - { .name = "build_meta", - .out = "*meta compilation*", .footer_panel = false, .save_dirty_files = true, - .cmd = { { "build\build_meta_msvc_win32_debug.bat" , .os = "win" }, + { .name = "build_meta", + .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, + .cmd = { { "build\build_meta_msvc_win32_debug.bat" , .os = "win" }, { "./build_meta.sh", .os = "linux" }, { "./build_meta.sh", .os = "mac" }, }, }, }; diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 4eb7230..cee488d 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -18,6 +18,25 @@ Smoothstep(r32 T, r32 A, r32 B) { return LerpR32(Smoothstep(T), A, B); } +internal v3 +Smoothstep(v3 P) +{ + v3 R = {}; + R.x = Smoothstep(P.x); + R.y = Smoothstep(P.y); + R.z = Smoothstep(P.z); + return R; +} + +internal v3 +AbsV3(v3 P) +{ + v3 Result = {}; + Result.x = Abs(P.x); + Result.y = Abs(P.y); + Result.z = Abs(P.z); + return Result; +} internal v2 FloorV2(v2 P) @@ -112,6 +131,15 @@ Hash3(v2 P) return FractV3(SinV3(Q) * 43758.5453f); } +internal r32 +HashV3ToR32(v3 P) +{ + v3 Pp = FractV3(P * 0.3183099f + v3{0.1f, 0.1f, 0.1f}); + Pp *= 17.0f; + r32 Result = FractR32(Pp.x * Pp.y * Pp.z * (Pp.x + Pp.y + Pp.z)); + return Result; +} + internal r32 Random(v2 N) { @@ -135,7 +163,35 @@ Noise2D(v2 P) } internal r32 -Noise3D(v3 Pp) +Noise3D(v3 P) +{ + P = AbsV3(P); + v3 PFloor = FloorV3(P); + v3 PFract = FractV3(P); + v3 F = Smoothstep(PFract); + + r32 Result = LerpR32(F.z, + LerpR32(F.y, + LerpR32(F.x, + HashV3ToR32(PFloor + v3{0, 0, 0}), + HashV3ToR32(PFloor + v3{1, 0, 0})), + LerpR32(F.x, + HashV3ToR32(PFloor + v3{0, 1, 0}), + HashV3ToR32(PFloor + v3{1, 1, 0}))), + LerpR32(F.y, + LerpR32(F.x, + HashV3ToR32(PFloor + v3{0, 0, 1}), + HashV3ToR32(PFloor + v3{1, 0, 1})), + LerpR32(F.x, + HashV3ToR32(PFloor + v3{0, 1, 1}), + HashV3ToR32(PFloor + v3{1, 1, 1})))); + + Assert(Result >= 0 && Result <= 1); + return Result; +} + +internal r32 +Noise3D_(v3 Pp) { v3 P = FloorV3(Pp); v3 W = FractV3(Pp); @@ -219,6 +275,26 @@ Fbm3D(v3 P) return A; } +internal r32 +Fbm3D(v3 P, r32 T) +{ + v3 Tt = v3{T, T, T}; + r32 SinT = SinR32(T); + v3 Tv = v3{SinT, SinT, SinT}; + v3 Pp = P; + r32 F = 0.0; + + F += 0.500000f * Noise3D(Pp + Tt); Pp = Pp * 2.02; + F += 0.031250f * Noise3D(Pp); Pp = Pp * 2.01; + F += 0.250000f * Noise3D(Pp); Pp = Pp * 2.03; + F += 0.125000f * Noise3D(Pp); Pp = Pp * 2.01; + F += 0.062500f * Noise3D(Pp); Pp = Pp * 2.04; + F += 0.015625f * Noise3D(Pp + Tv); + + F = F / 0.984375f; + return F; +} + internal r32 Voronoise(v2 P, r32 U, r32 V) { @@ -974,6 +1050,7 @@ Pattern_Wavy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Tra internal void Pattern_Patchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { +#if 0 for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) { v4 P = Leds->Positions[LedIndex]; @@ -994,6 +1071,49 @@ Pattern_Patchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* T Leds->Colors[LedIndex] = V4ToRGBPixel(C); //Leds->Colors[LedIndex] = pixel{NV, NV, NV}; } +#elif 1 + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + { + v4 P = Leds->Positions[LedIndex]; + r32 LedRange = 300.0f; + r32 ScaleFactor = 1.0f / LedRange; + v3 Pp = P.xyz + v3{150, 100, 0}; + + r32 NoiseA = Noise3D((Pp / 38) + v3{0, 0, Time}); + NoiseA = PowR32(NoiseA, 3); + NoiseA = Smoothstep(NoiseA); + v4 CA = v4{1, 0, 1, 1} * NoiseA; + + r32 NoiseB = Noise3D((Pp / 75) + v3{Time * 0.5f, 0, 0}); + NoiseB = PowR32(NoiseB, 3); + NoiseB = Smoothstep(NoiseB); + v4 CB = v4{0, 1, 1, 1} * NoiseB; + + v4 C = CA + CB; + Leds->Colors[LedIndex] = V4ToRGBPixel(C); + } +#else + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + { + v4 P = Leds->Positions[LedIndex]; + r32 LedRange = 300.0f; + r32 ScaleFactor = 1.0f / LedRange; + v3 Pp = P.xyz + v3{150, 100, 0}; + + r32 NoiseA = Fbm3D((Pp / 35), Time * 0.5f); + //NoiseA = PowR32(NoiseA, 3); + NoiseA = Smoothstep(NoiseA); + v4 CA = v4{1, 0, 1, 1} * NoiseA; + + r32 NoiseB = Noise3D((Pp / 35) + v3{0, 0, Time * 5}); + NoiseB = PowR32(NoiseB, 3); + NoiseB = Smoothstep(NoiseB); + v4 CB = v4{0, 1, 1, 1}; + + v4 C = V4Lerp(NoiseB, CA, CB); + Leds->Colors[LedIndex] = V4ToRGBPixel(C); + } +#endif } internal void diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index c34f3f4..7ebdea7 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -683,7 +683,7 @@ WinMain ( Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); - bool Multithread = true; + bool Multithread = false; if (Multithread) { for (addressed_data_buffer* At = OutputData.Root; diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index 820da09..f82e198 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -74,38 +74,60 @@ HANDLE Win32SerialPort_Open(char* PortName) { DEBUG_TRACK_FUNCTION; - HANDLE ComPortHandle = CreateFile(PortName, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, // Default Security Attr - OPEN_EXISTING, - 0, // Not overlapped I/O - NULL); + HANDLE ComPortHandle = INVALID_HANDLE_VALUE;; - if (ComPortHandle != INVALID_HANDLE_VALUE) + WIN32_FIND_DATA FindData; + HANDLE ComPortExists = FindFirstFile(PortName, &FindData); + + // TODO(PS): we aren't sure yet if FindFirstFile will actually work + // for the purpose of checking to see if a ComPort actually exists. + // When you go to Foldspace next time, make sure we are still connecting + // the sculpture + if (ComPortExists != INVALID_HANDLE_VALUE) { - COMMTIMEOUTS Timeouts = { 0 }; - Timeouts.ReadIntervalTimeout = 0; // in milliseconds - Timeouts.ReadTotalTimeoutConstant = 0; // in milliseconds - Timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds - Timeouts.WriteTotalTimeoutConstant = 0; // in milliseconds - Timeouts.WriteTotalTimeoutMultiplier = 0; // in milliseconds - if (SetCommTimeouts(ComPortHandle, &Timeouts)) + ComPortHandle = CreateFile(PortName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, // Default Security Attr + OPEN_EXISTING, + 0, // Not overlapped I/O + NULL); + + bool HasError = false; + + if (ComPortHandle != INVALID_HANDLE_VALUE) { + COMMTIMEOUTS Timeouts = { 0 }; + Timeouts.ReadIntervalTimeout = 0; // in milliseconds + Timeouts.ReadTotalTimeoutConstant = 0; // in milliseconds + Timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds + Timeouts.WriteTotalTimeoutConstant = 0; // in milliseconds + Timeouts.WriteTotalTimeoutMultiplier = 0; // in milliseconds + HasError = !SetCommTimeouts(ComPortHandle, &Timeouts); } else { - s32 Error = GetLastError(); - // TODO(pjs): Error logging + HasError = true; + } + + if (HasError) + { + // Error + s32 Error = GetLastError(); + switch (Error) + { + case ERROR_NO_SUCH_DEVICE: + case ERROR_FILE_NOT_FOUND: + { + // NOTE(PS): The outer scope should handle these cases + ComPortHandle = INVALID_HANDLE_VALUE; + }break; + + InvalidDefaultCase; + } } - } - else - { - // Error - s32 Error = GetLastError(); - // TODO(pjs): Error logging } return ComPortHandle; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index cfc8b54..ea9abc7 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -173,7 +173,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; BLState->MicListenJobData.ListenSocket = CreateSocket(Context.SocketManager, "127.0.0.1", "20185"); -#if 1 +#if 0 BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); #endif @@ -213,7 +213,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) Anim2.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(17), 0); + Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(16), 0); BLState->AnimHandles[2] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); From 3140ff3fe6188927171c7e6c1d58bacab5f73666 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Mon, 22 Mar 2021 20:58:52 -0700 Subject: [PATCH 017/151] Some pattern work, added a way to identify what COM ports are available on windows, implemented multithreading patterns, and added a path to turn all the lights off on shutdown --- build/build_app_msvc_win32_debug.bat | 4 +- src/app/engine/animation/foldhaus_animation.h | 2 +- .../animation/foldhaus_animation_renderer.cpp | 79 +++++++++- src/app/engine/assembly/foldhaus_assembly.h | 12 ++ src/app/foldhaus_app.cpp | 35 +++-- src/app/foldhaus_platform.h | 2 +- src/app/patterns/blumen_patterns.h | 135 +++++++++--------- src/app/platform_win32/win32_foldhaus.cpp | 63 ++++---- .../platform_win32/win32_foldhaus_serial.h | 76 ++++++++-- src/app/ss_blumen_lumen/blumen_lumen.cpp | 3 + src/gs_libs/gs_types.cpp | 19 +++ src/gs_libs/gs_types.h | 25 +++- src/serial_monitor/first.cpp | 2 +- 13 files changed, 329 insertions(+), 128 deletions(-) diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index 663ae28..eec2df0 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -25,12 +25,12 @@ SET LastError=%ERRORLEVEL% del lock.tmp -cl %CommonCompilerFlags% %SourceCodePath%\platform_win32\win32_foldhaus.cpp /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib opengl32.lib dsound.lib Ws2_32.lib Comdlg32.lib +cl %CommonCompilerFlags% %SourceCodePath%\platform_win32\win32_foldhaus.cpp /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib opengl32.lib dsound.lib Ws2_32.lib Comdlg32.lib Winspool.lib REM COMPILE UTILITY EXES -cl %CommonCompilerFlags% %ProjectDevPath%\src\serial_monitor\first.cpp /Feserial_monitor.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib +cl %CommonCompilerFlags% %ProjectDevPath%\src\serial_monitor\first.cpp /Feserial_monitor.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib Winspool.lib cl %CommonCompilerFlags% %ProjectDevPath%\src\sculpture_gen\gen_blumen_lumen.cpp /Fegen_blumen_lumen.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 33d466a..30c0589 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -5,7 +5,7 @@ // #ifndef FOLDHAUS_ANIMATION -#define ANIMATION_PROC(name) void name(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +#define ANIMATION_PROC(name) void name(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) typedef ANIMATION_PROC(animation_proc); struct frame_range diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index 16072ad..a22c417 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -89,14 +89,71 @@ struct pattern_args u8* UserData; }; -internal void -AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, animation_pattern_array Patterns, pattern_args PatternArgs) +struct render_anim_to_led_buffer_job_data { + animation_pattern Pattern; + led_buffer Buffer; + led_buffer_range BufferRange; + pattern_args PatternArgs; + r32 SecondsIntoBlock; +}; + +internal void +AnimationSystem_RenderAnimationToLedBufferJob(gs_thread_context Context, gs_data Data) +{ + render_anim_to_led_buffer_job_data JobData = *(render_anim_to_led_buffer_job_data*)Data.Memory; + JobData.Pattern.Proc(&JobData.Buffer, + JobData.BufferRange, + JobData.PatternArgs.Assembly, + JobData.SecondsIntoBlock, + JobData.PatternArgs.Transient, + JobData.PatternArgs.UserData); +} + +#define MULTITHREAD_PATTERN_RENDERING 1 + +internal void +AnimationSystem_BeginRenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, animation_pattern_array Patterns, pattern_args PatternArgs, + context Context) +{ + DEBUG_TRACK_FUNCTION; + u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min; r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame; animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle); + +#if MULTITHREAD_PATTERN_RENDERING + u32 JobsCount = 4; + u32 LedsPerJob = Buffer->LedCount / JobsCount; + + for (u32 i = 0; i < JobsCount; i++) + { + gs_data Data = PushSizeToData(Context.ThreadContext.Transient, sizeof(render_anim_to_led_buffer_job_data)); + render_anim_to_led_buffer_job_data* JobData = (render_anim_to_led_buffer_job_data*)Data.Memory; + JobData->Pattern = Pattern; + JobData->Buffer = *Buffer; + JobData->BufferRange.First = LedsPerJob * i; + JobData->BufferRange.OnePastLast = LedsPerJob * (i + 1); + JobData->PatternArgs = PatternArgs; + JobData->SecondsIntoBlock = SecondsIntoBlock; + + Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, + (thread_proc*)AnimationSystem_RenderAnimationToLedBufferJob, + Data, + ConstString("Render Pattern To Buffer")); + } +#else Pattern.Proc(Buffer, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData); +#endif +} + +internal void +AnimationSystem_EndRenderBlockToLedBuffer (context Context) +{ +#if MULTITHREAD_PATTERN_RENDERING + Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); +#endif } // NOTE(pjs): This mirrors animation_layer_frame to account @@ -114,7 +171,8 @@ RenderAnimationToLedBuffer (animation_system* System, layer_led_buffer* LayerBuffers, led_buffer* AssemblyLedBuffer, animation_pattern_array Patterns, - gs_memory_arena* Transient) + gs_memory_arena* Transient, + context Context) { led_buffer AccBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); @@ -144,15 +202,17 @@ RenderAnimationToLedBuffer (animation_system* System, { led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer; animation_block Block = LayerFrame.Hot; - AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs); + AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context); } if (LayerFrame.HasNextHot) { led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer; animation_block Block = LayerFrame.NextHot; - AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs); + AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context); } + + AnimationSystem_EndRenderBlockToLedBuffer(Context); } // Blend together any layers that have a hot and next hot buffer @@ -193,8 +253,11 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse led_system* LedSystem, animation_pattern_array Patterns, gs_memory_arena* Transient, + context Context, u8* UserData) { + DEBUG_TRACK_FUNCTION; + s32 CurrentFrame = System->CurrentFrame; r32 FrameTime = CurrentFrame * System->SecondsPerFrame; @@ -231,7 +294,8 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse FromLayerBuffers, AssemblyLedBuffer, Patterns, - Transient); + Transient, + Context); led_buffer ConsolidatedBuffer = FromBuffer; if (ToAnim) { @@ -241,7 +305,8 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse ToLayerBuffers, AssemblyLedBuffer, Patterns, - Transient); + Transient, + Context); r32 BlendPercent = FadeGroup.FadeElapsed / FadeGroup.FadeDuration; LedBuffer_Blend(FromBuffer, ToBuffer, &ConsolidatedBuffer, LedBlend_Lerp, (u8*)&BlendPercent); diff --git a/src/app/engine/assembly/foldhaus_assembly.h b/src/app/engine/assembly/foldhaus_assembly.h index d1a2562..0744e8e 100644 --- a/src/app/engine/assembly/foldhaus_assembly.h +++ b/src/app/engine/assembly/foldhaus_assembly.h @@ -32,6 +32,12 @@ struct led_buffer v4* Positions; }; +struct led_buffer_range +{ + u32 First; + u32 OnePastLast; +}; + struct led_system { gs_allocator PlatformMemory; @@ -184,6 +190,8 @@ LedBuffer_ClearToBlack(led_buffer* Buffer) internal void LedBuffer_Copy(led_buffer From, led_buffer* To) { + DEBUG_TRACK_FUNCTION; + Assert(From.LedCount == To->LedCount); u32 LedCount = To->LedCount; for (u32 i = 0; i < LedCount; i++) @@ -195,6 +203,8 @@ LedBuffer_Copy(led_buffer From, led_buffer* To) internal void LedBuffer_Blend(led_buffer A, led_buffer B, led_buffer* Dest, led_blend_proc* BlendProc, u8* UserData) { + DEBUG_TRACK_FUNCTION; + Assert(A.LedCount == B.LedCount); Assert(Dest->LedCount == A.LedCount); Assert(BlendProc); @@ -211,6 +221,8 @@ LedBuffer_Blend(led_buffer A, led_buffer B, led_buffer* Dest, led_blend_proc* Bl internal led_buffer LedBuffer_CreateCopyCleared (led_buffer Buffer, gs_memory_arena* Arena) { + DEBUG_TRACK_FUNCTION; + led_buffer Result = {}; Result.LedCount = Buffer.LedCount; Result.Positions = Buffer.Positions; diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index e5c1fed..5519446 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -95,6 +95,21 @@ INITIALIZE_APPLICATION(InitializeApplication) State->RunEditor = true; } +internal void +BuildAssemblyData (app_state* State, context Context, addressed_data_buffer_list* OutputData) +{ + +#define SEND_DATA +#ifdef SEND_DATA + // NOTE(pjs): Building data buffers to be sent out to the sculpture + // This array is used on the platform side to actually send the information + assembly_array SACNAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaSACN, State->Transient); + assembly_array UARTAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaUART, State->Transient); + SACN_BuildOutputData(&State->SACN, OutputData, SACNAssemblies, &State->LedSystem); + UART_BuildOutputData(OutputData, UARTAssemblies, &State->LedSystem, State->Transient); +#endif +} + UPDATE_AND_RENDER(UpdateAndRender) { DEBUG_TRACK_FUNCTION; @@ -119,6 +134,7 @@ UPDATE_AND_RENDER(UpdateAndRender) &State->LedSystem, State->Patterns, State->Transient, + *Context, State->UserSpaceDesc.UserData.Memory); } @@ -133,20 +149,21 @@ UPDATE_AND_RENDER(UpdateAndRender) Editor_Render(State, Context, RenderBuffer); } -#define SEND_DATA -#ifdef SEND_DATA - // NOTE(pjs): Building data buffers to be sent out to the sculpture - // This array is used on the platform side to actually send the information - assembly_array SACNAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaSACN, State->Transient); - assembly_array UARTAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaUART, State->Transient); - SACN_BuildOutputData(&State->SACN, OutputData, SACNAssemblies, &State->LedSystem); - UART_BuildOutputData(OutputData, UARTAssemblies, &State->LedSystem, State->Transient); -#endif + BuildAssemblyData(State, *Context, OutputData); } CLEANUP_APPLICATION(CleanupApplication) { app_state* State = (app_state*)Context.MemoryBase; + + for (u32 i = 0; i < State->Assemblies.Count; i++) + { + assembly Assembly = State->Assemblies.Values[i]; + led_buffer LedBuffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0}); + } + BuildAssemblyData(State, Context, OutputData); + US_CustomCleanup(&State->UserSpaceDesc, State, Context); SACN_Cleanup(&State->SACN, Context); } diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 8a30d92..f464603 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -76,7 +76,7 @@ typedef UPDATE_AND_RENDER(update_and_render); #define RELOAD_STATIC_DATA(name) void name(context Context, debug_services* DebugServices) typedef RELOAD_STATIC_DATA(reload_static_data); -#define CLEANUP_APPLICATION(name) void name(context Context) +#define CLEANUP_APPLICATION(name) void name(context Context, addressed_data_buffer_list* OutputData) typedef CLEANUP_APPLICATION(cleanup_application); // Platform Functions diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 1a4a10a..a0d46e6 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -411,30 +411,30 @@ GetColor(v4* Colors, u32 ColorsCount, r32 Percent) } internal void -SolidColorPattern(led_buffer* Leds, pixel Color) +SolidColorPattern(led_buffer* Leds, led_buffer_range Range, pixel Color) { - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { Leds->Colors[LedIndex] = Color; } } internal void -Pattern_Blue(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_Blue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { pixel Blue = pixel{0, 0, 255}; - SolidColorPattern(Leds, Blue); + SolidColorPattern(Leds, Range, Blue); } internal void -Pattern_Green(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_Green(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { pixel Green = pixel{0, 255, 0}; - SolidColorPattern(Leds, Green); + SolidColorPattern(Leds, Range, Green); } internal void -Pattern_FlowerColors(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_FlowerColors(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { r32 CycleTime = 10; r32 CyclePercent = ModR32(Time, CycleTime) / CycleTime; @@ -442,7 +442,7 @@ Pattern_FlowerColors(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_ar v4 CA = GetColor(FlowerAColors, FLOWER_COLORS_COUNT, CyclePercent); v4 CB = GetColor(FlowerAColors, FLOWER_COLORS_COUNT, 1.0f - CyclePercent); - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; r32 Pct = (Abs(ModR32(P.y, 150) / 150) + CycleTime) * PiR32; @@ -453,7 +453,7 @@ Pattern_FlowerColors(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_ar } internal void -TestPatternOne(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +TestPatternOne(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { led_strip_list BlumenStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("assembly"), ConstString("Blumen Lumen"), Transient); led_strip_list RadiaStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("assembly"), ConstString("Radialumia"), Transient); @@ -485,7 +485,7 @@ TestPatternOne(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* T } internal void -TestPatternTwo(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +TestPatternTwo(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { r32 PeriodicTime = (Time / PiR32) * 2; @@ -504,7 +504,7 @@ TestPatternTwo(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* T r32 OuterRadiusSquared = 1000000; r32 InnerRadiusSquared = 0; - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 Position = Leds->Positions[LedIndex]; @@ -537,7 +537,7 @@ TestPatternTwo(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* T } internal void -TestPatternThree(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +TestPatternThree(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { v4 GreenCenter = v4{0, 0, 150, 1}; r32 GreenRadius = Abs(SinR32(Time)) * 200; @@ -548,7 +548,7 @@ TestPatternThree(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* r32 FadeDist = 35; - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 LedPosition = Leds->Positions[LedIndex]; u8 Red = 0; @@ -702,7 +702,7 @@ while (Hue > 360.0f) { Hue -= 360.0f; } } internal void -Pattern_HueShift(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_HueShift(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { r32 Height = SinR32(Time) * 25; @@ -713,7 +713,7 @@ Pattern_HueShift(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* v4 HSV = { CycleProgress * 360, 1, 1, 1 }; v4 RGB = HSVToRGB(HSV); - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 Pos = Leds->Positions[LedIndex]; r32 Dist = Pos.y - Height; @@ -732,7 +732,7 @@ Pattern_HueShift(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* } internal void -Pattern_HueFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_HueFade(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { r32 HueBase = ModR32(Time * 50, 360); @@ -740,7 +740,7 @@ Pattern_HueFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* r32 CycleProgress = FractR32(Time / CycleLength); r32 CycleBlend = (SinR32(Time) * .5f) + .5f; - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 Pos = Leds->Positions[LedIndex]; r32 Hue = HueBase + Pos.y + Pos.x; @@ -752,9 +752,9 @@ Pattern_HueFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* } internal void -Pattern_AllGreen(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_AllGreen(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { u32 I = LedIndex + 1; Leds->Colors[LedIndex] = {}; @@ -781,7 +781,7 @@ PatternHash(r32 Seed) } internal void -Pattern_Spots(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_Spots(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { pixel ColorA = { 0, 255, 255 }; pixel ColorB = { 255, 0, 255 }; @@ -790,7 +790,7 @@ Pattern_Spots(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Tr Time *= Speed; r32 ScaleA = 2 * SinR32(Time / 5); r32 ScaleB = 2.4f * CosR32(Time / 2.5f); - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; r32 V = P.y; @@ -807,10 +807,10 @@ Pattern_Spots(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Tr } internal void -Pattern_LighthouseRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_LighthouseRainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { v2 RefVector = V2Normalize(v2{ SinR32(Time), CosR32(Time) }); - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v2 Vector = v2{ Leds->Positions[LedIndex].x, @@ -828,7 +828,7 @@ Pattern_LighthouseRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memo } internal void -Pattern_SmoothGrowRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_SmoothGrowRainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { r32 FillCycleTime = ModR32(Time, 7.0f) / 7.0f; r32 ColorCycleTime = ModR32(Time, 21.0f) / 21.0f; @@ -850,7 +850,7 @@ Pattern_SmoothGrowRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memo } internal void -Pattern_GrowAndFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_GrowAndFade(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { r32 PercentCycle = ModR32(Time, 10) / 10; v4 HSV = { PercentCycle * 360, 1, 1, 1 }; @@ -859,7 +859,7 @@ Pattern_GrowAndFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_are r32 RefHeight = -100 + (Smoothstep(PercentCycle * 1.4f) * 400); r32 RefBrightness = 1.0f - Smoothstep(PercentCycle); - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; @@ -873,7 +873,7 @@ Pattern_GrowAndFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_are } internal void -Pattern_ColorToWhite(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_ColorToWhite(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { r32 FadeBottomBase = 50; r32 FadeTop = 125; @@ -935,7 +935,7 @@ Pattern_ColorToWhite(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_ar } internal void -Pattern_FlowerColorToWhite(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_FlowerColorToWhite(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { r32 FadeBottomBase = 50; r32 FadeTop = 125; @@ -999,7 +999,7 @@ v4* FBC = &FlowerBColors[0]; v4* FCC = &FlowerCColors[0]; internal void -Pattern_BasicFlowers(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_BasicFlowers(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { if (TLastFrame > Time) { @@ -1042,16 +1042,11 @@ Pattern_BasicFlowers(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_ar } internal void -Pattern_Wavy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + DEBUG_TRACK_FUNCTION; -} - -internal void -Pattern_Patchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ -#if 0 - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; @@ -1072,8 +1067,14 @@ Pattern_Patchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* T Leds->Colors[LedIndex] = V4ToRGBPixel(C); //Leds->Colors[LedIndex] = pixel{NV, NV, NV}; } -#elif 1 - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) +} + +internal void +Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + DEBUG_TRACK_FUNCTION; + + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; r32 LedRange = 300.0f; @@ -1093,37 +1094,17 @@ Pattern_Patchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* T v4 C = CA + CB; Leds->Colors[LedIndex] = V4ToRGBPixel(C); } -#else - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) - { - v4 P = Leds->Positions[LedIndex]; - r32 LedRange = 300.0f; - r32 ScaleFactor = 1.0f / LedRange; - v3 Pp = P.xyz + v3{150, 100, 0}; - - r32 NoiseA = Fbm3D((Pp / 35), Time * 0.5f); - //NoiseA = PowR32(NoiseA, 3); - NoiseA = Smoothstep(NoiseA); - v4 CA = v4{1, 0, 1, 1} * NoiseA; - - r32 NoiseB = Noise3D((Pp / 35) + v3{0, 0, Time * 5}); - NoiseB = PowR32(NoiseB, 3); - NoiseB = Smoothstep(NoiseB); - v4 CB = v4{0, 1, 1, 1}; - - v4 C = V4Lerp(NoiseB, CA, CB); - Leds->Colors[LedIndex] = V4ToRGBPixel(C); - } -#endif } internal void -Pattern_Leafy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_Leafy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + DEBUG_TRACK_FUNCTION; + v4* Colors = &FlowerBColors[0]; - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; @@ -1177,15 +1158,35 @@ Pattern_Leafy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Tr } internal void -Pattern_LeafyPatchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_LeafyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { - + DEBUG_TRACK_FUNCTION; + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + v4 P = Leds->Positions[LedIndex]; + r32 LedRange = 300.0f; + r32 ScaleFactor = 1.0f / LedRange; + v3 Pp = P.xyz + v3{150, 100, 0}; + + r32 NoiseA = Fbm3D((Pp / 35), Time * 0.5f); + //NoiseA = PowR32(NoiseA, 3); + NoiseA = Smoothstep(NoiseA); + v4 CA = v4{1, 0, 1, 1} * NoiseA; + + r32 NoiseB = Noise3D((Pp / 35) + v3{0, 0, Time * 5}); + NoiseB = PowR32(NoiseB, 3); + NoiseB = Smoothstep(NoiseB); + v4 CB = v4{0, 1, 1, 1}; + + v4 C = V4Lerp(NoiseB, CA, CB); + Leds->Colors[LedIndex] = V4ToRGBPixel(C); + } } internal void -Pattern_WavyPatchy(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { - + DEBUG_TRACK_FUNCTION; } #define BLUMEN_PATTERNS_H diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 7edec1e..b9db4c6 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -396,7 +396,7 @@ Win32_SendAddressedDataBuffer(gs_thread_context Context, addressed_data_buffer* { if (BufferAt->ComPort.Length > 0) { - HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 2000000, 8, NOPARITY, 1); + HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 2000000, 8, NOPARITY, 1, Context.Transient); if (SerialPort != INVALID_HANDLE_VALUE) { if (Win32SerialPort_Write(SerialPort, BufferAt->Data)) @@ -525,6 +525,37 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) #include "../../gs_libs/gs_path.h" +internal void +Win32_SendOutputData(gs_thread_context ThreadContext, addressed_data_buffer_list OutputData) +{ + bool Multithread = true; + if (Multithread) + { + for (addressed_data_buffer* At = OutputData.Root; + At != 0; + At = At->Next) + { + gs_data ProcArg = {}; + ProcArg.Memory = (u8*)At; + ProcArg.Size = sizeof(addressed_data_buffer); + Win32PushWorkOnQueue(&Win32WorkQueue.WorkQueue, Win32_SendAddressedDataBuffer_Job, ProcArg, ConstString("Send UART Data")); + } + } + else + { + for (addressed_data_buffer* At = OutputData.Root; + At != 0; + At = At->Next) + { + gs_data ProcArg = {}; + ProcArg.Memory = (u8*)At; + ProcArg.Size = sizeof(addressed_data_buffer); + Win32_SendAddressedDataBuffer_Job(ThreadContext, ProcArg); + } + } + +} + int WINAPI WinMain ( HINSTANCE HInstance, @@ -689,31 +720,7 @@ WinMain ( Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); - bool Multithread = false; - if (Multithread) - { - for (addressed_data_buffer* At = OutputData.Root; - At != 0; - At = At->Next) - { - gs_data ProcArg = {}; - ProcArg.Memory = (u8*)At; - ProcArg.Size = sizeof(addressed_data_buffer); - Win32PushWorkOnQueue(&Win32WorkQueue.WorkQueue, Win32_SendAddressedDataBuffer_Job, ProcArg, ConstString("Send UART Data")); - } - } - else - { - for (addressed_data_buffer* At = OutputData.Root; - At != 0; - At = At->Next) - { - gs_data ProcArg = {}; - ProcArg.Memory = (u8*)At; - ProcArg.Size = sizeof(addressed_data_buffer); - Win32_SendAddressedDataBuffer_Job(ThreadContext, ProcArg); - } - } + Win32_SendOutputData(ThreadContext, OutputData); RenderCommandBuffer(RenderBuffer); ClearRenderBuffer(&RenderBuffer); @@ -741,7 +748,9 @@ WinMain ( LastFrameEnd = GetWallClock(); } - Context.CleanupApplication(Context); + Context.CleanupApplication(Context, &OutputData); + Win32_SendOutputData(ThreadContext, OutputData); + Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext); Win32WorkQueue_Cleanup(); //Win32_TestCode_SocketReading_Cleanup(); diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index f82e198..a1244a9 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -70,20 +70,74 @@ Win32SerialPort_SetState(HANDLE ComPortHandle, u32 BaudRate, u8 ByteSize, u8 Par bool Success = SetCommState(ComPortHandle, &ControlSettings); } +gs_const_string_array +Win32SerialPorts_List(gs_memory_arena* Arena, gs_memory_arena* Transient) +{ + gs_const_string_array Result = {}; + + DWORD SizeNeeded0 = 0; + DWORD CountReturned0 = 0; + EnumPorts(NULL, 1, 0, 0, &SizeNeeded0, &CountReturned0); + Assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER); + + DWORD SizeNeeded1 = 0; + DWORD CountReturned1 = 0; + PORT_INFO_1* PortsArray = (PORT_INFO_1*)PushSize(Transient, SizeNeeded0); + if (EnumPorts(NULL, + 1, + (u8*)PortsArray, + SizeNeeded0, + &SizeNeeded1, + &CountReturned1)) + { + Result.CountMax = (u64)CountReturned1; + Result.Strings = PushArray(Arena, gs_const_string, Result.CountMax); + + for (; Result.Count < Result.CountMax; Result.Count++) + { + u64 Index = Result.Count; + u64 StrLen = CStringLength(PortsArray[Index].pName); + gs_string Str = PushString(Arena, StrLen); + PrintF(&Str, "%.*s", StrLen, PortsArray[Index].pName); + Result.Strings[Result.Count] = Str.ConstString; + } + } + + return Result; +} + +bool +Win32SerialPort_Exists(char* PortName, gs_memory_arena* Transient) +{ + bool Result = false; + if (PortName != 0) + { + gs_const_string PortIdent = ConstString(PortName); + u32 IdentBegin = FindLast(PortIdent, '\\') + 1; + PortIdent = Substring(PortIdent, IdentBegin, PortIdent.Length); + + gs_const_string_array PortsAvailable = Win32SerialPorts_List(Transient, Transient); + + for (u64 i = 0; i < PortsAvailable.Count; i++) + { + gs_const_string AvailablePortName = PortsAvailable.Strings[i]; + if (StringsEqualUpToLength(AvailablePortName, PortIdent, PortIdent.Length)) + { + Result = true; + break; + } + } + } + return Result; +} + HANDLE -Win32SerialPort_Open(char* PortName) +Win32SerialPort_Open(char* PortName, gs_memory_arena* Transient) { DEBUG_TRACK_FUNCTION; HANDLE ComPortHandle = INVALID_HANDLE_VALUE;; - WIN32_FIND_DATA FindData; - HANDLE ComPortExists = FindFirstFile(PortName, &FindData); - - // TODO(PS): we aren't sure yet if FindFirstFile will actually work - // for the purpose of checking to see if a ComPort actually exists. - // When you go to Foldspace next time, make sure we are still connecting - // the sculpture - if (ComPortExists != INVALID_HANDLE_VALUE) + if (Win32SerialPort_Exists(PortName, Transient)) { ComPortHandle = CreateFile(PortName, @@ -289,7 +343,7 @@ Win32SerialArray_Get(gs_const_string PortName) } HANDLE -Win32SerialArray_GetOrOpen(gs_const_string PortName, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits) +Win32SerialArray_GetOrOpen(gs_const_string PortName, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits, gs_memory_arena* Transient) { DEBUG_TRACK_FUNCTION; @@ -297,7 +351,7 @@ Win32SerialArray_GetOrOpen(gs_const_string PortName, u32 BaudRate, u8 ByteSize, if (PortHandle == INVALID_HANDLE_VALUE) { Assert(IsNullTerminated(PortName)); - PortHandle = Win32SerialPort_Open(PortName.Str); + PortHandle = Win32SerialPort_Open(PortName.Str, Transient); if (PortHandle != INVALID_HANDLE_VALUE) { Win32SerialPort_SetState(PortHandle, BaudRate, ByteSize, Parity, StopBits); diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index fe97067..2a0e150 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -133,8 +133,11 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_FlowerColorToWhite); Patterns_PushPattern(Patterns, Pattern_BasicFlowers); // 15 + Patterns_PushPattern(Patterns, Pattern_Wavy); Patterns_PushPattern(Patterns, Pattern_Patchy); Patterns_PushPattern(Patterns, Pattern_Leafy); + Patterns_PushPattern(Patterns, Pattern_LeafyPatchy); + Patterns_PushPattern(Patterns, Pattern_WavyPatchy); } internal v4 diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index 1b59488..ca3336d 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -1557,6 +1557,25 @@ FindFirst(gs_string String, char C) return FindFirst(String.ConstString, 0, C); } +internal s64 +FindLast(char* String, s64 StartIndex, char C) +{ + s64 Result = -1; + s64 i = 0; + while (String[i] != 0 && i < StartIndex) + { + i++; + } + while (String[i]) + { + if (String[i] == C) + { + Result = i; + } + i++; + } + return Result; +} internal s64 FindLast(gs_const_string String, u64 StartIndex, char C) { diff --git a/src/gs_libs/gs_types.h b/src/gs_libs/gs_types.h index 792f2de..243f51b 100644 --- a/src/gs_libs/gs_types.h +++ b/src/gs_libs/gs_types.h @@ -232,7 +232,7 @@ struct u64_array # define InvalidDefaultCase default: { AssertBreak("invalid default case"); } break; # define StaticAssert(c) \ enum { \ - Glue(gs_AssertFail_, __LINE__) = 1 / (int)(!!(c)), \ +Glue(gs_AssertFail_, __LINE__) = 1 / (int)(!!(c)), \ } #else # define Assert(c) @@ -560,7 +560,7 @@ struct gs_const_string_array { gs_const_string* Strings; u64 Count; - u64 Used; + u64 CountMax; }; struct gs_string_array @@ -579,6 +579,27 @@ CStringLength(char* Str) return Result; } +internal bool +CStringsEqual(char* A, char* B) +{ + bool Result = true; + + char* AAt = A; + char* BAt = B; + while (AAt[0] && BAt[0]) + { + if (AAt[0] != BAt[0]) + { + Result = false; + break; + } + AAt++; + BAt++; + } + if (AAt[0] != 0 || BAt[0] != 0) { Result = false; } + return Result; +} + #define StringExpand(str) (int)(str).Length, (str).Str #define LitString(cstr) gs_const_string{(char*)(cstr), CStringLength((char*)cstr) } diff --git a/src/serial_monitor/first.cpp b/src/serial_monitor/first.cpp index 1f0c6d6..4ef8ccc 100644 --- a/src/serial_monitor/first.cpp +++ b/src/serial_monitor/first.cpp @@ -93,7 +93,7 @@ int main(int ArgCount, char** Args) { gs_thread_context Ctx = Win32CreateThreadContext(); - HANDLE SerialHandle = Win32SerialPort_Open("\\\\.\\COM9"); + HANDLE SerialHandle = Win32SerialPort_Open("\\\\.\\COM9", Ctx.Transient); Win32SerialPort_SetState(SerialHandle, 2000000, 8, 0, 1); gs_const_string OutFileName = ConstString("./serial_dump.data"); From 4d0d916d97cf83de1fb8beea8ccf408396ec7870 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Mon, 22 Mar 2021 22:36:33 -0700 Subject: [PATCH 018/151] More pattern work, fixed a problem where switching animations in the timeline window would overwrite fields of the new animation because the interface was caching the previous interfaces values, and created helpers to load animations from files. --- src/app/editor/interface.h | 23 ++++++++ .../foldhaus_panel_animation_timeline.h | 35 ++++++----- src/app/engine/animation/foldhaus_animation.h | 16 ++++- .../animation/foldhaus_animation_renderer.cpp | 58 +++++++++++-------- .../foldhaus_animation_serializer.cpp | 16 ++++- src/app/patterns/blumen_patterns.h | 35 +++++++++++ src/app/ss_blumen_lumen/blumen_lumen.cpp | 58 +++++++++++-------- 7 files changed, 174 insertions(+), 67 deletions(-) diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index 6b91bef..f06c16a 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -752,6 +752,17 @@ ui_PushOverlayLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction return Result; } +static gs_string +ui_PushLayoutCategoryName(ui_interface* Interface, gs_string Category, gs_string Identifier) +{ + gs_string Result = PushStringF(Interface->PerFrameMemory, + Category.Length + Identifier.Length, + "%S%S", + Category.ConstString, + Identifier.ConstString); + return Result; +} + static ui_widget* ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name) { @@ -770,6 +781,12 @@ ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir Interface->ActiveLayout = Result; return Result; } +static ui_widget* +ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Category, gs_string Identifier) +{ + gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Identifier); + return ui_PushLayout(Interface, Bounds, FillDir, Name); +} static ui_widget* ui_PushLayout(ui_interface* Interface, gs_string Name, bool Inset = true) @@ -851,6 +868,12 @@ ui_PopLayout(ui_interface* Interface, gs_string LayoutName) ui_CommitBounds(Interface->ActiveLayout, Layout->Bounds); } } +static void +ui_PopLayout(ui_interface* Interface, gs_string Category, gs_string Identifier) +{ + gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Identifier); + ui_PopLayout(Interface, Name); +} static ui_widget* ui_BeginRow(ui_interface* Interface, u32 ColumnsMax) diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 886b1a3..b0b7dcc 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -15,6 +15,8 @@ struct animation_timeline_state handle SelectedBlockHandle; animation_handle EditingAnimationHandle; u32 SelectedAnimationLayer; + + animation_handle NextActiveAnim; }; inline u32 @@ -504,13 +506,10 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) if (FileInfo.Path.Length > 0) { - gs_file AnimFile = ReadEntireFile(Context.ThreadContext.FileHandler, FileInfo.Path); - - gs_string AnimFileString = MakeString((char*)AnimFile.Data.Memory, AnimFile.Data.Size); - animation NewAnim = AnimParser_Parse(AnimFileString, State->AnimationSystem.Storage, State->Patterns); - NewAnim.FileInfo = AnimFile.FileInfo; - - animation_handle NewAnimHandle = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); + animation_handle NewAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + FileInfo.Path); State->AnimationSystem.ActiveFadeGroup.From = NewAnimHandle; } } @@ -762,11 +761,9 @@ PANEL_MODAL_OVERRIDE_CALLBACK(AnimInfoView_SaveAnimFileCallback) } internal void -AnimationTimeline_SetActiveAnimation (animation_handle Handle, animation_timeline_state* TimelineState, - animation_system* System) +AnimationTimeline_SetActiveAnimation (animation_handle Handle, animation_timeline_state* TimelineState) { - System->ActiveFadeGroup.From = Handle; - TimelineState->EditingAnimationHandle = Handle; + TimelineState->NextActiveAnim = Handle; } internal void @@ -775,7 +772,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn animation_system* AnimSystem = &State->AnimationSystem; ui_interface* Interface = &State->Interface; - ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout")); + ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout"), ActiveAnim->Name); ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBG); @@ -796,7 +793,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn { animation_handle NewHandle = {}; NewHandle.Index = i; - AnimationTimeline_SetActiveAnimation(NewHandle, TimelineState, AnimSystem); + AnimationTimeline_SetActiveAnimation(NewHandle, TimelineState); } } } @@ -899,7 +896,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn AnimationTimeline_AddAnimationBlockCommand(TimelineState, State, Context); } } - ui_PopLayout(Interface, MakeString("AnimInfo Layout")); + ui_PopLayout(Interface, MakeString("AnimInfo Layout"), ActiveAnim->Name); } internal void @@ -917,11 +914,12 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* animation* ActiveAnim = 0; animation_handle Handle = State->AnimationSystem.ActiveFadeGroup.From; - TimelineState->EditingAnimationHandle = Handle; if (IsValid(Handle)) { animation_array Animations = State->AnimationSystem.Animations; ActiveAnim = AnimationArray_GetSafe(Animations, Handle); + TimelineState->EditingAnimationHandle = Handle; + TimelineState->NextActiveAnim = Handle; } ui_FillRect(&State->Interface, PanelBounds, v4{.1f,.1f,.1f,1.f}); @@ -944,6 +942,13 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* LayerList_Render(TimelineState, ActiveAnim, LayersBounds, Panel, RenderBuffer, State, Context); TimeRange_Render(TimelineState, ActiveAnim, TimeRangeBounds, RenderBuffer, State, Context); AnimInfoView_Render(TimelineState, ActiveAnim, InfoBounds, Panel, RenderBuffer, State, Context); + + if (!AnimHandlesAreEqual(TimelineState->NextActiveAnim, + Handle)) + { + State->AnimationSystem.ActiveFadeGroup.From = TimelineState->NextActiveAnim; + TimelineState->EditingAnimationHandle = TimelineState->NextActiveAnim; + } } #define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 30c0589..12281fe 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -164,6 +164,8 @@ struct animation_system r32 SecondsPerFrame; b32 TimelineShouldAdvance; + // Settings + bool Multithreaded; }; // NOTE(pjs): A Pattern is a named procedure which can be used as @@ -174,6 +176,7 @@ struct animation_pattern char* Name; s32 NameLength; animation_proc* Proc; + bool Multithreaded; }; struct animation_pattern_array @@ -250,9 +253,14 @@ Patterns_Create(gs_memory_arena* Arena, s32 CountMax) return Result; } -#define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)) - 1) +#define PATTERN_MULTITHREADED true +#define PATTERN_SINGLETHREADED false + +#define Patterns_PushPattern(array, proc, multithread) \ +Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)) - 1, (multithread)) + internal void -Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength) +Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength, bool Multithreaded) { Assert(Array->Count < Array->CountMax); @@ -260,6 +268,7 @@ Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char Pattern.Name = Name; Pattern.NameLength = NameLength; Pattern.Proc = Proc; + Pattern.Multithreaded = Multithreaded; Array->Values[Array->Count++] = Pattern; } @@ -604,6 +613,9 @@ AnimationSystem_Init(animation_system_desc Desc) Clear(&Result.ActiveFadeGroup.To); Result.ActiveFadeGroup.FadeElapsed = 0; + // Settings + Result.Multithreaded = true; + return Result; } diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index a22c417..f43184b 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -123,37 +123,45 @@ AnimationSystem_BeginRenderBlockToLedBuffer(animation_system* System, animation_ animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle); -#if MULTITHREAD_PATTERN_RENDERING - u32 JobsCount = 4; - u32 LedsPerJob = Buffer->LedCount / JobsCount; - - for (u32 i = 0; i < JobsCount; i++) + if (System->Multithreaded && Pattern.Multithreaded) { - gs_data Data = PushSizeToData(Context.ThreadContext.Transient, sizeof(render_anim_to_led_buffer_job_data)); - render_anim_to_led_buffer_job_data* JobData = (render_anim_to_led_buffer_job_data*)Data.Memory; - JobData->Pattern = Pattern; - JobData->Buffer = *Buffer; - JobData->BufferRange.First = LedsPerJob * i; - JobData->BufferRange.OnePastLast = LedsPerJob * (i + 1); - JobData->PatternArgs = PatternArgs; - JobData->SecondsIntoBlock = SecondsIntoBlock; + u32 JobsCount = 4; + u32 LedsPerJob = Buffer->LedCount / JobsCount; - Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, - (thread_proc*)AnimationSystem_RenderAnimationToLedBufferJob, - Data, - ConstString("Render Pattern To Buffer")); + for (u32 i = 0; i < JobsCount; i++) + { + gs_data Data = PushSizeToData(Context.ThreadContext.Transient, sizeof(render_anim_to_led_buffer_job_data)); + render_anim_to_led_buffer_job_data* JobData = (render_anim_to_led_buffer_job_data*)Data.Memory; + JobData->Pattern = Pattern; + JobData->Buffer = *Buffer; + JobData->BufferRange.First = LedsPerJob * i; + JobData->BufferRange.OnePastLast = LedsPerJob * (i + 1); + JobData->PatternArgs = PatternArgs; + JobData->SecondsIntoBlock = SecondsIntoBlock; + + Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, + (thread_proc*)AnimationSystem_RenderAnimationToLedBufferJob, + Data, + ConstString("Render Pattern To Buffer")); + } + } + else + { + led_buffer_range Range = {}; + Range.First = 0; + Range.OnePastLast = Buffer->LedCount; + + Pattern.Proc(Buffer, Range, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData); } -#else - Pattern.Proc(Buffer, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData); -#endif } internal void -AnimationSystem_EndRenderBlockToLedBuffer (context Context) +AnimationSystem_EndRenderBlockToLedBuffer (animation_system* System, context Context) { -#if MULTITHREAD_PATTERN_RENDERING - Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); -#endif + if (System->Multithreaded) + { + Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); + } } // NOTE(pjs): This mirrors animation_layer_frame to account @@ -212,7 +220,7 @@ RenderAnimationToLedBuffer (animation_system* System, AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context); } - AnimationSystem_EndRenderBlockToLedBuffer(Context); + AnimationSystem_EndRenderBlockToLedBuffer(System, Context); } // Blend together any layers that have a hot and next hot buffer diff --git a/src/app/engine/animation/foldhaus_animation_serializer.cpp b/src/app/engine/animation/foldhaus_animation_serializer.cpp index effbcda..9227328 100644 --- a/src/app/engine/animation/foldhaus_animation_serializer.cpp +++ b/src/app/engine/animation/foldhaus_animation_serializer.cpp @@ -182,9 +182,23 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array } } } - return Result; } +internal animation +AnimParser_Parse(gs_data File, gs_memory_arena* Arena, animation_pattern_array AnimPatterns) +{ + gs_string FileString = MakeString((char*)File.Memory, File.Size); + return AnimParser_Parse(FileString, Arena, AnimPatterns); +} +internal animation_handle +AnimationSystem_LoadAnimationFromFile(animation_system* System, animation_pattern_array AnimPatterns, context Context, gs_const_string FilePath) +{ + gs_file AnimFile = ReadEntireFile(Context.ThreadContext.FileHandler, FilePath); + animation NewAnim = AnimParser_Parse(AnimFile.Data, System->Storage, AnimPatterns); + NewAnim.FileInfo = AnimFile.FileInfo; + animation_handle NewAnimHandle = AnimationArray_Push(&System->Animations, NewAnim); + return NewAnimHandle; +} #define FOLDHAUS_ANIMATION_SERIALIZER_CPP #endif // FOLDHAUS_ANIMATION_SERIALIZER_CPP \ No newline at end of file diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index a0d46e6..ee59638 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -1187,6 +1187,41 @@ internal void Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { DEBUG_TRACK_FUNCTION; + + gs_random_series Random = InitRandomSeries(24601); + + r32 LightSpeedMin = 1; + r32 LightSpeedMax = 5; + + r32 LightHueMin = (ModR32(Time, 10) / 10) * 360; + r32 LightHueMax = ModR32((LightHueMin + 45), 360) ; + + s32 LightTailLength = 10; + for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) + { + v2_strip Strip = Assembly.Strips[StripIndex]; + + r32 LightHue = LerpR32(NextRandomUnilateral(&Random), + LightHueMin, + LightHueMax); + r32 LightStartHeight = NextRandomUnilateral(&Random); + r32 LightSpeed = LerpR32(NextRandomUnilateral(&Random), + LightSpeedMin, + LightSpeedMax); + r32 LightCurrentHeight = LightStartHeight + (LightSpeed * Time * 0.1f); + s32 StartIndex = (s32)(LightCurrentHeight * (r32)Strip.LedCount) % Strip.LedCount; + + for (s32 i = 0; i < LightTailLength; i++) + { + s32 StripLedIndex = StartIndex + i; + if (StripLedIndex >= (s32)Strip.LedCount) continue; + + u32 LedIndex = Strip.LedLUT[StripLedIndex]; + r32 PctTail = ((r32)i / (r32)LightTailLength); + v4 C = HSVToRGB(v4{LightHue, 1, 1, 1}) * PctTail; + Leds->Colors[LedIndex] = V4ToRGBPixel(C); + } + } } #define BLUMEN_PATTERNS_H diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 2a0e150..29bf6a1 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -43,6 +43,9 @@ MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE); u32 Index = Queue->WriteHead; + Assert(Index >= 0 && + Index < BLUMEN_MESSAGE_QUEUE_COUNT); + gs_data* Dest = Queue->Buffers + Index; CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size); Dest->Size = Msg.Size; @@ -50,7 +53,7 @@ MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) // NOTE(pjs): We increment write head at the end of writing so that // a reader thread doesn't pull the message off before we've finished // filling it out - Queue->WriteHead++; + Queue->WriteHead = (Queue->WriteHead + 1) % BLUMEN_MESSAGE_QUEUE_COUNT; return true; } @@ -116,28 +119,28 @@ BlumenLumen_LoadPatterns(app_state* State) } Patterns->Count = 0; - Patterns_PushPattern(Patterns, TestPatternOne); - Patterns_PushPattern(Patterns, TestPatternTwo); - Patterns_PushPattern(Patterns, TestPatternThree); - Patterns_PushPattern(Patterns, Pattern_AllGreen); - Patterns_PushPattern(Patterns, Pattern_HueShift); - Patterns_PushPattern(Patterns, Pattern_HueFade); - Patterns_PushPattern(Patterns, Pattern_Spots); - Patterns_PushPattern(Patterns, Pattern_LighthouseRainbow); - Patterns_PushPattern(Patterns, Pattern_SmoothGrowRainbow); - Patterns_PushPattern(Patterns, Pattern_GrowAndFade); - Patterns_PushPattern(Patterns, Pattern_ColorToWhite); - Patterns_PushPattern(Patterns, Pattern_Blue); - Patterns_PushPattern(Patterns, Pattern_Green); - Patterns_PushPattern(Patterns, Pattern_FlowerColors); - Patterns_PushPattern(Patterns, Pattern_FlowerColorToWhite); - Patterns_PushPattern(Patterns, Pattern_BasicFlowers); + Patterns_PushPattern(Patterns, TestPatternOne, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, TestPatternTwo, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, TestPatternThree, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_AllGreen, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_HueFade, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Spots, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_LighthouseRainbow, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_SmoothGrowRainbow, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_GrowAndFade, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_ColorToWhite, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Green, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_FlowerColors, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_FlowerColorToWhite, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED); // 15 - Patterns_PushPattern(Patterns, Pattern_Wavy); - Patterns_PushPattern(Patterns, Pattern_Patchy); - Patterns_PushPattern(Patterns, Pattern_Leafy); - Patterns_PushPattern(Patterns, Pattern_LeafyPatchy); - Patterns_PushPattern(Patterns, Pattern_WavyPatchy); + Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED); } internal v4 @@ -183,6 +186,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) gs_const_string SculpturePath = ConstString("data/test_blumen.fold"); LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); +#if 0 { // Animation PLAYGROUND animation Anim0 = {0}; Anim0.Name = PushStringF(&State->Permanent, 256, "test_anim_zero"); @@ -216,13 +220,19 @@ BlumenLumen_CustomInit(app_state* State, context Context) Anim2.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(16), 0); + Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(20), 0); BLState->AnimHandles[2] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); State->AnimationSystem.ActiveFadeGroup.From = BLState->AnimHandles[2]; - State->AnimationSystem.TimelineShouldAdvance = true; } // End Animation Playground +#endif + animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/demo_patterns.foldanim")); + State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; + State->AnimationSystem.TimelineShouldAdvance = true; for (u32 i = 0; i < FLOWER_COLORS_COUNT; i++) { From 0e7596cafc728c73fe78c97451ce43b65617cfa1 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Mon, 22 Mar 2021 23:12:55 -0700 Subject: [PATCH 019/151] Implemented masking the inner strips on blumen --- src/app/editor/interface.h | 28 ++++++--- .../foldhaus_panel_animation_timeline.h | 49 +++++++++------ src/app/engine/animation/foldhaus_animation.h | 32 ++++++++++ src/app/ss_blumen_lumen/blumen_lumen.cpp | 61 +++++++++++-------- 4 files changed, 117 insertions(+), 53 deletions(-) diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index f06c16a..4b4d13e 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -753,13 +753,13 @@ ui_PushOverlayLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction } static gs_string -ui_PushLayoutCategoryName(ui_interface* Interface, gs_string Category, gs_string Identifier) +ui_PushLayoutCategoryName(ui_interface* Interface, gs_string Category, u32 Value) { gs_string Result = PushStringF(Interface->PerFrameMemory, - Category.Length + Identifier.Length, - "%S%S", + Category.Length + 25, + "%S%d", Category.ConstString, - Identifier.ConstString); + Value); return Result; } @@ -782,9 +782,9 @@ ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir return Result; } static ui_widget* -ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Category, gs_string Identifier) +ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Category, u32 Value) { - gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Identifier); + gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Value); return ui_PushLayout(Interface, Bounds, FillDir, Name); } @@ -854,7 +854,16 @@ ui_PopLayout(ui_interface* Interface, gs_string LayoutName) // NOTE(pjs): If this isn't true then a layout was opened without being closed // Go check for ui_PushLayout, ui_BeginDropdown, ui_BeginRow, etc that don't have // a corresponding ui_Pop/ui_End* - Assert(StringsEqual(Layout->String, LayoutName)); + // + // We use StringsEqualUpToLength here becuase its possible that + // the current layout used the Category + Identifier method + // for generating Layout->String. And if Identifier was a string + // that was edited within the scope of this layout, then + // Layout->String and LayoutName will no longer match. + // + // This is a compromise that will at least let us know if + // we aren't popping all our layouts correctly. + Assert(StringsEqualUpToLength(Layout->String, LayoutName, LayoutName.Length)); ui_ExpandToFitChildren(Layout); @@ -869,9 +878,9 @@ ui_PopLayout(ui_interface* Interface, gs_string LayoutName) } } static void -ui_PopLayout(ui_interface* Interface, gs_string Category, gs_string Identifier) +ui_PopLayout(ui_interface* Interface, gs_string Category, u32 Value) { - gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Identifier); + gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Value); ui_PopLayout(Interface, Name); } @@ -1073,6 +1082,7 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds) else { OutChar(&State->EditString, Interface->TempInputString.Str[i]); + Interface->CursorPosition += 1; } } } diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index b0b7dcc..d319a0e 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -14,7 +14,7 @@ struct animation_timeline_state frame_range VisibleRange; handle SelectedBlockHandle; animation_handle EditingAnimationHandle; - u32 SelectedAnimationLayer; + s32 SelectedAnimationLayer; animation_handle NextActiveAnim; }; @@ -265,7 +265,8 @@ AnimationTimeline_AddAnimationBlockCommand(animation_timeline_state* TimelineSta if ((EndFrame - StartFrame) > 0) { animation_pattern_handle PatternHandle = Patterns_IndexToHandle(0); - u32 Layer = TimelineState->SelectedAnimationLayer; + s32 Layer = TimelineState->SelectedAnimationLayer; + Assert(Layer >= 0); handle NewBlockHandle = Animation_AddBlock(ActiveAnim, StartFrame, EndFrame, PatternHandle, Layer); @@ -638,7 +639,7 @@ LayerList_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, if (ActiveAnim) { v2 LayerTextPos = {}; - for (u32 i = 0; i < ActiveAnim->Layers.Count; i++) + for (s32 i = 0; i < (s32)ActiveAnim->Layers.Count; i++) { anim_layer* Layer = ActiveAnim->Layers.Values + i; @@ -771,8 +772,9 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn { animation_system* AnimSystem = &State->AnimationSystem; + animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From; ui_interface* Interface = &State->Interface; - ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout"), ActiveAnim->Name); + ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout"), ActiveAnimHandle.Index); ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBG); @@ -803,18 +805,22 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn { if (ui_Button(Interface, MakeString("New"))) { - animation NewAnim = {}; - NewAnim.Name = PushString(State->AnimationSystem.Storage, 256); + animation_desc Desc = {}; + Desc.NameSize = 256; + Desc.LayersCount = 8; + Desc.BlocksCount = 8; + Desc.MinFrames = 0; + Desc.MaxFrames = SecondsToFrames(15, State->AnimationSystem); + animation NewAnim = Animation_Create(Desc, &State->AnimationSystem); animation_handle NewAnimHandle = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); - State->AnimationSystem.ActiveFadeGroup.From = NewAnimHandle; + AnimationTimeline_SetActiveAnimation(NewAnimHandle, TimelineState); } if (ActiveAnim && ui_Button(Interface, MakeString("Save"))) { // Save Animation File // TODO(pjs): If you created the animation via the "new" button, there won't be a file attached. // need to use the file browser to create a file - animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From; animation ActiveAnimation = *AnimationArray_GetSafe(State->AnimationSystem.Animations, ActiveAnimHandle); if (!ActiveAnimation.FileInfo.Path.Str) @@ -850,22 +856,26 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn ui_Label(Interface, MakeString("Layer")); - u32 LayerIndex = TimelineState->SelectedAnimationLayer; - anim_layer* SelectedLayer = ActiveAnim->Layers.Values + LayerIndex; - - ui_TextEntry(Interface, MakeString("Layer Name"), &SelectedLayer->Name); - gs_string BlendStr = BlendModeStrings[SelectedLayer->BlendMode]; - if (ui_BeginLabeledDropdown(Interface, MakeString("Blend Mode"), BlendStr)) + s32 LayerIndex = TimelineState->SelectedAnimationLayer; + anim_layer* SelectedLayer = 0; + if (LayerIndex >= 0) { - for (u32 i = 0; i < BlendMode_Count; i++) + SelectedLayer = ActiveAnim->Layers.Values + LayerIndex; + + ui_TextEntry(Interface, MakeString("Layer Name"), &SelectedLayer->Name); + gs_string BlendStr = BlendModeStrings[SelectedLayer->BlendMode]; + if (ui_BeginLabeledDropdown(Interface, MakeString("Blend Mode"), BlendStr)) { - if (ui_Button(Interface, BlendModeStrings[i])) + for (u32 i = 0; i < BlendMode_Count; i++) { - SelectedLayer->BlendMode = (blend_mode)i; + if (ui_Button(Interface, BlendModeStrings[i])) + { + SelectedLayer->BlendMode = (blend_mode)i; + } } } + ui_EndLabeledDropdown(Interface); } - ui_EndLabeledDropdown(Interface); ui_Label(Interface, MakeString("Pattern")); @@ -896,7 +906,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn AnimationTimeline_AddAnimationBlockCommand(TimelineState, State, Context); } } - ui_PopLayout(Interface, MakeString("AnimInfo Layout"), ActiveAnim->Name); + ui_PopLayout(Interface, MakeString("AnimInfo Layout")); } internal void @@ -948,6 +958,7 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* { State->AnimationSystem.ActiveFadeGroup.From = TimelineState->NextActiveAnim; TimelineState->EditingAnimationHandle = TimelineState->NextActiveAnim; + TimelineState->SelectedAnimationLayer = -1; } } diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 12281fe..91204c7 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -419,6 +419,38 @@ AnimationArray_GetSafe(animation_array Array, animation_handle Handle) // // Animation +typedef struct animation_desc +{ + u32 NameSize; + char* Name; + + u32 LayersCount; + u32 BlocksCount; + + u32 MinFrames; + u32 MaxFrames; +} animation_desc; + +internal animation +Animation_Create(animation_desc Desc, animation_system* System) +{ + animation Result = {}; + u32 NameLen = Desc.NameSize; + if (Desc.Name) + { + NameLen = Max(CStringLength(Desc.Name), NameLen); + Result.Name = PushStringF(System->Storage, NameLen, "%s", Desc.Name); + } else { + Result.Name = PushStringF(System->Storage, NameLen, "[New Animation]"); + } + + Result.Layers = AnimLayerArray_Create(System->Storage, Desc.LayersCount); + Result.Blocks_ = AnimBlockArray_Create(System->Storage, Desc.BlocksCount); + Result.PlayableRange.Min = Desc.MinFrames; + Result.PlayableRange.Max = Desc.MaxFrames; + return Result; +} + internal handle Animation_AddBlock(animation* Animation, u32 StartFrame, s32 EndFrame, animation_pattern_handle AnimationProcHandle, u32 LayerIndex) { diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 29bf6a1..7361fd4 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -188,40 +188,32 @@ BlumenLumen_CustomInit(app_state* State, context Context) #if 0 { // Animation PLAYGROUND - animation Anim0 = {0}; - Anim0.Name = PushStringF(&State->Permanent, 256, "test_anim_zero"); - Anim0.Layers = AnimLayerArray_Create(State->AnimationSystem.Storage, 8); - Anim0.Blocks_ = AnimBlockArray_Create(State->AnimationSystem.Storage, 8); - Anim0.PlayableRange.Min = 0; - Anim0.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); + animation_desc Desc = {}; + Desc.NameSize = 256; + Desc.LayersCount = 8; + Desc.BlocksCount = 8; + Desc.MinFrames = 0; + Desc.MaxFrames = SecondsToFrames(15, State->AnimationSystem); + + animation_desc Desc0 = Desc; + Desc.Name = "test_anim_zero"; + animation Anim0 = Animation_Create(Desc0, &State->AnimationSystem); Animation_AddLayer(&Anim0, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim0, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(15), 0); - BLState->AnimHandles[0] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim0); - animation Anim1 = {0}; - Anim1.Name = PushStringF(&State->Permanent, 256, "test_anim_one"); - Anim1.Layers = AnimLayerArray_Create(State->AnimationSystem.Storage, 8); - Anim1.Blocks_ = AnimBlockArray_Create(State->AnimationSystem.Storage, 8); - Anim1.PlayableRange.Min = 0; - Anim1.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); + animation_desc Desc1 = Desc; + Desc1.Name = "test_anim_one"; + animation Anim1 = Animation_Create(Desc1, &State->AnimationSystem); Animation_AddLayer(&Anim1, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim1, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(12), 0); - BLState->AnimHandles[1] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim1); - animation Anim2 = {0}; - Anim2.Name = PushStringF(&State->Permanent, 256, "i_love_you"); - Anim2.Layers = AnimLayerArray_Create(State->AnimationSystem.Storage, 8); - Anim2.Blocks_ = AnimBlockArray_Create(State->AnimationSystem.Storage, 8); - Anim2.PlayableRange.Min = 0; - Anim2.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); + animation_desc Desc2 = Desc; + Desc2.Name = "i_love_you"; + animation Anim2 = Animation_Create(Desc2, &State->AnimationSystem);; Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(20), 0); - BLState->AnimHandles[2] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); State->AnimationSystem.ActiveFadeGroup.From = BLState->AnimHandles[2]; @@ -417,6 +409,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } // Dim the leds based on temp data +#define DIM_LED_BRIGHTNESS 1 #if DIM_LED_BRIGHTNESS for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) { @@ -430,7 +423,25 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } - // TODO(pjs): dim stem to 50% + // TODO(PS): This should really only happen if we think the + // flower _might_ be open + for (u32 a = 0; a < State->Assemblies.Count; a++) + { + assembly Assembly = State->Assemblies.Values[a]; + led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; + + led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient); + for (u32 s = 0; s < TopStrips.Count; s++) + { + u32 SIndex = TopStrips.StripIndices[s]; + v2_strip Strip = Assembly.Strips[SIndex]; + for (u32 l = 0; l < Strip.LedCount; l++) + { + u32 LIndex = Strip.LedLUT[l]; + Buffer.Colors[LIndex] = {0}; + } + } + } #endif // Send Status Packet From 743181fe9bb3d0655f357ac3a2e1706388d2efe1 Mon Sep 17 00:00:00 2001 From: PS Date: Wed, 24 Mar 2021 19:09:26 -0700 Subject: [PATCH 020/151] Built & tested gs_csv.h --- src/app/platform_win32/win32_foldhaus.cpp | 30 ++- src/gs_libs/gs_csv.h | 214 ++++++++++++++++++++++ src/tests/sanity_tests.cpp | 24 +++ 3 files changed, 262 insertions(+), 6 deletions(-) create mode 100644 src/gs_libs/gs_csv.h diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 7edec1e..452405e 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -507,7 +507,7 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) { OutputDebugStringA("Setting Working Directory\n"); OutputDebugStringA(WorkingDirectory.Str); - + OutputDebugStringA("\n"); Result = SetCurrentDirectory(WorkingDirectory.Str); if (!Result) { @@ -525,6 +525,8 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) #include "../../gs_libs/gs_path.h" +#include "../../gs_libs/gs_csv.h" + int WINAPI WinMain ( HINSTANCE HInstance, @@ -541,13 +543,29 @@ WinMain ( ThreadContext.Allocator.Debug = &AllocDebug; - gs_file_info A = GetFileInfo(ThreadContext.FileHandler, ConstString("C:\\projects\\Lumenarium")); - - gs_file_info B = GetFileInfo(ThreadContext.FileHandler, ConstString("C:\\projects\\Lumenarium\\")); - - if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; + + gs_file TestFile = ReadEntireFile(ThreadContext.FileHandler, ConstString("data/flower_codes.tsv")); + gs_const_string TestFileStr = {}; + TestFileStr.Str = (char*)TestFile.Memory; + TestFileStr.Length = TestFile.Size; + gscsv_sheet Sheet = CSV_Parse(TestFileStr, { '\t' }, ThreadContext.Transient); + + gs_string Out = PushString(ThreadContext.Transient, TestFile.Size * 2); + + for (u64 y = 0; y < Sheet.RowCount; y++) + { + for (u64 x = 0; x < Sheet.ColumnCount; x++) + { + gs_const_string Cell = CSVSheet_GetCell(Sheet, x, y); + AppendPrintF(&Out, "%S\t", Cell); + } + AppendPrintF(&Out, "\n"); + } + NullTerminate(&Out); + OutputDebugStringA(Out.Str); + MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); Win32UpdateWindowDimension(&MainWindow); diff --git a/src/gs_libs/gs_csv.h b/src/gs_libs/gs_csv.h new file mode 100644 index 0000000..5b8c965 --- /dev/null +++ b/src/gs_libs/gs_csv.h @@ -0,0 +1,214 @@ +/* date = March 24th 2021 5:53 pm */ + +#ifndef GS_CSV_H +#define GS_CSV_H + +struct gscsv_cell +{ + gs_const_string Value; +}; + +struct gscsv_row +{ + u64* CellIndices; +}; + +struct gscsv_sheet +{ + char SeparatorChar; + gscsv_cell* Cells; + gscsv_row* Rows; + u64 RowCount; + u64 ColumnCount; +}; + +struct gscsv_sheet_desc +{ + char SeparatorChar; +}; + +struct gscsv_parser +{ + gs_const_string Str; + u64 At; + u64 Line; +}; + +internal void +CSVParser_Reset(gscsv_parser* Parser) +{ + Parser->At = 0; +} + +internal char +CSVParser_CharAt(gscsv_parser Parser) +{ + char Result = Parser.Str.Str[Parser.At]; + return Result; +} + +internal bool +CSVParser_CanAdvance(gscsv_parser Parser) { + bool Result = Parser.At < Parser.Str.Length; + return Result; +} + +internal void +CSVParser_Advance(gscsv_parser* Parser) +{ + if (CSVParser_CanAdvance(*Parser)) { + if (IsNewline(CSVParser_CharAt(*Parser))) + { + Parser->Line += 1; + } + Parser->At += 1; + } +} + +internal void +CSVParser_AdvancePastChar(gscsv_parser* Parser, char Char) +{ + while (CSVParser_CanAdvance(*Parser) && CSVParser_CharAt(*Parser) != Char) + { + CSVParser_Advance(Parser); + } + + if (CSVParser_CanAdvance(*Parser)) + { + Assert(CSVParser_CharAt(*Parser) == Char); + CSVParser_Advance(Parser); + } +} + +internal u64 +CSVParser_AdvancePastSeparatorOrNewline(gscsv_parser* Parser, char SeparatorChar) +{ + u64 PointBeforeSeparator = 0; + + while (CSVParser_CanAdvance(*Parser) && + !(CSVParser_CharAt(*Parser) == SeparatorChar || + IsNewline(CSVParser_CharAt(*Parser)))) + { + CSVParser_Advance(Parser); + } + + PointBeforeSeparator = Parser->At; + + if (CSVParser_CanAdvance(*Parser)) + { + while(IsNewline(CSVParser_CharAt(*Parser))) { + CSVParser_Advance(Parser); + } + if (CSVParser_CharAt(*Parser) == SeparatorChar) + { + CSVParser_Advance(Parser); + } + } + + return PointBeforeSeparator; +} + +internal gscsv_cell +CSVCell_Init(gs_const_string File, u64 CellStart, u64 CellEnd) +{ + gscsv_cell Result = {}; + Result.Value = Substring(File, CellStart, CellEnd); + return Result; +} + +internal void +CSVRow_PushCell(gscsv_row* Row, u64 CellIndex, u64 ColumnIndex) +{ + Row->CellIndices[ColumnIndex] = CellIndex; +} + +internal gscsv_sheet +CSV_Parse(gs_const_string File, gscsv_sheet_desc Desc, gs_memory_arena* Arena) +{ + gscsv_sheet Result = {}; + + gscsv_parser Parser = {}; + Parser.Str = File; + Parser.At = 0; + Parser.Line = 0; + + // Count Tabs in first line + u64 Columns = 0; + while (CSVParser_CanAdvance(Parser) && !IsNewline(CSVParser_CharAt(Parser))) + { + char At = CSVParser_CharAt(Parser); + if (At == Desc.SeparatorChar) { + Columns += 1; + } + CSVParser_Advance(&Parser); + } + // NOTE(PS): Add one on the end because the last column won't end in a SeparatorChar, + // it ends in a newline + Columns += 1; + CSVParser_Reset(&Parser); + + // Count New Lines + u64 Rows = 0; + while (CSVParser_CanAdvance(Parser)) + { + char At = CSVParser_CharAt(Parser); + if (IsNewline(At)) + { + Rows++; + while (CSVParser_CanAdvance(Parser) && IsNewline(CSVParser_CharAt(Parser))) + { + CSVParser_Advance(&Parser); + } + } else { + CSVParser_Advance(&Parser); + } + } + // NOTE(PS): Adding a row becuase the last row will just end in the EOF + Rows += 1; + CSVParser_Reset(&Parser); + + // Allocate Result + Result.SeparatorChar = Desc.SeparatorChar; + Result.RowCount = Rows; + Result.ColumnCount = Columns; + Result.Cells = PushArray(Arena, gscsv_cell, Result.RowCount * Result.ColumnCount); + Result.Rows = PushArray(Arena, gscsv_row, Result.RowCount); + + // for rows, parse row + // for cells parse cells + for (u64 r = 0; r < Result.RowCount; r++) + { + u64 RowIndex = r; + gscsv_row* Row = Result.Rows + RowIndex; + Row->CellIndices = PushArray(Arena, u64, Result.ColumnCount); + + for (u64 c = 0; c < Result.ColumnCount; c++) + { + u64 CellIndex = (r * Result.ColumnCount) + c; + u64 CellStart = Parser.At; + u64 CellEnd = CSVParser_AdvancePastSeparatorOrNewline(&Parser, Desc.SeparatorChar); + + Result.Cells[CellIndex] = CSVCell_Init(Parser.Str, CellStart, CellEnd); + CSVRow_PushCell(Row, CellIndex, c); + } + } + + return Result; +} + +internal gs_const_string +CSVSheet_GetCell(gscsv_sheet Sheet, u64 Column, u64 Row) +{ + gs_const_string Result = {}; + + if (Sheet.RowCount > Row && Sheet.ColumnCount > Column) + { + u64 CellIndex = (Row * Sheet.ColumnCount) + Column; + Result = Sheet.Cells[CellIndex].Value; + } + + return Result; +} + + +#endif //GS_CSV_H diff --git a/src/tests/sanity_tests.cpp b/src/tests/sanity_tests.cpp index 437fc4d..cd4576e 100644 --- a/src/tests/sanity_tests.cpp +++ b/src/tests/sanity_tests.cpp @@ -11,6 +11,7 @@ #include "../gs_libs/gs_tests.h" #include "../gs_libs/gs_path.h" +#include "../gs_libs/gs_csv.h" gs_memory_arena Scratch = {}; void* Alloc(u64 Size, u64* ResultSize) { *ResultSize = Size; return malloc(Size); } @@ -29,6 +30,11 @@ bool PathTest (char* In, char* Out) { return StringsEqual(SanitizePath(ConstString(In), &Scratch), ConstString(Out)); } +global char* SampleCSV = R"FOO(Flower Primary Hue (0-365) Secondary Hue (0-365) Tertiary Hue (0-365) Homonyms +Flower A 55 32 128 foo, bar, blah, baz, whatever +Flower B 123 344 32 foo, bar, blah, baz, whatever +Flower C 55 32 128 foo, bar, blah, baz, whatever)FOO"; + int main (int ArgCount, char** Args) { Scratch = CreateMemoryArena(CreateAllocator(Alloc, Free), "Scratch"); @@ -99,6 +105,24 @@ int main (int ArgCount, char** Args) TestResult(PathTest("C:\\hello\\world\\.\\test", "C:\\hello\\world\\test")); } + Test("gs_csv.h") + { + gs_const_string TestCSV = ConstString(SampleCSV); + gscsv_sheet Sheet = CSV_Parse(TestCSV, { '\t' }, &Scratch); + + gs_const_string Cell = CSVSheet_GetCell(Sheet, 0, 0); + TestResult(StringsEqual(Cell, ConstString("Flower"))); + + Cell = CSVSheet_GetCell(Sheet, 1, 1); + TestResult(StringsEqual(Cell, ConstString("55"))); + + Cell = CSVSheet_GetCell(Sheet, 4, 1); + TestResult(StringsEqual(Cell, ConstString("foo, bar, blah, baz, whatever"))); + + Cell = CSVSheet_GetCell(Sheet, 4, 3); + TestResult(StringsEqual(Cell, ConstString("foo, bar, blah, baz, whatever"))); + } + return 0; } From 83707b10b9031c27a075da8bc0e50859a14bcc37 Mon Sep 17 00:00:00 2001 From: PS Date: Wed, 24 Mar 2021 19:45:11 -0700 Subject: [PATCH 021/151] Split the sculpture into 3 assemblies, and created a way for patterns to use assembly specific color palettes --- src/app/engine/assembly/foldhaus_assembly.cpp | 1 + src/app/engine/assembly/foldhaus_assembly.h | 1 + src/app/patterns/blumen_patterns.h | 14 ++++---- src/app/ss_blumen_lumen/blumen_lumen.cpp | 22 +++++++++--- src/app/ss_blumen_lumen/blumen_lumen.h | 2 ++ src/sculpture_gen/gen_blumen_lumen.cpp | 34 ++++++++++++++----- 6 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/app/engine/assembly/foldhaus_assembly.cpp b/src/app/engine/assembly/foldhaus_assembly.cpp index 1a8cb6f..ebbff73 100644 --- a/src/app/engine/assembly/foldhaus_assembly.cpp +++ b/src/app/engine/assembly/foldhaus_assembly.cpp @@ -26,6 +26,7 @@ AssemblyArray_Push(assembly_array* Array, assembly Assembly) Assert(Array->Count < Array->CountMax); u32 Index = Array->Count++; Array->Values[Index] = Assembly; + Array->Values[Index].AssemblyIndex = Index; return Index; } diff --git a/src/app/engine/assembly/foldhaus_assembly.h b/src/app/engine/assembly/foldhaus_assembly.h index 0744e8e..7f6ca8b 100644 --- a/src/app/engine/assembly/foldhaus_assembly.h +++ b/src/app/engine/assembly/foldhaus_assembly.h @@ -143,6 +143,7 @@ struct assembly { gs_memory_arena Arena; + u32 AssemblyIndex; gs_string Name; gs_string FilePath; diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index ee59638..84d51e5 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -577,7 +577,6 @@ v4 RGBToHSV(v4 In) r32 Min = Min(In.r, Min(In.g, In.b)); r32 Max = Max(In.r, Max(In.g, In.b)); - r32 V = Max; r32 Delta = Max - Min; r32 S = 0; @@ -601,9 +600,6 @@ v4 RGBToHSV(v4 In) H *= 60; // degrees if( H < 0 ) H += 360; - Assert(H); - //if ( isNaN(h) ) - //H = 0; Result = v4{H, S, V, 1}; } else @@ -1187,15 +1183,21 @@ internal void Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { DEBUG_TRACK_FUNCTION; - + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; gs_random_series Random = InitRandomSeries(24601); r32 LightSpeedMin = 1; r32 LightSpeedMax = 5; +#if 0 r32 LightHueMin = (ModR32(Time, 10) / 10) * 360; r32 LightHueMax = ModR32((LightHueMin + 45), 360) ; - +#else + v4 CenterColor = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + r32 CenterHue = RGBToHSV(CenterColor).x; + r32 LightHueMin = ModR32(CenterHue + 30, 360);; + r32 LightHueMax = ModR32(CenterHue - 30, 360) ; +#endif s32 LightTailLength = 10; for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) { diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 7361fd4..fd02a59 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -183,10 +183,19 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); #endif +#if 0 gs_const_string SculpturePath = ConstString("data/test_blumen.fold"); LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); +#else + gs_const_string SculpturePath0 = ConstString("data/ss_blumen_one.fold"); + gs_const_string SculpturePath1 = ConstString("data/ss_blumen_two.fold"); + gs_const_string SculpturePath2 = ConstString("data/ss_blumen_three.fold"); + LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath0, State->GlobalLog); + LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath1, State->GlobalLog); + LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath2, State->GlobalLog); +#endif -#if 0 +#if 1 { // Animation PLAYGROUND animation_desc Desc = {}; Desc.NameSize = 256; @@ -218,14 +227,15 @@ BlumenLumen_CustomInit(app_state* State, context Context) State->AnimationSystem.ActiveFadeGroup.From = BLState->AnimHandles[2]; } // End Animation Playground -#endif +#else animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/demo_patterns.foldanim")); - State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; - State->AnimationSystem.TimelineShouldAdvance = true; + State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; +#endif + State->AnimationSystem.TimelineShouldAdvance = true; for (u32 i = 0; i < FLOWER_COLORS_COUNT; i++) { //FlowerAColors[i] = TEMP_Saturate(FlowerAColors[i]); @@ -233,6 +243,10 @@ BlumenLumen_CustomInit(app_state* State, context Context) //FlowerCColors[i] = TEMP_Saturate(FlowerCColors[i]); } + BLState->AssemblyColors[0] = RedV4; + BLState->AssemblyColors[1] = GreenV4; + BLState->AssemblyColors[2] = BlueV4; + return Result; } diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 30b975a..fb5b238 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -169,6 +169,8 @@ struct blumen_lumen_state system_time LastStatusUpdateTime; system_time LastSendTime; + + v4 AssemblyColors[3]; }; diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index 7beea9d..4555311 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -175,13 +175,27 @@ int main(int ArgCount, char** Args) { gs_thread_context Ctx = Win32CreateThreadContext(); - gs_string OutputBuffer = PushString(Ctx.Transient, MB(4)); + gs_string OutputBuffer0 = PushString(Ctx.Transient, MB(4)); + gs_string OutputBuffer1 = PushString(Ctx.Transient, MB(4)); + gs_string OutputBuffer2 = PushString(Ctx.Transient, MB(4)); - WriteAssemblyUARTOpen(&OutputBuffer, - "Blumen Lumen - Silver Spring", + WriteAssemblyUARTOpen(&OutputBuffer0, + "Blumen Lumen - Silver Spring - 00", 100, v3{0, 0, 0}, - 63, + 21, + ""); + WriteAssemblyUARTOpen(&OutputBuffer1, + "Blumen Lumen - Silver Spring - 01", + 100, + v3{0, 0, 0}, + 21, + ""); + WriteAssemblyUARTOpen(&OutputBuffer2, + "Blumen Lumen - Silver Spring - 02", + 100, + v3{0, 0, 0}, + 21, ""); u32 StripCount = 0; @@ -196,7 +210,7 @@ int main(int ArgCount, char** Args) F0.StemChannels = StemChannels; F0.BloomOuterChannels = BloomOuterChannels; F0.BloomInnerChannels = BloomInnerChannels; - StripCount += BuildFlower(&OutputBuffer, F0); + StripCount += BuildFlower(&OutputBuffer0, F0); flower_desc F1 = {}; F1.Pos = v3{0, 0, 0}; @@ -205,7 +219,7 @@ int main(int ArgCount, char** Args) F1.StemChannels = StemChannels; F1.BloomInnerChannels = BloomInnerChannels; F1.BloomOuterChannels = BloomOuterChannels; - StripCount += BuildFlower(&OutputBuffer, F1); + StripCount += BuildFlower(&OutputBuffer1, F1); flower_desc F2 = {}; F2.Pos = v3{1, 0, 0}; @@ -214,9 +228,13 @@ int main(int ArgCount, char** Args) F2.StemChannels = StemChannels; F2.BloomInnerChannels = BloomInnerChannels; F2.BloomOuterChannels = BloomOuterChannels; - StripCount += BuildFlower(&OutputBuffer, F2); + StripCount += BuildFlower(&OutputBuffer2, F2); - printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str); + WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_one.fold"), StringToData(OutputBuffer0)); + WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_two.fold"), StringToData(OutputBuffer1)); + WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_three.fold"), StringToData(OutputBuffer2)); + + //printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str); //printf("%d\n", StripCount); From b5a3c4903abb22971ba10fb94458faf381c77729 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Thu, 25 Mar 2021 01:46:27 -0700 Subject: [PATCH 022/151] Debug interface for sculptures, lots of blumen updates. --- src/app/editor/interface.h | 16 ++ .../panels/foldhaus_panel_assembly_debug.h | 125 ++++---- src/app/engine/animation/foldhaus_animation.h | 37 +-- src/app/engine/user_space.cpp | 10 + src/app/engine/user_space.h | 4 + src/app/foldhaus_app.h | 1 + src/app/platform_win32/win32_foldhaus.cpp | 21 -- .../platform_win32/win32_foldhaus_serial.h | 1 + .../platform_win32/win32_foldhaus_socket.h | 41 ++- src/app/ss_blumen_lumen/blumen_lumen.cpp | 270 ++++++++++++------ src/app/ss_blumen_lumen/blumen_lumen.h | 54 +++- src/gs_libs/gs_types.cpp | 70 +++-- src/gs_libs/gs_types.h | 15 +- 13 files changed, 434 insertions(+), 231 deletions(-) diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index 4b4d13e..1c3dfaf 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -1444,6 +1444,22 @@ ui_Toggle(ui_interface* Interface, gs_string Text, bool Value) return Result; } +internal bool +ui_ToggleText(ui_interface* Interface, gs_string Text, bool Value) +{ + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + ui_eval_result Eval = ui_EvaluateWidget(Interface, Widget); + + bool Result = Eval.Clicked ? !Value : Value; + Widget->FillPercent = Result ? 1.0f : 0.0f; + return Result; +} + internal void ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows, u32 ElementCount) { diff --git a/src/app/editor/panels/foldhaus_panel_assembly_debug.h b/src/app/editor/panels/foldhaus_panel_assembly_debug.h index ee75876..7d7b122 100644 --- a/src/app/editor/panels/foldhaus_panel_assembly_debug.h +++ b/src/app/editor/panels/foldhaus_panel_assembly_debug.h @@ -43,70 +43,91 @@ AssemblyDebug_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren ui_interface* Interface = &State->Interface; ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("Assembly Debug Layout")); - InterfaceAssert(Interface->PerFrameMemory); - - gs_string OverrideStr = MakeString(OverrideTypeStrings[State->AssemblyDebugState.Override]); - if (ui_BeginLabeledDropdown(Interface, MakeString("Override"), OverrideStr)) + ui_BeginRow(Interface, 2); { - for (u32 i = 0; i < ADS_Override_Count; i++) + if (ui_Button(Interface, MakeString("Assembly"))) { - if (ui_Button(Interface, MakeString(OverrideTypeStrings[i]))) - { - State->AssemblyDebugState.Override = (override_type)i; - } + State->ShowingUserSpaceDebug = false; + } + + if (ui_Button(Interface, MakeString("User Space"))) + { + State->ShowingUserSpaceDebug = true; } } - ui_EndLabeledDropdown(Interface); - InterfaceAssert(Interface->PerFrameMemory); + ui_EndRow(Interface); - switch (State->AssemblyDebugState.Override) + if (State->ShowingUserSpaceDebug && State->UserSpaceDesc.CustomDebugUI) { - case ADS_Override_TagWhite: - case ADS_Override_TagStripWhite: + US_CustomDebugUI(&State->UserSpaceDesc, Panel, PanelBounds, RenderBuffer, + State, Context); + } + else + { + InterfaceAssert(Interface->PerFrameMemory); + + gs_string OverrideStr = MakeString(OverrideTypeStrings[State->AssemblyDebugState.Override]); + if (ui_BeginLabeledDropdown(Interface, MakeString("Override"), OverrideStr)) { - ui_LabeledTextEntry(Interface, MakeString("Tag Name"), &State->AssemblyDebugState.TagName); - ui_LabeledTextEntry(Interface, MakeString("Tag Value"), &State->AssemblyDebugState.TagValue); - - if (State->AssemblyDebugState.Override == ADS_Override_TagStripWhite) + for (u32 i = 0; i < ADS_Override_Count; i++) { + if (ui_Button(Interface, MakeString(OverrideTypeStrings[i]))) + { + State->AssemblyDebugState.Override = (override_type)i; + } + } + } + ui_EndLabeledDropdown(Interface); + InterfaceAssert(Interface->PerFrameMemory); + + switch (State->AssemblyDebugState.Override) + { + case ADS_Override_TagWhite: + case ADS_Override_TagStripWhite: + { + ui_LabeledTextEntry(Interface, MakeString("Tag Name"), &State->AssemblyDebugState.TagName); + ui_LabeledTextEntry(Interface, MakeString("Tag Value"), &State->AssemblyDebugState.TagValue); + + if (State->AssemblyDebugState.Override == ADS_Override_TagStripWhite) + { + State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly); + + State->AssemblyDebugState.TargetStrip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), State->AssemblyDebugState.TargetStrip); + } + }break; + + case ADS_Override_ChannelWhite: + { + u64 Board = 0; + u64 Strip = 0; + Board = ui_LabeledTextEntryU64(Interface, MakeString("Board"), Board); + Strip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), Strip); + + State->AssemblyDebugState.TargetChannel = FSC(Board, Strip); + }break; + + case ADS_Override_AllRed: + case ADS_Override_AllGreen: + case ADS_Override_AllBlue: + case ADS_Override_AllWhite: + { + State->AssemblyDebugState.Brightness = (u8)ui_LabeledRangeSlider(Interface, MakeString("Brightness"), (r32)State->AssemblyDebugState.Brightness, 0, 255); + }break; + + default: + { + InterfaceAssert(Interface->PerFrameMemory); + State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly); + InterfaceAssert(Interface->PerFrameMemory); + State->AssemblyDebugState.TargetStrip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), State->AssemblyDebugState.TargetStrip); - } - }break; - - case ADS_Override_ChannelWhite: - { - u64 Board = 0; - u64 Strip = 0; - Board = ui_LabeledTextEntryU64(Interface, MakeString("Board"), Board); - Strip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), Strip); - - State->AssemblyDebugState.TargetChannel = FSC(Board, Strip); - }break; - - case ADS_Override_AllRed: - case ADS_Override_AllGreen: - case ADS_Override_AllBlue: - case ADS_Override_AllWhite: - { - State->AssemblyDebugState.Brightness = (u8)ui_LabeledRangeSlider(Interface, MakeString("Brightness"), (r32)State->AssemblyDebugState.Brightness, 0, 255); - }break; - - default: - { - InterfaceAssert(Interface->PerFrameMemory); - - State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly); - - InterfaceAssert(Interface->PerFrameMemory); - - State->AssemblyDebugState.TargetStrip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), State->AssemblyDebugState.TargetStrip); - - InterfaceAssert(Interface->PerFrameMemory); - }break; + + InterfaceAssert(Interface->PerFrameMemory); + }break; + } } - ui_PopLayout(Interface, MakeString("Assembly Debug Layout")); } diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 91204c7..64ef95c 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -725,26 +725,29 @@ AnimationSystem_Update(animation_system* System, r32 DeltaTime) AnimationFadeGroup_Update(&System->ActiveFadeGroup, DeltaTime); animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - // TODO(Peter): Revisit this. This implies that the framerate of the animation system - // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure - System->CurrentFrame += 1; - - // Loop back to the beginning - if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) + if (ActiveAnim) { - switch (System->RepeatMode) + // TODO(Peter): Revisit this. This implies that the framerate of the animation system + // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure + System->CurrentFrame += 1; + + // Loop back to the beginning + if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) { - case AnimationRepeat_Single: + switch (System->RepeatMode) { - System->CurrentFrame = 0; - }break; - - case AnimationRepeat_Loop: - { - // TODO(pjs): - }break; - - InvalidDefaultCase; + case AnimationRepeat_Single: + { + System->CurrentFrame = 0; + }break; + + case AnimationRepeat_Loop: + { + // TODO(pjs): + }break; + + InvalidDefaultCase; + } } } } diff --git a/src/app/engine/user_space.cpp b/src/app/engine/user_space.cpp index 752435a..7e401df 100644 --- a/src/app/engine/user_space.cpp +++ b/src/app/engine/user_space.cpp @@ -32,6 +32,16 @@ US_CustomUpdate(user_space_desc* Desc, app_state* State, context* Context) } } +internal void +US_CustomDebugUI(user_space_desc* Desc, panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, + app_state* State, context Context) +{ + if (Desc->CustomDebugUI) + { + Desc->CustomDebugUI(Desc->UserData, Panel, PanelBounds, RenderBuffer, State, Context); + } +} + internal void US_CustomCleanup(user_space_desc* Desc, app_state* State, context Context) { diff --git a/src/app/engine/user_space.h b/src/app/engine/user_space.h index 8dc5f8a..5373fcb 100644 --- a/src/app/engine/user_space.h +++ b/src/app/engine/user_space.h @@ -14,6 +14,9 @@ typedef US_CUSTOM_INIT(us_custom_init_proc); #define US_CUSTOM_UPDATE(name) void name(gs_data UserData, app_state* State, context* Context) typedef US_CUSTOM_UPDATE(us_custom_update_proc); +#define US_CUSTOM_DEBUG_UI(name) void name(gs_data UserData, panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) +typedef US_CUSTOM_DEBUG_UI(us_custom_debug_ui); + #define US_CUSTOM_CLEANUP(name) void name(gs_data UserData, app_state* State, context Context) typedef US_CUSTOM_CLEANUP(us_custom_cleanup_proc); @@ -22,6 +25,7 @@ typedef struct user_space_desc us_load_patterns_proc* LoadPatterns; us_custom_init_proc* CustomInit; us_custom_update_proc* CustomUpdate; + us_custom_debug_ui* CustomDebugUI; us_custom_cleanup_proc* CustomCleanup; gs_data UserData; diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index f18ea3f..6e44c4d 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -72,6 +72,7 @@ struct app_state panel* HotPanel; user_space_desc UserSpaceDesc; + bool ShowingUserSpaceDebug; bool RunEditor; }; diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 28769ba..cc1020e 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -575,27 +575,6 @@ WinMain ( if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; - - gs_file TestFile = ReadEntireFile(ThreadContext.FileHandler, ConstString("data/flower_codes.tsv")); - gs_const_string TestFileStr = {}; - TestFileStr.Str = (char*)TestFile.Memory; - TestFileStr.Length = TestFile.Size; - gscsv_sheet Sheet = CSV_Parse(TestFileStr, { '\t' }, ThreadContext.Transient); - - gs_string Out = PushString(ThreadContext.Transient, TestFile.Size * 2); - - for (u64 y = 0; y < Sheet.RowCount; y++) - { - for (u64 x = 0; x < Sheet.ColumnCount; x++) - { - gs_const_string Cell = CSVSheet_GetCell(Sheet, x, y); - AppendPrintF(&Out, "%S\t", Cell); - } - AppendPrintF(&Out, "\n"); - } - NullTerminate(&Out); - OutputDebugStringA(Out.Str); - MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); Win32UpdateWindowDimension(&MainWindow); diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index a1244a9..23d8cdf 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -172,6 +172,7 @@ Win32SerialPort_Open(char* PortName, gs_memory_arena* Transient) s32 Error = GetLastError(); switch (Error) { + case ERROR_INVALID_FUNCTION: case ERROR_NO_SUCH_DEVICE: case ERROR_FILE_NOT_FOUND: { diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index 00bceec..2cb326a 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -132,7 +132,7 @@ Win32Socket_ConnectToAddress(char* Address, char* DefaultPort) } internal bool -Win32ConnectSocket(platform_socket* Socket) +Win32ConnectSocket(platform_socket_manager* Manager, platform_socket* Socket) { bool Result = false; @@ -156,7 +156,7 @@ Win32ConnectSocket(platform_socket* Socket) // If iMode == 0, blocking is enabled // if iMode != 0, non-blocking mode is enabled - u_long iMode = 1; + u_long iMode = 0; Error = ioctlsocket(SocketHandle, FIONBIO, &iMode); if (Error != NO_ERROR) { @@ -218,7 +218,7 @@ Win32ConnectSocket(platform_socket* Socket) } internal bool -Win32CloseSocket(platform_socket* Socket) +Win32CloseSocket(platform_socket_manager* Manager, platform_socket* Socket) { SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; closesocket(*Win32Sock); @@ -228,7 +228,7 @@ Win32CloseSocket(platform_socket* Socket) } internal bool -Win32SocketQueryStatus(platform_socket* Socket) +Win32SocketQueryStatus(platform_socket_manager* Manager, platform_socket* Socket) { SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; bool Result = (*Win32Sock != INVALID_SOCKET); @@ -236,7 +236,7 @@ Win32SocketQueryStatus(platform_socket* Socket) } internal u32 -Win32SocketPeek(platform_socket* Socket) +Win32SocketPeek(platform_socket_manager* Manager, platform_socket* Socket) { u32 Result = 0; s32 Flags = MSG_PEEK; @@ -257,16 +257,10 @@ Win32SocketPeek(platform_socket* Socket) { case WSAEWOULDBLOCK: case WSAENOTCONN: - { - }break; - case WSAECONNRESET: - { - }break; - case WSAECONNABORTED: { - Win32CloseSocket(Socket); + CloseSocket(Manager, Socket); }break; InvalidDefaultCase; @@ -276,7 +270,7 @@ Win32SocketPeek(platform_socket* Socket) } internal gs_data -Win32SocketReceive(platform_socket* Socket, gs_memory_arena* Storage) +Win32SocketReceive(platform_socket_manager* Manager, platform_socket* Socket, gs_memory_arena* Storage) { // TODO(pjs): Test this first code path when you have data running - it should // get the actual size of the data packet being sent @@ -309,7 +303,7 @@ Win32SocketReceive(platform_socket* Socket, gs_memory_arena* Storage) internal s32 -Win32SocketSend(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags) +Win32SocketSend(platform_socket_manager* Manager, platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags) { SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; @@ -327,26 +321,23 @@ Win32SocketSend(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s3 switch (Error) { case WSAECONNABORTED: - { - }break; - case WSAENETUNREACH: - { - }break; - case WSAECONNRESET: - { - - }break; - case WSAENOTCONN: { + if (CloseSocket(Manager, Socket)) + { + Error = 0; + } }break; InvalidDefaultCase; } - OutputDebugString("Error\n"); + if (Error) + { + OutputDebugString("Error\n"); + } } else { diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index fd02a59..3c11b10 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -5,24 +5,50 @@ // #ifndef BLUMEN_LUMEN_CPP -internal bool -MessageQueue_CanRead(blumen_network_msg_queue* Queue) +internal void +DEBUG_AppendText(gs_string Str, gs_thread_context Ctx) { - bool Result = (Queue->ReadHead != Queue->WriteHead); - return Result; + gs_const_string DebugPath = ConstString("data/debug_motor_changes.txt"); + gs_file DebugFile = ReadEntireFile(Ctx.FileHandler, + DebugPath); + gs_string NewString = PushString(Ctx.Transient, DebugFile.Size + Str.Size + 16); + if (DebugFile.Size > 0) + { + PrintF(&NewString, "%.*s\nENTRY:\n", DebugFile.Size, (char*)DebugFile.Memory); + } + AppendPrintF(&NewString, "%S\n", Str.ConstString); + NullTerminate(&NewString); + + if (!WriteEntireFile(Ctx.FileHandler, DebugPath, StringToData(NewString))) + { + InvalidCodePath; + } } -internal gs_data -MessageQueue_Read(blumen_network_msg_queue* Queue) +internal void +DEBUG_SentMotorCommand(motor_packet Packet, gs_thread_context Ctx) { - gs_data Result = {}; - u32 ReadIndex = Queue->ReadHead++; - if (Queue->ReadHead >= BLUMEN_MESSAGE_QUEUE_COUNT) - { - Queue->ReadHead = 0; - } - Result = Queue->Buffers[ReadIndex]; - return Result; + gs_string Str = PushStringF(Ctx.Transient, 256, "Motor Command Sent\nRequested Positions: %d %d %d\n", + Packet.FlowerPositions[0], + Packet.FlowerPositions[1], + Packet.FlowerPositions[2]); + DEBUG_AppendText(Str, Ctx); + + NullTerminate(&Str); + OutputDebugStringA(Str.Str); +} + +internal void +DEBUG_ReceivedMotorPositions(motor_packet Packet, gs_thread_context Ctx) +{ + gs_string Str = PushStringF(Ctx.Transient, 256, "Motor Status Received\nCurrent Positions: %d %d %d\n", + Packet.FlowerPositions[0], + Packet.FlowerPositions[1], + Packet.FlowerPositions[2]); + DEBUG_AppendText(Str, Ctx); + + NullTerminate(&Str); + OutputDebugStringA(Str.Str); } // KB(1) is just bigger than any packet we send. Good for now @@ -37,6 +63,14 @@ MessageQueue_Init(blumen_network_msg_queue* Queue, gs_memory_arena* Arena) } } +internal bool +MessageQueue_CanWrite(blumen_network_msg_queue Queue) +{ + bool Result = ((Queue.WriteHead >= Queue.ReadHead) || + (Queue.WriteHead < Queue.ReadHead)); + return Result; +} + internal bool MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) { @@ -58,13 +92,34 @@ MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) } internal bool -MessageQueue_CanWrite(blumen_network_msg_queue Queue) +MessageQueue_CanRead(blumen_network_msg_queue Queue) { - bool Result = ((Queue.WriteHead >= Queue.ReadHead) || - (Queue.WriteHead < Queue.ReadHead)); + bool Result = (Queue.ReadHead != Queue.WriteHead); return Result; } +internal gs_data +MessageQueue_Read(blumen_network_msg_queue* Queue) +{ + gs_data Result = {}; + u32 ReadIndex = Queue->ReadHead++; + if (Queue->ReadHead >= BLUMEN_MESSAGE_QUEUE_COUNT) + { + Queue->ReadHead = 0; + } + Result = Queue->Buffers[ReadIndex]; + return Result; +} + +internal void +MessageQueue_Clear(blumen_network_msg_queue* Queue) +{ + while (MessageQueue_CanRead(*Queue)) + { + MessageQueue_Read(Queue); + } +} + internal void BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) { @@ -81,32 +136,51 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) u32 WeathermanIPV4 = (u32)UpackB4(WeathermanIPAddr); u32 WeathermanPort = 20185; + platform_socket_handle_ ListenSocket = {0}; + while (*Data->Running) { - if (SocketQueryStatus(Data->SocketManager, Data->ListenSocket)) + if (!SocketQueryStatus(Data->SocketManager, ListenSocket)) { - if (SocketPeek(Data->SocketManager, Data->ListenSocket)) + if (SocketHandleIsValid(ListenSocket)) + { + OutputDebugStringA("Disconnected from Python Server\n"); + CloseSocket(Data->SocketManager, ListenSocket); + } + ListenSocket = CreateSocket(Data->SocketManager, "127.0.0.1", "20185"); + if (ListenSocket.Index != 0) + { + OutputDebugStringA("Connected to Python Server\n"); + } + } + + if (SocketQueryStatus(Data->SocketManager, ListenSocket)) + { + if (SocketPeek(Data->SocketManager, ListenSocket)) { // TODO(pjs): Make this a peek operation - Msg = SocketRecieve(Data->SocketManager, Data->ListenSocket, Ctx->Transient); + Msg = SocketRecieve(Data->SocketManager, ListenSocket, Ctx->Transient); if (Msg.Size > 0) { MessageQueue_Write(Data->IncomingMsgQueue, Msg); } } - while (MessageQueue_CanRead(Data->OutgoingMsgQueue)) + while (MessageQueue_CanRead(*Data->OutgoingMsgQueue)) { Msg = MessageQueue_Read(Data->OutgoingMsgQueue); + u32 Address = WeathermanIPV4; u32 Port = WeathermanPort; s32 Flags = 0; - SocketSend(Data->SocketManager, Data->ListenSocket, Address, Port, Msg, Flags); + SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags); } } + + MessageQueue_Clear(Data->OutgoingMsgQueue); } - CloseSocket(Data->SocketManager, Data->ListenSocket); + CloseSocket(Data->SocketManager, ListenSocket); } internal void @@ -143,19 +217,6 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED); } -internal v4 -TEMP_Saturate(v4 P) -{ - v4 CRGB = P; - v4 CHSV = RGBToHSV(CRGB); - if (CHSV.g > .3f) - { - CHSV.g = 1; - CRGB = HSVToRGB(CHSV); - } - return CRGB; -} - internal gs_data BlumenLumen_CustomInit(app_state* State, context Context) { @@ -177,9 +238,8 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->MicListenJobData.SocketManager = Context.SocketManager; BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue; BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; - BLState->MicListenJobData.ListenSocket = CreateSocket(Context.SocketManager, "127.0.0.1", "20185"); -#if 0 +#if 1 BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); #endif @@ -236,12 +296,6 @@ BlumenLumen_CustomInit(app_state* State, context Context) State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; #endif State->AnimationSystem.TimelineShouldAdvance = true; - for (u32 i = 0; i < FLOWER_COLORS_COUNT; i++) - { - //FlowerAColors[i] = TEMP_Saturate(FlowerAColors[i]); - //FlowerBColors[i] = TEMP_Saturate(FlowerBColors[i]); - //FlowerCColors[i] = TEMP_Saturate(FlowerCColors[i]); - } BLState->AssemblyColors[0] = RedV4; BLState->AssemblyColors[1] = GreenV4; @@ -271,7 +325,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } #endif - while (MessageQueue_CanRead(&BLState->IncomingMsgQueue)) + while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) { gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); @@ -291,29 +345,12 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, animation_handle{(s32)PhraseIndex}, 3.0f); - OutputDebugStringA("\nReceived Pattern Packet\n"); - { - // DEBUG CODE - u8 MotorState = BLState->LastKnownMotorState.FlowerPositions[0]; - if (MotorState == 2) { - OutputDebugStringA("Sending 1\n"); - MotorState = 1; - } - else - { - OutputDebugStringA("Sending 1\n"); - MotorState = 2; - } - - blumen_packet MPacket = {}; - MPacket.Type = PacketType_MotorState; - MPacket.MotorPacket.FlowerPositions[0] = MotorState; - MPacket.MotorPacket.FlowerPositions[1] = MotorState; - MPacket.MotorPacket.FlowerPositions[2] = MotorState; - MotorCommand = MPacket; - SendMotorCommand = true; - } + gs_string T = PushStringF(State->Transient, 256, + "Received Animation Packet:\nAnimationIndex: %d\n", + PhraseIndex); + NullTerminate(&T); + OutputDebugStringA(T.Str); } } }break; @@ -328,27 +365,22 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Motor.Temperature = (T[0] << 8 | T[1] << 0); + motor_packet LastPos = BLState->LastKnownMotorState; + if (LastPos.FlowerPositions[0] != Motor.Pos.FlowerPositions[0] || + LastPos.FlowerPositions[1] != Motor.Pos.FlowerPositions[1] || + LastPos.FlowerPositions[2] != Motor.Pos.FlowerPositions[2]) + { + DEBUG_ReceivedMotorPositions(Motor.Pos, Context->ThreadContext); + } BLState->LastKnownMotorState = Motor.Pos; - gs_string Temp = PushStringF(State->Transient, 256, "\nReceived Motor States: \n\tPos: %d %d %d\n\tErr: %d %d %d\n\tTemp: %d\n\n", - Motor.Pos.FlowerPositions[0], - Motor.Pos.FlowerPositions[1], - Motor.Pos.FlowerPositions[2], - Motor.MotorStatus[0], - Motor.MotorStatus[1], - Motor.MotorStatus[2], - (u32)Motor.Temperature - ); - NullTerminate(&Temp); - - OutputDebugStringA(Temp.Str); }break; case PacketType_Temperature: { temp_packet Temp = Packet.TempPacket; - if (Temp.Temperature > 21) + if (Temp.Temperature > 0) { BLState->BrightnessPercent = .25f; } @@ -372,19 +404,19 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue)) { -#if 0 +#if 1 for (u32 i = 0; i < MotorOpenTimesCount; i++) { time_range Range = MotorOpenTimes[i]; bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); + bool LastSendTimeInRange = SystemTimeIsInTimeRange(BLState->LastSendTime, Range); + bool LastTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Last, Range); - bool SendOpen = CurrTimeInRange && !LastTimeInRange; - - r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)BLState->LastSendTime.NanosSinceEpoch); - r64 SecondsSinceLastSend = NanosSinceLastSend / PowR32(10, 8); + bool SendOpen = CurrTimeInRange && !LastSendTimeInRange; + bool SendClose = !CurrTimeInRange && LastSendTimeInRange; //SendOpen = SecondsSinceLastSend > 2; if (SendOpen) @@ -401,9 +433,10 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Packet.MotorPacket.FlowerPositions[2] = 2; MotorCommand = Packet; } - else if (!CurrTimeInRange && LastTimeInRange) + else if (SendClose) { SendMotorCommand = true; + BLState->LastSendTime = Context->SystemTime_Current; OutputDebugString("Motors: Close\n"); blumen_packet Packet = {}; @@ -420,6 +453,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { gs_data Msg = StructToData(&MotorCommand, blumen_packet); MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + DEBUG_SentMotorCommand(MotorCommand.MotorPacket, Context->ThreadContext); } } // Dim the leds based on temp data @@ -439,8 +473,14 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) // TODO(PS): This should really only happen if we think the // flower _might_ be open + for (u32 a = 0; a < State->Assemblies.Count; a++) { + // TODO(PS): make sure to align which assembly goes with which + // flower index + bool FlowerIsOpen = BLState->LastKnownMotorState.FlowerPositions[a] == 2; + //if (!FlowerIsOpen) continue; + assembly Assembly = State->Assemblies.Values[a]; led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; @@ -466,7 +506,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) { BLState->LastStatusUpdateTime = Context->SystemTime_Current; - OutputDebugString("Sending Status\n"); + OutputDebugString("Attempting to Send Lumenarium Status\n"); blumen_packet Packet = {}; Packet.Type = PacketType_LumenariumStatus; @@ -484,6 +524,61 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } +US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) +{ + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + ui_interface* I = &State->Interface; + + // NOTE(PS): Motors + { + motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; + + for (u32 MotorIndex = 0; MotorIndex < 3; MotorIndex++) + { + gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); + ui_BeginRow(I, 5); + { + ui_Label(I, Label); + + bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == 1; + if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) + { + PendingPacket.FlowerPositions[MotorIndex] = 1; + } + bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == 3; + if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = 3; + } + bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == 4; + if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = 4; + } + bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == 2; + if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = 2; + } + } + ui_EndRow(I); + } + BLState->DEBUG_PendingMotorPacket = PendingPacket; + + if (ui_Button(I, MakeString("Send Motor Packet"))) + { + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + + DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); + } + + } +} + US_CUSTOM_CLEANUP(BlumenLumen_CustomCleanup) { blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; @@ -497,6 +592,7 @@ BlumenLumen_UserSpaceCreate() Result.LoadPatterns = BlumenLumen_LoadPatterns; Result.CustomInit = BlumenLumen_CustomInit; Result.CustomUpdate = BlumenLumen_CustomUpdate; + Result.CustomDebugUI = BlumenLumen_DebugUI; Result.CustomCleanup = BlumenLumen_CustomCleanup; return Result; } diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index fb5b238..160d90b 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -95,7 +95,6 @@ struct mic_listen_job_data platform_socket_manager* SocketManager; blumen_network_msg_queue* IncomingMsgQueue; - platform_socket_handle_ ListenSocket; blumen_network_msg_queue* OutgoingMsgQueue; }; @@ -120,12 +119,52 @@ SystemTimeIsInTimeRange(system_time SysTime, time_range Range) } global time_range MotorOpenTimes[] = { - { 17, 56, 17, 56 }, - { 17, 58, 17, 56 }, - { 18, 00, 18, 00 }, - + { 00, 30, 00, 40 }, + { 00, 50, 01, 00 }, + { 01, 10, 01, 20 }, + { 01, 30, 01, 40 }, + { 01, 50, 02, 00 }, + { 02, 10, 02, 20 }, + { 02, 30, 02, 40 }, + { 02, 50, 03, 00 }, + { 03, 10, 03, 20 }, + { 03, 30, 03, 40 }, + { 03, 50, 04, 00 }, + { 04, 10, 04, 20 }, + { 04, 30, 04, 40 }, + { 04, 50, 05, 00 }, + { 05, 10, 05, 20 }, + { 05, 30, 05, 40 }, + { 05, 50, 06, 00 }, + { 06, 10, 06, 20 }, + { 06, 30, 06, 40 }, + { 06, 50, 07, 00 }, + { 07, 10, 07, 20 }, + { 07, 30, 07, 40 }, + { 07, 50, 8, 00 }, + { 8, 10, 8, 20 }, + { 8, 30, 8, 40 }, + { 8, 50, 9, 00 }, + { 9, 10, 9, 20 }, + { 9, 30, 9, 40 }, + { 9, 50, 10, 00 }, + { 10, 10, 10, 20 }, + { 10, 30, 10, 40 }, + { 10, 50, 11, 00 }, + { 11, 10, 11, 20 }, + { 11, 30, 11, 40 }, + { 11, 50, 12, 00 }, + { 12, 10, 12, 20 }, + { 12, 30, 12, 40 }, + { 12, 50, 13, 00 }, + { 13, 10, 13, 20 }, + { 13, 30, 13, 40 }, + { 13, 50, 14, 00 }, + { 14, 10, 14, 20 }, + { 14, 30, 14, 40 }, + { 14, 50, 15, 00 }, }; -global u32 MotorOpenTimesCount = 3; +global u32 MotorOpenTimesCount = sizeof(MotorOpenTimes) / sizeof(MotorOpenTimes[0]);; struct phrase_string_to_anim_file { @@ -171,6 +210,9 @@ struct blumen_lumen_state system_time LastSendTime; v4 AssemblyColors[3]; + + // Debug + motor_packet DEBUG_PendingMotorPacket; }; diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index ca3336d..04fabd6 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -3584,6 +3584,12 @@ CreatePlatformSocketManager(platform_connect_socket* ConnectSocketProc, return Result; } +internal bool +SocketHandleIsValid(platform_socket_handle_ Handle) +{ + return Handle.Index != 0; +} + internal platform_socket* SocketManagerGetSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle) { @@ -3606,11 +3612,19 @@ ConnectSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle) platform_socket* Socket = SocketManagerGetSocket(Manager, Handle); if (Socket) { - Result = Manager->ConnectSocketProc(Socket); + Result = Manager->ConnectSocketProc(Manager, Socket); } return Result; } +internal bool +RemoveSocket (platform_socket_manager* Manager, platform_socket_handle_ Handle) +{ + bool Result = Manager->SocketsUsed[Handle.Index]; + Manager->SocketsUsed[Handle.Index] = false; + return Result; +} + internal platform_socket_handle_ CreateSocket(platform_socket_manager* Manager, char* Addr, char* Port) { @@ -3627,30 +3641,52 @@ CreateSocket(platform_socket_manager* Manager, char* Addr, char* Port) Assert(Result.Index != 0); platform_socket* Socket = &Manager->Sockets[Result.Index]; + Socket->Handle = Result; CopyArray(Addr, Socket->Addr, char, CStringLength(Addr) + 1); CopyArray(Port, Socket->Port, char, CStringLength(Port) + 1); - bool Success = Manager->ConnectSocketProc(Socket); - Assert(Success); + bool Success = Manager->ConnectSocketProc(Manager, Socket); + if (!Success) + { + if (RemoveSocket(Manager, Result)) + { + Result = {}; + } + else + { + InvalidCodePath; + } + } return Result; } +internal bool +CloseSocket(platform_socket_manager* Manager, platform_socket* Socket) +{ + bool Result = false; + if (Socket) + { + if (Manager->CloseSocketProc(Manager, Socket)) + { + RemoveSocket(Manager, Socket->Handle); + *Socket = {}; + Result = true; + } + else + { + InvalidCodePath; + } + } + return Result; +} + internal bool CloseSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle) { bool Result = false; platform_socket* Socket = SocketManagerGetSocket(Manager, Handle); - if (Socket) - { - if (Manager->CloseSocketProc(Socket)) - { - Manager->SocketsUsed[Handle.Index] = false; - *Socket = {}; - Result = true; - } - } - return Result; + return CloseSocket(Manager, Socket); } // NOTE(pjs): returns true if the socket is connected @@ -3662,7 +3698,7 @@ SocketQueryStatus(platform_socket_manager* Manager, platform_socket_handle_ Sock platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); if (Socket) { - Result = Manager->SocketQueryStatusProc(Socket); + Result = Manager->SocketQueryStatusProc(Manager, Socket); } return Result; } @@ -3674,7 +3710,7 @@ SocketPeek(platform_socket_manager* Manager, platform_socket_handle_ SocketHandl platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); if (Socket) { - Result = Manager->SocketPeekProc(Socket); + Result = Manager->SocketPeekProc(Manager, Socket); } return Result; } @@ -3686,7 +3722,7 @@ SocketRecieve(platform_socket_manager* Manager, platform_socket_handle_ SocketHa platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); if (Socket) { - Result = Manager->SocketRecieveProc(Socket, Storage); + Result = Manager->SocketRecieveProc(Manager, Socket, Storage); } return Result; } @@ -3698,7 +3734,7 @@ SocketSend(platform_socket_manager* Manager, platform_socket_handle_ SocketHandl platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); if (Socket) { - s32 SizeSent = Manager->SocketSendProc(Socket, Address, Port, Data, Flags); + s32 SizeSent = Manager->SocketSendProc(Manager, Socket, Address, Port, Data, Flags); Result = (SizeSent == Data.Size); } return Result; diff --git a/src/gs_libs/gs_types.h b/src/gs_libs/gs_types.h index 243f51b..5006ffe 100644 --- a/src/gs_libs/gs_types.h +++ b/src/gs_libs/gs_types.h @@ -1158,31 +1158,34 @@ typedef struct platform_socket_handle_ typedef struct platform_socket { + platform_socket_handle_ Handle; char Addr[128]; char Port[32]; u8* PlatformHandle; } platform_socket; -#define CONNECT_SOCKET(name) bool name(platform_socket* Socket) +typedef struct platform_socket_manager platform_socket_manager; + +#define CONNECT_SOCKET(name) bool name(platform_socket_manager* Manager, platform_socket* Socket) typedef CONNECT_SOCKET(platform_connect_socket); -#define CLOSE_SOCKET(name) bool name(platform_socket* Socket) +#define CLOSE_SOCKET(name) bool name(platform_socket_manager* Manager, platform_socket* Socket) typedef CLOSE_SOCKET(platform_close_socket); -#define SOCKET_QUERY_STATUS(name) bool name(platform_socket* Socket) +#define SOCKET_QUERY_STATUS(name) bool name(platform_socket_manager* Manager, platform_socket* Socket) typedef SOCKET_QUERY_STATUS(platform_socket_query_status); -#define SOCKET_PEEK(name) u32 name(platform_socket* Socket) +#define SOCKET_PEEK(name) u32 name(platform_socket_manager* Manager, platform_socket* Socket) typedef SOCKET_PEEK(platform_socket_peek); // TODO(pjs): allow for a size parameter that can be zero // if provided, that is how big the message it expects to be // if it equals zero, the proc will peek at the message first to determine // the needed size -#define SOCKET_RECEIVE(name) gs_data name(platform_socket* Socket, gs_memory_arena* Storage) +#define SOCKET_RECEIVE(name) gs_data name(platform_socket_manager* Manager, platform_socket* Socket, gs_memory_arena* Storage) typedef SOCKET_RECEIVE(platform_socket_receive); -#define SOCKET_SEND(name) s32 name(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags) +#define SOCKET_SEND(name) s32 name(platform_socket_manager* Manager, platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags) typedef SOCKET_SEND(platform_socket_send); #define SOCKETS_COUNT_MAX 32 From bb7c175c33944a14862f5ea13c22100ed2c71134 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 27 Mar 2021 13:22:02 -0700 Subject: [PATCH 023/151] Masking off the top leds if the flowers are open, and mapping between file names and clear core motor indices --- src/app/engine/assembly/foldhaus_assembly.cpp | 8 +++- src/app/ss_blumen_lumen/blumen_lumen.cpp | 48 +++++++++++++++++-- src/app/ss_blumen_lumen/blumen_lumen.h | 18 +++++++ 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/app/engine/assembly/foldhaus_assembly.cpp b/src/app/engine/assembly/foldhaus_assembly.cpp index ebbff73..6264b0e 100644 --- a/src/app/engine/assembly/foldhaus_assembly.cpp +++ b/src/app/engine/assembly/foldhaus_assembly.cpp @@ -195,9 +195,11 @@ ConstructAssemblyFromDefinition (assembly* Assembly, led_system* LedSystem) } } -internal void +internal assembly* LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena* Scratch, context Context, gs_const_string Path, event_log* GlobalLog) { + assembly* NewAssembly = 0; + gs_file AssemblyFile = ReadEntireFile(Context.ThreadContext.FileHandler, Path); if (FileNoError(AssemblyFile)) { @@ -206,7 +208,7 @@ LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena s32 IndexOfLastSlash = FindLast(Path, '\\'); gs_const_string FileName = Substring(Path, IndexOfLastSlash + 1, Path.Length); - assembly* NewAssembly = AssemblyArray_Take(Assemblies); + NewAssembly = AssemblyArray_Take(Assemblies); NewAssembly->Arena = CreateMemoryArena(Context.ThreadContext.Allocator, "Assembly Arena"); parser AssemblyParser = ParseAssemblyFile(NewAssembly, FileName, AssemblyFileText, Scratch); @@ -232,6 +234,8 @@ LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena { LogError(GlobalLog, "Unable to load assembly file"); } + + return NewAssembly; } internal void diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index fd02a59..c317696 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -5,6 +5,24 @@ // #ifndef BLUMEN_LUMEN_CPP +internal s32 +GetCCIndex (assembly Assembly, blumen_lumen_state* BLState) +{ + s32 Result = 0; + + u64 AssemblyNameHash = HashDJB2ToU32(StringExpand(Assembly.Name)); + for (u32 i = 0; i < BLState->AssemblyNameToClearCoreMapCount; i++) + { + if (AssemblyNameHash == BLState->AssemblyNameToClearCore_Names[i]) + { + Result = (s32)i; + break; + } + } + + return Result; +} + internal bool MessageQueue_CanRead(blumen_network_msg_queue* Queue) { @@ -190,9 +208,17 @@ BlumenLumen_CustomInit(app_state* State, context Context) gs_const_string SculpturePath0 = ConstString("data/ss_blumen_one.fold"); gs_const_string SculpturePath1 = ConstString("data/ss_blumen_two.fold"); gs_const_string SculpturePath2 = ConstString("data/ss_blumen_three.fold"); - LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath0, State->GlobalLog); - LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath1, State->GlobalLog); - LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath2, State->GlobalLog); + assembly* Flower0 = LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath0, State->GlobalLog); + assembly* Flower1 = LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath1, State->GlobalLog); + assembly* Flower2 = LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath2, State->GlobalLog); + + BLState->AssemblyNameToClearCoreMapCount = 3; + BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent, + u64, + BLState->AssemblyNameToClearCoreMapCount); + BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU32(StringExpand(Flower0->Name)); + BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU32(StringExpand(Flower1->Name)); + BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower2->Name)); #endif #if 1 @@ -437,11 +463,23 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } - // TODO(PS): This should really only happen if we think the - // flower _might_ be open + // NOTE(PS): If the flowers are mostly open or full open + // we mask off the top leds to prevent them from overheating + // while telescoped inside the flower + motor_packet CurrMotorPos = BLState->LastKnownMotorState; for (u32 a = 0; a < State->Assemblies.Count; a++) { assembly Assembly = State->Assemblies.Values[a]; + u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState); + + u8 MotorPos = CurrMotorPos.FlowerPositions[AssemblyCCIndex]; + + if (MotorPos == MotorState_Closed || + MotorPos == MotorState_HalfOpen) + { + continue; + } + led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient); diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index fb5b238..2e253e0 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -14,6 +14,16 @@ enum bl_python_packet_type PacketType_LumenariumStatus = 4, }; +enum bl_motor_state_value +{ + MotorState_Invalid = 0, + + MotorState_Closed = 1, + MotorState_Open = 2, + MotorState_HalfOpen = 3, + MotorState_MostlyOpen = 4, +}; + #pragma pack(push, 1) typedef struct motor_packet { @@ -171,6 +181,14 @@ struct blumen_lumen_state system_time LastSendTime; v4 AssemblyColors[3]; + + // The indices of this array are the index the clear core uses to + // represent a motor. + // The values of the array are the names Lumenarium uses to + // represent assemblies. + // + u32 AssemblyNameToClearCoreMapCount; + u64* AssemblyNameToClearCore_Names; }; From 874925a5fe12feb311c527939588767e5fc56a9c Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 27 Mar 2021 14:46:17 -0700 Subject: [PATCH 024/151] mapping phrases to colors for patterns --- src/app/foldhaus_app.h | 1 + src/app/foldhaus_platform.h | 1 + src/app/patterns/blumen_patterns.h | 3 +- src/app/platform_win32/win32_foldhaus.cpp | 22 +++++- src/app/ss_blumen_lumen/blumen_lumen.cpp | 22 ++++-- src/app/ss_blumen_lumen/blumen_lumen.h | 5 +- src/app/ss_blumen_lumen/phrase_hue_map.h | 83 +++++++++++++++++++++++ 7 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 src/app/ss_blumen_lumen/phrase_hue_map.h diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index 6e44c4d..a8eb0f1 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -42,6 +42,7 @@ typedef struct panel panel; #include "engine/animation/foldhaus_animation_renderer.cpp" #include "engine/user_space.h" +#include "ss_blumen_lumen/phrase_hue_map.h" #include "ss_blumen_lumen/blumen_lumen.h" struct app_state diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index f464603..dcfafc8 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -27,6 +27,7 @@ Handle_IsValid(handle Handle) } #include "..\gs_libs\gs_string.h" +#include "..\gs_libs\gs_csv.h" #include "foldhaus_debug.h" global debug_services* GlobalDebugServices; diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 84d51e5..a1db355 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -1193,8 +1193,7 @@ Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 LightHueMin = (ModR32(Time, 10) / 10) * 360; r32 LightHueMax = ModR32((LightHueMin + 45), 360) ; #else - v4 CenterColor = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; - r32 CenterHue = RGBToHSV(CenterColor).x; + r32 CenterHue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3].Hue0; r32 LightHueMin = ModR32(CenterHue + 30, 360);; r32 LightHueMax = ModR32(CenterHue - 30, 360) ; #endif diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index cc1020e..e98567f 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -524,7 +524,6 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) } #include "../../gs_libs/gs_path.h" -#include "../../gs_libs/gs_csv.h" internal void Win32_SendOutputData(gs_thread_context ThreadContext, addressed_data_buffer_list OutputData) @@ -575,6 +574,27 @@ WinMain ( if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; + + gs_file TestFile = ReadEntireFile(ThreadContext.FileHandler, ConstString("data/flower_codes.tsv")); + gs_const_string TestFileStr = {}; + TestFileStr.Str = (char*)TestFile.Memory; + TestFileStr.Length = TestFile.Size; + gscsv_sheet Sheet = CSV_Parse(TestFileStr, { '\t' }, ThreadContext.Transient); + + gs_string Out = PushString(ThreadContext.Transient, TestFile.Size * 2); + + for (u64 y = 0; y < Sheet.RowCount; y++) + { + for (u64 x = 0; x < Sheet.ColumnCount; x++) + { + gs_const_string Cell = CSVSheet_GetCell(Sheet, x, y); + AppendPrintF(&Out, "%S\t", Cell); + } + AppendPrintF(&Out, "\n"); + } + NullTerminate(&Out); + OutputDebugStringA(Out.Str); + MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); Win32UpdateWindowDimension(&MainWindow); diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 5dc1bde..404a0c0 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -281,6 +281,15 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower2->Name)); #endif + { + gs_file ColorPhraseCSVFile = ReadEntireFile(Context.ThreadContext.FileHandler, ConstString("data/flower_codes.tsv")); + gs_const_string ColorPhraseMapStr = ConstString((char*)ColorPhraseCSVFile.Memory, + ColorPhraseCSVFile.Size); + gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, { '\t' }, State->Transient); + + BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, &State->Permanent); + } + #if 1 { // Animation PLAYGROUND animation_desc Desc = {}; @@ -323,10 +332,6 @@ BlumenLumen_CustomInit(app_state* State, context Context) #endif State->AnimationSystem.TimelineShouldAdvance = true; - BLState->AssemblyColors[0] = RedV4; - BLState->AssemblyColors[1] = GreenV4; - BLState->AssemblyColors[2] = BlueV4; - return Result; } @@ -360,8 +365,16 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) case PacketType_PatternCommand: { microphone_packet Mic = Packet.MicPacket; + u64 NameHash = HashDJB2ToU32(Mic.AnimationFileName); u32 NameLen = CStringLength(Mic.AnimationFileName); + phrase_hue NewHue = PhraseHueMap_Get(BLState->PhraseHueMap, NameHash); + if (NewHue.PhraseHash != 0) + { + u32 AssemblyIdx = BLState->LastAssemblyColorSet; + BLState->AssemblyColors[AssemblyIdx] = NewHue; + } +#if 0 for (u32 i = 0; i < PhraseToAnimMapCount; i++) { gs_const_string PhraseStr = ConstString(PhraseToAnimMap[i].Phrase); @@ -379,6 +392,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) OutputDebugStringA(T.Str); } } +#endif }break; case PacketType_MotorState: diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index b097194..c8fd808 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -219,7 +219,8 @@ struct blumen_lumen_state system_time LastSendTime; - v4 AssemblyColors[3]; + phrase_hue AssemblyColors[3]; + u32 LastAssemblyColorSet; // The indices of this array are the index the clear core uses to // represent a motor. @@ -229,6 +230,8 @@ struct blumen_lumen_state u32 AssemblyNameToClearCoreMapCount; u64* AssemblyNameToClearCore_Names; + phrase_hue_map PhraseHueMap; + // Debug motor_packet DEBUG_PendingMotorPacket; }; diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h new file mode 100644 index 0000000..0d7d66c --- /dev/null +++ b/src/app/ss_blumen_lumen/phrase_hue_map.h @@ -0,0 +1,83 @@ +/* date = March 27th 2021 1:55 pm */ + +#ifndef PHRASE_HUE_MAP_H +#define PHRASE_HUE_MAP_H + +typedef struct phrase_hue_map +{ + u64 Count; + gs_const_string* Phrases; + u64* PhraseHashes; + r32* Hue0; + r32* Hue1; + r32* Hue2; +} phrase_hue_map; + +typedef struct phrase_hue +{ + gs_const_string Phrase; + u64 PhraseHash; + r32 Hue0; + r32 Hue1; + r32 Hue2; +} phrase_hue; + +internal phrase_hue_map +PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) +{ + phrase_hue_map Result = {}; + Result.Count = Sheet.RowCount - 1; // we don't include the header row + Result.Phrases = PushArray(Arena, gs_const_string, Result.Count); + Result.PhraseHashes = PushArray(Arena, u64, Result.Count); + Result.Hue0 = PushArray(Arena, r32, Result.Count); + Result.Hue1 = PushArray(Arena, r32, Result.Count); + Result.Hue2 = PushArray(Arena, r32, Result.Count); + + for (u32 Row = 1; Row < Sheet.RowCount; Row++) + { + u32 Index = Row - 1; + gs_const_string Phrase = CSVSheet_GetCell(Sheet, + 0, Row); + u64 PhraseHash = HashDJB2ToU32(StringExpand(Phrase)); + gs_const_string Hue0Str = CSVSheet_GetCell(Sheet, + 1, Row); + gs_const_string Hue1Str = CSVSheet_GetCell(Sheet, + 2, Row); + gs_const_string Hue2Str = CSVSheet_GetCell(Sheet, + 3, Row); + gs_const_string Homonyms = CSVSheet_GetCell(Sheet, + 4, Row); + + Result.Phrases[Index] = PushStringF(Arena, Phrase.Length, "%S", Phrase).ConstString; + Result.PhraseHashes[Index] = PhraseHash; + Result.Hue0[Index] = (r64)ParseFloat(Hue0Str); + Result.Hue1[Index] = (r64)ParseFloat(Hue1Str); + Result.Hue2[Index] = (r64)ParseFloat(Hue2Str); + } + + return Result; +} + +internal phrase_hue +PhraseHueMap_Get(phrase_hue_map Map, u64 PhraseHash) +{ + phrase_hue Result = {}; + + for (u32 i = 0; i < Map.Count; i++) + { + if (Map.PhraseHashes[i] == PhraseHash) + { + Result.Phrase = Map.Phrases[i]; + Result.PhraseHash = Map.PhraseHashes[i]; + Result.Hue0 = Map.Hue0[i]; + Result.Hue1 = Map.Hue1[i]; + Result.Hue2 = Map.Hue2[i]; + + break; + } + } + + return Result; +} + +#endif //PHRASE_HUE_MAP_H From c4769bd53c9c8ad3016d02ae25955e7923914d61 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 27 Mar 2021 15:04:18 -0700 Subject: [PATCH 025/151] Cleaning Up blumen_lumen user space code --- src/app/foldhaus_app.h | 11 +++ src/app/ss_blumen_lumen/blumen_lumen.cpp | 96 +++++++------------ src/app/ss_blumen_lumen/blumen_lumen.h | 49 +--------- .../ss_blumen_lumen/blumen_lumen_settings.h | 61 ++++++++++++ src/gs_libs/gs_types.cpp | 27 ++++++ src/gs_libs/gs_types.h | 2 + 6 files changed, 138 insertions(+), 108 deletions(-) create mode 100644 src/app/ss_blumen_lumen/blumen_lumen_settings.h diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index a8eb0f1..f9813fe 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -82,6 +82,17 @@ internal void OpenColorPicker(app_state* State, v4* Address); #include "engine/assembly/foldhaus_assembly.cpp" +internal assembly* +LoadAssembly(gs_const_string Path, app_state* State, context Context) +{ + return LoadAssembly(&State->Assemblies, + &State->LedSystem, + State->Transient, + Context, + Path, + State->GlobalLog); +} + #include "engine/user_space.cpp" #include "patterns/blumen_patterns.h" diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 404a0c0..2c91b5b 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -57,16 +57,25 @@ DEBUG_SentMotorCommand(motor_packet Packet, gs_thread_context Ctx) } internal void -DEBUG_ReceivedMotorPositions(motor_packet Packet, gs_thread_context Ctx) +DEBUG_ReceivedMotorPositions(motor_packet NewPos, + motor_packet LastPos, + gs_thread_context Ctx) { - gs_string Str = PushStringF(Ctx.Transient, 256, "Motor Status Received\nCurrent Positions: %d %d %d\n", - Packet.FlowerPositions[0], - Packet.FlowerPositions[1], - Packet.FlowerPositions[2]); - DEBUG_AppendText(Str, Ctx); + bool PosChanged = (LastPos.FlowerPositions[0] != NewPos.FlowerPositions[0] || + LastPos.FlowerPositions[1] != NewPos.FlowerPositions[1] || + LastPos.FlowerPositions[2] != NewPos.FlowerPositions[2]); - NullTerminate(&Str); - OutputDebugStringA(Str.Str); + if (PosChanged) + { + gs_string Str = PushStringF(Ctx.Transient, 256, "Motor Status Received\nCurrent Positions: %d %d %d\n", + NewPos.FlowerPositions[0], + NewPos.FlowerPositions[1], + NewPos.FlowerPositions[2]); + DEBUG_AppendText(Str, Ctx); + + NullTerminate(&Str); + OutputDebugStringA(Str.Str); + } } // KB(1) is just bigger than any packet we send. Good for now @@ -261,34 +270,27 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); #endif -#if 0 - gs_const_string SculpturePath = ConstString("data/test_blumen.fold"); - LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); -#else - gs_const_string SculpturePath0 = ConstString("data/ss_blumen_one.fold"); - gs_const_string SculpturePath1 = ConstString("data/ss_blumen_two.fold"); - gs_const_string SculpturePath2 = ConstString("data/ss_blumen_three.fold"); - assembly* Flower0 = LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath0, State->GlobalLog); - assembly* Flower1 = LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath1, State->GlobalLog); - assembly* Flower2 = LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath2, State->GlobalLog); + assembly* Flower0 = LoadAssembly(Flower0AssemblyPath, State, Context); + assembly* Flower1 = LoadAssembly(Flower1AssemblyPath, State, Context); + assembly* Flower2 = LoadAssembly(Flower2AssemblyPath, State, Context); BLState->AssemblyNameToClearCoreMapCount = 3; BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent, u64, BLState->AssemblyNameToClearCoreMapCount); - BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU32(StringExpand(Flower0->Name)); - BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU32(StringExpand(Flower1->Name)); - BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower2->Name)); -#endif + BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU64(Flower0->Name); + BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU64(Flower1->Name); + BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU64(Flower2->Name); - { - gs_file ColorPhraseCSVFile = ReadEntireFile(Context.ThreadContext.FileHandler, ConstString("data/flower_codes.tsv")); - gs_const_string ColorPhraseMapStr = ConstString((char*)ColorPhraseCSVFile.Memory, - ColorPhraseCSVFile.Size); - gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, { '\t' }, State->Transient); - - BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, &State->Permanent); - } + gs_file_handler FileHandler = Context.ThreadContext.FileHandler; + gs_file ColorPhraseCSVFile = ReadEntireFile(FileHandler, PhraseMapCSVPath); + gs_const_string ColorPhraseMapStr = DataToString(ColorPhraseCSVFile.Data); + gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, + { PhraseMapCSVSeparator }, + State->Transient); + + BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, + &State->Permanent); #if 1 { // Animation PLAYGROUND @@ -343,19 +345,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) bool SendMotorCommand = false; blumen_packet MotorCommand = {}; -#if 0 - MotorTimeElapsed += Context->DeltaTime; - BLState->TimeElapsed += Context->DeltaTime; - - if (BLState->TimeElapsed > 5) - { - u32 NextIndex = ++BLState->CurrAnim % 3; - animation_handle Next = BLState->AnimHandles[NextIndex]; - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, Next, 5); - BLState->TimeElapsed = 0; - } -#endif - while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) { gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); @@ -406,12 +395,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) T[1] << 0); motor_packet LastPos = BLState->LastKnownMotorState; - if (LastPos.FlowerPositions[0] != Motor.Pos.FlowerPositions[0] || - LastPos.FlowerPositions[1] != Motor.Pos.FlowerPositions[1] || - LastPos.FlowerPositions[2] != Motor.Pos.FlowerPositions[2]) - { - DEBUG_ReceivedMotorPositions(Motor.Pos, Context->ThreadContext); - } + DEBUG_ReceivedMotorPositions(LastPos, Motor.Pos, Context->ThreadContext); BLState->LastKnownMotorState = Motor.Pos; }break; @@ -429,7 +413,8 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) BLState->BrightnessPercent = 1.f; } - gs_string TempStr = PushStringF(State->Transient, 256, "\nTemperature: %d\n", + gs_string TempStr = PushStringF(State->Transient, 256, + "\nTemperature: %d\n", Temp.Temperature); NullTerminate(&TempStr); OutputDebugStringA(TempStr.Str); @@ -441,10 +426,8 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) // Open / Close the Motor - if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue)) { -#if 1 for (u32 i = 0; i < MotorOpenTimesCount; i++) { time_range Range = MotorOpenTimes[i]; @@ -487,7 +470,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) MotorCommand = Packet; } } -#endif if (SendMotorCommand) { @@ -497,8 +479,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } // Dim the leds based on temp data -#define DIM_LED_BRIGHTNESS 1 -#if DIM_LED_BRIGHTNESS for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) { led_buffer Buffer = State->LedSystem.Buffers[i]; @@ -517,11 +497,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) motor_packet CurrMotorPos = BLState->LastKnownMotorState; for (u32 a = 0; a < State->Assemblies.Count; a++) { - // TODO(PS): make sure to align which assembly goes with which - // flower index - bool FlowerIsOpen = BLState->LastKnownMotorState.FlowerPositions[a] == 2; - //if (!FlowerIsOpen) continue; - assembly Assembly = State->Assemblies.Values[a]; u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState); @@ -547,7 +522,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } } -#endif // Send Status Packet { diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index c8fd808..b23378d 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -128,53 +128,6 @@ SystemTimeIsInTimeRange(system_time SysTime, time_range Range) return Result; } -global time_range MotorOpenTimes[] = { - { 00, 30, 00, 40 }, - { 00, 50, 01, 00 }, - { 01, 10, 01, 20 }, - { 01, 30, 01, 40 }, - { 01, 50, 02, 00 }, - { 02, 10, 02, 20 }, - { 02, 30, 02, 40 }, - { 02, 50, 03, 00 }, - { 03, 10, 03, 20 }, - { 03, 30, 03, 40 }, - { 03, 50, 04, 00 }, - { 04, 10, 04, 20 }, - { 04, 30, 04, 40 }, - { 04, 50, 05, 00 }, - { 05, 10, 05, 20 }, - { 05, 30, 05, 40 }, - { 05, 50, 06, 00 }, - { 06, 10, 06, 20 }, - { 06, 30, 06, 40 }, - { 06, 50, 07, 00 }, - { 07, 10, 07, 20 }, - { 07, 30, 07, 40 }, - { 07, 50, 8, 00 }, - { 8, 10, 8, 20 }, - { 8, 30, 8, 40 }, - { 8, 50, 9, 00 }, - { 9, 10, 9, 20 }, - { 9, 30, 9, 40 }, - { 9, 50, 10, 00 }, - { 10, 10, 10, 20 }, - { 10, 30, 10, 40 }, - { 10, 50, 11, 00 }, - { 11, 10, 11, 20 }, - { 11, 30, 11, 40 }, - { 11, 50, 12, 00 }, - { 12, 10, 12, 20 }, - { 12, 30, 12, 40 }, - { 12, 50, 13, 00 }, - { 13, 10, 13, 20 }, - { 13, 30, 13, 40 }, - { 13, 50, 14, 00 }, - { 14, 10, 14, 20 }, - { 14, 30, 14, 40 }, - { 14, 50, 15, 00 }, -}; -global u32 MotorOpenTimesCount = sizeof(MotorOpenTimes) / sizeof(MotorOpenTimes[0]);; struct phrase_string_to_anim_file { @@ -193,6 +146,8 @@ phrase_string_to_anim_file PhraseToAnimMap[] = { }; u32 PhraseToAnimMapCount = sizeof(PhraseToAnimMap) / sizeof(PhraseToAnimMap[0]); +#include "blumen_lumen_settings.h" + struct blumen_lumen_state { bool Running; diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h new file mode 100644 index 0000000..4849ddf --- /dev/null +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -0,0 +1,61 @@ +/* date = March 27th 2021 2:50 pm */ + +#ifndef BLUMEN_LUMEN_SETTINGS_H +#define BLUMEN_LUMEN_SETTINGS_H + +gs_const_string Flower0AssemblyPath = ConstString("data/ss_blumen_one.fold"); +gs_const_string Flower1AssemblyPath = ConstString("data/ss_blumen_two.fold"); +gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold"); + +gs_const_string PhraseMapCSVPath = ConstString("data/flower_codes.tsv"); +char PhraseMapCSVSeparator = '\t'; + +global time_range MotorOpenTimes[] = { + { 00, 30, 00, 40 }, + { 00, 50, 01, 00 }, + { 01, 10, 01, 20 }, + { 01, 30, 01, 40 }, + { 01, 50, 02, 00 }, + { 02, 10, 02, 20 }, + { 02, 30, 02, 40 }, + { 02, 50, 03, 00 }, + { 03, 10, 03, 20 }, + { 03, 30, 03, 40 }, + { 03, 50, 04, 00 }, + { 04, 10, 04, 20 }, + { 04, 30, 04, 40 }, + { 04, 50, 05, 00 }, + { 05, 10, 05, 20 }, + { 05, 30, 05, 40 }, + { 05, 50, 06, 00 }, + { 06, 10, 06, 20 }, + { 06, 30, 06, 40 }, + { 06, 50, 07, 00 }, + { 07, 10, 07, 20 }, + { 07, 30, 07, 40 }, + { 07, 50, 8, 00 }, + { 8, 10, 8, 20 }, + { 8, 30, 8, 40 }, + { 8, 50, 9, 00 }, + { 9, 10, 9, 20 }, + { 9, 30, 9, 40 }, + { 9, 50, 10, 00 }, + { 10, 10, 10, 20 }, + { 10, 30, 10, 40 }, + { 10, 50, 11, 00 }, + { 11, 10, 11, 20 }, + { 11, 30, 11, 40 }, + { 11, 50, 12, 00 }, + { 12, 10, 12, 20 }, + { 12, 30, 12, 40 }, + { 12, 50, 13, 00 }, + { 13, 10, 13, 20 }, + { 13, 30, 13, 40 }, + { 13, 50, 14, 00 }, + { 14, 10, 14, 20 }, + { 14, 30, 14, 40 }, + { 14, 50, 15, 00 }, +}; +global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); + +#endif //BLUMEN_LUMEN_SETTINGS_H diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index 04fabd6..64dec4a 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -1390,6 +1390,13 @@ internal gs_data StringToData(gs_string String) { return StringToData(String.ConstString); } +internal gs_const_string DataToString(gs_data Data) +{ + gs_const_string Result = {}; + Result.Str = (char*)Data.Memory; + Result.Length = Data.Size; + return Result; +} internal bool IsSlash(char C) { return ((C == '/') || (C == '\\')); } internal bool IsUpper(char C) { return(('A' <= C) && (C <= 'Z')); } @@ -3783,6 +3790,16 @@ HashDJB2ToU32(u32 Length, char* String) } return Hash; } +internal u32 +HashDJB2ToU32(gs_const_string Str) +{ + return HashDJB2ToU32(StringExpand(Str)); +} +internal u32 +HashDJB2ToU32(gs_string Str) +{ + return HashDJB2ToU32(StringExpand(Str)); +} internal u64 HashDJB2ToU64(char* String) @@ -3805,6 +3822,16 @@ HashDJB2ToU64(u32 Length, char* String) } return Hash; } +internal u64 +HashDJB2ToU64(gs_const_string Str) +{ + return HashDJB2ToU64(StringExpand(Str)); +} +internal u64 +HashDJB2ToU64(gs_string Str) +{ + return HashDJB2ToU64(StringExpand(Str)); +} /////////////////////////// // diff --git a/src/gs_libs/gs_types.h b/src/gs_libs/gs_types.h index 5006ffe..293dbf2 100644 --- a/src/gs_libs/gs_types.h +++ b/src/gs_libs/gs_types.h @@ -273,6 +273,8 @@ Glue(gs_AssertFail_, __LINE__) = 1 / (int)(!!(c)), \ #define IsPowerOfTwo(x) (((x) & ((x) - 1)) == 0) #define IsOdd(x) (((x) & 1) != 0) +#define CArrayLength(arr) (sizeof(arr) / (sizeof(arr[0]))) + internal void ZeroMemory_(u8* Memory, u64 Size) { From c27cd4052b98bdcb447a6ecfcc8d7e5b4a25553c Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sat, 27 Mar 2021 16:00:06 -0700 Subject: [PATCH 026/151] Patterns, Debug Mode, and Phrase Mapping --- .../panels/foldhaus_panel_assembly_debug.h | 2 + src/app/patterns/blumen_patterns.h | 58 ++++++++++++------- src/app/platform_win32/win32_foldhaus.cpp | 21 ------- src/app/ss_blumen_lumen/blumen_lumen.cpp | 29 ++-------- .../ss_blumen_lumen/blumen_lumen_settings.h | 4 +- 5 files changed, 47 insertions(+), 67 deletions(-) diff --git a/src/app/editor/panels/foldhaus_panel_assembly_debug.h b/src/app/editor/panels/foldhaus_panel_assembly_debug.h index 7d7b122..5528a3a 100644 --- a/src/app/editor/panels/foldhaus_panel_assembly_debug.h +++ b/src/app/editor/panels/foldhaus_panel_assembly_debug.h @@ -106,12 +106,14 @@ AssemblyDebug_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren State->AssemblyDebugState.TargetChannel = FSC(Board, Strip); }break; + case ADS_Override_AllOff: case ADS_Override_AllRed: case ADS_Override_AllGreen: case ADS_Override_AllBlue: case ADS_Override_AllWhite: { State->AssemblyDebugState.Brightness = (u8)ui_LabeledRangeSlider(Interface, MakeString("Brightness"), (r32)State->AssemblyDebugState.Brightness, 0, 255); + State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly); }break; default: diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index a1db355..d026493 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -1006,21 +1006,15 @@ Pattern_BasicFlowers(led_buffer* Leds, led_buffer_range Range, assembly Assembly } TLastFrame = Time; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); + v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); + v4 C2 = HSVToRGB({Hue.Hue2, 1, 1, 1}); + for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) { v2_strip Strip = Assembly.Strips[StripIndex]; - - // Each flower different - v4 * Colors = FAC; - if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "center")) - { - Colors = FBC; - } - else if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "right")) - { - Colors = FCC; - } - r32 CycleT = ModR32(Time, 10) * 20; for (u32 i = 0; i < Strip.LedCount; i++) @@ -1031,7 +1025,15 @@ Pattern_BasicFlowers(led_buffer* Leds, led_buffer_range Range, assembly Assembly r32 T = ModR32(P.y + CycleT, 200) / 200.f; T = Clamp01(T); - v4 Color = GetColor(Colors, FLOWER_COLORS_COUNT, T); + v4 Color = {}; + if (T < 0.5f) + { + Color = V4Lerp(T * 2, C0, C1); + } + else + { + Color = V4Lerp((T - 0.5f) * 2, C1, C2); + } Leds->Colors[LedIndex] = V4ToRGBPixel(Color); } } @@ -1042,6 +1044,11 @@ Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Ti { DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); + v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; @@ -1056,7 +1063,7 @@ Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Ti v3 BSeed = v3{P.z, P.x, P.y}; r32 BNoise = 1.0f; //Fbm3D(BSeed / 50); - v4 C = GetColor(&FlowerAColors[0], FLOWER_COLORS_COUNT, Noise); + v4 C = V4Lerp(BNoise, C0, C1); C = C * BNoise; //Leds->Colors[LedIndex] = V4ToRGBPixel(v4{Noise, Noise, Noise, 1}); @@ -1070,6 +1077,11 @@ Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 { DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); + v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; @@ -1080,12 +1092,12 @@ Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 r32 NoiseA = Noise3D((Pp / 38) + v3{0, 0, Time}); NoiseA = PowR32(NoiseA, 3); NoiseA = Smoothstep(NoiseA); - v4 CA = v4{1, 0, 1, 1} * NoiseA; + v4 CA = C0 * NoiseA; r32 NoiseB = Noise3D((Pp / 75) + v3{Time * 0.5f, 0, 0}); NoiseB = PowR32(NoiseB, 3); NoiseB = Smoothstep(NoiseB); - v4 CB = v4{0, 1, 1, 1} * NoiseB; + v4 CB = C1 * NoiseB; v4 C = CA + CB; Leds->Colors[LedIndex] = V4ToRGBPixel(C); @@ -1097,8 +1109,10 @@ Pattern_Leafy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 T { DEBUG_TRACK_FUNCTION; - v4* Colors = &FlowerBColors[0]; - + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); + v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { @@ -1157,6 +1171,10 @@ internal void Pattern_LeafyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); + v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; @@ -1167,12 +1185,12 @@ Pattern_LeafyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 NoiseA = Fbm3D((Pp / 35), Time * 0.5f); //NoiseA = PowR32(NoiseA, 3); NoiseA = Smoothstep(NoiseA); - v4 CA = v4{1, 0, 1, 1} * NoiseA; + v4 CA = C0 * NoiseA; r32 NoiseB = Noise3D((Pp / 35) + v3{0, 0, Time * 5}); NoiseB = PowR32(NoiseB, 3); NoiseB = Smoothstep(NoiseB); - v4 CB = v4{0, 1, 1, 1}; + v4 CB = C1; v4 C = V4Lerp(NoiseB, CA, CB); Leds->Colors[LedIndex] = V4ToRGBPixel(C); diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index e98567f..cd1316d 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -574,27 +574,6 @@ WinMain ( if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; - - gs_file TestFile = ReadEntireFile(ThreadContext.FileHandler, ConstString("data/flower_codes.tsv")); - gs_const_string TestFileStr = {}; - TestFileStr.Str = (char*)TestFile.Memory; - TestFileStr.Length = TestFile.Size; - gscsv_sheet Sheet = CSV_Parse(TestFileStr, { '\t' }, ThreadContext.Transient); - - gs_string Out = PushString(ThreadContext.Transient, TestFile.Size * 2); - - for (u64 y = 0; y < Sheet.RowCount; y++) - { - for (u64 x = 0; x < Sheet.ColumnCount; x++) - { - gs_const_string Cell = CSVSheet_GetCell(Sheet, x, y); - AppendPrintF(&Out, "%S\t", Cell); - } - AppendPrintF(&Out, "\n"); - } - NullTerminate(&Out); - OutputDebugStringA(Out.Str); - MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); Win32UpdateWindowDimension(&MainWindow); diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 2c91b5b..563adbe 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -278,9 +278,9 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent, u64, BLState->AssemblyNameToClearCoreMapCount); - BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU64(Flower0->Name); - BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU64(Flower1->Name); - BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU64(Flower2->Name); + BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU32(StringExpand(Flower2->Name)); + BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU32(StringExpand(Flower1->Name)); + BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower0->Name)); gs_file_handler FileHandler = Context.ThreadContext.FileHandler; gs_file ColorPhraseCSVFile = ReadEntireFile(FileHandler, PhraseMapCSVPath); @@ -292,7 +292,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, &State->Permanent); -#if 1 +#if 0 { // Animation PLAYGROUND animation_desc Desc = {}; Desc.NameSize = 256; @@ -363,25 +363,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) u32 AssemblyIdx = BLState->LastAssemblyColorSet; BLState->AssemblyColors[AssemblyIdx] = NewHue; } -#if 0 - for (u32 i = 0; i < PhraseToAnimMapCount; i++) - { - gs_const_string PhraseStr = ConstString(PhraseToAnimMap[i].Phrase); - u32 PhraseIndex = PhraseToAnimMap[i].PatternIndex; - if (StringEqualsCharArray(PhraseStr, Mic.AnimationFileName, NameLen)) - { - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, - animation_handle{(s32)PhraseIndex}, - 3.0f); - - gs_string T = PushStringF(State->Transient, 256, - "Received Animation Packet:\nAnimationIndex: %d\n", - PhraseIndex); - NullTerminate(&T); - OutputDebugStringA(T.Str); - } - } -#endif }break; case PacketType_MotorState: @@ -623,4 +604,4 @@ BlumenLumen_UserSpaceCreate() } #define BLUMEN_LUMEN_CPP -#endif // BLUMEN_LUMEN_CPP \ No newline at end of file +#endif // BLUMEN_LUMEN_CPP diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 4849ddf..4657663 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -7,8 +7,8 @@ gs_const_string Flower0AssemblyPath = ConstString("data/ss_blumen_one.fold"); gs_const_string Flower1AssemblyPath = ConstString("data/ss_blumen_two.fold"); gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold"); -gs_const_string PhraseMapCSVPath = ConstString("data/flower_codes.tsv"); -char PhraseMapCSVSeparator = '\t'; +gs_const_string PhraseMapCSVPath = ConstString("data/flower_codes.csv"); +char PhraseMapCSVSeparator = ','; global time_range MotorOpenTimes[] = { { 00, 30, 00, 40 }, From 9dc75eff451994c6f1d4cb2c04cbdf7b3792d072 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 27 Mar 2021 16:07:31 -0700 Subject: [PATCH 027/151] message queue cleanup --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 77 +++-------------------- src/app/ss_blumen_lumen/blumen_lumen.h | 11 +--- src/app/ss_blumen_lumen/message_queue.cpp | 66 +++++++++++++++++++ src/app/ss_blumen_lumen/message_queue.h | 17 +++++ 4 files changed, 93 insertions(+), 78 deletions(-) create mode 100644 src/app/ss_blumen_lumen/message_queue.cpp create mode 100644 src/app/ss_blumen_lumen/message_queue.h diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 563adbe..803d86d 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -78,73 +78,14 @@ DEBUG_ReceivedMotorPositions(motor_packet NewPos, } } -// KB(1) is just bigger than any packet we send. Good for now -#define DEFAULT_QUEUE_ENTRY_SIZE KB(1) - internal void -MessageQueue_Init(blumen_network_msg_queue* Queue, gs_memory_arena* Arena) +DEBUG_ReceivedTemperature(temp_packet Temp, gs_thread_context Ctx) { - for (u32 i = 0; i < BLUMEN_MESSAGE_QUEUE_COUNT; i++) - { - Queue->Buffers[i] = PushSizeToData(Arena, DEFAULT_QUEUE_ENTRY_SIZE); - } -} - -internal bool -MessageQueue_CanWrite(blumen_network_msg_queue Queue) -{ - bool Result = ((Queue.WriteHead >= Queue.ReadHead) || - (Queue.WriteHead < Queue.ReadHead)); - return Result; -} - -internal bool -MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) -{ - Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE); - - u32 Index = Queue->WriteHead; - Assert(Index >= 0 && - Index < BLUMEN_MESSAGE_QUEUE_COUNT); - - gs_data* Dest = Queue->Buffers + Index; - CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size); - Dest->Size = Msg.Size; - - // NOTE(pjs): We increment write head at the end of writing so that - // a reader thread doesn't pull the message off before we've finished - // filling it out - Queue->WriteHead = (Queue->WriteHead + 1) % BLUMEN_MESSAGE_QUEUE_COUNT; - return true; -} - -internal bool -MessageQueue_CanRead(blumen_network_msg_queue Queue) -{ - bool Result = (Queue.ReadHead != Queue.WriteHead); - return Result; -} - -internal gs_data -MessageQueue_Read(blumen_network_msg_queue* Queue) -{ - gs_data Result = {}; - u32 ReadIndex = Queue->ReadHead++; - if (Queue->ReadHead >= BLUMEN_MESSAGE_QUEUE_COUNT) - { - Queue->ReadHead = 0; - } - Result = Queue->Buffers[ReadIndex]; - return Result; -} - -internal void -MessageQueue_Clear(blumen_network_msg_queue* Queue) -{ - while (MessageQueue_CanRead(*Queue)) - { - MessageQueue_Read(Queue); - } + gs_string TempStr = PushStringF(Ctx.Transient, 256, + "\nTemperature: %d\n", + Temp.Temperature); + NullTerminate(&TempStr); + OutputDebugStringA(TempStr.Str); } internal void @@ -394,11 +335,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) BLState->BrightnessPercent = 1.f; } - gs_string TempStr = PushStringF(State->Transient, 256, - "\nTemperature: %d\n", - Temp.Temperature); - NullTerminate(&TempStr); - OutputDebugStringA(TempStr.Str); + DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); }break; InvalidDefaultCase; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index b23378d..57666dd 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -5,6 +5,8 @@ // #ifndef BLUMEN_LUMEN_H +#include "message_queue.h" + enum bl_python_packet_type { PacketType_Invalid = 0, @@ -90,14 +92,6 @@ typedef struct blumen_packet #pragma pack(pop) -#define BLUMEN_MESSAGE_QUEUE_COUNT 32 -typedef struct blumen_network_msg_queue -{ - gs_data Buffers[BLUMEN_MESSAGE_QUEUE_COUNT]; - u32 WriteHead; - u32 ReadHead; -} blumen_network_msg_queue; - // TODO(pjs): Refactor this -> blumen_network_job_state struct mic_listen_job_data { @@ -191,6 +185,7 @@ struct blumen_lumen_state motor_packet DEBUG_PendingMotorPacket; }; +#include "message_queue.cpp" diff --git a/src/app/ss_blumen_lumen/message_queue.cpp b/src/app/ss_blumen_lumen/message_queue.cpp new file mode 100644 index 0000000..241b864 --- /dev/null +++ b/src/app/ss_blumen_lumen/message_queue.cpp @@ -0,0 +1,66 @@ + +internal void +MessageQueue_Init(blumen_network_msg_queue* Queue, gs_memory_arena* Arena) +{ + for (u32 i = 0; i < BLUMEN_MESSAGE_QUEUE_COUNT; i++) + { + Queue->Buffers[i] = PushSizeToData(Arena, DEFAULT_QUEUE_ENTRY_SIZE); + } +} + +internal bool +MessageQueue_CanWrite(blumen_network_msg_queue Queue) +{ + bool Result = ((Queue.WriteHead >= Queue.ReadHead) || + (Queue.WriteHead < Queue.ReadHead)); + return Result; +} + +internal bool +MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) +{ + Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE); + + u32 Index = Queue->WriteHead; + Assert(Index >= 0 && + Index < BLUMEN_MESSAGE_QUEUE_COUNT); + + gs_data* Dest = Queue->Buffers + Index; + CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size); + Dest->Size = Msg.Size; + + // NOTE(pjs): We increment write head at the end of writing so that + // a reader thread doesn't pull the message off before we've finished + // filling it out + Queue->WriteHead = (Queue->WriteHead + 1) % BLUMEN_MESSAGE_QUEUE_COUNT; + return true; +} + +internal bool +MessageQueue_CanRead(blumen_network_msg_queue Queue) +{ + bool Result = (Queue.ReadHead != Queue.WriteHead); + return Result; +} + +internal gs_data +MessageQueue_Read(blumen_network_msg_queue* Queue) +{ + gs_data Result = {}; + u32 ReadIndex = Queue->ReadHead++; + if (Queue->ReadHead >= BLUMEN_MESSAGE_QUEUE_COUNT) + { + Queue->ReadHead = 0; + } + Result = Queue->Buffers[ReadIndex]; + return Result; +} + +internal void +MessageQueue_Clear(blumen_network_msg_queue* Queue) +{ + while (MessageQueue_CanRead(*Queue)) + { + MessageQueue_Read(Queue); + } +} diff --git a/src/app/ss_blumen_lumen/message_queue.h b/src/app/ss_blumen_lumen/message_queue.h new file mode 100644 index 0000000..a450b54 --- /dev/null +++ b/src/app/ss_blumen_lumen/message_queue.h @@ -0,0 +1,17 @@ +/* date = March 27th 2021 3:07 pm */ + +#ifndef MESSAGE_QUEUE_H +#define MESSAGE_QUEUE_H + +#define BLUMEN_MESSAGE_QUEUE_COUNT 32 +typedef struct blumen_network_msg_queue +{ + gs_data Buffers[BLUMEN_MESSAGE_QUEUE_COUNT]; + u32 WriteHead; + u32 ReadHead; +} blumen_network_msg_queue; + +// KB(1) is just bigger than any packet we send. Good for now +#define DEFAULT_QUEUE_ENTRY_SIZE KB(1) + +#endif //MESSAGE_QUEUE_H From 3f9a8dfe85ce2b79fc1f325e89175f998ac51253 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 27 Mar 2021 16:40:33 -0700 Subject: [PATCH 028/151] Loaded animations from folders, switch to voice mode or default mode --- .../foldhaus_panel_animation_timeline.h | 2 +- src/app/engine/animation/foldhaus_animation.h | 7 +++ .../foldhaus_animation_serializer.cpp | 10 ++- src/app/ss_blumen_lumen/blumen_lumen.cpp | 62 ++++++++++++++++--- src/app/ss_blumen_lumen/blumen_lumen.h | 30 ++++----- .../ss_blumen_lumen/blumen_lumen_settings.h | 5 ++ 6 files changed, 87 insertions(+), 29 deletions(-) diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index d319a0e..ca30811 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -924,12 +924,12 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* animation* ActiveAnim = 0; animation_handle Handle = State->AnimationSystem.ActiveFadeGroup.From; + TimelineState->NextActiveAnim = Handle; if (IsValid(Handle)) { animation_array Animations = State->AnimationSystem.Animations; ActiveAnim = AnimationArray_GetSafe(Animations, Handle); TimelineState->EditingAnimationHandle = Handle; - TimelineState->NextActiveAnim = Handle; } ui_FillRect(&State->Interface, PanelBounds, v4{.1f,.1f,.1f,1.f}); diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 64ef95c..a9a327d 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -89,6 +89,13 @@ struct animation_handle s32 Index; }; +struct animation_handle_array +{ + u32 Count; + animation_handle* Handles; +}; + +internal animation_handle InvalidAnimHandle () { return { -1 }; } internal bool IsValid (animation_handle H) { return H.Index >= 0; } internal void Clear (animation_handle* H) { H->Index = -1; } internal bool AnimHandlesAreEqual (animation_handle A, animation_handle B) diff --git a/src/app/engine/animation/foldhaus_animation_serializer.cpp b/src/app/engine/animation/foldhaus_animation_serializer.cpp index 9227328..577e84b 100644 --- a/src/app/engine/animation/foldhaus_animation_serializer.cpp +++ b/src/app/engine/animation/foldhaus_animation_serializer.cpp @@ -194,10 +194,14 @@ AnimParser_Parse(gs_data File, gs_memory_arena* Arena, animation_pattern_array A internal animation_handle AnimationSystem_LoadAnimationFromFile(animation_system* System, animation_pattern_array AnimPatterns, context Context, gs_const_string FilePath) { + animation_handle NewAnimHandle = InvalidAnimHandle(); gs_file AnimFile = ReadEntireFile(Context.ThreadContext.FileHandler, FilePath); - animation NewAnim = AnimParser_Parse(AnimFile.Data, System->Storage, AnimPatterns); - NewAnim.FileInfo = AnimFile.FileInfo; - animation_handle NewAnimHandle = AnimationArray_Push(&System->Animations, NewAnim); + if (AnimFile.Size > 0) + { + animation NewAnim = AnimParser_Parse(AnimFile.Data, System->Storage, AnimPatterns); + NewAnim.FileInfo = AnimFile.FileInfo; + NewAnimHandle = AnimationArray_Push(&System->Animations, NewAnim); + } return NewAnimHandle; } #define FOLDHAUS_ANIMATION_SERIALIZER_CPP diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 803d86d..d596bfc 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -5,6 +5,29 @@ // #ifndef BLUMEN_LUMEN_CPP +internal animation_handle_array +LoadAllAnimationsInDir(gs_const_string Path, blumen_lumen_state* BLState, app_state* State, context Context) +{ + animation_handle_array Result = {}; + + gs_thread_context Ctx = Context.ThreadContext; + gs_file_info_array FilesInDir = EnumerateDirectory(Ctx.FileHandler, State->Transient, Path, 0); + + Result.Count = FilesInDir.Count; + Result.Handles = PushArray(&State->Permanent, animation_handle, Result.Count); + + for (u32 i = 0; i < FilesInDir.Count; i++) + { + gs_file_info File = FilesInDir.Values[i]; + Result.Handles[i] = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + File.Path); + } + + return Result; +} + internal s32 GetCCIndex (assembly Assembly, blumen_lumen_state* BLState) { @@ -265,13 +288,17 @@ BlumenLumen_CustomInit(app_state* State, context Context) State->AnimationSystem.ActiveFadeGroup.From = BLState->AnimHandles[2]; } // End Animation Playground -#else +#elif 0 animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/demo_patterns.foldanim")); - State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; +#else + BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context); + BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); + + State->AnimationSystem.ActiveFadeGroup.From = BLState->ModeAnimations[BlumenPattern_Standard].Handles[0]; #endif State->AnimationSystem.TimelineShouldAdvance = true; @@ -301,8 +328,26 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) phrase_hue NewHue = PhraseHueMap_Get(BLState->PhraseHueMap, NameHash); if (NewHue.PhraseHash != 0) { - u32 AssemblyIdx = BLState->LastAssemblyColorSet; - BLState->AssemblyColors[AssemblyIdx] = NewHue; + if (BLState->PatternMode == BlumenPattern_Standard) + { + BLState->AssemblyColors[0] = NewHue; + BLState->AssemblyColors[1] = NewHue; + BLState->AssemblyColors[2] = NewHue; + + animation_handle NewAnim = BLState->ModeAnimations[BlumenPattern_VoiceCommand].Handles[0]; + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + NewAnim, + VoiceCommandFadeDuration); + } + else + { + u32 AssemblyIdx = BLState->LastAssemblyColorSet; + BLState->AssemblyColors[AssemblyIdx] = NewHue; + } + + BLState->PatternMode = BlumenPattern_VoiceCommand; + // TODO(PS): get current time so we can fade back after + // a while } }break; @@ -457,9 +502,12 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Packet.StatusPacket.NextEventTime = 0; animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName, - Min(ActiveAnim->Name.Length, 32)); - Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0; + if (ActiveAnim) + { + CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName, + Min(ActiveAnim->Name.Length, 32)); + Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0; + } gs_data Msg = StructToData(&Packet, blumen_packet); MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 57666dd..1624a75 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -7,6 +7,14 @@ #include "message_queue.h" +enum bl_pattern_mode +{ + BlumenPattern_Standard, + BlumenPattern_VoiceCommand, + + BlumenPattern_Count, +}; + enum bl_python_packet_type { PacketType_Invalid = 0, @@ -122,24 +130,6 @@ SystemTimeIsInTimeRange(system_time SysTime, time_range Range) return Result; } - -struct phrase_string_to_anim_file -{ - char* Phrase; - u32 PatternIndex; -}; - -phrase_string_to_anim_file PhraseToAnimMap[] = { - { "begonia", 0}, - { "hyacinth", 1 }, - { "tulip", 1 }, - { "calla lilly", 0 }, - { "sunflower", 1 }, - { "salvia", 2 }, - { "freesia", 2 }, -}; -u32 PhraseToAnimMapCount = sizeof(PhraseToAnimMap) / sizeof(PhraseToAnimMap[0]); - #include "blumen_lumen_settings.h" struct blumen_lumen_state @@ -179,6 +169,10 @@ struct blumen_lumen_state u32 AssemblyNameToClearCoreMapCount; u64* AssemblyNameToClearCore_Names; + bl_pattern_mode PatternMode; + animation_handle_array ModeAnimations[BlumenPattern_Count]; + u32 CurrentAnimation; + phrase_hue_map PhraseHueMap; // Debug diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 4657663..c254555 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -10,6 +10,9 @@ gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold"); gs_const_string PhraseMapCSVPath = ConstString("data/flower_codes.csv"); char PhraseMapCSVSeparator = ','; +gs_const_string AmbientPatternFolder = ConstString("data/blumen_animations/ambient_patterns/*.foldanim"); +gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_responses/*.foldanim"); + global time_range MotorOpenTimes[] = { { 00, 30, 00, 40 }, { 00, 50, 01, 00 }, @@ -58,4 +61,6 @@ global time_range MotorOpenTimes[] = { }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); +r32 VoiceCommandFadeDuration = 1.0f; // in seconds + #endif //BLUMEN_LUMEN_SETTINGS_H From 5ddca7fbac3d6e2fb16ad223cba5e2ba7f5198c0 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 27 Mar 2021 17:06:30 -0700 Subject: [PATCH 029/151] Scrollable pattern lists --- src/app/editor/interface.h | 4 +++- .../panels/foldhaus_panel_animation_timeline.h | 5 +++++ src/app/patterns/blumen_patterns.h | 15 +-------------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index 1c3dfaf..54a956e 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -1524,7 +1524,9 @@ ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows, u32 Elem // Create the viewport that offsets list contents (and at render time determines what is visible) // ui_widget* ViewportLayout = ui_PushLayout(Interface, MakeString("Contents")); + ui_WidgetSetFlag(ViewportLayout, UIWidgetFlag_DrawOutline); ui_WidgetClearFlag(ViewportLayout, UIWidgetFlag_ExpandsToFitChildren); + ViewportLayout->FillDirection = LayoutDirection_TopDown; ViewportLayout->Bounds.Min.y = SliderBounds.Min.y; ViewportLayout->Bounds.Max.y = SliderBounds.Max.y; @@ -1532,7 +1534,7 @@ ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows, u32 Elem s32 ScrollableElements = Max(0, ElementCount - ViewportRows); ui_widget_retained_state* ViewportState = ui_GetOrCreateRetainedState(Interface, ViewportLayout); ViewportState->ChildrenDrawOffset.x = 0; - ViewportState->ChildrenDrawOffset.y = ((1.0f - State->InitialValueR32) * (r32)(ScrollableElements)) * ViewportLayout->RowHeight; + ViewportState->ChildrenDrawOffset.y = ((1.0f - State->InitialValueR32) * (r32)(ScrollableElements + 1)) * ViewportLayout->RowHeight; } internal void diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index ca30811..e1dd6b6 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -889,6 +889,10 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn //if (ui_BeginLabeledDropdown(Interface, MakeString("Selected Pattern"), MakeString(BlockPattern.Name, BlockPattern.NameLength))) if (ui_BeginDropdown(Interface, MakeString(BlockPattern.Name, BlockPattern.NameLength))) { + Interface->ActiveLayout->Bounds.Max.x += 128.0f; + Interface->ActiveLayout->Columns[0].XMax += 128.0f; + + ui_BeginList(Interface, MakeString("Patterns List"), 5, State->Patterns.Count); for (u32 i = 0; i < State->Patterns.Count; i++) { animation_pattern Pattern = State->Patterns.Values[i]; @@ -897,6 +901,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn SelectedBlock->AnimationProcHandle = Patterns_IndexToHandle(i); } } + ui_EndList(Interface); } ui_EndLabeledDropdown(Interface); } diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index d026493..152d86c 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -753,20 +753,7 @@ Pattern_AllGreen(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r3 for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { u32 I = LedIndex + 1; - Leds->Colors[LedIndex] = {}; - if (I % 3 == 0) - { - Leds->Colors[LedIndex].R = 255; - } - else if (I % 3 == 1) - { - Leds->Colors[LedIndex].G = 255; - } - else if (I % 3 == 2) - { - Leds->Colors[LedIndex].B = 255; - } - + Leds->Colors[LedIndex] = {0, 255, 0}; } } From 6b137154bcfd8e17e71b2fc65ed2d954e4e56ce4 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sat, 27 Mar 2021 21:41:47 -0700 Subject: [PATCH 030/151] Animation Playlists, lots of cleanup, settings file stuff, etc. --- .gitignore | 1 + src/app/engine/animation/foldhaus_animation.h | 41 ++++- src/app/foldhaus_app.cpp | 5 + src/app/patterns/blumen_patterns.h | 2 +- src/app/platform_win32/win32_foldhaus.cpp | 67 +++++--- src/app/ss_blumen_lumen/blumen_lumen.cpp | 150 ++++++++++++++---- src/app/ss_blumen_lumen/blumen_lumen.h | 12 +- .../ss_blumen_lumen/blumen_lumen_settings.h | 65 +++++++- src/gs_libs/gs_types.cpp | 26 ++- src/gs_libs/gs_types.h | 3 + 10 files changed, 298 insertions(+), 74 deletions(-) diff --git a/.gitignore b/.gitignore index 4da1bd7..67d2a76 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ meta_run_tree/ process/ reference/ working_data/ +nssm_log.log \ No newline at end of file diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index a9a327d..adac56e 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -160,6 +160,9 @@ struct animation_system animation_array Animations; animation_repeat_mode RepeatMode; + animation_handle_array Playlist; + u32 PlaylistAt; + r32 PlaylistFadeTime; // NOTE(Peter): The frame currently being displayed/processed. you // can see which frame you're on by looking at the time slider on the timeline @@ -621,14 +624,21 @@ AnimationFadeGroup_Update(animation_fade_group* Group, r32 DeltaTime) internal void AnimationFadeGroup_FadeTo(animation_fade_group* Group, animation_handle To, r32 Duration) { - // complete current fade if there is one in progress - if (IsValid(Group->To)) + if (IsValid(Group->From)) { - AnimationFadeGroup_Advance(Group); + // complete current fade if there is one in progress + if (IsValid(Group->To)) + { + AnimationFadeGroup_Advance(Group); + } + + Group->To = To; + Group->FadeDuration = Duration; + } + else + { + Group->From = To; } - - Group->To = To; - Group->FadeDuration = Duration; } // System @@ -750,7 +760,15 @@ AnimationSystem_Update(animation_system* System, r32 DeltaTime) case AnimationRepeat_Loop: { - // TODO(pjs): + Assert(System->Playlist.Count > 0); + u32 NextIndex = System->PlaylistAt; + System->PlaylistAt = (System->PlaylistAt + 1) % System->Playlist.Count; + animation_handle Next = System->Playlist.Handles[NextIndex]; + + AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup, + Next, + System->PlaylistFadeTime); + System->CurrentFrame = 0; }break; InvalidDefaultCase; @@ -759,6 +777,15 @@ AnimationSystem_Update(animation_system* System, r32 DeltaTime) } } +internal void +AnimationSystem_FadeToPlaylist(animation_system* System, animation_handle_array Playlist) +{ + System->Playlist = Playlist; + System->PlaylistAt = 0; + + AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup, Playlist.Handles[0], System->PlaylistFadeTime); +} + inline bool AnimationSystem_NeedsRender(animation_system System) { diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 5519446..13852a5 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -120,6 +120,7 @@ UPDATE_AND_RENDER(UpdateAndRender) // zero the Transient arena when we clear it so it wouldn't be a problem, but it is technically // incorrect to clear the arena, and then access the memory later. ClearArena(State->Transient); + Assert(State->UserSpaceDesc.UserData.Memory != 0); if (State->RunEditor) { @@ -129,6 +130,7 @@ UPDATE_AND_RENDER(UpdateAndRender) AnimationSystem_Update(&State->AnimationSystem, Context->DeltaTime); if (AnimationSystem_NeedsRender(State->AnimationSystem)) { + Assert(State->UserSpaceDesc.UserData.Memory != 0); AnimationSystem_RenderToLedBuffers(&State->AnimationSystem, State->Assemblies, &State->LedSystem, @@ -138,7 +140,9 @@ UPDATE_AND_RENDER(UpdateAndRender) State->UserSpaceDesc.UserData.Memory); } + Assert(State->UserSpaceDesc.UserData.Memory != 0); US_CustomUpdate(&State->UserSpaceDesc, State, Context); + Assert(State->UserSpaceDesc.UserData.Memory != 0); AssemblyDebug_OverrideOutput(State->AssemblyDebugState, State->Assemblies, @@ -149,6 +153,7 @@ UPDATE_AND_RENDER(UpdateAndRender) Editor_Render(State, Context, RenderBuffer); } + Assert(State->UserSpaceDesc.UserData.Memory != 0); BuildAssemblyData(State, *Context, OutputData); } diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 152d86c..4fe9588 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -1065,7 +1065,7 @@ Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 DEBUG_TRACK_FUNCTION; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; - phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % BL_FLOWER_COUNT]; v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index cd1316d..82a98ff 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -556,6 +556,41 @@ Win32_SendOutputData(gs_thread_context ThreadContext, addressed_data_buffer_list } +// Time +internal system_time +Win32GetSystemTime() +{ + system_time Result = {}; + + SYSTEMTIME WinLocalTime; + GetLocalTime(&WinLocalTime); + + SYSTEMTIME WinSysTime; + FILETIME WinSysFileTime; + GetSystemTime(&WinSysTime); + if (SystemTimeToFileTime((const SYSTEMTIME*)&WinSysTime, &WinSysFileTime)) + { + ULARGE_INTEGER SysTime = {}; + SysTime.LowPart = WinSysFileTime.dwLowDateTime; + SysTime.HighPart = WinSysFileTime.dwHighDateTime; + + Result.NanosSinceEpoch = SysTime.QuadPart; + Result.Year = WinLocalTime.wYear; + Result.Month = WinLocalTime.wMonth; + Result.Day = WinLocalTime.wDay; + Result.Hour = WinLocalTime.wHour; + Result.Minute = WinLocalTime.wMinute; + Result.Second = WinLocalTime.wSecond; + } + else + { + u32 Error = GetLastError(); + InvalidCodePath; + } + + return Result; +} + int WINAPI WinMain ( HINSTANCE HInstance, @@ -636,6 +671,8 @@ WinMain ( Context.InitializeApplication(Context); + system_time StartTime = Win32GetSystemTime(); + Running = true; Context.WindowIsVisible = true; while (Running) @@ -647,31 +684,8 @@ WinMain ( DEBUG_TRACK_SCOPE(MainLoop); { - // update system time - SYSTEMTIME WinLocalTime; - GetLocalTime(&WinLocalTime); - - SYSTEMTIME WinSysTime; - FILETIME WinSysFileTime; - GetSystemTime(&WinSysTime); - if (!SystemTimeToFileTime((const SYSTEMTIME*)&WinSysTime, &WinSysFileTime)) - { - u32 Error = GetLastError(); - InvalidCodePath; - } - ULARGE_INTEGER SysTime = {}; - SysTime.LowPart = WinSysFileTime.dwLowDateTime; - SysTime.HighPart = WinSysFileTime.dwHighDateTime; - Context.SystemTime_Last = Context.SystemTime_Current; - - Context.SystemTime_Current.NanosSinceEpoch = SysTime.QuadPart; - Context.SystemTime_Current.Year = WinLocalTime.wYear; - Context.SystemTime_Current.Month = WinLocalTime.wMonth; - Context.SystemTime_Current.Day = WinLocalTime.wDay; - Context.SystemTime_Current.Hour = WinLocalTime.wHour; - Context.SystemTime_Current.Minute = WinLocalTime.wMinute; - Context.SystemTime_Current.Second = WinLocalTime.wSecond; + Context.SystemTime_Current = Win32GetSystemTime(); #define PRINT_SYSTEM_TIME 0 #if PRINT_SYSTEM_TIME @@ -682,6 +696,11 @@ WinMain ( Context.SystemTime_Current.Minute, Context.SystemTime_Current.Second, Context.SystemTime_Current.NanosSinceEpoch); + + u64 NanosElapsed = Context.SystemTime_Current.NanosSinceEpoch - StartTime.NanosSinceEpoch; + r64 SecondsElapsed = (r64)NanosElapsed * NanosToSeconds; + + PrintF(&T, "%lld %f Seconds\n", NanosElapsed, SecondsElapsed); NullTerminate(&T); OutputDebugStringA(T.Str); #endif diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index d596bfc..4b4d224 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -174,6 +174,16 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) CloseSocket(Data->SocketManager, ListenSocket); } +internal void +BlumenLumen_SetPatternMode(bl_pattern_mode Mode, r32 FadeDuration, animation_system* System, blumen_lumen_state* BLState) +{ + BLState->PatternMode = Mode; + animation_handle_array Playlist = BLState->ModeAnimations[Mode]; + System->RepeatMode = AnimationRepeat_Loop; + System->PlaylistFadeTime = FadeDuration; + AnimationSystem_FadeToPlaylist(System, Playlist); +} + internal void BlumenLumen_LoadPatterns(app_state* State) { @@ -298,7 +308,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context); BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); - State->AnimationSystem.ActiveFadeGroup.From = BLState->ModeAnimations[BlumenPattern_Standard].Handles[0]; + BlumenLumen_SetPatternMode(BlumenPattern_Standard, 5, &State->AnimationSystem, BLState); #endif State->AnimationSystem.TimelineShouldAdvance = true; @@ -333,21 +343,16 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) BLState->AssemblyColors[0] = NewHue; BLState->AssemblyColors[1] = NewHue; BLState->AssemblyColors[2] = NewHue; - - animation_handle NewAnim = BLState->ModeAnimations[BlumenPattern_VoiceCommand].Handles[0]; - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, - NewAnim, - VoiceCommandFadeDuration); } else { u32 AssemblyIdx = BLState->LastAssemblyColorSet; BLState->AssemblyColors[AssemblyIdx] = NewHue; + BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; } - BLState->PatternMode = BlumenPattern_VoiceCommand; - // TODO(PS): get current time so we can fade back after - // a while + BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, 5, &State->AnimationSystem, BLState); + BLState->TimeLastSetToVoiceMode = Context->SystemTime_Current; } }break; @@ -361,8 +366,18 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Motor.Temperature = (T[0] << 8 | T[1] << 0); + motor_packet CurrPos = Motor.Pos; motor_packet LastPos = BLState->LastKnownMotorState; DEBUG_ReceivedMotorPositions(LastPos, Motor.Pos, Context->ThreadContext); + + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i]) + { + BLState->LastTimeMotorStateChanged[i] = Context->SystemTime_Current.NanosSinceEpoch; + } + } + BLState->LastKnownMotorState = Motor.Pos; }break; @@ -373,11 +388,11 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (Temp.Temperature > 0) { - BLState->BrightnessPercent = .25f; + BLState->BrightnessPercent = HighTemperatureBrightnessPercent; } else { - BLState->BrightnessPercent = 1.f; + BLState->BrightnessPercent = FullBrightnessPercent; } DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); @@ -387,6 +402,23 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } + // Transition back to standard mode after some time + if (BLState->PatternMode == BlumenPattern_VoiceCommand) + { + u64 LastChangeClock = BLState->TimeLastSetToVoiceMode.NanosSinceEpoch; + u64 NowClocks = Context->SystemTime_Current.NanosSinceEpoch; + s64 NanosSinceChange = NowClocks - LastChangeClock; + r64 SecondsSinceChange = (r64)NanosSinceChange * NanosToSeconds; + + if (SecondsSinceChange > VoiceCommandSustainDuration) + { + BLState->PatternMode = BlumenPattern_Standard; + animation_handle NewAnim = BLState->ModeAnimations[BlumenPattern_Standard].Handles[0]; + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + NewAnim, + VoiceCommandFadeDuration); + } + } // Open / Close the Motor if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue)) @@ -441,38 +473,42 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) DEBUG_SentMotorCommand(MotorCommand.MotorPacket, Context->ThreadContext); } } - // Dim the leds based on temp data - for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) + + // When a motor state changes to being open, wait to turn Upper Leds on + // in order to hide the fact that they are turning off + motor_packet CurrMotorPos = BLState->LastKnownMotorState; + u64 NowNanos = Context->SystemTime_Current.NanosSinceEpoch; + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) { - led_buffer Buffer = State->LedSystem.Buffers[i]; - for (u32 j = 0; j < Buffer.LedCount; j++) + // have to map from "assembly load order" to + // the order that the clear core is referencing the + // motors by + assembly Assembly = State->Assemblies.Values[i]; + u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState); + u8 MotorPos = CurrMotorPos.FlowerPositions[AssemblyCCIndex]; + + if ((MotorPos == MotorState_Open || MotorPos == MotorState_MostlyOpen) && + !BLState->ShouldDimUpperLeds[i]) { - pixel* Color = Buffer.Colors + j; - Color->R = Color->R * BLState->BrightnessPercent; - Color->G = Color->G * BLState->BrightnessPercent; - Color->B = Color->B * BLState->BrightnessPercent; + u64 ChangedNanos = BLState->LastTimeMotorStateChanged[i]; + u64 NanosSinceChanged = NowNanos - ChangedNanos; + r64 SecondsSinceChanged = (r64)NanosSinceChanged * NanosToSeconds; + if (SecondsSinceChanged > TurnUpperLedsOffAfterMotorCloseCommandDelay) + { + BLState->ShouldDimUpperLeds[i] = true; + } } } // NOTE(PS): If the flowers are mostly open or full open // we mask off the top leds to prevent them from overheating // while telescoped inside the flower - motor_packet CurrMotorPos = BLState->LastKnownMotorState; - for (u32 a = 0; a < State->Assemblies.Count; a++) + for (u32 a = 0; a < BL_FLOWER_COUNT; a++) { assembly Assembly = State->Assemblies.Values[a]; - u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState); - - u8 MotorPos = CurrMotorPos.FlowerPositions[AssemblyCCIndex]; - - if (MotorPos == MotorState_Closed || - MotorPos == MotorState_HalfOpen) - { - continue; - } + if (!BLState->ShouldDimUpperLeds[a]) continue; led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; - led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient); for (u32 s = 0; s < TopStrips.Count; s++) { @@ -486,6 +522,22 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } + // Dim the leds based on temp data + if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) + { + for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) + { + led_buffer Buffer = State->LedSystem.Buffers[i]; + for (u32 j = 0; j < Buffer.LedCount; j++) + { + pixel* Color = Buffer.Colors + j; + Color->R = Color->R * BLState->BrightnessPercent; + Color->G = Color->G * BLState->BrightnessPercent; + Color->B = Color->B * BLState->BrightnessPercent; + } + } + } + // Send Status Packet { system_time LastSendTime = BLState->LastStatusUpdateTime; @@ -524,7 +576,7 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) { motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; - for (u32 MotorIndex = 0; MotorIndex < 3; MotorIndex++) + for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) { gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); ui_BeginRow(I, 5); @@ -567,6 +619,40 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); } + motor_packet MotorPos = BLState->LastKnownMotorState; + ui_Label(I, MakeString("Current Motor Positions")); + { + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + ui_BeginRow(I, 2); + gs_string MotorStr = PushStringF(State->Transient, 32, + "Motor %d", + i); + ui_Label(I, MotorStr); + + gs_string StateStr = {}; + switch (MotorPos.FlowerPositions[i]) + { + case MotorState_Closed: { + StateStr = MakeString("Closed"); + } break; + case MotorState_HalfOpen: { + StateStr = MakeString("Half Open"); + } break; + case MotorState_MostlyOpen: { + StateStr = MakeString("Mostly Open"); + } break; + case MotorState_Open: { + StateStr = MakeString("Open"); + } break; + } + + ui_Label(I, StateStr); + ui_EndRow(I); + } + } + + BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); } } diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 1624a75..21d42c3 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -145,11 +145,8 @@ struct blumen_lumen_state mic_listen_job_data MicListenJobData; motor_packet LastKnownMotorState; - - r64 TimeElapsed; - - animation_handle AnimHandles[3]; - u32 CurrAnim; + u64 LastTimeMotorStateChanged[BL_FLOWER_COUNT]; + b8 ShouldDimUpperLeds[BL_FLOWER_COUNT]; // NOTE(pjs): Based on temperature data from weatherman // dim the leds. @@ -158,7 +155,7 @@ struct blumen_lumen_state system_time LastSendTime; - phrase_hue AssemblyColors[3]; + phrase_hue AssemblyColors[BL_FLOWER_COUNT]; u32 LastAssemblyColorSet; // The indices of this array are the index the clear core uses to @@ -171,12 +168,13 @@ struct blumen_lumen_state bl_pattern_mode PatternMode; animation_handle_array ModeAnimations[BlumenPattern_Count]; - u32 CurrentAnimation; phrase_hue_map PhraseHueMap; + system_time TimeLastSetToVoiceMode; // Debug motor_packet DEBUG_PendingMotorPacket; + bool DEBUG_IgnoreWeatherDimmingLeds; }; #include "message_queue.cpp" diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index c254555..8fb514d 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -3,16 +3,35 @@ #ifndef BLUMEN_LUMEN_SETTINGS_H #define BLUMEN_LUMEN_SETTINGS_H +// Hey you never know, might need to change this some day lololol +// The number of flowers in the sculpture. Used to size all sorts of +// arrays. Maybe don't touch this unless you really know what you're doing? +#define BL_FLOWER_COUNT 3 + +// The path to the three flower assembly files +// PS is 90% sure you don't need to touch these ever gs_const_string Flower0AssemblyPath = ConstString("data/ss_blumen_one.fold"); gs_const_string Flower1AssemblyPath = ConstString("data/ss_blumen_two.fold"); gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold"); +// The path to the phrase map CSV. Can be an absolute path, or relative +// to the app_run_tree folder gs_const_string PhraseMapCSVPath = ConstString("data/flower_codes.csv"); char PhraseMapCSVSeparator = ','; +// Search Strings for which folders to find ambient animation files and +// voice animation files in. +// these search patterns should always end in *.foldanim so they only +// return valid animation files gs_const_string AmbientPatternFolder = ConstString("data/blumen_animations/ambient_patterns/*.foldanim"); gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_responses/*.foldanim"); +// The times of day when the motors should be open. +// these are in the format { Start_Hour, Start_Minute, End_Hour, End_Minute } +// Hours are in the range 0-23 inclusive +// Minutes are in the range 0-59 inclusive +// NOTE: There is no need to modify the MotorOpenTimesCount variable - +// it is a compile time constant that gets calculated automatically global time_range MotorOpenTimes[] = { { 00, 30, 00, 40 }, { 00, 50, 01, 00 }, @@ -59,8 +78,52 @@ global time_range MotorOpenTimes[] = { { 14, 30, 14, 40 }, { 14, 50, 15, 00 }, }; -global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); +global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit +// How long it takes to fade from the default pattern to the +// voice activated pattern r32 VoiceCommandFadeDuration = 1.0f; // in seconds +// How long the voice activated pattern will remain active +// without additional voice commands, before fading back to +// default behaviour. +// ie. +// if this is set to 30 seconds, upon receiving a voice command +// lumenarium will fade to the requested pattern/color palette +// and then wait 30 seconds before fading back to the original +// pattern. If, in that 30 second window, another voice command +// is issued, lumenarium will reset the 30 second counter. +r64 VoiceCommandSustainDuration = 30.0; // in seconds + +// When we send a Motor Close command, we don't want the upper leds to +// immediately turn off. Instead, we want to wait until the flower is +// at least some of the way closed. This variable dictates how long +// we wait for. +// For example: +// 1. We send a 'motor close' command to the clear core +// 2. the clear core sends back a 'motor closed' state packet +// 3. We begin a timer +// 4. When the timer reaches the value set in this variable, +// we turn the upper leds off. +// +// NOTE: This is not a symmetric operation. When we send a 'motor open' +// command, we want to immediately turn the upper leds on so they appear +// to have been on the whole time. +r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 5.0; // in seconds + +// NOTE: Temperature & Time of Day Based Led Brightness Settings + +// The percent brightness we set leds to during high temperatures. +// A value in the range 0:1 inclusive +// This is multiplied by each pixels R, G, & B channels before being +// sent. So if it is set to .1f, then the maximum brightness value sent +// to any channel of any pixel will be 25 (255 * .1 = 25). +r32 HighTemperatureBrightnessPercent = .25f; + +// The percent brightness we set leds to when no other conditions apply +// A value in the range 0:1 inclusive. +// Probably wants to be something high like 1 but we might want to +// lower it for heat reasons? +r32 FullBrightnessPercent = 1.0f; + #endif //BLUMEN_LUMEN_SETTINGS_H diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index 64dec4a..8a6d074 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -2709,8 +2709,30 @@ PushSize_(gs_memory_arena* Arena, u64 Size, char* Location) { CursorEntry = MemoryArenaNewCursor(Arena, Size, Location); } - Assert(CursorEntry); - Assert(CursorHasRoom(CursorEntry->Cursor, Size)); + + if (!CursorEntry || !CursorHasRoom(CursorEntry->Cursor, Size)) + { + __debugbreak(); + + CursorEntry = 0; + for (u64 i = 0; + i < Arena->CursorsCount; + i++) + { + gs_memory_cursor_list* At = Arena->Cursors + i; + if (CursorHasRoom(At->Cursor, Size)) + { + CursorEntry = At; + break; + } + } + if (!CursorEntry) + { + CursorEntry = MemoryArenaNewCursor(Arena, Size, Location); + } + } + //Assert(CursorEntry); + //Assert(CursorHasRoom(CursorEntry->Cursor, Size)); #else gs_memory_cursor_list* CursorEntry = Arena->CursorList; diff --git a/src/gs_libs/gs_types.h b/src/gs_libs/gs_types.h index 293dbf2..49513f0 100644 --- a/src/gs_libs/gs_types.h +++ b/src/gs_libs/gs_types.h @@ -137,6 +137,9 @@ global_const r64 MinR64 = -MaxR64; global_const r64 SmallestPositiveR64 = 4.94065645841247e-324; global_const r64 EpsilonR64 = 1.11022302462515650e-16; +global_const r64 NanosToSeconds = 1 / 10000000.0; +global_const r64 SecondsToNanos = 10000000.0; + // TODO: va_start and va_arg replacements internal r32 From 963415335bd93fb73c7225494ea023f5cfc6fd46 Mon Sep 17 00:00:00 2001 From: PS Date: Tue, 30 Mar 2021 19:55:54 -0700 Subject: [PATCH 031/151] shipping time --- src/app/editor/foldhaus_editor.cpp | 2 - src/app/editor/interface.h | 5 +- .../foldhaus_panel_animation_timeline.h | 203 +++-- src/app/engine/animation/foldhaus_animation.h | 29 +- .../animation/foldhaus_animation_renderer.cpp | 6 +- .../foldhaus_animation_serializer.cpp | 13 +- .../assembly/foldhaus_assembly_parser.cpp | 2 +- src/app/foldhaus_app.cpp | 65 +- src/app/foldhaus_app.h | 1 + src/app/foldhaus_platform.h | 6 +- src/app/patterns/blumen_patterns.h | 718 ++++++------------ src/app/platform_win32/win32_foldhaus.cpp | 86 ++- src/app/ss_blumen_lumen/blumen_lumen.cpp | 69 +- src/app/ss_blumen_lumen/blumen_lumen.h | 22 + .../ss_blumen_lumen/blumen_lumen_settings.h | 4 + src/app/ss_blumen_lumen/sdf.h | 20 + src/sculpture_gen/gen_blumen_lumen.cpp | 20 +- 17 files changed, 581 insertions(+), 690 deletions(-) create mode 100644 src/app/ss_blumen_lumen/sdf.h diff --git a/src/app/editor/foldhaus_editor.cpp b/src/app/editor/foldhaus_editor.cpp index d6abf2e..3764b25 100644 --- a/src/app/editor/foldhaus_editor.cpp +++ b/src/app/editor/foldhaus_editor.cpp @@ -149,8 +149,6 @@ Editor_Render(app_state* State, context* Context, render_command_buffer* RenderB } Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext); - ResetWorkQueue(Context->GeneralWorkQueue); - } diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index 54a956e..ee3c9d6 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -1534,7 +1534,10 @@ ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows, u32 Elem s32 ScrollableElements = Max(0, ElementCount - ViewportRows); ui_widget_retained_state* ViewportState = ui_GetOrCreateRetainedState(Interface, ViewportLayout); ViewportState->ChildrenDrawOffset.x = 0; - ViewportState->ChildrenDrawOffset.y = ((1.0f - State->InitialValueR32) * (r32)(ScrollableElements + 1)) * ViewportLayout->RowHeight; + r32 BaseOffset = Rect2Height(ViewportLayout->Bounds) - ViewportLayout->RowHeight; + r32 ScrollPct = 1.0 - State->InitialValueR32; + r32 ScrollOffset = ScrollPct * ViewportLayout->RowHeight * ScrollableElements; + ViewportState->ChildrenDrawOffset.y = BaseOffset + ScrollOffset; } internal void diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index e1dd6b6..822cf58 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -19,6 +19,12 @@ struct animation_timeline_state animation_handle NextActiveAnim; }; +internal void +AnimationTimeline_SelectLayer(animation_timeline_state* TLState, s32 Layer) +{ + TLState->SelectedAnimationLayer = Layer; +} + inline u32 GetFrameFromPointInAnimationPanel(v2 Point, rect2 PanelBounds, frame_range VisibleRange) { @@ -266,6 +272,11 @@ AnimationTimeline_AddAnimationBlockCommand(animation_timeline_state* TimelineSta { animation_pattern_handle PatternHandle = Patterns_IndexToHandle(0); s32 Layer = TimelineState->SelectedAnimationLayer; + if (Layer < 0) + { + Layer = Animation_AddLayer(ActiveAnim, MakeString("[New Layer]"), BlendMode_Add, &State->AnimationSystem); + AnimationTimeline_SelectLayer(TimelineState, Layer); + } Assert(Layer >= 0); handle NewBlockHandle = Animation_AddBlock(ActiveAnim, StartFrame, EndFrame, PatternHandle, Layer); @@ -472,33 +483,6 @@ DrawLayerMenu(animation_system* AnimationSystem, animation ActiveAnim, ui_interf } } -internal rect2 -DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, frame_range VisibleFrames, rect2 TimelineBounds, render_command_buffer* RenderBuffer) -{ - rect2 BlockBounds = {}; - - r32 TimelineWidth = Rect2Width(TimelineBounds); - - u32 ClampedBlockStartFrame = ClampFrameToRange(AnimationBlock.Range.Min, VisibleFrames); - r32 StartFramePercent = FrameToPercentRange(ClampedBlockStartFrame, VisibleFrames); - r32 StartPosition = TimelineWidth * StartFramePercent; - - u32 ClampedBlockEndFrame = ClampFrameToRange(AnimationBlock.Range.Max, VisibleFrames); - r32 EndFramePercent = FrameToPercentRange(ClampedBlockEndFrame, VisibleFrames); - r32 EndPosition = TimelineWidth * EndFramePercent; - - r32 LayerYOffset = LAYER_HEIGHT * AnimationBlock.Layer; - BlockBounds.Min = TimelineBounds.Min + v2{StartPosition, LayerYOffset}; - BlockBounds.Max = TimelineBounds.Min + v2{EndPosition, LayerYOffset + LAYER_HEIGHT}; - - PushRenderQuad2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, BlockColor); - PushRenderBoundingBox2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, 1, WhiteV4); - - // TODO(pjs): If mouse is on one of the border hot spots, render an off colored square to signal the region is hot - - return BlockBounds; -} - PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) { Assert(ReturningFrom->TypeIndex == PanelType_FileView); @@ -518,6 +502,8 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) internal void PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { + DEBUG_TRACK_FUNCTION; + animation_system* AnimSystem = &State->AnimationSystem; ui_interface* Interface = &State->Interface; ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("PlayBar Layout")); @@ -548,6 +534,8 @@ PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Pan internal void FrameCount_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { + DEBUG_TRACK_FUNCTION; + ui_interface* Interface = &State->Interface; gs_string TempString = PushString(State->Transient, 256); @@ -627,6 +615,7 @@ LayerList_DrawLayerButton (ui_interface* Interface, gs_string Name, rect2 Bounds internal void LayerList_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { + DEBUG_TRACK_FUNCTION; ui_interface* Interface = &State->Interface; ui_FillRect(Interface, Bounds, Interface->Style.PanelBG); @@ -658,9 +647,49 @@ LayerList_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, } } +internal void +TimeRange_RenderBlock (rect2 BlockBounds, u32 BlockIndex, animation* ActiveAnim, handle SelectedBlockHandle, ui_interface* Interface, render_command_buffer* RenderBuffer) +{ + v4 BlockColor = BlackV4; + if (SelectedBlockHandle.Index == BlockIndex && SelectedBlockHandle.Generation == ActiveAnim->Blocks_.Generations[BlockIndex]) + { + BlockColor = TealV4; + } + + PushRenderQuad2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, BlockColor); + PushRenderBoundingBox2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, 1, WhiteV4); +} + +struct block_bounds_lut_entry +{ + rect2 Bounds; + u32 Index; +}; + +internal void +TimeRange_RenderBlockArray(block_bounds_lut_entry* Blocks, u32* LUT, u32 LUTCount, r32 HeightOffset, animation* ActiveAnim, handle SelectedBlockHandle, handle* DragBlockHandle, ui_interface* Interface, render_command_buffer* RenderBuffer) +{ + for (u32 i = 0; i < LUTCount; i++) + { + u32 BlockBoundsIndex = LUT[i]; + block_bounds_lut_entry Block = Blocks[BlockBoundsIndex]; + Block.Bounds.Max.y += HeightOffset; + + TimeRange_RenderBlock(Block.Bounds, Block.Index, ActiveAnim, SelectedBlockHandle, Interface, RenderBuffer); + + if (PointIsInRect(Block.Bounds, Interface->Mouse.Pos)) + { + DragBlockHandle->Index = Block.Index; + DragBlockHandle->Generation = ActiveAnim->Blocks_.Generations[Block.Index]; + } + } +} + internal void TimeRange_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { + DEBUG_TRACK_FUNCTION; + ui_interface* Interface = &State->Interface; // TODO(pjs): setting the timeline to show the entire range @@ -682,32 +711,108 @@ TimeRange_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, handle DragBlockHandle = {0}; if (ActiveAnim) { - for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++) + u32 BlocksCountMax = ActiveAnim->Blocks_.Count; + u32 BlocksCount = 0; + block_bounds_lut_entry* Blocks = PushArray(State->Transient, block_bounds_lut_entry, BlocksCountMax); + + u32 FrontBlocksCount = 0; + u32* FrontBlocks = PushArray(State->Transient, u32, BlocksCountMax); + u32 BackBlocksCount = 0; + u32* BackBlocks = PushArray(State->Transient, u32, BlocksCountMax); + + for (u32 l = 0; l < ActiveAnim->Layers.Count; l++) { - animation_block* AnimationBlockAt = ActiveAnim->Blocks_.Values + i; + BlocksCount = 0; + FrontBlocksCount = 0; + BackBlocksCount = 0; - // If either end is in the range, we should draw it - b32 RangeIsVisible = (FrameIsInRange(ViewRange, AnimationBlockAt->Range.Min) || - FrameIsInRange(ViewRange, AnimationBlockAt->Range.Max)); - // If neither end is in the range, but the ends surround the visible range, - // we should still draw it. - RangeIsVisible |= (AnimationBlockAt->Range.Min <= ViewRange.Min && - AnimationBlockAt->Range.Max>= ViewRange.Max); - if (RangeIsVisible) + for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++) { - v4 BlockColor = BlackV4; - if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == ActiveAnim->Blocks_.Generations[i]) - { - BlockColor = PinkV4; - } - rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, ViewRange, Bounds, Interface->RenderBuffer); + animation_block* AnimationBlockAt = ActiveAnim->Blocks_.Values + i; + if (AnimationBlockAt->Layer != l) continue; - if (PointIsInRect(BlockBounds, Interface->Mouse.Pos)) + // If either end is in the range, we should draw it + b32 RangeIsVisible = (FrameIsInRange(ViewRange, AnimationBlockAt->Range.Min) || + FrameIsInRange(ViewRange, AnimationBlockAt->Range.Max)); + // If neither end is in the range, but the ends surround the visible range, + // we should still draw it. + RangeIsVisible |= (AnimationBlockAt->Range.Min <= ViewRange.Min && + AnimationBlockAt->Range.Max>= ViewRange.Max); + if (!RangeIsVisible) continue; + + r32 TimelineWidth = Rect2Width(Bounds); + + frame_range BlockAtRange = AnimationBlockAt->Range; + u32 ClampedBlockStartFrame = ClampFrameToRange(BlockAtRange.Min, ViewRange); + r32 StartFramePercent = FrameToPercentRange(ClampedBlockStartFrame, ViewRange); + r32 StartPosition = TimelineWidth * StartFramePercent; + + u32 ClampedBlockEndFrame = ClampFrameToRange(BlockAtRange.Max, ViewRange); + r32 EndFramePercent = FrameToPercentRange(ClampedBlockEndFrame, ViewRange); + r32 EndPosition = TimelineWidth * EndFramePercent; + + r32 LayerYOffset = LAYER_HEIGHT * AnimationBlockAt->Layer; + + rect2 NewBlockBounds = {}; + NewBlockBounds.Min = Bounds.Min + v2{StartPosition, LayerYOffset}; + NewBlockBounds.Max = Bounds.Min + v2{EndPosition, LayerYOffset + LAYER_HEIGHT}; + + block_bounds_lut_entry NewBlock = {}; + NewBlock.Bounds = NewBlockBounds; + NewBlock.Index = i; + + // fast (implementation-wise) insert sort. + // TODO(PS): probably not great + for (u32 j = 0; j < BlocksCount; j++) { - DragBlockHandle.Index = i; - DragBlockHandle.Generation = ActiveAnim->Blocks_.Generations[i]; + if (Blocks[j].Bounds.Min.x > NewBlock.Bounds.Min.x) + { + block_bounds_lut_entry Old = Blocks[j]; + Blocks[j] = NewBlock; + NewBlock = Old; + } + } + Blocks[BlocksCount++] = NewBlock; + } + + // BlockBounds are sorted by their render bounds from left to right + // This iterates over them to see if any on the same layer overlap, and if + // so, shrinks one of them, putting it in a new list + for (u32 i = 0; i < BlocksCount; i++) + { + if (i % 2 == 0) + { + BackBlocks[BackBlocksCount++] = i; + continue; + } + + bool ShortCandidate = false; + block_bounds_lut_entry Block = Blocks[i]; + if (i > 0) + { + block_bounds_lut_entry PrevBlock = Blocks[i - 1]; + rect2 Union = Rect2Union(PrevBlock.Bounds, Block.Bounds); + ShortCandidate |= Rect2Width(Union) > 0; + } + if (i < BlocksCount - 1) + { + block_bounds_lut_entry NextBlock = Blocks[i + 1]; + rect2 Union = Rect2Union(NextBlock.Bounds, Block.Bounds); + ShortCandidate |= Rect2Width(Union) > 0; + } + + if (ShortCandidate) + { + FrontBlocks[FrontBlocksCount++] = i; + } + else + { + BackBlocks[BackBlocksCount++] = i; } } + + TimeRange_RenderBlockArray(Blocks, BackBlocks, BackBlocksCount, 0, ActiveAnim, SelectedBlockHandle, &DragBlockHandle, Interface, RenderBuffer); + TimeRange_RenderBlockArray(Blocks, FrontBlocks, FrontBlocksCount, -15, ActiveAnim, SelectedBlockHandle, &DragBlockHandle, Interface, RenderBuffer); } } @@ -770,6 +875,7 @@ AnimationTimeline_SetActiveAnimation (animation_handle Handle, animation_timelin internal void AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { + DEBUG_TRACK_FUNCTION; animation_system* AnimSystem = &State->AnimationSystem; animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From; @@ -884,9 +990,8 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn { animation_pattern BlockPattern = Patterns_GetPattern(State->Patterns, SelectedBlock->AnimationProcHandle); - ui_BeginRow(Interface, 3); + ui_BeginRow(Interface, 2); ui_Label(Interface, MakeString("Selected Pattern")); - //if (ui_BeginLabeledDropdown(Interface, MakeString("Selected Pattern"), MakeString(BlockPattern.Name, BlockPattern.NameLength))) if (ui_BeginDropdown(Interface, MakeString(BlockPattern.Name, BlockPattern.NameLength))) { Interface->ActiveLayout->Bounds.Max.x += 128.0f; @@ -925,6 +1030,8 @@ GSMetaTag(panel_type_animation_timeline); internal void AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { + DEBUG_TRACK_FUNCTION; + animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state); animation* ActiveAnim = 0; diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index adac56e..4bcad4a 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -291,6 +291,13 @@ Patterns_IndexToHandle(s32 Index) return Result; } +internal bool +IsValid(animation_pattern_handle Handle) +{ + bool Result = Handle.IndexPlusOne > 0; + return Result; +} + internal animation_pattern Patterns_GetPattern(animation_pattern_array Patterns, animation_pattern_handle Handle) { @@ -312,7 +319,7 @@ internal animation_block_array AnimBlockArray_Create(gs_memory_arena* Storage, u32 CountMax) { animation_block_array Result = {0}; - Result.CountMax = CountMax; + Result.CountMax = Max(CountMax, 32); Result.Values = PushArray(Storage, animation_block, Result.CountMax); Result.Generations = PushArray(Storage, u32, Result.CountMax); return Result; @@ -359,7 +366,7 @@ internal anim_layer_array AnimLayerArray_Create(gs_memory_arena* Storage, u32 CountMax) { anim_layer_array Result = {0}; - Result.CountMax = CountMax; + Result.CountMax = Max(CountMax, 32); Result.Values = PushArray(Storage, anim_layer, Result.CountMax); return Result; } @@ -409,6 +416,8 @@ AnimationArray_Push(animation_array* Array, animation Value) internal animation* AnimationArray_Get(animation_array Array, animation_handle Handle) { + DEBUG_TRACK_FUNCTION; + animation* Result = 0; if (IsValid(Handle) && Handle.Index < (s32)Array.Count) { @@ -541,6 +550,13 @@ SecondsToFrames(r32 Seconds, animation_system System) return Result; } +inline frame_range +FrameRange_Overlap(frame_range A, frame_range B) +{ + frame_range Result = {}; + +} + inline bool FrameIsInRange(frame_range Range, s32 Frame) { @@ -679,6 +695,8 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, animation* Animation, gs_memory_arena* Arena) { + DEBUG_TRACK_FUNCTION; + animation_frame Result = {0}; Result.LayersCount = Animation->Layers.Count; Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount); @@ -793,5 +811,12 @@ AnimationSystem_NeedsRender(animation_system System) return Result; } +inline r32 +AnimationSystem_GetCurrentTime(animation_system System) +{ + r32 Result = System.CurrentFrame * System.SecondsPerFrame; + return Result; +} + #define FOLDHAUS_ANIMATION #endif // FOLDHAUS_ANIMATION \ No newline at end of file diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index f43184b..208c87a 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -122,6 +122,7 @@ AnimationSystem_BeginRenderBlockToLedBuffer(animation_system* System, animation_ r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame; animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle); + Assert(Pattern.Proc); if (System->Multithreaded && Pattern.Multithreaded) { @@ -182,6 +183,8 @@ RenderAnimationToLedBuffer (animation_system* System, gs_memory_arena* Transient, context Context) { + DEBUG_TRACK_FUNCTION; + led_buffer AccBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); // Create the LayerLEDBuffers @@ -266,8 +269,7 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse { DEBUG_TRACK_FUNCTION; - s32 CurrentFrame = System->CurrentFrame; - r32 FrameTime = CurrentFrame * System->SecondsPerFrame; + r32 FrameTime = AnimationSystem_GetCurrentTime(*System); #if 1 animation_array Animations = System->Animations; diff --git a/src/app/engine/animation/foldhaus_animation_serializer.cpp b/src/app/engine/animation/foldhaus_animation_serializer.cpp index 577e84b..55a4422 100644 --- a/src/app/engine/animation/foldhaus_animation_serializer.cpp +++ b/src/app/engine/animation/foldhaus_animation_serializer.cpp @@ -82,12 +82,10 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array { Result.Name = Parser_ReadStringValue(&Parser, AnimField_AnimName); - Result.Layers.CountMax = Parser_ReadU32Value(&Parser, AnimField_LayersCount); - Result.Layers.Values = PushArray(Arena, anim_layer, Result.Layers.CountMax); - - Result.Blocks_.CountMax = Parser_ReadU32Value(&Parser, AnimField_BlocksCount); - Result.Blocks_.Generations = PushArray(Arena, u32, Result.Blocks_.CountMax); - Result.Blocks_.Values = PushArray(Arena, animation_block, Result.Blocks_.CountMax); + u32 LayersNeeded = Parser_ReadU32Value(&Parser, AnimField_LayersCount); + u32 BlocksNeeded = Parser_ReadU32Value(&Parser, AnimField_BlocksCount); + Result.Layers = AnimLayerArray_Create(Arena, LayersNeeded); + Result.Blocks_ = AnimBlockArray_Create(Arena, BlocksNeeded); if (Parser_ReadOpenStruct(&Parser, AnimField_PlayableRange)) { @@ -169,7 +167,7 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array break; } } - + Assert(IsValid(Block.AnimationProcHandle)); if (Parser_ReadCloseStruct(&Parser)) { AnimBlockArray_Push(&Result.Blocks_, Block); @@ -200,6 +198,7 @@ AnimationSystem_LoadAnimationFromFile(animation_system* System, animation_patter { animation NewAnim = AnimParser_Parse(AnimFile.Data, System->Storage, AnimPatterns); NewAnim.FileInfo = AnimFile.FileInfo; + NewAnim.FileInfo.Path = PushStringF(System->Storage, AnimFile.FileInfo.Path.Length, "%S", AnimFile.FileInfo.Path).ConstString; NewAnimHandle = AnimationArray_Push(&System->Animations, NewAnim); } return NewAnimHandle; diff --git a/src/app/engine/assembly/foldhaus_assembly_parser.cpp b/src/app/engine/assembly/foldhaus_assembly_parser.cpp index 5024fb6..dbeb890 100644 --- a/src/app/engine/assembly/foldhaus_assembly_parser.cpp +++ b/src/app/engine/assembly/foldhaus_assembly_parser.cpp @@ -276,7 +276,7 @@ ParseAssemblyFile(assembly* Assembly, gs_const_string FileName, gs_string FileTe Assembly->Name = Parser_ReadStringValue(&Parser, AssemblyField_AssemblyName); Assembly->Scale = Parser_ReadR32Value(&Parser, AssemblyField_AssemblyScale); - Assembly->Center = Parser_ReadV3Value(&Parser, AssemblyField_AssemblyCenter); + Assembly->Center = Parser_ReadV3Value(&Parser, AssemblyField_AssemblyCenter) * Assembly->Scale; Assembly->StripCount = Parser_ReadU32Value(&Parser, AssemblyField_LedStripCount); Assembly->Strips = PushArray(&Assembly->Arena, v2_strip, Assembly->StripCount); diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 13852a5..dc19ab6 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -10,13 +10,22 @@ RELOAD_STATIC_DATA(ReloadStaticData) { - app_state* State = (app_state*)Context.MemoryBase; - GlobalDebugServices = DebugServices; - State->PanelSystem.PanelDefs = GlobalPanelDefs; - State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount; - US_LoadPatterns(&State->UserSpaceDesc, State, Context); + if (AppReady) + { + app_state* State = (app_state*)Context.MemoryBase; + State->PanelSystem.PanelDefs = GlobalPanelDefs; + State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount; + + gs_data UserData = State->UserSpaceDesc.UserData; + State->UserSpaceDesc = BlumenLumen_UserSpaceCreate(); + if (UserData.Memory && !State->UserSpaceDesc.UserData.Memory) + { + State->UserSpaceDesc.UserData = UserData; + } + US_LoadPatterns(&State->UserSpaceDesc, State, Context); + } } INITIALIZE_APPLICATION(InitializeApplication) @@ -38,19 +47,25 @@ INITIALIZE_APPLICATION(InitializeApplication) AnimSysDesc.SecondsPerFrame = 1.0f / 24.0f; State->AnimationSystem = AnimationSystem_Init(AnimSysDesc); - interface_config IConfig = {0}; - IConfig.FontSize = 14; - IConfig.PanelBG = v4{ .3f, .3f, .3f, 1.f }; - IConfig.ButtonColor_Inactive = BlackV4; - IConfig.ButtonColor_Active = v4{ .1f, .1f, .1f, 1.f }; - IConfig.ButtonColor_Selected = v4{ .3f, .3f, .3f, 1.f }; - IConfig.TextColor = WhiteV4; - IConfig.ListBGColors[0] = v4{ .16f, .16f, .16f, 1.f }; - IConfig.ListBGColors[1] = v4{ .18f, .18f, .18f, 1.f }; - IConfig.ListBGHover = v4{ .22f, .22f, .22f, 1.f }; - IConfig.ListBGSelected = v4{ .44f, .44f, .44f, 1.f }; - IConfig.Margin = v2{5, 5}; - State->Interface = ui_InterfaceCreate(Context, IConfig, &State->Permanent); + if (!Context.Headless) + { + interface_config IConfig = {0}; + IConfig.FontSize = 14; + IConfig.PanelBG = v4{ .3f, .3f, .3f, 1.f }; + IConfig.ButtonColor_Inactive = BlackV4; + IConfig.ButtonColor_Active = v4{ .1f, .1f, .1f, 1.f }; + IConfig.ButtonColor_Selected = v4{ .3f, .3f, .3f, 1.f }; + IConfig.TextColor = WhiteV4; + IConfig.ListBGColors[0] = v4{ .16f, .16f, .16f, 1.f }; + IConfig.ListBGColors[1] = v4{ .18f, .18f, .18f, 1.f }; + IConfig.ListBGHover = v4{ .22f, .22f, .22f, 1.f }; + IConfig.ListBGSelected = v4{ .44f, .44f, .44f, 1.f }; + IConfig.Margin = v2{5, 5}; + State->Interface = ui_InterfaceCreate(Context, IConfig, &State->Permanent); + + PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); + + } State->SACN = SACN_Initialize(Context); @@ -59,17 +74,14 @@ INITIALIZE_APPLICATION(InitializeApplication) State->AssemblyDebugState.Brightness = 255; State->AssemblyDebugState.Override = ADS_Override_None; - GlobalDebugServices->Interface.RenderSculpture = true; - - PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); - State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext); - State->UserSpaceDesc = BlumenLumen_UserSpaceCreate(); - - ReloadStaticData(Context, GlobalDebugServices); + ReloadStaticData(Context, GlobalDebugServices, true); US_CustomInit(&State->UserSpaceDesc, State, Context); + GlobalDebugServices->Interface.RenderSculpture = true; + + if (!Context.Headless) { // NOTE(pjs): This just sets up the default panel layout panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context); @@ -92,7 +104,7 @@ INITIALIZE_APPLICATION(InitializeApplication) } - State->RunEditor = true; + State->RunEditor = !Context.Headless; } internal void @@ -152,6 +164,7 @@ UPDATE_AND_RENDER(UpdateAndRender) { Editor_Render(State, Context, RenderBuffer); } + ResetWorkQueue(Context->GeneralWorkQueue); Assert(State->UserSpaceDesc.UserData.Memory != 0); BuildAssemblyData(State, *Context, OutputData); diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index f9813fe..565b797 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -95,6 +95,7 @@ LoadAssembly(gs_const_string Path, app_state* State, context Context) #include "engine/user_space.cpp" +#include "ss_blumen_lumen/sdf.h" #include "patterns/blumen_patterns.h" #include "ss_blumen_lumen/blumen_lumen.cpp" diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index dcfafc8..1009001 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -74,7 +74,7 @@ typedef INITIALIZE_APPLICATION(initialize_application); #define UPDATE_AND_RENDER(name) void name(context* Context, input_queue InputQueue, render_command_buffer* RenderBuffer, addressed_data_buffer_list* OutputData) typedef UPDATE_AND_RENDER(update_and_render); -#define RELOAD_STATIC_DATA(name) void name(context Context, debug_services* DebugServices) +#define RELOAD_STATIC_DATA(name) void name(context Context, debug_services* DebugServices, bool AppReady) typedef RELOAD_STATIC_DATA(reload_static_data); #define CLEANUP_APPLICATION(name) void name(context Context, addressed_data_buffer_list* OutputData) @@ -213,6 +213,7 @@ struct context b32 WindowIsVisible; rect2 WindowBounds; + r64 TotalTime; r32 DeltaTime; mouse_state Mouse; @@ -236,6 +237,9 @@ struct context system_time SystemTime_Last; system_time SystemTime_Current; + + // + bool Headless; }; #define FOLDHAUS_PLATFORM_H diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 4fe9588..62b913f 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -286,9 +286,9 @@ Fbm3D(v3 P, r32 T) F += 0.500000f * Noise3D(Pp + Tt); Pp = Pp * 2.02; F += 0.031250f * Noise3D(Pp); Pp = Pp * 2.01; - F += 0.250000f * Noise3D(Pp); Pp = Pp * 2.03; + F += 0.250000f * Noise3D(Pp - Tt); Pp = Pp * 2.03; F += 0.125000f * Noise3D(Pp); Pp = Pp * 2.01; - F += 0.062500f * Noise3D(Pp); Pp = Pp * 2.04; + F += 0.062500f * Noise3D(Pp + Tt); Pp = Pp * 2.04; F += 0.015625f * Noise3D(Pp + Tv); F = F / 0.984375f; @@ -319,49 +319,6 @@ Voronoise(v2 P, r32 U, r32 V) return A.x / A.y; } -v4 FlowerAColors[FLOWER_COLORS_COUNT] = { - { 232 / 255.f, 219 / 255.f, 88 / 255.f }, - { 232 / 255.f, 219 / 255.f, 88 / 255.f }, - { 232 / 255.f, 219 / 255.f, 88 / 255.f }, - { 147 / 255.f, 75 / 255.f, 176 / 255.f }, - { 193 / 255.f, 187 / 255.f, 197 / 255.f }, - { 223 / 255.f, 190 / 255.f, 49 / 255.f }, - { 198 / 255.f, 76 / 255.f, 65 / 255.f }, - { 198 / 255.f, 76 / 255.f, 65 / 255.f }, - { 198 / 255.f, 76 / 255.f, 65 / 255.f }, - { 226 / 255.f, 200 / 255.f, 17 / 255.f }, - { 116 / 255.f, 126 / 255.f, 39 / 255.f }, - { 61 / 255.f, 62 / 255.f, 31 / 255.f } -}; -v4 FlowerBColors[FLOWER_COLORS_COUNT] = { - { 62 / 255.f, 56 / 255.f, 139 / 255.f }, - { 93 / 255.f, 87 / 255.f, 164 / 255.f }, - { 93 / 255.f, 87 / 255.f, 164 / 255.f }, - { 93 / 255.f, 87 / 255.f, 164 / 255.f }, - { 155 / 255.f, 140 / 255.f, 184 / 255.f }, - { 191 / 255.f, 201 / 255.f, 204 / 255.f }, - { 45 / 255.f, 31 / 255.f, 116 / 255.f }, - { 201 / 255.f, 196 / 255.f, 156 / 255.f }, - { 191 / 255.f, 175 / 255.f, 109 / 255.f }, - { 186 / 255.f, 176 / 255.f, 107 / 255.f }, - { 89 / 255.f, 77 / 255.f, 17 / 255.f }, - { 47 / 255.f, 49 / 255.f, 18 / 255.f }, -}; -v4 FlowerCColors[FLOWER_COLORS_COUNT] = { - { 220 / 255.f, 217 / 255.f, 210 / 255.f }, - { 220 / 255.f, 217 / 255.f, 210 / 255.f }, - { 220 / 255.f, 217 / 255.f, 210 / 255.f }, - { 225 / 255.f, 193 / 255.f, 110 / 255.f }, - { 225 / 255.f, 193 / 255.f, 110 / 255.f }, - { 227 / 255.f, 221 / 255.f, 214 / 255.f }, - { 227 / 255.f, 221 / 255.f, 214 / 255.f }, - { 230 / 255.f, 218 / 255.f, 187 / 255.f }, - { 230 / 255.f, 218 / 255.f, 187 / 255.f }, - { 172 / 255.f, 190 / 255.f, 211 / 255.f }, - { 172 / 255.f, 190 / 255.f, 211 / 255.f }, - { 172 / 255.f, 190 / 255.f, 211 / 255.f }, -}; - internal pixel V4ToRGBPixel(v4 C) { @@ -410,166 +367,6 @@ GetColor(v4* Colors, u32 ColorsCount, r32 Percent) return Result; } -internal void -SolidColorPattern(led_buffer* Leds, led_buffer_range Range, pixel Color) -{ - for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) - { - Leds->Colors[LedIndex] = Color; - } -} - -internal void -Pattern_Blue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - pixel Blue = pixel{0, 0, 255}; - SolidColorPattern(Leds, Range, Blue); -} - -internal void -Pattern_Green(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - pixel Green = pixel{0, 255, 0}; - SolidColorPattern(Leds, Range, Green); -} - -internal void -Pattern_FlowerColors(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - r32 CycleTime = 10; - r32 CyclePercent = ModR32(Time, CycleTime) / CycleTime; - - v4 CA = GetColor(FlowerAColors, FLOWER_COLORS_COUNT, CyclePercent); - v4 CB = GetColor(FlowerAColors, FLOWER_COLORS_COUNT, 1.0f - CyclePercent); - - for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) - { - v4 P = Leds->Positions[LedIndex]; - r32 Pct = (Abs(ModR32(P.y, 150) / 150) + CycleTime) * PiR32; - - r32 APct = RemapR32(SinR32(Pct), -1, 1, 0, 1); - Leds->Colors[LedIndex] = PixelMix(APct, CA, CB); - } -} - -internal void -TestPatternOne(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - led_strip_list BlumenStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("assembly"), ConstString("Blumen Lumen"), Transient); - led_strip_list RadiaStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("assembly"), ConstString("Radialumia"), Transient); - - for (u32 i = 0; i < BlumenStrips.Count; i++) - { - u32 StripIndex = BlumenStrips.StripIndices[i]; - v2_strip StripAt = Assembly.Strips[StripIndex]; - - for (u32 j = 0; j < StripAt.LedCount; j++) - { - u32 LedIndex = StripAt.LedLUT[j]; - Leds->Colors[LedIndex] = { 255, 0, 0 }; - - } - } - - for (u32 i = 0; i < RadiaStrips.Count; i++) - { - u32 StripIndex = RadiaStrips.StripIndices[i]; - v2_strip StripAt = Assembly.Strips[StripIndex]; - - for (u32 j = 0; j < StripAt.LedCount; j++) - { - u32 LedIndex = StripAt.LedLUT[j]; - Leds->Colors[LedIndex] = { 0, 255, 0 }; - } - } -} - -internal void -TestPatternTwo(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - r32 PeriodicTime = (Time / PiR32) * 2; - - r32 ZeroOneSin = (SinR32(PeriodicTime) * .5f) + .5f; - r32 ZeroOneCos = (CosR32(PeriodicTime) * .5f) + .5f; - pixel Color = { (u8)(ZeroOneSin * 255), 0, (u8)(ZeroOneCos * 255) }; - - v4 Center = v4{0, 0, 0, 1}; - r32 ThetaZ = Time / 2; - v4 Normal = v4{CosR32(ThetaZ), 0, SinR32(ThetaZ), 0}; // NOTE(Peter): dont' need to normalize. Should always be 1 - v4 Right = V4Cross(Normal, v4{0, 1, 0, 0}); - - v4 FrontCenter = Center + (Normal * 25); - v4 BackCenter = Center - (Normal * 25); - - r32 OuterRadiusSquared = 1000000; - r32 InnerRadiusSquared = 0; - - for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) - { - v4 Position = Leds->Positions[LedIndex]; - - v4 ToFront = Position + FrontCenter; - v4 ToBack = Position + BackCenter; - - r32 ToFrontDotNormal = V4Dot(ToFront, Normal); - r32 ToBackDotNormal = V4Dot(ToBack, Normal); - - ToFrontDotNormal = Clamp01(ToFrontDotNormal * 1000); - ToBackDotNormal = Clamp01(ToBackDotNormal * 1000); - - r32 SqDistToCenter = V4MagSquared(Position); - if (SqDistToCenter < OuterRadiusSquared && SqDistToCenter > InnerRadiusSquared) - { - if (XOR(ToFrontDotNormal > 0, ToBackDotNormal > 0)) - { - Leds->Colors[LedIndex] = Color; - } - else - { - //Leds->Colors[LedIndex] = {}; - } - } - else - { - //Leds->Colors[LedIndex] = {}; - } - } -} - -internal void -TestPatternThree(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - v4 GreenCenter = v4{0, 0, 150, 1}; - r32 GreenRadius = Abs(SinR32(Time)) * 200; - - v4 TealCenter = v4{0, 0, 150, 1}; - r32 TealRadius = Abs(SinR32(Time + 1.5)) * 200; - - r32 FadeDist = 35; - - - for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) - { - v4 LedPosition = Leds->Positions[LedIndex]; - u8 Red = 0; - u8 Green = 0; - u8 Blue = 0; - - r32 GreenDist = Abs(V4Mag(LedPosition - GreenCenter) - GreenRadius); - r32 GreenBrightness = Clamp(0.f, FadeDist - Abs(GreenDist), FadeDist); - Green = (u8)(GreenBrightness * 255); - - r32 TealDist = Abs(V4Mag(LedPosition - TealCenter) - TealRadius); - r32 TealBrightness = Clamp(0.f, FadeDist - Abs(TealDist), FadeDist); - Red = (u8)(TealBrightness * 255); - Blue = (u8)(TealBrightness * 255); - - Leds->Colors[LedIndex].R = Red; - Leds->Colors[LedIndex].B = Green; - Leds->Colors[LedIndex].G = Green; - } -} - v4 RGBToHSV(v4 In) { v4 Result = {}; @@ -697,6 +494,22 @@ while (Hue > 360.0f) { Hue -= 360.0f; } return Result; } +internal void +Pattern_AltBloomMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + v3 SphereCenter = Assembly.Center - v3{0, -150, 0}; + r32 SphereRadius = Time; + r32 SphereBrightness = 1; + + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + v3 P = Leds->Positions[LedIndex].xyz; + r32 Sphere = SDF_SphereNormalized(P, SphereCenter, SphereRadius); + Sphere = Clamp01(-Sphere); + Leds->Colors[LedIndex] = V4ToRGBPixel(WhiteV4 * Sphere); + } +} + internal void Pattern_HueShift(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { @@ -728,7 +541,7 @@ Pattern_HueShift(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r3 } internal void -Pattern_HueFade(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_Rainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { r32 HueBase = ModR32(Time * 50, 360); @@ -748,49 +561,7 @@ Pattern_HueFade(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 } internal void -Pattern_AllGreen(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) - { - u32 I = LedIndex + 1; - Leds->Colors[LedIndex] = {0, 255, 0}; - } -} - -internal r32 -PatternHash(r32 Seed) -{ - return FractR32(Seed * 17.0 * FractR32(Seed * 0.3183099)); -} - -internal void -Pattern_Spots(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - pixel ColorA = { 0, 255, 255 }; - pixel ColorB = { 255, 0, 255 }; - - r32 Speed = .5f; - Time *= Speed; - r32 ScaleA = 2 * SinR32(Time / 5); - r32 ScaleB = 2.4f * CosR32(Time / 2.5f); - for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) - { - v4 P = Leds->Positions[LedIndex]; - r32 V = P.y; - r32 Noise = .3f * PatternHash(V); - r32 ThetaY = (Leds->Positions[LedIndex].y / 10) + Time + Noise; - r32 ThetaX = (Leds->Positions[LedIndex].x / 13) + Time + Noise; - r32 Fade = (ScaleA * SinR32(ThetaY)) + (ScaleB * CosR32(3 * ThetaX)); - Fade = RemapClampedR32(Fade, -1, 1, 0, 1); - - Leds->Colors[LedIndex].R = (u8)LerpR32(Fade, ColorA.R, ColorB.R); - Leds->Colors[LedIndex].G = (u8)LerpR32(Fade, ColorA.G, ColorB.G); - Leds->Colors[LedIndex].B = (u8)LerpR32(Fade, ColorA.B, ColorB.B); - } -} - -internal void -Pattern_LighthouseRainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_RadialRainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { v2 RefVector = V2Normalize(v2{ SinR32(Time), CosR32(Time) }); for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) @@ -803,196 +574,16 @@ Pattern_LighthouseRainbow(led_buffer* Leds, led_buffer_range Range, assembly Ass r32 Angle = V2Dot(RefVector, Vector); - v4 HSV = { (Angle * 30) + (Time * 10) + Leds->Positions[LedIndex].y, 1, 1, 1 }; + v4 HSV = { (Angle * 30) + (Time * 10), 1, 1, 1 }; v4 RGB = HSVToRGB(HSV); Leds->Colors[LedIndex] = V4ToRGBPixel(RGB); } } -internal void -Pattern_SmoothGrowRainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - r32 FillCycleTime = ModR32(Time, 7.0f) / 7.0f; - r32 ColorCycleTime = ModR32(Time, 21.0f) / 21.0f; - - v4 HSV = { 0, 1, 1, 1 }; - for (u32 s = 0; s < Assembly.StripCount; s++) - { - v2_strip Strip = Assembly.Strips[s]; - - v4 RGB0 = HSVToRGB(HSV); - for (u32 l = 0; l < Strip.LedCount; l++) - { - u32 LedIndex = Strip.LedLUT[l]; - Leds->Colors[LedIndex] = V4ToRGBPixel(RGB0); - } - - HSV.x += 15; - } -} - -internal void -Pattern_GrowAndFade(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - r32 PercentCycle = ModR32(Time, 10) / 10; - v4 HSV = { PercentCycle * 360, 1, 1, 1 }; - v4 RGB = HSVToRGB(HSV); - - r32 RefHeight = -100 + (Smoothstep(PercentCycle * 1.4f) * 400); - r32 RefBrightness = 1.0f - Smoothstep(PercentCycle); - - for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) - { - v4 P = Leds->Positions[LedIndex]; - - v4 RgbFaded = v4{}; - if (P.y < RefHeight) - { - RgbFaded = RGB * RefBrightness; - } - Leds->Colors[LedIndex] = V4ToRGBPixel(RgbFaded); - } -} - -internal void -Pattern_ColorToWhite(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - r32 FadeBottomBase = 50; - r32 FadeTop = 125; - - for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) - { - v2_strip Strip = Assembly.Strips[StripIndex]; - - r32 FlowerSpread = .8f; - r32 FlowerOffset = 0; - if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "center")) - { - FlowerOffset = 1; - } - else if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "right")) - { - FlowerOffset = 2; - } - FlowerOffset *= FlowerSpread; - - r32 PercentCycle = ModR32(Time + FlowerOffset, 10) / 10; - - r32 FadeBottom = FadeBottomBase + RemapR32(SinR32((PercentCycle * 4) * TauR32), -1, 1, -50, 50); - - v4 TopRGB = WhiteV4; - pixel TopColor = V4ToRGBPixel(TopRGB); - - for (u32 i = 0; i < Strip.LedCount; i++) - { - u32 LedIndex = Strip.LedLUT[i]; - v4 P = Leds->Positions[LedIndex]; - - pixel FinalColor = {}; - if (P.y > FadeTop) - { - FinalColor = TopColor; - } - else - { - r32 B = RemapR32(SinR32((P.y / 15.f) + (PercentCycle * TauR32)), -1, 1, .5f, 1.f); - r32 HNoise = RemapR32(SinR32((P.y / 31.f) + (PercentCycle * TauR32)), -1, 1, -32.f, 32.f); - v4 BottomRGB = HSVToRGB(v4{ (PercentCycle * 360) + HNoise, 1, B, 1 }); - - if (P.y < FadeBottom) - { - FinalColor = V4ToRGBPixel(BottomRGB); - } - else if (P.y >= FadeBottom && P.y <= FadeTop) - { - r32 FadePct = RemapR32(P.y, FadeBottom, FadeTop, 0, 1); - v4 MixRGB = V4Lerp(FadePct, BottomRGB, TopRGB); - FinalColor = V4ToRGBPixel(MixRGB); - } - } - - Leds->Colors[LedIndex] = FinalColor; - } - } -} - -internal void -Pattern_FlowerColorToWhite(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) -{ - r32 FadeBottomBase = 50; - r32 FadeTop = 125; - - for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) - { - v2_strip Strip = Assembly.Strips[StripIndex]; - -#if 0 - // All flowers same flower type - pixel* Colors = &FlowerAColors[0]; - r32 FlowerSpread = .8f; - r32 FlowerOffset = 0; - if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "center")) - { - FlowerOffset = 1; - } - else if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "right")) - { - FlowerOffset = 2; - } - FlowerOffset *= FlowerSpread; -#else - // Each flower different - v4* Colors = &FlowerAColors[0]; - r32 FlowerOffset = 0; - if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "center")) - { - Colors = &FlowerBColors[0]; - } - else if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "right")) - { - Colors = &FlowerCColors[0]; - } -#endif - r32 PercentCycle = ModR32(Time + FlowerOffset, 10) / 10; - - r32 FadeBottom = FadeBottomBase + RemapR32(SinR32((PercentCycle * 4) * TauR32), -1, 1, -50, 50); - - for (u32 i = 0; i < Strip.LedCount; i++) - { - u32 LedIndex = Strip.LedLUT[i]; - v4 P = Leds->Positions[LedIndex]; - - v4 FinalColor = {}; - r32 B = RemapR32(SinR32((P.y / 15.f) + (PercentCycle * TauR32)), -1, 1, .5f, 1.f); - r32 HNoise = RemapR32(SinR32((P.y / 31.f) + (PercentCycle * TauR32)), -1, 1, 0.f, 1.f); - - v4 BottomColor = GetColor(Colors, FLOWER_COLORS_COUNT, (PercentCycle + HNoise) / 2); - - FinalColor = BottomColor; - - Leds->Colors[LedIndex] = V4ToRGBPixel(FinalColor); - } - } -} - -r32 TLastFrame = 0; -v4* FAC = &FlowerAColors[0]; -v4* FBC = &FlowerBColors[0]; -v4* FCC = &FlowerCColors[0]; - internal void Pattern_BasicFlowers(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { - if (TLastFrame > Time) - { - v4 * Temp = FAC; - FAC = FBC; - FBC = FCC; - FCC = Temp; - } - TLastFrame = Time; - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); @@ -1032,30 +623,58 @@ Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Ti DEBUG_TRACK_FUNCTION; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; - phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); + v4 C2 = HSVToRGB({Hue.Hue2, 1, 1, 1}); + r32 Top = 120 + (SinR32(Time) * 10); + r32 Mid = 70 + (CosR32(Time * 2.13) * 20); + r32 Bot = 0; + + r32 TopD = Top - Mid; + r32 BotD = Mid - Bot; + r32 MidD = Min(TopD, BotD); + + //r32 MaxFadeDistance = 10; for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { - v4 P = Leds->Positions[LedIndex]; + v3 P = Leds->Positions[LedIndex].xyz; - v3 Pp = P.xyz; + r32 PercentTop = Clamp01(1.0f - ((Top - P.y) / TopD)); - r32 Noise = Fbm3D((Pp / 1000) + (v3{Time, -Time, Time} * 0.01f)); - Noise = RemapR32(Noise, -1, 1, 0, 1); - Noise = Smoothstep(Noise, 0, 1); - u8 NV = (u8)(Noise * 255); + r32 PercentMid = Clamp01(1.0f - Abs(P.y - Mid) / MidD); + r32 N = Noise3D((P / 17) + v3{Time, -Time, 0}); + N = Clamp01(N) * 2; + N = Smoothstep(N); + N *= N; + N = Smoothstep(N); + N *= 1.0f - PowR32(1.0f - PercentMid, 4); + PercentMid = Clamp01(PercentMid + N); - v3 BSeed = v3{P.z, P.x, P.y}; - r32 BNoise = 1.0f; //Fbm3D(BSeed / 50); + r32 PercentBot = Clamp01(1.0f - ((P.y - Bot) / BotD)); - v4 C = V4Lerp(BNoise, C0, C1); - C = C * BNoise; + v4 TopC = (C0 * PercentTop); + v4 MidC = (C1 * PercentMid); + v4 BotC = (C2 * PercentBot); - //Leds->Colors[LedIndex] = V4ToRGBPixel(v4{Noise, Noise, Noise, 1}); + v4 C = {}; + if (PercentTop > PercentMid && PercentTop > PercentBot) + { + C = C0; + } + else if (PercentMid > PercentBot) + { + C = C1; + } + else + { + C = C2; + } + + r32 ScaleFactor = PercentTop + PercentMid + PercentBot; + C = (TopC + MidC + BotC) / ScaleFactor; Leds->Colors[LedIndex] = V4ToRGBPixel(C); - //Leds->Colors[LedIndex] = pixel{NV, NV, NV}; } } @@ -1065,7 +684,7 @@ Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 DEBUG_TRACK_FUNCTION; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; - phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % BL_FLOWER_COUNT]; + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); @@ -1086,70 +705,71 @@ Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 NoiseB = Smoothstep(NoiseB); v4 CB = C1 * NoiseB; - v4 C = CA + CB; + v4 C = (C0 * NoiseA) + (C1 * NoiseB); + C /= (NoiseA + NoiseB); Leds->Colors[LedIndex] = V4ToRGBPixel(C); } } +internal r32 +Leafy_BandSDF(v3 P, gs_random_series* Random, r32 Time) +{ + r32 MinBandThickness = 5; + r32 MaxBandThickness = 10; + r32 MaxTransitionPeriod = 120.0f; + + r32 BandTransitionPeriod = NextRandomUnilateral(Random) * MaxTransitionPeriod; + r32 BandTransitionBias = (1 - Clamp(0, (Time / (MaxTransitionPeriod / 2)), 0.7f)); // approaches 0.5 over time + BandTransitionPeriod *= BandTransitionBias; + + r32 BandPercent = ModR32(Time, BandTransitionPeriod) / BandTransitionPeriod; + BandPercent = Smoothstep(BandPercent); + r32 BandY = -150 + (BandPercent * 290); + + r32 ThickRand = NextRandomUnilateral(Random); + // 1 - 4((ThickRand - .5)^2) - distribution curve + ThickRand = 1.0f - ((4 * PowR32(ThickRand, 2)) - (4 * ThickRand) + 1); + r32 BandThickness = MinBandThickness + (ThickRand * (MaxBandThickness - MinBandThickness)); + + // BandBrightness = 1 - ((2x - 1) ^ 8) where x is BandPercent + r32 BandBrightness = 1.0f - PowR32((2 * BandPercent) - 1, 8); + BandBrightness *= RemapR32(NextRandomUnilateral(Random), 0, 1, .25f, 1); + r32 Result = 1 - Clamp01(Abs(P.y - BandY) / BandThickness); + Result *= BandBrightness; + return Result; +} + internal void Pattern_Leafy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { DEBUG_TRACK_FUNCTION; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; - phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); + v4 C2 = HSVToRGB({Hue.Hue2, 1, 1, 1}); for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; -#if 0 - r32 RefPos = P.y + Noise2D(v2{P.x, P.z} * 10); - v4 C = {}; r32 B = 0; - r32 BandWidth = 5; - r32 TransitionPeriod = 30.0f; - u32 BandCount = 10; + // NOTE(PS): initializing the Random seed inside the Led Loop + // so that the bands are consistently calculated for each led + // ie. each time you calculate a band, the random numbers requested + // will always be the same + gs_random_series Random = InitRandomSeries(24601); + u32 BandCount = 25; for (u32 Band = 0; Band < BandCount; Band++) { - r32 BandSeed = Hash1((r32)Band); - r32 BandSeedPos = RemapR32(BandSeed, -1, 1, 0, 1); - r32 BandDelay = BandSeedPos * TransitionPeriod; - r32 BandTransitionPeriod = RemapR32(Hash1((r32)Band * 3.413f), -1, 1, 0, 1) * TransitionPeriod; - r32 BandOffset = Time + BandDelay; - r32 BandPercent = ModR32(BandOffset, BandTransitionPeriod) / BandTransitionPeriod; - r32 BandSmoothed = Smoothstep(BandPercent, 0, 1); - - r32 BandHeight = -125 + BandPercent * 250; - - r32 BandDist = Abs(RefPos - BandHeight); - - B += Max(0, (BandWidth + BandSeed * 2.5) - BandDist); + B += Leafy_BandSDF(P.xyz, &Random, Time); } - B = Clamp(0, B, 1); + B = Clamp01(B); - r32 BandCP = (P.y + 100) / 200; - BandCP = 0.8f; - v4 BandC = GetColor(&FlowerBColors[0], FLOWER_COLORS_COUNT, BandCP); - - v4 GradientC = GetColor(&FlowerBColors[0], FLOWER_COLORS_COUNT, 0); - r32 GradientB = RemapR32(P.y, 200, 0, 1, 0); - GradientB = Clamp(0, GradientB, 1); - - C = (GradientC * GradientB) + (BandC * B); -#endif - //v4 C = GetColor(&FlowerBColors[0], FLOWER_COLORS_COUNT, 0); - v4 C = v4{ 255, 100, 3 }; - C /= 255.f; - //r32 B = Fbm3D(P.xyz / 200); - //C *= B; - if (P.y < 75) { - C = v4{ 139 / 255.f, 69 / 255.f, 19 / 255.f, 1.0f} * .25f; - } + C = WhiteV4 * B; Leds->Colors[LedIndex] = V4ToRGBPixel(C); } } @@ -1159,33 +779,44 @@ Pattern_LeafyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, { DEBUG_TRACK_FUNCTION; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; - phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); + v4 C2 = HSVToRGB({Hue.Hue2, 1, 1, 1}); + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; - r32 LedRange = 300.0f; - r32 ScaleFactor = 1.0f / LedRange; v3 Pp = P.xyz + v3{150, 100, 0}; - r32 NoiseA = Fbm3D((Pp / 35), Time * 0.5f); - //NoiseA = PowR32(NoiseA, 3); + r32 NoiseA = Fbm3D((Pp / 18), Time * 0.25f); NoiseA = Smoothstep(NoiseA); - v4 CA = C0 * NoiseA; - r32 NoiseB = Noise3D((Pp / 35) + v3{0, 0, Time * 5}); + r32 NoiseB = Noise3D((Pp / 35) + v3{0, 0, Time * 0.5f}); NoiseB = PowR32(NoiseB, 3); NoiseB = Smoothstep(NoiseB); - v4 CB = C1; - v4 C = V4Lerp(NoiseB, CA, CB); + r32 NoiseC = Noise3D((Pp / 25) + v3{0, 0, Time * 4}); + r32 CPresence = SinR32((P.y / 50) - Time) + (0.8f * SinR32((P.y / 25) - (Time * 5.0f))); + CPresence = RemapR32(CPresence, -1.8, 1.8, 0, 1); + CPresence = PowR32(CPresence, 4); + NoiseC *= CPresence; + + v4 C = (C0 * NoiseA * 0.5f) + (C1 * NoiseB) + (C2 * NoiseC); + C *= 1.0f / (NoiseA + NoiseB + NoiseC); Leds->Colors[LedIndex] = V4ToRGBPixel(C); } } internal void Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + +} + +internal void +Pattern_VerticalLines(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { DEBUG_TRACK_FUNCTION; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; @@ -1194,22 +825,11 @@ Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 LightSpeedMin = 1; r32 LightSpeedMax = 5; -#if 0 - r32 LightHueMin = (ModR32(Time, 10) / 10) * 360; - r32 LightHueMax = ModR32((LightHueMin + 45), 360) ; -#else - r32 CenterHue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3].Hue0; - r32 LightHueMin = ModR32(CenterHue + 30, 360);; - r32 LightHueMax = ModR32(CenterHue - 30, 360) ; -#endif s32 LightTailLength = 10; for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) { v2_strip Strip = Assembly.Strips[StripIndex]; - r32 LightHue = LerpR32(NextRandomUnilateral(&Random), - LightHueMin, - LightHueMax); r32 LightStartHeight = NextRandomUnilateral(&Random); r32 LightSpeed = LerpR32(NextRandomUnilateral(&Random), LightSpeedMin, @@ -1224,11 +844,99 @@ Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, u32 LedIndex = Strip.LedLUT[StripLedIndex]; r32 PctTail = ((r32)i / (r32)LightTailLength); - v4 C = HSVToRGB(v4{LightHue, 1, 1, 1}) * PctTail; + v4 C = WhiteV4 * PctTail; Leds->Colors[LedIndex] = V4ToRGBPixel(C); } } } +internal void +Pattern_Rotary(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + gs_random_series Random = InitRandomSeries(24601); + +#define SphereCount 32 + v3 SphereCenter[SphereCount]; + + r32 MaxHeightOffset = 150; + r32 MaxSpeed = 10; + r32 SphereRadius = 2.0f; + for (u32 i = 0; i < SphereCount; i++) + { + r32 SphereSeedA = NextRandomBilateral(&Random); + r32 SphereSeedB = NextRandomBilateral(&Random); + r32 SphereSpeed = NextRandomUnilateral(&Random) * MaxSpeed; + + r32 SphereTime = Time + SphereSpeed; + r32 HeightOffset = SphereTime + (SphereSeedA * MaxHeightOffset); + r32 RotationOffset = SphereTime + SphereSeedB * TauR32; + r32 SphereRotationDir = NextRandomBilateral(&Random) < 0 ? -1 : 1; + v3 SpherePosOffset = v3{ + SinR32(RotationOffset * SphereRotationDir) * (SphereRadius * 2), + HeightOffset, + CosR32(RotationOffset * SphereRotationDir) * (SphereRadius * 2) + }; + SphereCenter[i] = Assembly.Center + SpherePosOffset; + } + + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + v3 P = Leds->Positions[LedIndex].xyz; + + r32 Dist = 10000000; + for (u32 i = 0; i < SphereCount; i++) + { + r32 SphereSDF = Abs(SDF_Sphere(P, SphereCenter[i], SphereRadius)); + SphereSDF = SphereSDF / SphereRadius; + Dist = Min(Dist, SphereSDF); + } + + v4 C = BlackV4; + if (Dist <= 1) + { + r32 Brightness = Clamp01(SphereRadius - Dist); + C = WhiteV4 * Brightness; + } + + Leds->Colors[LedIndex] = V4ToRGBPixel(C); + } +} + +internal void +Pattern_AllOnMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + pixel White = V4ToRGBPixel(WhiteV4); + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + v3 P = Leds->Positions[LedIndex].xyz; + Leds->Colors[LedIndex] = White; + } +} + +internal void +Pattern_BulbMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + r32 Top = 141; + r32 BulbRange = 50; + + pixel White = V4ToRGBPixel(WhiteV4); + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + v3 P = Leds->Positions[LedIndex].xyz; + + r32 BulbSDF = 1 - Clamp01(((Top - P.y) - BulbRange) / BulbRange); + r32 N = Noise3D((P / 17) + v3{Time, -Time, 0}); + N = Clamp01(N) * 2; + N = Smoothstep(N); + N *= N; + N = Smoothstep(N); + N *= 1.0f - PowR32(1.0f - BulbSDF, 4); + BulbSDF += N; + BulbSDF = Clamp01(BulbSDF); + v4 C = WhiteV4 * BulbSDF; + Leds->Colors[LedIndex] = V4ToRGBPixel(C); + } +} + #define BLUMEN_PATTERNS_H #endif // BLUMEN_PATTERNS_H \ No newline at end of file diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 82a98ff..3957f03 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -430,14 +430,15 @@ Win32_SendAddressedDataBuffer_Job(gs_thread_context Context, gs_data Arg) } internal bool -ReloadAndLinkDLL(win32_dll_refresh* DLL, context* Context, gs_work_queue* WorkQueue, bool ShouldError) +ReloadAndLinkDLL(win32_dll_refresh* DLL, context* Context, gs_work_queue* WorkQueue, bool ShouldError, bool AppReady) { bool Success = false; if (HotLoadDLL(DLL)) { SetApplicationLinks(Context, *DLL, WorkQueue); - Context->ReloadStaticData(*Context, GlobalDebugServices); + Context->ReloadStaticData(*Context, GlobalDebugServices, AppReady); Success = true; + OutputDebugStringA("Reloaded DLL\n"); } else if(ShouldError) { @@ -609,20 +610,30 @@ WinMain ( if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; - MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); - Win32UpdateWindowDimension(&MainWindow); - - win32_opengl_window_info OpenGLWindowInfo = {}; - OpenGLWindowInfo.ColorBits = 32; - OpenGLWindowInfo.AlphaBits = 8; - OpenGLWindowInfo.DepthBits = 0; - CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow); - context Context = {}; Context.ThreadContext = ThreadContext; Context.MemorySize = MB(64); Context.MemoryBase = (u8*)Win32Alloc(Context.MemorySize, 0); + gs_const_string Args = ConstString((char*)CmdLineArgs); + if (StringsEqual(Args, ConstString("-headless"))) + { + OutputDebugStringA("Running Headless\n"); + Context.Headless = true; + } + + if (!Context.Headless) + { + MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); + Win32UpdateWindowDimension(&MainWindow); + + win32_opengl_window_info OpenGLWindowInfo = {}; + OpenGLWindowInfo.ColorBits = 32; + OpenGLWindowInfo.AlphaBits = 8; + OpenGLWindowInfo.DepthBits = 0; + CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow); + } + gs_memory_arena PlatformPermanent = CreateMemoryArena(Context.ThreadContext.Allocator, "Platform Memory"); s64 PerformanceCountFrequency = GetPerformanceFrequency(); @@ -657,7 +668,7 @@ WinMain ( *Context.SocketManager = CreatePlatformSocketManager(Win32ConnectSocket, Win32CloseSocket, Win32SocketQueryStatus, Win32SocketPeek, Win32SocketReceive, Win32SocketSend); win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName); - if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true)) { return -1; } + if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true, false)) { return -1; } Mouse_Init(); @@ -708,42 +719,43 @@ WinMain ( ResetInputQueue(&InputQueue); - ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false); + ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false, true); AddressedDataBufferList_Clear(&OutputData); - Mouse_Update(MainWindow, &Context); - - MSG Message; - while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE)) + if (!Context.Headless) { - DEBUG_TRACK_SCOPE(PeekWindowsMessages); - HandleWindowMessage(Message, &MainWindow, &InputQueue, &Context.Mouse); + Mouse_Update(MainWindow, &Context); + MSG Message; + while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE)) + { + DEBUG_TRACK_SCOPE(PeekWindowsMessages); + HandleWindowMessage(Message, &MainWindow, &InputQueue, &Context.Mouse); + } + + Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}}; + RenderBuffer.ViewWidth = MainWindow.Width; + RenderBuffer.ViewHeight = MainWindow.Height; } - Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}}; - RenderBuffer.ViewWidth = MainWindow.Width; - RenderBuffer.ViewHeight = MainWindow.Height; Context.DeltaTime = LastFrameSecondsElapsed; - -#if 0 - gs_string T = PushStringF(Context.ThreadContext.Transient, 256, "%f\n", Context.DeltaTime); - NullTerminate(&T); - OutputDebugStringA(T.Str); -#endif + Context.TotalTime += (r64)Context.DeltaTime; Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); Win32_SendOutputData(ThreadContext, OutputData); - RenderCommandBuffer(RenderBuffer); - ClearRenderBuffer(&RenderBuffer); - - Mouse_Advance(&Context); - - HDC DeviceContext = GetDC(MainWindow.Handle); - SwapBuffers(DeviceContext); - ReleaseDC(MainWindow.Handle, DeviceContext); + if (!Context.Headless) + { + RenderCommandBuffer(RenderBuffer); + ClearRenderBuffer(&RenderBuffer); + + Mouse_Advance(&Context); + + HDC DeviceContext = GetDC(MainWindow.Handle); + SwapBuffers(DeviceContext); + ReleaseDC(MainWindow.Handle, DeviceContext); + } Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext); @@ -767,8 +779,6 @@ WinMain ( Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext); Win32WorkQueue_Cleanup(); - //Win32_TestCode_SocketReading_Cleanup(); - Win32SocketSystem_Cleanup(); return 0; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 4b4d224..ee55776 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -194,28 +194,18 @@ BlumenLumen_LoadPatterns(app_state* State) } Patterns->Count = 0; - Patterns_PushPattern(Patterns, TestPatternOne, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, TestPatternTwo, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, TestPatternThree, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_AllGreen, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_HueFade, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Spots, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_LighthouseRainbow, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_SmoothGrowRainbow, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_GrowAndFade, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_ColorToWhite, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Green, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_FlowerColors, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_FlowerColorToWhite, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Rainbow, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED); - // 15 Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED); + Patterns_PushPattern(Patterns, Pattern_VerticalLines, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Rotary, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); } internal gs_data @@ -240,6 +230,8 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue; BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; + BLState->PatternSpeed = GlobalAnimSpeed; + #if 1 BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); #endif @@ -267,38 +259,6 @@ BlumenLumen_CustomInit(app_state* State, context Context) &State->Permanent); #if 0 - { // Animation PLAYGROUND - animation_desc Desc = {}; - Desc.NameSize = 256; - Desc.LayersCount = 8; - Desc.BlocksCount = 8; - Desc.MinFrames = 0; - Desc.MaxFrames = SecondsToFrames(15, State->AnimationSystem); - - animation_desc Desc0 = Desc; - Desc.Name = "test_anim_zero"; - animation Anim0 = Animation_Create(Desc0, &State->AnimationSystem); - Animation_AddLayer(&Anim0, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim0, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(15), 0); - BLState->AnimHandles[0] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim0); - - animation_desc Desc1 = Desc; - Desc1.Name = "test_anim_one"; - animation Anim1 = Animation_Create(Desc1, &State->AnimationSystem); - Animation_AddLayer(&Anim1, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim1, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(12), 0); - BLState->AnimHandles[1] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim1); - - animation_desc Desc2 = Desc; - Desc2.Name = "i_love_you"; - animation Anim2 = Animation_Create(Desc2, &State->AnimationSystem);; - Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(20), 0); - BLState->AnimHandles[2] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); - - State->AnimationSystem.ActiveFadeGroup.From = BLState->AnimHandles[2]; - } // End Animation Playground -#elif 0 animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, @@ -307,6 +267,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) #else BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context); BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); + AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); BlumenLumen_SetPatternMode(BlumenPattern_Standard, 5, &State->AnimationSystem, BLState); #endif @@ -402,6 +363,20 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } + // Update next frames Hues + r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); + AnimTime = (r32)Context->TotalTime; + r32 BaseTime = AnimTime * BLState->PatternSpeed; + + r32 ColorSpeed = 1; //.001; + r32 ColorOscSpeed = .05 * ColorSpeed; + r32 ColorRelOscSpeed = 1 * ColorSpeed;; + r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; + r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); + BLState->StandardPatternHues.Hue0 = ModR32(ColorOscillation * 360, 360); + BLState->StandardPatternHues.Hue1 = ModR32(BaseTime + ColorRelationship, 360); + BLState->StandardPatternHues.Hue2 = LerpR32(.3f, BLState->StandardPatternHues.Hue0, BLState->StandardPatternHues.Hue1); + // Transition back to standard mode after some time if (BLState->PatternMode == BlumenPattern_VoiceCommand) { diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 21d42c3..6dd55ac 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -155,6 +155,7 @@ struct blumen_lumen_state system_time LastSendTime; + phrase_hue StandardPatternHues; phrase_hue AssemblyColors[BL_FLOWER_COUNT]; u32 LastAssemblyColorSet; @@ -172,6 +173,7 @@ struct blumen_lumen_state phrase_hue_map PhraseHueMap; system_time TimeLastSetToVoiceMode; + r32 PatternSpeed; // Debug motor_packet DEBUG_PendingMotorPacket; bool DEBUG_IgnoreWeatherDimmingLeds; @@ -179,6 +181,26 @@ struct blumen_lumen_state #include "message_queue.cpp" +internal phrase_hue +BlumenLumen_GetCurrentHue(blumen_lumen_state* BLState, assembly Assembly) +{ + phrase_hue Result = {}; + + switch (BLState->PatternMode) + { + case BlumenPattern_Standard: + { + Result = BLState->StandardPatternHues; + }break; + + case BlumenPattern_VoiceCommand: + { + Result = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + }break; + } + + return Result; +} diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 8fb514d..97b6686 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -126,4 +126,8 @@ r32 HighTemperatureBrightnessPercent = .25f; // lower it for heat reasons? r32 FullBrightnessPercent = 1.0f; +// A global modifier so Joerg can just slow all the patterns right down +// XD +r32 GlobalAnimSpeed = 1.0f; + #endif //BLUMEN_LUMEN_SETTINGS_H diff --git a/src/app/ss_blumen_lumen/sdf.h b/src/app/ss_blumen_lumen/sdf.h new file mode 100644 index 0000000..58e40e9 --- /dev/null +++ b/src/app/ss_blumen_lumen/sdf.h @@ -0,0 +1,20 @@ +/* date = March 29th 2021 10:41 pm */ + +#ifndef SDF_H +#define SDF_H + +internal r32 +SDF_Sphere(v3 Point, v3 Center, r32 Radius) +{ + r32 Result = V3Mag(Point - Center) - Radius; + return Result; +} + +internal r32 +SDF_SphereNormalized(v3 Point, v3 Center, r32 Radius) +{ + r32 Result = SDF_Sphere(Point, Center, Radius) / Radius; + return Result; +} + +#endif //SDF_H diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index 4555311..a93f79d 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -106,8 +106,8 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc) #if 1 // the bloom stem inner loop_desc BloomStemInner = {}; - BloomStemInner.CenterStart = v3{0, 1.4f, 0} + Desc.Pos; - BloomStemInner.CenterEnd = v3{0, .9f, 0} + Desc.Pos; + BloomStemInner.CenterStart = v3{0, 1.4f, 0}; + BloomStemInner.CenterEnd = v3{0, .9f, 0}; BloomStemInner.Radius = .05f; BloomStemInner.SegmentsCount = 6; BloomStemInner.SubsegmentsCount = 3; @@ -120,8 +120,8 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc) // the bloom stem outer loop_desc BloomStemOuter = {}; - BloomStemOuter.CenterStart = v3{0, .5f, 0} + Desc.Pos; - BloomStemOuter.CenterEnd = v3{0, .9f, 0} + Desc.Pos; + BloomStemOuter.CenterStart = v3{0, .5f, 0}; + BloomStemOuter.CenterEnd = v3{0, .9f, 0}; BloomStemOuter.Radius = .07f; BloomStemOuter.SegmentsCount = 9; BloomStemOuter.SubsegmentsCount = 3; @@ -136,8 +136,8 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc) #if 1 // the flower stem loop_desc FlowerStem = {}; - FlowerStem.CenterStart = v3{0, -1.5f, 0} + Desc.Pos; - FlowerStem.CenterEnd = v3{0, .5f, 0} + Desc.Pos; + FlowerStem.CenterStart = v3{0, -1.5f, 0}; + FlowerStem.CenterEnd = v3{0, .5f, 0}; FlowerStem.Radius = .05f; FlowerStem.SegmentsCount = 6; FlowerStem.SubsegmentsCount = 1; @@ -182,7 +182,7 @@ int main(int ArgCount, char** Args) WriteAssemblyUARTOpen(&OutputBuffer0, "Blumen Lumen - Silver Spring - 00", 100, - v3{0, 0, 0}, + v3{-1, 0, 0}, 21, ""); WriteAssemblyUARTOpen(&OutputBuffer1, @@ -194,7 +194,7 @@ int main(int ArgCount, char** Args) WriteAssemblyUARTOpen(&OutputBuffer2, "Blumen Lumen - Silver Spring - 02", 100, - v3{0, 0, 0}, + v3{1, 0, 0}, 21, ""); @@ -204,7 +204,7 @@ int main(int ArgCount, char** Args) u32 BloomOuterChannels[] = { FSC(1, 0), FSC(1, 1), FSC(1, 2), FSC(1, 3), FSC(1, 4), FSC(1, 5), FSC(1, 6), FSC(1, 7), FSC(2, 0) }; u32 BloomInnerChannels[] = { FSC(0, 0), FSC(0, 1), FSC(0, 2), FSC(0, 3), FSC(0, 4), FSC(0, 5) }; flower_desc F0 = {}; - F0.Pos = v3{-1, 0, 0}; + F0.Pos = v3{0, 0, 0}; F0.ComPort = "\\\\.\\COM11"; F0.FlowerTagValue = "left"; F0.StemChannels = StemChannels; @@ -222,7 +222,7 @@ int main(int ArgCount, char** Args) StripCount += BuildFlower(&OutputBuffer1, F1); flower_desc F2 = {}; - F2.Pos = v3{1, 0, 0}; + F2.Pos = v3{0, 0, 0}; F2.ComPort = "\\\\.\\COM6"; F2.FlowerTagValue = "right"; F2.StemChannels = StemChannels; From a49fa26a43c670a9a92444bd3d57c17339bb78dd Mon Sep 17 00:00:00 2001 From: PS Date: Tue, 30 Mar 2021 20:00:16 -0700 Subject: [PATCH 032/151] animations --- .../ambient_patterns/ambient_0.foldanim | 92 ++++++++++++ .../ambient_patterns/ambient_1.foldanim | 56 ++++++++ .../data/blumen_animations/anim_demo.foldanim | 132 ++++++++++++++++++ .../audio_responses/voice_command.foldanim | 24 ++++ 4 files changed, 304 insertions(+) create mode 100644 app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim create mode 100644 app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim create mode 100644 app_run_tree/data/blumen_animations/anim_demo.foldanim create mode 100644 app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim new file mode 100644 index 0000000..15a653b --- /dev/null +++ b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim @@ -0,0 +1,92 @@ +lumenarium_animation_file; +animation_name: "ambient_0"; +layers_count: 2; +blocks_count: 9; +playable_range:{ + min: 0; + max: 1000; +}; +layers:{ + layer:{ + name: "Color"; + blend: "Add"; + }; + layer:{ + name: "Mask"; + blend: "Multiply"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 88; + max: 315; + }; + layer_index: 0; + animation_name: "Pattern_Rotary"; + }; + block:{ + frame_range:{ + min: 156; + max: 429; + }; + layer_index: 1; + animation_name: "Pattern_LeafyPatchy"; + }; + block:{ + frame_range:{ + min: 403; + max: 675; + }; + layer_index: 1; + animation_name: "Pattern_Patchy"; + }; + block:{ + frame_range:{ + min: 643; + max: 997; + }; + layer_index: 1; + animation_name: "Pattern_LeafyPatchy"; + }; + block:{ + frame_range:{ + min: 253; + max: 571; + }; + layer_index: 0; + animation_name: "Pattern_VerticalLines"; + }; + block:{ + frame_range:{ + min: 522; + max: 772; + }; + layer_index: 0; + animation_name: "Pattern_Rotary"; + }; + block:{ + frame_range:{ + min: 737; + max: 998; + }; + layer_index: 0; + animation_name: "Pattern_VerticalLines"; + }; + block:{ + frame_range:{ + min: 0; + max: 117; + }; + layer_index: 0; + animation_name: "Pattern_VerticalLines"; + }; + block:{ + frame_range:{ + min: 0; + max: 179; + }; + layer_index: 1; + animation_name: "Pattern_Rainbow"; + }; +}; diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim new file mode 100644 index 0000000..19d552e --- /dev/null +++ b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim @@ -0,0 +1,56 @@ +lumenarium_animation_file; +animation_name: "digital_fire"; +layers_count: 3; +blocks_count: 4; +playable_range:{ + min: 0; + max: 2000; +}; +layers:{ + layer:{ + name: "mask1"; + blend: "Add"; + }; + layer:{ + name: "mask2"; + blend: "Add"; + }; + layer:{ + name: "color"; + blend: "Multiply"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 0; + max: 2000; + }; + layer_index: 0; + animation_name: "Pattern_BulbMask"; + }; + block:{ + frame_range:{ + min: 0; + max: 2000; + }; + layer_index: 1; + animation_name: "Pattern_VerticalLines"; + }; + block:{ + frame_range:{ + min: 0; + max: 1032; + }; + layer_index: 2; + animation_name: "Pattern_Rainbow"; + }; + block:{ + frame_range:{ + min: 959; + max: 2000; + }; + layer_index: 2; + animation_name: "Pattern_Wavy"; + }; +}; diff --git a/app_run_tree/data/blumen_animations/anim_demo.foldanim b/app_run_tree/data/blumen_animations/anim_demo.foldanim new file mode 100644 index 0000000..6f424e1 --- /dev/null +++ b/app_run_tree/data/blumen_animations/anim_demo.foldanim @@ -0,0 +1,132 @@ +lumenarium_animation_file; +animation_name: "demo_anim"; +layers_count: 2; +blocks_count: 14; +playable_range:{ + min: 0; + max: 10000; +}; +layers:{ + layer:{ + name: "main"; + blend: "Add"; + }; + layer:{ + name: "color"; + blend: "Multiply"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 2428; + max: 3017; + }; + layer_index: 0; + animation_name: "Pattern_HueShift"; + }; + block:{ + frame_range:{ + min: 1711; + max: 2487; + }; + layer_index: 0; + animation_name: "Pattern_Rainbow"; + }; + block:{ + frame_range:{ + min: 0; + max: 603; + }; + layer_index: 0; + animation_name: "Pattern_VerticalLines"; + }; + block:{ + frame_range:{ + min: 2945; + max: 3656; + }; + layer_index: 0; + animation_name: "Pattern_Wavy"; + }; + block:{ + frame_range:{ + min: 3592; + max: 4296; + }; + layer_index: 0; + animation_name: "Pattern_Patchy"; + }; + block:{ + frame_range:{ + min: 520; + max: 1225; + }; + layer_index: 0; + animation_name: "Pattern_Rotary"; + }; + block:{ + frame_range:{ + min: 4218; + max: 4939; + }; + layer_index: 0; + animation_name: "Pattern_LeafyPatchy"; + }; + block:{ + frame_range:{ + min: 4849; + max: 5431; + }; + layer_index: 0; + animation_name: "Pattern_HueShift"; + }; + block:{ + frame_range:{ + min: 1149; + max: 1819; + }; + layer_index: 0; + animation_name: "Pattern_Leafy"; + }; + block:{ + frame_range:{ + min: 5529; + max: 7782; + }; + layer_index: 1; + animation_name: "Pattern_Wavy"; + }; + block:{ + frame_range:{ + min: 5529; + max: 5979; + }; + layer_index: 0; + animation_name: "Pattern_Leafy"; + }; + block:{ + frame_range:{ + min: 5912; + max: 6328; + }; + layer_index: 0; + animation_name: "Pattern_VerticalLines"; + }; + block:{ + frame_range:{ + min: 6281; + max: 6581; + }; + layer_index: 0; + animation_name: "Pattern_Rotary"; + }; + block:{ + frame_range:{ + min: 6524; + max: 7025; + }; + layer_index: 0; + animation_name: "Pattern_HueShift"; + }; +}; diff --git a/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim new file mode 100644 index 0000000..91c364f --- /dev/null +++ b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim @@ -0,0 +1,24 @@ +lumenarium_animation_file; +animation_name: "voice_command"; +layers_count: 1; +blocks_count: 1; +playable_range:{ + min: 0; + max: 360; +}; +layers:{ + layer:{ + name: "Colors"; + blend: "Add"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 0; + max: 360; + }; + layer_index: 0; + animation_name: "Pattern_Leafy"; + }; +}; From 18bef60ba1e2286f3ec91ca3a22fc8b40cfb6fa5 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Tue, 30 Mar 2021 22:04:05 -0700 Subject: [PATCH 033/151] Trying to reduce memory usage --- src/app/engine/animation/foldhaus_animation.h | 5 ++- src/app/foldhaus_app.cpp | 40 ++++++++++--------- src/app/foldhaus_platform.h | 2 +- src/app/platform_win32/win32_foldhaus.cpp | 4 +- src/gs_libs/gs_types.cpp | 2 +- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 4bcad4a..2385238 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -801,7 +801,10 @@ AnimationSystem_FadeToPlaylist(animation_system* System, animation_handle_array System->Playlist = Playlist; System->PlaylistAt = 0; - AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup, Playlist.Handles[0], System->PlaylistFadeTime); + if (System->Playlist.Count > 0) + { + AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup, Playlist.Handles[0], System->PlaylistFadeTime); + } } inline bool diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index dc19ab6..0712c40 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -30,11 +30,13 @@ RELOAD_STATIC_DATA(ReloadStaticData) INITIALIZE_APPLICATION(InitializeApplication) { - app_state* State = (app_state*)Context.MemoryBase; + Context->MemorySize = sizeof(app_state); + Context->MemoryBase = AllocatorAlloc(Context->ThreadContext.Allocator, Context->MemorySize).Memory; + app_state* State = (app_state*)Context->MemoryBase; *State = {}; - State->Permanent = CreateMemoryArena(Context.ThreadContext.Allocator, "Permanent"); - State->Transient = Context.ThreadContext.Transient; + State->Permanent = CreateMemoryArena(Context->ThreadContext.Allocator, "Permanent"); + State->Transient = Context->ThreadContext.Transient; State->Assemblies = AssemblyArray_Create(8, &State->Permanent); State->GlobalLog = PushStruct(&State->Permanent, event_log); @@ -47,7 +49,7 @@ INITIALIZE_APPLICATION(InitializeApplication) AnimSysDesc.SecondsPerFrame = 1.0f / 24.0f; State->AnimationSystem = AnimationSystem_Init(AnimSysDesc); - if (!Context.Headless) + if (!Context->Headless) { interface_config IConfig = {0}; IConfig.FontSize = 14; @@ -61,50 +63,50 @@ INITIALIZE_APPLICATION(InitializeApplication) IConfig.ListBGHover = v4{ .22f, .22f, .22f, 1.f }; IConfig.ListBGSelected = v4{ .44f, .44f, .44f, 1.f }; IConfig.Margin = v2{5, 5}; - State->Interface = ui_InterfaceCreate(Context, IConfig, &State->Permanent); + State->Interface = ui_InterfaceCreate(*Context, IConfig, &State->Permanent); PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); } - State->SACN = SACN_Initialize(Context); + State->SACN = SACN_Initialize(*Context); - State->LedSystem = LedSystem_Create(Context.ThreadContext.Allocator, 128); + State->LedSystem = LedSystem_Create(Context->ThreadContext.Allocator, 128); State->AssemblyDebugState = AssemblyDebug_Create(&State->Permanent); State->AssemblyDebugState.Brightness = 255; State->AssemblyDebugState.Override = ADS_Override_None; - State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext); + State->Modes = OperationModeSystemInit(&State->Permanent, Context->ThreadContext); - ReloadStaticData(Context, GlobalDebugServices, true); - US_CustomInit(&State->UserSpaceDesc, State, Context); + ReloadStaticData(*Context, GlobalDebugServices, true); + US_CustomInit(&State->UserSpaceDesc, State, *Context); GlobalDebugServices->Interface.RenderSculpture = true; - if (!Context.Headless) + if (!Context->Headless) { // NOTE(pjs): This just sets up the default panel layout - panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context); - SplitPanel(RootPanel, .25f, PanelSplit_Horizontal, &State->PanelSystem, State, Context); + panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, *Context); + SplitPanel(RootPanel, .25f, PanelSplit_Horizontal, &State->PanelSystem, State, *Context); panel* AnimPanel = RootPanel->Bottom; - Panel_SetType(AnimPanel, &State->PanelSystem, PanelType_AnimationTimeline, State, Context); + Panel_SetType(AnimPanel, &State->PanelSystem, PanelType_AnimationTimeline, State, *Context); panel* TopPanel = RootPanel->Top; - SplitPanel(TopPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, Context); + SplitPanel(TopPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context); panel* LeftPanel = TopPanel->Left; - SplitPanel(LeftPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, Context); + SplitPanel(LeftPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context); panel* Profiler = LeftPanel->Right; - Panel_SetType(Profiler, &State->PanelSystem, PanelType_ProfilerView, State, Context); + Panel_SetType(Profiler, &State->PanelSystem, PanelType_ProfilerView, State, *Context); panel* Hierarchy = LeftPanel->Left; - Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, Context); + Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, *Context); } - State->RunEditor = !Context.Headless; + State->RunEditor = !Context->Headless; } internal void diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 1009001..76eb968 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -68,7 +68,7 @@ struct packet_ringbuffer u32 WriteHead; }; -#define INITIALIZE_APPLICATION(name) void name(context Context) +#define INITIALIZE_APPLICATION(name) void name(context* Context) typedef INITIALIZE_APPLICATION(initialize_application); #define UPDATE_AND_RENDER(name) void name(context* Context, input_queue InputQueue, render_command_buffer* RenderBuffer, addressed_data_buffer_list* OutputData) diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 3957f03..f8bc0cd 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -612,8 +612,6 @@ WinMain ( context Context = {}; Context.ThreadContext = ThreadContext; - Context.MemorySize = MB(64); - Context.MemoryBase = (u8*)Win32Alloc(Context.MemorySize, 0); gs_const_string Args = ConstString((char*)CmdLineArgs); if (StringsEqual(Args, ConstString("-headless"))) @@ -680,7 +678,7 @@ WinMain ( addressed_data_buffer_list OutputData = AddressedDataBufferList_Create(ThreadContext); - Context.InitializeApplication(Context); + Context.InitializeApplication(&Context); system_time StartTime = Win32GetSystemTime(); diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index 8a6d074..1098c92 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -2607,7 +2607,7 @@ CreateMemoryArena_(arena_type ArenaType, gs_allocator Allocator, u64 ChunkSize, } internal gs_memory_arena -CreateMemoryArena(gs_allocator Allocator, char* Name, u64 ChunkSize = KB(32), u64 Alignment = Bytes(8)) +CreateMemoryArena(gs_allocator Allocator, char* Name, u64 ChunkSize = KB(4), u64 Alignment = Bytes(8)) { return CreateMemoryArena_(Arena_BaseArena, Allocator, ChunkSize, Alignment, 0, Name); } From bafbcd966a773af6352eb0c3223e93e4ab9dacfc Mon Sep 17 00:00:00 2001 From: PS Date: Wed, 31 Mar 2021 02:00:25 -0700 Subject: [PATCH 034/151] Cleaning up todos, documentation, blumen lumen status, etc. --- README.md | 6 +- compile.bat => bin/compile.bat | 0 debug.bat => bin/debug.bat | 0 run.bat => bin/run.bat | 0 build/build_app_msvc_win32_debug.bat | 2 +- src/app/editor/foldhaus_editor_draw.h | 1 - src/app/editor/foldhaus_operation_mode.h | 3 +- .../editor/panels/foldhaus_panel_profiler.h | 131 ++++++++++++------ src/app/engine/animation/foldhaus_animation.h | 24 +++- src/app/foldhaus_app.cpp | 2 - src/app/foldhaus_debug.h | 86 +++++++----- src/app/foldhaus_platform.h | 2 - src/app/foldhaus_renderer.cpp | 8 +- src/app/foldhaus_renderer.h | 9 +- src/app/platform_win32/win32_foldhaus.cpp | 49 +++---- .../platform_win32/win32_foldhaus_socket.h | 1 - .../win32_foldhaus_work_queue.h | 6 +- src/app/ss_blumen_lumen/blumen_lumen.cpp | 65 ++++++++- src/app/ss_blumen_lumen/blumen_lumen.h | 8 ++ .../ss_blumen_lumen/blumen_lumen_settings.h | 15 ++ src/gs_libs/gs_language.h | 12 +- src/gs_libs/gs_memory_arena.h | 6 +- src/gs_libs/gs_radix_sort.h | 3 +- src/gs_libs/gs_types.cpp | 7 +- src/gs_libs/gs_types.h | 5 +- src/gs_libs/gs_win32.cpp | 2 +- test_motor_header.h | 19 --- test_sound_interface_packet.h | 29 ---- 28 files changed, 294 insertions(+), 207 deletions(-) rename compile.bat => bin/compile.bat (100%) rename debug.bat => bin/debug.bat (100%) rename run.bat => bin/run.bat (100%) delete mode 100644 test_motor_header.h delete mode 100644 test_sound_interface_packet.h diff --git a/README.md b/README.md index 46e3eee..35225ed 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,14 @@ Building Lumenarium requires having MSVC installed (sorry, Windows only for now! 2. Run the appropriate build batch file - for Windows: use `build\build_app_msvc_win32_debug.bat` - other platforms coming soon +3. Build scripts will output executables into the app_run_tree directory, by platform ## Run Lumenarium Windows - Debug -1. Just run `win32_msvc\debug\win32_foldhaus.exe` +1. Run `app_run_tree\win32_msvc\debug\win32_foldhaus.exe` + +If you want to run in headless mode: +1. Run `app_run_tree\win32_msvc\debug\win32_foldhaus.exe -headless` ## Debug Lumenarium ### Windows diff --git a/compile.bat b/bin/compile.bat similarity index 100% rename from compile.bat rename to bin/compile.bat diff --git a/debug.bat b/bin/debug.bat similarity index 100% rename from debug.bat rename to bin/debug.bat diff --git a/run.bat b/bin/run.bat similarity index 100% rename from run.bat rename to bin/run.bat diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index eec2df0..7e14230 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -6,7 +6,7 @@ SET MyPath=%MyPath:~0,-1% call %MyPath%\_prebuild_win32.bat app debug msvc call %MyPath%\setup_cl.bat -SET CommonCompilerFlags=-nologo -DDEBUG=1 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src +SET CommonCompilerFlags=-nologo -DDEBUG=0 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src SET CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -I%CommonLibs% -O2 %CommonCompilerFlags% diff --git a/src/app/editor/foldhaus_editor_draw.h b/src/app/editor/foldhaus_editor_draw.h index f4a4edc..bad44a2 100644 --- a/src/app/editor/foldhaus_editor_draw.h +++ b/src/app/editor/foldhaus_editor_draw.h @@ -128,7 +128,6 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0) { - // TODO(pjs): add this color to the style v4 TextColor = BlackV4; Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, ClippedFillBounds, TextColor, -1); } diff --git a/src/app/editor/foldhaus_operation_mode.h b/src/app/editor/foldhaus_operation_mode.h index 81e12fe..46d5830 100644 --- a/src/app/editor/foldhaus_operation_mode.h +++ b/src/app/editor/foldhaus_operation_mode.h @@ -40,11 +40,10 @@ OperationModeSystemInit(gs_memory_arena* Storage, gs_thread_context ThreadContex // TODO(Peter): Do we really need an arena? Can this just operate in constant memory footprint? Result.Arena.Allocator = ThreadContext.Allocator; - Result.ModeMemoryPagesFreeList.CountMax = 32; // TODO(Peter): Static number of modes that can be active simultaneously + Result.ModeMemoryPagesFreeList.CountMax = 8; Result.ModeMemoryPagesFreeList.Data = PushArray(Storage, gs_data, Result.ModeMemoryPagesFreeList.CountMax); for (u32 Page = 0; Page < Result.ModeMemoryPagesFreeList.CountMax; Page++) { - // TODO(Peter): 4k pages = page size on windows Result.ModeMemoryPagesFreeList.Data[Page] = PushSizeToData(Storage, KB(4)); } Result.ModeMemoryPagesFreeList.Count = Result.ModeMemoryPagesFreeList.CountMax; diff --git a/src/app/editor/panels/foldhaus_panel_profiler.h b/src/app/editor/panels/foldhaus_panel_profiler.h index 82e7d6f..b8684ee 100644 --- a/src/app/editor/panels/foldhaus_panel_profiler.h +++ b/src/app/editor/panels/foldhaus_panel_profiler.h @@ -163,15 +163,42 @@ RenderProfiler_ListVisualization(ui_interface* Interface, ui_widget* Layout, deb ui_EndList(Interface); } +struct mem_amt +{ + u64 OrigSize; + r64 Size; + char* Units; +}; + +internal mem_amt +GetMemAmt (u64 BytesCount) +{ + mem_amt Result = {}; + Result.OrigSize = BytesCount; + Result.Size = (r64)BytesCount; + Result.Units = "bytes"; + + u32 i = 0; + char* UnitList[] = { "kb", "mb", "gb", "tb" }; + while (Result.Size > 1024) { + Result.Size /= 1024.0; + Result.Units = UnitList[i++]; + } + + return Result; +} + internal void RenderProfiler_MemoryView(ui_interface* Interface, ui_widget* Layout, app_state* State, context Context, gs_memory_arena* Memory) { gs_allocator_debug Debug = *Context.ThreadContext.Allocator.Debug; gs_string TempString = PushString(State->Transient, 256); - u64 MemFootprint = Debug.TotalAllocSize; + mem_amt MemFootprint = GetMemAmt(Debug.TotalAllocSize); u64 AllocCount = Debug.AllocationsCount; - PrintF(&TempString, "Total Memory Size: %lld | Allocations: %lld", MemFootprint, AllocCount); + + + PrintF(&TempString, "Total Memory Size: %.2f %s | Allocations: %lld", MemFootprint.Size, MemFootprint.Units, AllocCount); ui_Label(Interface, TempString); ui_column_spec ColumnWidths[] = { @@ -194,7 +221,9 @@ RenderProfiler_MemoryView(ui_interface* Interface, ui_widget* Layout, app_state* PrintF(&TempString, "%S", A.Location); ui_Label(Interface, TempString); - PrintF(&TempString, "%lld bytes", A.Size); + mem_amt Amt = GetMemAmt(A.Size); + + PrintF(&TempString, "%.2f %s", Amt.Size, Amt.Units); ui_Label(Interface, TempString); } ui_EndRow(Interface); @@ -216,59 +245,65 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend RectHSplitAtDistanceFromTop(PanelBounds, FrameListHeight, &FrameListBounds, &ProcListBounds); rect2 FrameListInner = RectInset(FrameListBounds, 4); - r32 SingleFrameStep = Rect2Width(FrameListInner) / DEBUG_FRAME_COUNT; - r32 SingleFrameWidth = (r32)((s32)SingleFrameStep - 2); - - ui_OutlineRect(&State->Interface, FrameListBounds, 2, WhiteV4); - if (MouseButtonHeldDown(Context.Mouse.LeftButtonState)) + s32 FramesToDisplay = DEBUG_FRAME_COUNT; + if (FramesToDisplay != 0) { - if (PointIsInRect(FrameListBounds, Context.Mouse.Pos)) + r32 SingleFrameStep = Rect2Width(FrameListInner) / FramesToDisplay; + r32 SingleFrameWidth = (r32)((s32)SingleFrameStep - 2); + + ui_OutlineRect(&State->Interface, FrameListBounds, 2, WhiteV4); + if (MouseButtonHeldDown(Context.Mouse.LeftButtonState)) { - v2 LocalMouse = Rect2GetRectLocalPoint(FrameListBounds, Context.Mouse.Pos); - s32 ClosestFrameIndex = (LocalMouse.x / SingleFrameStep); - if (ClosestFrameIndex >= 0 && ClosestFrameIndex < DEBUG_FRAME_COUNT) + if (PointIsInRect(FrameListBounds, Context.Mouse.Pos)) { - GlobalDebugServices->RecordFrames = false; - GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex; + v2 LocalMouse = Rect2GetRectLocalPoint(FrameListBounds, Context.Mouse.Pos); + s32 ClosestFrameIndex = (LocalMouse.x / SingleFrameStep); + if (ClosestFrameIndex >= 0 && ClosestFrameIndex < FramesToDisplay) + { + GlobalDebugServices->RecordFrames = false; + GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex; + } } } + + rect2 FrameBounds = MakeRect2MinDim(FrameListInner.Min, v2{SingleFrameWidth, Rect2Height(FrameListInner)}); + for (s32 F = 0; F < DEBUG_FRAME_COUNT; F++) + { + rect2 PositionedFrameBounds = Rect2TranslateX(FrameBounds, F * SingleFrameStep); + s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F); + if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; } + v4 Color = FrameColors[Clamp(0, FramesAgo, 3)]; + ui_FillRect(&State->Interface, PositionedFrameBounds, Color); + } } - rect2 FrameBounds = MakeRect2MinDim(FrameListInner.Min, v2{SingleFrameWidth, Rect2Height(FrameListInner)}); - for (s32 F = 0; F < DEBUG_FRAME_COUNT; F++) - { - rect2 PositionedFrameBounds = Rect2TranslateX(FrameBounds, F * SingleFrameStep); - s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F); - if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; } - v4 Color = FrameColors[Clamp(0, FramesAgo, 3)]; - ui_FillRect(&State->Interface, PositionedFrameBounds, Color); - } - - debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices); - ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown, MakeString("Profiler Layout")); - ui_BeginRow(&State->Interface, 4); + debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices); + if (VisibleFrame) { - s64 FrameStartCycles = VisibleFrame->FrameStartCycles; - s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles; - u32 CurrentDebugFrame = GlobalDebugServices->CurrentDebugFrame - 1; - PrintF(&String, "Frame %d", CurrentDebugFrame); - ui_Label(&State->Interface, String); - - PrintF(&String, "Total Cycles: %lld", FrameTotalCycles); - ui_Label(&State->Interface, String); - - // NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could - // be removed, or used for something else - ui_ReserveBounds(&State->Interface, Layout, true); - - if (ui_Button(&State->Interface, MakeString("Resume Recording"))) + ui_BeginRow(&State->Interface, 4); { - GlobalDebugServices->RecordFrames = true; + s64 FrameStartCycles = VisibleFrame->FrameStartCycles; + s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles; + u32 CurrentDebugFrame = GlobalDebugServices->CurrentDebugFrame - 1; + PrintF(&String, "Frame %d", CurrentDebugFrame); + ui_Label(&State->Interface, String); + + PrintF(&String, "Total Cycles: %lld", FrameTotalCycles); + ui_Label(&State->Interface, String); + + // NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could + // be removed, or used for something else + ui_ReserveBounds(&State->Interface, Layout, true); + + if (ui_Button(&State->Interface, MakeString("Resume Recording"))) + { + GlobalDebugServices->RecordFrames = true; + } } + ui_EndRow(&State->Interface); } - ui_EndRow(&State->Interface); ui_BeginRow(&State->Interface, 8); { @@ -291,12 +326,18 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend { case DebugUI_Profiler: { - RenderProfiler_ScopeVisualization(&State->Interface, Layout, VisibleFrame, Memory); + if (VisibleFrame) + { + RenderProfiler_ScopeVisualization(&State->Interface, Layout, VisibleFrame, Memory); + } }break; case DebugUI_ScopeList: { - RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory); + if (VisibleFrame) + { + RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory); + } }break; case DebugUI_MemoryView: diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 2385238..313ee68 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -74,8 +74,6 @@ struct animation gs_string Name; anim_layer_array Layers; - // TODO(pjs): Pretty sure Blocks_ should be obsolete and - // Layers should contain their own blocks animation_block_array Blocks_; frame_range PlayableRange; @@ -169,6 +167,7 @@ struct animation_system // panel animation_fade_group ActiveFadeGroup; + r32 SecondsOnCurrentFrame; s32 CurrentFrame; s32 LastUpdatedFrame; r32 SecondsPerFrame; @@ -762,13 +761,28 @@ AnimationSystem_Update(animation_system* System, r32 DeltaTime) animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); if (ActiveAnim) { - // TODO(Peter): Revisit this. This implies that the framerate of the animation system - // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure - System->CurrentFrame += 1; + System->SecondsOnCurrentFrame += DeltaTime; + while (System->SecondsOnCurrentFrame > System->SecondsPerFrame) + { + System->CurrentFrame += 1; + System->SecondsOnCurrentFrame -= System->SecondsPerFrame; + } // Loop back to the beginning if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) { + // NOTE(PS): There's no long term reason why this needs to be true + // but I don't want to implement dealing with PlayableRanges that + // don't start at zero right now becuse there's literally no reason + // I can think of where that's useful. + Assert(ActiveAnim->PlayableRange.Min == 0); + + s32 FramesPastEnd = System->CurrentFrame; + while (FramesPastEnd > ActiveAnim->PlayableRange.Max) + { + FramesPastEnd -= ActiveAnim->PlayableRange.Max; + } + switch (System->RepeatMode) { case AnimationRepeat_Single: diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 0712c40..e71f40b 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -81,8 +81,6 @@ INITIALIZE_APPLICATION(InitializeApplication) ReloadStaticData(*Context, GlobalDebugServices, true); US_CustomInit(&State->UserSpaceDesc, State, *Context); - GlobalDebugServices->Interface.RenderSculpture = true; - if (!Context->Headless) { // NOTE(pjs): This just sets up the default panel layout diff --git a/src/app/foldhaus_debug.h b/src/app/foldhaus_debug.h index c6b7133..8c99d6e 100644 --- a/src/app/foldhaus_debug.h +++ b/src/app/foldhaus_debug.h @@ -75,11 +75,6 @@ enum debug_ui_view struct debug_interface { - b32 ShowCameraMouse; - b32 ShowTrackedScopes; - b32 RenderSculpture; - b32 SendSACNData; - s32 FrameView; }; @@ -105,34 +100,39 @@ struct debug_histogram_entry u32 Total_CallCount; }; -#define DEBUG_FRAME_COUNT 128 +#if DEBUG +# define DEBUG_FRAME_COUNT 128 +#else +# define DEBUG_FRAME_COUNT 0 +#endif + struct debug_services { s64 PerformanceCountFrequency; b32 RecordFrames; s32 CurrentDebugFrame; - debug_frame Frames[DEBUG_FRAME_COUNT]; + debug_frame* Frames; debug_interface Interface; + gs_thread_context Ctx; + debug_get_thread_id* GetThreadId; debug_timing_proc* GetWallClock; - debug_alloc* Alloc; - debug_realloc* Realloc; }; internal void InitializeDebugFrame (debug_frame* Frame, s32 NameHashMax, s32 ThreadCount, s32 ScopeCallsMax, debug_services* Services) { Frame->ScopeNamesMax = NameHashMax; - Frame->ScopeNamesHash = (scope_name*)Services->Alloc(sizeof(scope_name), NameHashMax); + Frame->ScopeNamesHash = AllocatorAllocArray(Services->Ctx.Allocator, scope_name, NameHashMax); // NOTE(Peter): We use the same size as scope names because we're only storing a single instance // per scope. If ScopeNamesMax can't hold all the scopes, this will never get filled and // we should assert and recompile with a resized NameHashMax Frame->CollatedScopesMax = NameHashMax; - Frame->CollatedScopes = (collated_scope_record*)Services->Alloc(sizeof(collated_scope_record), NameHashMax); + Frame->CollatedScopes = AllocatorAllocArray(Services->Ctx.Allocator, collated_scope_record, NameHashMax); for (s32 i = 0; i < Frame->ScopeNamesMax; i++) { @@ -141,14 +141,13 @@ InitializeDebugFrame (debug_frame* Frame, s32 NameHashMax, s32 ThreadCount, s32 } Frame->ThreadCount = ThreadCount; - Frame->ThreadCalls = (debug_scope_record_list*)Services->Alloc(sizeof(debug_scope_record_list), - ThreadCount); + Frame->ThreadCalls = AllocatorAllocArray(Services->Ctx.Allocator, debug_scope_record_list, ThreadCount); for (s32 i = 0; i < ThreadCount; i++) { Frame->ThreadCalls[i].Max = ScopeCallsMax; Frame->ThreadCalls[i].Count = 0; - Frame->ThreadCalls[i].Calls = (scope_record*)Services->Alloc(sizeof(scope_record), ScopeCallsMax); + Frame->ThreadCalls[i].Calls = AllocatorAllocArray(Services->Ctx.Allocator, scope_record, ScopeCallsMax); Frame->ThreadCalls[i].CurrentScopeCallDepth = 0; Frame->ThreadCalls[i].ThreadId = 0; } @@ -178,16 +177,34 @@ StartDebugFrame(debug_frame* Frame, debug_services* Services) } internal void -InitDebugServices (debug_services* Services, - s64 PerformanceCountFrequency, - debug_alloc* Alloc, - debug_realloc* Realloc, - debug_timing_proc* GetWallClock, - debug_get_thread_id* GetThreadId, - s32 ThreadCount) +InitDebugServices_OffMode (debug_services* Services, + s64 PerformanceCountFrequency, + debug_timing_proc* GetWallClock, + debug_get_thread_id* GetThreadId, + gs_thread_context Ctx, + s32 ThreadCount) { - Services->Alloc = Alloc; - Services->Realloc = Realloc; + Services->Ctx = Ctx; + Services->GetWallClock = GetWallClock; + Services->GetThreadId = GetThreadId; + + Services->RecordFrames = false; + Services->Frames = 0; + + Services->CurrentDebugFrame = 0; + Services->PerformanceCountFrequency = PerformanceCountFrequency; +} + + +internal void +InitDebugServices_DebugMode (debug_services* Services, + s64 PerformanceCountFrequency, + debug_timing_proc* GetWallClock, + debug_get_thread_id* GetThreadId, + gs_thread_context Ctx, + s32 ThreadCount) +{ + Services->Ctx = Ctx; Services->GetWallClock = GetWallClock; Services->GetThreadId = GetThreadId; @@ -196,19 +213,13 @@ InitDebugServices (debug_services* Services, Services->CurrentDebugFrame = 0; s32 NameHashMax = 4096; s32 ScopeCallsMax = 4096; + Services->Frames = AllocatorAllocArray(Ctx.Allocator, debug_frame, DEBUG_FRAME_COUNT); for (s32 i = 0; i < DEBUG_FRAME_COUNT; i++) { InitializeDebugFrame(&Services->Frames[i], NameHashMax, ThreadCount, ScopeCallsMax, Services); } - Services->Interface.RenderSculpture = true; - Services->PerformanceCountFrequency = PerformanceCountFrequency; - - Services->Interface.ShowCameraMouse = false; - Services->Interface.ShowTrackedScopes = false; - Services->Interface.RenderSculpture = true; - Services->Interface.SendSACNData = false; } internal debug_frame* @@ -221,6 +232,8 @@ GetCurrentDebugFrame (debug_services* Services) internal debug_frame* GetLastDebugFrame(debug_services* Services) { + if (!Services->Frames) return 0; + s32 Index = (Services->CurrentDebugFrame - 1); if (Index < 0) { Index += DEBUG_FRAME_COUNT; } debug_frame* Result = Services->Frames + Index; @@ -323,7 +336,11 @@ EndDebugFrame (debug_services* Services) } ClosingFrame->ScopeNamesCount = ScopeNamesCount; - Services->CurrentDebugFrame = (Services->CurrentDebugFrame + 1) % DEBUG_FRAME_COUNT; + s32 FramesCount = DEBUG_FRAME_COUNT; + if (FramesCount > 0) + { + Services->CurrentDebugFrame = (Services->CurrentDebugFrame + 1) % FramesCount; + } StartDebugFrame(&Services->Frames[Services->CurrentDebugFrame], Services); } @@ -388,10 +405,9 @@ PushScopeTimeOnFrame (debug_services* Services, s32 NameHash, u64 StartCycles, u if (ThreadList->Count >= ThreadList->Max) { - s32 CurrentSize = ThreadList->Max * sizeof(scope_record); s32 NewMax = (ThreadList->Max + DEBUG_FRAME_GROW_SIZE); - s32 NewSize = NewMax * sizeof(scope_record); - ThreadList->Calls = (scope_record*)Services->Realloc((u8*)ThreadList->Calls, CurrentSize, NewSize); + AllocatorFreeArray(Services->Ctx.Allocator, ThreadList->Calls, scope_record, ThreadList->Max); + ThreadList->Calls = AllocatorAllocArray(Services->Ctx.Allocator, scope_record, NewMax); ThreadList->Max = NewMax; } @@ -411,7 +427,7 @@ internal r32 DEBUGGetSecondsElapsed (s64 Start, s64 End, r32 PerformanceCountFre return Result; } -#ifdef DEBUG +#if DEBUG #define DEBUG_TRACK_FUNCTION scope_tracker ScopeTracker ((char*)__func__, GlobalDebugServices) #define DEBUG_TRACK_SCOPE(name) scope_tracker ScopeTracker_##name (#name, GlobalDebugServices) #else diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 76eb968..d2d190b 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -202,8 +202,6 @@ typedef struct system_time s32 Second; } system_time; -#define STATUS_PACKET_FREQ_SECONDS 2 - struct context { gs_thread_context ThreadContext; diff --git a/src/app/foldhaus_renderer.cpp b/src/app/foldhaus_renderer.cpp index bfd6522..548acda 100644 --- a/src/app/foldhaus_renderer.cpp +++ b/src/app/foldhaus_renderer.cpp @@ -6,22 +6,22 @@ #ifndef FOLDHAUS_RENDERER_CPP internal render_command_buffer -AllocateRenderCommandBuffer (u8* Memory, s32 Size, renderer_realloc* Realloc) +AllocateRenderCommandBuffer (u8* Memory, s32 Size, gs_thread_context Ctx) { render_command_buffer Result = {}; Result.CommandMemory = Memory; Result.CommandMemorySize = Size; Result.CommandMemoryUsed = 0; - Result.Realloc = Realloc; + Result.Ctx = Ctx; return Result; } internal render_command_buffer AllocateRenderCommandBuffer(u32 MemorySize, gs_memory_arena* Arena, - renderer_realloc* Realloc) + gs_thread_context Ctx) { u8* Memory = PushSize(Arena, MemorySize); - return AllocateRenderCommandBuffer(Memory, MemorySize, Realloc); + return AllocateRenderCommandBuffer(Memory, MemorySize, Ctx); } internal void diff --git a/src/app/foldhaus_renderer.h b/src/app/foldhaus_renderer.h index 7e25346..0a264de 100644 --- a/src/app/foldhaus_renderer.h +++ b/src/app/foldhaus_renderer.h @@ -124,7 +124,6 @@ struct render_quad_3d struct render_texture { - // TODO(Peter): Is all this necessary? u8* Memory; s32 Handle; s32 Width; @@ -214,7 +213,7 @@ struct render_command_buffer s32 CommandMemoryUsed; s32 CommandMemorySize; - renderer_realloc* Realloc; + gs_thread_context Ctx; s32 ViewWidth; s32 ViewHeight; @@ -265,9 +264,9 @@ ResizeBufferIfNecessary(render_command_buffer* Buffer, s32 DataSize) s32 SpaceNeeded = DataSize - SpaceAvailable; // This is known to be positive at this point s32 AdditionSize = Max(SpaceNeeded, COMMAND_BUFFER_MIN_GROW_SIZE); s32 NewSize = Buffer->CommandMemorySize + AdditionSize; - Buffer->CommandMemory = Buffer->Realloc(Buffer->CommandMemory, - Buffer->CommandMemorySize, - NewSize); + + AllocatorFree(Buffer->Ctx.Allocator, Buffer->CommandMemory, Buffer->CommandMemorySize); + Buffer->CommandMemory = AllocatorAlloc(Buffer->Ctx.Allocator, NewSize).Memory; Buffer->CommandMemorySize = NewSize; } } diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index f8bc0cd..d284503 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -356,22 +356,6 @@ SetApplicationLinks (context* Context, win32_dll_refresh DLL, gs_work_queue* Wor } } -// TODO(Peter): :Redundant remove -internal u8* -DEBUGAlloc(s32 ElementSize, s32 ElementCount) -{ - return (u8*)Win32Alloc(ElementSize * ElementCount, 0); -} - -// TODO(Peter): :Redundant remove -internal u8* -Win32Realloc(u8* Buf, s32 OldSize, s32 NewSize) -{ - u8* NewMemory = (u8*)Win32Alloc(NewSize, 0); - CopyMemoryTo(Buf, NewMemory, OldSize); - return NewMemory; -} - internal void Win32_SendAddressedDataBuffer(gs_thread_context Context, addressed_data_buffer* BufferAt) { @@ -604,7 +588,7 @@ WinMain ( gs_allocator_debug AllocDebug = {}; AllocDebug.AllocationsCountMax = 4096; - AllocDebug.Allocations = (gs_debug_allocation*)Win32Alloc(sizeof(gs_debug_allocation) * AllocDebug.AllocationsCountMax, 0); + AllocDebug.Allocations = AllocatorAllocArray(ThreadContext.Allocator, gs_debug_allocation, AllocDebug.AllocationsCountMax); ThreadContext.Allocator.Debug = &AllocDebug; @@ -636,17 +620,25 @@ WinMain ( s64 PerformanceCountFrequency = GetPerformanceFrequency(); s64 LastFrameEnd = GetWallClock(); - r32 TargetSecondsPerFrame = 1 / 60.0f; + r32 TargetSecondsPerFrame = 1 / 30.0f; r32 LastFrameSecondsElapsed = 0.0f; GlobalDebugServices = PushStruct(&PlatformPermanent, debug_services); - InitDebugServices(GlobalDebugServices, - PerformanceCountFrequency, - DEBUGAlloc, - Win32Realloc, - GetWallClock, - Win32GetThreadId, - PLATFORM_THREAD_COUNT + 1); +#if DEBUG + InitDebugServices_DebugMode(GlobalDebugServices, + PerformanceCountFrequency, + GetWallClock, + Win32GetThreadId, + Context.ThreadContext, + PLATFORM_THREAD_COUNT + 1); +#else + InitDebugServices_OffMode(GlobalDebugServices, + PerformanceCountFrequency, + GetWallClock, + Win32GetThreadId, + Context.ThreadContext, + PLATFORM_THREAD_COUNT + 1); +#endif input_queue InputQueue = InputQueue_Create(&PlatformPermanent, 32); @@ -674,7 +666,11 @@ WinMain ( Win32SerialArray_Create(ThreadContext); - render_command_buffer RenderBuffer = AllocateRenderCommandBuffer(MB(12), &PlatformPermanent, Win32Realloc); + render_command_buffer RenderBuffer = {}; + if (!Context.Headless) + { + RenderBuffer = AllocateRenderCommandBuffer(MB(12), &PlatformPermanent, ThreadContext); + } addressed_data_buffer_list OutputData = AddressedDataBufferList_Create(ThreadContext); @@ -690,6 +686,7 @@ WinMain ( { EndDebugFrame(GlobalDebugServices); } + DEBUG_TRACK_SCOPE(MainLoop); { diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index 2cb326a..b67107f 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -251,7 +251,6 @@ Win32SocketPeek(platform_socket_manager* Manager, platform_socket* Socket) } else { - // TODO(pjs): Error handling s32 Error = WSAGetLastError(); switch (Error) { diff --git a/src/app/platform_win32/win32_foldhaus_work_queue.h b/src/app/platform_win32/win32_foldhaus_work_queue.h index 8d18986..5f9ce27 100644 --- a/src/app/platform_win32/win32_foldhaus_work_queue.h +++ b/src/app/platform_win32/win32_foldhaus_work_queue.h @@ -63,7 +63,7 @@ Win32CreateThreadContext(gs_memory_arena* Transient = 0) PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue) { -#ifdef DEBUG +#if DEBUG // NOTE(Peter): Just prints out the names of all the pending jobs if we end up // overflowing the buffer if (Queue->JobsCount >= Queue->JobsMax) @@ -82,7 +82,7 @@ PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue) gs_threaded_job* Job = Queue->Jobs + Queue->JobsCount; Job->WorkProc = WorkProc; Job->Data = Data; -#ifdef DEBUG +#if DEBUG Job->JobName = JobName; #endif @@ -184,7 +184,7 @@ CREATE_THREAD(Win32CreateThread) Thread->UserData = UserData; // TODO(pjs): ugh, allocation out in the middle of nowhere - HANDLE* ThreadHandle = (HANDLE*)Win32Alloc(sizeof(HANDLE), 0); + HANDLE* ThreadHandle = AllocatorAllocStruct(Ctx.Allocator, HANDLE); *ThreadHandle = CreateThread(0, 0, Win32ThreadProcWrapper, (void*)Thread, 0, 0); // TODO(pjs): Error checking on the created thread diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index ee55776..d287917 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -149,7 +149,6 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) { if (SocketPeek(Data->SocketManager, ListenSocket)) { - // TODO(pjs): Make this a peek operation Msg = SocketRecieve(Data->SocketManager, ListenSocket, Ctx->Transient); if (Msg.Size > 0) { @@ -233,7 +232,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->PatternSpeed = GlobalAnimSpeed; #if 1 - BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData); + BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData, Context.ThreadContext); #endif assembly* Flower0 = LoadAssembly(Flower0AssemblyPath, State, Context); @@ -276,10 +275,54 @@ BlumenLumen_CustomInit(app_state* State, context Context) return Result; } +internal void +BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Context) +{ + if (!BLState->ShouldUpdateLog) return; + + gs_string FileStr = PushString(State->Transient, 1024); + AppendPrintF(&FileStr, "Lumenarium Status\n\n"); + + animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); + + char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected"; + AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected); + + u8 MP0 = BLState->LastKnownMotorState.FlowerPositions[0]; + u8 MP1 = BLState->LastKnownMotorState.FlowerPositions[1]; + u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2]; + AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2); + + char* PatternMode = 0; + switch (BLState->PatternMode) + { + case BlumenPattern_Standard: { PatternMode = "Standard"; } break; + case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break; + } + AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode); + + phrase_hue LastHuePhrase = BLState->LastHuePhrase; + AppendPrintF(&FileStr, "Last Mic Phrase: %S\n", LastHuePhrase.Phrase); + + AppendPrintF(&FileStr, "Pattern Speed: %f\n", BLState->PatternSpeed); + + AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent); + + AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived); + + gs_data LogMem = StringToData(FileStr); + if (!WriteEntireFile(Context.ThreadContext.FileHandler, ConstString("lumenarium_status.log"), LogMem)) + { + InvalidCodePath; + } +} + internal void BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + BLState->ShouldUpdateLog = false; bool SendMotorCommand = false; blumen_packet MotorCommand = {}; @@ -314,7 +357,9 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, 5, &State->AnimationSystem, BLState); BLState->TimeLastSetToVoiceMode = Context->SystemTime_Current; + BLState->LastHuePhrase = NewHue; } + BLState->ShouldUpdateLog = true; }break; case PacketType_MotorState: @@ -340,14 +385,14 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } BLState->LastKnownMotorState = Motor.Pos; - + BLState->ShouldUpdateLog = true; }break; case PacketType_Temperature: { temp_packet Temp = Packet.TempPacket; - if (Temp.Temperature > 0) + if (Temp.Temperature > MinHighTemperature) { BLState->BrightnessPercent = HighTemperatureBrightnessPercent; } @@ -355,8 +400,10 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { BLState->BrightnessPercent = FullBrightnessPercent; } + BLState->LastTemperatureReceived = Temp.Temperature; DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); + BLState->ShouldUpdateLog = true; }break; InvalidDefaultCase; @@ -392,6 +439,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, NewAnim, VoiceCommandFadeDuration); + BLState->ShouldUpdateLog = true; } } @@ -517,7 +565,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { system_time LastSendTime = BLState->LastStatusUpdateTime; r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)LastSendTime.NanosSinceEpoch); - r64 SecondsSinceLastSend = NanosSinceLastSend / PowR32(10, 8); + r64 SecondsSinceLastSend = NanosSinceLastSend * NanosToSeconds; if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) { BLState->LastStatusUpdateTime = Context->SystemTime_Current; @@ -538,8 +586,15 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) gs_data Msg = StructToData(&Packet, blumen_packet); MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + + // there's no new information here, but by updating the log here, + // we're updating it at some infrequent but regular period that isnt + // every single frame + BLState->ShouldUpdateLog = true; } } + + BlumenLumen_UpdateLog(State, BLState, *Context); } US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 6dd55ac..cc271d7 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -109,6 +109,9 @@ struct mic_listen_job_data blumen_network_msg_queue* IncomingMsgQueue; blumen_network_msg_queue* OutgoingMsgQueue; + + // Status + bool IsConnected; }; typedef struct time_range @@ -151,6 +154,7 @@ struct blumen_lumen_state // NOTE(pjs): Based on temperature data from weatherman // dim the leds. r32 BrightnessPercent; + s8 LastTemperatureReceived; system_time LastStatusUpdateTime; system_time LastSendTime; @@ -172,11 +176,15 @@ struct blumen_lumen_state phrase_hue_map PhraseHueMap; system_time TimeLastSetToVoiceMode; + phrase_hue LastHuePhrase; r32 PatternSpeed; + // Debug motor_packet DEBUG_PendingMotorPacket; bool DEBUG_IgnoreWeatherDimmingLeds; + + bool ShouldUpdateLog; }; #include "message_queue.cpp" diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 97b6686..809106e 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -111,8 +111,17 @@ r64 VoiceCommandSustainDuration = 30.0; // in seconds // to have been on the whole time. r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 5.0; // in seconds + // NOTE: Temperature & Time of Day Based Led Brightness Settings +// The temperature above which we dim the leds to +// HighTemperatureBrightnessPercent (below) +// +// NOTE: this is an 8bit signed integer so its range is +// -128 to 127, not that we should need either of those extremes for +// this, but just a note that you can't just put anything in here. +s8 MinHighTemperature = 0; + // The percent brightness we set leds to during high temperatures. // A value in the range 0:1 inclusive // This is multiplied by each pixels R, G, & B channels before being @@ -128,6 +137,12 @@ r32 FullBrightnessPercent = 1.0f; // A global modifier so Joerg can just slow all the patterns right down // XD +// This is a percent - so 1 is full speed, 0.1f is 1/10 full speed and +// 2 is 2x as fast. r32 GlobalAnimSpeed = 1.0f; +// How often should Lumenarium send its status to the python server? +// +#define STATUS_PACKET_FREQ_SECONDS 2 // in seconds + #endif //BLUMEN_LUMEN_SETTINGS_H diff --git a/src/gs_libs/gs_language.h b/src/gs_libs/gs_language.h index b95b246..3e31391 100644 --- a/src/gs_libs/gs_language.h +++ b/src/gs_libs/gs_language.h @@ -85,7 +85,7 @@ typedef double r64; #endif -#ifdef DEBUG +#if DEBUG static void DebugPrint(char* Format, ...); @@ -95,8 +95,8 @@ static void DebugPrint(char* Format, ...); if((expression)) \ { \ }else{ \ - volatile int* p = 0; \ - *p = 5; \ +volatile int* p = 0; \ +*p = 5; \ } #endif @@ -319,9 +319,9 @@ GSIntDivideRoundUpDef(s64); #define GSRemapDef(type) \ static type GSRemap(type Value, type OldMin, type OldMax, type NewMin, type NewMax) { \ - type Result = (Value - OldMin) / (OldMax - OldMin); \ - Result = (Result * (NewMax - NewMin)) + NewMin; \ - return Result; \ +type Result = (Value - OldMin) / (OldMax - OldMin); \ +Result = (Result * (NewMax - NewMin)) + NewMin; \ +return Result; \ } GSRemapDef(u8); GSRemapDef(u16); diff --git a/src/gs_libs/gs_memory_arena.h b/src/gs_libs/gs_memory_arena.h index 4fbeb3b..e4bad68 100644 --- a/src/gs_libs/gs_memory_arena.h +++ b/src/gs_libs/gs_memory_arena.h @@ -153,13 +153,13 @@ typedef unsigned int gs_mem_u32; typedef unsigned long long int gs_mem_u64; -#ifdef DEBUG +#if DEBUG #if !defined(GSMem_Assert) #define GSMem_Assert(expression) \ if((expression)) { \ }else{ \ - volatile int* p = 0; \ - *p = 5; \ +volatile int* p = 0; \ +*p = 5; \ } #endif diff --git a/src/gs_libs/gs_radix_sort.h b/src/gs_libs/gs_radix_sort.h index 8e62a27..cf4464d 100644 --- a/src/gs_libs/gs_radix_sort.h +++ b/src/gs_libs/gs_radix_sort.h @@ -1,12 +1,11 @@ /* gs_radix_sort.h - An implementation of radix sort for fixed size unsigned 32bit integer buffers -TODO */ #ifndef GS_RADIX_SORT_H -#ifdef DEBUG +#if DEBUG #if !defined(GSRad_Assert) #define GSRad_Assert(expression) \ if(!(expression)) { \ diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index 1098c92..28238c4 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -2474,7 +2474,6 @@ AllocatorDebug_PushAlloc(gs_allocator_debug* Debug, u64 Size, char* Location) internal gs_data AllocatorAlloc_(gs_allocator Allocator, u64 Size, char* Location) { - // TODO(Peter): Memory Profiling with Location u64 SizeResult = 0; void* Memory = Allocator.Alloc(Size, &SizeResult); if (Allocator.Debug) @@ -2486,7 +2485,6 @@ AllocatorAlloc_(gs_allocator Allocator, u64 Size, char* Location) internal void AllocatorFree_(gs_allocator Allocator, void* Base, u64 Size, char* Location) { - // TODO(Peter): Memory Profiling with Location if (Base != 0 && Size != 0) { Allocator.Free(Base, Size); @@ -3498,7 +3496,7 @@ CreatePlatformThreadManager(platform_create_thread* CreateThreadProc, } internal platform_thread_handle -CreateThread(platform_thread_manager* Manager, thread_proc_* Proc, u8* Arg) +CreateThread(platform_thread_manager* Manager, thread_proc_* Proc, u8* Arg, gs_thread_context Ctx) { platform_thread_handle Result = {}; @@ -3513,7 +3511,7 @@ CreateThread(platform_thread_manager* Manager, thread_proc_* Proc, u8* Arg) Assert(Result.Index != 0); Manager->ThreadsUsed[Result.Index] = true; - Manager->CreateThreadProc(&Manager->Threads[Result.Index], Proc, Arg); + Manager->CreateThreadProc(&Manager->Threads[Result.Index], Proc, Arg, Ctx); return Result; } @@ -3719,7 +3717,6 @@ CloseSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle) } // NOTE(pjs): returns true if the socket is connected -// TODO(pjs): make this more descriptive? internal bool SocketQueryStatus(platform_socket_manager* Manager, platform_socket_handle_ SocketHandle) { diff --git a/src/gs_libs/gs_types.h b/src/gs_libs/gs_types.h index 49513f0..7356fbb 100644 --- a/src/gs_libs/gs_types.h +++ b/src/gs_libs/gs_types.h @@ -140,8 +140,6 @@ global_const r64 EpsilonR64 = 1.11022302462515650e-16; global_const r64 NanosToSeconds = 1 / 10000000.0; global_const r64 SecondsToNanos = 10000000.0; -// TODO: va_start and va_arg replacements - internal r32 DegToRadR32(r32 Degrees) { @@ -1090,10 +1088,9 @@ typedef struct platform_thread u8* PlatformHandle; thread_proc_* Proc; u8* UserData; - // TODO(pjs): Some kind of platform thread handle } platform_thread; -#define CREATE_THREAD(name) bool name(platform_thread* Thread, thread_proc_* Proc, u8* UserData) +#define CREATE_THREAD(name) bool name(platform_thread* Thread, thread_proc_* Proc, u8* UserData, gs_thread_context Ctx) typedef CREATE_THREAD(platform_create_thread); #define KILL_THREAD(name) bool name(platform_thread* Thread) diff --git a/src/gs_libs/gs_win32.cpp b/src/gs_libs/gs_win32.cpp index b96ae2e..120bbd1 100644 --- a/src/gs_libs/gs_win32.cpp +++ b/src/gs_libs/gs_win32.cpp @@ -40,7 +40,7 @@ struct window struct handle_window_msg_result { b32 NeedsUpdate; -#ifdef DEBUG +#if DEBUG char MessageType[128]; #endif }; diff --git a/test_motor_header.h b/test_motor_header.h deleted file mode 100644 index 3a443b3..0000000 --- a/test_motor_header.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// File: test_motor_header.h -// Author: Peter Slattery -// Creation Date: 2021-01-14 -// -#ifndef TEST_MOTOR_HEADER_H - -struct -{ - u8 MotorOnePos; - u8 MotorTwoPos; - u8 MotorThreePos; -} typedef packet; - -static packet -UnpackPacket - -#define TEST_MOTOR_HEADER_H -#endif // TEST_MOTOR_HEADER_H \ No newline at end of file diff --git a/test_sound_interface_packet.h b/test_sound_interface_packet.h deleted file mode 100644 index a046c21..0000000 --- a/test_sound_interface_packet.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// File: test_sound_interface_packet.h -// Author: Peter Slattery -// Creation Date: 2021-01-14 -// -#ifndef TEST_SOUND_INTERFACE_PACKET_H - -enum selectable_patterns -{ -}; - -typedef struct -{ - u8 PrimaryColor[3]; - u8 SecondaryColor[3]; - u32 PatternIndex; // Set via selectable_patterns - u8 Speed; // -} sound_interface_packet; - -// expects V to be in the range -1:1 -u8 FloatToSpeed (float V) -{ - u8 Result = (u8)(((V + 1) / 2) * 255); - return Result; -} - - -#define TEST_SOUND_INTERFACE_PACKET_H -#endif // TEST_SOUND_INTERFACE_PACKET_H \ No newline at end of file From 9e1b5d45f00b4240fcd1686d1a808e5ec733763b Mon Sep 17 00:00:00 2001 From: PS Date: Wed, 31 Mar 2021 02:04:03 -0700 Subject: [PATCH 035/151] Added some info to the blumen status log --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 7 ++++++- src/app/ss_blumen_lumen/blumen_lumen_settings.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index d287917..cf9e01b 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -281,7 +281,12 @@ BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Con if (!BLState->ShouldUpdateLog) return; gs_string FileStr = PushString(State->Transient, 1024); - AppendPrintF(&FileStr, "Lumenarium Status\n\n"); + AppendPrintF(&FileStr, "Lumenarium Status\n"); + + system_time Time = Context.SystemTime_Current; + AppendPrintF(&FileStr, "Last Updated At: %d-%d-%d : %d:%d:%d\n\n", + Time.Year, Time.Month, Time.Day, + Time.Hour, Time.Minute, Time.Second); animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 809106e..ab38ed8 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -143,6 +143,6 @@ r32 GlobalAnimSpeed = 1.0f; // How often should Lumenarium send its status to the python server? // -#define STATUS_PACKET_FREQ_SECONDS 2 // in seconds +#define STATUS_PACKET_FREQ_SECONDS 10 // in seconds #endif //BLUMEN_LUMEN_SETTINGS_H From 1f06b183a6e7d5d9b78b6234a489fa3982583ef2 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Wed, 31 Mar 2021 02:13:37 -0700 Subject: [PATCH 036/151] Added in connection status to the blumen status log --- src/app/foldhaus_debug.h | 2 ++ src/app/ss_blumen_lumen/blumen_lumen.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/app/foldhaus_debug.h b/src/app/foldhaus_debug.h index 8c99d6e..00a494c 100644 --- a/src/app/foldhaus_debug.h +++ b/src/app/foldhaus_debug.h @@ -184,6 +184,8 @@ InitDebugServices_OffMode (debug_services* Services, gs_thread_context Ctx, s32 ThreadCount) { + *Services = {0}; + Services->Ctx = Ctx; Services->GetWallClock = GetWallClock; Services->GetThreadId = GetThreadId; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index cf9e01b..ee6b42c 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -133,6 +133,7 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) { if (!SocketQueryStatus(Data->SocketManager, ListenSocket)) { + Data->IsConnected = false; if (SocketHandleIsValid(ListenSocket)) { OutputDebugStringA("Disconnected from Python Server\n"); @@ -142,6 +143,7 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) if (ListenSocket.Index != 0) { OutputDebugStringA("Connected to Python Server\n"); + Data->IsConnected = true; } } From 29e6c640ad784e28f676eadb40abdfd9b68d5c77 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Wed, 31 Mar 2021 02:19:13 -0700 Subject: [PATCH 037/151] Added respecting global pattern speed to every pattern --- src/app/patterns/blumen_patterns.h | 35 ++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 62b913f..9e155c5 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -497,6 +497,9 @@ while (Hue > 360.0f) { Hue -= 360.0f; } internal void Pattern_AltBloomMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + v3 SphereCenter = Assembly.Center - v3{0, -150, 0}; r32 SphereRadius = Time; r32 SphereBrightness = 1; @@ -513,6 +516,9 @@ Pattern_AltBloomMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly internal void Pattern_HueShift(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + r32 Height = SinR32(Time) * 25; r32 CycleLength = 5.0f; @@ -543,6 +549,9 @@ Pattern_HueShift(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r3 internal void Pattern_Rainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + r32 HueBase = ModR32(Time * 50, 360); r32 CycleLength = 5.0f; @@ -563,6 +572,9 @@ Pattern_Rainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 internal void Pattern_RadialRainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + v2 RefVector = V2Normalize(v2{ SinR32(Time), CosR32(Time) }); for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { @@ -585,6 +597,8 @@ internal void Pattern_BasicFlowers(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); @@ -621,8 +635,9 @@ internal void Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { DEBUG_TRACK_FUNCTION; - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); @@ -682,8 +697,9 @@ internal void Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { DEBUG_TRACK_FUNCTION; - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); @@ -743,8 +759,9 @@ internal void Pattern_Leafy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { DEBUG_TRACK_FUNCTION; - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); @@ -779,6 +796,7 @@ Pattern_LeafyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, { DEBUG_TRACK_FUNCTION; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); @@ -812,7 +830,8 @@ Pattern_LeafyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, internal void Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { - + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; } internal void @@ -820,6 +839,8 @@ Pattern_VerticalLines(led_buffer* Leds, led_buffer_range Range, assembly Assembl { DEBUG_TRACK_FUNCTION; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + gs_random_series Random = InitRandomSeries(24601); r32 LightSpeedMin = 1; @@ -853,6 +874,9 @@ Pattern_VerticalLines(led_buffer* Leds, led_buffer_range Range, assembly Assembl internal void Pattern_Rotary(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + gs_random_series Random = InitRandomSeries(24601); #define SphereCount 32 @@ -916,6 +940,9 @@ Pattern_AllOnMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r internal void Pattern_BulbMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + r32 Top = 141; r32 BulbRange = 50; From e5be1298d1a589d8ee2be51fcf0fa7a05ecdf343 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Wed, 31 Mar 2021 02:39:16 -0700 Subject: [PATCH 038/151] cleaned up some pattern stuff, and logged restarts --- src/app/foldhaus_app.h | 1 + src/app/patterns/blumen_patterns.h | 477 ---------------------- src/app/platform_win32/win32_foldhaus.cpp | 1 + src/app/ss_blumen_lumen/blumen_lumen.cpp | 152 ++++--- src/app/ss_blumen_lumen/gfx_math.h | 446 ++++++++++++++++++++ 5 files changed, 552 insertions(+), 525 deletions(-) create mode 100644 src/app/ss_blumen_lumen/gfx_math.h diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index 565b797..4123248 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -95,6 +95,7 @@ LoadAssembly(gs_const_string Path, app_state* State, context Context) #include "engine/user_space.cpp" +#include "ss_blumen_lumen/gfx_math.h" #include "ss_blumen_lumen/sdf.h" #include "patterns/blumen_patterns.h" #include "ss_blumen_lumen/blumen_lumen.cpp" diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 9e155c5..9a6cb28 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -7,318 +7,6 @@ #define FLOWER_COLORS_COUNT 12 -internal r32 -Smoothstep(r32 T) -{ - r32 Result = (T * T * (3 - (2 * T))); - return Result; -} -internal r32 -Smoothstep(r32 T, r32 A, r32 B) -{ - return LerpR32(Smoothstep(T), A, B); -} -internal v3 -Smoothstep(v3 P) -{ - v3 R = {}; - R.x = Smoothstep(P.x); - R.y = Smoothstep(P.y); - R.z = Smoothstep(P.z); - return R; -} - -internal v3 -AbsV3(v3 P) -{ - v3 Result = {}; - Result.x = Abs(P.x); - Result.y = Abs(P.y); - Result.z = Abs(P.z); - return Result; -} - -internal v2 -FloorV2(v2 P) -{ - v2 Result = {}; - Result.x = FloorR32(P.x); - Result.y = FloorR32(P.y); - return Result; -} -internal v3 -FloorV3(v3 P) -{ - v3 Result = {}; - Result.x = FloorR32(P.x); - Result.y = FloorR32(P.y); - Result.z = FloorR32(P.z); - return Result; -} - -internal v2 -FractV2(v2 P) -{ - v2 Result = {}; - Result.x = FractR32(P.x); - Result.y = FractR32(P.y); - return Result; -} -internal v3 -FractV3(v3 P) -{ - v3 Result = {}; - Result.x = FractR32(P.x); - Result.y = FractR32(P.y); - Result.z = FractR32(P.z); - return Result; -} - -internal v2 -SinV2(v2 P) -{ - v2 Result = {}; - Result.x = SinR32(P.x); - Result.y = SinR32(P.y); - return Result; -} -internal v3 -SinV3(v3 P) -{ - v3 Result = {}; - Result.x = SinR32(P.x); - Result.y = SinR32(P.y); - Result.y = SinR32(P.z); - return Result; -} - -internal r32 -Hash1(v2 P) -{ - v2 Result = FractV2( P * 0.3183099f ) * 50.f; - return FractR32(P.x * P.y * (P.x + P.y)); -} - -internal r32 -Hash1(r32 N) -{ - return FractR32(N * 17.0f * FractR32(N * 0.3183099f)); -} - -internal v2 -Hash2(r32 N) -{ - v2 P = V2MultiplyPairwise(SinV2(v2{N,N+1.0f}), v2{43758.5453123f,22578.1459123f}); - return FractV2(P); -} - -internal v2 -Hash2(v2 P) -{ - v2 K = v2{ 0.3183099f, 0.3678794f }; - v2 Kp = v2{K.y, K.x}; - v2 R = V2MultiplyPairwise(P, K) + Kp; - return FractV2( K * 16.0f * FractR32( P.x * P.y * (P.x + P.y))); -} - -internal v3 -Hash3(v2 P) -{ - v3 Q = v3{}; - Q.x = V2Dot(P, v2{127.1f, 311.7f}); - Q.y = V2Dot(P, v2{267.5f, 183.3f}); - Q.z = V2Dot(P, v2{419.2f, 371.9f}); - return FractV3(SinV3(Q) * 43758.5453f); -} - -internal r32 -HashV3ToR32(v3 P) -{ - v3 Pp = FractV3(P * 0.3183099f + v3{0.1f, 0.1f, 0.1f}); - Pp *= 17.0f; - r32 Result = FractR32(Pp.x * Pp.y * Pp.z * (Pp.x + Pp.y + Pp.z)); - return Result; -} - -internal r32 -Random(v2 N) -{ - v2 V = v2{12.9898f, 4.1414f}; - return FractR32(SinR32(V2Dot(N, V)) * 43758.5453); -} - -internal r32 -Noise2D(v2 P) -{ - v2 IP = FloorV2(P); - v2 U = FractV2(P); - U = V2MultiplyPairwise(U, U); - U = V2MultiplyPairwise(U, ((U * 2.0f) + v2{-3, -3})); - - r32 A = LerpR32(U.x, Random(IP), Random(IP + v2{1.0f, 0})); - r32 B = LerpR32(U.x, Random(IP + v2{0, 1}), Random(IP + v2{1, 1})); - r32 Res = LerpR32(U.y, A, B); - - return Res * Res; -} - -internal r32 -Noise3D(v3 P) -{ - P = AbsV3(P); - v3 PFloor = FloorV3(P); - v3 PFract = FractV3(P); - v3 F = Smoothstep(PFract); - - r32 Result = LerpR32(F.z, - LerpR32(F.y, - LerpR32(F.x, - HashV3ToR32(PFloor + v3{0, 0, 0}), - HashV3ToR32(PFloor + v3{1, 0, 0})), - LerpR32(F.x, - HashV3ToR32(PFloor + v3{0, 1, 0}), - HashV3ToR32(PFloor + v3{1, 1, 0}))), - LerpR32(F.y, - LerpR32(F.x, - HashV3ToR32(PFloor + v3{0, 0, 1}), - HashV3ToR32(PFloor + v3{1, 0, 1})), - LerpR32(F.x, - HashV3ToR32(PFloor + v3{0, 1, 1}), - HashV3ToR32(PFloor + v3{1, 1, 1})))); - - Assert(Result >= 0 && Result <= 1); - return Result; -} - -internal r32 -Noise3D_(v3 Pp) -{ - v3 P = FloorV3(Pp); - v3 W = FractV3(Pp); - - //v3 U = W * W * W * (W * (W * 6.0f - 15.0f) + 10.0f); - v3 U = V3MultiplyPairwise(W, W * 6.0f - v3{15, 15, 15}); - U = U + v3{10, 10, 10}; - U = V3MultiplyPairwise(U, W); - U = V3MultiplyPairwise(U, W); - U = V3MultiplyPairwise(U, W); - - r32 N = P.x + 317.0f * P.y + 157.0f * P.z; - - r32 A = Hash1(N + 0.0f); - r32 B = Hash1(N + 1.0f); - r32 C = Hash1(N + 317.0f); - r32 D = Hash1(N + 317.0f); - r32 E = Hash1(N + 157.0f); - r32 F = Hash1(N + 158.0f); - r32 G = Hash1(N + 474.0f); - r32 H = Hash1(N + 475.0f); - - r32 K0 = A; - r32 K1 = B - A; - r32 K2 = C - A; - r32 K3 = E - A; - r32 K4 = A - B - C + D; - r32 K5 = A - C - E + G; - r32 K6 = A - B - E + F; - r32 K7 = A + B + C - D + E - F - G + H; - - return -1.0f + 2.0f * (K0 + - K1 * U.x + - K2 * U.y + - K3 * U.z + - K4 * U.x * U.y + - K5 * U.y + U.z + - K6 * U.z * U.x + - K7 * U.x * U.y * U.z); -} - -internal r32 -Fbm2D(v2 P) -{ - r32 R = 0; - r32 Amp = 1.0; - r32 Freq = 1.0; - for (u32 i = 0; i < 3; i++) - { - R += Amp * Noise2D(P * Freq); - Amp *= 0.5f; - Freq *= 1.0f / 0.5f; - } - return R; -} - -global m44 M3 = m44{ - 0.00f, 0.80f, 0.60f, 0, - -0.80f, 0.36f, -0.48f, 0, - -0.60f, -0.48f, 0.64f, 0, - 0, 0, 0, 1 -}; - -internal r32 -Fbm3D(v3 P) -{ - v3 X = P; - r32 F = 2.0f; - r32 S = 0.5f; - r32 A = 0.0f; - r32 B = 0.5f; - for (u32 i = 0; i < 4; i++) - { - r32 N = Noise3D(X); - A += B * N; - B *= S; - v4 Xp = M3 * ToV4Point(X); - X = Xp.xyz * F; - } - - return A; -} - -internal r32 -Fbm3D(v3 P, r32 T) -{ - v3 Tt = v3{T, T, T}; - r32 SinT = SinR32(T); - v3 Tv = v3{SinT, SinT, SinT}; - v3 Pp = P; - r32 F = 0.0; - - F += 0.500000f * Noise3D(Pp + Tt); Pp = Pp * 2.02; - F += 0.031250f * Noise3D(Pp); Pp = Pp * 2.01; - F += 0.250000f * Noise3D(Pp - Tt); Pp = Pp * 2.03; - F += 0.125000f * Noise3D(Pp); Pp = Pp * 2.01; - F += 0.062500f * Noise3D(Pp + Tt); Pp = Pp * 2.04; - F += 0.015625f * Noise3D(Pp + Tv); - - F = F / 0.984375f; - return F; -} - -internal r32 -Voronoise(v2 P, r32 U, r32 V) -{ - r32 K = 1.0f + 63.0f + PowR32(1.0f - V, 6.0f); - - v2 I = FloorV2(P); - v2 F = FractV2(P); - - v2 A = v2{0, 0}; - for (s32 y = -2; y <= 2; y++) - { - for (s32 x = -2; x <= 2; x++) - { - v2 G = v2{(r32)x, (r32)y}; - v3 O = V3MultiplyPairwise(Hash3(I + G), v3{U, U, 1.0f}); - v2 D = G - F + O.xy; - r32 W = PowR32(1.0f - Smoothstep(V2Mag(D), 0.0f, 1.414f), K); - A += v2{O.z * W, W}; - } - } - - return A.x / A.y; -} - internal pixel V4ToRGBPixel(v4 C) { @@ -329,171 +17,6 @@ V4ToRGBPixel(v4 C) return Result; } -internal pixel -PixelMix(r32 T, pixel A, pixel B) -{ - pixel Result = { - LerpU8(T, A.R, B.R), - LerpU8(T, A.G, B.G), - LerpU8(T, A.B, B.B), - }; - return Result; -} - -internal pixel -PixelMix(r32 T, v4 A, v4 B) -{ - v4 Result = V4Lerp(T, A, B); - pixel P = V4ToRGBPixel(Result); - return P; -} - -internal v4 -GetColor(v4* Colors, u32 ColorsCount, r32 Percent) -{ - Percent = Clamp01(Percent); - - u32 LowerIndex = Percent * ColorsCount; - - u32 HigherIndex = LowerIndex + 1; - if (HigherIndex >= ColorsCount) HigherIndex = 0; - - r32 LowerPercent = (r32)LowerIndex / (r32)ColorsCount; - r32 StepPercent = 1.f / (r32)ColorsCount; - r32 PercentLower = (Percent - LowerPercent) / StepPercent; - - v4 Result = V4Lerp(PercentLower, Colors[LowerIndex], Colors[HigherIndex]); - - return Result; -} - -v4 RGBToHSV(v4 In) -{ - v4 Result = {}; - - r32 Min = Min(In.r, Min(In.g, In.b)); - r32 Max = Max(In.r, Max(In.g, In.b)); - - r32 V = Max; - r32 Delta = Max - Min; - r32 S = 0; - r32 H = 0; - if( Max != 0 ) - { - S = Delta / Max; - - if( In.r == Max ) - { - H = ( In.g - In.b ) / Delta; // between yellow & magenta - } - else if( In.g == Max ) - { - H = 2 + ( In.b - In.r ) / Delta; // between cyan & yellow - } - else - { - H = 4 + ( In.r - In.g ) / Delta; // between magenta & cyan - } - H *= 60; // degrees - if( H < 0 ) - H += 360; - Result = v4{H, S, V, 1}; - } - else - { - // r = g = b = 0 - // s = 0, v is undefined - S = 0; - H = -1; - Result = v4{H, S, 1, 1}; - } - - return Result; -} - -v4 HSVToRGB (v4 In) -{ - float Hue = In.x; - /* -while (Hue > 360.0f) { Hue -= 360.0f; } - while (Hue < 0.0f) { Hue += 360.0f; } - */ - Hue = ModR32(Hue, 360.0f); - if (Hue < 0) { Hue += 360.0f; } - if (Hue == MinR32) { Hue = 0; } - if (Hue == MaxR32) { Hue = 360; } - Assert(Hue >= 0 && Hue < 360); - - float Sat = In.y; - float Value = In.z; - - float hh, p, q, t, ff; - long i; - v4 Result = {}; - Result.a = In.a; - - if(Sat <= 0.0f) { // < is bogus, just shuts up warnings - Result.r = Value; - Result.g = Value; - Result.b = Value; - return Result; - } - hh = Hue; - if(hh >= 360.0f) hh = 0.0f; - hh /= 60.0f; - i = (long)hh; - ff = hh - i; - p = Value * (1.0f - Sat); - q = Value * (1.0f - (Sat * ff)); - t = Value * (1.0f - (Sat * (1.0f - ff))); - - switch(i) { - case 0: - {Result.r = Value; - Result.g = t; - Result.b = p; - }break; - - case 1: - { - Result.r = q; - Result.g = Value; - Result.b = p; - }break; - - case 2: - { - Result.r = p; - Result.g = Value; - Result.b = t; - }break; - - case 3: - { - Result.r = p; - Result.g = q; - Result.b = Value; - }break; - - case 4: - { - Result.r = t; - Result.g = p; - Result.b = Value; - }break; - - case 5: - default: - { - Result.r = Value; - Result.g = p; - Result.b = q; - }break; - } - - return Result; -} - internal void Pattern_AltBloomMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index d284503..19653ac 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -596,6 +596,7 @@ WinMain ( context Context = {}; Context.ThreadContext = ThreadContext; + Context.SystemTime_Current = Win32GetSystemTime(); gs_const_string Args = ConstString((char*)CmdLineArgs); if (StringsEqual(Args, ConstString("-headless"))) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index ee6b42c..8715e49 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -209,6 +209,109 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); } +internal void +AppendPrintDate(gs_string* WriteStr, system_time Time) +{ + AppendPrintF(WriteStr, "%d-%d-%d : %d:%d:%d\n\n", + Time.Year, Time.Month, Time.Day, + Time.Hour, Time.Minute, Time.Second); +} + +internal void +BlumenLumen_AppendBootupLog(app_state* State, blumen_lumen_state* BLState, context Context) +{ + gs_thread_context Ctx = Context.ThreadContext; + gs_const_string BootupLogPath = ConstString("lumenarium_boot_log.log"); + + gs_file BootLog = ReadEntireFile(Ctx.FileHandler, BootupLogPath); + gs_string WriteStr = {}; + + // we don't want the log file getting huge + // if it gets above some threshold, instead of appending, + // copy what there is to an _old file, and start this one over + // + // The thinking is that without the copy operation, when we reached + // our threshold, we'd overwrite the whole log. If something went + // wrong at that point, we'd have nothing to go on. This way, there is + // always some historical data present on the system + // + if (BootLog.Size < MB(4)) + { + WriteStr = PushString(State->Transient, BootLog.Size + 1024); + } + else + { + if (!WriteEntireFile(Ctx.FileHandler, ConstString("lumenarium_boot_log_old.log"), + BootLog.Data)) + { + InvalidCodePath; + } + WriteStr = PushString(State->Transient, 1024); + } + + + // Copy old entries in + if (BootLog.Size > 0) + { + PrintF(&WriteStr, "%.*s", BootLog.Size, BootLog.Memory); + } + + // New Entry + AppendPrintF(&WriteStr, "Lumenarium Restarted\n"); + AppendPrintF(&WriteStr, "* Time: "); + AppendPrintDate(&WriteStr, Context.SystemTime_Current); + + gs_data Data = StringToData(WriteStr); + WriteEntireFile(Ctx.FileHandler, BootupLogPath, Data); +} + +internal void +BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Context) +{ + if (!BLState->ShouldUpdateLog) return; + + gs_string FileStr = PushString(State->Transient, 1024); + AppendPrintF(&FileStr, "Lumenarium Status\n"); + + AppendPrintF(&FileStr, "Last Updated At:"); + AppendPrintDate(&FileStr, Context.SystemTime_Current); + AppendPrintF(&FileStr, "\n\n"); + + animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); + + char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected"; + AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected); + + u8 MP0 = BLState->LastKnownMotorState.FlowerPositions[0]; + u8 MP1 = BLState->LastKnownMotorState.FlowerPositions[1]; + u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2]; + AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2); + + char* PatternMode = 0; + switch (BLState->PatternMode) + { + case BlumenPattern_Standard: { PatternMode = "Standard"; } break; + case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break; + } + AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode); + + phrase_hue LastHuePhrase = BLState->LastHuePhrase; + AppendPrintF(&FileStr, "Last Mic Phrase: %S\n", LastHuePhrase.Phrase); + + AppendPrintF(&FileStr, "Pattern Speed: %f\n", BLState->PatternSpeed); + + AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent); + + AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived); + + gs_data LogMem = StringToData(FileStr); + if (!WriteEntireFile(Context.ThreadContext.FileHandler, ConstString("lumenarium_status.log"), LogMem)) + { + InvalidCodePath; + } +} + internal gs_data BlumenLumen_CustomInit(app_state* State, context Context) { @@ -274,57 +377,10 @@ BlumenLumen_CustomInit(app_state* State, context Context) #endif State->AnimationSystem.TimelineShouldAdvance = true; + BlumenLumen_AppendBootupLog(State, BLState, Context); return Result; } -internal void -BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Context) -{ - if (!BLState->ShouldUpdateLog) return; - - gs_string FileStr = PushString(State->Transient, 1024); - AppendPrintF(&FileStr, "Lumenarium Status\n"); - - system_time Time = Context.SystemTime_Current; - AppendPrintF(&FileStr, "Last Updated At: %d-%d-%d : %d:%d:%d\n\n", - Time.Year, Time.Month, Time.Day, - Time.Hour, Time.Minute, Time.Second); - - animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); - - char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected"; - AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected); - - u8 MP0 = BLState->LastKnownMotorState.FlowerPositions[0]; - u8 MP1 = BLState->LastKnownMotorState.FlowerPositions[1]; - u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2]; - AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2); - - char* PatternMode = 0; - switch (BLState->PatternMode) - { - case BlumenPattern_Standard: { PatternMode = "Standard"; } break; - case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break; - } - AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode); - - phrase_hue LastHuePhrase = BLState->LastHuePhrase; - AppendPrintF(&FileStr, "Last Mic Phrase: %S\n", LastHuePhrase.Phrase); - - AppendPrintF(&FileStr, "Pattern Speed: %f\n", BLState->PatternSpeed); - - AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent); - - AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived); - - gs_data LogMem = StringToData(FileStr); - if (!WriteEntireFile(Context.ThreadContext.FileHandler, ConstString("lumenarium_status.log"), LogMem)) - { - InvalidCodePath; - } -} - internal void BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { diff --git a/src/app/ss_blumen_lumen/gfx_math.h b/src/app/ss_blumen_lumen/gfx_math.h new file mode 100644 index 0000000..b4409e7 --- /dev/null +++ b/src/app/ss_blumen_lumen/gfx_math.h @@ -0,0 +1,446 @@ +/* date = March 31st 2021 2:19 am */ + +#ifndef GFX_MATH_H +#define GFX_MATH_H + +internal r32 +Smoothstep(r32 T) +{ + r32 Result = (T * T * (3 - (2 * T))); + return Result; +} +internal r32 +Smoothstep(r32 T, r32 A, r32 B) +{ + return LerpR32(Smoothstep(T), A, B); +} +internal v3 +Smoothstep(v3 P) +{ + v3 R = {}; + R.x = Smoothstep(P.x); + R.y = Smoothstep(P.y); + R.z = Smoothstep(P.z); + return R; +} + +internal v3 +AbsV3(v3 P) +{ + v3 Result = {}; + Result.x = Abs(P.x); + Result.y = Abs(P.y); + Result.z = Abs(P.z); + return Result; +} + +internal v2 +FloorV2(v2 P) +{ + v2 Result = {}; + Result.x = FloorR32(P.x); + Result.y = FloorR32(P.y); + return Result; +} +internal v3 +FloorV3(v3 P) +{ + v3 Result = {}; + Result.x = FloorR32(P.x); + Result.y = FloorR32(P.y); + Result.z = FloorR32(P.z); + return Result; +} + +internal v2 +FractV2(v2 P) +{ + v2 Result = {}; + Result.x = FractR32(P.x); + Result.y = FractR32(P.y); + return Result; +} +internal v3 +FractV3(v3 P) +{ + v3 Result = {}; + Result.x = FractR32(P.x); + Result.y = FractR32(P.y); + Result.z = FractR32(P.z); + return Result; +} + +internal v2 +SinV2(v2 P) +{ + v2 Result = {}; + Result.x = SinR32(P.x); + Result.y = SinR32(P.y); + return Result; +} +internal v3 +SinV3(v3 P) +{ + v3 Result = {}; + Result.x = SinR32(P.x); + Result.y = SinR32(P.y); + Result.y = SinR32(P.z); + return Result; +} + +internal r32 +Hash1(v2 P) +{ + v2 Result = FractV2( P * 0.3183099f ) * 50.f; + return FractR32(P.x * P.y * (P.x + P.y)); +} + +internal r32 +Hash1(r32 N) +{ + return FractR32(N * 17.0f * FractR32(N * 0.3183099f)); +} + +internal v2 +Hash2(r32 N) +{ + v2 P = V2MultiplyPairwise(SinV2(v2{N,N+1.0f}), v2{43758.5453123f,22578.1459123f}); + return FractV2(P); +} + +internal v2 +Hash2(v2 P) +{ + v2 K = v2{ 0.3183099f, 0.3678794f }; + v2 Kp = v2{K.y, K.x}; + v2 R = V2MultiplyPairwise(P, K) + Kp; + return FractV2( K * 16.0f * FractR32( P.x * P.y * (P.x + P.y))); +} + +internal v3 +Hash3(v2 P) +{ + v3 Q = v3{}; + Q.x = V2Dot(P, v2{127.1f, 311.7f}); + Q.y = V2Dot(P, v2{267.5f, 183.3f}); + Q.z = V2Dot(P, v2{419.2f, 371.9f}); + return FractV3(SinV3(Q) * 43758.5453f); +} + +internal r32 +HashV3ToR32(v3 P) +{ + v3 Pp = FractV3(P * 0.3183099f + v3{0.1f, 0.1f, 0.1f}); + Pp *= 17.0f; + r32 Result = FractR32(Pp.x * Pp.y * Pp.z * (Pp.x + Pp.y + Pp.z)); + return Result; +} + +internal r32 +Random(v2 N) +{ + v2 V = v2{12.9898f, 4.1414f}; + return FractR32(SinR32(V2Dot(N, V)) * 43758.5453); +} + +internal r32 +Noise2D(v2 P) +{ + v2 IP = FloorV2(P); + v2 U = FractV2(P); + U = V2MultiplyPairwise(U, U); + U = V2MultiplyPairwise(U, ((U * 2.0f) + v2{-3, -3})); + + r32 A = LerpR32(U.x, Random(IP), Random(IP + v2{1.0f, 0})); + r32 B = LerpR32(U.x, Random(IP + v2{0, 1}), Random(IP + v2{1, 1})); + r32 Res = LerpR32(U.y, A, B); + + return Res * Res; +} + +internal r32 +Noise3D(v3 P) +{ + P = AbsV3(P); + v3 PFloor = FloorV3(P); + v3 PFract = FractV3(P); + v3 F = Smoothstep(PFract); + + r32 Result = LerpR32(F.z, + LerpR32(F.y, + LerpR32(F.x, + HashV3ToR32(PFloor + v3{0, 0, 0}), + HashV3ToR32(PFloor + v3{1, 0, 0})), + LerpR32(F.x, + HashV3ToR32(PFloor + v3{0, 1, 0}), + HashV3ToR32(PFloor + v3{1, 1, 0}))), + LerpR32(F.y, + LerpR32(F.x, + HashV3ToR32(PFloor + v3{0, 0, 1}), + HashV3ToR32(PFloor + v3{1, 0, 1})), + LerpR32(F.x, + HashV3ToR32(PFloor + v3{0, 1, 1}), + HashV3ToR32(PFloor + v3{1, 1, 1})))); + + Assert(Result >= 0 && Result <= 1); + return Result; +} + +internal r32 +Noise3D_(v3 Pp) +{ + v3 P = FloorV3(Pp); + v3 W = FractV3(Pp); + + //v3 U = W * W * W * (W * (W * 6.0f - 15.0f) + 10.0f); + v3 U = V3MultiplyPairwise(W, W * 6.0f - v3{15, 15, 15}); + U = U + v3{10, 10, 10}; + U = V3MultiplyPairwise(U, W); + U = V3MultiplyPairwise(U, W); + U = V3MultiplyPairwise(U, W); + + r32 N = P.x + 317.0f * P.y + 157.0f * P.z; + + r32 A = Hash1(N + 0.0f); + r32 B = Hash1(N + 1.0f); + r32 C = Hash1(N + 317.0f); + r32 D = Hash1(N + 317.0f); + r32 E = Hash1(N + 157.0f); + r32 F = Hash1(N + 158.0f); + r32 G = Hash1(N + 474.0f); + r32 H = Hash1(N + 475.0f); + + r32 K0 = A; + r32 K1 = B - A; + r32 K2 = C - A; + r32 K3 = E - A; + r32 K4 = A - B - C + D; + r32 K5 = A - C - E + G; + r32 K6 = A - B - E + F; + r32 K7 = A + B + C - D + E - F - G + H; + + return -1.0f + 2.0f * (K0 + + K1 * U.x + + K2 * U.y + + K3 * U.z + + K4 * U.x * U.y + + K5 * U.y + U.z + + K6 * U.z * U.x + + K7 * U.x * U.y * U.z); +} + +internal r32 +Fbm2D(v2 P) +{ + r32 R = 0; + r32 Amp = 1.0; + r32 Freq = 1.0; + for (u32 i = 0; i < 3; i++) + { + R += Amp * Noise2D(P * Freq); + Amp *= 0.5f; + Freq *= 1.0f / 0.5f; + } + return R; +} + +global m44 M3 = m44{ + 0.00f, 0.80f, 0.60f, 0, + -0.80f, 0.36f, -0.48f, 0, + -0.60f, -0.48f, 0.64f, 0, + 0, 0, 0, 1 +}; + +internal r32 +Fbm3D(v3 P) +{ + v3 X = P; + r32 F = 2.0f; + r32 S = 0.5f; + r32 A = 0.0f; + r32 B = 0.5f; + for (u32 i = 0; i < 4; i++) + { + r32 N = Noise3D(X); + A += B * N; + B *= S; + v4 Xp = M3 * ToV4Point(X); + X = Xp.xyz * F; + } + + return A; +} + +internal r32 +Fbm3D(v3 P, r32 T) +{ + v3 Tt = v3{T, T, T}; + r32 SinT = SinR32(T); + v3 Tv = v3{SinT, SinT, SinT}; + v3 Pp = P; + r32 F = 0.0; + + F += 0.500000f * Noise3D(Pp + Tt); Pp = Pp * 2.02; + F += 0.031250f * Noise3D(Pp); Pp = Pp * 2.01; + F += 0.250000f * Noise3D(Pp - Tt); Pp = Pp * 2.03; + F += 0.125000f * Noise3D(Pp); Pp = Pp * 2.01; + F += 0.062500f * Noise3D(Pp + Tt); Pp = Pp * 2.04; + F += 0.015625f * Noise3D(Pp + Tv); + + F = F / 0.984375f; + return F; +} + +internal r32 +Voronoise(v2 P, r32 U, r32 V) +{ + r32 K = 1.0f + 63.0f + PowR32(1.0f - V, 6.0f); + + v2 I = FloorV2(P); + v2 F = FractV2(P); + + v2 A = v2{0, 0}; + for (s32 y = -2; y <= 2; y++) + { + for (s32 x = -2; x <= 2; x++) + { + v2 G = v2{(r32)x, (r32)y}; + v3 O = V3MultiplyPairwise(Hash3(I + G), v3{U, U, 1.0f}); + v2 D = G - F + O.xy; + r32 W = PowR32(1.0f - Smoothstep(V2Mag(D), 0.0f, 1.414f), K); + A += v2{O.z * W, W}; + } + } + + return A.x / A.y; +} + + +v4 RGBToHSV(v4 In) +{ + v4 Result = {}; + + r32 Min = Min(In.r, Min(In.g, In.b)); + r32 Max = Max(In.r, Max(In.g, In.b)); + + r32 V = Max; + r32 Delta = Max - Min; + r32 S = 0; + r32 H = 0; + if( Max != 0 ) + { + S = Delta / Max; + + if( In.r == Max ) + { + H = ( In.g - In.b ) / Delta; // between yellow & magenta + } + else if( In.g == Max ) + { + H = 2 + ( In.b - In.r ) / Delta; // between cyan & yellow + } + else + { + H = 4 + ( In.r - In.g ) / Delta; // between magenta & cyan + } + H *= 60; // degrees + if( H < 0 ) + H += 360; + Result = v4{H, S, V, 1}; + } + else + { + // r = g = b = 0 + // s = 0, v is undefined + S = 0; + H = -1; + Result = v4{H, S, 1, 1}; + } + + return Result; +} + +v4 HSVToRGB (v4 In) +{ + float Hue = In.x; + /* +while (Hue > 360.0f) { Hue -= 360.0f; } + while (Hue < 0.0f) { Hue += 360.0f; } + */ + Hue = ModR32(Hue, 360.0f); + if (Hue < 0) { Hue += 360.0f; } + if (Hue == MinR32) { Hue = 0; } + if (Hue == MaxR32) { Hue = 360; } + Assert(Hue >= 0 && Hue < 360); + + float Sat = In.y; + float Value = In.z; + + float hh, p, q, t, ff; + long i; + v4 Result = {}; + Result.a = In.a; + + if(Sat <= 0.0f) { // < is bogus, just shuts up warnings + Result.r = Value; + Result.g = Value; + Result.b = Value; + return Result; + } + hh = Hue; + if(hh >= 360.0f) hh = 0.0f; + hh /= 60.0f; + i = (long)hh; + ff = hh - i; + p = Value * (1.0f - Sat); + q = Value * (1.0f - (Sat * ff)); + t = Value * (1.0f - (Sat * (1.0f - ff))); + + switch(i) { + case 0: + {Result.r = Value; + Result.g = t; + Result.b = p; + }break; + + case 1: + { + Result.r = q; + Result.g = Value; + Result.b = p; + }break; + + case 2: + { + Result.r = p; + Result.g = Value; + Result.b = t; + }break; + + case 3: + { + Result.r = p; + Result.g = q; + Result.b = Value; + }break; + + case 4: + { + Result.r = t; + Result.g = p; + Result.b = Value; + }break; + + case 5: + default: + { + Result.r = Value; + Result.g = p; + Result.b = q; + }break; + } + + return Result; +} + +#endif //GFX_MATH_H From 5ba251012d31abc42ec30283e55313d79b8225e8 Mon Sep 17 00:00:00 2001 From: PS Date: Wed, 7 Apr 2021 10:47:10 -0700 Subject: [PATCH 039/151] Patterns and phrase map priority --- .../audio_responses/voice_command.foldanim | 12 +- src/app/foldhaus_platform.h | 8 + src/app/patterns/blumen_patterns.h | 141 ++++++++++++++++++ src/app/ss_blumen_lumen/blumen_lumen.cpp | 57 ++++--- src/app/ss_blumen_lumen/blumen_lumen.h | 6 + 5 files changed, 201 insertions(+), 23 deletions(-) diff --git a/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim index 91c364f..b5b409a 100644 --- a/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim +++ b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim @@ -1,24 +1,24 @@ lumenarium_animation_file; -animation_name: "voice_command"; +animation_name: "voice_anim"; layers_count: 1; blocks_count: 1; playable_range:{ min: 0; - max: 360; + max: 3600; }; layers:{ layer:{ - name: "Colors"; + name: "[New Layer]"; blend: "Add"; }; }; blocks:{ block:{ frame_range:{ - min: 0; - max: 360; + min: 1; + max: 3599; }; layer_index: 0; - animation_name: "Pattern_Leafy"; + animation_name: "Pattern_VoicePattern"; }; }; diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index d2d190b..9e63d61 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -202,6 +202,14 @@ typedef struct system_time s32 Second; } system_time; +internal r64 +SecondsElapsed(system_time Start, system_time End) +{ + u64 N = End.NanosSinceEpoch - Start.NanosSinceEpoch; + r64 S = (r64)N * NanosToSeconds; + return S; +} + struct context { gs_thread_context ThreadContext; diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 9a6cb28..7362394 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -10,6 +10,10 @@ internal pixel V4ToRGBPixel(v4 C) { + C.x = Clamp01(C.x); + C.y = Clamp01(C.y); + C.z = Clamp01(C.z); + pixel Result = {}; Result.R = (u8)(C.x * 255); Result.G = (u8)(C.y * 255); @@ -488,5 +492,142 @@ Pattern_BulbMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r3 } } +internal v4 +GenPatchyColor(v3 P, r32 Time, v4 C0, v4 C1, v4 C2) +{ + r32 LedRange = 300.0f; + r32 ScaleFactor = 1.0f / LedRange; + v3 Pp = P + v3{150, 100, 0}; + + r32 ScaleA = 1; + r32 NoiseA = Noise3D(((Pp / 38) + v3{0, 0, Time}) * ScaleA); + NoiseA = PowR32(NoiseA, 3); + NoiseA = Smoothstep(NoiseA); + + r32 ScaleBP = 2; + r32 ScaleB = 15; + r32 NoiseBP = Noise3D(((Pp / 13) + v3{ 0, Time * -0.33f, 0}) * ScaleBP); + NoiseBP = PowR32(NoiseBP, 3); + r32 NoiseB = Noise3D(((Pp / 75) + v3{Time * 0.5f, 0, 0}) * ScaleB); + NoiseB = PowR32(NoiseB, 3); + NoiseB = Smoothstep(NoiseB) * NoiseBP; + + r32 ScaleC = 1.5; + r32 NoiseCP = Noise3D(((Pp / 132) + v3{Time * -0.33f, 0, 0}) * 0.5f); + r32 NoiseC = Noise3D(((Pp / 164) + v3{Time * 0.25f, 0, 0}) * ScaleC); + NoiseC = PowR32(NoiseC, 3); + NoiseC = Smoothstep(NoiseC) * NoiseCP; + + v4 C = (C0 * NoiseA) + (C1 * NoiseB) + (C2 * NoiseC); + C /= (NoiseA + NoiseB + NoiseC); + return C; +} + +internal r32 +GenVerticalStrips(v3 P, r32 Time) +{ + v2 Right = v2{1, 0}; + v2 Pa = V2Normalize(v2{P.x, P.z}); + r32 Angle = .5f + (.5f * V2Dot(Pa, Right)); + + r32 HOffset = 70.0f; + r32 O = 50.0f; + r32 C = 10.0f; + + r32 X = Angle; + r32 Y = P.y; + r32 I = FloorR32(Y / C) * C; + I += (X * HOffset) + (Time * 25); + + r32 V = FractR32(I / O); + V = 2.0f * (0.5f - Abs(V - 0.5f)); + Assert(V >= 0 && V <= 1); + + return V; +} + +internal v4 +GenVerticalLeaves(v3 P, r32 Time, v4 C0, v4 C1, v4 C2) +{ + r32 A = GenVerticalStrips(P, Time * .25f); + r32 B = GenVerticalStrips(P * .3f, Time); + r32 C = GenVerticalStrips(P * .25f, Time * 2); + + v4 R = (C0 * A) + (C1 * B) + (C2 * C); + R /= A + B + C; + return R; +} + +internal r32 +GenLiquidBands(v3 P, r32 Offset, r32 Time) +{ + r32 Width = 30; + r32 VAcc = 0; + for (u32 i = 1; i < 3; i++) + { + v3 P0 = v3{P.x + (23.124f * i), 0, P.z - (-12.34f * i)}; + + r32 Y = (P.y - Offset); + r32 S = Fbm3D(P0 * .005f, Time) * 250; + S += ModR32((Time * 100) - (150 * i), 400); + + r32 V = (Width - Abs(Y - S)) / Width; + V = Clamp01(V); + + VAcc += V; + } + + return VAcc; +} + +internal r32 +GenDotBands(v3 P, r32 Time) +{ + r32 RowHeight = 25; + r32 DotRadius = 20; + + r32 Y = P.y + 150; + s32 Row = (s32)FloorR32(Y / RowHeight); + r32 RowH = Abs(FractR32(Y / RowHeight)); + r32 DotDistY = Max(0, .5f - RowH) * 2; + + r32 Angle = (V2Dot(V2Normalize(v2{P.x, P.z}), v2{1,0}) * .5f) + .5f; + r32 DotDistX = Abs(ModR32(Angle, .2f)); + + r32 DotDist = SqrtR32(PowR32(DotDistX, 2) + PowR32(RowH, 2)); + r32 B = (DotRadius - DotDist) / DotRadius; + B = Clamp01(DotDist); + + return DotDistY; + +} + +internal void +Pattern_VoicePattern(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); + v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); + v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); + v4 C2 = HSVToRGB({Hue.Hue2, 1, 1, 1}); + + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + v3 P = Leds->Positions[LedIndex].xyz; + + v4 C = {}; + C += GenPatchyColor(P, Time, C0, C2, {}); + //C = GenVerticalLeaves((P - Assembly.Center) + v3{0, 150, 0}, Time, C0, C1, C2); + r32 Bands = GenLiquidBands(P, -250, Time); + //C = V4Lerp(Bands, C * .5f, C1); + + //C = WhiteV4 * GenDotBands(P - Assembly.Center, Time); + + Leds->Colors[LedIndex] = V4ToRGBPixel(C); + } +} + #define BLUMEN_PATTERNS_H #endif // BLUMEN_PATTERNS_H \ No newline at end of file diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 8715e49..2c41df2 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -207,6 +207,8 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_Rotary, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED); + } internal void @@ -373,7 +375,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); - BlumenLumen_SetPatternMode(BlumenPattern_Standard, 5, &State->AnimationSystem, BLState); + BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, 5, &State->AnimationSystem, BLState); #endif State->AnimationSystem.TimelineShouldAdvance = true; @@ -390,6 +392,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) bool SendMotorCommand = false; blumen_packet MotorCommand = {}; + r64 PhraseGroupingTime = 1.0f; while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) { gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); @@ -403,26 +406,17 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) u32 NameLen = CStringLength(Mic.AnimationFileName); phrase_hue NewHue = PhraseHueMap_Get(BLState->PhraseHueMap, NameHash); - if (NewHue.PhraseHash != 0) + if (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length) { - if (BLState->PatternMode == BlumenPattern_Standard) + BLState->NextHotHue = NewHue; + if (SecondsElapsed(BLState->TimePhraseReceptionBegan, + Context->SystemTime_Current) > PhraseGroupingTime) { - BLState->AssemblyColors[0] = NewHue; - BLState->AssemblyColors[1] = NewHue; - BLState->AssemblyColors[2] = NewHue; + BLState->TimePhraseReceptionBegan = Context->SystemTime_Current; + BLState->InPhraseReceptionMode = true; } - else - { - u32 AssemblyIdx = BLState->LastAssemblyColorSet; - BLState->AssemblyColors[AssemblyIdx] = NewHue; - BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; - } - - BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, 5, &State->AnimationSystem, BLState); - BLState->TimeLastSetToVoiceMode = Context->SystemTime_Current; - BLState->LastHuePhrase = NewHue; + BLState->TimeLastPhraseReceived = Context->SystemTime_Current; } - BLState->ShouldUpdateLog = true; }break; case PacketType_MotorState: @@ -473,6 +467,35 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } + if (BLState->InPhraseReceptionMode) + { + r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current); + if (SecondsSincePhraseBegan > PhraseGroupingTime) + { + // if we are in standard color mode, shift all flowers to the new color + // otherwise, only shift the next flower in the sequence to the new color + phrase_hue NewHue = BLState->NextHotHue; + if (BLState->PatternMode == BlumenPattern_Standard) + { + BLState->AssemblyColors[0] = NewHue; + BLState->AssemblyColors[1] = NewHue; + BLState->AssemblyColors[2] = NewHue; + } + else + { + u32 AssemblyIdx = BLState->LastAssemblyColorSet; + BLState->AssemblyColors[AssemblyIdx] = NewHue; + BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; + } + + BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, 5, &State->AnimationSystem, BLState); + BLState->TimeLastSetToVoiceMode = Context->SystemTime_Current; + BLState->LastHuePhrase = NewHue; + BLState->ShouldUpdateLog = true; + BLState->InPhraseReceptionMode = false; + } + } + // Update next frames Hues r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); AnimTime = (r32)Context->TotalTime; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index cc271d7..0a81e8f 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -175,6 +175,12 @@ struct blumen_lumen_state animation_handle_array ModeAnimations[BlumenPattern_Count]; phrase_hue_map PhraseHueMap; + + bool InPhraseReceptionMode; + phrase_hue NextHotHue; + system_time TimePhraseReceptionBegan; + system_time TimeLastPhraseReceived; + system_time TimeLastSetToVoiceMode; phrase_hue LastHuePhrase; From 249082170b6ff1fe146e32fae415ddd4460b697f Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Wed, 7 Apr 2021 12:16:01 -0700 Subject: [PATCH 040/151] changed motor settings --- .../ss_blumen_lumen/blumen_lumen_settings.h | 70 ++++--------------- 1 file changed, 14 insertions(+), 56 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index ab38ed8..30aab2c 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -19,8 +19,8 @@ gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold"); gs_const_string PhraseMapCSVPath = ConstString("data/flower_codes.csv"); char PhraseMapCSVSeparator = ','; -// Search Strings for which folders to find ambient animation files and -// voice animation files in. +// Search Strings for which folders to find ambient animation files and +// voice animation files in. // these search patterns should always end in *.foldanim so they only // return valid animation files gs_const_string AmbientPatternFolder = ConstString("data/blumen_animations/ambient_patterns/*.foldanim"); @@ -30,57 +30,15 @@ gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_r // these are in the format { Start_Hour, Start_Minute, End_Hour, End_Minute } // Hours are in the range 0-23 inclusive // Minutes are in the range 0-59 inclusive -// NOTE: There is no need to modify the MotorOpenTimesCount variable - +// NOTE: There is no need to modify the MotorOpenTimesCount variable - // it is a compile time constant that gets calculated automatically global time_range MotorOpenTimes[] = { - { 00, 30, 00, 40 }, - { 00, 50, 01, 00 }, - { 01, 10, 01, 20 }, - { 01, 30, 01, 40 }, - { 01, 50, 02, 00 }, - { 02, 10, 02, 20 }, - { 02, 30, 02, 40 }, - { 02, 50, 03, 00 }, - { 03, 10, 03, 20 }, - { 03, 30, 03, 40 }, - { 03, 50, 04, 00 }, - { 04, 10, 04, 20 }, - { 04, 30, 04, 40 }, - { 04, 50, 05, 00 }, - { 05, 10, 05, 20 }, - { 05, 30, 05, 40 }, - { 05, 50, 06, 00 }, - { 06, 10, 06, 20 }, - { 06, 30, 06, 40 }, - { 06, 50, 07, 00 }, - { 07, 10, 07, 20 }, - { 07, 30, 07, 40 }, - { 07, 50, 8, 00 }, - { 8, 10, 8, 20 }, - { 8, 30, 8, 40 }, - { 8, 50, 9, 00 }, - { 9, 10, 9, 20 }, - { 9, 30, 9, 40 }, - { 9, 50, 10, 00 }, - { 10, 10, 10, 20 }, - { 10, 30, 10, 40 }, - { 10, 50, 11, 00 }, - { 11, 10, 11, 20 }, - { 11, 30, 11, 40 }, - { 11, 50, 12, 00 }, - { 12, 10, 12, 20 }, - { 12, 30, 12, 40 }, - { 12, 50, 13, 00 }, - { 13, 10, 13, 20 }, - { 13, 30, 13, 40 }, - { 13, 50, 14, 00 }, - { 14, 10, 14, 20 }, - { 14, 30, 14, 40 }, - { 14, 50, 15, 00 }, + { 08, 45, 17, 45 }, + { 19, 00, 23, 00 }, }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit -// How long it takes to fade from the default pattern to the +// How long it takes to fade from the default pattern to the // voice activated pattern r32 VoiceCommandFadeDuration = 1.0f; // in seconds @@ -90,14 +48,14 @@ r32 VoiceCommandFadeDuration = 1.0f; // in seconds // ie. // if this is set to 30 seconds, upon receiving a voice command // lumenarium will fade to the requested pattern/color palette -// and then wait 30 seconds before fading back to the original +// and then wait 30 seconds before fading back to the original // pattern. If, in that 30 second window, another voice command // is issued, lumenarium will reset the 30 second counter. r64 VoiceCommandSustainDuration = 30.0; // in seconds // When we send a Motor Close command, we don't want the upper leds to // immediately turn off. Instead, we want to wait until the flower is -// at least some of the way closed. This variable dictates how long +// at least some of the way closed. This variable dictates how long // we wait for. // For example: // 1. We send a 'motor close' command to the clear core @@ -105,7 +63,7 @@ r64 VoiceCommandSustainDuration = 30.0; // in seconds // 3. We begin a timer // 4. When the timer reaches the value set in this variable, // we turn the upper leds off. -// +// // NOTE: This is not a symmetric operation. When we send a 'motor open' // command, we want to immediately turn the upper leds on so they appear // to have been on the whole time. @@ -118,7 +76,7 @@ r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 5.0; // in seconds // HighTemperatureBrightnessPercent (below) // // NOTE: this is an 8bit signed integer so its range is -// -128 to 127, not that we should need either of those extremes for +// -128 to 127, not that we should need either of those extremes for // this, but just a note that you can't just put anything in here. s8 MinHighTemperature = 0; @@ -131,18 +89,18 @@ r32 HighTemperatureBrightnessPercent = .25f; // The percent brightness we set leds to when no other conditions apply // A value in the range 0:1 inclusive. -// Probably wants to be something high like 1 but we might want to +// Probably wants to be something high like 1 but we might want to // lower it for heat reasons? r32 FullBrightnessPercent = 1.0f; // A global modifier so Joerg can just slow all the patterns right down // XD -// This is a percent - so 1 is full speed, 0.1f is 1/10 full speed and -// 2 is 2x as fast. +// This is a percent - so 1 is full speed, 0.1f is 1/10 full speed and +// 2 is 2x as fast. r32 GlobalAnimSpeed = 1.0f; // How often should Lumenarium send its status to the python server? -// +// #define STATUS_PACKET_FREQ_SECONDS 10 // in seconds #endif //BLUMEN_LUMEN_SETTINGS_H From 2190051274d87d72ec195770e2eece0e265f10e5 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 06:19:07 -0700 Subject: [PATCH 041/151] Added phrase priority to the settings --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 5 ++--- src/app/ss_blumen_lumen/blumen_lumen_settings.h | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 2c41df2..8a1aa27 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -392,7 +392,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) bool SendMotorCommand = false; blumen_packet MotorCommand = {}; - r64 PhraseGroupingTime = 1.0f; while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) { gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); @@ -410,7 +409,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { BLState->NextHotHue = NewHue; if (SecondsElapsed(BLState->TimePhraseReceptionBegan, - Context->SystemTime_Current) > PhraseGroupingTime) + Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime) { BLState->TimePhraseReceptionBegan = Context->SystemTime_Current; BLState->InPhraseReceptionMode = true; @@ -470,7 +469,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (BLState->InPhraseReceptionMode) { r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current); - if (SecondsSincePhraseBegan > PhraseGroupingTime) + if (SecondsSincePhraseBegan > PhrasePriorityMessageGroupingTime) { // if we are in standard color mode, shift all flowers to the new color // otherwise, only shift the next flower in the sequence to the new color diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index ab38ed8..cb52530 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -141,6 +141,8 @@ r32 FullBrightnessPercent = 1.0f; // 2 is 2x as fast. r32 GlobalAnimSpeed = 1.0f; +r64 PhrasePriorityMessageGroupingTime = 1.0f; + // How often should Lumenarium send its status to the python server? // #define STATUS_PACKET_FREQ_SECONDS 10 // in seconds From d0b54e92ef9ba280dd111f4fd6fb06642e2699ab Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 10:44:40 -0700 Subject: [PATCH 042/151] Networking debugging --- src/app/platform_win32/win32_foldhaus_socket.h | 4 +++- src/app/ss_blumen_lumen/blumen_lumen.cpp | 3 ++- src/app/ss_blumen_lumen/message_queue.cpp | 7 ++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index b67107f..eb4ffa5 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -156,7 +156,7 @@ Win32ConnectSocket(platform_socket_manager* Manager, platform_socket* Socket) // If iMode == 0, blocking is enabled // if iMode != 0, non-blocking mode is enabled - u_long iMode = 0; + u_long iMode = 1; Error = ioctlsocket(SocketHandle, FIONBIO, &iMode); if (Error != NO_ERROR) { @@ -244,7 +244,9 @@ Win32SocketPeek(platform_socket_manager* Manager, platform_socket* Socket) char Temp[4]; u32 TempSize = 4; + OutputDebugString("Pre Peek"); s32 BytesQueued = recv(*Win32Sock, Temp, TempSize, Flags); + OutputDebugString("Post Peek"); if (BytesQueued != SOCKET_ERROR) { Result = (u32)BytesQueued; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 8a1aa27..ae89252 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -167,6 +167,8 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) s32 Flags = 0; SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags); } + + Assert(!MessageQueue_CanRead(*Data->OutgoingMsgQueue)); } MessageQueue_Clear(Data->OutgoingMsgQueue); @@ -208,7 +210,6 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED); - } internal void diff --git a/src/app/ss_blumen_lumen/message_queue.cpp b/src/app/ss_blumen_lumen/message_queue.cpp index 241b864..bd783a5 100644 --- a/src/app/ss_blumen_lumen/message_queue.cpp +++ b/src/app/ss_blumen_lumen/message_queue.cpp @@ -47,12 +47,13 @@ internal gs_data MessageQueue_Read(blumen_network_msg_queue* Queue) { gs_data Result = {}; - u32 ReadIndex = Queue->ReadHead++; - if (Queue->ReadHead >= BLUMEN_MESSAGE_QUEUE_COUNT) + u32 ReadIndex = Queue->ReadHead; + if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT) { - Queue->ReadHead = 0; + ReadIndex = 0; } Result = Queue->Buffers[ReadIndex]; + Queue->ReadHead = ReadIndex; return Result; } From bbf07cc6e233b34251f38960c7f339d85e916be5 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Thu, 8 Apr 2021 14:33:37 -0400 Subject: [PATCH 043/151] Converting to non-blocking socket calls --- .../platform_win32/win32_foldhaus_socket.h | 38 +++++++++++++------ src/app/ss_blumen_lumen/blumen_lumen.cpp | 16 +++++--- .../ss_blumen_lumen/blumen_lumen_settings.h | 2 +- src/app/ss_blumen_lumen/message_queue.cpp | 16 +++++++- 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index eb4ffa5..1b449bd 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -156,7 +156,7 @@ Win32ConnectSocket(platform_socket_manager* Manager, platform_socket* Socket) // If iMode == 0, blocking is enabled // if iMode != 0, non-blocking mode is enabled - u_long iMode = 1; + u_long iMode = 0; Error = ioctlsocket(SocketHandle, FIONBIO, &iMode); if (Error != NO_ERROR) { @@ -169,6 +169,7 @@ Win32ConnectSocket(platform_socket_manager* Manager, platform_socket* Socket) u32 Status = WSAGetLastError(); if (Status == WSAEWOULDBLOCK) { + // Non-blocking sockets #if 0 TIMEVAL Timeout = { 0, 500 }; fd_set SocketSet = {}; @@ -244,9 +245,12 @@ Win32SocketPeek(platform_socket_manager* Manager, platform_socket* Socket) char Temp[4]; u32 TempSize = 4; - OutputDebugString("Pre Peek"); - s32 BytesQueued = recv(*Win32Sock, Temp, TempSize, Flags); - OutputDebugString("Post Peek"); + //OutputDebugString("Pre Peek..."); + //s32 BytesQueued = recv(*Win32Sock, Temp, TempSize, Flags); + u_long BytesQueued = 0; + ioctlsocket(*Win32Sock, FIONREAD, &BytesQueued); + //OutputDebugString("Post Peek\n"); + if (BytesQueued != SOCKET_ERROR) { Result = (u32)BytesQueued; @@ -257,6 +261,14 @@ Win32SocketPeek(platform_socket_manager* Manager, platform_socket* Socket) switch (Error) { case WSAEWOULDBLOCK: + { + // NOTE(PS): This case covers non-blocking sockets + // if we peek and there's nothing there, it returns + // this error code. MSDN says its a non-fatal error + // and the operation should be retried later + Result = 0; + } break; + case WSAENOTCONN: case WSAECONNRESET: case WSAECONNABORTED: @@ -267,7 +279,7 @@ Win32SocketPeek(platform_socket_manager* Manager, platform_socket* Socket) InvalidDefaultCase; } } - return Result; + return (s32)Result; } internal gs_data @@ -315,12 +327,20 @@ Win32SocketSend(platform_socket_manager* Manager, platform_socket* Socket, u32 A s32 LengthSent = sendto(*Win32Sock, (char*)Data.Memory, Data.Size, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); - OutputDebugString("Attempting To Send Network Data: "); + //OutputDebugString("Attempting To Send Network Data: "); if (LengthSent == SOCKET_ERROR) { s32 Error = WSAGetLastError(); switch (Error) { + case WSAEWOULDBLOCK: + { + // NOTE(PS): This covers non-blocking sockets + // In this case the message should be tried again + LengthSent = 0; + //OutputDebugString("Not sent, buffered\n"); + }break; + case WSAECONNABORTED: case WSAENETUNREACH: case WSAECONNRESET: @@ -329,16 +349,12 @@ Win32SocketSend(platform_socket_manager* Manager, platform_socket* Socket, u32 A if (CloseSocket(Manager, Socket)) { Error = 0; + OutputDebugString("Error\n"); } }break; InvalidDefaultCase; } - - if (Error) - { - OutputDebugString("Error\n"); - } } else { diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index ae89252..e807938 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -160,18 +160,22 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) while (MessageQueue_CanRead(*Data->OutgoingMsgQueue)) { - Msg = MessageQueue_Read(Data->OutgoingMsgQueue); + Msg = MessageQueue_Peek(Data->OutgoingMsgQueue); u32 Address = WeathermanIPV4; u32 Port = WeathermanPort; s32 Flags = 0; - SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags); + s32 LengthSent = SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags); + if (LengthSent != 0) + { + // if we actually sent the message, THEN we pull it off the + // message queue + MessageQueue_Read(Data->OutgoingMsgQueue); + } else { + break; + } } - - Assert(!MessageQueue_CanRead(*Data->OutgoingMsgQueue)); } - - MessageQueue_Clear(Data->OutgoingMsgQueue); } CloseSocket(Data->SocketManager, ListenSocket); diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 5ad23dc..3fbefc3 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -33,7 +33,7 @@ gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_r // NOTE: There is no need to modify the MotorOpenTimesCount variable - // it is a compile time constant that gets calculated automatically global time_range MotorOpenTimes[] = { - { 08, 45, 17, 45 }, + { 12, 45, 17, 45 }, { 19, 00, 23, 00 }, }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit diff --git a/src/app/ss_blumen_lumen/message_queue.cpp b/src/app/ss_blumen_lumen/message_queue.cpp index bd783a5..ba974de 100644 --- a/src/app/ss_blumen_lumen/message_queue.cpp +++ b/src/app/ss_blumen_lumen/message_queue.cpp @@ -44,7 +44,7 @@ MessageQueue_CanRead(blumen_network_msg_queue Queue) } internal gs_data -MessageQueue_Read(blumen_network_msg_queue* Queue) +MessageQueue_Peek(blumen_network_msg_queue* Queue) { gs_data Result = {}; u32 ReadIndex = Queue->ReadHead; @@ -53,7 +53,19 @@ MessageQueue_Read(blumen_network_msg_queue* Queue) ReadIndex = 0; } Result = Queue->Buffers[ReadIndex]; - Queue->ReadHead = ReadIndex; + return Result; +} + +internal gs_data +MessageQueue_Read(blumen_network_msg_queue* Queue) +{ + gs_data Result = {}; + u32 ReadIndex = Queue->ReadHead++; + if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT) + { + Queue->ReadHead = 0; + } + Result = Queue->Buffers[ReadIndex]; return Result; } From f45be27edcac157f8683796dafca93db71985cd0 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 10:07:41 -1000 Subject: [PATCH 044/151] Fixed some CSV parsing errors and implemented white and black colors --- src/app/foldhaus_app.h | 2 +- src/app/patterns/blumen_patterns.h | 35 +++--- src/app/ss_blumen_lumen/blumen_lumen.cpp | 12 ++- .../ss_blumen_lumen/blumen_lumen_settings.h | 4 +- src/app/ss_blumen_lumen/phrase_hue_map.h | 102 +++++++++++++----- 5 files changed, 107 insertions(+), 48 deletions(-) diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index 4123248..9d9e857 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -42,6 +42,7 @@ typedef struct panel panel; #include "engine/animation/foldhaus_animation_renderer.cpp" #include "engine/user_space.h" +#include "ss_blumen_lumen/gfx_math.h" #include "ss_blumen_lumen/phrase_hue_map.h" #include "ss_blumen_lumen/blumen_lumen.h" @@ -95,7 +96,6 @@ LoadAssembly(gs_const_string Path, app_state* State, context Context) #include "engine/user_space.cpp" -#include "ss_blumen_lumen/gfx_math.h" #include "ss_blumen_lumen/sdf.h" #include "patterns/blumen_patterns.h" #include "ss_blumen_lumen/blumen_lumen.cpp" diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 7362394..b325d25 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -127,9 +127,9 @@ Pattern_BasicFlowers(led_buffer* Leds, led_buffer_range Range, assembly Assembly Time = Time * BLState->PatternSpeed; phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; - v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); - v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); - v4 C2 = HSVToRGB({Hue.Hue2, 1, 1, 1}); + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) { @@ -166,9 +166,9 @@ Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Ti Time = Time * BLState->PatternSpeed; phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); - v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); - v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); - v4 C2 = HSVToRGB({Hue.Hue2, 1, 1, 1}); + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); r32 Top = 120 + (SinR32(Time) * 10); r32 Mid = 70 + (CosR32(Time * 2.13) * 20); @@ -228,8 +228,9 @@ Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time = Time * BLState->PatternSpeed; phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); - v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); - v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { @@ -290,9 +291,9 @@ Pattern_Leafy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 T Time = Time * BLState->PatternSpeed; phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); - v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); - v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); - v4 C2 = HSVToRGB({Hue.Hue2, 1, 1, 1}); + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { @@ -326,9 +327,9 @@ Pattern_LeafyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, Time = Time * BLState->PatternSpeed; phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); - v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); - v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); - v4 C2 = HSVToRGB({Hue.Hue2, 1, 1, 1}); + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { @@ -609,9 +610,9 @@ Pattern_VoicePattern(led_buffer* Leds, led_buffer_range Range, assembly Assembly Time = Time * BLState->PatternSpeed; phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); - v4 C0 = HSVToRGB({Hue.Hue0, 1, 1, 1}); - v4 C1 = HSVToRGB({Hue.Hue1, 1, 1, 1}); - v4 C2 = HSVToRGB({Hue.Hue2, 1, 1, 1}); + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index e807938..a257bf4 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -380,10 +380,14 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); - BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, 5, &State->AnimationSystem, BLState); + BlumenLumen_SetPatternMode(BlumenPattern_Standard, 5, &State->AnimationSystem, BLState); #endif State->AnimationSystem.TimelineShouldAdvance = true; + BLState->StandardPatternHues.Hue0.Flags = Hue_Value; + BLState->StandardPatternHues.Hue1.Flags = Hue_Value; + BLState->StandardPatternHues.Hue2.Flags = Hue_Value; + BlumenLumen_AppendBootupLog(State, BLState, Context); return Result; } @@ -510,9 +514,9 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) r32 ColorRelOscSpeed = 1 * ColorSpeed;; r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); - BLState->StandardPatternHues.Hue0 = ModR32(ColorOscillation * 360, 360); - BLState->StandardPatternHues.Hue1 = ModR32(BaseTime + ColorRelationship, 360); - BLState->StandardPatternHues.Hue2 = LerpR32(.3f, BLState->StandardPatternHues.Hue0, BLState->StandardPatternHues.Hue1); + BLState->StandardPatternHues.Hue0.Hue = ModR32(ColorOscillation * 360, 360); + BLState->StandardPatternHues.Hue1.Hue = ModR32(BaseTime + ColorRelationship, 360); + BLState->StandardPatternHues.Hue2.Hue = LerpR32(.3f, BLState->StandardPatternHues.Hue0.Hue, BLState->StandardPatternHues.Hue1.Hue); // Transition back to standard mode after some time if (BLState->PatternMode == BlumenPattern_VoiceCommand) diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 3fbefc3..b7d7aa7 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -16,8 +16,8 @@ gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold"); // The path to the phrase map CSV. Can be an absolute path, or relative // to the app_run_tree folder -gs_const_string PhraseMapCSVPath = ConstString("data/flower_codes.csv"); -char PhraseMapCSVSeparator = ','; +gs_const_string PhraseMapCSVPath = ConstString("data/flower_codes.tsv"); +char PhraseMapCSVSeparator = '\t'; // Search Strings for which folders to find ambient animation files and // voice animation files in. diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h index 0d7d66c..29390be 100644 --- a/src/app/ss_blumen_lumen/phrase_hue_map.h +++ b/src/app/ss_blumen_lumen/phrase_hue_map.h @@ -3,58 +3,112 @@ #ifndef PHRASE_HUE_MAP_H #define PHRASE_HUE_MAP_H +enum p_hue_flag +{ + Hue_Value = 0, + Hue_White = 1, + Hue_Black = 2, +}; + +typedef struct p_hue +{ + r64 Hue; + p_hue_flag Flags; +} p_hue; + typedef struct phrase_hue_map { + u64 CountMax; u64 Count; gs_const_string* Phrases; u64* PhraseHashes; - r32* Hue0; - r32* Hue1; - r32* Hue2; + p_hue* Hue0; + p_hue* Hue1; + p_hue* Hue2; } phrase_hue_map; typedef struct phrase_hue { gs_const_string Phrase; u64 PhraseHash; - r32 Hue0; - r32 Hue1; - r32 Hue2; + p_hue Hue0; + p_hue Hue1; + p_hue Hue2; } phrase_hue; +internal p_hue +CreateHueFromString(gs_const_string Str) +{ + p_hue Result = {}; + if (Str.Str[0] == 'b') { + Result.Flags = Hue_Black; + } else if (Str.Str[0] == 'w') { + Result.Flags = Hue_White; + } else { + Result.Flags = Hue_Value; + Result.Hue = (r64)ParseFloat(Str); + } + return Result; +} + +internal v4 +RGBFromPhraseHue (p_hue H) +{ + v4 Result = {}; + switch (H.Flags) + { + case Hue_Black: { Result = v4{1, 0, 0, 1}; } break; + case Hue_White: { Result = v4{1, 0, 1, 1}; } break; + case Hue_Value: { Result = v4{(r32)H.Hue, 1, 1, 1}; } break; + InvalidDefaultCase; + } + Result = HSVToRGB(Result); + return Result; +} + internal phrase_hue_map PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) { phrase_hue_map Result = {}; - Result.Count = Sheet.RowCount - 1; // we don't include the header row - Result.Phrases = PushArray(Arena, gs_const_string, Result.Count); - Result.PhraseHashes = PushArray(Arena, u64, Result.Count); - Result.Hue0 = PushArray(Arena, r32, Result.Count); - Result.Hue1 = PushArray(Arena, r32, Result.Count); - Result.Hue2 = PushArray(Arena, r32, Result.Count); + Result.CountMax = Sheet.RowCount - 1; // we don't include the header row + Result.Phrases = PushArray(Arena, gs_const_string, Result.CountMax); + Result.PhraseHashes = PushArray(Arena, u64, Result.CountMax); + Result.Hue0 = PushArray(Arena, p_hue, Result.CountMax); + Result.Hue1 = PushArray(Arena, p_hue, Result.CountMax); + Result.Hue2 = PushArray(Arena, p_hue, Result.CountMax); + s32 DestOffset = 0; for (u32 Row = 1; Row < Sheet.RowCount; Row++) { - u32 Index = Row - 1; + s32 Index = (Row - 1) - DestOffset; + Assert(Index >= 0 && Index < Result.CountMax); + gs_const_string Phrase = CSVSheet_GetCell(Sheet, 0, Row); u64 PhraseHash = HashDJB2ToU32(StringExpand(Phrase)); - gs_const_string Hue0Str = CSVSheet_GetCell(Sheet, - 1, Row); - gs_const_string Hue1Str = CSVSheet_GetCell(Sheet, - 2, Row); - gs_const_string Hue2Str = CSVSheet_GetCell(Sheet, - 3, Row); - gs_const_string Homonyms = CSVSheet_GetCell(Sheet, - 4, Row); + + gs_const_string Hue0Str = CSVSheet_GetCell(Sheet, 1, Row); + gs_const_string Hue1Str = CSVSheet_GetCell(Sheet, 2, Row); + gs_const_string Hue2Str = CSVSheet_GetCell(Sheet, 3, Row); + gs_const_string Homonyms = CSVSheet_GetCell(Sheet, 4, Row); + if (Phrase.Length == 0 || + Hue0Str.Length == 0 || + Hue1Str.Length == 0 || + Hue2Str.Length == 0) + { + DestOffset++; + continue; + } Result.Phrases[Index] = PushStringF(Arena, Phrase.Length, "%S", Phrase).ConstString; Result.PhraseHashes[Index] = PhraseHash; - Result.Hue0[Index] = (r64)ParseFloat(Hue0Str); - Result.Hue1[Index] = (r64)ParseFloat(Hue1Str); - Result.Hue2[Index] = (r64)ParseFloat(Hue2Str); + Result.Hue0[Index] = CreateHueFromString(Hue0Str); + Result.Hue1[Index] = CreateHueFromString(Hue1Str); + Result.Hue2[Index] = CreateHueFromString(Hue2Str); } + Result.Count = Result.CountMax + DestOffset; + return Result; } From 114ab0b5d88cab2dc44aba0d24c13e2d86748889 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 12:33:00 -1000 Subject: [PATCH 045/151] Fixed a problem where we were overwriting retained states --- src/app/editor/interface.h | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index ee3c9d6..a1af8c1 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -424,7 +424,7 @@ ui_InterfaceReset(ui_interface* Interface) Interface->RetainedState[i].FramesSinceAccess += 1; if (Interface->RetainedState[i].FramesSinceAccess > 1) { - Interface->RetainedState[i] = {0}; + Interface->RetainedState[i].Id = {0}; } } @@ -515,6 +515,9 @@ ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id) { if (ui_WidgetIdsEqual(Interface->RetainedState[i].Id, Id)) { + // NOTE(PS): If we are accessing a retained state, it shouldn't + // have been accessed longer ago than the last frame + Assert(Interface->RetainedState[i].FramesSinceAccess <= 2); Interface->RetainedState[i].FramesSinceAccess = 0; Result = Interface->RetainedState + i; break; @@ -526,10 +529,28 @@ ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id) internal ui_widget_retained_state* ui_CreateRetainedState(ui_interface* Interface, ui_widget* Widget) { - u64 Index = Interface->RetainedStateCount++; + u64 Index = RETAINED_STATE_MAX; + if (Interface->RetainedStateCount >= RETAINED_STATE_MAX) + { + for (u32 i = 0; i < Interface->RetainedStateCount; i++) + { + if (Interface->RetainedState[i].FramesSinceAccess > 0) + { + Index = i; + break; + } + } + } else { + Index = Interface->RetainedStateCount++; + } + + Assert(Index < RETAINED_STATE_MAX); ui_widget_retained_state* Result = Interface->RetainedState + Index; Result->Id = Widget->Id; - Result->EditString = PushString(Interface->Permanent, 256); + if (Result->EditString.Size != 256) + { + Result->EditString = PushString(Interface->Permanent, 256); + } return Result; } From 95107a268532d42885ffc91201c51362ca461e3f Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 13:46:08 -1000 Subject: [PATCH 046/151] Guarding against empty network packets --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index a257bf4..f12a54e 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -404,6 +404,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) { gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); + if (PacketData.Memory == 0) continue; blumen_packet Packet = *(blumen_packet*)PacketData.Memory; switch (Packet.Type) { From cd2cfac16e52996eab89379920cff5d793f4e1a4 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 13:54:26 -1000 Subject: [PATCH 047/151] preventing reading from past the end of the message queue --- src/app/ss_blumen_lumen/message_queue.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/ss_blumen_lumen/message_queue.cpp b/src/app/ss_blumen_lumen/message_queue.cpp index ba974de..a132d64 100644 --- a/src/app/ss_blumen_lumen/message_queue.cpp +++ b/src/app/ss_blumen_lumen/message_queue.cpp @@ -64,6 +64,7 @@ MessageQueue_Read(blumen_network_msg_queue* Queue) if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT) { Queue->ReadHead = 0; + ReadIndex = 0; } Result = Queue->Buffers[ReadIndex]; return Result; From 232180299f9aeefb1cf950ace74d776d5bc76b5b Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 14:53:41 -1000 Subject: [PATCH 048/151] Fixed a problem with dropdowns where, if you clicked a button inside them, they wouldn't register that they are no longer open. --- src/app/editor/interface.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index a1af8c1..6a0e228 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -1386,13 +1386,20 @@ ui_BeginDropdown(ui_interface* Interface, gs_string Text) internal void ui_EndDropdown(ui_interface* Interface) { + gs_string LayoutName = MakeString("DropdownLayout"); ui_widget* Layout = Interface->ActiveLayout; ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->WidgetReference); if (State) { - if (State->Value) + bool IsOpen = State->Value; + ui_widget* Widget = Interface->ActiveLayout; + bool IsStillHot = StringsEqualUpToLength(Widget->String, LayoutName, LayoutName.Length); + if (IsOpen && IsStillHot) { - ui_PopLayout(Interface, MakeString("DropdownLayout")); + ui_PopLayout(Interface, LayoutName); + } else if (IsOpen && !IsStillHot) + { + State->Value = false; } } } From b952b3abade40bef3e55a7d48be1984a57e5cce9 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 15:11:39 -1000 Subject: [PATCH 049/151] added pattern transition speed to the settings file --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 4 ++-- src/app/ss_blumen_lumen/blumen_lumen_settings.h | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index f12a54e..371b8db 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -380,7 +380,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); - BlumenLumen_SetPatternMode(BlumenPattern_Standard, 5, &State->AnimationSystem, BLState); + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); #endif State->AnimationSystem.TimelineShouldAdvance = true; @@ -497,7 +497,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; } - BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, 5, &State->AnimationSystem, BLState); + BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); BLState->TimeLastSetToVoiceMode = Context->SystemTime_Current; BLState->LastHuePhrase = NewHue; BLState->ShouldUpdateLog = true; diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index b7d7aa7..abcb17d 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -99,6 +99,12 @@ r32 FullBrightnessPercent = 1.0f; // 2 is 2x as fast. r32 GlobalAnimSpeed = 1.0f; +// How long it takes to fade from one animation to the next. +// This is used both for transitioning between animation files +// as well as transitioning from Standard pattern mode to voice +// activated mode +r32 GlobalAnimTransitionSpeed = 5.0f; + r64 PhrasePriorityMessageGroupingTime = 1.0f; // How often should Lumenarium send its status to the python server? From 876c7107d161f8e28ae0cce5d5416f2c8915d26e Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 15:15:36 -1000 Subject: [PATCH 050/151] Printing out what hues are being displayed --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 371b8db..8995226 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -398,6 +398,8 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; BLState->ShouldUpdateLog = false; + gs_string DebugStr = PushString(State->Transient, 256); + bool SendMotorCommand = false; blumen_packet MotorCommand = {}; @@ -415,8 +417,12 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) u32 NameLen = CStringLength(Mic.AnimationFileName); phrase_hue NewHue = PhraseHueMap_Get(BLState->PhraseHueMap, NameHash); - if (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length) + if (NewHue.Phrase.Length > 0 && BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length) { + PrintF(&DebugStr, "Queuing: %S\n", NewHue.Phrase); + NullTerminate(&DebugStr); + OutputDebugString(DebugStr.Str); + BLState->NextHotHue = NewHue; if (SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime) @@ -484,6 +490,12 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) // if we are in standard color mode, shift all flowers to the new color // otherwise, only shift the next flower in the sequence to the new color phrase_hue NewHue = BLState->NextHotHue; + + PrintF(&DebugStr, "Switching To: %S\n", NewHue.Phrase); + NullTerminate(&DebugStr); + OutputDebugString(DebugStr.Str); + + if (BLState->PatternMode == BlumenPattern_Standard) { BLState->AssemblyColors[0] = NewHue; @@ -778,6 +790,16 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) } BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); + + ui_Label(I, MakeString("Set Internal Motor State:")); + if (ui_Button(I, MakeString("Closed"))) + { + + } + if (ui_Button(I, MakeString("Open"))) + { + + } } } From b636e9ef3db03bfc6c69e1b63dd32ee3743c47f2 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 15:35:55 -1000 Subject: [PATCH 051/151] Debug utilities for internal motor state tracking, and fixed a bug where closing the motors wouldn't turn the upper leds back on --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 51 +++++++++++++++++------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 8995226..be87d78 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -392,6 +392,25 @@ BlumenLumen_CustomInit(app_state* State, context Context) return Result; } +internal void +BlumenLumen_UpdateMotorState(blumen_lumen_state* BLState, motor_status_packet Motor, context Context) +{ + motor_packet CurrPos = Motor.Pos; + motor_packet LastPos = BLState->LastKnownMotorState; + DEBUG_ReceivedMotorPositions(LastPos, Motor.Pos, Context.ThreadContext); + + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i]) + { + BLState->LastTimeMotorStateChanged[i] = Context.SystemTime_Current.NanosSinceEpoch; + } + } + + BLState->LastKnownMotorState = Motor.Pos; + BLState->ShouldUpdateLog = true; +} + internal void BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { @@ -444,20 +463,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Motor.Temperature = (T[0] << 8 | T[1] << 0); - motor_packet CurrPos = Motor.Pos; - motor_packet LastPos = BLState->LastKnownMotorState; - DEBUG_ReceivedMotorPositions(LastPos, Motor.Pos, Context->ThreadContext); - - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) - { - if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i]) - { - BLState->LastTimeMotorStateChanged[i] = Context->SystemTime_Current.NanosSinceEpoch; - } - } - - BLState->LastKnownMotorState = Motor.Pos; - BLState->ShouldUpdateLog = true; + BlumenLumen_UpdateMotorState(BLState, Motor, *Context); }break; case PacketType_Temperature: @@ -628,6 +634,11 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) BLState->ShouldDimUpperLeds[i] = true; } } + else if (MotorPos == MotorState_Closed || + MotorPos == MotorState_HalfOpen) + { + BLState->ShouldDimUpperLeds[i] = false; + } } // NOTE(PS): If the flowers are mostly open or full open @@ -794,11 +805,23 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) ui_Label(I, MakeString("Set Internal Motor State:")); if (ui_Button(I, MakeString("Closed"))) { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Closed; + Motor.Pos.FlowerPositions[1] = MotorState_Closed; + Motor.Pos.FlowerPositions[2] = MotorState_Closed; + Motor.Temperature = 16; + BlumenLumen_UpdateMotorState(BLState, Motor, Context); } if (ui_Button(I, MakeString("Open"))) { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Open; + Motor.Pos.FlowerPositions[1] = MotorState_Open; + Motor.Pos.FlowerPositions[2] = MotorState_Open; + Motor.Temperature = 16; + BlumenLumen_UpdateMotorState(BLState, Motor, Context); } } } From 1d3d7f5f869d30270487457cc89c2253de3bdd65 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 15:42:26 -1000 Subject: [PATCH 052/151] Making Motor Debug Commands more verbose --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index be87d78..e2b7035 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -731,25 +731,25 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) { ui_Label(I, Label); - bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == 1; + bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) { - PendingPacket.FlowerPositions[MotorIndex] = 1; + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; } - bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == 3; + bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) { - PendingPacket.FlowerPositions[MotorIndex] = 3; + PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; } - bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == 4; + bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) { - PendingPacket.FlowerPositions[MotorIndex] = 4; + PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; } - bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == 2; + bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) { - PendingPacket.FlowerPositions[MotorIndex] = 2; + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; } } ui_EndRow(I); From 7a95861bf8ab5ff56847224922a34af07cfeed73 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 16:06:58 -1000 Subject: [PATCH 053/151] Ability to override all assemblies in debug panel --- build/build_app_msvc_win32_debug.bat | 2 +- .../panels/foldhaus_panel_assembly_debug.h | 2 ++ .../engine/assembly/foldhaus_assembly_debug.h | 29 +++++++++++++++---- src/app/ss_blumen_lumen/blumen_lumen.h | 5 ---- .../ss_blumen_lumen/blumen_lumen_settings.h | 2 +- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index 7e14230..eec2df0 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -6,7 +6,7 @@ SET MyPath=%MyPath:~0,-1% call %MyPath%\_prebuild_win32.bat app debug msvc call %MyPath%\setup_cl.bat -SET CommonCompilerFlags=-nologo -DDEBUG=0 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src +SET CommonCompilerFlags=-nologo -DDEBUG=1 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src SET CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -I%CommonLibs% -O2 %CommonCompilerFlags% diff --git a/src/app/editor/panels/foldhaus_panel_assembly_debug.h b/src/app/editor/panels/foldhaus_panel_assembly_debug.h index 5528a3a..d1c912e 100644 --- a/src/app/editor/panels/foldhaus_panel_assembly_debug.h +++ b/src/app/editor/panels/foldhaus_panel_assembly_debug.h @@ -66,6 +66,8 @@ AssemblyDebug_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren { InterfaceAssert(Interface->PerFrameMemory); + State->AssemblyDebugState.AllAssemblies = ui_ToggleText(Interface, MakeString("All Assemblies"), State->AssemblyDebugState.AllAssemblies); + gs_string OverrideStr = MakeString(OverrideTypeStrings[State->AssemblyDebugState.Override]); if (ui_BeginLabeledDropdown(Interface, MakeString("Override"), OverrideStr)) { diff --git a/src/app/engine/assembly/foldhaus_assembly_debug.h b/src/app/engine/assembly/foldhaus_assembly_debug.h index 2bf6f40..c7c59d8 100644 --- a/src/app/engine/assembly/foldhaus_assembly_debug.h +++ b/src/app/engine/assembly/foldhaus_assembly_debug.h @@ -42,6 +42,7 @@ struct assembly_debug_state { override_type Override; + bool AllAssemblies; u32 TargetAssembly; u32 TargetStrip; @@ -101,12 +102,9 @@ AssemblyDebug_OverrideTagValueWithColor(assembly Assembly, led_buffer LedBuffer, } internal void -AssemblyDebug_OverrideOutput(assembly_debug_state State, assembly_array Assemblies, led_system LedSystem) +AssemblyDebug_OverrideOutputForAssembly(assembly_debug_state State, led_system LedSystem, + assembly Assembly) { - if (State.Override == ADS_Override_None) return; - State.TargetColor = pixel{255,255,255}; - - assembly Assembly = Assemblies.Values[State.TargetAssembly]; led_buffer LedBuffer = LedSystem.Buffers[Assembly.LedBufferIndex]; u8 V = State.Brightness; @@ -203,6 +201,27 @@ AssemblyDebug_OverrideOutput(assembly_debug_state State, assembly_array Assembli } } +internal void +AssemblyDebug_OverrideOutput(assembly_debug_state State, assembly_array Assemblies, led_system LedSystem) +{ + if (State.Override == ADS_Override_None) return; + State.TargetColor = pixel{255,255,255}; + + if (State.AllAssemblies) + { + for (u32 i = 0; i < Assemblies.Count; i++) + { + assembly Assembly = Assemblies.Values[i]; + AssemblyDebug_OverrideOutputForAssembly(State, LedSystem, Assembly); + } + } + else + { + assembly Assembly = Assemblies.Values[State.TargetAssembly]; + AssemblyDebug_OverrideOutputForAssembly(State, LedSystem, Assembly); + } +} + #define FOLDHAUS_ASSEMBLY_DEBUG_H #endif // FOLDHAUS_ASSEMBLY_DEBUG_H \ No newline at end of file diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 0a81e8f..5e98557 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -43,11 +43,6 @@ typedef struct motor_packet typedef struct motor_status_packet { motor_packet Pos; - /* -u8 Motor1Pos; -u8 Motor2Pos; -u8 Motor3Pos; -*/ u8 MotorStatus[3]; u16 Temperature; diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index abcb17d..46f69b8 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -67,7 +67,7 @@ r64 VoiceCommandSustainDuration = 30.0; // in seconds // NOTE: This is not a symmetric operation. When we send a 'motor open' // command, we want to immediately turn the upper leds on so they appear // to have been on the whole time. -r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 5.0; // in seconds +r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 30.0; // in seconds // NOTE: Temperature & Time of Day Based Led Brightness Settings From c08222df13e3f68d2d7d2a6d18fd0b4215e38c11 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Thu, 8 Apr 2021 22:13:03 -0400 Subject: [PATCH 054/151] settings update --- src/app/ss_blumen_lumen/blumen_lumen_settings.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index abcb17d..9236c48 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -67,7 +67,7 @@ r64 VoiceCommandSustainDuration = 30.0; // in seconds // NOTE: This is not a symmetric operation. When we send a 'motor open' // command, we want to immediately turn the upper leds on so they appear // to have been on the whole time. -r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 5.0; // in seconds +r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 3000.0; // in seconds // NOTE: Temperature & Time of Day Based Led Brightness Settings @@ -85,7 +85,7 @@ s8 MinHighTemperature = 0; // This is multiplied by each pixels R, G, & B channels before being // sent. So if it is set to .1f, then the maximum brightness value sent // to any channel of any pixel will be 25 (255 * .1 = 25). -r32 HighTemperatureBrightnessPercent = .25f; +r32 HighTemperatureBrightnessPercent = 1.0f; // The percent brightness we set leds to when no other conditions apply // A value in the range 0:1 inclusive. @@ -101,9 +101,9 @@ r32 GlobalAnimSpeed = 1.0f; // How long it takes to fade from one animation to the next. // This is used both for transitioning between animation files -// as well as transitioning from Standard pattern mode to voice +// as well as transitioning from Standard pattern mode to voice // activated mode -r32 GlobalAnimTransitionSpeed = 5.0f; +r32 GlobalAnimTransitionSpeed = 0.1f; r64 PhrasePriorityMessageGroupingTime = 1.0f; From 4130d50c1bab1bb62228327c7e337345acc8d0e4 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 16:40:59 -1000 Subject: [PATCH 055/151] Primary Hue Pattern --- .../ambient_patterns/ambient_0.foldanim | 2 +- src/app/patterns/blumen_patterns.h | 70 +++++++++++++++++++ src/app/ss_blumen_lumen/blumen_lumen.cpp | 9 +++ src/app/ss_blumen_lumen/blumen_lumen.h | 2 + src/gs_libs/gs_types.cpp | 1 + 5 files changed, 83 insertions(+), 1 deletion(-) diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim index 15a653b..b965ed5 100644 --- a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim +++ b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim @@ -87,6 +87,6 @@ blocks:{ max: 179; }; layer_index: 1; - animation_name: "Pattern_Rainbow"; + animation_name: "Pattern_StemSolid"; }; }; diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index b325d25..bac4015 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -52,6 +52,32 @@ Pattern_HueShift(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r3 r32 CycleProgress = FractR32(Time / CycleLength); r32 CycleBlend = (SinR32(Time) * .5f) + .5f; +#if 0 + phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); + + v4 HSV = {}; + if (CycleProgress < .25f) + { + r32 P = CycleProgress * 4; + HSV = V4Lerp(C0, + } + else if (CycleProgress >= .25f && CycleProgress < .5f) + { + + } + else if (CycleProgress >= .5f && CycleProgress < .75f) + { + + } + else if (CycleProgress >= .75f) + { + + } +#endif + v4 HSV = { CycleProgress * 360, 1, 1, 1 }; v4 RGB = HSVToRGB(HSV); @@ -630,5 +656,49 @@ Pattern_VoicePattern(led_buffer* Leds, led_buffer_range Range, assembly Assembly } } +internal void +Pattern_StemSolid(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); + + pixel WhiteMask = V4ToRGBPixel(WhiteV4); + + led_strip_list Stem = BLState->StemStrips[Assembly.AssemblyIndex]; + for (u32 s = 0; s < Stem.Count; s++) + { + u32 StripIndex = Stem.StripIndices[s]; + v2_strip Strip = Assembly.Strips[StripIndex]; + for (u32 i = 0; i < Strip.LedCount; i++) + { + v4 P = Leds->Positions[i]; + Leds->Colors[i] = WhiteMask; + } + } +} + +internal void +Pattern_PrimaryHue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); + + pixel HueOut = V4ToRGBPixel(C0); + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + Leds->Colors[LedIndex] = HueOut; + } +} + #define BLUMEN_PATTERNS_H #endif // BLUMEN_PATTERNS_H \ No newline at end of file diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index e2b7035..d46c1ec 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -214,6 +214,9 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_StemSolid, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED); } internal void @@ -351,6 +354,12 @@ BlumenLumen_CustomInit(app_state* State, context Context) assembly* Flower1 = LoadAssembly(Flower1AssemblyPath, State, Context); assembly* Flower2 = LoadAssembly(Flower2AssemblyPath, State, Context); + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + assembly Assembly = State->Assemblies.Values[i]; + BLState->StemStrips[i] = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("stem"), &State->Permanent); + } + BLState->AssemblyNameToClearCoreMapCount = 3; BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent, u64, diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 5e98557..8b381cf 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -139,6 +139,8 @@ struct blumen_lumen_state temp_job_req JobReq; + led_strip_list StemStrips[BL_FLOWER_COUNT]; + platform_thread_handle MicListenThread; mic_listen_job_data MicListenJobData; diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index 28238c4..bcf9f99 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -2751,6 +2751,7 @@ PushSize_(gs_memory_arena* Arena, u64 Size, char* Location) } #endif Assert(CursorEntry != 0); + Assert(CursorHasRoom(CursorEntry->Cursor, Size)); Result = PushSizeOnCursor_(&CursorEntry->Cursor, Size, Location); Assert(Result.Memory != 0); From 03804d90b6d98ba2b9a891433a07af569ca04abf Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 16:46:46 -1000 Subject: [PATCH 056/151] Forcing all phrase hue maps to lowercase --- src/app/ss_blumen_lumen/phrase_hue_map.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h index 29390be..1bd5d2d 100644 --- a/src/app/ss_blumen_lumen/phrase_hue_map.h +++ b/src/app/ss_blumen_lumen/phrase_hue_map.h @@ -101,6 +101,15 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) } Result.Phrases[Index] = PushStringF(Arena, Phrase.Length, "%S", Phrase).ConstString; + for (u64 i = 0; i < Result.Phrases[Index].Length; i++) + { + char C = Result.Phrases[Index].Str[i]; + if (IsAlpha(C)) + { + Result.Phrases[Index].Str[i] = ToLower(C); + } + } + Result.PhraseHashes[Index] = PhraseHash; Result.Hue0[Index] = CreateHueFromString(Hue0Str); Result.Hue1[Index] = CreateHueFromString(Hue1Str); From afd890afeafa08367270f07f4017a34eb73a2c51 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 16:50:45 -1000 Subject: [PATCH 057/151] to lower before we hash --- src/app/ss_blumen_lumen/phrase_hue_map.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h index 1bd5d2d..34ef475 100644 --- a/src/app/ss_blumen_lumen/phrase_hue_map.h +++ b/src/app/ss_blumen_lumen/phrase_hue_map.h @@ -85,7 +85,6 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) gs_const_string Phrase = CSVSheet_GetCell(Sheet, 0, Row); - u64 PhraseHash = HashDJB2ToU32(StringExpand(Phrase)); gs_const_string Hue0Str = CSVSheet_GetCell(Sheet, 1, Row); gs_const_string Hue1Str = CSVSheet_GetCell(Sheet, 2, Row); @@ -110,6 +109,8 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) } } + u64 PhraseHash = HashDJB2ToU32(StringExpand(Result.Phrases[Index])); + Result.PhraseHashes[Index] = PhraseHash; Result.Hue0[Index] = CreateHueFromString(Hue0Str); Result.Hue1[Index] = CreateHueFromString(Hue1Str); From 2a11fc815b7ae23dfbbb257e145a92ae6a1ce9f0 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 16:54:50 -1000 Subject: [PATCH 058/151] Phrase reception timeout --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 27 ++++++++++++++---------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index d46c1ec..ede2fb1 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -445,20 +445,25 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) u32 NameLen = CStringLength(Mic.AnimationFileName); phrase_hue NewHue = PhraseHueMap_Get(BLState->PhraseHueMap, NameHash); - if (NewHue.Phrase.Length > 0 && BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length) + if (NewHue.Phrase.Length > 0) { - PrintF(&DebugStr, "Queuing: %S\n", NewHue.Phrase); - NullTerminate(&DebugStr); - OutputDebugString(DebugStr.Str); - - BLState->NextHotHue = NewHue; - if (SecondsElapsed(BLState->TimePhraseReceptionBegan, - Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime) + bool IsLonger = (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length); + bool IsntInPhraseReceptionMode = !BLState->InPhraseReceptionMode; + if (IsLonger || IsntInPhraseReceptionMode) { - BLState->TimePhraseReceptionBegan = Context->SystemTime_Current; - BLState->InPhraseReceptionMode = true; + PrintF(&DebugStr, "Queuing: %S\n", NewHue.Phrase); + NullTerminate(&DebugStr); + OutputDebugString(DebugStr.Str); + + BLState->NextHotHue = NewHue; + if (SecondsElapsed(BLState->TimePhraseReceptionBegan, + Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime) + { + BLState->TimePhraseReceptionBegan = Context->SystemTime_Current; + BLState->InPhraseReceptionMode = true; + } + BLState->TimeLastPhraseReceived = Context->SystemTime_Current; } - BLState->TimeLastPhraseReceived = Context->SystemTime_Current; } }break; From 29a92837a6e70b9d60ab46ee18f895d956d5ce16 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 17:42:20 -1000 Subject: [PATCH 059/151] Added a way to test hues on the sculpture from the debug panel --- .../panels/foldhaus_panel_assembly_debug.h | 5 +++++ .../engine/assembly/foldhaus_assembly_debug.h | 11 +++++++++++ src/app/foldhaus_app.h | 4 +++- src/app/patterns/blumen_patterns.h | 16 +--------------- src/app/ss_blumen_lumen/blumen_lumen.cpp | 2 +- src/app/ss_blumen_lumen/gfx_math.h | 14 ++++++++++++++ 6 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/app/editor/panels/foldhaus_panel_assembly_debug.h b/src/app/editor/panels/foldhaus_panel_assembly_debug.h index d1c912e..24922b3 100644 --- a/src/app/editor/panels/foldhaus_panel_assembly_debug.h +++ b/src/app/editor/panels/foldhaus_panel_assembly_debug.h @@ -118,6 +118,11 @@ AssemblyDebug_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly); }break; + case ADS_Override_AllHue: + { + State->AssemblyDebugState.TargetHue = (u32)ui_LabeledRangeSlider(Interface, MakeString("Hue"), (r32)State->AssemblyDebugState.TargetHue, 0, 360); + }break; + default: { InterfaceAssert(Interface->PerFrameMemory); diff --git a/src/app/engine/assembly/foldhaus_assembly_debug.h b/src/app/engine/assembly/foldhaus_assembly_debug.h index c7c59d8..ffe33ed 100644 --- a/src/app/engine/assembly/foldhaus_assembly_debug.h +++ b/src/app/engine/assembly/foldhaus_assembly_debug.h @@ -16,6 +16,7 @@ enum override_type ADS_Override_AllBlue, ADS_Override_AllOff, ADS_Override_AllWhite, + ADS_Override_AllHue, ADS_Override_TagWhite, ADS_Override_TagStripWhite, ADS_Override_ChannelWhite, @@ -32,6 +33,7 @@ global gs_const_string OverrideTypeStrings[] = { LitString("Override_AllBlue" ), LitString("ADS_Override_AllOff" ), LitString("ADS_Override_AllWhite" ), + LitString("ADS_Override_AllHue" ), LitString("ADS_Override_TagWhite" ), LitString("ADS_Override_TagStripWhite" ), LitString("ADS_Override_ChannelWhite," ), @@ -49,6 +51,7 @@ struct assembly_debug_state gs_string TagName; gs_string TagValue; + u32 TargetHue; pixel TargetColor; u32 TargetChannel; @@ -158,6 +161,14 @@ AssemblyDebug_OverrideOutputForAssembly(assembly_debug_state State, led_system AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{V, V, V}); }break; + case ADS_Override_AllHue: + { + v4 HSV = v4{(r32)State.TargetHue, 1, 1, 1}; + v4 RGB = HSVToRGB(HSV); + pixel P = V4ToRGBPixel(RGB); + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, P); + }break; + case ADS_Override_TagWhite: { AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0}); diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index 9d9e857..c0e425d 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -18,6 +18,9 @@ #include "engine/foldhaus_network_ordering.h" #include "engine/assembly/foldhaus_assembly.h" + +#include "ss_blumen_lumen/gfx_math.h" + #include "engine/assembly/foldhaus_assembly_parser.cpp" #include "engine/assembly/foldhaus_assembly_debug.h" @@ -42,7 +45,6 @@ typedef struct panel panel; #include "engine/animation/foldhaus_animation_renderer.cpp" #include "engine/user_space.h" -#include "ss_blumen_lumen/gfx_math.h" #include "ss_blumen_lumen/phrase_hue_map.h" #include "ss_blumen_lumen/blumen_lumen.h" diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index bac4015..8684d56 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -7,20 +7,6 @@ #define FLOWER_COLORS_COUNT 12 -internal pixel -V4ToRGBPixel(v4 C) -{ - C.x = Clamp01(C.x); - C.y = Clamp01(C.y); - C.z = Clamp01(C.z); - - pixel Result = {}; - Result.R = (u8)(C.x * 255); - Result.G = (u8)(C.y * 255); - Result.B = (u8)(C.z * 255); - return Result; -} - internal void Pattern_AltBloomMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { @@ -647,7 +633,7 @@ Pattern_VoicePattern(led_buffer* Leds, led_buffer_range Range, assembly Assembly v4 C = {}; C += GenPatchyColor(P, Time, C0, C2, {}); //C = GenVerticalLeaves((P - Assembly.Center) + v3{0, 150, 0}, Time, C0, C1, C2); - r32 Bands = GenLiquidBands(P, -250, Time); + //r32 Bands = GenLiquidBands(P, -250, Time); //C = V4Lerp(Bands, C * .5f, C1); //C = WhiteV4 * GenDotBands(P - Assembly.Center, Time); diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index ede2fb1..b80c80e 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -357,7 +357,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) for (u32 i = 0; i < BL_FLOWER_COUNT; i++) { assembly Assembly = State->Assemblies.Values[i]; - BLState->StemStrips[i] = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("stem"), &State->Permanent); + BLState->StemStrips[Assembly.AssemblyIndex] = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("stem"), &State->Permanent); } BLState->AssemblyNameToClearCoreMapCount = 3; diff --git a/src/app/ss_blumen_lumen/gfx_math.h b/src/app/ss_blumen_lumen/gfx_math.h index b4409e7..8b1acc8 100644 --- a/src/app/ss_blumen_lumen/gfx_math.h +++ b/src/app/ss_blumen_lumen/gfx_math.h @@ -443,4 +443,18 @@ while (Hue > 360.0f) { Hue -= 360.0f; } return Result; } +internal pixel +V4ToRGBPixel(v4 C) +{ + C.x = Clamp01(C.x); + C.y = Clamp01(C.y); + C.z = Clamp01(C.z); + + pixel Result = {}; + Result.R = (u8)(C.x * 255); + Result.G = (u8)(C.y * 255); + Result.B = (u8)(C.z * 255); + return Result; +} + #endif //GFX_MATH_H From 175f58fd077def0127a09081b37581abbd9bb701 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 18:11:56 -1000 Subject: [PATCH 060/151] Pattern Grow Fade Mask --- src/app/patterns/blumen_patterns.h | 37 +++++++++++++++++++++++- src/app/ss_blumen_lumen/blumen_lumen.cpp | 2 ++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 8684d56..bdfc539 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -662,7 +662,7 @@ Pattern_StemSolid(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r v2_strip Strip = Assembly.Strips[StripIndex]; for (u32 i = 0; i < Strip.LedCount; i++) { - v4 P = Leds->Positions[i]; + v4 P = Leds->Positions[Strip.LedLUT[i]]; Leds->Colors[i] = WhiteMask; } } @@ -686,5 +686,40 @@ Pattern_PrimaryHue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, } } +internal void +Pattern_GrowFadeMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + + r32 Period = 10.0f; // seconds + r32 ElapsedPct = FractR32(Time / Period); + + r32 ElapsedPctGrow = PowR32(ElapsedPct * 2, 2); + r32 ElapsedPctFade = Clamp01((ElapsedPct * 2) - 1); + + r32 Radius = 300 * ElapsedPctGrow; + + v3 Origin = Assembly.Center - v3{0, 150, 0}; + + r32 Brightness = Smoothstep(1.0f - ElapsedPctFade); + + pixel COutside = V4ToRGBPixel(BlackV4); + pixel CInside = V4ToRGBPixel(WhiteV4 * Brightness); + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + v3 P = Leds->Positions[LedIndex].xyz; + r32 Dist = V3Mag(P - Origin); + if (Dist < Radius) + { + Leds->Colors[LedIndex] = CInside; + } + else + { + Leds->Colors[LedIndex] = COutside; + } + } +} + #define BLUMEN_PATTERNS_H #endif // BLUMEN_PATTERNS_H \ No newline at end of file diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index b80c80e..cef7f08 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -217,6 +217,8 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_StemSolid, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_GrowFadeMask, PATTERN_MULTITHREADED); } internal void From 40014ca4714c7a27fde28691a9a774d34f9a048a Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 20:58:44 -1000 Subject: [PATCH 061/151] Rainbow Loading Pattern --- .../audio_responses/voice_command.foldanim | 2 +- src/app/patterns/blumen_patterns.h | 45 +++++++++++++++++++ src/app/ss_blumen_lumen/blumen_lumen.cpp | 1 + 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim index b5b409a..8303d19 100644 --- a/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim +++ b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim @@ -19,6 +19,6 @@ blocks:{ max: 3599; }; layer_index: 0; - animation_name: "Pattern_VoicePattern"; + animation_name: "Pattern_RainbowLoadingBar"; }; }; diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index bdfc539..e98594f 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -721,5 +721,50 @@ Pattern_GrowFadeMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly } } +internal void +Pattern_RainbowLoadingBar(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + + // constants + r32 Period = 5.0f; // seconds + r32 CSpeed = 16.0f; + r32 HIncrement = CSpeed * Period; + r32 HOffset = CSpeed * Period; + r32 MaxSphereRadius = 300; + + // sphere + r32 ElapsedPct = FractR32(Time / Period); + r32 ElapsedPctGrow = PowR32(ElapsedPct, 2); + r32 Radius = MaxSphereRadius * ElapsedPctGrow; + v3 Origin = Assembly.Center - v3{0, 150, 0}; + + // colors + r32 T = Time * CSpeed; + r32 TimeStep0 = T; + r32 TimeStep1 = T + HOffset; + r32 Hue0 = FloorR32(TimeStep0 / HIncrement) * HIncrement; + r32 Hue1 = FloorR32(TimeStep1 / HIncrement) * HIncrement; + v4 H0 = v4{ModR32(Hue0, 360), 1, 1, 1}; + v4 H1 = v4{ModR32(Hue1, 360), 1, 1, 1}; + pixel C0 = V4ToRGBPixel(HSVToRGB(H0)); + pixel C1 = V4ToRGBPixel(HSVToRGB(H1)); + + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + v3 P = Leds->Positions[LedIndex].xyz; + r32 Dist = V3Mag(P - Origin); + if (Dist < Radius) + { + Leds->Colors[LedIndex] = C1; + } + else + { + Leds->Colors[LedIndex] = C0; + } + } +} + #define BLUMEN_PATTERNS_H #endif // BLUMEN_PATTERNS_H \ No newline at end of file diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index cef7f08..d9ed8bb 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -219,6 +219,7 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_GrowFadeMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_RainbowLoadingBar, PATTERN_MULTITHREADED); } internal void From 7ff47ad4455ee3983dfb1697a7ab916cb7e0618c Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 8 Apr 2021 21:01:11 -1000 Subject: [PATCH 062/151] Fixed Pattern_StemSolid --- src/app/patterns/blumen_patterns.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index e98594f..fac6499 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -646,12 +646,6 @@ internal void Pattern_StemSolid(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; - Time = Time * BLState->PatternSpeed; - - phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); - v4 C0 = RGBFromPhraseHue(Hue.Hue0); - v4 C1 = RGBFromPhraseHue(Hue.Hue1); - v4 C2 = RGBFromPhraseHue(Hue.Hue2); pixel WhiteMask = V4ToRGBPixel(WhiteV4); @@ -662,8 +656,8 @@ Pattern_StemSolid(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r v2_strip Strip = Assembly.Strips[StripIndex]; for (u32 i = 0; i < Strip.LedCount; i++) { - v4 P = Leds->Positions[Strip.LedLUT[i]]; - Leds->Colors[i] = WhiteMask; + u32 LedIndex = Strip.LedLUT[i]; + Leds->Colors[LedIndex] = WhiteMask; } } } From 7a103dae420b19ea36c36671b458fb405acc810e Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 00:42:04 -1000 Subject: [PATCH 063/151] Lots of pattern work, plus color fading --- .../ambient_patterns/ambient_1.foldanim | 2 +- .../audio_responses/voice_command.foldanim | 20 +- .../animation/foldhaus_animation_renderer.cpp | 9 +- src/app/patterns/blumen_patterns.h | 215 ++++++++++---- src/app/ss_blumen_lumen/blumen_lumen.cpp | 272 ++++++++++-------- src/app/ss_blumen_lumen/blumen_lumen.h | 43 ++- .../ss_blumen_lumen/blumen_lumen_settings.h | 3 + src/app/ss_blumen_lumen/gfx_math.h | 9 +- src/app/ss_blumen_lumen/phrase_hue_map.h | 160 +++++++++-- 9 files changed, 528 insertions(+), 205 deletions(-) diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim index 19d552e..d7a5167 100644 --- a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim +++ b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim @@ -35,7 +35,7 @@ blocks:{ max: 2000; }; layer_index: 1; - animation_name: "Pattern_VerticalLines"; + animation_name: "Pattern_Leafy"; }; block:{ frame_range:{ diff --git a/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim index 8303d19..c21484c 100644 --- a/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim +++ b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim @@ -1,16 +1,20 @@ lumenarium_animation_file; animation_name: "voice_anim"; -layers_count: 1; -blocks_count: 1; +layers_count: 2; +blocks_count: 2; playable_range:{ min: 0; max: 3600; }; layers:{ layer:{ - name: "[New Layer]"; + name: "Base"; blend: "Add"; }; + layer:{ + name: "Add In"; + blend: "Overwrite"; + }; }; blocks:{ block:{ @@ -19,6 +23,14 @@ blocks:{ max: 3599; }; layer_index: 0; - animation_name: "Pattern_RainbowLoadingBar"; + animation_name: "Pattern_VoicePattern"; + }; + block:{ + frame_range:{ + min: 0; + max: 3600; + }; + layer_index: 1; + animation_name: "Pattern_VoiceAddIns"; }; }; diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index 208c87a..5bdaec1 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -8,7 +8,14 @@ internal pixel LedBlend_Overwrite(pixel PixelA, pixel PixelB, u8* UserData) { - return PixelB; + pixel Result = PixelB; + if (PixelB.R == 0 && + PixelB.G == 0 && + PixelB.G == 0) + { + Result = PixelA; + } + return Result; } internal pixel diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index fac6499..732192c 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -171,17 +171,8 @@ Pattern_BasicFlowers(led_buffer* Leds, led_buffer_range Range, assembly Assembly } internal void -Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_WavyOptions(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 C0, v4 C1, v4 C2) { - DEBUG_TRACK_FUNCTION; - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; - Time = Time * BLState->PatternSpeed; - - phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); - v4 C0 = RGBFromPhraseHue(Hue.Hue0); - v4 C1 = RGBFromPhraseHue(Hue.Hue1); - v4 C2 = RGBFromPhraseHue(Hue.Hue2); - r32 Top = 120 + (SinR32(Time) * 10); r32 Mid = 70 + (CosR32(Time * 2.13) * 20); r32 Bot = 0; @@ -233,7 +224,7 @@ Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Ti } internal void -Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { DEBUG_TRACK_FUNCTION; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; @@ -244,6 +235,18 @@ Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 v4 C1 = RGBFromPhraseHue(Hue.Hue1); v4 C2 = RGBFromPhraseHue(Hue.Hue2); + Pattern_WavyOptions(Leds, Range, Assembly, Time, Transient, UserData, 1, C0, C1, C2); +} + +internal void +Pattern_PatchyOptions(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 C0, v4 C1, v4 C2) +{ + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + + r32 BaseGA = 50.000f * (1 / Granularity); + r32 BaseGB = 135.20f * (1 / Granularity); + r32 BaseGC = 260.74f * (1 / Granularity); + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) { v4 P = Leds->Positions[LedIndex]; @@ -251,22 +254,41 @@ Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 r32 ScaleFactor = 1.0f / LedRange; v3 Pp = P.xyz + v3{150, 100, 0}; - r32 NoiseA = Noise3D((Pp / 38) + v3{0, 0, Time}); + r32 NoiseA = Noise3D((Pp / BaseGA) + v3{0, 0, Time}); NoiseA = PowR32(NoiseA, 3); NoiseA = Smoothstep(NoiseA); - v4 CA = C0 * NoiseA; - r32 NoiseB = Noise3D((Pp / 75) + v3{Time * 0.5f, 0, 0}); + r32 NoiseB = Noise3D((Pp / BaseGB) + v3{Time * 0.5f, 0, 0}); NoiseB = PowR32(NoiseB, 3); NoiseB = Smoothstep(NoiseB); - v4 CB = C1 * NoiseB; - v4 C = (C0 * NoiseA) + (C1 * NoiseB); - C /= (NoiseA + NoiseB); +#if 1 + r32 NoiseC = Noise3D((Pp / BaseGC) + v3{Time * 0.5f, 0, 0}); + NoiseC = PowR32(NoiseC, 3); + NoiseC = Smoothstep(NoiseC); +#else + r32 NoiseC = 0; +#endif + + v4 C = (C0 * NoiseA) + (C1 * NoiseB) + (C2 * NoiseC); + C /= (NoiseA + NoiseB + NoiseC); Leds->Colors[LedIndex] = V4ToRGBPixel(C); } } +internal void +Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); + Time = Time * BLState->PatternSpeed; + Pattern_PatchyOptions(Leds, Range, Assembly, Time, Transient, UserData, 5, C0, C1, C2); +} + internal r32 Leafy_BandSDF(v3 P, gs_random_series* Random, r32 Time) { @@ -412,33 +434,35 @@ Pattern_VerticalLines(led_buffer* Leds, led_buffer_range Range, assembly Assembl } internal void -Pattern_Rotary(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +Pattern_RotaryOptions(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 BGColor, v4 FGColor) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; - Time = Time * BLState->PatternSpeed; + DEBUG_TRACK_FUNCTION; - gs_random_series Random = InitRandomSeries(24601); + gs_random_series Random = InitRandomSeries((u32)(24601 * (Assembly.Center.x + 1.032f))); #define SphereCount 32 v3 SphereCenter[SphereCount]; - r32 MaxHeightOffset = 150; + r32 G = RemapR32(Granularity, 1, 5, .75f, 2); + r32 MaxHeightOffset = 250; r32 MaxSpeed = 10; - r32 SphereRadius = 2.0f; + r32 SphereRotationRadius = 3.0f; + r32 SphereRadius = 2.0f / G; for (u32 i = 0; i < SphereCount; i++) { - r32 SphereSeedA = NextRandomBilateral(&Random); + r32 SphereSeedA = NextRandomUnilateral(&Random); + SphereSeedA = PowR32(SphereSeedA, 2); r32 SphereSeedB = NextRandomBilateral(&Random); r32 SphereSpeed = NextRandomUnilateral(&Random) * MaxSpeed; r32 SphereTime = Time + SphereSpeed; - r32 HeightOffset = SphereTime + (SphereSeedA * MaxHeightOffset); + r32 HeightOffset = 150 - (SphereSeedA * MaxHeightOffset); r32 RotationOffset = SphereTime + SphereSeedB * TauR32; r32 SphereRotationDir = NextRandomBilateral(&Random) < 0 ? -1 : 1; v3 SpherePosOffset = v3{ - SinR32(RotationOffset * SphereRotationDir) * (SphereRadius * 2), + SinR32(RotationOffset * SphereRotationDir) * (SphereRotationRadius * 2), HeightOffset, - CosR32(RotationOffset * SphereRotationDir) * (SphereRadius * 2) + CosR32(RotationOffset * SphereRotationDir) * (SphereRotationRadius * 2) }; SphereCenter[i] = Assembly.Center + SpherePosOffset; } @@ -455,17 +479,27 @@ Pattern_Rotary(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Dist = Min(Dist, SphereSDF); } - v4 C = BlackV4; + v4 C = BGColor; if (Dist <= 1) { r32 Brightness = Clamp01(SphereRadius - Dist); - C = WhiteV4 * Brightness; + C = V4Lerp(Brightness, BGColor, FGColor); } Leds->Colors[LedIndex] = V4ToRGBPixel(C); } } +internal void +Pattern_Rotary(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + DEBUG_TRACK_FUNCTION; + + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + Time = Time * BLState->PatternSpeed; + Pattern_RotaryOptions(Leds, Range, Assembly, Time, Transient, UserData, 2, BlackV4, WhiteV4); +} + internal void Pattern_AllOnMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { @@ -480,6 +514,8 @@ Pattern_AllOnMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r internal void Pattern_BulbMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; Time = Time * BLState->PatternSpeed; @@ -571,26 +607,47 @@ GenVerticalLeaves(v3 P, r32 Time, v4 C0, v4 C1, v4 C2) return R; } -internal r32 -GenLiquidBands(v3 P, r32 Offset, r32 Time) +internal void +AddIn_WavesPattern(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 C0, v4 C1, v4 C2) { - r32 Width = 30; - r32 VAcc = 0; - for (u32 i = 1; i < 3; i++) - { - v3 P0 = v3{P.x + (23.124f * i), 0, P.z - (-12.34f * i)}; - - r32 Y = (P.y - Offset); - r32 S = Fbm3D(P0 * .005f, Time) * 250; - S += ModR32((Time * 100) - (150 * i), 400); - - r32 V = (Width - Abs(Y - S)) / Width; - V = Clamp01(V); - - VAcc += V; - } + DEBUG_TRACK_FUNCTION; - return VAcc; + v4 C2P = C2 * 255; + + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + v3 P = Leds->Positions[LedIndex].xyz; + + v4 C = v4{ + (r32)Leds->Colors[LedIndex].R, + (r32)Leds->Colors[LedIndex].G, + (r32)Leds->Colors[LedIndex].B, + 1 + }; + + r32 Offset = -250; + r32 Width = 30; + r32 Bands = 0; + for (u32 i = 1; i <= 1; i++) + { + P.x = FloorR32(P.x); + P.z = FloorR32(P.z); + + v3 P0 = v3{P.x + (23.124f * i), 0, P.z - (-12.34f * i) + Time}; + r32 S = Fbm3D(P0 * .005f, Time) * 250; + S += ModR32((Time * 100) - (150 * i), 400); + + r32 Y = (P.y - Offset); + r32 V = (Width - Abs(Y - S)) / Width; + V = Clamp01(V); + + Bands += V; + } + + C = V4Lerp(Bands, C * .5f, C2P); + + Leds->Colors[LedIndex] = pixel{(u8)C.r, (u8)C.g, (u8)C.b}; + } } internal r32 @@ -618,33 +675,69 @@ GenDotBands(v3 P, r32 Time) internal void Pattern_VoicePattern(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; - Time = Time * BLState->PatternSpeed; + DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); v4 C0 = RGBFromPhraseHue(Hue.Hue0); v4 C1 = RGBFromPhraseHue(Hue.Hue1); v4 C2 = RGBFromPhraseHue(Hue.Hue2); - for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + Time = Time * BLState->PatternSpeed * Hue.Speed;; + + switch (Hue.Pattern) { - v3 P = Leds->Positions[LedIndex].xyz; + case HuePattern_Wavy: + { + Pattern_WavyOptions(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, C0, C1, C0); + }break; - v4 C = {}; - C += GenPatchyColor(P, Time, C0, C2, {}); - //C = GenVerticalLeaves((P - Assembly.Center) + v3{0, 150, 0}, Time, C0, C1, C2); - //r32 Bands = GenLiquidBands(P, -250, Time); - //C = V4Lerp(Bands, C * .5f, C1); + default: + { + Pattern_PatchyOptions(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, C0, C1, C2); + }break; + } + + +} + +internal void +Pattern_VoiceAddIns(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + DEBUG_TRACK_FUNCTION; + + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; + phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); + v4 C0 = RGBFromPhraseHue(Hue.Hue0); + v4 C1 = RGBFromPhraseHue(Hue.Hue1); + v4 C2 = RGBFromPhraseHue(Hue.Hue2); + + Time = Time * BLState->PatternSpeed * Hue.Speed;; + + switch (Hue.AddIn) + { + case AddIn_Rotary: + { + Pattern_RotaryOptions(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, BlackV4, C2); + }break; - //C = WhiteV4 * GenDotBands(P - Assembly.Center, Time); + case AddIn_Waves: + { + AddIn_WavesPattern(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, C0, C1, C2); + }break; - Leds->Colors[LedIndex] = V4ToRGBPixel(C); + case AddIn_None: + default: + { + }break; } } internal void Pattern_StemSolid(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; pixel WhiteMask = V4ToRGBPixel(WhiteV4); @@ -665,6 +758,8 @@ Pattern_StemSolid(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r internal void Pattern_PrimaryHue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; Time = Time * BLState->PatternSpeed; @@ -683,6 +778,8 @@ Pattern_PrimaryHue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, internal void Pattern_GrowFadeMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; Time = Time * BLState->PatternSpeed; @@ -718,6 +815,8 @@ Pattern_GrowFadeMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly internal void Pattern_RainbowLoadingBar(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { + DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; Time = Time * BLState->PatternSpeed; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index d9ed8bb..295a47d 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -214,6 +214,7 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_VoiceAddIns, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_StemSolid, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED); @@ -399,6 +400,10 @@ BlumenLumen_CustomInit(app_state* State, context Context) BLState->StandardPatternHues.Hue0.Flags = Hue_Value; BLState->StandardPatternHues.Hue1.Flags = Hue_Value; BLState->StandardPatternHues.Hue2.Flags = Hue_Value; + BLState->StandardPatternHues.Granularity = 1; + BLState->StandardPatternHues.Speed = 1; + BLState->StandardPatternHues.AddIn = AddIn_Rotary; + BLState->StandardPatternHues.Pattern = HuePattern_Wavy; BlumenLumen_AppendBootupLog(State, BLState, Context); return Result; @@ -423,6 +428,38 @@ BlumenLumen_UpdateMotorState(blumen_lumen_state* BLState, motor_status_packet Mo BLState->ShouldUpdateLog = true; } +internal void +BlumenLumen_ApplyNextHotHue(blumen_lumen_state* BLState, context Context, gs_string* DebugStr, app_state* State) +{ + // if we are in standard color mode, shift all flowers to the new color + // otherwise, only shift the next flower in the sequence to the new color + phrase_hue NewHue = BLState->NextHotHue; + + PrintF(DebugStr, "Switching To: %S\n", NewHue.Phrase); + NullTerminate(DebugStr); + OutputDebugString(DebugStr->Str); + + + if (BLState->PatternMode == BlumenPattern_Standard) + { + BlumenLumen_SetNextHue(BLState, 0, NewHue); + BlumenLumen_SetNextHue(BLState, 1, NewHue); + BlumenLumen_SetNextHue(BLState, 2, NewHue); + } + else + { + u32 AssemblyIdx = BLState->LastAssemblyColorSet; + BlumenLumen_SetNextHue(BLState, AssemblyIdx, NewHue); + BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; + } + + BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + BLState->TimeLastSetToVoiceMode = Context.SystemTime_Current; + BLState->LastHuePhrase = NewHue; + BLState->ShouldUpdateLog = true; + BLState->InPhraseReceptionMode = false; +} + internal void BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { @@ -447,7 +484,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) u64 NameHash = HashDJB2ToU32(Mic.AnimationFileName); u32 NameLen = CStringLength(Mic.AnimationFileName); - phrase_hue NewHue = PhraseHueMap_Get(BLState->PhraseHueMap, NameHash); + phrase_hue NewHue = PhraseHueMap_Find(BLState->PhraseHueMap, NameHash); if (NewHue.Phrase.Length > 0) { bool IsLonger = (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length); @@ -510,36 +547,12 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current); if (SecondsSincePhraseBegan > PhrasePriorityMessageGroupingTime) { - // if we are in standard color mode, shift all flowers to the new color - // otherwise, only shift the next flower in the sequence to the new color - phrase_hue NewHue = BLState->NextHotHue; - - PrintF(&DebugStr, "Switching To: %S\n", NewHue.Phrase); - NullTerminate(&DebugStr); - OutputDebugString(DebugStr.Str); - - - if (BLState->PatternMode == BlumenPattern_Standard) - { - BLState->AssemblyColors[0] = NewHue; - BLState->AssemblyColors[1] = NewHue; - BLState->AssemblyColors[2] = NewHue; - } - else - { - u32 AssemblyIdx = BLState->LastAssemblyColorSet; - BLState->AssemblyColors[AssemblyIdx] = NewHue; - BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; - } - - BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); - BLState->TimeLastSetToVoiceMode = Context->SystemTime_Current; - BLState->LastHuePhrase = NewHue; - BLState->ShouldUpdateLog = true; - BLState->InPhraseReceptionMode = false; + BlumenLumen_ApplyNextHotHue(BLState, *Context, &DebugStr, State); } } + BlumenLumen_AdvanceHueFade(BLState, *Context); + // Update next frames Hues r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); AnimTime = (r32)Context->TotalTime; @@ -737,110 +750,131 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; ui_interface* I = &State->Interface; - // NOTE(PS): Motors + motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; + + for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) { - motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; - - for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) + gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); + ui_BeginRow(I, 5); { - gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); - ui_BeginRow(I, 5); + ui_Label(I, Label); + + bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; + if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) { - ui_Label(I, Label); - - bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; - if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; - } - bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; - if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; - } - bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; - if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; - } - bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; - if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; - } + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; } + bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; + if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; + } + bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; + if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; + } + bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; + if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; + } + } + ui_EndRow(I); + } + BLState->DEBUG_PendingMotorPacket = PendingPacket; + + if (ui_Button(I, MakeString("Send Motor Packet"))) + { + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + + DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); + } + + motor_packet MotorPos = BLState->LastKnownMotorState; + ui_Label(I, MakeString("Current Motor Positions")); + { + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + ui_BeginRow(I, 2); + gs_string MotorStr = PushStringF(State->Transient, 32, + "Motor %d", + i); + ui_Label(I, MotorStr); + + gs_string StateStr = {}; + switch (MotorPos.FlowerPositions[i]) + { + case MotorState_Closed: { + StateStr = MakeString("Closed"); + } break; + case MotorState_HalfOpen: { + StateStr = MakeString("Half Open"); + } break; + case MotorState_MostlyOpen: { + StateStr = MakeString("Mostly Open"); + } break; + case MotorState_Open: { + StateStr = MakeString("Open"); + } break; + } + + ui_Label(I, StateStr); ui_EndRow(I); } - BLState->DEBUG_PendingMotorPacket = PendingPacket; + } + + BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); + + ui_Label(I, MakeString("Set Internal Motor State:")); + if (ui_Button(I, MakeString("Closed"))) + { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Closed; + Motor.Pos.FlowerPositions[1] = MotorState_Closed; + Motor.Pos.FlowerPositions[2] = MotorState_Closed; + Motor.Temperature = 16; - if (ui_Button(I, MakeString("Send Motor Packet"))) - { - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - - DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); - } + BlumenLumen_UpdateMotorState(BLState, Motor, Context); + } + if (ui_Button(I, MakeString("Open"))) + { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Open; + Motor.Pos.FlowerPositions[1] = MotorState_Open; + Motor.Pos.FlowerPositions[2] = MotorState_Open; + Motor.Temperature = 16; - motor_packet MotorPos = BLState->LastKnownMotorState; - ui_Label(I, MakeString("Current Motor Positions")); + BlumenLumen_UpdateMotorState(BLState, Motor, Context); + } + + if (ui_BeginLabeledDropdown(I, MakeString("Phrase"), MakeString(BLState->PendingPhrase.Phrase))) + { + u32 ListCount = BLState->PhraseHueMap.Count; + ui_BeginList(I, MakeString("Phrase List"), 5, ListCount); + for (u32 i = 0; i < ListCount; i++) { - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + gs_string Str = MakeString(BLState->PhraseHueMap.Phrases[i]); + if (ui_Button(I, Str)) { - ui_BeginRow(I, 2); - gs_string MotorStr = PushStringF(State->Transient, 32, - "Motor %d", - i); - ui_Label(I, MotorStr); - - gs_string StateStr = {}; - switch (MotorPos.FlowerPositions[i]) - { - case MotorState_Closed: { - StateStr = MakeString("Closed"); - } break; - case MotorState_HalfOpen: { - StateStr = MakeString("Half Open"); - } break; - case MotorState_MostlyOpen: { - StateStr = MakeString("Mostly Open"); - } break; - case MotorState_Open: { - StateStr = MakeString("Open"); - } break; - } - - ui_Label(I, StateStr); - ui_EndRow(I); + BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i); } } - - BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); - - ui_Label(I, MakeString("Set Internal Motor State:")); - if (ui_Button(I, MakeString("Closed"))) - { - motor_status_packet Motor = {}; - Motor.Pos.FlowerPositions[0] = MotorState_Closed; - Motor.Pos.FlowerPositions[1] = MotorState_Closed; - Motor.Pos.FlowerPositions[2] = MotorState_Closed; - Motor.Temperature = 16; - - BlumenLumen_UpdateMotorState(BLState, Motor, Context); - } - if (ui_Button(I, MakeString("Open"))) - { - motor_status_packet Motor = {}; - Motor.Pos.FlowerPositions[0] = MotorState_Open; - Motor.Pos.FlowerPositions[1] = MotorState_Open; - Motor.Pos.FlowerPositions[2] = MotorState_Open; - Motor.Temperature = 16; - - BlumenLumen_UpdateMotorState(BLState, Motor, Context); - } + ui_EndList(I); } + ui_EndLabeledDropdown(I); + if (ui_Button(I, MakeString("Say Phrase"))) + { + gs_string DebugStr = PushString(State->Transient, 256); + BLState->NextHotHue = BLState->PendingPhrase; + BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State); + } + + InterfaceAssert(I->PerFrameMemory); } US_CUSTOM_CLEANUP(BlumenLumen_CustomCleanup) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 8b381cf..a22cb40 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -157,6 +157,8 @@ struct blumen_lumen_state system_time LastSendTime; phrase_hue StandardPatternHues; + r32 AssemblyColorsTransitionTimeLeft[BL_FLOWER_COUNT]; + phrase_hue NextAssemblyColors[BL_FLOWER_COUNT]; phrase_hue AssemblyColors[BL_FLOWER_COUNT]; u32 LastAssemblyColorSet; @@ -188,10 +190,41 @@ struct blumen_lumen_state bool DEBUG_IgnoreWeatherDimmingLeds; bool ShouldUpdateLog; + + phrase_hue PendingPhrase; }; #include "message_queue.cpp" +internal void +BlumenLumen_SetNextHue(blumen_lumen_state* BLState, u32 AssemblyIndex, phrase_hue Hue) +{ +#if 1 + BLState->NextAssemblyColors[AssemblyIndex] = Hue; + BLState->AssemblyColorsTransitionTimeLeft[AssemblyIndex] = PhraseHueFadeInDuration; +#else + BLState->AssemblyColors[AssemblyIndex] = Hue; +#endif +} + +internal void +BlumenLumen_AdvanceHueFade(blumen_lumen_state* BLState, context Context) +{ + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + r32 T = BLState->AssemblyColorsTransitionTimeLeft[i]; + if (T > 0) + { + T -= Context.DeltaTime; + if (T <= 0) + { + BLState->AssemblyColors[i] = BLState->NextAssemblyColors[i]; + } + BLState->AssemblyColorsTransitionTimeLeft[i] = T; + } + } +} + internal phrase_hue BlumenLumen_GetCurrentHue(blumen_lumen_state* BLState, assembly Assembly) { @@ -206,7 +239,15 @@ BlumenLumen_GetCurrentHue(blumen_lumen_state* BLState, assembly Assembly) case BlumenPattern_VoiceCommand: { - Result = BLState->AssemblyColors[Assembly.AssemblyIndex % 3]; + u32 i = Assembly.AssemblyIndex % 3; + r32 T = BLState->AssemblyColorsTransitionTimeLeft[i]; + if (T > 0) + { + T = Clamp(T / PhraseHueFadeInDuration, 0, 1); + Result = LerpPhraseHue(T, BLState->NextAssemblyColors[i], BLState->AssemblyColors[i]); + } else { + Result = BLState->AssemblyColors[i]; + } }break; } diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 46f69b8..b610b2b 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -105,6 +105,9 @@ r32 GlobalAnimSpeed = 1.0f; // activated mode r32 GlobalAnimTransitionSpeed = 5.0f; +// how long it takes to fade from the old voice hue to the new one +r32 PhraseHueFadeInDuration = 2.0f; // seconds + r64 PhrasePriorityMessageGroupingTime = 1.0f; // How often should Lumenarium send its status to the python server? diff --git a/src/app/ss_blumen_lumen/gfx_math.h b/src/app/ss_blumen_lumen/gfx_math.h index 8b1acc8..34b667d 100644 --- a/src/app/ss_blumen_lumen/gfx_math.h +++ b/src/app/ss_blumen_lumen/gfx_math.h @@ -281,13 +281,14 @@ Fbm3D(v3 P, r32 T) r32 F = 0.0; F += 0.500000f * Noise3D(Pp + Tt); Pp = Pp * 2.02; - F += 0.031250f * Noise3D(Pp); Pp = Pp * 2.01; - F += 0.250000f * Noise3D(Pp - Tt); Pp = Pp * 2.03; + //F += 0.031250f * Noise3D(Pp); Pp = Pp * 2.01; + F += 0.300000f * Noise3D(Pp - Tt); Pp = Pp * 2.03; F += 0.125000f * Noise3D(Pp); Pp = Pp * 2.01; F += 0.062500f * Noise3D(Pp + Tt); Pp = Pp * 2.04; - F += 0.015625f * Noise3D(Pp + Tv); + //F += 0.015625f * Noise3D(Pp + Tv); + r32 D = 0.9875f; - F = F / 0.984375f; + F = F / D; return F; } diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h index 34ef475..91256d4 100644 --- a/src/app/ss_blumen_lumen/phrase_hue_map.h +++ b/src/app/ss_blumen_lumen/phrase_hue_map.h @@ -10,6 +10,19 @@ enum p_hue_flag Hue_Black = 2, }; +enum p_hue_pattern +{ + HuePattern_Patchy, + HuePattern_Wavy, +}; + +enum p_hue_add_in +{ + AddIn_None, + AddIn_Waves, + AddIn_Rotary, +}; + typedef struct p_hue { r64 Hue; @@ -25,6 +38,10 @@ typedef struct phrase_hue_map p_hue* Hue0; p_hue* Hue1; p_hue* Hue2; + u32* Gran; // granularity + r32* Speed; + u8* Pattern; + u8* AddIn; } phrase_hue_map; typedef struct phrase_hue @@ -34,8 +51,62 @@ typedef struct phrase_hue p_hue Hue0; p_hue Hue1; p_hue Hue2; + u32 Granularity; + r32 Speed; + u8 Pattern; + u8 AddIn; } phrase_hue; +internal p_hue +LerpPHue(r32 T, p_hue A, p_hue B) +{ + p_hue Result = {}; + + if (Abs(A.Hue - B.Hue) < 180.0f) + { + Result.Hue = LerpR64(T, A.Hue, B.Hue); + } + else + { + Result.Hue = LerpR64(T, A.Hue + 360.0f, B.Hue); + Result.Hue = ModR32(Result.Hue, 360.0f); + } + + if (T < 0.5f) + { + Result.Flags = A.Flags; + } else { + Result.Flags = B.Flags; + } + return Result; +} + +internal phrase_hue +LerpPhraseHue(r32 T, phrase_hue A, phrase_hue B) +{ + phrase_hue Result = {}; + Result.Hue0 = LerpPHue(T, A.Hue0, B.Hue0); + Result.Hue1 = LerpPHue(T, A.Hue1, B.Hue1); + Result.Hue2 = LerpPHue(T, A.Hue2, B.Hue2); + Result.Granularity = (u32)LerpR32(T, (r32)A.Granularity, (r32)B.Granularity); + Result.Speed = LerpR32(T, A.Speed, B.Speed); + + if (T < .5f) { + Result.Phrase = A.Phrase; + Result.PhraseHash = A.PhraseHash; + Result.Pattern = A.Pattern; + Result.AddIn = A.AddIn; + } + else { + Result.Phrase = B.Phrase; + Result.PhraseHash = B.PhraseHash; + Result.Pattern = B.Pattern; + Result.AddIn = B.AddIn; + } + + return Result; +} + internal p_hue CreateHueFromString(gs_const_string Str) { @@ -46,7 +117,13 @@ CreateHueFromString(gs_const_string Str) Result.Flags = Hue_White; } else { Result.Flags = Hue_Value; - Result.Hue = (r64)ParseFloat(Str); + parse_float_result Parsed = ValidateAndParseFloat(Str); + if (!Parsed.Success) + { + OutputDebugString("Failed to Parse CSV Float\n"); + Parsed.Value = 0.0; + } + Result.Hue = Parsed.Value; } return Result; } @@ -73,9 +150,13 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) Result.CountMax = Sheet.RowCount - 1; // we don't include the header row Result.Phrases = PushArray(Arena, gs_const_string, Result.CountMax); Result.PhraseHashes = PushArray(Arena, u64, Result.CountMax); - Result.Hue0 = PushArray(Arena, p_hue, Result.CountMax); - Result.Hue1 = PushArray(Arena, p_hue, Result.CountMax); - Result.Hue2 = PushArray(Arena, p_hue, Result.CountMax); + Result.Hue0 = PushArray(Arena, p_hue, Result.CountMax); + Result.Hue1 = PushArray(Arena, p_hue, Result.CountMax); + Result.Hue2 = PushArray(Arena, p_hue, Result.CountMax); + Result.Gran = PushArray(Arena, u32, Result.CountMax); + Result.Pattern = PushArray(Arena, u8, Result.CountMax); + Result.Speed = PushArray(Arena, r32, Result.CountMax); + Result.AddIn = PushArray(Arena, u8, Result.CountMax); s32 DestOffset = 0; for (u32 Row = 1; Row < Sheet.RowCount; Row++) @@ -86,10 +167,15 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) gs_const_string Phrase = CSVSheet_GetCell(Sheet, 0, Row); - gs_const_string Hue0Str = CSVSheet_GetCell(Sheet, 1, Row); - gs_const_string Hue1Str = CSVSheet_GetCell(Sheet, 2, Row); - gs_const_string Hue2Str = CSVSheet_GetCell(Sheet, 3, Row); - gs_const_string Homonyms = CSVSheet_GetCell(Sheet, 4, Row); + gs_const_string Hue0Str = CSVSheet_GetCell(Sheet, 1, Row); + gs_const_string Hue1Str = CSVSheet_GetCell(Sheet, 2, Row); + gs_const_string Hue2Str = CSVSheet_GetCell(Sheet, 3, Row); + gs_const_string Homonyms = CSVSheet_GetCell(Sheet, 4, Row); + gs_const_string Granularity = CSVSheet_GetCell(Sheet, 5, Row); + gs_const_string Speed = CSVSheet_GetCell(Sheet, 6, Row); + gs_const_string Pattern = CSVSheet_GetCell(Sheet, 7, Row); + gs_const_string AddIn = CSVSheet_GetCell(Sheet, 8, Row); + if (Phrase.Length == 0 || Hue0Str.Length == 0 || Hue1Str.Length == 0 || @@ -115,6 +201,36 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) Result.Hue0[Index] = CreateHueFromString(Hue0Str); Result.Hue1[Index] = CreateHueFromString(Hue1Str); Result.Hue2[Index] = CreateHueFromString(Hue2Str); + + parse_float_result ParsedSpeed = ValidateAndParseFloat(Speed); + if (!ParsedSpeed.Success) + { + ParsedSpeed.Value = 1.0; + } + Result.Speed[Index] = ParsedSpeed.Value; + + if (StringsEqual(Pattern, ConstString("wavy"))) + { + Result.Pattern[Index] = HuePattern_Wavy; + } else { + Result.Pattern[Index] = HuePattern_Patchy; + } + + if (StringsEqual(AddIn, ConstString("waves"))) + { + Result.AddIn[Index] = AddIn_Waves; + } else if (StringsEqual(AddIn, ConstString("rotary"))) { + Result.AddIn[Index] = AddIn_Rotary; + } else { + Result.AddIn[Index] = AddIn_None; + } + + parse_uint_result ParsedGranularity = ValidateAndParseUInt(Granularity); + if (!ParsedGranularity.Success) + { + ParsedGranularity.Value = 1; + } + Result.Gran[Index] = ParsedGranularity.Value; } Result.Count = Result.CountMax + DestOffset; @@ -123,24 +239,34 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) } internal phrase_hue -PhraseHueMap_Get(phrase_hue_map Map, u64 PhraseHash) +PhraseHueMap_Get(phrase_hue_map Map, u32 Index) +{ + Assert(Index < Map.Count); + phrase_hue Result = {}; + Result.Phrase = Map.Phrases[Index]; + Result.PhraseHash = Map.PhraseHashes[Index]; + Result.Hue0 = Map.Hue0[Index]; + Result.Hue1 = Map.Hue1[Index]; + Result.Hue2 = Map.Hue2[Index]; + Result.Granularity = Map.Gran[Index]; + Result.Speed = Map.Speed[Index]; + Result.AddIn = Map.AddIn[Index]; + Result.Pattern = Map.Pattern[Index]; + return Result; +} + +internal phrase_hue +PhraseHueMap_Find(phrase_hue_map Map, u64 PhraseHash) { phrase_hue Result = {}; - for (u32 i = 0; i < Map.Count; i++) { if (Map.PhraseHashes[i] == PhraseHash) { - Result.Phrase = Map.Phrases[i]; - Result.PhraseHash = Map.PhraseHashes[i]; - Result.Hue0 = Map.Hue0[i]; - Result.Hue1 = Map.Hue1[i]; - Result.Hue2 = Map.Hue2[i]; - + Result = PhraseHueMap_Get(Map, i); break; } } - return Result; } From 0c96f6bd19491725be506fe8b93aea049bfd4b8e Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 00:58:50 -1000 Subject: [PATCH 064/151] Added setting for turning leds off at certain times of day --- .../ambient_patterns/ambient_0.foldanim | 92 ------------------- src/app/ss_blumen_lumen/blumen_lumen.cpp | 19 ++++ .../ss_blumen_lumen/blumen_lumen_settings.h | 14 ++- 3 files changed, 31 insertions(+), 94 deletions(-) delete mode 100644 app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim deleted file mode 100644 index b965ed5..0000000 --- a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_0.foldanim +++ /dev/null @@ -1,92 +0,0 @@ -lumenarium_animation_file; -animation_name: "ambient_0"; -layers_count: 2; -blocks_count: 9; -playable_range:{ - min: 0; - max: 1000; -}; -layers:{ - layer:{ - name: "Color"; - blend: "Add"; - }; - layer:{ - name: "Mask"; - blend: "Multiply"; - }; -}; -blocks:{ - block:{ - frame_range:{ - min: 88; - max: 315; - }; - layer_index: 0; - animation_name: "Pattern_Rotary"; - }; - block:{ - frame_range:{ - min: 156; - max: 429; - }; - layer_index: 1; - animation_name: "Pattern_LeafyPatchy"; - }; - block:{ - frame_range:{ - min: 403; - max: 675; - }; - layer_index: 1; - animation_name: "Pattern_Patchy"; - }; - block:{ - frame_range:{ - min: 643; - max: 997; - }; - layer_index: 1; - animation_name: "Pattern_LeafyPatchy"; - }; - block:{ - frame_range:{ - min: 253; - max: 571; - }; - layer_index: 0; - animation_name: "Pattern_VerticalLines"; - }; - block:{ - frame_range:{ - min: 522; - max: 772; - }; - layer_index: 0; - animation_name: "Pattern_Rotary"; - }; - block:{ - frame_range:{ - min: 737; - max: 998; - }; - layer_index: 0; - animation_name: "Pattern_VerticalLines"; - }; - block:{ - frame_range:{ - min: 0; - max: 117; - }; - layer_index: 0; - animation_name: "Pattern_VerticalLines"; - }; - block:{ - frame_range:{ - min: 0; - max: 179; - }; - layer_index: 1; - animation_name: "Pattern_StemSolid"; - }; -}; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 295a47d..be23b28 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -693,6 +693,25 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } + bool TimelineShouldAdvance = false; + r32 OverrideBrightness = 0.0f; + for (u32 i = 0; i < LedOnTimesCount; i++) + { + time_range Range = LedOnTimes[i]; + bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); + if (CurrTimeInRange) + { + // If we're in one of the specified time ranges, + // play animations and set brightness + OverrideBrightness = BLState->BrightnessPercent; + TimelineShouldAdvance = State->AnimationSystem.TimelineShouldAdvance; + break; + } + } + State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; + BLState->BrightnessPercent = OverrideBrightness; + + // Dim the leds based on temp data if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) { diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index b610b2b..129a54f 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -27,17 +27,27 @@ gs_const_string AmbientPatternFolder = ConstString("data/blumen_animations/ambie gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_responses/*.foldanim"); // The times of day when the motors should be open. +// +// @TimeFormat: documentation follows // these are in the format { Start_Hour, Start_Minute, End_Hour, End_Minute } // Hours are in the range 0-23 inclusive // Minutes are in the range 0-59 inclusive +// // NOTE: There is no need to modify the MotorOpenTimesCount variable - // it is a compile time constant that gets calculated automatically global time_range MotorOpenTimes[] = { - { 12, 45, 17, 45 }, - { 19, 00, 23, 00 }, + { 12, 45, 17, 45 }, // 12:45pm to 5:45pm + { 19, 00, 23, 00 }, // 7:00pm to 11:00pm }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit +// The times of day when the leds should be on +// Search for @TimeFormat to find documentation +global time_range LedOnTimes[] = { + { 00, 00, 23, 59 }, // literally always +}; +global u32 LedOnTimesCount = CArrayLength(LedOnTimes); // do not edit + // How long it takes to fade from the default pattern to the // voice activated pattern r32 VoiceCommandFadeDuration = 1.0f; // in seconds From ee1163be952718eec06166ccf500b2708dcda8fe Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 01:00:00 -1000 Subject: [PATCH 065/151] Ambient Patterns --- .../ambient_patterns/fishy_0.foldanim | 48 +++++++++++++++++++ .../patchy_loading_bar.foldanim | 36 ++++++++++++++ .../ambient_patterns/primary_hue_0.foldanim | 28 +++++++++++ .../ambient_patterns/rainbow_0.foldanim | 28 +++++++++++ .../rainbow_loading_bar.foldanim | 28 +++++++++++ .../ambient_patterns/wavy_0.foldanim | 28 +++++++++++ 6 files changed, 196 insertions(+) create mode 100644 app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim create mode 100644 app_run_tree/data/blumen_animations/ambient_patterns/patchy_loading_bar.foldanim create mode 100644 app_run_tree/data/blumen_animations/ambient_patterns/primary_hue_0.foldanim create mode 100644 app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim create mode 100644 app_run_tree/data/blumen_animations/ambient_patterns/rainbow_loading_bar.foldanim create mode 100644 app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim new file mode 100644 index 0000000..7119bbc --- /dev/null +++ b/app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim @@ -0,0 +1,48 @@ +lumenarium_animation_file; +animation_name: "fishy_0"; +layers_count: 3; +blocks_count: 3; +playable_range:{ + min: 0; + max: 3600; +}; +layers:{ + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; + layer:{ + name: "Color"; + blend: "Multiply"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 0; + max: 3600; + }; + layer_index: 2; + animation_name: "Pattern_HueShift"; + }; + block:{ + frame_range:{ + min: 0; + max: 3600; + }; + layer_index: 1; + animation_name: "Pattern_StemSolid"; + }; + block:{ + frame_range:{ + min: 0; + max: 3600; + }; + layer_index: 0; + animation_name: "Pattern_Rotary"; + }; +}; diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/patchy_loading_bar.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/patchy_loading_bar.foldanim new file mode 100644 index 0000000..1fe290a --- /dev/null +++ b/app_run_tree/data/blumen_animations/ambient_patterns/patchy_loading_bar.foldanim @@ -0,0 +1,36 @@ +lumenarium_animation_file; +animation_name: "patchy_loading_bar_0"; +layers_count: 2; +blocks_count: 2; +playable_range:{ + min: 0; + max: 3350; +}; +layers:{ + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; + layer:{ + name: "[New Layer]"; + blend: "Multiply"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 0; + max: 3600; + }; + layer_index: 1; + animation_name: "Pattern_Patchy"; + }; + block:{ + frame_range:{ + min: 0; + max: 3600; + }; + layer_index: 0; + animation_name: "Pattern_GrowFadeMask"; + }; +}; diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/primary_hue_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/primary_hue_0.foldanim new file mode 100644 index 0000000..66670b8 --- /dev/null +++ b/app_run_tree/data/blumen_animations/ambient_patterns/primary_hue_0.foldanim @@ -0,0 +1,28 @@ +lumenarium_animation_file; +animation_name: "primary_hue_0"; +layers_count: 2; +blocks_count: 1; +playable_range:{ + min: 0; + max: 360; +}; +layers:{ + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 0; + max: 36000; + }; + layer_index: 1; + animation_name: "Pattern_PrimaryHue"; + }; +}; diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim new file mode 100644 index 0000000..ab6f4e3 --- /dev/null +++ b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim @@ -0,0 +1,28 @@ +lumenarium_animation_file; +animation_name: "rainbow"; +layers_count: 2; +blocks_count: 1; +playable_range:{ + min: 0; + max: 360; +}; +layers:{ + layer:{ + name: "Mask"; + blend: "Add"; + }; + layer:{ + name: "Color"; + blend: "Add"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 0; + max: 360; + }; + layer_index: 1; + animation_name: "Pattern_Rainbow"; + }; +}; diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_loading_bar.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_loading_bar.foldanim new file mode 100644 index 0000000..6f6f0c6 --- /dev/null +++ b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_loading_bar.foldanim @@ -0,0 +1,28 @@ +lumenarium_animation_file; +animation_name: "rainbow_loading_bar_0"; +layers_count: 2; +blocks_count: 1; +playable_range:{ + min: 0; + max: 9000; +}; +layers:{ + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 0; + max: 9000; + }; + layer_index: 1; + animation_name: "Pattern_RainbowLoadingBar"; + }; +}; diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim new file mode 100644 index 0000000..2ed33bc --- /dev/null +++ b/app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim @@ -0,0 +1,28 @@ +lumenarium_animation_file; +animation_name: "wavy_0"; +layers_count: 2; +blocks_count: 1; +playable_range:{ + min: 0; + max: 7200; +}; +layers:{ + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; + layer:{ + name: "Color"; + blend: "Add"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 0; + max: 7200; + }; + layer_index: 1; + animation_name: "Pattern_Wavy"; + }; +}; From 7e1e22ed5a402b7cae86206ceadb81d748d23c16 Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 01:48:29 -1000 Subject: [PATCH 066/151] Created a panel for the Awaken sequence --- src/app/patterns/blumen_patterns.h | 6 + src/app/ss_blumen_lumen/blumen_lumen.cpp | 357 ++++++++++++++--------- src/app/ss_blumen_lumen/blumen_lumen.h | 22 ++ 3 files changed, 255 insertions(+), 130 deletions(-) diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 732192c..534d78e 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -7,6 +7,12 @@ #define FLOWER_COLORS_COUNT 12 +internal void +Pattern_None(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + // just here so you can fade in from black +} + internal void Pattern_AltBloomMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index be23b28..ab31797 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -201,6 +201,7 @@ BlumenLumen_LoadPatterns(app_state* State) } Patterns->Count = 0; + Patterns_PushPattern(Patterns, Pattern_None, PATTERN_SINGLETHREADED); Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_Rainbow, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED); @@ -307,6 +308,7 @@ BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Con { case BlumenPattern_Standard: { PatternMode = "Standard"; } break; case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break; + case BlumenPattern_NoControl: { PatternMode = "No Control: Someone's doing the Awaken sequence!"; } break; } AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode); @@ -394,6 +396,15 @@ BlumenLumen_CustomInit(app_state* State, context Context) AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + + BLState->AwakenHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/blumen_animations/awaken.foldanim")); + BLState->OffAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/blumen_animations/off_anim.foldanim")); #endif State->AnimationSystem.TimelineShouldAdvance = true; @@ -587,7 +598,8 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } // Open / Close the Motor - if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue)) + if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue) && + !BLState->IgnoreTimeOfDay_MotorState) { for (u32 i = 0; i < MotorOpenTimesCount; i++) { @@ -693,24 +705,26 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } - bool TimelineShouldAdvance = false; - r32 OverrideBrightness = 0.0f; - for (u32 i = 0; i < LedOnTimesCount; i++) + if (!BLState->IgnoreTimeOfDay_LedDimming) { - time_range Range = LedOnTimes[i]; - bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); - if (CurrTimeInRange) + bool TimelineShouldAdvance = false; + r32 OverrideBrightness = 0.0f; + for (u32 i = 0; i < LedOnTimesCount; i++) { - // If we're in one of the specified time ranges, - // play animations and set brightness - OverrideBrightness = BLState->BrightnessPercent; - TimelineShouldAdvance = State->AnimationSystem.TimelineShouldAdvance; - break; + time_range Range = LedOnTimes[i]; + bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); + if (CurrTimeInRange) + { + // If we're in one of the specified time ranges, + // play animations and set brightness + OverrideBrightness = BLState->BrightnessPercent; + TimelineShouldAdvance = State->AnimationSystem.TimelineShouldAdvance; + break; + } } + State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; + BLState->BrightnessPercent = OverrideBrightness; } - State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; - BLState->BrightnessPercent = OverrideBrightness; - // Dim the leds based on temp data if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) @@ -769,131 +783,214 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; ui_interface* I = &State->Interface; - motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; - - for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) + ui_BeginRow(I, BlumenDebug_Count); + for (u32 i = 0; i < BlumenDebug_Count; i++) { - gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); - ui_BeginRow(I, 5); + if (ui_Button(I, MakeString(BlDebugUiModeStrings[i]))) { - ui_Label(I, Label); - - bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; - if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; - } - bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; - if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; - } - bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; - if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; - } - bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; - if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; - } - } - ui_EndRow(I); - } - BLState->DEBUG_PendingMotorPacket = PendingPacket; - - if (ui_Button(I, MakeString("Send Motor Packet"))) - { - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - - DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); - } - - motor_packet MotorPos = BLState->LastKnownMotorState; - ui_Label(I, MakeString("Current Motor Positions")); - { - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) - { - ui_BeginRow(I, 2); - gs_string MotorStr = PushStringF(State->Transient, 32, - "Motor %d", - i); - ui_Label(I, MotorStr); - - gs_string StateStr = {}; - switch (MotorPos.FlowerPositions[i]) - { - case MotorState_Closed: { - StateStr = MakeString("Closed"); - } break; - case MotorState_HalfOpen: { - StateStr = MakeString("Half Open"); - } break; - case MotorState_MostlyOpen: { - StateStr = MakeString("Mostly Open"); - } break; - case MotorState_Open: { - StateStr = MakeString("Open"); - } break; - } - - ui_Label(I, StateStr); - ui_EndRow(I); + BLState->DebugMode = (bl_debug_ui_mode)i; } } + ui_EndRow(I); - BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); - - ui_Label(I, MakeString("Set Internal Motor State:")); - if (ui_Button(I, MakeString("Closed"))) + switch (BLState->DebugMode) { - motor_status_packet Motor = {}; - Motor.Pos.FlowerPositions[0] = MotorState_Closed; - Motor.Pos.FlowerPositions[1] = MotorState_Closed; - Motor.Pos.FlowerPositions[2] = MotorState_Closed; - Motor.Temperature = 16; - - BlumenLumen_UpdateMotorState(BLState, Motor, Context); - } - if (ui_Button(I, MakeString("Open"))) - { - motor_status_packet Motor = {}; - Motor.Pos.FlowerPositions[0] = MotorState_Open; - Motor.Pos.FlowerPositions[1] = MotorState_Open; - Motor.Pos.FlowerPositions[2] = MotorState_Open; - Motor.Temperature = 16; - - BlumenLumen_UpdateMotorState(BLState, Motor, Context); - } - - if (ui_BeginLabeledDropdown(I, MakeString("Phrase"), MakeString(BLState->PendingPhrase.Phrase))) - { - u32 ListCount = BLState->PhraseHueMap.Count; - ui_BeginList(I, MakeString("Phrase List"), 5, ListCount); - for (u32 i = 0; i < ListCount; i++) + case BlumenDebug_Motors: { - gs_string Str = MakeString(BLState->PhraseHueMap.Phrases[i]); - if (ui_Button(I, Str)) + motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; + + BLState->IgnoreTimeOfDay_MotorState = ui_ToggleText(I, MakeString("Motors Ignore Time Limit"), BLState->IgnoreTimeOfDay_MotorState); + + for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) { - BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i); + gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); + ui_BeginRow(I, 5); + { + ui_Label(I, Label); + + bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; + if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; + } + bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; + if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; + } + bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; + if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; + } + bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; + if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; + } + } + ui_EndRow(I); } - } - ui_EndList(I); + BLState->DEBUG_PendingMotorPacket = PendingPacket; + + if (ui_Button(I, MakeString("Send Motor Packet"))) + { + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); + + } + + motor_packet MotorPos = BLState->LastKnownMotorState; + ui_Label(I, MakeString("Current Motor Positions")); + { + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + ui_BeginRow(I, 2); + gs_string MotorStr = PushStringF(State->Transient, 32, + "Motor %d", + i); + ui_Label(I, MotorStr); + + gs_string StateStr = {}; + switch (MotorPos.FlowerPositions[i]) + { + case MotorState_Closed: { + StateStr = MakeString("Closed"); + } break; + case MotorState_HalfOpen: { + StateStr = MakeString("Half Open"); + } break; + case MotorState_MostlyOpen: { + StateStr = MakeString("Mostly Open"); + } break; + case MotorState_Open: { + StateStr = MakeString("Open"); + } break; + } + + ui_Label(I, StateStr); + ui_EndRow(I); + } + } + + ui_Label(I, MakeString("Set Internal Motor State:")); + if (ui_Button(I, MakeString("Closed"))) + { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Closed; + Motor.Pos.FlowerPositions[1] = MotorState_Closed; + Motor.Pos.FlowerPositions[2] = MotorState_Closed; + Motor.Temperature = 16; + + BlumenLumen_UpdateMotorState(BLState, Motor, Context); + } + if (ui_Button(I, MakeString("Open"))) + { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Open; + Motor.Pos.FlowerPositions[1] = MotorState_Open; + Motor.Pos.FlowerPositions[2] = MotorState_Open; + Motor.Temperature = 16; + + BlumenLumen_UpdateMotorState(BLState, Motor, Context); + } + } break; + + case BlumenDebug_Leds: + { + BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); + + BLState->IgnoreTimeOfDay_LedDimming = ui_ToggleText(I, MakeString("Leds Ignore Time Limit"), BLState->IgnoreTimeOfDay_LedDimming); + + if (ui_BeginLabeledDropdown(I, MakeString("Phrase"), MakeString(BLState->PendingPhrase.Phrase))) + { + u32 ListCount = BLState->PhraseHueMap.Count; + ui_BeginList(I, MakeString("Phrase List"), 5, ListCount); + for (u32 i = 0; i < ListCount; i++) + { + gs_string Str = MakeString(BLState->PhraseHueMap.Phrases[i]); + if (ui_Button(I, Str)) + { + BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i); + } + } + ui_EndList(I); + } + ui_EndLabeledDropdown(I); + if (ui_Button(I, MakeString("Say Phrase"))) + { + gs_string DebugStr = PushString(State->Transient, 256); + BLState->NextHotHue = BLState->PendingPhrase; + BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State); + } + + InterfaceAssert(I->PerFrameMemory); + }break; + + case BlumenDebug_Awaken: + { + ui_Label(I, MakeString("Step 1:")); + ui_Label(I, MakeString("Leds off, flowers closed")); + if (ui_Button(I, MakeString("Prepare"))) + { + // motors closed + blumen_packet M = {}; + M.Type = PacketType_MotorState; + M.MotorPacket.FlowerPositions[0] = MotorState_Closed; + M.MotorPacket.FlowerPositions[1] = MotorState_Closed; + M.MotorPacket.FlowerPositions[2] = MotorState_Closed; + gs_data D = StructToData(&M, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, D); + + // animation + State->AnimationSystem.RepeatMode = AnimationRepeat_Single; + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + BLState->OffAnimHandle, + VoiceCommandFadeDuration); + + BLState->PatternMode = BlumenPattern_NoControl; + BLState->IgnoreTimeOfDay_LedDimming = true; + BLState->IgnoreTimeOfDay_MotorState = true; + } + + ui_Label(I, MakeString("Step 2:")); + if (ui_Button(I, MakeString("Begin Light Show"))) + { + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + BLState->AwakenHandle, + VoiceCommandFadeDuration); + } + + ui_Label(I, MakeString("Step 3:")); + if (ui_Button(I, MakeString("Open Flowers"))) + { + // motors closed + blumen_packet M = {}; + M.Type = PacketType_MotorState; + M.MotorPacket.FlowerPositions[0] = MotorState_Open; + M.MotorPacket.FlowerPositions[1] = MotorState_Open; + M.MotorPacket.FlowerPositions[2] = MotorState_Open; + gs_data D = StructToData(&M, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, D); + } + + ui_Label(I, MakeString("Step 4:")); + ui_Label(I, MakeString("Resets Lumenarium")); + if (ui_Button(I, MakeString("Complete"))) + { + BLState->IgnoreTimeOfDay_LedDimming = false; + BLState->IgnoreTimeOfDay_MotorState = false; + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, + BLState); + } + }break; + + InvalidDefaultCase; } - ui_EndLabeledDropdown(I); - if (ui_Button(I, MakeString("Say Phrase"))) - { - gs_string DebugStr = PushString(State->Transient, 256); - BLState->NextHotHue = BLState->PendingPhrase; - BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State); - } - - InterfaceAssert(I->PerFrameMemory); } US_CUSTOM_CLEANUP(BlumenLumen_CustomCleanup) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index a22cb40..eb266eb 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -7,10 +7,26 @@ #include "message_queue.h" +enum bl_debug_ui_mode +{ + BlumenDebug_Motors, + BlumenDebug_Leds, + BlumenDebug_Awaken, + + BlumenDebug_Count, +}; + +char* BlDebugUiModeStrings[] = { + "Motors", + "Leds", + "Awaken", +}; + enum bl_pattern_mode { BlumenPattern_Standard, BlumenPattern_VoiceCommand, + BlumenPattern_NoControl, BlumenPattern_Count, }; @@ -172,6 +188,8 @@ struct blumen_lumen_state bl_pattern_mode PatternMode; animation_handle_array ModeAnimations[BlumenPattern_Count]; + animation_handle OffAnimHandle; + animation_handle AwakenHandle; phrase_hue_map PhraseHueMap; @@ -186,10 +204,14 @@ struct blumen_lumen_state r32 PatternSpeed; // Debug + bl_debug_ui_mode DebugMode; + motor_packet DEBUG_PendingMotorPacket; bool DEBUG_IgnoreWeatherDimmingLeds; bool ShouldUpdateLog; + bool IgnoreTimeOfDay_LedDimming; + bool IgnoreTimeOfDay_MotorState; phrase_hue PendingPhrase; }; From 6cb0749f2ca7690907d15e9372e75e5a5a90bbfa Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 01:52:14 -1000 Subject: [PATCH 067/151] awaken and off_anim --- .../data/blumen_animations/awaken.foldanim | 64 +++++++++++++++++++ .../data/blumen_animations/off_anim.foldanim | 12 ++++ 2 files changed, 76 insertions(+) create mode 100644 app_run_tree/data/blumen_animations/awaken.foldanim create mode 100644 app_run_tree/data/blumen_animations/off_anim.foldanim diff --git a/app_run_tree/data/blumen_animations/awaken.foldanim b/app_run_tree/data/blumen_animations/awaken.foldanim new file mode 100644 index 0000000..efd7194 --- /dev/null +++ b/app_run_tree/data/blumen_animations/awaken.foldanim @@ -0,0 +1,64 @@ +lumenarium_animation_file; +animation_name: "awaken"; +layers_count: 3; +blocks_count: 5; +playable_range:{ + min: 0; + max: 7200; +}; +layers:{ + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; + layer:{ + name: "[New Layer]"; + blend: "Multiply"; + }; +}; +blocks:{ + block:{ + frame_range:{ + min: 94; + max: 1363; + }; + layer_index: 0; + animation_name: "Pattern_Leafy"; + }; + block:{ + frame_range:{ + min: 1169; + max: 7200; + }; + layer_index: 0; + animation_name: "Pattern_BulbMask"; + }; + block:{ + frame_range:{ + min: 71; + max: 7200; + }; + layer_index: 2; + animation_name: "Pattern_Wavy"; + }; + block:{ + frame_range:{ + min: 2135; + max: 2555; + }; + layer_index: 1; + animation_name: "Pattern_None"; + }; + block:{ + frame_range:{ + min: 2470; + max: 7200; + }; + layer_index: 1; + animation_name: "Pattern_StemSolid"; + }; +}; diff --git a/app_run_tree/data/blumen_animations/off_anim.foldanim b/app_run_tree/data/blumen_animations/off_anim.foldanim new file mode 100644 index 0000000..d7a3d45 --- /dev/null +++ b/app_run_tree/data/blumen_animations/off_anim.foldanim @@ -0,0 +1,12 @@ +lumenarium_animation_file; +animation_name: "off_anim"; +layers_count: 0; +blocks_count: 0; +playable_range:{ + min: 0; + max: 2; +}; +layers:{ +}; +blocks:{ +}; From 29b05d8057a01071ed0f0dec4dd4e5ee1fbc2bbe Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Fri, 9 Apr 2021 12:35:58 -0400 Subject: [PATCH 068/151] edit settings --- src/app/ss_blumen_lumen/blumen_lumen_settings.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 1194dae..b33a224 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -50,7 +50,7 @@ global u32 LedOnTimesCount = CArrayLength(LedOnTimes); // do not edit // How long it takes to fade from the default pattern to the // voice activated pattern -r32 VoiceCommandFadeDuration = 1.0f; // in seconds +r32 VoiceCommandFadeDuration = 0.0f; // in seconds // How long the voice activated pattern will remain active // without additional voice commands, before fading back to @@ -61,7 +61,7 @@ r32 VoiceCommandFadeDuration = 1.0f; // in seconds // and then wait 30 seconds before fading back to the original // pattern. If, in that 30 second window, another voice command // is issued, lumenarium will reset the 30 second counter. -r64 VoiceCommandSustainDuration = 30.0; // in seconds +r64 VoiceCommandSustainDuration = 120.0; // in seconds // When we send a Motor Close command, we don't want the upper leds to // immediately turn off. Instead, we want to wait until the flower is @@ -77,7 +77,7 @@ r64 VoiceCommandSustainDuration = 30.0; // in seconds // NOTE: This is not a symmetric operation. When we send a 'motor open' // command, we want to immediately turn the upper leds on so they appear // to have been on the whole time. -r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 60.0; // in seconds +r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 120.0; // in seconds // NOTE: Temperature & Time of Day Based Led Brightness Settings @@ -113,12 +113,12 @@ r32 GlobalAnimSpeed = 1.0f; // This is used both for transitioning between animation files // as well as transitioning from Standard pattern mode to voice // activated mode -r32 GlobalAnimTransitionSpeed = 0.1f; +r32 GlobalAnimTransitionSpeed = 0.0f; // how long it takes to fade from the old voice hue to the new one r32 PhraseHueFadeInDuration = 2.0f; // seconds -r64 PhrasePriorityMessageGroupingTime = 1.0f; +r64 PhrasePriorityMessageGroupingTime = 0.0f; // How often should Lumenarium send its status to the python server? // From 5d321d6f542d0c77f04f766e4e2c8af4f855c0ed Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 09:35:06 -1000 Subject: [PATCH 069/151] Pattern builder in debug menu --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 131 +++++++++++++++++------ src/app/ss_blumen_lumen/blumen_lumen.h | 3 + src/app/ss_blumen_lumen/phrase_hue_map.h | 10 +- src/gs_libs/gs_csv.h | 2 +- 4 files changed, 111 insertions(+), 35 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index ab31797..f33cc5f 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -376,13 +376,16 @@ BlumenLumen_CustomInit(app_state* State, context Context) gs_file_handler FileHandler = Context.ThreadContext.FileHandler; gs_file ColorPhraseCSVFile = ReadEntireFile(FileHandler, PhraseMapCSVPath); - gs_const_string ColorPhraseMapStr = DataToString(ColorPhraseCSVFile.Data); - gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, - { PhraseMapCSVSeparator }, - State->Transient); - - BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, - &State->Permanent); + if (ColorPhraseCSVFile.Memory != 0) + { + gs_const_string ColorPhraseMapStr = DataToString(ColorPhraseCSVFile.Data); + gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, + { PhraseMapCSVSeparator }, + State->Transient); + + BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, + &State->Permanent); + } #if 0 animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, @@ -391,6 +394,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) ConstString("data/demo_patterns.foldanim")); State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; #else + BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context); BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); @@ -405,6 +409,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) State->Patterns, Context, ConstString("data/blumen_animations/off_anim.foldanim")); + #endif State->AnimationSystem.TimelineShouldAdvance = true; @@ -565,36 +570,46 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) BlumenLumen_AdvanceHueFade(BLState, *Context); // Update next frames Hues - r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); - AnimTime = (r32)Context->TotalTime; - r32 BaseTime = AnimTime * BLState->PatternSpeed; - - r32 ColorSpeed = 1; //.001; - r32 ColorOscSpeed = .05 * ColorSpeed; - r32 ColorRelOscSpeed = 1 * ColorSpeed;; - r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; - r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); - BLState->StandardPatternHues.Hue0.Hue = ModR32(ColorOscillation * 360, 360); - BLState->StandardPatternHues.Hue1.Hue = ModR32(BaseTime + ColorRelationship, 360); - BLState->StandardPatternHues.Hue2.Hue = LerpR32(.3f, BLState->StandardPatternHues.Hue0.Hue, BLState->StandardPatternHues.Hue1.Hue); - - // Transition back to standard mode after some time - if (BLState->PatternMode == BlumenPattern_VoiceCommand) + if (!BLState->DebugOverrideHue) { - u64 LastChangeClock = BLState->TimeLastSetToVoiceMode.NanosSinceEpoch; - u64 NowClocks = Context->SystemTime_Current.NanosSinceEpoch; - s64 NanosSinceChange = NowClocks - LastChangeClock; - r64 SecondsSinceChange = (r64)NanosSinceChange * NanosToSeconds; + r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); + AnimTime = (r32)Context->TotalTime; + r32 BaseTime = AnimTime * BLState->PatternSpeed; - if (SecondsSinceChange > VoiceCommandSustainDuration) + r32 ColorSpeed = 1; //.001; + r32 ColorOscSpeed = .05 * ColorSpeed; + r32 ColorRelOscSpeed = 1 * ColorSpeed;; + r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; + r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); + BLState->StandardPatternHues.Hue0.Hue = ModR32(ColorOscillation * 360, 360); + BLState->StandardPatternHues.Hue1.Hue = ModR32(BaseTime + ColorRelationship, 360); + BLState->StandardPatternHues.Hue2.Hue = LerpR32(.3f, BLState->StandardPatternHues.Hue0.Hue, BLState->StandardPatternHues.Hue1.Hue); + + // Transition back to standard mode after some time + if (BLState->PatternMode == BlumenPattern_VoiceCommand) { - BLState->PatternMode = BlumenPattern_Standard; - animation_handle NewAnim = BLState->ModeAnimations[BlumenPattern_Standard].Handles[0]; - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, - NewAnim, - VoiceCommandFadeDuration); - BLState->ShouldUpdateLog = true; + u64 LastChangeClock = BLState->TimeLastSetToVoiceMode.NanosSinceEpoch; + u64 NowClocks = Context->SystemTime_Current.NanosSinceEpoch; + s64 NanosSinceChange = NowClocks - LastChangeClock; + r64 SecondsSinceChange = (r64)NanosSinceChange * NanosToSeconds; + + if (SecondsSinceChange > VoiceCommandSustainDuration) + { + BLState->PatternMode = BlumenPattern_Standard; + animation_handle NewAnim = BLState->ModeAnimations[BlumenPattern_Standard].Handles[0]; + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + NewAnim, + VoiceCommandFadeDuration); + BLState->ShouldUpdateLog = true; + } } + + } + else + { + BLState->StandardPatternHues = BLState->DebugHue; + AnimationSystem_FadeToPlaylist(&State->AnimationSystem, BLState->ModeAnimations[BlumenPattern_VoiceCommand]); + } // Open / Close the Motor @@ -928,6 +943,56 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State); } + ui_Label(I, MakeString("Phrase Constructor")); + BLState->DebugOverrideHue = ui_ToggleText(I, MakeString("Override Hue"), BLState->DebugOverrideHue); + if (BLState->DebugOverrideHue) + { + phrase_hue PHue = BLState->DebugHue; + PHue.Hue0.Hue = (r64)ui_LabeledRangeSlider(I, MakeString("Hue0"), (r32)PHue.Hue0.Hue, 0, 360); + PHue.Hue1.Hue = (r64)ui_LabeledRangeSlider(I, MakeString("Hue1"), (r32)PHue.Hue1.Hue, 0, 360); + PHue.Hue2.Hue = (r64)ui_LabeledRangeSlider(I, MakeString("Hue2"), (r32)PHue.Hue2.Hue, 0, 360); + PHue.Granularity = (u32)ui_LabeledRangeSlider(I, MakeString("Granularity"), (r32)PHue.Granularity, 0, 5); + PHue.Speed = ui_LabeledRangeSlider(I, MakeString("Speed"), PHue.Speed, 0, 4); + + gs_string PatternOptions[HuePattern_Count] = {}; + PatternOptions[HuePattern_Patchy] = MakeString("patchy"); + PatternOptions[HuePattern_Wavy] = MakeString("wavy"); + + gs_string CPattern = PatternOptions[PHue.Pattern]; + if (ui_BeginLabeledDropdown(I, MakeString("Pattern"), CPattern)) + { + for (u32 i = 0; i < HuePattern_Count; i++) + { + if (ui_Button(I, PatternOptions[i])) + { + PHue.Pattern = i; + } + } + } + ui_EndLabeledDropdown(I); + + + gs_string AddInOptions[AddIn_Count] = {}; + AddInOptions[AddIn_None] = MakeString("NA"); + AddInOptions[AddIn_Waves] = MakeString("waves"); + AddInOptions[AddIn_Rotary] = MakeString("rotary"); + + gs_string CAddIn = AddInOptions[PHue.AddIn]; + if (ui_BeginLabeledDropdown(I, MakeString("Add In"), CAddIn)) + { + for (u32 i = 0; i < AddIn_Count; i++) + { + if (ui_Button(I, AddInOptions[i])) + { + PHue.AddIn = i; + } + } + } + ui_EndLabeledDropdown(I); + BLState->DebugHue = PHue; + } + + InterfaceAssert(I->PerFrameMemory); }break; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index eb266eb..3d2bec5 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -214,6 +214,9 @@ struct blumen_lumen_state bool IgnoreTimeOfDay_MotorState; phrase_hue PendingPhrase; + + bool DebugOverrideHue; + phrase_hue DebugHue; }; #include "message_queue.cpp" diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h index 91256d4..da3516d 100644 --- a/src/app/ss_blumen_lumen/phrase_hue_map.h +++ b/src/app/ss_blumen_lumen/phrase_hue_map.h @@ -14,6 +14,8 @@ enum p_hue_pattern { HuePattern_Patchy, HuePattern_Wavy, + + HuePattern_Count, }; enum p_hue_add_in @@ -21,6 +23,8 @@ enum p_hue_add_in AddIn_None, AddIn_Waves, AddIn_Rotary, + + AddIn_Count, }; typedef struct p_hue @@ -147,6 +151,8 @@ internal phrase_hue_map PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) { phrase_hue_map Result = {}; + if (Sheet.RowCount == 0) return Result; + Result.CountMax = Sheet.RowCount - 1; // we don't include the header row Result.Phrases = PushArray(Arena, gs_const_string, Result.CountMax); Result.PhraseHashes = PushArray(Arena, u64, Result.CountMax); @@ -158,6 +164,8 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) Result.Speed = PushArray(Arena, r32, Result.CountMax); Result.AddIn = PushArray(Arena, u8, Result.CountMax); + // this lets us tightly pack phrase_hues even if there is a + // row in the csv that is empty or invalid s32 DestOffset = 0; for (u32 Row = 1; Row < Sheet.RowCount; Row++) { @@ -166,7 +174,6 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) gs_const_string Phrase = CSVSheet_GetCell(Sheet, 0, Row); - gs_const_string Hue0Str = CSVSheet_GetCell(Sheet, 1, Row); gs_const_string Hue1Str = CSVSheet_GetCell(Sheet, 2, Row); gs_const_string Hue2Str = CSVSheet_GetCell(Sheet, 3, Row); @@ -176,6 +183,7 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) gs_const_string Pattern = CSVSheet_GetCell(Sheet, 7, Row); gs_const_string AddIn = CSVSheet_GetCell(Sheet, 8, Row); + // essential parameters if (Phrase.Length == 0 || Hue0Str.Length == 0 || Hue1Str.Length == 0 || diff --git a/src/gs_libs/gs_csv.h b/src/gs_libs/gs_csv.h index 5b8c965..6969d4c 100644 --- a/src/gs_libs/gs_csv.h +++ b/src/gs_libs/gs_csv.h @@ -201,7 +201,7 @@ CSVSheet_GetCell(gscsv_sheet Sheet, u64 Column, u64 Row) { gs_const_string Result = {}; - if (Sheet.RowCount > Row && Sheet.ColumnCount > Column) + if (Row < Sheet.RowCount && Column < Sheet.ColumnCount) { u64 CellIndex = (Row * Sheet.ColumnCount) + Column; Result = Sheet.Cells[CellIndex].Value; From 4f293980edbc19aa95f4adb62b8ee32f57ff5682 Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 12:09:56 -1000 Subject: [PATCH 070/151] Picking a Phrase now also sets it as teh debug phrase --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index f33cc5f..e6f0f02 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -931,6 +931,7 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) if (ui_Button(I, Str)) { BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i); + BLState->DebugHue = BLState->PendingPhrase; } } ui_EndList(I); From b3fbae340ba4ab21b81a030bdf7b13439b338fc4 Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 12:14:00 -1000 Subject: [PATCH 071/151] Made LedBlend_Overwrite more useful --- .../engine/animation/foldhaus_animation_renderer.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index 5bdaec1..eb2c130 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -8,13 +8,22 @@ internal pixel LedBlend_Overwrite(pixel PixelA, pixel PixelB, u8* UserData) { + r32 MagB = (r32)(PixelB.R + PixelB.G + PixelB.B) / (255 * 3); + + pixel Result = {}; + Result.R = (u8)LerpR32(MagB, PixelA.R, PixelB.R); + Result.G = (u8)LerpR32(MagB, PixelA.G, PixelB.G); + Result.B = (u8)LerpR32(MagB, PixelA.B, PixelB.B); + +#if 0 pixel Result = PixelB; if (PixelB.R == 0 && PixelB.G == 0 && - PixelB.G == 0) + PixelB.B == 0) { Result = PixelA; } +#endif return Result; } From fae031b746e079faa4dd3e1be67c3cf75f55e5f8 Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 14:15:02 -1000 Subject: [PATCH 072/151] fixing problem where closing the motor immediately masks the upper leds --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index e6f0f02..b36653c 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -690,6 +690,10 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { BLState->ShouldDimUpperLeds[i] = true; } + else + { + BLState->ShouldDimUpperLeds[i] = false; + } } else if (MotorPos == MotorState_Closed || MotorPos == MotorState_HalfOpen) From 37c30e1cb21f7ddeb2adc0d4011b9161806510f0 Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 14:59:55 -1000 Subject: [PATCH 073/151] Lumenarium now repeatedly sends the current motor position desired based on a variable in settings --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 12 +++++++++++- src/app/ss_blumen_lumen/blumen_lumen_settings.h | 5 +++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index b36653c..1a91994 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -616,6 +616,10 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue) && !BLState->IgnoreTimeOfDay_MotorState) { + u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; + r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; + bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; + for (u32 i = 0; i < MotorOpenTimesCount; i++) { time_range Range = MotorOpenTimes[i]; @@ -626,9 +630,13 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) bool LastTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Last, Range); +#if 0 bool SendOpen = CurrTimeInRange && !LastSendTimeInRange; bool SendClose = !CurrTimeInRange && LastSendTimeInRange; - +#else + bool SendOpen = CurrTimeInRange && ShouldSendCurrentState; + bool SendClose = !CurrTimeInRange && ShouldSendCurrentState; +#endif //SendOpen = SecondsSinceLastSend > 2; if (SendOpen) { @@ -643,6 +651,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Packet.MotorPacket.FlowerPositions[1] = 2; Packet.MotorPacket.FlowerPositions[2] = 2; MotorCommand = Packet; + break; } else if (SendClose) { @@ -656,6 +665,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Packet.MotorPacket.FlowerPositions[1] = 1; Packet.MotorPacket.FlowerPositions[2] = 1; MotorCommand = Packet; + break; } } diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index b33a224..a55ce1b 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -41,6 +41,11 @@ global time_range MotorOpenTimes[] = { }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit +// Lumenarium repeatedly resends the current motor state to the python +// server. This variable determines how much time elapses between each +// message. +global r32 MotorResendStatePeriod = 90.0f; // seconds + // The times of day when the leds should be on // Search for @TimeFormat to find documentation global time_range LedOnTimes[] = { From cbd6433a30b8def5ebbfc95e116d5b13a6425f50 Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 15:45:28 -1000 Subject: [PATCH 074/151] Hue fading --- src/app/engine/animation/foldhaus_animation.h | 2 +- src/app/ss_blumen_lumen/phrase_hue_map.h | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 313ee68..8ae3430 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -678,7 +678,7 @@ AnimationSystem_Init(animation_system_desc Desc) Result.ActiveFadeGroup.FadeElapsed = 0; // Settings - Result.Multithreaded = true; + Result.Multithreaded = false; return Result; } diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h index da3516d..3667210 100644 --- a/src/app/ss_blumen_lumen/phrase_hue_map.h +++ b/src/app/ss_blumen_lumen/phrase_hue_map.h @@ -70,11 +70,17 @@ LerpPHue(r32 T, p_hue A, p_hue B) { Result.Hue = LerpR64(T, A.Hue, B.Hue); } + else if (B.Hue > A.Hue) + { + Result.Hue = LerpR64(T, A.Hue, B.Hue - 360.0f); + } else { - Result.Hue = LerpR64(T, A.Hue + 360.0f, B.Hue); - Result.Hue = ModR32(Result.Hue, 360.0f); + Result.Hue = LerpR64(T, A.Hue - 360.0f, B.Hue); } + if (Result.Hue < 360) Result.Hue += 360; + if (Result.Hue > 360) Result.Hue -= 360; + Result.Hue = Clamp(0, Result.Hue, 360); if (T < 0.5f) { From 6f1d247bc8813b6ff3db774c5bc1f7853c91736d Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 15:59:32 -1000 Subject: [PATCH 075/151] Made Hues capable of smoothly lerping to white or black --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 25 +++++++---- .../ss_blumen_lumen/blumen_lumen_settings.h | 2 +- src/app/ss_blumen_lumen/phrase_hue_map.h | 45 +++++++------------ 3 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 1a91994..cee7f4e 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -413,14 +413,15 @@ BlumenLumen_CustomInit(app_state* State, context Context) #endif State->AnimationSystem.TimelineShouldAdvance = true; - BLState->StandardPatternHues.Hue0.Flags = Hue_Value; - BLState->StandardPatternHues.Hue1.Flags = Hue_Value; - BLState->StandardPatternHues.Hue2.Flags = Hue_Value; BLState->StandardPatternHues.Granularity = 1; BLState->StandardPatternHues.Speed = 1; BLState->StandardPatternHues.AddIn = AddIn_Rotary; BLState->StandardPatternHues.Pattern = HuePattern_Wavy; + BLState->DebugHue.Hue0.HSV = v4{0, 1, 1, 1}; + BLState->DebugHue.Hue1.HSV = v4{0, 1, 1, 1}; + BLState->DebugHue.Hue2.HSV = v4{0, 1, 1, 1}; + BlumenLumen_AppendBootupLog(State, BLState, Context); return Result; } @@ -581,9 +582,15 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) r32 ColorRelOscSpeed = 1 * ColorSpeed;; r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); - BLState->StandardPatternHues.Hue0.Hue = ModR32(ColorOscillation * 360, 360); - BLState->StandardPatternHues.Hue1.Hue = ModR32(BaseTime + ColorRelationship, 360); - BLState->StandardPatternHues.Hue2.Hue = LerpR32(.3f, BLState->StandardPatternHues.Hue0.Hue, BLState->StandardPatternHues.Hue1.Hue); + + r32 H0 = ModR32(ColorOscillation * 360, 360); + r32 H1 = ModR32(BaseTime + ColorRelationship, 360); + // TODO(PS): use our new HSV lerp + r32 H2 = LerpR32(.3f, H0, H1); + + BLState->StandardPatternHues.Hue0.HSV = v4{ H0, 1, 1, 1 }; + BLState->StandardPatternHues.Hue1.HSV = v4{ H1, 1, 1, 1 }; + BLState->StandardPatternHues.Hue2.HSV = v4{ H2, 1, 1, 1 }; // Transition back to standard mode after some time if (BLState->PatternMode == BlumenPattern_VoiceCommand) @@ -963,9 +970,9 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) if (BLState->DebugOverrideHue) { phrase_hue PHue = BLState->DebugHue; - PHue.Hue0.Hue = (r64)ui_LabeledRangeSlider(I, MakeString("Hue0"), (r32)PHue.Hue0.Hue, 0, 360); - PHue.Hue1.Hue = (r64)ui_LabeledRangeSlider(I, MakeString("Hue1"), (r32)PHue.Hue1.Hue, 0, 360); - PHue.Hue2.Hue = (r64)ui_LabeledRangeSlider(I, MakeString("Hue2"), (r32)PHue.Hue2.Hue, 0, 360); + PHue.Hue0.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue0"), (r32)PHue.Hue0.HSV.x, 0, 360); + PHue.Hue1.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue1"), (r32)PHue.Hue1.HSV.x, 0, 360); + PHue.Hue2.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue2"), (r32)PHue.Hue2.HSV.x, 0, 360); PHue.Granularity = (u32)ui_LabeledRangeSlider(I, MakeString("Granularity"), (r32)PHue.Granularity, 0, 5); PHue.Speed = ui_LabeledRangeSlider(I, MakeString("Speed"), PHue.Speed, 0, 4); diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index a55ce1b..6f8c46c 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -121,7 +121,7 @@ r32 GlobalAnimSpeed = 1.0f; r32 GlobalAnimTransitionSpeed = 0.0f; // how long it takes to fade from the old voice hue to the new one -r32 PhraseHueFadeInDuration = 2.0f; // seconds +r32 PhraseHueFadeInDuration = 1.0f; // seconds r64 PhrasePriorityMessageGroupingTime = 0.0f; diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h index 3667210..39b36d2 100644 --- a/src/app/ss_blumen_lumen/phrase_hue_map.h +++ b/src/app/ss_blumen_lumen/phrase_hue_map.h @@ -29,8 +29,7 @@ enum p_hue_add_in typedef struct p_hue { - r64 Hue; - p_hue_flag Flags; + v4 HSV; } p_hue; typedef struct phrase_hue_map @@ -66,28 +65,25 @@ LerpPHue(r32 T, p_hue A, p_hue B) { p_hue Result = {}; - if (Abs(A.Hue - B.Hue) < 180.0f) + if (Abs(A.HSV.x - B.HSV.x) < 180.0f) { - Result.Hue = LerpR64(T, A.Hue, B.Hue); + Result.HSV.x = LerpR64(T, A.HSV.x, B.HSV.x); } - else if (B.Hue > A.Hue) + else if (B.HSV.x > A.HSV.x) { - Result.Hue = LerpR64(T, A.Hue, B.Hue - 360.0f); + Result.HSV.x = LerpR64(T, A.HSV.x, B.HSV.x - 360.0f); } else { - Result.Hue = LerpR64(T, A.Hue - 360.0f, B.Hue); + Result.HSV.x = LerpR64(T, A.HSV.x - 360.0f, B.HSV.x); } - if (Result.Hue < 360) Result.Hue += 360; - if (Result.Hue > 360) Result.Hue -= 360; - Result.Hue = Clamp(0, Result.Hue, 360); + if (Result.HSV.x < 360) Result.HSV.x += 360; + if (Result.HSV.x > 360) Result.HSV.x -= 360; + Result.HSV.x = Clamp(0, Result.HSV.x, 360); + Result.HSV.y = LerpR32(T, A.HSV.y, B.HSV.y); + Result.HSV.z = LerpR32(T, A.HSV.z, B.HSV.z); + Result.HSV.w = LerpR32(T, A.HSV.w, B.HSV.w); - if (T < 0.5f) - { - Result.Flags = A.Flags; - } else { - Result.Flags = B.Flags; - } return Result; } @@ -122,18 +118,18 @@ CreateHueFromString(gs_const_string Str) { p_hue Result = {}; if (Str.Str[0] == 'b') { - Result.Flags = Hue_Black; + Result.HSV = v4{0, 0, 1, 1 }; } else if (Str.Str[0] == 'w') { - Result.Flags = Hue_White; + Result.HSV = v4{0, 0, 0, 1 };; } else { - Result.Flags = Hue_Value; parse_float_result Parsed = ValidateAndParseFloat(Str); if (!Parsed.Success) { OutputDebugString("Failed to Parse CSV Float\n"); Parsed.Value = 0.0; } - Result.Hue = Parsed.Value; + Result.HSV = v4{ (r32)Parsed.Value, 1, 1, 1 }; + } return Result; } @@ -141,14 +137,7 @@ CreateHueFromString(gs_const_string Str) internal v4 RGBFromPhraseHue (p_hue H) { - v4 Result = {}; - switch (H.Flags) - { - case Hue_Black: { Result = v4{1, 0, 0, 1}; } break; - case Hue_White: { Result = v4{1, 0, 1, 1}; } break; - case Hue_Value: { Result = v4{(r32)H.Hue, 1, 1, 1}; } break; - InvalidDefaultCase; - } + v4 Result = H.HSV; Result = HSVToRGB(Result); return Result; } From 9404a6b57de5e52e8d7a8127f50f6aa8242fd3c4 Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 9 Apr 2021 16:15:25 -1000 Subject: [PATCH 076/151] Override all hue phrases --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 3 ++- src/app/ss_blumen_lumen/phrase_hue_map.h | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index cee7f4e..d711f23 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -457,7 +457,8 @@ BlumenLumen_ApplyNextHotHue(blumen_lumen_state* BLState, context Context, gs_str OutputDebugString(DebugStr->Str); - if (BLState->PatternMode == BlumenPattern_Standard) + if (BLState->PatternMode == BlumenPattern_Standard || + NewHue.OverrideAll) { BlumenLumen_SetNextHue(BLState, 0, NewHue); BlumenLumen_SetNextHue(BLState, 1, NewHue); diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h index 39b36d2..89ec6e0 100644 --- a/src/app/ss_blumen_lumen/phrase_hue_map.h +++ b/src/app/ss_blumen_lumen/phrase_hue_map.h @@ -45,6 +45,7 @@ typedef struct phrase_hue_map r32* Speed; u8* Pattern; u8* AddIn; + bool* OverrideAll; } phrase_hue_map; typedef struct phrase_hue @@ -58,6 +59,7 @@ typedef struct phrase_hue r32 Speed; u8 Pattern; u8 AddIn; + bool OverrideAll; } phrase_hue; internal p_hue @@ -118,9 +120,9 @@ CreateHueFromString(gs_const_string Str) { p_hue Result = {}; if (Str.Str[0] == 'b') { - Result.HSV = v4{0, 0, 1, 1 }; + Result.HSV = v4{0, 0, 0, 1 }; } else if (Str.Str[0] == 'w') { - Result.HSV = v4{0, 0, 0, 1 };; + Result.HSV = v4{0, 0, 1, 1 };; } else { parse_float_result Parsed = ValidateAndParseFloat(Str); if (!Parsed.Success) @@ -158,6 +160,7 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) Result.Pattern = PushArray(Arena, u8, Result.CountMax); Result.Speed = PushArray(Arena, r32, Result.CountMax); Result.AddIn = PushArray(Arena, u8, Result.CountMax); + Result.OverrideAll = PushArray(Arena, bool, Result.CountMax); // this lets us tightly pack phrase_hues even if there is a // row in the csv that is empty or invalid @@ -177,6 +180,7 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) gs_const_string Speed = CSVSheet_GetCell(Sheet, 6, Row); gs_const_string Pattern = CSVSheet_GetCell(Sheet, 7, Row); gs_const_string AddIn = CSVSheet_GetCell(Sheet, 8, Row); + gs_const_string OverrideAll = CSVSheet_GetCell(Sheet, 9, Row); // essential parameters if (Phrase.Length == 0 || @@ -234,6 +238,8 @@ PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena) ParsedGranularity.Value = 1; } Result.Gran[Index] = ParsedGranularity.Value; + + Result.OverrideAll[Index] = StringsEqualUpToLength(OverrideAll, ConstString("yes"), 3); } Result.Count = Result.CountMax + DestOffset; @@ -255,6 +261,7 @@ PhraseHueMap_Get(phrase_hue_map Map, u32 Index) Result.Speed = Map.Speed[Index]; Result.AddIn = Map.AddIn[Index]; Result.Pattern = Map.Pattern[Index]; + Result.OverrideAll = Map.OverrideAll[Index]; return Result; } From 14ded0bbe6338094c9420c6d604a2a0dc9ecd769 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Fri, 9 Apr 2021 22:15:59 -0400 Subject: [PATCH 077/151] live editing --- .../ambient_patterns/fishy_0.foldanim | 2 +- .../ambient_patterns/rainbow_0.foldanim | 10 +++++++--- src/app/editor/interface.h | 2 +- src/app/patterns/blumen_patterns.h | 4 ++-- src/app/ss_blumen_lumen/blumen_lumen.h | 1 + .../ss_blumen_lumen/blumen_lumen_settings.h | 20 ++++++++++--------- 6 files changed, 23 insertions(+), 16 deletions(-) diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim index 7119bbc..562f43d 100644 --- a/app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim +++ b/app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim @@ -43,6 +43,6 @@ blocks:{ max: 3600; }; layer_index: 0; - animation_name: "Pattern_Rotary"; + animation_name: "Pattern_VerticalLines"; }; }; diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim index ab6f4e3..ee12af4 100644 --- a/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim +++ b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim @@ -1,10 +1,10 @@ lumenarium_animation_file; animation_name: "rainbow"; -layers_count: 2; +layers_count: 3; blocks_count: 1; playable_range:{ min: 0; - max: 360; + max: 3600; }; layers:{ layer:{ @@ -15,12 +15,16 @@ layers:{ name: "Color"; blend: "Add"; }; + layer:{ + name: "[New Layer]"; + blend: "Add"; + }; }; blocks:{ block:{ frame_range:{ min: 0; - max: 360; + max: 3600; }; layer_index: 1; animation_name: "Pattern_Rainbow"; diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index 6a0e228..244e85d 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -517,7 +517,7 @@ ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id) { // NOTE(PS): If we are accessing a retained state, it shouldn't // have been accessed longer ago than the last frame - Assert(Interface->RetainedState[i].FramesSinceAccess <= 2); + //Assert(Interface->RetainedState[i].FramesSinceAccess <= 2); Interface->RetainedState[i].FramesSinceAccess = 0; Result = Interface->RetainedState + i; break; diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 534d78e..3023506 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -414,7 +414,7 @@ Pattern_VerticalLines(led_buffer* Leds, led_buffer_range Range, assembly Assembl r32 LightSpeedMin = 1; r32 LightSpeedMax = 5; - s32 LightTailLength = 10; + s32 LightTailLength = 60; for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) { v2_strip Strip = Assembly.Strips[StripIndex]; @@ -503,7 +503,7 @@ Pattern_Rotary(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; Time = Time * BLState->PatternSpeed; - Pattern_RotaryOptions(Leds, Range, Assembly, Time, Transient, UserData, 2, BlackV4, WhiteV4); + Pattern_RotaryOptions(Leds, Range, Assembly, Time, Transient, UserData, .25f, BlackV4, WhiteV4); } internal void diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 3d2bec5..db52256 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -257,6 +257,7 @@ BlumenLumen_GetCurrentHue(blumen_lumen_state* BLState, assembly Assembly) switch (BLState->PatternMode) { + case BlumenPattern_NoControl: case BlumenPattern_Standard: { Result = BLState->StandardPatternHues; diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index b33a224..24c0348 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -16,7 +16,7 @@ gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold"); // The path to the phrase map CSV. Can be an absolute path, or relative // to the app_run_tree folder -gs_const_string PhraseMapCSVPath = ConstString("data/flower_codes.tsv"); +gs_const_string PhraseMapCSVPath = ConstString("C:/projects/flowers-sound/flower_codes.tsv"); char PhraseMapCSVSeparator = '\t'; // Search Strings for which folders to find ambient animation files and @@ -36,21 +36,23 @@ gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_r // NOTE: There is no need to modify the MotorOpenTimesCount variable - // it is a compile time constant that gets calculated automatically global time_range MotorOpenTimes[] = { - { 12, 45, 17, 45 }, // 12:45pm to 5:45pm - { 19, 00, 23, 00 }, // 7:00pm to 11:00pm + { 8, 00, 12, 00 }, + { 12, 30, 13, 00 }, + { 18, 00, 22, 00 }, // 7:00pm to 11:00pm }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit // The times of day when the leds should be on // Search for @TimeFormat to find documentation global time_range LedOnTimes[] = { - { 00, 00, 23, 59 }, // literally always + { 17, 00, 23, 59 }, + { 00, 00, 06, 30 }, }; global u32 LedOnTimesCount = CArrayLength(LedOnTimes); // do not edit // How long it takes to fade from the default pattern to the // voice activated pattern -r32 VoiceCommandFadeDuration = 0.0f; // in seconds +r32 VoiceCommandFadeDuration = 0.5f; // in seconds // How long the voice activated pattern will remain active // without additional voice commands, before fading back to @@ -88,14 +90,14 @@ r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 120.0; // in seconds // NOTE: this is an 8bit signed integer so its range is // -128 to 127, not that we should need either of those extremes for // this, but just a note that you can't just put anything in here. -s8 MinHighTemperature = 0; +s8 MinHighTemperature = 26; // The percent brightness we set leds to during high temperatures. // A value in the range 0:1 inclusive // This is multiplied by each pixels R, G, & B channels before being // sent. So if it is set to .1f, then the maximum brightness value sent // to any channel of any pixel will be 25 (255 * .1 = 25). -r32 HighTemperatureBrightnessPercent = 1.0f; +r32 HighTemperatureBrightnessPercent = 0.7f; // The percent brightness we set leds to when no other conditions apply // A value in the range 0:1 inclusive. @@ -116,9 +118,9 @@ r32 GlobalAnimSpeed = 1.0f; r32 GlobalAnimTransitionSpeed = 0.0f; // how long it takes to fade from the old voice hue to the new one -r32 PhraseHueFadeInDuration = 2.0f; // seconds +r32 PhraseHueFadeInDuration = 0.01f; // seconds -r64 PhrasePriorityMessageGroupingTime = 0.0f; +r64 PhrasePriorityMessageGroupingTime = 0.1f; // How often should Lumenarium send its status to the python server? // From e4d9a389c317f8910cc5b132cd846e2568047a61 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Mon, 12 Apr 2021 21:21:47 -0400 Subject: [PATCH 078/151] Working on fixing motor commands --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 47 +++++++++++++++++-- src/app/ss_blumen_lumen/blumen_lumen.h | 1 + .../ss_blumen_lumen/blumen_lumen_settings.h | 3 +- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index d711f23..bbc7fe1 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -628,6 +628,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; + bool SendOpen = false; for (u32 i = 0; i < MotorOpenTimesCount; i++) { time_range Range = MotorOpenTimes[i]; @@ -641,10 +642,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) #if 0 bool SendOpen = CurrTimeInRange && !LastSendTimeInRange; bool SendClose = !CurrTimeInRange && LastSendTimeInRange; -#else - bool SendOpen = CurrTimeInRange && ShouldSendCurrentState; - bool SendClose = !CurrTimeInRange && ShouldSendCurrentState; -#endif //SendOpen = SecondsSinceLastSend > 2; if (SendOpen) { @@ -675,10 +672,50 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) MotorCommand = Packet; break; } +#else + if (CurrTimeInRange) { + SendOpen = true; + // if the current state isn't what we want, we want to + // send immediately, rather than wait for the periodic time out + if (BLState->LastSendState != MotorState_Open) + { + ShouldSendCurrentState = true; + } + break; + } +#endif } - if (SendMotorCommand) + if (ShouldSendCurrentState) { + + if (SendOpen) + { + BLState->LastSendTime = Context->SystemTime_Current; + BLState->LastSendState = MotorState_Open; + OutputDebugString("Motors: Open\n"); + + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket.FlowerPositions[0] = 2; + Packet.MotorPacket.FlowerPositions[1] = 2; + Packet.MotorPacket.FlowerPositions[2] = 2; + MotorCommand = Packet; + } + else + { + BLState->LastSendTime = Context->SystemTime_Current; + BLState->LastSendState = MotorState_Closed; + OutputDebugString("Motors: Close\n"); + + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket.FlowerPositions[0] = 1; + Packet.MotorPacket.FlowerPositions[1] = 1; + Packet.MotorPacket.FlowerPositions[2] = 1; + MotorCommand = Packet; + } + gs_data Msg = StructToData(&MotorCommand, blumen_packet); MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); DEBUG_SentMotorCommand(MotorCommand.MotorPacket, Context->ThreadContext); diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index db52256..e05d83a 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -171,6 +171,7 @@ struct blumen_lumen_state system_time LastStatusUpdateTime; system_time LastSendTime; + bl_motor_state_value LastSendState; phrase_hue StandardPatternHues; r32 AssemblyColorsTransitionTimeLeft[BL_FLOWER_COUNT]; diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 184df19..2c4858c 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -38,7 +38,8 @@ gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_r global time_range MotorOpenTimes[] = { { 8, 00, 12, 00 }, { 12, 30, 13, 00 }, - { 18, 00, 22, 00 }, // 7:00pm to 11:00pm + { 18, 00, 22, 00 }, // 6:00pm to 10:00pm + { 23, 05, 23, 53 }, }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit From 40f3a9f8174897067af594f5d40fd89a46a8502d Mon Sep 17 00:00:00 2001 From: PS Date: Mon, 12 Apr 2021 16:00:09 -1000 Subject: [PATCH 079/151] Fixed motor time of day functionality --- build/build_app_msvc_win32_debug.bat | 2 +- src/app/ss_blumen_lumen/blumen_lumen.cpp | 98 +++++------------------- src/app/ss_blumen_lumen/blumen_lumen.h | 21 ++++- 3 files changed, 36 insertions(+), 85 deletions(-) diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index eec2df0..7494131 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -8,7 +8,7 @@ call %MyPath%\setup_cl.bat SET CommonCompilerFlags=-nologo -DDEBUG=1 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src -SET CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -I%CommonLibs% -O2 %CommonCompilerFlags% +SET CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -I%CommonLibs% -Od %CommonCompilerFlags% SET CommonLinkerFlags= -opt:ref -incremental:no diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index bbc7fe1..fc21fa3 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -486,9 +486,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) gs_string DebugStr = PushString(State->Transient, 256); - bool SendMotorCommand = false; - blumen_packet MotorCommand = {}; - while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) { gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); @@ -624,101 +621,42 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue) && !BLState->IgnoreTimeOfDay_MotorState) { + bool SendMotorCommand = false; + u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; + bl_motor_state_value NewMotorState = MotorState_Closed; bool SendOpen = false; for (u32 i = 0; i < MotorOpenTimesCount; i++) { time_range Range = MotorOpenTimes[i]; - bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); - - bool LastSendTimeInRange = SystemTimeIsInTimeRange(BLState->LastSendTime, Range); - - bool LastTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Last, Range); - -#if 0 - bool SendOpen = CurrTimeInRange && !LastSendTimeInRange; - bool SendClose = !CurrTimeInRange && LastSendTimeInRange; - //SendOpen = SecondsSinceLastSend > 2; - if (SendOpen) - { - SendMotorCommand = true; - - BLState->LastSendTime = Context->SystemTime_Current; - OutputDebugString("Motors: Open\n"); - - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket.FlowerPositions[0] = 2; - Packet.MotorPacket.FlowerPositions[1] = 2; - Packet.MotorPacket.FlowerPositions[2] = 2; - MotorCommand = Packet; - break; - } - else if (SendClose) - { - SendMotorCommand = true; - BLState->LastSendTime = Context->SystemTime_Current; - OutputDebugString("Motors: Close\n"); - - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket.FlowerPositions[0] = 1; - Packet.MotorPacket.FlowerPositions[1] = 1; - Packet.MotorPacket.FlowerPositions[2] = 1; - MotorCommand = Packet; - break; - } -#else if (CurrTimeInRange) { - SendOpen = true; - // if the current state isn't what we want, we want to - // send immediately, rather than wait for the periodic time out - if (BLState->LastSendState != MotorState_Open) - { - ShouldSendCurrentState = true; - } - break; + NewMotorState = MotorState_Open; } -#endif + } + + if (NewMotorState != BLState->LastSendState) + { + ShouldSendCurrentState = true; } if (ShouldSendCurrentState) { + BLState->LastSendTime = Context->SystemTime_Current; + BLState->LastSendState = NewMotorState; - if (SendOpen) - { - BLState->LastSendTime = Context->SystemTime_Current; - BLState->LastSendState = MotorState_Open; - OutputDebugString("Motors: Open\n"); - - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket.FlowerPositions[0] = 2; - Packet.MotorPacket.FlowerPositions[1] = 2; - Packet.MotorPacket.FlowerPositions[2] = 2; - MotorCommand = Packet; - } - else - { - BLState->LastSendTime = Context->SystemTime_Current; - BLState->LastSendState = MotorState_Closed; - OutputDebugString("Motors: Close\n"); - - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket.FlowerPositions[0] = 1; - Packet.MotorPacket.FlowerPositions[1] = 1; - Packet.MotorPacket.FlowerPositions[2] = 1; - MotorCommand = Packet; - } + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket.FlowerPositions[0] = NewMotorState; + Packet.MotorPacket.FlowerPositions[1] = NewMotorState; + Packet.MotorPacket.FlowerPositions[2] = NewMotorState; - gs_data Msg = StructToData(&MotorCommand, blumen_packet); + gs_data Msg = StructToData(&Packet, blumen_packet); MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - DEBUG_SentMotorCommand(MotorCommand.MotorPacket, Context->ThreadContext); + DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); } } diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index e05d83a..c5822a8 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -137,10 +137,23 @@ typedef struct time_range internal bool SystemTimeIsInTimeRange(system_time SysTime, time_range Range) { - bool Result = (SysTime.Hour >= Range.StartHour && - SysTime.Minute >= Range.StartMinute && - SysTime.Hour <= Range.EndHour && - SysTime.Minute <= Range.EndMinute); + bool Result = false; + if (SysTime.Hour >= Range.StartHour && + SysTime.Hour <= Range.EndHour) + { + if (SysTime.Hour == Range.StartHour) + { + Result = (SysTime.Minute >= Range.StartMinute); + } + else if (SysTime.Hour == Range.EndHour) + { + Result = (SysTime.Minute <= Range.EndMinute); + } + else + { + Result = true; + } + } return Result; } From f7aa8722a60e6b7e5df8f575a757f265442eec53 Mon Sep 17 00:00:00 2001 From: PS Date: Mon, 12 Apr 2021 17:51:14 -1000 Subject: [PATCH 080/151] Added a log into Lumenarium --- src/app/editor/interface.h | 5 +- .../editor/panels/foldhaus_panel_hierarchy.h | 2 +- .../panels/foldhaus_panel_message_log.h | 46 +++++++ .../editor/panels/foldhaus_panel_types.cpp | 3 +- src/app/editor/panels/foldhaus_panel_types.h | 1 + src/app/engine/assembly/foldhaus_assembly.cpp | 4 +- src/app/engine/foldhaus_log.h | 116 ++++++++++++++++++ src/app/foldhaus_app.cpp | 6 +- src/app/foldhaus_app.h | 9 +- src/app/foldhaus_log.h | 3 - src/app/ss_blumen_lumen/blumen_lumen.cpp | 38 +++--- 11 files changed, 195 insertions(+), 38 deletions(-) create mode 100644 src/app/editor/panels/foldhaus_panel_message_log.h create mode 100644 src/app/engine/foldhaus_log.h diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index 244e85d..854c3dc 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -1562,9 +1562,10 @@ ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows, u32 Elem s32 ScrollableElements = Max(0, ElementCount - ViewportRows); ui_widget_retained_state* ViewportState = ui_GetOrCreateRetainedState(Interface, ViewportLayout); ViewportState->ChildrenDrawOffset.x = 0; - r32 BaseOffset = Rect2Height(ViewportLayout->Bounds) - ViewportLayout->RowHeight; + r32 BaseOffset = 0; r32 ScrollPct = 1.0 - State->InitialValueR32; - r32 ScrollOffset = ScrollPct * ViewportLayout->RowHeight * ScrollableElements; + r32 RowsOffset = ScrollPct * ScrollableElements; + r32 ScrollOffset = (ViewportLayout->RowHeight - (Interface->Style.Margin.y)) * RowsOffset; ViewportState->ChildrenDrawOffset.y = BaseOffset + ScrollOffset; } diff --git a/src/app/editor/panels/foldhaus_panel_hierarchy.h b/src/app/editor/panels/foldhaus_panel_hierarchy.h index 7979512..7e616b2 100644 --- a/src/app/editor/panels/foldhaus_panel_hierarchy.h +++ b/src/app/editor/panels/foldhaus_panel_hierarchy.h @@ -30,7 +30,7 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAssemblyCallback) file_view_state* FileViewState = Panel_GetStateStruct(ReturningFrom, file_view_state); gs_file_info FileInfo = FileViewState->SelectedFile; - LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, FileInfo.Path, State->GlobalLog); + LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, FileInfo.Path, &State->GlobalLog); } GSMetaTag(panel_render); diff --git a/src/app/editor/panels/foldhaus_panel_message_log.h b/src/app/editor/panels/foldhaus_panel_message_log.h new file mode 100644 index 0000000..dbabc5c --- /dev/null +++ b/src/app/editor/panels/foldhaus_panel_message_log.h @@ -0,0 +1,46 @@ +/* date = April 12th 2021 4:47 pm */ + +#ifndef FOLDHAUS_PANEL_MESSAGE_LOG_H +#define FOLDHAUS_PANEL_MESSAGE_LOG_H + +GSMetaTag(panel_init); +GSMetaTag(panel_type_file_view); +internal void +MessageLog_Init(panel* Panel, app_state* State, context Context) +{ +} + +GSMetaTag(panel_cleanup); +GSMetaTag(panel_type_file_view); +internal void +MessageLog_Cleanup(panel* Panel, app_state* State) +{ + +} + +GSMetaTag(panel_render); +GSMetaTag(panel_type_file_view); +internal void +MessageLog_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) +{ + ui_interface* Interface = &State->Interface; + ui_widget* Layout = ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("Message Log Layout")); + + ui_BeginList(Interface, MakeString("Message Log List"), 10, State->GlobalLog.EntriesCount); + + log_buffer_iter Iter = Log_GetIter(&State->GlobalLog); + while (true) + { + log_entry* At = Iter.At; + ui_Label(Interface, At->String); + if (!LogIter_CanAdvance(Iter)) + { + break; + } + LogIter_Advance(&Iter); + } + ui_EndList(Interface); + + ui_PopLayout(Interface, MakeString("Message Log Layout")); +} +#endif //FOLDHAUS_PANEL_MESSAGE_LOG_H diff --git a/src/app/editor/panels/foldhaus_panel_types.cpp b/src/app/editor/panels/foldhaus_panel_types.cpp index ea56618..db28c35 100644 --- a/src/app/editor/panels/foldhaus_panel_types.cpp +++ b/src/app/editor/panels/foldhaus_panel_types.cpp @@ -4,7 +4,7 @@ // Creation Date: 2020-10-17 // #ifndef FOLDHAUS_PANEL_TYPES_CPP -global s32 GlobalPanelDefsCount = 7; +global s32 GlobalPanelDefsCount = 8; global panel_definition GlobalPanelDefs[] = { { "File View", 9, FileView_Init, FileView_Cleanup, FileView_Render, FileView_Commands, FileView_CommandsCount }, { "Sculpture View", 14, SculptureView_Init, SculptureView_Cleanup, SculptureView_Render, SculptureView_Commands, SculptureView_CommandsCount }, @@ -13,6 +13,7 @@ global panel_definition GlobalPanelDefs[] = { { "Hierarchy", 9, HierarchyView_Init, HierarchyView_Cleanup, HierarchyView_Render, HierarchyView_Commands, HierarchyView_CommandsCount }, { "Profiler", 8, ProfilerView_Init, ProfilerView_Cleanup, ProfilerView_Render, ProfilerView_Commands, ProfilerView_CommandsCount }, { "Assembly Debug", 14, AssemblyDebug_Init, AssemblyDebug_Cleanup, AssemblyDebug_Render, 0, 0 }, + { "Message Log", 11, MessageLog_Init, MessageLog_Cleanup, MessageLog_Render, 0, 0 }, }; #define FOLDHAUS_PANEL_TYPES_CPP #endif // FOLDHAUS_PANEL_TYPES_CPP \ No newline at end of file diff --git a/src/app/editor/panels/foldhaus_panel_types.h b/src/app/editor/panels/foldhaus_panel_types.h index 9177cdd..559bc43 100644 --- a/src/app/editor/panels/foldhaus_panel_types.h +++ b/src/app/editor/panels/foldhaus_panel_types.h @@ -12,6 +12,7 @@ enum panel_type { PanelType_HierarchyView, PanelType_ProfilerView, PanelType_AssemblyDebug, + PanelType_MessageLog }; #define FOLDHAUS_PANEL_TYPES_H #endif // FOLDHAUS_PANEL_TYPES_H \ No newline at end of file diff --git a/src/app/engine/assembly/foldhaus_assembly.cpp b/src/app/engine/assembly/foldhaus_assembly.cpp index 6264b0e..840fddc 100644 --- a/src/app/engine/assembly/foldhaus_assembly.cpp +++ b/src/app/engine/assembly/foldhaus_assembly.cpp @@ -196,7 +196,7 @@ ConstructAssemblyFromDefinition (assembly* Assembly, led_system* LedSystem) } internal assembly* -LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena* Scratch, context Context, gs_const_string Path, event_log* GlobalLog) +LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena* Scratch, context Context, gs_const_string Path, log_buffer* GlobalLog) { assembly* NewAssembly = 0; @@ -232,7 +232,7 @@ LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena } else { - LogError(GlobalLog, "Unable to load assembly file"); + Log_Error(GlobalLog, "Unable to load assembly file"); } return NewAssembly; diff --git a/src/app/engine/foldhaus_log.h b/src/app/engine/foldhaus_log.h new file mode 100644 index 0000000..a369e13 --- /dev/null +++ b/src/app/engine/foldhaus_log.h @@ -0,0 +1,116 @@ +/* date = April 12th 2021 4:25 pm */ + +#ifndef FOLDHAUS_LOG_H +#define FOLDHAUS_LOG_H + +enum log_entry_type +{ + LogEntry_Message, + LogEntry_Error, +}; + +struct log_entry +{ + log_entry_type Type; + gs_string String; +}; + +struct log_buffer +{ + gs_allocator Allocator; + + u64 EntriesCount; + u64 NextEntry; + log_entry* Entries; +}; + +struct log_buffer_iter +{ + log_buffer* Buffer; + u64 Start; + u64 IndexAt; + log_entry* At; +}; + +internal log_buffer +Log_Init(gs_allocator Allocator, u64 Count) +{ + log_buffer Result = {}; + Result.Allocator = Allocator; + Result.EntriesCount = Count; + Result.Entries = AllocatorAllocArray(Allocator, log_entry, Result.EntriesCount); + + for (u32 i = 0; i < Result.EntriesCount; i++) + { + Result.Entries[i].String = AllocatorAllocString(Allocator, 512); + } + + return Result; +} + +internal u64 +Log_GetNextIndex(log_buffer Log, u64 At) +{ + u64 Result = At + 1; + if (Result >= Log.EntriesCount) + { + Result = 0; + } + return Result; +} + +internal log_entry* +Log_TakeNextEntry(log_buffer* Log) +{ + log_entry* Result = Log->Entries + Log->NextEntry; + Log->NextEntry = Log_GetNextIndex(*Log, Log->NextEntry); + return Result; +} + +#define Log_Message(log, fmt, ...) Log_PrintF(log, LogEntry_Message, fmt, __VA_ARGS__) +#define Log_Error(log, fmt, ...) Log_PrintF(log, LogEntry_Error, fmt, __VA_ARGS__) +internal void +Log_PrintF(log_buffer* Log, log_entry_type Type, char* Format, ...) +{ + log_entry* NextEntry = Log_TakeNextEntry(Log); + + va_list Args; + va_start(Args, Format); + NextEntry->String.Length = 0; + NextEntry->Type = Type; + PrintFArgsList(&NextEntry->String, Format, Args); + NullTerminate(&NextEntry->String); + va_end(Args); + +#if DEBUG + OutputDebugStringA(NextEntry->String.Str); +#endif +} + +internal log_buffer_iter +Log_GetIter(log_buffer* Buffer) +{ + log_buffer_iter Result = {}; + Result.Buffer = Buffer; + Result.Start = Buffer->NextEntry; + Result.IndexAt = Result.Start; + Result.At = Result.Buffer->Entries + Result.IndexAt; + return Result; +} + +internal bool +LogIter_CanAdvance(log_buffer_iter Iter) +{ + u64 Next = Log_GetNextIndex(*Iter.Buffer, Iter.IndexAt); + bool Result = Next != Iter.Start; + return Result; +} + +internal void +LogIter_Advance(log_buffer_iter* Iter) +{ + Iter->IndexAt = Log_GetNextIndex(*Iter->Buffer, Iter->IndexAt); + Iter->At = Iter->Buffer->Entries + Iter->IndexAt; +} + +#endif //FOLDHAUS_LOG_H diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index e71f40b..652a0ac 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -11,10 +11,10 @@ RELOAD_STATIC_DATA(ReloadStaticData) { GlobalDebugServices = DebugServices; - if (AppReady) { app_state* State = (app_state*)Context.MemoryBase; + GlobalLogBuffer = &State->GlobalLog; State->PanelSystem.PanelDefs = GlobalPanelDefs; State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount; @@ -39,7 +39,7 @@ INITIALIZE_APPLICATION(InitializeApplication) State->Transient = Context->ThreadContext.Transient; State->Assemblies = AssemblyArray_Create(8, &State->Permanent); - State->GlobalLog = PushStruct(&State->Permanent, event_log); + State->GlobalLog = Log_Init(Context->ThreadContext.Allocator, 32); State->CommandQueue = CommandQueue_Create(&State->Permanent, 32); @@ -97,7 +97,7 @@ INITIALIZE_APPLICATION(InitializeApplication) SplitPanel(LeftPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context); panel* Profiler = LeftPanel->Right; - Panel_SetType(Profiler, &State->PanelSystem, PanelType_ProfilerView, State, *Context); + Panel_SetType(Profiler, &State->PanelSystem, PanelType_MessageLog, State, *Context); panel* Hierarchy = LeftPanel->Left; Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, *Context); diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index c0e425d..3a7f70c 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -11,7 +11,7 @@ #include "engine/foldhaus_serializer.h" #include "../gs_libs/gs_font.h" -#include "foldhaus_log.h" +#include "engine/foldhaus_log.h" #include "editor/interface.h" @@ -48,6 +48,8 @@ typedef struct panel panel; #include "ss_blumen_lumen/phrase_hue_map.h" #include "ss_blumen_lumen/blumen_lumen.h" +global log_buffer* GlobalLogBuffer; + struct app_state { gs_memory_arena Permanent; @@ -61,7 +63,7 @@ struct app_state assembly_array Assemblies; assembly_debug_state AssemblyDebugState; animation_system AnimationSystem; - event_log* GlobalLog; + log_buffer GlobalLog; animation_pattern_array Patterns; // Interface @@ -93,7 +95,7 @@ LoadAssembly(gs_const_string Path, app_state* State, context Context) State->Transient, Context, Path, - State->GlobalLog); + &State->GlobalLog); } #include "engine/user_space.cpp" @@ -117,6 +119,7 @@ EndCurrentOperationMode(app_state* State) #include "editor/panels/foldhaus_panel_animation_timeline.h" #include "editor/panels/foldhaus_panel_hierarchy.h" #include "editor/panels/foldhaus_panel_assembly_debug.h" +#include "editor/panels/foldhaus_panel_message_log.h" #include "editor/panels/foldhaus_panel_types.cpp" diff --git a/src/app/foldhaus_log.h b/src/app/foldhaus_log.h index 5d1c936..dea66da 100644 --- a/src/app/foldhaus_log.h +++ b/src/app/foldhaus_log.h @@ -42,8 +42,5 @@ PushLogEntry(event_log* Log, gs_string Message, log_entry_type Type) NewEntry->Type = Type; } - - - #define FOLDHAUS_LOG_H #endif // FOLDHAUS_LOG_H \ No newline at end of file diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index fc21fa3..25c1c5f 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -69,14 +69,11 @@ DEBUG_AppendText(gs_string Str, gs_thread_context Ctx) internal void DEBUG_SentMotorCommand(motor_packet Packet, gs_thread_context Ctx) { - gs_string Str = PushStringF(Ctx.Transient, 256, "Motor Command Sent\nRequested Positions: %d %d %d\n", - Packet.FlowerPositions[0], - Packet.FlowerPositions[1], - Packet.FlowerPositions[2]); - DEBUG_AppendText(Str, Ctx); - - NullTerminate(&Str); - OutputDebugStringA(Str.Str); + Log_Message(GlobalLogBuffer, + "Motor Command Sent\nRequested Positions: %d %d %d\n", + Packet.FlowerPositions[0], + Packet.FlowerPositions[1], + Packet.FlowerPositions[2]); } internal void @@ -90,25 +87,20 @@ DEBUG_ReceivedMotorPositions(motor_packet NewPos, if (PosChanged) { - gs_string Str = PushStringF(Ctx.Transient, 256, "Motor Status Received\nCurrent Positions: %d %d %d\n", - NewPos.FlowerPositions[0], - NewPos.FlowerPositions[1], - NewPos.FlowerPositions[2]); - DEBUG_AppendText(Str, Ctx); - - NullTerminate(&Str); - OutputDebugStringA(Str.Str); + Log_Message(GlobalLogBuffer, + "Motor Status Received\nCurrent Positions: %d %d %d\n", + NewPos.FlowerPositions[0], + NewPos.FlowerPositions[1], + NewPos.FlowerPositions[2]); } } internal void DEBUG_ReceivedTemperature(temp_packet Temp, gs_thread_context Ctx) { - gs_string TempStr = PushStringF(Ctx.Transient, 256, - "\nTemperature: %d\n", - Temp.Temperature); - NullTerminate(&TempStr); - OutputDebugStringA(TempStr.Str); + Log_Message(GlobalLogBuffer, + "\nTemperature: %d\n", + Temp.Temperature); } internal void @@ -136,13 +128,13 @@ BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) Data->IsConnected = false; if (SocketHandleIsValid(ListenSocket)) { - OutputDebugStringA("Disconnected from Python Server\n"); + Log_Message(GlobalLogBuffer, "Disconnected from Python Server\n"); CloseSocket(Data->SocketManager, ListenSocket); } ListenSocket = CreateSocket(Data->SocketManager, "127.0.0.1", "20185"); if (ListenSocket.Index != 0) { - OutputDebugStringA("Connected to Python Server\n"); + Log_Message(GlobalLogBuffer, "Connected to Python Server\n"); Data->IsConnected = true; } } From 5a1c37a23eeed70a4ded76d718db6bf9c2ea43e4 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Tue, 13 Apr 2021 03:00:14 -0400 Subject: [PATCH 081/151] Finished motor time of day controls --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 39 ++++++++++++++++-------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 25c1c5f..294e9b8 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -77,21 +77,22 @@ DEBUG_SentMotorCommand(motor_packet Packet, gs_thread_context Ctx) } internal void -DEBUG_ReceivedMotorPositions(motor_packet NewPos, - motor_packet LastPos, +DEBUG_ReceivedMotorPositions(blumen_lumen_state* BLState, + motor_status_packet NewPos, gs_thread_context Ctx) { - bool PosChanged = (LastPos.FlowerPositions[0] != NewPos.FlowerPositions[0] || - LastPos.FlowerPositions[1] != NewPos.FlowerPositions[1] || - LastPos.FlowerPositions[2] != NewPos.FlowerPositions[2]); + motor_packet LastPos = BLState->LastKnownMotorState; + bool PosChanged = (LastPos.FlowerPositions[0] != NewPos.Pos.FlowerPositions[0] || + LastPos.FlowerPositions[1] != NewPos.Pos.FlowerPositions[1] || + LastPos.FlowerPositions[2] != NewPos.Pos.FlowerPositions[2]); if (PosChanged) { Log_Message(GlobalLogBuffer, "Motor Status Received\nCurrent Positions: %d %d %d\n", - NewPos.FlowerPositions[0], - NewPos.FlowerPositions[1], - NewPos.FlowerPositions[2]); + NewPos.Pos.FlowerPositions[0], + NewPos.Pos.FlowerPositions[1], + NewPos.Pos.FlowerPositions[2]); } } @@ -421,10 +422,10 @@ BlumenLumen_CustomInit(app_state* State, context Context) internal void BlumenLumen_UpdateMotorState(blumen_lumen_state* BLState, motor_status_packet Motor, context Context) { - motor_packet CurrPos = Motor.Pos; - motor_packet LastPos = BLState->LastKnownMotorState; - DEBUG_ReceivedMotorPositions(LastPos, Motor.Pos, Context.ThreadContext); + DEBUG_ReceivedMotorPositions(BLState, Motor, Context.ThreadContext); + motor_packet LastPos = BLState->LastKnownMotorState; + motor_packet CurrPos = Motor.Pos; for (u32 i = 0; i < BL_FLOWER_COUNT; i++) { if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i]) @@ -635,6 +636,15 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) ShouldSendCurrentState = true; } + if (ShouldSendCurrentState) + { + BLState->LastSendTime = Context->SystemTime_Current; + BLState->LastSendState = NewMotorState; + Log_Message(GlobalLogBuffer, + "Would send motor state: %s", + NewMotorState == MotorState_Closed ? "Closed" : "Open"); + } + ShouldSendCurrentState = false; if (ShouldSendCurrentState) { BLState->LastSendTime = Context->SystemTime_Current; @@ -754,7 +764,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) { BLState->LastStatusUpdateTime = Context->SystemTime_Current; - OutputDebugString("Attempting to Send Lumenarium Status\n"); + Log_Message(GlobalLogBuffer, "Attempting to Send Lumenarium Status\n"); blumen_packet Packet = {}; Packet.Type = PacketType_LumenariumStatus; @@ -874,6 +884,11 @@ US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) case MotorState_Open: { StateStr = MakeString("Open"); } break; + + default: + { + StateStr = MakeString("Invalid Value"); + } break; } ui_Label(I, StateStr); From a983644f6b89fb08448567f76086fe4ff468f9db Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Tue, 13 Apr 2021 03:12:59 -0400 Subject: [PATCH 082/151] Began convertin all messaging to the logging system --- src/app/engine/assembly/foldhaus_assembly.cpp | 2 +- src/app/foldhaus_app.h | 3 --- src/app/foldhaus_platform.h | 3 +++ src/app/platform_win32/win32_foldhaus.cpp | 24 ++++++++++--------- .../platform_win32/win32_foldhaus_serial.h | 2 +- src/app/ss_blumen_lumen/blumen_lumen.cpp | 9 ++----- src/app/ss_blumen_lumen/phrase_hue_map.h | 2 +- 7 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/app/engine/assembly/foldhaus_assembly.cpp b/src/app/engine/assembly/foldhaus_assembly.cpp index 840fddc..6e69f4b 100644 --- a/src/app/engine/assembly/foldhaus_assembly.cpp +++ b/src/app/engine/assembly/foldhaus_assembly.cpp @@ -226,7 +226,7 @@ LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena ErrorAt != 0; ErrorAt = ErrorAt->Next) { - OutputDebugString(ErrorAt->Message.Str); + Log_Error(GlobalLogBuffer, ErrorAt->Message.Str); } } diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index 3a7f70c..28227b7 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -11,7 +11,6 @@ #include "engine/foldhaus_serializer.h" #include "../gs_libs/gs_font.h" -#include "engine/foldhaus_log.h" #include "editor/interface.h" @@ -48,8 +47,6 @@ typedef struct panel panel; #include "ss_blumen_lumen/phrase_hue_map.h" #include "ss_blumen_lumen/blumen_lumen.h" -global log_buffer* GlobalLogBuffer; - struct app_state { gs_memory_arena Permanent; diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 9e63d61..adbc19a 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -29,6 +29,9 @@ Handle_IsValid(handle Handle) #include "..\gs_libs\gs_string.h" #include "..\gs_libs\gs_csv.h" +#include "engine/foldhaus_log.h" +global log_buffer* GlobalLogBuffer; + #include "foldhaus_debug.h" global debug_services* GlobalDebugServices; diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 19653ac..1e25934 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -397,7 +397,8 @@ Win32_SendAddressedDataBuffer(gs_thread_context Context, addressed_data_buffer* else { #if 0 - OutputDebugStringA("Skipping data buffer because its COM Port isn't set"); + Log_Message(GlobalLogBuffer, + "Skipping data buffer because its COM Port isn't set"); #endif } }break; @@ -422,11 +423,11 @@ ReloadAndLinkDLL(win32_dll_refresh* DLL, context* Context, gs_work_queue* WorkQu SetApplicationLinks(Context, *DLL, WorkQueue); Context->ReloadStaticData(*Context, GlobalDebugServices, AppReady); Success = true; - OutputDebugStringA("Reloaded DLL\n"); + Log_Message(GlobalLogBuffer, "Reloaded DLL\n"); } else if(ShouldError) { - OutputDebugStringA("Unable to load application DLL at startup.\nAborting\n"); + Log_Error(GlobalLogBuffer, "Unable to load application DLL at startup.\nAborting\n"); } return Success; } @@ -490,9 +491,9 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) if (WorkingDirectory.Length > 0) { - OutputDebugStringA("Setting Working Directory\n"); - OutputDebugStringA(WorkingDirectory.Str); - OutputDebugStringA("\n"); + Log_Message(GlobalLogBuffer, + "Setting Working Directory \n%S \n", + WorkingDirectory.ConstString); Result = SetCurrentDirectory(WorkingDirectory.Str); if (!Result) { @@ -502,7 +503,7 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) } else { - OutputDebugStringA("Error, no data folder found\n"); + Log_Error(GlobalLogBuffer, "Error, no data folder found\n"); } return Result; @@ -601,7 +602,7 @@ WinMain ( gs_const_string Args = ConstString((char*)CmdLineArgs); if (StringsEqual(Args, ConstString("-headless"))) { - OutputDebugStringA("Running Headless\n"); + Log_Message(GlobalLogBuffer, "Running Headless\n"); Context.Headless = true; } @@ -707,9 +708,10 @@ WinMain ( u64 NanosElapsed = Context.SystemTime_Current.NanosSinceEpoch - StartTime.NanosSinceEpoch; r64 SecondsElapsed = (r64)NanosElapsed * NanosToSeconds; - PrintF(&T, "%lld %f Seconds\n", NanosElapsed, SecondsElapsed); - NullTerminate(&T); - OutputDebugStringA(T.Str); + Log_Message(GlobalLogBuffer, + "%lld %f Seconds \n", + NanosElapsed, + SecondsElapsed); #endif } diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index 23d8cdf..1609013 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -212,7 +212,7 @@ Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer) } else { - OutputDebugStringA("Error: Unable to write to port\n"); + Log_Error(GlobalLogBuffer, "Error: Unable to write to port\n"); s32 Error = GetLastError(); switch (Error) { diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 294e9b8..84a1c89 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -444,10 +444,7 @@ BlumenLumen_ApplyNextHotHue(blumen_lumen_state* BLState, context Context, gs_str // if we are in standard color mode, shift all flowers to the new color // otherwise, only shift the next flower in the sequence to the new color phrase_hue NewHue = BLState->NextHotHue; - - PrintF(DebugStr, "Switching To: %S\n", NewHue.Phrase); - NullTerminate(DebugStr); - OutputDebugString(DebugStr->Str); + Log_Message(GlobalLogBuffer, "Switching To: %S\n", NewHue.Phrase); if (BLState->PatternMode == BlumenPattern_Standard || @@ -499,9 +496,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) bool IsntInPhraseReceptionMode = !BLState->InPhraseReceptionMode; if (IsLonger || IsntInPhraseReceptionMode) { - PrintF(&DebugStr, "Queuing: %S\n", NewHue.Phrase); - NullTerminate(&DebugStr); - OutputDebugString(DebugStr.Str); + Log_Message(GlobalLogBuffer, "Queueing: %S\n", NewHue.Phrase); BLState->NextHotHue = NewHue; if (SecondsElapsed(BLState->TimePhraseReceptionBegan, diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h index 89ec6e0..3b2af84 100644 --- a/src/app/ss_blumen_lumen/phrase_hue_map.h +++ b/src/app/ss_blumen_lumen/phrase_hue_map.h @@ -127,7 +127,7 @@ CreateHueFromString(gs_const_string Str) parse_float_result Parsed = ValidateAndParseFloat(Str); if (!Parsed.Success) { - OutputDebugString("Failed to Parse CSV Float\n"); + Log_Error(GlobalLogBuffer, "Failed to Parse CSV Float\n"); Parsed.Value = 0.0; } Result.HSV = v4{ (r32)Parsed.Value, 1, 1, 1 }; From 495e760306a3094b8622e0da95ffcbd76fbae3ed Mon Sep 17 00:00:00 2001 From: PS Date: Mon, 12 Apr 2021 21:27:25 -1000 Subject: [PATCH 083/151] Finished replacing logging with the new system --- .../editor/panels/foldhaus_panel_hierarchy.h | 2 +- .../panels/foldhaus_panel_message_log.h | 4 +-- src/app/engine/foldhaus_log.h | 25 +++++++++++-------- src/app/foldhaus_app.cpp | 6 ++--- src/app/foldhaus_app.h | 3 +-- src/app/foldhaus_platform.h | 2 +- src/app/platform_win32/win32_foldhaus.cpp | 7 +++--- src/app/platform_win32/win32_foldhaus_utils.h | 3 +-- .../win32_foldhaus_work_queue.h | 4 +-- src/sculpture_gen/gen_blumen_lumen.cpp | 5 ++++ src/serial_monitor/first.cpp | 9 +++++-- 11 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/app/editor/panels/foldhaus_panel_hierarchy.h b/src/app/editor/panels/foldhaus_panel_hierarchy.h index 7e616b2..8731660 100644 --- a/src/app/editor/panels/foldhaus_panel_hierarchy.h +++ b/src/app/editor/panels/foldhaus_panel_hierarchy.h @@ -30,7 +30,7 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAssemblyCallback) file_view_state* FileViewState = Panel_GetStateStruct(ReturningFrom, file_view_state); gs_file_info FileInfo = FileViewState->SelectedFile; - LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, FileInfo.Path, &State->GlobalLog); + LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, FileInfo.Path, GlobalLogBuffer); } GSMetaTag(panel_render); diff --git a/src/app/editor/panels/foldhaus_panel_message_log.h b/src/app/editor/panels/foldhaus_panel_message_log.h index dbabc5c..76d7d59 100644 --- a/src/app/editor/panels/foldhaus_panel_message_log.h +++ b/src/app/editor/panels/foldhaus_panel_message_log.h @@ -26,9 +26,9 @@ MessageLog_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Render ui_interface* Interface = &State->Interface; ui_widget* Layout = ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("Message Log Layout")); - ui_BeginList(Interface, MakeString("Message Log List"), 10, State->GlobalLog.EntriesCount); + ui_BeginList(Interface, MakeString("Message Log List"), 10, GlobalLogBuffer->EntriesCount); - log_buffer_iter Iter = Log_GetIter(&State->GlobalLog); + log_buffer_iter Iter = Log_GetIter(GlobalLogBuffer); while (true) { log_entry* At = Iter.At; diff --git a/src/app/engine/foldhaus_log.h b/src/app/engine/foldhaus_log.h index a369e13..8472701 100644 --- a/src/app/engine/foldhaus_log.h +++ b/src/app/engine/foldhaus_log.h @@ -67,24 +67,29 @@ Log_TakeNextEntry(log_buffer* Log) return Result; } +internal void +Log_PrintFVarArgs(log_buffer* Log, log_entry_type Type, char* Format, va_list Args) +{ + log_entry* NextEntry = Log_TakeNextEntry(Log); + NextEntry->String.Length = 0; + NextEntry->Type = Type; + PrintFArgsList(&NextEntry->String, Format, Args); + NullTerminate(&NextEntry->String); + +#if DEBUG + OutputDebugStringA(NextEntry->String.Str); +#endif +} + #define Log_Message(log, fmt, ...) Log_PrintF(log, LogEntry_Message, fmt, __VA_ARGS__) #define Log_Error(log, fmt, ...) Log_PrintF(log, LogEntry_Error, fmt, __VA_ARGS__) internal void Log_PrintF(log_buffer* Log, log_entry_type Type, char* Format, ...) { - log_entry* NextEntry = Log_TakeNextEntry(Log); - va_list Args; va_start(Args, Format); - NextEntry->String.Length = 0; - NextEntry->Type = Type; - PrintFArgsList(&NextEntry->String, Format, Args); - NullTerminate(&NextEntry->String); + Log_PrintFVarArgs(Log, Type, Format, Args); va_end(Args); - -#if DEBUG - OutputDebugStringA(NextEntry->String.Str); -#endif } internal log_buffer_iter diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 652a0ac..89bf4fe 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -11,10 +11,10 @@ RELOAD_STATIC_DATA(ReloadStaticData) { GlobalDebugServices = DebugServices; + GlobalLogBuffer = LogBuffer; if (AppReady) { app_state* State = (app_state*)Context.MemoryBase; - GlobalLogBuffer = &State->GlobalLog; State->PanelSystem.PanelDefs = GlobalPanelDefs; State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount; @@ -39,8 +39,6 @@ INITIALIZE_APPLICATION(InitializeApplication) State->Transient = Context->ThreadContext.Transient; State->Assemblies = AssemblyArray_Create(8, &State->Permanent); - State->GlobalLog = Log_Init(Context->ThreadContext.Allocator, 32); - State->CommandQueue = CommandQueue_Create(&State->Permanent, 32); animation_system_desc AnimSysDesc = {}; @@ -78,7 +76,7 @@ INITIALIZE_APPLICATION(InitializeApplication) State->Modes = OperationModeSystemInit(&State->Permanent, Context->ThreadContext); - ReloadStaticData(*Context, GlobalDebugServices, true); + ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, true); US_CustomInit(&State->UserSpaceDesc, State, *Context); if (!Context->Headless) diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index 28227b7..4241794 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -60,7 +60,6 @@ struct app_state assembly_array Assemblies; assembly_debug_state AssemblyDebugState; animation_system AnimationSystem; - log_buffer GlobalLog; animation_pattern_array Patterns; // Interface @@ -92,7 +91,7 @@ LoadAssembly(gs_const_string Path, app_state* State, context Context) State->Transient, Context, Path, - &State->GlobalLog); + GlobalLogBuffer); } #include "engine/user_space.cpp" diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index adbc19a..0f80d4d 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -77,7 +77,7 @@ typedef INITIALIZE_APPLICATION(initialize_application); #define UPDATE_AND_RENDER(name) void name(context* Context, input_queue InputQueue, render_command_buffer* RenderBuffer, addressed_data_buffer_list* OutputData) typedef UPDATE_AND_RENDER(update_and_render); -#define RELOAD_STATIC_DATA(name) void name(context Context, debug_services* DebugServices, bool AppReady) +#define RELOAD_STATIC_DATA(name) void name(context Context, debug_services* DebugServices, log_buffer* LogBuffer, bool AppReady) typedef RELOAD_STATIC_DATA(reload_static_data); #define CLEANUP_APPLICATION(name) void name(context Context, addressed_data_buffer_list* OutputData) diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 1e25934..bb3edde 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -332,8 +332,7 @@ DebugPrint (char* Format, ...) gs_string StringBuffer = MakeString(Buffer, 256); va_list Args; va_start(Args, Format); - PrintF(&StringBuffer, Format, Args); - OutputDebugStringA(Buffer); + Log_PrintFVarArgs(GlobalLogBuffer, LogEntry_Message, Format, Args); va_end(Args); } @@ -421,7 +420,7 @@ ReloadAndLinkDLL(win32_dll_refresh* DLL, context* Context, gs_work_queue* WorkQu if (HotLoadDLL(DLL)) { SetApplicationLinks(Context, *DLL, WorkQueue); - Context->ReloadStaticData(*Context, GlobalDebugServices, AppReady); + Context->ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, AppReady); Success = true; Log_Message(GlobalLogBuffer, "Reloaded DLL\n"); } @@ -586,6 +585,8 @@ WinMain ( ) { gs_thread_context ThreadContext = Win32CreateThreadContext(); + GlobalLogBuffer = AllocatorAllocStruct(ThreadContext.Allocator, log_buffer); + *GlobalLogBuffer = Log_Init(ThreadContext.Allocator, 32); gs_allocator_debug AllocDebug = {}; AllocDebug.AllocationsCountMax = 4096; diff --git a/src/app/platform_win32/win32_foldhaus_utils.h b/src/app/platform_win32/win32_foldhaus_utils.h index 5392c9f..dc71929 100644 --- a/src/app/platform_win32/win32_foldhaus_utils.h +++ b/src/app/platform_win32/win32_foldhaus_utils.h @@ -54,8 +54,7 @@ PrintLastError_(char* File, u32 Line) char DebugStringData[256]; gs_string DebugString = MakeString(DebugStringData, 0, 256); u32 Error = GetLastError(); - PrintF(&DebugString, "%s Line %d: Win32 Error %d\n\0", File, Line, Error); - OutputDebugStringA(DebugString.Str); + Log_Error(GlobalLogBuffer, "%s Line %d: Win32 Error %d\n\0", File, Line, Error); } diff --git a/src/app/platform_win32/win32_foldhaus_work_queue.h b/src/app/platform_win32/win32_foldhaus_work_queue.h index 5f9ce27..f35259e 100644 --- a/src/app/platform_win32/win32_foldhaus_work_queue.h +++ b/src/app/platform_win32/win32_foldhaus_work_queue.h @@ -71,9 +71,7 @@ PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue) gs_string DebugString = MakeString((char*)malloc(256), 256); for (u32 i = 0; i < Queue->JobsCount; i++) { - PrintF(&DebugString, "%d %s\n", i, Queue->Jobs[i].JobName); - NullTerminate(&DebugString); - OutputDebugStringA(DebugString.Str); + Log_Message(GlobalLogBuffer, "%d %s \n", i, Queue->Jobs[i].JobName); } } #endif diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index a93f79d..d7f4cb6 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -10,6 +10,9 @@ #include "../gs_libs/gs_types.h" #include "../gs_libs/gs_types.cpp" +#include "../app/engine/foldhaus_log.h" +global log_buffer* GlobalLogBuffer; + #include "../app/platform_win32/win32_foldhaus_utils.h" #include "../app/platform_win32/win32_foldhaus_memory.h" #include "../app/platform_win32/win32_foldhaus_fileio.h" @@ -174,6 +177,8 @@ FlowerStripToChannel(u8 Flower, u8 Channel) int main(int ArgCount, char** Args) { gs_thread_context Ctx = Win32CreateThreadContext(); + GlobalLogBuffer = AllocatorAllocStruct(Ctx.Allocator, log_buffer); + *GlobalLogBuffer = Log_Init(Ctx.Allocator, 32); gs_string OutputBuffer0 = PushString(Ctx.Transient, MB(4)); gs_string OutputBuffer1 = PushString(Ctx.Transient, MB(4)); diff --git a/src/serial_monitor/first.cpp b/src/serial_monitor/first.cpp index 4ef8ccc..d2e47b0 100644 --- a/src/serial_monitor/first.cpp +++ b/src/serial_monitor/first.cpp @@ -6,13 +6,16 @@ #ifndef FIRST_CPP +#include +#include + #include "../gs_libs/gs_types.h" #include "../gs_libs/gs_types.cpp" +#include "../app/engine/foldhaus_log.h" +global log_buffer* GlobalLogBuffer; #define DEBUG_TRACK_FUNCTION -#include -#include //#include "../app/foldhaus_platform.h" //#include "../gs_libs/gs_win32.cpp" @@ -92,6 +95,8 @@ CreateMessage(gs_data* Data, u8 Count) int main(int ArgCount, char** Args) { gs_thread_context Ctx = Win32CreateThreadContext(); + GlobalLogBuffer = AllocatorAllocStruct(Ctx.Allocator, log_buffer); + *GlobalLogBuffer = Log_Init(Ctx.Allocator, 32); HANDLE SerialHandle = Win32SerialPort_Open("\\\\.\\COM9", Ctx.Transient); Win32SerialPort_SetState(SerialHandle, 2000000, 8, 0, 1); From 692dcc81b3b966f0ef62089c62e321017d8d1dcd Mon Sep 17 00:00:00 2001 From: PS Date: Mon, 12 Apr 2021 21:34:05 -1000 Subject: [PATCH 084/151] Actually removed all OutputDebugStringA calls except in Log_PrintFVarArgs --- .../platform_win32/win32_foldhaus_serial.h | 2 +- .../platform_win32/win32_foldhaus_socket.h | 35 +++++++++++-------- src/app/platform_win32/win32_foldhaus_utils.h | 3 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index 1609013..567811b 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -207,7 +207,7 @@ Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer) Success = (BytesWritten == Buffer.Size); if (!Success) { - OutputDebugString("Error: Entire buffer not written.\n"); + Log_Error(GlobalLogBuffer, "Error: Entire buffer not written.\n"); } } else diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index 1b449bd..ca36cac 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -245,11 +245,11 @@ Win32SocketPeek(platform_socket_manager* Manager, platform_socket* Socket) char Temp[4]; u32 TempSize = 4; - //OutputDebugString("Pre Peek..."); + //Log_Message(GlobalLogBuffer, "Pre Peek..."); //s32 BytesQueued = recv(*Win32Sock, Temp, TempSize, Flags); u_long BytesQueued = 0; ioctlsocket(*Win32Sock, FIONREAD, &BytesQueued); - //OutputDebugString("Post Peek\n"); + //Log_Message(GlobalLogBuffer, "Post Peek\n"); if (BytesQueued != SOCKET_ERROR) { @@ -327,7 +327,7 @@ Win32SocketSend(platform_socket_manager* Manager, platform_socket* Socket, u32 A s32 LengthSent = sendto(*Win32Sock, (char*)Data.Memory, Data.Size, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); - //OutputDebugString("Attempting To Send Network Data: "); + //Log_Message(GlobalLogBuffer, "Attempting To Send Network Data: "); if (LengthSent == SOCKET_ERROR) { s32 Error = WSAGetLastError(); @@ -338,7 +338,7 @@ Win32SocketSend(platform_socket_manager* Manager, platform_socket* Socket, u32 A // NOTE(PS): This covers non-blocking sockets // In this case the message should be tried again LengthSent = 0; - //OutputDebugString("Not sent, buffered\n"); + //Log_Message(GlobalLogBuffer, "Not sent, buffered\n"); }break; case WSAECONNABORTED: @@ -348,8 +348,8 @@ Win32SocketSend(platform_socket_manager* Manager, platform_socket* Socket, u32 A { if (CloseSocket(Manager, Socket)) { + Log_Error(GlobalLogBuffer, "Error: %d\n", Error); Error = 0; - OutputDebugString("Error\n"); } }break; @@ -358,7 +358,7 @@ Win32SocketSend(platform_socket_manager* Manager, platform_socket* Socket, u32 A } else { - OutputDebugString("Sent\n"); + Log_Message(GlobalLogBuffer, "Sent\n"); } return LengthSent; @@ -397,23 +397,28 @@ Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, c s32 LengthSent = sendto(Socket->Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); - OutputDebugString("Attempting To Send Network Data: "); + Log_Message(GlobalLogBuffer, "Attempting To Send Network Data: "); if (LengthSent == SOCKET_ERROR) { s32 Error = WSAGetLastError(); - OutputDebugString("Error\n"); - if (Error == 10051) + switch (Error) { - } - else - { - // TODO(Peter): :ErrorLogging - InvalidCodePath; + case WSAENETUNREACH: + { + Log_Message(GlobalLogBuffer, + "Non Critical Error: WSAENETUNREACH \n"); + } break; + + default: + { + Log_Error(GlobalLogBuffer, "Error: %d \n", Error); + InvalidCodePath; + } break; } } else { - OutputDebugString("Sent\n"); + Log_Message(GlobalLogBuffer, "Sent\n"); } return LengthSent; diff --git a/src/app/platform_win32/win32_foldhaus_utils.h b/src/app/platform_win32/win32_foldhaus_utils.h index dc71929..c1a4579 100644 --- a/src/app/platform_win32/win32_foldhaus_utils.h +++ b/src/app/platform_win32/win32_foldhaus_utils.h @@ -43,8 +43,7 @@ Error Code: %d\n DEBUG_PRINT(Win32DebugPrint) { - Assert(IsNullTerminated(Message)); - OutputDebugStringA(Message.Str); + Log_Message(GlobalLogBuffer, "%S", Message); } #define PrintLastError() PrintLastError_(__FILE__, __LINE__) From 176c543160e054213ff248a7697f7c8dba8e2e68 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Tue, 13 Apr 2021 03:37:11 -0400 Subject: [PATCH 085/151] removed testing code to prevent sending motor states --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 84a1c89..c058497 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -631,15 +631,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) ShouldSendCurrentState = true; } - if (ShouldSendCurrentState) - { - BLState->LastSendTime = Context->SystemTime_Current; - BLState->LastSendState = NewMotorState; - Log_Message(GlobalLogBuffer, - "Would send motor state: %s", - NewMotorState == MotorState_Closed ? "Closed" : "Open"); - } - ShouldSendCurrentState = false; if (ShouldSendCurrentState) { BLState->LastSendTime = Context->SystemTime_Current; @@ -650,9 +641,9 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Packet.MotorPacket.FlowerPositions[0] = NewMotorState; Packet.MotorPacket.FlowerPositions[1] = NewMotorState; Packet.MotorPacket.FlowerPositions[2] = NewMotorState; - gs_data Msg = StructToData(&Packet, blumen_packet); MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); } } From 27ad4cd5792fc8e1a72a2fc4f069893fc6f879f8 Mon Sep 17 00:00:00 2001 From: PS Date: Mon, 19 Apr 2021 18:58:51 -1000 Subject: [PATCH 086/151] updated awaken animation for unveiling --- .../data/blumen_animations/awaken.foldanim | 30 +++++++++++++++++-- src/app/patterns/blumen_patterns.h | 10 +++++++ src/app/ss_blumen_lumen/blumen_lumen.cpp | 2 ++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/app_run_tree/data/blumen_animations/awaken.foldanim b/app_run_tree/data/blumen_animations/awaken.foldanim index efd7194..b82e48f 100644 --- a/app_run_tree/data/blumen_animations/awaken.foldanim +++ b/app_run_tree/data/blumen_animations/awaken.foldanim @@ -1,7 +1,7 @@ lumenarium_animation_file; animation_name: "awaken"; layers_count: 3; -blocks_count: 5; +blocks_count: 8; playable_range:{ min: 0; max: 7200; @@ -23,7 +23,7 @@ layers:{ blocks:{ block:{ frame_range:{ - min: 94; + min: 0; max: 1363; }; layer_index: 0; @@ -39,7 +39,7 @@ blocks:{ }; block:{ frame_range:{ - min: 71; + min: 5525; max: 7200; }; layer_index: 2; @@ -61,4 +61,28 @@ blocks:{ layer_index: 1; animation_name: "Pattern_StemSolid"; }; + block:{ + frame_range:{ + min: 0; + max: 1917; + }; + layer_index: 2; + animation_name: "Pattern_Blue"; + }; + block:{ + frame_range:{ + min: 1823; + max: 3803; + }; + layer_index: 2; + animation_name: "Pattern_RainbowLoadingBar"; + }; + block:{ + frame_range:{ + min: 3670; + max: 5720; + }; + layer_index: 2; + animation_name: "Pattern_Rainbow"; + }; }; diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 3023506..65283c5 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -865,5 +865,15 @@ Pattern_RainbowLoadingBar(led_buffer* Leds, led_buffer_range Range, assembly Ass } } +internal void +Pattern_Blue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + pixel Blue = pixel{0, 0, 255}; + for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++) + { + Leds->Colors[LedIndex] = Blue; + } +} + #define BLUMEN_PATTERNS_H #endif // BLUMEN_PATTERNS_H \ No newline at end of file diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index c058497..9bde8d9 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -215,6 +215,8 @@ BlumenLumen_LoadPatterns(app_state* State) Patterns_PushPattern(Patterns, Pattern_GrowFadeMask, PATTERN_MULTITHREADED); Patterns_PushPattern(Patterns, Pattern_RainbowLoadingBar, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED); } internal void From c1ac7173db0c150527974d2553e2adff71bbaa0d Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 22 Apr 2021 12:44:55 -1000 Subject: [PATCH 087/151] Updating time / led brightness logging and fixed a time range checking issue --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 49 ++++++++++++++----- src/app/ss_blumen_lumen/blumen_lumen.h | 62 +++++++++++++++++++----- 2 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 9bde8d9..6e0995c 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -314,6 +314,24 @@ BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Con AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent); + time_range RangeIn = {}; + if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, + LedOnTimes, + LedOnTimesCount, + &RangeIn)) + { + AppendPrintF(&FileStr, "\tIn Leds-On Time Range: ( %d:%d - %d:%d)\n", + RangeIn.StartHour, RangeIn.StartMinute, + RangeIn.EndHour, RangeIn.EndMinute); + } + else + { + AppendPrintF(&FileStr, "\tIn Leds-On Time Range: None\n"); + } + + AppendPrintF(&FileStr, "\tTemp Dimming: %s\n", + Blumen_TempShouldDimLeds(BLState) ? "On" : "Off"); + AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived); gs_data LogMem = StringToData(FileStr); @@ -528,8 +546,9 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) case PacketType_Temperature: { temp_packet Temp = Packet.TempPacket; + BLState->LastTemperatureReceived = Temp.Temperature; - if (Temp.Temperature > MinHighTemperature) + if (Blumen_TempShouldDimLeds(BLState)) { BLState->BrightnessPercent = HighTemperatureBrightnessPercent; } @@ -537,7 +556,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { BLState->BrightnessPercent = FullBrightnessPercent; } - BLState->LastTemperatureReceived = Temp.Temperature; DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); BLState->ShouldUpdateLog = true; @@ -711,19 +729,24 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { bool TimelineShouldAdvance = false; r32 OverrideBrightness = 0.0f; - for (u32 i = 0; i < LedOnTimesCount; i++) + + time_range RangeIn = {}; + if (SystemTimeIsInTimeRangeList(Context->SystemTime_Current, + LedOnTimes, + LedOnTimesCount, + &RangeIn)) { - time_range Range = LedOnTimes[i]; - bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); - if (CurrTimeInRange) - { - // If we're in one of the specified time ranges, - // play animations and set brightness - OverrideBrightness = BLState->BrightnessPercent; - TimelineShouldAdvance = State->AnimationSystem.TimelineShouldAdvance; - break; - } + // If we're in one of the specified time ranges, + // play animations and set brightness + // + // The values of BrightnessPercent and TimelineShouldAdvance + // are set according to less strict rules above in this update + // function. All we are doing is saying "If we are in a valid + // time range, keep those values". + OverrideBrightness = BLState->BrightnessPercent; + TimelineShouldAdvance = State->AnimationSystem.TimelineShouldAdvance; } + State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; BLState->BrightnessPercent = OverrideBrightness; } diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index c5822a8..6c5ab47 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -134,24 +134,57 @@ typedef struct time_range s32 EndMinute; } time_range; +internal bool +SystemTimeIsBeforeTime(system_time SysTime, s32 Hour, s32 Minute) +{ + bool Result = false; + if (SysTime.Hour == Hour) { + Result = SysTime.Minute < Minute; + } else { + Result = SysTime.Hour < Hour; + } + return Result; +} + +internal bool +SystemTimeIsAfterTime(system_time SysTime, s32 Hour, s32 Minute) +{ + bool Result = false; + if (SysTime.Hour == Hour) { + Result = SysTime.Minute > Minute; + } else { + Result = SysTime.Hour > Hour; + } + return Result; +} + internal bool SystemTimeIsInTimeRange(system_time SysTime, time_range Range) { bool Result = false; - if (SysTime.Hour >= Range.StartHour && - SysTime.Hour <= Range.EndHour) + + bool IsAfterStartTime = SystemTimeIsAfterTime(SysTime, Range.StartHour, Range.StartMinute); + bool IsBeforeEndTime = SystemTimeIsBeforeTime(SysTime, Range.EndHour, Range.EndMinute); + Result = IsAfterStartTime && IsBeforeEndTime; + + return Result; +} + +internal bool +SystemTimeIsInTimeRangeList(system_time SysTime, time_range* Ranges, u32 RangesCount, time_range* RangeOut = 0) +{ + bool Result = false; + for (u32 i = 0; i < RangesCount; i++) { - if (SysTime.Hour == Range.StartHour) - { - Result = (SysTime.Minute >= Range.StartMinute); - } - else if (SysTime.Hour == Range.EndHour) - { - Result = (SysTime.Minute <= Range.EndMinute); - } - else + time_range Range = Ranges[i]; + bool CurrTimeInRange = SystemTimeIsInTimeRange(SysTime, Range); + if (CurrTimeInRange) { Result = true; + if (RangeOut != 0) { + *RangeOut = Range; + } + break; } } return Result; @@ -233,6 +266,13 @@ struct blumen_lumen_state phrase_hue DebugHue; }; +internal bool +Blumen_TempShouldDimLeds(blumen_lumen_state* BLState) +{ + bool Result = BLState->LastTemperatureReceived > MinHighTemperature; + return Result; +} + #include "message_queue.cpp" internal void From f809d670fbfcfad07fccb8a516b6b25302c81c4d Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 22 Apr 2021 13:09:31 -1000 Subject: [PATCH 088/151] Added motor time range logging --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 6e0995c..ece98e7 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -298,6 +298,21 @@ BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Con u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2]; AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2); + time_range MotorRange = {}; + if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, + MotorOpenTimes, + MotorOpenTimesCount, + &MotorRange)) + { + AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: ( %d:%d - %d:%d)\n", + MotorRange.StartHour, MotorRange.StartMinute, + MotorRange.EndHour, MotorRange.EndMinute); + } + else + { + AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: None\n"); + } + char* PatternMode = 0; switch (BLState->PatternMode) { From 9ebadc543bc190f42489ddfcf914bdb7ca28a7db Mon Sep 17 00:00:00 2001 From: PS Date: Mon, 26 Apr 2021 14:17:41 -1000 Subject: [PATCH 089/151] Fix for exiting voice mode and resuming other animations --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 10 +++++----- src/app/ss_blumen_lumen/blumen_lumen_settings.h | 7 +++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index ece98e7..2833d6c 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -290,6 +290,10 @@ BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Con animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); + bool IsPlaying = State->AnimationSystem.TimelineShouldAdvance; + AppendPrintF(&FileStr, "\tIs Playing: %s\n", + IsPlaying ? "True" : "False"); + char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected"; AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected); @@ -623,11 +627,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (SecondsSinceChange > VoiceCommandSustainDuration) { - BLState->PatternMode = BlumenPattern_Standard; - animation_handle NewAnim = BLState->ModeAnimations[BlumenPattern_Standard].Handles[0]; - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, - NewAnim, - VoiceCommandFadeDuration); + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); BLState->ShouldUpdateLog = true; } } diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 2c4858c..c95029e 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -36,10 +36,9 @@ gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_r // NOTE: There is no need to modify the MotorOpenTimesCount variable - // it is a compile time constant that gets calculated automatically global time_range MotorOpenTimes[] = { - { 8, 00, 12, 00 }, - { 12, 30, 13, 00 }, - { 18, 00, 22, 00 }, // 6:00pm to 10:00pm - { 23, 05, 23, 53 }, + { 8, 00, 12, 00 }, // 8:00am to 12:00pm + { 12, 30, 13, 00 }, // 12:30pm to 01:00pm + { 18, 00, 22, 00 }, // 6:00pm to 10:00pm }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit From 4074389bcef82a7b4ebe48541d2c5d93aeed8955 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Mon, 26 Apr 2021 20:18:43 -0400 Subject: [PATCH 090/151] time of day fix --- .../audio_responses/voice_command.foldanim | 4 ++-- src/app/ss_blumen_lumen/blumen_lumen_settings.h | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim index c21484c..9a5339b 100644 --- a/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim +++ b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim @@ -19,8 +19,8 @@ layers:{ blocks:{ block:{ frame_range:{ - min: 1; - max: 3599; + min: 0; + max: 3600; }; layer_index: 0; animation_name: "Pattern_VoicePattern"; diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 2c4858c..0e21265 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -36,10 +36,12 @@ gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_r // NOTE: There is no need to modify the MotorOpenTimesCount variable - // it is a compile time constant that gets calculated automatically global time_range MotorOpenTimes[] = { - { 8, 00, 12, 00 }, - { 12, 30, 13, 00 }, - { 18, 00, 22, 00 }, // 6:00pm to 10:00pm - { 23, 05, 23, 53 }, + { 8, 00, 11, 59 }, // 8a to noon + { 12, 30, 17, 59 }, // midday show - 12:30 - 6p + { 18, 30, 22, 00 }, // 6:30pm to 10:00pm + // { 18, 00, 21, 51 }, // test, remove me + // { 21, 55, 22, 30 }, // test, remove me + // { 23, 05, 23, 53 }, // test, remove me }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit From 965ca7c89186d3adc1cb63a75f001c4cf1079e2d Mon Sep 17 00:00:00 2001 From: PS Date: Mon, 26 Apr 2021 14:46:07 -1000 Subject: [PATCH 091/151] Fixed brightness for leds --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 29 ++++++++---------------- src/app/ss_blumen_lumen/blumen_lumen.h | 2 +- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 2833d6c..349db9c 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -566,16 +566,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { temp_packet Temp = Packet.TempPacket; BLState->LastTemperatureReceived = Temp.Temperature; - - if (Blumen_TempShouldDimLeds(BLState)) - { - BLState->BrightnessPercent = HighTemperatureBrightnessPercent; - } - else - { - BLState->BrightnessPercent = FullBrightnessPercent; - } - DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); BLState->ShouldUpdateLog = true; }break; @@ -751,15 +741,16 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) LedOnTimesCount, &RangeIn)) { - // If we're in one of the specified time ranges, - // play animations and set brightness - // - // The values of BrightnessPercent and TimelineShouldAdvance - // are set according to less strict rules above in this update - // function. All we are doing is saying "If we are in a valid - // time range, keep those values". - OverrideBrightness = BLState->BrightnessPercent; - TimelineShouldAdvance = State->AnimationSystem.TimelineShouldAdvance; + + if (Blumen_TempShouldDimLeds(BLState)) + { + OverrideBrightness = HighTemperatureBrightnessPercent; + } + else + { + OverrideBrightness = FullBrightnessPercent; + } + TimelineShouldAdvance = true; } State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h index 6c5ab47..fb04238 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.h +++ b/src/app/ss_blumen_lumen/blumen_lumen.h @@ -151,7 +151,7 @@ SystemTimeIsAfterTime(system_time SysTime, s32 Hour, s32 Minute) { bool Result = false; if (SysTime.Hour == Hour) { - Result = SysTime.Minute > Minute; + Result = SysTime.Minute >= Minute; } else { Result = SysTime.Hour > Hour; } From faf45bb9c40b5e0e1eeea16844b67206bbbb824e Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 6 May 2021 10:47:22 -1000 Subject: [PATCH 092/151] added the ability to zero out packets being sent to led destinations --- .../editor/panels/foldhaus_panel_assembly_debug.h | 2 ++ src/app/foldhaus_app.cpp | 12 ++++++++++++ src/app/foldhaus_app.h | 1 + src/app/ss_blumen_lumen/blumen_lumen_settings.h | 5 ++++- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/app/editor/panels/foldhaus_panel_assembly_debug.h b/src/app/editor/panels/foldhaus_panel_assembly_debug.h index 24922b3..4ef3599 100644 --- a/src/app/editor/panels/foldhaus_panel_assembly_debug.h +++ b/src/app/editor/panels/foldhaus_panel_assembly_debug.h @@ -137,6 +137,8 @@ AssemblyDebug_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren }break; } } + + State->SendEmptyPackets = ui_LabeledToggle(Interface, MakeString("Send Empty Packets"), State->SendEmptyPackets); ui_PopLayout(Interface, MakeString("Assembly Debug Layout")); } diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 89bf4fe..9b37512 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -166,6 +166,18 @@ UPDATE_AND_RENDER(UpdateAndRender) Assert(State->UserSpaceDesc.UserData.Memory != 0); BuildAssemblyData(State, *Context, OutputData); + + // NOTE(PS): We introduced this in order to test some things on the + // blumen lumen circuit boards, to see if they were getting out + // of sync + if (State->SendEmptyPackets) { + for (addressed_data_buffer* At = OutputData->Root; + At != 0; + At = At->Next) + { + ZeroMemoryBlock(At->Memory, At->MemorySize); + } + } } CLEANUP_APPLICATION(CleanupApplication) diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index 4241794..5666c95 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -77,6 +77,7 @@ struct app_state bool ShowingUserSpaceDebug; bool RunEditor; + bool SendEmptyPackets; }; internal void OpenColorPicker(app_state* State, v4* Address); diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index c95029e..85b9dd0 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -16,7 +16,7 @@ gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold"); // The path to the phrase map CSV. Can be an absolute path, or relative // to the app_run_tree folder -gs_const_string PhraseMapCSVPath = ConstString("C:/projects/flowers-sound/flower_codes.tsv"); +gs_const_string PhraseMapCSVPath = ConstString("C:/projects/Lumenarium/app_run_tree/data/flower_codes.tsv"); char PhraseMapCSVSeparator = '\t'; // Search Strings for which folders to find ambient animation files and @@ -50,6 +50,9 @@ global r32 MotorResendStatePeriod = 90.0f; // seconds // The times of day when the leds should be on // Search for @TimeFormat to find documentation global time_range LedOnTimes[] = { + { 14, 43, 14, 44 }, + { 14, 45, 14, 46 }, + { 17, 00, 23, 59 }, { 00, 00, 06, 30 }, }; From b310e524a9efe33250701f2d0848dfdd96b9be54 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Thu, 6 May 2021 16:48:26 -0400 Subject: [PATCH 093/151] settings --- src/app/ss_blumen_lumen/blumen_lumen_settings.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index c95029e..dffacec 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -37,8 +37,8 @@ gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_r // it is a compile time constant that gets calculated automatically global time_range MotorOpenTimes[] = { { 8, 00, 12, 00 }, // 8:00am to 12:00pm - { 12, 30, 13, 00 }, // 12:30pm to 01:00pm - { 18, 00, 22, 00 }, // 6:00pm to 10:00pm + { 12, 30, 18, 00 }, // 12:30pm to 06:00pm + { 18, 30, 22, 00 }, // 6:30pm to 10:00pm }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit @@ -102,13 +102,13 @@ s8 MinHighTemperature = 26; // This is multiplied by each pixels R, G, & B channels before being // sent. So if it is set to .1f, then the maximum brightness value sent // to any channel of any pixel will be 25 (255 * .1 = 25). -r32 HighTemperatureBrightnessPercent = 0.7f; +r32 HighTemperatureBrightnessPercent = 0.25f; // The percent brightness we set leds to when no other conditions apply // A value in the range 0:1 inclusive. // Probably wants to be something high like 1 but we might want to // lower it for heat reasons? -r32 FullBrightnessPercent = 1.0f; +r32 FullBrightnessPercent = 0.50f; // A global modifier so Joerg can just slow all the patterns right down // XD From baf4c5d5a691f741a79f695b345268e2e98deb61 Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 6 Aug 2021 14:51:04 -0500 Subject: [PATCH 094/151] Introduced memory_arena_tests --- build/build_app_msvc_win32_debug.bat | 2 + src/tests/memory_arena_tests.cpp | 184 +++++++++++++++++++++++++++ src/tests/sanity_tests.cpp | 177 +++++++++++++------------- 3 files changed, 277 insertions(+), 86 deletions(-) create mode 100644 src/tests/memory_arena_tests.cpp diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index 7494131..1997c89 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -37,7 +37,9 @@ cl %CommonCompilerFlags% %ProjectDevPath%\src\sculpture_gen\gen_blumen_lumen.cpp REM COMPILE AND RUN TESTS cl %CommonCompilerFlags% %ProjectDevPath%\src\tests\sanity_tests.cpp /Fesanity_tests.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib +ECHO SANITY TESTS BEGIN sanity_tests.exe +ECHO SANITY TESTS END popd diff --git a/src/tests/memory_arena_tests.cpp b/src/tests/memory_arena_tests.cpp new file mode 100644 index 0000000..d52623a --- /dev/null +++ b/src/tests/memory_arena_tests.cpp @@ -0,0 +1,184 @@ +#include "../app/platform_win32/win32_foldhaus_memory.h" + +internal u32 +TESTNextRandom(u32* LastRandomValue) +{ + u32 Result = *LastRandomValue; + Result ^= Result << 13; + Result ^= Result >> 17; + Result ^= Result << 5; + *LastRandomValue = Result; + return Result; +} + +internal void +MemoryArenaTests() +{ + Test("Allocator") + { + gs_allocator Allocator = CreateAllocator(Win32Alloc, Win32Free); + + u8* Data = AllocatorAllocArray(Allocator, u8, 4096); + for (int i = 0; i < 4096; i++) Data[i] = (i % MaxU8); + bool Success = true; + for (int i = 0; i < 4096; i++) Success &= (Data[i] == (i % MaxU8)); + TestResult(Success); + + AllocatorFreeArray(Allocator, Data, u8, 4096); + // idk how to test free + } + + Test("Memory Cursor") + { + gs_allocator A = CreateAllocator(Win32Alloc, Win32Free); + + u64 Size = 4096; + gs_data D = AllocatorAlloc(A, Size); + gs_memory_cursor C = CreateMemoryCursor(D); + + u64 RoomLeft = CursorRoomLeft(C); + TestResult(RoomLeft == Size); + + TestResult(CursorHasRoom(C, 2048)); + TestResult(CursorHasRoom(C, Size)); + TestResult(!CursorHasRoom(C, Size + 1)); + + for (u64 i = 0; i < 2048; i++) + { + u8* Byte = PushSizeOnCursor(&C, 1).Memory; + *Byte = (u8)(i % 256); + } + RoomLeft = CursorRoomLeft(C); + TestResult(RoomLeft == (Size - 2048)); + + PopSizeOnCursor(&C, 2048); + TestResult(C.Position == 0); + + bool Success = true; + for (u64 i = 0; i < 2048; i++) + { + u8* Byte = PushSizeOnCursor(&C, 1).Memory; + Success &= *Byte == (u8)(i % 256); + } + TestResult(Success); + + AllocatorFree(A, D.Memory, D.Size); + } + + Test("Memory Arena") + { + gs_allocator Al = CreateAllocator(Win32Alloc, Win32Free); + gs_memory_arena A = CreateMemoryArena(Al, "test", 128, 4); + + // NOTE(PS): We loop through this block 3 times + // 1. Make sure the arena works out of the box + // 2. Make sure the arena works the same way after clearing + // 3. Make sure the arena works the same way after freeing + for (int i = 0; i < 3; i++) + { + gs_data D0 = PushSize_(&A, 32, FileNameAndLineNumberString); + TestResult(D0.Size == 32); + + // NOTE(PS): This should still result in 32 bytes + // because its going to align the Cursor after + // it allocates to a multiple of 4 bytes + gs_data D1 = PushSize_(&A, 30, FileNameAndLineNumberString); + TestResult(D1.Size == 32); + + // NOTE(PS): Allocating bigger than the size remaining + // in the current cursor + gs_data D2 = PushSize_(&A, 128, FileNameAndLineNumberString); + TestResult(D2.Size == 128); + TestResult(A.CursorsCount != 1); + + // NOTE(PS): Because there is still room in cursor + // 0, the head of this gs_data should be one byte + // past the end of D1 + gs_data D3 = PushSize_(&A, 32, FileNameAndLineNumberString); + TestResult(D3.Memory == D1.Memory + D1.Size); + + if (i == 0) + { + ClearArena(&A); + } else if (i == 1) { + FreeMemoryArena(&A); + } + } + + FreeMemoryArena(&A); + } + + Test("Memory Arena: Push") + { + gs_allocator Al = CreateAllocator(Win32Alloc, Win32Free); + gs_memory_arena A = CreateMemoryArena(Al, "test", 128, 4); + + // NOTE(PS): This makes sure that the Arena is moving its next allocation + // pointer forward the appropriate amount after each allocation. If it isnt' + // then Array1 should be overlapping with Array0 in the event that the arena + // doesn't push the pointer forward enough + u32* Array0 = PushArray(&A, u32, 32); + u32* Array1 = PushArray(&A, u32, 32); + + for (u32 i = 0; i < 32; i++) + { + Array0[i] = i; + Array1[i] = i * 4; + } + + bool Success = true; + for (u32 i = 0; i < 32; i++) + { + Success &= Array0[i] == i && Array1[i] == i * 4; + } + TestResult(Success); + + FreeArena(&A); + } + + int FreeCount = 0; + int ClearCount = 0; + + Test("Memory Arena: Stress Test") + { + gs_allocator Al = CreateAllocator(Win32Alloc, Win32Free); + gs_memory_arena A = CreateMemoryArena(Al, "test", 128, 4); + + // NOTE(PS): This is an array of allocation sizes + // As we repeat the loop we will get values out of this array + // semi-randomly. + // * if the value is 0, we will clear the arena + // * if the value is 2, we will free the arena + // * otherwise we will push a value sized allocation on the arena + u64 RandomSizes[] = { 8, 32, 128, 93, 1256, 4098, 0, 1024, 7, 18, 967, 53, 1, 2 }; + u32 RandomSizesCount = sizeof(RandomSizes) / sizeof(u64); + + bool Success = true; + u32 RandomSeed = 1923; + for (u64 i = 0; i < (4096 * 14); i++) + { + TESTNextRandom(&RandomSeed); + u32 SizeIndex = RandomSeed % RandomSizesCount; + u64 RandomSize = RandomSizes[SizeIndex]; + + if (RandomSize == 0) + { + ClearArena(&A); + ClearCount++; + } else if (RandomSize == 2) { + FreeArena(&A); + FreeCount++; + } else { + gs_data D = PushSize_(&A, RandomSize, FileNameAndLineNumberString); + // NOTE(PS): This check has to be >= because the arena + // might have adjusted to maintain alignment on this + // allocation. + Success &= D.Size >= RandomSize; + } + } + + TestResult(Success); + + } + +} diff --git a/src/tests/sanity_tests.cpp b/src/tests/sanity_tests.cpp index cd4576e..fa8f271 100644 --- a/src/tests/sanity_tests.cpp +++ b/src/tests/sanity_tests.cpp @@ -5,6 +5,7 @@ // #ifndef SANITY_TESTS_CPP +#include #include #include "../gs_libs/gs_types.h" #include "../gs_libs/gs_types.cpp" @@ -13,21 +14,23 @@ #include "../gs_libs/gs_path.h" #include "../gs_libs/gs_csv.h" +#include "./memory_arena_tests.cpp" + gs_memory_arena Scratch = {}; void* Alloc(u64 Size, u64* ResultSize) { *ResultSize = Size; return malloc(Size); } void Free(void* Ptr, u64 Size) { return free(Ptr); } bool StringTest (gs_const_string StrA, gs_const_string StrB) { - return StringsEqual(StrA, StrB); + return StringsEqual(StrA, StrB); } bool StringTest (gs_string StrA, gs_string StrB) { - return StringsEqual(StrA, StrB); + return StringsEqual(StrA, StrB); } bool PathTest (char* In, char* Out) { - return StringsEqual(SanitizePath(ConstString(In), &Scratch), ConstString(Out)); + return StringsEqual(SanitizePath(ConstString(In), &Scratch), ConstString(Out)); } global char* SampleCSV = R"FOO(Flower Primary Hue (0-365) Secondary Hue (0-365) Tertiary Hue (0-365) Homonyms @@ -37,93 +40,95 @@ Flower C 55 32 128 foo, bar, blah, baz, whatever)FOO"; int main (int ArgCount, char** Args) { - Scratch = CreateMemoryArena(CreateAllocator(Alloc, Free), "Scratch"); + Scratch = CreateMemoryArena(CreateAllocator(Alloc, Free), "Scratch"); + + Test("gs_string") + { + gs_string TestString = PushStringF(&Scratch, 256, "Hello there, Sailor!"); - Test("gs_string") - { - gs_string TestString = PushStringF(&Scratch, 256, "Hello there, Sailor!"); - - NullTerminate(&TestString); - TestResult(IsNullTerminated(TestString)); - - TestResult(StringTest(GetStringPrefix(TestString.ConstString, 5), ConstString("Hello"))); - TestResult(StringTest(GetStringPostfix(TestString.ConstString, 5), ConstString("ilor!"))); - TestResult(StringTest(GetStringAfter(TestString.ConstString, 13), ConstString("Sailor!"))); - TestResult(StringTest(GetStringBefore(TestString.ConstString, 5), ConstString("Hello"))); - TestResult(StringTest(Substring(TestString.ConstString, 5, 11), ConstString(" there"))); - - TestResult(FindFirst(TestString, 5, 'l') == 16); - TestResult(FindFirst(TestString, 0, 'k') == -1); - TestResult(FindLast(TestString, 10, 'l') == 3); - TestResult(FindLast(TestString, 'k') == -1); - - TestResult(FindFirstFromSet(TestString.ConstString, "re") == 1); - TestResult(FindFirstFromSet(TestString.ConstString, "er") == 1); - TestResult(FindFirstFromSet(TestString.ConstString, "bk") == -1); - TestResult(FindFirstFromSet(TestString.ConstString, "ek") == 1); - - TestResult(FindLastFromSet(TestString.ConstString, "re") == 18); - TestResult(FindLastFromSet(TestString.ConstString, "er") == 18); - TestResult(FindLastFromSet(TestString.ConstString, "bk") == -1); - TestResult(FindLastFromSet(TestString.ConstString, "rk") == 18); - - TestResult(StringContains(TestString.ConstString, ',')); - TestResult(!StringContains(TestString.ConstString, '@')); - TestResult(StringsEqual(TestString, TestString)); - - TestResult(StringEqualsCharArray(TestString, "Hello there, Sailor!")); - TestResult(!StringEqualsCharArray(TestString, "Hello there, Sailor")); - TestResult(!StringEqualsCharArray(TestString, "Foobar")); - - ReverseStringInPlace(&TestString); - TestResult(StringTest(TestString, MakeString("!roliaS ,ereht olleH"))); - ReverseStringInPlace(&TestString); - - TestResult(ParseUInt(ConstString("532")) == 532); - TestResult(ParseInt(ConstString("-1234567890")) == -1234567890); - TestResult(ParseFloat(ConstString("-12345.6789")) == -12345.6789); - TestResult(ParseFloat(ConstString("-1")) == -1); - TestResult(ParseFloat(ConstString("-.035")) == -.035); - - TestString.Length = 0; - U64ToASCII(&TestString, 53298, 10); - TestResult(StringTest(TestString.ConstString, ConstString("53298"))); - - TestString.Length = 0; - R64ToASCII(&TestString, -145732.321, 2); - TestResult(StringTest(TestString.ConstString, ConstString("-145732.32"))); - } + NullTerminate(&TestString); + TestResult(IsNullTerminated(TestString)); - Test("gs_path.h") - { - TestResult(PathTest(".", ".")); - TestResult(PathTest(".\\", ".\\")); - TestResult(PathTest("./", ".\\")); - TestResult(PathTest("./../", "..\\")); - TestResult(PathTest("C:/users/pslattery\\test.foo", "C:\\users\\pslattery\\test.foo")); - TestResult(PathTest("./test/../foo.bar", ".\\foo.bar")); - TestResult(PathTest("C:\\hello\\world\\.\\test", "C:\\hello\\world\\test")); - } + TestResult(StringTest(GetStringPrefix(TestString.ConstString, 5), ConstString("Hello"))); + TestResult(StringTest(GetStringPostfix(TestString.ConstString, 5), ConstString("ilor!"))); + TestResult(StringTest(GetStringAfter(TestString.ConstString, 13), ConstString("Sailor!"))); + TestResult(StringTest(GetStringBefore(TestString.ConstString, 5), ConstString("Hello"))); + TestResult(StringTest(Substring(TestString.ConstString, 5, 11), ConstString(" there"))); - Test("gs_csv.h") - { - gs_const_string TestCSV = ConstString(SampleCSV); - gscsv_sheet Sheet = CSV_Parse(TestCSV, { '\t' }, &Scratch); - - gs_const_string Cell = CSVSheet_GetCell(Sheet, 0, 0); - TestResult(StringsEqual(Cell, ConstString("Flower"))); - - Cell = CSVSheet_GetCell(Sheet, 1, 1); - TestResult(StringsEqual(Cell, ConstString("55"))); - - Cell = CSVSheet_GetCell(Sheet, 4, 1); - TestResult(StringsEqual(Cell, ConstString("foo, bar, blah, baz, whatever"))); - - Cell = CSVSheet_GetCell(Sheet, 4, 3); - TestResult(StringsEqual(Cell, ConstString("foo, bar, blah, baz, whatever"))); - } + TestResult(FindFirst(TestString, 5, 'l') == 16); + TestResult(FindFirst(TestString, 0, 'k') == -1); + TestResult(FindLast(TestString, 10, 'l') == 3); + TestResult(FindLast(TestString, 'k') == -1); - return 0; + TestResult(FindFirstFromSet(TestString.ConstString, "re") == 1); + TestResult(FindFirstFromSet(TestString.ConstString, "er") == 1); + TestResult(FindFirstFromSet(TestString.ConstString, "bk") == -1); + TestResult(FindFirstFromSet(TestString.ConstString, "ek") == 1); + + TestResult(FindLastFromSet(TestString.ConstString, "re") == 18); + TestResult(FindLastFromSet(TestString.ConstString, "er") == 18); + TestResult(FindLastFromSet(TestString.ConstString, "bk") == -1); + TestResult(FindLastFromSet(TestString.ConstString, "rk") == 18); + + TestResult(StringContains(TestString.ConstString, ',')); + TestResult(!StringContains(TestString.ConstString, '@')); + TestResult(StringsEqual(TestString, TestString)); + + TestResult(StringEqualsCharArray(TestString, "Hello there, Sailor!")); + TestResult(!StringEqualsCharArray(TestString, "Hello there, Sailor")); + TestResult(!StringEqualsCharArray(TestString, "Foobar")); + + ReverseStringInPlace(&TestString); + TestResult(StringTest(TestString, MakeString("!roliaS ,ereht olleH"))); + ReverseStringInPlace(&TestString); + + TestResult(ParseUInt(ConstString("532")) == 532); + TestResult(ParseInt(ConstString("-1234567890")) == -1234567890); + TestResult(ParseFloat(ConstString("-12345.6789")) == -12345.6789); + TestResult(ParseFloat(ConstString("-1")) == -1); + TestResult(ParseFloat(ConstString("-.035")) == -.035); + + TestString.Length = 0; + U64ToASCII(&TestString, 53298, 10); + TestResult(StringTest(TestString.ConstString, ConstString("53298"))); + + TestString.Length = 0; + R64ToASCII(&TestString, -145732.321, 2); + TestResult(StringTest(TestString.ConstString, ConstString("-145732.32"))); + } + + Test("gs_path.h") + { + TestResult(PathTest(".", ".")); + TestResult(PathTest(".\\", ".\\")); + TestResult(PathTest("./", ".\\")); + TestResult(PathTest("./../", "..\\")); + TestResult(PathTest("C:/users/pslattery\\test.foo", "C:\\users\\pslattery\\test.foo")); + TestResult(PathTest("./test/../foo.bar", ".\\foo.bar")); + TestResult(PathTest("C:\\hello\\world\\.\\test", "C:\\hello\\world\\test")); + } + + Test("gs_csv.h") + { + gs_const_string TestCSV = ConstString(SampleCSV); + gscsv_sheet Sheet = CSV_Parse(TestCSV, { '\t' }, &Scratch); + + gs_const_string Cell = CSVSheet_GetCell(Sheet, 0, 0); + TestResult(StringsEqual(Cell, ConstString("Flower"))); + + Cell = CSVSheet_GetCell(Sheet, 1, 1); + TestResult(StringsEqual(Cell, ConstString("55"))); + + Cell = CSVSheet_GetCell(Sheet, 4, 1); + TestResult(StringsEqual(Cell, ConstString("foo, bar, blah, baz, whatever"))); + + Cell = CSVSheet_GetCell(Sheet, 4, 3); + TestResult(StringsEqual(Cell, ConstString("foo, bar, blah, baz, whatever"))); + } + + MemoryArenaTests(); + + return 0; } From f261cbd55a629c308fc4cafb59d40aacbdfd9006 Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 6 Aug 2021 18:19:30 -0500 Subject: [PATCH 095/151] Replaced old memory arena with a new, better one, which also has a test suite --- build/build_app_msvc_win32_debug.bat | 2 +- src/app/editor/foldhaus_operation_mode.h | 136 +- src/app/editor/interface.h | 2274 ++++----- .../editor/panels/foldhaus_panel_file_view.h | 274 +- .../editor/panels/foldhaus_panel_profiler.h | 542 +- .../panels/foldhaus_panel_sculpture_view.h | 284 +- .../animation/foldhaus_animation_renderer.cpp | 512 +- src/app/engine/assembly/foldhaus_assembly.cpp | 340 +- src/app/engine/assembly/foldhaus_assembly.h | 339 +- src/app/engine/foldhaus_addressed_data.h | 102 +- src/app/engine/foldhaus_log.h | 117 +- src/app/engine/uart/foldhaus_uart.cpp | 270 +- src/app/foldhaus_app.cpp | 302 +- src/app/foldhaus_debug.h | 597 +-- src/app/foldhaus_renderer.cpp | 290 +- src/app/foldhaus_renderer.h | 768 +-- src/app/platform_win32/win32_foldhaus.cpp | 1231 +++-- src/app/platform_win32/win32_foldhaus_dll.h | 206 +- .../platform_win32/win32_foldhaus_memory.h | 48 +- .../platform_win32/win32_foldhaus_serial.h | 532 +- .../platform_win32/win32_foldhaus_socket.h | 742 +-- .../win32_foldhaus_work_queue.h | 322 +- src/app/ss_blumen_lumen/blumen_lumen.cpp | 1886 +++---- src/app/ss_blumen_lumen/message_queue.cpp | 93 +- src/gs_libs/gs_input.h | 854 ++-- src/gs_libs/gs_memory.h | 528 ++ src/gs_libs/gs_types.cpp | 4377 ++++++++--------- src/gs_libs/gs_types.h | 927 ++-- src/gs_libs/gs_win32.cpp | 979 ++-- src/sculpture_gen/gen_blumen_lumen.cpp | 382 +- src/serial_monitor/first.cpp | 332 +- src/tests/memory_arena_tests.cpp | 108 +- src/tests/sanity_tests.cpp | 72 +- 33 files changed, 10388 insertions(+), 10380 deletions(-) create mode 100644 src/gs_libs/gs_memory.h diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index 1997c89..2dafabf 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -6,7 +6,7 @@ SET MyPath=%MyPath:~0,-1% call %MyPath%\_prebuild_win32.bat app debug msvc call %MyPath%\setup_cl.bat -SET CommonCompilerFlags=-nologo -DDEBUG=1 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src +SET CommonCompilerFlags=-nologo -DDEBUG=0 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src SET CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -I%CommonLibs% -Od %CommonCompilerFlags% diff --git a/src/app/editor/foldhaus_operation_mode.h b/src/app/editor/foldhaus_operation_mode.h index 46d5830..375d342 100644 --- a/src/app/editor/foldhaus_operation_mode.h +++ b/src/app/editor/foldhaus_operation_mode.h @@ -14,77 +14,77 @@ typedef OPERATION_RENDER_PROC(operation_render_proc); struct operation_mode { - input_command_registry Commands; - operation_render_proc* Render; - gs_memory_cursor Memory; - u8* OpStateMemory; + input_command_registry Commands; + operation_render_proc* Render; + gs_memory_cursor Memory; + u8* OpStateMemory; }; #define OPERATION_MODES_MAX 32 struct operation_mode_system { - s32 ActiveModesCount; - operation_mode ActiveModes[OPERATION_MODES_MAX]; - //arena_snapshot ModeMemorySnapshots[OPERATION_MODES_MAX]; - gs_data_array ModeMemoryPagesFreeList; - - // NOTE(Peter): This acts as mode scoped memory. When a mode gets activated, it can allocate - // temporary memory which then gets freed when the mode is deactivated - gs_memory_arena Arena; + s32 ActiveModesCount; + operation_mode ActiveModes[OPERATION_MODES_MAX]; + //arena_snapshot ModeMemorySnapshots[OPERATION_MODES_MAX]; + gs_data_array ModeMemoryPagesFreeList; + + // NOTE(Peter): This acts as mode scoped memory. When a mode gets activated, it can allocate + // temporary memory which then gets freed when the mode is deactivated + gs_memory_arena Arena; }; internal operation_mode_system OperationModeSystemInit(gs_memory_arena* Storage, gs_thread_context ThreadContext) { - operation_mode_system Result = {0}; - // TODO(Peter): Do we really need an arena? Can this just operate in constant memory footprint? - Result.Arena.Allocator = ThreadContext.Allocator; - - Result.ModeMemoryPagesFreeList.CountMax = 8; - Result.ModeMemoryPagesFreeList.Data = PushArray(Storage, gs_data, Result.ModeMemoryPagesFreeList.CountMax); - for (u32 Page = 0; Page < Result.ModeMemoryPagesFreeList.CountMax; Page++) - { - Result.ModeMemoryPagesFreeList.Data[Page] = PushSizeToData(Storage, KB(4)); - } - Result.ModeMemoryPagesFreeList.Count = Result.ModeMemoryPagesFreeList.CountMax; - - return Result; + operation_mode_system Result = {0}; + // TODO(Peter): Do we really need an arena? Can this just operate in constant memory footprint? + Result.Arena.Allocator = ThreadContext.Allocator; + + Result.ModeMemoryPagesFreeList.CountMax = 8; + Result.ModeMemoryPagesFreeList.Data = PushArray(Storage, gs_data, Result.ModeMemoryPagesFreeList.CountMax); + for (u32 Page = 0; Page < Result.ModeMemoryPagesFreeList.CountMax; Page++) + { + Result.ModeMemoryPagesFreeList.Data[Page] = PushSize(Storage, KB(4)); + } + Result.ModeMemoryPagesFreeList.Count = Result.ModeMemoryPagesFreeList.CountMax; + + return Result; } internal gs_data OperationModeTakeMemoryPage(operation_mode_system* System) { - Assert(System->ModeMemoryPagesFreeList.Count > 0); - gs_data Result = {0}; - System->ModeMemoryPagesFreeList.Count -= 1; - u64 LastIndex = System->ModeMemoryPagesFreeList.Count; - Result = System->ModeMemoryPagesFreeList.Data[LastIndex]; - return Result; + Assert(System->ModeMemoryPagesFreeList.Count > 0); + gs_data Result = {0}; + System->ModeMemoryPagesFreeList.Count -= 1; + u64 LastIndex = System->ModeMemoryPagesFreeList.Count; + Result = System->ModeMemoryPagesFreeList.Data[LastIndex]; + return Result; } internal void OperationModeFreeMemoryPage(operation_mode_system* System, gs_data Data) { - Assert(System->ModeMemoryPagesFreeList.Count < System->ModeMemoryPagesFreeList.CountMax); - u64 LastIndex = System->ModeMemoryPagesFreeList.Count; - System->ModeMemoryPagesFreeList.Count += 1; - System->ModeMemoryPagesFreeList.Data[LastIndex] = Data; + Assert(System->ModeMemoryPagesFreeList.Count < System->ModeMemoryPagesFreeList.CountMax); + u64 LastIndex = System->ModeMemoryPagesFreeList.Count; + System->ModeMemoryPagesFreeList.Count += 1; + System->ModeMemoryPagesFreeList.Data[LastIndex] = Data; } internal operation_mode* ActivateOperationMode (operation_mode_system* System, operation_render_proc* RenderProc) { - Assert(System->ActiveModesCount < OPERATION_MODES_MAX); - operation_mode* Result = 0; - s32 ModeIndex = System->ActiveModesCount++; - - //System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(&System->Arena); - - Result = &System->ActiveModes[ModeIndex]; - Result->Memory = CreateMemoryCursor(OperationModeTakeMemoryPage(System)); - Result->Render = RenderProc; - - return Result; + Assert(System->ActiveModesCount < OPERATION_MODES_MAX); + operation_mode* Result = 0; + s32 ModeIndex = System->ActiveModesCount++; + + //System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(&System->Arena); + + Result = &System->ActiveModes[ModeIndex]; + Result->Memory = MemoryCursorCreateFromData(OperationModeTakeMemoryPage(System)); + Result->Render = RenderProc; + + return Result; } #define ActivateOperationModeWithCommands(sys, cmds, render) \ @@ -93,30 +93,30 @@ ActivateOperationModeWithCommands_(sys, cmds, (s32)(sizeof(cmds) / sizeof(cmds[0 internal operation_mode* ActivateOperationModeWithCommands_(operation_mode_system* System, input_command* Commands, s32 CommandsCount, operation_render_proc* RenderProc) { - operation_mode* NewMode = ActivateOperationMode(System, RenderProc); - + operation_mode* NewMode = ActivateOperationMode(System, RenderProc); + #if 0 - InitializeInputCommandRegistry(&NewMode->Commands, CommandsCount, &System->Arena); - for (s32 i = 0; i < CommandsCount; i++) - { - input_command Command = Commands[i]; - RegisterKeyPressCommand(&NewMode->Commands, Command.Key, Command.Flags, Command.Mdfr, Command.Proc); - } + InitializeInputCommandRegistry(&NewMode->Commands, CommandsCount, &System->Arena); + for (s32 i = 0; i < CommandsCount; i++) + { + input_command Command = Commands[i]; + RegisterKeyPressCommand(&NewMode->Commands, Command.Key, Command.Flags, Command.Mdfr, Command.Proc); + } #else - NewMode->Commands.Commands = Commands; - NewMode->Commands.Size = CommandsCount; - NewMode->Commands.Used = CommandsCount; + NewMode->Commands.Commands = Commands; + NewMode->Commands.Size = CommandsCount; + NewMode->Commands.Used = CommandsCount; #endif - return NewMode; + return NewMode; } internal void DeactivateCurrentOperationMode (operation_mode_system* System) { - Assert(System->ActiveModesCount > 0); - s32 ModeIndex = --System->ActiveModesCount; - OperationModeFreeMemoryPage(System, System->ActiveModes[ModeIndex].Memory.Data); - //ClearArenaToSnapshot(&System->Arena, System->ModeMemorySnapshots[ModeIndex]); + Assert(System->ActiveModesCount > 0); + s32 ModeIndex = --System->ActiveModesCount; + OperationModeFreeMemoryPage(System, System->ActiveModes[ModeIndex].Memory.Data); + //ClearArenaToSnapshot(&System->Arena, System->ModeMemorySnapshots[ModeIndex]); } #define CreateOperationState(mode, modeSystem, stateType) \ @@ -129,12 +129,12 @@ DeactivateCurrentOperationMode (operation_mode_system* System) internal u8* CreateOperationState_ (operation_mode* Mode, operation_mode_system* System, s32 StateSize) { - // NOTE(Peter): This isn't a problem if this fires, it just means our page size is too small, - // and its time to make the pages dynamically sized - Assert(Mode->Memory.Data.Size >= StateSize); - u8* Result = PushSizeOnCursor(&Mode->Memory, StateSize).Memory; - Mode->OpStateMemory = Result; - return Result; + // NOTE(Peter): This isn't a problem if this fires, it just means our page size is too small, + // and its time to make the pages dynamically sized + Assert(Mode->Memory.Data.Size >= StateSize); + u8* Result = MemoryCursorPushSize(&Mode->Memory, StateSize).Memory; + Mode->OpStateMemory = Result; + return Result; } diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index 854c3dc..d618089 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -9,600 +9,600 @@ enum gs_string_alignment { - Align_Left, - Align_Center, - Align_Right, + Align_Left, + Align_Center, + Align_Right, }; internal void ClipUVRect(rect2* Bounds, rect2* UVs, rect2 ClippingBox) { - rect2 NewBounds = Rect2Union(*Bounds, ClippingBox); - - r32 OldWidth = Rect2Width(*Bounds); - r32 OldHeight = Rect2Height(*Bounds); - - v2 MinInsetPercent = v2{ - (NewBounds.Min.x - Bounds->Min.x) / OldWidth, - (NewBounds.Min.y - Bounds->Min.y) / OldHeight, - }; - - v2 MaxInsetPercent = v2{ - (NewBounds.Max.x - Bounds->Min.x) / OldWidth, - (NewBounds.Max.y - Bounds->Min.y) / OldHeight, - }; - - UVs->Min.x = LerpR32(MinInsetPercent.x, UVs->Min.x, UVs->Max.x); - UVs->Min.y = LerpR32(MinInsetPercent.y, UVs->Min.y, UVs->Max.y); - UVs->Max.x = LerpR32(MaxInsetPercent.x, UVs->Min.x, UVs->Max.x); - UVs->Max.y = LerpR32(MaxInsetPercent.y, UVs->Min.y, UVs->Max.y); - - *Bounds = NewBounds; + rect2 NewBounds = Rect2Union(*Bounds, ClippingBox); + + r32 OldWidth = Rect2Width(*Bounds); + r32 OldHeight = Rect2Height(*Bounds); + + v2 MinInsetPercent = v2{ + (NewBounds.Min.x - Bounds->Min.x) / OldWidth, + (NewBounds.Min.y - Bounds->Min.y) / OldHeight, + }; + + v2 MaxInsetPercent = v2{ + (NewBounds.Max.x - Bounds->Min.x) / OldWidth, + (NewBounds.Max.y - Bounds->Min.y) / OldHeight, + }; + + UVs->Min.x = LerpR32(MinInsetPercent.x, UVs->Min.x, UVs->Max.x); + UVs->Min.y = LerpR32(MinInsetPercent.y, UVs->Min.y, UVs->Max.y); + UVs->Max.x = LerpR32(MaxInsetPercent.x, UVs->Min.x, UVs->Max.x); + UVs->Max.y = LerpR32(MaxInsetPercent.y, UVs->Min.y, UVs->Max.y); + + *Bounds = NewBounds; } internal void DrawCharacter_ (render_quad_batch_constructor* BatchConstructor, r32 MinX, r32 MinY, codepoint_bitmap CodepointInfo, rect2 ClippingBox, v4 Color) { - rect2 Bounds = {}; - Bounds.Min.x = FloorR32(MinX); - Bounds.Min.y = FloorR32(MinY); - Bounds.Max.x = Bounds.Min.x + (CodepointInfo.Width); - Bounds.Max.y = Bounds.Min.y + (CodepointInfo.Height); - - rect2 UVBounds = {}; - UVBounds.Min = CodepointInfo.UVMin; - UVBounds.Max = CodepointInfo.UVMax; - - ClipUVRect(&Bounds, &UVBounds, ClippingBox); - - s32 AlignedMinX = (s32)(MinX); - s32 AlignedMinY = (s32)(MinY); - s32 AlignedMaxX = AlignedMinX + (CodepointInfo.Width); - s32 AlignedMaxY = AlignedMinY + (CodepointInfo.Height); - - PushQuad2DOnBatch(BatchConstructor, - Rect2BottomLeft(Bounds), Rect2BottomRight(Bounds), - Rect2TopRight(Bounds), Rect2TopLeft(Bounds), - UVBounds.Min, UVBounds.Max, - Color); + rect2 Bounds = {}; + Bounds.Min.x = FloorR32(MinX); + Bounds.Min.y = FloorR32(MinY); + Bounds.Max.x = Bounds.Min.x + (CodepointInfo.Width); + Bounds.Max.y = Bounds.Min.y + (CodepointInfo.Height); + + rect2 UVBounds = {}; + UVBounds.Min = CodepointInfo.UVMin; + UVBounds.Max = CodepointInfo.UVMax; + + ClipUVRect(&Bounds, &UVBounds, ClippingBox); + + s32 AlignedMinX = (s32)(MinX); + s32 AlignedMinY = (s32)(MinY); + s32 AlignedMaxX = AlignedMinX + (CodepointInfo.Width); + s32 AlignedMaxY = AlignedMinY + (CodepointInfo.Height); + + PushQuad2DOnBatch(BatchConstructor, + Rect2BottomLeft(Bounds), Rect2BottomRight(Bounds), + Rect2TopRight(Bounds), Rect2TopLeft(Bounds), + UVBounds.Min, UVBounds.Max, + Color); } internal v2 DrawCharacterLeftAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, rect2 ClippingBox, v4 Color) { - s32 GlyphDataIndex = GetIndexForCodepoint(Font, C); - codepoint_bitmap CodepointInfo = Font.CodepointValues[GlyphDataIndex]; - - // NOTE(Peter): - r32 MinX = Position.x + CodepointInfo.XOffset; - r32 MinY = Position.y + CodepointInfo.YOffset; - DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color); - - // NOTE(Peter): - v2 PointAfterCharacter = v2{Position.x + CodepointInfo.Width, Position.y}; - return PointAfterCharacter; + s32 GlyphDataIndex = GetIndexForCodepoint(Font, C); + codepoint_bitmap CodepointInfo = Font.CodepointValues[GlyphDataIndex]; + + // NOTE(Peter): + r32 MinX = Position.x + CodepointInfo.XOffset; + r32 MinY = Position.y + CodepointInfo.YOffset; + DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color); + + // NOTE(Peter): + v2 PointAfterCharacter = v2{Position.x + CodepointInfo.Width, Position.y}; + return PointAfterCharacter; } internal v2 DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, rect2 ClippingBox, v4 Color) { - s32 GlyphDataIndex = GetIndexForCodepoint(Font, C); - codepoint_bitmap CodepointInfo = Font.CodepointValues[GlyphDataIndex]; - - // NOTE(Peter): - r32 MinX = Position.x - (CodepointInfo.XOffset + CodepointInfo.Width); - r32 MinY = Position.y + CodepointInfo.YOffset + CodepointInfo.YOffset; - DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color); - - // NOTE(Peter): - v2 PointAfterCharacter = v2{Position.x-(CodepointInfo.Width + CodepointInfo.XOffset), Position.y}; - return PointAfterCharacter; + s32 GlyphDataIndex = GetIndexForCodepoint(Font, C); + codepoint_bitmap CodepointInfo = Font.CodepointValues[GlyphDataIndex]; + + // NOTE(Peter): + r32 MinX = Position.x - (CodepointInfo.XOffset + CodepointInfo.Width); + r32 MinY = Position.y + CodepointInfo.YOffset + CodepointInfo.YOffset; + DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color); + + // NOTE(Peter): + v2 PointAfterCharacter = v2{Position.x-(CodepointInfo.Width + CodepointInfo.XOffset), Position.y}; + return PointAfterCharacter; } internal void DrawCursor (render_command_buffer* RenderBuffer, v2 RegisterPosition, bitmap_font* Font, v4 Color) { - rect2 CursorRect = {}; - CursorRect.Min = RegisterPosition; - CursorRect.Max = CursorRect.Min + v2{5, (r32)Font->Ascent}; - PushRenderQuad2D(RenderBuffer, CursorRect, Color); + rect2 CursorRect = {}; + CursorRect.Min = RegisterPosition; + CursorRect.Max = CursorRect.Min + v2{5, (r32)Font->Ascent}; + PushRenderQuad2D(RenderBuffer, CursorRect, Color); } internal v2 DrawStringLeftAligned (render_command_buffer* RenderBuffer, render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color, s32 CursorBeforeIndex, v4 CursorColor) { - v2 RegisterPosition = InitialRegisterPosition; - char* C = gs_string; - for (s32 i = 0; i < Length; i++) + v2 RegisterPosition = InitialRegisterPosition; + char* C = gs_string; + for (s32 i = 0; i < Length; i++) + { + if (i == CursorBeforeIndex) { - if (i == CursorBeforeIndex) - { - DrawCursor(RenderBuffer, RegisterPosition, Font, CursorColor); - } - - v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color); - RegisterPosition.x = PositionAfterCharacter.x; - C++; + DrawCursor(RenderBuffer, RegisterPosition, Font, CursorColor); } - if (CursorBeforeIndex == Length) - { - DrawCursor(RenderBuffer, RegisterPosition, Font, CursorColor); - } - return RegisterPosition; + + v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color); + RegisterPosition.x = PositionAfterCharacter.x; + C++; + } + if (CursorBeforeIndex == Length) + { + DrawCursor(RenderBuffer, RegisterPosition, Font, CursorColor); + } + return RegisterPosition; } internal v2 DrawStringRightAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color) { - v2 RegisterPosition = InitialRegisterPosition; - char* C = gs_string + Length - 1; - for (s32 i = Length - 1; i >= 0; i--) - { - v2 PositionAfterCharacter = DrawCharacterRightAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color); - RegisterPosition.x = PositionAfterCharacter.x; - C--; - } - return RegisterPosition; + v2 RegisterPosition = InitialRegisterPosition; + char* C = gs_string + Length - 1; + for (s32 i = Length - 1; i >= 0; i--) + { + v2 PositionAfterCharacter = DrawCharacterRightAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color); + RegisterPosition.x = PositionAfterCharacter.x; + C--; + } + return RegisterPosition; } internal v2 DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* Font, v2 Position, v4 Color, s32 CursorPosition, v4 CursorColor, gs_string_alignment Alignment = Align_Left) { - DEBUG_TRACK_FUNCTION; - v2 LowerRight = Position; - - render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length + 1, - Font->BitmapMemory, - Font->BitmapTextureHandle, - Font->BitmapWidth, - Font->BitmapHeight, - Font->BitmapBytesPerPixel, - Font->BitmapStride); - - // TODO(pjs): I don't like this solution but it'll do for now and I want to focus on other problems - // especially since I think this one will go away once I finish the ui overhaul - rect2 InfiniteClipBox = {}; - InfiniteClipBox.Min.x = -100000; - InfiniteClipBox.Min.y = -100000; - InfiniteClipBox.Max.x = 100000; - InfiniteClipBox.Max.y = 100000; - - v2 RegisterPosition = Position; - if (Alignment == Align_Left) - { - RegisterPosition = DrawStringLeftAligned(RenderBuffer, &BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color, CursorPosition, CursorColor); - } - else if (Alignment == Align_Right) - { - RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color); - } - else - { - InvalidCodePath; - } - - LowerRight.x = RegisterPosition.x; - - return LowerRight; + DEBUG_TRACK_FUNCTION; + v2 LowerRight = Position; + + render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length + 1, + Font->BitmapMemory, + Font->BitmapTextureHandle, + Font->BitmapWidth, + Font->BitmapHeight, + Font->BitmapBytesPerPixel, + Font->BitmapStride); + + // TODO(pjs): I don't like this solution but it'll do for now and I want to focus on other problems + // especially since I think this one will go away once I finish the ui overhaul + rect2 InfiniteClipBox = {}; + InfiniteClipBox.Min.x = -100000; + InfiniteClipBox.Min.y = -100000; + InfiniteClipBox.Max.x = 100000; + InfiniteClipBox.Max.y = 100000; + + v2 RegisterPosition = Position; + if (Alignment == Align_Left) + { + RegisterPosition = DrawStringLeftAligned(RenderBuffer, &BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color, CursorPosition, CursorColor); + } + else if (Alignment == Align_Right) + { + RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color); + } + else + { + InvalidCodePath; + } + + LowerRight.x = RegisterPosition.x; + + return LowerRight; } internal void DrawCursor (render_quad_batch_constructor* BatchConstructor, v2 Position, v4 Color, bitmap_font Font) { - v2 Min = Position; - v2 Max = Position + v2{(r32)Font.MaxCharWidth, (r32)(Font.Ascent + Font.Descent)}; - PushQuad2DOnBatch(BatchConstructor, Min, Max, Color); + v2 Min = Position; + v2 Max = Position + v2{(r32)Font.MaxCharWidth, (r32)(Font.Ascent + Font.Descent)}; + PushQuad2DOnBatch(BatchConstructor, Min, Max, Color); } #if 0 internal v2 DrawStringWithCursor (render_command_buffer* RenderBuffer, gs_string String, s32 CursorPosition, bitmap_font* Font, v2 Position, v4 Color, v4 CursorColor, gs_string_alignment Alignment = Align_Left) { - DEBUG_TRACK_FUNCTION; - v2 LowerRight = Position; - - // NOTE(Peter): We push this on first so that the cursor will be drawn underneath any character it may overlap with - render_quad_batch_constructor CursorBatch = PushRenderQuad2DBatch(RenderBuffer, 1); - render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length, - Font->BitmapMemory, - Font->BitmapTextureHandle, - Font->BitmapWidth, - Font->BitmapHeight, - Font->BitmapBytesPerPixel, - Font->BitmapStride); - - v2 RegisterPosition = Position; - if (Alignment == Align_Left) + DEBUG_TRACK_FUNCTION; + v2 LowerRight = Position; + + // NOTE(Peter): We push this on first so that the cursor will be drawn underneath any character it may overlap with + render_quad_batch_constructor CursorBatch = PushRenderQuad2DBatch(RenderBuffer, 1); + render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length, + Font->BitmapMemory, + Font->BitmapTextureHandle, + Font->BitmapWidth, + Font->BitmapHeight, + Font->BitmapBytesPerPixel, + Font->BitmapStride); + + v2 RegisterPosition = Position; + if (Alignment == Align_Left) + { + RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, Color); + DrawCursor(&CursorBatch, RegisterPosition, GreenV4, *Font); + if (String.Length - CursorPosition > 0) { - RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, Color); - DrawCursor(&CursorBatch, RegisterPosition, GreenV4, *Font); - if (String.Length - CursorPosition > 0) - { - RegisterPosition = DrawStringLeftAligned(&BatchConstructor, - String.Length - CursorPosition, - String.Str + CursorPosition, - RegisterPosition, Font, Color); - } + RegisterPosition = DrawStringLeftAligned(&BatchConstructor, + String.Length - CursorPosition, + String.Str + CursorPosition, + RegisterPosition, Font, Color); } - else if (Alignment == Align_Right) + } + else if (Alignment == Align_Right) + { + RegisterPosition = DrawStringRightAligned(&BatchConstructor, + CursorPosition, String.Str, + RegisterPosition, Font, Color); + DrawCursor(&CursorBatch, RegisterPosition, GreenV4, *Font); + if (String.Length - CursorPosition > 0) { - RegisterPosition = DrawStringRightAligned(&BatchConstructor, - CursorPosition, String.Str, - RegisterPosition, Font, Color); - DrawCursor(&CursorBatch, RegisterPosition, GreenV4, *Font); - if (String.Length - CursorPosition > 0) - { - RegisterPosition = DrawStringRightAligned(&BatchConstructor, - String.Length - CursorPosition, - String.Str + CursorPosition, - RegisterPosition, Font, Color); - } + RegisterPosition = DrawStringRightAligned(&BatchConstructor, + String.Length - CursorPosition, + String.Str + CursorPosition, + RegisterPosition, Font, Color); } - else - { - InvalidCodePath; - } - - LowerRight.x = RegisterPosition.x; - return LowerRight; + } + else + { + InvalidCodePath; + } + + LowerRight.x = RegisterPosition.x; + return LowerRight; } #endif enum ui_widget_flag { - UIWidgetFlag_ExpandsToFitChildren, - - UIWidgetFlag_DrawBackground, - UIWidgetFlag_DrawString, - UIWidgetFlag_DrawOutline, - UIWidgetFlag_DrawHorizontalFill, - UIWidgetFlag_DrawVerticalFill, - UIWidgetFlag_DrawFillReversed, - UIWidgetFlag_DrawFillAsHandle, - - UIWidgetFlag_Clickable, - UIWidgetFlag_Selectable, - UIWidgetFlag_Typable, + UIWidgetFlag_ExpandsToFitChildren, + + UIWidgetFlag_DrawBackground, + UIWidgetFlag_DrawString, + UIWidgetFlag_DrawOutline, + UIWidgetFlag_DrawHorizontalFill, + UIWidgetFlag_DrawVerticalFill, + UIWidgetFlag_DrawFillReversed, + UIWidgetFlag_DrawFillAsHandle, + + UIWidgetFlag_Clickable, + UIWidgetFlag_Selectable, + UIWidgetFlag_Typable, }; struct ui_widget_id { - u64 Id; - u64 ZIndex; + u64 Id; + u64 ZIndex; }; enum ui_layout_direction { - LayoutDirection_TopDown, - LayoutDirection_BottomUp, - LayoutDirection_Inherit, + LayoutDirection_TopDown, + LayoutDirection_BottomUp, + LayoutDirection_Inherit, }; struct ui_column { - r32 XMin; - r32 XMax; + r32 XMin; + r32 XMax; }; struct ui_widget { - ui_widget_id Id; - - gs_string String; - gs_string_alignment Alignment; - - rect2 Bounds; - u64 Flags; - - ui_widget* Next; - - // Slider - r32 FillPercent; - - // Layout - ui_widget* Parent; - - v2 Margin; - r32 RowHeight; - r32 RowYAt; - - ui_layout_direction FillDirection; - - ui_column* Columns; - u32 ColumnsCount; - u32 ColumnsFilled; - - // NOTE(pjs): I'm not sure this will stay but - // its here so that when we end things like a dropdown, - // we can check the retained state of that dropdown - ui_widget_id WidgetReference; - - u64 ChildZIndexOffset; - - ui_widget* ChildrenRoot; - ui_widget* ChildrenHead; - u32 ChildCount; + ui_widget_id Id; + + gs_string String; + gs_string_alignment Alignment; + + rect2 Bounds; + u64 Flags; + + ui_widget* Next; + + // Slider + r32 FillPercent; + + // Layout + ui_widget* Parent; + + v2 Margin; + r32 RowHeight; + r32 RowYAt; + + ui_layout_direction FillDirection; + + ui_column* Columns; + u32 ColumnsCount; + u32 ColumnsFilled; + + // NOTE(pjs): I'm not sure this will stay but + // its here so that when we end things like a dropdown, + // we can check the retained state of that dropdown + ui_widget_id WidgetReference; + + u64 ChildZIndexOffset; + + ui_widget* ChildrenRoot; + ui_widget* ChildrenHead; + u32 ChildCount; }; struct ui_eval_result { - bool Clicked; - bool Held; - v2 DragDelta; + bool Clicked; + bool Held; + v2 DragDelta; }; struct interface_config { - v4 PanelBG; - - // TODO(pjs): Turn these into _Default, _Hot, _Active - v4 ButtonColor_Inactive, ButtonColor_Active, ButtonColor_Selected; - - v4 TextColor; - + v4 PanelBG; + + // TODO(pjs): Turn these into _Default, _Hot, _Active + v4 ButtonColor_Inactive, ButtonColor_Active, ButtonColor_Selected; + + v4 TextColor; + #define LIST_BG_COLORS_COUNT 2 - v4 ListBGColors[LIST_BG_COLORS_COUNT]; - v4 ListBGHover; - v4 ListBGSelected; - - bitmap_font* Font; - r32 FontSize; - v2 Margin; - r32 RowHeight; + v4 ListBGColors[LIST_BG_COLORS_COUNT]; + v4 ListBGHover; + v4 ListBGSelected; + + bitmap_font* Font; + r32 FontSize; + v2 Margin; + r32 RowHeight; }; struct ui_widget_retained_state { - ui_widget_id Id; - bool Value; - r32 InitialValueR32; - u32 FramesSinceAccess; - - // For use in layouts that allow you to scroll / pan - v2 ChildrenDrawOffset; - - gs_string EditString; - - // For dropdowns and rows to be able to error check not closing - // a layout you open - u32 MaxChildren; + ui_widget_id Id; + bool Value; + r32 InitialValueR32; + u32 FramesSinceAccess; + + // For use in layouts that allow you to scroll / pan + v2 ChildrenDrawOffset; + + gs_string EditString; + + // For dropdowns and rows to be able to error check not closing + // a layout you open + u32 MaxChildren; }; struct ui_interface { - interface_config Style; - - mouse_state Mouse; - rect2 WindowBounds; - - // A per-frame string of the characters which have been typed - gs_const_string TempInputString; - u64 CursorPosition; - - render_command_buffer* RenderBuffer; - - ui_widget* Widgets; - u64 WidgetsCount; - u64 WidgetsCountMax; - - ui_widget* DrawOrderHead; - ui_widget* DrawOrderRoot; - - ui_widget_id HotWidget; - // This should really never get higher than 1 or 2 - u8 HotWidgetFramesSinceUpdate; - - ui_widget_id ActiveWidget; - ui_widget_id LastActiveWidget; - - ui_widget* ActiveLayout; - + interface_config Style; + + mouse_state Mouse; + rect2 WindowBounds; + + // A per-frame string of the characters which have been typed + gs_const_string TempInputString; + u64 CursorPosition; + + render_command_buffer* RenderBuffer; + + ui_widget* Widgets; + u64 WidgetsCount; + u64 WidgetsCountMax; + + ui_widget* DrawOrderHead; + ui_widget* DrawOrderRoot; + + ui_widget_id HotWidget; + // This should really never get higher than 1 or 2 + u8 HotWidgetFramesSinceUpdate; + + ui_widget_id ActiveWidget; + ui_widget_id LastActiveWidget; + + ui_widget* ActiveLayout; + #define RETAINED_STATE_MAX 128 - ui_widget_retained_state RetainedState[RETAINED_STATE_MAX]; - u64 RetainedStateCount; - - gs_memory_arena* PerFrameMemory; - - // TODO(pjs): DONT USE THIS - // Right now you only need this to create EditStrings for ui_widget_retained_state's - // and even for those, you eventually want a better solution than "create a string and it lives forever" - // TODO(pjs): Get rid of the need for this vvv - gs_memory_arena* Permanent; + ui_widget_retained_state RetainedState[RETAINED_STATE_MAX]; + u64 RetainedStateCount; + + gs_memory_arena* PerFrameMemory; + + // TODO(pjs): DONT USE THIS + // Right now you only need this to create EditStrings for ui_widget_retained_state's + // and even for those, you eventually want a better solution than "create a string and it lives forever" + // TODO(pjs): Get rid of the need for this vvv + gs_memory_arena* Permanent; }; internal void ui_InterfaceReset(ui_interface* Interface) { - Interface->WidgetsCount = 0; - Interface->DrawOrderHead = 0; - Interface->DrawOrderRoot = 0; - ClearArena(Interface->PerFrameMemory); - InterfaceAssert(Interface->PerFrameMemory); - - for (u32 i = 0; i < Interface->RetainedStateCount; i++) + Interface->WidgetsCount = 0; + Interface->DrawOrderHead = 0; + Interface->DrawOrderRoot = 0; + MemoryArenaClear(Interface->PerFrameMemory); + InterfaceAssert(Interface->PerFrameMemory); + + for (u32 i = 0; i < Interface->RetainedStateCount; i++) + { + Interface->RetainedState[i].FramesSinceAccess += 1; + if (Interface->RetainedState[i].FramesSinceAccess > 1) { - Interface->RetainedState[i].FramesSinceAccess += 1; - if (Interface->RetainedState[i].FramesSinceAccess > 1) - { - Interface->RetainedState[i].Id = {0}; - } + Interface->RetainedState[i].Id = {0}; } - - Interface->LastActiveWidget = Interface->ActiveWidget; + } + + Interface->LastActiveWidget = Interface->ActiveWidget; } internal bool ui_WidgetIdsEqual(ui_widget_id A, ui_widget_id B) { - bool Result = (A.Id == B.Id);// && (A.ParentId == B.ParentId); - return Result; + bool Result = (A.Id == B.Id);// && (A.ParentId == B.ParentId); + return Result; } internal void ui_WidgetSetFlag(ui_widget* Widget, u64 Flag) { - u64 Value = ((u64)1 << Flag); - Widget->Flags = Widget->Flags | Value; + u64 Value = ((u64)1 << Flag); + Widget->Flags = Widget->Flags | Value; } internal void ui_WidgetClearFlag(ui_widget* Widget, u64 Flag) { - u64 Value = ((u64)1 << Flag); - Widget->Flags = Widget->Flags & ~Value; + u64 Value = ((u64)1 << Flag); + Widget->Flags = Widget->Flags & ~Value; } internal bool ui_WidgetIsFlagSet(ui_widget Widget, u64 Flag) { - u64 Value = ((u64)1 << Flag); - bool Result = (Widget.Flags & Value); - return Result; + u64 Value = ((u64)1 << Flag); + bool Result = (Widget.Flags & Value); + return Result; } internal void ui_WidgetSetChildrenPopover(ui_widget* Widget) { - Widget->ChildZIndexOffset = 1000; + Widget->ChildZIndexOffset = 1000; } internal ui_widget* ui_WidgetGetWidgetWithId(ui_widget* Parent, ui_widget_id Id) { - ui_widget* Result = 0; - - if (ui_WidgetIdsEqual(Parent->Id, Id)) + ui_widget* Result = 0; + + if (ui_WidgetIdsEqual(Parent->Id, Id)) + { + Result = Parent; + } + else if (Parent->ChildrenRoot != 0) + { + for (ui_widget* At = Parent->ChildrenRoot; At != 0; At = At->Next) { - Result = Parent; + Result = ui_WidgetGetWidgetWithId(At, Id); + if (Result != 0) + { + break; + } } - else if (Parent->ChildrenRoot != 0) - { - for (ui_widget* At = Parent->ChildrenRoot; At != 0; At = At->Next) - { - Result = ui_WidgetGetWidgetWithId(At, Id); - if (Result != 0) - { - break; - } - } - } - - return Result; + } + + return Result; } internal ui_widget* ui_InterfaceGetWidgetWithId(ui_interface* Interface, ui_widget_id Id) { - ui_widget* Result = 0; - - for (ui_widget* At = Interface->DrawOrderRoot; At != 0; At = At->Next) + ui_widget* Result = 0; + + for (ui_widget* At = Interface->DrawOrderRoot; At != 0; At = At->Next) + { + Result = ui_WidgetGetWidgetWithId(At, Id); + if (Result != 0) { - Result = ui_WidgetGetWidgetWithId(At, Id); - if (Result != 0) - { - break; - } + break; } - - return Result; + } + + return Result; } internal ui_widget_retained_state* ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id) { - ui_widget_retained_state* Result = 0; - for (u64 i = 0; i < Interface->RetainedStateCount; i++) + ui_widget_retained_state* Result = 0; + for (u64 i = 0; i < Interface->RetainedStateCount; i++) + { + if (ui_WidgetIdsEqual(Interface->RetainedState[i].Id, Id)) { - if (ui_WidgetIdsEqual(Interface->RetainedState[i].Id, Id)) - { - // NOTE(PS): If we are accessing a retained state, it shouldn't - // have been accessed longer ago than the last frame - //Assert(Interface->RetainedState[i].FramesSinceAccess <= 2); - Interface->RetainedState[i].FramesSinceAccess = 0; - Result = Interface->RetainedState + i; - break; - } + // NOTE(PS): If we are accessing a retained state, it shouldn't + // have been accessed longer ago than the last frame + //Assert(Interface->RetainedState[i].FramesSinceAccess <= 2); + Interface->RetainedState[i].FramesSinceAccess = 0; + Result = Interface->RetainedState + i; + break; } - return Result; + } + return Result; } internal ui_widget_retained_state* ui_CreateRetainedState(ui_interface* Interface, ui_widget* Widget) { - u64 Index = RETAINED_STATE_MAX; - if (Interface->RetainedStateCount >= RETAINED_STATE_MAX) + u64 Index = RETAINED_STATE_MAX; + if (Interface->RetainedStateCount >= RETAINED_STATE_MAX) + { + for (u32 i = 0; i < Interface->RetainedStateCount; i++) { - for (u32 i = 0; i < Interface->RetainedStateCount; i++) - { - if (Interface->RetainedState[i].FramesSinceAccess > 0) - { - Index = i; - break; - } - } - } else { - Index = Interface->RetainedStateCount++; + if (Interface->RetainedState[i].FramesSinceAccess > 0) + { + Index = i; + break; + } } - - Assert(Index < RETAINED_STATE_MAX); - ui_widget_retained_state* Result = Interface->RetainedState + Index; - Result->Id = Widget->Id; - if (Result->EditString.Size != 256) - { - Result->EditString = PushString(Interface->Permanent, 256); - } - return Result; + } else { + Index = Interface->RetainedStateCount++; + } + + Assert(Index < RETAINED_STATE_MAX); + ui_widget_retained_state* Result = Interface->RetainedState + Index; + Result->Id = Widget->Id; + if (Result->EditString.Size != 256) + { + Result->EditString = PushString(Interface->Permanent, 256); + } + return Result; } internal ui_widget_retained_state* ui_GetOrCreateRetainedState(ui_interface* Interface, ui_widget* Widget) { - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); - if (!State) - { - State = ui_CreateRetainedState(Interface, Widget); - } - return State; + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (!State) + { + State = ui_CreateRetainedState(Interface, Widget); + } + return State; } internal ui_widget* ui_CreateWidget(ui_interface* Interface, gs_string String) { - InterfaceAssert(Interface->PerFrameMemory); - Assert(Interface->WidgetsCount < Interface->WidgetsCountMax); - u64 Index = Interface->WidgetsCount++; - ui_widget* Result = Interface->Widgets + Index; - ZeroStruct(Result); - - Result->Parent = Interface->ActiveLayout; - - u64 Id = HashDJB2ToU64(StringExpand(String)); - if (Result->Parent) - { - Id = HashAppendDJB2ToU32(Id, Result->Parent->Id.Id); - Id = HashAppendDJB2ToU32(Id, Result->Parent->ChildCount); - //Result->Id.ParentId = Result->Parent->Id.Id; - } - Result->Id.Id = Id; - - u64 ZIndex = Index + 1; - if (Result->Parent) - { - Result->ChildZIndexOffset += Result->Parent->ChildZIndexOffset; - ZIndex += Result->Parent->ChildZIndexOffset; - } - Result->Id.ZIndex = ZIndex; - - Result->String = PushStringCopy(Interface->PerFrameMemory, String.ConstString); - InterfaceAssert(Interface->PerFrameMemory); - Result->Alignment = Align_Left; - Result->Next = 0; - Result->ChildrenRoot = 0; - Result->ChildrenHead = 0; - Result->Flags = 0; - ui_WidgetSetFlag(Result, UIWidgetFlag_ExpandsToFitChildren); - - return Result; + InterfaceAssert(Interface->PerFrameMemory); + Assert(Interface->WidgetsCount < Interface->WidgetsCountMax); + u64 Index = Interface->WidgetsCount++; + ui_widget* Result = Interface->Widgets + Index; + ZeroStruct(Result); + + Result->Parent = Interface->ActiveLayout; + + u64 Id = HashDJB2ToU64(StringExpand(String)); + if (Result->Parent) + { + Id = HashAppendDJB2ToU32(Id, Result->Parent->Id.Id); + Id = HashAppendDJB2ToU32(Id, Result->Parent->ChildCount); + //Result->Id.ParentId = Result->Parent->Id.Id; + } + Result->Id.Id = Id; + + u64 ZIndex = Index + 1; + if (Result->Parent) + { + Result->ChildZIndexOffset += Result->Parent->ChildZIndexOffset; + ZIndex += Result->Parent->ChildZIndexOffset; + } + Result->Id.ZIndex = ZIndex; + + Result->String = PushStringCopy(Interface->PerFrameMemory, String.ConstString); + InterfaceAssert(Interface->PerFrameMemory); + Result->Alignment = Align_Left; + Result->Next = 0; + Result->ChildrenRoot = 0; + Result->ChildrenHead = 0; + Result->Flags = 0; + ui_WidgetSetFlag(Result, UIWidgetFlag_ExpandsToFitChildren); + + return Result; } // @@ -612,9 +612,9 @@ ui_CreateWidget(ui_interface* Interface, gs_string String) internal b32 ui_MouseClickedRect(ui_interface Interface, rect2 Rect) { - b32 Result = MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState); - Result &= PointIsInRect(Rect, Interface.Mouse.Pos); - return Result; + b32 Result = MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState); + Result &= PointIsInRect(Rect, Interface.Mouse.Pos); + return Result; } // Layout @@ -622,413 +622,413 @@ ui_MouseClickedRect(ui_interface Interface, rect2 Rect) static rect2 ui_ReserveBounds(ui_interface* Interface, ui_widget* Widget, bool Inset) { - Assert(Widget->ColumnsCount > 0); - rect2 Bounds = {0}; - u32 ColumnIndex = Widget->ChildCount % Widget->ColumnsCount; - - ui_column Column = Widget->Columns[ColumnIndex]; - Bounds.Min.x = Column.XMin; - Bounds.Min.y = Widget->RowYAt; - Bounds.Max.x = Column.XMax; - Bounds.Max.y = Bounds.Min.y + Widget->RowHeight; - - if (Inset) + Assert(Widget->ColumnsCount > 0); + rect2 Bounds = {0}; + u32 ColumnIndex = Widget->ChildCount % Widget->ColumnsCount; + + ui_column Column = Widget->Columns[ColumnIndex]; + Bounds.Min.x = Column.XMin; + Bounds.Min.y = Widget->RowYAt; + Bounds.Max.x = Column.XMax; + Bounds.Max.y = Bounds.Min.y + Widget->RowHeight; + + if (Inset) + { + Bounds.Min.x += Widget->Margin.x; + Bounds.Min.y += Widget->Margin.y; + Bounds.Max.x -= Widget->Margin.x; + Bounds.Max.y -= Widget->Margin.y; + } + + if (Widget->ChildCount == 0) + { + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (State) { - Bounds.Min.x += Widget->Margin.x; - Bounds.Min.y += Widget->Margin.y; - Bounds.Max.x -= Widget->Margin.x; - Bounds.Max.y -= Widget->Margin.y; + Bounds = Rect2Translate(Bounds, State->ChildrenDrawOffset); } - - if (Widget->ChildCount == 0) - { - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); - if (State) - { - Bounds = Rect2Translate(Bounds, State->ChildrenDrawOffset); - } - } - - return Bounds; + } + + return Bounds; } internal void ui_CommitBounds(ui_widget* Parent, rect2 Bounds) { - u32 ColumnIndex = Parent->ChildCount % Parent->ColumnsCount; - if (ColumnIndex == 0) + u32 ColumnIndex = Parent->ChildCount % Parent->ColumnsCount; + if (ColumnIndex == 0) + { + switch (Parent->FillDirection) { - switch (Parent->FillDirection) + case LayoutDirection_BottomUp: + { + Parent->RowYAt = Bounds.Max.y; + if (ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren)) { - case LayoutDirection_BottomUp: - { - Parent->RowYAt = Bounds.Max.y; - if (ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren)) - { - Parent->Bounds.Max.y = Parent->RowYAt; - } - }break; - - case LayoutDirection_TopDown: - { - Parent->RowYAt = Bounds.Min.y - Parent->RowHeight; - if (ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren)) - { - Parent->Bounds.Min.y = Bounds.Min.y; - } - }break; + Parent->Bounds.Max.y = Parent->RowYAt; } + }break; + + case LayoutDirection_TopDown: + { + Parent->RowYAt = Bounds.Min.y - Parent->RowHeight; + if (ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren)) + { + Parent->Bounds.Min.y = Bounds.Min.y; + } + }break; } + } } internal void ui_ExpandParentToFit(ui_widget* Widget) { - ui_widget* Parent = Widget->Parent; - switch (Widget->FillDirection) + ui_widget* Parent = Widget->Parent; + switch (Widget->FillDirection) + { + case LayoutDirection_TopDown: { - case LayoutDirection_TopDown: - { - Parent->Bounds.Min.y = Min(Parent->Bounds.Min.y, Widget->Bounds.Min.y - Parent->Margin.y); - }break; - - case LayoutDirection_BottomUp: - { - Parent->Bounds.Max.y = Max(Parent->Bounds.Max.y, Widget->Bounds.Max.y + Parent->Margin.y); - }break; - - InvalidDefaultCase; - } + Parent->Bounds.Min.y = Min(Parent->Bounds.Min.y, Widget->Bounds.Min.y - Parent->Margin.y); + }break; + + case LayoutDirection_BottomUp: + { + Parent->Bounds.Max.y = Max(Parent->Bounds.Max.y, Widget->Bounds.Max.y + Parent->Margin.y); + }break; + + InvalidDefaultCase; + } } internal void ui_WidgetCreateColumns(ui_widget* Widget, u32 ColumnsCount, ui_interface* Interface) { - Widget->Columns = PushArray(Interface->PerFrameMemory, ui_column, ColumnsCount); - InterfaceAssert(Interface->PerFrameMemory); - Widget->ColumnsCount = ColumnsCount; - Widget->ColumnsFilled = 0; + Widget->Columns = PushArray(Interface->PerFrameMemory, ui_column, ColumnsCount); + InterfaceAssert(Interface->PerFrameMemory); + Widget->ColumnsCount = ColumnsCount; + Widget->ColumnsFilled = 0; } internal void ui_WidgetInitUniformColumns(ui_widget* Widget) { - r32 CurrentRowWidth = Rect2Width(Widget->Bounds); - r32 ColumnWidth = CurrentRowWidth / Widget->ColumnsCount; - for (u32 i = 0; i < Widget->ColumnsCount; i++) - { - ui_column* Column = Widget->Columns + i; - Column->XMin = Widget->Bounds.Min.x + (ColumnWidth * i); - Column->XMax = Column->XMin + ColumnWidth; - } + r32 CurrentRowWidth = Rect2Width(Widget->Bounds); + r32 ColumnWidth = CurrentRowWidth / Widget->ColumnsCount; + for (u32 i = 0; i < Widget->ColumnsCount; i++) + { + ui_column* Column = Widget->Columns + i; + Column->XMin = Widget->Bounds.Min.x + (ColumnWidth * i); + Column->XMax = Column->XMin + ColumnWidth; + } } internal ui_widget* ui_CreateLayoutWidget(ui_interface* Interface, rect2 Bounds, gs_string Name, ui_layout_direction FillDir = LayoutDirection_Inherit) { - ui_widget* Result = ui_CreateWidget(Interface, Name); - //ui_WidgetSetFlag(Result, UIWidgetFlag_DrawOutline); - - Result->Bounds = Bounds; - Result->Margin = Interface->Style.Margin; - Result->RowHeight = Interface->Style.RowHeight; - - // Single Column Layout - ui_WidgetCreateColumns(Result, 1, Interface); - ui_WidgetInitUniformColumns(Result); - - if (FillDir == LayoutDirection_Inherit && Result->Parent != 0) + ui_widget* Result = ui_CreateWidget(Interface, Name); + //ui_WidgetSetFlag(Result, UIWidgetFlag_DrawOutline); + + Result->Bounds = Bounds; + Result->Margin = Interface->Style.Margin; + Result->RowHeight = Interface->Style.RowHeight; + + // Single Column Layout + ui_WidgetCreateColumns(Result, 1, Interface); + ui_WidgetInitUniformColumns(Result); + + if (FillDir == LayoutDirection_Inherit && Result->Parent != 0) + { + Result->FillDirection = Result->Parent->FillDirection; + } + else + { + Result->FillDirection = FillDir; + } + + switch(Result->FillDirection) + { + case LayoutDirection_BottomUp: { - Result->FillDirection = Result->Parent->FillDirection; - } - else - { - Result->FillDirection = FillDir; - } + Result->RowYAt = Bounds.Min.y; + }break; - switch(Result->FillDirection) + case LayoutDirection_TopDown: { - case LayoutDirection_BottomUp: - { - Result->RowYAt = Bounds.Min.y; - }break; - - case LayoutDirection_TopDown: - { - Result->RowYAt = Bounds.Max.y - Result->RowHeight; - }break; - - InvalidDefaultCase; - } + Result->RowYAt = Bounds.Max.y - Result->RowHeight; + }break; - return Result; + InvalidDefaultCase; + } + + return Result; } static ui_widget* ui_PushOverlayLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name) { - ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir); - SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result); - Interface->ActiveLayout = Result; - return Result; + ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir); + SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result); + Interface->ActiveLayout = Result; + return Result; } static gs_string ui_PushLayoutCategoryName(ui_interface* Interface, gs_string Category, u32 Value) { - gs_string Result = PushStringF(Interface->PerFrameMemory, - Category.Length + 25, - "%S%d", - Category.ConstString, - Value); - return Result; + gs_string Result = PushStringF(Interface->PerFrameMemory, + Category.Length + 25, + "%S%d", + Category.ConstString, + Value); + return Result; } static ui_widget* ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name) { - ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir); - - if (Interface->DrawOrderRoot) - { - SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Result); - Interface->ActiveLayout->ChildCount++; - } - else - { - SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result); - } - - Interface->ActiveLayout = Result; - return Result; + ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir); + + if (Interface->DrawOrderRoot) + { + SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Result); + Interface->ActiveLayout->ChildCount++; + } + else + { + SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result); + } + + Interface->ActiveLayout = Result; + return Result; } static ui_widget* ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Category, u32 Value) { - gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Value); - return ui_PushLayout(Interface, Bounds, FillDir, Name); + gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Value); + return ui_PushLayout(Interface, Bounds, FillDir, Name); } static ui_widget* ui_PushLayout(ui_interface* Interface, gs_string Name, bool Inset = true) { - rect2 Bounds = {}; - ui_layout_direction Direction = LayoutDirection_TopDown; - if (Interface->ActiveLayout) + rect2 Bounds = {}; + ui_layout_direction Direction = LayoutDirection_TopDown; + if (Interface->ActiveLayout) + { + Bounds = ui_ReserveBounds(Interface, Interface->ActiveLayout, Inset); + Direction = Interface->ActiveLayout->FillDirection; + } + else + { + Bounds.Min.x = Interface->WindowBounds.Min.x; + Bounds.Min.y = Interface->WindowBounds.Max.y; + Bounds.Max.x = Interface->WindowBounds.Max.x; + Bounds.Max.y = Interface->WindowBounds.Max.y; + + if (Inset) { - Bounds = ui_ReserveBounds(Interface, Interface->ActiveLayout, Inset); - Direction = Interface->ActiveLayout->FillDirection; + Bounds.Min.x += Interface->Style.Margin.x; + Bounds.Max.x -= Interface->Style.Margin.x; } - else - { - Bounds.Min.x = Interface->WindowBounds.Min.x; - Bounds.Min.y = Interface->WindowBounds.Max.y; - Bounds.Max.x = Interface->WindowBounds.Max.x; - Bounds.Max.y = Interface->WindowBounds.Max.y; - - if (Inset) - { - Bounds.Min.x += Interface->Style.Margin.x; - Bounds.Max.x -= Interface->Style.Margin.x; - } - - Direction = LayoutDirection_TopDown; - } - return ui_PushLayout(Interface, Bounds, Direction, Name); + + Direction = LayoutDirection_TopDown; + } + return ui_PushLayout(Interface, Bounds, Direction, Name); } internal void ui_ExpandToFitChildren(ui_widget* Parent) { - if (!ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren)) { return; } - - v2 Extents = { Parent->Bounds.Max.y, Parent->Bounds.Min.y }; - for (ui_widget* Child = Parent->ChildrenRoot; Child != 0; Child = Child->Next) + if (!ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren)) { return; } + + v2 Extents = { Parent->Bounds.Max.y, Parent->Bounds.Min.y }; + for (ui_widget* Child = Parent->ChildrenRoot; Child != 0; Child = Child->Next) + { + Extents.x = Min(Extents.x, Child->Bounds.Min.y); + Extents.y = Max(Extents.y, Child->Bounds.Max.y); + } + + switch(Parent->FillDirection) + { + case LayoutDirection_BottomUp: { - Extents.x = Min(Extents.x, Child->Bounds.Min.y); - Extents.y = Max(Extents.y, Child->Bounds.Max.y); - } + Parent->Bounds.Max.y = Max(Extents.y + Parent->Margin.y, Parent->Bounds.Max.y); + }break; - switch(Parent->FillDirection) + case LayoutDirection_TopDown: { - case LayoutDirection_BottomUp: - { - Parent->Bounds.Max.y = Max(Extents.y + Parent->Margin.y, Parent->Bounds.Max.y); - }break; - - case LayoutDirection_TopDown: - { - Parent->Bounds.Min.y = Min(Extents.x - Parent->Margin.y, Parent->Bounds.Min.y); - }break; - - InvalidDefaultCase; - } + Parent->Bounds.Min.y = Min(Extents.x - Parent->Margin.y, Parent->Bounds.Min.y); + }break; + + InvalidDefaultCase; + } } static void ui_PopLayout(ui_interface* Interface, gs_string LayoutName) { - Assert(Interface->ActiveLayout != 0); - - ui_widget* Layout = Interface->ActiveLayout; - - // NOTE(pjs): If this isn't true then a layout was opened without being closed - // Go check for ui_PushLayout, ui_BeginDropdown, ui_BeginRow, etc that don't have - // a corresponding ui_Pop/ui_End* - // - // We use StringsEqualUpToLength here becuase its possible that - // the current layout used the Category + Identifier method - // for generating Layout->String. And if Identifier was a string - // that was edited within the scope of this layout, then - // Layout->String and LayoutName will no longer match. - // - // This is a compromise that will at least let us know if - // we aren't popping all our layouts correctly. - Assert(StringsEqualUpToLength(Layout->String, LayoutName, LayoutName.Length)); - - ui_ExpandToFitChildren(Layout); - - Interface->ActiveLayout = Interface->ActiveLayout->Parent; - - // NOTE(pjs): This indicates that the parent layout should - // expand to fit the layout that we just popped - if (Interface->ActiveLayout != 0 && - Interface->ActiveLayout->ChildrenHead == Layout) - { - ui_CommitBounds(Interface->ActiveLayout, Layout->Bounds); - } + Assert(Interface->ActiveLayout != 0); + + ui_widget* Layout = Interface->ActiveLayout; + + // NOTE(pjs): If this isn't true then a layout was opened without being closed + // Go check for ui_PushLayout, ui_BeginDropdown, ui_BeginRow, etc that don't have + // a corresponding ui_Pop/ui_End* + // + // We use StringsEqualUpToLength here becuase its possible that + // the current layout used the Category + Identifier method + // for generating Layout->String. And if Identifier was a string + // that was edited within the scope of this layout, then + // Layout->String and LayoutName will no longer match. + // + // This is a compromise that will at least let us know if + // we aren't popping all our layouts correctly. + Assert(StringsEqualUpToLength(Layout->String, LayoutName, LayoutName.Length)); + + ui_ExpandToFitChildren(Layout); + + Interface->ActiveLayout = Interface->ActiveLayout->Parent; + + // NOTE(pjs): This indicates that the parent layout should + // expand to fit the layout that we just popped + if (Interface->ActiveLayout != 0 && + Interface->ActiveLayout->ChildrenHead == Layout) + { + ui_CommitBounds(Interface->ActiveLayout, Layout->Bounds); + } } static void ui_PopLayout(ui_interface* Interface, gs_string Category, u32 Value) { - gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Value); - ui_PopLayout(Interface, Name); + gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Value); + ui_PopLayout(Interface, Name); } static ui_widget* ui_BeginRow(ui_interface* Interface, u32 ColumnsMax) { - ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false); - ui_WidgetCreateColumns(Layout, ColumnsMax, Interface); - ui_WidgetInitUniformColumns(Layout); - return Layout; + ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false); + ui_WidgetCreateColumns(Layout, ColumnsMax, Interface); + ui_WidgetInitUniformColumns(Layout); + return Layout; } enum ui_column_size_rule { - UIColumnSize_Fixed, - UIColumnSize_Percent, - UIColumnSize_Fill, - UIColumnSize_MaxWidth, + UIColumnSize_Fixed, + UIColumnSize_Percent, + UIColumnSize_Fill, + UIColumnSize_MaxWidth, }; struct ui_column_spec { - ui_column_size_rule Rule; - union - { - r32 Width; - r32 Percent; - }; + ui_column_size_rule Rule; + union + { + r32 Width; + r32 Percent; + }; }; static ui_widget* ui_BeginRow(ui_interface* Interface, u32 ColumnsMax, ui_column_spec* ColumnRules) { - ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false); - ui_WidgetCreateColumns(Layout, ColumnsMax, Interface); + ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false); + ui_WidgetCreateColumns(Layout, ColumnsMax, Interface); + + // First Pass, determine widths of each column, and how much space is left to be divided by the fill columns + // If a size is specified, it is stored in Column->XMax + r32 RowWidth = Rect2Width(Layout->Bounds); + r32 RemainingSpace = RowWidth; + u32 FillColumnsCount = 0; + for (u32 i = 0; i < Layout->ColumnsCount; i++) + { + ui_column_spec Spec = ColumnRules[i]; + ui_column* Column = Layout->Columns + i; - // First Pass, determine widths of each column, and how much space is left to be divided by the fill columns - // If a size is specified, it is stored in Column->XMax - r32 RowWidth = Rect2Width(Layout->Bounds); - r32 RemainingSpace = RowWidth; - u32 FillColumnsCount = 0; - for (u32 i = 0; i < Layout->ColumnsCount; i++) + switch (Spec.Rule) { - ui_column_spec Spec = ColumnRules[i]; - ui_column* Column = Layout->Columns + i; - - switch (Spec.Rule) + case UIColumnSize_Fixed: + { + Column->XMax = Spec.Width; + RemainingSpace -= Column->XMax; + }break; + + case UIColumnSize_Percent: + { + Column->XMax = Spec.Percent * RowWidth; + RemainingSpace -= Column->XMax; + }break; + + case UIColumnSize_Fill: + { + FillColumnsCount += 1; + }break; + + case UIColumnSize_MaxWidth: + { + if (RemainingSpace >= Spec.Width) { - case UIColumnSize_Fixed: - { - Column->XMax = Spec.Width; - RemainingSpace -= Column->XMax; - }break; - - case UIColumnSize_Percent: - { - Column->XMax = Spec.Percent * RowWidth; - RemainingSpace -= Column->XMax; - }break; - - case UIColumnSize_Fill: - { - FillColumnsCount += 1; - }break; - - case UIColumnSize_MaxWidth: - { - if (RemainingSpace >= Spec.Width) - { - Column->XMax = Spec.Width; - } - else - { - Column->XMax = RemainingSpace; - } - RemainingSpace -= Column->XMax; - }break; - - InvalidDefaultCase; + Column->XMax = Spec.Width; } + else + { + Column->XMax = RemainingSpace; + } + RemainingSpace -= Column->XMax; + }break; + + InvalidDefaultCase; + } + } + + r32 FillColumnWidth = RemainingSpace / FillColumnsCount; + + // Second Pass, specify the actual XMin and XMax of each column + r32 ColumnStartX = Layout->Bounds.Min.x; + for (u32 i = 0; i < Layout->ColumnsCount; i++) + { + ui_column_spec Spec = ColumnRules[i]; + ui_column* Column = Layout->Columns + i; + + r32 ColumnWidth = 0; + switch (Spec.Rule) + { + case UIColumnSize_Fixed: + case UIColumnSize_Percent: + case UIColumnSize_MaxWidth: + { + ColumnWidth = Column->XMax; + }break; + + case UIColumnSize_Fill: + { + ColumnWidth = FillColumnWidth; + }break; } - r32 FillColumnWidth = RemainingSpace / FillColumnsCount; - - // Second Pass, specify the actual XMin and XMax of each column - r32 ColumnStartX = Layout->Bounds.Min.x; - for (u32 i = 0; i < Layout->ColumnsCount; i++) - { - ui_column_spec Spec = ColumnRules[i]; - ui_column* Column = Layout->Columns + i; - - r32 ColumnWidth = 0; - switch (Spec.Rule) - { - case UIColumnSize_Fixed: - case UIColumnSize_Percent: - case UIColumnSize_MaxWidth: - { - ColumnWidth = Column->XMax; - }break; - - case UIColumnSize_Fill: - { - ColumnWidth = FillColumnWidth; - }break; - } - - Column->XMin = ColumnStartX ; - Column->XMax = Column->XMin + Max(0, ColumnWidth); - ColumnStartX = Column->XMax; - } - - return Layout; + Column->XMin = ColumnStartX ; + Column->XMax = Column->XMin + Max(0, ColumnWidth); + ColumnStartX = Column->XMax; + } + + return Layout; } static void ui_EndRow(ui_interface* Interface) { - ui_PopLayout(Interface, MakeString("Row")); + ui_PopLayout(Interface, MakeString("Row")); } static rect2 ui_LayoutRemaining(ui_widget Layout) { - rect2 Result = Layout.Bounds; - Result.Max.y = Layout.RowYAt; - return Result; + rect2 Result = Layout.Bounds; + Result.Max.y = Layout.RowYAt; + return Result; } // Widgets @@ -1036,89 +1036,89 @@ ui_LayoutRemaining(ui_widget Layout) internal ui_eval_result ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds) { - ui_eval_result Result = {}; - - Widget->Bounds = Bounds; - SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Widget); - Interface->ActiveLayout->ChildCount += 1; - ui_CommitBounds(Widget->Parent, Widget->Bounds); - - if (PointIsInRect(Widget->Parent->Bounds, Interface->Mouse.Pos) && - PointIsInRect(Widget->Bounds, Interface->Mouse.Pos)) + ui_eval_result Result = {}; + + Widget->Bounds = Bounds; + SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Widget); + Interface->ActiveLayout->ChildCount += 1; + ui_CommitBounds(Widget->Parent, Widget->Bounds); + + if (PointIsInRect(Widget->Parent->Bounds, Interface->Mouse.Pos) && + PointIsInRect(Widget->Bounds, Interface->Mouse.Pos)) + { + if (MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState)) { - if (MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState)) - { - if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id)) - { - Result.Clicked = true; - Interface->ActiveWidget = Widget->Id; - - if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable)) - { - Interface->CursorPosition = Widget->String.Length; - } - } - } + if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id)) + { + Result.Clicked = true; + Interface->ActiveWidget = Widget->Id; - if (Interface->HotWidget.ZIndex == 0 || - Interface->HotWidget.ZIndex <= Widget->Id.ZIndex) + if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable)) { - Interface->HotWidget = Widget->Id; - Interface->HotWidgetFramesSinceUpdate = 0; - } - } - else - { - if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id) && - MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState)) - { - Interface->ActiveWidget = {}; + Interface->CursorPosition = Widget->String.Length; } + } } - if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id)) + if (Interface->HotWidget.ZIndex == 0 || + Interface->HotWidget.ZIndex <= Widget->Id.ZIndex) { - // click & drag - if (MouseButtonHeldDown(Interface->Mouse.LeftButtonState)) - { - Result.Held = true; - Result.DragDelta = Interface->Mouse.Pos - Interface->Mouse.DownPos; - } - - if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable) && - Interface->TempInputString.Length > 0) - { - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); - - Interface->CursorPosition = Clamp(0, Interface->CursorPosition, State->EditString.Length); - for (u32 i = 0; i < Interface->TempInputString.Length; i++) - { - if (Interface->TempInputString.Str[i] == '\b') - { - if (Interface->CursorPosition > 0) - { - State->EditString.Length -= 1; - } - } - else - { - OutChar(&State->EditString, Interface->TempInputString.Str[i]); - Interface->CursorPosition += 1; - } - } - } + Interface->HotWidget = Widget->Id; + Interface->HotWidgetFramesSinceUpdate = 0; + } + } + else + { + if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id) && + MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState)) + { + Interface->ActiveWidget = {}; + } + } + + if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id)) + { + // click & drag + if (MouseButtonHeldDown(Interface->Mouse.LeftButtonState)) + { + Result.Held = true; + Result.DragDelta = Interface->Mouse.Pos - Interface->Mouse.DownPos; } - Assert(Widget->Parent != 0); - return Result; + if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable) && + Interface->TempInputString.Length > 0) + { + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + + Interface->CursorPosition = Clamp(0, Interface->CursorPosition, State->EditString.Length); + for (u32 i = 0; i < Interface->TempInputString.Length; i++) + { + if (Interface->TempInputString.Str[i] == '\b') + { + if (Interface->CursorPosition > 0) + { + State->EditString.Length -= 1; + } + } + else + { + OutChar(&State->EditString, Interface->TempInputString.Str[i]); + Interface->CursorPosition += 1; + } + } + } + } + + Assert(Widget->Parent != 0); + return Result; } internal ui_eval_result ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget) { - ui_widget* Layout = Interface->ActiveLayout; - rect2 Bounds = ui_ReserveBounds(Interface, Layout, true); - return ui_EvaluateWidget(Interface, Widget, Bounds); + ui_widget* Layout = Interface->ActiveLayout; + rect2 Bounds = ui_ReserveBounds(Interface, Layout, true); + return ui_EvaluateWidget(Interface, Widget, Bounds); } // @@ -1128,549 +1128,549 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget) static r32 ui_GetTextLineHeight(ui_interface Interface) { - r32 Result = Interface.Style.Font->PixelHeight + (2 * Interface.Style.Margin.y); - return Result; + r32 Result = Interface.Style.Font->PixelHeight + (2 * Interface.Style.Margin.y); + return Result; } static void ui_FillRect(ui_interface* Interface, rect2 Bounds, v4 Color) { - PushRenderQuad2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, Color); + PushRenderQuad2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, Color); } static void ui_OutlineRect(ui_interface* Interface, rect2 Bounds, r32 Thickness, v4 Color) { - PushRenderBoundingBox2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, Thickness, Color); + PushRenderBoundingBox2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, Thickness, Color); } internal void ui_Label(ui_interface* Interface, gs_string String, rect2 Bounds, gs_string_alignment Alignment = Align_Left) { - DEBUG_TRACK_FUNCTION; - ui_widget* Widget = ui_CreateWidget(Interface, String); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); - ui_EvaluateWidget(Interface, Widget, Bounds); + DEBUG_TRACK_FUNCTION; + ui_widget* Widget = ui_CreateWidget(Interface, String); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_EvaluateWidget(Interface, Widget, Bounds); } internal void ui_Label(ui_interface* Interface, gs_string String, gs_string_alignment Alignment = Align_Left) { - DEBUG_TRACK_FUNCTION; - ui_widget* Widget = ui_CreateWidget(Interface, String); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); - ui_EvaluateWidget(Interface, Widget); + DEBUG_TRACK_FUNCTION; + ui_widget* Widget = ui_CreateWidget(Interface, String); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_EvaluateWidget(Interface, Widget); } internal void ui_TextEntrySetFlags(ui_widget* Widget, gs_string EditString) { - Widget->String = EditString; - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); - ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(Widget, UIWidgetFlag_Selectable); - ui_WidgetSetFlag(Widget, UIWidgetFlag_Typable); + Widget->String = EditString; + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Selectable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Typable); } internal bool ui_TextEntry(ui_interface* Interface, gs_string Identifier, gs_string* Value) { - ui_widget* Widget = ui_CreateWidget(Interface, Identifier); - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); - if (!State) - { - State = ui_CreateRetainedState(Interface, Widget); - } - PrintF(&State->EditString, "%S", *Value); - - ui_TextEntrySetFlags(Widget, State->EditString); - - ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); - bool StringEdited = !StringsEqual(*Value, State->EditString); - PrintF(Value, "%S", State->EditString); - - return StringEdited; + ui_widget* Widget = ui_CreateWidget(Interface, Identifier); + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (!State) + { + State = ui_CreateRetainedState(Interface, Widget); + } + PrintF(&State->EditString, "%S", *Value); + + ui_TextEntrySetFlags(Widget, State->EditString); + + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); + bool StringEdited = !StringsEqual(*Value, State->EditString); + PrintF(Value, "%S", State->EditString); + + return StringEdited; } internal u64 ui_TextEntryU64(ui_interface* Interface, gs_string String, u64 CurrentValue) { - ui_widget* Widget = ui_CreateWidget(Interface, String); - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); - if (!State) - { - State = ui_CreateRetainedState(Interface, Widget); - PrintF(&State->EditString, "%u", CurrentValue); - } - ui_TextEntrySetFlags(Widget, State->EditString); - - ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); - parse_uint_result ParseResult = ValidateAndParseUInt(State->EditString.ConstString); - u64 ValueResult = CurrentValue; - if (ParseResult.Success) - { - ValueResult = ParseResult.Value; - } - return ValueResult; + ui_widget* Widget = ui_CreateWidget(Interface, String); + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (!State) + { + State = ui_CreateRetainedState(Interface, Widget); + PrintF(&State->EditString, "%u", CurrentValue); + } + ui_TextEntrySetFlags(Widget, State->EditString); + + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); + parse_uint_result ParseResult = ValidateAndParseUInt(State->EditString.ConstString); + u64 ValueResult = CurrentValue; + if (ParseResult.Success) + { + ValueResult = ParseResult.Value; + } + return ValueResult; } internal r64 ui_TextEntryR64(ui_interface* Interface, gs_string String, r64 CurrentValue) { - ui_widget* Widget = ui_CreateWidget(Interface, String); - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); - if (!State) - { - State = ui_CreateRetainedState(Interface, Widget); - PrintF(&State->EditString, "%f", CurrentValue); - } - ui_TextEntrySetFlags(Widget, State->EditString); - ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); - parse_float_result ParseResult = ValidateAndParseFloat(State->EditString.ConstString); - r64 ValueResult = CurrentValue; - if (ParseResult.Success) - { - ValueResult = ParseResult.Value; - } - return ValueResult; + ui_widget* Widget = ui_CreateWidget(Interface, String); + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (!State) + { + State = ui_CreateRetainedState(Interface, Widget); + PrintF(&State->EditString, "%f", CurrentValue); + } + ui_TextEntrySetFlags(Widget, State->EditString); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); + parse_float_result ParseResult = ValidateAndParseFloat(State->EditString.ConstString); + r64 ValueResult = CurrentValue; + if (ParseResult.Success) + { + ValueResult = ParseResult.Value; + } + return ValueResult; } internal ui_widget* ui_CreateButtonWidget(ui_interface* Interface, gs_string Text) { - ui_widget* Widget = ui_CreateWidget(Interface, Text); - ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); - return Widget; + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + return Widget; } internal b32 ui_Button(ui_interface* Interface, gs_string Text) { - ui_widget* Widget = ui_CreateButtonWidget(Interface, Text); - ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); - return Result.Clicked; + ui_widget* Widget = ui_CreateButtonWidget(Interface, Text); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); + return Result.Clicked; } internal b32 ui_Button(ui_interface* Interface, gs_string Text, rect2 Bounds) { - ui_widget* Widget = ui_CreateButtonWidget(Interface, Text); - ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds); - return Result.Clicked; + ui_widget* Widget = ui_CreateButtonWidget(Interface, Text); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds); + return Result.Clicked; } struct list_item_colors { - v4 Hover; - v4 Selected; - v4 BGColor; + v4 Hover; + v4 Selected; + v4 BGColor; }; inline v4 ui_GetListItemBGColor(interface_config Style, u32 ElementIndex) { - v4 Result = Style.ListBGColors[ElementIndex % LIST_BG_COLORS_COUNT]; - return Result; + v4 Result = Style.ListBGColors[ElementIndex % LIST_BG_COLORS_COUNT]; + return Result; } static list_item_colors ui_GetListItemColors(ui_interface* Interface, u32 ListItemIndex) { - list_item_colors Result = {}; - Result.Hover = Interface->Style.ListBGHover; - Result.Selected = Interface->Style.ListBGSelected; - Result.BGColor = ui_GetListItemBGColor(Interface->Style, ListItemIndex); - return Result; + list_item_colors Result = {}; + Result.Hover = Interface->Style.ListBGHover; + Result.Selected = Interface->Style.ListBGSelected; + Result.BGColor = ui_GetListItemBGColor(Interface->Style, ListItemIndex); + return Result; } static b32 ui_ListButton(ui_interface* Interface, gs_string Text, rect2 Bounds, u32 ListItemIndex) { - ui_widget* Widget = ui_CreateWidget(Interface, Text); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); - ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); - // TODO(pjs): Reimplement alternating color backgrounds - Widget->Bounds = Bounds; - ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); - return Result.Clicked; + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + // TODO(pjs): Reimplement alternating color backgrounds + Widget->Bounds = Bounds; + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); + return Result.Clicked; } static b32 ui_LayoutListButton(ui_interface* Interface, gs_string Text, u32 ListItemIndex) { - // TODO(pjs): Reimplement alternating colors - return ui_Button(Interface, Text); + // TODO(pjs): Reimplement alternating colors + return ui_Button(Interface, Text); } internal bool ui_EvaluateDropdown(ui_interface* Interface, ui_widget* Widget, ui_eval_result EvalResult) { - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); - if (!State) + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (!State) + { + State = ui_CreateRetainedState(Interface, Widget); + } + + if (EvalResult.Clicked) + { + State->Value = !State->Value; + } + + if (State->Value) + { + ui_widget ParentLayout = *Interface->ActiveLayout; + + r32 SpaceAbove = Interface->WindowBounds.Max.y - Widget->Bounds.Max.y; + r32 SpaceBelow = Widget->Bounds.Min.y - Interface->WindowBounds.Min.y; + ui_layout_direction Direction = LayoutDirection_TopDown; + rect2 MenuBounds = {}; + + if (SpaceAbove > SpaceBelow) { - State = ui_CreateRetainedState(Interface, Widget); + r32 ParentLayoutMaxY = ParentLayout.Bounds.Max.y; + Direction = LayoutDirection_BottomUp; + MenuBounds = rect2{ + v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, Widget->Bounds.Max.y }, + v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, ParentLayoutMaxY } + }; + } + else + { + r32 ParentLayoutMinY = ParentLayout.Bounds.Min.y; + Direction = LayoutDirection_TopDown; + MenuBounds = rect2{ + v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, ParentLayoutMinY }, + v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, Widget->Bounds.Min.y } + }; } - if (EvalResult.Clicked) - { - State->Value = !State->Value; - } - - if (State->Value) - { - ui_widget ParentLayout = *Interface->ActiveLayout; - - r32 SpaceAbove = Interface->WindowBounds.Max.y - Widget->Bounds.Max.y; - r32 SpaceBelow = Widget->Bounds.Min.y - Interface->WindowBounds.Min.y; - ui_layout_direction Direction = LayoutDirection_TopDown; - rect2 MenuBounds = {}; - - if (SpaceAbove > SpaceBelow) - { - r32 ParentLayoutMaxY = ParentLayout.Bounds.Max.y; - Direction = LayoutDirection_BottomUp; - MenuBounds = rect2{ - v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, Widget->Bounds.Max.y }, - v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, ParentLayoutMaxY } - }; - } - else - { - r32 ParentLayoutMinY = ParentLayout.Bounds.Min.y; - Direction = LayoutDirection_TopDown; - MenuBounds = rect2{ - v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, ParentLayoutMinY }, - v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, Widget->Bounds.Min.y } - }; - } - - ui_widget* Layout = ui_PushOverlayLayout(Interface, MenuBounds, Direction, MakeString("DropdownLayout")); - Layout->Margin.y = 0; - Layout->WidgetReference = Widget->Id; - ui_WidgetClearFlag(Layout, UIWidgetFlag_DrawOutline); - ui_WidgetSetChildrenPopover(Layout); - } - - return State->Value; + ui_widget* Layout = ui_PushOverlayLayout(Interface, MenuBounds, Direction, MakeString("DropdownLayout")); + Layout->Margin.y = 0; + Layout->WidgetReference = Widget->Id; + ui_WidgetClearFlag(Layout, UIWidgetFlag_DrawOutline); + ui_WidgetSetChildrenPopover(Layout); + } + + return State->Value; } internal bool ui_BeginDropdown(ui_interface* Interface, gs_string Text, rect2 Bounds) { - ui_widget* Widget = ui_CreateWidget(Interface, Text); - ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); - - ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds); - return ui_EvaluateDropdown(Interface, Widget, Result); + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds); + return ui_EvaluateDropdown(Interface, Widget, Result); } internal bool ui_BeginDropdown(ui_interface* Interface, gs_string Text) { - ui_widget* Widget = ui_CreateWidget(Interface, Text); - ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); - ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); - return ui_EvaluateDropdown(Interface, Widget, Result); + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); + return ui_EvaluateDropdown(Interface, Widget, Result); } internal void ui_EndDropdown(ui_interface* Interface) { - gs_string LayoutName = MakeString("DropdownLayout"); - ui_widget* Layout = Interface->ActiveLayout; - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->WidgetReference); - if (State) + gs_string LayoutName = MakeString("DropdownLayout"); + ui_widget* Layout = Interface->ActiveLayout; + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->WidgetReference); + if (State) + { + bool IsOpen = State->Value; + ui_widget* Widget = Interface->ActiveLayout; + bool IsStillHot = StringsEqualUpToLength(Widget->String, LayoutName, LayoutName.Length); + if (IsOpen && IsStillHot) { - bool IsOpen = State->Value; - ui_widget* Widget = Interface->ActiveLayout; - bool IsStillHot = StringsEqualUpToLength(Widget->String, LayoutName, LayoutName.Length); - if (IsOpen && IsStillHot) - { - ui_PopLayout(Interface, LayoutName); - } else if (IsOpen && !IsStillHot) - { - State->Value = false; - } + ui_PopLayout(Interface, LayoutName); + } else if (IsOpen && !IsStillHot) + { + State->Value = false; } + } } internal r32 ui_EvaluateRangeSlider(ui_interface* Interface, ui_widget* Widget, ui_eval_result EvalResult, r32 Value, r32 MinValue, r32 MaxValue) { - r32 NewValue = Value; - ui_widget_retained_state* State = ui_GetOrCreateRetainedState(Interface, Widget); - - if (EvalResult.Clicked) - { - State->InitialValueR32 = Value; - } - - if (EvalResult.Held) - { - r32 Percent = (Interface->Mouse.Pos.x - Widget->Bounds.Min.x) / Rect2Width(Widget->Bounds); - NewValue = LerpR32(Percent, MinValue, MaxValue); - } - - NewValue = Clamp(MinValue, NewValue, MaxValue); - Widget->FillPercent = RemapR32(NewValue, MinValue, MaxValue, 0, 1); - return NewValue; + r32 NewValue = Value; + ui_widget_retained_state* State = ui_GetOrCreateRetainedState(Interface, Widget); + + if (EvalResult.Clicked) + { + State->InitialValueR32 = Value; + } + + if (EvalResult.Held) + { + r32 Percent = (Interface->Mouse.Pos.x - Widget->Bounds.Min.x) / Rect2Width(Widget->Bounds); + NewValue = LerpR32(Percent, MinValue, MaxValue); + } + + NewValue = Clamp(MinValue, NewValue, MaxValue); + Widget->FillPercent = RemapR32(NewValue, MinValue, MaxValue, 0, 1); + return NewValue; } internal ui_widget* ui_CreateRangeSliderWidget(ui_interface* Interface, gs_string Text, r32 Value) { - ui_widget* Widget = ui_CreateWidget(Interface, Text); - ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); - Widget->String = PushStringF(Interface->PerFrameMemory, 128, "%f", Value); - InterfaceAssert(Interface->PerFrameMemory); - return Widget; + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + Widget->String = PushStringF(Interface->PerFrameMemory, 128, "%f", Value); + InterfaceAssert(Interface->PerFrameMemory); + return Widget; } internal r32 ui_RangeSlider(ui_interface* Interface, gs_string Text, r32 Value, r32 ValueMin, r32 ValueMax) { - ui_widget* Widget = ui_CreateRangeSliderWidget(Interface, Text, Value); - ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); - return ui_EvaluateRangeSlider(Interface, Widget, Result, Value, ValueMin, ValueMax); - + ui_widget* Widget = ui_CreateRangeSliderWidget(Interface, Text, Value); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); + return ui_EvaluateRangeSlider(Interface, Widget, Result, Value, ValueMin, ValueMax); + } internal r32 ui_RangeSlider(ui_interface* Interface, gs_string Text, rect2 Bounds, r32 Value, r32 ValueMin, r32 ValueMax) { - ui_widget* Widget = ui_CreateRangeSliderWidget(Interface, Text, Value); - ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds); - return ui_EvaluateRangeSlider(Interface, Widget, Result, Value, ValueMin, ValueMax); + ui_widget* Widget = ui_CreateRangeSliderWidget(Interface, Text, Value); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds); + return ui_EvaluateRangeSlider(Interface, Widget, Result, Value, ValueMin, ValueMax); } internal bool ui_Toggle(ui_interface* Interface, gs_string Text, bool Value) { - ui_widget* Widget = ui_CreateWidget(Interface, Text); - ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); - ui_eval_result Eval = ui_EvaluateWidget(Interface, Widget); - - bool Result = Eval.Clicked ? !Value : Value; - Widget->FillPercent = Result ? 1.0f : 0.0f; - return Result; + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + ui_eval_result Eval = ui_EvaluateWidget(Interface, Widget); + + bool Result = Eval.Clicked ? !Value : Value; + Widget->FillPercent = Result ? 1.0f : 0.0f; + return Result; } internal bool ui_ToggleText(ui_interface* Interface, gs_string Text, bool Value) { - ui_widget* Widget = ui_CreateWidget(Interface, Text); - ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill); - ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); - ui_eval_result Eval = ui_EvaluateWidget(Interface, Widget); - - bool Result = Eval.Clicked ? !Value : Value; - Widget->FillPercent = Result ? 1.0f : 0.0f; - return Result; + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + ui_eval_result Eval = ui_EvaluateWidget(Interface, Widget); + + bool Result = Eval.Clicked ? !Value : Value; + Widget->FillPercent = Result ? 1.0f : 0.0f; + return Result; } internal void ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows, u32 ElementCount) { - if (ElementCount < ViewportRows) + if (ElementCount < ViewportRows) + { + ViewportRows = ElementCount; + } + + ui_column_spec ColumnRules[] = { + { UIColumnSize_Fixed, 32 }, + { UIColumnSize_Fill, 0 }, + }; + ui_widget* Layout = ui_BeginRow(Interface, 2, ColumnRules); + ui_WidgetClearFlag(Layout, UIWidgetFlag_ExpandsToFitChildren); + + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->Id); + if (!State) + { + State = ui_CreateRetainedState(Interface, Layout); + State->InitialValueR32 = 1.0f; + } + + r32 LayoutHeight = Layout->RowHeight * ViewportRows; + switch (Layout->Parent->FillDirection) + { + case LayoutDirection_TopDown: { - ViewportRows = ElementCount; - } + Layout->Bounds.Min.y = Layout->Bounds.Max.y - LayoutHeight; + }break; - ui_column_spec ColumnRules[] = { - { UIColumnSize_Fixed, 32 }, - { UIColumnSize_Fill, 0 }, - }; - ui_widget* Layout = ui_BeginRow(Interface, 2, ColumnRules); - ui_WidgetClearFlag(Layout, UIWidgetFlag_ExpandsToFitChildren); - - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->Id); - if (!State) + case LayoutDirection_BottomUp: { - State = ui_CreateRetainedState(Interface, Layout); - State->InitialValueR32 = 1.0f; - } + Layout->Bounds.Max.y = Layout->Bounds.Min.y + LayoutHeight; + }break; - r32 LayoutHeight = Layout->RowHeight * ViewportRows; - switch (Layout->Parent->FillDirection) - { - case LayoutDirection_TopDown: - { - Layout->Bounds.Min.y = Layout->Bounds.Max.y - LayoutHeight; - }break; - - case LayoutDirection_BottomUp: - { - Layout->Bounds.Max.y = Layout->Bounds.Min.y + LayoutHeight; - }break; - - InvalidDefaultCase; - } - - // Create the scroll bar - // - // TODO(pjs): Maybe make this a vertical slider widget? - - ui_widget* SliderRegion = ui_CreateWidget(Interface, MakeString("Slider")); - ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawOutline); - ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawVerticalFill); - ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawFillAsHandle); - - ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_Clickable); - - rect2 SliderBounds = ui_ReserveBounds(Interface, Layout, true); - SliderBounds.Min.y = Layout->Bounds.Min.y + Layout->Margin.y; - SliderBounds.Max.y = Layout->Bounds.Max.y - Layout->Margin.y; - - ui_eval_result SliderEval = ui_EvaluateWidget(Interface, SliderRegion, SliderBounds); - if (SliderEval.Clicked || SliderEval.Held) - { - r32 Percent = (Interface->Mouse.Pos.y - SliderRegion->Bounds.Min.y) / Rect2Height(SliderRegion->Bounds); - State->InitialValueR32 = Clamp01(Percent); - } - SliderRegion->FillPercent = State->InitialValueR32; - - // Create the viewport that offsets list contents (and at render time determines what is visible) - // - ui_widget* ViewportLayout = ui_PushLayout(Interface, MakeString("Contents")); - ui_WidgetSetFlag(ViewportLayout, UIWidgetFlag_DrawOutline); - ui_WidgetClearFlag(ViewportLayout, UIWidgetFlag_ExpandsToFitChildren); - ViewportLayout->FillDirection = LayoutDirection_TopDown; - - ViewportLayout->Bounds.Min.y = SliderBounds.Min.y; - ViewportLayout->Bounds.Max.y = SliderBounds.Max.y; - - s32 ScrollableElements = Max(0, ElementCount - ViewportRows); - ui_widget_retained_state* ViewportState = ui_GetOrCreateRetainedState(Interface, ViewportLayout); - ViewportState->ChildrenDrawOffset.x = 0; - r32 BaseOffset = 0; - r32 ScrollPct = 1.0 - State->InitialValueR32; - r32 RowsOffset = ScrollPct * ScrollableElements; - r32 ScrollOffset = (ViewportLayout->RowHeight - (Interface->Style.Margin.y)) * RowsOffset; - ViewportState->ChildrenDrawOffset.y = BaseOffset + ScrollOffset; + InvalidDefaultCase; + } + + // Create the scroll bar + // + // TODO(pjs): Maybe make this a vertical slider widget? + + ui_widget* SliderRegion = ui_CreateWidget(Interface, MakeString("Slider")); + ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawOutline); + ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawVerticalFill); + ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawFillAsHandle); + + ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_Clickable); + + rect2 SliderBounds = ui_ReserveBounds(Interface, Layout, true); + SliderBounds.Min.y = Layout->Bounds.Min.y + Layout->Margin.y; + SliderBounds.Max.y = Layout->Bounds.Max.y - Layout->Margin.y; + + ui_eval_result SliderEval = ui_EvaluateWidget(Interface, SliderRegion, SliderBounds); + if (SliderEval.Clicked || SliderEval.Held) + { + r32 Percent = (Interface->Mouse.Pos.y - SliderRegion->Bounds.Min.y) / Rect2Height(SliderRegion->Bounds); + State->InitialValueR32 = Clamp01(Percent); + } + SliderRegion->FillPercent = State->InitialValueR32; + + // Create the viewport that offsets list contents (and at render time determines what is visible) + // + ui_widget* ViewportLayout = ui_PushLayout(Interface, MakeString("Contents")); + ui_WidgetSetFlag(ViewportLayout, UIWidgetFlag_DrawOutline); + ui_WidgetClearFlag(ViewportLayout, UIWidgetFlag_ExpandsToFitChildren); + ViewportLayout->FillDirection = LayoutDirection_TopDown; + + ViewportLayout->Bounds.Min.y = SliderBounds.Min.y; + ViewportLayout->Bounds.Max.y = SliderBounds.Max.y; + + s32 ScrollableElements = Max(0, ElementCount - ViewportRows); + ui_widget_retained_state* ViewportState = ui_GetOrCreateRetainedState(Interface, ViewportLayout); + ViewportState->ChildrenDrawOffset.x = 0; + r32 BaseOffset = 0; + r32 ScrollPct = 1.0 - State->InitialValueR32; + r32 RowsOffset = ScrollPct * ScrollableElements; + r32 ScrollOffset = (ViewportLayout->RowHeight - (Interface->Style.Margin.y)) * RowsOffset; + ViewportState->ChildrenDrawOffset.y = BaseOffset + ScrollOffset; } internal void ui_EndList(ui_interface* Interface) { - // Pop the Viewport Layout - ui_PopLayout(Interface, MakeString("Contents")); - // Pop the actual list layout - ui_EndRow(Interface); + // Pop the Viewport Layout + ui_PopLayout(Interface, MakeString("Contents")); + // Pop the actual list layout + ui_EndRow(Interface); } internal void ui_BeginMousePopup(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Text) { - rect2 FollowMouseBounds = Rect2Translate(Bounds, Interface->Mouse.Pos); - ui_widget* Layout = ui_PushOverlayLayout(Interface, FollowMouseBounds, FillDir, MakeString("MousePopup")); - ui_WidgetSetFlag(Layout, UIWidgetFlag_DrawBackground); + rect2 FollowMouseBounds = Rect2Translate(Bounds, Interface->Mouse.Pos); + ui_widget* Layout = ui_PushOverlayLayout(Interface, FollowMouseBounds, FillDir, MakeString("MousePopup")); + ui_WidgetSetFlag(Layout, UIWidgetFlag_DrawBackground); } internal void ui_EndMousePopup(ui_interface* Interface) { - ui_PopLayout(Interface, MakeString("MousePopup")); + ui_PopLayout(Interface, MakeString("MousePopup")); } // internal void ui_BeginLabelRow(ui_interface* Interface, gs_string Label, u32 Count = 2) { - ui_BeginRow(Interface, Count); - ui_Label(Interface, Label); + ui_BeginRow(Interface, Count); + ui_Label(Interface, Label); } internal bool ui_LabeledToggle(ui_interface* Interface, gs_string Label, bool Value) { - ui_BeginLabelRow(Interface, Label); - bool Result = ui_Toggle(Interface, Label, Value); - ui_EndRow(Interface); - return Result; + ui_BeginLabelRow(Interface, Label); + bool Result = ui_Toggle(Interface, Label, Value); + ui_EndRow(Interface); + return Result; } internal r32 ui_LabeledRangeSlider(ui_interface* Interface, gs_string Label, r32 Value, r32 ValueMin, r32 ValueMax) { - ui_BeginLabelRow(Interface, Label); - r32 Result = ui_RangeSlider(Interface, Label, Value, ValueMin, ValueMax); - ui_EndRow(Interface); - return Result; + ui_BeginLabelRow(Interface, Label); + r32 Result = ui_RangeSlider(Interface, Label, Value, ValueMin, ValueMax); + ui_EndRow(Interface); + return Result; } internal void ui_LabeledTextEntry(ui_interface* Interface, gs_string Label, gs_string* Value) { - ui_BeginLabelRow(Interface, Label); - ui_TextEntry(Interface, Label, Value); - ui_EndRow(Interface); + ui_BeginLabelRow(Interface, Label); + ui_TextEntry(Interface, Label, Value); + ui_EndRow(Interface); } internal u64 ui_LabeledTextEntryU64(ui_interface* Interface, gs_string Label, u32 Value) { - ui_BeginLabelRow(Interface, Label); - u64 Result = ui_TextEntryU64(Interface, Label, Value); - ui_EndRow(Interface); - return Result; + ui_BeginLabelRow(Interface, Label); + u64 Result = ui_TextEntryU64(Interface, Label, Value); + ui_EndRow(Interface); + return Result; } internal bool ui_BeginLabeledDropdown(ui_interface* Interface, gs_string Label, gs_string DropdownValue) { - ui_BeginLabelRow(Interface, Label); - return ui_BeginDropdown(Interface, DropdownValue); + ui_BeginLabelRow(Interface, Label); + return ui_BeginDropdown(Interface, DropdownValue); } internal void ui_EndLabeledDropdown(ui_interface* Interface) { - ui_EndDropdown(Interface); - ui_EndRow(Interface); + ui_EndDropdown(Interface); + ui_EndRow(Interface); } internal ui_interface ui_InterfaceCreate(context Context, interface_config Style, gs_memory_arena* Permanent) { - ui_interface Result = {0}; - Result.Style = Style; - - gs_file FontFile = ReadEntireFile(Context.ThreadContext.FileHandler, ConstString("data/Anonymous Pro.ttf")); - Result.Style.Font = PushStruct(Permanent, bitmap_font); - *Result.Style.Font = TextFont_Create(FontFile, 512, Style.FontSize, Context, Permanent); - - Result.Style.RowHeight = ui_GetTextLineHeight(Result) + (2 * Result.Style.Margin.y); - - Result.WidgetsCountMax = 4096; - Result.Widgets = PushArray(Permanent, ui_widget, Result.WidgetsCountMax); - Result.PerFrameMemory = PushStruct(Permanent, gs_memory_arena); - *Result.PerFrameMemory = CreateMemoryArena(Context.ThreadContext.Allocator, "Interface Per Frame Memory Arena", KB(32)); - InterfaceAssert(Result.PerFrameMemory); - - Result.Permanent = Permanent; - - return Result; + ui_interface Result = {0}; + Result.Style = Style; + + gs_file FontFile = ReadEntireFile(Context.ThreadContext.FileHandler, ConstString("data/Anonymous Pro.ttf")); + Result.Style.Font = PushStruct(Permanent, bitmap_font); + *Result.Style.Font = TextFont_Create(FontFile, 512, Style.FontSize, Context, Permanent); + + Result.Style.RowHeight = ui_GetTextLineHeight(Result) + (2 * Result.Style.Margin.y); + + Result.WidgetsCountMax = 4096; + Result.Widgets = PushArray(Permanent, ui_widget, Result.WidgetsCountMax); + Result.PerFrameMemory = PushStruct(Permanent, gs_memory_arena); + *Result.PerFrameMemory = MemoryArenaCreate(KB(32), Bytes(8), Context.ThreadContext.Allocator, 0, 0, "Interface Per Frame Memory Arena"); + InterfaceAssert(Result.PerFrameMemory); + + Result.Permanent = Permanent; + + return Result; } #define INTERFACE_H diff --git a/src/app/editor/panels/foldhaus_panel_file_view.h b/src/app/editor/panels/foldhaus_panel_file_view.h index 050bd0a..64b03d5 100644 --- a/src/app/editor/panels/foldhaus_panel_file_view.h +++ b/src/app/editor/panels/foldhaus_panel_file_view.h @@ -7,42 +7,42 @@ enum file_view_mode { - FileViewMode_Load, - FileViewMode_Save, + FileViewMode_Load, + FileViewMode_Save, }; struct file_view_state { - file_view_mode Mode; - - gs_string WorkingDirectory; - gs_string DisplayDirectory; - - gs_memory_arena FileNamesArena; - gs_file_info_array FileNames; - - gs_file_info SelectedFile; + file_view_mode Mode; + + gs_string WorkingDirectory; + gs_string DisplayDirectory; + + gs_memory_arena FileNamesArena; + gs_file_info_array FileNames; + + gs_file_info SelectedFile; }; internal void FileView_SetMode(panel* Panel, file_view_mode Mode) { - file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state); - FileViewState->Mode = Mode; + file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state); + FileViewState->Mode = Mode; } internal void FileView_Exit_(panel* FileViewPanel, app_state* State, context Context) { - // TODO(pjs): Free State->FileNamesArena - - Assert(FileViewPanel->IsModalOverrideFor != 0); - panel* ReturnTo = FileViewPanel->IsModalOverrideFor; - if (ReturnTo->ModalOverrideCB) - { - ReturnTo->ModalOverrideCB(FileViewPanel, State, Context); - } - Panel_PopModalOverride(ReturnTo, &State->PanelSystem); + // TODO(pjs): Free State->FileNamesArena + + Assert(FileViewPanel->IsModalOverrideFor != 0); + panel* ReturnTo = FileViewPanel->IsModalOverrideFor; + if (ReturnTo->ModalOverrideCB) + { + ReturnTo->ModalOverrideCB(FileViewPanel, State, Context); + } + Panel_PopModalOverride(ReturnTo, &State->PanelSystem); } global input_command* FileView_Commands = 0; @@ -51,35 +51,35 @@ s32 FileView_CommandsCount = 0; internal void FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_state* State, context Context) { - // NOTE(pjs): make sure we're only passing valid directory paths to the - // function - char LastChar = WorkingDirectory.Str[WorkingDirectory.Length - 1]; - Assert(LastChar == '\\' || LastChar == '/'); - ClearArena(&State->FileNamesArena); + // NOTE(pjs): make sure we're only passing valid directory paths to the + // function + char LastChar = WorkingDirectory.Str[WorkingDirectory.Length - 1]; + Assert(LastChar == '\\' || LastChar == '/'); + MemoryArenaClear(&State->FileNamesArena); + + + gs_string SanitizedDir = PushString(Context.ThreadContext.Transient, WorkingDirectory.Length + 2); + SanitizePath(WorkingDirectory, &SanitizedDir, Context.ThreadContext.Transient); + if (SanitizedDir.Str[SanitizedDir.Length - 1] != '\\') + { + AppendPrintF(&SanitizedDir, "\\"); + } + + gs_const_string SanitizedDisplayDir = SanitizedDir.ConstString; + + gs_file_info NewWorkingDir = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDir.ConstString); + if (NewWorkingDir.IsDirectory) + { + AppendPrintF(&SanitizedDir, "*"); + NullTerminate(&SanitizedDir); + State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, SanitizedDir.ConstString, EnumerateDirectory_IncludeDirectories); - gs_string SanitizedDir = PushString(Context.ThreadContext.Transient, WorkingDirectory.Length + 2); - SanitizePath(WorkingDirectory, &SanitizedDir, Context.ThreadContext.Transient); - if (SanitizedDir.Str[SanitizedDir.Length - 1] != '\\') - { - AppendPrintF(&SanitizedDir, "\\"); - } - - gs_const_string SanitizedDisplayDir = SanitizedDir.ConstString; - - gs_file_info NewWorkingDir = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDir.ConstString); - if (NewWorkingDir.IsDirectory) - { - AppendPrintF(&SanitizedDir, "*"); - NullTerminate(&SanitizedDir); - - State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, SanitizedDir.ConstString, EnumerateDirectory_IncludeDirectories); - - // NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory - // in some cases. Shouldn't be a problem but it is unnecessary - PrintF(&State->WorkingDirectory, "%S", SanitizedDir.ConstString); - PrintF(&State->DisplayDirectory, "%S", SanitizedDisplayDir); - } + // NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory + // in some cases. Shouldn't be a problem but it is unnecessary + PrintF(&State->WorkingDirectory, "%S", SanitizedDir.ConstString); + PrintF(&State->DisplayDirectory, "%S", SanitizedDisplayDir); + } } GSMetaTag(panel_init); @@ -87,16 +87,16 @@ GSMetaTag(panel_type_file_view); internal void FileView_Init(panel* Panel, app_state* State, context Context) { - // TODO: :FreePanelMemory - file_view_state* FileViewState = PushStruct(&State->Permanent, file_view_state); - Panel->StateMemory = StructToData(FileViewState, file_view_state); - FileViewState->FileNamesArena = CreateMemoryArena(Context.ThreadContext.Allocator, "File View - File Names Arena"); - - // TODO(pjs): this shouldn't be stored in permanent - FileViewState->DisplayDirectory = PushString(&State->Permanent, 1024); - FileViewState->WorkingDirectory = PushString(&State->Permanent, 1024); - - FileView_UpdateWorkingDirectory(ConstString(".\\"), FileViewState, Context); + // TODO: :FreePanelMemory + file_view_state* FileViewState = PushStruct(&State->Permanent, file_view_state); + Panel->StateMemory = StructToData(FileViewState, file_view_state); + FileViewState->FileNamesArena = MemoryArenaCreate(MB(4), Bytes(8), Context.ThreadContext.Allocator, 0, 0, "File View - File Names Arena"); + + // TODO(pjs): this shouldn't be stored in permanent + FileViewState->DisplayDirectory = PushString(&State->Permanent, 1024); + FileViewState->WorkingDirectory = PushString(&State->Permanent, 1024); + + FileView_UpdateWorkingDirectory(ConstString(".\\"), FileViewState, Context); } GSMetaTag(panel_cleanup); @@ -104,7 +104,7 @@ GSMetaTag(panel_type_file_view); internal void FileView_Cleanup(panel* Panel, app_state* State) { - + } GSMetaTag(panel_render); @@ -112,87 +112,87 @@ GSMetaTag(panel_type_file_view); internal void FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { - file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state); - - ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout")); + file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state); + + ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout")); + { + ui_BeginRow(&State->Interface, 3); { - ui_BeginRow(&State->Interface, 3); + if (FileViewState->Mode == FileViewMode_Save) + { + if (ui_Button(&State->Interface, MakeString("Save"))) { - if (FileViewState->Mode == FileViewMode_Save) - { - if (ui_Button(&State->Interface, MakeString("Save"))) - { - if (!FileViewState->SelectedFile.Path.Str) - { - FileViewState->SelectedFile.Path = FileViewState->DisplayDirectory.ConstString; - } - - FileView_Exit_(Panel, State, Context); - } - } - - if (ui_Button(&State->Interface, MakeString("Exit"))) - { - FileView_Exit_(Panel, State, Context); - } + if (!FileViewState->SelectedFile.Path.Str) + { + FileViewState->SelectedFile.Path = FileViewState->DisplayDirectory.ConstString; + } + + FileView_Exit_(Panel, State, Context); } - ui_EndRow(&State->Interface); - - // Header - if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->DisplayDirectory)) - { - // if last character is a slash, update pwd, and clear the filter string - // otherwise update the filter string - gs_string Pwd = FileViewState->DisplayDirectory; - char LastChar = Pwd.Str[Pwd.Length - 1]; - if (LastChar == '\\' || LastChar == '/') - { - FileView_UpdateWorkingDirectory(Pwd.ConstString, FileViewState, Context); - } - else - { - - } - } - - // File Display - ui_BeginList(&State->Interface, MakeString("Files"), 10, FileViewState->FileNames.Count); - for (u32 i = 0; i < FileViewState->FileNames.Count; i++) - { - gs_file_info File = FileViewState->FileNames.Values[i]; - - u32 LastSlashIndex = FindLast(File.Path, File.Path.Length - 2, '\\'); - gs_const_string FileName = Substring(File.Path, LastSlashIndex + 1, File.Path.Length); - gs_string PathString = PushString(State->Transient, FileName.Length); - PrintF(&PathString, "%S", FileName); - - if (ui_LayoutListButton(&State->Interface, PathString, i)) - { - if (File.IsDirectory) - { - FileView_UpdateWorkingDirectory(File.Path, FileViewState, Context); - } - else - { - FileViewState->SelectedFile = File; - switch (FileViewState->Mode) - { - case FileViewMode_Load: - { - FileView_Exit_(Panel, State, Context); - } break; - - case FileViewMode_Save: - { - - } break; - } - } - } - } - ui_EndList(&State->Interface); + } + + if (ui_Button(&State->Interface, MakeString("Exit"))) + { + FileView_Exit_(Panel, State, Context); + } } - ui_PopLayout(&State->Interface, MakeString("FileView Layout")); + ui_EndRow(&State->Interface); + + // Header + if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->DisplayDirectory)) + { + // if last character is a slash, update pwd, and clear the filter string + // otherwise update the filter string + gs_string Pwd = FileViewState->DisplayDirectory; + char LastChar = Pwd.Str[Pwd.Length - 1]; + if (LastChar == '\\' || LastChar == '/') + { + FileView_UpdateWorkingDirectory(Pwd.ConstString, FileViewState, Context); + } + else + { + + } + } + + // File Display + ui_BeginList(&State->Interface, MakeString("Files"), 10, FileViewState->FileNames.Count); + for (u32 i = 0; i < FileViewState->FileNames.Count; i++) + { + gs_file_info File = FileViewState->FileNames.Values[i]; + + u32 LastSlashIndex = FindLast(File.Path, File.Path.Length - 2, '\\'); + gs_const_string FileName = Substring(File.Path, LastSlashIndex + 1, File.Path.Length); + gs_string PathString = PushString(State->Transient, FileName.Length); + PrintF(&PathString, "%S", FileName); + + if (ui_LayoutListButton(&State->Interface, PathString, i)) + { + if (File.IsDirectory) + { + FileView_UpdateWorkingDirectory(File.Path, FileViewState, Context); + } + else + { + FileViewState->SelectedFile = File; + switch (FileViewState->Mode) + { + case FileViewMode_Load: + { + FileView_Exit_(Panel, State, Context); + } break; + + case FileViewMode_Save: + { + + } break; + } + } + } + } + ui_EndList(&State->Interface); + } + ui_PopLayout(&State->Interface, MakeString("FileView Layout")); } diff --git a/src/app/editor/panels/foldhaus_panel_profiler.h b/src/app/editor/panels/foldhaus_panel_profiler.h index b8684ee..c0ecd79 100644 --- a/src/app/editor/panels/foldhaus_panel_profiler.h +++ b/src/app/editor/panels/foldhaus_panel_profiler.h @@ -13,7 +13,7 @@ GSMetaTag(panel_type_profiler); internal void ProfilerView_Init(panel* Panel, app_state* State, context Context) { - + } GSMetaTag(panel_cleanup); @@ -21,213 +21,217 @@ GSMetaTag(panel_type_profiler); internal void ProfilerView_Cleanup(panel* Panel, app_state* State) { - + } internal void RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Transient) { - rect2 Bounds = ui_LayoutRemaining(*Layout); - r32 Width = Rect2Width(Bounds); - r32 DepthHeight = 32; + rect2 Bounds = ui_LayoutRemaining(*Layout); + r32 Width = Rect2Width(Bounds); + r32 DepthHeight = 32; + + s64 FrameStartCycles = VisibleFrame->FrameStartCycles; + r32 FrameTotalCycles = (r32)(VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles); + + r32 NextThreadTop = Bounds.Max.y; + + for (s32 t = 0; t < VisibleFrame->ThreadCount; t++) + { + debug_scope_record_list ThreadCalls = VisibleFrame->ThreadCalls[t]; - s64 FrameStartCycles = VisibleFrame->FrameStartCycles; - r32 FrameTotalCycles = (r32)(VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles); + gs_string String = PushString(Transient, 256); - r32 NextThreadTop = Bounds.Max.y; + r32 ThreadScopeMin = Bounds.Max.y; - for (s32 t = 0; t < VisibleFrame->ThreadCount; t++) + //PrintF(&String, "Thread %d", ThreadCalls.ThreadId); + //ui_Label(Interface, String, rect2{ThreadScopeMin); + + r32 Hue = (r32)(t) / (r32)(VisibleFrame->ThreadCount); + Hue += (.5f * (t % 2)); + v4 ThreadHSV = v4{ 360.0f * Hue, .5f, 1.0f, 1.0f }; + v4 ThreadRGB = HSVToRGB(ThreadHSV); + + for (s32 i = 0; i < ThreadCalls.Count; i++) { - debug_scope_record_list ThreadCalls = VisibleFrame->ThreadCalls[t]; - - gs_string String = PushString(Transient, 256); - - r32 ThreadScopeMin = Bounds.Max.y; - - //PrintF(&String, "Thread %d", ThreadCalls.ThreadId); - //ui_Label(Interface, String, rect2{ThreadScopeMin); - - r32 Hue = (r32)(t) / (r32)(VisibleFrame->ThreadCount); - Hue += (.5f * (t % 2)); - v4 ThreadHSV = v4{ 360.0f * Hue, .5f, 1.0f, 1.0f }; - v4 ThreadRGB = HSVToRGB(ThreadHSV); - - for (s32 i = 0; i < ThreadCalls.Count; i++) + scope_record* Record = ThreadCalls.Calls + i; + scope_name* Name = GetOrAddNameHashEntry(VisibleFrame, Record->NameHash); + s64 OffsetStart = Record->StartCycles - FrameStartCycles; + s64 OffsetEnd = Record->EndCycles - FrameStartCycles; + r32 PercentStart = (r32)(OffsetStart) / FrameTotalCycles; + r32 PercentEnd = (r32)(OffsetEnd) / FrameTotalCycles; + r32 PercentWidth = PercentEnd - PercentStart; + + rect2 ScopeBounds = { + v2{0, 0}, + v2{PercentWidth * Width, DepthHeight - 4}, + }; + v2 Offset = { + Bounds.Min.x + (PercentStart * Width), + NextThreadTop - ((Record->CallDepth + 1) * DepthHeight) + }; + ScopeBounds = Rect2Translate(ScopeBounds, Offset); + ThreadScopeMin = Min(ScopeBounds.Min.y, ThreadScopeMin); + + if (Rect2Width(ScopeBounds) >= 1) + { + v4 Color = ThreadRGB; + if (PointIsInRect(ScopeBounds, Interface->Mouse.Pos)) { - scope_record* Record = ThreadCalls.Calls + i; - scope_name* Name = GetOrAddNameHashEntry(VisibleFrame, Record->NameHash); - s64 OffsetStart = Record->StartCycles - FrameStartCycles; - s64 OffsetEnd = Record->EndCycles - FrameStartCycles; - r32 PercentStart = (r32)(OffsetStart) / FrameTotalCycles; - r32 PercentEnd = (r32)(OffsetEnd) / FrameTotalCycles; - r32 PercentWidth = PercentEnd - PercentStart; - - rect2 ScopeBounds = { - v2{0, 0}, - v2{PercentWidth * Width, DepthHeight - 4}, - }; - v2 Offset = { - Bounds.Min.x + (PercentStart * Width), - NextThreadTop - ((Record->CallDepth + 1) * DepthHeight) - }; - ScopeBounds = Rect2Translate(ScopeBounds, Offset); - ThreadScopeMin = Min(ScopeBounds.Min.y, ThreadScopeMin); - - if (Rect2Width(ScopeBounds) >= 1) - { - v4 Color = ThreadRGB; - if (PointIsInRect(ScopeBounds, Interface->Mouse.Pos)) - { - Color = GreenV4; - - ui_BeginMousePopup(Interface, rect2{ 25, 25, 300, 57 }, LayoutDirection_TopDown, MakeString("Hover")); - { - s64 Cycles = (Record->EndCycles - Record->StartCycles); - r32 PercentFrame = (r32)(Cycles) / FrameTotalCycles; - PrintF(&String, "%S : %.2f%% frame | %dcy", - Name->Name, - PercentFrame, - Cycles); - ui_Label(Interface, String); - } - ui_EndMousePopup(Interface); - } - - ui_FillRect(Interface, ScopeBounds, Color); - ui_OutlineRect(Interface, ScopeBounds, 1, BlackV4); - } + Color = GreenV4; + + ui_BeginMousePopup(Interface, rect2{ 25, 25, 300, 57 }, LayoutDirection_TopDown, MakeString("Hover")); + { + s64 Cycles = (Record->EndCycles - Record->StartCycles); + r32 PercentFrame = (r32)(Cycles) / FrameTotalCycles; + PrintF(&String, "%S : %.2f%% frame | %dcy", + Name->Name, + PercentFrame, + Cycles); + ui_Label(Interface, String); + } + ui_EndMousePopup(Interface); } - NextThreadTop = ThreadScopeMin; + ui_FillRect(Interface, ScopeBounds, Color); + ui_OutlineRect(Interface, ScopeBounds, 1, BlackV4); + } } + + NextThreadTop = ThreadScopeMin; + } } internal void RenderProfiler_ListVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Memory) { - char Backbuffer[256]; - gs_string String = MakeString(Backbuffer, 0, 256); - - ui_column_spec ColumnWidths[] = { - { UIColumnSize_Fixed, 256 }, - { UIColumnSize_Fixed, 128 }, - { UIColumnSize_Fixed, 128 }, - { UIColumnSize_Fixed, 128 }, - { UIColumnSize_Fixed, 128 }}; - ui_BeginRow(Interface, 5, &ColumnWidths[0]); + char Backbuffer[256]; + gs_string String = MakeString(Backbuffer, 0, 256); + + ui_column_spec ColumnWidths[] = { + { UIColumnSize_Fixed, 256 }, + { UIColumnSize_Fixed, 128 }, + { UIColumnSize_Fixed, 128 }, + { UIColumnSize_Fixed, 128 }, + { UIColumnSize_Fixed, 128 }}; + ui_BeginRow(Interface, 5, &ColumnWidths[0]); + { + ui_Label(Interface, MakeString("Procedure")); + ui_Label(Interface, MakeString("% Frame")); + ui_Label(Interface, MakeString("Seconds")); + ui_Label(Interface, MakeString("Cycles")); + ui_Label(Interface, MakeString("Calls")); + } + ui_EndRow(Interface); + + s32 CountedScopes = 0; + for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++) + { + scope_name NameEntry = VisibleFrame->ScopeNamesHash[n]; + if (NameEntry.Hash != 0) { - ui_Label(Interface, MakeString("Procedure")); - ui_Label(Interface, MakeString("% Frame")); - ui_Label(Interface, MakeString("Seconds")); - ui_Label(Interface, MakeString("Cycles")); - ui_Label(Interface, MakeString("Calls")); + CountedScopes += 1; } - ui_EndRow(Interface); - - s32 CountedScopes = 0; - for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++) + } + + ui_BeginList(Interface, MakeString("Scope List"), 10, CountedScopes); + ui_BeginRow(Interface, 5, &ColumnWidths[0]); + for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++) + { + scope_name NameEntry = VisibleFrame->ScopeNamesHash[n]; + if (NameEntry.Hash != 0) { - scope_name NameEntry = VisibleFrame->ScopeNamesHash[n]; - if (NameEntry.Hash != 0) - { - CountedScopes += 1; - } + collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n; + + PrintF(&String, "%S", NameEntry.Name); + ui_Label(Interface, String); + + PrintF(&String, "%f%%", CollatedRecord->PercentFrameTime); + ui_Label(Interface, String); + + PrintF(&String, "%fs", CollatedRecord->TotalSeconds); + ui_Label(Interface, String); + + PrintF(&String, "%dcy", CollatedRecord->TotalCycles); + ui_Label(Interface, String); + + PrintF(&String, "%d", CollatedRecord->CallCount); + ui_Label(Interface, String); } - - ui_BeginList(Interface, MakeString("Scope List"), 10, CountedScopes); - ui_BeginRow(Interface, 5, &ColumnWidths[0]); - for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++) - { - scope_name NameEntry = VisibleFrame->ScopeNamesHash[n]; - if (NameEntry.Hash != 0) - { - collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n; - - PrintF(&String, "%S", NameEntry.Name); - ui_Label(Interface, String); - - PrintF(&String, "%f%%", CollatedRecord->PercentFrameTime); - ui_Label(Interface, String); - - PrintF(&String, "%fs", CollatedRecord->TotalSeconds); - ui_Label(Interface, String); - - PrintF(&String, "%dcy", CollatedRecord->TotalCycles); - ui_Label(Interface, String); - - PrintF(&String, "%d", CollatedRecord->CallCount); - ui_Label(Interface, String); - } - } - ui_EndRow(Interface); - ui_EndList(Interface); + } + ui_EndRow(Interface); + ui_EndList(Interface); } struct mem_amt { - u64 OrigSize; - r64 Size; - char* Units; + u64 OrigSize; + r64 Size; + char* Units; }; internal mem_amt GetMemAmt (u64 BytesCount) { - mem_amt Result = {}; - Result.OrigSize = BytesCount; - Result.Size = (r64)BytesCount; - Result.Units = "bytes"; - - u32 i = 0; - char* UnitList[] = { "kb", "mb", "gb", "tb" }; - while (Result.Size > 1024) { - Result.Size /= 1024.0; - Result.Units = UnitList[i++]; - } - - return Result; + mem_amt Result = {}; + Result.OrigSize = BytesCount; + Result.Size = (r64)BytesCount; + Result.Units = "bytes"; + + u32 i = 0; + char* UnitList[] = { "kb", "mb", "gb", "tb" }; + while (Result.Size > 1024) { + Result.Size /= 1024.0; + Result.Units = UnitList[i++]; + } + + return Result; } internal void RenderProfiler_MemoryView(ui_interface* Interface, ui_widget* Layout, app_state* State, context Context, gs_memory_arena* Memory) { - gs_allocator_debug Debug = *Context.ThreadContext.Allocator.Debug; - gs_string TempString = PushString(State->Transient, 256); - - mem_amt MemFootprint = GetMemAmt(Debug.TotalAllocSize); - u64 AllocCount = Debug.AllocationsCount; - - - PrintF(&TempString, "Total Memory Size: %.2f %s | Allocations: %lld", MemFootprint.Size, MemFootprint.Units, AllocCount); + gs_debug_allocations_list* DA = Context.ThreadContext.Allocator.DEBUGAllocList; + + gs_string TempString = PushString(State->Transient, 256); + + mem_amt MemFootprint = GetMemAmt(DA->AllocationsSizeTotal); + u64 AllocCount = DA->AllocationsCount; + + PrintF(&TempString, "Total Memory Size: %.2f %s | Allocations: %lld", MemFootprint.Size, MemFootprint.Units, AllocCount); + ui_Label(Interface, TempString); + + ui_column_spec ColumnWidths[] = { + { UIColumnSize_Fill, 0 }, + { UIColumnSize_Fixed,256 }, + }; + ui_BeginRow(Interface, 2, &ColumnWidths[0]); + { + ui_Label(Interface, MakeString("Location")); + ui_Label(Interface, MakeString("Alloc Size")); + } + ui_EndRow(Interface); + + ui_BeginList(Interface, MakeString("Alloc List"), 10, DA->AllocationsCount); + ui_BeginRow(Interface, 2, &ColumnWidths[0]); + + for (gs_debug_memory_allocation* A = DA->Root; + A && A->Next != 0; + A = A->Next) + { + gs_const_string Str = ConstString(A->Loc.File); + u64 LastSlash = FindLastFromSet(Str, "\\/"); + gs_const_string JustFileName = Substring(Str, LastSlash + 1, Str.Length); + PrintF(&TempString, "%s:%s(%d)", JustFileName.Str, A->Loc.Function, A->Loc.Line); ui_Label(Interface, TempString); - ui_column_spec ColumnWidths[] = { - { UIColumnSize_Fill, 0 }, - { UIColumnSize_Fixed,256 }, - }; - ui_BeginRow(Interface, 2, &ColumnWidths[0]); - { - ui_Label(Interface, MakeString("Location")); - ui_Label(Interface, MakeString("Alloc Size")); - } - ui_EndRow(Interface); + mem_amt Amt = GetMemAmt(A->Size); - ui_BeginList(Interface, MakeString("Alloc List"), 10, Debug.AllocationsCount); - ui_BeginRow(Interface, 2, &ColumnWidths[0]); - for (s32 n = 0; n < Debug.AllocationsCount; n++) - { - gs_debug_allocation A = Debug.Allocations[n]; - - PrintF(&TempString, "%S", A.Location); - ui_Label(Interface, TempString); - - mem_amt Amt = GetMemAmt(A.Size); - - PrintF(&TempString, "%.2f %s", Amt.Size, Amt.Units); - ui_Label(Interface, TempString); - } - ui_EndRow(Interface); - ui_EndList(Interface); + PrintF(&TempString, "%.2f %s", Amt.Size, Amt.Units); + ui_Label(Interface, TempString); + } + ui_EndRow(Interface); + ui_EndList(Interface); } GSMetaTag(panel_render); @@ -235,120 +239,120 @@ GSMetaTag(panel_type_profiler); internal void ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { - gs_memory_arena* Memory = State->Transient; - gs_string String = PushString(Memory, 256); + gs_memory_arena* Memory = State->Transient; + gs_string String = PushString(Memory, 256); + + v4 FrameColors[] = { GreenV4, YellowV4, RedV4, WhiteV4 }; + + r32 FrameListHeight = 64; + rect2 FrameListBounds, ProcListBounds; + RectHSplitAtDistanceFromTop(PanelBounds, FrameListHeight, &FrameListBounds, &ProcListBounds); + rect2 FrameListInner = RectInset(FrameListBounds, 4); + + s32 FramesToDisplay = DEBUG_FRAME_COUNT; + if (FramesToDisplay != 0) + { + r32 SingleFrameStep = Rect2Width(FrameListInner) / FramesToDisplay; + r32 SingleFrameWidth = (r32)((s32)SingleFrameStep - 2); - v4 FrameColors[] = { GreenV4, YellowV4, RedV4, WhiteV4 }; - - r32 FrameListHeight = 64; - rect2 FrameListBounds, ProcListBounds; - RectHSplitAtDistanceFromTop(PanelBounds, FrameListHeight, &FrameListBounds, &ProcListBounds); - rect2 FrameListInner = RectInset(FrameListBounds, 4); - - s32 FramesToDisplay = DEBUG_FRAME_COUNT; - if (FramesToDisplay != 0) + ui_OutlineRect(&State->Interface, FrameListBounds, 2, WhiteV4); + if (MouseButtonHeldDown(Context.Mouse.LeftButtonState)) { - r32 SingleFrameStep = Rect2Width(FrameListInner) / FramesToDisplay; - r32 SingleFrameWidth = (r32)((s32)SingleFrameStep - 2); - - ui_OutlineRect(&State->Interface, FrameListBounds, 2, WhiteV4); - if (MouseButtonHeldDown(Context.Mouse.LeftButtonState)) + if (PointIsInRect(FrameListBounds, Context.Mouse.Pos)) + { + v2 LocalMouse = Rect2GetRectLocalPoint(FrameListBounds, Context.Mouse.Pos); + s32 ClosestFrameIndex = (LocalMouse.x / SingleFrameStep); + if (ClosestFrameIndex >= 0 && ClosestFrameIndex < FramesToDisplay) { - if (PointIsInRect(FrameListBounds, Context.Mouse.Pos)) - { - v2 LocalMouse = Rect2GetRectLocalPoint(FrameListBounds, Context.Mouse.Pos); - s32 ClosestFrameIndex = (LocalMouse.x / SingleFrameStep); - if (ClosestFrameIndex >= 0 && ClosestFrameIndex < FramesToDisplay) - { - GlobalDebugServices->RecordFrames = false; - GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex; - } - } - } - - rect2 FrameBounds = MakeRect2MinDim(FrameListInner.Min, v2{SingleFrameWidth, Rect2Height(FrameListInner)}); - for (s32 F = 0; F < DEBUG_FRAME_COUNT; F++) - { - rect2 PositionedFrameBounds = Rect2TranslateX(FrameBounds, F * SingleFrameStep); - s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F); - if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; } - v4 Color = FrameColors[Clamp(0, FramesAgo, 3)]; - ui_FillRect(&State->Interface, PositionedFrameBounds, Color); + GlobalDebugServices->RecordFrames = false; + GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex; } + } } - ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown, MakeString("Profiler Layout")); - - debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices); - if (VisibleFrame) + rect2 FrameBounds = MakeRect2MinDim(FrameListInner.Min, v2{SingleFrameWidth, Rect2Height(FrameListInner)}); + for (s32 F = 0; F < DEBUG_FRAME_COUNT; F++) { - ui_BeginRow(&State->Interface, 4); - { - s64 FrameStartCycles = VisibleFrame->FrameStartCycles; - s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles; - u32 CurrentDebugFrame = GlobalDebugServices->CurrentDebugFrame - 1; - PrintF(&String, "Frame %d", CurrentDebugFrame); - ui_Label(&State->Interface, String); - - PrintF(&String, "Total Cycles: %lld", FrameTotalCycles); - ui_Label(&State->Interface, String); - - // NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could - // be removed, or used for something else - ui_ReserveBounds(&State->Interface, Layout, true); - - if (ui_Button(&State->Interface, MakeString("Resume Recording"))) - { - GlobalDebugServices->RecordFrames = true; - } - } - ui_EndRow(&State->Interface); + rect2 PositionedFrameBounds = Rect2TranslateX(FrameBounds, F * SingleFrameStep); + s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F); + if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; } + v4 Color = FrameColors[Clamp(0, FramesAgo, 3)]; + ui_FillRect(&State->Interface, PositionedFrameBounds, Color); } - - ui_BeginRow(&State->Interface, 8); + } + + ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown, MakeString("Profiler Layout")); + + debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices); + if (VisibleFrame) + { + ui_BeginRow(&State->Interface, 4); { - if (ui_Button(&State->Interface, MakeString("Profiler"))) - { - GlobalDebugServices->Interface.FrameView = DebugUI_Profiler; - } - if (ui_Button(&State->Interface, MakeString("List View"))) - { - GlobalDebugServices->Interface.FrameView = DebugUI_ScopeList; - } - if (ui_Button(&State->Interface, MakeString("Memory"))) - { - GlobalDebugServices->Interface.FrameView = DebugUI_MemoryView; - } + s64 FrameStartCycles = VisibleFrame->FrameStartCycles; + s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles; + u32 CurrentDebugFrame = GlobalDebugServices->CurrentDebugFrame - 1; + PrintF(&String, "Frame %d", CurrentDebugFrame); + ui_Label(&State->Interface, String); + + PrintF(&String, "Total Cycles: %lld", FrameTotalCycles); + ui_Label(&State->Interface, String); + + // NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could + // be removed, or used for something else + ui_ReserveBounds(&State->Interface, Layout, true); + + if (ui_Button(&State->Interface, MakeString("Resume Recording"))) + { + GlobalDebugServices->RecordFrames = true; + } } ui_EndRow(&State->Interface); - - switch (GlobalDebugServices->Interface.FrameView) + } + + ui_BeginRow(&State->Interface, 8); + { + if (ui_Button(&State->Interface, MakeString("Profiler"))) { - case DebugUI_Profiler: - { - if (VisibleFrame) - { - RenderProfiler_ScopeVisualization(&State->Interface, Layout, VisibleFrame, Memory); - } - }break; - - case DebugUI_ScopeList: - { - if (VisibleFrame) - { - RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory); - } - }break; - - case DebugUI_MemoryView: - { - RenderProfiler_MemoryView(&State->Interface, Layout, State, Context, Memory); - }break; - - InvalidDefaultCase; + GlobalDebugServices->Interface.FrameView = DebugUI_Profiler; } + if (ui_Button(&State->Interface, MakeString("List View"))) + { + GlobalDebugServices->Interface.FrameView = DebugUI_ScopeList; + } + if (ui_Button(&State->Interface, MakeString("Memory"))) + { + GlobalDebugServices->Interface.FrameView = DebugUI_MemoryView; + } + } + ui_EndRow(&State->Interface); + + switch (GlobalDebugServices->Interface.FrameView) + { + case DebugUI_Profiler: + { + if (VisibleFrame) + { + RenderProfiler_ScopeVisualization(&State->Interface, Layout, VisibleFrame, Memory); + } + }break; - ui_PopLayout(&State->Interface, MakeString("Profiler Layout")); + case DebugUI_ScopeList: + { + if (VisibleFrame) + { + RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory); + } + }break; + + case DebugUI_MemoryView: + { + RenderProfiler_MemoryView(&State->Interface, Layout, State, Context, Memory); + }break; + + InvalidDefaultCase; + } + + ui_PopLayout(&State->Interface, MakeString("Profiler Layout")); } diff --git a/src/app/editor/panels/foldhaus_panel_sculpture_view.h b/src/app/editor/panels/foldhaus_panel_sculpture_view.h index ad51493..474d5ca 100644 --- a/src/app/editor/panels/foldhaus_panel_sculpture_view.h +++ b/src/app/editor/panels/foldhaus_panel_sculpture_view.h @@ -13,49 +13,49 @@ struct sculpture_view_panel_state { - camera Camera; + camera Camera; }; // 3D Mouse View OPERATION_STATE_DEF(mouse_rotate_view_operation_state) { - v4 CameraStartPos; - camera* Camera; + v4 CameraStartPos; + camera* Camera; }; OPERATION_RENDER_PROC(Update3DViewMouseRotate) { - mouse_rotate_view_operation_state* OpState = (mouse_rotate_view_operation_state*)Operation.OpStateMemory; - - v2 TotalDeltaPos = Mouse.Pos - Mouse.DownPos; - - m44 XRotation = M44RotationX(-TotalDeltaPos.y * PIXEL_TO_WORLD_SCALE); - m44 YRotation = M44RotationY(TotalDeltaPos.x * PIXEL_TO_WORLD_SCALE); - m44 Combined = XRotation * YRotation; - - OpState->Camera->Position = (Combined * OpState->CameraStartPos).xyz; + mouse_rotate_view_operation_state* OpState = (mouse_rotate_view_operation_state*)Operation.OpStateMemory; + + v2 TotalDeltaPos = Mouse.Pos - Mouse.DownPos; + + m44 XRotation = M44RotationX(-TotalDeltaPos.y * PIXEL_TO_WORLD_SCALE); + m44 YRotation = M44RotationY(TotalDeltaPos.x * PIXEL_TO_WORLD_SCALE); + m44 Combined = XRotation * YRotation; + + OpState->Camera->Position = (Combined * OpState->CameraStartPos).xyz; } FOLDHAUS_INPUT_COMMAND_PROC(End3DViewMouseRotate) { - DeactivateCurrentOperationMode(&State->Modes); + DeactivateCurrentOperationMode(&State->Modes); } input_command MouseRotateViewCommands [] = { - { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, End3DViewMouseRotate}, + { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, End3DViewMouseRotate}, }; FOLDHAUS_INPUT_COMMAND_PROC(Begin3DViewMouseRotate) { - sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state); - - operation_mode* RotateViewMode = ActivateOperationModeWithCommands(&State->Modes, MouseRotateViewCommands, Update3DViewMouseRotate); - mouse_rotate_view_operation_state* OpState = CreateOperationState(RotateViewMode, - &State->Modes, - mouse_rotate_view_operation_state); - OpState->CameraStartPos = ToV4Point(PanelState->Camera.Position); - OpState->Camera = &PanelState->Camera; + sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state); + + operation_mode* RotateViewMode = ActivateOperationModeWithCommands(&State->Modes, MouseRotateViewCommands, Update3DViewMouseRotate); + mouse_rotate_view_operation_state* OpState = CreateOperationState(RotateViewMode, + &State->Modes, + mouse_rotate_view_operation_state); + OpState->CameraStartPos = ToV4Point(PanelState->Camera.Position); + OpState->Camera = &PanelState->Camera; } // ---------------- @@ -63,7 +63,7 @@ FOLDHAUS_INPUT_COMMAND_PROC(Begin3DViewMouseRotate) GSMetaTag(panel_commands); GSMetaTag(panel_type_sculpture_view); global input_command SculptureView_Commands[] = { - { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, Begin3DViewMouseRotate }, + { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, Begin3DViewMouseRotate }, }; global s32 SculptureView_CommandsCount = 1; @@ -72,16 +72,16 @@ GSMetaTag(panel_type_sculpture_view); internal void SculptureView_Init(panel* Panel, app_state* State, context Context) { - sculpture_view_panel_state* PanelState = PushStruct(&State->Permanent, sculpture_view_panel_state); - - PanelState->Camera.FieldOfView = 45.0f; - PanelState->Camera.AspectRatio = RectAspectRatio(State->WindowBounds); - PanelState->Camera.Near = .1f; - PanelState->Camera.Far = 800.0f; - PanelState->Camera.Position = v3{0, 0, 400}; - PanelState->Camera.LookAt = v3{0, 0, 0}; - - Panel->StateMemory = StructToData(PanelState, sculpture_view_panel_state); + sculpture_view_panel_state* PanelState = PushStruct(&State->Permanent, sculpture_view_panel_state); + + PanelState->Camera.FieldOfView = 45.0f; + PanelState->Camera.AspectRatio = RectAspectRatio(State->WindowBounds); + PanelState->Camera.Near = .1f; + PanelState->Camera.Far = 800.0f; + PanelState->Camera.Position = v3{0, 0, 400}; + PanelState->Camera.LookAt = v3{0, 0, 0}; + + Panel->StateMemory = StructToData(PanelState, sculpture_view_panel_state); } GSMetaTag(panel_cleanup); @@ -89,92 +89,92 @@ GSMetaTag(panel_type_sculpture_view); internal void SculptureView_Cleanup(panel* Panel, app_state* State) { - + } struct draw_leds_job_data { - v4 CameraPosition; - led_buffer LedBuffer; - s32 StartIndex; - s32 OnePastLastIndex; - render_quad_batch_constructor* Batch; - quad_batch_constructor_reserved_range BatchReservedRange; - r32 LEDHalfWidth; + v4 CameraPosition; + led_buffer LedBuffer; + s32 StartIndex; + s32 OnePastLastIndex; + render_quad_batch_constructor* Batch; + quad_batch_constructor_reserved_range BatchReservedRange; + r32 LEDHalfWidth; }; internal void DrawLedsInBuffer(led_buffer LedBuffer, s32 StartIndex, s32 OnePastLastIndex, render_quad_batch_constructor* Batch, quad_batch_constructor_reserved_range ReservedRange, r32 LedHalfWidth) { - s32 TrisUsed = 0; + s32 TrisUsed = 0; + + v4 P0_In = v4{-LedHalfWidth, -LedHalfWidth, 0, 1}; + v4 P1_In = v4{LedHalfWidth, -LedHalfWidth, 0, 1}; + v4 P2_In = v4{LedHalfWidth, LedHalfWidth, 0, 1}; + v4 P3_In = v4{-LedHalfWidth, LedHalfWidth, 0, 1}; + + v2 UV0 = v2{0, 0}; + v2 UV1 = v2{1, 0}; + v2 UV2 = v2{1, 1}; + v2 UV3 = v2{0, 1}; + + Assert(OnePastLastIndex <= (s32)LedBuffer.LedCount); + for (s32 LedIndex = StartIndex; LedIndex < OnePastLastIndex; LedIndex++) + { + pixel PixelColor = LedBuffer.Colors[LedIndex]; + v4 Color = v4{PixelColor.R / 255.f, PixelColor.G / 255.f, PixelColor.B / 255.f, 1.0f}; - v4 P0_In = v4{-LedHalfWidth, -LedHalfWidth, 0, 1}; - v4 P1_In = v4{LedHalfWidth, -LedHalfWidth, 0, 1}; - v4 P2_In = v4{LedHalfWidth, LedHalfWidth, 0, 1}; - v4 P3_In = v4{-LedHalfWidth, LedHalfWidth, 0, 1}; + v4 Position = LedBuffer.Positions[LedIndex]; + v4 PositionOffset = ToV4Vec(Position.xyz); + v4 P0 = P0_In + PositionOffset; + v4 P1 = P1_In + PositionOffset; + v4 P2 = P2_In + PositionOffset; + v4 P3 = P3_In + PositionOffset; - v2 UV0 = v2{0, 0}; - v2 UV1 = v2{1, 0}; - v2 UV2 = v2{1, 1}; - v2 UV3 = v2{0, 1}; - - Assert(OnePastLastIndex <= (s32)LedBuffer.LedCount); - for (s32 LedIndex = StartIndex; LedIndex < OnePastLastIndex; LedIndex++) - { - pixel PixelColor = LedBuffer.Colors[LedIndex]; - v4 Color = v4{PixelColor.R / 255.f, PixelColor.G / 255.f, PixelColor.B / 255.f, 1.0f}; - - v4 Position = LedBuffer.Positions[LedIndex]; - v4 PositionOffset = ToV4Vec(Position.xyz); - v4 P0 = P0_In + PositionOffset; - v4 P1 = P1_In + PositionOffset; - v4 P2 = P2_In + PositionOffset; - v4 P3 = P3_In + PositionOffset; - - SetTri3DInBatch(Batch, ReservedRange.Start + TrisUsed++, P0, P1, P2, UV0, UV1, UV2, Color, Color, Color); - SetTri3DInBatch(Batch, ReservedRange.Start + TrisUsed++, P0, P2, P3, UV0, UV2, UV3, Color, Color, Color); - } + SetTri3DInBatch(Batch, ReservedRange.Start + TrisUsed++, P0, P1, P2, UV0, UV1, UV2, Color, Color, Color); + SetTri3DInBatch(Batch, ReservedRange.Start + TrisUsed++, P0, P2, P3, UV0, UV2, UV3, Color, Color, Color); + } } internal void DrawLEDsInBufferRangeJob (gs_thread_context Context, gs_data JobData) { - DEBUG_TRACK_FUNCTION; - draw_leds_job_data* Data = (draw_leds_job_data*)JobData.Memory; - DrawLedsInBuffer(Data->LedBuffer, Data->StartIndex, Data->OnePastLastIndex, Data->Batch, Data->BatchReservedRange, Data->LEDHalfWidth); + DEBUG_TRACK_FUNCTION; + draw_leds_job_data* Data = (draw_leds_job_data*)JobData.Memory; + DrawLedsInBuffer(Data->LedBuffer, Data->StartIndex, Data->OnePastLastIndex, Data->Batch, Data->BatchReservedRange, Data->LEDHalfWidth); } internal void DrawQuad(render_command_buffer* RenderBuffer, v4 C, r32 Rad, v4 Color) { - v4 P0 = C + v4{-Rad,-Rad,0,0}; - v4 P1 = C + v4{ Rad,-Rad,0,0}; - v4 P2 = C + v4{ Rad,Rad,0,0}; - v4 P3 = C + v4{ -Rad,Rad,0,0}; - PushRenderQuad3D(RenderBuffer, P0, P1, P2, P3, Color); + v4 P0 = C + v4{-Rad,-Rad,0,0}; + v4 P1 = C + v4{ Rad,-Rad,0,0}; + v4 P2 = C + v4{ Rad,Rad,0,0}; + v4 P3 = C + v4{ -Rad,Rad,0,0}; + PushRenderQuad3D(RenderBuffer, P0, P1, P2, P3, Color); } internal v2 SculptureView_WorldToScreenPosition(v4 WorldPosition, camera Camera, rect2 PanelBounds) { - v2 Result = {0}; - - r32 PanelW = Rect2Width(PanelBounds); - r32 PanelH = Rect2Height(PanelBounds); - - m44 Matrix = GetCameraPerspectiveProjectionMatrix(Camera) * GetCameraModelViewMatrix(Camera); - v4 WorldPos = Matrix * WorldPosition; - - // this is the Perspective Divide - v2 ProjectedPos = WorldPos.xy / WorldPos.w; - - // Projection gets us in a range [-1, 1], and we want [0, width] - ProjectedPos.x = ((ProjectedPos.x / 2) * PanelW) + (PanelW / 2); - ProjectedPos.y = ((ProjectedPos.y / 2) * PanelH) + (PanelH / 2); - - Result = ProjectedPos + PanelBounds.Min; - return Result; + v2 Result = {0}; + + r32 PanelW = Rect2Width(PanelBounds); + r32 PanelH = Rect2Height(PanelBounds); + + m44 Matrix = GetCameraPerspectiveProjectionMatrix(Camera) * GetCameraModelViewMatrix(Camera); + v4 WorldPos = Matrix * WorldPosition; + + // this is the Perspective Divide + v2 ProjectedPos = WorldPos.xy / WorldPos.w; + + // Projection gets us in a range [-1, 1], and we want [0, width] + ProjectedPos.x = ((ProjectedPos.x / 2) * PanelW) + (PanelW / 2); + ProjectedPos.y = ((ProjectedPos.y / 2) * PanelH) + (PanelH / 2); + + Result = ProjectedPos + PanelBounds.Min; + return Result; } GSMetaTag(panel_render); @@ -182,62 +182,62 @@ GSMetaTag(panel_type_sculpture_view); internal void SculptureView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { - DEBUG_TRACK_SCOPE(RenderSculpture); - sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state); - PanelState->Camera.AspectRatio = RectAspectRatio(PanelBounds); - - PushRenderPerspective(RenderBuffer, PanelBounds, PanelState->Camera); - - u32 MaxLEDsPerJob = 2048; - render_quad_batch_constructor RenderLEDsBatch = PushRenderQuad3DBatch(RenderBuffer, State->LedSystem.LedsCountTotal); - - u32 FocusPixel = 100; - - for (u32 BufferIndex = 0; BufferIndex < State->LedSystem.BuffersCount; BufferIndex++) + DEBUG_TRACK_SCOPE(RenderSculpture); + sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state); + PanelState->Camera.AspectRatio = RectAspectRatio(PanelBounds); + + PushRenderPerspective(RenderBuffer, PanelBounds, PanelState->Camera); + + u32 MaxLEDsPerJob = 2048; + render_quad_batch_constructor RenderLEDsBatch = PushRenderQuad3DBatch(RenderBuffer, State->LedSystem.LedsCountTotal); + + u32 FocusPixel = 100; + + for (u32 BufferIndex = 0; BufferIndex < State->LedSystem.BuffersCount; BufferIndex++) + { + led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, BufferIndex); + u32 JobsNeeded = U32DivideRoundUp(LedBuffer->LedCount, MaxLEDsPerJob); + u32 NextLEDIndex = 0; + for (u32 Job = 0; Job < JobsNeeded; Job++) { - led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, BufferIndex); - u32 JobsNeeded = U32DivideRoundUp(LedBuffer->LedCount, MaxLEDsPerJob); - u32 NextLEDIndex = 0; - for (u32 Job = 0; Job < JobsNeeded; Job++) - { - gs_data Data = PushSizeToData(State->Transient, sizeof(draw_leds_job_data)); - draw_leds_job_data* JobData = (draw_leds_job_data*)Data.Memory; - JobData->LedBuffer = *LedBuffer; - JobData->StartIndex = NextLEDIndex; - JobData->OnePastLastIndex = Min(JobData->StartIndex + MaxLEDsPerJob, LedBuffer->LedCount); - s32 JobLedCount = JobData->OnePastLastIndex - JobData->StartIndex; - JobData->Batch = &RenderLEDsBatch; - JobData->BatchReservedRange = ReserveRangeInQuadConstructor(JobData->Batch, JobLedCount * 2); - JobData->LEDHalfWidth = .5f; - JobData->CameraPosition = ToV4Point(PanelState->Camera.Position); + gs_data Data = PushSize(State->Transient, sizeof(draw_leds_job_data)); + draw_leds_job_data* JobData = (draw_leds_job_data*)Data.Memory; + JobData->LedBuffer = *LedBuffer; + JobData->StartIndex = NextLEDIndex; + JobData->OnePastLastIndex = Min(JobData->StartIndex + MaxLEDsPerJob, LedBuffer->LedCount); + s32 JobLedCount = JobData->OnePastLastIndex - JobData->StartIndex; + JobData->Batch = &RenderLEDsBatch; + JobData->BatchReservedRange = ReserveRangeInQuadConstructor(JobData->Batch, JobLedCount * 2); + JobData->LEDHalfWidth = .5f; + JobData->CameraPosition = ToV4Point(PanelState->Camera.Position); #if 1 - Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, (thread_proc*)DrawLEDsInBufferRangeJob, Data, ConstString("Sculpture Draw LEDS")); + Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, (thread_proc*)DrawLEDsInBufferRangeJob, Data, ConstString("Sculpture Draw LEDS")); #else - DrawLedsInBuffer(JobData->LedBuffer, JobData->StartIndex, JobData->OnePastLastIndex, JobData->Batch, JobData->BatchReservedRange, JobData->LEDHalfWidth); + DrawLedsInBuffer(JobData->LedBuffer, JobData->StartIndex, JobData->OnePastLastIndex, JobData->Batch, JobData->BatchReservedRange, JobData->LEDHalfWidth); #endif - NextLEDIndex = JobData->OnePastLastIndex; - } + NextLEDIndex = JobData->OnePastLastIndex; } + } + + // TODO(Peter): I don't like the fact that setting an orthographic view inside a panel render function + // needs to relyon the window bounds rather than the panel bounds. Ideally the panel only needs to know where + // itself is, and nothing else. + PushRenderOrthographic(RenderBuffer, State->WindowBounds); + if (State->Assemblies.Count > 0) + { + assembly Assembly = State->Assemblies.Values[0]; + led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, Assembly.LedBufferIndex); - // TODO(Peter): I don't like the fact that setting an orthographic view inside a panel render function - // needs to relyon the window bounds rather than the panel bounds. Ideally the panel only needs to know where - // itself is, and nothing else. - PushRenderOrthographic(RenderBuffer, State->WindowBounds); - if (State->Assemblies.Count > 0) - { - assembly Assembly = State->Assemblies.Values[0]; - led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, Assembly.LedBufferIndex); - - v4 LedPosition = LedBuffer->Positions[FocusPixel]; - v2 LedOnScreenPosition = SculptureView_WorldToScreenPosition(LedPosition, PanelState->Camera, PanelBounds); - - gs_string Tempgs_string = PushString(State->Transient, 256); - PrintF(&Tempgs_string, "Hot Id: %u, ZIndex: %u | Active Id: %u", State->Interface.HotWidget.Id, - State->Interface.HotWidget.ZIndex,State->Interface.ActiveWidget.Id); - DrawString(RenderBuffer, Tempgs_string, State->Interface.Style.Font, v2{PanelBounds.Min.x + 100, PanelBounds.Max.y - 200}, WhiteV4, -1, GreenV4); - - } - Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); + v4 LedPosition = LedBuffer->Positions[FocusPixel]; + v2 LedOnScreenPosition = SculptureView_WorldToScreenPosition(LedPosition, PanelState->Camera, PanelBounds); + + gs_string Tempgs_string = PushString(State->Transient, 256); + PrintF(&Tempgs_string, "Hot Id: %u, ZIndex: %u | Active Id: %u", State->Interface.HotWidget.Id, + State->Interface.HotWidget.ZIndex,State->Interface.ActiveWidget.Id); + DrawString(RenderBuffer, Tempgs_string, State->Interface.Style.Font, v2{PanelBounds.Min.x + 100, PanelBounds.Max.y - 200}, WhiteV4, -1, GreenV4); + + } + Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); } #define FOLDHAUS_PANEL_SCULPTURE_VIEW_H diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index eb2c130..6f7d243 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -8,122 +8,122 @@ internal pixel LedBlend_Overwrite(pixel PixelA, pixel PixelB, u8* UserData) { - r32 MagB = (r32)(PixelB.R + PixelB.G + PixelB.B) / (255 * 3); - - pixel Result = {}; - Result.R = (u8)LerpR32(MagB, PixelA.R, PixelB.R); - Result.G = (u8)LerpR32(MagB, PixelA.G, PixelB.G); - Result.B = (u8)LerpR32(MagB, PixelA.B, PixelB.B); - + r32 MagB = (r32)(PixelB.R + PixelB.G + PixelB.B) / (255 * 3); + + pixel Result = {}; + Result.R = (u8)LerpR32(MagB, PixelA.R, PixelB.R); + Result.G = (u8)LerpR32(MagB, PixelA.G, PixelB.G); + Result.B = (u8)LerpR32(MagB, PixelA.B, PixelB.B); + #if 0 - pixel Result = PixelB; - if (PixelB.R == 0 && - PixelB.G == 0 && - PixelB.B == 0) - { - Result = PixelA; - } + pixel Result = PixelB; + if (PixelB.R == 0 && + PixelB.G == 0 && + PixelB.B == 0) + { + Result = PixelA; + } #endif - return Result; + return Result; } internal pixel LedBlend_Lerp(pixel PixelA, pixel PixelB, u8* UserData) { - r32 BOpacity = *(r32*)UserData; - - pixel Result = {}; - - r32 AOpacity = 1.0f - BOpacity; - Result.R = (u8)((PixelA.R * AOpacity) + (PixelB.R * BOpacity)); - Result.G = (u8)((PixelA.G * AOpacity) + (PixelB.G * BOpacity)); - Result.B = (u8)((PixelA.B * AOpacity) + (PixelB.B * BOpacity)); - return Result; + r32 BOpacity = *(r32*)UserData; + + pixel Result = {}; + + r32 AOpacity = 1.0f - BOpacity; + Result.R = (u8)((PixelA.R * AOpacity) + (PixelB.R * BOpacity)); + Result.G = (u8)((PixelA.G * AOpacity) + (PixelB.G * BOpacity)); + Result.B = (u8)((PixelA.B * AOpacity) + (PixelB.B * BOpacity)); + return Result; } internal pixel LedBlend_Add(pixel PixelA, pixel PixelB, u8* UserData) { - pixel Result = {}; - - u32 R = (u32)PixelA.R + (u32)PixelB.R; - u32 G = (u32)PixelA.G + (u32)PixelB.G; - u32 B = (u32)PixelA.B + (u32)PixelB.B; - - Result.R = (u8)Min(R, (u32)255); - Result.G = (u8)Min(G, (u32)255); - Result.B = (u8)Min(B, (u32)255); - - return Result; + pixel Result = {}; + + u32 R = (u32)PixelA.R + (u32)PixelB.R; + u32 G = (u32)PixelA.G + (u32)PixelB.G; + u32 B = (u32)PixelA.B + (u32)PixelB.B; + + Result.R = (u8)Min(R, (u32)255); + Result.G = (u8)Min(G, (u32)255); + Result.B = (u8)Min(B, (u32)255); + + return Result; } internal pixel LedBlend_Multiply(pixel PixelA, pixel PixelB, u8* UserData) { - pixel Result = {}; - - r32 DR = (r32)PixelA.R / 255.f; - r32 DG = (r32)PixelA.G / 255.f; - r32 DB = (r32)PixelA.B / 255.f; - - r32 SR = (r32)PixelB.R / 255.f; - r32 SG = (r32)PixelB.G / 255.f; - r32 SB = (r32)PixelB.B / 255.f; - - Result.R = (u8)((DR * SR) * 255.f); - Result.G = (u8)((DG * SG) * 255.f); - Result.B = (u8)((DB * SB) * 255.f); - - return Result; + pixel Result = {}; + + r32 DR = (r32)PixelA.R / 255.f; + r32 DG = (r32)PixelA.G / 255.f; + r32 DB = (r32)PixelA.B / 255.f; + + r32 SR = (r32)PixelB.R / 255.f; + r32 SG = (r32)PixelB.G / 255.f; + r32 SB = (r32)PixelB.B / 255.f; + + Result.R = (u8)((DR * SR) * 255.f); + Result.G = (u8)((DG * SG) * 255.f); + Result.B = (u8)((DB * SB) * 255.f); + + return Result; } internal pixel LedBlend_Overlay(pixel PixelA, pixel PixelB, u8* UserData) { - pixel Result = {}; - return Result; + pixel Result = {}; + return Result; } internal led_blend_proc* LedBlend_GetProc(blend_mode BlendMode) { - led_blend_proc* Result = 0; - switch (BlendMode) - { - case BlendMode_Overwrite: { Result = LedBlend_Overwrite; }break; - case BlendMode_Add: { Result = LedBlend_Add; }break; - case BlendMode_Multiply: { Result = LedBlend_Multiply; }break; - InvalidDefaultCase; - } - return Result; + led_blend_proc* Result = 0; + switch (BlendMode) + { + case BlendMode_Overwrite: { Result = LedBlend_Overwrite; }break; + case BlendMode_Add: { Result = LedBlend_Add; }break; + case BlendMode_Multiply: { Result = LedBlend_Multiply; }break; + InvalidDefaultCase; + } + return Result; } struct pattern_args { - assembly Assembly; - gs_memory_arena* Transient; - u8* UserData; + assembly Assembly; + gs_memory_arena* Transient; + u8* UserData; }; struct render_anim_to_led_buffer_job_data { - animation_pattern Pattern; - led_buffer Buffer; - led_buffer_range BufferRange; - pattern_args PatternArgs; - r32 SecondsIntoBlock; + animation_pattern Pattern; + led_buffer Buffer; + led_buffer_range BufferRange; + pattern_args PatternArgs; + r32 SecondsIntoBlock; }; internal void AnimationSystem_RenderAnimationToLedBufferJob(gs_thread_context Context, gs_data Data) { - render_anim_to_led_buffer_job_data JobData = *(render_anim_to_led_buffer_job_data*)Data.Memory; - JobData.Pattern.Proc(&JobData.Buffer, - JobData.BufferRange, - JobData.PatternArgs.Assembly, - JobData.SecondsIntoBlock, - JobData.PatternArgs.Transient, - JobData.PatternArgs.UserData); + render_anim_to_led_buffer_job_data JobData = *(render_anim_to_led_buffer_job_data*)Data.Memory; + JobData.Pattern.Proc(&JobData.Buffer, + JobData.BufferRange, + JobData.PatternArgs.Assembly, + JobData.SecondsIntoBlock, + JobData.PatternArgs.Transient, + JobData.PatternArgs.UserData); } #define MULTITHREAD_PATTERN_RENDERING 1 @@ -132,61 +132,61 @@ internal void AnimationSystem_BeginRenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, animation_pattern_array Patterns, pattern_args PatternArgs, context Context) { - DEBUG_TRACK_FUNCTION; + DEBUG_TRACK_FUNCTION; + + u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min; + r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame; + + animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle); + Assert(Pattern.Proc); + + if (System->Multithreaded && Pattern.Multithreaded) + { + u32 JobsCount = 4; + u32 LedsPerJob = Buffer->LedCount / JobsCount; - u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min; - r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame; - - animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle); - Assert(Pattern.Proc); - - if (System->Multithreaded && Pattern.Multithreaded) + for (u32 i = 0; i < JobsCount; i++) { - u32 JobsCount = 4; - u32 LedsPerJob = Buffer->LedCount / JobsCount; - - for (u32 i = 0; i < JobsCount; i++) - { - gs_data Data = PushSizeToData(Context.ThreadContext.Transient, sizeof(render_anim_to_led_buffer_job_data)); - render_anim_to_led_buffer_job_data* JobData = (render_anim_to_led_buffer_job_data*)Data.Memory; - JobData->Pattern = Pattern; - JobData->Buffer = *Buffer; - JobData->BufferRange.First = LedsPerJob * i; - JobData->BufferRange.OnePastLast = LedsPerJob * (i + 1); - JobData->PatternArgs = PatternArgs; - JobData->SecondsIntoBlock = SecondsIntoBlock; - - Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, - (thread_proc*)AnimationSystem_RenderAnimationToLedBufferJob, - Data, - ConstString("Render Pattern To Buffer")); - } - } - else - { - led_buffer_range Range = {}; - Range.First = 0; - Range.OnePastLast = Buffer->LedCount; - - Pattern.Proc(Buffer, Range, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData); + gs_data Data = PushSize(Context.ThreadContext.Transient, sizeof(render_anim_to_led_buffer_job_data)); + render_anim_to_led_buffer_job_data* JobData = (render_anim_to_led_buffer_job_data*)Data.Memory; + JobData->Pattern = Pattern; + JobData->Buffer = *Buffer; + JobData->BufferRange.First = LedsPerJob * i; + JobData->BufferRange.OnePastLast = LedsPerJob * (i + 1); + JobData->PatternArgs = PatternArgs; + JobData->SecondsIntoBlock = SecondsIntoBlock; + + Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, + (thread_proc*)AnimationSystem_RenderAnimationToLedBufferJob, + Data, + ConstString("Render Pattern To Buffer")); } + } + else + { + led_buffer_range Range = {}; + Range.First = 0; + Range.OnePastLast = Buffer->LedCount; + + Pattern.Proc(Buffer, Range, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData); + } } internal void AnimationSystem_EndRenderBlockToLedBuffer (animation_system* System, context Context) { - if (System->Multithreaded) - { - Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); - } + if (System->Multithreaded) + { + Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); + } } // NOTE(pjs): This mirrors animation_layer_frame to account // for overlapping struct layer_led_buffer { - led_buffer HotBuffer; - led_buffer NextHotBuffer; + led_buffer HotBuffer; + led_buffer NextHotBuffer; }; internal led_buffer @@ -199,80 +199,80 @@ RenderAnimationToLedBuffer (animation_system* System, gs_memory_arena* Transient, context Context) { - DEBUG_TRACK_FUNCTION; + DEBUG_TRACK_FUNCTION; + + led_buffer AccBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); + + // Create the LayerLEDBuffers + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + layer_led_buffer TempBuffer = {}; - led_buffer AccBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); - - // Create the LayerLEDBuffers - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + if (CurrFrame.Layers[Layer].HasHot) { - layer_led_buffer TempBuffer = {}; - - if (CurrFrame.Layers[Layer].HasHot) - { - TempBuffer.HotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); - - if (CurrFrame.Layers[Layer].HasNextHot) - { - TempBuffer.NextHotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); - } - } - - LayerBuffers[Layer] = TempBuffer; + TempBuffer.HotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); + + if (CurrFrame.Layers[Layer].HasNextHot) + { + TempBuffer.NextHotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); + } } - // Render Each layer's block to the appropriate temp buffer - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + LayerBuffers[Layer] = TempBuffer; + } + + // Render Each layer's block to the appropriate temp buffer + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; + if (LayerFrame.HasHot) { - animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; - if (LayerFrame.HasHot) - { - led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer; - animation_block Block = LayerFrame.Hot; - AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context); - } - - if (LayerFrame.HasNextHot) - { - led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer; - animation_block Block = LayerFrame.NextHot; - AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context); - } - - AnimationSystem_EndRenderBlockToLedBuffer(System, Context); + led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer; + animation_block Block = LayerFrame.Hot; + AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context); } - // Blend together any layers that have a hot and next hot buffer - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + if (LayerFrame.HasNextHot) { - animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; - layer_led_buffer LayerBuffer = LayerBuffers[Layer]; - if (LayerFrame.HasNextHot) - { - LedBuffer_Blend(LayerBuffer.HotBuffer, - LayerBuffer.NextHotBuffer, - &LayerBuffer.HotBuffer, - LedBlend_Lerp, - (u8*)&LayerFrame.NextHotOpacity); - } + led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer; + animation_block Block = LayerFrame.NextHot; + AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context); } - // Consolidate Temp Buffers back into AssemblyLedBuffer - // We do this in reverse order so that they go from top to bottom - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + AnimationSystem_EndRenderBlockToLedBuffer(System, Context); + } + + // Blend together any layers that have a hot and next hot buffer + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; + layer_led_buffer LayerBuffer = LayerBuffers[Layer]; + if (LayerFrame.HasNextHot) { - if (CurrFrame.Layers[Layer].HasHot) - { - led_blend_proc* Blend = LedBlend_GetProc(CurrFrame.Layers[Layer].BlendMode); - LedBuffer_Blend(AccBuffer, - LayerBuffers[Layer].HotBuffer, - &AccBuffer, - Blend, - 0); - } + LedBuffer_Blend(LayerBuffer.HotBuffer, + LayerBuffer.NextHotBuffer, + &LayerBuffer.HotBuffer, + LedBlend_Lerp, + (u8*)&LayerFrame.NextHotOpacity); } - - return AccBuffer; + } + + // Consolidate Temp Buffers back into AssemblyLedBuffer + // We do this in reverse order so that they go from top to bottom + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + if (CurrFrame.Layers[Layer].HasHot) + { + led_blend_proc* Blend = LedBlend_GetProc(CurrFrame.Layers[Layer].BlendMode); + LedBuffer_Blend(AccBuffer, + LayerBuffers[Layer].HotBuffer, + &AccBuffer, + Blend, + 0); + } + } + + return AccBuffer; } internal void @@ -283,92 +283,92 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse context Context, u8* UserData) { - DEBUG_TRACK_FUNCTION; - - r32 FrameTime = AnimationSystem_GetCurrentTime(*System); - + DEBUG_TRACK_FUNCTION; + + r32 FrameTime = AnimationSystem_GetCurrentTime(*System); + #if 1 - animation_array Animations = System->Animations; - animation_fade_group FadeGroup = System->ActiveFadeGroup; + animation_array Animations = System->Animations; + animation_fade_group FadeGroup = System->ActiveFadeGroup; + + animation* FromAnim = AnimationArray_Get(Animations, FadeGroup.From); + animation_frame FromFrame = AnimationSystem_CalculateAnimationFrame(System, FromAnim, Transient); + layer_led_buffer* FromLayerBuffers = PushArray(Transient, layer_led_buffer, FromFrame.LayersCount); + + animation* ToAnim = AnimationArray_Get(Animations, FadeGroup.To); + animation_frame ToFrame = {0}; + layer_led_buffer* ToLayerBuffers = 0; + if (ToAnim) + { + ToFrame = AnimationSystem_CalculateAnimationFrame(System, ToAnim, Transient); + ToLayerBuffers = PushArray(Transient, layer_led_buffer, ToFrame.LayersCount); + } + + for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) + { + assembly Assembly = Assemblies.Values[AssemblyIndex]; + led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); - animation* FromAnim = AnimationArray_Get(Animations, FadeGroup.From); - animation_frame FromFrame = AnimationSystem_CalculateAnimationFrame(System, FromAnim, Transient); - layer_led_buffer* FromLayerBuffers = PushArray(Transient, layer_led_buffer, FromFrame.LayersCount); + pattern_args PatternArgs = {}; + PatternArgs.Assembly = Assembly; + PatternArgs.Transient = Transient; + PatternArgs.UserData = UserData; - animation* ToAnim = AnimationArray_Get(Animations, FadeGroup.To); - animation_frame ToFrame = {0}; - layer_led_buffer* ToLayerBuffers = 0; - if (ToAnim) - { - ToFrame = AnimationSystem_CalculateAnimationFrame(System, ToAnim, Transient); - ToLayerBuffers = PushArray(Transient, layer_led_buffer, ToFrame.LayersCount); - } - - for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) - { - assembly Assembly = Assemblies.Values[AssemblyIndex]; - led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); - - pattern_args PatternArgs = {}; - PatternArgs.Assembly = Assembly; - PatternArgs.Transient = Transient; - PatternArgs.UserData = UserData; - - led_buffer FromBuffer = RenderAnimationToLedBuffer(System, - PatternArgs, - FromFrame, - FromLayerBuffers, - AssemblyLedBuffer, - Patterns, - Transient, - Context); - led_buffer ConsolidatedBuffer = FromBuffer; - - if (ToAnim) { - led_buffer ToBuffer = RenderAnimationToLedBuffer(System, - PatternArgs, - ToFrame, - ToLayerBuffers, - AssemblyLedBuffer, - Patterns, - Transient, - Context); - - r32 BlendPercent = FadeGroup.FadeElapsed / FadeGroup.FadeDuration; - LedBuffer_Blend(FromBuffer, ToBuffer, &ConsolidatedBuffer, LedBlend_Lerp, (u8*)&BlendPercent); - } - - LedBuffer_Copy(ConsolidatedBuffer, AssemblyLedBuffer); + led_buffer FromBuffer = RenderAnimationToLedBuffer(System, + PatternArgs, + FromFrame, + FromLayerBuffers, + AssemblyLedBuffer, + Patterns, + Transient, + Context); + led_buffer ConsolidatedBuffer = FromBuffer; + + if (ToAnim) { + led_buffer ToBuffer = RenderAnimationToLedBuffer(System, + PatternArgs, + ToFrame, + ToLayerBuffers, + AssemblyLedBuffer, + Patterns, + Transient, + Context); + + r32 BlendPercent = FadeGroup.FadeElapsed / FadeGroup.FadeDuration; + LedBuffer_Blend(FromBuffer, ToBuffer, &ConsolidatedBuffer, LedBlend_Lerp, (u8*)&BlendPercent); } + LedBuffer_Copy(ConsolidatedBuffer, AssemblyLedBuffer); + } + #else + + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); + animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, ActiveAnim, Transient); + + for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) + { + assembly Assembly = Assemblies.Values[AssemblyIndex]; + led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, ActiveAnim, Transient); - - for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) - { - assembly Assembly = Assemblies.Values[AssemblyIndex]; - led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); - - pattern_args PatternArgs = {}; - PatternArgs.Assembly = Assembly; - PatternArgs.Transient = Transient; - PatternArgs.UserData = UserData; - - led_buffer AccBuffer = RenderAnimationToLedBuffer(System, - PatternArgs, - CurrFrame, - LayerBuffers, - AssemblyLedBuffer, - Patterns, - Transient); - LedBuffer_Copy(AccBuffer, AssemblyLedBuffer); - } + pattern_args PatternArgs = {}; + PatternArgs.Assembly = Assembly; + PatternArgs.Transient = Transient; + PatternArgs.UserData = UserData; + led_buffer AccBuffer = RenderAnimationToLedBuffer(System, + PatternArgs, + CurrFrame, + LayerBuffers, + AssemblyLedBuffer, + Patterns, + Transient); + LedBuffer_Copy(AccBuffer, AssemblyLedBuffer); + } + #endif - - System->LastUpdatedFrame = System->CurrentFrame; + + System->LastUpdatedFrame = System->CurrentFrame; } #define FOLDHAUS_ANIMATION_RENDERER_CPP diff --git a/src/app/engine/assembly/foldhaus_assembly.cpp b/src/app/engine/assembly/foldhaus_assembly.cpp index 6e69f4b..0e383f0 100644 --- a/src/app/engine/assembly/foldhaus_assembly.cpp +++ b/src/app/engine/assembly/foldhaus_assembly.cpp @@ -14,35 +14,35 @@ internal assembly_array AssemblyArray_Create(u32 CountMax, gs_memory_arena* Storage) { - assembly_array Result = {0}; - Result.CountMax = CountMax; - Result.Values = PushArray(Storage, assembly, Result.CountMax); - return Result; + assembly_array Result = {0}; + Result.CountMax = CountMax; + Result.Values = PushArray(Storage, assembly, Result.CountMax); + return Result; } internal u32 AssemblyArray_Push(assembly_array* Array, assembly Assembly) { - Assert(Array->Count < Array->CountMax); - u32 Index = Array->Count++; - Array->Values[Index] = Assembly; - Array->Values[Index].AssemblyIndex = Index; - return Index; + Assert(Array->Count < Array->CountMax); + u32 Index = Array->Count++; + Array->Values[Index] = Assembly; + Array->Values[Index].AssemblyIndex = Index; + return Index; } internal assembly* AssemblyArray_Take(assembly_array* Array) { - u32 Index = AssemblyArray_Push(Array, {}); - assembly* Result = Array->Values + Index; - return Result; + u32 Index = AssemblyArray_Push(Array, {}); + assembly* Result = Array->Values + Index; + return Result; } internal void AssemblyArray_RemoveAt(assembly_array* Array, u32 Index) { - u32 LastAssemblyIndex = --Array->Count; - Array->Values[Index] = Array->Values[LastAssemblyIndex]; + u32 LastAssemblyIndex = --Array->Count; + Array->Values[Index] = Array->Values[LastAssemblyIndex]; } typedef bool assembly_array_filter_proc(assembly A); @@ -52,18 +52,18 @@ bool AssemblyFilter_OutputsViaUART(assembly A) { return A.OutputMode == NetworkP internal assembly_array AssemblyArray_Filter(assembly_array Array, assembly_array_filter_proc* Filter, gs_memory_arena* Storage) { - assembly_array Result = AssemblyArray_Create(Array.Count, Storage); - - for (u32 i = 0; i < Array.Count; i++) + assembly_array Result = AssemblyArray_Create(Array.Count, Storage); + + for (u32 i = 0; i < Array.Count; i++) + { + assembly At = Array.Values[i]; + if (Filter(At)) { - assembly At = Array.Values[i]; - if (Filter(At)) - { - AssemblyArray_Push(&Result, At); - } + AssemblyArray_Push(&Result, At); } - - return Result; + } + + return Result; } /////////////////////////// @@ -75,177 +75,177 @@ AssemblyArray_Filter(assembly_array Array, assembly_array_filter_proc* Filter, g internal led_system LedSystem_Create(gs_allocator PlatformMemory, u32 BuffersMax) { - led_system Result = {}; - Result.PlatformMemory = PlatformMemory; - // TODO(Peter): Since we have access to PlatformMemory, just realloc Buffers when we fill it up - Result.BuffersCountMax = BuffersMax; - Result.Buffers = AllocatorAllocArray(PlatformMemory, led_buffer, Result.BuffersCountMax); - return Result; + led_system Result = {}; + Result.PlatformMemory = PlatformMemory; + // TODO(Peter): Since we have access to PlatformMemory, just realloc Buffers when we fill it up + Result.BuffersCountMax = BuffersMax; + Result.Buffers = AllocArray(PlatformMemory, led_buffer, Result.BuffersCountMax, "led system"); + return Result; } internal u32 LedSystemTakeFreeBuffer(led_system* System, u32 LedCount) { - s32 Result = -1; - - if (System->BuffersCount < System->BuffersCountMax) + s32 Result = -1; + + if (System->BuffersCount < System->BuffersCountMax) + { + Result = System->BuffersCount++; + } + else + { + // NOTE(Peter): Look for a buffer that's flagged as empty + for (u32 i = 0; i < System->BuffersCount; i++) { - Result = System->BuffersCount++; + if (System->Buffers[i].LedCount == 0 + && System->Buffers[i].Colors == 0 + && System->Buffers[i].Positions == 0) + { + Result = i; + break; + } } - else - { - // NOTE(Peter): Look for a buffer that's flagged as empty - for (u32 i = 0; i < System->BuffersCount; i++) - { - if (System->Buffers[i].LedCount == 0 - && System->Buffers[i].Colors == 0 - && System->Buffers[i].Positions == 0) - { - Result = i; - break; - } - } - Assert(Result >= 0); // NOTE(Peter): We ran out of room for led buffers - } - - led_buffer* Buffer = &System->Buffers[Result]; - Buffer->LedCount = LedCount; - Buffer->Colors = AllocatorAllocArray(System->PlatformMemory, pixel, Buffer->LedCount); - Buffer->Positions = AllocatorAllocArray(System->PlatformMemory, v4, Buffer->LedCount); - - System->LedsCountTotal += LedCount; - - return (u32)Result; + Assert(Result >= 0); // NOTE(Peter): We ran out of room for led buffers + } + + led_buffer* Buffer = &System->Buffers[Result]; + Buffer->A = MemoryArenaCreate(KB(16),Bytes(8),System->PlatformMemory,0,0,"Led Buffer Arena"); + Buffer->LedCount = LedCount; + Buffer->Colors = PushArray(&Buffer->A, pixel, Buffer->LedCount); + Buffer->Positions = PushArray(&Buffer->A, v4, Buffer->LedCount); + + System->LedsCountTotal += LedCount; + + return (u32)Result; } internal void LedSystemFreeBuffer(led_system* System, u32 BufferIndex) { - Assert(BufferIndex < System->BuffersCountMax); - led_buffer* Buffer = &System->Buffers[BufferIndex]; - AllocatorFreeArray(System->PlatformMemory, Buffer->Colors, pixel, Buffer->LedCount); - AllocatorFreeArray(System->PlatformMemory, Buffer->Positions, v4, Buffer->LedCount); - System->LedsCountTotal -= Buffer->LedCount; - *Buffer = {}; + Assert(BufferIndex < System->BuffersCountMax); + led_buffer* Buffer = &System->Buffers[BufferIndex]; + MemoryArenaFree(&Buffer->A); + System->LedsCountTotal -= Buffer->LedCount; + *Buffer = {}; } internal void LedBufferSetLed(led_buffer* Buffer, u32 Led, v4 Position) { - Assert(Led < Buffer->LedCount); - Buffer->Positions[Led] = Position; + Assert(Led < Buffer->LedCount); + Buffer->Positions[Led] = Position; } internal u32 Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* StripAt, strip_gen_data GenData, v4 RootPosition, u32 LedStartIndex, u32 LedLUTStartIndex) { - u32 LedsAdded = 0; - - switch (GenData.Method) + u32 LedsAdded = 0; + + switch (GenData.Method) + { + case StripGeneration_InterpolatePoints: { - case StripGeneration_InterpolatePoints: - { - strip_gen_interpolate_points InterpPoints = GenData.InterpolatePoints; - v4 WS_StripStart = RootPosition + ToV4Point(InterpPoints.StartPosition * Assembly->Scale); - v4 WS_StripEnd = RootPosition + ToV4Point(InterpPoints.EndPosition * Assembly->Scale); - - v4 SingleStep = (WS_StripEnd - WS_StripStart) / (r32)InterpPoints.LedCount; - for (u32 Step = 0; Step < InterpPoints.LedCount; Step++) - { - s32 LedIndex = LedStartIndex + LedsAdded++; - v4 LedPosition = WS_StripStart + (SingleStep * Step); - LedBufferSetLed(LedBuffer, LedIndex, LedPosition); - StripAt->LedLUT[Step + LedLUTStartIndex] = LedIndex; - } - }break; - - case StripGeneration_Sequence: - { - strip_gen_sequence Sequence = GenData.Sequence; - for (u32 i = 0; i < Sequence.ElementsCount; i++) - { - strip_gen_data SegmentGenData = Sequence.Elements[i]; - LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, SegmentGenData, RootPosition, LedStartIndex + LedsAdded, LedsAdded); - } - }break; - - InvalidDefaultCase; - } + strip_gen_interpolate_points InterpPoints = GenData.InterpolatePoints; + v4 WS_StripStart = RootPosition + ToV4Point(InterpPoints.StartPosition * Assembly->Scale); + v4 WS_StripEnd = RootPosition + ToV4Point(InterpPoints.EndPosition * Assembly->Scale); + + v4 SingleStep = (WS_StripEnd - WS_StripStart) / (r32)InterpPoints.LedCount; + for (u32 Step = 0; Step < InterpPoints.LedCount; Step++) + { + s32 LedIndex = LedStartIndex + LedsAdded++; + v4 LedPosition = WS_StripStart + (SingleStep * Step); + LedBufferSetLed(LedBuffer, LedIndex, LedPosition); + StripAt->LedLUT[Step + LedLUTStartIndex] = LedIndex; + } + }break; - return LedsAdded; + case StripGeneration_Sequence: + { + strip_gen_sequence Sequence = GenData.Sequence; + for (u32 i = 0; i < Sequence.ElementsCount; i++) + { + strip_gen_data SegmentGenData = Sequence.Elements[i]; + LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, SegmentGenData, RootPosition, LedStartIndex + LedsAdded, LedsAdded); + } + }break; + + InvalidDefaultCase; + } + + return LedsAdded; } internal void ConstructAssemblyFromDefinition (assembly* Assembly, led_system* LedSystem) { - Assembly->LedBufferIndex = LedSystemTakeFreeBuffer(LedSystem, Assembly->LedCountTotal); - led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex); + Assembly->LedBufferIndex = LedSystemTakeFreeBuffer(LedSystem, Assembly->LedCountTotal); + led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex); + + v4 RootPosition = ToV4Vec(Assembly->Center); + + // Add Leds + u32 LedsAdded = 0; + for (u32 StripIdx = 0; StripIdx < Assembly->StripCount; StripIdx++) + { + v2_strip* StripAt = &Assembly->Strips[StripIdx]; + StripAt->LedLUT = PushArray(&Assembly->Arena, u32, StripAt->LedCount); - v4 RootPosition = ToV4Vec(Assembly->Center); - - // Add Leds - u32 LedsAdded = 0; - for (u32 StripIdx = 0; StripIdx < Assembly->StripCount; StripIdx++) - { - v2_strip* StripAt = &Assembly->Strips[StripIdx]; - StripAt->LedLUT = PushArray(&Assembly->Arena, u32, StripAt->LedCount); - - strip_gen_data GenData = StripAt->GenerationData; - LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, GenData, RootPosition, LedsAdded, 0); - } + strip_gen_data GenData = StripAt->GenerationData; + LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, GenData, RootPosition, LedsAdded, 0); + } } internal assembly* LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena* Scratch, context Context, gs_const_string Path, log_buffer* GlobalLog) { - assembly* NewAssembly = 0; + assembly* NewAssembly = 0; + + gs_file AssemblyFile = ReadEntireFile(Context.ThreadContext.FileHandler, Path); + if (FileNoError(AssemblyFile)) + { + gs_string AssemblyFileText = MakeString((char*)AssemblyFile.Memory); - gs_file AssemblyFile = ReadEntireFile(Context.ThreadContext.FileHandler, Path); - if (FileNoError(AssemblyFile)) + s32 IndexOfLastSlash = FindLast(Path, '\\'); + gs_const_string FileName = Substring(Path, IndexOfLastSlash + 1, Path.Length); + + NewAssembly = AssemblyArray_Take(Assemblies); + NewAssembly->Arena = MemoryArenaCreate(MB(4), Bytes(8), Context.ThreadContext.Allocator, 0, 0, "Assembly Arena"); + + parser AssemblyParser = ParseAssemblyFile(NewAssembly, FileName, AssemblyFileText, Scratch); + if (AssemblyParser.Success) { - gs_string AssemblyFileText = MakeString((char*)AssemblyFile.Memory); - - s32 IndexOfLastSlash = FindLast(Path, '\\'); - gs_const_string FileName = Substring(Path, IndexOfLastSlash + 1, Path.Length); - - NewAssembly = AssemblyArray_Take(Assemblies); - NewAssembly->Arena = CreateMemoryArena(Context.ThreadContext.Allocator, "Assembly Arena"); - - parser AssemblyParser = ParseAssemblyFile(NewAssembly, FileName, AssemblyFileText, Scratch); - if (AssemblyParser.Success) - { - ConstructAssemblyFromDefinition(NewAssembly, LedSystem); - } - else - { - FreeMemoryArena(&NewAssembly->Arena); - Assemblies->Count -= 1; - } - - for (parser_error* ErrorAt = AssemblyParser.ErrorsRoot; - ErrorAt != 0; - ErrorAt = ErrorAt->Next) - { - Log_Error(GlobalLogBuffer, ErrorAt->Message.Str); - } - + ConstructAssemblyFromDefinition(NewAssembly, LedSystem); } else { - Log_Error(GlobalLog, "Unable to load assembly file"); + MemoryArenaFree(&NewAssembly->Arena); + Assemblies->Count -= 1; } - return NewAssembly; + for (parser_error* ErrorAt = AssemblyParser.ErrorsRoot; + ErrorAt != 0; + ErrorAt = ErrorAt->Next) + { + Log_Error(GlobalLogBuffer, ErrorAt->Message.Str); + } + + } + else + { + Log_Error(GlobalLog, "Unable to load assembly file"); + } + + return NewAssembly; } internal void UnloadAssembly (u32 AssemblyIndex, app_state* State, context Context) { - Assert(AssemblyIndex < State->Assemblies.Count); - assembly* Assembly = &State->Assemblies.Values[AssemblyIndex]; - LedSystemFreeBuffer(&State->LedSystem, Assembly->LedBufferIndex); - FreeMemoryArena(&Assembly->Arena); - AssemblyArray_RemoveAt(&State->Assemblies, AssemblyIndex); + Assert(AssemblyIndex < State->Assemblies.Count); + assembly* Assembly = &State->Assemblies.Values[AssemblyIndex]; + LedSystemFreeBuffer(&State->LedSystem, Assembly->LedBufferIndex); + MemoryArenaFree(&Assembly->Arena); + AssemblyArray_RemoveAt(&State->Assemblies, AssemblyIndex); } // Querying Assemblies @@ -253,30 +253,30 @@ UnloadAssembly (u32 AssemblyIndex, app_state* State, context Context) internal led_strip_list AssemblyStripsGetWithTagValue(assembly Assembly, gs_const_string TagName, gs_const_string TagValue, gs_memory_arena* Storage) { - led_strip_list Result = {0}; - // TODO(pjs): @Optimization - // We can probably come back here and do this allocation procedurally, or in buckets, or with - // a linked list. But for now, I just want to get this up and running - Result.CountMax = Assembly.StripCount; - Result.StripIndices = PushArray(Storage, u32, Result.CountMax); - - u64 NameHash = HashDJB2ToU32(StringExpand(TagName)); - u64 ValueHash = 0; - if (TagValue.Length > 0) + led_strip_list Result = {0}; + // TODO(pjs): @Optimization + // We can probably come back here and do this allocation procedurally, or in buckets, or with + // a linked list. But for now, I just want to get this up and running + Result.CountMax = Assembly.StripCount; + Result.StripIndices = PushArray(Storage, u32, Result.CountMax); + + u64 NameHash = HashDJB2ToU32(StringExpand(TagName)); + u64 ValueHash = 0; + if (TagValue.Length > 0) + { + ValueHash = HashDJB2ToU32(StringExpand(TagValue)); + } + + for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) + { + v2_strip StripAt = Assembly.Strips[StripIndex]; + if (AssemblyStrip_HasTagValue(StripAt, NameHash, ValueHash)) { - ValueHash = HashDJB2ToU32(StringExpand(TagValue)); + Result.StripIndices[Result.Count++] = StripIndex; } - - for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) - { - v2_strip StripAt = Assembly.Strips[StripIndex]; - if (AssemblyStrip_HasTagValue(StripAt, NameHash, ValueHash)) - { - Result.StripIndices[Result.Count++] = StripIndex; - } - } - - return Result; + } + + return Result; } #define FOLDHAUS_ASSEMBLY_CPP diff --git a/src/app/engine/assembly/foldhaus_assembly.h b/src/app/engine/assembly/foldhaus_assembly.h index 7f6ca8b..b1b9db9 100644 --- a/src/app/engine/assembly/foldhaus_assembly.h +++ b/src/app/engine/assembly/foldhaus_assembly.h @@ -7,165 +7,172 @@ enum network_protocol { - NetworkProtocol_SACN, - NetworkProtocol_ArtNet, - NetworkProtocol_UART, - - NetworkProtocol_Count, + NetworkProtocol_SACN, + NetworkProtocol_ArtNet, + NetworkProtocol_UART, + + NetworkProtocol_Count, }; union pixel { - struct - { - u8 R; - u8 G; - u8 B; - }; - u8 Channels[3]; + struct + { + u8 R; + u8 G; + u8 B; + }; + u8 Channels[3]; }; struct led_buffer { - u32 LedCount; - pixel* Colors; - v4* Positions; + // NOTE(PS): This is just a tracking structure, + // that enables allocations for a particular buffer + // to occur all in contiguous memory + // and should not be pushed to after the initial + // allocation + gs_memory_arena A; + + u32 LedCount; + pixel* Colors; + v4* Positions; }; struct led_buffer_range { - u32 First; - u32 OnePastLast; + u32 First; + u32 OnePastLast; }; struct led_system { - gs_allocator PlatformMemory; - - u32 BuffersCountMax; - u32 BuffersCount; - led_buffer* Buffers; - - u32 LedsCountTotal; + gs_allocator PlatformMemory; + + u32 BuffersCountMax; + u32 BuffersCount; + led_buffer* Buffers; + + u32 LedsCountTotal; }; struct v2_tag { - u64 NameHash; - u64 ValueHash; + u64 NameHash; + u64 ValueHash; }; struct strip_sacn_addr { - s32 StartUniverse; - s32 StartChannel; + s32 StartUniverse; + s32 StartChannel; }; struct strip_uart_addr { - u8 Channel; - - gs_string ComPort; - // This may not be used based on the value of the parent - // assembly's NetworkPortMode field + u8 Channel; + + gs_string ComPort; + // This may not be used based on the value of the parent + // assembly's NetworkPortMode field }; enum strip_gen_method { - StripGeneration_InterpolatePoints, - StripGeneration_Sequence, - - StripGeneration_Count, + StripGeneration_InterpolatePoints, + StripGeneration_Sequence, + + StripGeneration_Count, }; typedef struct strip_gen_data strip_gen_data; struct strip_gen_interpolate_points { - v3 StartPosition; - v3 EndPosition; - u32 LedCount; + v3 StartPosition; + v3 EndPosition; + u32 LedCount; }; struct strip_gen_sequence { - strip_gen_data* Elements; - u32 ElementsCount; + strip_gen_data* Elements; + u32 ElementsCount; }; struct strip_gen_data { - strip_gen_method Method; - - strip_gen_interpolate_points InterpolatePoints; - strip_gen_sequence Sequence; + strip_gen_method Method; + + strip_gen_interpolate_points InterpolatePoints; + strip_gen_sequence Sequence; }; struct v2_strip { - s32 ControlBoxID; // TODO(Peter): I don't think we need this anymore - - strip_sacn_addr SACNAddr; - strip_uart_addr UARTAddr; - - strip_gen_data GenerationData; - - u32 LedCount; - u32* LedLUT; - - u32 TagsCount; - v2_tag* Tags; + s32 ControlBoxID; // TODO(Peter): I don't think we need this anymore + + strip_sacn_addr SACNAddr; + strip_uart_addr UARTAddr; + + strip_gen_data GenerationData; + + u32 LedCount; + u32* LedLUT; + + u32 TagsCount; + v2_tag* Tags; }; struct led_strip_list { - u32 Count; - u32 CountMax; - u32* StripIndices; + u32 Count; + u32 CountMax; + u32* StripIndices; }; enum network_port_mode { - // This enum defines the scope which contains what network - // port each address should be sent over. - - NetworkPortMode_GlobalPort, - // GlobalPort means that the port is defined in the assembly structure - - NetworkPortMode_PortPerStrip, - // PortPerStrip means that the address stored in the strip structure - // should be used, and each strip might have a different port - - NetworkPortMode_Count, + // This enum defines the scope which contains what network + // port each address should be sent over. + + NetworkPortMode_GlobalPort, + // GlobalPort means that the port is defined in the assembly structure + + NetworkPortMode_PortPerStrip, + // PortPerStrip means that the address stored in the strip structure + // should be used, and each strip might have a different port + + NetworkPortMode_Count, }; struct assembly { - gs_memory_arena Arena; - - u32 AssemblyIndex; - gs_string Name; - gs_string FilePath; - - r32 Scale; - v3 Center; - v3 MinLedPos, MaxLedPos; - s32 LedCountTotal; - u32 LedBufferIndex; - - u32 StripCount; - v2_strip* Strips; - - network_protocol OutputMode; - network_port_mode NetPortMode; - gs_string UARTComPort; + gs_memory_arena Arena; + + u32 AssemblyIndex; + gs_string Name; + gs_string FilePath; + + r32 Scale; + v3 Center; + v3 MinLedPos, MaxLedPos; + s32 LedCountTotal; + u32 LedBufferIndex; + + u32 StripCount; + v2_strip* Strips; + + network_protocol OutputMode; + network_port_mode NetPortMode; + gs_string UARTComPort; }; struct assembly_array { - u32 CountMax; - u32 Count; - assembly* Values; + u32 CountMax; + u32 Count; + assembly* Values; }; typedef pixel led_blend_proc(pixel A, pixel B, u8* UserData); @@ -173,118 +180,118 @@ typedef pixel led_blend_proc(pixel A, pixel B, u8* UserData); internal led_buffer* LedSystemGetBuffer(led_system* System, u32 Index) { - led_buffer* Result = &System->Buffers[Index]; - return Result; + led_buffer* Result = &System->Buffers[Index]; + return Result; } internal void LedBuffer_ClearToBlack(led_buffer* Buffer) { - for (u32 i = 0; i < Buffer->LedCount; i++) - { - Buffer->Colors[i].R = 0; - Buffer->Colors[i].G = 0; - Buffer->Colors[i].B = 0; - } + for (u32 i = 0; i < Buffer->LedCount; i++) + { + Buffer->Colors[i].R = 0; + Buffer->Colors[i].G = 0; + Buffer->Colors[i].B = 0; + } } internal void LedBuffer_Copy(led_buffer From, led_buffer* To) { - DEBUG_TRACK_FUNCTION; - - Assert(From.LedCount == To->LedCount); - u32 LedCount = To->LedCount; - for (u32 i = 0; i < LedCount; i++) - { - To->Colors[i] = From.Colors[i]; - } + DEBUG_TRACK_FUNCTION; + + Assert(From.LedCount == To->LedCount); + u32 LedCount = To->LedCount; + for (u32 i = 0; i < LedCount; i++) + { + To->Colors[i] = From.Colors[i]; + } } internal void LedBuffer_Blend(led_buffer A, led_buffer B, led_buffer* Dest, led_blend_proc* BlendProc, u8* UserData) { - DEBUG_TRACK_FUNCTION; - - Assert(A.LedCount == B.LedCount); - Assert(Dest->LedCount == A.LedCount); - Assert(BlendProc); - - u32 LedCount = Dest->LedCount; - for (u32 i = 0; i < LedCount; i++) - { - pixel PA = A.Colors[i]; - pixel PB = B.Colors[i]; - Dest->Colors[i] = BlendProc(PA, PB, UserData); - } + DEBUG_TRACK_FUNCTION; + + Assert(A.LedCount == B.LedCount); + Assert(Dest->LedCount == A.LedCount); + Assert(BlendProc); + + u32 LedCount = Dest->LedCount; + for (u32 i = 0; i < LedCount; i++) + { + pixel PA = A.Colors[i]; + pixel PB = B.Colors[i]; + Dest->Colors[i] = BlendProc(PA, PB, UserData); + } } internal led_buffer LedBuffer_CreateCopyCleared (led_buffer Buffer, gs_memory_arena* Arena) { - DEBUG_TRACK_FUNCTION; - - led_buffer Result = {}; - Result.LedCount = Buffer.LedCount; - Result.Positions = Buffer.Positions; - Result.Colors = PushArray(Arena, pixel, Buffer.LedCount); - LedBuffer_ClearToBlack(&Result); - return Result; + DEBUG_TRACK_FUNCTION; + + led_buffer Result = {}; + Result.LedCount = Buffer.LedCount; + Result.Positions = Buffer.Positions; + Result.Colors = PushArray(Arena, pixel, Buffer.LedCount); + LedBuffer_ClearToBlack(&Result); + return Result; } internal u32 StripGenData_CountLeds(strip_gen_data Data) { - u32 Result = 0; - - switch (Data.Method) + u32 Result = 0; + + switch (Data.Method) + { + case StripGeneration_InterpolatePoints: { - case StripGeneration_InterpolatePoints: - { - Result += Data.InterpolatePoints.LedCount; - }break; - - case StripGeneration_Sequence: - { - for (u32 i = 0; i < Data.Sequence.ElementsCount; i++) - { - Result += StripGenData_CountLeds(Data.Sequence.Elements[i]); - } - }break; - - InvalidDefaultCase; - } + Result += Data.InterpolatePoints.LedCount; + }break; - return Result; + case StripGeneration_Sequence: + { + for (u32 i = 0; i < Data.Sequence.ElementsCount; i++) + { + Result += StripGenData_CountLeds(Data.Sequence.Elements[i]); + } + }break; + + InvalidDefaultCase; + } + + return Result; } internal bool AssemblyStrip_HasTagValue(v2_strip Strip, u64 NameHash, u64 ValueHash) { - bool Result = false; - for (u32 i = 0; i < Strip.TagsCount; i++) + bool Result = false; + for (u32 i = 0; i < Strip.TagsCount; i++) + { + v2_tag TagAt = Strip.Tags[i]; + if (TagAt.NameHash == NameHash) { - v2_tag TagAt = Strip.Tags[i]; - if (TagAt.NameHash == NameHash) - { - // NOTE(pjs): We can pass an empty string to the Value parameter, - // and it will match all values of Tag - if (ValueHash == 0 || ValueHash == TagAt.ValueHash) - { - Result = true; - break; - } - } + // NOTE(pjs): We can pass an empty string to the Value parameter, + // and it will match all values of Tag + if (ValueHash == 0 || ValueHash == TagAt.ValueHash) + { + Result = true; + break; + } } - return Result; + } + return Result; } internal bool AssemblyStrip_HasTagValueSLOW(v2_strip Strip, char* Name, char* Value) { - u64 NameHash = HashDJB2ToU32(Name); - u64 ValueHash = HashDJB2ToU32(Value); - return AssemblyStrip_HasTagValue(Strip, NameHash, ValueHash); + u64 NameHash = HashDJB2ToU32(Name); + u64 ValueHash = HashDJB2ToU32(Value); + return AssemblyStrip_HasTagValue(Strip, NameHash, ValueHash); } #define FOLDHAUS_ASSEMBLY_H diff --git a/src/app/engine/foldhaus_addressed_data.h b/src/app/engine/foldhaus_addressed_data.h index 8339b6e..e2db91e 100644 --- a/src/app/engine/foldhaus_addressed_data.h +++ b/src/app/engine/foldhaus_addressed_data.h @@ -11,97 +11,97 @@ enum data_buffer_address_type { - AddressType_NetworkIP, - AddressType_ComPort, - AddressType_Invalid, + AddressType_NetworkIP, + AddressType_ComPort, + AddressType_Invalid, }; struct addressed_data_buffer { - union + union + { + struct { - struct - { - u8* Memory; - u32 MemorySize; - }; - gs_data Data; + u8* Memory; + u32 MemorySize; }; - - data_buffer_address_type AddressType; - - // IP Address - platform_socket_handle SendSocket; - u32 V4SendAddress; - u32 SendPort; - - // COM - gs_const_string ComPort; - - addressed_data_buffer* Next; + gs_data Data; + }; + + data_buffer_address_type AddressType; + + // IP Address + platform_socket_handle SendSocket; + u32 V4SendAddress; + u32 SendPort; + + // COM + gs_const_string ComPort; + + addressed_data_buffer* Next; }; struct addressed_data_buffer_list { - gs_memory_arena* Arena; - addressed_data_buffer* Root; - addressed_data_buffer* Head; + gs_memory_arena* Arena; + addressed_data_buffer* Root; + addressed_data_buffer* Head; }; internal void AddressedDataBufferList_Clear(addressed_data_buffer_list* List) { - List->Root = 0; - List->Head = 0; - ClearArena(List->Arena); + List->Root = 0; + List->Head = 0; + MemoryArenaClear(List->Arena); } internal addressed_data_buffer* AddressedDataBufferList_PushEmpty(addressed_data_buffer_list* List) { - addressed_data_buffer* Result = PushStruct(List->Arena, addressed_data_buffer); - *Result = {0}; - Result->Next = 0; - Result->MemorySize = 0; - Result->Memory = 0; - - SLLPushOrInit(List->Root, List->Head, Result); - - return Result; + addressed_data_buffer* Result = PushStruct(List->Arena, addressed_data_buffer); + *Result = {0}; + Result->Next = 0; + Result->MemorySize = 0; + Result->Memory = 0; + + SLLPushOrInit(List->Root, List->Head, Result); + + return Result; } internal addressed_data_buffer* AddressedDataBufferList_Push(addressed_data_buffer_list* List, u32 BufferSize) { - addressed_data_buffer* Result = AddressedDataBufferList_PushEmpty(List); - Result->MemorySize = BufferSize; - Result->Memory = PushArray(List->Arena, u8, Result->MemorySize); - return Result; + addressed_data_buffer* Result = AddressedDataBufferList_PushEmpty(List); + Result->MemorySize = BufferSize; + Result->Memory = PushArray(List->Arena, u8, Result->MemorySize); + return Result; } internal void AddressedDataBuffer_SetNetworkAddress(addressed_data_buffer* Buffer, platform_socket_handle SendSocket, u32 V4SendAddress, u32 SendPort) { - Buffer->AddressType = AddressType_NetworkIP; - Buffer->SendSocket = SendSocket; - Buffer->V4SendAddress = V4SendAddress; - Buffer->SendPort = SendPort; + Buffer->AddressType = AddressType_NetworkIP; + Buffer->SendSocket = SendSocket; + Buffer->V4SendAddress = V4SendAddress; + Buffer->SendPort = SendPort; } internal void AddressedDataBuffer_SetCOMPort(addressed_data_buffer* Buffer, gs_const_string ComPort) { - Buffer->AddressType = AddressType_ComPort; - Buffer->ComPort = ComPort; + Buffer->AddressType = AddressType_ComPort; + Buffer->ComPort = ComPort; } internal addressed_data_buffer_list AddressedDataBufferList_Create(gs_thread_context TC) { - addressed_data_buffer_list Result = {}; - Result.Arena = AllocatorAllocStruct(TC.Allocator, gs_memory_arena); - *Result.Arena = CreateMemoryArena(TC.Allocator, "Addressed Data Buffer List Arena"); - return Result; + addressed_data_buffer_list Result = {}; + Result.Arena = AllocStruct(TC.Allocator, gs_memory_arena, "Addressed Data"); + *Result.Arena = MemoryArenaCreate(KB(256), Bytes(8), TC.Allocator, 0, 0, "Addressed Data Buffer List Arena"); + return Result; } #define FOLDHAUS_ADDRESSED_DATA_H diff --git a/src/app/engine/foldhaus_log.h b/src/app/engine/foldhaus_log.h index 8472701..3d538b2 100644 --- a/src/app/engine/foldhaus_log.h +++ b/src/app/engine/foldhaus_log.h @@ -5,79 +5,84 @@ enum log_entry_type { - LogEntry_Message, - LogEntry_Error, + LogEntry_Message, + LogEntry_Error, }; struct log_entry { - log_entry_type Type; - gs_string String; + log_entry_type Type; + gs_string String; }; struct log_buffer { - gs_allocator Allocator; - - u64 EntriesCount; - u64 NextEntry; - log_entry* Entries; + gs_memory_arena* Arena; + + u64 EntriesCount; + u64 NextEntry; + log_entry* Entries; }; struct log_buffer_iter { - log_buffer* Buffer; - u64 Start; - u64 IndexAt; - log_entry* At; + log_buffer* Buffer; + u64 Start; + u64 IndexAt; + log_entry* At; }; internal log_buffer -Log_Init(gs_allocator Allocator, u64 Count) +Log_Init(gs_memory_arena* A, u64 Count) { - log_buffer Result = {}; - Result.Allocator = Allocator; - Result.EntriesCount = Count; - Result.Entries = AllocatorAllocArray(Allocator, log_entry, Result.EntriesCount); - - for (u32 i = 0; i < Result.EntriesCount; i++) - { - Result.Entries[i].String = AllocatorAllocString(Allocator, 512); - } - - return Result; + log_buffer Result = {}; + Result.Arena = A; + Result.EntriesCount = Count; + Result.Entries = PushArray(A, log_entry, Result.EntriesCount); + + u64 LogStringLength = 512; + u64 LogStringBufferSize = LogStringLength * Result.EntriesCount; + char* LogStringBuffer = PushArray(A, char, LogStringBufferSize); + char* LogStringBufferAt = LogStringBuffer; + for (u32 i = 0; i < Result.EntriesCount; i++) + { + Result.Entries[i].String = MakeString(LogStringBufferAt, 0, LogStringLength); + LogStringBufferAt += LogStringLength; + } + + return Result; } internal u64 Log_GetNextIndex(log_buffer Log, u64 At) { - u64 Result = At + 1; - if (Result >= Log.EntriesCount) - { - Result = 0; - } - return Result; + u64 Result = At + 1; + if (Result >= Log.EntriesCount) + { + Result = 0; + } + return Result; } internal log_entry* Log_TakeNextEntry(log_buffer* Log) { - log_entry* Result = Log->Entries + Log->NextEntry; - Log->NextEntry = Log_GetNextIndex(*Log, Log->NextEntry); - return Result; + log_entry* Result = Log->Entries + Log->NextEntry; + Log->NextEntry = Log_GetNextIndex(*Log, Log->NextEntry); + return Result; } internal void Log_PrintFVarArgs(log_buffer* Log, log_entry_type Type, char* Format, va_list Args) { - log_entry* NextEntry = Log_TakeNextEntry(Log); - NextEntry->String.Length = 0; - NextEntry->Type = Type; - PrintFArgsList(&NextEntry->String, Format, Args); - NullTerminate(&NextEntry->String); - + log_entry* NextEntry = Log_TakeNextEntry(Log); + NextEntry->String.Length = 0; + NextEntry->Type = Type; + PrintFArgsList(&NextEntry->String, Format, Args); + NullTerminate(&NextEntry->String); + #if DEBUG - OutputDebugStringA(NextEntry->String.Str); + OutputDebugStringA(NextEntry->String.Str); #endif } @@ -86,36 +91,36 @@ Log_PrintFVarArgs(log_buffer* Log, log_entry_type Type, char* Format, va_list Ar internal void Log_PrintF(log_buffer* Log, log_entry_type Type, char* Format, ...) { - va_list Args; - va_start(Args, Format); - Log_PrintFVarArgs(Log, Type, Format, Args); - va_end(Args); + va_list Args; + va_start(Args, Format); + Log_PrintFVarArgs(Log, Type, Format, Args); + va_end(Args); } internal log_buffer_iter Log_GetIter(log_buffer* Buffer) { - log_buffer_iter Result = {}; - Result.Buffer = Buffer; - Result.Start = Buffer->NextEntry; - Result.IndexAt = Result.Start; - Result.At = Result.Buffer->Entries + Result.IndexAt; - return Result; + log_buffer_iter Result = {}; + Result.Buffer = Buffer; + Result.Start = Buffer->NextEntry; + Result.IndexAt = Result.Start; + Result.At = Result.Buffer->Entries + Result.IndexAt; + return Result; } internal bool LogIter_CanAdvance(log_buffer_iter Iter) { - u64 Next = Log_GetNextIndex(*Iter.Buffer, Iter.IndexAt); - bool Result = Next != Iter.Start; - return Result; + u64 Next = Log_GetNextIndex(*Iter.Buffer, Iter.IndexAt); + bool Result = Next != Iter.Start; + return Result; } internal void LogIter_Advance(log_buffer_iter* Iter) { - Iter->IndexAt = Log_GetNextIndex(*Iter->Buffer, Iter->IndexAt); - Iter->At = Iter->Buffer->Entries + Iter->IndexAt; + Iter->IndexAt = Log_GetNextIndex(*Iter->Buffer, Iter->IndexAt); + Iter->At = Iter->Buffer->Entries + Iter->IndexAt; } #endif //FOLDHAUS_LOG_H diff --git a/src/app/engine/uart/foldhaus_uart.cpp b/src/app/engine/uart/foldhaus_uart.cpp index f18360f..b6ea3fb 100644 --- a/src/app/engine/uart/foldhaus_uart.cpp +++ b/src/app/engine/uart/foldhaus_uart.cpp @@ -9,163 +9,163 @@ internal void UART_SetChannelBuffer_Create(gs_memory_cursor* WriteCursor, uart_channel ChannelSettings, v2_strip Strip, led_buffer LedBuffer) { - // NOTE(pjs): This is just here because the information is duplicated and I want to be sure - // to catch the error where they are different - Assert(ChannelSettings.PixelsCount == Strip.LedCount); + // NOTE(pjs): This is just here because the information is duplicated and I want to be sure + // to catch the error where they are different + Assert(ChannelSettings.PixelsCount == Strip.LedCount); + + uart_header* Header = MemoryCursorPushStruct(WriteCursor, uart_header); + UART_FillHeader(Header, Strip.UARTAddr.Channel, UART_SET_CHANNEL_WS2812); + + uart_channel* Channel = MemoryCursorPushStruct(WriteCursor, uart_channel); + *Channel = ChannelSettings; + + for (u32 i = 0; i < Channel->PixelsCount; i++) + { + u32 LedIndex = Strip.LedLUT[i]; + pixel Color = LedBuffer.Colors[LedIndex]; - uart_header* Header = PushStructOnCursor(WriteCursor, uart_header); - UART_FillHeader(Header, Strip.UARTAddr.Channel, UART_SET_CHANNEL_WS2812); + u8* OutputPixel = MemoryCursorPushArray(WriteCursor, u8, 3); - uart_channel* Channel = PushStructOnCursor(WriteCursor, uart_channel); - *Channel = ChannelSettings; - - for (u32 i = 0; i < Channel->PixelsCount; i++) - { - u32 LedIndex = Strip.LedLUT[i]; - pixel Color = LedBuffer.Colors[LedIndex]; - - u8* OutputPixel = PushArrayOnCursor(WriteCursor, u8, 3); - - // TODO(pjs): Use the Output mask + // TODO(pjs): Use the Output mask #if 1 - OutputPixel[0] = Color.R; - OutputPixel[1] = Color.G; - OutputPixel[2] = Color.B; + OutputPixel[0] = Color.R; + OutputPixel[1] = Color.G; + OutputPixel[2] = Color.B; #else - OutputPixel[0] = 255; - OutputPixel[1] = 255; - OutputPixel[2] = 255; + OutputPixel[0] = 255; + OutputPixel[1] = 255; + OutputPixel[2] = 255; #endif - if (Channel->ElementsCount == 4) - { - // TODO(pjs): Calculate white from the RGB components? - // Generally we just need a good way to handle the white channel, - // both in the renderer and in output - - //OutputPixel[Channel->WhiteIndex] = Color.W; - } + if (Channel->ElementsCount == 4) + { + // TODO(pjs): Calculate white from the RGB components? + // Generally we just need a good way to handle the white channel, + // both in the renderer and in output + + //OutputPixel[Channel->WhiteIndex] = Color.W; } - - uart_footer* Footer = PushStructOnCursor(WriteCursor, uart_footer); - UART_FillFooter(Footer, (u8*)Header); + } + + uart_footer* Footer = MemoryCursorPushStruct(WriteCursor, uart_footer); + UART_FillFooter(Footer, (u8*)Header); } internal void UART_DrawAll_Create(gs_memory_cursor* WriteCursor) { - uart_header* Header = PushStructOnCursor(WriteCursor, uart_header); - UART_FillHeader(Header, 1, UART_DRAW_ALL); - - uart_footer* Footer = PushStructOnCursor(WriteCursor, uart_footer); - UART_FillFooter(Footer, (u8*)Header); + uart_header* Header = MemoryCursorPushStruct(WriteCursor, uart_header); + UART_FillHeader(Header, 1, UART_DRAW_ALL); + + uart_footer* Footer = MemoryCursorPushStruct(WriteCursor, uart_footer); + UART_FillFooter(Footer, (u8*)Header); } internal void UART_BuildOutputData(addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem, gs_memory_arena* Transient) { - uart_channel ChannelSettings = {0}; - ChannelSettings.ElementsCount = 3; - ChannelSettings.ColorPackingOrder = 36; + uart_channel ChannelSettings = {0}; + ChannelSettings.ElementsCount = 3; + ChannelSettings.ColorPackingOrder = 36; + + // NOTE(pjs): This is the minimum size of every UART message. SetChannelBuffer messages will + // be bigger than this, but their size is based on the number of pixels in each channel + u32 MessageBaseSize = UART_MESSAGE_MIN_SIZE; + + for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++) + { + assembly Assembly = Assemblies.Values[AssemblyIdx]; + led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); - // NOTE(pjs): This is the minimum size of every UART message. SetChannelBuffer messages will - // be bigger than this, but their size is based on the number of pixels in each channel - u32 MessageBaseSize = UART_MESSAGE_MIN_SIZE; - - for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++) + struct strips_to_data_buffer { - assembly Assembly = Assemblies.Values[AssemblyIdx]; - led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); - - struct strips_to_data_buffer + gs_const_string ComPort; + + u32* StripIndices; + u32 StripIndicesCount; + u32 StripIndicesCountMax; + + u64 LedCount; + + u8** ChannelsStart; + + strips_to_data_buffer* Next; + }; + + u32 BuffersNeededCount = 0; + strips_to_data_buffer* BuffersNeededHead = 0; + strips_to_data_buffer* BuffersNeededTail = 0; + + for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++) + { + v2_strip StripAt = Assembly.Strips[StripIdx]; + + // If there is a buffer for this com port already created + // we use that + strips_to_data_buffer* BufferSelected = 0; + for (strips_to_data_buffer* At = BuffersNeededHead; + At!= 0; + At = At->Next) + { + if (StringsEqual(At->ComPort, StripAt.UARTAddr.ComPort.ConstString)) { - gs_const_string ComPort; - - u32* StripIndices; - u32 StripIndicesCount; - u32 StripIndicesCountMax; - - u64 LedCount; - - u8** ChannelsStart; - - strips_to_data_buffer* Next; - }; - - u32 BuffersNeededCount = 0; - strips_to_data_buffer* BuffersNeededHead = 0; - strips_to_data_buffer* BuffersNeededTail = 0; - - for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++) - { - v2_strip StripAt = Assembly.Strips[StripIdx]; - - // If there is a buffer for this com port already created - // we use that - strips_to_data_buffer* BufferSelected = 0; - for (strips_to_data_buffer* At = BuffersNeededHead; - At!= 0; - At = At->Next) - { - if (StringsEqual(At->ComPort, StripAt.UARTAddr.ComPort.ConstString)) - { - BufferSelected = At; - break; - } - } - - // if no existing buffer for this com port - // create a new one - if (!BufferSelected) - { - BufferSelected = PushStruct(Transient, strips_to_data_buffer); - *BufferSelected = {}; - BufferSelected->ComPort = StripAt.UARTAddr.ComPort.ConstString; - // we don't know at this point how many indices per - // com port so just make enough room to fit all the strips - // if necessary - BufferSelected->StripIndicesCountMax = Assembly.StripCount; - BufferSelected->StripIndices = PushArray(Transient, u32, BufferSelected->StripIndicesCountMax); - BufferSelected->LedCount = 0; - BufferSelected->Next = 0; - - SLLPushOrInit(BuffersNeededHead, BuffersNeededTail, BufferSelected); - BuffersNeededCount += 1; - } - - Assert(BufferSelected->StripIndicesCount < BufferSelected->StripIndicesCountMax); - u32 Index = BufferSelected->StripIndicesCount++; - BufferSelected->StripIndices[Index] = StripIdx; - BufferSelected->LedCount += StripAt.LedCount; + BufferSelected = At; + break; } + } + + // if no existing buffer for this com port + // create a new one + if (!BufferSelected) + { + BufferSelected = PushStruct(Transient, strips_to_data_buffer); + *BufferSelected = {}; + BufferSelected->ComPort = StripAt.UARTAddr.ComPort.ConstString; + // we don't know at this point how many indices per + // com port so just make enough room to fit all the strips + // if necessary + BufferSelected->StripIndicesCountMax = Assembly.StripCount; + BufferSelected->StripIndices = PushArray(Transient, u32, BufferSelected->StripIndicesCountMax); + BufferSelected->LedCount = 0; + BufferSelected->Next = 0; - for (strips_to_data_buffer* At = BuffersNeededHead; - At!= 0; - At = At->Next) - { - u32 TotalBufferSize = MessageBaseSize * Assembly.StripCount; // SetChannelBuffer messages - TotalBufferSize += MessageBaseSize; // DrawAll message - TotalBufferSize += ChannelSettings.ElementsCount * At->LedCount; // pixels * channels per pixel - - At->ChannelsStart = PushArray(Transient, u8*, At->StripIndicesCount); - - addressed_data_buffer* Buffer = AddressedDataBufferList_Push(Output, TotalBufferSize); - gs_const_string ComPort = At->ComPort; - AddressedDataBuffer_SetCOMPort(Buffer, ComPort); - - gs_memory_cursor WriteCursor = CreateMemoryCursor(Buffer->Data); - - for (u32 i = 0; i < At->StripIndicesCount; i++) - { - u32 StripIdx = At->StripIndices[i]; - v2_strip StripAt = Assembly.Strips[StripIdx]; - - ChannelSettings.PixelsCount = StripAt.LedCount; - UART_SetChannelBuffer_Create(&WriteCursor, ChannelSettings, StripAt, *LedBuffer); - } - - UART_DrawAll_Create(&WriteCursor); - } + SLLPushOrInit(BuffersNeededHead, BuffersNeededTail, BufferSelected); + BuffersNeededCount += 1; + } + + Assert(BufferSelected->StripIndicesCount < BufferSelected->StripIndicesCountMax); + u32 Index = BufferSelected->StripIndicesCount++; + BufferSelected->StripIndices[Index] = StripIdx; + BufferSelected->LedCount += StripAt.LedCount; } + + for (strips_to_data_buffer* At = BuffersNeededHead; + At!= 0; + At = At->Next) + { + u32 TotalBufferSize = MessageBaseSize * Assembly.StripCount; // SetChannelBuffer messages + TotalBufferSize += MessageBaseSize; // DrawAll message + TotalBufferSize += ChannelSettings.ElementsCount * At->LedCount; // pixels * channels per pixel + + At->ChannelsStart = PushArray(Transient, u8*, At->StripIndicesCount); + + addressed_data_buffer* Buffer = AddressedDataBufferList_Push(Output, TotalBufferSize); + gs_const_string ComPort = At->ComPort; + AddressedDataBuffer_SetCOMPort(Buffer, ComPort); + + gs_memory_cursor WriteCursor = MemoryCursorCreate(Buffer->Memory, Buffer->MemorySize); + + for (u32 i = 0; i < At->StripIndicesCount; i++) + { + u32 StripIdx = At->StripIndices[i]; + v2_strip StripAt = Assembly.Strips[StripIdx]; + + ChannelSettings.PixelsCount = StripAt.LedCount; + UART_SetChannelBuffer_Create(&WriteCursor, ChannelSettings, StripAt, *LedBuffer); + } + + UART_DrawAll_Create(&WriteCursor); + } + } } diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 9b37512..94d8073 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -10,190 +10,190 @@ RELOAD_STATIC_DATA(ReloadStaticData) { - GlobalDebugServices = DebugServices; - GlobalLogBuffer = LogBuffer; - if (AppReady) + GlobalDebugServices = DebugServices; + GlobalLogBuffer = LogBuffer; + if (AppReady) + { + app_state* State = (app_state*)Context.MemoryBase; + State->PanelSystem.PanelDefs = GlobalPanelDefs; + State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount; + + gs_data UserData = State->UserSpaceDesc.UserData; + State->UserSpaceDesc = BlumenLumen_UserSpaceCreate(); + if (UserData.Memory && !State->UserSpaceDesc.UserData.Memory) { - app_state* State = (app_state*)Context.MemoryBase; - State->PanelSystem.PanelDefs = GlobalPanelDefs; - State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount; - - gs_data UserData = State->UserSpaceDesc.UserData; - State->UserSpaceDesc = BlumenLumen_UserSpaceCreate(); - if (UserData.Memory && !State->UserSpaceDesc.UserData.Memory) - { - State->UserSpaceDesc.UserData = UserData; - } - US_LoadPatterns(&State->UserSpaceDesc, State, Context); + State->UserSpaceDesc.UserData = UserData; } + US_LoadPatterns(&State->UserSpaceDesc, State, Context); + } } INITIALIZE_APPLICATION(InitializeApplication) { - Context->MemorySize = sizeof(app_state); - Context->MemoryBase = AllocatorAlloc(Context->ThreadContext.Allocator, Context->MemorySize).Memory; - app_state* State = (app_state*)Context->MemoryBase; - *State = {}; + Context->MemorySize = sizeof(app_state); + Context->MemoryBase = Alloc(Context->ThreadContext.Allocator, Context->MemorySize, "Memory Base"); + app_state* State = (app_state*)Context->MemoryBase; + *State = {}; + + State->Permanent = MemoryArenaCreate(MB(4), Bytes(8), Context->ThreadContext.Allocator,0, 0, "Permanent"); + State->Transient = Context->ThreadContext.Transient; + State->Assemblies = AssemblyArray_Create(8, &State->Permanent); + + State->CommandQueue = CommandQueue_Create(&State->Permanent, 32); + + animation_system_desc AnimSysDesc = {}; + AnimSysDesc.Storage = &State->Permanent; + AnimSysDesc.AnimArrayCount = 32; + AnimSysDesc.SecondsPerFrame = 1.0f / 24.0f; + State->AnimationSystem = AnimationSystem_Init(AnimSysDesc); + + if (!Context->Headless) + { + interface_config IConfig = {0}; + IConfig.FontSize = 14; + IConfig.PanelBG = v4{ .3f, .3f, .3f, 1.f }; + IConfig.ButtonColor_Inactive = BlackV4; + IConfig.ButtonColor_Active = v4{ .1f, .1f, .1f, 1.f }; + IConfig.ButtonColor_Selected = v4{ .3f, .3f, .3f, 1.f }; + IConfig.TextColor = WhiteV4; + IConfig.ListBGColors[0] = v4{ .16f, .16f, .16f, 1.f }; + IConfig.ListBGColors[1] = v4{ .18f, .18f, .18f, 1.f }; + IConfig.ListBGHover = v4{ .22f, .22f, .22f, 1.f }; + IConfig.ListBGSelected = v4{ .44f, .44f, .44f, 1.f }; + IConfig.Margin = v2{5, 5}; + State->Interface = ui_InterfaceCreate(*Context, IConfig, &State->Permanent); - State->Permanent = CreateMemoryArena(Context->ThreadContext.Allocator, "Permanent"); - State->Transient = Context->ThreadContext.Transient; - State->Assemblies = AssemblyArray_Create(8, &State->Permanent); + PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); - State->CommandQueue = CommandQueue_Create(&State->Permanent, 32); + } + + State->SACN = SACN_Initialize(*Context); + + State->LedSystem = LedSystem_Create(Context->ThreadContext.Allocator, 128); + State->AssemblyDebugState = AssemblyDebug_Create(&State->Permanent); + State->AssemblyDebugState.Brightness = 255; + State->AssemblyDebugState.Override = ADS_Override_None; + + State->Modes = OperationModeSystemInit(&State->Permanent, Context->ThreadContext); + + ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, true); + US_CustomInit(&State->UserSpaceDesc, State, *Context); + + if (!Context->Headless) + { + // NOTE(pjs): This just sets up the default panel layout + panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, *Context); + SplitPanel(RootPanel, .25f, PanelSplit_Horizontal, &State->PanelSystem, State, *Context); - animation_system_desc AnimSysDesc = {}; - AnimSysDesc.Storage = &State->Permanent; - AnimSysDesc.AnimArrayCount = 32; - AnimSysDesc.SecondsPerFrame = 1.0f / 24.0f; - State->AnimationSystem = AnimationSystem_Init(AnimSysDesc); + panel* AnimPanel = RootPanel->Bottom; + Panel_SetType(AnimPanel, &State->PanelSystem, PanelType_AnimationTimeline, State, *Context); - if (!Context->Headless) - { - interface_config IConfig = {0}; - IConfig.FontSize = 14; - IConfig.PanelBG = v4{ .3f, .3f, .3f, 1.f }; - IConfig.ButtonColor_Inactive = BlackV4; - IConfig.ButtonColor_Active = v4{ .1f, .1f, .1f, 1.f }; - IConfig.ButtonColor_Selected = v4{ .3f, .3f, .3f, 1.f }; - IConfig.TextColor = WhiteV4; - IConfig.ListBGColors[0] = v4{ .16f, .16f, .16f, 1.f }; - IConfig.ListBGColors[1] = v4{ .18f, .18f, .18f, 1.f }; - IConfig.ListBGHover = v4{ .22f, .22f, .22f, 1.f }; - IConfig.ListBGSelected = v4{ .44f, .44f, .44f, 1.f }; - IConfig.Margin = v2{5, 5}; - State->Interface = ui_InterfaceCreate(*Context, IConfig, &State->Permanent); - - PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); - - } + panel* TopPanel = RootPanel->Top; + SplitPanel(TopPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context); - State->SACN = SACN_Initialize(*Context); + panel* LeftPanel = TopPanel->Left; + SplitPanel(LeftPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context); - State->LedSystem = LedSystem_Create(Context->ThreadContext.Allocator, 128); - State->AssemblyDebugState = AssemblyDebug_Create(&State->Permanent); - State->AssemblyDebugState.Brightness = 255; - State->AssemblyDebugState.Override = ADS_Override_None; + panel* Profiler = LeftPanel->Right; + Panel_SetType(Profiler, &State->PanelSystem, PanelType_MessageLog, State, *Context); - State->Modes = OperationModeSystemInit(&State->Permanent, Context->ThreadContext); + panel* Hierarchy = LeftPanel->Left; + Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, *Context); - ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, true); - US_CustomInit(&State->UserSpaceDesc, State, *Context); - - if (!Context->Headless) - { - // NOTE(pjs): This just sets up the default panel layout - panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, *Context); - SplitPanel(RootPanel, .25f, PanelSplit_Horizontal, &State->PanelSystem, State, *Context); - - panel* AnimPanel = RootPanel->Bottom; - Panel_SetType(AnimPanel, &State->PanelSystem, PanelType_AnimationTimeline, State, *Context); - - panel* TopPanel = RootPanel->Top; - SplitPanel(TopPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context); - - panel* LeftPanel = TopPanel->Left; - SplitPanel(LeftPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context); - - panel* Profiler = LeftPanel->Right; - Panel_SetType(Profiler, &State->PanelSystem, PanelType_MessageLog, State, *Context); - - panel* Hierarchy = LeftPanel->Left; - Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, *Context); - - } - - State->RunEditor = !Context->Headless; + } + + State->RunEditor = !Context->Headless; } internal void BuildAssemblyData (app_state* State, context Context, addressed_data_buffer_list* OutputData) { - + #define SEND_DATA #ifdef SEND_DATA - // NOTE(pjs): Building data buffers to be sent out to the sculpture - // This array is used on the platform side to actually send the information - assembly_array SACNAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaSACN, State->Transient); - assembly_array UARTAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaUART, State->Transient); - SACN_BuildOutputData(&State->SACN, OutputData, SACNAssemblies, &State->LedSystem); - UART_BuildOutputData(OutputData, UARTAssemblies, &State->LedSystem, State->Transient); + // NOTE(pjs): Building data buffers to be sent out to the sculpture + // This array is used on the platform side to actually send the information + assembly_array SACNAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaSACN, State->Transient); + assembly_array UARTAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaUART, State->Transient); + SACN_BuildOutputData(&State->SACN, OutputData, SACNAssemblies, &State->LedSystem); + UART_BuildOutputData(OutputData, UARTAssemblies, &State->LedSystem, State->Transient); #endif } UPDATE_AND_RENDER(UpdateAndRender) { - DEBUG_TRACK_FUNCTION; - app_state* State = (app_state*)Context->MemoryBase; - - // NOTE(Peter): We do this at the beginning because all the render commands are stored in Transient, - // and need to persist beyond the end of the UpdateAndRender call. In the release version, we won't - // zero the Transient arena when we clear it so it wouldn't be a problem, but it is technically - // incorrect to clear the arena, and then access the memory later. - ClearArena(State->Transient); + DEBUG_TRACK_FUNCTION; + app_state* State = (app_state*)Context->MemoryBase; + + // NOTE(Peter): We do this at the beginning because all the render commands are stored in Transient, + // and need to persist beyond the end of the UpdateAndRender call. In the release version, we won't + // zero the Transient arena when we clear it so it wouldn't be a problem, but it is technically + // incorrect to clear the arena, and then access the memory later. + MemoryArenaClear(State->Transient); + Assert(State->UserSpaceDesc.UserData.Memory != 0); + + if (State->RunEditor) + { + Editor_Update(State, Context, InputQueue); + } + + AnimationSystem_Update(&State->AnimationSystem, Context->DeltaTime); + if (AnimationSystem_NeedsRender(State->AnimationSystem)) + { Assert(State->UserSpaceDesc.UserData.Memory != 0); - - if (State->RunEditor) + AnimationSystem_RenderToLedBuffers(&State->AnimationSystem, + State->Assemblies, + &State->LedSystem, + State->Patterns, + State->Transient, + *Context, + State->UserSpaceDesc.UserData.Memory); + } + + Assert(State->UserSpaceDesc.UserData.Memory != 0); + US_CustomUpdate(&State->UserSpaceDesc, State, Context); + Assert(State->UserSpaceDesc.UserData.Memory != 0); + + AssemblyDebug_OverrideOutput(State->AssemblyDebugState, + State->Assemblies, + State->LedSystem); + + if (State->RunEditor) + { + Editor_Render(State, Context, RenderBuffer); + } + ResetWorkQueue(Context->GeneralWorkQueue); + + Assert(State->UserSpaceDesc.UserData.Memory != 0); + BuildAssemblyData(State, *Context, OutputData); + + // NOTE(PS): We introduced this in order to test some things on the + // blumen lumen circuit boards, to see if they were getting out + // of sync + if (State->SendEmptyPackets) { + for (addressed_data_buffer* At = OutputData->Root; + At != 0; + At = At->Next) { - Editor_Update(State, Context, InputQueue); - } - - AnimationSystem_Update(&State->AnimationSystem, Context->DeltaTime); - if (AnimationSystem_NeedsRender(State->AnimationSystem)) - { - Assert(State->UserSpaceDesc.UserData.Memory != 0); - AnimationSystem_RenderToLedBuffers(&State->AnimationSystem, - State->Assemblies, - &State->LedSystem, - State->Patterns, - State->Transient, - *Context, - State->UserSpaceDesc.UserData.Memory); - } - - Assert(State->UserSpaceDesc.UserData.Memory != 0); - US_CustomUpdate(&State->UserSpaceDesc, State, Context); - Assert(State->UserSpaceDesc.UserData.Memory != 0); - - AssemblyDebug_OverrideOutput(State->AssemblyDebugState, - State->Assemblies, - State->LedSystem); - - if (State->RunEditor) - { - Editor_Render(State, Context, RenderBuffer); - } - ResetWorkQueue(Context->GeneralWorkQueue); - - Assert(State->UserSpaceDesc.UserData.Memory != 0); - BuildAssemblyData(State, *Context, OutputData); - - // NOTE(PS): We introduced this in order to test some things on the - // blumen lumen circuit boards, to see if they were getting out - // of sync - if (State->SendEmptyPackets) { - for (addressed_data_buffer* At = OutputData->Root; - At != 0; - At = At->Next) - { - ZeroMemoryBlock(At->Memory, At->MemorySize); - } + ZeroMemoryBlock(At->Memory, At->MemorySize); } + } } CLEANUP_APPLICATION(CleanupApplication) { - app_state* State = (app_state*)Context.MemoryBase; - - for (u32 i = 0; i < State->Assemblies.Count; i++) - { - assembly Assembly = State->Assemblies.Values[i]; - led_buffer LedBuffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; - AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0}); - } - BuildAssemblyData(State, Context, OutputData); - - US_CustomCleanup(&State->UserSpaceDesc, State, Context); - SACN_Cleanup(&State->SACN, Context); + app_state* State = (app_state*)Context.MemoryBase; + + for (u32 i = 0; i < State->Assemblies.Count; i++) + { + assembly Assembly = State->Assemblies.Values[i]; + led_buffer LedBuffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0}); + } + BuildAssemblyData(State, Context, OutputData); + + US_CustomCleanup(&State->UserSpaceDesc, State, Context); + SACN_Cleanup(&State->SACN, Context); } #define FOLDHAUS_APP_CPP diff --git a/src/app/foldhaus_debug.h b/src/app/foldhaus_debug.h index 00a494c..8a66201 100644 --- a/src/app/foldhaus_debug.h +++ b/src/app/foldhaus_debug.h @@ -11,71 +11,71 @@ #define SCOPE_NAME_LENGTH 256 struct scope_record { - u32 NameHash; - s64 StartCycles; - s64 EndCycles; - s32 CallDepth; + u32 NameHash; + s64 StartCycles; + s64 EndCycles; + s32 CallDepth; }; struct collated_scope_record { - u32 NameHash; - s64 TotalCycles; - s32 CallCount; - - r32 PercentFrameTime; - r32 TotalSeconds; - - r32 AverageSecondsPerCall; + u32 NameHash; + s64 TotalCycles; + s32 CallCount; + + r32 PercentFrameTime; + r32 TotalSeconds; + + r32 AverageSecondsPerCall; }; #define SCOPE_NAME_BUFFER_LENGTH 128 struct scope_name { - u32 Hash; - gs_string Name; - char Buffer[SCOPE_NAME_BUFFER_LENGTH]; + u32 Hash; + gs_string Name; + char Buffer[SCOPE_NAME_BUFFER_LENGTH]; }; struct debug_scope_record_list { - s32 ThreadId; - s32 Max; - s32 Count; - scope_record* Calls; - - s32 CurrentScopeCallDepth; + s32 ThreadId; + s32 Max; + s32 Count; + scope_record* Calls; + + s32 CurrentScopeCallDepth; }; #define DEBUG_FRAME_GROW_SIZE 8102 struct debug_frame { - s64 FrameStartCycles; - s64 FrameEndCycles; - - s32 ScopeNamesMax; - s32 ScopeNamesCount; - scope_name* ScopeNamesHash; - - s32 ThreadCount; - debug_scope_record_list* ThreadCalls; - - s32 CollatedScopesMax; - collated_scope_record* CollatedScopes; + s64 FrameStartCycles; + s64 FrameEndCycles; + + s32 ScopeNamesMax; + s32 ScopeNamesCount; + scope_name* ScopeNamesHash; + + s32 ThreadCount; + debug_scope_record_list* ThreadCalls; + + s32 CollatedScopesMax; + collated_scope_record* CollatedScopes; }; enum debug_ui_view { - DebugUI_Profiler, - DebugUI_ScopeList, - DebugUI_MemoryView, - - DebugUI_Count, + DebugUI_Profiler, + DebugUI_ScopeList, + DebugUI_MemoryView, + + DebugUI_Count, }; struct debug_interface { - s32 FrameView; + s32 FrameView; }; typedef s32 debug_get_thread_id(); @@ -86,18 +86,18 @@ typedef u8* debug_realloc(u8* Memory, s32 OldSize, s32 NewSize); #define HISTOGRAM_DEPTH 10 struct debug_histogram_entry { - char ScopeName_[SCOPE_NAME_LENGTH]; - gs_string ScopeName; - - u32 PerFrame_Cycles[HISTOGRAM_DEPTH]; - u32 PerFrame_CallCount[HISTOGRAM_DEPTH]; - s32 CurrentFrame; - - // NOTE(Peter): Cached Values, recalculated ever frame - u32 Average_Cycles; - u32 Average_CallCount; - u32 Total_Cycles; - u32 Total_CallCount; + char ScopeName_[SCOPE_NAME_LENGTH]; + gs_string ScopeName; + + u32 PerFrame_Cycles[HISTOGRAM_DEPTH]; + u32 PerFrame_CallCount[HISTOGRAM_DEPTH]; + s32 CurrentFrame; + + // NOTE(Peter): Cached Values, recalculated ever frame + u32 Average_Cycles; + u32 Average_CallCount; + u32 Total_Cycles; + u32 Total_CallCount; }; #if DEBUG @@ -108,72 +108,73 @@ struct debug_histogram_entry struct debug_services { - s64 PerformanceCountFrequency; - - b32 RecordFrames; - s32 CurrentDebugFrame; - debug_frame* Frames; - - debug_interface Interface; - - gs_thread_context Ctx; - - debug_get_thread_id* GetThreadId; - debug_timing_proc* GetWallClock; + s64 PerformanceCountFrequency; + + b32 RecordFrames; + s32 CurrentDebugFrame; + debug_frame* Frames; + + debug_interface Interface; + + gs_thread_context Ctx; + gs_memory_arena A; + + debug_get_thread_id* GetThreadId; + debug_timing_proc* GetWallClock; }; internal void InitializeDebugFrame (debug_frame* Frame, s32 NameHashMax, s32 ThreadCount, s32 ScopeCallsMax, debug_services* Services) { - Frame->ScopeNamesMax = NameHashMax; - Frame->ScopeNamesHash = AllocatorAllocArray(Services->Ctx.Allocator, scope_name, NameHashMax); - - // NOTE(Peter): We use the same size as scope names because we're only storing a single instance - // per scope. If ScopeNamesMax can't hold all the scopes, this will never get filled and - // we should assert and recompile with a resized NameHashMax - Frame->CollatedScopesMax = NameHashMax; - Frame->CollatedScopes = AllocatorAllocArray(Services->Ctx.Allocator, collated_scope_record, NameHashMax); - - for (s32 i = 0; i < Frame->ScopeNamesMax; i++) - { - scope_name* Entry = Frame->ScopeNamesHash + i; - Entry->Name = MakeString(Entry->Buffer, 0, SCOPE_NAME_BUFFER_LENGTH); - } - - Frame->ThreadCount = ThreadCount; - Frame->ThreadCalls = AllocatorAllocArray(Services->Ctx.Allocator, debug_scope_record_list, ThreadCount); - - for (s32 i = 0; i < ThreadCount; i++) - { - Frame->ThreadCalls[i].Max = ScopeCallsMax; - Frame->ThreadCalls[i].Count = 0; - Frame->ThreadCalls[i].Calls = AllocatorAllocArray(Services->Ctx.Allocator, scope_record, ScopeCallsMax); - Frame->ThreadCalls[i].CurrentScopeCallDepth = 0; - Frame->ThreadCalls[i].ThreadId = 0; - } - - for (s32 c = 0; c < Frame->CollatedScopesMax; c++) - { - Frame->CollatedScopes[c].NameHash = 0; - } + Frame->ScopeNamesMax = NameHashMax; + Frame->ScopeNamesHash = PushArray(&Services->A, scope_name, NameHashMax); + + // NOTE(Peter): We use the same size as scope names because we're only storing a single instance + // per scope. If ScopeNamesMax can't hold all the scopes, this will never get filled and + // we should assert and recompile with a resized NameHashMax + Frame->CollatedScopesMax = NameHashMax; + Frame->CollatedScopes = PushArray(&Services->A, collated_scope_record, NameHashMax); + + for (s32 i = 0; i < Frame->ScopeNamesMax; i++) + { + scope_name* Entry = Frame->ScopeNamesHash + i; + Entry->Name = MakeString(Entry->Buffer, 0, SCOPE_NAME_BUFFER_LENGTH); + } + + Frame->ThreadCount = ThreadCount; + Frame->ThreadCalls = PushArray(&Services->A, debug_scope_record_list, ThreadCount); + + for (s32 i = 0; i < ThreadCount; i++) + { + Frame->ThreadCalls[i].Max = ScopeCallsMax; + Frame->ThreadCalls[i].Count = 0; + Frame->ThreadCalls[i].Calls = PushArray(&Services->A, scope_record, ScopeCallsMax); + Frame->ThreadCalls[i].CurrentScopeCallDepth = 0; + Frame->ThreadCalls[i].ThreadId = 0; + } + + for (s32 c = 0; c < Frame->CollatedScopesMax; c++) + { + Frame->CollatedScopes[c].NameHash = 0; + } } internal void StartDebugFrame(debug_frame* Frame, debug_services* Services) { - Frame->FrameStartCycles = Services->GetWallClock(); - for (s32 i = 0; i < Frame->ThreadCount; i++) - { - Frame->ThreadCalls[i].Count = 0; - Frame->ThreadCalls[i].CurrentScopeCallDepth = 0; - } - - for (s32 c = 0; c < Frame->CollatedScopesMax; c++) - { - s32 Hash = Frame->CollatedScopes[c].NameHash; - Frame->CollatedScopes[c] = {}; - Frame->CollatedScopes[c].NameHash = Hash; - } + Frame->FrameStartCycles = Services->GetWallClock(); + for (s32 i = 0; i < Frame->ThreadCount; i++) + { + Frame->ThreadCalls[i].Count = 0; + Frame->ThreadCalls[i].CurrentScopeCallDepth = 0; + } + + for (s32 c = 0; c < Frame->CollatedScopesMax; c++) + { + s32 Hash = Frame->CollatedScopes[c].NameHash; + Frame->CollatedScopes[c] = {}; + Frame->CollatedScopes[c].NameHash = Hash; + } } internal void @@ -184,17 +185,17 @@ InitDebugServices_OffMode (debug_services* Services, gs_thread_context Ctx, s32 ThreadCount) { - *Services = {0}; - - Services->Ctx = Ctx; - Services->GetWallClock = GetWallClock; - Services->GetThreadId = GetThreadId; - - Services->RecordFrames = false; - Services->Frames = 0; - - Services->CurrentDebugFrame = 0; - Services->PerformanceCountFrequency = PerformanceCountFrequency; + *Services = {0}; + + Services->Ctx = Ctx; + Services->GetWallClock = GetWallClock; + Services->GetThreadId = GetThreadId; + + Services->RecordFrames = false; + Services->Frames = 0; + + Services->CurrentDebugFrame = 0; + Services->PerformanceCountFrequency = PerformanceCountFrequency; } @@ -206,227 +207,229 @@ InitDebugServices_DebugMode (debug_services* Services, gs_thread_context Ctx, s32 ThreadCount) { - Services->Ctx = Ctx; - Services->GetWallClock = GetWallClock; - Services->GetThreadId = GetThreadId; - - Services->RecordFrames = true; - - Services->CurrentDebugFrame = 0; - s32 NameHashMax = 4096; - s32 ScopeCallsMax = 4096; - Services->Frames = AllocatorAllocArray(Ctx.Allocator, debug_frame, DEBUG_FRAME_COUNT); - for (s32 i = 0; i < DEBUG_FRAME_COUNT; i++) - { - InitializeDebugFrame(&Services->Frames[i], NameHashMax, ThreadCount, ScopeCallsMax, Services); - } - - Services->PerformanceCountFrequency = PerformanceCountFrequency; + Services->Ctx = Ctx; + Services->GetWallClock = GetWallClock; + Services->GetThreadId = GetThreadId; + + Services->RecordFrames = true; + + Services->CurrentDebugFrame = 0; + Services->A = MemoryArenaCreate(MB(64), Bytes(8), Ctx.Allocator, 0, 0, "Debug Services Allocator"); + + s32 NameHashMax = 4096; + s32 ScopeCallsMax = 4096; + Services->Frames = PushArray(&Services->A, debug_frame, DEBUG_FRAME_COUNT); + for (s32 i = 0; i < DEBUG_FRAME_COUNT; i++) + { + InitializeDebugFrame(&Services->Frames[i], NameHashMax, ThreadCount, ScopeCallsMax, Services); + } + + Services->PerformanceCountFrequency = PerformanceCountFrequency; } internal debug_frame* GetCurrentDebugFrame (debug_services* Services) { - debug_frame* Result = Services->Frames + Services->CurrentDebugFrame; - return Result; + debug_frame* Result = Services->Frames + Services->CurrentDebugFrame; + return Result; } internal debug_frame* GetLastDebugFrame(debug_services* Services) { - if (!Services->Frames) return 0; - - s32 Index = (Services->CurrentDebugFrame - 1); - if (Index < 0) { Index += DEBUG_FRAME_COUNT; } - debug_frame* Result = Services->Frames + Index; - return Result; + if (!Services->Frames) return 0; + + s32 Index = (Services->CurrentDebugFrame - 1); + if (Index < 0) { Index += DEBUG_FRAME_COUNT; } + debug_frame* Result = Services->Frames + Index; + return Result; } internal s32 GetIndexForNameHash(debug_frame* Frame, u32 NameHash) { - s32 Result = -1; - - for (s32 Offset = 0; Offset < Frame->ScopeNamesMax; Offset++) + s32 Result = -1; + + for (s32 Offset = 0; Offset < Frame->ScopeNamesMax; Offset++) + { + u32 Index = (NameHash + Offset) % Frame->ScopeNamesMax; + if (Frame->ScopeNamesHash[Index].Hash == NameHash) { - u32 Index = (NameHash + Offset) % Frame->ScopeNamesMax; - if (Frame->ScopeNamesHash[Index].Hash == NameHash) - { - Result = Index; - break; - } + Result = Index; + break; } - - // NOTE(Peter): Its not technically wrong to return a -1 here, just means we didn't find it. - // At the time of writing however, this function is only being called in contexts where we - // know there should be an entry in the Name table, so a -1 actually indicates a problem. - Assert(Result >= 0); - return Result; + } + + // NOTE(Peter): Its not technically wrong to return a -1 here, just means we didn't find it. + // At the time of writing however, this function is only being called in contexts where we + // know there should be an entry in the Name table, so a -1 actually indicates a problem. + Assert(Result >= 0); + return Result; } internal debug_scope_record_list* GetScopeListForThreadInFrame(debug_services* Services, debug_frame* Frame) { - debug_scope_record_list* List = 0; - - s32 CurrentThreadId = Services->GetThreadId(); - for (s32 Offset = 0; Offset < Frame->ThreadCount; Offset++) + debug_scope_record_list* List = 0; + + s32 CurrentThreadId = Services->GetThreadId(); + for (s32 Offset = 0; Offset < Frame->ThreadCount; Offset++) + { + s32 Index = (CurrentThreadId + Offset) % Frame->ThreadCount; + if (Frame->ThreadCalls[Index].ThreadId == CurrentThreadId) { - s32 Index = (CurrentThreadId + Offset) % Frame->ThreadCount; - if (Frame->ThreadCalls[Index].ThreadId == CurrentThreadId) - { - List = Frame->ThreadCalls + Index; - break; - } - else if (Frame->ThreadCalls[Index].ThreadId == 0) - { - Frame->ThreadCalls[Index].ThreadId = CurrentThreadId; - List = Frame->ThreadCalls + Index; - break; - } + List = Frame->ThreadCalls + Index; + break; } - - Assert(List); - return List; + else if (Frame->ThreadCalls[Index].ThreadId == 0) + { + Frame->ThreadCalls[Index].ThreadId = CurrentThreadId; + List = Frame->ThreadCalls + Index; + break; + } + } + + Assert(List); + return List; } internal void CollateThreadScopeCalls (debug_scope_record_list* ThreadRecords, debug_frame* Frame) { - for (s32 i = 0; i < ThreadRecords->Count; i++) + for (s32 i = 0; i < ThreadRecords->Count; i++) + { + scope_record Record = ThreadRecords->Calls[i]; + s32 Index = GetIndexForNameHash (Frame, Record.NameHash); + collated_scope_record* CollatedRecord = Frame->CollatedScopes + Index; + + if (CollatedRecord->NameHash != Record.NameHash) { - scope_record Record = ThreadRecords->Calls[i]; - s32 Index = GetIndexForNameHash (Frame, Record.NameHash); - collated_scope_record* CollatedRecord = Frame->CollatedScopes + Index; - - if (CollatedRecord->NameHash != Record.NameHash) - { - CollatedRecord->NameHash = Record.NameHash; - CollatedRecord->TotalCycles = 0; - CollatedRecord->CallCount = 0; - } - - CollatedRecord->TotalCycles += Record.EndCycles - Record.StartCycles; - CollatedRecord->CallCount += 1; + CollatedRecord->NameHash = Record.NameHash; + CollatedRecord->TotalCycles = 0; + CollatedRecord->CallCount = 0; } + + CollatedRecord->TotalCycles += Record.EndCycles - Record.StartCycles; + CollatedRecord->CallCount += 1; + } } internal void EndDebugFrame (debug_services* Services) { - debug_frame* ClosingFrame = GetCurrentDebugFrame(Services); - ClosingFrame->FrameEndCycles = Services->GetWallClock(); - - s64 FrameTotalCycles = ClosingFrame->FrameEndCycles - ClosingFrame->FrameStartCycles; - - for (s32 t = 0; t < ClosingFrame->ThreadCount; t++) + debug_frame* ClosingFrame = GetCurrentDebugFrame(Services); + ClosingFrame->FrameEndCycles = Services->GetWallClock(); + + s64 FrameTotalCycles = ClosingFrame->FrameEndCycles - ClosingFrame->FrameStartCycles; + + for (s32 t = 0; t < ClosingFrame->ThreadCount; t++) + { + CollateThreadScopeCalls(ClosingFrame->ThreadCalls + t, ClosingFrame); + } + + s32 ScopeNamesCount = 0; + for (s32 n = 0; n < ClosingFrame->ScopeNamesMax; n++) + { + if (ClosingFrame->ScopeNamesHash[n].Hash != 0) { - CollateThreadScopeCalls(ClosingFrame->ThreadCalls + t, ClosingFrame); + collated_scope_record* CollatedRecord = ClosingFrame->CollatedScopes + n; + CollatedRecord->TotalSeconds = (r32)CollatedRecord->TotalCycles / (r32)Services->PerformanceCountFrequency; + CollatedRecord->PercentFrameTime = (r32)CollatedRecord->TotalCycles / (r32)FrameTotalCycles; + CollatedRecord->AverageSecondsPerCall = CollatedRecord->TotalSeconds / CollatedRecord->CallCount; + ScopeNamesCount += 1; } - - s32 ScopeNamesCount = 0; - for (s32 n = 0; n < ClosingFrame->ScopeNamesMax; n++) - { - if (ClosingFrame->ScopeNamesHash[n].Hash != 0) - { - collated_scope_record* CollatedRecord = ClosingFrame->CollatedScopes + n; - CollatedRecord->TotalSeconds = (r32)CollatedRecord->TotalCycles / (r32)Services->PerformanceCountFrequency; - CollatedRecord->PercentFrameTime = (r32)CollatedRecord->TotalCycles / (r32)FrameTotalCycles; - CollatedRecord->AverageSecondsPerCall = CollatedRecord->TotalSeconds / CollatedRecord->CallCount; - ScopeNamesCount += 1; - } - } - ClosingFrame->ScopeNamesCount = ScopeNamesCount; - - s32 FramesCount = DEBUG_FRAME_COUNT; - if (FramesCount > 0) - { - Services->CurrentDebugFrame = (Services->CurrentDebugFrame + 1) % FramesCount; - } - StartDebugFrame(&Services->Frames[Services->CurrentDebugFrame], Services); + } + ClosingFrame->ScopeNamesCount = ScopeNamesCount; + + s32 FramesCount = DEBUG_FRAME_COUNT; + if (FramesCount > 0) + { + Services->CurrentDebugFrame = (Services->CurrentDebugFrame + 1) % FramesCount; + } + StartDebugFrame(&Services->Frames[Services->CurrentDebugFrame], Services); } internal u32 HashScopeName (char* ScopeName) { - // djb2 hash - u32 Hash = 5381; - char* C = ScopeName; - while(*C) - { - Hash = ((Hash << 5) + Hash) + *C; - C++; - } - return Hash; + // djb2 hash + u32 Hash = 5381; + char* C = ScopeName; + while(*C) + { + Hash = ((Hash << 5) + Hash) + *C; + C++; + } + return Hash; } internal scope_name* GetOrAddNameHashEntry(debug_frame* Frame, u32 NameHash) { - scope_name* Result = 0; - - for (s32 Offset = 0; Offset < Frame->ScopeNamesMax; Offset++) + scope_name* Result = 0; + + for (s32 Offset = 0; Offset < Frame->ScopeNamesMax; Offset++) + { + u32 Index = (NameHash + Offset) % Frame->ScopeNamesMax; + if ((Frame->ScopeNamesHash[Index].Hash == 0) || (Frame->ScopeNamesHash[Index].Hash == NameHash)) { - u32 Index = (NameHash + Offset) % Frame->ScopeNamesMax; - if ((Frame->ScopeNamesHash[Index].Hash == 0) || (Frame->ScopeNamesHash[Index].Hash == NameHash)) - { - Result = Frame->ScopeNamesHash + Index; - break; - } + Result = Frame->ScopeNamesHash + Index; + break; } - - return Result; + } + + return Result; } internal u32 BeginTrackingScopeAndGetNameHash (debug_services* Services, char* ScopeName) { - debug_frame* CurrentFrame = GetCurrentDebugFrame(Services); - debug_scope_record_list* ThreadList = GetScopeListForThreadInFrame(Services, CurrentFrame); - - ThreadList->CurrentScopeCallDepth++; - - u32 NameHash = HashScopeName(ScopeName); - scope_name* Entry = GetOrAddNameHashEntry(CurrentFrame, NameHash); - if (Entry->Hash == 0) // If its new - { - Entry->Hash = NameHash; - // TODO(Peter): need to initialize all entry name gs_strings to point at the buffer - // This will break eventually. when it does, do this ^^^^ when on startup - PrintF(&Entry->Name, "%s", ScopeName); - } - - return NameHash; + debug_frame* CurrentFrame = GetCurrentDebugFrame(Services); + debug_scope_record_list* ThreadList = GetScopeListForThreadInFrame(Services, CurrentFrame); + + ThreadList->CurrentScopeCallDepth++; + + u32 NameHash = HashScopeName(ScopeName); + scope_name* Entry = GetOrAddNameHashEntry(CurrentFrame, NameHash); + if (Entry->Hash == 0) // If its new + { + Entry->Hash = NameHash; + // TODO(Peter): need to initialize all entry name gs_strings to point at the buffer + // This will break eventually. when it does, do this ^^^^ when on startup + PrintF(&Entry->Name, "%s", ScopeName); + } + + return NameHash; } internal void PushScopeTimeOnFrame (debug_services* Services, s32 NameHash, u64 StartCycles, u64 EndCycles) { - debug_frame* CurrentFrame = GetCurrentDebugFrame(Services); - debug_scope_record_list* ThreadList = GetScopeListForThreadInFrame(Services, CurrentFrame); - - if (ThreadList->Count >= ThreadList->Max) - { - s32 NewMax = (ThreadList->Max + DEBUG_FRAME_GROW_SIZE); - AllocatorFreeArray(Services->Ctx.Allocator, ThreadList->Calls, scope_record, ThreadList->Max); - ThreadList->Calls = AllocatorAllocArray(Services->Ctx.Allocator, scope_record, NewMax); - ThreadList->Max = NewMax; - } - - Assert(ThreadList->Count < ThreadList->Max); - - s32 EntryIndex = ThreadList->Count++; - scope_record* Record = ThreadList->Calls + EntryIndex; - Record->NameHash = NameHash; - Record->StartCycles = StartCycles; - Record->EndCycles = EndCycles; - Record->CallDepth = --ThreadList->CurrentScopeCallDepth; + debug_frame* CurrentFrame = GetCurrentDebugFrame(Services); + debug_scope_record_list* ThreadList = GetScopeListForThreadInFrame(Services, CurrentFrame); + + if (ThreadList->Count >= ThreadList->Max) + { + s32 NewMax = (ThreadList->Max + DEBUG_FRAME_GROW_SIZE); + FreeArray(Services->Ctx.Allocator, ThreadList->Calls, scope_record, ThreadList->Max); + ThreadList->Calls = AllocArray(Services->Ctx.Allocator, scope_record, NewMax, "Debug Frame"); + ThreadList->Max = NewMax; + } + + Assert(ThreadList->Count < ThreadList->Max); + + s32 EntryIndex = ThreadList->Count++; + scope_record* Record = ThreadList->Calls + EntryIndex; + Record->NameHash = NameHash; + Record->StartCycles = StartCycles; + Record->EndCycles = EndCycles; + Record->CallDepth = --ThreadList->CurrentScopeCallDepth; } internal r32 DEBUGGetSecondsElapsed (s64 Start, s64 End, r32 PerformanceCountFrequency) { - r32 Result = ((r32)(End - Start) / (r32)PerformanceCountFrequency); - return Result; + r32 Result = ((r32)(End - Start) / (r32)PerformanceCountFrequency); + return Result; } #if DEBUG @@ -438,32 +441,32 @@ internal r32 DEBUGGetSecondsElapsed (s64 Start, s64 End, r32 PerformanceCountFre #endif struct scope_tracker { - s64 ScopeStart; - u32 ScopeNameHash; - debug_services* DebugServices; - - scope_tracker(char* ScopeName, debug_services* DebugServices) + s64 ScopeStart; + u32 ScopeNameHash; + debug_services* DebugServices; + + scope_tracker(char* ScopeName, debug_services* DebugServices) + { + if (DebugServices->RecordFrames) { - if (DebugServices->RecordFrames) - { - this->ScopeNameHash = BeginTrackingScopeAndGetNameHash(DebugServices, ScopeName); - this->ScopeStart = DebugServices->GetWallClock(); - this->DebugServices = DebugServices; - } - else - { - this->DebugServices = 0; - } + this->ScopeNameHash = BeginTrackingScopeAndGetNameHash(DebugServices, ScopeName); + this->ScopeStart = DebugServices->GetWallClock(); + this->DebugServices = DebugServices; } - - ~scope_tracker() + else { - if (this->DebugServices) // NOTE(Peter): If DebugServices == 0, then we werent' recording this frame - { - s64 ScopeEnd = this->DebugServices->GetWallClock(); - PushScopeTimeOnFrame(this->DebugServices, this->ScopeNameHash, this->ScopeStart, ScopeEnd); - } + this->DebugServices = 0; } + } + + ~scope_tracker() + { + if (this->DebugServices) // NOTE(Peter): If DebugServices == 0, then we werent' recording this frame + { + s64 ScopeEnd = this->DebugServices->GetWallClock(); + PushScopeTimeOnFrame(this->DebugServices, this->ScopeNameHash, this->ScopeStart, ScopeEnd); + } + } }; diff --git a/src/app/foldhaus_renderer.cpp b/src/app/foldhaus_renderer.cpp index 548acda..51c2579 100644 --- a/src/app/foldhaus_renderer.cpp +++ b/src/app/foldhaus_renderer.cpp @@ -8,190 +8,190 @@ internal render_command_buffer AllocateRenderCommandBuffer (u8* Memory, s32 Size, gs_thread_context Ctx) { - render_command_buffer Result = {}; - Result.CommandMemory = Memory; - Result.CommandMemorySize = Size; - Result.CommandMemoryUsed = 0; - Result.Ctx = Ctx; - return Result; + render_command_buffer Result = {}; + Result.CommandMemory = Memory; + Result.CommandMemorySize = Size; + Result.CommandMemoryUsed = 0; + Result.Ctx = Ctx; + return Result; } internal render_command_buffer AllocateRenderCommandBuffer(u32 MemorySize, gs_memory_arena* Arena, gs_thread_context Ctx) { - u8* Memory = PushSize(Arena, MemorySize); - return AllocateRenderCommandBuffer(Memory, MemorySize, Ctx); + u8* Memory = PushSize(Arena, MemorySize).Memory; + return AllocateRenderCommandBuffer(Memory, MemorySize, Ctx); } internal void Render3DQuadBatch (u8* CommandData, s32 TriCount) { - DEBUG_TRACK_FUNCTION; - - v4* Vertecies = (v4*)(CommandData + BATCH_3D_VERTECIES_OFFSET(TriCount)); - v2* UVs = (v2*)(CommandData + BATCH_3D_UVS_OFFSET(TriCount)); - v4* Colors = (v4*)(CommandData + BATCH_3D_COLORS_OFFSET(TriCount)); - + DEBUG_TRACK_FUNCTION; + + v4* Vertecies = (v4*)(CommandData + BATCH_3D_VERTECIES_OFFSET(TriCount)); + v2* UVs = (v2*)(CommandData + BATCH_3D_UVS_OFFSET(TriCount)); + v4* Colors = (v4*)(CommandData + BATCH_3D_COLORS_OFFSET(TriCount)); + #if IMMEDIATE_MODE_RENDERING + + for (s32 Tri = 0; Tri < TriCount; Tri++) + { + v4 P0 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 0)]; + v4 P1 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 1)]; + v4 P2 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 2)]; + v2 UV0 = UVs[BATCH_3D_UV_INDEX(Tri, 0)]; + v2 UV1 = UVs[BATCH_3D_UV_INDEX(Tri, 1)]; + v2 UV2 = UVs[BATCH_3D_UV_INDEX(Tri, 2)]; + v4 C0 = Colors[BATCH_3D_COLOR_INDEX(Tri, 0)]; + v4 C1 = Colors[BATCH_3D_COLOR_INDEX(Tri, 1)]; + v4 C2 = Colors[BATCH_3D_COLOR_INDEX(Tri, 2)]; - for (s32 Tri = 0; Tri < TriCount; Tri++) - { - v4 P0 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 0)]; - v4 P1 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 1)]; - v4 P2 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 2)]; - v2 UV0 = UVs[BATCH_3D_UV_INDEX(Tri, 0)]; - v2 UV1 = UVs[BATCH_3D_UV_INDEX(Tri, 1)]; - v2 UV2 = UVs[BATCH_3D_UV_INDEX(Tri, 2)]; - v4 C0 = Colors[BATCH_3D_COLOR_INDEX(Tri, 0)]; - v4 C1 = Colors[BATCH_3D_COLOR_INDEX(Tri, 1)]; - v4 C2 = Colors[BATCH_3D_COLOR_INDEX(Tri, 2)]; - - OpenGLDraw3DTri(P0, P1, P2, UV0, UV1, UV2, C0, C1, C2); - } + OpenGLDraw3DTri(P0, P1, P2, UV0, UV1, UV2, C0, C1, C2); + } #else - OpenGLRenderTriBuffer((u8*)Vertecies, 4, (u8*)UVs, 2, (u8*)Colors, 4, TriCount * 3); + OpenGLRenderTriBuffer((u8*)Vertecies, 4, (u8*)UVs, 2, (u8*)Colors, 4, TriCount * 3); #endif } internal void Render2DQuadBatch (u8* CommandData, s32 QuadCount) { - DEBUG_TRACK_FUNCTION; - - v2* Vertecies = (v2*)(CommandData + BATCH_2D_VERTECIES_OFFSET(QuadCount)); - v2* UVs = (v2*)(CommandData + BATCH_2D_UVS_OFFSET(QuadCount)); - v4* Colors = (v4*)(CommandData + BATCH_2D_COLORS_OFFSET(QuadCount)); - + DEBUG_TRACK_FUNCTION; + + v2* Vertecies = (v2*)(CommandData + BATCH_2D_VERTECIES_OFFSET(QuadCount)); + v2* UVs = (v2*)(CommandData + BATCH_2D_UVS_OFFSET(QuadCount)); + v4* Colors = (v4*)(CommandData + BATCH_2D_COLORS_OFFSET(QuadCount)); + #if IMMEDIATE_MODE_RENDERING - for (s32 Quad = 0; Quad < QuadCount; Quad++) + for (s32 Quad = 0; Quad < QuadCount; Quad++) + { + for (s32 Tri = 0; Tri < 2; Tri++) { - for (s32 Tri = 0; Tri < 2; Tri++) - { - v2 P0 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 0)]; - v2 P1 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 1)]; - v2 P2 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 2)]; - v2 UV0 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 0)]; - v2 UV1 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 1)]; - v2 UV2 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 2)]; - v4 C0 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 0)]; - v4 C1 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 1)]; - v4 C2 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 2)]; - - OpenGLDraw2DTri(P0, P1, P2, UV0, UV1, UV2, C0, C1, C2); - } + v2 P0 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 0)]; + v2 P1 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 1)]; + v2 P2 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 2)]; + v2 UV0 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 0)]; + v2 UV1 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 1)]; + v2 UV2 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 2)]; + v4 C0 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 0)]; + v4 C1 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 1)]; + v4 C2 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 2)]; + + OpenGLDraw2DTri(P0, P1, P2, UV0, UV1, UV2, C0, C1, C2); } + } #else - OpenGLRenderTriBuffer((u8*)Vertecies, 2, (u8*)UVs, 2, (u8*)Colors, 4, QuadCount * 2 * 3); + OpenGLRenderTriBuffer((u8*)Vertecies, 2, (u8*)UVs, 2, (u8*)Colors, 4, QuadCount * 2 * 3); #endif } internal void RenderCommandBuffer (render_command_buffer CommandBuffer) { - DEBUG_TRACK_FUNCTION; - - 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); - b32 GLTextureEnabled = false; - - u8* CurrentPosition = CommandBuffer.CommandMemory; - while(CurrentPosition < CommandBuffer.CommandMemory + CommandBuffer.CommandMemoryUsed) + DEBUG_TRACK_FUNCTION; + + 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); + b32 GLTextureEnabled = false; + + u8* CurrentPosition = CommandBuffer.CommandMemory; + while(CurrentPosition < CommandBuffer.CommandMemory + CommandBuffer.CommandMemoryUsed) + { + render_command_header* CommandHeader = (render_command_header*)CurrentPosition; + CurrentPosition += sizeof(render_command_header); + switch (CommandHeader->Type) { - render_command_header* CommandHeader = (render_command_header*)CurrentPosition; - CurrentPosition += sizeof(render_command_header); - switch (CommandHeader->Type) + case RenderCommand_render_command_set_render_mode: + { + DEBUG_TRACK_SCOPE(SetRenderMode); + + render_command_set_render_mode* Command = (render_command_set_render_mode*)(CommandHeader + 1); + + glViewport(Command->ViewOffsetX, Command->ViewOffsetY, + Command->ViewWidth, Command->ViewHeight); + + LoadModelView(Command->ModelView.Array); + LoadProjection(Command->Projection.Array); + + if (Command->UseDepthBuffer) { - case RenderCommand_render_command_set_render_mode: - { - DEBUG_TRACK_SCOPE(SetRenderMode); - - render_command_set_render_mode* Command = (render_command_set_render_mode*)(CommandHeader + 1); - - glViewport(Command->ViewOffsetX, Command->ViewOffsetY, - Command->ViewWidth, Command->ViewHeight); - - LoadModelView(Command->ModelView.Array); - LoadProjection(Command->Projection.Array); - - if (Command->UseDepthBuffer) - { - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LESS); - } - else - { - glDisable(GL_DEPTH_TEST); - } - - CurrentPosition += sizeof(render_command_set_render_mode); - }break; - - case RenderCommand_render_command_clear_screen: - { - DEBUG_TRACK_SCOPE(RendererClearScreen); - - render_command_clear_screen* Command = (render_command_clear_screen*)(CommandHeader + 1); - - ClearRenderBuffer(); - - CurrentPosition += sizeof(render_command_clear_screen); - }break; - - case RenderCommand_render_batch_command_quad_2d: - { - render_batch_command_quad_2d* Command = (render_batch_command_quad_2d*)(CommandHeader + 1); - - if (GLTextureEnabled) { glDisable(GL_TEXTURE_2D); GLTextureEnabled = false; } - u8* CommandData = (u8*)(Command + 1); - Render2DQuadBatch(CommandData, Command->QuadCount); - - CurrentPosition += sizeof(render_batch_command_quad_2d) + Command->DataSize; - }break; - - case RenderCommand_render_batch_command_quad_3d: - { - render_batch_command_quad_3d* Command = (render_batch_command_quad_3d*)(CommandHeader + 1); - - if (GLTextureEnabled) { glDisable(GL_TEXTURE_2D); GLTextureEnabled = false; } - u8* CommandData = (u8*)(Command + 1); - Render3DQuadBatch(CommandData, Command->QuadCount * 2); - - CurrentPosition += sizeof(render_batch_command_quad_3d) + Command->DataSize; - }break; - - case RenderCommand_render_batch_command_texture_2d: - { - render_batch_command_texture_2d* Command = (render_batch_command_texture_2d*)(CommandHeader + 1); - - if (!GLTextureEnabled) { glEnable(GL_TEXTURE_2D); GLTextureEnabled = true; } - Assert(Command->Texture.Handle > 0); - glBindTexture(GL_TEXTURE_2D, Command->Texture.Handle); - u8* CommandData = (u8*)(Command + 1); - Render2DQuadBatch(CommandData, Command->QuadCount); - - CurrentPosition += sizeof(render_batch_command_texture_2d) + Command->DataSize; - }break; - - default: - { - InvalidCodePath; - }break; + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); } + else + { + glDisable(GL_DEPTH_TEST); + } + + CurrentPosition += sizeof(render_command_set_render_mode); + }break; + + case RenderCommand_render_command_clear_screen: + { + DEBUG_TRACK_SCOPE(RendererClearScreen); + + render_command_clear_screen* Command = (render_command_clear_screen*)(CommandHeader + 1); + + ClearRenderBuffer(); + + CurrentPosition += sizeof(render_command_clear_screen); + }break; + + case RenderCommand_render_batch_command_quad_2d: + { + render_batch_command_quad_2d* Command = (render_batch_command_quad_2d*)(CommandHeader + 1); + + if (GLTextureEnabled) { glDisable(GL_TEXTURE_2D); GLTextureEnabled = false; } + u8* CommandData = (u8*)(Command + 1); + Render2DQuadBatch(CommandData, Command->QuadCount); + + CurrentPosition += sizeof(render_batch_command_quad_2d) + Command->DataSize; + }break; + + case RenderCommand_render_batch_command_quad_3d: + { + render_batch_command_quad_3d* Command = (render_batch_command_quad_3d*)(CommandHeader + 1); + + if (GLTextureEnabled) { glDisable(GL_TEXTURE_2D); GLTextureEnabled = false; } + u8* CommandData = (u8*)(Command + 1); + Render3DQuadBatch(CommandData, Command->QuadCount * 2); + + CurrentPosition += sizeof(render_batch_command_quad_3d) + Command->DataSize; + }break; + + case RenderCommand_render_batch_command_texture_2d: + { + render_batch_command_texture_2d* Command = (render_batch_command_texture_2d*)(CommandHeader + 1); + + if (!GLTextureEnabled) { glEnable(GL_TEXTURE_2D); GLTextureEnabled = true; } + Assert(Command->Texture.Handle > 0); + glBindTexture(GL_TEXTURE_2D, Command->Texture.Handle); + u8* CommandData = (u8*)(Command + 1); + Render2DQuadBatch(CommandData, Command->QuadCount); + + CurrentPosition += sizeof(render_batch_command_texture_2d) + Command->DataSize; + }break; + + default: + { + InvalidCodePath; + }break; } + } } internal void ClearRenderBuffer (render_command_buffer* Buffer) { - Buffer->CommandMemoryUsed = 0; + Buffer->CommandMemoryUsed = 0; } #define FOLDHAUS_RENDERER_CPP diff --git a/src/app/foldhaus_renderer.h b/src/app/foldhaus_renderer.h index 0a264de..6e48836 100644 --- a/src/app/foldhaus_renderer.h +++ b/src/app/foldhaus_renderer.h @@ -9,104 +9,104 @@ struct camera { - r32 FieldOfView; - r32 AspectRatio; - r32 Near, Far; - v3 Position; - v3 LookAt; + r32 FieldOfView; + r32 AspectRatio; + r32 Near, Far; + v3 Position; + v3 LookAt; }; inline m44 GetCameraModelViewMatrix (camera Camera) { - m44 RotationMatrix = M44LookAt(ToV4Point(Camera.Position), ToV4Point(Camera.LookAt)); - m44 PositionMatrix = M44Translation(ToV4Point(-Camera.Position)); - m44 ModelViewMatrix = RotationMatrix * PositionMatrix; - return ModelViewMatrix; + m44 RotationMatrix = M44LookAt(ToV4Point(Camera.Position), ToV4Point(Camera.LookAt)); + m44 PositionMatrix = M44Translation(ToV4Point(-Camera.Position)); + m44 ModelViewMatrix = RotationMatrix * PositionMatrix; + return ModelViewMatrix; } inline m44 GetCameraPerspectiveProjectionMatrix(camera Camera) { - m44 Result = M44ProjectionPerspective(Camera.FieldOfView, Camera.AspectRatio, Camera.Near, Camera.Far); - return Result; + m44 Result = M44ProjectionPerspective(Camera.FieldOfView, Camera.AspectRatio, Camera.Near, Camera.Far); + return Result; } internal m44 GetCameraMatrix(camera Camera) { - m44 ModelView = GetCameraModelViewMatrix(Camera); - m44 Projection = GetCameraPerspectiveProjectionMatrix(Camera); - m44 Result = Projection * ModelView; - return Result; + m44 ModelView = GetCameraModelViewMatrix(Camera); + m44 Projection = GetCameraPerspectiveProjectionMatrix(Camera); + m44 Result = Projection * ModelView; + return Result; } internal v2 ProjectWorldPointToScreen(v4 WorldSpacePoint, camera Camera, rect2 WindowBounds) { - v2 WindowExtents = v2{Rect2Width(WindowBounds), Rect2Height(WindowBounds)}; - v4 ProjectedPosition = GetCameraMatrix(Camera) * WorldSpacePoint; - ProjectedPosition.xyz /= ProjectedPosition.w; - v2 ScreenPosition = V2MultiplyPairwise(ProjectedPosition.xy, (WindowExtents / 2)) + (WindowExtents / 2); - - return ScreenPosition; + v2 WindowExtents = v2{Rect2Width(WindowBounds), Rect2Height(WindowBounds)}; + v4 ProjectedPosition = GetCameraMatrix(Camera) * WorldSpacePoint; + ProjectedPosition.xyz /= ProjectedPosition.w; + v2 ScreenPosition = V2MultiplyPairwise(ProjectedPosition.xy, (WindowExtents / 2)) + (WindowExtents / 2); + + return ScreenPosition; } internal v4_ray ProjectScreenPointToWorldRay(v2 ScreenPoint, camera Camera, rect2 WindowBounds) { - v4_ray Result = {0}; - - r32 TanFOVOverTwo = TanR32(DegToRadR32(Camera.FieldOfView / 2.0f)); - r32 Aspect = RectAspectRatio(WindowBounds); - - r32 NormalizedX = ScreenPoint.x / Rect2Width(WindowBounds); - r32 NormalizedY = ScreenPoint.y / Rect2Height(WindowBounds); - - r32 CenteredX = (2.0f * NormalizedX) - 1.0f; - r32 CenteredY = (2.0f * NormalizedY) - 1.0f; - - r32 ScaledX = CenteredX * Aspect; - r32 ScaledY = CenteredY; - - r32 CameraX = ScaledX * TanFOVOverTwo; - r32 CameraY = ScaledY * TanFOVOverTwo; - - r32 Near = Camera.Near; - r32 Far = Camera.Far; - v3 MousePointOnNearPlane = v3{CameraX, CameraY, -1} * Near; - v3 MousePointOnFarPlane = v3{CameraX, CameraY, -1} * Far; - - v4 MouseRayDirection = ToV4Vec(V3Normalize(MousePointOnFarPlane - MousePointOnNearPlane)); - m44 CameraTransform = M44Transpose(M44LookAt(ToV4Point(Camera.Position), ToV4Point(Camera.LookAt))); - - Result.Origin = ToV4Point(Camera.Position); - Result.Direction = CameraTransform * MouseRayDirection; - - return Result; + v4_ray Result = {0}; + + r32 TanFOVOverTwo = TanR32(DegToRadR32(Camera.FieldOfView / 2.0f)); + r32 Aspect = RectAspectRatio(WindowBounds); + + r32 NormalizedX = ScreenPoint.x / Rect2Width(WindowBounds); + r32 NormalizedY = ScreenPoint.y / Rect2Height(WindowBounds); + + r32 CenteredX = (2.0f * NormalizedX) - 1.0f; + r32 CenteredY = (2.0f * NormalizedY) - 1.0f; + + r32 ScaledX = CenteredX * Aspect; + r32 ScaledY = CenteredY; + + r32 CameraX = ScaledX * TanFOVOverTwo; + r32 CameraY = ScaledY * TanFOVOverTwo; + + r32 Near = Camera.Near; + r32 Far = Camera.Far; + v3 MousePointOnNearPlane = v3{CameraX, CameraY, -1} * Near; + v3 MousePointOnFarPlane = v3{CameraX, CameraY, -1} * Far; + + v4 MouseRayDirection = ToV4Vec(V3Normalize(MousePointOnFarPlane - MousePointOnNearPlane)); + m44 CameraTransform = M44Transpose(M44LookAt(ToV4Point(Camera.Position), ToV4Point(Camera.LookAt))); + + Result.Origin = ToV4Point(Camera.Position); + Result.Direction = CameraTransform * MouseRayDirection; + + return Result; } // Render Commands // Discriminated Union enum render_command_type { - RenderCommand_Invalid, - - RenderCommand_render_command_clear_screen, - RenderCommand_render_command_set_render_mode, - - RenderCommand_render_batch_command_quad_2d, - RenderCommand_render_batch_command_quad_3d, - RenderCommand_render_batch_command_texture_2d, - - RenderCommand_render_command_texture_3d, - - RenderCommand_Count + RenderCommand_Invalid, + + RenderCommand_render_command_clear_screen, + RenderCommand_render_command_set_render_mode, + + RenderCommand_render_batch_command_quad_2d, + RenderCommand_render_batch_command_quad_3d, + RenderCommand_render_batch_command_texture_2d, + + RenderCommand_render_command_texture_3d, + + RenderCommand_Count }; struct render_command_header { - render_command_type Type; + render_command_type Type; }; // NOTE(Peter): Just to keep with the rest of the system @@ -114,22 +114,22 @@ struct render_command_clear_screen {}; struct render_quad_2d { - v2 Min, Max; + v2 Min, Max; }; struct render_quad_3d { - v4 P0, P1, P2, P3; + v4 P0, P1, P2, P3; }; struct render_texture { - u8* Memory; - s32 Handle; - s32 Width; - s32 Height; - s32 BytesPerPixel; - s32 Stride; + u8* Memory; + s32 Handle; + s32 Width; + s32 Height; + s32 BytesPerPixel; + s32 Stride; }; #define BATCH_3D_SIZE(tricount) (((sizeof(v4) + sizeof(v2) + sizeof(v4)) * 3) * tricount) @@ -150,57 +150,57 @@ struct render_texture struct render_quad_batch_constructor { - s32 Max; - s32 Count; - - v4* Vertecies; - v2* UVs; - v4* ColorsV; + s32 Max; + s32 Count; + + v4* Vertecies; + v2* UVs; + v4* ColorsV; }; struct render_batch_command_quad_2d { - s32 QuadCount; - s32 DataSize; - // NOTE(Peter): The data immediately follows the command in memory + s32 QuadCount; + s32 DataSize; + // NOTE(Peter): The data immediately follows the command in memory }; struct render_batch_command_quad_3d { - s32 QuadCount; - s32 DataSize; - // NOTE(Peter): The data immediately follows the command in memory + s32 QuadCount; + s32 DataSize; + // NOTE(Peter): The data immediately follows the command in memory }; struct render_command_texture_2d { - render_quad_2d Quad; - render_quad_2d UV; - v4 Color; - render_texture Texture; + render_quad_2d Quad; + render_quad_2d UV; + v4 Color; + render_texture Texture; }; struct render_batch_command_texture_2d { - s32 QuadCount; - s32 DataSize; - render_texture Texture; + s32 QuadCount; + s32 DataSize; + render_texture Texture; }; struct render_command_texture_3d { - render_quad_3d Quad; - v4 Color; - render_texture Texture; + render_quad_3d Quad; + v4 Color; + render_texture Texture; }; struct render_command_set_render_mode { - m44 ModelView; - m44 Projection; - r32 ViewOffsetX, ViewOffsetY; - r32 ViewWidth, ViewHeight; - b32 UseDepthBuffer; + m44 ModelView; + m44 Projection; + r32 ViewOffsetX, ViewOffsetY; + r32 ViewWidth, ViewHeight; + b32 UseDepthBuffer; }; typedef u8* renderer_realloc(u8* Base, s32 CurrentSize, s32 NewSize); @@ -209,14 +209,14 @@ typedef u8* renderer_realloc(u8* Base, s32 CurrentSize, s32 NewSize); struct render_command_buffer { - u8* CommandMemory; - s32 CommandMemoryUsed; - s32 CommandMemorySize; - - gs_thread_context Ctx; - - s32 ViewWidth; - s32 ViewHeight; + u8* CommandMemory; + s32 CommandMemoryUsed; + s32 CommandMemorySize; + + gs_thread_context Ctx; + + s32 ViewWidth; + s32 ViewHeight; }; /// @@ -226,49 +226,49 @@ struct render_command_buffer internal u32 PackColorStructU8 (u8 R, u8 G, u8 B, u8 A) { - u32 Result = (u32)(A << 24 | - R << 16 | - G << 8 | - B<< 0); - return Result; + u32 Result = (u32)(A << 24 | + R << 16 | + G << 8 | + B<< 0); + return Result; } internal u32 PackColorStructR32 (r32 In_R, r32 In_G, r32 In_B, r32 In_A) { - Assert ((In_R >= 0.0f && In_R <= 1.0f) && - (In_G >= 0.0f && In_G <= 1.0f) && - (In_B >= 0.0f && In_B <= 1.0f) && - (In_A >= 0.0f && In_A <= 1.0f)); - - u8 R = (u8)(255 * In_R); - u8 G = (u8)(255 * In_G); - u8 B = (u8)(255 * In_B); - u8 A = (u8)(255 * In_A); - - u32 Result = (u32)(A << 24 | - R << 16 | - G << 8 | - B<< 0); - return Result; + Assert ((In_R >= 0.0f && In_R <= 1.0f) && + (In_G >= 0.0f && In_G <= 1.0f) && + (In_B >= 0.0f && In_B <= 1.0f) && + (In_A >= 0.0f && In_A <= 1.0f)); + + u8 R = (u8)(255 * In_R); + u8 G = (u8)(255 * In_G); + u8 B = (u8)(255 * In_B); + u8 A = (u8)(255 * In_A); + + u32 Result = (u32)(A << 24 | + R << 16 | + G << 8 | + B<< 0); + return Result; } internal void ResizeBufferIfNecessary(render_command_buffer* Buffer, s32 DataSize) { - if (Buffer->CommandMemoryUsed + DataSize > Buffer->CommandMemorySize) - { - // NOTE(Peter): If this becomes a problem just go back to the original solution of - // NewSize = Buffer->CommandMemorySize + (2 * DataSize); - s32 SpaceAvailable = Buffer->CommandMemorySize - Buffer->CommandMemoryUsed; - s32 SpaceNeeded = DataSize - SpaceAvailable; // This is known to be positive at this point - s32 AdditionSize = Max(SpaceNeeded, COMMAND_BUFFER_MIN_GROW_SIZE); - s32 NewSize = Buffer->CommandMemorySize + AdditionSize; - - AllocatorFree(Buffer->Ctx.Allocator, Buffer->CommandMemory, Buffer->CommandMemorySize); - Buffer->CommandMemory = AllocatorAlloc(Buffer->Ctx.Allocator, NewSize).Memory; - Buffer->CommandMemorySize = NewSize; - } + if (Buffer->CommandMemoryUsed + DataSize > Buffer->CommandMemorySize) + { + // NOTE(Peter): If this becomes a problem just go back to the original solution of + // NewSize = Buffer->CommandMemorySize + (2 * DataSize); + s32 SpaceAvailable = Buffer->CommandMemorySize - Buffer->CommandMemoryUsed; + s32 SpaceNeeded = DataSize - SpaceAvailable; // This is known to be positive at this point + s32 AdditionSize = Max(SpaceNeeded, COMMAND_BUFFER_MIN_GROW_SIZE); + s32 NewSize = Buffer->CommandMemorySize + AdditionSize; + + Free(Buffer->Ctx.Allocator, Buffer->CommandMemory, Buffer->CommandMemorySize); + Buffer->CommandMemory = Alloc(Buffer->Ctx.Allocator, NewSize, "Renderer"); + Buffer->CommandMemorySize = NewSize; + } } // Batch @@ -276,68 +276,68 @@ ResizeBufferIfNecessary(render_command_buffer* Buffer, s32 DataSize) internal s32 PushQuad3DBatch (render_command_buffer* Buffer, render_quad_batch_constructor* Constructor, u8* MemStart, s32 TriCount, s32 DataSize, b32 UseIntegerColor = false) { - Constructor->Max = TriCount; - Constructor->Count = 0; - - Constructor->Vertecies = (v4*)(MemStart + BATCH_3D_VERTECIES_OFFSET(TriCount)); - Constructor->UVs = (v2*)(MemStart + BATCH_3D_UVS_OFFSET(TriCount)); - Constructor->ColorsV = (v4*)(MemStart + BATCH_3D_COLORS_OFFSET(TriCount)); - - Buffer->CommandMemoryUsed += DataSize; - return DataSize; + Constructor->Max = TriCount; + Constructor->Count = 0; + + Constructor->Vertecies = (v4*)(MemStart + BATCH_3D_VERTECIES_OFFSET(TriCount)); + Constructor->UVs = (v2*)(MemStart + BATCH_3D_UVS_OFFSET(TriCount)); + Constructor->ColorsV = (v4*)(MemStart + BATCH_3D_COLORS_OFFSET(TriCount)); + + Buffer->CommandMemoryUsed += DataSize; + return DataSize; } internal s32 PushQuad2DBatch (render_command_buffer* Buffer, render_quad_batch_constructor* Constructor, s32 QuadCount, s32 DataSize, u8* MemStart) { - ZeroMemoryBlock(MemStart, DataSize); - - Constructor->Max = QuadCount; - Constructor->Count = 0; - - Constructor->Vertecies = (v4*)(MemStart + BATCH_2D_VERTECIES_OFFSET(QuadCount)); - Constructor->UVs = (v2*)(MemStart + BATCH_2D_UVS_OFFSET(QuadCount)); - Constructor->ColorsV = (v4*)(MemStart + BATCH_2D_COLORS_OFFSET(QuadCount)); - - Buffer->CommandMemoryUsed += DataSize; - return DataSize; + ZeroMemoryBlock(MemStart, DataSize); + + Constructor->Max = QuadCount; + Constructor->Count = 0; + + Constructor->Vertecies = (v4*)(MemStart + BATCH_2D_VERTECIES_OFFSET(QuadCount)); + Constructor->UVs = (v2*)(MemStart + BATCH_2D_UVS_OFFSET(QuadCount)); + Constructor->ColorsV = (v4*)(MemStart + BATCH_2D_COLORS_OFFSET(QuadCount)); + + Buffer->CommandMemoryUsed += DataSize; + return DataSize; } internal s32 ThreadSafeIncrementQuadConstructorCount (render_quad_batch_constructor* Constructor) { - s32 Result = InterlockedIncrement((long*)&Constructor->Count); - // NOTE(Peter): Have to decrement the value by one. - // Interlocked Increment acts as (++Constructor->Count), not (Constructor->Count++) which - // is what we wanted; - // This was causing the first triangle to be garbage data. - Result -= 1; - return Result; + s32 Result = InterlockedIncrement((long*)&Constructor->Count); + // NOTE(Peter): Have to decrement the value by one. + // Interlocked Increment acts as (++Constructor->Count), not (Constructor->Count++) which + // is what we wanted; + // This was causing the first triangle to be garbage data. + Result -= 1; + return Result; } struct quad_batch_constructor_reserved_range { - s32 Start; - s32 OnePastLast; + s32 Start; + s32 OnePastLast; }; internal quad_batch_constructor_reserved_range ReserveRangeInQuadConstructor(render_quad_batch_constructor* Constructor, s32 TrisNeeded) { - quad_batch_constructor_reserved_range Result = {}; - Result.OnePastLast = Constructor->Count + TrisNeeded; - Constructor->Count = Result.OnePastLast; - Result.Start = Result.OnePastLast - TrisNeeded; - return Result; + quad_batch_constructor_reserved_range Result = {}; + Result.OnePastLast = Constructor->Count + TrisNeeded; + Constructor->Count = Result.OnePastLast; + Result.Start = Result.OnePastLast - TrisNeeded; + return Result; } internal quad_batch_constructor_reserved_range ThreadSafeReserveRangeInQuadConstructor(render_quad_batch_constructor* Constructor, s32 TrisNeeded) { - quad_batch_constructor_reserved_range Result = {}; - Result.OnePastLast = InterlockedAdd((long*)&Constructor->Count, TrisNeeded); - Result.Start = Result.OnePastLast - TrisNeeded; - return Result; + quad_batch_constructor_reserved_range Result = {}; + Result.OnePastLast = InterlockedAdd((long*)&Constructor->Count, TrisNeeded); + Result.Start = Result.OnePastLast - TrisNeeded; + return Result; } inline void @@ -346,22 +346,22 @@ SetTri3DInBatch (render_quad_batch_constructor* Constructor, s32 TriIndex, v2 UV0, v2 UV1, v2 UV2, v4 C0, v4 C1, v4 C2) { - //Assert(P0.w != 0 && P1.w != 0 && P2.w != 0); // Passing vectors, rather than positions. Will draw wrong - - // Vertecies - Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 0)] = P0; - Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 1)] = P1; - Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 2)] = P2; - - // UVs - Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 0)] = UV0; - Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 1)] = UV1; - Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 2)] = UV1; - - // Color V0 - Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 0)] = C0; - Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 1)] = C1; - Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 2)] = C2; + //Assert(P0.w != 0 && P1.w != 0 && P2.w != 0); // Passing vectors, rather than positions. Will draw wrong + + // Vertecies + Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 0)] = P0; + Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 1)] = P1; + Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 2)] = P2; + + // UVs + Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 0)] = UV0; + Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 1)] = UV1; + Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 2)] = UV1; + + // Color V0 + Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 0)] = C0; + Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 1)] = C1; + Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 2)] = C2; } @@ -371,18 +371,18 @@ PushTri3DOnBatch (render_quad_batch_constructor* Constructor, v2 UV0, v2 UV1, v2 UV2, v4 C0, v4 C1, v4 C2) { - DEBUG_TRACK_FUNCTION; - // TODO(Peter): I think we avoid doing cross thread filling of a batch so do we need this? - s32 Tri = ThreadSafeIncrementQuadConstructorCount(Constructor); - SetTri3DInBatch(Constructor, Tri, P0, P1, P2, UV0, UV1, UV2, C0, C1, C2); + DEBUG_TRACK_FUNCTION; + // TODO(Peter): I think we avoid doing cross thread filling of a batch so do we need this? + s32 Tri = ThreadSafeIncrementQuadConstructorCount(Constructor); + SetTri3DInBatch(Constructor, Tri, P0, P1, P2, UV0, UV1, UV2, C0, C1, C2); }; internal void PushQuad3DOnBatch (render_quad_batch_constructor* Constructor, v4 P0, v4 P1, v4 P2, v4 P3, v2 UVMin, v2 UVMax, v4 Color) { - Assert(Constructor->Count + 2 <= Constructor->Max); - PushTri3DOnBatch(Constructor, P0, P1, P2, UVMin, v2{UVMax.x, UVMin.y}, UVMax, Color, Color, Color); - PushTri3DOnBatch(Constructor, P0, P2, P3, UVMin, UVMax, v2{UVMin.x, UVMax.y}, Color, Color, Color); + Assert(Constructor->Count + 2 <= Constructor->Max); + PushTri3DOnBatch(Constructor, P0, P1, P2, UVMin, v2{UVMax.x, UVMin.y}, UVMax, Color, Color, Color); + PushTri3DOnBatch(Constructor, P0, P2, P3, UVMin, UVMax, v2{UVMin.x, UVMax.y}, Color, Color, Color); } internal void @@ -391,15 +391,15 @@ PushQuad3DOnBatch (render_quad_batch_constructor* Constructor, v2 UV0, v2 UV1, v2 UV2, v2 UV3, v4 C0, v4 C1, v4 C2, v4 C3) { - Assert(Constructor->Count <= Constructor->Max); - PushTri3DOnBatch(Constructor, P0, P1, P2, UV0, UV1, UV2, C0, C1, C2); - PushTri3DOnBatch(Constructor, P0, P2, P3, UV0, UV2, UV3, C0, C2, C3); + Assert(Constructor->Count <= Constructor->Max); + PushTri3DOnBatch(Constructor, P0, P1, P2, UV0, UV1, UV2, C0, C1, C2); + PushTri3DOnBatch(Constructor, P0, P2, P3, UV0, UV2, UV3, C0, C2, C3); } internal void PushQuad3DOnBatch (render_quad_batch_constructor* Constructor, v4 P0, v4 P1, v4 P2, v4 P3, v4 Color) { - PushQuad3DOnBatch(Constructor, P0, P1, P2, P3, v2{0, 0}, v2{1, 1}, Color); + PushQuad3DOnBatch(Constructor, P0, P1, P2, P3, v2{0, 0}, v2{1, 1}, Color); } internal void @@ -408,99 +408,99 @@ PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, v2 UV0, v2 UV1, v2 UV2, v2 UV3, v4 C0, v4 C1, v4 C2, v4 C3) { - DEBUG_TRACK_FUNCTION; - - s32 Quad = ThreadSafeIncrementQuadConstructorCount(Constructor); - v2* Vertecies = (v2*)Constructor->Vertecies; - - // Tri 1 - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 0)] = P0; - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 1)] = P1; - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 2)] = P2; - - // Tri 2 - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 0)] = P0; - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 1)] = P2; - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 2)] = P3; - - // Tri 1 UVs - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 0)] = UV0; - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 1)] = UV1; - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 2)] = UV2; - // Tri 2 UVs - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 0)] = UV0; - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 1)] = UV2; - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 2)] = UV3; - - // Tri 1 Colors - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 0)] = C0; - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 1)] = C1; - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 2)] = C2; - // Tri 2 Colors - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 0)] = C0; - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 1)] = C2; - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 2)] = C3; + DEBUG_TRACK_FUNCTION; + + s32 Quad = ThreadSafeIncrementQuadConstructorCount(Constructor); + v2* Vertecies = (v2*)Constructor->Vertecies; + + // Tri 1 + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 0)] = P0; + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 1)] = P1; + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 2)] = P2; + + // Tri 2 + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 0)] = P0; + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 1)] = P2; + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 2)] = P3; + + // Tri 1 UVs + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 0)] = UV0; + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 1)] = UV1; + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 2)] = UV2; + // Tri 2 UVs + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 0)] = UV0; + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 1)] = UV2; + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 2)] = UV3; + + // Tri 1 Colors + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 0)] = C0; + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 1)] = C1; + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 2)] = C2; + // Tri 2 Colors + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 0)] = C0; + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 1)] = C2; + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 2)] = C3; } internal void PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, v2 P2, v2 P3, v2 UVMin, v2 UVMax, v4 Color) { - DEBUG_TRACK_FUNCTION; - - s32 Quad = ThreadSafeIncrementQuadConstructorCount(Constructor); - v2* Vertecies = (v2*)Constructor->Vertecies; - - // Tri 1 - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 0)] = P0; - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 1)] = P1; - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 2)] = P2; - - // Tri 2 - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 0)] = P0; - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 1)] = P2; - Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 2)] = P3; - - // Tri 1 UVs - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 0)] = UVMin; - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 1)] = v2{UVMax.x, UVMin.y}; - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 2)] = UVMax; - // Tri 2 UVs - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 0)] = UVMin; - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 1)] = UVMax; - Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 2)] = v2{UVMin.x, UVMax.y}; - - // Tri 1 Colors - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 0)] = Color; - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 1)] = Color; - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 2)] = Color; - // Tri 2 Colors - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 0)] = Color; - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 1)] = Color; - Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 2)] = Color; + DEBUG_TRACK_FUNCTION; + + s32 Quad = ThreadSafeIncrementQuadConstructorCount(Constructor); + v2* Vertecies = (v2*)Constructor->Vertecies; + + // Tri 1 + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 0)] = P0; + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 1)] = P1; + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 2)] = P2; + + // Tri 2 + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 0)] = P0; + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 1)] = P2; + Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 2)] = P3; + + // Tri 1 UVs + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 0)] = UVMin; + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 1)] = v2{UVMax.x, UVMin.y}; + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 2)] = UVMax; + // Tri 2 UVs + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 0)] = UVMin; + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 1)] = UVMax; + Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 2)] = v2{UVMin.x, UVMax.y}; + + // Tri 1 Colors + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 0)] = Color; + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 1)] = Color; + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 2)] = Color; + // Tri 2 Colors + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 0)] = Color; + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 1)] = Color; + Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 2)] = Color; } internal void PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, v2 Min, v2 Max, v4 Color) { - PushQuad2DOnBatch(Constructor, v2{Min.x, Min.y}, v2{Max.x, Min.y}, v2{Max.x, Max.y}, v2{Min.x, Max.y}, - v2{0, 0}, v2{1, 1}, Color); + PushQuad2DOnBatch(Constructor, v2{Min.x, Min.y}, v2{Max.x, Min.y}, v2{Max.x, Max.y}, v2{Min.x, Max.y}, + v2{0, 0}, v2{1, 1}, Color); } internal void PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, rect2 Rect, v4 Color) { - PushQuad2DOnBatch(Constructor, v2{Rect.Min.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Max.y}, v2{Rect.Min.x, Rect.Max.y}, - v2{0, 0}, v2{1, 1}, Color); + PushQuad2DOnBatch(Constructor, v2{Rect.Min.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Max.y}, v2{Rect.Min.x, Rect.Max.y}, + v2{0, 0}, v2{1, 1}, Color); } internal void PushLine2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, r32 Thickness, v4 Color) { - r32 HalfThickness = Thickness / 2.0f; - v2 Perpendicular = V2Normalize(V2PerpendicularCCW(P1 - P0)) * HalfThickness; - - PushQuad2DOnBatch(Constructor, P0 - Perpendicular, P1 - Perpendicular, P1 + Perpendicular, P0 + Perpendicular, - v2{0, 0}, v2{1, 1}, Color); + r32 HalfThickness = Thickness / 2.0f; + v2 Perpendicular = V2Normalize(V2PerpendicularCCW(P1 - P0)) * HalfThickness; + + PushQuad2DOnBatch(Constructor, P0 - Perpendicular, P1 - Perpendicular, P1 + Perpendicular, P0 + Perpendicular, + v2{0, 0}, v2{1, 1}, Color); } // Commands @@ -509,174 +509,174 @@ PushLine2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, r32 internal u8* PushRenderCommand_ (render_command_buffer* CommandBuffer, render_command_type CommandType, s32 CommandSize) { - ResizeBufferIfNecessary(CommandBuffer, CommandSize); - Assert(CommandBuffer->CommandMemoryUsed + CommandSize <= CommandBuffer->CommandMemorySize); - - render_command_header* Header = (render_command_header*)(CommandBuffer->CommandMemory + CommandBuffer->CommandMemoryUsed); - Header->Type = CommandType; - - u8* Result = (u8*)(Header + 1); - CommandBuffer->CommandMemoryUsed += CommandSize; - - return Result; + ResizeBufferIfNecessary(CommandBuffer, CommandSize); + Assert(CommandBuffer->CommandMemoryUsed + CommandSize <= CommandBuffer->CommandMemorySize); + + render_command_header* Header = (render_command_header*)(CommandBuffer->CommandMemory + CommandBuffer->CommandMemoryUsed); + Header->Type = CommandType; + + u8* Result = (u8*)(Header + 1); + CommandBuffer->CommandMemoryUsed += CommandSize; + + return Result; } internal render_command_set_render_mode* PushRenderPerspective (render_command_buffer* Buffer, s32 OffsetX, s32 OffsetY, s32 ViewWidth, s32 ViewHeight, camera Camera) { - render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode); - - Command->ModelView = M44Transpose(GetCameraModelViewMatrix(Camera)); - Command->Projection = M44Transpose(GetCameraPerspectiveProjectionMatrix(Camera)); - - Command->ViewOffsetX = (r32)OffsetX; - Command->ViewOffsetY = (r32)OffsetY; - Command->ViewWidth = (r32)ViewWidth; - Command->ViewHeight = (r32)ViewHeight; - - Command->UseDepthBuffer = true; - - return Command; + render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode); + + Command->ModelView = M44Transpose(GetCameraModelViewMatrix(Camera)); + Command->Projection = M44Transpose(GetCameraPerspectiveProjectionMatrix(Camera)); + + Command->ViewOffsetX = (r32)OffsetX; + Command->ViewOffsetY = (r32)OffsetY; + Command->ViewWidth = (r32)ViewWidth; + Command->ViewHeight = (r32)ViewHeight; + + Command->UseDepthBuffer = true; + + return Command; } internal void PushRenderPerspective(render_command_buffer* Buffer, rect2 Viewport, camera Camera) { - PushRenderPerspective(Buffer, Viewport.Min.x, Viewport.Min.y, Rect2Width(Viewport), Rect2Height(Viewport), Camera); + PushRenderPerspective(Buffer, Viewport.Min.x, Viewport.Min.y, Rect2Width(Viewport), Rect2Height(Viewport), Camera); } internal void PushRenderOrthographic (render_command_buffer* Buffer, s32 OffsetX, s32 OffsetY, s32 ViewWidth, s32 ViewHeight) { - render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode); - Command->ModelView = M44Identity(); - Command->Projection = M44ProjectionOrtho((r32)ViewWidth, (r32)ViewHeight, 0, 100, ViewWidth, 0, ViewHeight, 0); - - Command->ViewOffsetX = (r32)OffsetX; - Command->ViewOffsetY = (r32)OffsetY; - Command->ViewWidth = ViewWidth; - Command->ViewHeight = ViewHeight; - - Command->UseDepthBuffer = false;; + render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode); + Command->ModelView = M44Identity(); + Command->Projection = M44ProjectionOrtho((r32)ViewWidth, (r32)ViewHeight, 0, 100, ViewWidth, 0, ViewHeight, 0); + + Command->ViewOffsetX = (r32)OffsetX; + Command->ViewOffsetY = (r32)OffsetY; + Command->ViewWidth = ViewWidth; + Command->ViewHeight = ViewHeight; + + Command->UseDepthBuffer = false;; } internal void PushRenderOrthographic(render_command_buffer* Buffer, rect2 Viewport) { - PushRenderOrthographic(Buffer, Viewport.Min.x, Viewport.Min.y, Rect2Width(Viewport), Rect2Height(Viewport)); + PushRenderOrthographic(Buffer, Viewport.Min.x, Viewport.Min.y, Rect2Width(Viewport), Rect2Height(Viewport)); } internal void PushRenderClearScreen (render_command_buffer* Buffer) { - render_command_clear_screen* Command = PushRenderCommand(Buffer, render_command_clear_screen); + render_command_clear_screen* Command = PushRenderCommand(Buffer, render_command_clear_screen); } internal render_quad_batch_constructor PushRenderQuad2DBatch(render_command_buffer* Buffer, s32 QuadCount) { - s32 DataSize = BATCH_2D_SIZE(QuadCount); - ResizeBufferIfNecessary(Buffer, DataSize + sizeof(render_batch_command_quad_2d)); - Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize); - - render_quad_batch_constructor Result = {}; - - render_batch_command_quad_2d* Command = PushRenderCommand(Buffer, render_batch_command_quad_2d); - Command->QuadCount = QuadCount; - Command->DataSize = PushQuad2DBatch(Buffer, &Result, QuadCount, DataSize, (u8*)(Command + 1)); - - return Result; + s32 DataSize = BATCH_2D_SIZE(QuadCount); + ResizeBufferIfNecessary(Buffer, DataSize + sizeof(render_batch_command_quad_2d)); + Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize); + + render_quad_batch_constructor Result = {}; + + render_batch_command_quad_2d* Command = PushRenderCommand(Buffer, render_batch_command_quad_2d); + Command->QuadCount = QuadCount; + Command->DataSize = PushQuad2DBatch(Buffer, &Result, QuadCount, DataSize, (u8*)(Command + 1)); + + return Result; } internal void PushRenderQuad2D (render_command_buffer* Buffer, v2 Min, v2 Max, v4 Color) { - render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); - PushQuad2DOnBatch(&Batch, Min, Max, Color); + render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); + PushQuad2DOnBatch(&Batch, Min, Max, Color); } internal void PushRenderQuad2D (render_command_buffer* Buffer, rect2 Rect, v4 Color) { - render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); - PushQuad2DOnBatch(&Batch, Rect.Min, Rect.Max, Color); + render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); + PushQuad2DOnBatch(&Batch, Rect.Min, Rect.Max, Color); } internal void PushRenderQuad2DClipped (render_command_buffer* Buffer, rect2 Rect, rect2 ClippingBox, v4 Color) { - rect2 Clipped = Rect2Union(Rect, ClippingBox); - render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); - PushQuad2DOnBatch(&Batch, Clipped.Min, Clipped.Max, Color); + rect2 Clipped = Rect2Union(Rect, ClippingBox); + render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); + PushQuad2DOnBatch(&Batch, Clipped.Min, Clipped.Max, Color); } internal void PushRenderQuad2D(render_command_buffer* Buffer, v2 P0, v2 P1, v2 P2, v2 P3, v4 Color) { - render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); - PushQuad2DOnBatch(&Batch, P0, P1, P2, P3, v2{0,0}, v2{1,1}, Color); + render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); + PushQuad2DOnBatch(&Batch, P0, P1, P2, P3, v2{0,0}, v2{1,1}, Color); } internal void PushRenderLine2D (render_command_buffer* Buffer, v2 P0, v2 P1, r32 Thickness, v4 Color) { - render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); - PushLine2DOnBatch(&Batch, P0, P1, Thickness, Color); + render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); + PushLine2DOnBatch(&Batch, P0, P1, Thickness, Color); } internal render_quad_batch_constructor PushRenderQuad3DBatch(render_command_buffer* Buffer, s32 QuadCount) { - s32 TriCount = QuadCount * 2; - s32 DataSize = BATCH_3D_SIZE(TriCount); - ResizeBufferIfNecessary(Buffer, DataSize + sizeof(render_batch_command_quad_3d)); - Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize); - - render_quad_batch_constructor Result = {}; - - render_batch_command_quad_3d* Command = PushRenderCommand(Buffer, render_batch_command_quad_3d); - Command->QuadCount = QuadCount; - Command->DataSize = PushQuad3DBatch(Buffer, &Result, (u8*)(Command + 1), TriCount, DataSize); - - return Result; + s32 TriCount = QuadCount * 2; + s32 DataSize = BATCH_3D_SIZE(TriCount); + ResizeBufferIfNecessary(Buffer, DataSize + sizeof(render_batch_command_quad_3d)); + Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize); + + render_quad_batch_constructor Result = {}; + + render_batch_command_quad_3d* Command = PushRenderCommand(Buffer, render_batch_command_quad_3d); + Command->QuadCount = QuadCount; + Command->DataSize = PushQuad3DBatch(Buffer, &Result, (u8*)(Command + 1), TriCount, DataSize); + + return Result; } internal void PushRenderQuad3D (render_command_buffer* Buffer, v4 A, v4 B, v4 C, v4 D, v4 Color) { - render_quad_batch_constructor Batch = PushRenderQuad3DBatch(Buffer, 1); - PushQuad3DOnBatch(&Batch, A, B, C, D, Color); + render_quad_batch_constructor Batch = PushRenderQuad3DBatch(Buffer, 1); + PushQuad3DOnBatch(&Batch, A, B, C, D, Color); } internal void PushRenderCameraFacingQuad (render_command_buffer* Buffer, v4 Center, v2 Dimensions, v4 Color) { - // TODO(Peter): Turn this into an actual camera facing quad - v4 A = v4{Center.x - Dimensions.x, Center.y - Dimensions.y, Center.z, Center.w}; - v4 B = v4{Center.x + Dimensions.x, Center.y - Dimensions.y, Center.z, Center.w}; - v4 C = v4{Center.x + Dimensions.x, Center.y + Dimensions.y, Center.z, Center.w}; - v4 D = v4{Center.x - Dimensions.x, Center.y + Dimensions.y, Center.z, Center.w}; - - PushRenderQuad3D(Buffer, A, B, C, D, Color); + // TODO(Peter): Turn this into an actual camera facing quad + v4 A = v4{Center.x - Dimensions.x, Center.y - Dimensions.y, Center.z, Center.w}; + v4 B = v4{Center.x + Dimensions.x, Center.y - Dimensions.y, Center.z, Center.w}; + v4 C = v4{Center.x + Dimensions.x, Center.y + Dimensions.y, Center.z, Center.w}; + v4 D = v4{Center.x - Dimensions.x, Center.y + Dimensions.y, Center.z, Center.w}; + + PushRenderQuad3D(Buffer, A, B, C, D, Color); } internal render_quad_batch_constructor PushRenderTexture2DBatch(render_command_buffer* Buffer, s32 QuadCount, render_texture Texture) { - s32 DataSize = BATCH_2D_SIZE(QuadCount); - ResizeBufferIfNecessary(Buffer, DataSize); - Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize); - - render_quad_batch_constructor Result = {}; - - render_batch_command_texture_2d* Command = PushRenderCommand(Buffer, render_batch_command_texture_2d); - Command->QuadCount = QuadCount; - Command->DataSize = PushQuad2DBatch(Buffer, &Result, QuadCount, DataSize, (u8*)(Command + 1)); - Command->Texture = Texture; - - return Result; + s32 DataSize = BATCH_2D_SIZE(QuadCount); + ResizeBufferIfNecessary(Buffer, DataSize); + Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize); + + render_quad_batch_constructor Result = {}; + + render_batch_command_texture_2d* Command = PushRenderCommand(Buffer, render_batch_command_texture_2d); + Command->QuadCount = QuadCount; + Command->DataSize = PushQuad2DBatch(Buffer, &Result, QuadCount, DataSize, (u8*)(Command + 1)); + Command->Texture = Texture; + + return Result; } internal render_quad_batch_constructor @@ -684,14 +684,14 @@ PushRenderTexture2DBatch (render_command_buffer* Buffer, s32 QuadCount, u8* TextureMemory, s32 TextureHandle, s32 TextureWidth, s32 TextureHeight, s32 TextureBytesPerPixel, s32 TextureStride) { - render_texture Texture = render_texture{ - TextureMemory, - TextureHandle, - TextureWidth, - TextureHeight, - TextureBytesPerPixel, - TextureStride}; - return PushRenderTexture2DBatch(Buffer, QuadCount, Texture); + render_texture Texture = render_texture{ + TextureMemory, + TextureHandle, + TextureWidth, + TextureHeight, + TextureBytesPerPixel, + TextureStride}; + return PushRenderTexture2DBatch(Buffer, QuadCount, Texture); } internal void @@ -699,19 +699,19 @@ PushRenderTexture2D (render_command_buffer* Buffer, v2 Min, v2 Max, v4 Color, v2 UVMin, v2 UVMax, render_texture* Texture) { - render_quad_batch_constructor Batch = PushRenderTexture2DBatch(Buffer, 1, *Texture); - PushQuad2DOnBatch(&Batch, v2{Min.x, Min.y}, v2{Max.x, Min.y}, v2{Max.x, Max.y}, v2{Min.x, Max.y}, - UVMin, UVMax, Color); + render_quad_batch_constructor Batch = PushRenderTexture2DBatch(Buffer, 1, *Texture); + PushQuad2DOnBatch(&Batch, v2{Min.x, Min.y}, v2{Max.x, Min.y}, v2{Max.x, Max.y}, v2{Min.x, Max.y}, + UVMin, UVMax, Color); } internal void PushRenderBoundingBox2D (render_command_buffer* Buffer, v2 Min, v2 Max, r32 Thickness, v4 Color) { - render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 4); - PushQuad2DOnBatch(&Batch, Min, v2{Min.x + Thickness, Max.y}, Color); - PushQuad2DOnBatch(&Batch, v2{Min.x, Max.y - Thickness}, Max, Color); - PushQuad2DOnBatch(&Batch, v2{Max.x - Thickness, Min.y}, Max, Color); - PushQuad2DOnBatch(&Batch, Min, v2{Max.x, Min.y + Thickness}, Color); + render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 4); + PushQuad2DOnBatch(&Batch, Min, v2{Min.x + Thickness, Max.y}, Color); + PushQuad2DOnBatch(&Batch, v2{Min.x, Max.y - Thickness}, Max, Color); + PushQuad2DOnBatch(&Batch, v2{Max.x - Thickness, Min.y}, Max, Color); + PushQuad2DOnBatch(&Batch, Min, v2{Max.x, Min.y + Thickness}, Color); } diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index bb3edde..4a4752d 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -40,8 +40,8 @@ window MainWindow; PLATFORM_GET_GPU_TEXTURE_HANDLE(Win32GetGPUTextureHandle) { - s32 Handle = SubmitTexture(Memory, Width, Height); - return Handle; + s32 Handle = SubmitTexture(Memory, Width, Height); + return Handle; } HDC FontDrawingDC; @@ -50,462 +50,462 @@ HFONT CurrentFont; GET_FONT_INFO(Win32GetFontInfo) { - platform_font_info Result = {}; - - FontDrawingDC = CreateCompatibleDC(NULL); - SetBkColor(FontDrawingDC, RGB(0, 0, 0)); - SetTextColor(FontDrawingDC, RGB(255, 255, 255)); - FontBitmap = CreateCompatibleBitmap(FontDrawingDC, PixelHeight * 2, PixelHeight * 2); - HGDIOBJ SelectObjectResult = SelectObject(FontDrawingDC, FontBitmap); - - CurrentFont = CreateFont(PixelHeight, 0, 0, 0, - FontWeight, - Italic, - Underline, - Strikeout, - ANSI_CHARSET, - OUT_OUTLINE_PRECIS, - CLIP_DEFAULT_PRECIS, - PROOF_QUALITY, - FIXED_PITCH, - FontName); - SelectFont(FontDrawingDC, CurrentFont); - - TEXTMETRIC WindowsFontMetrics = {}; - if (GetTextMetrics(FontDrawingDC, &WindowsFontMetrics)) - { - Result.PixelHeight = WindowsFontMetrics.tmHeight; - Result.Ascent = WindowsFontMetrics.tmAscent; - Result.Descent = WindowsFontMetrics.tmDescent; - Result.Leading = WindowsFontMetrics.tmExternalLeading; - Result.MaxCharWidth = WindowsFontMetrics.tmMaxCharWidth; - Result.CodepointStart = WindowsFontMetrics.tmFirstChar; - Result.CodepointOnePastLast = WindowsFontMetrics.tmLastChar + 1; - } - - return Result; + platform_font_info Result = {}; + + FontDrawingDC = CreateCompatibleDC(NULL); + SetBkColor(FontDrawingDC, RGB(0, 0, 0)); + SetTextColor(FontDrawingDC, RGB(255, 255, 255)); + FontBitmap = CreateCompatibleBitmap(FontDrawingDC, PixelHeight * 2, PixelHeight * 2); + HGDIOBJ SelectObjectResult = SelectObject(FontDrawingDC, FontBitmap); + + CurrentFont = CreateFont(PixelHeight, 0, 0, 0, + FontWeight, + Italic, + Underline, + Strikeout, + ANSI_CHARSET, + OUT_OUTLINE_PRECIS, + CLIP_DEFAULT_PRECIS, + PROOF_QUALITY, + FIXED_PITCH, + FontName); + SelectFont(FontDrawingDC, CurrentFont); + + TEXTMETRIC WindowsFontMetrics = {}; + if (GetTextMetrics(FontDrawingDC, &WindowsFontMetrics)) + { + Result.PixelHeight = WindowsFontMetrics.tmHeight; + Result.Ascent = WindowsFontMetrics.tmAscent; + Result.Descent = WindowsFontMetrics.tmDescent; + Result.Leading = WindowsFontMetrics.tmExternalLeading; + Result.MaxCharWidth = WindowsFontMetrics.tmMaxCharWidth; + Result.CodepointStart = WindowsFontMetrics.tmFirstChar; + Result.CodepointOnePastLast = WindowsFontMetrics.tmLastChar + 1; + } + + return Result; } DRAW_FONT_CODEPOINT(Win32DrawFontCodepoint) { - SIZE CodepointSize = {}; - if (GetTextExtentPoint32(FontDrawingDC, &Codepoint, 1, &CodepointSize)) + SIZE CodepointSize = {}; + if (GetTextExtentPoint32(FontDrawingDC, &Codepoint, 1, &CodepointSize)) + { + *OutWidth = CodepointSize.cx; + *OutHeight = CodepointSize.cy; + + RECT TextRect = {}; + TextRect.left = 0; + TextRect.right = *OutWidth; + TextRect.top = 0; + TextRect.bottom = *OutHeight; + + int Error = DrawText(FontDrawingDC, &Codepoint, 1, &TextRect, DT_LEFT | DT_NOCLIP | DT_TOP); + + u8* Row = DestBuffer + (YOffset * (DestBufferWidth * 4)); + COLORREF PixelColor; + for (u32 Y = 0; Y < *OutHeight; Y++) { - *OutWidth = CodepointSize.cx; - *OutHeight = CodepointSize.cy; - - RECT TextRect = {}; - TextRect.left = 0; - TextRect.right = *OutWidth; - TextRect.top = 0; - TextRect.bottom = *OutHeight; - - int Error = DrawText(FontDrawingDC, &Codepoint, 1, &TextRect, DT_LEFT | DT_NOCLIP | DT_TOP); - - u8* Row = DestBuffer + (YOffset * (DestBufferWidth * 4)); - COLORREF PixelColor; - for (u32 Y = 0; Y < *OutHeight; Y++) - { - // NOTE(Peter): XOffset * 4 b/c its 4 bytes per pixel. - u8* Channel = (u8*)Row + (XOffset * 4); - for (u32 X = 0; X < *OutWidth; X++) - { - PixelColor = GetPixel(FontDrawingDC, X + TextRect.left, TextRect.bottom - Y); - Assert(PixelColor != CLR_INVALID); - u8 RValue = GetRValue(PixelColor); - *Channel++ = RValue; - *Channel++ = RValue; - *Channel++ = RValue; - *Channel++ = RValue; - } - Row += DestBufferWidth * 4; - } - + // NOTE(Peter): XOffset * 4 b/c its 4 bytes per pixel. + u8* Channel = (u8*)Row + (XOffset * 4); + for (u32 X = 0; X < *OutWidth; X++) + { + PixelColor = GetPixel(FontDrawingDC, X + TextRect.left, TextRect.bottom - Y); + Assert(PixelColor != CLR_INVALID); + u8 RValue = GetRValue(PixelColor); + *Channel++ = RValue; + *Channel++ = RValue; + *Channel++ = RValue; + *Channel++ = RValue; + } + Row += DestBufferWidth * 4; } + + } } LRESULT CALLBACK HandleWindowEvents (HWND WindowHandle, UINT Msg, WPARAM WParam, LPARAM LParam) { - LRESULT Result = 0; - - switch (Msg) + LRESULT Result = 0; + + switch (Msg) + { + case WM_SIZE: { - case WM_SIZE: - { - Win32UpdateWindowDimension(&MainWindow); - //Win32ResizeDIBSection(&GlobalBackbuffer, MainWindow.Info.Width, MainWindow.Info.Height); - }break; - - case WM_CLOSE: - { - Result = DefWindowProc(WindowHandle, Msg, WParam, LParam); - Running = false; - }break; - - case WM_DESTROY: - { - }break; - - case WM_PAINT: - { - PAINTSTRUCT PaintStruct; - HDC DeviceContext; - b32 PaintResult; - - DeviceContext = BeginPaint(WindowHandle, &PaintStruct); - PaintResult = EndPaint(WindowHandle, &PaintStruct); - }break; - - case WM_ACTIVATE: - { - WindowIsActive = (LOWORD(WParam) == WA_ACTIVE || LOWORD(WParam) == WA_CLICKACTIVE); - }break; - - default: - { - Result = DefWindowProc(WindowHandle, Msg, WParam, LParam); - } - } + Win32UpdateWindowDimension(&MainWindow); + //Win32ResizeDIBSection(&GlobalBackbuffer, MainWindow.Info.Width, MainWindow.Info.Height); + }break; - return Result; + case WM_CLOSE: + { + Result = DefWindowProc(WindowHandle, Msg, WParam, LParam); + Running = false; + }break; + + case WM_DESTROY: + { + }break; + + case WM_PAINT: + { + PAINTSTRUCT PaintStruct; + HDC DeviceContext; + b32 PaintResult; + + DeviceContext = BeginPaint(WindowHandle, &PaintStruct); + PaintResult = EndPaint(WindowHandle, &PaintStruct); + }break; + + case WM_ACTIVATE: + { + WindowIsActive = (LOWORD(WParam) == WA_ACTIVE || LOWORD(WParam) == WA_CLICKACTIVE); + }break; + + default: + { + Result = DefWindowProc(WindowHandle, Msg, WParam, LParam); + } + } + + return Result; } internal void HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse_state* Mouse) { - switch (Message.message) + switch (Message.message) + { + case WM_MOUSEWHEEL: + { + Mouse->Scroll = GET_WHEEL_DELTA_WPARAM(Message.wParam); + }break; + + case WM_LBUTTONDOWN: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, false, true, + ShiftDown, AltDown, CtrlDown, false); + + Mouse->LeftButtonState |= KeyState_IsDown; + Mouse->DownPos = Mouse->Pos; + + // :Win32MouseEventCapture + // 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(Window->Handle); + }break; + + case WM_MBUTTONDOWN: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseMiddleButton, false, true, + ShiftDown, AltDown, CtrlDown, false); + Mouse->MiddleButtonState = KeyState_IsDown & ~KeyState_WasDown; + + // :Win32MouseEventCapture + SetCapture(Window->Handle); + }break; + + case WM_RBUTTONDOWN: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, false, true, + ShiftDown, AltDown, CtrlDown, false); + Mouse->RightButtonState = KeyState_IsDown & ~KeyState_WasDown; + Mouse->DownPos = Mouse->Pos; + + // :Win32MouseEventCapture + SetCapture(Window->Handle); + }break; + + case WM_LBUTTONUP: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, true, false, + ShiftDown, AltDown, CtrlDown, false); + Mouse->LeftButtonState &= ~KeyState_IsDown; + + // :Win32MouseEventCapture + ReleaseCapture(); + }break; + + case WM_MBUTTONUP: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseMiddleButton, true, false, + ShiftDown, AltDown, CtrlDown, false); + Mouse->MiddleButtonState = ~KeyState_IsDown & KeyState_WasDown; + + // :Win32MouseEventCapture + ReleaseCapture(); + }break; + + case WM_RBUTTONUP: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, true, false, + ShiftDown, AltDown, CtrlDown, false); + Mouse->RightButtonState = ~KeyState_IsDown & KeyState_WasDown; + + // :Win32MouseEventCapture + ReleaseCapture(); + }break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: { - case WM_MOUSEWHEEL: - { - Mouse->Scroll = GET_WHEEL_DELTA_WPARAM(Message.wParam); - }break; - - case WM_LBUTTONDOWN: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, false, true, - ShiftDown, AltDown, CtrlDown, false); - - Mouse->LeftButtonState |= KeyState_IsDown; - Mouse->DownPos = Mouse->Pos; - - // :Win32MouseEventCapture - // 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(Window->Handle); - }break; - - case WM_MBUTTONDOWN: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseMiddleButton, false, true, - ShiftDown, AltDown, CtrlDown, false); - Mouse->MiddleButtonState = KeyState_IsDown & ~KeyState_WasDown; - - // :Win32MouseEventCapture - SetCapture(Window->Handle); - }break; - - case WM_RBUTTONDOWN: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, false, true, - ShiftDown, AltDown, CtrlDown, false); - Mouse->RightButtonState = KeyState_IsDown & ~KeyState_WasDown; - Mouse->DownPos = Mouse->Pos; - - // :Win32MouseEventCapture - SetCapture(Window->Handle); - }break; - - case WM_LBUTTONUP: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, true, false, - ShiftDown, AltDown, CtrlDown, false); - Mouse->LeftButtonState &= ~KeyState_IsDown; - - // :Win32MouseEventCapture - ReleaseCapture(); - }break; - - case WM_MBUTTONUP: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseMiddleButton, true, false, - ShiftDown, AltDown, CtrlDown, false); - Mouse->MiddleButtonState = ~KeyState_IsDown & KeyState_WasDown; - - // :Win32MouseEventCapture - ReleaseCapture(); - }break; - - case WM_RBUTTONUP: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, true, false, - ShiftDown, AltDown, CtrlDown, false); - Mouse->RightButtonState = ~KeyState_IsDown & KeyState_WasDown; - - // :Win32MouseEventCapture - ReleaseCapture(); - }break; - - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - case WM_KEYDOWN: - case WM_KEYUP: - { #if 0 - int VirtualKey = (int)Message.wParam; - key_code Key = Win32GetKeyCode(VirtualKey, true, false); - s32 KeyIndex = (int)Key; - - b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0; - b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0; - - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - // New Input Queue - AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown, - ShiftDown, AltDown, CtrlDown, false); + int VirtualKey = (int)Message.wParam; + key_code Key = Win32GetKeyCode(VirtualKey, true, false); + s32 KeyIndex = (int)Key; + + b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0; + b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0; + + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + // New Input Queue + AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown, + ShiftDown, AltDown, CtrlDown, false); #endif - TranslateMessage(&Message); - DispatchMessage(&Message); - }break; - - case WM_CHAR: - { - char VirtualKey = (char)Message.wParam; - key_code Key = CharToKeyCode(VirtualKey); - s32 KeyIndex = (int)Key; - - b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0; - b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0; - - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - // New Input Queue - AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown, - ShiftDown, AltDown, CtrlDown, false); - }break; - - default: - { - TranslateMessage(&Message); - DispatchMessage(&Message); - }break; - } + TranslateMessage(&Message); + DispatchMessage(&Message); + }break; + + case WM_CHAR: + { + char VirtualKey = (char)Message.wParam; + key_code Key = CharToKeyCode(VirtualKey); + s32 KeyIndex = (int)Key; + + b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0; + b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0; + + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + // New Input Queue + AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown, + ShiftDown, AltDown, CtrlDown, false); + }break; + + default: + { + TranslateMessage(&Message); + DispatchMessage(&Message); + }break; + } } internal void DebugPrint (char* Format, ...) { - char Buffer[256]; - gs_string StringBuffer = MakeString(Buffer, 256); - va_list Args; - va_start(Args, Format); - Log_PrintFVarArgs(GlobalLogBuffer, LogEntry_Message, Format, Args); - va_end(Args); + char Buffer[256]; + gs_string StringBuffer = MakeString(Buffer, 256); + va_list Args; + va_start(Args, Format); + Log_PrintFVarArgs(GlobalLogBuffer, LogEntry_Message, Format, Args); + va_end(Args); } internal void SetApplicationLinks (context* Context, win32_dll_refresh DLL, gs_work_queue* WorkQueue) { - if (DLL.IsValid) - { - Context->InitializeApplication = (initialize_application*)GetProcAddress(DLL.DLL, "InitializeApplication"); - Context->ReloadStaticData = (reload_static_data*)GetProcAddress(DLL.DLL, "ReloadStaticData"); - Context->UpdateAndRender = (update_and_render*)GetProcAddress(DLL.DLL, "UpdateAndRender"); - Context->CleanupApplication = (cleanup_application*)GetProcAddress(DLL.DLL, "CleanupApplication"); - } - else - { - Context->InitializeApplication = 0; - Context->ReloadStaticData = 0; - Context->UpdateAndRender = 0; - Context->CleanupApplication = 0; - } + if (DLL.IsValid) + { + Context->InitializeApplication = (initialize_application*)GetProcAddress(DLL.DLL, "InitializeApplication"); + Context->ReloadStaticData = (reload_static_data*)GetProcAddress(DLL.DLL, "ReloadStaticData"); + Context->UpdateAndRender = (update_and_render*)GetProcAddress(DLL.DLL, "UpdateAndRender"); + Context->CleanupApplication = (cleanup_application*)GetProcAddress(DLL.DLL, "CleanupApplication"); + } + else + { + Context->InitializeApplication = 0; + Context->ReloadStaticData = 0; + Context->UpdateAndRender = 0; + Context->CleanupApplication = 0; + } } internal void Win32_SendAddressedDataBuffer(gs_thread_context Context, addressed_data_buffer* BufferAt) { - DEBUG_TRACK_FUNCTION; - - u32 BuffersSent = 0; - u32 DataSizeSent = 0; - - switch(BufferAt->AddressType) + DEBUG_TRACK_FUNCTION; + + u32 BuffersSent = 0; + u32 DataSizeSent = 0; + + switch(BufferAt->AddressType) + { + case AddressType_NetworkIP: { - case AddressType_NetworkIP: + Win32Socket_SendTo(BufferAt->SendSocket, + BufferAt->V4SendAddress, + BufferAt->SendPort, + (const char*)BufferAt->Memory, + BufferAt->MemorySize, + 0); + }break; + + case AddressType_ComPort: + { + if (BufferAt->ComPort.Length > 0) + { + HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 2000000, 8, NOPARITY, 1, Context.Transient); + if (SerialPort != INVALID_HANDLE_VALUE) { - Win32Socket_SendTo(BufferAt->SendSocket, - BufferAt->V4SendAddress, - BufferAt->SendPort, - (const char*)BufferAt->Memory, - BufferAt->MemorySize, - 0); - }break; - - case AddressType_ComPort: - { - if (BufferAt->ComPort.Length > 0) - { - HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 2000000, 8, NOPARITY, 1, Context.Transient); - if (SerialPort != INVALID_HANDLE_VALUE) - { - if (Win32SerialPort_Write(SerialPort, BufferAt->Data)) - { - BuffersSent += 1; - DataSizeSent += BufferAt->Data.Size; - } - else - { - Win32SerialArray_Close(BufferAt->ComPort); - } - } - } - else - { + if (Win32SerialPort_Write(SerialPort, BufferAt->Data)) + { + BuffersSent += 1; + DataSizeSent += BufferAt->Data.Size; + } + else + { + Win32SerialArray_Close(BufferAt->ComPort); + } + } + } + else + { #if 0 - Log_Message(GlobalLogBuffer, - "Skipping data buffer because its COM Port isn't set"); + Log_Message(GlobalLogBuffer, + "Skipping data buffer because its COM Port isn't set"); #endif - } - }break; - - InvalidDefaultCase; - } + } + }break; + + InvalidDefaultCase; + } } internal void Win32_SendAddressedDataBuffer_Job(gs_thread_context Context, gs_data Arg) { - addressed_data_buffer* OutputData = (addressed_data_buffer*)Arg.Memory; - Win32_SendAddressedDataBuffer(Context, OutputData); + addressed_data_buffer* OutputData = (addressed_data_buffer*)Arg.Memory; + Win32_SendAddressedDataBuffer(Context, OutputData); } internal bool ReloadAndLinkDLL(win32_dll_refresh* DLL, context* Context, gs_work_queue* WorkQueue, bool ShouldError, bool AppReady) { - bool Success = false; - if (HotLoadDLL(DLL)) - { - SetApplicationLinks(Context, *DLL, WorkQueue); - Context->ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, AppReady); - Success = true; - Log_Message(GlobalLogBuffer, "Reloaded DLL\n"); - } - else if(ShouldError) - { - Log_Error(GlobalLogBuffer, "Unable to load application DLL at startup.\nAborting\n"); - } - return Success; + bool Success = false; + if (HotLoadDLL(DLL)) + { + SetApplicationLinks(Context, *DLL, WorkQueue); + Context->ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, AppReady); + Success = true; + Log_Message(GlobalLogBuffer, "Reloaded DLL\n"); + } + else if(ShouldError) + { + Log_Error(GlobalLogBuffer, "Unable to load application DLL at startup.\nAborting\n"); + } + return Success; } internal gs_const_string GetExePath(HINSTANCE HInstance, gs_thread_context ThreadContext) { - gs_const_string Result = {}; - - u32 Error = 0; - u32 PathSize = MAX_PATH; - char* Path = PushArray(ThreadContext.Transient, char, PathSize); - DWORD Length = GetModuleFileNameA(HInstance, Path, PathSize); - - if (Length) - { - Error = GetLastError(); - if (Error == ERROR_INSUFFICIENT_BUFFER) { - // PathSize wasn't long enough - // TODO(pjs): handle this case - InvalidCodePath; - } - - Result.Str = Path; - Result.Length = (u64)Length; - } - else - { - Error = GetLastError(); - InvalidCodePath; + gs_const_string Result = {}; + + u32 Error = 0; + u32 PathSize = MAX_PATH; + char* Path = PushArray(ThreadContext.Transient, char, PathSize); + DWORD Length = GetModuleFileNameA(HInstance, Path, PathSize); + + if (Length) + { + Error = GetLastError(); + if (Error == ERROR_INSUFFICIENT_BUFFER) { + // PathSize wasn't long enough + // TODO(pjs): handle this case + InvalidCodePath; } - return Result; + Result.Str = Path; + Result.Length = (u64)Length; + } + else + { + Error = GetLastError(); + InvalidCodePath; + } + + return Result; } internal bool SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) { - bool Result = false; + bool Result = false; + + gs_const_string ExePath = GetExePath(HInstance, ThreadContext); + gs_string ScratchPath = PushString(ThreadContext.Transient, ExePath.Length + 128); + gs_string WorkingDirectory = PushString(ThreadContext.Transient, ExePath.Length + 128); + + while (WorkingDirectory.Length == 0) + { + s64 LastSlash = FindLastFromSet(ExePath, "\\/"); + if (LastSlash < 0) break; - gs_const_string ExePath = GetExePath(HInstance, ThreadContext); - gs_string ScratchPath = PushString(ThreadContext.Transient, ExePath.Length + 128); - gs_string WorkingDirectory = PushString(ThreadContext.Transient, ExePath.Length + 128); + ExePath = Substring(ExePath, 0, LastSlash); + PrintF(&ScratchPath, "%S\\data", ExePath); + NullTerminate(&ScratchPath); - while (WorkingDirectory.Length == 0) - { - s64 LastSlash = FindLastFromSet(ExePath, "\\/"); - if (LastSlash < 0) break; - - ExePath = Substring(ExePath, 0, LastSlash); - PrintF(&ScratchPath, "%S\\data", ExePath); - NullTerminate(&ScratchPath); - - gs_file_info PathInfo = GetFileInfo(ThreadContext.FileHandler, ScratchPath.ConstString); - if (PathInfo.Path.Length > 0 && - PathInfo.IsDirectory) { - PrintF(&WorkingDirectory, "%S", ExePath); - NullTerminate(&WorkingDirectory); - } + gs_file_info PathInfo = GetFileInfo(ThreadContext.FileHandler, ScratchPath.ConstString); + if (PathInfo.Path.Length > 0 && + PathInfo.IsDirectory) { + PrintF(&WorkingDirectory, "%S", ExePath); + NullTerminate(&WorkingDirectory); } - - if (WorkingDirectory.Length > 0) + } + + if (WorkingDirectory.Length > 0) + { + Log_Message(GlobalLogBuffer, + "Setting Working Directory \n%S \n", + WorkingDirectory.ConstString); + Result = SetCurrentDirectory(WorkingDirectory.Str); + if (!Result) { - Log_Message(GlobalLogBuffer, - "Setting Working Directory \n%S \n", - WorkingDirectory.ConstString); - Result = SetCurrentDirectory(WorkingDirectory.Str); - if (!Result) - { - u32 Error = GetLastError(); - InvalidCodePath; - } + u32 Error = GetLastError(); + InvalidCodePath; } - else - { - Log_Error(GlobalLogBuffer, "Error, no data folder found\n"); - } - - return Result; + } + else + { + Log_Error(GlobalLogBuffer, "Error, no data folder found\n"); + } + + return Result; } #include "../../gs_libs/gs_path.h" @@ -513,67 +513,67 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) internal void Win32_SendOutputData(gs_thread_context ThreadContext, addressed_data_buffer_list OutputData) { - bool Multithread = true; - if (Multithread) + bool Multithread = true; + if (Multithread) + { + for (addressed_data_buffer* At = OutputData.Root; + At != 0; + At = At->Next) { - for (addressed_data_buffer* At = OutputData.Root; - At != 0; - At = At->Next) - { - gs_data ProcArg = {}; - ProcArg.Memory = (u8*)At; - ProcArg.Size = sizeof(addressed_data_buffer); - Win32PushWorkOnQueue(&Win32WorkQueue.WorkQueue, Win32_SendAddressedDataBuffer_Job, ProcArg, ConstString("Send UART Data")); - } + gs_data ProcArg = {}; + ProcArg.Memory = (u8*)At; + ProcArg.Size = sizeof(addressed_data_buffer); + Win32PushWorkOnQueue(&Win32WorkQueue.WorkQueue, Win32_SendAddressedDataBuffer_Job, ProcArg, ConstString("Send UART Data")); } - else + } + else + { + for (addressed_data_buffer* At = OutputData.Root; + At != 0; + At = At->Next) { - for (addressed_data_buffer* At = OutputData.Root; - At != 0; - At = At->Next) - { - gs_data ProcArg = {}; - ProcArg.Memory = (u8*)At; - ProcArg.Size = sizeof(addressed_data_buffer); - Win32_SendAddressedDataBuffer_Job(ThreadContext, ProcArg); - } + gs_data ProcArg = {}; + ProcArg.Memory = (u8*)At; + ProcArg.Size = sizeof(addressed_data_buffer); + Win32_SendAddressedDataBuffer_Job(ThreadContext, ProcArg); } - + } + } // Time internal system_time Win32GetSystemTime() { - system_time Result = {}; + system_time Result = {}; + + SYSTEMTIME WinLocalTime; + GetLocalTime(&WinLocalTime); + + SYSTEMTIME WinSysTime; + FILETIME WinSysFileTime; + GetSystemTime(&WinSysTime); + if (SystemTimeToFileTime((const SYSTEMTIME*)&WinSysTime, &WinSysFileTime)) + { + ULARGE_INTEGER SysTime = {}; + SysTime.LowPart = WinSysFileTime.dwLowDateTime; + SysTime.HighPart = WinSysFileTime.dwHighDateTime; - SYSTEMTIME WinLocalTime; - GetLocalTime(&WinLocalTime); - - SYSTEMTIME WinSysTime; - FILETIME WinSysFileTime; - GetSystemTime(&WinSysTime); - if (SystemTimeToFileTime((const SYSTEMTIME*)&WinSysTime, &WinSysFileTime)) - { - ULARGE_INTEGER SysTime = {}; - SysTime.LowPart = WinSysFileTime.dwLowDateTime; - SysTime.HighPart = WinSysFileTime.dwHighDateTime; - - Result.NanosSinceEpoch = SysTime.QuadPart; - Result.Year = WinLocalTime.wYear; - Result.Month = WinLocalTime.wMonth; - Result.Day = WinLocalTime.wDay; - Result.Hour = WinLocalTime.wHour; - Result.Minute = WinLocalTime.wMinute; - Result.Second = WinLocalTime.wSecond; - } - else - { - u32 Error = GetLastError(); - InvalidCodePath; - } - - return Result; + Result.NanosSinceEpoch = SysTime.QuadPart; + Result.Year = WinLocalTime.wYear; + Result.Month = WinLocalTime.wMonth; + Result.Day = WinLocalTime.wDay; + Result.Hour = WinLocalTime.wHour; + Result.Minute = WinLocalTime.wMinute; + Result.Second = WinLocalTime.wSecond; + } + else + { + u32 Error = GetLastError(); + InvalidCodePath; + } + + return Result; } int WINAPI @@ -584,203 +584,202 @@ WinMain ( INT NCmdShow ) { - gs_thread_context ThreadContext = Win32CreateThreadContext(); - GlobalLogBuffer = AllocatorAllocStruct(ThreadContext.Allocator, log_buffer); - *GlobalLogBuffer = Log_Init(ThreadContext.Allocator, 32); + gs_thread_context ThreadContext = Win32CreateThreadContext(); + + gs_memory_arena PlatformPermanent = MemoryArenaCreate(MB(4), + Bytes(8), ThreadContext.Allocator, + 0, + 0, + "Platform Memory"); + + GlobalLogBuffer = AllocStruct(ThreadContext.Allocator, log_buffer, "Global Log Buffer"); + *GlobalLogBuffer = Log_Init(&PlatformPermanent, 32); + + if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; + + context Context = {}; + Context.ThreadContext = ThreadContext; + Context.SystemTime_Current = Win32GetSystemTime(); + + gs_const_string Args = ConstString((char*)CmdLineArgs); + if (StringsEqual(Args, ConstString("-headless"))) + { + Log_Message(GlobalLogBuffer, "Running Headless\n"); + Context.Headless = true; + } + + if (!Context.Headless) + { + MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); + Win32UpdateWindowDimension(&MainWindow); - gs_allocator_debug AllocDebug = {}; - AllocDebug.AllocationsCountMax = 4096; - AllocDebug.Allocations = AllocatorAllocArray(ThreadContext.Allocator, gs_debug_allocation, AllocDebug.AllocationsCountMax); - - ThreadContext.Allocator.Debug = &AllocDebug; - - if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; - - context Context = {}; - Context.ThreadContext = ThreadContext; - Context.SystemTime_Current = Win32GetSystemTime(); - - gs_const_string Args = ConstString((char*)CmdLineArgs); - if (StringsEqual(Args, ConstString("-headless"))) - { - Log_Message(GlobalLogBuffer, "Running Headless\n"); - Context.Headless = true; - } - - if (!Context.Headless) - { - MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); - Win32UpdateWindowDimension(&MainWindow); - - win32_opengl_window_info OpenGLWindowInfo = {}; - OpenGLWindowInfo.ColorBits = 32; - OpenGLWindowInfo.AlphaBits = 8; - OpenGLWindowInfo.DepthBits = 0; - CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow); - } - - gs_memory_arena PlatformPermanent = CreateMemoryArena(Context.ThreadContext.Allocator, "Platform Memory"); - - s64 PerformanceCountFrequency = GetPerformanceFrequency(); - s64 LastFrameEnd = GetWallClock(); - r32 TargetSecondsPerFrame = 1 / 30.0f; - r32 LastFrameSecondsElapsed = 0.0f; - - GlobalDebugServices = PushStruct(&PlatformPermanent, debug_services); + win32_opengl_window_info OpenGLWindowInfo = {}; + OpenGLWindowInfo.ColorBits = 32; + OpenGLWindowInfo.AlphaBits = 8; + OpenGLWindowInfo.DepthBits = 0; + CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow); + } + + s64 PerformanceCountFrequency = GetPerformanceFrequency(); + s64 LastFrameEnd = GetWallClock(); + r32 TargetSecondsPerFrame = 1 / 30.0f; + r32 LastFrameSecondsElapsed = 0.0f; + + GlobalDebugServices = PushStruct(&PlatformPermanent, debug_services); #if DEBUG - InitDebugServices_DebugMode(GlobalDebugServices, - PerformanceCountFrequency, - GetWallClock, - Win32GetThreadId, - Context.ThreadContext, - PLATFORM_THREAD_COUNT + 1); -#else - InitDebugServices_OffMode(GlobalDebugServices, + InitDebugServices_DebugMode(GlobalDebugServices, PerformanceCountFrequency, GetWallClock, Win32GetThreadId, Context.ThreadContext, PLATFORM_THREAD_COUNT + 1); +#else + InitDebugServices_OffMode(GlobalDebugServices, + PerformanceCountFrequency, + GetWallClock, + Win32GetThreadId, + Context.ThreadContext, + PLATFORM_THREAD_COUNT + 1); #endif - - input_queue InputQueue = InputQueue_Create(&PlatformPermanent, 32); - - Win32WorkQueue_Init(&PlatformPermanent, PLATFORM_THREAD_COUNT); - - // Platform functions - Context.GeneralWorkQueue = &Win32WorkQueue.WorkQueue; - Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle; - Context.PlatformGetSocketHandle = Win32GetSocketHandle; - Context.PlatformGetFontInfo = Win32GetFontInfo; - Context.PlatformDrawFontCodepoint = Win32DrawFontCodepoint; - - Context.ThreadManager = PushStruct(&PlatformPermanent, platform_thread_manager); - *Context.ThreadManager = CreatePlatformThreadManager(Win32CreateThread, Win32KillThread); - - Context.SocketManager = PushStruct(&PlatformPermanent, platform_socket_manager); - *Context.SocketManager = CreatePlatformSocketManager(Win32ConnectSocket, Win32CloseSocket, Win32SocketQueryStatus, Win32SocketPeek, Win32SocketReceive, Win32SocketSend); - - win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName); - if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true, false)) { return -1; } - - Mouse_Init(); - - Win32SocketSystem_Init(&PlatformPermanent); - - Win32SerialArray_Create(ThreadContext); - - render_command_buffer RenderBuffer = {}; - if (!Context.Headless) + + input_queue InputQueue = InputQueue_Create(&PlatformPermanent, 32); + + Win32WorkQueue_Init(&PlatformPermanent, PLATFORM_THREAD_COUNT); + + // Platform functions + Context.GeneralWorkQueue = &Win32WorkQueue.WorkQueue; + Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle; + Context.PlatformGetSocketHandle = Win32GetSocketHandle; + Context.PlatformGetFontInfo = Win32GetFontInfo; + Context.PlatformDrawFontCodepoint = Win32DrawFontCodepoint; + + Context.ThreadManager = PushStruct(&PlatformPermanent, platform_thread_manager); + *Context.ThreadManager = CreatePlatformThreadManager(Win32CreateThread, Win32KillThread); + + Context.SocketManager = PushStruct(&PlatformPermanent, platform_socket_manager); + *Context.SocketManager = CreatePlatformSocketManager(Win32ConnectSocket, Win32CloseSocket, Win32SocketQueryStatus, Win32SocketPeek, Win32SocketReceive, Win32SocketSend); + + win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName); + if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true, false)) { return -1; } + + Mouse_Init(); + + Win32SocketSystem_Init(&PlatformPermanent); + + Win32SerialArray_Create(&PlatformPermanent); + + render_command_buffer RenderBuffer = {}; + if (!Context.Headless) + { + RenderBuffer = AllocateRenderCommandBuffer(MB(12), &PlatformPermanent, ThreadContext); + } + + addressed_data_buffer_list OutputData = AddressedDataBufferList_Create(ThreadContext); + + Context.InitializeApplication(&Context); + + system_time StartTime = Win32GetSystemTime(); + + Running = true; + Context.WindowIsVisible = true; + while (Running) + { + if (GlobalDebugServices->RecordFrames) { - RenderBuffer = AllocateRenderCommandBuffer(MB(12), &PlatformPermanent, ThreadContext); + EndDebugFrame(GlobalDebugServices); } - addressed_data_buffer_list OutputData = AddressedDataBufferList_Create(ThreadContext); + DEBUG_TRACK_SCOPE(MainLoop); - Context.InitializeApplication(&Context); - - system_time StartTime = Win32GetSystemTime(); - - Running = true; - Context.WindowIsVisible = true; - while (Running) { - if (GlobalDebugServices->RecordFrames) - { - EndDebugFrame(GlobalDebugServices); - } - - DEBUG_TRACK_SCOPE(MainLoop); - - { - Context.SystemTime_Last = Context.SystemTime_Current; - Context.SystemTime_Current = Win32GetSystemTime(); - + Context.SystemTime_Last = Context.SystemTime_Current; + Context.SystemTime_Current = Win32GetSystemTime(); + #define PRINT_SYSTEM_TIME 0 #if PRINT_SYSTEM_TIME - gs_string T = PushStringF(Context.ThreadContext.Transient, - 256, - "%d %d %d - %lld\n", - Context.SystemTime_Current.Hour, - Context.SystemTime_Current.Minute, - Context.SystemTime_Current.Second, - Context.SystemTime_Current.NanosSinceEpoch); - - u64 NanosElapsed = Context.SystemTime_Current.NanosSinceEpoch - StartTime.NanosSinceEpoch; - r64 SecondsElapsed = (r64)NanosElapsed * NanosToSeconds; - - Log_Message(GlobalLogBuffer, - "%lld %f Seconds \n", - NanosElapsed, - SecondsElapsed); + gs_string T = PushStringF(Context.ThreadContext.Transient, + 256, + "%d %d %d - %lld\n", + Context.SystemTime_Current.Hour, + Context.SystemTime_Current.Minute, + Context.SystemTime_Current.Second, + Context.SystemTime_Current.NanosSinceEpoch); + + u64 NanosElapsed = Context.SystemTime_Current.NanosSinceEpoch - StartTime.NanosSinceEpoch; + r64 SecondsElapsed = (r64)NanosElapsed * NanosToSeconds; + + Log_Message(GlobalLogBuffer, + "%lld %f Seconds \n", + NanosElapsed, + SecondsElapsed); #endif - } - - ResetInputQueue(&InputQueue); - - ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false, true); - - AddressedDataBufferList_Clear(&OutputData); - - if (!Context.Headless) - { - Mouse_Update(MainWindow, &Context); - MSG Message; - while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE)) - { - DEBUG_TRACK_SCOPE(PeekWindowsMessages); - HandleWindowMessage(Message, &MainWindow, &InputQueue, &Context.Mouse); - } - - Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}}; - RenderBuffer.ViewWidth = MainWindow.Width; - RenderBuffer.ViewHeight = MainWindow.Height; - } - - Context.DeltaTime = LastFrameSecondsElapsed; - Context.TotalTime += (r64)Context.DeltaTime; - - Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); - - Win32_SendOutputData(ThreadContext, OutputData); - - if (!Context.Headless) - { - RenderCommandBuffer(RenderBuffer); - ClearRenderBuffer(&RenderBuffer); - - Mouse_Advance(&Context); - - HDC DeviceContext = GetDC(MainWindow.Handle); - SwapBuffers(DeviceContext); - ReleaseDC(MainWindow.Handle, DeviceContext); - } - - Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext); - - s64 FinishedWorkTime = GetWallClock(); - r32 SecondsElapsed = GetSecondsElapsed(LastFrameEnd, FinishedWorkTime, PerformanceCountFrequency); - - while (SecondsElapsed < TargetSecondsPerFrame) - { - DEBUG_TRACK_SCOPE(UnusedTime); - u32 SleepTime = 1000.0f * (TargetSecondsPerFrame - SecondsElapsed); - Sleep(SleepTime); - SecondsElapsed = GetSecondsElapsed(LastFrameEnd, GetWallClock(), PerformanceCountFrequency); - } - - LastFrameSecondsElapsed = SecondsElapsed; - LastFrameEnd = GetWallClock(); } - Context.CleanupApplication(Context, &OutputData); + ResetInputQueue(&InputQueue); + + ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false, true); + + AddressedDataBufferList_Clear(&OutputData); + + if (!Context.Headless) + { + Mouse_Update(MainWindow, &Context); + MSG Message; + while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE)) + { + DEBUG_TRACK_SCOPE(PeekWindowsMessages); + HandleWindowMessage(Message, &MainWindow, &InputQueue, &Context.Mouse); + } + + Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}}; + RenderBuffer.ViewWidth = MainWindow.Width; + RenderBuffer.ViewHeight = MainWindow.Height; + } + + Context.DeltaTime = LastFrameSecondsElapsed; + Context.TotalTime += (r64)Context.DeltaTime; + + Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); + Win32_SendOutputData(ThreadContext, OutputData); + + if (!Context.Headless) + { + RenderCommandBuffer(RenderBuffer); + ClearRenderBuffer(&RenderBuffer); + + Mouse_Advance(&Context); + + HDC DeviceContext = GetDC(MainWindow.Handle); + SwapBuffers(DeviceContext); + ReleaseDC(MainWindow.Handle, DeviceContext); + } + Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext); - Win32WorkQueue_Cleanup(); - Win32SocketSystem_Cleanup(); + s64 FinishedWorkTime = GetWallClock(); + r32 SecondsElapsed = GetSecondsElapsed(LastFrameEnd, FinishedWorkTime, PerformanceCountFrequency); - return 0; + while (SecondsElapsed < TargetSecondsPerFrame) + { + DEBUG_TRACK_SCOPE(UnusedTime); + u32 SleepTime = 1000.0f * (TargetSecondsPerFrame - SecondsElapsed); + Sleep(SleepTime); + SecondsElapsed = GetSecondsElapsed(LastFrameEnd, GetWallClock(), PerformanceCountFrequency); + } + + LastFrameSecondsElapsed = SecondsElapsed; + LastFrameEnd = GetWallClock(); + } + + Context.CleanupApplication(Context, &OutputData); + Win32_SendOutputData(ThreadContext, OutputData); + Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext); + + Win32WorkQueue_Cleanup(); + Win32SocketSystem_Cleanup(); + + return 0; } #define WIN32_FOLDHAUS_CPP diff --git a/src/app/platform_win32/win32_foldhaus_dll.h b/src/app/platform_win32/win32_foldhaus_dll.h index 26071e6..396f15e 100644 --- a/src/app/platform_win32/win32_foldhaus_dll.h +++ b/src/app/platform_win32/win32_foldhaus_dll.h @@ -11,92 +11,92 @@ // DLL struct win32_dll_refresh { - FILETIME LastWriteTime; - HMODULE DLL; - - bool IsValid; - - char SourceDLLPath[MAX_PATH]; - char WorkingDLLPath[MAX_PATH]; - char LockFilePath[MAX_PATH]; + FILETIME LastWriteTime; + HMODULE DLL; + + bool IsValid; + + char SourceDLLPath[MAX_PATH]; + char WorkingDLLPath[MAX_PATH]; + char LockFilePath[MAX_PATH]; }; internal int Win32DLLgs_stringLength(char* gs_string) { - char* At = gs_string; - while (*At) { At++; }; - return At - gs_string; + char* At = gs_string; + while (*At) { At++; }; + return At - gs_string; } internal int Win32DLLConcatgs_strings(int ALength, char* A, int BLength, char* B, int DestLength, char* Dest) { - char* Dst = Dest; - char* AAt = A; - int ALengthToCopy = ALength < DestLength ? ALength : DestLength; - for (s32 a = 0; a < ALength; a++) - { - *Dst++ = *AAt++; - } - char* BAt = B; - int DestLengthRemaining = DestLength - (Dst - Dest); - int BLengthToCopy = BLength < DestLengthRemaining ? BLength : DestLength; - for (s32 b = 0; b < BLengthToCopy; b++) - { - *Dst++ = *BAt++; - } - int DestLengthOut = Dst - Dest; - int NullTermIndex = DestLengthOut < DestLength ? DestLengthOut : DestLength; - Dest[NullTermIndex] = 0; - return DestLengthOut; + char* Dst = Dest; + char* AAt = A; + int ALengthToCopy = ALength < DestLength ? ALength : DestLength; + for (s32 a = 0; a < ALength; a++) + { + *Dst++ = *AAt++; + } + char* BAt = B; + int DestLengthRemaining = DestLength - (Dst - Dest); + int BLengthToCopy = BLength < DestLengthRemaining ? BLength : DestLength; + for (s32 b = 0; b < BLengthToCopy; b++) + { + *Dst++ = *BAt++; + } + int DestLengthOut = Dst - Dest; + int NullTermIndex = DestLengthOut < DestLength ? DestLengthOut : DestLength; + Dest[NullTermIndex] = 0; + return DestLengthOut; } internal void GetApplicationPath(system_path* Result) { - Assert(Result->Path); - Result->PathLength = GetModuleFileNameA(0, Result->Path, Result->PathLength); - - u32 CharactersScanned = 0; - u32 IndexOfLastSlash = 0; - char *Scan = Result->Path; - while(*Scan) + Assert(Result->Path); + Result->PathLength = GetModuleFileNameA(0, Result->Path, Result->PathLength); + + u32 CharactersScanned = 0; + u32 IndexOfLastSlash = 0; + char *Scan = Result->Path; + while(*Scan) + { + if (*Scan == '\\') { - if (*Scan == '\\') - { - Result->IndexOfLastSlash = CharactersScanned + 1; - } - Scan++; - CharactersScanned++; + Result->IndexOfLastSlash = CharactersScanned + 1; } + Scan++; + CharactersScanned++; + } } internal b32 LoadApplicationDLL(char* DLLName, win32_dll_refresh* DLLResult) { - b32 Success = false; - Assert(DLLResult->DLL == 0); - - DLLResult->DLL = LoadLibraryA(DLLName); - if (DLLResult->DLL) - { - Success = true; - DLLResult->IsValid = true; - } - - return Success; + b32 Success = false; + Assert(DLLResult->DLL == 0); + + DLLResult->DLL = LoadLibraryA(DLLName); + if (DLLResult->DLL) + { + Success = true; + DLLResult->IsValid = true; + } + + return Success; } internal void UnloadApplicationDLL(win32_dll_refresh* DLL) { - if (DLL->DLL) - { - FreeLibrary(DLL->DLL); - } - DLL->DLL = 0; - DLL->IsValid = false; + if (DLL->DLL) + { + FreeLibrary(DLL->DLL); + } + DLL->DLL = 0; + DLL->IsValid = false; } internal win32_dll_refresh @@ -104,57 +104,57 @@ InitializeDLLHotReloading(char* SourceDLLName, char* WorkingDLLFileName, char* LockFileName) { - win32_dll_refresh Result = {}; - Result.IsValid = false; - - system_path ExePath = {}; - ExePath.PathLength = MAX_PATH; - ExePath.Path = (char*)VirtualAlloc(NULL, ExePath.PathLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - GetApplicationPath(&ExePath); - - Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path, - Win32DLLgs_stringLength(SourceDLLName), SourceDLLName, - MAX_PATH, Result.SourceDLLPath); - Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path, - Win32DLLgs_stringLength(WorkingDLLFileName), WorkingDLLFileName, - MAX_PATH, Result.WorkingDLLPath); - Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path, - Win32DLLgs_stringLength(LockFileName), LockFileName, - MAX_PATH, Result.LockFilePath); - - Win32Free((u8*)ExePath.Path, ExePath.PathLength); - return Result; - + win32_dll_refresh Result = {}; + Result.IsValid = false; + + system_path ExePath = {}; + ExePath.PathLength = MAX_PATH; + ExePath.Path = (char*)VirtualAlloc(NULL, ExePath.PathLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + GetApplicationPath(&ExePath); + + Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path, + Win32DLLgs_stringLength(SourceDLLName), SourceDLLName, + MAX_PATH, Result.SourceDLLPath); + Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path, + Win32DLLgs_stringLength(WorkingDLLFileName), WorkingDLLFileName, + MAX_PATH, Result.WorkingDLLPath); + Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path, + Win32DLLgs_stringLength(LockFileName), LockFileName, + MAX_PATH, Result.LockFilePath); + + Win32Free((u8*)ExePath.Path, ExePath.PathLength, 0); + return Result; + } internal b32 HotLoadDLL(win32_dll_refresh* DLL) { - b32 DidReload = false; - - FILETIME UpdatedLastWriteTime = {}; - WIN32_FIND_DATA FindData = {}; - HANDLE FileHandle = FindFirstFileA(DLL->SourceDLLPath, &FindData); - if (FileHandle != INVALID_HANDLE_VALUE) + b32 DidReload = false; + + FILETIME UpdatedLastWriteTime = {}; + WIN32_FIND_DATA FindData = {}; + HANDLE FileHandle = FindFirstFileA(DLL->SourceDLLPath, &FindData); + if (FileHandle != INVALID_HANDLE_VALUE) + { + UpdatedLastWriteTime = FindData.ftLastWriteTime; + FindClose(FileHandle); + } + + if (CompareFileTime(&UpdatedLastWriteTime, &DLL->LastWriteTime)) + { + WIN32_FILE_ATTRIBUTE_DATA Ignored; + if (!GetFileAttributesEx(DLL->LockFilePath, GetFileExInfoStandard, &Ignored)) { - UpdatedLastWriteTime = FindData.ftLastWriteTime; - FindClose(FileHandle); + UnloadApplicationDLL(DLL); + CopyFileA(DLL->SourceDLLPath, DLL->WorkingDLLPath, FALSE); + LoadApplicationDLL(DLL->WorkingDLLPath, DLL); + DLL->LastWriteTime = UpdatedLastWriteTime; + DidReload = true; } - - if (CompareFileTime(&UpdatedLastWriteTime, &DLL->LastWriteTime)) - { - WIN32_FILE_ATTRIBUTE_DATA Ignored; - if (!GetFileAttributesEx(DLL->LockFilePath, GetFileExInfoStandard, &Ignored)) - { - UnloadApplicationDLL(DLL); - CopyFileA(DLL->SourceDLLPath, DLL->WorkingDLLPath, FALSE); - LoadApplicationDLL(DLL->WorkingDLLPath, DLL); - DLL->LastWriteTime = UpdatedLastWriteTime; - DidReload = true; - } - } - - return DidReload; + } + + return DidReload; } diff --git a/src/app/platform_win32/win32_foldhaus_memory.h b/src/app/platform_win32/win32_foldhaus_memory.h index 100dd1a..6dbce1c 100644 --- a/src/app/platform_win32/win32_foldhaus_memory.h +++ b/src/app/platform_win32/win32_foldhaus_memory.h @@ -1,36 +1,26 @@ -// -// File: win32_foldhaus_memory.h -// Author: Peter Slattery -// Creation Date: 2020-02-04 -// -// -// NOTE: Relies on having imported foldhaus_platform.h prior to this file -// -#ifndef WIN32_FOLDHAUS_MEMORY_H +/* date = May 10th 2021 11:48 pm */ -ALLOCATOR_ALLOC(Win32Alloc) +#ifndef GS_MEMORY_WIN32_H +#define GS_MEMORY_WIN32_H + +PLATFORM_ALLOC(Win32Alloc) { - u8* Result = (u8*)VirtualAlloc(NULL, Size, - MEM_COMMIT | MEM_RESERVE, - PAGE_EXECUTE_READWRITE); - if (ResultSize != 0) - { - *ResultSize = Size; - } - return Result; + u8* Result = (u8*)VirtualAlloc(NULL, Size, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + if (ResultSize) *ResultSize = Size; + return Result; } -ALLOCATOR_FREE(Win32Free) +PLATFORM_FREE(Win32Free) { - b32 Result = VirtualFree(Ptr, 0, MEM_RELEASE); - if (!Result) - { - s32 Error = GetLastError(); - // TODO(Peter): I'm waiting to see an error actually occur here - // to know what it could possibly be. - InvalidCodePath; - } + VirtualFree(Base, 0, MEM_RELEASE); } -#define WIN32_FOLDHAUS_MEMORY_H -#endif // WIN32_FOLDHAUS_MEMORY_H \ No newline at end of file +internal gs_allocator +CreatePlatformAllocator() +{ + return AllocatorCreate(Win32Alloc, Win32Free, 0); +} + +#endif //GS_MEMORY_WIN32_H diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index 567811b..4b309c8 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -13,367 +13,367 @@ global s32* Win32SerialPortFilled; DCB Win32SerialPort_GetState(HANDLE ComPortHandle) { - DEBUG_TRACK_FUNCTION; - DCB ControlSettings = {0}; - ZeroStruct(&ControlSettings); - ControlSettings.DCBlength = sizeof(ControlSettings); - - bool Success = GetCommState(ComPortHandle, &ControlSettings); - Assert(Success); - - return ControlSettings; + DEBUG_TRACK_FUNCTION; + DCB ControlSettings = {0}; + ZeroStruct(&ControlSettings); + ControlSettings.DCBlength = sizeof(ControlSettings); + + bool Success = GetCommState(ComPortHandle, &ControlSettings); + Assert(Success); + + return ControlSettings; } void Win32SerialPort_SetState(HANDLE ComPortHandle, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits) { - DEBUG_TRACK_FUNCTION; - DCB ControlSettings = Win32SerialPort_GetState(ComPortHandle); - - // TODO(pjs): Validate BaudRate - There's only certain rates that are valid right? - ControlSettings.BaudRate = BaudRate; - - if (Parity == NOPARITY) - { - ControlSettings.Parity = Parity; - ControlSettings.fParity = 0; - } - if (Parity == EVENPARITY || Parity == ODDPARITY) - { - ControlSettings.Parity = Parity; - ControlSettings.fParity = 1; - } - - ControlSettings.StopBits = StopBits; - ControlSettings.ByteSize = ByteSize; - - ControlSettings.fBinary = true; - - ControlSettings.fOutxCtsFlow = false; - ControlSettings.fOutxDsrFlow = false; - ControlSettings.fDtrControl = DTR_CONTROL_DISABLE; - ControlSettings.fDsrSensitivity = 0; - ControlSettings.fRtsControl = RTS_CONTROL_DISABLE; - ControlSettings.fOutX = false; - ControlSettings.fInX = false; - - ControlSettings.fErrorChar = 0; - ControlSettings.fNull = false; - ControlSettings.fAbortOnError = false; - ControlSettings.wReserved = false; - ControlSettings.XonLim = 2; - ControlSettings.XoffLim = 4; - ControlSettings.XonChar = 0x13; - ControlSettings.XoffChar = 0x19; - ControlSettings.EvtChar = 0; - - bool Success = SetCommState(ComPortHandle, &ControlSettings); + DEBUG_TRACK_FUNCTION; + DCB ControlSettings = Win32SerialPort_GetState(ComPortHandle); + + // TODO(pjs): Validate BaudRate - There's only certain rates that are valid right? + ControlSettings.BaudRate = BaudRate; + + if (Parity == NOPARITY) + { + ControlSettings.Parity = Parity; + ControlSettings.fParity = 0; + } + if (Parity == EVENPARITY || Parity == ODDPARITY) + { + ControlSettings.Parity = Parity; + ControlSettings.fParity = 1; + } + + ControlSettings.StopBits = StopBits; + ControlSettings.ByteSize = ByteSize; + + ControlSettings.fBinary = true; + + ControlSettings.fOutxCtsFlow = false; + ControlSettings.fOutxDsrFlow = false; + ControlSettings.fDtrControl = DTR_CONTROL_DISABLE; + ControlSettings.fDsrSensitivity = 0; + ControlSettings.fRtsControl = RTS_CONTROL_DISABLE; + ControlSettings.fOutX = false; + ControlSettings.fInX = false; + + ControlSettings.fErrorChar = 0; + ControlSettings.fNull = false; + ControlSettings.fAbortOnError = false; + ControlSettings.wReserved = false; + ControlSettings.XonLim = 2; + ControlSettings.XoffLim = 4; + ControlSettings.XonChar = 0x13; + ControlSettings.XoffChar = 0x19; + ControlSettings.EvtChar = 0; + + bool Success = SetCommState(ComPortHandle, &ControlSettings); } gs_const_string_array Win32SerialPorts_List(gs_memory_arena* Arena, gs_memory_arena* Transient) { - gs_const_string_array Result = {}; + gs_const_string_array Result = {}; + + DWORD SizeNeeded0 = 0; + DWORD CountReturned0 = 0; + EnumPorts(NULL, 1, 0, 0, &SizeNeeded0, &CountReturned0); + Assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER); + + DWORD SizeNeeded1 = 0; + DWORD CountReturned1 = 0; + PORT_INFO_1* PortsArray = (PORT_INFO_1*)PushSize(Transient, SizeNeeded0).Memory; + if (EnumPorts(NULL, + 1, + (u8*)PortsArray, + SizeNeeded0, + &SizeNeeded1, + &CountReturned1)) + { + Result.CountMax = (u64)CountReturned1; + Result.Strings = PushArray(Arena, gs_const_string, Result.CountMax); - DWORD SizeNeeded0 = 0; - DWORD CountReturned0 = 0; - EnumPorts(NULL, 1, 0, 0, &SizeNeeded0, &CountReturned0); - Assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER); - - DWORD SizeNeeded1 = 0; - DWORD CountReturned1 = 0; - PORT_INFO_1* PortsArray = (PORT_INFO_1*)PushSize(Transient, SizeNeeded0); - if (EnumPorts(NULL, - 1, - (u8*)PortsArray, - SizeNeeded0, - &SizeNeeded1, - &CountReturned1)) + for (; Result.Count < Result.CountMax; Result.Count++) { - Result.CountMax = (u64)CountReturned1; - Result.Strings = PushArray(Arena, gs_const_string, Result.CountMax); - - for (; Result.Count < Result.CountMax; Result.Count++) - { - u64 Index = Result.Count; - u64 StrLen = CStringLength(PortsArray[Index].pName); - gs_string Str = PushString(Arena, StrLen); - PrintF(&Str, "%.*s", StrLen, PortsArray[Index].pName); - Result.Strings[Result.Count] = Str.ConstString; - } + u64 Index = Result.Count; + u64 StrLen = CStringLength(PortsArray[Index].pName); + gs_string Str = PushString(Arena, StrLen); + PrintF(&Str, "%.*s", StrLen, PortsArray[Index].pName); + Result.Strings[Result.Count] = Str.ConstString; } - - return Result; + } + + return Result; } bool Win32SerialPort_Exists(char* PortName, gs_memory_arena* Transient) { - bool Result = false; - if (PortName != 0) + bool Result = false; + if (PortName != 0) + { + gs_const_string PortIdent = ConstString(PortName); + u32 IdentBegin = FindLast(PortIdent, '\\') + 1; + PortIdent = Substring(PortIdent, IdentBegin, PortIdent.Length); + + gs_const_string_array PortsAvailable = Win32SerialPorts_List(Transient, Transient); + + for (u64 i = 0; i < PortsAvailable.Count; i++) { - gs_const_string PortIdent = ConstString(PortName); - u32 IdentBegin = FindLast(PortIdent, '\\') + 1; - PortIdent = Substring(PortIdent, IdentBegin, PortIdent.Length); - - gs_const_string_array PortsAvailable = Win32SerialPorts_List(Transient, Transient); - - for (u64 i = 0; i < PortsAvailable.Count; i++) - { - gs_const_string AvailablePortName = PortsAvailable.Strings[i]; - if (StringsEqualUpToLength(AvailablePortName, PortIdent, PortIdent.Length)) - { - Result = true; - break; - } - } + gs_const_string AvailablePortName = PortsAvailable.Strings[i]; + if (StringsEqualUpToLength(AvailablePortName, PortIdent, PortIdent.Length)) + { + Result = true; + break; + } } - return Result; + } + return Result; } HANDLE Win32SerialPort_Open(char* PortName, gs_memory_arena* Transient) { - DEBUG_TRACK_FUNCTION; - HANDLE ComPortHandle = INVALID_HANDLE_VALUE;; + DEBUG_TRACK_FUNCTION; + HANDLE ComPortHandle = INVALID_HANDLE_VALUE;; + + if (Win32SerialPort_Exists(PortName, Transient)) + { - if (Win32SerialPort_Exists(PortName, Transient)) + ComPortHandle = CreateFile(PortName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, // Default Security Attr + OPEN_EXISTING, + 0, // Not overlapped I/O + NULL); + + bool HasError = false; + + if (ComPortHandle != INVALID_HANDLE_VALUE) { - - ComPortHandle = CreateFile(PortName, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, // Default Security Attr - OPEN_EXISTING, - 0, // Not overlapped I/O - NULL); - - bool HasError = false; - - if (ComPortHandle != INVALID_HANDLE_VALUE) - { - COMMTIMEOUTS Timeouts = { 0 }; - Timeouts.ReadIntervalTimeout = 0; // in milliseconds - Timeouts.ReadTotalTimeoutConstant = 0; // in milliseconds - Timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds - Timeouts.WriteTotalTimeoutConstant = 0; // in milliseconds - Timeouts.WriteTotalTimeoutMultiplier = 0; // in milliseconds - - HasError = !SetCommTimeouts(ComPortHandle, &Timeouts); - } - else - { - HasError = true; - } - - if (HasError) - { - // Error - s32 Error = GetLastError(); - switch (Error) - { - case ERROR_INVALID_FUNCTION: - case ERROR_NO_SUCH_DEVICE: - case ERROR_FILE_NOT_FOUND: - { - // NOTE(PS): The outer scope should handle these cases - ComPortHandle = INVALID_HANDLE_VALUE; - }break; - - InvalidDefaultCase; - } - } + COMMTIMEOUTS Timeouts = { 0 }; + Timeouts.ReadIntervalTimeout = 0; // in milliseconds + Timeouts.ReadTotalTimeoutConstant = 0; // in milliseconds + Timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds + Timeouts.WriteTotalTimeoutConstant = 0; // in milliseconds + Timeouts.WriteTotalTimeoutMultiplier = 0; // in milliseconds + + HasError = !SetCommTimeouts(ComPortHandle, &Timeouts); + } + else + { + HasError = true; } - return ComPortHandle; + if (HasError) + { + // Error + s32 Error = GetLastError(); + switch (Error) + { + case ERROR_INVALID_FUNCTION: + case ERROR_NO_SUCH_DEVICE: + case ERROR_FILE_NOT_FOUND: + { + // NOTE(PS): The outer scope should handle these cases + ComPortHandle = INVALID_HANDLE_VALUE; + }break; + + InvalidDefaultCase; + } + } + } + + return ComPortHandle; } void Win32SerialPort_Close(HANDLE PortHandle) { - CloseHandle(PortHandle); + CloseHandle(PortHandle); } bool Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer) { - DEBUG_TRACK_FUNCTION; - Assert(PortHandle != INVALID_HANDLE_VALUE); - bool Success = false; - - DWORD BytesWritten = 0; - if (WriteFile(PortHandle, Buffer.Memory, Buffer.Size, &BytesWritten, NULL)) + DEBUG_TRACK_FUNCTION; + Assert(PortHandle != INVALID_HANDLE_VALUE); + bool Success = false; + + DWORD BytesWritten = 0; + if (WriteFile(PortHandle, Buffer.Memory, Buffer.Size, &BytesWritten, NULL)) + { + Success = (BytesWritten == Buffer.Size); + if (!Success) { - Success = (BytesWritten == Buffer.Size); - if (!Success) - { - Log_Error(GlobalLogBuffer, "Error: Entire buffer not written.\n"); - } + Log_Error(GlobalLogBuffer, "Error: Entire buffer not written.\n"); } - else + } + else + { + Log_Error(GlobalLogBuffer, "Error: Unable to write to port\n"); + s32 Error = GetLastError(); + switch (Error) { - Log_Error(GlobalLogBuffer, "Error: Unable to write to port\n"); - s32 Error = GetLastError(); - switch (Error) - { - case ERROR_OPERATION_ABORTED: - case ERROR_GEN_FAILURE: - { - // NOTE(pjs): Probably means that the serial port became invalid - // ie. the usb stick was removed - }break; - - case ERROR_ACCESS_DENIED: - { - // ?? - }break; - - case ERROR_NO_SUCH_DEVICE: - { - }break; - - case ERROR_INVALID_HANDLE: - InvalidDefaultCase; - } + case ERROR_OPERATION_ABORTED: + case ERROR_GEN_FAILURE: + { + // NOTE(pjs): Probably means that the serial port became invalid + // ie. the usb stick was removed + }break; + + case ERROR_ACCESS_DENIED: + { + // ?? + }break; + + case ERROR_NO_SUCH_DEVICE: + { + }break; + + case ERROR_INVALID_HANDLE: + InvalidDefaultCase; } - - return Success; + } + + return Success; } bool Win32SerialPort_SetRead(HANDLE PortHandle) { - bool Status = SetCommMask(PortHandle, EV_RXCHAR); - return Status; + bool Status = SetCommMask(PortHandle, EV_RXCHAR); + return Status; } u32 Win32SerialPort_ReadMessageWhenReady(HANDLE PortHandle, gs_data Data) { - u32 ReadSize = 0; - - DWORD EventMask = 0; - bool Status = WaitCommEvent(PortHandle, &EventMask, NULL); - if (Status) + u32 ReadSize = 0; + + DWORD EventMask = 0; + bool Status = WaitCommEvent(PortHandle, &EventMask, NULL); + if (Status) + { + DWORD NoBytesRead = 0; + do { - DWORD NoBytesRead = 0; - do - { - u8 Byte = 0; - Status = ReadFile(PortHandle, &Byte, sizeof(char), &NoBytesRead, NULL); - Data.Memory[ReadSize] = Byte; - ReadSize++; - } - while (NoBytesRead > 0 && ReadSize < Data.Size); + u8 Byte = 0; + Status = ReadFile(PortHandle, &Byte, sizeof(char), &NoBytesRead, NULL); + Data.Memory[ReadSize] = Byte; + ReadSize++; } - //Read data and store in a buffer - - return ReadSize; + while (NoBytesRead > 0 && ReadSize < Data.Size); + } + //Read data and store in a buffer + + return ReadSize; } ///////////////////////// // Win32SerialArray void -Win32SerialArray_Create(gs_thread_context Context) +Win32SerialArray_Create(gs_memory_arena* A) { - DEBUG_TRACK_FUNCTION; - - Win32SerialHandlesCountMax = 32; - - Win32SerialHandles = AllocatorAllocArray(Context.Allocator, HANDLE, Win32SerialHandlesCountMax); - Win32SerialPortNames = AllocatorAllocArray(Context.Allocator, gs_string, Win32SerialHandlesCountMax); - Win32SerialPortFilled = AllocatorAllocArray(Context.Allocator, s32, Win32SerialHandlesCountMax); - - u64 PortNameSize = 256; - u64 PortNameBufferSize = PortNameSize * Win32SerialHandlesCountMax; - char* PortNameBuffer = AllocatorAllocArray(Context.Allocator, char, PortNameBufferSize); - for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) - { - char* NameBase = PortNameBuffer + (PortNameSize * i); - Win32SerialPortNames[i] = MakeString(NameBase, 0, PortNameSize); - Win32SerialPortFilled[i] = 0; - } + DEBUG_TRACK_FUNCTION; + + Win32SerialHandlesCountMax = 32; + + Win32SerialHandles = PushArray(A, HANDLE, Win32SerialHandlesCountMax); + Win32SerialPortNames = PushArray(A, gs_string, Win32SerialHandlesCountMax); + Win32SerialPortFilled = PushArray(A, s32, Win32SerialHandlesCountMax); + + u64 PortNameSize = 256; + u64 PortNameBufferSize = PortNameSize * Win32SerialHandlesCountMax; + char* PortNameBuffer = PushArray(A, char, PortNameBufferSize); + for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) + { + char* NameBase = PortNameBuffer + (PortNameSize * i); + Win32SerialPortNames[i] = MakeString(NameBase, 0, PortNameSize); + Win32SerialPortFilled[i] = 0; + } } void Win32SerialArray_Push(HANDLE SerialHandle, gs_const_string PortName) { - DEBUG_TRACK_FUNCTION; - - bool Found = false; - for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) + DEBUG_TRACK_FUNCTION; + + bool Found = false; + for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) + { + bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + i, 1, 0); + if (!WasFilled) { - bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + i, 1, 0); - if (!WasFilled) - { - Win32SerialHandles[i] = SerialHandle; - PrintF(&Win32SerialPortNames[i], "%S", PortName); - Found = true; - break; - } + Win32SerialHandles[i] = SerialHandle; + PrintF(&Win32SerialPortNames[i], "%S", PortName); + Found = true; + break; } - Assert(Found); + } + Assert(Found); } void Win32SerialArray_Pop(u32 Index) { - bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + Index, 0, 1); - Assert(WasFilled); - Win32SerialPortFilled[Index] = false; - Win32SerialHandles[Index] = INVALID_HANDLE_VALUE; + bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + Index, 0, 1); + Assert(WasFilled); + Win32SerialPortFilled[Index] = false; + Win32SerialHandles[Index] = INVALID_HANDLE_VALUE; } HANDLE Win32SerialArray_Get(gs_const_string PortName) { - DEBUG_TRACK_FUNCTION; - - HANDLE PortHandle = INVALID_HANDLE_VALUE; - for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) + DEBUG_TRACK_FUNCTION; + + HANDLE PortHandle = INVALID_HANDLE_VALUE; + for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) + { + if (Win32SerialPortFilled[i] && + StringsEqual(Win32SerialPortNames[i].ConstString, PortName)) { - if (Win32SerialPortFilled[i] && - StringsEqual(Win32SerialPortNames[i].ConstString, PortName)) - { - PortHandle = Win32SerialHandles[i]; - break; - } + PortHandle = Win32SerialHandles[i]; + break; } - return PortHandle; + } + return PortHandle; } HANDLE Win32SerialArray_GetOrOpen(gs_const_string PortName, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits, gs_memory_arena* Transient) { - DEBUG_TRACK_FUNCTION; - - HANDLE PortHandle = Win32SerialArray_Get(PortName); - if (PortHandle == INVALID_HANDLE_VALUE) + DEBUG_TRACK_FUNCTION; + + HANDLE PortHandle = Win32SerialArray_Get(PortName); + if (PortHandle == INVALID_HANDLE_VALUE) + { + Assert(IsNullTerminated(PortName)); + PortHandle = Win32SerialPort_Open(PortName.Str, Transient); + if (PortHandle != INVALID_HANDLE_VALUE) { - Assert(IsNullTerminated(PortName)); - PortHandle = Win32SerialPort_Open(PortName.Str, Transient); - if (PortHandle != INVALID_HANDLE_VALUE) - { - Win32SerialPort_SetState(PortHandle, BaudRate, ByteSize, Parity, StopBits); - Win32SerialArray_Push(PortHandle, PortName); - } + Win32SerialPort_SetState(PortHandle, BaudRate, ByteSize, Parity, StopBits); + Win32SerialArray_Push(PortHandle, PortName); } - return PortHandle; + } + return PortHandle; } void Win32SerialArray_Close(gs_const_string PortName) { - for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) + for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) + { + if (Win32SerialPortFilled[i] && StringsEqual(Win32SerialPortNames[i].ConstString, PortName)) { - if (Win32SerialPortFilled[i] && StringsEqual(Win32SerialPortNames[i].ConstString, PortName)) - { - Win32SerialPort_Close(Win32SerialHandles[i]); - Win32SerialArray_Pop(i); - break; - } + Win32SerialPort_Close(Win32SerialHandles[i]); + Win32SerialArray_Pop(i); + break; } + } } #define WIN32_SERIAL_H diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index ca36cac..ab656f5 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -7,14 +7,14 @@ struct win32_socket { - SOCKET Socket; + SOCKET Socket; }; struct win32_socket_array { - win32_socket* Values; - s32 CountMax; - s32 Count; + win32_socket* Values; + s32 CountMax; + s32 Count; }; global WSADATA WSAData; @@ -27,28 +27,28 @@ global win32_socket_array Win32Sockets; internal win32_socket_array Win32SocketArray_Create(u32 CountMax, gs_memory_arena* Storage) { - win32_socket_array Result = {}; - Result.CountMax = CountMax; - Result.Values = PushArray(Storage, win32_socket, CountMax); - return Result; + win32_socket_array Result = {}; + Result.CountMax = CountMax; + Result.Values = PushArray(Storage, win32_socket, CountMax); + return Result; } internal s32 Win32SocketArray_Take(win32_socket_array* Array) { - Assert(Array->Count < Array->CountMax); - s32 Result = Array->Count++; - win32_socket* Socket = Array->Values + Result; - *Socket = {0}; - return Result; + Assert(Array->Count < Array->CountMax); + s32 Result = Array->Count++; + win32_socket* Socket = Array->Values + Result; + *Socket = {0}; + return Result; } internal win32_socket* Win32SocketArray_Get(win32_socket_array Array, s32 Index) { - Assert(Index < Array.Count); - win32_socket* Result = Array.Values + Index; - return Result; + Assert(Index < Array.Count); + win32_socket* Result = Array.Values + Index; + return Result; } ////////////////////// @@ -58,259 +58,259 @@ Win32SocketArray_Get(win32_socket_array Array, s32 Index) internal win32_socket Win32Socket_Create(s32 AddressFamily, s32 Type, s32 Protocol) { - win32_socket Result = {0}; - Result.Socket = socket(AddressFamily, Type, Protocol); - if (Result.Socket == INVALID_SOCKET) - { - s32 Error = WSAGetLastError(); - InvalidCodePath; - } - return Result; + win32_socket Result = {0}; + Result.Socket = socket(AddressFamily, Type, Protocol); + if (Result.Socket == INVALID_SOCKET) + { + s32 Error = WSAGetLastError(); + InvalidCodePath; + } + return Result; } internal void Win32Socket_Bind(win32_socket* Socket, s32 AddressFamily, char* Address, s32 Port) { - sockaddr_in Service = {0}; - Service.sin_family = AddressFamily; - Service.sin_addr.s_addr = inet_addr(Address); - Service.sin_port = htons(Port); - - s32 Result = bind(Socket->Socket, (SOCKADDR*)&Service, sizeof(Service)); - if (Result == SOCKET_ERROR) - { - s32 Error = WSAGetLastError(); - InvalidCodePath; - } + sockaddr_in Service = {0}; + Service.sin_family = AddressFamily; + Service.sin_addr.s_addr = inet_addr(Address); + Service.sin_port = htons(Port); + + s32 Result = bind(Socket->Socket, (SOCKADDR*)&Service, sizeof(Service)); + if (Result == SOCKET_ERROR) + { + s32 Error = WSAGetLastError(); + InvalidCodePath; + } } internal win32_socket Win32Socket_ConnectToAddress(char* Address, char* DefaultPort) { - win32_socket Result = {}; - - addrinfo Hints = {0}; - Hints.ai_family = AF_UNSPEC; - Hints.ai_socktype = SOCK_STREAM; - Hints.ai_protocol = IPPROTO_TCP; - - addrinfo* PotentialConnections; - s32 Error = getaddrinfo(Address, DefaultPort, &Hints, &PotentialConnections); - if (Error == 0) - { - for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next) - { - win32_socket Socket = Win32Socket_Create(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol); - if (Socket.Socket == INVALID_SOCKET) - { - Error = WSAGetLastError(); - InvalidCodePath; - } - - Error = connect(Socket.Socket, InfoAt->ai_addr, (int)InfoAt->ai_addrlen); - if (Error == SOCKET_ERROR) - { - closesocket(Socket.Socket); - continue; - } - else - { - Result = Socket; - break; - } - } - } - else + win32_socket Result = {}; + + addrinfo Hints = {0}; + Hints.ai_family = AF_UNSPEC; + Hints.ai_socktype = SOCK_STREAM; + Hints.ai_protocol = IPPROTO_TCP; + + addrinfo* PotentialConnections; + s32 Error = getaddrinfo(Address, DefaultPort, &Hints, &PotentialConnections); + if (Error == 0) + { + for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next) { + win32_socket Socket = Win32Socket_Create(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol); + if (Socket.Socket == INVALID_SOCKET) + { Error = WSAGetLastError(); InvalidCodePath; + } + + Error = connect(Socket.Socket, InfoAt->ai_addr, (int)InfoAt->ai_addrlen); + if (Error == SOCKET_ERROR) + { + closesocket(Socket.Socket); + continue; + } + else + { + Result = Socket; + break; + } } - - freeaddrinfo(PotentialConnections); - - return Result; + } + else + { + Error = WSAGetLastError(); + InvalidCodePath; + } + + freeaddrinfo(PotentialConnections); + + return Result; } internal bool Win32ConnectSocket(platform_socket_manager* Manager, platform_socket* Socket) { - bool Result = false; - - addrinfo Hints = {0}; - Hints.ai_family = AF_UNSPEC; - Hints.ai_socktype = SOCK_STREAM; - Hints.ai_protocol = IPPROTO_TCP; - - addrinfo* PotentialConnections; - s32 Error = getaddrinfo(Socket->Addr, Socket->Port, &Hints, &PotentialConnections); - if (Error == 0) - { - for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next) - { - SOCKET SocketHandle = socket(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol); - if (SocketHandle == INVALID_SOCKET) - { - Error = WSAGetLastError(); - InvalidCodePath; - } - - // If iMode == 0, blocking is enabled - // if iMode != 0, non-blocking mode is enabled - u_long iMode = 0; - Error = ioctlsocket(SocketHandle, FIONBIO, &iMode); - if (Error != NO_ERROR) - { - InvalidCodePath; - } - - Error = connect(SocketHandle, InfoAt->ai_addr, (int)InfoAt->ai_addrlen); - if (Error == SOCKET_ERROR) - { - u32 Status = WSAGetLastError(); - if (Status == WSAEWOULDBLOCK) - { - // Non-blocking sockets -#if 0 - TIMEVAL Timeout = { 0, 500 }; - fd_set SocketSet = {}; - FD_ZERO(&SocketSet); - FD_SET(SocketHandle, &SocketSet); - Assert(FD_ISSET(SocketHandle, &SocketSet)); - Status = select(0, &SocketSet, 0, 0, (const TIMEVAL*)&Timeout); - if (Status == SOCKET_ERROR) - { - - } - else if (Status == 0) - { - } - else - { - - } -#endif - } - else - { - closesocket(SocketHandle); - continue; - } - } - - Socket->PlatformHandle = (u8*)Win32Alloc(sizeof(SOCKET), 0); - *(SOCKET*)Socket->PlatformHandle = SocketHandle; - Result = true; - break; - } - } - else + bool Result = false; + + addrinfo Hints = {0}; + Hints.ai_family = AF_UNSPEC; + Hints.ai_socktype = SOCK_STREAM; + Hints.ai_protocol = IPPROTO_TCP; + + addrinfo* PotentialConnections; + s32 Error = getaddrinfo(Socket->Addr, Socket->Port, &Hints, &PotentialConnections); + if (Error == 0) + { + for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next) { + SOCKET SocketHandle = socket(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol); + if (SocketHandle == INVALID_SOCKET) + { Error = WSAGetLastError(); InvalidCodePath; + } + + // If iMode == 0, blocking is enabled + // if iMode != 0, non-blocking mode is enabled + u_long iMode = 0; + Error = ioctlsocket(SocketHandle, FIONBIO, &iMode); + if (Error != NO_ERROR) + { + InvalidCodePath; + } + + Error = connect(SocketHandle, InfoAt->ai_addr, (int)InfoAt->ai_addrlen); + if (Error == SOCKET_ERROR) + { + u32 Status = WSAGetLastError(); + if (Status == WSAEWOULDBLOCK) + { + // Non-blocking sockets +#if 0 + TIMEVAL Timeout = { 0, 500 }; + fd_set SocketSet = {}; + FD_ZERO(&SocketSet); + FD_SET(SocketHandle, &SocketSet); + Assert(FD_ISSET(SocketHandle, &SocketSet)); + Status = select(0, &SocketSet, 0, 0, (const TIMEVAL*)&Timeout); + if (Status == SOCKET_ERROR) + { + + } + else if (Status == 0) + { + } + else + { + + } +#endif + } + else + { + closesocket(SocketHandle); + continue; + } + } + + Socket->PlatformHandle = (u8*)Win32Alloc(sizeof(SOCKET), 0, 0); + *(SOCKET*)Socket->PlatformHandle = SocketHandle; + Result = true; + break; } - - if (!Result) - { - Assert(Socket->PlatformHandle == 0); - } - - freeaddrinfo(PotentialConnections); - return Result; + } + else + { + Error = WSAGetLastError(); + InvalidCodePath; + } + + if (!Result) + { + Assert(Socket->PlatformHandle == 0); + } + + freeaddrinfo(PotentialConnections); + return Result; } internal bool Win32CloseSocket(platform_socket_manager* Manager, platform_socket* Socket) { - SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; - closesocket(*Win32Sock); - Win32Free((u8*)Socket->PlatformHandle, sizeof(SOCKET)); - *Socket = {}; - return true; + SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; + closesocket(*Win32Sock); + Win32Free((u8*)Socket->PlatformHandle, sizeof(SOCKET), 0); + *Socket = {}; + return true; } internal bool Win32SocketQueryStatus(platform_socket_manager* Manager, platform_socket* Socket) { - SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; - bool Result = (*Win32Sock != INVALID_SOCKET); - return Result; + SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; + bool Result = (*Win32Sock != INVALID_SOCKET); + return Result; } internal u32 Win32SocketPeek(platform_socket_manager* Manager, platform_socket* Socket) { - u32 Result = 0; - s32 Flags = MSG_PEEK; - SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; - char Temp[4]; - u32 TempSize = 4; - - //Log_Message(GlobalLogBuffer, "Pre Peek..."); - //s32 BytesQueued = recv(*Win32Sock, Temp, TempSize, Flags); - u_long BytesQueued = 0; - ioctlsocket(*Win32Sock, FIONREAD, &BytesQueued); - //Log_Message(GlobalLogBuffer, "Post Peek\n"); - - if (BytesQueued != SOCKET_ERROR) + u32 Result = 0; + s32 Flags = MSG_PEEK; + SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; + char Temp[4]; + u32 TempSize = 4; + + //Log_Message(GlobalLogBuffer, "Pre Peek..."); + //s32 BytesQueued = recv(*Win32Sock, Temp, TempSize, Flags); + u_long BytesQueued = 0; + ioctlsocket(*Win32Sock, FIONREAD, &BytesQueued); + //Log_Message(GlobalLogBuffer, "Post Peek\n"); + + if (BytesQueued != SOCKET_ERROR) + { + Result = (u32)BytesQueued; + } + else + { + s32 Error = WSAGetLastError(); + switch (Error) { - Result = (u32)BytesQueued; + case WSAEWOULDBLOCK: + { + // NOTE(PS): This case covers non-blocking sockets + // if we peek and there's nothing there, it returns + // this error code. MSDN says its a non-fatal error + // and the operation should be retried later + Result = 0; + } break; + + case WSAENOTCONN: + case WSAECONNRESET: + case WSAECONNABORTED: + { + CloseSocket(Manager, Socket); + }break; + + InvalidDefaultCase; } - else - { - s32 Error = WSAGetLastError(); - switch (Error) - { - case WSAEWOULDBLOCK: - { - // NOTE(PS): This case covers non-blocking sockets - // if we peek and there's nothing there, it returns - // this error code. MSDN says its a non-fatal error - // and the operation should be retried later - Result = 0; - } break; - - case WSAENOTCONN: - case WSAECONNRESET: - case WSAECONNABORTED: - { - CloseSocket(Manager, Socket); - }break; - - InvalidDefaultCase; - } - } - return (s32)Result; + } + return (s32)Result; } internal gs_data Win32SocketReceive(platform_socket_manager* Manager, platform_socket* Socket, gs_memory_arena* Storage) { - // TODO(pjs): Test this first code path when you have data running - it should - // get the actual size of the data packet being sent + // TODO(pjs): Test this first code path when you have data running - it should + // get the actual size of the data packet being sent #if 0 - gs_data Result = {}; - s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket); - if (BytesQueued > 0) - { - Result = PushSizeToData(Storage, BytesQueued); - s32 Flags = 0; - s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags); - Assert(BytesReceived == BytesQueued); - } - return Result; -#else - gs_data Result = PushSizeToData(Storage, 1024); + gs_data Result = {}; + s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket); + if (BytesQueued > 0) + { + Result = PushSizeToData(Storage, BytesQueued); s32 Flags = 0; - SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; - s32 BytesReceived = recv(*Win32Sock, (char*)Result.Memory, Result.Size, Flags); - if (BytesReceived == SOCKET_ERROR) - { - // TODO(pjs): Error logging - s32 Error = WSAGetLastError(); - InvalidCodePath; - } - Result.Size = BytesReceived; - return Result; + s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags); + Assert(BytesReceived == BytesQueued); + } + return Result; +#else + gs_data Result = PushSize(Storage, 1024); + s32 Flags = 0; + SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; + s32 BytesReceived = recv(*Win32Sock, (char*)Result.Memory, Result.Size, Flags); + if (BytesReceived == SOCKET_ERROR) + { + // TODO(pjs): Error logging + s32 Error = WSAGetLastError(); + InvalidCodePath; + } + Result.Size = BytesReceived; + return Result; #endif } @@ -318,193 +318,193 @@ Win32SocketReceive(platform_socket_manager* Manager, platform_socket* Socket, gs internal s32 Win32SocketSend(platform_socket_manager* Manager, platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags) { - SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; - - sockaddr_in SockAddress = {}; - SockAddress.sin_family = AF_INET; - SockAddress.sin_port = HostToNetU16(Port); - SockAddress.sin_addr.s_addr = HostToNetU32(Address); - - s32 LengthSent = sendto(*Win32Sock, (char*)Data.Memory, Data.Size, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); - - //Log_Message(GlobalLogBuffer, "Attempting To Send Network Data: "); - if (LengthSent == SOCKET_ERROR) + SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; + + sockaddr_in SockAddress = {}; + SockAddress.sin_family = AF_INET; + SockAddress.sin_port = HostToNetU16(Port); + SockAddress.sin_addr.s_addr = HostToNetU32(Address); + + s32 LengthSent = sendto(*Win32Sock, (char*)Data.Memory, Data.Size, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); + + //Log_Message(GlobalLogBuffer, "Attempting To Send Network Data: "); + if (LengthSent == SOCKET_ERROR) + { + s32 Error = WSAGetLastError(); + switch (Error) { - s32 Error = WSAGetLastError(); - switch (Error) + case WSAEWOULDBLOCK: + { + // NOTE(PS): This covers non-blocking sockets + // In this case the message should be tried again + LengthSent = 0; + //Log_Message(GlobalLogBuffer, "Not sent, buffered\n"); + }break; + + case WSAECONNABORTED: + case WSAENETUNREACH: + case WSAECONNRESET: + case WSAENOTCONN: + { + if (CloseSocket(Manager, Socket)) { - case WSAEWOULDBLOCK: - { - // NOTE(PS): This covers non-blocking sockets - // In this case the message should be tried again - LengthSent = 0; - //Log_Message(GlobalLogBuffer, "Not sent, buffered\n"); - }break; - - case WSAECONNABORTED: - case WSAENETUNREACH: - case WSAECONNRESET: - case WSAENOTCONN: - { - if (CloseSocket(Manager, Socket)) - { - Log_Error(GlobalLogBuffer, "Error: %d\n", Error); - Error = 0; - } - }break; - - InvalidDefaultCase; + Log_Error(GlobalLogBuffer, "Error: %d\n", Error); + Error = 0; } + }break; + + InvalidDefaultCase; } - else - { - Log_Message(GlobalLogBuffer, "Sent\n"); - } - - return LengthSent; + } + else + { + Log_Message(GlobalLogBuffer, "Sent\n"); + } + + return LengthSent; } internal s32 Win32Socket_SetOption(win32_socket* Socket, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength) { - int Error = setsockopt(Socket->Socket, Level, Option, OptionValue, OptionLength); - if (Error == SOCKET_ERROR) - { - Error = WSAGetLastError(); - // TODO(Peter): :ErrorLogging - } - - return Error; + int Error = setsockopt(Socket->Socket, Level, Option, OptionValue, OptionLength); + if (Error == SOCKET_ERROR) + { + Error = WSAGetLastError(); + // TODO(Peter): :ErrorLogging + } + + return Error; } internal s32 Win32Socket_SetOption(platform_socket_handle SocketHandle, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength) { - win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle); - return Win32Socket_SetOption(Socket, Level, Option, OptionValue, OptionLength); + win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle); + return Win32Socket_SetOption(Socket, Level, Option, OptionValue, OptionLength); } internal s32 Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, const char* Buffer, s32 BufferLength, s32 Flags) { - win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle); - - sockaddr_in SockAddress = {}; - SockAddress.sin_family = AF_INET; - SockAddress.sin_port = HostToNetU16(Port); - SockAddress.sin_addr.s_addr = HostToNetU32(Address); - - s32 LengthSent = sendto(Socket->Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); - - Log_Message(GlobalLogBuffer, "Attempting To Send Network Data: "); - if (LengthSent == SOCKET_ERROR) + win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle); + + sockaddr_in SockAddress = {}; + SockAddress.sin_family = AF_INET; + SockAddress.sin_port = HostToNetU16(Port); + SockAddress.sin_addr.s_addr = HostToNetU32(Address); + + s32 LengthSent = sendto(Socket->Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); + + Log_Message(GlobalLogBuffer, "Attempting To Send Network Data: "); + if (LengthSent == SOCKET_ERROR) + { + s32 Error = WSAGetLastError(); + switch (Error) { - s32 Error = WSAGetLastError(); - switch (Error) - { - case WSAENETUNREACH: - { - Log_Message(GlobalLogBuffer, - "Non Critical Error: WSAENETUNREACH \n"); - } break; - - default: - { - Log_Error(GlobalLogBuffer, "Error: %d \n", Error); - InvalidCodePath; - } break; - } + case WSAENETUNREACH: + { + Log_Message(GlobalLogBuffer, + "Non Critical Error: WSAENETUNREACH \n"); + } break; + + default: + { + Log_Error(GlobalLogBuffer, "Error: %d \n", Error); + InvalidCodePath; + } break; } - else - { - Log_Message(GlobalLogBuffer, "Sent\n"); - } - - return LengthSent; + } + else + { + Log_Message(GlobalLogBuffer, "Sent\n"); + } + + return LengthSent; } internal void Win32Socket_SetListening(win32_socket* Socket) { - if (listen(Socket->Socket, SOMAXCONN) == SOCKET_ERROR) - { - // TODO(pjs): Error logging - s32 Error = WSAGetLastError(); - InvalidCodePath; - } + if (listen(Socket->Socket, SOMAXCONN) == SOCKET_ERROR) + { + // TODO(pjs): Error logging + s32 Error = WSAGetLastError(); + InvalidCodePath; + } } internal s32 Win32Socket_PeekGetTotalSize(win32_socket* Socket) { - s32 Flags = MSG_PEEK; - char Temp[4]; - s32 BytesQueued = recv(Socket->Socket, Temp, 4, Flags); - if (BytesQueued == SOCKET_ERROR) - { - // TODO(pjs): Error logging - s32 Error = WSAGetLastError(); - BytesQueued = 0; - } - return BytesQueued; + s32 Flags = MSG_PEEK; + char Temp[4]; + s32 BytesQueued = recv(Socket->Socket, Temp, 4, Flags); + if (BytesQueued == SOCKET_ERROR) + { + // TODO(pjs): Error logging + s32 Error = WSAGetLastError(); + BytesQueued = 0; + } + return BytesQueued; } internal gs_data Win32Socket_Receive(win32_socket* Socket, gs_memory_arena* Storage) { #if 0 - gs_data Result = {}; - s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket); - if (BytesQueued > 0) - { - Result = PushSizeToData(Storage, BytesQueued); - s32 Flags = 0; - s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags); - Assert(BytesReceived == BytesQueued); - } - return Result; -#else - gs_data Result = PushSizeToData(Storage, 1024); + gs_data Result = {}; + s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket); + if (BytesQueued > 0) + { + Result = PushSizeToData(Storage, BytesQueued); s32 Flags = 0; s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags); - if (BytesReceived == SOCKET_ERROR) + Assert(BytesReceived == BytesQueued); + } + return Result; +#else + gs_data Result = PushSize(Storage, 1024); + s32 Flags = 0; + s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags); + if (BytesReceived == SOCKET_ERROR) + { + // TODO(pjs): Error logging + s32 Error = WSAGetLastError(); + switch (Error) { - // TODO(pjs): Error logging - s32 Error = WSAGetLastError(); - switch (Error) - { - case WSAECONNABORTED: - case WSANOTINITIALISED: - break; - - case WSAENOTCONN: - { - - }break; - InvalidDefaultCase; - } + case WSAECONNABORTED: + case WSANOTINITIALISED: + break; + + case WSAENOTCONN: + { + + }break; + InvalidDefaultCase; } - Result.Size = BytesReceived; - return Result; + } + Result.Size = BytesReceived; + return Result; #endif } internal void Win32Socket_Close(win32_socket* Socket) { - closesocket(Socket->Socket); - Socket->Socket = INVALID_SOCKET; + closesocket(Socket->Socket); + Socket->Socket = INVALID_SOCKET; } internal void Win32Socket_CloseArray(win32_socket_array Array) { - for (s32 i = 0; i < Array.Count; i++) - { - win32_socket* Socket = Array.Values + i; - Win32Socket_Close(Socket); - } + for (s32 i = 0; i < Array.Count; i++) + { + win32_socket* Socket = Array.Values + i; + Win32Socket_Close(Socket); + } } ////////////////////// @@ -514,30 +514,30 @@ Win32Socket_CloseArray(win32_socket_array Array) internal void Win32SocketSystem_Init(gs_memory_arena* Arena) { - WSAStartup(MAKEWORD(2, 2), &WSAData); - Win32Sockets = Win32SocketArray_Create(16, Arena); + WSAStartup(MAKEWORD(2, 2), &WSAData); + Win32Sockets = Win32SocketArray_Create(16, Arena); } internal void Win32SocketSystem_Cleanup() { - Win32Socket_CloseArray(Win32Sockets); - - s32 CleanupResult = 0; - do { - CleanupResult = WSACleanup(); - }while(CleanupResult == SOCKET_ERROR); + Win32Socket_CloseArray(Win32Sockets); + + s32 CleanupResult = 0; + do { + CleanupResult = WSACleanup(); + }while(CleanupResult == SOCKET_ERROR); } PLATFORM_GET_SOCKET_HANDLE(Win32GetSocketHandle) { - s32 Result = Win32SocketArray_Take(&Win32Sockets); - s32 Error = 0; - win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, Result); - *Socket = Win32Socket_Create(AF_INET, SOCK_DGRAM, 0); - Error = Win32Socket_SetOption(Socket, IPPROTO_IP, IP_MULTICAST_TTL, - (const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive)); - return (platform_socket_handle)Result; + s32 Result = Win32SocketArray_Take(&Win32Sockets); + s32 Error = 0; + win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, Result); + *Socket = Win32Socket_Create(AF_INET, SOCK_DGRAM, 0); + Error = Win32Socket_SetOption(Socket, IPPROTO_IP, IP_MULTICAST_TTL, + (const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive)); + return (platform_socket_handle)Result; } #define WIN32_FOLDHAUS_SOCKET_H diff --git a/src/app/platform_win32/win32_foldhaus_work_queue.h b/src/app/platform_win32/win32_foldhaus_work_queue.h index f35259e..7c56418 100644 --- a/src/app/platform_win32/win32_foldhaus_work_queue.h +++ b/src/app/platform_win32/win32_foldhaus_work_queue.h @@ -7,22 +7,22 @@ struct worker_thread_entry { - b32 IsValid; - u32 Index; + b32 IsValid; + u32 Index; }; struct worker_thread_info { - gs_thread_context ThreadContext; - HANDLE Handle; - gs_work_queue* Queue; + gs_thread_context ThreadContext; + HANDLE Handle; + gs_work_queue* Queue; }; struct win32_work_queue { - u32 ThreadCount; - worker_thread_info* Threads; - gs_work_queue WorkQueue; + u32 ThreadCount; + worker_thread_info* Threads; + gs_work_queue WorkQueue; }; worker_thread_info* WorkerThreads; @@ -31,219 +31,219 @@ win32_work_queue Win32WorkQueue; internal s32 Win32GetThreadId() { - s32 Result = GetCurrentThreadId(); - return Result; + s32 Result = GetCurrentThreadId(); + return Result; } internal gs_thread_context Win32CreateThreadContext(gs_memory_arena* Transient = 0) { - gs_thread_context Result = {0}; - Result.ThreadInfo.ThreadID = Win32GetThreadId(); - Result.Allocator = CreateAllocator(Win32Alloc, Win32Free); - if (Transient != 0) - { - Result.Transient = Transient; - } - else - { - Result.Transient = (gs_memory_arena*)AllocatorAlloc(Result.Allocator, sizeof(gs_memory_arena)).Memory; - *Result.Transient = CreateMemoryArena(Result.Allocator, "Tctx Transient"); - } - Result.FileHandler = CreateFileHandler(Win32GetFileInfo, - Win32ReadEntireFile, - Win32WriteEntireFile, - Win32EnumerateDirectory, - Result.Transient); - - Result.DebugOutput.Print = Win32DebugPrint; - - return Result; + gs_thread_context Result = {0}; + Result.ThreadInfo.ThreadID = Win32GetThreadId(); + Result.Allocator = CreatePlatformAllocator(); + if (Transient != 0) + { + Result.Transient = Transient; + } + else + { + Result.Transient = AllocStruct(Result.Allocator, gs_memory_arena, "Work Queue"); + *Result.Transient = MemoryArenaCreate(MB(4), Bytes(8), Result.Allocator, 0, 0, "Tctx Transient"); + } + Result.FileHandler = CreateFileHandler(Win32GetFileInfo, + Win32ReadEntireFile, + Win32WriteEntireFile, + Win32EnumerateDirectory, + Result.Transient); + + Result.DebugOutput.Print = Win32DebugPrint; + + return Result; } PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue) { #if DEBUG - // NOTE(Peter): Just prints out the names of all the pending jobs if we end up - // overflowing the buffer - if (Queue->JobsCount >= Queue->JobsMax) + // NOTE(Peter): Just prints out the names of all the pending jobs if we end up + // overflowing the buffer + if (Queue->JobsCount >= Queue->JobsMax) + { + gs_string DebugString = MakeString((char*)malloc(256), 256); + for (u32 i = 0; i < Queue->JobsCount; i++) { - gs_string DebugString = MakeString((char*)malloc(256), 256); - for (u32 i = 0; i < Queue->JobsCount; i++) - { - Log_Message(GlobalLogBuffer, "%d %s \n", i, Queue->Jobs[i].JobName); - } + Log_Message(GlobalLogBuffer, "%d %s \n", i, Queue->Jobs[i].JobName); } + } #endif - Assert(Queue->JobsCount < Queue->JobsMax); - - gs_threaded_job* Job = Queue->Jobs + Queue->JobsCount; - Job->WorkProc = WorkProc; - Job->Data = Data; + Assert(Queue->JobsCount < Queue->JobsMax); + + gs_threaded_job* Job = Queue->Jobs + Queue->JobsCount; + Job->WorkProc = WorkProc; + Job->Data = Data; #if DEBUG - Job->JobName = JobName; + Job->JobName = JobName; #endif - - // Complete Past Writes before Future Writes - _WriteBarrier(); - _mm_sfence(); - - ++Queue->JobsCount; - ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0); + + // Complete Past Writes before Future Writes + _WriteBarrier(); + _mm_sfence(); + + ++Queue->JobsCount; + ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0); } internal worker_thread_entry CompleteAndTakeNextJob(gs_work_queue* Queue, worker_thread_entry Completed, gs_thread_context Context) { - if (Completed.IsValid) + if (Completed.IsValid) + { + InterlockedIncrement((LONG volatile*)&Queue->JobsCompleted); + } + + worker_thread_entry Result = {}; + Result.IsValid = false; + + u32 OriginalNextJobIndex = Queue->NextJobIndex; + while (OriginalNextJobIndex < Queue->JobsCount) + { + u32 Index = InterlockedCompareExchange((LONG volatile*)&Queue->NextJobIndex, + OriginalNextJobIndex + 1, + OriginalNextJobIndex); + if (Index == OriginalNextJobIndex) { - InterlockedIncrement((LONG volatile*)&Queue->JobsCompleted); + Result.Index = Index; + Result.IsValid = true; + break; } - - worker_thread_entry Result = {}; - Result.IsValid = false; - - u32 OriginalNextJobIndex = Queue->NextJobIndex; - while (OriginalNextJobIndex < Queue->JobsCount) - { - u32 Index = InterlockedCompareExchange((LONG volatile*)&Queue->NextJobIndex, - OriginalNextJobIndex + 1, - OriginalNextJobIndex); - if (Index == OriginalNextJobIndex) - { - Result.Index = Index; - Result.IsValid = true; - break; - } - OriginalNextJobIndex = Queue->NextJobIndex; - } - - return Result; + OriginalNextJobIndex = Queue->NextJobIndex; + } + + return Result; } COMPLETE_QUEUE_WORK(Win32DoQueueWorkUntilDone) { - worker_thread_entry Entry = {}; - Entry.IsValid = false; - while (Queue->JobsCompleted < Queue->JobsCount) + worker_thread_entry Entry = {}; + Entry.IsValid = false; + while (Queue->JobsCompleted < Queue->JobsCount) + { + Entry = CompleteAndTakeNextJob(Queue, Entry, Context); + if (Entry.IsValid) { - Entry = CompleteAndTakeNextJob(Queue, Entry, Context); - if (Entry.IsValid) - { - Queue->Jobs[Entry.Index].WorkProc(Context, Queue->Jobs[Entry.Index].Data); - } + Queue->Jobs[Entry.Index].WorkProc(Context, Queue->Jobs[Entry.Index].Data); } + } } DWORD WINAPI WorkerThreadProc (LPVOID InputThreadInfo) { - worker_thread_info* ThreadInfo = (worker_thread_info*)InputThreadInfo; - ThreadInfo->ThreadContext = Win32CreateThreadContext(); - - worker_thread_entry Entry = {}; - Entry.IsValid = false; - while (true) + worker_thread_info* ThreadInfo = (worker_thread_info*)InputThreadInfo; + ThreadInfo->ThreadContext = Win32CreateThreadContext(); + + worker_thread_entry Entry = {}; + Entry.IsValid = false; + while (true) + { + MemoryArenaClear(ThreadInfo->ThreadContext.Transient); + Entry = CompleteAndTakeNextJob(ThreadInfo->Queue, Entry, ThreadInfo->ThreadContext); + if (Entry.IsValid) { - ClearArena(ThreadInfo->ThreadContext.Transient); - Entry = CompleteAndTakeNextJob(ThreadInfo->Queue, Entry, ThreadInfo->ThreadContext); - if (Entry.IsValid) - { - ThreadInfo->Queue->Jobs[Entry.Index].WorkProc(ThreadInfo->ThreadContext, - ThreadInfo->Queue->Jobs[Entry.Index].Data); - } - else - { - WaitForSingleObjectEx(ThreadInfo->Queue->SemaphoreHandle, INFINITE, 0); - } + ThreadInfo->Queue->Jobs[Entry.Index].WorkProc(ThreadInfo->ThreadContext, + ThreadInfo->Queue->Jobs[Entry.Index].Data); } - - return 0; + else + { + WaitForSingleObjectEx(ThreadInfo->Queue->SemaphoreHandle, INFINITE, 0); + } + } + + return 0; } DWORD WINAPI Win32ThreadProcWrapper(LPVOID ThreadInfo) { - platform_thread* Thread = (platform_thread*)ThreadInfo; - gs_thread_context Ctx = Win32CreateThreadContext(); - Thread->Proc(&Ctx, Thread->UserData); - - // TODO(pjs): Destroy Thread Context - // TODO(pjs): How do we notify the thread manager this thread belongs to that it is free? - // Probaby put a pointer to the thread manager in the platform_thread struct - // so we can update the tracking structure? - - return 0; + platform_thread* Thread = (platform_thread*)ThreadInfo; + gs_thread_context Ctx = Win32CreateThreadContext(); + Thread->Proc(&Ctx, Thread->UserData); + + // TODO(pjs): Destroy Thread Context + // TODO(pjs): How do we notify the thread manager this thread belongs to that it is free? + // Probaby put a pointer to the thread manager in the platform_thread struct + // so we can update the tracking structure? + + return 0; } CREATE_THREAD(Win32CreateThread) { - Thread->Proc = Proc; - Thread->UserData = UserData; - - // TODO(pjs): ugh, allocation out in the middle of nowhere - HANDLE* ThreadHandle = AllocatorAllocStruct(Ctx.Allocator, HANDLE); - *ThreadHandle = CreateThread(0, 0, Win32ThreadProcWrapper, (void*)Thread, 0, 0); - // TODO(pjs): Error checking on the created thread - - Thread->PlatformHandle = (u8*)ThreadHandle; - - return true; + Thread->Proc = Proc; + Thread->UserData = UserData; + + // TODO(pjs): ugh, allocation out in the middle of nowhere + HANDLE* ThreadHandle = AllocStruct(Ctx.Allocator, HANDLE, "Create Thread"); + *ThreadHandle = CreateThread(0, 0, Win32ThreadProcWrapper, (void*)Thread, 0, 0); + // TODO(pjs): Error checking on the created thread + + Thread->PlatformHandle = (u8*)ThreadHandle; + + return true; } KILL_THREAD(Win32KillThread) { - HANDLE* ThreadHandle = (HANDLE*)Thread->PlatformHandle; - TerminateThread(ThreadHandle, 0); - - // TODO(pjs): see allocation out in the middle of nowhere in Win32CreateThread - Win32Free((void*)Thread->PlatformHandle, sizeof(HANDLE)); - - // TODO(pjs): Error checking - return true; + HANDLE* ThreadHandle = (HANDLE*)Thread->PlatformHandle; + TerminateThread(ThreadHandle, 0); + + // TODO(pjs): see allocation out in the middle of nowhere in Win32CreateThread + Win32Free((void*)Thread->PlatformHandle, sizeof(HANDLE), 0); + + // TODO(pjs): Error checking + return true; } internal void Win32WorkQueue_Init(gs_memory_arena* Arena, u32 ThreadCount) { - if (ThreadCount > 0) - { - Win32WorkQueue.ThreadCount = ThreadCount; - Win32WorkQueue.Threads = PushArray(Arena, worker_thread_info, ThreadCount); - } - - gs_work_queue WQ = {}; - WQ.SemaphoreHandle = CreateSemaphoreEx(0, 0, ThreadCount, 0, 0, SEMAPHORE_ALL_ACCESS);; - WQ.JobsMax = 512; - WQ.Jobs = PushArray(Arena, gs_threaded_job, WQ.JobsMax); - WQ.NextJobIndex = 0; - WQ.PushWorkOnQueue = Win32PushWorkOnQueue; - WQ.CompleteQueueWork = Win32DoQueueWorkUntilDone; - - Win32WorkQueue.WorkQueue = WQ; - - // ID = 0 is reserved for this thread - for (u32 i = 0; i < ThreadCount; i++) - { - worker_thread_info* T = Win32WorkQueue.Threads + i; - T->Queue = &Win32WorkQueue.WorkQueue; - T->Handle = CreateThread(0, 0, &WorkerThreadProc, (void*)T, 0, 0); - } + if (ThreadCount > 0) + { + Win32WorkQueue.ThreadCount = ThreadCount; + Win32WorkQueue.Threads = PushArray(Arena, worker_thread_info, ThreadCount); + } + + gs_work_queue WQ = {}; + WQ.SemaphoreHandle = CreateSemaphoreEx(0, 0, ThreadCount, 0, 0, SEMAPHORE_ALL_ACCESS);; + WQ.JobsMax = 512; + WQ.Jobs = PushArray(Arena, gs_threaded_job, WQ.JobsMax); + WQ.NextJobIndex = 0; + WQ.PushWorkOnQueue = Win32PushWorkOnQueue; + WQ.CompleteQueueWork = Win32DoQueueWorkUntilDone; + + Win32WorkQueue.WorkQueue = WQ; + + // ID = 0 is reserved for this thread + for (u32 i = 0; i < ThreadCount; i++) + { + worker_thread_info* T = Win32WorkQueue.Threads + i; + T->Queue = &Win32WorkQueue.WorkQueue; + T->Handle = CreateThread(0, 0, &WorkerThreadProc, (void*)T, 0, 0); + } } internal void Win32WorkQueue_Cleanup() { - u32 Error = 0; - for (u32 Thread = 0; Thread < Win32WorkQueue.ThreadCount; Thread++) + u32 Error = 0; + for (u32 Thread = 0; Thread < Win32WorkQueue.ThreadCount; Thread++) + { + u32 Success = TerminateThread(Win32WorkQueue.Threads[Thread].Handle, 0); + if (!Success) { - u32 Success = TerminateThread(Win32WorkQueue.Threads[Thread].Handle, 0); - if (!Success) - { - Error = GetLastError(); - InvalidCodePath; - } + Error = GetLastError(); + InvalidCodePath; } + } } #define WIN32_FOLDHAUS_WORK_QUEUE_H diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 349db9c..5dcc0a8 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -8,72 +8,72 @@ internal animation_handle_array LoadAllAnimationsInDir(gs_const_string Path, blumen_lumen_state* BLState, app_state* State, context Context) { - animation_handle_array Result = {}; - - gs_thread_context Ctx = Context.ThreadContext; - gs_file_info_array FilesInDir = EnumerateDirectory(Ctx.FileHandler, State->Transient, Path, 0); - - Result.Count = FilesInDir.Count; - Result.Handles = PushArray(&State->Permanent, animation_handle, Result.Count); - - for (u32 i = 0; i < FilesInDir.Count; i++) - { - gs_file_info File = FilesInDir.Values[i]; - Result.Handles[i] = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - File.Path); - } - - return Result; + animation_handle_array Result = {}; + + gs_thread_context Ctx = Context.ThreadContext; + gs_file_info_array FilesInDir = EnumerateDirectory(Ctx.FileHandler, State->Transient, Path, 0); + + Result.Count = FilesInDir.Count; + Result.Handles = PushArray(&State->Permanent, animation_handle, Result.Count); + + for (u32 i = 0; i < FilesInDir.Count; i++) + { + gs_file_info File = FilesInDir.Values[i]; + Result.Handles[i] = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + File.Path); + } + + return Result; } internal s32 GetCCIndex (assembly Assembly, blumen_lumen_state* BLState) { - s32 Result = 0; - - u64 AssemblyNameHash = HashDJB2ToU32(StringExpand(Assembly.Name)); - for (u32 i = 0; i < BLState->AssemblyNameToClearCoreMapCount; i++) + s32 Result = 0; + + u64 AssemblyNameHash = HashDJB2ToU32(StringExpand(Assembly.Name)); + for (u32 i = 0; i < BLState->AssemblyNameToClearCoreMapCount; i++) + { + if (AssemblyNameHash == BLState->AssemblyNameToClearCore_Names[i]) { - if (AssemblyNameHash == BLState->AssemblyNameToClearCore_Names[i]) - { - Result = (s32)i; - break; - } + Result = (s32)i; + break; } - - return Result; + } + + return Result; } internal void DEBUG_AppendText(gs_string Str, gs_thread_context Ctx) { - gs_const_string DebugPath = ConstString("data/debug_motor_changes.txt"); - gs_file DebugFile = ReadEntireFile(Ctx.FileHandler, - DebugPath); - gs_string NewString = PushString(Ctx.Transient, DebugFile.Size + Str.Size + 16); - if (DebugFile.Size > 0) - { - PrintF(&NewString, "%.*s\nENTRY:\n", DebugFile.Size, (char*)DebugFile.Memory); - } - AppendPrintF(&NewString, "%S\n", Str.ConstString); - NullTerminate(&NewString); - - if (!WriteEntireFile(Ctx.FileHandler, DebugPath, StringToData(NewString))) - { - InvalidCodePath; - } + gs_const_string DebugPath = ConstString("data/debug_motor_changes.txt"); + gs_file DebugFile = ReadEntireFile(Ctx.FileHandler, + DebugPath); + gs_string NewString = PushString(Ctx.Transient, DebugFile.Size + Str.Size + 16); + if (DebugFile.Size > 0) + { + PrintF(&NewString, "%.*s\nENTRY:\n", DebugFile.Size, (char*)DebugFile.Memory); + } + AppendPrintF(&NewString, "%S\n", Str.ConstString); + NullTerminate(&NewString); + + if (!WriteEntireFile(Ctx.FileHandler, DebugPath, StringToData(NewString))) + { + InvalidCodePath; + } } internal void DEBUG_SentMotorCommand(motor_packet Packet, gs_thread_context Ctx) { - Log_Message(GlobalLogBuffer, - "Motor Command Sent\nRequested Positions: %d %d %d\n", - Packet.FlowerPositions[0], - Packet.FlowerPositions[1], - Packet.FlowerPositions[2]); + Log_Message(GlobalLogBuffer, + "Motor Command Sent\nRequested Positions: %d %d %d\n", + Packet.FlowerPositions[0], + Packet.FlowerPositions[1], + Packet.FlowerPositions[2]); } internal void @@ -81,1021 +81,1021 @@ DEBUG_ReceivedMotorPositions(blumen_lumen_state* BLState, motor_status_packet NewPos, gs_thread_context Ctx) { - motor_packet LastPos = BLState->LastKnownMotorState; - bool PosChanged = (LastPos.FlowerPositions[0] != NewPos.Pos.FlowerPositions[0] || - LastPos.FlowerPositions[1] != NewPos.Pos.FlowerPositions[1] || - LastPos.FlowerPositions[2] != NewPos.Pos.FlowerPositions[2]); - - if (PosChanged) - { - Log_Message(GlobalLogBuffer, - "Motor Status Received\nCurrent Positions: %d %d %d\n", - NewPos.Pos.FlowerPositions[0], - NewPos.Pos.FlowerPositions[1], - NewPos.Pos.FlowerPositions[2]); - } + motor_packet LastPos = BLState->LastKnownMotorState; + bool PosChanged = (LastPos.FlowerPositions[0] != NewPos.Pos.FlowerPositions[0] || + LastPos.FlowerPositions[1] != NewPos.Pos.FlowerPositions[1] || + LastPos.FlowerPositions[2] != NewPos.Pos.FlowerPositions[2]); + + if (PosChanged) + { + Log_Message(GlobalLogBuffer, + "Motor Status Received\nCurrent Positions: %d %d %d\n", + NewPos.Pos.FlowerPositions[0], + NewPos.Pos.FlowerPositions[1], + NewPos.Pos.FlowerPositions[2]); + } } internal void DEBUG_ReceivedTemperature(temp_packet Temp, gs_thread_context Ctx) { - Log_Message(GlobalLogBuffer, - "\nTemperature: %d\n", - Temp.Temperature); + Log_Message(GlobalLogBuffer, + "\nTemperature: %d\n", + Temp.Temperature); } internal void BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) { - mic_listen_job_data* Data = (mic_listen_job_data*)UserData; - - gs_data Msg = {}; - - u8 WeathermanIPAddr[4] = {}; - WeathermanIPAddr[0] = 127; - WeathermanIPAddr[1] = 0; - WeathermanIPAddr[2] = 0; - WeathermanIPAddr[3] = 1; - - u32 WeathermanIPV4 = (u32)UpackB4(WeathermanIPAddr); - u32 WeathermanPort = 20185; - - platform_socket_handle_ ListenSocket = {0}; - - while (*Data->Running) + mic_listen_job_data* Data = (mic_listen_job_data*)UserData; + + gs_data Msg = {}; + + u8 WeathermanIPAddr[4] = {}; + WeathermanIPAddr[0] = 127; + WeathermanIPAddr[1] = 0; + WeathermanIPAddr[2] = 0; + WeathermanIPAddr[3] = 1; + + u32 WeathermanIPV4 = (u32)UpackB4(WeathermanIPAddr); + u32 WeathermanPort = 20185; + + platform_socket_handle_ ListenSocket = {0}; + + while (*Data->Running) + { + if (!SocketQueryStatus(Data->SocketManager, ListenSocket)) { - if (!SocketQueryStatus(Data->SocketManager, ListenSocket)) - { - Data->IsConnected = false; - if (SocketHandleIsValid(ListenSocket)) - { - Log_Message(GlobalLogBuffer, "Disconnected from Python Server\n"); - CloseSocket(Data->SocketManager, ListenSocket); - } - ListenSocket = CreateSocket(Data->SocketManager, "127.0.0.1", "20185"); - if (ListenSocket.Index != 0) - { - Log_Message(GlobalLogBuffer, "Connected to Python Server\n"); - Data->IsConnected = true; - } - } - - if (SocketQueryStatus(Data->SocketManager, ListenSocket)) - { - if (SocketPeek(Data->SocketManager, ListenSocket)) - { - Msg = SocketRecieve(Data->SocketManager, ListenSocket, Ctx->Transient); - if (Msg.Size > 0) - { - MessageQueue_Write(Data->IncomingMsgQueue, Msg); - } - } - - while (MessageQueue_CanRead(*Data->OutgoingMsgQueue)) - { - Msg = MessageQueue_Peek(Data->OutgoingMsgQueue); - - u32 Address = WeathermanIPV4; - u32 Port = WeathermanPort; - s32 Flags = 0; - s32 LengthSent = SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags); - if (LengthSent != 0) - { - // if we actually sent the message, THEN we pull it off the - // message queue - MessageQueue_Read(Data->OutgoingMsgQueue); - } else { - break; - } - } - } + Data->IsConnected = false; + if (SocketHandleIsValid(ListenSocket)) + { + Log_Message(GlobalLogBuffer, "Disconnected from Python Server\n"); + CloseSocket(Data->SocketManager, ListenSocket); + } + ListenSocket = CreateSocket(Data->SocketManager, "127.0.0.1", "20185"); + if (ListenSocket.Index != 0) + { + Log_Message(GlobalLogBuffer, "Connected to Python Server\n"); + Data->IsConnected = true; + } } - CloseSocket(Data->SocketManager, ListenSocket); + if (SocketQueryStatus(Data->SocketManager, ListenSocket)) + { + if (SocketPeek(Data->SocketManager, ListenSocket)) + { + Msg = SocketRecieve(Data->SocketManager, ListenSocket, Ctx->Transient); + if (Msg.Size > 0) + { + MessageQueue_Write(Data->IncomingMsgQueue, Msg); + } + } + + while (MessageQueue_CanRead(*Data->OutgoingMsgQueue)) + { + Msg = MessageQueue_Peek(Data->OutgoingMsgQueue); + + u32 Address = WeathermanIPV4; + u32 Port = WeathermanPort; + s32 Flags = 0; + s32 LengthSent = SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags); + if (LengthSent != 0) + { + // if we actually sent the message, THEN we pull it off the + // message queue + MessageQueue_Read(Data->OutgoingMsgQueue); + } else { + break; + } + } + } + } + + CloseSocket(Data->SocketManager, ListenSocket); } internal void BlumenLumen_SetPatternMode(bl_pattern_mode Mode, r32 FadeDuration, animation_system* System, blumen_lumen_state* BLState) { - BLState->PatternMode = Mode; - animation_handle_array Playlist = BLState->ModeAnimations[Mode]; - System->RepeatMode = AnimationRepeat_Loop; - System->PlaylistFadeTime = FadeDuration; - AnimationSystem_FadeToPlaylist(System, Playlist); + BLState->PatternMode = Mode; + animation_handle_array Playlist = BLState->ModeAnimations[Mode]; + System->RepeatMode = AnimationRepeat_Loop; + System->PlaylistFadeTime = FadeDuration; + AnimationSystem_FadeToPlaylist(System, Playlist); } internal void BlumenLumen_LoadPatterns(app_state* State) { - animation_pattern_array* Patterns = &State->Patterns; - if (Patterns->CountMax == 0) - { - *Patterns = Patterns_Create(&State->Permanent, 32); - } - - Patterns->Count = 0; - Patterns_PushPattern(Patterns, Pattern_None, PATTERN_SINGLETHREADED); - Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Rainbow, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED); - Patterns_PushPattern(Patterns, Pattern_VerticalLines, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Rotary, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_VoiceAddIns, PATTERN_MULTITHREADED); - - Patterns_PushPattern(Patterns, Pattern_StemSolid, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED); - - Patterns_PushPattern(Patterns, Pattern_GrowFadeMask, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_RainbowLoadingBar, PATTERN_MULTITHREADED); - - Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED); + animation_pattern_array* Patterns = &State->Patterns; + if (Patterns->CountMax == 0) + { + *Patterns = Patterns_Create(&State->Permanent, 32); + } + + Patterns->Count = 0; + Patterns_PushPattern(Patterns, Pattern_None, PATTERN_SINGLETHREADED); + Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Rainbow, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED); + Patterns_PushPattern(Patterns, Pattern_VerticalLines, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Rotary, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_VoiceAddIns, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_StemSolid, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_GrowFadeMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_RainbowLoadingBar, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED); } internal void AppendPrintDate(gs_string* WriteStr, system_time Time) { - AppendPrintF(WriteStr, "%d-%d-%d : %d:%d:%d\n\n", - Time.Year, Time.Month, Time.Day, - Time.Hour, Time.Minute, Time.Second); + AppendPrintF(WriteStr, "%d-%d-%d : %d:%d:%d\n\n", + Time.Year, Time.Month, Time.Day, + Time.Hour, Time.Minute, Time.Second); } internal void BlumenLumen_AppendBootupLog(app_state* State, blumen_lumen_state* BLState, context Context) { - gs_thread_context Ctx = Context.ThreadContext; - gs_const_string BootupLogPath = ConstString("lumenarium_boot_log.log"); - - gs_file BootLog = ReadEntireFile(Ctx.FileHandler, BootupLogPath); - gs_string WriteStr = {}; - - // we don't want the log file getting huge - // if it gets above some threshold, instead of appending, - // copy what there is to an _old file, and start this one over - // - // The thinking is that without the copy operation, when we reached - // our threshold, we'd overwrite the whole log. If something went - // wrong at that point, we'd have nothing to go on. This way, there is - // always some historical data present on the system - // - if (BootLog.Size < MB(4)) + gs_thread_context Ctx = Context.ThreadContext; + gs_const_string BootupLogPath = ConstString("lumenarium_boot_log.log"); + + gs_file BootLog = ReadEntireFile(Ctx.FileHandler, BootupLogPath); + gs_string WriteStr = {}; + + // we don't want the log file getting huge + // if it gets above some threshold, instead of appending, + // copy what there is to an _old file, and start this one over + // + // The thinking is that without the copy operation, when we reached + // our threshold, we'd overwrite the whole log. If something went + // wrong at that point, we'd have nothing to go on. This way, there is + // always some historical data present on the system + // + if (BootLog.Size < MB(4)) + { + WriteStr = PushString(State->Transient, BootLog.Size + 1024); + } + else + { + if (!WriteEntireFile(Ctx.FileHandler, ConstString("lumenarium_boot_log_old.log"), + BootLog.Data)) { - WriteStr = PushString(State->Transient, BootLog.Size + 1024); + InvalidCodePath; } - else - { - if (!WriteEntireFile(Ctx.FileHandler, ConstString("lumenarium_boot_log_old.log"), - BootLog.Data)) - { - InvalidCodePath; - } - WriteStr = PushString(State->Transient, 1024); - } - - - // Copy old entries in - if (BootLog.Size > 0) - { - PrintF(&WriteStr, "%.*s", BootLog.Size, BootLog.Memory); - } - - // New Entry - AppendPrintF(&WriteStr, "Lumenarium Restarted\n"); - AppendPrintF(&WriteStr, "* Time: "); - AppendPrintDate(&WriteStr, Context.SystemTime_Current); - - gs_data Data = StringToData(WriteStr); - WriteEntireFile(Ctx.FileHandler, BootupLogPath, Data); + WriteStr = PushString(State->Transient, 1024); + } + + + // Copy old entries in + if (BootLog.Size > 0) + { + PrintF(&WriteStr, "%.*s", BootLog.Size, BootLog.Memory); + } + + // New Entry + AppendPrintF(&WriteStr, "Lumenarium Restarted\n"); + AppendPrintF(&WriteStr, "* Time: "); + AppendPrintDate(&WriteStr, Context.SystemTime_Current); + + gs_data Data = StringToData(WriteStr); + WriteEntireFile(Ctx.FileHandler, BootupLogPath, Data); } internal void BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Context) { - if (!BLState->ShouldUpdateLog) return; - - gs_string FileStr = PushString(State->Transient, 1024); - AppendPrintF(&FileStr, "Lumenarium Status\n"); - - AppendPrintF(&FileStr, "Last Updated At:"); - AppendPrintDate(&FileStr, Context.SystemTime_Current); - AppendPrintF(&FileStr, "\n\n"); - - animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); - - bool IsPlaying = State->AnimationSystem.TimelineShouldAdvance; - AppendPrintF(&FileStr, "\tIs Playing: %s\n", - IsPlaying ? "True" : "False"); - - char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected"; - AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected); - - u8 MP0 = BLState->LastKnownMotorState.FlowerPositions[0]; - u8 MP1 = BLState->LastKnownMotorState.FlowerPositions[1]; - u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2]; - AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2); - - time_range MotorRange = {}; - if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, - MotorOpenTimes, - MotorOpenTimesCount, - &MotorRange)) - { - AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: ( %d:%d - %d:%d)\n", - MotorRange.StartHour, MotorRange.StartMinute, - MotorRange.EndHour, MotorRange.EndMinute); - } - else - { - AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: None\n"); - } - - char* PatternMode = 0; - switch (BLState->PatternMode) - { - case BlumenPattern_Standard: { PatternMode = "Standard"; } break; - case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break; - case BlumenPattern_NoControl: { PatternMode = "No Control: Someone's doing the Awaken sequence!"; } break; - } - AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode); - - phrase_hue LastHuePhrase = BLState->LastHuePhrase; - AppendPrintF(&FileStr, "Last Mic Phrase: %S\n", LastHuePhrase.Phrase); - - AppendPrintF(&FileStr, "Pattern Speed: %f\n", BLState->PatternSpeed); - - AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent); - - time_range RangeIn = {}; - if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, - LedOnTimes, - LedOnTimesCount, - &RangeIn)) - { - AppendPrintF(&FileStr, "\tIn Leds-On Time Range: ( %d:%d - %d:%d)\n", - RangeIn.StartHour, RangeIn.StartMinute, - RangeIn.EndHour, RangeIn.EndMinute); - } - else - { - AppendPrintF(&FileStr, "\tIn Leds-On Time Range: None\n"); - } - - AppendPrintF(&FileStr, "\tTemp Dimming: %s\n", - Blumen_TempShouldDimLeds(BLState) ? "On" : "Off"); - - AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived); - - gs_data LogMem = StringToData(FileStr); - if (!WriteEntireFile(Context.ThreadContext.FileHandler, ConstString("lumenarium_status.log"), LogMem)) - { - InvalidCodePath; - } + if (!BLState->ShouldUpdateLog) return; + + gs_string FileStr = PushString(State->Transient, 1024); + AppendPrintF(&FileStr, "Lumenarium Status\n"); + + AppendPrintF(&FileStr, "Last Updated At:"); + AppendPrintDate(&FileStr, Context.SystemTime_Current); + AppendPrintF(&FileStr, "\n\n"); + + animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); + + bool IsPlaying = State->AnimationSystem.TimelineShouldAdvance; + AppendPrintF(&FileStr, "\tIs Playing: %s\n", + IsPlaying ? "True" : "False"); + + char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected"; + AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected); + + u8 MP0 = BLState->LastKnownMotorState.FlowerPositions[0]; + u8 MP1 = BLState->LastKnownMotorState.FlowerPositions[1]; + u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2]; + AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2); + + time_range MotorRange = {}; + if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, + MotorOpenTimes, + MotorOpenTimesCount, + &MotorRange)) + { + AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: ( %d:%d - %d:%d)\n", + MotorRange.StartHour, MotorRange.StartMinute, + MotorRange.EndHour, MotorRange.EndMinute); + } + else + { + AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: None\n"); + } + + char* PatternMode = 0; + switch (BLState->PatternMode) + { + case BlumenPattern_Standard: { PatternMode = "Standard"; } break; + case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break; + case BlumenPattern_NoControl: { PatternMode = "No Control: Someone's doing the Awaken sequence!"; } break; + } + AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode); + + phrase_hue LastHuePhrase = BLState->LastHuePhrase; + AppendPrintF(&FileStr, "Last Mic Phrase: %S\n", LastHuePhrase.Phrase); + + AppendPrintF(&FileStr, "Pattern Speed: %f\n", BLState->PatternSpeed); + + AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent); + + time_range RangeIn = {}; + if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, + LedOnTimes, + LedOnTimesCount, + &RangeIn)) + { + AppendPrintF(&FileStr, "\tIn Leds-On Time Range: ( %d:%d - %d:%d)\n", + RangeIn.StartHour, RangeIn.StartMinute, + RangeIn.EndHour, RangeIn.EndMinute); + } + else + { + AppendPrintF(&FileStr, "\tIn Leds-On Time Range: None\n"); + } + + AppendPrintF(&FileStr, "\tTemp Dimming: %s\n", + Blumen_TempShouldDimLeds(BLState) ? "On" : "Off"); + + AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived); + + gs_data LogMem = StringToData(FileStr); + if (!WriteEntireFile(Context.ThreadContext.FileHandler, ConstString("lumenarium_status.log"), LogMem)) + { + InvalidCodePath; + } } internal gs_data BlumenLumen_CustomInit(app_state* State, context Context) { - // This is memory for any custom data that we want to use - // as a part of a particular sculpture. - // By returning it from here, it will be sent as an argument to - // the sculpture's CustomUpdate function; - gs_data Result = {}; - - Result = PushSizeToData(&State->Permanent, sizeof(blumen_lumen_state)); - - blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory; - BLState->Running = true; - BLState->BrightnessPercent = 1; - MessageQueue_Init(&BLState->IncomingMsgQueue, &State->Permanent); - MessageQueue_Init(&BLState->OutgoingMsgQueue, &State->Permanent); - - BLState->MicListenJobData.Running = &BLState->Running; - BLState->MicListenJobData.SocketManager = Context.SocketManager; - BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue; - BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; - - BLState->PatternSpeed = GlobalAnimSpeed; - + // This is memory for any custom data that we want to use + // as a part of a particular sculpture. + // By returning it from here, it will be sent as an argument to + // the sculpture's CustomUpdate function; + gs_data Result = {}; + + Result = PushSize(&State->Permanent, sizeof(blumen_lumen_state)); + + blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory; + BLState->Running = true; + BLState->BrightnessPercent = 1; + MessageQueue_Init(&BLState->IncomingMsgQueue, &State->Permanent); + MessageQueue_Init(&BLState->OutgoingMsgQueue, &State->Permanent); + + BLState->MicListenJobData.Running = &BLState->Running; + BLState->MicListenJobData.SocketManager = Context.SocketManager; + BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue; + BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; + + BLState->PatternSpeed = GlobalAnimSpeed; + #if 1 - BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData, Context.ThreadContext); + BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData, Context.ThreadContext); #endif + + assembly* Flower0 = LoadAssembly(Flower0AssemblyPath, State, Context); + assembly* Flower1 = LoadAssembly(Flower1AssemblyPath, State, Context); + assembly* Flower2 = LoadAssembly(Flower2AssemblyPath, State, Context); + + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + assembly Assembly = State->Assemblies.Values[i]; + BLState->StemStrips[Assembly.AssemblyIndex] = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("stem"), &State->Permanent); + } + + BLState->AssemblyNameToClearCoreMapCount = 3; + BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent, + u64, + BLState->AssemblyNameToClearCoreMapCount); + BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU32(StringExpand(Flower2->Name)); + BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU32(StringExpand(Flower1->Name)); + BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower0->Name)); + + gs_file_handler FileHandler = Context.ThreadContext.FileHandler; + gs_file ColorPhraseCSVFile = ReadEntireFile(FileHandler, PhraseMapCSVPath); + if (ColorPhraseCSVFile.Memory != 0) + { + gs_const_string ColorPhraseMapStr = DataToString(ColorPhraseCSVFile.Data); + gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, + { PhraseMapCSVSeparator }, + State->Transient); - assembly* Flower0 = LoadAssembly(Flower0AssemblyPath, State, Context); - assembly* Flower1 = LoadAssembly(Flower1AssemblyPath, State, Context); - assembly* Flower2 = LoadAssembly(Flower2AssemblyPath, State, Context); - - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) - { - assembly Assembly = State->Assemblies.Values[i]; - BLState->StemStrips[Assembly.AssemblyIndex] = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("stem"), &State->Permanent); - } - - BLState->AssemblyNameToClearCoreMapCount = 3; - BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent, - u64, - BLState->AssemblyNameToClearCoreMapCount); - BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU32(StringExpand(Flower2->Name)); - BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU32(StringExpand(Flower1->Name)); - BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower0->Name)); - - gs_file_handler FileHandler = Context.ThreadContext.FileHandler; - gs_file ColorPhraseCSVFile = ReadEntireFile(FileHandler, PhraseMapCSVPath); - if (ColorPhraseCSVFile.Memory != 0) - { - gs_const_string ColorPhraseMapStr = DataToString(ColorPhraseCSVFile.Data); - gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, - { PhraseMapCSVSeparator }, - State->Transient); - - BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, - &State->Permanent); - } - + BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, + &State->Permanent); + } + #if 0 - animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - ConstString("data/demo_patterns.foldanim")); - State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; + animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/demo_patterns.foldanim")); + State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; #else - - BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context); - BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); - AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); - - BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); - - BLState->AwakenHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - ConstString("data/blumen_animations/awaken.foldanim")); - BLState->OffAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - ConstString("data/blumen_animations/off_anim.foldanim")); - + + BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context); + BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); + AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); + + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + + BLState->AwakenHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/blumen_animations/awaken.foldanim")); + BLState->OffAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/blumen_animations/off_anim.foldanim")); + #endif - State->AnimationSystem.TimelineShouldAdvance = true; - - BLState->StandardPatternHues.Granularity = 1; - BLState->StandardPatternHues.Speed = 1; - BLState->StandardPatternHues.AddIn = AddIn_Rotary; - BLState->StandardPatternHues.Pattern = HuePattern_Wavy; - - BLState->DebugHue.Hue0.HSV = v4{0, 1, 1, 1}; - BLState->DebugHue.Hue1.HSV = v4{0, 1, 1, 1}; - BLState->DebugHue.Hue2.HSV = v4{0, 1, 1, 1}; - - BlumenLumen_AppendBootupLog(State, BLState, Context); - return Result; + State->AnimationSystem.TimelineShouldAdvance = true; + + BLState->StandardPatternHues.Granularity = 1; + BLState->StandardPatternHues.Speed = 1; + BLState->StandardPatternHues.AddIn = AddIn_Rotary; + BLState->StandardPatternHues.Pattern = HuePattern_Wavy; + + BLState->DebugHue.Hue0.HSV = v4{0, 1, 1, 1}; + BLState->DebugHue.Hue1.HSV = v4{0, 1, 1, 1}; + BLState->DebugHue.Hue2.HSV = v4{0, 1, 1, 1}; + + BlumenLumen_AppendBootupLog(State, BLState, Context); + return Result; } internal void BlumenLumen_UpdateMotorState(blumen_lumen_state* BLState, motor_status_packet Motor, context Context) { - DEBUG_ReceivedMotorPositions(BLState, Motor, Context.ThreadContext); - - motor_packet LastPos = BLState->LastKnownMotorState; - motor_packet CurrPos = Motor.Pos; - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + DEBUG_ReceivedMotorPositions(BLState, Motor, Context.ThreadContext); + + motor_packet LastPos = BLState->LastKnownMotorState; + motor_packet CurrPos = Motor.Pos; + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i]) { - if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i]) - { - BLState->LastTimeMotorStateChanged[i] = Context.SystemTime_Current.NanosSinceEpoch; - } + BLState->LastTimeMotorStateChanged[i] = Context.SystemTime_Current.NanosSinceEpoch; } - - BLState->LastKnownMotorState = Motor.Pos; - BLState->ShouldUpdateLog = true; + } + + BLState->LastKnownMotorState = Motor.Pos; + BLState->ShouldUpdateLog = true; } internal void BlumenLumen_ApplyNextHotHue(blumen_lumen_state* BLState, context Context, gs_string* DebugStr, app_state* State) { - // if we are in standard color mode, shift all flowers to the new color - // otherwise, only shift the next flower in the sequence to the new color - phrase_hue NewHue = BLState->NextHotHue; - Log_Message(GlobalLogBuffer, "Switching To: %S\n", NewHue.Phrase); - - - if (BLState->PatternMode == BlumenPattern_Standard || - NewHue.OverrideAll) - { - BlumenLumen_SetNextHue(BLState, 0, NewHue); - BlumenLumen_SetNextHue(BLState, 1, NewHue); - BlumenLumen_SetNextHue(BLState, 2, NewHue); - } - else - { - u32 AssemblyIdx = BLState->LastAssemblyColorSet; - BlumenLumen_SetNextHue(BLState, AssemblyIdx, NewHue); - BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; - } - - BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); - BLState->TimeLastSetToVoiceMode = Context.SystemTime_Current; - BLState->LastHuePhrase = NewHue; - BLState->ShouldUpdateLog = true; - BLState->InPhraseReceptionMode = false; + // if we are in standard color mode, shift all flowers to the new color + // otherwise, only shift the next flower in the sequence to the new color + phrase_hue NewHue = BLState->NextHotHue; + Log_Message(GlobalLogBuffer, "Switching To: %S\n", NewHue.Phrase); + + + if (BLState->PatternMode == BlumenPattern_Standard || + NewHue.OverrideAll) + { + BlumenLumen_SetNextHue(BLState, 0, NewHue); + BlumenLumen_SetNextHue(BLState, 1, NewHue); + BlumenLumen_SetNextHue(BLState, 2, NewHue); + } + else + { + u32 AssemblyIdx = BLState->LastAssemblyColorSet; + BlumenLumen_SetNextHue(BLState, AssemblyIdx, NewHue); + BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; + } + + BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + BLState->TimeLastSetToVoiceMode = Context.SystemTime_Current; + BLState->LastHuePhrase = NewHue; + BLState->ShouldUpdateLog = true; + BLState->InPhraseReceptionMode = false; } internal void BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; - BLState->ShouldUpdateLog = false; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + BLState->ShouldUpdateLog = false; + + gs_string DebugStr = PushString(State->Transient, 256); + + while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) + { + gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); + if (PacketData.Memory == 0) continue; - gs_string DebugStr = PushString(State->Transient, 256); - - while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) - { - gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); - if (PacketData.Memory == 0) continue; + blumen_packet Packet = *(blumen_packet*)PacketData.Memory; + switch (Packet.Type) { + case PacketType_PatternCommand: + { + microphone_packet Mic = Packet.MicPacket; + u64 NameHash = HashDJB2ToU32(Mic.AnimationFileName); + u32 NameLen = CStringLength(Mic.AnimationFileName); - blumen_packet Packet = *(blumen_packet*)PacketData.Memory; - switch (Packet.Type) { - case PacketType_PatternCommand: - { - microphone_packet Mic = Packet.MicPacket; - u64 NameHash = HashDJB2ToU32(Mic.AnimationFileName); - u32 NameLen = CStringLength(Mic.AnimationFileName); - - phrase_hue NewHue = PhraseHueMap_Find(BLState->PhraseHueMap, NameHash); - if (NewHue.Phrase.Length > 0) - { - bool IsLonger = (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length); - bool IsntInPhraseReceptionMode = !BLState->InPhraseReceptionMode; - if (IsLonger || IsntInPhraseReceptionMode) - { - Log_Message(GlobalLogBuffer, "Queueing: %S\n", NewHue.Phrase); - - BLState->NextHotHue = NewHue; - if (SecondsElapsed(BLState->TimePhraseReceptionBegan, - Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime) - { - BLState->TimePhraseReceptionBegan = Context->SystemTime_Current; - BLState->InPhraseReceptionMode = true; - } - BLState->TimeLastPhraseReceived = Context->SystemTime_Current; - } - } - }break; - - case PacketType_MotorState: - { - motor_status_packet Motor = Packet.MotorStatusPacket; - - // NOTE(pjs): Python sends multi-byte integers in little endian - // order. Have to unpack - u8* T = (u8*)&Motor.Temperature; - Motor.Temperature = (T[0] << 8 | - T[1] << 0); - - BlumenLumen_UpdateMotorState(BLState, Motor, *Context); - }break; - - case PacketType_Temperature: - { - temp_packet Temp = Packet.TempPacket; - BLState->LastTemperatureReceived = Temp.Temperature; - DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); - BLState->ShouldUpdateLog = true; - }break; - - InvalidDefaultCase; - } - } - - if (BLState->InPhraseReceptionMode) - { - r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current); - if (SecondsSincePhraseBegan > PhrasePriorityMessageGroupingTime) + phrase_hue NewHue = PhraseHueMap_Find(BLState->PhraseHueMap, NameHash); + if (NewHue.Phrase.Length > 0) { - BlumenLumen_ApplyNextHotHue(BLState, *Context, &DebugStr, State); - } - } - - BlumenLumen_AdvanceHueFade(BLState, *Context); - - // Update next frames Hues - if (!BLState->DebugOverrideHue) - { - r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); - AnimTime = (r32)Context->TotalTime; - r32 BaseTime = AnimTime * BLState->PatternSpeed; - - r32 ColorSpeed = 1; //.001; - r32 ColorOscSpeed = .05 * ColorSpeed; - r32 ColorRelOscSpeed = 1 * ColorSpeed;; - r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; - r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); - - r32 H0 = ModR32(ColorOscillation * 360, 360); - r32 H1 = ModR32(BaseTime + ColorRelationship, 360); - // TODO(PS): use our new HSV lerp - r32 H2 = LerpR32(.3f, H0, H1); - - BLState->StandardPatternHues.Hue0.HSV = v4{ H0, 1, 1, 1 }; - BLState->StandardPatternHues.Hue1.HSV = v4{ H1, 1, 1, 1 }; - BLState->StandardPatternHues.Hue2.HSV = v4{ H2, 1, 1, 1 }; - - // Transition back to standard mode after some time - if (BLState->PatternMode == BlumenPattern_VoiceCommand) - { - u64 LastChangeClock = BLState->TimeLastSetToVoiceMode.NanosSinceEpoch; - u64 NowClocks = Context->SystemTime_Current.NanosSinceEpoch; - s64 NanosSinceChange = NowClocks - LastChangeClock; - r64 SecondsSinceChange = (r64)NanosSinceChange * NanosToSeconds; + bool IsLonger = (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length); + bool IsntInPhraseReceptionMode = !BLState->InPhraseReceptionMode; + if (IsLonger || IsntInPhraseReceptionMode) + { + Log_Message(GlobalLogBuffer, "Queueing: %S\n", NewHue.Phrase); - if (SecondsSinceChange > VoiceCommandSustainDuration) + BLState->NextHotHue = NewHue; + if (SecondsElapsed(BLState->TimePhraseReceptionBegan, + Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime) { - BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); - BLState->ShouldUpdateLog = true; + BLState->TimePhraseReceptionBegan = Context->SystemTime_Current; + BLState->InPhraseReceptionMode = true; } + BLState->TimeLastPhraseReceived = Context->SystemTime_Current; + } } + }break; + + case PacketType_MotorState: + { + motor_status_packet Motor = Packet.MotorStatusPacket; + // NOTE(pjs): Python sends multi-byte integers in little endian + // order. Have to unpack + u8* T = (u8*)&Motor.Temperature; + Motor.Temperature = (T[0] << 8 | + T[1] << 0); + + BlumenLumen_UpdateMotorState(BLState, Motor, *Context); + }break; + + case PacketType_Temperature: + { + temp_packet Temp = Packet.TempPacket; + BLState->LastTemperatureReceived = Temp.Temperature; + DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); + BLState->ShouldUpdateLog = true; + }break; + + InvalidDefaultCase; } - else + } + + if (BLState->InPhraseReceptionMode) + { + r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current); + if (SecondsSincePhraseBegan > PhrasePriorityMessageGroupingTime) { - BLState->StandardPatternHues = BLState->DebugHue; - AnimationSystem_FadeToPlaylist(&State->AnimationSystem, BLState->ModeAnimations[BlumenPattern_VoiceCommand]); - + BlumenLumen_ApplyNextHotHue(BLState, *Context, &DebugStr, State); + } + } + + BlumenLumen_AdvanceHueFade(BLState, *Context); + + // Update next frames Hues + if (!BLState->DebugOverrideHue) + { + r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); + AnimTime = (r32)Context->TotalTime; + r32 BaseTime = AnimTime * BLState->PatternSpeed; + + r32 ColorSpeed = 1; //.001; + r32 ColorOscSpeed = .05 * ColorSpeed; + r32 ColorRelOscSpeed = 1 * ColorSpeed;; + r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; + r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); + + r32 H0 = ModR32(ColorOscillation * 360, 360); + r32 H1 = ModR32(BaseTime + ColorRelationship, 360); + // TODO(PS): use our new HSV lerp + r32 H2 = LerpR32(.3f, H0, H1); + + BLState->StandardPatternHues.Hue0.HSV = v4{ H0, 1, 1, 1 }; + BLState->StandardPatternHues.Hue1.HSV = v4{ H1, 1, 1, 1 }; + BLState->StandardPatternHues.Hue2.HSV = v4{ H2, 1, 1, 1 }; + + // Transition back to standard mode after some time + if (BLState->PatternMode == BlumenPattern_VoiceCommand) + { + u64 LastChangeClock = BLState->TimeLastSetToVoiceMode.NanosSinceEpoch; + u64 NowClocks = Context->SystemTime_Current.NanosSinceEpoch; + s64 NanosSinceChange = NowClocks - LastChangeClock; + r64 SecondsSinceChange = (r64)NanosSinceChange * NanosToSeconds; + + if (SecondsSinceChange > VoiceCommandSustainDuration) + { + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + BLState->ShouldUpdateLog = true; + } } - // Open / Close the Motor - if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue) && - !BLState->IgnoreTimeOfDay_MotorState) + } + else + { + BLState->StandardPatternHues = BLState->DebugHue; + AnimationSystem_FadeToPlaylist(&State->AnimationSystem, BLState->ModeAnimations[BlumenPattern_VoiceCommand]); + + } + + // Open / Close the Motor + if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue) && + !BLState->IgnoreTimeOfDay_MotorState) + { + bool SendMotorCommand = false; + + u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; + r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; + bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; + + bl_motor_state_value NewMotorState = MotorState_Closed; + bool SendOpen = false; + for (u32 i = 0; i < MotorOpenTimesCount; i++) { - bool SendMotorCommand = false; - - u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; - r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; - bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; - - bl_motor_state_value NewMotorState = MotorState_Closed; - bool SendOpen = false; - for (u32 i = 0; i < MotorOpenTimesCount; i++) - { - time_range Range = MotorOpenTimes[i]; - bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); - if (CurrTimeInRange) { - NewMotorState = MotorState_Open; - } - } - - if (NewMotorState != BLState->LastSendState) - { - ShouldSendCurrentState = true; - } - - if (ShouldSendCurrentState) - { - BLState->LastSendTime = Context->SystemTime_Current; - BLState->LastSendState = NewMotorState; - - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket.FlowerPositions[0] = NewMotorState; - Packet.MotorPacket.FlowerPositions[1] = NewMotorState; - Packet.MotorPacket.FlowerPositions[2] = NewMotorState; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - - DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); - } + time_range Range = MotorOpenTimes[i]; + bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); + if (CurrTimeInRange) { + NewMotorState = MotorState_Open; + } } - // When a motor state changes to being open, wait to turn Upper Leds on - // in order to hide the fact that they are turning off - motor_packet CurrMotorPos = BLState->LastKnownMotorState; - u64 NowNanos = Context->SystemTime_Current.NanosSinceEpoch; - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + if (NewMotorState != BLState->LastSendState) { - // have to map from "assembly load order" to - // the order that the clear core is referencing the - // motors by - assembly Assembly = State->Assemblies.Values[i]; - u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState); - u8 MotorPos = CurrMotorPos.FlowerPositions[AssemblyCCIndex]; - - if ((MotorPos == MotorState_Open || MotorPos == MotorState_MostlyOpen) && - !BLState->ShouldDimUpperLeds[i]) - { - u64 ChangedNanos = BLState->LastTimeMotorStateChanged[i]; - u64 NanosSinceChanged = NowNanos - ChangedNanos; - r64 SecondsSinceChanged = (r64)NanosSinceChanged * NanosToSeconds; - if (SecondsSinceChanged > TurnUpperLedsOffAfterMotorCloseCommandDelay) - { - BLState->ShouldDimUpperLeds[i] = true; - } - else - { - BLState->ShouldDimUpperLeds[i] = false; - } - } - else if (MotorPos == MotorState_Closed || - MotorPos == MotorState_HalfOpen) - { - BLState->ShouldDimUpperLeds[i] = false; - } + ShouldSendCurrentState = true; } - // NOTE(PS): If the flowers are mostly open or full open - // we mask off the top leds to prevent them from overheating - // while telescoped inside the flower - for (u32 a = 0; a < BL_FLOWER_COUNT; a++) + if (ShouldSendCurrentState) { - assembly Assembly = State->Assemblies.Values[a]; - if (!BLState->ShouldDimUpperLeds[a]) continue; - - led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; - led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient); - for (u32 s = 0; s < TopStrips.Count; s++) - { - u32 SIndex = TopStrips.StripIndices[s]; - v2_strip Strip = Assembly.Strips[SIndex]; - for (u32 l = 0; l < Strip.LedCount; l++) - { - u32 LIndex = Strip.LedLUT[l]; - Buffer.Colors[LIndex] = {0}; - } - } + BLState->LastSendTime = Context->SystemTime_Current; + BLState->LastSendState = NewMotorState; + + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket.FlowerPositions[0] = NewMotorState; + Packet.MotorPacket.FlowerPositions[1] = NewMotorState; + Packet.MotorPacket.FlowerPositions[2] = NewMotorState; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + + DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); + } + } + + // When a motor state changes to being open, wait to turn Upper Leds on + // in order to hide the fact that they are turning off + motor_packet CurrMotorPos = BLState->LastKnownMotorState; + u64 NowNanos = Context->SystemTime_Current.NanosSinceEpoch; + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + // have to map from "assembly load order" to + // the order that the clear core is referencing the + // motors by + assembly Assembly = State->Assemblies.Values[i]; + u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState); + u8 MotorPos = CurrMotorPos.FlowerPositions[AssemblyCCIndex]; + + if ((MotorPos == MotorState_Open || MotorPos == MotorState_MostlyOpen) && + !BLState->ShouldDimUpperLeds[i]) + { + u64 ChangedNanos = BLState->LastTimeMotorStateChanged[i]; + u64 NanosSinceChanged = NowNanos - ChangedNanos; + r64 SecondsSinceChanged = (r64)NanosSinceChanged * NanosToSeconds; + if (SecondsSinceChanged > TurnUpperLedsOffAfterMotorCloseCommandDelay) + { + BLState->ShouldDimUpperLeds[i] = true; + } + else + { + BLState->ShouldDimUpperLeds[i] = false; + } + } + else if (MotorPos == MotorState_Closed || + MotorPos == MotorState_HalfOpen) + { + BLState->ShouldDimUpperLeds[i] = false; + } + } + + // NOTE(PS): If the flowers are mostly open or full open + // we mask off the top leds to prevent them from overheating + // while telescoped inside the flower + for (u32 a = 0; a < BL_FLOWER_COUNT; a++) + { + assembly Assembly = State->Assemblies.Values[a]; + if (!BLState->ShouldDimUpperLeds[a]) continue; + + led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; + led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient); + for (u32 s = 0; s < TopStrips.Count; s++) + { + u32 SIndex = TopStrips.StripIndices[s]; + v2_strip Strip = Assembly.Strips[SIndex]; + for (u32 l = 0; l < Strip.LedCount; l++) + { + u32 LIndex = Strip.LedLUT[l]; + Buffer.Colors[LIndex] = {0}; + } + } + } + + if (!BLState->IgnoreTimeOfDay_LedDimming) + { + bool TimelineShouldAdvance = false; + r32 OverrideBrightness = 0.0f; + + time_range RangeIn = {}; + if (SystemTimeIsInTimeRangeList(Context->SystemTime_Current, + LedOnTimes, + LedOnTimesCount, + &RangeIn)) + { + + if (Blumen_TempShouldDimLeds(BLState)) + { + OverrideBrightness = HighTemperatureBrightnessPercent; + } + else + { + OverrideBrightness = FullBrightnessPercent; + } + TimelineShouldAdvance = true; } - if (!BLState->IgnoreTimeOfDay_LedDimming) + State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; + BLState->BrightnessPercent = OverrideBrightness; + } + + // Dim the leds based on temp data + if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) + { + for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) { - bool TimelineShouldAdvance = false; - r32 OverrideBrightness = 0.0f; - - time_range RangeIn = {}; - if (SystemTimeIsInTimeRangeList(Context->SystemTime_Current, - LedOnTimes, - LedOnTimesCount, - &RangeIn)) - { - - if (Blumen_TempShouldDimLeds(BLState)) - { - OverrideBrightness = HighTemperatureBrightnessPercent; - } - else - { - OverrideBrightness = FullBrightnessPercent; - } - TimelineShouldAdvance = true; - } - - State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; - BLState->BrightnessPercent = OverrideBrightness; + led_buffer Buffer = State->LedSystem.Buffers[i]; + for (u32 j = 0; j < Buffer.LedCount; j++) + { + pixel* Color = Buffer.Colors + j; + Color->R = Color->R * BLState->BrightnessPercent; + Color->G = Color->G * BLState->BrightnessPercent; + Color->B = Color->B * BLState->BrightnessPercent; + } } - - // Dim the leds based on temp data - if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) + } + + // Send Status Packet + { + system_time LastSendTime = BLState->LastStatusUpdateTime; + r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)LastSendTime.NanosSinceEpoch); + r64 SecondsSinceLastSend = NanosSinceLastSend * NanosToSeconds; + if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) { - for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) - { - led_buffer Buffer = State->LedSystem.Buffers[i]; - for (u32 j = 0; j < Buffer.LedCount; j++) - { - pixel* Color = Buffer.Colors + j; - Color->R = Color->R * BLState->BrightnessPercent; - Color->G = Color->G * BLState->BrightnessPercent; - Color->B = Color->B * BLState->BrightnessPercent; - } - } + BLState->LastStatusUpdateTime = Context->SystemTime_Current; + Log_Message(GlobalLogBuffer, "Attempting to Send Lumenarium Status\n"); + + blumen_packet Packet = {}; + Packet.Type = PacketType_LumenariumStatus; + Packet.StatusPacket.NextMotorEventType = 0; + Packet.StatusPacket.NextEventTime = 0; + + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + if (ActiveAnim) + { + CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName, + Min(ActiveAnim->Name.Length, 32)); + Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0; + } + + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + + // there's no new information here, but by updating the log here, + // we're updating it at some infrequent but regular period that isnt + // every single frame + BLState->ShouldUpdateLog = true; } - - // Send Status Packet - { - system_time LastSendTime = BLState->LastStatusUpdateTime; - r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)LastSendTime.NanosSinceEpoch); - r64 SecondsSinceLastSend = NanosSinceLastSend * NanosToSeconds; - if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) - { - BLState->LastStatusUpdateTime = Context->SystemTime_Current; - Log_Message(GlobalLogBuffer, "Attempting to Send Lumenarium Status\n"); - - blumen_packet Packet = {}; - Packet.Type = PacketType_LumenariumStatus; - Packet.StatusPacket.NextMotorEventType = 0; - Packet.StatusPacket.NextEventTime = 0; - - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - if (ActiveAnim) - { - CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName, - Min(ActiveAnim->Name.Length, 32)); - Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0; - } - - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - - // there's no new information here, but by updating the log here, - // we're updating it at some infrequent but regular period that isnt - // every single frame - BLState->ShouldUpdateLog = true; - } - } - - BlumenLumen_UpdateLog(State, BLState, *Context); + } + + BlumenLumen_UpdateLog(State, BLState, *Context); } US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; - ui_interface* I = &State->Interface; - - ui_BeginRow(I, BlumenDebug_Count); - for (u32 i = 0; i < BlumenDebug_Count; i++) + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + ui_interface* I = &State->Interface; + + ui_BeginRow(I, BlumenDebug_Count); + for (u32 i = 0; i < BlumenDebug_Count; i++) + { + if (ui_Button(I, MakeString(BlDebugUiModeStrings[i]))) { - if (ui_Button(I, MakeString(BlDebugUiModeStrings[i]))) + BLState->DebugMode = (bl_debug_ui_mode)i; + } + } + ui_EndRow(I); + + switch (BLState->DebugMode) + { + case BlumenDebug_Motors: + { + motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; + + BLState->IgnoreTimeOfDay_MotorState = ui_ToggleText(I, MakeString("Motors Ignore Time Limit"), BLState->IgnoreTimeOfDay_MotorState); + + for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) + { + gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); + ui_BeginRow(I, 5); { - BLState->DebugMode = (bl_debug_ui_mode)i; + ui_Label(I, Label); + + bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; + if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; + } + bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; + if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; + } + bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; + if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; + } + bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; + if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; + } } - } - ui_EndRow(I); + ui_EndRow(I); + } + BLState->DEBUG_PendingMotorPacket = PendingPacket; + + if (ui_Button(I, MakeString("Send Motor Packet"))) + { + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); + + } + + motor_packet MotorPos = BLState->LastKnownMotorState; + ui_Label(I, MakeString("Current Motor Positions")); + { + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + ui_BeginRow(I, 2); + gs_string MotorStr = PushStringF(State->Transient, 32, + "Motor %d", + i); + ui_Label(I, MotorStr); + + gs_string StateStr = {}; + switch (MotorPos.FlowerPositions[i]) + { + case MotorState_Closed: { + StateStr = MakeString("Closed"); + } break; + case MotorState_HalfOpen: { + StateStr = MakeString("Half Open"); + } break; + case MotorState_MostlyOpen: { + StateStr = MakeString("Mostly Open"); + } break; + case MotorState_Open: { + StateStr = MakeString("Open"); + } break; + + default: + { + StateStr = MakeString("Invalid Value"); + } break; + } + + ui_Label(I, StateStr); + ui_EndRow(I); + } + } + + ui_Label(I, MakeString("Set Internal Motor State:")); + if (ui_Button(I, MakeString("Closed"))) + { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Closed; + Motor.Pos.FlowerPositions[1] = MotorState_Closed; + Motor.Pos.FlowerPositions[2] = MotorState_Closed; + Motor.Temperature = 16; + + BlumenLumen_UpdateMotorState(BLState, Motor, Context); + } + if (ui_Button(I, MakeString("Open"))) + { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Open; + Motor.Pos.FlowerPositions[1] = MotorState_Open; + Motor.Pos.FlowerPositions[2] = MotorState_Open; + Motor.Temperature = 16; + + BlumenLumen_UpdateMotorState(BLState, Motor, Context); + } + } break; - switch (BLState->DebugMode) + case BlumenDebug_Leds: { - case BlumenDebug_Motors: + BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); + + BLState->IgnoreTimeOfDay_LedDimming = ui_ToggleText(I, MakeString("Leds Ignore Time Limit"), BLState->IgnoreTimeOfDay_LedDimming); + + if (ui_BeginLabeledDropdown(I, MakeString("Phrase"), MakeString(BLState->PendingPhrase.Phrase))) + { + u32 ListCount = BLState->PhraseHueMap.Count; + ui_BeginList(I, MakeString("Phrase List"), 5, ListCount); + for (u32 i = 0; i < ListCount; i++) { - motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; - - BLState->IgnoreTimeOfDay_MotorState = ui_ToggleText(I, MakeString("Motors Ignore Time Limit"), BLState->IgnoreTimeOfDay_MotorState); - - for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) - { - gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); - ui_BeginRow(I, 5); - { - ui_Label(I, Label); - - bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; - if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; - } - bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; - if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; - } - bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; - if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; - } - bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; - if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; - } - } - ui_EndRow(I); - } - BLState->DEBUG_PendingMotorPacket = PendingPacket; - - if (ui_Button(I, MakeString("Send Motor Packet"))) - { - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); - - } - - motor_packet MotorPos = BLState->LastKnownMotorState; - ui_Label(I, MakeString("Current Motor Positions")); - { - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) - { - ui_BeginRow(I, 2); - gs_string MotorStr = PushStringF(State->Transient, 32, - "Motor %d", - i); - ui_Label(I, MotorStr); - - gs_string StateStr = {}; - switch (MotorPos.FlowerPositions[i]) - { - case MotorState_Closed: { - StateStr = MakeString("Closed"); - } break; - case MotorState_HalfOpen: { - StateStr = MakeString("Half Open"); - } break; - case MotorState_MostlyOpen: { - StateStr = MakeString("Mostly Open"); - } break; - case MotorState_Open: { - StateStr = MakeString("Open"); - } break; - - default: - { - StateStr = MakeString("Invalid Value"); - } break; - } - - ui_Label(I, StateStr); - ui_EndRow(I); - } - } - - ui_Label(I, MakeString("Set Internal Motor State:")); - if (ui_Button(I, MakeString("Closed"))) - { - motor_status_packet Motor = {}; - Motor.Pos.FlowerPositions[0] = MotorState_Closed; - Motor.Pos.FlowerPositions[1] = MotorState_Closed; - Motor.Pos.FlowerPositions[2] = MotorState_Closed; - Motor.Temperature = 16; - - BlumenLumen_UpdateMotorState(BLState, Motor, Context); - } - if (ui_Button(I, MakeString("Open"))) - { - motor_status_packet Motor = {}; - Motor.Pos.FlowerPositions[0] = MotorState_Open; - Motor.Pos.FlowerPositions[1] = MotorState_Open; - Motor.Pos.FlowerPositions[2] = MotorState_Open; - Motor.Temperature = 16; - - BlumenLumen_UpdateMotorState(BLState, Motor, Context); - } - } break; + gs_string Str = MakeString(BLState->PhraseHueMap.Phrases[i]); + if (ui_Button(I, Str)) + { + BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i); + BLState->DebugHue = BLState->PendingPhrase; + } + } + ui_EndList(I); + } + ui_EndLabeledDropdown(I); + if (ui_Button(I, MakeString("Say Phrase"))) + { + gs_string DebugStr = PushString(State->Transient, 256); + BLState->NextHotHue = BLState->PendingPhrase; + BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State); + } + + ui_Label(I, MakeString("Phrase Constructor")); + BLState->DebugOverrideHue = ui_ToggleText(I, MakeString("Override Hue"), BLState->DebugOverrideHue); + if (BLState->DebugOverrideHue) + { + phrase_hue PHue = BLState->DebugHue; + PHue.Hue0.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue0"), (r32)PHue.Hue0.HSV.x, 0, 360); + PHue.Hue1.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue1"), (r32)PHue.Hue1.HSV.x, 0, 360); + PHue.Hue2.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue2"), (r32)PHue.Hue2.HSV.x, 0, 360); + PHue.Granularity = (u32)ui_LabeledRangeSlider(I, MakeString("Granularity"), (r32)PHue.Granularity, 0, 5); + PHue.Speed = ui_LabeledRangeSlider(I, MakeString("Speed"), PHue.Speed, 0, 4); - case BlumenDebug_Leds: + gs_string PatternOptions[HuePattern_Count] = {}; + PatternOptions[HuePattern_Patchy] = MakeString("patchy"); + PatternOptions[HuePattern_Wavy] = MakeString("wavy"); + + gs_string CPattern = PatternOptions[PHue.Pattern]; + if (ui_BeginLabeledDropdown(I, MakeString("Pattern"), CPattern)) { - BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); - - BLState->IgnoreTimeOfDay_LedDimming = ui_ToggleText(I, MakeString("Leds Ignore Time Limit"), BLState->IgnoreTimeOfDay_LedDimming); - - if (ui_BeginLabeledDropdown(I, MakeString("Phrase"), MakeString(BLState->PendingPhrase.Phrase))) + for (u32 i = 0; i < HuePattern_Count; i++) + { + if (ui_Button(I, PatternOptions[i])) { - u32 ListCount = BLState->PhraseHueMap.Count; - ui_BeginList(I, MakeString("Phrase List"), 5, ListCount); - for (u32 i = 0; i < ListCount; i++) - { - gs_string Str = MakeString(BLState->PhraseHueMap.Phrases[i]); - if (ui_Button(I, Str)) - { - BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i); - BLState->DebugHue = BLState->PendingPhrase; - } - } - ui_EndList(I); + PHue.Pattern = i; } - ui_EndLabeledDropdown(I); - if (ui_Button(I, MakeString("Say Phrase"))) - { - gs_string DebugStr = PushString(State->Transient, 256); - BLState->NextHotHue = BLState->PendingPhrase; - BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State); - } - - ui_Label(I, MakeString("Phrase Constructor")); - BLState->DebugOverrideHue = ui_ToggleText(I, MakeString("Override Hue"), BLState->DebugOverrideHue); - if (BLState->DebugOverrideHue) - { - phrase_hue PHue = BLState->DebugHue; - PHue.Hue0.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue0"), (r32)PHue.Hue0.HSV.x, 0, 360); - PHue.Hue1.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue1"), (r32)PHue.Hue1.HSV.x, 0, 360); - PHue.Hue2.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue2"), (r32)PHue.Hue2.HSV.x, 0, 360); - PHue.Granularity = (u32)ui_LabeledRangeSlider(I, MakeString("Granularity"), (r32)PHue.Granularity, 0, 5); - PHue.Speed = ui_LabeledRangeSlider(I, MakeString("Speed"), PHue.Speed, 0, 4); - - gs_string PatternOptions[HuePattern_Count] = {}; - PatternOptions[HuePattern_Patchy] = MakeString("patchy"); - PatternOptions[HuePattern_Wavy] = MakeString("wavy"); - - gs_string CPattern = PatternOptions[PHue.Pattern]; - if (ui_BeginLabeledDropdown(I, MakeString("Pattern"), CPattern)) - { - for (u32 i = 0; i < HuePattern_Count; i++) - { - if (ui_Button(I, PatternOptions[i])) - { - PHue.Pattern = i; - } - } - } - ui_EndLabeledDropdown(I); - - - gs_string AddInOptions[AddIn_Count] = {}; - AddInOptions[AddIn_None] = MakeString("NA"); - AddInOptions[AddIn_Waves] = MakeString("waves"); - AddInOptions[AddIn_Rotary] = MakeString("rotary"); - - gs_string CAddIn = AddInOptions[PHue.AddIn]; - if (ui_BeginLabeledDropdown(I, MakeString("Add In"), CAddIn)) - { - for (u32 i = 0; i < AddIn_Count; i++) - { - if (ui_Button(I, AddInOptions[i])) - { - PHue.AddIn = i; - } - } - } - ui_EndLabeledDropdown(I); - BLState->DebugHue = PHue; - } - - - InterfaceAssert(I->PerFrameMemory); - }break; + } + } + ui_EndLabeledDropdown(I); - case BlumenDebug_Awaken: + + gs_string AddInOptions[AddIn_Count] = {}; + AddInOptions[AddIn_None] = MakeString("NA"); + AddInOptions[AddIn_Waves] = MakeString("waves"); + AddInOptions[AddIn_Rotary] = MakeString("rotary"); + + gs_string CAddIn = AddInOptions[PHue.AddIn]; + if (ui_BeginLabeledDropdown(I, MakeString("Add In"), CAddIn)) { - ui_Label(I, MakeString("Step 1:")); - ui_Label(I, MakeString("Leds off, flowers closed")); - if (ui_Button(I, MakeString("Prepare"))) + for (u32 i = 0; i < AddIn_Count; i++) + { + if (ui_Button(I, AddInOptions[i])) { - // motors closed - blumen_packet M = {}; - M.Type = PacketType_MotorState; - M.MotorPacket.FlowerPositions[0] = MotorState_Closed; - M.MotorPacket.FlowerPositions[1] = MotorState_Closed; - M.MotorPacket.FlowerPositions[2] = MotorState_Closed; - gs_data D = StructToData(&M, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, D); - - // animation - State->AnimationSystem.RepeatMode = AnimationRepeat_Single; - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, - BLState->OffAnimHandle, - VoiceCommandFadeDuration); - - BLState->PatternMode = BlumenPattern_NoControl; - BLState->IgnoreTimeOfDay_LedDimming = true; - BLState->IgnoreTimeOfDay_MotorState = true; + PHue.AddIn = i; } - - ui_Label(I, MakeString("Step 2:")); - if (ui_Button(I, MakeString("Begin Light Show"))) - { - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, - BLState->AwakenHandle, - VoiceCommandFadeDuration); - } - - ui_Label(I, MakeString("Step 3:")); - if (ui_Button(I, MakeString("Open Flowers"))) - { - // motors closed - blumen_packet M = {}; - M.Type = PacketType_MotorState; - M.MotorPacket.FlowerPositions[0] = MotorState_Open; - M.MotorPacket.FlowerPositions[1] = MotorState_Open; - M.MotorPacket.FlowerPositions[2] = MotorState_Open; - gs_data D = StructToData(&M, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, D); - } - - ui_Label(I, MakeString("Step 4:")); - ui_Label(I, MakeString("Resets Lumenarium")); - if (ui_Button(I, MakeString("Complete"))) - { - BLState->IgnoreTimeOfDay_LedDimming = false; - BLState->IgnoreTimeOfDay_MotorState = false; - BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, - BLState); - } - }break; + } + } + ui_EndLabeledDropdown(I); + BLState->DebugHue = PHue; + } + + + InterfaceAssert(I->PerFrameMemory); + }break; + + case BlumenDebug_Awaken: + { + ui_Label(I, MakeString("Step 1:")); + ui_Label(I, MakeString("Leds off, flowers closed")); + if (ui_Button(I, MakeString("Prepare"))) + { + // motors closed + blumen_packet M = {}; + M.Type = PacketType_MotorState; + M.MotorPacket.FlowerPositions[0] = MotorState_Closed; + M.MotorPacket.FlowerPositions[1] = MotorState_Closed; + M.MotorPacket.FlowerPositions[2] = MotorState_Closed; + gs_data D = StructToData(&M, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, D); - InvalidDefaultCase; - } + // animation + State->AnimationSystem.RepeatMode = AnimationRepeat_Single; + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + BLState->OffAnimHandle, + VoiceCommandFadeDuration); + + BLState->PatternMode = BlumenPattern_NoControl; + BLState->IgnoreTimeOfDay_LedDimming = true; + BLState->IgnoreTimeOfDay_MotorState = true; + } + + ui_Label(I, MakeString("Step 2:")); + if (ui_Button(I, MakeString("Begin Light Show"))) + { + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + BLState->AwakenHandle, + VoiceCommandFadeDuration); + } + + ui_Label(I, MakeString("Step 3:")); + if (ui_Button(I, MakeString("Open Flowers"))) + { + // motors closed + blumen_packet M = {}; + M.Type = PacketType_MotorState; + M.MotorPacket.FlowerPositions[0] = MotorState_Open; + M.MotorPacket.FlowerPositions[1] = MotorState_Open; + M.MotorPacket.FlowerPositions[2] = MotorState_Open; + gs_data D = StructToData(&M, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, D); + } + + ui_Label(I, MakeString("Step 4:")); + ui_Label(I, MakeString("Resets Lumenarium")); + if (ui_Button(I, MakeString("Complete"))) + { + BLState->IgnoreTimeOfDay_LedDimming = false; + BLState->IgnoreTimeOfDay_MotorState = false; + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, + BLState); + } + }break; + + InvalidDefaultCase; + } } US_CUSTOM_CLEANUP(BlumenLumen_CustomCleanup) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; - BLState->Running = false; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + BLState->Running = false; } internal user_space_desc BlumenLumen_UserSpaceCreate() { - user_space_desc Result = {}; - Result.LoadPatterns = BlumenLumen_LoadPatterns; - Result.CustomInit = BlumenLumen_CustomInit; - Result.CustomUpdate = BlumenLumen_CustomUpdate; - Result.CustomDebugUI = BlumenLumen_DebugUI; - Result.CustomCleanup = BlumenLumen_CustomCleanup; - return Result; + user_space_desc Result = {}; + Result.LoadPatterns = BlumenLumen_LoadPatterns; + Result.CustomInit = BlumenLumen_CustomInit; + Result.CustomUpdate = BlumenLumen_CustomUpdate; + Result.CustomDebugUI = BlumenLumen_DebugUI; + Result.CustomCleanup = BlumenLumen_CustomCleanup; + return Result; } #define BLUMEN_LUMEN_CPP diff --git a/src/app/ss_blumen_lumen/message_queue.cpp b/src/app/ss_blumen_lumen/message_queue.cpp index a132d64..4d4211d 100644 --- a/src/app/ss_blumen_lumen/message_queue.cpp +++ b/src/app/ss_blumen_lumen/message_queue.cpp @@ -2,79 +2,82 @@ internal void MessageQueue_Init(blumen_network_msg_queue* Queue, gs_memory_arena* Arena) { - for (u32 i = 0; i < BLUMEN_MESSAGE_QUEUE_COUNT; i++) - { - Queue->Buffers[i] = PushSizeToData(Arena, DEFAULT_QUEUE_ENTRY_SIZE); - } + gs_data MessageQueueData = PushSize(Arena, DEFAULT_QUEUE_ENTRY_SIZE * BLUMEN_MESSAGE_QUEUE_COUNT); + gs_memory_cursor C = MemoryCursorCreateFromData(MessageQueueData); + + for (u32 i = 0; i < BLUMEN_MESSAGE_QUEUE_COUNT; i++) + { + Queue->Buffers[i] = MemoryCursorPushSize(&C, DEFAULT_QUEUE_ENTRY_SIZE); + } } internal bool MessageQueue_CanWrite(blumen_network_msg_queue Queue) { - bool Result = ((Queue.WriteHead >= Queue.ReadHead) || - (Queue.WriteHead < Queue.ReadHead)); - return Result; + bool Result = ((Queue.WriteHead >= Queue.ReadHead) || + (Queue.WriteHead < Queue.ReadHead)); + return Result; } internal bool MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) { - Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE); - - u32 Index = Queue->WriteHead; - Assert(Index >= 0 && - Index < BLUMEN_MESSAGE_QUEUE_COUNT); - - gs_data* Dest = Queue->Buffers + Index; - CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size); - Dest->Size = Msg.Size; - - // NOTE(pjs): We increment write head at the end of writing so that - // a reader thread doesn't pull the message off before we've finished - // filling it out - Queue->WriteHead = (Queue->WriteHead + 1) % BLUMEN_MESSAGE_QUEUE_COUNT; - return true; + Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE); + + u32 Index = Queue->WriteHead; + Assert(Index >= 0 && + Index < BLUMEN_MESSAGE_QUEUE_COUNT); + + gs_data* Dest = Queue->Buffers + Index; + CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size); + Dest->Size = Msg.Size; + + // NOTE(pjs): We increment write head at the end of writing so that + // a reader thread doesn't pull the message off before we've finished + // filling it out + Queue->WriteHead = (Queue->WriteHead + 1) % BLUMEN_MESSAGE_QUEUE_COUNT; + return true; } internal bool MessageQueue_CanRead(blumen_network_msg_queue Queue) { - bool Result = (Queue.ReadHead != Queue.WriteHead); - return Result; + bool Result = (Queue.ReadHead != Queue.WriteHead); + return Result; } internal gs_data MessageQueue_Peek(blumen_network_msg_queue* Queue) { - gs_data Result = {}; - u32 ReadIndex = Queue->ReadHead; - if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT) - { - ReadIndex = 0; - } - Result = Queue->Buffers[ReadIndex]; - return Result; + gs_data Result = {}; + u32 ReadIndex = Queue->ReadHead; + if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT) + { + ReadIndex = 0; + } + Result = Queue->Buffers[ReadIndex]; + return Result; } internal gs_data MessageQueue_Read(blumen_network_msg_queue* Queue) { - gs_data Result = {}; - u32 ReadIndex = Queue->ReadHead++; - if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT) - { - Queue->ReadHead = 0; - ReadIndex = 0; - } - Result = Queue->Buffers[ReadIndex]; - return Result; + gs_data Result = {}; + u32 ReadIndex = Queue->ReadHead++; + if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT) + { + Queue->ReadHead = 0; + ReadIndex = 0; + } + Result = Queue->Buffers[ReadIndex]; + return Result; } internal void MessageQueue_Clear(blumen_network_msg_queue* Queue) { - while (MessageQueue_CanRead(*Queue)) - { - MessageQueue_Read(Queue); - } + while (MessageQueue_CanRead(*Queue)) + { + MessageQueue_Read(Queue); + } } diff --git a/src/gs_libs/gs_input.h b/src/gs_libs/gs_input.h index 4a696b3..f0c1c79 100644 --- a/src/gs_libs/gs_input.h +++ b/src/gs_libs/gs_input.h @@ -6,86 +6,86 @@ #ifndef GS_INPUT_H enum key_code { - KeyCode_Invalid, - - 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, + KeyCode_Invalid, + + 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, }; internal char CharacterFromKeyCode (key_code Code) { - char Result = 0; + char Result = 0; + + switch (Code) + { + case KeyCode_Space: { Result = ' '; }break; + case KeyCode_Tab: { Result = '\t'; }break; - switch (Code) - { - case KeyCode_Space: { Result = ' '; }break; - case KeyCode_Tab: { Result = '\t'; }break; - - // Letters - case KeyCode_a: { Result = 'a'; }break; - case KeyCode_b: { Result = 'b'; }break; - case KeyCode_c: { Result = 'c'; }break; - case KeyCode_d: { Result = 'd'; }break; - case KeyCode_e: { Result = 'e'; }break; + // Letters + case KeyCode_a: { Result = 'a'; }break; + case KeyCode_b: { Result = 'b'; }break; + case KeyCode_c: { Result = 'c'; }break; + case KeyCode_d: { Result = 'd'; }break; + case KeyCode_e: { Result = 'e'; }break; case KeyCode_f: { Result = 'f'; }break; case KeyCode_g: { Result = 'g'; }break; case KeyCode_h: { Result = 'h'; }break; - case KeyCode_i: { Result = 'i'; }break; + case KeyCode_i: { Result = 'i'; }break; case KeyCode_j: { Result = 'j'; }break; case KeyCode_k: { Result = 'k'; }break; case KeyCode_l: { Result = 'l'; }break; @@ -93,7 +93,7 @@ CharacterFromKeyCode (key_code Code) case KeyCode_n: { Result = 'n'; }break; case KeyCode_o: { Result = 'o'; }break; case KeyCode_p: { Result = 'p'; }break; - case KeyCode_q: { Result = 'q'; }break; + case KeyCode_q: { Result = 'q'; }break; case KeyCode_r: { Result = 'r'; }break; case KeyCode_s: { Result = 's'; }break; case KeyCode_t: { Result = 't'; }break; @@ -101,10 +101,10 @@ CharacterFromKeyCode (key_code Code) case KeyCode_v: { Result = 'v'; }break; case KeyCode_w: { Result = 'w'; }break; case KeyCode_x: { Result = 'x'; }break; - case KeyCode_y: { Result = 'y'; }break; + case KeyCode_y: { Result = 'y'; }break; case KeyCode_z: { Result = 'z'; }break; - - case KeyCode_A: { Result = 'A'; }break; + + case KeyCode_A: { Result = 'A'; }break; case KeyCode_B: { Result = 'B'; }break; case KeyCode_C: { Result = 'C'; }break; case KeyCode_D: { Result = 'D'; }break; @@ -112,7 +112,7 @@ CharacterFromKeyCode (key_code Code) case KeyCode_F: { Result = 'F'; }break; case KeyCode_G: { Result = 'G'; }break; case KeyCode_H: { Result = 'H'; }break; - case KeyCode_I: { Result = 'I'; }break; + case KeyCode_I: { Result = 'I'; }break; case KeyCode_J: { Result = 'J'; }break; case KeyCode_K: { Result = 'K'; }break; case KeyCode_L: { Result = 'L'; }break; @@ -120,7 +120,7 @@ CharacterFromKeyCode (key_code Code) case KeyCode_N: { Result = 'N'; }break; case KeyCode_O: { Result = 'O'; }break; case KeyCode_P: { Result = 'P'; }break; - case KeyCode_Q: { Result = 'Q'; }break; + case KeyCode_Q: { Result = 'Q'; }break; case KeyCode_R: { Result = 'R'; }break; case KeyCode_S: { Result = 'S'; }break; case KeyCode_T: { Result = 'T'; }break; @@ -128,11 +128,11 @@ CharacterFromKeyCode (key_code Code) case KeyCode_V: { Result = 'V'; }break; case KeyCode_W: { Result = 'W'; }break; case KeyCode_X: { Result = 'X'; }break; - case KeyCode_Y: { Result = 'Y'; }break; + case KeyCode_Y: { Result = 'Y'; }break; case KeyCode_Z: { Result = 'Z'; }break; - - // Numbers - case KeyCode_0: { Result = '0'; }break; + + // Numbers + case KeyCode_0: { Result = '0'; }break; case KeyCode_1: { Result = '1'; }break; case KeyCode_2: { Result = '2'; }break; case KeyCode_3: { Result = '3'; }break; @@ -140,88 +140,88 @@ CharacterFromKeyCode (key_code Code) case KeyCode_5: { Result = '5'; }break; case KeyCode_6: { Result = '6'; }break; case KeyCode_7: { Result = '7'; }break; - case KeyCode_8: { Result = '8'; }break; + case KeyCode_8: { Result = '8'; }break; case KeyCode_9: { Result = '9'; }break; - - case KeyCode_Num0: { Result = '0'; }break; + + case KeyCode_Num0: { Result = '0'; }break; case KeyCode_Num1: { Result = '1'; }break; case KeyCode_Num2: { Result = '2'; }break; case KeyCode_Num3: { Result = '3'; }break; case KeyCode_Num4: { Result = '4'; }break; case KeyCode_Num5: { Result = '5'; }break; - case KeyCode_Num6: { Result = '6'; }break; + case KeyCode_Num6: { Result = '6'; }break; case KeyCode_Num7: { Result = '7'; }break; case KeyCode_Num8: { Result = '8'; }break; case KeyCode_Num9: { Result = '9'; }break; - - // Symbols - case KeyCode_Bang: { Result = '!'; }break; + + // Symbols + case KeyCode_Bang: { Result = '!'; }break; case KeyCode_At: { Result = '@'; }break; case KeyCode_Pound: { Result = '#'; }break; case KeyCode_Dollar: { Result = '$'; }break; case KeyCode_Percent: { Result = '%'; }break; case KeyCode_Carrot: { Result = '^'; }break; - case KeyCode_Ampersand: { Result = '&'; }break; + case KeyCode_Ampersand: { Result = '&'; }break; case KeyCode_Star: { Result = '*'; }break; case KeyCode_LeftParen: { Result = '('; }break; case KeyCode_RightParen: { Result = ')'; }break; case KeyCode_Minus: { Result = '-'; }break; case KeyCode_Plus: { Result = '+'; }break; - case KeyCode_Equals: { Result = '='; }break; + case KeyCode_Equals: { Result = '='; }break; case KeyCode_Underscore: { Result = '_'; }break; case KeyCode_LeftBrace: { Result = '['; }break; case KeyCode_RightBrace: { Result = ']'; }break; case KeyCode_LeftBracket: { Result = '{'; }break; - case KeyCode_RightBracket: { Result = '}'; }break; + case KeyCode_RightBracket: { Result = '}'; }break; case KeyCode_Colon: { Result = ':'; }break; case KeyCode_SemiColon: { Result = ';'; }break; case KeyCode_SingleQuote: { Result = '\''; }break; case KeyCode_DoubleQuote: { Result = '"'; }break; - case KeyCode_ForwardSlash: { Result = '/'; }break; + case KeyCode_ForwardSlash: { Result = '/'; }break; case KeyCode_Backslash: { Result = '\\'; }break; case KeyCode_Pipe: { Result = '|'; }break; case KeyCode_Comma: { Result = ','; }break; case KeyCode_Period: { Result = '.'; }break; - case KeyCode_QuestionMark: { Result = '?'; }break; + case KeyCode_QuestionMark: { Result = '?'; }break; case KeyCode_LessThan: { Result = '<'; }break; case KeyCode_GreaterThan: { Result = '>'; }break; case KeyCode_Tilde: { Result = '~'; }break; case KeyCode_BackQuote: { Result = '`'; }break; - } - - Assert(Result != 0); - return Result; + } + + Assert(Result != 0); + return Result; } enum modifier_flags { - Modifier_Shift = 1 << 0, - Modifier_Ctrl = 1 << 1, - Modifier_Alt = 1 << 2, - Modifier_Sys = 1 << 3, // NOTE(Peter): this is the windows key + Modifier_Shift = 1 << 0, + Modifier_Ctrl = 1 << 1, + Modifier_Alt = 1 << 2, + Modifier_Sys = 1 << 3, // NOTE(Peter): this is the windows key }; #define INPUT_FRAME_STRING_LENGTH 32 struct input_frame { - b32 KeysDown[(int)KeyCode_Count]; - s32 StringInputUsed; - char StringInput[INPUT_FRAME_STRING_LENGTH]; - s32 MouseX, MouseY, MouseScroll; + b32 KeysDown[(int)KeyCode_Count]; + s32 StringInputUsed; + char StringInput[INPUT_FRAME_STRING_LENGTH]; + s32 MouseX, MouseY, MouseScroll; }; struct input { - input_frame Frames[2]; - input_frame* New; - input_frame* Old; - s32 MouseDownX, MouseDownY; + input_frame Frames[2]; + input_frame* New; + input_frame* Old; + s32 MouseDownX, MouseDownY; }; enum key_state_flags { - KeyState_WasDown = 1 << 0, - KeyState_IsDown = 1 << 1, + KeyState_WasDown = 1 << 0, + KeyState_IsDown = 1 << 1, }; #define KeyWasDown(event) ((event & KeyState_WasDown) > 0) @@ -234,75 +234,75 @@ enum key_state_flags struct input_entry { - key_code Key; - b32 State; - b32 Modifiers; + key_code Key; + b32 State; + b32 Modifiers; }; struct input_queue { - s32 QueueSize; - s32 QueueUsed; - input_entry* Entries; + s32 QueueSize; + s32 QueueUsed; + input_entry* Entries; }; enum cursor_type { - CursorType_Arrow, - CursorType_Pointer, - CursorType_Loading, - CursorType_HArrows, - CursorType_VArrows, - CursorType_DTopLeftArrows, - CursorType_DTopRightArrows, - CursorType_Count, + CursorType_Arrow, + CursorType_Pointer, + CursorType_Loading, + CursorType_HArrows, + CursorType_VArrows, + CursorType_DTopLeftArrows, + CursorType_DTopRightArrows, + CursorType_Count, }; struct mouse_state { - s32 Scroll; - - v2 Pos; - v2 OldPos; - v2 DeltaPos; - v2 DownPos; - - b32 LeftButtonState; - b32 MiddleButtonState; - b32 RightButtonState; - - - - cursor_type CursorType; + s32 Scroll; + + v2 Pos; + v2 OldPos; + v2 DeltaPos; + v2 DownPos; + + b32 LeftButtonState; + b32 MiddleButtonState; + b32 RightButtonState; + + + + cursor_type CursorType; }; internal input_queue InputQueue_Create (u8* Memory, s32 MemorySize) { - input_queue Result = {}; - s32 EntriesCount = MemorySize / sizeof(input_entry); - Result.QueueSize = EntriesCount; - Result.QueueUsed = 0; - Result.Entries = (input_entry*)Memory; - return Result; + input_queue Result = {}; + s32 EntriesCount = MemorySize / sizeof(input_entry); + Result.QueueSize = EntriesCount; + Result.QueueUsed = 0; + Result.Entries = (input_entry*)Memory; + return Result; } internal input_queue InputQueue_Create(gs_memory_arena* Arena, u32 CountMax) { - input_queue Result = {0}; - if (CountMax > 0) - { - s32 Size = sizeof(input_entry) * 32; - u8* Memory = PushSize(Arena, Size); - Result = InputQueue_Create(Memory, Size); - } - return Result; + input_queue Result = {0}; + if (CountMax > 0) + { + s32 Size = sizeof(input_entry) * 32; + u8* Memory = PushSize(Arena, Size).Memory; + Result = InputQueue_Create(Memory, Size); + } + return Result; } internal void ResetInputQueue (input_queue* Queue) { - Queue->QueueUsed = 0; + Queue->QueueUsed = 0; } internal void InitializeInput (input* Input); @@ -311,360 +311,360 @@ internal void SwapInputBuffers (input* Input); internal void InitializeInput (input* Input) { - *(Input) = {}; - Input->New = &Input->Frames[0]; - Input->Old = &Input->Frames[1]; + *(Input) = {}; + Input->New = &Input->Frames[0]; + Input->Old = &Input->Frames[1]; } internal void SwapInputBuffers (input* Input) { - input_frame* NowOld = Input->New; - Input->New = Input->Old; - Input->Old = NowOld; - - for (s32 Key = 0; Key < KeyCode_Count; Key++) { Input->New->KeysDown[Key] = Input->Old->KeysDown[Key]; } - Input->New->StringInputUsed = 0; + input_frame* NowOld = Input->New; + Input->New = Input->Old; + Input->Old = NowOld; + + for (s32 Key = 0; Key < KeyCode_Count; Key++) { Input->New->KeysDown[Key] = Input->Old->KeysDown[Key]; } + Input->New->StringInputUsed = 0; } internal b32 KeyDown (input Input, key_code Key) { - return Input.New->KeysDown[Key]; + return Input.New->KeysDown[Key]; } internal b32 KeyTransitionedDown (input Input, key_code Key) { - return Input.New->KeysDown[Key] && !Input.Old->KeysDown[Key]; + return Input.New->KeysDown[Key] && !Input.Old->KeysDown[Key]; } internal b32 KeyTransitionedUp (input Input, key_code Key) { - return !Input.New->KeysDown[Key] && Input.Old->KeysDown[Key]; + return !Input.New->KeysDown[Key] && Input.Old->KeysDown[Key]; } internal void AddInputEventEntry (input_queue* Queue, key_code Key, b32 WasDown, b32 IsDown, b32 ShiftDown, b32 AltDown, b32 CtrlDown, b32 SysDown) { - Assert(Queue->QueueUsed < Queue->QueueSize); - - input_entry* Entry = Queue->Entries + Queue->QueueUsed++; - Entry->Key = Key; - - Entry->State = (key_state_flags)0; - if (WasDown) { Entry->State |= KeyState_WasDown; } - if (IsDown) { Entry->State |= KeyState_IsDown; } - - Entry->Modifiers = 0; - if (ShiftDown) { Entry->Modifiers |= Modifier_Shift; } - if (CtrlDown) { Entry->Modifiers |= Modifier_Ctrl; } - if (AltDown) { Entry->Modifiers |= Modifier_Alt; } - if (SysDown) { Entry->Modifiers |= Modifier_Sys; } + Assert(Queue->QueueUsed < Queue->QueueSize); + + input_entry* Entry = Queue->Entries + Queue->QueueUsed++; + Entry->Key = Key; + + Entry->State = (key_state_flags)0; + if (WasDown) { Entry->State |= KeyState_WasDown; } + if (IsDown) { Entry->State |= KeyState_IsDown; } + + Entry->Modifiers = 0; + if (ShiftDown) { Entry->Modifiers |= Modifier_Shift; } + if (CtrlDown) { Entry->Modifiers |= Modifier_Ctrl; } + if (AltDown) { Entry->Modifiers |= Modifier_Alt; } + if (SysDown) { Entry->Modifiers |= Modifier_Sys; } } internal b32 KeyTransitionedDown (input_entry Entry) { - return (!KeyWasDown(Entry.State) && KeyIsDown(Entry.State)); + return (!KeyWasDown(Entry.State) && KeyIsDown(Entry.State)); } internal b32 KeyTransitionedUp (input_entry Entry) { - return (KeyWasDown(Entry.State) && !KeyIsDown(Entry.State)); + return (KeyWasDown(Entry.State) && !KeyIsDown(Entry.State)); } internal b32 KeyHeldDown (input_entry Entry) { - return (KeyWasDown(Entry.State) && KeyIsDown(Entry.State)); + return (KeyWasDown(Entry.State) && KeyIsDown(Entry.State)); } internal b32 MouseButtonTransitionedDown (b32 MouseButtonState) { - return (!KeyWasDown(MouseButtonState) && KeyIsDown(MouseButtonState)); + return (!KeyWasDown(MouseButtonState) && KeyIsDown(MouseButtonState)); } internal b32 MouseButtonTransitionedUp (b32 MouseButtonState) { - return (KeyWasDown(MouseButtonState) && !KeyIsDown(MouseButtonState)); + return (KeyWasDown(MouseButtonState) && !KeyIsDown(MouseButtonState)); } internal b32 MouseButtonHeldDown (b32 MouseButtonState) { - b32 WasDown = KeyWasDown(MouseButtonState); - b32 IsDown = KeyIsDown(MouseButtonState); - return (WasDown && IsDown); + b32 WasDown = KeyWasDown(MouseButtonState); + b32 IsDown = KeyIsDown(MouseButtonState); + return (WasDown && IsDown); } inline b32 GetMouseButtonStateAdvanced (b32 ButtonState) { - b32 Result = ButtonState; - if (ButtonState & KeyState_WasDown && - !((ButtonState & KeyState_IsDown) > 0)) - { - Result= 0; - } - else if (ButtonState & KeyState_IsDown) - { - Result |= KeyState_WasDown; - } - return Result; + b32 Result = ButtonState; + if (ButtonState & KeyState_WasDown && + !((ButtonState & KeyState_IsDown) > 0)) + { + Result= 0; + } + else if (ButtonState & KeyState_IsDown) + { + Result |= KeyState_WasDown; + } + return Result; } internal char KeyCodeToChar(key_code Code) { - char Result = 0; + char Result = 0; + + switch (Code) + { + case KeyCode_Space: { Result = ' '; } break; + case KeyCode_Tab: { Result = '\t'; } break; + case KeyCode_Enter: { Result = '\n'; } break; + case KeyCode_Backspace: { Result = '\b'; } break; - switch (Code) - { - case KeyCode_Space: { Result = ' '; } break; - case KeyCode_Tab: { Result = '\t'; } break; - case KeyCode_Enter: { Result = '\n'; } break; - case KeyCode_Backspace: { Result = '\b'; } break; - - case KeyCode_a: { Result = 'a'; } break; - case KeyCode_b: { Result = 'b'; } break; - case KeyCode_c: { Result = 'c'; } break; - case KeyCode_d: { Result = 'd'; } break; - case KeyCode_e: { Result = 'e'; } break; - case KeyCode_f: { Result = 'f'; } break; - case KeyCode_g: { Result = 'g'; } break; - case KeyCode_h: { Result = 'h'; } break; - case KeyCode_i: { Result = 'i'; } break; - case KeyCode_j: { Result = 'j'; } break; - case KeyCode_k: { Result = 'k'; } break; - case KeyCode_l: { Result = 'l'; } break; - case KeyCode_m: { Result = 'm'; } break; - case KeyCode_n: { Result = 'n'; } break; - case KeyCode_o: { Result = 'o'; } break; - case KeyCode_p: { Result = 'p'; } break; - case KeyCode_q: { Result = 'q'; } break; - case KeyCode_r: { Result = 'r'; } break; - case KeyCode_s: { Result = 's'; } break; - case KeyCode_t: { Result = 't'; } break; - case KeyCode_u: { Result = 'u'; } break; - case KeyCode_v: { Result = 'v'; } break; - case KeyCode_w: { Result = 'w'; } break; - case KeyCode_x: { Result = 'x'; } break; - case KeyCode_y: { Result = 'y'; } break; - case KeyCode_z: { Result = 'z'; } break; - case KeyCode_A: { Result = 'A'; } break; - case KeyCode_B: { Result = 'B'; } break; - case KeyCode_C: { Result = 'C'; } break; - case KeyCode_D: { Result = 'D'; } break; - case KeyCode_E: { Result = 'E'; } break; - case KeyCode_F: { Result = 'F'; } break; - case KeyCode_G: { Result = 'G'; } break; - case KeyCode_H: { Result = 'H'; } break; - case KeyCode_I: { Result = 'I'; } break; - case KeyCode_J: { Result = 'J'; } break; - case KeyCode_K: { Result = 'K'; } break; - case KeyCode_L: { Result = 'L'; } break; - case KeyCode_M: { Result = 'M'; } break; - case KeyCode_N: { Result = 'N'; } break; - case KeyCode_O: { Result = 'O'; } break; - case KeyCode_P: { Result = 'P'; } break; - case KeyCode_Q: { Result = 'Q'; } break; - case KeyCode_R: { Result = 'R'; } break; - case KeyCode_S: { Result = 'S'; } break; - case KeyCode_T: { Result = 'T'; } break; - case KeyCode_U: { Result = 'U'; } break; - case KeyCode_V: { Result = 'V'; } break; - case KeyCode_W: { Result = 'W'; } break; - case KeyCode_X: { Result = 'X'; } break; - case KeyCode_Y: { Result = 'Y'; } break; - case KeyCode_Z: { Result = 'Z'; } break; - - case KeyCode_Num0: - case KeyCode_0: { Result = '0'; } break; - case KeyCode_Num1: - case KeyCode_1: { Result = '1'; } break; - case KeyCode_Num2: - case KeyCode_2: { Result = '2'; } break; - case KeyCode_Num3: - case KeyCode_3: { Result = '3'; } break; - case KeyCode_Num4: - case KeyCode_4: { Result = '4'; } break; - case KeyCode_Num5: - case KeyCode_5: { Result = '5'; } break; - case KeyCode_Num6: - case KeyCode_6: { Result = '6'; } break; - case KeyCode_Num7: - case KeyCode_7: { Result = '7'; } break; - case KeyCode_Num8: - case KeyCode_8: { Result = '8'; } break; - case KeyCode_Num9: - case KeyCode_9: { Result = '9'; } break; - - case KeyCode_Bang: { Result = '!'; } break; - case KeyCode_At: { Result = '@'; } break; - case KeyCode_Pound: { Result = '#'; } break; - case KeyCode_Dollar: { Result = '$'; } break; - case KeyCode_Percent: { Result = '%'; } break; - case KeyCode_Carrot: { Result = '^'; } break; - case KeyCode_Ampersand: { Result = '&'; } break; - case KeyCode_Star: { Result = '*'; } break; - case KeyCode_LeftParen: { Result = '('; } break; - case KeyCode_RightParen: { Result = ')'; } break; - case KeyCode_Minus: { Result = '-'; } break; - case KeyCode_Plus: { Result = '+'; } break; - case KeyCode_Equals: { Result = '='; } break; - case KeyCode_Underscore: { Result = '_'; } break; - case KeyCode_LeftBrace: { Result = '{'; } break; - case KeyCode_RightBrace: { Result = '}'; } break; - case KeyCode_LeftBracket: { Result = '['; } break; - case KeyCode_RightBracket: { Result = ']'; } break; - case KeyCode_Colon: { Result = ':'; } break; - case KeyCode_SemiColon: { Result = ';'; } break; - case KeyCode_SingleQuote: { Result = '\''; } break; - case KeyCode_DoubleQuote: { Result = '"'; } break; - case KeyCode_ForwardSlash: { Result = '/'; } break; - case KeyCode_Backslash: { Result = '\\'; } break; - case KeyCode_Pipe: { Result = '|'; } break; - case KeyCode_Comma: { Result = ','; } break; - case KeyCode_Period: { Result = '.'; } break; - case KeyCode_QuestionMark: { Result = '?'; } break; - case KeyCode_LessThan: { Result = '<'; } break; - case KeyCode_GreaterThan: { Result = '>'; } break; - case KeyCode_Tilde: { Result = '~'; } break; - case KeyCode_BackQuote: { Result = '`'; } break; - - default: { Result = 0; } break; - } + case KeyCode_a: { Result = 'a'; } break; + case KeyCode_b: { Result = 'b'; } break; + case KeyCode_c: { Result = 'c'; } break; + case KeyCode_d: { Result = 'd'; } break; + case KeyCode_e: { Result = 'e'; } break; + case KeyCode_f: { Result = 'f'; } break; + case KeyCode_g: { Result = 'g'; } break; + case KeyCode_h: { Result = 'h'; } break; + case KeyCode_i: { Result = 'i'; } break; + case KeyCode_j: { Result = 'j'; } break; + case KeyCode_k: { Result = 'k'; } break; + case KeyCode_l: { Result = 'l'; } break; + case KeyCode_m: { Result = 'm'; } break; + case KeyCode_n: { Result = 'n'; } break; + case KeyCode_o: { Result = 'o'; } break; + case KeyCode_p: { Result = 'p'; } break; + case KeyCode_q: { Result = 'q'; } break; + case KeyCode_r: { Result = 'r'; } break; + case KeyCode_s: { Result = 's'; } break; + case KeyCode_t: { Result = 't'; } break; + case KeyCode_u: { Result = 'u'; } break; + case KeyCode_v: { Result = 'v'; } break; + case KeyCode_w: { Result = 'w'; } break; + case KeyCode_x: { Result = 'x'; } break; + case KeyCode_y: { Result = 'y'; } break; + case KeyCode_z: { Result = 'z'; } break; + case KeyCode_A: { Result = 'A'; } break; + case KeyCode_B: { Result = 'B'; } break; + case KeyCode_C: { Result = 'C'; } break; + case KeyCode_D: { Result = 'D'; } break; + case KeyCode_E: { Result = 'E'; } break; + case KeyCode_F: { Result = 'F'; } break; + case KeyCode_G: { Result = 'G'; } break; + case KeyCode_H: { Result = 'H'; } break; + case KeyCode_I: { Result = 'I'; } break; + case KeyCode_J: { Result = 'J'; } break; + case KeyCode_K: { Result = 'K'; } break; + case KeyCode_L: { Result = 'L'; } break; + case KeyCode_M: { Result = 'M'; } break; + case KeyCode_N: { Result = 'N'; } break; + case KeyCode_O: { Result = 'O'; } break; + case KeyCode_P: { Result = 'P'; } break; + case KeyCode_Q: { Result = 'Q'; } break; + case KeyCode_R: { Result = 'R'; } break; + case KeyCode_S: { Result = 'S'; } break; + case KeyCode_T: { Result = 'T'; } break; + case KeyCode_U: { Result = 'U'; } break; + case KeyCode_V: { Result = 'V'; } break; + case KeyCode_W: { Result = 'W'; } break; + case KeyCode_X: { Result = 'X'; } break; + case KeyCode_Y: { Result = 'Y'; } break; + case KeyCode_Z: { Result = 'Z'; } break; - return Result; + case KeyCode_Num0: + case KeyCode_0: { Result = '0'; } break; + case KeyCode_Num1: + case KeyCode_1: { Result = '1'; } break; + case KeyCode_Num2: + case KeyCode_2: { Result = '2'; } break; + case KeyCode_Num3: + case KeyCode_3: { Result = '3'; } break; + case KeyCode_Num4: + case KeyCode_4: { Result = '4'; } break; + case KeyCode_Num5: + case KeyCode_5: { Result = '5'; } break; + case KeyCode_Num6: + case KeyCode_6: { Result = '6'; } break; + case KeyCode_Num7: + case KeyCode_7: { Result = '7'; } break; + case KeyCode_Num8: + case KeyCode_8: { Result = '8'; } break; + case KeyCode_Num9: + case KeyCode_9: { Result = '9'; } break; + + case KeyCode_Bang: { Result = '!'; } break; + case KeyCode_At: { Result = '@'; } break; + case KeyCode_Pound: { Result = '#'; } break; + case KeyCode_Dollar: { Result = '$'; } break; + case KeyCode_Percent: { Result = '%'; } break; + case KeyCode_Carrot: { Result = '^'; } break; + case KeyCode_Ampersand: { Result = '&'; } break; + case KeyCode_Star: { Result = '*'; } break; + case KeyCode_LeftParen: { Result = '('; } break; + case KeyCode_RightParen: { Result = ')'; } break; + case KeyCode_Minus: { Result = '-'; } break; + case KeyCode_Plus: { Result = '+'; } break; + case KeyCode_Equals: { Result = '='; } break; + case KeyCode_Underscore: { Result = '_'; } break; + case KeyCode_LeftBrace: { Result = '{'; } break; + case KeyCode_RightBrace: { Result = '}'; } break; + case KeyCode_LeftBracket: { Result = '['; } break; + case KeyCode_RightBracket: { Result = ']'; } break; + case KeyCode_Colon: { Result = ':'; } break; + case KeyCode_SemiColon: { Result = ';'; } break; + case KeyCode_SingleQuote: { Result = '\''; } break; + case KeyCode_DoubleQuote: { Result = '"'; } break; + case KeyCode_ForwardSlash: { Result = '/'; } break; + case KeyCode_Backslash: { Result = '\\'; } break; + case KeyCode_Pipe: { Result = '|'; } break; + case KeyCode_Comma: { Result = ','; } break; + case KeyCode_Period: { Result = '.'; } break; + case KeyCode_QuestionMark: { Result = '?'; } break; + case KeyCode_LessThan: { Result = '<'; } break; + case KeyCode_GreaterThan: { Result = '>'; } break; + case KeyCode_Tilde: { Result = '~'; } break; + case KeyCode_BackQuote: { Result = '`'; } break; + + default: { Result = 0; } break; + } + + return Result; } internal bool KeyCodeHasChar(key_code Code) { - bool Result = KeyCodeToChar(Code) != 0; - return Result; + bool Result = KeyCodeToChar(Code) != 0; + return Result; } internal key_code CharToKeyCode(char C) { - key_code Result = KeyCode_Invalid; + key_code Result = KeyCode_Invalid; + + switch (C) + { + case ' ': { Result = KeyCode_Space; } break; + case '\t': { Result = KeyCode_Tab; } break; + case '\n': { Result = KeyCode_Enter; } break; + case '\b': { Result = KeyCode_Backspace; } break; - switch (C) - { - case ' ': { Result = KeyCode_Space; } break; - case '\t': { Result = KeyCode_Tab; } break; - case '\n': { Result = KeyCode_Enter; } break; - case '\b': { Result = KeyCode_Backspace; } break; - - case 'a': { Result = KeyCode_a; } break; - case 'b': { Result = KeyCode_b; } break; - case 'c': { Result = KeyCode_c; } break; - case 'd': { Result = KeyCode_d; } break; - case 'e': { Result = KeyCode_e; } break; - case 'f': { Result = KeyCode_f; } break; - case 'g': { Result = KeyCode_g; } break; - case 'h': { Result = KeyCode_h; } break; - case 'i': { Result = KeyCode_i; } break; - case 'j': { Result = KeyCode_j; } break; - case 'k': { Result = KeyCode_k; } break; - case 'l': { Result = KeyCode_l; } break; - case 'm': { Result = KeyCode_m; } break; - case 'n': { Result = KeyCode_n; } break; - case 'o': { Result = KeyCode_o; } break; - case 'p': { Result = KeyCode_p; } break; - case 'q': { Result = KeyCode_q; } break; - case 'r': { Result = KeyCode_r; } break; - case 's': { Result = KeyCode_s; } break; - case 't': { Result = KeyCode_t; } break; - case 'u': { Result = KeyCode_u; } break; - case 'v': { Result = KeyCode_v; } break; - case 'w': { Result = KeyCode_w; } break; - case 'x': { Result = KeyCode_x; } break; - case 'y': { Result = KeyCode_y; } break; - case 'z': { Result = KeyCode_z; } break; - case 'A': { Result = KeyCode_A; } break; - case 'B': { Result = KeyCode_B; } break; - case 'C': { Result = KeyCode_C; } break; - case 'D': { Result = KeyCode_D; } break; - case 'E': { Result = KeyCode_E; } break; - case 'F': { Result = KeyCode_F; } break; - case 'G': { Result = KeyCode_G; } break; - case 'H': { Result = KeyCode_H; } break; - case 'I': { Result = KeyCode_I; } break; - case 'J': { Result = KeyCode_J; } break; - case 'K': { Result = KeyCode_K; } break; - case 'L': { Result = KeyCode_L; } break; - case 'M': { Result = KeyCode_M; } break; - case 'N': { Result = KeyCode_N; } break; - case 'O': { Result = KeyCode_O; } break; - case 'P': { Result = KeyCode_P; } break; - case 'Q': { Result = KeyCode_Q; } break; - case 'R': { Result = KeyCode_R; } break; - case 'S': { Result = KeyCode_S; } break; - case 'T': { Result = KeyCode_T; } break; - case 'U': { Result = KeyCode_U; } break; - case 'V': { Result = KeyCode_V; } break; - case 'W': { Result = KeyCode_W; } break; - case 'X': { Result = KeyCode_X; } break; - case 'Y': { Result = KeyCode_Y; } break; - case 'Z': { Result = KeyCode_Z; } break; - - case '0': { Result = KeyCode_0; } break; - case '1': { Result = KeyCode_1; } break; - case '2': { Result = KeyCode_2; } break; - case '3': { Result = KeyCode_3; } break; - case '4': { Result = KeyCode_4; } break; - case '5': { Result = KeyCode_5; } break; - case '6': { Result = KeyCode_6; } break; - case '7': { Result = KeyCode_7; } break; - case '8': { Result = KeyCode_8; } break; - case '9': { Result = KeyCode_9; } break; - - case '!': { Result = KeyCode_Bang; } break; - case '@': { Result = KeyCode_At; } break; - case '#': { Result = KeyCode_Pound; } break; - case '$': { Result = KeyCode_Dollar; } break; - case '%': { Result = KeyCode_Percent; } break; - case '^': { Result = KeyCode_Carrot; } break; - case '&': { Result = KeyCode_Ampersand; } break; - case '*': { Result = KeyCode_Star; } break; - case '(': { Result = KeyCode_LeftParen; } break; - case ')': { Result = KeyCode_RightParen; } break; - case '-': { Result = KeyCode_Minus; } break; - case '+': { Result = KeyCode_Plus; } break; - case '=': { Result = KeyCode_Equals; } break; - case '_': { Result = KeyCode_Underscore; } break; - case '{': { Result = KeyCode_LeftBrace; } break; - case '}': { Result = KeyCode_RightBrace; } break; - case '[': { Result = KeyCode_LeftBracket; } break; - case ']': { Result = KeyCode_RightBracket; } break; - case ':': { Result = KeyCode_Colon; } break; - case ';': { Result = KeyCode_SemiColon; } break; - case '\'': { Result = KeyCode_SingleQuote; } break; - case '"': { Result = KeyCode_DoubleQuote; } break; - case '/': { Result = KeyCode_ForwardSlash; } break; - case '\\': { Result = KeyCode_Backslash; } break; - case '|': { Result = KeyCode_Pipe; } break; - case ',': { Result = KeyCode_Comma; } break; - case '.': { Result = KeyCode_Period; } break; - case '?': { Result = KeyCode_QuestionMark; } break; - case '<': { Result = KeyCode_LessThan; } break; - case '>': { Result = KeyCode_GreaterThan; } break; - case '~': { Result = KeyCode_Tilde; } break; - case '`': { Result = KeyCode_BackQuote; } break; - - default: { Result = KeyCode_Invalid; } break; - } + case 'a': { Result = KeyCode_a; } break; + case 'b': { Result = KeyCode_b; } break; + case 'c': { Result = KeyCode_c; } break; + case 'd': { Result = KeyCode_d; } break; + case 'e': { Result = KeyCode_e; } break; + case 'f': { Result = KeyCode_f; } break; + case 'g': { Result = KeyCode_g; } break; + case 'h': { Result = KeyCode_h; } break; + case 'i': { Result = KeyCode_i; } break; + case 'j': { Result = KeyCode_j; } break; + case 'k': { Result = KeyCode_k; } break; + case 'l': { Result = KeyCode_l; } break; + case 'm': { Result = KeyCode_m; } break; + case 'n': { Result = KeyCode_n; } break; + case 'o': { Result = KeyCode_o; } break; + case 'p': { Result = KeyCode_p; } break; + case 'q': { Result = KeyCode_q; } break; + case 'r': { Result = KeyCode_r; } break; + case 's': { Result = KeyCode_s; } break; + case 't': { Result = KeyCode_t; } break; + case 'u': { Result = KeyCode_u; } break; + case 'v': { Result = KeyCode_v; } break; + case 'w': { Result = KeyCode_w; } break; + case 'x': { Result = KeyCode_x; } break; + case 'y': { Result = KeyCode_y; } break; + case 'z': { Result = KeyCode_z; } break; + case 'A': { Result = KeyCode_A; } break; + case 'B': { Result = KeyCode_B; } break; + case 'C': { Result = KeyCode_C; } break; + case 'D': { Result = KeyCode_D; } break; + case 'E': { Result = KeyCode_E; } break; + case 'F': { Result = KeyCode_F; } break; + case 'G': { Result = KeyCode_G; } break; + case 'H': { Result = KeyCode_H; } break; + case 'I': { Result = KeyCode_I; } break; + case 'J': { Result = KeyCode_J; } break; + case 'K': { Result = KeyCode_K; } break; + case 'L': { Result = KeyCode_L; } break; + case 'M': { Result = KeyCode_M; } break; + case 'N': { Result = KeyCode_N; } break; + case 'O': { Result = KeyCode_O; } break; + case 'P': { Result = KeyCode_P; } break; + case 'Q': { Result = KeyCode_Q; } break; + case 'R': { Result = KeyCode_R; } break; + case 'S': { Result = KeyCode_S; } break; + case 'T': { Result = KeyCode_T; } break; + case 'U': { Result = KeyCode_U; } break; + case 'V': { Result = KeyCode_V; } break; + case 'W': { Result = KeyCode_W; } break; + case 'X': { Result = KeyCode_X; } break; + case 'Y': { Result = KeyCode_Y; } break; + case 'Z': { Result = KeyCode_Z; } break; - return Result; + case '0': { Result = KeyCode_0; } break; + case '1': { Result = KeyCode_1; } break; + case '2': { Result = KeyCode_2; } break; + case '3': { Result = KeyCode_3; } break; + case '4': { Result = KeyCode_4; } break; + case '5': { Result = KeyCode_5; } break; + case '6': { Result = KeyCode_6; } break; + case '7': { Result = KeyCode_7; } break; + case '8': { Result = KeyCode_8; } break; + case '9': { Result = KeyCode_9; } break; + + case '!': { Result = KeyCode_Bang; } break; + case '@': { Result = KeyCode_At; } break; + case '#': { Result = KeyCode_Pound; } break; + case '$': { Result = KeyCode_Dollar; } break; + case '%': { Result = KeyCode_Percent; } break; + case '^': { Result = KeyCode_Carrot; } break; + case '&': { Result = KeyCode_Ampersand; } break; + case '*': { Result = KeyCode_Star; } break; + case '(': { Result = KeyCode_LeftParen; } break; + case ')': { Result = KeyCode_RightParen; } break; + case '-': { Result = KeyCode_Minus; } break; + case '+': { Result = KeyCode_Plus; } break; + case '=': { Result = KeyCode_Equals; } break; + case '_': { Result = KeyCode_Underscore; } break; + case '{': { Result = KeyCode_LeftBrace; } break; + case '}': { Result = KeyCode_RightBrace; } break; + case '[': { Result = KeyCode_LeftBracket; } break; + case ']': { Result = KeyCode_RightBracket; } break; + case ':': { Result = KeyCode_Colon; } break; + case ';': { Result = KeyCode_SemiColon; } break; + case '\'': { Result = KeyCode_SingleQuote; } break; + case '"': { Result = KeyCode_DoubleQuote; } break; + case '/': { Result = KeyCode_ForwardSlash; } break; + case '\\': { Result = KeyCode_Backslash; } break; + case '|': { Result = KeyCode_Pipe; } break; + case ',': { Result = KeyCode_Comma; } break; + case '.': { Result = KeyCode_Period; } break; + case '?': { Result = KeyCode_QuestionMark; } break; + case '<': { Result = KeyCode_LessThan; } break; + case '>': { Result = KeyCode_GreaterThan; } break; + case '~': { Result = KeyCode_Tilde; } break; + case '`': { Result = KeyCode_BackQuote; } break; + + default: { Result = KeyCode_Invalid; } break; + } + + return Result; } #define GS_INPUT_H diff --git a/src/gs_libs/gs_memory.h b/src/gs_libs/gs_memory.h new file mode 100644 index 0000000..d72ac56 --- /dev/null +++ b/src/gs_libs/gs_memory.h @@ -0,0 +1,528 @@ +/* date = May 10th 2021 10:19 pm */ + +#ifndef GS_MEMORY_H +#define GS_MEMORY_H + +#if !defined(Assert) +# define Assert(c) +#endif + +#if !defined(InvalidCodePath) +# define InvalidCodePath +#endif + +#if !defined(GS_MEMORY_PROFILE_FUNC) +# define GS_MEMORY_PROFILE_FUNC +#endif + +#define MemoryClearArray(b,t,s) MemoryClear((u8*)(b), sizeof(t) * (s)) +internal void +MemoryClear(u8* Base, u64 Size) +{ + for (u64 i = 0; i < Size; i++) Base[i] = 0; +} + +#ifndef DEBUG_LOC + +typedef struct gs_debug_loc +{ + char* File; + char* Function; + u32 Line; +} gs_debug_loc; + +# define DEBUG_LOC gs_debug_loc{ (char*)__FILE__, (char*)__FUNCTION__, __LINE__ } + +#endif +// +// Debug Info +// + +typedef struct gs_debug_memory_allocation +{ + gs_debug_loc Loc; + u64 ArenaHash; + u64 Size; + + struct gs_debug_memory_allocation* Next; +} gs_debug_memory_allocation; + +typedef struct gs_debug_arena_info +{ + char* ArenaName; + u64 AllocationsCount; + u64 TotalSize; +} gs_debug_arena_info; + +typedef struct gs_debug_allocations_list +{ + gs_debug_memory_allocation* Root; + gs_debug_memory_allocation* Head; + u64 AllocationsSizeTotal; + u64 AllocationsCount; + + u64 ArenaHashesCount; + u64* ArenaHashes; + gs_debug_arena_info* ArenaInfos; +} gs_debug_allocations_list; + +// +// Allocator +// + +#define PLATFORM_ALLOC(name) void* name(u64 Size, u64* ResultSize, u8* UserData) +typedef PLATFORM_ALLOC(platform_alloc); + +#define PLATFORM_FREE(name) void name(void* Base, u64 Size, u8* UserData) +typedef PLATFORM_FREE(platform_free); + +typedef struct gs_allocator +{ + platform_alloc* PAlloc; + platform_free* PFree; + u8* UserData; + + gs_debug_allocations_list* DEBUGAllocList; +} gs_allocator; + +PLATFORM_ALLOC(AllocNoOp) +{ + GS_MEMORY_PROFILE_FUNC; + if (ResultSize) *ResultSize = 0; + return 0; +} + +PLATFORM_FREE(FreeNoOp) +{ + GS_MEMORY_PROFILE_FUNC; + return; +} + +internal u64 +GSMemoryHash(char* Str, u64 Len) +{ + u64 Hash = 5381; + for (u32 i = 0; i < Len; i++) + { + Hash = ((Hash << 5) + Hash) + Str[i]; + } + return Hash; +} + +#define Alloc(a,s,an) Alloc_((a),(s),DEBUG_LOC, (char*)(an)).Memory +#define AllocStruct(a,t,an) (t*)Alloc_((a),sizeof(t),DEBUG_LOC, (char*)(an)).Memory +#define AllocArray(a,t,c,an) (t*)Alloc_((a),sizeof(t)*(c),DEBUG_LOC, (char*)(an)).Memory +#define AllocData(a,s,an) Alloc_((a),(s),DEBUG_LOC, (char*)(an)) + +internal gs_data +Alloc_(gs_allocator A, u64 Size, gs_debug_loc Loc, char* ArenaName) +{ + GS_MEMORY_PROFILE_FUNC; + + gs_data Result = {}; + Result.Memory = (u8*)A.PAlloc(Size, &Result.Size, A.UserData); + + // Debug + { + u64 ArenaHash = GSMemoryHash(ArenaName, CStringLength(ArenaName)); + + gs_debug_memory_allocation* Allocation = (gs_debug_memory_allocation*)A.PAlloc(sizeof(gs_debug_memory_allocation*), 0, A.UserData); + Allocation->Loc = Loc; + Allocation->Size = Size; + Allocation->ArenaHash = ArenaHash; + SLLPushOrInit(A.DEBUGAllocList->Root, A.DEBUGAllocList->Head, Allocation); + + A.DEBUGAllocList->AllocationsSizeTotal += Size; + A.DEBUGAllocList->AllocationsCount += 1; + + u64 ArenaStartIndex = ArenaHash % A.DEBUGAllocList->ArenaHashesCount; + u64 ArenaIndex = ArenaStartIndex; + while (A.DEBUGAllocList->ArenaHashes[ArenaIndex] != 0 && + A.DEBUGAllocList->ArenaHashes[ArenaIndex] != ArenaHash) + { + ArenaIndex = (ArenaIndex + 1) % A.DEBUGAllocList->ArenaHashesCount; + + // NOTE(PS): this means you've created enough arena's to fill up every + // slot in the arena hash table. Go increase the size of of DEBUG_ALLOCATOR_ARENA_MAX + Assert(ArenaIndex != ArenaStartIndex); + } + + if (A.DEBUGAllocList->ArenaHashes[ArenaIndex] == 0) + { + A.DEBUGAllocList->ArenaHashes[ArenaIndex] = ArenaHash; + + gs_debug_arena_info AI = {}; + AI.ArenaName = ArenaName; + A.DEBUGAllocList->ArenaInfos[ArenaIndex] = AI; + } + + A.DEBUGAllocList->ArenaInfos[ArenaIndex].AllocationsCount += 1; + A.DEBUGAllocList->ArenaInfos[ArenaIndex].TotalSize += Size; + } + + + + return Result; +} + +#define AllocString(a,s,l) AllocString_((a), (s), DEBUG_LOC, (l)) +internal gs_string +AllocString_(gs_allocator A, u64 Size, gs_debug_loc Loc, char* LocString) +{ + gs_string Result = {}; + Result.Str = (char*)Alloc_(A, sizeof(char)*Size, Loc, LocString).Memory; + Result.Length = 0; + Result.Size = Size; + return Result; +} + +#define Free(a,b,s) Free_((a),(b),(s),DEBUG_LOC) +#define FreeStruct(a,b,t) Free_((a),(u8*)(b),sizeof(t),DEBUG_LOC) +#define FreeArray(a,b,t,c) Free_((a),(u8*)(b),sizeof(t)*(c),DEBUG_LOC) +internal void +Free_(gs_allocator A, u8* Base, u64 Size, gs_debug_loc Loc) +{ + GS_MEMORY_PROFILE_FUNC; + + A.PFree(Base, Size, A.UserData); +} + +// NOTE(PS): cast function and struct pointers to proper data types +// for convenience +#define AllocatorCreate(a,f,u) AllocatorCreate_((platform_alloc*)(a),(platform_free*)(f),(u8*)(u)) +internal gs_allocator +AllocatorCreate_(platform_alloc* PAlloc, platform_free* PFree, u8* UserData) +{ + gs_allocator Result = {}; + Result.PAlloc = PAlloc; + Result.PFree = PFree; + Result.UserData = UserData; + + if (!PAlloc) Result.PAlloc = AllocNoOp; + if (!PFree) Result.PFree = FreeNoOp; + + // @DEBUG +#define DEBUG_ALLOCATOR_ARENA_MAX 256 + Result.DEBUGAllocList = (gs_debug_allocations_list*)PAlloc(sizeof(gs_debug_allocations_list*), 0, UserData); + + Result.DEBUGAllocList->ArenaHashesCount = DEBUG_ALLOCATOR_ARENA_MAX; + Result.DEBUGAllocList->ArenaHashes = (u64*)PAlloc(sizeof(u64*) * DEBUG_ALLOCATOR_ARENA_MAX, 0, UserData); + Result.DEBUGAllocList->ArenaInfos = (gs_debug_arena_info*)PAlloc(sizeof(gs_debug_arena_info) * DEBUG_ALLOCATOR_ARENA_MAX, 0, UserData); + return Result; +} + +// +// Memory Cursor +// + +typedef struct gs_memory_cursor +{ + gs_data Data; + u64 Position; +} gs_memory_cursor; + +internal gs_memory_cursor +MemoryCursorCreate(u8* Base, u64 Size) +{ + GS_MEMORY_PROFILE_FUNC; + + gs_memory_cursor Result = {}; + Result.Data.Memory = Base; + Result.Data.Size = Size; + Result.Position = 0; + + return Result; +} +internal gs_memory_cursor +MemoryCursorCreateFromData(gs_data Data) +{ + GS_MEMORY_PROFILE_FUNC; + return MemoryCursorCreate(Data.Memory, Data.Size); +} + +internal u64 +MemoryCursorRoomLeft(gs_memory_cursor Cursor) +{ + GS_MEMORY_PROFILE_FUNC; + + u64 Result = 0; + if (Cursor.Data.Size >= Cursor.Position) + { + Result = Cursor.Data.Size - Cursor.Position; + } + return Result; +} + +internal bool +MemoryCursorHasRoom(gs_memory_cursor Cursor) +{ + GS_MEMORY_PROFILE_FUNC; + + u64 RoomLeft = MemoryCursorRoomLeft(Cursor); + bool Result = RoomLeft > 0; + return Result; +} + +internal bool +MemoryCursorCanPush(gs_memory_cursor Cursor, u64 Size) +{ + GS_MEMORY_PROFILE_FUNC; + + u64 RoomLeft = MemoryCursorRoomLeft(Cursor); + bool Result = RoomLeft >= Size; + return Result; +} + +#define MemoryCursorPushSize(c,s) MemoryCursorPushSize_((c),(s)) +#define MemoryCursorPushStruct(c,s) (s*)MemoryCursorPushSize_((c),sizeof(s)).Memory +#define MemoryCursorPushArray(c,s,l) (s*)MemoryCursorPushSize_((c),sizeof(s)*(l)).Memory +internal gs_data +MemoryCursorPushSize_(gs_memory_cursor* C, u64 Size) +{ + GS_MEMORY_PROFILE_FUNC; + + gs_data Result = {0}; + if (MemoryCursorCanPush(*C, Size)) + { + Result.Memory = C->Data.Memory + C->Position; + Result.Size = Size, + C->Position += Size; + } + return Result; +} + +internal gs_data +MemoryCursorAlign(gs_memory_cursor* C, u64 Alignment) +{ + GS_MEMORY_PROFILE_FUNC; + + u64 Position = RoundUpTo64(C->Position, Alignment); + if (Position > C->Data.Size) + { + Position = C->Data.Size; + } + u64 AlignmentDist = Position - C->Position; + gs_data Result = MemoryCursorPushSize_(C, AlignmentDist); + return Result; +} + +#define MemoryCursorWriteValue(c,t,v) *PushStructOnCursor((c),(t)) = (v) +internal void +MemoryCursorWriteBuffer(gs_memory_cursor* C, gs_data Buffer) +{ + GS_MEMORY_PROFILE_FUNC; + + gs_data Dest = MemoryCursorPushSize(C, Buffer.Size); + if (Dest.Size == Buffer.Size) + { + CopyMemoryTo(Buffer.Memory, Dest.Memory, Buffer.Size); + } +} + +internal void +MemoryCursorPopSize(gs_memory_cursor* C, u64 Size) +{ + GS_MEMORY_PROFILE_FUNC; + + u64 SizeToPop = Size; + if (SizeToPop > C->Position) + { + SizeToPop = C->Position; + } + + C->Position -= SizeToPop; +} + +internal void +MemoryCursorReset(gs_memory_cursor* C) +{ + GS_MEMORY_PROFILE_FUNC; + + C->Position = 0; +} + +// +// Memory Arena +// + +typedef struct gs_memory_cursor_sll +{ + gs_memory_cursor Cursor; + struct gs_memory_cursor_sll* Next; +} gs_memory_cursor_sll; + +typedef struct gs_memory_arena +{ + u64 ChunkSize; + u64 Alignment; + char* ArenaName; + + gs_memory_cursor_sll* CursorsRoot; + gs_memory_cursor_sll* CursorsHead; + + struct gs_memory_arena* Parent; + gs_allocator Allocator; + u8* UserData; + // TODO: some sort of GrowArena function +} gs_memory_arena; + +internal gs_memory_arena +MemoryArenaCreate(u64 ChunkSize, u64 Alignment, gs_allocator Allocator, gs_memory_arena* Parent, u8* UserData, char* Name) +{ + GS_MEMORY_PROFILE_FUNC; + + gs_memory_arena Result = {}; + Result.ChunkSize = ChunkSize; + Result.Alignment = Alignment; + Result.Allocator = Allocator; + Result.Parent = Parent; + Result.UserData = UserData; + Result.ArenaName = Name; + + return Result; +} + +internal gs_data PushSize_(gs_memory_arena* Arena, u64 Size, gs_debug_loc Loc); + +internal gs_memory_cursor* +MemoryArenaPushCursor(gs_memory_arena* Arena, u64 MinSize, gs_debug_loc Loc) +{ + GS_MEMORY_PROFILE_FUNC; + + gs_memory_cursor* Result = 0; + + u64 CursorSize = MinSize; + if (CursorSize < Arena->ChunkSize) + { + CursorSize = Arena->ChunkSize; + } + u64 AllocSize = CursorSize + sizeof(gs_memory_cursor_sll); + + gs_data CursorMemory = {0}; + if (Arena->Parent) + { + CursorMemory = PushSize_(Arena->Parent, AllocSize, Loc); + } else if (Arena->UserData) { + // TODO(PS): implement custom MemoryArenaAllocCursor functionality + InvalidCodePath; + } else { + Assert(Arena->Allocator.PAlloc); + CursorMemory = Alloc_(Arena->Allocator, AllocSize, Loc, Arena->ArenaName); + } + + gs_memory_cursor_sll* CursorEntry = (gs_memory_cursor_sll*)CursorMemory.Memory; + if (!Arena->CursorsRoot) + { + Arena->CursorsRoot = CursorEntry; + } + if (Arena->CursorsHead) + { + Arena->CursorsHead->Next = CursorEntry; + } + Arena->CursorsHead = CursorEntry; + + u8* CursorBase = (u8*)(CursorEntry + 1); + CursorEntry->Cursor = MemoryCursorCreate(CursorBase, CursorSize); + Result = &CursorEntry->Cursor; + + return Result; +} + +#define PushSize(a,s) PushSize_((a), (s), DEBUG_LOC) +#define PushStruct(a,t) (t*)PushSize_((a), sizeof(t), DEBUG_LOC).Memory +#define PushArray(a,t,c) (t*)PushSize_((a), sizeof(t) * (c), DEBUG_LOC).Memory +#define PushString(a,c) gs_string{ PushArray((a),char,(c)), 0, (c) } + +internal gs_data +PushSize_(gs_memory_arena* Arena, u64 Size, gs_debug_loc Loc) +{ + GS_MEMORY_PROFILE_FUNC; + + gs_data Result = {0}; + if (Size > 0) + { + gs_memory_cursor* Cursor = 0; + for (gs_memory_cursor_sll* C = Arena->CursorsRoot; + C != 0; + C = C->Next) + { + if (MemoryCursorCanPush(C->Cursor, Size)) + { + Cursor = &C->Cursor; + break; + } + } + + // NOTE(PS): We didn't find a cursor with enough room + // for the allocation being requested + if (!Cursor) + { + Cursor = MemoryArenaPushCursor(Arena, Size, Loc); + } + Assert(Cursor); + Assert(MemoryCursorCanPush(*Cursor, Size)); + + Result = MemoryCursorPushSize(Cursor, Size); + + gs_data Alignment = MemoryCursorAlign(Cursor, Arena->Alignment); + Result.Size += Alignment.Size; + } + return Result; +} + +internal void +MemoryArenaClear(gs_memory_arena* Arena) +{ + GS_MEMORY_PROFILE_FUNC; + + for (gs_memory_cursor_sll* C = Arena->CursorsRoot; + C != 0; + C = C->Next) + { + MemoryCursorReset(&C->Cursor); + } +} + +internal void +MemoryArenaFree(gs_memory_arena* Arena) +{ + GS_MEMORY_PROFILE_FUNC; + + // NOTE(PS): If this isn't a base arena, we can't + // really free it. + // TODO(PS): Once we have the User Specified codepaths + // in, we can probably provide a way for the user to + // let us free a custom allocator + Assert(Arena->Allocator.PFree); + + gs_allocator A = Arena->Allocator; + gs_memory_cursor_sll* At = Arena->CursorsRoot; + while (At) + { + gs_memory_cursor_sll* NextAt = At->Next; + + u64 Size = At->Cursor.Data.Size + sizeof(gs_memory_cursor_sll); + Free(A, (u8*)At, Size); + + At = NextAt; + } + + Arena->CursorsRoot = 0; + Arena->CursorsHead = 0; +} + +#ifdef GS_PLATFORM_IMPLEMENTATION + +internal gs_allocator CreatePlatformAllocator(); + +# if PLATFORM_WINDOWS +# include "./gs_memory_win32.h" +# elif PLATFORM_OSX +# include "./gs_memory_osx.h" +# elif PLATFORM_LINUX +# include "./gs_memory_linux.h" +# endif + +#endif + +#endif //GS_MEMORY_H diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index bcf9f99..bdf12c4 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -9,18 +9,18 @@ internal gs_data StructToData_(u8* Memory, u64 Size) { - gs_data Result = {0}; - Result.Memory = Memory; - Result.Size = Size; - return Result; + gs_data Result = {0}; + Result.Memory = Memory; + Result.Size = Size; + return Result; } internal u32 U32DivideRoundUp (u32 A, u32 B) { - r32 Result = (r32)A / (r32)B; - Result += .99999f; - return (u32)Result; + r32 Result = (r32)A / (r32)B; + Result += .99999f; + return (u32)Result; } inline bool XOR(bool A, bool B) { return (A == !B); } @@ -31,495 +31,495 @@ inline bool XOR(b64 A, b64 B) { return (A == !B); } internal u32 RoundUpToMultiple(u32 Value, u32 MultipleOf) { - u32 Result = Value; - if (MultipleOf != 0) - { - u32 Remainder = Value % MultipleOf; - Result = Value + (MultipleOf - Remainder); - } - return Result; + u32 Result = Value; + if (MultipleOf != 0) + { + u32 Remainder = Value % MultipleOf; + Result = Value + (MultipleOf - Remainder); + } + return Result; } internal u32 RoundUpToPow2U32(u32 Value) { - u32 Result = Value - 1; - Result |= Result >> 1; - Result |= Result >> 2; - Result |= Result >> 4; - Result |= Result >> 8; - Result |= Result >> 16; - Result++; - return Result; + u32 Result = Value - 1; + Result |= Result >> 1; + Result |= Result >> 2; + Result |= Result >> 4; + Result |= Result >> 8; + Result |= Result >> 16; + Result++; + return Result; } internal u32 RoundUpToPow2U64(u64 Value) { - u64 Result = Value - 1; - Result |= Result >> 1; - Result |= Result >> 2; - Result |= Result >> 4; - Result |= Result >> 8; - Result |= Result >> 16; - Result |= Result >> 32; - Result++; - return Result; + u64 Result = Value - 1; + Result |= Result >> 1; + Result |= Result >> 2; + Result |= Result >> 4; + Result |= Result >> 8; + Result |= Result >> 16; + Result |= Result >> 32; + Result++; + return Result; } internal u64 RoundUpTo64(u64 Value, u64 Alignment) { - Value += Alignment - 1; - Value -= Value % Alignment; - return Value; + Value += Alignment - 1; + Value -= Value % Alignment; + return Value; } internal u8 PowU8(u8 X, u32 Power) { - u8 Result = X; - for (u32 i = 1; i < Power; i++) { Result *= X; } - return Result; + u8 Result = X; + for (u32 i = 1; i < Power; i++) { Result *= X; } + return Result; } internal u16 PowU16(u16 X, u32 Power) { - u16 Result = X; - for (u32 i = 1; i < Power; i++) { Result *= X; } - return Result; + u16 Result = X; + for (u32 i = 1; i < Power; i++) { Result *= X; } + return Result; } internal u32 PowU32(u32 X, u32 Power) { - u32 Result = X; - for (u32 i = 1; i < Power; i++) { Result *= X; } - return Result; + u32 Result = X; + for (u32 i = 1; i < Power; i++) { Result *= X; } + return Result; } internal u64 PowU64(u64 X, u32 Power) { - u64 Result = X; - for (u32 i = 1; i < Power; i++) { Result *= X; } - return Result; + u64 Result = X; + for (u32 i = 1; i < Power; i++) { Result *= X; } + return Result; } internal s8 PowS8(s8 X, u32 Power) { - s8 Result = X; - for (u32 i = 1; i < Power; i++) { Result *= X; } - return Result; + s8 Result = X; + for (u32 i = 1; i < Power; i++) { Result *= X; } + return Result; } internal s16 PowS16(s16 X, u32 Power) { - s16 Result = X; - for (u32 i = 1; i < Power; i++) { Result *= X; } - return Result; + s16 Result = X; + for (u32 i = 1; i < Power; i++) { Result *= X; } + return Result; } internal s32 PowS32(s32 X, u32 Power) { - s32 Result = X; - for (u32 i = 1; i < Power; i++) { Result *= X; } - return Result; + s32 Result = X; + for (u32 i = 1; i < Power; i++) { Result *= X; } + return Result; } internal s64 PowS64(s64 X, u32 Power) { - s64 Result = X; - for (u32 i = 1; i < Power; i++) { Result *= X; } - return Result; + s64 Result = X; + for (u32 i = 1; i < Power; i++) { Result *= X; } + return Result; } internal r32 PowR32(r32 X, u32 Power) { - r32 Result = X; - for (u32 i = 1; i < Power; i++) { Result *= X; } - return Result; + r32 Result = X; + for (u32 i = 1; i < Power; i++) { Result *= X; } + return Result; } internal r64 PowR64(r64 X, u32 Power) { - r64 Result = X; - for (u32 i = 1; i < Power; i++) { Result *= X; } - return Result; + r64 Result = X; + for (u32 i = 1; i < Power; i++) { Result *= X; } + return Result; } internal u8 LerpU8(r32 T, u8 A, u8 B) { - return (u8)((A * (1.0f - T)) + (B * T)); + return (u8)((A * (1.0f - T)) + (B * T)); } internal u16 LerpU16(r32 T, u16 A, u16 B) { - return (u16)((A * (1.0f - T)) + (B * T)); + return (u16)((A * (1.0f - T)) + (B * T)); } internal u32 LerpU32(r32 T, u32 A, u32 B) { - return (u32)((A * (1.0f - T)) + (B * T)); + return (u32)((A * (1.0f - T)) + (B * T)); } internal u64 LerpU64(r32 T, u64 A, u64 B) { - return (u64)((A * (1.0f - T)) + (B * T)); + return (u64)((A * (1.0f - T)) + (B * T)); } internal s8 LerpS8(r32 T, s8 A, s8 B) { - return (s8)((A * (1.0f - T)) + (B * T)); + return (s8)((A * (1.0f - T)) + (B * T)); } internal s16 LerpS16(r32 T, s16 A, s16 B) { - return (s16)((A * (1.0f - T)) + (B * T)); + return (s16)((A * (1.0f - T)) + (B * T)); } internal s32 LerpS32(r32 T, s32 A, s32 B) { - return (s32)((A * (1.0f - T)) + (B * T)); + return (s32)((A * (1.0f - T)) + (B * T)); } internal s64 LerpS64(r32 T, s64 A, s64 B) { - return (s64)((A * (1.0f - T)) + (B * T)); + return (s64)((A * (1.0f - T)) + (B * T)); } internal r32 LerpR32(r32 T, r32 A, r32 B) { - return (r32)((A * (1.0f - T)) + (B * T)); + return (r32)((A * (1.0f - T)) + (B * T)); } internal r64 LerpR64(r32 T, r64 A, r64 B) { - return (r64)((A * (1.0f - T)) + (B * T)); + return (r64)((A * (1.0f - T)) + (B * T)); } internal u8 UnlerpU8(u8 Value, u8 Min, u8 Max) { - return (u8)((r64)(Value - Min) / (r64)(Max - Min)); + return (u8)((r64)(Value - Min) / (r64)(Max - Min)); } internal u16 UnlerpU16(u16 Value, u16 Min, u16 Max) { - return (u16)((r64)(Value - Min) / (r64)(Max - Min)); + return (u16)((r64)(Value - Min) / (r64)(Max - Min)); } internal u32 UnlerpU32(u32 Value, u32 Min, u32 Max) { - return (u32)((r64)(Value - Min) / (r64)(Max - Min)); + return (u32)((r64)(Value - Min) / (r64)(Max - Min)); } internal u64 UnlerpU64(u64 Value, u64 Min, u64 Max) { - return (u64)((r64)(Value - Min) / (r64)(Max - Min)); + return (u64)((r64)(Value - Min) / (r64)(Max - Min)); } internal s8 UnlerpS8(s8 Value, s8 Min, s8 Max) { - return (s8)((r64)(Value - Min) / (r64)(Max - Min)); + return (s8)((r64)(Value - Min) / (r64)(Max - Min)); } internal s16 UnlerpS16(s16 Value, s16 Min, s16 Max) { - return (s16)((r64)(Value - Min) / (r64)(Max - Min)); + return (s16)((r64)(Value - Min) / (r64)(Max - Min)); } internal s32 UnlerpS32(s32 Value, s32 Min, s32 Max) { - return (s32)((r64)(Value - Min) / (r64)(Max - Min)); + return (s32)((r64)(Value - Min) / (r64)(Max - Min)); } internal s64 UnlerpS64(s64 Value, s64 Min, s64 Max) { - return (s64)((r64)(Value - Min) / (r64)(Max - Min)); + return (s64)((r64)(Value - Min) / (r64)(Max - Min)); } internal r32 UnlerpR32(r32 Value, r32 Min, r32 Max) { - return (Value - Min) / (Max - Min); + return (Value - Min) / (Max - Min); } internal r64 UnlerpR64(r64 Value, r64 Min, r64 Max) { - return (Value - Min) / (Max - Min); + return (Value - Min) / (Max - Min); } internal u8 RemapU8(u8 Value, u8 OldMin, u8 OldMax, u8 NewMin, u8 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - u8 Result = (u8)((A * (NewMax - NewMin)) + NewMin); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + u8 Result = (u8)((A * (NewMax - NewMin)) + NewMin); + return Result; } internal u16 RemapU16(u16 Value, u16 OldMin, u16 OldMax, u16 NewMin, u16 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - u16 Result = (u16)((A * (NewMax - NewMin)) + NewMin); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + u16 Result = (u16)((A * (NewMax - NewMin)) + NewMin); + return Result; } internal u32 RemapU32(u32 Value, u32 OldMin, u32 OldMax, u32 NewMin, u32 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - u32 Result = (u32)((A * (NewMax - NewMin)) + NewMin); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + u32 Result = (u32)((A * (NewMax - NewMin)) + NewMin); + return Result; } internal u64 RemapU64(u64 Value, u64 OldMin, u64 OldMax, u64 NewMin, u64 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - u64 Result = (u64)((A * (NewMax - NewMin)) + NewMin); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + u64 Result = (u64)((A * (NewMax - NewMin)) + NewMin); + return Result; } internal s8 RemapS8(s8 Value, s8 OldMin, s8 OldMax, s8 NewMin, s8 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - s8 Result = (s8)((A * (NewMax - NewMin)) + NewMin); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + s8 Result = (s8)((A * (NewMax - NewMin)) + NewMin); + return Result; } internal s16 RemapS16(s16 Value, s16 OldMin, s16 OldMax, s16 NewMin, s16 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - s16 Result = (s16)((A * (NewMax - NewMin)) + NewMin); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + s16 Result = (s16)((A * (NewMax - NewMin)) + NewMin); + return Result; } internal s32 RemapS32(s32 Value, s32 OldMin, s32 OldMax, s32 NewMin, s32 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - s32 Result = (s32)((A * (NewMax - NewMin)) + NewMin); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + s32 Result = (s32)((A * (NewMax - NewMin)) + NewMin); + return Result; } internal s64 RemapS64(s64 Value, s64 OldMin, s64 OldMax, s64 NewMin, s64 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - s64 Result = (s64)((A * (NewMax - NewMin)) + NewMin); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + s64 Result = (s64)((A * (NewMax - NewMin)) + NewMin); + return Result; } internal r32 RemapR32(r32 Value, r32 OldMin, r32 OldMax, r32 NewMin, r32 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r32 Result = (r32)((A * (NewMax - NewMin)) + NewMin); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r32 Result = (r32)((A * (NewMax - NewMin)) + NewMin); + return Result; } internal r64 RemapR64(r64 Value, r64 OldMin, r64 OldMax, r64 NewMin, r64 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 Result = (r64)((A * (NewMax - NewMin)) + NewMin); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 Result = (r64)((A * (NewMax - NewMin)) + NewMin); + return Result; } internal u8 RemapClampedU8(u8 Value, u8 OldMin, u8 OldMax, u8 NewMin, u8 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 AClamped = Clamp01(A); - r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); - u8 Result = (u8)Clamp(NewMin, UnclampedResult, NewMax); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 AClamped = Clamp01(A); + r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); + u8 Result = (u8)Clamp(NewMin, UnclampedResult, NewMax); + return Result; } internal u16 RemapClampedU16(u16 Value, u16 OldMin, u16 OldMax, u16 NewMin, u16 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 AClamped = Clamp01(A); - r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); - u16 Result = (u16)Clamp(NewMin, UnclampedResult, NewMax); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 AClamped = Clamp01(A); + r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); + u16 Result = (u16)Clamp(NewMin, UnclampedResult, NewMax); + return Result; } internal u32 RemapClampedU32(u32 Value, u32 OldMin, u32 OldMax, u32 NewMin, u32 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 AClamped = Clamp01(A); - r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); - u32 Result = (u32)Clamp(NewMin, UnclampedResult, NewMax); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 AClamped = Clamp01(A); + r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); + u32 Result = (u32)Clamp(NewMin, UnclampedResult, NewMax); + return Result; } internal u64 RemapClampedU64(u64 Value, u64 OldMin, u64 OldMax, u64 NewMin, u64 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 AClamped = Clamp01(A); - r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); - u64 Result = (u64)Clamp(NewMin, UnclampedResult, NewMax); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 AClamped = Clamp01(A); + r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); + u64 Result = (u64)Clamp(NewMin, UnclampedResult, NewMax); + return Result; } internal s8 RemapClampedS8(s8 Value, s8 OldMin, s8 OldMax, s8 NewMin, s8 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 AClamped = Clamp01(A); - r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); - s8 Result = (s8)Clamp(NewMin, UnclampedResult, NewMax); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 AClamped = Clamp01(A); + r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); + s8 Result = (s8)Clamp(NewMin, UnclampedResult, NewMax); + return Result; } internal s16 RemapClampedS16(s16 Value, s16 OldMin, s16 OldMax, s16 NewMin, s16 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 AClamped = Clamp01(A); - r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); - s16 Result = (s16)Clamp(NewMin, UnclampedResult, NewMax); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 AClamped = Clamp01(A); + r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); + s16 Result = (s16)Clamp(NewMin, UnclampedResult, NewMax); + return Result; } internal s32 RemapClampedS32(s32 Value, s32 OldMin, s32 OldMax, s32 NewMin, s32 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 AClamped = Clamp01(A); - r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); - s32 Result = (s32)Clamp(NewMin, UnclampedResult, NewMax); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 AClamped = Clamp01(A); + r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); + s32 Result = (s32)Clamp(NewMin, UnclampedResult, NewMax); + return Result; } internal s64 RemapClampedS64(s64 Value, s64 OldMin, s64 OldMax, s64 NewMin, s64 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 AClamped = Clamp01(A); - r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); - s64 Result = (s64)Clamp(NewMin, UnclampedResult, NewMax); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 AClamped = Clamp01(A); + r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); + s64 Result = (s64)Clamp(NewMin, UnclampedResult, NewMax); + return Result; } internal r32 RemapClampedR32(r32 Value, r32 OldMin, r32 OldMax, r32 NewMin, r32 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 AClamped = Clamp01(A); - r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); - r32 Result = (r32)Clamp(NewMin, UnclampedResult, NewMax); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 AClamped = Clamp01(A); + r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); + r32 Result = (r32)Clamp(NewMin, UnclampedResult, NewMax); + return Result; } internal r64 RemapClampedR64(r64 Value, r64 OldMin, r64 OldMax, r64 NewMin, r64 NewMax) { - r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); - r64 AClamped = Clamp01(A); - r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); - r64 Result = (r64)Clamp(NewMin, UnclampedResult, NewMax); - return Result; + r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin); + r64 AClamped = Clamp01(A); + r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin); + r64 Result = (r64)Clamp(NewMin, UnclampedResult, NewMax); + return Result; } internal r32 FloorR32(r32 V) { - return (r32)((s64)V); + return (r32)((s64)V); } internal r64 FloorR64(r64 V) { - return (r64)((s64)V); + return (r64)((s64)V); } internal r32 FractR32(r32 V) { - return V - FloorR32(V); + return V - FloorR32(V); } internal r64 FractR64(r64 V) { - return V - FloorR64(V); + return V - FloorR64(V); } internal r32 SqrtR32(r32 V) { - return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(V))); + return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(V))); } internal u32 SqrtU32(u32 V) { - return sqrt(V); + return sqrt(V); } internal r32 ModR32(r32 Value, r32 Int) { - r32 Div = Value / Int; - r32 Fract = Abs(FractR32(Div)); - return Int * Fract; + r32 Div = Value / Int; + r32 Fract = Abs(FractR32(Div)); + return Int * Fract; } internal r64 ModR64(r64 Value, r64 Int) { - r64 Div = Value / Int; - r64 Fract = Abs(FractR64(Div)); - return Int * Fract; + r64 Div = Value / Int; + r64 Fract = Abs(FractR64(Div)); + return Int * Fract; } internal r32 SinR32(r32 Rad) { - return sinf(Rad); + return sinf(Rad); } internal r64 SinR64(r64 Rad) { - return sin(Rad); + return sin(Rad); } internal r32 CosR32(r32 Rad) { - return cosf(Rad); + return cosf(Rad); } internal r64 CosR64(r64 Rad) { - return cos(Rad); + return cos(Rad); } internal r32 TanR32(r32 Rad) { - return tanf(Rad); + return tanf(Rad); } internal r64 TanR64(r64 Rad) { - return tan(Rad); + return tan(Rad); } internal r32 ASinR32(r32 Rad) { - return asinf(Rad); + return asinf(Rad); } internal r64 ASinR64(r64 Rad) { - return asin(Rad); + return asin(Rad); } internal r32 ACosR32(r32 Rad) { - return acosf(Rad); + return acosf(Rad); } internal r64 ACosR64(r64 Rad) { - return acos(Rad); + return acos(Rad); } internal r32 ATanR32(r32 Rad) { - return atanf(Rad); + return atanf(Rad); } internal r64 ATanR64(r64 Rad) { - return atan(Rad); + return atan(Rad); } /////////////////////////// @@ -529,34 +529,34 @@ ATanR64(r64 Rad) internal v2 V2MultiplyPairwise(v2 A, v2 B) { - v2 Result = v2{ - A.x * B.x, - A.y * B.y, - }; - return Result; + v2 Result = v2{ + A.x * B.x, + A.y * B.y, + }; + return Result; } internal v3 V3MultiplyPairwise(v3 A, v3 B) { - v3 Result = v3{ - A.x * B.x, - A.y * B.y, - A.z * B.z, - }; - return Result; + v3 Result = v3{ + A.x * B.x, + A.y * B.y, + A.z * B.z, + }; + return Result; } internal v4 V4MultiplyPairwise(v4 A, v4 B) { - v4 Result = v4{ - A.x * B.x, - A.y * B.y, - A.z * B.z, - A.w * B.w, - }; - return Result; + v4 Result = v4{ + A.x * B.x, + A.y * B.y, + A.z * B.z, + A.w * B.w, + }; + return Result; } @@ -608,7 +608,7 @@ internal v3 ToV3(v2 V, r32 Z = 0) { return v3{V.x, V.y, Z}; } internal v4 V2ToV4(v2 V, r32 Z = 0, r32 W = 0) { return v4{V.x, V.y, Z, W}; } internal v4 ToV4_(v3 V, r32 W) { - return v4{V.x, V.y, V.z, W}; + return v4{V.x, V.y, V.z, W}; } #define ToV4Point(v) ToV4_((v), 1.0f) // all points have a w value of 1 #define ToV4Vec(v) ToV4_((v), 0.0f) // all vectors have a w value of 0 ie. they cannot be translated @@ -632,20 +632,20 @@ internal r32 V4Distance(v4 A, v4 B) { return V4Mag(A - B); } internal v2 V2Normalize(v2 A) { - r32 Magnitude = V2Mag(A); - return A / Magnitude; + r32 Magnitude = V2Mag(A); + return A / Magnitude; } internal v3 V3Normalize(v3 A) { - r32 Magnitude = V3Mag(A); - return A / Magnitude; + r32 Magnitude = V3Mag(A); + return A / Magnitude; } internal v4 V4Normalize(v4 A) { - r32 Magnitude = V4Mag(A); - return A / Magnitude; + r32 Magnitude = V4Mag(A); + return A / Magnitude; } internal r32 V2Dot(v2 A, v2 B) { return ((A.x * B.x) + (A.y * B.y)); } @@ -658,102 +658,102 @@ internal v2 V2PerpendicularCCW(v2 A) { return v2{A.y, A.x}; } internal r32 V2Cross(v2 A, v2 B) { - return ((A.x * B.y) - (A.y * B.x)); + return ((A.x * B.y) - (A.y * B.x)); } internal v3 V3Cross(v3 A, v3 B) { - v3 Result = { - (A.y * B.z) - (A.z * B.y), - (A.z * B.x) - (A.x * B.z), - (A.x * B.y) - (A.y * B.x) - }; - return Result; + v3 Result = { + (A.y * B.z) - (A.z * B.y), + (A.z * B.x) - (A.x * B.z), + (A.x * B.y) - (A.y * B.x) + }; + return Result; } internal v4 V4Cross(v4 A, v4 B) { - v4 Result = { - (A.y * B.z) - (A.z * B.y), - (A.z * B.x) - (A.x * B.z), - (A.x * B.y) - (A.y * B.x), - 0 - }; - return Result; + v4 Result = { + (A.y * B.z) - (A.z * B.y), + (A.z * B.x) - (A.x * B.z), + (A.x * B.y) - (A.y * B.x), + 0 + }; + return Result; } internal v2 V2Lerp(r32 T, v2 A, v2 B) { - v2 Result = v2{ - LerpR32(T, A.x, B.x), - LerpR32(T, A.y, B.y), - }; - return Result; + v2 Result = v2{ + LerpR32(T, A.x, B.x), + LerpR32(T, A.y, B.y), + }; + return Result; } internal v3 V3Lerp(r32 T, v3 A, v3 B) { - v3 Result = v3{ - LerpR32(T, A.x, B.x), - LerpR32(T, A.y, B.y), - LerpR32(T, A.z, B.z), - }; - return Result; + v3 Result = v3{ + LerpR32(T, A.x, B.x), + LerpR32(T, A.y, B.y), + LerpR32(T, A.z, B.z), + }; + return Result; } internal v4 V4Lerp(r32 T, v4 A, v4 B) { - v4 Result = v4{ - LerpR32(T, A.x, B.x), - LerpR32(T, A.y, B.y), - LerpR32(T, A.z, B.z), - LerpR32(T, A.w, B.w), - }; - return Result; + v4 Result = v4{ + LerpR32(T, A.x, B.x), + LerpR32(T, A.y, B.y), + LerpR32(T, A.z, B.z), + LerpR32(T, A.w, B.w), + }; + return Result; } internal v2 V2Remap(v2 P, v2 OldMin, v2 OldMax, v2 NewMin, v2 NewMax) { - v2 Result = {0}; - Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x); - Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y); - return Result; + v2 Result = {0}; + Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x); + Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y); + return Result; } internal v3 V3Remap(v3 P, v3 OldMin, v3 OldMax, v3 NewMin, v3 NewMax) { - v3 Result = {0}; - Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x); - Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y); - Result.z = RemapR32(P.z, OldMin.z, OldMax.z, NewMin.z, NewMax.z); - return Result; + v3 Result = {0}; + Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x); + Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y); + Result.z = RemapR32(P.z, OldMin.z, OldMax.z, NewMin.z, NewMax.z); + return Result; } internal v4 V4Remap(v4 P, v4 OldMin, v4 OldMax, v4 NewMin, v4 NewMax) { - v4 Result = {0}; - Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x); - Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y); - Result.z = RemapR32(P.z, OldMin.z, OldMax.z, NewMin.z, NewMax.z); - Result.w = RemapR32(P.w, OldMin.w, OldMax.w, NewMin.w, NewMax.w); - return Result; + v4 Result = {0}; + Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x); + Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y); + Result.z = RemapR32(P.z, OldMin.z, OldMax.z, NewMin.z, NewMax.z); + Result.w = RemapR32(P.w, OldMin.w, OldMax.w, NewMin.w, NewMax.w); + return Result; } internal v4 V4RemapAsV3(v4 P, v4 OldMin, v4 OldMax, v4 NewMin, v4 NewMax) { - v4 Result = {0}; - Result.xyz = V3Remap(P.xyz, OldMin.xyz, OldMax.xyz, NewMin.xyz, NewMax.xyz); - Result.w = P.w; - return Result; + v4 Result = {0}; + Result.xyz = V3Remap(P.xyz, OldMin.xyz, OldMax.xyz, NewMin.xyz, NewMax.xyz); + Result.w = P.w; + return Result; } /////////////////////////// @@ -762,47 +762,47 @@ V4RemapAsV3(v4 P, v4 OldMin, v4 OldMax, v4 NewMin, v4 NewMax) internal rect2 MakeRect2MinDim(v2 Min, v2 Dim) { - rect2 Result = {0}; - Result.Min = Min; - Result.Max = Min + Dim; - return Result; + rect2 Result = {0}; + Result.Min = Min; + Result.Max = Min + Dim; + return Result; } internal rect2 MakeRect2CenterDim(v2 Center, v2 Dim) { - v2 HalfDim = Dim / 2; - rect2 Result = {0}; - Result.Min = Center - HalfDim; - Result.Max = Center + HalfDim; - return Result; + v2 HalfDim = Dim / 2; + rect2 Result = {0}; + Result.Min = Center - HalfDim; + Result.Max = Center + HalfDim; + return Result; } internal b32 ValueInRangeR32(r32 Min, r32 Max, r32 V) { - return ((V >= Min) && (V <= Max)); + return ((V >= Min) && (V <= Max)); } internal b32 ValueInRange1(range1 Range, r32 V) { - return ValueInRangeR32(Range.Min, Range.Max, V); + return ValueInRangeR32(Range.Min, Range.Max, V); } internal b32 ValueInRange2(range2 Range, v2 V) { - return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) && - ValueInRangeR32(Range.Min.y, Range.Max.y, V.y)); + return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) && + ValueInRangeR32(Range.Min.y, Range.Max.y, V.y)); } internal b32 ValueInRange3(range3 Range, v3 V) { - return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) && - ValueInRangeR32(Range.Min.y, Range.Max.y, V.y) && - ValueInRangeR32(Range.Min.z, Range.Max.z, V.z)); + return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) && + ValueInRangeR32(Range.Min.y, Range.Max.y, V.y) && + ValueInRangeR32(Range.Min.z, Range.Max.z, V.z)); } internal b32 ValueInRange4(range4 Range, v4 V) { - return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) && - ValueInRangeR32(Range.Min.y, Range.Max.y, V.y) && - ValueInRangeR32(Range.Min.z, Range.Max.z, V.z) && - ValueInRangeR32(Range.Min.w, Range.Max.w, V.w)); + return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) && + ValueInRangeR32(Range.Min.y, Range.Max.y, V.y) && + ValueInRangeR32(Range.Min.z, Range.Max.z, V.z) && + ValueInRangeR32(Range.Min.w, Range.Max.w, V.w)); } #define PointIsInRect(range, point) ValueInRange2((range), (point)) @@ -843,19 +843,19 @@ internal range4 Range4Offset(range4 Range, v4 Delta) { return range4{ Range.Min internal v2 RectTopLeft(rect2 Rect) { - return v2{ Rect.Min.x, Rect.Max.y }; + return v2{ Rect.Min.x, Rect.Max.y }; } internal v2 RectTopRight(rect2 Rect) { - return Rect.Max; + return Rect.Max; } internal v2 RectBottomLeft(rect2 Rect) { - return Rect.Min; + return Rect.Min; } internal v2 RectBottomRight(rect2 Rect) { - return v2{ Rect.Max.x, Rect.Min.y }; + return v2{ Rect.Max.x, Rect.Min.y }; } internal r32 AspectRatio(r32 Width, r32 Height) { return Width / Height; } @@ -864,143 +864,143 @@ internal r32 RectAspectRatio(rect2 Rect) { return Range2SizeX(Rect) / Range2Size internal void RectHSplit(rect2 Rect, r32 YValue, rect2* Top, rect2* Bottom) { - r32 ClampedYValue = Clamp(Rect.Min.y, YValue, Rect.Max.y); - Top->Max = Rect.Max; - Top->Min = { Rect.Min.x, ClampedYValue }; - Bottom->Max = { Rect.Max.x, ClampedYValue }; - Bottom->Min = Rect.Min; + r32 ClampedYValue = Clamp(Rect.Min.y, YValue, Rect.Max.y); + Top->Max = Rect.Max; + Top->Min = { Rect.Min.x, ClampedYValue }; + Bottom->Max = { Rect.Max.x, ClampedYValue }; + Bottom->Min = Rect.Min; } internal void RectVSplit(rect2 Rect, r32 XValue, rect2* Left, rect2* Right) { - r32 ClampedXValue = Clamp(Rect.Min.x, XValue, Rect.Max.x); - Left->Max = { ClampedXValue, Rect.Max.y}; - Left->Min = Rect.Min; - Right->Max = Rect.Max; - Right->Min = { ClampedXValue, Rect.Min.y }; + r32 ClampedXValue = Clamp(Rect.Min.x, XValue, Rect.Max.x); + Left->Max = { ClampedXValue, Rect.Max.y}; + Left->Min = Rect.Min; + Right->Max = Rect.Max; + Right->Min = { ClampedXValue, Rect.Min.y }; } internal void RectHSplitAtDistanceFromTop(rect2 Rect, r32 YDist, rect2* Top, rect2* Bottom) { - RectHSplit(Rect, Rect.Max.y - YDist, Top, Bottom); + RectHSplit(Rect, Rect.Max.y - YDist, Top, Bottom); } internal void RectHSplitAtDistanceFromBottom(rect2 Rect, r32 YDist, rect2* Top, rect2* Bottom) { - RectHSplit(Rect, Rect.Min.y + YDist, Top, Bottom); + RectHSplit(Rect, Rect.Min.y + YDist, Top, Bottom); } internal void RectVSplitAtDistanceFromRight(rect2 Rect, r32 XDist, rect2* Left, rect2* Right) { - RectVSplit(Rect, Rect.Max.x - XDist, Left, Right); + RectVSplit(Rect, Rect.Max.x - XDist, Left, Right); } internal void RectVSplitAtDistanceFromLeft(rect2 Rect, r32 XDist, rect2* Left, rect2* Right) { - RectVSplit(Rect, Rect.Min.x + XDist, Left, Right); + RectVSplit(Rect, Rect.Min.x + XDist, Left, Right); } internal void RectHSplitAtPercent(rect2 Rect, r32 YPercent, rect2* Top, rect2* Bottom) { - RectHSplit(Rect, LerpR32(YPercent, Rect.Min.y, Rect.Max.y), Top, Bottom); + RectHSplit(Rect, LerpR32(YPercent, Rect.Min.y, Rect.Max.y), Top, Bottom); } internal void RectVSplitAtPercent(rect2 Rect, r32 XPercent, rect2* Left, rect2* Right) { - RectVSplit(Rect, LerpR32(XPercent, Rect.Min.x, Rect.Max.x), Left, Right); + RectVSplit(Rect, LerpR32(XPercent, Rect.Min.x, Rect.Max.x), Left, Right); } internal rect2 RectInset(rect2 Outer, v2 Amount) { - rect2 Result = { Outer.Min + Amount, Outer.Max - Amount }; - return Result; + rect2 Result = { Outer.Min + Amount, Outer.Max - Amount }; + return Result; } internal rect2 RectInset(rect2 Outer, r32 UniformAmount) { - return RectInset(Outer, v2{UniformAmount, UniformAmount}); + return RectInset(Outer, v2{UniformAmount, UniformAmount}); } internal range1 Range1Union(range1 A, range1 B) { - range1 Result = {}; - Result.Min = Max(A.Min, B.Min); - Result.Max = Min(A.Max, B.Max); - return Result; + range1 Result = {}; + Result.Min = Max(A.Min, B.Min); + Result.Max = Min(A.Max, B.Max); + return Result; } #define Rect2Union(a,b) Range2Union((a), (b)) internal range2 Range2Union(range2 A, range2 B) { - range2 Result = {}; - Result.Min.x = Max(A.Min.x, B.Min.x); - Result.Min.y = Max(A.Min.y, B.Min.y); - Result.Max.x = Min(A.Max.x, B.Max.x); - Result.Max.y = Min(A.Max.y, B.Max.y); - - if (Rect2Width(Result) < 0) { Result.Min.x = Result.Max.x; } - if (Rect2Height(Result) < 0) { Result.Min.y = Result.Max.y; } - - return Result; + range2 Result = {}; + Result.Min.x = Max(A.Min.x, B.Min.x); + Result.Min.y = Max(A.Min.y, B.Min.y); + Result.Max.x = Min(A.Max.x, B.Max.x); + Result.Max.y = Min(A.Max.y, B.Max.y); + + if (Rect2Width(Result) < 0) { Result.Min.x = Result.Max.x; } + if (Rect2Height(Result) < 0) { Result.Min.y = Result.Max.y; } + + return Result; } internal range3 Range3Union(range3 A, range3 B) { - range3 Result = {}; - Result.Min.x = Max(A.Min.x, B.Min.x); - Result.Min.y = Max(A.Min.y, B.Min.y); - Result.Min.z = Max(A.Min.z, B.Min.z); - Result.Max.x = Min(A.Max.x, B.Max.x); - Result.Max.y = Min(A.Max.y, B.Max.y); - Result.Max.z = Min(A.Max.z, B.Max.z); - return Result; + range3 Result = {}; + Result.Min.x = Max(A.Min.x, B.Min.x); + Result.Min.y = Max(A.Min.y, B.Min.y); + Result.Min.z = Max(A.Min.z, B.Min.z); + Result.Max.x = Min(A.Max.x, B.Max.x); + Result.Max.y = Min(A.Max.y, B.Max.y); + Result.Max.z = Min(A.Max.z, B.Max.z); + return Result; } internal v2 Rect2GetRectLocalPoint(rect2 Rect, v2 Point) { - v2 Result = Point - Rect.Min; - return Result; + v2 Result = Point - Rect.Min; + return Result; } internal r32 Rect2Area(rect2 Rect) { - r32 Result = Rect2Width(Rect) * Rect2Height(Rect); - return Result; + r32 Result = Rect2Width(Rect) * Rect2Height(Rect); + return Result; } internal v2 Rect2BottomLeft(rect2 Rect) { - v2 Result = Rect.Min; - return Result; + v2 Result = Rect.Min; + return Result; } internal v2 Rect2BottomRight(rect2 Rect) { - v2 Result = v2{ Rect.Max.x, Rect.Min.y }; - return Result; + v2 Result = v2{ Rect.Max.x, Rect.Min.y }; + return Result; } internal v2 Rect2TopRight(rect2 Rect) { - v2 Result = Rect.Max; - return Result; + v2 Result = Rect.Max; + return Result; } internal v2 Rect2TopLeft(rect2 Rect) { - v2 Result = v2{ Rect.Min.x, Rect.Max.y }; - return Result; + v2 Result = v2{ Rect.Min.x, Rect.Max.y }; + return Result; } /////////////////////////// @@ -1010,55 +1010,55 @@ Rect2TopLeft(rect2 Rect) internal v4 RayGetPointAlong(v4 RayOrigin, v4 RayDirection, r32 T) { - v4 Result = RayOrigin + (RayDirection * T); - return Result; + v4 Result = RayOrigin + (RayDirection * T); + return Result; } internal r32 RayPlaneIntersectionDistance(v4 RayOrigin, v4 RayDirection, v4 PlanePoint, v4 PlaneNormal) { - r32 T = 0.0f; - float Denominator = V4Dot(PlaneNormal, RayDirection); - if (Abs(Denominator) > 0.00001f) - { - T = V4Dot(PlanePoint - RayDirection, PlaneNormal) / Denominator; - } - return T; + r32 T = 0.0f; + float Denominator = V4Dot(PlaneNormal, RayDirection); + if (Abs(Denominator) > 0.00001f) + { + T = V4Dot(PlanePoint - RayDirection, PlaneNormal) / Denominator; + } + return T; } internal v4 GetRayPlaneIntersectionPoint(v4 RayOrigin, v4 RayDirection, v4 PlanePoint, v4 PlaneNormal) { - v4 Result = {0}; - r32 T = RayPlaneIntersectionDistance(RayOrigin, RayDirection, PlanePoint, PlaneNormal); - if (T >= 0) - { - Result = RayGetPointAlong(RayOrigin, RayDirection, T); - } - return Result; + v4 Result = {0}; + r32 T = RayPlaneIntersectionDistance(RayOrigin, RayDirection, PlanePoint, PlaneNormal); + if (T >= 0) + { + Result = RayGetPointAlong(RayOrigin, RayDirection, T); + } + return Result; } internal v4 GetRayPlaneIntersectionPoint(v4_ray Ray, v4 PlanePoint, v4 PlaneNormal) { - return GetRayPlaneIntersectionPoint(Ray.Origin, Ray.Direction, PlanePoint, PlaneNormal); + return GetRayPlaneIntersectionPoint(Ray.Origin, Ray.Direction, PlanePoint, PlaneNormal); } internal bool RayIntersectsPlane(v4 RayOrigin, v4 RayDirection, v4 PlanePoint, v4 PlaneNormal, v4* OutPoint) { - bool Result = false; - r32 T = RayPlaneIntersectionDistance(RayOrigin, RayDirection, PlanePoint, PlaneNormal); - if (T >= 0) - { - Result = true; - *OutPoint = RayGetPointAlong(RayOrigin, RayDirection, T); - } - return Result; + bool Result = false; + r32 T = RayPlaneIntersectionDistance(RayOrigin, RayDirection, PlanePoint, PlaneNormal); + if (T >= 0) + { + Result = true; + *OutPoint = RayGetPointAlong(RayOrigin, RayDirection, T); + } + return Result; } internal bool RayIntersectsPlane(v4_ray Ray, v4 PlanePoint, v4 PlaneNormal, v4* OutPoint) { - return RayIntersectsPlane(Ray.Origin, Ray.Direction, PlanePoint, PlaneNormal, OutPoint); + return RayIntersectsPlane(Ray.Origin, Ray.Direction, PlanePoint, PlaneNormal, OutPoint); } /////////////////////////// @@ -1068,286 +1068,286 @@ RayIntersectsPlane(v4_ray Ray, v4 PlanePoint, v4 PlaneNormal, v4* OutPoint) internal m44 M44Identity() { - m44 M = {0}; - M.AXx = 1.0f; - M.AYy = 1.0f; - M.AZz = 1.0f; - M.Tw = 1.0f; - return M; + m44 M = {0}; + M.AXx = 1.0f; + M.AYy = 1.0f; + M.AZz = 1.0f; + M.Tw = 1.0f; + return M; } internal m44 M44Transpose(m44 M) { - m44 Result = {0}; - for (u32 Y = 0; Y < 4; Y++) + m44 Result = {0}; + for (u32 Y = 0; Y < 4; Y++) + { + for (u32 X = 0; X < 4; X++) { - for (u32 X = 0; X < 4; X++) - { - Result.Array[(X * 4) + Y] = M.Array[(Y * 4) + X]; - } + Result.Array[(X * 4) + Y] = M.Array[(Y * 4) + X]; } - return Result; + } + return Result; } // Matrix * Matrix m44 operator* (m44 L, m44 R) { - m44 M = {0}; - - // ci ic ci ic ci ic i ic - M.AXx = (L.AXx * R.AXx) + (L.AYx * R.AXy) + (L.AZx * R.AXz) + (L.Tx * R.AXw); - M.AXy = (L.AXy * R.AXx) + (L.AYy * R.AXy) + (L.AZy * R.AXz) + (L.Ty * R.AXw); - M.AXz = (L.AXz * R.AXx) + (L.AYz * R.AXy) + (L.AZz * R.AXz) + (L.Tz * R.AXw); - M.AXw = (L.AXw * R.AXx) + (L.AYw * R.AXy) + (L.AZw * R.AXz) + (L.Tw * R.AXw); - - M.AYx = (L.AXx * R.AYx) + (L.AYx * R.AYy) + (L.AZx * R.AYz) + (L.Tx * R.AYw); - M.AYy = (L.AXy * R.AYx) + (L.AYy * R.AYy) + (L.AZy * R.AYz) + (L.Ty * R.AYw); - M.AYz = (L.AXz * R.AYx) + (L.AYz * R.AYy) + (L.AZz * R.AYz) + (L.Tz * R.AYw); - M.AYz = (L.AXw * R.AYx) + (L.AYw * R.AYy) + (L.AZw * R.AYz) + (L.Tw * R.AYw); - - M.AZx = (L.AXx * R.AZx) + (L.AYx * R.AZy) + (L.AZx * R.AZz) + (L.Tx * R.AZw); - M.AZy = (L.AXy * R.AZx) + (L.AYy * R.AZy) + (L.AZy * R.AZz) + (L.Ty * R.AZw); - M.AZz = (L.AXz * R.AZx) + (L.AYz * R.AZy) + (L.AZz * R.AZz) + (L.Tz * R.AZw); - M.AZw = (L.AXw * R.AZx) + (L.AYw * R.AZy) + (L.AZw * R.AZz) + (L.Tw * R.AZw); - - M.Tx = (L.AXx * R.Tx) + (L.AYx * R.Ty) + (L.AZx * R.Tz) + (L.Tx * R.Tw); - M.Ty = (L.AXy * R.Tx) + (L.AYy * R.Ty) + (L.AZy * R.Tz) + (L.Ty * R.Tw); - M.Tz = (L.AXz * R.Tx) + (L.AYz * R.Ty) + (L.AZz * R.Tz) + (L.Tz * R.Tw); - M.Tw = (L.AXw * R.Tx) + (L.AYw * R.Ty) + (L.AZw * R.Tz) + (L.Tw * R.Tw); - - return M; + m44 M = {0}; + + // ci ic ci ic ci ic i ic + M.AXx = (L.AXx * R.AXx) + (L.AYx * R.AXy) + (L.AZx * R.AXz) + (L.Tx * R.AXw); + M.AXy = (L.AXy * R.AXx) + (L.AYy * R.AXy) + (L.AZy * R.AXz) + (L.Ty * R.AXw); + M.AXz = (L.AXz * R.AXx) + (L.AYz * R.AXy) + (L.AZz * R.AXz) + (L.Tz * R.AXw); + M.AXw = (L.AXw * R.AXx) + (L.AYw * R.AXy) + (L.AZw * R.AXz) + (L.Tw * R.AXw); + + M.AYx = (L.AXx * R.AYx) + (L.AYx * R.AYy) + (L.AZx * R.AYz) + (L.Tx * R.AYw); + M.AYy = (L.AXy * R.AYx) + (L.AYy * R.AYy) + (L.AZy * R.AYz) + (L.Ty * R.AYw); + M.AYz = (L.AXz * R.AYx) + (L.AYz * R.AYy) + (L.AZz * R.AYz) + (L.Tz * R.AYw); + M.AYz = (L.AXw * R.AYx) + (L.AYw * R.AYy) + (L.AZw * R.AYz) + (L.Tw * R.AYw); + + M.AZx = (L.AXx * R.AZx) + (L.AYx * R.AZy) + (L.AZx * R.AZz) + (L.Tx * R.AZw); + M.AZy = (L.AXy * R.AZx) + (L.AYy * R.AZy) + (L.AZy * R.AZz) + (L.Ty * R.AZw); + M.AZz = (L.AXz * R.AZx) + (L.AYz * R.AZy) + (L.AZz * R.AZz) + (L.Tz * R.AZw); + M.AZw = (L.AXw * R.AZx) + (L.AYw * R.AZy) + (L.AZw * R.AZz) + (L.Tw * R.AZw); + + M.Tx = (L.AXx * R.Tx) + (L.AYx * R.Ty) + (L.AZx * R.Tz) + (L.Tx * R.Tw); + M.Ty = (L.AXy * R.Tx) + (L.AYy * R.Ty) + (L.AZy * R.Tz) + (L.Ty * R.Tw); + M.Tz = (L.AXz * R.Tx) + (L.AYz * R.Ty) + (L.AZz * R.Tz) + (L.Tz * R.Tw); + M.Tw = (L.AXw * R.Tx) + (L.AYw * R.Ty) + (L.AZw * R.Tz) + (L.Tw * R.Tw); + + return M; } // Matrix * Vector v4 operator* (m44 M, v4 V) { - v4 Result = {0}; - Result.x = (V.x * M.AXx) + (V.y * M.AYx) + (V.z * M.AZx) + (V.w * M.Tx); - Result.y = (V.x * M.AXy) + (V.y * M.AYy) + (V.z * M.AZy) + (V.w * M.Ty); - Result.z = (V.x * M.AXz) + (V.y * M.AYz) + (V.z * M.AZz) + (V.w * M.Tz); - Result.w = (V.x * M.AXw) + (V.y * M.AYw) + (V.z * M.AZw) + (V.w * M.Tw); - return Result; + v4 Result = {0}; + Result.x = (V.x * M.AXx) + (V.y * M.AYx) + (V.z * M.AZx) + (V.w * M.Tx); + Result.y = (V.x * M.AXy) + (V.y * M.AYy) + (V.z * M.AZy) + (V.w * M.Ty); + Result.z = (V.x * M.AXz) + (V.y * M.AYz) + (V.z * M.AZz) + (V.w * M.Tz); + Result.w = (V.x * M.AXw) + (V.y * M.AYw) + (V.z * M.AZw) + (V.w * M.Tw); + return Result; } internal m44 M44Translation(v4 Offset) { - m44 Result = M44Identity(); - Result.Tx = Offset.x; - Result.Ty = Offset.y; - Result.Tz = Offset.z; - return Result; + m44 Result = M44Identity(); + Result.Tx = Offset.x; + Result.Ty = Offset.y; + Result.Tz = Offset.z; + return Result; } internal m44 M44RotationX(r32 Radians) { - r32 CosRad = CosR32(Radians); - r32 SinRad = SinR32(Radians); - m44 Result = M44Identity(); - Result.AYy = CosRad; - Result.AZy = SinRad; - Result.AYz = -SinRad; - Result.AZz = CosRad; - return Result; + r32 CosRad = CosR32(Radians); + r32 SinRad = SinR32(Radians); + m44 Result = M44Identity(); + Result.AYy = CosRad; + Result.AZy = SinRad; + Result.AYz = -SinRad; + Result.AZz = CosRad; + return Result; } internal m44 M44RotationY(r32 Radians) { - r32 CosRad = CosR32(Radians); - r32 SinRad = SinR32(Radians); - m44 Result = M44Identity(); - Result.AXx = CosRad; - Result.AZx = SinRad; - Result.AXz = -SinRad; - Result.AZz = CosRad; - return Result; + r32 CosRad = CosR32(Radians); + r32 SinRad = SinR32(Radians); + m44 Result = M44Identity(); + Result.AXx = CosRad; + Result.AZx = SinRad; + Result.AXz = -SinRad; + Result.AZz = CosRad; + return Result; } internal m44 M44RotationZ(r32 Radians) { - r32 CosRad = CosR32(Radians); - r32 SinRad = SinR32(Radians); - m44 Result = M44Identity(); - Result.AXx = CosRad; - Result.AYx = -SinRad; - Result.AXy = SinRad; - Result.AYy = CosRad; - return Result; + r32 CosRad = CosR32(Radians); + r32 SinRad = SinR32(Radians); + m44 Result = M44Identity(); + Result.AXx = CosRad; + Result.AYx = -SinRad; + Result.AXy = SinRad; + Result.AYy = CosRad; + return Result; } internal m44 M44Rotation(v3 Radians) { - r32 CosX = CosR32(Radians.x); - r32 SinX = SinR32(Radians.x); - r32 CosY = CosR32(Radians.y); - r32 SinY = SinR32(Radians.y); - r32 CosZ = CosR32(Radians.z); - r32 SinZ = SinR32(Radians.z); - - m44 Result = {0}; - Result.AXx = CosY * CosZ; - Result.AXy = -(SinX * SinY * CosZ) + (CosX * SinZ); - Result.AXz = -(CosX * SinY * CosZ) - (SinX * SinZ); - Result.AXw = 0; - - Result.AYx = -(SinZ * CosY); - Result.AYy = (SinX * SinY * SinZ) + (CosX * CosZ); - Result.AYz = (CosX * SinY * SinZ) - (SinX * CosZ); - Result.AYw = 0; - - Result.AZx = SinY; - Result.AZy = SinX * CosY; - Result.AZz = CosX * CosY; - Result.AZw = 0; - - Result.Tx = 0; - Result.Ty = 0; - Result.Tz = 0; - Result.Tw = 1; - - return Result; + r32 CosX = CosR32(Radians.x); + r32 SinX = SinR32(Radians.x); + r32 CosY = CosR32(Radians.y); + r32 SinY = SinR32(Radians.y); + r32 CosZ = CosR32(Radians.z); + r32 SinZ = SinR32(Radians.z); + + m44 Result = {0}; + Result.AXx = CosY * CosZ; + Result.AXy = -(SinX * SinY * CosZ) + (CosX * SinZ); + Result.AXz = -(CosX * SinY * CosZ) - (SinX * SinZ); + Result.AXw = 0; + + Result.AYx = -(SinZ * CosY); + Result.AYy = (SinX * SinY * SinZ) + (CosX * CosZ); + Result.AYz = (CosX * SinY * SinZ) - (SinX * CosZ); + Result.AYw = 0; + + Result.AZx = SinY; + Result.AZy = SinX * CosY; + Result.AZz = CosX * CosY; + Result.AZw = 0; + + Result.Tx = 0; + Result.Ty = 0; + Result.Tz = 0; + Result.Tw = 1; + + return Result; } internal m44 M44Scale(v3 Scale) { - m44 Result = M44Identity(); - Result.AXx = Scale.x; - Result.AYy = Scale.y; - Result.AZz = Scale.z; - return Result; + m44 Result = M44Identity(); + Result.AXx = Scale.x; + Result.AYy = Scale.y; + Result.AZz = Scale.z; + return Result; } internal m44 M44ScaleUniform(r32 Scale) { - m44 Result = M44Identity(); - Result.AXx = Scale; - Result.AYy = Scale; - Result.AZz = Scale; - return Result; + m44 Result = M44Identity(); + Result.AXx = Scale; + Result.AYy = Scale; + Result.AZz = Scale; + return Result; } internal m44 M44CoordinateFrame(v4 Forward, v4 Right, v4 Up) { - m44 Result = {0}; - Result.AXx = Right.x; - Result.AYx = Right.y; - Result.AZx = Right.z; - Result.Tx = Right.w; - - Result.AXy = Up.x; - Result.AYy = Up.y; - Result.AZy = Up.z; - Result.Ty = Up.w; - - Result.AXz = Forward.x; - Result.AYz = Forward.y; - Result.AZz = Forward.z; - Result.Tz = Forward.w; - - Result.Tw = 1.0f; - return Result; + m44 Result = {0}; + Result.AXx = Right.x; + Result.AYx = Right.y; + Result.AZx = Right.z; + Result.Tx = Right.w; + + Result.AXy = Up.x; + Result.AYy = Up.y; + Result.AZy = Up.z; + Result.Ty = Up.w; + + Result.AXz = Forward.x; + Result.AYz = Forward.y; + Result.AZz = Forward.z; + Result.Tz = Forward.w; + + Result.Tw = 1.0f; + return Result; } internal m44 M44ModelMatrix(v4 Forward, v4 Right, v4 Up, v4 Position) { - m44 RotationMatrix = M44CoordinateFrame(Forward, Right, Up); - m44 PositionMatrix = M44Translation(-Position); - m44 ModelViewMatrix = PositionMatrix * RotationMatrix; - return ModelViewMatrix; + m44 RotationMatrix = M44CoordinateFrame(Forward, Right, Up); + m44 PositionMatrix = M44Translation(-Position); + m44 ModelViewMatrix = PositionMatrix * RotationMatrix; + return ModelViewMatrix; } internal m44 M44ProjectionOrtho(r32 Width, r32 Height, r32 Near, r32 Far, r32 Right, r32 Left, r32 Top, r32 Bottom) { - m44 Result = {0}; - Result.AXx = 2.0f / Width; - Result.AYy = 2.0f / Height; - Result.AZz = 2.0f / (Near - Far); - Result.AXw = -(Right + Left) / (Right - Left); - Result.AYw = -(Top + Bottom) / (Top - Bottom); - Result.AZw = -(Far + Near) / (Far - Near); - Result.Tw = 1; - return Result; + m44 Result = {0}; + Result.AXx = 2.0f / Width; + Result.AYy = 2.0f / Height; + Result.AZz = 2.0f / (Near - Far); + Result.AXw = -(Right + Left) / (Right - Left); + Result.AYw = -(Top + Bottom) / (Top - Bottom); + Result.AZw = -(Far + Near) / (Far - Near); + Result.Tw = 1; + return Result; } internal m44 M44ProjectionOrtho(r32 Aspect, r32 Scale, r32 Near, r32 Far) { - m44 Result = {0}; - r32 Width = Scale * Aspect; - r32 Height = Scale; - r32 Right = Width / 2.0f; - r32 Left = -Right; - r32 Top = Height / 2.0f; - r32 Bottom = -Top; - Result = M44ProjectionOrtho(Width, Height, Near, Far, Right, Left, Top, Bottom); - return Result; + m44 Result = {0}; + r32 Width = Scale * Aspect; + r32 Height = Scale; + r32 Right = Width / 2.0f; + r32 Left = -Right; + r32 Top = Height / 2.0f; + r32 Bottom = -Top; + Result = M44ProjectionOrtho(Width, Height, Near, Far, Right, Left, Top, Bottom); + return Result; } internal m44 M44ProjectionInterfaceOrtho(r32 Width, r32 Height, r32 Near, r32 Far) { - m44 Result = {0}; - r32 Aspect = Width / Height; - r32 Right = Width; - r32 Left = 0; - r32 Top = Height; - r32 Bottom = 0; - Result = M44ProjectionOrtho(Width, Height, Near, Far, Right, Left, Top, Bottom); - return Result; + m44 Result = {0}; + r32 Aspect = Width / Height; + r32 Right = Width; + r32 Left = 0; + r32 Top = Height; + r32 Bottom = 0; + Result = M44ProjectionOrtho(Width, Height, Near, Far, Right, Left, Top, Bottom); + return Result; } internal m44 M44ProjectionPerspective(r32 FieldOfViewDegrees, r32 AspectRatio, r32 Near, r32 Far) { - m44 Result = M44Identity(); - - // The perspective divide step involves dividing x and y by -z - // Making Tz = -1 will make Tw of the result = -z - Result.Tw = 0; - Result.AZw = -1; - - // Remap z' from the range [near clip : far clip] to [0 : 1] - r32 ViewRange = Far - Near; - Result.AZz = -((Far + Near) / ViewRange); - Result.Tz = -(2 * Near * Far) / ViewRange; - - // Adjust for field of view - adjust the x' and y coordinates based - // on how - r32 FovBasedScale = TanR32(DegToRadR32(FieldOfViewDegrees / 2)); - r32 Top = Near * FovBasedScale; - r32 Bottom = -Top; - r32 Right = Top * AspectRatio; - r32 Left = -Right; - Result.AXx = (2 * Near) / (Right - Left); - Result.AZx = (Right + Left) / (Right - Left); - Result.AYy = (2 * Near) / (Top - Bottom); - Result.AZy = (Top + Bottom) / (Top - Bottom); - - return Result; + m44 Result = M44Identity(); + + // The perspective divide step involves dividing x and y by -z + // Making Tz = -1 will make Tw of the result = -z + Result.Tw = 0; + Result.AZw = -1; + + // Remap z' from the range [near clip : far clip] to [0 : 1] + r32 ViewRange = Far - Near; + Result.AZz = -((Far + Near) / ViewRange); + Result.Tz = -(2 * Near * Far) / ViewRange; + + // Adjust for field of view - adjust the x' and y coordinates based + // on how + r32 FovBasedScale = TanR32(DegToRadR32(FieldOfViewDegrees / 2)); + r32 Top = Near * FovBasedScale; + r32 Bottom = -Top; + r32 Right = Top * AspectRatio; + r32 Left = -Right; + Result.AXx = (2 * Near) / (Right - Left); + Result.AZx = (Right + Left) / (Right - Left); + Result.AYy = (2 * Near) / (Top - Bottom); + Result.AZy = (Top + Bottom) / (Top - Bottom); + + return Result; } internal m44 M44LookAt(v4 Position, v4 Target) { - // NOTE(Peter): the camera usually points along the -z axis, hence - // Forward = a ray that points from the target back towards your position - v4 Forward = V4Normalize(Position - Target); - v4 Right = V4Normalize(V4Cross(v4{0, 1, 0, 0}, Forward)); - v4 Up = V4Normalize(V4Cross(Forward, Right)); - m44 Result = M44CoordinateFrame(Forward, Right, Up); - return Result; + // NOTE(Peter): the camera usually points along the -z axis, hence + // Forward = a ray that points from the target back towards your position + v4 Forward = V4Normalize(Position - Target); + v4 Right = V4Normalize(V4Cross(v4{0, 1, 0, 0}, Forward)); + v4 Up = V4Normalize(V4Cross(Forward, Right)); + m44 Result = M44CoordinateFrame(Forward, Right, Up); + return Result; } /////////////////////////// @@ -1358,44 +1358,44 @@ internal gs_const_string ConstString(char* Data, u64 Length) { return gs_const_s internal gs_const_string ConstString(char* Data) { return gs_const_string{Data, CStringLength(Data)}; } internal gs_string MakeString(char* Data, u64 Length, u64 Size) { - Assert(Length <= Size); - gs_string Result = {0}; - Result.Str = Data; - Result.Length = Length; - Result.Size = Size; - return Result; + Assert(Length <= Size); + gs_string Result = {0}; + Result.Str = Data; + Result.Length = Length; + Result.Size = Size; + return Result; } internal gs_string MakeString(char* Data, u64 Length) { - return MakeString(Data, Length, Length); + return MakeString(Data, Length, Length); } internal gs_string MakeString(char* Data) { - u64 StringLength = CStringLength(Data); - return MakeString(Data, StringLength, StringLength); + u64 StringLength = CStringLength(Data); + return MakeString(Data, StringLength, StringLength); } internal gs_string MakeString(gs_const_string ConstString) { - return MakeString(ConstString.Str, ConstString.Length); + return MakeString(ConstString.Str, ConstString.Length); } internal gs_data StringToData(gs_const_string String) { - gs_data Result = gs_data{0}; - Result.Memory = (u8*)String.Str; - Result.Size = String.Length * sizeof(char); - return Result; + gs_data Result = gs_data{0}; + Result.Memory = (u8*)String.Str; + Result.Size = String.Length * sizeof(char); + return Result; } internal gs_data StringToData(gs_string String) { - return StringToData(String.ConstString); + return StringToData(String.ConstString); } internal gs_const_string DataToString(gs_data Data) { - gs_const_string Result = {}; - Result.Str = (char*)Data.Memory; - Result.Length = Data.Size; - return Result; + gs_const_string Result = {}; + Result.Str = (char*)Data.Memory; + Result.Length = Data.Size; + return Result; } internal bool IsSlash(char C) { return ((C == '/') || (C == '\\')); } @@ -1412,1002 +1412,1002 @@ internal bool IsNumericExtended(char C) { return IsNumericDecimal(C) || (C == 'x internal bool IsAlpha(char C) { return( (('a' <= C) && (C <= 'z')) || (('A' <= C) && (C <= 'Z')) || C == '_'); } internal bool IsAlphaNumeric(char C) { return((('a' <= C) && (C <= 'z')) || (('A' <= C) && (C <= 'Z')) || (('0' <= C) && (C <= '9')) || C == '_'); } internal bool IsOperator(char C) { - return ((C == '+') || (C == '-') || (C == '*') || (C == '/') || - (C == '=') || (C == '%') || (C == '<') || (C == '>')); + return ((C == '+') || (C == '-') || (C == '*') || (C == '/') || + (C == '=') || (C == '%') || (C == '<') || (C == '>')); } internal char ToUpper(char C) { - if ((C >= 'a') && (C <= 'z')) - { - C -= 'a' - 'A'; - } - return C; + if ((C >= 'a') && (C <= 'z')) + { + C -= 'a' - 'A'; + } + return C; } internal char ToLower(char C) { - if ((C >= 'A') && (C <= 'Z')) - { - C += 'a' - 'A'; - } - return C; + if ((C >= 'A') && (C <= 'Z')) + { + C += 'a' - 'A'; + } + return C; } internal bool CharsEqualCaseInsensitive(char A, char B) { return ToLower(A) == ToLower(B); } internal u64 CharArrayLength (char* CS) { - char* At = CS; - while (*At) { At++; } - return (u64)(At - CS); + char* At = CS; + while (*At) { At++; } + return (u64)(At - CS); } internal bool IsNullTerminated(gs_const_string String) { - bool Result = false; - if (String.Str) - { - Result = (String.Str[String.Length] == 0); - } - return Result; + bool Result = false; + if (String.Str) + { + Result = (String.Str[String.Length] == 0); + } + return Result; } internal bool IsNullTerminated(gs_string String) { - return IsNullTerminated(String.ConstString); + return IsNullTerminated(String.ConstString); } internal char GetChar(gs_const_string String, u64 I) { - char Result = 0; - if (I < String.Length) - { - Result = String.Str[I]; - } - return Result; + char Result = 0; + if (I < String.Length) + { + Result = String.Str[I]; + } + return Result; } internal char GetChar(gs_string String, u64 I) { - char Result = 0; - if (I < String.Length) - { - Result = String.Str[I]; - } - return Result; + char Result = 0; + if (I < String.Length) + { + Result = String.Str[I]; + } + return Result; } internal gs_const_string GetStringPrefix(gs_const_string String, u64 Size) { - gs_const_string Result = String; - Result.Length = Min(Size, String.Length); - return Result; + gs_const_string Result = String; + Result.Length = Min(Size, String.Length); + return Result; } internal gs_const_string GetStringPostfix(gs_const_string String, u64 Size) { - gs_const_string Result = String; - u64 PostfixSize = Min(Size, String.Length); - Result.Str += (Result.Length - PostfixSize); - Result.Length = PostfixSize; - return Result; + gs_const_string Result = String; + u64 PostfixSize = Min(Size, String.Length); + Result.Str += (Result.Length - PostfixSize); + Result.Length = PostfixSize; + return Result; } internal gs_const_string GetStringAfter(gs_const_string String, u64 Cut) { - gs_const_string Result = String; - u64 CutSize = Min(Cut, String.Length); - Result.Str += CutSize; - Result.Length -= CutSize; - return Result; + gs_const_string Result = String; + u64 CutSize = Min(Cut, String.Length); + Result.Str += CutSize; + Result.Length -= CutSize; + return Result; } internal gs_string GetStringAfter(gs_string String, u64 Cut) { - gs_string Result = {0}; - Result.ConstString = GetStringAfter(String.ConstString, Cut); - Result.Size = String.Size - Cut; - return Result; + gs_string Result = {0}; + Result.ConstString = GetStringAfter(String.ConstString, Cut); + Result.Size = String.Size - Cut; + return Result; } internal gs_const_string GetStringBefore(gs_const_string String, u64 Cut) { - gs_const_string Result = String; - Result.Length = Min(Cut, String.Length); - return Result; + gs_const_string Result = String; + Result.Length = Min(Cut, String.Length); + return Result; } internal gs_const_string Substring(gs_const_string String, u64 First, u64 Last) { - gs_const_string Result = {0}; - Result.Str = String.Str + Min(First, String.Length); - Result.Length = Min(Last - First, String.Length); - return Result; + gs_const_string Result = {0}; + Result.Str = String.Str + Min(First, String.Length); + Result.Length = Min(Last - First, String.Length); + return Result; } internal gs_const_string Substring(gs_string String, u64 First, u64 Last) { - return Substring(String.ConstString, First, Last); + return Substring(String.ConstString, First, Last); } internal s64 FindFirst(gs_const_string String, u64 StartIndex, char C) { - s64 Result = -1; - for(u64 i = StartIndex; i < String.Length; i++) - { - if (String.Str[i] == C) { - Result = (s64)i; - break; - } + s64 Result = -1; + for(u64 i = StartIndex; i < String.Length; i++) + { + if (String.Str[i] == C) { + Result = (s64)i; + break; } - return Result; + } + return Result; } internal s64 FindFirst(gs_const_string String, char C) { - return FindFirst(String, 0, C); + return FindFirst(String, 0, C); } internal s64 FindFirst(gs_string String, u64 StartIndex, char C) { - return FindFirst(String.ConstString, StartIndex, C); + return FindFirst(String.ConstString, StartIndex, C); } internal s64 FindFirst(gs_string String, char C) { - return FindFirst(String.ConstString, 0, C); + return FindFirst(String.ConstString, 0, C); } internal s64 FindLast(char* String, s64 StartIndex, char C) { - s64 Result = -1; - s64 i = 0; - while (String[i] != 0 && i < StartIndex) + s64 Result = -1; + s64 i = 0; + while (String[i] != 0 && i < StartIndex) + { + i++; + } + while (String[i]) + { + if (String[i] == C) { - i++; + Result = i; } - while (String[i]) - { - if (String[i] == C) - { - Result = i; - } - i++; - } - return Result; + i++; + } + return Result; } internal s64 FindLast(gs_const_string String, u64 StartIndex, char C) { - s64 Result = -1; - for(s64 i= StartIndex; i >= 0; i--) - { - if (String.Str[i] == C) { - Result = i; - break; - } + s64 Result = -1; + for(s64 i= StartIndex; i >= 0; i--) + { + if (String.Str[i] == C) { + Result = i; + break; } - return (u64)Result; + } + return (u64)Result; } internal s64 FindLast(gs_const_string String, char C) { - return FindLast(String, String.Length - 1, C); + return FindLast(String, String.Length - 1, C); } internal s64 FindLast(gs_string String, u64 StartIndex, char C) { - return FindLast(String.ConstString, StartIndex, C); + return FindLast(String.ConstString, StartIndex, C); } internal s64 FindLast(gs_string String, char C) { - return FindLast(String.ConstString, String.Length - 1, C); + return FindLast(String.ConstString, String.Length - 1, C); } internal s64 FindFirstFromSet(gs_const_string String, char* SetArray) { - gs_const_string Set = ConstString(SetArray); - s64 Result = -1; - - s64 CurrMin = String.Length; - for (u64 SetAt = 0; SetAt < Set.Length; SetAt++) + gs_const_string Set = ConstString(SetArray); + s64 Result = -1; + + s64 CurrMin = String.Length; + for (u64 SetAt = 0; SetAt < Set.Length; SetAt++) + { + s64 Index = FindFirst(String, Set.Str[SetAt]); + if (Index >= 0 && Index < CurrMin) { - s64 Index = FindFirst(String, Set.Str[SetAt]); - if (Index >= 0 && Index < CurrMin) - { - CurrMin = Index; - } + CurrMin = Index; } - - if (CurrMin < (s64)String.Length) - { - Result = CurrMin; - } - - return Result; + } + + if (CurrMin < (s64)String.Length) + { + Result = CurrMin; + } + + return Result; } internal s64 FindLastFromSet(gs_const_string String, char* SetArray) { - gs_const_string Set = ConstString(SetArray); - s64 Result = -1; - for(s64 At = String.Length - 1; At >= 0; At--) + gs_const_string Set = ConstString(SetArray); + s64 Result = -1; + for(s64 At = String.Length - 1; At >= 0; At--) + { + char CharAt = String.Str[At]; + for (u64 SetAt = 0; SetAt < Set.Length; SetAt++) { - char CharAt = String.Str[At]; - for (u64 SetAt = 0; SetAt < Set.Length; SetAt++) - { - if (CharAt == Set.Str[SetAt]) - { - Result = (u64)At; - // NOTE(Peter): The alternative to this goto is a break in the inner loop - // followed by an if check in the outer loop, that must be evaluated - // every character you check. This is more efficient - goto find_first_from_set_complete; - } - } + if (CharAt == Set.Str[SetAt]) + { + Result = (u64)At; + // NOTE(Peter): The alternative to this goto is a break in the inner loop + // followed by an if check in the outer loop, that must be evaluated + // every character you check. This is more efficient + goto find_first_from_set_complete; + } } - find_first_from_set_complete: - return Result; + } + find_first_from_set_complete: + return Result; } internal bool StringContains(gs_const_string Str, char C) { - bool Result = false; - for (u32 i = 0; i < Str.Length; i++) + bool Result = false; + for (u32 i = 0; i < Str.Length; i++) + { + if (Str.Str[i] == C) { - if (Str.Str[i] == C) - { - Result = true; - break; - } + Result = true; + break; } - return Result; + } + return Result; } internal bool StringsEqualUpToLength(gs_const_string A, gs_const_string B, u64 Length) { - bool Result = false; - if (A.Length >= Length && B.Length >= Length) + bool Result = false; + if (A.Length >= Length && B.Length >= Length) + { + Result = true; + Length = Min(Length, A.Length); + for (u64 i = 0; i < Length; i++) { - Result = true; - Length = Min(Length, A.Length); - for (u64 i = 0; i < Length; i++) - { - if (A.Str[i] != B.Str[i]) - { - Result = false; - break; - } - } + if (A.Str[i] != B.Str[i]) + { + Result = false; + break; + } } - return Result; + } + return Result; } internal bool StringsEqual(gs_const_string A, gs_const_string B) { - bool Result = false; - if (A.Length == B.Length) - { - Result = StringsEqualUpToLength(A, B, A.Length); - } - return Result; + bool Result = false; + if (A.Length == B.Length) + { + Result = StringsEqualUpToLength(A, B, A.Length); + } + return Result; } internal bool StringEqualsCharArray(gs_const_string A, char* B, u64 Length) { - gs_const_string BStr = ConstString(B, Length); - return StringsEqual(A, BStr); + gs_const_string BStr = ConstString(B, Length); + return StringsEqual(A, BStr); } internal bool StringEqualsCharArray(gs_const_string A, char* B) { - u64 Length = CStringLength(B); - return StringEqualsCharArray(A, B, Length); + u64 Length = CStringLength(B); + return StringEqualsCharArray(A, B, Length); } internal bool StringsEqualUpToLength(gs_string A, gs_string B, u64 Length) { - return StringsEqualUpToLength(A.ConstString, B.ConstString, Length); + return StringsEqualUpToLength(A.ConstString, B.ConstString, Length); } internal bool StringsEqual(gs_string A, gs_string B) { - return StringsEqual(A.ConstString, B.ConstString); + return StringsEqual(A.ConstString, B.ConstString); } internal bool StringEqualsCharArray(gs_string A, char* B, u64 Length) { - return StringEqualsCharArray(A.ConstString, B, Length); + return StringEqualsCharArray(A.ConstString, B, Length); } internal bool StringEqualsCharArray(gs_string A, char* B) { - return StringEqualsCharArray(A.ConstString, B); + return StringEqualsCharArray(A.ConstString, B); } internal u64 StringSizeLeft(gs_string String) { - u64 Result = String.Size - String.Length; - return Result; + u64 Result = String.Size - String.Length; + return Result; } internal void ReverseStringInPlace(gs_string* String) { - char* Start = String->Str; - char* End = String->Str + String->Length; - while (Start < End) - { - End--; - char Temp = End[0]; - End[0] = Start[0]; - Start[0] = Temp; - Start++; - } + char* Start = String->Str; + char* End = String->Str + String->Length; + while (Start < End) + { + End--; + char Temp = End[0]; + End[0] = Start[0]; + Start[0] = Temp; + Start++; + } } internal gs_const_string GetCharSetForBase(u64 Base) { - gs_const_string Result = {0}; - switch(Base) - { - case 8: { Result = Base8Chars; }break; - case 10: { Result = Base10Chars; }break; - case 16: { Result = Base16Chars; }break; - InvalidDefaultCase; - } - return Result; + gs_const_string Result = {0}; + switch(Base) + { + case 8: { Result = Base8Chars; }break; + case 10: { Result = Base10Chars; }break; + case 16: { Result = Base16Chars; }break; + InvalidDefaultCase; + } + return Result; } internal u64 CharToUInt(char C, gs_const_string CharSet) { - return (u64)FindFirst(CharSet, C); + return (u64)FindFirst(CharSet, C); } internal u64 CharToUInt(char C) { - return (u64)CharToUInt(C, Base10Chars); + return (u64)CharToUInt(C, Base10Chars); } internal u64 CharToUInt(char C, u64 Base) { - return CharToUInt(C, GetCharSetForBase(Base)); + return CharToUInt(C, GetCharSetForBase(Base)); } struct parse_uint_result { - b8 Success; - u64 Value; - u32 ParsedLength; + b8 Success; + u64 Value; + u32 ParsedLength; }; internal parse_uint_result ValidateAndParseUInt(gs_const_string String, u64 Base = 10) { - parse_uint_result Result = {0}; - - gs_const_string CharSet = GetCharSetForBase(Base); - - bool StringIsValid = true; - for (u32 i = 0; i < String.Length; i++) + parse_uint_result Result = {0}; + + gs_const_string CharSet = GetCharSetForBase(Base); + + bool StringIsValid = true; + for (u32 i = 0; i < String.Length; i++) + { + if (!StringContains(CharSet, String.Str[i])) { - if (!StringContains(CharSet, String.Str[i])) - { - StringIsValid = false; - break; - } + StringIsValid = false; + break; + } + } + + if (StringIsValid) + { + u64 Acc = 0; + u64 i = 0; + for (; i < String.Length; i++) + { + u64 CharIndex = FindFirst(CharSet, String.Str[i]); + if (CharIndex < CharSet.Length) + { + Acc = CharToUInt(String.Str[i], CharSet) + (Acc * Base); + } + else + { + break; + } } - if (StringIsValid) - { - u64 Acc = 0; - u64 i = 0; - for (; i < String.Length; i++) - { - u64 CharIndex = FindFirst(CharSet, String.Str[i]); - if (CharIndex < CharSet.Length) - { - Acc = CharToUInt(String.Str[i], CharSet) + (Acc * Base); - } - else - { - break; - } - } - - Result.Success = true; - Result.Value = Acc; - Result.ParsedLength = i; - } - - return Result; + Result.Success = true; + Result.Value = Acc; + Result.ParsedLength = i; + } + + return Result; } internal u64 ParseUInt(gs_const_string String, u64 Base = 10, u64* ParsedLength = 0) { - parse_uint_result ParseResult = ValidateAndParseUInt(String, Base); - Assert(ParseResult.Success); - if (ParsedLength) - { - *ParsedLength = ParseResult.ParsedLength; - } - return ParseResult.Value; + parse_uint_result ParseResult = ValidateAndParseUInt(String, Base); + Assert(ParseResult.Success); + if (ParsedLength) + { + *ParsedLength = ParseResult.ParsedLength; + } + return ParseResult.Value; } internal u64 ParseUInt(u64 Length, char* String, u64 Base = 10, u64* ParsedLength = 0) { - return ParseUInt(ConstString(String, Length), Base, ParsedLength); + return ParseUInt(ConstString(String, Length), Base, ParsedLength); } internal u64 ParseUInt(char* String, u64 Base = 10, u64* ParsedLength = 0) { - return ParseUInt(LitString(String), Base, ParsedLength); + return ParseUInt(LitString(String), Base, ParsedLength); } internal s64 ParseInt(gs_const_string String, u64 Base = 10, u64* ParsedLength = 0) { - s64 Result = 0; - u64 TempParsedLength = 0; - if (String.Str[0] == '-') - { - Result = -1 * (s64)ParseUInt(GetStringAfter(String, 1), Base, &TempParsedLength); - TempParsedLength += 1; - } - else - { - Result = (s64)ParseUInt(String, Base, &TempParsedLength); - } - if (ParsedLength != 0) - { - *ParsedLength = TempParsedLength; - } - return Result; + s64 Result = 0; + u64 TempParsedLength = 0; + if (String.Str[0] == '-') + { + Result = -1 * (s64)ParseUInt(GetStringAfter(String, 1), Base, &TempParsedLength); + TempParsedLength += 1; + } + else + { + Result = (s64)ParseUInt(String, Base, &TempParsedLength); + } + if (ParsedLength != 0) + { + *ParsedLength = TempParsedLength; + } + return Result; } internal s64 ParseInt(char* String, u64 Base = 10, u64* ParsedLength = 0) { - return ParseInt(LitString(String), Base, ParsedLength); + return ParseInt(LitString(String), Base, ParsedLength); } struct parse_float_result { - b8 Success; - r64 Value; - u64 ParsedLength; + b8 Success; + r64 Value; + u64 ParsedLength; }; internal parse_float_result ValidateAndParseFloat(gs_const_string String) { - parse_float_result Result = {0}; - Result.Success = false; - - // Validate - bool StringIsValid = true; - for (u64 i = 0; i < String.Length; i++) + parse_float_result Result = {0}; + Result.Success = false; + + // Validate + bool StringIsValid = true; + for (u64 i = 0; i < String.Length; i++) + { + if (!IsNumericDecimal(String.Str[i]) && String.Str[i] != '-') { - if (!IsNumericDecimal(String.Str[i]) && String.Str[i] != '-') - { - StringIsValid = false; - break; - } + StringIsValid = false; + break; + } + } + + if (StringIsValid) + { + s64 DecimalIndex = FindFirst(String, '.'); + u64 TempParsedLength = 0; + u64 PlacesAfterPoint = 0; + + gs_const_string IntegerString = GetStringBefore(String, DecimalIndex); + gs_const_string DecimalString = {}; + if (DecimalIndex >= 0) + { + DecimalString = GetStringAfter(String, DecimalIndex + 1); } - if (StringIsValid) + r32 Polarity = 1; + if (IntegerString.Str[0] == '-') { - s64 DecimalIndex = FindFirst(String, '.'); - u64 TempParsedLength = 0; - u64 PlacesAfterPoint = 0; - - gs_const_string IntegerString = GetStringBefore(String, DecimalIndex); - gs_const_string DecimalString = {}; - if (DecimalIndex >= 0) - { - DecimalString = GetStringAfter(String, DecimalIndex + 1); - } - - r32 Polarity = 1; - if (IntegerString.Str[0] == '-') - { - IntegerString = GetStringAfter(IntegerString, 1); - Polarity = -1; - } - - Result.Value = (r64)ParseInt(IntegerString, 10, &TempParsedLength); - - if (TempParsedLength == IntegerString.Length) - { - r64 AfterPoint = (r64)ParseUInt(DecimalString, 10, &PlacesAfterPoint); - r64 Decimal = (AfterPoint / PowR64(10, PlacesAfterPoint)); - Result.Value = Result.Value + Decimal; - Result.Value *= Polarity; - } - - Result.ParsedLength = TempParsedLength + PlacesAfterPoint; - if (DecimalIndex < (s64)String.Length) { Result.ParsedLength += 1; } - - Result.Success = true; + IntegerString = GetStringAfter(IntegerString, 1); + Polarity = -1; } - return Result; + Result.Value = (r64)ParseInt(IntegerString, 10, &TempParsedLength); + + if (TempParsedLength == IntegerString.Length) + { + r64 AfterPoint = (r64)ParseUInt(DecimalString, 10, &PlacesAfterPoint); + r64 Decimal = (AfterPoint / PowR64(10, PlacesAfterPoint)); + Result.Value = Result.Value + Decimal; + Result.Value *= Polarity; + } + + Result.ParsedLength = TempParsedLength + PlacesAfterPoint; + if (DecimalIndex < (s64)String.Length) { Result.ParsedLength += 1; } + + Result.Success = true; + } + + return Result; } internal r64 ParseFloat(gs_const_string String, u64* ParsedLength = 0) { - parse_float_result Result = ValidateAndParseFloat(String); - Assert(Result.Success); - if (ParsedLength != 0) - { - *ParsedLength = Result.ParsedLength; - } - return Result.Value; + parse_float_result Result = ValidateAndParseFloat(String); + Assert(Result.Success); + if (ParsedLength != 0) + { + *ParsedLength = Result.ParsedLength; + } + return Result.Value; } internal r64 ParseFloat(char* String, u64* ParsedLength = 0) { - return ParseFloat(LitString(String), ParsedLength); + return ParseFloat(LitString(String), ParsedLength); } internal u64 AppendString(gs_string* Base, gs_const_string Appendix) { - u64 StartIndex = Base->Length; - u64 LengthAvailable = Base->Size - Base->Length; - u64 Written = 0; - for (; Written < Min(LengthAvailable, Appendix.Length); Written++) - { - Base->Str[StartIndex + Written] = Appendix.Str[Written]; - } - Base->Length += Written; - Assert(Base->Length <= Base->Size); - return Written; + u64 StartIndex = Base->Length; + u64 LengthAvailable = Base->Size - Base->Length; + u64 Written = 0; + for (; Written < Min(LengthAvailable, Appendix.Length); Written++) + { + Base->Str[StartIndex + Written] = Appendix.Str[Written]; + } + Base->Length += Written; + Assert(Base->Length <= Base->Size); + return Written; } internal u64 AppendString(gs_string* Base, gs_string Appendix) { - return AppendString(Base, Appendix.ConstString); + return AppendString(Base, Appendix.ConstString); } internal void InsertAt(gs_string* Str, u64 Index, char C) { - if (Str->Length > Index) + if (Str->Length > Index) + { + for (u64 i = Str->Length; i > Index; i--) { - for (u64 i = Str->Length; i > Index; i--) - { - Str->Str[i] = Str->Str[i - 1]; - } - } - - if (Index < Str->Size) - { - Str->Str[Index] = C; - Str->Length += 1; - Assert(Str->Length < Str->Size); + Str->Str[i] = Str->Str[i - 1]; } + } + + if (Index < Str->Size) + { + Str->Str[Index] = C; + Str->Length += 1; + Assert(Str->Length < Str->Size); + } } internal void RemoveAt(gs_string* Str, u64 Index) { - if (Str->Length > 0 && Index < Str->Length) + if (Str->Length > 0 && Index < Str->Length) + { + for (u64 i = Index; i < Str->Length - 1; i++) { - for (u64 i = Index; i < Str->Length - 1; i++) - { - Str->Str[i] = Str->Str[i + 1]; - } - Str->Length -= 1; + Str->Str[i] = Str->Str[i + 1]; } + Str->Length -= 1; + } } internal void NullTerminate(gs_string* String) { - if (String->Length < String->Size) - { - String->Str[String->Length] = 0; - } - else - { - String->Str[String->Length - 1] = 0; - } + if (String->Length < String->Size) + { + String->Str[String->Length] = 0; + } + else + { + String->Str[String->Length - 1] = 0; + } } internal void OutChar(gs_string* String, char C) { - if (String->Length < String->Size) - { - String->Str[String->Length++] = C; - } + if (String->Length < String->Size) + { + String->Str[String->Length++] = C; + } } internal void U64ToASCII(gs_string* String, u64 Value, u64 Base, gs_const_string Digits) { - u64 ValueRemaining = Value; - u64 At = 0; - do { - u64 Index = ValueRemaining % Base; - char Digit = Digits.Str[Index]; - OutChar(String, Digit); - ValueRemaining /= Base; - }while(ValueRemaining); - char* End = String->Str + String->Length; - ReverseStringInPlace(String); + u64 ValueRemaining = Value; + u64 At = 0; + do { + u64 Index = ValueRemaining % Base; + char Digit = Digits.Str[Index]; + OutChar(String, Digit); + ValueRemaining /= Base; + }while(ValueRemaining); + char* End = String->Str + String->Length; + ReverseStringInPlace(String); } internal void U64ToASCII(gs_string* String, u64 Value, u64 Base) { - U64ToASCII(String, Value, Base, GetCharSetForBase(Base)); + U64ToASCII(String, Value, Base, GetCharSetForBase(Base)); } internal void R64ToASCII(gs_string* String, r64 Value, u64 Precision) { - if (Value < 0) + if (Value < 0) + { + OutChar(String, '-'); + Value = Abs(Value); + } + u64 IntegerPart = (u64)Value; + // NOTE(Peter): If we don't use the inner string, when U64ToASCII reverses the characters + // it'll put the negative sign at the end. + gs_string IntegerString = GetStringAfter(*String, String->Length); + U64ToASCII(&IntegerString, IntegerPart, 10); + String->Length += IntegerString.Length; + Value -= IntegerPart; + if (Value > 0) + { + OutChar(String, '.'); + for (u64 i = 0; i < Precision; i++) { - OutChar(String, '-'); - Value = Abs(Value); - } - u64 IntegerPart = (u64)Value; - // NOTE(Peter): If we don't use the inner string, when U64ToASCII reverses the characters - // it'll put the negative sign at the end. - gs_string IntegerString = GetStringAfter(*String, String->Length); - U64ToASCII(&IntegerString, IntegerPart, 10); - String->Length += IntegerString.Length; - Value -= IntegerPart; - if (Value > 0) - { - OutChar(String, '.'); - for (u64 i = 0; i < Precision; i++) - { - Value *= 10.0f; - u64 DecimalPlace = (u64)Value; - Value -= DecimalPlace; - OutChar(String, Base10Chars.Str[DecimalPlace]); - } + Value *= 10.0f; + u64 DecimalPlace = (u64)Value; + Value -= DecimalPlace; + OutChar(String, Base10Chars.Str[DecimalPlace]); } + } } internal s64 ReadVarArgsSignedInteger (s32 Width, va_list* Args) { - s64 Result = 0; - switch (Width) - { - // NOTE(Peter): For Width lower than 4 bytes, the C++ spec specifies - // that it will get promoted to an int anyways - case 1: { Result = (s64)va_arg(*Args, s32); } break; - case 2: { Result = (s64)va_arg(*Args, s32); } break; - case 4: { Result = (s64)va_arg(*Args, s32); } break; - case 8: { Result = (s64)va_arg(*Args, s64); } break; - InvalidDefaultCase; - } - return Result; + s64 Result = 0; + switch (Width) + { + // NOTE(Peter): For Width lower than 4 bytes, the C++ spec specifies + // that it will get promoted to an int anyways + case 1: { Result = (s64)va_arg(*Args, s32); } break; + case 2: { Result = (s64)va_arg(*Args, s32); } break; + case 4: { Result = (s64)va_arg(*Args, s32); } break; + case 8: { Result = (s64)va_arg(*Args, s64); } break; + InvalidDefaultCase; + } + return Result; } internal r64 ReadVarArgsUnsignedInteger (s32 Width, va_list* Args) { - u64 Result = 0; - switch (Width) - { - // NOTE(Peter): For Width lower than 4 bytes, the C++ spec specifies - // that it will get promoted to an int anyways - case 1: { Result = (u64)va_arg(*Args, u32); } break; - case 2: { Result = (u64)va_arg(*Args, u32); } break; - case 4: { Result = (u64)va_arg(*Args, u32); } break; - case 8: { Result = (u64)va_arg(*Args, u64); } break; - InvalidDefaultCase; - } - return Result; + u64 Result = 0; + switch (Width) + { + // NOTE(Peter): For Width lower than 4 bytes, the C++ spec specifies + // that it will get promoted to an int anyways + case 1: { Result = (u64)va_arg(*Args, u32); } break; + case 2: { Result = (u64)va_arg(*Args, u32); } break; + case 4: { Result = (u64)va_arg(*Args, u32); } break; + case 8: { Result = (u64)va_arg(*Args, u64); } break; + InvalidDefaultCase; + } + return Result; } internal r64 ReadVarArgsFloat (s32 Width, va_list* Args) { - r64 Result = 0; - switch (Width) - { - case 4: { Result = (r64)va_arg(*Args, r64); } break; - case 8: { Result = (r64)va_arg(*Args, r64); } break; - InvalidDefaultCase; - } - return Result; + r64 Result = 0; + switch (Width) + { + case 4: { Result = (r64)va_arg(*Args, r64); } break; + case 8: { Result = (r64)va_arg(*Args, r64); } break; + InvalidDefaultCase; + } + return Result; } internal s32 PrintFArgsList (gs_string* String, char* Format, va_list Args) { - char* FormatAt = Format; - while (*FormatAt) + char* FormatAt = Format; + while (*FormatAt) + { + if (FormatAt[0] != '%') { - if (FormatAt[0] != '%') - { - if (FormatAt[0] == '\\') - { - OutChar(String, *FormatAt++); - } - else - { - OutChar(String, *FormatAt++); - } - } - else if (FormatAt[0] == '%' && FormatAt[1] == '%') // Print the % symbol - { - OutChar(String, '%'); - FormatAt += 2; - } - else - { - FormatAt++; - - // Flags - if (FormatAt[0] == '-') - { - FormatAt++; - } - else if (FormatAt[0] == '+') - { - FormatAt++; - } - else if (FormatAt[0] == ' ') - { - FormatAt++; - } - else if (FormatAt[0] == '#') - { - FormatAt++; - } - else if (FormatAt[0] == '0') - { - FormatAt++; - } - - // Width - b32 WidthSpecified = false; - s32 Width = 0; - - if (IsBase10(FormatAt[0])) - { - WidthSpecified = true; - u64 Parsed = 0; - AssertMessage("ParseInt assumes whole string is an integer"); - Width = (s32)ParseInt(FormatAt, 10, &Parsed); - FormatAt += Parsed; - } - else if (FormatAt[0] == '*') - { - WidthSpecified = true; - Width = va_arg(Args, s32); - Assert(Width >= 0); - FormatAt++; - } - - // Precision - b32 PrecisionSpecified = false; - s32 Precision = 0; - - if (FormatAt[0] == '.') - { - FormatAt++; - if (IsBase10(FormatAt[0])) - { - PrecisionSpecified = true; - - gs_const_string PrecisionStr = {}; - PrecisionStr.Str = FormatAt; - for (char* C = FormatAt; *FormatAt && IsBase10(*C); C++) - { - PrecisionStr.Length++; - } - u64 Parsed = 0; - Precision = (s32)ParseInt(PrecisionStr, 10, &Parsed); - FormatAt += Parsed; - } - else if (FormatAt[0] == '*') - { - PrecisionSpecified = true; - Precision = va_arg(Args, s32); - Assert(Precision >= 0); - FormatAt++; - } - } - - // Length - b32 LengthSpecified = false; - s32 Length = 4; - - if (FormatAt[0] == 'h' && FormatAt[1] == 'h') - { - LengthSpecified = true; - Length = 1; - FormatAt += 2; - } - else if (FormatAt[0] == 'h') - { - LengthSpecified = true; - Length = 2; - FormatAt++; - } - else if (FormatAt[0] == 'l' && FormatAt[1] == 'l') - { - LengthSpecified = true; - Length = 8; - FormatAt += 2; - } - else if (FormatAt[0] == 'l') - { - LengthSpecified = true; - Length = 4; - FormatAt++; - } - else if (FormatAt[0] == 'j') - { - LengthSpecified = true; - Length = 8; - FormatAt++; - } - else if (FormatAt[0] == 'z') - { - FormatAt++; - } - else if (FormatAt[0] == 't') - { - FormatAt++; - } - else if (FormatAt[0] == 'L') - { - FormatAt++; - } - - // Format Specifiers - gs_string StringRemaining = GetStringAfter(*String, String->Length); - Assert(StringRemaining.Length == 0); - if (FormatAt[0] == 'd' || FormatAt[0] == 'i') - { - s64 SignedInt = ReadVarArgsSignedInteger(Length, &Args); - if (SignedInt < 0) - { - OutChar(&StringRemaining, '-'); - SignedInt *= -1; - } - U64ToASCII(&StringRemaining, (u64)SignedInt, 10, Base10Chars); - } - else if (FormatAt[0] == 'u') - { - u64 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); - U64ToASCII(&StringRemaining, UnsignedInt, 10, Base10Chars); - } - else if (FormatAt[0] == 'o') - { - u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); - U64ToASCII(&StringRemaining, UnsignedInt, 8, Base8Chars); - } - else if (FormatAt[0] == 'x' || FormatAt[0] == 'X') - { - u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); - U64ToASCII(&StringRemaining, UnsignedInt, 16, Base16Chars); - } - else if (FormatAt[0] == 'f' || FormatAt[0] == 'F') - { - r64 Float = ReadVarArgsFloat(Length, &Args); - s32 AfterPoint = 6; - if (PrecisionSpecified) - { - AfterPoint = Precision; - } - R64ToASCII(&StringRemaining, Float, AfterPoint); - } - else if (FormatAt[0] == 'c') - { - char InsertChar = va_arg(Args, s32); - OutChar(&StringRemaining, InsertChar); - } - else if (FormatAt[0] == 's') - { - char* InsertString = va_arg(Args, char*); - - s32 InsertStringLength = CStringLength(InsertString); - if (PrecisionSpecified) - { - InsertStringLength = Min(InsertStringLength, Precision); - } - InsertStringLength = Min(StringSizeLeft(StringRemaining), InsertStringLength); - - for (s32 c = 0; c < InsertStringLength; c++) - { - OutChar(&StringRemaining, InsertString[c]); - } - } - else if (FormatAt[0] == 'S') - { - gs_const_string InsertString = va_arg(Args, gs_const_string); - - for (s32 c = 0; c < InsertString.Length; c++) - { - OutChar(&StringRemaining, InsertString.Str[c]); - } - } - else if (FormatAt[0] == 'p') - { - // TODO(Peter): Pointer Address - } - else - { - // NOTE(Peter): Non-specifier character found - InvalidCodePath; - } - - String->Length += StringRemaining.Length; - FormatAt++; - } + if (FormatAt[0] == '\\') + { + OutChar(String, *FormatAt++); + } + else + { + OutChar(String, *FormatAt++); + } } - - return String->Length; + else if (FormatAt[0] == '%' && FormatAt[1] == '%') // Print the % symbol + { + OutChar(String, '%'); + FormatAt += 2; + } + else + { + FormatAt++; + + // Flags + if (FormatAt[0] == '-') + { + FormatAt++; + } + else if (FormatAt[0] == '+') + { + FormatAt++; + } + else if (FormatAt[0] == ' ') + { + FormatAt++; + } + else if (FormatAt[0] == '#') + { + FormatAt++; + } + else if (FormatAt[0] == '0') + { + FormatAt++; + } + + // Width + b32 WidthSpecified = false; + s32 Width = 0; + + if (IsBase10(FormatAt[0])) + { + WidthSpecified = true; + u64 Parsed = 0; + AssertMessage("ParseInt assumes whole string is an integer"); + Width = (s32)ParseInt(FormatAt, 10, &Parsed); + FormatAt += Parsed; + } + else if (FormatAt[0] == '*') + { + WidthSpecified = true; + Width = va_arg(Args, s32); + Assert(Width >= 0); + FormatAt++; + } + + // Precision + b32 PrecisionSpecified = false; + s32 Precision = 0; + + if (FormatAt[0] == '.') + { + FormatAt++; + if (IsBase10(FormatAt[0])) + { + PrecisionSpecified = true; + + gs_const_string PrecisionStr = {}; + PrecisionStr.Str = FormatAt; + for (char* C = FormatAt; *FormatAt && IsBase10(*C); C++) + { + PrecisionStr.Length++; + } + u64 Parsed = 0; + Precision = (s32)ParseInt(PrecisionStr, 10, &Parsed); + FormatAt += Parsed; + } + else if (FormatAt[0] == '*') + { + PrecisionSpecified = true; + Precision = va_arg(Args, s32); + Assert(Precision >= 0); + FormatAt++; + } + } + + // Length + b32 LengthSpecified = false; + s32 Length = 4; + + if (FormatAt[0] == 'h' && FormatAt[1] == 'h') + { + LengthSpecified = true; + Length = 1; + FormatAt += 2; + } + else if (FormatAt[0] == 'h') + { + LengthSpecified = true; + Length = 2; + FormatAt++; + } + else if (FormatAt[0] == 'l' && FormatAt[1] == 'l') + { + LengthSpecified = true; + Length = 8; + FormatAt += 2; + } + else if (FormatAt[0] == 'l') + { + LengthSpecified = true; + Length = 4; + FormatAt++; + } + else if (FormatAt[0] == 'j') + { + LengthSpecified = true; + Length = 8; + FormatAt++; + } + else if (FormatAt[0] == 'z') + { + FormatAt++; + } + else if (FormatAt[0] == 't') + { + FormatAt++; + } + else if (FormatAt[0] == 'L') + { + FormatAt++; + } + + // Format Specifiers + gs_string StringRemaining = GetStringAfter(*String, String->Length); + Assert(StringRemaining.Length == 0); + if (FormatAt[0] == 'd' || FormatAt[0] == 'i') + { + s64 SignedInt = ReadVarArgsSignedInteger(Length, &Args); + if (SignedInt < 0) + { + OutChar(&StringRemaining, '-'); + SignedInt *= -1; + } + U64ToASCII(&StringRemaining, (u64)SignedInt, 10, Base10Chars); + } + else if (FormatAt[0] == 'u') + { + u64 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + U64ToASCII(&StringRemaining, UnsignedInt, 10, Base10Chars); + } + else if (FormatAt[0] == 'o') + { + u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + U64ToASCII(&StringRemaining, UnsignedInt, 8, Base8Chars); + } + else if (FormatAt[0] == 'x' || FormatAt[0] == 'X') + { + u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + U64ToASCII(&StringRemaining, UnsignedInt, 16, Base16Chars); + } + else if (FormatAt[0] == 'f' || FormatAt[0] == 'F') + { + r64 Float = ReadVarArgsFloat(Length, &Args); + s32 AfterPoint = 6; + if (PrecisionSpecified) + { + AfterPoint = Precision; + } + R64ToASCII(&StringRemaining, Float, AfterPoint); + } + else if (FormatAt[0] == 'c') + { + char InsertChar = va_arg(Args, s32); + OutChar(&StringRemaining, InsertChar); + } + else if (FormatAt[0] == 's') + { + char* InsertString = va_arg(Args, char*); + + s32 InsertStringLength = CStringLength(InsertString); + if (PrecisionSpecified) + { + InsertStringLength = Min(InsertStringLength, Precision); + } + InsertStringLength = Min(StringSizeLeft(StringRemaining), InsertStringLength); + + for (s32 c = 0; c < InsertStringLength; c++) + { + OutChar(&StringRemaining, InsertString[c]); + } + } + else if (FormatAt[0] == 'S') + { + gs_const_string InsertString = va_arg(Args, gs_const_string); + + for (s32 c = 0; c < InsertString.Length; c++) + { + OutChar(&StringRemaining, InsertString.Str[c]); + } + } + else if (FormatAt[0] == 'p') + { + // TODO(Peter): Pointer Address + } + else + { + // NOTE(Peter): Non-specifier character found + InvalidCodePath; + } + + String->Length += StringRemaining.Length; + FormatAt++; + } + } + + return String->Length; } internal void PrintF (gs_string* String, char* Format, ...) { - va_list Args; - va_start(Args, Format); - String->Length = 0; - PrintFArgsList(String, Format, Args); - va_end(Args); + va_list Args; + va_start(Args, Format); + String->Length = 0; + PrintFArgsList(String, Format, Args); + va_end(Args); } internal void PrintF (gs_string* String, const char* Format, ...) { - // NOTE(Peter): This variant is here for clang/gcc - C++ spec doesn't allow - // implicit conversion from a const char* (a static c string) to char*, so this - // version of the function just provides the conversion so the compiler will be quiet - // without removing the other implementation, which is more useful - va_list Args; - va_start(Args, Format); - String->Length = 0; - PrintFArgsList(String, (char*)Format, Args); - va_end(Args); + // NOTE(Peter): This variant is here for clang/gcc - C++ spec doesn't allow + // implicit conversion from a const char* (a static c string) to char*, so this + // version of the function just provides the conversion so the compiler will be quiet + // without removing the other implementation, which is more useful + va_list Args; + va_start(Args, Format); + String->Length = 0; + PrintFArgsList(String, (char*)Format, Args); + va_end(Args); } internal void AppendPrintF (gs_string* String, char* Format, ...) { - va_list Args; - va_start(Args, Format); - PrintFArgsList(String, Format, Args); - va_end(Args); + va_list Args; + va_start(Args, Format); + PrintFArgsList(String, Format, Args); + va_end(Args); } internal void AppendPrintF (gs_string* String, const char* Format, ...) { - // NOTE(Peter): This variant is here for clang/gcc - C++ spec doesn't allow - // implicit conversion from a const char* (a static c string) to char*, so this - // version of the function just provides the conversion so the compiler will be quiet - // without removing the other implementation, which is more useful - va_list Args; - va_start(Args, Format); - PrintFArgsList(String, (char*)Format, Args); - va_end(Args); + // NOTE(Peter): This variant is here for clang/gcc - C++ spec doesn't allow + // implicit conversion from a const char* (a static c string) to char*, so this + // version of the function just provides the conversion so the compiler will be quiet + // without removing the other implementation, which is more useful + va_list Args; + va_start(Args, Format); + PrintFArgsList(String, (char*)Format, Args); + va_end(Args); } /////////////////////////// @@ -2417,436 +2417,44 @@ AppendPrintF (gs_string* String, const char* Format, ...) internal gs_data CreateData(u8* Memory, u64 Size) { - gs_data Result = {Memory, Size}; - return Result; + gs_data Result = {Memory, Size}; + return Result; } internal bool DataIsNonEmpty(gs_data Data) { - return ((Data.Size > 0) && (Data.Memory != 0)); + return ((Data.Size > 0) && (Data.Memory != 0)); } -internal void* AllocatorAlloc_NoOp(u64 Size, u64* SizeResult) { - *SizeResult = 0; - return 0; -} -internal void AllocatorFree_NoOp(void* Base, u64 Size) { return; } - -internal gs_allocator -CreateAllocator_(allocator_allocate* Alloc, allocator_free* Free) -{ - if (Alloc == 0) - { - Alloc = AllocatorAlloc_NoOp; - } - if (Free == 0) - { - Free = AllocatorFree_NoOp; - } - gs_allocator Result = {0}; - Result.Alloc = Alloc; - Result.Free = Free; - return Result; -} -#define CreateAllocator(a, f) CreateAllocator_((allocator_allocate*)(a), (allocator_free*)(f)) - -internal void -AllocatorDebug_PushAlloc(gs_allocator_debug* Debug, u64 Size, char* Location) -{ - // NOTE(pjs): I don't want this debug procedure to be the reason the - // application crashes. - if (Debug->AllocationsCount < Debug->AllocationsCountMax) - { - gs_debug_allocation Allocation = {}; - - gs_const_string L = ConstString(Location); - - s64 LastSlash = FindLastFromSet(L, "\\/"); - if (LastSlash < 0) LastSlash = 0; - Allocation.Location = GetStringAfter(L, LastSlash); - Allocation.Size = Size; - - Debug->Allocations[Debug->AllocationsCount++] = Allocation; - } - Debug->TotalAllocSize += Size; -} - -internal gs_data -AllocatorAlloc_(gs_allocator Allocator, u64 Size, char* Location) -{ - u64 SizeResult = 0; - void* Memory = Allocator.Alloc(Size, &SizeResult); - if (Allocator.Debug) - { - AllocatorDebug_PushAlloc(Allocator.Debug, Size, Location); - } - return CreateData((u8*)Memory, SizeResult); -} -internal void -AllocatorFree_(gs_allocator Allocator, void* Base, u64 Size, char* Location) -{ - if (Base != 0 && Size != 0) - { - Allocator.Free(Base, Size); - if (Allocator.Debug) - { - // NOTE(pjs): There's no reason we should be going negative - // ie. Freeing more memory than we allocated - Assert(Allocator.Debug->TotalAllocSize >= Size); - Allocator.Debug->TotalAllocSize -= Size; - } - } -} - -#define AllocatorAlloc(alloc,size) AllocatorAlloc_((alloc), (size), FileNameAndLineNumberString) -#define AllocatorAllocStruct(alloc, type) (type*)(AllocatorAlloc((alloc), sizeof(type)).Memory) -#define AllocatorAllocArray(alloc, type, count) (type*)(AllocatorAlloc((alloc), sizeof(type) * (count)).Memory) -#define AllocatorAllocString(alloc, size) gs_string{ AllocatorAllocArray((alloc), char, (size)), 0, (size) } -#define AllocatorFree(alloc,base,size) AllocatorFree_((alloc), (base), (size), FileNameAndLineNumberString) -#define AllocatorFreeArray(alloc,base,type,count) AllocatorFree_((alloc), (base), sizeof(type) * count, FileNameAndLineNumberString) -internal gs_memory_cursor -CreateMemoryCursor(u8* Base, u64 Size) -{ - gs_memory_cursor Result = {0}; - Result.Data.Memory = Base; - Result.Data.Size = Size; - return Result; -}; -internal gs_memory_cursor -CreateMemoryCursor(gs_data Data) -{ - return CreateMemoryCursor(Data.Memory, Data.Size); -} -internal gs_memory_cursor -CreateMemoryCursor(gs_allocator Allocator, u64 Size) -{ - gs_data Data = AllocatorAlloc(Allocator, Size); - return CreateMemoryCursor(Data); -} -internal bool -CursorHasRoom(gs_memory_cursor Cursor, u64 Size) -{ - bool Result = ((Cursor.Position + Size) <= Cursor.Data.Size); - return Result; -} -internal gs_data -PushSizeOnCursor_(gs_memory_cursor* Cursor, u64 Size, char* Location) -{ - gs_data Result = {0}; - if (CursorHasRoom(*Cursor, Size)) - { - Result.Memory = Cursor->Data.Memory + Cursor->Position; - Result.Size = Size; - Cursor->Position += Size; - } - return Result; -} - -#define PushSizeOnCursor(cursor,size) PushSizeOnCursor_((cursor), (size), FileNameAndLineNumberString) -#define PushStructOnCursor(cursor,type) (type*)PushSizeOnCursor_((cursor), sizeof(type), FileNameAndLineNumberString).Memory -#define PushArrayOnCursor(cursor,type,count) (type*)PushSizeOnCursor_((cursor), sizeof(type) * (count), FileNameAndLineNumberString).Memory - -#define MemoryCursor_WriteValue(cursor, type, value) *PushStructOnCursor(cursor, type) = value -#define MemoryCursor_WriteBuffer(cursor, buf, len) CopyMemoryTo((u8*)(buf), PushArrayOnCursor((cursor), u8, (len)), (len)) - -internal void -PopSizeOnCursor(gs_memory_cursor* Cursor, u64 Size) -{ - if (Cursor->Position > Size) - { - Cursor->Position -= Size; - } - else - { - Cursor->Position = 0; - } -} -internal gs_data -AlignCursor(gs_memory_cursor* Cursor, u64 Alignment) -{ - u64 Position = RoundUpTo64(Cursor->Position, Alignment); - Position = Min(Position, Cursor->Data.Size); - u64 NewSize = Position - Cursor->Position; - return PushSizeOnCursor(Cursor, NewSize); -} -internal void -ClearCursor(gs_memory_cursor* Cursor) -{ - Cursor->Position = 0; -} - -internal void -FreeCursorListEntry(gs_allocator Allocator, gs_memory_cursor_list* CursorEntry) -{ - AllocatorFree(Allocator, CursorEntry, CursorEntry->Cursor.Data.Size + sizeof(gs_memory_cursor)); -} - -internal gs_memory_arena -CreateMemoryArena_(arena_type ArenaType, gs_allocator Allocator, u64 ChunkSize, u64 Alignment, gs_memory_arena* ParentArena, char* Name) -{ - // we only want a parent arena if the type is Arena_SubArena - Assert(((ArenaType == Arena_BaseArena) && (ParentArena == 0)) || - ((ArenaType == Arena_SubArena) && (ParentArena != 0))); - - gs_memory_arena Arena = {}; - Arena.ArenaName = Name; - Arena.Type = ArenaType; - Arena.Allocator = Allocator; - Arena.Parent = ParentArena; - -#if MEMORY_CURSOR_STATIC_ARRAY - Arena.CursorsCountMax = 4096; - Arena.Cursors = AllocatorAllocArray(Allocator, gs_memory_cursor_list, Arena.CursorsCountMax); -#endif - - Arena.MemoryChunkSize = ChunkSize; - Arena.MemoryAlignment = Alignment; - return Arena; -} - -internal gs_memory_arena -CreateMemoryArena(gs_allocator Allocator, char* Name, u64 ChunkSize = KB(4), u64 Alignment = Bytes(8)) -{ - return CreateMemoryArena_(Arena_BaseArena, Allocator, ChunkSize, Alignment, 0, Name); -} -internal gs_memory_arena -CreateMemorySubArena(gs_memory_arena* Parent, char* Name, u64 ChunkSize = KB(32), u64 Alignment = Bytes(8)) -{ - return CreateMemoryArena_(Arena_SubArena, Parent->Allocator, ChunkSize, Alignment, Parent, Name); -} - -internal gs_data PushSize_(gs_memory_arena* Arena, u64 Size, char* Location); - -internal void -FreeCursorList(gs_memory_cursor_list* List, gs_allocator Allocator) -{ -#if !MEMORY_CURSOR_STATIC_ARRAY - gs_memory_cursor_list* CursorAt = List; - while (CursorAt != 0) - { - gs_memory_cursor_list* Prev = CursorAt->Prev; - FreeCursorListEntry(Allocator, CursorAt); - CursorAt = Prev; - } -#endif -} - -internal gs_memory_cursor_list* -MemoryArenaNewCursor(gs_memory_arena* Arena, u64 MinSize, char* Location) -{ -#if MEMORY_CURSOR_STATIC_ARRAY - u64 AllocSize = Max(MinSize, Arena->MemoryChunkSize); -#else - // Allocate enough spcae for the minimum size needed + sizeo for the cursor list - u64 AllocSize = Max(MinSize, Arena->MemoryChunkSize) + sizeof(gs_memory_cursor_list); -#endif - - gs_data Data = {0}; - switch (Arena->Type) - { - case Arena_SubArena: - { - Data = PushSize_(Arena->Parent, AllocSize, Location); - }break; - - case Arena_BaseArena: - { - Data = AllocatorAlloc_(Arena->Allocator, AllocSize, Location); - }break; - - InvalidDefaultCase; - } - -#if MEMORY_CURSOR_STATIC_ARRAY - Assert(Arena->CursorsCount < Arena->CursorsCountMax); - gs_memory_cursor_list* Result = Arena->Cursors + Arena->CursorsCount++; - Result->Cursor = CreateMemoryCursor(Data.Memory, Data.Size); -#else - // Fit the memory cursor into the region allocated - Assert(MinSize + sizeof(gs_memory_cursor_list) <= Data.Size); - gs_memory_cursor_list* Result = (gs_memory_cursor_list*)Data.Memory; - u8* CursorMemoryStart = (u8*)(Result + 1); - u64 CursorMemorySize = Data.Size - sizeof(gs_memory_cursor_list); - Result->Cursor = CreateMemoryCursor(CursorMemoryStart, CursorMemorySize); - - Result->Prev = Arena->CursorList; - Result->Next = 0; - if (Arena->CursorList != 0) - { - if (Arena->CursorList->Next != 0) - { - Result->Next = Arena->CursorList->Next; - } - Arena->CursorList->Next = Result; - } - Arena->CursorList = Result; -#endif - return Result; -} - -internal gs_data -PushSize_(gs_memory_arena* Arena, u64 Size, char* Location) -{ - gs_data Result = {0}; - if (Size > 0) - { -#if MEMORY_CURSOR_STATIC_ARRAY - gs_memory_cursor_list* CursorEntry = 0; - for (u64 i = 0; - i < Arena->CursorsCount; - i++) - { - gs_memory_cursor_list* At = Arena->Cursors + i; - if (CursorHasRoom(At->Cursor, Size)) - { - CursorEntry = At; - break; - } - } - if (!CursorEntry) - { - CursorEntry = MemoryArenaNewCursor(Arena, Size, Location); - } - - if (!CursorEntry || !CursorHasRoom(CursorEntry->Cursor, Size)) - { - __debugbreak(); - - CursorEntry = 0; - for (u64 i = 0; - i < Arena->CursorsCount; - i++) - { - gs_memory_cursor_list* At = Arena->Cursors + i; - if (CursorHasRoom(At->Cursor, Size)) - { - CursorEntry = At; - break; - } - } - if (!CursorEntry) - { - CursorEntry = MemoryArenaNewCursor(Arena, Size, Location); - } - } - //Assert(CursorEntry); - //Assert(CursorHasRoom(CursorEntry->Cursor, Size)); -#else - - gs_memory_cursor_list* CursorEntry = Arena->CursorList; - if (CursorEntry == 0) - { - CursorEntry = MemoryArenaNewCursor(Arena, Size, Location); - } - if (!CursorHasRoom(CursorEntry->Cursor, Size)) - { - while ((CursorEntry != 0) && !CursorHasRoom(CursorEntry->Cursor, Size)) - { - CursorEntry = CursorEntry->Next; - } - if (CursorEntry == 0) - { - CursorEntry = MemoryArenaNewCursor(Arena, Size, Location); - } - } -#endif - Assert(CursorEntry != 0); - Assert(CursorHasRoom(CursorEntry->Cursor, Size)); - Result = PushSizeOnCursor_(&CursorEntry->Cursor, Size, Location); - Assert(Result.Memory != 0); - - gs_data Alignment = AlignCursor(&CursorEntry->Cursor, Arena->MemoryAlignment); - Result.Size += Alignment.Size; - } - - // TODO(Peter): @Cleanup @Efficiency - // There is a case I want to handle at some point: - // You have a Cursor that is empty, but the size you want to allocate is bigger - // than the cursor. So you create a new cursor, of the exact size you need, - // immediately fill it up, and push it onto the head of the cursor list. Now, - // the list looks like this: - // [root] [cursor] ... [empty cursor] [full cursor] [new, empty cursor] - // and you'll never use the memory in 'empty cursor' - // What I'd like to do is, when you fill up a cursor, it gets pushed back until - // the next cursor is more full - // NOTE: Thought on this tho - you don't want this behavior in a scratch arena - // where usage across frames could change drastically. This matters way more in - // a permanent storage (i think) - return Result; -} - -internal void -FreeMemoryArena(gs_memory_arena* Arena) -{ -#if MEMORY_CURSOR_STATIC_ARRAY - for (u32 i = 0; i < Arena->CursorsCount; i++) - { - gs_memory_cursor_list E = Arena->Cursors[i]; - AllocatorFree(Arena->Allocator, E.Cursor.Data.Memory, E.Cursor.Data.Size); - } - AllocatorFreeArray(Arena->Allocator, Arena->Cursors, gs_memory_cursor_list, Arena->CursorsCountMax); -#else - FreeCursorList(Arena->CursorList, Arena->Allocator); -#endif -} - -#define PushSizeToData(arena, size) PushSize_((arena), (size), FileNameAndLineNumberString) -#define PushSize(arena, size) PushSize_((arena), (size), FileNameAndLineNumberString).Memory -#define PushStruct(arena, type) (type*)(PushSize_((arena), sizeof(type), FileNameAndLineNumberString).Memory) -#define PushArray(arena, type, count) (type*)(PushSize_((arena), sizeof(type) * (count), FileNameAndLineNumberString).Memory) -#define PushString(arena, length) MakeString(PushArray((arena), char, (length)), 0, (length)); - +#define PushStringF(a,l,f,...) PushStringF_((a),(l),(f), DEBUG_LOC, __VA_ARGS__) internal gs_string -PushStringF(gs_memory_arena* Arena, u32 MaxLength, char* Format, ...) +PushStringF_(gs_memory_arena* Arena, u32 MaxLength, char* Format, gs_debug_loc Loc, ...) { - gs_string Result = PushString(Arena, MaxLength); - - va_list Args; - va_start(Args, Format); - PrintFArgsList(&Result, Format, Args); - va_end(Args); - - return Result; + gs_string Result = gs_string { + (char*)PushSize_(Arena, sizeof(char) * MaxLength, Loc).Memory, // Str + 0, // Length + MaxLength, // Size + }; + + va_list Args; + va_start(Args, Loc); + PrintFArgsList(&Result, Format, Args); + va_end(Args); + + return Result; } internal gs_string PushStringCopy(gs_memory_arena* Arena, gs_const_string String) { - gs_string Result = PushString(Arena, String.Length); - Result.Size = String.Length; - Result.Length = String.Length; - for (u32 i = 0; i < String.Length; i++) - { - Result.Str[i] = String.Str[i]; - } - return Result; -} - -internal void -ClearArena(gs_memory_arena* Arena) -{ -#if MEMORY_CURSOR_STATIC_ARRAY - for (u32 i = 0; i < Arena->CursorsCount; i++) - { - Arena->Cursors[i].Cursor.Position = 0; - } -#else - gs_memory_cursor_list* First = 0; - for (gs_memory_cursor_list* CursorEntry = Arena->CursorList; - CursorEntry != 0; - CursorEntry = CursorEntry->Prev) - { - First = CursorEntry; - CursorEntry->Cursor.Position = 0; - } - Arena->CursorList = First; -#endif -} - -internal void -FreeArena(gs_memory_arena* Arena) -{ - FreeMemoryArena(Arena); + gs_string Result = PushString(Arena, String.Length); + Result.Size = String.Length; + Result.Length = String.Length; + for (u32 i = 0; i < String.Length; i++) + { + Result.Str[i] = String.Str[i]; + } + return Result; } /////////////////////////// @@ -2856,162 +2464,29 @@ FreeArena(gs_memory_arena* Arena) inline void DebugPrint(debug_output Output, gs_const_string Message) { - Output.Print(Output, Message); + Output.Print(Output, Message); } inline void DebugPrint(debug_output Output, char* Message) { - gs_const_string String = ConstString(Message); - Output.Print(Output, String); + gs_const_string String = ConstString(Message); + Output.Print(Output, String); } internal void DebugPrintF(debug_output Output, char* Format, ...) { - gs_string Message = PushString(Output.Storage, 1024); - va_list Args; - va_start(Args, Format); - PrintFArgsList(&Message, Format, Args); - NullTerminate(&Message); - Output.Print(Output, Message.ConstString); -} - -/////////////////////////// -// -// Dynamic Array - -internal gs_dynarray -CreateDynarrayWithStorage(gs_memory_arena Storage, u32 ElementSize, u32 ElementsPerBuffer) -{ - gs_dynarray Result = {}; - Result.Arena = Storage; - Result.ElementSize = ElementSize; - Result.ElementsPerBuffer = ElementsPerBuffer; - Result.ElementCount = 0; - return Result; -} - -internal gs_dynarray -CreateDynarray_(gs_allocator Allocator, u32 ElementSize, u32 ElementsPerBuffer) -{ - gs_memory_arena Storage = CreateMemoryArena(Allocator, "Dynarray Arena", ElementSize * ElementsPerBuffer); - return CreateDynarrayWithStorage(Storage, ElementSize, ElementsPerBuffer); -}; - -internal gs_dynarray -CreateDynarray_(gs_memory_arena* Arena, u32 ElementSize, u32 ElementsPerBuffer) -{ - gs_memory_arena Storage = CreateMemorySubArena(Arena, "Dynarray Sub Arena", ElementSize * ElementsPerBuffer); - return CreateDynarrayWithStorage(Storage, ElementSize, ElementsPerBuffer); -}; - -internal u64 -CalculateBufferSize(gs_dynarray Array) -{ - u64 Result = Array.ElementsPerBuffer * Array.ElementSize; - return Result; -} - -internal void -GrowDynarray(gs_dynarray* Array) -{ - gs_dynarray_buffer* OldBufferList = Array->Buffers; - u64 NewBuffersCount = Array->BuffersCount + 1; - gs_dynarray_buffer* NewBufferList = AllocatorAllocArray(Array->Arena.Allocator, gs_dynarray_buffer, NewBuffersCount); - if (OldBufferList) - { - CopyArray(OldBufferList, NewBufferList, gs_dynarray_buffer, Array->BuffersCount); - AllocatorFree(Array->Arena.Allocator, OldBufferList, sizeof(gs_dynarray_buffer) * Array->BuffersCount); - } - u64 BufferSize = CalculateBufferSize(*Array); - NewBufferList[Array->BuffersCount].Memory = PushSize(&Array->Arena, BufferSize); - Array->Buffers = NewBufferList; - Array->BuffersCount = NewBuffersCount; -} -internal u64 -DynarraySize(gs_dynarray Array) -{ - u64 Result = Array.BuffersCount * Array.ElementsPerBuffer; - return Result; -} -internal gs_dynarray_handle -IndexToHandle(gs_dynarray* Array, u64 Index) -{ - gs_dynarray_handle Result = {0}; - Result.BufferIndex = Index / Array->ElementsPerBuffer; - Result.IndexInBuffer = Index % Array->ElementsPerBuffer; - return Result; -} -internal u64 -HandleToIndex(gs_dynarray Array, gs_dynarray_handle Handle) -{ - u64 Result = Handle.IndexInBuffer + (Handle.BufferIndex * Array.ElementsPerBuffer); - return Result; -} -internal gs_dynarray_handle -TakeFreeElement(gs_dynarray* Array) -{ - gs_dynarray_handle Result = {0}; - if (Array->ElementCount >= DynarraySize(*Array)) - { - GrowDynarray(Array); - } - Assert(Array->ElementCount < DynarraySize(*Array)); - u64 ElementIndex = Array->ElementCount++; - Result = IndexToHandle(Array, ElementIndex); - return Result; -} -internal bool -HandleIsValid(gs_dynarray Array, gs_dynarray_handle Handle) -{ - bool Result = Handle.IndexInBuffer < Array.ElementsPerBuffer; - Result &= Handle.BufferIndex < Array.BuffersCount; - return Result; -} -internal bool -IndexIsValid(gs_dynarray Array, u64 Index) -{ - bool Result = Index < DynarraySize(Array); - return Result; -} -internal gs_data -GetElementInList_(gs_dynarray* Array, gs_dynarray_handle Handle, u64 SizeRequested) -{ - Assert(SizeRequested == Array->ElementSize); - Assert(HandleIsValid(*Array, Handle)); - gs_dynarray_buffer Buffer = Array->Buffers[Handle.BufferIndex]; - - gs_data Result = {0}; - Result.Memory = Buffer.Memory + (Handle.IndexInBuffer * Array->ElementSize); - Result.Size = SizeRequested; - - return Result; -} -internal gs_data -GetElementInList_(gs_dynarray* Array, u64 Index, u64 SizeRequested) -{ - gs_dynarray_handle Handle = IndexToHandle(Array, Index); - return GetElementInList_(Array, Handle, SizeRequested); -} -internal void -FreeDynarray(gs_dynarray* Array) -{ - gs_allocator Allocator = Array->Arena.Allocator; - u64 BufferSize = CalculateBufferSize(*Array); - for (u64 i = 0; i < Array->BuffersCount; i++) - { - AllocatorFree(Allocator, Array->Buffers[i].Memory, BufferSize); - } - AllocatorFree(Allocator, Array->Buffers, sizeof(gs_dynarray_buffer) * Array->BuffersCount); + gs_string Message = PushString(Output.Storage, 1024); + va_list Args; + va_start(Args, Format); + PrintFArgsList(&Message, Format, Args); + NullTerminate(&Message); + Output.Print(Output, Message.ConstString); } #define HandlesAreEqual(ha, hb) ((ha.IndexInBuffer == hb.IndexInBuffer) && (ha.BufferIndex == hb.BufferIndex)) -#define CreateDynarray(allocator,type,elePerBuf) CreateDynarray_((allocator), sizeof(type), (elePerBuf)) -#define GetElement_(array,type,size,indexOrHandle) (type*)GetElementInList_(array, indexOrHandle, size).Memory -#define GetElement(array,type,indexOrHandle) GetElement_(array, type, sizeof(type), indexOrHandle) - /////////////////////////// // // String Builder @@ -3019,19 +2494,19 @@ FreeDynarray(gs_dynarray* Array) internal void GrowStringBuilder_(gs_string_builder* StringBuilder) { - gs_string_builder_buffer* NewBuffer = PushStruct(StringBuilder->Arena, gs_string_builder_buffer); - NewBuffer->String = PushString(StringBuilder->Arena, StringBuilder->BufferSize); - SLLPushOrInit(StringBuilder->Root, StringBuilder->Head, NewBuffer); + gs_string_builder_buffer* NewBuffer = PushStruct(StringBuilder->Arena, gs_string_builder_buffer); + NewBuffer->String = PushString(StringBuilder->Arena, StringBuilder->BufferSize); + SLLPushOrInit(StringBuilder->Root, StringBuilder->Head, NewBuffer); } internal void OutChar(gs_string_builder* Builder, char C) { - if (Builder->Head == 0 || Builder->Head->String.Length >= Builder->Head->String.Size) - { - GrowStringBuilder_(Builder); - } - OutChar(&Builder->Head->String, C); + if (Builder->Head == 0 || Builder->Head->String.Length >= Builder->Head->String.Size) + { + GrowStringBuilder_(Builder); + } + OutChar(&Builder->Head->String, C); } #if 0 @@ -3040,264 +2515,264 @@ OutChar(gs_string_builder* Builder, char C) internal void StringBuilderWriteFArgsList(gs_string_builder* Builder, char* Format, va_list Args) { - char* FormatAt = Format; - while (*FormatAt) + char* FormatAt = Format; + while (*FormatAt) + { + if (FormatAt[0] != '%') { - if (FormatAt[0] != '%') + if (FormatAt[0] == '\\') + { + FormatAt++; + Assert(IsBase8(FormatAt[0]) || // Octal Escape Sequences - \0 is in this set + FormatAt[0] == '\'' || + FormatAt[0] == '\"' || + FormatAt[0] == '\?' || + FormatAt[0] == '\\' || + FormatAt[0] == 'a' || // Audible Bell + FormatAt[0] == 'b' || // Backspace + FormatAt[0] == 'f' || // Form Feed - New Page + FormatAt[0] == 'n' || // Line Feed - New Line + FormatAt[0] == 'r' || // Carriage Return + FormatAt[0] == 't' || // Tab + FormatAt[0] == 'v'); // Vertical Tab + + // Not Handled (see cpp spec) \nnn \xnn \unnnn \Unnnnnnnn + Assert(FormatAt[0] != 'x' || FormatAt[0] != 'u' || FormatAt[0] != 'U'); + + if (IsBase8(FormatAt[0])) { - if (FormatAt[0] == '\\') - { - FormatAt++; - Assert(IsBase8(FormatAt[0]) || // Octal Escape Sequences - \0 is in this set - FormatAt[0] == '\'' || - FormatAt[0] == '\"' || - FormatAt[0] == '\?' || - FormatAt[0] == '\\' || - FormatAt[0] == 'a' || // Audible Bell - FormatAt[0] == 'b' || // Backspace - FormatAt[0] == 'f' || // Form Feed - New Page - FormatAt[0] == 'n' || // Line Feed - New Line - FormatAt[0] == 'r' || // Carriage Return - FormatAt[0] == 't' || // Tab - FormatAt[0] == 'v'); // Vertical Tab - - // Not Handled (see cpp spec) \nnn \xnn \unnnn \Unnnnnnnn - Assert(FormatAt[0] != 'x' || FormatAt[0] != 'u' || FormatAt[0] != 'U'); - - if (IsBase8(FormatAt[0])) - { - // TODO(Peter): this should keep going until it finds a non-octal character code - // but the only one we really need is \0 atm so I'm just handling that one - Assert(FormatAt[0] == '0'); - OutChar(Builder, (char)0); - FormatAt++; - } - else - { - OutChar(Builder, *FormatAt++); - } - } - else - { - OutChar(Builder, *FormatAt++); - } - } - else if (FormatAt[0] == '%' && FormatAt[1] == '%') // Print the % symbol - { - OutChar(Builder, '%'); - FormatAt += 2; + // TODO(Peter): this should keep going until it finds a non-octal character code + // but the only one we really need is \0 atm so I'm just handling that one + Assert(FormatAt[0] == '0'); + OutChar(Builder, (char)0); + FormatAt++; } else { - FormatAt++; - - // Flags - if (FormatAt[0] == '-') - { - FormatAt++; - } - else if (FormatAt[0] == '+') - { - FormatAt++; - } - else if (FormatAt[0] == ' ') - { - FormatAt++; - } - else if (FormatAt[0] == '#') - { - FormatAt++; - } - else if (FormatAt[0] == '0') - { - FormatAt++; - } - - // Width - b32 WidthSpecified = false; - s32 Width = 0; - - if (IsBase10(FormatAt[0])) - { - WidthSpecified = true; - u64 Parsed = 0; - AssertMessage("ParseInt assumes whole string is an integer"); - Width = (s32)ParseInt(FormatAt, 10, &Parsed); - FormatAt += Parsed; - } - else if (FormatAt[0] == '*') - { - WidthSpecified = true; - Width = va_arg(Args, s32); - Assert(Width >= 0); - FormatAt++; - } - - // Precision - b32 PrecisionSpecified = false; - s32 Precision = 0; - - if (FormatAt[0] == '.') - { - FormatAt++; - if (IsBase10(FormatAt[0])) - { - - PrecisionSpecified = true; - u64 Parsed = 0; - AssertMessage("ParseInt assumes whole string is an integer"); - Precision = (s32)ParseInt(FormatAt, 10, &Parsed); - FormatAt += Parsed; - } - else if (FormatAt[0] == '*') - { - PrecisionSpecified = true; - Precision = va_arg(Args, s32); - Assert(Precision >= 0); - FormatAt++; - } - } - - // Length - b32 LengthSpecified = false; - s32 Length = 4; - - if (FormatAt[0] == 'h' && FormatAt[1] == 'h') - { - LengthSpecified = true; - LengthSpecified = 1; - FormatAt += 2; - } - else if (FormatAt[0] == 'h') - { - LengthSpecified = true; - LengthSpecified = 2; - FormatAt++; - } - else if (FormatAt[0] == 'l' && FormatAt[1] == 'l') - { - LengthSpecified = true; - LengthSpecified = 8; - FormatAt += 2; - } - else if (FormatAt[0] == 'l') - { - LengthSpecified = true; - LengthSpecified = 4; - FormatAt++; - } - else if (FormatAt[0] == 'j') - { - LengthSpecified = true; - LengthSpecified = 8; - FormatAt++; - } - else if (FormatAt[0] == 'z') - { - FormatAt++; - } - else if (FormatAt[0] == 't') - { - FormatAt++; - } - else if (FormatAt[0] == 'L') - { - FormatAt++; - } - - // Format Specifiers - gs_string StringRemaining = GetStringAfter(*String, String->Length); - Assert(StringRemaining.Length == 0); - if (FormatAt[0] == 'd' || FormatAt[0] == 'i') - { - s64 SignedInt = ReadVarArgsSignedInteger(Length, &Args); - if (SignedInt < 0) - { - OutChar(&StringRemaining, '-'); - SignedInt *= -1; - } - U64ToASCII(&StringRemaining, (u64)SignedInt, 10, Base10Chars); - } - else if (FormatAt[0] == 'u') - { - u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); - U64ToASCII(&StringRemaining, UnsignedInt, 10, Base10Chars); - } - else if (FormatAt[0] == 'o') - { - u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); - U64ToASCII(&StringRemaining, UnsignedInt, 8, Base8Chars); - } - else if (FormatAt[0] == 'x' || FormatAt[0] == 'X') - { - u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); - U64ToASCII(&StringRemaining, UnsignedInt, 16, Base16Chars); - } - else if (FormatAt[0] == 'f' || FormatAt[0] == 'F') - { - r64 Float = ReadVarArgsFloat(Length, &Args); - s32 AfterPoint = 6; - if (PrecisionSpecified) - { - AfterPoint = Precision; - } - R64ToASCII(&StringRemaining, Float, AfterPoint); - } - else if (FormatAt[0] == 'c') - { - char InsertChar = va_arg(Args, s32); - OutChar(&StringRemaining, InsertChar); - } - else if (FormatAt[0] == 's') - { - char* InsertString = va_arg(Args, char*); - - s32 InsertStringLength = CStringLength(InsertString); - if (PrecisionSpecified) - { - InsertStringLength = Min(InsertStringLength, Precision); - } - InsertStringLength = Min(StringSizeLeft(StringRemaining), InsertStringLength); - - for (s32 c = 0; c < InsertStringLength; c++) - { - OutChar(&StringRemaining, InsertString[c]); - } - } - else if (FormatAt[0] == 'S') - { - gs_const_string InsertString = va_arg(Args, gs_const_string); - - for (s32 c = 0; c < InsertString.Length; c++) - { - OutChar(&StringRemaining, InsertString.Str[c]); - } - } - else if (FormatAt[0] == 'p') - { - // TODO(Peter): Pointer Address - } - else - { - // NOTE(Peter): Non-specifier character found - InvalidCodePath; - } - - String->Length += StringRemaining.Length; - FormatAt++; + OutChar(Builder, *FormatAt++); } + } + else + { + OutChar(Builder, *FormatAt++); + } } - - return String->Length; + else if (FormatAt[0] == '%' && FormatAt[1] == '%') // Print the % symbol + { + OutChar(Builder, '%'); + FormatAt += 2; + } + else + { + FormatAt++; + + // Flags + if (FormatAt[0] == '-') + { + FormatAt++; + } + else if (FormatAt[0] == '+') + { + FormatAt++; + } + else if (FormatAt[0] == ' ') + { + FormatAt++; + } + else if (FormatAt[0] == '#') + { + FormatAt++; + } + else if (FormatAt[0] == '0') + { + FormatAt++; + } + + // Width + b32 WidthSpecified = false; + s32 Width = 0; + + if (IsBase10(FormatAt[0])) + { + WidthSpecified = true; + u64 Parsed = 0; + AssertMessage("ParseInt assumes whole string is an integer"); + Width = (s32)ParseInt(FormatAt, 10, &Parsed); + FormatAt += Parsed; + } + else if (FormatAt[0] == '*') + { + WidthSpecified = true; + Width = va_arg(Args, s32); + Assert(Width >= 0); + FormatAt++; + } + + // Precision + b32 PrecisionSpecified = false; + s32 Precision = 0; + + if (FormatAt[0] == '.') + { + FormatAt++; + if (IsBase10(FormatAt[0])) + { + + PrecisionSpecified = true; + u64 Parsed = 0; + AssertMessage("ParseInt assumes whole string is an integer"); + Precision = (s32)ParseInt(FormatAt, 10, &Parsed); + FormatAt += Parsed; + } + else if (FormatAt[0] == '*') + { + PrecisionSpecified = true; + Precision = va_arg(Args, s32); + Assert(Precision >= 0); + FormatAt++; + } + } + + // Length + b32 LengthSpecified = false; + s32 Length = 4; + + if (FormatAt[0] == 'h' && FormatAt[1] == 'h') + { + LengthSpecified = true; + LengthSpecified = 1; + FormatAt += 2; + } + else if (FormatAt[0] == 'h') + { + LengthSpecified = true; + LengthSpecified = 2; + FormatAt++; + } + else if (FormatAt[0] == 'l' && FormatAt[1] == 'l') + { + LengthSpecified = true; + LengthSpecified = 8; + FormatAt += 2; + } + else if (FormatAt[0] == 'l') + { + LengthSpecified = true; + LengthSpecified = 4; + FormatAt++; + } + else if (FormatAt[0] == 'j') + { + LengthSpecified = true; + LengthSpecified = 8; + FormatAt++; + } + else if (FormatAt[0] == 'z') + { + FormatAt++; + } + else if (FormatAt[0] == 't') + { + FormatAt++; + } + else if (FormatAt[0] == 'L') + { + FormatAt++; + } + + // Format Specifiers + gs_string StringRemaining = GetStringAfter(*String, String->Length); + Assert(StringRemaining.Length == 0); + if (FormatAt[0] == 'd' || FormatAt[0] == 'i') + { + s64 SignedInt = ReadVarArgsSignedInteger(Length, &Args); + if (SignedInt < 0) + { + OutChar(&StringRemaining, '-'); + SignedInt *= -1; + } + U64ToASCII(&StringRemaining, (u64)SignedInt, 10, Base10Chars); + } + else if (FormatAt[0] == 'u') + { + u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + U64ToASCII(&StringRemaining, UnsignedInt, 10, Base10Chars); + } + else if (FormatAt[0] == 'o') + { + u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + U64ToASCII(&StringRemaining, UnsignedInt, 8, Base8Chars); + } + else if (FormatAt[0] == 'x' || FormatAt[0] == 'X') + { + u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + U64ToASCII(&StringRemaining, UnsignedInt, 16, Base16Chars); + } + else if (FormatAt[0] == 'f' || FormatAt[0] == 'F') + { + r64 Float = ReadVarArgsFloat(Length, &Args); + s32 AfterPoint = 6; + if (PrecisionSpecified) + { + AfterPoint = Precision; + } + R64ToASCII(&StringRemaining, Float, AfterPoint); + } + else if (FormatAt[0] == 'c') + { + char InsertChar = va_arg(Args, s32); + OutChar(&StringRemaining, InsertChar); + } + else if (FormatAt[0] == 's') + { + char* InsertString = va_arg(Args, char*); + + s32 InsertStringLength = CStringLength(InsertString); + if (PrecisionSpecified) + { + InsertStringLength = Min(InsertStringLength, Precision); + } + InsertStringLength = Min(StringSizeLeft(StringRemaining), InsertStringLength); + + for (s32 c = 0; c < InsertStringLength; c++) + { + OutChar(&StringRemaining, InsertString[c]); + } + } + else if (FormatAt[0] == 'S') + { + gs_const_string InsertString = va_arg(Args, gs_const_string); + + for (s32 c = 0; c < InsertString.Length; c++) + { + OutChar(&StringRemaining, InsertString.Str[c]); + } + } + else if (FormatAt[0] == 'p') + { + // TODO(Peter): Pointer Address + } + else + { + // NOTE(Peter): Non-specifier character found + InvalidCodePath; + } + + String->Length += StringRemaining.Length; + FormatAt++; + } + } + + return String->Length; } internal void StringBuilderWriteF(gs_string_builder* Builder, char* Format, ...) { - va_list Args; - va_start(Args, Format); - StringBuilderWriteFArgsList(Builder, Format, Args); - va_end(Args); + va_list Args; + va_start(Args, Format); + StringBuilderWriteFArgsList(Builder, Format, Args); + va_end(Args); } #endif // String builder @@ -3309,25 +2784,25 @@ StringBuilderWriteF(gs_string_builder* Builder, char* Format, ...) internal u64 FileHandlerGetFileInfo_NoOp(gs_file_handler FileHandler, gs_const_string Path) { - return 0; + return 0; } internal gs_file FileHandlerReadFile_NoOp(gs_const_string Path) { - return gs_file{0}; + return gs_file{0}; } internal bool FileHandlerWriteFile_NoOp(gs_const_string Path, gs_data Data) { - return false; + return false; } internal gs_const_string_array FileHandlerEnumerateDirectory_NoOp(gs_const_string Path, bool Recursive, bool IncludeDirs) { - return gs_const_string_array{0}; + return gs_const_string_array{0}; } internal gs_file_handler @@ -3337,110 +2812,110 @@ CreateFileHandler(file_handler_get_file_info* GetFileInfo, file_handler_enumerate_directory* EnumerateDirectory, gs_memory_arena* Transient) { - if (GetFileInfo == 0) - { - GetFileInfo = (file_handler_get_file_info*)FileHandlerGetFileInfo_NoOp; - } - if (ReadEntireFile == 0) - { - ReadEntireFile = (file_handler_read_entire_file*)FileHandlerReadFile_NoOp; - } - if (WriteEntireFile == 0) - { - WriteEntireFile = (file_handler_write_entire_file*)FileHandlerWriteFile_NoOp; - } - if (EnumerateDirectory == 0) - { - EnumerateDirectory = (file_handler_enumerate_directory*)FileHandlerEnumerateDirectory_NoOp; - } - gs_file_handler Result = {0}; - Result.GetFileInfo = GetFileInfo; - Result.ReadEntireFile = ReadEntireFile; - Result.WriteEntireFile = WriteEntireFile; - Result.EnumerateDirectory = EnumerateDirectory; - Result.Transient = Transient; - - return Result; + if (GetFileInfo == 0) + { + GetFileInfo = (file_handler_get_file_info*)FileHandlerGetFileInfo_NoOp; + } + if (ReadEntireFile == 0) + { + ReadEntireFile = (file_handler_read_entire_file*)FileHandlerReadFile_NoOp; + } + if (WriteEntireFile == 0) + { + WriteEntireFile = (file_handler_write_entire_file*)FileHandlerWriteFile_NoOp; + } + if (EnumerateDirectory == 0) + { + EnumerateDirectory = (file_handler_enumerate_directory*)FileHandlerEnumerateDirectory_NoOp; + } + gs_file_handler Result = {0}; + Result.GetFileInfo = GetFileInfo; + Result.ReadEntireFile = ReadEntireFile; + Result.WriteEntireFile = WriteEntireFile; + Result.EnumerateDirectory = EnumerateDirectory; + Result.Transient = Transient; + + return Result; } internal gs_const_string GetNullTerminatedPath(gs_file_handler FileHandler, gs_const_string Path) { - gs_const_string Result = {}; - if (!IsNullTerminated(Path)) - { - gs_string NullTermPath = PushString(FileHandler.Transient, Path.Length + 1); - PrintF(&NullTermPath, "%S", Path); - NullTerminate(&NullTermPath); - Result = NullTermPath.ConstString; - } - else - { - Result = Path; - } - return Result; + gs_const_string Result = {}; + if (!IsNullTerminated(Path)) + { + gs_string NullTermPath = PushString(FileHandler.Transient, Path.Length + 1); + PrintF(&NullTermPath, "%S", Path); + NullTerminate(&NullTermPath); + Result = NullTermPath.ConstString; + } + else + { + Result = Path; + } + return Result; } internal gs_file_info GetFileInfo(gs_file_handler FileHandler, gs_const_string Path) { - Assert(FileHandler.GetFileInfo != 0); - - Path = GetNullTerminatedPath(FileHandler, Path); - gs_file_info Result = FileHandler.GetFileInfo(FileHandler, Path); - return Result; + Assert(FileHandler.GetFileInfo != 0); + + Path = GetNullTerminatedPath(FileHandler, Path); + gs_file_info Result = FileHandler.GetFileInfo(FileHandler, Path); + return Result; } internal gs_file ReadEntireFile(gs_file_handler FileHandler, gs_const_string Path, gs_data Memory) { - Assert(FileHandler.ReadEntireFile != 0); - - Path = GetNullTerminatedPath(FileHandler, Path); - gs_file Result = FileHandler.ReadEntireFile(FileHandler, Path, Memory); - return Result; + Assert(FileHandler.ReadEntireFile != 0); + + Path = GetNullTerminatedPath(FileHandler, Path); + gs_file Result = FileHandler.ReadEntireFile(FileHandler, Path, Memory); + return Result; } internal gs_file ReadEntireFile(gs_file_handler FileHandler, gs_const_string Path) { - Assert(FileHandler.GetFileInfo != 0); - Assert(FileHandler.ReadEntireFile != 0); - - Path = GetNullTerminatedPath(FileHandler, Path); - gs_file Result = {0}; - gs_file_info FileInfo = FileHandler.GetFileInfo(FileHandler, Path); - if (FileInfo.FileSize > 0) - { - gs_data FileMemory = PushSizeToData(FileHandler.Transient, FileInfo.FileSize); - Result = ReadEntireFile(FileHandler, Path, FileMemory); - } - return Result; + Assert(FileHandler.GetFileInfo != 0); + Assert(FileHandler.ReadEntireFile != 0); + + Path = GetNullTerminatedPath(FileHandler, Path); + gs_file Result = {0}; + gs_file_info FileInfo = FileHandler.GetFileInfo(FileHandler, Path); + if (FileInfo.FileSize > 0) + { + gs_data FileMemory = PushSize(FileHandler.Transient, FileInfo.FileSize); + Result = ReadEntireFile(FileHandler, Path, FileMemory); + } + return Result; } internal bool WriteEntireFile(gs_file_handler FileHandler, gs_const_string Path, gs_data Memory) { - Assert(FileHandler.WriteEntireFile != 0); - - Path = GetNullTerminatedPath(FileHandler, Path); - return FileHandler.WriteEntireFile(FileHandler, Path, Memory); + Assert(FileHandler.WriteEntireFile != 0); + + Path = GetNullTerminatedPath(FileHandler, Path); + return FileHandler.WriteEntireFile(FileHandler, Path, Memory); } internal gs_file_info_array EnumerateDirectory(gs_file_handler FileHandler, gs_memory_arena* Storage, gs_const_string Path, u32 Flags) { - Assert(FileHandler.EnumerateDirectory != 0); - - Path = GetNullTerminatedPath(FileHandler, Path); - return FileHandler.EnumerateDirectory(FileHandler, Storage, Path, Flags); + Assert(FileHandler.EnumerateDirectory != 0); + + Path = GetNullTerminatedPath(FileHandler, Path); + return FileHandler.EnumerateDirectory(FileHandler, Storage, Path, Flags); } internal bool FileNoError(gs_file File) { - bool Result = (File.Size > 0); - return Result; + bool Result = (File.Size > 0); + return Result; } ////////////////////////// @@ -3450,15 +2925,15 @@ FileNoError(gs_file File) internal s64 TimeHandlerGetWallClock(gs_time_handler TimeHandler) { - s64 Result = TimeHandler.GetWallClock(); - return Result; + s64 Result = TimeHandler.GetWallClock(); + return Result; } internal s64 TimeHandlerGetSecondsElapsed(gs_time_handler TimeHandler, s64 StartCycles, s64 EndCycles) { - s64 Result = TimeHandler.GetSecondsElapsed(StartCycles, EndCycles); - return Result; + s64 Result = TimeHandler.GetSecondsElapsed(StartCycles, EndCycles); + return Result; } ////////////////////////// @@ -3467,71 +2942,71 @@ TimeHandlerGetSecondsElapsed(gs_time_handler TimeHandler, s64 StartCycles, s64 E CREATE_THREAD(CreateThreadStub) { - return {}; + return {}; } KILL_THREAD(KillThreadStub) { - return false; + return false; } internal platform_thread_manager CreatePlatformThreadManager(platform_create_thread* CreateThreadProc, platform_kill_thread* KillThreadProc) { - platform_thread_manager Result = {}; - Result.CreateThreadProc = CreateThreadProc; - Result.KillThreadProc = KillThreadProc; - - if (!CreateThreadProc) - { - Result.CreateThreadProc = CreateThreadStub; - } - if (!KillThreadProc) - { - Result.KillThreadProc = KillThreadStub; - } - - return Result; + platform_thread_manager Result = {}; + Result.CreateThreadProc = CreateThreadProc; + Result.KillThreadProc = KillThreadProc; + + if (!CreateThreadProc) + { + Result.CreateThreadProc = CreateThreadStub; + } + if (!KillThreadProc) + { + Result.KillThreadProc = KillThreadStub; + } + + return Result; } internal platform_thread_handle CreateThread(platform_thread_manager* Manager, thread_proc_* Proc, u8* Arg, gs_thread_context Ctx) { - platform_thread_handle Result = {}; - - for (u32 i = 1; i < THREADS_MAX; i++) + platform_thread_handle Result = {}; + + for (u32 i = 1; i < THREADS_MAX; i++) + { + if (!Manager->ThreadsUsed[i]) { - if (!Manager->ThreadsUsed[i]) - { - Result.Index = i; - break; - } + Result.Index = i; + break; } - Assert(Result.Index != 0); - - Manager->ThreadsUsed[Result.Index] = true; - Manager->CreateThreadProc(&Manager->Threads[Result.Index], Proc, Arg, Ctx); - - return Result; + } + Assert(Result.Index != 0); + + Manager->ThreadsUsed[Result.Index] = true; + Manager->CreateThreadProc(&Manager->Threads[Result.Index], Proc, Arg, Ctx); + + return Result; } internal bool KillThread(platform_thread_manager* Manager, platform_thread_handle Handle) { - Assert(Manager->ThreadsUsed[Handle.Index]); - - platform_thread* Thread = &Manager->Threads[Handle.Index]; - bool Result = Manager->KillThreadProc(Thread); - - if (Result) - { - Manager->ThreadsUsed[Handle.Index] = false; - Manager->Threads[Handle.Index] = {}; - } - - return Result; + Assert(Manager->ThreadsUsed[Handle.Index]); + + platform_thread* Thread = &Manager->Threads[Handle.Index]; + bool Result = Manager->KillThreadProc(Thread); + + if (Result) + { + Manager->ThreadsUsed[Handle.Index] = false; + Manager->Threads[Handle.Index] = {}; + } + + return Result; } @@ -3541,32 +3016,32 @@ KillThread(platform_thread_manager* Manager, platform_thread_handle Handle) CONNECT_SOCKET(PlatformConnectSocket_Stub) { - return false; + return false; } CLOSE_SOCKET(PlatformCloseSocket_Stub) { - return false; + return false; } SOCKET_QUERY_STATUS(PlatformSocketQueryStatus_Stub) { - return false; + return false; } SOCKET_PEEK(PlatformSocketPeek_Stub) { - return 0; + return 0; } SOCKET_RECEIVE(PlatformSocketRecieve_Stub) { - return {}; + return {}; } SOCKET_SEND(PlatformSocketSend_Stub) { - return false; + return false; } internal platform_socket_manager @@ -3577,194 +3052,194 @@ CreatePlatformSocketManager(platform_connect_socket* ConnectSocketProc, platform_socket_receive* SocketRecieveProc, platform_socket_send* SocketSendProc) { - platform_socket_manager Result = {}; - Result.ConnectSocketProc = ConnectSocketProc; - Result.CloseSocketProc = CloseSocketProc; - Result.SocketQueryStatusProc = SocketQueryStatusProc; - Result.SocketPeekProc = SocketPeekProc; - Result.SocketRecieveProc = SocketRecieveProc; - Result.SocketSendProc = SocketSendProc; - - if (!ConnectSocketProc) - { - Result.ConnectSocketProc = PlatformConnectSocket_Stub; - } - if (!CloseSocketProc) - { - Result.CloseSocketProc = PlatformCloseSocket_Stub; - } - if (!SocketQueryStatusProc) - { - Result.SocketQueryStatusProc = PlatformSocketQueryStatus_Stub; - } - if (!SocketPeekProc) - { - Result.SocketPeekProc = PlatformSocketPeek_Stub; - } - if (!SocketRecieveProc) - { - Result.SocketRecieveProc = PlatformSocketRecieve_Stub; - } - if (!SocketSendProc) - { - Result.SocketSendProc = PlatformSocketSend_Stub; - } - return Result; + platform_socket_manager Result = {}; + Result.ConnectSocketProc = ConnectSocketProc; + Result.CloseSocketProc = CloseSocketProc; + Result.SocketQueryStatusProc = SocketQueryStatusProc; + Result.SocketPeekProc = SocketPeekProc; + Result.SocketRecieveProc = SocketRecieveProc; + Result.SocketSendProc = SocketSendProc; + + if (!ConnectSocketProc) + { + Result.ConnectSocketProc = PlatformConnectSocket_Stub; + } + if (!CloseSocketProc) + { + Result.CloseSocketProc = PlatformCloseSocket_Stub; + } + if (!SocketQueryStatusProc) + { + Result.SocketQueryStatusProc = PlatformSocketQueryStatus_Stub; + } + if (!SocketPeekProc) + { + Result.SocketPeekProc = PlatformSocketPeek_Stub; + } + if (!SocketRecieveProc) + { + Result.SocketRecieveProc = PlatformSocketRecieve_Stub; + } + if (!SocketSendProc) + { + Result.SocketSendProc = PlatformSocketSend_Stub; + } + return Result; } internal bool SocketHandleIsValid(platform_socket_handle_ Handle) { - return Handle.Index != 0; + return Handle.Index != 0; } internal platform_socket* SocketManagerGetSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle) { - platform_socket* Result = 0; - if (Manager->SocketsUsed[Handle.Index]) + platform_socket* Result = 0; + if (Manager->SocketsUsed[Handle.Index]) + { + platform_socket* Socket = &Manager->Sockets[Handle.Index]; + if (Socket->PlatformHandle != 0) { - platform_socket* Socket = &Manager->Sockets[Handle.Index]; - if (Socket->PlatformHandle != 0) - { - Result = Socket; - } + Result = Socket; } - return Result; + } + return Result; } internal bool ConnectSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle) { - bool Result = false; - platform_socket* Socket = SocketManagerGetSocket(Manager, Handle); - if (Socket) - { - Result = Manager->ConnectSocketProc(Manager, Socket); - } - return Result; + bool Result = false; + platform_socket* Socket = SocketManagerGetSocket(Manager, Handle); + if (Socket) + { + Result = Manager->ConnectSocketProc(Manager, Socket); + } + return Result; } internal bool RemoveSocket (platform_socket_manager* Manager, platform_socket_handle_ Handle) { - bool Result = Manager->SocketsUsed[Handle.Index]; - Manager->SocketsUsed[Handle.Index] = false; - return Result; + bool Result = Manager->SocketsUsed[Handle.Index]; + Manager->SocketsUsed[Handle.Index] = false; + return Result; } internal platform_socket_handle_ CreateSocket(platform_socket_manager* Manager, char* Addr, char* Port) { - platform_socket_handle_ Result = {}; - for (u32 i = 1; i < SOCKETS_COUNT_MAX; i++) + platform_socket_handle_ Result = {}; + for (u32 i = 1; i < SOCKETS_COUNT_MAX; i++) + { + if (!Manager->SocketsUsed[i]) { - if (!Manager->SocketsUsed[i]) - { - Result.Index = i; - Manager->SocketsUsed[i] = true; - break; - } + Result.Index = i; + Manager->SocketsUsed[i] = true; + break; } - - Assert(Result.Index != 0); - platform_socket* Socket = &Manager->Sockets[Result.Index]; - Socket->Handle = Result; - CopyArray(Addr, Socket->Addr, char, CStringLength(Addr) + 1); - CopyArray(Port, Socket->Port, char, CStringLength(Port) + 1); - - bool Success = Manager->ConnectSocketProc(Manager, Socket); - if (!Success) + } + + Assert(Result.Index != 0); + platform_socket* Socket = &Manager->Sockets[Result.Index]; + Socket->Handle = Result; + CopyArray(Addr, Socket->Addr, char, CStringLength(Addr) + 1); + CopyArray(Port, Socket->Port, char, CStringLength(Port) + 1); + + bool Success = Manager->ConnectSocketProc(Manager, Socket); + if (!Success) + { + if (RemoveSocket(Manager, Result)) { - if (RemoveSocket(Manager, Result)) - { - Result = {}; - } - else - { - InvalidCodePath; - } + Result = {}; } - - return Result; + else + { + InvalidCodePath; + } + } + + return Result; } internal bool CloseSocket(platform_socket_manager* Manager, platform_socket* Socket) { - bool Result = false; - if (Socket) + bool Result = false; + if (Socket) + { + if (Manager->CloseSocketProc(Manager, Socket)) { - if (Manager->CloseSocketProc(Manager, Socket)) - { - RemoveSocket(Manager, Socket->Handle); - *Socket = {}; - Result = true; - } - else - { - InvalidCodePath; - } + RemoveSocket(Manager, Socket->Handle); + *Socket = {}; + Result = true; } - return Result; + else + { + InvalidCodePath; + } + } + return Result; } internal bool CloseSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle) { - bool Result = false; - platform_socket* Socket = SocketManagerGetSocket(Manager, Handle); - return CloseSocket(Manager, Socket); + bool Result = false; + platform_socket* Socket = SocketManagerGetSocket(Manager, Handle); + return CloseSocket(Manager, Socket); } // NOTE(pjs): returns true if the socket is connected internal bool SocketQueryStatus(platform_socket_manager* Manager, platform_socket_handle_ SocketHandle) { - bool Result = false; - platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); - if (Socket) - { - Result = Manager->SocketQueryStatusProc(Manager, Socket); - } - return Result; + bool Result = false; + platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); + if (Socket) + { + Result = Manager->SocketQueryStatusProc(Manager, Socket); + } + return Result; } internal u32 SocketPeek(platform_socket_manager* Manager, platform_socket_handle_ SocketHandle) { - u32 Result = 0; - platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); - if (Socket) - { - Result = Manager->SocketPeekProc(Manager, Socket); - } - return Result; + u32 Result = 0; + platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); + if (Socket) + { + Result = Manager->SocketPeekProc(Manager, Socket); + } + return Result; } internal gs_data SocketRecieve(platform_socket_manager* Manager, platform_socket_handle_ SocketHandle, gs_memory_arena* Storage) { - gs_data Result = {}; - platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); - if (Socket) - { - Result = Manager->SocketRecieveProc(Manager, Socket, Storage); - } - return Result; + gs_data Result = {}; + platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); + if (Socket) + { + Result = Manager->SocketRecieveProc(Manager, Socket, Storage); + } + return Result; } internal bool SocketSend(platform_socket_manager* Manager, platform_socket_handle_ SocketHandle, u32 Address, u32 Port, gs_data Data, s32 Flags) { - bool Result = false; - platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); - if (Socket) - { - s32 SizeSent = Manager->SocketSendProc(Manager, Socket, Address, Port, Data, Flags); - Result = (SizeSent == Data.Size); - } - return Result; + bool Result = false; + platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); + if (Socket) + { + s32 SizeSent = Manager->SocketSendProc(Manager, Socket, Address, Port, Data, Flags); + Result = (SizeSent == Data.Size); + } + return Result; } /////////////////////////// @@ -3774,83 +3249,83 @@ SocketSend(platform_socket_manager* Manager, platform_socket_handle_ SocketHandl internal u32 HashAppendDJB2ToU32(u32 Hash, u8 Byte) { - u32 Result = Hash; - if (Result == 0) { Result = 5381; } - Result = ((Result << 5) + Result) + Byte; - return Result; + u32 Result = Hash; + if (Result == 0) { Result = 5381; } + Result = ((Result << 5) + Result) + Byte; + return Result; } internal u64 HashAppendDJB2ToU32(u64 Hash, u8 Byte) { - u64 Result = Hash; - if (Result == 0) { Result = 5381; } - Result = ((Result << 5) + Result) + Byte; - return Result; + u64 Result = Hash; + if (Result == 0) { Result = 5381; } + Result = ((Result << 5) + Result) + Byte; + return Result; } internal u32 HashDJB2ToU32(char* String) { - u32 Hash = 5381; - char* C = String; - while(*C) - { - Hash = ((Hash << 5) + Hash) + *C++; - } - return Hash; + u32 Hash = 5381; + char* C = String; + while(*C) + { + Hash = ((Hash << 5) + Hash) + *C++; + } + return Hash; } internal u32 HashDJB2ToU32(u32 Length, char* String) { - u32 Hash = 5381; - for (u32 i = 0; i < Length; i++) - { - Hash = ((Hash << 5) + Hash) + String[i]; - } - return Hash; + u32 Hash = 5381; + for (u32 i = 0; i < Length; i++) + { + Hash = ((Hash << 5) + Hash) + String[i]; + } + return Hash; } internal u32 HashDJB2ToU32(gs_const_string Str) { - return HashDJB2ToU32(StringExpand(Str)); + return HashDJB2ToU32(StringExpand(Str)); } internal u32 HashDJB2ToU32(gs_string Str) { - return HashDJB2ToU32(StringExpand(Str)); + return HashDJB2ToU32(StringExpand(Str)); } internal u64 HashDJB2ToU64(char* String) { - u64 Hash = 5381; - char* C = String; - while(*C) - { - Hash = ((Hash << 5) + Hash) + *C++; - } - return Hash; + u64 Hash = 5381; + char* C = String; + while(*C) + { + Hash = ((Hash << 5) + Hash) + *C++; + } + return Hash; } internal u64 HashDJB2ToU64(u32 Length, char* String) { - u64 Hash = 5381; - for (u32 i = 0; i < Length; i++) - { - Hash = ((Hash << 5) + Hash) + String[i]; - } - return Hash; + u64 Hash = 5381; + for (u32 i = 0; i < Length; i++) + { + Hash = ((Hash << 5) + Hash) + String[i]; + } + return Hash; } internal u64 HashDJB2ToU64(gs_const_string Str) { - return HashDJB2ToU64(StringExpand(Str)); + return HashDJB2ToU64(StringExpand(Str)); } internal u64 HashDJB2ToU64(gs_string Str) { - return HashDJB2ToU64(StringExpand(Str)); + return HashDJB2ToU64(StringExpand(Str)); } /////////////////////////// @@ -3860,36 +3335,36 @@ HashDJB2ToU64(gs_string Str) internal gs_random_series InitRandomSeries(u32 Seed) { - gs_random_series Result = {0}; - Result.Value = Seed; - return Result; + gs_random_series Result = {0}; + Result.Value = Seed; + return Result; } internal u32 NextRandom(gs_random_series* Series) { - u32 Result = Series->Value; - Result ^= Result << 13; - Result ^= Result >> 17; - Result ^= Result << 5; - Series->Value = Result; - return Result; + u32 Result = Series->Value; + Result ^= Result << 13; + Result ^= Result >> 17; + Result ^= Result << 5; + Series->Value = Result; + return Result; } internal r32 NextRandomUnilateral(gs_random_series* Series) { - r32 Result = (r32)NextRandom(Series) / (r32)UINT32_MAX; - return Result; + r32 Result = (r32)NextRandom(Series) / (r32)UINT32_MAX; + return Result; } internal r32 NextRandomBilateral(gs_random_series* Series) { - r32 Result = (r32)NextRandom(Series); - Result = Result / (r32)0xFFFFFFFF; - Result = (Result * 2.0f) - 1.0f; - return Result; + r32 Result = (r32)NextRandom(Series); + Result = Result / (r32)0xFFFFFFFF; + Result = (Result * 2.0f) - 1.0f; + return Result; } @@ -3901,62 +3376,62 @@ NextRandomBilateral(gs_random_series* Series) static void RadixSortInPlace_ (gs_radix_list* List, u32 Start, u32 End, u32 Iteration) { - u32 Shift = Iteration; - u32 ZerosBoundary = Start; - u32 OnesBoundary = End - 1; - - for (u32 d = Start; d < End; d++) + u32 Shift = Iteration; + u32 ZerosBoundary = Start; + u32 OnesBoundary = End - 1; + + for (u32 d = Start; d < End; d++) + { + u64 CurrentIndex = ZerosBoundary; + u64 Radix = List->Radixes.Values[CurrentIndex]; + u64 Place = (Radix >> Shift) & 0x1; + if (Place) { - u64 CurrentIndex = ZerosBoundary; - u64 Radix = List->Radixes.Values[CurrentIndex]; - u64 Place = (Radix >> Shift) & 0x1; - if (Place) - { - u64 EvictedIndex = OnesBoundary; - u64 EvictedRadix = List->Radixes.Values[EvictedIndex]; - u64 EvictedID = List->IDs.Values[EvictedIndex]; - - List->Radixes.Values[EvictedIndex] = Radix; - List->IDs.Values[EvictedIndex] = List->IDs.Values[CurrentIndex]; - - List->Radixes.Values[CurrentIndex] = EvictedRadix; - List->IDs.Values[CurrentIndex] = EvictedID; - - OnesBoundary -= 1; - } - else - { - ZerosBoundary += 1; - } + u64 EvictedIndex = OnesBoundary; + u64 EvictedRadix = List->Radixes.Values[EvictedIndex]; + u64 EvictedID = List->IDs.Values[EvictedIndex]; + + List->Radixes.Values[EvictedIndex] = Radix; + List->IDs.Values[EvictedIndex] = List->IDs.Values[CurrentIndex]; + + List->Radixes.Values[CurrentIndex] = EvictedRadix; + List->IDs.Values[CurrentIndex] = EvictedID; + + OnesBoundary -= 1; } - - if (Iteration > 0) + else { - RadixSortInPlace_(List, Start, ZerosBoundary, Iteration - 1); - RadixSortInPlace_(List, ZerosBoundary, End, Iteration - 1); + ZerosBoundary += 1; } + } + + if (Iteration > 0) + { + RadixSortInPlace_(List, Start, ZerosBoundary, Iteration - 1); + RadixSortInPlace_(List, ZerosBoundary, End, Iteration - 1); + } } static void RadixSortInPlace (gs_radix_list* List) { - u32 Highest = 0; - for (u32 i = 0; i < List->Radixes.Count; i++) + u32 Highest = 0; + for (u32 i = 0; i < List->Radixes.Count; i++) + { + if (List->Radixes.Values[i] > Highest) { - if (List->Radixes.Values[i] > Highest) - { - Highest = List->Radixes.Values[i]; - } + Highest = List->Radixes.Values[i]; } - - u32 Iterations = 0; - while (Highest > 1) - { - ++Iterations; - Highest = Highest >> 1; - } - - RadixSortInPlace_(List, 0, List->Radixes.Count, Iterations); + } + + u32 Iterations = 0; + while (Highest > 1) + { + ++Iterations; + Highest = Highest >> 1; + } + + RadixSortInPlace_(List, 0, List->Radixes.Count, Iterations); } @@ -3967,105 +3442,105 @@ RadixSortInPlace (gs_radix_list* List) inline bool KeyIsMouseButton(gs_key Key) { - bool Result = (Key >= gs_Key_MouseLeftButton); - Result = Result && Key <= gs_Key_MouseRightButton; - return Result; + bool Result = (Key >= gs_Key_MouseLeftButton); + Result = Result && Key <= gs_Key_MouseRightButton; + return Result; } inline u32 GetMouseButtonIndex(gs_key Button) { - Assert(KeyIsMouseButton(Button)); - u32 Result = Button - gs_Key_MouseLeftButton; - return Result; + Assert(KeyIsMouseButton(Button)); + u32 Result = Button - gs_Key_MouseLeftButton; + return Result; } inline bool MouseButtonTransitionedDown(gs_mouse_state Mouse, u32 Index) { - bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0; - bool WasDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_WasDownBit)) != 0; - return IsDown && !WasDown; + bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0; + bool WasDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_WasDownBit)) != 0; + return IsDown && !WasDown; } inline bool MouseButtonTransitionedDown(gs_mouse_state Mouse, gs_key Button) { - u32 Index = GetMouseButtonIndex(Button); - return MouseButtonTransitionedDown(Mouse, Index); + u32 Index = GetMouseButtonIndex(Button); + return MouseButtonTransitionedDown(Mouse, Index); } inline bool MouseButtonIsDown(gs_mouse_state Mouse, u32 Index) { - bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0; - return IsDown; + bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0; + return IsDown; } inline bool MouseButtonIsDown(gs_mouse_state Mouse, gs_key Button) { - u32 Index = GetMouseButtonIndex(Button); - return MouseButtonIsDown(Mouse, Index); + u32 Index = GetMouseButtonIndex(Button); + return MouseButtonIsDown(Mouse, Index); } inline bool MouseButtonTransitionedUp(gs_mouse_state Mouse, u32 Index) { - bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0; - bool WasDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_WasDownBit)) != 0; - return !IsDown && WasDown; + bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0; + bool WasDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_WasDownBit)) != 0; + return !IsDown && WasDown; } inline bool MouseButtonTransitionedUp(gs_mouse_state Mouse, gs_key Button) { - u32 Index = GetMouseButtonIndex(Button); - return MouseButtonTransitionedUp(Mouse, Index); + u32 Index = GetMouseButtonIndex(Button); + return MouseButtonTransitionedUp(Mouse, Index); } inline bool MouseButtonIsUp(gs_mouse_state Mouse, u32 Index) { - bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0; - return !IsDown; + bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0; + return !IsDown; } inline bool MouseButtonIsUp(gs_mouse_state Mouse, gs_key Button) { - u32 Index = GetMouseButtonIndex(Button); - return MouseButtonIsUp(Mouse, Index); + u32 Index = GetMouseButtonIndex(Button); + return MouseButtonIsUp(Mouse, Index); } internal void SetMouseButtonTransitionedDown(gs_mouse_state* Mouse, gs_key Button) { - u32 Index = GetMouseButtonIndex(Button); - - Mouse->ButtonStates[Index] = 0; - Mouse->ButtonStates[Index] |= MouseButton_IsDown << MouseButton_IsDownBit; - Mouse->ButtonStates[Index] |= MouseButton_WasNotDown << MouseButton_WasDownBit; + u32 Index = GetMouseButtonIndex(Button); + + Mouse->ButtonStates[Index] = 0; + Mouse->ButtonStates[Index] |= MouseButton_IsDown << MouseButton_IsDownBit; + Mouse->ButtonStates[Index] |= MouseButton_WasNotDown << MouseButton_WasDownBit; } internal void SetMouseButtonTransitionedUp(gs_mouse_state* Mouse, gs_key Button) { - u32 Index = GetMouseButtonIndex(Button); - - Mouse->ButtonStates[Index] = 0; - Mouse->ButtonStates[Index] |= MouseButton_IsNotDown << MouseButton_IsDownBit; - Mouse->ButtonStates[Index] |= MouseButton_WasDown << MouseButton_WasDownBit; + u32 Index = GetMouseButtonIndex(Button); + + Mouse->ButtonStates[Index] = 0; + Mouse->ButtonStates[Index] |= MouseButton_IsNotDown << MouseButton_IsDownBit; + Mouse->ButtonStates[Index] |= MouseButton_WasDown << MouseButton_WasDownBit; } internal void AdvanceMouseButtonState(gs_mouse_state* Mouse, gs_key Button) { - u32 Index = GetMouseButtonIndex(Button); - - if (MouseButtonIsDown(*Mouse, Index)) - { - Mouse->ButtonStates[Index] |= MouseButton_WasDown << MouseButton_WasDownBit; - } - else - { - Mouse->ButtonStates[Index] &= MouseButton_WasNotDown << MouseButton_WasDownBit; - } + u32 Index = GetMouseButtonIndex(Button); + + if (MouseButtonIsDown(*Mouse, Index)) + { + Mouse->ButtonStates[Index] |= MouseButton_WasDown << MouseButton_WasDownBit; + } + else + { + Mouse->ButtonStates[Index] &= MouseButton_WasNotDown << MouseButton_WasDownBit; + } } internal void AdvanceMouseButtonsState(gs_mouse_state* Mouse) { - AdvanceMouseButtonState(Mouse, gs_Key_MouseLeftButton); - AdvanceMouseButtonState(Mouse, gs_Key_MouseMiddleButton); - AdvanceMouseButtonState(Mouse, gs_Key_MouseRightButton); + AdvanceMouseButtonState(Mouse, gs_Key_MouseLeftButton); + AdvanceMouseButtonState(Mouse, gs_Key_MouseMiddleButton); + AdvanceMouseButtonState(Mouse, gs_Key_MouseRightButton); } /////////////////////////// @@ -4076,17 +3551,17 @@ AdvanceMouseButtonsState(gs_mouse_state* Mouse) static u32 HostToNetU32(u32 In) { - unsigned char *s = (unsigned char *)&In; - u32 Result = (u32)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]); - return Result; + unsigned char *s = (unsigned char *)&In; + u32 Result = (u32)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]); + return Result; } static u16 HostToNetU16(u16 In) { - unsigned char *s = (unsigned char *)&In; - u16 Result = (u16)(s[0] << 8 | s[1]); - return Result; + unsigned char *s = (unsigned char *)&In; + u16 Result = (u16)(s[0] << 8 | s[1]); + return Result; } diff --git a/src/gs_libs/gs_types.h b/src/gs_libs/gs_types.h index 7356fbb..2a1051e 100644 --- a/src/gs_libs/gs_types.h +++ b/src/gs_libs/gs_types.h @@ -66,46 +66,46 @@ typedef double r64; enum gs_basic_type { - gs_BasicType_char, - gs_BasicType_b8, - gs_BasicType_b32, - gs_BasicType_b64, - gs_BasicType_u8, - gs_BasicType_u16, - gs_BasicType_u32, - gs_BasicType_u64, - gs_BasicType_s8, - gs_BasicType_s16, - gs_BasicType_s32, - gs_BasicType_s64, - gs_BasicType_r32, - gs_BasicType_r64, - - gs_BasicType_Count, + gs_BasicType_char, + gs_BasicType_b8, + gs_BasicType_b32, + gs_BasicType_b64, + gs_BasicType_u8, + gs_BasicType_u16, + gs_BasicType_u32, + gs_BasicType_u64, + gs_BasicType_s8, + gs_BasicType_s16, + gs_BasicType_s32, + gs_BasicType_s64, + gs_BasicType_r32, + gs_BasicType_r64, + + gs_BasicType_Count, }; global_const u64 gs_BasicTypeSizes[] = { - sizeof(char), - sizeof(b8), - sizeof(b32), - sizeof(b64), - sizeof(u8), - sizeof(u16), - sizeof(u32), - sizeof(u64), - sizeof(s8), - sizeof(s16), - sizeof(s32), - sizeof(s64), - sizeof(r32), - sizeof(r64), + sizeof(char), + sizeof(b8), + sizeof(b32), + sizeof(b64), + sizeof(u8), + sizeof(u16), + sizeof(u32), + sizeof(u64), + sizeof(s8), + sizeof(s16), + sizeof(s32), + sizeof(s64), + sizeof(r32), + sizeof(r64), }; internal u64 BasicTypeSize(gs_basic_type Type) { - return gs_BasicTypeSizes[(u32)Type]; + return gs_BasicTypeSizes[(u32)Type]; } global_const u8 MaxU8 = 0xFF; @@ -143,69 +143,69 @@ global_const r64 SecondsToNanos = 10000000.0; internal r32 DegToRadR32(r32 Degrees) { - return (Degrees * (PiR32 / 180.0f)); + return (Degrees * (PiR32 / 180.0f)); } internal r32 RadToDegR32(r32 Radians) { - return (Radians * (180.0f / PiR32)); + return (Radians * (180.0f / PiR32)); } struct s8_array { - s8* Values; - u32 Count; - u32 CountMax; + s8* Values; + u32 Count; + u32 CountMax; }; struct s16_array { - s16* Values; - u32 Count; - u32 CountMax; + s16* Values; + u32 Count; + u32 CountMax; }; struct s32_array { - s32* Values; - u32 Count; - u32 CountMax; + s32* Values; + u32 Count; + u32 CountMax; }; struct s64_array { - s64* Values; - u32 Count; - u32 CountMax; + s64* Values; + u32 Count; + u32 CountMax; }; struct u8_array { - u8* Values; - u32 Count; - u32 CountMax; + u8* Values; + u32 Count; + u32 CountMax; }; struct u16_array { - u16* Values; - u32 Count; - u32 CountMax; + u16* Values; + u32 Count; + u32 CountMax; }; struct u32_array { - u32* Values; - u32 Count; - u32 CountMax; + u32* Values; + u32 Count; + u32 CountMax; }; struct u64_array { - u64* Values; - u32 Count; - u32 CountMax; + u64* Values; + u32 Count; + u32 CountMax; }; @@ -279,19 +279,19 @@ Glue(gs_AssertFail_, __LINE__) = 1 / (int)(!!(c)), \ internal void ZeroMemory_(u8* Memory, u64 Size) { - for (u64 i = 0; i < Size; i++) - { - Memory[i] = 0; - } + for (u64 i = 0; i < Size; i++) + { + Memory[i] = 0; + } } internal void CopyMemory_(u8* From, u8* To, u64 Size) { - for (u64 i = 0; i < Size; i++) - { - To[i] = From[i]; - } + for (u64 i = 0; i < Size; i++) + { + To[i] = From[i]; + } } #define StaticArrayLength(arr) sizeof(arr) / sizeof((arr)[0]) @@ -309,8 +309,8 @@ CopyMemory_(u8* From, u8* To, u64 Size) #define SLLPop_(list_tail) list_tail=list_tail=list_tail->next #define SLLPop(list_tail) (SLLPop_((list_tail))) -#define SLLNext(ele_at) ele_at = ele_at->Next; -#define SLLPrev(ele_at) ele_at = ele_at->Prev; +#define SLLNext(ele_at) ele_at = ele_at->Next +#define SLLPrev(ele_at) ele_at = ele_at->Prev #define SLLInit(head,tail,first_ele) head=first_ele, tail=first_ele; @@ -322,80 +322,80 @@ else { SLLInit(first,last,new_ele); } union v2 { - struct - { - r32 x; - r32 y; - }; - r32 E[2]; + struct + { + r32 x; + r32 y; + }; + r32 E[2]; }; union v3 { - struct - { - r32 x; - r32 y; - r32 z; - }; - struct - { - r32 r; - r32 g; - r32 b; - }; - struct - { - v2 xy; - r32 _z0; - }; - struct - { - r32 _x0; - v2 yz; - }; - r32 E[3]; + struct + { + r32 x; + r32 y; + r32 z; + }; + struct + { + r32 r; + r32 g; + r32 b; + }; + struct + { + v2 xy; + r32 _z0; + }; + struct + { + r32 _x0; + v2 yz; + }; + r32 E[3]; }; union v4 { - struct - { - r32 x; - r32 y; - r32 z; - r32 w; - }; - struct - { - r32 r; - r32 g; - r32 b; - r32 a; - }; - struct - { - v2 xy; - v2 zw; - }; - struct - { - r32 _x0; - v2 yz; - r32 _w0; - }; - struct - { - v3 xyz; - r32 _w1; - }; - r32 E[4]; + struct + { + r32 x; + r32 y; + r32 z; + r32 w; + }; + struct + { + r32 r; + r32 g; + r32 b; + r32 a; + }; + struct + { + v2 xy; + v2 zw; + }; + struct + { + r32 _x0; + v2 yz; + r32 _w0; + }; + struct + { + v3 xyz; + r32 _w1; + }; + r32 E[4]; }; struct v4_ray { - v4 Origin; - v4 Direction; + v4 Origin; + v4 Direction; }; #define WhiteV4 v4{1, 1, 1, 1} @@ -413,76 +413,76 @@ struct v4_ray struct v2_array { - v2* Vectors; - u32 Count; - u32 CountMax; + v2* Vectors; + u32 Count; + u32 CountMax; }; struct v3_array { - v3* Vectors; - u32 Count; - u32 CountMax; + v3* Vectors; + u32 Count; + u32 CountMax; }; struct v4_array { - v4* Vectors; - u32 Count; - u32 CountMax; + v4* Vectors; + u32 Count; + u32 CountMax; }; struct range1 { - r32 Min; - r32 Max; + r32 Min; + r32 Max; }; struct range2 { - v2 Min; - v2 Max; + v2 Min; + v2 Max; }; typedef range2 rect2; struct range3 { - v3 Min; - v3 Max; + v3 Min; + v3 Max; }; struct range4 { - v4 Min; - v4 Max; + v4 Min; + v4 Max; }; struct range1_array { - range1* Ranges; - u32 Count; - u32 CountMax; + range1* Ranges; + u32 Count; + u32 CountMax; }; struct range2_array { - range2* Ranges; - u32 Count; - u32 CountMax; + range2* Ranges; + u32 Count; + u32 CountMax; }; struct range3_array { - range3* Ranges; - u32 Count; - u32 CountMax; + range3* Ranges; + u32 Count; + u32 CountMax; }; struct range4_array { - range4* Ranges; - u32 Count; - u32 CountMax; + range4* Ranges; + u32 Count; + u32 CountMax; }; #define Range1Expand(r) (r).Min, (r).Max @@ -496,39 +496,39 @@ struct range4_array union m33 { - float Array[9]; - struct - { - r32 AXx; r32 AYx; r32 AZx; - r32 AXy; r32 AYy; r32 AZy; - r32 AXz; r32 AYz; r32 AZz; - }; + float Array[9]; + struct + { + r32 AXx; r32 AYx; r32 AZx; + r32 AXy; r32 AYy; r32 AZy; + r32 AXz; r32 AYz; r32 AZz; + }; }; union m44 { - float Array[16]; - struct - { - r32 AXx; r32 AYx; r32 AZx; r32 Tx; - r32 AXy; r32 AYy; r32 AZy; r32 Ty; - r32 AXz; r32 AYz; r32 AZz; r32 Tz; - r32 AXw; r32 AYw; r32 AZw; r32 Tw; - }; + float Array[16]; + struct + { + r32 AXx; r32 AYx; r32 AZx; r32 Tx; + r32 AXy; r32 AYy; r32 AZy; r32 Ty; + r32 AXz; r32 AYz; r32 AZz; r32 Tz; + r32 AXw; r32 AYw; r32 AZw; r32 Tw; + }; }; struct m33_array { - m33* Matrices; - u32 Count; - u32 CountMax; + m33* Matrices; + u32 Count; + u32 CountMax; }; struct m44_array { - m44* Matrices; - u32 Count; - u32 CountMax; + m44* Matrices; + u32 Count; + u32 CountMax; }; ////////////////////////// @@ -537,70 +537,70 @@ struct m44_array struct gs_const_string { - union - { - char* Str; - u8* Data;; - }; - u64 Length; + union + { + char* Str; + u8* Data;; + }; + u64 Length; }; struct gs_string { - union + union + { + gs_const_string ConstString; + struct { - gs_const_string ConstString; - struct - { - char* Str; - u64 Length; - }; + char* Str; + u64 Length; }; - u64 Size; + }; + u64 Size; }; struct gs_const_string_array { - gs_const_string* Strings; - u64 Count; - u64 CountMax; + gs_const_string* Strings; + u64 Count; + u64 CountMax; }; struct gs_string_array { - gs_string* Strings; - u64 Count; - u64 CountMax; + gs_string* Strings; + u64 Count; + u64 CountMax; }; internal u64 CStringLength(char* Str) { - char* At = Str; - while (*At) { At++; } - u64 Result = PointerDifference(At, Str); - return Result; + char* At = Str; + while (*At) { At++; } + u64 Result = PointerDifference(At, Str); + return Result; } internal bool CStringsEqual(char* A, char* B) { - bool Result = true; - - char* AAt = A; - char* BAt = B; - while (AAt[0] && BAt[0]) + bool Result = true; + + char* AAt = A; + char* BAt = B; + while (AAt[0] && BAt[0]) + { + if (AAt[0] != BAt[0]) { - if (AAt[0] != BAt[0]) - { - Result = false; - break; - } - AAt++; - BAt++; + Result = false; + break; } - if (AAt[0] != 0 || BAt[0] != 0) { Result = false; } - return Result; + AAt++; + BAt++; + } + if (AAt[0] != 0 || BAt[0] != 0) { Result = false; } + return Result; } #define StringExpand(str) (int)(str).Length, (str).Str @@ -626,120 +626,35 @@ typedef struct gs_thread_context gs_thread_context; struct gs_data { - u8* Memory; - u64 Size; + u8* Memory; + u64 Size; }; struct gs_data_array { - gs_data* Data; - u64 Count; - u64 CountMax; + gs_data* Data; + u64 Count; + u64 CountMax; }; enum gs_access_flag { - gs_AccessFlag_Read = 1 << 0, - gs_AccessFlag_Write = 1 << 1, - gs_AccessFlag_Exec = 1 << 2, + gs_AccessFlag_Read = 1 << 0, + gs_AccessFlag_Write = 1 << 1, + gs_AccessFlag_Exec = 1 << 2, }; typedef s32 gs_scan_direction; enum { - gs_Scan_Backward = -1, - gs_Scan_Forward = 1, + gs_Scan_Backward = -1, + gs_Scan_Forward = 1, }; -#define ALLOCATOR_ALLOC(name) void* name(u64 Size, u64* ResultSize) -typedef ALLOCATOR_ALLOC(allocator_allocate); -#define ALLOCATOR_FREE(name) void name(void* Ptr, u64 Size) -typedef ALLOCATOR_FREE(allocator_free); +internal u64 RoundUpTo64(u64 Value, u64 Alignment); -struct gs_debug_allocation -{ - gs_const_string Location; - u64 Size; -}; - -struct gs_allocator_debug -{ - u64 TotalAllocSize; - - u64 AllocationsCount; - u64 AllocationsCountMax; - gs_debug_allocation* Allocations; -}; - -struct gs_allocator -{ - allocator_allocate* Alloc; - allocator_free* Free; - - gs_allocator_debug* Debug; -}; - -struct gs_memory_cursor -{ - gs_data Data; - u64 Position; -}; - -/* TODO(pjs): Setting MEMORY_CURSOR_STATIC_ARRAY will still compile, - However, it introduces a bug that I haven't fully diagnosed. -The problem seems to occur when trying to push to a cleared memory arena -Where the FirstCursor doesn't have enough room for the allocation, but -also FirstCursor->Next points to a valid cursor. The new cursor is put -in the middle however we seem to continually keep allocating new -cursors forever and losing old ones. -The problem in Lumenarium is found in the OutputData structure - -Leaving this in a simplified state for now -*/ -#define MEMORY_CURSOR_STATIC_ARRAY 1 - -struct gs_memory_cursor_list -{ - gs_memory_cursor Cursor; -#if !MEMORY_CURSOR_STATIC_ARRAY - gs_memory_cursor_list* Next; - gs_memory_cursor_list* Prev; -#endif -}; - -enum arena_type -{ - Arena_BaseArena, - Arena_SubArena, -}; - -struct gs_memory_arena -{ - arena_type Type; - gs_allocator Allocator; - gs_memory_arena* Parent; - -#if MEMORY_CURSOR_STATIC_ARRAY - gs_memory_cursor_list* Cursors; - u64 CursorsCount; - u64 CursorsCountMax; -#else - gs_memory_cursor_list* CursorList; -#endif - - u64 MemoryChunkSize; - u64 MemoryAlignment; - - char* ArenaName; -}; - -struct gs_memory_arena_array -{ - gs_memory_arena* Arenas; - u32 Count; - u32 Size; -}; +#include "gs_memory.h" /////////////////////////////// // @@ -748,16 +663,16 @@ struct gs_memory_arena_array struct gs_string_builder_buffer { - gs_string String; - gs_string_builder_buffer* Next; + gs_string String; + gs_string_builder_buffer* Next; }; struct gs_string_builder { - gs_memory_arena* Arena; - u32 BufferSize; - gs_string_builder_buffer* Root; - gs_string_builder_buffer* Head; + gs_memory_arena* Arena; + u32 BufferSize; + gs_string_builder_buffer* Root; + gs_string_builder_buffer* Head; }; /////////////////////////////// @@ -772,8 +687,8 @@ typedef DEBUG_PRINT(debug_print); struct debug_output { - gs_memory_arena* Storage; - debug_print* Print; + gs_memory_arena* Storage; + debug_print* Print; }; /////////////////////////////// @@ -788,34 +703,34 @@ struct debug_output struct gs_dynarray_buffer { - u8* Memory; + u8* Memory; }; struct gs_dynarray { - gs_memory_arena Arena; - - gs_dynarray_buffer* Buffers; - u64 BuffersCount; - u64 ElementCount; - - u64 ElementSize; - u64 ElementsPerBuffer; + gs_memory_arena Arena; + + gs_dynarray_buffer* Buffers; + u64 BuffersCount; + u64 ElementCount; + + u64 ElementSize; + u64 ElementsPerBuffer; }; struct gs_dynarray_handle { - u64 BufferIndex; - u64 IndexInBuffer; + u64 BufferIndex; + u64 IndexInBuffer; }; #define INVALID_DYNARRAY_HANDLE gs_dynarray_handle{0, 0} struct gs_dynarray_handle_list { - gs_dynarray_handle* Handles; - u32 Count; - u32 Size; + gs_dynarray_handle* Handles; + u32 Count; + u32 Size; }; /////////////////////////////// @@ -828,39 +743,39 @@ struct gs_dynarray_handle_list enum enumerate_directory_flag { - EnumerateDirectory_Recurse = 1 << 0, - EnumerateDirectory_IncludeDirectories = 1 << 1, + EnumerateDirectory_Recurse = 1 << 0, + EnumerateDirectory_IncludeDirectories = 1 << 1, }; struct gs_file_info { - gs_const_string Path; - gs_const_string AbsolutePath; - u64 FileSize; - u64 CreationTime; - u64 LastWriteTime; - b32 IsDirectory; + gs_const_string Path; + gs_const_string AbsolutePath; + u64 FileSize; + u64 CreationTime; + u64 LastWriteTime; + b32 IsDirectory; }; struct gs_file_info_array { - gs_file_info* Values; - u32 Count; - u32 MaxCount; + gs_file_info* Values; + u32 Count; + u32 MaxCount; }; struct gs_file { - union + union + { + gs_data Data; + struct { - gs_data Data; - struct - { - u8* Memory; - u64 Size; - }; + u8* Memory; + u64 Size; }; - gs_file_info FileInfo; + }; + gs_file_info FileInfo; }; typedef struct gs_file_handler gs_file_handler; @@ -879,11 +794,11 @@ typedef ENUMERATE_DIRECTORY(file_handler_enumerate_directory); struct gs_file_handler { - file_handler_get_file_info* GetFileInfo; - file_handler_read_entire_file* ReadEntireFile; - file_handler_write_entire_file* WriteEntireFile; - file_handler_enumerate_directory* EnumerateDirectory; - gs_memory_arena* Transient; + file_handler_get_file_info* GetFileInfo; + file_handler_read_entire_file* ReadEntireFile; + file_handler_write_entire_file* WriteEntireFile; + file_handler_enumerate_directory* EnumerateDirectory; + gs_memory_arena* Transient; }; @@ -899,8 +814,8 @@ typedef GET_SECONDS_ELAPSED(get_seconds_elapsed); struct gs_time_handler { - get_wall_clock* GetWallClock; - get_seconds_elapsed* GetSecondsElapsed; + get_wall_clock* GetWallClock; + get_seconds_elapsed* GetSecondsElapsed; }; /////////////////////////////// @@ -909,7 +824,7 @@ struct gs_time_handler struct gs_random_series { - u32 Value; + u32 Value; }; /////////////////////////////// @@ -918,8 +833,8 @@ struct gs_random_series struct gs_radix_list { - u64_array Radixes; - u64_array IDs; + u64_array Radixes; + u64_array IDs; }; @@ -929,115 +844,115 @@ struct gs_radix_list enum gs_event_type { - gs_EventType_Unknown, - - // Reached end of event stream - gs_EventType_NoMoreEvents, - // There was an event but it requires no action from the using program - gs_EventType_NoEvent, - - gs_EventType_KeyPressed, - gs_EventType_KeyReleased, - - gs_EventType_MouseMoved, - gs_EventType_MouseWheel, - - gs_EventType_Count, + gs_EventType_Unknown, + + // Reached end of event stream + gs_EventType_NoMoreEvents, + // There was an event but it requires no action from the using program + gs_EventType_NoEvent, + + gs_EventType_KeyPressed, + gs_EventType_KeyReleased, + + gs_EventType_MouseMoved, + gs_EventType_MouseWheel, + + gs_EventType_Count, }; enum gs_key { - gs_Key_Invalid, - - gs_Key_Esc, - - gs_Key_Space, - gs_Key_Tab, - gs_Key_CapsLock, - gs_Key_Shift, gs_Key_LeftShift, gs_Key_RightShift, - gs_Key_Control, gs_Key_LeftCtrl, gs_Key_RightCtrl, - gs_Key_Fn, - gs_Key_Alt, - gs_Key_PageUp, gs_Key_PageDown, - gs_Key_End, gs_Key_Home, gs_Key_Select, - gs_Key_Backspace, gs_Key_Delete, - gs_Key_Enter, - - // Function Keys - gs_Key_F0, gs_Key_F1, gs_Key_F2, gs_Key_F3, gs_Key_F4, gs_Key_F5, gs_Key_F6, gs_Key_F7, - gs_Key_F8, gs_Key_F9, gs_Key_F10, gs_Key_F11, gs_Key_F12, - - // Letters - gs_Key_a, gs_Key_b, gs_Key_c, gs_Key_d, gs_Key_e, gs_Key_f, gs_Key_g, gs_Key_h, - gs_Key_i, gs_Key_j, gs_Key_k, gs_Key_l, gs_Key_m, gs_Key_n, gs_Key_o, gs_Key_p, - gs_Key_q, gs_Key_r, gs_Key_s, gs_Key_t, gs_Key_u, gs_Key_v, gs_Key_w, gs_Key_x, - gs_Key_y, gs_Key_z, - - gs_Key_A, gs_Key_B, gs_Key_C, gs_Key_D, gs_Key_E, gs_Key_F, gs_Key_G, gs_Key_H, - gs_Key_I, gs_Key_J, gs_Key_K, gs_Key_L, gs_Key_M, gs_Key_N, gs_Key_O, gs_Key_P, - gs_Key_Q, gs_Key_R, gs_Key_S, gs_Key_T, gs_Key_U, gs_Key_V, gs_Key_W, gs_Key_X, - gs_Key_Y, gs_Key_Z, - - // Numbers - gs_Key_0, gs_Key_1, gs_Key_2, gs_Key_3, gs_Key_4, gs_Key_5, gs_Key_6, gs_Key_7, - gs_Key_8, gs_Key_9, - - gs_Key_Num0, gs_Key_Num1, gs_Key_Num2, gs_Key_Num3, gs_Key_Num4, gs_Key_Num5, - gs_Key_Num6, gs_Key_Num7, gs_Key_Num8, gs_Key_Num9, - - // Symbols - gs_Key_Bang, gs_Key_At, gs_Key_Pound, gs_Key_Dollar, gs_Key_Percent, gs_Key_Carrot, - gs_Key_Ampersand, gs_Key_Star, gs_Key_LeftParen, gs_Key_RightParen, gs_Key_Minus, gs_Key_Plus, - gs_Key_Equals, gs_Key_Underscore, gs_Key_OpenSquareBracket, gs_Key_CloseSquareBracket, gs_Key_OpenCurlyBracket, - gs_Key_CloseCurlyBracket, gs_Key_Colon, gs_Key_SemiColon, gs_Key_SingleQuote, gs_Key_DoubleQuote, - gs_Key_ForwardSlash, gs_Key_Backslash, gs_Key_Pipe, gs_Key_Comma, gs_Key_Period, - gs_Key_QuestionMark, gs_Key_LessThan, gs_Key_GreaterThan, gs_Key_Tilde, gs_Key_BackQuote, - - // Arrows - gs_Key_UpArrow, - gs_Key_DownArrow, - gs_Key_LeftArrow, - gs_Key_RightArrow, - - // Mouse - // NOTE(Peter): Including this here so we can utilize the same KeyDown, KeyUp etc. functions - gs_Key_MouseLeftButton, - gs_Key_MouseMiddleButton, - gs_Key_MouseRightButton, - gs_Key_MouseX1Button, - gs_Key_MouseX2Button, - - gs_Key_Count, + gs_Key_Invalid, + + gs_Key_Esc, + + gs_Key_Space, + gs_Key_Tab, + gs_Key_CapsLock, + gs_Key_Shift, gs_Key_LeftShift, gs_Key_RightShift, + gs_Key_Control, gs_Key_LeftCtrl, gs_Key_RightCtrl, + gs_Key_Fn, + gs_Key_Alt, + gs_Key_PageUp, gs_Key_PageDown, + gs_Key_End, gs_Key_Home, gs_Key_Select, + gs_Key_Backspace, gs_Key_Delete, + gs_Key_Enter, + + // Function Keys + gs_Key_F0, gs_Key_F1, gs_Key_F2, gs_Key_F3, gs_Key_F4, gs_Key_F5, gs_Key_F6, gs_Key_F7, + gs_Key_F8, gs_Key_F9, gs_Key_F10, gs_Key_F11, gs_Key_F12, + + // Letters + gs_Key_a, gs_Key_b, gs_Key_c, gs_Key_d, gs_Key_e, gs_Key_f, gs_Key_g, gs_Key_h, + gs_Key_i, gs_Key_j, gs_Key_k, gs_Key_l, gs_Key_m, gs_Key_n, gs_Key_o, gs_Key_p, + gs_Key_q, gs_Key_r, gs_Key_s, gs_Key_t, gs_Key_u, gs_Key_v, gs_Key_w, gs_Key_x, + gs_Key_y, gs_Key_z, + + gs_Key_A, gs_Key_B, gs_Key_C, gs_Key_D, gs_Key_E, gs_Key_F, gs_Key_G, gs_Key_H, + gs_Key_I, gs_Key_J, gs_Key_K, gs_Key_L, gs_Key_M, gs_Key_N, gs_Key_O, gs_Key_P, + gs_Key_Q, gs_Key_R, gs_Key_S, gs_Key_T, gs_Key_U, gs_Key_V, gs_Key_W, gs_Key_X, + gs_Key_Y, gs_Key_Z, + + // Numbers + gs_Key_0, gs_Key_1, gs_Key_2, gs_Key_3, gs_Key_4, gs_Key_5, gs_Key_6, gs_Key_7, + gs_Key_8, gs_Key_9, + + gs_Key_Num0, gs_Key_Num1, gs_Key_Num2, gs_Key_Num3, gs_Key_Num4, gs_Key_Num5, + gs_Key_Num6, gs_Key_Num7, gs_Key_Num8, gs_Key_Num9, + + // Symbols + gs_Key_Bang, gs_Key_At, gs_Key_Pound, gs_Key_Dollar, gs_Key_Percent, gs_Key_Carrot, + gs_Key_Ampersand, gs_Key_Star, gs_Key_LeftParen, gs_Key_RightParen, gs_Key_Minus, gs_Key_Plus, + gs_Key_Equals, gs_Key_Underscore, gs_Key_OpenSquareBracket, gs_Key_CloseSquareBracket, gs_Key_OpenCurlyBracket, + gs_Key_CloseCurlyBracket, gs_Key_Colon, gs_Key_SemiColon, gs_Key_SingleQuote, gs_Key_DoubleQuote, + gs_Key_ForwardSlash, gs_Key_Backslash, gs_Key_Pipe, gs_Key_Comma, gs_Key_Period, + gs_Key_QuestionMark, gs_Key_LessThan, gs_Key_GreaterThan, gs_Key_Tilde, gs_Key_BackQuote, + + // Arrows + gs_Key_UpArrow, + gs_Key_DownArrow, + gs_Key_LeftArrow, + gs_Key_RightArrow, + + // Mouse + // NOTE(Peter): Including this here so we can utilize the same KeyDown, KeyUp etc. functions + gs_Key_MouseLeftButton, + gs_Key_MouseMiddleButton, + gs_Key_MouseRightButton, + gs_Key_MouseX1Button, + gs_Key_MouseX2Button, + + gs_Key_Count, }; enum gs_modifier_key_flags { - gs_ModifierKeyFlag_Shift = 1 << 0, - gs_ModifierKeyFlag_Ctrl = 1 << 1, - gs_ModifierKeyFlag_Alt = 1 << 2, + gs_ModifierKeyFlag_Shift = 1 << 0, + gs_ModifierKeyFlag_Ctrl = 1 << 1, + gs_ModifierKeyFlag_Alt = 1 << 2, }; struct gs_input_event { - gs_event_type Type; - gs_key Key; - v2 Position; - r32 Amount; - b32 Modifiers; + gs_event_type Type; + gs_key Key; + v2 Position; + r32 Amount; + b32 Modifiers; }; struct gs_input_event_buffer { - gs_input_event* Values; - u32 Count; - u32 MaxCount; + gs_input_event* Values; + u32 Count; + u32 MaxCount; }; struct gs_mouse_state { - v2 Position; - b32 ButtonStates[3]; - v2 DownPosition; + v2 Position; + b32 ButtonStates[3]; + v2 DownPosition; }; #define MouseButton_IsDownBit 0 @@ -1053,29 +968,29 @@ struct gs_mouse_state struct gs_thread_info { - u32 ThreadID; + u32 ThreadID; }; struct gs_thread_context { - gs_thread_info ThreadInfo; - - // TODO(Peter): Pull these handlers out into just a gs_context struct so - // they can be shared across threads. - // specifically the allocator - gs_allocator Allocator; - gs_file_handler FileHandler; - debug_output DebugOutput; - gs_time_handler TimeHandler; - - gs_memory_arena* Transient; + gs_thread_info ThreadInfo; + + // TODO(Peter): Pull these handlers out into just a gs_context struct so + // they can be shared across threads. + // specifically the allocator + gs_allocator Allocator; + gs_file_handler FileHandler; + debug_output DebugOutput; + gs_time_handler TimeHandler; + + gs_memory_arena* Transient; }; // Threads typedef struct platform_thread_handle { - u32 Index; + u32 Index; } platform_thread_handle; typedef struct platform_thread_manager platform_thread_manager; @@ -1085,9 +1000,9 @@ typedef THREAD_PROC_(thread_proc_); typedef struct platform_thread { - u8* PlatformHandle; - thread_proc_* Proc; - u8* UserData; + u8* PlatformHandle; + thread_proc_* Proc; + u8* UserData; } platform_thread; #define CREATE_THREAD(name) bool name(platform_thread* Thread, thread_proc_* Proc, u8* UserData, gs_thread_context Ctx) @@ -1099,11 +1014,11 @@ typedef KILL_THREAD(platform_kill_thread); #define THREADS_MAX 32 typedef struct platform_thread_manager { - b8 ThreadsUsed[THREADS_MAX]; - platform_thread Threads[THREADS_MAX]; - - platform_create_thread* CreateThreadProc; - platform_kill_thread* KillThreadProc; + b8 ThreadsUsed[THREADS_MAX]; + platform_thread Threads[THREADS_MAX]; + + platform_create_thread* CreateThreadProc; + platform_kill_thread* KillThreadProc; } platform_thread_manager; // Work Queue @@ -1112,9 +1027,9 @@ typedef struct gs_work_queue gs_work_queue; typedef struct gs_worker_thread { - gs_thread_context Context; - gs_work_queue* Queue; - b32 ShouldExit; + gs_thread_context Context; + gs_work_queue* Queue; + b32 ShouldExit; } gs_worker_thread; #define THREAD_PROC(name) void name(gs_thread_context Context, gs_data Data) @@ -1122,9 +1037,9 @@ typedef THREAD_PROC(thread_proc); struct gs_threaded_job { - thread_proc* WorkProc; - gs_data Data; - gs_const_string JobName; + thread_proc* WorkProc; + gs_data Data; + gs_const_string JobName; }; #define PUSH_WORK_ON_QUEUE(name) void name(gs_work_queue* Queue, thread_proc* WorkProc, gs_data Data, gs_const_string JobName) @@ -1138,32 +1053,32 @@ typedef RESET_WORK_QUEUE(reset_work_queue); struct gs_work_queue { - void* SemaphoreHandle; - - u32 JobsMax; - u32 volatile JobsCount; - u32 volatile NextJobIndex; - u32 volatile JobsCompleted; - gs_threaded_job* Jobs; - - // Work Queue - push_work_on_queue* PushWorkOnQueue; - complete_queue_work* CompleteQueueWork; + void* SemaphoreHandle; + + u32 JobsMax; + u32 volatile JobsCount; + u32 volatile NextJobIndex; + u32 volatile JobsCompleted; + gs_threaded_job* Jobs; + + // Work Queue + push_work_on_queue* PushWorkOnQueue; + complete_queue_work* CompleteQueueWork; }; // Sockets typedef struct platform_socket_handle_ { - u32 Index; + u32 Index; } platform_socket_handle_; typedef struct platform_socket { - platform_socket_handle_ Handle; - char Addr[128]; - char Port[32]; - u8* PlatformHandle; + platform_socket_handle_ Handle; + char Addr[128]; + char Port[32]; + u8* PlatformHandle; } platform_socket; typedef struct platform_socket_manager platform_socket_manager; @@ -1193,15 +1108,15 @@ typedef SOCKET_SEND(platform_socket_send); #define SOCKETS_COUNT_MAX 32 typedef struct platform_socket_manager { - b8 SocketsUsed[SOCKETS_COUNT_MAX]; - platform_socket Sockets[SOCKETS_COUNT_MAX]; - - platform_connect_socket* ConnectSocketProc; - platform_close_socket* CloseSocketProc; - platform_socket_query_status* SocketQueryStatusProc; - platform_socket_peek* SocketPeekProc; - platform_socket_receive* SocketRecieveProc; - platform_socket_send* SocketSendProc; + b8 SocketsUsed[SOCKETS_COUNT_MAX]; + platform_socket Sockets[SOCKETS_COUNT_MAX]; + + platform_connect_socket* ConnectSocketProc; + platform_close_socket* CloseSocketProc; + platform_socket_query_status* SocketQueryStatusProc; + platform_socket_peek* SocketPeekProc; + platform_socket_receive* SocketRecieveProc; + platform_socket_send* SocketSendProc; } platform_socket_manager; #define GS_TYPES_H diff --git a/src/gs_libs/gs_win32.cpp b/src/gs_libs/gs_win32.cpp index 120bbd1..18074c6 100644 --- a/src/gs_libs/gs_win32.cpp +++ b/src/gs_libs/gs_win32.cpp @@ -9,39 +9,39 @@ struct win32_state { - b32 Initialized; - b32 Running; + b32 Initialized; + b32 Running; }; struct win32_opengl_window_info { - s32 ColorBits; - s32 AlphaBits; - s32 DepthBits; - HGLRC RenderContext; + s32 ColorBits; + s32 AlphaBits; + s32 DepthBits; + HGLRC RenderContext; }; struct window { - char* Name; - char* ClassName; - s32 Width; - s32 Height; - WNDPROC WindowEventHandler; - - WNDCLASS Class; - HWND Handle; - HDC DeviceContext; - - // TODO(peter): Make this a union? - win32_opengl_window_info OpenGLInfo; + char* Name; + char* ClassName; + s32 Width; + s32 Height; + WNDPROC WindowEventHandler; + + WNDCLASS Class; + HWND Handle; + HDC DeviceContext; + + // TODO(peter): Make this a union? + win32_opengl_window_info OpenGLInfo; }; struct handle_window_msg_result { - b32 NeedsUpdate; + b32 NeedsUpdate; #if DEBUG - char MessageType[128]; + char MessageType[128]; #endif }; @@ -54,8 +54,8 @@ internal s32 Win32ConcatStrings(s32 ALen, char* A, s32 BLen, char* B, s32 DestLe // Windowing & Graphics struct win32_offscreen_buffer { - texture_buffer Buffer; - BITMAPINFO Info; + texture_buffer Buffer; + BITMAPINFO Info; }; LRESULT CALLBACK Win32HandleWindowsEvents (HWND WindowHandle, UINT Msg, WPARAM wParam, LPARAM lParam); @@ -64,11 +64,6 @@ internal void Win32UpdateWindowDimension(window* Window); internal void Win32ResizeDIBSection(win32_offscreen_buffer *Buffer, int Width, int Height); internal void Win32DisplayBufferInWindow(win32_offscreen_buffer* Buffer, window Window); -// Memory - -internal ALLOCATOR_ALLOC(Win32Alloc); -internal ALLOCATOR_FREE(Win32Free); - /// // Utils /// @@ -76,32 +71,32 @@ internal ALLOCATOR_FREE(Win32Free); internal s32 Win32StringLength(char* String) { - char* At = String; - while (*At) { At++; }; - return At - String; + char* At = String; + while (*At) { At++; }; + return At - String; } internal s32 Win32ConcatStrings(s32 ALength, char* A, s32 BLength, char* B, s32 DestLength, char* Dest) { - char* Dst = Dest; - char* AAt = A; - int ALengthToCopy = ALength < DestLength ? ALength : DestLength; - for (s32 a = 0; a < ALength; a++) - { - *Dst++ = *AAt++; - } - char* BAt = B; - int DestLengthRemaining = DestLength - (Dst - Dest); - int BLengthToCopy = BLength < DestLengthRemaining ? BLength : DestLength; - for (s32 b = 0; b < BLengthToCopy; b++) - { - *Dst++ = *BAt++; - } - int DestLengthOut = Dst - Dest; - int NullTermIndex = DestLengthOut < DestLength ? DestLengthOut : DestLength; - Dest[NullTermIndex] = 0; - return DestLengthOut; + char* Dst = Dest; + char* AAt = A; + int ALengthToCopy = ALength < DestLength ? ALength : DestLength; + for (s32 a = 0; a < ALength; a++) + { + *Dst++ = *AAt++; + } + char* BAt = B; + int DestLengthRemaining = DestLength - (Dst - Dest); + int BLengthToCopy = BLength < DestLengthRemaining ? BLength : DestLength; + for (s32 b = 0; b < BLengthToCopy; b++) + { + *Dst++ = *BAt++; + } + int DestLengthOut = Dst - Dest; + int NullTermIndex = DestLengthOut < DestLength ? DestLengthOut : DestLength; + Dest[NullTermIndex] = 0; + return DestLengthOut; } /// @@ -112,158 +107,158 @@ internal window Win32CreateWindow (HINSTANCE HInstance, char* WindowName, s32 Width, s32 Height, WNDPROC WindowEventHandler) { - window Result = {}; - Result.Name = WindowName; - Result.ClassName = WindowName; - Result.Width = Width; - Result.Height = Height; - Result.WindowEventHandler = WindowEventHandler; - - Result.Class = {}; - Result.Class.style = CS_HREDRAW | CS_VREDRAW; - Result.Class.lpfnWndProc = WindowEventHandler; - Result.Class.hInstance = HInstance; - Result.Class.lpszClassName = WindowName; - - if (RegisterClass(&Result.Class)) - { - Result.Handle = CreateWindowEx( - 0, - Result.Class.lpszClassName, - WindowName, - WS_OVERLAPPEDWINDOW | WS_VISIBLE, - CW_USEDEFAULT, - CW_USEDEFAULT, - Width, - Height, - 0, - 0, - HInstance, - 0); - Result.DeviceContext = GetDC(Result.Handle); - } - - return Result; + window Result = {}; + Result.Name = WindowName; + Result.ClassName = WindowName; + Result.Width = Width; + Result.Height = Height; + Result.WindowEventHandler = WindowEventHandler; + + Result.Class = {}; + Result.Class.style = CS_HREDRAW | CS_VREDRAW; + Result.Class.lpfnWndProc = WindowEventHandler; + Result.Class.hInstance = HInstance; + Result.Class.lpszClassName = WindowName; + + if (RegisterClass(&Result.Class)) + { + Result.Handle = CreateWindowEx( + 0, + Result.Class.lpszClassName, + WindowName, + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, + CW_USEDEFAULT, + Width, + Height, + 0, + 0, + HInstance, + 0); + Result.DeviceContext = GetDC(Result.Handle); + } + + return Result; }; internal window PlatformCreateWindow (char* WindowName, s32 Width, s32 Height) { - HINSTANCE HInstance = GetModuleHandle(NULL); - return Win32CreateWindow(HInstance, WindowName, Width, Height, Win32HandleWindowsEvents); + HINSTANCE HInstance = GetModuleHandle(NULL); + return Win32CreateWindow(HInstance, WindowName, Width, Height, Win32HandleWindowsEvents); } internal void CreateOpenGLWindowContext (win32_opengl_window_info Info, window* Window) { - // Setup pixel format - { - PIXELFORMATDESCRIPTOR PixelFormatDesc = { 0 }; - // TODO: Program seems to work perfectly fine without all other params except dwFlags. - // Can we skip other params for the sake of brevity? - PixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR); - PixelFormatDesc.nVersion = 1; - PixelFormatDesc.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; - PixelFormatDesc.iPixelType = PFD_TYPE_RGBA;// TODO(Peter): include this in win32_opengl_window_info? - PixelFormatDesc.cColorBits = Info.ColorBits; - PixelFormatDesc.cAlphaBits = Info.AlphaBits; - PixelFormatDesc.cDepthBits = Info.DepthBits; - PixelFormatDesc.dwLayerMask = PFD_MAIN_PLANE; // TODO(Peter): include this in win32_opengl_window_info? - // - - s32 PixelFormat = ChoosePixelFormat(Window->DeviceContext, &PixelFormatDesc); - if (!PixelFormat) { InvalidCodePath; } // TODO: Log: Choose pixel format failed - if (!SetPixelFormat(Window->DeviceContext, PixelFormat, &PixelFormatDesc)) { InvalidCodePath; } // TODO: Log: Set pixel format failed - } + // Setup pixel format + { + PIXELFORMATDESCRIPTOR PixelFormatDesc = { 0 }; + // TODO: Program seems to work perfectly fine without all other params except dwFlags. + // Can we skip other params for the sake of brevity? + PixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR); + PixelFormatDesc.nVersion = 1; + PixelFormatDesc.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; + PixelFormatDesc.iPixelType = PFD_TYPE_RGBA;// TODO(Peter): include this in win32_opengl_window_info? + PixelFormatDesc.cColorBits = Info.ColorBits; + PixelFormatDesc.cAlphaBits = Info.AlphaBits; + PixelFormatDesc.cDepthBits = Info.DepthBits; + PixelFormatDesc.dwLayerMask = PFD_MAIN_PLANE; // TODO(Peter): include this in win32_opengl_window_info? + // - // Create rendering context - { - // TODO: Create "proper" context? - // https://www.opengl.org/wiki/Creating_an_OpenGL_Context_(WGL)#Proper_Context_Creation - - Info.RenderContext = wglCreateContext(Window->DeviceContext); - wglMakeCurrent(Window->DeviceContext, Info.RenderContext); - - // TODO(Peter): do we want this? - /* - glGetIntegerv(GL_MAJOR_VERSION, ); - glGetIntegerv(GL_MINOR_VERSION, ); - (char*)glGetString(GL_VENDOR); - (char*)glGetString(GL_RENDERER); - */ - } + s32 PixelFormat = ChoosePixelFormat(Window->DeviceContext, &PixelFormatDesc); + if (!PixelFormat) { InvalidCodePath; } // TODO: Log: Choose pixel format failed + if (!SetPixelFormat(Window->DeviceContext, PixelFormat, &PixelFormatDesc)) { InvalidCodePath; } // TODO: Log: Set pixel format failed + } + + // Create rendering context + { + // TODO: Create "proper" context? + // https://www.opengl.org/wiki/Creating_an_OpenGL_Context_(WGL)#Proper_Context_Creation - Window->OpenGLInfo = Info; + Info.RenderContext = wglCreateContext(Window->DeviceContext); + wglMakeCurrent(Window->DeviceContext, Info.RenderContext); + + // TODO(Peter): do we want this? + /* + glGetIntegerv(GL_MAJOR_VERSION, ); + glGetIntegerv(GL_MINOR_VERSION, ); + (char*)glGetString(GL_VENDOR); + (char*)glGetString(GL_RENDERER); + */ + } + + Window->OpenGLInfo = Info; } struct handle_window_event_result { - LRESULT Result; - b32 Handled; + LRESULT Result; + b32 Handled; }; internal void Win32UpdateInputFrameMouseButtonState (input_frame* InputFrame, key_code KeyCode, int Win32VirtualKey) { - InputFrame->KeysDown[KeyCode] = (GetKeyState(Win32VirtualKey) & (1 << 15)) != 0; + InputFrame->KeysDown[KeyCode] = (GetKeyState(Win32VirtualKey) & (1 << 15)) != 0; } internal void Win32UpdateInputFrameMouseState (input_frame* InputFrame) { - Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseLeftButton, VK_LBUTTON); - Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseMiddleButton, VK_MBUTTON); - Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseRightButton, VK_RBUTTON); - // NOTE(Peter): If you decide to support extra mouse buttons, on windows the key codes are - // VK_XBUTTON1 and VK_XBUTTON2 + Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseLeftButton, VK_LBUTTON); + Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseMiddleButton, VK_MBUTTON); + Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseRightButton, VK_RBUTTON); + // NOTE(Peter): If you decide to support extra mouse buttons, on windows the key codes are + // VK_XBUTTON1 and VK_XBUTTON2 } internal void Win32UpdateInputFrameMouseWheelDelta (input_frame* InputFrame, MSG Message) { - int MouseWheel = GET_WHEEL_DELTA_WPARAM(Message.wParam); - InputFrame->MouseScroll = MouseWheel; + int MouseWheel = GET_WHEEL_DELTA_WPARAM(Message.wParam); + InputFrame->MouseScroll = MouseWheel; } internal handle_window_event_result HandleWindowEventUnlessWouldUseDefault (HWND WindowHandle, UINT Msg, WPARAM wParam, LPARAM lParam) { - handle_window_event_result Result = {}; - Result.Handled = false; - - switch (Msg) + handle_window_event_result Result = {}; + Result.Handled = false; + + switch (Msg) + { + case WM_SIZE: { - case WM_SIZE: - { - - Result.Handled = true; - }break; - - case WM_CLOSE: - { - Result.Result = DefWindowProc(WindowHandle, Msg, wParam, lParam); - Result.Handled = true; - }break; - - case WM_DESTROY: - { - GlobalWin32State.Running = false; - Result.Handled = true; - }break; - - case WM_PAINT: - { - PAINTSTRUCT PaintStruct; - HDC DeviceContext; - b32 PaintResult; - - DeviceContext = BeginPaint(WindowHandle, &PaintStruct); - PaintResult = EndPaint(WindowHandle, &PaintStruct); - Result.Handled = true; - }break; - } + + Result.Handled = true; + }break; - return Result; + case WM_CLOSE: + { + Result.Result = DefWindowProc(WindowHandle, Msg, wParam, lParam); + Result.Handled = true; + }break; + + case WM_DESTROY: + { + GlobalWin32State.Running = false; + Result.Handled = true; + }break; + + case WM_PAINT: + { + PAINTSTRUCT PaintStruct; + HDC DeviceContext; + b32 PaintResult; + + DeviceContext = BeginPaint(WindowHandle, &PaintStruct); + PaintResult = EndPaint(WindowHandle, &PaintStruct); + Result.Handled = true; + }break; + } + + return Result; } LRESULT CALLBACK @@ -274,124 +269,124 @@ Win32HandleWindowsEvents ( LPARAM lParam ) { - handle_window_event_result EventResult = HandleWindowEventUnlessWouldUseDefault( - WindowHandle, - Msg, - wParam, - lParam); - - if (!EventResult.Handled) - { - EventResult.Result = DefWindowProc(WindowHandle, Msg, wParam, lParam); - } - - return EventResult.Result; + handle_window_event_result EventResult = HandleWindowEventUnlessWouldUseDefault( + WindowHandle, + Msg, + wParam, + lParam); + + if (!EventResult.Handled) + { + EventResult.Result = DefWindowProc(WindowHandle, Msg, wParam, lParam); + } + + return EventResult.Result; } static key_code Win32GetKeyCode (int Win32VirtualKey, bool NumpadValid, bool TranslateToChar) { - key_code Result = KeyCode_Invalid; + key_code Result = KeyCode_Invalid; + + if (Win32VirtualKey == VK_ESCAPE) { Result = KeyCode_Esc; } + + if (!TranslateToChar) + { + if (Win32VirtualKey == VK_SPACE) { Result = KeyCode_Space; } - if (Win32VirtualKey == VK_ESCAPE) { Result = KeyCode_Esc; } + } + + if (Win32VirtualKey == VK_CAPITAL) { Result = KeyCode_CapsLock; } + else if (Win32VirtualKey == VK_TAB) { Result = KeyCode_Tab; } + else if (Win32VirtualKey == VK_LSHIFT) { Result = KeyCode_LeftShift; } + else if (Win32VirtualKey == VK_RSHIFT) { Result = KeyCode_RightShift; } + else if (Win32VirtualKey == VK_LCONTROL) { Result = KeyCode_LeftCtrl; } + else if (Win32VirtualKey == VK_RCONTROL) { Result = KeyCode_RightCtrl; } + + // TODO(Peter): support the function key? + //else if (Win32VirtualKey == VK_) { Result = KeyCode_Fn; } + + else if (Win32VirtualKey == VK_MENU) { Result = KeyCode_Alt; } + else if (Win32VirtualKey == VK_PRIOR) { Result = KeyCode_PageUp; } + else if (Win32VirtualKey == VK_NEXT) { Result = KeyCode_PageDown; } + else if (Win32VirtualKey == VK_BACK) { Result = KeyCode_Backspace; } + else if (Win32VirtualKey == VK_DELETE) { Result = KeyCode_Delete; } + else if (Win32VirtualKey == VK_RETURN) { Result = KeyCode_Enter; } + + else if (Win32VirtualKey == VK_F1) { Result = KeyCode_F1; } + else if (Win32VirtualKey == VK_F2) { Result = KeyCode_F2; } + else if (Win32VirtualKey == VK_F3) { Result = KeyCode_F3; } + else if (Win32VirtualKey == VK_F4) { Result = KeyCode_F4; } + else if (Win32VirtualKey == VK_F5) { Result = KeyCode_F5; } + else if (Win32VirtualKey == VK_F6) { Result = KeyCode_F6; } + else if (Win32VirtualKey == VK_F7) { Result = KeyCode_F7; } + else if (Win32VirtualKey == VK_F8) { Result = KeyCode_F8; } + else if (Win32VirtualKey == VK_F9) { Result = KeyCode_F9; } + else if (Win32VirtualKey == VK_F10) { Result = KeyCode_F10; } + else if (Win32VirtualKey == VK_F11) { Result = KeyCode_F11; } + else if (Win32VirtualKey == VK_F12) { Result = KeyCode_F12; } + + if (!TranslateToChar) + { + if (Win32VirtualKey == 0x30) { Result = KeyCode_0; } + else if (Win32VirtualKey == 0x31) { Result = KeyCode_1; } + else if (Win32VirtualKey == 0x32) { Result = KeyCode_2; } + else if (Win32VirtualKey == 0x33) { Result = KeyCode_3; } + else if (Win32VirtualKey == 0x34) { Result = KeyCode_4; } + else if (Win32VirtualKey == 0x35) { Result = KeyCode_5; } + else if (Win32VirtualKey == 0x36) { Result = KeyCode_6; } + else if (Win32VirtualKey == 0x37) { Result = KeyCode_7; } + else if (Win32VirtualKey == 0x38) { Result = KeyCode_8; } + else if (Win32VirtualKey == 0x39) { Result = KeyCode_9; } - if (!TranslateToChar) - { - if (Win32VirtualKey == VK_SPACE) { Result = KeyCode_Space; } - - } - - if (Win32VirtualKey == VK_CAPITAL) { Result = KeyCode_CapsLock; } - else if (Win32VirtualKey == VK_TAB) { Result = KeyCode_Tab; } - else if (Win32VirtualKey == VK_LSHIFT) { Result = KeyCode_LeftShift; } - else if (Win32VirtualKey == VK_RSHIFT) { Result = KeyCode_RightShift; } - else if (Win32VirtualKey == VK_LCONTROL) { Result = KeyCode_LeftCtrl; } - else if (Win32VirtualKey == VK_RCONTROL) { Result = KeyCode_RightCtrl; } - - // TODO(Peter): support the function key? - //else if (Win32VirtualKey == VK_) { Result = KeyCode_Fn; } - - else if (Win32VirtualKey == VK_MENU) { Result = KeyCode_Alt; } - else if (Win32VirtualKey == VK_PRIOR) { Result = KeyCode_PageUp; } - else if (Win32VirtualKey == VK_NEXT) { Result = KeyCode_PageDown; } - else if (Win32VirtualKey == VK_BACK) { Result = KeyCode_Backspace; } - else if (Win32VirtualKey == VK_DELETE) { Result = KeyCode_Delete; } - else if (Win32VirtualKey == VK_RETURN) { Result = KeyCode_Enter; } - - else if (Win32VirtualKey == VK_F1) { Result = KeyCode_F1; } - else if (Win32VirtualKey == VK_F2) { Result = KeyCode_F2; } - else if (Win32VirtualKey == VK_F3) { Result = KeyCode_F3; } - else if (Win32VirtualKey == VK_F4) { Result = KeyCode_F4; } - else if (Win32VirtualKey == VK_F5) { Result = KeyCode_F5; } - else if (Win32VirtualKey == VK_F6) { Result = KeyCode_F6; } - else if (Win32VirtualKey == VK_F7) { Result = KeyCode_F7; } - else if (Win32VirtualKey == VK_F8) { Result = KeyCode_F8; } - else if (Win32VirtualKey == VK_F9) { Result = KeyCode_F9; } - else if (Win32VirtualKey == VK_F10) { Result = KeyCode_F10; } - else if (Win32VirtualKey == VK_F11) { Result = KeyCode_F11; } - else if (Win32VirtualKey == VK_F12) { Result = KeyCode_F12; } - - if (!TranslateToChar) - { - if (Win32VirtualKey == 0x30) { Result = KeyCode_0; } - else if (Win32VirtualKey == 0x31) { Result = KeyCode_1; } - else if (Win32VirtualKey == 0x32) { Result = KeyCode_2; } - else if (Win32VirtualKey == 0x33) { Result = KeyCode_3; } - else if (Win32VirtualKey == 0x34) { Result = KeyCode_4; } - else if (Win32VirtualKey == 0x35) { Result = KeyCode_5; } - else if (Win32VirtualKey == 0x36) { Result = KeyCode_6; } - else if (Win32VirtualKey == 0x37) { Result = KeyCode_7; } - else if (Win32VirtualKey == 0x38) { Result = KeyCode_8; } - else if (Win32VirtualKey == 0x39) { Result = KeyCode_9; } - - else if (Win32VirtualKey == 0x41) { Result = KeyCode_A; } - else if (Win32VirtualKey == 0x42) { Result = KeyCode_B; } - else if (Win32VirtualKey == 0x43) { Result = KeyCode_C; } - else if (Win32VirtualKey == 0x44) { Result = KeyCode_D; } - else if (Win32VirtualKey == 0x45) { Result = KeyCode_E; } - else if (Win32VirtualKey == 0x46) { Result = KeyCode_F; } - else if (Win32VirtualKey == 0x47) { Result = KeyCode_G; } - else if (Win32VirtualKey == 0x48) { Result = KeyCode_H; } - else if (Win32VirtualKey == 0x49) { Result = KeyCode_I; } - else if (Win32VirtualKey == 0x4A) { Result = KeyCode_J; } - else if (Win32VirtualKey == 0x4B) { Result = KeyCode_K; } - else if (Win32VirtualKey == 0x4C) { Result = KeyCode_L; } - else if (Win32VirtualKey == 0x4D) { Result = KeyCode_M; } - else if (Win32VirtualKey == 0x4E) { Result = KeyCode_N; } - else if (Win32VirtualKey == 0x4F) { Result = KeyCode_O; } - else if (Win32VirtualKey == 0x50) { Result = KeyCode_P; } - else if (Win32VirtualKey == 0x51) { Result = KeyCode_Q; } - else if (Win32VirtualKey == 0x52) { Result = KeyCode_R; } - else if (Win32VirtualKey == 0x53) { Result = KeyCode_S; } - else if (Win32VirtualKey == 0x54) { Result = KeyCode_T; } - else if (Win32VirtualKey == 0x55) { Result = KeyCode_U; } - else if (Win32VirtualKey == 0x56) { Result = KeyCode_V; } - else if (Win32VirtualKey == 0x57) { Result = KeyCode_W; } - else if (Win32VirtualKey == 0x58) { Result = KeyCode_X; } - else if (Win32VirtualKey == 0x59) { Result = KeyCode_Y; } - else if (Win32VirtualKey == 0x5A) { Result = KeyCode_Z; } - } - - if (NumpadValid) - { - if (Win32VirtualKey == VK_NUMPAD0) { Result = KeyCode_Num0; } - else if (Win32VirtualKey == VK_NUMPAD1) { Result = KeyCode_Num1; } - else if (Win32VirtualKey == VK_NUMPAD2) { Result = KeyCode_Num2; } - else if (Win32VirtualKey == VK_NUMPAD3) { Result = KeyCode_Num3; } - else if (Win32VirtualKey == VK_NUMPAD4) { Result = KeyCode_Num4; } - else if (Win32VirtualKey == VK_NUMPAD5) { Result = KeyCode_Num5; } - else if (Win32VirtualKey == VK_NUMPAD6) { Result = KeyCode_Num6; } - else if (Win32VirtualKey == VK_NUMPAD7) { Result = KeyCode_Num7; } - else if (Win32VirtualKey == VK_NUMPAD8) { Result = KeyCode_Num8; } - else if (Win32VirtualKey == VK_NUMPAD9) { Result = KeyCode_Num9; } - } - - if (Win32VirtualKey == VK_UP) { Result = KeyCode_UpArrow; } - else if (Win32VirtualKey == VK_DOWN) { Result = KeyCode_DownArrow; } - else if (Win32VirtualKey == VK_LEFT) { Result = KeyCode_LeftArrow; } - else if (Win32VirtualKey == VK_RIGHT) { Result = KeyCode_RightArrow; } - - return Result; + else if (Win32VirtualKey == 0x41) { Result = KeyCode_A; } + else if (Win32VirtualKey == 0x42) { Result = KeyCode_B; } + else if (Win32VirtualKey == 0x43) { Result = KeyCode_C; } + else if (Win32VirtualKey == 0x44) { Result = KeyCode_D; } + else if (Win32VirtualKey == 0x45) { Result = KeyCode_E; } + else if (Win32VirtualKey == 0x46) { Result = KeyCode_F; } + else if (Win32VirtualKey == 0x47) { Result = KeyCode_G; } + else if (Win32VirtualKey == 0x48) { Result = KeyCode_H; } + else if (Win32VirtualKey == 0x49) { Result = KeyCode_I; } + else if (Win32VirtualKey == 0x4A) { Result = KeyCode_J; } + else if (Win32VirtualKey == 0x4B) { Result = KeyCode_K; } + else if (Win32VirtualKey == 0x4C) { Result = KeyCode_L; } + else if (Win32VirtualKey == 0x4D) { Result = KeyCode_M; } + else if (Win32VirtualKey == 0x4E) { Result = KeyCode_N; } + else if (Win32VirtualKey == 0x4F) { Result = KeyCode_O; } + else if (Win32VirtualKey == 0x50) { Result = KeyCode_P; } + else if (Win32VirtualKey == 0x51) { Result = KeyCode_Q; } + else if (Win32VirtualKey == 0x52) { Result = KeyCode_R; } + else if (Win32VirtualKey == 0x53) { Result = KeyCode_S; } + else if (Win32VirtualKey == 0x54) { Result = KeyCode_T; } + else if (Win32VirtualKey == 0x55) { Result = KeyCode_U; } + else if (Win32VirtualKey == 0x56) { Result = KeyCode_V; } + else if (Win32VirtualKey == 0x57) { Result = KeyCode_W; } + else if (Win32VirtualKey == 0x58) { Result = KeyCode_X; } + else if (Win32VirtualKey == 0x59) { Result = KeyCode_Y; } + else if (Win32VirtualKey == 0x5A) { Result = KeyCode_Z; } + } + + if (NumpadValid) + { + if (Win32VirtualKey == VK_NUMPAD0) { Result = KeyCode_Num0; } + else if (Win32VirtualKey == VK_NUMPAD1) { Result = KeyCode_Num1; } + else if (Win32VirtualKey == VK_NUMPAD2) { Result = KeyCode_Num2; } + else if (Win32VirtualKey == VK_NUMPAD3) { Result = KeyCode_Num3; } + else if (Win32VirtualKey == VK_NUMPAD4) { Result = KeyCode_Num4; } + else if (Win32VirtualKey == VK_NUMPAD5) { Result = KeyCode_Num5; } + else if (Win32VirtualKey == VK_NUMPAD6) { Result = KeyCode_Num6; } + else if (Win32VirtualKey == VK_NUMPAD7) { Result = KeyCode_Num7; } + else if (Win32VirtualKey == VK_NUMPAD8) { Result = KeyCode_Num8; } + else if (Win32VirtualKey == VK_NUMPAD9) { Result = KeyCode_Num9; } + } + + if (Win32VirtualKey == VK_UP) { Result = KeyCode_UpArrow; } + else if (Win32VirtualKey == VK_DOWN) { Result = KeyCode_DownArrow; } + else if (Win32VirtualKey == VK_LEFT) { Result = KeyCode_LeftArrow; } + else if (Win32VirtualKey == VK_RIGHT) { Result = KeyCode_RightArrow; } + + return Result; } internal handle_window_msg_result @@ -399,173 +394,173 @@ HandleWindowsMessage ( HWND WindowHandle, MSG Message) { - handle_window_msg_result Result = {}; - Result.NeedsUpdate = 0; - - switch (Message.message) + handle_window_msg_result Result = {}; + Result.NeedsUpdate = 0; + + switch (Message.message) + { + case WM_HOTKEY: { - case WM_HOTKEY: - { - }break; - - case WM_MOUSEWHEEL: - { - int MouseWheel = GET_WHEEL_DELTA_WPARAM(Message.wParam); - /* - Input.New->MouseScroll = MouseWheel; - Result.NeedsUpdate = true; - */ - }break; - - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - { - /* - Input.New->KeyStates[KeyCode_MouseLeftButton] = (GetKeyState(VK_LBUTTON) & (1 << 15)) != 0; - Input.New->KeyStates[KeyCode_MouseMiddleButton] = (GetKeyState(VK_MBUTTON) & (1 << 15)) != 0; - Input.New->KeyStates[KeyCode_MouseRightButton] = (GetKeyState(VK_RBUTTON) & (1 << 15)) != 0; - // NOTE(Peter): If you decide to support extra mouse buttons, on windows the key codes are - // VK_XBUTTON1 and VK_XBUTTON2 - - if (KeyTransitionedDown(KeyCode_MouseLeftButton, Input)) - { - Input.MouseDownX = Input.New->MouseX; - Input.MouseDownY = Input.New->MouseY; - } - Result.NeedsUpdate = true;*/ - }break; - - case WM_MOUSEMOVE: - { - POINT MousePos; - GetCursorPos(&MousePos); - ScreenToClient(WindowHandle, &MousePos); - - /* - Input.New->MouseX = MousePos.x; - Input.New->MouseY = App.WindowHeight - MousePos.y; - - Result.NeedsUpdate = true; - */ - }break; - - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - case WM_KEYDOWN: - case WM_KEYUP: - { - int VirtualKey = (int)Message.wParam; - bool KeyDown = (Message.lParam & (1 << 31)) == 0; - int KeyIndex = Win32GetKeyCode(VirtualKey, true, true); - /* - if (KeyIndex >= 0) - { - DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_KEYEvent "); - Input.New->KeyStates[KeyIndex] = KeyDown; - Result.NeedsUpdate = true; - } - else - { - if (Input.TranslateInputToCharValues && KeyDown) - { - // NOTE(Peter): Took this out b/c we're translating the WM_CHAR messages - // in the message pump, and if we do it here as well, character producing - // key messages get put on the message queue twice - TranslateMessage(&Message); - DispatchMessage(&Message); - } - else - { - DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_KEYEvent "); - // NOTE(Peter): This is so that when you lift up a key that was generating a WM_CHAR, - // the app still has a chance to respond to it. - Result.NeedsUpdate = true; - } - } - */ - }break; - - case WM_CHAR: - { - /* - char TranslatedChar = (char)Message.wParam; - int KeyIndex = GetKeyIndexFromChar(TranslatedChar); - - if (KeyIndex >= 0) - { - // NOTE(Peter): Always setting this to true becuase windows is stupid and doesn't - // pass the press/release bit through correctly. So now the KEYDOWN/KEYUP Messages above - // only translate the message to a WM_CHAR message if its a key down. Since we clear all - // keystates to false at the beginning of an input frame, this will make transitions - // get registered correctly. - Input.New->KeyStates[KeyIndex] = true; - Result.NeedsUpdate = true; - } - else - { - printf("Translated Char Not Recognized: %c\n", TranslatedChar); - } - */ - }break; - - default: - { - TranslateMessage(&Message); - DispatchMessage(&Message); - }break; - } + }break; - return Result; + case WM_MOUSEWHEEL: + { + int MouseWheel = GET_WHEEL_DELTA_WPARAM(Message.wParam); + /* + Input.New->MouseScroll = MouseWheel; + Result.NeedsUpdate = true; + */ + }break; + + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + { + /* + Input.New->KeyStates[KeyCode_MouseLeftButton] = (GetKeyState(VK_LBUTTON) & (1 << 15)) != 0; + Input.New->KeyStates[KeyCode_MouseMiddleButton] = (GetKeyState(VK_MBUTTON) & (1 << 15)) != 0; + Input.New->KeyStates[KeyCode_MouseRightButton] = (GetKeyState(VK_RBUTTON) & (1 << 15)) != 0; + // NOTE(Peter): If you decide to support extra mouse buttons, on windows the key codes are + // VK_XBUTTON1 and VK_XBUTTON2 + + if (KeyTransitionedDown(KeyCode_MouseLeftButton, Input)) + { + Input.MouseDownX = Input.New->MouseX; + Input.MouseDownY = Input.New->MouseY; + } + Result.NeedsUpdate = true;*/ + }break; + + case WM_MOUSEMOVE: + { + POINT MousePos; + GetCursorPos(&MousePos); + ScreenToClient(WindowHandle, &MousePos); + + /* + Input.New->MouseX = MousePos.x; + Input.New->MouseY = App.WindowHeight - MousePos.y; + + Result.NeedsUpdate = true; + */ + }break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + { + int VirtualKey = (int)Message.wParam; + bool KeyDown = (Message.lParam & (1 << 31)) == 0; + int KeyIndex = Win32GetKeyCode(VirtualKey, true, true); + /* + if (KeyIndex >= 0) + { + DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_KEYEvent "); + Input.New->KeyStates[KeyIndex] = KeyDown; + Result.NeedsUpdate = true; + } + else + { + if (Input.TranslateInputToCharValues && KeyDown) + { + // NOTE(Peter): Took this out b/c we're translating the WM_CHAR messages + // in the message pump, and if we do it here as well, character producing + // key messages get put on the message queue twice + TranslateMessage(&Message); + DispatchMessage(&Message); + } + else + { + DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_KEYEvent "); + // NOTE(Peter): This is so that when you lift up a key that was generating a WM_CHAR, + // the app still has a chance to respond to it. + Result.NeedsUpdate = true; + } + } + */ + }break; + + case WM_CHAR: + { + /* + char TranslatedChar = (char)Message.wParam; + int KeyIndex = GetKeyIndexFromChar(TranslatedChar); + + if (KeyIndex >= 0) + { + // NOTE(Peter): Always setting this to true becuase windows is stupid and doesn't + // pass the press/release bit through correctly. So now the KEYDOWN/KEYUP Messages above + // only translate the message to a WM_CHAR message if its a key down. Since we clear all + // keystates to false at the beginning of an input frame, this will make transitions + // get registered correctly. + Input.New->KeyStates[KeyIndex] = true; + Result.NeedsUpdate = true; + } + else + { + printf("Translated Char Not Recognized: %c\n", TranslatedChar); + } + */ + }break; + + default: + { + TranslateMessage(&Message); + DispatchMessage(&Message); + }break; + } + + return Result; } internal void Win32UpdateWindowDimension(window* Window) { - RECT ClientRect; - GetClientRect(Window->Handle, &ClientRect); - Window->Width = ClientRect.right - ClientRect.left; - Window->Height = ClientRect.bottom - ClientRect.top; + RECT ClientRect; + GetClientRect(Window->Handle, &ClientRect); + Window->Width = ClientRect.right - ClientRect.left; + Window->Height = ClientRect.bottom - ClientRect.top; } internal void Win32ResizeDIBSection(win32_offscreen_buffer *Win32Buffer, int Width, int Height) { - if(Win32Buffer->Buffer.Memory) - { - VirtualFree(Win32Buffer->Buffer.Memory, 0, MEM_RELEASE); - } - - Win32Buffer->Buffer.Width = Width; - Win32Buffer->Buffer.Height = Height; - - int BytesPerPixel = 4; - Win32Buffer->Buffer.BytesPerPixel = BytesPerPixel; - - Win32Buffer->Info.bmiHeader.biSize = sizeof(Win32Buffer->Info.bmiHeader); - Win32Buffer->Info.bmiHeader.biWidth = Win32Buffer->Buffer.Width; - Win32Buffer->Info.bmiHeader.biHeight = -Win32Buffer->Buffer.Height; // Top down, not bottom up - Win32Buffer->Info.bmiHeader.biPlanes = 1; - Win32Buffer->Info.bmiHeader.biBitCount = 32; - Win32Buffer->Info.bmiHeader.biCompression = BI_RGB; - - int BitmapMemorySize = (Win32Buffer->Buffer.Width * Win32Buffer->Buffer.Height)*BytesPerPixel; - Win32Buffer->Buffer.Memory = (u8*)VirtualAlloc(0, BitmapMemorySize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - Win32Buffer->Buffer.Pitch = Width*BytesPerPixel; + if(Win32Buffer->Buffer.Memory) + { + VirtualFree(Win32Buffer->Buffer.Memory, 0, MEM_RELEASE); + } + + Win32Buffer->Buffer.Width = Width; + Win32Buffer->Buffer.Height = Height; + + int BytesPerPixel = 4; + Win32Buffer->Buffer.BytesPerPixel = BytesPerPixel; + + Win32Buffer->Info.bmiHeader.biSize = sizeof(Win32Buffer->Info.bmiHeader); + Win32Buffer->Info.bmiHeader.biWidth = Win32Buffer->Buffer.Width; + Win32Buffer->Info.bmiHeader.biHeight = -Win32Buffer->Buffer.Height; // Top down, not bottom up + Win32Buffer->Info.bmiHeader.biPlanes = 1; + Win32Buffer->Info.bmiHeader.biBitCount = 32; + Win32Buffer->Info.bmiHeader.biCompression = BI_RGB; + + int BitmapMemorySize = (Win32Buffer->Buffer.Width * Win32Buffer->Buffer.Height)*BytesPerPixel; + Win32Buffer->Buffer.Memory = (u8*)VirtualAlloc(0, BitmapMemorySize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + Win32Buffer->Buffer.Pitch = Width*BytesPerPixel; } internal void Win32DisplayBufferInWindow(win32_offscreen_buffer* Win32Buffer, window Window) { - StretchDIBits(Window.DeviceContext, - 0, 0, Win32Buffer->Buffer.Width, Win32Buffer->Buffer.Height, - 0, 0, Win32Buffer->Buffer.Width, Win32Buffer->Buffer.Height, - Win32Buffer->Buffer.Memory, - &Win32Buffer->Info, - DIB_RGB_COLORS, SRCCOPY); + StretchDIBits(Window.DeviceContext, + 0, 0, Win32Buffer->Buffer.Width, Win32Buffer->Buffer.Height, + 0, 0, Win32Buffer->Buffer.Width, Win32Buffer->Buffer.Height, + Win32Buffer->Buffer.Memory, + &Win32Buffer->Info, + DIB_RGB_COLORS, SRCCOPY); } ///////////////////////////////////////// @@ -581,20 +576,20 @@ OpenGLRenderTriBuffer (u8* Vertecies, s32 VertexElements, u8* Colors, s32 ColorsElements, s32 TriCount) { - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(VertexElements, GL_FLOAT, VertexElements * sizeof(r32), Vertecies); - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(UVElements, GL_FLOAT, UVElements * sizeof(r32), UVs); - - glEnableClientState(GL_COLOR_ARRAY); - glColorPointer(ColorsElements, GL_FLOAT, ColorsElements * sizeof(r32), Colors); - - glDrawArrays(GL_TRIANGLES, 0, TriCount); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(VertexElements, GL_FLOAT, VertexElements * sizeof(r32), Vertecies); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(UVElements, GL_FLOAT, UVElements * sizeof(r32), UVs); + + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(ColorsElements, GL_FLOAT, ColorsElements * sizeof(r32), Colors); + + glDrawArrays(GL_TRIANGLES, 0, TriCount); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); } internal void @@ -602,21 +597,21 @@ OpenGLDraw3DTri (v4 P0, v4 P1, v4 P2, v2 UV0, v2 UV1, v2 UV2, v4 C0, v4 C1, v4 C2) { - glBegin(GL_TRIANGLES); - - glTexCoord2f(UV0.x, UV0.y); - glColor4f(C0.r, C0.g, C0.b, C0.a); - glVertex4f(P0.x, P0.y, P0.z, P0.w); - - glTexCoord2f(UV1.x, UV1.y); - glColor4f(C1.r, C1.g, C1.b, C1.a); - glVertex4f(P1.x, P1.y, P1.z, P1.w); - - glTexCoord2f(UV2.x, UV2.y); - glColor4f(C2.r, C2.g, C2.b, C2.a); - glVertex4f(P2.x, P2.y, P2.z, P2.w); - - glEnd(); + glBegin(GL_TRIANGLES); + + glTexCoord2f(UV0.x, UV0.y); + glColor4f(C0.r, C0.g, C0.b, C0.a); + glVertex4f(P0.x, P0.y, P0.z, P0.w); + + glTexCoord2f(UV1.x, UV1.y); + glColor4f(C1.r, C1.g, C1.b, C1.a); + glVertex4f(P1.x, P1.y, P1.z, P1.w); + + glTexCoord2f(UV2.x, UV2.y); + glColor4f(C2.r, C2.g, C2.b, C2.a); + glVertex4f(P2.x, P2.y, P2.z, P2.w); + + glEnd(); } internal void @@ -624,72 +619,72 @@ OpenGLDraw2DTri (v2 P0, v2 P1, v2 P2, v2 UV0, v2 UV1, v2 UV2, v4 C0, v4 C1, v4 C2) { - glBegin(GL_TRIANGLES); - - glTexCoord2f(UV0.x, UV0.y); - glColor4f(C0.r, C0.g, C0.b, C0.a); - glVertex2f(P0.x, P0.y); - - glTexCoord2f(UV1.x, UV1.y); - glColor4f(C1.r, C1.g, C1.b, C1.a); - glVertex2f(P1.x, P1.y); - - glTexCoord2f(UV2.x, UV2.y); - glColor4f(C2.r, C2.g, C2.b, C2.a); - glVertex2f(P2.x, P2.y); - - glEnd(); + glBegin(GL_TRIANGLES); + + glTexCoord2f(UV0.x, UV0.y); + glColor4f(C0.r, C0.g, C0.b, C0.a); + glVertex2f(P0.x, P0.y); + + glTexCoord2f(UV1.x, UV1.y); + glColor4f(C1.r, C1.g, C1.b, C1.a); + glVertex2f(P1.x, P1.y); + + glTexCoord2f(UV2.x, UV2.y); + glColor4f(C2.r, C2.g, C2.b, C2.a); + glVertex2f(P2.x, P2.y); + + glEnd(); } internal void LoadModelView (r32 Matrix[16]) { - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(Matrix); + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(Matrix); } internal void LoadProjection (r32 Matrix[16]) { - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(Matrix); + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(Matrix); } internal void ClearRenderBuffer () { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } s32 NextTextureHandle = 1; internal s32 SubmitTexture (u8* Memory, s32 Width, s32 Height) { - s32 TextureHandle = NextTextureHandle++; - glBindTexture(GL_TEXTURE_2D, TextureHandle); - glTexImage2D(GL_TEXTURE_2D, - 0, // mip map level - GL_RGBA8, - Width, - Height, - 0, // border - GL_RGBA, - GL_UNSIGNED_BYTE, - Memory); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - return TextureHandle; + s32 TextureHandle = NextTextureHandle++; + glBindTexture(GL_TEXTURE_2D, TextureHandle); + glTexImage2D(GL_TEXTURE_2D, + 0, // mip map level + GL_RGBA8, + Width, + Height, + 0, // border + GL_RGBA, + GL_UNSIGNED_BYTE, + Memory); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + return TextureHandle; } internal void BindTexture (s32 TextureHandle) { - glBindTexture(GL_TEXTURE_2D, TextureHandle); + glBindTexture(GL_TEXTURE_2D, TextureHandle); } #define GS_WIN32_CPP diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index d7f4cb6..12b09d3 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -22,141 +22,141 @@ global log_buffer* GlobalLogBuffer; typedef struct { - v3 CenterStart; - v3 CenterEnd; - r32 Radius; - u32 SegmentsCount; - u32 SubsegmentsCount; - u32 SubsegmentLeds; - - // Only one of these two values is needed. - // If ChannelsArray != 0, then it will be used, and assumed to - // have SegmentsCount values - // Otherwise, each segment will increment from ChannelStart - u32 ChannelStart; - u32* ChannelsArray; - - char* ComPort; - char* SectionTagValue; - char* FlowerTagValue; + v3 CenterStart; + v3 CenterEnd; + r32 Radius; + u32 SegmentsCount; + u32 SubsegmentsCount; + u32 SubsegmentLeds; + + // Only one of these two values is needed. + // If ChannelsArray != 0, then it will be used, and assumed to + // have SegmentsCount values + // Otherwise, each segment will increment from ChannelStart + u32 ChannelStart; + u32* ChannelsArray; + + char* ComPort; + char* SectionTagValue; + char* FlowerTagValue; } loop_desc; internal void BuildLoop(gs_string* OutputBuffer, loop_desc Desc) { - r32 SegmentsArc = TauR32 / Desc.SegmentsCount; - r32 SubsegmentsArc = SegmentsArc / Desc.SubsegmentsCount; + r32 SegmentsArc = TauR32 / Desc.SegmentsCount; + r32 SubsegmentsArc = SegmentsArc / Desc.SubsegmentsCount; + + for (u32 i = 0; i < Desc.SegmentsCount; i++) + { + r32 ArcBase = SegmentsArc * i; - for (u32 i = 0; i < Desc.SegmentsCount; i++) + u32 Channel = 0; + if (Desc.ChannelsArray != 0) { - r32 ArcBase = SegmentsArc * i; - - u32 Channel = 0; - if (Desc.ChannelsArray != 0) - { - Channel = Desc.ChannelsArray[i]; - } - else - { - Channel = Desc.ChannelStart + i; - } - - WriteLedStripOpen(OutputBuffer, Channel, Desc.ComPort); - WriteSegmentSequenceOpen(OutputBuffer, Desc.SubsegmentsCount); - - for (u32 j = 0; j < Desc.SubsegmentsCount; j++) - { - r32 Arc = ArcBase + (SubsegmentsArc * j); - v3 Offset = v3{ SinR32(Arc), 0, CosR32(Arc) } * Desc.Radius; - v3 P0 = Desc.CenterStart + Offset; - v3 P1 = Desc.CenterEnd + Offset; - - // Swap directions on the middle strip - if (j%2 != 0) - { - v3 Temp = P0; - P0 = P1; - P1 = Temp; - } - - WriteSegmentSequenceSegment(OutputBuffer, P0, P1, Desc.SubsegmentLeds); - } - - WriteSegmentSequenceClose(OutputBuffer); - WriteSegmentTagsOpen(OutputBuffer, 2); - WriteSegmentTag(OutputBuffer, "section", Desc.SectionTagValue); - WriteSegmentTag(OutputBuffer, "flower", Desc.FlowerTagValue); - WriteSegmentTagsClose(OutputBuffer); - WriteLedStripClose(OutputBuffer); + Channel = Desc.ChannelsArray[i]; + } + else + { + Channel = Desc.ChannelStart + i; } + WriteLedStripOpen(OutputBuffer, Channel, Desc.ComPort); + WriteSegmentSequenceOpen(OutputBuffer, Desc.SubsegmentsCount); + + for (u32 j = 0; j < Desc.SubsegmentsCount; j++) + { + r32 Arc = ArcBase + (SubsegmentsArc * j); + v3 Offset = v3{ SinR32(Arc), 0, CosR32(Arc) } * Desc.Radius; + v3 P0 = Desc.CenterStart + Offset; + v3 P1 = Desc.CenterEnd + Offset; + + // Swap directions on the middle strip + if (j%2 != 0) + { + v3 Temp = P0; + P0 = P1; + P1 = Temp; + } + + WriteSegmentSequenceSegment(OutputBuffer, P0, P1, Desc.SubsegmentLeds); + } + + WriteSegmentSequenceClose(OutputBuffer); + WriteSegmentTagsOpen(OutputBuffer, 2); + WriteSegmentTag(OutputBuffer, "section", Desc.SectionTagValue); + WriteSegmentTag(OutputBuffer, "flower", Desc.FlowerTagValue); + WriteSegmentTagsClose(OutputBuffer); + WriteLedStripClose(OutputBuffer); + } + } typedef struct { - v3 Pos; - char* ComPort; - char* FlowerTagValue; - u32* StemChannels; - u32* BloomOuterChannels; - u32* BloomInnerChannels; + v3 Pos; + char* ComPort; + char* FlowerTagValue; + u32* StemChannels; + u32* BloomOuterChannels; + u32* BloomInnerChannels; } flower_desc; internal u32 BuildFlower(gs_string* OutputBuffer, flower_desc Desc) { - + #if 1 - // the bloom stem inner - loop_desc BloomStemInner = {}; - BloomStemInner.CenterStart = v3{0, 1.4f, 0}; - BloomStemInner.CenterEnd = v3{0, .9f, 0}; - BloomStemInner.Radius = .05f; - BloomStemInner.SegmentsCount = 6; - BloomStemInner.SubsegmentsCount = 3; - BloomStemInner.SubsegmentLeds = 35; - BloomStemInner.ChannelsArray = Desc.BloomInnerChannels; - BloomStemInner.ComPort = Desc.ComPort; - BloomStemInner.SectionTagValue = "inner_bloom"; - BloomStemInner.FlowerTagValue = Desc.FlowerTagValue; - BuildLoop(OutputBuffer, BloomStemInner); - - // the bloom stem outer - loop_desc BloomStemOuter = {}; - BloomStemOuter.CenterStart = v3{0, .5f, 0}; - BloomStemOuter.CenterEnd = v3{0, .9f, 0}; - BloomStemOuter.Radius = .07f; - BloomStemOuter.SegmentsCount = 9; - BloomStemOuter.SubsegmentsCount = 3; - BloomStemOuter.SubsegmentLeds = 41; - BloomStemOuter.ChannelsArray = Desc.BloomOuterChannels; - BloomStemOuter.ComPort = Desc.ComPort; - BloomStemOuter.SectionTagValue = "outer_bloom"; - BloomStemOuter.FlowerTagValue = Desc.FlowerTagValue; - BuildLoop(OutputBuffer, BloomStemOuter); + // the bloom stem inner + loop_desc BloomStemInner = {}; + BloomStemInner.CenterStart = v3{0, 1.4f, 0}; + BloomStemInner.CenterEnd = v3{0, .9f, 0}; + BloomStemInner.Radius = .05f; + BloomStemInner.SegmentsCount = 6; + BloomStemInner.SubsegmentsCount = 3; + BloomStemInner.SubsegmentLeds = 35; + BloomStemInner.ChannelsArray = Desc.BloomInnerChannels; + BloomStemInner.ComPort = Desc.ComPort; + BloomStemInner.SectionTagValue = "inner_bloom"; + BloomStemInner.FlowerTagValue = Desc.FlowerTagValue; + BuildLoop(OutputBuffer, BloomStemInner); + + // the bloom stem outer + loop_desc BloomStemOuter = {}; + BloomStemOuter.CenterStart = v3{0, .5f, 0}; + BloomStemOuter.CenterEnd = v3{0, .9f, 0}; + BloomStemOuter.Radius = .07f; + BloomStemOuter.SegmentsCount = 9; + BloomStemOuter.SubsegmentsCount = 3; + BloomStemOuter.SubsegmentLeds = 41; + BloomStemOuter.ChannelsArray = Desc.BloomOuterChannels; + BloomStemOuter.ComPort = Desc.ComPort; + BloomStemOuter.SectionTagValue = "outer_bloom"; + BloomStemOuter.FlowerTagValue = Desc.FlowerTagValue; + BuildLoop(OutputBuffer, BloomStemOuter); #endif - + #if 1 - // the flower stem - loop_desc FlowerStem = {}; - FlowerStem.CenterStart = v3{0, -1.5f, 0}; - FlowerStem.CenterEnd = v3{0, .5f, 0}; - FlowerStem.Radius = .05f; - FlowerStem.SegmentsCount = 6; - FlowerStem.SubsegmentsCount = 1; - FlowerStem.SubsegmentLeds = 300; - FlowerStem.ChannelsArray = Desc.StemChannels; - FlowerStem.ComPort = Desc.ComPort; - FlowerStem.SectionTagValue = "stem"; - FlowerStem.FlowerTagValue = Desc.FlowerTagValue; - BuildLoop(OutputBuffer, FlowerStem); + // the flower stem + loop_desc FlowerStem = {}; + FlowerStem.CenterStart = v3{0, -1.5f, 0}; + FlowerStem.CenterEnd = v3{0, .5f, 0}; + FlowerStem.Radius = .05f; + FlowerStem.SegmentsCount = 6; + FlowerStem.SubsegmentsCount = 1; + FlowerStem.SubsegmentLeds = 300; + FlowerStem.ChannelsArray = Desc.StemChannels; + FlowerStem.ComPort = Desc.ComPort; + FlowerStem.SectionTagValue = "stem"; + FlowerStem.FlowerTagValue = Desc.FlowerTagValue; + BuildLoop(OutputBuffer, FlowerStem); #endif - - u32 StripsCount = BloomStemInner.SegmentsCount; - StripsCount += BloomStemOuter.SegmentsCount; - StripsCount += FlowerStem.SegmentsCount; - - return StripsCount; + + u32 StripsCount = BloomStemInner.SegmentsCount; + StripsCount += BloomStemOuter.SegmentsCount; + StripsCount += FlowerStem.SegmentsCount; + + return StripsCount; } // Just for brevity, no real function provided @@ -164,88 +164,88 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc) internal u8 FlowerStripToChannel(u8 Flower, u8 Channel) { - Assert(Flower < 3); - Assert(Channel < 8); - - u8 Result = 0; - Result |= (Flower & 0x03) << 3; - Result |= (Channel & 0x07); - - return Result; + Assert(Flower < 3); + Assert(Channel < 8); + + u8 Result = 0; + Result |= (Flower & 0x03) << 3; + Result |= (Channel & 0x07); + + return Result; } int main(int ArgCount, char** Args) { - gs_thread_context Ctx = Win32CreateThreadContext(); - GlobalLogBuffer = AllocatorAllocStruct(Ctx.Allocator, log_buffer); - *GlobalLogBuffer = Log_Init(Ctx.Allocator, 32); - - gs_string OutputBuffer0 = PushString(Ctx.Transient, MB(4)); - gs_string OutputBuffer1 = PushString(Ctx.Transient, MB(4)); - gs_string OutputBuffer2 = PushString(Ctx.Transient, MB(4)); - - WriteAssemblyUARTOpen(&OutputBuffer0, - "Blumen Lumen - Silver Spring - 00", - 100, - v3{-1, 0, 0}, - 21, - ""); - WriteAssemblyUARTOpen(&OutputBuffer1, - "Blumen Lumen - Silver Spring - 01", - 100, - v3{0, 0, 0}, - 21, - ""); - WriteAssemblyUARTOpen(&OutputBuffer2, - "Blumen Lumen - Silver Spring - 02", - 100, - v3{1, 0, 0}, - 21, - ""); - - u32 StripCount = 0; - - u32 StemChannels[] = { FSC(2, 1), FSC(2, 2), FSC(2, 3), FSC(2, 4), FSC(2, 5), FSC(2, 6) }; - u32 BloomOuterChannels[] = { FSC(1, 0), FSC(1, 1), FSC(1, 2), FSC(1, 3), FSC(1, 4), FSC(1, 5), FSC(1, 6), FSC(1, 7), FSC(2, 0) }; - u32 BloomInnerChannels[] = { FSC(0, 0), FSC(0, 1), FSC(0, 2), FSC(0, 3), FSC(0, 4), FSC(0, 5) }; - flower_desc F0 = {}; - F0.Pos = v3{0, 0, 0}; - F0.ComPort = "\\\\.\\COM11"; - F0.FlowerTagValue = "left"; - F0.StemChannels = StemChannels; - F0.BloomOuterChannels = BloomOuterChannels; - F0.BloomInnerChannels = BloomInnerChannels; - StripCount += BuildFlower(&OutputBuffer0, F0); - - flower_desc F1 = {}; - F1.Pos = v3{0, 0, 0}; - F1.ComPort = "\\\\.\\COM12"; - F1.FlowerTagValue = "center"; - F1.StemChannels = StemChannels; - F1.BloomInnerChannels = BloomInnerChannels; - F1.BloomOuterChannels = BloomOuterChannels; - StripCount += BuildFlower(&OutputBuffer1, F1); - - flower_desc F2 = {}; - F2.Pos = v3{0, 0, 0}; - F2.ComPort = "\\\\.\\COM6"; - F2.FlowerTagValue = "right"; - F2.StemChannels = StemChannels; - F2.BloomInnerChannels = BloomInnerChannels; - F2.BloomOuterChannels = BloomOuterChannels; - StripCount += BuildFlower(&OutputBuffer2, F2); - - WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_one.fold"), StringToData(OutputBuffer0)); - WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_two.fold"), StringToData(OutputBuffer1)); - WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_three.fold"), StringToData(OutputBuffer2)); - - //printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str); - //printf("%d\n", StripCount); - - - - - return 0; + gs_thread_context Ctx = Win32CreateThreadContext(); + GlobalLogBuffer = PushStruct(Ctx.Transient, log_buffer); + *GlobalLogBuffer = Log_Init(Ctx.Transient, 32); + + gs_string OutputBuffer0 = PushString(Ctx.Transient, MB(4)); + gs_string OutputBuffer1 = PushString(Ctx.Transient, MB(4)); + gs_string OutputBuffer2 = PushString(Ctx.Transient, MB(4)); + + WriteAssemblyUARTOpen(&OutputBuffer0, + "Blumen Lumen - Silver Spring - 00", + 100, + v3{-1, 0, 0}, + 21, + ""); + WriteAssemblyUARTOpen(&OutputBuffer1, + "Blumen Lumen - Silver Spring - 01", + 100, + v3{0, 0, 0}, + 21, + ""); + WriteAssemblyUARTOpen(&OutputBuffer2, + "Blumen Lumen - Silver Spring - 02", + 100, + v3{1, 0, 0}, + 21, + ""); + + u32 StripCount = 0; + + u32 StemChannels[] = { FSC(2, 1), FSC(2, 2), FSC(2, 3), FSC(2, 4), FSC(2, 5), FSC(2, 6) }; + u32 BloomOuterChannels[] = { FSC(1, 0), FSC(1, 1), FSC(1, 2), FSC(1, 3), FSC(1, 4), FSC(1, 5), FSC(1, 6), FSC(1, 7), FSC(2, 0) }; + u32 BloomInnerChannels[] = { FSC(0, 0), FSC(0, 1), FSC(0, 2), FSC(0, 3), FSC(0, 4), FSC(0, 5) }; + flower_desc F0 = {}; + F0.Pos = v3{0, 0, 0}; + F0.ComPort = "\\\\.\\COM11"; + F0.FlowerTagValue = "left"; + F0.StemChannels = StemChannels; + F0.BloomOuterChannels = BloomOuterChannels; + F0.BloomInnerChannels = BloomInnerChannels; + StripCount += BuildFlower(&OutputBuffer0, F0); + + flower_desc F1 = {}; + F1.Pos = v3{0, 0, 0}; + F1.ComPort = "\\\\.\\COM12"; + F1.FlowerTagValue = "center"; + F1.StemChannels = StemChannels; + F1.BloomInnerChannels = BloomInnerChannels; + F1.BloomOuterChannels = BloomOuterChannels; + StripCount += BuildFlower(&OutputBuffer1, F1); + + flower_desc F2 = {}; + F2.Pos = v3{0, 0, 0}; + F2.ComPort = "\\\\.\\COM6"; + F2.FlowerTagValue = "right"; + F2.StemChannels = StemChannels; + F2.BloomInnerChannels = BloomInnerChannels; + F2.BloomOuterChannels = BloomOuterChannels; + StripCount += BuildFlower(&OutputBuffer2, F2); + + WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_one.fold"), StringToData(OutputBuffer0)); + WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_two.fold"), StringToData(OutputBuffer1)); + WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_three.fold"), StringToData(OutputBuffer2)); + + //printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str); + //printf("%d\n", StripCount); + + + + + return 0; } diff --git a/src/serial_monitor/first.cpp b/src/serial_monitor/first.cpp index d2e47b0..a87e7c7 100644 --- a/src/serial_monitor/first.cpp +++ b/src/serial_monitor/first.cpp @@ -30,195 +30,195 @@ global log_buffer* GlobalLogBuffer; u8* FindNextHeader(gs_data Data, u8* StartAt) { - u8* At = StartAt; - while (!(At[0] == 'U' && - At[1] == 'P' && - At[2] == 'X' && - At[3] == 'L') && - (u32)(At - Data.Memory) < Data.Size) - { - At++; - } - return At; + u8* At = StartAt; + while (!(At[0] == 'U' && + At[1] == 'P' && + At[2] == 'X' && + At[3] == 'L') && + (u32)(At - Data.Memory) < Data.Size) + { + At++; + } + return At; } void CreateMessage(gs_data* Data, u8 Count) { - gs_memory_cursor WriteCursor = CreateMemoryCursor(*Data); + gs_memory_cursor WriteCursor = MemoryCursorCreateFromData(*Data); + + u32 Channels[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 16, 17, 18, 19, 20, 21, 22, 23, + //40, 41, 42, 43, 44, 45, 46, 47, + }; + + u8* FirstHeaderAddr = 0; + + for (u32 j = 0; j < sizeof(Channels) / sizeof(u32); j++) + { + u32 ChannelIndex = Channels[j]; + uart_header* Header = MemoryCursorPushStruct(&WriteCursor, uart_header); + UART_FillHeader(Header, ChannelIndex, UART_SET_CHANNEL_WS2812); - u32 Channels[] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 16, 17, 18, 19, 20, 21, 22, 23, - //40, 41, 42, 43, 44, 45, 46, 47, - }; - - u8* FirstHeaderAddr = 0; - - for (u32 j = 0; j < sizeof(Channels) / sizeof(u32); j++) + if (FirstHeaderAddr == 0) { - u32 ChannelIndex = Channels[j]; - uart_header* Header = PushStructOnCursor(&WriteCursor, uart_header); - UART_FillHeader(Header, ChannelIndex, UART_SET_CHANNEL_WS2812); - - if (FirstHeaderAddr == 0) - { - FirstHeaderAddr = (u8*)Header; - } - - uart_channel* Channel = PushStructOnCursor(&WriteCursor, uart_channel); - Channel->ElementsCount = 3; - Channel->ColorPackingOrder = 36; // 10010000 - Channel->PixelsCount = 300; - - for (u32 i = 0; i < Channel->PixelsCount; i++) - { - u8* Pixel = PushArrayOnCursor(&WriteCursor, u8, 3); - Pixel[0] = Count; - Pixel[1] = 0; - Pixel[2] = 255 - Count; - } - - uart_footer* Footer = PushStructOnCursor(&WriteCursor, uart_footer); - Footer->CRC = UART_CalculateCRC((u8*)Header, (u8*)(Footer)); + FirstHeaderAddr = (u8*)Header; } - uart_header* DrawAllHeader = PushStructOnCursor(&WriteCursor, uart_header); - UART_FillHeader(DrawAllHeader, 255, UART_DRAW_ALL); - uart_footer* DrawAllFooter = - PushStructOnCursor(&WriteCursor, uart_footer); - DrawAllFooter->CRC = UART_CalculateCRC((u8*)DrawAllHeader, (u8*)(DrawAllFooter)); + uart_channel* Channel = MemoryCursorPushStruct(&WriteCursor, uart_channel); + Channel->ElementsCount = 3; + Channel->ColorPackingOrder = 36; // 10010000 + Channel->PixelsCount = 300; - Data->Size = ((u8*)DrawAllFooter - (u8*)FirstHeaderAddr) + sizeof(uart_footer); + for (u32 i = 0; i < Channel->PixelsCount; i++) + { + u8* Pixel = MemoryCursorPushArray(&WriteCursor, u8, 3); + Pixel[0] = Count; + Pixel[1] = 0; + Pixel[2] = 255 - Count; + } + + uart_footer* Footer = MemoryCursorPushStruct(&WriteCursor, uart_footer); + Footer->CRC = UART_CalculateCRC((u8*)Header, (u8*)(Footer)); + } + + uart_header* DrawAllHeader = MemoryCursorPushStruct(&WriteCursor, uart_header); + UART_FillHeader(DrawAllHeader, 255, UART_DRAW_ALL); + uart_footer* DrawAllFooter = + MemoryCursorPushStruct(&WriteCursor, uart_footer); + DrawAllFooter->CRC = UART_CalculateCRC((u8*)DrawAllHeader, (u8*)(DrawAllFooter)); + + Data->Size = ((u8*)DrawAllFooter - (u8*)FirstHeaderAddr) + sizeof(uart_footer); } int main(int ArgCount, char** Args) { - gs_thread_context Ctx = Win32CreateThreadContext(); - GlobalLogBuffer = AllocatorAllocStruct(Ctx.Allocator, log_buffer); - *GlobalLogBuffer = Log_Init(Ctx.Allocator, 32); + gs_thread_context Ctx = Win32CreateThreadContext(); + GlobalLogBuffer = PushStruct(Ctx.Transient, log_buffer); + *GlobalLogBuffer = Log_Init(Ctx.Transient, 32); + + HANDLE SerialHandle = Win32SerialPort_Open("\\\\.\\COM9", Ctx.Transient); + Win32SerialPort_SetState(SerialHandle, 2000000, 8, 0, 1); + + gs_const_string OutFileName = ConstString("./serial_dump.data"); + + + if (false) + { + Win32SerialPort_SetRead(SerialHandle); - HANDLE SerialHandle = Win32SerialPort_Open("\\\\.\\COM9", Ctx.Transient); - Win32SerialPort_SetState(SerialHandle, 2000000, 8, 0, 1); + gs_data Data = PushSize(Ctx.Transient, KB(32)); - gs_const_string OutFileName = ConstString("./serial_dump.data"); + Win32SerialPort_SetRead(SerialHandle); + u32 ReadSize = Win32SerialPort_ReadMessageWhenReady(SerialHandle, Data); - - if (false) + u8* SetChannelHeaderAddr = 0; + uart_header* SetChannelHeader = 0; + uart_header* DrawAllHeader = 0; + u8* ScanAt = Data.Memory; + do { - Win32SerialPort_SetRead(SerialHandle); - - gs_data Data = PushSizeToData(Ctx.Transient, KB(32)); - - Win32SerialPort_SetRead(SerialHandle); - u32 ReadSize = Win32SerialPort_ReadMessageWhenReady(SerialHandle, Data); - - u8* SetChannelHeaderAddr = 0; - uart_header* SetChannelHeader = 0; - uart_header* DrawAllHeader = 0; - u8* ScanAt = Data.Memory; - do + ScanAt = FindNextHeader(Data, ScanAt); + uart_header* Header = (uart_header*)ScanAt; + + if (Header->RecordType == UART_SET_CHANNEL_WS2812) + { + printf("Set Channel:\n"); + printf(" Channel: %d\n", Header->Channel); + printf(" Pixels: %d\n", ((uart_channel*)(Header + 1))->PixelsCount); + if (!SetChannelHeader) { - ScanAt = FindNextHeader(Data, ScanAt); - uart_header* Header = (uart_header*)ScanAt; - - if (Header->RecordType == UART_SET_CHANNEL_WS2812) - { - printf("Set Channel:\n"); - printf(" Channel: %d\n", Header->Channel); - printf(" Pixels: %d\n", ((uart_channel*)(Header + 1))->PixelsCount); - if (!SetChannelHeader) - { - SetChannelHeaderAddr = (u8*)Header; - SetChannelHeader = Header; - } - } - - if (Header->RecordType == UART_DRAW_ALL) - { - printf("Draw All:\n"); - printf(" Channel: %d\n", Header->Channel); - if (!DrawAllHeader) - { - DrawAllHeader= Header; - } - } - - ScanAt += sizeof(uart_header); - }while(((u32)(ScanAt - Data.Memory + sizeof(uart_header)) < Data.Size)); - - uart_channel* Channel = (uart_channel*)(SetChannelHeader + 1); - - u8* DataStart = (u8*)(Channel + 1); - - uart_footer* Footer = (uart_footer*)(DataStart + (Channel->ElementsCount * Channel->PixelsCount)); - - u32 TestCRC = UART_CalculateCRC((u8*)SetChannelHeader, (u8*)(Footer)); - - uart_footer* DrawAllFooter = (uart_footer*)(DrawAllHeader + 1); - u32 DrawwAllCRC = UART_CalculateCRC((u8*)DrawAllHeader, (u8*)(DrawAllFooter)); - - HANDLE FileHandle = CreateFileA(OutFileName.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (FileHandle != INVALID_HANDLE_VALUE) - { - DWORD BytesWritten = 0; - if (!WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL)) - { - InvalidCodePath; - } + SetChannelHeaderAddr = (u8*)Header; + SetChannelHeader = Header; } - CloseHandle(FileHandle); - Win32SerialPort_Close(SerialHandle); - - } - else if (true) - { - gs_data Data = PushSizeToData(Ctx.Transient, KB(32)); - - u8 Count = 0; - while(true) + } + + if (Header->RecordType == UART_DRAW_ALL) + { + printf("Draw All:\n"); + printf(" Channel: %d\n", Header->Channel); + if (!DrawAllHeader) { - CreateMessage(&Data, ++Count); - Win32SerialPort_Write(SerialHandle, Data); - Sleep(100); + DrawAllHeader= Header; } - } - else if (false) - { - gs_data Data = PushSizeToData(Ctx.Transient, KB(32)); - gs_file File = Win32ReadEntireFile(Ctx.FileHandler, OutFileName, Data); - - gs_data Messages = {0}; - u8* ScanAt = Data.Memory; - ScanAt = FindNextHeader(Data, ScanAt); - uart_header* FirstHeader = (uart_header*)ScanAt; - ScanAt += sizeof(uart_header); - - uart_header* LastHeader = 0; - do - { - ScanAt = FindNextHeader(Data, ScanAt); - uart_header* Header = (uart_header*)ScanAt; - if (Header->RecordType == UART_DRAW_ALL) - { - LastHeader = Header; - } - ScanAt += sizeof(uart_header); - }while((u32)(ScanAt - Data.Memory) < Data.Size); - - u8* OnePastLastByte = ((u8*)(LastHeader + 1)) + sizeof(uart_footer); - - Messages.Memory = (u8*)FirstHeader; - Messages.Size = OnePastLastByte - Messages.Memory; - - while (true) - { - Win32SerialPort_Write(SerialHandle, Messages); - Sleep(100); - } - } + } + + ScanAt += sizeof(uart_header); + }while(((u32)(ScanAt - Data.Memory + sizeof(uart_header)) < Data.Size)); - return 0; + uart_channel* Channel = (uart_channel*)(SetChannelHeader + 1); + + u8* DataStart = (u8*)(Channel + 1); + + uart_footer* Footer = (uart_footer*)(DataStart + (Channel->ElementsCount * Channel->PixelsCount)); + + u32 TestCRC = UART_CalculateCRC((u8*)SetChannelHeader, (u8*)(Footer)); + + uart_footer* DrawAllFooter = (uart_footer*)(DrawAllHeader + 1); + u32 DrawwAllCRC = UART_CalculateCRC((u8*)DrawAllHeader, (u8*)(DrawAllFooter)); + + HANDLE FileHandle = CreateFileA(OutFileName.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (FileHandle != INVALID_HANDLE_VALUE) + { + DWORD BytesWritten = 0; + if (!WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL)) + { + InvalidCodePath; + } + } + CloseHandle(FileHandle); + Win32SerialPort_Close(SerialHandle); + + } + else if (true) + { + gs_data Data = PushSize(Ctx.Transient, KB(32)); + + u8 Count = 0; + while(true) + { + CreateMessage(&Data, ++Count); + Win32SerialPort_Write(SerialHandle, Data); + Sleep(100); + } + } + else if (false) + { + gs_data Data = PushSize(Ctx.Transient, KB(32)); + gs_file File = Win32ReadEntireFile(Ctx.FileHandler, OutFileName, Data); + + gs_data Messages = {0}; + u8* ScanAt = Data.Memory; + ScanAt = FindNextHeader(Data, ScanAt); + uart_header* FirstHeader = (uart_header*)ScanAt; + ScanAt += sizeof(uart_header); + + uart_header* LastHeader = 0; + do + { + ScanAt = FindNextHeader(Data, ScanAt); + uart_header* Header = (uart_header*)ScanAt; + if (Header->RecordType == UART_DRAW_ALL) + { + LastHeader = Header; + } + ScanAt += sizeof(uart_header); + }while((u32)(ScanAt - Data.Memory) < Data.Size); + + u8* OnePastLastByte = ((u8*)(LastHeader + 1)) + sizeof(uart_footer); + + Messages.Memory = (u8*)FirstHeader; + Messages.Size = OnePastLastByte - Messages.Memory; + + while (true) + { + Win32SerialPort_Write(SerialHandle, Messages); + Sleep(100); + } + } + + return 0; } diff --git a/src/tests/memory_arena_tests.cpp b/src/tests/memory_arena_tests.cpp index d52623a..82d446f 100644 --- a/src/tests/memory_arena_tests.cpp +++ b/src/tests/memory_arena_tests.cpp @@ -1,7 +1,5 @@ -#include "../app/platform_win32/win32_foldhaus_memory.h" - internal u32 -TESTNextRandom(u32* LastRandomValue) +NextRandom(u32* LastRandomValue) { u32 Result = *LastRandomValue; Result ^= Result << 13; @@ -11,64 +9,63 @@ TESTNextRandom(u32* LastRandomValue) return Result; } -internal void +void MemoryArenaTests() { Test("Allocator") { - gs_allocator Allocator = CreateAllocator(Win32Alloc, Win32Free); + gs_allocator A = CreatePlatformAllocator(); - u8* Data = AllocatorAllocArray(Allocator, u8, 4096); + u8* Data = AllocArray(A, u8, 4096, "root"); for (int i = 0; i < 4096; i++) Data[i] = (i % MaxU8); bool Success = true; - for (int i = 0; i < 4096; i++) Success &= (Data[i] == (i % MaxU8)); + for (int i = 0; i < 4096; i++) Success &= Data[i] == (i % MaxU8); TestResult(Success); - AllocatorFreeArray(Allocator, Data, u8, 4096); - // idk how to test free + FreeArray(A, Data, u8, 4096); + TestResult(true); // TODO(PS): How do we test free? } Test("Memory Cursor") { - gs_allocator A = CreateAllocator(Win32Alloc, Win32Free); + gs_allocator A = CreatePlatformAllocator(); u64 Size = 4096; - gs_data D = AllocatorAlloc(A, Size); - gs_memory_cursor C = CreateMemoryCursor(D); + gs_data D = AllocData(A, Size, "root"); + gs_memory_cursor C = MemoryCursorCreate(D.Memory, D.Size); - u64 RoomLeft = CursorRoomLeft(C); + u64 RoomLeft = MemoryCursorRoomLeft(C); TestResult(RoomLeft == Size); + TestResult(MemoryCursorHasRoom(C)); - TestResult(CursorHasRoom(C, 2048)); - TestResult(CursorHasRoom(C, Size)); - TestResult(!CursorHasRoom(C, Size + 1)); + TestResult(MemoryCursorCanPush(C, 2048)); + TestResult(MemoryCursorCanPush(C, Size)); + TestResult(!MemoryCursorCanPush(C, Size + 1)); for (u64 i = 0; i < 2048; i++) { - u8* Byte = PushSizeOnCursor(&C, 1).Memory; + u8* Byte = MemoryCursorPushSize(&C, 1).Memory; *Byte = (u8)(i % 256); } - RoomLeft = CursorRoomLeft(C); + RoomLeft = MemoryCursorRoomLeft(C); TestResult(RoomLeft == (Size - 2048)); - PopSizeOnCursor(&C, 2048); + MemoryCursorPopSize(&C, 2048); TestResult(C.Position == 0); bool Success = true; for (u64 i = 0; i < 2048; i++) { - u8* Byte = PushSizeOnCursor(&C, 1).Memory; + u8* Byte = MemoryCursorPushSize(&C, 1).Memory; Success &= *Byte == (u8)(i % 256); } TestResult(Success); - - AllocatorFree(A, D.Memory, D.Size); } Test("Memory Arena") { - gs_allocator Al = CreateAllocator(Win32Alloc, Win32Free); - gs_memory_arena A = CreateMemoryArena(Al, "test", 128, 4); + gs_allocator Al = CreatePlatformAllocator(); + gs_memory_arena A = MemoryArenaCreate(128, 4, Al, 0, 0, "Test"); // NOTE(PS): We loop through this block 3 times // 1. Make sure the arena works out of the box @@ -76,42 +73,40 @@ MemoryArenaTests() // 3. Make sure the arena works the same way after freeing for (int i = 0; i < 3; i++) { - gs_data D0 = PushSize_(&A, 32, FileNameAndLineNumberString); + gs_data D0 = PushSize_(&A, 32, DEBUG_LOC); TestResult(D0.Size == 32); // NOTE(PS): This should still result in 32 bytes // because its going to align the Cursor after // it allocates to a multiple of 4 bytes - gs_data D1 = PushSize_(&A, 30, FileNameAndLineNumberString); + gs_data D1 = PushSize_(&A, 30, DEBUG_LOC); TestResult(D1.Size == 32); // NOTE(PS): Allocating bigger than the size remaining // in the current cursor - gs_data D2 = PushSize_(&A, 128, FileNameAndLineNumberString); + gs_data D2 = PushSize_(&A, 128, DEBUG_LOC); TestResult(D2.Size == 128); - TestResult(A.CursorsCount != 1); + TestResult(A.CursorsRoot != A.CursorsHead); // NOTE(PS): Because there is still room in cursor // 0, the head of this gs_data should be one byte // past the end of D1 - gs_data D3 = PushSize_(&A, 32, FileNameAndLineNumberString); + gs_data D3 = PushSize_(&A, 32, DEBUG_LOC); TestResult(D3.Memory == D1.Memory + D1.Size); if (i == 0) { - ClearArena(&A); + MemoryArenaClear(&A); } else if (i == 1) { - FreeMemoryArena(&A); + MemoryArenaFree(&A); } } - - FreeMemoryArena(&A); } - Test("Memory Arena: Push") + Test("Memory Arena - Push") { - gs_allocator Al = CreateAllocator(Win32Alloc, Win32Free); - gs_memory_arena A = CreateMemoryArena(Al, "test", 128, 4); + gs_allocator Al = CreatePlatformAllocator(); + gs_memory_arena A = MemoryArenaCreate(128, 8, Al, 0, 0, "Test"); // NOTE(PS): This makes sure that the Arena is moving its next allocation // pointer forward the appropriate amount after each allocation. If it isnt' @@ -133,16 +128,21 @@ MemoryArenaTests() } TestResult(Success); - FreeArena(&A); } int FreeCount = 0; int ClearCount = 0; - Test("Memory Arena: Stress Test") + gs_debug_allocations_list* DEBUGAllocations = 0; + + Test("Memory Arena - Stress Test") { - gs_allocator Al = CreateAllocator(Win32Alloc, Win32Free); - gs_memory_arena A = CreateMemoryArena(Al, "test", 128, 4); + // NOTE(PS): We're going to create thousands of allocations + // on the allocator of varying sizes. We're also going to clear + // and free the arena at random times to make sure it all works. + + gs_allocator Al = CreatePlatformAllocator(); + gs_memory_arena A = MemoryArenaCreate(4096, 4, Al, 0, 0, "Test"); // NOTE(PS): This is an array of allocation sizes // As we repeat the loop we will get values out of this array @@ -157,19 +157,19 @@ MemoryArenaTests() u32 RandomSeed = 1923; for (u64 i = 0; i < (4096 * 14); i++) { - TESTNextRandom(&RandomSeed); + NextRandom(&RandomSeed); u32 SizeIndex = RandomSeed % RandomSizesCount; u64 RandomSize = RandomSizes[SizeIndex]; if (RandomSize == 0) { - ClearArena(&A); + MemoryArenaClear(&A); ClearCount++; } else if (RandomSize == 2) { - FreeArena(&A); + MemoryArenaFree(&A); FreeCount++; } else { - gs_data D = PushSize_(&A, RandomSize, FileNameAndLineNumberString); + gs_data D = PushSize_(&A, RandomSize, DEBUG_LOC); // NOTE(PS): This check has to be >= because the arena // might have adjusted to maintain alignment on this // allocation. @@ -179,6 +179,24 @@ MemoryArenaTests() TestResult(Success); + DEBUGAllocations = Al.DEBUGAllocList; } -} + printf("\tMemory Arena Cleared: %d times\n", ClearCount); + printf("\tMemory Arena Freed: %d times\n", FreeCount); + +#if 0 + printf("\n\nAllocations:\n"); + for (gs_debug_memory_allocation* ARecord = DEBUGAllocations->Root; + ARecord != 0; + ARecord = ARecord->Next) + { + printf("\t"); + printf("%lld\t%s:%d - %s\n", + ARecord->Size, + ARecord->Loc.File, + ARecord->Loc.Line, + ARecord->Loc.Function); + } +#endif +} \ No newline at end of file diff --git a/src/tests/sanity_tests.cpp b/src/tests/sanity_tests.cpp index fa8f271..440d4e6 100644 --- a/src/tests/sanity_tests.cpp +++ b/src/tests/sanity_tests.cpp @@ -14,11 +14,10 @@ #include "../gs_libs/gs_path.h" #include "../gs_libs/gs_csv.h" +#include "../app/platform_win32/win32_foldhaus_memory.h" #include "./memory_arena_tests.cpp" gs_memory_arena Scratch = {}; -void* Alloc(u64 Size, u64* ResultSize) { *ResultSize = Size; return malloc(Size); } -void Free(void* Ptr, u64 Size) { return free(Ptr); } bool StringTest (gs_const_string StrA, gs_const_string StrB) { @@ -38,9 +37,76 @@ Flower A 55 32 128 foo, bar, blah, baz, whatever Flower B 123 344 32 foo, bar, blah, baz, whatever Flower C 55 32 128 foo, bar, blah, baz, whatever)FOO"; +struct test_sll +{ + u32 Val; + test_sll* Next; +}; + int main (int ArgCount, char** Args) { - Scratch = CreateMemoryArena(CreateAllocator(Alloc, Free), "Scratch"); + gs_allocator Al = CreatePlatformAllocator(); + Scratch = MemoryArenaCreate(KB(4), Bytes(8), Al, 0, 0, "Scratch"); + + Test("SLL 1") + { + test_sll* Root = 0; + test_sll* Head = 0; + + test_sll* First = PushStruct(&Scratch, test_sll); + First->Val = 0; + SLLInit(Root, Head, First); + TestResult((Root == First) && (Head == First)); + + for (u32 i = 1; i < 4; i++) + { + test_sll* New = PushStruct(&Scratch, test_sll); + New->Val = i; + SLLPush(Head, New); + TestResult((Root == First) && (Head == New)); + } + + bool Success = true; + u32 i = 0; + for (test_sll* At = Root; + At && At->Next != 0; + SLLNext(At)) + { + Success &= (At->Val == i); + i += 1; + } + TestResult(Success); + } + + Test("SLL Push Or Init") + { + test_sll* Root = 0; + test_sll* Head = 0; + + test_sll* First = PushStruct(&Scratch, test_sll); + First->Val = 0; + SLLPushOrInit(Root, Head, First); + TestResult((Root == First) && (Head == First)); + + for (u32 i = 1; i < 4; i++) + { + test_sll* New = PushStruct(&Scratch, test_sll); + New->Val = i; + SLLPushOrInit(Root, Head, New); + TestResult((Root == First) && (Head == New)); + } + + bool Success = true; + u32 i = 0; + for (test_sll* At = Root; + At && At->Next != 0; + SLLNext(At)) + { + Success &= (At->Val == i); + i += 1; + } + TestResult(Success); + } Test("gs_string") { From 9a643cd5b63ceed9f6440622c74449f45e33bcc8 Mon Sep 17 00:00:00 2001 From: PS Date: Fri, 6 Aug 2021 18:35:14 -0500 Subject: [PATCH 096/151] Updated gen_blumen_lumen to be able to output both UART and SACN variants --- src/sculpture_gen/gen_blumen_lumen.cpp | 23 ++++- src/sculpture_gen/sculpture_gen.h | 118 +++++++++++++++---------- 2 files changed, 92 insertions(+), 49 deletions(-) diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index 12b09d3..7f101f0 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -61,7 +61,10 @@ BuildLoop(gs_string* OutputBuffer, loop_desc Desc) Channel = Desc.ChannelStart + i; } - WriteLedStripOpen(OutputBuffer, Channel, Desc.ComPort); + u32 SACNUniverseStart = 0; + u32 SACNChannelStart = 0; + WriteLedStripOpen(OutputBuffer, Channel, Desc.ComPort, + SACNUniverseStart, SACNChannelStart); WriteSegmentSequenceOpen(OutputBuffer, Desc.SubsegmentsCount); for (u32 j = 0; j < Desc.SubsegmentsCount; j++) @@ -184,6 +187,7 @@ int main(int ArgCount, char** Args) gs_string OutputBuffer1 = PushString(Ctx.Transient, MB(4)); gs_string OutputBuffer2 = PushString(Ctx.Transient, MB(4)); +#if 0 WriteAssemblyUARTOpen(&OutputBuffer0, "Blumen Lumen - Silver Spring - 00", 100, @@ -202,6 +206,23 @@ int main(int ArgCount, char** Args) v3{1, 0, 0}, 21, ""); +#else + WriteAssemblySACNOpen(&OutputBuffer0, + "Blumen Lumen - Silver Spring - 00", + 100, + v3{-1, 0, 0}, + 21); + WriteAssemblySACNOpen(&OutputBuffer1, + "Blumen Lumen - Silver Spring - 01", + 100, + v3{0, 0, 0}, + 21); + WriteAssemblySACNOpen(&OutputBuffer2, + "Blumen Lumen - Silver Spring - 02", + 100, + v3{1, 0, 0}, + 21); +#endif u32 StripCount = 0; diff --git a/src/sculpture_gen/sculpture_gen.h b/src/sculpture_gen/sculpture_gen.h index bbba18c..6336f46 100644 --- a/src/sculpture_gen/sculpture_gen.h +++ b/src/sculpture_gen/sculpture_gen.h @@ -8,87 +8,109 @@ internal void WriteIndented(gs_string* Buffer, u32 Indent, char* Format, ...) { - va_list Args; - va_start(Args, Format); - - for (u32 i = 0; i < Indent; i++) - { - OutChar(Buffer, '\t'); - } - - PrintFArgsList(Buffer, Format, Args); - va_end(Args); + va_list Args; + va_start(Args, Format); + + for (u32 i = 0; i < Indent; i++) + { + OutChar(Buffer, '\t'); + } + + PrintFArgsList(Buffer, Format, Args); + va_end(Args); +} + +internal void +WriteAssemblyCommonOpen(gs_string* Buffer, char* Name, u32 Scale, v3 Center, u32 StripCount) +{ + WriteIndented(Buffer, 0, "assembly_name: \"%s\";\n", Name); + WriteIndented(Buffer, 0, "assembly_scale: %d;\n", Scale); + WriteIndented(Buffer, 0, "assembly_center: (%f, %f, %f);\n", Center.x, Center.y, Center.z); + WriteIndented(Buffer, 0, "led_strip_count: %d;\n", StripCount); } internal void WriteAssemblyUARTOpen(gs_string* Buffer, char* Name, u32 Scale, v3 Center, u32 StripCount, char* ComPort) { - WriteIndented(Buffer, 0, "assembly_name: \"%s\";\n", Name); - WriteIndented(Buffer, 0, "assembly_scale: %d;\n", Scale); - WriteIndented(Buffer, 0, "assembly_center: (%f, %f, %f);\n", Center.x, Center.y, Center.z); - WriteIndented(Buffer, 0, "led_strip_count: %d;\n", StripCount); - WriteIndented(Buffer, 0, "output_mode: \"UART\";\n"); - - if (ComPort) - { - WriteIndented(Buffer, 0, "com_port: \"%s\";\n", ComPort); - } + WriteAssemblyCommonOpen(Buffer, Name, Scale, Center, StripCount); + WriteIndented(Buffer, 0, "output_mode: \"UART\";\n"); + + if (ComPort) + { + WriteIndented(Buffer, 0, "com_port: \"%s\";\n", ComPort); + } } internal void -WriteLedStripOpen(gs_string* Buffer, u32 Channel, char* ComPort) +WriteAssemblySACNOpen(gs_string* Buffer, char* Name, u32 Scale, v3 Center, u32 StripCount) { - WriteIndented(Buffer, 0, "led_strip:\n{\n"); - WriteIndented(Buffer, 1, "output_uart: {\n"); - WriteIndented(Buffer, 2, "channel: %d;\n", Channel); - WriteIndented(Buffer, 2, "com_port: \"%s\";\n", ComPort); - WriteIndented(Buffer, 1, "};\n\n"); + WriteAssemblyCommonOpen(Buffer, Name, Scale, Center, StripCount); + WriteIndented(Buffer, 0, "output_mode: \"SACN\";\n"); +} + +internal void +WriteLedStripOpen(gs_string* Buffer, u32 UARTChannel, char* UARTComPort, u32 SACNStartUniverse, u32 SACNStartChannel) +{ + WriteIndented(Buffer, 0, "led_strip:\n{\n"); + + // SACN + WriteIndented(Buffer, 1, "output_sacn: {\n"); + WriteIndented(Buffer, 2, "start_universe: %d;\n", SACNStartUniverse); + WriteIndented(Buffer, 2, "start_channel: %d;\n", SACNStartChannel); + WriteIndented(Buffer, 1, "};\n\n"); + + // UART + WriteIndented(Buffer, 1, "output_uart: {\n"); + WriteIndented(Buffer, 2, "channel: %d;\n", UARTChannel); + WriteIndented(Buffer, 2, "com_port: \"%s\";\n", UARTComPort); + WriteIndented(Buffer, 1, "};\n\n"); + } internal void WriteSegmentSequenceOpen(gs_string* Buffer, u32 SegmentCount) { - WriteIndented(Buffer, 1, "segment: {\n"); - WriteIndented(Buffer, 2, "point_placement_type: \"SegmentSequence\";\n"); - WriteIndented(Buffer, 2, "segment_sequence:\n"); - WriteIndented(Buffer, 2, "{\n"); - WriteIndented(Buffer, 3, "segment_count: %d;\n", SegmentCount); + WriteIndented(Buffer, 1, "segment: {\n"); + WriteIndented(Buffer, 2, "point_placement_type: \"SegmentSequence\";\n"); + WriteIndented(Buffer, 2, "segment_sequence:\n"); + WriteIndented(Buffer, 2, "{\n"); + WriteIndented(Buffer, 3, "segment_count: %d;\n", SegmentCount); } internal void WriteSegmentSequenceSegment(gs_string* Buffer, v3 P0, v3 P1, u32 LedCount) { - WriteIndented(Buffer, 3, "segment: {\n"); - WriteIndented(Buffer, 4, "point_placement_type: \"InterpolatePoints\";\n"); - WriteIndented(Buffer, 4, "interpolate_points: {\n"); - WriteIndented(Buffer, 5, "start: (%f, %f, %f);\n", P0.x, P0.y, P0.z); - WriteIndented(Buffer, 5, "end: (%f, %f, %f);\n", P1.x, P1.y, P1.z); - WriteIndented(Buffer, 5, "led_count: %d;\n", LedCount); - WriteIndented(Buffer, 4, "};\n"); - WriteIndented(Buffer, 3, "};\n"); + WriteIndented(Buffer, 3, "segment: {\n"); + WriteIndented(Buffer, 4, "point_placement_type: \"InterpolatePoints\";\n"); + WriteIndented(Buffer, 4, "interpolate_points: {\n"); + WriteIndented(Buffer, 5, "start: (%f, %f, %f);\n", P0.x, P0.y, P0.z); + WriteIndented(Buffer, 5, "end: (%f, %f, %f);\n", P1.x, P1.y, P1.z); + WriteIndented(Buffer, 5, "led_count: %d;\n", LedCount); + WriteIndented(Buffer, 4, "};\n"); + WriteIndented(Buffer, 3, "};\n"); } internal void WriteSegmentSequenceClose(gs_string* Buffer) { - WriteIndented(Buffer, 2, "};\n"); - WriteIndented(Buffer, 1, "};\n"); + WriteIndented(Buffer, 2, "};\n"); + WriteIndented(Buffer, 1, "};\n"); } internal void WriteSegmentTagsOpen(gs_string* Buffer, u32 TagCount) { - WriteIndented(Buffer, 1, "tags_count: %d;\n", TagCount); + WriteIndented(Buffer, 1, "tags_count: %d;\n", TagCount); } internal void WriteSegmentTag(gs_string* Buffer, char* TagName, char* TagValue) { - WriteIndented(Buffer, 1, "tag: {\n"); - WriteIndented(Buffer, 2, "name: \"%s\";\n", TagName); - WriteIndented(Buffer, 2, "value: \"%s\";\n", TagValue); - WriteIndented(Buffer, 1, "};\n"); - + WriteIndented(Buffer, 1, "tag: {\n"); + WriteIndented(Buffer, 2, "name: \"%s\";\n", TagName); + WriteIndented(Buffer, 2, "value: \"%s\";\n", TagValue); + WriteIndented(Buffer, 1, "};\n"); + } internal void @@ -99,7 +121,7 @@ WriteSegmentTagsClose(gs_string* Buffer) internal void WriteLedStripClose(gs_string* Buffer) { - WriteIndented(Buffer, 0, "};\n"); + WriteIndented(Buffer, 0, "};\n"); } #define SCULPTURE_GEN_H From d68cd217bc9f3067dbd5900e6b1f63876ddf6340 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 18 Sep 2021 12:54:02 -0500 Subject: [PATCH 097/151] Updating to new flower format --- build/build_app_msvc_win32_debug.bat | 2 +- .../platform_win32/win32_foldhaus_socket.h | 5 -- src/sculpture_gen/gen_blumen_lumen.cpp | 61 ++++++++++++++----- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index 2dafabf..1997c89 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -6,7 +6,7 @@ SET MyPath=%MyPath:~0,-1% call %MyPath%\_prebuild_win32.bat app debug msvc call %MyPath%\setup_cl.bat -SET CommonCompilerFlags=-nologo -DDEBUG=0 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src +SET CommonCompilerFlags=-nologo -DDEBUG=1 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src SET CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -I%CommonLibs% -Od %CommonCompilerFlags% diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index ab656f5..1f05067 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -397,7 +397,6 @@ Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, c s32 LengthSent = sendto(Socket->Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in)); - Log_Message(GlobalLogBuffer, "Attempting To Send Network Data: "); if (LengthSent == SOCKET_ERROR) { s32 Error = WSAGetLastError(); @@ -416,10 +415,6 @@ Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, c } break; } } - else - { - Log_Message(GlobalLogBuffer, "Sent\n"); - } return LengthSent; } diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index 7f101f0..b14b19a 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -29,6 +29,11 @@ typedef struct u32 SubsegmentsCount; u32 SubsegmentLeds; + // SACN + u32 SACNUniverseStart; + u32 SACNChannelStart; + + // UART // Only one of these two values is needed. // If ChannelsArray != 0, then it will be used, and assumed to // have SegmentsCount values @@ -61,10 +66,8 @@ BuildLoop(gs_string* OutputBuffer, loop_desc Desc) Channel = Desc.ChannelStart + i; } - u32 SACNUniverseStart = 0; - u32 SACNChannelStart = 0; WriteLedStripOpen(OutputBuffer, Channel, Desc.ComPort, - SACNUniverseStart, SACNChannelStart); + Desc.SACNUniverseStart, Desc.SACNChannelStart); WriteSegmentSequenceOpen(OutputBuffer, Desc.SubsegmentsCount); for (u32 j = 0; j < Desc.SubsegmentsCount; j++) @@ -100,6 +103,13 @@ typedef struct v3 Pos; char* ComPort; char* FlowerTagValue; + + // SACN + u32 SACNStemInnerStartUniverse; + u32 SACNStemOuterStartUniverse; + u32 SACNFlowerStemStartUniverse; + + // UART u32* StemChannels; u32* BloomOuterChannels; u32* BloomInnerChannels; @@ -115,9 +125,12 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc) BloomStemInner.CenterStart = v3{0, 1.4f, 0}; BloomStemInner.CenterEnd = v3{0, .9f, 0}; BloomStemInner.Radius = .05f; - BloomStemInner.SegmentsCount = 6; + //BloomStemInner.SegmentsCount = 6; + BloomStemInner.SegmentsCount = 1; BloomStemInner.SubsegmentsCount = 3; BloomStemInner.SubsegmentLeds = 35; + BloomStemInner.SACNUniverseStart = Desc.SACNStemInnerStartUniverse; + BloomStemInner.SACNChannelStart = 1; BloomStemInner.ChannelsArray = Desc.BloomInnerChannels; BloomStemInner.ComPort = Desc.ComPort; BloomStemInner.SectionTagValue = "inner_bloom"; @@ -129,9 +142,12 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc) BloomStemOuter.CenterStart = v3{0, .5f, 0}; BloomStemOuter.CenterEnd = v3{0, .9f, 0}; BloomStemOuter.Radius = .07f; - BloomStemOuter.SegmentsCount = 9; + //BloomStemOuter.SegmentsCount = 9; + BloomStemOuter.SegmentsCount = 1; BloomStemOuter.SubsegmentsCount = 3; BloomStemOuter.SubsegmentLeds = 41; + BloomStemOuter.SACNUniverseStart = Desc.SACNStemOuterStartUniverse; + BloomStemOuter.SACNChannelStart = 1; BloomStemOuter.ChannelsArray = Desc.BloomOuterChannels; BloomStemOuter.ComPort = Desc.ComPort; BloomStemOuter.SectionTagValue = "outer_bloom"; @@ -145,9 +161,12 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc) FlowerStem.CenterStart = v3{0, -1.5f, 0}; FlowerStem.CenterEnd = v3{0, .5f, 0}; FlowerStem.Radius = .05f; - FlowerStem.SegmentsCount = 6; + //FlowerStem.SegmentsCount = 6; + FlowerStem.SegmentsCount = 1; FlowerStem.SubsegmentsCount = 1; FlowerStem.SubsegmentLeds = 300; + FlowerStem.SACNUniverseStart = Desc.SACNFlowerStemStartUniverse; + FlowerStem.SACNChannelStart = 1; FlowerStem.ChannelsArray = Desc.StemChannels; FlowerStem.ComPort = Desc.ComPort; FlowerStem.SectionTagValue = "stem"; @@ -187,44 +206,45 @@ int main(int ArgCount, char** Args) gs_string OutputBuffer1 = PushString(Ctx.Transient, MB(4)); gs_string OutputBuffer2 = PushString(Ctx.Transient, MB(4)); + u32 StripCount = 3; // used to be 21 #if 0 WriteAssemblyUARTOpen(&OutputBuffer0, "Blumen Lumen - Silver Spring - 00", 100, v3{-1, 0, 0}, - 21, + StripCount, ""); WriteAssemblyUARTOpen(&OutputBuffer1, "Blumen Lumen - Silver Spring - 01", 100, v3{0, 0, 0}, - 21, + StripCount, ""); WriteAssemblyUARTOpen(&OutputBuffer2, "Blumen Lumen - Silver Spring - 02", 100, v3{1, 0, 0}, - 21, + StripCount, ""); #else WriteAssemblySACNOpen(&OutputBuffer0, "Blumen Lumen - Silver Spring - 00", 100, v3{-1, 0, 0}, - 21); + StripCount); WriteAssemblySACNOpen(&OutputBuffer1, "Blumen Lumen - Silver Spring - 01", 100, v3{0, 0, 0}, - 21); + StripCount); WriteAssemblySACNOpen(&OutputBuffer2, "Blumen Lumen - Silver Spring - 02", 100, v3{1, 0, 0}, - 21); + StripCount); #endif - u32 StripCount = 0; + u32 StripCountOut = 0; u32 StemChannels[] = { FSC(2, 1), FSC(2, 2), FSC(2, 3), FSC(2, 4), FSC(2, 5), FSC(2, 6) }; u32 BloomOuterChannels[] = { FSC(1, 0), FSC(1, 1), FSC(1, 2), FSC(1, 3), FSC(1, 4), FSC(1, 5), FSC(1, 6), FSC(1, 7), FSC(2, 0) }; @@ -233,28 +253,37 @@ int main(int ArgCount, char** Args) F0.Pos = v3{0, 0, 0}; F0.ComPort = "\\\\.\\COM11"; F0.FlowerTagValue = "left"; + F0.SACNStemInnerStartUniverse = 1; + F0.SACNStemOuterStartUniverse = 2; + F0.SACNFlowerStemStartUniverse = 3; F0.StemChannels = StemChannels; F0.BloomOuterChannels = BloomOuterChannels; F0.BloomInnerChannels = BloomInnerChannels; - StripCount += BuildFlower(&OutputBuffer0, F0); + StripCountOut += BuildFlower(&OutputBuffer0, F0); flower_desc F1 = {}; F1.Pos = v3{0, 0, 0}; F1.ComPort = "\\\\.\\COM12"; F1.FlowerTagValue = "center"; + F1.SACNStemInnerStartUniverse = 6; + F1.SACNStemOuterStartUniverse = 7; + F1.SACNFlowerStemStartUniverse = 8; F1.StemChannels = StemChannels; F1.BloomInnerChannels = BloomInnerChannels; F1.BloomOuterChannels = BloomOuterChannels; - StripCount += BuildFlower(&OutputBuffer1, F1); + StripCountOut += BuildFlower(&OutputBuffer1, F1); flower_desc F2 = {}; F2.Pos = v3{0, 0, 0}; F2.ComPort = "\\\\.\\COM6"; F2.FlowerTagValue = "right"; + F2.SACNStemInnerStartUniverse = 11; + F2.SACNStemOuterStartUniverse = 12; + F2.SACNFlowerStemStartUniverse = 13; F2.StemChannels = StemChannels; F2.BloomInnerChannels = BloomInnerChannels; F2.BloomOuterChannels = BloomOuterChannels; - StripCount += BuildFlower(&OutputBuffer2, F2); + StripCountOut += BuildFlower(&OutputBuffer2, F2); WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_one.fold"), StringToData(OutputBuffer0)); WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_two.fold"), StringToData(OutputBuffer1)); From 2a2668e2b71c1845bc7f67fdd16512e14ed34488 Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 19 Sep 2021 13:54:49 -0500 Subject: [PATCH 098/151] Fixing blumen output --- src/sculpture_gen/gen_blumen_lumen.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index b14b19a..f67d4e6 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -253,9 +253,9 @@ int main(int ArgCount, char** Args) F0.Pos = v3{0, 0, 0}; F0.ComPort = "\\\\.\\COM11"; F0.FlowerTagValue = "left"; - F0.SACNStemInnerStartUniverse = 1; + F0.SACNStemInnerStartUniverse = 3; F0.SACNStemOuterStartUniverse = 2; - F0.SACNFlowerStemStartUniverse = 3; + F0.SACNFlowerStemStartUniverse = 1; F0.StemChannels = StemChannels; F0.BloomOuterChannels = BloomOuterChannels; F0.BloomInnerChannels = BloomInnerChannels; @@ -265,9 +265,9 @@ int main(int ArgCount, char** Args) F1.Pos = v3{0, 0, 0}; F1.ComPort = "\\\\.\\COM12"; F1.FlowerTagValue = "center"; - F1.SACNStemInnerStartUniverse = 6; + F1.SACNStemInnerStartUniverse = 8; F1.SACNStemOuterStartUniverse = 7; - F1.SACNFlowerStemStartUniverse = 8; + F1.SACNFlowerStemStartUniverse = 6; F1.StemChannels = StemChannels; F1.BloomInnerChannels = BloomInnerChannels; F1.BloomOuterChannels = BloomOuterChannels; @@ -277,9 +277,9 @@ int main(int ArgCount, char** Args) F2.Pos = v3{0, 0, 0}; F2.ComPort = "\\\\.\\COM6"; F2.FlowerTagValue = "right"; - F2.SACNStemInnerStartUniverse = 11; + F2.SACNStemInnerStartUniverse = 13; F2.SACNStemOuterStartUniverse = 12; - F2.SACNFlowerStemStartUniverse = 13; + F2.SACNFlowerStemStartUniverse = 11; F2.StemChannels = StemChannels; F2.BloomInnerChannels = BloomInnerChannels; F2.BloomOuterChannels = BloomOuterChannels; From 70e0a3e4cb789efa3a71996ff23153a4e8918019 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sun, 19 Sep 2021 15:30:40 -0400 Subject: [PATCH 099/151] local changes --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 1901 +++++++++-------- .../ss_blumen_lumen/blumen_lumen_settings.h | 3 +- 2 files changed, 953 insertions(+), 951 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 5dcc0a8..486a3fc 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -8,72 +8,72 @@ internal animation_handle_array LoadAllAnimationsInDir(gs_const_string Path, blumen_lumen_state* BLState, app_state* State, context Context) { - animation_handle_array Result = {}; - - gs_thread_context Ctx = Context.ThreadContext; - gs_file_info_array FilesInDir = EnumerateDirectory(Ctx.FileHandler, State->Transient, Path, 0); - - Result.Count = FilesInDir.Count; - Result.Handles = PushArray(&State->Permanent, animation_handle, Result.Count); - - for (u32 i = 0; i < FilesInDir.Count; i++) - { - gs_file_info File = FilesInDir.Values[i]; - Result.Handles[i] = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - File.Path); - } - - return Result; + animation_handle_array Result = {}; + + gs_thread_context Ctx = Context.ThreadContext; + gs_file_info_array FilesInDir = EnumerateDirectory(Ctx.FileHandler, State->Transient, Path, 0); + + Result.Count = FilesInDir.Count; + Result.Handles = PushArray(&State->Permanent, animation_handle, Result.Count); + + for (u32 i = 0; i < FilesInDir.Count; i++) + { + gs_file_info File = FilesInDir.Values[i]; + Result.Handles[i] = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + File.Path); + } + + return Result; } internal s32 GetCCIndex (assembly Assembly, blumen_lumen_state* BLState) { - s32 Result = 0; - - u64 AssemblyNameHash = HashDJB2ToU32(StringExpand(Assembly.Name)); - for (u32 i = 0; i < BLState->AssemblyNameToClearCoreMapCount; i++) - { - if (AssemblyNameHash == BLState->AssemblyNameToClearCore_Names[i]) + s32 Result = 0; + + u64 AssemblyNameHash = HashDJB2ToU32(StringExpand(Assembly.Name)); + for (u32 i = 0; i < BLState->AssemblyNameToClearCoreMapCount; i++) { - Result = (s32)i; - break; + if (AssemblyNameHash == BLState->AssemblyNameToClearCore_Names[i]) + { + Result = (s32)i; + break; + } } - } - - return Result; + + return Result; } internal void DEBUG_AppendText(gs_string Str, gs_thread_context Ctx) { - gs_const_string DebugPath = ConstString("data/debug_motor_changes.txt"); - gs_file DebugFile = ReadEntireFile(Ctx.FileHandler, - DebugPath); - gs_string NewString = PushString(Ctx.Transient, DebugFile.Size + Str.Size + 16); - if (DebugFile.Size > 0) - { - PrintF(&NewString, "%.*s\nENTRY:\n", DebugFile.Size, (char*)DebugFile.Memory); - } - AppendPrintF(&NewString, "%S\n", Str.ConstString); - NullTerminate(&NewString); - - if (!WriteEntireFile(Ctx.FileHandler, DebugPath, StringToData(NewString))) - { - InvalidCodePath; - } + gs_const_string DebugPath = ConstString("data/debug_motor_changes.txt"); + gs_file DebugFile = ReadEntireFile(Ctx.FileHandler, + DebugPath); + gs_string NewString = PushString(Ctx.Transient, DebugFile.Size + Str.Size + 16); + if (DebugFile.Size > 0) + { + PrintF(&NewString, "%.*s\nENTRY:\n", DebugFile.Size, (char*)DebugFile.Memory); + } + AppendPrintF(&NewString, "%S\n", Str.ConstString); + NullTerminate(&NewString); + + if (!WriteEntireFile(Ctx.FileHandler, DebugPath, StringToData(NewString))) + { + InvalidCodePath; + } } internal void DEBUG_SentMotorCommand(motor_packet Packet, gs_thread_context Ctx) { - Log_Message(GlobalLogBuffer, - "Motor Command Sent\nRequested Positions: %d %d %d\n", - Packet.FlowerPositions[0], - Packet.FlowerPositions[1], - Packet.FlowerPositions[2]); + Log_Message(GlobalLogBuffer, + "Motor Command Sent\nRequested Positions: %d %d %d\n", + Packet.FlowerPositions[0], + Packet.FlowerPositions[1], + Packet.FlowerPositions[2]); } internal void @@ -81,1021 +81,1022 @@ DEBUG_ReceivedMotorPositions(blumen_lumen_state* BLState, motor_status_packet NewPos, gs_thread_context Ctx) { - motor_packet LastPos = BLState->LastKnownMotorState; - bool PosChanged = (LastPos.FlowerPositions[0] != NewPos.Pos.FlowerPositions[0] || - LastPos.FlowerPositions[1] != NewPos.Pos.FlowerPositions[1] || - LastPos.FlowerPositions[2] != NewPos.Pos.FlowerPositions[2]); - - if (PosChanged) - { - Log_Message(GlobalLogBuffer, - "Motor Status Received\nCurrent Positions: %d %d %d\n", - NewPos.Pos.FlowerPositions[0], - NewPos.Pos.FlowerPositions[1], - NewPos.Pos.FlowerPositions[2]); - } + motor_packet LastPos = BLState->LastKnownMotorState; + bool PosChanged = (LastPos.FlowerPositions[0] != NewPos.Pos.FlowerPositions[0] || + LastPos.FlowerPositions[1] != NewPos.Pos.FlowerPositions[1] || + LastPos.FlowerPositions[2] != NewPos.Pos.FlowerPositions[2]); + + if (PosChanged) + { + Log_Message(GlobalLogBuffer, + "Motor Status Received\nCurrent Positions: %d %d %d\n", + NewPos.Pos.FlowerPositions[0], + NewPos.Pos.FlowerPositions[1], + NewPos.Pos.FlowerPositions[2]); + } } internal void DEBUG_ReceivedTemperature(temp_packet Temp, gs_thread_context Ctx) { - Log_Message(GlobalLogBuffer, - "\nTemperature: %d\n", - Temp.Temperature); + Log_Message(GlobalLogBuffer, + "\nTemperature: %d\n", + Temp.Temperature); } internal void BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) { - mic_listen_job_data* Data = (mic_listen_job_data*)UserData; - - gs_data Msg = {}; - - u8 WeathermanIPAddr[4] = {}; - WeathermanIPAddr[0] = 127; - WeathermanIPAddr[1] = 0; - WeathermanIPAddr[2] = 0; - WeathermanIPAddr[3] = 1; - - u32 WeathermanIPV4 = (u32)UpackB4(WeathermanIPAddr); - u32 WeathermanPort = 20185; - - platform_socket_handle_ ListenSocket = {0}; - - while (*Data->Running) - { - if (!SocketQueryStatus(Data->SocketManager, ListenSocket)) + mic_listen_job_data* Data = (mic_listen_job_data*)UserData; + + gs_data Msg = {}; + + u8 WeathermanIPAddr[4] = {}; + WeathermanIPAddr[0] = 127; + WeathermanIPAddr[1] = 0; + WeathermanIPAddr[2] = 0; + WeathermanIPAddr[3] = 1; + + u32 WeathermanIPV4 = (u32)UpackB4(WeathermanIPAddr); + u32 WeathermanPort = 20185; + + platform_socket_handle_ ListenSocket = {0}; + + while (*Data->Running) { - Data->IsConnected = false; - if (SocketHandleIsValid(ListenSocket)) - { - Log_Message(GlobalLogBuffer, "Disconnected from Python Server\n"); - CloseSocket(Data->SocketManager, ListenSocket); - } - ListenSocket = CreateSocket(Data->SocketManager, "127.0.0.1", "20185"); - if (ListenSocket.Index != 0) - { - Log_Message(GlobalLogBuffer, "Connected to Python Server\n"); - Data->IsConnected = true; - } + if (!SocketQueryStatus(Data->SocketManager, ListenSocket)) + { + Data->IsConnected = false; + if (SocketHandleIsValid(ListenSocket)) + { + Log_Message(GlobalLogBuffer, "Disconnected from Python Server\n"); + CloseSocket(Data->SocketManager, ListenSocket); + } + ListenSocket = CreateSocket(Data->SocketManager, "127.0.0.1", "20185"); + if (ListenSocket.Index != 0) + { + Log_Message(GlobalLogBuffer, "Connected to Python Server\n"); + Data->IsConnected = true; + } + } + + if (SocketQueryStatus(Data->SocketManager, ListenSocket)) + { + if (SocketPeek(Data->SocketManager, ListenSocket)) + { + Msg = SocketRecieve(Data->SocketManager, ListenSocket, Ctx->Transient); + if (Msg.Size > 0) + { + MessageQueue_Write(Data->IncomingMsgQueue, Msg); + } + } + + while (MessageQueue_CanRead(*Data->OutgoingMsgQueue)) + { + Msg = MessageQueue_Peek(Data->OutgoingMsgQueue); + + u32 Address = WeathermanIPV4; + u32 Port = WeathermanPort; + s32 Flags = 0; + s32 LengthSent = SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags); + if (LengthSent != 0) + { + // if we actually sent the message, THEN we pull it off the + // message queue + MessageQueue_Read(Data->OutgoingMsgQueue); + } else { + break; + } + } + } } - if (SocketQueryStatus(Data->SocketManager, ListenSocket)) - { - if (SocketPeek(Data->SocketManager, ListenSocket)) - { - Msg = SocketRecieve(Data->SocketManager, ListenSocket, Ctx->Transient); - if (Msg.Size > 0) - { - MessageQueue_Write(Data->IncomingMsgQueue, Msg); - } - } - - while (MessageQueue_CanRead(*Data->OutgoingMsgQueue)) - { - Msg = MessageQueue_Peek(Data->OutgoingMsgQueue); - - u32 Address = WeathermanIPV4; - u32 Port = WeathermanPort; - s32 Flags = 0; - s32 LengthSent = SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags); - if (LengthSent != 0) - { - // if we actually sent the message, THEN we pull it off the - // message queue - MessageQueue_Read(Data->OutgoingMsgQueue); - } else { - break; - } - } - } - } - - CloseSocket(Data->SocketManager, ListenSocket); + CloseSocket(Data->SocketManager, ListenSocket); } internal void BlumenLumen_SetPatternMode(bl_pattern_mode Mode, r32 FadeDuration, animation_system* System, blumen_lumen_state* BLState) { - BLState->PatternMode = Mode; - animation_handle_array Playlist = BLState->ModeAnimations[Mode]; - System->RepeatMode = AnimationRepeat_Loop; - System->PlaylistFadeTime = FadeDuration; - AnimationSystem_FadeToPlaylist(System, Playlist); + BLState->PatternMode = Mode; + animation_handle_array Playlist = BLState->ModeAnimations[Mode]; + System->RepeatMode = AnimationRepeat_Loop; + System->PlaylistFadeTime = FadeDuration; + AnimationSystem_FadeToPlaylist(System, Playlist); } internal void BlumenLumen_LoadPatterns(app_state* State) { - animation_pattern_array* Patterns = &State->Patterns; - if (Patterns->CountMax == 0) - { - *Patterns = Patterns_Create(&State->Permanent, 32); - } - - Patterns->Count = 0; - Patterns_PushPattern(Patterns, Pattern_None, PATTERN_SINGLETHREADED); - Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Rainbow, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED); - Patterns_PushPattern(Patterns, Pattern_VerticalLines, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Rotary, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_VoiceAddIns, PATTERN_MULTITHREADED); - - Patterns_PushPattern(Patterns, Pattern_StemSolid, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED); - - Patterns_PushPattern(Patterns, Pattern_GrowFadeMask, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_RainbowLoadingBar, PATTERN_MULTITHREADED); - - Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED); + animation_pattern_array* Patterns = &State->Patterns; + if (Patterns->CountMax == 0) + { + *Patterns = Patterns_Create(&State->Permanent, 32); + } + + Patterns->Count = 0; + Patterns_PushPattern(Patterns, Pattern_None, PATTERN_SINGLETHREADED); + Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Rainbow, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED); + Patterns_PushPattern(Patterns, Pattern_VerticalLines, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Rotary, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_VoiceAddIns, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_StemSolid, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_GrowFadeMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_RainbowLoadingBar, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED); } internal void AppendPrintDate(gs_string* WriteStr, system_time Time) { - AppendPrintF(WriteStr, "%d-%d-%d : %d:%d:%d\n\n", - Time.Year, Time.Month, Time.Day, - Time.Hour, Time.Minute, Time.Second); + AppendPrintF(WriteStr, "%d-%d-%d : %d:%d:%d\n\n", + Time.Year, Time.Month, Time.Day, + Time.Hour, Time.Minute, Time.Second); } internal void BlumenLumen_AppendBootupLog(app_state* State, blumen_lumen_state* BLState, context Context) { - gs_thread_context Ctx = Context.ThreadContext; - gs_const_string BootupLogPath = ConstString("lumenarium_boot_log.log"); - - gs_file BootLog = ReadEntireFile(Ctx.FileHandler, BootupLogPath); - gs_string WriteStr = {}; - - // we don't want the log file getting huge - // if it gets above some threshold, instead of appending, - // copy what there is to an _old file, and start this one over - // - // The thinking is that without the copy operation, when we reached - // our threshold, we'd overwrite the whole log. If something went - // wrong at that point, we'd have nothing to go on. This way, there is - // always some historical data present on the system - // - if (BootLog.Size < MB(4)) - { - WriteStr = PushString(State->Transient, BootLog.Size + 1024); - } - else - { - if (!WriteEntireFile(Ctx.FileHandler, ConstString("lumenarium_boot_log_old.log"), - BootLog.Data)) + gs_thread_context Ctx = Context.ThreadContext; + gs_const_string BootupLogPath = ConstString("lumenarium_boot_log.log"); + + gs_file BootLog = ReadEntireFile(Ctx.FileHandler, BootupLogPath); + gs_string WriteStr = {}; + + // we don't want the log file getting huge + // if it gets above some threshold, instead of appending, + // copy what there is to an _old file, and start this one over + // + // The thinking is that without the copy operation, when we reached + // our threshold, we'd overwrite the whole log. If something went + // wrong at that point, we'd have nothing to go on. This way, there is + // always some historical data present on the system + // + if (BootLog.Size < MB(4)) { - InvalidCodePath; + WriteStr = PushString(State->Transient, BootLog.Size + 1024); } - WriteStr = PushString(State->Transient, 1024); - } - - - // Copy old entries in - if (BootLog.Size > 0) - { - PrintF(&WriteStr, "%.*s", BootLog.Size, BootLog.Memory); - } - - // New Entry - AppendPrintF(&WriteStr, "Lumenarium Restarted\n"); - AppendPrintF(&WriteStr, "* Time: "); - AppendPrintDate(&WriteStr, Context.SystemTime_Current); - - gs_data Data = StringToData(WriteStr); - WriteEntireFile(Ctx.FileHandler, BootupLogPath, Data); + else + { + if (!WriteEntireFile(Ctx.FileHandler, ConstString("lumenarium_boot_log_old.log"), + BootLog.Data)) + { + InvalidCodePath; + } + WriteStr = PushString(State->Transient, 1024); + } + + + // Copy old entries in + if (BootLog.Size > 0) + { + PrintF(&WriteStr, "%.*s", BootLog.Size, BootLog.Memory); + } + + // New Entry + AppendPrintF(&WriteStr, "Lumenarium Restarted\n"); + AppendPrintF(&WriteStr, "* Time: "); + AppendPrintDate(&WriteStr, Context.SystemTime_Current); + + gs_data Data = StringToData(WriteStr); + WriteEntireFile(Ctx.FileHandler, BootupLogPath, Data); } internal void BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Context) { - if (!BLState->ShouldUpdateLog) return; - - gs_string FileStr = PushString(State->Transient, 1024); - AppendPrintF(&FileStr, "Lumenarium Status\n"); - - AppendPrintF(&FileStr, "Last Updated At:"); - AppendPrintDate(&FileStr, Context.SystemTime_Current); - AppendPrintF(&FileStr, "\n\n"); - - animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); - - bool IsPlaying = State->AnimationSystem.TimelineShouldAdvance; - AppendPrintF(&FileStr, "\tIs Playing: %s\n", - IsPlaying ? "True" : "False"); - - char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected"; - AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected); - - u8 MP0 = BLState->LastKnownMotorState.FlowerPositions[0]; - u8 MP1 = BLState->LastKnownMotorState.FlowerPositions[1]; - u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2]; - AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2); - - time_range MotorRange = {}; - if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, - MotorOpenTimes, - MotorOpenTimesCount, - &MotorRange)) - { - AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: ( %d:%d - %d:%d)\n", - MotorRange.StartHour, MotorRange.StartMinute, - MotorRange.EndHour, MotorRange.EndMinute); - } - else - { - AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: None\n"); - } - - char* PatternMode = 0; - switch (BLState->PatternMode) - { - case BlumenPattern_Standard: { PatternMode = "Standard"; } break; - case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break; - case BlumenPattern_NoControl: { PatternMode = "No Control: Someone's doing the Awaken sequence!"; } break; - } - AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode); - - phrase_hue LastHuePhrase = BLState->LastHuePhrase; - AppendPrintF(&FileStr, "Last Mic Phrase: %S\n", LastHuePhrase.Phrase); - - AppendPrintF(&FileStr, "Pattern Speed: %f\n", BLState->PatternSpeed); - - AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent); - - time_range RangeIn = {}; - if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, - LedOnTimes, - LedOnTimesCount, - &RangeIn)) - { - AppendPrintF(&FileStr, "\tIn Leds-On Time Range: ( %d:%d - %d:%d)\n", - RangeIn.StartHour, RangeIn.StartMinute, - RangeIn.EndHour, RangeIn.EndMinute); - } - else - { - AppendPrintF(&FileStr, "\tIn Leds-On Time Range: None\n"); - } - - AppendPrintF(&FileStr, "\tTemp Dimming: %s\n", - Blumen_TempShouldDimLeds(BLState) ? "On" : "Off"); - - AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived); - - gs_data LogMem = StringToData(FileStr); - if (!WriteEntireFile(Context.ThreadContext.FileHandler, ConstString("lumenarium_status.log"), LogMem)) - { - InvalidCodePath; - } + if (!BLState->ShouldUpdateLog) return; + + gs_string FileStr = PushString(State->Transient, 1024); + AppendPrintF(&FileStr, "Lumenarium Status\n"); + + AppendPrintF(&FileStr, "Last Updated At:"); + AppendPrintDate(&FileStr, Context.SystemTime_Current); + AppendPrintF(&FileStr, "\n\n"); + + animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); + + bool IsPlaying = State->AnimationSystem.TimelineShouldAdvance; + AppendPrintF(&FileStr, "\tIs Playing: %s\n", + IsPlaying ? "True" : "False"); + + char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected"; + AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected); + + u8 MP0 = BLState->LastKnownMotorState.FlowerPositions[0]; + u8 MP1 = BLState->LastKnownMotorState.FlowerPositions[1]; + u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2]; + AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2); + + time_range MotorRange = {}; + if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, + MotorOpenTimes, + MotorOpenTimesCount, + &MotorRange)) + { + AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: ( %d:%d - %d:%d)\n", + MotorRange.StartHour, MotorRange.StartMinute, + MotorRange.EndHour, MotorRange.EndMinute); + } + else + { + AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: None\n"); + } + + char* PatternMode = 0; + switch (BLState->PatternMode) + { + case BlumenPattern_Standard: { PatternMode = "Standard"; } break; + case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break; + case BlumenPattern_NoControl: { PatternMode = "No Control: Someone's doing the Awaken sequence!"; } break; + } + AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode); + + phrase_hue LastHuePhrase = BLState->LastHuePhrase; + AppendPrintF(&FileStr, "Last Mic Phrase: %S\n", LastHuePhrase.Phrase); + + AppendPrintF(&FileStr, "Pattern Speed: %f\n", BLState->PatternSpeed); + + AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent); + + time_range RangeIn = {}; + if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, + LedOnTimes, + LedOnTimesCount, + &RangeIn)) + { + AppendPrintF(&FileStr, "\tIn Leds-On Time Range: ( %d:%d - %d:%d)\n", + RangeIn.StartHour, RangeIn.StartMinute, + RangeIn.EndHour, RangeIn.EndMinute); + } + else + { + AppendPrintF(&FileStr, "\tIn Leds-On Time Range: None\n"); + } + + AppendPrintF(&FileStr, "\tTemp Dimming: %s\n", + Blumen_TempShouldDimLeds(BLState) ? "On" : "Off"); + + AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived); + + gs_data LogMem = StringToData(FileStr); + if (!WriteEntireFile(Context.ThreadContext.FileHandler, ConstString("lumenarium_status.log"), LogMem)) + { + InvalidCodePath; + } } internal gs_data BlumenLumen_CustomInit(app_state* State, context Context) { - // This is memory for any custom data that we want to use - // as a part of a particular sculpture. - // By returning it from here, it will be sent as an argument to - // the sculpture's CustomUpdate function; - gs_data Result = {}; - - Result = PushSize(&State->Permanent, sizeof(blumen_lumen_state)); - - blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory; - BLState->Running = true; - BLState->BrightnessPercent = 1; - MessageQueue_Init(&BLState->IncomingMsgQueue, &State->Permanent); - MessageQueue_Init(&BLState->OutgoingMsgQueue, &State->Permanent); - - BLState->MicListenJobData.Running = &BLState->Running; - BLState->MicListenJobData.SocketManager = Context.SocketManager; - BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue; - BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; - - BLState->PatternSpeed = GlobalAnimSpeed; - -#if 1 - BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData, Context.ThreadContext); -#endif - - assembly* Flower0 = LoadAssembly(Flower0AssemblyPath, State, Context); - assembly* Flower1 = LoadAssembly(Flower1AssemblyPath, State, Context); - assembly* Flower2 = LoadAssembly(Flower2AssemblyPath, State, Context); - - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) - { - assembly Assembly = State->Assemblies.Values[i]; - BLState->StemStrips[Assembly.AssemblyIndex] = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("stem"), &State->Permanent); - } - - BLState->AssemblyNameToClearCoreMapCount = 3; - BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent, - u64, - BLState->AssemblyNameToClearCoreMapCount); - BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU32(StringExpand(Flower2->Name)); - BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU32(StringExpand(Flower1->Name)); - BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower0->Name)); - - gs_file_handler FileHandler = Context.ThreadContext.FileHandler; - gs_file ColorPhraseCSVFile = ReadEntireFile(FileHandler, PhraseMapCSVPath); - if (ColorPhraseCSVFile.Memory != 0) - { - gs_const_string ColorPhraseMapStr = DataToString(ColorPhraseCSVFile.Data); - gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, - { PhraseMapCSVSeparator }, - State->Transient); + // This is memory for any custom data that we want to use + // as a part of a particular sculpture. + // By returning it from here, it will be sent as an argument to + // the sculpture's CustomUpdate function; + gs_data Result = {}; - BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, - &State->Permanent); - } - -#if 0 - animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - ConstString("data/demo_patterns.foldanim")); - State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; -#else - - BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context); - BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); - AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); - - BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); - - BLState->AwakenHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - ConstString("data/blumen_animations/awaken.foldanim")); - BLState->OffAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - ConstString("data/blumen_animations/off_anim.foldanim")); - + Result = PushSize(&State->Permanent, sizeof(blumen_lumen_state)); + + blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory; + BLState->Running = true; + BLState->BrightnessPercent = 1; + MessageQueue_Init(&BLState->IncomingMsgQueue, &State->Permanent); + MessageQueue_Init(&BLState->OutgoingMsgQueue, &State->Permanent); + + BLState->MicListenJobData.Running = &BLState->Running; + BLState->MicListenJobData.SocketManager = Context.SocketManager; + BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue; + BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; + + BLState->PatternSpeed = GlobalAnimSpeed; + +#if 1 + BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData, Context.ThreadContext); #endif - State->AnimationSystem.TimelineShouldAdvance = true; - - BLState->StandardPatternHues.Granularity = 1; - BLState->StandardPatternHues.Speed = 1; - BLState->StandardPatternHues.AddIn = AddIn_Rotary; - BLState->StandardPatternHues.Pattern = HuePattern_Wavy; - - BLState->DebugHue.Hue0.HSV = v4{0, 1, 1, 1}; - BLState->DebugHue.Hue1.HSV = v4{0, 1, 1, 1}; - BLState->DebugHue.Hue2.HSV = v4{0, 1, 1, 1}; - - BlumenLumen_AppendBootupLog(State, BLState, Context); - return Result; + + assembly* Flower0 = LoadAssembly(Flower0AssemblyPath, State, Context); + assembly* Flower1 = LoadAssembly(Flower1AssemblyPath, State, Context); + assembly* Flower2 = LoadAssembly(Flower2AssemblyPath, State, Context); + + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + assembly Assembly = State->Assemblies.Values[i]; + BLState->StemStrips[Assembly.AssemblyIndex] = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("stem"), &State->Permanent); + } + + BLState->AssemblyNameToClearCoreMapCount = 3; + BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent, + u64, + BLState->AssemblyNameToClearCoreMapCount); + BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU32(StringExpand(Flower2->Name)); + BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU32(StringExpand(Flower1->Name)); + BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower0->Name)); + + gs_file_handler FileHandler = Context.ThreadContext.FileHandler; + gs_file ColorPhraseCSVFile = ReadEntireFile(FileHandler, PhraseMapCSVPath); + if (ColorPhraseCSVFile.Memory != 0) + { + gs_const_string ColorPhraseMapStr = DataToString(ColorPhraseCSVFile.Data); + gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, + { PhraseMapCSVSeparator }, + State->Transient); + + BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, + &State->Permanent); + } + +#if 0 + animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/demo_patterns.foldanim")); + State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; +#else + + BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context); + BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); + AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); + + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + + BLState->AwakenHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/blumen_animations/awaken.foldanim")); + BLState->OffAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/blumen_animations/off_anim.foldanim")); + +#endif + State->AnimationSystem.TimelineShouldAdvance = true; + + BLState->StandardPatternHues.Granularity = 1; + BLState->StandardPatternHues.Speed = 1; + BLState->StandardPatternHues.AddIn = AddIn_Rotary; + BLState->StandardPatternHues.Pattern = HuePattern_Wavy; + + BLState->DebugHue.Hue0.HSV = v4{0, 1, 1, 1}; + BLState->DebugHue.Hue1.HSV = v4{0, 1, 1, 1}; + BLState->DebugHue.Hue2.HSV = v4{0, 1, 1, 1}; + + BlumenLumen_AppendBootupLog(State, BLState, Context); + return Result; } internal void BlumenLumen_UpdateMotorState(blumen_lumen_state* BLState, motor_status_packet Motor, context Context) { - DEBUG_ReceivedMotorPositions(BLState, Motor, Context.ThreadContext); - - motor_packet LastPos = BLState->LastKnownMotorState; - motor_packet CurrPos = Motor.Pos; - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) - { - if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i]) + DEBUG_ReceivedMotorPositions(BLState, Motor, Context.ThreadContext); + + motor_packet LastPos = BLState->LastKnownMotorState; + motor_packet CurrPos = Motor.Pos; + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) { - BLState->LastTimeMotorStateChanged[i] = Context.SystemTime_Current.NanosSinceEpoch; + if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i]) + { + BLState->LastTimeMotorStateChanged[i] = Context.SystemTime_Current.NanosSinceEpoch; + } } - } - - BLState->LastKnownMotorState = Motor.Pos; - BLState->ShouldUpdateLog = true; + + BLState->LastKnownMotorState = Motor.Pos; + BLState->ShouldUpdateLog = true; } internal void BlumenLumen_ApplyNextHotHue(blumen_lumen_state* BLState, context Context, gs_string* DebugStr, app_state* State) { - // if we are in standard color mode, shift all flowers to the new color - // otherwise, only shift the next flower in the sequence to the new color - phrase_hue NewHue = BLState->NextHotHue; - Log_Message(GlobalLogBuffer, "Switching To: %S\n", NewHue.Phrase); - - - if (BLState->PatternMode == BlumenPattern_Standard || - NewHue.OverrideAll) - { - BlumenLumen_SetNextHue(BLState, 0, NewHue); - BlumenLumen_SetNextHue(BLState, 1, NewHue); - BlumenLumen_SetNextHue(BLState, 2, NewHue); - } - else - { - u32 AssemblyIdx = BLState->LastAssemblyColorSet; - BlumenLumen_SetNextHue(BLState, AssemblyIdx, NewHue); - BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; - } - - BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); - BLState->TimeLastSetToVoiceMode = Context.SystemTime_Current; - BLState->LastHuePhrase = NewHue; - BLState->ShouldUpdateLog = true; - BLState->InPhraseReceptionMode = false; + // if we are in standard color mode, shift all flowers to the new color + // otherwise, only shift the next flower in the sequence to the new color + phrase_hue NewHue = BLState->NextHotHue; + Log_Message(GlobalLogBuffer, "Switching To: %S\n", NewHue.Phrase); + + + if (BLState->PatternMode == BlumenPattern_Standard || + NewHue.OverrideAll) + { + BlumenLumen_SetNextHue(BLState, 0, NewHue); + BlumenLumen_SetNextHue(BLState, 1, NewHue); + BlumenLumen_SetNextHue(BLState, 2, NewHue); + } + else + { + u32 AssemblyIdx = BLState->LastAssemblyColorSet; + BlumenLumen_SetNextHue(BLState, AssemblyIdx, NewHue); + BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; + } + + BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + BLState->TimeLastSetToVoiceMode = Context.SystemTime_Current; + BLState->LastHuePhrase = NewHue; + BLState->ShouldUpdateLog = true; + BLState->InPhraseReceptionMode = false; } internal void BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; - BLState->ShouldUpdateLog = false; - - gs_string DebugStr = PushString(State->Transient, 256); - - while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) - { - gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); - if (PacketData.Memory == 0) continue; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + BLState->ShouldUpdateLog = false; - blumen_packet Packet = *(blumen_packet*)PacketData.Memory; - switch (Packet.Type) { - case PacketType_PatternCommand: - { - microphone_packet Mic = Packet.MicPacket; - u64 NameHash = HashDJB2ToU32(Mic.AnimationFileName); - u32 NameLen = CStringLength(Mic.AnimationFileName); + gs_string DebugStr = PushString(State->Transient, 256); + + while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) + { + gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); + if (PacketData.Memory == 0) continue; - phrase_hue NewHue = PhraseHueMap_Find(BLState->PhraseHueMap, NameHash); - if (NewHue.Phrase.Length > 0) - { - bool IsLonger = (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length); - bool IsntInPhraseReceptionMode = !BLState->InPhraseReceptionMode; - if (IsLonger || IsntInPhraseReceptionMode) - { - Log_Message(GlobalLogBuffer, "Queueing: %S\n", NewHue.Phrase); - - BLState->NextHotHue = NewHue; - if (SecondsElapsed(BLState->TimePhraseReceptionBegan, - Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime) + blumen_packet Packet = *(blumen_packet*)PacketData.Memory; + switch (Packet.Type) { + case PacketType_PatternCommand: { - BLState->TimePhraseReceptionBegan = Context->SystemTime_Current; - BLState->InPhraseReceptionMode = true; - } - BLState->TimeLastPhraseReceived = Context->SystemTime_Current; - } + microphone_packet Mic = Packet.MicPacket; + u64 NameHash = HashDJB2ToU32(Mic.AnimationFileName); + u32 NameLen = CStringLength(Mic.AnimationFileName); + + phrase_hue NewHue = PhraseHueMap_Find(BLState->PhraseHueMap, NameHash); + if (NewHue.Phrase.Length > 0) + { + bool IsLonger = (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length); + bool IsntInPhraseReceptionMode = !BLState->InPhraseReceptionMode; + if (IsLonger || IsntInPhraseReceptionMode) + { + Log_Message(GlobalLogBuffer, "Queueing: %S\n", NewHue.Phrase); + + BLState->NextHotHue = NewHue; + if (SecondsElapsed(BLState->TimePhraseReceptionBegan, + Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime) + { + BLState->TimePhraseReceptionBegan = Context->SystemTime_Current; + BLState->InPhraseReceptionMode = true; + } + BLState->TimeLastPhraseReceived = Context->SystemTime_Current; + } + } + }break; + + case PacketType_MotorState: + { + motor_status_packet Motor = Packet.MotorStatusPacket; + + // NOTE(pjs): Python sends multi-byte integers in little endian + // order. Have to unpack + u8* T = (u8*)&Motor.Temperature; + Motor.Temperature = (T[0] << 8 | + T[1] << 0); + + BlumenLumen_UpdateMotorState(BLState, Motor, *Context); + }break; + + case PacketType_Temperature: + { + temp_packet Temp = Packet.TempPacket; + BLState->LastTemperatureReceived = Temp.Temperature; + DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); + BLState->ShouldUpdateLog = true; + }break; + + InvalidDefaultCase; } - }break; - - case PacketType_MotorState: - { - motor_status_packet Motor = Packet.MotorStatusPacket; + } + + if (BLState->InPhraseReceptionMode) + { + r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current); + if (SecondsSincePhraseBegan > PhrasePriorityMessageGroupingTime) + { + BlumenLumen_ApplyNextHotHue(BLState, *Context, &DebugStr, State); + } + } + + BlumenLumen_AdvanceHueFade(BLState, *Context); + + // Update next frames Hues + if (!BLState->DebugOverrideHue) + { + r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); + AnimTime = (r32)Context->TotalTime; + r32 BaseTime = AnimTime * BLState->PatternSpeed; - // NOTE(pjs): Python sends multi-byte integers in little endian - // order. Have to unpack - u8* T = (u8*)&Motor.Temperature; - Motor.Temperature = (T[0] << 8 | - T[1] << 0); + r32 ColorSpeed = 1; //.001; + r32 ColorOscSpeed = .05 * ColorSpeed; + r32 ColorRelOscSpeed = 1 * ColorSpeed;; + r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; + r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); + + r32 H0 = ModR32(ColorOscillation * 360, 360); + r32 H1 = ModR32(BaseTime + ColorRelationship, 360); + // TODO(PS): use our new HSV lerp + r32 H2 = LerpR32(.3f, H0, H1); + + BLState->StandardPatternHues.Hue0.HSV = v4{ H0, 1, 1, 1 }; + BLState->StandardPatternHues.Hue1.HSV = v4{ H1, 1, 1, 1 }; + BLState->StandardPatternHues.Hue2.HSV = v4{ H2, 1, 1, 1 }; + + // Transition back to standard mode after some time + if (BLState->PatternMode == BlumenPattern_VoiceCommand) + { + u64 LastChangeClock = BLState->TimeLastSetToVoiceMode.NanosSinceEpoch; + u64 NowClocks = Context->SystemTime_Current.NanosSinceEpoch; + s64 NanosSinceChange = NowClocks - LastChangeClock; + r64 SecondsSinceChange = (r64)NanosSinceChange * NanosToSeconds; + + if (SecondsSinceChange > VoiceCommandSustainDuration) + { + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + BLState->ShouldUpdateLog = true; + } + } - BlumenLumen_UpdateMotorState(BLState, Motor, *Context); - }break; - - case PacketType_Temperature: - { - temp_packet Temp = Packet.TempPacket; - BLState->LastTemperatureReceived = Temp.Temperature; - DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); - BLState->ShouldUpdateLog = true; - }break; - - InvalidDefaultCase; } - } - - if (BLState->InPhraseReceptionMode) - { - r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current); - if (SecondsSincePhraseBegan > PhrasePriorityMessageGroupingTime) + else { - BlumenLumen_ApplyNextHotHue(BLState, *Context, &DebugStr, State); + BLState->StandardPatternHues = BLState->DebugHue; + AnimationSystem_FadeToPlaylist(&State->AnimationSystem, BLState->ModeAnimations[BlumenPattern_VoiceCommand]); + } - } - - BlumenLumen_AdvanceHueFade(BLState, *Context); - - // Update next frames Hues - if (!BLState->DebugOverrideHue) - { - r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); - AnimTime = (r32)Context->TotalTime; - r32 BaseTime = AnimTime * BLState->PatternSpeed; - r32 ColorSpeed = 1; //.001; - r32 ColorOscSpeed = .05 * ColorSpeed; - r32 ColorRelOscSpeed = 1 * ColorSpeed;; - r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; - r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); - - r32 H0 = ModR32(ColorOscillation * 360, 360); - r32 H1 = ModR32(BaseTime + ColorRelationship, 360); - // TODO(PS): use our new HSV lerp - r32 H2 = LerpR32(.3f, H0, H1); - - BLState->StandardPatternHues.Hue0.HSV = v4{ H0, 1, 1, 1 }; - BLState->StandardPatternHues.Hue1.HSV = v4{ H1, 1, 1, 1 }; - BLState->StandardPatternHues.Hue2.HSV = v4{ H2, 1, 1, 1 }; - - // Transition back to standard mode after some time - if (BLState->PatternMode == BlumenPattern_VoiceCommand) + // Open / Close the Motor + if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue) && + !BLState->IgnoreTimeOfDay_MotorState) { - u64 LastChangeClock = BLState->TimeLastSetToVoiceMode.NanosSinceEpoch; - u64 NowClocks = Context->SystemTime_Current.NanosSinceEpoch; - s64 NanosSinceChange = NowClocks - LastChangeClock; - r64 SecondsSinceChange = (r64)NanosSinceChange * NanosToSeconds; - - if (SecondsSinceChange > VoiceCommandSustainDuration) - { - BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); - BLState->ShouldUpdateLog = true; - } + bool SendMotorCommand = false; + + u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; + r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; + bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; + + bl_motor_state_value NewMotorState = MotorState_Closed; + bool SendOpen = false; + for (u32 i = 0; i < MotorOpenTimesCount; i++) + { + time_range Range = MotorOpenTimes[i]; + bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); + if (CurrTimeInRange) { + NewMotorState = MotorState_Open; + } + } + + if (NewMotorState != BLState->LastSendState) + { + ShouldSendCurrentState = true; + } + + if (ShouldSendCurrentState) + { + BLState->LastSendTime = Context->SystemTime_Current; + BLState->LastSendState = NewMotorState; + + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket.FlowerPositions[0] = NewMotorState; + Packet.MotorPacket.FlowerPositions[1] = NewMotorState; + Packet.MotorPacket.FlowerPositions[2] = NewMotorState; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + + DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); + } } - } - else - { - BLState->StandardPatternHues = BLState->DebugHue; - AnimationSystem_FadeToPlaylist(&State->AnimationSystem, BLState->ModeAnimations[BlumenPattern_VoiceCommand]); - - } - - // Open / Close the Motor - if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue) && - !BLState->IgnoreTimeOfDay_MotorState) - { - bool SendMotorCommand = false; - - u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; - r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; - bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; - - bl_motor_state_value NewMotorState = MotorState_Closed; - bool SendOpen = false; - for (u32 i = 0; i < MotorOpenTimesCount; i++) + // When a motor state changes to being open, wait to turn Upper Leds on + // in order to hide the fact that they are turning off + motor_packet CurrMotorPos = BLState->LastKnownMotorState; + u64 NowNanos = Context->SystemTime_Current.NanosSinceEpoch; + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) { - time_range Range = MotorOpenTimes[i]; - bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); - if (CurrTimeInRange) { - NewMotorState = MotorState_Open; - } + // have to map from "assembly load order" to + // the order that the clear core is referencing the + // motors by + assembly Assembly = State->Assemblies.Values[i]; + u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState); + u8 MotorPos = CurrMotorPos.FlowerPositions[AssemblyCCIndex]; + + if ((MotorPos == MotorState_Open || MotorPos == MotorState_MostlyOpen) && + !BLState->ShouldDimUpperLeds[i]) + { + u64 ChangedNanos = BLState->LastTimeMotorStateChanged[i]; + u64 NanosSinceChanged = NowNanos - ChangedNanos; + r64 SecondsSinceChanged = (r64)NanosSinceChanged * NanosToSeconds; + if (SecondsSinceChanged > TurnUpperLedsOffAfterMotorCloseCommandDelay) + { + BLState->ShouldDimUpperLeds[i] = true; + } + else + { + BLState->ShouldDimUpperLeds[i] = false; + } + } + else if (MotorPos == MotorState_Closed || + MotorPos == MotorState_HalfOpen) + { + BLState->ShouldDimUpperLeds[i] = false; + } } - if (NewMotorState != BLState->LastSendState) + // NOTE(PS): If the flowers are mostly open or full open + // we mask off the top leds to prevent them from overheating + // while telescoped inside the flower + for (u32 a = 0; a < BL_FLOWER_COUNT; a++) { - ShouldSendCurrentState = true; + assembly Assembly = State->Assemblies.Values[a]; + if (!BLState->ShouldDimUpperLeds[a]) continue; + + led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; + led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient); + for (u32 s = 0; s < TopStrips.Count; s++) + { + u32 SIndex = TopStrips.StripIndices[s]; + v2_strip Strip = Assembly.Strips[SIndex]; + for (u32 l = 0; l < Strip.LedCount; l++) + { + u32 LIndex = Strip.LedLUT[l]; + Buffer.Colors[LIndex] = {0}; + } + } } - if (ShouldSendCurrentState) + if (!BLState->IgnoreTimeOfDay_LedDimming) { - BLState->LastSendTime = Context->SystemTime_Current; - BLState->LastSendState = NewMotorState; - - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket.FlowerPositions[0] = NewMotorState; - Packet.MotorPacket.FlowerPositions[1] = NewMotorState; - Packet.MotorPacket.FlowerPositions[2] = NewMotorState; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - - DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); - } - } - - // When a motor state changes to being open, wait to turn Upper Leds on - // in order to hide the fact that they are turning off - motor_packet CurrMotorPos = BLState->LastKnownMotorState; - u64 NowNanos = Context->SystemTime_Current.NanosSinceEpoch; - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) - { - // have to map from "assembly load order" to - // the order that the clear core is referencing the - // motors by - assembly Assembly = State->Assemblies.Values[i]; - u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState); - u8 MotorPos = CurrMotorPos.FlowerPositions[AssemblyCCIndex]; - - if ((MotorPos == MotorState_Open || MotorPos == MotorState_MostlyOpen) && - !BLState->ShouldDimUpperLeds[i]) - { - u64 ChangedNanos = BLState->LastTimeMotorStateChanged[i]; - u64 NanosSinceChanged = NowNanos - ChangedNanos; - r64 SecondsSinceChanged = (r64)NanosSinceChanged * NanosToSeconds; - if (SecondsSinceChanged > TurnUpperLedsOffAfterMotorCloseCommandDelay) - { - BLState->ShouldDimUpperLeds[i] = true; - } - else - { - BLState->ShouldDimUpperLeds[i] = false; - } - } - else if (MotorPos == MotorState_Closed || - MotorPos == MotorState_HalfOpen) - { - BLState->ShouldDimUpperLeds[i] = false; - } - } - - // NOTE(PS): If the flowers are mostly open or full open - // we mask off the top leds to prevent them from overheating - // while telescoped inside the flower - for (u32 a = 0; a < BL_FLOWER_COUNT; a++) - { - assembly Assembly = State->Assemblies.Values[a]; - if (!BLState->ShouldDimUpperLeds[a]) continue; - - led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; - led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient); - for (u32 s = 0; s < TopStrips.Count; s++) - { - u32 SIndex = TopStrips.StripIndices[s]; - v2_strip Strip = Assembly.Strips[SIndex]; - for (u32 l = 0; l < Strip.LedCount; l++) - { - u32 LIndex = Strip.LedLUT[l]; - Buffer.Colors[LIndex] = {0}; - } - } - } - - if (!BLState->IgnoreTimeOfDay_LedDimming) - { - bool TimelineShouldAdvance = false; - r32 OverrideBrightness = 0.0f; - - time_range RangeIn = {}; - if (SystemTimeIsInTimeRangeList(Context->SystemTime_Current, - LedOnTimes, - LedOnTimesCount, - &RangeIn)) - { - - if (Blumen_TempShouldDimLeds(BLState)) - { - OverrideBrightness = HighTemperatureBrightnessPercent; - } - else - { - OverrideBrightness = FullBrightnessPercent; - } - TimelineShouldAdvance = true; + bool TimelineShouldAdvance = false; + r32 OverrideBrightness = 0.0f; + + time_range RangeIn = {}; + if (SystemTimeIsInTimeRangeList(Context->SystemTime_Current, + LedOnTimes, + LedOnTimesCount, + &RangeIn) || + DEBUGIgnoreLedOnTimeRange) + { + + if (Blumen_TempShouldDimLeds(BLState)) + { + OverrideBrightness = HighTemperatureBrightnessPercent; + } + else + { + OverrideBrightness = FullBrightnessPercent; + } + TimelineShouldAdvance = true; + } + + State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; + BLState->BrightnessPercent = OverrideBrightness; } - State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; - BLState->BrightnessPercent = OverrideBrightness; - } - - // Dim the leds based on temp data - if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) - { - for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) + // Dim the leds based on temp data + if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) { - led_buffer Buffer = State->LedSystem.Buffers[i]; - for (u32 j = 0; j < Buffer.LedCount; j++) - { - pixel* Color = Buffer.Colors + j; - Color->R = Color->R * BLState->BrightnessPercent; - Color->G = Color->G * BLState->BrightnessPercent; - Color->B = Color->B * BLState->BrightnessPercent; - } + for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) + { + led_buffer Buffer = State->LedSystem.Buffers[i]; + for (u32 j = 0; j < Buffer.LedCount; j++) + { + pixel* Color = Buffer.Colors + j; + Color->R = Color->R * BLState->BrightnessPercent; + Color->G = Color->G * BLState->BrightnessPercent; + Color->B = Color->B * BLState->BrightnessPercent; + } + } } - } - - // Send Status Packet - { - system_time LastSendTime = BLState->LastStatusUpdateTime; - r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)LastSendTime.NanosSinceEpoch); - r64 SecondsSinceLastSend = NanosSinceLastSend * NanosToSeconds; - if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) + + // Send Status Packet { - BLState->LastStatusUpdateTime = Context->SystemTime_Current; - Log_Message(GlobalLogBuffer, "Attempting to Send Lumenarium Status\n"); - - blumen_packet Packet = {}; - Packet.Type = PacketType_LumenariumStatus; - Packet.StatusPacket.NextMotorEventType = 0; - Packet.StatusPacket.NextEventTime = 0; - - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - if (ActiveAnim) - { - CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName, - Min(ActiveAnim->Name.Length, 32)); - Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0; - } - - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - - // there's no new information here, but by updating the log here, - // we're updating it at some infrequent but regular period that isnt - // every single frame - BLState->ShouldUpdateLog = true; + system_time LastSendTime = BLState->LastStatusUpdateTime; + r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)LastSendTime.NanosSinceEpoch); + r64 SecondsSinceLastSend = NanosSinceLastSend * NanosToSeconds; + if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) + { + BLState->LastStatusUpdateTime = Context->SystemTime_Current; + Log_Message(GlobalLogBuffer, "Attempting to Send Lumenarium Status\n"); + + blumen_packet Packet = {}; + Packet.Type = PacketType_LumenariumStatus; + Packet.StatusPacket.NextMotorEventType = 0; + Packet.StatusPacket.NextEventTime = 0; + + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + if (ActiveAnim) + { + CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName, + Min(ActiveAnim->Name.Length, 32)); + Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0; + } + + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + + // there's no new information here, but by updating the log here, + // we're updating it at some infrequent but regular period that isnt + // every single frame + BLState->ShouldUpdateLog = true; + } } - } - - BlumenLumen_UpdateLog(State, BLState, *Context); + + BlumenLumen_UpdateLog(State, BLState, *Context); } US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; - ui_interface* I = &State->Interface; - - ui_BeginRow(I, BlumenDebug_Count); - for (u32 i = 0; i < BlumenDebug_Count; i++) - { - if (ui_Button(I, MakeString(BlDebugUiModeStrings[i]))) + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + ui_interface* I = &State->Interface; + + ui_BeginRow(I, BlumenDebug_Count); + for (u32 i = 0; i < BlumenDebug_Count; i++) { - BLState->DebugMode = (bl_debug_ui_mode)i; + if (ui_Button(I, MakeString(BlDebugUiModeStrings[i]))) + { + BLState->DebugMode = (bl_debug_ui_mode)i; + } } - } - ui_EndRow(I); - - switch (BLState->DebugMode) - { - case BlumenDebug_Motors: + ui_EndRow(I); + + switch (BLState->DebugMode) { - motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; - - BLState->IgnoreTimeOfDay_MotorState = ui_ToggleText(I, MakeString("Motors Ignore Time Limit"), BLState->IgnoreTimeOfDay_MotorState); - - for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) - { - gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); - ui_BeginRow(I, 5); + case BlumenDebug_Motors: { - ui_Label(I, Label); - - bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; - if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; - } - bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; - if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; - } - bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; - if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; - } - bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; - if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; - } - } - ui_EndRow(I); - } - BLState->DEBUG_PendingMotorPacket = PendingPacket; - - if (ui_Button(I, MakeString("Send Motor Packet"))) - { - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); - - } - - motor_packet MotorPos = BLState->LastKnownMotorState; - ui_Label(I, MakeString("Current Motor Positions")); - { - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) - { - ui_BeginRow(I, 2); - gs_string MotorStr = PushStringF(State->Transient, 32, - "Motor %d", - i); - ui_Label(I, MotorStr); - - gs_string StateStr = {}; - switch (MotorPos.FlowerPositions[i]) - { - case MotorState_Closed: { - StateStr = MakeString("Closed"); - } break; - case MotorState_HalfOpen: { - StateStr = MakeString("Half Open"); - } break; - case MotorState_MostlyOpen: { - StateStr = MakeString("Mostly Open"); - } break; - case MotorState_Open: { - StateStr = MakeString("Open"); - } break; + motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; - default: + BLState->IgnoreTimeOfDay_MotorState = ui_ToggleText(I, MakeString("Motors Ignore Time Limit"), BLState->IgnoreTimeOfDay_MotorState); + + for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) { - StateStr = MakeString("Invalid Value"); - } break; - } - - ui_Label(I, StateStr); - ui_EndRow(I); - } - } - - ui_Label(I, MakeString("Set Internal Motor State:")); - if (ui_Button(I, MakeString("Closed"))) - { - motor_status_packet Motor = {}; - Motor.Pos.FlowerPositions[0] = MotorState_Closed; - Motor.Pos.FlowerPositions[1] = MotorState_Closed; - Motor.Pos.FlowerPositions[2] = MotorState_Closed; - Motor.Temperature = 16; - - BlumenLumen_UpdateMotorState(BLState, Motor, Context); - } - if (ui_Button(I, MakeString("Open"))) - { - motor_status_packet Motor = {}; - Motor.Pos.FlowerPositions[0] = MotorState_Open; - Motor.Pos.FlowerPositions[1] = MotorState_Open; - Motor.Pos.FlowerPositions[2] = MotorState_Open; - Motor.Temperature = 16; - - BlumenLumen_UpdateMotorState(BLState, Motor, Context); - } - } break; - - case BlumenDebug_Leds: - { - BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); - - BLState->IgnoreTimeOfDay_LedDimming = ui_ToggleText(I, MakeString("Leds Ignore Time Limit"), BLState->IgnoreTimeOfDay_LedDimming); - - if (ui_BeginLabeledDropdown(I, MakeString("Phrase"), MakeString(BLState->PendingPhrase.Phrase))) - { - u32 ListCount = BLState->PhraseHueMap.Count; - ui_BeginList(I, MakeString("Phrase List"), 5, ListCount); - for (u32 i = 0; i < ListCount; i++) - { - gs_string Str = MakeString(BLState->PhraseHueMap.Phrases[i]); - if (ui_Button(I, Str)) - { - BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i); - BLState->DebugHue = BLState->PendingPhrase; - } - } - ui_EndList(I); - } - ui_EndLabeledDropdown(I); - if (ui_Button(I, MakeString("Say Phrase"))) - { - gs_string DebugStr = PushString(State->Transient, 256); - BLState->NextHotHue = BLState->PendingPhrase; - BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State); - } - - ui_Label(I, MakeString("Phrase Constructor")); - BLState->DebugOverrideHue = ui_ToggleText(I, MakeString("Override Hue"), BLState->DebugOverrideHue); - if (BLState->DebugOverrideHue) - { - phrase_hue PHue = BLState->DebugHue; - PHue.Hue0.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue0"), (r32)PHue.Hue0.HSV.x, 0, 360); - PHue.Hue1.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue1"), (r32)PHue.Hue1.HSV.x, 0, 360); - PHue.Hue2.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue2"), (r32)PHue.Hue2.HSV.x, 0, 360); - PHue.Granularity = (u32)ui_LabeledRangeSlider(I, MakeString("Granularity"), (r32)PHue.Granularity, 0, 5); - PHue.Speed = ui_LabeledRangeSlider(I, MakeString("Speed"), PHue.Speed, 0, 4); - - gs_string PatternOptions[HuePattern_Count] = {}; - PatternOptions[HuePattern_Patchy] = MakeString("patchy"); - PatternOptions[HuePattern_Wavy] = MakeString("wavy"); - - gs_string CPattern = PatternOptions[PHue.Pattern]; - if (ui_BeginLabeledDropdown(I, MakeString("Pattern"), CPattern)) - { - for (u32 i = 0; i < HuePattern_Count; i++) - { - if (ui_Button(I, PatternOptions[i])) - { - PHue.Pattern = i; + gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); + ui_BeginRow(I, 5); + { + ui_Label(I, Label); + + bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; + if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; + } + bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; + if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; + } + bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; + if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; + } + bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; + if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; + } + } + ui_EndRow(I); } - } - } - ui_EndLabeledDropdown(I); - - - gs_string AddInOptions[AddIn_Count] = {}; - AddInOptions[AddIn_None] = MakeString("NA"); - AddInOptions[AddIn_Waves] = MakeString("waves"); - AddInOptions[AddIn_Rotary] = MakeString("rotary"); - - gs_string CAddIn = AddInOptions[PHue.AddIn]; - if (ui_BeginLabeledDropdown(I, MakeString("Add In"), CAddIn)) - { - for (u32 i = 0; i < AddIn_Count; i++) - { - if (ui_Button(I, AddInOptions[i])) + BLState->DEBUG_PendingMotorPacket = PendingPacket; + + if (ui_Button(I, MakeString("Send Motor Packet"))) { - PHue.AddIn = i; + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); + } - } - } - ui_EndLabeledDropdown(I); - BLState->DebugHue = PHue; - } - - - InterfaceAssert(I->PerFrameMemory); - }break; - - case BlumenDebug_Awaken: - { - ui_Label(I, MakeString("Step 1:")); - ui_Label(I, MakeString("Leds off, flowers closed")); - if (ui_Button(I, MakeString("Prepare"))) - { - // motors closed - blumen_packet M = {}; - M.Type = PacketType_MotorState; - M.MotorPacket.FlowerPositions[0] = MotorState_Closed; - M.MotorPacket.FlowerPositions[1] = MotorState_Closed; - M.MotorPacket.FlowerPositions[2] = MotorState_Closed; - gs_data D = StructToData(&M, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, D); + + motor_packet MotorPos = BLState->LastKnownMotorState; + ui_Label(I, MakeString("Current Motor Positions")); + { + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + ui_BeginRow(I, 2); + gs_string MotorStr = PushStringF(State->Transient, 32, + "Motor %d", + i); + ui_Label(I, MotorStr); + + gs_string StateStr = {}; + switch (MotorPos.FlowerPositions[i]) + { + case MotorState_Closed: { + StateStr = MakeString("Closed"); + } break; + case MotorState_HalfOpen: { + StateStr = MakeString("Half Open"); + } break; + case MotorState_MostlyOpen: { + StateStr = MakeString("Mostly Open"); + } break; + case MotorState_Open: { + StateStr = MakeString("Open"); + } break; + + default: + { + StateStr = MakeString("Invalid Value"); + } break; + } + + ui_Label(I, StateStr); + ui_EndRow(I); + } + } + + ui_Label(I, MakeString("Set Internal Motor State:")); + if (ui_Button(I, MakeString("Closed"))) + { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Closed; + Motor.Pos.FlowerPositions[1] = MotorState_Closed; + Motor.Pos.FlowerPositions[2] = MotorState_Closed; + Motor.Temperature = 16; + + BlumenLumen_UpdateMotorState(BLState, Motor, Context); + } + if (ui_Button(I, MakeString("Open"))) + { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Open; + Motor.Pos.FlowerPositions[1] = MotorState_Open; + Motor.Pos.FlowerPositions[2] = MotorState_Open; + Motor.Temperature = 16; + + BlumenLumen_UpdateMotorState(BLState, Motor, Context); + } + } break; - // animation - State->AnimationSystem.RepeatMode = AnimationRepeat_Single; - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, - BLState->OffAnimHandle, - VoiceCommandFadeDuration); + case BlumenDebug_Leds: + { + BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); + + BLState->IgnoreTimeOfDay_LedDimming = ui_ToggleText(I, MakeString("Leds Ignore Time Limit"), BLState->IgnoreTimeOfDay_LedDimming); + + if (ui_BeginLabeledDropdown(I, MakeString("Phrase"), MakeString(BLState->PendingPhrase.Phrase))) + { + u32 ListCount = BLState->PhraseHueMap.Count; + ui_BeginList(I, MakeString("Phrase List"), 5, ListCount); + for (u32 i = 0; i < ListCount; i++) + { + gs_string Str = MakeString(BLState->PhraseHueMap.Phrases[i]); + if (ui_Button(I, Str)) + { + BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i); + BLState->DebugHue = BLState->PendingPhrase; + } + } + ui_EndList(I); + } + ui_EndLabeledDropdown(I); + if (ui_Button(I, MakeString("Say Phrase"))) + { + gs_string DebugStr = PushString(State->Transient, 256); + BLState->NextHotHue = BLState->PendingPhrase; + BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State); + } + + ui_Label(I, MakeString("Phrase Constructor")); + BLState->DebugOverrideHue = ui_ToggleText(I, MakeString("Override Hue"), BLState->DebugOverrideHue); + if (BLState->DebugOverrideHue) + { + phrase_hue PHue = BLState->DebugHue; + PHue.Hue0.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue0"), (r32)PHue.Hue0.HSV.x, 0, 360); + PHue.Hue1.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue1"), (r32)PHue.Hue1.HSV.x, 0, 360); + PHue.Hue2.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue2"), (r32)PHue.Hue2.HSV.x, 0, 360); + PHue.Granularity = (u32)ui_LabeledRangeSlider(I, MakeString("Granularity"), (r32)PHue.Granularity, 0, 5); + PHue.Speed = ui_LabeledRangeSlider(I, MakeString("Speed"), PHue.Speed, 0, 4); + + gs_string PatternOptions[HuePattern_Count] = {}; + PatternOptions[HuePattern_Patchy] = MakeString("patchy"); + PatternOptions[HuePattern_Wavy] = MakeString("wavy"); + + gs_string CPattern = PatternOptions[PHue.Pattern]; + if (ui_BeginLabeledDropdown(I, MakeString("Pattern"), CPattern)) + { + for (u32 i = 0; i < HuePattern_Count; i++) + { + if (ui_Button(I, PatternOptions[i])) + { + PHue.Pattern = i; + } + } + } + ui_EndLabeledDropdown(I); + + + gs_string AddInOptions[AddIn_Count] = {}; + AddInOptions[AddIn_None] = MakeString("NA"); + AddInOptions[AddIn_Waves] = MakeString("waves"); + AddInOptions[AddIn_Rotary] = MakeString("rotary"); + + gs_string CAddIn = AddInOptions[PHue.AddIn]; + if (ui_BeginLabeledDropdown(I, MakeString("Add In"), CAddIn)) + { + for (u32 i = 0; i < AddIn_Count; i++) + { + if (ui_Button(I, AddInOptions[i])) + { + PHue.AddIn = i; + } + } + } + ui_EndLabeledDropdown(I); + BLState->DebugHue = PHue; + } + + + InterfaceAssert(I->PerFrameMemory); + }break; - BLState->PatternMode = BlumenPattern_NoControl; - BLState->IgnoreTimeOfDay_LedDimming = true; - BLState->IgnoreTimeOfDay_MotorState = true; - } - - ui_Label(I, MakeString("Step 2:")); - if (ui_Button(I, MakeString("Begin Light Show"))) - { - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, - BLState->AwakenHandle, - VoiceCommandFadeDuration); - } - - ui_Label(I, MakeString("Step 3:")); - if (ui_Button(I, MakeString("Open Flowers"))) - { - // motors closed - blumen_packet M = {}; - M.Type = PacketType_MotorState; - M.MotorPacket.FlowerPositions[0] = MotorState_Open; - M.MotorPacket.FlowerPositions[1] = MotorState_Open; - M.MotorPacket.FlowerPositions[2] = MotorState_Open; - gs_data D = StructToData(&M, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, D); - } - - ui_Label(I, MakeString("Step 4:")); - ui_Label(I, MakeString("Resets Lumenarium")); - if (ui_Button(I, MakeString("Complete"))) - { - BLState->IgnoreTimeOfDay_LedDimming = false; - BLState->IgnoreTimeOfDay_MotorState = false; - BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, - BLState); - } - }break; - - InvalidDefaultCase; - } + case BlumenDebug_Awaken: + { + ui_Label(I, MakeString("Step 1:")); + ui_Label(I, MakeString("Leds off, flowers closed")); + if (ui_Button(I, MakeString("Prepare"))) + { + // motors closed + blumen_packet M = {}; + M.Type = PacketType_MotorState; + M.MotorPacket.FlowerPositions[0] = MotorState_Closed; + M.MotorPacket.FlowerPositions[1] = MotorState_Closed; + M.MotorPacket.FlowerPositions[2] = MotorState_Closed; + gs_data D = StructToData(&M, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, D); + + // animation + State->AnimationSystem.RepeatMode = AnimationRepeat_Single; + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + BLState->OffAnimHandle, + VoiceCommandFadeDuration); + + BLState->PatternMode = BlumenPattern_NoControl; + BLState->IgnoreTimeOfDay_LedDimming = true; + BLState->IgnoreTimeOfDay_MotorState = true; + } + + ui_Label(I, MakeString("Step 2:")); + if (ui_Button(I, MakeString("Begin Light Show"))) + { + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + BLState->AwakenHandle, + VoiceCommandFadeDuration); + } + + ui_Label(I, MakeString("Step 3:")); + if (ui_Button(I, MakeString("Open Flowers"))) + { + // motors closed + blumen_packet M = {}; + M.Type = PacketType_MotorState; + M.MotorPacket.FlowerPositions[0] = MotorState_Open; + M.MotorPacket.FlowerPositions[1] = MotorState_Open; + M.MotorPacket.FlowerPositions[2] = MotorState_Open; + gs_data D = StructToData(&M, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, D); + } + + ui_Label(I, MakeString("Step 4:")); + ui_Label(I, MakeString("Resets Lumenarium")); + if (ui_Button(I, MakeString("Complete"))) + { + BLState->IgnoreTimeOfDay_LedDimming = false; + BLState->IgnoreTimeOfDay_MotorState = false; + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, + BLState); + } + }break; + + InvalidDefaultCase; + } } US_CUSTOM_CLEANUP(BlumenLumen_CustomCleanup) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; - BLState->Running = false; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + BLState->Running = false; } internal user_space_desc BlumenLumen_UserSpaceCreate() { - user_space_desc Result = {}; - Result.LoadPatterns = BlumenLumen_LoadPatterns; - Result.CustomInit = BlumenLumen_CustomInit; - Result.CustomUpdate = BlumenLumen_CustomUpdate; - Result.CustomDebugUI = BlumenLumen_DebugUI; - Result.CustomCleanup = BlumenLumen_CustomCleanup; - return Result; + user_space_desc Result = {}; + Result.LoadPatterns = BlumenLumen_LoadPatterns; + Result.CustomInit = BlumenLumen_CustomInit; + Result.CustomUpdate = BlumenLumen_CustomUpdate; + Result.CustomDebugUI = BlumenLumen_DebugUI; + Result.CustomCleanup = BlumenLumen_CustomCleanup; + return Result; } #define BLUMEN_LUMEN_CPP diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 80e3565..c8d44c1 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -16,7 +16,7 @@ gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold"); // The path to the phrase map CSV. Can be an absolute path, or relative // to the app_run_tree folder -gs_const_string PhraseMapCSVPath = ConstString("C:/projects/Lumenarium/app_run_tree/data/flower_codes.tsv"); +gs_const_string PhraseMapCSVPath = ConstString("C:/projects/flowers-sound/flower_codes.tsv"); char PhraseMapCSVSeparator = '\t'; // Search Strings for which folders to find ambient animation files and @@ -57,6 +57,7 @@ global time_range LedOnTimes[] = { { 00, 00, 06, 30 }, }; global u32 LedOnTimesCount = CArrayLength(LedOnTimes); // do not edit +global b8 DEBUGIgnoreLedOnTimeRange = true; // How long it takes to fade from the default pattern to the // voice activated pattern From b32ca1d0922265ba6a784a5338e7da37b84ff309 Mon Sep 17 00:00:00 2001 From: PS Date: Mon, 20 Sep 2021 18:35:25 -0500 Subject: [PATCH 100/151] fixing blumen addressing --- src/sculpture_gen/gen_blumen_lumen.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index f67d4e6..63ad60c 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -253,8 +253,8 @@ int main(int ArgCount, char** Args) F0.Pos = v3{0, 0, 0}; F0.ComPort = "\\\\.\\COM11"; F0.FlowerTagValue = "left"; - F0.SACNStemInnerStartUniverse = 3; - F0.SACNStemOuterStartUniverse = 2; + F0.SACNStemInnerStartUniverse = 4; + F0.SACNStemOuterStartUniverse = 3; F0.SACNFlowerStemStartUniverse = 1; F0.StemChannels = StemChannels; F0.BloomOuterChannels = BloomOuterChannels; @@ -265,8 +265,8 @@ int main(int ArgCount, char** Args) F1.Pos = v3{0, 0, 0}; F1.ComPort = "\\\\.\\COM12"; F1.FlowerTagValue = "center"; - F1.SACNStemInnerStartUniverse = 8; - F1.SACNStemOuterStartUniverse = 7; + F1.SACNStemInnerStartUniverse = 9; + F1.SACNStemOuterStartUniverse = 8; F1.SACNFlowerStemStartUniverse = 6; F1.StemChannels = StemChannels; F1.BloomInnerChannels = BloomInnerChannels; @@ -277,8 +277,8 @@ int main(int ArgCount, char** Args) F2.Pos = v3{0, 0, 0}; F2.ComPort = "\\\\.\\COM6"; F2.FlowerTagValue = "right"; - F2.SACNStemInnerStartUniverse = 13; - F2.SACNStemOuterStartUniverse = 12; + F2.SACNStemInnerStartUniverse = 14; + F2.SACNStemOuterStartUniverse = 13; F2.SACNFlowerStemStartUniverse = 11; F2.StemChannels = StemChannels; F2.BloomInnerChannels = BloomInnerChannels; From d6eabfe3ac5a1e13de4b7be7ea4d623e7680c580 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Mon, 20 Sep 2021 20:20:40 -0400 Subject: [PATCH 101/151] on site edits --- .../ambient_patterns/ambient_1.foldanim | 12 +- .../ambient_patterns/wavy_0.foldanim | 14 +- .../data/blumen_animations/anim_demo.foldanim | 132 -- src/app/patterns/blumen_patterns.h | 2 +- src/app/platform_win32/win32_foldhaus.cpp | 1228 ++++++++--------- src/app/ss_blumen_lumen/blumen_lumen.cpp | 4 +- .../ss_blumen_lumen/blumen_lumen_settings.h | 2 +- src/sculpture_gen/gen_blumen_lumen.cpp | 476 +++---- 8 files changed, 874 insertions(+), 996 deletions(-) delete mode 100644 app_run_tree/data/blumen_animations/anim_demo.foldanim diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim index d7a5167..42794d8 100644 --- a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim +++ b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim @@ -4,7 +4,7 @@ layers_count: 3; blocks_count: 4; playable_range:{ min: 0; - max: 2000; + max: 10000; }; layers:{ layer:{ @@ -24,7 +24,7 @@ blocks:{ block:{ frame_range:{ min: 0; - max: 2000; + max: 19; }; layer_index: 0; animation_name: "Pattern_BulbMask"; @@ -32,7 +32,7 @@ blocks:{ block:{ frame_range:{ min: 0; - max: 2000; + max: 10000; }; layer_index: 1; animation_name: "Pattern_Leafy"; @@ -40,15 +40,15 @@ blocks:{ block:{ frame_range:{ min: 0; - max: 1032; + max: 5156; }; layer_index: 2; animation_name: "Pattern_Rainbow"; }; block:{ frame_range:{ - min: 959; - max: 2000; + min: 5040; + max: 10000; }; layer_index: 2; animation_name: "Pattern_Wavy"; diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim index 2ed33bc..bc4979c 100644 --- a/app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim +++ b/app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim @@ -1,7 +1,7 @@ lumenarium_animation_file; animation_name: "wavy_0"; layers_count: 2; -blocks_count: 1; +blocks_count: 2; playable_range:{ min: 0; max: 7200; @@ -9,11 +9,11 @@ playable_range:{ layers:{ layer:{ name: "[New Layer]"; - blend: "Add"; + blend: "Overwrite"; }; layer:{ name: "Color"; - blend: "Add"; + blend: "Multiply"; }; }; blocks:{ @@ -25,4 +25,12 @@ blocks:{ layer_index: 1; animation_name: "Pattern_Wavy"; }; + block:{ + frame_range:{ + min: 78; + max: 3571; + }; + layer_index: 0; + animation_name: "Pattern_Leafy"; + }; }; diff --git a/app_run_tree/data/blumen_animations/anim_demo.foldanim b/app_run_tree/data/blumen_animations/anim_demo.foldanim deleted file mode 100644 index 6f424e1..0000000 --- a/app_run_tree/data/blumen_animations/anim_demo.foldanim +++ /dev/null @@ -1,132 +0,0 @@ -lumenarium_animation_file; -animation_name: "demo_anim"; -layers_count: 2; -blocks_count: 14; -playable_range:{ - min: 0; - max: 10000; -}; -layers:{ - layer:{ - name: "main"; - blend: "Add"; - }; - layer:{ - name: "color"; - blend: "Multiply"; - }; -}; -blocks:{ - block:{ - frame_range:{ - min: 2428; - max: 3017; - }; - layer_index: 0; - animation_name: "Pattern_HueShift"; - }; - block:{ - frame_range:{ - min: 1711; - max: 2487; - }; - layer_index: 0; - animation_name: "Pattern_Rainbow"; - }; - block:{ - frame_range:{ - min: 0; - max: 603; - }; - layer_index: 0; - animation_name: "Pattern_VerticalLines"; - }; - block:{ - frame_range:{ - min: 2945; - max: 3656; - }; - layer_index: 0; - animation_name: "Pattern_Wavy"; - }; - block:{ - frame_range:{ - min: 3592; - max: 4296; - }; - layer_index: 0; - animation_name: "Pattern_Patchy"; - }; - block:{ - frame_range:{ - min: 520; - max: 1225; - }; - layer_index: 0; - animation_name: "Pattern_Rotary"; - }; - block:{ - frame_range:{ - min: 4218; - max: 4939; - }; - layer_index: 0; - animation_name: "Pattern_LeafyPatchy"; - }; - block:{ - frame_range:{ - min: 4849; - max: 5431; - }; - layer_index: 0; - animation_name: "Pattern_HueShift"; - }; - block:{ - frame_range:{ - min: 1149; - max: 1819; - }; - layer_index: 0; - animation_name: "Pattern_Leafy"; - }; - block:{ - frame_range:{ - min: 5529; - max: 7782; - }; - layer_index: 1; - animation_name: "Pattern_Wavy"; - }; - block:{ - frame_range:{ - min: 5529; - max: 5979; - }; - layer_index: 0; - animation_name: "Pattern_Leafy"; - }; - block:{ - frame_range:{ - min: 5912; - max: 6328; - }; - layer_index: 0; - animation_name: "Pattern_VerticalLines"; - }; - block:{ - frame_range:{ - min: 6281; - max: 6581; - }; - layer_index: 0; - animation_name: "Pattern_Rotary"; - }; - block:{ - frame_range:{ - min: 6524; - max: 7025; - }; - layer_index: 0; - animation_name: "Pattern_HueShift"; - }; -}; diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index 65283c5..6530ccd 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -328,7 +328,7 @@ Pattern_Leafy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 T { DEBUG_TRACK_FUNCTION; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData; - Time = Time * BLState->PatternSpeed; + Time = Time * BLState->PatternSpeed * .3f; phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly); v4 C0 = RGBFromPhraseHue(Hue.Hue0); diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 4a4752d..d281552 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -40,8 +40,8 @@ window MainWindow; PLATFORM_GET_GPU_TEXTURE_HANDLE(Win32GetGPUTextureHandle) { - s32 Handle = SubmitTexture(Memory, Width, Height); - return Handle; + s32 Handle = SubmitTexture(Memory, Width, Height); + return Handle; } HDC FontDrawingDC; @@ -50,462 +50,462 @@ HFONT CurrentFont; GET_FONT_INFO(Win32GetFontInfo) { - platform_font_info Result = {}; - - FontDrawingDC = CreateCompatibleDC(NULL); - SetBkColor(FontDrawingDC, RGB(0, 0, 0)); - SetTextColor(FontDrawingDC, RGB(255, 255, 255)); - FontBitmap = CreateCompatibleBitmap(FontDrawingDC, PixelHeight * 2, PixelHeight * 2); - HGDIOBJ SelectObjectResult = SelectObject(FontDrawingDC, FontBitmap); - - CurrentFont = CreateFont(PixelHeight, 0, 0, 0, - FontWeight, - Italic, - Underline, - Strikeout, - ANSI_CHARSET, - OUT_OUTLINE_PRECIS, - CLIP_DEFAULT_PRECIS, - PROOF_QUALITY, - FIXED_PITCH, - FontName); - SelectFont(FontDrawingDC, CurrentFont); - - TEXTMETRIC WindowsFontMetrics = {}; - if (GetTextMetrics(FontDrawingDC, &WindowsFontMetrics)) - { - Result.PixelHeight = WindowsFontMetrics.tmHeight; - Result.Ascent = WindowsFontMetrics.tmAscent; - Result.Descent = WindowsFontMetrics.tmDescent; - Result.Leading = WindowsFontMetrics.tmExternalLeading; - Result.MaxCharWidth = WindowsFontMetrics.tmMaxCharWidth; - Result.CodepointStart = WindowsFontMetrics.tmFirstChar; - Result.CodepointOnePastLast = WindowsFontMetrics.tmLastChar + 1; - } - - return Result; + platform_font_info Result = {}; + + FontDrawingDC = CreateCompatibleDC(NULL); + SetBkColor(FontDrawingDC, RGB(0, 0, 0)); + SetTextColor(FontDrawingDC, RGB(255, 255, 255)); + FontBitmap = CreateCompatibleBitmap(FontDrawingDC, PixelHeight * 2, PixelHeight * 2); + HGDIOBJ SelectObjectResult = SelectObject(FontDrawingDC, FontBitmap); + + CurrentFont = CreateFont(PixelHeight, 0, 0, 0, + FontWeight, + Italic, + Underline, + Strikeout, + ANSI_CHARSET, + OUT_OUTLINE_PRECIS, + CLIP_DEFAULT_PRECIS, + PROOF_QUALITY, + FIXED_PITCH, + FontName); + SelectFont(FontDrawingDC, CurrentFont); + + TEXTMETRIC WindowsFontMetrics = {}; + if (GetTextMetrics(FontDrawingDC, &WindowsFontMetrics)) + { + Result.PixelHeight = WindowsFontMetrics.tmHeight; + Result.Ascent = WindowsFontMetrics.tmAscent; + Result.Descent = WindowsFontMetrics.tmDescent; + Result.Leading = WindowsFontMetrics.tmExternalLeading; + Result.MaxCharWidth = WindowsFontMetrics.tmMaxCharWidth; + Result.CodepointStart = WindowsFontMetrics.tmFirstChar; + Result.CodepointOnePastLast = WindowsFontMetrics.tmLastChar + 1; + } + + return Result; } DRAW_FONT_CODEPOINT(Win32DrawFontCodepoint) { - SIZE CodepointSize = {}; - if (GetTextExtentPoint32(FontDrawingDC, &Codepoint, 1, &CodepointSize)) - { - *OutWidth = CodepointSize.cx; - *OutHeight = CodepointSize.cy; - - RECT TextRect = {}; - TextRect.left = 0; - TextRect.right = *OutWidth; - TextRect.top = 0; - TextRect.bottom = *OutHeight; - - int Error = DrawText(FontDrawingDC, &Codepoint, 1, &TextRect, DT_LEFT | DT_NOCLIP | DT_TOP); - - u8* Row = DestBuffer + (YOffset * (DestBufferWidth * 4)); - COLORREF PixelColor; - for (u32 Y = 0; Y < *OutHeight; Y++) + SIZE CodepointSize = {}; + if (GetTextExtentPoint32(FontDrawingDC, &Codepoint, 1, &CodepointSize)) { - // NOTE(Peter): XOffset * 4 b/c its 4 bytes per pixel. - u8* Channel = (u8*)Row + (XOffset * 4); - for (u32 X = 0; X < *OutWidth; X++) - { - PixelColor = GetPixel(FontDrawingDC, X + TextRect.left, TextRect.bottom - Y); - Assert(PixelColor != CLR_INVALID); - u8 RValue = GetRValue(PixelColor); - *Channel++ = RValue; - *Channel++ = RValue; - *Channel++ = RValue; - *Channel++ = RValue; - } - Row += DestBufferWidth * 4; + *OutWidth = CodepointSize.cx; + *OutHeight = CodepointSize.cy; + + RECT TextRect = {}; + TextRect.left = 0; + TextRect.right = *OutWidth; + TextRect.top = 0; + TextRect.bottom = *OutHeight; + + int Error = DrawText(FontDrawingDC, &Codepoint, 1, &TextRect, DT_LEFT | DT_NOCLIP | DT_TOP); + + u8* Row = DestBuffer + (YOffset * (DestBufferWidth * 4)); + COLORREF PixelColor; + for (u32 Y = 0; Y < *OutHeight; Y++) + { + // NOTE(Peter): XOffset * 4 b/c its 4 bytes per pixel. + u8* Channel = (u8*)Row + (XOffset * 4); + for (u32 X = 0; X < *OutWidth; X++) + { + PixelColor = GetPixel(FontDrawingDC, X + TextRect.left, TextRect.bottom - Y); + Assert(PixelColor != CLR_INVALID); + u8 RValue = GetRValue(PixelColor); + *Channel++ = RValue; + *Channel++ = RValue; + *Channel++ = RValue; + *Channel++ = RValue; + } + Row += DestBufferWidth * 4; + } + } - - } } LRESULT CALLBACK HandleWindowEvents (HWND WindowHandle, UINT Msg, WPARAM WParam, LPARAM LParam) { - LRESULT Result = 0; - - switch (Msg) - { - case WM_SIZE: - { - Win32UpdateWindowDimension(&MainWindow); - //Win32ResizeDIBSection(&GlobalBackbuffer, MainWindow.Info.Width, MainWindow.Info.Height); - }break; + LRESULT Result = 0; - case WM_CLOSE: + switch (Msg) { - Result = DefWindowProc(WindowHandle, Msg, WParam, LParam); - Running = false; - }break; - - case WM_DESTROY: - { - }break; - - case WM_PAINT: - { - PAINTSTRUCT PaintStruct; - HDC DeviceContext; - b32 PaintResult; - - DeviceContext = BeginPaint(WindowHandle, &PaintStruct); - PaintResult = EndPaint(WindowHandle, &PaintStruct); - }break; - - case WM_ACTIVATE: - { - WindowIsActive = (LOWORD(WParam) == WA_ACTIVE || LOWORD(WParam) == WA_CLICKACTIVE); - }break; - - default: - { - Result = DefWindowProc(WindowHandle, Msg, WParam, LParam); + case WM_SIZE: + { + Win32UpdateWindowDimension(&MainWindow); + //Win32ResizeDIBSection(&GlobalBackbuffer, MainWindow.Info.Width, MainWindow.Info.Height); + }break; + + case WM_CLOSE: + { + Result = DefWindowProc(WindowHandle, Msg, WParam, LParam); + Running = false; + }break; + + case WM_DESTROY: + { + }break; + + case WM_PAINT: + { + PAINTSTRUCT PaintStruct; + HDC DeviceContext; + b32 PaintResult; + + DeviceContext = BeginPaint(WindowHandle, &PaintStruct); + PaintResult = EndPaint(WindowHandle, &PaintStruct); + }break; + + case WM_ACTIVATE: + { + WindowIsActive = (LOWORD(WParam) == WA_ACTIVE || LOWORD(WParam) == WA_CLICKACTIVE); + }break; + + default: + { + Result = DefWindowProc(WindowHandle, Msg, WParam, LParam); + } } - } - - return Result; + + return Result; } internal void HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse_state* Mouse) { - switch (Message.message) - { - case WM_MOUSEWHEEL: - { - Mouse->Scroll = GET_WHEEL_DELTA_WPARAM(Message.wParam); - }break; - - case WM_LBUTTONDOWN: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, false, true, - ShiftDown, AltDown, CtrlDown, false); - - Mouse->LeftButtonState |= KeyState_IsDown; - Mouse->DownPos = Mouse->Pos; - - // :Win32MouseEventCapture - // 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(Window->Handle); - }break; - - case WM_MBUTTONDOWN: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseMiddleButton, false, true, - ShiftDown, AltDown, CtrlDown, false); - Mouse->MiddleButtonState = KeyState_IsDown & ~KeyState_WasDown; - - // :Win32MouseEventCapture - SetCapture(Window->Handle); - }break; - - case WM_RBUTTONDOWN: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, false, true, - ShiftDown, AltDown, CtrlDown, false); - Mouse->RightButtonState = KeyState_IsDown & ~KeyState_WasDown; - Mouse->DownPos = Mouse->Pos; - - // :Win32MouseEventCapture - SetCapture(Window->Handle); - }break; - - case WM_LBUTTONUP: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, true, false, - ShiftDown, AltDown, CtrlDown, false); - Mouse->LeftButtonState &= ~KeyState_IsDown; - - // :Win32MouseEventCapture - ReleaseCapture(); - }break; - - case WM_MBUTTONUP: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseMiddleButton, true, false, - ShiftDown, AltDown, CtrlDown, false); - Mouse->MiddleButtonState = ~KeyState_IsDown & KeyState_WasDown; - - // :Win32MouseEventCapture - ReleaseCapture(); - }break; - - case WM_RBUTTONUP: - { - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, true, false, - ShiftDown, AltDown, CtrlDown, false); - Mouse->RightButtonState = ~KeyState_IsDown & KeyState_WasDown; - - // :Win32MouseEventCapture - ReleaseCapture(); - }break; - - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - case WM_KEYDOWN: - case WM_KEYUP: + switch (Message.message) { + case WM_MOUSEWHEEL: + { + Mouse->Scroll = GET_WHEEL_DELTA_WPARAM(Message.wParam); + }break; + + case WM_LBUTTONDOWN: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, false, true, + ShiftDown, AltDown, CtrlDown, false); + + Mouse->LeftButtonState |= KeyState_IsDown; + Mouse->DownPos = Mouse->Pos; + + // :Win32MouseEventCapture + // 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(Window->Handle); + }break; + + case WM_MBUTTONDOWN: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseMiddleButton, false, true, + ShiftDown, AltDown, CtrlDown, false); + Mouse->MiddleButtonState = KeyState_IsDown & ~KeyState_WasDown; + + // :Win32MouseEventCapture + SetCapture(Window->Handle); + }break; + + case WM_RBUTTONDOWN: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, false, true, + ShiftDown, AltDown, CtrlDown, false); + Mouse->RightButtonState = KeyState_IsDown & ~KeyState_WasDown; + Mouse->DownPos = Mouse->Pos; + + // :Win32MouseEventCapture + SetCapture(Window->Handle); + }break; + + case WM_LBUTTONUP: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, true, false, + ShiftDown, AltDown, CtrlDown, false); + Mouse->LeftButtonState &= ~KeyState_IsDown; + + // :Win32MouseEventCapture + ReleaseCapture(); + }break; + + case WM_MBUTTONUP: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseMiddleButton, true, false, + ShiftDown, AltDown, CtrlDown, false); + Mouse->MiddleButtonState = ~KeyState_IsDown & KeyState_WasDown; + + // :Win32MouseEventCapture + ReleaseCapture(); + }break; + + case WM_RBUTTONUP: + { + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, true, false, + ShiftDown, AltDown, CtrlDown, false); + Mouse->RightButtonState = ~KeyState_IsDown & KeyState_WasDown; + + // :Win32MouseEventCapture + ReleaseCapture(); + }break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + { #if 0 - int VirtualKey = (int)Message.wParam; - key_code Key = Win32GetKeyCode(VirtualKey, true, false); - s32 KeyIndex = (int)Key; - - b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0; - b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0; - - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - // New Input Queue - AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown, - ShiftDown, AltDown, CtrlDown, false); + int VirtualKey = (int)Message.wParam; + key_code Key = Win32GetKeyCode(VirtualKey, true, false); + s32 KeyIndex = (int)Key; + + b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0; + b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0; + + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + // New Input Queue + AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown, + ShiftDown, AltDown, CtrlDown, false); #endif - TranslateMessage(&Message); - DispatchMessage(&Message); - }break; - - case WM_CHAR: - { - char VirtualKey = (char)Message.wParam; - key_code Key = CharToKeyCode(VirtualKey); - s32 KeyIndex = (int)Key; - - b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0; - b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0; - - b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; - b32 AltDown = GetKeyState(VK_MENU) & 0x8000; - b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; - - // New Input Queue - AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown, - ShiftDown, AltDown, CtrlDown, false); - }break; - - default: - { - TranslateMessage(&Message); - DispatchMessage(&Message); - }break; - } + TranslateMessage(&Message); + DispatchMessage(&Message); + }break; + + case WM_CHAR: + { + char VirtualKey = (char)Message.wParam; + key_code Key = CharToKeyCode(VirtualKey); + s32 KeyIndex = (int)Key; + + b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0; + b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0; + + b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000; + b32 AltDown = GetKeyState(VK_MENU) & 0x8000; + b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000; + + // New Input Queue + AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown, + ShiftDown, AltDown, CtrlDown, false); + }break; + + default: + { + TranslateMessage(&Message); + DispatchMessage(&Message); + }break; + } } internal void DebugPrint (char* Format, ...) { - char Buffer[256]; - gs_string StringBuffer = MakeString(Buffer, 256); - va_list Args; - va_start(Args, Format); - Log_PrintFVarArgs(GlobalLogBuffer, LogEntry_Message, Format, Args); - va_end(Args); + char Buffer[256]; + gs_string StringBuffer = MakeString(Buffer, 256); + va_list Args; + va_start(Args, Format); + Log_PrintFVarArgs(GlobalLogBuffer, LogEntry_Message, Format, Args); + va_end(Args); } internal void SetApplicationLinks (context* Context, win32_dll_refresh DLL, gs_work_queue* WorkQueue) { - if (DLL.IsValid) - { - Context->InitializeApplication = (initialize_application*)GetProcAddress(DLL.DLL, "InitializeApplication"); - Context->ReloadStaticData = (reload_static_data*)GetProcAddress(DLL.DLL, "ReloadStaticData"); - Context->UpdateAndRender = (update_and_render*)GetProcAddress(DLL.DLL, "UpdateAndRender"); - Context->CleanupApplication = (cleanup_application*)GetProcAddress(DLL.DLL, "CleanupApplication"); - } - else - { - Context->InitializeApplication = 0; - Context->ReloadStaticData = 0; - Context->UpdateAndRender = 0; - Context->CleanupApplication = 0; - } + if (DLL.IsValid) + { + Context->InitializeApplication = (initialize_application*)GetProcAddress(DLL.DLL, "InitializeApplication"); + Context->ReloadStaticData = (reload_static_data*)GetProcAddress(DLL.DLL, "ReloadStaticData"); + Context->UpdateAndRender = (update_and_render*)GetProcAddress(DLL.DLL, "UpdateAndRender"); + Context->CleanupApplication = (cleanup_application*)GetProcAddress(DLL.DLL, "CleanupApplication"); + } + else + { + Context->InitializeApplication = 0; + Context->ReloadStaticData = 0; + Context->UpdateAndRender = 0; + Context->CleanupApplication = 0; + } } internal void Win32_SendAddressedDataBuffer(gs_thread_context Context, addressed_data_buffer* BufferAt) { - DEBUG_TRACK_FUNCTION; - - u32 BuffersSent = 0; - u32 DataSizeSent = 0; - - switch(BufferAt->AddressType) - { - case AddressType_NetworkIP: - { - Win32Socket_SendTo(BufferAt->SendSocket, - BufferAt->V4SendAddress, - BufferAt->SendPort, - (const char*)BufferAt->Memory, - BufferAt->MemorySize, - 0); - }break; + DEBUG_TRACK_FUNCTION; - case AddressType_ComPort: + u32 BuffersSent = 0; + u32 DataSizeSent = 0; + + switch(BufferAt->AddressType) { - if (BufferAt->ComPort.Length > 0) - { - HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 2000000, 8, NOPARITY, 1, Context.Transient); - if (SerialPort != INVALID_HANDLE_VALUE) + case AddressType_NetworkIP: { - if (Win32SerialPort_Write(SerialPort, BufferAt->Data)) - { - BuffersSent += 1; - DataSizeSent += BufferAt->Data.Size; - } - else - { - Win32SerialArray_Close(BufferAt->ComPort); - } - } - } - else - { + Win32Socket_SendTo(BufferAt->SendSocket, + BufferAt->V4SendAddress, + BufferAt->SendPort, + (const char*)BufferAt->Memory, + BufferAt->MemorySize, + 0); + }break; + + case AddressType_ComPort: + { + if (BufferAt->ComPort.Length > 0) + { + HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 2000000, 8, NOPARITY, 1, Context.Transient); + if (SerialPort != INVALID_HANDLE_VALUE) + { + if (Win32SerialPort_Write(SerialPort, BufferAt->Data)) + { + BuffersSent += 1; + DataSizeSent += BufferAt->Data.Size; + } + else + { + Win32SerialArray_Close(BufferAt->ComPort); + } + } + } + else + { #if 0 - Log_Message(GlobalLogBuffer, - "Skipping data buffer because its COM Port isn't set"); + Log_Message(GlobalLogBuffer, + "Skipping data buffer because its COM Port isn't set"); #endif - } - }break; - - InvalidDefaultCase; - } + } + }break; + + InvalidDefaultCase; + } } internal void Win32_SendAddressedDataBuffer_Job(gs_thread_context Context, gs_data Arg) { - addressed_data_buffer* OutputData = (addressed_data_buffer*)Arg.Memory; - Win32_SendAddressedDataBuffer(Context, OutputData); + addressed_data_buffer* OutputData = (addressed_data_buffer*)Arg.Memory; + Win32_SendAddressedDataBuffer(Context, OutputData); } internal bool ReloadAndLinkDLL(win32_dll_refresh* DLL, context* Context, gs_work_queue* WorkQueue, bool ShouldError, bool AppReady) { - bool Success = false; - if (HotLoadDLL(DLL)) - { - SetApplicationLinks(Context, *DLL, WorkQueue); - Context->ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, AppReady); - Success = true; - Log_Message(GlobalLogBuffer, "Reloaded DLL\n"); - } - else if(ShouldError) - { - Log_Error(GlobalLogBuffer, "Unable to load application DLL at startup.\nAborting\n"); - } - return Success; + bool Success = false; + if (HotLoadDLL(DLL)) + { + SetApplicationLinks(Context, *DLL, WorkQueue); + Context->ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, AppReady); + Success = true; + Log_Message(GlobalLogBuffer, "Reloaded DLL\n"); + } + else if(ShouldError) + { + Log_Error(GlobalLogBuffer, "Unable to load application DLL at startup.\nAborting\n"); + } + return Success; } internal gs_const_string GetExePath(HINSTANCE HInstance, gs_thread_context ThreadContext) { - gs_const_string Result = {}; - - u32 Error = 0; - u32 PathSize = MAX_PATH; - char* Path = PushArray(ThreadContext.Transient, char, PathSize); - DWORD Length = GetModuleFileNameA(HInstance, Path, PathSize); - - if (Length) - { - Error = GetLastError(); - if (Error == ERROR_INSUFFICIENT_BUFFER) { - // PathSize wasn't long enough - // TODO(pjs): handle this case - InvalidCodePath; + gs_const_string Result = {}; + + u32 Error = 0; + u32 PathSize = MAX_PATH; + char* Path = PushArray(ThreadContext.Transient, char, PathSize); + DWORD Length = GetModuleFileNameA(HInstance, Path, PathSize); + + if (Length) + { + Error = GetLastError(); + if (Error == ERROR_INSUFFICIENT_BUFFER) { + // PathSize wasn't long enough + // TODO(pjs): handle this case + InvalidCodePath; + } + + Result.Str = Path; + Result.Length = (u64)Length; + } + else + { + Error = GetLastError(); + InvalidCodePath; } - Result.Str = Path; - Result.Length = (u64)Length; - } - else - { - Error = GetLastError(); - InvalidCodePath; - } - - return Result; + return Result; } internal bool SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) { - bool Result = false; - - gs_const_string ExePath = GetExePath(HInstance, ThreadContext); - gs_string ScratchPath = PushString(ThreadContext.Transient, ExePath.Length + 128); - gs_string WorkingDirectory = PushString(ThreadContext.Transient, ExePath.Length + 128); - - while (WorkingDirectory.Length == 0) - { - s64 LastSlash = FindLastFromSet(ExePath, "\\/"); - if (LastSlash < 0) break; + bool Result = false; - ExePath = Substring(ExePath, 0, LastSlash); - PrintF(&ScratchPath, "%S\\data", ExePath); - NullTerminate(&ScratchPath); + gs_const_string ExePath = GetExePath(HInstance, ThreadContext); + gs_string ScratchPath = PushString(ThreadContext.Transient, ExePath.Length + 128); + gs_string WorkingDirectory = PushString(ThreadContext.Transient, ExePath.Length + 128); - gs_file_info PathInfo = GetFileInfo(ThreadContext.FileHandler, ScratchPath.ConstString); - if (PathInfo.Path.Length > 0 && - PathInfo.IsDirectory) { - PrintF(&WorkingDirectory, "%S", ExePath); - NullTerminate(&WorkingDirectory); - } - } - - if (WorkingDirectory.Length > 0) - { - Log_Message(GlobalLogBuffer, - "Setting Working Directory \n%S \n", - WorkingDirectory.ConstString); - Result = SetCurrentDirectory(WorkingDirectory.Str); - if (!Result) + while (WorkingDirectory.Length == 0) { - u32 Error = GetLastError(); - InvalidCodePath; + s64 LastSlash = FindLastFromSet(ExePath, "\\/"); + if (LastSlash < 0) break; + + ExePath = Substring(ExePath, 0, LastSlash); + PrintF(&ScratchPath, "%S\\data", ExePath); + NullTerminate(&ScratchPath); + + gs_file_info PathInfo = GetFileInfo(ThreadContext.FileHandler, ScratchPath.ConstString); + if (PathInfo.Path.Length > 0 && + PathInfo.IsDirectory) { + PrintF(&WorkingDirectory, "%S", ExePath); + NullTerminate(&WorkingDirectory); + } } - } - else - { - Log_Error(GlobalLogBuffer, "Error, no data folder found\n"); - } - - return Result; + + if (WorkingDirectory.Length > 0) + { + Log_Message(GlobalLogBuffer, + "Setting Working Directory \n%S \n", + WorkingDirectory.ConstString); + Result = SetCurrentDirectory(WorkingDirectory.Str); + if (!Result) + { + u32 Error = GetLastError(); + InvalidCodePath; + } + } + else + { + Log_Error(GlobalLogBuffer, "Error, no data folder found\n"); + } + + return Result; } #include "../../gs_libs/gs_path.h" @@ -513,67 +513,67 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext) internal void Win32_SendOutputData(gs_thread_context ThreadContext, addressed_data_buffer_list OutputData) { - bool Multithread = true; - if (Multithread) - { - for (addressed_data_buffer* At = OutputData.Root; - At != 0; - At = At->Next) + bool Multithread = true; + if (Multithread) { - gs_data ProcArg = {}; - ProcArg.Memory = (u8*)At; - ProcArg.Size = sizeof(addressed_data_buffer); - Win32PushWorkOnQueue(&Win32WorkQueue.WorkQueue, Win32_SendAddressedDataBuffer_Job, ProcArg, ConstString("Send UART Data")); + for (addressed_data_buffer* At = OutputData.Root; + At != 0; + At = At->Next) + { + gs_data ProcArg = {}; + ProcArg.Memory = (u8*)At; + ProcArg.Size = sizeof(addressed_data_buffer); + Win32PushWorkOnQueue(&Win32WorkQueue.WorkQueue, Win32_SendAddressedDataBuffer_Job, ProcArg, ConstString("Send UART Data")); + } } - } - else - { - for (addressed_data_buffer* At = OutputData.Root; - At != 0; - At = At->Next) + else { - gs_data ProcArg = {}; - ProcArg.Memory = (u8*)At; - ProcArg.Size = sizeof(addressed_data_buffer); - Win32_SendAddressedDataBuffer_Job(ThreadContext, ProcArg); + for (addressed_data_buffer* At = OutputData.Root; + At != 0; + At = At->Next) + { + gs_data ProcArg = {}; + ProcArg.Memory = (u8*)At; + ProcArg.Size = sizeof(addressed_data_buffer); + Win32_SendAddressedDataBuffer_Job(ThreadContext, ProcArg); + } } - } - + } // Time internal system_time Win32GetSystemTime() { - system_time Result = {}; - - SYSTEMTIME WinLocalTime; - GetLocalTime(&WinLocalTime); - - SYSTEMTIME WinSysTime; - FILETIME WinSysFileTime; - GetSystemTime(&WinSysTime); - if (SystemTimeToFileTime((const SYSTEMTIME*)&WinSysTime, &WinSysFileTime)) - { - ULARGE_INTEGER SysTime = {}; - SysTime.LowPart = WinSysFileTime.dwLowDateTime; - SysTime.HighPart = WinSysFileTime.dwHighDateTime; + system_time Result = {}; - Result.NanosSinceEpoch = SysTime.QuadPart; - Result.Year = WinLocalTime.wYear; - Result.Month = WinLocalTime.wMonth; - Result.Day = WinLocalTime.wDay; - Result.Hour = WinLocalTime.wHour; - Result.Minute = WinLocalTime.wMinute; - Result.Second = WinLocalTime.wSecond; - } - else - { - u32 Error = GetLastError(); - InvalidCodePath; - } - - return Result; + SYSTEMTIME WinLocalTime; + GetLocalTime(&WinLocalTime); + + SYSTEMTIME WinSysTime; + FILETIME WinSysFileTime; + GetSystemTime(&WinSysTime); + if (SystemTimeToFileTime((const SYSTEMTIME*)&WinSysTime, &WinSysFileTime)) + { + ULARGE_INTEGER SysTime = {}; + SysTime.LowPart = WinSysFileTime.dwLowDateTime; + SysTime.HighPart = WinSysFileTime.dwHighDateTime; + + Result.NanosSinceEpoch = SysTime.QuadPart; + Result.Year = WinLocalTime.wYear; + Result.Month = WinLocalTime.wMonth; + Result.Day = WinLocalTime.wDay; + Result.Hour = WinLocalTime.wHour; + Result.Minute = WinLocalTime.wMinute; + Result.Second = WinLocalTime.wSecond; + } + else + { + u32 Error = GetLastError(); + InvalidCodePath; + } + + return Result; } int WINAPI @@ -584,202 +584,202 @@ WinMain ( INT NCmdShow ) { - gs_thread_context ThreadContext = Win32CreateThreadContext(); - - gs_memory_arena PlatformPermanent = MemoryArenaCreate(MB(4), - Bytes(8), ThreadContext.Allocator, - 0, - 0, - "Platform Memory"); - - GlobalLogBuffer = AllocStruct(ThreadContext.Allocator, log_buffer, "Global Log Buffer"); - *GlobalLogBuffer = Log_Init(&PlatformPermanent, 32); - - if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; - - context Context = {}; - Context.ThreadContext = ThreadContext; - Context.SystemTime_Current = Win32GetSystemTime(); - - gs_const_string Args = ConstString((char*)CmdLineArgs); - if (StringsEqual(Args, ConstString("-headless"))) - { - Log_Message(GlobalLogBuffer, "Running Headless\n"); - Context.Headless = true; - } - - if (!Context.Headless) - { - MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); - Win32UpdateWindowDimension(&MainWindow); + gs_thread_context ThreadContext = Win32CreateThreadContext(); - win32_opengl_window_info OpenGLWindowInfo = {}; - OpenGLWindowInfo.ColorBits = 32; - OpenGLWindowInfo.AlphaBits = 8; - OpenGLWindowInfo.DepthBits = 0; - CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow); - } - - s64 PerformanceCountFrequency = GetPerformanceFrequency(); - s64 LastFrameEnd = GetWallClock(); - r32 TargetSecondsPerFrame = 1 / 30.0f; - r32 LastFrameSecondsElapsed = 0.0f; - - GlobalDebugServices = PushStruct(&PlatformPermanent, debug_services); + gs_memory_arena PlatformPermanent = MemoryArenaCreate(MB(4), + Bytes(8), ThreadContext.Allocator, + 0, + 0, + "Platform Memory"); + + GlobalLogBuffer = AllocStruct(ThreadContext.Allocator, log_buffer, "Global Log Buffer"); + *GlobalLogBuffer = Log_Init(&PlatformPermanent, 32); + + if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; + + context Context = {}; + Context.ThreadContext = ThreadContext; + Context.SystemTime_Current = Win32GetSystemTime(); + + gs_const_string Args = ConstString((char*)CmdLineArgs); + if (StringsEqual(Args, ConstString("-headless"))) + { + Log_Message(GlobalLogBuffer, "Running Headless\n"); + Context.Headless = true; + } + + if (!Context.Headless) + { + MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); + Win32UpdateWindowDimension(&MainWindow); + + win32_opengl_window_info OpenGLWindowInfo = {}; + OpenGLWindowInfo.ColorBits = 32; + OpenGLWindowInfo.AlphaBits = 8; + OpenGLWindowInfo.DepthBits = 0; + CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow); + } + + s64 PerformanceCountFrequency = GetPerformanceFrequency(); + s64 LastFrameEnd = GetWallClock(); + r32 TargetSecondsPerFrame = 1 / 30.0f; + r32 LastFrameSecondsElapsed = 0.0f; + + GlobalDebugServices = PushStruct(&PlatformPermanent, debug_services); #if DEBUG - InitDebugServices_DebugMode(GlobalDebugServices, + InitDebugServices_DebugMode(GlobalDebugServices, + PerformanceCountFrequency, + GetWallClock, + Win32GetThreadId, + Context.ThreadContext, + PLATFORM_THREAD_COUNT + 1); +#else + InitDebugServices_OffMode(GlobalDebugServices, PerformanceCountFrequency, GetWallClock, Win32GetThreadId, Context.ThreadContext, PLATFORM_THREAD_COUNT + 1); -#else - InitDebugServices_OffMode(GlobalDebugServices, - PerformanceCountFrequency, - GetWallClock, - Win32GetThreadId, - Context.ThreadContext, - PLATFORM_THREAD_COUNT + 1); #endif - - input_queue InputQueue = InputQueue_Create(&PlatformPermanent, 32); - - Win32WorkQueue_Init(&PlatformPermanent, PLATFORM_THREAD_COUNT); - - // Platform functions - Context.GeneralWorkQueue = &Win32WorkQueue.WorkQueue; - Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle; - Context.PlatformGetSocketHandle = Win32GetSocketHandle; - Context.PlatformGetFontInfo = Win32GetFontInfo; - Context.PlatformDrawFontCodepoint = Win32DrawFontCodepoint; - - Context.ThreadManager = PushStruct(&PlatformPermanent, platform_thread_manager); - *Context.ThreadManager = CreatePlatformThreadManager(Win32CreateThread, Win32KillThread); - - Context.SocketManager = PushStruct(&PlatformPermanent, platform_socket_manager); - *Context.SocketManager = CreatePlatformSocketManager(Win32ConnectSocket, Win32CloseSocket, Win32SocketQueryStatus, Win32SocketPeek, Win32SocketReceive, Win32SocketSend); - - win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName); - if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true, false)) { return -1; } - - Mouse_Init(); - - Win32SocketSystem_Init(&PlatformPermanent); - - Win32SerialArray_Create(&PlatformPermanent); - - render_command_buffer RenderBuffer = {}; - if (!Context.Headless) - { - RenderBuffer = AllocateRenderCommandBuffer(MB(12), &PlatformPermanent, ThreadContext); - } - - addressed_data_buffer_list OutputData = AddressedDataBufferList_Create(ThreadContext); - - Context.InitializeApplication(&Context); - - system_time StartTime = Win32GetSystemTime(); - - Running = true; - Context.WindowIsVisible = true; - while (Running) - { - if (GlobalDebugServices->RecordFrames) + + input_queue InputQueue = InputQueue_Create(&PlatformPermanent, 32); + + Win32WorkQueue_Init(&PlatformPermanent, PLATFORM_THREAD_COUNT); + + // Platform functions + Context.GeneralWorkQueue = &Win32WorkQueue.WorkQueue; + Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle; + Context.PlatformGetSocketHandle = Win32GetSocketHandle; + Context.PlatformGetFontInfo = Win32GetFontInfo; + Context.PlatformDrawFontCodepoint = Win32DrawFontCodepoint; + + Context.ThreadManager = PushStruct(&PlatformPermanent, platform_thread_manager); + *Context.ThreadManager = CreatePlatformThreadManager(Win32CreateThread, Win32KillThread); + + Context.SocketManager = PushStruct(&PlatformPermanent, platform_socket_manager); + *Context.SocketManager = CreatePlatformSocketManager(Win32ConnectSocket, Win32CloseSocket, Win32SocketQueryStatus, Win32SocketPeek, Win32SocketReceive, Win32SocketSend); + + win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName); + if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true, false)) { return -1; } + + Mouse_Init(); + + Win32SocketSystem_Init(&PlatformPermanent); + + Win32SerialArray_Create(&PlatformPermanent); + + render_command_buffer RenderBuffer = {}; + if (!Context.Headless) { - EndDebugFrame(GlobalDebugServices); + RenderBuffer = AllocateRenderCommandBuffer(MB(12), &PlatformPermanent, ThreadContext); } - DEBUG_TRACK_SCOPE(MainLoop); + addressed_data_buffer_list OutputData = AddressedDataBufferList_Create(ThreadContext); + Context.InitializeApplication(&Context); + + system_time StartTime = Win32GetSystemTime(); + + Running = true; + Context.WindowIsVisible = true; + while (Running) { - Context.SystemTime_Last = Context.SystemTime_Current; - Context.SystemTime_Current = Win32GetSystemTime(); - + if (GlobalDebugServices->RecordFrames) + { + EndDebugFrame(GlobalDebugServices); + } + + DEBUG_TRACK_SCOPE(MainLoop); + + { + Context.SystemTime_Last = Context.SystemTime_Current; + Context.SystemTime_Current = Win32GetSystemTime(); + #define PRINT_SYSTEM_TIME 0 #if PRINT_SYSTEM_TIME - gs_string T = PushStringF(Context.ThreadContext.Transient, - 256, - "%d %d %d - %lld\n", - Context.SystemTime_Current.Hour, - Context.SystemTime_Current.Minute, - Context.SystemTime_Current.Second, - Context.SystemTime_Current.NanosSinceEpoch); - - u64 NanosElapsed = Context.SystemTime_Current.NanosSinceEpoch - StartTime.NanosSinceEpoch; - r64 SecondsElapsed = (r64)NanosElapsed * NanosToSeconds; - - Log_Message(GlobalLogBuffer, - "%lld %f Seconds \n", - NanosElapsed, - SecondsElapsed); + gs_string T = PushStringF(Context.ThreadContext.Transient, + 256, + "%d %d %d - %lld\n", + Context.SystemTime_Current.Hour, + Context.SystemTime_Current.Minute, + Context.SystemTime_Current.Second, + Context.SystemTime_Current.NanosSinceEpoch); + + u64 NanosElapsed = Context.SystemTime_Current.NanosSinceEpoch - StartTime.NanosSinceEpoch; + r64 SecondsElapsed = (r64)NanosElapsed * NanosToSeconds; + + Log_Message(GlobalLogBuffer, + "%lld %f Seconds \n", + NanosElapsed, + SecondsElapsed); #endif + } + + ResetInputQueue(&InputQueue); + + ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false, true); + + AddressedDataBufferList_Clear(&OutputData); + + if (!Context.Headless) + { + Mouse_Update(MainWindow, &Context); + MSG Message; + while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE)) + { + DEBUG_TRACK_SCOPE(PeekWindowsMessages); + HandleWindowMessage(Message, &MainWindow, &InputQueue, &Context.Mouse); + } + + Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}}; + RenderBuffer.ViewWidth = MainWindow.Width; + RenderBuffer.ViewHeight = MainWindow.Height; + } + + Context.DeltaTime = LastFrameSecondsElapsed; + Context.TotalTime += (r64)Context.DeltaTime; + + Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); + + Win32_SendOutputData(ThreadContext, OutputData); + + if (!Context.Headless) + { + RenderCommandBuffer(RenderBuffer); + ClearRenderBuffer(&RenderBuffer); + + Mouse_Advance(&Context); + + HDC DeviceContext = GetDC(MainWindow.Handle); + SwapBuffers(DeviceContext); + ReleaseDC(MainWindow.Handle, DeviceContext); + } + + Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext); + + s64 FinishedWorkTime = GetWallClock(); + r32 SecondsElapsed = GetSecondsElapsed(LastFrameEnd, FinishedWorkTime, PerformanceCountFrequency); + + while (SecondsElapsed < TargetSecondsPerFrame) + { + DEBUG_TRACK_SCOPE(UnusedTime); + u32 SleepTime = 1000.0f * (TargetSecondsPerFrame - SecondsElapsed); + Sleep(SleepTime); + SecondsElapsed = GetSecondsElapsed(LastFrameEnd, GetWallClock(), PerformanceCountFrequency); + } + + LastFrameSecondsElapsed = SecondsElapsed; + LastFrameEnd = GetWallClock(); } - ResetInputQueue(&InputQueue); - - ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false, true); - - AddressedDataBufferList_Clear(&OutputData); - - if (!Context.Headless) - { - Mouse_Update(MainWindow, &Context); - MSG Message; - while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE)) - { - DEBUG_TRACK_SCOPE(PeekWindowsMessages); - HandleWindowMessage(Message, &MainWindow, &InputQueue, &Context.Mouse); - } - - Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}}; - RenderBuffer.ViewWidth = MainWindow.Width; - RenderBuffer.ViewHeight = MainWindow.Height; - } - - Context.DeltaTime = LastFrameSecondsElapsed; - Context.TotalTime += (r64)Context.DeltaTime; - - Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData); - + Context.CleanupApplication(Context, &OutputData); Win32_SendOutputData(ThreadContext, OutputData); - - if (!Context.Headless) - { - RenderCommandBuffer(RenderBuffer); - ClearRenderBuffer(&RenderBuffer); - - Mouse_Advance(&Context); - - HDC DeviceContext = GetDC(MainWindow.Handle); - SwapBuffers(DeviceContext); - ReleaseDC(MainWindow.Handle, DeviceContext); - } - Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext); - s64 FinishedWorkTime = GetWallClock(); - r32 SecondsElapsed = GetSecondsElapsed(LastFrameEnd, FinishedWorkTime, PerformanceCountFrequency); + Win32WorkQueue_Cleanup(); + Win32SocketSystem_Cleanup(); - while (SecondsElapsed < TargetSecondsPerFrame) - { - DEBUG_TRACK_SCOPE(UnusedTime); - u32 SleepTime = 1000.0f * (TargetSecondsPerFrame - SecondsElapsed); - Sleep(SleepTime); - SecondsElapsed = GetSecondsElapsed(LastFrameEnd, GetWallClock(), PerformanceCountFrequency); - } - - LastFrameSecondsElapsed = SecondsElapsed; - LastFrameEnd = GetWallClock(); - } - - Context.CleanupApplication(Context, &OutputData); - Win32_SendOutputData(ThreadContext, OutputData); - Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext); - - Win32WorkQueue_Cleanup(); - Win32SocketSystem_Cleanup(); - - return 0; + return 0; } #define WIN32_FOLDHAUS_CPP diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 486a3fc..382690a 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -574,6 +574,8 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } } + + if (BLState->InPhraseReceptionMode) { r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current); @@ -759,7 +761,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } // Dim the leds based on temp data - if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) + if (false && !BLState->DEBUG_IgnoreWeatherDimmingLeds) { for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) { diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index c8d44c1..7784fc0 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -57,7 +57,7 @@ global time_range LedOnTimes[] = { { 00, 00, 06, 30 }, }; global u32 LedOnTimesCount = CArrayLength(LedOnTimes); // do not edit -global b8 DEBUGIgnoreLedOnTimeRange = true; +global b8 DEBUGIgnoreLedOnTimeRange = false; // How long it takes to fade from the default pattern to the // voice activated pattern diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp index 63ad60c..9edceb6 100644 --- a/src/sculpture_gen/gen_blumen_lumen.cpp +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -22,163 +22,163 @@ global log_buffer* GlobalLogBuffer; typedef struct { - v3 CenterStart; - v3 CenterEnd; - r32 Radius; - u32 SegmentsCount; - u32 SubsegmentsCount; - u32 SubsegmentLeds; - - // SACN - u32 SACNUniverseStart; - u32 SACNChannelStart; - - // UART - // Only one of these two values is needed. - // If ChannelsArray != 0, then it will be used, and assumed to - // have SegmentsCount values - // Otherwise, each segment will increment from ChannelStart - u32 ChannelStart; - u32* ChannelsArray; - - char* ComPort; - char* SectionTagValue; - char* FlowerTagValue; + v3 CenterStart; + v3 CenterEnd; + r32 Radius; + u32 SegmentsCount; + u32 SubsegmentsCount; + u32 SubsegmentLeds; + + // SACN + u32 SACNUniverseStart; + u32 SACNChannelStart; + + // UART + // Only one of these two values is needed. + // If ChannelsArray != 0, then it will be used, and assumed to + // have SegmentsCount values + // Otherwise, each segment will increment from ChannelStart + u32 ChannelStart; + u32* ChannelsArray; + + char* ComPort; + char* SectionTagValue; + char* FlowerTagValue; } loop_desc; internal void BuildLoop(gs_string* OutputBuffer, loop_desc Desc) { - r32 SegmentsArc = TauR32 / Desc.SegmentsCount; - r32 SubsegmentsArc = SegmentsArc / Desc.SubsegmentsCount; - - for (u32 i = 0; i < Desc.SegmentsCount; i++) - { - r32 ArcBase = SegmentsArc * i; + r32 SegmentsArc = TauR32 / Desc.SegmentsCount; + r32 SubsegmentsArc = SegmentsArc / Desc.SubsegmentsCount; - u32 Channel = 0; - if (Desc.ChannelsArray != 0) + for (u32 i = 0; i < Desc.SegmentsCount; i++) { - Channel = Desc.ChannelsArray[i]; - } - else - { - Channel = Desc.ChannelStart + i; + r32 ArcBase = SegmentsArc * i; + + u32 Channel = 0; + if (Desc.ChannelsArray != 0) + { + Channel = Desc.ChannelsArray[i]; + } + else + { + Channel = Desc.ChannelStart + i; + } + + WriteLedStripOpen(OutputBuffer, Channel, Desc.ComPort, + Desc.SACNUniverseStart, Desc.SACNChannelStart); + WriteSegmentSequenceOpen(OutputBuffer, Desc.SubsegmentsCount); + + for (u32 j = 0; j < Desc.SubsegmentsCount; j++) + { + r32 Arc = ArcBase + (SubsegmentsArc * j); + v3 Offset = v3{ SinR32(Arc), 0, CosR32(Arc) } * Desc.Radius; + v3 P0 = Desc.CenterStart + Offset; + v3 P1 = Desc.CenterEnd + Offset; + + // Swap directions on the middle strip + if (j%2 != 0) + { + v3 Temp = P0; + P0 = P1; + P1 = Temp; + } + + WriteSegmentSequenceSegment(OutputBuffer, P0, P1, Desc.SubsegmentLeds); + } + + WriteSegmentSequenceClose(OutputBuffer); + WriteSegmentTagsOpen(OutputBuffer, 2); + WriteSegmentTag(OutputBuffer, "section", Desc.SectionTagValue); + WriteSegmentTag(OutputBuffer, "flower", Desc.FlowerTagValue); + WriteSegmentTagsClose(OutputBuffer); + WriteLedStripClose(OutputBuffer); } - WriteLedStripOpen(OutputBuffer, Channel, Desc.ComPort, - Desc.SACNUniverseStart, Desc.SACNChannelStart); - WriteSegmentSequenceOpen(OutputBuffer, Desc.SubsegmentsCount); - - for (u32 j = 0; j < Desc.SubsegmentsCount; j++) - { - r32 Arc = ArcBase + (SubsegmentsArc * j); - v3 Offset = v3{ SinR32(Arc), 0, CosR32(Arc) } * Desc.Radius; - v3 P0 = Desc.CenterStart + Offset; - v3 P1 = Desc.CenterEnd + Offset; - - // Swap directions on the middle strip - if (j%2 != 0) - { - v3 Temp = P0; - P0 = P1; - P1 = Temp; - } - - WriteSegmentSequenceSegment(OutputBuffer, P0, P1, Desc.SubsegmentLeds); - } - - WriteSegmentSequenceClose(OutputBuffer); - WriteSegmentTagsOpen(OutputBuffer, 2); - WriteSegmentTag(OutputBuffer, "section", Desc.SectionTagValue); - WriteSegmentTag(OutputBuffer, "flower", Desc.FlowerTagValue); - WriteSegmentTagsClose(OutputBuffer); - WriteLedStripClose(OutputBuffer); - } - } typedef struct { - v3 Pos; - char* ComPort; - char* FlowerTagValue; - - // SACN - u32 SACNStemInnerStartUniverse; - u32 SACNStemOuterStartUniverse; - u32 SACNFlowerStemStartUniverse; - - // UART - u32* StemChannels; - u32* BloomOuterChannels; - u32* BloomInnerChannels; + v3 Pos; + char* ComPort; + char* FlowerTagValue; + + // SACN + u32 SACNStemInnerStartUniverse; + u32 SACNStemOuterStartUniverse; + u32 SACNFlowerStemStartUniverse; + + // UART + u32* StemChannels; + u32* BloomOuterChannels; + u32* BloomInnerChannels; } flower_desc; internal u32 BuildFlower(gs_string* OutputBuffer, flower_desc Desc) { - + #if 1 - // the bloom stem inner - loop_desc BloomStemInner = {}; - BloomStemInner.CenterStart = v3{0, 1.4f, 0}; - BloomStemInner.CenterEnd = v3{0, .9f, 0}; - BloomStemInner.Radius = .05f; - //BloomStemInner.SegmentsCount = 6; - BloomStemInner.SegmentsCount = 1; - BloomStemInner.SubsegmentsCount = 3; - BloomStemInner.SubsegmentLeds = 35; - BloomStemInner.SACNUniverseStart = Desc.SACNStemInnerStartUniverse; - BloomStemInner.SACNChannelStart = 1; - BloomStemInner.ChannelsArray = Desc.BloomInnerChannels; - BloomStemInner.ComPort = Desc.ComPort; - BloomStemInner.SectionTagValue = "inner_bloom"; - BloomStemInner.FlowerTagValue = Desc.FlowerTagValue; - BuildLoop(OutputBuffer, BloomStemInner); - - // the bloom stem outer - loop_desc BloomStemOuter = {}; - BloomStemOuter.CenterStart = v3{0, .5f, 0}; - BloomStemOuter.CenterEnd = v3{0, .9f, 0}; - BloomStemOuter.Radius = .07f; - //BloomStemOuter.SegmentsCount = 9; - BloomStemOuter.SegmentsCount = 1; - BloomStemOuter.SubsegmentsCount = 3; - BloomStemOuter.SubsegmentLeds = 41; - BloomStemOuter.SACNUniverseStart = Desc.SACNStemOuterStartUniverse; - BloomStemOuter.SACNChannelStart = 1; - BloomStemOuter.ChannelsArray = Desc.BloomOuterChannels; - BloomStemOuter.ComPort = Desc.ComPort; - BloomStemOuter.SectionTagValue = "outer_bloom"; - BloomStemOuter.FlowerTagValue = Desc.FlowerTagValue; - BuildLoop(OutputBuffer, BloomStemOuter); + // the bloom stem inner + loop_desc BloomStemInner = {}; + BloomStemInner.CenterStart = v3{0, 1.4f, 0}; + BloomStemInner.CenterEnd = v3{0, .9f, 0}; + BloomStemInner.Radius = .05f; + //BloomStemInner.SegmentsCount = 6; + BloomStemInner.SegmentsCount = 1; + BloomStemInner.SubsegmentsCount = 3; + BloomStemInner.SubsegmentLeds = 35; + BloomStemInner.SACNUniverseStart = Desc.SACNStemInnerStartUniverse; + BloomStemInner.SACNChannelStart = 1; + BloomStemInner.ChannelsArray = Desc.BloomInnerChannels; + BloomStemInner.ComPort = Desc.ComPort; + BloomStemInner.SectionTagValue = "inner_bloom"; + BloomStemInner.FlowerTagValue = Desc.FlowerTagValue; + BuildLoop(OutputBuffer, BloomStemInner); + + // the bloom stem outer + loop_desc BloomStemOuter = {}; + BloomStemOuter.CenterStart = v3{0, .5f, 0}; + BloomStemOuter.CenterEnd = v3{0, .9f, 0}; + BloomStemOuter.Radius = .07f; + //BloomStemOuter.SegmentsCount = 9; + BloomStemOuter.SegmentsCount = 1; + BloomStemOuter.SubsegmentsCount = 3; + BloomStemOuter.SubsegmentLeds = 41; + BloomStemOuter.SACNUniverseStart = Desc.SACNStemOuterStartUniverse; + BloomStemOuter.SACNChannelStart = 1; + BloomStemOuter.ChannelsArray = Desc.BloomOuterChannels; + BloomStemOuter.ComPort = Desc.ComPort; + BloomStemOuter.SectionTagValue = "outer_bloom"; + BloomStemOuter.FlowerTagValue = Desc.FlowerTagValue; + BuildLoop(OutputBuffer, BloomStemOuter); #endif - + #if 1 - // the flower stem - loop_desc FlowerStem = {}; - FlowerStem.CenterStart = v3{0, -1.5f, 0}; - FlowerStem.CenterEnd = v3{0, .5f, 0}; - FlowerStem.Radius = .05f; - //FlowerStem.SegmentsCount = 6; - FlowerStem.SegmentsCount = 1; - FlowerStem.SubsegmentsCount = 1; - FlowerStem.SubsegmentLeds = 300; - FlowerStem.SACNUniverseStart = Desc.SACNFlowerStemStartUniverse; - FlowerStem.SACNChannelStart = 1; - FlowerStem.ChannelsArray = Desc.StemChannels; - FlowerStem.ComPort = Desc.ComPort; - FlowerStem.SectionTagValue = "stem"; - FlowerStem.FlowerTagValue = Desc.FlowerTagValue; - BuildLoop(OutputBuffer, FlowerStem); + // the flower stem + loop_desc FlowerStem = {}; + FlowerStem.CenterStart = v3{0, .5f, 0}; + FlowerStem.CenterEnd = v3{0, -1.5f, 0}; + FlowerStem.Radius = .05f; + //FlowerStem.SegmentsCount = 6; + FlowerStem.SegmentsCount = 1; + FlowerStem.SubsegmentsCount = 1; + FlowerStem.SubsegmentLeds = 300; + FlowerStem.SACNUniverseStart = Desc.SACNFlowerStemStartUniverse; + FlowerStem.SACNChannelStart = 1; + FlowerStem.ChannelsArray = Desc.StemChannels; + FlowerStem.ComPort = Desc.ComPort; + FlowerStem.SectionTagValue = "stem"; + FlowerStem.FlowerTagValue = Desc.FlowerTagValue; + BuildLoop(OutputBuffer, FlowerStem); #endif - - u32 StripsCount = BloomStemInner.SegmentsCount; - StripsCount += BloomStemOuter.SegmentsCount; - StripsCount += FlowerStem.SegmentsCount; - - return StripsCount; + + u32 StripsCount = BloomStemInner.SegmentsCount; + StripsCount += BloomStemOuter.SegmentsCount; + StripsCount += FlowerStem.SegmentsCount; + + return StripsCount; } // Just for brevity, no real function provided @@ -186,116 +186,116 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc) internal u8 FlowerStripToChannel(u8 Flower, u8 Channel) { - Assert(Flower < 3); - Assert(Channel < 8); - - u8 Result = 0; - Result |= (Flower & 0x03) << 3; - Result |= (Channel & 0x07); - - return Result; + Assert(Flower < 3); + Assert(Channel < 8); + + u8 Result = 0; + Result |= (Flower & 0x03) << 3; + Result |= (Channel & 0x07); + + return Result; } int main(int ArgCount, char** Args) { - gs_thread_context Ctx = Win32CreateThreadContext(); - GlobalLogBuffer = PushStruct(Ctx.Transient, log_buffer); - *GlobalLogBuffer = Log_Init(Ctx.Transient, 32); - - gs_string OutputBuffer0 = PushString(Ctx.Transient, MB(4)); - gs_string OutputBuffer1 = PushString(Ctx.Transient, MB(4)); - gs_string OutputBuffer2 = PushString(Ctx.Transient, MB(4)); - - u32 StripCount = 3; // used to be 21 + gs_thread_context Ctx = Win32CreateThreadContext(); + GlobalLogBuffer = PushStruct(Ctx.Transient, log_buffer); + *GlobalLogBuffer = Log_Init(Ctx.Transient, 32); + + gs_string OutputBuffer0 = PushString(Ctx.Transient, MB(4)); + gs_string OutputBuffer1 = PushString(Ctx.Transient, MB(4)); + gs_string OutputBuffer2 = PushString(Ctx.Transient, MB(4)); + + u32 StripCount = 3; // used to be 21 #if 0 - WriteAssemblyUARTOpen(&OutputBuffer0, - "Blumen Lumen - Silver Spring - 00", - 100, - v3{-1, 0, 0}, - StripCount, - ""); - WriteAssemblyUARTOpen(&OutputBuffer1, - "Blumen Lumen - Silver Spring - 01", - 100, - v3{0, 0, 0}, - StripCount, - ""); - WriteAssemblyUARTOpen(&OutputBuffer2, - "Blumen Lumen - Silver Spring - 02", - 100, - v3{1, 0, 0}, - StripCount, - ""); + WriteAssemblyUARTOpen(&OutputBuffer0, + "Blumen Lumen - Silver Spring - 00", + 100, + v3{-1, 0, 0}, + StripCount, + ""); + WriteAssemblyUARTOpen(&OutputBuffer1, + "Blumen Lumen - Silver Spring - 01", + 100, + v3{0, 0, 0}, + StripCount, + ""); + WriteAssemblyUARTOpen(&OutputBuffer2, + "Blumen Lumen - Silver Spring - 02", + 100, + v3{1, 0, 0}, + StripCount, + ""); #else - WriteAssemblySACNOpen(&OutputBuffer0, - "Blumen Lumen - Silver Spring - 00", - 100, - v3{-1, 0, 0}, - StripCount); - WriteAssemblySACNOpen(&OutputBuffer1, - "Blumen Lumen - Silver Spring - 01", - 100, - v3{0, 0, 0}, - StripCount); - WriteAssemblySACNOpen(&OutputBuffer2, - "Blumen Lumen - Silver Spring - 02", - 100, - v3{1, 0, 0}, - StripCount); + WriteAssemblySACNOpen(&OutputBuffer0, + "Blumen Lumen - Silver Spring - 00", + 100, + v3{-1, 0, 0}, + StripCount); + WriteAssemblySACNOpen(&OutputBuffer1, + "Blumen Lumen - Silver Spring - 01", + 100, + v3{0, 0, 0}, + StripCount); + WriteAssemblySACNOpen(&OutputBuffer2, + "Blumen Lumen - Silver Spring - 02", + 100, + v3{1, 0, 0}, + StripCount); #endif - - u32 StripCountOut = 0; - - u32 StemChannels[] = { FSC(2, 1), FSC(2, 2), FSC(2, 3), FSC(2, 4), FSC(2, 5), FSC(2, 6) }; - u32 BloomOuterChannels[] = { FSC(1, 0), FSC(1, 1), FSC(1, 2), FSC(1, 3), FSC(1, 4), FSC(1, 5), FSC(1, 6), FSC(1, 7), FSC(2, 0) }; - u32 BloomInnerChannels[] = { FSC(0, 0), FSC(0, 1), FSC(0, 2), FSC(0, 3), FSC(0, 4), FSC(0, 5) }; - flower_desc F0 = {}; - F0.Pos = v3{0, 0, 0}; - F0.ComPort = "\\\\.\\COM11"; - F0.FlowerTagValue = "left"; - F0.SACNStemInnerStartUniverse = 4; - F0.SACNStemOuterStartUniverse = 3; - F0.SACNFlowerStemStartUniverse = 1; - F0.StemChannels = StemChannels; - F0.BloomOuterChannels = BloomOuterChannels; - F0.BloomInnerChannels = BloomInnerChannels; - StripCountOut += BuildFlower(&OutputBuffer0, F0); - - flower_desc F1 = {}; - F1.Pos = v3{0, 0, 0}; - F1.ComPort = "\\\\.\\COM12"; - F1.FlowerTagValue = "center"; - F1.SACNStemInnerStartUniverse = 9; - F1.SACNStemOuterStartUniverse = 8; - F1.SACNFlowerStemStartUniverse = 6; - F1.StemChannels = StemChannels; - F1.BloomInnerChannels = BloomInnerChannels; - F1.BloomOuterChannels = BloomOuterChannels; - StripCountOut += BuildFlower(&OutputBuffer1, F1); - - flower_desc F2 = {}; - F2.Pos = v3{0, 0, 0}; - F2.ComPort = "\\\\.\\COM6"; - F2.FlowerTagValue = "right"; - F2.SACNStemInnerStartUniverse = 14; - F2.SACNStemOuterStartUniverse = 13; - F2.SACNFlowerStemStartUniverse = 11; - F2.StemChannels = StemChannels; - F2.BloomInnerChannels = BloomInnerChannels; - F2.BloomOuterChannels = BloomOuterChannels; - StripCountOut += BuildFlower(&OutputBuffer2, F2); - - WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_one.fold"), StringToData(OutputBuffer0)); - WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_two.fold"), StringToData(OutputBuffer1)); - WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_three.fold"), StringToData(OutputBuffer2)); - - //printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str); - //printf("%d\n", StripCount); - - - - - return 0; + + u32 StripCountOut = 0; + + u32 StemChannels[] = { FSC(2, 1), FSC(2, 2), FSC(2, 3), FSC(2, 4), FSC(2, 5), FSC(2, 6) }; + u32 BloomOuterChannels[] = { FSC(1, 0), FSC(1, 1), FSC(1, 2), FSC(1, 3), FSC(1, 4), FSC(1, 5), FSC(1, 6), FSC(1, 7), FSC(2, 0) }; + u32 BloomInnerChannels[] = { FSC(0, 0), FSC(0, 1), FSC(0, 2), FSC(0, 3), FSC(0, 4), FSC(0, 5) }; + flower_desc F0 = {}; + F0.Pos = v3{0, 0, 0}; + F0.ComPort = "\\\\.\\COM11"; + F0.FlowerTagValue = "left"; + F0.SACNStemInnerStartUniverse = 4; + F0.SACNStemOuterStartUniverse = 3; + F0.SACNFlowerStemStartUniverse = 1; + F0.StemChannels = StemChannels; + F0.BloomOuterChannels = BloomOuterChannels; + F0.BloomInnerChannels = BloomInnerChannels; + StripCountOut += BuildFlower(&OutputBuffer0, F0); + + flower_desc F1 = {}; + F1.Pos = v3{0, 0, 0}; + F1.ComPort = "\\\\.\\COM12"; + F1.FlowerTagValue = "center"; + F1.SACNStemInnerStartUniverse = 9; + F1.SACNStemOuterStartUniverse = 8; + F1.SACNFlowerStemStartUniverse = 6; + F1.StemChannels = StemChannels; + F1.BloomInnerChannels = BloomInnerChannels; + F1.BloomOuterChannels = BloomOuterChannels; + StripCountOut += BuildFlower(&OutputBuffer1, F1); + + flower_desc F2 = {}; + F2.Pos = v3{0, 0, 0}; + F2.ComPort = "\\\\.\\COM6"; + F2.FlowerTagValue = "right"; + F2.SACNStemInnerStartUniverse = 14; + F2.SACNStemOuterStartUniverse = 13; + F2.SACNFlowerStemStartUniverse = 11; + F2.StemChannels = StemChannels; + F2.BloomInnerChannels = BloomInnerChannels; + F2.BloomOuterChannels = BloomOuterChannels; + StripCountOut += BuildFlower(&OutputBuffer2, F2); + + WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_one.fold"), StringToData(OutputBuffer0)); + WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_two.fold"), StringToData(OutputBuffer1)); + WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_three.fold"), StringToData(OutputBuffer2)); + + //printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str); + //printf("%d\n", StripCount); + + + + + return 0; } From f0f0a48acb7240fd75b7216609062aefdddfd930 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 25 Sep 2021 17:08:01 -0500 Subject: [PATCH 102/151] SACN strips now wrap around to the next universe --- src/app/engine/sacn/foldhaus_sacn.h | 446 +++++++++--------- .../ss_blumen_lumen/blumen_lumen_settings.h | 16 +- 2 files changed, 239 insertions(+), 223 deletions(-) diff --git a/src/app/engine/sacn/foldhaus_sacn.h b/src/app/engine/sacn/foldhaus_sacn.h index 6004361..7c03209 100644 --- a/src/app/engine/sacn/foldhaus_sacn.h +++ b/src/app/engine/sacn/foldhaus_sacn.h @@ -81,14 +81,14 @@ const u8 VHD_D_FLAG = 0x10; #define CID_Bytes 16 struct cid { - u8 Bytes[CID_Bytes]; + u8 Bytes[CID_Bytes]; }; struct streaming_acn { - platform_socket_handle SendSocket; - cid CID; - s32 SequenceIterator; + platform_socket_handle SendSocket; + cid CID; + s32 SequenceIterator; }; /////////////////////////////////////////////// @@ -100,100 +100,100 @@ struct streaming_acn internal void SetStreamHeaderSequence_ (u8* Buffer, u8 Sequence, b32 Draft) { - DEBUG_TRACK_FUNCTION; - PackB1(Buffer + SEQ_NUM_ADDR, Sequence); + DEBUG_TRACK_FUNCTION; + PackB1(Buffer + SEQ_NUM_ADDR, Sequence); } internal void VHD_PackFlags_(u8* Buffer, b32 InheritVec, b32 InheritHead, b32 InheritData) { - u8* Cursor = Buffer; - u8 NewByte = UpackB1(Cursor) & 0x8f; - - if (!InheritVec) { NewByte |= VHD_V_FLAG; } - if (!InheritHead) { NewByte |= VHD_H_FLAG; } - if (!InheritData) { NewByte |= VHD_D_FLAG; } - - PackB1(Cursor, NewByte); + u8* Cursor = Buffer; + u8 NewByte = UpackB1(Cursor) & 0x8f; + + if (!InheritVec) { NewByte |= VHD_V_FLAG; } + if (!InheritHead) { NewByte |= VHD_H_FLAG; } + if (!InheritData) { NewByte |= VHD_D_FLAG; } + + PackB1(Cursor, NewByte); } internal u8* VHD_PackLength_(u8* Buffer, u32 Length, b32 IncludeLength) { - u8* Cursor = Buffer; - u32 AdjustedLength = Length; - if (IncludeLength) + u8* Cursor = Buffer; + u32 AdjustedLength = Length; + if (IncludeLength) + { + if (Length + 1 > VHD_MAXMINLENGTH) { - if (Length + 1 > VHD_MAXMINLENGTH) - { - AdjustedLength += 2; - } - else - { - AdjustedLength += 1; - } - } - - // Mask out the length bits to keep flags intact - u8 NewByte = UpackB1(Cursor) & 0x70; - if (AdjustedLength > VHD_MAXMINLENGTH) - { - NewByte |= VHD_L_FLAG; - } - - u8 PackBuffer[4]; - PackB4(PackBuffer, AdjustedLength); - if (AdjustedLength <= VHD_MAXMINLENGTH) - { - NewByte |= (PackBuffer[2] & 0x0f); - Cursor = PackB1(Cursor, NewByte); - Cursor = PackB1(Cursor, PackBuffer[3]); + AdjustedLength += 2; } else { - NewByte |= (PackBuffer[1] & 0x0f); - Cursor = PackB1(Cursor, PackBuffer[2]); - Cursor = PackB1(Cursor, PackBuffer[3]); + AdjustedLength += 1; } - - return Cursor; + } + + // Mask out the length bits to keep flags intact + u8 NewByte = UpackB1(Cursor) & 0x70; + if (AdjustedLength > VHD_MAXMINLENGTH) + { + NewByte |= VHD_L_FLAG; + } + + u8 PackBuffer[4]; + PackB4(PackBuffer, AdjustedLength); + if (AdjustedLength <= VHD_MAXMINLENGTH) + { + NewByte |= (PackBuffer[2] & 0x0f); + Cursor = PackB1(Cursor, NewByte); + Cursor = PackB1(Cursor, PackBuffer[3]); + } + else + { + NewByte |= (PackBuffer[1] & 0x0f); + Cursor = PackB1(Cursor, PackBuffer[2]); + Cursor = PackB1(Cursor, PackBuffer[3]); + } + + return Cursor; } internal cid gs_stringToCID_ (const char* gs_string) { - cid Result = {}; + cid Result = {}; + + const char* Src = gs_string; + u8* Dest = &Result.Bytes[0]; + b32 FirstNibble = true; + + while(*Src && (Dest - &Result.Bytes[0] < CID_Bytes)) + { + u8 Offset = 0; + if ((*Src >= 0x30) && (*Src <= 0x39)){ Offset = 0x30; } + else if ((*Src >= 0x41) && (*Src <= 0x46)) { Offset = 0x37; } + else if ((*Src >= 0x61) && (*Src <= 0x66)) { Offset = 0x66; } - const char* Src = gs_string; - u8* Dest = &Result.Bytes[0]; - b32 FirstNibble = true; - - while(*Src && (Dest - &Result.Bytes[0] < CID_Bytes)) + if (Offset != 0) { - u8 Offset = 0; - if ((*Src >= 0x30) && (*Src <= 0x39)){ Offset = 0x30; } - else if ((*Src >= 0x41) && (*Src <= 0x46)) { Offset = 0x37; } - else if ((*Src >= 0x61) && (*Src <= 0x66)) { Offset = 0x66; } - - if (Offset != 0) - { - if (FirstNibble) - { - *Dest = (u8)(*Src - Offset); - *Dest <<= 4; - FirstNibble = false; - } - else - { - *Dest |= (*Src - Offset); - Dest++; - FirstNibble = true; - } - } - Src++; + if (FirstNibble) + { + *Dest = (u8)(*Src - Offset); + *Dest <<= 4; + FirstNibble = false; + } + else + { + *Dest |= (*Src - Offset); + Dest++; + FirstNibble = true; + } } - - return Result; + Src++; + } + + return Result; } internal void @@ -208,85 +208,85 @@ InitStreamHeader (u8* Buffer, s32 BufferSize, cid CID ) { - // TODO(pjs): Replace packing with gs_memory_cursor - - u8* Cursor = Buffer; - - // Preamble Size - Cursor = PackB2(Cursor, RLP_PREAMBLE_SIZE); - Cursor = PackB2(Cursor, RLP_POSTAMBLE_SIZE); - - CopyMemoryTo(ACN_IDENTIFIER, Cursor, ACN_IDENTIFIER_SIZE); - Cursor += ACN_IDENTIFIER_SIZE; - - // TODO(Peter): If you never use this anywhere else, go back and remove the parameters - VHD_PackFlags_(Cursor, false, false, false); - Cursor = VHD_PackLength_(Cursor, - STREAM_HEADER_SIZE - RLP_PREAMBLE_SIZE + SlotCount, - false); - - // root vector - Cursor = PackB4(Cursor, ROOT_VECTOR); - - // CID Pack - for (s32 i = 0; i < CID_Bytes; i++) - { - *Cursor++ = CID.Bytes[i]; - } - - VHD_PackFlags_(Cursor, false, false, false); - Cursor = VHD_PackLength_(Cursor, - STREAM_HEADER_SIZE - FRAMING_FLAGS_AND_LENGTH_ADDR + SlotCount, - false); - - // framing vector - Cursor = PackB4(Cursor, FRAMING_VECTOR); - - // framing source name - // :Check - CopyMemoryTo(SourceName, (char*)Cursor, SOURCE_NAME_SIZE); - Cursor[SOURCE_NAME_SIZE - 1] = '\0'; - Cursor += SOURCE_NAME_SIZE; - - // priority - Cursor = PackB1(Cursor, Priority); - - // reserved - Cursor = PackB2(Cursor, Reserved); - - // Sequence # is always set to 0/NONE at the beginning, but it is incremented when sending data - Cursor = PackB1(Cursor, 0); - - // Options - Cursor = PackB1(Cursor, Options); - - // Universe - Cursor = PackB2(Cursor, Universe); - - VHD_PackFlags_(Cursor, false, false, false); - Cursor = VHD_PackLength_(Cursor, - STREAM_HEADER_SIZE - DMP_FLAGS_AND_LENGTH_ADDR + SlotCount, - false); - - // DMP Vector - Cursor = PackB1(Cursor, DMP_VECTOR); - - // DMP Address and data type - Cursor = PackB1(Cursor, ADDRESS_AND_DATA_FORMAT); - - // DMP first property address - Cursor = PackB2(Cursor, 0); - - // DMP Address Increment - Cursor = PackB2(Cursor, ADDRESS_INC); - - // Property Value Count -- Includes one byte for start code - Cursor = PackB2(Cursor, SlotCount + 1); - - Cursor = PackB1(Cursor, StartCode); - - Assert(Cursor - Buffer == STREAM_HEADER_SIZE); - + // TODO(pjs): Replace packing with gs_memory_cursor + + u8* Cursor = Buffer; + + // Preamble Size + Cursor = PackB2(Cursor, RLP_PREAMBLE_SIZE); + Cursor = PackB2(Cursor, RLP_POSTAMBLE_SIZE); + + CopyMemoryTo(ACN_IDENTIFIER, Cursor, ACN_IDENTIFIER_SIZE); + Cursor += ACN_IDENTIFIER_SIZE; + + // TODO(Peter): If you never use this anywhere else, go back and remove the parameters + VHD_PackFlags_(Cursor, false, false, false); + Cursor = VHD_PackLength_(Cursor, + STREAM_HEADER_SIZE - RLP_PREAMBLE_SIZE + SlotCount, + false); + + // root vector + Cursor = PackB4(Cursor, ROOT_VECTOR); + + // CID Pack + for (s32 i = 0; i < CID_Bytes; i++) + { + *Cursor++ = CID.Bytes[i]; + } + + VHD_PackFlags_(Cursor, false, false, false); + Cursor = VHD_PackLength_(Cursor, + STREAM_HEADER_SIZE - FRAMING_FLAGS_AND_LENGTH_ADDR + SlotCount, + false); + + // framing vector + Cursor = PackB4(Cursor, FRAMING_VECTOR); + + // framing source name + // :Check + CopyMemoryTo(SourceName, (char*)Cursor, SOURCE_NAME_SIZE); + Cursor[SOURCE_NAME_SIZE - 1] = '\0'; + Cursor += SOURCE_NAME_SIZE; + + // priority + Cursor = PackB1(Cursor, Priority); + + // reserved + Cursor = PackB2(Cursor, Reserved); + + // Sequence # is always set to 0/NONE at the beginning, but it is incremented when sending data + Cursor = PackB1(Cursor, 0); + + // Options + Cursor = PackB1(Cursor, Options); + + // Universe + Cursor = PackB2(Cursor, Universe); + + VHD_PackFlags_(Cursor, false, false, false); + Cursor = VHD_PackLength_(Cursor, + STREAM_HEADER_SIZE - DMP_FLAGS_AND_LENGTH_ADDR + SlotCount, + false); + + // DMP Vector + Cursor = PackB1(Cursor, DMP_VECTOR); + + // DMP Address and data type + Cursor = PackB1(Cursor, ADDRESS_AND_DATA_FORMAT); + + // DMP first property address + Cursor = PackB2(Cursor, 0); + + // DMP Address Increment + Cursor = PackB2(Cursor, ADDRESS_INC); + + // Property Value Count -- Includes one byte for start code + Cursor = PackB2(Cursor, SlotCount + 1); + + Cursor = PackB1(Cursor, StartCode); + + Assert(Cursor - Buffer == STREAM_HEADER_SIZE); + } // @@ -296,13 +296,13 @@ InitStreamHeader (u8* Buffer, s32 BufferSize, internal streaming_acn SACN_Initialize (context Context) { - streaming_acn SACN = {}; - - s32 Multicast_TimeToLive = 20; - SACN.SendSocket = Context.PlatformGetSocketHandle(Multicast_TimeToLive); - SACN.CID = gs_stringToCID_ ("{67F9D986-544E-4abb-8986-D5F79382586C}"); - - return SACN; + streaming_acn SACN = {}; + + s32 Multicast_TimeToLive = 20; + SACN.SendSocket = Context.PlatformGetSocketHandle(Multicast_TimeToLive); + SACN.CID = gs_stringToCID_ ("{67F9D986-544E-4abb-8986-D5F79382586C}"); + + return SACN; } internal void @@ -313,82 +313,98 @@ SACN_Cleanup(streaming_acn* SACN, context Context) internal void SACN_UpdateSequence (streaming_acn* SACN) { - // Never use 0 after the first one - if (++SACN->SequenceIterator == 0) - { - ++SACN->SequenceIterator; - } + // Never use 0 after the first one + if (++SACN->SequenceIterator == 0) + { + ++SACN->SequenceIterator; + } } internal void SACN_PrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReservedForHeader, streaming_acn SACN) { - Assert(SizeReservedForHeader == STREAM_HEADER_SIZE); - Assert(Buffer && BufferSize > 0); - - s32 Priority = 0; - InitStreamHeader(Buffer, BufferSize, STREAM_BODY_SIZE, STARTCODE_DMX, Universe, Priority, 0, 0, "Lumenarium", SACN.CID); - SetStreamHeaderSequence_(Buffer, SACN.SequenceIterator, false); + Assert(SizeReservedForHeader == STREAM_HEADER_SIZE); + Assert(Buffer && BufferSize > 0); + + s32 Priority = 0; + InitStreamHeader(Buffer, BufferSize, STREAM_BODY_SIZE, STARTCODE_DMX, Universe, Priority, 0, 0, "Lumenarium", SACN.CID); + SetStreamHeaderSequence_(Buffer, SACN.SequenceIterator, false); } internal u32 SACN_GetUniverseSendAddress(s32 Universe) { - u8 MulticastAddressBuffer[4] = {}; - MulticastAddressBuffer[0] = 239; - MulticastAddressBuffer[1] = 255; - MulticastAddressBuffer[2] = (u8)((Universe & 0xff00) >> 8); // high bit - MulticastAddressBuffer[3] = (u8)((Universe & 0x00ff)); // low bit - - u32 V4Address = (u32)UpackB4(MulticastAddressBuffer); - return V4Address; + u8 MulticastAddressBuffer[4] = {}; + MulticastAddressBuffer[0] = 239; + MulticastAddressBuffer[1] = 255; + MulticastAddressBuffer[2] = (u8)((Universe & 0xff00) >> 8); // high bit + MulticastAddressBuffer[3] = (u8)((Universe & 0x00ff)); // low bit + + u32 V4Address = (u32)UpackB4(MulticastAddressBuffer); + return V4Address; } -internal void -SACN_FillBufferWithLeds(u8* BufferStart, u32 BufferSize, v2_strip Strip, led_buffer LedBuffer) +internal u64 +SACN_FillBufferWithLeds(u8* BufferStart, u32 BufferSize, v2_strip Strip, u64 LedsPlaced, led_buffer LedBuffer) { - u8* DestChannel = BufferStart; - for (u32 i = 0; i < Strip.LedCount; i++) - { - u32 LedIndex = Strip.LedLUT[i]; - pixel Color = LedBuffer.Colors[LedIndex]; - - DestChannel[0] = Color.R; - DestChannel[1] = Color.G; - DestChannel[2] = Color.B; - DestChannel += 3; - } + u8* DestChannel = BufferStart; + u64 FirstLed = LedsPlaced; + u64 LedsToAdd = Min(Strip.LedCount - LedsPlaced, STREAM_BODY_SIZE / 3); + u64 OnePastLastLed = FirstLed + LedsToAdd; + for (u32 i = FirstLed; i < OnePastLastLed; i++) + { + u32 LedIndex = Strip.LedLUT[i]; + pixel Color = LedBuffer.Colors[LedIndex]; + + DestChannel[0] = Color.R; + DestChannel[1] = Color.G; + DestChannel[2] = Color.B; + DestChannel += 3; + } + return LedsToAdd; } internal void SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem) { - SACN_UpdateSequence(SACN); + SACN_UpdateSequence(SACN); + + // TODO(pjs): 512 is a magic number - make it a constant? + s32 BufferHeaderSize = STREAM_HEADER_SIZE; + s32 BufferBodySize = STREAM_BODY_SIZE; + s32 BufferSize = BufferHeaderSize + BufferBodySize; + + for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++) + { + assembly Assembly = Assemblies.Values[AssemblyIdx]; + led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); - // TODO(pjs): 512 is a magic number - make it a constant? - s32 BufferHeaderSize = STREAM_HEADER_SIZE; - s32 BufferBodySize = 512; - s32 BufferSize = BufferHeaderSize + BufferBodySize; - - for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++) + for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++) { - assembly Assembly = Assemblies.Values[AssemblyIdx]; - led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); + v2_strip StripAt = Assembly.Strips[StripIdx]; + + // NOTE(PS): This isn't actually invalid, we just haven't needed to implement + // something more complex than only allowing strips to start at the first + // channel of a universe + Assert(StripAt.SACNAddr.StartChannel == 1); + + u32 UniverseAt = StripAt.SACNAddr.StartUniverse; + u64 LedsPlaced = 0; + while (LedsPlaced < StripAt.LedCount) + { + u32 V4SendAddress = SACN_GetUniverseSendAddress(UniverseAt); + u32 SendPort = DEFAULT_STREAMING_ACN_PORT; - for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++) - { - v2_strip StripAt = Assembly.Strips[StripIdx]; - - u32 V4SendAddress = SACN_GetUniverseSendAddress(StripAt.SACNAddr.StartUniverse); - u32 SendPort = DEFAULT_STREAMING_ACN_PORT; - - addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize); - AddressedDataBuffer_SetNetworkAddress(Data, SACN->SendSocket, V4SendAddress, SendPort); - - SACN_PrepareBufferHeader(StripAt.SACNAddr.StartUniverse, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN); - SACN_FillBufferWithLeds(Data->Memory + BufferHeaderSize, BufferBodySize, StripAt, *LedBuffer); - } + addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize); + AddressedDataBuffer_SetNetworkAddress(Data, SACN->SendSocket, V4SendAddress, SendPort); + + SACN_PrepareBufferHeader(UniverseAt, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN); + LedsPlaced += SACN_FillBufferWithLeds(Data->Memory + BufferHeaderSize, BufferBodySize, StripAt, LedsPlaced, *LedBuffer); + + UniverseAt += 1; + } } + } } #define SACN_H diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 7784fc0..3c046ad 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -36,9 +36,9 @@ gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_r // NOTE: There is no need to modify the MotorOpenTimesCount variable - // it is a compile time constant that gets calculated automatically global time_range MotorOpenTimes[] = { - { 8, 00, 12, 00 }, // 8:00am to 12:00pm - { 12, 30, 18, 00 }, // 12:30pm to 06:00pm - { 18, 30, 22, 00 }, // 6:30pm to 10:00pm + { 8, 00, 12, 00 }, // 8:00am to 12:00pm + { 12, 30, 18, 00 }, // 12:30pm to 06:00pm + { 18, 30, 22, 00 }, // 6:30pm to 10:00pm }; global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit @@ -50,11 +50,11 @@ global r32 MotorResendStatePeriod = 90.0f; // seconds // The times of day when the leds should be on // Search for @TimeFormat to find documentation global time_range LedOnTimes[] = { - { 14, 43, 14, 44 }, - { 14, 45, 14, 46 }, - - { 17, 00, 23, 59 }, - { 00, 00, 06, 30 }, + { 14, 43, 14, 44 }, + { 14, 45, 14, 46 }, + + { 17, 00, 23, 59 }, + { 00, 00, 06, 30 }, }; global u32 LedOnTimesCount = CArrayLength(LedOnTimes); // do not edit global b8 DEBUGIgnoreLedOnTimeRange = false; From 0a1ad2d4ac25aa93c11573c981e2469607b7fab4 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sat, 25 Sep 2021 18:10:33 -0400 Subject: [PATCH 103/151] Animations and settings updates --- .../blumen_animations/ambient_patterns/rainbow_0.foldanim | 4 ++-- src/app/ss_blumen_lumen/blumen_lumen_settings.h | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim index ee12af4..edb16ec 100644 --- a/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim +++ b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim @@ -4,7 +4,7 @@ layers_count: 3; blocks_count: 1; playable_range:{ min: 0; - max: 3600; + max: 36000; }; layers:{ layer:{ @@ -24,7 +24,7 @@ blocks:{ block:{ frame_range:{ min: 0; - max: 3600; + max: 36000; }; layer_index: 1; animation_name: "Pattern_Rainbow"; diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h index 3c046ad..59d4767 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen_settings.h +++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h @@ -50,11 +50,8 @@ global r32 MotorResendStatePeriod = 90.0f; // seconds // The times of day when the leds should be on // Search for @TimeFormat to find documentation global time_range LedOnTimes[] = { - { 14, 43, 14, 44 }, - { 14, 45, 14, 46 }, - - { 17, 00, 23, 59 }, - { 00, 00, 06, 30 }, + { 17, 00, 23, 59 }, + { 00, 00, 06, 30 }, }; global u32 LedOnTimesCount = CArrayLength(LedOnTimes); // do not edit global b8 DEBUGIgnoreLedOnTimeRange = false; From 732356739bd6472f4235961e6760a84ecce24f06 Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 26 Sep 2021 16:24:57 -0500 Subject: [PATCH 104/151] trying to log dimming --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 1918 +++++++++++----------- 1 file changed, 964 insertions(+), 954 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 382690a..5b0ea40 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -8,72 +8,72 @@ internal animation_handle_array LoadAllAnimationsInDir(gs_const_string Path, blumen_lumen_state* BLState, app_state* State, context Context) { - animation_handle_array Result = {}; - - gs_thread_context Ctx = Context.ThreadContext; - gs_file_info_array FilesInDir = EnumerateDirectory(Ctx.FileHandler, State->Transient, Path, 0); - - Result.Count = FilesInDir.Count; - Result.Handles = PushArray(&State->Permanent, animation_handle, Result.Count); - - for (u32 i = 0; i < FilesInDir.Count; i++) - { - gs_file_info File = FilesInDir.Values[i]; - Result.Handles[i] = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - File.Path); - } - - return Result; + animation_handle_array Result = {}; + + gs_thread_context Ctx = Context.ThreadContext; + gs_file_info_array FilesInDir = EnumerateDirectory(Ctx.FileHandler, State->Transient, Path, 0); + + Result.Count = FilesInDir.Count; + Result.Handles = PushArray(&State->Permanent, animation_handle, Result.Count); + + for (u32 i = 0; i < FilesInDir.Count; i++) + { + gs_file_info File = FilesInDir.Values[i]; + Result.Handles[i] = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + File.Path); + } + + return Result; } internal s32 GetCCIndex (assembly Assembly, blumen_lumen_state* BLState) { - s32 Result = 0; - - u64 AssemblyNameHash = HashDJB2ToU32(StringExpand(Assembly.Name)); - for (u32 i = 0; i < BLState->AssemblyNameToClearCoreMapCount; i++) + s32 Result = 0; + + u64 AssemblyNameHash = HashDJB2ToU32(StringExpand(Assembly.Name)); + for (u32 i = 0; i < BLState->AssemblyNameToClearCoreMapCount; i++) + { + if (AssemblyNameHash == BLState->AssemblyNameToClearCore_Names[i]) { - if (AssemblyNameHash == BLState->AssemblyNameToClearCore_Names[i]) - { - Result = (s32)i; - break; - } + Result = (s32)i; + break; } - - return Result; + } + + return Result; } internal void DEBUG_AppendText(gs_string Str, gs_thread_context Ctx) { - gs_const_string DebugPath = ConstString("data/debug_motor_changes.txt"); - gs_file DebugFile = ReadEntireFile(Ctx.FileHandler, - DebugPath); - gs_string NewString = PushString(Ctx.Transient, DebugFile.Size + Str.Size + 16); - if (DebugFile.Size > 0) - { - PrintF(&NewString, "%.*s\nENTRY:\n", DebugFile.Size, (char*)DebugFile.Memory); - } - AppendPrintF(&NewString, "%S\n", Str.ConstString); - NullTerminate(&NewString); - - if (!WriteEntireFile(Ctx.FileHandler, DebugPath, StringToData(NewString))) - { - InvalidCodePath; - } + gs_const_string DebugPath = ConstString("data/debug_motor_changes.txt"); + gs_file DebugFile = ReadEntireFile(Ctx.FileHandler, + DebugPath); + gs_string NewString = PushString(Ctx.Transient, DebugFile.Size + Str.Size + 16); + if (DebugFile.Size > 0) + { + PrintF(&NewString, "%.*s\nENTRY:\n", DebugFile.Size, (char*)DebugFile.Memory); + } + AppendPrintF(&NewString, "%S\n", Str.ConstString); + NullTerminate(&NewString); + + if (!WriteEntireFile(Ctx.FileHandler, DebugPath, StringToData(NewString))) + { + InvalidCodePath; + } } internal void DEBUG_SentMotorCommand(motor_packet Packet, gs_thread_context Ctx) { - Log_Message(GlobalLogBuffer, - "Motor Command Sent\nRequested Positions: %d %d %d\n", - Packet.FlowerPositions[0], - Packet.FlowerPositions[1], - Packet.FlowerPositions[2]); + Log_Message(GlobalLogBuffer, + "Motor Command Sent\nRequested Positions: %d %d %d\n", + Packet.FlowerPositions[0], + Packet.FlowerPositions[1], + Packet.FlowerPositions[2]); } internal void @@ -81,1024 +81,1034 @@ DEBUG_ReceivedMotorPositions(blumen_lumen_state* BLState, motor_status_packet NewPos, gs_thread_context Ctx) { - motor_packet LastPos = BLState->LastKnownMotorState; - bool PosChanged = (LastPos.FlowerPositions[0] != NewPos.Pos.FlowerPositions[0] || - LastPos.FlowerPositions[1] != NewPos.Pos.FlowerPositions[1] || - LastPos.FlowerPositions[2] != NewPos.Pos.FlowerPositions[2]); - - if (PosChanged) - { - Log_Message(GlobalLogBuffer, - "Motor Status Received\nCurrent Positions: %d %d %d\n", - NewPos.Pos.FlowerPositions[0], - NewPos.Pos.FlowerPositions[1], - NewPos.Pos.FlowerPositions[2]); - } + motor_packet LastPos = BLState->LastKnownMotorState; + bool PosChanged = (LastPos.FlowerPositions[0] != NewPos.Pos.FlowerPositions[0] || + LastPos.FlowerPositions[1] != NewPos.Pos.FlowerPositions[1] || + LastPos.FlowerPositions[2] != NewPos.Pos.FlowerPositions[2]); + + if (PosChanged) + { + Log_Message(GlobalLogBuffer, + "Motor Status Received\nCurrent Positions: %d %d %d\n", + NewPos.Pos.FlowerPositions[0], + NewPos.Pos.FlowerPositions[1], + NewPos.Pos.FlowerPositions[2]); + } } internal void DEBUG_ReceivedTemperature(temp_packet Temp, gs_thread_context Ctx) { - Log_Message(GlobalLogBuffer, - "\nTemperature: %d\n", - Temp.Temperature); + Log_Message(GlobalLogBuffer, + "\nTemperature: %d\n", + Temp.Temperature); } internal void BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) { - mic_listen_job_data* Data = (mic_listen_job_data*)UserData; - - gs_data Msg = {}; - - u8 WeathermanIPAddr[4] = {}; - WeathermanIPAddr[0] = 127; - WeathermanIPAddr[1] = 0; - WeathermanIPAddr[2] = 0; - WeathermanIPAddr[3] = 1; - - u32 WeathermanIPV4 = (u32)UpackB4(WeathermanIPAddr); - u32 WeathermanPort = 20185; - - platform_socket_handle_ ListenSocket = {0}; - - while (*Data->Running) + mic_listen_job_data* Data = (mic_listen_job_data*)UserData; + + gs_data Msg = {}; + + u8 WeathermanIPAddr[4] = {}; + WeathermanIPAddr[0] = 127; + WeathermanIPAddr[1] = 0; + WeathermanIPAddr[2] = 0; + WeathermanIPAddr[3] = 1; + + u32 WeathermanIPV4 = (u32)UpackB4(WeathermanIPAddr); + u32 WeathermanPort = 20185; + + platform_socket_handle_ ListenSocket = {0}; + + while (*Data->Running) + { + if (!SocketQueryStatus(Data->SocketManager, ListenSocket)) { - if (!SocketQueryStatus(Data->SocketManager, ListenSocket)) - { - Data->IsConnected = false; - if (SocketHandleIsValid(ListenSocket)) - { - Log_Message(GlobalLogBuffer, "Disconnected from Python Server\n"); - CloseSocket(Data->SocketManager, ListenSocket); - } - ListenSocket = CreateSocket(Data->SocketManager, "127.0.0.1", "20185"); - if (ListenSocket.Index != 0) - { - Log_Message(GlobalLogBuffer, "Connected to Python Server\n"); - Data->IsConnected = true; - } - } - - if (SocketQueryStatus(Data->SocketManager, ListenSocket)) - { - if (SocketPeek(Data->SocketManager, ListenSocket)) - { - Msg = SocketRecieve(Data->SocketManager, ListenSocket, Ctx->Transient); - if (Msg.Size > 0) - { - MessageQueue_Write(Data->IncomingMsgQueue, Msg); - } - } - - while (MessageQueue_CanRead(*Data->OutgoingMsgQueue)) - { - Msg = MessageQueue_Peek(Data->OutgoingMsgQueue); - - u32 Address = WeathermanIPV4; - u32 Port = WeathermanPort; - s32 Flags = 0; - s32 LengthSent = SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags); - if (LengthSent != 0) - { - // if we actually sent the message, THEN we pull it off the - // message queue - MessageQueue_Read(Data->OutgoingMsgQueue); - } else { - break; - } - } - } + Data->IsConnected = false; + if (SocketHandleIsValid(ListenSocket)) + { + Log_Message(GlobalLogBuffer, "Disconnected from Python Server\n"); + CloseSocket(Data->SocketManager, ListenSocket); + } + ListenSocket = CreateSocket(Data->SocketManager, "127.0.0.1", "20185"); + if (ListenSocket.Index != 0) + { + Log_Message(GlobalLogBuffer, "Connected to Python Server\n"); + Data->IsConnected = true; + } } - CloseSocket(Data->SocketManager, ListenSocket); + if (SocketQueryStatus(Data->SocketManager, ListenSocket)) + { + if (SocketPeek(Data->SocketManager, ListenSocket)) + { + Msg = SocketRecieve(Data->SocketManager, ListenSocket, Ctx->Transient); + if (Msg.Size > 0) + { + MessageQueue_Write(Data->IncomingMsgQueue, Msg); + } + } + + while (MessageQueue_CanRead(*Data->OutgoingMsgQueue)) + { + Msg = MessageQueue_Peek(Data->OutgoingMsgQueue); + + u32 Address = WeathermanIPV4; + u32 Port = WeathermanPort; + s32 Flags = 0; + s32 LengthSent = SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags); + if (LengthSent != 0) + { + // if we actually sent the message, THEN we pull it off the + // message queue + MessageQueue_Read(Data->OutgoingMsgQueue); + } else { + break; + } + } + } + } + + CloseSocket(Data->SocketManager, ListenSocket); } internal void BlumenLumen_SetPatternMode(bl_pattern_mode Mode, r32 FadeDuration, animation_system* System, blumen_lumen_state* BLState) { - BLState->PatternMode = Mode; - animation_handle_array Playlist = BLState->ModeAnimations[Mode]; - System->RepeatMode = AnimationRepeat_Loop; - System->PlaylistFadeTime = FadeDuration; - AnimationSystem_FadeToPlaylist(System, Playlist); + BLState->PatternMode = Mode; + animation_handle_array Playlist = BLState->ModeAnimations[Mode]; + System->RepeatMode = AnimationRepeat_Loop; + System->PlaylistFadeTime = FadeDuration; + AnimationSystem_FadeToPlaylist(System, Playlist); } internal void BlumenLumen_LoadPatterns(app_state* State) { - animation_pattern_array* Patterns = &State->Patterns; - if (Patterns->CountMax == 0) - { - *Patterns = Patterns_Create(&State->Permanent, 32); - } - - Patterns->Count = 0; - Patterns_PushPattern(Patterns, Pattern_None, PATTERN_SINGLETHREADED); - Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Rainbow, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED); - Patterns_PushPattern(Patterns, Pattern_VerticalLines, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_Rotary, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_VoiceAddIns, PATTERN_MULTITHREADED); - - Patterns_PushPattern(Patterns, Pattern_StemSolid, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED); - - Patterns_PushPattern(Patterns, Pattern_GrowFadeMask, PATTERN_MULTITHREADED); - Patterns_PushPattern(Patterns, Pattern_RainbowLoadingBar, PATTERN_MULTITHREADED); - - Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED); + animation_pattern_array* Patterns = &State->Patterns; + if (Patterns->CountMax == 0) + { + *Patterns = Patterns_Create(&State->Permanent, 32); + } + + Patterns->Count = 0; + Patterns_PushPattern(Patterns, Pattern_None, PATTERN_SINGLETHREADED); + Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Rainbow, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED); + Patterns_PushPattern(Patterns, Pattern_VerticalLines, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Rotary, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_VoiceAddIns, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_StemSolid, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_GrowFadeMask, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_RainbowLoadingBar, PATTERN_MULTITHREADED); + + Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED); } internal void AppendPrintDate(gs_string* WriteStr, system_time Time) { - AppendPrintF(WriteStr, "%d-%d-%d : %d:%d:%d\n\n", - Time.Year, Time.Month, Time.Day, - Time.Hour, Time.Minute, Time.Second); + AppendPrintF(WriteStr, "%d-%d-%d : %d:%d:%d\n\n", + Time.Year, Time.Month, Time.Day, + Time.Hour, Time.Minute, Time.Second); } internal void BlumenLumen_AppendBootupLog(app_state* State, blumen_lumen_state* BLState, context Context) { - gs_thread_context Ctx = Context.ThreadContext; - gs_const_string BootupLogPath = ConstString("lumenarium_boot_log.log"); - - gs_file BootLog = ReadEntireFile(Ctx.FileHandler, BootupLogPath); - gs_string WriteStr = {}; - - // we don't want the log file getting huge - // if it gets above some threshold, instead of appending, - // copy what there is to an _old file, and start this one over - // - // The thinking is that without the copy operation, when we reached - // our threshold, we'd overwrite the whole log. If something went - // wrong at that point, we'd have nothing to go on. This way, there is - // always some historical data present on the system - // - if (BootLog.Size < MB(4)) + gs_thread_context Ctx = Context.ThreadContext; + gs_const_string BootupLogPath = ConstString("lumenarium_boot_log.log"); + + gs_file BootLog = ReadEntireFile(Ctx.FileHandler, BootupLogPath); + gs_string WriteStr = {}; + + // we don't want the log file getting huge + // if it gets above some threshold, instead of appending, + // copy what there is to an _old file, and start this one over + // + // The thinking is that without the copy operation, when we reached + // our threshold, we'd overwrite the whole log. If something went + // wrong at that point, we'd have nothing to go on. This way, there is + // always some historical data present on the system + // + if (BootLog.Size < MB(4)) + { + WriteStr = PushString(State->Transient, BootLog.Size + 1024); + } + else + { + if (!WriteEntireFile(Ctx.FileHandler, ConstString("lumenarium_boot_log_old.log"), + BootLog.Data)) { - WriteStr = PushString(State->Transient, BootLog.Size + 1024); + InvalidCodePath; } - else - { - if (!WriteEntireFile(Ctx.FileHandler, ConstString("lumenarium_boot_log_old.log"), - BootLog.Data)) - { - InvalidCodePath; - } - WriteStr = PushString(State->Transient, 1024); - } - - - // Copy old entries in - if (BootLog.Size > 0) - { - PrintF(&WriteStr, "%.*s", BootLog.Size, BootLog.Memory); - } - - // New Entry - AppendPrintF(&WriteStr, "Lumenarium Restarted\n"); - AppendPrintF(&WriteStr, "* Time: "); - AppendPrintDate(&WriteStr, Context.SystemTime_Current); - - gs_data Data = StringToData(WriteStr); - WriteEntireFile(Ctx.FileHandler, BootupLogPath, Data); + WriteStr = PushString(State->Transient, 1024); + } + + + // Copy old entries in + if (BootLog.Size > 0) + { + PrintF(&WriteStr, "%.*s", BootLog.Size, BootLog.Memory); + } + + // New Entry + AppendPrintF(&WriteStr, "Lumenarium Restarted\n"); + AppendPrintF(&WriteStr, "* Time: "); + AppendPrintDate(&WriteStr, Context.SystemTime_Current); + + gs_data Data = StringToData(WriteStr); + WriteEntireFile(Ctx.FileHandler, BootupLogPath, Data); } internal void BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Context) { - if (!BLState->ShouldUpdateLog) return; - - gs_string FileStr = PushString(State->Transient, 1024); - AppendPrintF(&FileStr, "Lumenarium Status\n"); - - AppendPrintF(&FileStr, "Last Updated At:"); - AppendPrintDate(&FileStr, Context.SystemTime_Current); - AppendPrintF(&FileStr, "\n\n"); - - animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); - - bool IsPlaying = State->AnimationSystem.TimelineShouldAdvance; - AppendPrintF(&FileStr, "\tIs Playing: %s\n", - IsPlaying ? "True" : "False"); - - char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected"; - AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected); - - u8 MP0 = BLState->LastKnownMotorState.FlowerPositions[0]; - u8 MP1 = BLState->LastKnownMotorState.FlowerPositions[1]; - u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2]; - AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2); - - time_range MotorRange = {}; - if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, - MotorOpenTimes, - MotorOpenTimesCount, - &MotorRange)) - { - AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: ( %d:%d - %d:%d)\n", - MotorRange.StartHour, MotorRange.StartMinute, - MotorRange.EndHour, MotorRange.EndMinute); - } - else - { - AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: None\n"); - } - - char* PatternMode = 0; - switch (BLState->PatternMode) - { - case BlumenPattern_Standard: { PatternMode = "Standard"; } break; - case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break; - case BlumenPattern_NoControl: { PatternMode = "No Control: Someone's doing the Awaken sequence!"; } break; - } - AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode); - - phrase_hue LastHuePhrase = BLState->LastHuePhrase; - AppendPrintF(&FileStr, "Last Mic Phrase: %S\n", LastHuePhrase.Phrase); - - AppendPrintF(&FileStr, "Pattern Speed: %f\n", BLState->PatternSpeed); - - AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent); - - time_range RangeIn = {}; - if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, - LedOnTimes, - LedOnTimesCount, - &RangeIn)) - { - AppendPrintF(&FileStr, "\tIn Leds-On Time Range: ( %d:%d - %d:%d)\n", - RangeIn.StartHour, RangeIn.StartMinute, - RangeIn.EndHour, RangeIn.EndMinute); - } - else - { - AppendPrintF(&FileStr, "\tIn Leds-On Time Range: None\n"); - } - - AppendPrintF(&FileStr, "\tTemp Dimming: %s\n", - Blumen_TempShouldDimLeds(BLState) ? "On" : "Off"); - - AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived); - - gs_data LogMem = StringToData(FileStr); - if (!WriteEntireFile(Context.ThreadContext.FileHandler, ConstString("lumenarium_status.log"), LogMem)) - { - InvalidCodePath; - } + if (!BLState->ShouldUpdateLog) return; + + gs_string FileStr = PushString(State->Transient, 1024); + AppendPrintF(&FileStr, "Lumenarium Status\n"); + + AppendPrintF(&FileStr, "Last Updated At:"); + AppendPrintDate(&FileStr, Context.SystemTime_Current); + AppendPrintF(&FileStr, "\n\n"); + + animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name); + + bool IsPlaying = State->AnimationSystem.TimelineShouldAdvance; + AppendPrintF(&FileStr, "\tIs Playing: %s\n", + IsPlaying ? "True" : "False"); + + char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected"; + AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected); + + u8 MP0 = BLState->LastKnownMotorState.FlowerPositions[0]; + u8 MP1 = BLState->LastKnownMotorState.FlowerPositions[1]; + u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2]; + AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2); + + time_range MotorRange = {}; + if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, + MotorOpenTimes, + MotorOpenTimesCount, + &MotorRange)) + { + AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: ( %d:%d - %d:%d)\n", + MotorRange.StartHour, MotorRange.StartMinute, + MotorRange.EndHour, MotorRange.EndMinute); + } + else + { + AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: None\n"); + } + + char* PatternMode = 0; + switch (BLState->PatternMode) + { + case BlumenPattern_Standard: { PatternMode = "Standard"; } break; + case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break; + case BlumenPattern_NoControl: { PatternMode = "No Control: Someone's doing the Awaken sequence!"; } break; + } + AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode); + + phrase_hue LastHuePhrase = BLState->LastHuePhrase; + AppendPrintF(&FileStr, "Last Mic Phrase: %S\n", LastHuePhrase.Phrase); + + AppendPrintF(&FileStr, "Pattern Speed: %f\n", BLState->PatternSpeed); + + AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent); + + time_range RangeIn = {}; + if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current, + LedOnTimes, + LedOnTimesCount, + &RangeIn)) + { + AppendPrintF(&FileStr, "\tIn Leds-On Time Range: ( %d:%d - %d:%d)\n", + RangeIn.StartHour, RangeIn.StartMinute, + RangeIn.EndHour, RangeIn.EndMinute); + } + else + { + AppendPrintF(&FileStr, "\tIn Leds-On Time Range: None\n"); + } + + AppendPrintF(&FileStr, "\tTemp Dimming: %s\n", + Blumen_TempShouldDimLeds(BLState) ? "On" : "Off"); + + AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived); + + gs_data LogMem = StringToData(FileStr); + if (!WriteEntireFile(Context.ThreadContext.FileHandler, ConstString("lumenarium_status.log"), LogMem)) + { + InvalidCodePath; + } } internal gs_data BlumenLumen_CustomInit(app_state* State, context Context) { - // This is memory for any custom data that we want to use - // as a part of a particular sculpture. - // By returning it from here, it will be sent as an argument to - // the sculpture's CustomUpdate function; - gs_data Result = {}; - - Result = PushSize(&State->Permanent, sizeof(blumen_lumen_state)); - - blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory; - BLState->Running = true; - BLState->BrightnessPercent = 1; - MessageQueue_Init(&BLState->IncomingMsgQueue, &State->Permanent); - MessageQueue_Init(&BLState->OutgoingMsgQueue, &State->Permanent); - - BLState->MicListenJobData.Running = &BLState->Running; - BLState->MicListenJobData.SocketManager = Context.SocketManager; - BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue; - BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; - - BLState->PatternSpeed = GlobalAnimSpeed; - + // This is memory for any custom data that we want to use + // as a part of a particular sculpture. + // By returning it from here, it will be sent as an argument to + // the sculpture's CustomUpdate function; + gs_data Result = {}; + + Result = PushSize(&State->Permanent, sizeof(blumen_lumen_state)); + + blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory; + BLState->Running = true; + BLState->BrightnessPercent = 1; + MessageQueue_Init(&BLState->IncomingMsgQueue, &State->Permanent); + MessageQueue_Init(&BLState->OutgoingMsgQueue, &State->Permanent); + + BLState->MicListenJobData.Running = &BLState->Running; + BLState->MicListenJobData.SocketManager = Context.SocketManager; + BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue; + BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue; + + BLState->PatternSpeed = GlobalAnimSpeed; + #if 1 - BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData, Context.ThreadContext); + BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData, Context.ThreadContext); #endif + + assembly* Flower0 = LoadAssembly(Flower0AssemblyPath, State, Context); + assembly* Flower1 = LoadAssembly(Flower1AssemblyPath, State, Context); + assembly* Flower2 = LoadAssembly(Flower2AssemblyPath, State, Context); + + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + assembly Assembly = State->Assemblies.Values[i]; + BLState->StemStrips[Assembly.AssemblyIndex] = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("stem"), &State->Permanent); + } + + BLState->AssemblyNameToClearCoreMapCount = 3; + BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent, + u64, + BLState->AssemblyNameToClearCoreMapCount); + BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU32(StringExpand(Flower2->Name)); + BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU32(StringExpand(Flower1->Name)); + BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower0->Name)); + + gs_file_handler FileHandler = Context.ThreadContext.FileHandler; + gs_file ColorPhraseCSVFile = ReadEntireFile(FileHandler, PhraseMapCSVPath); + if (ColorPhraseCSVFile.Memory != 0) + { + gs_const_string ColorPhraseMapStr = DataToString(ColorPhraseCSVFile.Data); + gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, + { PhraseMapCSVSeparator }, + State->Transient); - assembly* Flower0 = LoadAssembly(Flower0AssemblyPath, State, Context); - assembly* Flower1 = LoadAssembly(Flower1AssemblyPath, State, Context); - assembly* Flower2 = LoadAssembly(Flower2AssemblyPath, State, Context); - - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) - { - assembly Assembly = State->Assemblies.Values[i]; - BLState->StemStrips[Assembly.AssemblyIndex] = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("stem"), &State->Permanent); - } - - BLState->AssemblyNameToClearCoreMapCount = 3; - BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent, - u64, - BLState->AssemblyNameToClearCoreMapCount); - BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU32(StringExpand(Flower2->Name)); - BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU32(StringExpand(Flower1->Name)); - BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower0->Name)); - - gs_file_handler FileHandler = Context.ThreadContext.FileHandler; - gs_file ColorPhraseCSVFile = ReadEntireFile(FileHandler, PhraseMapCSVPath); - if (ColorPhraseCSVFile.Memory != 0) - { - gs_const_string ColorPhraseMapStr = DataToString(ColorPhraseCSVFile.Data); - gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr, - { PhraseMapCSVSeparator }, - State->Transient); - - BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, - &State->Permanent); - } - + BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet, + &State->Permanent); + } + #if 0 - animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - ConstString("data/demo_patterns.foldanim")); - State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; + animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/demo_patterns.foldanim")); + State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; #else - - BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context); - BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); - AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); - - BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); - - BLState->AwakenHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - ConstString("data/blumen_animations/awaken.foldanim")); - BLState->OffAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, - State->Patterns, - Context, - ConstString("data/blumen_animations/off_anim.foldanim")); - + + BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context); + BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context); + AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim")); + + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + + BLState->AwakenHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/blumen_animations/awaken.foldanim")); + BLState->OffAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/blumen_animations/off_anim.foldanim")); + #endif - State->AnimationSystem.TimelineShouldAdvance = true; - - BLState->StandardPatternHues.Granularity = 1; - BLState->StandardPatternHues.Speed = 1; - BLState->StandardPatternHues.AddIn = AddIn_Rotary; - BLState->StandardPatternHues.Pattern = HuePattern_Wavy; - - BLState->DebugHue.Hue0.HSV = v4{0, 1, 1, 1}; - BLState->DebugHue.Hue1.HSV = v4{0, 1, 1, 1}; - BLState->DebugHue.Hue2.HSV = v4{0, 1, 1, 1}; - - BlumenLumen_AppendBootupLog(State, BLState, Context); - return Result; + State->AnimationSystem.TimelineShouldAdvance = true; + + BLState->StandardPatternHues.Granularity = 1; + BLState->StandardPatternHues.Speed = 1; + BLState->StandardPatternHues.AddIn = AddIn_Rotary; + BLState->StandardPatternHues.Pattern = HuePattern_Wavy; + + BLState->DebugHue.Hue0.HSV = v4{0, 1, 1, 1}; + BLState->DebugHue.Hue1.HSV = v4{0, 1, 1, 1}; + BLState->DebugHue.Hue2.HSV = v4{0, 1, 1, 1}; + + BlumenLumen_AppendBootupLog(State, BLState, Context); + return Result; } internal void BlumenLumen_UpdateMotorState(blumen_lumen_state* BLState, motor_status_packet Motor, context Context) { - DEBUG_ReceivedMotorPositions(BLState, Motor, Context.ThreadContext); - - motor_packet LastPos = BLState->LastKnownMotorState; - motor_packet CurrPos = Motor.Pos; - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + DEBUG_ReceivedMotorPositions(BLState, Motor, Context.ThreadContext); + + motor_packet LastPos = BLState->LastKnownMotorState; + motor_packet CurrPos = Motor.Pos; + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i]) { - if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i]) - { - BLState->LastTimeMotorStateChanged[i] = Context.SystemTime_Current.NanosSinceEpoch; - } + BLState->LastTimeMotorStateChanged[i] = Context.SystemTime_Current.NanosSinceEpoch; } - - BLState->LastKnownMotorState = Motor.Pos; - BLState->ShouldUpdateLog = true; + } + + BLState->LastKnownMotorState = Motor.Pos; + BLState->ShouldUpdateLog = true; } internal void BlumenLumen_ApplyNextHotHue(blumen_lumen_state* BLState, context Context, gs_string* DebugStr, app_state* State) { - // if we are in standard color mode, shift all flowers to the new color - // otherwise, only shift the next flower in the sequence to the new color - phrase_hue NewHue = BLState->NextHotHue; - Log_Message(GlobalLogBuffer, "Switching To: %S\n", NewHue.Phrase); - - - if (BLState->PatternMode == BlumenPattern_Standard || - NewHue.OverrideAll) - { - BlumenLumen_SetNextHue(BLState, 0, NewHue); - BlumenLumen_SetNextHue(BLState, 1, NewHue); - BlumenLumen_SetNextHue(BLState, 2, NewHue); - } - else - { - u32 AssemblyIdx = BLState->LastAssemblyColorSet; - BlumenLumen_SetNextHue(BLState, AssemblyIdx, NewHue); - BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; - } - - BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); - BLState->TimeLastSetToVoiceMode = Context.SystemTime_Current; - BLState->LastHuePhrase = NewHue; - BLState->ShouldUpdateLog = true; - BLState->InPhraseReceptionMode = false; + // if we are in standard color mode, shift all flowers to the new color + // otherwise, only shift the next flower in the sequence to the new color + phrase_hue NewHue = BLState->NextHotHue; + Log_Message(GlobalLogBuffer, "Switching To: %S\n", NewHue.Phrase); + + + if (BLState->PatternMode == BlumenPattern_Standard || + NewHue.OverrideAll) + { + BlumenLumen_SetNextHue(BLState, 0, NewHue); + BlumenLumen_SetNextHue(BLState, 1, NewHue); + BlumenLumen_SetNextHue(BLState, 2, NewHue); + } + else + { + u32 AssemblyIdx = BLState->LastAssemblyColorSet; + BlumenLumen_SetNextHue(BLState, AssemblyIdx, NewHue); + BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3; + } + + BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + BLState->TimeLastSetToVoiceMode = Context.SystemTime_Current; + BLState->LastHuePhrase = NewHue; + BLState->ShouldUpdateLog = true; + BLState->InPhraseReceptionMode = false; } internal void BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; - BLState->ShouldUpdateLog = false; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + BLState->ShouldUpdateLog = false; + + gs_string DebugStr = PushString(State->Transient, 256); + + while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) + { + gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); + if (PacketData.Memory == 0) continue; - gs_string DebugStr = PushString(State->Transient, 256); - - while (MessageQueue_CanRead(BLState->IncomingMsgQueue)) - { - gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue); - if (PacketData.Memory == 0) continue; + blumen_packet Packet = *(blumen_packet*)PacketData.Memory; + switch (Packet.Type) { + case PacketType_PatternCommand: + { + microphone_packet Mic = Packet.MicPacket; + u64 NameHash = HashDJB2ToU32(Mic.AnimationFileName); + u32 NameLen = CStringLength(Mic.AnimationFileName); - blumen_packet Packet = *(blumen_packet*)PacketData.Memory; - switch (Packet.Type) { - case PacketType_PatternCommand: - { - microphone_packet Mic = Packet.MicPacket; - u64 NameHash = HashDJB2ToU32(Mic.AnimationFileName); - u32 NameLen = CStringLength(Mic.AnimationFileName); - - phrase_hue NewHue = PhraseHueMap_Find(BLState->PhraseHueMap, NameHash); - if (NewHue.Phrase.Length > 0) - { - bool IsLonger = (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length); - bool IsntInPhraseReceptionMode = !BLState->InPhraseReceptionMode; - if (IsLonger || IsntInPhraseReceptionMode) - { - Log_Message(GlobalLogBuffer, "Queueing: %S\n", NewHue.Phrase); - - BLState->NextHotHue = NewHue; - if (SecondsElapsed(BLState->TimePhraseReceptionBegan, - Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime) - { - BLState->TimePhraseReceptionBegan = Context->SystemTime_Current; - BLState->InPhraseReceptionMode = true; - } - BLState->TimeLastPhraseReceived = Context->SystemTime_Current; - } - } - }break; - - case PacketType_MotorState: - { - motor_status_packet Motor = Packet.MotorStatusPacket; - - // NOTE(pjs): Python sends multi-byte integers in little endian - // order. Have to unpack - u8* T = (u8*)&Motor.Temperature; - Motor.Temperature = (T[0] << 8 | - T[1] << 0); - - BlumenLumen_UpdateMotorState(BLState, Motor, *Context); - }break; - - case PacketType_Temperature: - { - temp_packet Temp = Packet.TempPacket; - BLState->LastTemperatureReceived = Temp.Temperature; - DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); - BLState->ShouldUpdateLog = true; - }break; - - InvalidDefaultCase; - } - } - - - - if (BLState->InPhraseReceptionMode) - { - r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current); - if (SecondsSincePhraseBegan > PhrasePriorityMessageGroupingTime) + phrase_hue NewHue = PhraseHueMap_Find(BLState->PhraseHueMap, NameHash); + if (NewHue.Phrase.Length > 0) { - BlumenLumen_ApplyNextHotHue(BLState, *Context, &DebugStr, State); - } - } - - BlumenLumen_AdvanceHueFade(BLState, *Context); - - // Update next frames Hues - if (!BLState->DebugOverrideHue) - { - r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); - AnimTime = (r32)Context->TotalTime; - r32 BaseTime = AnimTime * BLState->PatternSpeed; - - r32 ColorSpeed = 1; //.001; - r32 ColorOscSpeed = .05 * ColorSpeed; - r32 ColorRelOscSpeed = 1 * ColorSpeed;; - r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; - r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); - - r32 H0 = ModR32(ColorOscillation * 360, 360); - r32 H1 = ModR32(BaseTime + ColorRelationship, 360); - // TODO(PS): use our new HSV lerp - r32 H2 = LerpR32(.3f, H0, H1); - - BLState->StandardPatternHues.Hue0.HSV = v4{ H0, 1, 1, 1 }; - BLState->StandardPatternHues.Hue1.HSV = v4{ H1, 1, 1, 1 }; - BLState->StandardPatternHues.Hue2.HSV = v4{ H2, 1, 1, 1 }; - - // Transition back to standard mode after some time - if (BLState->PatternMode == BlumenPattern_VoiceCommand) - { - u64 LastChangeClock = BLState->TimeLastSetToVoiceMode.NanosSinceEpoch; - u64 NowClocks = Context->SystemTime_Current.NanosSinceEpoch; - s64 NanosSinceChange = NowClocks - LastChangeClock; - r64 SecondsSinceChange = (r64)NanosSinceChange * NanosToSeconds; + bool IsLonger = (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length); + bool IsntInPhraseReceptionMode = !BLState->InPhraseReceptionMode; + if (IsLonger || IsntInPhraseReceptionMode) + { + Log_Message(GlobalLogBuffer, "Queueing: %S\n", NewHue.Phrase); - if (SecondsSinceChange > VoiceCommandSustainDuration) + BLState->NextHotHue = NewHue; + if (SecondsElapsed(BLState->TimePhraseReceptionBegan, + Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime) { - BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); - BLState->ShouldUpdateLog = true; + BLState->TimePhraseReceptionBegan = Context->SystemTime_Current; + BLState->InPhraseReceptionMode = true; } + BLState->TimeLastPhraseReceived = Context->SystemTime_Current; + } } + }break; + + case PacketType_MotorState: + { + motor_status_packet Motor = Packet.MotorStatusPacket; + // NOTE(pjs): Python sends multi-byte integers in little endian + // order. Have to unpack + u8* T = (u8*)&Motor.Temperature; + Motor.Temperature = (T[0] << 8 | + T[1] << 0); + + BlumenLumen_UpdateMotorState(BLState, Motor, *Context); + }break; + + case PacketType_Temperature: + { + temp_packet Temp = Packet.TempPacket; + BLState->LastTemperatureReceived = Temp.Temperature; + DEBUG_ReceivedTemperature(Temp, Context->ThreadContext); + BLState->ShouldUpdateLog = true; + }break; + + InvalidDefaultCase; + } + } + + + + if (BLState->InPhraseReceptionMode) + { + r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current); + if (SecondsSincePhraseBegan > PhrasePriorityMessageGroupingTime) + { + BlumenLumen_ApplyNextHotHue(BLState, *Context, &DebugStr, State); + } + } + + BlumenLumen_AdvanceHueFade(BLState, *Context); + + // Update next frames Hues + if (!BLState->DebugOverrideHue) + { + r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem); + AnimTime = (r32)Context->TotalTime; + r32 BaseTime = AnimTime * BLState->PatternSpeed; + + r32 ColorSpeed = 1; //.001; + r32 ColorOscSpeed = .05 * ColorSpeed; + r32 ColorRelOscSpeed = 1 * ColorSpeed;; + r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2; + r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300); + + r32 H0 = ModR32(ColorOscillation * 360, 360); + r32 H1 = ModR32(BaseTime + ColorRelationship, 360); + // TODO(PS): use our new HSV lerp + r32 H2 = LerpR32(.3f, H0, H1); + + BLState->StandardPatternHues.Hue0.HSV = v4{ H0, 1, 1, 1 }; + BLState->StandardPatternHues.Hue1.HSV = v4{ H1, 1, 1, 1 }; + BLState->StandardPatternHues.Hue2.HSV = v4{ H2, 1, 1, 1 }; + + // Transition back to standard mode after some time + if (BLState->PatternMode == BlumenPattern_VoiceCommand) + { + u64 LastChangeClock = BLState->TimeLastSetToVoiceMode.NanosSinceEpoch; + u64 NowClocks = Context->SystemTime_Current.NanosSinceEpoch; + s64 NanosSinceChange = NowClocks - LastChangeClock; + r64 SecondsSinceChange = (r64)NanosSinceChange * NanosToSeconds; + + if (SecondsSinceChange > VoiceCommandSustainDuration) + { + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState); + BLState->ShouldUpdateLog = true; + } + } + + } + else + { + BLState->StandardPatternHues = BLState->DebugHue; + AnimationSystem_FadeToPlaylist(&State->AnimationSystem, BLState->ModeAnimations[BlumenPattern_VoiceCommand]); + + } + + // Open / Close the Motor + if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue) && + !BLState->IgnoreTimeOfDay_MotorState) + { + bool SendMotorCommand = false; + + u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; + r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; + bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; + + bl_motor_state_value NewMotorState = MotorState_Closed; + bool SendOpen = false; + for (u32 i = 0; i < MotorOpenTimesCount; i++) + { + time_range Range = MotorOpenTimes[i]; + bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); + if (CurrTimeInRange) { + NewMotorState = MotorState_Open; + } + } + + if (NewMotorState != BLState->LastSendState) + { + ShouldSendCurrentState = true; + } + + if (ShouldSendCurrentState) + { + BLState->LastSendTime = Context->SystemTime_Current; + BLState->LastSendState = NewMotorState; + + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket.FlowerPositions[0] = NewMotorState; + Packet.MotorPacket.FlowerPositions[1] = NewMotorState; + Packet.MotorPacket.FlowerPositions[2] = NewMotorState; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + + DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); + } + } + + // When a motor state changes to being open, wait to turn Upper Leds on + // in order to hide the fact that they are turning off + motor_packet CurrMotorPos = BLState->LastKnownMotorState; + u64 NowNanos = Context->SystemTime_Current.NanosSinceEpoch; + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + // have to map from "assembly load order" to + // the order that the clear core is referencing the + // motors by + assembly Assembly = State->Assemblies.Values[i]; + u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState); + u8 MotorPos = CurrMotorPos.FlowerPositions[AssemblyCCIndex]; + + if ((MotorPos == MotorState_Open || MotorPos == MotorState_MostlyOpen) && + !BLState->ShouldDimUpperLeds[i]) + { + u64 ChangedNanos = BLState->LastTimeMotorStateChanged[i]; + u64 NanosSinceChanged = NowNanos - ChangedNanos; + r64 SecondsSinceChanged = (r64)NanosSinceChanged * NanosToSeconds; + if (SecondsSinceChanged > TurnUpperLedsOffAfterMotorCloseCommandDelay) + { + BLState->ShouldDimUpperLeds[i] = true; + } + else + { + BLState->ShouldDimUpperLeds[i] = false; + } + } + else if (MotorPos == MotorState_Closed || + MotorPos == MotorState_HalfOpen) + { + BLState->ShouldDimUpperLeds[i] = false; + } + } + + // NOTE(PS): If the flowers are mostly open or full open + // we mask off the top leds to prevent them from overheating + // while telescoped inside the flower + for (u32 a = 0; a < BL_FLOWER_COUNT; a++) + { + assembly Assembly = State->Assemblies.Values[a]; + if (!BLState->ShouldDimUpperLeds[a]) continue; + + led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; + led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient); + for (u32 s = 0; s < TopStrips.Count; s++) + { + u32 SIndex = TopStrips.StripIndices[s]; + v2_strip Strip = Assembly.Strips[SIndex]; + for (u32 l = 0; l < Strip.LedCount; l++) + { + u32 LIndex = Strip.LedLUT[l]; + Buffer.Colors[LIndex] = {0}; + } + } + } + + if (!BLState->IgnoreTimeOfDay_LedDimming) + { + bool TimelineShouldAdvance = false; + r32 OverrideBrightness = 0.0f; + + time_range RangeIn = {}; + if (SystemTimeIsInTimeRangeList(Context->SystemTime_Current, + LedOnTimes, + LedOnTimesCount, + &RangeIn) || + DEBUGIgnoreLedOnTimeRange) + { + + if (Blumen_TempShouldDimLeds(BLState)) + { + OverrideBrightness = HighTemperatureBrightnessPercent; + OutputDebugString("Set Brightness High Temp"); + } + else + { + OverrideBrightness = FullBrightnessPercent; + OutputDebugString("Set Brightness Full"); + } + TimelineShouldAdvance = true; } else { - BLState->StandardPatternHues = BLState->DebugHue; - AnimationSystem_FadeToPlaylist(&State->AnimationSystem, BLState->ModeAnimations[BlumenPattern_VoiceCommand]); - + OutputDebugString("Skipped Time Range Dimming"); } - // Open / Close the Motor - if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue) && - !BLState->IgnoreTimeOfDay_MotorState) + State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; + BLState->BrightnessPercent = OverrideBrightness; + } + else + { + OutputDebugString("Skipped Dimming Altogether"); + } + + // Dim the leds based on temp data + if (false && !BLState->DEBUG_IgnoreWeatherDimmingLeds) + { + for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) { - bool SendMotorCommand = false; - - u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; - r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; - bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; - - bl_motor_state_value NewMotorState = MotorState_Closed; - bool SendOpen = false; - for (u32 i = 0; i < MotorOpenTimesCount; i++) - { - time_range Range = MotorOpenTimes[i]; - bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); - if (CurrTimeInRange) { - NewMotorState = MotorState_Open; - } - } - - if (NewMotorState != BLState->LastSendState) - { - ShouldSendCurrentState = true; - } - - if (ShouldSendCurrentState) - { - BLState->LastSendTime = Context->SystemTime_Current; - BLState->LastSendState = NewMotorState; - - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket.FlowerPositions[0] = NewMotorState; - Packet.MotorPacket.FlowerPositions[1] = NewMotorState; - Packet.MotorPacket.FlowerPositions[2] = NewMotorState; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - - DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); - } + led_buffer Buffer = State->LedSystem.Buffers[i]; + for (u32 j = 0; j < Buffer.LedCount; j++) + { + pixel* Color = Buffer.Colors + j; + Color->R = Color->R * BLState->BrightnessPercent; + Color->G = Color->G * BLState->BrightnessPercent; + Color->B = Color->B * BLState->BrightnessPercent; + } } - - // When a motor state changes to being open, wait to turn Upper Leds on - // in order to hide the fact that they are turning off - motor_packet CurrMotorPos = BLState->LastKnownMotorState; - u64 NowNanos = Context->SystemTime_Current.NanosSinceEpoch; - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + } + + // Send Status Packet + { + system_time LastSendTime = BLState->LastStatusUpdateTime; + r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)LastSendTime.NanosSinceEpoch); + r64 SecondsSinceLastSend = NanosSinceLastSend * NanosToSeconds; + if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) { - // have to map from "assembly load order" to - // the order that the clear core is referencing the - // motors by - assembly Assembly = State->Assemblies.Values[i]; - u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState); - u8 MotorPos = CurrMotorPos.FlowerPositions[AssemblyCCIndex]; - - if ((MotorPos == MotorState_Open || MotorPos == MotorState_MostlyOpen) && - !BLState->ShouldDimUpperLeds[i]) - { - u64 ChangedNanos = BLState->LastTimeMotorStateChanged[i]; - u64 NanosSinceChanged = NowNanos - ChangedNanos; - r64 SecondsSinceChanged = (r64)NanosSinceChanged * NanosToSeconds; - if (SecondsSinceChanged > TurnUpperLedsOffAfterMotorCloseCommandDelay) - { - BLState->ShouldDimUpperLeds[i] = true; - } - else - { - BLState->ShouldDimUpperLeds[i] = false; - } - } - else if (MotorPos == MotorState_Closed || - MotorPos == MotorState_HalfOpen) - { - BLState->ShouldDimUpperLeds[i] = false; - } + BLState->LastStatusUpdateTime = Context->SystemTime_Current; + Log_Message(GlobalLogBuffer, "Attempting to Send Lumenarium Status\n"); + + blumen_packet Packet = {}; + Packet.Type = PacketType_LumenariumStatus; + Packet.StatusPacket.NextMotorEventType = 0; + Packet.StatusPacket.NextEventTime = 0; + + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + if (ActiveAnim) + { + CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName, + Min(ActiveAnim->Name.Length, 32)); + Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0; + } + + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + + // there's no new information here, but by updating the log here, + // we're updating it at some infrequent but regular period that isnt + // every single frame + BLState->ShouldUpdateLog = true; } - - // NOTE(PS): If the flowers are mostly open or full open - // we mask off the top leds to prevent them from overheating - // while telescoped inside the flower - for (u32 a = 0; a < BL_FLOWER_COUNT; a++) - { - assembly Assembly = State->Assemblies.Values[a]; - if (!BLState->ShouldDimUpperLeds[a]) continue; - - led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex]; - led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient); - for (u32 s = 0; s < TopStrips.Count; s++) - { - u32 SIndex = TopStrips.StripIndices[s]; - v2_strip Strip = Assembly.Strips[SIndex]; - for (u32 l = 0; l < Strip.LedCount; l++) - { - u32 LIndex = Strip.LedLUT[l]; - Buffer.Colors[LIndex] = {0}; - } - } - } - - if (!BLState->IgnoreTimeOfDay_LedDimming) - { - bool TimelineShouldAdvance = false; - r32 OverrideBrightness = 0.0f; - - time_range RangeIn = {}; - if (SystemTimeIsInTimeRangeList(Context->SystemTime_Current, - LedOnTimes, - LedOnTimesCount, - &RangeIn) || - DEBUGIgnoreLedOnTimeRange) - { - - if (Blumen_TempShouldDimLeds(BLState)) - { - OverrideBrightness = HighTemperatureBrightnessPercent; - } - else - { - OverrideBrightness = FullBrightnessPercent; - } - TimelineShouldAdvance = true; - } - - State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; - BLState->BrightnessPercent = OverrideBrightness; - } - - // Dim the leds based on temp data - if (false && !BLState->DEBUG_IgnoreWeatherDimmingLeds) - { - for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) - { - led_buffer Buffer = State->LedSystem.Buffers[i]; - for (u32 j = 0; j < Buffer.LedCount; j++) - { - pixel* Color = Buffer.Colors + j; - Color->R = Color->R * BLState->BrightnessPercent; - Color->G = Color->G * BLState->BrightnessPercent; - Color->B = Color->B * BLState->BrightnessPercent; - } - } - } - - // Send Status Packet - { - system_time LastSendTime = BLState->LastStatusUpdateTime; - r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)LastSendTime.NanosSinceEpoch); - r64 SecondsSinceLastSend = NanosSinceLastSend * NanosToSeconds; - if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS) - { - BLState->LastStatusUpdateTime = Context->SystemTime_Current; - Log_Message(GlobalLogBuffer, "Attempting to Send Lumenarium Status\n"); - - blumen_packet Packet = {}; - Packet.Type = PacketType_LumenariumStatus; - Packet.StatusPacket.NextMotorEventType = 0; - Packet.StatusPacket.NextEventTime = 0; - - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - if (ActiveAnim) - { - CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName, - Min(ActiveAnim->Name.Length, 32)); - Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0; - } - - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - - // there's no new information here, but by updating the log here, - // we're updating it at some infrequent but regular period that isnt - // every single frame - BLState->ShouldUpdateLog = true; - } - } - - BlumenLumen_UpdateLog(State, BLState, *Context); + } + + BlumenLumen_UpdateLog(State, BLState, *Context); } US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; - ui_interface* I = &State->Interface; - - ui_BeginRow(I, BlumenDebug_Count); - for (u32 i = 0; i < BlumenDebug_Count; i++) + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + ui_interface* I = &State->Interface; + + ui_BeginRow(I, BlumenDebug_Count); + for (u32 i = 0; i < BlumenDebug_Count; i++) + { + if (ui_Button(I, MakeString(BlDebugUiModeStrings[i]))) { - if (ui_Button(I, MakeString(BlDebugUiModeStrings[i]))) + BLState->DebugMode = (bl_debug_ui_mode)i; + } + } + ui_EndRow(I); + + switch (BLState->DebugMode) + { + case BlumenDebug_Motors: + { + motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; + + BLState->IgnoreTimeOfDay_MotorState = ui_ToggleText(I, MakeString("Motors Ignore Time Limit"), BLState->IgnoreTimeOfDay_MotorState); + + for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) + { + gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); + ui_BeginRow(I, 5); { - BLState->DebugMode = (bl_debug_ui_mode)i; + ui_Label(I, Label); + + bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; + if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; + } + bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; + if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; + } + bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; + if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; + } + bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; + if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) + { + PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; + } } - } - ui_EndRow(I); + ui_EndRow(I); + } + BLState->DEBUG_PendingMotorPacket = PendingPacket; + + if (ui_Button(I, MakeString("Send Motor Packet"))) + { + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); + + } + + motor_packet MotorPos = BLState->LastKnownMotorState; + ui_Label(I, MakeString("Current Motor Positions")); + { + for (u32 i = 0; i < BL_FLOWER_COUNT; i++) + { + ui_BeginRow(I, 2); + gs_string MotorStr = PushStringF(State->Transient, 32, + "Motor %d", + i); + ui_Label(I, MotorStr); + + gs_string StateStr = {}; + switch (MotorPos.FlowerPositions[i]) + { + case MotorState_Closed: { + StateStr = MakeString("Closed"); + } break; + case MotorState_HalfOpen: { + StateStr = MakeString("Half Open"); + } break; + case MotorState_MostlyOpen: { + StateStr = MakeString("Mostly Open"); + } break; + case MotorState_Open: { + StateStr = MakeString("Open"); + } break; + + default: + { + StateStr = MakeString("Invalid Value"); + } break; + } + + ui_Label(I, StateStr); + ui_EndRow(I); + } + } + + ui_Label(I, MakeString("Set Internal Motor State:")); + if (ui_Button(I, MakeString("Closed"))) + { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Closed; + Motor.Pos.FlowerPositions[1] = MotorState_Closed; + Motor.Pos.FlowerPositions[2] = MotorState_Closed; + Motor.Temperature = 16; + + BlumenLumen_UpdateMotorState(BLState, Motor, Context); + } + if (ui_Button(I, MakeString("Open"))) + { + motor_status_packet Motor = {}; + Motor.Pos.FlowerPositions[0] = MotorState_Open; + Motor.Pos.FlowerPositions[1] = MotorState_Open; + Motor.Pos.FlowerPositions[2] = MotorState_Open; + Motor.Temperature = 16; + + BlumenLumen_UpdateMotorState(BLState, Motor, Context); + } + } break; - switch (BLState->DebugMode) + case BlumenDebug_Leds: { - case BlumenDebug_Motors: + BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); + + BLState->IgnoreTimeOfDay_LedDimming = ui_ToggleText(I, MakeString("Leds Ignore Time Limit"), BLState->IgnoreTimeOfDay_LedDimming); + + if (ui_BeginLabeledDropdown(I, MakeString("Phrase"), MakeString(BLState->PendingPhrase.Phrase))) + { + u32 ListCount = BLState->PhraseHueMap.Count; + ui_BeginList(I, MakeString("Phrase List"), 5, ListCount); + for (u32 i = 0; i < ListCount; i++) { - motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket; - - BLState->IgnoreTimeOfDay_MotorState = ui_ToggleText(I, MakeString("Motors Ignore Time Limit"), BLState->IgnoreTimeOfDay_MotorState); - - for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++) - { - gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex); - ui_BeginRow(I, 5); - { - ui_Label(I, Label); - - bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed; - if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed; - } - bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen; - if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen; - } - bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen; - if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen; - } - bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open; - if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen)) - { - PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open; - } - } - ui_EndRow(I); - } - BLState->DEBUG_PendingMotorPacket = PendingPacket; - - if (ui_Button(I, MakeString("Send Motor Packet"))) - { - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext); - - } - - motor_packet MotorPos = BLState->LastKnownMotorState; - ui_Label(I, MakeString("Current Motor Positions")); - { - for (u32 i = 0; i < BL_FLOWER_COUNT; i++) - { - ui_BeginRow(I, 2); - gs_string MotorStr = PushStringF(State->Transient, 32, - "Motor %d", - i); - ui_Label(I, MotorStr); - - gs_string StateStr = {}; - switch (MotorPos.FlowerPositions[i]) - { - case MotorState_Closed: { - StateStr = MakeString("Closed"); - } break; - case MotorState_HalfOpen: { - StateStr = MakeString("Half Open"); - } break; - case MotorState_MostlyOpen: { - StateStr = MakeString("Mostly Open"); - } break; - case MotorState_Open: { - StateStr = MakeString("Open"); - } break; - - default: - { - StateStr = MakeString("Invalid Value"); - } break; - } - - ui_Label(I, StateStr); - ui_EndRow(I); - } - } - - ui_Label(I, MakeString("Set Internal Motor State:")); - if (ui_Button(I, MakeString("Closed"))) - { - motor_status_packet Motor = {}; - Motor.Pos.FlowerPositions[0] = MotorState_Closed; - Motor.Pos.FlowerPositions[1] = MotorState_Closed; - Motor.Pos.FlowerPositions[2] = MotorState_Closed; - Motor.Temperature = 16; - - BlumenLumen_UpdateMotorState(BLState, Motor, Context); - } - if (ui_Button(I, MakeString("Open"))) - { - motor_status_packet Motor = {}; - Motor.Pos.FlowerPositions[0] = MotorState_Open; - Motor.Pos.FlowerPositions[1] = MotorState_Open; - Motor.Pos.FlowerPositions[2] = MotorState_Open; - Motor.Temperature = 16; - - BlumenLumen_UpdateMotorState(BLState, Motor, Context); - } - } break; + gs_string Str = MakeString(BLState->PhraseHueMap.Phrases[i]); + if (ui_Button(I, Str)) + { + BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i); + BLState->DebugHue = BLState->PendingPhrase; + } + } + ui_EndList(I); + } + ui_EndLabeledDropdown(I); + if (ui_Button(I, MakeString("Say Phrase"))) + { + gs_string DebugStr = PushString(State->Transient, 256); + BLState->NextHotHue = BLState->PendingPhrase; + BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State); + } + + ui_Label(I, MakeString("Phrase Constructor")); + BLState->DebugOverrideHue = ui_ToggleText(I, MakeString("Override Hue"), BLState->DebugOverrideHue); + if (BLState->DebugOverrideHue) + { + phrase_hue PHue = BLState->DebugHue; + PHue.Hue0.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue0"), (r32)PHue.Hue0.HSV.x, 0, 360); + PHue.Hue1.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue1"), (r32)PHue.Hue1.HSV.x, 0, 360); + PHue.Hue2.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue2"), (r32)PHue.Hue2.HSV.x, 0, 360); + PHue.Granularity = (u32)ui_LabeledRangeSlider(I, MakeString("Granularity"), (r32)PHue.Granularity, 0, 5); + PHue.Speed = ui_LabeledRangeSlider(I, MakeString("Speed"), PHue.Speed, 0, 4); - case BlumenDebug_Leds: + gs_string PatternOptions[HuePattern_Count] = {}; + PatternOptions[HuePattern_Patchy] = MakeString("patchy"); + PatternOptions[HuePattern_Wavy] = MakeString("wavy"); + + gs_string CPattern = PatternOptions[PHue.Pattern]; + if (ui_BeginLabeledDropdown(I, MakeString("Pattern"), CPattern)) { - BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds); - - BLState->IgnoreTimeOfDay_LedDimming = ui_ToggleText(I, MakeString("Leds Ignore Time Limit"), BLState->IgnoreTimeOfDay_LedDimming); - - if (ui_BeginLabeledDropdown(I, MakeString("Phrase"), MakeString(BLState->PendingPhrase.Phrase))) + for (u32 i = 0; i < HuePattern_Count; i++) + { + if (ui_Button(I, PatternOptions[i])) { - u32 ListCount = BLState->PhraseHueMap.Count; - ui_BeginList(I, MakeString("Phrase List"), 5, ListCount); - for (u32 i = 0; i < ListCount; i++) - { - gs_string Str = MakeString(BLState->PhraseHueMap.Phrases[i]); - if (ui_Button(I, Str)) - { - BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i); - BLState->DebugHue = BLState->PendingPhrase; - } - } - ui_EndList(I); + PHue.Pattern = i; } - ui_EndLabeledDropdown(I); - if (ui_Button(I, MakeString("Say Phrase"))) - { - gs_string DebugStr = PushString(State->Transient, 256); - BLState->NextHotHue = BLState->PendingPhrase; - BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State); - } - - ui_Label(I, MakeString("Phrase Constructor")); - BLState->DebugOverrideHue = ui_ToggleText(I, MakeString("Override Hue"), BLState->DebugOverrideHue); - if (BLState->DebugOverrideHue) - { - phrase_hue PHue = BLState->DebugHue; - PHue.Hue0.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue0"), (r32)PHue.Hue0.HSV.x, 0, 360); - PHue.Hue1.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue1"), (r32)PHue.Hue1.HSV.x, 0, 360); - PHue.Hue2.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue2"), (r32)PHue.Hue2.HSV.x, 0, 360); - PHue.Granularity = (u32)ui_LabeledRangeSlider(I, MakeString("Granularity"), (r32)PHue.Granularity, 0, 5); - PHue.Speed = ui_LabeledRangeSlider(I, MakeString("Speed"), PHue.Speed, 0, 4); - - gs_string PatternOptions[HuePattern_Count] = {}; - PatternOptions[HuePattern_Patchy] = MakeString("patchy"); - PatternOptions[HuePattern_Wavy] = MakeString("wavy"); - - gs_string CPattern = PatternOptions[PHue.Pattern]; - if (ui_BeginLabeledDropdown(I, MakeString("Pattern"), CPattern)) - { - for (u32 i = 0; i < HuePattern_Count; i++) - { - if (ui_Button(I, PatternOptions[i])) - { - PHue.Pattern = i; - } - } - } - ui_EndLabeledDropdown(I); - - - gs_string AddInOptions[AddIn_Count] = {}; - AddInOptions[AddIn_None] = MakeString("NA"); - AddInOptions[AddIn_Waves] = MakeString("waves"); - AddInOptions[AddIn_Rotary] = MakeString("rotary"); - - gs_string CAddIn = AddInOptions[PHue.AddIn]; - if (ui_BeginLabeledDropdown(I, MakeString("Add In"), CAddIn)) - { - for (u32 i = 0; i < AddIn_Count; i++) - { - if (ui_Button(I, AddInOptions[i])) - { - PHue.AddIn = i; - } - } - } - ui_EndLabeledDropdown(I); - BLState->DebugHue = PHue; - } - - - InterfaceAssert(I->PerFrameMemory); - }break; + } + } + ui_EndLabeledDropdown(I); - case BlumenDebug_Awaken: + + gs_string AddInOptions[AddIn_Count] = {}; + AddInOptions[AddIn_None] = MakeString("NA"); + AddInOptions[AddIn_Waves] = MakeString("waves"); + AddInOptions[AddIn_Rotary] = MakeString("rotary"); + + gs_string CAddIn = AddInOptions[PHue.AddIn]; + if (ui_BeginLabeledDropdown(I, MakeString("Add In"), CAddIn)) { - ui_Label(I, MakeString("Step 1:")); - ui_Label(I, MakeString("Leds off, flowers closed")); - if (ui_Button(I, MakeString("Prepare"))) + for (u32 i = 0; i < AddIn_Count; i++) + { + if (ui_Button(I, AddInOptions[i])) { - // motors closed - blumen_packet M = {}; - M.Type = PacketType_MotorState; - M.MotorPacket.FlowerPositions[0] = MotorState_Closed; - M.MotorPacket.FlowerPositions[1] = MotorState_Closed; - M.MotorPacket.FlowerPositions[2] = MotorState_Closed; - gs_data D = StructToData(&M, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, D); - - // animation - State->AnimationSystem.RepeatMode = AnimationRepeat_Single; - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, - BLState->OffAnimHandle, - VoiceCommandFadeDuration); - - BLState->PatternMode = BlumenPattern_NoControl; - BLState->IgnoreTimeOfDay_LedDimming = true; - BLState->IgnoreTimeOfDay_MotorState = true; + PHue.AddIn = i; } - - ui_Label(I, MakeString("Step 2:")); - if (ui_Button(I, MakeString("Begin Light Show"))) - { - AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, - BLState->AwakenHandle, - VoiceCommandFadeDuration); - } - - ui_Label(I, MakeString("Step 3:")); - if (ui_Button(I, MakeString("Open Flowers"))) - { - // motors closed - blumen_packet M = {}; - M.Type = PacketType_MotorState; - M.MotorPacket.FlowerPositions[0] = MotorState_Open; - M.MotorPacket.FlowerPositions[1] = MotorState_Open; - M.MotorPacket.FlowerPositions[2] = MotorState_Open; - gs_data D = StructToData(&M, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, D); - } - - ui_Label(I, MakeString("Step 4:")); - ui_Label(I, MakeString("Resets Lumenarium")); - if (ui_Button(I, MakeString("Complete"))) - { - BLState->IgnoreTimeOfDay_LedDimming = false; - BLState->IgnoreTimeOfDay_MotorState = false; - BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, - BLState); - } - }break; + } + } + ui_EndLabeledDropdown(I); + BLState->DebugHue = PHue; + } + + + InterfaceAssert(I->PerFrameMemory); + }break; + + case BlumenDebug_Awaken: + { + ui_Label(I, MakeString("Step 1:")); + ui_Label(I, MakeString("Leds off, flowers closed")); + if (ui_Button(I, MakeString("Prepare"))) + { + // motors closed + blumen_packet M = {}; + M.Type = PacketType_MotorState; + M.MotorPacket.FlowerPositions[0] = MotorState_Closed; + M.MotorPacket.FlowerPositions[1] = MotorState_Closed; + M.MotorPacket.FlowerPositions[2] = MotorState_Closed; + gs_data D = StructToData(&M, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, D); - InvalidDefaultCase; - } + // animation + State->AnimationSystem.RepeatMode = AnimationRepeat_Single; + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + BLState->OffAnimHandle, + VoiceCommandFadeDuration); + + BLState->PatternMode = BlumenPattern_NoControl; + BLState->IgnoreTimeOfDay_LedDimming = true; + BLState->IgnoreTimeOfDay_MotorState = true; + } + + ui_Label(I, MakeString("Step 2:")); + if (ui_Button(I, MakeString("Begin Light Show"))) + { + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, + BLState->AwakenHandle, + VoiceCommandFadeDuration); + } + + ui_Label(I, MakeString("Step 3:")); + if (ui_Button(I, MakeString("Open Flowers"))) + { + // motors closed + blumen_packet M = {}; + M.Type = PacketType_MotorState; + M.MotorPacket.FlowerPositions[0] = MotorState_Open; + M.MotorPacket.FlowerPositions[1] = MotorState_Open; + M.MotorPacket.FlowerPositions[2] = MotorState_Open; + gs_data D = StructToData(&M, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, D); + } + + ui_Label(I, MakeString("Step 4:")); + ui_Label(I, MakeString("Resets Lumenarium")); + if (ui_Button(I, MakeString("Complete"))) + { + BLState->IgnoreTimeOfDay_LedDimming = false; + BLState->IgnoreTimeOfDay_MotorState = false; + BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, + BLState); + } + }break; + + InvalidDefaultCase; + } } US_CUSTOM_CLEANUP(BlumenLumen_CustomCleanup) { - blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; - BLState->Running = false; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + BLState->Running = false; } internal user_space_desc BlumenLumen_UserSpaceCreate() { - user_space_desc Result = {}; - Result.LoadPatterns = BlumenLumen_LoadPatterns; - Result.CustomInit = BlumenLumen_CustomInit; - Result.CustomUpdate = BlumenLumen_CustomUpdate; - Result.CustomDebugUI = BlumenLumen_DebugUI; - Result.CustomCleanup = BlumenLumen_CustomCleanup; - return Result; + user_space_desc Result = {}; + Result.LoadPatterns = BlumenLumen_LoadPatterns; + Result.CustomInit = BlumenLumen_CustomInit; + Result.CustomUpdate = BlumenLumen_CustomUpdate; + Result.CustomDebugUI = BlumenLumen_DebugUI; + Result.CustomCleanup = BlumenLumen_CustomCleanup; + return Result; } #define BLUMEN_LUMEN_CPP From 747a2debf292fc466fea30e6c22b616989653392 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sun, 26 Sep 2021 17:25:47 -0400 Subject: [PATCH 105/151] removing false check --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 40 +++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 5b0ea40..c3f0af5 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -706,7 +706,45 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) else if (MotorPos == MotorState_Closed || MotorPos == MotorState_HalfOpen) { - BLState->ShouldDimUpperLeds[i] = false; + bool SendMotorCommand = false; + + u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; + r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; + bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; + + bl_motor_state_value NewMotorState = MotorState_Closed; + bool SendOpen = false; + for (u32 i = 0; i < MotorOpenTimesCount; i++) + { + time_range Range = MotorOpenTimes[i]; + bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); + if (CurrTimeInRange) { + NewMotorState = MotorState_Open; + } + } + + + if (NewMotorState != BLState->LastSendState) + { + ShouldSendCurrentState = true; + } + + + if (ShouldSendCurrentState) + { + BLState->LastSendTime = Context->SystemTime_Current; + BLState->LastSendState = NewMotorState; + + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket.FlowerPositions[0] = NewMotorState; + Packet.MotorPacket.FlowerPositions[1] = NewMotorState; + Packet.MotorPacket.FlowerPositions[2] = NewMotorState; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); + + DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); + } } } From 9e474fe846e86a572a5b6ad5f2a4139c6025b6f6 Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 26 Sep 2021 16:31:55 -0500 Subject: [PATCH 106/151] bug fixing --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 71 ++++++++++++------------ 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index c3f0af5..ae6b201 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -706,45 +706,44 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) else if (MotorPos == MotorState_Closed || MotorPos == MotorState_HalfOpen) { - bool SendMotorCommand = false; - - u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; - r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; - bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; - - bl_motor_state_value NewMotorState = MotorState_Closed; - bool SendOpen = false; - for (u32 i = 0; i < MotorOpenTimesCount; i++) - { - time_range Range = MotorOpenTimes[i]; - bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); - if (CurrTimeInRange) { - NewMotorState = MotorState_Open; - } + bool SendMotorCommand = false; + + u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; + r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds; + bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod; + + bl_motor_state_value NewMotorState = MotorState_Closed; + bool SendOpen = false; + for (u32 j = 0; j < MotorOpenTimesCount; j++) + { + time_range Range = MotorOpenTimes[j]; + bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range); + if (CurrTimeInRange) { + NewMotorState = MotorState_Open; } + } + + + if (NewMotorState != BLState->LastSendState) + { + ShouldSendCurrentState = true; + } + + if (ShouldSendCurrentState) + { + BLState->LastSendTime = Context->SystemTime_Current; + BLState->LastSendState = NewMotorState; + blumen_packet Packet = {}; + Packet.Type = PacketType_MotorState; + Packet.MotorPacket.FlowerPositions[0] = NewMotorState; + Packet.MotorPacket.FlowerPositions[1] = NewMotorState; + Packet.MotorPacket.FlowerPositions[2] = NewMotorState; + gs_data Msg = StructToData(&Packet, blumen_packet); + MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - if (NewMotorState != BLState->LastSendState) - { - ShouldSendCurrentState = true; - } - - - if (ShouldSendCurrentState) - { - BLState->LastSendTime = Context->SystemTime_Current; - BLState->LastSendState = NewMotorState; - - blumen_packet Packet = {}; - Packet.Type = PacketType_MotorState; - Packet.MotorPacket.FlowerPositions[0] = NewMotorState; - Packet.MotorPacket.FlowerPositions[1] = NewMotorState; - Packet.MotorPacket.FlowerPositions[2] = NewMotorState; - gs_data Msg = StructToData(&Packet, blumen_packet); - MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg); - - DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); - } + DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext); + } } } From 3d4bf578a526966a700ecd906cbb5a8df4df74d9 Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 26 Sep 2021 16:41:55 -0500 Subject: [PATCH 107/151] debug --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index ae6b201..17ed7e0 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -510,6 +510,8 @@ BlumenLumen_ApplyNextHotHue(blumen_lumen_state* BLState, context Context, gs_str internal void BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) { + DEBUG_TRACK_FUNCTION; + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; BLState->ShouldUpdateLog = false; @@ -785,31 +787,25 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) if (Blumen_TempShouldDimLeds(BLState)) { OverrideBrightness = HighTemperatureBrightnessPercent; - OutputDebugString("Set Brightness High Temp"); } else { - OverrideBrightness = FullBrightnessPercent; - OutputDebugString("Set Brightness Full"); + OverrideBrightness = FullBrightnessPercent; } TimelineShouldAdvance = true; } - else - { - OutputDebugString("Skipped Time Range Dimming"); - } State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance; BLState->BrightnessPercent = OverrideBrightness; } - else - { - OutputDebugString("Skipped Dimming Altogether"); - } // Dim the leds based on temp data - if (false && !BLState->DEBUG_IgnoreWeatherDimmingLeds) + if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) { + Log_Message(GlobalLogBuffer, "Dimming: %f %d %d\n", + BLState->BrightnessPercent, + State->LedSystem.BuffersCount, + State->LedSystem.LedsCountTotal); for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) { led_buffer Buffer = State->LedSystem.Buffers[i]; From e625c20f0767abc42fe7720414f844ed221316ce Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 26 Sep 2021 16:45:34 -0500 Subject: [PATCH 108/151] logging pixel colors --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 17ed7e0..46b330e 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -802,10 +802,9 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) // Dim the leds based on temp data if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) { - Log_Message(GlobalLogBuffer, "Dimming: %f %d %d\n", - BLState->BrightnessPercent, - State->LedSystem.BuffersCount, - State->LedSystem.LedsCountTotal); + led_buffer B0 = State->LedSystem.Buffers[0]; + pixel P0 = B0.Colors[0]; + for (u32 i = 0; i < State->LedSystem.BuffersCount; i++) { led_buffer Buffer = State->LedSystem.Buffers[i]; @@ -817,6 +816,14 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) Color->B = Color->B * BLState->BrightnessPercent; } } + + led_buffer B1 = State->LedSystem.Buffers[0]; + pixel P1 = B1.Colors[0]; + + Log_Message(GlobalLogBuffer, "Dimming: %f %d %d %d - %d %d %d\n", + BLState->BrightnessPercent, + P0.R, P0.G, P0.B, + P1.R, P1.G, P1.B); } // Send Status Packet From 04b351e79265d5c7f1fd79e85d8049a1f219f7fe Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 26 Sep 2021 16:50:51 -0500 Subject: [PATCH 109/151] trying single threaded --- src/app/foldhaus_platform.h | 206 ++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 0f80d4d..7eec19b 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -15,15 +15,15 @@ struct handle { - u32 Generation; - u32 Index; + u32 Generation; + u32 Index; }; inline bool Handle_IsValid(handle Handle) { - bool Result = (Handle.Generation != 0); - return Result; + bool Result = (Handle.Generation != 0); + return Result; } #include "..\gs_libs\gs_string.h" @@ -40,9 +40,9 @@ global debug_services* GlobalDebugServices; struct platform_network_address { - s32 Family; - u16 Port; - u32 Address; + s32 Family; + u16 Port; + u32 Address; }; typedef s32 platform_socket_handle; @@ -59,16 +59,16 @@ typedef struct context context; typedef void temp_job_req_proc(gs_thread_context* Ctx, u8* Memory); struct temp_job_req { - temp_job_req_proc* Proc; - u8* Memory; + temp_job_req_proc* Proc; + u8* Memory; }; // This isn't necessarily temp but I'm not sure it goes here #define PACKETS_MAX 32 struct packet_ringbuffer { - gs_data Values[PACKETS_MAX]; - u32 ReadHead; - u32 WriteHead; + gs_data Values[PACKETS_MAX]; + u32 ReadHead; + u32 WriteHead; }; #define INITIALIZE_APPLICATION(name) void name(context* Context) @@ -87,9 +87,9 @@ typedef CLEANUP_APPLICATION(cleanup_application); struct window_info { - char* Name; - s32 Width; - s32 Height; + char* Name; + s32 Width; + s32 Height; }; typedef struct window window; @@ -97,38 +97,38 @@ typedef struct window window; #define PLATFORM_MEMORY_NO_ERROR 0 enum platform_memory_error { - PlatformMemory_NoError, - PlatformMemory_FileNotFound, - - PlatformMemory_UnknownError, // You should implement handling this when you see it + PlatformMemory_NoError, + PlatformMemory_FileNotFound, + + PlatformMemory_UnknownError, // You should implement handling this when you see it }; struct data { - u8* Base; - u64 Size; + u8* Base; + u64 Size; }; struct platform_memory_result { - data Data; - platform_memory_error Error; + data Data; + platform_memory_error Error; }; struct system_path { - char* Path; - s32 PathLength; - s32 IndexOfLastSlash; + char* Path; + s32 PathLength; + s32 IndexOfLastSlash; }; struct texture_buffer { - u8* Memory; - s32 Width; - s32 Height; - s32 Pitch; - s32 BytesPerPixel; + u8* Memory; + s32 Width; + s32 Height; + s32 Pitch; + s32 BytesPerPixel; }; #define PLATFORM_GET_GPU_TEXTURE_HANDLE(name) s32 name(u8* Memory, s32 Width, s32 Height) @@ -140,25 +140,25 @@ typedef PLATFORM_GET_SOCKET_HANDLE(platform_get_socket_handle); // Font struct platform_font_info { - s32 PixelHeight; - s32 Ascent, Descent, Leading; - s32 MaxCharWidth; - s32 CodepointStart; - s32 CodepointOnePastLast; + s32 PixelHeight; + s32 Ascent, Descent, Leading; + s32 MaxCharWidth; + s32 CodepointStart; + s32 CodepointOnePastLast; }; enum font_weight { - FontWeight_Invalid = 0, - FontWeight_Thin = 100, - FontWeight_ExtraLight = 200, - FontWeight_Light = 300, - FontWeight_Normal = 400, - FontWeight_Medium = 500, - FontWeight_SemiBold = 600, - FontWeight_Bold = 700, - FontWeight_ExtraBold = 800, - FontWeight_Heavy = 900, + FontWeight_Invalid = 0, + FontWeight_Thin = 100, + FontWeight_ExtraLight = 200, + FontWeight_Light = 300, + FontWeight_Normal = 400, + FontWeight_Medium = 500, + FontWeight_SemiBold = 600, + FontWeight_Bold = 700, + FontWeight_ExtraBold = 800, + FontWeight_Heavy = 900, }; #define GET_FONT_INFO(name) platform_font_info name(char* FontName, s32 PixelHeight, font_weight FontWeight, b32 Italic, b32 Underline, b32 Strikeout) @@ -169,19 +169,19 @@ typedef DRAW_FONT_CODEPOINT(platform_draw_font_codepoint); // Worker Threads -#define PLATFORM_THREAD_COUNT 3 +#define PLATFORM_THREAD_COUNT 0 RESET_WORK_QUEUE(ResetWorkQueue) { - for (u32 i = 0; i < Queue->JobsMax; i++) - { - Queue->Jobs[i].Data = {0}; - Queue->Jobs[i].WorkProc = 0; - } - - Queue->JobsCount = 0; - Queue->NextJobIndex = 0; - Queue->JobsCompleted = 0; + for (u32 i = 0; i < Queue->JobsMax; i++) + { + Queue->Jobs[i].Data = {0}; + Queue->Jobs[i].WorkProc = 0; + } + + Queue->JobsCount = 0; + Queue->NextJobIndex = 0; + Queue->JobsCompleted = 0; } // Time @@ -189,66 +189,66 @@ RESET_WORK_QUEUE(ResetWorkQueue) internal r32 GetSecondsElapsed (s64 Start, s64 End, s64 PerformanceCountFrequency) { - r32 Result = ((r32)(End - Start) / (r32) PerformanceCountFrequency); - return Result; + r32 Result = ((r32)(End - Start) / (r32) PerformanceCountFrequency); + return Result; } typedef struct system_time { - u64 NanosSinceEpoch; - - s32 Year; - s32 Month; - s32 Day; - s32 Hour; // [0:23] - s32 Minute; - s32 Second; + u64 NanosSinceEpoch; + + s32 Year; + s32 Month; + s32 Day; + s32 Hour; // [0:23] + s32 Minute; + s32 Second; } system_time; internal r64 SecondsElapsed(system_time Start, system_time End) { - u64 N = End.NanosSinceEpoch - Start.NanosSinceEpoch; - r64 S = (r64)N * NanosToSeconds; - return S; + u64 N = End.NanosSinceEpoch - Start.NanosSinceEpoch; + r64 S = (r64)N * NanosToSeconds; + return S; } struct context { - gs_thread_context ThreadContext; - - u8* MemoryBase; - u32 MemorySize; - - b32 WindowIsVisible; - rect2 WindowBounds; - r64 TotalTime; - r32 DeltaTime; - mouse_state Mouse; - - // Application Services - initialize_application* InitializeApplication; - reload_static_data* ReloadStaticData; - update_and_render* UpdateAndRender; - cleanup_application* CleanupApplication; - - platform_thread_manager* ThreadManager; - platform_socket_manager* SocketManager; - - // Platform Services - gs_work_queue* GeneralWorkQueue; - - platform_get_gpu_texture_handle* PlatformGetGPUTextureHandle; - platform_get_font_info* PlatformGetFontInfo; - platform_draw_font_codepoint* PlatformDrawFontCodepoint; - - platform_get_socket_handle* PlatformGetSocketHandle; - - system_time SystemTime_Last; - system_time SystemTime_Current; - - // - bool Headless; + gs_thread_context ThreadContext; + + u8* MemoryBase; + u32 MemorySize; + + b32 WindowIsVisible; + rect2 WindowBounds; + r64 TotalTime; + r32 DeltaTime; + mouse_state Mouse; + + // Application Services + initialize_application* InitializeApplication; + reload_static_data* ReloadStaticData; + update_and_render* UpdateAndRender; + cleanup_application* CleanupApplication; + + platform_thread_manager* ThreadManager; + platform_socket_manager* SocketManager; + + // Platform Services + gs_work_queue* GeneralWorkQueue; + + platform_get_gpu_texture_handle* PlatformGetGPUTextureHandle; + platform_get_font_info* PlatformGetFontInfo; + platform_draw_font_codepoint* PlatformDrawFontCodepoint; + + platform_get_socket_handle* PlatformGetSocketHandle; + + system_time SystemTime_Last; + system_time SystemTime_Current; + + // + bool Headless; }; #define FOLDHAUS_PLATFORM_H From a1e6981bf218cb3d8735df684caaff265f0384d0 Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 26 Sep 2021 16:57:15 -0500 Subject: [PATCH 110/151] only applying dimming on frames where the animations were updated --- src/app/engine/animation/foldhaus_animation.h | 906 +++++++++--------- src/app/foldhaus_platform.h | 2 +- src/app/ss_blumen_lumen/blumen_lumen.cpp | 2 +- 3 files changed, 457 insertions(+), 453 deletions(-) diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 8ae3430..f0011f2 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -10,13 +10,13 @@ typedef ANIMATION_PROC(animation_proc); struct frame_range { - s32 Min; - s32 Max; + s32 Min; + s32 Max; }; struct animation_pattern_handle { - s32 IndexPlusOne; + s32 IndexPlusOne; }; // NOTE(pjs): An animation block is a time range paired with an @@ -25,72 +25,72 @@ struct animation_pattern_handle // will run struct animation_block { - frame_range Range; - animation_pattern_handle AnimationProcHandle; - u32 Layer; + frame_range Range; + animation_pattern_handle AnimationProcHandle; + u32 Layer; }; struct animation_block_array { - u32* Generations; - animation_block* Values; - u32 Count; - u32 CountMax; + u32* Generations; + animation_block* Values; + u32 Count; + u32 CountMax; }; enum blend_mode { - BlendMode_Overwrite, - BlendMode_Add, - BlendMode_Multiply, - BlendMode_Count, + BlendMode_Overwrite, + BlendMode_Add, + BlendMode_Multiply, + BlendMode_Count, }; // TODO(pjs): This really doesn't belong here global gs_string BlendModeStrings[] = { - MakeString("Overwrite"), - MakeString("Add"), - MakeString("Multiply"), - MakeString("Count"), + MakeString("Overwrite"), + MakeString("Add"), + MakeString("Multiply"), + MakeString("Count"), }; struct anim_layer { - gs_string Name; - blend_mode BlendMode; + gs_string Name; + blend_mode BlendMode; }; struct anim_layer_array { - anim_layer* Values; - u32 Count; - u32 CountMax; + anim_layer* Values; + u32 Count; + u32 CountMax; }; // NOTE(pjs): An animation is a stack of layers, each of which // is a timeline of animation blocks. struct animation { - gs_string Name; - - anim_layer_array Layers; - animation_block_array Blocks_; - - frame_range PlayableRange; - - // The information / path to the file where this animation is to be saved / where it is loaded from - gs_file_info FileInfo; + gs_string Name; + + anim_layer_array Layers; + animation_block_array Blocks_; + + frame_range PlayableRange; + + // The information / path to the file where this animation is to be saved / where it is loaded from + gs_file_info FileInfo; }; struct animation_handle { - s32 Index; + s32 Index; }; struct animation_handle_array { - u32 Count; - animation_handle* Handles; + u32 Count; + animation_handle* Handles; }; internal animation_handle InvalidAnimHandle () { return { -1 }; } @@ -98,83 +98,84 @@ internal bool IsValid (animation_handle H) { return H.Index >= 0; } internal void Clear (animation_handle* H) { H->Index = -1; } internal bool AnimHandlesAreEqual (animation_handle A, animation_handle B) { - return A.Index == B.Index; + return A.Index == B.Index; } struct animation_array { - animation* Values; - u32 Count; - u32 CountMax; + animation* Values; + u32 Count; + u32 CountMax; }; struct animation_layer_frame { - animation_block Hot; - bool HasHot; - - animation_block NextHot; - bool HasNextHot; - - r32 NextHotOpacity; - - blend_mode BlendMode; + animation_block Hot; + bool HasHot; + + animation_block NextHot; + bool HasNextHot; + + r32 NextHotOpacity; + + blend_mode BlendMode; }; // NOTE(pjs): This is an evaluated frame - across all layers in an // animation, these are the blocks that need to be run struct animation_frame { - animation_layer_frame* Layers; - u32 LayersCount; + animation_layer_frame* Layers; + u32 LayersCount; }; enum animation_repeat_mode { - AnimationRepeat_Single, - AnimationRepeat_Loop, - AnimationRepeat_Invalid, + AnimationRepeat_Single, + AnimationRepeat_Loop, + AnimationRepeat_Invalid, }; global gs_const_string AnimationRepeatModeStrings[] = { - ConstString("Repeat Single"), - ConstString("Loop"), - ConstString("Invalid"), + ConstString("Repeat Single"), + ConstString("Loop"), + ConstString("Invalid"), }; struct animation_fade_group { - animation_handle From; - animation_handle To; - r32 FadeElapsed; - r32 FadeDuration; + animation_handle From; + animation_handle To; + r32 FadeElapsed; + r32 FadeDuration; }; #define ANIMATION_SYSTEM_LAYERS_MAX 128 #define ANIMATION_SYSTEM_BLOCKS_MAX 128 struct animation_system { - gs_memory_arena* Storage; - animation_array Animations; - - animation_repeat_mode RepeatMode; - animation_handle_array Playlist; - u32 PlaylistAt; - r32 PlaylistFadeTime; - - // NOTE(Peter): The frame currently being displayed/processed. you - // can see which frame you're on by looking at the time slider on the timeline - // panel - animation_fade_group ActiveFadeGroup; - - r32 SecondsOnCurrentFrame; - s32 CurrentFrame; - s32 LastUpdatedFrame; - r32 SecondsPerFrame; - b32 TimelineShouldAdvance; - - // Settings - bool Multithreaded; + gs_memory_arena* Storage; + animation_array Animations; + + animation_repeat_mode RepeatMode; + animation_handle_array Playlist; + u32 PlaylistAt; + r32 PlaylistFadeTime; + + // NOTE(Peter): The frame currently being displayed/processed. you + // can see which frame you're on by looking at the time slider on the timeline + // panel + animation_fade_group ActiveFadeGroup; + + r32 SecondsOnCurrentFrame; + s32 CurrentFrame; + s32 LastUpdatedFrame; + r32 SecondsPerFrame; + b32 TimelineShouldAdvance; + u32 UpdatesThisFrame; + + // Settings + bool Multithreaded; }; // NOTE(pjs): A Pattern is a named procedure which can be used as @@ -182,70 +183,70 @@ struct animation_system // and blended via layers to create an animation struct animation_pattern { - char* Name; - s32 NameLength; - animation_proc* Proc; - bool Multithreaded; + char* Name; + s32 NameLength; + animation_proc* Proc; + bool Multithreaded; }; struct animation_pattern_array { - animation_pattern* Values; - u32 Count; - u32 CountMax; + animation_pattern* Values; + u32 Count; + u32 CountMax; }; // Serialization enum animation_field { - AnimField_FileIdent, - AnimField_AnimName, - AnimField_LayersCount, - AnimField_BlocksCount, - - AnimField_PlayableRange, - AnimField_PlayableRangeMin, - AnimField_PlayableRangeMax, - - AnimField_LayersArray, - AnimField_Layer, - AnimField_LayerName, - AnimField_LayerBlendMode, - - AnimField_BlocksArray, - AnimField_Block, - AnimField_BlockFrameRange, - AnimField_BlockFrameRangeMin, - AnimField_BlockFrameRangeMax, - AnimField_BlockLayerIndex, - AnimField_BlockAnimName, - - AnimField_Count, + AnimField_FileIdent, + AnimField_AnimName, + AnimField_LayersCount, + AnimField_BlocksCount, + + AnimField_PlayableRange, + AnimField_PlayableRangeMin, + AnimField_PlayableRangeMax, + + AnimField_LayersArray, + AnimField_Layer, + AnimField_LayerName, + AnimField_LayerBlendMode, + + AnimField_BlocksArray, + AnimField_Block, + AnimField_BlockFrameRange, + AnimField_BlockFrameRangeMin, + AnimField_BlockFrameRangeMax, + AnimField_BlockLayerIndex, + AnimField_BlockAnimName, + + AnimField_Count, }; global gs_const_string AnimationFieldStrings[] = { - ConstString("lumenarium_animation_file"), // AnimField_FileIdent - ConstString("animation_name"),// AnimField_AnimName - ConstString("layers_count"),// AnimField_LayersCount - ConstString("blocks_count"),// AnimField_BlocksCount - - ConstString("playable_range"),// AnimField_PlayableRange - ConstString("min"),// AnimField_PlayableRangeMin - ConstString("max"),// AnimField_PlayableRangeMax - - ConstString("layers"),// AnimField_LayersArray - ConstString("layer"),// AnimField_Layer - ConstString("name"),// AnimField_LayerName - ConstString("blend"),// AnimField_LayerBlendMode - - ConstString("blocks"),// AnimField_BlocksArray - ConstString("block"),// AnimField_Block - ConstString("frame_range"),// AnimField_BlockFrameRange - ConstString("min"),// AnimField_BlockFrameRangeMin - ConstString("max"),// AnimField_BlockFrameRangeMax - ConstString("layer_index"),// AnimField_BlockLayerIndex - ConstString("animation_name"),// AnimField_BlockAnimName + ConstString("lumenarium_animation_file"), // AnimField_FileIdent + ConstString("animation_name"),// AnimField_AnimName + ConstString("layers_count"),// AnimField_LayersCount + ConstString("blocks_count"),// AnimField_BlocksCount + + ConstString("playable_range"),// AnimField_PlayableRange + ConstString("min"),// AnimField_PlayableRangeMin + ConstString("max"),// AnimField_PlayableRangeMax + + ConstString("layers"),// AnimField_LayersArray + ConstString("layer"),// AnimField_Layer + ConstString("name"),// AnimField_LayerName + ConstString("blend"),// AnimField_LayerBlendMode + + ConstString("blocks"),// AnimField_BlocksArray + ConstString("block"),// AnimField_Block + ConstString("frame_range"),// AnimField_BlockFrameRange + ConstString("min"),// AnimField_BlockFrameRangeMin + ConstString("max"),// AnimField_BlockFrameRangeMax + ConstString("layer_index"),// AnimField_BlockLayerIndex + ConstString("animation_name"),// AnimField_BlockAnimName }; @@ -256,10 +257,10 @@ global gs_const_string AnimationFieldStrings[] = { internal animation_pattern_array Patterns_Create(gs_memory_arena* Arena, s32 CountMax) { - animation_pattern_array Result = {0}; - Result.CountMax = CountMax; - Result.Values = PushArray(Arena, animation_pattern, Result.CountMax); - return Result; + animation_pattern_array Result = {0}; + Result.CountMax = CountMax; + Result.Values = PushArray(Arena, animation_pattern, Result.CountMax); + return Result; } #define PATTERN_MULTITHREADED true @@ -271,43 +272,43 @@ Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)) internal void Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength, bool Multithreaded) { - Assert(Array->Count < Array->CountMax); - - animation_pattern Pattern = {0}; - Pattern.Name = Name; - Pattern.NameLength = NameLength; - Pattern.Proc = Proc; - Pattern.Multithreaded = Multithreaded; - - Array->Values[Array->Count++] = Pattern; + Assert(Array->Count < Array->CountMax); + + animation_pattern Pattern = {0}; + Pattern.Name = Name; + Pattern.NameLength = NameLength; + Pattern.Proc = Proc; + Pattern.Multithreaded = Multithreaded; + + Array->Values[Array->Count++] = Pattern; } internal animation_pattern_handle Patterns_IndexToHandle(s32 Index) { - animation_pattern_handle Result = {}; - Result.IndexPlusOne = Index + 1; - return Result; + animation_pattern_handle Result = {}; + Result.IndexPlusOne = Index + 1; + return Result; } internal bool IsValid(animation_pattern_handle Handle) { - bool Result = Handle.IndexPlusOne > 0; - return Result; + bool Result = Handle.IndexPlusOne > 0; + return Result; } internal animation_pattern Patterns_GetPattern(animation_pattern_array Patterns, animation_pattern_handle Handle) { - animation_pattern Result = {0}; - if (Handle.IndexPlusOne > 0) - { - u32 Index = Handle.IndexPlusOne - 1; - Assert(Index < Patterns.Count); - Result = Patterns.Values[Index]; - } - return Result; + animation_pattern Result = {0}; + if (Handle.IndexPlusOne > 0) + { + u32 Index = Handle.IndexPlusOne - 1; + Assert(Index < Patterns.Count); + Result = Patterns.Values[Index]; + } + return Result; } ////////////////////////// @@ -317,44 +318,44 @@ Patterns_GetPattern(animation_pattern_array Patterns, animation_pattern_handle H internal animation_block_array AnimBlockArray_Create(gs_memory_arena* Storage, u32 CountMax) { - animation_block_array Result = {0}; - Result.CountMax = Max(CountMax, 32); - Result.Values = PushArray(Storage, animation_block, Result.CountMax); - Result.Generations = PushArray(Storage, u32, Result.CountMax); - return Result; + animation_block_array Result = {0}; + Result.CountMax = Max(CountMax, 32); + Result.Values = PushArray(Storage, animation_block, Result.CountMax); + Result.Generations = PushArray(Storage, u32, Result.CountMax); + return Result; } internal handle AnimBlockArray_Push(animation_block_array* Array, animation_block Value) { - Assert(Array->Count < Array->CountMax); - handle Result = {0}; - Result.Index = Array->Count++; - // NOTE(pjs): pre-increment so that generation 0 is always invalid - Result.Generation = ++Array->Generations[Result.Index]; - - Array->Values[Result.Index] = Value; - - return Result; + Assert(Array->Count < Array->CountMax); + handle Result = {0}; + Result.Index = Array->Count++; + // NOTE(pjs): pre-increment so that generation 0 is always invalid + Result.Generation = ++Array->Generations[Result.Index]; + + Array->Values[Result.Index] = Value; + + return Result; } internal void AnimBlockArray_Remove(animation_block_array* Array, handle Handle) { - Assert(Handle.Index < Array->Count); - Assert(Handle_IsValid(Handle)); - Array->Generations[Handle.Index]++; + Assert(Handle.Index < Array->Count); + Assert(Handle_IsValid(Handle)); + Array->Generations[Handle.Index]++; } internal void AnimBlockArray_RemoveAt(animation_block_array* Array, u32 Index) { - Assert(Index < Array->Count); - - handle Handle = {}; - Handle.Index = Index; - Handle.Generation = Array->Generations[Index]; - AnimBlockArray_Remove(Array, Handle); + Assert(Index < Array->Count); + + handle Handle = {}; + Handle.Index = Index; + Handle.Generation = Array->Generations[Index]; + AnimBlockArray_Remove(Array, Handle); } ////////////////////////// @@ -364,29 +365,29 @@ AnimBlockArray_RemoveAt(animation_block_array* Array, u32 Index) internal anim_layer_array AnimLayerArray_Create(gs_memory_arena* Storage, u32 CountMax) { - anim_layer_array Result = {0}; - Result.CountMax = Max(CountMax, 32); - Result.Values = PushArray(Storage, anim_layer, Result.CountMax); - return Result; + anim_layer_array Result = {0}; + Result.CountMax = Max(CountMax, 32); + Result.Values = PushArray(Storage, anim_layer, Result.CountMax); + return Result; } internal u32 AnimLayerArray_Push(anim_layer_array* Array, anim_layer Value) { - Assert(Array->Count < Array->CountMax); - u32 Index = Array->Count++; - Array->Values[Index] = Value; - return Index; + Assert(Array->Count < Array->CountMax); + u32 Index = Array->Count++; + Array->Values[Index] = Value; + return Index; } internal void AnimLayerArray_Remove(anim_layer_array* Array, u32 Index) { - Assert(Index < Array->Count); - for (u32 i = Index; i < Array->Count - 1; i++) - { - Array->Values[i] = Array->Values[i + 1]; - } + Assert(Index < Array->Count); + for (u32 i = Index; i < Array->Count - 1; i++) + { + Array->Values[i] = Array->Values[i + 1]; + } } ////////////////////////// @@ -396,41 +397,41 @@ AnimLayerArray_Remove(anim_layer_array* Array, u32 Index) internal animation_array AnimationArray_Create(gs_memory_arena* Storage, u32 CountMax) { - animation_array Result = {0}; - Result.CountMax = CountMax; - Result.Values = PushArray(Storage, animation, Result.CountMax); - return Result; + animation_array Result = {0}; + Result.CountMax = CountMax; + Result.Values = PushArray(Storage, animation, Result.CountMax); + return Result; } internal animation_handle AnimationArray_Push(animation_array* Array, animation Value) { - Assert(Array->Count < Array->CountMax); - animation_handle Result = {0}; - Result.Index = Array->Count++; - Array->Values[Result.Index] = Value; - return Result; + Assert(Array->Count < Array->CountMax); + animation_handle Result = {0}; + Result.Index = Array->Count++; + Array->Values[Result.Index] = Value; + return Result; } internal animation* AnimationArray_Get(animation_array Array, animation_handle Handle) { - DEBUG_TRACK_FUNCTION; - - animation* Result = 0; - if (IsValid(Handle) && Handle.Index < (s32)Array.Count) - { - Result = Array.Values + Handle.Index; - } - return Result; + DEBUG_TRACK_FUNCTION; + + animation* Result = 0; + if (IsValid(Handle) && Handle.Index < (s32)Array.Count) + { + Result = Array.Values + Handle.Index; + } + return Result; } internal animation* AnimationArray_GetSafe(animation_array Array, animation_handle Handle) { - Assert(IsValid(Handle)); - Assert(Handle.Index < (s32)Array.Count); - return AnimationArray_Get(Array, Handle); + Assert(IsValid(Handle)); + Assert(Handle.Index < (s32)Array.Count); + return AnimationArray_Get(Array, Handle); } ////////////////////////// @@ -439,103 +440,103 @@ AnimationArray_GetSafe(animation_array Array, animation_handle Handle) typedef struct animation_desc { - u32 NameSize; - char* Name; - - u32 LayersCount; - u32 BlocksCount; - - u32 MinFrames; - u32 MaxFrames; + u32 NameSize; + char* Name; + + u32 LayersCount; + u32 BlocksCount; + + u32 MinFrames; + u32 MaxFrames; } animation_desc; internal animation Animation_Create(animation_desc Desc, animation_system* System) { - animation Result = {}; - u32 NameLen = Desc.NameSize; - if (Desc.Name) - { - NameLen = Max(CStringLength(Desc.Name), NameLen); - Result.Name = PushStringF(System->Storage, NameLen, "%s", Desc.Name); - } else { - Result.Name = PushStringF(System->Storage, NameLen, "[New Animation]"); - } - - Result.Layers = AnimLayerArray_Create(System->Storage, Desc.LayersCount); - Result.Blocks_ = AnimBlockArray_Create(System->Storage, Desc.BlocksCount); - Result.PlayableRange.Min = Desc.MinFrames; - Result.PlayableRange.Max = Desc.MaxFrames; - return Result; + animation Result = {}; + u32 NameLen = Desc.NameSize; + if (Desc.Name) + { + NameLen = Max(CStringLength(Desc.Name), NameLen); + Result.Name = PushStringF(System->Storage, NameLen, "%s", Desc.Name); + } else { + Result.Name = PushStringF(System->Storage, NameLen, "[New Animation]"); + } + + Result.Layers = AnimLayerArray_Create(System->Storage, Desc.LayersCount); + Result.Blocks_ = AnimBlockArray_Create(System->Storage, Desc.BlocksCount); + Result.PlayableRange.Min = Desc.MinFrames; + Result.PlayableRange.Max = Desc.MaxFrames; + return Result; } internal handle Animation_AddBlock(animation* Animation, u32 StartFrame, s32 EndFrame, animation_pattern_handle AnimationProcHandle, u32 LayerIndex) { - Assert(LayerIndex < Animation->Layers.Count); - - animation_block NewBlock = {0}; - NewBlock.Range.Min = StartFrame; - NewBlock.Range.Max = EndFrame; - NewBlock.AnimationProcHandle = AnimationProcHandle; - NewBlock.Layer = LayerIndex; - - handle Handle = AnimBlockArray_Push(&Animation->Blocks_, NewBlock); - return Handle; + Assert(LayerIndex < Animation->Layers.Count); + + animation_block NewBlock = {0}; + NewBlock.Range.Min = StartFrame; + NewBlock.Range.Max = EndFrame; + NewBlock.AnimationProcHandle = AnimationProcHandle; + NewBlock.Layer = LayerIndex; + + handle Handle = AnimBlockArray_Push(&Animation->Blocks_, NewBlock); + return Handle; } internal void Animation_RemoveBlock(animation* Animation, handle AnimHandle) { - AnimBlockArray_Remove(&Animation->Blocks_, AnimHandle); + AnimBlockArray_Remove(&Animation->Blocks_, AnimHandle); } internal animation_block* Animation_GetBlockFromHandle(animation* Animation, handle AnimHandle) { - animation_block* Result = 0; - - if (AnimHandle.Generation != 0 && - Animation->Blocks_.Generations[AnimHandle.Index] == AnimHandle.Generation) - { - Result = Animation->Blocks_.Values + AnimHandle.Index; - } - - return Result; + animation_block* Result = 0; + + if (AnimHandle.Generation != 0 && + Animation->Blocks_.Generations[AnimHandle.Index] == AnimHandle.Generation) + { + Result = Animation->Blocks_.Values + AnimHandle.Index; + } + + return Result; } internal u32 Animation_AddLayer(animation* Animation, anim_layer Layer) { - return AnimLayerArray_Push(&Animation->Layers, Layer); + return AnimLayerArray_Push(&Animation->Layers, Layer); } internal u32 Animation_AddLayer (animation* Animation, gs_string Name, blend_mode BlendMode, animation_system* System) { - anim_layer NewLayer = {0}; - NewLayer.Name = PushStringF(System->Storage, 256, "%S", Name); - NewLayer.BlendMode = BlendMode; - - return Animation_AddLayer(Animation, NewLayer); + anim_layer NewLayer = {0}; + NewLayer.Name = PushStringF(System->Storage, 256, "%S", Name); + NewLayer.BlendMode = BlendMode; + + return Animation_AddLayer(Animation, NewLayer); } internal void Animation_RemoveLayer (animation* Animation, u32 LayerIndex) { - AnimLayerArray_Remove(&Animation->Layers, LayerIndex); - for (u32 i = Animation->Blocks_.Count - 1; i >= 0; i--) + AnimLayerArray_Remove(&Animation->Layers, LayerIndex); + for (u32 i = Animation->Blocks_.Count - 1; i >= 0; i--) + { + animation_block* Block = Animation->Blocks_.Values + i; + if (Block->Layer > LayerIndex) { - animation_block* Block = Animation->Blocks_.Values + i; - if (Block->Layer > LayerIndex) - { - Block->Layer -= 1; - } - else if (Block->Layer == LayerIndex) - { - AnimBlockArray_RemoveAt(&Animation->Blocks_, i); - } + Block->Layer -= 1; } + else if (Block->Layer == LayerIndex) + { + AnimBlockArray_RemoveAt(&Animation->Blocks_, i); + } + } } ////////////////////////// @@ -545,59 +546,59 @@ Animation_RemoveLayer (animation* Animation, u32 LayerIndex) internal u32 SecondsToFrames(r32 Seconds, animation_system System) { - u32 Result = Seconds * (1.0f / System.SecondsPerFrame); - return Result; + u32 Result = Seconds * (1.0f / System.SecondsPerFrame); + return Result; } inline frame_range FrameRange_Overlap(frame_range A, frame_range B) { - frame_range Result = {}; - + frame_range Result = {}; + } inline bool FrameIsInRange(frame_range Range, s32 Frame) { - bool Result = (Frame >= Range.Min) && (Frame <= Range.Max); - return Result; + bool Result = (Frame >= Range.Min) && (Frame <= Range.Max); + return Result; } internal u32 GetFrameCount(frame_range Range) { - u32 Result = (u32)Max(0, Range.Max - Range.Min); - return Result; + u32 Result = (u32)Max(0, Range.Max - Range.Min); + return Result; } internal r32 FrameToPercentRange(s32 Frame, frame_range Range) { - r32 Result = (r32)(Frame - Range.Min); - Result = Result / GetFrameCount(Range); - return Result; + r32 Result = (r32)(Frame - Range.Min); + Result = Result / GetFrameCount(Range); + return Result; } internal s32 PercentToFrameInRange(r32 Percent, frame_range Range) { - s32 Result = Range.Min + (s32)(Percent * GetFrameCount(Range)); - return Result; + s32 Result = Range.Min + (s32)(Percent * GetFrameCount(Range)); + return Result; } internal s32 ClampFrameToRange(s32 Frame, frame_range Range) { - s32 Result = Frame; - if (Result < Range.Min) - { - Result = Range.Min; - } - else if (Result > Range.Max) - { - Result = Range.Max; - } - return Result; + s32 Result = Frame; + if (Result < Range.Min) + { + Result = Range.Min; + } + else if (Result > Range.Max) + { + Result = Range.Max; + } + return Result; } // Blocks @@ -609,84 +610,84 @@ ClampFrameToRange(s32 Frame, frame_range Range) internal bool AnimationFadeGroup_ShouldRender (animation_fade_group FadeGroup) { - return IsValid(FadeGroup.From); + return IsValid(FadeGroup.From); } internal void AnimationFadeGroup_Advance(animation_fade_group* Group) { - Group->From = Group->To; - Clear(&Group->To); - Group->FadeElapsed = 0; - Group->FadeDuration = 0; + Group->From = Group->To; + Clear(&Group->To); + Group->FadeElapsed = 0; + Group->FadeDuration = 0; } internal void AnimationFadeGroup_Update(animation_fade_group* Group, r32 DeltaTime) { - if (IsValid(Group->To)) + if (IsValid(Group->To)) + { + r32 FadeBefore = Group->FadeElapsed; + Group->FadeElapsed += DeltaTime; + + if (Group->FadeElapsed >= Group->FadeDuration) { - r32 FadeBefore = Group->FadeElapsed; - Group->FadeElapsed += DeltaTime; - - if (Group->FadeElapsed >= Group->FadeDuration) - { - AnimationFadeGroup_Advance(Group); - } + AnimationFadeGroup_Advance(Group); } + } } internal void AnimationFadeGroup_FadeTo(animation_fade_group* Group, animation_handle To, r32 Duration) { - if (IsValid(Group->From)) + if (IsValid(Group->From)) + { + // complete current fade if there is one in progress + if (IsValid(Group->To)) { - // complete current fade if there is one in progress - if (IsValid(Group->To)) - { - AnimationFadeGroup_Advance(Group); - } - - Group->To = To; - Group->FadeDuration = Duration; - } - else - { - Group->From = To; + AnimationFadeGroup_Advance(Group); } + + Group->To = To; + Group->FadeDuration = Duration; + } + else + { + Group->From = To; + } } // System struct animation_system_desc { - gs_memory_arena* Storage; - u32 AnimArrayCount; - r32 SecondsPerFrame; + gs_memory_arena* Storage; + u32 AnimArrayCount; + r32 SecondsPerFrame; }; internal animation_system AnimationSystem_Init(animation_system_desc Desc) { - animation_system Result = {}; - Result.Storage = Desc.Storage; - Result.Animations = AnimationArray_Create(Result.Storage, Desc.AnimArrayCount); - Result.SecondsPerFrame = Desc.SecondsPerFrame; - - Clear(&Result.ActiveFadeGroup.From); - Clear(&Result.ActiveFadeGroup.To); - Result.ActiveFadeGroup.FadeElapsed = 0; - - // Settings - Result.Multithreaded = false; - - return Result; + animation_system Result = {}; + Result.Storage = Desc.Storage; + Result.Animations = AnimationArray_Create(Result.Storage, Desc.AnimArrayCount); + Result.SecondsPerFrame = Desc.SecondsPerFrame; + + Clear(&Result.ActiveFadeGroup.From); + Clear(&Result.ActiveFadeGroup.To); + Result.ActiveFadeGroup.FadeElapsed = 0; + + // Settings + Result.Multithreaded = false; + + return Result; } internal animation* AnimationSystem_GetActiveAnimation(animation_system* System) { - return AnimationArray_Get(System->Animations, System->ActiveFadeGroup.From); + return AnimationArray_Get(System->Animations, System->ActiveFadeGroup.From); } internal animation_frame @@ -694,145 +695,148 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, animation* Animation, gs_memory_arena* Arena) { - DEBUG_TRACK_FUNCTION; + DEBUG_TRACK_FUNCTION; + + animation_frame Result = {0}; + Result.LayersCount = Animation->Layers.Count; + Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount); + ZeroArray(Result.Layers, animation_layer_frame, Result.LayersCount); + + for (u32 l = 0; l < Animation->Layers.Count; l++) + { + animation_layer_frame* Layer = Result.Layers + l; + Layer->BlendMode = Animation->Layers.Values[l].BlendMode; + } + + for (u32 i = 0; i < Animation->Blocks_.Count; i++) + { + animation_block Block = Animation->Blocks_.Values[i]; - animation_frame Result = {0}; - Result.LayersCount = Animation->Layers.Count; - Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount); - ZeroArray(Result.Layers, animation_layer_frame, Result.LayersCount); - - for (u32 l = 0; l < Animation->Layers.Count; l++) + if (FrameIsInRange(Block.Range, System->CurrentFrame)) { - animation_layer_frame* Layer = Result.Layers + l; - Layer->BlendMode = Animation->Layers.Values[l].BlendMode; - } - - for (u32 i = 0; i < Animation->Blocks_.Count; i++) - { - animation_block Block = Animation->Blocks_.Values[i]; + animation_layer_frame* Layer = Result.Layers + Block.Layer; + if (Layer->HasHot) + { + // NOTE(pjs): With current implementation, we don't allow + // animations to hvae more than 2 concurrent blocks in the + // timeline + Assert(!Layer->HasNextHot); - if (FrameIsInRange(Block.Range, System->CurrentFrame)) + // NOTE(pjs): Make sure that Hot comes before NextHot + if (Layer->Hot.Range.Min < Block.Range.Min) { - animation_layer_frame* Layer = Result.Layers + Block.Layer; - if (Layer->HasHot) - { - // NOTE(pjs): With current implementation, we don't allow - // animations to hvae more than 2 concurrent blocks in the - // timeline - Assert(!Layer->HasNextHot); - - // NOTE(pjs): Make sure that Hot comes before NextHot - if (Layer->Hot.Range.Min < Block.Range.Min) - { - Layer->NextHot = Block; - } - else - { - Layer->NextHot = Layer->Hot; - Layer->Hot = Block; - } - Layer->HasNextHot = true; - - frame_range BlendRange = {}; - BlendRange.Min = Layer->NextHot.Range.Min; - BlendRange.Max = Layer->Hot.Range.Max; - Layer->NextHotOpacity = FrameToPercentRange(System->CurrentFrame, BlendRange); - } - else - { - Layer->Hot = Block; - Layer->NextHotOpacity = 0.0f; - Layer->HasHot = true; - } + Layer->NextHot = Block; } + else + { + Layer->NextHot = Layer->Hot; + Layer->Hot = Block; + } + Layer->HasNextHot = true; + + frame_range BlendRange = {}; + BlendRange.Min = Layer->NextHot.Range.Min; + BlendRange.Max = Layer->Hot.Range.Max; + Layer->NextHotOpacity = FrameToPercentRange(System->CurrentFrame, BlendRange); + } + else + { + Layer->Hot = Block; + Layer->NextHotOpacity = 0.0f; + Layer->HasHot = true; + } } - - return Result; + } + + return Result; } internal void AnimationSystem_Update(animation_system* System, r32 DeltaTime) { - if (!System->TimelineShouldAdvance) { return; } - if (!AnimationFadeGroup_ShouldRender(System->ActiveFadeGroup)) { return; } - - AnimationFadeGroup_Update(&System->ActiveFadeGroup, DeltaTime); - - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - if (ActiveAnim) + if (!System->TimelineShouldAdvance) { return; } + if (!AnimationFadeGroup_ShouldRender(System->ActiveFadeGroup)) { return; } + + System->UpdatesThisFrame = 0; + + AnimationFadeGroup_Update(&System->ActiveFadeGroup, DeltaTime); + + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); + if (ActiveAnim) + { + System->SecondsOnCurrentFrame += DeltaTime; + while (System->SecondsOnCurrentFrame > System->SecondsPerFrame) { - System->SecondsOnCurrentFrame += DeltaTime; - while (System->SecondsOnCurrentFrame > System->SecondsPerFrame) - { - System->CurrentFrame += 1; - System->SecondsOnCurrentFrame -= System->SecondsPerFrame; - } - - // Loop back to the beginning - if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) - { - // NOTE(PS): There's no long term reason why this needs to be true - // but I don't want to implement dealing with PlayableRanges that - // don't start at zero right now becuse there's literally no reason - // I can think of where that's useful. - Assert(ActiveAnim->PlayableRange.Min == 0); - - s32 FramesPastEnd = System->CurrentFrame; - while (FramesPastEnd > ActiveAnim->PlayableRange.Max) - { - FramesPastEnd -= ActiveAnim->PlayableRange.Max; - } - - switch (System->RepeatMode) - { - case AnimationRepeat_Single: - { - System->CurrentFrame = 0; - }break; - - case AnimationRepeat_Loop: - { - Assert(System->Playlist.Count > 0); - u32 NextIndex = System->PlaylistAt; - System->PlaylistAt = (System->PlaylistAt + 1) % System->Playlist.Count; - animation_handle Next = System->Playlist.Handles[NextIndex]; - - AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup, - Next, - System->PlaylistFadeTime); - System->CurrentFrame = 0; - }break; - - InvalidDefaultCase; - } - } + System->CurrentFrame += 1; + System->SecondsOnCurrentFrame -= System->SecondsPerFrame; + System->UpdatesThisFrame += 1; } + + // Loop back to the beginning + if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) + { + // NOTE(PS): There's no long term reason why this needs to be true + // but I don't want to implement dealing with PlayableRanges that + // don't start at zero right now becuse there's literally no reason + // I can think of where that's useful. + Assert(ActiveAnim->PlayableRange.Min == 0); + + s32 FramesPastEnd = System->CurrentFrame; + while (FramesPastEnd > ActiveAnim->PlayableRange.Max) + { + FramesPastEnd -= ActiveAnim->PlayableRange.Max; + } + + switch (System->RepeatMode) + { + case AnimationRepeat_Single: + { + System->CurrentFrame = 0; + }break; + + case AnimationRepeat_Loop: + { + Assert(System->Playlist.Count > 0); + u32 NextIndex = System->PlaylistAt; + System->PlaylistAt = (System->PlaylistAt + 1) % System->Playlist.Count; + animation_handle Next = System->Playlist.Handles[NextIndex]; + + AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup, + Next, + System->PlaylistFadeTime); + System->CurrentFrame = 0; + }break; + + InvalidDefaultCase; + } + } + } } internal void AnimationSystem_FadeToPlaylist(animation_system* System, animation_handle_array Playlist) { - System->Playlist = Playlist; - System->PlaylistAt = 0; - - if (System->Playlist.Count > 0) - { - AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup, Playlist.Handles[0], System->PlaylistFadeTime); - } + System->Playlist = Playlist; + System->PlaylistAt = 0; + + if (System->Playlist.Count > 0) + { + AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup, Playlist.Handles[0], System->PlaylistFadeTime); + } } inline bool AnimationSystem_NeedsRender(animation_system System) { - bool Result = (System.CurrentFrame != System.LastUpdatedFrame); - return Result; + bool Result = (System.CurrentFrame != System.LastUpdatedFrame); + return Result; } inline r32 AnimationSystem_GetCurrentTime(animation_system System) { - r32 Result = System.CurrentFrame * System.SecondsPerFrame; - return Result; + r32 Result = System.CurrentFrame * System.SecondsPerFrame; + return Result; } #define FOLDHAUS_ANIMATION diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 7eec19b..14e1ae5 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -169,7 +169,7 @@ typedef DRAW_FONT_CODEPOINT(platform_draw_font_codepoint); // Worker Threads -#define PLATFORM_THREAD_COUNT 0 +#define PLATFORM_THREAD_COUNT 3 RESET_WORK_QUEUE(ResetWorkQueue) { diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 46b330e..cac0548 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -800,7 +800,7 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } // Dim the leds based on temp data - if (!BLState->DEBUG_IgnoreWeatherDimmingLeds) + if (State->AnimationSystem.UpdatesThisFrame > 0 && !BLState->DEBUG_IgnoreWeatherDimmingLeds) { led_buffer B0 = State->LedSystem.Buffers[0]; pixel P0 = B0.Colors[0]; From f99db5accad5d2b14e24aa2779845ac2dd0248b7 Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 26 Sep 2021 16:58:38 -0500 Subject: [PATCH 111/151] Removing debug logging --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index cac0548..d849120 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -819,11 +819,6 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) led_buffer B1 = State->LedSystem.Buffers[0]; pixel P1 = B1.Colors[0]; - - Log_Message(GlobalLogBuffer, "Dimming: %f %d %d %d - %d %d %d\n", - BLState->BrightnessPercent, - P0.R, P0.G, P0.B, - P1.R, P1.G, P1.B); } // Send Status Packet From e1d652d88442dc87a3e9f2e339d5650908ba9ec4 Mon Sep 17 00:00:00 2001 From: PS Date: Tue, 28 Sep 2021 11:42:16 -0500 Subject: [PATCH 112/151] Leds are always off when BrigthnessPercent is zero --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index d849120..c9d1a52 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -800,7 +800,10 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) } // Dim the leds based on temp data - if (State->AnimationSystem.UpdatesThisFrame > 0 && !BLState->DEBUG_IgnoreWeatherDimmingLeds) + b8 ShouldDim = (State->AnimationSystem.UpdatesThisFrame > 0); + if (BLState->BrightnessPercent == 0) ShouldDim = true; + if (BLState->DEBUG_IgnoreWeatherDimmingLeds) ShouldDim = false; + if (ShouldDim) { led_buffer B0 = State->LedSystem.Buffers[0]; pixel P0 = B0.Colors[0]; From bb9c395388cd05adb9a12ce1224b61f2a65f10bc Mon Sep 17 00:00:00 2001 From: PS Date: Tue, 28 Sep 2021 19:33:38 -0500 Subject: [PATCH 113/151] turning upper leds back on when the flower opens --- src/app/ss_blumen_lumen/blumen_lumen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index c9d1a52..08ac0e0 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -708,6 +708,8 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) else if (MotorPos == MotorState_Closed || MotorPos == MotorState_HalfOpen) { + BLState->ShouldDimUpperLeds[i] = false; + bool SendMotorCommand = false; u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch; From 5838345c095038ef2c7eb4de7eda53608b4f37e4 Mon Sep 17 00:00:00 2001 From: PS Date: Mon, 21 Mar 2022 21:11:17 +0100 Subject: [PATCH 114/151] admin --- admin.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 admin.txt diff --git a/admin.txt b/admin.txt new file mode 100644 index 0000000..8586538 --- /dev/null +++ b/admin.txt @@ -0,0 +1,22 @@ +# Project Admin Stuff + +## Priorities + +1. Upgrade Lumenarium's plumbing +2. Begin work on Incenter + +## TODO + +1. Upgrade Lumenarium's plubming + 1. switch over to compiling with clang & bash based build scripts + 2. better platform layer separation + 3. osx and webgl layers, possibly linux? + 4. remove dll compiling, just build all in one go + 5. improve ui + 1. default to using a basic layout object + 2. improve widget handling (see a_trick_of_fate) + +2. Incenter + 1. Sculpture generation from list of lat-long coordinates + 2. + From 56518bdd66783927be17e4b263040a35fefd2f44 Mon Sep 17 00:00:00 2001 From: PS Date: Tue, 22 Mar 2022 19:13:06 +0100 Subject: [PATCH 115/151] 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; +} From 7925279d5921c064dbde2e0046e9d580809f86b7 Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 27 Mar 2022 12:47:18 +0200 Subject: [PATCH 116/151] Rendering context, wasm platform layer and win32 platform layer work --- build/build.sh | 294 +- build/build_.sh | 326 + project.4coder | 7 +- run_tree/{webgl => wasm}/debug/loader.html | 3 + run_tree/wasm/debug/loader.js | 39 + run_tree/wasm/debug/lumenarium.wasm | Bin 0 -> 49390 bytes .../wasm/debug/lumenarium_wasm_imports.js | 274 + run_tree/wasm/debug/text.txt | Bin 0 -> 37 bytes run_tree/webgl/debug/loader.js | 130 - run_tree/webgl/debug/lumenarium.wasm | Bin 633 -> 0 bytes .../webgl/debug/lumenarium_wasm_imports.js | 167 + run_tree/win32/intel/debug/debug.rdbg | Bin 0 -> 1349 bytes .../intel/debug/lumenarium_first_win32.obj | Bin 108086 -> 134583 bytes run_tree/win32/intel/debug/test.obj | Bin 0 -> 69097 bytes src_v2/editor/lumenarium_editor_renderer.cpp | 102 +- src_v2/editor/lumenarium_editor_renderer.h | 11 + src_v2/editor/lumenarium_interface.cpp | 1 + src_v2/engine/lumenarium_engine.cpp | 4 +- src_v2/engine/lumenarium_engine_assembly.cpp | 102 + src_v2/engine/lumenarium_engine_assembly.h | 25 +- src_v2/libs/HandmadeMath.h | 10 +- src_v2/lumenarium_first.cpp | 106 +- src_v2/lumenarium_first.h | 20 +- src_v2/lumenarium_memory.cpp | 11 +- src_v2/lumenarium_string.cpp | 2 +- src_v2/lumenarium_tests.cpp | 36 + src_v2/lumenarium_types.h | 25 - src_v2/platform/glcorearb.h | 5991 ++++++ src_v2/platform/glext.h | 12894 +++++++++++++ src_v2/platform/khrplatform.h | 311 + src_v2/platform/lumenarium_assert.h | 34 + src_v2/platform/lumenarium_compiler_flags.h | 2 +- src_v2/platform/lumenarium_platform.h | 42 + .../lumenarium_platform_common_includes.h | 6 + src_v2/platform/osx/lumenarium_osx_time.cpp | 4 + src_v2/platform/sokol_gfx.h | 16066 ++++++++++++++++ .../platform/wasm/lumenarium_first_wasm.cpp | 90 + src_v2/platform/wasm/lumenarium_wasm_file.cpp | 45 + .../platform/wasm/lumenarium_wasm_imports.js | 274 + .../platform/wasm/lumenarium_wasm_memory.cpp | 67 + .../platform/wasm/lumenarium_wasm_thread.cpp | 24 + src_v2/platform/wasm/lumenarium_wasm_time.cpp | 26 + .../platform/wasm/lumenarium_wasm_webgl.cpp | 185 + .../platform/webgl/lumenarium_first_webgl.cpp | 18 - src_v2/platform/wglext.h | 845 + .../platform/win32/lumenarium_first_win32.cpp | 43 +- .../win32/lumenarium_win32_graphics.cpp | 193 + .../platform/win32/lumenarium_win32_opengl.h | 80 + .../win32/lumenarium_win32_thread.cpp | 6 +- .../win32/lumenarium_win32_window.cpp | 341 +- src_v2/platform/win32/test.cpp | 423 + src_v2/user_space/user_space_incenter.cpp | 57 + 52 files changed, 39188 insertions(+), 574 deletions(-) create mode 100644 build/build_.sh rename run_tree/{webgl => wasm}/debug/loader.html (51%) create mode 100644 run_tree/wasm/debug/loader.js create mode 100644 run_tree/wasm/debug/lumenarium.wasm create mode 100644 run_tree/wasm/debug/lumenarium_wasm_imports.js create mode 100644 run_tree/wasm/debug/text.txt delete mode 100644 run_tree/webgl/debug/loader.js delete mode 100644 run_tree/webgl/debug/lumenarium.wasm create mode 100644 run_tree/webgl/debug/lumenarium_wasm_imports.js create mode 100644 run_tree/win32/intel/debug/debug.rdbg create mode 100644 run_tree/win32/intel/debug/test.obj create mode 100644 src_v2/editor/lumenarium_editor_renderer.h create mode 100644 src_v2/editor/lumenarium_interface.cpp create mode 100644 src_v2/engine/lumenarium_engine_assembly.cpp create mode 100644 src_v2/platform/glcorearb.h create mode 100644 src_v2/platform/glext.h create mode 100644 src_v2/platform/khrplatform.h create mode 100644 src_v2/platform/lumenarium_assert.h create mode 100644 src_v2/platform/osx/lumenarium_osx_time.cpp create mode 100644 src_v2/platform/sokol_gfx.h create mode 100644 src_v2/platform/wasm/lumenarium_first_wasm.cpp create mode 100644 src_v2/platform/wasm/lumenarium_wasm_file.cpp create mode 100644 src_v2/platform/wasm/lumenarium_wasm_imports.js create mode 100644 src_v2/platform/wasm/lumenarium_wasm_memory.cpp create mode 100644 src_v2/platform/wasm/lumenarium_wasm_thread.cpp create mode 100644 src_v2/platform/wasm/lumenarium_wasm_time.cpp create mode 100644 src_v2/platform/wasm/lumenarium_wasm_webgl.cpp delete mode 100644 src_v2/platform/webgl/lumenarium_first_webgl.cpp create mode 100644 src_v2/platform/wglext.h create mode 100644 src_v2/platform/win32/lumenarium_win32_graphics.cpp create mode 100644 src_v2/platform/win32/lumenarium_win32_opengl.h create mode 100644 src_v2/platform/win32/test.cpp create mode 100644 src_v2/user_space/user_space_incenter.cpp diff --git a/build/build.sh b/build/build.sh index 6c61fa8..9be186f 100644 --- a/build/build.sh +++ b/build/build.sh @@ -1,294 +1,2 @@ -#!/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 +$SCRIPT_REL_DIR/build_.sh debug win32 intel \ No newline at end of file diff --git a/build/build_.sh b/build/build_.sh new file mode 100644 index 0000000..b70899a --- /dev/null +++ b/build/build_.sh @@ -0,0 +1,326 @@ +#!/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 " wasm" + 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 +} + +add_flag () { + local -n ref=$1 + ref="$ref $2" +} + +# -------------------------------------------- +# 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_wasm="$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_wasm="src_v2/platform/wasm/lumenarium_first_wasm.cpp" +PlatformEntry_linux="src_v2/platform/linux/lumenarium_first_linux.cpp" + +# Intermediate Outputs + +CompilerOutput_win32="lumenarium.o" +CompilerOutput_osx="lumenarium" +CompilerOutput_wasm="lumenarium.wasm" +CompilerOutput_linux="" + +# Executables + +LinkerOutput_win32="lumenarium.exe" +LinkerOutput_osx="lumenarium" +LinkerOutput_wasm="lumenarium.wasm" +LinkerOutput_linux="" + +# Wasm Sys Root +WasmSysRoot="${PROJECT_PATH}/src_v2/platform/wasm/sysroot/" + +# Compiler Flags + +CompilerFlags_win32="-nologo" +add_flag CompilerFlags_win32 "-FC" # display errors with full path +add_flag CompilerFlags_win32 "-WX" # treat warnings as errors +add_flag CompilerFlags_win32 "-W4" # output warning level +add_flag CompilerFlags_win32 "-Z7" # generate C compatible debug info +# add_flag CompilerFlags_win32 "-Oi" # generate intrinsic functions +# add_flag CompilerFlags_win32 "-MTd" # create a debug multithreaded exe w/ Libcmtd.lib +# add_flag CompilerFlags_win32 "-fp:fast" # fast floating point model +add_flag CompilerFlags_win32 "-wd4505" # +add_flag CompilerFlags_win32 "-wd4100" # +add_flag CompilerFlags_win32 "-wd4189" # +add_flag CompilerFlags_win32 "-wd4702" # + +CompilerFlags_osx="" + +CompilerFlags_wasm="" +add_flag CompilerFlags_wasm "-Wno-writable-strings" # +add_flag CompilerFlags_wasm "--target=wasm32" # +add_flag CompilerFlags_wasm "-nostdlib" # +add_flag CompilerFlags_wasm "-Wl,--no-entry" # +add_flag CompilerFlags_wasm "-Wl,--allow-undefined" # +add_flag CompilerFlags_wasm "-Wl,--export-all" # + +CompilerFlags_linux="" + +CompilerFlags_DEBUG_win32="" +add_flag CompilerFlags_DEBUG_win32 "-Od" # +add_flag CompilerFlags_DEBUG_win32 "-Zi" # +add_flag CompilerFlags_DEBUG_win32 "-DDEBUG" # + +CompilerFlags_DEBUG="-O0" +add_flag CompilerFlags_DEBUG "-g" # +add_flag CompilerFlags_DEBUG "-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}=1 -DMODE_${MODE}=1 -DARCH_${ARCH}=1" + +# Linker Flags + +LinkerFlags_win32="-NOLOGO" +add_flag LinkerFlags_win32 "-incremental:no" # +add_flag LinkerFlags_win32 "-subsystem:windows" # +# add_flag LinkerFlags_win32 "-entry:WinMain" # +add_flag LinkerFlags_win32 "-opt:ref" # eliminate functions that are never referenced + +LinkerFlags_osx="" + +LinkerFlags_wasm="--no-entry" +add_flag LinkerFlags_wasm "--export-dynamic" # +add_flag LinkerFlags_wasm "--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_wasm="" +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}" == "wasm" ] +then + Compiler=$Compiler_wasm + PlatformEntry=$PlatformEntry_wasm + CompilerFlags=$CompilerFlags_wasm + CompilerOutput=$CompilerOutput_wasm + LinkerOutput=$LinkerOutput_wasm + LinkerFlags=$LinkerFlags_wasm + LinkerLibs=$LinkerLibs_wasm + +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 wasm, which doesn't care about cpu architecture +if [ $PLATFORM == "wasm" ] +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 == "wasm" ] +then + $Compiler \ + $CompilerFlags \ + -o $LinkerOutput \ + $EntryPath + cp \ + "${PROJECT_PATH}/src_v2/platform/wasm/lumenarium_wasm_imports.js" \ + ./lumenarium_wasm_imports.js + +else + $Compiler -o $LinkerOutput $CompilerFlags $EntryPath $LinkerLibs + +fi + +echo "Finished..." +popdir \ No newline at end of file diff --git a/project.4coder b/project.4coder index 8596496..b445ad7 100644 --- a/project.4coder +++ b/project.4coder @@ -1,6 +1,7 @@ version(1); project_name = "main.exe"; patterns = { + "*.js", "*.c", "*.cpp", "*.h", @@ -13,6 +14,8 @@ blacklist_patterns = { ".*", }; load_paths_base = { + { ".", .relative = true, .recursive = false, }, + { "build", .relative = true, .recursive = false, }, { "src", .relative = true, .recursive = true, }, { "src_v2", .relative = true, .recursive = true, }, { "meta", .relative = true, .recursive = true, }, @@ -23,7 +26,7 @@ load_paths = { { load_paths_base, .os = "linux", }, { load_paths_base, .os = "mac", }, }; -enable_virtual_whitespace=true; +enable_virtual_whitespace = true; command_list = { { .name = "build_application", @@ -38,7 +41,7 @@ command_list = { { "./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" }, + .cmd = { { "bash build\build.sh" , .os = "win" }, { "./build_meta.sh", .os = "linux" }, { "./build_meta.sh", .os = "mac" }, }, }, }; diff --git a/run_tree/webgl/debug/loader.html b/run_tree/wasm/debug/loader.html similarity index 51% rename from run_tree/webgl/debug/loader.html rename to run_tree/wasm/debug/loader.html index af51233..840c19b 100644 --- a/run_tree/webgl/debug/loader.html +++ b/run_tree/wasm/debug/loader.html @@ -4,6 +4,9 @@ + + + \ No newline at end of file diff --git a/run_tree/wasm/debug/loader.js b/run_tree/wasm/debug/loader.js new file mode 100644 index 0000000..3573534 --- /dev/null +++ b/run_tree/wasm/debug/loader.js @@ -0,0 +1,39 @@ + +let module = null; +let instance = null; + + +async function load_webassembly_module () +{ + lumenarium_wasm_imports = webgl_add_imports("#gl_canvas", lumenarium_wasm_imports); + + const path = "lumenarium.wasm"; + const promise = fetch(path); + const module = await WebAssembly.compileStreaming(promise); + + let memory = new WebAssembly.Memory({ initial: 2 }); + const env = { + memory, + ...lumenarium_wasm_imports, + }; + + let table = new WebAssembly.Table({ element: "anyfunc", initial: 32, }); + + instance = await WebAssembly.instantiate(module, { env }) + .then((res, err) => { + return res; + }) + .catch((a, b) => { + console.log(a,b); + }); + lumenarium_wasm_instance = instance; + + // If function '__wasm_call_ctors' (global C++ constructors) exists, call it + if (instance.exports.__wasm_call_ctors) instance.exports.__wasm_call_ctors(); + + // If function 'main' exists, call it with dummy arguments + let result = 0; + if (instance.exports.main) result = instance.exports.main(); +} + +window.addEventListener("load", load_webassembly_module) diff --git a/run_tree/wasm/debug/lumenarium.wasm b/run_tree/wasm/debug/lumenarium.wasm new file mode 100644 index 0000000000000000000000000000000000000000..898b757ab1bdd748ff42ca3a34a8134640ba3f36 GIT binary patch literal 49390 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 D`Tw4yRqh^ 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 YbrqQSbF?UYQvEb?L4=rl|4(T7AHu_35&!@I literal 0 HcmV?d00001 diff --git a/run_tree/wasm/debug/lumenarium_wasm_imports.js b/run_tree/wasm/debug/lumenarium_wasm_imports.js new file mode 100644 index 0000000..299fa8a --- /dev/null +++ b/run_tree/wasm/debug/lumenarium_wasm_imports.js @@ -0,0 +1,274 @@ +var lumenarium_wasm_module = null; +var lumenarium_wasm_instance = null; + +var WASM_PAGE_SIZE = 65536; + +function wasm_mem_get_u8_arr(inst, ptr, size) +{ + let view = new Uint8Array(inst.exports.memory.buffer, ptr, size); + return view; +} + +function wasm_read_string(inst, ptr, len) +{ + let view = wasm_mem_get_u8_arr(inst, ptr, len); + let string = ''; + for (let i = 0; i < len; i++) + { + string += String.fromCharCode(view[i]); + } + return string; +} + +function wasm_write_bytes(inst, src, ptr, len) +{ + let view = wasm_mem_get_u8_arr(inst, ptr, len); + for (let i = 0; i < len; i++) view[i] = src[i]; +} + +function wasm_get_proc(inst, proc_ptr) +{ + let result = inst.exports.__indirect_function_table.get(proc_ptr); + return result; +} + +function fract (v) { return v % 1; } + +var lumenarium_wasm_imports = { + + memset: (dst, size, value) => { + let view_dst = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dst, size); + for (let i = 0; i < size; i++) + { + view_dst[i] = value; + } + }, + + memcpy: (dst, src, size) => { + let view_dst = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dst, size); + let view_src = wasm_mem_get_u8_arr(lumenarium_wasm_instance, src, size); + for (let i = 0; i < size; i++) + { + view_dst[i] = view_src[i]; + } + }, + + wasm_assert_always: (file, file_len, line) => { + let file_str = wasm_read_string(lumenarium_wasm_instance, file, file_len); + console.assert(false, "At: " + file_str + "::" + line); + }, + + wasm_get_memory_size: () => { + return instance.exports.memory.buffer.byteLength; + }, + + wasm_mem_grow: (new_size) => { + let pages = new_size / WASM_PAGE_SIZE; + let pages_rem = fract(pages); + if (pages_rem > 0) pages = Math.floor(pages) + 1; + let size_before = lumenarium_wasm_instance.exports.memory.buffer.byteLength; + let old_page_count = lumenarium_wasm_instance.exports.memory.grow(pages); + + console.log("mem_grow\n", + "req size: ", new_size, "\n", + "old size: ", (old_page_count * WASM_PAGE_SIZE), "\n", + "old size: ", size_before, "\n", + "grew by: ", (pages * WASM_PAGE_SIZE), "\n", + "new size: ", lumenarium_wasm_instance.exports.memory.buffer.byteLength, ""); + }, + + wasm_performance_now: () => { + return performance.now(); + }, + + wasm_sleep: (milliseconds) => { + let start = Date.now(); + for (let at = Date.now(); (at - start) < milliseconds; at = Date.now()) {} + }, + + wasm_fetch: async (file_path, file_path_len, dest, dest_size) => { + let path = wasm_read_string(lumenarium_wasm_instance, file_path, file_path_len); + fetch(path) + .then(async (res) => { + // TODO(PS): success checking + let reader = res.body.getReader(); + let read_res = { done: false }; + + let view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dest, dest_size); + let last_write = 0; + while (!read_res.done) + { + read_res = await reader.read(); + if (read_res.done) break; + + let len = read_res.value.length; + let write_end = last_write + len; + for (let i = last_write; i < write_end; i++) + { + view[i] = read_res.value[i - last_write]; + } + last_write = write_end + 1; + } + }); + return 0; + }, + + wasm_request_animation_frame: (cb) => { + let cb_proc = wasm_get_proc(lumenarium_wasm_instance, cb); + window.requestAnimationFrame(cb_proc); + }, + + print: (str_base, len) => { + let string = wasm_read_string(lumenarium_wasm_instance, str_base, len); + console.log(string); + }, +}; + +/////////////////////////////////////// +// Web GL Imports + +let gl = null; + +// NOTE(PS): it seems like its not enough to set +// the values of imports to gl.function +// ie. imports.glClearColor = gl.clearColor +// instead we need to wrap them for some reason. +// Not sure why +function glClearColor (r, g, b, a) { return gl.clearColor(r,g,b,a); } +function glEnable(v) { return gl.enable(v); } +function glDisable(v) { return gl.disable(v); } +function glBlendFunc(a,b) { return gl.blendFunc(a,b); } +function glViewport(xmin, ymin, xmax, ymax) { return gl.viewport(xmin,ymin,xmax,ymax); } +function glDepthFunc(v) { return gl.depthFunc(v); } +function glClear(mask) { return gl.clear(mask); } + +let glBuffers = []; +let glShaders = []; +let glPrograms = []; +function gl_get_managed_resource(arr, id) { + if (id == 0) return null; + return arr[id - 1]; +} +function gl_get_buffer(id) { return gl_get_managed_resource(glBuffers, id); } +function gl_get_shader(id) { return gl_get_managed_resource(glShaders, id); } +function gl_get_program(id) { return gl_get_managed_resource(glPrograms, id); } + +function glCreateBuffer() { + let buffer = gl.createBuffer(); + let new_len = glBuffers.push(buffer); + return new_len; +} +function glBindBuffer(buffer_kind, buffer_id) +{ + return gl.bindBuffer(buffer_kind, gl_get_buffer(buffer_id)); +} +function glBufferData(target, size, ptr, usage) +{ + let data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, size); + return gl.bufferData(target, data, usage); +} +function glCreateShader(kind) +{ + let shader = gl.createShader(kind); + let new_len = glShaders.push(shader); + return new_len; +} +function glShaderSource(shader_id, shader_code, shader_code_len) +{ + let str = wasm_read_string(lumenarium_wasm_instance, shader_code, shader_code_len); + console.error("For some reason, str isn't getting the correct data out of here", str); + return gl.shaderSource(gl_get_shader(shader_id), str); +} +function glCompileShader(shader_id) +{ + let s = gl_get_shader(shader_id); + let r = gl.compileShader(s); + let m = gl.getShaderInfoLog(s); + if (m.length > 0) + { + console.error("glCompileShader: \n\n" + m); + } +} +function glCreateProgram() +{ + let prog = gl.createProgram(); + let new_len = glPrograms.push(prog); + return new_len; +} +function glAttachShader(program, shader) +{ + let s = gl_get_shader(shader); + let p = gl_get_program(program); + return gl.attachShader(p, s); +} +function glLinkProgram(program) +{ + let p = gl_get_program(program); + gl.linkProgram(p); + if (!gl.getProgramParameter(p, gl.LINK_STATUS)) { + var info = gl.getProgramInfoLog(p); + console.error("Failed to compile WebGL program. \n\n"+info); + } +} +function glUseProgram(program) +{ + let p = gl_get_program(program); + return gl.useProgram(p); +} +function glGetAttribLocation(program, name, name_len) +{ + let str = wasm_read_string(lumenarium_wasm_instance, name, name_len); + return gl.getAttribLocation(gl_get_program(program), str); +} +function glVertexAttribPointer(attr, size, type, normalized, stride, pointer) +{ + return gl.vertexAttribPointer(attr, size, type, normalized, stride, pointer); +} +function glEnableVertexAttribArray(index) +{ + return gl.enableVertexAttribArray(index); +} +function glDrawElements(type, index_count, ele_type, indices) +{ + return gl.drawElements(type, index_count, ele_type, indices); +} + +function webgl_add_imports (canvas_selector, imports) { + const canvas = document.querySelector(canvas_selector); + if (!canvas) return console.error("no canvas"); + + gl = canvas.getContext("webgl"); + if (gl === null) return console.error("no webgl ctx"); + + console.log( + gl.FLOAT.toString(16), "\n", + gl.UNSIGNED_INT.toString(16), "\n" + ); + + + + imports.glClearColor = glClearColor; + imports.glEnable = glEnable; + imports.glDisable = glDisable; + imports.glBlendFunc = glBlendFunc; + imports.glViewport = glViewport; + imports.glDepthFunc = glDepthFunc; + imports.glClear = glClear; + + imports.glCreateBuffer = glCreateBuffer; + imports.glBindBuffer = glBindBuffer; + imports.glBufferData = glBufferData; + imports.glCreateShader = glCreateShader; + imports.glShaderSource = glShaderSource; + imports.glCompileShader = glCompileShader; + imports.glCreateProgram = glCreateProgram; + imports.glAttachShader = glAttachShader; + imports.glLinkProgram = glLinkProgram; + imports.glUseProgram = glUseProgram; + imports.glGetAttribLocation = glGetAttribLocation; + imports.glVertexAttribPointer = glVertexAttribPointer; + imports.glEnableVertexAttribArray = glEnableVertexAttribArray; + imports.glDrawElements = glDrawElements; + + return imports; +} \ No newline at end of file diff --git a/run_tree/wasm/debug/text.txt b/run_tree/wasm/debug/text.txt new file mode 100644 index 0000000000000000000000000000000000000000..ccfbc2907e133e5420b8c0917ace2b28be40e2bd GIT binary patch literal 37 UcmYexhl3 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 deleted file mode 100644 index 85b7240938f3633b8d0825ba916b1a189b08a033..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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`- diff --git a/run_tree/webgl/debug/lumenarium_wasm_imports.js b/run_tree/webgl/debug/lumenarium_wasm_imports.js new file mode 100644 index 0000000..b7808c3 --- /dev/null +++ b/run_tree/webgl/debug/lumenarium_wasm_imports.js @@ -0,0 +1,167 @@ +var lumenarium_wasm_module = null; +var lumenarium_wasm_instance = null; + +var WASM_PAGE_SIZE = 65536; + +function wasm_mem_get_u8_arr(inst, ptr, size) +{ + let view = new Uint8Array(inst.exports.memory.buffer, ptr, size); + return view; +} + +function wasm_read_string(inst, ptr, len) +{ + let view = wasm_mem_get_u8_arr(inst, ptr, len); + let string = ''; + for (let i = 0; i < len; i++) + { + string += String.fromCharCode(view[i]); + } + return string; +} + +function wasm_write_bytes(inst, src, ptr, len) +{ + let view = wasm_mem_get_u8_arr(inst, ptr, len); + for (let i = 0; i < len; i++) view[i] = src[i]; +} + +function wasm_get_proc(inst, proc_ptr) +{ + let result = inst.exports.__indirect_function_table.get(proc_ptr); + return result; +} + +function fract (v) { return v % 1; } + +var lumenarium_wasm_imports = { + + memset: (dst, size, value) => { + let view_dst = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dst, size); + for (let i = 0; i < size; i++) + { + view_dst[i] = value; + } + }, + + memcpy: (dst, src, size) => { + let view_dst = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dst, size); + let view_src = wasm_mem_get_u8_arr(lumenarium_wasm_instance, src, size); + for (let i = 0; i < size; i++) + { + view_dst[i] = view_src[i]; + } + }, + + wasm_assert_always: () => { + console.assert(false); + }, + + wasm_get_memory_size: () => { + return instance.exports.memory.buffer.byteLength; + }, + + wasm_mem_grow: (new_size) => { + let pages = new_size / WASM_PAGE_SIZE; + let pages_rem = fract(pages); + if (pages_rem > 0) pages = Math.floor(pages) + 1; + let size_before = lumenarium_wasm_instance.exports.memory.buffer.byteLength; + let old_page_count = lumenarium_wasm_instance.exports.memory.grow(pages); + + console.log("mem_grow\n", + "req size: ", new_size, "\n", + "old size: ", (old_page_count * WASM_PAGE_SIZE), "\n", + "old size: ", size_before, "\n", + "grew by: ", (pages * WASM_PAGE_SIZE), "\n", + "new size: ", lumenarium_wasm_instance.exports.memory.buffer.byteLength, ""); + }, + + wasm_performance_now: () => { + return performance.now(); + }, + + wasm_sleep: (milliseconds) => { + let start = Date.now(); + for (let at = Date.now(); (at - start) < milliseconds; at = Date.now()) {} + }, + + wasm_fetch: async (file_path, file_path_len, dest, dest_size) => { + let path = wasm_read_string(lumenarium_wasm_instance, file_path, file_path_len); + fetch(path) + .then(async (res) => { + // TODO(PS): success checking + let reader = res.body.getReader(); + let read_res = { done: false }; + + let view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dest, dest_size); + let last_write = 0; + while (!read_res.done) + { + read_res = await reader.read(); + if (read_res.done) break; + + let len = read_res.value.length; + let write_end = last_write + len; + for (let i = last_write; i < write_end; i++) + { + view[i] = read_res.value[i - last_write]; + } + last_write = write_end + 1; + } + }); + return 0; + }, + + wasm_request_animation_frame: (cb) => { + let cb_proc = wasm_get_proc(lumenarium_wasm_instance, cb); + window.requestAnimationFrame(cb_proc); + }, + + print: (str_base, len) => { + let string = wasm_read_string(lumenarium_wasm_instance, str_base, len); + console.log(string); + }, +}; + +let gl = null; + +function glClearColor (r, g, b, a) { gl.clearColor(r,g,b,a); } +function glEnable(v) { gl.enable(v); } +function glDisable(v) { gl.disable(v); } +function glBlendFunc(a,b) { gl.blendFunc(a,b); } +function glViewport(xmin, ymin, xmax, ymax) { gl.viewport(xmin,ymin,xmax,ymax); } +function glDepthFunc(v) { gl.depthFunc(v); } +function glClear(mask) { gl.clear(mask); } + +function webgl_add_imports (canvas_selector, imports) { + const canvas = document.querySelector(canvas_selector); + if (!canvas) return console.error("no canvas"); + + gl = canvas.getContext("webgl"); + if (gl === null) return console.error("no webgl ctx"); + + /////////////////////////////////////// + // Constants + + imports.GL_TEXTURE_2D = gl.TEXTURE_2D; + imports.GL_BLEND = gl.BLEND; + imports.GL_SRC_ALPHA = gl.SRC_ALPHA; + imports.GL_ONE_MINUS_SRC_ALPHA = gl.ONE_MINUS_SRC_ALPHA; + imports.GL_DEPTH_TEST = gl.DEPTH_TEST; + imports.GL_LESS = gl.LESS; + imports.GL_COLOR_BUFFER_BIT = gl.COLOR_BUFFER_BIT; + imports.GL_DEPTH_BUFFER_BIT = gl.DEPTH_BUFFER_BIT; + + /////////////////////////////////////// + // Functions + + imports.glClearColor = glClearColor; + imports.glEnable = glEnable; + imports.glDisable = glDisable; + imports.glBlendFunc = glBlendFunc; + imports.glViewport = glViewport; + imports.glDepthFunc = glDepthFunc; + imports.glClear = glClear; + + return imports; +} \ No newline at end of file diff --git a/run_tree/win32/intel/debug/debug.rdbg b/run_tree/win32/intel/debug/debug.rdbg new file mode 100644 index 0000000000000000000000000000000000000000..0f16f81b1dfd750060cf056d8d209882ec828e87 GIT binary patch literal 1349 zcmcJOO-sW-5QZBC6+8&y*`p^3QZMRBTJZy_h*wvZG?}C;$!?h4?Z7&deml`bQvtP_xR+k|&jLMOUEi)h2W~K_QtH}(! zXbEh%a$sF$<`1=1ckX-n`@RJf24`IS)UtThWdEN?HvOlwbzlx?v7?Co s(=c)l?d#dw;!L9rPwx2sW5)3{MM~A;akBXQGI1eC5b#T^BkmY{0uXkw-T(jq 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 index 551d7f3ebd5810ccb57900a2b4a1312569983160..895b93cf864f72c26e1bd471d5b60ca8972521e3 100644 GIT binary patch literal 134583 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 literal 108086 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 S)AQCA76wD9K~&A}vHu5sq$215 literal 0 HcmV?d00001 diff --git a/src_v2/editor/lumenarium_editor_renderer.cpp b/src_v2/editor/lumenarium_editor_renderer.cpp index 452471c..53b443f 100644 --- a/src_v2/editor/lumenarium_editor_renderer.cpp +++ b/src_v2/editor/lumenarium_editor_renderer.cpp @@ -1,20 +1,94 @@ +internal void +edr_init(App_State* state) +{ + v4 quad_verts[] = { + -0.5f, 0.5f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.0f, 1.0f, + 00.5f, -0.5f, 0.0f, 1.0f, + 00.5f, 0.5f, 0.0f, 1.0f, + }; + + u32 quad_indices[] = { + 3, 2, 1, + 3, 1, 0, + }; + + char* shader_code_vert = + "#version 140\n" + "attribute vec4 coordinates;\n" + "void main(void) {\n" + " gl_Position = coordinates;\n" + "}"; + + char* shader_code_frag = + "#version 140\n" + "void main(void) {\n" + " gl_FragColor = vec4(1, 0, 1, 1);\n" + "}"; + +#if 0 + /* ======= Geometry =======*/ + + glCreateBuffers(1, &buffer_vertex); + glBindBuffer(GL_ARRAY_BUFFER, buffer_vertex); + glBufferData(GL_ARRAY_BUFFER, quad_verts, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, NULL); + + glCreateBuffer(1, &buffer_index); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_index); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, quad_indices, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); + + /* ======= Shaders =======*/ + + shader_vert = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(shader_vertex, shader_code_vert); + glCompileShader(shader_vert); + + shader_frag = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(shader_frag, shader_code_frag); + glCompileShader(shader_frag); + + shader_prog = glCreateProgram(); + glAttachShader(shader_prog, shader_vert); + glAttachShader(shader_prog, shader_frag); + glLinkProgram(shader_prog); + glUseProgram(shader_prog); + + /* ======= Associating shaders to buffer objects =======*/ + + glBindBuffer(GL_ARRAY_BUFFER, buffer_vertex); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_index); + coord = glGetAttribLocation(shader_prog, "coordinates"); + glVertexAttribPointer(coord, 4, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(coord); +#endif +} + +internal void +edr_render_quad() +{ +#if 0 + glBindBuffer(GL_ARRAY_BUFFER, buffer_vertex); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_index); + glEnableVertexAttribArray(coord); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); +#endif +} + + internal void edr_render(App_State* state) { - glMatrixMode(GL_TEXTURE_2D); - glLoadIdentity(); + Platform_Graphics_Frame_Desc desc = {}; + desc.clear_color = { 0.1f, 0.1f, 0.1f, 1 }; + desc.viewport_min = { 0, 0 }; + desc.viewport_max = { 1600, 900 }; + platform_frame_begin(desc); + platform_frame_clear(); - 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); +#if 0 + edr_render_quad(); +#endif } \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_renderer.h b/src_v2/editor/lumenarium_editor_renderer.h new file mode 100644 index 0000000..2be94b0 --- /dev/null +++ b/src_v2/editor/lumenarium_editor_renderer.h @@ -0,0 +1,11 @@ +/* date = March 24th 2022 4:18 pm */ + +#ifndef LUMENARIUM_EDITOR_RENDERER_H +#define LUMENARIUM_EDITOR_RENDERER_H + +struct Editor_Renderer +{ + +}; + +#endif //LUMENARIUM_EDITOR_RENDERER_H diff --git a/src_v2/editor/lumenarium_interface.cpp b/src_v2/editor/lumenarium_interface.cpp new file mode 100644 index 0000000..ab0c014 --- /dev/null +++ b/src_v2/editor/lumenarium_interface.cpp @@ -0,0 +1 @@ +// \ No newline at end of file diff --git a/src_v2/engine/lumenarium_engine.cpp b/src_v2/engine/lumenarium_engine.cpp index 718c899..9870e8b 100644 --- a/src_v2/engine/lumenarium_engine.cpp +++ b/src_v2/engine/lumenarium_engine.cpp @@ -1,8 +1,8 @@ internal void -en_init(App_State* state) +en_init(App_State* state, App_Init_Desc desc) { - + state->assemblies = assembly_array_create(permanent, desc.assembly_cap); } internal void diff --git a/src_v2/engine/lumenarium_engine_assembly.cpp b/src_v2/engine/lumenarium_engine_assembly.cpp new file mode 100644 index 0000000..1e58678 --- /dev/null +++ b/src_v2/engine/lumenarium_engine_assembly.cpp @@ -0,0 +1,102 @@ + +Assembly_Array +assembly_array_create(Allocator* allocator, u32 cap) +{ + Assembly_Array result = {}; + result.cap = cap; + result.names = allocator_alloc_array(allocator, String, cap); + result.pixel_buffers = allocator_alloc_array(allocator, Assembly_Pixel_Buffer, cap); + result.strip_arrays = allocator_alloc_array(allocator, Assembly_Strip_Array, cap); + result.allocator = allocator; + return result; +} + +bool +assembly_handle_is_valid(Assembly_Handle h) +{ + return ((h.value & ASSEMBLY_HANDLE_VALID_BIT) != 0); +} + +u32 +assembly_handle_to_index(Assembly_Handle h) +{ + assert(assembly_handle_is_valid(h)); + u32 index = (h.value & ASSEMBLY_HANDLE_INDEX_MASK); // mask off high bit + return index; +} + +Assembly_Handle +assembly_add(Assembly_Array* a, String name, u32 pixels_cap, u32 strips_cap) +{ + Assembly_Handle result = {}; + if (a->len < a->cap) + { + result.value = a->len++; + result.value |= ASSEMBLY_HANDLE_VALID_BIT; // high bit being set means its valid + } + else + { + // TODO(PS): find empty index + // we can use the name being zero as a signal that the slot is empty + } + + // NOTE(PS): not actually a bug, just go increase App_Init_Desc::assemblies_cap + // b/c you ran out of room + assert(assembly_handle_is_valid(result)); + + u32 index = assembly_handle_to_index(result); + + a->names[index] = name; + + Assembly_Pixel_Buffer* pixel_buffer = a->pixel_buffers + index; + pixel_buffer->cap = pixels_cap; + pixel_buffer->pixels = allocator_alloc_array(a->allocator, Assembly_Pixel, pixels_cap); + pixel_buffer->positions = allocator_alloc_array(a->allocator, v4, pixels_cap); + + Assembly_Strip_Array* strip_array = a->strip_arrays + index; + strip_array->cap = strips_cap; + strip_array->strips = allocator_alloc_array(a->allocator, Assembly_Strip, strips_cap); + + return result; +} + +void +assembly_rem(Assembly_Array* a, Assembly_Handle h) +{ +} + +Assembly_Strip* +assembly_add_strip(Assembly_Array* a, Assembly_Handle h, u32 pixels_cap) +{ + u32 index = assembly_handle_to_index(h); + + Assembly_Strip_Array* strip_array = a->strip_arrays + index; + assert(strip_array->len < strip_array->cap); + + Assembly_Strip* result = strip_array->strips + strip_array->len++; + result->pixels_cap = pixels_cap; + result->pixels_len = 0; + result->pixels = allocator_alloc_array(a->allocator, u32, pixels_cap); + + return result; +} + +void +assembly_add_led( + Assembly_Array* a, + Assembly_Handle h, + Assembly_Strip* strip, + v4 position + ){ + u32 index = assembly_handle_to_index(h); + + Assembly_Pixel_Buffer* pixel_buffer = a->pixel_buffers + index; + assert(pixel_buffer->len < pixel_buffer->cap); + + u32 pixel_index = pixel_buffer->len++; + pixel_buffer->pixels[pixel_index] = {}; + pixel_buffer->positions[pixel_index] = position; + + assert(strip->pixels_len < strip->pixels_cap); + strip->pixels[strip->pixels_len++] = pixel_index; +} diff --git a/src_v2/engine/lumenarium_engine_assembly.h b/src_v2/engine/lumenarium_engine_assembly.h index 72d0271..f105ff9 100644 --- a/src_v2/engine/lumenarium_engine_assembly.h +++ b/src_v2/engine/lumenarium_engine_assembly.h @@ -3,6 +3,16 @@ #ifndef LUMENARIUM_ENGINE_ASSEMBLY_H #define LUMENARIUM_ENGINE_ASSEMBLY_H +// Assembly_Handle is valid for any index, including zero. However, +// valid values must have the high bit set. This way, the handle declared +// via Assembly_Handle my_handle = {}; is invalid, while still allowing +// index zero to be used in the array. +// +// If memory corruption becomes an issue we can make this a bigger bit +// field that we check since Lumenarium doesn't ever really expect to have +// more than 128 sculptures in a scene - but who knows, maybe someday O.o? +#define ASSEMBLY_HANDLE_VALID_BIT (1 << 31) +#define ASSEMBLY_HANDLE_INDEX_MASK ~ASSEMBLY_HANDLE_VALID_BIT struct Assembly_Handle { u32 value; @@ -29,12 +39,15 @@ struct Assembly_Pixel_Buffer struct Assembly_Strip { u32 pixels_cap; + u32 pixels_len; + // array of indices into the Assembly_Pixel_Buffer for the same assembly u32* pixels; }; struct Assembly_Strip_Array { u32 cap; + u32 len; Assembly_Strip* strips; }; @@ -42,16 +55,24 @@ struct Assembly_Array { u32 cap; u32 len; + + // assembly names String* names; + + // each assembly gets its own pixel buffer Assembly_Pixel_Buffer* pixel_buffers; + + // each assembly gets its own array of strips which + // index into that assemblies pixel_buffer Assembly_Strip_Array* strip_arrays; Allocator* allocator; }; -Assembly_Handle assembly_add(Assembly_Array* a, String name, u64 pixels_cap, u64 strips_cap); +Assembly_Array assembly_array_create(Allocator* allocator, u32 cap); +Assembly_Handle assembly_add(Assembly_Array* a, String name, u32 pixels_cap, u32 strips_cap); void assembly_rem(Assembly_Array* a, Assembly_Handle h); -Assembly_Strip* assembly_add_strip(Assembly_Array* a, Assembly_Handle h); +Assembly_Strip* assembly_add_strip(Assembly_Array* a, Assembly_Handle h, u32 pixels_cap); void assembly_add_led(Assembly_Array* a, Assembly_Handle h, Assembly_Strip* strip, v4 position); diff --git a/src_v2/libs/HandmadeMath.h b/src_v2/libs/HandmadeMath.h index ef54521..c80d4c3 100644 --- a/src_v2/libs/HandmadeMath.h +++ b/src_v2/libs/HandmadeMath.h @@ -443,9 +443,17 @@ extern "C" { float R, G, B; }; + struct + { + float r, g, b; + }; }; - float A; + union + { + float A; + float a; + }; }; struct diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.cpp index e81ff6a..9e81109 100644 --- a/src_v2/lumenarium_first.cpp +++ b/src_v2/lumenarium_first.cpp @@ -1,23 +1,101 @@ #include "lumenarium_first.h" +#include "user_space/user_space_incenter.cpp" + +Platform_Geometry_Buffer quad0; +Platform_Shader shader0; + +static r32 z_ = 0; +static r32 r_ = 0.3f; +static r32 quad_verts[] = { + -r_, -r_, z_, 1.0f, + r_, -r_, z_, 1.0f, + r_, r_, z_, 1.0f, + -r_, r_, z_, 1.0f, +}; + +static u32 quad_indices[] = { + 0, 1, 2, + 0, 2, 3, +}; + +static String shader_code_vert_win32 = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec4 coordinates;\n" + "void main(void) {\n" + " gl_Position = coordinates;\n" + "}" + ); + +static String shader_code_vert_wasm = lit_str( + "attribute vec4 coordinates;\n" + "void main(void) {\n" + " gl_Position = coordinates;\n" + "}"); + +static String shader_code_frag_win32 = lit_str( + "#version 330 core\n" + "out vec4 FragColor;\n" + "void main(void) {\n" + " FragColor = vec4(1,0,1,1);\n" + "}" + ); + +static String shader_code_frag_wasm = lit_str( + "void main(void) {\n" + " gl_FragColor = vec4(1, 0, 1, 1);\n" + "}"); + +void make_quad() +{ + // TODO(PS): TEMP +#if defined(PLATFORM_win32) + String shader_code_vert = shader_code_vert_win32; + String shader_code_frag = shader_code_frag_win32; +#elif defined(PLATFORM_wasm) + String shader_code_vert = shader_code_vert_wasm; + String shader_code_frag = shader_code_frag_wasm; +#endif + + quad0 = platform_geometry_buffer_create( + quad_verts, 16, quad_indices, 6 + ); + + String attribs[] = { lit_str("coordinates") }; + shader0 = platform_shader_create( + shader_code_vert, shader_code_frag, attribs, 1 + ); + + platform_vertex_attrib_pointer(quad0, shader0, 0); +} internal App_State* lumenarium_init() { - permanent = bump_allocator_create_reserve(MB(64)); - scratch = bump_allocator_create_reserve(MB(64)); + App_State* state = 0; + + permanent = bump_allocator_create_reserve(MB(4)); + scratch = bump_allocator_create_reserve(KB(64)); + run_tests(); - App_State* state = allocator_alloc_struct(permanent, App_State); + App_Init_Desc desc = incenter_get_init_desc(); + // TODO(PS): make sure the values make sense in desc + + state = allocator_alloc_struct(permanent, App_State); add_flag(state->flags, AppState_IsRunning); state->input_state = input_state_create(); - en_init(state); + en_init(state, desc); if (!has_flag(state->flags, AppState_NoEditor)) { ed_init(state); } + incenter_init(state); + + + make_quad(); return state; } @@ -26,6 +104,7 @@ internal void lumenarium_frame_prepare(App_State* state) { allocator_clear(scratch); + input_state_swap_frames(&state->input_state); en_frame_prepare(state); @@ -33,6 +112,7 @@ lumenarium_frame_prepare(App_State* state) { ed_frame_prepare(state); } + incenter_frame_prepare(state); } internal void @@ -41,8 +121,23 @@ lumenarium_frame(App_State* state) en_frame(state); if (!has_flag(state->flags, AppState_NoEditor)) { - ed_frame(state); + //ed_frame(state); + + Platform_Graphics_Frame_Desc desc = {}; + desc.clear_color = { 0.1f, 0.1f, 0.1f, 1 }; + desc.viewport_min = { 0, 0 }; + desc.viewport_max = { 1600, 900 }; + platform_frame_begin(desc); + platform_frame_clear(); + + + platform_geometry_bind(quad0); + platform_shader_bind(shader0); + platform_geometry_draw(quad0); + + } + incenter_frame(state); } internal void @@ -79,6 +174,7 @@ lumenarium_event(Platform_Window_Event evt, App_State* state) internal void lumenarium_cleanup(App_State* state) { + incenter_cleanup(state); en_cleanup(state); if (!has_flag(state->flags, AppState_NoEditor)) { diff --git a/src_v2/lumenarium_first.h b/src_v2/lumenarium_first.h index f6d93c0..392008c 100644 --- a/src_v2/lumenarium_first.h +++ b/src_v2/lumenarium_first.h @@ -5,15 +5,16 @@ typedef struct App_State App_State; +// Environment #include "lumenarium_memory.cpp" #include "lumenarium_string.cpp" #include "lumenarium_input.cpp" +// Engine #include "engine/lumenarium_engine_assembly.h" -#include "engine/lumenarium_engine.cpp" -#include "editor/lumenarium_editor_renderer.cpp" -#include "editor/lumenarium_editor.cpp" +// Editor +#include "editor/lumenarium_editor_renderer.h" ////////////////////////////////////////////// // Lumenarium Runtime Environment @@ -38,11 +39,24 @@ enum AppState_NoEditor = 2, }; +struct App_Init_Desc +{ + u32 assembly_cap; +}; + struct App_State { App_State_Flags flags; Input_State input_state; + Assembly_Array assemblies; }; +#include "engine/lumenarium_engine_assembly.cpp" +#include "engine/lumenarium_engine.cpp" + +#include "editor/lumenarium_editor_renderer.cpp" +#include "editor/lumenarium_editor.cpp" + + #endif //LUMENARIUM_FIRST_H diff --git a/src_v2/lumenarium_memory.cpp b/src_v2/lumenarium_memory.cpp index 6fa8123..f6a3421 100644 --- a/src_v2/lumenarium_memory.cpp +++ b/src_v2/lumenarium_memory.cpp @@ -138,11 +138,16 @@ bump_allocator_alloc(Allocator* allocator, u64 size) } else { - if (new_size < bump->size_reserved) + if (new_size <= bump->size_reserved) { u64 next_page = size_to_pages(bump->at); + if (bump->at == 0 && bump->size_committed == 0) next_page = 0; u64 commit_amt = new_size - next_page; - bump->base = platform_mem_commit(bump->base + next_page, commit_amt); + u8* new_page = platform_mem_commit(bump->base + next_page, commit_amt); + if (new_page != 0) + { + bump->size_committed = new_size; + } } else { @@ -151,7 +156,7 @@ bump_allocator_alloc(Allocator* allocator, u64 size) } } - u8* result = bump->base; + u8* result = bump->base + bump->at; bump->at = at_after; return result; diff --git a/src_v2/lumenarium_string.cpp b/src_v2/lumenarium_string.cpp index b7850a0..83f4ad1 100644 --- a/src_v2/lumenarium_string.cpp +++ b/src_v2/lumenarium_string.cpp @@ -61,7 +61,7 @@ string_substring(String s, u64 min, u64 max) if (min > max) { u64 t = min; min = max; - max = min; + max = t; } String result = {}; result.str = s.str + min; diff --git a/src_v2/lumenarium_tests.cpp b/src_v2/lumenarium_tests.cpp index 3bdfa5c..7472451 100644 --- a/src_v2/lumenarium_tests.cpp +++ b/src_v2/lumenarium_tests.cpp @@ -2,12 +2,40 @@ Platform_Thread_Result thread_proc(Platform_Thread_Data* td) { + //Sleep(100); return {}; } internal void run_tests() { + // memory tests + + u8* a0 = allocator_alloc_array(scratch, u8, 32); + u8* a1 = allocator_alloc_array(scratch, u8, 32); + assert(a0 != a1); + assert((a0 + 32) <= a1); + + for (u32 i = 0; i < 32; i++) + { + a0[i] = (u8)i; + a1[i] = (u8)(100 + i); + } + + + for (u32 i = 0; i < 32; i++) + { + assert(a0[i] == i); + assert(a1[i] == (100 + i)); + } + +#if defined(PLATFORM_wasm) + // NOTE(PS): the tests below this point don't make sense on a web assembly + // platform + return; +#endif + + // testing strings and exe path String exe_file_path = platform_get_exe_path(scratch); assert(exe_file_path.str != 0); @@ -31,16 +59,24 @@ run_tests() bool r = platform_file_write_all(f, d1); assert(r); +#if 0 + // TODO(PS): these were causing startup problems but you weren't focusing on + // threads/ When you build something multithreaded come back here and + // make tests that actually work + // 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]); } +#endif allocator_clear(scratch); + } \ No newline at end of file diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h index 29b96c9..e9eac22 100644 --- a/src_v2/lumenarium_types.h +++ b/src_v2/lumenarium_types.h @@ -54,31 +54,6 @@ struct 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 diff --git a/src_v2/platform/glcorearb.h b/src_v2/platform/glcorearb.h new file mode 100644 index 0000000..4855b20 --- /dev/null +++ b/src_v2/platform/glcorearb.h @@ -0,0 +1,5991 @@ +#ifndef __gl_glcorearb_h_ +#define __gl_glcorearb_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + + /* + ** Copyright 2013-2020 The Khronos Group Inc. + ** SPDX-License-Identifier: MIT + ** + ** This header is generated from the Khronos OpenGL / OpenGL ES XML + ** API Registry. The current version of the Registry, generator scripts + ** used to make the header, and the header can be found at + ** https://github.com/KhronosGroup/OpenGL-Registry + */ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + + /* glcorearb.h is for use with OpenGL core profile implementations. + ** It should should be placed in the same directory as gl.h and + ** included as . + ** + ** glcorearb.h includes only APIs in the latest OpenGL core profile + ** implementation together with APIs in newer ARB extensions which + ** can be supported by the core profile. It does not, and never will + ** include functionality removed from the core profile, such as + ** fixed-function vertex and fragment processing. + ** + ** Do not #include both and either of or + ** in the same source file. + */ + + /* Generated C header for: + * API: gl + * Profile: core + * Versions considered: .* + * Versions emitted: .* + * Default extensions included: glcore + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef GL_VERSION_1_0 +#define GL_VERSION_1_0 1 + typedef void GLvoid; + typedef unsigned int GLenum; +#include "khrplatform.h" + typedef khronos_float_t GLfloat; + typedef int GLint; + typedef int GLsizei; + typedef unsigned int GLbitfield; + typedef double GLdouble; + typedef unsigned int GLuint; + typedef unsigned char GLboolean; + typedef khronos_uint8_t GLubyte; +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_NONE 0 +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_CW 0x0900 +#define GL_CCW 0x0901 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_RANGE 0x0B12 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_RANGE 0x0B22 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_VIEWPORT 0x0BA2 +#define GL_DITHER 0x0BD0 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND 0x0BE2 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_DRAW_BUFFER 0x0C01 +#define GL_READ_BUFFER 0x0C02 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_STEREO 0x0C33 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F +#define GL_TEXTURE 0x1702 +#define GL_COLOR 0x1800 +#define GL_DEPTH 0x1801 +#define GL_STENCIL 0x1802 +#define GL_STENCIL_INDEX 0x1901 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_REPEAT 0x2901 + typedef void (APIENTRYP PFNGLCULLFACEPROC) (GLenum mode); + typedef void (APIENTRYP PFNGLFRONTFACEPROC) (GLenum mode); + typedef void (APIENTRYP PFNGLHINTPROC) (GLenum target, GLenum mode); + typedef void (APIENTRYP PFNGLLINEWIDTHPROC) (GLfloat width); + typedef void (APIENTRYP PFNGLPOINTSIZEPROC) (GLfloat size); + typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode); + typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLDRAWBUFFERPROC) (GLenum buf); + typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask); + typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + typedef void (APIENTRYP PFNGLCLEARSTENCILPROC) (GLint s); + typedef void (APIENTRYP PFNGLCLEARDEPTHPROC) (GLdouble depth); + typedef void (APIENTRYP PFNGLSTENCILMASKPROC) (GLuint mask); + typedef void (APIENTRYP PFNGLCOLORMASKPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); + typedef void (APIENTRYP PFNGLDEPTHMASKPROC) (GLboolean flag); + typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap); + typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap); + typedef void (APIENTRYP PFNGLFINISHPROC) (void); + typedef void (APIENTRYP PFNGLFLUSHPROC) (void); + typedef void (APIENTRYP PFNGLBLENDFUNCPROC) (GLenum sfactor, GLenum dfactor); + typedef void (APIENTRYP PFNGLLOGICOPPROC) (GLenum opcode); + typedef void (APIENTRYP PFNGLSTENCILFUNCPROC) (GLenum func, GLint ref, GLuint mask); + typedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum zpass); + typedef void (APIENTRYP PFNGLDEPTHFUNCPROC) (GLenum func); + typedef void (APIENTRYP PFNGLPIXELSTOREFPROC) (GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLREADBUFFERPROC) (GLenum src); + typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); + typedef void (APIENTRYP PFNGLGETBOOLEANVPROC) (GLenum pname, GLboolean *data); + typedef void (APIENTRYP PFNGLGETDOUBLEVPROC) (GLenum pname, GLdouble *data); + typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void); + typedef void (APIENTRYP PFNGLGETFLOATVPROC) (GLenum pname, GLfloat *data); + typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data); + typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); + typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, void *pixels); + typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC) (GLenum target, GLint level, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC) (GLenum target, GLint level, GLenum pname, GLint *params); + typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap); + typedef void (APIENTRYP PFNGLDEPTHRANGEPROC) (GLdouble n, GLdouble f); + typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCullFace (GLenum mode); + GLAPI void APIENTRY glFrontFace (GLenum mode); + GLAPI void APIENTRY glHint (GLenum target, GLenum mode); + GLAPI void APIENTRY glLineWidth (GLfloat width); + GLAPI void APIENTRY glPointSize (GLfloat size); + GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode); + GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); + GLAPI void APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glTexImage1D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glDrawBuffer (GLenum buf); + GLAPI void APIENTRY glClear (GLbitfield mask); + GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + GLAPI void APIENTRY glClearStencil (GLint s); + GLAPI void APIENTRY glClearDepth (GLdouble depth); + GLAPI void APIENTRY glStencilMask (GLuint mask); + GLAPI void APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); + GLAPI void APIENTRY glDepthMask (GLboolean flag); + GLAPI void APIENTRY glDisable (GLenum cap); + GLAPI void APIENTRY glEnable (GLenum cap); + GLAPI void APIENTRY glFinish (void); + GLAPI void APIENTRY glFlush (void); + GLAPI void APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); + GLAPI void APIENTRY glLogicOp (GLenum opcode); + GLAPI void APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); + GLAPI void APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); + GLAPI void APIENTRY glDepthFunc (GLenum func); + GLAPI void APIENTRY glPixelStoref (GLenum pname, GLfloat param); + GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param); + GLAPI void APIENTRY glReadBuffer (GLenum src); + GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); + GLAPI void APIENTRY glGetBooleanv (GLenum pname, GLboolean *data); + GLAPI void APIENTRY glGetDoublev (GLenum pname, GLdouble *data); + GLAPI GLenum APIENTRY glGetError (void); + GLAPI void APIENTRY glGetFloatv (GLenum pname, GLfloat *data); + GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data); + GLAPI const GLubyte *APIENTRY glGetString (GLenum name); + GLAPI void APIENTRY glGetTexImage (GLenum target, GLint level, GLenum format, GLenum type, void *pixels); + GLAPI void APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTexLevelParameterfv (GLenum target, GLint level, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetTexLevelParameteriv (GLenum target, GLint level, GLenum pname, GLint *params); + GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap); + GLAPI void APIENTRY glDepthRange (GLdouble n, GLdouble f); + GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_0 */ + +#ifndef GL_VERSION_1_1 +#define GL_VERSION_1_1 1 + typedef khronos_float_t GLclampf; + typedef double GLclampd; +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_DOUBLE 0x140A +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_R3_G3_B2 0x2A10 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_VERTEX_ARRAY 0x8074 + typedef void (APIENTRYP PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count); + typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices); + typedef void (APIENTRYP PFNGLGETPOINTERVPROC) (GLenum pname, void **params); + typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat units); + typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture); + typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures); + typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); + typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC) (GLuint texture); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); + GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices); + GLAPI void APIENTRY glGetPointerv (GLenum pname, void **params); + GLAPI void APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); + GLAPI void APIENTRY glCopyTexImage1D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + GLAPI void APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + GLAPI void APIENTRY glCopyTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture); + GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); + GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); + GLAPI GLboolean APIENTRY glIsTexture (GLuint texture); +#endif +#endif /* GL_VERSION_1_1 */ + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E + typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); + typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); + GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_2 */ + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D + typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); + typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glActiveTexture (GLenum texture); + GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); + GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img); +#endif +#endif /* GL_VERSION_1_3 */ + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 + typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); + typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); + GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); + GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); + GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); + GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); + GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); + GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + GLAPI void APIENTRY glBlendEquation (GLenum mode); +#endif +#endif /* GL_VERSION_1_4 */ + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 + typedef khronos_ssize_t GLsizeiptr; + typedef khronos_intptr_t GLintptr; +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 + typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); + typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); + typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); + typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); + typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); + typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); + typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); + typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); + typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); + typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); + typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); + typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); + typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); + typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); + GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); + GLAPI GLboolean APIENTRY glIsQuery (GLuint id); + GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); + GLAPI void APIENTRY glEndQuery (GLenum target); + GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); + GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); + GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); + GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); + GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); + GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); + GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); + GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data); + GLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access); + GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); + GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_VERSION_1_5 */ + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 + typedef char GLchar; + typedef khronos_int16_t GLshort; + typedef khronos_int8_t GLbyte; + typedef khronos_uint16_t GLushort; +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 + typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); + typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); + typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); + typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); + typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); + typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); + typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); + typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); + typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); + typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); + typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); + typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); + typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); + typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); + typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); + typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); + typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); + typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); + typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); + typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); + typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); + typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); + typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); + typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); + typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); + typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); + typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); + typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); + typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); + typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); + typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); + typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); + typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); + GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); + GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); + GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); + GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); + GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); + GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); + GLAPI void APIENTRY glCompileShader (GLuint shader); + GLAPI GLuint APIENTRY glCreateProgram (void); + GLAPI GLuint APIENTRY glCreateShader (GLenum type); + GLAPI void APIENTRY glDeleteProgram (GLuint program); + GLAPI void APIENTRY glDeleteShader (GLuint shader); + GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); + GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); + GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); + GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); + GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); + GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); + GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); + GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); + GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); + GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); + GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); + GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); + GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); + GLAPI GLboolean APIENTRY glIsProgram (GLuint program); + GLAPI GLboolean APIENTRY glIsShader (GLuint shader); + GLAPI void APIENTRY glLinkProgram (GLuint program); + GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); + GLAPI void APIENTRY glUseProgram (GLuint program); + GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); + GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); + GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); + GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); + GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); + GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glValidateProgram (GLuint program); + GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); + GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); + GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); + GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); + GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); + GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); + GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); + GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); + GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); + GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); + GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); + GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); + GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); + GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); + GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); + GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#endif +#endif /* GL_VERSION_2_0 */ + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif +#endif /* GL_VERSION_2_1 */ + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 + typedef khronos_uint16_t GLhalf; +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 + typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); + typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); + typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); + typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); + typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); + typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); + typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); + typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); + typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); + typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); + typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); + typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); + typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); + typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); + typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); + typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); + typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); + typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); + typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); + typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); + typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); + typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); + typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); + typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); + typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); + typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); + typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); + typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); + typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); + typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); + typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); + typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); + typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); + typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); + typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); + typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); + typedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); + typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); + typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); + typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); + typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); + typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); + GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); + GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); + GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); + GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); + GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); + GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); + GLAPI void APIENTRY glEndTransformFeedback (void); + GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); + GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); + GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); + GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); + GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); + GLAPI void APIENTRY glEndConditionalRender (void); + GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); + GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); + GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); + GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); + GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); + GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); + GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); + GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); + GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); + GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); + GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); + GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); + GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); + GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); + GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); + GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); + GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); + GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); + GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); + GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); + GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); + GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); + GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); + GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); + GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); + GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); + GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); + GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); + GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); + GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); + GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); + GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); + GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); + GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); + GLAPI void APIENTRY glGenerateMipmap (GLenum target); + GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); + GLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); + GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); + GLAPI void APIENTRY glBindVertexArray (GLuint array); + GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); + GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); + GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); +#endif +#endif /* GL_VERSION_3_0 */ + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu + typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); + typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); + typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); + typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); + typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); + typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); + GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); + GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); + GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); + GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); + GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); + GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); + GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); + GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif +#endif /* GL_VERSION_3_1 */ + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 + typedef struct __GLsync *GLsync; + typedef khronos_uint64_t GLuint64; + typedef khronos_int64_t GLint64; +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_DEPTH_CLAMP 0x864F +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 + typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); + typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); + typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); + typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); + typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); + typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); + typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); + typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); + typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); + typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); + typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); + typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); + typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); + GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); + GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); + GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); + GLAPI void APIENTRY glProvokingVertex (GLenum mode); + GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); + GLAPI GLboolean APIENTRY glIsSync (GLsync sync); + GLAPI void APIENTRY glDeleteSync (GLsync sync); + GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); + GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); + GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data); + GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); + GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); + GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); + GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); + GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); + GLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask); +#endif +#endif /* GL_VERSION_3_2 */ + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_RGB10_A2UI 0x906F +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#define GL_INT_2_10_10_10_REV 0x8D9F + typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); + typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); + typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); + typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); + typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); + typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); + typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); + typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); + GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); + GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); + GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); + GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); + GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); + GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); + GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); + GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); + GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); + GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); + GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); + GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); + GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); + GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); + GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); + GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); + GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); + GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); + GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); + GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); + GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +#endif +#endif /* GL_VERSION_3_3 */ + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_ISOLINES 0x8E7A +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 + typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); + typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); + typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); + typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); + typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect); + typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); + typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); + typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); + typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); + typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); + typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); + typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); + typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); + typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); + typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); + typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); + typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); + typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); + typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); + typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); + typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); + typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); + typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); + typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); + typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMinSampleShading (GLfloat value); + GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); + GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); + GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); + GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect); + GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect); + GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); + GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); + GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); + GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); + GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); + GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); + GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); + GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); + GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); + GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); + GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); + GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); + GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); + GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); + GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); + GLAPI void APIENTRY glPauseTransformFeedback (void); + GLAPI void APIENTRY glResumeTransformFeedback (void); + GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); + GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); + GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); + GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); + GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_VERSION_4_0 */ + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 + typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); + typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); + typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); + typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); + typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); + typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); + typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); + typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); + typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); + typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); + typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings); + typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); + typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); + typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); + typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); + typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); + typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); + typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); + typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); + typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); + typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); + typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); + typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glReleaseShaderCompiler (void); + GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); + GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); + GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); + GLAPI void APIENTRY glClearDepthf (GLfloat d); + GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); + GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); + GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); + GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); + GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); + GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings); + GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); + GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); + GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); + GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); + GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); + GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); + GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); + GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); + GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); + GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); + GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); + GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); + GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); + GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); + GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); + GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); + GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); + GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); + GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); + GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); + GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); + GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); + GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); + GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); + GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); + GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); + GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); + GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); + GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); +#endif +#endif /* GL_VERSION_4_1 */ + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F + typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); + typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); + typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); + typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); + typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); + typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); + GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); + GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); + GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); + GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); + GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); + GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); + GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); + GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif +#endif /* GL_VERSION_4_2 */ + +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 + typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_VERTEX_BINDING_BUFFER 0x8F4F + typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); + typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); + typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); + typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); + typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); + typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); + typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); + typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); + typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); + typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); + typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); + typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); + typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); + typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); + typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); + typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); + typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); + typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); + typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); + typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); + typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); + typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); + typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); + typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); + typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); + GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); + GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); + GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); + GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); + GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); + GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); + GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); + GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); + GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); + GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); + GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); + GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); + GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); + GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); + GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); + GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); + GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); + GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); + GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); + GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); + GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); + GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); + GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); + GLAPI void APIENTRY glPopDebugGroup (void); + GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); + GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); + GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); + GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_VERSION_4_3 */ + +#ifndef GL_VERSION_4_4 +#define GL_VERSION_4_4 1 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 + typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); + typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); + typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); + typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); + typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers); + typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); + typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); + GLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); + GLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); + GLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures); + GLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers); + GLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures); + GLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#endif +#endif /* GL_VERSION_4_4 */ + +#ifndef GL_VERSION_4_5 +#define GL_VERSION_4_5 1 +#define GL_CONTEXT_LOST 0x0507 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_ZERO_TO_ONE 0x935F +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_QUERY_WAIT_INVERTED 0x8E17 +#define GL_QUERY_NO_WAIT_INVERTED 0x8E18 +#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 +#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_TEXTURE_TARGET 0x1006 +#define GL_QUERY_TARGET 0x82EA +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET 0x8252 +#define GL_NO_RESET_NOTIFICATION 0x8261 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC + typedef void (APIENTRYP PFNGLCLIPCONTROLPROC) (GLenum origin, GLenum depth); + typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); + typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) (GLuint xfb, GLuint index, GLuint buffer); + typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC) (GLuint xfb, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param); + typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); + typedef void (APIENTRYP PFNGLCREATEBUFFERSPROC) (GLsizei n, GLuint *buffers); + typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); + typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); + typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + typedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERPROC) (GLuint buffer, GLenum access); + typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); + typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC) (GLuint buffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) (GLuint buffer, GLenum pname, GLint64 *params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC) (GLuint buffer, GLenum pname, void **params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); + typedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) (GLuint framebuffer, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) (GLuint framebuffer, GLenum buf); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) (GLuint framebuffer, GLenum src); + typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); + typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); + typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); + typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); + typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + typedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC) (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) (GLuint framebuffer, GLenum target); + typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) (GLuint framebuffer, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) (GLuint renderbuffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLCREATETEXTURESPROC) (GLenum target, GLsizei n, GLuint *textures); + typedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC) (GLuint texture, GLenum internalformat, GLuint buffer); + typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC) (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC) (GLuint texture, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, const GLfloat *param); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC) (GLuint texture, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, const GLuint *params); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, const GLint *param); + typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC) (GLuint texture); + typedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC) (GLuint unit, GLuint texture); + typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLsizei bufSize, void *pixels); + typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC) (GLuint texture, GLint level, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC) (GLuint texture, GLint level, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); + typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); + typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); + typedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC) (GLuint vaobj, GLuint buffer); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC) (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); + typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); + typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC) (GLuint vaobj, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); + typedef void (APIENTRYP PFNGLCREATESAMPLERSPROC) (GLsizei n, GLuint *samplers); + typedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); + typedef void (APIENTRYP PFNGLCREATEQUERIESPROC) (GLenum target, GLsizei n, GLuint *ids); + typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC) (GLbitfield barriers); + typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); + typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC) (void); + typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint lod, GLsizei bufSize, void *pixels); + typedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + typedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); + typedef void (APIENTRYP PFNGLREADNPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); + typedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glClipControl (GLenum origin, GLenum depth); + GLAPI void APIENTRY glCreateTransformFeedbacks (GLsizei n, GLuint *ids); + GLAPI void APIENTRY glTransformFeedbackBufferBase (GLuint xfb, GLuint index, GLuint buffer); + GLAPI void APIENTRY glTransformFeedbackBufferRange (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glGetTransformFeedbackiv (GLuint xfb, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetTransformFeedbacki_v (GLuint xfb, GLenum pname, GLuint index, GLint *param); + GLAPI void APIENTRY glGetTransformFeedbacki64_v (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); + GLAPI void APIENTRY glCreateBuffers (GLsizei n, GLuint *buffers); + GLAPI void APIENTRY glNamedBufferStorage (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); + GLAPI void APIENTRY glNamedBufferData (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); + GLAPI void APIENTRY glNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + GLAPI void APIENTRY glCopyNamedBufferSubData (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + GLAPI void APIENTRY glClearNamedBufferData (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glClearNamedBufferSubData (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + GLAPI void *APIENTRY glMapNamedBuffer (GLuint buffer, GLenum access); + GLAPI void *APIENTRY glMapNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); + GLAPI GLboolean APIENTRY glUnmapNamedBuffer (GLuint buffer); + GLAPI void APIENTRY glFlushMappedNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length); + GLAPI void APIENTRY glGetNamedBufferParameteriv (GLuint buffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetNamedBufferParameteri64v (GLuint buffer, GLenum pname, GLint64 *params); + GLAPI void APIENTRY glGetNamedBufferPointerv (GLuint buffer, GLenum pname, void **params); + GLAPI void APIENTRY glGetNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); + GLAPI void APIENTRY glCreateFramebuffers (GLsizei n, GLuint *framebuffers); + GLAPI void APIENTRY glNamedFramebufferRenderbuffer (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + GLAPI void APIENTRY glNamedFramebufferParameteri (GLuint framebuffer, GLenum pname, GLint param); + GLAPI void APIENTRY glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); + GLAPI void APIENTRY glNamedFramebufferTextureLayer (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); + GLAPI void APIENTRY glNamedFramebufferDrawBuffer (GLuint framebuffer, GLenum buf); + GLAPI void APIENTRY glNamedFramebufferDrawBuffers (GLuint framebuffer, GLsizei n, const GLenum *bufs); + GLAPI void APIENTRY glNamedFramebufferReadBuffer (GLuint framebuffer, GLenum src); + GLAPI void APIENTRY glInvalidateNamedFramebufferData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); + GLAPI void APIENTRY glInvalidateNamedFramebufferSubData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glClearNamedFramebufferiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); + GLAPI void APIENTRY glClearNamedFramebufferuiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); + GLAPI void APIENTRY glClearNamedFramebufferfv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); + GLAPI void APIENTRY glClearNamedFramebufferfi (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + GLAPI void APIENTRY glBlitNamedFramebuffer (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + GLAPI GLenum APIENTRY glCheckNamedFramebufferStatus (GLuint framebuffer, GLenum target); + GLAPI void APIENTRY glGetNamedFramebufferParameteriv (GLuint framebuffer, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameteriv (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); + GLAPI void APIENTRY glCreateRenderbuffers (GLsizei n, GLuint *renderbuffers); + GLAPI void APIENTRY glNamedRenderbufferStorage (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glNamedRenderbufferStorageMultisample (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetNamedRenderbufferParameteriv (GLuint renderbuffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glCreateTextures (GLenum target, GLsizei n, GLuint *textures); + GLAPI void APIENTRY glTextureBuffer (GLuint texture, GLenum internalformat, GLuint buffer); + GLAPI void APIENTRY glTextureBufferRange (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glTextureStorage1D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); + GLAPI void APIENTRY glTextureStorage2D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTextureStorage3D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + GLAPI void APIENTRY glTextureStorage2DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTextureStorage3DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCompressedTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCopyTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glCopyTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glCopyTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTextureParameterf (GLuint texture, GLenum pname, GLfloat param); + GLAPI void APIENTRY glTextureParameterfv (GLuint texture, GLenum pname, const GLfloat *param); + GLAPI void APIENTRY glTextureParameteri (GLuint texture, GLenum pname, GLint param); + GLAPI void APIENTRY glTextureParameterIiv (GLuint texture, GLenum pname, const GLint *params); + GLAPI void APIENTRY glTextureParameterIuiv (GLuint texture, GLenum pname, const GLuint *params); + GLAPI void APIENTRY glTextureParameteriv (GLuint texture, GLenum pname, const GLint *param); + GLAPI void APIENTRY glGenerateTextureMipmap (GLuint texture); + GLAPI void APIENTRY glBindTextureUnit (GLuint unit, GLuint texture); + GLAPI void APIENTRY glGetTextureImage (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + GLAPI void APIENTRY glGetCompressedTextureImage (GLuint texture, GLint level, GLsizei bufSize, void *pixels); + GLAPI void APIENTRY glGetTextureLevelParameterfv (GLuint texture, GLint level, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetTextureLevelParameteriv (GLuint texture, GLint level, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTextureParameterfv (GLuint texture, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetTextureParameterIiv (GLuint texture, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTextureParameterIuiv (GLuint texture, GLenum pname, GLuint *params); + GLAPI void APIENTRY glGetTextureParameteriv (GLuint texture, GLenum pname, GLint *params); + GLAPI void APIENTRY glCreateVertexArrays (GLsizei n, GLuint *arrays); + GLAPI void APIENTRY glDisableVertexArrayAttrib (GLuint vaobj, GLuint index); + GLAPI void APIENTRY glEnableVertexArrayAttrib (GLuint vaobj, GLuint index); + GLAPI void APIENTRY glVertexArrayElementBuffer (GLuint vaobj, GLuint buffer); + GLAPI void APIENTRY glVertexArrayVertexBuffer (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + GLAPI void APIENTRY glVertexArrayVertexBuffers (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); + GLAPI void APIENTRY glVertexArrayAttribBinding (GLuint vaobj, GLuint attribindex, GLuint bindingindex); + GLAPI void APIENTRY glVertexArrayAttribFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayAttribIFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayAttribLFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayBindingDivisor (GLuint vaobj, GLuint bindingindex, GLuint divisor); + GLAPI void APIENTRY glGetVertexArrayiv (GLuint vaobj, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetVertexArrayIndexediv (GLuint vaobj, GLuint index, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetVertexArrayIndexed64iv (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); + GLAPI void APIENTRY glCreateSamplers (GLsizei n, GLuint *samplers); + GLAPI void APIENTRY glCreateProgramPipelines (GLsizei n, GLuint *pipelines); + GLAPI void APIENTRY glCreateQueries (GLenum target, GLsizei n, GLuint *ids); + GLAPI void APIENTRY glGetQueryBufferObjecti64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + GLAPI void APIENTRY glGetQueryBufferObjectiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + GLAPI void APIENTRY glGetQueryBufferObjectui64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + GLAPI void APIENTRY glGetQueryBufferObjectuiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + GLAPI void APIENTRY glMemoryBarrierByRegion (GLbitfield barriers); + GLAPI void APIENTRY glGetTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + GLAPI void APIENTRY glGetCompressedTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); + GLAPI GLenum APIENTRY glGetGraphicsResetStatus (void); + GLAPI void APIENTRY glGetnCompressedTexImage (GLenum target, GLint lod, GLsizei bufSize, void *pixels); + GLAPI void APIENTRY glGetnTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + GLAPI void APIENTRY glGetnUniformdv (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); + GLAPI void APIENTRY glGetnUniformfv (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); + GLAPI void APIENTRY glGetnUniformiv (GLuint program, GLint location, GLsizei bufSize, GLint *params); + GLAPI void APIENTRY glGetnUniformuiv (GLuint program, GLint location, GLsizei bufSize, GLuint *params); + GLAPI void APIENTRY glReadnPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); + GLAPI void APIENTRY glTextureBarrier (void); +#endif +#endif /* GL_VERSION_4_5 */ + +#ifndef GL_VERSION_4_6 +#define GL_VERSION_4_6 1 +#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551 +#define GL_SPIR_V_BINARY 0x9552 +#define GL_PARAMETER_BUFFER 0x80EE +#define GL_PARAMETER_BUFFER_BINDING 0x80EF +#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 +#define GL_VERTICES_SUBMITTED 0x82EE +#define GL_PRIMITIVES_SUBMITTED 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 +#define GL_POLYGON_OFFSET_CLAMP 0x8E1B +#define GL_SPIR_V_EXTENSIONS 0x9553 +#define GL_NUM_SPIR_V_EXTENSIONS 0x9554 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED + typedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC) (GLfloat factor, GLfloat units, GLfloat clamp); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSpecializeShader (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); + GLAPI void APIENTRY glMultiDrawArraysIndirectCount (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + GLAPI void APIENTRY glMultiDrawElementsIndirectCount (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + GLAPI void APIENTRY glPolygonOffsetClamp (GLfloat factor, GLfloat units, GLfloat clamp); +#endif +#endif /* GL_VERSION_4_6 */ + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#endif /* GL_ARB_ES2_compatibility */ + +#ifndef GL_ARB_ES3_1_compatibility +#define GL_ARB_ES3_1_compatibility 1 +#endif /* GL_ARB_ES3_1_compatibility */ + +#ifndef GL_ARB_ES3_2_compatibility +#define GL_ARB_ES3_2_compatibility 1 +#define GL_PRIMITIVE_BOUNDING_BOX_ARB 0x92BE +#define GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB 0x9381 +#define GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB 0x9382 + typedef void (APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXARBPROC) (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPrimitiveBoundingBoxARB (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +#endif +#endif /* GL_ARB_ES3_2_compatibility */ + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#endif /* GL_ARB_ES3_compatibility */ + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif /* GL_ARB_arrays_of_arrays */ + +#ifndef GL_ARB_base_instance +#define GL_ARB_base_instance 1 +#endif /* GL_ARB_base_instance */ + +#ifndef GL_ARB_bindless_texture +#define GL_ARB_bindless_texture 1 + typedef khronos_uint64_t GLuint64EXT; +#define GL_UNSIGNED_INT64_ARB 0x140F + typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture); + typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler); + typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); + typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle); + typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); + typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access); + typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle); + typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value); + typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); + typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); + typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLuint64 APIENTRY glGetTextureHandleARB (GLuint texture); + GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleARB (GLuint texture, GLuint sampler); + GLAPI void APIENTRY glMakeTextureHandleResidentARB (GLuint64 handle); + GLAPI void APIENTRY glMakeTextureHandleNonResidentARB (GLuint64 handle); + GLAPI GLuint64 APIENTRY glGetImageHandleARB (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); + GLAPI void APIENTRY glMakeImageHandleResidentARB (GLuint64 handle, GLenum access); + GLAPI void APIENTRY glMakeImageHandleNonResidentARB (GLuint64 handle); + GLAPI void APIENTRY glUniformHandleui64ARB (GLint location, GLuint64 value); + GLAPI void APIENTRY glUniformHandleui64vARB (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glProgramUniformHandleui64ARB (GLuint program, GLint location, GLuint64 value); + GLAPI void APIENTRY glProgramUniformHandleui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *values); + GLAPI GLboolean APIENTRY glIsTextureHandleResidentARB (GLuint64 handle); + GLAPI GLboolean APIENTRY glIsImageHandleResidentARB (GLuint64 handle); + GLAPI void APIENTRY glVertexAttribL1ui64ARB (GLuint index, GLuint64EXT x); + GLAPI void APIENTRY glVertexAttribL1ui64vARB (GLuint index, const GLuint64EXT *v); + GLAPI void APIENTRY glGetVertexAttribLui64vARB (GLuint index, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_ARB_bindless_texture */ + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#endif /* GL_ARB_blend_func_extended */ + +#ifndef GL_ARB_buffer_storage +#define GL_ARB_buffer_storage 1 +#endif /* GL_ARB_buffer_storage */ + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 + struct _cl_context; + struct _cl_event; +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 + typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#endif +#endif /* GL_ARB_cl_event */ + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#endif /* GL_ARB_clear_buffer_object */ + +#ifndef GL_ARB_clear_texture +#define GL_ARB_clear_texture 1 +#endif /* GL_ARB_clear_texture */ + +#ifndef GL_ARB_clip_control +#define GL_ARB_clip_control 1 +#endif /* GL_ARB_clip_control */ + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_ARB_compressed_texture_pixel_storage 1 +#endif /* GL_ARB_compressed_texture_pixel_storage */ + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#endif /* GL_ARB_compute_shader */ + +#ifndef GL_ARB_compute_variable_group_size +#define GL_ARB_compute_variable_group_size 1 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB +#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 +#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF + typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDispatchComputeGroupSizeARB (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#endif +#endif /* GL_ARB_compute_variable_group_size */ + +#ifndef GL_ARB_conditional_render_inverted +#define GL_ARB_conditional_render_inverted 1 +#endif /* GL_ARB_conditional_render_inverted */ + +#ifndef GL_ARB_conservative_depth +#define GL_ARB_conservative_depth 1 +#endif /* GL_ARB_conservative_depth */ + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#endif /* GL_ARB_copy_buffer */ + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#endif /* GL_ARB_copy_image */ + +#ifndef GL_ARB_cull_distance +#define GL_ARB_cull_distance 1 +#endif /* GL_ARB_cull_distance */ + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 + typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 + typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); + typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); + typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); + GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const void *userParam); + GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif +#endif /* GL_ARB_debug_output */ + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#endif /* GL_ARB_depth_buffer_float */ + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#endif /* GL_ARB_depth_clamp */ + +#ifndef GL_ARB_derivative_control +#define GL_ARB_derivative_control 1 +#endif /* GL_ARB_derivative_control */ + +#ifndef GL_ARB_direct_state_access +#define GL_ARB_direct_state_access 1 +#endif /* GL_ARB_direct_state_access */ + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 + typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); + typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); + typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); + typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); + GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); + GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); + GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +#endif /* GL_ARB_draw_buffers_blend */ + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#endif /* GL_ARB_draw_elements_base_vertex */ + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#endif /* GL_ARB_draw_indirect */ + +#ifndef GL_ARB_draw_instanced +#define GL_ARB_draw_instanced 1 + typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawArraysInstancedARB (GLenum mode, GLint first, GLsizei count, GLsizei primcount); + GLAPI void APIENTRY glDrawElementsInstancedARB (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_ARB_draw_instanced */ + +#ifndef GL_ARB_enhanced_layouts +#define GL_ARB_enhanced_layouts 1 +#endif /* GL_ARB_enhanced_layouts */ + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif /* GL_ARB_explicit_attrib_location */ + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#endif /* GL_ARB_explicit_uniform_location */ + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif /* GL_ARB_fragment_coord_conventions */ + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif /* GL_ARB_fragment_layer_viewport */ + +#ifndef GL_ARB_fragment_shader_interlock +#define GL_ARB_fragment_shader_interlock 1 +#endif /* GL_ARB_fragment_shader_interlock */ + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#endif /* GL_ARB_framebuffer_no_attachments */ + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#endif /* GL_ARB_framebuffer_object */ + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#endif /* GL_ARB_framebuffer_sRGB */ + +#ifndef GL_ARB_geometry_shader4 +#define GL_ARB_geometry_shader4 1 +#define GL_LINES_ADJACENCY_ARB 0x000A +#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B +#define GL_TRIANGLES_ADJACENCY_ARB 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D +#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 + typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProgramParameteriARB (GLuint program, GLenum pname, GLint value); + GLAPI void APIENTRY glFramebufferTextureARB (GLenum target, GLenum attachment, GLuint texture, GLint level); + GLAPI void APIENTRY glFramebufferTextureLayerARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); + GLAPI void APIENTRY glFramebufferTextureFaceARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_ARB_geometry_shader4 */ + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#endif /* GL_ARB_get_program_binary */ + +#ifndef GL_ARB_get_texture_sub_image +#define GL_ARB_get_texture_sub_image 1 +#endif /* GL_ARB_get_texture_sub_image */ + +#ifndef GL_ARB_gl_spirv +#define GL_ARB_gl_spirv 1 +#define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551 +#define GL_SPIR_V_BINARY_ARB 0x9552 + typedef void (APIENTRYP PFNGLSPECIALIZESHADERARBPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSpecializeShaderARB (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +#endif +#endif /* GL_ARB_gl_spirv */ + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#endif /* GL_ARB_gpu_shader5 */ + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#endif /* GL_ARB_gpu_shader_fp64 */ + +#ifndef GL_ARB_gpu_shader_int64 +#define GL_ARB_gpu_shader_int64 1 +#define GL_INT64_ARB 0x140E +#define GL_INT64_VEC2_ARB 0x8FE9 +#define GL_INT64_VEC3_ARB 0x8FEA +#define GL_INT64_VEC4_ARB 0x8FEB +#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FF7 + typedef void (APIENTRYP PFNGLUNIFORM1I64ARBPROC) (GLint location, GLint64 x); + typedef void (APIENTRYP PFNGLUNIFORM2I64ARBPROC) (GLint location, GLint64 x, GLint64 y); + typedef void (APIENTRYP PFNGLUNIFORM3I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z); + typedef void (APIENTRYP PFNGLUNIFORM4I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); + typedef void (APIENTRYP PFNGLUNIFORM1I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM2I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM3I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM4I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM1UI64ARBPROC) (GLint location, GLuint64 x); + typedef void (APIENTRYP PFNGLUNIFORM2UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y); + typedef void (APIENTRYP PFNGLUNIFORM3UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z); + typedef void (APIENTRYP PFNGLUNIFORM4UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); + typedef void (APIENTRYP PFNGLUNIFORM1UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM2UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM3UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM4UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLGETUNIFORMI64VARBPROC) (GLuint program, GLint location, GLint64 *params); + typedef void (APIENTRYP PFNGLGETUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLuint64 *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint64 *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint64 *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64ARBPROC) (GLuint program, GLint location, GLint64 x); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64ARBPROC) (GLuint program, GLint location, GLuint64 x); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glUniform1i64ARB (GLint location, GLint64 x); + GLAPI void APIENTRY glUniform2i64ARB (GLint location, GLint64 x, GLint64 y); + GLAPI void APIENTRY glUniform3i64ARB (GLint location, GLint64 x, GLint64 y, GLint64 z); + GLAPI void APIENTRY glUniform4i64ARB (GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); + GLAPI void APIENTRY glUniform1i64vARB (GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glUniform2i64vARB (GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glUniform3i64vARB (GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glUniform4i64vARB (GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glUniform1ui64ARB (GLint location, GLuint64 x); + GLAPI void APIENTRY glUniform2ui64ARB (GLint location, GLuint64 x, GLuint64 y); + GLAPI void APIENTRY glUniform3ui64ARB (GLint location, GLuint64 x, GLuint64 y, GLuint64 z); + GLAPI void APIENTRY glUniform4ui64ARB (GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); + GLAPI void APIENTRY glUniform1ui64vARB (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glUniform2ui64vARB (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glUniform3ui64vARB (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glUniform4ui64vARB (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glGetUniformi64vARB (GLuint program, GLint location, GLint64 *params); + GLAPI void APIENTRY glGetUniformui64vARB (GLuint program, GLint location, GLuint64 *params); + GLAPI void APIENTRY glGetnUniformi64vARB (GLuint program, GLint location, GLsizei bufSize, GLint64 *params); + GLAPI void APIENTRY glGetnUniformui64vARB (GLuint program, GLint location, GLsizei bufSize, GLuint64 *params); + GLAPI void APIENTRY glProgramUniform1i64ARB (GLuint program, GLint location, GLint64 x); + GLAPI void APIENTRY glProgramUniform2i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y); + GLAPI void APIENTRY glProgramUniform3i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); + GLAPI void APIENTRY glProgramUniform4i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); + GLAPI void APIENTRY glProgramUniform1i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glProgramUniform2i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glProgramUniform3i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glProgramUniform4i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glProgramUniform1ui64ARB (GLuint program, GLint location, GLuint64 x); + GLAPI void APIENTRY glProgramUniform2ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y); + GLAPI void APIENTRY glProgramUniform3ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); + GLAPI void APIENTRY glProgramUniform4ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); + GLAPI void APIENTRY glProgramUniform1ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glProgramUniform2ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glProgramUniform3ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glProgramUniform4ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +#endif +#endif /* GL_ARB_gpu_shader_int64 */ + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#endif /* GL_ARB_half_float_vertex */ + +#ifndef GL_ARB_imaging +#define GL_ARB_imaging 1 +#endif /* GL_ARB_imaging */ + +#ifndef GL_ARB_indirect_parameters +#define GL_ARB_indirect_parameters 1 +#define GL_PARAMETER_BUFFER_ARB 0x80EE +#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_ARB_indirect_parameters */ + +#ifndef GL_ARB_instanced_arrays +#define GL_ARB_instanced_arrays 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE + typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexAttribDivisorARB (GLuint index, GLuint divisor); +#endif +#endif /* GL_ARB_instanced_arrays */ + +#ifndef GL_ARB_internalformat_query +#define GL_ARB_internalformat_query 1 +#endif /* GL_ARB_internalformat_query */ + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#define GL_SRGB_DECODE_ARB 0x8299 +#define GL_VIEW_CLASS_EAC_R11 0x9383 +#define GL_VIEW_CLASS_EAC_RG11 0x9384 +#define GL_VIEW_CLASS_ETC2_RGB 0x9385 +#define GL_VIEW_CLASS_ETC2_RGBA 0x9386 +#define GL_VIEW_CLASS_ETC2_EAC_RGBA 0x9387 +#define GL_VIEW_CLASS_ASTC_4x4_RGBA 0x9388 +#define GL_VIEW_CLASS_ASTC_5x4_RGBA 0x9389 +#define GL_VIEW_CLASS_ASTC_5x5_RGBA 0x938A +#define GL_VIEW_CLASS_ASTC_6x5_RGBA 0x938B +#define GL_VIEW_CLASS_ASTC_6x6_RGBA 0x938C +#define GL_VIEW_CLASS_ASTC_8x5_RGBA 0x938D +#define GL_VIEW_CLASS_ASTC_8x6_RGBA 0x938E +#define GL_VIEW_CLASS_ASTC_8x8_RGBA 0x938F +#define GL_VIEW_CLASS_ASTC_10x5_RGBA 0x9390 +#define GL_VIEW_CLASS_ASTC_10x6_RGBA 0x9391 +#define GL_VIEW_CLASS_ASTC_10x8_RGBA 0x9392 +#define GL_VIEW_CLASS_ASTC_10x10_RGBA 0x9393 +#define GL_VIEW_CLASS_ASTC_12x10_RGBA 0x9394 +#define GL_VIEW_CLASS_ASTC_12x12_RGBA 0x9395 +#endif /* GL_ARB_internalformat_query2 */ + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#endif /* GL_ARB_invalidate_subdata */ + +#ifndef GL_ARB_map_buffer_alignment +#define GL_ARB_map_buffer_alignment 1 +#endif /* GL_ARB_map_buffer_alignment */ + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#endif /* GL_ARB_map_buffer_range */ + +#ifndef GL_ARB_multi_bind +#define GL_ARB_multi_bind 1 +#endif /* GL_ARB_multi_bind */ + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#endif /* GL_ARB_multi_draw_indirect */ + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#endif /* GL_ARB_occlusion_query2 */ + +#ifndef GL_ARB_parallel_shader_compile +#define GL_ARB_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_ARB 0x91B0 +#define GL_COMPLETION_STATUS_ARB 0x91B1 + typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSARBPROC) (GLuint count); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMaxShaderCompilerThreadsARB (GLuint count); +#endif +#endif /* GL_ARB_parallel_shader_compile */ + +#ifndef GL_ARB_pipeline_statistics_query +#define GL_ARB_pipeline_statistics_query 1 +#define GL_VERTICES_SUBMITTED_ARB 0x82EE +#define GL_PRIMITIVES_SUBMITTED_ARB 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS_ARB 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 +#endif /* GL_ARB_pipeline_statistics_query */ + +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif /* GL_ARB_pixel_buffer_object */ + +#ifndef GL_ARB_polygon_offset_clamp +#define GL_ARB_polygon_offset_clamp 1 +#endif /* GL_ARB_polygon_offset_clamp */ + +#ifndef GL_ARB_post_depth_coverage +#define GL_ARB_post_depth_coverage 1 +#endif /* GL_ARB_post_depth_coverage */ + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#endif /* GL_ARB_program_interface_query */ + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#endif /* GL_ARB_provoking_vertex */ + +#ifndef GL_ARB_query_buffer_object +#define GL_ARB_query_buffer_object 1 +#endif /* GL_ARB_query_buffer_object */ + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif /* GL_ARB_robust_buffer_access_behavior */ + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 + typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); + typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); + typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); + typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img); + typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); + GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); + GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); + GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, void *img); + GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); + GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); + GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); + GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +#endif +#endif /* GL_ARB_robustness */ + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif /* GL_ARB_robustness_isolation */ + +#ifndef GL_ARB_sample_locations +#define GL_ARB_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB 0x9340 +#define GL_SAMPLE_LOCATION_ARB 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB 0x9343 + typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLEVALUATEDEPTHVALUESARBPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferSampleLocationsfvARB (GLenum target, GLuint start, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glNamedFramebufferSampleLocationsfvARB (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glEvaluateDepthValuesARB (void); +#endif +#endif /* GL_ARB_sample_locations */ + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 + typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); +#endif +#endif /* GL_ARB_sample_shading */ + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#endif /* GL_ARB_sampler_objects */ + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#endif /* GL_ARB_seamless_cube_map */ + +#ifndef GL_ARB_seamless_cubemap_per_texture +#define GL_ARB_seamless_cubemap_per_texture 1 +#endif /* GL_ARB_seamless_cubemap_per_texture */ + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#endif /* GL_ARB_separate_shader_objects */ + +#ifndef GL_ARB_shader_atomic_counter_ops +#define GL_ARB_shader_atomic_counter_ops 1 +#endif /* GL_ARB_shader_atomic_counter_ops */ + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ARB_shader_atomic_counters 1 +#endif /* GL_ARB_shader_atomic_counters */ + +#ifndef GL_ARB_shader_ballot +#define GL_ARB_shader_ballot 1 +#endif /* GL_ARB_shader_ballot */ + +#ifndef GL_ARB_shader_bit_encoding +#define GL_ARB_shader_bit_encoding 1 +#endif /* GL_ARB_shader_bit_encoding */ + +#ifndef GL_ARB_shader_clock +#define GL_ARB_shader_clock 1 +#endif /* GL_ARB_shader_clock */ + +#ifndef GL_ARB_shader_draw_parameters +#define GL_ARB_shader_draw_parameters 1 +#endif /* GL_ARB_shader_draw_parameters */ + +#ifndef GL_ARB_shader_group_vote +#define GL_ARB_shader_group_vote 1 +#endif /* GL_ARB_shader_group_vote */ + +#ifndef GL_ARB_shader_image_load_store +#define GL_ARB_shader_image_load_store 1 +#endif /* GL_ARB_shader_image_load_store */ + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif /* GL_ARB_shader_image_size */ + +#ifndef GL_ARB_shader_precision +#define GL_ARB_shader_precision 1 +#endif /* GL_ARB_shader_precision */ + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif /* GL_ARB_shader_stencil_export */ + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#endif /* GL_ARB_shader_storage_buffer_object */ + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#endif /* GL_ARB_shader_subroutine */ + +#ifndef GL_ARB_shader_texture_image_samples +#define GL_ARB_shader_texture_image_samples 1 +#endif /* GL_ARB_shader_texture_image_samples */ + +#ifndef GL_ARB_shader_viewport_layer_array +#define GL_ARB_shader_viewport_layer_array 1 +#endif /* GL_ARB_shader_viewport_layer_array */ + +#ifndef GL_ARB_shading_language_420pack +#define GL_ARB_shading_language_420pack 1 +#endif /* GL_ARB_shading_language_420pack */ + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA + typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); + typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); + typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); + typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); + typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); + typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); + GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); + GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); + GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); + GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); + GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif +#endif /* GL_ARB_shading_language_include */ + +#ifndef GL_ARB_shading_language_packing +#define GL_ARB_shading_language_packing 1 +#endif /* GL_ARB_shading_language_packing */ + +#ifndef GL_ARB_sparse_buffer +#define GL_ARB_sparse_buffer 1 +#define GL_SPARSE_STORAGE_BIT_ARB 0x0400 +#define GL_SPARSE_BUFFER_PAGE_SIZE_ARB 0x82F8 + typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTARBPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); + typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); + typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBufferPageCommitmentARB (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); + GLAPI void APIENTRY glNamedBufferPageCommitmentEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); + GLAPI void APIENTRY glNamedBufferPageCommitmentARB (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_buffer */ + +#ifndef GL_ARB_sparse_texture +#define GL_ARB_sparse_texture 1 +#define GL_TEXTURE_SPARSE_ARB 0x91A6 +#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 +#define GL_NUM_SPARSE_LEVELS_ARB 0x91AA +#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 +#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 + typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexPageCommitmentARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_texture */ + +#ifndef GL_ARB_sparse_texture2 +#define GL_ARB_sparse_texture2 1 +#endif /* GL_ARB_sparse_texture2 */ + +#ifndef GL_ARB_sparse_texture_clamp +#define GL_ARB_sparse_texture_clamp 1 +#endif /* GL_ARB_sparse_texture_clamp */ + +#ifndef GL_ARB_spirv_extensions +#define GL_ARB_spirv_extensions 1 +#endif /* GL_ARB_spirv_extensions */ + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#endif /* GL_ARB_stencil_texturing */ + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#endif /* GL_ARB_sync */ + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#endif /* GL_ARB_tessellation_shader */ + +#ifndef GL_ARB_texture_barrier +#define GL_ARB_texture_barrier 1 +#endif /* GL_ARB_texture_barrier */ + +#ifndef GL_ARB_texture_border_clamp +#define GL_ARB_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif /* GL_ARB_texture_border_clamp */ + +#ifndef GL_ARB_texture_buffer_object +#define GL_ARB_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_ARB 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E + typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexBufferARB (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_ARB_texture_buffer_object */ + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#endif /* GL_ARB_texture_buffer_object_rgb32 */ + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#endif /* GL_ARB_texture_buffer_range */ + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif /* GL_ARB_texture_compression_bptc */ + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#endif /* GL_ARB_texture_compression_rgtc */ + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif /* GL_ARB_texture_cube_map_array */ + +#ifndef GL_ARB_texture_filter_anisotropic +#define GL_ARB_texture_filter_anisotropic 1 +#endif /* GL_ARB_texture_filter_anisotropic */ + +#ifndef GL_ARB_texture_filter_minmax +#define GL_ARB_texture_filter_minmax 1 +#define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366 +#define GL_WEIGHTED_AVERAGE_ARB 0x9367 +#endif /* GL_ARB_texture_filter_minmax */ + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F +#endif /* GL_ARB_texture_gather */ + +#ifndef GL_ARB_texture_mirror_clamp_to_edge +#define GL_ARB_texture_mirror_clamp_to_edge 1 +#endif /* GL_ARB_texture_mirror_clamp_to_edge */ + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif /* GL_ARB_texture_mirrored_repeat */ + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#endif /* GL_ARB_texture_multisample */ + +#ifndef GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_non_power_of_two 1 +#endif /* GL_ARB_texture_non_power_of_two */ + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif /* GL_ARB_texture_query_levels */ + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif /* GL_ARB_texture_query_lod */ + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#endif /* GL_ARB_texture_rg */ + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#endif /* GL_ARB_texture_rgb10_a2ui */ + +#ifndef GL_ARB_texture_stencil8 +#define GL_ARB_texture_stencil8 1 +#endif /* GL_ARB_texture_stencil8 */ + +#ifndef GL_ARB_texture_storage +#define GL_ARB_texture_storage 1 +#endif /* GL_ARB_texture_storage */ + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#endif /* GL_ARB_texture_storage_multisample */ + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#endif /* GL_ARB_texture_swizzle */ + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#endif /* GL_ARB_texture_view */ + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#endif /* GL_ARB_timer_query */ + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#endif /* GL_ARB_transform_feedback2 */ + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#endif /* GL_ARB_transform_feedback3 */ + +#ifndef GL_ARB_transform_feedback_instanced +#define GL_ARB_transform_feedback_instanced 1 +#endif /* GL_ARB_transform_feedback_instanced */ + +#ifndef GL_ARB_transform_feedback_overflow_query +#define GL_ARB_transform_feedback_overflow_query 1 +#define GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB 0x82ED +#endif /* GL_ARB_transform_feedback_overflow_query */ + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#endif /* GL_ARB_uniform_buffer_object */ + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#endif /* GL_ARB_vertex_array_bgra */ + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#endif /* GL_ARB_vertex_array_object */ + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#endif /* GL_ARB_vertex_attrib_64bit */ + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#endif /* GL_ARB_vertex_attrib_binding */ + +#ifndef GL_ARB_vertex_type_10f_11f_11f_rev +#define GL_ARB_vertex_type_10f_11f_11f_rev 1 +#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#endif /* GL_ARB_vertex_type_2_10_10_10_rev */ + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 + typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYDVNVPROC) (GLuint first, GLsizei count, const GLdouble *v); + typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDDNVPROC) (GLuint index, GLdouble n, GLdouble f); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDepthRangeArraydvNV (GLuint first, GLsizei count, const GLdouble *v); + GLAPI void APIENTRY glDepthRangeIndexeddNV (GLuint index, GLdouble n, GLdouble f); +#endif +#endif /* GL_ARB_viewport_array */ + +#ifndef GL_KHR_blend_equation_advanced +#define GL_KHR_blend_equation_advanced 1 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_SCREEN_KHR 0x9295 +#define GL_OVERLAY_KHR 0x9296 +#define GL_DARKEN_KHR 0x9297 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLORBURN_KHR 0x929A +#define GL_HARDLIGHT_KHR 0x929B +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_DIFFERENCE_KHR 0x929E +#define GL_EXCLUSION_KHR 0x92A0 +#define GL_HSL_HUE_KHR 0x92AD +#define GL_HSL_SATURATION_KHR 0x92AE +#define GL_HSL_COLOR_KHR 0x92AF +#define GL_HSL_LUMINOSITY_KHR 0x92B0 + typedef void (APIENTRYP PFNGLBLENDBARRIERKHRPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendBarrierKHR (void); +#endif +#endif /* GL_KHR_blend_equation_advanced */ + +#ifndef GL_KHR_blend_equation_advanced_coherent +#define GL_KHR_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#endif /* GL_KHR_blend_equation_advanced_coherent */ + +#ifndef GL_KHR_context_flush_control +#define GL_KHR_context_flush_control 1 +#endif /* GL_KHR_context_flush_control */ + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#endif /* GL_KHR_debug */ + +#ifndef GL_KHR_no_error +#define GL_KHR_no_error 1 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 +#endif /* GL_KHR_no_error */ + +#ifndef GL_KHR_parallel_shader_compile +#define GL_KHR_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 +#define GL_COMPLETION_STATUS_KHR 0x91B1 + typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) (GLuint count); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMaxShaderCompilerThreadsKHR (GLuint count); +#endif +#endif /* GL_KHR_parallel_shader_compile */ + +#ifndef GL_KHR_robust_buffer_access_behavior +#define GL_KHR_robust_buffer_access_behavior 1 +#endif /* GL_KHR_robust_buffer_access_behavior */ + +#ifndef GL_KHR_robustness +#define GL_KHR_robustness 1 +#define GL_CONTEXT_ROBUST_ACCESS 0x90F3 +#endif /* GL_KHR_robustness */ + +#ifndef GL_KHR_shader_subgroup +#define GL_KHR_shader_subgroup 1 +#define GL_SUBGROUP_SIZE_KHR 0x9532 +#define GL_SUBGROUP_SUPPORTED_STAGES_KHR 0x9533 +#define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534 +#define GL_SUBGROUP_QUAD_ALL_STAGES_KHR 0x9535 +#define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001 +#define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR 0x00000002 +#define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004 +#define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008 +#define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010 +#define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020 +#define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040 +#define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR 0x00000080 +#endif /* GL_KHR_shader_subgroup */ + +#ifndef GL_KHR_texture_compression_astc_hdr +#define GL_KHR_texture_compression_astc_hdr 1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif /* GL_KHR_texture_compression_astc_hdr */ + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#endif /* GL_KHR_texture_compression_astc_ldr */ + +#ifndef GL_KHR_texture_compression_astc_sliced_3d +#define GL_KHR_texture_compression_astc_sliced_3d 1 +#endif /* GL_KHR_texture_compression_astc_sliced_3d */ + +#ifndef GL_AMD_framebuffer_multisample_advanced +#define GL_AMD_framebuffer_multisample_advanced 1 +#define GL_RENDERBUFFER_STORAGE_SAMPLES_AMD 0x91B2 +#define GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD 0x91B3 +#define GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD 0x91B4 +#define GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD 0x91B5 +#define GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B6 +#define GL_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B7 + typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glRenderbufferStorageMultisampleAdvancedAMD (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleAdvancedAMD (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_AMD_framebuffer_multisample_advanced */ + +#ifndef GL_AMD_performance_monitor +#define GL_AMD_performance_monitor 1 +#define GL_COUNTER_TYPE_AMD 0x8BC0 +#define GL_COUNTER_RANGE_AMD 0x8BC1 +#define GL_UNSIGNED_INT64_AMD 0x8BC2 +#define GL_PERCENTAGE_AMD 0x8BC3 +#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 +#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 +#define GL_PERFMON_RESULT_AMD 0x8BC6 + typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); + typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); + typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); + typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); + typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data); + typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); + typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); + typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); + typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); + typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); + typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); + GLAPI void APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); + GLAPI void APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); + GLAPI void APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); + GLAPI void APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data); + GLAPI void APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); + GLAPI void APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); + GLAPI void APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); + GLAPI void APIENTRY glBeginPerfMonitorAMD (GLuint monitor); + GLAPI void APIENTRY glEndPerfMonitorAMD (GLuint monitor); + GLAPI void APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif +#endif /* GL_AMD_performance_monitor */ + +#ifndef GL_APPLE_rgb_422 +#define GL_APPLE_rgb_422 1 +#define GL_RGB_422_APPLE 0x8A1F +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#define GL_RGB_RAW_422_APPLE 0x8A51 +#endif /* GL_APPLE_rgb_422 */ + +#ifndef GL_EXT_EGL_image_storage +#define GL_EXT_EGL_image_storage 1 + typedef void *GLeglImageOES; + typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC) (GLenum target, GLeglImageOES image, const GLint* attrib_list); + typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC) (GLuint texture, GLeglImageOES image, const GLint* attrib_list); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glEGLImageTargetTexStorageEXT (GLenum target, GLeglImageOES image, const GLint* attrib_list); + GLAPI void APIENTRY glEGLImageTargetTextureStorageEXT (GLuint texture, GLeglImageOES image, const GLint* attrib_list); +#endif +#endif /* GL_EXT_EGL_image_storage */ + +#ifndef GL_EXT_EGL_sync +#define GL_EXT_EGL_sync 1 +#endif /* GL_EXT_EGL_sync */ + +#ifndef GL_EXT_debug_label +#define GL_EXT_debug_label 1 +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 + typedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label); + typedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glLabelObjectEXT (GLenum type, GLuint object, GLsizei length, const GLchar *label); + GLAPI void APIENTRY glGetObjectLabelEXT (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_EXT_debug_label */ + +#ifndef GL_EXT_debug_marker +#define GL_EXT_debug_marker 1 + typedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker); + typedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker); + typedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glInsertEventMarkerEXT (GLsizei length, const GLchar *marker); + GLAPI void APIENTRY glPushGroupMarkerEXT (GLsizei length, const GLchar *marker); + GLAPI void APIENTRY glPopGroupMarkerEXT (void); +#endif +#endif /* GL_EXT_debug_marker */ + +#ifndef GL_EXT_direct_state_access +#define GL_EXT_direct_state_access 1 +#define GL_PROGRAM_MATRIX_EXT 0x8E2D +#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E +#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F + typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m); + typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m); + typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); + typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); + typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); + typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode); + typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode); + typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); + typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture); + typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); + typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); + typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); + typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); + typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); + typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); + typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data); + typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data); + typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, void **data); + typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); + typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); + typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); + typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); + typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, void *img); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, void *img); + typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); + typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); + typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); + typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access); + typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, void **params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); + typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); + typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params); + typedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); + typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); + typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params); + typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params); + typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, void *string); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); + typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target); + typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target); + typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); + typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); + typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); + typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); + typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); + typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); + typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); + typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC) (GLuint vaobj, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC) (GLuint vaobj, GLenum pname, void **param); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param); + typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); + typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); + typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); + typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC) (GLuint vaobj, GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMatrixLoadfEXT (GLenum mode, const GLfloat *m); + GLAPI void APIENTRY glMatrixLoaddEXT (GLenum mode, const GLdouble *m); + GLAPI void APIENTRY glMatrixMultfEXT (GLenum mode, const GLfloat *m); + GLAPI void APIENTRY glMatrixMultdEXT (GLenum mode, const GLdouble *m); + GLAPI void APIENTRY glMatrixLoadIdentityEXT (GLenum mode); + GLAPI void APIENTRY glMatrixRotatefEXT (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glMatrixRotatedEXT (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glMatrixScalefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glMatrixScaledEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glMatrixTranslatefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glMatrixTranslatedEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glMatrixFrustumEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); + GLAPI void APIENTRY glMatrixOrthoEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); + GLAPI void APIENTRY glMatrixPopEXT (GLenum mode); + GLAPI void APIENTRY glMatrixPushEXT (GLenum mode); + GLAPI void APIENTRY glClientAttribDefaultEXT (GLbitfield mask); + GLAPI void APIENTRY glPushClientAttribDefaultEXT (GLbitfield mask); + GLAPI void APIENTRY glTextureParameterfEXT (GLuint texture, GLenum target, GLenum pname, GLfloat param); + GLAPI void APIENTRY glTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glTextureParameteriEXT (GLuint texture, GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCopyTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + GLAPI void APIENTRY glCopyTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + GLAPI void APIENTRY glCopyTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glCopyTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetTextureImageEXT (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); + GLAPI void APIENTRY glGetTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTextureLevelParameterfvEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetTextureLevelParameterivEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); + GLAPI void APIENTRY glTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCopyTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glBindMultiTextureEXT (GLenum texunit, GLenum target, GLuint texture); + GLAPI void APIENTRY glMultiTexCoordPointerEXT (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glMultiTexEnvfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); + GLAPI void APIENTRY glMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glMultiTexEnviEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glMultiTexGendEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); + GLAPI void APIENTRY glMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); + GLAPI void APIENTRY glMultiTexGenfEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); + GLAPI void APIENTRY glMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glMultiTexGeniEXT (GLenum texunit, GLenum coord, GLenum pname, GLint param); + GLAPI void APIENTRY glMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); + GLAPI void APIENTRY glGetMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); + GLAPI void APIENTRY glGetMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, GLint *params); + GLAPI void APIENTRY glMultiTexParameteriEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glMultiTexParameterfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); + GLAPI void APIENTRY glMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCopyMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + GLAPI void APIENTRY glCopyMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + GLAPI void APIENTRY glCopyMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glCopyMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetMultiTexImageEXT (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); + GLAPI void APIENTRY glGetMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetMultiTexLevelParameterfvEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMultiTexLevelParameterivEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); + GLAPI void APIENTRY glMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCopyMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glEnableClientStateIndexedEXT (GLenum array, GLuint index); + GLAPI void APIENTRY glDisableClientStateIndexedEXT (GLenum array, GLuint index); + GLAPI void APIENTRY glGetFloatIndexedvEXT (GLenum target, GLuint index, GLfloat *data); + GLAPI void APIENTRY glGetDoubleIndexedvEXT (GLenum target, GLuint index, GLdouble *data); + GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, void **data); + GLAPI void APIENTRY glEnableIndexedEXT (GLenum target, GLuint index); + GLAPI void APIENTRY glDisableIndexedEXT (GLenum target, GLuint index); + GLAPI GLboolean APIENTRY glIsEnabledIndexedEXT (GLenum target, GLuint index); + GLAPI void APIENTRY glGetIntegerIndexedvEXT (GLenum target, GLuint index, GLint *data); + GLAPI void APIENTRY glGetBooleanIndexedvEXT (GLenum target, GLuint index, GLboolean *data); + GLAPI void APIENTRY glCompressedTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glGetCompressedTextureImageEXT (GLuint texture, GLenum target, GLint lod, void *img); + GLAPI void APIENTRY glCompressedMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glGetCompressedMultiTexImageEXT (GLenum texunit, GLenum target, GLint lod, void *img); + GLAPI void APIENTRY glMatrixLoadTransposefEXT (GLenum mode, const GLfloat *m); + GLAPI void APIENTRY glMatrixLoadTransposedEXT (GLenum mode, const GLdouble *m); + GLAPI void APIENTRY glMatrixMultTransposefEXT (GLenum mode, const GLfloat *m); + GLAPI void APIENTRY glMatrixMultTransposedEXT (GLenum mode, const GLdouble *m); + GLAPI void APIENTRY glNamedBufferDataEXT (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); + GLAPI void APIENTRY glNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + GLAPI void *APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access); + GLAPI GLboolean APIENTRY glUnmapNamedBufferEXT (GLuint buffer); + GLAPI void APIENTRY glGetNamedBufferParameterivEXT (GLuint buffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, void **params); + GLAPI void APIENTRY glGetNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); + GLAPI void APIENTRY glProgramUniform1fEXT (GLuint program, GLint location, GLfloat v0); + GLAPI void APIENTRY glProgramUniform2fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1); + GLAPI void APIENTRY glProgramUniform3fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + GLAPI void APIENTRY glProgramUniform4fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + GLAPI void APIENTRY glProgramUniform1iEXT (GLuint program, GLint location, GLint v0); + GLAPI void APIENTRY glProgramUniform2iEXT (GLuint program, GLint location, GLint v0, GLint v1); + GLAPI void APIENTRY glProgramUniform3iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); + GLAPI void APIENTRY glProgramUniform4iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + GLAPI void APIENTRY glProgramUniform1fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform2fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform3fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform4fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform1ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform2ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform3ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform4ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniformMatrix2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix2x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix2x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glTextureBufferEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); + GLAPI void APIENTRY glMultiTexBufferEXT (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); + GLAPI void APIENTRY glTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, const GLuint *params); + GLAPI void APIENTRY glGetTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, GLuint *params); + GLAPI void APIENTRY glMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); + GLAPI void APIENTRY glGetMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, GLuint *params); + GLAPI void APIENTRY glProgramUniform1uiEXT (GLuint program, GLint location, GLuint v0); + GLAPI void APIENTRY glProgramUniform2uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1); + GLAPI void APIENTRY glProgramUniform3uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); + GLAPI void APIENTRY glProgramUniform4uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + GLAPI void APIENTRY glProgramUniform1uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform2uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform3uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform4uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glNamedProgramLocalParameters4fvEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); + GLAPI void APIENTRY glNamedProgramLocalParameterI4iEXT (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); + GLAPI void APIENTRY glNamedProgramLocalParameterI4ivEXT (GLuint program, GLenum target, GLuint index, const GLint *params); + GLAPI void APIENTRY glNamedProgramLocalParametersI4ivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); + GLAPI void APIENTRY glNamedProgramLocalParameterI4uiEXT (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + GLAPI void APIENTRY glNamedProgramLocalParameterI4uivEXT (GLuint program, GLenum target, GLuint index, const GLuint *params); + GLAPI void APIENTRY glNamedProgramLocalParametersI4uivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); + GLAPI void APIENTRY glGetNamedProgramLocalParameterIivEXT (GLuint program, GLenum target, GLuint index, GLint *params); + GLAPI void APIENTRY glGetNamedProgramLocalParameterIuivEXT (GLuint program, GLenum target, GLuint index, GLuint *params); + GLAPI void APIENTRY glEnableClientStateiEXT (GLenum array, GLuint index); + GLAPI void APIENTRY glDisableClientStateiEXT (GLenum array, GLuint index); + GLAPI void APIENTRY glGetFloati_vEXT (GLenum pname, GLuint index, GLfloat *params); + GLAPI void APIENTRY glGetDoublei_vEXT (GLenum pname, GLuint index, GLdouble *params); + GLAPI void APIENTRY glGetPointeri_vEXT (GLenum pname, GLuint index, void **params); + GLAPI void APIENTRY glNamedProgramStringEXT (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); + GLAPI void APIENTRY glNamedProgramLocalParameter4dEXT (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glNamedProgramLocalParameter4dvEXT (GLuint program, GLenum target, GLuint index, const GLdouble *params); + GLAPI void APIENTRY glNamedProgramLocalParameter4fEXT (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glNamedProgramLocalParameter4fvEXT (GLuint program, GLenum target, GLuint index, const GLfloat *params); + GLAPI void APIENTRY glGetNamedProgramLocalParameterdvEXT (GLuint program, GLenum target, GLuint index, GLdouble *params); + GLAPI void APIENTRY glGetNamedProgramLocalParameterfvEXT (GLuint program, GLenum target, GLuint index, GLfloat *params); + GLAPI void APIENTRY glGetNamedProgramivEXT (GLuint program, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetNamedProgramStringEXT (GLuint program, GLenum target, GLenum pname, void *string); + GLAPI void APIENTRY glNamedRenderbufferStorageEXT (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetNamedRenderbufferParameterivEXT (GLuint renderbuffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleEXT (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleCoverageEXT (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI GLenum APIENTRY glCheckNamedFramebufferStatusEXT (GLuint framebuffer, GLenum target); + GLAPI void APIENTRY glNamedFramebufferTexture1DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + GLAPI void APIENTRY glNamedFramebufferTexture2DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + GLAPI void APIENTRY glNamedFramebufferTexture3DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); + GLAPI void APIENTRY glNamedFramebufferRenderbufferEXT (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameterivEXT (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); + GLAPI void APIENTRY glGenerateTextureMipmapEXT (GLuint texture, GLenum target); + GLAPI void APIENTRY glGenerateMultiTexMipmapEXT (GLenum texunit, GLenum target); + GLAPI void APIENTRY glFramebufferDrawBufferEXT (GLuint framebuffer, GLenum mode); + GLAPI void APIENTRY glFramebufferDrawBuffersEXT (GLuint framebuffer, GLsizei n, const GLenum *bufs); + GLAPI void APIENTRY glFramebufferReadBufferEXT (GLuint framebuffer, GLenum mode); + GLAPI void APIENTRY glGetFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glNamedCopyBufferSubDataEXT (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + GLAPI void APIENTRY glNamedFramebufferTextureEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); + GLAPI void APIENTRY glNamedFramebufferTextureLayerEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); + GLAPI void APIENTRY glNamedFramebufferTextureFaceEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); + GLAPI void APIENTRY glTextureRenderbufferEXT (GLuint texture, GLenum target, GLuint renderbuffer); + GLAPI void APIENTRY glMultiTexRenderbufferEXT (GLenum texunit, GLenum target, GLuint renderbuffer); + GLAPI void APIENTRY glVertexArrayVertexOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayEdgeFlagOffsetEXT (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayIndexOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayNormalOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayMultiTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayFogCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArraySecondaryColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayVertexAttribOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayVertexAttribIOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glEnableVertexArrayEXT (GLuint vaobj, GLenum array); + GLAPI void APIENTRY glDisableVertexArrayEXT (GLuint vaobj, GLenum array); + GLAPI void APIENTRY glEnableVertexArrayAttribEXT (GLuint vaobj, GLuint index); + GLAPI void APIENTRY glDisableVertexArrayAttribEXT (GLuint vaobj, GLuint index); + GLAPI void APIENTRY glGetVertexArrayIntegervEXT (GLuint vaobj, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetVertexArrayPointervEXT (GLuint vaobj, GLenum pname, void **param); + GLAPI void APIENTRY glGetVertexArrayIntegeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetVertexArrayPointeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, void **param); + GLAPI void *APIENTRY glMapNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); + GLAPI void APIENTRY glFlushMappedNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length); + GLAPI void APIENTRY glNamedBufferStorageEXT (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); + GLAPI void APIENTRY glClearNamedBufferDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glClearNamedBufferSubDataEXT (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glNamedFramebufferParameteriEXT (GLuint framebuffer, GLenum pname, GLint param); + GLAPI void APIENTRY glGetNamedFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glProgramUniform1dEXT (GLuint program, GLint location, GLdouble x); + GLAPI void APIENTRY glProgramUniform2dEXT (GLuint program, GLint location, GLdouble x, GLdouble y); + GLAPI void APIENTRY glProgramUniform3dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glProgramUniform4dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glProgramUniform1dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform2dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform3dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform4dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix2x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix2x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glTextureBufferRangeEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glTextureStorage1DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + GLAPI void APIENTRY glTextureStorage2DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTextureStorage3DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + GLAPI void APIENTRY glTextureStorage2DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTextureStorage3DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glVertexArrayBindVertexBufferEXT (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + GLAPI void APIENTRY glVertexArrayVertexAttribFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayVertexAttribIFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayVertexAttribLFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayVertexAttribBindingEXT (GLuint vaobj, GLuint attribindex, GLuint bindingindex); + GLAPI void APIENTRY glVertexArrayVertexBindingDivisorEXT (GLuint vaobj, GLuint bindingindex, GLuint divisor); + GLAPI void APIENTRY glVertexArrayVertexAttribLOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glTexturePageCommitmentEXT (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); + GLAPI void APIENTRY glVertexArrayVertexAttribDivisorEXT (GLuint vaobj, GLuint index, GLuint divisor); +#endif +#endif /* GL_EXT_direct_state_access */ + +#ifndef GL_EXT_draw_instanced +#define GL_EXT_draw_instanced 1 + typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawArraysInstancedEXT (GLenum mode, GLint start, GLsizei count, GLsizei primcount); + GLAPI void APIENTRY glDrawElementsInstancedEXT (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_EXT_draw_instanced */ + +#ifndef GL_EXT_multiview_tessellation_geometry_shader +#define GL_EXT_multiview_tessellation_geometry_shader 1 +#endif /* GL_EXT_multiview_tessellation_geometry_shader */ + +#ifndef GL_EXT_multiview_texture_multisample +#define GL_EXT_multiview_texture_multisample 1 +#endif /* GL_EXT_multiview_texture_multisample */ + +#ifndef GL_EXT_multiview_timer_query +#define GL_EXT_multiview_timer_query 1 +#endif /* GL_EXT_multiview_timer_query */ + +#ifndef GL_EXT_polygon_offset_clamp +#define GL_EXT_polygon_offset_clamp 1 +#define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B + typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPEXTPROC) (GLfloat factor, GLfloat units, GLfloat clamp); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPolygonOffsetClampEXT (GLfloat factor, GLfloat units, GLfloat clamp); +#endif +#endif /* GL_EXT_polygon_offset_clamp */ + +#ifndef GL_EXT_post_depth_coverage +#define GL_EXT_post_depth_coverage 1 +#endif /* GL_EXT_post_depth_coverage */ + +#ifndef GL_EXT_raster_multisample +#define GL_EXT_raster_multisample 1 +#define GL_RASTER_MULTISAMPLE_EXT 0x9327 +#define GL_RASTER_SAMPLES_EXT 0x9328 +#define GL_MAX_RASTER_SAMPLES_EXT 0x9329 +#define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A +#define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B +#define GL_EFFECTIVE_RASTER_SAMPLES_EXT 0x932C + typedef void (APIENTRYP PFNGLRASTERSAMPLESEXTPROC) (GLuint samples, GLboolean fixedsamplelocations); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glRasterSamplesEXT (GLuint samples, GLboolean fixedsamplelocations); +#endif +#endif /* GL_EXT_raster_multisample */ + +#ifndef GL_EXT_separate_shader_objects +#define GL_EXT_separate_shader_objects 1 +#define GL_ACTIVE_PROGRAM_EXT 0x8B8D + typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program); + typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program); + typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glUseShaderProgramEXT (GLenum type, GLuint program); + GLAPI void APIENTRY glActiveProgramEXT (GLuint program); + GLAPI GLuint APIENTRY glCreateShaderProgramEXT (GLenum type, const GLchar *string); +#endif +#endif /* GL_EXT_separate_shader_objects */ + +#ifndef GL_EXT_shader_framebuffer_fetch +#define GL_EXT_shader_framebuffer_fetch 1 +#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52 +#endif /* GL_EXT_shader_framebuffer_fetch */ + +#ifndef GL_EXT_shader_framebuffer_fetch_non_coherent +#define GL_EXT_shader_framebuffer_fetch_non_coherent 1 + typedef void (APIENTRYP PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferFetchBarrierEXT (void); +#endif +#endif /* GL_EXT_shader_framebuffer_fetch_non_coherent */ + +#ifndef GL_EXT_shader_integer_mix +#define GL_EXT_shader_integer_mix 1 +#endif /* GL_EXT_shader_integer_mix */ + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_compression_s3tc 1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif /* GL_EXT_texture_compression_s3tc */ + +#ifndef GL_EXT_texture_filter_minmax +#define GL_EXT_texture_filter_minmax 1 +#define GL_TEXTURE_REDUCTION_MODE_EXT 0x9366 +#define GL_WEIGHTED_AVERAGE_EXT 0x9367 +#endif /* GL_EXT_texture_filter_minmax */ + +#ifndef GL_EXT_texture_sRGB_R8 +#define GL_EXT_texture_sRGB_R8 1 +#define GL_SR8_EXT 0x8FBD +#endif /* GL_EXT_texture_sRGB_R8 */ + +#ifndef GL_EXT_texture_sRGB_RG8 +#define GL_EXT_texture_sRGB_RG8 1 +#define GL_SRG8_EXT 0x8FBE +#endif /* GL_EXT_texture_sRGB_RG8 */ + +#ifndef GL_EXT_texture_sRGB_decode +#define GL_EXT_texture_sRGB_decode 1 +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#define GL_DECODE_EXT 0x8A49 +#define GL_SKIP_DECODE_EXT 0x8A4A +#endif /* GL_EXT_texture_sRGB_decode */ + +#ifndef GL_EXT_texture_shadow_lod +#define GL_EXT_texture_shadow_lod 1 +#endif /* GL_EXT_texture_shadow_lod */ + +#ifndef GL_EXT_texture_storage +#define GL_EXT_texture_storage 1 +#define GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F +#define GL_ALPHA8_EXT 0x803C +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_RGBA32F_EXT 0x8814 +#define GL_RGB32F_EXT 0x8815 +#define GL_ALPHA32F_EXT 0x8816 +#define GL_LUMINANCE32F_EXT 0x8818 +#define GL_LUMINANCE_ALPHA32F_EXT 0x8819 +#define GL_RGBA16F_EXT 0x881A +#define GL_RGB16F_EXT 0x881B +#define GL_ALPHA16F_EXT 0x881C +#define GL_LUMINANCE16F_EXT 0x881E +#define GL_LUMINANCE_ALPHA16F_EXT 0x881F +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGB10_EXT 0x8052 +#define GL_BGRA8_EXT 0x93A1 +#define GL_R8_EXT 0x8229 +#define GL_RG8_EXT 0x822B +#define GL_R32F_EXT 0x822E +#define GL_RG32F_EXT 0x8230 +#define GL_R16F_EXT 0x822D +#define GL_RG16F_EXT 0x822F + typedef void (APIENTRYP PFNGLTEXSTORAGE1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + typedef void (APIENTRYP PFNGLTEXSTORAGE2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXSTORAGE3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexStorage1DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + GLAPI void APIENTRY glTexStorage2DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTexStorage3DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* GL_EXT_texture_storage */ + +#ifndef GL_EXT_window_rectangles +#define GL_EXT_window_rectangles 1 +#define GL_INCLUSIVE_EXT 0x8F10 +#define GL_EXCLUSIVE_EXT 0x8F11 +#define GL_WINDOW_RECTANGLE_EXT 0x8F12 +#define GL_WINDOW_RECTANGLE_MODE_EXT 0x8F13 +#define GL_MAX_WINDOW_RECTANGLES_EXT 0x8F14 +#define GL_NUM_WINDOW_RECTANGLES_EXT 0x8F15 + typedef void (APIENTRYP PFNGLWINDOWRECTANGLESEXTPROC) (GLenum mode, GLsizei count, const GLint *box); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glWindowRectanglesEXT (GLenum mode, GLsizei count, const GLint *box); +#endif +#endif /* GL_EXT_window_rectangles */ + +#ifndef GL_INTEL_blackhole_render +#define GL_INTEL_blackhole_render 1 +#define GL_BLACKHOLE_RENDER_INTEL 0x83FC +#endif /* GL_INTEL_blackhole_render */ + +#ifndef GL_INTEL_conservative_rasterization +#define GL_INTEL_conservative_rasterization 1 +#define GL_CONSERVATIVE_RASTERIZATION_INTEL 0x83FE +#endif /* GL_INTEL_conservative_rasterization */ + +#ifndef GL_INTEL_framebuffer_CMAA +#define GL_INTEL_framebuffer_CMAA 1 + typedef void (APIENTRYP PFNGLAPPLYFRAMEBUFFERATTACHMENTCMAAINTELPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glApplyFramebufferAttachmentCMAAINTEL (void); +#endif +#endif /* GL_INTEL_framebuffer_CMAA */ + +#ifndef GL_INTEL_performance_query +#define GL_INTEL_performance_query 1 +#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000 +#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001 +#define GL_PERFQUERY_WAIT_INTEL 0x83FB +#define GL_PERFQUERY_FLUSH_INTEL 0x83FA +#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9 +#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0 +#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1 +#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2 +#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3 +#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4 +#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5 +#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8 +#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9 +#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA +#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB +#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC +#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD +#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE +#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF +#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500 + typedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle); + typedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle); + typedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle); + typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle); + typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId); + typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId); + typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); + typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten); + typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId); + typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBeginPerfQueryINTEL (GLuint queryHandle); + GLAPI void APIENTRY glCreatePerfQueryINTEL (GLuint queryId, GLuint *queryHandle); + GLAPI void APIENTRY glDeletePerfQueryINTEL (GLuint queryHandle); + GLAPI void APIENTRY glEndPerfQueryINTEL (GLuint queryHandle); + GLAPI void APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId); + GLAPI void APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId); + GLAPI void APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); + GLAPI void APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten); + GLAPI void APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId); + GLAPI void APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#endif +#endif /* GL_INTEL_performance_query */ + +#ifndef GL_MESA_framebuffer_flip_x +#define GL_MESA_framebuffer_flip_x 1 +#define GL_FRAMEBUFFER_FLIP_X_MESA 0x8BBC +#endif /* GL_MESA_framebuffer_flip_x */ + +#ifndef GL_MESA_framebuffer_flip_y +#define GL_MESA_framebuffer_flip_y 1 +#define GL_FRAMEBUFFER_FLIP_Y_MESA 0x8BBB + typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIMESAPROC) (GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC) (GLenum target, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferParameteriMESA (GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glGetFramebufferParameterivMESA (GLenum target, GLenum pname, GLint *params); +#endif +#endif /* GL_MESA_framebuffer_flip_y */ + +#ifndef GL_MESA_framebuffer_swap_xy +#define GL_MESA_framebuffer_swap_xy 1 +#define GL_FRAMEBUFFER_SWAP_XY_MESA 0x8BBD +#endif /* GL_MESA_framebuffer_swap_xy */ + +#ifndef GL_NV_bindless_multi_draw_indirect +#define GL_NV_bindless_multi_draw_indirect 1 + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); + GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect */ + +#ifndef GL_NV_bindless_multi_draw_indirect_count +#define GL_NV_bindless_multi_draw_indirect_count 1 + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessCountNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); + GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessCountNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect_count */ + +#ifndef GL_NV_bindless_texture +#define GL_NV_bindless_texture 1 + typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture); + typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler); + typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); + typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle); + typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); + typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access); + typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle); + typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value); + typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); + typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); + typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLuint64 APIENTRY glGetTextureHandleNV (GLuint texture); + GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleNV (GLuint texture, GLuint sampler); + GLAPI void APIENTRY glMakeTextureHandleResidentNV (GLuint64 handle); + GLAPI void APIENTRY glMakeTextureHandleNonResidentNV (GLuint64 handle); + GLAPI GLuint64 APIENTRY glGetImageHandleNV (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); + GLAPI void APIENTRY glMakeImageHandleResidentNV (GLuint64 handle, GLenum access); + GLAPI void APIENTRY glMakeImageHandleNonResidentNV (GLuint64 handle); + GLAPI void APIENTRY glUniformHandleui64NV (GLint location, GLuint64 value); + GLAPI void APIENTRY glUniformHandleui64vNV (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glProgramUniformHandleui64NV (GLuint program, GLint location, GLuint64 value); + GLAPI void APIENTRY glProgramUniformHandleui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64 *values); + GLAPI GLboolean APIENTRY glIsTextureHandleResidentNV (GLuint64 handle); + GLAPI GLboolean APIENTRY glIsImageHandleResidentNV (GLuint64 handle); +#endif +#endif /* GL_NV_bindless_texture */ + +#ifndef GL_NV_blend_equation_advanced +#define GL_NV_blend_equation_advanced 1 +#define GL_BLEND_OVERLAP_NV 0x9281 +#define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280 +#define GL_BLUE_NV 0x1905 +#define GL_COLORBURN_NV 0x929A +#define GL_COLORDODGE_NV 0x9299 +#define GL_CONJOINT_NV 0x9284 +#define GL_CONTRAST_NV 0x92A1 +#define GL_DARKEN_NV 0x9297 +#define GL_DIFFERENCE_NV 0x929E +#define GL_DISJOINT_NV 0x9283 +#define GL_DST_ATOP_NV 0x928F +#define GL_DST_IN_NV 0x928B +#define GL_DST_NV 0x9287 +#define GL_DST_OUT_NV 0x928D +#define GL_DST_OVER_NV 0x9289 +#define GL_EXCLUSION_NV 0x92A0 +#define GL_GREEN_NV 0x1904 +#define GL_HARDLIGHT_NV 0x929B +#define GL_HARDMIX_NV 0x92A9 +#define GL_HSL_COLOR_NV 0x92AF +#define GL_HSL_HUE_NV 0x92AD +#define GL_HSL_LUMINOSITY_NV 0x92B0 +#define GL_HSL_SATURATION_NV 0x92AE +#define GL_INVERT_OVG_NV 0x92B4 +#define GL_INVERT_RGB_NV 0x92A3 +#define GL_LIGHTEN_NV 0x9298 +#define GL_LINEARBURN_NV 0x92A5 +#define GL_LINEARDODGE_NV 0x92A4 +#define GL_LINEARLIGHT_NV 0x92A7 +#define GL_MINUS_CLAMPED_NV 0x92B3 +#define GL_MINUS_NV 0x929F +#define GL_MULTIPLY_NV 0x9294 +#define GL_OVERLAY_NV 0x9296 +#define GL_PINLIGHT_NV 0x92A8 +#define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2 +#define GL_PLUS_CLAMPED_NV 0x92B1 +#define GL_PLUS_DARKER_NV 0x9292 +#define GL_PLUS_NV 0x9291 +#define GL_RED_NV 0x1903 +#define GL_SCREEN_NV 0x9295 +#define GL_SOFTLIGHT_NV 0x929C +#define GL_SRC_ATOP_NV 0x928E +#define GL_SRC_IN_NV 0x928A +#define GL_SRC_NV 0x9286 +#define GL_SRC_OUT_NV 0x928C +#define GL_SRC_OVER_NV 0x9288 +#define GL_UNCORRELATED_NV 0x9282 +#define GL_VIVIDLIGHT_NV 0x92A6 +#define GL_XOR_NV 0x1506 + typedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC) (GLenum pname, GLint value); + typedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendParameteriNV (GLenum pname, GLint value); + GLAPI void APIENTRY glBlendBarrierNV (void); +#endif +#endif /* GL_NV_blend_equation_advanced */ + +#ifndef GL_NV_blend_equation_advanced_coherent +#define GL_NV_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285 +#endif /* GL_NV_blend_equation_advanced_coherent */ + +#ifndef GL_NV_blend_minmax_factor +#define GL_NV_blend_minmax_factor 1 +#define GL_FACTOR_MIN_AMD 0x901C +#define GL_FACTOR_MAX_AMD 0x901D +#endif /* GL_NV_blend_minmax_factor */ + +#ifndef GL_NV_clip_space_w_scaling +#define GL_NV_clip_space_w_scaling 1 +#define GL_VIEWPORT_POSITION_W_SCALE_NV 0x937C +#define GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV 0x937D +#define GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV 0x937E + typedef void (APIENTRYP PFNGLVIEWPORTPOSITIONWSCALENVPROC) (GLuint index, GLfloat xcoeff, GLfloat ycoeff); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glViewportPositionWScaleNV (GLuint index, GLfloat xcoeff, GLfloat ycoeff); +#endif +#endif /* GL_NV_clip_space_w_scaling */ + +#ifndef GL_NV_command_list +#define GL_NV_command_list 1 +#define GL_TERMINATE_SEQUENCE_COMMAND_NV 0x0000 +#define GL_NOP_COMMAND_NV 0x0001 +#define GL_DRAW_ELEMENTS_COMMAND_NV 0x0002 +#define GL_DRAW_ARRAYS_COMMAND_NV 0x0003 +#define GL_DRAW_ELEMENTS_STRIP_COMMAND_NV 0x0004 +#define GL_DRAW_ARRAYS_STRIP_COMMAND_NV 0x0005 +#define GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV 0x0006 +#define GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV 0x0007 +#define GL_ELEMENT_ADDRESS_COMMAND_NV 0x0008 +#define GL_ATTRIBUTE_ADDRESS_COMMAND_NV 0x0009 +#define GL_UNIFORM_ADDRESS_COMMAND_NV 0x000A +#define GL_BLEND_COLOR_COMMAND_NV 0x000B +#define GL_STENCIL_REF_COMMAND_NV 0x000C +#define GL_LINE_WIDTH_COMMAND_NV 0x000D +#define GL_POLYGON_OFFSET_COMMAND_NV 0x000E +#define GL_ALPHA_REF_COMMAND_NV 0x000F +#define GL_VIEWPORT_COMMAND_NV 0x0010 +#define GL_SCISSOR_COMMAND_NV 0x0011 +#define GL_FRONT_FACE_COMMAND_NV 0x0012 + typedef void (APIENTRYP PFNGLCREATESTATESNVPROC) (GLsizei n, GLuint *states); + typedef void (APIENTRYP PFNGLDELETESTATESNVPROC) (GLsizei n, const GLuint *states); + typedef GLboolean (APIENTRYP PFNGLISSTATENVPROC) (GLuint state); + typedef void (APIENTRYP PFNGLSTATECAPTURENVPROC) (GLuint state, GLenum mode); + typedef GLuint (APIENTRYP PFNGLGETCOMMANDHEADERNVPROC) (GLenum tokenID, GLuint size); + typedef GLushort (APIENTRYP PFNGLGETSTAGEINDEXNVPROC) (GLenum shadertype); + typedef void (APIENTRYP PFNGLDRAWCOMMANDSNVPROC) (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); + typedef void (APIENTRYP PFNGLDRAWCOMMANDSADDRESSNVPROC) (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); + typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESNVPROC) (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC) (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + typedef void (APIENTRYP PFNGLCREATECOMMANDLISTSNVPROC) (GLsizei n, GLuint *lists); + typedef void (APIENTRYP PFNGLDELETECOMMANDLISTSNVPROC) (GLsizei n, const GLuint *lists); + typedef GLboolean (APIENTRYP PFNGLISCOMMANDLISTNVPROC) (GLuint list); + typedef void (APIENTRYP PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC) (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + typedef void (APIENTRYP PFNGLCOMMANDLISTSEGMENTSNVPROC) (GLuint list, GLuint segments); + typedef void (APIENTRYP PFNGLCOMPILECOMMANDLISTNVPROC) (GLuint list); + typedef void (APIENTRYP PFNGLCALLCOMMANDLISTNVPROC) (GLuint list); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCreateStatesNV (GLsizei n, GLuint *states); + GLAPI void APIENTRY glDeleteStatesNV (GLsizei n, const GLuint *states); + GLAPI GLboolean APIENTRY glIsStateNV (GLuint state); + GLAPI void APIENTRY glStateCaptureNV (GLuint state, GLenum mode); + GLAPI GLuint APIENTRY glGetCommandHeaderNV (GLenum tokenID, GLuint size); + GLAPI GLushort APIENTRY glGetStageIndexNV (GLenum shadertype); + GLAPI void APIENTRY glDrawCommandsNV (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); + GLAPI void APIENTRY glDrawCommandsAddressNV (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); + GLAPI void APIENTRY glDrawCommandsStatesNV (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + GLAPI void APIENTRY glDrawCommandsStatesAddressNV (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + GLAPI void APIENTRY glCreateCommandListsNV (GLsizei n, GLuint *lists); + GLAPI void APIENTRY glDeleteCommandListsNV (GLsizei n, const GLuint *lists); + GLAPI GLboolean APIENTRY glIsCommandListNV (GLuint list); + GLAPI void APIENTRY glListDrawCommandsStatesClientNV (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + GLAPI void APIENTRY glCommandListSegmentsNV (GLuint list, GLuint segments); + GLAPI void APIENTRY glCompileCommandListNV (GLuint list); + GLAPI void APIENTRY glCallCommandListNV (GLuint list); +#endif +#endif /* GL_NV_command_list */ + +#ifndef GL_NV_compute_shader_derivatives +#define GL_NV_compute_shader_derivatives 1 +#endif /* GL_NV_compute_shader_derivatives */ + +#ifndef GL_NV_conditional_render +#define GL_NV_conditional_render 1 +#define GL_QUERY_WAIT_NV 0x8E13 +#define GL_QUERY_NO_WAIT_NV 0x8E14 +#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 + typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode); + typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBeginConditionalRenderNV (GLuint id, GLenum mode); + GLAPI void APIENTRY glEndConditionalRenderNV (void); +#endif +#endif /* GL_NV_conditional_render */ + +#ifndef GL_NV_conservative_raster +#define GL_NV_conservative_raster 1 +#define GL_CONSERVATIVE_RASTERIZATION_NV 0x9346 +#define GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV 0x9347 +#define GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV 0x9348 +#define GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV 0x9349 + typedef void (APIENTRYP PFNGLSUBPIXELPRECISIONBIASNVPROC) (GLuint xbits, GLuint ybits); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSubpixelPrecisionBiasNV (GLuint xbits, GLuint ybits); +#endif +#endif /* GL_NV_conservative_raster */ + +#ifndef GL_NV_conservative_raster_dilate +#define GL_NV_conservative_raster_dilate 1 +#define GL_CONSERVATIVE_RASTER_DILATE_NV 0x9379 +#define GL_CONSERVATIVE_RASTER_DILATE_RANGE_NV 0x937A +#define GL_CONSERVATIVE_RASTER_DILATE_GRANULARITY_NV 0x937B + typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERFNVPROC) (GLenum pname, GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glConservativeRasterParameterfNV (GLenum pname, GLfloat value); +#endif +#endif /* GL_NV_conservative_raster_dilate */ + +#ifndef GL_NV_conservative_raster_pre_snap +#define GL_NV_conservative_raster_pre_snap 1 +#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV 0x9550 +#endif /* GL_NV_conservative_raster_pre_snap */ + +#ifndef GL_NV_conservative_raster_pre_snap_triangles +#define GL_NV_conservative_raster_pre_snap_triangles 1 +#define GL_CONSERVATIVE_RASTER_MODE_NV 0x954D +#define GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV 0x954E +#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV 0x954F + typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERINVPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glConservativeRasterParameteriNV (GLenum pname, GLint param); +#endif +#endif /* GL_NV_conservative_raster_pre_snap_triangles */ + +#ifndef GL_NV_conservative_raster_underestimation +#define GL_NV_conservative_raster_underestimation 1 +#endif /* GL_NV_conservative_raster_underestimation */ + +#ifndef GL_NV_depth_buffer_float +#define GL_NV_depth_buffer_float 1 +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD +#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF + typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); + typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); + typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDepthRangedNV (GLdouble zNear, GLdouble zFar); + GLAPI void APIENTRY glClearDepthdNV (GLdouble depth); + GLAPI void APIENTRY glDepthBoundsdNV (GLdouble zmin, GLdouble zmax); +#endif +#endif /* GL_NV_depth_buffer_float */ + +#ifndef GL_NV_draw_vulkan_image +#define GL_NV_draw_vulkan_image 1 + typedef void (APIENTRY *GLVULKANPROCNV)(void); + typedef void (APIENTRYP PFNGLDRAWVKIMAGENVPROC) (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); + typedef GLVULKANPROCNV (APIENTRYP PFNGLGETVKPROCADDRNVPROC) (const GLchar *name); + typedef void (APIENTRYP PFNGLWAITVKSEMAPHORENVPROC) (GLuint64 vkSemaphore); + typedef void (APIENTRYP PFNGLSIGNALVKSEMAPHORENVPROC) (GLuint64 vkSemaphore); + typedef void (APIENTRYP PFNGLSIGNALVKFENCENVPROC) (GLuint64 vkFence); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawVkImageNV (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); + GLAPI GLVULKANPROCNV APIENTRY glGetVkProcAddrNV (const GLchar *name); + GLAPI void APIENTRY glWaitVkSemaphoreNV (GLuint64 vkSemaphore); + GLAPI void APIENTRY glSignalVkSemaphoreNV (GLuint64 vkSemaphore); + GLAPI void APIENTRY glSignalVkFenceNV (GLuint64 vkFence); +#endif +#endif /* GL_NV_draw_vulkan_image */ + +#ifndef GL_NV_fill_rectangle +#define GL_NV_fill_rectangle 1 +#define GL_FILL_RECTANGLE_NV 0x933C +#endif /* GL_NV_fill_rectangle */ + +#ifndef GL_NV_fragment_coverage_to_color +#define GL_NV_fragment_coverage_to_color 1 +#define GL_FRAGMENT_COVERAGE_TO_COLOR_NV 0x92DD +#define GL_FRAGMENT_COVERAGE_COLOR_NV 0x92DE + typedef void (APIENTRYP PFNGLFRAGMENTCOVERAGECOLORNVPROC) (GLuint color); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFragmentCoverageColorNV (GLuint color); +#endif +#endif /* GL_NV_fragment_coverage_to_color */ + +#ifndef GL_NV_fragment_shader_barycentric +#define GL_NV_fragment_shader_barycentric 1 +#endif /* GL_NV_fragment_shader_barycentric */ + +#ifndef GL_NV_fragment_shader_interlock +#define GL_NV_fragment_shader_interlock 1 +#endif /* GL_NV_fragment_shader_interlock */ + +#ifndef GL_NV_framebuffer_mixed_samples +#define GL_NV_framebuffer_mixed_samples 1 +#define GL_COVERAGE_MODULATION_TABLE_NV 0x9331 +#define GL_COLOR_SAMPLES_NV 0x8E20 +#define GL_DEPTH_SAMPLES_NV 0x932D +#define GL_STENCIL_SAMPLES_NV 0x932E +#define GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV 0x932F +#define GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV 0x9330 +#define GL_COVERAGE_MODULATION_NV 0x9332 +#define GL_COVERAGE_MODULATION_TABLE_SIZE_NV 0x9333 + typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONTABLENVPROC) (GLsizei n, const GLfloat *v); + typedef void (APIENTRYP PFNGLGETCOVERAGEMODULATIONTABLENVPROC) (GLsizei bufSize, GLfloat *v); + typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONNVPROC) (GLenum components); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCoverageModulationTableNV (GLsizei n, const GLfloat *v); + GLAPI void APIENTRY glGetCoverageModulationTableNV (GLsizei bufSize, GLfloat *v); + GLAPI void APIENTRY glCoverageModulationNV (GLenum components); +#endif +#endif /* GL_NV_framebuffer_mixed_samples */ + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_NV_framebuffer_multisample_coverage 1 +#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB +#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 +#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 +#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 + typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glRenderbufferStorageMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_NV_framebuffer_multisample_coverage */ + +#ifndef GL_NV_geometry_shader_passthrough +#define GL_NV_geometry_shader_passthrough 1 +#endif /* GL_NV_geometry_shader_passthrough */ + +#ifndef GL_NV_gpu_shader5 +#define GL_NV_gpu_shader5 1 + typedef khronos_int64_t GLint64EXT; +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +#define GL_INT8_NV 0x8FE0 +#define GL_INT8_VEC2_NV 0x8FE1 +#define GL_INT8_VEC3_NV 0x8FE2 +#define GL_INT8_VEC4_NV 0x8FE3 +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 +#define GL_INT64_VEC2_NV 0x8FE9 +#define GL_INT64_VEC3_NV 0x8FEA +#define GL_INT64_VEC4_NV 0x8FEB +#define GL_UNSIGNED_INT8_NV 0x8FEC +#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED +#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE +#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF +#define GL_UNSIGNED_INT16_NV 0x8FF0 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 +#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB + typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x); + typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y); + typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); + typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x); + typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y); + typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glUniform1i64NV (GLint location, GLint64EXT x); + GLAPI void APIENTRY glUniform2i64NV (GLint location, GLint64EXT x, GLint64EXT y); + GLAPI void APIENTRY glUniform3i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); + GLAPI void APIENTRY glUniform4i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + GLAPI void APIENTRY glUniform1i64vNV (GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glUniform2i64vNV (GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glUniform3i64vNV (GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glUniform4i64vNV (GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glUniform1ui64NV (GLint location, GLuint64EXT x); + GLAPI void APIENTRY glUniform2ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y); + GLAPI void APIENTRY glUniform3ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + GLAPI void APIENTRY glUniform4ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + GLAPI void APIENTRY glUniform1ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glUniform2ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glUniform3ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glUniform4ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glGetUniformi64vNV (GLuint program, GLint location, GLint64EXT *params); + GLAPI void APIENTRY glProgramUniform1i64NV (GLuint program, GLint location, GLint64EXT x); + GLAPI void APIENTRY glProgramUniform2i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); + GLAPI void APIENTRY glProgramUniform3i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); + GLAPI void APIENTRY glProgramUniform4i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + GLAPI void APIENTRY glProgramUniform1i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glProgramUniform2i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glProgramUniform3i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glProgramUniform4i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glProgramUniform1ui64NV (GLuint program, GLint location, GLuint64EXT x); + GLAPI void APIENTRY glProgramUniform2ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); + GLAPI void APIENTRY glProgramUniform3ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + GLAPI void APIENTRY glProgramUniform4ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + GLAPI void APIENTRY glProgramUniform1ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glProgramUniform2ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glProgramUniform3ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glProgramUniform4ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_gpu_shader5 */ + +#ifndef GL_NV_internalformat_sample_query +#define GL_NV_internalformat_sample_query 1 +#define GL_MULTISAMPLES_NV 0x9371 +#define GL_SUPERSAMPLE_SCALE_X_NV 0x9372 +#define GL_SUPERSAMPLE_SCALE_Y_NV 0x9373 +#define GL_CONFORMANT_NV 0x9374 + typedef void (APIENTRYP PFNGLGETINTERNALFORMATSAMPLEIVNVPROC) (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetInternalformatSampleivNV (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params); +#endif +#endif /* GL_NV_internalformat_sample_query */ + +#ifndef GL_NV_memory_attachment +#define GL_NV_memory_attachment 1 +#define GL_ATTACHED_MEMORY_OBJECT_NV 0x95A4 +#define GL_ATTACHED_MEMORY_OFFSET_NV 0x95A5 +#define GL_MEMORY_ATTACHABLE_ALIGNMENT_NV 0x95A6 +#define GL_MEMORY_ATTACHABLE_SIZE_NV 0x95A7 +#define GL_MEMORY_ATTACHABLE_NV 0x95A8 +#define GL_DETACHED_MEMORY_INCARNATION_NV 0x95A9 +#define GL_DETACHED_TEXTURES_NV 0x95AA +#define GL_DETACHED_BUFFERS_NV 0x95AB +#define GL_MAX_DETACHED_TEXTURES_NV 0x95AC +#define GL_MAX_DETACHED_BUFFERS_NV 0x95AD + typedef void (APIENTRYP PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC) (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params); + typedef void (APIENTRYP PFNGLRESETMEMORYOBJECTPARAMETERNVPROC) (GLuint memory, GLenum pname); + typedef void (APIENTRYP PFNGLTEXATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLBUFFERATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXTUREATTACHMEMORYNVPROC) (GLuint texture, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLNAMEDBUFFERATTACHMEMORYNVPROC) (GLuint buffer, GLuint memory, GLuint64 offset); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetMemoryObjectDetachedResourcesuivNV (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params); + GLAPI void APIENTRY glResetMemoryObjectParameterNV (GLuint memory, GLenum pname); + GLAPI void APIENTRY glTexAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glBufferAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTextureAttachMemoryNV (GLuint texture, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glNamedBufferAttachMemoryNV (GLuint buffer, GLuint memory, GLuint64 offset); +#endif +#endif /* GL_NV_memory_attachment */ + +#ifndef GL_NV_memory_object_sparse +#define GL_NV_memory_object_sparse 1 + typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTMEMNVPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); + typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTMEMNVPROC) (GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); + typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTMEMNVPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); + typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTMEMNVPROC) (GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBufferPageCommitmentMemNV (GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); + GLAPI void APIENTRY glTexPageCommitmentMemNV (GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); + GLAPI void APIENTRY glNamedBufferPageCommitmentMemNV (GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); + GLAPI void APIENTRY glTexturePageCommitmentMemNV (GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +#endif +#endif /* GL_NV_memory_object_sparse */ + +#ifndef GL_NV_mesh_shader +#define GL_NV_mesh_shader 1 +#define GL_MESH_SHADER_NV 0x9559 +#define GL_TASK_SHADER_NV 0x955A +#define GL_MAX_MESH_UNIFORM_BLOCKS_NV 0x8E60 +#define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV 0x8E61 +#define GL_MAX_MESH_IMAGE_UNIFORMS_NV 0x8E62 +#define GL_MAX_MESH_UNIFORM_COMPONENTS_NV 0x8E63 +#define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV 0x8E64 +#define GL_MAX_MESH_ATOMIC_COUNTERS_NV 0x8E65 +#define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV 0x8E66 +#define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV 0x8E67 +#define GL_MAX_TASK_UNIFORM_BLOCKS_NV 0x8E68 +#define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV 0x8E69 +#define GL_MAX_TASK_IMAGE_UNIFORMS_NV 0x8E6A +#define GL_MAX_TASK_UNIFORM_COMPONENTS_NV 0x8E6B +#define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV 0x8E6C +#define GL_MAX_TASK_ATOMIC_COUNTERS_NV 0x8E6D +#define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV 0x8E6E +#define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV 0x8E6F +#define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV 0x95A2 +#define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV 0x95A3 +#define GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV 0x9536 +#define GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV 0x9537 +#define GL_MAX_MESH_OUTPUT_VERTICES_NV 0x9538 +#define GL_MAX_MESH_OUTPUT_PRIMITIVES_NV 0x9539 +#define GL_MAX_TASK_OUTPUT_COUNT_NV 0x953A +#define GL_MAX_DRAW_MESH_TASKS_COUNT_NV 0x953D +#define GL_MAX_MESH_VIEWS_NV 0x9557 +#define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV 0x92DF +#define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV 0x9543 +#define GL_MAX_MESH_WORK_GROUP_SIZE_NV 0x953B +#define GL_MAX_TASK_WORK_GROUP_SIZE_NV 0x953C +#define GL_MESH_WORK_GROUP_SIZE_NV 0x953E +#define GL_TASK_WORK_GROUP_SIZE_NV 0x953F +#define GL_MESH_VERTICES_OUT_NV 0x9579 +#define GL_MESH_PRIMITIVES_OUT_NV 0x957A +#define GL_MESH_OUTPUT_TYPE_NV 0x957B +#define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV 0x959C +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV 0x959D +#define GL_REFERENCED_BY_MESH_SHADER_NV 0x95A0 +#define GL_REFERENCED_BY_TASK_SHADER_NV 0x95A1 +#define GL_MESH_SHADER_BIT_NV 0x00000040 +#define GL_TASK_SHADER_BIT_NV 0x00000080 +#define GL_MESH_SUBROUTINE_NV 0x957C +#define GL_TASK_SUBROUTINE_NV 0x957D +#define GL_MESH_SUBROUTINE_UNIFORM_NV 0x957E +#define GL_TASK_SUBROUTINE_UNIFORM_NV 0x957F +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV 0x959E +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV 0x959F + typedef void (APIENTRYP PFNGLDRAWMESHTASKSNVPROC) (GLuint first, GLuint count); + typedef void (APIENTRYP PFNGLDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect); + typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect, GLsizei drawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC) (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawMeshTasksNV (GLuint first, GLuint count); + GLAPI void APIENTRY glDrawMeshTasksIndirectNV (GLintptr indirect); + GLAPI void APIENTRY glMultiDrawMeshTasksIndirectNV (GLintptr indirect, GLsizei drawcount, GLsizei stride); + GLAPI void APIENTRY glMultiDrawMeshTasksIndirectCountNV (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_NV_mesh_shader */ + +#ifndef GL_NV_path_rendering +#define GL_NV_path_rendering 1 +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D + typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); + typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); + typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path); + typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); + typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); + typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); + typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); + typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); + typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); + typedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); + typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); + typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); + typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); + typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); + typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); + typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); + typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); + typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); + typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); + typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); + typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); + typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); + typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); + typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); + typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); + typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); + typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); + typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); + typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); + typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); + typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); + typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); + typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); + typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); + typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); + typedef void (APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); + typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); + typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount); + typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + typedef GLenum (APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + typedef void (APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); + typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLuint APIENTRY glGenPathsNV (GLsizei range); + GLAPI void APIENTRY glDeletePathsNV (GLuint path, GLsizei range); + GLAPI GLboolean APIENTRY glIsPathNV (GLuint path); + GLAPI void APIENTRY glPathCommandsNV (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); + GLAPI void APIENTRY glPathCoordsNV (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); + GLAPI void APIENTRY glPathSubCommandsNV (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); + GLAPI void APIENTRY glPathSubCoordsNV (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); + GLAPI void APIENTRY glPathStringNV (GLuint path, GLenum format, GLsizei length, const void *pathString); + GLAPI void APIENTRY glPathGlyphsNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + GLAPI void APIENTRY glPathGlyphRangeNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + GLAPI void APIENTRY glWeightPathsNV (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); + GLAPI void APIENTRY glCopyPathNV (GLuint resultPath, GLuint srcPath); + GLAPI void APIENTRY glInterpolatePathsNV (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); + GLAPI void APIENTRY glTransformPathNV (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glPathParameterivNV (GLuint path, GLenum pname, const GLint *value); + GLAPI void APIENTRY glPathParameteriNV (GLuint path, GLenum pname, GLint value); + GLAPI void APIENTRY glPathParameterfvNV (GLuint path, GLenum pname, const GLfloat *value); + GLAPI void APIENTRY glPathParameterfNV (GLuint path, GLenum pname, GLfloat value); + GLAPI void APIENTRY glPathDashArrayNV (GLuint path, GLsizei dashCount, const GLfloat *dashArray); + GLAPI void APIENTRY glPathStencilFuncNV (GLenum func, GLint ref, GLuint mask); + GLAPI void APIENTRY glPathStencilDepthOffsetNV (GLfloat factor, GLfloat units); + GLAPI void APIENTRY glStencilFillPathNV (GLuint path, GLenum fillMode, GLuint mask); + GLAPI void APIENTRY glStencilStrokePathNV (GLuint path, GLint reference, GLuint mask); + GLAPI void APIENTRY glStencilFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glStencilStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glPathCoverDepthFuncNV (GLenum func); + GLAPI void APIENTRY glCoverFillPathNV (GLuint path, GLenum coverMode); + GLAPI void APIENTRY glCoverStrokePathNV (GLuint path, GLenum coverMode); + GLAPI void APIENTRY glCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glGetPathParameterivNV (GLuint path, GLenum pname, GLint *value); + GLAPI void APIENTRY glGetPathParameterfvNV (GLuint path, GLenum pname, GLfloat *value); + GLAPI void APIENTRY glGetPathCommandsNV (GLuint path, GLubyte *commands); + GLAPI void APIENTRY glGetPathCoordsNV (GLuint path, GLfloat *coords); + GLAPI void APIENTRY glGetPathDashArrayNV (GLuint path, GLfloat *dashArray); + GLAPI void APIENTRY glGetPathMetricsNV (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); + GLAPI void APIENTRY glGetPathMetricRangeNV (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); + GLAPI void APIENTRY glGetPathSpacingNV (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); + GLAPI GLboolean APIENTRY glIsPointInFillPathNV (GLuint path, GLuint mask, GLfloat x, GLfloat y); + GLAPI GLboolean APIENTRY glIsPointInStrokePathNV (GLuint path, GLfloat x, GLfloat y); + GLAPI GLfloat APIENTRY glGetPathLengthNV (GLuint path, GLsizei startSegment, GLsizei numSegments); + GLAPI GLboolean APIENTRY glPointAlongPathNV (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); + GLAPI void APIENTRY glMatrixLoad3x2fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glMatrixLoad3x3fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glMatrixLoadTranspose3x3fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glMatrixMult3x2fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glMatrixMult3x3fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glMatrixMultTranspose3x3fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glStencilThenCoverFillPathNV (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); + GLAPI void APIENTRY glStencilThenCoverStrokePathNV (GLuint path, GLint reference, GLuint mask, GLenum coverMode); + GLAPI void APIENTRY glStencilThenCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glStencilThenCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + GLAPI GLenum APIENTRY glPathGlyphIndexRangeNV (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount); + GLAPI GLenum APIENTRY glPathGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + GLAPI GLenum APIENTRY glPathMemoryGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + GLAPI void APIENTRY glProgramPathFragmentInputGenNV (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); + GLAPI void APIENTRY glGetProgramResourcefvNV (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLfloat *params); +#endif +#endif /* GL_NV_path_rendering */ + +#ifndef GL_NV_path_rendering_shared_edge +#define GL_NV_path_rendering_shared_edge 1 +#define GL_SHARED_EDGE_NV 0xC0 +#endif /* GL_NV_path_rendering_shared_edge */ + +#ifndef GL_NV_primitive_shading_rate +#define GL_NV_primitive_shading_rate 1 +#define GL_SHADING_RATE_IMAGE_PER_PRIMITIVE_NV 0x95B1 +#define GL_SHADING_RATE_IMAGE_PALETTE_COUNT_NV 0x95B2 +#endif /* GL_NV_primitive_shading_rate */ + +#ifndef GL_NV_representative_fragment_test +#define GL_NV_representative_fragment_test 1 +#define GL_REPRESENTATIVE_FRAGMENT_TEST_NV 0x937F +#endif /* GL_NV_representative_fragment_test */ + +#ifndef GL_NV_sample_locations +#define GL_NV_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV 0x9340 +#define GL_SAMPLE_LOCATION_NV 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV 0x9343 + typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLRESOLVEDEPTHVALUESNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferSampleLocationsfvNV (GLenum target, GLuint start, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glNamedFramebufferSampleLocationsfvNV (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glResolveDepthValuesNV (void); +#endif +#endif /* GL_NV_sample_locations */ + +#ifndef GL_NV_sample_mask_override_coverage +#define GL_NV_sample_mask_override_coverage 1 +#endif /* GL_NV_sample_mask_override_coverage */ + +#ifndef GL_NV_scissor_exclusive +#define GL_NV_scissor_exclusive 1 +#define GL_SCISSOR_TEST_EXCLUSIVE_NV 0x9555 +#define GL_SCISSOR_BOX_EXCLUSIVE_NV 0x9556 + typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVENVPROC) (GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVEARRAYVNVPROC) (GLuint first, GLsizei count, const GLint *v); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glScissorExclusiveNV (GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glScissorExclusiveArrayvNV (GLuint first, GLsizei count, const GLint *v); +#endif +#endif /* GL_NV_scissor_exclusive */ + +#ifndef GL_NV_shader_atomic_counters +#define GL_NV_shader_atomic_counters 1 +#endif /* GL_NV_shader_atomic_counters */ + +#ifndef GL_NV_shader_atomic_float +#define GL_NV_shader_atomic_float 1 +#endif /* GL_NV_shader_atomic_float */ + +#ifndef GL_NV_shader_atomic_float64 +#define GL_NV_shader_atomic_float64 1 +#endif /* GL_NV_shader_atomic_float64 */ + +#ifndef GL_NV_shader_atomic_fp16_vector +#define GL_NV_shader_atomic_fp16_vector 1 +#endif /* GL_NV_shader_atomic_fp16_vector */ + +#ifndef GL_NV_shader_atomic_int64 +#define GL_NV_shader_atomic_int64 1 +#endif /* GL_NV_shader_atomic_int64 */ + +#ifndef GL_NV_shader_buffer_load +#define GL_NV_shader_buffer_load 1 +#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D +#define GL_GPU_ADDRESS_NV 0x8F34 +#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 + typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access); + typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target); + typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target); + typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access); + typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer); + typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params); + typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result); + typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value); + typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMakeBufferResidentNV (GLenum target, GLenum access); + GLAPI void APIENTRY glMakeBufferNonResidentNV (GLenum target); + GLAPI GLboolean APIENTRY glIsBufferResidentNV (GLenum target); + GLAPI void APIENTRY glMakeNamedBufferResidentNV (GLuint buffer, GLenum access); + GLAPI void APIENTRY glMakeNamedBufferNonResidentNV (GLuint buffer); + GLAPI GLboolean APIENTRY glIsNamedBufferResidentNV (GLuint buffer); + GLAPI void APIENTRY glGetBufferParameterui64vNV (GLenum target, GLenum pname, GLuint64EXT *params); + GLAPI void APIENTRY glGetNamedBufferParameterui64vNV (GLuint buffer, GLenum pname, GLuint64EXT *params); + GLAPI void APIENTRY glGetIntegerui64vNV (GLenum value, GLuint64EXT *result); + GLAPI void APIENTRY glUniformui64NV (GLint location, GLuint64EXT value); + GLAPI void APIENTRY glUniformui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glGetUniformui64vNV (GLuint program, GLint location, GLuint64EXT *params); + GLAPI void APIENTRY glProgramUniformui64NV (GLuint program, GLint location, GLuint64EXT value); + GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_shader_buffer_load */ + +#ifndef GL_NV_shader_buffer_store +#define GL_NV_shader_buffer_store 1 +#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 +#endif /* GL_NV_shader_buffer_store */ + +#ifndef GL_NV_shader_subgroup_partitioned +#define GL_NV_shader_subgroup_partitioned 1 +#define GL_SUBGROUP_FEATURE_PARTITIONED_BIT_NV 0x00000100 +#endif /* GL_NV_shader_subgroup_partitioned */ + +#ifndef GL_NV_shader_texture_footprint +#define GL_NV_shader_texture_footprint 1 +#endif /* GL_NV_shader_texture_footprint */ + +#ifndef GL_NV_shader_thread_group +#define GL_NV_shader_thread_group 1 +#define GL_WARP_SIZE_NV 0x9339 +#define GL_WARPS_PER_SM_NV 0x933A +#define GL_SM_COUNT_NV 0x933B +#endif /* GL_NV_shader_thread_group */ + +#ifndef GL_NV_shader_thread_shuffle +#define GL_NV_shader_thread_shuffle 1 +#endif /* GL_NV_shader_thread_shuffle */ + +#ifndef GL_NV_shading_rate_image +#define GL_NV_shading_rate_image 1 +#define GL_SHADING_RATE_IMAGE_NV 0x9563 +#define GL_SHADING_RATE_NO_INVOCATIONS_NV 0x9564 +#define GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV 0x9565 +#define GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV 0x9566 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV 0x9567 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV 0x9568 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV 0x9569 +#define GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV 0x956A +#define GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV 0x956B +#define GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV 0x956C +#define GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV 0x956D +#define GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV 0x956E +#define GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV 0x956F +#define GL_SHADING_RATE_IMAGE_BINDING_NV 0x955B +#define GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV 0x955C +#define GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV 0x955D +#define GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV 0x955E +#define GL_MAX_COARSE_FRAGMENT_SAMPLES_NV 0x955F +#define GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV 0x95AE +#define GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV 0x95AF +#define GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV 0x95B0 + typedef void (APIENTRYP PFNGLBINDSHADINGRATEIMAGENVPROC) (GLuint texture); + typedef void (APIENTRYP PFNGLGETSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint entry, GLenum *rate); + typedef void (APIENTRYP PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC) (GLenum rate, GLuint samples, GLuint index, GLint *location); + typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEBARRIERNVPROC) (GLboolean synchronize); + typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates); + typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERNVPROC) (GLenum order); + typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC) (GLenum rate, GLuint samples, const GLint *locations); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBindShadingRateImageNV (GLuint texture); + GLAPI void APIENTRY glGetShadingRateImagePaletteNV (GLuint viewport, GLuint entry, GLenum *rate); + GLAPI void APIENTRY glGetShadingRateSampleLocationivNV (GLenum rate, GLuint samples, GLuint index, GLint *location); + GLAPI void APIENTRY glShadingRateImageBarrierNV (GLboolean synchronize); + GLAPI void APIENTRY glShadingRateImagePaletteNV (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates); + GLAPI void APIENTRY glShadingRateSampleOrderNV (GLenum order); + GLAPI void APIENTRY glShadingRateSampleOrderCustomNV (GLenum rate, GLuint samples, const GLint *locations); +#endif +#endif /* GL_NV_shading_rate_image */ + +#ifndef GL_NV_stereo_view_rendering +#define GL_NV_stereo_view_rendering 1 +#endif /* GL_NV_stereo_view_rendering */ + +#ifndef GL_NV_texture_barrier +#define GL_NV_texture_barrier 1 + typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTextureBarrierNV (void); +#endif +#endif /* GL_NV_texture_barrier */ + +#ifndef GL_NV_texture_rectangle_compressed +#define GL_NV_texture_rectangle_compressed 1 +#endif /* GL_NV_texture_rectangle_compressed */ + +#ifndef GL_NV_uniform_buffer_unified_memory +#define GL_NV_uniform_buffer_unified_memory 1 +#define GL_UNIFORM_BUFFER_UNIFIED_NV 0x936E +#define GL_UNIFORM_BUFFER_ADDRESS_NV 0x936F +#define GL_UNIFORM_BUFFER_LENGTH_NV 0x9370 +#endif /* GL_NV_uniform_buffer_unified_memory */ + +#ifndef GL_NV_vertex_attrib_integer_64bit +#define GL_NV_vertex_attrib_integer_64bit 1 + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params); + typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexAttribL1i64NV (GLuint index, GLint64EXT x); + GLAPI void APIENTRY glVertexAttribL2i64NV (GLuint index, GLint64EXT x, GLint64EXT y); + GLAPI void APIENTRY glVertexAttribL3i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); + GLAPI void APIENTRY glVertexAttribL4i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + GLAPI void APIENTRY glVertexAttribL1i64vNV (GLuint index, const GLint64EXT *v); + GLAPI void APIENTRY glVertexAttribL2i64vNV (GLuint index, const GLint64EXT *v); + GLAPI void APIENTRY glVertexAttribL3i64vNV (GLuint index, const GLint64EXT *v); + GLAPI void APIENTRY glVertexAttribL4i64vNV (GLuint index, const GLint64EXT *v); + GLAPI void APIENTRY glVertexAttribL1ui64NV (GLuint index, GLuint64EXT x); + GLAPI void APIENTRY glVertexAttribL2ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y); + GLAPI void APIENTRY glVertexAttribL3ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + GLAPI void APIENTRY glVertexAttribL4ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + GLAPI void APIENTRY glVertexAttribL1ui64vNV (GLuint index, const GLuint64EXT *v); + GLAPI void APIENTRY glVertexAttribL2ui64vNV (GLuint index, const GLuint64EXT *v); + GLAPI void APIENTRY glVertexAttribL3ui64vNV (GLuint index, const GLuint64EXT *v); + GLAPI void APIENTRY glVertexAttribL4ui64vNV (GLuint index, const GLuint64EXT *v); + GLAPI void APIENTRY glGetVertexAttribLi64vNV (GLuint index, GLenum pname, GLint64EXT *params); + GLAPI void APIENTRY glGetVertexAttribLui64vNV (GLuint index, GLenum pname, GLuint64EXT *params); + GLAPI void APIENTRY glVertexAttribLFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +#endif +#endif /* GL_NV_vertex_attrib_integer_64bit */ + +#ifndef GL_NV_vertex_buffer_unified_memory +#define GL_NV_vertex_buffer_unified_memory 1 +#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E +#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F +#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 +#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 +#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 +#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 +#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 +#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 +#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 +#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 +#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 +#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 +#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A +#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B +#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C +#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D +#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E +#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F +#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 +#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 +#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 +#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 +#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 +#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 +#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 + typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); + typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride); + typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); + typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBufferAddressRangeNV (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); + GLAPI void APIENTRY glVertexFormatNV (GLint size, GLenum type, GLsizei stride); + GLAPI void APIENTRY glNormalFormatNV (GLenum type, GLsizei stride); + GLAPI void APIENTRY glColorFormatNV (GLint size, GLenum type, GLsizei stride); + GLAPI void APIENTRY glIndexFormatNV (GLenum type, GLsizei stride); + GLAPI void APIENTRY glTexCoordFormatNV (GLint size, GLenum type, GLsizei stride); + GLAPI void APIENTRY glEdgeFlagFormatNV (GLsizei stride); + GLAPI void APIENTRY glSecondaryColorFormatNV (GLint size, GLenum type, GLsizei stride); + GLAPI void APIENTRY glFogCoordFormatNV (GLenum type, GLsizei stride); + GLAPI void APIENTRY glVertexAttribFormatNV (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); + GLAPI void APIENTRY glVertexAttribIFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); + GLAPI void APIENTRY glGetIntegerui64i_vNV (GLenum value, GLuint index, GLuint64EXT *result); +#endif +#endif /* GL_NV_vertex_buffer_unified_memory */ + +#ifndef GL_NV_viewport_array2 +#define GL_NV_viewport_array2 1 +#endif /* GL_NV_viewport_array2 */ + +#ifndef GL_NV_viewport_swizzle +#define GL_NV_viewport_swizzle 1 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV 0x9350 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV 0x9351 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV 0x9352 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV 0x9353 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV 0x9354 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV 0x9355 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV 0x9356 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV 0x9357 +#define GL_VIEWPORT_SWIZZLE_X_NV 0x9358 +#define GL_VIEWPORT_SWIZZLE_Y_NV 0x9359 +#define GL_VIEWPORT_SWIZZLE_Z_NV 0x935A +#define GL_VIEWPORT_SWIZZLE_W_NV 0x935B + typedef void (APIENTRYP PFNGLVIEWPORTSWIZZLENVPROC) (GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glViewportSwizzleNV (GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew); +#endif +#endif /* GL_NV_viewport_swizzle */ + +#ifndef GL_OVR_multiview +#define GL_OVR_multiview 1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 +#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633 + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferTextureMultiviewOVR (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#endif +#endif /* GL_OVR_multiview */ + +#ifndef GL_OVR_multiview2 +#define GL_OVR_multiview2 1 +#endif /* GL_OVR_multiview2 */ + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src_v2/platform/glext.h b/src_v2/platform/glext.h new file mode 100644 index 0000000..ece8c51 --- /dev/null +++ b/src_v2/platform/glext.h @@ -0,0 +1,12894 @@ +#ifndef __gl_glext_h_ +#define __gl_glext_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + + /* + ** Copyright 2013-2020 The Khronos Group Inc. + ** SPDX-License-Identifier: MIT + ** + ** This header is generated from the Khronos OpenGL / OpenGL ES XML + ** API Registry. The current version of the Registry, generator scripts + ** used to make the header, and the header can be found at + ** https://github.com/KhronosGroup/OpenGL-Registry + */ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +#define GL_GLEXT_VERSION 20211115 + +#include "khrplatform.h" + + /* Generated C header for: + * API: gl + * Profile: compatibility + * Versions considered: .* + * Versions emitted: 1\.[2-9]|[234]\.[0-9] + * Default extensions included: gl + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_RESCALE_NORMAL 0x803A +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D + typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); + typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); + GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_2 */ + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_NORMAL_MAP 0x8511 +#define GL_REFLECTION_MAP 0x8512 +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF + typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); + typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img); + typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC) (GLenum target, GLdouble s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC) (GLenum target, const GLdouble *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, GLfloat s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC) (GLenum target, const GLfloat *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC) (GLenum target, GLint s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC) (GLenum target, const GLint *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC) (GLenum target, GLshort s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC) (GLenum target, const GLshort *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC) (GLenum target, GLdouble s, GLdouble t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC) (GLenum target, const GLdouble *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, GLfloat s, GLfloat t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC) (GLenum target, GLint s, GLint t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC) (GLenum target, const GLint *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC) (GLenum target, GLshort s, GLshort t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC) (GLenum target, const GLshort *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC) (GLenum target, const GLdouble *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC) (GLenum target, const GLfloat *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC) (GLenum target, GLint s, GLint t, GLint r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC) (GLenum target, const GLint *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC) (GLenum target, GLshort s, GLshort t, GLshort r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC) (GLenum target, const GLshort *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC) (GLenum target, const GLdouble *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC) (GLenum target, const GLfloat *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC) (GLenum target, const GLint *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC) (GLenum target, const GLshort *v); + typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC) (const GLfloat *m); + typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC) (const GLdouble *m); + typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC) (const GLfloat *m); + typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glActiveTexture (GLenum texture); + GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); + GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img); + GLAPI void APIENTRY glClientActiveTexture (GLenum texture); + GLAPI void APIENTRY glMultiTexCoord1d (GLenum target, GLdouble s); + GLAPI void APIENTRY glMultiTexCoord1dv (GLenum target, const GLdouble *v); + GLAPI void APIENTRY glMultiTexCoord1f (GLenum target, GLfloat s); + GLAPI void APIENTRY glMultiTexCoord1fv (GLenum target, const GLfloat *v); + GLAPI void APIENTRY glMultiTexCoord1i (GLenum target, GLint s); + GLAPI void APIENTRY glMultiTexCoord1iv (GLenum target, const GLint *v); + GLAPI void APIENTRY glMultiTexCoord1s (GLenum target, GLshort s); + GLAPI void APIENTRY glMultiTexCoord1sv (GLenum target, const GLshort *v); + GLAPI void APIENTRY glMultiTexCoord2d (GLenum target, GLdouble s, GLdouble t); + GLAPI void APIENTRY glMultiTexCoord2dv (GLenum target, const GLdouble *v); + GLAPI void APIENTRY glMultiTexCoord2f (GLenum target, GLfloat s, GLfloat t); + GLAPI void APIENTRY glMultiTexCoord2fv (GLenum target, const GLfloat *v); + GLAPI void APIENTRY glMultiTexCoord2i (GLenum target, GLint s, GLint t); + GLAPI void APIENTRY glMultiTexCoord2iv (GLenum target, const GLint *v); + GLAPI void APIENTRY glMultiTexCoord2s (GLenum target, GLshort s, GLshort t); + GLAPI void APIENTRY glMultiTexCoord2sv (GLenum target, const GLshort *v); + GLAPI void APIENTRY glMultiTexCoord3d (GLenum target, GLdouble s, GLdouble t, GLdouble r); + GLAPI void APIENTRY glMultiTexCoord3dv (GLenum target, const GLdouble *v); + GLAPI void APIENTRY glMultiTexCoord3f (GLenum target, GLfloat s, GLfloat t, GLfloat r); + GLAPI void APIENTRY glMultiTexCoord3fv (GLenum target, const GLfloat *v); + GLAPI void APIENTRY glMultiTexCoord3i (GLenum target, GLint s, GLint t, GLint r); + GLAPI void APIENTRY glMultiTexCoord3iv (GLenum target, const GLint *v); + GLAPI void APIENTRY glMultiTexCoord3s (GLenum target, GLshort s, GLshort t, GLshort r); + GLAPI void APIENTRY glMultiTexCoord3sv (GLenum target, const GLshort *v); + GLAPI void APIENTRY glMultiTexCoord4d (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); + GLAPI void APIENTRY glMultiTexCoord4dv (GLenum target, const GLdouble *v); + GLAPI void APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); + GLAPI void APIENTRY glMultiTexCoord4fv (GLenum target, const GLfloat *v); + GLAPI void APIENTRY glMultiTexCoord4i (GLenum target, GLint s, GLint t, GLint r, GLint q); + GLAPI void APIENTRY glMultiTexCoord4iv (GLenum target, const GLint *v); + GLAPI void APIENTRY glMultiTexCoord4s (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); + GLAPI void APIENTRY glMultiTexCoord4sv (GLenum target, const GLshort *v); + GLAPI void APIENTRY glLoadTransposeMatrixf (const GLfloat *m); + GLAPI void APIENTRY glLoadTransposeMatrixd (const GLdouble *m); + GLAPI void APIENTRY glMultTransposeMatrixf (const GLfloat *m); + GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *m); +#endif +#endif /* GL_VERSION_1_3 */ + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_COLOR_SUM 0x8458 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 + typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); + typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLFOGCOORDFPROC) (GLfloat coord); + typedef void (APIENTRYP PFNGLFOGCOORDFVPROC) (const GLfloat *coord); + typedef void (APIENTRYP PFNGLFOGCOORDDPROC) (GLdouble coord); + typedef void (APIENTRYP PFNGLFOGCOORDDVPROC) (const GLdouble *coord); + typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC) (GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC) (GLbyte red, GLbyte green, GLbyte blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC) (const GLbyte *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC) (GLdouble red, GLdouble green, GLdouble blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC) (GLfloat red, GLfloat green, GLfloat blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC) (GLint red, GLint green, GLint blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC) (GLshort red, GLshort green, GLshort blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC) (const GLshort *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC) (GLubyte red, GLubyte green, GLubyte blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC) (const GLubyte *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC) (GLuint red, GLuint green, GLuint blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC) (const GLuint *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC) (GLushort red, GLushort green, GLushort blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC) (const GLushort *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC) (GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC) (GLfloat x, GLfloat y); + typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC) (GLint x, GLint y); + typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC) (GLshort x, GLshort y); + typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC) (const GLshort *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC) (GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC) (GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC) (GLint x, GLint y, GLint z); + typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC) (GLshort x, GLshort y, GLshort z); + typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC) (const GLshort *v); + typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); + GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); + GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); + GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); + GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); + GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); + GLAPI void APIENTRY glFogCoordf (GLfloat coord); + GLAPI void APIENTRY glFogCoordfv (const GLfloat *coord); + GLAPI void APIENTRY glFogCoordd (GLdouble coord); + GLAPI void APIENTRY glFogCoorddv (const GLdouble *coord); + GLAPI void APIENTRY glFogCoordPointer (GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glSecondaryColor3b (GLbyte red, GLbyte green, GLbyte blue); + GLAPI void APIENTRY glSecondaryColor3bv (const GLbyte *v); + GLAPI void APIENTRY glSecondaryColor3d (GLdouble red, GLdouble green, GLdouble blue); + GLAPI void APIENTRY glSecondaryColor3dv (const GLdouble *v); + GLAPI void APIENTRY glSecondaryColor3f (GLfloat red, GLfloat green, GLfloat blue); + GLAPI void APIENTRY glSecondaryColor3fv (const GLfloat *v); + GLAPI void APIENTRY glSecondaryColor3i (GLint red, GLint green, GLint blue); + GLAPI void APIENTRY glSecondaryColor3iv (const GLint *v); + GLAPI void APIENTRY glSecondaryColor3s (GLshort red, GLshort green, GLshort blue); + GLAPI void APIENTRY glSecondaryColor3sv (const GLshort *v); + GLAPI void APIENTRY glSecondaryColor3ub (GLubyte red, GLubyte green, GLubyte blue); + GLAPI void APIENTRY glSecondaryColor3ubv (const GLubyte *v); + GLAPI void APIENTRY glSecondaryColor3ui (GLuint red, GLuint green, GLuint blue); + GLAPI void APIENTRY glSecondaryColor3uiv (const GLuint *v); + GLAPI void APIENTRY glSecondaryColor3us (GLushort red, GLushort green, GLushort blue); + GLAPI void APIENTRY glSecondaryColor3usv (const GLushort *v); + GLAPI void APIENTRY glSecondaryColorPointer (GLint size, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glWindowPos2d (GLdouble x, GLdouble y); + GLAPI void APIENTRY glWindowPos2dv (const GLdouble *v); + GLAPI void APIENTRY glWindowPos2f (GLfloat x, GLfloat y); + GLAPI void APIENTRY glWindowPos2fv (const GLfloat *v); + GLAPI void APIENTRY glWindowPos2i (GLint x, GLint y); + GLAPI void APIENTRY glWindowPos2iv (const GLint *v); + GLAPI void APIENTRY glWindowPos2s (GLshort x, GLshort y); + GLAPI void APIENTRY glWindowPos2sv (const GLshort *v); + GLAPI void APIENTRY glWindowPos3d (GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glWindowPos3dv (const GLdouble *v); + GLAPI void APIENTRY glWindowPos3f (GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glWindowPos3fv (const GLfloat *v); + GLAPI void APIENTRY glWindowPos3i (GLint x, GLint y, GLint z); + GLAPI void APIENTRY glWindowPos3iv (const GLint *v); + GLAPI void APIENTRY glWindowPos3s (GLshort x, GLshort y, GLshort z); + GLAPI void APIENTRY glWindowPos3sv (const GLshort *v); + GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + GLAPI void APIENTRY glBlendEquation (GLenum mode); +#endif +#endif /* GL_VERSION_1_4 */ + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 + typedef khronos_ssize_t GLsizeiptr; + typedef khronos_intptr_t GLintptr; +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_FOG_COORD_SRC 0x8450 +#define GL_FOG_COORD 0x8451 +#define GL_CURRENT_FOG_COORD 0x8453 +#define GL_FOG_COORD_ARRAY_TYPE 0x8454 +#define GL_FOG_COORD_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORD_ARRAY_POINTER 0x8456 +#define GL_FOG_COORD_ARRAY 0x8457 +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC2_ALPHA 0x858A + typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); + typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); + typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); + typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); + typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); + typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); + typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); + typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); + typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); + typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); + typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); + typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); + typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); + typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); + GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); + GLAPI GLboolean APIENTRY glIsQuery (GLuint id); + GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); + GLAPI void APIENTRY glEndQuery (GLenum target); + GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); + GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); + GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); + GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); + GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); + GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); + GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); + GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data); + GLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access); + GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); + GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_VERSION_1_5 */ + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 + typedef char GLchar; +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 +#define GL_MAX_TEXTURE_COORDS 0x8871 + typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); + typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); + typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); + typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); + typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); + typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); + typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); + typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); + typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); + typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); + typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); + typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); + typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); + typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); + typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); + typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); + typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); + typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); + typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); + typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); + typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); + typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); + typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); + typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); + typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); + typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); + typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); + typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); + typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); + typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); + typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); + typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); + typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); + GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); + GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); + GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); + GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); + GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); + GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); + GLAPI void APIENTRY glCompileShader (GLuint shader); + GLAPI GLuint APIENTRY glCreateProgram (void); + GLAPI GLuint APIENTRY glCreateShader (GLenum type); + GLAPI void APIENTRY glDeleteProgram (GLuint program); + GLAPI void APIENTRY glDeleteShader (GLuint shader); + GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); + GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); + GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); + GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); + GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); + GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); + GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); + GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); + GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); + GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); + GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); + GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); + GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); + GLAPI GLboolean APIENTRY glIsProgram (GLuint program); + GLAPI GLboolean APIENTRY glIsShader (GLuint shader); + GLAPI void APIENTRY glLinkProgram (GLuint program); + GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); + GLAPI void APIENTRY glUseProgram (GLuint program); + GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); + GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); + GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); + GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); + GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); + GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glValidateProgram (GLuint program); + GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); + GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); + GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); + GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); + GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); + GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); + GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); + GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); + GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); + GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); + GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); + GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); + GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); + GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); + GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); + GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#endif +#endif /* GL_VERSION_2_0 */ + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#define GL_COMPRESSED_SLUMINANCE 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif +#endif /* GL_VERSION_2_1 */ + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 + typedef khronos_uint16_t GLhalf; +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_INDEX 0x8222 +#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE 0x8C15 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_CLAMP_VERTEX_COLOR 0x891A +#define GL_CLAMP_FRAGMENT_COLOR 0x891B +#define GL_ALPHA_INTEGER 0x8D97 + typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); + typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); + typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); + typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); + typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); + typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); + typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); + typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); + typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); + typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); + typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); + typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); + typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); + typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); + typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); + typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); + typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); + typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); + typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); + typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); + typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); + typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); + typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); + typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); + typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); + typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); + typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); + typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); + typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); + typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); + typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); + typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); + typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); + typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); + typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); + typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); + typedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); + typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); + typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); + typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); + typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); + typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); + GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); + GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); + GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); + GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); + GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); + GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); + GLAPI void APIENTRY glEndTransformFeedback (void); + GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); + GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); + GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); + GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); + GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); + GLAPI void APIENTRY glEndConditionalRender (void); + GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); + GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); + GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); + GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); + GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); + GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); + GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); + GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); + GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); + GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); + GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); + GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); + GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); + GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); + GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); + GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); + GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); + GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); + GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); + GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); + GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); + GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); + GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); + GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); + GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); + GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); + GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); + GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); + GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); + GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); + GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); + GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); + GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); + GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); + GLAPI void APIENTRY glGenerateMipmap (GLenum target); + GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); + GLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); + GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); + GLAPI void APIENTRY glBindVertexArray (GLuint array); + GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); + GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); + GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); +#endif +#endif /* GL_VERSION_3_0 */ + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu + typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); + typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); + typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); + typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); + typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); + typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); + GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); + GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); + GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); + GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); + GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); + GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); + GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); + GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif +#endif /* GL_VERSION_3_1 */ + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 + typedef struct __GLsync *GLsync; + typedef khronos_uint64_t GLuint64; + typedef khronos_int64_t GLint64; +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_DEPTH_CLAMP 0x864F +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 + typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); + typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); + typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); + typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); + typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); + typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); + typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); + typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); + typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); + typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); + typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); + typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); + typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); + GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); + GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); + GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); + GLAPI void APIENTRY glProvokingVertex (GLenum mode); + GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); + GLAPI GLboolean APIENTRY glIsSync (GLsync sync); + GLAPI void APIENTRY glDeleteSync (GLsync sync); + GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); + GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); + GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data); + GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); + GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); + GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); + GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); + GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); + GLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask); +#endif +#endif /* GL_VERSION_3_2 */ + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_RGB10_A2UI 0x906F +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#define GL_INT_2_10_10_10_REV 0x8D9F + typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); + typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); + typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); + typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); + typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); + typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); + typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); + typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); + typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + typedef void (APIENTRYP PFNGLVERTEXP2UIPROC) (GLenum type, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC) (GLenum type, const GLuint *value); + typedef void (APIENTRYP PFNGLVERTEXP3UIPROC) (GLenum type, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC) (GLenum type, const GLuint *value); + typedef void (APIENTRYP PFNGLVERTEXP4UIPROC) (GLenum type, GLuint value); + typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC) (GLenum type, const GLuint *value); + typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC) (GLenum type, GLuint coords); + typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC) (GLenum type, const GLuint *coords); + typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC) (GLenum type, GLuint coords); + typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC) (GLenum type, const GLuint *coords); + typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC) (GLenum type, GLuint coords); + typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC) (GLenum type, const GLuint *coords); + typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC) (GLenum type, GLuint coords); + typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC) (GLenum type, const GLuint *coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC) (GLenum texture, GLenum type, GLuint coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC) (GLenum texture, GLenum type, GLuint coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC) (GLenum texture, GLenum type, GLuint coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC) (GLenum texture, GLenum type, GLuint coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); + typedef void (APIENTRYP PFNGLNORMALP3UIPROC) (GLenum type, GLuint coords); + typedef void (APIENTRYP PFNGLNORMALP3UIVPROC) (GLenum type, const GLuint *coords); + typedef void (APIENTRYP PFNGLCOLORP3UIPROC) (GLenum type, GLuint color); + typedef void (APIENTRYP PFNGLCOLORP3UIVPROC) (GLenum type, const GLuint *color); + typedef void (APIENTRYP PFNGLCOLORP4UIPROC) (GLenum type, GLuint color); + typedef void (APIENTRYP PFNGLCOLORP4UIVPROC) (GLenum type, const GLuint *color); + typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC) (GLenum type, GLuint color); + typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC) (GLenum type, const GLuint *color); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); + GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); + GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); + GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); + GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); + GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); + GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); + GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); + GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); + GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); + GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); + GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); + GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); + GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); + GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); + GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); + GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); + GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); + GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); + GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); + GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); + GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); + GLAPI void APIENTRY glVertexP2ui (GLenum type, GLuint value); + GLAPI void APIENTRY glVertexP2uiv (GLenum type, const GLuint *value); + GLAPI void APIENTRY glVertexP3ui (GLenum type, GLuint value); + GLAPI void APIENTRY glVertexP3uiv (GLenum type, const GLuint *value); + GLAPI void APIENTRY glVertexP4ui (GLenum type, GLuint value); + GLAPI void APIENTRY glVertexP4uiv (GLenum type, const GLuint *value); + GLAPI void APIENTRY glTexCoordP1ui (GLenum type, GLuint coords); + GLAPI void APIENTRY glTexCoordP1uiv (GLenum type, const GLuint *coords); + GLAPI void APIENTRY glTexCoordP2ui (GLenum type, GLuint coords); + GLAPI void APIENTRY glTexCoordP2uiv (GLenum type, const GLuint *coords); + GLAPI void APIENTRY glTexCoordP3ui (GLenum type, GLuint coords); + GLAPI void APIENTRY glTexCoordP3uiv (GLenum type, const GLuint *coords); + GLAPI void APIENTRY glTexCoordP4ui (GLenum type, GLuint coords); + GLAPI void APIENTRY glTexCoordP4uiv (GLenum type, const GLuint *coords); + GLAPI void APIENTRY glMultiTexCoordP1ui (GLenum texture, GLenum type, GLuint coords); + GLAPI void APIENTRY glMultiTexCoordP1uiv (GLenum texture, GLenum type, const GLuint *coords); + GLAPI void APIENTRY glMultiTexCoordP2ui (GLenum texture, GLenum type, GLuint coords); + GLAPI void APIENTRY glMultiTexCoordP2uiv (GLenum texture, GLenum type, const GLuint *coords); + GLAPI void APIENTRY glMultiTexCoordP3ui (GLenum texture, GLenum type, GLuint coords); + GLAPI void APIENTRY glMultiTexCoordP3uiv (GLenum texture, GLenum type, const GLuint *coords); + GLAPI void APIENTRY glMultiTexCoordP4ui (GLenum texture, GLenum type, GLuint coords); + GLAPI void APIENTRY glMultiTexCoordP4uiv (GLenum texture, GLenum type, const GLuint *coords); + GLAPI void APIENTRY glNormalP3ui (GLenum type, GLuint coords); + GLAPI void APIENTRY glNormalP3uiv (GLenum type, const GLuint *coords); + GLAPI void APIENTRY glColorP3ui (GLenum type, GLuint color); + GLAPI void APIENTRY glColorP3uiv (GLenum type, const GLuint *color); + GLAPI void APIENTRY glColorP4ui (GLenum type, GLuint color); + GLAPI void APIENTRY glColorP4uiv (GLenum type, const GLuint *color); + GLAPI void APIENTRY glSecondaryColorP3ui (GLenum type, GLuint color); + GLAPI void APIENTRY glSecondaryColorP3uiv (GLenum type, const GLuint *color); +#endif +#endif /* GL_VERSION_3_3 */ + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_ISOLINES 0x8E7A +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 + typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); + typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); + typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); + typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); + typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect); + typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); + typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); + typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); + typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); + typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); + typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); + typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); + typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); + typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); + typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); + typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); + typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); + typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); + typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); + typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); + typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); + typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); + typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); + typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); + typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMinSampleShading (GLfloat value); + GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); + GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); + GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); + GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect); + GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect); + GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); + GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); + GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); + GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); + GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); + GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); + GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); + GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); + GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); + GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); + GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); + GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); + GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); + GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); + GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); + GLAPI void APIENTRY glPauseTransformFeedback (void); + GLAPI void APIENTRY glResumeTransformFeedback (void); + GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); + GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); + GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); + GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); + GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_VERSION_4_0 */ + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 + typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); + typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); + typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); + typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); + typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); + typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); + typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); + typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); + typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); + typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); + typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings); + typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); + typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); + typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); + typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); + typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); + typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); + typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); + typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); + typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); + typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); + typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); + typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glReleaseShaderCompiler (void); + GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); + GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); + GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); + GLAPI void APIENTRY glClearDepthf (GLfloat d); + GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); + GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); + GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); + GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); + GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); + GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings); + GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); + GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); + GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); + GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); + GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); + GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); + GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); + GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); + GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); + GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); + GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); + GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); + GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); + GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); + GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); + GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); + GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); + GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); + GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); + GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); + GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); + GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); + GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); + GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); + GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); + GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); + GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); + GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); + GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); +#endif +#endif /* GL_VERSION_4_1 */ + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F + typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); + typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); + typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); + typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); + typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); + typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); + GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); + GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); + GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); + GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); + GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); + GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); + GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); + GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif +#endif /* GL_VERSION_4_2 */ + +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 + typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_VERTEX_BINDING_BUFFER 0x8F4F +#define GL_DISPLAY_LIST 0x82E7 + typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); + typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); + typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); + typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); + typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); + typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); + typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); + typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); + typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); + typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); + typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); + typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); + typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); + typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); + typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); + typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); + typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); + typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); + typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); + typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); + typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); + typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); + typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); + typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); + typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); + GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); + GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); + GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); + GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); + GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); + GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); + GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); + GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); + GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); + GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); + GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); + GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); + GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); + GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); + GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); + GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); + GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); + GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); + GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); + GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); + GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); + GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); + GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); + GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); + GLAPI void APIENTRY glPopDebugGroup (void); + GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); + GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); + GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); + GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_VERSION_4_3 */ + +#ifndef GL_VERSION_4_4 +#define GL_VERSION_4_4 1 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 + typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); + typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); + typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); + typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); + typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers); + typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); + typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); + GLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); + GLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); + GLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures); + GLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers); + GLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures); + GLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#endif +#endif /* GL_VERSION_4_4 */ + +#ifndef GL_VERSION_4_5 +#define GL_VERSION_4_5 1 +#define GL_CONTEXT_LOST 0x0507 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_ZERO_TO_ONE 0x935F +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_QUERY_WAIT_INVERTED 0x8E17 +#define GL_QUERY_NO_WAIT_INVERTED 0x8E18 +#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 +#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_TEXTURE_TARGET 0x1006 +#define GL_QUERY_TARGET 0x82EA +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET 0x8252 +#define GL_NO_RESET_NOTIFICATION 0x8261 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004 +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_MINMAX 0x802E +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC + typedef void (APIENTRYP PFNGLCLIPCONTROLPROC) (GLenum origin, GLenum depth); + typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); + typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) (GLuint xfb, GLuint index, GLuint buffer); + typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC) (GLuint xfb, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param); + typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); + typedef void (APIENTRYP PFNGLCREATEBUFFERSPROC) (GLsizei n, GLuint *buffers); + typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); + typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); + typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + typedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERPROC) (GLuint buffer, GLenum access); + typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); + typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC) (GLuint buffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) (GLuint buffer, GLenum pname, GLint64 *params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC) (GLuint buffer, GLenum pname, void **params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); + typedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) (GLuint framebuffer, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) (GLuint framebuffer, GLenum buf); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) (GLuint framebuffer, GLenum src); + typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); + typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); + typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); + typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); + typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + typedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC) (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) (GLuint framebuffer, GLenum target); + typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) (GLuint framebuffer, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) (GLuint renderbuffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLCREATETEXTURESPROC) (GLenum target, GLsizei n, GLuint *textures); + typedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC) (GLuint texture, GLenum internalformat, GLuint buffer); + typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC) (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC) (GLuint texture, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, const GLfloat *param); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC) (GLuint texture, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, const GLuint *params); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, const GLint *param); + typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC) (GLuint texture); + typedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC) (GLuint unit, GLuint texture); + typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLsizei bufSize, void *pixels); + typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC) (GLuint texture, GLint level, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC) (GLuint texture, GLint level, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); + typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); + typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); + typedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC) (GLuint vaobj, GLuint buffer); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC) (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); + typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); + typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC) (GLuint vaobj, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); + typedef void (APIENTRYP PFNGLCREATESAMPLERSPROC) (GLsizei n, GLuint *samplers); + typedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); + typedef void (APIENTRYP PFNGLCREATEQUERIESPROC) (GLenum target, GLsizei n, GLuint *ids); + typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC) (GLbitfield barriers); + typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); + typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC) (void); + typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint lod, GLsizei bufSize, void *pixels); + typedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + typedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); + typedef void (APIENTRYP PFNGLREADNPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); + typedef void (APIENTRYP PFNGLGETNMAPDVPROC) (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); + typedef void (APIENTRYP PFNGLGETNMAPFVPROC) (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); + typedef void (APIENTRYP PFNGLGETNMAPIVPROC) (GLenum target, GLenum query, GLsizei bufSize, GLint *v); + typedef void (APIENTRYP PFNGLGETNPIXELMAPFVPROC) (GLenum map, GLsizei bufSize, GLfloat *values); + typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVPROC) (GLenum map, GLsizei bufSize, GLuint *values); + typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVPROC) (GLenum map, GLsizei bufSize, GLushort *values); + typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEPROC) (GLsizei bufSize, GLubyte *pattern); + typedef void (APIENTRYP PFNGLGETNCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); + typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); + typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); + typedef void (APIENTRYP PFNGLGETNHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); + typedef void (APIENTRYP PFNGLGETNMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); + typedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glClipControl (GLenum origin, GLenum depth); + GLAPI void APIENTRY glCreateTransformFeedbacks (GLsizei n, GLuint *ids); + GLAPI void APIENTRY glTransformFeedbackBufferBase (GLuint xfb, GLuint index, GLuint buffer); + GLAPI void APIENTRY glTransformFeedbackBufferRange (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glGetTransformFeedbackiv (GLuint xfb, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetTransformFeedbacki_v (GLuint xfb, GLenum pname, GLuint index, GLint *param); + GLAPI void APIENTRY glGetTransformFeedbacki64_v (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); + GLAPI void APIENTRY glCreateBuffers (GLsizei n, GLuint *buffers); + GLAPI void APIENTRY glNamedBufferStorage (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); + GLAPI void APIENTRY glNamedBufferData (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); + GLAPI void APIENTRY glNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + GLAPI void APIENTRY glCopyNamedBufferSubData (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + GLAPI void APIENTRY glClearNamedBufferData (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glClearNamedBufferSubData (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + GLAPI void *APIENTRY glMapNamedBuffer (GLuint buffer, GLenum access); + GLAPI void *APIENTRY glMapNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); + GLAPI GLboolean APIENTRY glUnmapNamedBuffer (GLuint buffer); + GLAPI void APIENTRY glFlushMappedNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length); + GLAPI void APIENTRY glGetNamedBufferParameteriv (GLuint buffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetNamedBufferParameteri64v (GLuint buffer, GLenum pname, GLint64 *params); + GLAPI void APIENTRY glGetNamedBufferPointerv (GLuint buffer, GLenum pname, void **params); + GLAPI void APIENTRY glGetNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); + GLAPI void APIENTRY glCreateFramebuffers (GLsizei n, GLuint *framebuffers); + GLAPI void APIENTRY glNamedFramebufferRenderbuffer (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + GLAPI void APIENTRY glNamedFramebufferParameteri (GLuint framebuffer, GLenum pname, GLint param); + GLAPI void APIENTRY glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); + GLAPI void APIENTRY glNamedFramebufferTextureLayer (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); + GLAPI void APIENTRY glNamedFramebufferDrawBuffer (GLuint framebuffer, GLenum buf); + GLAPI void APIENTRY glNamedFramebufferDrawBuffers (GLuint framebuffer, GLsizei n, const GLenum *bufs); + GLAPI void APIENTRY glNamedFramebufferReadBuffer (GLuint framebuffer, GLenum src); + GLAPI void APIENTRY glInvalidateNamedFramebufferData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); + GLAPI void APIENTRY glInvalidateNamedFramebufferSubData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glClearNamedFramebufferiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); + GLAPI void APIENTRY glClearNamedFramebufferuiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); + GLAPI void APIENTRY glClearNamedFramebufferfv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); + GLAPI void APIENTRY glClearNamedFramebufferfi (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + GLAPI void APIENTRY glBlitNamedFramebuffer (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + GLAPI GLenum APIENTRY glCheckNamedFramebufferStatus (GLuint framebuffer, GLenum target); + GLAPI void APIENTRY glGetNamedFramebufferParameteriv (GLuint framebuffer, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameteriv (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); + GLAPI void APIENTRY glCreateRenderbuffers (GLsizei n, GLuint *renderbuffers); + GLAPI void APIENTRY glNamedRenderbufferStorage (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glNamedRenderbufferStorageMultisample (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetNamedRenderbufferParameteriv (GLuint renderbuffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glCreateTextures (GLenum target, GLsizei n, GLuint *textures); + GLAPI void APIENTRY glTextureBuffer (GLuint texture, GLenum internalformat, GLuint buffer); + GLAPI void APIENTRY glTextureBufferRange (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glTextureStorage1D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); + GLAPI void APIENTRY glTextureStorage2D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTextureStorage3D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + GLAPI void APIENTRY glTextureStorage2DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTextureStorage3DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCompressedTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCopyTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glCopyTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glCopyTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTextureParameterf (GLuint texture, GLenum pname, GLfloat param); + GLAPI void APIENTRY glTextureParameterfv (GLuint texture, GLenum pname, const GLfloat *param); + GLAPI void APIENTRY glTextureParameteri (GLuint texture, GLenum pname, GLint param); + GLAPI void APIENTRY glTextureParameterIiv (GLuint texture, GLenum pname, const GLint *params); + GLAPI void APIENTRY glTextureParameterIuiv (GLuint texture, GLenum pname, const GLuint *params); + GLAPI void APIENTRY glTextureParameteriv (GLuint texture, GLenum pname, const GLint *param); + GLAPI void APIENTRY glGenerateTextureMipmap (GLuint texture); + GLAPI void APIENTRY glBindTextureUnit (GLuint unit, GLuint texture); + GLAPI void APIENTRY glGetTextureImage (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + GLAPI void APIENTRY glGetCompressedTextureImage (GLuint texture, GLint level, GLsizei bufSize, void *pixels); + GLAPI void APIENTRY glGetTextureLevelParameterfv (GLuint texture, GLint level, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetTextureLevelParameteriv (GLuint texture, GLint level, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTextureParameterfv (GLuint texture, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetTextureParameterIiv (GLuint texture, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTextureParameterIuiv (GLuint texture, GLenum pname, GLuint *params); + GLAPI void APIENTRY glGetTextureParameteriv (GLuint texture, GLenum pname, GLint *params); + GLAPI void APIENTRY glCreateVertexArrays (GLsizei n, GLuint *arrays); + GLAPI void APIENTRY glDisableVertexArrayAttrib (GLuint vaobj, GLuint index); + GLAPI void APIENTRY glEnableVertexArrayAttrib (GLuint vaobj, GLuint index); + GLAPI void APIENTRY glVertexArrayElementBuffer (GLuint vaobj, GLuint buffer); + GLAPI void APIENTRY glVertexArrayVertexBuffer (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + GLAPI void APIENTRY glVertexArrayVertexBuffers (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); + GLAPI void APIENTRY glVertexArrayAttribBinding (GLuint vaobj, GLuint attribindex, GLuint bindingindex); + GLAPI void APIENTRY glVertexArrayAttribFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayAttribIFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayAttribLFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayBindingDivisor (GLuint vaobj, GLuint bindingindex, GLuint divisor); + GLAPI void APIENTRY glGetVertexArrayiv (GLuint vaobj, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetVertexArrayIndexediv (GLuint vaobj, GLuint index, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetVertexArrayIndexed64iv (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); + GLAPI void APIENTRY glCreateSamplers (GLsizei n, GLuint *samplers); + GLAPI void APIENTRY glCreateProgramPipelines (GLsizei n, GLuint *pipelines); + GLAPI void APIENTRY glCreateQueries (GLenum target, GLsizei n, GLuint *ids); + GLAPI void APIENTRY glGetQueryBufferObjecti64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + GLAPI void APIENTRY glGetQueryBufferObjectiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + GLAPI void APIENTRY glGetQueryBufferObjectui64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + GLAPI void APIENTRY glGetQueryBufferObjectuiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); + GLAPI void APIENTRY glMemoryBarrierByRegion (GLbitfield barriers); + GLAPI void APIENTRY glGetTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + GLAPI void APIENTRY glGetCompressedTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); + GLAPI GLenum APIENTRY glGetGraphicsResetStatus (void); + GLAPI void APIENTRY glGetnCompressedTexImage (GLenum target, GLint lod, GLsizei bufSize, void *pixels); + GLAPI void APIENTRY glGetnTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); + GLAPI void APIENTRY glGetnUniformdv (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); + GLAPI void APIENTRY glGetnUniformfv (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); + GLAPI void APIENTRY glGetnUniformiv (GLuint program, GLint location, GLsizei bufSize, GLint *params); + GLAPI void APIENTRY glGetnUniformuiv (GLuint program, GLint location, GLsizei bufSize, GLuint *params); + GLAPI void APIENTRY glReadnPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); + GLAPI void APIENTRY glGetnMapdv (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); + GLAPI void APIENTRY glGetnMapfv (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); + GLAPI void APIENTRY glGetnMapiv (GLenum target, GLenum query, GLsizei bufSize, GLint *v); + GLAPI void APIENTRY glGetnPixelMapfv (GLenum map, GLsizei bufSize, GLfloat *values); + GLAPI void APIENTRY glGetnPixelMapuiv (GLenum map, GLsizei bufSize, GLuint *values); + GLAPI void APIENTRY glGetnPixelMapusv (GLenum map, GLsizei bufSize, GLushort *values); + GLAPI void APIENTRY glGetnPolygonStipple (GLsizei bufSize, GLubyte *pattern); + GLAPI void APIENTRY glGetnColorTable (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); + GLAPI void APIENTRY glGetnConvolutionFilter (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); + GLAPI void APIENTRY glGetnSeparableFilter (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); + GLAPI void APIENTRY glGetnHistogram (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); + GLAPI void APIENTRY glGetnMinmax (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); + GLAPI void APIENTRY glTextureBarrier (void); +#endif +#endif /* GL_VERSION_4_5 */ + +#ifndef GL_VERSION_4_6 +#define GL_VERSION_4_6 1 +#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551 +#define GL_SPIR_V_BINARY 0x9552 +#define GL_PARAMETER_BUFFER 0x80EE +#define GL_PARAMETER_BUFFER_BINDING 0x80EF +#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 +#define GL_VERTICES_SUBMITTED 0x82EE +#define GL_PRIMITIVES_SUBMITTED 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 +#define GL_POLYGON_OFFSET_CLAMP 0x8E1B +#define GL_SPIR_V_EXTENSIONS 0x9553 +#define GL_NUM_SPIR_V_EXTENSIONS 0x9554 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED + typedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC) (GLfloat factor, GLfloat units, GLfloat clamp); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSpecializeShader (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); + GLAPI void APIENTRY glMultiDrawArraysIndirectCount (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + GLAPI void APIENTRY glMultiDrawElementsIndirectCount (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + GLAPI void APIENTRY glPolygonOffsetClamp (GLfloat factor, GLfloat units, GLfloat clamp); +#endif +#endif /* GL_VERSION_4_6 */ + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#endif /* GL_ARB_ES2_compatibility */ + +#ifndef GL_ARB_ES3_1_compatibility +#define GL_ARB_ES3_1_compatibility 1 +#endif /* GL_ARB_ES3_1_compatibility */ + +#ifndef GL_ARB_ES3_2_compatibility +#define GL_ARB_ES3_2_compatibility 1 +#define GL_PRIMITIVE_BOUNDING_BOX_ARB 0x92BE +#define GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB 0x9381 +#define GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB 0x9382 + typedef void (APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXARBPROC) (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPrimitiveBoundingBoxARB (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +#endif +#endif /* GL_ARB_ES3_2_compatibility */ + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#endif /* GL_ARB_ES3_compatibility */ + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif /* GL_ARB_arrays_of_arrays */ + +#ifndef GL_ARB_base_instance +#define GL_ARB_base_instance 1 +#endif /* GL_ARB_base_instance */ + +#ifndef GL_ARB_bindless_texture +#define GL_ARB_bindless_texture 1 + typedef khronos_uint64_t GLuint64EXT; +#define GL_UNSIGNED_INT64_ARB 0x140F + typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture); + typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler); + typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); + typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle); + typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); + typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access); + typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle); + typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value); + typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); + typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); + typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLuint64 APIENTRY glGetTextureHandleARB (GLuint texture); + GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleARB (GLuint texture, GLuint sampler); + GLAPI void APIENTRY glMakeTextureHandleResidentARB (GLuint64 handle); + GLAPI void APIENTRY glMakeTextureHandleNonResidentARB (GLuint64 handle); + GLAPI GLuint64 APIENTRY glGetImageHandleARB (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); + GLAPI void APIENTRY glMakeImageHandleResidentARB (GLuint64 handle, GLenum access); + GLAPI void APIENTRY glMakeImageHandleNonResidentARB (GLuint64 handle); + GLAPI void APIENTRY glUniformHandleui64ARB (GLint location, GLuint64 value); + GLAPI void APIENTRY glUniformHandleui64vARB (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glProgramUniformHandleui64ARB (GLuint program, GLint location, GLuint64 value); + GLAPI void APIENTRY glProgramUniformHandleui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *values); + GLAPI GLboolean APIENTRY glIsTextureHandleResidentARB (GLuint64 handle); + GLAPI GLboolean APIENTRY glIsImageHandleResidentARB (GLuint64 handle); + GLAPI void APIENTRY glVertexAttribL1ui64ARB (GLuint index, GLuint64EXT x); + GLAPI void APIENTRY glVertexAttribL1ui64vARB (GLuint index, const GLuint64EXT *v); + GLAPI void APIENTRY glGetVertexAttribLui64vARB (GLuint index, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_ARB_bindless_texture */ + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#endif /* GL_ARB_blend_func_extended */ + +#ifndef GL_ARB_buffer_storage +#define GL_ARB_buffer_storage 1 +#endif /* GL_ARB_buffer_storage */ + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 + struct _cl_context; + struct _cl_event; +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 + typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#endif +#endif /* GL_ARB_cl_event */ + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#endif /* GL_ARB_clear_buffer_object */ + +#ifndef GL_ARB_clear_texture +#define GL_ARB_clear_texture 1 +#endif /* GL_ARB_clear_texture */ + +#ifndef GL_ARB_clip_control +#define GL_ARB_clip_control 1 +#endif /* GL_ARB_clip_control */ + +#ifndef GL_ARB_color_buffer_float +#define GL_ARB_color_buffer_float 1 +#define GL_RGBA_FLOAT_MODE_ARB 0x8820 +#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A +#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B +#define GL_CLAMP_READ_COLOR_ARB 0x891C +#define GL_FIXED_ONLY_ARB 0x891D + typedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC) (GLenum target, GLenum clamp); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glClampColorARB (GLenum target, GLenum clamp); +#endif +#endif /* GL_ARB_color_buffer_float */ + +#ifndef GL_ARB_compatibility +#define GL_ARB_compatibility 1 +#endif /* GL_ARB_compatibility */ + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_ARB_compressed_texture_pixel_storage 1 +#endif /* GL_ARB_compressed_texture_pixel_storage */ + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#endif /* GL_ARB_compute_shader */ + +#ifndef GL_ARB_compute_variable_group_size +#define GL_ARB_compute_variable_group_size 1 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB +#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 +#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF + typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDispatchComputeGroupSizeARB (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#endif +#endif /* GL_ARB_compute_variable_group_size */ + +#ifndef GL_ARB_conditional_render_inverted +#define GL_ARB_conditional_render_inverted 1 +#endif /* GL_ARB_conditional_render_inverted */ + +#ifndef GL_ARB_conservative_depth +#define GL_ARB_conservative_depth 1 +#endif /* GL_ARB_conservative_depth */ + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#endif /* GL_ARB_copy_buffer */ + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#endif /* GL_ARB_copy_image */ + +#ifndef GL_ARB_cull_distance +#define GL_ARB_cull_distance 1 +#endif /* GL_ARB_cull_distance */ + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 + typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 + typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); + typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); + typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); + GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const void *userParam); + GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif +#endif /* GL_ARB_debug_output */ + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#endif /* GL_ARB_depth_buffer_float */ + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#endif /* GL_ARB_depth_clamp */ + +#ifndef GL_ARB_depth_texture +#define GL_ARB_depth_texture 1 +#define GL_DEPTH_COMPONENT16_ARB 0x81A5 +#define GL_DEPTH_COMPONENT24_ARB 0x81A6 +#define GL_DEPTH_COMPONENT32_ARB 0x81A7 +#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A +#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B +#endif /* GL_ARB_depth_texture */ + +#ifndef GL_ARB_derivative_control +#define GL_ARB_derivative_control 1 +#endif /* GL_ARB_derivative_control */ + +#ifndef GL_ARB_direct_state_access +#define GL_ARB_direct_state_access 1 +#endif /* GL_ARB_direct_state_access */ + +#ifndef GL_ARB_draw_buffers +#define GL_ARB_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15_ARB 0x8834 + typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawBuffersARB (GLsizei n, const GLenum *bufs); +#endif +#endif /* GL_ARB_draw_buffers */ + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 + typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); + typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); + typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); + typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); + GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); + GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); + GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +#endif /* GL_ARB_draw_buffers_blend */ + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#endif /* GL_ARB_draw_elements_base_vertex */ + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#endif /* GL_ARB_draw_indirect */ + +#ifndef GL_ARB_draw_instanced +#define GL_ARB_draw_instanced 1 + typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawArraysInstancedARB (GLenum mode, GLint first, GLsizei count, GLsizei primcount); + GLAPI void APIENTRY glDrawElementsInstancedARB (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_ARB_draw_instanced */ + +#ifndef GL_ARB_enhanced_layouts +#define GL_ARB_enhanced_layouts 1 +#endif /* GL_ARB_enhanced_layouts */ + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif /* GL_ARB_explicit_attrib_location */ + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#endif /* GL_ARB_explicit_uniform_location */ + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif /* GL_ARB_fragment_coord_conventions */ + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif /* GL_ARB_fragment_layer_viewport */ + +#ifndef GL_ARB_fragment_program +#define GL_ARB_fragment_program 1 +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 +#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 +#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 +#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 +#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 +#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A +#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B +#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C +#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D +#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E +#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F +#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF + typedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC) (GLenum target, GLenum format, GLsizei len, const void *string); + typedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC) (GLenum target, GLuint program); + typedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC) (GLsizei n, const GLuint *programs); + typedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC) (GLsizei n, GLuint *programs); + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); + typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); + typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); + typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); + typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); + typedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pname, void *string); + typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProgramStringARB (GLenum target, GLenum format, GLsizei len, const void *string); + GLAPI void APIENTRY glBindProgramARB (GLenum target, GLuint program); + GLAPI void APIENTRY glDeleteProgramsARB (GLsizei n, const GLuint *programs); + GLAPI void APIENTRY glGenProgramsARB (GLsizei n, GLuint *programs); + GLAPI void APIENTRY glProgramEnvParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glProgramEnvParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); + GLAPI void APIENTRY glProgramEnvParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glProgramEnvParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); + GLAPI void APIENTRY glProgramLocalParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glProgramLocalParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); + GLAPI void APIENTRY glProgramLocalParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glProgramLocalParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); + GLAPI void APIENTRY glGetProgramEnvParameterdvARB (GLenum target, GLuint index, GLdouble *params); + GLAPI void APIENTRY glGetProgramEnvParameterfvARB (GLenum target, GLuint index, GLfloat *params); + GLAPI void APIENTRY glGetProgramLocalParameterdvARB (GLenum target, GLuint index, GLdouble *params); + GLAPI void APIENTRY glGetProgramLocalParameterfvARB (GLenum target, GLuint index, GLfloat *params); + GLAPI void APIENTRY glGetProgramivARB (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetProgramStringARB (GLenum target, GLenum pname, void *string); + GLAPI GLboolean APIENTRY glIsProgramARB (GLuint program); +#endif +#endif /* GL_ARB_fragment_program */ + +#ifndef GL_ARB_fragment_program_shadow +#define GL_ARB_fragment_program_shadow 1 +#endif /* GL_ARB_fragment_program_shadow */ + +#ifndef GL_ARB_fragment_shader +#define GL_ARB_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B +#endif /* GL_ARB_fragment_shader */ + +#ifndef GL_ARB_fragment_shader_interlock +#define GL_ARB_fragment_shader_interlock 1 +#endif /* GL_ARB_fragment_shader_interlock */ + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#endif /* GL_ARB_framebuffer_no_attachments */ + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#endif /* GL_ARB_framebuffer_object */ + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#endif /* GL_ARB_framebuffer_sRGB */ + +#ifndef GL_ARB_geometry_shader4 +#define GL_ARB_geometry_shader4 1 +#define GL_LINES_ADJACENCY_ARB 0x000A +#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B +#define GL_TRIANGLES_ADJACENCY_ARB 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D +#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 + typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProgramParameteriARB (GLuint program, GLenum pname, GLint value); + GLAPI void APIENTRY glFramebufferTextureARB (GLenum target, GLenum attachment, GLuint texture, GLint level); + GLAPI void APIENTRY glFramebufferTextureLayerARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); + GLAPI void APIENTRY glFramebufferTextureFaceARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_ARB_geometry_shader4 */ + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#endif /* GL_ARB_get_program_binary */ + +#ifndef GL_ARB_get_texture_sub_image +#define GL_ARB_get_texture_sub_image 1 +#endif /* GL_ARB_get_texture_sub_image */ + +#ifndef GL_ARB_gl_spirv +#define GL_ARB_gl_spirv 1 +#define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551 +#define GL_SPIR_V_BINARY_ARB 0x9552 + typedef void (APIENTRYP PFNGLSPECIALIZESHADERARBPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSpecializeShaderARB (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +#endif +#endif /* GL_ARB_gl_spirv */ + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#endif /* GL_ARB_gpu_shader5 */ + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#endif /* GL_ARB_gpu_shader_fp64 */ + +#ifndef GL_ARB_gpu_shader_int64 +#define GL_ARB_gpu_shader_int64 1 +#define GL_INT64_ARB 0x140E +#define GL_INT64_VEC2_ARB 0x8FE9 +#define GL_INT64_VEC3_ARB 0x8FEA +#define GL_INT64_VEC4_ARB 0x8FEB +#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FF7 + typedef void (APIENTRYP PFNGLUNIFORM1I64ARBPROC) (GLint location, GLint64 x); + typedef void (APIENTRYP PFNGLUNIFORM2I64ARBPROC) (GLint location, GLint64 x, GLint64 y); + typedef void (APIENTRYP PFNGLUNIFORM3I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z); + typedef void (APIENTRYP PFNGLUNIFORM4I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); + typedef void (APIENTRYP PFNGLUNIFORM1I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM2I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM3I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM4I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM1UI64ARBPROC) (GLint location, GLuint64 x); + typedef void (APIENTRYP PFNGLUNIFORM2UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y); + typedef void (APIENTRYP PFNGLUNIFORM3UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z); + typedef void (APIENTRYP PFNGLUNIFORM4UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); + typedef void (APIENTRYP PFNGLUNIFORM1UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM2UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM3UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLUNIFORM4UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLGETUNIFORMI64VARBPROC) (GLuint program, GLint location, GLint64 *params); + typedef void (APIENTRYP PFNGLGETUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLuint64 *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint64 *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint64 *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64ARBPROC) (GLuint program, GLint location, GLint64 x); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64ARBPROC) (GLuint program, GLint location, GLuint64 x); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glUniform1i64ARB (GLint location, GLint64 x); + GLAPI void APIENTRY glUniform2i64ARB (GLint location, GLint64 x, GLint64 y); + GLAPI void APIENTRY glUniform3i64ARB (GLint location, GLint64 x, GLint64 y, GLint64 z); + GLAPI void APIENTRY glUniform4i64ARB (GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); + GLAPI void APIENTRY glUniform1i64vARB (GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glUniform2i64vARB (GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glUniform3i64vARB (GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glUniform4i64vARB (GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glUniform1ui64ARB (GLint location, GLuint64 x); + GLAPI void APIENTRY glUniform2ui64ARB (GLint location, GLuint64 x, GLuint64 y); + GLAPI void APIENTRY glUniform3ui64ARB (GLint location, GLuint64 x, GLuint64 y, GLuint64 z); + GLAPI void APIENTRY glUniform4ui64ARB (GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); + GLAPI void APIENTRY glUniform1ui64vARB (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glUniform2ui64vARB (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glUniform3ui64vARB (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glUniform4ui64vARB (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glGetUniformi64vARB (GLuint program, GLint location, GLint64 *params); + GLAPI void APIENTRY glGetUniformui64vARB (GLuint program, GLint location, GLuint64 *params); + GLAPI void APIENTRY glGetnUniformi64vARB (GLuint program, GLint location, GLsizei bufSize, GLint64 *params); + GLAPI void APIENTRY glGetnUniformui64vARB (GLuint program, GLint location, GLsizei bufSize, GLuint64 *params); + GLAPI void APIENTRY glProgramUniform1i64ARB (GLuint program, GLint location, GLint64 x); + GLAPI void APIENTRY glProgramUniform2i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y); + GLAPI void APIENTRY glProgramUniform3i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); + GLAPI void APIENTRY glProgramUniform4i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); + GLAPI void APIENTRY glProgramUniform1i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glProgramUniform2i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glProgramUniform3i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glProgramUniform4i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); + GLAPI void APIENTRY glProgramUniform1ui64ARB (GLuint program, GLint location, GLuint64 x); + GLAPI void APIENTRY glProgramUniform2ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y); + GLAPI void APIENTRY glProgramUniform3ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); + GLAPI void APIENTRY glProgramUniform4ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); + GLAPI void APIENTRY glProgramUniform1ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glProgramUniform2ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glProgramUniform3ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glProgramUniform4ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +#endif +#endif /* GL_ARB_gpu_shader_int64 */ + +#ifndef GL_ARB_half_float_pixel +#define GL_ARB_half_float_pixel 1 + typedef khronos_uint16_t GLhalfARB; +#define GL_HALF_FLOAT_ARB 0x140B +#endif /* GL_ARB_half_float_pixel */ + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#endif /* GL_ARB_half_float_vertex */ + +#ifndef GL_ARB_imaging +#define GL_ARB_imaging 1 +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#define GL_REDUCE 0x8016 +#define GL_CONVOLUTION_FORMAT 0x8017 +#define GL_CONVOLUTION_WIDTH 0x8018 +#define GL_CONVOLUTION_HEIGHT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#define GL_HISTOGRAM_WIDTH 0x8026 +#define GL_HISTOGRAM_FORMAT 0x8027 +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#define GL_HISTOGRAM_SINK 0x802D +#define GL_MINMAX_FORMAT 0x802F +#define GL_MINMAX_SINK 0x8030 +#define GL_TABLE_TOO_LARGE 0x8031 +#define GL_COLOR_MATRIX 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +#define GL_COLOR_TABLE_SCALE 0x80D6 +#define GL_COLOR_TABLE_BIAS 0x80D7 +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +#define GL_CONSTANT_BORDER 0x8151 +#define GL_REPLICATE_BORDER 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 + typedef void (APIENTRYP PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); + typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLGETCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, void *table); + typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); + typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); + typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat params); + typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC) (GLenum target, GLenum pname, GLint params); + typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, void *image); + typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); + typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); + typedef void (APIENTRYP PFNGLGETHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); + typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); + typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLHISTOGRAMPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); + typedef void (APIENTRYP PFNGLMINMAXPROC) (GLenum target, GLenum internalformat, GLboolean sink); + typedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC) (GLenum target); + typedef void (APIENTRYP PFNGLRESETMINMAXPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glColorTable (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); + GLAPI void APIENTRY glColorTableParameterfv (GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glColorTableParameteriv (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glCopyColorTable (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glGetColorTable (GLenum target, GLenum format, GLenum type, void *table); + GLAPI void APIENTRY glGetColorTableParameterfv (GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetColorTableParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glColorSubTable (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glCopyColorSubTable (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glConvolutionFilter1D (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); + GLAPI void APIENTRY glConvolutionFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); + GLAPI void APIENTRY glConvolutionParameterf (GLenum target, GLenum pname, GLfloat params); + GLAPI void APIENTRY glConvolutionParameterfv (GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glConvolutionParameteri (GLenum target, GLenum pname, GLint params); + GLAPI void APIENTRY glConvolutionParameteriv (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glCopyConvolutionFilter1D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glCopyConvolutionFilter2D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetConvolutionFilter (GLenum target, GLenum format, GLenum type, void *image); + GLAPI void APIENTRY glGetConvolutionParameterfv (GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetConvolutionParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetSeparableFilter (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); + GLAPI void APIENTRY glSeparableFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); + GLAPI void APIENTRY glGetHistogram (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); + GLAPI void APIENTRY glGetHistogramParameterfv (GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetHistogramParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetMinmax (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); + GLAPI void APIENTRY glGetMinmaxParameterfv (GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMinmaxParameteriv (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glHistogram (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); + GLAPI void APIENTRY glMinmax (GLenum target, GLenum internalformat, GLboolean sink); + GLAPI void APIENTRY glResetHistogram (GLenum target); + GLAPI void APIENTRY glResetMinmax (GLenum target); +#endif +#endif /* GL_ARB_imaging */ + +#ifndef GL_ARB_indirect_parameters +#define GL_ARB_indirect_parameters 1 +#define GL_PARAMETER_BUFFER_ARB 0x80EE +#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_ARB_indirect_parameters */ + +#ifndef GL_ARB_instanced_arrays +#define GL_ARB_instanced_arrays 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE + typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexAttribDivisorARB (GLuint index, GLuint divisor); +#endif +#endif /* GL_ARB_instanced_arrays */ + +#ifndef GL_ARB_internalformat_query +#define GL_ARB_internalformat_query 1 +#endif /* GL_ARB_internalformat_query */ + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#define GL_SRGB_DECODE_ARB 0x8299 +#define GL_VIEW_CLASS_EAC_R11 0x9383 +#define GL_VIEW_CLASS_EAC_RG11 0x9384 +#define GL_VIEW_CLASS_ETC2_RGB 0x9385 +#define GL_VIEW_CLASS_ETC2_RGBA 0x9386 +#define GL_VIEW_CLASS_ETC2_EAC_RGBA 0x9387 +#define GL_VIEW_CLASS_ASTC_4x4_RGBA 0x9388 +#define GL_VIEW_CLASS_ASTC_5x4_RGBA 0x9389 +#define GL_VIEW_CLASS_ASTC_5x5_RGBA 0x938A +#define GL_VIEW_CLASS_ASTC_6x5_RGBA 0x938B +#define GL_VIEW_CLASS_ASTC_6x6_RGBA 0x938C +#define GL_VIEW_CLASS_ASTC_8x5_RGBA 0x938D +#define GL_VIEW_CLASS_ASTC_8x6_RGBA 0x938E +#define GL_VIEW_CLASS_ASTC_8x8_RGBA 0x938F +#define GL_VIEW_CLASS_ASTC_10x5_RGBA 0x9390 +#define GL_VIEW_CLASS_ASTC_10x6_RGBA 0x9391 +#define GL_VIEW_CLASS_ASTC_10x8_RGBA 0x9392 +#define GL_VIEW_CLASS_ASTC_10x10_RGBA 0x9393 +#define GL_VIEW_CLASS_ASTC_12x10_RGBA 0x9394 +#define GL_VIEW_CLASS_ASTC_12x12_RGBA 0x9395 +#endif /* GL_ARB_internalformat_query2 */ + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#endif /* GL_ARB_invalidate_subdata */ + +#ifndef GL_ARB_map_buffer_alignment +#define GL_ARB_map_buffer_alignment 1 +#endif /* GL_ARB_map_buffer_alignment */ + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#endif /* GL_ARB_map_buffer_range */ + +#ifndef GL_ARB_matrix_palette +#define GL_ARB_matrix_palette 1 +#define GL_MATRIX_PALETTE_ARB 0x8840 +#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 +#define GL_MAX_PALETTE_MATRICES_ARB 0x8842 +#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 +#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 +#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 +#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 + typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index); + typedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC) (GLint size, const GLubyte *indices); + typedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC) (GLint size, const GLushort *indices); + typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices); + typedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCurrentPaletteMatrixARB (GLint index); + GLAPI void APIENTRY glMatrixIndexubvARB (GLint size, const GLubyte *indices); + GLAPI void APIENTRY glMatrixIndexusvARB (GLint size, const GLushort *indices); + GLAPI void APIENTRY glMatrixIndexuivARB (GLint size, const GLuint *indices); + GLAPI void APIENTRY glMatrixIndexPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_ARB_matrix_palette */ + +#ifndef GL_ARB_multi_bind +#define GL_ARB_multi_bind 1 +#endif /* GL_ARB_multi_bind */ + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#endif /* GL_ARB_multi_draw_indirect */ + +#ifndef GL_ARB_multisample +#define GL_ARB_multisample 1 +#define GL_MULTISAMPLE_ARB 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F +#define GL_SAMPLE_COVERAGE_ARB 0x80A0 +#define GL_SAMPLE_BUFFERS_ARB 0x80A8 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB +#define GL_MULTISAMPLE_BIT_ARB 0x20000000 + typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLfloat value, GLboolean invert); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSampleCoverageARB (GLfloat value, GLboolean invert); +#endif +#endif /* GL_ARB_multisample */ + +#ifndef GL_ARB_multitexture +#define GL_ARB_multitexture 1 +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 + typedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC) (GLenum texture); + typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glActiveTextureARB (GLenum texture); + GLAPI void APIENTRY glClientActiveTextureARB (GLenum texture); + GLAPI void APIENTRY glMultiTexCoord1dARB (GLenum target, GLdouble s); + GLAPI void APIENTRY glMultiTexCoord1dvARB (GLenum target, const GLdouble *v); + GLAPI void APIENTRY glMultiTexCoord1fARB (GLenum target, GLfloat s); + GLAPI void APIENTRY glMultiTexCoord1fvARB (GLenum target, const GLfloat *v); + GLAPI void APIENTRY glMultiTexCoord1iARB (GLenum target, GLint s); + GLAPI void APIENTRY glMultiTexCoord1ivARB (GLenum target, const GLint *v); + GLAPI void APIENTRY glMultiTexCoord1sARB (GLenum target, GLshort s); + GLAPI void APIENTRY glMultiTexCoord1svARB (GLenum target, const GLshort *v); + GLAPI void APIENTRY glMultiTexCoord2dARB (GLenum target, GLdouble s, GLdouble t); + GLAPI void APIENTRY glMultiTexCoord2dvARB (GLenum target, const GLdouble *v); + GLAPI void APIENTRY glMultiTexCoord2fARB (GLenum target, GLfloat s, GLfloat t); + GLAPI void APIENTRY glMultiTexCoord2fvARB (GLenum target, const GLfloat *v); + GLAPI void APIENTRY glMultiTexCoord2iARB (GLenum target, GLint s, GLint t); + GLAPI void APIENTRY glMultiTexCoord2ivARB (GLenum target, const GLint *v); + GLAPI void APIENTRY glMultiTexCoord2sARB (GLenum target, GLshort s, GLshort t); + GLAPI void APIENTRY glMultiTexCoord2svARB (GLenum target, const GLshort *v); + GLAPI void APIENTRY glMultiTexCoord3dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r); + GLAPI void APIENTRY glMultiTexCoord3dvARB (GLenum target, const GLdouble *v); + GLAPI void APIENTRY glMultiTexCoord3fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r); + GLAPI void APIENTRY glMultiTexCoord3fvARB (GLenum target, const GLfloat *v); + GLAPI void APIENTRY glMultiTexCoord3iARB (GLenum target, GLint s, GLint t, GLint r); + GLAPI void APIENTRY glMultiTexCoord3ivARB (GLenum target, const GLint *v); + GLAPI void APIENTRY glMultiTexCoord3sARB (GLenum target, GLshort s, GLshort t, GLshort r); + GLAPI void APIENTRY glMultiTexCoord3svARB (GLenum target, const GLshort *v); + GLAPI void APIENTRY glMultiTexCoord4dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); + GLAPI void APIENTRY glMultiTexCoord4dvARB (GLenum target, const GLdouble *v); + GLAPI void APIENTRY glMultiTexCoord4fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); + GLAPI void APIENTRY glMultiTexCoord4fvARB (GLenum target, const GLfloat *v); + GLAPI void APIENTRY glMultiTexCoord4iARB (GLenum target, GLint s, GLint t, GLint r, GLint q); + GLAPI void APIENTRY glMultiTexCoord4ivARB (GLenum target, const GLint *v); + GLAPI void APIENTRY glMultiTexCoord4sARB (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); + GLAPI void APIENTRY glMultiTexCoord4svARB (GLenum target, const GLshort *v); +#endif +#endif /* GL_ARB_multitexture */ + +#ifndef GL_ARB_occlusion_query +#define GL_ARB_occlusion_query 1 +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#define GL_SAMPLES_PASSED_ARB 0x8914 + typedef void (APIENTRYP PFNGLGENQUERIESARBPROC) (GLsizei n, GLuint *ids); + typedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC) (GLsizei n, const GLuint *ids); + typedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC) (GLuint id); + typedef void (APIENTRYP PFNGLBEGINQUERYARBPROC) (GLenum target, GLuint id); + typedef void (APIENTRYP PFNGLENDQUERYARBPROC) (GLenum target); + typedef void (APIENTRYP PFNGLGETQUERYIVARBPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC) (GLuint id, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC) (GLuint id, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGenQueriesARB (GLsizei n, GLuint *ids); + GLAPI void APIENTRY glDeleteQueriesARB (GLsizei n, const GLuint *ids); + GLAPI GLboolean APIENTRY glIsQueryARB (GLuint id); + GLAPI void APIENTRY glBeginQueryARB (GLenum target, GLuint id); + GLAPI void APIENTRY glEndQueryARB (GLenum target); + GLAPI void APIENTRY glGetQueryivARB (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetQueryObjectivARB (GLuint id, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint id, GLenum pname, GLuint *params); +#endif +#endif /* GL_ARB_occlusion_query */ + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#endif /* GL_ARB_occlusion_query2 */ + +#ifndef GL_ARB_parallel_shader_compile +#define GL_ARB_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_ARB 0x91B0 +#define GL_COMPLETION_STATUS_ARB 0x91B1 + typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSARBPROC) (GLuint count); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMaxShaderCompilerThreadsARB (GLuint count); +#endif +#endif /* GL_ARB_parallel_shader_compile */ + +#ifndef GL_ARB_pipeline_statistics_query +#define GL_ARB_pipeline_statistics_query 1 +#define GL_VERTICES_SUBMITTED_ARB 0x82EE +#define GL_PRIMITIVES_SUBMITTED_ARB 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS_ARB 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 +#endif /* GL_ARB_pipeline_statistics_query */ + +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif /* GL_ARB_pixel_buffer_object */ + +#ifndef GL_ARB_point_parameters +#define GL_ARB_point_parameters 1 +#define GL_POINT_SIZE_MIN_ARB 0x8126 +#define GL_POINT_SIZE_MAX_ARB 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 + typedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC) (GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPointParameterfARB (GLenum pname, GLfloat param); + GLAPI void APIENTRY glPointParameterfvARB (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_ARB_point_parameters */ + +#ifndef GL_ARB_point_sprite +#define GL_ARB_point_sprite 1 +#define GL_POINT_SPRITE_ARB 0x8861 +#define GL_COORD_REPLACE_ARB 0x8862 +#endif /* GL_ARB_point_sprite */ + +#ifndef GL_ARB_polygon_offset_clamp +#define GL_ARB_polygon_offset_clamp 1 +#endif /* GL_ARB_polygon_offset_clamp */ + +#ifndef GL_ARB_post_depth_coverage +#define GL_ARB_post_depth_coverage 1 +#endif /* GL_ARB_post_depth_coverage */ + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#endif /* GL_ARB_program_interface_query */ + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#endif /* GL_ARB_provoking_vertex */ + +#ifndef GL_ARB_query_buffer_object +#define GL_ARB_query_buffer_object 1 +#endif /* GL_ARB_query_buffer_object */ + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif /* GL_ARB_robust_buffer_access_behavior */ + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 + typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); + typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); + typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); + typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img); + typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); + typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); + typedef void (APIENTRYP PFNGLGETNMAPDVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); + typedef void (APIENTRYP PFNGLGETNMAPFVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); + typedef void (APIENTRYP PFNGLGETNMAPIVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLint *v); + typedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC) (GLenum map, GLsizei bufSize, GLfloat *values); + typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC) (GLenum map, GLsizei bufSize, GLuint *values); + typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC) (GLenum map, GLsizei bufSize, GLushort *values); + typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC) (GLsizei bufSize, GLubyte *pattern); + typedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); + typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); + typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); + typedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); + typedef void (APIENTRYP PFNGLGETNMINMAXARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); + GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); + GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); + GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, void *img); + GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); + GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); + GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); + GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); + GLAPI void APIENTRY glGetnMapdvARB (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); + GLAPI void APIENTRY glGetnMapfvARB (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); + GLAPI void APIENTRY glGetnMapivARB (GLenum target, GLenum query, GLsizei bufSize, GLint *v); + GLAPI void APIENTRY glGetnPixelMapfvARB (GLenum map, GLsizei bufSize, GLfloat *values); + GLAPI void APIENTRY glGetnPixelMapuivARB (GLenum map, GLsizei bufSize, GLuint *values); + GLAPI void APIENTRY glGetnPixelMapusvARB (GLenum map, GLsizei bufSize, GLushort *values); + GLAPI void APIENTRY glGetnPolygonStippleARB (GLsizei bufSize, GLubyte *pattern); + GLAPI void APIENTRY glGetnColorTableARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); + GLAPI void APIENTRY glGetnConvolutionFilterARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); + GLAPI void APIENTRY glGetnSeparableFilterARB (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); + GLAPI void APIENTRY glGetnHistogramARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); + GLAPI void APIENTRY glGetnMinmaxARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +#endif +#endif /* GL_ARB_robustness */ + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif /* GL_ARB_robustness_isolation */ + +#ifndef GL_ARB_sample_locations +#define GL_ARB_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB 0x9340 +#define GL_SAMPLE_LOCATION_ARB 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB 0x9343 + typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLEVALUATEDEPTHVALUESARBPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferSampleLocationsfvARB (GLenum target, GLuint start, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glNamedFramebufferSampleLocationsfvARB (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glEvaluateDepthValuesARB (void); +#endif +#endif /* GL_ARB_sample_locations */ + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 + typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); +#endif +#endif /* GL_ARB_sample_shading */ + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#endif /* GL_ARB_sampler_objects */ + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#endif /* GL_ARB_seamless_cube_map */ + +#ifndef GL_ARB_seamless_cubemap_per_texture +#define GL_ARB_seamless_cubemap_per_texture 1 +#endif /* GL_ARB_seamless_cubemap_per_texture */ + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#endif /* GL_ARB_separate_shader_objects */ + +#ifndef GL_ARB_shader_atomic_counter_ops +#define GL_ARB_shader_atomic_counter_ops 1 +#endif /* GL_ARB_shader_atomic_counter_ops */ + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ARB_shader_atomic_counters 1 +#endif /* GL_ARB_shader_atomic_counters */ + +#ifndef GL_ARB_shader_ballot +#define GL_ARB_shader_ballot 1 +#endif /* GL_ARB_shader_ballot */ + +#ifndef GL_ARB_shader_bit_encoding +#define GL_ARB_shader_bit_encoding 1 +#endif /* GL_ARB_shader_bit_encoding */ + +#ifndef GL_ARB_shader_clock +#define GL_ARB_shader_clock 1 +#endif /* GL_ARB_shader_clock */ + +#ifndef GL_ARB_shader_draw_parameters +#define GL_ARB_shader_draw_parameters 1 +#endif /* GL_ARB_shader_draw_parameters */ + +#ifndef GL_ARB_shader_group_vote +#define GL_ARB_shader_group_vote 1 +#endif /* GL_ARB_shader_group_vote */ + +#ifndef GL_ARB_shader_image_load_store +#define GL_ARB_shader_image_load_store 1 +#endif /* GL_ARB_shader_image_load_store */ + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif /* GL_ARB_shader_image_size */ + +#ifndef GL_ARB_shader_objects +#define GL_ARB_shader_objects 1 +#ifdef __APPLE__ + typedef void *GLhandleARB; +#else + typedef unsigned int GLhandleARB; +#endif + typedef char GLcharARB; +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 + typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj); + typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC) (GLenum pname); + typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB attachedObj); + typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType); + typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); + typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj); + typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC) (void); + typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj); + typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC) (GLhandleARB programObj); + typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC) (GLhandleARB programObj); + typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC) (GLhandleARB programObj); + typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC) (GLint location, GLfloat v0); + typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC) (GLint location, GLfloat v0, GLfloat v1); + typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC) (GLint location, GLint v0); + typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC) (GLint location, GLint v0, GLint v1); + typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2); + typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC) (GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC) (GLhandleARB obj, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); + typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC) (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); + typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); + typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); + typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC) (GLhandleARB programObj, GLint location, GLfloat *params); + typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC) (GLhandleARB programObj, GLint location, GLint *params); + typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDeleteObjectARB (GLhandleARB obj); + GLAPI GLhandleARB APIENTRY glGetHandleARB (GLenum pname); + GLAPI void APIENTRY glDetachObjectARB (GLhandleARB containerObj, GLhandleARB attachedObj); + GLAPI GLhandleARB APIENTRY glCreateShaderObjectARB (GLenum shaderType); + GLAPI void APIENTRY glShaderSourceARB (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); + GLAPI void APIENTRY glCompileShaderARB (GLhandleARB shaderObj); + GLAPI GLhandleARB APIENTRY glCreateProgramObjectARB (void); + GLAPI void APIENTRY glAttachObjectARB (GLhandleARB containerObj, GLhandleARB obj); + GLAPI void APIENTRY glLinkProgramARB (GLhandleARB programObj); + GLAPI void APIENTRY glUseProgramObjectARB (GLhandleARB programObj); + GLAPI void APIENTRY glValidateProgramARB (GLhandleARB programObj); + GLAPI void APIENTRY glUniform1fARB (GLint location, GLfloat v0); + GLAPI void APIENTRY glUniform2fARB (GLint location, GLfloat v0, GLfloat v1); + GLAPI void APIENTRY glUniform3fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + GLAPI void APIENTRY glUniform4fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + GLAPI void APIENTRY glUniform1iARB (GLint location, GLint v0); + GLAPI void APIENTRY glUniform2iARB (GLint location, GLint v0, GLint v1); + GLAPI void APIENTRY glUniform3iARB (GLint location, GLint v0, GLint v1, GLint v2); + GLAPI void APIENTRY glUniform4iARB (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + GLAPI void APIENTRY glUniform1fvARB (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform2fvARB (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform3fvARB (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform4fvARB (GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glUniform1ivARB (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniform2ivARB (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniform3ivARB (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniform4ivARB (GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glUniformMatrix2fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix3fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glUniformMatrix4fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glGetObjectParameterfvARB (GLhandleARB obj, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetObjectParameterivARB (GLhandleARB obj, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetInfoLogARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); + GLAPI void APIENTRY glGetAttachedObjectsARB (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); + GLAPI GLint APIENTRY glGetUniformLocationARB (GLhandleARB programObj, const GLcharARB *name); + GLAPI void APIENTRY glGetActiveUniformARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); + GLAPI void APIENTRY glGetUniformfvARB (GLhandleARB programObj, GLint location, GLfloat *params); + GLAPI void APIENTRY glGetUniformivARB (GLhandleARB programObj, GLint location, GLint *params); + GLAPI void APIENTRY glGetShaderSourceARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#endif +#endif /* GL_ARB_shader_objects */ + +#ifndef GL_ARB_shader_precision +#define GL_ARB_shader_precision 1 +#endif /* GL_ARB_shader_precision */ + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif /* GL_ARB_shader_stencil_export */ + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#endif /* GL_ARB_shader_storage_buffer_object */ + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#endif /* GL_ARB_shader_subroutine */ + +#ifndef GL_ARB_shader_texture_image_samples +#define GL_ARB_shader_texture_image_samples 1 +#endif /* GL_ARB_shader_texture_image_samples */ + +#ifndef GL_ARB_shader_texture_lod +#define GL_ARB_shader_texture_lod 1 +#endif /* GL_ARB_shader_texture_lod */ + +#ifndef GL_ARB_shader_viewport_layer_array +#define GL_ARB_shader_viewport_layer_array 1 +#endif /* GL_ARB_shader_viewport_layer_array */ + +#ifndef GL_ARB_shading_language_100 +#define GL_ARB_shading_language_100 1 +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#endif /* GL_ARB_shading_language_100 */ + +#ifndef GL_ARB_shading_language_420pack +#define GL_ARB_shading_language_420pack 1 +#endif /* GL_ARB_shading_language_420pack */ + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA + typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); + typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); + typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); + typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); + typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); + typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); + GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); + GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); + GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); + GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); + GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif +#endif /* GL_ARB_shading_language_include */ + +#ifndef GL_ARB_shading_language_packing +#define GL_ARB_shading_language_packing 1 +#endif /* GL_ARB_shading_language_packing */ + +#ifndef GL_ARB_shadow +#define GL_ARB_shadow 1 +#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C +#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D +#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E +#endif /* GL_ARB_shadow */ + +#ifndef GL_ARB_shadow_ambient +#define GL_ARB_shadow_ambient 1 +#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF +#endif /* GL_ARB_shadow_ambient */ + +#ifndef GL_ARB_sparse_buffer +#define GL_ARB_sparse_buffer 1 +#define GL_SPARSE_STORAGE_BIT_ARB 0x0400 +#define GL_SPARSE_BUFFER_PAGE_SIZE_ARB 0x82F8 + typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTARBPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); + typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); + typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBufferPageCommitmentARB (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); + GLAPI void APIENTRY glNamedBufferPageCommitmentEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); + GLAPI void APIENTRY glNamedBufferPageCommitmentARB (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_buffer */ + +#ifndef GL_ARB_sparse_texture +#define GL_ARB_sparse_texture 1 +#define GL_TEXTURE_SPARSE_ARB 0x91A6 +#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 +#define GL_NUM_SPARSE_LEVELS_ARB 0x91AA +#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 +#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 + typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexPageCommitmentARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_texture */ + +#ifndef GL_ARB_sparse_texture2 +#define GL_ARB_sparse_texture2 1 +#endif /* GL_ARB_sparse_texture2 */ + +#ifndef GL_ARB_sparse_texture_clamp +#define GL_ARB_sparse_texture_clamp 1 +#endif /* GL_ARB_sparse_texture_clamp */ + +#ifndef GL_ARB_spirv_extensions +#define GL_ARB_spirv_extensions 1 +#endif /* GL_ARB_spirv_extensions */ + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#endif /* GL_ARB_stencil_texturing */ + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#endif /* GL_ARB_sync */ + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#endif /* GL_ARB_tessellation_shader */ + +#ifndef GL_ARB_texture_barrier +#define GL_ARB_texture_barrier 1 +#endif /* GL_ARB_texture_barrier */ + +#ifndef GL_ARB_texture_border_clamp +#define GL_ARB_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif /* GL_ARB_texture_border_clamp */ + +#ifndef GL_ARB_texture_buffer_object +#define GL_ARB_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_ARB 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E + typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexBufferARB (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_ARB_texture_buffer_object */ + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#endif /* GL_ARB_texture_buffer_object_rgb32 */ + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#endif /* GL_ARB_texture_buffer_range */ + +#ifndef GL_ARB_texture_compression +#define GL_ARB_texture_compression 1 +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint level, void *img); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexImage1DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexSubImage3DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexSubImage2DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glCompressedTexSubImage1DARB (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); + GLAPI void APIENTRY glGetCompressedTexImageARB (GLenum target, GLint level, void *img); +#endif +#endif /* GL_ARB_texture_compression */ + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif /* GL_ARB_texture_compression_bptc */ + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#endif /* GL_ARB_texture_compression_rgtc */ + +#ifndef GL_ARB_texture_cube_map +#define GL_ARB_texture_cube_map 1 +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#endif /* GL_ARB_texture_cube_map */ + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif /* GL_ARB_texture_cube_map_array */ + +#ifndef GL_ARB_texture_env_add +#define GL_ARB_texture_env_add 1 +#endif /* GL_ARB_texture_env_add */ + +#ifndef GL_ARB_texture_env_combine +#define GL_ARB_texture_env_combine 1 +#define GL_COMBINE_ARB 0x8570 +#define GL_COMBINE_RGB_ARB 0x8571 +#define GL_COMBINE_ALPHA_ARB 0x8572 +#define GL_SOURCE0_RGB_ARB 0x8580 +#define GL_SOURCE1_RGB_ARB 0x8581 +#define GL_SOURCE2_RGB_ARB 0x8582 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_OPERAND2_ALPHA_ARB 0x859A +#define GL_RGB_SCALE_ARB 0x8573 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_CONSTANT_ARB 0x8576 +#define GL_PRIMARY_COLOR_ARB 0x8577 +#define GL_PREVIOUS_ARB 0x8578 +#endif /* GL_ARB_texture_env_combine */ + +#ifndef GL_ARB_texture_env_crossbar +#define GL_ARB_texture_env_crossbar 1 +#endif /* GL_ARB_texture_env_crossbar */ + +#ifndef GL_ARB_texture_env_dot3 +#define GL_ARB_texture_env_dot3 1 +#define GL_DOT3_RGB_ARB 0x86AE +#define GL_DOT3_RGBA_ARB 0x86AF +#endif /* GL_ARB_texture_env_dot3 */ + +#ifndef GL_ARB_texture_filter_anisotropic +#define GL_ARB_texture_filter_anisotropic 1 +#endif /* GL_ARB_texture_filter_anisotropic */ + +#ifndef GL_ARB_texture_filter_minmax +#define GL_ARB_texture_filter_minmax 1 +#define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366 +#define GL_WEIGHTED_AVERAGE_ARB 0x9367 +#endif /* GL_ARB_texture_filter_minmax */ + +#ifndef GL_ARB_texture_float +#define GL_ARB_texture_float 1 +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGB32F_ARB 0x8815 +#define GL_ALPHA32F_ARB 0x8816 +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_RGBA16F_ARB 0x881A +#define GL_RGB16F_ARB 0x881B +#define GL_ALPHA16F_ARB 0x881C +#define GL_INTENSITY16F_ARB 0x881D +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#endif /* GL_ARB_texture_float */ + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F +#endif /* GL_ARB_texture_gather */ + +#ifndef GL_ARB_texture_mirror_clamp_to_edge +#define GL_ARB_texture_mirror_clamp_to_edge 1 +#endif /* GL_ARB_texture_mirror_clamp_to_edge */ + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif /* GL_ARB_texture_mirrored_repeat */ + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#endif /* GL_ARB_texture_multisample */ + +#ifndef GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_non_power_of_two 1 +#endif /* GL_ARB_texture_non_power_of_two */ + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif /* GL_ARB_texture_query_levels */ + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif /* GL_ARB_texture_query_lod */ + +#ifndef GL_ARB_texture_rectangle +#define GL_ARB_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif /* GL_ARB_texture_rectangle */ + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#endif /* GL_ARB_texture_rg */ + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#endif /* GL_ARB_texture_rgb10_a2ui */ + +#ifndef GL_ARB_texture_stencil8 +#define GL_ARB_texture_stencil8 1 +#endif /* GL_ARB_texture_stencil8 */ + +#ifndef GL_ARB_texture_storage +#define GL_ARB_texture_storage 1 +#endif /* GL_ARB_texture_storage */ + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#endif /* GL_ARB_texture_storage_multisample */ + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#endif /* GL_ARB_texture_swizzle */ + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#endif /* GL_ARB_texture_view */ + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#endif /* GL_ARB_timer_query */ + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#endif /* GL_ARB_transform_feedback2 */ + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#endif /* GL_ARB_transform_feedback3 */ + +#ifndef GL_ARB_transform_feedback_instanced +#define GL_ARB_transform_feedback_instanced 1 +#endif /* GL_ARB_transform_feedback_instanced */ + +#ifndef GL_ARB_transform_feedback_overflow_query +#define GL_ARB_transform_feedback_overflow_query 1 +#define GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB 0x82ED +#endif /* GL_ARB_transform_feedback_overflow_query */ + +#ifndef GL_ARB_transpose_matrix +#define GL_ARB_transpose_matrix 1 +#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 + typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); + typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); + typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); + typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glLoadTransposeMatrixfARB (const GLfloat *m); + GLAPI void APIENTRY glLoadTransposeMatrixdARB (const GLdouble *m); + GLAPI void APIENTRY glMultTransposeMatrixfARB (const GLfloat *m); + GLAPI void APIENTRY glMultTransposeMatrixdARB (const GLdouble *m); +#endif +#endif /* GL_ARB_transpose_matrix */ + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#endif /* GL_ARB_uniform_buffer_object */ + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#endif /* GL_ARB_vertex_array_bgra */ + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#endif /* GL_ARB_vertex_array_object */ + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#endif /* GL_ARB_vertex_attrib_64bit */ + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#endif /* GL_ARB_vertex_attrib_binding */ + +#ifndef GL_ARB_vertex_blend +#define GL_ARB_vertex_blend 1 +#define GL_MAX_VERTEX_UNITS_ARB 0x86A4 +#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 +#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 +#define GL_VERTEX_BLEND_ARB 0x86A7 +#define GL_CURRENT_WEIGHT_ARB 0x86A8 +#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA +#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB +#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC +#define GL_WEIGHT_ARRAY_ARB 0x86AD +#define GL_MODELVIEW0_ARB 0x1700 +#define GL_MODELVIEW1_ARB 0x850A +#define GL_MODELVIEW2_ARB 0x8722 +#define GL_MODELVIEW3_ARB 0x8723 +#define GL_MODELVIEW4_ARB 0x8724 +#define GL_MODELVIEW5_ARB 0x8725 +#define GL_MODELVIEW6_ARB 0x8726 +#define GL_MODELVIEW7_ARB 0x8727 +#define GL_MODELVIEW8_ARB 0x8728 +#define GL_MODELVIEW9_ARB 0x8729 +#define GL_MODELVIEW10_ARB 0x872A +#define GL_MODELVIEW11_ARB 0x872B +#define GL_MODELVIEW12_ARB 0x872C +#define GL_MODELVIEW13_ARB 0x872D +#define GL_MODELVIEW14_ARB 0x872E +#define GL_MODELVIEW15_ARB 0x872F +#define GL_MODELVIEW16_ARB 0x8730 +#define GL_MODELVIEW17_ARB 0x8731 +#define GL_MODELVIEW18_ARB 0x8732 +#define GL_MODELVIEW19_ARB 0x8733 +#define GL_MODELVIEW20_ARB 0x8734 +#define GL_MODELVIEW21_ARB 0x8735 +#define GL_MODELVIEW22_ARB 0x8736 +#define GL_MODELVIEW23_ARB 0x8737 +#define GL_MODELVIEW24_ARB 0x8738 +#define GL_MODELVIEW25_ARB 0x8739 +#define GL_MODELVIEW26_ARB 0x873A +#define GL_MODELVIEW27_ARB 0x873B +#define GL_MODELVIEW28_ARB 0x873C +#define GL_MODELVIEW29_ARB 0x873D +#define GL_MODELVIEW30_ARB 0x873E +#define GL_MODELVIEW31_ARB 0x873F + typedef void (APIENTRYP PFNGLWEIGHTBVARBPROC) (GLint size, const GLbyte *weights); + typedef void (APIENTRYP PFNGLWEIGHTSVARBPROC) (GLint size, const GLshort *weights); + typedef void (APIENTRYP PFNGLWEIGHTIVARBPROC) (GLint size, const GLint *weights); + typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); + typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); + typedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC) (GLint size, const GLubyte *weights); + typedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC) (GLint size, const GLushort *weights); + typedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC) (GLint size, const GLuint *weights); + typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glWeightbvARB (GLint size, const GLbyte *weights); + GLAPI void APIENTRY glWeightsvARB (GLint size, const GLshort *weights); + GLAPI void APIENTRY glWeightivARB (GLint size, const GLint *weights); + GLAPI void APIENTRY glWeightfvARB (GLint size, const GLfloat *weights); + GLAPI void APIENTRY glWeightdvARB (GLint size, const GLdouble *weights); + GLAPI void APIENTRY glWeightubvARB (GLint size, const GLubyte *weights); + GLAPI void APIENTRY glWeightusvARB (GLint size, const GLushort *weights); + GLAPI void APIENTRY glWeightuivARB (GLint size, const GLuint *weights); + GLAPI void APIENTRY glWeightPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glVertexBlendARB (GLint count); +#endif +#endif /* GL_ARB_vertex_blend */ + +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 + typedef khronos_ssize_t GLsizeiptrARB; + typedef khronos_intptr_t GLintptrARB; +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA + typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer); + typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers); + typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers); + typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); + typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); + typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); + typedef void *(APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access); + typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); + typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBindBufferARB (GLenum target, GLuint buffer); + GLAPI void APIENTRY glDeleteBuffersARB (GLsizei n, const GLuint *buffers); + GLAPI void APIENTRY glGenBuffersARB (GLsizei n, GLuint *buffers); + GLAPI GLboolean APIENTRY glIsBufferARB (GLuint buffer); + GLAPI void APIENTRY glBufferDataARB (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); + GLAPI void APIENTRY glBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); + GLAPI void APIENTRY glGetBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); + GLAPI void *APIENTRY glMapBufferARB (GLenum target, GLenum access); + GLAPI GLboolean APIENTRY glUnmapBufferARB (GLenum target); + GLAPI void APIENTRY glGetBufferParameterivARB (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetBufferPointervARB (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_ARB_vertex_buffer_object */ + +#ifndef GL_ARB_vertex_program +#define GL_ARB_vertex_program 1 +#define GL_COLOR_SUM_ARB 0x8458 +#define GL_VERTEX_PROGRAM_ARB 0x8620 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 +#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 +#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 +#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 + typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC) (GLuint index, GLfloat x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC) (GLuint index, GLshort x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC) (GLuint index, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC) (GLuint index, GLfloat x, GLfloat y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC) (GLuint index, GLshort x, GLshort y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC) (GLuint index, const GLbyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC) (GLuint index, const GLubyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC) (GLuint index, const GLushort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC) (GLuint index, const GLbyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC) (GLuint index, const GLubyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC) (GLuint index, const GLushort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); + typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, void **pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexAttrib1dARB (GLuint index, GLdouble x); + GLAPI void APIENTRY glVertexAttrib1dvARB (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib1fARB (GLuint index, GLfloat x); + GLAPI void APIENTRY glVertexAttrib1fvARB (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib1sARB (GLuint index, GLshort x); + GLAPI void APIENTRY glVertexAttrib1svARB (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib2dARB (GLuint index, GLdouble x, GLdouble y); + GLAPI void APIENTRY glVertexAttrib2dvARB (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib2fARB (GLuint index, GLfloat x, GLfloat y); + GLAPI void APIENTRY glVertexAttrib2fvARB (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib2sARB (GLuint index, GLshort x, GLshort y); + GLAPI void APIENTRY glVertexAttrib2svARB (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib3dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glVertexAttrib3dvARB (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib3fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glVertexAttrib3fvARB (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib3sARB (GLuint index, GLshort x, GLshort y, GLshort z); + GLAPI void APIENTRY glVertexAttrib3svARB (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4NbvARB (GLuint index, const GLbyte *v); + GLAPI void APIENTRY glVertexAttrib4NivARB (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttrib4NsvARB (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4NubARB (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); + GLAPI void APIENTRY glVertexAttrib4NubvARB (GLuint index, const GLubyte *v); + GLAPI void APIENTRY glVertexAttrib4NuivARB (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttrib4NusvARB (GLuint index, const GLushort *v); + GLAPI void APIENTRY glVertexAttrib4bvARB (GLuint index, const GLbyte *v); + GLAPI void APIENTRY glVertexAttrib4dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glVertexAttrib4dvARB (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib4fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glVertexAttrib4fvARB (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib4ivARB (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttrib4sARB (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); + GLAPI void APIENTRY glVertexAttrib4svARB (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4ubvARB (GLuint index, const GLubyte *v); + GLAPI void APIENTRY glVertexAttrib4uivARB (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttrib4usvARB (GLuint index, const GLushort *v); + GLAPI void APIENTRY glVertexAttribPointerARB (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glEnableVertexAttribArrayARB (GLuint index); + GLAPI void APIENTRY glDisableVertexAttribArrayARB (GLuint index); + GLAPI void APIENTRY glGetVertexAttribdvARB (GLuint index, GLenum pname, GLdouble *params); + GLAPI void APIENTRY glGetVertexAttribfvARB (GLuint index, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetVertexAttribivARB (GLuint index, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint index, GLenum pname, void **pointer); +#endif +#endif /* GL_ARB_vertex_program */ + +#ifndef GL_ARB_vertex_shader +#define GL_ARB_vertex_shader 1 +#define GL_VERTEX_SHADER_ARB 0x8B31 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A +#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D +#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 +#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A + typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC) (GLhandleARB programObj, GLuint index, const GLcharARB *name); + typedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); + typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBindAttribLocationARB (GLhandleARB programObj, GLuint index, const GLcharARB *name); + GLAPI void APIENTRY glGetActiveAttribARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); + GLAPI GLint APIENTRY glGetAttribLocationARB (GLhandleARB programObj, const GLcharARB *name); +#endif +#endif /* GL_ARB_vertex_shader */ + +#ifndef GL_ARB_vertex_type_10f_11f_11f_rev +#define GL_ARB_vertex_type_10f_11f_11f_rev 1 +#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#endif /* GL_ARB_vertex_type_2_10_10_10_rev */ + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 + typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYDVNVPROC) (GLuint first, GLsizei count, const GLdouble *v); + typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDDNVPROC) (GLuint index, GLdouble n, GLdouble f); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDepthRangeArraydvNV (GLuint first, GLsizei count, const GLdouble *v); + GLAPI void APIENTRY glDepthRangeIndexeddNV (GLuint index, GLdouble n, GLdouble f); +#endif +#endif /* GL_ARB_viewport_array */ + +#ifndef GL_ARB_window_pos +#define GL_ARB_window_pos 1 + typedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC) (GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC) (GLfloat x, GLfloat y); + typedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC) (GLint x, GLint y); + typedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC) (GLshort x, GLshort y); + typedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC) (const GLshort *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC) (GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC) (GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC) (GLint x, GLint y, GLint z); + typedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC) (GLshort x, GLshort y, GLshort z); + typedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC) (const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glWindowPos2dARB (GLdouble x, GLdouble y); + GLAPI void APIENTRY glWindowPos2dvARB (const GLdouble *v); + GLAPI void APIENTRY glWindowPos2fARB (GLfloat x, GLfloat y); + GLAPI void APIENTRY glWindowPos2fvARB (const GLfloat *v); + GLAPI void APIENTRY glWindowPos2iARB (GLint x, GLint y); + GLAPI void APIENTRY glWindowPos2ivARB (const GLint *v); + GLAPI void APIENTRY glWindowPos2sARB (GLshort x, GLshort y); + GLAPI void APIENTRY glWindowPos2svARB (const GLshort *v); + GLAPI void APIENTRY glWindowPos3dARB (GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glWindowPos3dvARB (const GLdouble *v); + GLAPI void APIENTRY glWindowPos3fARB (GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glWindowPos3fvARB (const GLfloat *v); + GLAPI void APIENTRY glWindowPos3iARB (GLint x, GLint y, GLint z); + GLAPI void APIENTRY glWindowPos3ivARB (const GLint *v); + GLAPI void APIENTRY glWindowPos3sARB (GLshort x, GLshort y, GLshort z); + GLAPI void APIENTRY glWindowPos3svARB (const GLshort *v); +#endif +#endif /* GL_ARB_window_pos */ + +#ifndef GL_KHR_blend_equation_advanced +#define GL_KHR_blend_equation_advanced 1 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_SCREEN_KHR 0x9295 +#define GL_OVERLAY_KHR 0x9296 +#define GL_DARKEN_KHR 0x9297 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLORBURN_KHR 0x929A +#define GL_HARDLIGHT_KHR 0x929B +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_DIFFERENCE_KHR 0x929E +#define GL_EXCLUSION_KHR 0x92A0 +#define GL_HSL_HUE_KHR 0x92AD +#define GL_HSL_SATURATION_KHR 0x92AE +#define GL_HSL_COLOR_KHR 0x92AF +#define GL_HSL_LUMINOSITY_KHR 0x92B0 + typedef void (APIENTRYP PFNGLBLENDBARRIERKHRPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendBarrierKHR (void); +#endif +#endif /* GL_KHR_blend_equation_advanced */ + +#ifndef GL_KHR_blend_equation_advanced_coherent +#define GL_KHR_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#endif /* GL_KHR_blend_equation_advanced_coherent */ + +#ifndef GL_KHR_context_flush_control +#define GL_KHR_context_flush_control 1 +#endif /* GL_KHR_context_flush_control */ + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#endif /* GL_KHR_debug */ + +#ifndef GL_KHR_no_error +#define GL_KHR_no_error 1 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 +#endif /* GL_KHR_no_error */ + +#ifndef GL_KHR_parallel_shader_compile +#define GL_KHR_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 +#define GL_COMPLETION_STATUS_KHR 0x91B1 + typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) (GLuint count); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMaxShaderCompilerThreadsKHR (GLuint count); +#endif +#endif /* GL_KHR_parallel_shader_compile */ + +#ifndef GL_KHR_robust_buffer_access_behavior +#define GL_KHR_robust_buffer_access_behavior 1 +#endif /* GL_KHR_robust_buffer_access_behavior */ + +#ifndef GL_KHR_robustness +#define GL_KHR_robustness 1 +#define GL_CONTEXT_ROBUST_ACCESS 0x90F3 +#endif /* GL_KHR_robustness */ + +#ifndef GL_KHR_shader_subgroup +#define GL_KHR_shader_subgroup 1 +#define GL_SUBGROUP_SIZE_KHR 0x9532 +#define GL_SUBGROUP_SUPPORTED_STAGES_KHR 0x9533 +#define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534 +#define GL_SUBGROUP_QUAD_ALL_STAGES_KHR 0x9535 +#define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001 +#define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR 0x00000002 +#define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004 +#define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008 +#define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010 +#define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020 +#define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040 +#define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR 0x00000080 +#endif /* GL_KHR_shader_subgroup */ + +#ifndef GL_KHR_texture_compression_astc_hdr +#define GL_KHR_texture_compression_astc_hdr 1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif /* GL_KHR_texture_compression_astc_hdr */ + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#endif /* GL_KHR_texture_compression_astc_ldr */ + +#ifndef GL_KHR_texture_compression_astc_sliced_3d +#define GL_KHR_texture_compression_astc_sliced_3d 1 +#endif /* GL_KHR_texture_compression_astc_sliced_3d */ + +#ifndef GL_OES_byte_coordinates +#define GL_OES_byte_coordinates 1 + typedef void (APIENTRYP PFNGLMULTITEXCOORD1BOESPROC) (GLenum texture, GLbyte s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1BVOESPROC) (GLenum texture, const GLbyte *coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2BOESPROC) (GLenum texture, GLbyte s, GLbyte t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2BVOESPROC) (GLenum texture, const GLbyte *coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3BVOESPROC) (GLenum texture, const GLbyte *coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4BVOESPROC) (GLenum texture, const GLbyte *coords); + typedef void (APIENTRYP PFNGLTEXCOORD1BOESPROC) (GLbyte s); + typedef void (APIENTRYP PFNGLTEXCOORD1BVOESPROC) (const GLbyte *coords); + typedef void (APIENTRYP PFNGLTEXCOORD2BOESPROC) (GLbyte s, GLbyte t); + typedef void (APIENTRYP PFNGLTEXCOORD2BVOESPROC) (const GLbyte *coords); + typedef void (APIENTRYP PFNGLTEXCOORD3BOESPROC) (GLbyte s, GLbyte t, GLbyte r); + typedef void (APIENTRYP PFNGLTEXCOORD3BVOESPROC) (const GLbyte *coords); + typedef void (APIENTRYP PFNGLTEXCOORD4BOESPROC) (GLbyte s, GLbyte t, GLbyte r, GLbyte q); + typedef void (APIENTRYP PFNGLTEXCOORD4BVOESPROC) (const GLbyte *coords); + typedef void (APIENTRYP PFNGLVERTEX2BOESPROC) (GLbyte x, GLbyte y); + typedef void (APIENTRYP PFNGLVERTEX2BVOESPROC) (const GLbyte *coords); + typedef void (APIENTRYP PFNGLVERTEX3BOESPROC) (GLbyte x, GLbyte y, GLbyte z); + typedef void (APIENTRYP PFNGLVERTEX3BVOESPROC) (const GLbyte *coords); + typedef void (APIENTRYP PFNGLVERTEX4BOESPROC) (GLbyte x, GLbyte y, GLbyte z, GLbyte w); + typedef void (APIENTRYP PFNGLVERTEX4BVOESPROC) (const GLbyte *coords); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMultiTexCoord1bOES (GLenum texture, GLbyte s); + GLAPI void APIENTRY glMultiTexCoord1bvOES (GLenum texture, const GLbyte *coords); + GLAPI void APIENTRY glMultiTexCoord2bOES (GLenum texture, GLbyte s, GLbyte t); + GLAPI void APIENTRY glMultiTexCoord2bvOES (GLenum texture, const GLbyte *coords); + GLAPI void APIENTRY glMultiTexCoord3bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r); + GLAPI void APIENTRY glMultiTexCoord3bvOES (GLenum texture, const GLbyte *coords); + GLAPI void APIENTRY glMultiTexCoord4bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); + GLAPI void APIENTRY glMultiTexCoord4bvOES (GLenum texture, const GLbyte *coords); + GLAPI void APIENTRY glTexCoord1bOES (GLbyte s); + GLAPI void APIENTRY glTexCoord1bvOES (const GLbyte *coords); + GLAPI void APIENTRY glTexCoord2bOES (GLbyte s, GLbyte t); + GLAPI void APIENTRY glTexCoord2bvOES (const GLbyte *coords); + GLAPI void APIENTRY glTexCoord3bOES (GLbyte s, GLbyte t, GLbyte r); + GLAPI void APIENTRY glTexCoord3bvOES (const GLbyte *coords); + GLAPI void APIENTRY glTexCoord4bOES (GLbyte s, GLbyte t, GLbyte r, GLbyte q); + GLAPI void APIENTRY glTexCoord4bvOES (const GLbyte *coords); + GLAPI void APIENTRY glVertex2bOES (GLbyte x, GLbyte y); + GLAPI void APIENTRY glVertex2bvOES (const GLbyte *coords); + GLAPI void APIENTRY glVertex3bOES (GLbyte x, GLbyte y, GLbyte z); + GLAPI void APIENTRY glVertex3bvOES (const GLbyte *coords); + GLAPI void APIENTRY glVertex4bOES (GLbyte x, GLbyte y, GLbyte z, GLbyte w); + GLAPI void APIENTRY glVertex4bvOES (const GLbyte *coords); +#endif +#endif /* GL_OES_byte_coordinates */ + +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif /* GL_OES_compressed_paletted_texture */ + +#ifndef GL_OES_fixed_point +#define GL_OES_fixed_point 1 + typedef khronos_int32_t GLfixed; +#define GL_FIXED_OES 0x140C + typedef void (APIENTRYP PFNGLALPHAFUNCXOESPROC) (GLenum func, GLfixed ref); + typedef void (APIENTRYP PFNGLCLEARCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); + typedef void (APIENTRYP PFNGLCLEARDEPTHXOESPROC) (GLfixed depth); + typedef void (APIENTRYP PFNGLCLIPPLANEXOESPROC) (GLenum plane, const GLfixed *equation); + typedef void (APIENTRYP PFNGLCOLOR4XOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); + typedef void (APIENTRYP PFNGLDEPTHRANGEXOESPROC) (GLfixed n, GLfixed f); + typedef void (APIENTRYP PFNGLFOGXOESPROC) (GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLFOGXVOESPROC) (GLenum pname, const GLfixed *param); + typedef void (APIENTRYP PFNGLFRUSTUMXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); + typedef void (APIENTRYP PFNGLGETCLIPPLANEXOESPROC) (GLenum plane, GLfixed *equation); + typedef void (APIENTRYP PFNGLGETFIXEDVOESPROC) (GLenum pname, GLfixed *params); + typedef void (APIENTRYP PFNGLGETTEXENVXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); + typedef void (APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); + typedef void (APIENTRYP PFNGLLIGHTMODELXOESPROC) (GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLLIGHTMODELXVOESPROC) (GLenum pname, const GLfixed *param); + typedef void (APIENTRYP PFNGLLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLLIGHTXVOESPROC) (GLenum light, GLenum pname, const GLfixed *params); + typedef void (APIENTRYP PFNGLLINEWIDTHXOESPROC) (GLfixed width); + typedef void (APIENTRYP PFNGLLOADMATRIXXOESPROC) (const GLfixed *m); + typedef void (APIENTRYP PFNGLMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLMATERIALXVOESPROC) (GLenum face, GLenum pname, const GLfixed *param); + typedef void (APIENTRYP PFNGLMULTMATRIXXOESPROC) (const GLfixed *m); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); + typedef void (APIENTRYP PFNGLNORMAL3XOESPROC) (GLfixed nx, GLfixed ny, GLfixed nz); + typedef void (APIENTRYP PFNGLORTHOXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); + typedef void (APIENTRYP PFNGLPOINTPARAMETERXVOESPROC) (GLenum pname, const GLfixed *params); + typedef void (APIENTRYP PFNGLPOINTSIZEXOESPROC) (GLfixed size); + typedef void (APIENTRYP PFNGLPOLYGONOFFSETXOESPROC) (GLfixed factor, GLfixed units); + typedef void (APIENTRYP PFNGLROTATEXOESPROC) (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); + typedef void (APIENTRYP PFNGLSCALEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); + typedef void (APIENTRYP PFNGLTEXENVXOESPROC) (GLenum target, GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLTEXENVXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); + typedef void (APIENTRYP PFNGLTEXPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); + typedef void (APIENTRYP PFNGLTRANSLATEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); + typedef void (APIENTRYP PFNGLACCUMXOESPROC) (GLenum op, GLfixed value); + typedef void (APIENTRYP PFNGLBITMAPXOESPROC) (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); + typedef void (APIENTRYP PFNGLBLENDCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); + typedef void (APIENTRYP PFNGLCLEARACCUMXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); + typedef void (APIENTRYP PFNGLCOLOR3XOESPROC) (GLfixed red, GLfixed green, GLfixed blue); + typedef void (APIENTRYP PFNGLCOLOR3XVOESPROC) (const GLfixed *components); + typedef void (APIENTRYP PFNGLCOLOR4XVOESPROC) (const GLfixed *components); + typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); + typedef void (APIENTRYP PFNGLEVALCOORD1XOESPROC) (GLfixed u); + typedef void (APIENTRYP PFNGLEVALCOORD1XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLEVALCOORD2XOESPROC) (GLfixed u, GLfixed v); + typedef void (APIENTRYP PFNGLEVALCOORD2XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLFEEDBACKBUFFERXOESPROC) (GLsizei n, GLenum type, const GLfixed *buffer); + typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); + typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); + typedef void (APIENTRYP PFNGLGETLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed *params); + typedef void (APIENTRYP PFNGLGETMAPXVOESPROC) (GLenum target, GLenum query, GLfixed *v); + typedef void (APIENTRYP PFNGLGETMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLGETPIXELMAPXVPROC) (GLenum map, GLint size, GLfixed *values); + typedef void (APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, GLfixed *params); + typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERXVOESPROC) (GLenum target, GLint level, GLenum pname, GLfixed *params); + typedef void (APIENTRYP PFNGLINDEXXOESPROC) (GLfixed component); + typedef void (APIENTRYP PFNGLINDEXXVOESPROC) (const GLfixed *component); + typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); + typedef void (APIENTRYP PFNGLMAP1XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); + typedef void (APIENTRYP PFNGLMAP2XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); + typedef void (APIENTRYP PFNGLMAPGRID1XOESPROC) (GLint n, GLfixed u1, GLfixed u2); + typedef void (APIENTRYP PFNGLMAPGRID2XOESPROC) (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); + typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1XOESPROC) (GLenum texture, GLfixed s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1XVOESPROC) (GLenum texture, const GLfixed *coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2XOESPROC) (GLenum texture, GLfixed s, GLfixed t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2XVOESPROC) (GLenum texture, const GLfixed *coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3XVOESPROC) (GLenum texture, const GLfixed *coords); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4XVOESPROC) (GLenum texture, const GLfixed *coords); + typedef void (APIENTRYP PFNGLNORMAL3XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLPASSTHROUGHXOESPROC) (GLfixed token); + typedef void (APIENTRYP PFNGLPIXELMAPXPROC) (GLenum map, GLint size, const GLfixed *values); + typedef void (APIENTRYP PFNGLPIXELSTOREXPROC) (GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLPIXELTRANSFERXOESPROC) (GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLPIXELZOOMXOESPROC) (GLfixed xfactor, GLfixed yfactor); + typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESXOESPROC) (GLsizei n, const GLuint *textures, const GLfixed *priorities); + typedef void (APIENTRYP PFNGLRASTERPOS2XOESPROC) (GLfixed x, GLfixed y); + typedef void (APIENTRYP PFNGLRASTERPOS2XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLRASTERPOS3XOESPROC) (GLfixed x, GLfixed y, GLfixed z); + typedef void (APIENTRYP PFNGLRASTERPOS3XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLRASTERPOS4XOESPROC) (GLfixed x, GLfixed y, GLfixed z, GLfixed w); + typedef void (APIENTRYP PFNGLRASTERPOS4XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLRECTXOESPROC) (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); + typedef void (APIENTRYP PFNGLRECTXVOESPROC) (const GLfixed *v1, const GLfixed *v2); + typedef void (APIENTRYP PFNGLTEXCOORD1XOESPROC) (GLfixed s); + typedef void (APIENTRYP PFNGLTEXCOORD1XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLTEXCOORD2XOESPROC) (GLfixed s, GLfixed t); + typedef void (APIENTRYP PFNGLTEXCOORD2XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLTEXCOORD3XOESPROC) (GLfixed s, GLfixed t, GLfixed r); + typedef void (APIENTRYP PFNGLTEXCOORD3XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLTEXCOORD4XOESPROC) (GLfixed s, GLfixed t, GLfixed r, GLfixed q); + typedef void (APIENTRYP PFNGLTEXCOORD4XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLTEXGENXOESPROC) (GLenum coord, GLenum pname, GLfixed param); + typedef void (APIENTRYP PFNGLTEXGENXVOESPROC) (GLenum coord, GLenum pname, const GLfixed *params); + typedef void (APIENTRYP PFNGLVERTEX2XOESPROC) (GLfixed x); + typedef void (APIENTRYP PFNGLVERTEX2XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLVERTEX3XOESPROC) (GLfixed x, GLfixed y); + typedef void (APIENTRYP PFNGLVERTEX3XVOESPROC) (const GLfixed *coords); + typedef void (APIENTRYP PFNGLVERTEX4XOESPROC) (GLfixed x, GLfixed y, GLfixed z); + typedef void (APIENTRYP PFNGLVERTEX4XVOESPROC) (const GLfixed *coords); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glAlphaFuncxOES (GLenum func, GLfixed ref); + GLAPI void APIENTRY glClearColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); + GLAPI void APIENTRY glClearDepthxOES (GLfixed depth); + GLAPI void APIENTRY glClipPlanexOES (GLenum plane, const GLfixed *equation); + GLAPI void APIENTRY glColor4xOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); + GLAPI void APIENTRY glDepthRangexOES (GLfixed n, GLfixed f); + GLAPI void APIENTRY glFogxOES (GLenum pname, GLfixed param); + GLAPI void APIENTRY glFogxvOES (GLenum pname, const GLfixed *param); + GLAPI void APIENTRY glFrustumxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); + GLAPI void APIENTRY glGetClipPlanexOES (GLenum plane, GLfixed *equation); + GLAPI void APIENTRY glGetFixedvOES (GLenum pname, GLfixed *params); + GLAPI void APIENTRY glGetTexEnvxvOES (GLenum target, GLenum pname, GLfixed *params); + GLAPI void APIENTRY glGetTexParameterxvOES (GLenum target, GLenum pname, GLfixed *params); + GLAPI void APIENTRY glLightModelxOES (GLenum pname, GLfixed param); + GLAPI void APIENTRY glLightModelxvOES (GLenum pname, const GLfixed *param); + GLAPI void APIENTRY glLightxOES (GLenum light, GLenum pname, GLfixed param); + GLAPI void APIENTRY glLightxvOES (GLenum light, GLenum pname, const GLfixed *params); + GLAPI void APIENTRY glLineWidthxOES (GLfixed width); + GLAPI void APIENTRY glLoadMatrixxOES (const GLfixed *m); + GLAPI void APIENTRY glMaterialxOES (GLenum face, GLenum pname, GLfixed param); + GLAPI void APIENTRY glMaterialxvOES (GLenum face, GLenum pname, const GLfixed *param); + GLAPI void APIENTRY glMultMatrixxOES (const GLfixed *m); + GLAPI void APIENTRY glMultiTexCoord4xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); + GLAPI void APIENTRY glNormal3xOES (GLfixed nx, GLfixed ny, GLfixed nz); + GLAPI void APIENTRY glOrthoxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); + GLAPI void APIENTRY glPointParameterxvOES (GLenum pname, const GLfixed *params); + GLAPI void APIENTRY glPointSizexOES (GLfixed size); + GLAPI void APIENTRY glPolygonOffsetxOES (GLfixed factor, GLfixed units); + GLAPI void APIENTRY glRotatexOES (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); + GLAPI void APIENTRY glScalexOES (GLfixed x, GLfixed y, GLfixed z); + GLAPI void APIENTRY glTexEnvxOES (GLenum target, GLenum pname, GLfixed param); + GLAPI void APIENTRY glTexEnvxvOES (GLenum target, GLenum pname, const GLfixed *params); + GLAPI void APIENTRY glTexParameterxOES (GLenum target, GLenum pname, GLfixed param); + GLAPI void APIENTRY glTexParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); + GLAPI void APIENTRY glTranslatexOES (GLfixed x, GLfixed y, GLfixed z); + GLAPI void APIENTRY glAccumxOES (GLenum op, GLfixed value); + GLAPI void APIENTRY glBitmapxOES (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); + GLAPI void APIENTRY glBlendColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); + GLAPI void APIENTRY glClearAccumxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); + GLAPI void APIENTRY glColor3xOES (GLfixed red, GLfixed green, GLfixed blue); + GLAPI void APIENTRY glColor3xvOES (const GLfixed *components); + GLAPI void APIENTRY glColor4xvOES (const GLfixed *components); + GLAPI void APIENTRY glConvolutionParameterxOES (GLenum target, GLenum pname, GLfixed param); + GLAPI void APIENTRY glConvolutionParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); + GLAPI void APIENTRY glEvalCoord1xOES (GLfixed u); + GLAPI void APIENTRY glEvalCoord1xvOES (const GLfixed *coords); + GLAPI void APIENTRY glEvalCoord2xOES (GLfixed u, GLfixed v); + GLAPI void APIENTRY glEvalCoord2xvOES (const GLfixed *coords); + GLAPI void APIENTRY glFeedbackBufferxOES (GLsizei n, GLenum type, const GLfixed *buffer); + GLAPI void APIENTRY glGetConvolutionParameterxvOES (GLenum target, GLenum pname, GLfixed *params); + GLAPI void APIENTRY glGetHistogramParameterxvOES (GLenum target, GLenum pname, GLfixed *params); + GLAPI void APIENTRY glGetLightxOES (GLenum light, GLenum pname, GLfixed *params); + GLAPI void APIENTRY glGetMapxvOES (GLenum target, GLenum query, GLfixed *v); + GLAPI void APIENTRY glGetMaterialxOES (GLenum face, GLenum pname, GLfixed param); + GLAPI void APIENTRY glGetPixelMapxv (GLenum map, GLint size, GLfixed *values); + GLAPI void APIENTRY glGetTexGenxvOES (GLenum coord, GLenum pname, GLfixed *params); + GLAPI void APIENTRY glGetTexLevelParameterxvOES (GLenum target, GLint level, GLenum pname, GLfixed *params); + GLAPI void APIENTRY glIndexxOES (GLfixed component); + GLAPI void APIENTRY glIndexxvOES (const GLfixed *component); + GLAPI void APIENTRY glLoadTransposeMatrixxOES (const GLfixed *m); + GLAPI void APIENTRY glMap1xOES (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); + GLAPI void APIENTRY glMap2xOES (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); + GLAPI void APIENTRY glMapGrid1xOES (GLint n, GLfixed u1, GLfixed u2); + GLAPI void APIENTRY glMapGrid2xOES (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); + GLAPI void APIENTRY glMultTransposeMatrixxOES (const GLfixed *m); + GLAPI void APIENTRY glMultiTexCoord1xOES (GLenum texture, GLfixed s); + GLAPI void APIENTRY glMultiTexCoord1xvOES (GLenum texture, const GLfixed *coords); + GLAPI void APIENTRY glMultiTexCoord2xOES (GLenum texture, GLfixed s, GLfixed t); + GLAPI void APIENTRY glMultiTexCoord2xvOES (GLenum texture, const GLfixed *coords); + GLAPI void APIENTRY glMultiTexCoord3xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r); + GLAPI void APIENTRY glMultiTexCoord3xvOES (GLenum texture, const GLfixed *coords); + GLAPI void APIENTRY glMultiTexCoord4xvOES (GLenum texture, const GLfixed *coords); + GLAPI void APIENTRY glNormal3xvOES (const GLfixed *coords); + GLAPI void APIENTRY glPassThroughxOES (GLfixed token); + GLAPI void APIENTRY glPixelMapx (GLenum map, GLint size, const GLfixed *values); + GLAPI void APIENTRY glPixelStorex (GLenum pname, GLfixed param); + GLAPI void APIENTRY glPixelTransferxOES (GLenum pname, GLfixed param); + GLAPI void APIENTRY glPixelZoomxOES (GLfixed xfactor, GLfixed yfactor); + GLAPI void APIENTRY glPrioritizeTexturesxOES (GLsizei n, const GLuint *textures, const GLfixed *priorities); + GLAPI void APIENTRY glRasterPos2xOES (GLfixed x, GLfixed y); + GLAPI void APIENTRY glRasterPos2xvOES (const GLfixed *coords); + GLAPI void APIENTRY glRasterPos3xOES (GLfixed x, GLfixed y, GLfixed z); + GLAPI void APIENTRY glRasterPos3xvOES (const GLfixed *coords); + GLAPI void APIENTRY glRasterPos4xOES (GLfixed x, GLfixed y, GLfixed z, GLfixed w); + GLAPI void APIENTRY glRasterPos4xvOES (const GLfixed *coords); + GLAPI void APIENTRY glRectxOES (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); + GLAPI void APIENTRY glRectxvOES (const GLfixed *v1, const GLfixed *v2); + GLAPI void APIENTRY glTexCoord1xOES (GLfixed s); + GLAPI void APIENTRY glTexCoord1xvOES (const GLfixed *coords); + GLAPI void APIENTRY glTexCoord2xOES (GLfixed s, GLfixed t); + GLAPI void APIENTRY glTexCoord2xvOES (const GLfixed *coords); + GLAPI void APIENTRY glTexCoord3xOES (GLfixed s, GLfixed t, GLfixed r); + GLAPI void APIENTRY glTexCoord3xvOES (const GLfixed *coords); + GLAPI void APIENTRY glTexCoord4xOES (GLfixed s, GLfixed t, GLfixed r, GLfixed q); + GLAPI void APIENTRY glTexCoord4xvOES (const GLfixed *coords); + GLAPI void APIENTRY glTexGenxOES (GLenum coord, GLenum pname, GLfixed param); + GLAPI void APIENTRY glTexGenxvOES (GLenum coord, GLenum pname, const GLfixed *params); + GLAPI void APIENTRY glVertex2xOES (GLfixed x); + GLAPI void APIENTRY glVertex2xvOES (const GLfixed *coords); + GLAPI void APIENTRY glVertex3xOES (GLfixed x, GLfixed y); + GLAPI void APIENTRY glVertex3xvOES (const GLfixed *coords); + GLAPI void APIENTRY glVertex4xOES (GLfixed x, GLfixed y, GLfixed z); + GLAPI void APIENTRY glVertex4xvOES (const GLfixed *coords); +#endif +#endif /* GL_OES_fixed_point */ + +#ifndef GL_OES_query_matrix +#define GL_OES_query_matrix 1 + typedef GLbitfield (APIENTRYP PFNGLQUERYMATRIXXOESPROC) (GLfixed *mantissa, GLint *exponent); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLbitfield APIENTRY glQueryMatrixxOES (GLfixed *mantissa, GLint *exponent); +#endif +#endif /* GL_OES_query_matrix */ + +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif /* GL_OES_read_format */ + +#ifndef GL_OES_single_precision +#define GL_OES_single_precision 1 + typedef void (APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); + typedef void (APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); + typedef void (APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f); + typedef void (APIENTRYP PFNGLFRUSTUMFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); + typedef void (APIENTRYP PFNGLGETCLIPPLANEFOESPROC) (GLenum plane, GLfloat *equation); + typedef void (APIENTRYP PFNGLORTHOFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glClearDepthfOES (GLclampf depth); + GLAPI void APIENTRY glClipPlanefOES (GLenum plane, const GLfloat *equation); + GLAPI void APIENTRY glDepthRangefOES (GLclampf n, GLclampf f); + GLAPI void APIENTRY glFrustumfOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); + GLAPI void APIENTRY glGetClipPlanefOES (GLenum plane, GLfloat *equation); + GLAPI void APIENTRY glOrthofOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#endif +#endif /* GL_OES_single_precision */ + +#ifndef GL_3DFX_multisample +#define GL_3DFX_multisample 1 +#define GL_MULTISAMPLE_3DFX 0x86B2 +#define GL_SAMPLE_BUFFERS_3DFX 0x86B3 +#define GL_SAMPLES_3DFX 0x86B4 +#define GL_MULTISAMPLE_BIT_3DFX 0x20000000 +#endif /* GL_3DFX_multisample */ + +#ifndef GL_3DFX_tbuffer +#define GL_3DFX_tbuffer 1 + typedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC) (GLuint mask); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTbufferMask3DFX (GLuint mask); +#endif +#endif /* GL_3DFX_tbuffer */ + +#ifndef GL_3DFX_texture_compression_FXT1 +#define GL_3DFX_texture_compression_FXT1 1 +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 +#endif /* GL_3DFX_texture_compression_FXT1 */ + +#ifndef GL_AMD_blend_minmax_factor +#define GL_AMD_blend_minmax_factor 1 +#define GL_FACTOR_MIN_AMD 0x901C +#define GL_FACTOR_MAX_AMD 0x901D +#endif /* GL_AMD_blend_minmax_factor */ + +#ifndef GL_AMD_conservative_depth +#define GL_AMD_conservative_depth 1 +#endif /* GL_AMD_conservative_depth */ + +#ifndef GL_AMD_debug_output +#define GL_AMD_debug_output 1 + typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +#define GL_MAX_DEBUG_MESSAGE_LENGTH_AMD 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147 +#define GL_DEBUG_SEVERITY_LOW_AMD 0x9148 +#define GL_DEBUG_CATEGORY_API_ERROR_AMD 0x9149 +#define GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD 0x914A +#define GL_DEBUG_CATEGORY_DEPRECATION_AMD 0x914B +#define GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD 0x914C +#define GL_DEBUG_CATEGORY_PERFORMANCE_AMD 0x914D +#define GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD 0x914E +#define GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914F +#define GL_DEBUG_CATEGORY_OTHER_AMD 0x9150 + typedef void (APIENTRYP PFNGLDEBUGMESSAGEENABLEAMDPROC) (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTAMDPROC) (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); + typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKAMDPROC) (GLDEBUGPROCAMD callback, void *userParam); + typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC) (GLuint count, GLsizei bufSize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDebugMessageEnableAMD (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + GLAPI void APIENTRY glDebugMessageInsertAMD (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); + GLAPI void APIENTRY glDebugMessageCallbackAMD (GLDEBUGPROCAMD callback, void *userParam); + GLAPI GLuint APIENTRY glGetDebugMessageLogAMD (GLuint count, GLsizei bufSize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#endif +#endif /* GL_AMD_debug_output */ + +#ifndef GL_AMD_depth_clamp_separate +#define GL_AMD_depth_clamp_separate 1 +#define GL_DEPTH_CLAMP_NEAR_AMD 0x901E +#define GL_DEPTH_CLAMP_FAR_AMD 0x901F +#endif /* GL_AMD_depth_clamp_separate */ + +#ifndef GL_AMD_draw_buffers_blend +#define GL_AMD_draw_buffers_blend 1 + typedef void (APIENTRYP PFNGLBLENDFUNCINDEXEDAMDPROC) (GLuint buf, GLenum src, GLenum dst); + typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + typedef void (APIENTRYP PFNGLBLENDEQUATIONINDEXEDAMDPROC) (GLuint buf, GLenum mode); + typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendFuncIndexedAMD (GLuint buf, GLenum src, GLenum dst); + GLAPI void APIENTRY glBlendFuncSeparateIndexedAMD (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + GLAPI void APIENTRY glBlendEquationIndexedAMD (GLuint buf, GLenum mode); + GLAPI void APIENTRY glBlendEquationSeparateIndexedAMD (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#endif +#endif /* GL_AMD_draw_buffers_blend */ + +#ifndef GL_AMD_framebuffer_multisample_advanced +#define GL_AMD_framebuffer_multisample_advanced 1 +#define GL_RENDERBUFFER_STORAGE_SAMPLES_AMD 0x91B2 +#define GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD 0x91B3 +#define GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD 0x91B4 +#define GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD 0x91B5 +#define GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B6 +#define GL_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B7 + typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glRenderbufferStorageMultisampleAdvancedAMD (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleAdvancedAMD (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_AMD_framebuffer_multisample_advanced */ + +#ifndef GL_AMD_framebuffer_sample_positions +#define GL_AMD_framebuffer_sample_positions 1 +#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F +#define GL_PIXELS_PER_SAMPLE_PATTERN_X_AMD 0x91AE +#define GL_PIXELS_PER_SAMPLE_PATTERN_Y_AMD 0x91AF +#define GL_ALL_PIXELS_AMD 0xFFFFFFFF + typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC) (GLenum target, GLuint numsamples, GLuint pixelindex, const GLfloat *values); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC) (GLuint framebuffer, GLuint numsamples, GLuint pixelindex, const GLfloat *values); + typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERFVAMDPROC) (GLenum target, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values); + typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERFVAMDPROC) (GLuint framebuffer, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferSamplePositionsfvAMD (GLenum target, GLuint numsamples, GLuint pixelindex, const GLfloat *values); + GLAPI void APIENTRY glNamedFramebufferSamplePositionsfvAMD (GLuint framebuffer, GLuint numsamples, GLuint pixelindex, const GLfloat *values); + GLAPI void APIENTRY glGetFramebufferParameterfvAMD (GLenum target, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values); + GLAPI void APIENTRY glGetNamedFramebufferParameterfvAMD (GLuint framebuffer, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values); +#endif +#endif /* GL_AMD_framebuffer_sample_positions */ + +#ifndef GL_AMD_gcn_shader +#define GL_AMD_gcn_shader 1 +#endif /* GL_AMD_gcn_shader */ + +#ifndef GL_AMD_gpu_shader_half_float +#define GL_AMD_gpu_shader_half_float 1 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB +#define GL_FLOAT16_MAT2_AMD 0x91C5 +#define GL_FLOAT16_MAT3_AMD 0x91C6 +#define GL_FLOAT16_MAT4_AMD 0x91C7 +#define GL_FLOAT16_MAT2x3_AMD 0x91C8 +#define GL_FLOAT16_MAT2x4_AMD 0x91C9 +#define GL_FLOAT16_MAT3x2_AMD 0x91CA +#define GL_FLOAT16_MAT3x4_AMD 0x91CB +#define GL_FLOAT16_MAT4x2_AMD 0x91CC +#define GL_FLOAT16_MAT4x3_AMD 0x91CD +#endif /* GL_AMD_gpu_shader_half_float */ + +#ifndef GL_AMD_gpu_shader_int16 +#define GL_AMD_gpu_shader_int16 1 +#endif /* GL_AMD_gpu_shader_int16 */ + +#ifndef GL_AMD_gpu_shader_int64 +#define GL_AMD_gpu_shader_int64 1 + typedef khronos_int64_t GLint64EXT; +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +#define GL_INT8_NV 0x8FE0 +#define GL_INT8_VEC2_NV 0x8FE1 +#define GL_INT8_VEC3_NV 0x8FE2 +#define GL_INT8_VEC4_NV 0x8FE3 +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 +#define GL_INT64_VEC2_NV 0x8FE9 +#define GL_INT64_VEC3_NV 0x8FEA +#define GL_INT64_VEC4_NV 0x8FEB +#define GL_UNSIGNED_INT8_NV 0x8FEC +#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED +#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE +#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF +#define GL_UNSIGNED_INT16_NV 0x8FF0 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 +#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 + typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x); + typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y); + typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); + typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x); + typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y); + typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params); + typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glUniform1i64NV (GLint location, GLint64EXT x); + GLAPI void APIENTRY glUniform2i64NV (GLint location, GLint64EXT x, GLint64EXT y); + GLAPI void APIENTRY glUniform3i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); + GLAPI void APIENTRY glUniform4i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + GLAPI void APIENTRY glUniform1i64vNV (GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glUniform2i64vNV (GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glUniform3i64vNV (GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glUniform4i64vNV (GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glUniform1ui64NV (GLint location, GLuint64EXT x); + GLAPI void APIENTRY glUniform2ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y); + GLAPI void APIENTRY glUniform3ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + GLAPI void APIENTRY glUniform4ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + GLAPI void APIENTRY glUniform1ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glUniform2ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glUniform3ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glUniform4ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glGetUniformi64vNV (GLuint program, GLint location, GLint64EXT *params); + GLAPI void APIENTRY glGetUniformui64vNV (GLuint program, GLint location, GLuint64EXT *params); + GLAPI void APIENTRY glProgramUniform1i64NV (GLuint program, GLint location, GLint64EXT x); + GLAPI void APIENTRY glProgramUniform2i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); + GLAPI void APIENTRY glProgramUniform3i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); + GLAPI void APIENTRY glProgramUniform4i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + GLAPI void APIENTRY glProgramUniform1i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glProgramUniform2i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glProgramUniform3i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glProgramUniform4i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); + GLAPI void APIENTRY glProgramUniform1ui64NV (GLuint program, GLint location, GLuint64EXT x); + GLAPI void APIENTRY glProgramUniform2ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); + GLAPI void APIENTRY glProgramUniform3ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + GLAPI void APIENTRY glProgramUniform4ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + GLAPI void APIENTRY glProgramUniform1ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glProgramUniform2ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glProgramUniform3ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glProgramUniform4ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_AMD_gpu_shader_int64 */ + +#ifndef GL_AMD_interleaved_elements +#define GL_AMD_interleaved_elements 1 +#define GL_VERTEX_ELEMENT_SWIZZLE_AMD 0x91A4 +#define GL_VERTEX_ID_SWIZZLE_AMD 0x91A5 + typedef void (APIENTRYP PFNGLVERTEXATTRIBPARAMETERIAMDPROC) (GLuint index, GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexAttribParameteriAMD (GLuint index, GLenum pname, GLint param); +#endif +#endif /* GL_AMD_interleaved_elements */ + +#ifndef GL_AMD_multi_draw_indirect +#define GL_AMD_multi_draw_indirect 1 + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC) (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMultiDrawArraysIndirectAMD (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); + GLAPI void APIENTRY glMultiDrawElementsIndirectAMD (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); +#endif +#endif /* GL_AMD_multi_draw_indirect */ + +#ifndef GL_AMD_name_gen_delete +#define GL_AMD_name_gen_delete 1 +#define GL_DATA_BUFFER_AMD 0x9151 +#define GL_PERFORMANCE_MONITOR_AMD 0x9152 +#define GL_QUERY_OBJECT_AMD 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_AMD 0x9154 +#define GL_SAMPLER_OBJECT_AMD 0x9155 + typedef void (APIENTRYP PFNGLGENNAMESAMDPROC) (GLenum identifier, GLuint num, GLuint *names); + typedef void (APIENTRYP PFNGLDELETENAMESAMDPROC) (GLenum identifier, GLuint num, const GLuint *names); + typedef GLboolean (APIENTRYP PFNGLISNAMEAMDPROC) (GLenum identifier, GLuint name); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGenNamesAMD (GLenum identifier, GLuint num, GLuint *names); + GLAPI void APIENTRY glDeleteNamesAMD (GLenum identifier, GLuint num, const GLuint *names); + GLAPI GLboolean APIENTRY glIsNameAMD (GLenum identifier, GLuint name); +#endif +#endif /* GL_AMD_name_gen_delete */ + +#ifndef GL_AMD_occlusion_query_event +#define GL_AMD_occlusion_query_event 1 +#define GL_OCCLUSION_QUERY_EVENT_MASK_AMD 0x874F +#define GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD 0x00000001 +#define GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD 0x00000002 +#define GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD 0x00000004 +#define GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD 0x00000008 +#define GL_QUERY_ALL_EVENT_BITS_AMD 0xFFFFFFFF + typedef void (APIENTRYP PFNGLQUERYOBJECTPARAMETERUIAMDPROC) (GLenum target, GLuint id, GLenum pname, GLuint param); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glQueryObjectParameteruiAMD (GLenum target, GLuint id, GLenum pname, GLuint param); +#endif +#endif /* GL_AMD_occlusion_query_event */ + +#ifndef GL_AMD_performance_monitor +#define GL_AMD_performance_monitor 1 +#define GL_COUNTER_TYPE_AMD 0x8BC0 +#define GL_COUNTER_RANGE_AMD 0x8BC1 +#define GL_UNSIGNED_INT64_AMD 0x8BC2 +#define GL_PERCENTAGE_AMD 0x8BC3 +#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 +#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 +#define GL_PERFMON_RESULT_AMD 0x8BC6 + typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); + typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); + typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); + typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); + typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data); + typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); + typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); + typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); + typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); + typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); + typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); + GLAPI void APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); + GLAPI void APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); + GLAPI void APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); + GLAPI void APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data); + GLAPI void APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); + GLAPI void APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); + GLAPI void APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); + GLAPI void APIENTRY glBeginPerfMonitorAMD (GLuint monitor); + GLAPI void APIENTRY glEndPerfMonitorAMD (GLuint monitor); + GLAPI void APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif +#endif /* GL_AMD_performance_monitor */ + +#ifndef GL_AMD_pinned_memory +#define GL_AMD_pinned_memory 1 +#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160 +#endif /* GL_AMD_pinned_memory */ + +#ifndef GL_AMD_query_buffer_object +#define GL_AMD_query_buffer_object 1 +#define GL_QUERY_BUFFER_AMD 0x9192 +#define GL_QUERY_BUFFER_BINDING_AMD 0x9193 +#define GL_QUERY_RESULT_NO_WAIT_AMD 0x9194 +#endif /* GL_AMD_query_buffer_object */ + +#ifndef GL_AMD_sample_positions +#define GL_AMD_sample_positions 1 + typedef void (APIENTRYP PFNGLSETMULTISAMPLEFVAMDPROC) (GLenum pname, GLuint index, const GLfloat *val); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSetMultisamplefvAMD (GLenum pname, GLuint index, const GLfloat *val); +#endif +#endif /* GL_AMD_sample_positions */ + +#ifndef GL_AMD_seamless_cubemap_per_texture +#define GL_AMD_seamless_cubemap_per_texture 1 +#endif /* GL_AMD_seamless_cubemap_per_texture */ + +#ifndef GL_AMD_shader_atomic_counter_ops +#define GL_AMD_shader_atomic_counter_ops 1 +#endif /* GL_AMD_shader_atomic_counter_ops */ + +#ifndef GL_AMD_shader_ballot +#define GL_AMD_shader_ballot 1 +#endif /* GL_AMD_shader_ballot */ + +#ifndef GL_AMD_shader_explicit_vertex_parameter +#define GL_AMD_shader_explicit_vertex_parameter 1 +#endif /* GL_AMD_shader_explicit_vertex_parameter */ + +#ifndef GL_AMD_shader_gpu_shader_half_float_fetch +#define GL_AMD_shader_gpu_shader_half_float_fetch 1 +#endif /* GL_AMD_shader_gpu_shader_half_float_fetch */ + +#ifndef GL_AMD_shader_image_load_store_lod +#define GL_AMD_shader_image_load_store_lod 1 +#endif /* GL_AMD_shader_image_load_store_lod */ + +#ifndef GL_AMD_shader_stencil_export +#define GL_AMD_shader_stencil_export 1 +#endif /* GL_AMD_shader_stencil_export */ + +#ifndef GL_AMD_shader_trinary_minmax +#define GL_AMD_shader_trinary_minmax 1 +#endif /* GL_AMD_shader_trinary_minmax */ + +#ifndef GL_AMD_sparse_texture +#define GL_AMD_sparse_texture 1 +#define GL_VIRTUAL_PAGE_SIZE_X_AMD 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_AMD 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_AMD 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_AMD 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS 0x919A +#define GL_MIN_SPARSE_LEVEL_AMD 0x919B +#define GL_MIN_LOD_WARNING_AMD 0x919C +#define GL_TEXTURE_STORAGE_SPARSE_BIT_AMD 0x00000001 + typedef void (APIENTRYP PFNGLTEXSTORAGESPARSEAMDPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); + typedef void (APIENTRYP PFNGLTEXTURESTORAGESPARSEAMDPROC) (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexStorageSparseAMD (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); + GLAPI void APIENTRY glTextureStorageSparseAMD (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#endif +#endif /* GL_AMD_sparse_texture */ + +#ifndef GL_AMD_stencil_operation_extended +#define GL_AMD_stencil_operation_extended 1 +#define GL_SET_AMD 0x874A +#define GL_REPLACE_VALUE_AMD 0x874B +#define GL_STENCIL_OP_VALUE_AMD 0x874C +#define GL_STENCIL_BACK_OP_VALUE_AMD 0x874D + typedef void (APIENTRYP PFNGLSTENCILOPVALUEAMDPROC) (GLenum face, GLuint value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glStencilOpValueAMD (GLenum face, GLuint value); +#endif +#endif /* GL_AMD_stencil_operation_extended */ + +#ifndef GL_AMD_texture_gather_bias_lod +#define GL_AMD_texture_gather_bias_lod 1 +#endif /* GL_AMD_texture_gather_bias_lod */ + +#ifndef GL_AMD_texture_texture4 +#define GL_AMD_texture_texture4 1 +#endif /* GL_AMD_texture_texture4 */ + +#ifndef GL_AMD_transform_feedback3_lines_triangles +#define GL_AMD_transform_feedback3_lines_triangles 1 +#endif /* GL_AMD_transform_feedback3_lines_triangles */ + +#ifndef GL_AMD_transform_feedback4 +#define GL_AMD_transform_feedback4 1 +#define GL_STREAM_RASTERIZATION_AMD 0x91A0 +#endif /* GL_AMD_transform_feedback4 */ + +#ifndef GL_AMD_vertex_shader_layer +#define GL_AMD_vertex_shader_layer 1 +#endif /* GL_AMD_vertex_shader_layer */ + +#ifndef GL_AMD_vertex_shader_tessellator +#define GL_AMD_vertex_shader_tessellator 1 +#define GL_SAMPLER_BUFFER_AMD 0x9001 +#define GL_INT_SAMPLER_BUFFER_AMD 0x9002 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003 +#define GL_TESSELLATION_MODE_AMD 0x9004 +#define GL_TESSELLATION_FACTOR_AMD 0x9005 +#define GL_DISCRETE_AMD 0x9006 +#define GL_CONTINUOUS_AMD 0x9007 + typedef void (APIENTRYP PFNGLTESSELLATIONFACTORAMDPROC) (GLfloat factor); + typedef void (APIENTRYP PFNGLTESSELLATIONMODEAMDPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTessellationFactorAMD (GLfloat factor); + GLAPI void APIENTRY glTessellationModeAMD (GLenum mode); +#endif +#endif /* GL_AMD_vertex_shader_tessellator */ + +#ifndef GL_AMD_vertex_shader_viewport_index +#define GL_AMD_vertex_shader_viewport_index 1 +#endif /* GL_AMD_vertex_shader_viewport_index */ + +#ifndef GL_APPLE_aux_depth_stencil +#define GL_APPLE_aux_depth_stencil 1 +#define GL_AUX_DEPTH_STENCIL_APPLE 0x8A14 +#endif /* GL_APPLE_aux_depth_stencil */ + +#ifndef GL_APPLE_client_storage +#define GL_APPLE_client_storage 1 +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 +#endif /* GL_APPLE_client_storage */ + +#ifndef GL_APPLE_element_array +#define GL_APPLE_element_array 1 +#define GL_ELEMENT_ARRAY_APPLE 0x8A0C +#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D +#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E + typedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC) (GLenum type, const void *pointer); + typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, GLint first, GLsizei count); + typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); + typedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glElementPointerAPPLE (GLenum type, const void *pointer); + GLAPI void APIENTRY glDrawElementArrayAPPLE (GLenum mode, GLint first, GLsizei count); + GLAPI void APIENTRY glDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); + GLAPI void APIENTRY glMultiDrawElementArrayAPPLE (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); + GLAPI void APIENTRY glMultiDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#endif +#endif /* GL_APPLE_element_array */ + +#ifndef GL_APPLE_fence +#define GL_APPLE_fence 1 +#define GL_DRAW_PIXELS_APPLE 0x8A0A +#define GL_FENCE_APPLE 0x8A0B + typedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC) (GLsizei n, GLuint *fences); + typedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC) (GLsizei n, const GLuint *fences); + typedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC) (GLuint fence); + typedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC) (GLuint fence); + typedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC) (GLuint fence); + typedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC) (GLuint fence); + typedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC) (GLenum object, GLuint name); + typedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC) (GLenum object, GLint name); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGenFencesAPPLE (GLsizei n, GLuint *fences); + GLAPI void APIENTRY glDeleteFencesAPPLE (GLsizei n, const GLuint *fences); + GLAPI void APIENTRY glSetFenceAPPLE (GLuint fence); + GLAPI GLboolean APIENTRY glIsFenceAPPLE (GLuint fence); + GLAPI GLboolean APIENTRY glTestFenceAPPLE (GLuint fence); + GLAPI void APIENTRY glFinishFenceAPPLE (GLuint fence); + GLAPI GLboolean APIENTRY glTestObjectAPPLE (GLenum object, GLuint name); + GLAPI void APIENTRY glFinishObjectAPPLE (GLenum object, GLint name); +#endif +#endif /* GL_APPLE_fence */ + +#ifndef GL_APPLE_float_pixels +#define GL_APPLE_float_pixels 1 +#define GL_HALF_APPLE 0x140B +#define GL_RGBA_FLOAT32_APPLE 0x8814 +#define GL_RGB_FLOAT32_APPLE 0x8815 +#define GL_ALPHA_FLOAT32_APPLE 0x8816 +#define GL_INTENSITY_FLOAT32_APPLE 0x8817 +#define GL_LUMINANCE_FLOAT32_APPLE 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819 +#define GL_RGBA_FLOAT16_APPLE 0x881A +#define GL_RGB_FLOAT16_APPLE 0x881B +#define GL_ALPHA_FLOAT16_APPLE 0x881C +#define GL_INTENSITY_FLOAT16_APPLE 0x881D +#define GL_LUMINANCE_FLOAT16_APPLE 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F +#define GL_COLOR_FLOAT_APPLE 0x8A0F +#endif /* GL_APPLE_float_pixels */ + +#ifndef GL_APPLE_flush_buffer_range +#define GL_APPLE_flush_buffer_range 1 +#define GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12 +#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13 + typedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC) (GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC) (GLenum target, GLintptr offset, GLsizeiptr size); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBufferParameteriAPPLE (GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glFlushMappedBufferRangeAPPLE (GLenum target, GLintptr offset, GLsizeiptr size); +#endif +#endif /* GL_APPLE_flush_buffer_range */ + +#ifndef GL_APPLE_object_purgeable +#define GL_APPLE_object_purgeable 1 +#define GL_BUFFER_OBJECT_APPLE 0x85B3 +#define GL_RELEASED_APPLE 0x8A19 +#define GL_VOLATILE_APPLE 0x8A1A +#define GL_RETAINED_APPLE 0x8A1B +#define GL_UNDEFINED_APPLE 0x8A1C +#define GL_PURGEABLE_APPLE 0x8A1D + typedef GLenum (APIENTRYP PFNGLOBJECTPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); + typedef GLenum (APIENTRYP PFNGLOBJECTUNPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); + typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVAPPLEPROC) (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLenum APIENTRY glObjectPurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); + GLAPI GLenum APIENTRY glObjectUnpurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); + GLAPI void APIENTRY glGetObjectParameterivAPPLE (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#endif +#endif /* GL_APPLE_object_purgeable */ + +#ifndef GL_APPLE_rgb_422 +#define GL_APPLE_rgb_422 1 +#define GL_RGB_422_APPLE 0x8A1F +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#define GL_RGB_RAW_422_APPLE 0x8A51 +#endif /* GL_APPLE_rgb_422 */ + +#ifndef GL_APPLE_row_bytes +#define GL_APPLE_row_bytes 1 +#define GL_PACK_ROW_BYTES_APPLE 0x8A15 +#define GL_UNPACK_ROW_BYTES_APPLE 0x8A16 +#endif /* GL_APPLE_row_bytes */ + +#ifndef GL_APPLE_specular_vector +#define GL_APPLE_specular_vector 1 +#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0 +#endif /* GL_APPLE_specular_vector */ + +#ifndef GL_APPLE_texture_range +#define GL_APPLE_texture_range 1 +#define GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7 +#define GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8 +#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC +#define GL_STORAGE_PRIVATE_APPLE 0x85BD +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF + typedef void (APIENTRYP PFNGLTEXTURERANGEAPPLEPROC) (GLenum target, GLsizei length, const void *pointer); + typedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTextureRangeAPPLE (GLenum target, GLsizei length, const void *pointer); + GLAPI void APIENTRY glGetTexParameterPointervAPPLE (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_APPLE_texture_range */ + +#ifndef GL_APPLE_transform_hint +#define GL_APPLE_transform_hint 1 +#define GL_TRANSFORM_HINT_APPLE 0x85B1 +#endif /* GL_APPLE_transform_hint */ + +#ifndef GL_APPLE_vertex_array_object +#define GL_APPLE_vertex_array_object 1 +#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5 + typedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC) (GLuint array); + typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays); + typedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC) (GLsizei n, GLuint *arrays); + typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBindVertexArrayAPPLE (GLuint array); + GLAPI void APIENTRY glDeleteVertexArraysAPPLE (GLsizei n, const GLuint *arrays); + GLAPI void APIENTRY glGenVertexArraysAPPLE (GLsizei n, GLuint *arrays); + GLAPI GLboolean APIENTRY glIsVertexArrayAPPLE (GLuint array); +#endif +#endif /* GL_APPLE_vertex_array_object */ + +#ifndef GL_APPLE_vertex_array_range +#define GL_APPLE_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E +#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F +#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 +#define GL_STORAGE_CLIENT_APPLE 0x85B4 + typedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); + typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); + typedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexArrayRangeAPPLE (GLsizei length, void *pointer); + GLAPI void APIENTRY glFlushVertexArrayRangeAPPLE (GLsizei length, void *pointer); + GLAPI void APIENTRY glVertexArrayParameteriAPPLE (GLenum pname, GLint param); +#endif +#endif /* GL_APPLE_vertex_array_range */ + +#ifndef GL_APPLE_vertex_program_evaluators +#define GL_APPLE_vertex_program_evaluators 1 +#define GL_VERTEX_ATTRIB_MAP1_APPLE 0x8A00 +#define GL_VERTEX_ATTRIB_MAP2_APPLE 0x8A01 +#define GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE 0x8A02 +#define GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE 0x8A03 +#define GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE 0x8A04 +#define GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE 0x8A05 +#define GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE 0x8A06 +#define GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE 0x8A07 +#define GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE 0x8A08 +#define GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE 0x8A09 + typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); + typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); + typedef GLboolean (APIENTRYP PFNGLISVERTEXATTRIBENABLEDAPPLEPROC) (GLuint index, GLenum pname); + typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); + typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); + typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); + typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glEnableVertexAttribAPPLE (GLuint index, GLenum pname); + GLAPI void APIENTRY glDisableVertexAttribAPPLE (GLuint index, GLenum pname); + GLAPI GLboolean APIENTRY glIsVertexAttribEnabledAPPLE (GLuint index, GLenum pname); + GLAPI void APIENTRY glMapVertexAttrib1dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); + GLAPI void APIENTRY glMapVertexAttrib1fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); + GLAPI void APIENTRY glMapVertexAttrib2dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); + GLAPI void APIENTRY glMapVertexAttrib2fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#endif +#endif /* GL_APPLE_vertex_program_evaluators */ + +#ifndef GL_APPLE_ycbcr_422 +#define GL_APPLE_ycbcr_422 1 +#define GL_YCBCR_422_APPLE 0x85B9 +#endif /* GL_APPLE_ycbcr_422 */ + +#ifndef GL_ATI_draw_buffers +#define GL_ATI_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ATI 0x8824 +#define GL_DRAW_BUFFER0_ATI 0x8825 +#define GL_DRAW_BUFFER1_ATI 0x8826 +#define GL_DRAW_BUFFER2_ATI 0x8827 +#define GL_DRAW_BUFFER3_ATI 0x8828 +#define GL_DRAW_BUFFER4_ATI 0x8829 +#define GL_DRAW_BUFFER5_ATI 0x882A +#define GL_DRAW_BUFFER6_ATI 0x882B +#define GL_DRAW_BUFFER7_ATI 0x882C +#define GL_DRAW_BUFFER8_ATI 0x882D +#define GL_DRAW_BUFFER9_ATI 0x882E +#define GL_DRAW_BUFFER10_ATI 0x882F +#define GL_DRAW_BUFFER11_ATI 0x8830 +#define GL_DRAW_BUFFER12_ATI 0x8831 +#define GL_DRAW_BUFFER13_ATI 0x8832 +#define GL_DRAW_BUFFER14_ATI 0x8833 +#define GL_DRAW_BUFFER15_ATI 0x8834 + typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC) (GLsizei n, const GLenum *bufs); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawBuffersATI (GLsizei n, const GLenum *bufs); +#endif +#endif /* GL_ATI_draw_buffers */ + +#ifndef GL_ATI_element_array +#define GL_ATI_element_array 1 +#define GL_ELEMENT_ARRAY_ATI 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A + typedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC) (GLenum type, const void *pointer); + typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC) (GLenum mode, GLsizei count); + typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glElementPointerATI (GLenum type, const void *pointer); + GLAPI void APIENTRY glDrawElementArrayATI (GLenum mode, GLsizei count); + GLAPI void APIENTRY glDrawRangeElementArrayATI (GLenum mode, GLuint start, GLuint end, GLsizei count); +#endif +#endif /* GL_ATI_element_array */ + +#ifndef GL_ATI_envmap_bumpmap +#define GL_ATI_envmap_bumpmap 1 +#define GL_BUMP_ROT_MATRIX_ATI 0x8775 +#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776 +#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777 +#define GL_BUMP_TEX_UNITS_ATI 0x8778 +#define GL_DUDV_ATI 0x8779 +#define GL_DU8DV8_ATI 0x877A +#define GL_BUMP_ENVMAP_ATI 0x877B +#define GL_BUMP_TARGET_ATI 0x877C + typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC) (GLenum pname, const GLint *param); + typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC) (GLenum pname, const GLfloat *param); + typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC) (GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC) (GLenum pname, GLfloat *param); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexBumpParameterivATI (GLenum pname, const GLint *param); + GLAPI void APIENTRY glTexBumpParameterfvATI (GLenum pname, const GLfloat *param); + GLAPI void APIENTRY glGetTexBumpParameterivATI (GLenum pname, GLint *param); + GLAPI void APIENTRY glGetTexBumpParameterfvATI (GLenum pname, GLfloat *param); +#endif +#endif /* GL_ATI_envmap_bumpmap */ + +#ifndef GL_ATI_fragment_shader +#define GL_ATI_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ATI 0x8920 +#define GL_REG_0_ATI 0x8921 +#define GL_REG_1_ATI 0x8922 +#define GL_REG_2_ATI 0x8923 +#define GL_REG_3_ATI 0x8924 +#define GL_REG_4_ATI 0x8925 +#define GL_REG_5_ATI 0x8926 +#define GL_REG_6_ATI 0x8927 +#define GL_REG_7_ATI 0x8928 +#define GL_REG_8_ATI 0x8929 +#define GL_REG_9_ATI 0x892A +#define GL_REG_10_ATI 0x892B +#define GL_REG_11_ATI 0x892C +#define GL_REG_12_ATI 0x892D +#define GL_REG_13_ATI 0x892E +#define GL_REG_14_ATI 0x892F +#define GL_REG_15_ATI 0x8930 +#define GL_REG_16_ATI 0x8931 +#define GL_REG_17_ATI 0x8932 +#define GL_REG_18_ATI 0x8933 +#define GL_REG_19_ATI 0x8934 +#define GL_REG_20_ATI 0x8935 +#define GL_REG_21_ATI 0x8936 +#define GL_REG_22_ATI 0x8937 +#define GL_REG_23_ATI 0x8938 +#define GL_REG_24_ATI 0x8939 +#define GL_REG_25_ATI 0x893A +#define GL_REG_26_ATI 0x893B +#define GL_REG_27_ATI 0x893C +#define GL_REG_28_ATI 0x893D +#define GL_REG_29_ATI 0x893E +#define GL_REG_30_ATI 0x893F +#define GL_REG_31_ATI 0x8940 +#define GL_CON_0_ATI 0x8941 +#define GL_CON_1_ATI 0x8942 +#define GL_CON_2_ATI 0x8943 +#define GL_CON_3_ATI 0x8944 +#define GL_CON_4_ATI 0x8945 +#define GL_CON_5_ATI 0x8946 +#define GL_CON_6_ATI 0x8947 +#define GL_CON_7_ATI 0x8948 +#define GL_CON_8_ATI 0x8949 +#define GL_CON_9_ATI 0x894A +#define GL_CON_10_ATI 0x894B +#define GL_CON_11_ATI 0x894C +#define GL_CON_12_ATI 0x894D +#define GL_CON_13_ATI 0x894E +#define GL_CON_14_ATI 0x894F +#define GL_CON_15_ATI 0x8950 +#define GL_CON_16_ATI 0x8951 +#define GL_CON_17_ATI 0x8952 +#define GL_CON_18_ATI 0x8953 +#define GL_CON_19_ATI 0x8954 +#define GL_CON_20_ATI 0x8955 +#define GL_CON_21_ATI 0x8956 +#define GL_CON_22_ATI 0x8957 +#define GL_CON_23_ATI 0x8958 +#define GL_CON_24_ATI 0x8959 +#define GL_CON_25_ATI 0x895A +#define GL_CON_26_ATI 0x895B +#define GL_CON_27_ATI 0x895C +#define GL_CON_28_ATI 0x895D +#define GL_CON_29_ATI 0x895E +#define GL_CON_30_ATI 0x895F +#define GL_CON_31_ATI 0x8960 +#define GL_MOV_ATI 0x8961 +#define GL_ADD_ATI 0x8963 +#define GL_MUL_ATI 0x8964 +#define GL_SUB_ATI 0x8965 +#define GL_DOT3_ATI 0x8966 +#define GL_DOT4_ATI 0x8967 +#define GL_MAD_ATI 0x8968 +#define GL_LERP_ATI 0x8969 +#define GL_CND_ATI 0x896A +#define GL_CND0_ATI 0x896B +#define GL_DOT2_ADD_ATI 0x896C +#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D +#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E +#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F +#define GL_NUM_PASSES_ATI 0x8970 +#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971 +#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972 +#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973 +#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974 +#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975 +#define GL_SWIZZLE_STR_ATI 0x8976 +#define GL_SWIZZLE_STQ_ATI 0x8977 +#define GL_SWIZZLE_STR_DR_ATI 0x8978 +#define GL_SWIZZLE_STQ_DQ_ATI 0x8979 +#define GL_SWIZZLE_STRQ_ATI 0x897A +#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B +#define GL_RED_BIT_ATI 0x00000001 +#define GL_GREEN_BIT_ATI 0x00000002 +#define GL_BLUE_BIT_ATI 0x00000004 +#define GL_2X_BIT_ATI 0x00000001 +#define GL_4X_BIT_ATI 0x00000002 +#define GL_8X_BIT_ATI 0x00000004 +#define GL_HALF_BIT_ATI 0x00000008 +#define GL_QUARTER_BIT_ATI 0x00000010 +#define GL_EIGHTH_BIT_ATI 0x00000020 +#define GL_SATURATE_BIT_ATI 0x00000040 +#define GL_COMP_BIT_ATI 0x00000002 +#define GL_NEGATE_BIT_ATI 0x00000004 +#define GL_BIAS_BIT_ATI 0x00000008 + typedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC) (GLuint range); + typedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC) (GLuint id); + typedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC) (GLuint id); + typedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC) (void); + typedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC) (void); + typedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC) (GLuint dst, GLuint coord, GLenum swizzle); + typedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC) (GLuint dst, GLuint interp, GLenum swizzle); + typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); + typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); + typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); + typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); + typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); + typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); + typedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC) (GLuint dst, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLuint APIENTRY glGenFragmentShadersATI (GLuint range); + GLAPI void APIENTRY glBindFragmentShaderATI (GLuint id); + GLAPI void APIENTRY glDeleteFragmentShaderATI (GLuint id); + GLAPI void APIENTRY glBeginFragmentShaderATI (void); + GLAPI void APIENTRY glEndFragmentShaderATI (void); + GLAPI void APIENTRY glPassTexCoordATI (GLuint dst, GLuint coord, GLenum swizzle); + GLAPI void APIENTRY glSampleMapATI (GLuint dst, GLuint interp, GLenum swizzle); + GLAPI void APIENTRY glColorFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); + GLAPI void APIENTRY glColorFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); + GLAPI void APIENTRY glColorFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); + GLAPI void APIENTRY glAlphaFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); + GLAPI void APIENTRY glAlphaFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); + GLAPI void APIENTRY glAlphaFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); + GLAPI void APIENTRY glSetFragmentShaderConstantATI (GLuint dst, const GLfloat *value); +#endif +#endif /* GL_ATI_fragment_shader */ + +#ifndef GL_ATI_map_object_buffer +#define GL_ATI_map_object_buffer 1 + typedef void *(APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC) (GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void *APIENTRY glMapObjectBufferATI (GLuint buffer); + GLAPI void APIENTRY glUnmapObjectBufferATI (GLuint buffer); +#endif +#endif /* GL_ATI_map_object_buffer */ + +#ifndef GL_ATI_meminfo +#define GL_ATI_meminfo 1 +#define GL_VBO_FREE_MEMORY_ATI 0x87FB +#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC +#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD +#endif /* GL_ATI_meminfo */ + +#ifndef GL_ATI_pixel_format_float +#define GL_ATI_pixel_format_float 1 +#define GL_RGBA_FLOAT_MODE_ATI 0x8820 +#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 +#endif /* GL_ATI_pixel_format_float */ + +#ifndef GL_ATI_pn_triangles +#define GL_ATI_pn_triangles 1 +#define GL_PN_TRIANGLES_ATI 0x87F0 +#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 +#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 +#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 +#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 +#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 +#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 +#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 +#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 + typedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC) (GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC) (GLenum pname, GLfloat param); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPNTrianglesiATI (GLenum pname, GLint param); + GLAPI void APIENTRY glPNTrianglesfATI (GLenum pname, GLfloat param); +#endif +#endif /* GL_ATI_pn_triangles */ + +#ifndef GL_ATI_separate_stencil +#define GL_ATI_separate_stencil 1 +#define GL_STENCIL_BACK_FUNC_ATI 0x8800 +#define GL_STENCIL_BACK_FAIL_ATI 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803 + typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); + typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glStencilOpSeparateATI (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); + GLAPI void APIENTRY glStencilFuncSeparateATI (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#endif +#endif /* GL_ATI_separate_stencil */ + +#ifndef GL_ATI_text_fragment_shader +#define GL_ATI_text_fragment_shader 1 +#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 +#endif /* GL_ATI_text_fragment_shader */ + +#ifndef GL_ATI_texture_env_combine3 +#define GL_ATI_texture_env_combine3 1 +#define GL_MODULATE_ADD_ATI 0x8744 +#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 +#define GL_MODULATE_SUBTRACT_ATI 0x8746 +#endif /* GL_ATI_texture_env_combine3 */ + +#ifndef GL_ATI_texture_float +#define GL_ATI_texture_float 1 +#define GL_RGBA_FLOAT32_ATI 0x8814 +#define GL_RGB_FLOAT32_ATI 0x8815 +#define GL_ALPHA_FLOAT32_ATI 0x8816 +#define GL_INTENSITY_FLOAT32_ATI 0x8817 +#define GL_LUMINANCE_FLOAT32_ATI 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819 +#define GL_RGBA_FLOAT16_ATI 0x881A +#define GL_RGB_FLOAT16_ATI 0x881B +#define GL_ALPHA_FLOAT16_ATI 0x881C +#define GL_INTENSITY_FLOAT16_ATI 0x881D +#define GL_LUMINANCE_FLOAT16_ATI 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F +#endif /* GL_ATI_texture_float */ + +#ifndef GL_ATI_texture_mirror_once +#define GL_ATI_texture_mirror_once 1 +#define GL_MIRROR_CLAMP_ATI 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743 +#endif /* GL_ATI_texture_mirror_once */ + +#ifndef GL_ATI_vertex_array_object +#define GL_ATI_vertex_array_object 1 +#define GL_STATIC_ATI 0x8760 +#define GL_DYNAMIC_ATI 0x8761 +#define GL_PRESERVE_ATI 0x8762 +#define GL_DISCARD_ATI 0x8763 +#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764 +#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765 +#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766 +#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767 + typedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC) (GLsizei size, const void *pointer, GLenum usage); + typedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC) (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); + typedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC) (GLuint buffer, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC) (GLuint buffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC) (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); + typedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC) (GLenum array, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC) (GLenum array, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC) (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); + typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC) (GLuint id, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC) (GLuint id, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLuint APIENTRY glNewObjectBufferATI (GLsizei size, const void *pointer, GLenum usage); + GLAPI GLboolean APIENTRY glIsObjectBufferATI (GLuint buffer); + GLAPI void APIENTRY glUpdateObjectBufferATI (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); + GLAPI void APIENTRY glGetObjectBufferfvATI (GLuint buffer, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetObjectBufferivATI (GLuint buffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glFreeObjectBufferATI (GLuint buffer); + GLAPI void APIENTRY glArrayObjectATI (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); + GLAPI void APIENTRY glGetArrayObjectfvATI (GLenum array, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetArrayObjectivATI (GLenum array, GLenum pname, GLint *params); + GLAPI void APIENTRY glVariantArrayObjectATI (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); + GLAPI void APIENTRY glGetVariantArrayObjectfvATI (GLuint id, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetVariantArrayObjectivATI (GLuint id, GLenum pname, GLint *params); +#endif +#endif /* GL_ATI_vertex_array_object */ + +#ifndef GL_ATI_vertex_attrib_array_object +#define GL_ATI_vertex_attrib_array_object 1 + typedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC) (GLuint index, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC) (GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexAttribArrayObjectATI (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); + GLAPI void APIENTRY glGetVertexAttribArrayObjectfvATI (GLuint index, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetVertexAttribArrayObjectivATI (GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_ATI_vertex_attrib_array_object */ + +#ifndef GL_ATI_vertex_streams +#define GL_ATI_vertex_streams 1 +#define GL_MAX_VERTEX_STREAMS_ATI 0x876B +#define GL_VERTEX_STREAM0_ATI 0x876C +#define GL_VERTEX_STREAM1_ATI 0x876D +#define GL_VERTEX_STREAM2_ATI 0x876E +#define GL_VERTEX_STREAM3_ATI 0x876F +#define GL_VERTEX_STREAM4_ATI 0x8770 +#define GL_VERTEX_STREAM5_ATI 0x8771 +#define GL_VERTEX_STREAM6_ATI 0x8772 +#define GL_VERTEX_STREAM7_ATI 0x8773 +#define GL_VERTEX_SOURCE_ATI 0x8774 + typedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC) (GLenum stream, GLshort x); + typedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC) (GLenum stream, const GLshort *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC) (GLenum stream, GLint x); + typedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC) (GLenum stream, const GLint *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC) (GLenum stream, GLfloat x); + typedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC) (GLenum stream, const GLfloat *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC) (GLenum stream, GLdouble x); + typedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC) (GLenum stream, const GLdouble *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC) (GLenum stream, GLshort x, GLshort y); + typedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC) (GLenum stream, const GLshort *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC) (GLenum stream, GLint x, GLint y); + typedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC) (GLenum stream, const GLint *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC) (GLenum stream, GLfloat x, GLfloat y); + typedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC) (GLenum stream, const GLfloat *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC) (GLenum stream, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC) (GLenum stream, const GLdouble *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z); + typedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC) (GLenum stream, GLint x, GLint y, GLint z); + typedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); + typedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC) (GLenum stream, const GLshort *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC) (GLenum stream, GLint x, GLint y, GLint z, GLint w); + typedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC) (GLenum stream, const GLint *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC) (GLenum stream, const GLfloat *coords); + typedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC) (GLenum stream, const GLdouble *coords); + typedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC) (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); + typedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC) (GLenum stream, const GLbyte *coords); + typedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC) (GLenum stream, GLshort nx, GLshort ny, GLshort nz); + typedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); + typedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC) (GLenum stream, GLint nx, GLint ny, GLint nz); + typedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); + typedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC) (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); + typedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); + typedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC) (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); + typedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); + typedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC) (GLenum stream); + typedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC) (GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC) (GLenum pname, GLfloat param); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexStream1sATI (GLenum stream, GLshort x); + GLAPI void APIENTRY glVertexStream1svATI (GLenum stream, const GLshort *coords); + GLAPI void APIENTRY glVertexStream1iATI (GLenum stream, GLint x); + GLAPI void APIENTRY glVertexStream1ivATI (GLenum stream, const GLint *coords); + GLAPI void APIENTRY glVertexStream1fATI (GLenum stream, GLfloat x); + GLAPI void APIENTRY glVertexStream1fvATI (GLenum stream, const GLfloat *coords); + GLAPI void APIENTRY glVertexStream1dATI (GLenum stream, GLdouble x); + GLAPI void APIENTRY glVertexStream1dvATI (GLenum stream, const GLdouble *coords); + GLAPI void APIENTRY glVertexStream2sATI (GLenum stream, GLshort x, GLshort y); + GLAPI void APIENTRY glVertexStream2svATI (GLenum stream, const GLshort *coords); + GLAPI void APIENTRY glVertexStream2iATI (GLenum stream, GLint x, GLint y); + GLAPI void APIENTRY glVertexStream2ivATI (GLenum stream, const GLint *coords); + GLAPI void APIENTRY glVertexStream2fATI (GLenum stream, GLfloat x, GLfloat y); + GLAPI void APIENTRY glVertexStream2fvATI (GLenum stream, const GLfloat *coords); + GLAPI void APIENTRY glVertexStream2dATI (GLenum stream, GLdouble x, GLdouble y); + GLAPI void APIENTRY glVertexStream2dvATI (GLenum stream, const GLdouble *coords); + GLAPI void APIENTRY glVertexStream3sATI (GLenum stream, GLshort x, GLshort y, GLshort z); + GLAPI void APIENTRY glVertexStream3svATI (GLenum stream, const GLshort *coords); + GLAPI void APIENTRY glVertexStream3iATI (GLenum stream, GLint x, GLint y, GLint z); + GLAPI void APIENTRY glVertexStream3ivATI (GLenum stream, const GLint *coords); + GLAPI void APIENTRY glVertexStream3fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glVertexStream3fvATI (GLenum stream, const GLfloat *coords); + GLAPI void APIENTRY glVertexStream3dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glVertexStream3dvATI (GLenum stream, const GLdouble *coords); + GLAPI void APIENTRY glVertexStream4sATI (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); + GLAPI void APIENTRY glVertexStream4svATI (GLenum stream, const GLshort *coords); + GLAPI void APIENTRY glVertexStream4iATI (GLenum stream, GLint x, GLint y, GLint z, GLint w); + GLAPI void APIENTRY glVertexStream4ivATI (GLenum stream, const GLint *coords); + GLAPI void APIENTRY glVertexStream4fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glVertexStream4fvATI (GLenum stream, const GLfloat *coords); + GLAPI void APIENTRY glVertexStream4dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glVertexStream4dvATI (GLenum stream, const GLdouble *coords); + GLAPI void APIENTRY glNormalStream3bATI (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); + GLAPI void APIENTRY glNormalStream3bvATI (GLenum stream, const GLbyte *coords); + GLAPI void APIENTRY glNormalStream3sATI (GLenum stream, GLshort nx, GLshort ny, GLshort nz); + GLAPI void APIENTRY glNormalStream3svATI (GLenum stream, const GLshort *coords); + GLAPI void APIENTRY glNormalStream3iATI (GLenum stream, GLint nx, GLint ny, GLint nz); + GLAPI void APIENTRY glNormalStream3ivATI (GLenum stream, const GLint *coords); + GLAPI void APIENTRY glNormalStream3fATI (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); + GLAPI void APIENTRY glNormalStream3fvATI (GLenum stream, const GLfloat *coords); + GLAPI void APIENTRY glNormalStream3dATI (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); + GLAPI void APIENTRY glNormalStream3dvATI (GLenum stream, const GLdouble *coords); + GLAPI void APIENTRY glClientActiveVertexStreamATI (GLenum stream); + GLAPI void APIENTRY glVertexBlendEnviATI (GLenum pname, GLint param); + GLAPI void APIENTRY glVertexBlendEnvfATI (GLenum pname, GLfloat param); +#endif +#endif /* GL_ATI_vertex_streams */ + +#ifndef GL_EXT_422_pixels +#define GL_EXT_422_pixels 1 +#define GL_422_EXT 0x80CC +#define GL_422_REV_EXT 0x80CD +#define GL_422_AVERAGE_EXT 0x80CE +#define GL_422_REV_AVERAGE_EXT 0x80CF +#endif /* GL_EXT_422_pixels */ + +#ifndef GL_EXT_EGL_image_storage +#define GL_EXT_EGL_image_storage 1 + typedef void *GLeglImageOES; + typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC) (GLenum target, GLeglImageOES image, const GLint* attrib_list); + typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC) (GLuint texture, GLeglImageOES image, const GLint* attrib_list); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glEGLImageTargetTexStorageEXT (GLenum target, GLeglImageOES image, const GLint* attrib_list); + GLAPI void APIENTRY glEGLImageTargetTextureStorageEXT (GLuint texture, GLeglImageOES image, const GLint* attrib_list); +#endif +#endif /* GL_EXT_EGL_image_storage */ + +#ifndef GL_EXT_EGL_sync +#define GL_EXT_EGL_sync 1 +#endif /* GL_EXT_EGL_sync */ + +#ifndef GL_EXT_abgr +#define GL_EXT_abgr 1 +#define GL_ABGR_EXT 0x8000 +#endif /* GL_EXT_abgr */ + +#ifndef GL_EXT_bgra +#define GL_EXT_bgra 1 +#define GL_BGR_EXT 0x80E0 +#define GL_BGRA_EXT 0x80E1 +#endif /* GL_EXT_bgra */ + +#ifndef GL_EXT_bindable_uniform +#define GL_EXT_bindable_uniform 1 +#define GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2 +#define GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3 +#define GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4 +#define GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED +#define GL_UNIFORM_BUFFER_EXT 0x8DEE +#define GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF + typedef void (APIENTRYP PFNGLUNIFORMBUFFEREXTPROC) (GLuint program, GLint location, GLuint buffer); + typedef GLint (APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC) (GLuint program, GLint location); + typedef GLintptr (APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC) (GLuint program, GLint location); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glUniformBufferEXT (GLuint program, GLint location, GLuint buffer); + GLAPI GLint APIENTRY glGetUniformBufferSizeEXT (GLuint program, GLint location); + GLAPI GLintptr APIENTRY glGetUniformOffsetEXT (GLuint program, GLint location); +#endif +#endif /* GL_EXT_bindable_uniform */ + +#ifndef GL_EXT_blend_color +#define GL_EXT_blend_color 1 +#define GL_CONSTANT_COLOR_EXT 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 +#define GL_CONSTANT_ALPHA_EXT 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 +#define GL_BLEND_COLOR_EXT 0x8005 + typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendColorEXT (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#endif +#endif /* GL_EXT_blend_color */ + +#ifndef GL_EXT_blend_equation_separate +#define GL_EXT_blend_equation_separate 1 +#define GL_BLEND_EQUATION_RGB_EXT 0x8009 +#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D + typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC) (GLenum modeRGB, GLenum modeAlpha); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendEquationSeparateEXT (GLenum modeRGB, GLenum modeAlpha); +#endif +#endif /* GL_EXT_blend_equation_separate */ + +#ifndef GL_EXT_blend_func_separate +#define GL_EXT_blend_func_separate 1 +#define GL_BLEND_DST_RGB_EXT 0x80C8 +#define GL_BLEND_SRC_RGB_EXT 0x80C9 +#define GL_BLEND_DST_ALPHA_EXT 0x80CA +#define GL_BLEND_SRC_ALPHA_EXT 0x80CB + typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendFuncSeparateEXT (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif +#endif /* GL_EXT_blend_func_separate */ + +#ifndef GL_EXT_blend_logic_op +#define GL_EXT_blend_logic_op 1 +#endif /* GL_EXT_blend_logic_op */ + +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#define GL_FUNC_ADD_EXT 0x8006 +#define GL_BLEND_EQUATION_EXT 0x8009 + typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendEquationEXT (GLenum mode); +#endif +#endif /* GL_EXT_blend_minmax */ + +#ifndef GL_EXT_blend_subtract +#define GL_EXT_blend_subtract 1 +#define GL_FUNC_SUBTRACT_EXT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B +#endif /* GL_EXT_blend_subtract */ + +#ifndef GL_EXT_clip_volume_hint +#define GL_EXT_clip_volume_hint 1 +#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0 +#endif /* GL_EXT_clip_volume_hint */ + +#ifndef GL_EXT_cmyka +#define GL_EXT_cmyka 1 +#define GL_CMYK_EXT 0x800C +#define GL_CMYKA_EXT 0x800D +#define GL_PACK_CMYK_HINT_EXT 0x800E +#define GL_UNPACK_CMYK_HINT_EXT 0x800F +#endif /* GL_EXT_cmyka */ + +#ifndef GL_EXT_color_subtable +#define GL_EXT_color_subtable 1 + typedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glColorSubTableEXT (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glCopyColorSubTableEXT (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#endif +#endif /* GL_EXT_color_subtable */ + +#ifndef GL_EXT_compiled_vertex_array +#define GL_EXT_compiled_vertex_array 1 +#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8 +#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9 + typedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count); + typedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glLockArraysEXT (GLint first, GLsizei count); + GLAPI void APIENTRY glUnlockArraysEXT (void); +#endif +#endif /* GL_EXT_compiled_vertex_array */ + +#ifndef GL_EXT_convolution +#define GL_EXT_convolution 1 +#define GL_CONVOLUTION_1D_EXT 0x8010 +#define GL_CONVOLUTION_2D_EXT 0x8011 +#define GL_SEPARABLE_2D_EXT 0x8012 +#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015 +#define GL_REDUCE_EXT 0x8016 +#define GL_CONVOLUTION_FORMAT_EXT 0x8017 +#define GL_CONVOLUTION_WIDTH_EXT 0x8018 +#define GL_CONVOLUTION_HEIGHT_EXT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023 + typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); + typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); + typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat params); + typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint params); + typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *image); + typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); + typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); + GLAPI void APIENTRY glConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); + GLAPI void APIENTRY glConvolutionParameterfEXT (GLenum target, GLenum pname, GLfloat params); + GLAPI void APIENTRY glConvolutionParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glConvolutionParameteriEXT (GLenum target, GLenum pname, GLint params); + GLAPI void APIENTRY glConvolutionParameterivEXT (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glCopyConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glCopyConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetConvolutionFilterEXT (GLenum target, GLenum format, GLenum type, void *image); + GLAPI void APIENTRY glGetConvolutionParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetConvolutionParameterivEXT (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetSeparableFilterEXT (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); + GLAPI void APIENTRY glSeparableFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +#endif +#endif /* GL_EXT_convolution */ + +#ifndef GL_EXT_coordinate_frame +#define GL_EXT_coordinate_frame 1 +#define GL_TANGENT_ARRAY_EXT 0x8439 +#define GL_BINORMAL_ARRAY_EXT 0x843A +#define GL_CURRENT_TANGENT_EXT 0x843B +#define GL_CURRENT_BINORMAL_EXT 0x843C +#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E +#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F +#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440 +#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441 +#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442 +#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443 +#define GL_MAP1_TANGENT_EXT 0x8444 +#define GL_MAP2_TANGENT_EXT 0x8445 +#define GL_MAP1_BINORMAL_EXT 0x8446 +#define GL_MAP2_BINORMAL_EXT 0x8447 + typedef void (APIENTRYP PFNGLTANGENT3BEXTPROC) (GLbyte tx, GLbyte ty, GLbyte tz); + typedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC) (const GLbyte *v); + typedef void (APIENTRYP PFNGLTANGENT3DEXTPROC) (GLdouble tx, GLdouble ty, GLdouble tz); + typedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLTANGENT3FEXTPROC) (GLfloat tx, GLfloat ty, GLfloat tz); + typedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLTANGENT3IEXTPROC) (GLint tx, GLint ty, GLint tz); + typedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLTANGENT3SEXTPROC) (GLshort tx, GLshort ty, GLshort tz); + typedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC) (const GLshort *v); + typedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC) (GLbyte bx, GLbyte by, GLbyte bz); + typedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC) (const GLbyte *v); + typedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC) (GLdouble bx, GLdouble by, GLdouble bz); + typedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC) (GLfloat bx, GLfloat by, GLfloat bz); + typedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC) (GLint bx, GLint by, GLint bz); + typedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC) (GLshort bx, GLshort by, GLshort bz); + typedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC) (const GLshort *v); + typedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTangent3bEXT (GLbyte tx, GLbyte ty, GLbyte tz); + GLAPI void APIENTRY glTangent3bvEXT (const GLbyte *v); + GLAPI void APIENTRY glTangent3dEXT (GLdouble tx, GLdouble ty, GLdouble tz); + GLAPI void APIENTRY glTangent3dvEXT (const GLdouble *v); + GLAPI void APIENTRY glTangent3fEXT (GLfloat tx, GLfloat ty, GLfloat tz); + GLAPI void APIENTRY glTangent3fvEXT (const GLfloat *v); + GLAPI void APIENTRY glTangent3iEXT (GLint tx, GLint ty, GLint tz); + GLAPI void APIENTRY glTangent3ivEXT (const GLint *v); + GLAPI void APIENTRY glTangent3sEXT (GLshort tx, GLshort ty, GLshort tz); + GLAPI void APIENTRY glTangent3svEXT (const GLshort *v); + GLAPI void APIENTRY glBinormal3bEXT (GLbyte bx, GLbyte by, GLbyte bz); + GLAPI void APIENTRY glBinormal3bvEXT (const GLbyte *v); + GLAPI void APIENTRY glBinormal3dEXT (GLdouble bx, GLdouble by, GLdouble bz); + GLAPI void APIENTRY glBinormal3dvEXT (const GLdouble *v); + GLAPI void APIENTRY glBinormal3fEXT (GLfloat bx, GLfloat by, GLfloat bz); + GLAPI void APIENTRY glBinormal3fvEXT (const GLfloat *v); + GLAPI void APIENTRY glBinormal3iEXT (GLint bx, GLint by, GLint bz); + GLAPI void APIENTRY glBinormal3ivEXT (const GLint *v); + GLAPI void APIENTRY glBinormal3sEXT (GLshort bx, GLshort by, GLshort bz); + GLAPI void APIENTRY glBinormal3svEXT (const GLshort *v); + GLAPI void APIENTRY glTangentPointerEXT (GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glBinormalPointerEXT (GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_coordinate_frame */ + +#ifndef GL_EXT_copy_texture +#define GL_EXT_copy_texture 1 + typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCopyTexImage1DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + GLAPI void APIENTRY glCopyTexImage2DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + GLAPI void APIENTRY glCopyTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glCopyTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glCopyTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_EXT_copy_texture */ + +#ifndef GL_EXT_cull_vertex +#define GL_EXT_cull_vertex 1 +#define GL_CULL_VERTEX_EXT 0x81AA +#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB +#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC + typedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC) (GLenum pname, GLdouble *params); + typedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC) (GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCullParameterdvEXT (GLenum pname, GLdouble *params); + GLAPI void APIENTRY glCullParameterfvEXT (GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_cull_vertex */ + +#ifndef GL_EXT_debug_label +#define GL_EXT_debug_label 1 +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 + typedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label); + typedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glLabelObjectEXT (GLenum type, GLuint object, GLsizei length, const GLchar *label); + GLAPI void APIENTRY glGetObjectLabelEXT (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_EXT_debug_label */ + +#ifndef GL_EXT_debug_marker +#define GL_EXT_debug_marker 1 + typedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker); + typedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker); + typedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glInsertEventMarkerEXT (GLsizei length, const GLchar *marker); + GLAPI void APIENTRY glPushGroupMarkerEXT (GLsizei length, const GLchar *marker); + GLAPI void APIENTRY glPopGroupMarkerEXT (void); +#endif +#endif /* GL_EXT_debug_marker */ + +#ifndef GL_EXT_depth_bounds_test +#define GL_EXT_depth_bounds_test 1 +#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 +#define GL_DEPTH_BOUNDS_EXT 0x8891 + typedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC) (GLclampd zmin, GLclampd zmax); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDepthBoundsEXT (GLclampd zmin, GLclampd zmax); +#endif +#endif /* GL_EXT_depth_bounds_test */ + +#ifndef GL_EXT_direct_state_access +#define GL_EXT_direct_state_access 1 +#define GL_PROGRAM_MATRIX_EXT 0x8E2D +#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E +#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F + typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m); + typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m); + typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); + typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); + typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); + typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode); + typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode); + typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); + typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture); + typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); + typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); + typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); + typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); + typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); + typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); + typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data); + typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data); + typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, void **data); + typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); + typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); + typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); + typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); + typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, void *img); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); + typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, void *img); + typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); + typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); + typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); + typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access); + typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, void **params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); + typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); + typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params); + typedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); + typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); + typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params); + typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params); + typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, void *string); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); + typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target); + typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target); + typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); + typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); + typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); + typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); + typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); + typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); + typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); + typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC) (GLuint vaobj, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC) (GLuint vaobj, GLenum pname, void **param); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); + typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param); + typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); + typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); + typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); + typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); + typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); + typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC) (GLuint vaobj, GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMatrixLoadfEXT (GLenum mode, const GLfloat *m); + GLAPI void APIENTRY glMatrixLoaddEXT (GLenum mode, const GLdouble *m); + GLAPI void APIENTRY glMatrixMultfEXT (GLenum mode, const GLfloat *m); + GLAPI void APIENTRY glMatrixMultdEXT (GLenum mode, const GLdouble *m); + GLAPI void APIENTRY glMatrixLoadIdentityEXT (GLenum mode); + GLAPI void APIENTRY glMatrixRotatefEXT (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glMatrixRotatedEXT (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glMatrixScalefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glMatrixScaledEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glMatrixTranslatefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glMatrixTranslatedEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glMatrixFrustumEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); + GLAPI void APIENTRY glMatrixOrthoEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); + GLAPI void APIENTRY glMatrixPopEXT (GLenum mode); + GLAPI void APIENTRY glMatrixPushEXT (GLenum mode); + GLAPI void APIENTRY glClientAttribDefaultEXT (GLbitfield mask); + GLAPI void APIENTRY glPushClientAttribDefaultEXT (GLbitfield mask); + GLAPI void APIENTRY glTextureParameterfEXT (GLuint texture, GLenum target, GLenum pname, GLfloat param); + GLAPI void APIENTRY glTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glTextureParameteriEXT (GLuint texture, GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCopyTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + GLAPI void APIENTRY glCopyTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + GLAPI void APIENTRY glCopyTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glCopyTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetTextureImageEXT (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); + GLAPI void APIENTRY glGetTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTextureLevelParameterfvEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetTextureLevelParameterivEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); + GLAPI void APIENTRY glTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCopyTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glBindMultiTextureEXT (GLenum texunit, GLenum target, GLuint texture); + GLAPI void APIENTRY glMultiTexCoordPointerEXT (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glMultiTexEnvfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); + GLAPI void APIENTRY glMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glMultiTexEnviEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glMultiTexGendEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); + GLAPI void APIENTRY glMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); + GLAPI void APIENTRY glMultiTexGenfEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); + GLAPI void APIENTRY glMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glMultiTexGeniEXT (GLenum texunit, GLenum coord, GLenum pname, GLint param); + GLAPI void APIENTRY glMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); + GLAPI void APIENTRY glGetMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); + GLAPI void APIENTRY glGetMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, GLint *params); + GLAPI void APIENTRY glMultiTexParameteriEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glMultiTexParameterfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); + GLAPI void APIENTRY glMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCopyMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); + GLAPI void APIENTRY glCopyMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + GLAPI void APIENTRY glCopyMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glCopyMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetMultiTexImageEXT (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); + GLAPI void APIENTRY glGetMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetMultiTexLevelParameterfvEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMultiTexLevelParameterivEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); + GLAPI void APIENTRY glMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glCopyMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glEnableClientStateIndexedEXT (GLenum array, GLuint index); + GLAPI void APIENTRY glDisableClientStateIndexedEXT (GLenum array, GLuint index); + GLAPI void APIENTRY glGetFloatIndexedvEXT (GLenum target, GLuint index, GLfloat *data); + GLAPI void APIENTRY glGetDoubleIndexedvEXT (GLenum target, GLuint index, GLdouble *data); + GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, void **data); + GLAPI void APIENTRY glEnableIndexedEXT (GLenum target, GLuint index); + GLAPI void APIENTRY glDisableIndexedEXT (GLenum target, GLuint index); + GLAPI GLboolean APIENTRY glIsEnabledIndexedEXT (GLenum target, GLuint index); + GLAPI void APIENTRY glGetIntegerIndexedvEXT (GLenum target, GLuint index, GLint *data); + GLAPI void APIENTRY glGetBooleanIndexedvEXT (GLenum target, GLuint index, GLboolean *data); + GLAPI void APIENTRY glCompressedTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glGetCompressedTextureImageEXT (GLuint texture, GLenum target, GLint lod, void *img); + GLAPI void APIENTRY glCompressedMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glCompressedMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); + GLAPI void APIENTRY glGetCompressedMultiTexImageEXT (GLenum texunit, GLenum target, GLint lod, void *img); + GLAPI void APIENTRY glMatrixLoadTransposefEXT (GLenum mode, const GLfloat *m); + GLAPI void APIENTRY glMatrixLoadTransposedEXT (GLenum mode, const GLdouble *m); + GLAPI void APIENTRY glMatrixMultTransposefEXT (GLenum mode, const GLfloat *m); + GLAPI void APIENTRY glMatrixMultTransposedEXT (GLenum mode, const GLdouble *m); + GLAPI void APIENTRY glNamedBufferDataEXT (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); + GLAPI void APIENTRY glNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + GLAPI void *APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access); + GLAPI GLboolean APIENTRY glUnmapNamedBufferEXT (GLuint buffer); + GLAPI void APIENTRY glGetNamedBufferParameterivEXT (GLuint buffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, void **params); + GLAPI void APIENTRY glGetNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); + GLAPI void APIENTRY glProgramUniform1fEXT (GLuint program, GLint location, GLfloat v0); + GLAPI void APIENTRY glProgramUniform2fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1); + GLAPI void APIENTRY glProgramUniform3fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + GLAPI void APIENTRY glProgramUniform4fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + GLAPI void APIENTRY glProgramUniform1iEXT (GLuint program, GLint location, GLint v0); + GLAPI void APIENTRY glProgramUniform2iEXT (GLuint program, GLint location, GLint v0, GLint v1); + GLAPI void APIENTRY glProgramUniform3iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); + GLAPI void APIENTRY glProgramUniform4iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + GLAPI void APIENTRY glProgramUniform1fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform2fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform3fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform4fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); + GLAPI void APIENTRY glProgramUniform1ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform2ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform3ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniform4ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); + GLAPI void APIENTRY glProgramUniformMatrix2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix2x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix2x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix3x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glProgramUniformMatrix4x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + GLAPI void APIENTRY glTextureBufferEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); + GLAPI void APIENTRY glMultiTexBufferEXT (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); + GLAPI void APIENTRY glTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, const GLuint *params); + GLAPI void APIENTRY glGetTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, GLuint *params); + GLAPI void APIENTRY glMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); + GLAPI void APIENTRY glGetMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, GLuint *params); + GLAPI void APIENTRY glProgramUniform1uiEXT (GLuint program, GLint location, GLuint v0); + GLAPI void APIENTRY glProgramUniform2uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1); + GLAPI void APIENTRY glProgramUniform3uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); + GLAPI void APIENTRY glProgramUniform4uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + GLAPI void APIENTRY glProgramUniform1uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform2uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform3uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glProgramUniform4uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glNamedProgramLocalParameters4fvEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); + GLAPI void APIENTRY glNamedProgramLocalParameterI4iEXT (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); + GLAPI void APIENTRY glNamedProgramLocalParameterI4ivEXT (GLuint program, GLenum target, GLuint index, const GLint *params); + GLAPI void APIENTRY glNamedProgramLocalParametersI4ivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); + GLAPI void APIENTRY glNamedProgramLocalParameterI4uiEXT (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + GLAPI void APIENTRY glNamedProgramLocalParameterI4uivEXT (GLuint program, GLenum target, GLuint index, const GLuint *params); + GLAPI void APIENTRY glNamedProgramLocalParametersI4uivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); + GLAPI void APIENTRY glGetNamedProgramLocalParameterIivEXT (GLuint program, GLenum target, GLuint index, GLint *params); + GLAPI void APIENTRY glGetNamedProgramLocalParameterIuivEXT (GLuint program, GLenum target, GLuint index, GLuint *params); + GLAPI void APIENTRY glEnableClientStateiEXT (GLenum array, GLuint index); + GLAPI void APIENTRY glDisableClientStateiEXT (GLenum array, GLuint index); + GLAPI void APIENTRY glGetFloati_vEXT (GLenum pname, GLuint index, GLfloat *params); + GLAPI void APIENTRY glGetDoublei_vEXT (GLenum pname, GLuint index, GLdouble *params); + GLAPI void APIENTRY glGetPointeri_vEXT (GLenum pname, GLuint index, void **params); + GLAPI void APIENTRY glNamedProgramStringEXT (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); + GLAPI void APIENTRY glNamedProgramLocalParameter4dEXT (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glNamedProgramLocalParameter4dvEXT (GLuint program, GLenum target, GLuint index, const GLdouble *params); + GLAPI void APIENTRY glNamedProgramLocalParameter4fEXT (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glNamedProgramLocalParameter4fvEXT (GLuint program, GLenum target, GLuint index, const GLfloat *params); + GLAPI void APIENTRY glGetNamedProgramLocalParameterdvEXT (GLuint program, GLenum target, GLuint index, GLdouble *params); + GLAPI void APIENTRY glGetNamedProgramLocalParameterfvEXT (GLuint program, GLenum target, GLuint index, GLfloat *params); + GLAPI void APIENTRY glGetNamedProgramivEXT (GLuint program, GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetNamedProgramStringEXT (GLuint program, GLenum target, GLenum pname, void *string); + GLAPI void APIENTRY glNamedRenderbufferStorageEXT (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetNamedRenderbufferParameterivEXT (GLuint renderbuffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleEXT (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleCoverageEXT (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI GLenum APIENTRY glCheckNamedFramebufferStatusEXT (GLuint framebuffer, GLenum target); + GLAPI void APIENTRY glNamedFramebufferTexture1DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + GLAPI void APIENTRY glNamedFramebufferTexture2DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + GLAPI void APIENTRY glNamedFramebufferTexture3DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); + GLAPI void APIENTRY glNamedFramebufferRenderbufferEXT (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameterivEXT (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); + GLAPI void APIENTRY glGenerateTextureMipmapEXT (GLuint texture, GLenum target); + GLAPI void APIENTRY glGenerateMultiTexMipmapEXT (GLenum texunit, GLenum target); + GLAPI void APIENTRY glFramebufferDrawBufferEXT (GLuint framebuffer, GLenum mode); + GLAPI void APIENTRY glFramebufferDrawBuffersEXT (GLuint framebuffer, GLsizei n, const GLenum *bufs); + GLAPI void APIENTRY glFramebufferReadBufferEXT (GLuint framebuffer, GLenum mode); + GLAPI void APIENTRY glGetFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glNamedCopyBufferSubDataEXT (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + GLAPI void APIENTRY glNamedFramebufferTextureEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); + GLAPI void APIENTRY glNamedFramebufferTextureLayerEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); + GLAPI void APIENTRY glNamedFramebufferTextureFaceEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); + GLAPI void APIENTRY glTextureRenderbufferEXT (GLuint texture, GLenum target, GLuint renderbuffer); + GLAPI void APIENTRY glMultiTexRenderbufferEXT (GLenum texunit, GLenum target, GLuint renderbuffer); + GLAPI void APIENTRY glVertexArrayVertexOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayEdgeFlagOffsetEXT (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayIndexOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayNormalOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayMultiTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayFogCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArraySecondaryColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayVertexAttribOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glVertexArrayVertexAttribIOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glEnableVertexArrayEXT (GLuint vaobj, GLenum array); + GLAPI void APIENTRY glDisableVertexArrayEXT (GLuint vaobj, GLenum array); + GLAPI void APIENTRY glEnableVertexArrayAttribEXT (GLuint vaobj, GLuint index); + GLAPI void APIENTRY glDisableVertexArrayAttribEXT (GLuint vaobj, GLuint index); + GLAPI void APIENTRY glGetVertexArrayIntegervEXT (GLuint vaobj, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetVertexArrayPointervEXT (GLuint vaobj, GLenum pname, void **param); + GLAPI void APIENTRY glGetVertexArrayIntegeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, GLint *param); + GLAPI void APIENTRY glGetVertexArrayPointeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, void **param); + GLAPI void *APIENTRY glMapNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); + GLAPI void APIENTRY glFlushMappedNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length); + GLAPI void APIENTRY glNamedBufferStorageEXT (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); + GLAPI void APIENTRY glClearNamedBufferDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glClearNamedBufferSubDataEXT (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); + GLAPI void APIENTRY glNamedFramebufferParameteriEXT (GLuint framebuffer, GLenum pname, GLint param); + GLAPI void APIENTRY glGetNamedFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); + GLAPI void APIENTRY glProgramUniform1dEXT (GLuint program, GLint location, GLdouble x); + GLAPI void APIENTRY glProgramUniform2dEXT (GLuint program, GLint location, GLdouble x, GLdouble y); + GLAPI void APIENTRY glProgramUniform3dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glProgramUniform4dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glProgramUniform1dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform2dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform3dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniform4dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix2x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix2x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix3x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glProgramUniformMatrix4x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); + GLAPI void APIENTRY glTextureBufferRangeEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glTextureStorage1DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + GLAPI void APIENTRY glTextureStorage2DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTextureStorage3DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + GLAPI void APIENTRY glTextureStorage2DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glTextureStorage3DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + GLAPI void APIENTRY glVertexArrayBindVertexBufferEXT (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + GLAPI void APIENTRY glVertexArrayVertexAttribFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayVertexAttribIFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayVertexAttribLFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + GLAPI void APIENTRY glVertexArrayVertexAttribBindingEXT (GLuint vaobj, GLuint attribindex, GLuint bindingindex); + GLAPI void APIENTRY glVertexArrayVertexBindingDivisorEXT (GLuint vaobj, GLuint bindingindex, GLuint divisor); + GLAPI void APIENTRY glVertexArrayVertexAttribLOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); + GLAPI void APIENTRY glTexturePageCommitmentEXT (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); + GLAPI void APIENTRY glVertexArrayVertexAttribDivisorEXT (GLuint vaobj, GLuint index, GLuint divisor); +#endif +#endif /* GL_EXT_direct_state_access */ + +#ifndef GL_EXT_draw_buffers2 +#define GL_EXT_draw_buffers2 1 + typedef void (APIENTRYP PFNGLCOLORMASKINDEXEDEXTPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glColorMaskIndexedEXT (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#endif +#endif /* GL_EXT_draw_buffers2 */ + +#ifndef GL_EXT_draw_instanced +#define GL_EXT_draw_instanced 1 + typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); + typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawArraysInstancedEXT (GLenum mode, GLint start, GLsizei count, GLsizei primcount); + GLAPI void APIENTRY glDrawElementsInstancedEXT (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_EXT_draw_instanced */ + +#ifndef GL_EXT_draw_range_elements +#define GL_EXT_draw_range_elements 1 +#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8 +#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9 + typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawRangeElementsEXT (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +#endif +#endif /* GL_EXT_draw_range_elements */ + +#ifndef GL_EXT_external_buffer +#define GL_EXT_external_buffer 1 + typedef void *GLeglClientBufferEXT; + typedef void (APIENTRYP PFNGLBUFFERSTORAGEEXTERNALEXTPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); + typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBufferStorageExternalEXT (GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); + GLAPI void APIENTRY glNamedBufferStorageExternalEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +#endif +#endif /* GL_EXT_external_buffer */ + +#ifndef GL_EXT_fog_coord +#define GL_EXT_fog_coord 1 +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 + typedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC) (GLfloat coord); + typedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC) (const GLfloat *coord); + typedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC) (GLdouble coord); + typedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC) (const GLdouble *coord); + typedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFogCoordfEXT (GLfloat coord); + GLAPI void APIENTRY glFogCoordfvEXT (const GLfloat *coord); + GLAPI void APIENTRY glFogCoorddEXT (GLdouble coord); + GLAPI void APIENTRY glFogCoorddvEXT (const GLdouble *coord); + GLAPI void APIENTRY glFogCoordPointerEXT (GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_fog_coord */ + +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit 1 +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA + typedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlitFramebufferEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +#endif /* GL_EXT_framebuffer_blit */ + +#ifndef GL_EXT_framebuffer_multisample +#define GL_EXT_framebuffer_multisample 1 +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_MAX_SAMPLES_EXT 0x8D57 + typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glRenderbufferStorageMultisampleEXT (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_EXT_framebuffer_multisample */ + +#ifndef GL_EXT_framebuffer_multisample_blit_scaled +#define GL_EXT_framebuffer_multisample_blit_scaled 1 +#define GL_SCALED_RESOLVE_FASTEST_EXT 0x90BA +#define GL_SCALED_RESOLVE_NICEST_EXT 0x90BB +#endif /* GL_EXT_framebuffer_multisample_blit_scaled */ + +#ifndef GL_EXT_framebuffer_object +#define GL_EXT_framebuffer_object 1 +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 + typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); + typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); + typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); + typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); + typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); + typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); + typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); + typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); + typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); + typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLboolean APIENTRY glIsRenderbufferEXT (GLuint renderbuffer); + GLAPI void APIENTRY glBindRenderbufferEXT (GLenum target, GLuint renderbuffer); + GLAPI void APIENTRY glDeleteRenderbuffersEXT (GLsizei n, const GLuint *renderbuffers); + GLAPI void APIENTRY glGenRenderbuffersEXT (GLsizei n, GLuint *renderbuffers); + GLAPI void APIENTRY glRenderbufferStorageEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glGetRenderbufferParameterivEXT (GLenum target, GLenum pname, GLint *params); + GLAPI GLboolean APIENTRY glIsFramebufferEXT (GLuint framebuffer); + GLAPI void APIENTRY glBindFramebufferEXT (GLenum target, GLuint framebuffer); + GLAPI void APIENTRY glDeleteFramebuffersEXT (GLsizei n, const GLuint *framebuffers); + GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei n, GLuint *framebuffers); + GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum target); + GLAPI void APIENTRY glFramebufferTexture1DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + GLAPI void APIENTRY glFramebufferTexture3DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); + GLAPI void APIENTRY glFramebufferRenderbufferEXT (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + GLAPI void APIENTRY glGetFramebufferAttachmentParameterivEXT (GLenum target, GLenum attachment, GLenum pname, GLint *params); + GLAPI void APIENTRY glGenerateMipmapEXT (GLenum target); +#endif +#endif /* GL_EXT_framebuffer_object */ + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_EXT_framebuffer_sRGB 1 +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#endif /* GL_EXT_framebuffer_sRGB */ + +#ifndef GL_EXT_geometry_shader4 +#define GL_EXT_geometry_shader4 1 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE +#define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +#define GL_PROGRAM_POINT_SIZE_EXT 0x8642 + typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProgramParameteriEXT (GLuint program, GLenum pname, GLint value); +#endif +#endif /* GL_EXT_geometry_shader4 */ + +#ifndef GL_EXT_gpu_program_parameters +#define GL_EXT_gpu_program_parameters 1 + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProgramEnvParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); + GLAPI void APIENTRY glProgramLocalParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#endif +#endif /* GL_EXT_gpu_program_parameters */ + +#ifndef GL_EXT_gpu_shader4 +#define GL_EXT_gpu_shader4 1 +#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0 +#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 +#define GL_SAMPLER_BUFFER_EXT 0x8DC2 +#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5 +#define GL_UNSIGNED_INT_VEC2_EXT 0x8DC6 +#define GL_UNSIGNED_INT_VEC3_EXT 0x8DC7 +#define GL_UNSIGNED_INT_VEC4_EXT 0x8DC8 +#define GL_INT_SAMPLER_1D_EXT 0x8DC9 +#define GL_INT_SAMPLER_2D_EXT 0x8DCA +#define GL_INT_SAMPLER_3D_EXT 0x8DCB +#define GL_INT_SAMPLER_CUBE_EXT 0x8DCC +#define GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD +#define GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF +#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_EXT 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD + typedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC) (GLuint program, GLint location, GLuint *params); + typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC) (GLuint program, GLuint color, const GLchar *name); + typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC) (GLuint program, const GLchar *name); + typedef void (APIENTRYP PFNGLUNIFORM1UIEXTPROC) (GLint location, GLuint v0); + typedef void (APIENTRYP PFNGLUNIFORM2UIEXTPROC) (GLint location, GLuint v0, GLuint v1); + typedef void (APIENTRYP PFNGLUNIFORM3UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); + typedef void (APIENTRYP PFNGLUNIFORM4UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + typedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC) (GLuint index, GLint x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC) (GLuint index, GLint x, GLint y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC) (GLuint index, GLint x, GLint y, GLint z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC) (GLuint index, GLuint x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC) (GLuint index, GLuint x, GLuint y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC) (GLuint index, const GLint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC) (GLuint index, const GLuint *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC) (GLuint index, const GLbyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC) (GLuint index, const GLubyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC) (GLuint index, const GLushort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC) (GLuint index, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC) (GLuint index, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetUniformuivEXT (GLuint program, GLint location, GLuint *params); + GLAPI void APIENTRY glBindFragDataLocationEXT (GLuint program, GLuint color, const GLchar *name); + GLAPI GLint APIENTRY glGetFragDataLocationEXT (GLuint program, const GLchar *name); + GLAPI void APIENTRY glUniform1uiEXT (GLint location, GLuint v0); + GLAPI void APIENTRY glUniform2uiEXT (GLint location, GLuint v0, GLuint v1); + GLAPI void APIENTRY glUniform3uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2); + GLAPI void APIENTRY glUniform4uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); + GLAPI void APIENTRY glUniform1uivEXT (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glUniform2uivEXT (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glUniform3uivEXT (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glUniform4uivEXT (GLint location, GLsizei count, const GLuint *value); + GLAPI void APIENTRY glVertexAttribI1iEXT (GLuint index, GLint x); + GLAPI void APIENTRY glVertexAttribI2iEXT (GLuint index, GLint x, GLint y); + GLAPI void APIENTRY glVertexAttribI3iEXT (GLuint index, GLint x, GLint y, GLint z); + GLAPI void APIENTRY glVertexAttribI4iEXT (GLuint index, GLint x, GLint y, GLint z, GLint w); + GLAPI void APIENTRY glVertexAttribI1uiEXT (GLuint index, GLuint x); + GLAPI void APIENTRY glVertexAttribI2uiEXT (GLuint index, GLuint x, GLuint y); + GLAPI void APIENTRY glVertexAttribI3uiEXT (GLuint index, GLuint x, GLuint y, GLuint z); + GLAPI void APIENTRY glVertexAttribI4uiEXT (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + GLAPI void APIENTRY glVertexAttribI1ivEXT (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI2ivEXT (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI3ivEXT (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI4ivEXT (GLuint index, const GLint *v); + GLAPI void APIENTRY glVertexAttribI1uivEXT (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI2uivEXT (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI3uivEXT (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI4uivEXT (GLuint index, const GLuint *v); + GLAPI void APIENTRY glVertexAttribI4bvEXT (GLuint index, const GLbyte *v); + GLAPI void APIENTRY glVertexAttribI4svEXT (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttribI4ubvEXT (GLuint index, const GLubyte *v); + GLAPI void APIENTRY glVertexAttribI4usvEXT (GLuint index, const GLushort *v); + GLAPI void APIENTRY glVertexAttribIPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glGetVertexAttribIivEXT (GLuint index, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVertexAttribIuivEXT (GLuint index, GLenum pname, GLuint *params); +#endif +#endif /* GL_EXT_gpu_shader4 */ + +#ifndef GL_EXT_histogram +#define GL_EXT_histogram 1 +#define GL_HISTOGRAM_EXT 0x8024 +#define GL_PROXY_HISTOGRAM_EXT 0x8025 +#define GL_HISTOGRAM_WIDTH_EXT 0x8026 +#define GL_HISTOGRAM_FORMAT_EXT 0x8027 +#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C +#define GL_HISTOGRAM_SINK_EXT 0x802D +#define GL_MINMAX_EXT 0x802E +#define GL_MINMAX_FORMAT_EXT 0x802F +#define GL_MINMAX_SINK_EXT 0x8030 +#define GL_TABLE_TOO_LARGE_EXT 0x8031 + typedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); + typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETMINMAXEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); + typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); + typedef void (APIENTRYP PFNGLMINMAXEXTPROC) (GLenum target, GLenum internalformat, GLboolean sink); + typedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC) (GLenum target); + typedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetHistogramEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); + GLAPI void APIENTRY glGetHistogramParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetHistogramParameterivEXT (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetMinmaxEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); + GLAPI void APIENTRY glGetMinmaxParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMinmaxParameterivEXT (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glHistogramEXT (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); + GLAPI void APIENTRY glMinmaxEXT (GLenum target, GLenum internalformat, GLboolean sink); + GLAPI void APIENTRY glResetHistogramEXT (GLenum target); + GLAPI void APIENTRY glResetMinmaxEXT (GLenum target); +#endif +#endif /* GL_EXT_histogram */ + +#ifndef GL_EXT_index_array_formats +#define GL_EXT_index_array_formats 1 +#define GL_IUI_V2F_EXT 0x81AD +#define GL_IUI_V3F_EXT 0x81AE +#define GL_IUI_N3F_V2F_EXT 0x81AF +#define GL_IUI_N3F_V3F_EXT 0x81B0 +#define GL_T2F_IUI_V2F_EXT 0x81B1 +#define GL_T2F_IUI_V3F_EXT 0x81B2 +#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3 +#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4 +#endif /* GL_EXT_index_array_formats */ + +#ifndef GL_EXT_index_func +#define GL_EXT_index_func 1 +#define GL_INDEX_TEST_EXT 0x81B5 +#define GL_INDEX_TEST_FUNC_EXT 0x81B6 +#define GL_INDEX_TEST_REF_EXT 0x81B7 + typedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC) (GLenum func, GLclampf ref); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glIndexFuncEXT (GLenum func, GLclampf ref); +#endif +#endif /* GL_EXT_index_func */ + +#ifndef GL_EXT_index_material +#define GL_EXT_index_material 1 +#define GL_INDEX_MATERIAL_EXT 0x81B8 +#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9 +#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA + typedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC) (GLenum face, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glIndexMaterialEXT (GLenum face, GLenum mode); +#endif +#endif /* GL_EXT_index_material */ + +#ifndef GL_EXT_index_texture +#define GL_EXT_index_texture 1 +#endif /* GL_EXT_index_texture */ + +#ifndef GL_EXT_light_texture +#define GL_EXT_light_texture 1 +#define GL_FRAGMENT_MATERIAL_EXT 0x8349 +#define GL_FRAGMENT_NORMAL_EXT 0x834A +#define GL_FRAGMENT_COLOR_EXT 0x834C +#define GL_ATTENUATION_EXT 0x834D +#define GL_SHADOW_ATTENUATION_EXT 0x834E +#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F +#define GL_TEXTURE_LIGHT_EXT 0x8350 +#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351 +#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352 + typedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC) (GLenum mode); + typedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC) (GLenum pname); + typedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC) (GLenum face, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glApplyTextureEXT (GLenum mode); + GLAPI void APIENTRY glTextureLightEXT (GLenum pname); + GLAPI void APIENTRY glTextureMaterialEXT (GLenum face, GLenum mode); +#endif +#endif /* GL_EXT_light_texture */ + +#ifndef GL_EXT_memory_object +#define GL_EXT_memory_object 1 +#define GL_TEXTURE_TILING_EXT 0x9580 +#define GL_DEDICATED_MEMORY_OBJECT_EXT 0x9581 +#define GL_PROTECTED_MEMORY_OBJECT_EXT 0x959B +#define GL_NUM_TILING_TYPES_EXT 0x9582 +#define GL_TILING_TYPES_EXT 0x9583 +#define GL_OPTIMAL_TILING_EXT 0x9584 +#define GL_LINEAR_TILING_EXT 0x9585 +#define GL_NUM_DEVICE_UUIDS_EXT 0x9596 +#define GL_DEVICE_UUID_EXT 0x9597 +#define GL_DRIVER_UUID_EXT 0x9598 +#define GL_UUID_SIZE_EXT 16 + typedef void (APIENTRYP PFNGLGETUNSIGNEDBYTEVEXTPROC) (GLenum pname, GLubyte *data); + typedef void (APIENTRYP PFNGLGETUNSIGNEDBYTEI_VEXTPROC) (GLenum target, GLuint index, GLubyte *data); + typedef void (APIENTRYP PFNGLDELETEMEMORYOBJECTSEXTPROC) (GLsizei n, const GLuint *memoryObjects); + typedef GLboolean (APIENTRYP PFNGLISMEMORYOBJECTEXTPROC) (GLuint memoryObject); + typedef void (APIENTRYP PFNGLCREATEMEMORYOBJECTSEXTPROC) (GLsizei n, GLuint *memoryObjects); + typedef void (APIENTRYP PFNGLMEMORYOBJECTPARAMETERIVEXTPROC) (GLuint memoryObject, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC) (GLuint memoryObject, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLTEXSTORAGEMEM2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXSTORAGEMEM3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLBUFFERSTORAGEMEMEXTPROC) (GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM2DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC) (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM3DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC) (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC) (GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXSTORAGEMEM1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM1DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetUnsignedBytevEXT (GLenum pname, GLubyte *data); + GLAPI void APIENTRY glGetUnsignedBytei_vEXT (GLenum target, GLuint index, GLubyte *data); + GLAPI void APIENTRY glDeleteMemoryObjectsEXT (GLsizei n, const GLuint *memoryObjects); + GLAPI GLboolean APIENTRY glIsMemoryObjectEXT (GLuint memoryObject); + GLAPI void APIENTRY glCreateMemoryObjectsEXT (GLsizei n, GLuint *memoryObjects); + GLAPI void APIENTRY glMemoryObjectParameterivEXT (GLuint memoryObject, GLenum pname, const GLint *params); + GLAPI void APIENTRY glGetMemoryObjectParameterivEXT (GLuint memoryObject, GLenum pname, GLint *params); + GLAPI void APIENTRY glTexStorageMem2DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTexStorageMem2DMultisampleEXT (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTexStorageMem3DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTexStorageMem3DMultisampleEXT (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glBufferStorageMemEXT (GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTextureStorageMem2DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTextureStorageMem2DMultisampleEXT (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTextureStorageMem3DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTextureStorageMem3DMultisampleEXT (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glNamedBufferStorageMemEXT (GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTexStorageMem1DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTextureStorageMem1DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset); +#endif +#endif /* GL_EXT_memory_object */ + +#ifndef GL_EXT_memory_object_fd +#define GL_EXT_memory_object_fd 1 +#define GL_HANDLE_TYPE_OPAQUE_FD_EXT 0x9586 + typedef void (APIENTRYP PFNGLIMPORTMEMORYFDEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, GLint fd); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glImportMemoryFdEXT (GLuint memory, GLuint64 size, GLenum handleType, GLint fd); +#endif +#endif /* GL_EXT_memory_object_fd */ + +#ifndef GL_EXT_memory_object_win32 +#define GL_EXT_memory_object_win32 1 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_EXT 0x9587 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT 0x9588 +#define GL_DEVICE_LUID_EXT 0x9599 +#define GL_DEVICE_NODE_MASK_EXT 0x959A +#define GL_LUID_SIZE_EXT 8 +#define GL_HANDLE_TYPE_D3D12_TILEPOOL_EXT 0x9589 +#define GL_HANDLE_TYPE_D3D12_RESOURCE_EXT 0x958A +#define GL_HANDLE_TYPE_D3D11_IMAGE_EXT 0x958B +#define GL_HANDLE_TYPE_D3D11_IMAGE_KMT_EXT 0x958C + typedef void (APIENTRYP PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, void *handle); + typedef void (APIENTRYP PFNGLIMPORTMEMORYWIN32NAMEEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, const void *name); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glImportMemoryWin32HandleEXT (GLuint memory, GLuint64 size, GLenum handleType, void *handle); + GLAPI void APIENTRY glImportMemoryWin32NameEXT (GLuint memory, GLuint64 size, GLenum handleType, const void *name); +#endif +#endif /* GL_EXT_memory_object_win32 */ + +#ifndef GL_EXT_misc_attribute +#define GL_EXT_misc_attribute 1 +#endif /* GL_EXT_misc_attribute */ + +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMultiDrawArraysEXT (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); + GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); +#endif +#endif /* GL_EXT_multi_draw_arrays */ + +#ifndef GL_EXT_multisample +#define GL_EXT_multisample 1 +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_MASK_EXT 0x80A0 +#define GL_1PASS_EXT 0x80A1 +#define GL_2PASS_0_EXT 0x80A2 +#define GL_2PASS_1_EXT 0x80A3 +#define GL_4PASS_0_EXT 0x80A4 +#define GL_4PASS_1_EXT 0x80A5 +#define GL_4PASS_2_EXT 0x80A6 +#define GL_4PASS_3_EXT 0x80A7 +#define GL_SAMPLE_BUFFERS_EXT 0x80A8 +#define GL_SAMPLES_EXT 0x80A9 +#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA +#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB +#define GL_SAMPLE_PATTERN_EXT 0x80AC +#define GL_MULTISAMPLE_BIT_EXT 0x20000000 + typedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC) (GLclampf value, GLboolean invert); + typedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC) (GLenum pattern); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSampleMaskEXT (GLclampf value, GLboolean invert); + GLAPI void APIENTRY glSamplePatternEXT (GLenum pattern); +#endif +#endif /* GL_EXT_multisample */ + +#ifndef GL_EXT_multiview_tessellation_geometry_shader +#define GL_EXT_multiview_tessellation_geometry_shader 1 +#endif /* GL_EXT_multiview_tessellation_geometry_shader */ + +#ifndef GL_EXT_multiview_texture_multisample +#define GL_EXT_multiview_texture_multisample 1 +#endif /* GL_EXT_multiview_texture_multisample */ + +#ifndef GL_EXT_multiview_timer_query +#define GL_EXT_multiview_timer_query 1 +#endif /* GL_EXT_multiview_timer_query */ + +#ifndef GL_EXT_packed_depth_stencil +#define GL_EXT_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#define GL_UNSIGNED_INT_24_8_EXT 0x84FA +#define GL_DEPTH24_STENCIL8_EXT 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 +#endif /* GL_EXT_packed_depth_stencil */ + +#ifndef GL_EXT_packed_float +#define GL_EXT_packed_float 1 +#define GL_R11F_G11F_B10F_EXT 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B +#define GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C +#endif /* GL_EXT_packed_float */ + +#ifndef GL_EXT_packed_pixels +#define GL_EXT_packed_pixels 1 +#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 +#endif /* GL_EXT_packed_pixels */ + +#ifndef GL_EXT_paletted_texture +#define GL_EXT_paletted_texture 1 +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED + typedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); + typedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC) (GLenum target, GLenum format, GLenum type, void *data); + typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glColorTableEXT (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); + GLAPI void APIENTRY glGetColorTableEXT (GLenum target, GLenum format, GLenum type, void *data); + GLAPI void APIENTRY glGetColorTableParameterivEXT (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetColorTableParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_paletted_texture */ + +#ifndef GL_EXT_pixel_buffer_object +#define GL_EXT_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF +#endif /* GL_EXT_pixel_buffer_object */ + +#ifndef GL_EXT_pixel_transform +#define GL_EXT_pixel_transform 1 +#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330 +#define GL_PIXEL_MAG_FILTER_EXT 0x8331 +#define GL_PIXEL_MIN_FILTER_EXT 0x8332 +#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333 +#define GL_CUBIC_EXT 0x8334 +#define GL_AVERAGE_EXT 0x8335 +#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336 +#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337 +#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338 + typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPixelTransformParameteriEXT (GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glPixelTransformParameterfEXT (GLenum target, GLenum pname, GLfloat param); + GLAPI void APIENTRY glPixelTransformParameterivEXT (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glPixelTransformParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glGetPixelTransformParameterivEXT (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetPixelTransformParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_pixel_transform */ + +#ifndef GL_EXT_pixel_transform_color_table +#define GL_EXT_pixel_transform_color_table 1 +#endif /* GL_EXT_pixel_transform_color_table */ + +#ifndef GL_EXT_point_parameters +#define GL_EXT_point_parameters 1 +#define GL_POINT_SIZE_MIN_EXT 0x8126 +#define GL_POINT_SIZE_MAX_EXT 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +#define GL_DISTANCE_ATTENUATION_EXT 0x8129 + typedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC) (GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPointParameterfEXT (GLenum pname, GLfloat param); + GLAPI void APIENTRY glPointParameterfvEXT (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_EXT_point_parameters */ + +#ifndef GL_EXT_polygon_offset +#define GL_EXT_polygon_offset 1 +#define GL_POLYGON_OFFSET_EXT 0x8037 +#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038 +#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039 + typedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC) (GLfloat factor, GLfloat bias); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPolygonOffsetEXT (GLfloat factor, GLfloat bias); +#endif +#endif /* GL_EXT_polygon_offset */ + +#ifndef GL_EXT_polygon_offset_clamp +#define GL_EXT_polygon_offset_clamp 1 +#define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B + typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPEXTPROC) (GLfloat factor, GLfloat units, GLfloat clamp); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPolygonOffsetClampEXT (GLfloat factor, GLfloat units, GLfloat clamp); +#endif +#endif /* GL_EXT_polygon_offset_clamp */ + +#ifndef GL_EXT_post_depth_coverage +#define GL_EXT_post_depth_coverage 1 +#endif /* GL_EXT_post_depth_coverage */ + +#ifndef GL_EXT_provoking_vertex +#define GL_EXT_provoking_vertex 1 +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D +#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E +#define GL_PROVOKING_VERTEX_EXT 0x8E4F + typedef void (APIENTRYP PFNGLPROVOKINGVERTEXEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProvokingVertexEXT (GLenum mode); +#endif +#endif /* GL_EXT_provoking_vertex */ + +#ifndef GL_EXT_raster_multisample +#define GL_EXT_raster_multisample 1 +#define GL_RASTER_MULTISAMPLE_EXT 0x9327 +#define GL_RASTER_SAMPLES_EXT 0x9328 +#define GL_MAX_RASTER_SAMPLES_EXT 0x9329 +#define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A +#define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B +#define GL_EFFECTIVE_RASTER_SAMPLES_EXT 0x932C + typedef void (APIENTRYP PFNGLRASTERSAMPLESEXTPROC) (GLuint samples, GLboolean fixedsamplelocations); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glRasterSamplesEXT (GLuint samples, GLboolean fixedsamplelocations); +#endif +#endif /* GL_EXT_raster_multisample */ + +#ifndef GL_EXT_rescale_normal +#define GL_EXT_rescale_normal 1 +#define GL_RESCALE_NORMAL_EXT 0x803A +#endif /* GL_EXT_rescale_normal */ + +#ifndef GL_EXT_secondary_color +#define GL_EXT_secondary_color 1 +#define GL_COLOR_SUM_EXT 0x8458 +#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D +#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC) (GLbyte red, GLbyte green, GLbyte blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC) (const GLbyte *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC) (GLdouble red, GLdouble green, GLdouble blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC) (GLfloat red, GLfloat green, GLfloat blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC) (GLint red, GLint green, GLint blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC) (GLshort red, GLshort green, GLshort blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC) (const GLshort *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC) (GLubyte red, GLubyte green, GLubyte blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC) (const GLubyte *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC) (GLuint red, GLuint green, GLuint blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC) (const GLuint *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC) (GLushort red, GLushort green, GLushort blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC) (const GLushort *v); + typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSecondaryColor3bEXT (GLbyte red, GLbyte green, GLbyte blue); + GLAPI void APIENTRY glSecondaryColor3bvEXT (const GLbyte *v); + GLAPI void APIENTRY glSecondaryColor3dEXT (GLdouble red, GLdouble green, GLdouble blue); + GLAPI void APIENTRY glSecondaryColor3dvEXT (const GLdouble *v); + GLAPI void APIENTRY glSecondaryColor3fEXT (GLfloat red, GLfloat green, GLfloat blue); + GLAPI void APIENTRY glSecondaryColor3fvEXT (const GLfloat *v); + GLAPI void APIENTRY glSecondaryColor3iEXT (GLint red, GLint green, GLint blue); + GLAPI void APIENTRY glSecondaryColor3ivEXT (const GLint *v); + GLAPI void APIENTRY glSecondaryColor3sEXT (GLshort red, GLshort green, GLshort blue); + GLAPI void APIENTRY glSecondaryColor3svEXT (const GLshort *v); + GLAPI void APIENTRY glSecondaryColor3ubEXT (GLubyte red, GLubyte green, GLubyte blue); + GLAPI void APIENTRY glSecondaryColor3ubvEXT (const GLubyte *v); + GLAPI void APIENTRY glSecondaryColor3uiEXT (GLuint red, GLuint green, GLuint blue); + GLAPI void APIENTRY glSecondaryColor3uivEXT (const GLuint *v); + GLAPI void APIENTRY glSecondaryColor3usEXT (GLushort red, GLushort green, GLushort blue); + GLAPI void APIENTRY glSecondaryColor3usvEXT (const GLushort *v); + GLAPI void APIENTRY glSecondaryColorPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_secondary_color */ + +#ifndef GL_EXT_semaphore +#define GL_EXT_semaphore 1 +#define GL_LAYOUT_GENERAL_EXT 0x958D +#define GL_LAYOUT_COLOR_ATTACHMENT_EXT 0x958E +#define GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT 0x958F +#define GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT 0x9590 +#define GL_LAYOUT_SHADER_READ_ONLY_EXT 0x9591 +#define GL_LAYOUT_TRANSFER_SRC_EXT 0x9592 +#define GL_LAYOUT_TRANSFER_DST_EXT 0x9593 +#define GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT 0x9530 +#define GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT 0x9531 + typedef void (APIENTRYP PFNGLGENSEMAPHORESEXTPROC) (GLsizei n, GLuint *semaphores); + typedef void (APIENTRYP PFNGLDELETESEMAPHORESEXTPROC) (GLsizei n, const GLuint *semaphores); + typedef GLboolean (APIENTRYP PFNGLISSEMAPHOREEXTPROC) (GLuint semaphore); + typedef void (APIENTRYP PFNGLSEMAPHOREPARAMETERUI64VEXTPROC) (GLuint semaphore, GLenum pname, const GLuint64 *params); + typedef void (APIENTRYP PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC) (GLuint semaphore, GLenum pname, GLuint64 *params); + typedef void (APIENTRYP PFNGLWAITSEMAPHOREEXTPROC) (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *srcLayouts); + typedef void (APIENTRYP PFNGLSIGNALSEMAPHOREEXTPROC) (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *dstLayouts); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGenSemaphoresEXT (GLsizei n, GLuint *semaphores); + GLAPI void APIENTRY glDeleteSemaphoresEXT (GLsizei n, const GLuint *semaphores); + GLAPI GLboolean APIENTRY glIsSemaphoreEXT (GLuint semaphore); + GLAPI void APIENTRY glSemaphoreParameterui64vEXT (GLuint semaphore, GLenum pname, const GLuint64 *params); + GLAPI void APIENTRY glGetSemaphoreParameterui64vEXT (GLuint semaphore, GLenum pname, GLuint64 *params); + GLAPI void APIENTRY glWaitSemaphoreEXT (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *srcLayouts); + GLAPI void APIENTRY glSignalSemaphoreEXT (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *dstLayouts); +#endif +#endif /* GL_EXT_semaphore */ + +#ifndef GL_EXT_semaphore_fd +#define GL_EXT_semaphore_fd 1 + typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREFDEXTPROC) (GLuint semaphore, GLenum handleType, GLint fd); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glImportSemaphoreFdEXT (GLuint semaphore, GLenum handleType, GLint fd); +#endif +#endif /* GL_EXT_semaphore_fd */ + +#ifndef GL_EXT_semaphore_win32 +#define GL_EXT_semaphore_win32 1 +#define GL_HANDLE_TYPE_D3D12_FENCE_EXT 0x9594 +#define GL_D3D12_FENCE_VALUE_EXT 0x9595 + typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC) (GLuint semaphore, GLenum handleType, void *handle); + typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC) (GLuint semaphore, GLenum handleType, const void *name); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glImportSemaphoreWin32HandleEXT (GLuint semaphore, GLenum handleType, void *handle); + GLAPI void APIENTRY glImportSemaphoreWin32NameEXT (GLuint semaphore, GLenum handleType, const void *name); +#endif +#endif /* GL_EXT_semaphore_win32 */ + +#ifndef GL_EXT_separate_shader_objects +#define GL_EXT_separate_shader_objects 1 +#define GL_ACTIVE_PROGRAM_EXT 0x8B8D + typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program); + typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program); + typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glUseShaderProgramEXT (GLenum type, GLuint program); + GLAPI void APIENTRY glActiveProgramEXT (GLuint program); + GLAPI GLuint APIENTRY glCreateShaderProgramEXT (GLenum type, const GLchar *string); +#endif +#endif /* GL_EXT_separate_shader_objects */ + +#ifndef GL_EXT_separate_specular_color +#define GL_EXT_separate_specular_color 1 +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif /* GL_EXT_separate_specular_color */ + +#ifndef GL_EXT_shader_framebuffer_fetch +#define GL_EXT_shader_framebuffer_fetch 1 +#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52 +#endif /* GL_EXT_shader_framebuffer_fetch */ + +#ifndef GL_EXT_shader_framebuffer_fetch_non_coherent +#define GL_EXT_shader_framebuffer_fetch_non_coherent 1 + typedef void (APIENTRYP PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferFetchBarrierEXT (void); +#endif +#endif /* GL_EXT_shader_framebuffer_fetch_non_coherent */ + +#ifndef GL_EXT_shader_image_load_formatted +#define GL_EXT_shader_image_load_formatted 1 +#endif /* GL_EXT_shader_image_load_formatted */ + +#ifndef GL_EXT_shader_image_load_store +#define GL_EXT_shader_image_load_store 1 +#define GL_MAX_IMAGE_UNITS_EXT 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT 0x8F39 +#define GL_IMAGE_BINDING_NAME_EXT 0x8F3A +#define GL_IMAGE_BINDING_LEVEL_EXT 0x8F3B +#define GL_IMAGE_BINDING_LAYERED_EXT 0x8F3C +#define GL_IMAGE_BINDING_LAYER_EXT 0x8F3D +#define GL_IMAGE_BINDING_ACCESS_EXT 0x8F3E +#define GL_IMAGE_1D_EXT 0x904C +#define GL_IMAGE_2D_EXT 0x904D +#define GL_IMAGE_3D_EXT 0x904E +#define GL_IMAGE_2D_RECT_EXT 0x904F +#define GL_IMAGE_CUBE_EXT 0x9050 +#define GL_IMAGE_BUFFER_EXT 0x9051 +#define GL_IMAGE_1D_ARRAY_EXT 0x9052 +#define GL_IMAGE_2D_ARRAY_EXT 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE_EXT 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9056 +#define GL_INT_IMAGE_1D_EXT 0x9057 +#define GL_INT_IMAGE_2D_EXT 0x9058 +#define GL_INT_IMAGE_3D_EXT 0x9059 +#define GL_INT_IMAGE_2D_RECT_EXT 0x905A +#define GL_INT_IMAGE_CUBE_EXT 0x905B +#define GL_INT_IMAGE_BUFFER_EXT 0x905C +#define GL_INT_IMAGE_1D_ARRAY_EXT 0x905D +#define GL_INT_IMAGE_2D_ARRAY_EXT 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE_EXT 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D_EXT 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D_EXT 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D_EXT 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE_EXT 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x906C +#define GL_MAX_IMAGE_SAMPLES_EXT 0x906D +#define GL_IMAGE_BINDING_FORMAT_EXT 0x906E +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT_EXT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT_EXT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT_EXT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT 0x00000020 +#define GL_COMMAND_BARRIER_BIT_EXT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT_EXT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT_EXT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT_EXT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT_EXT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT_EXT 0x00001000 +#define GL_ALL_BARRIER_BITS_EXT 0xFFFFFFFF + typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREEXTPROC) (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); + typedef void (APIENTRYP PFNGLMEMORYBARRIEREXTPROC) (GLbitfield barriers); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBindImageTextureEXT (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); + GLAPI void APIENTRY glMemoryBarrierEXT (GLbitfield barriers); +#endif +#endif /* GL_EXT_shader_image_load_store */ + +#ifndef GL_EXT_shader_integer_mix +#define GL_EXT_shader_integer_mix 1 +#endif /* GL_EXT_shader_integer_mix */ + +#ifndef GL_EXT_shadow_funcs +#define GL_EXT_shadow_funcs 1 +#endif /* GL_EXT_shadow_funcs */ + +#ifndef GL_EXT_shared_texture_palette +#define GL_EXT_shared_texture_palette 1 +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif /* GL_EXT_shared_texture_palette */ + +#ifndef GL_EXT_sparse_texture2 +#define GL_EXT_sparse_texture2 1 +#endif /* GL_EXT_sparse_texture2 */ + +#ifndef GL_EXT_stencil_clear_tag +#define GL_EXT_stencil_clear_tag 1 +#define GL_STENCIL_TAG_BITS_EXT 0x88F2 +#define GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3 + typedef void (APIENTRYP PFNGLSTENCILCLEARTAGEXTPROC) (GLsizei stencilTagBits, GLuint stencilClearTag); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glStencilClearTagEXT (GLsizei stencilTagBits, GLuint stencilClearTag); +#endif +#endif /* GL_EXT_stencil_clear_tag */ + +#ifndef GL_EXT_stencil_two_side +#define GL_EXT_stencil_two_side 1 +#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 +#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 + typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glActiveStencilFaceEXT (GLenum face); +#endif +#endif /* GL_EXT_stencil_two_side */ + +#ifndef GL_EXT_stencil_wrap +#define GL_EXT_stencil_wrap 1 +#define GL_INCR_WRAP_EXT 0x8507 +#define GL_DECR_WRAP_EXT 0x8508 +#endif /* GL_EXT_stencil_wrap */ + +#ifndef GL_EXT_subtexture +#define GL_EXT_subtexture 1 + typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_EXT_subtexture */ + +#ifndef GL_EXT_texture +#define GL_EXT_texture 1 +#define GL_ALPHA4_EXT 0x803B +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA12_EXT 0x803D +#define GL_ALPHA16_EXT 0x803E +#define GL_LUMINANCE4_EXT 0x803F +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE12_EXT 0x8041 +#define GL_LUMINANCE16_EXT 0x8042 +#define GL_LUMINANCE4_ALPHA4_EXT 0x8043 +#define GL_LUMINANCE6_ALPHA2_EXT 0x8044 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE12_ALPHA4_EXT 0x8046 +#define GL_LUMINANCE12_ALPHA12_EXT 0x8047 +#define GL_LUMINANCE16_ALPHA16_EXT 0x8048 +#define GL_INTENSITY_EXT 0x8049 +#define GL_INTENSITY4_EXT 0x804A +#define GL_INTENSITY8_EXT 0x804B +#define GL_INTENSITY12_EXT 0x804C +#define GL_INTENSITY16_EXT 0x804D +#define GL_RGB2_EXT 0x804E +#define GL_RGB4_EXT 0x804F +#define GL_RGB5_EXT 0x8050 +#define GL_RGB8_EXT 0x8051 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB12_EXT 0x8053 +#define GL_RGB16_EXT 0x8054 +#define GL_RGBA2_EXT 0x8055 +#define GL_RGBA4_EXT 0x8056 +#define GL_RGB5_A1_EXT 0x8057 +#define GL_RGBA8_EXT 0x8058 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGBA12_EXT 0x805A +#define GL_RGBA16_EXT 0x805B +#define GL_TEXTURE_RED_SIZE_EXT 0x805C +#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D +#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E +#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061 +#define GL_REPLACE_EXT 0x8062 +#define GL_PROXY_TEXTURE_1D_EXT 0x8063 +#define GL_PROXY_TEXTURE_2D_EXT 0x8064 +#define GL_TEXTURE_TOO_LARGE_EXT 0x8065 +#endif /* GL_EXT_texture */ + +#ifndef GL_EXT_texture3D +#define GL_EXT_texture3D 1 +#define GL_PACK_SKIP_IMAGES_EXT 0x806B +#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C +#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D +#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_TEXTURE_DEPTH_EXT 0x8071 +#define GL_TEXTURE_WRAP_R_EXT 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 + typedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexImage3DEXT (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_EXT_texture3D */ + +#ifndef GL_EXT_texture_array +#define GL_EXT_texture_array 1 +#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19 +#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D +#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF +#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferTextureLayerEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#endif +#endif /* GL_EXT_texture_array */ + +#ifndef GL_EXT_texture_buffer_object +#define GL_EXT_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E + typedef void (APIENTRYP PFNGLTEXBUFFEREXTPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexBufferEXT (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_EXT_texture_buffer_object */ + +#ifndef GL_EXT_texture_compression_latc +#define GL_EXT_texture_compression_latc 1 +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif /* GL_EXT_texture_compression_latc */ + +#ifndef GL_EXT_texture_compression_rgtc +#define GL_EXT_texture_compression_rgtc 1 +#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#endif /* GL_EXT_texture_compression_rgtc */ + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_compression_s3tc 1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif /* GL_EXT_texture_compression_s3tc */ + +#ifndef GL_EXT_texture_cube_map +#define GL_EXT_texture_cube_map 1 +#define GL_NORMAL_MAP_EXT 0x8511 +#define GL_REFLECTION_MAP_EXT 0x8512 +#define GL_TEXTURE_CUBE_MAP_EXT 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C +#endif /* GL_EXT_texture_cube_map */ + +#ifndef GL_EXT_texture_env_add +#define GL_EXT_texture_env_add 1 +#endif /* GL_EXT_texture_env_add */ + +#ifndef GL_EXT_texture_env_combine +#define GL_EXT_texture_env_combine 1 +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#endif /* GL_EXT_texture_env_combine */ + +#ifndef GL_EXT_texture_env_dot3 +#define GL_EXT_texture_env_dot3 1 +#define GL_DOT3_RGB_EXT 0x8740 +#define GL_DOT3_RGBA_EXT 0x8741 +#endif /* GL_EXT_texture_env_dot3 */ + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif /* GL_EXT_texture_filter_anisotropic */ + +#ifndef GL_EXT_texture_filter_minmax +#define GL_EXT_texture_filter_minmax 1 +#define GL_TEXTURE_REDUCTION_MODE_EXT 0x9366 +#define GL_WEIGHTED_AVERAGE_EXT 0x9367 +#endif /* GL_EXT_texture_filter_minmax */ + +#ifndef GL_EXT_texture_integer +#define GL_EXT_texture_integer 1 +#define GL_RGBA32UI_EXT 0x8D70 +#define GL_RGB32UI_EXT 0x8D71 +#define GL_ALPHA32UI_EXT 0x8D72 +#define GL_INTENSITY32UI_EXT 0x8D73 +#define GL_LUMINANCE32UI_EXT 0x8D74 +#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 +#define GL_RGBA16UI_EXT 0x8D76 +#define GL_RGB16UI_EXT 0x8D77 +#define GL_ALPHA16UI_EXT 0x8D78 +#define GL_INTENSITY16UI_EXT 0x8D79 +#define GL_LUMINANCE16UI_EXT 0x8D7A +#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B +#define GL_RGBA8UI_EXT 0x8D7C +#define GL_RGB8UI_EXT 0x8D7D +#define GL_ALPHA8UI_EXT 0x8D7E +#define GL_INTENSITY8UI_EXT 0x8D7F +#define GL_LUMINANCE8UI_EXT 0x8D80 +#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 +#define GL_RGBA32I_EXT 0x8D82 +#define GL_RGB32I_EXT 0x8D83 +#define GL_ALPHA32I_EXT 0x8D84 +#define GL_INTENSITY32I_EXT 0x8D85 +#define GL_LUMINANCE32I_EXT 0x8D86 +#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 +#define GL_RGBA16I_EXT 0x8D88 +#define GL_RGB16I_EXT 0x8D89 +#define GL_ALPHA16I_EXT 0x8D8A +#define GL_INTENSITY16I_EXT 0x8D8B +#define GL_LUMINANCE16I_EXT 0x8D8C +#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D +#define GL_RGBA8I_EXT 0x8D8E +#define GL_RGB8I_EXT 0x8D8F +#define GL_ALPHA8I_EXT 0x8D90 +#define GL_INTENSITY8I_EXT 0x8D91 +#define GL_LUMINANCE8I_EXT 0x8D92 +#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 +#define GL_RED_INTEGER_EXT 0x8D94 +#define GL_GREEN_INTEGER_EXT 0x8D95 +#define GL_BLUE_INTEGER_EXT 0x8D96 +#define GL_ALPHA_INTEGER_EXT 0x8D97 +#define GL_RGB_INTEGER_EXT 0x8D98 +#define GL_RGBA_INTEGER_EXT 0x8D99 +#define GL_BGR_INTEGER_EXT 0x8D9A +#define GL_BGRA_INTEGER_EXT 0x8D9B +#define GL_LUMINANCE_INTEGER_EXT 0x8D9C +#define GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D +#define GL_RGBA_INTEGER_MODE_EXT 0x8D9E + typedef void (APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, const GLuint *params); + typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLCLEARCOLORIIEXTPROC) (GLint red, GLint green, GLint blue, GLint alpha); + typedef void (APIENTRYP PFNGLCLEARCOLORIUIEXTPROC) (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexParameterIivEXT (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glTexParameterIuivEXT (GLenum target, GLenum pname, const GLuint *params); + GLAPI void APIENTRY glGetTexParameterIivEXT (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetTexParameterIuivEXT (GLenum target, GLenum pname, GLuint *params); + GLAPI void APIENTRY glClearColorIiEXT (GLint red, GLint green, GLint blue, GLint alpha); + GLAPI void APIENTRY glClearColorIuiEXT (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#endif +#endif /* GL_EXT_texture_integer */ + +#ifndef GL_EXT_texture_lod_bias +#define GL_EXT_texture_lod_bias 1 +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif /* GL_EXT_texture_lod_bias */ + +#ifndef GL_EXT_texture_mirror_clamp +#define GL_EXT_texture_mirror_clamp 1 +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 +#endif /* GL_EXT_texture_mirror_clamp */ + +#ifndef GL_EXT_texture_object +#define GL_EXT_texture_object 1 +#define GL_TEXTURE_PRIORITY_EXT 0x8066 +#define GL_TEXTURE_RESIDENT_EXT 0x8067 +#define GL_TEXTURE_1D_BINDING_EXT 0x8068 +#define GL_TEXTURE_2D_BINDING_EXT 0x8069 +#define GL_TEXTURE_3D_BINDING_EXT 0x806A + typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC) (GLsizei n, const GLuint *textures, GLboolean *residences); + typedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC) (GLenum target, GLuint texture); + typedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC) (GLsizei n, const GLuint *textures); + typedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC) (GLsizei n, GLuint *textures); + typedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC) (GLuint texture); + typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC) (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLboolean APIENTRY glAreTexturesResidentEXT (GLsizei n, const GLuint *textures, GLboolean *residences); + GLAPI void APIENTRY glBindTextureEXT (GLenum target, GLuint texture); + GLAPI void APIENTRY glDeleteTexturesEXT (GLsizei n, const GLuint *textures); + GLAPI void APIENTRY glGenTexturesEXT (GLsizei n, GLuint *textures); + GLAPI GLboolean APIENTRY glIsTextureEXT (GLuint texture); + GLAPI void APIENTRY glPrioritizeTexturesEXT (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#endif +#endif /* GL_EXT_texture_object */ + +#ifndef GL_EXT_texture_perturb_normal +#define GL_EXT_texture_perturb_normal 1 +#define GL_PERTURB_EXT 0x85AE +#define GL_TEXTURE_NORMAL_EXT 0x85AF + typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTextureNormalEXT (GLenum mode); +#endif +#endif /* GL_EXT_texture_perturb_normal */ + +#ifndef GL_EXT_texture_sRGB +#define GL_EXT_texture_sRGB 1 +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB8_EXT 0x8C41 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 +#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 +#define GL_SLUMINANCE_EXT 0x8C46 +#define GL_SLUMINANCE8_EXT 0x8C47 +#define GL_COMPRESSED_SRGB_EXT 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 +#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif /* GL_EXT_texture_sRGB */ + +#ifndef GL_EXT_texture_sRGB_R8 +#define GL_EXT_texture_sRGB_R8 1 +#define GL_SR8_EXT 0x8FBD +#endif /* GL_EXT_texture_sRGB_R8 */ + +#ifndef GL_EXT_texture_sRGB_RG8 +#define GL_EXT_texture_sRGB_RG8 1 +#define GL_SRG8_EXT 0x8FBE +#endif /* GL_EXT_texture_sRGB_RG8 */ + +#ifndef GL_EXT_texture_sRGB_decode +#define GL_EXT_texture_sRGB_decode 1 +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#define GL_DECODE_EXT 0x8A49 +#define GL_SKIP_DECODE_EXT 0x8A4A +#endif /* GL_EXT_texture_sRGB_decode */ + +#ifndef GL_EXT_texture_shadow_lod +#define GL_EXT_texture_shadow_lod 1 +#endif /* GL_EXT_texture_shadow_lod */ + +#ifndef GL_EXT_texture_shared_exponent +#define GL_EXT_texture_shared_exponent 1 +#define GL_RGB9_E5_EXT 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E +#define GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F +#endif /* GL_EXT_texture_shared_exponent */ + +#ifndef GL_EXT_texture_snorm +#define GL_EXT_texture_snorm 1 +#define GL_ALPHA_SNORM 0x9010 +#define GL_LUMINANCE_SNORM 0x9011 +#define GL_LUMINANCE_ALPHA_SNORM 0x9012 +#define GL_INTENSITY_SNORM 0x9013 +#define GL_ALPHA8_SNORM 0x9014 +#define GL_LUMINANCE8_SNORM 0x9015 +#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016 +#define GL_INTENSITY8_SNORM 0x9017 +#define GL_ALPHA16_SNORM 0x9018 +#define GL_LUMINANCE16_SNORM 0x9019 +#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A +#define GL_INTENSITY16_SNORM 0x901B +#define GL_RED_SNORM 0x8F90 +#define GL_RG_SNORM 0x8F91 +#define GL_RGB_SNORM 0x8F92 +#define GL_RGBA_SNORM 0x8F93 +#endif /* GL_EXT_texture_snorm */ + +#ifndef GL_EXT_texture_storage +#define GL_EXT_texture_storage 1 +#define GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F +#define GL_RGBA32F_EXT 0x8814 +#define GL_RGB32F_EXT 0x8815 +#define GL_ALPHA32F_EXT 0x8816 +#define GL_LUMINANCE32F_EXT 0x8818 +#define GL_LUMINANCE_ALPHA32F_EXT 0x8819 +#define GL_RGBA16F_EXT 0x881A +#define GL_RGB16F_EXT 0x881B +#define GL_ALPHA16F_EXT 0x881C +#define GL_LUMINANCE16F_EXT 0x881E +#define GL_LUMINANCE_ALPHA16F_EXT 0x881F +#define GL_BGRA8_EXT 0x93A1 +#define GL_R8_EXT 0x8229 +#define GL_RG8_EXT 0x822B +#define GL_R32F_EXT 0x822E +#define GL_RG32F_EXT 0x8230 +#define GL_R16F_EXT 0x822D +#define GL_RG16F_EXT 0x822F + typedef void (APIENTRYP PFNGLTEXSTORAGE1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + typedef void (APIENTRYP PFNGLTEXSTORAGE2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLTEXSTORAGE3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexStorage1DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + GLAPI void APIENTRY glTexStorage2DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + GLAPI void APIENTRY glTexStorage3DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* GL_EXT_texture_storage */ + +#ifndef GL_EXT_texture_swizzle +#define GL_EXT_texture_swizzle 1 +#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42 +#define GL_TEXTURE_SWIZZLE_G_EXT 0x8E43 +#define GL_TEXTURE_SWIZZLE_B_EXT 0x8E44 +#define GL_TEXTURE_SWIZZLE_A_EXT 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA_EXT 0x8E46 +#endif /* GL_EXT_texture_swizzle */ + +#ifndef GL_EXT_timer_query +#define GL_EXT_timer_query 1 +#define GL_TIME_ELAPSED_EXT 0x88BF + typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC) (GLuint id, GLenum pname, GLint64 *params); + typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64 *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetQueryObjecti64vEXT (GLuint id, GLenum pname, GLint64 *params); + GLAPI void APIENTRY glGetQueryObjectui64vEXT (GLuint id, GLenum pname, GLuint64 *params); +#endif +#endif /* GL_EXT_timer_query */ + +#ifndef GL_EXT_transform_feedback +#define GL_EXT_transform_feedback 1 +#define GL_TRANSFORM_FEEDBACK_BUFFER_EXT 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT 0x8C85 +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT 0x8C8F +#define GL_INTERLEAVED_ATTRIBS_EXT 0x8C8C +#define GL_SEPARATE_ATTRIBS_EXT 0x8C8D +#define GL_PRIMITIVES_GENERATED_EXT 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT 0x8C88 +#define GL_RASTERIZER_DISCARD_EXT 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_EXT 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT 0x8C7F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT 0x8C76 + typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKEXTPROC) (GLenum primitiveMode); + typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKEXTPROC) (void); + typedef void (APIENTRYP PFNGLBINDBUFFERRANGEEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); + typedef void (APIENTRYP PFNGLBINDBUFFERBASEEXTPROC) (GLenum target, GLuint index, GLuint buffer); + typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); + typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBeginTransformFeedbackEXT (GLenum primitiveMode); + GLAPI void APIENTRY glEndTransformFeedbackEXT (void); + GLAPI void APIENTRY glBindBufferRangeEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glBindBufferOffsetEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset); + GLAPI void APIENTRY glBindBufferBaseEXT (GLenum target, GLuint index, GLuint buffer); + GLAPI void APIENTRY glTransformFeedbackVaryingsEXT (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); + GLAPI void APIENTRY glGetTransformFeedbackVaryingEXT (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#endif +#endif /* GL_EXT_transform_feedback */ + +#ifndef GL_EXT_vertex_array +#define GL_EXT_vertex_array 1 +#define GL_VERTEX_ARRAY_EXT 0x8074 +#define GL_NORMAL_ARRAY_EXT 0x8075 +#define GL_COLOR_ARRAY_EXT 0x8076 +#define GL_INDEX_ARRAY_EXT 0x8077 +#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078 +#define GL_EDGE_FLAG_ARRAY_EXT 0x8079 +#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A +#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B +#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C +#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D +#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E +#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F +#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080 +#define GL_COLOR_ARRAY_SIZE_EXT 0x8081 +#define GL_COLOR_ARRAY_TYPE_EXT 0x8082 +#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083 +#define GL_COLOR_ARRAY_COUNT_EXT 0x8084 +#define GL_INDEX_ARRAY_TYPE_EXT 0x8085 +#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086 +#define GL_INDEX_ARRAY_COUNT_EXT 0x8087 +#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A +#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B +#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C +#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D +#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E +#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F +#define GL_COLOR_ARRAY_POINTER_EXT 0x8090 +#define GL_INDEX_ARRAY_POINTER_EXT 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093 + typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC) (GLint i); + typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); + typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count); + typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer); + typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, void **params); + typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); + typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); + typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); + typedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glArrayElementEXT (GLint i); + GLAPI void APIENTRY glColorPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); + GLAPI void APIENTRY glDrawArraysEXT (GLenum mode, GLint first, GLsizei count); + GLAPI void APIENTRY glEdgeFlagPointerEXT (GLsizei stride, GLsizei count, const GLboolean *pointer); + GLAPI void APIENTRY glGetPointervEXT (GLenum pname, void **params); + GLAPI void APIENTRY glIndexPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); + GLAPI void APIENTRY glNormalPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); + GLAPI void APIENTRY glTexCoordPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); + GLAPI void APIENTRY glVertexPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +#endif +#endif /* GL_EXT_vertex_array */ + +#ifndef GL_EXT_vertex_array_bgra +#define GL_EXT_vertex_array_bgra 1 +#endif /* GL_EXT_vertex_array_bgra */ + +#ifndef GL_EXT_vertex_attrib_64bit +#define GL_EXT_vertex_attrib_64bit 1 +#define GL_DOUBLE_VEC2_EXT 0x8FFC +#define GL_DOUBLE_VEC3_EXT 0x8FFD +#define GL_DOUBLE_VEC4_EXT 0x8FFE +#define GL_DOUBLE_MAT2_EXT 0x8F46 +#define GL_DOUBLE_MAT3_EXT 0x8F47 +#define GL_DOUBLE_MAT4_EXT 0x8F48 +#define GL_DOUBLE_MAT2x3_EXT 0x8F49 +#define GL_DOUBLE_MAT2x4_EXT 0x8F4A +#define GL_DOUBLE_MAT3x2_EXT 0x8F4B +#define GL_DOUBLE_MAT3x4_EXT 0x8F4C +#define GL_DOUBLE_MAT4x2_EXT 0x8F4D +#define GL_DOUBLE_MAT4x3_EXT 0x8F4E + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DEXTPROC) (GLuint index, GLdouble x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DEXTPROC) (GLuint index, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVEXTPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVEXTPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVEXTPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVEXTPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVEXTPROC) (GLuint index, GLenum pname, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexAttribL1dEXT (GLuint index, GLdouble x); + GLAPI void APIENTRY glVertexAttribL2dEXT (GLuint index, GLdouble x, GLdouble y); + GLAPI void APIENTRY glVertexAttribL3dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glVertexAttribL4dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glVertexAttribL1dvEXT (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribL2dvEXT (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribL3dvEXT (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribL4dvEXT (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribLPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glGetVertexAttribLdvEXT (GLuint index, GLenum pname, GLdouble *params); +#endif +#endif /* GL_EXT_vertex_attrib_64bit */ + +#ifndef GL_EXT_vertex_shader +#define GL_EXT_vertex_shader 1 +#define GL_VERTEX_SHADER_EXT 0x8780 +#define GL_VERTEX_SHADER_BINDING_EXT 0x8781 +#define GL_OP_INDEX_EXT 0x8782 +#define GL_OP_NEGATE_EXT 0x8783 +#define GL_OP_DOT3_EXT 0x8784 +#define GL_OP_DOT4_EXT 0x8785 +#define GL_OP_MUL_EXT 0x8786 +#define GL_OP_ADD_EXT 0x8787 +#define GL_OP_MADD_EXT 0x8788 +#define GL_OP_FRAC_EXT 0x8789 +#define GL_OP_MAX_EXT 0x878A +#define GL_OP_MIN_EXT 0x878B +#define GL_OP_SET_GE_EXT 0x878C +#define GL_OP_SET_LT_EXT 0x878D +#define GL_OP_CLAMP_EXT 0x878E +#define GL_OP_FLOOR_EXT 0x878F +#define GL_OP_ROUND_EXT 0x8790 +#define GL_OP_EXP_BASE_2_EXT 0x8791 +#define GL_OP_LOG_BASE_2_EXT 0x8792 +#define GL_OP_POWER_EXT 0x8793 +#define GL_OP_RECIP_EXT 0x8794 +#define GL_OP_RECIP_SQRT_EXT 0x8795 +#define GL_OP_SUB_EXT 0x8796 +#define GL_OP_CROSS_PRODUCT_EXT 0x8797 +#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798 +#define GL_OP_MOV_EXT 0x8799 +#define GL_OUTPUT_VERTEX_EXT 0x879A +#define GL_OUTPUT_COLOR0_EXT 0x879B +#define GL_OUTPUT_COLOR1_EXT 0x879C +#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D +#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E +#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F +#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0 +#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1 +#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2 +#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3 +#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4 +#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5 +#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6 +#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7 +#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8 +#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9 +#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA +#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB +#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC +#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD +#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE +#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF +#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0 +#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1 +#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2 +#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3 +#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4 +#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5 +#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6 +#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7 +#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8 +#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9 +#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA +#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB +#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC +#define GL_OUTPUT_FOG_EXT 0x87BD +#define GL_SCALAR_EXT 0x87BE +#define GL_VECTOR_EXT 0x87BF +#define GL_MATRIX_EXT 0x87C0 +#define GL_VARIANT_EXT 0x87C1 +#define GL_INVARIANT_EXT 0x87C2 +#define GL_LOCAL_CONSTANT_EXT 0x87C3 +#define GL_LOCAL_EXT 0x87C4 +#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5 +#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6 +#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7 +#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8 +#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9 +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE +#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF +#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0 +#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1 +#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2 +#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3 +#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4 +#define GL_X_EXT 0x87D5 +#define GL_Y_EXT 0x87D6 +#define GL_Z_EXT 0x87D7 +#define GL_W_EXT 0x87D8 +#define GL_NEGATIVE_X_EXT 0x87D9 +#define GL_NEGATIVE_Y_EXT 0x87DA +#define GL_NEGATIVE_Z_EXT 0x87DB +#define GL_NEGATIVE_W_EXT 0x87DC +#define GL_ZERO_EXT 0x87DD +#define GL_ONE_EXT 0x87DE +#define GL_NEGATIVE_ONE_EXT 0x87DF +#define GL_NORMALIZED_RANGE_EXT 0x87E0 +#define GL_FULL_RANGE_EXT 0x87E1 +#define GL_CURRENT_VERTEX_EXT 0x87E2 +#define GL_MVP_MATRIX_EXT 0x87E3 +#define GL_VARIANT_VALUE_EXT 0x87E4 +#define GL_VARIANT_DATATYPE_EXT 0x87E5 +#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6 +#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7 +#define GL_VARIANT_ARRAY_EXT 0x87E8 +#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9 +#define GL_INVARIANT_VALUE_EXT 0x87EA +#define GL_INVARIANT_DATATYPE_EXT 0x87EB +#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC +#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED + typedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC) (void); + typedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC) (void); + typedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC) (GLuint id); + typedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC) (GLuint range); + typedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC) (GLuint id); + typedef void (APIENTRYP PFNGLSHADEROP1EXTPROC) (GLenum op, GLuint res, GLuint arg1); + typedef void (APIENTRYP PFNGLSHADEROP2EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2); + typedef void (APIENTRYP PFNGLSHADEROP3EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); + typedef void (APIENTRYP PFNGLSWIZZLEEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); + typedef void (APIENTRYP PFNGLWRITEMASKEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); + typedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); + typedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); + typedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC) (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); + typedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC) (GLuint id, GLenum type, const void *addr); + typedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC) (GLuint id, GLenum type, const void *addr); + typedef void (APIENTRYP PFNGLVARIANTBVEXTPROC) (GLuint id, const GLbyte *addr); + typedef void (APIENTRYP PFNGLVARIANTSVEXTPROC) (GLuint id, const GLshort *addr); + typedef void (APIENTRYP PFNGLVARIANTIVEXTPROC) (GLuint id, const GLint *addr); + typedef void (APIENTRYP PFNGLVARIANTFVEXTPROC) (GLuint id, const GLfloat *addr); + typedef void (APIENTRYP PFNGLVARIANTDVEXTPROC) (GLuint id, const GLdouble *addr); + typedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC) (GLuint id, const GLubyte *addr); + typedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC) (GLuint id, const GLushort *addr); + typedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC) (GLuint id, const GLuint *addr); + typedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC) (GLuint id, GLenum type, GLuint stride, const void *addr); + typedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); + typedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); + typedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC) (GLenum light, GLenum value); + typedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC) (GLenum face, GLenum value); + typedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC) (GLenum unit, GLenum coord, GLenum value); + typedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC) (GLenum unit, GLenum value); + typedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC) (GLenum value); + typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC) (GLuint id, GLenum cap); + typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); + typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); + typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); + typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, void **data); + typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); + typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); + typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); + typedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); + typedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); + typedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBeginVertexShaderEXT (void); + GLAPI void APIENTRY glEndVertexShaderEXT (void); + GLAPI void APIENTRY glBindVertexShaderEXT (GLuint id); + GLAPI GLuint APIENTRY glGenVertexShadersEXT (GLuint range); + GLAPI void APIENTRY glDeleteVertexShaderEXT (GLuint id); + GLAPI void APIENTRY glShaderOp1EXT (GLenum op, GLuint res, GLuint arg1); + GLAPI void APIENTRY glShaderOp2EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2); + GLAPI void APIENTRY glShaderOp3EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); + GLAPI void APIENTRY glSwizzleEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); + GLAPI void APIENTRY glWriteMaskEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); + GLAPI void APIENTRY glInsertComponentEXT (GLuint res, GLuint src, GLuint num); + GLAPI void APIENTRY glExtractComponentEXT (GLuint res, GLuint src, GLuint num); + GLAPI GLuint APIENTRY glGenSymbolsEXT (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); + GLAPI void APIENTRY glSetInvariantEXT (GLuint id, GLenum type, const void *addr); + GLAPI void APIENTRY glSetLocalConstantEXT (GLuint id, GLenum type, const void *addr); + GLAPI void APIENTRY glVariantbvEXT (GLuint id, const GLbyte *addr); + GLAPI void APIENTRY glVariantsvEXT (GLuint id, const GLshort *addr); + GLAPI void APIENTRY glVariantivEXT (GLuint id, const GLint *addr); + GLAPI void APIENTRY glVariantfvEXT (GLuint id, const GLfloat *addr); + GLAPI void APIENTRY glVariantdvEXT (GLuint id, const GLdouble *addr); + GLAPI void APIENTRY glVariantubvEXT (GLuint id, const GLubyte *addr); + GLAPI void APIENTRY glVariantusvEXT (GLuint id, const GLushort *addr); + GLAPI void APIENTRY glVariantuivEXT (GLuint id, const GLuint *addr); + GLAPI void APIENTRY glVariantPointerEXT (GLuint id, GLenum type, GLuint stride, const void *addr); + GLAPI void APIENTRY glEnableVariantClientStateEXT (GLuint id); + GLAPI void APIENTRY glDisableVariantClientStateEXT (GLuint id); + GLAPI GLuint APIENTRY glBindLightParameterEXT (GLenum light, GLenum value); + GLAPI GLuint APIENTRY glBindMaterialParameterEXT (GLenum face, GLenum value); + GLAPI GLuint APIENTRY glBindTexGenParameterEXT (GLenum unit, GLenum coord, GLenum value); + GLAPI GLuint APIENTRY glBindTextureUnitParameterEXT (GLenum unit, GLenum value); + GLAPI GLuint APIENTRY glBindParameterEXT (GLenum value); + GLAPI GLboolean APIENTRY glIsVariantEnabledEXT (GLuint id, GLenum cap); + GLAPI void APIENTRY glGetVariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); + GLAPI void APIENTRY glGetVariantIntegervEXT (GLuint id, GLenum value, GLint *data); + GLAPI void APIENTRY glGetVariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); + GLAPI void APIENTRY glGetVariantPointervEXT (GLuint id, GLenum value, void **data); + GLAPI void APIENTRY glGetInvariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); + GLAPI void APIENTRY glGetInvariantIntegervEXT (GLuint id, GLenum value, GLint *data); + GLAPI void APIENTRY glGetInvariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); + GLAPI void APIENTRY glGetLocalConstantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); + GLAPI void APIENTRY glGetLocalConstantIntegervEXT (GLuint id, GLenum value, GLint *data); + GLAPI void APIENTRY glGetLocalConstantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +#endif +#endif /* GL_EXT_vertex_shader */ + +#ifndef GL_EXT_vertex_weighting +#define GL_EXT_vertex_weighting 1 +#define GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3 +#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 +#define GL_MODELVIEW0_MATRIX_EXT 0x0BA6 +#define GL_MODELVIEW1_MATRIX_EXT 0x8506 +#define GL_VERTEX_WEIGHTING_EXT 0x8509 +#define GL_MODELVIEW0_EXT 0x1700 +#define GL_MODELVIEW1_EXT 0x850A +#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B +#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C +#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D +#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E +#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F +#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510 + typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight); + typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight); + typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexWeightfEXT (GLfloat weight); + GLAPI void APIENTRY glVertexWeightfvEXT (const GLfloat *weight); + GLAPI void APIENTRY glVertexWeightPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_vertex_weighting */ + +#ifndef GL_EXT_win32_keyed_mutex +#define GL_EXT_win32_keyed_mutex 1 + typedef GLboolean (APIENTRYP PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC) (GLuint memory, GLuint64 key, GLuint timeout); + typedef GLboolean (APIENTRYP PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC) (GLuint memory, GLuint64 key); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLboolean APIENTRY glAcquireKeyedMutexWin32EXT (GLuint memory, GLuint64 key, GLuint timeout); + GLAPI GLboolean APIENTRY glReleaseKeyedMutexWin32EXT (GLuint memory, GLuint64 key); +#endif +#endif /* GL_EXT_win32_keyed_mutex */ + +#ifndef GL_EXT_window_rectangles +#define GL_EXT_window_rectangles 1 +#define GL_INCLUSIVE_EXT 0x8F10 +#define GL_EXCLUSIVE_EXT 0x8F11 +#define GL_WINDOW_RECTANGLE_EXT 0x8F12 +#define GL_WINDOW_RECTANGLE_MODE_EXT 0x8F13 +#define GL_MAX_WINDOW_RECTANGLES_EXT 0x8F14 +#define GL_NUM_WINDOW_RECTANGLES_EXT 0x8F15 + typedef void (APIENTRYP PFNGLWINDOWRECTANGLESEXTPROC) (GLenum mode, GLsizei count, const GLint *box); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glWindowRectanglesEXT (GLenum mode, GLsizei count, const GLint *box); +#endif +#endif /* GL_EXT_window_rectangles */ + +#ifndef GL_EXT_x11_sync_object +#define GL_EXT_x11_sync_object 1 +#define GL_SYNC_X11_FENCE_EXT 0x90E1 + typedef GLsync (APIENTRYP PFNGLIMPORTSYNCEXTPROC) (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLsync APIENTRY glImportSyncEXT (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#endif +#endif /* GL_EXT_x11_sync_object */ + +#ifndef GL_GREMEDY_frame_terminator +#define GL_GREMEDY_frame_terminator 1 + typedef void (APIENTRYP PFNGLFRAMETERMINATORGREMEDYPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFrameTerminatorGREMEDY (void); +#endif +#endif /* GL_GREMEDY_frame_terminator */ + +#ifndef GL_GREMEDY_string_marker +#define GL_GREMEDY_string_marker 1 + typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const void *string); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glStringMarkerGREMEDY (GLsizei len, const void *string); +#endif +#endif /* GL_GREMEDY_string_marker */ + +#ifndef GL_HP_convolution_border_modes +#define GL_HP_convolution_border_modes 1 +#define GL_IGNORE_BORDER_HP 0x8150 +#define GL_CONSTANT_BORDER_HP 0x8151 +#define GL_REPLICATE_BORDER_HP 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154 +#endif /* GL_HP_convolution_border_modes */ + +#ifndef GL_HP_image_transform +#define GL_HP_image_transform 1 +#define GL_IMAGE_SCALE_X_HP 0x8155 +#define GL_IMAGE_SCALE_Y_HP 0x8156 +#define GL_IMAGE_TRANSLATE_X_HP 0x8157 +#define GL_IMAGE_TRANSLATE_Y_HP 0x8158 +#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159 +#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A +#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B +#define GL_IMAGE_MAG_FILTER_HP 0x815C +#define GL_IMAGE_MIN_FILTER_HP 0x815D +#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E +#define GL_CUBIC_HP 0x815F +#define GL_AVERAGE_HP 0x8160 +#define GL_IMAGE_TRANSFORM_2D_HP 0x8161 +#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162 +#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163 + typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC) (GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC) (GLenum target, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glImageTransformParameteriHP (GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glImageTransformParameterfHP (GLenum target, GLenum pname, GLfloat param); + GLAPI void APIENTRY glImageTransformParameterivHP (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glImageTransformParameterfvHP (GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glGetImageTransformParameterivHP (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetImageTransformParameterfvHP (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_HP_image_transform */ + +#ifndef GL_HP_occlusion_test +#define GL_HP_occlusion_test 1 +#define GL_OCCLUSION_TEST_HP 0x8165 +#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 +#endif /* GL_HP_occlusion_test */ + +#ifndef GL_HP_texture_lighting +#define GL_HP_texture_lighting 1 +#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167 +#define GL_TEXTURE_POST_SPECULAR_HP 0x8168 +#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169 +#endif /* GL_HP_texture_lighting */ + +#ifndef GL_IBM_cull_vertex +#define GL_IBM_cull_vertex 1 +#define GL_CULL_VERTEX_IBM 103050 +#endif /* GL_IBM_cull_vertex */ + +#ifndef GL_IBM_multimode_draw_arrays +#define GL_IBM_multimode_draw_arrays 1 + typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC) (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); + typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMultiModeDrawArraysIBM (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); + GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); +#endif +#endif /* GL_IBM_multimode_draw_arrays */ + +#ifndef GL_IBM_rasterpos_clip +#define GL_IBM_rasterpos_clip 1 +#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262 +#endif /* GL_IBM_rasterpos_clip */ + +#ifndef GL_IBM_static_data +#define GL_IBM_static_data 1 +#define GL_ALL_STATIC_DATA_IBM 103060 +#define GL_STATIC_VERTEX_ARRAY_IBM 103061 + typedef void (APIENTRYP PFNGLFLUSHSTATICDATAIBMPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFlushStaticDataIBM (GLenum target); +#endif +#endif /* GL_IBM_static_data */ + +#ifndef GL_IBM_texture_mirrored_repeat +#define GL_IBM_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_IBM 0x8370 +#endif /* GL_IBM_texture_mirrored_repeat */ + +#ifndef GL_IBM_vertex_array_lists +#define GL_IBM_vertex_array_lists 1 +#define GL_VERTEX_ARRAY_LIST_IBM 103070 +#define GL_NORMAL_ARRAY_LIST_IBM 103071 +#define GL_COLOR_ARRAY_LIST_IBM 103072 +#define GL_INDEX_ARRAY_LIST_IBM 103073 +#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074 +#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075 +#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076 +#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077 +#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080 +#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081 +#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082 +#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083 +#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084 +#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085 +#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086 +#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087 + typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); + typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); + typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean **pointer, GLint ptrstride); + typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); + typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); + typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); + typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); + typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); + GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); + GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint stride, const GLboolean **pointer, GLint ptrstride); + GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); + GLAPI void APIENTRY glIndexPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); + GLAPI void APIENTRY glNormalPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); + GLAPI void APIENTRY glTexCoordPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); + GLAPI void APIENTRY glVertexPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +#endif +#endif /* GL_IBM_vertex_array_lists */ + +#ifndef GL_INGR_blend_func_separate +#define GL_INGR_blend_func_separate 1 + typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendFuncSeparateINGR (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif +#endif /* GL_INGR_blend_func_separate */ + +#ifndef GL_INGR_color_clamp +#define GL_INGR_color_clamp 1 +#define GL_RED_MIN_CLAMP_INGR 0x8560 +#define GL_GREEN_MIN_CLAMP_INGR 0x8561 +#define GL_BLUE_MIN_CLAMP_INGR 0x8562 +#define GL_ALPHA_MIN_CLAMP_INGR 0x8563 +#define GL_RED_MAX_CLAMP_INGR 0x8564 +#define GL_GREEN_MAX_CLAMP_INGR 0x8565 +#define GL_BLUE_MAX_CLAMP_INGR 0x8566 +#define GL_ALPHA_MAX_CLAMP_INGR 0x8567 +#endif /* GL_INGR_color_clamp */ + +#ifndef GL_INGR_interlace_read +#define GL_INGR_interlace_read 1 +#define GL_INTERLACE_READ_INGR 0x8568 +#endif /* GL_INGR_interlace_read */ + +#ifndef GL_INTEL_blackhole_render +#define GL_INTEL_blackhole_render 1 +#define GL_BLACKHOLE_RENDER_INTEL 0x83FC +#endif /* GL_INTEL_blackhole_render */ + +#ifndef GL_INTEL_conservative_rasterization +#define GL_INTEL_conservative_rasterization 1 +#define GL_CONSERVATIVE_RASTERIZATION_INTEL 0x83FE +#endif /* GL_INTEL_conservative_rasterization */ + +#ifndef GL_INTEL_fragment_shader_ordering +#define GL_INTEL_fragment_shader_ordering 1 +#endif /* GL_INTEL_fragment_shader_ordering */ + +#ifndef GL_INTEL_framebuffer_CMAA +#define GL_INTEL_framebuffer_CMAA 1 + typedef void (APIENTRYP PFNGLAPPLYFRAMEBUFFERATTACHMENTCMAAINTELPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glApplyFramebufferAttachmentCMAAINTEL (void); +#endif +#endif /* GL_INTEL_framebuffer_CMAA */ + +#ifndef GL_INTEL_map_texture +#define GL_INTEL_map_texture 1 +#define GL_TEXTURE_MEMORY_LAYOUT_INTEL 0x83FF +#define GL_LAYOUT_DEFAULT_INTEL 0 +#define GL_LAYOUT_LINEAR_INTEL 1 +#define GL_LAYOUT_LINEAR_CPU_CACHED_INTEL 2 + typedef void (APIENTRYP PFNGLSYNCTEXTUREINTELPROC) (GLuint texture); + typedef void (APIENTRYP PFNGLUNMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level); + typedef void *(APIENTRYP PFNGLMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSyncTextureINTEL (GLuint texture); + GLAPI void APIENTRY glUnmapTexture2DINTEL (GLuint texture, GLint level); + GLAPI void *APIENTRY glMapTexture2DINTEL (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); +#endif +#endif /* GL_INTEL_map_texture */ + +#ifndef GL_INTEL_parallel_arrays +#define GL_INTEL_parallel_arrays 1 +#define GL_PARALLEL_ARRAYS_INTEL 0x83F4 +#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5 +#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6 +#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7 +#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8 + typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); + typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const void **pointer); + typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); + typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexPointervINTEL (GLint size, GLenum type, const void **pointer); + GLAPI void APIENTRY glNormalPointervINTEL (GLenum type, const void **pointer); + GLAPI void APIENTRY glColorPointervINTEL (GLint size, GLenum type, const void **pointer); + GLAPI void APIENTRY glTexCoordPointervINTEL (GLint size, GLenum type, const void **pointer); +#endif +#endif /* GL_INTEL_parallel_arrays */ + +#ifndef GL_INTEL_performance_query +#define GL_INTEL_performance_query 1 +#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000 +#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001 +#define GL_PERFQUERY_WAIT_INTEL 0x83FB +#define GL_PERFQUERY_FLUSH_INTEL 0x83FA +#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9 +#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0 +#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1 +#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2 +#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3 +#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4 +#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5 +#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8 +#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9 +#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA +#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB +#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC +#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD +#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE +#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF +#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500 + typedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle); + typedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle); + typedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle); + typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle); + typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId); + typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId); + typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); + typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten); + typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId); + typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBeginPerfQueryINTEL (GLuint queryHandle); + GLAPI void APIENTRY glCreatePerfQueryINTEL (GLuint queryId, GLuint *queryHandle); + GLAPI void APIENTRY glDeletePerfQueryINTEL (GLuint queryHandle); + GLAPI void APIENTRY glEndPerfQueryINTEL (GLuint queryHandle); + GLAPI void APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId); + GLAPI void APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId); + GLAPI void APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); + GLAPI void APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten); + GLAPI void APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId); + GLAPI void APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#endif +#endif /* GL_INTEL_performance_query */ + +#ifndef GL_MESAX_texture_stack +#define GL_MESAX_texture_stack 1 +#define GL_TEXTURE_1D_STACK_MESAX 0x8759 +#define GL_TEXTURE_2D_STACK_MESAX 0x875A +#define GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B +#define GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C +#define GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D +#define GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E +#endif /* GL_MESAX_texture_stack */ + +#ifndef GL_MESA_framebuffer_flip_x +#define GL_MESA_framebuffer_flip_x 1 +#define GL_FRAMEBUFFER_FLIP_X_MESA 0x8BBC +#endif /* GL_MESA_framebuffer_flip_x */ + +#ifndef GL_MESA_framebuffer_flip_y +#define GL_MESA_framebuffer_flip_y 1 +#define GL_FRAMEBUFFER_FLIP_Y_MESA 0x8BBB + typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIMESAPROC) (GLenum target, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC) (GLenum target, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferParameteriMESA (GLenum target, GLenum pname, GLint param); + GLAPI void APIENTRY glGetFramebufferParameterivMESA (GLenum target, GLenum pname, GLint *params); +#endif +#endif /* GL_MESA_framebuffer_flip_y */ + +#ifndef GL_MESA_framebuffer_swap_xy +#define GL_MESA_framebuffer_swap_xy 1 +#define GL_FRAMEBUFFER_SWAP_XY_MESA 0x8BBD +#endif /* GL_MESA_framebuffer_swap_xy */ + +#ifndef GL_MESA_pack_invert +#define GL_MESA_pack_invert 1 +#define GL_PACK_INVERT_MESA 0x8758 +#endif /* GL_MESA_pack_invert */ + +#ifndef GL_MESA_program_binary_formats +#define GL_MESA_program_binary_formats 1 +#define GL_PROGRAM_BINARY_FORMAT_MESA 0x875F +#endif /* GL_MESA_program_binary_formats */ + +#ifndef GL_MESA_resize_buffers +#define GL_MESA_resize_buffers 1 + typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glResizeBuffersMESA (void); +#endif +#endif /* GL_MESA_resize_buffers */ + +#ifndef GL_MESA_shader_integer_functions +#define GL_MESA_shader_integer_functions 1 +#endif /* GL_MESA_shader_integer_functions */ + +#ifndef GL_MESA_tile_raster_order +#define GL_MESA_tile_raster_order 1 +#define GL_TILE_RASTER_ORDER_FIXED_MESA 0x8BB8 +#define GL_TILE_RASTER_ORDER_INCREASING_X_MESA 0x8BB9 +#define GL_TILE_RASTER_ORDER_INCREASING_Y_MESA 0x8BBA +#endif /* GL_MESA_tile_raster_order */ + +#ifndef GL_MESA_window_pos +#define GL_MESA_window_pos 1 + typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC) (GLfloat x, GLfloat y); + typedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC) (GLint x, GLint y); + typedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC) (GLshort x, GLshort y); + typedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC) (const GLshort *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC) (GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC) (GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC) (GLint x, GLint y, GLint z); + typedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC) (GLshort x, GLshort y, GLshort z); + typedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC) (const GLshort *v); + typedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC) (const GLdouble *v); + typedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC) (const GLfloat *v); + typedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC) (GLint x, GLint y, GLint z, GLint w); + typedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC) (const GLint *v); + typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort z, GLshort w); + typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glWindowPos2dMESA (GLdouble x, GLdouble y); + GLAPI void APIENTRY glWindowPos2dvMESA (const GLdouble *v); + GLAPI void APIENTRY glWindowPos2fMESA (GLfloat x, GLfloat y); + GLAPI void APIENTRY glWindowPos2fvMESA (const GLfloat *v); + GLAPI void APIENTRY glWindowPos2iMESA (GLint x, GLint y); + GLAPI void APIENTRY glWindowPos2ivMESA (const GLint *v); + GLAPI void APIENTRY glWindowPos2sMESA (GLshort x, GLshort y); + GLAPI void APIENTRY glWindowPos2svMESA (const GLshort *v); + GLAPI void APIENTRY glWindowPos3dMESA (GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glWindowPos3dvMESA (const GLdouble *v); + GLAPI void APIENTRY glWindowPos3fMESA (GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glWindowPos3fvMESA (const GLfloat *v); + GLAPI void APIENTRY glWindowPos3iMESA (GLint x, GLint y, GLint z); + GLAPI void APIENTRY glWindowPos3ivMESA (const GLint *v); + GLAPI void APIENTRY glWindowPos3sMESA (GLshort x, GLshort y, GLshort z); + GLAPI void APIENTRY glWindowPos3svMESA (const GLshort *v); + GLAPI void APIENTRY glWindowPos4dMESA (GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glWindowPos4dvMESA (const GLdouble *v); + GLAPI void APIENTRY glWindowPos4fMESA (GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glWindowPos4fvMESA (const GLfloat *v); + GLAPI void APIENTRY glWindowPos4iMESA (GLint x, GLint y, GLint z, GLint w); + GLAPI void APIENTRY glWindowPos4ivMESA (const GLint *v); + GLAPI void APIENTRY glWindowPos4sMESA (GLshort x, GLshort y, GLshort z, GLshort w); + GLAPI void APIENTRY glWindowPos4svMESA (const GLshort *v); +#endif +#endif /* GL_MESA_window_pos */ + +#ifndef GL_MESA_ycbcr_texture +#define GL_MESA_ycbcr_texture 1 +#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB +#define GL_YCBCR_MESA 0x8757 +#endif /* GL_MESA_ycbcr_texture */ + +#ifndef GL_NVX_blend_equation_advanced_multi_draw_buffers +#define GL_NVX_blend_equation_advanced_multi_draw_buffers 1 +#endif /* GL_NVX_blend_equation_advanced_multi_draw_buffers */ + +#ifndef GL_NVX_conditional_render +#define GL_NVX_conditional_render 1 + typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVXPROC) (GLuint id); + typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBeginConditionalRenderNVX (GLuint id); + GLAPI void APIENTRY glEndConditionalRenderNVX (void); +#endif +#endif /* GL_NVX_conditional_render */ + +#ifndef GL_NVX_gpu_memory_info +#define GL_NVX_gpu_memory_info 1 +#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047 +#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048 +#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049 +#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A +#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B +#endif /* GL_NVX_gpu_memory_info */ + +#ifndef GL_NVX_gpu_multicast2 +#define GL_NVX_gpu_multicast2 1 +#define GL_UPLOAD_GPU_MASK_NVX 0x954A + typedef void (APIENTRYP PFNGLUPLOADGPUMASKNVXPROC) (GLbitfield mask); + typedef void (APIENTRYP PFNGLMULTICASTVIEWPORTARRAYVNVXPROC) (GLuint gpu, GLuint first, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLMULTICASTVIEWPORTPOSITIONWSCALENVXPROC) (GLuint gpu, GLuint index, GLfloat xcoeff, GLfloat ycoeff); + typedef void (APIENTRYP PFNGLMULTICASTSCISSORARRAYVNVXPROC) (GLuint gpu, GLuint first, GLsizei count, const GLint *v); + typedef GLuint (APIENTRYP PFNGLASYNCCOPYBUFFERSUBDATANVXPROC) (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *fenceValueArray, GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); + typedef GLuint (APIENTRYP PFNGLASYNCCOPYIMAGESUBDATANVXPROC) (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *waitValueArray, GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glUploadGpuMaskNVX (GLbitfield mask); + GLAPI void APIENTRY glMulticastViewportArrayvNVX (GLuint gpu, GLuint first, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glMulticastViewportPositionWScaleNVX (GLuint gpu, GLuint index, GLfloat xcoeff, GLfloat ycoeff); + GLAPI void APIENTRY glMulticastScissorArrayvNVX (GLuint gpu, GLuint first, GLsizei count, const GLint *v); + GLAPI GLuint APIENTRY glAsyncCopyBufferSubDataNVX (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *fenceValueArray, GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); + GLAPI GLuint APIENTRY glAsyncCopyImageSubDataNVX (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *waitValueArray, GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); +#endif +#endif /* GL_NVX_gpu_multicast2 */ + +#ifndef GL_NVX_linked_gpu_multicast +#define GL_NVX_linked_gpu_multicast 1 +#define GL_LGPU_SEPARATE_STORAGE_BIT_NVX 0x0800 +#define GL_MAX_LGPU_GPUS_NVX 0x92BA + typedef void (APIENTRYP PFNGLLGPUNAMEDBUFFERSUBDATANVXPROC) (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + typedef void (APIENTRYP PFNGLLGPUCOPYIMAGESUBDATANVXPROC) (GLuint sourceGpu, GLbitfield destinationGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); + typedef void (APIENTRYP PFNGLLGPUINTERLOCKNVXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glLGPUNamedBufferSubDataNVX (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + GLAPI void APIENTRY glLGPUCopyImageSubDataNVX (GLuint sourceGpu, GLbitfield destinationGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); + GLAPI void APIENTRY glLGPUInterlockNVX (void); +#endif +#endif /* GL_NVX_linked_gpu_multicast */ + +#ifndef GL_NVX_progress_fence +#define GL_NVX_progress_fence 1 + typedef GLuint (APIENTRYP PFNGLCREATEPROGRESSFENCENVXPROC) (void); + typedef void (APIENTRYP PFNGLSIGNALSEMAPHOREUI64NVXPROC) (GLuint signalGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); + typedef void (APIENTRYP PFNGLWAITSEMAPHOREUI64NVXPROC) (GLuint waitGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); + typedef void (APIENTRYP PFNGLCLIENTWAITSEMAPHOREUI64NVXPROC) (GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLuint APIENTRY glCreateProgressFenceNVX (void); + GLAPI void APIENTRY glSignalSemaphoreui64NVX (GLuint signalGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); + GLAPI void APIENTRY glWaitSemaphoreui64NVX (GLuint waitGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); + GLAPI void APIENTRY glClientWaitSemaphoreui64NVX (GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +#endif +#endif /* GL_NVX_progress_fence */ + +#ifndef GL_NV_alpha_to_coverage_dither_control +#define GL_NV_alpha_to_coverage_dither_control 1 +#define GL_ALPHA_TO_COVERAGE_DITHER_DEFAULT_NV 0x934D +#define GL_ALPHA_TO_COVERAGE_DITHER_ENABLE_NV 0x934E +#define GL_ALPHA_TO_COVERAGE_DITHER_DISABLE_NV 0x934F +#define GL_ALPHA_TO_COVERAGE_DITHER_MODE_NV 0x92BF + typedef void (APIENTRYP PFNGLALPHATOCOVERAGEDITHERCONTROLNVPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glAlphaToCoverageDitherControlNV (GLenum mode); +#endif +#endif /* GL_NV_alpha_to_coverage_dither_control */ + +#ifndef GL_NV_bindless_multi_draw_indirect +#define GL_NV_bindless_multi_draw_indirect 1 + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); + GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect */ + +#ifndef GL_NV_bindless_multi_draw_indirect_count +#define GL_NV_bindless_multi_draw_indirect_count 1 + typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); + typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessCountNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); + GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessCountNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect_count */ + +#ifndef GL_NV_bindless_texture +#define GL_NV_bindless_texture 1 + typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture); + typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler); + typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); + typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle); + typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); + typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access); + typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle); + typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value); + typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); + typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); + typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLuint64 APIENTRY glGetTextureHandleNV (GLuint texture); + GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleNV (GLuint texture, GLuint sampler); + GLAPI void APIENTRY glMakeTextureHandleResidentNV (GLuint64 handle); + GLAPI void APIENTRY glMakeTextureHandleNonResidentNV (GLuint64 handle); + GLAPI GLuint64 APIENTRY glGetImageHandleNV (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); + GLAPI void APIENTRY glMakeImageHandleResidentNV (GLuint64 handle, GLenum access); + GLAPI void APIENTRY glMakeImageHandleNonResidentNV (GLuint64 handle); + GLAPI void APIENTRY glUniformHandleui64NV (GLint location, GLuint64 value); + GLAPI void APIENTRY glUniformHandleui64vNV (GLint location, GLsizei count, const GLuint64 *value); + GLAPI void APIENTRY glProgramUniformHandleui64NV (GLuint program, GLint location, GLuint64 value); + GLAPI void APIENTRY glProgramUniformHandleui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64 *values); + GLAPI GLboolean APIENTRY glIsTextureHandleResidentNV (GLuint64 handle); + GLAPI GLboolean APIENTRY glIsImageHandleResidentNV (GLuint64 handle); +#endif +#endif /* GL_NV_bindless_texture */ + +#ifndef GL_NV_blend_equation_advanced +#define GL_NV_blend_equation_advanced 1 +#define GL_BLEND_OVERLAP_NV 0x9281 +#define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280 +#define GL_BLUE_NV 0x1905 +#define GL_COLORBURN_NV 0x929A +#define GL_COLORDODGE_NV 0x9299 +#define GL_CONJOINT_NV 0x9284 +#define GL_CONTRAST_NV 0x92A1 +#define GL_DARKEN_NV 0x9297 +#define GL_DIFFERENCE_NV 0x929E +#define GL_DISJOINT_NV 0x9283 +#define GL_DST_ATOP_NV 0x928F +#define GL_DST_IN_NV 0x928B +#define GL_DST_NV 0x9287 +#define GL_DST_OUT_NV 0x928D +#define GL_DST_OVER_NV 0x9289 +#define GL_EXCLUSION_NV 0x92A0 +#define GL_GREEN_NV 0x1904 +#define GL_HARDLIGHT_NV 0x929B +#define GL_HARDMIX_NV 0x92A9 +#define GL_HSL_COLOR_NV 0x92AF +#define GL_HSL_HUE_NV 0x92AD +#define GL_HSL_LUMINOSITY_NV 0x92B0 +#define GL_HSL_SATURATION_NV 0x92AE +#define GL_INVERT_OVG_NV 0x92B4 +#define GL_INVERT_RGB_NV 0x92A3 +#define GL_LIGHTEN_NV 0x9298 +#define GL_LINEARBURN_NV 0x92A5 +#define GL_LINEARDODGE_NV 0x92A4 +#define GL_LINEARLIGHT_NV 0x92A7 +#define GL_MINUS_CLAMPED_NV 0x92B3 +#define GL_MINUS_NV 0x929F +#define GL_MULTIPLY_NV 0x9294 +#define GL_OVERLAY_NV 0x9296 +#define GL_PINLIGHT_NV 0x92A8 +#define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2 +#define GL_PLUS_CLAMPED_NV 0x92B1 +#define GL_PLUS_DARKER_NV 0x9292 +#define GL_PLUS_NV 0x9291 +#define GL_RED_NV 0x1903 +#define GL_SCREEN_NV 0x9295 +#define GL_SOFTLIGHT_NV 0x929C +#define GL_SRC_ATOP_NV 0x928E +#define GL_SRC_IN_NV 0x928A +#define GL_SRC_NV 0x9286 +#define GL_SRC_OUT_NV 0x928C +#define GL_SRC_OVER_NV 0x9288 +#define GL_UNCORRELATED_NV 0x9282 +#define GL_VIVIDLIGHT_NV 0x92A6 +#define GL_XOR_NV 0x1506 + typedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC) (GLenum pname, GLint value); + typedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBlendParameteriNV (GLenum pname, GLint value); + GLAPI void APIENTRY glBlendBarrierNV (void); +#endif +#endif /* GL_NV_blend_equation_advanced */ + +#ifndef GL_NV_blend_equation_advanced_coherent +#define GL_NV_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285 +#endif /* GL_NV_blend_equation_advanced_coherent */ + +#ifndef GL_NV_blend_minmax_factor +#define GL_NV_blend_minmax_factor 1 +#endif /* GL_NV_blend_minmax_factor */ + +#ifndef GL_NV_blend_square +#define GL_NV_blend_square 1 +#endif /* GL_NV_blend_square */ + +#ifndef GL_NV_clip_space_w_scaling +#define GL_NV_clip_space_w_scaling 1 +#define GL_VIEWPORT_POSITION_W_SCALE_NV 0x937C +#define GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV 0x937D +#define GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV 0x937E + typedef void (APIENTRYP PFNGLVIEWPORTPOSITIONWSCALENVPROC) (GLuint index, GLfloat xcoeff, GLfloat ycoeff); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glViewportPositionWScaleNV (GLuint index, GLfloat xcoeff, GLfloat ycoeff); +#endif +#endif /* GL_NV_clip_space_w_scaling */ + +#ifndef GL_NV_command_list +#define GL_NV_command_list 1 +#define GL_TERMINATE_SEQUENCE_COMMAND_NV 0x0000 +#define GL_NOP_COMMAND_NV 0x0001 +#define GL_DRAW_ELEMENTS_COMMAND_NV 0x0002 +#define GL_DRAW_ARRAYS_COMMAND_NV 0x0003 +#define GL_DRAW_ELEMENTS_STRIP_COMMAND_NV 0x0004 +#define GL_DRAW_ARRAYS_STRIP_COMMAND_NV 0x0005 +#define GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV 0x0006 +#define GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV 0x0007 +#define GL_ELEMENT_ADDRESS_COMMAND_NV 0x0008 +#define GL_ATTRIBUTE_ADDRESS_COMMAND_NV 0x0009 +#define GL_UNIFORM_ADDRESS_COMMAND_NV 0x000A +#define GL_BLEND_COLOR_COMMAND_NV 0x000B +#define GL_STENCIL_REF_COMMAND_NV 0x000C +#define GL_LINE_WIDTH_COMMAND_NV 0x000D +#define GL_POLYGON_OFFSET_COMMAND_NV 0x000E +#define GL_ALPHA_REF_COMMAND_NV 0x000F +#define GL_VIEWPORT_COMMAND_NV 0x0010 +#define GL_SCISSOR_COMMAND_NV 0x0011 +#define GL_FRONT_FACE_COMMAND_NV 0x0012 + typedef void (APIENTRYP PFNGLCREATESTATESNVPROC) (GLsizei n, GLuint *states); + typedef void (APIENTRYP PFNGLDELETESTATESNVPROC) (GLsizei n, const GLuint *states); + typedef GLboolean (APIENTRYP PFNGLISSTATENVPROC) (GLuint state); + typedef void (APIENTRYP PFNGLSTATECAPTURENVPROC) (GLuint state, GLenum mode); + typedef GLuint (APIENTRYP PFNGLGETCOMMANDHEADERNVPROC) (GLenum tokenID, GLuint size); + typedef GLushort (APIENTRYP PFNGLGETSTAGEINDEXNVPROC) (GLenum shadertype); + typedef void (APIENTRYP PFNGLDRAWCOMMANDSNVPROC) (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); + typedef void (APIENTRYP PFNGLDRAWCOMMANDSADDRESSNVPROC) (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); + typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESNVPROC) (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC) (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + typedef void (APIENTRYP PFNGLCREATECOMMANDLISTSNVPROC) (GLsizei n, GLuint *lists); + typedef void (APIENTRYP PFNGLDELETECOMMANDLISTSNVPROC) (GLsizei n, const GLuint *lists); + typedef GLboolean (APIENTRYP PFNGLISCOMMANDLISTNVPROC) (GLuint list); + typedef void (APIENTRYP PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC) (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + typedef void (APIENTRYP PFNGLCOMMANDLISTSEGMENTSNVPROC) (GLuint list, GLuint segments); + typedef void (APIENTRYP PFNGLCOMPILECOMMANDLISTNVPROC) (GLuint list); + typedef void (APIENTRYP PFNGLCALLCOMMANDLISTNVPROC) (GLuint list); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCreateStatesNV (GLsizei n, GLuint *states); + GLAPI void APIENTRY glDeleteStatesNV (GLsizei n, const GLuint *states); + GLAPI GLboolean APIENTRY glIsStateNV (GLuint state); + GLAPI void APIENTRY glStateCaptureNV (GLuint state, GLenum mode); + GLAPI GLuint APIENTRY glGetCommandHeaderNV (GLenum tokenID, GLuint size); + GLAPI GLushort APIENTRY glGetStageIndexNV (GLenum shadertype); + GLAPI void APIENTRY glDrawCommandsNV (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); + GLAPI void APIENTRY glDrawCommandsAddressNV (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); + GLAPI void APIENTRY glDrawCommandsStatesNV (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + GLAPI void APIENTRY glDrawCommandsStatesAddressNV (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + GLAPI void APIENTRY glCreateCommandListsNV (GLsizei n, GLuint *lists); + GLAPI void APIENTRY glDeleteCommandListsNV (GLsizei n, const GLuint *lists); + GLAPI GLboolean APIENTRY glIsCommandListNV (GLuint list); + GLAPI void APIENTRY glListDrawCommandsStatesClientNV (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); + GLAPI void APIENTRY glCommandListSegmentsNV (GLuint list, GLuint segments); + GLAPI void APIENTRY glCompileCommandListNV (GLuint list); + GLAPI void APIENTRY glCallCommandListNV (GLuint list); +#endif +#endif /* GL_NV_command_list */ + +#ifndef GL_NV_compute_program5 +#define GL_NV_compute_program5 1 +#define GL_COMPUTE_PROGRAM_NV 0x90FB +#define GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC +#endif /* GL_NV_compute_program5 */ + +#ifndef GL_NV_compute_shader_derivatives +#define GL_NV_compute_shader_derivatives 1 +#endif /* GL_NV_compute_shader_derivatives */ + +#ifndef GL_NV_conditional_render +#define GL_NV_conditional_render 1 +#define GL_QUERY_WAIT_NV 0x8E13 +#define GL_QUERY_NO_WAIT_NV 0x8E14 +#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 + typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode); + typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBeginConditionalRenderNV (GLuint id, GLenum mode); + GLAPI void APIENTRY glEndConditionalRenderNV (void); +#endif +#endif /* GL_NV_conditional_render */ + +#ifndef GL_NV_conservative_raster +#define GL_NV_conservative_raster 1 +#define GL_CONSERVATIVE_RASTERIZATION_NV 0x9346 +#define GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV 0x9347 +#define GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV 0x9348 +#define GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV 0x9349 + typedef void (APIENTRYP PFNGLSUBPIXELPRECISIONBIASNVPROC) (GLuint xbits, GLuint ybits); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSubpixelPrecisionBiasNV (GLuint xbits, GLuint ybits); +#endif +#endif /* GL_NV_conservative_raster */ + +#ifndef GL_NV_conservative_raster_dilate +#define GL_NV_conservative_raster_dilate 1 +#define GL_CONSERVATIVE_RASTER_DILATE_NV 0x9379 +#define GL_CONSERVATIVE_RASTER_DILATE_RANGE_NV 0x937A +#define GL_CONSERVATIVE_RASTER_DILATE_GRANULARITY_NV 0x937B + typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERFNVPROC) (GLenum pname, GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glConservativeRasterParameterfNV (GLenum pname, GLfloat value); +#endif +#endif /* GL_NV_conservative_raster_dilate */ + +#ifndef GL_NV_conservative_raster_pre_snap +#define GL_NV_conservative_raster_pre_snap 1 +#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV 0x9550 +#endif /* GL_NV_conservative_raster_pre_snap */ + +#ifndef GL_NV_conservative_raster_pre_snap_triangles +#define GL_NV_conservative_raster_pre_snap_triangles 1 +#define GL_CONSERVATIVE_RASTER_MODE_NV 0x954D +#define GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV 0x954E +#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV 0x954F + typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERINVPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glConservativeRasterParameteriNV (GLenum pname, GLint param); +#endif +#endif /* GL_NV_conservative_raster_pre_snap_triangles */ + +#ifndef GL_NV_conservative_raster_underestimation +#define GL_NV_conservative_raster_underestimation 1 +#endif /* GL_NV_conservative_raster_underestimation */ + +#ifndef GL_NV_copy_depth_to_color +#define GL_NV_copy_depth_to_color 1 +#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E +#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F +#endif /* GL_NV_copy_depth_to_color */ + +#ifndef GL_NV_copy_image +#define GL_NV_copy_image 1 + typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATANVPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCopyImageSubDataNV (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* GL_NV_copy_image */ + +#ifndef GL_NV_deep_texture3D +#define GL_NV_deep_texture3D 1 +#define GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV 0x90D0 +#define GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV 0x90D1 +#endif /* GL_NV_deep_texture3D */ + +#ifndef GL_NV_depth_buffer_float +#define GL_NV_depth_buffer_float 1 +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD +#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF + typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); + typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); + typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDepthRangedNV (GLdouble zNear, GLdouble zFar); + GLAPI void APIENTRY glClearDepthdNV (GLdouble depth); + GLAPI void APIENTRY glDepthBoundsdNV (GLdouble zmin, GLdouble zmax); +#endif +#endif /* GL_NV_depth_buffer_float */ + +#ifndef GL_NV_depth_clamp +#define GL_NV_depth_clamp 1 +#define GL_DEPTH_CLAMP_NV 0x864F +#endif /* GL_NV_depth_clamp */ + +#ifndef GL_NV_draw_texture +#define GL_NV_draw_texture 1 + typedef void (APIENTRYP PFNGLDRAWTEXTURENVPROC) (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawTextureNV (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#endif +#endif /* GL_NV_draw_texture */ + +#ifndef GL_NV_draw_vulkan_image +#define GL_NV_draw_vulkan_image 1 + typedef void (APIENTRY *GLVULKANPROCNV)(void); + typedef void (APIENTRYP PFNGLDRAWVKIMAGENVPROC) (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); + typedef GLVULKANPROCNV (APIENTRYP PFNGLGETVKPROCADDRNVPROC) (const GLchar *name); + typedef void (APIENTRYP PFNGLWAITVKSEMAPHORENVPROC) (GLuint64 vkSemaphore); + typedef void (APIENTRYP PFNGLSIGNALVKSEMAPHORENVPROC) (GLuint64 vkSemaphore); + typedef void (APIENTRYP PFNGLSIGNALVKFENCENVPROC) (GLuint64 vkFence); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawVkImageNV (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); + GLAPI GLVULKANPROCNV APIENTRY glGetVkProcAddrNV (const GLchar *name); + GLAPI void APIENTRY glWaitVkSemaphoreNV (GLuint64 vkSemaphore); + GLAPI void APIENTRY glSignalVkSemaphoreNV (GLuint64 vkSemaphore); + GLAPI void APIENTRY glSignalVkFenceNV (GLuint64 vkFence); +#endif +#endif /* GL_NV_draw_vulkan_image */ + +#ifndef GL_NV_evaluators +#define GL_NV_evaluators 1 +#define GL_EVAL_2D_NV 0x86C0 +#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1 +#define GL_MAP_TESSELLATION_NV 0x86C2 +#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3 +#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4 +#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5 +#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6 +#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7 +#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8 +#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9 +#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA +#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB +#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC +#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD +#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE +#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF +#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0 +#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1 +#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2 +#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3 +#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4 +#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5 +#define GL_MAX_MAP_TESSELLATION_NV 0x86D6 +#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7 + typedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); + typedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); + typedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLEVALMAPSNVPROC) (GLenum target, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); + GLAPI void APIENTRY glMapParameterivNV (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glMapParameterfvNV (GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glGetMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); + GLAPI void APIENTRY glGetMapParameterivNV (GLenum target, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetMapParameterfvNV (GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetMapAttribParameterivNV (GLenum target, GLuint index, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetMapAttribParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glEvalMapsNV (GLenum target, GLenum mode); +#endif +#endif /* GL_NV_evaluators */ + +#ifndef GL_NV_explicit_multisample +#define GL_NV_explicit_multisample 1 +#define GL_SAMPLE_POSITION_NV 0x8E50 +#define GL_SAMPLE_MASK_NV 0x8E51 +#define GL_SAMPLE_MASK_VALUE_NV 0x8E52 +#define GL_TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53 +#define GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54 +#define GL_TEXTURE_RENDERBUFFER_NV 0x8E55 +#define GL_SAMPLER_RENDERBUFFER_NV 0x8E56 +#define GL_INT_SAMPLER_RENDERBUFFER_NV 0x8E57 +#define GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58 +#define GL_MAX_SAMPLE_MASK_WORDS_NV 0x8E59 + typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVNVPROC) (GLenum pname, GLuint index, GLfloat *val); + typedef void (APIENTRYP PFNGLSAMPLEMASKINDEXEDNVPROC) (GLuint index, GLbitfield mask); + typedef void (APIENTRYP PFNGLTEXRENDERBUFFERNVPROC) (GLenum target, GLuint renderbuffer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetMultisamplefvNV (GLenum pname, GLuint index, GLfloat *val); + GLAPI void APIENTRY glSampleMaskIndexedNV (GLuint index, GLbitfield mask); + GLAPI void APIENTRY glTexRenderbufferNV (GLenum target, GLuint renderbuffer); +#endif +#endif /* GL_NV_explicit_multisample */ + +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 + typedef void (APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); + typedef void (APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); + typedef GLboolean (APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); + typedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); + typedef void (APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); + typedef void (APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDeleteFencesNV (GLsizei n, const GLuint *fences); + GLAPI void APIENTRY glGenFencesNV (GLsizei n, GLuint *fences); + GLAPI GLboolean APIENTRY glIsFenceNV (GLuint fence); + GLAPI GLboolean APIENTRY glTestFenceNV (GLuint fence); + GLAPI void APIENTRY glGetFenceivNV (GLuint fence, GLenum pname, GLint *params); + GLAPI void APIENTRY glFinishFenceNV (GLuint fence); + GLAPI void APIENTRY glSetFenceNV (GLuint fence, GLenum condition); +#endif +#endif /* GL_NV_fence */ + +#ifndef GL_NV_fill_rectangle +#define GL_NV_fill_rectangle 1 +#define GL_FILL_RECTANGLE_NV 0x933C +#endif /* GL_NV_fill_rectangle */ + +#ifndef GL_NV_float_buffer +#define GL_NV_float_buffer 1 +#define GL_FLOAT_R_NV 0x8880 +#define GL_FLOAT_RG_NV 0x8881 +#define GL_FLOAT_RGB_NV 0x8882 +#define GL_FLOAT_RGBA_NV 0x8883 +#define GL_FLOAT_R16_NV 0x8884 +#define GL_FLOAT_R32_NV 0x8885 +#define GL_FLOAT_RG16_NV 0x8886 +#define GL_FLOAT_RG32_NV 0x8887 +#define GL_FLOAT_RGB16_NV 0x8888 +#define GL_FLOAT_RGB32_NV 0x8889 +#define GL_FLOAT_RGBA16_NV 0x888A +#define GL_FLOAT_RGBA32_NV 0x888B +#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C +#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D +#define GL_FLOAT_RGBA_MODE_NV 0x888E +#endif /* GL_NV_float_buffer */ + +#ifndef GL_NV_fog_distance +#define GL_NV_fog_distance 1 +#define GL_FOG_DISTANCE_MODE_NV 0x855A +#define GL_EYE_RADIAL_NV 0x855B +#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C +#endif /* GL_NV_fog_distance */ + +#ifndef GL_NV_fragment_coverage_to_color +#define GL_NV_fragment_coverage_to_color 1 +#define GL_FRAGMENT_COVERAGE_TO_COLOR_NV 0x92DD +#define GL_FRAGMENT_COVERAGE_COLOR_NV 0x92DE + typedef void (APIENTRYP PFNGLFRAGMENTCOVERAGECOLORNVPROC) (GLuint color); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFragmentCoverageColorNV (GLuint color); +#endif +#endif /* GL_NV_fragment_coverage_to_color */ + +#ifndef GL_NV_fragment_program +#define GL_NV_fragment_program 1 +#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868 +#define GL_FRAGMENT_PROGRAM_NV 0x8870 +#define GL_MAX_TEXTURE_COORDS_NV 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872 +#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873 +#define GL_PROGRAM_ERROR_STRING_NV 0x8874 + typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); + typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); + typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); + typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProgramNamedParameter4fNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glProgramNamedParameter4fvNV (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); + GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glProgramNamedParameter4dvNV (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); + GLAPI void APIENTRY glGetProgramNamedParameterfvNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); + GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#endif +#endif /* GL_NV_fragment_program */ + +#ifndef GL_NV_fragment_program2 +#define GL_NV_fragment_program2 1 +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6 +#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7 +#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8 +#endif /* GL_NV_fragment_program2 */ + +#ifndef GL_NV_fragment_program4 +#define GL_NV_fragment_program4 1 +#endif /* GL_NV_fragment_program4 */ + +#ifndef GL_NV_fragment_program_option +#define GL_NV_fragment_program_option 1 +#endif /* GL_NV_fragment_program_option */ + +#ifndef GL_NV_fragment_shader_barycentric +#define GL_NV_fragment_shader_barycentric 1 +#endif /* GL_NV_fragment_shader_barycentric */ + +#ifndef GL_NV_fragment_shader_interlock +#define GL_NV_fragment_shader_interlock 1 +#endif /* GL_NV_fragment_shader_interlock */ + +#ifndef GL_NV_framebuffer_mixed_samples +#define GL_NV_framebuffer_mixed_samples 1 +#define GL_COVERAGE_MODULATION_TABLE_NV 0x9331 +#define GL_COLOR_SAMPLES_NV 0x8E20 +#define GL_DEPTH_SAMPLES_NV 0x932D +#define GL_STENCIL_SAMPLES_NV 0x932E +#define GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV 0x932F +#define GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV 0x9330 +#define GL_COVERAGE_MODULATION_NV 0x9332 +#define GL_COVERAGE_MODULATION_TABLE_SIZE_NV 0x9333 + typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONTABLENVPROC) (GLsizei n, const GLfloat *v); + typedef void (APIENTRYP PFNGLGETCOVERAGEMODULATIONTABLENVPROC) (GLsizei bufSize, GLfloat *v); + typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONNVPROC) (GLenum components); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCoverageModulationTableNV (GLsizei n, const GLfloat *v); + GLAPI void APIENTRY glGetCoverageModulationTableNV (GLsizei bufSize, GLfloat *v); + GLAPI void APIENTRY glCoverageModulationNV (GLenum components); +#endif +#endif /* GL_NV_framebuffer_mixed_samples */ + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_NV_framebuffer_multisample_coverage 1 +#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB +#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 +#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 +#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 + typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glRenderbufferStorageMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_NV_framebuffer_multisample_coverage */ + +#ifndef GL_NV_geometry_program4 +#define GL_NV_geometry_program4 1 +#define GL_GEOMETRY_PROGRAM_NV 0x8C26 +#define GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27 +#define GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28 + typedef void (APIENTRYP PFNGLPROGRAMVERTEXLIMITNVPROC) (GLenum target, GLint limit); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProgramVertexLimitNV (GLenum target, GLint limit); + GLAPI void APIENTRY glFramebufferTextureEXT (GLenum target, GLenum attachment, GLuint texture, GLint level); + GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_NV_geometry_program4 */ + +#ifndef GL_NV_geometry_shader4 +#define GL_NV_geometry_shader4 1 +#endif /* GL_NV_geometry_shader4 */ + +#ifndef GL_NV_geometry_shader_passthrough +#define GL_NV_geometry_shader_passthrough 1 +#endif /* GL_NV_geometry_shader_passthrough */ + +#ifndef GL_NV_gpu_multicast +#define GL_NV_gpu_multicast 1 +#define GL_PER_GPU_STORAGE_BIT_NV 0x0800 +#define GL_MULTICAST_GPUS_NV 0x92BA +#define GL_RENDER_GPU_MASK_NV 0x9558 +#define GL_PER_GPU_STORAGE_NV 0x9548 +#define GL_MULTICAST_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9549 + typedef void (APIENTRYP PFNGLRENDERGPUMASKNVPROC) (GLbitfield mask); + typedef void (APIENTRYP PFNGLMULTICASTBUFFERSUBDATANVPROC) (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + typedef void (APIENTRYP PFNGLMULTICASTCOPYBUFFERSUBDATANVPROC) (GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLMULTICASTCOPYIMAGESUBDATANVPROC) (GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); + typedef void (APIENTRYP PFNGLMULTICASTBLITFRAMEBUFFERNVPROC) (GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + typedef void (APIENTRYP PFNGLMULTICASTFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint gpu, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLMULTICASTBARRIERNVPROC) (void); + typedef void (APIENTRYP PFNGLMULTICASTWAITSYNCNVPROC) (GLuint signalGpu, GLbitfield waitGpuMask); + typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTIVNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUIVNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTI64VNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLint64 *params); + typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUI64VNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLuint64 *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glRenderGpuMaskNV (GLbitfield mask); + GLAPI void APIENTRY glMulticastBufferSubDataNV (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); + GLAPI void APIENTRY glMulticastCopyBufferSubDataNV (GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); + GLAPI void APIENTRY glMulticastCopyImageSubDataNV (GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); + GLAPI void APIENTRY glMulticastBlitFramebufferNV (GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + GLAPI void APIENTRY glMulticastFramebufferSampleLocationsfvNV (GLuint gpu, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glMulticastBarrierNV (void); + GLAPI void APIENTRY glMulticastWaitSyncNV (GLuint signalGpu, GLbitfield waitGpuMask); + GLAPI void APIENTRY glMulticastGetQueryObjectivNV (GLuint gpu, GLuint id, GLenum pname, GLint *params); + GLAPI void APIENTRY glMulticastGetQueryObjectuivNV (GLuint gpu, GLuint id, GLenum pname, GLuint *params); + GLAPI void APIENTRY glMulticastGetQueryObjecti64vNV (GLuint gpu, GLuint id, GLenum pname, GLint64 *params); + GLAPI void APIENTRY glMulticastGetQueryObjectui64vNV (GLuint gpu, GLuint id, GLenum pname, GLuint64 *params); +#endif +#endif /* GL_NV_gpu_multicast */ + +#ifndef GL_NV_gpu_program4 +#define GL_NV_gpu_program4 1 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905 +#define GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906 +#define GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907 +#define GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908 +#define GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909 +#define GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5 +#define GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6 + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); + typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); + typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProgramLocalParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); + GLAPI void APIENTRY glProgramLocalParameterI4ivNV (GLenum target, GLuint index, const GLint *params); + GLAPI void APIENTRY glProgramLocalParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); + GLAPI void APIENTRY glProgramLocalParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + GLAPI void APIENTRY glProgramLocalParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); + GLAPI void APIENTRY glProgramLocalParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); + GLAPI void APIENTRY glProgramEnvParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); + GLAPI void APIENTRY glProgramEnvParameterI4ivNV (GLenum target, GLuint index, const GLint *params); + GLAPI void APIENTRY glProgramEnvParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); + GLAPI void APIENTRY glProgramEnvParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + GLAPI void APIENTRY glProgramEnvParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); + GLAPI void APIENTRY glProgramEnvParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); + GLAPI void APIENTRY glGetProgramLocalParameterIivNV (GLenum target, GLuint index, GLint *params); + GLAPI void APIENTRY glGetProgramLocalParameterIuivNV (GLenum target, GLuint index, GLuint *params); + GLAPI void APIENTRY glGetProgramEnvParameterIivNV (GLenum target, GLuint index, GLint *params); + GLAPI void APIENTRY glGetProgramEnvParameterIuivNV (GLenum target, GLuint index, GLuint *params); +#endif +#endif /* GL_NV_gpu_program4 */ + +#ifndef GL_NV_gpu_program5 +#define GL_NV_gpu_program5 1 +#define GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5C +#define GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV 0x8E5D +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5F +#define GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV 0x8F44 +#define GL_MAX_PROGRAM_SUBROUTINE_NUM_NV 0x8F45 + typedef void (APIENTRYP PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC) (GLenum target, GLsizei count, const GLuint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC) (GLenum target, GLuint index, GLuint *param); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProgramSubroutineParametersuivNV (GLenum target, GLsizei count, const GLuint *params); + GLAPI void APIENTRY glGetProgramSubroutineParameteruivNV (GLenum target, GLuint index, GLuint *param); +#endif +#endif /* GL_NV_gpu_program5 */ + +#ifndef GL_NV_gpu_program5_mem_extended +#define GL_NV_gpu_program5_mem_extended 1 +#endif /* GL_NV_gpu_program5_mem_extended */ + +#ifndef GL_NV_gpu_shader5 +#define GL_NV_gpu_shader5 1 +#endif /* GL_NV_gpu_shader5 */ + +#ifndef GL_NV_half_float +#define GL_NV_half_float 1 + typedef unsigned short GLhalfNV; +#define GL_HALF_FLOAT_NV 0x140B + typedef void (APIENTRYP PFNGLVERTEX2HNVPROC) (GLhalfNV x, GLhalfNV y); + typedef void (APIENTRYP PFNGLVERTEX2HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLVERTEX3HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z); + typedef void (APIENTRYP PFNGLVERTEX3HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLVERTEX4HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); + typedef void (APIENTRYP PFNGLVERTEX4HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLNORMAL3HNVPROC) (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); + typedef void (APIENTRYP PFNGLNORMAL3HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); + typedef void (APIENTRYP PFNGLCOLOR3HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLCOLOR4HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); + typedef void (APIENTRYP PFNGLCOLOR4HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC) (GLhalfNV s); + typedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC) (GLhalfNV s, GLhalfNV t); + typedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r); + typedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); + typedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC) (GLenum target, GLhalfNV s); + typedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC) (GLenum target, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t); + typedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC) (GLenum target, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); + typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC) (GLenum target, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); + typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC) (GLenum target, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog); + typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); + typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v); + typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight); + typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC) (GLuint index, GLhalfNV x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC) (GLuint index, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC) (GLuint index, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC) (GLuint index, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC) (GLuint index, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertex2hNV (GLhalfNV x, GLhalfNV y); + GLAPI void APIENTRY glVertex2hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glVertex3hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z); + GLAPI void APIENTRY glVertex3hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glVertex4hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); + GLAPI void APIENTRY glVertex4hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glNormal3hNV (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); + GLAPI void APIENTRY glNormal3hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); + GLAPI void APIENTRY glColor3hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glColor4hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); + GLAPI void APIENTRY glColor4hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glTexCoord1hNV (GLhalfNV s); + GLAPI void APIENTRY glTexCoord1hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glTexCoord2hNV (GLhalfNV s, GLhalfNV t); + GLAPI void APIENTRY glTexCoord2hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glTexCoord3hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r); + GLAPI void APIENTRY glTexCoord3hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glTexCoord4hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); + GLAPI void APIENTRY glTexCoord4hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glMultiTexCoord1hNV (GLenum target, GLhalfNV s); + GLAPI void APIENTRY glMultiTexCoord1hvNV (GLenum target, const GLhalfNV *v); + GLAPI void APIENTRY glMultiTexCoord2hNV (GLenum target, GLhalfNV s, GLhalfNV t); + GLAPI void APIENTRY glMultiTexCoord2hvNV (GLenum target, const GLhalfNV *v); + GLAPI void APIENTRY glMultiTexCoord3hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); + GLAPI void APIENTRY glMultiTexCoord3hvNV (GLenum target, const GLhalfNV *v); + GLAPI void APIENTRY glMultiTexCoord4hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); + GLAPI void APIENTRY glMultiTexCoord4hvNV (GLenum target, const GLhalfNV *v); + GLAPI void APIENTRY glFogCoordhNV (GLhalfNV fog); + GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *fog); + GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); + GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *v); + GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV weight); + GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *weight); + GLAPI void APIENTRY glVertexAttrib1hNV (GLuint index, GLhalfNV x); + GLAPI void APIENTRY glVertexAttrib1hvNV (GLuint index, const GLhalfNV *v); + GLAPI void APIENTRY glVertexAttrib2hNV (GLuint index, GLhalfNV x, GLhalfNV y); + GLAPI void APIENTRY glVertexAttrib2hvNV (GLuint index, const GLhalfNV *v); + GLAPI void APIENTRY glVertexAttrib3hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); + GLAPI void APIENTRY glVertexAttrib3hvNV (GLuint index, const GLhalfNV *v); + GLAPI void APIENTRY glVertexAttrib4hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); + GLAPI void APIENTRY glVertexAttrib4hvNV (GLuint index, const GLhalfNV *v); + GLAPI void APIENTRY glVertexAttribs1hvNV (GLuint index, GLsizei n, const GLhalfNV *v); + GLAPI void APIENTRY glVertexAttribs2hvNV (GLuint index, GLsizei n, const GLhalfNV *v); + GLAPI void APIENTRY glVertexAttribs3hvNV (GLuint index, GLsizei n, const GLhalfNV *v); + GLAPI void APIENTRY glVertexAttribs4hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +#endif +#endif /* GL_NV_half_float */ + +#ifndef GL_NV_internalformat_sample_query +#define GL_NV_internalformat_sample_query 1 +#define GL_MULTISAMPLES_NV 0x9371 +#define GL_SUPERSAMPLE_SCALE_X_NV 0x9372 +#define GL_SUPERSAMPLE_SCALE_Y_NV 0x9373 +#define GL_CONFORMANT_NV 0x9374 + typedef void (APIENTRYP PFNGLGETINTERNALFORMATSAMPLEIVNVPROC) (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetInternalformatSampleivNV (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params); +#endif +#endif /* GL_NV_internalformat_sample_query */ + +#ifndef GL_NV_light_max_exponent +#define GL_NV_light_max_exponent 1 +#define GL_MAX_SHININESS_NV 0x8504 +#define GL_MAX_SPOT_EXPONENT_NV 0x8505 +#endif /* GL_NV_light_max_exponent */ + +#ifndef GL_NV_memory_attachment +#define GL_NV_memory_attachment 1 +#define GL_ATTACHED_MEMORY_OBJECT_NV 0x95A4 +#define GL_ATTACHED_MEMORY_OFFSET_NV 0x95A5 +#define GL_MEMORY_ATTACHABLE_ALIGNMENT_NV 0x95A6 +#define GL_MEMORY_ATTACHABLE_SIZE_NV 0x95A7 +#define GL_MEMORY_ATTACHABLE_NV 0x95A8 +#define GL_DETACHED_MEMORY_INCARNATION_NV 0x95A9 +#define GL_DETACHED_TEXTURES_NV 0x95AA +#define GL_DETACHED_BUFFERS_NV 0x95AB +#define GL_MAX_DETACHED_TEXTURES_NV 0x95AC +#define GL_MAX_DETACHED_BUFFERS_NV 0x95AD + typedef void (APIENTRYP PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC) (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params); + typedef void (APIENTRYP PFNGLRESETMEMORYOBJECTPARAMETERNVPROC) (GLuint memory, GLenum pname); + typedef void (APIENTRYP PFNGLTEXATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLBUFFERATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLTEXTUREATTACHMEMORYNVPROC) (GLuint texture, GLuint memory, GLuint64 offset); + typedef void (APIENTRYP PFNGLNAMEDBUFFERATTACHMEMORYNVPROC) (GLuint buffer, GLuint memory, GLuint64 offset); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetMemoryObjectDetachedResourcesuivNV (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params); + GLAPI void APIENTRY glResetMemoryObjectParameterNV (GLuint memory, GLenum pname); + GLAPI void APIENTRY glTexAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glBufferAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glTextureAttachMemoryNV (GLuint texture, GLuint memory, GLuint64 offset); + GLAPI void APIENTRY glNamedBufferAttachMemoryNV (GLuint buffer, GLuint memory, GLuint64 offset); +#endif +#endif /* GL_NV_memory_attachment */ + +#ifndef GL_NV_memory_object_sparse +#define GL_NV_memory_object_sparse 1 + typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTMEMNVPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); + typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTMEMNVPROC) (GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); + typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTMEMNVPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); + typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTMEMNVPROC) (GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBufferPageCommitmentMemNV (GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); + GLAPI void APIENTRY glTexPageCommitmentMemNV (GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); + GLAPI void APIENTRY glNamedBufferPageCommitmentMemNV (GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); + GLAPI void APIENTRY glTexturePageCommitmentMemNV (GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +#endif +#endif /* GL_NV_memory_object_sparse */ + +#ifndef GL_NV_mesh_shader +#define GL_NV_mesh_shader 1 +#define GL_MESH_SHADER_NV 0x9559 +#define GL_TASK_SHADER_NV 0x955A +#define GL_MAX_MESH_UNIFORM_BLOCKS_NV 0x8E60 +#define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV 0x8E61 +#define GL_MAX_MESH_IMAGE_UNIFORMS_NV 0x8E62 +#define GL_MAX_MESH_UNIFORM_COMPONENTS_NV 0x8E63 +#define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV 0x8E64 +#define GL_MAX_MESH_ATOMIC_COUNTERS_NV 0x8E65 +#define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV 0x8E66 +#define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV 0x8E67 +#define GL_MAX_TASK_UNIFORM_BLOCKS_NV 0x8E68 +#define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV 0x8E69 +#define GL_MAX_TASK_IMAGE_UNIFORMS_NV 0x8E6A +#define GL_MAX_TASK_UNIFORM_COMPONENTS_NV 0x8E6B +#define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV 0x8E6C +#define GL_MAX_TASK_ATOMIC_COUNTERS_NV 0x8E6D +#define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV 0x8E6E +#define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV 0x8E6F +#define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV 0x95A2 +#define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV 0x95A3 +#define GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV 0x9536 +#define GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV 0x9537 +#define GL_MAX_MESH_OUTPUT_VERTICES_NV 0x9538 +#define GL_MAX_MESH_OUTPUT_PRIMITIVES_NV 0x9539 +#define GL_MAX_TASK_OUTPUT_COUNT_NV 0x953A +#define GL_MAX_DRAW_MESH_TASKS_COUNT_NV 0x953D +#define GL_MAX_MESH_VIEWS_NV 0x9557 +#define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV 0x92DF +#define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV 0x9543 +#define GL_MAX_MESH_WORK_GROUP_SIZE_NV 0x953B +#define GL_MAX_TASK_WORK_GROUP_SIZE_NV 0x953C +#define GL_MESH_WORK_GROUP_SIZE_NV 0x953E +#define GL_TASK_WORK_GROUP_SIZE_NV 0x953F +#define GL_MESH_VERTICES_OUT_NV 0x9579 +#define GL_MESH_PRIMITIVES_OUT_NV 0x957A +#define GL_MESH_OUTPUT_TYPE_NV 0x957B +#define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV 0x959C +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV 0x959D +#define GL_REFERENCED_BY_MESH_SHADER_NV 0x95A0 +#define GL_REFERENCED_BY_TASK_SHADER_NV 0x95A1 +#define GL_MESH_SHADER_BIT_NV 0x00000040 +#define GL_TASK_SHADER_BIT_NV 0x00000080 +#define GL_MESH_SUBROUTINE_NV 0x957C +#define GL_TASK_SUBROUTINE_NV 0x957D +#define GL_MESH_SUBROUTINE_UNIFORM_NV 0x957E +#define GL_TASK_SUBROUTINE_UNIFORM_NV 0x957F +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV 0x959E +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV 0x959F + typedef void (APIENTRYP PFNGLDRAWMESHTASKSNVPROC) (GLuint first, GLuint count); + typedef void (APIENTRYP PFNGLDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect); + typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect, GLsizei drawcount, GLsizei stride); + typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC) (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawMeshTasksNV (GLuint first, GLuint count); + GLAPI void APIENTRY glDrawMeshTasksIndirectNV (GLintptr indirect); + GLAPI void APIENTRY glMultiDrawMeshTasksIndirectNV (GLintptr indirect, GLsizei drawcount, GLsizei stride); + GLAPI void APIENTRY glMultiDrawMeshTasksIndirectCountNV (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_NV_mesh_shader */ + +#ifndef GL_NV_multisample_coverage +#define GL_NV_multisample_coverage 1 +#endif /* GL_NV_multisample_coverage */ + +#ifndef GL_NV_multisample_filter_hint +#define GL_NV_multisample_filter_hint 1 +#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 +#endif /* GL_NV_multisample_filter_hint */ + +#ifndef GL_NV_occlusion_query +#define GL_NV_occlusion_query 1 +#define GL_PIXEL_COUNTER_BITS_NV 0x8864 +#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 +#define GL_PIXEL_COUNT_NV 0x8866 +#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 + typedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids); + typedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids); + typedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC) (GLuint id); + typedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC) (GLuint id); + typedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC) (void); + typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei n, GLuint *ids); + GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei n, const GLuint *ids); + GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint id); + GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint id); + GLAPI void APIENTRY glEndOcclusionQueryNV (void); + GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint id, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint id, GLenum pname, GLuint *params); +#endif +#endif /* GL_NV_occlusion_query */ + +#ifndef GL_NV_packed_depth_stencil +#define GL_NV_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_NV 0x84F9 +#define GL_UNSIGNED_INT_24_8_NV 0x84FA +#endif /* GL_NV_packed_depth_stencil */ + +#ifndef GL_NV_parameter_buffer_object +#define GL_NV_parameter_buffer_object 1 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1 +#define GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2 +#define GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3 +#define GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4 + typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); + typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); + typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glProgramBufferParametersfvNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); + GLAPI void APIENTRY glProgramBufferParametersIivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); + GLAPI void APIENTRY glProgramBufferParametersIuivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); +#endif +#endif /* GL_NV_parameter_buffer_object */ + +#ifndef GL_NV_parameter_buffer_object2 +#define GL_NV_parameter_buffer_object2 1 +#endif /* GL_NV_parameter_buffer_object2 */ + +#ifndef GL_NV_path_rendering +#define GL_NV_path_rendering 1 +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D + typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); + typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); + typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path); + typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); + typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); + typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); + typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); + typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); + typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); + typedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); + typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); + typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); + typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); + typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); + typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); + typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); + typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); + typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); + typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); + typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); + typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); + typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); + typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); + typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); + typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); + typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); + typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); + typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); + typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); + typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); + typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); + typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); + typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); + typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); + typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); + typedef void (APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); + typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); + typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); + typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount); + typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + typedef GLenum (APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + typedef void (APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); + typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLfloat *params); + typedef void (APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); + typedef void (APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); + typedef void (APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); + typedef void (APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); + typedef void (APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); + typedef void (APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); + typedef void (APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLuint APIENTRY glGenPathsNV (GLsizei range); + GLAPI void APIENTRY glDeletePathsNV (GLuint path, GLsizei range); + GLAPI GLboolean APIENTRY glIsPathNV (GLuint path); + GLAPI void APIENTRY glPathCommandsNV (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); + GLAPI void APIENTRY glPathCoordsNV (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); + GLAPI void APIENTRY glPathSubCommandsNV (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); + GLAPI void APIENTRY glPathSubCoordsNV (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); + GLAPI void APIENTRY glPathStringNV (GLuint path, GLenum format, GLsizei length, const void *pathString); + GLAPI void APIENTRY glPathGlyphsNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + GLAPI void APIENTRY glPathGlyphRangeNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + GLAPI void APIENTRY glWeightPathsNV (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); + GLAPI void APIENTRY glCopyPathNV (GLuint resultPath, GLuint srcPath); + GLAPI void APIENTRY glInterpolatePathsNV (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); + GLAPI void APIENTRY glTransformPathNV (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glPathParameterivNV (GLuint path, GLenum pname, const GLint *value); + GLAPI void APIENTRY glPathParameteriNV (GLuint path, GLenum pname, GLint value); + GLAPI void APIENTRY glPathParameterfvNV (GLuint path, GLenum pname, const GLfloat *value); + GLAPI void APIENTRY glPathParameterfNV (GLuint path, GLenum pname, GLfloat value); + GLAPI void APIENTRY glPathDashArrayNV (GLuint path, GLsizei dashCount, const GLfloat *dashArray); + GLAPI void APIENTRY glPathStencilFuncNV (GLenum func, GLint ref, GLuint mask); + GLAPI void APIENTRY glPathStencilDepthOffsetNV (GLfloat factor, GLfloat units); + GLAPI void APIENTRY glStencilFillPathNV (GLuint path, GLenum fillMode, GLuint mask); + GLAPI void APIENTRY glStencilStrokePathNV (GLuint path, GLint reference, GLuint mask); + GLAPI void APIENTRY glStencilFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glStencilStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glPathCoverDepthFuncNV (GLenum func); + GLAPI void APIENTRY glCoverFillPathNV (GLuint path, GLenum coverMode); + GLAPI void APIENTRY glCoverStrokePathNV (GLuint path, GLenum coverMode); + GLAPI void APIENTRY glCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glGetPathParameterivNV (GLuint path, GLenum pname, GLint *value); + GLAPI void APIENTRY glGetPathParameterfvNV (GLuint path, GLenum pname, GLfloat *value); + GLAPI void APIENTRY glGetPathCommandsNV (GLuint path, GLubyte *commands); + GLAPI void APIENTRY glGetPathCoordsNV (GLuint path, GLfloat *coords); + GLAPI void APIENTRY glGetPathDashArrayNV (GLuint path, GLfloat *dashArray); + GLAPI void APIENTRY glGetPathMetricsNV (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); + GLAPI void APIENTRY glGetPathMetricRangeNV (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); + GLAPI void APIENTRY glGetPathSpacingNV (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); + GLAPI GLboolean APIENTRY glIsPointInFillPathNV (GLuint path, GLuint mask, GLfloat x, GLfloat y); + GLAPI GLboolean APIENTRY glIsPointInStrokePathNV (GLuint path, GLfloat x, GLfloat y); + GLAPI GLfloat APIENTRY glGetPathLengthNV (GLuint path, GLsizei startSegment, GLsizei numSegments); + GLAPI GLboolean APIENTRY glPointAlongPathNV (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); + GLAPI void APIENTRY glMatrixLoad3x2fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glMatrixLoad3x3fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glMatrixLoadTranspose3x3fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glMatrixMult3x2fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glMatrixMult3x3fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glMatrixMultTranspose3x3fNV (GLenum matrixMode, const GLfloat *m); + GLAPI void APIENTRY glStencilThenCoverFillPathNV (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); + GLAPI void APIENTRY glStencilThenCoverStrokePathNV (GLuint path, GLint reference, GLuint mask, GLenum coverMode); + GLAPI void APIENTRY glStencilThenCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + GLAPI void APIENTRY glStencilThenCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); + GLAPI GLenum APIENTRY glPathGlyphIndexRangeNV (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount); + GLAPI GLenum APIENTRY glPathGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + GLAPI GLenum APIENTRY glPathMemoryGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); + GLAPI void APIENTRY glProgramPathFragmentInputGenNV (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); + GLAPI void APIENTRY glGetProgramResourcefvNV (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLfloat *params); + GLAPI void APIENTRY glPathColorGenNV (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); + GLAPI void APIENTRY glPathTexGenNV (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); + GLAPI void APIENTRY glPathFogGenNV (GLenum genMode); + GLAPI void APIENTRY glGetPathColorGenivNV (GLenum color, GLenum pname, GLint *value); + GLAPI void APIENTRY glGetPathColorGenfvNV (GLenum color, GLenum pname, GLfloat *value); + GLAPI void APIENTRY glGetPathTexGenivNV (GLenum texCoordSet, GLenum pname, GLint *value); + GLAPI void APIENTRY glGetPathTexGenfvNV (GLenum texCoordSet, GLenum pname, GLfloat *value); +#endif +#endif /* GL_NV_path_rendering */ + +#ifndef GL_NV_path_rendering_shared_edge +#define GL_NV_path_rendering_shared_edge 1 +#define GL_SHARED_EDGE_NV 0xC0 +#endif /* GL_NV_path_rendering_shared_edge */ + +#ifndef GL_NV_pixel_data_range +#define GL_NV_pixel_data_range 1 +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 +#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879 +#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A +#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B +#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C +#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D + typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, const void *pointer); + typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPixelDataRangeNV (GLenum target, GLsizei length, const void *pointer); + GLAPI void APIENTRY glFlushPixelDataRangeNV (GLenum target); +#endif +#endif /* GL_NV_pixel_data_range */ + +#ifndef GL_NV_point_sprite +#define GL_NV_point_sprite 1 +#define GL_POINT_SPRITE_NV 0x8861 +#define GL_COORD_REPLACE_NV 0x8862 +#define GL_POINT_SPRITE_R_MODE_NV 0x8863 + typedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC) (GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPointParameteriNV (GLenum pname, GLint param); + GLAPI void APIENTRY glPointParameterivNV (GLenum pname, const GLint *params); +#endif +#endif /* GL_NV_point_sprite */ + +#ifndef GL_NV_present_video +#define GL_NV_present_video 1 +#define GL_FRAME_NV 0x8E26 +#define GL_FIELDS_NV 0x8E27 +#define GL_CURRENT_TIME_NV 0x8E28 +#define GL_NUM_FILL_STREAMS_NV 0x8E29 +#define GL_PRESENT_TIME_NV 0x8E2A +#define GL_PRESENT_DURATION_NV 0x8E2B + typedef void (APIENTRYP PFNGLPRESENTFRAMEKEYEDNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); + typedef void (APIENTRYP PFNGLPRESENTFRAMEDUALFILLNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); + typedef void (APIENTRYP PFNGLGETVIDEOIVNVPROC) (GLuint video_slot, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVIDEOUIVNVPROC) (GLuint video_slot, GLenum pname, GLuint *params); + typedef void (APIENTRYP PFNGLGETVIDEOI64VNVPROC) (GLuint video_slot, GLenum pname, GLint64EXT *params); + typedef void (APIENTRYP PFNGLGETVIDEOUI64VNVPROC) (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPresentFrameKeyedNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); + GLAPI void APIENTRY glPresentFrameDualFillNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); + GLAPI void APIENTRY glGetVideoivNV (GLuint video_slot, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVideouivNV (GLuint video_slot, GLenum pname, GLuint *params); + GLAPI void APIENTRY glGetVideoi64vNV (GLuint video_slot, GLenum pname, GLint64EXT *params); + GLAPI void APIENTRY glGetVideoui64vNV (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_NV_present_video */ + +#ifndef GL_NV_primitive_restart +#define GL_NV_primitive_restart 1 +#define GL_PRIMITIVE_RESTART_NV 0x8558 +#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 + typedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC) (void); + typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC) (GLuint index); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPrimitiveRestartNV (void); + GLAPI void APIENTRY glPrimitiveRestartIndexNV (GLuint index); +#endif +#endif /* GL_NV_primitive_restart */ + +#ifndef GL_NV_primitive_shading_rate +#define GL_NV_primitive_shading_rate 1 +#define GL_SHADING_RATE_IMAGE_PER_PRIMITIVE_NV 0x95B1 +#define GL_SHADING_RATE_IMAGE_PALETTE_COUNT_NV 0x95B2 +#endif /* GL_NV_primitive_shading_rate */ + +#ifndef GL_NV_query_resource +#define GL_NV_query_resource 1 +#define GL_QUERY_RESOURCE_TYPE_VIDMEM_ALLOC_NV 0x9540 +#define GL_QUERY_RESOURCE_MEMTYPE_VIDMEM_NV 0x9542 +#define GL_QUERY_RESOURCE_SYS_RESERVED_NV 0x9544 +#define GL_QUERY_RESOURCE_TEXTURE_NV 0x9545 +#define GL_QUERY_RESOURCE_RENDERBUFFER_NV 0x9546 +#define GL_QUERY_RESOURCE_BUFFEROBJECT_NV 0x9547 + typedef GLint (APIENTRYP PFNGLQUERYRESOURCENVPROC) (GLenum queryType, GLint tagId, GLuint count, GLint *buffer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLint APIENTRY glQueryResourceNV (GLenum queryType, GLint tagId, GLuint count, GLint *buffer); +#endif +#endif /* GL_NV_query_resource */ + +#ifndef GL_NV_query_resource_tag +#define GL_NV_query_resource_tag 1 + typedef void (APIENTRYP PFNGLGENQUERYRESOURCETAGNVPROC) (GLsizei n, GLint *tagIds); + typedef void (APIENTRYP PFNGLDELETEQUERYRESOURCETAGNVPROC) (GLsizei n, const GLint *tagIds); + typedef void (APIENTRYP PFNGLQUERYRESOURCETAGNVPROC) (GLint tagId, const GLchar *tagString); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGenQueryResourceTagNV (GLsizei n, GLint *tagIds); + GLAPI void APIENTRY glDeleteQueryResourceTagNV (GLsizei n, const GLint *tagIds); + GLAPI void APIENTRY glQueryResourceTagNV (GLint tagId, const GLchar *tagString); +#endif +#endif /* GL_NV_query_resource_tag */ + +#ifndef GL_NV_register_combiners +#define GL_NV_register_combiners 1 +#define GL_REGISTER_COMBINERS_NV 0x8522 +#define GL_VARIABLE_A_NV 0x8523 +#define GL_VARIABLE_B_NV 0x8524 +#define GL_VARIABLE_C_NV 0x8525 +#define GL_VARIABLE_D_NV 0x8526 +#define GL_VARIABLE_E_NV 0x8527 +#define GL_VARIABLE_F_NV 0x8528 +#define GL_VARIABLE_G_NV 0x8529 +#define GL_CONSTANT_COLOR0_NV 0x852A +#define GL_CONSTANT_COLOR1_NV 0x852B +#define GL_SPARE0_NV 0x852E +#define GL_SPARE1_NV 0x852F +#define GL_DISCARD_NV 0x8530 +#define GL_E_TIMES_F_NV 0x8531 +#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532 +#define GL_UNSIGNED_IDENTITY_NV 0x8536 +#define GL_UNSIGNED_INVERT_NV 0x8537 +#define GL_EXPAND_NORMAL_NV 0x8538 +#define GL_EXPAND_NEGATE_NV 0x8539 +#define GL_HALF_BIAS_NORMAL_NV 0x853A +#define GL_HALF_BIAS_NEGATE_NV 0x853B +#define GL_SIGNED_IDENTITY_NV 0x853C +#define GL_SIGNED_NEGATE_NV 0x853D +#define GL_SCALE_BY_TWO_NV 0x853E +#define GL_SCALE_BY_FOUR_NV 0x853F +#define GL_SCALE_BY_ONE_HALF_NV 0x8540 +#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541 +#define GL_COMBINER_INPUT_NV 0x8542 +#define GL_COMBINER_MAPPING_NV 0x8543 +#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544 +#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545 +#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546 +#define GL_COMBINER_MUX_SUM_NV 0x8547 +#define GL_COMBINER_SCALE_NV 0x8548 +#define GL_COMBINER_BIAS_NV 0x8549 +#define GL_COMBINER_AB_OUTPUT_NV 0x854A +#define GL_COMBINER_CD_OUTPUT_NV 0x854B +#define GL_COMBINER_SUM_OUTPUT_NV 0x854C +#define GL_MAX_GENERAL_COMBINERS_NV 0x854D +#define GL_NUM_GENERAL_COMBINERS_NV 0x854E +#define GL_COLOR_SUM_CLAMP_NV 0x854F +#define GL_COMBINER0_NV 0x8550 +#define GL_COMBINER1_NV 0x8551 +#define GL_COMBINER2_NV 0x8552 +#define GL_COMBINER3_NV 0x8553 +#define GL_COMBINER4_NV 0x8554 +#define GL_COMBINER5_NV 0x8555 +#define GL_COMBINER6_NV 0x8556 +#define GL_COMBINER7_NV 0x8557 + typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC) (GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC) (GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC) (GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC) (GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); + typedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC) (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); + typedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC) (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); + typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) (GLenum variable, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) (GLenum variable, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCombinerParameterfvNV (GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glCombinerParameterfNV (GLenum pname, GLfloat param); + GLAPI void APIENTRY glCombinerParameterivNV (GLenum pname, const GLint *params); + GLAPI void APIENTRY glCombinerParameteriNV (GLenum pname, GLint param); + GLAPI void APIENTRY glCombinerInputNV (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); + GLAPI void APIENTRY glCombinerOutputNV (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); + GLAPI void APIENTRY glFinalCombinerInputNV (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); + GLAPI void APIENTRY glGetCombinerInputParameterfvNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetCombinerInputParameterivNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetCombinerOutputParameterfvNV (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetCombinerOutputParameterivNV (GLenum stage, GLenum portion, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetFinalCombinerInputParameterfvNV (GLenum variable, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetFinalCombinerInputParameterivNV (GLenum variable, GLenum pname, GLint *params); +#endif +#endif /* GL_NV_register_combiners */ + +#ifndef GL_NV_register_combiners2 +#define GL_NV_register_combiners2 1 +#define GL_PER_STAGE_CONSTANTS_NV 0x8535 + typedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCombinerStageParameterfvNV (GLenum stage, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glGetCombinerStageParameterfvNV (GLenum stage, GLenum pname, GLfloat *params); +#endif +#endif /* GL_NV_register_combiners2 */ + +#ifndef GL_NV_representative_fragment_test +#define GL_NV_representative_fragment_test 1 +#define GL_REPRESENTATIVE_FRAGMENT_TEST_NV 0x937F +#endif /* GL_NV_representative_fragment_test */ + +#ifndef GL_NV_robustness_video_memory_purge +#define GL_NV_robustness_video_memory_purge 1 +#define GL_PURGED_CONTEXT_RESET_NV 0x92BB +#endif /* GL_NV_robustness_video_memory_purge */ + +#ifndef GL_NV_sample_locations +#define GL_NV_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV 0x9340 +#define GL_SAMPLE_LOCATION_NV 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV 0x9343 + typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLRESOLVEDEPTHVALUESNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferSampleLocationsfvNV (GLenum target, GLuint start, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glNamedFramebufferSampleLocationsfvNV (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glResolveDepthValuesNV (void); +#endif +#endif /* GL_NV_sample_locations */ + +#ifndef GL_NV_sample_mask_override_coverage +#define GL_NV_sample_mask_override_coverage 1 +#endif /* GL_NV_sample_mask_override_coverage */ + +#ifndef GL_NV_scissor_exclusive +#define GL_NV_scissor_exclusive 1 +#define GL_SCISSOR_TEST_EXCLUSIVE_NV 0x9555 +#define GL_SCISSOR_BOX_EXCLUSIVE_NV 0x9556 + typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVENVPROC) (GLint x, GLint y, GLsizei width, GLsizei height); + typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVEARRAYVNVPROC) (GLuint first, GLsizei count, const GLint *v); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glScissorExclusiveNV (GLint x, GLint y, GLsizei width, GLsizei height); + GLAPI void APIENTRY glScissorExclusiveArrayvNV (GLuint first, GLsizei count, const GLint *v); +#endif +#endif /* GL_NV_scissor_exclusive */ + +#ifndef GL_NV_shader_atomic_counters +#define GL_NV_shader_atomic_counters 1 +#endif /* GL_NV_shader_atomic_counters */ + +#ifndef GL_NV_shader_atomic_float +#define GL_NV_shader_atomic_float 1 +#endif /* GL_NV_shader_atomic_float */ + +#ifndef GL_NV_shader_atomic_float64 +#define GL_NV_shader_atomic_float64 1 +#endif /* GL_NV_shader_atomic_float64 */ + +#ifndef GL_NV_shader_atomic_fp16_vector +#define GL_NV_shader_atomic_fp16_vector 1 +#endif /* GL_NV_shader_atomic_fp16_vector */ + +#ifndef GL_NV_shader_atomic_int64 +#define GL_NV_shader_atomic_int64 1 +#endif /* GL_NV_shader_atomic_int64 */ + +#ifndef GL_NV_shader_buffer_load +#define GL_NV_shader_buffer_load 1 +#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D +#define GL_GPU_ADDRESS_NV 0x8F34 +#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 + typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access); + typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target); + typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target); + typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access); + typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer); + typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer); + typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params); + typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params); + typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result); + typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value); + typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value); + typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glMakeBufferResidentNV (GLenum target, GLenum access); + GLAPI void APIENTRY glMakeBufferNonResidentNV (GLenum target); + GLAPI GLboolean APIENTRY glIsBufferResidentNV (GLenum target); + GLAPI void APIENTRY glMakeNamedBufferResidentNV (GLuint buffer, GLenum access); + GLAPI void APIENTRY glMakeNamedBufferNonResidentNV (GLuint buffer); + GLAPI GLboolean APIENTRY glIsNamedBufferResidentNV (GLuint buffer); + GLAPI void APIENTRY glGetBufferParameterui64vNV (GLenum target, GLenum pname, GLuint64EXT *params); + GLAPI void APIENTRY glGetNamedBufferParameterui64vNV (GLuint buffer, GLenum pname, GLuint64EXT *params); + GLAPI void APIENTRY glGetIntegerui64vNV (GLenum value, GLuint64EXT *result); + GLAPI void APIENTRY glUniformui64NV (GLint location, GLuint64EXT value); + GLAPI void APIENTRY glUniformui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); + GLAPI void APIENTRY glProgramUniformui64NV (GLuint program, GLint location, GLuint64EXT value); + GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_shader_buffer_load */ + +#ifndef GL_NV_shader_buffer_store +#define GL_NV_shader_buffer_store 1 +#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 +#endif /* GL_NV_shader_buffer_store */ + +#ifndef GL_NV_shader_storage_buffer_object +#define GL_NV_shader_storage_buffer_object 1 +#endif /* GL_NV_shader_storage_buffer_object */ + +#ifndef GL_NV_shader_subgroup_partitioned +#define GL_NV_shader_subgroup_partitioned 1 +#define GL_SUBGROUP_FEATURE_PARTITIONED_BIT_NV 0x00000100 +#endif /* GL_NV_shader_subgroup_partitioned */ + +#ifndef GL_NV_shader_texture_footprint +#define GL_NV_shader_texture_footprint 1 +#endif /* GL_NV_shader_texture_footprint */ + +#ifndef GL_NV_shader_thread_group +#define GL_NV_shader_thread_group 1 +#define GL_WARP_SIZE_NV 0x9339 +#define GL_WARPS_PER_SM_NV 0x933A +#define GL_SM_COUNT_NV 0x933B +#endif /* GL_NV_shader_thread_group */ + +#ifndef GL_NV_shader_thread_shuffle +#define GL_NV_shader_thread_shuffle 1 +#endif /* GL_NV_shader_thread_shuffle */ + +#ifndef GL_NV_shading_rate_image +#define GL_NV_shading_rate_image 1 +#define GL_SHADING_RATE_IMAGE_NV 0x9563 +#define GL_SHADING_RATE_NO_INVOCATIONS_NV 0x9564 +#define GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV 0x9565 +#define GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV 0x9566 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV 0x9567 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV 0x9568 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV 0x9569 +#define GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV 0x956A +#define GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV 0x956B +#define GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV 0x956C +#define GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV 0x956D +#define GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV 0x956E +#define GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV 0x956F +#define GL_SHADING_RATE_IMAGE_BINDING_NV 0x955B +#define GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV 0x955C +#define GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV 0x955D +#define GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV 0x955E +#define GL_MAX_COARSE_FRAGMENT_SAMPLES_NV 0x955F +#define GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV 0x95AE +#define GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV 0x95AF +#define GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV 0x95B0 + typedef void (APIENTRYP PFNGLBINDSHADINGRATEIMAGENVPROC) (GLuint texture); + typedef void (APIENTRYP PFNGLGETSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint entry, GLenum *rate); + typedef void (APIENTRYP PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC) (GLenum rate, GLuint samples, GLuint index, GLint *location); + typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEBARRIERNVPROC) (GLboolean synchronize); + typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates); + typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERNVPROC) (GLenum order); + typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC) (GLenum rate, GLuint samples, const GLint *locations); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBindShadingRateImageNV (GLuint texture); + GLAPI void APIENTRY glGetShadingRateImagePaletteNV (GLuint viewport, GLuint entry, GLenum *rate); + GLAPI void APIENTRY glGetShadingRateSampleLocationivNV (GLenum rate, GLuint samples, GLuint index, GLint *location); + GLAPI void APIENTRY glShadingRateImageBarrierNV (GLboolean synchronize); + GLAPI void APIENTRY glShadingRateImagePaletteNV (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates); + GLAPI void APIENTRY glShadingRateSampleOrderNV (GLenum order); + GLAPI void APIENTRY glShadingRateSampleOrderCustomNV (GLenum rate, GLuint samples, const GLint *locations); +#endif +#endif /* GL_NV_shading_rate_image */ + +#ifndef GL_NV_stereo_view_rendering +#define GL_NV_stereo_view_rendering 1 +#endif /* GL_NV_stereo_view_rendering */ + +#ifndef GL_NV_tessellation_program5 +#define GL_NV_tessellation_program5 1 +#define GL_MAX_PROGRAM_PATCH_ATTRIBS_NV 0x86D8 +#define GL_TESS_CONTROL_PROGRAM_NV 0x891E +#define GL_TESS_EVALUATION_PROGRAM_NV 0x891F +#define GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV 0x8C74 +#define GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV 0x8C75 +#endif /* GL_NV_tessellation_program5 */ + +#ifndef GL_NV_texgen_emboss +#define GL_NV_texgen_emboss 1 +#define GL_EMBOSS_LIGHT_NV 0x855D +#define GL_EMBOSS_CONSTANT_NV 0x855E +#define GL_EMBOSS_MAP_NV 0x855F +#endif /* GL_NV_texgen_emboss */ + +#ifndef GL_NV_texgen_reflection +#define GL_NV_texgen_reflection 1 +#define GL_NORMAL_MAP_NV 0x8511 +#define GL_REFLECTION_MAP_NV 0x8512 +#endif /* GL_NV_texgen_reflection */ + +#ifndef GL_NV_texture_barrier +#define GL_NV_texture_barrier 1 + typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTextureBarrierNV (void); +#endif +#endif /* GL_NV_texture_barrier */ + +#ifndef GL_NV_texture_compression_vtc +#define GL_NV_texture_compression_vtc 1 +#endif /* GL_NV_texture_compression_vtc */ + +#ifndef GL_NV_texture_env_combine4 +#define GL_NV_texture_env_combine4 1 +#define GL_COMBINE4_NV 0x8503 +#define GL_SOURCE3_RGB_NV 0x8583 +#define GL_SOURCE3_ALPHA_NV 0x858B +#define GL_OPERAND3_RGB_NV 0x8593 +#define GL_OPERAND3_ALPHA_NV 0x859B +#endif /* GL_NV_texture_env_combine4 */ + +#ifndef GL_NV_texture_expand_normal +#define GL_NV_texture_expand_normal 1 +#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F +#endif /* GL_NV_texture_expand_normal */ + +#ifndef GL_NV_texture_multisample +#define GL_NV_texture_multisample 1 +#define GL_TEXTURE_COVERAGE_SAMPLES_NV 0x9045 +#define GL_TEXTURE_COLOR_SAMPLES_NV 0x9046 + typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexImage2DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + GLAPI void APIENTRY glTexImage3DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + GLAPI void APIENTRY glTextureImage2DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + GLAPI void APIENTRY glTextureImage3DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + GLAPI void APIENTRY glTextureImage2DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + GLAPI void APIENTRY glTextureImage3DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#endif +#endif /* GL_NV_texture_multisample */ + +#ifndef GL_NV_texture_rectangle +#define GL_NV_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_NV 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8 +#endif /* GL_NV_texture_rectangle */ + +#ifndef GL_NV_texture_rectangle_compressed +#define GL_NV_texture_rectangle_compressed 1 +#endif /* GL_NV_texture_rectangle_compressed */ + +#ifndef GL_NV_texture_shader +#define GL_NV_texture_shader 1 +#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C +#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D +#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E +#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9 +#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA +#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB +#define GL_DSDT_MAG_INTENSITY_NV 0x86DC +#define GL_SHADER_CONSISTENT_NV 0x86DD +#define GL_TEXTURE_SHADER_NV 0x86DE +#define GL_SHADER_OPERATION_NV 0x86DF +#define GL_CULL_MODES_NV 0x86E0 +#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 +#define GL_OFFSET_TEXTURE_2D_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_2D_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_2D_BIAS_NV 0x86E3 +#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 +#define GL_CONST_EYE_NV 0x86E5 +#define GL_PASS_THROUGH_NV 0x86E6 +#define GL_CULL_FRAGMENT_NV 0x86E7 +#define GL_OFFSET_TEXTURE_2D_NV 0x86E8 +#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9 +#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA +#define GL_DOT_PRODUCT_NV 0x86EC +#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED +#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE +#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0 +#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1 +#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2 +#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3 +#define GL_HILO_NV 0x86F4 +#define GL_DSDT_NV 0x86F5 +#define GL_DSDT_MAG_NV 0x86F6 +#define GL_DSDT_MAG_VIB_NV 0x86F7 +#define GL_HILO16_NV 0x86F8 +#define GL_SIGNED_HILO_NV 0x86F9 +#define GL_SIGNED_HILO16_NV 0x86FA +#define GL_SIGNED_RGBA_NV 0x86FB +#define GL_SIGNED_RGBA8_NV 0x86FC +#define GL_SIGNED_RGB_NV 0x86FE +#define GL_SIGNED_RGB8_NV 0x86FF +#define GL_SIGNED_LUMINANCE_NV 0x8701 +#define GL_SIGNED_LUMINANCE8_NV 0x8702 +#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 +#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 +#define GL_SIGNED_ALPHA_NV 0x8705 +#define GL_SIGNED_ALPHA8_NV 0x8706 +#define GL_SIGNED_INTENSITY_NV 0x8707 +#define GL_SIGNED_INTENSITY8_NV 0x8708 +#define GL_DSDT8_NV 0x8709 +#define GL_DSDT8_MAG8_NV 0x870A +#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B +#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C +#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D +#define GL_HI_SCALE_NV 0x870E +#define GL_LO_SCALE_NV 0x870F +#define GL_DS_SCALE_NV 0x8710 +#define GL_DT_SCALE_NV 0x8711 +#define GL_MAGNITUDE_SCALE_NV 0x8712 +#define GL_VIBRANCE_SCALE_NV 0x8713 +#define GL_HI_BIAS_NV 0x8714 +#define GL_LO_BIAS_NV 0x8715 +#define GL_DS_BIAS_NV 0x8716 +#define GL_DT_BIAS_NV 0x8717 +#define GL_MAGNITUDE_BIAS_NV 0x8718 +#define GL_VIBRANCE_BIAS_NV 0x8719 +#define GL_TEXTURE_BORDER_VALUES_NV 0x871A +#define GL_TEXTURE_HI_SIZE_NV 0x871B +#define GL_TEXTURE_LO_SIZE_NV 0x871C +#define GL_TEXTURE_DS_SIZE_NV 0x871D +#define GL_TEXTURE_DT_SIZE_NV 0x871E +#define GL_TEXTURE_MAG_SIZE_NV 0x871F +#endif /* GL_NV_texture_shader */ + +#ifndef GL_NV_texture_shader2 +#define GL_NV_texture_shader2 1 +#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF +#endif /* GL_NV_texture_shader2 */ + +#ifndef GL_NV_texture_shader3 +#define GL_NV_texture_shader3 1 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853 +#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854 +#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857 +#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858 +#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859 +#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A +#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B +#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C +#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D +#define GL_HILO8_NV 0x885E +#define GL_SIGNED_HILO8_NV 0x885F +#define GL_FORCE_BLUE_TO_ONE_NV 0x8860 +#endif /* GL_NV_texture_shader3 */ + +#ifndef GL_NV_timeline_semaphore +#define GL_NV_timeline_semaphore 1 +#define GL_TIMELINE_SEMAPHORE_VALUE_NV 0x9595 +#define GL_SEMAPHORE_TYPE_NV 0x95B3 +#define GL_SEMAPHORE_TYPE_BINARY_NV 0x95B4 +#define GL_SEMAPHORE_TYPE_TIMELINE_NV 0x95B5 +#define GL_MAX_TIMELINE_SEMAPHORE_VALUE_DIFFERENCE_NV 0x95B6 + typedef void (APIENTRYP PFNGLCREATESEMAPHORESNVPROC) (GLsizei n, GLuint *semaphores); + typedef void (APIENTRYP PFNGLSEMAPHOREPARAMETERIVNVPROC) (GLuint semaphore, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLGETSEMAPHOREPARAMETERIVNVPROC) (GLuint semaphore, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glCreateSemaphoresNV (GLsizei n, GLuint *semaphores); + GLAPI void APIENTRY glSemaphoreParameterivNV (GLuint semaphore, GLenum pname, const GLint *params); + GLAPI void APIENTRY glGetSemaphoreParameterivNV (GLuint semaphore, GLenum pname, GLint *params); +#endif +#endif /* GL_NV_timeline_semaphore */ + +#ifndef GL_NV_transform_feedback +#define GL_NV_transform_feedback 1 +#define GL_BACK_PRIMARY_COLOR_NV 0x8C77 +#define GL_BACK_SECONDARY_COLOR_NV 0x8C78 +#define GL_TEXTURE_COORD_NV 0x8C79 +#define GL_CLIP_DISTANCE_NV 0x8C7A +#define GL_VERTEX_ID_NV 0x8C7B +#define GL_PRIMITIVE_ID_NV 0x8C7C +#define GL_GENERIC_ATTRIB_NV 0x8C7D +#define GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80 +#define GL_ACTIVE_VARYINGS_NV 0x8C81 +#define GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85 +#define GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86 +#define GL_PRIMITIVES_GENERATED_NV 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88 +#define GL_RASTERIZER_DISCARD_NV 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B +#define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C +#define GL_SEPARATE_ATTRIBS_NV 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F +#define GL_LAYER_NV 0x8DAA +#define GL_NEXT_BUFFER_NV -2 +#define GL_SKIP_COMPONENTS4_NV -3 +#define GL_SKIP_COMPONENTS3_NV -4 +#define GL_SKIP_COMPONENTS2_NV -5 +#define GL_SKIP_COMPONENTS1_NV -6 + typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKNVPROC) (GLenum primitiveMode); + typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKNVPROC) (void); + typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC) (GLsizei count, const GLint *attribs, GLenum bufferMode); + typedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); + typedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC) (GLenum target, GLuint index, GLuint buffer); + typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC) (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); + typedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC) (GLuint program, const GLchar *name); + typedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC) (GLuint program, const GLchar *name); + typedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); + typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC) (GLuint program, GLuint index, GLint *location); + typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC) (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBeginTransformFeedbackNV (GLenum primitiveMode); + GLAPI void APIENTRY glEndTransformFeedbackNV (void); + GLAPI void APIENTRY glTransformFeedbackAttribsNV (GLsizei count, const GLint *attribs, GLenum bufferMode); + GLAPI void APIENTRY glBindBufferRangeNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + GLAPI void APIENTRY glBindBufferOffsetNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset); + GLAPI void APIENTRY glBindBufferBaseNV (GLenum target, GLuint index, GLuint buffer); + GLAPI void APIENTRY glTransformFeedbackVaryingsNV (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); + GLAPI void APIENTRY glActiveVaryingNV (GLuint program, const GLchar *name); + GLAPI GLint APIENTRY glGetVaryingLocationNV (GLuint program, const GLchar *name); + GLAPI void APIENTRY glGetActiveVaryingNV (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); + GLAPI void APIENTRY glGetTransformFeedbackVaryingNV (GLuint program, GLuint index, GLint *location); + GLAPI void APIENTRY glTransformFeedbackStreamAttribsNV (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#endif +#endif /* GL_NV_transform_feedback */ + +#ifndef GL_NV_transform_feedback2 +#define GL_NV_transform_feedback2 1 +#define GL_TRANSFORM_FEEDBACK_NV 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING_NV 0x8E25 + typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKNVPROC) (GLenum target, GLuint id); + typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSNVPROC) (GLsizei n, const GLuint *ids); + typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSNVPROC) (GLsizei n, GLuint *ids); + typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKNVPROC) (GLuint id); + typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKNVPROC) (void); + typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKNVPROC) (void); + typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKNVPROC) (GLenum mode, GLuint id); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBindTransformFeedbackNV (GLenum target, GLuint id); + GLAPI void APIENTRY glDeleteTransformFeedbacksNV (GLsizei n, const GLuint *ids); + GLAPI void APIENTRY glGenTransformFeedbacksNV (GLsizei n, GLuint *ids); + GLAPI GLboolean APIENTRY glIsTransformFeedbackNV (GLuint id); + GLAPI void APIENTRY glPauseTransformFeedbackNV (void); + GLAPI void APIENTRY glResumeTransformFeedbackNV (void); + GLAPI void APIENTRY glDrawTransformFeedbackNV (GLenum mode, GLuint id); +#endif +#endif /* GL_NV_transform_feedback2 */ + +#ifndef GL_NV_uniform_buffer_unified_memory +#define GL_NV_uniform_buffer_unified_memory 1 +#define GL_UNIFORM_BUFFER_UNIFIED_NV 0x936E +#define GL_UNIFORM_BUFFER_ADDRESS_NV 0x936F +#define GL_UNIFORM_BUFFER_LENGTH_NV 0x9370 +#endif /* GL_NV_uniform_buffer_unified_memory */ + +#ifndef GL_NV_vdpau_interop +#define GL_NV_vdpau_interop 1 + typedef GLintptr GLvdpauSurfaceNV; +#define GL_SURFACE_STATE_NV 0x86EB +#define GL_SURFACE_REGISTERED_NV 0x86FD +#define GL_SURFACE_MAPPED_NV 0x8700 +#define GL_WRITE_DISCARD_NV 0x88BE + typedef void (APIENTRYP PFNGLVDPAUINITNVPROC) (const void *vdpDevice, const void *getProcAddress); + typedef void (APIENTRYP PFNGLVDPAUFININVPROC) (void); + typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); + typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); + typedef GLboolean (APIENTRYP PFNGLVDPAUISSURFACENVPROC) (GLvdpauSurfaceNV surface); + typedef void (APIENTRYP PFNGLVDPAUUNREGISTERSURFACENVPROC) (GLvdpauSurfaceNV surface); + typedef void (APIENTRYP PFNGLVDPAUGETSURFACEIVNVPROC) (GLvdpauSurfaceNV surface, GLenum pname, GLsizei count, GLsizei *length, GLint *values); + typedef void (APIENTRYP PFNGLVDPAUSURFACEACCESSNVPROC) (GLvdpauSurfaceNV surface, GLenum access); + typedef void (APIENTRYP PFNGLVDPAUMAPSURFACESNVPROC) (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); + typedef void (APIENTRYP PFNGLVDPAUUNMAPSURFACESNVPROC) (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVDPAUInitNV (const void *vdpDevice, const void *getProcAddress); + GLAPI void APIENTRY glVDPAUFiniNV (void); + GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); + GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterOutputSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); + GLAPI GLboolean APIENTRY glVDPAUIsSurfaceNV (GLvdpauSurfaceNV surface); + GLAPI void APIENTRY glVDPAUUnregisterSurfaceNV (GLvdpauSurfaceNV surface); + GLAPI void APIENTRY glVDPAUGetSurfaceivNV (GLvdpauSurfaceNV surface, GLenum pname, GLsizei count, GLsizei *length, GLint *values); + GLAPI void APIENTRY glVDPAUSurfaceAccessNV (GLvdpauSurfaceNV surface, GLenum access); + GLAPI void APIENTRY glVDPAUMapSurfacesNV (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); + GLAPI void APIENTRY glVDPAUUnmapSurfacesNV (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#endif +#endif /* GL_NV_vdpau_interop */ + +#ifndef GL_NV_vdpau_interop2 +#define GL_NV_vdpau_interop2 1 + typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACEWITHPICTURESTRUCTURENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames, GLboolean isFrameStructure); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceWithPictureStructureNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames, GLboolean isFrameStructure); +#endif +#endif /* GL_NV_vdpau_interop2 */ + +#ifndef GL_NV_vertex_array_range +#define GL_NV_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_NV 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E +#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F +#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520 +#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521 + typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC) (void); + typedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC) (GLsizei length, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFlushVertexArrayRangeNV (void); + GLAPI void APIENTRY glVertexArrayRangeNV (GLsizei length, const void *pointer); +#endif +#endif /* GL_NV_vertex_array_range */ + +#ifndef GL_NV_vertex_array_range2 +#define GL_NV_vertex_array_range2 1 +#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533 +#endif /* GL_NV_vertex_array_range2 */ + +#ifndef GL_NV_vertex_attrib_integer_64bit +#define GL_NV_vertex_attrib_integer_64bit 1 + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params); + typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glVertexAttribL1i64NV (GLuint index, GLint64EXT x); + GLAPI void APIENTRY glVertexAttribL2i64NV (GLuint index, GLint64EXT x, GLint64EXT y); + GLAPI void APIENTRY glVertexAttribL3i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); + GLAPI void APIENTRY glVertexAttribL4i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); + GLAPI void APIENTRY glVertexAttribL1i64vNV (GLuint index, const GLint64EXT *v); + GLAPI void APIENTRY glVertexAttribL2i64vNV (GLuint index, const GLint64EXT *v); + GLAPI void APIENTRY glVertexAttribL3i64vNV (GLuint index, const GLint64EXT *v); + GLAPI void APIENTRY glVertexAttribL4i64vNV (GLuint index, const GLint64EXT *v); + GLAPI void APIENTRY glVertexAttribL1ui64NV (GLuint index, GLuint64EXT x); + GLAPI void APIENTRY glVertexAttribL2ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y); + GLAPI void APIENTRY glVertexAttribL3ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); + GLAPI void APIENTRY glVertexAttribL4ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); + GLAPI void APIENTRY glVertexAttribL1ui64vNV (GLuint index, const GLuint64EXT *v); + GLAPI void APIENTRY glVertexAttribL2ui64vNV (GLuint index, const GLuint64EXT *v); + GLAPI void APIENTRY glVertexAttribL3ui64vNV (GLuint index, const GLuint64EXT *v); + GLAPI void APIENTRY glVertexAttribL4ui64vNV (GLuint index, const GLuint64EXT *v); + GLAPI void APIENTRY glGetVertexAttribLi64vNV (GLuint index, GLenum pname, GLint64EXT *params); + GLAPI void APIENTRY glGetVertexAttribLui64vNV (GLuint index, GLenum pname, GLuint64EXT *params); + GLAPI void APIENTRY glVertexAttribLFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +#endif +#endif /* GL_NV_vertex_attrib_integer_64bit */ + +#ifndef GL_NV_vertex_buffer_unified_memory +#define GL_NV_vertex_buffer_unified_memory 1 +#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E +#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F +#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 +#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 +#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 +#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 +#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 +#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 +#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 +#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 +#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 +#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 +#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A +#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B +#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C +#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D +#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E +#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F +#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 +#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 +#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 +#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 +#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 +#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 +#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 + typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); + typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride); + typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); + typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); + typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBufferAddressRangeNV (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); + GLAPI void APIENTRY glVertexFormatNV (GLint size, GLenum type, GLsizei stride); + GLAPI void APIENTRY glNormalFormatNV (GLenum type, GLsizei stride); + GLAPI void APIENTRY glColorFormatNV (GLint size, GLenum type, GLsizei stride); + GLAPI void APIENTRY glIndexFormatNV (GLenum type, GLsizei stride); + GLAPI void APIENTRY glTexCoordFormatNV (GLint size, GLenum type, GLsizei stride); + GLAPI void APIENTRY glEdgeFlagFormatNV (GLsizei stride); + GLAPI void APIENTRY glSecondaryColorFormatNV (GLint size, GLenum type, GLsizei stride); + GLAPI void APIENTRY glFogCoordFormatNV (GLenum type, GLsizei stride); + GLAPI void APIENTRY glVertexAttribFormatNV (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); + GLAPI void APIENTRY glVertexAttribIFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); + GLAPI void APIENTRY glGetIntegerui64i_vNV (GLenum value, GLuint index, GLuint64EXT *result); +#endif +#endif /* GL_NV_vertex_buffer_unified_memory */ + +#ifndef GL_NV_vertex_program +#define GL_NV_vertex_program 1 +#define GL_VERTEX_PROGRAM_NV 0x8620 +#define GL_VERTEX_STATE_PROGRAM_NV 0x8621 +#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623 +#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624 +#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625 +#define GL_CURRENT_ATTRIB_NV 0x8626 +#define GL_PROGRAM_LENGTH_NV 0x8627 +#define GL_PROGRAM_STRING_NV 0x8628 +#define GL_MODELVIEW_PROJECTION_NV 0x8629 +#define GL_IDENTITY_NV 0x862A +#define GL_INVERSE_NV 0x862B +#define GL_TRANSPOSE_NV 0x862C +#define GL_INVERSE_TRANSPOSE_NV 0x862D +#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E +#define GL_MAX_TRACK_MATRICES_NV 0x862F +#define GL_MATRIX0_NV 0x8630 +#define GL_MATRIX1_NV 0x8631 +#define GL_MATRIX2_NV 0x8632 +#define GL_MATRIX3_NV 0x8633 +#define GL_MATRIX4_NV 0x8634 +#define GL_MATRIX5_NV 0x8635 +#define GL_MATRIX6_NV 0x8636 +#define GL_MATRIX7_NV 0x8637 +#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640 +#define GL_CURRENT_MATRIX_NV 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643 +#define GL_PROGRAM_PARAMETER_NV 0x8644 +#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645 +#define GL_PROGRAM_TARGET_NV 0x8646 +#define GL_PROGRAM_RESIDENT_NV 0x8647 +#define GL_TRACK_MATRIX_NV 0x8648 +#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649 +#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A +#define GL_PROGRAM_ERROR_POSITION_NV 0x864B +#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650 +#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651 +#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652 +#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653 +#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654 +#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655 +#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656 +#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657 +#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658 +#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659 +#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A +#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B +#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C +#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D +#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E +#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F +#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660 +#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661 +#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662 +#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663 +#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664 +#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665 +#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666 +#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667 +#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668 +#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669 +#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A +#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B +#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C +#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D +#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E +#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F +#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670 +#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671 +#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672 +#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673 +#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674 +#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675 +#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676 +#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677 +#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678 +#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679 +#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A +#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B +#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C +#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D +#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E +#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F + typedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC) (GLsizei n, const GLuint *programs, GLboolean *residences); + typedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC) (GLenum target, GLuint id); + typedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); + typedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC) (GLenum target, GLuint id, const GLfloat *params); + typedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC) (GLsizei n, GLuint *programs); + typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC) (GLenum target, GLuint index, GLenum pname, GLdouble *params); + typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC) (GLuint id, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC) (GLuint id, GLenum pname, GLubyte *program); + typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC) (GLenum target, GLuint address, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC) (GLuint index, GLenum pname, GLdouble *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC) (GLuint index, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC) (GLuint index, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, void **pointer); + typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC) (GLuint id); + typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC) (GLenum target, GLuint id, GLsizei len, const GLubyte *program); + typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC) (GLenum target, GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC) (GLenum target, GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLdouble *v); + typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); + typedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC) (GLenum target, GLuint address, GLenum matrix, GLenum transform); + typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC) (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC) (GLuint index, GLdouble x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC) (GLuint index, GLfloat x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC) (GLuint index, GLshort x); + typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC) (GLuint index, GLdouble x, GLdouble y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC) (GLuint index, GLfloat x, GLfloat y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC) (GLuint index, GLshort x, GLshort y); + typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z); + typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC) (GLuint index, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC) (GLuint index, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC) (GLuint index, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); + typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC) (GLuint index, const GLubyte *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); + typedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC) (GLuint index, GLsizei count, const GLubyte *v); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLboolean APIENTRY glAreProgramsResidentNV (GLsizei n, const GLuint *programs, GLboolean *residences); + GLAPI void APIENTRY glBindProgramNV (GLenum target, GLuint id); + GLAPI void APIENTRY glDeleteProgramsNV (GLsizei n, const GLuint *programs); + GLAPI void APIENTRY glExecuteProgramNV (GLenum target, GLuint id, const GLfloat *params); + GLAPI void APIENTRY glGenProgramsNV (GLsizei n, GLuint *programs); + GLAPI void APIENTRY glGetProgramParameterdvNV (GLenum target, GLuint index, GLenum pname, GLdouble *params); + GLAPI void APIENTRY glGetProgramParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetProgramivNV (GLuint id, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetProgramStringNV (GLuint id, GLenum pname, GLubyte *program); + GLAPI void APIENTRY glGetTrackMatrixivNV (GLenum target, GLuint address, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVertexAttribdvNV (GLuint index, GLenum pname, GLdouble *params); + GLAPI void APIENTRY glGetVertexAttribfvNV (GLuint index, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetVertexAttribivNV (GLuint index, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint index, GLenum pname, void **pointer); + GLAPI GLboolean APIENTRY glIsProgramNV (GLuint id); + GLAPI void APIENTRY glLoadProgramNV (GLenum target, GLuint id, GLsizei len, const GLubyte *program); + GLAPI void APIENTRY glProgramParameter4dNV (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glProgramParameter4dvNV (GLenum target, GLuint index, const GLdouble *v); + GLAPI void APIENTRY glProgramParameter4fNV (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glProgramParameter4fvNV (GLenum target, GLuint index, const GLfloat *v); + GLAPI void APIENTRY glProgramParameters4dvNV (GLenum target, GLuint index, GLsizei count, const GLdouble *v); + GLAPI void APIENTRY glProgramParameters4fvNV (GLenum target, GLuint index, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glRequestResidentProgramsNV (GLsizei n, const GLuint *programs); + GLAPI void APIENTRY glTrackMatrixNV (GLenum target, GLuint address, GLenum matrix, GLenum transform); + GLAPI void APIENTRY glVertexAttribPointerNV (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); + GLAPI void APIENTRY glVertexAttrib1dNV (GLuint index, GLdouble x); + GLAPI void APIENTRY glVertexAttrib1dvNV (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib1fNV (GLuint index, GLfloat x); + GLAPI void APIENTRY glVertexAttrib1fvNV (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib1sNV (GLuint index, GLshort x); + GLAPI void APIENTRY glVertexAttrib1svNV (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib2dNV (GLuint index, GLdouble x, GLdouble y); + GLAPI void APIENTRY glVertexAttrib2dvNV (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib2fNV (GLuint index, GLfloat x, GLfloat y); + GLAPI void APIENTRY glVertexAttrib2fvNV (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib2sNV (GLuint index, GLshort x, GLshort y); + GLAPI void APIENTRY glVertexAttrib2svNV (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib3dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z); + GLAPI void APIENTRY glVertexAttrib3dvNV (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib3fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glVertexAttrib3fvNV (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib3sNV (GLuint index, GLshort x, GLshort y, GLshort z); + GLAPI void APIENTRY glVertexAttrib3svNV (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); + GLAPI void APIENTRY glVertexAttrib4dvNV (GLuint index, const GLdouble *v); + GLAPI void APIENTRY glVertexAttrib4fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glVertexAttrib4fvNV (GLuint index, const GLfloat *v); + GLAPI void APIENTRY glVertexAttrib4sNV (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); + GLAPI void APIENTRY glVertexAttrib4svNV (GLuint index, const GLshort *v); + GLAPI void APIENTRY glVertexAttrib4ubNV (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); + GLAPI void APIENTRY glVertexAttrib4ubvNV (GLuint index, const GLubyte *v); + GLAPI void APIENTRY glVertexAttribs1dvNV (GLuint index, GLsizei count, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribs1fvNV (GLuint index, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glVertexAttribs1svNV (GLuint index, GLsizei count, const GLshort *v); + GLAPI void APIENTRY glVertexAttribs2dvNV (GLuint index, GLsizei count, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribs2fvNV (GLuint index, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glVertexAttribs2svNV (GLuint index, GLsizei count, const GLshort *v); + GLAPI void APIENTRY glVertexAttribs3dvNV (GLuint index, GLsizei count, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribs3fvNV (GLuint index, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glVertexAttribs3svNV (GLuint index, GLsizei count, const GLshort *v); + GLAPI void APIENTRY glVertexAttribs4dvNV (GLuint index, GLsizei count, const GLdouble *v); + GLAPI void APIENTRY glVertexAttribs4fvNV (GLuint index, GLsizei count, const GLfloat *v); + GLAPI void APIENTRY glVertexAttribs4svNV (GLuint index, GLsizei count, const GLshort *v); + GLAPI void APIENTRY glVertexAttribs4ubvNV (GLuint index, GLsizei count, const GLubyte *v); +#endif +#endif /* GL_NV_vertex_program */ + +#ifndef GL_NV_vertex_program1_1 +#define GL_NV_vertex_program1_1 1 +#endif /* GL_NV_vertex_program1_1 */ + +#ifndef GL_NV_vertex_program2 +#define GL_NV_vertex_program2 1 +#endif /* GL_NV_vertex_program2 */ + +#ifndef GL_NV_vertex_program2_option +#define GL_NV_vertex_program2_option 1 +#endif /* GL_NV_vertex_program2_option */ + +#ifndef GL_NV_vertex_program3 +#define GL_NV_vertex_program3 1 +#endif /* GL_NV_vertex_program3 */ + +#ifndef GL_NV_vertex_program4 +#define GL_NV_vertex_program4 1 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD +#endif /* GL_NV_vertex_program4 */ + +#ifndef GL_NV_video_capture +#define GL_NV_video_capture 1 +#define GL_VIDEO_BUFFER_NV 0x9020 +#define GL_VIDEO_BUFFER_BINDING_NV 0x9021 +#define GL_FIELD_UPPER_NV 0x9022 +#define GL_FIELD_LOWER_NV 0x9023 +#define GL_NUM_VIDEO_CAPTURE_STREAMS_NV 0x9024 +#define GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV 0x9025 +#define GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV 0x9026 +#define GL_LAST_VIDEO_CAPTURE_STATUS_NV 0x9027 +#define GL_VIDEO_BUFFER_PITCH_NV 0x9028 +#define GL_VIDEO_COLOR_CONVERSION_MATRIX_NV 0x9029 +#define GL_VIDEO_COLOR_CONVERSION_MAX_NV 0x902A +#define GL_VIDEO_COLOR_CONVERSION_MIN_NV 0x902B +#define GL_VIDEO_COLOR_CONVERSION_OFFSET_NV 0x902C +#define GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV 0x902D +#define GL_PARTIAL_SUCCESS_NV 0x902E +#define GL_SUCCESS_NV 0x902F +#define GL_FAILURE_NV 0x9030 +#define GL_YCBYCR8_422_NV 0x9031 +#define GL_YCBAYCR8A_4224_NV 0x9032 +#define GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV 0x9033 +#define GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV 0x9034 +#define GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV 0x9035 +#define GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV 0x9036 +#define GL_Z4Y12Z4CB12Z4CR12_444_NV 0x9037 +#define GL_VIDEO_CAPTURE_FRAME_WIDTH_NV 0x9038 +#define GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV 0x9039 +#define GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV 0x903A +#define GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV 0x903B +#define GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV 0x903C + typedef void (APIENTRYP PFNGLBEGINVIDEOCAPTURENVPROC) (GLuint video_capture_slot); + typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); + typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); + typedef void (APIENTRYP PFNGLENDVIDEOCAPTURENVPROC) (GLuint video_capture_slot); + typedef void (APIENTRYP PFNGLGETVIDEOCAPTUREIVNVPROC) (GLuint video_capture_slot, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); + typedef GLenum (APIENTRYP PFNGLVIDEOCAPTURENVPROC) (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); + typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glBeginVideoCaptureNV (GLuint video_capture_slot); + GLAPI void APIENTRY glBindVideoCaptureStreamBufferNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); + GLAPI void APIENTRY glBindVideoCaptureStreamTextureNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); + GLAPI void APIENTRY glEndVideoCaptureNV (GLuint video_capture_slot); + GLAPI void APIENTRY glGetVideoCaptureivNV (GLuint video_capture_slot, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVideoCaptureStreamivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetVideoCaptureStreamfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetVideoCaptureStreamdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); + GLAPI GLenum APIENTRY glVideoCaptureNV (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); + GLAPI void APIENTRY glVideoCaptureStreamParameterivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); + GLAPI void APIENTRY glVideoCaptureStreamParameterfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glVideoCaptureStreamParameterdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#endif +#endif /* GL_NV_video_capture */ + +#ifndef GL_NV_viewport_array2 +#define GL_NV_viewport_array2 1 +#endif /* GL_NV_viewport_array2 */ + +#ifndef GL_NV_viewport_swizzle +#define GL_NV_viewport_swizzle 1 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV 0x9350 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV 0x9351 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV 0x9352 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV 0x9353 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV 0x9354 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV 0x9355 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV 0x9356 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV 0x9357 +#define GL_VIEWPORT_SWIZZLE_X_NV 0x9358 +#define GL_VIEWPORT_SWIZZLE_Y_NV 0x9359 +#define GL_VIEWPORT_SWIZZLE_Z_NV 0x935A +#define GL_VIEWPORT_SWIZZLE_W_NV 0x935B + typedef void (APIENTRYP PFNGLVIEWPORTSWIZZLENVPROC) (GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glViewportSwizzleNV (GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew); +#endif +#endif /* GL_NV_viewport_swizzle */ + +#ifndef GL_OML_interlace +#define GL_OML_interlace 1 +#define GL_INTERLACE_OML 0x8980 +#define GL_INTERLACE_READ_OML 0x8981 +#endif /* GL_OML_interlace */ + +#ifndef GL_OML_resample +#define GL_OML_resample 1 +#define GL_PACK_RESAMPLE_OML 0x8984 +#define GL_UNPACK_RESAMPLE_OML 0x8985 +#define GL_RESAMPLE_REPLICATE_OML 0x8986 +#define GL_RESAMPLE_ZERO_FILL_OML 0x8987 +#define GL_RESAMPLE_AVERAGE_OML 0x8988 +#define GL_RESAMPLE_DECIMATE_OML 0x8989 +#endif /* GL_OML_resample */ + +#ifndef GL_OML_subsample +#define GL_OML_subsample 1 +#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982 +#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983 +#endif /* GL_OML_subsample */ + +#ifndef GL_OVR_multiview +#define GL_OVR_multiview 1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 +#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633 + typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFramebufferTextureMultiviewOVR (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#endif +#endif /* GL_OVR_multiview */ + +#ifndef GL_OVR_multiview2 +#define GL_OVR_multiview2 1 +#endif /* GL_OVR_multiview2 */ + +#ifndef GL_PGI_misc_hints +#define GL_PGI_misc_hints 1 +#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8 +#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD +#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE +#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202 +#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203 +#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204 +#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C +#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D +#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E +#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F +#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210 +#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211 +#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216 +#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217 +#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218 +#define GL_FULL_STIPPLE_HINT_PGI 0x1A219 +#define GL_CLIP_NEAR_HINT_PGI 0x1A220 +#define GL_CLIP_FAR_HINT_PGI 0x1A221 +#define GL_WIDE_LINE_HINT_PGI 0x1A222 +#define GL_BACK_NORMALS_HINT_PGI 0x1A223 + typedef void (APIENTRYP PFNGLHINTPGIPROC) (GLenum target, GLint mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glHintPGI (GLenum target, GLint mode); +#endif +#endif /* GL_PGI_misc_hints */ + +#ifndef GL_PGI_vertex_hints +#define GL_PGI_vertex_hints 1 +#define GL_VERTEX_DATA_HINT_PGI 0x1A22A +#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B +#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C +#define GL_MAX_VERTEX_HINT_PGI 0x1A22D +#define GL_COLOR3_BIT_PGI 0x00010000 +#define GL_COLOR4_BIT_PGI 0x00020000 +#define GL_EDGEFLAG_BIT_PGI 0x00040000 +#define GL_INDEX_BIT_PGI 0x00080000 +#define GL_MAT_AMBIENT_BIT_PGI 0x00100000 +#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000 +#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000 +#define GL_MAT_EMISSION_BIT_PGI 0x00800000 +#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000 +#define GL_MAT_SHININESS_BIT_PGI 0x02000000 +#define GL_MAT_SPECULAR_BIT_PGI 0x04000000 +#define GL_NORMAL_BIT_PGI 0x08000000 +#define GL_TEXCOORD1_BIT_PGI 0x10000000 +#define GL_TEXCOORD2_BIT_PGI 0x20000000 +#define GL_TEXCOORD3_BIT_PGI 0x40000000 +#define GL_TEXCOORD4_BIT_PGI 0x80000000 +#define GL_VERTEX23_BIT_PGI 0x00000004 +#define GL_VERTEX4_BIT_PGI 0x00000008 +#endif /* GL_PGI_vertex_hints */ + +#ifndef GL_REND_screen_coordinates +#define GL_REND_screen_coordinates 1 +#define GL_SCREEN_COORDINATES_REND 0x8490 +#define GL_INVERTED_SCREEN_W_REND 0x8491 +#endif /* GL_REND_screen_coordinates */ + +#ifndef GL_S3_s3tc +#define GL_S3_s3tc 1 +#define GL_RGB_S3TC 0x83A0 +#define GL_RGB4_S3TC 0x83A1 +#define GL_RGBA_S3TC 0x83A2 +#define GL_RGBA4_S3TC 0x83A3 +#define GL_RGBA_DXT5_S3TC 0x83A4 +#define GL_RGBA4_DXT5_S3TC 0x83A5 +#endif /* GL_S3_s3tc */ + +#ifndef GL_SGIS_detail_texture +#define GL_SGIS_detail_texture 1 +#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095 +#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096 +#define GL_LINEAR_DETAIL_SGIS 0x8097 +#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098 +#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099 +#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A +#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B +#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C + typedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); + typedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDetailTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); + GLAPI void APIENTRY glGetDetailTexFuncSGIS (GLenum target, GLfloat *points); +#endif +#endif /* GL_SGIS_detail_texture */ + +#ifndef GL_SGIS_fog_function +#define GL_SGIS_fog_function 1 +#define GL_FOG_FUNC_SGIS 0x812A +#define GL_FOG_FUNC_POINTS_SGIS 0x812B +#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C + typedef void (APIENTRYP PFNGLFOGFUNCSGISPROC) (GLsizei n, const GLfloat *points); + typedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC) (GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFogFuncSGIS (GLsizei n, const GLfloat *points); + GLAPI void APIENTRY glGetFogFuncSGIS (GLfloat *points); +#endif +#endif /* GL_SGIS_fog_function */ + +#ifndef GL_SGIS_generate_mipmap +#define GL_SGIS_generate_mipmap 1 +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif /* GL_SGIS_generate_mipmap */ + +#ifndef GL_SGIS_multisample +#define GL_SGIS_multisample 1 +#define GL_MULTISAMPLE_SGIS 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F +#define GL_SAMPLE_MASK_SGIS 0x80A0 +#define GL_1PASS_SGIS 0x80A1 +#define GL_2PASS_0_SGIS 0x80A2 +#define GL_2PASS_1_SGIS 0x80A3 +#define GL_4PASS_0_SGIS 0x80A4 +#define GL_4PASS_1_SGIS 0x80A5 +#define GL_4PASS_2_SGIS 0x80A6 +#define GL_4PASS_3_SGIS 0x80A7 +#define GL_SAMPLE_BUFFERS_SGIS 0x80A8 +#define GL_SAMPLES_SGIS 0x80A9 +#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA +#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB +#define GL_SAMPLE_PATTERN_SGIS 0x80AC + typedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC) (GLclampf value, GLboolean invert); + typedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC) (GLenum pattern); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSampleMaskSGIS (GLclampf value, GLboolean invert); + GLAPI void APIENTRY glSamplePatternSGIS (GLenum pattern); +#endif +#endif /* GL_SGIS_multisample */ + +#ifndef GL_SGIS_pixel_texture +#define GL_SGIS_pixel_texture 1 +#define GL_PIXEL_TEXTURE_SGIS 0x8353 +#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354 +#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355 +#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356 + typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC) (GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC) (GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPixelTexGenParameteriSGIS (GLenum pname, GLint param); + GLAPI void APIENTRY glPixelTexGenParameterivSGIS (GLenum pname, const GLint *params); + GLAPI void APIENTRY glPixelTexGenParameterfSGIS (GLenum pname, GLfloat param); + GLAPI void APIENTRY glPixelTexGenParameterfvSGIS (GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glGetPixelTexGenParameterivSGIS (GLenum pname, GLint *params); + GLAPI void APIENTRY glGetPixelTexGenParameterfvSGIS (GLenum pname, GLfloat *params); +#endif +#endif /* GL_SGIS_pixel_texture */ + +#ifndef GL_SGIS_point_line_texgen +#define GL_SGIS_point_line_texgen 1 +#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0 +#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1 +#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2 +#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3 +#define GL_EYE_POINT_SGIS 0x81F4 +#define GL_OBJECT_POINT_SGIS 0x81F5 +#define GL_EYE_LINE_SGIS 0x81F6 +#define GL_OBJECT_LINE_SGIS 0x81F7 +#endif /* GL_SGIS_point_line_texgen */ + +#ifndef GL_SGIS_point_parameters +#define GL_SGIS_point_parameters 1 +#define GL_POINT_SIZE_MIN_SGIS 0x8126 +#define GL_POINT_SIZE_MAX_SGIS 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128 +#define GL_DISTANCE_ATTENUATION_SGIS 0x8129 + typedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC) (GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPointParameterfSGIS (GLenum pname, GLfloat param); + GLAPI void APIENTRY glPointParameterfvSGIS (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_SGIS_point_parameters */ + +#ifndef GL_SGIS_sharpen_texture +#define GL_SGIS_sharpen_texture 1 +#define GL_LINEAR_SHARPEN_SGIS 0x80AD +#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE +#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF +#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0 + typedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); + typedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSharpenTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); + GLAPI void APIENTRY glGetSharpenTexFuncSGIS (GLenum target, GLfloat *points); +#endif +#endif /* GL_SGIS_sharpen_texture */ + +#ifndef GL_SGIS_texture4D +#define GL_SGIS_texture4D 1 +#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130 +#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131 +#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132 +#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133 +#define GL_TEXTURE_4D_SGIS 0x8134 +#define GL_PROXY_TEXTURE_4D_SGIS 0x8135 +#define GL_TEXTURE_4DSIZE_SGIS 0x8136 +#define GL_TEXTURE_WRAP_Q_SGIS 0x8137 +#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138 +#define GL_TEXTURE_4D_BINDING_SGIS 0x814F + typedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); + typedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTexImage4DSGIS (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); + GLAPI void APIENTRY glTexSubImage4DSGIS (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_SGIS_texture4D */ + +#ifndef GL_SGIS_texture_border_clamp +#define GL_SGIS_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_SGIS 0x812D +#endif /* GL_SGIS_texture_border_clamp */ + +#ifndef GL_SGIS_texture_color_mask +#define GL_SGIS_texture_color_mask 1 +#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF + typedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTextureColorMaskSGIS (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#endif +#endif /* GL_SGIS_texture_color_mask */ + +#ifndef GL_SGIS_texture_edge_clamp +#define GL_SGIS_texture_edge_clamp 1 +#define GL_CLAMP_TO_EDGE_SGIS 0x812F +#endif /* GL_SGIS_texture_edge_clamp */ + +#ifndef GL_SGIS_texture_filter4 +#define GL_SGIS_texture_filter4 1 +#define GL_FILTER4_SGIS 0x8146 +#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147 + typedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLfloat *weights); + typedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetTexFilterFuncSGIS (GLenum target, GLenum filter, GLfloat *weights); + GLAPI void APIENTRY glTexFilterFuncSGIS (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#endif +#endif /* GL_SGIS_texture_filter4 */ + +#ifndef GL_SGIS_texture_lod +#define GL_SGIS_texture_lod 1 +#define GL_TEXTURE_MIN_LOD_SGIS 0x813A +#define GL_TEXTURE_MAX_LOD_SGIS 0x813B +#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C +#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D +#endif /* GL_SGIS_texture_lod */ + +#ifndef GL_SGIS_texture_select +#define GL_SGIS_texture_select 1 +#define GL_DUAL_ALPHA4_SGIS 0x8110 +#define GL_DUAL_ALPHA8_SGIS 0x8111 +#define GL_DUAL_ALPHA12_SGIS 0x8112 +#define GL_DUAL_ALPHA16_SGIS 0x8113 +#define GL_DUAL_LUMINANCE4_SGIS 0x8114 +#define GL_DUAL_LUMINANCE8_SGIS 0x8115 +#define GL_DUAL_LUMINANCE12_SGIS 0x8116 +#define GL_DUAL_LUMINANCE16_SGIS 0x8117 +#define GL_DUAL_INTENSITY4_SGIS 0x8118 +#define GL_DUAL_INTENSITY8_SGIS 0x8119 +#define GL_DUAL_INTENSITY12_SGIS 0x811A +#define GL_DUAL_INTENSITY16_SGIS 0x811B +#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C +#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D +#define GL_QUAD_ALPHA4_SGIS 0x811E +#define GL_QUAD_ALPHA8_SGIS 0x811F +#define GL_QUAD_LUMINANCE4_SGIS 0x8120 +#define GL_QUAD_LUMINANCE8_SGIS 0x8121 +#define GL_QUAD_INTENSITY4_SGIS 0x8122 +#define GL_QUAD_INTENSITY8_SGIS 0x8123 +#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124 +#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125 +#endif /* GL_SGIS_texture_select */ + +#ifndef GL_SGIX_async +#define GL_SGIX_async 1 +#define GL_ASYNC_MARKER_SGIX 0x8329 + typedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC) (GLuint marker); + typedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC) (GLuint *markerp); + typedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC) (GLuint *markerp); + typedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC) (GLsizei range); + typedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC) (GLuint marker, GLsizei range); + typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC) (GLuint marker); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glAsyncMarkerSGIX (GLuint marker); + GLAPI GLint APIENTRY glFinishAsyncSGIX (GLuint *markerp); + GLAPI GLint APIENTRY glPollAsyncSGIX (GLuint *markerp); + GLAPI GLuint APIENTRY glGenAsyncMarkersSGIX (GLsizei range); + GLAPI void APIENTRY glDeleteAsyncMarkersSGIX (GLuint marker, GLsizei range); + GLAPI GLboolean APIENTRY glIsAsyncMarkerSGIX (GLuint marker); +#endif +#endif /* GL_SGIX_async */ + +#ifndef GL_SGIX_async_histogram +#define GL_SGIX_async_histogram 1 +#define GL_ASYNC_HISTOGRAM_SGIX 0x832C +#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D +#endif /* GL_SGIX_async_histogram */ + +#ifndef GL_SGIX_async_pixel +#define GL_SGIX_async_pixel 1 +#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C +#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D +#define GL_ASYNC_READ_PIXELS_SGIX 0x835E +#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F +#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360 +#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361 +#endif /* GL_SGIX_async_pixel */ + +#ifndef GL_SGIX_blend_alpha_minmax +#define GL_SGIX_blend_alpha_minmax 1 +#define GL_ALPHA_MIN_SGIX 0x8320 +#define GL_ALPHA_MAX_SGIX 0x8321 +#endif /* GL_SGIX_blend_alpha_minmax */ + +#ifndef GL_SGIX_calligraphic_fragment +#define GL_SGIX_calligraphic_fragment 1 +#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183 +#endif /* GL_SGIX_calligraphic_fragment */ + +#ifndef GL_SGIX_clipmap +#define GL_SGIX_clipmap 1 +#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170 +#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171 +#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172 +#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173 +#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174 +#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175 +#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176 +#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177 +#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178 +#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D +#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E +#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F +#endif /* GL_SGIX_clipmap */ + +#ifndef GL_SGIX_convolution_accuracy +#define GL_SGIX_convolution_accuracy 1 +#define GL_CONVOLUTION_HINT_SGIX 0x8316 +#endif /* GL_SGIX_convolution_accuracy */ + +#ifndef GL_SGIX_depth_pass_instrument +#define GL_SGIX_depth_pass_instrument 1 +#endif /* GL_SGIX_depth_pass_instrument */ + +#ifndef GL_SGIX_depth_texture +#define GL_SGIX_depth_texture 1 +#define GL_DEPTH_COMPONENT16_SGIX 0x81A5 +#define GL_DEPTH_COMPONENT24_SGIX 0x81A6 +#define GL_DEPTH_COMPONENT32_SGIX 0x81A7 +#endif /* GL_SGIX_depth_texture */ + +#ifndef GL_SGIX_flush_raster +#define GL_SGIX_flush_raster 1 + typedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFlushRasterSGIX (void); +#endif +#endif /* GL_SGIX_flush_raster */ + +#ifndef GL_SGIX_fog_offset +#define GL_SGIX_fog_offset 1 +#define GL_FOG_OFFSET_SGIX 0x8198 +#define GL_FOG_OFFSET_VALUE_SGIX 0x8199 +#endif /* GL_SGIX_fog_offset */ + +#ifndef GL_SGIX_fragment_lighting +#define GL_SGIX_fragment_lighting 1 +#define GL_FRAGMENT_LIGHTING_SGIX 0x8400 +#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401 +#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402 +#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403 +#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404 +#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405 +#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406 +#define GL_LIGHT_ENV_MODE_SGIX 0x8407 +#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408 +#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409 +#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A +#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B +#define GL_FRAGMENT_LIGHT0_SGIX 0x840C +#define GL_FRAGMENT_LIGHT1_SGIX 0x840D +#define GL_FRAGMENT_LIGHT2_SGIX 0x840E +#define GL_FRAGMENT_LIGHT3_SGIX 0x840F +#define GL_FRAGMENT_LIGHT4_SGIX 0x8410 +#define GL_FRAGMENT_LIGHT5_SGIX 0x8411 +#define GL_FRAGMENT_LIGHT6_SGIX 0x8412 +#define GL_FRAGMENT_LIGHT7_SGIX 0x8413 + typedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC) (GLenum face, GLenum mode); + typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC) (GLenum light, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC) (GLenum light, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC) (GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC) (GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC) (GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC) (GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC) (GLenum face, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC) (GLenum face, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFragmentColorMaterialSGIX (GLenum face, GLenum mode); + GLAPI void APIENTRY glFragmentLightfSGIX (GLenum light, GLenum pname, GLfloat param); + GLAPI void APIENTRY glFragmentLightfvSGIX (GLenum light, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glFragmentLightiSGIX (GLenum light, GLenum pname, GLint param); + GLAPI void APIENTRY glFragmentLightivSGIX (GLenum light, GLenum pname, const GLint *params); + GLAPI void APIENTRY glFragmentLightModelfSGIX (GLenum pname, GLfloat param); + GLAPI void APIENTRY glFragmentLightModelfvSGIX (GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glFragmentLightModeliSGIX (GLenum pname, GLint param); + GLAPI void APIENTRY glFragmentLightModelivSGIX (GLenum pname, const GLint *params); + GLAPI void APIENTRY glFragmentMaterialfSGIX (GLenum face, GLenum pname, GLfloat param); + GLAPI void APIENTRY glFragmentMaterialfvSGIX (GLenum face, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glFragmentMaterialiSGIX (GLenum face, GLenum pname, GLint param); + GLAPI void APIENTRY glFragmentMaterialivSGIX (GLenum face, GLenum pname, const GLint *params); + GLAPI void APIENTRY glGetFragmentLightfvSGIX (GLenum light, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetFragmentLightivSGIX (GLenum light, GLenum pname, GLint *params); + GLAPI void APIENTRY glGetFragmentMaterialfvSGIX (GLenum face, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetFragmentMaterialivSGIX (GLenum face, GLenum pname, GLint *params); + GLAPI void APIENTRY glLightEnviSGIX (GLenum pname, GLint param); +#endif +#endif /* GL_SGIX_fragment_lighting */ + +#ifndef GL_SGIX_framezoom +#define GL_SGIX_framezoom 1 +#define GL_FRAMEZOOM_SGIX 0x818B +#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C +#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D + typedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC) (GLint factor); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFrameZoomSGIX (GLint factor); +#endif +#endif /* GL_SGIX_framezoom */ + +#ifndef GL_SGIX_igloo_interface +#define GL_SGIX_igloo_interface 1 + typedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC) (GLenum pname, const void *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glIglooInterfaceSGIX (GLenum pname, const void *params); +#endif +#endif /* GL_SGIX_igloo_interface */ + +#ifndef GL_SGIX_instruments +#define GL_SGIX_instruments 1 +#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180 +#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181 + typedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC) (void); + typedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC) (GLsizei size, GLint *buffer); + typedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC) (GLint *marker_p); + typedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC) (GLint marker); + typedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC) (void); + typedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC) (GLint marker); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI GLint APIENTRY glGetInstrumentsSGIX (void); + GLAPI void APIENTRY glInstrumentsBufferSGIX (GLsizei size, GLint *buffer); + GLAPI GLint APIENTRY glPollInstrumentsSGIX (GLint *marker_p); + GLAPI void APIENTRY glReadInstrumentsSGIX (GLint marker); + GLAPI void APIENTRY glStartInstrumentsSGIX (void); + GLAPI void APIENTRY glStopInstrumentsSGIX (GLint marker); +#endif +#endif /* GL_SGIX_instruments */ + +#ifndef GL_SGIX_interlace +#define GL_SGIX_interlace 1 +#define GL_INTERLACE_SGIX 0x8094 +#endif /* GL_SGIX_interlace */ + +#ifndef GL_SGIX_ir_instrument1 +#define GL_SGIX_ir_instrument1 1 +#define GL_IR_INSTRUMENT1_SGIX 0x817F +#endif /* GL_SGIX_ir_instrument1 */ + +#ifndef GL_SGIX_list_priority +#define GL_SGIX_list_priority 1 +#define GL_LIST_PRIORITY_SGIX 0x8182 + typedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, GLint *params); + typedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC) (GLuint list, GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC) (GLuint list, GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGetListParameterfvSGIX (GLuint list, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetListParameterivSGIX (GLuint list, GLenum pname, GLint *params); + GLAPI void APIENTRY glListParameterfSGIX (GLuint list, GLenum pname, GLfloat param); + GLAPI void APIENTRY glListParameterfvSGIX (GLuint list, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glListParameteriSGIX (GLuint list, GLenum pname, GLint param); + GLAPI void APIENTRY glListParameterivSGIX (GLuint list, GLenum pname, const GLint *params); +#endif +#endif /* GL_SGIX_list_priority */ + +#ifndef GL_SGIX_pixel_texture +#define GL_SGIX_pixel_texture 1 +#define GL_PIXEL_TEX_GEN_SGIX 0x8139 +#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B + typedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glPixelTexGenSGIX (GLenum mode); +#endif +#endif /* GL_SGIX_pixel_texture */ + +#ifndef GL_SGIX_pixel_tiles +#define GL_SGIX_pixel_tiles 1 +#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E +#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F +#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140 +#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141 +#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142 +#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143 +#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144 +#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145 +#endif /* GL_SGIX_pixel_tiles */ + +#ifndef GL_SGIX_polynomial_ffd +#define GL_SGIX_polynomial_ffd 1 +#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001 +#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002 +#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194 +#define GL_TEXTURE_DEFORMATION_SGIX 0x8195 +#define GL_DEFORMATIONS_MASK_SGIX 0x8196 +#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197 + typedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); + typedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); + typedef void (APIENTRYP PFNGLDEFORMSGIXPROC) (GLbitfield mask); + typedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC) (GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDeformationMap3dSGIX (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); + GLAPI void APIENTRY glDeformationMap3fSGIX (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); + GLAPI void APIENTRY glDeformSGIX (GLbitfield mask); + GLAPI void APIENTRY glLoadIdentityDeformationMapSGIX (GLbitfield mask); +#endif +#endif /* GL_SGIX_polynomial_ffd */ + +#ifndef GL_SGIX_reference_plane +#define GL_SGIX_reference_plane 1 +#define GL_REFERENCE_PLANE_SGIX 0x817D +#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E + typedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC) (const GLdouble *equation); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glReferencePlaneSGIX (const GLdouble *equation); +#endif +#endif /* GL_SGIX_reference_plane */ + +#ifndef GL_SGIX_resample +#define GL_SGIX_resample 1 +#define GL_PACK_RESAMPLE_SGIX 0x842E +#define GL_UNPACK_RESAMPLE_SGIX 0x842F +#define GL_RESAMPLE_REPLICATE_SGIX 0x8433 +#define GL_RESAMPLE_ZERO_FILL_SGIX 0x8434 +#define GL_RESAMPLE_DECIMATE_SGIX 0x8430 +#endif /* GL_SGIX_resample */ + +#ifndef GL_SGIX_scalebias_hint +#define GL_SGIX_scalebias_hint 1 +#define GL_SCALEBIAS_HINT_SGIX 0x8322 +#endif /* GL_SGIX_scalebias_hint */ + +#ifndef GL_SGIX_shadow +#define GL_SGIX_shadow 1 +#define GL_TEXTURE_COMPARE_SGIX 0x819A +#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B +#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C +#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D +#endif /* GL_SGIX_shadow */ + +#ifndef GL_SGIX_shadow_ambient +#define GL_SGIX_shadow_ambient 1 +#define GL_SHADOW_AMBIENT_SGIX 0x80BF +#endif /* GL_SGIX_shadow_ambient */ + +#ifndef GL_SGIX_sprite +#define GL_SGIX_sprite 1 +#define GL_SPRITE_SGIX 0x8148 +#define GL_SPRITE_MODE_SGIX 0x8149 +#define GL_SPRITE_AXIS_SGIX 0x814A +#define GL_SPRITE_TRANSLATION_SGIX 0x814B +#define GL_SPRITE_AXIAL_SGIX 0x814C +#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D +#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E + typedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC) (GLenum pname, GLfloat param); + typedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC) (GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC) (GLenum pname, GLint param); + typedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC) (GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glSpriteParameterfSGIX (GLenum pname, GLfloat param); + GLAPI void APIENTRY glSpriteParameterfvSGIX (GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glSpriteParameteriSGIX (GLenum pname, GLint param); + GLAPI void APIENTRY glSpriteParameterivSGIX (GLenum pname, const GLint *params); +#endif +#endif /* GL_SGIX_sprite */ + +#ifndef GL_SGIX_subsample +#define GL_SGIX_subsample 1 +#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0 +#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1 +#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2 +#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3 +#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4 +#endif /* GL_SGIX_subsample */ + +#ifndef GL_SGIX_tag_sample_buffer +#define GL_SGIX_tag_sample_buffer 1 + typedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glTagSampleBufferSGIX (void); +#endif +#endif /* GL_SGIX_tag_sample_buffer */ + +#ifndef GL_SGIX_texture_add_env +#define GL_SGIX_texture_add_env 1 +#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE +#endif /* GL_SGIX_texture_add_env */ + +#ifndef GL_SGIX_texture_coordinate_clamp +#define GL_SGIX_texture_coordinate_clamp 1 +#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369 +#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A +#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B +#endif /* GL_SGIX_texture_coordinate_clamp */ + +#ifndef GL_SGIX_texture_lod_bias +#define GL_SGIX_texture_lod_bias 1 +#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E +#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F +#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190 +#endif /* GL_SGIX_texture_lod_bias */ + +#ifndef GL_SGIX_texture_multi_buffer +#define GL_SGIX_texture_multi_buffer 1 +#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E +#endif /* GL_SGIX_texture_multi_buffer */ + +#ifndef GL_SGIX_texture_scale_bias +#define GL_SGIX_texture_scale_bias 1 +#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179 +#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A +#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B +#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C +#endif /* GL_SGIX_texture_scale_bias */ + +#ifndef GL_SGIX_vertex_preclip +#define GL_SGIX_vertex_preclip 1 +#define GL_VERTEX_PRECLIP_SGIX 0x83EE +#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF +#endif /* GL_SGIX_vertex_preclip */ + +#ifndef GL_SGIX_ycrcb +#define GL_SGIX_ycrcb 1 +#define GL_YCRCB_422_SGIX 0x81BB +#define GL_YCRCB_444_SGIX 0x81BC +#endif /* GL_SGIX_ycrcb */ + +#ifndef GL_SGIX_ycrcb_subsample +#define GL_SGIX_ycrcb_subsample 1 +#endif /* GL_SGIX_ycrcb_subsample */ + +#ifndef GL_SGIX_ycrcba +#define GL_SGIX_ycrcba 1 +#define GL_YCRCB_SGIX 0x8318 +#define GL_YCRCBA_SGIX 0x8319 +#endif /* GL_SGIX_ycrcba */ + +#ifndef GL_SGI_color_matrix +#define GL_SGI_color_matrix 1 +#define GL_COLOR_MATRIX_SGI 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB +#endif /* GL_SGI_color_matrix */ + +#ifndef GL_SGI_color_table +#define GL_SGI_color_table 1 +#define GL_COLOR_TABLE_SGI 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2 +#define GL_PROXY_COLOR_TABLE_SGI 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5 +#define GL_COLOR_TABLE_SCALE_SGI 0x80D6 +#define GL_COLOR_TABLE_BIAS_SGI 0x80D7 +#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8 +#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF + typedef void (APIENTRYP PFNGLCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); + typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, const GLfloat *params); + typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, const GLint *params); + typedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); + typedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC) (GLenum target, GLenum format, GLenum type, void *table); + typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, GLfloat *params); + typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glColorTableSGI (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); + GLAPI void APIENTRY glColorTableParameterfvSGI (GLenum target, GLenum pname, const GLfloat *params); + GLAPI void APIENTRY glColorTableParameterivSGI (GLenum target, GLenum pname, const GLint *params); + GLAPI void APIENTRY glCopyColorTableSGI (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); + GLAPI void APIENTRY glGetColorTableSGI (GLenum target, GLenum format, GLenum type, void *table); + GLAPI void APIENTRY glGetColorTableParameterfvSGI (GLenum target, GLenum pname, GLfloat *params); + GLAPI void APIENTRY glGetColorTableParameterivSGI (GLenum target, GLenum pname, GLint *params); +#endif +#endif /* GL_SGI_color_table */ + +#ifndef GL_SGI_texture_color_table +#define GL_SGI_texture_color_table 1 +#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC +#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD +#endif /* GL_SGI_texture_color_table */ + +#ifndef GL_SUNX_constant_data +#define GL_SUNX_constant_data 1 +#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 +#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 + typedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glFinishTextureSUNX (void); +#endif +#endif /* GL_SUNX_constant_data */ + +#ifndef GL_SUN_convolution_border_modes +#define GL_SUN_convolution_border_modes 1 +#define GL_WRAP_BORDER_SUN 0x81D4 +#endif /* GL_SUN_convolution_border_modes */ + +#ifndef GL_SUN_global_alpha +#define GL_SUN_global_alpha 1 +#define GL_GLOBAL_ALPHA_SUN 0x81D9 +#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA + typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC) (GLbyte factor); + typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC) (GLshort factor); + typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC) (GLint factor); + typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC) (GLfloat factor); + typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC) (GLdouble factor); + typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC) (GLubyte factor); + typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC) (GLushort factor); + typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC) (GLuint factor); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glGlobalAlphaFactorbSUN (GLbyte factor); + GLAPI void APIENTRY glGlobalAlphaFactorsSUN (GLshort factor); + GLAPI void APIENTRY glGlobalAlphaFactoriSUN (GLint factor); + GLAPI void APIENTRY glGlobalAlphaFactorfSUN (GLfloat factor); + GLAPI void APIENTRY glGlobalAlphaFactordSUN (GLdouble factor); + GLAPI void APIENTRY glGlobalAlphaFactorubSUN (GLubyte factor); + GLAPI void APIENTRY glGlobalAlphaFactorusSUN (GLushort factor); + GLAPI void APIENTRY glGlobalAlphaFactoruiSUN (GLuint factor); +#endif +#endif /* GL_SUN_global_alpha */ + +#ifndef GL_SUN_mesh_array +#define GL_SUN_mesh_array 1 +#define GL_QUAD_MESH_SUN 0x8614 +#define GL_TRIANGLE_MESH_SUN 0x8615 + typedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC) (GLenum mode, GLint first, GLsizei count, GLsizei width); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glDrawMeshArraysSUN (GLenum mode, GLint first, GLsizei count, GLsizei width); +#endif +#endif /* GL_SUN_mesh_array */ + +#ifndef GL_SUN_slice_accum +#define GL_SUN_slice_accum 1 +#define GL_SLICE_ACCUM_SUN 0x85CC +#endif /* GL_SUN_slice_accum */ + +#ifndef GL_SUN_triangle_list +#define GL_SUN_triangle_list 1 +#define GL_RESTART_SUN 0x0001 +#define GL_REPLACE_MIDDLE_SUN 0x0002 +#define GL_REPLACE_OLDEST_SUN 0x0003 +#define GL_TRIANGLE_LIST_SUN 0x81D7 +#define GL_REPLACEMENT_CODE_SUN 0x81D8 +#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0 +#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1 +#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2 +#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3 +#define GL_R1UI_V3F_SUN 0x85C4 +#define GL_R1UI_C4UB_V3F_SUN 0x85C5 +#define GL_R1UI_C3F_V3F_SUN 0x85C6 +#define GL_R1UI_N3F_V3F_SUN 0x85C7 +#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8 +#define GL_R1UI_T2F_V3F_SUN 0x85C9 +#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA +#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC) (GLuint code); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC) (GLushort code); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC) (GLubyte code); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC) (const GLuint *code); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC) (const GLushort *code); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC) (const GLubyte *code); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const void **pointer); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glReplacementCodeuiSUN (GLuint code); + GLAPI void APIENTRY glReplacementCodeusSUN (GLushort code); + GLAPI void APIENTRY glReplacementCodeubSUN (GLubyte code); + GLAPI void APIENTRY glReplacementCodeuivSUN (const GLuint *code); + GLAPI void APIENTRY glReplacementCodeusvSUN (const GLushort *code); + GLAPI void APIENTRY glReplacementCodeubvSUN (const GLubyte *code); + GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum type, GLsizei stride, const void **pointer); +#endif +#endif /* GL_SUN_triangle_list */ + +#ifndef GL_SUN_vertex +#define GL_SUN_vertex 1 + typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); + typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC) (const GLubyte *c, const GLfloat *v); + typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC) (const GLubyte *c, const GLfloat *v); + typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *v); + typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC) (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *n, const GLfloat *v); + typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *n, const GLfloat *v); + typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *v); + typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *v); + typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC) (const GLfloat *tc, const GLubyte *c, const GLfloat *v); + typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *v); + typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *n, const GLfloat *v); + typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); + typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC) (GLuint rc, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *v); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC) (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC) (const GLuint *rc, const GLubyte *c, const GLfloat *v); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *v); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *n, const GLfloat *v); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *v); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#ifdef GL_GLEXT_PROTOTYPES + GLAPI void APIENTRY glColor4ubVertex2fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); + GLAPI void APIENTRY glColor4ubVertex2fvSUN (const GLubyte *c, const GLfloat *v); + GLAPI void APIENTRY glColor4ubVertex3fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glColor4ubVertex3fvSUN (const GLubyte *c, const GLfloat *v); + GLAPI void APIENTRY glColor3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glColor3fVertex3fvSUN (const GLfloat *c, const GLfloat *v); + GLAPI void APIENTRY glNormal3fVertex3fSUN (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glNormal3fVertex3fvSUN (const GLfloat *n, const GLfloat *v); + GLAPI void APIENTRY glColor4fNormal3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glColor4fNormal3fVertex3fvSUN (const GLfloat *c, const GLfloat *n, const GLfloat *v); + GLAPI void APIENTRY glTexCoord2fVertex3fSUN (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glTexCoord2fVertex3fvSUN (const GLfloat *tc, const GLfloat *v); + GLAPI void APIENTRY glTexCoord4fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glTexCoord4fVertex4fvSUN (const GLfloat *tc, const GLfloat *v); + GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fSUN (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fvSUN (const GLfloat *tc, const GLubyte *c, const GLfloat *v); + GLAPI void APIENTRY glTexCoord2fColor3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glTexCoord2fColor3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *v); + GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *n, const GLfloat *v); + GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); + GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); + GLAPI void APIENTRY glReplacementCodeuiVertex3fSUN (GLuint rc, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glReplacementCodeuiVertex3fvSUN (const GLuint *rc, const GLfloat *v); + GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fSUN (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fvSUN (const GLuint *rc, const GLubyte *c, const GLfloat *v); + GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *v); + GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fSUN (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *n, const GLfloat *v); + GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); + GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *v); + GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); + GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); + GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#endif +#endif /* GL_SUN_vertex */ + +#ifndef GL_WIN_phong_shading +#define GL_WIN_phong_shading 1 +#define GL_PHONG_WIN 0x80EA +#define GL_PHONG_HINT_WIN 0x80EB +#endif /* GL_WIN_phong_shading */ + +#ifndef GL_WIN_specular_fog +#define GL_WIN_specular_fog 1 +#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC +#endif /* GL_WIN_specular_fog */ + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src_v2/platform/khrplatform.h b/src_v2/platform/khrplatform.h new file mode 100644 index 0000000..0c320b1 --- /dev/null +++ b/src_v2/platform/khrplatform.h @@ -0,0 +1,311 @@ +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_APIENTRY + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) +/* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIENTRY + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) +/* Win32 but not WinCE */ +# define KHRONOS_APIENTRY __stdcall +#else +# define KHRONOS_APIENTRY +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ \ No newline at end of file diff --git a/src_v2/platform/lumenarium_assert.h b/src_v2/platform/lumenarium_assert.h new file mode 100644 index 0000000..69e837b --- /dev/null +++ b/src_v2/platform/lumenarium_assert.h @@ -0,0 +1,34 @@ +/* date = March 26th 2022 3:42 pm */ + +#ifndef LUMENARIUM_ASSERT_H +#define LUMENARIUM_ASSERT_H + +#if !defined(PLATFORM_wasm) +// 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) + +#else +WASM_EXTERN void wasm_assert_always(char* file, u32 file_len, u32 line); +# define assert_always wasm_assert_always(__FILE__, sizeof(__FILE__), __LINE__) +#endif // defined(PLATFORM_WASM) + +#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 + +#endif //LUMENARIUM_ASSERT_H diff --git a/src_v2/platform/lumenarium_compiler_flags.h b/src_v2/platform/lumenarium_compiler_flags.h index c137adf..fa9475f 100644 --- a/src_v2/platform/lumenarium_compiler_flags.h +++ b/src_v2/platform/lumenarium_compiler_flags.h @@ -10,7 +10,7 @@ # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif -#ifdef DEBUG +#if defined(DEBUG) # define USE_ASSERTS 1 #endif diff --git a/src_v2/platform/lumenarium_platform.h b/src_v2/platform/lumenarium_platform.h index 4a74d1e..67cf492 100644 --- a/src_v2/platform/lumenarium_platform.h +++ b/src_v2/platform/lumenarium_platform.h @@ -77,6 +77,8 @@ 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); +String platform_get_exe_path(Allocator* allocator); +bool platform_pwd_set(String path); typedef u32 Platform_Enum_Dir_Flags; enum @@ -291,4 +293,44 @@ s32 platform_Socket_set_opt(); /////////////////////////////////////// // Graphics Integration +#define PLATFORM_SHADER_MAX_ATTRS 8 +#define PLATFORM_SHADER_ATTR_LAST (u32)(1 << 31) +struct Platform_Shader +{ + u32 id; + u32 attrs[PLATFORM_SHADER_MAX_ATTRS]; +}; + +struct Platform_Geometry_Buffer +{ + u32 buffer_id_vao; + u32 buffer_id_vertices; + u32 buffer_id_indices; + u32 indices_len; +}; + +struct Platform_Graphics_Frame_Desc +{ + v4 clear_color; + v2 viewport_min; + v2 viewport_max; +}; + +void platform_frame_begin(Platform_Graphics_Frame_Desc desc); +void platform_frame_clear(); + +Platform_Geometry_Buffer platform_geometry_buffer_create(r32* vertices, u32 vertices_len, u32* indices, u32 indices_len); +Platform_Shader platform_shader_create( + String code_vert, String code_frag, String* attribs, u32 attribs_len + ); +void platform_vertex_attrib_pointer( + Platform_Geometry_Buffer geo, Platform_Shader shader, u32 attrib_index + ); + +void platform_geometry_bind(Platform_Geometry_Buffer geo); +void platform_shader_bind(Platform_Shader shader); +void platform_geometry_draw(Platform_Geometry_Buffer geo); +void platform_vertex_attrib_pointer( + Platform_Geometry_Buffer geo, Platform_Shader shader, u32 attr_index + ); #endif //LUMENARIUM_PLATFORM_H diff --git a/src_v2/platform/lumenarium_platform_common_includes.h b/src_v2/platform/lumenarium_platform_common_includes.h index 46e6c47..421b694 100644 --- a/src_v2/platform/lumenarium_platform_common_includes.h +++ b/src_v2/platform/lumenarium_platform_common_includes.h @@ -11,6 +11,12 @@ #include +#include "lumenarium_assert.h" + +#include "glcorearb.h" +#include "glext.h" +#include "wglext.h" + #if 0 #define HMM_SINF sin #define HMM_COSF cos diff --git a/src_v2/platform/osx/lumenarium_osx_time.cpp b/src_v2/platform/osx/lumenarium_osx_time.cpp new file mode 100644 index 0000000..39c1331 --- /dev/null +++ b/src_v2/platform/osx/lumenarium_osx_time.cpp @@ -0,0 +1,4 @@ + +// I think this SO post might be helpful +// https://stackoverflow.com/questions/30575995/multi-platform-equivalent-to-queryperformancecounter + diff --git a/src_v2/platform/sokol_gfx.h b/src_v2/platform/sokol_gfx.h new file mode 100644 index 0000000..94c780c --- /dev/null +++ b/src_v2/platform/sokol_gfx.h @@ -0,0 +1,16066 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_IMPL) +#define SOKOL_GFX_IMPL +#endif +#ifndef SOKOL_GFX_INCLUDED +/* + sokol_gfx.h -- simple 3D API wrapper + + Project URL: https://github.com/floooh/sokol + + Example code: https://github.com/floooh/sokol-samples + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GFX_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + In the same place define one of the following to select the rendering + backend: + #define SOKOL_GLCORE33 + #define SOKOL_GLES2 + #define SOKOL_GLES3 + #define SOKOL_D3D11 + #define SOKOL_METAL + #define SOKOL_WGPU + #define SOKOL_DUMMY_BACKEND + + I.e. for the GL 3.3 Core Profile it should look like this: + + #include ... + #include ... + #define SOKOL_IMPL + #define SOKOL_GLCORE33 + #include "sokol_gfx.h" + + The dummy backend replaces the platform-specific backend code with empty + stub functions. This is useful for writing tests that need to run on the + command line. + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) + SOKOL_FREE(p) - your own free function (default: free(p)) + SOKOL_LOG(msg) - your own logging function (default: puts(msg)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_GFX_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GFX_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_TRACE_HOOKS - enable trace hook callbacks (search below for TRACE HOOKS) + SOKOL_EXTERNAL_GL_LOADER - indicates that you're using your own GL loader, in this case + sokol_gfx.h will not include any platform GL headers and disable + the integrated Win32 GL loader + + If sokol_gfx.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GFX_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + If you want to compile without deprecated structs and functions, + define: + + SOKOL_NO_DEPRECATED + + Optionally define the following to force debug checks and validations + even in release mode: + + SOKOL_DEBUG - by default this is defined if _DEBUG is defined + + sokol_gfx DOES NOT: + =================== + - create a window or the 3D-API context/device, you must do this + before sokol_gfx is initialized, and pass any required information + (like 3D device pointers) to the sokol_gfx initialization call + + - present the rendered frame, how this is done exactly usually depends + on how the window and 3D-API context/device was created + + - provide a unified shader language, instead 3D-API-specific shader + source-code or shader-bytecode must be provided (for the "official" + offline shader cross-compiler, see here: + https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) + + STEP BY STEP + ============ + --- to initialize sokol_gfx, after creating a window and a 3D-API + context/device, call: + + sg_setup(const sg_desc*) + + --- create resource objects (at least buffers, shaders and pipelines, + and optionally images and passes): + + sg_buffer sg_make_buffer(const sg_buffer_desc*) + sg_image sg_make_image(const sg_image_desc*) + sg_shader sg_make_shader(const sg_shader_desc*) + sg_pipeline sg_make_pipeline(const sg_pipeline_desc*) + sg_pass sg_make_pass(const sg_pass_desc*) + + --- start rendering to the default frame buffer with: + + sg_begin_default_pass(const sg_pass_action* action, int width, int height) + + ...or alternatively with: + + sg_begin_default_passf(const sg_pass_action* action, float width, float height) + + ...which takes the framebuffer width and height as float values. + + --- or start rendering to an offscreen framebuffer with: + + sg_begin_pass(sg_pass pass, const sg_pass_action* action) + + --- set the pipeline state for the next draw call with: + + sg_apply_pipeline(sg_pipeline pip) + + --- fill an sg_bindings struct with the resource bindings for the next + draw call (1..N vertex buffers, 0 or 1 index buffer, 0..N image objects + to use as textures each on the vertex-shader- and fragment-shader-stage + and then call + + sg_apply_bindings(const sg_bindings* bindings) + + to update the resource bindings + + --- optionally update shader uniform data with: + + sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) + + Read the section 'UNIFORM DATA LAYOUT' to learn about the expected memory layout + of the uniform data passed into sg_apply_uniforms(). + + --- kick off a draw call with: + + sg_draw(int base_element, int num_elements, int num_instances) + + The sg_draw() function unifies all the different ways to render primitives + in a single call (indexed vs non-indexed rendering, and instanced vs non-instanced + rendering). In case of indexed rendering, base_element and num_element specify + indices in the currently bound index buffer. In case of non-indexed rendering + base_element and num_elements specify vertices in the currently bound + vertex-buffer(s). To perform instanced rendering, the rendering pipeline + must be setup for instancing (see sg_pipeline_desc below), a separate vertex buffer + containing per-instance data must be bound, and the num_instances parameter + must be > 1. + + --- finish the current rendering pass with: + + sg_end_pass() + + --- when done with the current frame, call + + sg_commit() + + --- at the end of your program, shutdown sokol_gfx with: + + sg_shutdown() + + --- if you need to destroy resources before sg_shutdown(), call: + + sg_destroy_buffer(sg_buffer buf) + sg_destroy_image(sg_image img) + sg_destroy_shader(sg_shader shd) + sg_destroy_pipeline(sg_pipeline pip) + sg_destroy_pass(sg_pass pass) + + --- to set a new viewport rectangle, call + + sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) + + ...or if you want to specifiy the viewport rectangle with float values: + + sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) + + --- to set a new scissor rect, call: + + sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) + + ...or with float values: + + sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) + + Both sg_apply_viewport() and sg_apply_scissor_rect() must be called + inside a rendering pass + + Note that sg_begin_default_pass() and sg_begin_pass() will reset both the + viewport and scissor rectangles to cover the entire framebuffer. + + --- to update (overwrite) the content of buffer and image resources, call: + + sg_update_buffer(sg_buffer buf, const sg_range* data) + sg_update_image(sg_image img, const sg_image_data* data) + + Buffers and images to be updated must have been created with + SG_USAGE_DYNAMIC or SG_USAGE_STREAM + + Only one update per frame is allowed for buffer and image resources when + using the sg_update_*() functions. The rationale is to have a simple + countermeasure to avoid the CPU scribbling over data the GPU is currently + using, or the CPU having to wait for the GPU + + Buffer and image updates can be partial, as long as a rendering + operation only references the valid (updated) data in the + buffer or image. + + --- to append a chunk of data to a buffer resource, call: + + int sg_append_buffer(sg_buffer buf, const sg_range* data) + + The difference to sg_update_buffer() is that sg_append_buffer() + can be called multiple times per frame to append new data to the + buffer piece by piece, optionally interleaved with draw calls referencing + the previously written data. + + sg_append_buffer() returns a byte offset to the start of the + written data, this offset can be assigned to + sg_bindings.vertex_buffer_offsets[n] or + sg_bindings.index_buffer_offset + + Code example: + + for (...) { + const void* data = ...; + const int num_bytes = ...; + int offset = sg_append_buffer(buf, &(sg_range) { .ptr=data, .size=num_bytes }); + bindings.vertex_buffer_offsets[0] = offset; + sg_apply_pipeline(pip); + sg_apply_bindings(&bindings); + sg_apply_uniforms(...); + sg_draw(...); + } + + A buffer to be used with sg_append_buffer() must have been created + with SG_USAGE_DYNAMIC or SG_USAGE_STREAM. + + If the application appends more data to the buffer then fits into + the buffer, the buffer will go into the "overflow" state for the + rest of the frame. + + Any draw calls attempting to render an overflown buffer will be + silently dropped (in debug mode this will also result in a + validation error). + + You can also check manually if a buffer is in overflow-state by calling + + bool sg_query_buffer_overflow(sg_buffer buf) + + NOTE: Due to restrictions in underlying 3D-APIs, appended chunks of + data will be 4-byte aligned in the destination buffer. This means + that there will be gaps in index buffers containing 16-bit indices + when the number of indices in a call to sg_append_buffer() is + odd. This isn't a problem when each call to sg_append_buffer() + is associated with one draw call, but will be problematic when + a single indexed draw call spans several appended chunks of indices. + + --- to check at runtime for optional features, limits and pixelformat support, + call: + + sg_features sg_query_features() + sg_limits sg_query_limits() + sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) + + --- if you need to call into the underlying 3D-API directly, you must call: + + sg_reset_state_cache() + + ...before calling sokol_gfx functions again + + --- you can inspect the original sg_desc structure handed to sg_setup() + by calling sg_query_desc(). This will return an sg_desc struct with + the default values patched in instead of any zero-initialized values + + --- you can inspect various internal resource attributes via: + + sg_buffer_info sg_query_buffer_info(sg_buffer buf) + sg_image_info sg_query_image_info(sg_image img) + sg_shader_info sg_query_shader_info(sg_shader shd) + sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) + sg_pass_info sg_query_pass_info(sg_pass pass) + + ...please note that the returned info-structs are tied quite closely + to sokol_gfx.h internals, and may change more often than other + public API functions and structs. + + --- you can ask at runtime what backend sokol_gfx.h has been compiled + for, or whether the GLES3 backend had to fall back to GLES2 with: + + sg_backend sg_query_backend(void) + + --- you can query the default resource creation parameters through the functions + + sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) + sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) + sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) + sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) + sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) + + These functions take a pointer to a desc structure which may contain + zero-initialized items for default values. These zero-init values + will be replaced with their concrete values in the returned desc + struct. + + ON INITIALIZATION: + ================== + When calling sg_setup(), a pointer to an sg_desc struct must be provided + which contains initialization options. These options provide two types + of information to sokol-gfx: + + (1) upper bounds and limits needed to allocate various internal + data structures: + - the max number of resources of each type that can + be alive at the same time, this is used for allocating + internal pools + - the max overall size of uniform data that can be + updated per frame, including a worst-case alignment + per uniform update (this worst-case alignment is 256 bytes) + - the max size of all dynamic resource updates (sg_update_buffer, + sg_append_buffer and sg_update_image) per frame + - the max number of entries in the texture sampler cache + (how many unique texture sampler can exist at the same time) + Not all of those limit values are used by all backends, but it is + good practice to provide them none-the-less. + + (2) 3D-API "context information" (sometimes also called "bindings"): + sokol_gfx.h doesn't create or initialize 3D API objects which are + closely related to the presentation layer (this includes the "rendering + device", the swapchain, and any objects which depend on the + swapchain). These API objects (or callback functions to obtain + them, if those objects might change between frames), must + be provided in a nested sg_context_desc struct inside the + sg_desc struct. If sokol_gfx.h is used together with + sokol_app.h, have a look at the sokol_glue.h header which provides + a convenience function to get a sg_context_desc struct filled out + with context information provided by sokol_app.h + + See the documention block of the sg_desc struct below for more information. + + + UNIFORM DATA LAYOUT: + ==================== + NOTE: if you use the sokol-shdc shader compiler tool, you don't need to worry + about the following details. + + The data that's passed into the sg_apply_uniforms() function must adhere to + specific layout rules so that the GPU shader finds the uniform block + items at the right offset. + + For the D3D11 and Metal backends, sokol-gfx only cares about the size of uniform + blocks, but not about the internal layout. The data will just be copied into + a uniform/constant buffer in a single operation and it's up you to arrange the + CPU-side layout so that it matches the GPU side layout. This also means that with + the D3D11 and Metal backends you are not limited to a 'cross-platform' subset + of uniform variable types. + + If you ever only use one of the D3D11, Metal *or* WebGPU backend, you can stop reading here. + + For the GL backends, the internal layout of uniform blocks matters though, + and you are limited to a small number of uniform variable types. This is + because sokol-gfx must be able to locate the uniform block members in order + to upload them to the GPU with glUniformXXX() calls. + + To describe the uniform block layout to sokol-gfx, the following information + must be passed to the sg_make_shader() call in the sg_shader_desc struct: + + - a hint about the used packing rule (either SG_UNIFORMLAYOUT_NATIVE or + SG_UNIFORMLAYOUT_STD140) + - a list of the uniform block members types in the correct order they + appear on the CPU side + + For example if the GLSL shader has the following uniform declarations: + + uniform mat4 mvp; + uniform vec2 offset0; + uniform vec2 offset1; + uniform vec2 offset2; + + ...and on the CPU side, there's a similar C struct: + + typedef struct { + float mvp[16]; + float offset0[2]; + float offset1[2]; + float offset2[2]; + } params_t; + + ...the uniform block description in the sg_shader_desc must look like this: + + sg_shader_desc desc = { + .vs.uniform_blocks[0] = { + .size = sizeof(params_t), + .layout = SG_UNIFORMLAYOUT_NATIVE, // this is the default and can be omitted + .uniforms = { + // order must be the same as in 'params_t': + [0] = { .name = "mvp", .type = SG_UNIFORMTYPE_MAT4 }, + [1] = { .name = "offset0", .type = SG_UNIFORMTYPE_VEC2 }, + [2] = { .name = "offset1", .type = SG_UNIFORMTYPE_VEC2 }, + [3] = { .name = "offset2", .type = SG_UNIFORMTYPE_VEC2 }, + } + } + }; + + With this information sokol-gfx can now compute the correct offsets of the data items + within the uniform block struct. + + The SG_UNIFORMLAYOUT_NATIVE packing rule works fine if only the GL backends are used, + but for proper D3D11/Metal/GL a subset of the std140 layout must be used which is + described in the next section: + + + CROSS-BACKEND COMMON UNIFORM DATA LAYOUT + ======================================== + For cross-platform / cross-3D-backend code it is important that the same uniform block + layout on the CPU side can be used for all sokol-gfx backends. To achieve this, + a common subset of the std140 layout must be used: + + - The uniform block layout hint in sg_shader_desc must be explicitely set to + SG_UNIFORMLAYOUT_STD140. + - Only the following GLSL uniform types can be used (with their associated sokol-gfx enums): + - float => SG_UNIFORMTYPE_FLOAT + - vec2 => SG_UNIFORMTYPE_FLOAT2 + - vec3 => SG_UNIFORMTYPE_FLOAT3 + - vec4 => SG_UNIFORMTYPE_FLOAT4 + - int => SG_UNIFORMTYPE_INT + - ivec2 => SG_UNIFORMTYPE_INT2 + - ivec3 => SG_UNIFORMTYPE_INT3 + - ivec4 => SG_UNIFORMTYPE_INT4 + - mat4 => SG_UNIFORMTYPE_MAT4 + - Alignment for those types must be as follows (in bytes): + - float => 4 + - vec2 => 8 + - vec3 => 16 + - vec4 => 16 + - int => 4 + - ivec2 => 8 + - ivec3 => 16 + - ivec4 => 16 + - mat4 => 16 + - Arrays are only allowed for the following types: vec4, int4, mat4. + + Note that the HLSL cbuffer layout rules are slightly different from the + std140 layout rules, this means that the cbuffer declarations in HLSL code + must be tweaked so that the layout is compatible with std140. + + The by far easiest way to tacke the common uniform block layout problem is + to use the sokol-shdc shader cross-compiler tool! + + + BACKEND-SPECIFIC TOPICS: + ======================== + --- The GL backends need to know about the internal structure of uniform + blocks, and the texture sampler-name and -type. The uniform layout details + are described in the UNIFORM DATA LAYOUT section above. + + // uniform block structure and texture image definition in sg_shader_desc: + sg_shader_desc desc = { + // uniform block description (size and internal structure) + .vs.uniform_blocks[0] = { + ... + }, + // one texture on the fragment-shader-stage, GLES2/WebGL needs name and image type + .fs.images[0] = { .name="tex", .type=SG_IMAGETYPE_ARRAY } + ... + }; + + --- the Metal and D3D11 backends only need to know the size of uniform blocks, + not their internal member structure, and they only need to know + the type of a texture sampler, not its name: + + sg_shader_desc desc = { + .vs.uniform_blocks[0].size = sizeof(params_t), + .fs.images[0].type = SG_IMAGETYPE_ARRAY, + ... + }; + + --- when creating a shader object, GLES2/WebGL need to know the vertex + attribute names as used in the vertex shader: + + sg_shader_desc desc = { + .attrs = { + [0] = { .name="position" }, + [1] = { .name="color1" } + } + }; + + The vertex attribute names provided when creating a shader will be + used later in sg_create_pipeline() for matching the vertex layout + to vertex shader inputs. + + --- on D3D11 you need to provide a semantic name and semantic index in the + shader description struct instead (see the D3D11 documentation on + D3D11_INPUT_ELEMENT_DESC for details): + + sg_shader_desc desc = { + .attrs = { + [0] = { .sem_name="POSITION", .sem_index=0 } + [1] = { .sem_name="COLOR", .sem_index=1 } + } + }; + + The provided semantic information will be used later in sg_create_pipeline() + to match the vertex layout to vertex shader inputs. + + --- on D3D11, and when passing HLSL source code (instead of byte code) to shader + creation, you can optionally define the shader model targets on the vertex + stage: + + sg_shader_Desc desc = { + .vs = { + ... + .d3d11_target = "vs_5_0" + }, + .fs = { + ... + .d3d11_target = "ps_5_0" + } + }; + + The default targets are "ps_4_0" and "fs_4_0". Note that those target names + are only used when compiling shaders from source. They are ignored when + creating a shader from bytecode. + + --- on Metal, GL 3.3 or GLES3/WebGL2, you don't need to provide an attribute + name or semantic name, since vertex attributes can be bound by their slot index + (this is mandatory in Metal, and optional in GL): + + sg_pipeline_desc desc = { + .layout = { + .attrs = { + [0] = { .format=SG_VERTEXFORMAT_FLOAT3 }, + [1] = { .format=SG_VERTEXFORMAT_FLOAT4 } + } + } + }; + + WORKING WITH CONTEXTS + ===================== + sokol-gfx allows to switch between different rendering contexts and + associate resource objects with contexts. This is useful to + create GL applications that render into multiple windows. + + A rendering context keeps track of all resources created while + the context is active. When the context is destroyed, all resources + "belonging to the context" are destroyed as well. + + A default context will be created and activated implicitly in + sg_setup(), and destroyed in sg_shutdown(). So for a typical application + which *doesn't* use multiple contexts, nothing changes, and calling + the context functions isn't necessary. + + Three functions have been added to work with contexts: + + --- sg_context sg_setup_context(): + This must be called once after a GL context has been created and + made active. + + --- void sg_activate_context(sg_context ctx) + This must be called after making a different GL context active. + Apart from 3D-API-specific actions, the call to sg_activate_context() + will internally call sg_reset_state_cache(). + + --- void sg_discard_context(sg_context ctx) + This must be called right before a GL context is destroyed and + will destroy all resources associated with the context (that + have been created while the context was active) The GL context must be + active at the time sg_discard_context(sg_context ctx) is called. + + Also note that resources (buffers, images, shaders and pipelines) must + only be used or destroyed while the same GL context is active that + was also active while the resource was created (an exception is + resource sharing on GL, such resources can be used while + another context is active, but must still be destroyed under + the same context that was active during creation). + + For more information, check out the multiwindow-glfw sample: + + https://github.com/floooh/sokol-samples/blob/master/glfw/multiwindow-glfw.c + + TRACE HOOKS: + ============ + sokol_gfx.h optionally allows to install "trace hook" callbacks for + each public API functions. When a public API function is called, and + a trace hook callback has been installed for this function, the + callback will be invoked with the parameters and result of the function. + This is useful for things like debugging- and profiling-tools, or + keeping track of resource creation and destruction. + + To use the trace hook feature: + + --- Define SOKOL_TRACE_HOOKS before including the implementation. + + --- Setup an sg_trace_hooks structure with your callback function + pointers (keep all function pointers you're not interested + in zero-initialized), optionally set the user_data member + in the sg_trace_hooks struct. + + --- Install the trace hooks by calling sg_install_trace_hooks(), + the return value of this function is another sg_trace_hooks + struct which contains the previously set of trace hooks. + You should keep this struct around, and call those previous + functions pointers from your own trace callbacks for proper + chaining. + + As an example of how trace hooks are used, have a look at the + imgui/sokol_gfx_imgui.h header which implements a realtime + debugging UI for sokol_gfx.h on top of Dear ImGui. + + A NOTE ON PORTABLE PACKED VERTEX FORMATS: + ========================================= + There are two things to consider when using packed + vertex formats like UBYTE4, SHORT2, etc which need to work + across all backends: + + - D3D11 can only convert *normalized* vertex formats to + floating point during vertex fetch, normalized formats + have a trailing 'N', and are "normalized" to a range + -1.0..+1.0 (for the signed formats) or 0.0..1.0 (for the + unsigned formats): + + - SG_VERTEXFORMAT_BYTE4N + - SG_VERTEXFORMAT_UBYTE4N + - SG_VERTEXFORMAT_SHORT2N + - SG_VERTEXFORMAT_USHORT2N + - SG_VERTEXFORMAT_SHORT4N + - SG_VERTEXFORMAT_USHORT4N + + D3D11 will not convert *non-normalized* vertex formats to floating point + vertex shader inputs, those can only be uses with the *ivecn* vertex shader + input types when D3D11 is used as backend (GL and Metal can use both formats) + + - SG_VERTEXFORMAT_BYTE4, + - SG_VERTEXFORMAT_UBYTE4 + - SG_VERTEXFORMAT_SHORT2 + - SG_VERTEXFORMAT_SHORT4 + + - WebGL/GLES2 cannot use integer vertex shader inputs (int or ivecn) + + - SG_VERTEXFORMAT_UINT10_N2 is not supported on WebGL/GLES2 + + So for a vertex input layout which works on all platforms, only use the following + vertex formats, and if needed "expand" the normalized vertex shader + inputs in the vertex shader by multiplying with 127.0, 255.0, 32767.0 or + 65535.0: + + - SG_VERTEXFORMAT_FLOAT, + - SG_VERTEXFORMAT_FLOAT2, + - SG_VERTEXFORMAT_FLOAT3, + - SG_VERTEXFORMAT_FLOAT4, + - SG_VERTEXFORMAT_BYTE4N, + - SG_VERTEXFORMAT_UBYTE4N, + - SG_VERTEXFORMAT_SHORT2N, + - SG_VERTEXFORMAT_USHORT2N + - SG_VERTEXFORMAT_SHORT4N, + - SG_VERTEXFORMAT_USHORT4N + + TODO: + ==== + - talk about asynchronous resource creation + + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_GFX_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GFX_API_DECL) +#define SOKOL_GFX_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GFX_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GFX_IMPL) +#define SOKOL_GFX_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GFX_API_DECL __declspec(dllimport) +#else +#define SOKOL_GFX_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + /* + Resource id typedefs: + + sg_buffer: vertex- and index-buffers + sg_image: textures and render targets + sg_shader: vertex- and fragment-shaders, uniform blocks + sg_pipeline: associated shader and vertex-layouts, and render states + sg_pass: a bundle of render targets and actions on them + sg_context: a 'context handle' for switching between 3D-API contexts + + Instead of pointers, resource creation functions return a 32-bit + number which uniquely identifies the resource object. + + The 32-bit resource id is split into a 16-bit pool index in the lower bits, + and a 16-bit 'unique counter' in the upper bits. The index allows fast + pool lookups, and combined with the unique-mask it allows to detect + 'dangling accesses' (trying to use an object which no longer exists, and + its pool slot has been reused for a new object) + + The resource ids are wrapped into a struct so that the compiler + can complain when the wrong resource type is used. + */ + typedef struct sg_buffer { uint32_t id; } sg_buffer; + typedef struct sg_image { uint32_t id; } sg_image; + typedef struct sg_shader { uint32_t id; } sg_shader; + typedef struct sg_pipeline { uint32_t id; } sg_pipeline; + typedef struct sg_pass { uint32_t id; } sg_pass; + typedef struct sg_context { uint32_t id; } sg_context; + + /* + sg_range is a pointer-size-pair struct used to pass memory blobs into + sokol-gfx. When initialized from a value type (array or struct), you can + use the SG_RANGE() macro to build an sg_range struct. For functions which + take either a sg_range pointer, or a (C++) sg_range reference, use the + SG_RANGE_REF macro as a solution which compiles both in C and C++. + */ + typedef struct sg_range { + const void* ptr; + size_t size; + } sg_range; + + // disabling this for every includer isn't great, but the warnings are also quite pointless +#if defined(_MSC_VER) +#pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */ +#pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */ +#endif +#if defined(__cplusplus) +#define SG_RANGE(x) sg_range{ &x, sizeof(x) } +#define SG_RANGE_REF(x) sg_range{ &x, sizeof(x) } +#else +#define SG_RANGE(x) (sg_range){ &x, sizeof(x) } +#define SG_RANGE_REF(x) &(sg_range){ &x, sizeof(x) } +#endif + + // various compile-time constants + enum { + SG_INVALID_ID = 0, + SG_NUM_SHADER_STAGES = 2, + SG_NUM_INFLIGHT_FRAMES = 2, + SG_MAX_COLOR_ATTACHMENTS = 4, + SG_MAX_SHADERSTAGE_BUFFERS = 8, + SG_MAX_SHADERSTAGE_IMAGES = 12, + SG_MAX_SHADERSTAGE_UBS = 4, + SG_MAX_UB_MEMBERS = 16, + SG_MAX_VERTEX_ATTRIBUTES = 16, /* NOTE: actual max vertex attrs can be less on GLES2, see sg_limits! */ + SG_MAX_MIPMAPS = 16, + SG_MAX_TEXTUREARRAY_LAYERS = 128 + }; + + /* + sg_color + + An RGBA color value. + */ + typedef struct sg_color { float r, g, b, a; } sg_color; + + /* + sg_backend + + The active 3D-API backend, use the function sg_query_backend() + to get the currently active backend. + + NOTE that SG_BACKEND_GLES2 will be returned if sokol-gfx was + compiled with SOKOL_GLES3, but the runtime platform doesn't support + GLES3/WebGL2 and sokol-gfx had to fallback to GLES2/WebGL. + */ + typedef enum sg_backend { + SG_BACKEND_GLCORE33, + SG_BACKEND_GLES2, + SG_BACKEND_GLES3, + SG_BACKEND_D3D11, + SG_BACKEND_METAL_IOS, + SG_BACKEND_METAL_MACOS, + SG_BACKEND_METAL_SIMULATOR, + SG_BACKEND_WGPU, + SG_BACKEND_DUMMY, + } sg_backend; + + /* + sg_pixel_format + + sokol_gfx.h basically uses the same pixel formats as WebGPU, since these + are supported on most newer GPUs. GLES2 and WebGL only supports a much + smaller subset of actually available pixel formats. Call + sg_query_pixelformat() to check at runtime if a pixel format supports the + desired features. + + A pixelformat name consist of three parts: + + - components (R, RG, RGB or RGBA) + - bit width per component (8, 16 or 32) + - component data type: + - unsigned normalized (no postfix) + - signed normalized (SN postfix) + - unsigned integer (UI postfix) + - signed integer (SI postfix) + - float (F postfix) + + Not all pixel formats can be used for everything, call sg_query_pixelformat() + to inspect the capabilities of a given pixelformat. The function returns + an sg_pixelformat_info struct with the following bool members: + + - sample: the pixelformat can be sampled as texture at least with + nearest filtering + - filter: the pixelformat can be samples as texture with linear + filtering + - render: the pixelformat can be used for render targets + - blend: blending is supported when using the pixelformat for + render targets + - msaa: multisample-antialiasing is supported when using the + pixelformat for render targets + - depth: the pixelformat can be used for depth-stencil attachments + + When targeting GLES2/WebGL, the only safe formats to use + as texture are SG_PIXELFORMAT_R8 and SG_PIXELFORMAT_RGBA8. For rendering + in GLES2/WebGL, only SG_PIXELFORMAT_RGBA8 is safe. All other formats + must be checked via sg_query_pixelformats(). + + The default pixel format for texture images is SG_PIXELFORMAT_RGBA8. + + The default pixel format for render target images is platform-dependent: + - for Metal and D3D11 it is SG_PIXELFORMAT_BGRA8 + - for GL backends it is SG_PIXELFORMAT_RGBA8 + + This is mainly because of the default framebuffer which is setup outside + of sokol_gfx.h. On some backends, using BGRA for the default frame buffer + allows more efficient frame flips. For your own offscreen-render-targets, + use whatever renderable pixel format is convenient for you. + */ + typedef enum sg_pixel_format { + _SG_PIXELFORMAT_DEFAULT, /* value 0 reserved for default-init */ + SG_PIXELFORMAT_NONE, + + SG_PIXELFORMAT_R8, + SG_PIXELFORMAT_R8SN, + SG_PIXELFORMAT_R8UI, + SG_PIXELFORMAT_R8SI, + + SG_PIXELFORMAT_R16, + SG_PIXELFORMAT_R16SN, + SG_PIXELFORMAT_R16UI, + SG_PIXELFORMAT_R16SI, + SG_PIXELFORMAT_R16F, + SG_PIXELFORMAT_RG8, + SG_PIXELFORMAT_RG8SN, + SG_PIXELFORMAT_RG8UI, + SG_PIXELFORMAT_RG8SI, + + SG_PIXELFORMAT_R32UI, + SG_PIXELFORMAT_R32SI, + SG_PIXELFORMAT_R32F, + SG_PIXELFORMAT_RG16, + SG_PIXELFORMAT_RG16SN, + SG_PIXELFORMAT_RG16UI, + SG_PIXELFORMAT_RG16SI, + SG_PIXELFORMAT_RG16F, + SG_PIXELFORMAT_RGBA8, + SG_PIXELFORMAT_RGBA8SN, + SG_PIXELFORMAT_RGBA8UI, + SG_PIXELFORMAT_RGBA8SI, + SG_PIXELFORMAT_BGRA8, + SG_PIXELFORMAT_RGB10A2, + SG_PIXELFORMAT_RG11B10F, + + SG_PIXELFORMAT_RG32UI, + SG_PIXELFORMAT_RG32SI, + SG_PIXELFORMAT_RG32F, + SG_PIXELFORMAT_RGBA16, + SG_PIXELFORMAT_RGBA16SN, + SG_PIXELFORMAT_RGBA16UI, + SG_PIXELFORMAT_RGBA16SI, + SG_PIXELFORMAT_RGBA16F, + + SG_PIXELFORMAT_RGBA32UI, + SG_PIXELFORMAT_RGBA32SI, + SG_PIXELFORMAT_RGBA32F, + + SG_PIXELFORMAT_DEPTH, + SG_PIXELFORMAT_DEPTH_STENCIL, + + SG_PIXELFORMAT_BC1_RGBA, + SG_PIXELFORMAT_BC2_RGBA, + SG_PIXELFORMAT_BC3_RGBA, + SG_PIXELFORMAT_BC4_R, + SG_PIXELFORMAT_BC4_RSN, + SG_PIXELFORMAT_BC5_RG, + SG_PIXELFORMAT_BC5_RGSN, + SG_PIXELFORMAT_BC6H_RGBF, + SG_PIXELFORMAT_BC6H_RGBUF, + SG_PIXELFORMAT_BC7_RGBA, + SG_PIXELFORMAT_PVRTC_RGB_2BPP, + SG_PIXELFORMAT_PVRTC_RGB_4BPP, + SG_PIXELFORMAT_PVRTC_RGBA_2BPP, + SG_PIXELFORMAT_PVRTC_RGBA_4BPP, + SG_PIXELFORMAT_ETC2_RGB8, + SG_PIXELFORMAT_ETC2_RGB8A1, + SG_PIXELFORMAT_ETC2_RGBA8, + SG_PIXELFORMAT_ETC2_RG11, + SG_PIXELFORMAT_ETC2_RG11SN, + + _SG_PIXELFORMAT_NUM, + _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF + } sg_pixel_format; + + /* + Runtime information about a pixel format, returned + by sg_query_pixelformat(). + */ + typedef struct sg_pixelformat_info { + bool sample; // pixel format can be sampled in shaders + bool filter; // pixel format can be sampled with filtering + bool render; // pixel format can be used as render target + bool blend; // alpha-blending is supported + bool msaa; // pixel format can be used as MSAA render target + bool depth; // pixel format is a depth format +#if defined(SOKOL_ZIG_BINDINGS) + uint32_t __pad[3]; +#endif + } sg_pixelformat_info; + + /* + Runtime information about available optional features, + returned by sg_query_features() + */ + typedef struct sg_features { + bool instancing; // hardware instancing supported + bool origin_top_left; // framebuffer and texture origin is in top left corner + bool multiple_render_targets; // offscreen render passes can have multiple render targets attached + bool msaa_render_targets; // offscreen render passes support MSAA antialiasing + bool imagetype_3d; // creation of SG_IMAGETYPE_3D images is supported + bool imagetype_array; // creation of SG_IMAGETYPE_ARRAY images is supported + bool image_clamp_to_border; // border color and clamp-to-border UV-wrap mode is supported + bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state + bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks +#if defined(SOKOL_ZIG_BINDINGS) + uint32_t __pad[3]; +#endif + } sg_features; + + /* + Runtime information about resource limits, returned by sg_query_limit() + */ + typedef struct sg_limits { + int max_image_size_2d; // max width/height of SG_IMAGETYPE_2D images + int max_image_size_cube; // max width/height of SG_IMAGETYPE_CUBE images + int max_image_size_3d; // max width/height/depth of SG_IMAGETYPE_3D images + int max_image_size_array; // max width/height of SG_IMAGETYPE_ARRAY images + int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images + int max_vertex_attrs; // <= SG_MAX_VERTEX_ATTRIBUTES or less (on some GLES2 impls) + int gl_max_vertex_uniform_vectors; // <= GL_MAX_VERTEX_UNIFORM_VECTORS (only on GL backends) + } sg_limits; + + /* + sg_resource_state + + The current state of a resource in its resource pool. + Resources start in the INITIAL state, which means the + pool slot is unoccupied and can be allocated. When a resource is + created, first an id is allocated, and the resource pool slot + is set to state ALLOC. After allocation, the resource is + initialized, which may result in the VALID or FAILED state. The + reason why allocation and initialization are separate is because + some resource types (e.g. buffers and images) might be asynchronously + initialized by the user application. If a resource which is not + in the VALID state is attempted to be used for rendering, rendering + operations will silently be dropped. + + The special INVALID state is returned in sg_query_xxx_state() if no + resource object exists for the provided resource id. + */ + typedef enum sg_resource_state { + SG_RESOURCESTATE_INITIAL, + SG_RESOURCESTATE_ALLOC, + SG_RESOURCESTATE_VALID, + SG_RESOURCESTATE_FAILED, + SG_RESOURCESTATE_INVALID, + _SG_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF + } sg_resource_state; + + /* + sg_usage + + A resource usage hint describing the update strategy of + buffers and images. This is used in the sg_buffer_desc.usage + and sg_image_desc.usage members when creating buffers + and images: + + SG_USAGE_IMMUTABLE: the resource will never be updated with + new data, instead the content of the + resource must be provided on creation + SG_USAGE_DYNAMIC: the resource will be updated infrequently + with new data (this could range from "once + after creation", to "quite often but not + every frame") + SG_USAGE_STREAM: the resource will be updated each frame + with new content + + The rendering backends use this hint to prevent that the + CPU needs to wait for the GPU when attempting to update + a resource that might be currently accessed by the GPU. + + Resource content is updated with the functions sg_update_buffer() or + sg_append_buffer() for buffer objects, and sg_update_image() for image + objects. For the sg_update_*() functions, only one update is allowed per + frame and resource object, while sg_append_buffer() can be called + multiple times per frame on the same buffer. The application must update + all data required for rendering (this means that the update data can be + smaller than the resource size, if only a part of the overall resource + size is used for rendering, you only need to make sure that the data that + *is* used is valid). + + The default usage is SG_USAGE_IMMUTABLE. + */ + typedef enum sg_usage { + _SG_USAGE_DEFAULT, /* value 0 reserved for default-init */ + SG_USAGE_IMMUTABLE, + SG_USAGE_DYNAMIC, + SG_USAGE_STREAM, + _SG_USAGE_NUM, + _SG_USAGE_FORCE_U32 = 0x7FFFFFFF + } sg_usage; + + /* + sg_buffer_type + + This indicates whether a buffer contains vertex- or index-data, + used in the sg_buffer_desc.type member when creating a buffer. + + The default value is SG_BUFFERTYPE_VERTEXBUFFER. + */ + typedef enum sg_buffer_type { + _SG_BUFFERTYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_BUFFERTYPE_VERTEXBUFFER, + SG_BUFFERTYPE_INDEXBUFFER, + _SG_BUFFERTYPE_NUM, + _SG_BUFFERTYPE_FORCE_U32 = 0x7FFFFFFF + } sg_buffer_type; + + /* + sg_index_type + + Indicates whether indexed rendering (fetching vertex-indices from an + index buffer) is used, and if yes, the index data type (16- or 32-bits). + This is used in the sg_pipeline_desc.index_type member when creating a + pipeline object. + + The default index type is SG_INDEXTYPE_NONE. + */ + typedef enum sg_index_type { + _SG_INDEXTYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_INDEXTYPE_NONE, + SG_INDEXTYPE_UINT16, + SG_INDEXTYPE_UINT32, + _SG_INDEXTYPE_NUM, + _SG_INDEXTYPE_FORCE_U32 = 0x7FFFFFFF + } sg_index_type; + + /* + sg_image_type + + Indicates the basic type of an image object (2D-texture, cubemap, + 3D-texture or 2D-array-texture). 3D- and array-textures are not supported + on the GLES2/WebGL backend (use sg_query_features().imagetype_3d and + sg_query_features().imagetype_array to check for support). The image type + is used in the sg_image_desc.type member when creating an image, and + in sg_shader_image_desc when describing a shader's texture sampler binding. + + The default image type when creating an image is SG_IMAGETYPE_2D. + */ + typedef enum sg_image_type { + _SG_IMAGETYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_IMAGETYPE_2D, + SG_IMAGETYPE_CUBE, + SG_IMAGETYPE_3D, + SG_IMAGETYPE_ARRAY, + _SG_IMAGETYPE_NUM, + _SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF + } sg_image_type; + + /* + sg_sampler_type + + Indicates the basic data type of a shader's texture sampler which + can be float , unsigned integer or signed integer. The sampler + type is used in the sg_shader_image_desc to describe the + sampler type of a shader's texture sampler binding. + + The default sampler type is SG_SAMPLERTYPE_FLOAT. + */ + typedef enum sg_sampler_type { + _SG_SAMPLERTYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_SAMPLERTYPE_FLOAT, + SG_SAMPLERTYPE_SINT, + SG_SAMPLERTYPE_UINT, + } sg_sampler_type; + + /* + sg_cube_face + + The cubemap faces. Use these as indices in the sg_image_desc.content + array. + */ + typedef enum sg_cube_face { + SG_CUBEFACE_POS_X, + SG_CUBEFACE_NEG_X, + SG_CUBEFACE_POS_Y, + SG_CUBEFACE_NEG_Y, + SG_CUBEFACE_POS_Z, + SG_CUBEFACE_NEG_Z, + SG_CUBEFACE_NUM, + _SG_CUBEFACE_FORCE_U32 = 0x7FFFFFFF + } sg_cube_face; + + /* + sg_shader_stage + + There are 2 shader stages: vertex- and fragment-shader-stage. + Each shader stage consists of: + + - one slot for a shader function (provided as source- or byte-code) + - SG_MAX_SHADERSTAGE_UBS slots for uniform blocks + - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures by + the shader function + */ + typedef enum sg_shader_stage { + SG_SHADERSTAGE_VS, + SG_SHADERSTAGE_FS, + _SG_SHADERSTAGE_FORCE_U32 = 0x7FFFFFFF + } sg_shader_stage; + + /* + sg_primitive_type + + This is the common subset of 3D primitive types supported across all 3D + APIs. This is used in the sg_pipeline_desc.primitive_type member when + creating a pipeline object. + + The default primitive type is SG_PRIMITIVETYPE_TRIANGLES. + */ + typedef enum sg_primitive_type { + _SG_PRIMITIVETYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_PRIMITIVETYPE_POINTS, + SG_PRIMITIVETYPE_LINES, + SG_PRIMITIVETYPE_LINE_STRIP, + SG_PRIMITIVETYPE_TRIANGLES, + SG_PRIMITIVETYPE_TRIANGLE_STRIP, + _SG_PRIMITIVETYPE_NUM, + _SG_PRIMITIVETYPE_FORCE_U32 = 0x7FFFFFFF + } sg_primitive_type; + + /* + sg_filter + + The filtering mode when sampling a texture image. This is + used in the sg_image_desc.min_filter and sg_image_desc.mag_filter + members when creating an image object. + + The default filter mode is SG_FILTER_NEAREST. + */ + typedef enum sg_filter { + _SG_FILTER_DEFAULT, /* value 0 reserved for default-init */ + SG_FILTER_NEAREST, + SG_FILTER_LINEAR, + SG_FILTER_NEAREST_MIPMAP_NEAREST, + SG_FILTER_NEAREST_MIPMAP_LINEAR, + SG_FILTER_LINEAR_MIPMAP_NEAREST, + SG_FILTER_LINEAR_MIPMAP_LINEAR, + _SG_FILTER_NUM, + _SG_FILTER_FORCE_U32 = 0x7FFFFFFF + } sg_filter; + + /* + sg_wrap + + The texture coordinates wrapping mode when sampling a texture + image. This is used in the sg_image_desc.wrap_u, .wrap_v + and .wrap_w members when creating an image. + + The default wrap mode is SG_WRAP_REPEAT. + + NOTE: SG_WRAP_CLAMP_TO_BORDER is not supported on all backends + and platforms. To check for support, call sg_query_features() + and check the "clamp_to_border" boolean in the returned + sg_features struct. + + Platforms which don't support SG_WRAP_CLAMP_TO_BORDER will silently fall back + to SG_WRAP_CLAMP_TO_EDGE without a validation error. + + Platforms which support clamp-to-border are: + + - all desktop GL platforms + - Metal on macOS + - D3D11 + + Platforms which do not support clamp-to-border: + + - GLES2/3 and WebGL/WebGL2 + - Metal on iOS + */ + typedef enum sg_wrap { + _SG_WRAP_DEFAULT, /* value 0 reserved for default-init */ + SG_WRAP_REPEAT, + SG_WRAP_CLAMP_TO_EDGE, + SG_WRAP_CLAMP_TO_BORDER, + SG_WRAP_MIRRORED_REPEAT, + _SG_WRAP_NUM, + _SG_WRAP_FORCE_U32 = 0x7FFFFFFF + } sg_wrap; + + /* + sg_border_color + + The border color to use when sampling a texture, and the UV wrap + mode is SG_WRAP_CLAMP_TO_BORDER. + + The default border color is SG_BORDERCOLOR_OPAQUE_BLACK + */ + typedef enum sg_border_color { + _SG_BORDERCOLOR_DEFAULT, /* value 0 reserved for default-init */ + SG_BORDERCOLOR_TRANSPARENT_BLACK, + SG_BORDERCOLOR_OPAQUE_BLACK, + SG_BORDERCOLOR_OPAQUE_WHITE, + _SG_BORDERCOLOR_NUM, + _SG_BORDERCOLOR_FORCE_U32 = 0x7FFFFFFF + } sg_border_color; + + /* + sg_vertex_format + + The data type of a vertex component. This is used to describe + the layout of vertex data when creating a pipeline object. + */ + typedef enum sg_vertex_format { + SG_VERTEXFORMAT_INVALID, + SG_VERTEXFORMAT_FLOAT, + SG_VERTEXFORMAT_FLOAT2, + SG_VERTEXFORMAT_FLOAT3, + SG_VERTEXFORMAT_FLOAT4, + SG_VERTEXFORMAT_BYTE4, + SG_VERTEXFORMAT_BYTE4N, + SG_VERTEXFORMAT_UBYTE4, + SG_VERTEXFORMAT_UBYTE4N, + SG_VERTEXFORMAT_SHORT2, + SG_VERTEXFORMAT_SHORT2N, + SG_VERTEXFORMAT_USHORT2N, + SG_VERTEXFORMAT_SHORT4, + SG_VERTEXFORMAT_SHORT4N, + SG_VERTEXFORMAT_USHORT4N, + SG_VERTEXFORMAT_UINT10_N2, + _SG_VERTEXFORMAT_NUM, + _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF + } sg_vertex_format; + + /* + sg_vertex_step + + Defines whether the input pointer of a vertex input stream is advanced + 'per vertex' or 'per instance'. The default step-func is + SG_VERTEXSTEP_PER_VERTEX. SG_VERTEXSTEP_PER_INSTANCE is used with + instanced-rendering. + + The vertex-step is part of the vertex-layout definition + when creating pipeline objects. + */ + typedef enum sg_vertex_step { + _SG_VERTEXSTEP_DEFAULT, /* value 0 reserved for default-init */ + SG_VERTEXSTEP_PER_VERTEX, + SG_VERTEXSTEP_PER_INSTANCE, + _SG_VERTEXSTEP_NUM, + _SG_VERTEXSTEP_FORCE_U32 = 0x7FFFFFFF + } sg_vertex_step; + + /* + sg_uniform_type + + The data type of a uniform block member. This is used to + describe the internal layout of uniform blocks when creating + a shader object. + */ + typedef enum sg_uniform_type { + SG_UNIFORMTYPE_INVALID, + SG_UNIFORMTYPE_FLOAT, + SG_UNIFORMTYPE_FLOAT2, + SG_UNIFORMTYPE_FLOAT3, + SG_UNIFORMTYPE_FLOAT4, + SG_UNIFORMTYPE_INT, + SG_UNIFORMTYPE_INT2, + SG_UNIFORMTYPE_INT3, + SG_UNIFORMTYPE_INT4, + SG_UNIFORMTYPE_MAT4, + _SG_UNIFORMTYPE_NUM, + _SG_UNIFORMTYPE_FORCE_U32 = 0x7FFFFFFF + } sg_uniform_type; + + /* + sg_uniform_layout + + A hint for the interior memory layout of uniform blocks. This is + only really relevant for the GL backend where the internal layout + of uniform blocks must be known to sokol-gfx. For all other backends the + internal memory layout of uniform blocks doesn't matter, sokol-gfx + will just pass uniform data as a single memory blob to the + 3D backend. + + SG_UNIFORMLAYOUT_NATIVE (default) + Native layout means that a 'backend-native' memory layout + is used. For the GL backend this means that uniforms + are packed tightly in memory (e.g. there are no padding + bytes). + + SG_UNIFORMLAYOUT_STD140 + The memory layout is a subset of std140. Arrays are only + allowed for the FLOAT4, INT4 and MAT4. Alignment is as + is as follows: + + FLOAT, INT: 4 byte alignment + FLOAT2, INT2: 8 byte alignment + FLOAT3, INT3: 16 byte alignment(!) + FLOAT4, INT4: 16 byte alignment + MAT4: 16 byte alignment + FLOAT4[], INT4[]: 16 byte alignment + + The overall size of the uniform block must be a multiple + of 16. + + For more information search for 'UNIFORM DATA LAYOUT' in the documentation block + at the start of the header. + */ + typedef enum sg_uniform_layout { + _SG_UNIFORMLAYOUT_DEFAULT, /* value 0 reserved for default-init */ + SG_UNIFORMLAYOUT_NATIVE, /* default: layout depends on currently active backend */ + SG_UNIFORMLAYOUT_STD140, /* std140: memory layout according to std140 */ + _SG_UNIFORMLAYOUT_NUM, + _SG_UNIFORMLAYOUT_FORCE_U32 = 0x7FFFFFFF + } sg_uniform_layout; + + /* + sg_cull_mode + + The face-culling mode, this is used in the + sg_pipeline_desc.cull_mode member when creating a + pipeline object. + + The default cull mode is SG_CULLMODE_NONE + */ + typedef enum sg_cull_mode { + _SG_CULLMODE_DEFAULT, /* value 0 reserved for default-init */ + SG_CULLMODE_NONE, + SG_CULLMODE_FRONT, + SG_CULLMODE_BACK, + _SG_CULLMODE_NUM, + _SG_CULLMODE_FORCE_U32 = 0x7FFFFFFF + } sg_cull_mode; + + /* + sg_face_winding + + The vertex-winding rule that determines a front-facing primitive. This + is used in the member sg_pipeline_desc.face_winding + when creating a pipeline object. + + The default winding is SG_FACEWINDING_CW (clockwise) + */ + typedef enum sg_face_winding { + _SG_FACEWINDING_DEFAULT, /* value 0 reserved for default-init */ + SG_FACEWINDING_CCW, + SG_FACEWINDING_CW, + _SG_FACEWINDING_NUM, + _SG_FACEWINDING_FORCE_U32 = 0x7FFFFFFF + } sg_face_winding; + + /* + sg_compare_func + + The compare-function for depth- and stencil-ref tests. + This is used when creating pipeline objects in the members: + + sg_pipeline_desc + .depth + .compare + .stencil + .front.compare + .back.compar + + The default compare func for depth- and stencil-tests is + SG_COMPAREFUNC_ALWAYS. + */ + typedef enum sg_compare_func { + _SG_COMPAREFUNC_DEFAULT, /* value 0 reserved for default-init */ + SG_COMPAREFUNC_NEVER, + SG_COMPAREFUNC_LESS, + SG_COMPAREFUNC_EQUAL, + SG_COMPAREFUNC_LESS_EQUAL, + SG_COMPAREFUNC_GREATER, + SG_COMPAREFUNC_NOT_EQUAL, + SG_COMPAREFUNC_GREATER_EQUAL, + SG_COMPAREFUNC_ALWAYS, + _SG_COMPAREFUNC_NUM, + _SG_COMPAREFUNC_FORCE_U32 = 0x7FFFFFFF + } sg_compare_func; + + /* + sg_stencil_op + + The operation performed on a currently stored stencil-value when a + comparison test passes or fails. This is used when creating a pipeline + object in the members: + + sg_pipeline_desc + .stencil + .front + .fail_op + .depth_fail_op + .pass_op + .back + .fail_op + .depth_fail_op + .pass_op + + The default value is SG_STENCILOP_KEEP. + */ + typedef enum sg_stencil_op { + _SG_STENCILOP_DEFAULT, /* value 0 reserved for default-init */ + SG_STENCILOP_KEEP, + SG_STENCILOP_ZERO, + SG_STENCILOP_REPLACE, + SG_STENCILOP_INCR_CLAMP, + SG_STENCILOP_DECR_CLAMP, + SG_STENCILOP_INVERT, + SG_STENCILOP_INCR_WRAP, + SG_STENCILOP_DECR_WRAP, + _SG_STENCILOP_NUM, + _SG_STENCILOP_FORCE_U32 = 0x7FFFFFFF + } sg_stencil_op; + + /* + sg_blend_factor + + The source and destination factors in blending operations. + This is used in the following members when creating a pipeline object: + + sg_pipeline_desc + .colors[i] + .blend + .src_factor_rgb + .dst_factor_rgb + .src_factor_alpha + .dst_factor_alpha + + The default value is SG_BLENDFACTOR_ONE for source + factors, and SG_BLENDFACTOR_ZERO for destination factors. + */ + typedef enum sg_blend_factor { + _SG_BLENDFACTOR_DEFAULT, /* value 0 reserved for default-init */ + SG_BLENDFACTOR_ZERO, + SG_BLENDFACTOR_ONE, + SG_BLENDFACTOR_SRC_COLOR, + SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR, + SG_BLENDFACTOR_SRC_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + SG_BLENDFACTOR_DST_COLOR, + SG_BLENDFACTOR_ONE_MINUS_DST_COLOR, + SG_BLENDFACTOR_DST_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA, + SG_BLENDFACTOR_SRC_ALPHA_SATURATED, + SG_BLENDFACTOR_BLEND_COLOR, + SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR, + SG_BLENDFACTOR_BLEND_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA, + _SG_BLENDFACTOR_NUM, + _SG_BLENDFACTOR_FORCE_U32 = 0x7FFFFFFF + } sg_blend_factor; + + /* + sg_blend_op + + Describes how the source and destination values are combined in the + fragment blending operation. It is used in the following members when + creating a pipeline object: + + sg_pipeline_desc + .colors[i] + .blend + .op_rgb + .op_alpha + + The default value is SG_BLENDOP_ADD. + */ + typedef enum sg_blend_op { + _SG_BLENDOP_DEFAULT, /* value 0 reserved for default-init */ + SG_BLENDOP_ADD, + SG_BLENDOP_SUBTRACT, + SG_BLENDOP_REVERSE_SUBTRACT, + _SG_BLENDOP_NUM, + _SG_BLENDOP_FORCE_U32 = 0x7FFFFFFF + } sg_blend_op; + + /* + sg_color_mask + + Selects the active color channels when writing a fragment color to the + framebuffer. This is used in the members + sg_pipeline_desc.colors[i].write_mask when creating a pipeline object. + + The default colormask is SG_COLORMASK_RGBA (write all colors channels) + + NOTE: since the color mask value 0 is reserved for the default value + (SG_COLORMASK_RGBA), use SG_COLORMASK_NONE if all color channels + should be disabled. + */ + typedef enum sg_color_mask { + _SG_COLORMASK_DEFAULT = 0, /* value 0 reserved for default-init */ + SG_COLORMASK_NONE = 0x10, /* special value for 'all channels disabled */ + SG_COLORMASK_R = 0x1, + SG_COLORMASK_G = 0x2, + SG_COLORMASK_RG = 0x3, + SG_COLORMASK_B = 0x4, + SG_COLORMASK_RB = 0x5, + SG_COLORMASK_GB = 0x6, + SG_COLORMASK_RGB = 0x7, + SG_COLORMASK_A = 0x8, + SG_COLORMASK_RA = 0x9, + SG_COLORMASK_GA = 0xA, + SG_COLORMASK_RGA = 0xB, + SG_COLORMASK_BA = 0xC, + SG_COLORMASK_RBA = 0xD, + SG_COLORMASK_GBA = 0xE, + SG_COLORMASK_RGBA = 0xF, + _SG_COLORMASK_FORCE_U32 = 0x7FFFFFFF + } sg_color_mask; + + /* + sg_action + + Defines what action should be performed at the start of a render pass: + + SG_ACTION_CLEAR: clear the render target image + SG_ACTION_LOAD: load the previous content of the render target image + SG_ACTION_DONTCARE: leave the render target image content undefined + + This is used in the sg_pass_action structure. + + The default action for all pass attachments is SG_ACTION_CLEAR, with the + clear color rgba = {0.5f, 0.5f, 0.5f, 1.0f], depth=1.0 and stencil=0. + + If you want to override the default behaviour, it is important to not + only set the clear color, but the 'action' field as well (as long as this + is in its _SG_ACTION_DEFAULT, the value fields will be ignored). + */ + typedef enum sg_action { + _SG_ACTION_DEFAULT, + SG_ACTION_CLEAR, + SG_ACTION_LOAD, + SG_ACTION_DONTCARE, + _SG_ACTION_NUM, + _SG_ACTION_FORCE_U32 = 0x7FFFFFFF + } sg_action; + + /* + sg_pass_action + + The sg_pass_action struct defines the actions to be performed + at the start of a rendering pass in the functions sg_begin_pass() + and sg_begin_default_pass(). + + A separate action and clear values can be defined for each + color attachment, and for the depth-stencil attachment. + + The default clear values are defined by the macros: + + - SG_DEFAULT_CLEAR_RED: 0.5f + - SG_DEFAULT_CLEAR_GREEN: 0.5f + - SG_DEFAULT_CLEAR_BLUE: 0.5f + - SG_DEFAULT_CLEAR_ALPHA: 1.0f + - SG_DEFAULT_CLEAR_DEPTH: 1.0f + - SG_DEFAULT_CLEAR_STENCIL: 0 + */ + typedef struct sg_color_attachment_action { + sg_action action; + sg_color value; + } sg_color_attachment_action; + + typedef struct sg_depth_attachment_action { + sg_action action; + float value; + } sg_depth_attachment_action; + + typedef struct sg_stencil_attachment_action { + sg_action action; + uint8_t value; + } sg_stencil_attachment_action; + + typedef struct sg_pass_action { + uint32_t _start_canary; + sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_depth_attachment_action depth; + sg_stencil_attachment_action stencil; + uint32_t _end_canary; + } sg_pass_action; + + /* + sg_bindings + + The sg_bindings structure defines the resource binding slots + of the sokol_gfx render pipeline, used as argument to the + sg_apply_bindings() function. + + A resource binding struct contains: + + - 1..N vertex buffers + - 0..N vertex buffer offsets + - 0..1 index buffers + - 0..1 index buffer offsets + - 0..N vertex shader stage images + - 0..N fragment shader stage images + + The max number of vertex buffer and shader stage images + are defined by the SG_MAX_SHADERSTAGE_BUFFERS and + SG_MAX_SHADERSTAGE_IMAGES configuration constants. + + The optional buffer offsets can be used to put different unrelated + chunks of vertex- and/or index-data into the same buffer objects. + */ + typedef struct sg_bindings { + uint32_t _start_canary; + sg_buffer vertex_buffers[SG_MAX_SHADERSTAGE_BUFFERS]; + int vertex_buffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_buffer index_buffer; + int index_buffer_offset; + sg_image vs_images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_image fs_images[SG_MAX_SHADERSTAGE_IMAGES]; + uint32_t _end_canary; + } sg_bindings; + + /* + sg_buffer_desc + + Creation parameters for sg_buffer objects, used in the + sg_make_buffer() call. + + The default configuration is: + + .size: 0 (*must* be >0 for buffers without data) + .type: SG_BUFFERTYPE_VERTEXBUFFER + .usage: SG_USAGE_IMMUTABLE + .data.ptr 0 (*must* be valid for immutable buffers) + .data.size 0 (*must* be > 0 for immutable buffers) + .label 0 (optional string label for trace hooks) + + The label will be ignored by sokol_gfx.h, it is only useful + when hooking into sg_make_buffer() or sg_init_buffer() via + the sg_install_trace_hooks() function. + + For immutable buffers which are initialized with initial data, + keep the .size item zero-initialized, and set the size together with the + pointer to the initial data in the .data item. + + For mutable buffers without initial data, keep the .data item + zero-initialized, and set the buffer size in the .size item instead. + + You can also set both size values, but currently both size values must + be identical (this may change in the future when the dynamic resource + management may become more flexible). + + ADVANCED TOPIC: Injecting native 3D-API buffers: + + The following struct members allow to inject your own GL, Metal + or D3D11 buffers into sokol_gfx: + + .gl_buffers[SG_NUM_INFLIGHT_FRAMES] + .mtl_buffers[SG_NUM_INFLIGHT_FRAMES] + .d3d11_buffer + + You must still provide all other struct items except the .data item, and + these must match the creation parameters of the native buffers you + provide. For SG_USAGE_IMMUTABLE, only provide a single native 3D-API + buffer, otherwise you need to provide SG_NUM_INFLIGHT_FRAMES buffers + (only for GL and Metal, not D3D11). Providing multiple buffers for GL and + Metal is necessary because sokol_gfx will rotate through them when + calling sg_update_buffer() to prevent lock-stalls. + + Note that it is expected that immutable injected buffer have already been + initialized with content, and the .content member must be 0! + + Also you need to call sg_reset_state_cache() after calling native 3D-API + functions, and before calling any sokol_gfx function. + */ + typedef struct sg_buffer_desc { + uint32_t _start_canary; + size_t size; + sg_buffer_type type; + sg_usage usage; + sg_range data; + const char* label; + /* GL specific */ + uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES]; + /* Metal specific */ + const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES]; + /* D3D11 specific */ + const void* d3d11_buffer; + /* WebGPU specific */ + const void* wgpu_buffer; + uint32_t _end_canary; + } sg_buffer_desc; + + /* + sg_image_data + + Defines the content of an image through a 2D array of sg_range structs. + The first array dimension is the cubemap face, and the second array + dimension the mipmap level. + */ + typedef struct sg_image_data { + sg_range subimage[SG_CUBEFACE_NUM][SG_MAX_MIPMAPS]; + } sg_image_data; + + /* + sg_image_desc + + Creation parameters for sg_image objects, used in the sg_make_image() + call. + + The default configuration is: + + .type: SG_IMAGETYPE_2D + .render_target: false + .width 0 (must be set to >0) + .height 0 (must be set to >0) + .num_slices 1 (3D textures: depth; array textures: number of layers) + .num_mipmaps: 1 + .usage: SG_USAGE_IMMUTABLE + .pixel_format: SG_PIXELFORMAT_RGBA8 for textures, or sg_desc.context.color_format for render targets + .sample_count: 1 for textures, or sg_desc.context.sample_count for render targets + .min_filter: SG_FILTER_NEAREST + .mag_filter: SG_FILTER_NEAREST + .wrap_u: SG_WRAP_REPEAT + .wrap_v: SG_WRAP_REPEAT + .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) + .border_color SG_BORDERCOLOR_OPAQUE_BLACK + .max_anisotropy 1 (must be 1..16) + .min_lod 0.0f + .max_lod FLT_MAX + .data an sg_image_data struct to define the initial content + .label 0 (optional string label for trace hooks) + + Q: Why is the default sample_count for render targets identical with the + "default sample count" from sg_desc.context.sample_count? + + A: So that it matches the default sample count in pipeline objects. Even + though it is a bit strange/confusing that offscreen render targets by default + get the same sample count as the default framebuffer, but it's better that + an offscreen render target created with default parameters matches + a pipeline object created with default parameters. + + NOTE: + + SG_IMAGETYPE_ARRAY and SG_IMAGETYPE_3D are not supported on WebGL/GLES2, + use sg_query_features().imagetype_array and + sg_query_features().imagetype_3d at runtime to check if array- and + 3D-textures are supported. + + Images with usage SG_USAGE_IMMUTABLE must be fully initialized by + providing a valid .data member which points to initialization data. + + ADVANCED TOPIC: Injecting native 3D-API textures: + + The following struct members allow to inject your own GL, Metal or D3D11 + textures into sokol_gfx: + + .gl_textures[SG_NUM_INFLIGHT_FRAMES] + .mtl_textures[SG_NUM_INFLIGHT_FRAMES] + .d3d11_texture + .d3d11_shader_resource_view + + For GL, you can also specify the texture target or leave it empty to use + the default texture target for the image type (GL_TEXTURE_2D for + SG_IMAGETYPE_2D etc) + + For D3D11, you can provide either a D3D11 texture, or a + shader-resource-view, or both. If only a texture is provided, a matching + shader-resource-view will be created. If only a shader-resource-view is + provided, the texture will be looked up from the shader-resource-view. + + The same rules apply as for injecting native buffers (see sg_buffer_desc + documentation for more details). + */ + typedef struct sg_image_desc { + uint32_t _start_canary; + sg_image_type type; + bool render_target; + int width; + int height; + int num_slices; + int num_mipmaps; + sg_usage usage; + sg_pixel_format pixel_format; + int sample_count; + sg_filter min_filter; + sg_filter mag_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + sg_border_color border_color; + uint32_t max_anisotropy; + float min_lod; + float max_lod; + sg_image_data data; + const char* label; + /* GL specific */ + uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES]; + uint32_t gl_texture_target; + /* Metal specific */ + const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; + /* D3D11 specific */ + const void* d3d11_texture; + const void* d3d11_shader_resource_view; + /* WebGPU specific */ + const void* wgpu_texture; + uint32_t _end_canary; + } sg_image_desc; + + /* + sg_shader_desc + + The structure sg_shader_desc defines all creation parameters for shader + programs, used as input to the sg_make_shader() function: + + - reflection information for vertex attributes (vertex shader inputs): + - vertex attribute name (required for GLES2, optional for GLES3 and GL) + - a semantic name and index (required for D3D11) + - for each shader-stage (vertex and fragment): + - the shader source or bytecode + - an optional entry function name + - an optional compile target (only for D3D11 when source is provided, + defaults are "vs_4_0" and "ps_4_0") + - reflection info for each uniform block used by the shader stage: + - the size of the uniform block in bytes + - a memory layout hint (native vs std140, only required for GL backends) + - reflection info for each uniform block member (only required for GL backends): + - member name + - member type (SG_UNIFORMTYPE_xxx) + - if the member is an array, the number of array items + - reflection info for the texture images used by the shader stage: + - the image type (SG_IMAGETYPE_xxx) + - the sampler type (SG_SAMPLERTYPE_xxx, default is SG_SAMPLERTYPE_FLOAT) + - the name of the texture sampler (required for GLES2, optional everywhere else) + + For all GL backends, shader source-code must be provided. For D3D11 and Metal, + either shader source-code or byte-code can be provided. + + For D3D11, if source code is provided, the d3dcompiler_47.dll will be loaded + on demand. If this fails, shader creation will fail. When compiling HLSL + source code, you can provide an optional target string via + sg_shader_stage_desc.d3d11_target, the default target is "vs_4_0" for the + vertex shader stage and "ps_4_0" for the pixel shader stage. + */ + typedef struct sg_shader_attr_desc { + const char* name; // GLSL vertex attribute name (only strictly required for GLES2) + const char* sem_name; // HLSL semantic name + int sem_index; // HLSL semantic index + } sg_shader_attr_desc; + + typedef struct sg_shader_uniform_desc { + const char* name; + sg_uniform_type type; + int array_count; + } sg_shader_uniform_desc; + + typedef struct sg_shader_uniform_block_desc { + size_t size; + sg_uniform_layout layout; + sg_shader_uniform_desc uniforms[SG_MAX_UB_MEMBERS]; + } sg_shader_uniform_block_desc; + + typedef struct sg_shader_image_desc { + const char* name; + sg_image_type image_type; + sg_sampler_type sampler_type; + } sg_shader_image_desc; + + typedef struct sg_shader_stage_desc { + const char* source; + sg_range bytecode; + const char* entry; + const char* d3d11_target; + sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + sg_shader_image_desc images[SG_MAX_SHADERSTAGE_IMAGES]; + } sg_shader_stage_desc; + + typedef struct sg_shader_desc { + uint32_t _start_canary; + sg_shader_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_shader_stage_desc vs; + sg_shader_stage_desc fs; + const char* label; + uint32_t _end_canary; + } sg_shader_desc; + + /* + sg_pipeline_desc + + The sg_pipeline_desc struct defines all creation parameters for an + sg_pipeline object, used as argument to the sg_make_pipeline() function: + + - the vertex layout for all input vertex buffers + - a shader object + - the 3D primitive type (points, lines, triangles, ...) + - the index type (none, 16- or 32-bit) + - all the fixed-function-pipeline state (depth-, stencil-, blend-state, etc...) + + If the vertex data has no gaps between vertex components, you can omit + the .layout.buffers[].stride and layout.attrs[].offset items (leave them + default-initialized to 0), sokol-gfx will then compute the offsets and + strides from the vertex component formats (.layout.attrs[].format). + Please note that ALL vertex attribute offsets must be 0 in order for the + automatic offset computation to kick in. + + The default configuration is as follows: + + .shader: 0 (must be initialized with a valid sg_shader id!) + .layout: + .buffers[]: vertex buffer layouts + .stride: 0 (if no stride is given it will be computed) + .step_func SG_VERTEXSTEP_PER_VERTEX + .step_rate 1 + .attrs[]: vertex attribute declarations + .buffer_index 0 the vertex buffer bind slot + .offset 0 (offsets can be omitted if the vertex layout has no gaps) + .format SG_VERTEXFORMAT_INVALID (must be initialized!) + .depth: + .pixel_format: sg_desc.context.depth_format + .compare: SG_COMPAREFUNC_ALWAYS + .write_enabled: false + .bias: 0.0f + .bias_slope_scale: 0.0f + .bias_clamp: 0.0f + .stencil: + .enabled: false + .front/back: + .compare: SG_COMPAREFUNC_ALWAYS + .depth_fail_op: SG_STENCILOP_KEEP + .pass_op: SG_STENCILOP_KEEP + .compare: SG_COMPAREFUNC_ALWAYS + .read_mask: 0 + .write_mask: 0 + .ref: 0 + .color_count 1 + .colors[0..color_count] + .pixel_format sg_desc.context.color_format + .write_mask: SG_COLORMASK_RGBA + .blend: + .enabled: false + .src_factor_rgb: SG_BLENDFACTOR_ONE + .dst_factor_rgb: SG_BLENDFACTOR_ZERO + .op_rgb: SG_BLENDOP_ADD + .src_factor_alpha: SG_BLENDFACTOR_ONE + .dst_factor_alpha: SG_BLENDFACTOR_ZERO + .op_alpha: SG_BLENDOP_ADD + .primitive_type: SG_PRIMITIVETYPE_TRIANGLES + .index_type: SG_INDEXTYPE_NONE + .cull_mode: SG_CULLMODE_NONE + .face_winding: SG_FACEWINDING_CW + .sample_count: sg_desc.context.sample_count + .blend_color: (sg_color) { 0.0f, 0.0f, 0.0f, 0.0f } + .alpha_to_coverage_enabled: false + .label 0 (optional string label for trace hooks) + */ + typedef struct sg_buffer_layout_desc { + int stride; + sg_vertex_step step_func; + int step_rate; +#if defined(SOKOL_ZIG_BINDINGS) + uint32_t __pad[2]; +#endif + } sg_buffer_layout_desc; + + typedef struct sg_vertex_attr_desc { + int buffer_index; + int offset; + sg_vertex_format format; +#if defined(SOKOL_ZIG_BINDINGS) + uint32_t __pad[2]; +#endif + } sg_vertex_attr_desc; + + typedef struct sg_layout_desc { + sg_buffer_layout_desc buffers[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_vertex_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; + } sg_layout_desc; + + typedef struct sg_stencil_face_state { + sg_compare_func compare; + sg_stencil_op fail_op; + sg_stencil_op depth_fail_op; + sg_stencil_op pass_op; + } sg_stencil_face_state; + + typedef struct sg_stencil_state { + bool enabled; + sg_stencil_face_state front; + sg_stencil_face_state back; + uint8_t read_mask; + uint8_t write_mask; + uint8_t ref; + } sg_stencil_state; + + typedef struct sg_depth_state { + sg_pixel_format pixel_format; + sg_compare_func compare; + bool write_enabled; + float bias; + float bias_slope_scale; + float bias_clamp; + } sg_depth_state; + + typedef struct sg_blend_state { + bool enabled; + sg_blend_factor src_factor_rgb; + sg_blend_factor dst_factor_rgb; + sg_blend_op op_rgb; + sg_blend_factor src_factor_alpha; + sg_blend_factor dst_factor_alpha; + sg_blend_op op_alpha; + } sg_blend_state; + + typedef struct sg_color_state { + sg_pixel_format pixel_format; + sg_color_mask write_mask; + sg_blend_state blend; + } sg_color_state; + + typedef struct sg_pipeline_desc { + uint32_t _start_canary; + sg_shader shader; + sg_layout_desc layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_primitive_type primitive_type; + sg_index_type index_type; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; + const char* label; + uint32_t _end_canary; + } sg_pipeline_desc; + + /* + sg_pass_desc + + Creation parameters for an sg_pass object, used as argument + to the sg_make_pass() function. + + A pass object contains 1..4 color-attachments and none, or one, + depth-stencil-attachment. Each attachment consists of + an image, and two additional indices describing + which subimage the pass will render to: one mipmap index, and + if the image is a cubemap, array-texture or 3D-texture, the + face-index, array-layer or depth-slice. + + Pass images must fulfill the following requirements: + + All images must have: + - been created as render target (sg_image_desc.render_target = true) + - the same size + - the same sample count + + In addition, all color-attachment images must have the same pixel format. + */ + typedef struct sg_pass_attachment_desc { + sg_image image; + int mip_level; + int slice; /* cube texture: face; array texture: layer; 3D texture: slice */ + } sg_pass_attachment_desc; + + typedef struct sg_pass_desc { + uint32_t _start_canary; + sg_pass_attachment_desc color_attachments[SG_MAX_COLOR_ATTACHMENTS]; + sg_pass_attachment_desc depth_stencil_attachment; + const char* label; + uint32_t _end_canary; + } sg_pass_desc; + + /* + sg_trace_hooks + + Installable callback functions to keep track of the sokol-gfx calls, + this is useful for debugging, or keeping track of resource creation + and destruction. + + Trace hooks are installed with sg_install_trace_hooks(), this returns + another sg_trace_hooks struct with the previous set of + trace hook function pointers. These should be invoked by the + new trace hooks to form a proper call chain. + */ + typedef struct sg_trace_hooks { + void* user_data; + void (*reset_state_cache)(void* user_data); + void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data); + void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data); + void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data); + void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data); + void (*make_pass)(const sg_pass_desc* desc, sg_pass result, void* user_data); + void (*destroy_buffer)(sg_buffer buf, void* user_data); + void (*destroy_image)(sg_image img, void* user_data); + void (*destroy_shader)(sg_shader shd, void* user_data); + void (*destroy_pipeline)(sg_pipeline pip, void* user_data); + void (*destroy_pass)(sg_pass pass, void* user_data); + void (*update_buffer)(sg_buffer buf, const sg_range* data, void* user_data); + void (*update_image)(sg_image img, const sg_image_data* data, void* user_data); + void (*append_buffer)(sg_buffer buf, const sg_range* data, int result, void* user_data); + void (*begin_default_pass)(const sg_pass_action* pass_action, int width, int height, void* user_data); + void (*begin_pass)(sg_pass pass, const sg_pass_action* pass_action, void* user_data); + void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_pipeline)(sg_pipeline pip, void* user_data); + void (*apply_bindings)(const sg_bindings* bindings, void* user_data); + void (*apply_uniforms)(sg_shader_stage stage, int ub_index, const sg_range* data, void* user_data); + void (*draw)(int base_element, int num_elements, int num_instances, void* user_data); + void (*end_pass)(void* user_data); + void (*commit)(void* user_data); + void (*alloc_buffer)(sg_buffer result, void* user_data); + void (*alloc_image)(sg_image result, void* user_data); + void (*alloc_shader)(sg_shader result, void* user_data); + void (*alloc_pipeline)(sg_pipeline result, void* user_data); + void (*alloc_pass)(sg_pass result, void* user_data); + void (*dealloc_buffer)(sg_buffer buf_id, void* user_data); + void (*dealloc_image)(sg_image img_id, void* user_data); + void (*dealloc_shader)(sg_shader shd_id, void* user_data); + void (*dealloc_pipeline)(sg_pipeline pip_id, void* user_data); + void (*dealloc_pass)(sg_pass pass_id, void* user_data); + void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data); + void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data); + void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data); + void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data); + void (*init_pass)(sg_pass pass_id, const sg_pass_desc* desc, void* user_data); + void (*uninit_buffer)(sg_buffer buf_id, void* user_data); + void (*uninit_image)(sg_image img_id, void* user_data); + void (*uninit_shader)(sg_shader shd_id, void* user_data); + void (*uninit_pipeline)(sg_pipeline pip_id, void* user_data); + void (*uninit_pass)(sg_pass pass_id, void* user_data); + void (*fail_buffer)(sg_buffer buf_id, void* user_data); + void (*fail_image)(sg_image img_id, void* user_data); + void (*fail_shader)(sg_shader shd_id, void* user_data); + void (*fail_pipeline)(sg_pipeline pip_id, void* user_data); + void (*fail_pass)(sg_pass pass_id, void* user_data); + void (*push_debug_group)(const char* name, void* user_data); + void (*pop_debug_group)(void* user_data); + void (*err_buffer_pool_exhausted)(void* user_data); + void (*err_image_pool_exhausted)(void* user_data); + void (*err_shader_pool_exhausted)(void* user_data); + void (*err_pipeline_pool_exhausted)(void* user_data); + void (*err_pass_pool_exhausted)(void* user_data); + void (*err_context_mismatch)(void* user_data); + void (*err_pass_invalid)(void* user_data); + void (*err_draw_invalid)(void* user_data); + void (*err_bindings_invalid)(void* user_data); + } sg_trace_hooks; + + /* + sg_buffer_info + sg_image_info + sg_shader_info + sg_pipeline_info + sg_pass_info + + These structs contain various internal resource attributes which + might be useful for debug-inspection. Please don't rely on the + actual content of those structs too much, as they are quite closely + tied to sokol_gfx.h internals and may change more frequently than + the other public API elements. + + The *_info structs are used as the return values of the following functions: + + sg_query_buffer_info() + sg_query_image_info() + sg_query_shader_info() + sg_query_pipeline_info() + sg_query_pass_info() + */ + typedef struct sg_slot_info { + sg_resource_state state; /* the current state of this resource slot */ + uint32_t res_id; /* type-neutral resource if (e.g. sg_buffer.id) */ + uint32_t ctx_id; /* the context this resource belongs to */ + } sg_slot_info; + + typedef struct sg_buffer_info { + sg_slot_info slot; /* resource pool slot info */ + uint32_t update_frame_index; /* frame index of last sg_update_buffer() */ + uint32_t append_frame_index; /* frame index of last sg_append_buffer() */ + int append_pos; /* current position in buffer for sg_append_buffer() */ + bool append_overflow; /* is buffer in overflow state (due to sg_append_buffer) */ + int num_slots; /* number of renaming-slots for dynamically updated buffers */ + int active_slot; /* currently active write-slot for dynamically updated buffers */ + } sg_buffer_info; + + typedef struct sg_image_info { + sg_slot_info slot; /* resource pool slot info */ + uint32_t upd_frame_index; /* frame index of last sg_update_image() */ + int num_slots; /* number of renaming-slots for dynamically updated images */ + int active_slot; /* currently active write-slot for dynamically updated images */ + int width; /* image width */ + int height; /* image height */ + } sg_image_info; + + typedef struct sg_shader_info { + sg_slot_info slot; /* resoure pool slot info */ + } sg_shader_info; + + typedef struct sg_pipeline_info { + sg_slot_info slot; /* resource pool slot info */ + } sg_pipeline_info; + + typedef struct sg_pass_info { + sg_slot_info slot; /* resource pool slot info */ + } sg_pass_info; + + /* + sg_desc + + The sg_desc struct contains configuration values for sokol_gfx, + it is used as parameter to the sg_setup() call. + + NOTE that all callback function pointers come in two versions, one without + a userdata pointer, and one with a userdata pointer. You would + either initialize one or the other depending on whether you pass data + to your callbacks. + + FIXME: explain the various configuration options + + The default configuration is: + + .buffer_pool_size 128 + .image_pool_size 128 + .shader_pool_size 32 + .pipeline_pool_size 64 + .pass_pool_size 16 + .context_pool_size 16 + .sampler_cache_size 64 + .uniform_buffer_size 4 MB (4*1024*1024) + .staging_buffer_size 8 MB (8*1024*1024) + + .context.color_format: default value depends on selected backend: + all GL backends: SG_PIXELFORMAT_RGBA8 + Metal and D3D11: SG_PIXELFORMAT_BGRA8 + WGPU: *no default* (must be queried from WGPU swapchain) + .context.depth_format SG_PIXELFORMAT_DEPTH_STENCIL + .context.sample_count 1 + + GL specific: + .context.gl.force_gles2 + if this is true the GL backend will act in "GLES2 fallback mode" even + when compiled with SOKOL_GLES3, this is useful to fall back + to traditional WebGL if a browser doesn't support a WebGL2 context + + Metal specific: + (NOTE: All Objective-C object references are transferred through + a bridged (const void*) to sokol_gfx, which will use a unretained + bridged cast (__bridged id) to retrieve the Objective-C + references back. Since the bridge cast is unretained, the caller + must hold a strong reference to the Objective-C object for the + duration of the sokol_gfx call! + + .context.metal.device + a pointer to the MTLDevice object + .context.metal.renderpass_descriptor_cb + .context.metal_renderpass_descriptor_userdata_cb + A C callback function to obtain the MTLRenderPassDescriptor for the + current frame when rendering to the default framebuffer, will be called + in sg_begin_default_pass(). + .context.metal.drawable_cb + .context.metal.drawable_userdata_cb + a C callback function to obtain a MTLDrawable for the current + frame when rendering to the default framebuffer, will be called in + sg_end_pass() of the default pass + .context.metal.user_data + optional user data pointer passed to the userdata versions of + callback functions + + D3D11 specific: + .context.d3d11.device + a pointer to the ID3D11Device object, this must have been created + before sg_setup() is called + .context.d3d11.device_context + a pointer to the ID3D11DeviceContext object + .context.d3d11.render_target_view_cb + .context.d3d11.render_target_view_userdata_cb + a C callback function to obtain a pointer to the current + ID3D11RenderTargetView object of the default framebuffer, + this function will be called in sg_begin_pass() when rendering + to the default framebuffer + .context.d3d11.depth_stencil_view_cb + .context.d3d11.depth_stencil_view_userdata_cb + a C callback function to obtain a pointer to the current + ID3D11DepthStencilView object of the default framebuffer, + this function will be called in sg_begin_pass() when rendering + to the default framebuffer + .context.metal.user_data + optional user data pointer passed to the userdata versions of + callback functions + + WebGPU specific: + .context.wgpu.device + a WGPUDevice handle + .context.wgpu.render_format + WGPUTextureFormat of the swap chain surface + .context.wgpu.render_view_cb + .context.wgpu.render_view_userdata_cb + callback to get the current WGPUTextureView of the swapchain's + rendering attachment (may be an MSAA surface) + .context.wgpu.resolve_view_cb + .context.wgpu.resolve_view_userdata_cb + callback to get the current WGPUTextureView of the swapchain's + MSAA-resolve-target surface, must return 0 if not MSAA rendering + .context.wgpu.depth_stencil_view_cb + .context.wgpu.depth_stencil_view_userdata_cb + callback to get current default-pass depth-stencil-surface WGPUTextureView + the pixel format of the default WGPUTextureView must be WGPUTextureFormat_Depth24Plus8 + .context.metal.user_data + optional user data pointer passed to the userdata versions of + callback functions + + When using sokol_gfx.h and sokol_app.h together, consider using the + helper function sapp_sgcontext() in the sokol_glue.h header to + initialize the sg_desc.context nested struct. sapp_sgcontext() returns + a completely initialized sg_context_desc struct with information + provided by sokol_app.h. + */ + typedef struct sg_gl_context_desc { + bool force_gles2; + } sg_gl_context_desc; + + typedef struct sg_metal_context_desc { + const void* device; + const void* (*renderpass_descriptor_cb)(void); + const void* (*renderpass_descriptor_userdata_cb)(void*); + const void* (*drawable_cb)(void); + const void* (*drawable_userdata_cb)(void*); + void* user_data; + } sg_metal_context_desc; + + typedef struct sg_d3d11_context_desc { + const void* device; + const void* device_context; + const void* (*render_target_view_cb)(void); + const void* (*render_target_view_userdata_cb)(void*); + const void* (*depth_stencil_view_cb)(void); + const void* (*depth_stencil_view_userdata_cb)(void*); + void* user_data; + } sg_d3d11_context_desc; + + typedef struct sg_wgpu_context_desc { + const void* device; /* WGPUDevice */ + const void* (*render_view_cb)(void); /* returns WGPUTextureView */ + const void* (*render_view_userdata_cb)(void*); + const void* (*resolve_view_cb)(void); /* returns WGPUTextureView */ + const void* (*resolve_view_userdata_cb)(void*); + const void* (*depth_stencil_view_cb)(void); /* returns WGPUTextureView, must be WGPUTextureFormat_Depth24Plus8 */ + const void* (*depth_stencil_view_userdata_cb)(void*); + void* user_data; + } sg_wgpu_context_desc; + + typedef struct sg_context_desc { + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_gl_context_desc gl; + sg_metal_context_desc metal; + sg_d3d11_context_desc d3d11; + sg_wgpu_context_desc wgpu; + } sg_context_desc; + + typedef struct sg_desc { + uint32_t _start_canary; + int buffer_pool_size; + int image_pool_size; + int shader_pool_size; + int pipeline_pool_size; + int pass_pool_size; + int context_pool_size; + int uniform_buffer_size; + int staging_buffer_size; + int sampler_cache_size; + sg_context_desc context; + uint32_t _end_canary; + } sg_desc; + + /* setup and misc functions */ + SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc); + SOKOL_GFX_API_DECL void sg_shutdown(void); + SOKOL_GFX_API_DECL bool sg_isvalid(void); + SOKOL_GFX_API_DECL void sg_reset_state_cache(void); + SOKOL_GFX_API_DECL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks); + SOKOL_GFX_API_DECL void sg_push_debug_group(const char* name); + SOKOL_GFX_API_DECL void sg_pop_debug_group(void); + + /* resource creation, destruction and updating */ + SOKOL_GFX_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc); + SOKOL_GFX_API_DECL sg_image sg_make_image(const sg_image_desc* desc); + SOKOL_GFX_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc); + SOKOL_GFX_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc); + SOKOL_GFX_API_DECL sg_pass sg_make_pass(const sg_pass_desc* desc); + SOKOL_GFX_API_DECL void sg_destroy_buffer(sg_buffer buf); + SOKOL_GFX_API_DECL void sg_destroy_image(sg_image img); + SOKOL_GFX_API_DECL void sg_destroy_shader(sg_shader shd); + SOKOL_GFX_API_DECL void sg_destroy_pipeline(sg_pipeline pip); + SOKOL_GFX_API_DECL void sg_destroy_pass(sg_pass pass); + SOKOL_GFX_API_DECL void sg_update_buffer(sg_buffer buf, const sg_range* data); + SOKOL_GFX_API_DECL void sg_update_image(sg_image img, const sg_image_data* data); + SOKOL_GFX_API_DECL int sg_append_buffer(sg_buffer buf, const sg_range* data); + SOKOL_GFX_API_DECL bool sg_query_buffer_overflow(sg_buffer buf); + + /* rendering functions */ + SOKOL_GFX_API_DECL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height); + SOKOL_GFX_API_DECL void sg_begin_default_passf(const sg_pass_action* pass_action, float width, float height); + SOKOL_GFX_API_DECL void sg_begin_pass(sg_pass pass, const sg_pass_action* pass_action); + SOKOL_GFX_API_DECL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left); + SOKOL_GFX_API_DECL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left); + SOKOL_GFX_API_DECL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left); + SOKOL_GFX_API_DECL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left); + SOKOL_GFX_API_DECL void sg_apply_pipeline(sg_pipeline pip); + SOKOL_GFX_API_DECL void sg_apply_bindings(const sg_bindings* bindings); + SOKOL_GFX_API_DECL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data); + SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_instances); + SOKOL_GFX_API_DECL void sg_end_pass(void); + SOKOL_GFX_API_DECL void sg_commit(void); + + /* getting information */ + SOKOL_GFX_API_DECL sg_desc sg_query_desc(void); + SOKOL_GFX_API_DECL sg_backend sg_query_backend(void); + SOKOL_GFX_API_DECL sg_features sg_query_features(void); + SOKOL_GFX_API_DECL sg_limits sg_query_limits(void); + SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt); + /* get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) */ + SOKOL_GFX_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf); + SOKOL_GFX_API_DECL sg_resource_state sg_query_image_state(sg_image img); + SOKOL_GFX_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd); + SOKOL_GFX_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip); + SOKOL_GFX_API_DECL sg_resource_state sg_query_pass_state(sg_pass pass); + /* get runtime information about a resource */ + SOKOL_GFX_API_DECL sg_buffer_info sg_query_buffer_info(sg_buffer buf); + SOKOL_GFX_API_DECL sg_image_info sg_query_image_info(sg_image img); + SOKOL_GFX_API_DECL sg_shader_info sg_query_shader_info(sg_shader shd); + SOKOL_GFX_API_DECL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip); + SOKOL_GFX_API_DECL sg_pass_info sg_query_pass_info(sg_pass pass); + /* get resource creation desc struct with their default values replaced */ + SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc); + SOKOL_GFX_API_DECL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc); + SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc); + SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc); + SOKOL_GFX_API_DECL sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc); + + /* separate resource allocation and initialization (for async setup) */ + SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void); + SOKOL_GFX_API_DECL sg_image sg_alloc_image(void); + SOKOL_GFX_API_DECL sg_shader sg_alloc_shader(void); + SOKOL_GFX_API_DECL sg_pipeline sg_alloc_pipeline(void); + SOKOL_GFX_API_DECL sg_pass sg_alloc_pass(void); + SOKOL_GFX_API_DECL void sg_dealloc_buffer(sg_buffer buf_id); + SOKOL_GFX_API_DECL void sg_dealloc_image(sg_image img_id); + SOKOL_GFX_API_DECL void sg_dealloc_shader(sg_shader shd_id); + SOKOL_GFX_API_DECL void sg_dealloc_pipeline(sg_pipeline pip_id); + SOKOL_GFX_API_DECL void sg_dealloc_pass(sg_pass pass_id); + SOKOL_GFX_API_DECL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc); + SOKOL_GFX_API_DECL void sg_init_image(sg_image img_id, const sg_image_desc* desc); + SOKOL_GFX_API_DECL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc); + SOKOL_GFX_API_DECL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc); + SOKOL_GFX_API_DECL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc); + SOKOL_GFX_API_DECL bool sg_uninit_buffer(sg_buffer buf_id); + SOKOL_GFX_API_DECL bool sg_uninit_image(sg_image img_id); + SOKOL_GFX_API_DECL bool sg_uninit_shader(sg_shader shd_id); + SOKOL_GFX_API_DECL bool sg_uninit_pipeline(sg_pipeline pip_id); + SOKOL_GFX_API_DECL bool sg_uninit_pass(sg_pass pass_id); + SOKOL_GFX_API_DECL void sg_fail_buffer(sg_buffer buf_id); + SOKOL_GFX_API_DECL void sg_fail_image(sg_image img_id); + SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd_id); + SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip_id); + SOKOL_GFX_API_DECL void sg_fail_pass(sg_pass pass_id); + + /* rendering contexts (optional) */ + SOKOL_GFX_API_DECL sg_context sg_setup_context(void); + SOKOL_GFX_API_DECL void sg_activate_context(sg_context ctx_id); + SOKOL_GFX_API_DECL void sg_discard_context(sg_context ctx_id); + + /* Backend-specific helper functions, these may come in handy for mixing + sokol-gfx rendering with 'native backend' rendering functions. + + This group of functions will be expanded as needed. + */ + + /* D3D11: return ID3D11Device */ + SOKOL_GFX_API_DECL const void* sg_d3d11_device(void); + + /* Metal: return __bridge-casted MTLDevice */ + SOKOL_GFX_API_DECL const void* sg_mtl_device(void); + + /* Metal: return __bridge-casted MTLRenderCommandEncoder in current pass (or zero if outside pass) */ + SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(void); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for c++ */ +inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); } + +inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); } +inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); } +inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); } +inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); } +inline sg_pass sg_make_pass(const sg_pass_desc& desc) { return sg_make_pass(&desc); } +inline void sg_update_image(sg_image img, const sg_image_data& data) { return sg_update_image(img, &data); } + +inline void sg_begin_default_pass(const sg_pass_action& pass_action, int width, int height) { return sg_begin_default_pass(&pass_action, width, height); } +inline void sg_begin_default_passf(const sg_pass_action& pass_action, float width, float height) { return sg_begin_default_passf(&pass_action, width, height); } +inline void sg_begin_pass(sg_pass pass, const sg_pass_action& pass_action) { return sg_begin_pass(pass, &pass_action); } +inline void sg_apply_bindings(const sg_bindings& bindings) { return sg_apply_bindings(&bindings); } +inline void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range& data) { return sg_apply_uniforms(stage, ub_index, &data); } + +inline sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc& desc) { return sg_query_buffer_defaults(&desc); } +inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return sg_query_image_defaults(&desc); } +inline sg_shader_desc sg_query_shader_defaults(const sg_shader_desc& desc) { return sg_query_shader_defaults(&desc); } +inline sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc& desc) { return sg_query_pipeline_defaults(&desc); } +inline sg_pass_desc sg_query_pass_defaults(const sg_pass_desc& desc) { return sg_query_pass_defaults(&desc); } + +inline void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc& desc) { return sg_init_buffer(buf_id, &desc); } +inline void sg_init_image(sg_image img_id, const sg_image_desc& desc) { return sg_init_image(img_id, &desc); } +inline void sg_init_shader(sg_shader shd_id, const sg_shader_desc& desc) { return sg_init_shader(shd_id, &desc); } +inline void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip_id, &desc); } +inline void sg_init_pass(sg_pass pass_id, const sg_pass_desc& desc) { return sg_init_pass(pass_id, &desc); } + +inline void sg_update_buffer(sg_buffer buf_id, const sg_range& data) { return sg_update_buffer(buf_id, &data); } +inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_append_buffer(buf_id, &data); } +#endif +#endif // SOKOL_GFX_INCLUDED + +/*--- IMPLEMENTATION ---------------------------------------------------------*/ +#ifdef SOKOL_GFX_IMPL +#define SOKOL_GFX_IMPL_INCLUDED (1) + +#if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES2)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) +#error "Please select a backend with SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" +#endif +#include /* memset */ +#include /* FLT_MAX */ + +#ifndef SOKOL_API_IMPL +#define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG +#ifndef NDEBUG +#define SOKOL_DEBUG (1) +#endif +#endif +#ifndef SOKOL_ASSERT +#include +#define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_VALIDATE_BEGIN +#define SOKOL_VALIDATE_BEGIN() _sg_validate_begin() +#endif +#ifndef SOKOL_VALIDATE +#define SOKOL_VALIDATE(cond, err) _sg_validate((cond), err) +#endif +#ifndef SOKOL_VALIDATE_END +#define SOKOL_VALIDATE_END() _sg_validate_end() +#endif +#ifndef SOKOL_UNREACHABLE +#define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef SOKOL_MALLOC +#include +#define SOKOL_MALLOC(s) malloc(s) +#define SOKOL_FREE(p) free(p) +#endif +#ifndef SOKOL_LOG +#ifdef SOKOL_DEBUG +#include +#define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } +#else +#define SOKOL_LOG(s) +#endif +#endif + +#ifndef _SOKOL_PRIVATE +#if defined(__GNUC__) || defined(__clang__) +#define _SOKOL_PRIVATE __attribute__((unused)) static +#else +#define _SOKOL_PRIVATE static +#endif +#endif + +#ifndef _SOKOL_UNUSED +#define _SOKOL_UNUSED(x) (void)(x) +#endif + +#if defined(SOKOL_TRACE_HOOKS) +#define _SG_TRACE_ARGS(fn, ...) if (_sg.hooks.fn) { _sg.hooks.fn(__VA_ARGS__, _sg.hooks.user_data); } +#define _SG_TRACE_NOARGS(fn) if (_sg.hooks.fn) { _sg.hooks.fn(_sg.hooks.user_data); } +#else +#define _SG_TRACE_ARGS(fn, ...) +#define _SG_TRACE_NOARGS(fn) +#endif + +/* default clear values */ +#ifndef SG_DEFAULT_CLEAR_RED +#define SG_DEFAULT_CLEAR_RED (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_GREEN +#define SG_DEFAULT_CLEAR_GREEN (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_BLUE +#define SG_DEFAULT_CLEAR_BLUE (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_ALPHA +#define SG_DEFAULT_CLEAR_ALPHA (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_DEPTH +#define SG_DEFAULT_CLEAR_DEPTH (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_STENCIL +#define SG_DEFAULT_CLEAR_STENCIL (0) +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4115) /* named type definition in parentheses */ +#pragma warning(disable:4505) /* unreferenced local function has been removed */ +#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union (needed by d3d11.h) */ +#pragma warning(disable:4054) /* 'type cast': from function pointer */ +#pragma warning(disable:4055) /* 'type cast': from data pointer */ +#endif + +#if defined(SOKOL_D3D11) +#ifndef D3D11_NO_HELPERS +#define D3D11_NO_HELPERS +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#ifdef _MSC_VER +#if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) +#pragma comment (lib, "WindowsApp") +#else +#pragma comment (lib, "kernel32") +#pragma comment (lib, "user32") +#pragma comment (lib, "dxgi") +#pragma comment (lib, "d3d11") +#endif +#endif +#elif defined(SOKOL_METAL) +// see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting +#if !defined(__cplusplus) +#if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) +#error "sokol_gfx.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" +#endif +#endif +#include +#if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE +#define _SG_TARGET_MACOS (1) +#else +#define _SG_TARGET_IOS (1) +#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR +#define _SG_TARGET_IOS_SIMULATOR (1) +#endif +#endif +#import +#elif defined(SOKOL_WGPU) +#if defined(__EMSCRIPTEN__) +#include +#else +#include +#endif +#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +#define _SOKOL_ANY_GL (1) + +// include platform specific GL headers (or on Win32: use an embedded GL loader) +#if !defined(SOKOL_EXTERNAL_GL_LOADER) +#if defined(_WIN32) +#if defined(SOKOL_GLCORE33) && !defined(SOKOL_EXTERNAL_GL_LOADER) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#define _SOKOL_USE_WIN32_GL_LOADER (1) +#pragma comment (lib, "kernel32") // GetProcAddress() +#endif +#elif defined(__APPLE__) +#include +#ifndef GL_SILENCE_DEPRECATION +#define GL_SILENCE_DEPRECATION +#endif +#if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE +#include +#else +#include +#include +#endif +#elif defined(__EMSCRIPTEN__) || defined(__ANDROID__) +#if defined(SOKOL_GLES3) +#include +#elif defined(SOKOL_GLES2) +#ifndef GL_EXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES +#endif +#include +#include +#endif +#elif defined(__linux__) || defined(__unix__) +#define GL_GLEXT_PROTOTYPES +#include +#endif +#endif + +// optional GL loader definitions (only on Win32) +#if defined(_SOKOL_USE_WIN32_GL_LOADER) +#define __gl_h_ 1 +#define __gl32_h_ 1 +#define __gl31_h_ 1 +#define __GL_H__ 1 +#define __glext_h_ 1 +#define __GLEXT_H_ 1 +#define __gltypes_h_ 1 +#define __glcorearb_h_ 1 +#define __gl_glcorearb_h_ 1 +#define GL_APIENTRY APIENTRY + +typedef unsigned int GLenum; +typedef unsigned int GLuint; +typedef int GLsizei; +typedef char GLchar; +typedef ptrdiff_t GLintptr; +typedef ptrdiff_t GLsizeiptr; +typedef double GLclampd; +typedef unsigned short GLushort; +typedef unsigned char GLubyte; +typedef unsigned char GLboolean; +typedef uint64_t GLuint64; +typedef double GLdouble; +typedef unsigned short GLhalf; +typedef float GLclampf; +typedef unsigned int GLbitfield; +typedef signed char GLbyte; +typedef short GLshort; +typedef void GLvoid; +typedef int64_t GLint64; +typedef float GLfloat; +typedef struct __GLsync * GLsync; +typedef int GLint; +#define GL_INT_2_10_10_10_REV 0x8D9F +#define GL_R32F 0x822E +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_R16F 0x822D +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_NUM_EXTENSIONS 0x821D +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_INCR 0x1E02 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_STATIC_DRAW 0x88E4 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_CONSTANT_COLOR 0x8001 +#define GL_DECR_WRAP 0x8508 +#define GL_R8 0x8229 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_SHORT 0x1402 +#define GL_DEPTH_TEST 0x0B71 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_LINK_STATUS 0x8B82 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_RGBA16F 0x881A +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_STREAM_DRAW 0x88E0 +#define GL_ONE 1 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA8 0x8058 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_RGBA4 0x8056 +#define GL_RGB8 0x8051 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_STENCIL 0x1802 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_DEPTH 0x1801 +#define GL_FRONT 0x0404 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_REPEAT 0x2901 +#define GL_RGBA 0x1908 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_DECR 0x1E03 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_FLOAT 0x1406 +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_COLOR 0x1800 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_TRIANGLES 0x0004 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_NONE 0 +#define GL_SRC_COLOR 0x0300 +#define GL_BYTE 0x1400 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_LINE_STRIP 0x0003 +#define GL_TEXTURE_3D 0x806F +#define GL_CW 0x0900 +#define GL_LINEAR 0x2601 +#define GL_RENDERBUFFER 0x8D41 +#define GL_GEQUAL 0x0206 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_RGBA32F 0x8814 +#define GL_BLEND 0x0BE2 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_EXTENSIONS 0x1F03 +#define GL_NO_ERROR 0 +#define GL_REPLACE 0x1E01 +#define GL_KEEP 0x1E00 +#define GL_CCW 0x0901 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_RGB 0x1907 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_FALSE 0 +#define GL_ZERO 0 +#define GL_CULL_FACE 0x0B44 +#define GL_INVERT 0x150A +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_NEAREST 0x2600 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_LEQUAL 0x0203 +#define GL_STENCIL_TEST 0x0B90 +#define GL_DITHER 0x0BD0 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_EQUAL 0x0202 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RGB5 0x8050 +#define GL_LINES 0x0001 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_SRC_ALPHA 0x0302 +#define GL_INCR_WRAP 0x8507 +#define GL_LESS 0x0201 +#define GL_MULTISAMPLE 0x809D +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_BACK 0x0405 +#define GL_ALWAYS 0x0207 +#define GL_FUNC_ADD 0x8006 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_NOTEQUAL 0x0205 +#define GL_DST_COLOR 0x0306 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_RED 0x1903 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_DST_ALPHA 0x0304 +#define GL_RGB5_A1 0x8057 +#define GL_GREATER 0x0204 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_TRUE 1 +#define GL_NEVER 0x0200 +#define GL_POINTS 0x0000 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_RGBA_INTEGER 0x8D99 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_RGBA16 0x805B +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#endif + +#ifndef GL_UNSIGNED_INT_2_10_10_10_REV +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#endif +#ifndef GL_UNSIGNED_INT_24_8 +#define GL_UNSIGNED_INT_24_8 0x84FA +#endif +#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#endif +#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif +#ifndef GL_COMPRESSED_RED_RGTC1 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#endif +#ifndef GL_COMPRESSED_SIGNED_RED_RGTC1 +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#endif +#ifndef GL_COMPRESSED_RED_GREEN_RGTC2 +#define GL_COMPRESSED_RED_GREEN_RGTC2 0x8DBD +#endif +#ifndef GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 0x8DBE +#endif +#ifndef GL_COMPRESSED_RGBA_BPTC_UNORM_ARB +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#endif +#ifndef GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#endif +#ifndef GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#endif +#ifndef GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif +#ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG +#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#endif +#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#endif +#ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 +#endif +#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#endif +#ifndef GL_COMPRESSED_RGB8_ETC2 +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#endif +#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#endif +#ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#endif +#ifndef GL_COMPRESSED_RG11_EAC +#define GL_COMPRESSED_RG11_EAC 0x9272 +#endif +#ifndef GL_COMPRESSED_SIGNED_RG11_EAC +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#endif +#ifndef GL_DEPTH24_STENCIL8 +#define GL_DEPTH24_STENCIL8 0x88F0 +#endif +#ifndef GL_HALF_FLOAT +#define GL_HALF_FLOAT 0x140B +#endif +#ifndef GL_DEPTH_STENCIL +#define GL_DEPTH_STENCIL 0x84F9 +#endif +#ifndef GL_LUMINANCE +#define GL_LUMINANCE 0x1909 +#endif + +#ifdef SOKOL_GLES2 +#ifdef GL_ANGLE_instanced_arrays +#define _SOKOL_GL_INSTANCING_ENABLED +#define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedANGLE(mode, first, count, instancecount) +#define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedANGLE(mode, count, type, indices, instancecount) +#define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorANGLE(index, divisor) +#elif defined(GL_EXT_draw_instanced) && defined(GL_EXT_instanced_arrays) +#define _SOKOL_GL_INSTANCING_ENABLED +#define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedEXT(mode, first, count, instancecount) +#define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedEXT(mode, count, type, indices, instancecount) +#define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorEXT(index, divisor) +#else +#define _SOKOL_GLES2_INSTANCING_ERROR "Select GL_ANGLE_instanced_arrays or (GL_EXT_draw_instanced & GL_EXT_instanced_arrays) to enable instancing in GLES2" +#define glDrawArraysInstanced(mode, first, count, instancecount) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) +#define glDrawElementsInstanced(mode, count, type, indices, instancecount) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) +#define glVertexAttribDivisor(index, divisor) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) +#endif +#else +#define _SOKOL_GL_INSTANCING_ENABLED +#endif +#define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } +#endif + +/*=== COMMON BACKEND STUFF ===================================================*/ + +/* resource pool slots */ +typedef struct { + uint32_t id; + uint32_t ctx_id; + sg_resource_state state; +} _sg_slot_t; + +/* constants */ +enum { + _SG_STRING_SIZE = 16, + _SG_SLOT_SHIFT = 16, + _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1, + _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), + _SG_DEFAULT_BUFFER_POOL_SIZE = 128, + _SG_DEFAULT_IMAGE_POOL_SIZE = 128, + _SG_DEFAULT_SHADER_POOL_SIZE = 32, + _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, + _SG_DEFAULT_PASS_POOL_SIZE = 16, + _SG_DEFAULT_CONTEXT_POOL_SIZE = 16, + _SG_DEFAULT_SAMPLER_CACHE_CAPACITY = 64, + _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, + _SG_DEFAULT_STAGING_SIZE = 8 * 1024 * 1024, +}; + +/* fixed-size string */ +typedef struct { + char buf[_SG_STRING_SIZE]; +} _sg_str_t; + +/* helper macros */ +#define _sg_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) +#define _sg_min(a,b) (((a)<(b))?(a):(b)) +#define _sg_max(a,b) (((a)>(b))?(a):(b)) +#define _sg_clamp(v,v0,v1) (((v)<(v0))?(v0):(((v)>(v1))?(v1):(v))) +#define _sg_fequal(val,cmp,delta) ((((val)-(cmp))> -(delta))&&(((val)-(cmp))<(delta))) + +typedef struct { + int size; + int append_pos; + bool append_overflow; + sg_buffer_type type; + sg_usage usage; + uint32_t update_frame_index; + uint32_t append_frame_index; + int num_slots; + int active_slot; +} _sg_buffer_common_t; + +_SOKOL_PRIVATE void _sg_buffer_common_init(_sg_buffer_common_t* cmn, const sg_buffer_desc* desc) { + cmn->size = (int)desc->size; + cmn->append_pos = 0; + cmn->append_overflow = false; + cmn->type = desc->type; + cmn->usage = desc->usage; + cmn->update_frame_index = 0; + cmn->append_frame_index = 0; + cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; +} + +typedef struct { + sg_image_type type; + bool render_target; + int width; + int height; + int num_slices; + int num_mipmaps; + sg_usage usage; + sg_pixel_format pixel_format; + int sample_count; + sg_filter min_filter; + sg_filter mag_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + sg_border_color border_color; + uint32_t max_anisotropy; + uint32_t upd_frame_index; + int num_slots; + int active_slot; +} _sg_image_common_t; + +_SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_image_desc* desc) { + cmn->type = desc->type; + cmn->render_target = desc->render_target; + cmn->width = desc->width; + cmn->height = desc->height; + cmn->num_slices = desc->num_slices; + cmn->num_mipmaps = desc->num_mipmaps; + cmn->usage = desc->usage; + cmn->pixel_format = desc->pixel_format; + cmn->sample_count = desc->sample_count; + cmn->min_filter = desc->min_filter; + cmn->mag_filter = desc->mag_filter; + cmn->wrap_u = desc->wrap_u; + cmn->wrap_v = desc->wrap_v; + cmn->wrap_w = desc->wrap_w; + cmn->border_color = desc->border_color; + cmn->max_anisotropy = desc->max_anisotropy; + cmn->upd_frame_index = 0; + cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; +} + +typedef struct { + size_t size; +} _sg_uniform_block_t; + +typedef struct { + sg_image_type image_type; + sg_sampler_type sampler_type; +} _sg_shader_image_t; + +typedef struct { + int num_uniform_blocks; + int num_images; + _sg_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; +} _sg_shader_stage_t; + +typedef struct { + _sg_shader_stage_t stage[SG_NUM_SHADER_STAGES]; +} _sg_shader_common_t; + +_SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_shader_desc* desc) { + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + _sg_shader_stage_t* stage = &cmn->stage[stage_index]; + SOKOL_ASSERT(stage->num_uniform_blocks == 0); + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + stage->uniform_blocks[ub_index].size = ub_desc->size; + stage->num_uniform_blocks++; + } + SOKOL_ASSERT(stage->num_images == 0); + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->image_type == _SG_IMAGETYPE_DEFAULT) { + break; + } + stage->images[img_index].image_type = img_desc->image_type; + stage->images[img_index].sampler_type = img_desc->sampler_type; + stage->num_images++; + } + } +} + +typedef struct { + sg_shader shader_id; + sg_index_type index_type; + bool use_instanced_draw; + bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; + int color_attachment_count; + sg_pixel_format color_formats[SG_MAX_COLOR_ATTACHMENTS]; + sg_pixel_format depth_format; + int sample_count; + float depth_bias; + float depth_bias_slope_scale; + float depth_bias_clamp; + sg_color blend_color; +} _sg_pipeline_common_t; + +_SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc) { + SOKOL_ASSERT((desc->color_count >= 1) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS)); + cmn->shader_id = desc->shader; + cmn->index_type = desc->index_type; + cmn->use_instanced_draw = false; + for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + cmn->vertex_layout_valid[i] = false; + } + cmn->color_attachment_count = desc->color_count; + for (int i = 0; i < cmn->color_attachment_count; i++) { + cmn->color_formats[i] = desc->colors[i].pixel_format; + } + cmn->depth_format = desc->depth.pixel_format; + cmn->sample_count = desc->sample_count; + cmn->depth_bias = desc->depth.bias; + cmn->depth_bias_slope_scale = desc->depth.bias_slope_scale; + cmn->depth_bias_clamp = desc->depth.bias_clamp; + cmn->blend_color = desc->blend_color; +} + +typedef struct { + sg_image image_id; + int mip_level; + int slice; +} _sg_pass_attachment_common_t; + +typedef struct { + int num_color_atts; + _sg_pass_attachment_common_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_pass_attachment_common_t ds_att; +} _sg_pass_common_t; + +_SOKOL_PRIVATE void _sg_pass_common_init(_sg_pass_common_t* cmn, const sg_pass_desc* desc) { + const sg_pass_attachment_desc* att_desc; + _sg_pass_attachment_common_t* att; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + cmn->num_color_atts++; + att = &cmn->color_atts[i]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } + } + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + att = &cmn->ds_att; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } +} + +/*=== GENERIC SAMPLER CACHE ==================================================*/ + +/* + this is used by the Metal and WGPU backends to reduce the + number of sampler state objects created through the backend API +*/ +typedef struct { + sg_filter min_filter; + sg_filter mag_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + sg_border_color border_color; + uint32_t max_anisotropy; + int min_lod; /* orig min/max_lod is float, this is int(min/max_lod*1000.0) */ + int max_lod; + uintptr_t sampler_handle; +} _sg_sampler_cache_item_t; + +typedef struct { + int capacity; + int num_items; + _sg_sampler_cache_item_t* items; +} _sg_sampler_cache_t; + +_SOKOL_PRIVATE void _sg_smpcache_init(_sg_sampler_cache_t* cache, int capacity) { + SOKOL_ASSERT(cache && (capacity > 0)); + memset(cache, 0, sizeof(_sg_sampler_cache_t)); + cache->capacity = capacity; + const size_t size = (size_t)cache->capacity * sizeof(_sg_sampler_cache_item_t); + cache->items = (_sg_sampler_cache_item_t*) SOKOL_MALLOC(size); + SOKOL_ASSERT(cache->items); + memset(cache->items, 0, size); +} + +_SOKOL_PRIVATE void _sg_smpcache_discard(_sg_sampler_cache_t* cache) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_FREE(cache->items); + cache->items = 0; + cache->num_items = 0; + cache->capacity = 0; +} + +_SOKOL_PRIVATE int _sg_smpcache_minlod_int(float min_lod) { + return (int) (min_lod * 1000.0f); +} + +_SOKOL_PRIVATE int _sg_smpcache_maxlod_int(float max_lod) { + return (int) (_sg_clamp(max_lod, 0.0f, 1000.0f) * 1000.0f); +} + +_SOKOL_PRIVATE int _sg_smpcache_find_item(const _sg_sampler_cache_t* cache, const sg_image_desc* img_desc) { + /* return matching sampler cache item index or -1 */ + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(img_desc); + const int min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); + const int max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); + for (int i = 0; i < cache->num_items; i++) { + const _sg_sampler_cache_item_t* item = &cache->items[i]; + if ((img_desc->min_filter == item->min_filter) && + (img_desc->mag_filter == item->mag_filter) && + (img_desc->wrap_u == item->wrap_u) && + (img_desc->wrap_v == item->wrap_v) && + (img_desc->wrap_w == item->wrap_w) && + (img_desc->max_anisotropy == item->max_anisotropy) && + (img_desc->border_color == item->border_color) && + (min_lod == item->min_lod) && + (max_lod == item->max_lod)) + { + return i; + } + } + /* fallthrough: no matching cache item found */ + return -1; +} + +_SOKOL_PRIVATE void _sg_smpcache_add_item(_sg_sampler_cache_t* cache, const sg_image_desc* img_desc, uintptr_t sampler_handle) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(img_desc); + SOKOL_ASSERT(cache->num_items < cache->capacity); + const int item_index = cache->num_items++; + _sg_sampler_cache_item_t* item = &cache->items[item_index]; + item->min_filter = img_desc->min_filter; + item->mag_filter = img_desc->mag_filter; + item->wrap_u = img_desc->wrap_u; + item->wrap_v = img_desc->wrap_v; + item->wrap_w = img_desc->wrap_w; + item->border_color = img_desc->border_color; + item->max_anisotropy = img_desc->max_anisotropy; + item->min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); + item->max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); + item->sampler_handle = sampler_handle; +} + +_SOKOL_PRIVATE uintptr_t _sg_smpcache_sampler(_sg_sampler_cache_t* cache, int item_index) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(item_index < cache->num_items); + return cache->items[item_index].sampler_handle; +} + +/*=== DUMMY BACKEND DECLARATIONS =============================================*/ +#if defined(SOKOL_DUMMY_BACKEND) +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; +} _sg_dummy_buffer_t; +typedef _sg_dummy_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; +} _sg_dummy_image_t; +typedef _sg_dummy_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; +} _sg_dummy_shader_t; +typedef _sg_dummy_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_t* shader; + _sg_pipeline_common_t cmn; +} _sg_dummy_pipeline_t; +typedef _sg_dummy_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_dummy_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + _sg_dummy_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_dummy_attachment_t ds_att; + } dmy; +} _sg_dummy_pass_t; +typedef _sg_dummy_pass_t _sg_pass_t; +typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; + +typedef struct { + _sg_slot_t slot; +} _sg_dummy_context_t; +typedef _sg_dummy_context_t _sg_context_t; + +/*== GL BACKEND DECLARATIONS =================================================*/ +#elif defined(_SOKOL_ANY_GL) +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + GLuint buf[SG_NUM_INFLIGHT_FRAMES]; + bool ext_buffers; /* if true, external buffers were injected with sg_buffer_desc.gl_buffers */ + } gl; +} _sg_gl_buffer_t; +typedef _sg_gl_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + GLenum target; + GLuint depth_render_buffer; + GLuint msaa_render_buffer; + GLuint tex[SG_NUM_INFLIGHT_FRAMES]; + bool ext_textures; /* if true, external textures were injected with sg_image_desc.gl_textures */ + } gl; +} _sg_gl_image_t; +typedef _sg_gl_image_t _sg_image_t; + +typedef struct { + GLint gl_loc; + sg_uniform_type type; + uint16_t count; + uint16_t offset; +} _sg_gl_uniform_t; + +typedef struct { + int num_uniforms; + _sg_gl_uniform_t uniforms[SG_MAX_UB_MEMBERS]; +} _sg_gl_uniform_block_t; + +typedef struct { + int gl_tex_slot; +} _sg_gl_shader_image_t; + +typedef struct { + _sg_str_t name; +} _sg_gl_shader_attr_t; + +typedef struct { + _sg_gl_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_gl_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; +} _sg_gl_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + GLuint prog; + _sg_gl_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_gl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } gl; +} _sg_gl_shader_t; +typedef _sg_gl_shader_t _sg_shader_t; + +typedef struct { + int8_t vb_index; /* -1 if attr is not enabled */ + int8_t divisor; /* -1 if not initialized */ + uint8_t stride; + uint8_t size; + uint8_t normalized; + int offset; + GLenum type; +} _sg_gl_attr_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + _sg_gl_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_depth_state depth; + sg_stencil_state stencil; + sg_primitive_type primitive_type; + sg_blend_state blend; + sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + bool alpha_to_coverage_enabled; + } gl; +} _sg_gl_pipeline_t; +typedef _sg_gl_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + GLuint gl_msaa_resolve_buffer; +} _sg_gl_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + GLuint fb; + _sg_gl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_gl_attachment_t ds_att; + } gl; +} _sg_gl_pass_t; +typedef _sg_gl_pass_t _sg_pass_t; +typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; + +typedef struct { + _sg_slot_t slot; +#if !defined(SOKOL_GLES2) + GLuint vao; +#endif + GLuint default_framebuffer; +} _sg_gl_context_t; +typedef _sg_gl_context_t _sg_context_t; + +typedef struct { + _sg_gl_attr_t gl_attr; + GLuint gl_vbuf; +} _sg_gl_cache_attr_t; + +typedef struct { + GLenum target; + GLuint texture; +} _sg_gl_texture_bind_slot; + +typedef struct { + sg_depth_state depth; + sg_stencil_state stencil; + sg_blend_state blend; + sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + bool polygon_offset_enabled; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; + _sg_gl_cache_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + GLuint vertex_buffer; + GLuint index_buffer; + GLuint stored_vertex_buffer; + GLuint stored_index_buffer; + GLuint prog; + _sg_gl_texture_bind_slot textures[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_gl_texture_bind_slot stored_texture; + int cur_ib_offset; + GLenum cur_primitive_type; + GLenum cur_index_type; + GLenum cur_active_texture; + _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; +} _sg_gl_state_cache_t; + +typedef struct { + bool valid; + bool gles2; + bool in_pass; + int cur_pass_width; + int cur_pass_height; + _sg_context_t* cur_context; + _sg_pass_t* cur_pass; + sg_pass cur_pass_id; + _sg_gl_state_cache_t cache; + bool ext_anisotropic; + GLint max_anisotropy; + GLint max_combined_texture_image_units; +#if _SOKOL_USE_WIN32_GL_LOADER + HINSTANCE opengl32_dll; +#endif +} _sg_gl_backend_t; + +/*== D3D11 BACKEND DECLARATIONS ==============================================*/ +#elif defined(SOKOL_D3D11) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + ID3D11Buffer* buf; + } d3d11; +} _sg_d3d11_buffer_t; +typedef _sg_d3d11_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + DXGI_FORMAT format; + ID3D11Texture2D* tex2d; + ID3D11Texture3D* tex3d; + ID3D11Texture2D* texds; + ID3D11Texture2D* texmsaa; + ID3D11ShaderResourceView* srv; + ID3D11SamplerState* smp; + } d3d11; +} _sg_d3d11_image_t; +typedef _sg_d3d11_image_t _sg_image_t; + +typedef struct { + _sg_str_t sem_name; + int sem_index; +} _sg_d3d11_shader_attr_t; + +typedef struct { + ID3D11Buffer* cbufs[SG_MAX_SHADERSTAGE_UBS]; +} _sg_d3d11_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_d3d11_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_d3d11_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + ID3D11VertexShader* vs; + ID3D11PixelShader* fs; + void* vs_blob; + size_t vs_blob_length; + } d3d11; +} _sg_d3d11_shader_t; +typedef _sg_d3d11_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + UINT stencil_ref; + UINT vb_strides[SG_MAX_SHADERSTAGE_BUFFERS]; + D3D_PRIMITIVE_TOPOLOGY topology; + DXGI_FORMAT index_format; + ID3D11InputLayout* il; + ID3D11RasterizerState* rs; + ID3D11DepthStencilState* dss; + ID3D11BlendState* bs; + } d3d11; +} _sg_d3d11_pipeline_t; +typedef _sg_d3d11_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + ID3D11RenderTargetView* rtv; +} _sg_d3d11_color_attachment_t; + +typedef struct { + _sg_image_t* image; + ID3D11DepthStencilView* dsv; +} _sg_d3d11_ds_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + _sg_d3d11_color_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_ds_attachment_t ds_att; + } d3d11; +} _sg_d3d11_pass_t; +typedef _sg_d3d11_pass_t _sg_pass_t; +typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; + +typedef struct { + _sg_slot_t slot; +} _sg_d3d11_context_t; +typedef _sg_d3d11_context_t _sg_context_t; + +typedef struct { + bool valid; + ID3D11Device* dev; + ID3D11DeviceContext* ctx; + const void* (*rtv_cb)(void); + const void* (*rtv_userdata_cb)(void*); + const void* (*dsv_cb)(void); + const void* (*dsv_userdata_cb)(void*); + void* user_data; + bool in_pass; + bool use_indexed_draw; + bool use_instanced_draw; + int cur_width; + int cur_height; + int num_rtvs; + _sg_pass_t* cur_pass; + sg_pass cur_pass_id; + _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + ID3D11RenderTargetView* cur_rtvs[SG_MAX_COLOR_ATTACHMENTS]; + ID3D11DepthStencilView* cur_dsv; + /* on-demand loaded d3dcompiler_47.dll handles */ + HINSTANCE d3dcompiler_dll; + bool d3dcompiler_dll_load_failed; + pD3DCompile D3DCompile_func; + /* global subresourcedata array for texture updates */ + D3D11_SUBRESOURCE_DATA subres_data[SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS]; +} _sg_d3d11_backend_t; + +/*=== METAL BACKEND DECLARATIONS =============================================*/ +#elif defined(SOKOL_METAL) + +#if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR) +#define _SG_MTL_UB_ALIGN (256) +#else +#define _SG_MTL_UB_ALIGN (16) +#endif +#define _SG_MTL_INVALID_SLOT_INDEX (0) + +typedef struct { + uint32_t frame_index; /* frame index at which it is safe to release this resource */ + int slot_index; +} _sg_mtl_release_item_t; + +typedef struct { + NSMutableArray* pool; + int num_slots; + int free_queue_top; + int* free_queue; + int release_queue_front; + int release_queue_back; + _sg_mtl_release_item_t* release_queue; +} _sg_mtl_idpool_t; + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + int buf[SG_NUM_INFLIGHT_FRAMES]; /* index into _sg_mtl_pool */ + } mtl; +} _sg_mtl_buffer_t; +typedef _sg_mtl_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + int tex[SG_NUM_INFLIGHT_FRAMES]; + int depth_tex; + int msaa_tex; + int sampler_state; + } mtl; +} _sg_mtl_image_t; +typedef _sg_mtl_image_t _sg_image_t; + +typedef struct { + int mtl_lib; + int mtl_func; +} _sg_mtl_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_mtl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } mtl; +} _sg_mtl_shader_t; +typedef _sg_mtl_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + MTLPrimitiveType prim_type; + int index_size; + MTLIndexType index_type; + MTLCullMode cull_mode; + MTLWinding winding; + uint32_t stencil_ref; + int rps; + int dss; + } mtl; +} _sg_mtl_pipeline_t; +typedef _sg_mtl_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_mtl_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + _sg_mtl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_mtl_attachment_t ds_att; + } mtl; +} _sg_mtl_pass_t; +typedef _sg_mtl_pass_t _sg_pass_t; +typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; + +typedef struct { + _sg_slot_t slot; +} _sg_mtl_context_t; +typedef _sg_mtl_context_t _sg_context_t; + +/* resouce binding state cache */ +typedef struct { + const _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + const _sg_buffer_t* cur_indexbuffer; + int cur_indexbuffer_offset; + sg_buffer cur_indexbuffer_id; + const _sg_buffer_t* cur_vertexbuffers[SG_MAX_SHADERSTAGE_BUFFERS]; + int cur_vertexbuffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_buffer cur_vertexbuffer_ids[SG_MAX_SHADERSTAGE_BUFFERS]; + const _sg_image_t* cur_vs_images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_image cur_vs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; + const _sg_image_t* cur_fs_images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_image cur_fs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; +} _sg_mtl_state_cache_t; + +typedef struct { + bool valid; + const void*(*renderpass_descriptor_cb)(void); + const void*(*renderpass_descriptor_userdata_cb)(void*); + const void*(*drawable_cb)(void); + const void*(*drawable_userdata_cb)(void*); + void* user_data; + uint32_t frame_index; + uint32_t cur_frame_rotate_index; + int ub_size; + int cur_ub_offset; + uint8_t* cur_ub_base_ptr; + bool in_pass; + bool pass_valid; + int cur_width; + int cur_height; + _sg_mtl_state_cache_t state_cache; + _sg_sampler_cache_t sampler_cache; + _sg_mtl_idpool_t idpool; + dispatch_semaphore_t sem; + id device; + id cmd_queue; + id cmd_buffer; + id cmd_encoder; + id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; +} _sg_mtl_backend_t; + +/*=== WGPU BACKEND DECLARATIONS ==============================================*/ +#elif defined(SOKOL_WGPU) + +#define _SG_WGPU_STAGING_ALIGN (256) +#define _SG_WGPU_STAGING_PIPELINE_SIZE (8) +#define _SG_WGPU_ROWPITCH_ALIGN (256) +#define _SG_WGPU_MAX_SHADERSTAGE_IMAGES (8) +#define _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE (1<<16) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + WGPUBuffer buf; + } wgpu; +} _sg_wgpu_buffer_t; +typedef _sg_wgpu_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + WGPUTexture tex; + WGPUTextureView tex_view; + WGPUTexture msaa_tex; + WGPUSampler sampler; + } wgpu; +} _sg_wgpu_image_t; +typedef _sg_wgpu_image_t _sg_image_t; + +typedef struct { + WGPUShaderModule module; + WGPUBindGroupLayout bind_group_layout; + _sg_str_t entry; +} _sg_wgpu_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_wgpu_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } wgpu; +} _sg_wgpu_shader_t; +typedef _sg_wgpu_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + WGPURenderPipeline pip; + uint32_t stencil_ref; + } wgpu; +} _sg_wgpu_pipeline_t; +typedef _sg_wgpu_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + WGPUTextureView render_tex_view; + WGPUTextureView resolve_tex_view; +} _sg_wgpu_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + _sg_wgpu_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_wgpu_attachment_t ds_att; + } wgpu; +} _sg_wgpu_pass_t; +typedef _sg_wgpu_pass_t _sg_pass_t; +typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; + +typedef struct { + _sg_slot_t slot; +} _sg_wgpu_context_t; +typedef _sg_wgpu_context_t _sg_context_t; + +/* a pool of per-frame uniform buffers */ +typedef struct { + WGPUBindGroupLayout bindgroup_layout; + uint32_t num_bytes; + uint32_t offset; /* current offset into current frame's mapped uniform buffer */ + uint32_t bind_offsets[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + WGPUBuffer buf; /* the GPU-side uniform buffer */ + WGPUBindGroup bindgroup; + struct { + int num; + int cur; + WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* CPU-side staging buffers */ + uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* if != 0, staging buffer currently mapped */ + } stage; +} _sg_wgpu_ubpool_t; + +/* ...a similar pool (like uniform buffer pool) of dynamic-resource staging buffers */ +typedef struct { + uint32_t num_bytes; + uint32_t offset; /* current offset into current frame's staging buffer */ + int num; /* number of staging buffers */ + int cur; /* this frame's staging buffer */ + WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* CPU-side staging buffers */ + uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* if != 0, staging buffer currently mapped */ +} _sg_wgpu_stagingpool_t; + +/* the WGPU backend state */ +typedef struct { + bool valid; + bool in_pass; + bool draw_indexed; + int cur_width; + int cur_height; + WGPUDevice dev; + WGPUTextureView (*render_view_cb)(void); + WGPUTextureView (*render_view_userdata_cb)(void*); + WGPUTextureView (*resolve_view_cb)(void); + WGPUTextureView (*resolve_view_userdata_cb)(void*); + WGPUTextureView (*depth_stencil_view_cb)(void); + WGPUTextureView (*depth_stencil_view_userdata_cb)(void*); + void* user_data; + WGPUQueue queue; + WGPUCommandEncoder render_cmd_enc; + WGPUCommandEncoder staging_cmd_enc; + WGPURenderPassEncoder pass_enc; + WGPUBindGroup empty_bind_group; + const _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + _sg_sampler_cache_t sampler_cache; + _sg_wgpu_ubpool_t ub; + _sg_wgpu_stagingpool_t staging; +} _sg_wgpu_backend_t; +#endif + +/*=== RESOURCE POOL DECLARATIONS =============================================*/ + +/* this *MUST* remain 0 */ +#define _SG_INVALID_SLOT_INDEX (0) + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sg_pool_t; + +typedef struct { + _sg_pool_t buffer_pool; + _sg_pool_t image_pool; + _sg_pool_t shader_pool; + _sg_pool_t pipeline_pool; + _sg_pool_t pass_pool; + _sg_pool_t context_pool; + _sg_buffer_t* buffers; + _sg_image_t* images; + _sg_shader_t* shaders; + _sg_pipeline_t* pipelines; + _sg_pass_t* passes; + _sg_context_t* contexts; +} _sg_pools_t; + +/*=== VALIDATION LAYER DECLARATIONS ==========================================*/ +typedef enum { + /* special case 'validation was successful' */ + _SG_VALIDATE_SUCCESS, + + /* buffer creation */ + _SG_VALIDATE_BUFFERDESC_CANARY, + _SG_VALIDATE_BUFFERDESC_SIZE, + _SG_VALIDATE_BUFFERDESC_DATA, + _SG_VALIDATE_BUFFERDESC_DATA_SIZE, + _SG_VALIDATE_BUFFERDESC_NO_DATA, + + /* image data (for image creation and updating) */ + _SG_VALIDATE_IMAGEDATA_NODATA, + _SG_VALIDATE_IMAGEDATA_DATA_SIZE, + + /* image creation */ + _SG_VALIDATE_IMAGEDESC_CANARY, + _SG_VALIDATE_IMAGEDESC_WIDTH, + _SG_VALIDATE_IMAGEDESC_HEIGHT, + _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT, + _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, + _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, + _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, + _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE, + _SG_VALIDATE_IMAGEDESC_RT_NO_DATA, + _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA, + _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, + _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, + + /* shader creation */ + _SG_VALIDATE_SHADERDESC_CANARY, + _SG_VALIDATE_SHADERDESC_SOURCE, + _SG_VALIDATE_SHADERDESC_BYTECODE, + _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, + _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, + _SG_VALIDATE_SHADERDESC_NO_CONT_UBS, + _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS, + _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, + _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS, + _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME, + _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, + _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT, + _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, + _SG_VALIDATE_SHADERDESC_IMG_NAME, + _SG_VALIDATE_SHADERDESC_ATTR_NAMES, + _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS, + _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, + + /* pipeline creation */ + _SG_VALIDATE_PIPELINEDESC_CANARY, + _SG_VALIDATE_PIPELINEDESC_SHADER, + _SG_VALIDATE_PIPELINEDESC_NO_ATTRS, + _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, + _SG_VALIDATE_PIPELINEDESC_ATTR_NAME, + _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, + + /* pass creation */ + _SG_VALIDATE_PASSDESC_CANARY, + _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS, + _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, + _SG_VALIDATE_PASSDESC_IMAGE, + _SG_VALIDATE_PASSDESC_MIPLEVEL, + _SG_VALIDATE_PASSDESC_FACE, + _SG_VALIDATE_PASSDESC_LAYER, + _SG_VALIDATE_PASSDESC_SLICE, + _SG_VALIDATE_PASSDESC_IMAGE_NO_RT, + _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, + _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, + _SG_VALIDATE_PASSDESC_IMAGE_SIZES, + _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, + + /* sg_begin_pass validation */ + _SG_VALIDATE_BEGINPASS_PASS, + _SG_VALIDATE_BEGINPASS_IMAGE, + + /* sg_apply_pipeline validation */ + _SG_VALIDATE_APIP_PIPELINE_VALID_ID, + _SG_VALIDATE_APIP_PIPELINE_EXISTS, + _SG_VALIDATE_APIP_PIPELINE_VALID, + _SG_VALIDATE_APIP_SHADER_EXISTS, + _SG_VALIDATE_APIP_SHADER_VALID, + _SG_VALIDATE_APIP_ATT_COUNT, + _SG_VALIDATE_APIP_COLOR_FORMAT, + _SG_VALIDATE_APIP_DEPTH_FORMAT, + _SG_VALIDATE_APIP_SAMPLE_COUNT, + + /* sg_apply_bindings validation */ + _SG_VALIDATE_ABND_PIPELINE, + _SG_VALIDATE_ABND_PIPELINE_EXISTS, + _SG_VALIDATE_ABND_PIPELINE_VALID, + _SG_VALIDATE_ABND_VBS, + _SG_VALIDATE_ABND_VB_EXISTS, + _SG_VALIDATE_ABND_VB_TYPE, + _SG_VALIDATE_ABND_VB_OVERFLOW, + _SG_VALIDATE_ABND_NO_IB, + _SG_VALIDATE_ABND_IB, + _SG_VALIDATE_ABND_IB_EXISTS, + _SG_VALIDATE_ABND_IB_TYPE, + _SG_VALIDATE_ABND_IB_OVERFLOW, + _SG_VALIDATE_ABND_VS_IMGS, + _SG_VALIDATE_ABND_VS_IMG_EXISTS, + _SG_VALIDATE_ABND_VS_IMG_TYPES, + _SG_VALIDATE_ABND_FS_IMGS, + _SG_VALIDATE_ABND_FS_IMG_EXISTS, + _SG_VALIDATE_ABND_FS_IMG_TYPES, + + /* sg_apply_uniforms validation */ + _SG_VALIDATE_AUB_NO_PIPELINE, + _SG_VALIDATE_AUB_NO_UB_AT_SLOT, + _SG_VALIDATE_AUB_SIZE, + + /* sg_update_buffer validation */ + _SG_VALIDATE_UPDATEBUF_USAGE, + _SG_VALIDATE_UPDATEBUF_SIZE, + _SG_VALIDATE_UPDATEBUF_ONCE, + _SG_VALIDATE_UPDATEBUF_APPEND, + + /* sg_append_buffer validation */ + _SG_VALIDATE_APPENDBUF_USAGE, + _SG_VALIDATE_APPENDBUF_SIZE, + _SG_VALIDATE_APPENDBUF_UPDATE, + + /* sg_update_image validation */ + _SG_VALIDATE_UPDIMG_USAGE, + _SG_VALIDATE_UPDIMG_NOTENOUGHDATA, + _SG_VALIDATE_UPDIMG_ONCE +} _sg_validate_error_t; + +/*=== GENERIC BACKEND STATE ==================================================*/ + +typedef struct { + bool valid; + sg_desc desc; /* original desc with default values patched in */ + uint32_t frame_index; + sg_context active_context; + sg_pass cur_pass; + sg_pipeline cur_pipeline; + bool pass_valid; + bool bindings_valid; + bool next_draw_valid; +#if defined(SOKOL_DEBUG) + _sg_validate_error_t validate_error; +#endif + _sg_pools_t pools; + sg_backend backend; + sg_features features; + sg_limits limits; + sg_pixelformat_info formats[_SG_PIXELFORMAT_NUM]; +#if defined(_SOKOL_ANY_GL) + _sg_gl_backend_t gl; +#elif defined(SOKOL_METAL) + _sg_mtl_backend_t mtl; +#elif defined(SOKOL_D3D11) + _sg_d3d11_backend_t d3d11; +#elif defined(SOKOL_WGPU) + _sg_wgpu_backend_t wgpu; +#endif +#if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks hooks; +#endif +} _sg_state_t; +static _sg_state_t _sg; + +/*-- helper functions --------------------------------------------------------*/ + +_SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) { + return 0 == str->buf[0]; +} + +_SOKOL_PRIVATE const char* _sg_strptr(const _sg_str_t* str) { + return &str->buf[0]; +} + +_SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) { + SOKOL_ASSERT(dst); + if (src) { +#if defined(_MSC_VER) + strncpy_s(dst->buf, _SG_STRING_SIZE, src, (_SG_STRING_SIZE-1)); +#else + strncpy(dst->buf, src, _SG_STRING_SIZE); +#endif + dst->buf[_SG_STRING_SIZE-1] = 0; + } + else { + memset(dst->buf, 0, _SG_STRING_SIZE); + } +} + +_SOKOL_PRIVATE uint32_t _sg_align_u32(uint32_t val, uint32_t align) { + SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0)); + return (val + (align - 1)) & ~(align - 1); +} + +/* return byte size of a vertex format */ +_SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 4; + case SG_VERTEXFORMAT_FLOAT2: return 8; + case SG_VERTEXFORMAT_FLOAT3: return 12; + case SG_VERTEXFORMAT_FLOAT4: return 16; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 4; + case SG_VERTEXFORMAT_SHORT2N: return 4; + case SG_VERTEXFORMAT_USHORT2N: return 4; + case SG_VERTEXFORMAT_SHORT4: return 8; + case SG_VERTEXFORMAT_SHORT4N: return 8; + case SG_VERTEXFORMAT_USHORT4N: return 8; + case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_INVALID: return 0; + default: + SOKOL_UNREACHABLE; + return -1; + } +} + +_SOKOL_PRIVATE uint32_t _sg_uniform_alignment(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { + if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { + return 1; + } + else { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 16; + default: + SOKOL_UNREACHABLE; + return 1; + } + } + else { + return 16; + } + } +} + +_SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_INT3: + return 12; + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 64; + default: + SOKOL_UNREACHABLE; + return 0; + } + } + else { + if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_INT3: + return 12 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT4: + return 16 * (uint32_t)array_count; + case SG_UNIFORMTYPE_MAT4: + return 64 * (uint32_t)array_count; + default: + SOKOL_UNREACHABLE; + return 0; + } + } + else { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT: + case SG_UNIFORMTYPE_INT2: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16 * (uint32_t)array_count; + case SG_UNIFORMTYPE_MAT4: + return 64 * (uint32_t)array_count; + default: + SOKOL_UNREACHABLE; + return 0; + } + } + } +} + +/* return true if pixel format is a compressed format */ +_SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_ETC2_RG11SN: + return true; + default: + return false; + } +} + +/* return true if pixel format is a valid render target format */ +_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_color_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].render && !_sg.formats[fmt_index].depth; +} + +/* return true if pixel format is a valid depth format */ +_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_depth_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].render && _sg.formats[fmt_index].depth; +} + +/* return true if pixel format is a depth-stencil format */ +_SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) { + return (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); +} + +/* return the bytes-per-pixel for a pixel format */ +_SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + return 1; + + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + return 2; + + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_BGRA8: + case SG_PIXELFORMAT_RGB10A2: + case SG_PIXELFORMAT_RG11B10F: + return 4; + + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + case SG_PIXELFORMAT_RG32F: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA16F: + return 8; + + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + case SG_PIXELFORMAT_RGBA32F: + return 16; + + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE int _sg_roundup(int val, int round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +/* return row pitch for an image + + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp + + For the special PVRTC pitch computation, see: + GL extension requirement (https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt) + + Quote: + + 6) How is the imageSize argument calculated for the CompressedTexImage2D + and CompressedTexSubImage2D functions. + + Resolution: For PVRTC 4BPP formats the imageSize is calculated as: + ( max(width, 8) * max(height, 8) * 4 + 7) / 8 + For PVRTC 2BPP formats the imageSize is calculated as: + ( max(width, 16) * max(height, 8) * 2 + 7) / 8 +*/ +_SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) { + int pitch; + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + pitch = ((width + 3) / 4) * 8; + pitch = pitch < 8 ? 8 : pitch; + break; + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_ETC2_RG11SN: + pitch = ((width + 3) / 4) * 16; + pitch = pitch < 16 ? 16 : pitch; + break; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + pitch = (_sg_max(width, 8) * 4 + 7) / 8; + break; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + pitch = (_sg_max(width, 16) * 2 + 7) / 8; + break; + default: + pitch = width * _sg_pixelformat_bytesize(fmt); + break; + } + pitch = _sg_roundup(pitch, row_align); + return pitch; +} + +/* compute the number of rows in a surface depending on pixel format */ +_SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { + int num_rows; + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + num_rows = ((height + 3) / 4); + break; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + /* NOTE: this is most likely not correct because it ignores any + PVCRTC block size, but multiplied with _sg_row_pitch() + it gives the correct surface pitch. + + See: https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt + */ + num_rows = ((_sg_max(height, 8) + 7) / 8) * 8; + break; + default: + num_rows = height; + break; + } + if (num_rows < 1) { + num_rows = 1; + } + return num_rows; +} + +/* return pitch of a 2D subimage / texture slice + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp +*/ +_SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align) { + int num_rows = _sg_num_rows(fmt, height); + return num_rows * _sg_row_pitch(fmt, width, row_align); +} + +/* capability table pixel format helper functions */ +_SOKOL_PRIVATE void _sg_pixelformat_all(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->blend = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_s(sg_pixelformat_info* pfi) { + pfi->sample = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sf(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->filter = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sr(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_srmd(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->render = true; + pfi->msaa = true; + pfi->depth = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_srm(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfrm(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->render = true; + pfi->msaa = true; +} +_SOKOL_PRIVATE void _sg_pixelformat_sbrm(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->blend = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sbr(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->blend = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfbr(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->blend = true; + pfi->render = true; +} + +/* resolve pass action defaults into a new pass action struct */ +_SOKOL_PRIVATE void _sg_resolve_default_pass_action(const sg_pass_action* from, sg_pass_action* to) { + SOKOL_ASSERT(from && to); + *to = *from; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (to->colors[i].action == _SG_ACTION_DEFAULT) { + to->colors[i].action = SG_ACTION_CLEAR; + to->colors[i].value.r = SG_DEFAULT_CLEAR_RED; + to->colors[i].value.g = SG_DEFAULT_CLEAR_GREEN; + to->colors[i].value.b = SG_DEFAULT_CLEAR_BLUE; + to->colors[i].value.a = SG_DEFAULT_CLEAR_ALPHA; + } + } + if (to->depth.action == _SG_ACTION_DEFAULT) { + to->depth.action = SG_ACTION_CLEAR; + to->depth.value = SG_DEFAULT_CLEAR_DEPTH; + } + if (to->stencil.action == _SG_ACTION_DEFAULT) { + to->stencil.action = SG_ACTION_CLEAR; + to->stencil.value = SG_DEFAULT_CLEAR_STENCIL; + } +} + +/*== DUMMY BACKEND IMPL ======================================================*/ +#if defined(SOKOL_DUMMY_BACKEND) + +_SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + _SOKOL_UNUSED(desc); + _sg.backend = SG_BACKEND_DUMMY; + for (int i = SG_PIXELFORMAT_R8; i < SG_PIXELFORMAT_BC1_RGBA; i++) { + _sg.formats[i].sample = true; + _sg.formats[i].filter = true; + _sg.formats[i].render = true; + _sg.formats[i].blend = true; + _sg.formats[i].msaa = true; + } + _sg.formats[SG_PIXELFORMAT_DEPTH].depth = true; + _sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL].depth = true; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_backend(void) { + /* empty */ +} + +_SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { + /* empty*/ +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); +} + +_SOKOL_PRIVATE void _sg_dummy_activate_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _sg_buffer_common_init(&buf->cmn, desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SOKOL_UNUSED(buf); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _sg_image_common_init(&img->cmn, desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SOKOL_UNUSED(img); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + _sg_shader_common_init(&shd->cmn, desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SOKOL_UNUSED(shd); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + + _sg_pass_common_init(&pass->cmn, desc); + + const sg_pass_attachment_desc* att_desc; + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + att_desc = &desc->color_attachments[i]; + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->dmy.color_atts[i].image); + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); + pass->dmy.color_atts[i].image = att_images[i]; + } + + SOKOL_ASSERT(0 == pass->dmy.ds_att.image); + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + pass->dmy.ds_att.image = att_images[ds_img_index]; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + _SOKOL_UNUSED(pass); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->dmy.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->dmy.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_dummy_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + _SOKOL_UNUSED(pass); + _SOKOL_UNUSED(action); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); +} + +_SOKOL_PRIVATE void _sg_dummy_end_pass(void) { + /* empty */ +} + +_SOKOL_PRIVATE void _sg_dummy_commit(void) { + /* empty */ +} + +_SOKOL_PRIVATE void _sg_dummy_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(pip); + SOKOL_ASSERT(vbs && vb_offsets); + SOKOL_ASSERT(vs_imgs); + SOKOL_ASSERT(fs_imgs); + _SOKOL_UNUSED(pip); + _SOKOL_UNUSED(vbs); _SOKOL_UNUSED(vb_offsets); _SOKOL_UNUSED(num_vbs); + _SOKOL_UNUSED(ib); _SOKOL_UNUSED(ib_offset); + _SOKOL_UNUSED(vs_imgs); _SOKOL_UNUSED(num_vs_imgs); + _SOKOL_UNUSED(fs_imgs); _SOKOL_UNUSED(num_fs_imgs); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + _SOKOL_UNUSED(stage_index); + _SOKOL_UNUSED(ub_index); + _SOKOL_UNUSED(data); +} + +_SOKOL_PRIVATE void _sg_dummy_draw(int base_element, int num_elements, int num_instances) { + _SOKOL_UNUSED(base_element); + _SOKOL_UNUSED(num_elements); + _SOKOL_UNUSED(num_instances); +} + +_SOKOL_PRIVATE void _sg_dummy_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(data); + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } +} + +_SOKOL_PRIVATE int _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(data); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + return _sg_roundup((int)data->size, 4); +} + +_SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + _SOKOL_UNUSED(data); + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } +} + +/*== GL BACKEND ==============================================================*/ +#elif defined(_SOKOL_ANY_GL) + +/*=== OPTIONAL GL LOADER FOR WIN32 ===========================================*/ +#if defined(_SOKOL_USE_WIN32_GL_LOADER) + +// X Macro list of GL function names and signatures +#define _SG_GL_FUNCS \ +_SG_XMACRO(glBindVertexArray, void, (GLuint array)) \ +_SG_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \ +_SG_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \ +_SG_XMACRO(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \ +_SG_XMACRO(glBindRenderbuffer, void, (GLenum target, GLuint renderbuffer)) \ +_SG_XMACRO(glGetStringi, const GLubyte *, (GLenum name, GLuint index)) \ +_SG_XMACRO(glClearBufferfi, void, (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)) \ +_SG_XMACRO(glClearBufferfv, void, (GLenum buffer, GLint drawbuffer, const GLfloat * value)) \ +_SG_XMACRO(glClearBufferuiv, void, (GLenum buffer, GLint drawbuffer, const GLuint * value)) \ +_SG_XMACRO(glClearBufferiv, void, (GLenum buffer, GLint drawbuffer, const GLint * value)) \ +_SG_XMACRO(glDeleteRenderbuffers, void, (GLsizei n, const GLuint * renderbuffers)) \ +_SG_XMACRO(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ +_SG_XMACRO(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ +_SG_XMACRO(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ +_SG_XMACRO(glUniform4fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ +_SG_XMACRO(glUniform1iv, void, (GLint location, GLsizei count, const GLint * value)) \ +_SG_XMACRO(glUniform2iv, void, (GLint location, GLsizei count, const GLint * value)) \ +_SG_XMACRO(glUniform3iv, void, (GLint location, GLsizei count, const GLint * value)) \ +_SG_XMACRO(glUniform4iv, void, (GLint location, GLsizei count, const GLint * value)) \ +_SG_XMACRO(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)) \ +_SG_XMACRO(glUseProgram, void, (GLuint program)) \ +_SG_XMACRO(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length)) \ +_SG_XMACRO(glLinkProgram, void, (GLuint program)) \ +_SG_XMACRO(glGetUniformLocation, GLint, (GLuint program, const GLchar * name)) \ +_SG_XMACRO(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint * params)) \ +_SG_XMACRO(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ +_SG_XMACRO(glGetAttribLocation, GLint, (GLuint program, const GLchar * name)) \ +_SG_XMACRO(glDisableVertexAttribArray, void, (GLuint index)) \ +_SG_XMACRO(glDeleteShader, void, (GLuint shader)) \ +_SG_XMACRO(glDeleteProgram, void, (GLuint program)) \ +_SG_XMACRO(glCompileShader, void, (GLuint shader)) \ +_SG_XMACRO(glStencilFuncSeparate, void, (GLenum face, GLenum func, GLint ref, GLuint mask)) \ +_SG_XMACRO(glStencilOpSeparate, void, (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) \ +_SG_XMACRO(glRenderbufferStorageMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)) \ +_SG_XMACRO(glDrawBuffers, void, (GLsizei n, const GLenum * bufs)) \ +_SG_XMACRO(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \ +_SG_XMACRO(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void * data)) \ +_SG_XMACRO(glGenBuffers, void, (GLsizei n, GLuint * buffers)) \ +_SG_XMACRO(glCheckFramebufferStatus, GLenum, (GLenum target)) \ +_SG_XMACRO(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \ +_SG_XMACRO(glCompressedTexImage2D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data)) \ +_SG_XMACRO(glCompressedTexImage3D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data)) \ +_SG_XMACRO(glActiveTexture, void, (GLenum texture)) \ +_SG_XMACRO(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels)) \ +_SG_XMACRO(glRenderbufferStorage, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) \ +_SG_XMACRO(glGenTextures, void, (GLsizei n, GLuint * textures)) \ +_SG_XMACRO(glPolygonOffset, void, (GLfloat factor, GLfloat units)) \ +_SG_XMACRO(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void * indices)) \ +_SG_XMACRO(glDeleteFramebuffers, void, (GLsizei n, const GLuint * framebuffers)) \ +_SG_XMACRO(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \ +_SG_XMACRO(glDeleteTextures, void, (GLsizei n, const GLuint * textures)) \ +_SG_XMACRO(glGetProgramiv, void, (GLuint program, GLenum pname, GLint * params)) \ +_SG_XMACRO(glBindTexture, void, (GLenum target, GLuint texture)) \ +_SG_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \ +_SG_XMACRO(glCreateShader, GLuint, (GLenum type)) \ +_SG_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \ +_SG_XMACRO(glClearDepth, void, (GLdouble depth)) \ +_SG_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \ +_SG_XMACRO(glCreateProgram, GLuint, (void)) \ +_SG_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ +_SG_XMACRO(glDeleteBuffers, void, (GLsizei n, const GLuint * buffers)) \ +_SG_XMACRO(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \ +_SG_XMACRO(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount)) \ +_SG_XMACRO(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer)) \ +_SG_XMACRO(glUniform1i, void, (GLint location, GLint v0)) \ +_SG_XMACRO(glDisable, void, (GLenum cap)) \ +_SG_XMACRO(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ +_SG_XMACRO(glColorMaski, void, (GLuint buf, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ +_SG_XMACRO(glBindBuffer, void, (GLenum target, GLuint buffer)) \ +_SG_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \ +_SG_XMACRO(glDepthMask, void, (GLboolean flag)) \ +_SG_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ +_SG_XMACRO(glClearStencil, void, (GLint s)) \ +_SG_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ +_SG_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \ +_SG_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \ +_SG_XMACRO(glBlendFuncSeparate, void, (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) \ +_SG_XMACRO(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \ +_SG_XMACRO(glGetIntegerv, void, (GLenum pname, GLint * data)) \ +_SG_XMACRO(glEnable, void, (GLenum cap)) \ +_SG_XMACRO(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \ +_SG_XMACRO(glStencilMask, void, (GLuint mask)) \ +_SG_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \ +_SG_XMACRO(glGetError, GLenum, (void)) \ +_SG_XMACRO(glClearColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ +_SG_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ +_SG_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \ +_SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, GLfloat* params)) \ +_SG_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ +_SG_XMACRO(glDepthFunc, void, (GLenum func)) \ +_SG_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \ +_SG_XMACRO(glStencilFunc, void, (GLenum func, GLint ref, GLuint mask)) \ +_SG_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \ +_SG_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \ +_SG_XMACRO(glReadBuffer, void, (GLenum src)) \ +_SG_XMACRO(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * data)) \ +_SG_XMACRO(glClear, void, (GLbitfield mask)) \ +_SG_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \ +_SG_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ +_SG_XMACRO(glFrontFace, void, (GLenum mode)) \ +_SG_XMACRO(glCullFace, void, (GLenum mode)) + +// generate GL function pointer typedefs +#define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; +_SG_GL_FUNCS +#undef _SG_XMACRO + +// generate GL function pointers +#define _SG_XMACRO(name, ret, args) static PFN_ ## name name; +_SG_GL_FUNCS +#undef _SG_XMACRO + +// helper function to lookup GL functions in GL DLL +typedef PROC (WINAPI * _sg_wglGetProcAddress)(LPCSTR); +_SOKOL_PRIVATE void* _sg_gl_getprocaddr(const char* name, _sg_wglGetProcAddress wgl_getprocaddress) { + void* proc_addr = (void*) wgl_getprocaddress(name); + if (0 == proc_addr) { + proc_addr = (void*) GetProcAddress(_sg.gl.opengl32_dll, name); + } + SOKOL_ASSERT(proc_addr); + return proc_addr; +} + +// populate GL function pointers +_SOKOL_PRIVATE void _sg_gl_load_opengl(void) { + SOKOL_ASSERT(0 == _sg.gl.opengl32_dll); + _sg.gl.opengl32_dll = LoadLibraryA("opengl32.dll"); + SOKOL_ASSERT(_sg.gl.opengl32_dll); + _sg_wglGetProcAddress wgl_getprocaddress = (_sg_wglGetProcAddress) GetProcAddress(_sg.gl.opengl32_dll, "wglGetProcAddress"); + SOKOL_ASSERT(wgl_getprocaddress); +#define _SG_XMACRO(name, ret, args) name = (PFN_ ## name) _sg_gl_getprocaddr(#name, wgl_getprocaddress); + _SG_GL_FUNCS +#undef _SG_XMACRO +} + +_SOKOL_PRIVATE void _sg_gl_unload_opengl(void) { + SOKOL_ASSERT(_sg.gl.opengl32_dll); + FreeLibrary(_sg.gl.opengl32_dll); + _sg.gl.opengl32_dll = 0; +} +#endif // _SOKOL_USE_WIN32_GL_LOADER + +/*-- type translation --------------------------------------------------------*/ +_SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; + case SG_BUFFERTYPE_INDEXBUFFER: return GL_ELEMENT_ARRAY_BUFFER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; + case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; +#if !defined(SOKOL_GLES2) + case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; + case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; +#endif + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_usage(sg_usage u) { + switch (u) { + case SG_USAGE_IMMUTABLE: return GL_STATIC_DRAW; + case SG_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; + case SG_USAGE_STREAM: return GL_STREAM_DRAW; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VS: return GL_VERTEX_SHADER; + case SG_SHADERSTAGE_FS: return GL_FRAGMENT_SHADER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 1; + case SG_VERTEXFORMAT_FLOAT2: return 2; + case SG_VERTEXFORMAT_FLOAT3: return 3; + case SG_VERTEXFORMAT_FLOAT4: return 4; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 2; + case SG_VERTEXFORMAT_SHORT2N: return 2; + case SG_VERTEXFORMAT_USHORT2N: return 2; + case SG_VERTEXFORMAT_SHORT4: return 4; + case SG_VERTEXFORMAT_SHORT4N: return 4; + case SG_VERTEXFORMAT_USHORT4N: return 4; + case SG_VERTEXFORMAT_UINT10_N2: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: + case SG_VERTEXFORMAT_FLOAT2: + case SG_VERTEXFORMAT_FLOAT3: + case SG_VERTEXFORMAT_FLOAT4: + return GL_FLOAT; + case SG_VERTEXFORMAT_BYTE4: + case SG_VERTEXFORMAT_BYTE4N: + return GL_BYTE; + case SG_VERTEXFORMAT_UBYTE4: + case SG_VERTEXFORMAT_UBYTE4N: + return GL_UNSIGNED_BYTE; + case SG_VERTEXFORMAT_SHORT2: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_SHORT4: + case SG_VERTEXFORMAT_SHORT4N: + return GL_SHORT; + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_USHORT4N: + return GL_UNSIGNED_SHORT; + case SG_VERTEXFORMAT_UINT10_N2: + return GL_UNSIGNED_INT_2_10_10_10_REV; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLboolean _sg_gl_vertexformat_normalized(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_BYTE4N: + case SG_VERTEXFORMAT_UBYTE4N: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_SHORT4N: + case SG_VERTEXFORMAT_USHORT4N: + case SG_VERTEXFORMAT_UINT10_N2: + return GL_TRUE; + default: + return GL_FALSE; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return GL_POINTS; + case SG_PRIMITIVETYPE_LINES: return GL_LINES; + case SG_PRIMITIVETYPE_LINE_STRIP: return GL_LINE_STRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return GL_UNSIGNED_SHORT; + case SG_INDEXTYPE_UINT32: return GL_UNSIGNED_INT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_compare_func(sg_compare_func cmp) { + switch (cmp) { + case SG_COMPAREFUNC_NEVER: return GL_NEVER; + case SG_COMPAREFUNC_LESS: return GL_LESS; + case SG_COMPAREFUNC_EQUAL: return GL_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return GL_LEQUAL; + case SG_COMPAREFUNC_GREATER: return GL_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return GL_NOTEQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return GL_GEQUAL; + case SG_COMPAREFUNC_ALWAYS: return GL_ALWAYS; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return GL_KEEP; + case SG_STENCILOP_ZERO: return GL_ZERO; + case SG_STENCILOP_REPLACE: return GL_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return GL_INCR; + case SG_STENCILOP_DECR_CLAMP: return GL_DECR; + case SG_STENCILOP_INVERT: return GL_INVERT; + case SG_STENCILOP_INCR_WRAP: return GL_INCR_WRAP; + case SG_STENCILOP_DECR_WRAP: return GL_DECR_WRAP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return GL_ZERO; + case SG_BLENDFACTOR_ONE: return GL_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return GL_DST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return GL_SRC_ALPHA_SATURATE; + case SG_BLENDFACTOR_BLEND_COLOR: return GL_CONSTANT_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return GL_CONSTANT_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return GL_FUNC_ADD; + case SG_BLENDOP_SUBTRACT: return GL_FUNC_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: return GL_NEAREST; + case SG_FILTER_LINEAR: return GL_LINEAR; + case SG_FILTER_NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; + case SG_FILTER_LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; + case SG_FILTER_LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) { + switch (w) { + case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; +#if defined(SOKOL_GLCORE33) + case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; +#else + case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_EDGE; +#endif + case SG_WRAP_REPEAT: return GL_REPEAT; + case SG_WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_BGRA8: + return GL_UNSIGNED_BYTE; + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA8SI: + return GL_BYTE; + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16UI: + return GL_UNSIGNED_SHORT; + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16SI: + return GL_SHORT; + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RGBA16F: + return GL_HALF_FLOAT; + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RGBA32UI: + return GL_UNSIGNED_INT; + case SG_PIXELFORMAT_R32SI: + case SG_PIXELFORMAT_RG32SI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_INT; + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_RG32F: + case SG_PIXELFORMAT_RGBA32F: + return GL_FLOAT; +#if !defined(SOKOL_GLES2) + case SG_PIXELFORMAT_RGB10A2: + return GL_UNSIGNED_INT_2_10_10_10_REV; + case SG_PIXELFORMAT_RG11B10F: + return GL_UNSIGNED_INT_10F_11F_11F_REV; +#endif + case SG_PIXELFORMAT_DEPTH: + return GL_UNSIGNED_SHORT; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return GL_UNSIGNED_INT_24_8; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_R32F: +#if defined(SOKOL_GLES2) + return GL_LUMINANCE; +#else + if (_sg.gl.gles2) { + return GL_LUMINANCE; + } + else { + return GL_RED; + } +#endif +#if !defined(SOKOL_GLES2) + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + return GL_RED_INTEGER; + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RG32F: + return GL_RG; + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + return GL_RG_INTEGER; +#endif + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16F: + case SG_PIXELFORMAT_RGBA32F: + case SG_PIXELFORMAT_RGB10A2: + return GL_RGBA; +#if !defined(SOKOL_GLES2) + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_RGBA_INTEGER; +#endif + case SG_PIXELFORMAT_RG11B10F: + return GL_RGB; + case SG_PIXELFORMAT_DEPTH: + return GL_DEPTH_COMPONENT; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return GL_DEPTH_STENCIL; + case SG_PIXELFORMAT_BC1_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: + return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: + return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: + return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: + return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: + return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: + return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: + return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: + return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: + return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: + return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_RG11: + return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_ETC2_RG11SN: + return GL_COMPRESSED_SIGNED_RG11_EAC; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { +#if defined(SOKOL_GLES2) + return _sg_gl_teximage_format(fmt); +#else + if (_sg.gl.gles2) { + return _sg_gl_teximage_format(fmt); + } + else { + switch (fmt) { + case SG_PIXELFORMAT_R8: return GL_R8; + case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return GL_R8UI; + case SG_PIXELFORMAT_R8SI: return GL_R8I; +#if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_R16: return GL_R16; + case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM; +#endif + case SG_PIXELFORMAT_R16UI: return GL_R16UI; + case SG_PIXELFORMAT_R16SI: return GL_R16I; + case SG_PIXELFORMAT_R16F: return GL_R16F; + case SG_PIXELFORMAT_RG8: return GL_RG8; + case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM; + case SG_PIXELFORMAT_RG8UI: return GL_RG8UI; + case SG_PIXELFORMAT_RG8SI: return GL_RG8I; + case SG_PIXELFORMAT_R32UI: return GL_R32UI; + case SG_PIXELFORMAT_R32SI: return GL_R32I; + case SG_PIXELFORMAT_R32F: return GL_R32F; +#if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RG16: return GL_RG16; + case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM; +#endif + case SG_PIXELFORMAT_RG16UI: return GL_RG16UI; + case SG_PIXELFORMAT_RG16SI: return GL_RG16I; + case SG_PIXELFORMAT_RG16F: return GL_RG16F; + case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; + case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI; + case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I; + case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2; + case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F; + case SG_PIXELFORMAT_RG32UI: return GL_RG32UI; + case SG_PIXELFORMAT_RG32SI: return GL_RG32I; + case SG_PIXELFORMAT_RG32F: return GL_RG32F; +#if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RGBA16: return GL_RGBA16; + case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM; +#endif + case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI; + case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I; + case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; + case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI; + case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I; + case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; + case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; + case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; + case SG_PIXELFORMAT_BC1_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_RG11: return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_ETC2_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; + default: SOKOL_UNREACHABLE; return 0; + } + } +#endif +} + +_SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { + switch (face_index) { + case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; + case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + case 5: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_depth_attachment_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; + case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; + default: SOKOL_UNREACHABLE; return 0; + } +} + +/* see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml */ +_SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) { +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + } + else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8]); + } +#else + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8]); +#endif +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); +#if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); +#endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); +#if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); +#endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + } +#endif + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + } +#endif + if (has_bgra) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + } +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); +#if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); +#endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + } +#endif + // FIXME: WEBGL_depth_texture extension? + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); +} + +/* FIXME: OES_half_float_blend */ +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float, bool has_texture_half_float_linear) { +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + if (has_texture_half_float_linear) { + if (has_colorbuffer_half_float) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + } + else { + if (has_colorbuffer_half_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + } + } + else { +#endif + /* GLES2 can only render to RGBA, and there's no RG format */ + if (has_texture_half_float_linear) { + if (has_colorbuffer_half_float) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); + } + else { + if (has_colorbuffer_half_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R16F]); + } +#if !defined(SOKOL_GLES2) + } +#endif +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_float(bool has_colorbuffer_float, bool has_texture_float_linear, bool has_float_blend) { +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + if (has_texture_float_linear) { + if (has_colorbuffer_float) { + if (has_float_blend) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + else { + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } + else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } + else { + if (has_colorbuffer_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } + } + else { +#endif + /* GLES2 can only render to RGBA, and there's no RG format */ + if (has_texture_float_linear) { + if (has_colorbuffer_float) { + if (has_float_blend) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + else { + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } + else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); + } + else { + if (has_colorbuffer_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); + } +#if !defined(SOKOL_GLES2) + } +#endif +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_s3tc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_rgtc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_bptc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_pvrtc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_etc2(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); +} + +_SOKOL_PRIVATE void _sg_gl_init_limits(void) { + _SG_GL_CHECK_ERROR(); + GLint gl_int; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_2d = gl_int; + _sg.limits.max_image_size_array = gl_int; + glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_cube = gl_int; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_int); + _SG_GL_CHECK_ERROR(); + if (gl_int > SG_MAX_VERTEX_ATTRIBUTES) { + gl_int = SG_MAX_VERTEX_ATTRIBUTES; + } + _sg.limits.max_vertex_attrs = gl_int; + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.gl_max_vertex_uniform_vectors = gl_int; +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_3d = gl_int; + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_array_layers = gl_int; + } +#endif + if (_sg.gl.ext_anisotropic) { + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.gl.max_anisotropy = gl_int; + } + else { + _sg.gl.max_anisotropy = 1; + } + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.gl.max_combined_texture_image_units = gl_int; +} + +#if defined(SOKOL_GLCORE33) +_SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { + _sg.backend = SG_BACKEND_GLCORE33; + + _sg.features.origin_top_left = false; + _sg.features.instancing = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; + _sg.features.image_clamp_to_border = true; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = true; + + /* scan extensions */ + bool has_s3tc = false; /* BC1..BC3 */ + bool has_rgtc = false; /* BC4 and BC5 */ + bool has_bptc = false; /* BC6H and BC7 */ + bool has_pvrtc = false; + bool has_etc2 = false; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (ext) { + if (strstr(ext, "_texture_compression_s3tc")) { + has_s3tc = true; + } + else if (strstr(ext, "_texture_compression_rgtc")) { + has_rgtc = true; + } + else if (strstr(ext, "_texture_compression_bptc")) { + has_bptc = true; + } + else if (strstr(ext, "_texture_compression_pvrtc")) { + has_pvrtc = true; + } + else if (strstr(ext, "_ES3_compatibility")) { + has_etc2 = true; + } + else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + } + } + } + + /* limits */ + _sg_gl_init_limits(); + + /* pixel formats */ + const bool has_bgra = false; /* not a bug */ + const bool has_colorbuffer_float = true; + const bool has_colorbuffer_half_float = true; + const bool has_texture_float_linear = true; /* FIXME??? */ + const bool has_texture_half_float_linear = true; + const bool has_float_blend = true; + _sg_gl_init_pixelformats(has_bgra); + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_pvrtc) { + _sg_gl_init_pixelformats_pvrtc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } +} +#endif + +#if defined(SOKOL_GLES3) +_SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { + _sg.backend = SG_BACKEND_GLES3; + + _sg.features.origin_top_left = false; + _sg.features.instancing = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = false; + + bool has_s3tc = false; /* BC1..BC3 */ + bool has_rgtc = false; /* BC4 and BC5 */ + bool has_bptc = false; /* BC6H and BC7 */ + bool has_pvrtc = false; +#if defined(__EMSCRIPTEN__) + bool has_etc2 = false; +#else + bool has_etc2 = true; +#endif + bool has_colorbuffer_float = false; + bool has_colorbuffer_half_float = false; + bool has_texture_float_linear = false; + bool has_float_blend = false; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (ext) { + if (strstr(ext, "_texture_compression_s3tc")) { + has_s3tc = true; + } + else if (strstr(ext, "_compressed_texture_s3tc")) { + has_s3tc = true; + } + else if (strstr(ext, "_texture_compression_rgtc")) { + has_rgtc = true; + } + else if (strstr(ext, "_texture_compression_bptc")) { + has_bptc = true; + } + else if (strstr(ext, "_texture_compression_pvrtc")) { + has_pvrtc = true; + } + else if (strstr(ext, "_compressed_texture_pvrtc")) { + has_pvrtc = true; + } + else if (strstr(ext, "_compressed_texture_etc")) { + has_etc2 = true; + } + else if (strstr(ext, "_color_buffer_float")) { + has_colorbuffer_float = true; + } + else if (strstr(ext, "_color_buffer_half_float")) { + has_colorbuffer_half_float = true; + } + else if (strstr(ext, "_texture_float_linear")) { + has_texture_float_linear = true; + } + else if (strstr(ext, "_float_blend")) { + has_float_blend = true; + } + else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + } + } + } + + /* on WebGL2, color_buffer_float also includes 16-bit formats + see: https://developer.mozilla.org/en-US/docs/Web/API/EXT_color_buffer_float + */ +#if defined(__EMSCRIPTEN__) + has_colorbuffer_half_float = has_colorbuffer_float; +#endif + + /* limits */ + _sg_gl_init_limits(); + + /* pixel formats */ + const bool has_texture_half_float_linear = true; + const bool has_bgra = false; /* not a bug */ + _sg_gl_init_pixelformats(has_bgra); + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_pvrtc) { + _sg_gl_init_pixelformats_pvrtc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } +} +#endif + +#if defined(SOKOL_GLES3) || defined(SOKOL_GLES2) +_SOKOL_PRIVATE void _sg_gl_init_caps_gles2(void) { + _sg.backend = SG_BACKEND_GLES2; + + bool has_s3tc = false; /* BC1..BC3 */ + bool has_rgtc = false; /* BC4 and BC5 */ + bool has_bptc = false; /* BC6H and BC7 */ + bool has_pvrtc = false; + bool has_etc2 = false; + bool has_texture_float = false; + bool has_texture_float_linear = false; + bool has_colorbuffer_float = false; + bool has_float_blend = false; + bool has_instancing = false; + const char* ext = (const char*) glGetString(GL_EXTENSIONS); + if (ext) { + has_s3tc = strstr(ext, "_texture_compression_s3tc") || strstr(ext, "_compressed_texture_s3tc"); + has_rgtc = strstr(ext, "_texture_compression_rgtc"); + has_bptc = strstr(ext, "_texture_compression_bptc"); + has_pvrtc = strstr(ext, "_texture_compression_pvrtc") || strstr(ext, "_compressed_texture_pvrtc"); + has_etc2 = strstr(ext, "_compressed_texture_etc"); + has_texture_float = strstr(ext, "_texture_float"); + has_texture_float_linear = strstr(ext, "_texture_float_linear"); + has_colorbuffer_float = strstr(ext, "_color_buffer_float"); + has_float_blend = strstr(ext, "_float_blend"); + /* don't bother with half_float support on WebGL1 + has_texture_half_float = strstr(ext, "_texture_half_float"); + has_texture_half_float_linear = strstr(ext, "_texture_half_float_linear"); + has_colorbuffer_half_float = strstr(ext, "_color_buffer_half_float"); + */ + has_instancing = strstr(ext, "_instanced_arrays"); + _sg.gl.ext_anisotropic = strstr(ext, "ext_anisotropic"); + } + + _sg.features.origin_top_left = false; +#if defined(_SOKOL_GL_INSTANCING_ENABLED) + _sg.features.instancing = has_instancing; +#endif + _sg.features.multiple_render_targets = false; + _sg.features.msaa_render_targets = false; + _sg.features.imagetype_3d = false; + _sg.features.imagetype_array = false; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = false; + + /* limits */ + _sg_gl_init_limits(); + + /* pixel formats */ + const bool has_bgra = false; /* not a bug */ + const bool has_texture_half_float = false; + const bool has_texture_half_float_linear = false; + const bool has_colorbuffer_half_float = false; + _sg_gl_init_pixelformats(has_bgra); + if (has_texture_float) { + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + } + if (has_texture_half_float) { + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); + } + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_pvrtc) { + _sg_gl_init_pixelformats_pvrtc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } + /* GLES2 doesn't allow multi-sampled render targets at all */ + for (int i = 0; i < _SG_PIXELFORMAT_NUM; i++) { + _sg.formats[i].msaa = false; + } +} +#endif + +/*-- state cache implementation ----------------------------------------------*/ +_SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { + if (force || (_sg.gl.cache.vertex_buffer != 0)) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + _sg.gl.cache.vertex_buffer = 0; + } + if (force || (_sg.gl.cache.index_buffer != 0)) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _sg.gl.cache.index_buffer = 0; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { + SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target)); + if (target == GL_ARRAY_BUFFER) { + if (_sg.gl.cache.vertex_buffer != buffer) { + _sg.gl.cache.vertex_buffer = buffer; + glBindBuffer(target, buffer); + } + } + else { + if (_sg.gl.cache.index_buffer != buffer) { + _sg.gl.cache.index_buffer = buffer; + glBindBuffer(target, buffer); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { + if (target == GL_ARRAY_BUFFER) { + _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer; + } + else { + _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { + if (target == GL_ARRAY_BUFFER) { + if (_sg.gl.cache.stored_vertex_buffer != 0) { + /* we only care restoring valid ids */ + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); + _sg.gl.cache.stored_vertex_buffer = 0; + } + } + else { + if (_sg.gl.cache.stored_index_buffer != 0) { + /* we only care restoring valid ids */ + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer); + _sg.gl.cache.stored_index_buffer = 0; + } + } +} + +/* called when from _sg_gl_destroy_buffer() */ +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { + if (buf == _sg.gl.cache.vertex_buffer) { + _sg.gl.cache.vertex_buffer = 0; + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + if (buf == _sg.gl.cache.index_buffer) { + _sg.gl.cache.index_buffer = 0; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + if (buf == _sg.gl.cache.stored_vertex_buffer) { + _sg.gl.cache.stored_vertex_buffer = 0; + } + if (buf == _sg.gl.cache.stored_index_buffer) { + _sg.gl.cache.stored_index_buffer = 0; + } + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (buf == _sg.gl.cache.attrs[i].gl_vbuf) { + _sg.gl.cache.attrs[i].gl_vbuf = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { + if (_sg.gl.cache.cur_active_texture != texture) { + _sg.gl.cache.cur_active_texture = texture; + glActiveTexture(texture); + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_bindings(bool force) { + for (int i = 0; (i < SG_MAX_SHADERSTAGE_IMAGES) && (i < _sg.gl.max_combined_texture_image_units); i++) { + if (force || (_sg.gl.cache.textures[i].texture != 0)) { + GLenum gl_texture_slot = (GLenum) (GL_TEXTURE0 + i); + glActiveTexture(gl_texture_slot); + glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glBindTexture(GL_TEXTURE_3D, 0); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + } +#endif + _sg.gl.cache.textures[i].target = 0; + _sg.gl.cache.textures[i].texture = 0; + _sg.gl.cache.cur_active_texture = gl_texture_slot; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_texture(int slot_index, GLenum target, GLuint texture) { + /* it's valid to call this function with target=0 and/or texture=0 + target=0 will unbind the previous binding, texture=0 will clear + the new binding + */ + SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); + if (slot_index >= _sg.gl.max_combined_texture_image_units) { + return; + } + _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[slot_index]; + if ((slot->target != target) || (slot->texture != texture)) { + _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + slot_index)); + /* if the target has changed, clear the previous binding on that target */ + if ((target != slot->target) && (slot->target != 0)) { + glBindTexture(slot->target, 0); + } + /* apply new binding (texture can be 0 to unbind) */ + if (target != 0) { + glBindTexture(target, texture); + } + slot->target = target; + slot->texture = texture; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_store_texture_binding(int slot_index) { + SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); + _sg.gl.cache.stored_texture = _sg.gl.cache.textures[slot_index]; +} + +_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_binding(int slot_index) { + SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); + _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.stored_texture; + if (slot->texture != 0) { + /* we only care restoring valid ids */ + SOKOL_ASSERT(slot->target != 0); + _sg_gl_cache_bind_texture(slot_index, slot->target, slot->texture); + slot->target = 0; + slot->texture = 0; + } +} + +/* called from _sg_gl_destroy_texture() */ +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture(GLuint tex) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[i]; + if (tex == slot->texture) { + _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i)); + glBindTexture(slot->target, 0); + slot->target = 0; + slot->texture = 0; + } + } + if (tex == _sg.gl.cache.stored_texture.texture) { + _sg.gl.cache.stored_texture.target = 0; + _sg.gl.cache.stored_texture.texture = 0; + } +} + +/* called from _sg_gl_destroy_shader() */ +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { + if (prog == _sg.gl.cache.prog) { + _sg.gl.cache.prog = 0; + glUseProgram(0); + } +} + +/* called from _sg_gl_destroy_pipeline() */ +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) { + if (pip == _sg.gl.cache.cur_pipeline) { + _sg.gl.cache.cur_pipeline = 0; + _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { + if (_sg.gl.cur_context) { + _SG_GL_CHECK_ERROR(); +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glBindVertexArray(_sg.gl.cur_context->vao); + _SG_GL_CHECK_ERROR(); + } +#endif + memset(&_sg.gl.cache, 0, sizeof(_sg.gl.cache)); + _sg_gl_cache_clear_buffer_bindings(true); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_clear_texture_bindings(true); + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < _sg.limits.max_vertex_attrs; i++) { + _sg_gl_attr_t* attr = &_sg.gl.cache.attrs[i].gl_attr; + attr->vb_index = -1; + attr->divisor = -1; + glDisableVertexAttribArray((GLuint)i); + _SG_GL_CHECK_ERROR(); + } + _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; + + /* shader program */ + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); + _SG_GL_CHECK_ERROR(); + + /* depth and stencil state */ + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.pass_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.back.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.pass_op = SG_STENCILOP_KEEP; + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_FALSE); + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilMask(0); + + /* blend state */ + _sg.gl.cache.blend.src_factor_rgb = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_rgb = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_rgb = SG_BLENDOP_ADD; + _sg.gl.cache.blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_alpha = SG_BLENDOP_ADD; + glDisable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); + + /* standalone state */ + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + _sg.gl.cache.cull_mode = SG_CULLMODE_NONE; + _sg.gl.cache.face_winding = SG_FACEWINDING_CW; + _sg.gl.cache.sample_count = 1; + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_CULL_FACE); + glFrontFace(GL_CW); + glCullFace(GL_BACK); + glEnable(GL_SCISSOR_TEST); + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + glEnable(GL_DITHER); + glDisable(GL_POLYGON_OFFSET_FILL); +#if defined(SOKOL_GLCORE33) + glEnable(GL_MULTISAMPLE); + glEnable(GL_PROGRAM_POINT_SIZE); +#endif + } +} + +_SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { + /* assumes that _sg.gl is already zero-initialized */ + _sg.gl.valid = true; +#if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + _sg.gl.gles2 = desc->context.gl.force_gles2; +#else + _SOKOL_UNUSED(desc); + _sg.gl.gles2 = false; +#endif + +#if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_load_opengl(); +#endif + + /* clear initial GL error state */ +#if defined(SOKOL_DEBUG) + while (glGetError() != GL_NO_ERROR); +#endif +#if defined(SOKOL_GLCORE33) + _sg_gl_init_caps_glcore33(); +#elif defined(SOKOL_GLES3) + if (_sg.gl.gles2) { + _sg_gl_init_caps_gles2(); + } + else { + _sg_gl_init_caps_gles3(); + } +#else + _sg_gl_init_caps_gles2(); +#endif +} + +_SOKOL_PRIVATE void _sg_gl_discard_backend(void) { + SOKOL_ASSERT(_sg.gl.valid); + _sg.gl.valid = false; +#if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_unload_opengl(); +#endif +} + +_SOKOL_PRIVATE void _sg_gl_activate_context(_sg_context_t* ctx) { + SOKOL_ASSERT(_sg.gl.valid); + /* NOTE: ctx can be 0 to unset the current context */ + _sg.gl.cur_context = ctx; + _sg_gl_reset_state_cache(); +} + +/*-- GL backend resource creation and destruction ----------------------------*/ +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + SOKOL_ASSERT(0 == ctx->default_framebuffer); + _SG_GL_CHECK_ERROR(); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&ctx->default_framebuffer); + _SG_GL_CHECK_ERROR(); +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + SOKOL_ASSERT(0 == ctx->vao); + glGenVertexArrays(1, &ctx->vao); + glBindVertexArray(ctx->vao); + _SG_GL_CHECK_ERROR(); + } +#endif + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + if (ctx->vao) { + glDeleteVertexArrays(1, &ctx->vao); + } + _SG_GL_CHECK_ERROR(); + } +#else + _SOKOL_UNUSED(ctx); +#endif +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _SG_GL_CHECK_ERROR(); + _sg_buffer_common_init(&buf->cmn, desc); + buf->gl.ext_buffers = (0 != desc->gl_buffers[0]); + GLenum gl_target = _sg_gl_buffer_target(buf->cmn.type); + GLenum gl_usage = _sg_gl_usage(buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + GLuint gl_buf = 0; + if (buf->gl.ext_buffers) { + SOKOL_ASSERT(desc->gl_buffers[slot]); + gl_buf = desc->gl_buffers[slot]; + } + else { + glGenBuffers(1, &gl_buf); + SOKOL_ASSERT(gl_buf); + _sg_gl_cache_store_buffer_binding(gl_target); + _sg_gl_cache_bind_buffer(gl_target, gl_buf); + glBufferData(gl_target, buf->cmn.size, 0, gl_usage); + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + glBufferSubData(gl_target, 0, buf->cmn.size, desc->data.ptr); + } + _sg_gl_cache_restore_buffer_binding(gl_target); + } + buf->gl.buf[slot] = gl_buf; + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SG_GL_CHECK_ERROR(); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + if (buf->gl.buf[slot]) { + _sg_gl_cache_invalidate_buffer(buf->gl.buf[slot]); + if (!buf->gl.ext_buffers) { + glDeleteBuffers(1, &buf->gl.buf[slot]); + } + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].sample; +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _SG_GL_CHECK_ERROR(); + _sg_image_common_init(&img->cmn, desc); + img->gl.ext_textures = (0 != desc->gl_textures[0]); + + /* check if texture format is support */ + if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) { + SOKOL_LOG("texture format not supported by GL context\n"); + return SG_RESOURCESTATE_FAILED; + } + /* check for optional texture types */ + if ((img->cmn.type == SG_IMAGETYPE_3D) && !_sg.features.imagetype_3d) { + SOKOL_LOG("3D textures not supported by GL context\n"); + return SG_RESOURCESTATE_FAILED; + } + if ((img->cmn.type == SG_IMAGETYPE_ARRAY) && !_sg.features.imagetype_array) { + SOKOL_LOG("array textures not supported by GL context\n"); + return SG_RESOURCESTATE_FAILED; + } + +#if !defined(SOKOL_GLES2) + bool msaa = false; + if (!_sg.gl.gles2) { + msaa = (img->cmn.sample_count > 1) && (_sg.features.msaa_render_targets); + } +#endif + + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { + /* special case depth-stencil-buffer? */ + SOKOL_ASSERT((img->cmn.usage == SG_USAGE_IMMUTABLE) && (img->cmn.num_slots == 1)); + SOKOL_ASSERT(!img->gl.ext_textures); /* cannot provide external texture for depth images */ + glGenRenderbuffers(1, &img->gl.depth_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl.depth_render_buffer); + GLenum gl_depth_format = _sg_gl_depth_attachment_format(img->cmn.pixel_format); +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && msaa) { + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_depth_format, img->cmn.width, img->cmn.height); + } + else +#endif + { + glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, img->cmn.width, img->cmn.height); + } + } + else { + /* regular color texture */ + img->gl.target = _sg_gl_texture_target(img->cmn.type); + const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); + + /* if this is a MSAA render target, need to create a separate render buffer */ +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && img->cmn.render_target && msaa) { + glGenRenderbuffers(1, &img->gl.msaa_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl.msaa_render_buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height); + } +#endif + + if (img->gl.ext_textures) { + /* inject externally GL textures */ + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + SOKOL_ASSERT(desc->gl_textures[slot]); + img->gl.tex[slot] = desc->gl_textures[slot]; + } + if (desc->gl_texture_target) { + img->gl.target = (GLenum)desc->gl_texture_target; + } + } + else { + /* create our own GL texture(s) */ + const GLenum gl_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const bool is_compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + glGenTextures(1, &img->gl.tex[slot]); + SOKOL_ASSERT(img->gl.tex[slot]); + _sg_gl_cache_store_texture_binding(0); + _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[slot]); + GLenum gl_min_filter = _sg_gl_filter(img->cmn.min_filter); + GLenum gl_mag_filter = _sg_gl_filter(img->cmn.mag_filter); + glTexParameteri(img->gl.target, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter); + glTexParameteri(img->gl.target, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter); + if (_sg.gl.ext_anisotropic && (img->cmn.max_anisotropy > 1)) { + GLint max_aniso = (GLint) img->cmn.max_anisotropy; + if (max_aniso > _sg.gl.max_anisotropy) { + max_aniso = _sg.gl.max_anisotropy; + } + glTexParameteri(img->gl.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); + } + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + else { + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(img->cmn.wrap_u)); + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(img->cmn.wrap_v)); +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && (img->cmn.type == SG_IMAGETYPE_3D)) { + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(img->cmn.wrap_w)); + } +#endif +#if defined(SOKOL_GLCORE33) + float border[4]; + switch (img->cmn.border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 0.0f; + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f; + break; + default: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; + break; + } + glTexParameterfv(img->gl.target, GL_TEXTURE_BORDER_COLOR, border); +#endif + } +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + /* GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 */ + const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); + const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f); + glTexParameterf(img->gl.target, GL_TEXTURE_MIN_LOD, min_lod); + glTexParameterf(img->gl.target, GL_TEXTURE_MAX_LOD, max_lod); + } +#endif + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + int data_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + gl_img_target = _sg_gl_cubeface_target(face_index); + } + const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr; + int mip_width = img->cmn.width >> mip_index; + if (mip_width == 0) { + mip_width = 1; + } + int mip_height = img->cmn.height >> mip_index; + if (mip_height == 0) { + mip_height = 1; + } + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, 0, data_size, data_ptr); + } + else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, 0, gl_format, gl_type, data_ptr); + } + } +#if !defined(SOKOL_GLES2) + else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { + int mip_depth = img->cmn.num_slices; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mip_depth >>= mip_index; + } + if (mip_depth == 0) { + mip_depth = 1; + } + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, mip_depth, 0, data_size, data_ptr); + } + else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + } + } +#endif + } + } + _sg_gl_cache_restore_texture_binding(0); + } + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SG_GL_CHECK_ERROR(); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + if (img->gl.tex[slot]) { + _sg_gl_cache_invalidate_texture(img->gl.tex[slot]); + if (!img->gl.ext_textures) { + glDeleteTextures(1, &img->gl.tex[slot]); + } + } + } + if (img->gl.depth_render_buffer) { + glDeleteRenderbuffers(1, &img->gl.depth_render_buffer); + } + if (img->gl.msaa_render_buffer) { + glDeleteRenderbuffers(1, &img->gl.msaa_render_buffer); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) { + SOKOL_ASSERT(src); + _SG_GL_CHECK_ERROR(); + GLuint gl_shd = glCreateShader(_sg_gl_shader_stage(stage)); + glShaderSource(gl_shd, 1, &src, 0); + glCompileShader(gl_shd); + GLint compile_status = 0; + glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status); + if (!compile_status) { + /* compilation failed, log error and delete shader */ + GLint log_len = 0; + glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) SOKOL_MALLOC((size_t)log_len); + glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf); + SOKOL_LOG(log_buf); + SOKOL_FREE(log_buf); + } + glDeleteShader(gl_shd); + gl_shd = 0; + } + _SG_GL_CHECK_ERROR(); + return gl_shd; +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->gl.prog); + _SG_GL_CHECK_ERROR(); + + _sg_shader_common_init(&shd->cmn, desc); + + /* copy vertex attribute names over, these are required for GLES2, and optional for GLES3 and GL3.x */ + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_strcpy(&shd->gl.attrs[i].name, desc->attrs[i].name); + } + + GLuint gl_vs = _sg_gl_compile_shader(SG_SHADERSTAGE_VS, desc->vs.source); + GLuint gl_fs = _sg_gl_compile_shader(SG_SHADERSTAGE_FS, desc->fs.source); + if (!(gl_vs && gl_fs)) { + return SG_RESOURCESTATE_FAILED; + } + GLuint gl_prog = glCreateProgram(); + glAttachShader(gl_prog, gl_vs); + glAttachShader(gl_prog, gl_fs); + glLinkProgram(gl_prog); + glDeleteShader(gl_vs); + glDeleteShader(gl_fs); + _SG_GL_CHECK_ERROR(); + + GLint link_status; + glGetProgramiv(gl_prog, GL_LINK_STATUS, &link_status); + if (!link_status) { + GLint log_len = 0; + glGetProgramiv(gl_prog, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) SOKOL_MALLOC((size_t)log_len); + glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf); + SOKOL_LOG(log_buf); + SOKOL_FREE(log_buf); + } + glDeleteProgram(gl_prog); + return SG_RESOURCESTATE_FAILED; + } + shd->gl.prog = gl_prog; + + /* resolve uniforms */ + _SG_GL_CHECK_ERROR(); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; + for (int ub_index = 0; ub_index < shd->cmn.stage[stage_index].num_uniform_blocks; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + SOKOL_ASSERT(ub_desc->size > 0); + _sg_gl_uniform_block_t* ub = &gl_stage->uniform_blocks[ub_index]; + SOKOL_ASSERT(ub->num_uniforms == 0); + uint32_t cur_uniform_offset = 0; + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type == SG_UNIFORMTYPE_INVALID) { + break; + } + const uint32_t u_align = _sg_uniform_alignment(u_desc->type, u_desc->array_count, ub_desc->layout); + const uint32_t u_size = _sg_uniform_size(u_desc->type, u_desc->array_count, ub_desc->layout); + cur_uniform_offset = _sg_align_u32(cur_uniform_offset, u_align); + _sg_gl_uniform_t* u = &ub->uniforms[u_index]; + u->type = u_desc->type; + u->count = (uint16_t) u_desc->array_count; + u->offset = (uint16_t) cur_uniform_offset; + cur_uniform_offset += u_size; + if (u_desc->name) { + u->gl_loc = glGetUniformLocation(gl_prog, u_desc->name); + } + else { + u->gl_loc = u_index; + } + ub->num_uniforms++; + } + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + cur_uniform_offset = _sg_align_u32(cur_uniform_offset, 16); + } + SOKOL_ASSERT(ub_desc->size == (size_t)cur_uniform_offset); + _SOKOL_UNUSED(cur_uniform_offset); + } + } + + /* resolve image locations */ + _SG_GL_CHECK_ERROR(); + GLuint cur_prog = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&cur_prog); + glUseProgram(gl_prog); + int gl_tex_slot = 0; + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; + for (int img_index = 0; img_index < shd->cmn.stage[stage_index].num_images; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + SOKOL_ASSERT(img_desc->image_type != _SG_IMAGETYPE_DEFAULT); + _sg_gl_shader_image_t* gl_img = &gl_stage->images[img_index]; + GLint gl_loc = img_index; + if (img_desc->name) { + gl_loc = glGetUniformLocation(gl_prog, img_desc->name); + } + if (gl_loc != -1) { + gl_img->gl_tex_slot = gl_tex_slot++; + glUniform1i(gl_loc, gl_img->gl_tex_slot); + } + else { + gl_img->gl_tex_slot = -1; + } + } + } + /* it's legal to call glUseProgram with 0 */ + glUseProgram(cur_prog); + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SG_GL_CHECK_ERROR(); + if (shd->gl.prog) { + _sg_gl_cache_invalidate_program(shd->gl.prog); + glDeleteProgram(shd->gl.prog); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(!pip->shader && pip->cmn.shader_id.id == SG_INVALID_ID); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->gl.prog); + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + pip->gl.primitive_type = desc->primitive_type; + pip->gl.depth = desc->depth; + pip->gl.stencil = desc->stencil; + // FIXME: blend color and write mask per draw-buffer-attachment (requires GL4) + pip->gl.blend = desc->colors[0].blend; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + pip->gl.color_write_mask[i] = desc->colors[i].write_mask; + } + pip->gl.cull_mode = desc->cull_mode; + pip->gl.face_winding = desc->face_winding; + pip->gl.sample_count = desc->sample_count; + pip->gl.alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; + + /* resolve vertex attributes */ + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + pip->gl.attrs[attr_index].vb_index = -1; + } + for (int attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; + const sg_vertex_step step_func = l_desc->step_func; + const int step_rate = l_desc->step_rate; + GLint attr_loc = attr_index; + if (!_sg_strempty(&shd->gl.attrs[attr_index].name)) { + attr_loc = glGetAttribLocation(pip->shader->gl.prog, _sg_strptr(&shd->gl.attrs[attr_index].name)); + } + SOKOL_ASSERT(attr_loc < (GLint)_sg.limits.max_vertex_attrs); + if (attr_loc != -1) { + _sg_gl_attr_t* gl_attr = &pip->gl.attrs[attr_loc]; + SOKOL_ASSERT(gl_attr->vb_index == -1); + gl_attr->vb_index = (int8_t) a_desc->buffer_index; + if (step_func == SG_VERTEXSTEP_PER_VERTEX) { + gl_attr->divisor = 0; + } + else { + gl_attr->divisor = (int8_t) step_rate; + pip->cmn.use_instanced_draw = true; + } + SOKOL_ASSERT(l_desc->stride > 0); + gl_attr->stride = (uint8_t) l_desc->stride; + gl_attr->offset = a_desc->offset; + gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_desc->format); + gl_attr->type = _sg_gl_vertexformat_type(a_desc->format); + gl_attr->normalized = _sg_gl_vertexformat_normalized(a_desc->format); + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + } + else { + SOKOL_LOG("Vertex attribute not found in shader: "); + SOKOL_LOG(_sg_strptr(&shd->gl.attrs[attr_index].name)); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_gl_cache_invalidate_pipeline(pip); +} + +/* + _sg_create_pass + + att_imgs must point to a _sg_image* att_imgs[SG_MAX_COLOR_ATTACHMENTS+1] array, + first entries are the color attachment images (or nullptr), last entry + is the depth-stencil image (or nullptr). +*/ +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && att_images && desc); + SOKOL_ASSERT(att_images && att_images[0]); + _SG_GL_CHECK_ERROR(); + + _sg_pass_common_init(&pass->cmn, desc); + + /* copy image pointers */ + const sg_pass_attachment_desc* att_desc; + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + att_desc = &desc->color_attachments[i]; + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->gl.color_atts[i].image); + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); + pass->gl.color_atts[i].image = att_images[i]; + } + SOKOL_ASSERT(0 == pass->gl.ds_att.image); + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + pass->gl.ds_att.image = att_images[ds_img_index]; + } + + /* store current framebuffer binding (restored at end of function) */ + GLuint gl_orig_fb; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); + + /* create a framebuffer object */ + glGenFramebuffers(1, &pass->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); + + /* attach msaa render buffer or textures */ + const bool is_msaa = (0 != att_images[0]->gl.msaa_render_buffer); + if (is_msaa) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_image_t* att_img = pass->gl.color_atts[i].image; + if (att_img) { + const GLuint gl_render_buffer = att_img->gl.msaa_render_buffer; + SOKOL_ASSERT(gl_render_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, (GLenum)(GL_COLOR_ATTACHMENT0+i), GL_RENDERBUFFER, gl_render_buffer); + } + } + } + else { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_image_t* att_img = pass->gl.color_atts[i].image; + const int mip_level = pass->cmn.color_atts[i].mip_level; + const int slice = pass->cmn.color_atts[i].slice; + if (att_img) { + const GLuint gl_tex = att_img->gl.tex[0]; + SOKOL_ASSERT(gl_tex); + const GLenum gl_att = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + switch (att_img->cmn.type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, GL_TEXTURE_2D, gl_tex, mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, _sg_gl_cubeface_target(slice), gl_tex, mip_level); + break; + default: + /* 3D- or array-texture */ +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att, gl_tex, mip_level, slice); + } +#endif + break; + } + } + } + } + + /* attach depth-stencil buffer to framebuffer */ + if (pass->gl.ds_att.image) { + const GLuint gl_render_buffer = pass->gl.ds_att.image->gl.depth_render_buffer; + SOKOL_ASSERT(gl_render_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); + if (_sg_is_depth_stencil_format(pass->gl.ds_att.image->cmn.pixel_format)) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); + } + } + + /* check if framebuffer is complete */ + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + SOKOL_LOG("Framebuffer completeness check failed!\n"); + return SG_RESOURCESTATE_FAILED; + } + + /* setup color attachments for the framebuffer */ +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + GLenum att[SG_MAX_COLOR_ATTACHMENTS] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3 + }; + glDrawBuffers(pass->cmn.num_color_atts, att); + } +#endif + + /* create MSAA resolve framebuffers if necessary */ + if (is_msaa) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[i]; + _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; + if (gl_att->image) { + SOKOL_ASSERT(0 == gl_att->gl_msaa_resolve_buffer); + glGenFramebuffers(1, &gl_att->gl_msaa_resolve_buffer); + glBindFramebuffer(GL_FRAMEBUFFER, gl_att->gl_msaa_resolve_buffer); + const GLuint gl_tex = gl_att->image->gl.tex[0]; + SOKOL_ASSERT(gl_tex); + switch (gl_att->image->cmn.type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, gl_tex, cmn_att->mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + _sg_gl_cubeface_target(cmn_att->slice), gl_tex, cmn_att->mip_level); + break; + default: +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gl_tex, cmn_att->mip_level, cmn_att->slice); + } +#endif + break; + } + /* check if framebuffer is complete */ + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + SOKOL_LOG("Framebuffer completeness check failed (msaa resolve buffer)!\n"); + return SG_RESOURCESTATE_FAILED; + } + /* setup color attachments for the framebuffer */ +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + const GLenum gl_draw_bufs = GL_COLOR_ATTACHMENT0; + glDrawBuffers(1, &gl_draw_bufs); + } +#endif + } + } + } + + /* restore original framebuffer binding */ + glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb); + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + SOKOL_ASSERT(pass != _sg.gl.cur_pass); + _SG_GL_CHECK_ERROR(); + if (0 != pass->gl.fb) { + glDeleteFramebuffers(1, &pass->gl.fb); + } + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (pass->gl.color_atts[i].gl_msaa_resolve_buffer) { + glDeleteFramebuffers(1, &pass->gl.color_atts[i].gl_msaa_resolve_buffer); + } + } + if (pass->gl.ds_att.gl_msaa_resolve_buffer) { + glDeleteFramebuffers(1, &pass->gl.ds_att.gl_msaa_resolve_buffer); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->gl.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->gl.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + /* FIXME: what if a texture used as render target is still bound, should we + unbind all currently bound textures in begin pass? */ + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.gl.in_pass); + _SG_GL_CHECK_ERROR(); + _sg.gl.in_pass = true; + _sg.gl.cur_pass = pass; /* can be 0 */ + if (pass) { + _sg.gl.cur_pass_id.id = pass->slot.id; + } + else { + _sg.gl.cur_pass_id.id = SG_INVALID_ID; + } + _sg.gl.cur_pass_width = w; + _sg.gl.cur_pass_height = h; + + /* number of color attachments */ + const int num_color_atts = pass ? pass->cmn.num_color_atts : 1; + + /* bind the render pass framebuffer */ + if (pass) { + /* offscreen pass */ + SOKOL_ASSERT(pass->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); + } + else { + /* default pass */ + SOKOL_ASSERT(_sg.gl.cur_context); + glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); + } + glViewport(0, 0, w, h); + glScissor(0, 0, w, h); + + /* clear color and depth-stencil attachments if needed */ + bool clear_color = false; + for (int i = 0; i < num_color_atts; i++) { + if (SG_ACTION_CLEAR == action->colors[i].action) { + clear_color = true; + break; + } + } + const bool clear_depth = (action->depth.action == SG_ACTION_CLEAR); + const bool clear_stencil = (action->stencil.action == SG_ACTION_CLEAR); + + bool need_pip_cache_flush = false; + if (clear_color) { + bool need_color_mask_flush = false; + // NOTE: not a bug to iterate over all possible color attachments + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (SG_COLORMASK_RGBA != _sg.gl.cache.color_write_mask[i]) { + need_pip_cache_flush = true; + need_color_mask_flush = true; + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + } + if (need_color_mask_flush) { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + } + if (clear_depth) { + if (!_sg.gl.cache.depth.write_enabled) { + need_pip_cache_flush = true; + _sg.gl.cache.depth.write_enabled = true; + glDepthMask(GL_TRUE); + } + if (_sg.gl.cache.depth.compare != SG_COMPAREFUNC_ALWAYS) { + need_pip_cache_flush = true; + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + glDepthFunc(GL_ALWAYS); + } + } + if (clear_stencil) { + if (_sg.gl.cache.stencil.write_mask != 0xFF) { + need_pip_cache_flush = true; + _sg.gl.cache.stencil.write_mask = 0xFF; + glStencilMask(0xFF); + } + } + if (need_pip_cache_flush) { + /* we messed with the state cache directly, need to clear cached + pipeline to force re-evaluation in next sg_apply_pipeline() */ + _sg.gl.cache.cur_pipeline = 0; + _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; + } + bool use_mrt_clear = (0 != pass); +#if defined(SOKOL_GLES2) + use_mrt_clear = false; +#else + if (_sg.gl.gles2) { + use_mrt_clear = false; + } +#endif + if (!use_mrt_clear) { + GLbitfield clear_mask = 0; + if (clear_color) { + clear_mask |= GL_COLOR_BUFFER_BIT; + const sg_color c = action->colors[0].value; + glClearColor(c.r, c.g, c.b, c.a); + } + if (clear_depth) { + clear_mask |= GL_DEPTH_BUFFER_BIT; +#ifdef SOKOL_GLCORE33 + glClearDepth(action->depth.value); +#else + glClearDepthf(action->depth.value); +#endif + } + if (clear_stencil) { + clear_mask |= GL_STENCIL_BUFFER_BIT; + glClearStencil(action->stencil.value); + } + if (0 != clear_mask) { + glClear(clear_mask); + } + } +#if !defined SOKOL_GLES2 + else { + SOKOL_ASSERT(pass); + for (int i = 0; i < num_color_atts; i++) { + if (action->colors[i].action == SG_ACTION_CLEAR) { + glClearBufferfv(GL_COLOR, i, &action->colors[i].value.r); + } + } + if (pass->gl.ds_att.image) { + if (clear_depth && clear_stencil) { + glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.value, action->stencil.value); + } + else if (clear_depth) { + glClearBufferfv(GL_DEPTH, 0, &action->depth.value); + } + else if (clear_stencil) { + GLint val = (GLint) action->stencil.value; + glClearBufferiv(GL_STENCIL, 0, &val); + } + } + } +#endif + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_end_pass(void) { + SOKOL_ASSERT(_sg.gl.in_pass); + _SG_GL_CHECK_ERROR(); + + /* if this was an offscreen pass, and MSAA rendering was used, need + to resolve into the pass images */ +#if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && _sg.gl.cur_pass) { + /* check if the pass object is still valid */ + const _sg_pass_t* pass = _sg.gl.cur_pass; + SOKOL_ASSERT(pass->slot.id == _sg.gl.cur_pass_id.id); + bool is_msaa = (0 != _sg.gl.cur_pass->gl.color_atts[0].gl_msaa_resolve_buffer); + if (is_msaa) { + SOKOL_ASSERT(pass->gl.fb); + glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->gl.fb); + SOKOL_ASSERT(pass->gl.color_atts[0].image); + const int w = pass->gl.color_atts[0].image->cmn.width; + const int h = pass->gl.color_atts[0].image->cmn.height; + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + const _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[att_index]; + if (gl_att->image) { + SOKOL_ASSERT(gl_att->gl_msaa_resolve_buffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gl_att->gl_msaa_resolve_buffer); + glReadBuffer((GLenum)(GL_COLOR_ATTACHMENT0 + att_index)); + glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + else { + break; + } + } + } + } +#endif + _sg.gl.cur_pass = 0; + _sg.gl.cur_pass_id.id = SG_INVALID_ID; + _sg.gl.cur_pass_width = 0; + _sg.gl.cur_pass_height = 0; + + SOKOL_ASSERT(_sg.gl.cur_context); + glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); + _sg.gl.in_pass = false; + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.gl.in_pass); + y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; + glViewport(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_gl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.gl.in_pass); + y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; + glScissor(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + _SG_GL_CHECK_ERROR(); + if ((_sg.gl.cache.cur_pipeline != pip) || (_sg.gl.cache.cur_pipeline_id.id != pip->slot.id)) { + _sg.gl.cache.cur_pipeline = pip; + _sg.gl.cache.cur_pipeline_id.id = pip->slot.id; + _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->gl.primitive_type); + _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->cmn.index_type); + + /* update depth state */ + { + const sg_depth_state* state_ds = &pip->gl.depth; + sg_depth_state* cache_ds = &_sg.gl.cache.depth; + if (state_ds->compare != cache_ds->compare) { + cache_ds->compare = state_ds->compare; + glDepthFunc(_sg_gl_compare_func(state_ds->compare)); + } + if (state_ds->write_enabled != cache_ds->write_enabled) { + cache_ds->write_enabled = state_ds->write_enabled; + glDepthMask(state_ds->write_enabled); + } + if (!_sg_fequal(state_ds->bias, cache_ds->bias, 0.000001f) || + !_sg_fequal(state_ds->bias_slope_scale, cache_ds->bias_slope_scale, 0.000001f)) + { + /* according to ANGLE's D3D11 backend: + D3D11 SlopeScaledDepthBias ==> GL polygonOffsetFactor + D3D11 DepthBias ==> GL polygonOffsetUnits + DepthBiasClamp has no meaning on GL + */ + cache_ds->bias = state_ds->bias; + cache_ds->bias_slope_scale = state_ds->bias_slope_scale; + glPolygonOffset(state_ds->bias_slope_scale, state_ds->bias); + bool po_enabled = true; + if (_sg_fequal(state_ds->bias, 0.0f, 0.000001f) && + _sg_fequal(state_ds->bias_slope_scale, 0.0f, 0.000001f)) + { + po_enabled = false; + } + if (po_enabled != _sg.gl.cache.polygon_offset_enabled) { + _sg.gl.cache.polygon_offset_enabled = po_enabled; + if (po_enabled) { + glEnable(GL_POLYGON_OFFSET_FILL); + } + else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + } + } + } + + /* update stencil state */ + { + const sg_stencil_state* state_ss = &pip->gl.stencil; + sg_stencil_state* cache_ss = &_sg.gl.cache.stencil; + if (state_ss->enabled != cache_ss->enabled) { + cache_ss->enabled = state_ss->enabled; + if (state_ss->enabled) { + glEnable(GL_STENCIL_TEST); + } + else { + glDisable(GL_STENCIL_TEST); + } + } + if (state_ss->write_mask != cache_ss->write_mask) { + cache_ss->write_mask = state_ss->write_mask; + glStencilMask(state_ss->write_mask); + } + for (int i = 0; i < 2; i++) { + const sg_stencil_face_state* state_sfs = (i==0)? &state_ss->front : &state_ss->back; + sg_stencil_face_state* cache_sfs = (i==0)? &cache_ss->front : &cache_ss->back; + GLenum gl_face = (i==0)? GL_FRONT : GL_BACK; + if ((state_sfs->compare != cache_sfs->compare) || + (state_ss->read_mask != cache_ss->read_mask) || + (state_ss->ref != cache_ss->ref)) + { + cache_sfs->compare = state_sfs->compare; + glStencilFuncSeparate(gl_face, + _sg_gl_compare_func(state_sfs->compare), + state_ss->ref, + state_ss->read_mask); + } + if ((state_sfs->fail_op != cache_sfs->fail_op) || + (state_sfs->depth_fail_op != cache_sfs->depth_fail_op) || + (state_sfs->pass_op != cache_sfs->pass_op)) + { + cache_sfs->fail_op = state_sfs->fail_op; + cache_sfs->depth_fail_op = state_sfs->depth_fail_op; + cache_sfs->pass_op = state_sfs->pass_op; + glStencilOpSeparate(gl_face, + _sg_gl_stencil_op(state_sfs->fail_op), + _sg_gl_stencil_op(state_sfs->depth_fail_op), + _sg_gl_stencil_op(state_sfs->pass_op)); + } + } + cache_ss->read_mask = state_ss->read_mask; + cache_ss->ref = state_ss->ref; + } + + /* update blend state + FIXME: separate blend state per color attachment not support, needs GL4 + */ + { + const sg_blend_state* state_bs = &pip->gl.blend; + sg_blend_state* cache_bs = &_sg.gl.cache.blend; + if (state_bs->enabled != cache_bs->enabled) { + cache_bs->enabled = state_bs->enabled; + if (state_bs->enabled) { + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } + } + if ((state_bs->src_factor_rgb != cache_bs->src_factor_rgb) || + (state_bs->dst_factor_rgb != cache_bs->dst_factor_rgb) || + (state_bs->src_factor_alpha != cache_bs->src_factor_alpha) || + (state_bs->dst_factor_alpha != cache_bs->dst_factor_alpha)) + { + cache_bs->src_factor_rgb = state_bs->src_factor_rgb; + cache_bs->dst_factor_rgb = state_bs->dst_factor_rgb; + cache_bs->src_factor_alpha = state_bs->src_factor_alpha; + cache_bs->dst_factor_alpha = state_bs->dst_factor_alpha; + glBlendFuncSeparate(_sg_gl_blend_factor(state_bs->src_factor_rgb), + _sg_gl_blend_factor(state_bs->dst_factor_rgb), + _sg_gl_blend_factor(state_bs->src_factor_alpha), + _sg_gl_blend_factor(state_bs->dst_factor_alpha)); + } + if ((state_bs->op_rgb != cache_bs->op_rgb) || (state_bs->op_alpha != cache_bs->op_alpha)) { + cache_bs->op_rgb = state_bs->op_rgb; + cache_bs->op_alpha = state_bs->op_alpha; + glBlendEquationSeparate(_sg_gl_blend_op(state_bs->op_rgb), _sg_gl_blend_op(state_bs->op_alpha)); + } + } + + /* standalone state */ + for (GLuint i = 0; i < (GLuint)pip->cmn.color_attachment_count; i++) { + if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) { + const sg_color_mask cm = pip->gl.color_write_mask[i]; + _sg.gl.cache.color_write_mask[i] = cm; +#ifdef SOKOL_GLCORE33 + glColorMaski(i, + (cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); +#else + if (0 == i) { + glColorMask((cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + } +#endif + } + } + + if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f)) + { + sg_color c = pip->cmn.blend_color; + _sg.gl.cache.blend_color = c; + glBlendColor(c.r, c.g, c.b, c.a); + } + if (pip->gl.cull_mode != _sg.gl.cache.cull_mode) { + _sg.gl.cache.cull_mode = pip->gl.cull_mode; + if (SG_CULLMODE_NONE == pip->gl.cull_mode) { + glDisable(GL_CULL_FACE); + } + else { + glEnable(GL_CULL_FACE); + GLenum gl_mode = (SG_CULLMODE_FRONT == pip->gl.cull_mode) ? GL_FRONT : GL_BACK; + glCullFace(gl_mode); + } + } + if (pip->gl.face_winding != _sg.gl.cache.face_winding) { + _sg.gl.cache.face_winding = pip->gl.face_winding; + GLenum gl_winding = (SG_FACEWINDING_CW == pip->gl.face_winding) ? GL_CW : GL_CCW; + glFrontFace(gl_winding); + } + if (pip->gl.alpha_to_coverage_enabled != _sg.gl.cache.alpha_to_coverage_enabled) { + _sg.gl.cache.alpha_to_coverage_enabled = pip->gl.alpha_to_coverage_enabled; + if (pip->gl.alpha_to_coverage_enabled) { + glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + else { + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + } +#ifdef SOKOL_GLCORE33 + if (pip->gl.sample_count != _sg.gl.cache.sample_count) { + _sg.gl.cache.sample_count = pip->gl.sample_count; + if (pip->gl.sample_count > 1) { + glEnable(GL_MULTISAMPLE); + } + else { + glDisable(GL_MULTISAMPLE); + } + } +#endif + + /* bind shader program */ + if (pip->shader->gl.prog != _sg.gl.cache.prog) { + _sg.gl.cache.prog = pip->shader->gl.prog; + glUseProgram(pip->shader->gl.prog); + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(num_fs_imgs); + _SOKOL_UNUSED(num_vs_imgs); + _SOKOL_UNUSED(num_vbs); + _SG_GL_CHECK_ERROR(); + + /* bind textures */ + _SG_GL_CHECK_ERROR(); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; + const _sg_gl_shader_stage_t* gl_stage = &pip->shader->gl.stage[stage_index]; + _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS)? vs_imgs : fs_imgs; + SOKOL_ASSERT(((stage_index == SG_SHADERSTAGE_VS)? num_vs_imgs : num_fs_imgs) == stage->num_images); + for (int img_index = 0; img_index < stage->num_images; img_index++) { + const _sg_gl_shader_image_t* gl_shd_img = &gl_stage->images[img_index]; + if (gl_shd_img->gl_tex_slot != -1) { + _sg_image_t* img = imgs[img_index]; + const GLuint gl_tex = img->gl.tex[img->cmn.active_slot]; + SOKOL_ASSERT(img && img->gl.target); + SOKOL_ASSERT((gl_shd_img->gl_tex_slot != -1) && gl_tex); + _sg_gl_cache_bind_texture(gl_shd_img->gl_tex_slot, img->gl.target, gl_tex); + } + } + } + _SG_GL_CHECK_ERROR(); + + /* index buffer (can be 0) */ + const GLuint gl_ib = ib ? ib->gl.buf[ib->cmn.active_slot] : 0; + _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); + _sg.gl.cache.cur_ib_offset = ib_offset; + + /* vertex attributes */ + for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_attrs; attr_index++) { + _sg_gl_attr_t* attr = &pip->gl.attrs[attr_index]; + _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index]; + bool cache_attr_dirty = false; + int vb_offset = 0; + GLuint gl_vb = 0; + if (attr->vb_index >= 0) { + /* attribute is enabled */ + SOKOL_ASSERT(attr->vb_index < num_vbs); + _sg_buffer_t* vb = vbs[attr->vb_index]; + SOKOL_ASSERT(vb); + gl_vb = vb->gl.buf[vb->cmn.active_slot]; + vb_offset = vb_offsets[attr->vb_index] + attr->offset; + if ((gl_vb != cache_attr->gl_vbuf) || + (attr->size != cache_attr->gl_attr.size) || + (attr->type != cache_attr->gl_attr.type) || + (attr->normalized != cache_attr->gl_attr.normalized) || + (attr->stride != cache_attr->gl_attr.stride) || + (vb_offset != cache_attr->gl_attr.offset) || + (cache_attr->gl_attr.divisor != attr->divisor)) + { + _sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb); + glVertexAttribPointer(attr_index, attr->size, attr->type, + attr->normalized, attr->stride, + (const GLvoid*)(GLintptr)vb_offset); +#if defined(_SOKOL_GL_INSTANCING_ENABLED) + if (_sg.features.instancing) { + glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); + } +#endif + cache_attr_dirty = true; + } + if (cache_attr->gl_attr.vb_index == -1) { + glEnableVertexAttribArray(attr_index); + cache_attr_dirty = true; + } + } + else { + /* attribute is disabled */ + if (cache_attr->gl_attr.vb_index != -1) { + glDisableVertexAttribArray(attr_index); + cache_attr_dirty = true; + } + } + if (cache_attr_dirty) { + cache_attr->gl_attr = *attr; + cache_attr->gl_attr.offset = vb_offset; + cache_attr->gl_vbuf = gl_vb; + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->slot.id == _sg.gl.cache.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->slot.id == _sg.gl.cache.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks > ub_index); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size == data->size); + const _sg_gl_shader_stage_t* gl_stage = &_sg.gl.cache.cur_pipeline->shader->gl.stage[stage_index]; + const _sg_gl_uniform_block_t* gl_ub = &gl_stage->uniform_blocks[ub_index]; + for (int u_index = 0; u_index < gl_ub->num_uniforms; u_index++) { + const _sg_gl_uniform_t* u = &gl_ub->uniforms[u_index]; + SOKOL_ASSERT(u->type != SG_UNIFORMTYPE_INVALID); + if (u->gl_loc == -1) { + continue; + } + GLfloat* fptr = (GLfloat*) (((uint8_t*)data->ptr) + u->offset); + GLint* iptr = (GLint*) (((uint8_t*)data->ptr) + u->offset); + switch (u->type) { + case SG_UNIFORMTYPE_INVALID: + break; + case SG_UNIFORMTYPE_FLOAT: + glUniform1fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT2: + glUniform2fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT3: + glUniform3fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT4: + glUniform4fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_INT: + glUniform1iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT2: + glUniform2iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT3: + glUniform3iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT4: + glUniform4iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_MAT4: + glUniformMatrix4fv(u->gl_loc, u->count, GL_FALSE, fptr); + break; + default: + SOKOL_UNREACHABLE; + break; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); + const GLenum i_type = _sg.gl.cache.cur_index_type; + const GLenum p_type = _sg.gl.cache.cur_primitive_type; + if (0 != i_type) { + /* indexed rendering */ + const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; + const int ib_offset = _sg.gl.cache.cur_ib_offset; + const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); + if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { + if (_sg.features.instancing) { + glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); + } + } + else { + glDrawElements(p_type, num_elements, i_type, indices); + } + } + else { + /* non-indexed rendering */ + if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { + if (_sg.features.instancing) { + glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); + } + } + else { + glDrawArrays(p_type, base_element, num_elements); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_commit(void) { + SOKOL_ASSERT(!_sg.gl.in_pass); + /* "soft" clear bindings (only those that are actually bound) */ + _sg_gl_cache_clear_buffer_bindings(false); + _sg_gl_cache_clear_texture_bindings(false); +} + +_SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + /* only one update per buffer per frame allowed */ + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, 0, (GLsizeiptr)data->size, data->ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE int _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, buf->cmn.append_pos, (GLsizeiptr)data->size, data->ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); + _SG_GL_CHECK_ERROR(); + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + return _sg_roundup((int)data->size, 4); +} + +_SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + /* only one update per image per frame allowed */ + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } + SOKOL_ASSERT(img->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]); + _sg_gl_cache_store_texture_binding(0); + _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[img->cmn.active_slot]); + const GLenum gl_img_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const GLenum gl_img_type = _sg_gl_teximage_type(img->cmn.pixel_format); + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + const int num_mips = img->cmn.num_mipmaps; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + gl_img_target = _sg_gl_cubeface_target(face_index); + } + const GLvoid* data_ptr = data->subimage[face_index][mip_index].ptr; + int mip_width = img->cmn.width >> mip_index; + if (mip_width == 0) { + mip_width = 1; + } + int mip_height = img->cmn.height >> mip_index; + if (mip_height == 0) { + mip_height = 1; + } + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + glTexSubImage2D(gl_img_target, mip_index, + 0, 0, + mip_width, mip_height, + gl_img_format, gl_img_type, + data_ptr); + } +#if !defined(SOKOL_GLES2) + else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { + int mip_depth = img->cmn.num_slices >> mip_index; + if (mip_depth == 0) { + mip_depth = 1; + } + glTexSubImage3D(gl_img_target, mip_index, + 0, 0, 0, + mip_width, mip_height, mip_depth, + gl_img_format, gl_img_type, + data_ptr); + + } +#endif + } + } + _sg_gl_cache_restore_texture_binding(0); +} + +/*== D3D11 BACKEND IMPLEMENTATION ============================================*/ +#elif defined(SOKOL_D3D11) + +#if defined(__cplusplus) +#define _sg_d3d11_AddRef(self) (self)->AddRef() +#else +#define _sg_d3d11_AddRef(self) (self)->lpVtbl->AddRef(self) +#endif + +#if defined(__cplusplus) +#define _sg_d3d11_Release(self) (self)->Release() +#else +#define _sg_d3d11_Release(self) (self)->lpVtbl->Release(self) +#endif + +/*-- D3D11 C/C++ wrappers ----------------------------------------------------*/ +static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) { +#if defined(__cplusplus) + return self->CheckFormatSupport(Format, pFormatSupport); +#else + return self->lpVtbl->CheckFormatSupport(self, Format, pFormatSupport); +#endif +} + +static inline void _sg_d3d11_OMSetRenderTargets(ID3D11DeviceContext* self, UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView *pDepthStencilView) { +#if defined(__cplusplus) + self->OMSetRenderTargets(NumViews, ppRenderTargetViews, pDepthStencilView); +#else + self->lpVtbl->OMSetRenderTargets(self, NumViews, ppRenderTargetViews, pDepthStencilView); +#endif +} + +static inline void _sg_d3d11_RSSetState(ID3D11DeviceContext* self, ID3D11RasterizerState* pRasterizerState) { +#if defined(__cplusplus) + self->RSSetState(pRasterizerState); +#else + self->lpVtbl->RSSetState(self, pRasterizerState); +#endif +} + +static inline void _sg_d3d11_OMSetDepthStencilState(ID3D11DeviceContext* self, ID3D11DepthStencilState* pDepthStencilState, UINT StencilRef) { +#if defined(__cplusplus) + self->OMSetDepthStencilState(pDepthStencilState, StencilRef); +#else + self->lpVtbl->OMSetDepthStencilState(self, pDepthStencilState, StencilRef); +#endif +} + +static inline void _sg_d3d11_OMSetBlendState(ID3D11DeviceContext* self, ID3D11BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask) { +#if defined(__cplusplus) + self->OMSetBlendState(pBlendState, BlendFactor, SampleMask); +#else + self->lpVtbl->OMSetBlendState(self, pBlendState, BlendFactor, SampleMask); +#endif +} + +static inline void _sg_d3d11_IASetVertexBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets) { +#if defined(__cplusplus) + self->IASetVertexBuffers(StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); +#else + self->lpVtbl->IASetVertexBuffers(self, StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); +#endif +} + +static inline void _sg_d3d11_IASetIndexBuffer(ID3D11DeviceContext* self, ID3D11Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset) { +#if defined(__cplusplus) + self->IASetIndexBuffer(pIndexBuffer, Format, Offset); +#else + self->lpVtbl->IASetIndexBuffer(self, pIndexBuffer, Format, Offset); +#endif +} + +static inline void _sg_d3d11_IASetInputLayout(ID3D11DeviceContext* self, ID3D11InputLayout* pInputLayout) { +#if defined(__cplusplus) + self->IASetInputLayout(pInputLayout); +#else + self->lpVtbl->IASetInputLayout(self, pInputLayout); +#endif +} + +static inline void _sg_d3d11_VSSetShader(ID3D11DeviceContext* self, ID3D11VertexShader* pVertexShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { +#if defined(__cplusplus) + self->VSSetShader(pVertexShader, ppClassInstances, NumClassInstances); +#else + self->lpVtbl->VSSetShader(self, pVertexShader, ppClassInstances, NumClassInstances); +#endif +} + +static inline void _sg_d3d11_PSSetShader(ID3D11DeviceContext* self, ID3D11PixelShader* pPixelShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { +#if defined(__cplusplus) + self->PSSetShader(pPixelShader, ppClassInstances, NumClassInstances); +#else + self->lpVtbl->PSSetShader(self, pPixelShader, ppClassInstances, NumClassInstances); +#endif +} + +static inline void _sg_d3d11_VSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { +#if defined(__cplusplus) + self->VSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); +#else + self->lpVtbl->VSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); +#endif +} + +static inline void _sg_d3d11_PSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { +#if defined(__cplusplus) + self->PSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); +#else + self->lpVtbl->PSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); +#endif +} + +static inline void _sg_d3d11_VSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { +#if defined(__cplusplus) + self->VSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); +#else + self->lpVtbl->VSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); +#endif +} + +static inline void _sg_d3d11_PSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { +#if defined(__cplusplus) + self->PSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); +#else + self->lpVtbl->PSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); +#endif +} + +static inline void _sg_d3d11_VSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { +#if defined(__cplusplus) + self->VSSetSamplers(StartSlot, NumSamplers, ppSamplers); +#else + self->lpVtbl->VSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); +#endif +} + +static inline void _sg_d3d11_PSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { +#if defined(__cplusplus) + self->PSSetSamplers(StartSlot, NumSamplers, ppSamplers); +#else + self->lpVtbl->PSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); +#endif +} + +static inline HRESULT _sg_d3d11_CreateBuffer(ID3D11Device* self, const D3D11_BUFFER_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Buffer** ppBuffer) { +#if defined(__cplusplus) + return self->CreateBuffer(pDesc, pInitialData, ppBuffer); +#else + return self->lpVtbl->CreateBuffer(self, pDesc, pInitialData, ppBuffer); +#endif +} + +static inline HRESULT _sg_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { +#if defined(__cplusplus) + return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); +#else + return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); +#endif +} + +static inline HRESULT _sg_d3d11_CreateShaderResourceView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRView) { +#if defined(__cplusplus) + return self->CreateShaderResourceView(pResource, pDesc, ppSRView); +#else + return self->lpVtbl->CreateShaderResourceView(self, pResource, pDesc, ppSRView); +#endif +} + +static inline void _sg_d3d11_GetResource(ID3D11View* self, ID3D11Resource** ppResource) { +#if defined(__cplusplus) + self->GetResource(ppResource); +#else + self->lpVtbl->GetResource(self, ppResource); +#endif +} + +static inline HRESULT _sg_d3d11_CreateTexture3D(ID3D11Device* self, const D3D11_TEXTURE3D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D** ppTexture3D) { +#if defined(__cplusplus) + return self->CreateTexture3D(pDesc, pInitialData, ppTexture3D); +#else + return self->lpVtbl->CreateTexture3D(self, pDesc, pInitialData, ppTexture3D); +#endif +} + +static inline HRESULT _sg_d3d11_CreateSamplerState(ID3D11Device* self, const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState) { +#if defined(__cplusplus) + return self->CreateSamplerState(pSamplerDesc, ppSamplerState); +#else + return self->lpVtbl->CreateSamplerState(self, pSamplerDesc, ppSamplerState); +#endif +} + +static inline LPVOID _sg_d3d11_GetBufferPointer(ID3D10Blob* self) { +#if defined(__cplusplus) + return self->GetBufferPointer(); +#else + return self->lpVtbl->GetBufferPointer(self); +#endif +} + +static inline SIZE_T _sg_d3d11_GetBufferSize(ID3D10Blob* self) { +#if defined(__cplusplus) + return self->GetBufferSize(); +#else + return self->lpVtbl->GetBufferSize(self); +#endif +} + +static inline HRESULT _sg_d3d11_CreateVertexShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader) { +#if defined(__cplusplus) + return self->CreateVertexShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); +#else + return self->lpVtbl->CreateVertexShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); +#endif +} + +static inline HRESULT _sg_d3d11_CreatePixelShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader) { +#if defined(__cplusplus) + return self->CreatePixelShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); +#else + return self->lpVtbl->CreatePixelShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); +#endif +} + +static inline HRESULT _sg_d3d11_CreateInputLayout(ID3D11Device* self, const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout **ppInputLayout) { +#if defined(__cplusplus) + return self->CreateInputLayout(pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); +#else + return self->lpVtbl->CreateInputLayout(self, pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); +#endif +} + +static inline HRESULT _sg_d3d11_CreateRasterizerState(ID3D11Device* self, const D3D11_RASTERIZER_DESC* pRasterizerDesc, ID3D11RasterizerState** ppRasterizerState) { +#if defined(__cplusplus) + return self->CreateRasterizerState(pRasterizerDesc, ppRasterizerState); +#else + return self->lpVtbl->CreateRasterizerState(self, pRasterizerDesc, ppRasterizerState); +#endif +} + +static inline HRESULT _sg_d3d11_CreateDepthStencilState(ID3D11Device* self, const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D11DepthStencilState** ppDepthStencilState) { +#if defined(__cplusplus) + return self->CreateDepthStencilState(pDepthStencilDesc, ppDepthStencilState); +#else + return self->lpVtbl->CreateDepthStencilState(self, pDepthStencilDesc, ppDepthStencilState); +#endif +} + +static inline HRESULT _sg_d3d11_CreateBlendState(ID3D11Device* self, const D3D11_BLEND_DESC* pBlendStateDesc, ID3D11BlendState** ppBlendState) { +#if defined(__cplusplus) + return self->CreateBlendState(pBlendStateDesc, ppBlendState); +#else + return self->lpVtbl->CreateBlendState(self, pBlendStateDesc, ppBlendState); +#endif +} + +static inline HRESULT _sg_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { +#if defined(__cplusplus) + return self->CreateRenderTargetView(pResource, pDesc, ppRTView); +#else + return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); +#endif +} + +static inline HRESULT _sg_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { +#if defined(__cplusplus) + return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); +#else + return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); +#endif +} + +static inline void _sg_d3d11_RSSetViewports(ID3D11DeviceContext* self, UINT NumViewports, const D3D11_VIEWPORT* pViewports) { +#if defined(__cplusplus) + self->RSSetViewports(NumViewports, pViewports); +#else + self->lpVtbl->RSSetViewports(self, NumViewports, pViewports); +#endif +} + +static inline void _sg_d3d11_RSSetScissorRects(ID3D11DeviceContext* self, UINT NumRects, const D3D11_RECT* pRects) { +#if defined(__cplusplus) + self->RSSetScissorRects(NumRects, pRects); +#else + self->lpVtbl->RSSetScissorRects(self, NumRects, pRects); +#endif +} + +static inline void _sg_d3d11_ClearRenderTargetView(ID3D11DeviceContext* self, ID3D11RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]) { +#if defined(__cplusplus) + self->ClearRenderTargetView(pRenderTargetView, ColorRGBA); +#else + self->lpVtbl->ClearRenderTargetView(self, pRenderTargetView, ColorRGBA); +#endif +} + +static inline void _sg_d3d11_ClearDepthStencilView(ID3D11DeviceContext* self, ID3D11DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil) { +#if defined(__cplusplus) + self->ClearDepthStencilView(pDepthStencilView, ClearFlags, Depth, Stencil); +#else + self->lpVtbl->ClearDepthStencilView(self, pDepthStencilView, ClearFlags, Depth, Stencil); +#endif +} + +static inline void _sg_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { +#if defined(__cplusplus) + self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); +#else + self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); +#endif +} + +static inline void _sg_d3d11_IASetPrimitiveTopology(ID3D11DeviceContext* self, D3D11_PRIMITIVE_TOPOLOGY Topology) { +#if defined(__cplusplus) + self->IASetPrimitiveTopology(Topology); +#else + self->lpVtbl->IASetPrimitiveTopology(self, Topology); +#endif +} + +static inline void _sg_d3d11_UpdateSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { +#if defined(__cplusplus) + self->UpdateSubresource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); +#else + self->lpVtbl->UpdateSubresource(self, pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); +#endif +} + +static inline void _sg_d3d11_DrawIndexed(ID3D11DeviceContext* self, UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation) { +#if defined(__cplusplus) + self->DrawIndexed(IndexCount, StartIndexLocation, BaseVertexLocation); +#else + self->lpVtbl->DrawIndexed(self, IndexCount, StartIndexLocation, BaseVertexLocation); +#endif +} + +static inline void _sg_d3d11_DrawIndexedInstanced(ID3D11DeviceContext* self, UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation) { +#if defined(__cplusplus) + self->DrawIndexedInstanced(IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); +#else + self->lpVtbl->DrawIndexedInstanced(self, IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); +#endif +} + +static inline void _sg_d3d11_Draw(ID3D11DeviceContext* self, UINT VertexCount, UINT StartVertexLocation) { +#if defined(__cplusplus) + self->Draw(VertexCount, StartVertexLocation); +#else + self->lpVtbl->Draw(self, VertexCount, StartVertexLocation); +#endif +} + +static inline void _sg_d3d11_DrawInstanced(ID3D11DeviceContext* self, UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation) { +#if defined(__cplusplus) + self->DrawInstanced(VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); +#else + self->lpVtbl->DrawInstanced(self, VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); +#endif +} + +static inline HRESULT _sg_d3d11_Map(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { +#if defined(__cplusplus) + return self->Map(pResource, Subresource, MapType, MapFlags, pMappedResource); +#else + return self->lpVtbl->Map(self, pResource, Subresource, MapType, MapFlags, pMappedResource); +#endif +} + +static inline void _sg_d3d11_Unmap(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource) { +#if defined(__cplusplus) + self->Unmap(pResource, Subresource); +#else + self->lpVtbl->Unmap(self, pResource, Subresource); +#endif +} + +static inline void _sg_d3d11_ClearState(ID3D11DeviceContext* self) { +#if defined(__cplusplus) + self->ClearState(); +#else + self->lpVtbl->ClearState(self); +#endif +} + +/*-- enum translation functions ----------------------------------------------*/ +_SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return D3D11_USAGE_IMMUTABLE; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return D3D11_USAGE_DYNAMIC; + default: + SOKOL_UNREACHABLE; + return (D3D11_USAGE) 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return 0; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return D3D11_CPU_ACCESS_WRITE; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return DXGI_FORMAT_R8_UNORM; + case SG_PIXELFORMAT_R8SN: return DXGI_FORMAT_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return DXGI_FORMAT_R8_UINT; + case SG_PIXELFORMAT_R8SI: return DXGI_FORMAT_R8_SINT; + case SG_PIXELFORMAT_R16: return DXGI_FORMAT_R16_UNORM; + case SG_PIXELFORMAT_R16SN: return DXGI_FORMAT_R16_SNORM; + case SG_PIXELFORMAT_R16UI: return DXGI_FORMAT_R16_UINT; + case SG_PIXELFORMAT_R16SI: return DXGI_FORMAT_R16_SINT; + case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; + case SG_PIXELFORMAT_RG8: return DXGI_FORMAT_R8G8_UNORM; + case SG_PIXELFORMAT_RG8SN: return DXGI_FORMAT_R8G8_SNORM; + case SG_PIXELFORMAT_RG8UI: return DXGI_FORMAT_R8G8_UINT; + case SG_PIXELFORMAT_RG8SI: return DXGI_FORMAT_R8G8_SINT; + case SG_PIXELFORMAT_R32UI: return DXGI_FORMAT_R32_UINT; + case SG_PIXELFORMAT_R32SI: return DXGI_FORMAT_R32_SINT; + case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; + case SG_PIXELFORMAT_RG16: return DXGI_FORMAT_R16G16_UNORM; + case SG_PIXELFORMAT_RG16SN: return DXGI_FORMAT_R16G16_SNORM; + case SG_PIXELFORMAT_RG16UI: return DXGI_FORMAT_R16G16_UINT; + case SG_PIXELFORMAT_RG16SI: return DXGI_FORMAT_R16G16_SINT; + case SG_PIXELFORMAT_RG16F: return DXGI_FORMAT_R16G16_FLOAT; + case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_PIXELFORMAT_RGBA8SN: return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_PIXELFORMAT_RGBA8SI: return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_PIXELFORMAT_BGRA8: return DXGI_FORMAT_B8G8R8A8_UNORM; + case SG_PIXELFORMAT_RGB10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; + case SG_PIXELFORMAT_RG11B10F: return DXGI_FORMAT_R11G11B10_FLOAT; + case SG_PIXELFORMAT_RG32UI: return DXGI_FORMAT_R32G32_UINT; + case SG_PIXELFORMAT_RG32SI: return DXGI_FORMAT_R32G32_SINT; + case SG_PIXELFORMAT_RG32F: return DXGI_FORMAT_R32G32_FLOAT; + case SG_PIXELFORMAT_RGBA16: return DXGI_FORMAT_R16G16B16A16_UNORM; + case SG_PIXELFORMAT_RGBA16SN: return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_PIXELFORMAT_RGBA16UI: return DXGI_FORMAT_R16G16B16A16_UINT; + case SG_PIXELFORMAT_RGBA16SI: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; + case SG_PIXELFORMAT_RGBA32UI: return DXGI_FORMAT_R32G32B32A32_UINT; + case SG_PIXELFORMAT_RGBA32SI: return DXGI_FORMAT_R32G32B32A32_SINT; + case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_D32_FLOAT; + case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_D24_UNORM_S8_UINT; + case SG_PIXELFORMAT_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM; + case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM; + case SG_PIXELFORMAT_BC3_RGBA: return DXGI_FORMAT_BC3_UNORM; + case SG_PIXELFORMAT_BC4_R: return DXGI_FORMAT_BC4_UNORM; + case SG_PIXELFORMAT_BC4_RSN: return DXGI_FORMAT_BC4_SNORM; + case SG_PIXELFORMAT_BC5_RG: return DXGI_FORMAT_BC5_UNORM; + case SG_PIXELFORMAT_BC5_RGSN: return DXGI_FORMAT_BC5_SNORM; + case SG_PIXELFORMAT_BC6H_RGBF: return DXGI_FORMAT_BC6H_SF16; + case SG_PIXELFORMAT_BC6H_RGBUF: return DXGI_FORMAT_BC6H_UF16; + case SG_PIXELFORMAT_BC7_RGBA: return DXGI_FORMAT_BC7_UNORM; + default: return DXGI_FORMAT_UNKNOWN; + }; +} + +_SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) { + switch (prim_type) { + case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; + case SG_PRIMITIVETYPE_LINES: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; + case SG_PRIMITIVETYPE_LINE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + default: SOKOL_UNREACHABLE; return (D3D11_PRIMITIVE_TOPOLOGY) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) { + switch (index_type) { + case SG_INDEXTYPE_NONE: return DXGI_FORMAT_UNKNOWN; + case SG_INDEXTYPE_UINT16: return DXGI_FORMAT_R16_UINT; + case SG_INDEXTYPE_UINT32: return DXGI_FORMAT_R32_UINT; + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, uint32_t max_anisotropy) { + if (max_anisotropy > 1) { + return D3D11_FILTER_ANISOTROPIC; + } + else if (mag_f == SG_FILTER_NEAREST) { + switch (min_f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_MAG_MIP_POINT; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + default: + SOKOL_UNREACHABLE; break; + } + } + else if (mag_f == SG_FILTER_LINEAR) { + switch (min_f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_MAG_MIP_LINEAR; + default: + SOKOL_UNREACHABLE; break; + } + } + /* invalid value for mag filter */ + SOKOL_UNREACHABLE; + return D3D11_FILTER_MIN_MAG_MIP_POINT; +} + +_SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: return D3D11_TEXTURE_ADDRESS_WRAP; + case SG_WRAP_CLAMP_TO_EDGE: return D3D11_TEXTURE_ADDRESS_CLAMP; + case SG_WRAP_CLAMP_TO_BORDER: return D3D11_TEXTURE_ADDRESS_BORDER; + case SG_WRAP_MIRRORED_REPEAT: return D3D11_TEXTURE_ADDRESS_MIRROR; + default: SOKOL_UNREACHABLE; return (D3D11_TEXTURE_ADDRESS_MODE) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return DXGI_FORMAT_R32_FLOAT; + case SG_VERTEXFORMAT_FLOAT2: return DXGI_FORMAT_R32G32_FLOAT; + case SG_VERTEXFORMAT_FLOAT3: return DXGI_FORMAT_R32G32B32_FLOAT; + case SG_VERTEXFORMAT_FLOAT4: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_VERTEXFORMAT_BYTE4: return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_VERTEXFORMAT_BYTE4N: return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_VERTEXFORMAT_UBYTE4: return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_VERTEXFORMAT_UBYTE4N: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_VERTEXFORMAT_SHORT2: return DXGI_FORMAT_R16G16_SINT; + case SG_VERTEXFORMAT_SHORT2N: return DXGI_FORMAT_R16G16_SNORM; + case SG_VERTEXFORMAT_USHORT2N: return DXGI_FORMAT_R16G16_UNORM; + case SG_VERTEXFORMAT_SHORT4: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_VERTEXFORMAT_USHORT4N: return DXGI_FORMAT_R16G16B16A16_UNORM; + case SG_VERTEXFORMAT_UINT10_N2: return DXGI_FORMAT_R10G10B10A2_UNORM; + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_INPUT_CLASSIFICATION _sg_d3d11_input_classification(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return D3D11_INPUT_PER_VERTEX_DATA; + case SG_VERTEXSTEP_PER_INSTANCE: return D3D11_INPUT_PER_INSTANCE_DATA; + default: SOKOL_UNREACHABLE; return (D3D11_INPUT_CLASSIFICATION) 0; + } +} + +_SOKOL_PRIVATE D3D11_CULL_MODE _sg_d3d11_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return D3D11_CULL_NONE; + case SG_CULLMODE_FRONT: return D3D11_CULL_FRONT; + case SG_CULLMODE_BACK: return D3D11_CULL_BACK; + default: SOKOL_UNREACHABLE; return (D3D11_CULL_MODE) 0; + } +} + +_SOKOL_PRIVATE D3D11_COMPARISON_FUNC _sg_d3d11_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return D3D11_COMPARISON_NEVER; + case SG_COMPAREFUNC_LESS: return D3D11_COMPARISON_LESS; + case SG_COMPAREFUNC_EQUAL: return D3D11_COMPARISON_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return D3D11_COMPARISON_LESS_EQUAL; + case SG_COMPAREFUNC_GREATER: return D3D11_COMPARISON_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return D3D11_COMPARISON_NOT_EQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return D3D11_COMPARISON_GREATER_EQUAL; + case SG_COMPAREFUNC_ALWAYS: return D3D11_COMPARISON_ALWAYS; + default: SOKOL_UNREACHABLE; return (D3D11_COMPARISON_FUNC) 0; + } +} + +_SOKOL_PRIVATE D3D11_STENCIL_OP _sg_d3d11_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return D3D11_STENCIL_OP_KEEP; + case SG_STENCILOP_ZERO: return D3D11_STENCIL_OP_ZERO; + case SG_STENCILOP_REPLACE: return D3D11_STENCIL_OP_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return D3D11_STENCIL_OP_INCR_SAT; + case SG_STENCILOP_DECR_CLAMP: return D3D11_STENCIL_OP_DECR_SAT; + case SG_STENCILOP_INVERT: return D3D11_STENCIL_OP_INVERT; + case SG_STENCILOP_INCR_WRAP: return D3D11_STENCIL_OP_INCR; + case SG_STENCILOP_DECR_WRAP: return D3D11_STENCIL_OP_DECR; + default: SOKOL_UNREACHABLE; return (D3D11_STENCIL_OP) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND _sg_d3d11_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return D3D11_BLEND_ZERO; + case SG_BLENDFACTOR_ONE: return D3D11_BLEND_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return D3D11_BLEND_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return D3D11_BLEND_DEST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return D3D11_BLEND_SRC_ALPHA_SAT; + case SG_BLENDFACTOR_BLEND_COLOR: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return D3D11_BLEND_INV_BLEND_FACTOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return D3D11_BLEND_INV_BLEND_FACTOR; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND_OP _sg_d3d11_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return D3D11_BLEND_OP_ADD; + case SG_BLENDOP_SUBTRACT: return D3D11_BLEND_OP_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return D3D11_BLEND_OP_REV_SUBTRACT; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND_OP) 0; + } +} + +_SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) { + UINT8 res = 0; + if (m & SG_COLORMASK_R) { + res |= D3D11_COLOR_WRITE_ENABLE_RED; + } + if (m & SG_COLORMASK_G) { + res |= D3D11_COLOR_WRITE_ENABLE_GREEN; + } + if (m & SG_COLORMASK_B) { + res |= D3D11_COLOR_WRITE_ENABLE_BLUE; + } + if (m & SG_COLORMASK_A) { + res |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + } + return res; +} + +/* see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware */ +_SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { + _sg.backend = SG_BACKEND_D3D11; + + _sg.features.instancing = true; + _sg.features.origin_top_left = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; + _sg.features.image_clamp_to_border = true; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + + _sg.limits.max_image_size_2d = 16 * 1024; + _sg.limits.max_image_size_cube = 16 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 16 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + /* see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support */ + for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) { + UINT dxgi_fmt_caps = 0; + const DXGI_FORMAT dxgi_fmt = _sg_d3d11_pixel_format((sg_pixel_format)fmt); + if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) { + HRESULT hr = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); + SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); + if (!SUCCEEDED(hr)) { + dxgi_fmt_caps = 0; + } + } + sg_pixelformat_info* info = &_sg.formats[fmt]; + info->sample = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D); + info->filter = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE); + info->render = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); + info->blend = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); + info->msaa = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); + info->depth = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); + if (info->depth) { + info->render = true; + } + } +} + +_SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) { + /* assume _sg.d3d11 already is zero-initialized */ + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->context.d3d11.device); + SOKOL_ASSERT(desc->context.d3d11.device_context); + SOKOL_ASSERT(desc->context.d3d11.render_target_view_cb || desc->context.d3d11.render_target_view_userdata_cb); + SOKOL_ASSERT(desc->context.d3d11.depth_stencil_view_cb || desc->context.d3d11.depth_stencil_view_userdata_cb); + _sg.d3d11.valid = true; + _sg.d3d11.dev = (ID3D11Device*) desc->context.d3d11.device; + _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->context.d3d11.device_context; + _sg.d3d11.rtv_cb = desc->context.d3d11.render_target_view_cb; + _sg.d3d11.rtv_userdata_cb = desc->context.d3d11.render_target_view_userdata_cb; + _sg.d3d11.dsv_cb = desc->context.d3d11.depth_stencil_view_cb; + _sg.d3d11.dsv_userdata_cb = desc->context.d3d11.depth_stencil_view_userdata_cb; + _sg.d3d11.user_data = desc->context.d3d11.user_data; + _sg_d3d11_init_caps(); +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_backend(void) { + SOKOL_ASSERT(_sg.d3d11.valid); + _sg.d3d11.valid = false; +} + +_SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { + /* clear all the device context state, so that resource refs don't keep stuck in the d3d device context */ + _sg_d3d11_ClearState(_sg.d3d11.ctx); +} + +_SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) { + /* just clear the d3d11 device context state */ + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE void _sg_d3d11_activate_context(_sg_context_t* ctx) { + _SOKOL_UNUSED(ctx); + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + /* empty */ +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + SOKOL_ASSERT(!buf->d3d11.buf); + _sg_buffer_common_init(&buf->cmn, desc); + const bool injected = (0 != desc->d3d11_buffer); + if (injected) { + buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; + _sg_d3d11_AddRef(buf->d3d11.buf); + } + else { + D3D11_BUFFER_DESC d3d11_desc; + memset(&d3d11_desc, 0, sizeof(d3d11_desc)); + d3d11_desc.ByteWidth = (UINT)buf->cmn.size; + d3d11_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); + d3d11_desc.BindFlags = buf->cmn.type == SG_BUFFERTYPE_VERTEXBUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER; + d3d11_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); + D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; + D3D11_SUBRESOURCE_DATA init_data; + memset(&init_data, 0, sizeof(init_data)); + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + init_data.pSysMem = desc->data.ptr; + init_data_ptr = &init_data; + } + HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11.buf); + if (!(SUCCEEDED(hr) && buf->d3d11.buf)) { + SOKOL_LOG("failed to create D3D11 buffer\n"); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + if (buf->d3d11.buf) { + _sg_d3d11_Release(buf->d3d11.buf); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_data* data) { + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; + int subres_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); + D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; + const int mip_width = ((img->cmn.width>>mip_index)>0) ? img->cmn.width>>mip_index : 1; + const int mip_height = ((img->cmn.height>>mip_index)>0) ? img->cmn.height>>mip_index : 1; + const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); + const size_t slice_size = subimg_data->size / (size_t)num_slices; + const size_t slice_offset = slice_size * (size_t)slice_index; + const uint8_t* ptr = (const uint8_t*) subimg_data->ptr; + subres_data->pSysMem = ptr + slice_offset; + subres_data->SysMemPitch = (UINT)_sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + if (img->cmn.type == SG_IMAGETYPE_3D) { + /* FIXME? const int mip_depth = ((img->depth>>mip_index)>0) ? img->depth>>mip_index : 1; */ + subres_data->SysMemSlicePitch = (UINT)_sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + } + else { + subres_data->SysMemSlicePitch = 0; + } + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + SOKOL_ASSERT(!img->d3d11.tex2d && !img->d3d11.tex3d && !img->d3d11.texds && !img->d3d11.texmsaa); + SOKOL_ASSERT(!img->d3d11.srv && !img->d3d11.smp); + HRESULT hr; + + _sg_image_common_init(&img->cmn, desc); + const bool injected = (0 != desc->d3d11_texture) || (0 != desc->d3d11_shader_resource_view); + const bool msaa = (img->cmn.sample_count > 1); + img->d3d11.format = _sg_d3d11_pixel_format(img->cmn.pixel_format); + + /* special case depth-stencil buffer? */ + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { + /* create only a depth-texture */ + SOKOL_ASSERT(!injected); + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + SOKOL_LOG("trying to create a D3D11 depth-texture with unsupported pixel format\n"); + return SG_RESOURCESTATE_FAILED; + } + D3D11_TEXTURE2D_DESC d3d11_desc; + memset(&d3d11_desc, 0, sizeof(d3d11_desc)); + d3d11_desc.Width = (UINT)img->cmn.width; + d3d11_desc.Height = (UINT)img->cmn.height; + d3d11_desc.MipLevels = 1; + d3d11_desc.ArraySize = 1; + d3d11_desc.Format = img->d3d11.format; + d3d11_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + d3d11_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; + d3d11_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11.texds); + if (!(SUCCEEDED(hr) && img->d3d11.texds)) { + SOKOL_LOG("failed to create D3D11 texture 2D\n"); + return SG_RESOURCESTATE_FAILED; + } + } + else { + /* create (or inject) color texture and shader-resource-view */ + + /* prepare initial content pointers */ + D3D11_SUBRESOURCE_DATA* init_data = 0; + if (!injected && (img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_d3d11_fill_subres_data(img, &desc->data); + init_data = _sg.d3d11.subres_data; + } + if (img->cmn.type != SG_IMAGETYPE_3D) { + /* 2D-, cube- or array-texture */ + /* if this is an MSAA render target, the following texture will be the 'resolve-texture' */ + + /* first check for injected texture and/or resource view */ + if (injected) { + img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; + if (img->d3d11.tex2d) { + _sg_d3d11_AddRef(img->d3d11.tex2d); + } + else { + /* if only a shader-resource-view was provided, but no texture, lookup + the texture from the shader-resource-view, this also bumps the refcount + */ + SOKOL_ASSERT(img->d3d11.srv); + _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex2d); + SOKOL_ASSERT(img->d3d11.tex2d); + } + if (img->d3d11.srv) { + _sg_d3d11_AddRef(img->d3d11.srv); + } + } + + /* if not injected, create texture */ + if (0 == img->d3d11.tex2d) { + D3D11_TEXTURE2D_DESC d3d11_tex_desc; + memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + switch (img->cmn.type) { + case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices; break; + case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; + default: d3d11_tex_desc.ArraySize = 1; break; + } + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.Format = img->d3d11.format; + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + if (!msaa) { + d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; + } + d3d11_tex_desc.CPUAccessFlags = 0; + } + else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); + } + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + /* trying to create a texture format that's not supported by D3D */ + SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); + return SG_RESOURCESTATE_FAILED; + } + d3d11_tex_desc.SampleDesc.Count = 1; + d3d11_tex_desc.SampleDesc.Quality = 0; + d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); + if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) { + SOKOL_LOG("failed to create D3D11 texture 2D\n"); + return SG_RESOURCESTATE_FAILED; + } + } + + /* ...and similar, if not injected, create shader-resource-view */ + if (0 == img->d3d11.srv) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = img->d3d11.format; + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_CUBE: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_ARRAY: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; + break; + default: + SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + SOKOL_LOG("failed to create D3D11 resource view\n"); + return SG_RESOURCESTATE_FAILED; + } + } + } + else { + /* 3D texture - same procedure, first check if injected, than create non-injected */ + if (injected) { + img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture; + img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; + if (img->d3d11.tex3d) { + _sg_d3d11_AddRef(img->d3d11.tex3d); + } + else { + SOKOL_ASSERT(img->d3d11.srv); + _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex3d); + SOKOL_ASSERT(img->d3d11.tex3d); + } + if (img->d3d11.srv) { + _sg_d3d11_AddRef(img->d3d11.srv); + } + } + + if (0 == img->d3d11.tex3d) { + D3D11_TEXTURE3D_DESC d3d11_tex_desc; + memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.Format = img->d3d11.format; + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + if (!msaa) { + d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; + } + d3d11_tex_desc.CPUAccessFlags = 0; + } + else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); + } + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + /* trying to create a texture format that's not supported by D3D */ + SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); + return SG_RESOURCESTATE_FAILED; + } + hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); + if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) { + SOKOL_LOG("failed to create D3D11 texture 3D\n"); + return SG_RESOURCESTATE_FAILED; + } + } + + if (0 == img->d3d11.srv) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = img->d3d11.format; + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + SOKOL_LOG("failed to create D3D11 resource view\n"); + return SG_RESOURCESTATE_FAILED; + } + } + } + + /* also need to create a separate MSAA render target texture? */ + if (msaa) { + D3D11_TEXTURE2D_DESC d3d11_tex_desc; + memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.MipLevels = 1; + d3d11_tex_desc.ArraySize = 1; + d3d11_tex_desc.Format = img->d3d11.format; + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + d3d11_tex_desc.CPUAccessFlags = 0; + d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; + d3d11_tex_desc.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN; + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11.texmsaa); + if (!(SUCCEEDED(hr) && img->d3d11.texmsaa)) { + SOKOL_LOG("failed to create D3D11 texture 2D\n"); + return SG_RESOURCESTATE_FAILED; + } + } + + /* sampler state object, note D3D11 implements an internal shared-pool for sampler objects */ + D3D11_SAMPLER_DESC d3d11_smp_desc; + memset(&d3d11_smp_desc, 0, sizeof(d3d11_smp_desc)); + d3d11_smp_desc.Filter = _sg_d3d11_filter(img->cmn.min_filter, img->cmn.mag_filter, img->cmn.max_anisotropy); + d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(img->cmn.wrap_u); + d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(img->cmn.wrap_v); + d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(img->cmn.wrap_w); + switch (img->cmn.border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + /* all 0.0f */ + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + for (int i = 0; i < 4; i++) { + d3d11_smp_desc.BorderColor[i] = 1.0f; + } + break; + default: + /* opaque black */ + d3d11_smp_desc.BorderColor[3] = 1.0f; + break; + } + d3d11_smp_desc.MaxAnisotropy = img->cmn.max_anisotropy; + d3d11_smp_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + d3d11_smp_desc.MinLOD = desc->min_lod; + d3d11_smp_desc.MaxLOD = desc->max_lod; + hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11.smp); + if (!(SUCCEEDED(hr) && img->d3d11.smp)) { + SOKOL_LOG("failed to create D3D11 sampler state\n"); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->d3d11.tex2d) { + _sg_d3d11_Release(img->d3d11.tex2d); + } + if (img->d3d11.tex3d) { + _sg_d3d11_Release(img->d3d11.tex3d); + } + if (img->d3d11.texds) { + _sg_d3d11_Release(img->d3d11.texds); + } + if (img->d3d11.texmsaa) { + _sg_d3d11_Release(img->d3d11.texmsaa); + } + if (img->d3d11.srv) { + _sg_d3d11_Release(img->d3d11.srv); + } + if (img->d3d11.smp) { + _sg_d3d11_Release(img->d3d11.smp); + } +} + +_SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { + /* on UWP, don't do anything (not tested) */ +#if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + return true; +#else + /* load DLL on demand */ + if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { + _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); + if (0 == _sg.d3d11.d3dcompiler_dll) { + /* don't attempt to load missing DLL in the future */ + SOKOL_LOG("failed to load d3dcompiler_47.dll!\n"); + _sg.d3d11.d3dcompiler_dll_load_failed = true; + return false; + } + /* look up function pointers */ + _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); + SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); + } + return 0 != _sg.d3d11.d3dcompiler_dll; +#endif +} + +#if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) +#define _sg_d3d11_D3DCompile D3DCompile +#else +#define _sg_d3d11_D3DCompile _sg.d3d11.D3DCompile_func +#endif + +_SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc) { + if (!_sg_d3d11_load_d3dcompiler_dll()) { + return NULL; + } + SOKOL_ASSERT(stage_desc->d3d11_target); + ID3DBlob* output = NULL; + ID3DBlob* errors_or_warnings = NULL; + HRESULT hr = _sg_d3d11_D3DCompile( + stage_desc->source, /* pSrcData */ + strlen(stage_desc->source), /* SrcDataSize */ + NULL, /* pSourceName */ + NULL, /* pDefines */ + NULL, /* pInclude */ + stage_desc->entry ? stage_desc->entry : "main", /* pEntryPoint */ + stage_desc->d3d11_target, /* pTarget (vs_5_0 or ps_5_0) */ + D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, /* Flags1 */ + 0, /* Flags2 */ + &output, /* ppCode */ + &errors_or_warnings); /* ppErrorMsgs */ + if (errors_or_warnings) { + SOKOL_LOG((LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); + _sg_d3d11_Release(errors_or_warnings); errors_or_warnings = NULL; + } + if (FAILED(hr)) { + /* just in case, usually output is NULL here */ + if (output) { + _sg_d3d11_Release(output); + output = NULL; + } + } + return output; +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.vs_blob); + HRESULT hr; + + _sg_shader_common_init(&shd->cmn, desc); + + /* copy vertex attribute semantic names and indices */ + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_strcpy(&shd->d3d11.attrs[i].sem_name, desc->attrs[i].sem_name); + shd->d3d11.attrs[i].sem_index = desc->attrs[i].sem_index; + } + + /* shader stage uniform blocks and image slots */ + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; + for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { + const _sg_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; + + /* create a D3D constant buffer for each uniform block */ + SOKOL_ASSERT(0 == d3d11_stage->cbufs[ub_index]); + D3D11_BUFFER_DESC cb_desc; + memset(&cb_desc, 0, sizeof(cb_desc)); + cb_desc.ByteWidth = (UINT)_sg_roundup((int)ub->size, 16); + cb_desc.Usage = D3D11_USAGE_DEFAULT; + cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &d3d11_stage->cbufs[ub_index]); + if (!(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index])) { + SOKOL_LOG("failed to create D3D11 buffer\n"); + return SG_RESOURCESTATE_FAILED; + } + } + } + + const void* vs_ptr = 0, *fs_ptr = 0; + SIZE_T vs_length = 0, fs_length = 0; + ID3DBlob* vs_blob = 0, *fs_blob = 0; + if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { + /* create from shader byte code */ + vs_ptr = desc->vs.bytecode.ptr; + fs_ptr = desc->fs.bytecode.ptr; + vs_length = desc->vs.bytecode.size; + fs_length = desc->fs.bytecode.size; + } + else { + /* compile from shader source code */ + vs_blob = _sg_d3d11_compile_shader(&desc->vs); + fs_blob = _sg_d3d11_compile_shader(&desc->fs); + if (vs_blob && fs_blob) { + vs_ptr = _sg_d3d11_GetBufferPointer(vs_blob); + vs_length = _sg_d3d11_GetBufferSize(vs_blob); + fs_ptr = _sg_d3d11_GetBufferPointer(fs_blob); + fs_length = _sg_d3d11_GetBufferSize(fs_blob); + } + } + sg_resource_state result = SG_RESOURCESTATE_FAILED; + if (vs_ptr && fs_ptr && (vs_length > 0) && (fs_length > 0)) { + /* create the D3D vertex- and pixel-shader objects */ + hr = _sg_d3d11_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11.vs); + bool vs_succeeded = SUCCEEDED(hr) && shd->d3d11.vs; + hr = _sg_d3d11_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11.fs); + bool fs_succeeded = SUCCEEDED(hr) && shd->d3d11.fs; + + /* need to store the vertex shader byte code, this is needed later in sg_create_pipeline */ + if (vs_succeeded && fs_succeeded) { + shd->d3d11.vs_blob_length = vs_length; + shd->d3d11.vs_blob = SOKOL_MALLOC((size_t)vs_length); + SOKOL_ASSERT(shd->d3d11.vs_blob); + memcpy(shd->d3d11.vs_blob, vs_ptr, vs_length); + result = SG_RESOURCESTATE_VALID; + } + } + if (vs_blob) { + _sg_d3d11_Release(vs_blob); vs_blob = 0; + } + if (fs_blob) { + _sg_d3d11_Release(fs_blob); fs_blob = 0; + } + return result; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + if (shd->d3d11.vs) { + _sg_d3d11_Release(shd->d3d11.vs); + } + if (shd->d3d11.fs) { + _sg_d3d11_Release(shd->d3d11.fs); + } + if (shd->d3d11.vs_blob) { + SOKOL_FREE(shd->d3d11.vs_blob); + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; + for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { + if (d3d11_stage->cbufs[ub_index]) { + _sg_d3d11_Release(d3d11_stage->cbufs[ub_index]); + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(shd->d3d11.vs_blob && shd->d3d11.vs_blob_length > 0); + SOKOL_ASSERT(!pip->d3d11.il && !pip->d3d11.rs && !pip->d3d11.dss && !pip->d3d11.bs); + + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + pip->d3d11.index_format = _sg_d3d11_index_format(pip->cmn.index_type); + pip->d3d11.topology = _sg_d3d11_primitive_topology(desc->primitive_type); + pip->d3d11.stencil_ref = desc->stencil.ref; + + /* create input layout object */ + HRESULT hr; + D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; + memset(d3d11_comps, 0, sizeof(d3d11_comps)); + int attr_index = 0; + for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; + const sg_vertex_step step_func = l_desc->step_func; + const int step_rate = l_desc->step_rate; + D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; + d3d11_comp->SemanticName = _sg_strptr(&shd->d3d11.attrs[attr_index].sem_name); + d3d11_comp->SemanticIndex = (UINT)shd->d3d11.attrs[attr_index].sem_index; + d3d11_comp->Format = _sg_d3d11_vertex_format(a_desc->format); + d3d11_comp->InputSlot = (UINT)a_desc->buffer_index; + d3d11_comp->AlignedByteOffset = (UINT)a_desc->offset; + d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); + if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { + d3d11_comp->InstanceDataStepRate = (UINT)step_rate; + pip->cmn.use_instanced_draw = true; + } + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + } + for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { + if (pip->cmn.vertex_layout_valid[layout_index]) { + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; + SOKOL_ASSERT(l_desc->stride > 0); + pip->d3d11.vb_strides[layout_index] = (UINT)l_desc->stride; + } + else { + pip->d3d11.vb_strides[layout_index] = 0; + } + } + hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, + d3d11_comps, /* pInputElementDesc */ + (UINT)attr_index, /* NumElements */ + shd->d3d11.vs_blob, /* pShaderByteCodeWithInputSignature */ + shd->d3d11.vs_blob_length, /* BytecodeLength */ + &pip->d3d11.il); + if (!(SUCCEEDED(hr) && pip->d3d11.il)) { + SOKOL_LOG("failed to create D3D11 input layout\n"); + return SG_RESOURCESTATE_FAILED; + } + + /* create rasterizer state */ + D3D11_RASTERIZER_DESC rs_desc; + memset(&rs_desc, 0, sizeof(rs_desc)); + rs_desc.FillMode = D3D11_FILL_SOLID; + rs_desc.CullMode = _sg_d3d11_cull_mode(desc->cull_mode); + rs_desc.FrontCounterClockwise = desc->face_winding == SG_FACEWINDING_CCW; + rs_desc.DepthBias = (INT) pip->cmn.depth_bias; + rs_desc.DepthBiasClamp = pip->cmn.depth_bias_clamp; + rs_desc.SlopeScaledDepthBias = pip->cmn.depth_bias_slope_scale; + rs_desc.DepthClipEnable = TRUE; + rs_desc.ScissorEnable = TRUE; + rs_desc.MultisampleEnable = desc->sample_count > 1; + rs_desc.AntialiasedLineEnable = FALSE; + hr = _sg_d3d11_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs); + if (!(SUCCEEDED(hr) && pip->d3d11.rs)) { + SOKOL_LOG("failed to create D3D11 rasterizer state\n"); + return SG_RESOURCESTATE_FAILED; + } + + /* create depth-stencil state */ + D3D11_DEPTH_STENCIL_DESC dss_desc; + memset(&dss_desc, 0, sizeof(dss_desc)); + dss_desc.DepthEnable = TRUE; + dss_desc.DepthWriteMask = desc->depth.write_enabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + dss_desc.DepthFunc = _sg_d3d11_compare_func(desc->depth.compare); + dss_desc.StencilEnable = desc->stencil.enabled; + dss_desc.StencilReadMask = desc->stencil.read_mask; + dss_desc.StencilWriteMask = desc->stencil.write_mask; + const sg_stencil_face_state* sf = &desc->stencil.front; + dss_desc.FrontFace.StencilFailOp = _sg_d3d11_stencil_op(sf->fail_op); + dss_desc.FrontFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sf->depth_fail_op); + dss_desc.FrontFace.StencilPassOp = _sg_d3d11_stencil_op(sf->pass_op); + dss_desc.FrontFace.StencilFunc = _sg_d3d11_compare_func(sf->compare); + const sg_stencil_face_state* sb = &desc->stencil.back; + dss_desc.BackFace.StencilFailOp = _sg_d3d11_stencil_op(sb->fail_op); + dss_desc.BackFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sb->depth_fail_op); + dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(sb->pass_op); + dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare); + hr = _sg_d3d11_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss); + if (!(SUCCEEDED(hr) && pip->d3d11.dss)) { + SOKOL_LOG("failed to create D3D11 depth stencil state\n"); + return SG_RESOURCESTATE_FAILED; + } + + /* create blend state */ + D3D11_BLEND_DESC bs_desc; + memset(&bs_desc, 0, sizeof(bs_desc)); + bs_desc.AlphaToCoverageEnable = desc->alpha_to_coverage_enabled; + bs_desc.IndependentBlendEnable = TRUE; + { + int i = 0; + for (i = 0; i < desc->color_count; i++) { + const sg_blend_state* src = &desc->colors[i].blend; + D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; + dst->BlendEnable = src->enabled; + dst->SrcBlend = _sg_d3d11_blend_factor(src->src_factor_rgb); + dst->DestBlend = _sg_d3d11_blend_factor(src->dst_factor_rgb); + dst->BlendOp = _sg_d3d11_blend_op(src->op_rgb); + dst->SrcBlendAlpha = _sg_d3d11_blend_factor(src->src_factor_alpha); + dst->DestBlendAlpha = _sg_d3d11_blend_factor(src->dst_factor_alpha); + dst->BlendOpAlpha = _sg_d3d11_blend_op(src->op_alpha); + dst->RenderTargetWriteMask = _sg_d3d11_color_write_mask(desc->colors[i].write_mask); + } + for (; i < 8; i++) { + D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; + dst->BlendEnable = FALSE; + dst->SrcBlend = dst->SrcBlendAlpha = D3D11_BLEND_ONE; + dst->DestBlend = dst->DestBlendAlpha = D3D11_BLEND_ZERO; + dst->BlendOp = dst->BlendOpAlpha = D3D11_BLEND_OP_ADD; + dst->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + } + } + hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs); + if (!(SUCCEEDED(hr) && pip->d3d11.bs)) { + SOKOL_LOG("failed to create D3D11 blend state\n"); + return SG_RESOURCESTATE_FAILED; + } + + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip == _sg.d3d11.cur_pipeline) { + _sg.d3d11.cur_pipeline = 0; + _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; + } + if (pip->d3d11.il) { + _sg_d3d11_Release(pip->d3d11.il); + } + if (pip->d3d11.rs) { + _sg_d3d11_Release(pip->d3d11.rs); + } + if (pip->d3d11.dss) { + _sg_d3d11_Release(pip->d3d11.dss); + } + if (pip->d3d11.bs) { + _sg_d3d11_Release(pip->d3d11.bs); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + SOKOL_ASSERT(_sg.d3d11.dev); + + _sg_pass_common_init(&pass->cmn, desc); + + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + const sg_pass_attachment_desc* att_desc = &desc->color_attachments[i]; + _SOKOL_UNUSED(att_desc); + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + _sg_image_t* att_img = att_images[i]; + SOKOL_ASSERT(att_img && (att_img->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_img->cmn.pixel_format)); + SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].image); + pass->d3d11.color_atts[i].image = att_img; + + /* create D3D11 render-target-view */ + const _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; + SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].rtv); + ID3D11Resource* d3d11_res = 0; + const bool is_msaa = att_img->cmn.sample_count > 1; + D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; + memset(&d3d11_rtv_desc, 0, sizeof(d3d11_rtv_desc)); + d3d11_rtv_desc.Format = att_img->d3d11.format; + if ((att_img->cmn.type == SG_IMAGETYPE_2D) || is_msaa) { + if (is_msaa) { + d3d11_res = (ID3D11Resource*) att_img->d3d11.texmsaa; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + } + else { + d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + d3d11_rtv_desc.Texture2D.MipSlice = (UINT)cmn_att->mip_level; + } + } + else if ((att_img->cmn.type == SG_IMAGETYPE_CUBE) || (att_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + d3d11_rtv_desc.Texture2DArray.MipSlice = (UINT)cmn_att->mip_level; + d3d11_rtv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_att->slice; + d3d11_rtv_desc.Texture2DArray.ArraySize = 1; + } + else { + SOKOL_ASSERT(att_img->cmn.type == SG_IMAGETYPE_3D); + d3d11_res = (ID3D11Resource*) att_img->d3d11.tex3d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; + d3d11_rtv_desc.Texture3D.MipSlice = (UINT)cmn_att->mip_level; + d3d11_rtv_desc.Texture3D.FirstWSlice = (UINT)cmn_att->slice; + d3d11_rtv_desc.Texture3D.WSize = 1; + } + SOKOL_ASSERT(d3d11_res); + HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11.color_atts[i].rtv); + if (!(SUCCEEDED(hr) && pass->d3d11.color_atts[i].rtv)) { + SOKOL_LOG("failed to create D3D11 render target view\n"); + return SG_RESOURCESTATE_FAILED; + } + } + + /* optional depth-stencil image */ + SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); + SOKOL_ASSERT(0 == pass->d3d11.ds_att.dsv); + if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + const sg_pass_attachment_desc* att_desc = &desc->depth_stencil_attachment; + _SOKOL_UNUSED(att_desc); + _sg_image_t* att_img = att_images[ds_img_index]; + SOKOL_ASSERT(att_img && (att_img->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_img->cmn.pixel_format)); + SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); + pass->d3d11.ds_att.image = att_img; + + /* create D3D11 depth-stencil-view */ + D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; + memset(&d3d11_dsv_desc, 0, sizeof(d3d11_dsv_desc)); + d3d11_dsv_desc.Format = att_img->d3d11.format; + const bool is_msaa = att_img->cmn.sample_count > 1; + if (is_msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + } + else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + } + ID3D11Resource* d3d11_res = (ID3D11Resource*) att_img->d3d11.texds; + SOKOL_ASSERT(d3d11_res); + HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11.ds_att.dsv); + if (!(SUCCEEDED(hr) && pass->d3d11.ds_att.dsv)) { + SOKOL_LOG("failed to create D3D11 depth stencil view\n"); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + SOKOL_ASSERT(pass != _sg.d3d11.cur_pass); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (pass->d3d11.color_atts[i].rtv) { + _sg_d3d11_Release(pass->d3d11.color_atts[i].rtv); + } + } + if (pass->d3d11.ds_att.dsv) { + _sg_d3d11_Release(pass->d3d11.ds_att.dsv); + } +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->d3d11.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->d3d11.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.d3d11.in_pass); + SOKOL_ASSERT(_sg.d3d11.rtv_cb || _sg.d3d11.rtv_userdata_cb); + SOKOL_ASSERT(_sg.d3d11.dsv_cb || _sg.d3d11.dsv_userdata_cb); + _sg.d3d11.in_pass = true; + _sg.d3d11.cur_width = w; + _sg.d3d11.cur_height = h; + if (pass) { + _sg.d3d11.cur_pass = pass; + _sg.d3d11.cur_pass_id.id = pass->slot.id; + _sg.d3d11.num_rtvs = 0; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.d3d11.cur_rtvs[i] = pass->d3d11.color_atts[i].rtv; + if (_sg.d3d11.cur_rtvs[i]) { + _sg.d3d11.num_rtvs++; + } + } + _sg.d3d11.cur_dsv = pass->d3d11.ds_att.dsv; + } + else { + /* render to default frame buffer */ + _sg.d3d11.cur_pass = 0; + _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; + _sg.d3d11.num_rtvs = 1; + if (_sg.d3d11.rtv_cb) { + _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_cb(); + } + else { + _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_userdata_cb(_sg.d3d11.user_data); + } + for (int i = 1; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.d3d11.cur_rtvs[i] = 0; + } + if (_sg.d3d11.dsv_cb) { + _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_cb(); + } + else { + _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_userdata_cb(_sg.d3d11.user_data); + } + SOKOL_ASSERT(_sg.d3d11.cur_rtvs[0] && _sg.d3d11.cur_dsv); + } + /* apply the render-target- and depth-stencil-views */ + _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.cur_rtvs, _sg.d3d11.cur_dsv); + + /* set viewport and scissor rect to cover whole screen */ + D3D11_VIEWPORT vp; + memset(&vp, 0, sizeof(vp)); + vp.Width = (FLOAT) w; + vp.Height = (FLOAT) h; + vp.MaxDepth = 1.0f; + _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); + D3D11_RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = w; + rect.bottom = h; + _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); + + /* perform clear action */ + for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { + if (action->colors[i].action == SG_ACTION_CLEAR) { + _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, _sg.d3d11.cur_rtvs[i], &action->colors[i].value.r); + } + } + UINT ds_flags = 0; + if (action->depth.action == SG_ACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_DEPTH; + } + if (action->stencil.action == SG_ACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_STENCIL; + } + if ((0 != ds_flags) && _sg.d3d11.cur_dsv) { + _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, _sg.d3d11.cur_dsv, ds_flags, action->depth.value, action->stencil.value); + } +} + +/* D3D11CalcSubresource only exists for C++ */ +_SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { + return mip_slice + array_slice * mip_levels; +} + +_SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { + SOKOL_ASSERT(_sg.d3d11.in_pass && _sg.d3d11.ctx); + _sg.d3d11.in_pass = false; + + /* need to resolve MSAA render target into texture? */ + if (_sg.d3d11.cur_pass) { + SOKOL_ASSERT(_sg.d3d11.cur_pass->slot.id == _sg.d3d11.cur_pass_id.id); + for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { + _sg_pass_attachment_t* cmn_att = &_sg.d3d11.cur_pass->cmn.color_atts[i]; + _sg_image_t* att_img = _sg.d3d11.cur_pass->d3d11.color_atts[i].image; + SOKOL_ASSERT(att_img && (att_img->slot.id == cmn_att->image_id.id)); + if (att_img->cmn.sample_count > 1) { + /* FIXME: support MSAA resolve into 3D texture */ + SOKOL_ASSERT(att_img->d3d11.tex2d && att_img->d3d11.texmsaa && !att_img->d3d11.tex3d); + SOKOL_ASSERT(DXGI_FORMAT_UNKNOWN != att_img->d3d11.format); + UINT dst_subres = _sg_d3d11_calcsubresource((UINT)cmn_att->mip_level, (UINT)cmn_att->slice, (UINT)att_img->cmn.num_mipmaps); + _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, + (ID3D11Resource*) att_img->d3d11.tex2d, /* pDstResource */ + dst_subres, /* DstSubresource */ + (ID3D11Resource*) att_img->d3d11.texmsaa, /* pSrcResource */ + 0, /* SrcSubresource */ + att_img->d3d11.format); + } + } + } + _sg.d3d11.cur_pass = 0; + _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; + _sg.d3d11.cur_pipeline = 0; + _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.d3d11.cur_rtvs[i] = 0; + } + _sg.d3d11.cur_dsv = 0; + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + D3D11_VIEWPORT vp; + vp.TopLeftX = (FLOAT) x; + vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); + vp.Width = (FLOAT) w; + vp.Height = (FLOAT) h; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + D3D11_RECT rect; + rect.left = x; + rect.top = (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); + rect.right = x + w; + rect.bottom = origin_top_left ? (y + h) : (_sg.d3d11.cur_height - y); + _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss && pip->d3d11.il); + + _sg.d3d11.cur_pipeline = pip; + _sg.d3d11.cur_pipeline_id.id = pip->slot.id; + _sg.d3d11.use_indexed_draw = (pip->d3d11.index_format != DXGI_FORMAT_UNKNOWN); + _sg.d3d11.use_instanced_draw = pip->cmn.use_instanced_draw; + + _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); + _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); + _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, &pip->cmn.blend_color.r, 0xFFFFFFFF); + _sg_d3d11_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11.topology); + _sg_d3d11_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11.il); + _sg_d3d11_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.vs, NULL, 0); + _sg_d3d11_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_VS].cbufs); + _sg_d3d11_PSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.fs, NULL, 0); + _sg_d3d11_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_FS].cbufs); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(pip); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + + /* gather all the D3D11 resources into arrays */ + ID3D11Buffer* d3d11_ib = ib ? ib->d3d11.buf : 0; + ID3D11Buffer* d3d11_vbs[SG_MAX_SHADERSTAGE_BUFFERS]; + UINT d3d11_vb_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; + ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_IMAGES]; + ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; + ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_IMAGES]; + int i; + for (i = 0; i < num_vbs; i++) { + SOKOL_ASSERT(vbs[i]->d3d11.buf); + d3d11_vbs[i] = vbs[i]->d3d11.buf; + d3d11_vb_offsets[i] = (UINT)vb_offsets[i]; + } + for (; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + d3d11_vbs[i] = 0; + d3d11_vb_offsets[i] = 0; + } + for (i = 0; i < num_vs_imgs; i++) { + SOKOL_ASSERT(vs_imgs[i]->d3d11.srv); + SOKOL_ASSERT(vs_imgs[i]->d3d11.smp); + d3d11_vs_srvs[i] = vs_imgs[i]->d3d11.srv; + d3d11_vs_smps[i] = vs_imgs[i]->d3d11.smp; + } + for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + d3d11_vs_srvs[i] = 0; + d3d11_vs_smps[i] = 0; + } + for (i = 0; i < num_fs_imgs; i++) { + SOKOL_ASSERT(fs_imgs[i]->d3d11.srv); + SOKOL_ASSERT(fs_imgs[i]->d3d11.smp); + d3d11_fs_srvs[i] = fs_imgs[i]->d3d11.srv; + d3d11_fs_smps[i] = fs_imgs[i]->d3d11.smp; + } + for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + d3d11_fs_srvs[i] = 0; + d3d11_fs_smps[i] = 0; + } + + _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, d3d11_vbs, pip->d3d11.vb_strides, d3d11_vb_offsets); + _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, pip->d3d11.index_format, (UINT)ib_offset); + _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); + _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_smps); + _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); + _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_smps); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.d3d11.ctx && _sg.d3d11.in_pass); + SOKOL_ASSERT(_sg.d3d11.cur_pipeline && _sg.d3d11.cur_pipeline->slot.id == _sg.d3d11.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.d3d11.cur_pipeline->shader && _sg.d3d11.cur_pipeline->shader->slot.id == _sg.d3d11.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size == _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + ID3D11Buffer* cb = _sg.d3d11.cur_pipeline->shader->d3d11.stage[stage_index].cbufs[ub_index]; + SOKOL_ASSERT(cb); + _sg_d3d11_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cb, 0, NULL, data->ptr, 0, 0); +} + +_SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.d3d11.in_pass); + if (_sg.d3d11.use_indexed_draw) { + if (_sg.d3d11.use_instanced_draw) { + _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); + } + else { + _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0); + } + } + else { + if (_sg.d3d11.use_instanced_draw) { + _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); + } + else { + _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); + } + } +} + +_SOKOL_PRIVATE void _sg_d3d11_commit(void) { + SOKOL_ASSERT(!_sg.d3d11.in_pass); +} + +_SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11.buf); + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + if (SUCCEEDED(hr)) { + memcpy(d3d11_msr.pData, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + } else { + SOKOL_LOG("failed to map buffer while updating!\n"); + } +} + +_SOKOL_PRIVATE int _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11.buf); + D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, map_type, 0, &d3d11_msr); + if (SUCCEEDED(hr)) { + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->cmn.append_pos; + memcpy(dst_ptr, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + } else { + SOKOL_LOG("failed to map buffer while appending!\n"); + } + /* NOTE: this alignment is a requirement from WebGPU, but we want identical behaviour across all backend */ + return _sg_roundup((int)data->size, 4); +} + +_SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(img->d3d11.tex2d || img->d3d11.tex3d); + ID3D11Resource* d3d11_res = 0; + if (img->d3d11.tex3d) { + d3d11_res = (ID3D11Resource*) img->d3d11.tex3d; + } + else { + d3d11_res = (ID3D11Resource*) img->d3d11.tex2d; + } + SOKOL_ASSERT(d3d11_res); + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; + UINT subres_index = 0; + HRESULT hr; + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); + const int mip_width = ((img->cmn.width>>mip_index)>0) ? img->cmn.width>>mip_index : 1; + const int mip_height = ((img->cmn.height>>mip_index)>0) ? img->cmn.height>>mip_index : 1; + const int src_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); + const size_t slice_size = subimg_data->size / (size_t)num_slices; + const size_t slice_offset = slice_size * (size_t)slice_index; + const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset; + hr = _sg_d3d11_Map(_sg.d3d11.ctx, d3d11_res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + if (SUCCEEDED(hr)) { + /* FIXME: need to handle difference in depth-pitch for 3D textures as well! */ + if (src_pitch == (int)d3d11_msr.RowPitch) { + memcpy(d3d11_msr.pData, slice_ptr, slice_size); + } + else { + SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch); + const uint8_t* src_ptr = slice_ptr; + uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData; + for (int row_index = 0; row_index < mip_height; row_index++) { + memcpy(dst_ptr, src_ptr, (size_t)src_pitch); + src_ptr += src_pitch; + dst_ptr += d3d11_msr.RowPitch; + } + } + _sg_d3d11_Unmap(_sg.d3d11.ctx, d3d11_res, subres_index); + } else { + SOKOL_LOG("failed to map texture!\n"); + } + } + } + } +} + +/*== METAL BACKEND IMPLEMENTATION ============================================*/ +#elif defined(SOKOL_METAL) + +#if __has_feature(objc_arc) +#define _SG_OBJC_RETAIN(obj) { } +#define _SG_OBJC_RELEASE(obj) { obj = nil; } +#define _SG_OBJC_RELEASE_WITH_NULL(obj) { obj = [NSNull null]; } +#else +#define _SG_OBJC_RETAIN(obj) { [obj retain]; } +#define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#define _SG_OBJC_RELEASE_WITH_NULL(obj) { [obj release]; obj = [NSNull null]; } +#endif + +/*-- enum translation functions ----------------------------------------------*/ +_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_action a) { + switch (a) { + case SG_ACTION_CLEAR: return MTLLoadActionClear; + case SG_ACTION_LOAD: return MTLLoadActionLoad; + case SG_ACTION_DONTCARE: return MTLLoadActionDontCare; + default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; + } +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: +#if defined(_SG_TARGET_MACOS) + return MTLResourceStorageModeManaged; +#else + return MTLResourceStorageModeShared; +#endif + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: +#if defined(_SG_TARGET_MACOS) + return MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; +#else + return MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared; +#endif + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE MTLVertexStepFunction _sg_mtl_step_function(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return MTLVertexStepFunctionPerVertex; + case SG_VERTEXSTEP_PER_INSTANCE: return MTLVertexStepFunctionPerInstance; + default: SOKOL_UNREACHABLE; return (MTLVertexStepFunction)0; + } +} + +_SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return MTLVertexFormatFloat; + case SG_VERTEXFORMAT_FLOAT2: return MTLVertexFormatFloat2; + case SG_VERTEXFORMAT_FLOAT3: return MTLVertexFormatFloat3; + case SG_VERTEXFORMAT_FLOAT4: return MTLVertexFormatFloat4; + case SG_VERTEXFORMAT_BYTE4: return MTLVertexFormatChar4; + case SG_VERTEXFORMAT_BYTE4N: return MTLVertexFormatChar4Normalized; + case SG_VERTEXFORMAT_UBYTE4: return MTLVertexFormatUChar4; + case SG_VERTEXFORMAT_UBYTE4N: return MTLVertexFormatUChar4Normalized; + case SG_VERTEXFORMAT_SHORT2: return MTLVertexFormatShort2; + case SG_VERTEXFORMAT_SHORT2N: return MTLVertexFormatShort2Normalized; + case SG_VERTEXFORMAT_USHORT2N: return MTLVertexFormatUShort2Normalized; + case SG_VERTEXFORMAT_SHORT4: return MTLVertexFormatShort4; + case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized; + case SG_VERTEXFORMAT_USHORT4N: return MTLVertexFormatUShort4Normalized; + case SG_VERTEXFORMAT_UINT10_N2: return MTLVertexFormatUInt1010102Normalized; + default: SOKOL_UNREACHABLE; return (MTLVertexFormat)0; + } +} + +_SOKOL_PRIVATE MTLPrimitiveType _sg_mtl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return MTLPrimitiveTypePoint; + case SG_PRIMITIVETYPE_LINES: return MTLPrimitiveTypeLine; + case SG_PRIMITIVETYPE_LINE_STRIP: return MTLPrimitiveTypeLineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return MTLPrimitiveTypeTriangle; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip; + default: SOKOL_UNREACHABLE; return (MTLPrimitiveType)0; + } +} + +_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return MTLPixelFormatR8Unorm; + case SG_PIXELFORMAT_R8SN: return MTLPixelFormatR8Snorm; + case SG_PIXELFORMAT_R8UI: return MTLPixelFormatR8Uint; + case SG_PIXELFORMAT_R8SI: return MTLPixelFormatR8Sint; + case SG_PIXELFORMAT_R16: return MTLPixelFormatR16Unorm; + case SG_PIXELFORMAT_R16SN: return MTLPixelFormatR16Snorm; + case SG_PIXELFORMAT_R16UI: return MTLPixelFormatR16Uint; + case SG_PIXELFORMAT_R16SI: return MTLPixelFormatR16Sint; + case SG_PIXELFORMAT_R16F: return MTLPixelFormatR16Float; + case SG_PIXELFORMAT_RG8: return MTLPixelFormatRG8Unorm; + case SG_PIXELFORMAT_RG8SN: return MTLPixelFormatRG8Snorm; + case SG_PIXELFORMAT_RG8UI: return MTLPixelFormatRG8Uint; + case SG_PIXELFORMAT_RG8SI: return MTLPixelFormatRG8Sint; + case SG_PIXELFORMAT_R32UI: return MTLPixelFormatR32Uint; + case SG_PIXELFORMAT_R32SI: return MTLPixelFormatR32Sint; + case SG_PIXELFORMAT_R32F: return MTLPixelFormatR32Float; + case SG_PIXELFORMAT_RG16: return MTLPixelFormatRG16Unorm; + case SG_PIXELFORMAT_RG16SN: return MTLPixelFormatRG16Snorm; + case SG_PIXELFORMAT_RG16UI: return MTLPixelFormatRG16Uint; + case SG_PIXELFORMAT_RG16SI: return MTLPixelFormatRG16Sint; + case SG_PIXELFORMAT_RG16F: return MTLPixelFormatRG16Float; + case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatRGBA8Unorm; + case SG_PIXELFORMAT_RGBA8SN: return MTLPixelFormatRGBA8Snorm; + case SG_PIXELFORMAT_RGBA8UI: return MTLPixelFormatRGBA8Uint; + case SG_PIXELFORMAT_RGBA8SI: return MTLPixelFormatRGBA8Sint; + case SG_PIXELFORMAT_BGRA8: return MTLPixelFormatBGRA8Unorm; + case SG_PIXELFORMAT_RGB10A2: return MTLPixelFormatRGB10A2Unorm; + case SG_PIXELFORMAT_RG11B10F: return MTLPixelFormatRG11B10Float; + case SG_PIXELFORMAT_RG32UI: return MTLPixelFormatRG32Uint; + case SG_PIXELFORMAT_RG32SI: return MTLPixelFormatRG32Sint; + case SG_PIXELFORMAT_RG32F: return MTLPixelFormatRG32Float; + case SG_PIXELFORMAT_RGBA16: return MTLPixelFormatRGBA16Unorm; + case SG_PIXELFORMAT_RGBA16SN: return MTLPixelFormatRGBA16Snorm; + case SG_PIXELFORMAT_RGBA16UI: return MTLPixelFormatRGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return MTLPixelFormatRGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return MTLPixelFormatRGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return MTLPixelFormatRGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float; + case SG_PIXELFORMAT_DEPTH: return MTLPixelFormatDepth32Float; + case SG_PIXELFORMAT_DEPTH_STENCIL: return MTLPixelFormatDepth32Float_Stencil8; +#if defined(_SG_TARGET_MACOS) + case SG_PIXELFORMAT_BC1_RGBA: return MTLPixelFormatBC1_RGBA; + case SG_PIXELFORMAT_BC2_RGBA: return MTLPixelFormatBC2_RGBA; + case SG_PIXELFORMAT_BC3_RGBA: return MTLPixelFormatBC3_RGBA; + case SG_PIXELFORMAT_BC4_R: return MTLPixelFormatBC4_RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return MTLPixelFormatBC4_RSnorm; + case SG_PIXELFORMAT_BC5_RG: return MTLPixelFormatBC5_RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return MTLPixelFormatBC5_RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return MTLPixelFormatBC6H_RGBFloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return MTLPixelFormatBC6H_RGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return MTLPixelFormatBC7_RGBAUnorm; +#else + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return MTLPixelFormatPVRTC_RGB_2BPP; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return MTLPixelFormatPVRTC_RGB_4BPP; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return MTLPixelFormatPVRTC_RGBA_2BPP; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return MTLPixelFormatPVRTC_RGBA_4BPP; + case SG_PIXELFORMAT_ETC2_RGB8: return MTLPixelFormatETC2_RGB8; + case SG_PIXELFORMAT_ETC2_RGB8A1: return MTLPixelFormatETC2_RGB8A1; + case SG_PIXELFORMAT_ETC2_RGBA8: return MTLPixelFormatEAC_RGBA8; + case SG_PIXELFORMAT_ETC2_RG11: return MTLPixelFormatEAC_RG11Unorm; + case SG_PIXELFORMAT_ETC2_RG11SN: return MTLPixelFormatEAC_RG11Snorm; +#endif + default: return MTLPixelFormatInvalid; + } +} + +_SOKOL_PRIVATE MTLColorWriteMask _sg_mtl_color_write_mask(sg_color_mask m) { + MTLColorWriteMask mtl_mask = MTLColorWriteMaskNone; + if (m & SG_COLORMASK_R) { + mtl_mask |= MTLColorWriteMaskRed; + } + if (m & SG_COLORMASK_G) { + mtl_mask |= MTLColorWriteMaskGreen; + } + if (m & SG_COLORMASK_B) { + mtl_mask |= MTLColorWriteMaskBlue; + } + if (m & SG_COLORMASK_A) { + mtl_mask |= MTLColorWriteMaskAlpha; + } + return mtl_mask; +} + +_SOKOL_PRIVATE MTLBlendOperation _sg_mtl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return MTLBlendOperationAdd; + case SG_BLENDOP_SUBTRACT: return MTLBlendOperationSubtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; + default: SOKOL_UNREACHABLE; return (MTLBlendOperation)0; + } +} + +_SOKOL_PRIVATE MTLBlendFactor _sg_mtl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return MTLBlendFactorZero; + case SG_BLENDFACTOR_ONE: return MTLBlendFactorOne; + case SG_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; + case SG_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; + case SG_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; + case SG_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return MTLBlendFactorBlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return MTLBlendFactorOneMinusBlendColor; + case SG_BLENDFACTOR_BLEND_ALPHA: return MTLBlendFactorBlendAlpha; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return MTLBlendFactorOneMinusBlendAlpha; + default: SOKOL_UNREACHABLE; return (MTLBlendFactor)0; + } +} + +_SOKOL_PRIVATE MTLCompareFunction _sg_mtl_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return MTLCompareFunctionNever; + case SG_COMPAREFUNC_LESS: return MTLCompareFunctionLess; + case SG_COMPAREFUNC_EQUAL: return MTLCompareFunctionEqual; + case SG_COMPAREFUNC_LESS_EQUAL: return MTLCompareFunctionLessEqual; + case SG_COMPAREFUNC_GREATER: return MTLCompareFunctionGreater; + case SG_COMPAREFUNC_NOT_EQUAL: return MTLCompareFunctionNotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return MTLCompareFunctionGreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return MTLCompareFunctionAlways; + default: SOKOL_UNREACHABLE; return (MTLCompareFunction)0; + } +} + +_SOKOL_PRIVATE MTLStencilOperation _sg_mtl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return MTLStencilOperationKeep; + case SG_STENCILOP_ZERO: return MTLStencilOperationZero; + case SG_STENCILOP_REPLACE: return MTLStencilOperationReplace; + case SG_STENCILOP_INCR_CLAMP: return MTLStencilOperationIncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return MTLStencilOperationDecrementClamp; + case SG_STENCILOP_INVERT: return MTLStencilOperationInvert; + case SG_STENCILOP_INCR_WRAP: return MTLStencilOperationIncrementWrap; + case SG_STENCILOP_DECR_WRAP: return MTLStencilOperationDecrementWrap; + default: SOKOL_UNREACHABLE; return (MTLStencilOperation)0; + } +} + +_SOKOL_PRIVATE MTLCullMode _sg_mtl_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return MTLCullModeNone; + case SG_CULLMODE_FRONT: return MTLCullModeFront; + case SG_CULLMODE_BACK: return MTLCullModeBack; + default: SOKOL_UNREACHABLE; return (MTLCullMode)0; + } +} + +_SOKOL_PRIVATE MTLWinding _sg_mtl_winding(sg_face_winding w) { + switch (w) { + case SG_FACEWINDING_CW: return MTLWindingClockwise; + case SG_FACEWINDING_CCW: return MTLWindingCounterClockwise; + default: SOKOL_UNREACHABLE; return (MTLWinding)0; + } +} + +_SOKOL_PRIVATE MTLIndexType _sg_mtl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_UINT16: return MTLIndexTypeUInt16; + case SG_INDEXTYPE_UINT32: return MTLIndexTypeUInt32; + default: SOKOL_UNREACHABLE; return (MTLIndexType)0; + } +} + +_SOKOL_PRIVATE int _sg_mtl_index_size(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return 2; + case SG_INDEXTYPE_UINT32: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE MTLTextureType _sg_mtl_texture_type(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return MTLTextureType2D; + case SG_IMAGETYPE_CUBE: return MTLTextureTypeCube; + case SG_IMAGETYPE_3D: return MTLTextureType3D; + case SG_IMAGETYPE_ARRAY: return MTLTextureType2DArray; + default: SOKOL_UNREACHABLE; return (MTLTextureType)0; + } +} + +_SOKOL_PRIVATE bool _sg_mtl_is_pvrtc(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + return true; + default: + return false; + } +} + +_SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) { + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; +#if defined(_SG_TARGET_MACOS) + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor; +#else + /* clamp-to-border not supported on iOS, fall back to clamp-to-edge */ + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToEdge; +#endif + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } +} + +#if defined(_SG_TARGET_MACOS) +_SOKOL_PRIVATE MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { + switch (c) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: return MTLSamplerBorderColorTransparentBlack; + case SG_BORDERCOLOR_OPAQUE_BLACK: return MTLSamplerBorderColorOpaqueBlack; + case SG_BORDERCOLOR_OPAQUE_WHITE: return MTLSamplerBorderColorOpaqueWhite; + default: SOKOL_UNREACHABLE; return (MTLSamplerBorderColor)0; + } +} +#endif + +_SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return MTLSamplerMinMagFilterNearest; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return MTLSamplerMinMagFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0; + } +} + +_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mip_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_LINEAR: + return MTLSamplerMipFilterNotMipmapped; + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return MTLSamplerMipFilterNearest; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return MTLSamplerMipFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0; + } +} + +/*-- a pool for all Metal resource objects, with deferred release queue -------*/ + +_SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { + _sg.mtl.idpool.num_slots = 2 * + ( + 2 * desc->buffer_pool_size + + 5 * desc->image_pool_size + + 4 * desc->shader_pool_size + + 2 * desc->pipeline_pool_size + + desc->pass_pool_size + ); + _sg.mtl.idpool.pool = [NSMutableArray arrayWithCapacity:(NSUInteger)_sg.mtl.idpool.num_slots]; + _SG_OBJC_RETAIN(_sg.mtl.idpool.pool); + NSNull* null = [NSNull null]; + for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { + [_sg.mtl.idpool.pool addObject:null]; + } + SOKOL_ASSERT([_sg.mtl.idpool.pool count] == (NSUInteger)_sg.mtl.idpool.num_slots); + /* a queue of currently free slot indices */ + _sg.mtl.idpool.free_queue_top = 0; + _sg.mtl.idpool.free_queue = (int*)SOKOL_MALLOC((size_t)_sg.mtl.idpool.num_slots * sizeof(int)); + /* pool slot 0 is reserved! */ + for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) { + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = i; + } + /* a circular queue which holds release items (frame index + when a resource is to be released, and the resource's + pool index + */ + _sg.mtl.idpool.release_queue_front = 0; + _sg.mtl.idpool.release_queue_back = 0; + _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)SOKOL_MALLOC((size_t)_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); + for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { + _sg.mtl.idpool.release_queue[i].frame_index = 0; + _sg.mtl.idpool.release_queue[i].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { + SOKOL_FREE(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0; + SOKOL_FREE(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0; + _SG_OBJC_RELEASE(_sg.mtl.idpool.pool); +} + +/* get a new free resource pool slot */ +_SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0); + const int slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + return slot_index; +} + +/* put a free resource pool slot back into the free-queue */ +_SOKOL_PRIVATE void _sg_mtl_free_pool_slot(int slot_index) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots); + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index; +} + +/* add an MTLResource to the pool, return pool index or 0 if input was 'nil' */ +_SOKOL_PRIVATE int _sg_mtl_add_resource(id res) { + if (nil == res) { + return _SG_MTL_INVALID_SLOT_INDEX; + } + const int slot_index = _sg_mtl_alloc_pool_slot(); + SOKOL_ASSERT([NSNull null] == _sg.mtl.idpool.pool[(NSUInteger)slot_index]); + _sg.mtl.idpool.pool[(NSUInteger)slot_index] = res; + return slot_index; +} + +/* mark an MTLResource for release, this will put the resource into the + deferred-release queue, and the resource will then be released N frames later, + the special pool index 0 will be ignored (this means that a nil + value was provided to _sg_mtl_add_resource() +*/ +_SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_index) { + if (slot_index == _SG_MTL_INVALID_SLOT_INDEX) { + return; + } + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + SOKOL_ASSERT([NSNull null] != _sg.mtl.idpool.pool[(NSUInteger)slot_index]); + int release_index = _sg.mtl.idpool.release_queue_front++; + if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) { + /* wrap-around */ + _sg.mtl.idpool.release_queue_front = 0; + } + /* release queue full? */ + SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back); + SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index); + const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1; + _sg.mtl.idpool.release_queue[release_index].frame_index = safe_to_release_frame_index; + _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index; +} + +/* run garbage-collection pass on all resources in the release-queue */ +_SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { + while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) { + if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) { + /* don't need to check further, release-items past this are too young */ + break; + } + /* safe to release this resource */ + const int slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + SOKOL_ASSERT(_sg.mtl.idpool.pool[(NSUInteger)slot_index] != [NSNull null]); + _SG_OBJC_RELEASE_WITH_NULL(_sg.mtl.idpool.pool[(NSUInteger)slot_index]); + /* put the now free pool index back on the free queue */ + _sg_mtl_free_pool_slot(slot_index); + /* reset the release queue slot and advance the back index */ + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0; + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + _sg.mtl.idpool.release_queue_back++; + if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { + /* wrap-around */ + _sg.mtl.idpool.release_queue_back = 0; + } + } +} + +_SOKOL_PRIVATE id _sg_mtl_id(int slot_index) { + return _sg.mtl.idpool.pool[(NSUInteger)slot_index]; +} + +_SOKOL_PRIVATE void _sg_mtl_init_sampler_cache(const sg_desc* desc) { + SOKOL_ASSERT(desc->sampler_cache_size > 0); + _sg_smpcache_init(&_sg.mtl.sampler_cache, desc->sampler_cache_size); +} + +/* destroy the sampler cache, and release all sampler objects */ +_SOKOL_PRIVATE void _sg_mtl_destroy_sampler_cache(uint32_t frame_index) { + SOKOL_ASSERT(_sg.mtl.sampler_cache.items); + SOKOL_ASSERT(_sg.mtl.sampler_cache.num_items <= _sg.mtl.sampler_cache.capacity); + for (int i = 0; i < _sg.mtl.sampler_cache.num_items; i++) { + _sg_mtl_release_resource(frame_index, (int)_sg_smpcache_sampler(&_sg.mtl.sampler_cache, i)); + } + _sg_smpcache_discard(&_sg.mtl.sampler_cache); +} + +/* + create and add an MTLSamplerStateObject and return its resource pool index, + reuse identical sampler state if one exists +*/ +_SOKOL_PRIVATE int _sg_mtl_create_sampler(id mtl_device, const sg_image_desc* img_desc) { + SOKOL_ASSERT(img_desc); + int index = _sg_smpcache_find_item(&_sg.mtl.sampler_cache, img_desc); + if (index >= 0) { + /* reuse existing sampler */ + return (int)_sg_smpcache_sampler(&_sg.mtl.sampler_cache, index); + } + else { + /* create a new Metal sampler state object and add to sampler cache */ + MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; + mtl_desc.sAddressMode = _sg_mtl_address_mode(img_desc->wrap_u); + mtl_desc.tAddressMode = _sg_mtl_address_mode(img_desc->wrap_v); + if (SG_IMAGETYPE_3D == img_desc->type) { + mtl_desc.rAddressMode = _sg_mtl_address_mode(img_desc->wrap_w); + } +#if defined(_SG_TARGET_MACOS) + mtl_desc.borderColor = _sg_mtl_border_color(img_desc->border_color); +#endif + mtl_desc.minFilter = _sg_mtl_minmag_filter(img_desc->min_filter); + mtl_desc.magFilter = _sg_mtl_minmag_filter(img_desc->mag_filter); + mtl_desc.mipFilter = _sg_mtl_mip_filter(img_desc->min_filter); + mtl_desc.lodMinClamp = img_desc->min_lod; + mtl_desc.lodMaxClamp = img_desc->max_lod; + mtl_desc.maxAnisotropy = img_desc->max_anisotropy; + mtl_desc.normalizedCoordinates = YES; + id mtl_sampler = [mtl_device newSamplerStateWithDescriptor:mtl_desc]; + _SG_OBJC_RELEASE(mtl_desc); + int sampler_handle = _sg_mtl_add_resource(mtl_sampler); + _sg_smpcache_add_item(&_sg.mtl.sampler_cache, img_desc, (uintptr_t)sampler_handle); + return sampler_handle; + } +} + +_SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { + memset(&_sg.mtl.state_cache, 0, sizeof(_sg.mtl.state_cache)); +} + +/* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ +_SOKOL_PRIVATE void _sg_mtl_init_caps(void) { +#if defined(_SG_TARGET_MACOS) + _sg.backend = SG_BACKEND_METAL_MACOS; +#elif defined(_SG_TARGET_IOS) +#if defined(_SG_TARGET_IOS_SIMULATOR) + _sg.backend = SG_BACKEND_METAL_SIMULATOR; +#else + _sg.backend = SG_BACKEND_METAL_IOS; +#endif +#endif + _sg.features.instancing = true; + _sg.features.origin_top_left = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; +#if defined(_SG_TARGET_MACOS) + _sg.features.image_clamp_to_border = true; +#else + _sg.features.image_clamp_to_border = false; +#endif + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + +#if defined(_SG_TARGET_MACOS) + _sg.limits.max_image_size_2d = 16 * 1024; + _sg.limits.max_image_size_cube = 16 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 16 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; +#else + /* newer iOS devices support 16k textures */ + _sg.limits.max_image_size_2d = 8 * 1024; + _sg.limits.max_image_size_cube = 8 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 8 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; +#endif + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); +#if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); +#else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16SN]); +#endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); +#if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); +#else + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); +#endif +#if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); +#else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16SN]); +#endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); +#if defined(_SG_TARGET_MACOS) + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); +#else + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); +#endif +#if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); +#else + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); +#endif +#if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); +#else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); +#endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); +#if defined(_SG_TARGET_MACOS) + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); +#else + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); +#endif + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); +#if defined(_SG_TARGET_MACOS) + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); +#else + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); +#endif +} + +/*-- main Metal backend state and functions ----------------------------------*/ +_SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { + /* assume already zero-initialized */ + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->context.metal.device); + SOKOL_ASSERT(desc->context.metal.renderpass_descriptor_cb || desc->context.metal.renderpass_descriptor_userdata_cb); + SOKOL_ASSERT(desc->context.metal.drawable_cb || desc->context.metal.drawable_userdata_cb); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + _sg_mtl_init_pool(desc); + _sg_mtl_init_sampler_cache(desc); + _sg_mtl_clear_state_cache(); + _sg.mtl.valid = true; + _sg.mtl.renderpass_descriptor_cb = desc->context.metal.renderpass_descriptor_cb; + _sg.mtl.renderpass_descriptor_userdata_cb = desc->context.metal.renderpass_descriptor_userdata_cb; + _sg.mtl.drawable_cb = desc->context.metal.drawable_cb; + _sg.mtl.drawable_userdata_cb = desc->context.metal.drawable_userdata_cb; + _sg.mtl.user_data = desc->context.metal.user_data; + _sg.mtl.frame_index = 1; + _sg.mtl.ub_size = desc->uniform_buffer_size; + _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); + _sg.mtl.device = (__bridge id) desc->context.metal.device; + _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue]; + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _sg.mtl.uniform_buffers[i] = [_sg.mtl.device + newBufferWithLength:(NSUInteger)_sg.mtl.ub_size + options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared + ]; + } + _sg_mtl_init_caps(); +} + +_SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { + SOKOL_ASSERT(_sg.mtl.valid); + /* wait for the last frame to finish */ + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + } + /* semaphore must be "relinquished" before destruction */ + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_signal(_sg.mtl.sem); + } + _sg_mtl_destroy_sampler_cache(_sg.mtl.frame_index); + _sg_mtl_garbage_collect(_sg.mtl.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); + _sg_mtl_destroy_pool(); + _sg.mtl.valid = false; + + _SG_OBJC_RELEASE(_sg.mtl.sem); + _SG_OBJC_RELEASE(_sg.mtl.device); + _SG_OBJC_RELEASE(_sg.mtl.cmd_queue); + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _SG_OBJC_RELEASE(_sg.mtl.uniform_buffers[i]); + } + /* NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released */ + _sg.mtl.cmd_buffer = nil; + _sg.mtl.cmd_encoder = nil; +} + +_SOKOL_PRIVATE void _sg_mtl_bind_uniform_buffers(void) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + for (int slot = 0; slot < SG_MAX_SHADERSTAGE_UBS; slot++) { + [_sg.mtl.cmd_encoder + setVertexBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:(NSUInteger)slot]; + [_sg.mtl.cmd_encoder + setFragmentBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:(NSUInteger)slot]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) { + _sg_mtl_clear_state_cache(); + + /* need to restore the uniform buffer binding (normally happens in + _sg_mtl_begin_pass() + */ + if (nil != _sg.mtl.cmd_encoder) { + _sg_mtl_bind_uniform_buffers(); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + /* empty */ +} + +_SOKOL_PRIVATE void _sg_mtl_activate_context(_sg_context_t* ctx) { + _SOKOL_UNUSED(ctx); + _sg_mtl_clear_state_cache(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _sg_buffer_common_init(&buf->cmn, desc); + const bool injected = (0 != desc->mtl_buffers[0]); + MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + id mtl_buf; + if (injected) { + SOKOL_ASSERT(desc->mtl_buffers[slot]); + mtl_buf = (__bridge id) desc->mtl_buffers[slot]; + } + else { + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + mtl_buf = [_sg.mtl.device newBufferWithBytes:desc->data.ptr length:(NSUInteger)buf->cmn.size options:mtl_options]; + } + else { + mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options]; + } + } + buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + /* it's valid to call release resource with '0' */ + _sg_mtl_release_resource(_sg.mtl.frame_index, buf->mtl.buf[slot]); + } +} + +_SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_image_data* data) { + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); + SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); + const uint8_t* data_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; + const int mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const int mip_height = _sg_max(img->cmn.height >> mip_index, 1); + /* special case PVRTC formats: bytePerRow and bytesPerImage must be 0 */ + int bytes_per_row = 0; + int bytes_per_slice = 0; + if (!_sg_mtl_is_pvrtc(img->cmn.pixel_format)) { + bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + } + /* bytesPerImage special case: https://developer.apple.com/documentation/metal/mtltexture/1515679-replaceregion + + "Supply a nonzero value only when you copy data to a MTLTextureType3D type texture" + */ + MTLRegion region; + int bytes_per_image; + if (img->cmn.type == SG_IMAGETYPE_3D) { + const int mip_depth = _sg_max(img->cmn.num_slices >> mip_index, 1); + region = MTLRegionMake3D(0, 0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height, (NSUInteger)mip_depth); + bytes_per_image = bytes_per_slice; + /* FIXME: apparently the minimal bytes_per_image size for 3D texture + is 4 KByte... somehow need to handle this */ + } + else { + region = MTLRegionMake2D(0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height); + bytes_per_image = 0; + } + + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + const int mtl_slice_index = (img->cmn.type == SG_IMAGETYPE_CUBE) ? face_index : slice_index; + const int slice_offset = slice_index * bytes_per_slice; + SOKOL_ASSERT((slice_offset + bytes_per_slice) <= (int)data->subimage[face_index][mip_index].size); + [mtl_tex replaceRegion:region + mipmapLevel:(NSUInteger)mip_index + slice:(NSUInteger)mtl_slice_index + withBytes:data_ptr + slice_offset + bytesPerRow:(NSUInteger)bytes_per_row + bytesPerImage:(NSUInteger)bytes_per_image]; + } + } + } +} + +/* + FIXME: METAL RESOURCE STORAGE MODE FOR macOS AND iOS + + For immutable textures on macOS, the recommended procedure is to create + a MTLStorageModeManaged texture with the immutable content first, + and then use the GPU to blit the content into a MTLStorageModePrivate + texture before the first use. + + On iOS use the same one-time-blit procedure, but from a + MTLStorageModeShared to a MTLStorageModePrivate texture. + + It probably makes sense to handle this in a separate 'resource manager' + with a recycable pool of blit-source-textures? +*/ + +/* initialize MTLTextureDescritor with common attributes */ +_SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + mtl_desc.textureType = _sg_mtl_texture_type(img->cmn.type); + mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format); + if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { + SOKOL_LOG("Unsupported texture pixel format!\n"); + return false; + } + mtl_desc.width = (NSUInteger)img->cmn.width; + mtl_desc.height = (NSUInteger)img->cmn.height; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mtl_desc.depth = (NSUInteger)img->cmn.num_slices; + } + else { + mtl_desc.depth = 1; + } + mtl_desc.mipmapLevelCount = (NSUInteger)img->cmn.num_mipmaps; + if (SG_IMAGETYPE_ARRAY == img->cmn.type) { + mtl_desc.arrayLength = (NSUInteger)img->cmn.num_slices; + } + else { + mtl_desc.arrayLength = 1; + } + mtl_desc.usage = MTLTextureUsageShaderRead; + MTLResourceOptions res_options = 0; + if (img->cmn.usage != SG_USAGE_IMMUTABLE) { + res_options |= MTLResourceCPUCacheModeWriteCombined; + } +#if defined(_SG_TARGET_MACOS) + /* macOS: use managed textures */ + res_options |= MTLResourceStorageModeManaged; +#else + /* iOS: use CPU/GPU shared memory */ + res_options |= MTLResourceStorageModeShared; +#endif + mtl_desc.resourceOptions = res_options; + return true; +} + +/* initialize MTLTextureDescritor with rendertarget attributes */ +_SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + SOKOL_ASSERT(img->cmn.render_target); + _SOKOL_UNUSED(img); + /* render targets are only visible to the GPU */ + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; + /* non-MSAA render targets are shader-readable */ + mtl_desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; +} + +/* initialize MTLTextureDescritor with MSAA attributes */ +_SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + SOKOL_ASSERT(img->cmn.sample_count > 1); + /* render targets are only visible to the GPU */ + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; + /* MSAA render targets are not shader-readable (instead they are resolved) */ + mtl_desc.usage = MTLTextureUsageRenderTarget; + mtl_desc.textureType = MTLTextureType2DMultisample; + mtl_desc.depth = 1; + mtl_desc.arrayLength = 1; + mtl_desc.mipmapLevelCount = 1; + mtl_desc.sampleCount = (NSUInteger)img->cmn.sample_count; +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _sg_image_common_init(&img->cmn, desc); + const bool injected = (0 != desc->mtl_textures[0]); + const bool msaa = (img->cmn.sample_count > 1); + + /* first initialize all Metal resource pool slots to 'empty' */ + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + img->mtl.tex[i] = _sg_mtl_add_resource(nil); + } + img->mtl.sampler_state = _sg_mtl_add_resource(nil); + img->mtl.depth_tex = _sg_mtl_add_resource(nil); + img->mtl.msaa_tex = _sg_mtl_add_resource(nil); + + /* initialize a Metal texture descriptor with common attributes */ + MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init]; + if (!_sg_mtl_init_texdesc_common(mtl_desc, img)) { + _SG_OBJC_RELEASE(mtl_desc); + return SG_RESOURCESTATE_FAILED; + } + + /* special case depth-stencil-buffer? */ + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { + /* depth-stencil buffer texture must always be a render target */ + SOKOL_ASSERT(img->cmn.render_target); + SOKOL_ASSERT(img->cmn.type == SG_IMAGETYPE_2D); + SOKOL_ASSERT(img->cmn.num_mipmaps == 1); + SOKOL_ASSERT(!injected); + if (msaa) { + _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); + } + else { + _sg_mtl_init_texdesc_rt(mtl_desc, img); + } + id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + SOKOL_ASSERT(nil != tex); + img->mtl.depth_tex = _sg_mtl_add_resource(tex); + } + else { + /* create the color texture + In case this is a render target without MSAA, add the relevant + render-target descriptor attributes. + In case this is a render target *with* MSAA, the color texture + will serve as MSAA-resolve target (not as render target), and rendering + will go into a separate render target texture of type + MTLTextureType2DMultisample. + */ + if (img->cmn.render_target && !msaa) { + _sg_mtl_init_texdesc_rt(mtl_desc, img); + } + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + id tex; + if (injected) { + SOKOL_ASSERT(desc->mtl_textures[slot]); + tex = (__bridge id) desc->mtl_textures[slot]; + } + else { + tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_mtl_copy_image_data(img, tex, &desc->data); + } + } + img->mtl.tex[slot] = _sg_mtl_add_resource(tex); + } + + /* if MSAA color render target, create an additional MSAA render-surface texture */ + if (img->cmn.render_target && msaa) { + _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); + id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + img->mtl.msaa_tex = _sg_mtl_add_resource(tex); + } + + /* create (possibly shared) sampler state */ + img->mtl.sampler_state = _sg_mtl_create_sampler(_sg.mtl.device, desc); + } + _SG_OBJC_RELEASE(mtl_desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + /* it's valid to call release resource with a 'null resource' */ + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.tex[slot]); + } + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.depth_tex); + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.msaa_tex); + /* NOTE: sampler state objects are shared and not released until shutdown */ +} + +_SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { + NSError* err = NULL; + id lib = [_sg.mtl.device + newLibraryWithSource:[NSString stringWithUTF8String:src] + options:nil + error:&err + ]; + if (err) { + SOKOL_LOG([err.localizedDescription UTF8String]); + } + return lib; +} + +_SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, size_t num_bytes) { + NSError* err = NULL; + dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + id lib = [_sg.mtl.device newLibraryWithData:lib_data error:&err]; + if (err) { + SOKOL_LOG([err.localizedDescription UTF8String]); + } + _SG_OBJC_RELEASE(lib_data); + return lib; +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + + _sg_shader_common_init(&shd->cmn, desc); + + /* create metal libray objects and lookup entry functions */ + id vs_lib; + id fs_lib; + id vs_func; + id fs_func; + const char* vs_entry = desc->vs.entry; + const char* fs_entry = desc->fs.entry; + if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { + /* separate byte code provided */ + vs_lib = _sg_mtl_library_from_bytecode(desc->vs.bytecode.ptr, desc->vs.bytecode.size); + fs_lib = _sg_mtl_library_from_bytecode(desc->fs.bytecode.ptr, desc->fs.bytecode.size); + if (nil == vs_lib || nil == fs_lib) { + return SG_RESOURCESTATE_FAILED; + } + vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; + fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; + } + else if (desc->vs.source && desc->fs.source) { + /* separate sources provided */ + vs_lib = _sg_mtl_compile_library(desc->vs.source); + fs_lib = _sg_mtl_compile_library(desc->fs.source); + if (nil == vs_lib || nil == fs_lib) { + return SG_RESOURCESTATE_FAILED; + } + vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; + fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; + } + else { + return SG_RESOURCESTATE_FAILED; + } + if (nil == vs_func) { + SOKOL_LOG("vertex shader entry function not found\n"); + return SG_RESOURCESTATE_FAILED; + } + if (nil == fs_func) { + SOKOL_LOG("fragment shader entry function not found\n"); + return SG_RESOURCESTATE_FAILED; + } + /* it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index */ + shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); + shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); + shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func = _sg_mtl_add_resource(vs_func); + shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func = _sg_mtl_add_resource(fs_func); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + /* it is valid to call _sg_mtl_release_resource with a 'null resource' */ + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + + sg_primitive_type prim_type = desc->primitive_type; + pip->mtl.prim_type = _sg_mtl_primitive_type(prim_type); + pip->mtl.index_size = _sg_mtl_index_size(pip->cmn.index_type); + if (SG_INDEXTYPE_NONE != pip->cmn.index_type) { + pip->mtl.index_type = _sg_mtl_index_type(pip->cmn.index_type); + } + pip->mtl.cull_mode = _sg_mtl_cull_mode(desc->cull_mode); + pip->mtl.winding = _sg_mtl_winding(desc->face_winding); + pip->mtl.stencil_ref = desc->stencil.ref; + + /* create vertex-descriptor */ + MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; + for (NSUInteger attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_desc->format); + vtx_desc.attributes[attr_index].offset = (NSUInteger)a_desc->offset; + vtx_desc.attributes[attr_index].bufferIndex = (NSUInteger)(a_desc->buffer_index + SG_MAX_SHADERSTAGE_UBS); + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + } + for (NSUInteger layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { + if (pip->cmn.vertex_layout_valid[layout_index]) { + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; + const NSUInteger mtl_vb_slot = layout_index + SG_MAX_SHADERSTAGE_UBS; + SOKOL_ASSERT(l_desc->stride > 0); + vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_desc->stride; + vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_desc->step_func); + vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_desc->step_rate; + if (SG_VERTEXSTEP_PER_INSTANCE == l_desc->step_func) { + // NOTE: not actually used in _sg_mtl_draw() + pip->cmn.use_instanced_draw = true; + } + } + } + + /* render-pipeline descriptor */ + MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; + rp_desc.vertexDescriptor = vtx_desc; + SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.vertexFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.fragmentFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); + rp_desc.sampleCount = (NSUInteger)desc->sample_count; + rp_desc.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled; + rp_desc.alphaToOneEnabled = NO; + rp_desc.rasterizationEnabled = YES; + rp_desc.depthAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); + if (desc->depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL) { + rp_desc.stencilAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); + } + /* FIXME: this only works on macOS 10.13! + for (int i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_SHADERSTAGE_BUFFERS); i++) { + rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + } + */ + for (NSUInteger i = 0; i < (NSUInteger)desc->color_count; i++) { + SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); + const sg_color_state* cs = &desc->colors[i]; + rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_pixel_format(cs->pixel_format); + rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask(cs->write_mask); + rp_desc.colorAttachments[i].blendingEnabled = cs->blend.enabled; + rp_desc.colorAttachments[i].alphaBlendOperation = _sg_mtl_blend_op(cs->blend.op_alpha); + rp_desc.colorAttachments[i].rgbBlendOperation = _sg_mtl_blend_op(cs->blend.op_rgb); + rp_desc.colorAttachments[i].destinationAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_alpha); + rp_desc.colorAttachments[i].destinationRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_rgb); + rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_alpha); + rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_rgb); + } + NSError* err = NULL; + id mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; + _SG_OBJC_RELEASE(rp_desc); + if (nil == mtl_rps) { + SOKOL_ASSERT(err); + SOKOL_LOG([err.localizedDescription UTF8String]); + return SG_RESOURCESTATE_FAILED; + } + + /* depth-stencil-state */ + MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init]; + ds_desc.depthCompareFunction = _sg_mtl_compare_func(desc->depth.compare); + ds_desc.depthWriteEnabled = desc->depth.write_enabled; + if (desc->stencil.enabled) { + const sg_stencil_face_state* sb = &desc->stencil.back; + ds_desc.backFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.backFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sb->fail_op); + ds_desc.backFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sb->depth_fail_op); + ds_desc.backFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sb->pass_op); + ds_desc.backFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sb->compare); + ds_desc.backFaceStencil.readMask = desc->stencil.read_mask; + ds_desc.backFaceStencil.writeMask = desc->stencil.write_mask; + const sg_stencil_face_state* sf = &desc->stencil.front; + ds_desc.frontFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.frontFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sf->fail_op); + ds_desc.frontFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sf->depth_fail_op); + ds_desc.frontFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sf->pass_op); + ds_desc.frontFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sf->compare); + ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask; + ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask; + } + id mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc]; + _SG_OBJC_RELEASE(ds_desc); + pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); + pip->mtl.dss = _sg_mtl_add_resource(mtl_dss); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + /* it's valid to call release resource with a 'null resource' */ + _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.rps); + _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.dss); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + + _sg_pass_common_init(&pass->cmn, desc); + + /* copy image pointers */ + const sg_pass_attachment_desc* att_desc; + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->mtl.color_atts[i].image); + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); + pass->mtl.color_atts[i].image = att_images[i]; + } + } + SOKOL_ASSERT(0 == pass->mtl.ds_att.image); + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + pass->mtl.ds_att.image = att_images[ds_img_index]; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + _SOKOL_UNUSED(pass); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->mtl.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->mtl.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.mtl.in_pass); + SOKOL_ASSERT(_sg.mtl.cmd_queue); + SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.mtl.renderpass_descriptor_cb || _sg.mtl.renderpass_descriptor_userdata_cb); + _sg.mtl.in_pass = true; + _sg.mtl.cur_width = w; + _sg.mtl.cur_height = h; + _sg_mtl_clear_state_cache(); + + /* if this is the first pass in the frame, create a command buffer */ + if (nil == _sg.mtl.cmd_buffer) { + /* block until the oldest frame in flight has finished */ + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; + [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buffer) { + // NOTE: this code is called on a different thread! + _SOKOL_UNUSED(cmd_buffer); + dispatch_semaphore_signal(_sg.mtl.sem); + }]; + } + + /* if this is first pass in frame, get uniform buffer base pointer */ + if (0 == _sg.mtl.cur_ub_base_ptr) { + _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; + } + + /* initialize a render pass descriptor */ + MTLRenderPassDescriptor* pass_desc = nil; + if (pass) { + /* offscreen render pass */ + pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; + } + else { + /* default render pass, call user-provided callback to provide render pass descriptor */ + if (_sg.mtl.renderpass_descriptor_cb) { + pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_cb(); + } + else { + pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_userdata_cb(_sg.mtl.user_data); + } + + } + if (pass_desc) { + _sg.mtl.pass_valid = true; + } + else { + /* default pass descriptor will not be valid if window is minimized, + don't do any rendering in this case */ + _sg.mtl.pass_valid = false; + return; + } + if (pass) { + /* setup pass descriptor for offscreen rendering */ + SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); + for (NSUInteger i = 0; i < (NSUInteger)pass->cmn.num_color_atts; i++) { + const _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; + const _sg_mtl_attachment_t* mtl_att = &pass->mtl.color_atts[i]; + const _sg_image_t* att_img = mtl_att->image; + SOKOL_ASSERT(att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(att_img->slot.id == cmn_att->image_id.id); + const bool is_msaa = (att_img->cmn.sample_count > 1); + pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].action); + pass_desc.colorAttachments[i].storeAction = is_msaa ? MTLStoreActionMultisampleResolve : MTLStoreActionStore; + sg_color c = action->colors[i].value; + pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); + if (is_msaa) { + SOKOL_ASSERT(att_img->mtl.msaa_tex != _SG_MTL_INVALID_SLOT_INDEX); + SOKOL_ASSERT(att_img->mtl.tex[mtl_att->image->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].texture = _sg_mtl_id(att_img->mtl.msaa_tex); + pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(att_img->mtl.tex[att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].resolveLevel = (NSUInteger)cmn_att->mip_level; + switch (att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)cmn_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)cmn_att->slice; + break; + default: break; + } + } + else { + SOKOL_ASSERT(att_img->mtl.tex[att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].texture = _sg_mtl_id(att_img->mtl.tex[att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].level = (NSUInteger)cmn_att->mip_level; + switch (att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].slice = (NSUInteger)cmn_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].depthPlane = (NSUInteger)cmn_att->slice; + break; + default: break; + } + } + } + const _sg_image_t* ds_att_img = pass->mtl.ds_att.image; + if (0 != ds_att_img) { + SOKOL_ASSERT(ds_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(ds_att_img->slot.id == pass->cmn.ds_att.image_id.id); + SOKOL_ASSERT(ds_att_img->mtl.depth_tex != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.depthAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); + pass_desc.depthAttachment.clearDepth = action->depth.value; + if (_sg_is_depth_stencil_format(ds_att_img->cmn.pixel_format)) { + pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); + pass_desc.stencilAttachment.clearStencil = action->stencil.value; + } + } + } + else { + /* setup pass descriptor for default rendering */ + pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].action); + sg_color c = action->colors[0].value; + pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); + pass_desc.depthAttachment.clearDepth = action->depth.value; + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); + pass_desc.stencilAttachment.clearStencil = action->stencil.value; + } + + /* create a render command encoder, this might return nil if window is minimized */ + _sg.mtl.cmd_encoder = [_sg.mtl.cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; + if (nil == _sg.mtl.cmd_encoder) { + _sg.mtl.pass_valid = false; + return; + } + + /* bind the global uniform buffer, this only happens once per pass */ + _sg_mtl_bind_uniform_buffers(); +} + +_SOKOL_PRIVATE void _sg_mtl_end_pass(void) { + SOKOL_ASSERT(_sg.mtl.in_pass); + _sg.mtl.in_pass = false; + _sg.mtl.pass_valid = false; + if (nil != _sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder endEncoding]; + /* NOTE: MTLRenderCommandEncoder is autoreleased */ + _sg.mtl.cmd_encoder = nil; + } +} + +_SOKOL_PRIVATE void _sg_mtl_commit(void) { + SOKOL_ASSERT(!_sg.mtl.in_pass); + SOKOL_ASSERT(!_sg.mtl.pass_valid); + SOKOL_ASSERT(_sg.mtl.drawable_cb || _sg.mtl.drawable_userdata_cb); + SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); + + /* present, commit and signal semaphore when done */ + id cur_drawable = nil; + if (_sg.mtl.drawable_cb) { + cur_drawable = (__bridge id) _sg.mtl.drawable_cb(); + } + else { + cur_drawable = (__bridge id) _sg.mtl.drawable_userdata_cb(_sg.mtl.user_data); + } + if (nil != cur_drawable) { + [_sg.mtl.cmd_buffer presentDrawable:cur_drawable]; + } + [_sg.mtl.cmd_buffer commit]; + + /* garbage-collect resources pending for release */ + _sg_mtl_garbage_collect(_sg.mtl.frame_index); + + /* rotate uniform buffer slot */ + if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) { + _sg.mtl.cur_frame_rotate_index = 0; + } + _sg.mtl.frame_index++; + _sg.mtl.cur_ub_offset = 0; + _sg.mtl.cur_ub_base_ptr = 0; + /* NOTE: MTLCommandBuffer is autoreleased */ + _sg.mtl.cmd_buffer = nil; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + MTLViewport vp; + vp.originX = (double) x; + vp.originY = (double) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); + vp.width = (double) w; + vp.height = (double) h; + vp.znear = 0.0; + vp.zfar = 1.0; + [_sg.mtl.cmd_encoder setViewport:vp]; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + /* clip against framebuffer rect */ + x = _sg_min(_sg_max(0, x), _sg.mtl.cur_width-1); + y = _sg_min(_sg_max(0, y), _sg.mtl.cur_height-1); + if ((x + w) > _sg.mtl.cur_width) { + w = _sg.mtl.cur_width - x; + } + if ((y + h) > _sg.mtl.cur_height) { + h = _sg.mtl.cur_height - y; + } + w = _sg_max(w, 1); + h = _sg_max(h, 1); + + MTLScissorRect r; + r.x = (NSUInteger)x; + r.y = (NSUInteger) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); + r.width = (NSUInteger)w; + r.height = (NSUInteger)h; + [_sg.mtl.cmd_encoder setScissorRect:r]; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + + if ((_sg.mtl.state_cache.cur_pipeline != pip) || (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id)) { + _sg.mtl.state_cache.cur_pipeline = pip; + _sg.mtl.state_cache.cur_pipeline_id.id = pip->slot.id; + sg_color c = pip->cmn.blend_color; + [_sg.mtl.cmd_encoder setBlendColorRed:c.r green:c.g blue:c.b alpha:c.a]; + [_sg.mtl.cmd_encoder setCullMode:pip->mtl.cull_mode]; + [_sg.mtl.cmd_encoder setFrontFacingWinding:pip->mtl.winding]; + [_sg.mtl.cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref]; + [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth_bias slopeScale:pip->cmn.depth_bias_slope_scale clamp:pip->cmn.depth_bias_clamp]; + SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)]; + SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setDepthStencilState:_sg_mtl_id(pip->mtl.dss)]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + _SOKOL_UNUSED(pip); + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + + /* store index buffer binding, this will be needed later in sg_draw() */ + _sg.mtl.state_cache.cur_indexbuffer = ib; + _sg.mtl.state_cache.cur_indexbuffer_offset = ib_offset; + if (ib) { + SOKOL_ASSERT(pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = ib->slot.id; + } + else { + SOKOL_ASSERT(pip->cmn.index_type == SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; + } + + /* apply vertex buffers */ + NSUInteger slot; + for (slot = 0; slot < (NSUInteger)num_vbs; slot++) { + const _sg_buffer_t* vb = vbs[slot]; + if ((_sg.mtl.state_cache.cur_vertexbuffers[slot] != vb) || + (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offsets[slot]) || + (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id)) + { + _sg.mtl.state_cache.cur_vertexbuffers[slot] = vb; + _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offsets[slot]; + _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; + SOKOL_ASSERT(vb->mtl.buf[vb->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[vb->cmn.active_slot]) + offset:(NSUInteger)vb_offsets[slot] + atIndex:mtl_slot]; + } + } + + /* apply vertex shader images */ + for (slot = 0; slot < (NSUInteger)num_vs_imgs; slot++) { + const _sg_image_t* img = vs_imgs[slot]; + if ((_sg.mtl.state_cache.cur_vs_images[slot] != img) || (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id)) { + _sg.mtl.state_cache.cur_vs_images[slot] = img; + _sg.mtl.state_cache.cur_vs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + SOKOL_ASSERT(img->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexSamplerState:_sg_mtl_id(img->mtl.sampler_state) atIndex:slot]; + } + } + + /* apply fragment shader images */ + for (slot = 0; slot < (NSUInteger)num_fs_imgs; slot++) { + const _sg_image_t* img = fs_imgs[slot]; + if ((_sg.mtl.state_cache.cur_fs_images[slot] != img) || (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id)) { + _sg.mtl.state_cache.cur_fs_images[slot] = img; + _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + SOKOL_ASSERT(img->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentSamplerState:_sg_mtl_id(img->mtl.sampler_state) atIndex:slot]; + } + } +} + +_SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(((size_t)_sg.mtl.cur_ub_offset + data->size) <= (size_t)_sg.mtl.ub_size); + SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && _sg.mtl.state_cache.cur_pipeline->shader); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->shader->slot.id == _sg.mtl.state_cache.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size <= _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + + /* copy to global uniform buffer, record offset into cmd encoder, and advance offset */ + uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset]; + memcpy(dst, data->ptr, data->size); + if (stage_index == SG_SHADERSTAGE_VS) { + [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; + } + else { + [_sg.mtl.cmd_encoder setFragmentBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; + } + _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + (int)data->size, _SG_MTL_UB_ALIGN); +} + +_SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && (_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id)); + if (SG_INDEXTYPE_NONE != _sg.mtl.state_cache.cur_pipeline->cmn.index_type) { + /* indexed rendering */ + SOKOL_ASSERT(_sg.mtl.state_cache.cur_indexbuffer && (_sg.mtl.state_cache.cur_indexbuffer->slot.id == _sg.mtl.state_cache.cur_indexbuffer_id.id)); + const _sg_buffer_t* ib = _sg.mtl.state_cache.cur_indexbuffer; + SOKOL_ASSERT(ib->mtl.buf[ib->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger index_buffer_offset = (NSUInteger) (_sg.mtl.state_cache.cur_indexbuffer_offset + base_element * _sg.mtl.state_cache.cur_pipeline->mtl.index_size); + [_sg.mtl.cmd_encoder drawIndexedPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type + indexCount:(NSUInteger)num_elements + indexType:_sg.mtl.state_cache.cur_pipeline->mtl.index_type + indexBuffer:_sg_mtl_id(ib->mtl.buf[ib->cmn.active_slot]) + indexBufferOffset:index_buffer_offset + instanceCount:(NSUInteger)num_instances]; + } + else { + /* non-indexed rendering */ + [_sg.mtl.cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type + vertexStart:(NSUInteger)base_element + vertexCount:(NSUInteger)num_elements + instanceCount:(NSUInteger)num_instances]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); + void* dst_ptr = [mtl_buf contents]; + memcpy(dst_ptr, data->ptr, data->size); +#if defined(_SG_TARGET_MACOS) + [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; +#endif +} + +_SOKOL_PRIVATE int _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); + uint8_t* dst_ptr = (uint8_t*) [mtl_buf contents]; + dst_ptr += buf->cmn.append_pos; + memcpy(dst_ptr, data->ptr, data->size); +#if defined(_SG_TARGET_MACOS) + [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; +#endif + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backends */ + return _sg_roundup((int)data->size, 4); +} + +_SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } + __unsafe_unretained id mtl_tex = _sg_mtl_id(img->mtl.tex[img->cmn.active_slot]); + _sg_mtl_copy_image_data(img, mtl_tex, data); +} + +/*== WEBGPU BACKEND IMPLEMENTATION ===========================================*/ +#elif defined(SOKOL_WGPU) + +_SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_usage u) { + WGPUBufferUsageFlags res = 0; + if (SG_BUFFERTYPE_VERTEXBUFFER == t) { + res |= WGPUBufferUsage_Vertex; + } + else { + res |= WGPUBufferUsage_Index; + } + if (SG_USAGE_IMMUTABLE != u) { + res |= WGPUBufferUsage_CopyDst; + } + return res; +} + +_SOKOL_PRIVATE WGPULoadOp _sg_wgpu_load_op(sg_action a) { + switch (a) { + case SG_ACTION_CLEAR: + case SG_ACTION_DONTCARE: + return WGPULoadOp_Clear; + case SG_ACTION_LOAD: + return WGPULoadOp_Load; + default: + SOKOL_UNREACHABLE; + return (WGPULoadOp)0; + } +} + +_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_tex_viewdim(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return WGPUTextureViewDimension_2D; + case SG_IMAGETYPE_CUBE: return WGPUTextureViewDimension_Cube; + case SG_IMAGETYPE_3D: return WGPUTextureViewDimension_3D; + case SG_IMAGETYPE_ARRAY: return WGPUTextureViewDimension_2DArray; + default: SOKOL_UNREACHABLE; return WGPUTextureViewDimension_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_sampler_type t) { + switch (t) { + case SG_SAMPLERTYPE_FLOAT: return WGPUTextureComponentType_Float; + case SG_SAMPLERTYPE_SINT: return WGPUTextureComponentType_Sint; + case SG_SAMPLERTYPE_UINT: return WGPUTextureComponentType_Uint; + default: SOKOL_UNREACHABLE; return WGPUTextureComponentType_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_tex_dim(sg_image_type t) { + if (SG_IMAGETYPE_3D == t) { + return WGPUTextureDimension_3D; + } + else { + return WGPUTextureDimension_2D; + } +} + +_SOKOL_PRIVATE WGPUAddressMode _sg_wgpu_sampler_addrmode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: + return WGPUAddressMode_Repeat; + case SG_WRAP_CLAMP_TO_EDGE: + case SG_WRAP_CLAMP_TO_BORDER: + return WGPUAddressMode_ClampToEdge; + case SG_WRAP_MIRRORED_REPEAT: + return WGPUAddressMode_MirrorRepeat; + default: + SOKOL_UNREACHABLE; + return WGPUAddressMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmagfilter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return WGPUFilterMode_Nearest; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return WGPUFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_mipfilter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_LINEAR: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return WGPUFilterMode_Nearest; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return WGPUFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) { + /* NOTE: there's no WGPUIndexFormat_None */ + return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32; +} + +_SOKOL_PRIVATE WGPUInputStepMode _sg_wgpu_stepmode(sg_vertex_step s) { + return (s == SG_VERTEXSTEP_PER_VERTEX) ? WGPUInputStepMode_Vertex : WGPUInputStepMode_Instance; +} + +_SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) { + switch (f) { + case SG_VERTEXFORMAT_FLOAT: return WGPUVertexFormat_Float; + case SG_VERTEXFORMAT_FLOAT2: return WGPUVertexFormat_Float2; + case SG_VERTEXFORMAT_FLOAT3: return WGPUVertexFormat_Float3; + case SG_VERTEXFORMAT_FLOAT4: return WGPUVertexFormat_Float4; + case SG_VERTEXFORMAT_BYTE4: return WGPUVertexFormat_Char4; + case SG_VERTEXFORMAT_BYTE4N: return WGPUVertexFormat_Char4Norm; + case SG_VERTEXFORMAT_UBYTE4: return WGPUVertexFormat_UChar4; + case SG_VERTEXFORMAT_UBYTE4N: return WGPUVertexFormat_UChar4Norm; + case SG_VERTEXFORMAT_SHORT2: return WGPUVertexFormat_Short2; + case SG_VERTEXFORMAT_SHORT2N: return WGPUVertexFormat_Short2Norm; + case SG_VERTEXFORMAT_USHORT2N: return WGPUVertexFormat_UShort2Norm; + case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Short4; + case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Short4Norm; + case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_UShort4Norm; + /* FIXME! UINT10_N2 */ + case SG_VERTEXFORMAT_UINT10_N2: + default: + SOKOL_UNREACHABLE; + return WGPUVertexFormat_Force32; + } +} + +_SOKOL_PRIVATE WGPUPrimitiveTopology _sg_wgpu_topology(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return WGPUPrimitiveTopology_PointList; + case SG_PRIMITIVETYPE_LINES: return WGPUPrimitiveTopology_LineList; + case SG_PRIMITIVETYPE_LINE_STRIP: return WGPUPrimitiveTopology_LineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return WGPUPrimitiveTopology_TriangleList; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return WGPUPrimitiveTopology_TriangleStrip; + default: SOKOL_UNREACHABLE; return WGPUPrimitiveTopology_Force32; + } +} + +_SOKOL_PRIVATE WGPUFrontFace _sg_wgpu_frontface(sg_face_winding fw) { + return (fw == SG_FACEWINDING_CCW) ? WGPUFrontFace_CCW : WGPUFrontFace_CW; +} + +_SOKOL_PRIVATE WGPUCullMode _sg_wgpu_cullmode(sg_cull_mode cm) { + switch (cm) { + case SG_CULLMODE_NONE: return WGPUCullMode_None; + case SG_CULLMODE_FRONT: return WGPUCullMode_Front; + case SG_CULLMODE_BACK: return WGPUCullMode_Back; + default: SOKOL_UNREACHABLE; return WGPUCullMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { + switch (p) { + case SG_PIXELFORMAT_NONE: return WGPUTextureFormat_Undefined; + case SG_PIXELFORMAT_R8: return WGPUTextureFormat_R8Unorm; + case SG_PIXELFORMAT_R8SN: return WGPUTextureFormat_R8Snorm; + case SG_PIXELFORMAT_R8UI: return WGPUTextureFormat_R8Uint; + case SG_PIXELFORMAT_R8SI: return WGPUTextureFormat_R8Sint; + case SG_PIXELFORMAT_R16UI: return WGPUTextureFormat_R16Uint; + case SG_PIXELFORMAT_R16SI: return WGPUTextureFormat_R16Sint; + case SG_PIXELFORMAT_R16F: return WGPUTextureFormat_R16Float; + case SG_PIXELFORMAT_RG8: return WGPUTextureFormat_RG8Unorm; + case SG_PIXELFORMAT_RG8SN: return WGPUTextureFormat_RG8Snorm; + case SG_PIXELFORMAT_RG8UI: return WGPUTextureFormat_RG8Uint; + case SG_PIXELFORMAT_RG8SI: return WGPUTextureFormat_RG8Sint; + case SG_PIXELFORMAT_R32UI: return WGPUTextureFormat_R32Uint; + case SG_PIXELFORMAT_R32SI: return WGPUTextureFormat_R32Sint; + case SG_PIXELFORMAT_R32F: return WGPUTextureFormat_R32Float; + case SG_PIXELFORMAT_RG16UI: return WGPUTextureFormat_RG16Uint; + case SG_PIXELFORMAT_RG16SI: return WGPUTextureFormat_RG16Sint; + case SG_PIXELFORMAT_RG16F: return WGPUTextureFormat_RG16Float; + case SG_PIXELFORMAT_RGBA8: return WGPUTextureFormat_RGBA8Unorm; + case SG_PIXELFORMAT_RGBA8SN: return WGPUTextureFormat_RGBA8Snorm; + case SG_PIXELFORMAT_RGBA8UI: return WGPUTextureFormat_RGBA8Uint; + case SG_PIXELFORMAT_RGBA8SI: return WGPUTextureFormat_RGBA8Sint; + case SG_PIXELFORMAT_BGRA8: return WGPUTextureFormat_BGRA8Unorm; + case SG_PIXELFORMAT_RGB10A2: return WGPUTextureFormat_RGB10A2Unorm; + case SG_PIXELFORMAT_RG11B10F: return WGPUTextureFormat_RG11B10Float; + case SG_PIXELFORMAT_RG32UI: return WGPUTextureFormat_RG32Uint; + case SG_PIXELFORMAT_RG32SI: return WGPUTextureFormat_RG32Sint; + case SG_PIXELFORMAT_RG32F: return WGPUTextureFormat_RG32Float; + case SG_PIXELFORMAT_RGBA16UI: return WGPUTextureFormat_RGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return WGPUTextureFormat_RGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return WGPUTextureFormat_RGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return WGPUTextureFormat_RGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return WGPUTextureFormat_RGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return WGPUTextureFormat_RGBA32Float; + case SG_PIXELFORMAT_DEPTH: return WGPUTextureFormat_Depth24Plus; + case SG_PIXELFORMAT_DEPTH_STENCIL: return WGPUTextureFormat_Depth24PlusStencil8; + case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm; + case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm; + case SG_PIXELFORMAT_BC3_RGBA: return WGPUTextureFormat_BC3RGBAUnorm; + case SG_PIXELFORMAT_BC4_R: return WGPUTextureFormat_BC4RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return WGPUTextureFormat_BC4RSnorm; + case SG_PIXELFORMAT_BC5_RG: return WGPUTextureFormat_BC5RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return WGPUTextureFormat_BC5RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return WGPUTextureFormat_BC6HRGBSfloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm; + + /* NOT SUPPORTED */ + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_ETC2_RG11SN: + default: + SOKOL_UNREACHABLE; + return WGPUTextureFormat_Force32; + } +} + +/* +FIXME ??? this isn't needed anywhere? +_SOKOL_PRIVATE WGPUTextureAspect _sg_wgpu_texture_aspect(sg_pixel_format fmt) { + if (_sg_is_valid_rendertarget_depth_format(fmt)) { + if (!_sg_is_depth_stencil_format(fmt)) { + return WGPUTextureAspect_DepthOnly; + } + } + return WGPUTextureAspect_All; +} +*/ + +_SOKOL_PRIVATE WGPUCompareFunction _sg_wgpu_comparefunc(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return WGPUCompareFunction_Never; + case SG_COMPAREFUNC_LESS: return WGPUCompareFunction_Less; + case SG_COMPAREFUNC_EQUAL: return WGPUCompareFunction_Equal; + case SG_COMPAREFUNC_LESS_EQUAL: return WGPUCompareFunction_LessEqual; + case SG_COMPAREFUNC_GREATER: return WGPUCompareFunction_Greater; + case SG_COMPAREFUNC_NOT_EQUAL: return WGPUCompareFunction_NotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return WGPUCompareFunction_GreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return WGPUCompareFunction_Always; + default: SOKOL_UNREACHABLE; return WGPUCompareFunction_Force32; + } +} + +_SOKOL_PRIVATE WGPUStencilOperation _sg_wgpu_stencilop(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return WGPUStencilOperation_Keep; + case SG_STENCILOP_ZERO: return WGPUStencilOperation_Zero; + case SG_STENCILOP_REPLACE: return WGPUStencilOperation_Replace; + case SG_STENCILOP_INCR_CLAMP: return WGPUStencilOperation_IncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return WGPUStencilOperation_DecrementClamp; + case SG_STENCILOP_INVERT: return WGPUStencilOperation_Invert; + case SG_STENCILOP_INCR_WRAP: return WGPUStencilOperation_IncrementWrap; + case SG_STENCILOP_DECR_WRAP: return WGPUStencilOperation_DecrementWrap; + default: SOKOL_UNREACHABLE; return WGPUStencilOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendOperation _sg_wgpu_blendop(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return WGPUBlendOperation_Add; + case SG_BLENDOP_SUBTRACT: return WGPUBlendOperation_Subtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return WGPUBlendOperation_ReverseSubtract; + default: SOKOL_UNREACHABLE; return WGPUBlendOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return WGPUBlendFactor_Zero; + case SG_BLENDFACTOR_ONE: return WGPUBlendFactor_One; + case SG_BLENDFACTOR_SRC_COLOR: return WGPUBlendFactor_SrcColor; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return WGPUBlendFactor_OneMinusSrcColor; + case SG_BLENDFACTOR_SRC_ALPHA: return WGPUBlendFactor_SrcAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return WGPUBlendFactor_OneMinusSrcAlpha; + case SG_BLENDFACTOR_DST_COLOR: return WGPUBlendFactor_DstColor; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return WGPUBlendFactor_OneMinusDstColor; + case SG_BLENDFACTOR_DST_ALPHA: return WGPUBlendFactor_DstAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return WGPUBlendFactor_OneMinusDstAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return WGPUBlendFactor_SrcAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return WGPUBlendFactor_BlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return WGPUBlendFactor_OneMinusBlendColor; + /* FIXME: separate blend alpha value not supported? */ + case SG_BLENDFACTOR_BLEND_ALPHA: return WGPUBlendFactor_BlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusBlendColor; + default: + SOKOL_UNREACHABLE; return WGPUBlendFactor_Force32; + } +} + +_SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { + WGPUColorWriteMaskFlags res = 0; + if (0 != (m & SG_COLORMASK_R)) { + res |= WGPUColorWriteMask_Red; + } + if (0 != (m & SG_COLORMASK_G)) { + res |= WGPUColorWriteMask_Green; + } + if (0 != (m & SG_COLORMASK_B)) { + res |= WGPUColorWriteMask_Blue; + } + if (0 != (m & SG_COLORMASK_A)) { + res |= WGPUColorWriteMask_Alpha; + } + return res; +} + +_SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { + _sg.backend = SG_BACKEND_WGPU; + _sg.features.instancing = true; + _sg.features.origin_top_left = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + + /* FIXME: max images size??? */ + _sg.limits.max_image_size_2d = 8 * 1024; + _sg.limits.max_image_size_cube = 8 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 8 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + + /* FIXME FIXME FIXME: need to check if BC texture compression is + actually supported, currently the WebGPU C-API doesn't allow this + */ + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); +} + +/* + WGPU uniform buffer pool implementation: + + At start of frame, a mapped buffer is grabbed from the pool, + or a new buffer is created if there is no mapped buffer available. + + At end of frame, the current buffer is unmapped before queue submit, + and async-mapped immediately again. + + UNIFORM BUFFER FIXME: + + - As per WebGPU spec, it should be possible to create a Uniform|MapWrite + buffer, but this isn't currently allowed in Dawn. +*/ +_SOKOL_PRIVATE void _sg_wgpu_ubpool_init(const sg_desc* desc) { + + /* Add the max-uniform-update size (64 KB) to the requested buffer size, + this is to prevent validation errors in the WebGPU implementation + if the entire buffer size is used per frame. 64 KB is the allowed + max uniform update size on NVIDIA + */ + _sg.wgpu.ub.num_bytes = desc->uniform_buffer_size + _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; + + WGPUBufferDescriptor ub_desc; + memset(&ub_desc, 0, sizeof(ub_desc)); + ub_desc.size = _sg.wgpu.ub.num_bytes; + ub_desc.usage = WGPUBufferUsage_Uniform|WGPUBufferUsage_CopyDst; + _sg.wgpu.ub.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc); + SOKOL_ASSERT(_sg.wgpu.ub.buf); + + WGPUBindGroupLayoutBinding ub_bglb_desc[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + memset(ub_bglb_desc, 0, sizeof(ub_bglb_desc)); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bglb_desc[stage_index][ub_index].binding = bind_index; + ub_bglb_desc[stage_index][ub_index].visibility = vis; + ub_bglb_desc[stage_index][ub_index].type = WGPUBindingType_UniformBuffer; + ub_bglb_desc[stage_index][ub_index].hasDynamicOffset = true; + } + } + + WGPUBindGroupLayoutDescriptor ub_bgl_desc; + memset(&ub_bgl_desc, 0, sizeof(ub_bgl_desc)); + ub_bgl_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + ub_bgl_desc.bindings = &ub_bglb_desc[0][0]; + _sg.wgpu.ub.bindgroup_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &ub_bgl_desc); + SOKOL_ASSERT(_sg.wgpu.ub.bindgroup_layout); + + WGPUBindGroupBinding ub_bgb[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + memset(ub_bgb, 0, sizeof(ub_bgb)); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bgb[stage_index][ub_index].binding = bind_index; + ub_bgb[stage_index][ub_index].buffer = _sg.wgpu.ub.buf; + // FIXME FIXME FIXME FIXME: HACK FOR VALIDATION BUG IN DAWN + ub_bgb[stage_index][ub_index].size = (1<<16); + } + } + WGPUBindGroupDescriptor bg_desc; + memset(&bg_desc, 0, sizeof(bg_desc)); + bg_desc.layout = _sg.wgpu.ub.bindgroup_layout; + bg_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + bg_desc.bindings = &ub_bgb[0][0]; + _sg.wgpu.ub.bindgroup = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.ub.bindgroup); +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_discard(void) { + if (_sg.wgpu.ub.buf) { + wgpuBufferRelease(_sg.wgpu.ub.buf); + _sg.wgpu.ub.buf = 0; + } + if (_sg.wgpu.ub.bindgroup) { + wgpuBindGroupRelease(_sg.wgpu.ub.bindgroup); + _sg.wgpu.ub.bindgroup = 0; + } + if (_sg.wgpu.ub.bindgroup_layout) { + wgpuBindGroupLayoutRelease(_sg.wgpu.ub.bindgroup_layout); + _sg.wgpu.ub.bindgroup_layout = 0; + } + for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { + if (_sg.wgpu.ub.stage.buf[i]) { + wgpuBufferRelease(_sg.wgpu.ub.stage.buf[i]); + _sg.wgpu.ub.stage.buf[i] = 0; + _sg.wgpu.ub.stage.ptr[i] = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_mapped_callback(WGPUBufferMapAsyncStatus status, void* data, uint64_t data_len, void* user_data) { + if (!_sg.wgpu.valid) { + return; + } + /* FIXME: better handling for this */ + if (WGPUBufferMapAsyncStatus_Success != status) { + SOKOL_LOG("Mapping uniform buffer failed!\n"); + SOKOL_ASSERT(false); + } + SOKOL_ASSERT(data && (data_len == _sg.wgpu.ub.num_bytes)); + int index = (int)(intptr_t) user_data; + SOKOL_ASSERT(index < _sg.wgpu.ub.stage.num); + SOKOL_ASSERT(0 == _sg.wgpu.ub.stage.ptr[index]); + _sg.wgpu.ub.stage.ptr[index] = (uint8_t*) data; +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { + + /* immediately request a new mapping for the last frame's current staging buffer */ + if (!first_frame) { + WGPUBuffer ub_src = _sg.wgpu.ub.stage.buf[_sg.wgpu.ub.stage.cur]; + wgpuBufferMapWriteAsync(ub_src, _sg_wgpu_ubpool_mapped_callback, (void*)(intptr_t)_sg.wgpu.ub.stage.cur); + } + + /* rewind per-frame offsets */ + _sg.wgpu.ub.offset = 0; + memset(&_sg.wgpu.ub.bind_offsets, 0, sizeof(_sg.wgpu.ub.bind_offsets)); + + /* check if a mapped staging buffer is available, otherwise create one */ + for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { + if (_sg.wgpu.ub.stage.ptr[i]) { + _sg.wgpu.ub.stage.cur = i; + return; + } + } + + /* no mapped uniform buffer available, create one */ + SOKOL_ASSERT(_sg.wgpu.ub.stage.num < _SG_WGPU_STAGING_PIPELINE_SIZE); + _sg.wgpu.ub.stage.cur = _sg.wgpu.ub.stage.num++; + const int cur = _sg.wgpu.ub.stage.cur; + + WGPUBufferDescriptor desc; + memset(&desc, 0, sizeof(desc)); + desc.size = _sg.wgpu.ub.num_bytes; + desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; + WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); + _sg.wgpu.ub.stage.buf[cur] = res.buffer; + _sg.wgpu.ub.stage.ptr[cur] = (uint8_t*) res.data; + SOKOL_ASSERT(_sg.wgpu.ub.stage.buf[cur]); + SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); + SOKOL_ASSERT(res.dataLength == _sg.wgpu.ub.num_bytes); +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_flush(void) { + /* unmap staging buffer and copy to uniform buffer */ + const int cur = _sg.wgpu.ub.stage.cur; + SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); + _sg.wgpu.ub.stage.ptr[cur] = 0; + WGPUBuffer src_buf = _sg.wgpu.ub.stage.buf[cur]; + wgpuBufferUnmap(src_buf); + if (_sg.wgpu.ub.offset > 0) { + WGPUBuffer dst_buf = _sg.wgpu.ub.buf; + wgpuCommandEncoderCopyBufferToBuffer(_sg.wgpu.render_cmd_enc, src_buf, 0, dst_buf, 0, _sg.wgpu.ub.offset); + } +} + +/* helper function to compute number of bytes needed in staging buffer to copy image data */ +_SOKOL_PRIVATE uint32_t _sg_wgpu_image_data_buffer_size(const _sg_image_t* img) { + uint32_t num_bytes = 0; + const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + const uint32_t mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const uint32_t mip_height = _sg_max(img->cmn.height >> mip_index, 1); + /* row-pitch must be 256-aligend */ + const uint32_t bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); + num_bytes += bytes_per_slice * num_slices * num_faces; + } + return num_bytes; +} + +/* helper function to copy image data into a texture via a staging buffer, returns number of + bytes copied +*/ +_SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* stg_base_ptr, uint32_t stg_base_offset, _sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + SOKOL_ASSERT(stg_buf && stg_base_ptr); + SOKOL_ASSERT(img); + SOKOL_ASSERT(data); + uint32_t stg_offset = stg_base_offset; + const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; + const sg_pixel_format fmt = img->cmn.pixel_format; + WGPUBufferCopyView src_view; + memset(&src_view, 0, sizeof(src_view)); + src_view.buffer = stg_buf; + WGPUTextureCopyView dst_view; + memset(&dst_view, 0, sizeof(dst_view)); + dst_view.texture = img->wgpu.tex; + WGPUExtent3D extent; + memset(&extent, 0, sizeof(extent)); + + for (uint32_t face_index = 0; face_index < num_faces; face_index++) { + for (uint32_t mip_index = 0; mip_index < (uint32_t)img->cmn.num_mipmaps; mip_index++) { + SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); + SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); + const uint8_t* src_base_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; + SOKOL_ASSERT(src_base_ptr); + uint8_t* dst_base_ptr = stg_base_ptr + stg_offset; + + const uint32_t mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const uint32_t mip_height = _sg_max(img->cmn.height >> mip_index, 1); + const uint32_t mip_depth = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_max(img->cmn.num_slices >> mip_index, 1) : 1; + const uint32_t num_rows = _sg_num_rows(fmt, mip_height); + const uint32_t src_bytes_per_row = _sg_row_pitch(fmt, mip_width, 1); + const uint32_t dst_bytes_per_row = _sg_row_pitch(fmt, mip_width, _SG_WGPU_ROWPITCH_ALIGN); + const uint32_t src_bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); + const uint32_t dst_bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); + SOKOL_ASSERT((uint32_t)data->subimage[face_index][mip_index].size == (src_bytes_per_slice * num_slices)); + SOKOL_ASSERT(src_bytes_per_row <= dst_bytes_per_row); + SOKOL_ASSERT(src_bytes_per_slice == (src_bytes_per_row * num_rows)); + SOKOL_ASSERT(dst_bytes_per_slice == (dst_bytes_per_row * num_rows)); + _SOKOL_UNUSED(src_bytes_per_slice); + + /* copy data into mapped staging buffer */ + if (src_bytes_per_row == dst_bytes_per_row) { + /* can do a single memcpy */ + uint32_t num_bytes = data->subimage[face_index][mip_index].size; + memcpy(dst_base_ptr, src_base_ptr, num_bytes); + } + else { + /* src/dst pitch doesn't match, need to copy row by row */ + uint8_t* dst_ptr = dst_base_ptr; + const uint8_t* src_ptr = src_base_ptr; + for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { + SOKOL_ASSERT(dst_ptr == dst_base_ptr + slice_index * dst_bytes_per_slice); + for (uint32_t row_index = 0; row_index < num_rows; row_index++) { + memcpy(dst_ptr, src_ptr, src_bytes_per_row); + src_ptr += src_bytes_per_row; + dst_ptr += dst_bytes_per_row; + } + } + } + + /* record the staging copy operation into command encoder */ + src_view.imageHeight = mip_height; + src_view.rowPitch = dst_bytes_per_row; + dst_view.mipLevel = mip_index; + extent.width = mip_width; + extent.height = mip_height; + extent.depth = mip_depth; + SOKOL_ASSERT((img->cmn.type != SG_IMAGETYPE_CUBE) || (num_slices == 1)); + for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { + const uint32_t layer_index = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? slice_index : face_index; + src_view.offset = stg_offset; + dst_view.arrayLayer = layer_index; + wgpuCommandEncoderCopyBufferToTexture(_sg.wgpu.staging_cmd_enc, &src_view, &dst_view, &extent); + stg_offset += dst_bytes_per_slice; + SOKOL_ASSERT(stg_offset <= _sg.wgpu.staging.num_bytes); + } + } + } + SOKOL_ASSERT(stg_offset >= stg_base_offset); + return (stg_offset - stg_base_offset); +} + +/* + The WGPU staging buffer implementation: + + Very similar to the uniform buffer pool, there's a pool of big + per-frame staging buffers, each must be big enough to hold + all data uploaded to dynamic resources for one frame. + + Staging buffers are created on demand and reused, because the + 'frame pipeline depth' of WGPU isn't predictable. + + The difference to the uniform buffer system is that there isn't + a 1:1 relationship for source- and destination for the + data-copy operation. There's always one staging buffer as copy-source + per frame, but many copy-destinations (regular vertex/index buffers + or images). Instead of one big copy-operation at the end of the frame, + multiple copy-operations will be written throughout the frame. +*/ +_SOKOL_PRIVATE void _sg_wgpu_staging_init(const sg_desc* desc) { + SOKOL_ASSERT(desc && (desc->staging_buffer_size > 0)); + _sg.wgpu.staging.num_bytes = desc->staging_buffer_size; + /* there's actually nothing more to do here */ +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_discard(void) { + for (int i = 0; i < _sg.wgpu.staging.num; i++) { + if (_sg.wgpu.staging.buf[i]) { + wgpuBufferRelease(_sg.wgpu.staging.buf[i]); + _sg.wgpu.staging.buf[i] = 0; + _sg.wgpu.staging.ptr[i] = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_mapped_callback(WGPUBufferMapAsyncStatus status, void* data, uint64_t data_len, void* user_data) { + if (!_sg.wgpu.valid) { + return; + } + /* FIXME: better handling for this */ + if (WGPUBufferMapAsyncStatus_Success != status) { + SOKOL_ASSERT("Mapping staging buffer failed!\n"); + SOKOL_ASSERT(false); + } + SOKOL_ASSERT(data && (data_len == _sg.wgpu.staging.num_bytes)); + int index = (int)(intptr_t) user_data; + SOKOL_ASSERT(index < _sg.wgpu.staging.num); + SOKOL_ASSERT(0 == _sg.wgpu.staging.ptr[index]); + _sg.wgpu.staging.ptr[index] = (uint8_t*) data; +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_next_frame(bool first_frame) { + + /* immediately request a new mapping for the last frame's current staging buffer */ + if (!first_frame) { + WGPUBuffer cur_buf = _sg.wgpu.staging.buf[_sg.wgpu.staging.cur]; + wgpuBufferMapWriteAsync(cur_buf, _sg_wgpu_staging_mapped_callback, (void*)(intptr_t)_sg.wgpu.staging.cur); + } + + /* rewind staging-buffer offset */ + _sg.wgpu.staging.offset = 0; + + /* check if mapped staging buffer is available, otherwise create one */ + for (int i = 0; i < _sg.wgpu.staging.num; i++) { + if (_sg.wgpu.staging.ptr[i]) { + _sg.wgpu.staging.cur = i; + return; + } + } + + /* no mapped buffer available, create one */ + SOKOL_ASSERT(_sg.wgpu.staging.num < _SG_WGPU_STAGING_PIPELINE_SIZE); + _sg.wgpu.staging.cur = _sg.wgpu.staging.num++; + const int cur = _sg.wgpu.staging.cur; + + WGPUBufferDescriptor desc; + memset(&desc, 0, sizeof(desc)); + desc.size = _sg.wgpu.staging.num_bytes; + desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; + WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); + _sg.wgpu.staging.buf[cur] = res.buffer; + _sg.wgpu.staging.ptr[cur] = (uint8_t*) res.data; + SOKOL_ASSERT(_sg.wgpu.staging.buf[cur]); + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + SOKOL_ASSERT(res.dataLength == _sg.wgpu.staging.num_bytes); +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_staging_copy_to_buffer(WGPUBuffer dst_buf, uint32_t dst_buf_offset, const void* data, uint32_t data_num_bytes) { + /* Copy a chunk of data into the staging buffer, and record a blit-operation into + the command encoder, bump the offset for the next data chunk, return 0 if there + was not enough room in the staging buffer, return the number of actually + copied bytes on success. + + NOTE: that the number of staging bytes to be copied must be a multiple of 4. + + */ + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + SOKOL_ASSERT((dst_buf_offset & 3) == 0); + SOKOL_ASSERT(data_num_bytes > 0); + uint32_t copy_num_bytes = _sg_roundup(data_num_bytes, 4); + if ((_sg.wgpu.staging.offset + copy_num_bytes) >= _sg.wgpu.staging.num_bytes) { + SOKOL_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_buffer())!\n"); + return false; + } + const int cur = _sg.wgpu.staging.cur; + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + uint32_t stg_buf_offset = _sg.wgpu.staging.offset; + uint8_t* stg_ptr = _sg.wgpu.staging.ptr[cur] + stg_buf_offset; + memcpy(stg_ptr, data, data_num_bytes); + WGPUBuffer stg_buf = _sg.wgpu.staging.buf[cur]; + wgpuCommandEncoderCopyBufferToBuffer(_sg.wgpu.staging_cmd_enc, stg_buf, stg_buf_offset, dst_buf, dst_buf_offset, copy_num_bytes); + _sg.wgpu.staging.offset = stg_buf_offset + copy_num_bytes; + return copy_num_bytes; +} + +_SOKOL_PRIVATE bool _sg_wgpu_staging_copy_to_texture(_sg_image_t* img, const sg_image_data* data) { + /* similar to _sg_wgpu_staging_copy_to_buffer(), but with image data instead */ + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + uint32_t num_bytes = _sg_wgpu_image_data_buffer_size(img); + if ((_sg.wgpu.staging.offset + num_bytes) >= _sg.wgpu.staging.num_bytes) { + SOKOL_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_texture)!\n"); + return false; + } + const int cur = _sg.wgpu.staging.cur; + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + uint32_t stg_offset = _sg.wgpu.staging.offset; + uint8_t* stg_ptr = _sg.wgpu.staging.ptr[cur]; + WGPUBuffer stg_buf = _sg.wgpu.staging.buf[cur]; + uint32_t bytes_copied = _sg_wgpu_copy_image_data(stg_buf, stg_ptr, stg_offset, img, data); + _SOKOL_UNUSED(bytes_copied); + SOKOL_ASSERT(bytes_copied == num_bytes); + _sg.wgpu.staging.offset = _sg_roundup(stg_offset + num_bytes, _SG_WGPU_STAGING_ALIGN); + return true; +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_unmap(void) { + /* called at end of frame before queue-submit */ + const int cur = _sg.wgpu.staging.cur; + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + _sg.wgpu.staging.ptr[cur] = 0; + wgpuBufferUnmap(_sg.wgpu.staging.buf[cur]); +} + +/*--- WGPU sampler cache functions ---*/ +_SOKOL_PRIVATE void _sg_wgpu_init_sampler_cache(const sg_desc* desc) { + SOKOL_ASSERT(desc->sampler_cache_size > 0); + _sg_smpcache_init(&_sg.wgpu.sampler_cache, desc->sampler_cache_size); +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_sampler_cache(void) { + SOKOL_ASSERT(_sg.wgpu.sampler_cache.items); + SOKOL_ASSERT(_sg.wgpu.sampler_cache.num_items <= _sg.wgpu.sampler_cache.capacity); + for (int i = 0; i < _sg.wgpu.sampler_cache.num_items; i++) { + wgpuSamplerRelease((WGPUSampler)_sg_smpcache_sampler(&_sg.wgpu.sampler_cache, i)); + } + _sg_smpcache_discard(&_sg.wgpu.sampler_cache); +} + +_SOKOL_PRIVATE WGPUSampler _sg_wgpu_create_sampler(const sg_image_desc* img_desc) { + SOKOL_ASSERT(img_desc); + int index = _sg_smpcache_find_item(&_sg.wgpu.sampler_cache, img_desc); + if (index >= 0) { + /* reuse existing sampler */ + return (WGPUSampler) _sg_smpcache_sampler(&_sg.wgpu.sampler_cache, index); + } + else { + /* create a new WGPU sampler and add to sampler cache */ + /* FIXME: anisotropic filtering not supported? */ + WGPUSamplerDescriptor smp_desc; + memset(&smp_desc, 0, sizeof(smp_desc)); + smp_desc.addressModeU = _sg_wgpu_sampler_addrmode(img_desc->wrap_u); + smp_desc.addressModeV = _sg_wgpu_sampler_addrmode(img_desc->wrap_v); + smp_desc.addressModeW = _sg_wgpu_sampler_addrmode(img_desc->wrap_w); + smp_desc.magFilter = _sg_wgpu_sampler_minmagfilter(img_desc->mag_filter); + smp_desc.minFilter = _sg_wgpu_sampler_minmagfilter(img_desc->min_filter); + smp_desc.mipmapFilter = _sg_wgpu_sampler_mipfilter(img_desc->min_filter); + smp_desc.lodMinClamp = img_desc->min_lod; + smp_desc.lodMaxClamp = img_desc->max_lod; + WGPUSampler smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &smp_desc); + SOKOL_ASSERT(smp); + _sg_smpcache_add_item(&_sg.wgpu.sampler_cache, img_desc, (uintptr_t)smp); + return smp; + } +} + +/*--- WGPU backend API functions ---*/ +_SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->context.wgpu.device); + SOKOL_ASSERT(desc->context.wgpu.render_view_cb || desc->context.wgpu.render_view_userdata_cb); + SOKOL_ASSERT(desc->context.wgpu.resolve_view_cb || desc->context.wgpu.resolve_view_userdata_cb); + SOKOL_ASSERT(desc->context.wgpu.depth_stencil_view_cb || desc->context.wgpu.depth_stencil_view_userdata_cb); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + SOKOL_ASSERT(desc->staging_buffer_size > 0); + _sg.backend = SG_BACKEND_WGPU; + _sg.wgpu.valid = true; + _sg.wgpu.dev = (WGPUDevice) desc->context.wgpu.device; + _sg.wgpu.render_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.render_view_cb; + _sg.wgpu.render_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.render_view_userdata_cb; + _sg.wgpu.resolve_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.resolve_view_cb; + _sg.wgpu.resolve_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.resolve_view_userdata_cb; + _sg.wgpu.depth_stencil_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.depth_stencil_view_cb; + _sg.wgpu.depth_stencil_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.depth_stencil_view_userdata_cb; + _sg.wgpu.user_data = desc->context.wgpu.user_data; + _sg.wgpu.queue = wgpuDeviceCreateQueue(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.queue); + + /* setup WebGPU features and limits */ + _sg_wgpu_init_caps(); + + /* setup the sampler cache, uniform and staging buffer pools */ + _sg_wgpu_init_sampler_cache(&_sg.desc); + _sg_wgpu_ubpool_init(desc); + _sg_wgpu_ubpool_next_frame(true); + _sg_wgpu_staging_init(desc); + _sg_wgpu_staging_next_frame(true); + + /* create an empty bind group for shader stages without bound images */ + WGPUBindGroupLayoutDescriptor bgl_desc; + memset(&bgl_desc, 0, sizeof(bgl_desc)); + WGPUBindGroupLayout empty_bgl = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); + SOKOL_ASSERT(empty_bgl); + WGPUBindGroupDescriptor bg_desc; + memset(&bg_desc, 0, sizeof(bg_desc)); + bg_desc.layout = empty_bgl; + _sg.wgpu.empty_bind_group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.empty_bind_group); + wgpuBindGroupLayoutRelease(empty_bgl); + + /* create initial per-frame command encoders */ + WGPUCommandEncoderDescriptor cmd_enc_desc; + memset(&cmd_enc_desc, 0, sizeof(cmd_enc_desc)); + _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { + SOKOL_ASSERT(_sg.wgpu.valid); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + _sg.wgpu.valid = false; + _sg_wgpu_ubpool_discard(); + _sg_wgpu_staging_discard(); + _sg_wgpu_destroy_sampler_cache(); + wgpuBindGroupRelease(_sg.wgpu.empty_bind_group); + wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); + _sg.wgpu.render_cmd_enc = 0; + wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); + _sg.wgpu.staging_cmd_enc = 0; + if (_sg.wgpu.queue) { + wgpuQueueRelease(_sg.wgpu.queue); + _sg.wgpu.queue = 0; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { + SOKOL_LOG("_sg_wgpu_reset_state_cache: FIXME\n"); +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); +} + +_SOKOL_PRIVATE void _sg_wgpu_activate_context(_sg_context_t* ctx) { + (void)ctx; + SOKOL_LOG("_sg_wgpu_activate_context: FIXME\n"); +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + const bool injected = (0 != desc->wgpu_buffer); + _sg_buffer_common_init(&buf->cmn, desc); + if (injected) { + buf->wgpu.buf = (WGPUBuffer) desc->wgpu_buffer; + wgpuBufferReference(buf->wgpu.buf); + } + else { + WGPUBufferDescriptor wgpu_buf_desc; + memset(&wgpu_buf_desc, 0, sizeof(wgpu_buf_desc)); + wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(buf->cmn.type, buf->cmn.usage); + wgpu_buf_desc.size = buf->cmn.size; + if (SG_USAGE_IMMUTABLE == buf->cmn.usage) { + SOKOL_ASSERT(desc->data.ptr); + WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); + buf->wgpu.buf = res.buffer; + SOKOL_ASSERT(res.data && (res.dataLength == buf->cmn.size)); + memcpy(res.data, desc->data.ptr, buf->cmn.size); + wgpuBufferUnmap(res.buffer); + } + else { + buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + WGPUBuffer wgpu_buf = buf->wgpu.buf; + if (0 != wgpu_buf) { + wgpuBufferRelease(wgpu_buf); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_init_texdesc_common(WGPUTextureDescriptor* wgpu_tex_desc, const sg_image_desc* desc) { + wgpu_tex_desc->usage = WGPUTextureUsage_Sampled|WGPUTextureUsage_CopyDst; + wgpu_tex_desc->dimension = _sg_wgpu_tex_dim(desc->type); + wgpu_tex_desc->size.width = desc->width; + wgpu_tex_desc->size.height = desc->height; + if (desc->type == SG_IMAGETYPE_3D) { + wgpu_tex_desc->size.depth = desc->num_slices; + wgpu_tex_desc->arrayLayerCount = 1; + } + else if (desc->type == SG_IMAGETYPE_CUBE) { + wgpu_tex_desc->size.depth = 1; + wgpu_tex_desc->arrayLayerCount = 6; + } + else { + wgpu_tex_desc->size.depth = 1; + wgpu_tex_desc->arrayLayerCount = desc->num_slices; + } + wgpu_tex_desc->format = _sg_wgpu_textureformat(desc->pixel_format); + wgpu_tex_desc->mipLevelCount = desc->num_mipmaps; + wgpu_tex_desc->sampleCount = 1; +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + + _sg_image_common_init(&img->cmn, desc); + + const bool injected = (0 != desc->wgpu_texture); + const bool is_msaa = desc->sample_count > 1; + WGPUTextureDescriptor wgpu_tex_desc; + memset(&wgpu_tex_desc, 0, sizeof(wgpu_tex_desc)); + _sg_wgpu_init_texdesc_common(&wgpu_tex_desc, desc); + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { + SOKOL_ASSERT(img->cmn.render_target); + SOKOL_ASSERT(img->cmn.type == SG_IMAGETYPE_2D); + SOKOL_ASSERT(img->cmn.num_mipmaps == 1); + SOKOL_ASSERT(!injected); + /* NOTE: a depth-stencil texture will never be MSAA-resolved, so there + won't be a separate MSAA- and resolve-texture + */ + wgpu_tex_desc.usage = WGPUTextureUsage_OutputAttachment; + wgpu_tex_desc.sampleCount = desc->sample_count; + img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + SOKOL_ASSERT(img->wgpu.tex); + } + else { + if (injected) { + img->wgpu.tex = (WGPUTexture) desc->wgpu_texture; + wgpuTextureReference(img->wgpu.tex); + } + else { + /* NOTE: in the MSAA-rendertarget case, both the MSAA texture *and* + the resolve texture need OutputAttachment usage + */ + if (img->cmn.render_target) { + wgpu_tex_desc.usage = WGPUTextureUsage_Sampled|WGPUTextureUsage_OutputAttachment; + } + img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + SOKOL_ASSERT(img->wgpu.tex); + + /* copy content into texture via a throw-away staging buffer */ + if (desc->usage == SG_USAGE_IMMUTABLE && !desc->render_target) { + WGPUBufferDescriptor wgpu_buf_desc; + memset(&wgpu_buf_desc, 0, sizeof(wgpu_buf_desc)); + wgpu_buf_desc.size = _sg_wgpu_image_data_buffer_size(img); + wgpu_buf_desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_CopyDst; + WGPUCreateBufferMappedResult map = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); + SOKOL_ASSERT(map.buffer && map.data); + uint32_t num_bytes = _sg_wgpu_copy_image_data(map.buffer, (uint8_t*)map.data, 0, img, &desc->data); + _SOKOL_UNUSED(num_bytes); + SOKOL_ASSERT(num_bytes == wgpu_buf_desc.size); + wgpuBufferUnmap(map.buffer); + wgpuBufferRelease(map.buffer); + } + } + + /* create texture view object */ + WGPUTextureViewDescriptor wgpu_view_desc; + memset(&wgpu_view_desc, 0, sizeof(wgpu_view_desc)); + wgpu_view_desc.dimension = _sg_wgpu_tex_viewdim(desc->type); + img->wgpu.tex_view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_view_desc); + + /* if render target and MSAA, then a separate texture in MSAA format is needed + which will be resolved into the regular texture at the end of the + offscreen-render pass + */ + if (desc->render_target && is_msaa) { + wgpu_tex_desc.dimension = WGPUTextureDimension_2D; + wgpu_tex_desc.size.depth = 1; + wgpu_tex_desc.arrayLayerCount = 1; + wgpu_tex_desc.mipLevelCount = 1; + wgpu_tex_desc.usage = WGPUTextureUsage_OutputAttachment; + wgpu_tex_desc.sampleCount = desc->sample_count; + img->wgpu.msaa_tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + SOKOL_ASSERT(img->wgpu.msaa_tex); + } + + /* create sampler via shared-sampler-cache */ + img->wgpu.sampler = _sg_wgpu_create_sampler(desc); + SOKOL_ASSERT(img->wgpu.sampler); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->wgpu.tex) { + wgpuTextureRelease(img->wgpu.tex); + img->wgpu.tex = 0; + } + if (img->wgpu.tex_view) { + wgpuTextureViewRelease(img->wgpu.tex_view); + img->wgpu.tex_view = 0; + } + if (img->wgpu.msaa_tex) { + wgpuTextureRelease(img->wgpu.msaa_tex); + img->wgpu.msaa_tex = 0; + } + /* NOTE: do *not* destroy the sampler from the shared-sampler-cache */ + img->wgpu.sampler = 0; +} + +/* + How BindGroups work in WebGPU: + + - up to 4 bind groups can be bound simultaneously + - up to 16 bindings per bind group + - 'binding' slots are local per bind group + - in the shader: + layout(set=0, binding=1) corresponds to bind group 0, binding 1 + + Now how to map this to sokol-gfx's bind model: + + Reduce SG_MAX_SHADERSTAGE_IMAGES to 8, then: + + 1 bind group for all 8 uniform buffers + 1 bind group for vertex shader textures + samplers + 1 bind group for fragment shader textures + samples + + Alternatively: + + 1 bind group for 8 uniform buffer slots + 1 bind group for 8 vs images + 8 vs samplers + 1 bind group for 12 fs images + 1 bind group for 12 fs samplers + + I guess this means that we need to create BindGroups on the + fly during sg_apply_bindings() :/ +*/ +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(desc->vs.bytecode.ptr && desc->fs.bytecode.ptr); + _sg_shader_common_init(&shd->cmn, desc); + + bool success = true; + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + SOKOL_ASSERT((stage_desc->bytecode.size & 3) == 0); + + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; + + _sg_strcpy(&wgpu_stage->entry, stage_desc->entry); + WGPUShaderModuleDescriptor wgpu_shdmod_desc; + memset(&wgpu_shdmod_desc, 0, sizeof(wgpu_shdmod_desc)); + wgpu_shdmod_desc.codeSize = stage_desc->bytecode.size >> 2; + wgpu_shdmod_desc.code = (const uint32_t*) stage_desc->bytecode.ptr; + wgpu_stage->module = wgpuDeviceCreateShaderModule(_sg.wgpu.dev, &wgpu_shdmod_desc); + if (0 == wgpu_stage->module) { + success = false; + } + + /* create image/sampler bind group for the shader stage */ + WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; + int num_imgs = cmn_stage->num_images; + if (num_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { + num_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + } + WGPUBindGroupLayoutBinding bglb_desc[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; + memset(bglb_desc, 0, sizeof(bglb_desc)); + for (int img_index = 0; img_index < num_imgs; img_index++) { + /* texture- and sampler-bindings */ + WGPUBindGroupLayoutBinding* tex_desc = &bglb_desc[img_index*2 + 0]; + WGPUBindGroupLayoutBinding* smp_desc = &bglb_desc[img_index*2 + 1]; + + tex_desc->binding = img_index; + tex_desc->visibility = vis; + tex_desc->type = WGPUBindingType_SampledTexture; + tex_desc->textureDimension = _sg_wgpu_tex_viewdim(cmn_stage->images[img_index].image_type); + tex_desc->textureComponentType = _sg_wgpu_tex_comptype(cmn_stage->images[img_index].sampler_type); + + smp_desc->binding = img_index + _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + smp_desc->visibility = vis; + smp_desc->type = WGPUBindingType_Sampler; + } + WGPUBindGroupLayoutDescriptor img_bgl_desc; + memset(&img_bgl_desc, 0, sizeof(img_bgl_desc)); + img_bgl_desc.bindingCount = num_imgs * 2; + img_bgl_desc.bindings = &bglb_desc[0]; + wgpu_stage->bind_group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &img_bgl_desc); + SOKOL_ASSERT(wgpu_stage->bind_group_layout); + } + return success ? SG_RESOURCESTATE_VALID : SG_RESOURCESTATE_FAILED; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; + if (wgpu_stage->module) { + wgpuShaderModuleRelease(wgpu_stage->module); + wgpu_stage->module = 0; + } + if (wgpu_stage->bind_group_layout) { + wgpuBindGroupLayoutRelease(wgpu_stage->bind_group_layout); + wgpu_stage->bind_group_layout = 0; + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout); + SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout); + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + pip->wgpu.stencil_ref = (uint32_t) desc->stencil.ref; + + WGPUBindGroupLayout pip_bgl[3] = { + _sg.wgpu.ub.bindgroup_layout, + shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout, + shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout + }; + WGPUPipelineLayoutDescriptor pl_desc; + memset(&pl_desc, 0, sizeof(pl_desc)); + pl_desc.bindGroupLayoutCount = 3; + pl_desc.bindGroupLayouts = &pip_bgl[0]; + WGPUPipelineLayout pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &pl_desc); + + WGPUVertexBufferLayoutDescriptor vb_desc[SG_MAX_SHADERSTAGE_BUFFERS]; + memset(&vb_desc, 0, sizeof(vb_desc)); + WGPUVertexAttributeDescriptor va_desc[SG_MAX_SHADERSTAGE_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; + memset(&va_desc, 0, sizeof(va_desc)); + int vb_idx = 0; + for (; vb_idx < SG_MAX_SHADERSTAGE_BUFFERS; vb_idx++) { + const sg_buffer_layout_desc* src_vb_desc = &desc->layout.buffers[vb_idx]; + if (0 == src_vb_desc->stride) { + break; + } + vb_desc[vb_idx].arrayStride = src_vb_desc->stride; + vb_desc[vb_idx].stepMode = _sg_wgpu_stepmode(src_vb_desc->step_func); + /* NOTE: WebGPU has no support for vertex step rate (because that's + not supported by Core Vulkan + */ + int va_idx = 0; + for (int va_loc = 0; va_loc < SG_MAX_VERTEX_ATTRIBUTES; va_loc++) { + const sg_vertex_attr_desc* src_va_desc = &desc->layout.attrs[va_loc]; + if (SG_VERTEXFORMAT_INVALID == src_va_desc->format) { + break; + } + pip->cmn.vertex_layout_valid[src_va_desc->buffer_index] = true; + if (vb_idx == src_va_desc->buffer_index) { + va_desc[vb_idx][va_idx].format = _sg_wgpu_vertexformat(src_va_desc->format); + va_desc[vb_idx][va_idx].offset = src_va_desc->offset; + va_desc[vb_idx][va_idx].shaderLocation = va_loc; + va_idx++; + } + } + vb_desc[vb_idx].attributeCount = va_idx; + vb_desc[vb_idx].attributes = &va_desc[vb_idx][0]; + } + WGPUVertexStateDescriptor vx_state_desc; + memset(&vx_state_desc, 0, sizeof(vx_state_desc)); + vx_state_desc.indexFormat = _sg_wgpu_indexformat(desc->index_type); + vx_state_desc.vertexBufferCount = vb_idx; + vx_state_desc.vertexBuffers = vb_desc; + + WGPURasterizationStateDescriptor rs_desc; + memset(&rs_desc, 0, sizeof(rs_desc)); + rs_desc.frontFace = _sg_wgpu_frontface(desc->face_winding); + rs_desc.cullMode = _sg_wgpu_cullmode(desc->cull_mode); + rs_desc.depthBias = (int32_t) desc->depth.bias; + rs_desc.depthBiasClamp = desc->depth.bias_clamp; + rs_desc.depthBiasSlopeScale = desc->depth.bias_slope_scale; + + WGPUDepthStencilStateDescriptor ds_desc; + memset(&ds_desc, 0, sizeof(ds_desc)); + ds_desc.format = _sg_wgpu_textureformat(desc->depth.pixel_format); + ds_desc.depthWriteEnabled = desc->depth.write_enabled; + ds_desc.depthCompare = _sg_wgpu_comparefunc(desc->depth.compare); + ds_desc.stencilReadMask = desc->stencil.read_mask; + ds_desc.stencilWriteMask = desc->stencil.write_mask; + ds_desc.stencilFront.compare = _sg_wgpu_comparefunc(desc->stencil.front.compare); + ds_desc.stencilFront.failOp = _sg_wgpu_stencilop(desc->stencil.front.fail_op); + ds_desc.stencilFront.depthFailOp = _sg_wgpu_stencilop(desc->stencil.front.depth_fail_op); + ds_desc.stencilFront.passOp = _sg_wgpu_stencilop(desc->stencil.front.pass_op); + ds_desc.stencilBack.compare = _sg_wgpu_comparefunc(desc->stencil.back.compare); + ds_desc.stencilBack.failOp = _sg_wgpu_stencilop(desc->stencil.back.fail_op); + ds_desc.stencilBack.depthFailOp = _sg_wgpu_stencilop(desc->stencil.back.depth_fail_op); + ds_desc.stencilBack.passOp = _sg_wgpu_stencilop(desc->stencil.back.pass_op); + + WGPUProgrammableStageDescriptor fs_desc; + memset(&fs_desc, 0, sizeof(fs_desc)); + fs_desc.module = shd->wgpu.stage[SG_SHADERSTAGE_FS].module; + fs_desc.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; + + WGPUColorStateDescriptor cs_desc[SG_MAX_COLOR_ATTACHMENTS]; + memset(cs_desc, 0, sizeof(cs_desc)); + for (uint32_t i = 0; i < desc->color_count; i++) { + SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); + cs_desc[i].format = _sg_wgpu_textureformat(desc->colors[i].pixel_format); + cs_desc[i].colorBlend.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_rgb); + cs_desc[i].colorBlend.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_rgb); + cs_desc[i].colorBlend.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_rgb); + cs_desc[i].alphaBlend.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_alpha); + cs_desc[i].alphaBlend.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_alpha); + cs_desc[i].alphaBlend.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_alpha); + cs_desc[i].writeMask = _sg_wgpu_colorwritemask(desc->colors[i].write_mask); + } + + WGPURenderPipelineDescriptor pip_desc; + memset(&pip_desc, 0, sizeof(pip_desc)); + pip_desc.layout = pip_layout; + pip_desc.vertexStage.module = shd->wgpu.stage[SG_SHADERSTAGE_VS].module; + pip_desc.vertexStage.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; + pip_desc.fragmentStage = &fs_desc; + pip_desc.vertexState = &vx_state_desc; + pip_desc.primitiveTopology = _sg_wgpu_topology(desc->primitive_type); + pip_desc.rasterizationState = &rs_desc; + pip_desc.sampleCount = desc->sample_count; + if (SG_PIXELFORMAT_NONE != desc->depth.pixel_format) { + pip_desc.depthStencilState = &ds_desc; + } + pip_desc.colorStateCount = desc->color_count; + pip_desc.colorStates = cs_desc; + pip_desc.sampleMask = 0xFFFFFFFF; /* FIXME: ??? */ + pip->wgpu.pip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &pip_desc); + SOKOL_ASSERT(0 != pip->wgpu.pip); + wgpuPipelineLayoutRelease(pip_layout); + + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip == _sg.wgpu.cur_pipeline) { + _sg.wgpu.cur_pipeline = 0; + _Sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; + } + if (pip->wgpu.pip) { + wgpuRenderPipelineRelease(pip->wgpu.pip); + pip->wgpu.pip = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + _sg_pass_common_init(&pass->cmn, desc); + + /* copy image pointers and create render-texture views */ + const sg_pass_attachment_desc* att_desc; + for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->wgpu.color_atts[i].image); + _sg_image_t* img = att_images[i]; + SOKOL_ASSERT(img && (img->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format)); + pass->wgpu.color_atts[i].image = img; + /* create a render-texture-view to render into the right sub-surface */ + const bool is_msaa = img->cmn.sample_count > 1; + WGPUTextureViewDescriptor view_desc; + memset(&view_desc, 0, sizeof(view_desc)); + view_desc.baseMipLevel = is_msaa ? 0 : att_desc->mip_level; + view_desc.mipLevelCount = 1; + view_desc.baseArrayLayer = is_msaa ? 0 : att_desc->slice; + view_desc.arrayLayerCount = 1; + WGPUTexture wgpu_tex = is_msaa ? img->wgpu.msaa_tex : img->wgpu.tex; + SOKOL_ASSERT(wgpu_tex); + pass->wgpu.color_atts[i].render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); + SOKOL_ASSERT(pass->wgpu.color_atts[i].render_tex_view); + /* ... and if needed a separate resolve texture view */ + if (is_msaa) { + view_desc.baseMipLevel = att_desc->mip_level; + view_desc.baseArrayLayer = att_desc->slice; + WGPUTexture wgpu_tex = img->wgpu.tex; + pass->wgpu.color_atts[i].resolve_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); + SOKOL_ASSERT(pass->wgpu.color_atts[i].resolve_tex_view); + } + } + } + SOKOL_ASSERT(0 == pass->wgpu.ds_att.image); + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + _sg_image_t* ds_img = att_images[ds_img_index]; + pass->wgpu.ds_att.image = ds_img; + /* create a render-texture view */ + SOKOL_ASSERT(0 == att_desc->mip_level); + SOKOL_ASSERT(0 == att_desc->slice); + WGPUTextureViewDescriptor view_desc; + memset(&view_desc, 0, sizeof(view_desc)); + WGPUTexture wgpu_tex = ds_img->wgpu.tex; + SOKOL_ASSERT(wgpu_tex); + pass->wgpu.ds_att.render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); + SOKOL_ASSERT(pass->wgpu.ds_att.render_tex_view); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { + if (pass->wgpu.color_atts[i].render_tex_view) { + wgpuTextureViewRelease(pass->wgpu.color_atts[i].render_tex_view); + pass->wgpu.color_atts[i].render_tex_view = 0; + } + if (pass->wgpu.color_atts[i].resolve_tex_view) { + wgpuTextureViewRelease(pass->wgpu.color_atts[i].resolve_tex_view); + pass->wgpu.color_atts[i].resolve_tex_view = 0; + } + } + if (pass->wgpu.ds_att.render_tex_view) { + wgpuTextureViewRelease(pass->wgpu.ds_att.render_tex_view); + pass->wgpu.ds_att.render_tex_view = 0; + } +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->wgpu.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->wgpu.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.render_view_cb || _sg.wgpu.render_view_userdata_cb); + SOKOL_ASSERT(_sg.wgpu.resolve_view_cb || _sg.wgpu.resolve_view_userdata_cb); + SOKOL_ASSERT(_sg.wgpu.depth_stencil_view_cb || _sg.wgpu.depth_stencil_view_userdata_cb); + _sg.wgpu.in_pass = true; + _sg.wgpu.cur_width = w; + _sg.wgpu.cur_height = h; + _sg.wgpu.cur_pipeline = 0; + _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; + + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + if (pass) { + WGPURenderPassDescriptor wgpu_pass_desc; + memset(&wgpu_pass_desc, 0, sizeof(wgpu_pass_desc)); + WGPURenderPassColorAttachmentDescriptor wgpu_color_att_desc[SG_MAX_COLOR_ATTACHMENTS]; + memset(&wgpu_color_att_desc, 0, sizeof(wgpu_color_att_desc)); + SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); + for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { + const _sg_wgpu_attachment_t* wgpu_att = &pass->wgpu.color_atts[i]; + wgpu_color_att_desc[i].loadOp = _sg_wgpu_load_op(action->colors[i].action); + wgpu_color_att_desc[i].storeOp = WGPUStoreOp_Store; + wgpu_color_att_desc[i].clearColor.r = action->colors[i].value.r; + wgpu_color_att_desc[i].clearColor.g = action->colors[i].value.g; + wgpu_color_att_desc[i].clearColor.b = action->colors[i].value.b; + wgpu_color_att_desc[i].clearColor.a = action->colors[i].value.a; + wgpu_color_att_desc[i].attachment = wgpu_att->render_tex_view; + if (wgpu_att->image->cmn.sample_count > 1) { + wgpu_color_att_desc[i].resolveTarget = wgpu_att->resolve_tex_view; + } + } + wgpu_pass_desc.colorAttachmentCount = pass->cmn.num_color_atts; + wgpu_pass_desc.colorAttachments = &wgpu_color_att_desc[0]; + if (pass->wgpu.ds_att.image) { + WGPURenderPassDepthStencilAttachmentDescriptor wgpu_ds_att_desc; + memset(&wgpu_ds_att_desc, 0, sizeof(wgpu_ds_att_desc)); + wgpu_ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); + wgpu_ds_att_desc.clearDepth = action->depth.value; + wgpu_ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); + wgpu_ds_att_desc.clearStencil = action->stencil.value; + wgpu_ds_att_desc.attachment = pass->wgpu.ds_att.render_tex_view; + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att_desc; + _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &wgpu_pass_desc); + } + } + else { + /* default render pass */ + WGPUTextureView wgpu_render_view = _sg.wgpu.render_view_cb ? _sg.wgpu.render_view_cb() : _sg.wgpu.render_view_userdata_cb(_sg.wgpu.user_data); + WGPUTextureView wgpu_resolve_view = _sg.wgpu.resolve_view_cb ? _sg.wgpu.resolve_view_cb() : _sg.wgpu.resolve_view_userdata_cb(_sg.wgpu.user_data); + WGPUTextureView wgpu_depth_stencil_view = _sg.wgpu.depth_stencil_view_cb ? _sg.wgpu.depth_stencil_view_cb() : _sg.wgpu.depth_stencil_view_userdata_cb(_sg.wgpu.user_data); + + WGPURenderPassDescriptor pass_desc; + memset(&pass_desc, 0, sizeof(pass_desc)); + WGPURenderPassColorAttachmentDescriptor color_att_desc; + memset(&color_att_desc, 0, sizeof(color_att_desc)); + color_att_desc.loadOp = _sg_wgpu_load_op(action->colors[0].action); + color_att_desc.clearColor.r = action->colors[0].value.r; + color_att_desc.clearColor.g = action->colors[0].value.g; + color_att_desc.clearColor.b = action->colors[0].value.b; + color_att_desc.clearColor.a = action->colors[0].value.a; + color_att_desc.attachment = wgpu_render_view; + color_att_desc.resolveTarget = wgpu_resolve_view; /* null if no MSAA rendering */ + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_att_desc; + WGPURenderPassDepthStencilAttachmentDescriptor ds_att_desc; + memset(&ds_att_desc, 0, sizeof(ds_att_desc)); + ds_att_desc.attachment = wgpu_depth_stencil_view; + SOKOL_ASSERT(0 != ds_att_desc.attachment); + ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); + ds_att_desc.clearDepth = action->depth.value; + ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); + ds_att_desc.clearStencil = action->stencil.value; + pass_desc.depthStencilAttachment = &ds_att_desc; + _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &pass_desc); + } + SOKOL_ASSERT(_sg.wgpu.pass_enc); + + /* initial uniform buffer binding (required even if no uniforms are set in the frame) */ + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + 0, /* groupIndex 0 is reserved for uniform buffers */ + _sg.wgpu.ub.bindgroup, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.ub.bind_offsets[0][0]); +} + +_SOKOL_PRIVATE void _sg_wgpu_end_pass(void) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + _sg.wgpu.in_pass = false; + wgpuRenderPassEncoderEndPass(_sg.wgpu.pass_enc); + wgpuRenderPassEncoderRelease(_sg.wgpu.pass_enc); + _sg.wgpu.pass_enc = 0; +} + +_SOKOL_PRIVATE void _sg_wgpu_commit(void) { + SOKOL_ASSERT(!_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.queue); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + + /* finish and submit this frame's work */ + _sg_wgpu_ubpool_flush(); + _sg_wgpu_staging_unmap(); + + WGPUCommandBuffer cmd_bufs[2]; + + WGPUCommandBufferDescriptor cmd_buf_desc; + memset(&cmd_buf_desc, 0, sizeof(cmd_buf_desc)); + cmd_bufs[0] = wgpuCommandEncoderFinish(_sg.wgpu.staging_cmd_enc, &cmd_buf_desc); + SOKOL_ASSERT(cmd_bufs[0]); + wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); + _sg.wgpu.staging_cmd_enc = 0; + + cmd_bufs[1] = wgpuCommandEncoderFinish(_sg.wgpu.render_cmd_enc, &cmd_buf_desc); + SOKOL_ASSERT(cmd_bufs[1]); + wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); + _sg.wgpu.render_cmd_enc = 0; + + wgpuQueueSubmit(_sg.wgpu.queue, 2, &cmd_bufs[0]); + + wgpuCommandBufferRelease(cmd_bufs[0]); + wgpuCommandBufferRelease(cmd_bufs[1]); + + /* create a new render- and staging-command-encoders for next frame */ + WGPUCommandEncoderDescriptor cmd_enc_desc; + memset(&cmd_enc_desc, 0, sizeof(cmd_enc_desc)); + _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + + /* grab new staging buffers for uniform- and vertex/image-updates */ + _sg_wgpu_ubpool_next_frame(false); + _sg_wgpu_staging_next_frame(false); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + float xf = (float) x; + float yf = (float) (origin_top_left ? y : (_sg.wgpu.cur_height - (y + h))); + float wf = (float) w; + float hf = (float) h; + wgpuRenderPassEncoderSetViewport(_sg.wgpu.pass_enc, xf, yf, wf, hf, 0.0f, 1.0f); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + + /* clip against framebuffer rect */ + x = _sg_min(_sg_max(0, x), _sg.wgpu.cur_width-1); + y = _sg_min(_sg_max(0, y), _sg.wgpu.cur_height-1); + if ((x + w) > _sg.wgpu.cur_width) { + w = _sg.wgpu.cur_width - x; + } + if ((y + h) > _sg.wgpu.cur_height) { + h = _sg.wgpu.cur_height - y; + } + w = _sg_max(w, 1); + h = _sg_max(h, 1); + + uint32_t sx = (uint32_t) x; + uint32_t sy = origin_top_left ? y : (_sg.wgpu.cur_height - (y + h)); + uint32_t sw = w; + uint32_t sh = h; + wgpuRenderPassEncoderSetScissorRect(_sg.wgpu.pass_enc, sx, sy, sw, sh); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->wgpu.pip); + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + _sg.wgpu.draw_indexed = (pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.wgpu.cur_pipeline = pip; + _sg.wgpu.cur_pipeline_id.id = pip->slot.id; + wgpuRenderPassEncoderSetPipeline(_sg.wgpu.pass_enc, pip->wgpu.pip); + wgpuRenderPassEncoderSetBlendColor(_sg.wgpu.pass_enc, (WGPUColor*)&pip->cmn.blend_color); + wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.pass_enc, pip->wgpu.stencil_ref); +} + +_SOKOL_PRIVATE WGPUBindGroup _sg_wgpu_create_images_bindgroup(WGPUBindGroupLayout bgl, _sg_image_t** imgs, int num_imgs) { + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(num_imgs <= _SG_WGPU_MAX_SHADERSTAGE_IMAGES); + WGPUBindGroupBinding img_bgb[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; + memset(&img_bgb, 0, sizeof(img_bgb)); + for (int img_index = 0; img_index < num_imgs; img_index++) { + WGPUBindGroupBinding* tex_bdg = &img_bgb[img_index*2 + 0]; + WGPUBindGroupBinding* smp_bdg = &img_bgb[img_index*2 + 1]; + tex_bdg->binding = img_index; + tex_bdg->textureView = imgs[img_index]->wgpu.tex_view; + smp_bdg->binding = img_index + _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + smp_bdg->sampler = imgs[img_index]->wgpu.sampler; + } + WGPUBindGroupDescriptor bg_desc; + memset(&bg_desc, 0, sizeof(bg_desc)); + bg_desc.layout = bgl; + bg_desc.bindingCount = 2 * num_imgs; + bg_desc.bindings = &img_bgb[0]; + WGPUBindGroup bg = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(bg); + return bg; +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + + /* index buffer */ + if (ib) { + wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.pass_enc, ib->wgpu.buf, ib_offset); + } + + /* vertex buffers */ + for (uint32_t slot = 0; slot < (uint32_t)num_vbs; slot++) { + wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.pass_enc, slot, vbs[slot]->wgpu.buf, (uint64_t)vb_offsets[slot]); + } + + /* need to create throw-away bind groups for images */ + if (num_vs_imgs > 0) { + if (num_vs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { + num_vs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + } + WGPUBindGroupLayout vs_bgl = pip->shader->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout; + SOKOL_ASSERT(vs_bgl); + WGPUBindGroup vs_img_bg = _sg_wgpu_create_images_bindgroup(vs_bgl, vs_imgs, num_vs_imgs); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, vs_img_bg, 0, 0); + wgpuBindGroupRelease(vs_img_bg); + } + else { + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, _sg.wgpu.empty_bind_group, 0, 0); + } + if (num_fs_imgs > 0) { + if (num_fs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { + num_fs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + } + WGPUBindGroupLayout fs_bgl = pip->shader->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout; + SOKOL_ASSERT(fs_bgl); + WGPUBindGroup fs_img_bg = _sg_wgpu_create_images_bindgroup(fs_bgl, fs_imgs, num_fs_imgs); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, fs_img_bg, 0, 0); + wgpuBindGroupRelease(fs_img_bg); + } + else { + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, _sg.wgpu.empty_bind_group, 0, 0); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT((_sg.wgpu.ub.offset + data->size) <= _sg.wgpu.ub.num_bytes); + SOKOL_ASSERT((_sg.wgpu.ub.offset & (_SG_WGPU_STAGING_ALIGN-1)) == 0); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline && _sg.wgpu.cur_pipeline->shader); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline->shader->slot.id == _sg.wgpu.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size <= _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + SOKOL_ASSERT(data->size <= _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); + SOKOL_ASSERT(0 != _sg.wgpu.ub.stage.ptr[_sg.wgpu.ub.stage.cur]); + + uint8_t* dst_ptr = _sg.wgpu.ub.stage.ptr[_sg.wgpu.ub.stage.cur] + _sg.wgpu.ub.offset; + memcpy(dst_ptr, data->ptr, data->size); + _sg.wgpu.ub.bind_offsets[stage_index][ub_index] = _sg.wgpu.ub.offset; + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + 0, /* groupIndex 0 is reserved for uniform buffers */ + _sg.wgpu.ub.bindgroup, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.ub.bind_offsets[0][0]); + _sg.wgpu.ub.offset = _sg_roundup(_sg.wgpu.ub.offset + data->size, _SG_WGPU_STAGING_ALIGN); +} + +_SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + if (_sg.wgpu.draw_indexed) { + wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0, 0); + } + else { + wgpuRenderPassEncoderDraw(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + uint32_t copied_num_bytes = _sg_wgpu_staging_copy_to_buffer(buf->wgpu.buf, 0, data->ptr, data->size); + SOKOL_ASSERT(copied_num_bytes > 0); _SOKOL_UNUSED(copied_num_bytes); +} + +_SOKOL_PRIVATE int _sg_wgpu_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(new_frame); + uint32_t copied_num_bytes = _sg_wgpu_staging_copy_to_buffer(buf->wgpu.buf, buf->cmn.append_pos, data->ptr, data->size); + SOKOL_ASSERT(copied_num_bytes > 0); _SOKOL_UNUSED(copied_num_bytes); + return (int)copied_num_bytes; +} + +_SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + bool success = _sg_wgpu_staging_copy_to_texture(img, data); + SOKOL_ASSERT(success); + _SOKOL_UNUSED(success); +} +#endif + +/*== BACKEND API WRAPPERS ====================================================*/ +static inline void _sg_setup_backend(const sg_desc* desc) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_setup_backend(desc); +#elif defined(SOKOL_METAL) + _sg_mtl_setup_backend(desc); +#elif defined(SOKOL_D3D11) + _sg_d3d11_setup_backend(desc); +#elif defined(SOKOL_WGPU) + _sg_wgpu_setup_backend(desc); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_setup_backend(desc); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_discard_backend(void) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_discard_backend(); +#elif defined(SOKOL_METAL) + _sg_mtl_discard_backend(); +#elif defined(SOKOL_D3D11) + _sg_d3d11_discard_backend(); +#elif defined(SOKOL_WGPU) + _sg_wgpu_discard_backend(); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_backend(); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_reset_state_cache(void) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_reset_state_cache(); +#elif defined(SOKOL_METAL) + _sg_mtl_reset_state_cache(); +#elif defined(SOKOL_D3D11) + _sg_d3d11_reset_state_cache(); +#elif defined(SOKOL_WGPU) + _sg_wgpu_reset_state_cache(); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_reset_state_cache(); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_activate_context(_sg_context_t* ctx) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_activate_context(ctx); +#elif defined(SOKOL_METAL) + _sg_mtl_activate_context(ctx); +#elif defined(SOKOL_D3D11) + _sg_d3d11_activate_context(ctx); +#elif defined(SOKOL_WGPU) + _sg_wgpu_activate_context(ctx); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_activate_context(ctx); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline sg_resource_state _sg_create_context(_sg_context_t* ctx) { +#if defined(_SOKOL_ANY_GL) + return _sg_gl_create_context(ctx); +#elif defined(SOKOL_METAL) + return _sg_mtl_create_context(ctx); +#elif defined(SOKOL_D3D11) + return _sg_d3d11_create_context(ctx); +#elif defined(SOKOL_WGPU) + return _sg_wgpu_create_context(ctx); +#elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_context(ctx); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_destroy_context(_sg_context_t* ctx) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_context(ctx); +#elif defined(SOKOL_METAL) + _sg_mtl_destroy_context(ctx); +#elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_context(ctx); +#elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_context(ctx); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_context(ctx); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { +#if defined(_SOKOL_ANY_GL) + return _sg_gl_create_buffer(buf, desc); +#elif defined(SOKOL_METAL) + return _sg_mtl_create_buffer(buf, desc); +#elif defined(SOKOL_D3D11) + return _sg_d3d11_create_buffer(buf, desc); +#elif defined(SOKOL_WGPU) + return _sg_wgpu_create_buffer(buf, desc); +#elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_buffer(buf, desc); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_destroy_buffer(_sg_buffer_t* buf) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_buffer(buf); +#elif defined(SOKOL_METAL) + _sg_mtl_destroy_buffer(buf); +#elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_buffer(buf); +#elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_buffer(buf); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_buffer(buf); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { +#if defined(_SOKOL_ANY_GL) + return _sg_gl_create_image(img, desc); +#elif defined(SOKOL_METAL) + return _sg_mtl_create_image(img, desc); +#elif defined(SOKOL_D3D11) + return _sg_d3d11_create_image(img, desc); +#elif defined(SOKOL_WGPU) + return _sg_wgpu_create_image(img, desc); +#elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_image(img, desc); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_destroy_image(_sg_image_t* img) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_image(img); +#elif defined(SOKOL_METAL) + _sg_mtl_destroy_image(img); +#elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_image(img); +#elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_image(img); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_image(img); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { +#if defined(_SOKOL_ANY_GL) + return _sg_gl_create_shader(shd, desc); +#elif defined(SOKOL_METAL) + return _sg_mtl_create_shader(shd, desc); +#elif defined(SOKOL_D3D11) + return _sg_d3d11_create_shader(shd, desc); +#elif defined(SOKOL_WGPU) + return _sg_wgpu_create_shader(shd, desc); +#elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_shader(shd, desc); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_destroy_shader(_sg_shader_t* shd) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_shader(shd); +#elif defined(SOKOL_METAL) + _sg_mtl_destroy_shader(shd); +#elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_shader(shd); +#elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_shader(shd); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_shader(shd); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { +#if defined(_SOKOL_ANY_GL) + return _sg_gl_create_pipeline(pip, shd, desc); +#elif defined(SOKOL_METAL) + return _sg_mtl_create_pipeline(pip, shd, desc); +#elif defined(SOKOL_D3D11) + return _sg_d3d11_create_pipeline(pip, shd, desc); +#elif defined(SOKOL_WGPU) + return _sg_wgpu_create_pipeline(pip, shd, desc); +#elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_pipeline(pip, shd, desc); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_destroy_pipeline(_sg_pipeline_t* pip) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_pipeline(pip); +#elif defined(SOKOL_METAL) + _sg_mtl_destroy_pipeline(pip); +#elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_pipeline(pip); +#elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_pipeline(pip); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_pipeline(pip); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { +#if defined(_SOKOL_ANY_GL) + return _sg_gl_create_pass(pass, att_images, desc); +#elif defined(SOKOL_METAL) + return _sg_mtl_create_pass(pass, att_images, desc); +#elif defined(SOKOL_D3D11) + return _sg_d3d11_create_pass(pass, att_images, desc); +#elif defined(SOKOL_WGPU) + return _sg_wgpu_create_pass(pass, att_images, desc); +#elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_pass(pass, att_images, desc); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_destroy_pass(_sg_pass_t* pass) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_pass(pass); +#elif defined(SOKOL_METAL) + _sg_mtl_destroy_pass(pass); +#elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_pass(pass); +#elif defined(SOKOL_WGPU) + return _sg_wgpu_destroy_pass(pass); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_pass(pass); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline _sg_image_t* _sg_pass_color_image(const _sg_pass_t* pass, int index) { +#if defined(_SOKOL_ANY_GL) + return _sg_gl_pass_color_image(pass, index); +#elif defined(SOKOL_METAL) + return _sg_mtl_pass_color_image(pass, index); +#elif defined(SOKOL_D3D11) + return _sg_d3d11_pass_color_image(pass, index); +#elif defined(SOKOL_WGPU) + return _sg_wgpu_pass_color_image(pass, index); +#elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_pass_color_image(pass, index); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline _sg_image_t* _sg_pass_ds_image(const _sg_pass_t* pass) { +#if defined(_SOKOL_ANY_GL) + return _sg_gl_pass_ds_image(pass); +#elif defined(SOKOL_METAL) + return _sg_mtl_pass_ds_image(pass); +#elif defined(SOKOL_D3D11) + return _sg_d3d11_pass_ds_image(pass); +#elif defined(SOKOL_WGPU) + return _sg_wgpu_pass_ds_image(pass); +#elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_pass_ds_image(pass); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_begin_pass(pass, action, w, h); +#elif defined(SOKOL_METAL) + _sg_mtl_begin_pass(pass, action, w, h); +#elif defined(SOKOL_D3D11) + _sg_d3d11_begin_pass(pass, action, w, h); +#elif defined(SOKOL_WGPU) + _sg_wgpu_begin_pass(pass, action, w, h); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_begin_pass(pass, action, w, h); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_end_pass(void) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_end_pass(); +#elif defined(SOKOL_METAL) + _sg_mtl_end_pass(); +#elif defined(SOKOL_D3D11) + _sg_d3d11_end_pass(); +#elif defined(SOKOL_WGPU) + _sg_wgpu_end_pass(); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_end_pass(); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_apply_viewport(x, y, w, h, origin_top_left); +#elif defined(SOKOL_METAL) + _sg_mtl_apply_viewport(x, y, w, h, origin_top_left); +#elif defined(SOKOL_D3D11) + _sg_d3d11_apply_viewport(x, y, w, h, origin_top_left); +#elif defined(SOKOL_WGPU) + _sg_wgpu_apply_viewport(x, y, w, h, origin_top_left); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_viewport(x, y, w, h, origin_top_left); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_apply_scissor_rect(x, y, w, h, origin_top_left); +#elif defined(SOKOL_METAL) + _sg_mtl_apply_scissor_rect(x, y, w, h, origin_top_left); +#elif defined(SOKOL_D3D11) + _sg_d3d11_apply_scissor_rect(x, y, w, h, origin_top_left); +#elif defined(SOKOL_WGPU) + _sg_wgpu_apply_scissor_rect(x, y, w, h, origin_top_left); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_scissor_rect(x, y, w, h, origin_top_left); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_apply_pipeline(_sg_pipeline_t* pip) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_apply_pipeline(pip); +#elif defined(SOKOL_METAL) + _sg_mtl_apply_pipeline(pip); +#elif defined(SOKOL_D3D11) + _sg_d3d11_apply_pipeline(pip); +#elif defined(SOKOL_WGPU) + _sg_wgpu_apply_pipeline(pip); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_pipeline(pip); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ +#if defined(_SOKOL_ANY_GL) + _sg_gl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); +#elif defined(SOKOL_METAL) + _sg_mtl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); +#elif defined(SOKOL_D3D11) + _sg_d3d11_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); +#elif defined(SOKOL_WGPU) + _sg_wgpu_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_apply_uniforms(stage_index, ub_index, data); +#elif defined(SOKOL_METAL) + _sg_mtl_apply_uniforms(stage_index, ub_index, data); +#elif defined(SOKOL_D3D11) + _sg_d3d11_apply_uniforms(stage_index, ub_index, data); +#elif defined(SOKOL_WGPU) + _sg_wgpu_apply_uniforms(stage_index, ub_index, data); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_uniforms(stage_index, ub_index, data); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_draw(int base_element, int num_elements, int num_instances) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_draw(base_element, num_elements, num_instances); +#elif defined(SOKOL_METAL) + _sg_mtl_draw(base_element, num_elements, num_instances); +#elif defined(SOKOL_D3D11) + _sg_d3d11_draw(base_element, num_elements, num_instances); +#elif defined(SOKOL_WGPU) + _sg_wgpu_draw(base_element, num_elements, num_instances); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_draw(base_element, num_elements, num_instances); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_commit(void) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_commit(); +#elif defined(SOKOL_METAL) + _sg_mtl_commit(); +#elif defined(SOKOL_D3D11) + _sg_d3d11_commit(); +#elif defined(SOKOL_WGPU) + _sg_wgpu_commit(); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_commit(); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_update_buffer(_sg_buffer_t* buf, const sg_range* data) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_update_buffer(buf, data); +#elif defined(SOKOL_METAL) + _sg_mtl_update_buffer(buf, data); +#elif defined(SOKOL_D3D11) + _sg_d3d11_update_buffer(buf, data); +#elif defined(SOKOL_WGPU) + _sg_wgpu_update_buffer(buf, data); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_buffer(buf, data); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline int _sg_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { +#if defined(_SOKOL_ANY_GL) + return _sg_gl_append_buffer(buf, data, new_frame); +#elif defined(SOKOL_METAL) + return _sg_mtl_append_buffer(buf, data, new_frame); +#elif defined(SOKOL_D3D11) + return _sg_d3d11_append_buffer(buf, data, new_frame); +#elif defined(SOKOL_WGPU) + return _sg_wgpu_append_buffer(buf, data, new_frame); +#elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_append_buffer(buf, data, new_frame); +#else +#error("INVALID BACKEND"); +#endif +} + +static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data) { +#if defined(_SOKOL_ANY_GL) + _sg_gl_update_image(img, data); +#elif defined(SOKOL_METAL) + _sg_mtl_update_image(img, data); +#elif defined(SOKOL_D3D11) + _sg_d3d11_update_image(img, data); +#elif defined(SOKOL_WGPU) + _sg_wgpu_update_image(img, data); +#elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_image(img, data); +#else +#error("INVALID BACKEND"); +#endif +} + +/*== RESOURCE POOLS ==========================================================*/ + +_SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ + pool->size = num + 1; + pool->queue_top = 0; + /* generation counters indexable by pool slot index, slot 0 is reserved */ + size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; + pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); + SOKOL_ASSERT(pool->gen_ctrs); + memset(pool->gen_ctrs, 0, gen_ctrs_size); + /* it's not a bug to only reserve 'num' here */ + pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int) * (size_t)num); + SOKOL_ASSERT(pool->free_queue); + /* never allocate the zero-th pool item since the invalid id is 0 */ + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +_SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_FREE(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + SOKOL_FREE(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } + else { + /* pool exhausted */ + return _SG_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); +#ifdef SOKOL_DEBUG + /* debug check against double-free */ + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } +#endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +_SOKOL_PRIVATE void _sg_reset_slot(_sg_slot_t* slot) { + SOKOL_ASSERT(slot); + memset(slot, 0, sizeof(_sg_slot_t)); +} + +_SOKOL_PRIVATE void _sg_reset_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _sg_slot_t slot = buf->slot; + memset(buf, 0, sizeof(_sg_buffer_t)); + buf->slot = slot; + buf->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _sg_slot_t slot = img->slot; + memset(img, 0, sizeof(_sg_image_t)); + img->slot = slot; + img->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _sg_slot_t slot = shd->slot; + memset(shd, 0, sizeof(_sg_shader_t)); + shd->slot = slot; + shd->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_slot_t slot = pip->slot; + memset(pip, 0, sizeof(_sg_pipeline_t)); + pip->slot = slot; + pip->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + _sg_slot_t slot = pass->slot; + memset(pass, 0, sizeof(_sg_pass_t)); + pass->slot = slot; + pass->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _sg_slot_t slot = ctx->slot; + memset(ctx, 0, sizeof(_sg_context_t)); + ctx->slot = slot; + ctx->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { + SOKOL_ASSERT(p); + SOKOL_ASSERT(desc); + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->buffer_pool, desc->buffer_pool_size); + size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * (size_t)p->buffer_pool.size; + p->buffers = (_sg_buffer_t*) SOKOL_MALLOC(buffer_pool_byte_size); + SOKOL_ASSERT(p->buffers); + memset(p->buffers, 0, buffer_pool_byte_size); + + SOKOL_ASSERT((desc->image_pool_size > 0) && (desc->image_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->image_pool, desc->image_pool_size); + size_t image_pool_byte_size = sizeof(_sg_image_t) * (size_t)p->image_pool.size; + p->images = (_sg_image_t*) SOKOL_MALLOC(image_pool_byte_size); + SOKOL_ASSERT(p->images); + memset(p->images, 0, image_pool_byte_size); + + SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->shader_pool, desc->shader_pool_size); + size_t shader_pool_byte_size = sizeof(_sg_shader_t) * (size_t)p->shader_pool.size; + p->shaders = (_sg_shader_t*) SOKOL_MALLOC(shader_pool_byte_size); + SOKOL_ASSERT(p->shaders); + memset(p->shaders, 0, shader_pool_byte_size); + + SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->pipeline_pool, desc->pipeline_pool_size); + size_t pipeline_pool_byte_size = sizeof(_sg_pipeline_t) * (size_t)p->pipeline_pool.size; + p->pipelines = (_sg_pipeline_t*) SOKOL_MALLOC(pipeline_pool_byte_size); + SOKOL_ASSERT(p->pipelines); + memset(p->pipelines, 0, pipeline_pool_byte_size); + + SOKOL_ASSERT((desc->pass_pool_size > 0) && (desc->pass_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->pass_pool, desc->pass_pool_size); + size_t pass_pool_byte_size = sizeof(_sg_pass_t) * (size_t)p->pass_pool.size; + p->passes = (_sg_pass_t*) SOKOL_MALLOC(pass_pool_byte_size); + SOKOL_ASSERT(p->passes); + memset(p->passes, 0, pass_pool_byte_size); + + SOKOL_ASSERT((desc->context_pool_size > 0) && (desc->context_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->context_pool, desc->context_pool_size); + size_t context_pool_byte_size = sizeof(_sg_context_t) * (size_t)p->context_pool.size; + p->contexts = (_sg_context_t*) SOKOL_MALLOC(context_pool_byte_size); + SOKOL_ASSERT(p->contexts); + memset(p->contexts, 0, context_pool_byte_size); +} + +_SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { + SOKOL_ASSERT(p); + SOKOL_FREE(p->contexts); p->contexts = 0; + SOKOL_FREE(p->passes); p->passes = 0; + SOKOL_FREE(p->pipelines); p->pipelines = 0; + SOKOL_FREE(p->shaders); p->shaders = 0; + SOKOL_FREE(p->images); p->images = 0; + SOKOL_FREE(p->buffers); p->buffers = 0; + _sg_discard_pool(&p->context_pool); + _sg_discard_pool(&p->pass_pool); + _sg_discard_pool(&p->pipeline_pool); + _sg_discard_pool(&p->shader_pool); + _sg_discard_pool(&p->image_pool); + _sg_discard_pool(&p->buffer_pool); +} + +/* allocate the slot at slot_index: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SG_SLOT_SHIFT)|(slot_index & _SG_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +/* extract slot index from id */ +_SOKOL_PRIVATE int _sg_slot_index(uint32_t id) { + int slot_index = (int) (id & _SG_SLOT_MASK); + SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +/* returns pointer to resource by id without matching id check */ +_SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(const _sg_pools_t* p, uint32_t buf_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != buf_id)); + int slot_index = _sg_slot_index(buf_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->buffer_pool.size)); + return &p->buffers[slot_index]; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_image_at(const _sg_pools_t* p, uint32_t img_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != img_id)); + int slot_index = _sg_slot_index(img_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->image_pool.size)); + return &p->images[slot_index]; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(const _sg_pools_t* p, uint32_t shd_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != shd_id)); + int slot_index = _sg_slot_index(shd_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->shader_pool.size)); + return &p->shaders[slot_index]; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_pipeline_at(const _sg_pools_t* p, uint32_t pip_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != pip_id)); + int slot_index = _sg_slot_index(pip_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pipeline_pool.size)); + return &p->pipelines[slot_index]; +} + +_SOKOL_PRIVATE _sg_pass_t* _sg_pass_at(const _sg_pools_t* p, uint32_t pass_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != pass_id)); + int slot_index = _sg_slot_index(pass_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pass_pool.size)); + return &p->passes[slot_index]; +} + +_SOKOL_PRIVATE _sg_context_t* _sg_context_at(const _sg_pools_t* p, uint32_t context_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != context_id)); + int slot_index = _sg_slot_index(context_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->context_pool.size)); + return &p->contexts[slot_index]; +} + +/* returns pointer to resource with matching id check, may return 0 */ +_SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(const _sg_pools_t* p, uint32_t buf_id) { + if (SG_INVALID_ID != buf_id) { + _sg_buffer_t* buf = _sg_buffer_at(p, buf_id); + if (buf->slot.id == buf_id) { + return buf; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(const _sg_pools_t* p, uint32_t img_id) { + if (SG_INVALID_ID != img_id) { + _sg_image_t* img = _sg_image_at(p, img_id); + if (img->slot.id == img_id) { + return img; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(const _sg_pools_t* p, uint32_t shd_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != shd_id) { + _sg_shader_t* shd = _sg_shader_at(p, shd_id); + if (shd->slot.id == shd_id) { + return shd; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_lookup_pipeline(const _sg_pools_t* p, uint32_t pip_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != pip_id) { + _sg_pipeline_t* pip = _sg_pipeline_at(p, pip_id); + if (pip->slot.id == pip_id) { + return pip; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_pass_t* _sg_lookup_pass(const _sg_pools_t* p, uint32_t pass_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != pass_id) { + _sg_pass_t* pass = _sg_pass_at(p, pass_id); + if (pass->slot.id == pass_id) { + return pass; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_context_t* _sg_lookup_context(const _sg_pools_t* p, uint32_t ctx_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != ctx_id) { + _sg_context_t* ctx = _sg_context_at(p, ctx_id); + if (ctx->slot.id == ctx_id) { + return ctx; + } + } + return 0; +} + +_SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { + /* this is a bit dumb since it loops over all pool slots to + find the occupied slots, on the other hand it is only ever + executed at shutdown + NOTE: ONLY EXECUTE THIS AT SHUTDOWN + ...because the free queues will not be reset + and the resource slots not be cleared! + */ + for (int i = 1; i < p->buffer_pool.size; i++) { + if (p->buffers[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->buffers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_buffer(&p->buffers[i]); + } + } + } + for (int i = 1; i < p->image_pool.size; i++) { + if (p->images[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->images[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_image(&p->images[i]); + } + } + } + for (int i = 1; i < p->shader_pool.size; i++) { + if (p->shaders[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->shaders[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_shader(&p->shaders[i]); + } + } + } + for (int i = 1; i < p->pipeline_pool.size; i++) { + if (p->pipelines[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->pipelines[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_pipeline(&p->pipelines[i]); + } + } + } + for (int i = 1; i < p->pass_pool.size; i++) { + if (p->passes[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->passes[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_pass(&p->passes[i]); + } + } + } +} + +/*== VALIDATION LAYER ========================================================*/ +#if defined(SOKOL_DEBUG) +/* return a human readable string for an _sg_validate_error */ +_SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) { + switch (err) { + /* buffer creation validation errors */ + case _SG_VALIDATE_BUFFERDESC_CANARY: return "sg_buffer_desc not initialized"; + case _SG_VALIDATE_BUFFERDESC_SIZE: return "sg_buffer_desc.size cannot be 0"; + case _SG_VALIDATE_BUFFERDESC_DATA: return "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)"; + case _SG_VALIDATE_BUFFERDESC_DATA_SIZE: return "immutable buffer data size differs from buffer size"; + case _SG_VALIDATE_BUFFERDESC_NO_DATA: return "dynamic/stream usage buffers cannot be initialized with data"; + + /* image data (in image creation and updating) */ + case _SG_VALIDATE_IMAGEDATA_NODATA: return "sg_image_data: no data (.ptr and/or .size is zero)"; + case _SG_VALIDATE_IMAGEDATA_DATA_SIZE: return "sg_image_data: data size doesn't match expected surface size"; + + /* image creation validation errros */ + case _SG_VALIDATE_IMAGEDESC_CANARY: return "sg_image_desc not initialized"; + case _SG_VALIDATE_IMAGEDESC_WIDTH: return "sg_image_desc.width must be > 0"; + case _SG_VALIDATE_IMAGEDESC_HEIGHT: return "sg_image_desc.height must be > 0"; + case _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT: return "invalid pixel format for render-target image"; + case _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT: return "invalid pixel format for non-render-target image"; + case _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT: return "non-render-target images cannot be multisampled"; + case _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT: return "MSAA not supported for this pixel format"; + case _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE: return "render target images must be SG_USAGE_IMMUTABLE"; + case _SG_VALIDATE_IMAGEDESC_RT_NO_DATA: return "render target images cannot be initialized with data"; + case _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA: return "images with injected textures cannot be initialized with data"; + case _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA: return "dynamic/stream images cannot be initialized with data"; + case _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE: return "compressed images must be immutable"; + + /* shader creation */ + case _SG_VALIDATE_SHADERDESC_CANARY: return "sg_shader_desc not initialized"; + case _SG_VALIDATE_SHADERDESC_SOURCE: return "shader source code required"; + case _SG_VALIDATE_SHADERDESC_BYTECODE: return "shader byte code required"; + case _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE: return "shader source or byte code required"; + case _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE: return "shader byte code length (in bytes) required"; + case _SG_VALIDATE_SHADERDESC_NO_CONT_UBS: return "shader uniform blocks must occupy continuous slots"; + case _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS: return "uniform block members must occupy continuous slots"; + case _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS: return "GL backend requires uniform block member declarations"; + case _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME: return "uniform block member name missing"; + case _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH: return "size of uniform block members doesn't match uniform block size"; + case _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT: return "uniform array count must be >= 1"; + case _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE: return "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout"; + + case _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS: return "shader images must occupy continuous slots"; + case _SG_VALIDATE_SHADERDESC_IMG_NAME: return "GL backend requires uniform block member names"; + case _SG_VALIDATE_SHADERDESC_ATTR_NAMES: return "GLES2 backend requires vertex attribute names"; + case _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS: return "D3D11 backend requires vertex attribute semantics"; + case _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG: return "vertex attribute name/semantic string too long (max len 16)"; + + /* pipeline creation */ + case _SG_VALIDATE_PIPELINEDESC_CANARY: return "sg_pipeline_desc not initialized"; + case _SG_VALIDATE_PIPELINEDESC_SHADER: return "sg_pipeline_desc.shader missing or invalid"; + case _SG_VALIDATE_PIPELINEDESC_NO_ATTRS: return "sg_pipeline_desc.layout.attrs is empty or not continuous"; + case _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4: return "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4"; + case _SG_VALIDATE_PIPELINEDESC_ATTR_NAME: return "GLES2/WebGL missing vertex attribute name in shader"; + case _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS: return "D3D11 missing vertex attribute semantics in shader"; + + /* pass creation */ + case _SG_VALIDATE_PASSDESC_CANARY: return "sg_pass_desc not initialized"; + case _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS: return "sg_pass_desc.color_attachments[0] must be valid"; + case _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS: return "color attachments must occupy continuous slots"; + case _SG_VALIDATE_PASSDESC_IMAGE: return "pass attachment image is not valid"; + case _SG_VALIDATE_PASSDESC_MIPLEVEL: return "pass attachment mip level is bigger than image has mipmaps"; + case _SG_VALIDATE_PASSDESC_FACE: return "pass attachment image is cubemap, but face index is too big"; + case _SG_VALIDATE_PASSDESC_LAYER: return "pass attachment image is array texture, but layer index is too big"; + case _SG_VALIDATE_PASSDESC_SLICE: return "pass attachment image is 3d texture, but slice value is too big"; + case _SG_VALIDATE_PASSDESC_IMAGE_NO_RT: return "pass attachment image must be render targets"; + case _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT: return "pass color-attachment images must have a renderable pixel format"; + case _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT: return "pass depth-attachment image must have depth pixel format"; + case _SG_VALIDATE_PASSDESC_IMAGE_SIZES: return "all pass attachments must have the same size"; + case _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS: return "all pass attachments must have the same sample count"; + + /* sg_begin_pass */ + case _SG_VALIDATE_BEGINPASS_PASS: return "sg_begin_pass: pass must be valid"; + case _SG_VALIDATE_BEGINPASS_IMAGE: return "sg_begin_pass: one or more attachment images are not valid"; + + /* sg_apply_pipeline */ + case _SG_VALIDATE_APIP_PIPELINE_VALID_ID: return "sg_apply_pipeline: invalid pipeline id provided"; + case _SG_VALIDATE_APIP_PIPELINE_EXISTS: return "sg_apply_pipeline: pipeline object no longer alive"; + case _SG_VALIDATE_APIP_PIPELINE_VALID: return "sg_apply_pipeline: pipeline object not in valid state"; + case _SG_VALIDATE_APIP_SHADER_EXISTS: return "sg_apply_pipeline: shader object no longer alive"; + case _SG_VALIDATE_APIP_SHADER_VALID: return "sg_apply_pipeline: shader object not in valid state"; + case _SG_VALIDATE_APIP_ATT_COUNT: return "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments"; + case _SG_VALIDATE_APIP_COLOR_FORMAT: return "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format"; + case _SG_VALIDATE_APIP_DEPTH_FORMAT: return "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format"; + case _SG_VALIDATE_APIP_SAMPLE_COUNT: return "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count"; + + /* sg_apply_bindings */ + case _SG_VALIDATE_ABND_PIPELINE: return "sg_apply_bindings: must be called after sg_apply_pipeline"; + case _SG_VALIDATE_ABND_PIPELINE_EXISTS: return "sg_apply_bindings: currently applied pipeline object no longer alive"; + case _SG_VALIDATE_ABND_PIPELINE_VALID: return "sg_apply_bindings: currently applied pipeline object not in valid state"; + case _SG_VALIDATE_ABND_VBS: return "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts"; + case _SG_VALIDATE_ABND_VB_EXISTS: return "sg_apply_bindings: vertex buffer no longer alive"; + case _SG_VALIDATE_ABND_VB_TYPE: return "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER"; + case _SG_VALIDATE_ABND_VB_OVERFLOW: return "sg_apply_bindings: buffer in vertex buffer slot is overflown"; + case _SG_VALIDATE_ABND_NO_IB: return "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided"; + case _SG_VALIDATE_ABND_IB: return "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided"; + case _SG_VALIDATE_ABND_IB_EXISTS: return "sg_apply_bindings: index buffer no longer alive"; + case _SG_VALIDATE_ABND_IB_TYPE: return "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER"; + case _SG_VALIDATE_ABND_IB_OVERFLOW: return "sg_apply_bindings: buffer in index buffer slot is overflown"; + case _SG_VALIDATE_ABND_VS_IMGS: return "sg_apply_bindings: vertex shader image count doesn't match sg_shader_desc"; + case _SG_VALIDATE_ABND_VS_IMG_EXISTS: return "sg_apply_bindings: vertex shader image no longer alive"; + case _SG_VALIDATE_ABND_VS_IMG_TYPES: return "sg_apply_bindings: one or more vertex shader image types don't match sg_shader_desc"; + case _SG_VALIDATE_ABND_FS_IMGS: return "sg_apply_bindings: fragment shader image count doesn't match sg_shader_desc"; + case _SG_VALIDATE_ABND_FS_IMG_EXISTS: return "sg_apply_bindings: fragment shader image no longer alive"; + case _SG_VALIDATE_ABND_FS_IMG_TYPES: return "sg_apply_bindings: one or more fragment shader image types don't match sg_shader_desc"; + + /* sg_apply_uniforms */ + case _SG_VALIDATE_AUB_NO_PIPELINE: return "sg_apply_uniforms: must be called after sg_apply_pipeline()"; + case _SG_VALIDATE_AUB_NO_UB_AT_SLOT: return "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot"; + case _SG_VALIDATE_AUB_SIZE: return "sg_apply_uniforms: data size exceeds declared uniform block size"; + + /* sg_update_buffer */ + case _SG_VALIDATE_UPDATEBUF_USAGE: return "sg_update_buffer: cannot update immutable buffer"; + case _SG_VALIDATE_UPDATEBUF_SIZE: return "sg_update_buffer: update size is bigger than buffer size"; + case _SG_VALIDATE_UPDATEBUF_ONCE: return "sg_update_buffer: only one update allowed per buffer and frame"; + case _SG_VALIDATE_UPDATEBUF_APPEND: return "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame"; + + /* sg_append_buffer */ + case _SG_VALIDATE_APPENDBUF_USAGE: return "sg_append_buffer: cannot append to immutable buffer"; + case _SG_VALIDATE_APPENDBUF_SIZE: return "sg_append_buffer: overall appended size is bigger than buffer size"; + case _SG_VALIDATE_APPENDBUF_UPDATE: return "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame"; + + /* sg_update_image */ + case _SG_VALIDATE_UPDIMG_USAGE: return "sg_update_image: cannot update immutable image"; + case _SG_VALIDATE_UPDIMG_ONCE: return "sg_update_image: only one update allowed per image and frame"; + + default: return "unknown validation error"; + } +} +#endif /* defined(SOKOL_DEBUG) */ + +/*-- validation checks -------------------------------------------------------*/ +#if defined(SOKOL_DEBUG) +_SOKOL_PRIVATE void _sg_validate_begin(void) { + _sg.validate_error = _SG_VALIDATE_SUCCESS; +} + +_SOKOL_PRIVATE void _sg_validate(bool cond, _sg_validate_error_t err) { + if (!cond) { + _sg.validate_error = err; + SOKOL_LOG(_sg_validate_string(err)); + } +} + +_SOKOL_PRIVATE bool _sg_validate_end(void) { + if (_sg.validate_error != _SG_VALIDATE_SUCCESS) { +#if !defined(SOKOL_VALIDATE_NON_FATAL) + SOKOL_LOG("^^^^ SOKOL-GFX VALIDATION FAILED, TERMINATING ^^^^"); + SOKOL_ASSERT(false); +#endif + return false; + } + else { + return true; + } +} +#endif + +_SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; +#else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); + SOKOL_VALIDATE(desc->size > 0, _SG_VALIDATE_BUFFERDESC_SIZE); + bool injected = (0 != desc->gl_buffers[0]) || + (0 != desc->mtl_buffers[0]) || + (0 != desc->d3d11_buffer) || + (0 != desc->wgpu_buffer); + if (!injected && (desc->usage == SG_USAGE_IMMUTABLE)) { + SOKOL_VALIDATE((0 != desc->data.ptr) && (desc->data.size > 0), _SG_VALIDATE_BUFFERDESC_DATA); + SOKOL_VALIDATE(desc->size == desc->data.size, _SG_VALIDATE_BUFFERDESC_DATA_SIZE); + } + else { + SOKOL_VALIDATE(0 == desc->data.ptr, _SG_VALIDATE_BUFFERDESC_NO_DATA); + } + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_format fmt, int width, int height, int num_faces, int num_mips, int num_slices) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(data); + _SOKOL_UNUSED(fmt); + _SOKOL_UNUSED(width); + _SOKOL_UNUSED(height); + _SOKOL_UNUSED(num_faces); + _SOKOL_UNUSED(num_mips); + _SOKOL_UNUSED(num_slices); +#else + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + const bool has_data = data->subimage[face_index][mip_index].ptr != 0; + const bool has_size = data->subimage[face_index][mip_index].size > 0; + SOKOL_VALIDATE(has_data && has_size, _SG_VALIDATE_IMAGEDATA_NODATA); + const int mip_width = _sg_max(width >> mip_index, 1); + const int mip_height = _sg_max(height >> mip_index, 1); + const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); + const int expected_size = bytes_per_slice * num_slices; + SOKOL_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, _SG_VALIDATE_IMAGEDATA_DATA_SIZE); + } + } +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; +#else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); + SOKOL_VALIDATE(desc->width > 0, _SG_VALIDATE_IMAGEDESC_WIDTH); + SOKOL_VALIDATE(desc->height > 0, _SG_VALIDATE_IMAGEDESC_HEIGHT); + const sg_pixel_format fmt = desc->pixel_format; + const sg_usage usage = desc->usage; + const bool injected = (0 != desc->gl_textures[0]) || + (0 != desc->mtl_textures[0]) || + (0 != desc->d3d11_texture) || + (0 != desc->wgpu_texture); + if (desc->render_target) { + SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + SOKOL_VALIDATE(_sg.formats[fmt].render, _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT); + /* on GLES2, sample count for render targets is completely ignored */ +#if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + if (!_sg.gl.gles2) { +#endif + if (desc->sample_count > 1) { + SOKOL_VALIDATE(_sg.features.msaa_render_targets && _sg.formats[fmt].msaa, _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); + } +#if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + } +#endif + SOKOL_VALIDATE(usage == SG_USAGE_IMMUTABLE, _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE); + SOKOL_VALIDATE(desc->data.subimage[0][0].ptr==0, _SG_VALIDATE_IMAGEDESC_RT_NO_DATA); + } + else { + SOKOL_VALIDATE(desc->sample_count <= 1, _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); + const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); + SOKOL_VALIDATE(valid_nonrt_fmt, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); + const bool is_compressed = _sg_is_compressed_pixel_format(desc->pixel_format); + const bool is_immutable = (usage == SG_USAGE_IMMUTABLE); + if (is_compressed) { + SOKOL_VALIDATE(is_immutable, _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); + } + if (!injected && is_immutable) { + // image desc must have valid data + _sg_validate_image_data(&desc->data, + desc->pixel_format, + desc->width, + desc->height, + (desc->type == SG_IMAGETYPE_CUBE) ? 6 : 1, + desc->num_mipmaps, + desc->num_slices); + } + else { + // image desc must not have data + for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { + for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { + const bool no_data = 0 == desc->data.subimage[face_index][mip_index].ptr; + const bool no_size = 0 == desc->data.subimage[face_index][mip_index].size; + if (injected) { + SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA); + } + if (!is_immutable) { + SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); + } + } + } + } + } + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; +#else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); +#if defined(SOKOL_GLES2) + SOKOL_VALIDATE(0 != desc->attrs[0].name, _SG_VALIDATE_SHADERDESC_ATTR_NAMES); +#elif defined(SOKOL_D3D11) + SOKOL_VALIDATE(0 != desc->attrs[0].sem_name, _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS); +#endif +#if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + /* on GL, must provide shader source code */ + SOKOL_VALIDATE(0 != desc->vs.source, _SG_VALIDATE_SHADERDESC_SOURCE); + SOKOL_VALIDATE(0 != desc->fs.source, _SG_VALIDATE_SHADERDESC_SOURCE); +#elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + /* on Metal or D3D11, must provide shader source code or byte code */ + SOKOL_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + SOKOL_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); +#elif defined(SOKOL_WGPU) + /* on WGPU byte code must be provided */ + SOKOL_VALIDATE((0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); + SOKOL_VALIDATE((0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); +#else + /* Dummy Backend, don't require source or bytecode */ +#endif + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (desc->attrs[i].name) { + SOKOL_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + } + if (desc->attrs[i].sem_name) { + SOKOL_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + } + } + /* if shader byte code, the size must also be provided */ + if (0 != desc->vs.bytecode.ptr) { + SOKOL_VALIDATE(desc->vs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + if (0 != desc->fs.bytecode.ptr) { + SOKOL_VALIDATE(desc->fs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == 0)? &desc->vs : &desc->fs; + bool uniform_blocks_continuous = true; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (ub_desc->size > 0) { + SOKOL_VALIDATE(uniform_blocks_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UBS); +#if defined(_SOKOL_ANY_GL) + bool uniforms_continuous = true; + uint32_t uniform_offset = 0; + int num_uniforms = 0; + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type != SG_UNIFORMTYPE_INVALID) { + SOKOL_VALIDATE(uniforms_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); +#if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + SOKOL_VALIDATE(0 != u_desc->name, _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME); +#endif + const int array_count = u_desc->array_count; + SOKOL_VALIDATE(array_count > 0, _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT); + const uint32_t u_align = _sg_uniform_alignment(u_desc->type, array_count, ub_desc->layout); + const uint32_t u_size = _sg_uniform_size(u_desc->type, array_count, ub_desc->layout); + uniform_offset = _sg_align_u32(uniform_offset, u_align); + uniform_offset += u_size; + num_uniforms++; + // with std140, arrays are only allowed for FLOAT4, INT4, MAT4 + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + if (array_count > 1) { + SOKOL_VALIDATE((u_desc->type == SG_UNIFORMTYPE_FLOAT4) || (u_desc->type == SG_UNIFORMTYPE_INT4) || (u_desc->type == SG_UNIFORMTYPE_MAT4), _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE); + } + } + } + else { + uniforms_continuous = false; + } + } + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + uniform_offset = _sg_align_u32(uniform_offset, 16); + } + SOKOL_VALIDATE((size_t)uniform_offset == ub_desc->size, _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); + SOKOL_VALIDATE(num_uniforms > 0, _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS); +#endif + } + else { + uniform_blocks_continuous = false; + } + } + bool images_continuous = true; + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->image_type != _SG_IMAGETYPE_DEFAULT) { + SOKOL_VALIDATE(images_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS); +#if defined(SOKOL_GLES2) + SOKOL_VALIDATE(0 != img_desc->name, _SG_VALIDATE_SHADERDESC_IMG_NAME); +#endif + } + else { + images_continuous = false; + } + } + } + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; +#else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); + SOKOL_VALIDATE(desc->shader.id != SG_INVALID_ID, _SG_VALIDATE_PIPELINEDESC_SHADER); + for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[buf_index]; + if (l_desc->stride == 0) { + continue; + } + SOKOL_VALIDATE((l_desc->stride & 3) == 0, _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); + } + SOKOL_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + SOKOL_VALIDATE(0 != shd, _SG_VALIDATE_PIPELINEDESC_SHADER); + if (shd) { + SOKOL_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PIPELINEDESC_SHADER); + bool attrs_cont = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + attrs_cont = false; + continue; + } + SOKOL_VALIDATE(attrs_cont, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); +#if defined(SOKOL_GLES2) + /* on GLES2, vertex attribute names must be provided */ + SOKOL_VALIDATE(!_sg_strempty(&shd->gl.attrs[attr_index].name), _SG_VALIDATE_PIPELINEDESC_ATTR_NAME); +#elif defined(SOKOL_D3D11) + /* on D3D11, semantic names (and semantic indices) must be provided */ + SOKOL_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); +#endif + } + } + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; +#else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); + bool atts_cont = true; + int width = -1, height = -1, sample_count = -1; + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + const sg_pass_attachment_desc* att = &desc->color_attachments[att_index]; + if (att->image.id == SG_INVALID_ID) { + SOKOL_VALIDATE(att_index > 0, _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS); + atts_cont = false; + continue; + } + SOKOL_VALIDATE(atts_cont, _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); + SOKOL_ASSERT(img); + SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); + SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); + } + else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); + } + else if (img->cmn.type == SG_IMAGETYPE_3D) { + SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); + } + SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); + if (att_index == 0) { + width = img->cmn.width >> att->mip_level; + height = img->cmn.height >> att->mip_level; + sample_count = img->cmn.sample_count; + } + else { + SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + } + SOKOL_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); + } + if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { + const sg_pass_attachment_desc* att = &desc->depth_stencil_attachment; + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); + SOKOL_ASSERT(img); + SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); + SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); + } + else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); + } + else if (img->cmn.type == SG_IMAGETYPE_3D) { + SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); + } + SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); + SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + SOKOL_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); + } + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pass); + return true; +#else + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_PASS); + + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_pass_attachment_t* att = &pass->cmn.color_atts[i]; + const _sg_image_t* img = _sg_pass_color_image(pass, i); + if (img) { + SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); + SOKOL_VALIDATE(img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + } + } + const _sg_image_t* ds_img = _sg_pass_ds_image(pass); + if (ds_img) { + const _sg_pass_attachment_t* att = &pass->cmn.ds_att; + SOKOL_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); + SOKOL_VALIDATE(ds_img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + } + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pip_id); + return true; +#else + SOKOL_VALIDATE_BEGIN(); + /* the pipeline object must be alive and valid */ + SOKOL_VALIDATE(pip_id.id != SG_INVALID_ID, _SG_VALIDATE_APIP_PIPELINE_VALID_ID); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_APIP_PIPELINE_EXISTS); + if (!pip) { + return SOKOL_VALIDATE_END(); + } + SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_PIPELINE_VALID); + /* the pipeline's shader must be alive and valid */ + SOKOL_ASSERT(pip->shader); + SOKOL_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, _SG_VALIDATE_APIP_SHADER_EXISTS); + SOKOL_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_SHADER_VALID); + /* check that pipeline attributes match current pass attributes */ + const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, _sg.cur_pass.id); + if (pass) { + /* an offscreen pass */ + SOKOL_VALIDATE(pip->cmn.color_attachment_count == pass->cmn.num_color_atts, _SG_VALIDATE_APIP_ATT_COUNT); + for (int i = 0; i < pip->cmn.color_attachment_count; i++) { + const _sg_image_t* att_img = _sg_pass_color_image(pass, i); + SOKOL_VALIDATE(pip->cmn.color_formats[i] == att_img->cmn.pixel_format, _SG_VALIDATE_APIP_COLOR_FORMAT); + SOKOL_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); + } + const _sg_image_t* att_dsimg = _sg_pass_ds_image(pass); + if (att_dsimg) { + SOKOL_VALIDATE(pip->cmn.depth_format == att_dsimg->cmn.pixel_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); + } + else { + SOKOL_VALIDATE(pip->cmn.depth_format == SG_PIXELFORMAT_NONE, _SG_VALIDATE_APIP_DEPTH_FORMAT); + } + } + else { + /* default pass */ + SOKOL_VALIDATE(pip->cmn.color_attachment_count == 1, _SG_VALIDATE_APIP_ATT_COUNT); + SOKOL_VALIDATE(pip->cmn.color_formats[0] == _sg.desc.context.color_format, _SG_VALIDATE_APIP_COLOR_FORMAT); + SOKOL_VALIDATE(pip->cmn.depth_format == _sg.desc.context.depth_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); + SOKOL_VALIDATE(pip->cmn.sample_count == _sg.desc.context.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); + } + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(bindings); + return true; +#else + SOKOL_VALIDATE_BEGIN(); + + /* a pipeline object must have been applied */ + SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_ABND_PIPELINE); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_ABND_PIPELINE_EXISTS); + if (!pip) { + return SOKOL_VALIDATE_END(); + } + SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_ABND_PIPELINE_VALID); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + + /* has expected vertex buffers, and vertex buffers still exist */ + for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { + SOKOL_VALIDATE(pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + /* buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER */ + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_VB_EXISTS); + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_VB_TYPE); + SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_VB_OVERFLOW); + } + } + else { + /* vertex buffer provided in a slot which has no vertex layout in pipeline */ + SOKOL_VALIDATE(!pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + } + } + + /* index buffer expected or not, and index buffer still exists */ + if (pip->cmn.index_type == SG_INDEXTYPE_NONE) { + /* pipeline defines non-indexed rendering, but index buffer provided */ + SOKOL_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, _SG_VALIDATE_ABND_IB); + } + else { + /* pipeline defines indexed rendering, but no index buffer provided */ + SOKOL_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, _SG_VALIDATE_ABND_NO_IB); + } + if (bindings->index_buffer.id != SG_INVALID_ID) { + /* buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER */ + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_IB_EXISTS); + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_IB_TYPE); + SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_IB_OVERFLOW); + } + } + + /* has expected vertex shader images */ + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (bindings->vs_images[i].id != SG_INVALID_ID) { + SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); + SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_VS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(img->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_VS_IMG_TYPES); + } + } + else { + SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); + } + } + + /* has expected fragment shader images */ + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (bindings->fs_images[i].id != SG_INVALID_ID) { + SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); + SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_FS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(img->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_FS_IMG_TYPES); + } + } + else { + SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); + } + } + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(stage_index); + _SOKOL_UNUSED(ub_index); + _SOKOL_UNUSED(data); + return true; +#else + SOKOL_ASSERT((stage_index == SG_SHADERSTAGE_VS) || (stage_index == SG_SHADERSTAGE_FS)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_AUB_NO_PIPELINE); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_ASSERT(pip && (pip->slot.id == _sg.cur_pipeline.id)); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); + + /* check that there is a uniform block at 'stage' and 'ub_index' */ + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; + SOKOL_VALIDATE(ub_index < stage->num_uniform_blocks, _SG_VALIDATE_AUB_NO_UB_AT_SLOT); + + /* check that the provided data size doesn't exceed the uniform block size */ + SOKOL_VALIDATE(data->size <= stage->uniform_blocks[ub_index].size, _SG_VALIDATE_AUB_SIZE); + + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const sg_range* data) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + return true; +#else + SOKOL_ASSERT(buf && data && data->ptr); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDATEBUF_USAGE); + SOKOL_VALIDATE(buf->cmn.size >= (int)data->size, _SG_VALIDATE_UPDATEBUF_SIZE); + SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_ONCE); + SOKOL_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_APPEND); + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const sg_range* data) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + return true; +#else + SOKOL_ASSERT(buf && data && data->ptr); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_APPENDBUF_USAGE); + SOKOL_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), _SG_VALIDATE_APPENDBUF_SIZE); + SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_APPENDBUF_UPDATE); + return SOKOL_VALIDATE_END(); +#endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_image_data* data) { +#if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(data); + return true; +#else + SOKOL_ASSERT(img && data); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDIMG_USAGE); + SOKOL_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, _SG_VALIDATE_UPDIMG_ONCE); + _sg_validate_image_data(data, + img->cmn.pixel_format, + img->cmn.width, + img->cmn.height, + (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1, + img->cmn.num_mipmaps, + img->cmn.num_slices); + return SOKOL_VALIDATE_END(); +#endif +} + +/*== fill in desc default values =============================================*/ +_SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) { + sg_buffer_desc def = *desc; + def.type = _sg_def(def.type, SG_BUFFERTYPE_VERTEXBUFFER); + def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); + if (def.size == 0) { + def.size = def.data.size; + } + else if (def.data.size == 0) { + def.data.size = def.size; + } + return def; +} + +_SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) { + sg_image_desc def = *desc; + def.type = _sg_def(def.type, SG_IMAGETYPE_2D); + def.num_slices = _sg_def(def.num_slices, 1); + def.num_mipmaps = _sg_def(def.num_mipmaps, 1); + def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); + if (desc->render_target) { + def.pixel_format = _sg_def(def.pixel_format, _sg.desc.context.color_format); + def.sample_count = _sg_def(def.sample_count, _sg.desc.context.sample_count); + } + else { + def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); + def.sample_count = _sg_def(def.sample_count, 1); + } + def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST); + def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST); + def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT); + def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT); + def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT); + def.border_color = _sg_def(def.border_color, SG_BORDERCOLOR_OPAQUE_BLACK); + def.max_anisotropy = _sg_def(def.max_anisotropy, 1); + def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); + return def; +} + +_SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* desc) { + sg_shader_desc def = *desc; +#if defined(SOKOL_METAL) + def.vs.entry = _sg_def(def.vs.entry, "_main"); + def.fs.entry = _sg_def(def.fs.entry, "_main"); +#else + def.vs.entry = _sg_def(def.vs.entry, "main"); + def.fs.entry = _sg_def(def.fs.entry, "main"); +#endif +#if defined(SOKOL_D3D11) + if (def.vs.source) { + def.vs.d3d11_target = _sg_def(def.vs.d3d11_target, "vs_4_0"); + } + if (def.fs.source) { + def.fs.d3d11_target = _sg_def(def.fs.d3d11_target, "ps_4_0"); + } +#endif + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &def.vs : &def.fs; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + ub_desc->layout = _sg_def(ub_desc->layout, SG_UNIFORMLAYOUT_NATIVE); + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type == SG_UNIFORMTYPE_INVALID) { + break; + } + u_desc->array_count = _sg_def(u_desc->array_count, 1); + } + } + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->image_type == _SG_IMAGETYPE_DEFAULT) { + break; + } + img_desc->sampler_type = _sg_def(img_desc->sampler_type, SG_SAMPLERTYPE_FLOAT); + } + } + return def; +} + +_SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_desc* desc) { + sg_pipeline_desc def = *desc; + + def.primitive_type = _sg_def(def.primitive_type, SG_PRIMITIVETYPE_TRIANGLES); + def.index_type = _sg_def(def.index_type, SG_INDEXTYPE_NONE); + def.cull_mode = _sg_def(def.cull_mode, SG_CULLMODE_NONE); + def.face_winding = _sg_def(def.face_winding, SG_FACEWINDING_CW); + def.sample_count = _sg_def(def.sample_count, _sg.desc.context.sample_count); + + def.stencil.front.compare = _sg_def(def.stencil.front.compare, SG_COMPAREFUNC_ALWAYS); + def.stencil.front.fail_op = _sg_def(def.stencil.front.fail_op, SG_STENCILOP_KEEP); + def.stencil.front.depth_fail_op = _sg_def(def.stencil.front.depth_fail_op, SG_STENCILOP_KEEP); + def.stencil.front.pass_op = _sg_def(def.stencil.front.pass_op, SG_STENCILOP_KEEP); + def.stencil.back.compare = _sg_def(def.stencil.back.compare, SG_COMPAREFUNC_ALWAYS); + def.stencil.back.fail_op = _sg_def(def.stencil.back.fail_op, SG_STENCILOP_KEEP); + def.stencil.back.depth_fail_op = _sg_def(def.stencil.back.depth_fail_op, SG_STENCILOP_KEEP); + def.stencil.back.pass_op = _sg_def(def.stencil.back.pass_op, SG_STENCILOP_KEEP); + + def.depth.compare = _sg_def(def.depth.compare, SG_COMPAREFUNC_ALWAYS); + def.depth.pixel_format = _sg_def(def.depth.pixel_format, _sg.desc.context.depth_format); + def.color_count = _sg_def(def.color_count, 1); + if (def.color_count > SG_MAX_COLOR_ATTACHMENTS) { + def.color_count = SG_MAX_COLOR_ATTACHMENTS; + } + for (int i = 0; i < def.color_count; i++) { + sg_color_state* cs = &def.colors[i]; + cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.context.color_format); + cs->write_mask = _sg_def(cs->write_mask, SG_COLORMASK_RGBA); + sg_blend_state* bs = &def.colors[i].blend; + bs->src_factor_rgb = _sg_def(bs->src_factor_rgb, SG_BLENDFACTOR_ONE); + bs->dst_factor_rgb = _sg_def(bs->dst_factor_rgb, SG_BLENDFACTOR_ZERO); + bs->op_rgb = _sg_def(bs->op_rgb, SG_BLENDOP_ADD); + bs->src_factor_alpha = _sg_def(bs->src_factor_alpha, SG_BLENDFACTOR_ONE); + bs->dst_factor_alpha = _sg_def(bs->dst_factor_alpha, SG_BLENDFACTOR_ZERO); + bs->op_alpha = _sg_def(bs->op_alpha, SG_BLENDOP_ADD); + } + + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + sg_buffer_layout_desc* b_desc = &def.layout.buffers[a_desc->buffer_index]; + b_desc->step_func = _sg_def(b_desc->step_func, SG_VERTEXSTEP_PER_VERTEX); + b_desc->step_rate = _sg_def(b_desc->step_rate, 1); + } + + /* resolve vertex layout strides and offsets */ + int auto_offset[SG_MAX_SHADERSTAGE_BUFFERS]; + memset(auto_offset, 0, sizeof(auto_offset)); + bool use_auto_offset = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + /* to use computed offsets, *all* attr offsets must be 0 */ + if (def.layout.attrs[attr_index].offset != 0) { + use_auto_offset = false; + } + } + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + if (use_auto_offset) { + a_desc->offset = auto_offset[a_desc->buffer_index]; + } + auto_offset[a_desc->buffer_index] += _sg_vertexformat_bytesize(a_desc->format); + } + /* compute vertex strides if needed */ + for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { + sg_buffer_layout_desc* l_desc = &def.layout.buffers[buf_index]; + if (l_desc->stride == 0) { + l_desc->stride = auto_offset[buf_index]; + } + } + + return def; +} + +_SOKOL_PRIVATE sg_pass_desc _sg_pass_desc_defaults(const sg_pass_desc* desc) { + /* FIXME: no values to replace in sg_pass_desc? */ + sg_pass_desc def = *desc; + return def; +} + +/*== allocate/initialize resource private functions ==========================*/ +_SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { + sg_buffer res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_image _sg_alloc_image(void) { + sg_image res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { + sg_shader res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { + sg_pipeline res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_pass _sg_alloc_pass(void) { + sg_pass res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.pass_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.pass_pool, &_sg.pools.passes[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE void _sg_dealloc_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(buf_id.id != SG_INVALID_ID); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_reset_slot(&buf->slot); + _sg_pool_free_index(&_sg.pools.buffer_pool, _sg_slot_index(buf_id.id)); +} + +_SOKOL_PRIVATE void _sg_dealloc_image(sg_image img_id) { + SOKOL_ASSERT(img_id.id != SG_INVALID_ID); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_reset_slot(&img->slot); + _sg_pool_free_index(&_sg.pools.image_pool, _sg_slot_index(img_id.id)); +} + +_SOKOL_PRIVATE void _sg_dealloc_shader(sg_shader shd_id) { + SOKOL_ASSERT(shd_id.id != SG_INVALID_ID); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_reset_slot(&shd->slot); + _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd_id.id)); +} + +_SOKOL_PRIVATE void _sg_dealloc_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(pip_id.id != SG_INVALID_ID); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_reset_slot(&pip->slot); + _sg_pool_free_index(&_sg.pools.pipeline_pool, _sg_slot_index(pip_id.id)); +} + +_SOKOL_PRIVATE void _sg_dealloc_pass(sg_pass pass_id) { + SOKOL_ASSERT(pass_id.id != SG_INVALID_ID); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_reset_slot(&pass->slot); + _sg_pool_free_index(&_sg.pools.pass_pool, _sg_slot_index(pass_id.id)); +} + +_SOKOL_PRIVATE void _sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf_id.id != SG_INVALID_ID && desc); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); + buf->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_buffer_desc(desc)) { + buf->slot.state = _sg_create_buffer(buf, desc); + } + else { + buf->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_image(sg_image img_id, const sg_image_desc* desc) { + SOKOL_ASSERT(img_id.id != SG_INVALID_ID && desc); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); + img->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_image_desc(desc)) { + img->slot.state = _sg_create_image(img, desc); + } + else { + img->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd_id.id != SG_INVALID_ID && desc); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); + shd->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_shader_desc(desc)) { + shd->slot.state = _sg_create_shader(shd, desc); + } + else { + shd->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip_id.id != SG_INVALID_ID && desc); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); + pip->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_pipeline_desc(desc)) { + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + if (shd && (shd->slot.state == SG_RESOURCESTATE_VALID)) { + pip->slot.state = _sg_create_pipeline(pip, shd, desc); + } + else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + } + else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass_id.id != SG_INVALID_ID && desc); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); + pass->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_pass_desc(desc)) { + /* lookup pass attachment image pointers */ + _sg_image_t* att_imgs[SG_MAX_COLOR_ATTACHMENTS + 1]; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (desc->color_attachments[i].image.id) { + att_imgs[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); + /* FIXME: this shouldn't be an assertion, but result in a SG_RESOURCESTATE_FAILED pass */ + SOKOL_ASSERT(att_imgs[i] && att_imgs[i]->slot.state == SG_RESOURCESTATE_VALID); + } + else { + att_imgs[i] = 0; + } + } + const int ds_att_index = SG_MAX_COLOR_ATTACHMENTS; + if (desc->depth_stencil_attachment.image.id) { + att_imgs[ds_att_index] = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); + /* FIXME: this shouldn't be an assertion, but result in a SG_RESOURCESTATE_FAILED pass */ + SOKOL_ASSERT(att_imgs[ds_att_index] && att_imgs[ds_att_index]->slot.state == SG_RESOURCESTATE_VALID); + } + else { + att_imgs[ds_att_index] = 0; + } + pass->slot.state = _sg_create_pass(pass, att_imgs, desc); + } + else { + pass->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE bool _sg_uninit_buffer(sg_buffer buf_id) { + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_buffer(buf); + _sg_reset_buffer(buf); + return true; + } + else { + SOKOL_LOG("_sg_uninit_buffer: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + return false; +} + +_SOKOL_PRIVATE bool _sg_uninit_image(sg_image img_id) { + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_image(img); + _sg_reset_image(img); + return true; + } + else { + SOKOL_LOG("_sg_uninit_image: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + return false; +} + +_SOKOL_PRIVATE bool _sg_uninit_shader(sg_shader shd_id) { + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_shader(shd); + _sg_reset_shader(shd); + return true; + } + else { + SOKOL_LOG("_sg_uninit_shader: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + return false; +} + +_SOKOL_PRIVATE bool _sg_uninit_pipeline(sg_pipeline pip_id) { + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_pipeline(pip); + _sg_reset_pipeline(pip); + return true; + } + else { + SOKOL_LOG("_sg_uninit_pipeline: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + return false; +} + +_SOKOL_PRIVATE bool _sg_uninit_pass(sg_pass pass_id) { + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + if (pass->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_pass(pass); + _sg_reset_pass(pass); + return true; + } + else { + SOKOL_LOG("_sg_uninit_pass: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + return false; +} + +/*== PUBLIC API FUNCTIONS ====================================================*/ + +#if defined(SOKOL_METAL) +// this is ARC compatible +#if defined(__cplusplus) +#define _SG_CLEAR(type, item) { item = { }; } +#else +#define _SG_CLEAR(type, item) { item = (type) { 0 }; } +#endif +#else +#define _SG_CLEAR(type, item) { memset(&item, 0, sizeof(item)); } +#endif + +SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); + _SG_CLEAR(_sg_state_t, _sg); + _sg.desc = *desc; + + /* replace zero-init items with their default values + NOTE: on WebGPU, the default color pixel format MUST be provided, + cannot be a default compile-time constant. + */ +#if defined(SOKOL_WGPU) + SOKOL_ASSERT(SG_PIXELFORMAT_NONE != _sg.desc.context.color_format); +#elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + _sg.desc.context.color_format = _sg_def(_sg.desc.context.color_format, SG_PIXELFORMAT_BGRA8); +#else + _sg.desc.context.color_format = _sg_def(_sg.desc.context.color_format, SG_PIXELFORMAT_RGBA8); +#endif + _sg.desc.context.depth_format = _sg_def(_sg.desc.context.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); + _sg.desc.context.sample_count = _sg_def(_sg.desc.context.sample_count, 1); + _sg.desc.buffer_pool_size = _sg_def(_sg.desc.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); + _sg.desc.image_pool_size = _sg_def(_sg.desc.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); + _sg.desc.shader_pool_size = _sg_def(_sg.desc.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); + _sg.desc.pipeline_pool_size = _sg_def(_sg.desc.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); + _sg.desc.pass_pool_size = _sg_def(_sg.desc.pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE); + _sg.desc.context_pool_size = _sg_def(_sg.desc.context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE); + _sg.desc.uniform_buffer_size = _sg_def(_sg.desc.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); + _sg.desc.staging_buffer_size = _sg_def(_sg.desc.staging_buffer_size, _SG_DEFAULT_STAGING_SIZE); + _sg.desc.sampler_cache_size = _sg_def(_sg.desc.sampler_cache_size, _SG_DEFAULT_SAMPLER_CACHE_CAPACITY); + + _sg_setup_pools(&_sg.pools, &_sg.desc); + _sg.frame_index = 1; + _sg_setup_backend(&_sg.desc); + _sg.valid = true; + sg_setup_context(); +} + +SOKOL_API_IMPL void sg_shutdown(void) { + /* can only delete resources for the currently set context here, if multiple + contexts are used, the app code must take care of properly releasing them + (since only the app code can switch between 3D-API contexts) + */ + if (_sg.active_context.id != SG_INVALID_ID) { + _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, _sg.active_context.id); + if (ctx) { + _sg_destroy_all_resources(&_sg.pools, _sg.active_context.id); + _sg_destroy_context(ctx); + } + } + _sg_discard_backend(); + _sg_discard_pools(&_sg.pools); + _sg.valid = false; +} + +SOKOL_API_IMPL bool sg_isvalid(void) { + return _sg.valid; +} + +SOKOL_API_IMPL sg_desc sg_query_desc(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.desc; +} + +SOKOL_API_IMPL sg_backend sg_query_backend(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.backend; +} + +SOKOL_API_IMPL sg_features sg_query_features(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.features; +} + +SOKOL_API_IMPL sg_limits sg_query_limits(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.limits; +} + +SOKOL_API_IMPL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) { + SOKOL_ASSERT(_sg.valid); + int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index]; +} + +SOKOL_API_IMPL sg_context sg_setup_context(void) { + SOKOL_ASSERT(_sg.valid); + sg_context res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.context_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.context_pool, &_sg.pools.contexts[slot_index].slot, slot_index); + _sg_context_t* ctx = _sg_context_at(&_sg.pools, res.id); + ctx->slot.state = _sg_create_context(ctx); + SOKOL_ASSERT(ctx->slot.state == SG_RESOURCESTATE_VALID); + _sg_activate_context(ctx); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + _sg.active_context = res; + return res; +} + +SOKOL_API_IMPL void sg_discard_context(sg_context ctx_id) { + SOKOL_ASSERT(_sg.valid); + _sg_destroy_all_resources(&_sg.pools, ctx_id.id); + _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); + if (ctx) { + _sg_destroy_context(ctx); + _sg_reset_context(ctx); + _sg_reset_slot(&ctx->slot); + _sg_pool_free_index(&_sg.pools.context_pool, _sg_slot_index(ctx_id.id)); + } + _sg.active_context.id = SG_INVALID_ID; + _sg_activate_context(0); +} + +SOKOL_API_IMPL void sg_activate_context(sg_context ctx_id) { + SOKOL_ASSERT(_sg.valid); + _sg.active_context = ctx_id; + _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); + /* NOTE: ctx can be 0 here if the context is no longer valid */ + _sg_activate_context(ctx); +} + +SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(trace_hooks); + _SOKOL_UNUSED(trace_hooks); +#if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks old_hooks = _sg.hooks; + _sg.hooks = *trace_hooks; +#else + static sg_trace_hooks old_hooks; + SOKOL_LOG("sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined!"); +#endif + return old_hooks; +} + +SOKOL_API_IMPL sg_buffer sg_alloc_buffer(void) { + SOKOL_ASSERT(_sg.valid); + sg_buffer res = _sg_alloc_buffer(); + _SG_TRACE_ARGS(alloc_buffer, res); + return res; +} + +SOKOL_API_IMPL sg_image sg_alloc_image(void) { + SOKOL_ASSERT(_sg.valid); + sg_image res = _sg_alloc_image(); + _SG_TRACE_ARGS(alloc_image, res); + return res; +} + +SOKOL_API_IMPL sg_shader sg_alloc_shader(void) { + SOKOL_ASSERT(_sg.valid); + sg_shader res = _sg_alloc_shader(); + _SG_TRACE_ARGS(alloc_shader, res); + return res; +} + +SOKOL_API_IMPL sg_pipeline sg_alloc_pipeline(void) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline res = _sg_alloc_pipeline(); + _SG_TRACE_ARGS(alloc_pipeline, res); + return res; +} + +SOKOL_API_IMPL sg_pass sg_alloc_pass(void) { + SOKOL_ASSERT(_sg.valid); + sg_pass res = _sg_alloc_pass(); + _SG_TRACE_ARGS(alloc_pass, res); + return res; +} + +SOKOL_API_IMPL void sg_dealloc_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_dealloc_buffer(buf_id); + _SG_TRACE_ARGS(dealloc_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_dealloc_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_dealloc_image(img_id); + _SG_TRACE_ARGS(dealloc_image, img_id); +} + +SOKOL_API_IMPL void sg_dealloc_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_dealloc_shader(shd_id); + _SG_TRACE_ARGS(dealloc_shader, shd_id); +} + +SOKOL_API_IMPL void sg_dealloc_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_dealloc_pipeline(pip_id); + _SG_TRACE_ARGS(dealloc_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_dealloc_pass(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + _sg_dealloc_pass(pass_id); + _SG_TRACE_ARGS(dealloc_pass, pass_id); +} + +SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); + _sg_init_buffer(buf_id, &desc_def); + _SG_TRACE_ARGS(init_buffer, buf_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_image_desc desc_def = _sg_image_desc_defaults(desc); + _sg_init_image(img_id, &desc_def); + _SG_TRACE_ARGS(init_image, img_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); + _sg_init_shader(shd_id, &desc_def); + _SG_TRACE_ARGS(init_shader, shd_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); + _sg_init_pipeline(pip_id, &desc_def); + _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); + _sg_init_pass(pass_id, &desc_def); + _SG_TRACE_ARGS(init_pass, pass_id, &desc_def); +} + +SOKOL_API_IMPL bool sg_uninit_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + bool res = _sg_uninit_buffer(buf_id); + _SG_TRACE_ARGS(uninit_buffer, buf_id); + return res; +} + +SOKOL_API_IMPL bool sg_uninit_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + bool res = _sg_uninit_image(img_id); + _SG_TRACE_ARGS(uninit_image, img_id); + return res; +} + +SOKOL_API_IMPL bool sg_uninit_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + bool res = _sg_uninit_shader(shd_id); + _SG_TRACE_ARGS(uninit_shader, shd_id); + return res; +} + +SOKOL_API_IMPL bool sg_uninit_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + bool res = _sg_uninit_pipeline(pip_id); + _SG_TRACE_ARGS(uninit_pipeline, pip_id); + return res; +} + +SOKOL_API_IMPL bool sg_uninit_pass(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + bool res = _sg_uninit_pass(pass_id); + _SG_TRACE_ARGS(uninit_pass, pass_id); + return res; +} + +/*-- set allocated resource to failed state ----------------------------------*/ +SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(buf_id.id != SG_INVALID_ID); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); + buf->slot.ctx_id = _sg.active_context.id; + buf->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(img_id.id != SG_INVALID_ID); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); + img->slot.ctx_id = _sg.active_context.id; + img->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_image, img_id); +} + +SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(shd_id.id != SG_INVALID_ID); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); + shd->slot.ctx_id = _sg.active_context.id; + shd->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_shader, shd_id); +} + +SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(pip_id.id != SG_INVALID_ID); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); + pip->slot.ctx_id = _sg.active_context.id; + pip->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_fail_pass(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(pass_id.id != SG_INVALID_ID); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); + pass->slot.ctx_id = _sg.active_context.id; + pass->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_pass, pass_id); +} + +/*-- get resource state */ +SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + sg_resource_state res = buf ? buf->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + sg_resource_state res = img ? img->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + sg_resource_state res = shd ? shd->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_pipeline_state(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + sg_resource_state res = pip ? pip->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_pass_state(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + sg_resource_state res = pass ? pass->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +/*-- allocate and initialize resource ----------------------------------------*/ +SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); + sg_buffer buf_id = _sg_alloc_buffer(); + if (buf_id.id != SG_INVALID_ID) { + _sg_init_buffer(buf_id, &desc_def); + } + else { + SOKOL_LOG("buffer pool exhausted!"); + _SG_TRACE_NOARGS(err_buffer_pool_exhausted); + } + _SG_TRACE_ARGS(make_buffer, &desc_def, buf_id); + return buf_id; +} + +SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_image_desc desc_def = _sg_image_desc_defaults(desc); + sg_image img_id = _sg_alloc_image(); + if (img_id.id != SG_INVALID_ID) { + _sg_init_image(img_id, &desc_def); + } + else { + SOKOL_LOG("image pool exhausted!"); + _SG_TRACE_NOARGS(err_image_pool_exhausted); + } + _SG_TRACE_ARGS(make_image, &desc_def, img_id); + return img_id; +} + +SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); + sg_shader shd_id = _sg_alloc_shader(); + if (shd_id.id != SG_INVALID_ID) { + _sg_init_shader(shd_id, &desc_def); + } + else { + SOKOL_LOG("shader pool exhausted!"); + _SG_TRACE_NOARGS(err_shader_pool_exhausted); + } + _SG_TRACE_ARGS(make_shader, &desc_def, shd_id); + return shd_id; +} + +SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); + sg_pipeline pip_id = _sg_alloc_pipeline(); + if (pip_id.id != SG_INVALID_ID) { + _sg_init_pipeline(pip_id, &desc_def); + } + else { + SOKOL_LOG("pipeline pool exhausted!"); + _SG_TRACE_NOARGS(err_pipeline_pool_exhausted); + } + _SG_TRACE_ARGS(make_pipeline, &desc_def, pip_id); + return pip_id; +} + +SOKOL_API_IMPL sg_pass sg_make_pass(const sg_pass_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); + sg_pass pass_id = _sg_alloc_pass(); + if (pass_id.id != SG_INVALID_ID) { + _sg_init_pass(pass_id, &desc_def); + } + else { + SOKOL_LOG("pass pool exhausted!"); + _SG_TRACE_NOARGS(err_pass_pool_exhausted); + } + _SG_TRACE_ARGS(make_pass, &desc_def, pass_id); + return pass_id; +} + +/*-- destroy resource --------------------------------------------------------*/ +SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_buffer, buf_id); + if (_sg_uninit_buffer(buf_id)) { + _sg_dealloc_buffer(buf_id); + } +} + +SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_image, img_id); + if (_sg_uninit_image(img_id)) { + _sg_dealloc_image(img_id); + } +} + +SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_shader, shd_id); + if (_sg_uninit_shader(shd_id)) { + _sg_dealloc_shader(shd_id); + } +} + +SOKOL_API_IMPL void sg_destroy_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_pipeline, pip_id); + if (_sg_uninit_pipeline(pip_id)) { + _sg_dealloc_pipeline(pip_id); + } +} + +SOKOL_API_IMPL void sg_destroy_pass(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_pass, pass_id); + if (_sg_uninit_pass(pass_id)) { + _sg_dealloc_pass(pass_id); + } +} + +SOKOL_API_IMPL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(pass_action); + SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); + sg_pass_action pa; + _sg_resolve_default_pass_action(pass_action, &pa); + _sg.cur_pass.id = SG_INVALID_ID; + _sg.pass_valid = true; + _sg_begin_pass(0, &pa, width, height); + _SG_TRACE_ARGS(begin_default_pass, pass_action, width, height); +} + +SOKOL_API_IMPL void sg_begin_default_passf(const sg_pass_action* pass_action, float width, float height) { + sg_begin_default_pass(pass_action, (int)width, (int)height); +} + +SOKOL_API_IMPL void sg_begin_pass(sg_pass pass_id, const sg_pass_action* pass_action) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(pass_action); + SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); + _sg.cur_pass = pass_id; + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass && _sg_validate_begin_pass(pass)) { + _sg.pass_valid = true; + sg_pass_action pa; + _sg_resolve_default_pass_action(pass_action, &pa); + const _sg_image_t* img = _sg_pass_color_image(pass, 0); + SOKOL_ASSERT(img); + const int w = img->cmn.width; + const int h = img->cmn.height; + _sg_begin_pass(pass, &pa, w, h); + _SG_TRACE_ARGS(begin_pass, pass_id, pass_action); + } + else { + _sg.pass_valid = false; + _SG_TRACE_NOARGS(err_pass_invalid); + } +} + +SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { + SOKOL_ASSERT(_sg.valid); + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg_apply_viewport(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_viewport, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) { + sg_apply_viewport((int)x, (int)y, (int)width, (int)height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { + SOKOL_ASSERT(_sg.valid); + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg_apply_scissor_rect(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_scissor_rect, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) { + sg_apply_scissor_rect((int)x, (int)y, (int)width, (int)height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg.bindings_valid = false; + if (!_sg_validate_apply_pipeline(pip_id)) { + _sg.next_draw_valid = false; + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg.cur_pipeline = pip_id; + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip); + _sg.next_draw_valid = (SG_RESOURCESTATE_VALID == pip->slot.state); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); + _sg_apply_pipeline(pip); + _SG_TRACE_ARGS(apply_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(bindings); + SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); + if (!_sg_validate_apply_bindings(bindings)) { + _sg.next_draw_valid = false; + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + _sg.bindings_valid = true; + + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_ASSERT(pip); + + _sg_buffer_t* vbs[SG_MAX_SHADERSTAGE_BUFFERS] = { 0 }; + int num_vbs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++, num_vbs++) { + if (bindings->vertex_buffers[i].id) { + vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + SOKOL_ASSERT(vbs[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vbs[i]->slot.state); + _sg.next_draw_valid &= !vbs[i]->cmn.append_overflow; + } + else { + break; + } + } + + _sg_buffer_t* ib = 0; + if (bindings->index_buffer.id) { + ib = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + SOKOL_ASSERT(ib); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == ib->slot.state); + _sg.next_draw_valid &= !ib->cmn.append_overflow; + } + + _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; + int num_vs_imgs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_vs_imgs++) { + if (bindings->vs_images[i].id) { + vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); + SOKOL_ASSERT(vs_imgs[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_imgs[i]->slot.state); + } + else { + break; + } + } + + _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; + int num_fs_imgs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_fs_imgs++) { + if (bindings->fs_images[i].id) { + fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); + SOKOL_ASSERT(fs_imgs[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_imgs[i]->slot.state); + } + else { + break; + } + } + if (_sg.next_draw_valid) { + const int* vb_offsets = bindings->vertex_buffer_offsets; + int ib_offset = bindings->index_buffer_offset; + _sg_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + _SG_TRACE_ARGS(apply_bindings, bindings); + } + else { + _SG_TRACE_NOARGS(err_draw_invalid); + } +} + +SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT((stage == SG_SHADERSTAGE_VS) || (stage == SG_SHADERSTAGE_FS)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + if (!_sg_validate_apply_uniforms(stage, ub_index, data)) { + _sg.next_draw_valid = false; + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + if (!_sg.next_draw_valid) { + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + _sg_apply_uniforms(stage, ub_index, data); + _SG_TRACE_ARGS(apply_uniforms, stage, ub_index, data); +} + +SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(base_element >= 0); + SOKOL_ASSERT(num_elements >= 0); + SOKOL_ASSERT(num_instances >= 0); +#if defined(SOKOL_DEBUG) + if (!_sg.bindings_valid) { + SOKOL_LOG("attempting to draw without resource bindings"); + } +#endif + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + if (!_sg.next_draw_valid) { + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + if (!_sg.bindings_valid) { + _SG_TRACE_NOARGS(err_bindings_invalid); + return; + } + /* attempting to draw with zero elements or instances is not technically an + error, but might be handled as an error in the backend API (e.g. on Metal) + */ + if ((0 == num_elements) || (0 == num_instances)) { + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + _sg_draw(base_element, num_elements, num_instances); + _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances); +} + +SOKOL_API_IMPL void sg_end_pass(void) { + SOKOL_ASSERT(_sg.valid); + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg_end_pass(); + _sg.cur_pass.id = SG_INVALID_ID; + _sg.cur_pipeline.id = SG_INVALID_ID; + _sg.pass_valid = false; + _SG_TRACE_NOARGS(end_pass); +} + +SOKOL_API_IMPL void sg_commit(void) { + SOKOL_ASSERT(_sg.valid); + _sg_commit(); + _SG_TRACE_NOARGS(commit); + _sg.frame_index++; +} + +SOKOL_API_IMPL void sg_reset_state_cache(void) { + SOKOL_ASSERT(_sg.valid); + _sg_reset_state_cache(); + _SG_TRACE_NOARGS(reset_state_cache); +} + +SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if ((data->size > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { + if (_sg_validate_update_buffer(buf, data)) { + SOKOL_ASSERT(data->size <= (size_t)buf->cmn.size); + /* only one update allowed per buffer and frame */ + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + /* update and append on same buffer in same frame not allowed */ + SOKOL_ASSERT(buf->cmn.append_frame_index != _sg.frame_index); + _sg_update_buffer(buf, data); + buf->cmn.update_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_buffer, buf_id, data); +} + +SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(data && data->ptr); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + int result; + if (buf) { + /* rewind append cursor in a new frame */ + if (buf->cmn.append_frame_index != _sg.frame_index) { + buf->cmn.append_pos = 0; + buf->cmn.append_overflow = false; + } + if ((buf->cmn.append_pos + _sg_roundup((int)data->size, 4)) > buf->cmn.size) { + buf->cmn.append_overflow = true; + } + const int start_pos = buf->cmn.append_pos; + if (buf->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_append_buffer(buf, data)) { + if (!buf->cmn.append_overflow && (data->size > 0)) { + /* update and append on same buffer in same frame not allowed */ + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + int copied_num_bytes = _sg_append_buffer(buf, data, buf->cmn.append_frame_index != _sg.frame_index); + buf->cmn.append_pos += copied_num_bytes; + buf->cmn.append_frame_index = _sg.frame_index; + } + } + } + result = start_pos; + } + else { + /* FIXME: should we return -1 here? */ + result = 0; + } + _SG_TRACE_ARGS(append_buffer, buf_id, data, result); + return result; +} + +SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + bool result = buf ? buf->cmn.append_overflow : false; + return result; +} + +SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_data* data) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_update_image(img, data)) { + SOKOL_ASSERT(img->cmn.upd_frame_index != _sg.frame_index); + _sg_update_image(img, data); + img->cmn.upd_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_image, img_id, data); +} + +SOKOL_API_IMPL void sg_push_debug_group(const char* name) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(name); + _SOKOL_UNUSED(name); + _SG_TRACE_ARGS(push_debug_group, name); +} + +SOKOL_API_IMPL void sg_pop_debug_group(void) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_NOARGS(pop_debug_group); +} + +SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_info info; + memset(&info, 0, sizeof(info)); + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + info.slot.state = buf->slot.state; + info.slot.res_id = buf->slot.id; + info.slot.ctx_id = buf->slot.ctx_id; + info.update_frame_index = buf->cmn.update_frame_index; + info.append_frame_index = buf->cmn.append_frame_index; + info.append_pos = buf->cmn.append_pos; + info.append_overflow = buf->cmn.append_overflow; +#if defined(SOKOL_D3D11) + info.num_slots = 1; + info.active_slot = 0; +#else + info.num_slots = buf->cmn.num_slots; + info.active_slot = buf->cmn.active_slot; +#endif + } + return info; +} + +SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_image_info info; + memset(&info, 0, sizeof(info)); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + info.slot.state = img->slot.state; + info.slot.res_id = img->slot.id; + info.slot.ctx_id = img->slot.ctx_id; + info.upd_frame_index = img->cmn.upd_frame_index; +#if defined(SOKOL_D3D11) + info.num_slots = 1; + info.active_slot = 0; +#else + info.num_slots = img->cmn.num_slots; + info.active_slot = img->cmn.active_slot; +#endif + info.width = img->cmn.width; + info.height = img->cmn.height; + } + return info; +} + +SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_shader_info info; + memset(&info, 0, sizeof(info)); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + info.slot.state = shd->slot.state; + info.slot.res_id = shd->slot.id; + info.slot.ctx_id = shd->slot.ctx_id; + } + return info; +} + +SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_info info; + memset(&info, 0, sizeof(info)); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + info.slot.state = pip->slot.state; + info.slot.res_id = pip->slot.id; + info.slot.ctx_id = pip->slot.ctx_id; + } + return info; +} + +SOKOL_API_IMPL sg_pass_info sg_query_pass_info(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + sg_pass_info info; + memset(&info, 0, sizeof(info)); + const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + info.slot.state = pass->slot.state; + info.slot.res_id = pass->slot.id; + info.slot.ctx_id = pass->slot.ctx_id; + } + return info; +} + +SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_buffer_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_image_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_shader_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_pipeline_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_pass_desc_defaults(desc); +} + +SOKOL_API_IMPL const void* sg_d3d11_device(void) { +#if defined(SOKOL_D3D11) + return (const void*) _sg.d3d11.dev; +#else + return 0; +#endif +} + +SOKOL_API_IMPL const void* sg_mtl_device(void) { +#if defined(SOKOL_METAL) + if (nil != _sg.mtl.device) { + return (__bridge const void*) _sg.mtl.device; + } + else { + return 0; + } +#else + return 0; +#endif +} + +SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { +#if defined(SOKOL_METAL) + if (nil != _sg.mtl.cmd_encoder) { + return (__bridge const void*) _sg.mtl.cmd_encoder; + } + else { + return 0; + } +#else + return 0; +#endif +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* SOKOL_GFX_IMPL */ \ No newline at end of file diff --git a/src_v2/platform/wasm/lumenarium_first_wasm.cpp b/src_v2/platform/wasm/lumenarium_first_wasm.cpp new file mode 100644 index 0000000..e4285a0 --- /dev/null +++ b/src_v2/platform/wasm/lumenarium_first_wasm.cpp @@ -0,0 +1,90 @@ + +#define WASM_EXPORT __attribute__((visibility("default"))) +#define WASM_EXTERN extern "C" + +#define EXTERN_C_BEGIN extern "C" { +#define EXTERN_C_END } + +#include "../lumenarium_compiler_flags.h" +#include "../lumenarium_platform_common_includes.h" + +#include "../../lumenarium_types.h" +#include "../lumenarium_platform.h" +#include "../../lumenarium_first.cpp" + +#include "lumenarium_wasm_webgl.cpp" + +#include "lumenarium_wasm_memory.cpp" +// window +#include "lumenarium_wasm_time.cpp" +#include "lumenarium_wasm_file.cpp" +#include "lumenarium_wasm_thread.cpp" + +WASM_EXTERN void print(const char* text, int len); + +typedef void wasm_animation_frame_cb(u32 time_elapsed); +WASM_EXTERN void wasm_request_animation_frame(wasm_animation_frame_cb* cb); + +EXTERN_C_BEGIN; + +int +str_len (char* str) +{ + int result = 0; + while (str[result] != 0) result++; + return result; +} + +u8* dest = 0; + +App_State* wasm_app_state = 0; + +WASM_EXPORT void +update(u32 time_elapsed) +{ + lumenarium_frame_prepare(wasm_app_state); + lumenarium_frame(wasm_app_state); + + // TODO(PS): check for app running flags + wasm_request_animation_frame(update); +} + +WASM_EXPORT int +main(void) +{ + wasm_app_state = lumenarium_init(); + //wasm_request_animation_frame(update); + return 0; + +#if 0 + Platform_Ticks first = platform_get_ticks(); + + char* str0 = "Hi there!"; + int str0_len = str_len(str0); + print(str0, str0_len); + + char a = str0[0]; + char* str1 = (char*)platform_mem_reserve(32); + char b = str0[0]; + + for (int i = 0; i < str0_len; i++) + { + int it = str0_len - (i + 1); + str1[i] = str0[i]; + } + print(str1, str0_len); + + char* file = "text.txt"; + int file_len = str_len(file); + + dest = platform_mem_reserve(KB(4)); + wasm_fetch(file, file_len, dest, KB(4)); + + Platform_Ticks last = platform_get_ticks(); + r64 seconds_elapsed = get_seconds_elapsed(first, last); + + return last.value - first.value; +#endif +} + +EXTERN_C_END; \ No newline at end of file diff --git a/src_v2/platform/wasm/lumenarium_wasm_file.cpp b/src_v2/platform/wasm/lumenarium_wasm_file.cpp new file mode 100644 index 0000000..ef11ded --- /dev/null +++ b/src_v2/platform/wasm/lumenarium_wasm_file.cpp @@ -0,0 +1,45 @@ + +WASM_EXTERN u32 wasm_fetch(char* file_path, u32 file_path_len, u8* dest, u32 size); + +Platform_File_Handle +platform_file_open(String path, Platform_File_Access_Flags flags_access, Platform_File_Create_Flags flags_create) +{ + return {}; +} + +void +platform_file_close(Platform_File_Handle file_handle) +{ + return; +} + +Platform_File_Info +platform_file_get_info(Platform_File_Handle file_handle, Allocator* allocator) +{ + return {}; +} + +Data +platform_file_read_all(Platform_File_Handle file_handle, Allocator* allocator) +{ + return {}; +} + +bool +platform_file_write_all(Platform_File_Handle file_handle, Data file_data) +{ + return {}; +} + +String +platform_get_exe_path(Allocator* allocator) +{ + return {}; +} + +bool +platform_pwd_set(String path) +{ + return false; +} + diff --git a/src_v2/platform/wasm/lumenarium_wasm_imports.js b/src_v2/platform/wasm/lumenarium_wasm_imports.js new file mode 100644 index 0000000..299fa8a --- /dev/null +++ b/src_v2/platform/wasm/lumenarium_wasm_imports.js @@ -0,0 +1,274 @@ +var lumenarium_wasm_module = null; +var lumenarium_wasm_instance = null; + +var WASM_PAGE_SIZE = 65536; + +function wasm_mem_get_u8_arr(inst, ptr, size) +{ + let view = new Uint8Array(inst.exports.memory.buffer, ptr, size); + return view; +} + +function wasm_read_string(inst, ptr, len) +{ + let view = wasm_mem_get_u8_arr(inst, ptr, len); + let string = ''; + for (let i = 0; i < len; i++) + { + string += String.fromCharCode(view[i]); + } + return string; +} + +function wasm_write_bytes(inst, src, ptr, len) +{ + let view = wasm_mem_get_u8_arr(inst, ptr, len); + for (let i = 0; i < len; i++) view[i] = src[i]; +} + +function wasm_get_proc(inst, proc_ptr) +{ + let result = inst.exports.__indirect_function_table.get(proc_ptr); + return result; +} + +function fract (v) { return v % 1; } + +var lumenarium_wasm_imports = { + + memset: (dst, size, value) => { + let view_dst = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dst, size); + for (let i = 0; i < size; i++) + { + view_dst[i] = value; + } + }, + + memcpy: (dst, src, size) => { + let view_dst = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dst, size); + let view_src = wasm_mem_get_u8_arr(lumenarium_wasm_instance, src, size); + for (let i = 0; i < size; i++) + { + view_dst[i] = view_src[i]; + } + }, + + wasm_assert_always: (file, file_len, line) => { + let file_str = wasm_read_string(lumenarium_wasm_instance, file, file_len); + console.assert(false, "At: " + file_str + "::" + line); + }, + + wasm_get_memory_size: () => { + return instance.exports.memory.buffer.byteLength; + }, + + wasm_mem_grow: (new_size) => { + let pages = new_size / WASM_PAGE_SIZE; + let pages_rem = fract(pages); + if (pages_rem > 0) pages = Math.floor(pages) + 1; + let size_before = lumenarium_wasm_instance.exports.memory.buffer.byteLength; + let old_page_count = lumenarium_wasm_instance.exports.memory.grow(pages); + + console.log("mem_grow\n", + "req size: ", new_size, "\n", + "old size: ", (old_page_count * WASM_PAGE_SIZE), "\n", + "old size: ", size_before, "\n", + "grew by: ", (pages * WASM_PAGE_SIZE), "\n", + "new size: ", lumenarium_wasm_instance.exports.memory.buffer.byteLength, ""); + }, + + wasm_performance_now: () => { + return performance.now(); + }, + + wasm_sleep: (milliseconds) => { + let start = Date.now(); + for (let at = Date.now(); (at - start) < milliseconds; at = Date.now()) {} + }, + + wasm_fetch: async (file_path, file_path_len, dest, dest_size) => { + let path = wasm_read_string(lumenarium_wasm_instance, file_path, file_path_len); + fetch(path) + .then(async (res) => { + // TODO(PS): success checking + let reader = res.body.getReader(); + let read_res = { done: false }; + + let view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dest, dest_size); + let last_write = 0; + while (!read_res.done) + { + read_res = await reader.read(); + if (read_res.done) break; + + let len = read_res.value.length; + let write_end = last_write + len; + for (let i = last_write; i < write_end; i++) + { + view[i] = read_res.value[i - last_write]; + } + last_write = write_end + 1; + } + }); + return 0; + }, + + wasm_request_animation_frame: (cb) => { + let cb_proc = wasm_get_proc(lumenarium_wasm_instance, cb); + window.requestAnimationFrame(cb_proc); + }, + + print: (str_base, len) => { + let string = wasm_read_string(lumenarium_wasm_instance, str_base, len); + console.log(string); + }, +}; + +/////////////////////////////////////// +// Web GL Imports + +let gl = null; + +// NOTE(PS): it seems like its not enough to set +// the values of imports to gl.function +// ie. imports.glClearColor = gl.clearColor +// instead we need to wrap them for some reason. +// Not sure why +function glClearColor (r, g, b, a) { return gl.clearColor(r,g,b,a); } +function glEnable(v) { return gl.enable(v); } +function glDisable(v) { return gl.disable(v); } +function glBlendFunc(a,b) { return gl.blendFunc(a,b); } +function glViewport(xmin, ymin, xmax, ymax) { return gl.viewport(xmin,ymin,xmax,ymax); } +function glDepthFunc(v) { return gl.depthFunc(v); } +function glClear(mask) { return gl.clear(mask); } + +let glBuffers = []; +let glShaders = []; +let glPrograms = []; +function gl_get_managed_resource(arr, id) { + if (id == 0) return null; + return arr[id - 1]; +} +function gl_get_buffer(id) { return gl_get_managed_resource(glBuffers, id); } +function gl_get_shader(id) { return gl_get_managed_resource(glShaders, id); } +function gl_get_program(id) { return gl_get_managed_resource(glPrograms, id); } + +function glCreateBuffer() { + let buffer = gl.createBuffer(); + let new_len = glBuffers.push(buffer); + return new_len; +} +function glBindBuffer(buffer_kind, buffer_id) +{ + return gl.bindBuffer(buffer_kind, gl_get_buffer(buffer_id)); +} +function glBufferData(target, size, ptr, usage) +{ + let data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, size); + return gl.bufferData(target, data, usage); +} +function glCreateShader(kind) +{ + let shader = gl.createShader(kind); + let new_len = glShaders.push(shader); + return new_len; +} +function glShaderSource(shader_id, shader_code, shader_code_len) +{ + let str = wasm_read_string(lumenarium_wasm_instance, shader_code, shader_code_len); + console.error("For some reason, str isn't getting the correct data out of here", str); + return gl.shaderSource(gl_get_shader(shader_id), str); +} +function glCompileShader(shader_id) +{ + let s = gl_get_shader(shader_id); + let r = gl.compileShader(s); + let m = gl.getShaderInfoLog(s); + if (m.length > 0) + { + console.error("glCompileShader: \n\n" + m); + } +} +function glCreateProgram() +{ + let prog = gl.createProgram(); + let new_len = glPrograms.push(prog); + return new_len; +} +function glAttachShader(program, shader) +{ + let s = gl_get_shader(shader); + let p = gl_get_program(program); + return gl.attachShader(p, s); +} +function glLinkProgram(program) +{ + let p = gl_get_program(program); + gl.linkProgram(p); + if (!gl.getProgramParameter(p, gl.LINK_STATUS)) { + var info = gl.getProgramInfoLog(p); + console.error("Failed to compile WebGL program. \n\n"+info); + } +} +function glUseProgram(program) +{ + let p = gl_get_program(program); + return gl.useProgram(p); +} +function glGetAttribLocation(program, name, name_len) +{ + let str = wasm_read_string(lumenarium_wasm_instance, name, name_len); + return gl.getAttribLocation(gl_get_program(program), str); +} +function glVertexAttribPointer(attr, size, type, normalized, stride, pointer) +{ + return gl.vertexAttribPointer(attr, size, type, normalized, stride, pointer); +} +function glEnableVertexAttribArray(index) +{ + return gl.enableVertexAttribArray(index); +} +function glDrawElements(type, index_count, ele_type, indices) +{ + return gl.drawElements(type, index_count, ele_type, indices); +} + +function webgl_add_imports (canvas_selector, imports) { + const canvas = document.querySelector(canvas_selector); + if (!canvas) return console.error("no canvas"); + + gl = canvas.getContext("webgl"); + if (gl === null) return console.error("no webgl ctx"); + + console.log( + gl.FLOAT.toString(16), "\n", + gl.UNSIGNED_INT.toString(16), "\n" + ); + + + + imports.glClearColor = glClearColor; + imports.glEnable = glEnable; + imports.glDisable = glDisable; + imports.glBlendFunc = glBlendFunc; + imports.glViewport = glViewport; + imports.glDepthFunc = glDepthFunc; + imports.glClear = glClear; + + imports.glCreateBuffer = glCreateBuffer; + imports.glBindBuffer = glBindBuffer; + imports.glBufferData = glBufferData; + imports.glCreateShader = glCreateShader; + imports.glShaderSource = glShaderSource; + imports.glCompileShader = glCompileShader; + imports.glCreateProgram = glCreateProgram; + imports.glAttachShader = glAttachShader; + imports.glLinkProgram = glLinkProgram; + imports.glUseProgram = glUseProgram; + imports.glGetAttribLocation = glGetAttribLocation; + imports.glVertexAttribPointer = glVertexAttribPointer; + imports.glEnableVertexAttribArray = glEnableVertexAttribArray; + imports.glDrawElements = glDrawElements; + + return imports; +} \ No newline at end of file diff --git a/src_v2/platform/wasm/lumenarium_wasm_memory.cpp b/src_v2/platform/wasm/lumenarium_wasm_memory.cpp new file mode 100644 index 0000000..77713f2 --- /dev/null +++ b/src_v2/platform/wasm/lumenarium_wasm_memory.cpp @@ -0,0 +1,67 @@ + +#define WASM_PAGE_SIZE KB(64) + +u64 +platform_page_size() +{ + return WASM_PAGE_SIZE; +} + +// this comes from: https://surma.dev/things/c-to-webassembly/ +extern u8 __heap_base; +global u8* heap_base = &__heap_base; +global u64 heap_used = 0; + +WASM_EXTERN u32 wasm_get_memory_size(); +WASM_EXTERN u32 wasm_mem_grow(u32 new_size); + +u64 +wasm_get_heap_size() +{ + u64 memory_size = wasm_get_memory_size(); + u64 heap_base_addr = (u64)&__heap_base; + u64 heap_size = memory_size - heap_base_addr; + return heap_size; +} + +u8* +platform_mem_reserve(u64 size) +{ + // if we are out of memory, double the size of wasm memory + u64 heap_size = wasm_get_heap_size(); + if (heap_used + size >= heap_size) + { + u32 heap_size_new = heap_size * 2; + if (heap_used + size >= heap_size_new) + { + heap_size_new = heap_used + size; + } + wasm_mem_grow(heap_size_new); + } + assert(heap_used + size <= wasm_get_heap_size()); + + u64 ptr_addr = (u64)(heap_base + heap_used); + u8* result = heap_base + heap_used; + heap_used += size; // TODO(PS): alignment + 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) +{ + // TODO(PS): we probably actually want to implement free at some point + return true; +} + diff --git a/src_v2/platform/wasm/lumenarium_wasm_thread.cpp b/src_v2/platform/wasm/lumenarium_wasm_thread.cpp new file mode 100644 index 0000000..4fd1f84 --- /dev/null +++ b/src_v2/platform/wasm/lumenarium_wasm_thread.cpp @@ -0,0 +1,24 @@ + +Platform_Thread_Handle +platform_thread_begin(Platform_Thread_Proc* proc, u8* user_data) +{ + return {}; +} + +void +platform_thread_end(Platform_Thread_Handle thread_handle) +{ + return; +} + +u32 +platform_interlocked_increment(volatile u32* value) +{ + return 0; +} + +u32 +platform_interlocked_cmp_exchg(volatile u32* dest, u32 new_value, u32 old_value) +{ + return 0; +} diff --git a/src_v2/platform/wasm/lumenarium_wasm_time.cpp b/src_v2/platform/wasm/lumenarium_wasm_time.cpp new file mode 100644 index 0000000..182193a --- /dev/null +++ b/src_v2/platform/wasm/lumenarium_wasm_time.cpp @@ -0,0 +1,26 @@ + +// NOTE(PS): should call performance.now() which is in milliseconds +WASM_EXTERN u32 wasm_performance_now(); + +WASM_EXTERN void wasm_sleep(u32 milliseconds); + +Platform_Ticks +platform_get_ticks() +{ + Platform_Ticks result = {}; + result.value = (u64)wasm_performance_now(); + return result; +} + +r64 +platform_ticks_to_seconds(Platform_Ticks ticks) +{ + r64 result = (r64)(ticks.value * 1000); + return result; +} + +void +platform_sleep(r64 milliseconds) +{ + wasm_sleep(milliseconds); +} \ No newline at end of file diff --git a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp new file mode 100644 index 0000000..d164900 --- /dev/null +++ b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp @@ -0,0 +1,185 @@ +// + +// TODO(PS): +// TODO(PS): +// TODO(PS): +// TODO(PS): you guessed the data types and names of ALL of this +// fix it! + +typedef unsigned int GLuint; +typedef float GLfloat; +typedef unsigned int GLenum; +typedef bool GLboolean; +typedef unsigned int GLsizei; + +// NOTE(PS): these values and function signatures all come from +// the GLES2/gl2.h header file that can be found here: +// https://www.khronos.org/registry/OpenGL/api/GLES2/gl2.h +// +// I resorted to hard coding these rather than passing them in because +// passing them in didn't seem to be working. + +#define GL_TEXTURE_2D 0x0DE1 + +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_BLEND 0x0BE2 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DEPTH_TEST 0x0B71 +#define GL_LESS 0x0201 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_STATIC_DRAW 0x88e4 +#define GL_FRAGMENT_SHADER 0x8b30 +#define GL_VERTEX_SHADER 0x8b31 +#define GL_TRIANGLES 0x0004 +#define GL_UNSIGNED_INT 0x1406 +#define GL_FLOAT 0x1405 + +WASM_EXTERN void glClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a); +WASM_EXTERN void glEnable(GLuint i); +WASM_EXTERN void glDisable(GLuint i); +WASM_EXTERN void glBlendFunc(GLuint a, GLuint b); +WASM_EXTERN void glViewport(GLuint xmin, GLuint ymin, GLuint xmax, GLuint ymax); +WASM_EXTERN void glDepthFunc(GLuint i); +WASM_EXTERN void glClear(GLuint i); + +WASM_EXTERN GLuint glCreateBuffer(); +WASM_EXTERN void glBindBuffer(GLenum buffer_kind, GLuint buffer_id); +WASM_EXTERN void glBufferData(GLenum target, size_t size, const void* data, GLenum usage); +WASM_EXTERN GLuint glCreateShader(GLenum kind); +WASM_EXTERN GLuint glShaderSource(GLuint shader_id, char* shader_code, GLuint shader_code_len); +WASM_EXTERN void glCompileShader(GLuint shader_id); +WASM_EXTERN GLuint glCreateProgram(void); +WASM_EXTERN void glAttachShader(GLuint program, GLuint shader); +WASM_EXTERN void glLinkProgram(GLuint program); +WASM_EXTERN void glUseProgram(GLuint program); +WASM_EXTERN GLuint glGetAttribLocation(GLuint program, const char* name, GLuint name_len); +WASM_EXTERN void glVertexAttribPointer(GLuint attr, GLuint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +WASM_EXTERN void glEnableVertexAttribArray(GLuint index); +WASM_EXTERN void glDrawElements(GLenum type, GLuint count, GLenum ele_type, void* indices); + +Platform_Geometry_Buffer +platform_geometry_buffer_create( + r32* vertices, GLuint vertices_len, + GLuint* indices, GLuint indices_len + ){ + Platform_Geometry_Buffer result = {}; + + result.buffer_id_vertices = glCreateBuffer(); + result.buffer_id_indices = glCreateBuffer(); + + // Vertices + glBindBuffer(GL_ARRAY_BUFFER, result.buffer_id_vertices); + glBufferData( + GL_ARRAY_BUFFER, sizeof(r32) * vertices_len, vertices, GL_STATIC_DRAW + ); + + // Indices + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.buffer_id_indices); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices_len, indices, GL_STATIC_DRAW + ); + result.indices_len = indices_len; + + glBindBuffer(GL_ARRAY_BUFFER, (GLuint)NULL); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)NULL); + + return result; +} + +Platform_Shader +platform_shader_create( + String code_vert, String code_frag, String* attrs, GLuint attrs_len + ){ + Platform_Shader result = {}; + + GLuint shader_vert = glCreateShader(GL_VERTEX_SHADER); + GLuint vert_len = (GLuint)code_vert.len; + glShaderSource(shader_vert, (char*)&code_vert.str, vert_len); + glCompileShader(shader_vert); + + GLuint shader_frag = glCreateShader(GL_FRAGMENT_SHADER); + GLuint frag_len = (GLuint)code_frag.len; + glShaderSource(shader_frag, (char*)&code_frag.str, frag_len); + glCompileShader(shader_frag); + + result.id = (GLuint)glCreateProgram(); + glAttachShader(result.id, shader_vert); + glAttachShader(result.id, shader_frag); + glLinkProgram(result.id); + glUseProgram(result.id); + + // TODO(PS): delete the vert and frag programs + + assert(attrs_len < PLATFORM_SHADER_MAX_ATTRS); + for (GLuint i = 0; i < attrs_len; i++) + { + s32 len = (s32)attrs[i].len; + result.attrs[i] = glGetAttribLocation( + result.id, (char*)attrs[i].str, len + ); + } + result.attrs[attrs_len] = PLATFORM_SHADER_ATTR_LAST; + + return result; +} + +void +platform_geometry_bind(Platform_Geometry_Buffer geo) +{ + glBindBuffer(GL_ARRAY_BUFFER, geo.buffer_id_vertices); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geo.buffer_id_indices); +} + +void +platform_shader_bind(Platform_Shader shader) +{ + glUseProgram(shader.id); + for (GLuint i = 0; i < PLATFORM_SHADER_MAX_ATTRS && shader.attrs[i] != PLATFORM_SHADER_MAX_ATTRS; i++) + { + glEnableVertexAttribArray(shader.attrs[i]); + } +} + +void +platform_geometry_draw( + Platform_Geometry_Buffer geo + ){ + glDrawElements(GL_TRIANGLES, geo.indices_len, GL_UNSIGNED_INT, 0); +} + +void platform_vertex_attrib_pointer( + Platform_Geometry_Buffer geo, Platform_Shader shader, GLuint attr_index + ){ + platform_shader_bind(shader); + platform_geometry_bind(geo); + glVertexAttribPointer(shader.attrs[attr_index], 4, GL_FLOAT, false, 0, 0); +} + +void +platform_frame_begin(Platform_Graphics_Frame_Desc desc) +{ + v4 cc = desc.clear_color; + glClearColor(cc.r, cc.g, cc.b, cc.a); + + v2 vmin = desc.viewport_min; + v2 vmax = desc.viewport_max; + glViewport(vmin.x, vmin.y, vmax.x, vmax.y); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //glDisable(GL_TEXTURE_2D); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); +} + +void +platform_frame_clear() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} \ No newline at end of file diff --git a/src_v2/platform/webgl/lumenarium_first_webgl.cpp b/src_v2/platform/webgl/lumenarium_first_webgl.cpp deleted file mode 100644 index a6814e1..0000000 --- a/src_v2/platform/webgl/lumenarium_first_webgl.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#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/wglext.h b/src_v2/platform/wglext.h new file mode 100644 index 0000000..c8455bb --- /dev/null +++ b/src_v2/platform/wglext.h @@ -0,0 +1,845 @@ +#ifndef __wgl_wglext_h_ +#define __wgl_wglext_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + + /* + ** Copyright 2013-2020 The Khronos Group Inc. + ** SPDX-License-Identifier: MIT + ** + ** This header is generated from the Khronos OpenGL / OpenGL ES XML + ** API Registry. The current version of the Registry, generator scripts + ** used to make the header, and the header can be found at + ** https://github.com/KhronosGroup/OpenGL-Registry + */ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#define WIN32_LEAN_AND_MEAN 1 +#include +#endif + +#define WGL_WGLEXT_VERSION 20211115 + + /* Generated C header for: + * API: wgl + * Versions considered: .* + * Versions emitted: _nomatch_^ + * Default extensions included: wgl + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef WGL_ARB_buffer_region +#define WGL_ARB_buffer_region 1 +#define WGL_FRONT_COLOR_BUFFER_BIT_ARB 0x00000001 +#define WGL_BACK_COLOR_BUFFER_BIT_ARB 0x00000002 +#define WGL_DEPTH_BUFFER_BIT_ARB 0x00000004 +#define WGL_STENCIL_BUFFER_BIT_ARB 0x00000008 + typedef HANDLE (WINAPI * PFNWGLCREATEBUFFERREGIONARBPROC) (HDC hDC, int iLayerPlane, UINT uType); + typedef VOID (WINAPI * PFNWGLDELETEBUFFERREGIONARBPROC) (HANDLE hRegion); + typedef BOOL (WINAPI * PFNWGLSAVEBUFFERREGIONARBPROC) (HANDLE hRegion, int x, int y, int width, int height); + typedef BOOL (WINAPI * PFNWGLRESTOREBUFFERREGIONARBPROC) (HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc); +#ifdef WGL_WGLEXT_PROTOTYPES + HANDLE WINAPI wglCreateBufferRegionARB (HDC hDC, int iLayerPlane, UINT uType); + VOID WINAPI wglDeleteBufferRegionARB (HANDLE hRegion); + BOOL WINAPI wglSaveBufferRegionARB (HANDLE hRegion, int x, int y, int width, int height); + BOOL WINAPI wglRestoreBufferRegionARB (HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc); +#endif +#endif /* WGL_ARB_buffer_region */ + +#ifndef WGL_ARB_context_flush_control +#define WGL_ARB_context_flush_control 1 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#endif /* WGL_ARB_context_flush_control */ + +#ifndef WGL_ARB_create_context +#define WGL_ARB_create_context 1 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 + typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int *attribList); +#ifdef WGL_WGLEXT_PROTOTYPES + HGLRC WINAPI wglCreateContextAttribsARB (HDC hDC, HGLRC hShareContext, const int *attribList); +#endif +#endif /* WGL_ARB_create_context */ + +#ifndef WGL_ARB_create_context_no_error +#define WGL_ARB_create_context_no_error 1 +#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3 +#endif /* WGL_ARB_create_context_no_error */ + +#ifndef WGL_ARB_create_context_profile +#define WGL_ARB_create_context_profile 1 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif /* WGL_ARB_create_context_profile */ + +#ifndef WGL_ARB_create_context_robustness +#define WGL_ARB_create_context_robustness 1 +#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 +#endif /* WGL_ARB_create_context_robustness */ + +#ifndef WGL_ARB_extensions_string +#define WGL_ARB_extensions_string 1 + typedef const char *(WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc); +#ifdef WGL_WGLEXT_PROTOTYPES + const char *WINAPI wglGetExtensionsStringARB (HDC hdc); +#endif +#endif /* WGL_ARB_extensions_string */ + +#ifndef WGL_ARB_framebuffer_sRGB +#define WGL_ARB_framebuffer_sRGB 1 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9 +#endif /* WGL_ARB_framebuffer_sRGB */ + +#ifndef WGL_ARB_make_current_read +#define WGL_ARB_make_current_read 1 +#define ERROR_INVALID_PIXEL_TYPE_ARB 0x2043 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 + typedef BOOL (WINAPI * PFNWGLMAKECONTEXTCURRENTARBPROC) (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); + typedef HDC (WINAPI * PFNWGLGETCURRENTREADDCARBPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglMakeContextCurrentARB (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); + HDC WINAPI wglGetCurrentReadDCARB (void); +#endif +#endif /* WGL_ARB_make_current_read */ + +#ifndef WGL_ARB_multisample +#define WGL_ARB_multisample 1 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif /* WGL_ARB_multisample */ + +#ifndef WGL_ARB_pbuffer +#define WGL_ARB_pbuffer 1 + DECLARE_HANDLE(HPBUFFERARB); +#define WGL_DRAW_TO_PBUFFER_ARB 0x202D +#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E +#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030 +#define WGL_PBUFFER_LARGEST_ARB 0x2033 +#define WGL_PBUFFER_WIDTH_ARB 0x2034 +#define WGL_PBUFFER_HEIGHT_ARB 0x2035 +#define WGL_PBUFFER_LOST_ARB 0x2036 + typedef HPBUFFERARB (WINAPI * PFNWGLCREATEPBUFFERARBPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); + typedef HDC (WINAPI * PFNWGLGETPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer); + typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer, HDC hDC); + typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFERARBPROC) (HPBUFFERARB hPbuffer); + typedef BOOL (WINAPI * PFNWGLQUERYPBUFFERARBPROC) (HPBUFFERARB hPbuffer, int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES + HPBUFFERARB WINAPI wglCreatePbufferARB (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); + HDC WINAPI wglGetPbufferDCARB (HPBUFFERARB hPbuffer); + int WINAPI wglReleasePbufferDCARB (HPBUFFERARB hPbuffer, HDC hDC); + BOOL WINAPI wglDestroyPbufferARB (HPBUFFERARB hPbuffer); + BOOL WINAPI wglQueryPbufferARB (HPBUFFERARB hPbuffer, int iAttribute, int *piValue); +#endif +#endif /* WGL_ARB_pbuffer */ + +#ifndef WGL_ARB_pixel_format +#define WGL_ARB_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C + typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues); + typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues); + typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglGetPixelFormatAttribivARB (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues); + BOOL WINAPI wglGetPixelFormatAttribfvARB (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues); + BOOL WINAPI wglChoosePixelFormatARB (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#endif +#endif /* WGL_ARB_pixel_format */ + +#ifndef WGL_ARB_pixel_format_float +#define WGL_ARB_pixel_format_float 1 +#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0 +#endif /* WGL_ARB_pixel_format_float */ + +#ifndef WGL_ARB_render_texture +#define WGL_ARB_render_texture 1 +#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070 +#define WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071 +#define WGL_TEXTURE_FORMAT_ARB 0x2072 +#define WGL_TEXTURE_TARGET_ARB 0x2073 +#define WGL_MIPMAP_TEXTURE_ARB 0x2074 +#define WGL_TEXTURE_RGB_ARB 0x2075 +#define WGL_TEXTURE_RGBA_ARB 0x2076 +#define WGL_NO_TEXTURE_ARB 0x2077 +#define WGL_TEXTURE_CUBE_MAP_ARB 0x2078 +#define WGL_TEXTURE_1D_ARB 0x2079 +#define WGL_TEXTURE_2D_ARB 0x207A +#define WGL_MIPMAP_LEVEL_ARB 0x207B +#define WGL_CUBE_MAP_FACE_ARB 0x207C +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080 +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081 +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082 +#define WGL_FRONT_LEFT_ARB 0x2083 +#define WGL_FRONT_RIGHT_ARB 0x2084 +#define WGL_BACK_LEFT_ARB 0x2085 +#define WGL_BACK_RIGHT_ARB 0x2086 +#define WGL_AUX0_ARB 0x2087 +#define WGL_AUX1_ARB 0x2088 +#define WGL_AUX2_ARB 0x2089 +#define WGL_AUX3_ARB 0x208A +#define WGL_AUX4_ARB 0x208B +#define WGL_AUX5_ARB 0x208C +#define WGL_AUX6_ARB 0x208D +#define WGL_AUX7_ARB 0x208E +#define WGL_AUX8_ARB 0x208F +#define WGL_AUX9_ARB 0x2090 + typedef BOOL (WINAPI * PFNWGLBINDTEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer); + typedef BOOL (WINAPI * PFNWGLRELEASETEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer); + typedef BOOL (WINAPI * PFNWGLSETPBUFFERATTRIBARBPROC) (HPBUFFERARB hPbuffer, const int *piAttribList); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglBindTexImageARB (HPBUFFERARB hPbuffer, int iBuffer); + BOOL WINAPI wglReleaseTexImageARB (HPBUFFERARB hPbuffer, int iBuffer); + BOOL WINAPI wglSetPbufferAttribARB (HPBUFFERARB hPbuffer, const int *piAttribList); +#endif +#endif /* WGL_ARB_render_texture */ + +#ifndef WGL_ARB_robustness_application_isolation +#define WGL_ARB_robustness_application_isolation 1 +#define WGL_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008 +#endif /* WGL_ARB_robustness_application_isolation */ + +#ifndef WGL_ARB_robustness_share_group_isolation +#define WGL_ARB_robustness_share_group_isolation 1 +#endif /* WGL_ARB_robustness_share_group_isolation */ + +#ifndef WGL_3DFX_multisample +#define WGL_3DFX_multisample 1 +#define WGL_SAMPLE_BUFFERS_3DFX 0x2060 +#define WGL_SAMPLES_3DFX 0x2061 +#endif /* WGL_3DFX_multisample */ + +#ifndef WGL_3DL_stereo_control +#define WGL_3DL_stereo_control 1 +#define WGL_STEREO_EMITTER_ENABLE_3DL 0x2055 +#define WGL_STEREO_EMITTER_DISABLE_3DL 0x2056 +#define WGL_STEREO_POLARITY_NORMAL_3DL 0x2057 +#define WGL_STEREO_POLARITY_INVERT_3DL 0x2058 + typedef BOOL (WINAPI * PFNWGLSETSTEREOEMITTERSTATE3DLPROC) (HDC hDC, UINT uState); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglSetStereoEmitterState3DL (HDC hDC, UINT uState); +#endif +#endif /* WGL_3DL_stereo_control */ + +#ifndef WGL_AMD_gpu_association +#define WGL_AMD_gpu_association 1 +#define WGL_GPU_VENDOR_AMD 0x1F00 +#define WGL_GPU_RENDERER_STRING_AMD 0x1F01 +#define WGL_GPU_OPENGL_VERSION_STRING_AMD 0x1F02 +#define WGL_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2 +#define WGL_GPU_RAM_AMD 0x21A3 +#define WGL_GPU_CLOCK_AMD 0x21A4 +#define WGL_GPU_NUM_PIPES_AMD 0x21A5 +#define WGL_GPU_NUM_SIMD_AMD 0x21A6 +#define WGL_GPU_NUM_RB_AMD 0x21A7 +#define WGL_GPU_NUM_SPI_AMD 0x21A8 + typedef UINT (WINAPI * PFNWGLGETGPUIDSAMDPROC) (UINT maxCount, UINT *ids); + typedef INT (WINAPI * PFNWGLGETGPUINFOAMDPROC) (UINT id, INT property, GLenum dataType, UINT size, void *data); + typedef UINT (WINAPI * PFNWGLGETCONTEXTGPUIDAMDPROC) (HGLRC hglrc); + typedef HGLRC (WINAPI * PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC) (UINT id); + typedef HGLRC (WINAPI * PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) (UINT id, HGLRC hShareContext, const int *attribList); + typedef BOOL (WINAPI * PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC) (HGLRC hglrc); + typedef BOOL (WINAPI * PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) (HGLRC hglrc); + typedef HGLRC (WINAPI * PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC) (void); + typedef VOID (WINAPI * PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC) (HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef WGL_WGLEXT_PROTOTYPES + UINT WINAPI wglGetGPUIDsAMD (UINT maxCount, UINT *ids); + INT WINAPI wglGetGPUInfoAMD (UINT id, INT property, GLenum dataType, UINT size, void *data); + UINT WINAPI wglGetContextGPUIDAMD (HGLRC hglrc); + HGLRC WINAPI wglCreateAssociatedContextAMD (UINT id); + HGLRC WINAPI wglCreateAssociatedContextAttribsAMD (UINT id, HGLRC hShareContext, const int *attribList); + BOOL WINAPI wglDeleteAssociatedContextAMD (HGLRC hglrc); + BOOL WINAPI wglMakeAssociatedContextCurrentAMD (HGLRC hglrc); + HGLRC WINAPI wglGetCurrentAssociatedContextAMD (void); + VOID WINAPI wglBlitContextFramebufferAMD (HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +#endif /* WGL_AMD_gpu_association */ + +#ifndef WGL_ATI_pixel_format_float +#define WGL_ATI_pixel_format_float 1 +#define WGL_TYPE_RGBA_FLOAT_ATI 0x21A0 +#endif /* WGL_ATI_pixel_format_float */ + +#ifndef WGL_ATI_render_texture_rectangle +#define WGL_ATI_render_texture_rectangle 1 +#define WGL_TEXTURE_RECTANGLE_ATI 0x21A5 +#endif /* WGL_ATI_render_texture_rectangle */ + +#ifndef WGL_EXT_colorspace +#define WGL_EXT_colorspace 1 +#define WGL_COLORSPACE_EXT 0x309D +#define WGL_COLORSPACE_SRGB_EXT 0x3089 +#define WGL_COLORSPACE_LINEAR_EXT 0x308A +#endif /* WGL_EXT_colorspace */ + +#ifndef WGL_EXT_create_context_es2_profile +#define WGL_EXT_create_context_es2_profile 1 +#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#endif /* WGL_EXT_create_context_es2_profile */ + +#ifndef WGL_EXT_create_context_es_profile +#define WGL_EXT_create_context_es_profile 1 +#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 +#endif /* WGL_EXT_create_context_es_profile */ + +#ifndef WGL_EXT_depth_float +#define WGL_EXT_depth_float 1 +#define WGL_DEPTH_FLOAT_EXT 0x2040 +#endif /* WGL_EXT_depth_float */ + +#ifndef WGL_EXT_display_color_table +#define WGL_EXT_display_color_table 1 + typedef GLboolean (WINAPI * PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC) (GLushort id); + typedef GLboolean (WINAPI * PFNWGLLOADDISPLAYCOLORTABLEEXTPROC) (const GLushort *table, GLuint length); + typedef GLboolean (WINAPI * PFNWGLBINDDISPLAYCOLORTABLEEXTPROC) (GLushort id); + typedef VOID (WINAPI * PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC) (GLushort id); +#ifdef WGL_WGLEXT_PROTOTYPES + GLboolean WINAPI wglCreateDisplayColorTableEXT (GLushort id); + GLboolean WINAPI wglLoadDisplayColorTableEXT (const GLushort *table, GLuint length); + GLboolean WINAPI wglBindDisplayColorTableEXT (GLushort id); + VOID WINAPI wglDestroyDisplayColorTableEXT (GLushort id); +#endif +#endif /* WGL_EXT_display_color_table */ + +#ifndef WGL_EXT_extensions_string +#define WGL_EXT_extensions_string 1 + typedef const char *(WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES + const char *WINAPI wglGetExtensionsStringEXT (void); +#endif +#endif /* WGL_EXT_extensions_string */ + +#ifndef WGL_EXT_framebuffer_sRGB +#define WGL_EXT_framebuffer_sRGB 1 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20A9 +#endif /* WGL_EXT_framebuffer_sRGB */ + +#ifndef WGL_EXT_make_current_read +#define WGL_EXT_make_current_read 1 +#define ERROR_INVALID_PIXEL_TYPE_EXT 0x2043 + typedef BOOL (WINAPI * PFNWGLMAKECONTEXTCURRENTEXTPROC) (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); + typedef HDC (WINAPI * PFNWGLGETCURRENTREADDCEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglMakeContextCurrentEXT (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); + HDC WINAPI wglGetCurrentReadDCEXT (void); +#endif +#endif /* WGL_EXT_make_current_read */ + +#ifndef WGL_EXT_multisample +#define WGL_EXT_multisample 1 +#define WGL_SAMPLE_BUFFERS_EXT 0x2041 +#define WGL_SAMPLES_EXT 0x2042 +#endif /* WGL_EXT_multisample */ + +#ifndef WGL_EXT_pbuffer +#define WGL_EXT_pbuffer 1 + DECLARE_HANDLE(HPBUFFEREXT); +#define WGL_DRAW_TO_PBUFFER_EXT 0x202D +#define WGL_MAX_PBUFFER_PIXELS_EXT 0x202E +#define WGL_MAX_PBUFFER_WIDTH_EXT 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_EXT 0x2030 +#define WGL_OPTIMAL_PBUFFER_WIDTH_EXT 0x2031 +#define WGL_OPTIMAL_PBUFFER_HEIGHT_EXT 0x2032 +#define WGL_PBUFFER_LARGEST_EXT 0x2033 +#define WGL_PBUFFER_WIDTH_EXT 0x2034 +#define WGL_PBUFFER_HEIGHT_EXT 0x2035 + typedef HPBUFFEREXT (WINAPI * PFNWGLCREATEPBUFFEREXTPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); + typedef HDC (WINAPI * PFNWGLGETPBUFFERDCEXTPROC) (HPBUFFEREXT hPbuffer); + typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCEXTPROC) (HPBUFFEREXT hPbuffer, HDC hDC); + typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFEREXTPROC) (HPBUFFEREXT hPbuffer); + typedef BOOL (WINAPI * PFNWGLQUERYPBUFFEREXTPROC) (HPBUFFEREXT hPbuffer, int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES + HPBUFFEREXT WINAPI wglCreatePbufferEXT (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); + HDC WINAPI wglGetPbufferDCEXT (HPBUFFEREXT hPbuffer); + int WINAPI wglReleasePbufferDCEXT (HPBUFFEREXT hPbuffer, HDC hDC); + BOOL WINAPI wglDestroyPbufferEXT (HPBUFFEREXT hPbuffer); + BOOL WINAPI wglQueryPbufferEXT (HPBUFFEREXT hPbuffer, int iAttribute, int *piValue); +#endif +#endif /* WGL_EXT_pbuffer */ + +#ifndef WGL_EXT_pixel_format +#define WGL_EXT_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_EXT 0x2000 +#define WGL_DRAW_TO_WINDOW_EXT 0x2001 +#define WGL_DRAW_TO_BITMAP_EXT 0x2002 +#define WGL_ACCELERATION_EXT 0x2003 +#define WGL_NEED_PALETTE_EXT 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_EXT 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_EXT 0x2006 +#define WGL_SWAP_METHOD_EXT 0x2007 +#define WGL_NUMBER_OVERLAYS_EXT 0x2008 +#define WGL_NUMBER_UNDERLAYS_EXT 0x2009 +#define WGL_TRANSPARENT_EXT 0x200A +#define WGL_TRANSPARENT_VALUE_EXT 0x200B +#define WGL_SHARE_DEPTH_EXT 0x200C +#define WGL_SHARE_STENCIL_EXT 0x200D +#define WGL_SHARE_ACCUM_EXT 0x200E +#define WGL_SUPPORT_GDI_EXT 0x200F +#define WGL_SUPPORT_OPENGL_EXT 0x2010 +#define WGL_DOUBLE_BUFFER_EXT 0x2011 +#define WGL_STEREO_EXT 0x2012 +#define WGL_PIXEL_TYPE_EXT 0x2013 +#define WGL_COLOR_BITS_EXT 0x2014 +#define WGL_RED_BITS_EXT 0x2015 +#define WGL_RED_SHIFT_EXT 0x2016 +#define WGL_GREEN_BITS_EXT 0x2017 +#define WGL_GREEN_SHIFT_EXT 0x2018 +#define WGL_BLUE_BITS_EXT 0x2019 +#define WGL_BLUE_SHIFT_EXT 0x201A +#define WGL_ALPHA_BITS_EXT 0x201B +#define WGL_ALPHA_SHIFT_EXT 0x201C +#define WGL_ACCUM_BITS_EXT 0x201D +#define WGL_ACCUM_RED_BITS_EXT 0x201E +#define WGL_ACCUM_GREEN_BITS_EXT 0x201F +#define WGL_ACCUM_BLUE_BITS_EXT 0x2020 +#define WGL_ACCUM_ALPHA_BITS_EXT 0x2021 +#define WGL_DEPTH_BITS_EXT 0x2022 +#define WGL_STENCIL_BITS_EXT 0x2023 +#define WGL_AUX_BUFFERS_EXT 0x2024 +#define WGL_NO_ACCELERATION_EXT 0x2025 +#define WGL_GENERIC_ACCELERATION_EXT 0x2026 +#define WGL_FULL_ACCELERATION_EXT 0x2027 +#define WGL_SWAP_EXCHANGE_EXT 0x2028 +#define WGL_SWAP_COPY_EXT 0x2029 +#define WGL_SWAP_UNDEFINED_EXT 0x202A +#define WGL_TYPE_RGBA_EXT 0x202B +#define WGL_TYPE_COLORINDEX_EXT 0x202C + typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVEXTPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *piValues); + typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVEXTPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues); + typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATEXTPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglGetPixelFormatAttribivEXT (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *piValues); + BOOL WINAPI wglGetPixelFormatAttribfvEXT (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues); + BOOL WINAPI wglChoosePixelFormatEXT (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#endif +#endif /* WGL_EXT_pixel_format */ + +#ifndef WGL_EXT_pixel_format_packed_float +#define WGL_EXT_pixel_format_packed_float 1 +#define WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT 0x20A8 +#endif /* WGL_EXT_pixel_format_packed_float */ + +#ifndef WGL_EXT_swap_control +#define WGL_EXT_swap_control 1 + typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); + typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglSwapIntervalEXT (int interval); + int WINAPI wglGetSwapIntervalEXT (void); +#endif +#endif /* WGL_EXT_swap_control */ + +#ifndef WGL_EXT_swap_control_tear +#define WGL_EXT_swap_control_tear 1 +#endif /* WGL_EXT_swap_control_tear */ + +#ifndef WGL_I3D_digital_video_control +#define WGL_I3D_digital_video_control 1 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D 0x2050 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D 0x2051 +#define WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D 0x2052 +#define WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D 0x2053 + typedef BOOL (WINAPI * PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC) (HDC hDC, int iAttribute, int *piValue); + typedef BOOL (WINAPI * PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC) (HDC hDC, int iAttribute, const int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglGetDigitalVideoParametersI3D (HDC hDC, int iAttribute, int *piValue); + BOOL WINAPI wglSetDigitalVideoParametersI3D (HDC hDC, int iAttribute, const int *piValue); +#endif +#endif /* WGL_I3D_digital_video_control */ + +#ifndef WGL_I3D_gamma +#define WGL_I3D_gamma 1 +#define WGL_GAMMA_TABLE_SIZE_I3D 0x204E +#define WGL_GAMMA_EXCLUDE_DESKTOP_I3D 0x204F + typedef BOOL (WINAPI * PFNWGLGETGAMMATABLEPARAMETERSI3DPROC) (HDC hDC, int iAttribute, int *piValue); + typedef BOOL (WINAPI * PFNWGLSETGAMMATABLEPARAMETERSI3DPROC) (HDC hDC, int iAttribute, const int *piValue); + typedef BOOL (WINAPI * PFNWGLGETGAMMATABLEI3DPROC) (HDC hDC, int iEntries, USHORT *puRed, USHORT *puGreen, USHORT *puBlue); + typedef BOOL (WINAPI * PFNWGLSETGAMMATABLEI3DPROC) (HDC hDC, int iEntries, const USHORT *puRed, const USHORT *puGreen, const USHORT *puBlue); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglGetGammaTableParametersI3D (HDC hDC, int iAttribute, int *piValue); + BOOL WINAPI wglSetGammaTableParametersI3D (HDC hDC, int iAttribute, const int *piValue); + BOOL WINAPI wglGetGammaTableI3D (HDC hDC, int iEntries, USHORT *puRed, USHORT *puGreen, USHORT *puBlue); + BOOL WINAPI wglSetGammaTableI3D (HDC hDC, int iEntries, const USHORT *puRed, const USHORT *puGreen, const USHORT *puBlue); +#endif +#endif /* WGL_I3D_gamma */ + +#ifndef WGL_I3D_genlock +#define WGL_I3D_genlock 1 +#define WGL_GENLOCK_SOURCE_MULTIVIEW_I3D 0x2044 +#define WGL_GENLOCK_SOURCE_EXTERNAL_SYNC_I3D 0x2045 +#define WGL_GENLOCK_SOURCE_EXTERNAL_FIELD_I3D 0x2046 +#define WGL_GENLOCK_SOURCE_EXTERNAL_TTL_I3D 0x2047 +#define WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D 0x2048 +#define WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D 0x2049 +#define WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D 0x204A +#define WGL_GENLOCK_SOURCE_EDGE_RISING_I3D 0x204B +#define WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D 0x204C + typedef BOOL (WINAPI * PFNWGLENABLEGENLOCKI3DPROC) (HDC hDC); + typedef BOOL (WINAPI * PFNWGLDISABLEGENLOCKI3DPROC) (HDC hDC); + typedef BOOL (WINAPI * PFNWGLISENABLEDGENLOCKI3DPROC) (HDC hDC, BOOL *pFlag); + typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEI3DPROC) (HDC hDC, UINT uSource); + typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEI3DPROC) (HDC hDC, UINT *uSource); + typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEEDGEI3DPROC) (HDC hDC, UINT uEdge); + typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEEDGEI3DPROC) (HDC hDC, UINT *uEdge); + typedef BOOL (WINAPI * PFNWGLGENLOCKSAMPLERATEI3DPROC) (HDC hDC, UINT uRate); + typedef BOOL (WINAPI * PFNWGLGETGENLOCKSAMPLERATEI3DPROC) (HDC hDC, UINT *uRate); + typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEDELAYI3DPROC) (HDC hDC, UINT uDelay); + typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEDELAYI3DPROC) (HDC hDC, UINT *uDelay); + typedef BOOL (WINAPI * PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC) (HDC hDC, UINT *uMaxLineDelay, UINT *uMaxPixelDelay); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglEnableGenlockI3D (HDC hDC); + BOOL WINAPI wglDisableGenlockI3D (HDC hDC); + BOOL WINAPI wglIsEnabledGenlockI3D (HDC hDC, BOOL *pFlag); + BOOL WINAPI wglGenlockSourceI3D (HDC hDC, UINT uSource); + BOOL WINAPI wglGetGenlockSourceI3D (HDC hDC, UINT *uSource); + BOOL WINAPI wglGenlockSourceEdgeI3D (HDC hDC, UINT uEdge); + BOOL WINAPI wglGetGenlockSourceEdgeI3D (HDC hDC, UINT *uEdge); + BOOL WINAPI wglGenlockSampleRateI3D (HDC hDC, UINT uRate); + BOOL WINAPI wglGetGenlockSampleRateI3D (HDC hDC, UINT *uRate); + BOOL WINAPI wglGenlockSourceDelayI3D (HDC hDC, UINT uDelay); + BOOL WINAPI wglGetGenlockSourceDelayI3D (HDC hDC, UINT *uDelay); + BOOL WINAPI wglQueryGenlockMaxSourceDelayI3D (HDC hDC, UINT *uMaxLineDelay, UINT *uMaxPixelDelay); +#endif +#endif /* WGL_I3D_genlock */ + +#ifndef WGL_I3D_image_buffer +#define WGL_I3D_image_buffer 1 +#define WGL_IMAGE_BUFFER_MIN_ACCESS_I3D 0x00000001 +#define WGL_IMAGE_BUFFER_LOCK_I3D 0x00000002 + typedef LPVOID (WINAPI * PFNWGLCREATEIMAGEBUFFERI3DPROC) (HDC hDC, DWORD dwSize, UINT uFlags); + typedef BOOL (WINAPI * PFNWGLDESTROYIMAGEBUFFERI3DPROC) (HDC hDC, LPVOID pAddress); + typedef BOOL (WINAPI * PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC) (HDC hDC, const HANDLE *pEvent, const LPVOID *pAddress, const DWORD *pSize, UINT count); + typedef BOOL (WINAPI * PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC) (HDC hDC, const LPVOID *pAddress, UINT count); +#ifdef WGL_WGLEXT_PROTOTYPES + LPVOID WINAPI wglCreateImageBufferI3D (HDC hDC, DWORD dwSize, UINT uFlags); + BOOL WINAPI wglDestroyImageBufferI3D (HDC hDC, LPVOID pAddress); + BOOL WINAPI wglAssociateImageBufferEventsI3D (HDC hDC, const HANDLE *pEvent, const LPVOID *pAddress, const DWORD *pSize, UINT count); + BOOL WINAPI wglReleaseImageBufferEventsI3D (HDC hDC, const LPVOID *pAddress, UINT count); +#endif +#endif /* WGL_I3D_image_buffer */ + +#ifndef WGL_I3D_swap_frame_lock +#define WGL_I3D_swap_frame_lock 1 + typedef BOOL (WINAPI * PFNWGLENABLEFRAMELOCKI3DPROC) (void); + typedef BOOL (WINAPI * PFNWGLDISABLEFRAMELOCKI3DPROC) (void); + typedef BOOL (WINAPI * PFNWGLISENABLEDFRAMELOCKI3DPROC) (BOOL *pFlag); + typedef BOOL (WINAPI * PFNWGLQUERYFRAMELOCKMASTERI3DPROC) (BOOL *pFlag); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglEnableFrameLockI3D (void); + BOOL WINAPI wglDisableFrameLockI3D (void); + BOOL WINAPI wglIsEnabledFrameLockI3D (BOOL *pFlag); + BOOL WINAPI wglQueryFrameLockMasterI3D (BOOL *pFlag); +#endif +#endif /* WGL_I3D_swap_frame_lock */ + +#ifndef WGL_I3D_swap_frame_usage +#define WGL_I3D_swap_frame_usage 1 + typedef BOOL (WINAPI * PFNWGLGETFRAMEUSAGEI3DPROC) (float *pUsage); + typedef BOOL (WINAPI * PFNWGLBEGINFRAMETRACKINGI3DPROC) (void); + typedef BOOL (WINAPI * PFNWGLENDFRAMETRACKINGI3DPROC) (void); + typedef BOOL (WINAPI * PFNWGLQUERYFRAMETRACKINGI3DPROC) (DWORD *pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglGetFrameUsageI3D (float *pUsage); + BOOL WINAPI wglBeginFrameTrackingI3D (void); + BOOL WINAPI wglEndFrameTrackingI3D (void); + BOOL WINAPI wglQueryFrameTrackingI3D (DWORD *pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage); +#endif +#endif /* WGL_I3D_swap_frame_usage */ + +#ifndef WGL_NV_DX_interop +#define WGL_NV_DX_interop 1 +#define WGL_ACCESS_READ_ONLY_NV 0x00000000 +#define WGL_ACCESS_READ_WRITE_NV 0x00000001 +#define WGL_ACCESS_WRITE_DISCARD_NV 0x00000002 + typedef BOOL (WINAPI * PFNWGLDXSETRESOURCESHAREHANDLENVPROC) (void *dxObject, HANDLE shareHandle); + typedef HANDLE (WINAPI * PFNWGLDXOPENDEVICENVPROC) (void *dxDevice); + typedef BOOL (WINAPI * PFNWGLDXCLOSEDEVICENVPROC) (HANDLE hDevice); + typedef HANDLE (WINAPI * PFNWGLDXREGISTEROBJECTNVPROC) (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access); + typedef BOOL (WINAPI * PFNWGLDXUNREGISTEROBJECTNVPROC) (HANDLE hDevice, HANDLE hObject); + typedef BOOL (WINAPI * PFNWGLDXOBJECTACCESSNVPROC) (HANDLE hObject, GLenum access); + typedef BOOL (WINAPI * PFNWGLDXLOCKOBJECTSNVPROC) (HANDLE hDevice, GLint count, HANDLE *hObjects); + typedef BOOL (WINAPI * PFNWGLDXUNLOCKOBJECTSNVPROC) (HANDLE hDevice, GLint count, HANDLE *hObjects); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglDXSetResourceShareHandleNV (void *dxObject, HANDLE shareHandle); + HANDLE WINAPI wglDXOpenDeviceNV (void *dxDevice); + BOOL WINAPI wglDXCloseDeviceNV (HANDLE hDevice); + HANDLE WINAPI wglDXRegisterObjectNV (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access); + BOOL WINAPI wglDXUnregisterObjectNV (HANDLE hDevice, HANDLE hObject); + BOOL WINAPI wglDXObjectAccessNV (HANDLE hObject, GLenum access); + BOOL WINAPI wglDXLockObjectsNV (HANDLE hDevice, GLint count, HANDLE *hObjects); + BOOL WINAPI wglDXUnlockObjectsNV (HANDLE hDevice, GLint count, HANDLE *hObjects); +#endif +#endif /* WGL_NV_DX_interop */ + +#ifndef WGL_NV_DX_interop2 +#define WGL_NV_DX_interop2 1 +#endif /* WGL_NV_DX_interop2 */ + +#ifndef WGL_NV_copy_image +#define WGL_NV_copy_image 1 + typedef BOOL (WINAPI * PFNWGLCOPYIMAGESUBDATANVPROC) (HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglCopyImageSubDataNV (HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* WGL_NV_copy_image */ + +#ifndef WGL_NV_delay_before_swap +#define WGL_NV_delay_before_swap 1 + typedef BOOL (WINAPI * PFNWGLDELAYBEFORESWAPNVPROC) (HDC hDC, GLfloat seconds); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglDelayBeforeSwapNV (HDC hDC, GLfloat seconds); +#endif +#endif /* WGL_NV_delay_before_swap */ + +#ifndef WGL_NV_float_buffer +#define WGL_NV_float_buffer 1 +#define WGL_FLOAT_COMPONENTS_NV 0x20B0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_R_NV 0x20B1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RG_NV 0x20B2 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV 0x20B3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV 0x20B4 +#define WGL_TEXTURE_FLOAT_R_NV 0x20B5 +#define WGL_TEXTURE_FLOAT_RG_NV 0x20B6 +#define WGL_TEXTURE_FLOAT_RGB_NV 0x20B7 +#define WGL_TEXTURE_FLOAT_RGBA_NV 0x20B8 +#endif /* WGL_NV_float_buffer */ + +#ifndef WGL_NV_gpu_affinity +#define WGL_NV_gpu_affinity 1 + DECLARE_HANDLE(HGPUNV); + struct _GPU_DEVICE { + DWORD cb; + CHAR DeviceName[32]; + CHAR DeviceString[128]; + DWORD Flags; + RECT rcVirtualScreen; + }; + typedef struct _GPU_DEVICE *PGPU_DEVICE; +#define ERROR_INCOMPATIBLE_AFFINITY_MASKS_NV 0x20D0 +#define ERROR_MISSING_AFFINITY_MASK_NV 0x20D1 + typedef BOOL (WINAPI * PFNWGLENUMGPUSNVPROC) (UINT iGpuIndex, HGPUNV *phGpu); + typedef BOOL (WINAPI * PFNWGLENUMGPUDEVICESNVPROC) (HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice); + typedef HDC (WINAPI * PFNWGLCREATEAFFINITYDCNVPROC) (const HGPUNV *phGpuList); + typedef BOOL (WINAPI * PFNWGLENUMGPUSFROMAFFINITYDCNVPROC) (HDC hAffinityDC, UINT iGpuIndex, HGPUNV *hGpu); + typedef BOOL (WINAPI * PFNWGLDELETEDCNVPROC) (HDC hdc); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglEnumGpusNV (UINT iGpuIndex, HGPUNV *phGpu); + BOOL WINAPI wglEnumGpuDevicesNV (HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice); + HDC WINAPI wglCreateAffinityDCNV (const HGPUNV *phGpuList); + BOOL WINAPI wglEnumGpusFromAffinityDCNV (HDC hAffinityDC, UINT iGpuIndex, HGPUNV *hGpu); + BOOL WINAPI wglDeleteDCNV (HDC hdc); +#endif +#endif /* WGL_NV_gpu_affinity */ + +#ifndef WGL_NV_multigpu_context +#define WGL_NV_multigpu_context 1 +#define WGL_CONTEXT_MULTIGPU_ATTRIB_NV 0x20AA +#define WGL_CONTEXT_MULTIGPU_ATTRIB_SINGLE_NV 0x20AB +#define WGL_CONTEXT_MULTIGPU_ATTRIB_AFR_NV 0x20AC +#define WGL_CONTEXT_MULTIGPU_ATTRIB_MULTICAST_NV 0x20AD +#define WGL_CONTEXT_MULTIGPU_ATTRIB_MULTI_DISPLAY_MULTICAST_NV 0x20AE +#endif /* WGL_NV_multigpu_context */ + +#ifndef WGL_NV_multisample_coverage +#define WGL_NV_multisample_coverage 1 +#define WGL_COVERAGE_SAMPLES_NV 0x2042 +#define WGL_COLOR_SAMPLES_NV 0x20B9 +#endif /* WGL_NV_multisample_coverage */ + +#ifndef WGL_NV_present_video +#define WGL_NV_present_video 1 + DECLARE_HANDLE(HVIDEOOUTPUTDEVICENV); +#define WGL_NUM_VIDEO_SLOTS_NV 0x20F0 + typedef int (WINAPI * PFNWGLENUMERATEVIDEODEVICESNVPROC) (HDC hDc, HVIDEOOUTPUTDEVICENV *phDeviceList); + typedef BOOL (WINAPI * PFNWGLBINDVIDEODEVICENVPROC) (HDC hDc, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int *piAttribList); + typedef BOOL (WINAPI * PFNWGLQUERYCURRENTCONTEXTNVPROC) (int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES + int WINAPI wglEnumerateVideoDevicesNV (HDC hDc, HVIDEOOUTPUTDEVICENV *phDeviceList); + BOOL WINAPI wglBindVideoDeviceNV (HDC hDc, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int *piAttribList); + BOOL WINAPI wglQueryCurrentContextNV (int iAttribute, int *piValue); +#endif +#endif /* WGL_NV_present_video */ + +#ifndef WGL_NV_render_depth_texture +#define WGL_NV_render_depth_texture 1 +#define WGL_BIND_TO_TEXTURE_DEPTH_NV 0x20A3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_DEPTH_NV 0x20A4 +#define WGL_DEPTH_TEXTURE_FORMAT_NV 0x20A5 +#define WGL_TEXTURE_DEPTH_COMPONENT_NV 0x20A6 +#define WGL_DEPTH_COMPONENT_NV 0x20A7 +#endif /* WGL_NV_render_depth_texture */ + +#ifndef WGL_NV_render_texture_rectangle +#define WGL_NV_render_texture_rectangle 1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV 0x20A0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV 0x20A1 +#define WGL_TEXTURE_RECTANGLE_NV 0x20A2 +#endif /* WGL_NV_render_texture_rectangle */ + +#ifndef WGL_NV_swap_group +#define WGL_NV_swap_group 1 + typedef BOOL (WINAPI * PFNWGLJOINSWAPGROUPNVPROC) (HDC hDC, GLuint group); + typedef BOOL (WINAPI * PFNWGLBINDSWAPBARRIERNVPROC) (GLuint group, GLuint barrier); + typedef BOOL (WINAPI * PFNWGLQUERYSWAPGROUPNVPROC) (HDC hDC, GLuint *group, GLuint *barrier); + typedef BOOL (WINAPI * PFNWGLQUERYMAXSWAPGROUPSNVPROC) (HDC hDC, GLuint *maxGroups, GLuint *maxBarriers); + typedef BOOL (WINAPI * PFNWGLQUERYFRAMECOUNTNVPROC) (HDC hDC, GLuint *count); + typedef BOOL (WINAPI * PFNWGLRESETFRAMECOUNTNVPROC) (HDC hDC); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglJoinSwapGroupNV (HDC hDC, GLuint group); + BOOL WINAPI wglBindSwapBarrierNV (GLuint group, GLuint barrier); + BOOL WINAPI wglQuerySwapGroupNV (HDC hDC, GLuint *group, GLuint *barrier); + BOOL WINAPI wglQueryMaxSwapGroupsNV (HDC hDC, GLuint *maxGroups, GLuint *maxBarriers); + BOOL WINAPI wglQueryFrameCountNV (HDC hDC, GLuint *count); + BOOL WINAPI wglResetFrameCountNV (HDC hDC); +#endif +#endif /* WGL_NV_swap_group */ + +#ifndef WGL_NV_vertex_array_range +#define WGL_NV_vertex_array_range 1 + typedef void *(WINAPI * PFNWGLALLOCATEMEMORYNVPROC) (GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority); + typedef void (WINAPI * PFNWGLFREEMEMORYNVPROC) (void *pointer); +#ifdef WGL_WGLEXT_PROTOTYPES + void *WINAPI wglAllocateMemoryNV (GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority); + void WINAPI wglFreeMemoryNV (void *pointer); +#endif +#endif /* WGL_NV_vertex_array_range */ + +#ifndef WGL_NV_video_capture +#define WGL_NV_video_capture 1 + DECLARE_HANDLE(HVIDEOINPUTDEVICENV); +#define WGL_UNIQUE_ID_NV 0x20CE +#define WGL_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF + typedef BOOL (WINAPI * PFNWGLBINDVIDEOCAPTUREDEVICENVPROC) (UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice); + typedef UINT (WINAPI * PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC) (HDC hDc, HVIDEOINPUTDEVICENV *phDeviceList); + typedef BOOL (WINAPI * PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice); + typedef BOOL (WINAPI * PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int *piValue); + typedef BOOL (WINAPI * PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglBindVideoCaptureDeviceNV (UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice); + UINT WINAPI wglEnumerateVideoCaptureDevicesNV (HDC hDc, HVIDEOINPUTDEVICENV *phDeviceList); + BOOL WINAPI wglLockVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice); + BOOL WINAPI wglQueryVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int *piValue); + BOOL WINAPI wglReleaseVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +#endif +#endif /* WGL_NV_video_capture */ + +#ifndef WGL_NV_video_output +#define WGL_NV_video_output 1 + DECLARE_HANDLE(HPVIDEODEV); +#define WGL_BIND_TO_VIDEO_RGB_NV 0x20C0 +#define WGL_BIND_TO_VIDEO_RGBA_NV 0x20C1 +#define WGL_BIND_TO_VIDEO_RGB_AND_DEPTH_NV 0x20C2 +#define WGL_VIDEO_OUT_COLOR_NV 0x20C3 +#define WGL_VIDEO_OUT_ALPHA_NV 0x20C4 +#define WGL_VIDEO_OUT_DEPTH_NV 0x20C5 +#define WGL_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6 +#define WGL_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7 +#define WGL_VIDEO_OUT_FRAME 0x20C8 +#define WGL_VIDEO_OUT_FIELD_1 0x20C9 +#define WGL_VIDEO_OUT_FIELD_2 0x20CA +#define WGL_VIDEO_OUT_STACKED_FIELDS_1_2 0x20CB +#define WGL_VIDEO_OUT_STACKED_FIELDS_2_1 0x20CC + typedef BOOL (WINAPI * PFNWGLGETVIDEODEVICENVPROC) (HDC hDC, int numDevices, HPVIDEODEV *hVideoDevice); + typedef BOOL (WINAPI * PFNWGLRELEASEVIDEODEVICENVPROC) (HPVIDEODEV hVideoDevice); + typedef BOOL (WINAPI * PFNWGLBINDVIDEOIMAGENVPROC) (HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer); + typedef BOOL (WINAPI * PFNWGLRELEASEVIDEOIMAGENVPROC) (HPBUFFERARB hPbuffer, int iVideoBuffer); + typedef BOOL (WINAPI * PFNWGLSENDPBUFFERTOVIDEONVPROC) (HPBUFFERARB hPbuffer, int iBufferType, unsigned long *pulCounterPbuffer, BOOL bBlock); + typedef BOOL (WINAPI * PFNWGLGETVIDEOINFONVPROC) (HPVIDEODEV hpVideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglGetVideoDeviceNV (HDC hDC, int numDevices, HPVIDEODEV *hVideoDevice); + BOOL WINAPI wglReleaseVideoDeviceNV (HPVIDEODEV hVideoDevice); + BOOL WINAPI wglBindVideoImageNV (HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer); + BOOL WINAPI wglReleaseVideoImageNV (HPBUFFERARB hPbuffer, int iVideoBuffer); + BOOL WINAPI wglSendPbufferToVideoNV (HPBUFFERARB hPbuffer, int iBufferType, unsigned long *pulCounterPbuffer, BOOL bBlock); + BOOL WINAPI wglGetVideoInfoNV (HPVIDEODEV hpVideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +#endif +#endif /* WGL_NV_video_output */ + +#ifndef WGL_OML_sync_control +#define WGL_OML_sync_control 1 + typedef BOOL (WINAPI * PFNWGLGETSYNCVALUESOMLPROC) (HDC hdc, INT64 *ust, INT64 *msc, INT64 *sbc); + typedef BOOL (WINAPI * PFNWGLGETMSCRATEOMLPROC) (HDC hdc, INT32 *numerator, INT32 *denominator); + typedef INT64 (WINAPI * PFNWGLSWAPBUFFERSMSCOMLPROC) (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder); + typedef INT64 (WINAPI * PFNWGLSWAPLAYERBUFFERSMSCOMLPROC) (HDC hdc, INT fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder); + typedef BOOL (WINAPI * PFNWGLWAITFORMSCOMLPROC) (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 *ust, INT64 *msc, INT64 *sbc); + typedef BOOL (WINAPI * PFNWGLWAITFORSBCOMLPROC) (HDC hdc, INT64 target_sbc, INT64 *ust, INT64 *msc, INT64 *sbc); +#ifdef WGL_WGLEXT_PROTOTYPES + BOOL WINAPI wglGetSyncValuesOML (HDC hdc, INT64 *ust, INT64 *msc, INT64 *sbc); + BOOL WINAPI wglGetMscRateOML (HDC hdc, INT32 *numerator, INT32 *denominator); + INT64 WINAPI wglSwapBuffersMscOML (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder); + INT64 WINAPI wglSwapLayerBuffersMscOML (HDC hdc, INT fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder); + BOOL WINAPI wglWaitForMscOML (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 *ust, INT64 *msc, INT64 *sbc); + BOOL WINAPI wglWaitForSbcOML (HDC hdc, INT64 target_sbc, INT64 *ust, INT64 *msc, INT64 *sbc); +#endif +#endif /* WGL_OML_sync_control */ + +#ifdef __cplusplus +} +#endif + +#endif \ 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 index 4eed08d..252ce0e 100644 --- a/src_v2/platform/win32/lumenarium_first_win32.cpp +++ b/src_v2/platform/win32/lumenarium_first_win32.cpp @@ -2,8 +2,9 @@ #include "../lumenarium_compiler_flags.h" #include "../lumenarium_platform_common_includes.h" -#include "windows.h" +#include #include +#include #include "../../lumenarium_types.h" #include "../lumenarium_platform.h" @@ -16,11 +17,18 @@ win32_get_last_error() win32_last_error = GetLastError(); } +global bool running = true; + +#include "lumenarium_win32_opengl.h" + +global Win32_OpenGL_Extensions gl; + #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" +#include "lumenarium_win32_graphics.cpp" internal Platform_Key_Flags win32_get_key_flags_mod() @@ -172,22 +180,25 @@ WinMain( 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_window_create( + &win32_main_window, + hInstance, + "Lumenariumtest0", + 1600, + 900, + win32_window_event_handler + ); win32_time_init(); win32_files_init(); win32_threads_init(); + App_State* state = lumenarium_init(); + if (!has_flag(state->flags, AppState_IsRunning)) return 0; + Platform_Ticks ticks_start = platform_get_ticks(); - while (has_flag(state->flags, AppState_IsRunning)) + while (running && has_flag(state->flags, AppState_IsRunning)) { win32_threads_reclaim(); lumenarium_frame_prepare(state); @@ -213,9 +224,14 @@ WinMain( win32_window_handle_event(window_msg, &win32_main_window, state); } + // NOTE(PS): WM_CLOSE and WM_DESTROY can both be issued + // the same frame, meaning our drawing context is destroyed + // before calling lumenarium_frame so skipping here to avoid + // using invalid resources + if (!running || !has_flag(state->flags, AppState_IsRunning)) continue; + lumenarium_frame(state); - // Swap Render Buffers SwapBuffers(win32_main_window.dc); //////////////////////////////////////// @@ -236,7 +252,6 @@ WinMain( lumenarium_cleanup(state); - // threads cleanup for (u32 i = 1; i < win32_threads_cap; i++) { @@ -244,6 +259,8 @@ WinMain( TerminateThread(win32_threads[i], 0); } - ExitProcess(0); + // windows cleanup + UnregisterClass(win32_main_window.window_class.lpszClassName, hInstance); + return 0; } diff --git a/src_v2/platform/win32/lumenarium_win32_graphics.cpp b/src_v2/platform/win32/lumenarium_win32_graphics.cpp new file mode 100644 index 0000000..59e04dc --- /dev/null +++ b/src_v2/platform/win32/lumenarium_win32_graphics.cpp @@ -0,0 +1,193 @@ + +#define win32_gl_no_error() win32_gl_no_error_() +void win32_gl_no_error_() { + u32 error = glGetError(); + char* str = 0; + if (error) { + str = win32_gl_error_to_string(error); + } + assert(error == 0); +} + +Platform_Geometry_Buffer +platform_geometry_buffer_create( + r32* vertices, u32 vertices_len, + u32* indices, u32 indices_len + ){ + Platform_Geometry_Buffer result = {}; + + gl.glGenVertexArrays(1, &result.buffer_id_vao); + win32_gl_no_error(); + + GLuint buffers[2]; + gl.glGenBuffers(2, (GLuint*)buffers); + win32_gl_no_error(); + + result.buffer_id_vertices = buffers[0]; + result.buffer_id_indices = buffers[1]; + + // Vertices + gl.glBindVertexArray(result.buffer_id_vao); + gl.glBindBuffer(GL_ARRAY_BUFFER, result.buffer_id_vertices); + win32_gl_no_error(); + + gl.glBufferData( + GL_ARRAY_BUFFER, sizeof(r32) * vertices_len, vertices, GL_STATIC_DRAW + ); + win32_gl_no_error(); + + // Indices + gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.buffer_id_indices); + win32_gl_no_error(); + + gl.glBufferData( + GL_ELEMENT_ARRAY_BUFFER, sizeof(u32) * indices_len, indices, GL_STATIC_DRAW + ); + win32_gl_no_error(); + result.indices_len = indices_len; + + gl.glBindBuffer(GL_ARRAY_BUFFER, NULL); + gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); + + return result; +} + +Platform_Shader +platform_shader_create( + String code_vert, String code_frag, String* attrs, u32 attrs_len + ){ + Platform_Shader result = {}; + + GLuint shader_vert = gl.glCreateShader(GL_VERTEX_SHADER); + gl.glShaderSource(shader_vert, 1, (const char**)&code_vert.str, &(s32)code_vert.len); + gl.glCompileShader(shader_vert); + { // errors + GLint shader_vert_compiled; + gl.glGetShaderiv(shader_vert, GL_COMPILE_STATUS, &shader_vert_compiled); + if (shader_vert_compiled != GL_TRUE) + { + GLsizei log_length = 0; + GLchar message[1024]; + gl.glGetShaderInfoLog(shader_vert, 1024, &log_length, message); + invalid_code_path; + } + } + + GLuint shader_frag = gl.glCreateShader(GL_FRAGMENT_SHADER); + gl.glShaderSource(shader_frag, 1, (const char**)&code_frag.str, &(s32)code_frag.len); + gl.glCompileShader(shader_frag); + { // errors + GLint shader_frag_compiled; + gl.glGetShaderiv(shader_frag, GL_COMPILE_STATUS, &shader_frag_compiled); + if (shader_frag_compiled != GL_TRUE) + { + GLsizei log_length = 0; + GLchar message[1024]; + gl.glGetShaderInfoLog(shader_frag, 1024, &log_length, message); + invalid_code_path; + } + } + + result.id = (u32)gl.glCreateProgram(); + gl.glAttachShader(result.id, shader_vert); + gl.glAttachShader(result.id, shader_frag); + gl.glLinkProgram(result.id); + + GLint program_linked; + gl.glGetProgramiv(result.id, GL_LINK_STATUS, &program_linked); + if (program_linked != GL_TRUE) + { + GLsizei log_length = 0; + GLchar message[1024]; + gl.glGetProgramInfoLog(result.id, 1024, &log_length, message); + invalid_code_path; + } + + gl.glUseProgram(result.id); + + // TODO(PS): delete the vert and frag programs + + assert(attrs_len < PLATFORM_SHADER_MAX_ATTRS); + for (u32 i = 0; i < attrs_len; i++) + { + result.attrs[i] = gl.glGetAttribLocation( + result.id, (char*)attrs[i].str + ); + } + result.attrs[attrs_len] = PLATFORM_SHADER_ATTR_LAST; + + return result; +} + +void +platform_geometry_bind(Platform_Geometry_Buffer geo) +{ + gl.glBindVertexArray(geo.buffer_id_vertices); + win32_gl_no_error(); + + gl.glBindBuffer(GL_ARRAY_BUFFER, geo.buffer_id_vertices); + win32_gl_no_error(); + + gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geo.buffer_id_indices); + win32_gl_no_error(); +} + +void +platform_shader_bind(Platform_Shader shader) +{ + gl.glUseProgram(shader.id); + win32_gl_no_error(); + for (u32 i = 0; + ((i < PLATFORM_SHADER_MAX_ATTRS) && (shader.attrs[i] != PLATFORM_SHADER_ATTR_LAST)); + i++) + { + gl.glEnableVertexAttribArray(shader.attrs[i]); + win32_gl_no_error(); + } +} + +void +platform_geometry_draw( + Platform_Geometry_Buffer geo + ){ + glDrawElements(GL_TRIANGLES, geo.indices_len, GL_UNSIGNED_INT, 0); + win32_gl_no_error(); +} + +void platform_vertex_attrib_pointer( + Platform_Geometry_Buffer geo, Platform_Shader shader, u32 attr_index + ){ + platform_geometry_bind(geo); + gl.glVertexAttribPointer(shader.attrs[attr_index], 4, GL_FLOAT, false, 0, 0); + win32_gl_no_error(); + gl.glEnableVertexAttribArray(shader.attrs[attr_index]); + win32_gl_no_error(); +} + + +void +platform_frame_begin(Platform_Graphics_Frame_Desc desc) +{ + v4 cc = desc.clear_color; + glClearColor(cc.r, cc.g, cc.b, cc.a); + + v2 vmin = desc.viewport_min; + v2 vmax = desc.viewport_max; + glViewport((GLsizei)vmin.x, (GLsizei)vmin.y, (GLint)vmax.x, (GLint)vmax.y); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDisable(GL_CULL_FACE); + + //glDisable(GL_TEXTURE_2D); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); +} + +void +platform_frame_clear() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} \ No newline at end of file diff --git a/src_v2/platform/win32/lumenarium_win32_opengl.h b/src_v2/platform/win32/lumenarium_win32_opengl.h new file mode 100644 index 0000000..9ad2cee --- /dev/null +++ b/src_v2/platform/win32/lumenarium_win32_opengl.h @@ -0,0 +1,80 @@ +/* date = March 24th 2022 6:05 pm */ + +#ifndef LUMENARIUM_WIN32_OPENGL_H +#define LUMENARIUM_WIN32_OPENGL_H + +// glext.h - https://github.com/KhronosGroup/OpenGL-Registry/blob/main/api/GL/glext.h +// wglext.h - + +// OpenGL 3.3+ Context Creation +typedef const char *WINAPI proc_wglGetExtensionsStringARB(HDC hdc); +typedef BOOL proc_wglChoosePixelFormatARB(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +typedef HGLRC proc_wglCreateContextAttribsARB(HDC hDC, HGLRC hshareContext, const int *attribList); + +// OpenGL 3.3+ Extensions +typedef void proc_glGenVertexArrays(GLsizei n, GLuint *arrays); +typedef void proc_glBindVertexArray(GLuint array); +typedef void proc_glGenBuffers (GLsizei n, GLuint *buffers); +typedef void proc_glBindBuffer(GLenum target, GLuint buffer); +typedef void proc_glBufferData(GLenum target, size_t size, const void *data, GLenum usage); +typedef GLuint proc_glCreateShader(GLenum type); +typedef void proc_glShaderSource(GLuint shader, u32 count, const char* const* string, const GLint *length); +typedef void proc_glCompileShader(GLuint shader); +typedef GLuint proc_glCreateProgram(void); +typedef void proc_glAttachShader(GLuint program, GLuint shader); +typedef void proc_glLinkProgram(GLuint program); +typedef void proc_glUseProgram(GLuint program); +typedef GLuint proc_glGetAttribLocation(GLuint program, const char* name); +typedef void proc_glVertexAttribPointer(GLuint attr, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +typedef void proc_glEnableVertexAttribArray(GLuint index); +typedef void proc_glGetShaderiv(GLuint shader, GLenum ele, GLint* value_out); +typedef void proc_glGetShaderInfoLog(GLuint shader, GLuint buf_len, GLsizei* len_out, GLchar* buf); +typedef void proc_glGetProgramiv(GLuint program, GLenum ele, GLint* value_out); +typedef void proc_glGetProgramInfoLog(GLuint program, GLuint cap, GLsizei* len_out, GLchar* buf); + +struct Win32_OpenGL_Extensions +{ + proc_wglGetExtensionsStringARB* wglGetExtensionsStringARB; + proc_wglChoosePixelFormatARB* wglChoosePixelFormatARB; + proc_wglCreateContextAttribsARB* wglCreateContextAttribsARB; + + proc_glGenVertexArrays* glGenVertexArrays; + proc_glBindVertexArray* glBindVertexArray; + proc_glGenBuffers* glGenBuffers; + proc_glBindBuffer* glBindBuffer; + proc_glBufferData* glBufferData; + proc_glCreateShader* glCreateShader; + proc_glShaderSource* glShaderSource; + proc_glCompileShader* glCompileShader; + proc_glCreateProgram* glCreateProgram; + proc_glAttachShader* glAttachShader; + proc_glLinkProgram* glLinkProgram; + proc_glUseProgram* glUseProgram; + proc_glGetAttribLocation* glGetAttribLocation; + proc_glVertexAttribPointer* glVertexAttribPointer; + proc_glEnableVertexAttribArray* glEnableVertexAttribArray; + proc_glGetShaderiv* glGetShaderiv; + proc_glGetShaderInfoLog* glGetShaderInfoLog; + proc_glGetProgramiv* glGetProgramiv; + proc_glGetProgramInfoLog* glGetProgramInfoLog; +}; + +//////////////////////////////////////// +// error strings + +#define WIN32_GL_ERROR_CASE(code) case (code): { result = #code; } break + +char* +win32_gl_error_to_string(u32 error) +{ + char* result = 0; + switch (error) + { + WIN32_GL_ERROR_CASE(GL_INVALID_VALUE); + WIN32_GL_ERROR_CASE(GL_INVALID_ENUM ); + WIN32_GL_ERROR_CASE(GL_INVALID_OPERATION); + default: { result = "unknown"; } + } + return result; +} +#endif //LUMENARIUM_WIN32_OPENGL_H diff --git a/src_v2/platform/win32/lumenarium_win32_thread.cpp b/src_v2/platform/win32/lumenarium_win32_thread.cpp index 2285b4c..0fdf3ed 100644 --- a/src_v2/platform/win32/lumenarium_win32_thread.cpp +++ b/src_v2/platform/win32/lumenarium_win32_thread.cpp @@ -76,7 +76,11 @@ void platform_thread_end(Platform_Thread_Handle thread) { HANDLE thread_handle = win32_threads[thread.value]; - TerminateThread(thread_handle, 0); + bool result = TerminateThread(thread_handle, 0); + if (!result) { + win32_get_last_error(); + invalid_code_path; + } win32_threads[thread.value] = INVALID_HANDLE_VALUE; } diff --git a/src_v2/platform/win32/lumenarium_win32_window.cpp b/src_v2/platform/win32/lumenarium_win32_window.cpp index 2241d0e..cf37008 100644 --- a/src_v2/platform/win32/lumenarium_win32_window.cpp +++ b/src_v2/platform/win32/lumenarium_win32_window.cpp @@ -15,18 +15,21 @@ struct Win32_Window_OpenGL_Info HGLRC rc; }; -struct Win32_Window +struct Win32_Window_Info { char* name; char* class_name; s32 width; s32 height; - - WNDCLASS window_class; +}; + +struct Win32_Window +{ + Win32_Window_Info info; + WNDCLASSEX window_class; WNDPROC window_event_handler; HWND window_handle; HDC dc; - Win32_Window_OpenGL_Info opengl_info; }; @@ -42,8 +45,9 @@ global Win32_Window win32_main_window = {}; ////////////////////////////////////////// // -internal Win32_Window +internal bool win32_window_create( + Win32_Window* dest, HINSTANCE hinstance, char* window_name, s32 width, @@ -51,39 +55,51 @@ win32_window_create( 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; + dest->info.name = window_name; + dest->info.class_name = window_name; + dest->info.width = width; + dest->info.height = height; - 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; + dest->window_event_handler = window_event_handler; - if (RegisterClass(&result.window_class)) + dest->window_class = {}; + dest->window_class.cbSize = sizeof(WNDCLASSEX); + dest->window_class.style = ( + CS_HREDRAW | + CS_VREDRAW | + CS_OWNDC // TODO(PS): need to know what this is + ); + dest->window_class.lpfnWndProc = window_event_handler; + dest->window_class.cbClsExtra = 0; + dest->window_class.cbWndExtra = 0; + dest->window_class.hInstance = hinstance; + dest->window_class.hIcon = NULL; + dest->window_class.hCursor = NULL; + dest->window_class.hbrBackground = NULL; + dest->window_class.lpszMenuName = 0; + dest->window_class.lpszClassName = window_name; // "main_window_class"; + dest->window_class.hIconSm = NULL; + + if (RegisterClassEx(&dest->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); + dest->window_handle = CreateWindowEx( + 0, + dest->window_class.lpszClassName, + window_name, + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, + CW_USEDEFAULT, + width, + height, + 0, + 0, + hinstance, + 0 + ); + + return true; } - - return result; + return false; } internal void @@ -91,10 +107,12 @@ 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; + win->info.width = client_rect.right - client_rect.left; + win->info.height = client_rect.bottom - client_rect.top; } +internal void win32_window_opengl_ctx_create(Win32_Window* win, Win32_Window_OpenGL_Info info, HINSTANCE hinstance); + LRESULT CALLBACK win32_window_event_handler(HWND window_handle, UINT msg, WPARAM wparam, LPARAM lparam) { @@ -102,6 +120,13 @@ win32_window_event_handler(HWND window_handle, UINT msg, WPARAM wparam, LPARAM l switch (msg) { + case WM_CREATE: + { + win32_main_window.dc = GetDC(window_handle); + HINSTANCE hinstance = GetModuleHandle(NULL); + win32_window_opengl_ctx_create(&win32_main_window, { 32, 8, 0 }, hinstance); + }break; + case WM_SIZE: { win32_window_update_dim(&win32_main_window); @@ -109,12 +134,38 @@ win32_window_event_handler(HWND window_handle, UINT msg, WPARAM wparam, LPARAM l case WM_CLOSE: { - result = DefWindowProc(window_handle, msg, wparam, lparam); + if (win32_main_window.opengl_info.rc) + { + wglDeleteContext(win32_main_window.opengl_info.rc); + win32_main_window.opengl_info.rc = NULL; + } + if (win32_main_window.dc) + { + ReleaseDC(win32_main_window.window_handle, win32_main_window.dc); + win32_main_window.dc = NULL; + } + add_flag(win32_window_event_flags, WindowEventFlag_CloseRequested); + running = false; + + DestroyWindow(win32_main_window.window_handle); }break; case WM_DESTROY: { + if (win32_main_window.opengl_info.rc) + { + wglDeleteContext(win32_main_window.opengl_info.rc); + win32_main_window.opengl_info.rc = NULL; + } + if (win32_main_window.dc) + { + ReleaseDC(win32_main_window.window_handle, win32_main_window.dc); + win32_main_window.dc = NULL; + } + + //PostQuitMessage(0); + //result = DefWindowProc(window_handle, msg, wparam, lparam); }break; case WM_PAINT: @@ -151,14 +202,31 @@ win32_window_event_handler(HWND window_handle, UINT msg, WPARAM wparam, LPARAM l return result; } +//////////////////////////////////////////// +// OpenGL dummy window functions + +LRESULT CALLBACK +win32_opengl_event_handler(HWND window_handle, UINT msg, WPARAM wparam, LPARAM lparam) +{ + LRESULT result = 0; + + switch (msg) + { + case WM_CREATE: { }break; + case WM_CLOSE: { DestroyWindow(window_handle); }break; + case WM_DESTROY: { } break; + default: { return DefWindowProc(window_handle, msg, wparam, lparam); } break; + } + + return result; +} + internal void -win32_window_opengl_ctx_create(Win32_Window* win, Win32_Window_OpenGL_Info info) +win32_window_opengl_ctx_create_no_ext(HDC dc, 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 = ( @@ -166,38 +234,191 @@ win32_window_opengl_ctx_create(Win32_Window* win, Win32_Window_OpenGL_Info info) 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.cColorBits = info->bits_color; + pixel_format_desc.cAlphaBits = info->bits_alpha; + pixel_format_desc.cDepthBits = info->bits_depth; 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)) + s32 pixel_fmt = ChoosePixelFormat(dc, &pixel_format_desc); + if (pixel_fmt == 0) + { + win32_get_last_error(); + invalid_code_path; + } + if ((pixel_fmt = ChoosePixelFormat(dc, &pixel_format_desc)) == 0) + { + win32_get_last_error(); + invalid_code_path; + } + if (SetPixelFormat(dc, pixel_fmt, &pixel_format_desc) == FALSE) { + win32_get_last_error(); 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); - */ + info->rc = wglCreateContext(dc); + if (info->rc == NULL) { + win32_get_last_error(); + invalid_code_path; + } + if (!wglMakeCurrent(dc, info->rc)) + { + win32_get_last_error(); + invalid_code_path; + } + } +} + +// Based on documentation at: +// https://www.opengl.org/wiki/Creating_an_OpenGL_Context_(WGL)#Proper_Context_Creation +internal void +win32_window_opengl_ctx_create_ext(HDC dc, Win32_Window_OpenGL_Info* info) +{ + const int pixel_fmt_attribs[] = { + WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, + WGL_SUPPORT_OPENGL_ARB, GL_TRUE, + WGL_DOUBLE_BUFFER_ARB, GL_TRUE, + WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, + WGL_COLOR_BITS_ARB, 32, + WGL_DEPTH_BITS_ARB, 24, + WGL_STENCIL_BITS_ARB, 8, + 0, 0 + }; + + s32 pixel_fmt; + u32 num_formats; + + // this will contain the description of pixel_fmt after we set it + PIXELFORMATDESCRIPTOR pixel_format_desc = { 0 }; + + if (!gl.wglChoosePixelFormatARB(dc, pixel_fmt_attribs, NULL, 1, &pixel_fmt, &num_formats)) + { + win32_get_last_error(); + invalid_code_path; + } + if (SetPixelFormat(dc, pixel_fmt, &pixel_format_desc) == FALSE) + { + win32_get_last_error(); + invalid_code_path; } + int ctx_flags = WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + +#if defined(DEBUG) + ctx_flags |= WGL_CONTEXT_DEBUG_BIT_ARB; +#endif + + const int ctx_attribs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 3, + WGL_CONTEXT_FLAGS_ARB, ctx_flags, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + + 0, 0, + }; + + info->rc = gl.wglCreateContextAttribsARB(dc, 0, ctx_attribs); + if (info->rc == NULL) { + win32_get_last_error(); + invalid_code_path; + } + if (!wglMakeCurrent(dc, info->rc)) + { + win32_get_last_error(); + invalid_code_path; + } + +#if 0 + //char* version_string = (char*)glGetString(GL_VERSION); + OutputDebugStringA("OpenGL Version: "); + OutputDebugStringA(version_string); + OutputDebugStringA("\n"); +#endif +} + +internal void +win32_window_opengl_make_current(Win32_Window* win) +{ + +} + +#define wgl_load_ext(e,n) e.n = (proc_##n*)wglGetProcAddress(#n); assert((e.n) != 0) + +internal Win32_OpenGL_Extensions +win32_window_opengl_get_wgl_ext(HINSTANCE hinstance) +{ + Win32_OpenGL_Extensions result = {0}; + + WNDCLASSEX window_class = {}; + window_class.cbSize = sizeof(WNDCLASSEX); + window_class.style = (CS_HREDRAW | CS_VREDRAW | CS_OWNDC); + window_class.lpfnWndProc = win32_opengl_event_handler; + window_class.hInstance = hinstance; + window_class.lpszClassName = "opengl_window_class"; + if (RegisterClassEx(&window_class)) + { + HWND window_handle = CreateWindowEx(0, window_class.lpszClassName, + "opengl_window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, 32, 32, 0, 0, hinstance, 0 + ); + + HDC dc = GetDC(window_handle); + Win32_Window_OpenGL_Info info = { 32, 24, 8 }; + win32_window_opengl_ctx_create_no_ext(dc, &info); + + wgl_load_ext(result, wglGetExtensionsStringARB); + if (result.wglGetExtensionsStringARB != 0) + { + const char* extension_string = result.wglGetExtensionsStringARB(dc); + OutputDebugStringA("OpenGL Extensions: \n"); + OutputDebugStringA(extension_string); + OutputDebugStringA("\n\n"); + + wgl_load_ext(result, wglChoosePixelFormatARB); + wgl_load_ext(result, wglCreateContextAttribsARB); + wgl_load_ext(result, glGenVertexArrays); + wgl_load_ext(result, glBindVertexArray); + wgl_load_ext(result, glGenBuffers); + wgl_load_ext(result, glBindBuffer); + wgl_load_ext(result, glBufferData); + wgl_load_ext(result, glCreateShader); + wgl_load_ext(result, glShaderSource); + wgl_load_ext(result, glCompileShader); + wgl_load_ext(result, glCreateProgram); + wgl_load_ext(result, glAttachShader); + wgl_load_ext(result, glLinkProgram); + wgl_load_ext(result, glUseProgram); + wgl_load_ext(result, glGetAttribLocation); + wgl_load_ext(result, glVertexAttribPointer); + wgl_load_ext(result, glEnableVertexAttribArray); + wgl_load_ext(result, glGetShaderiv); + wgl_load_ext(result, glGetShaderInfoLog); + wgl_load_ext(result, glGetProgramiv); + wgl_load_ext(result, glGetProgramInfoLog); + } + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(info.rc); + ReleaseDC(window_handle, dc); + DestroyWindow(window_handle); + } + + return result; +} + +internal void +win32_window_opengl_ctx_create(Win32_Window* win, Win32_Window_OpenGL_Info info, HINSTANCE hinstance) +{ + if (gl.wglGetExtensionsStringARB == 0) + { + gl = win32_window_opengl_get_wgl_ext(hinstance); + } + + win32_window_opengl_ctx_create_ext(win->dc, &info); win->opengl_info = info; } diff --git a/src_v2/platform/win32/test.cpp b/src_v2/platform/win32/test.cpp new file mode 100644 index 0000000..f4c32c1 --- /dev/null +++ b/src_v2/platform/win32/test.cpp @@ -0,0 +1,423 @@ +/* + * Example of a Windows OpenGL program. + * The OpenGL code is the same as that used in + * the X Window System sample + */ +#include +#include +//#include + +/* Windows globals, defines, and prototypes */ +CHAR szAppName[]="Win OpenGL"; +HWND ghWnd; +HDC ghDC; +HGLRC ghRC; + +#define SWAPBUFFERS SwapBuffers(ghDC) +#define BLACK_INDEX 0 +#define RED_INDEX 13 +#define GREEN_INDEX 14 +#define BLUE_INDEX 16 +#define WIDTH 300 +#define HEIGHT 200 + +LONG WINAPI MainWndProc (HWND, UINT, WPARAM, LPARAM); +BOOL bSetupPixelFormat(HDC); + +/* OpenGL globals, defines, and prototypes */ +GLfloat latitude, longitude, latinc, longinc; +GLdouble radius; + +#define GLOBE 1 +#define CYLINDER 2 +#define CONE 3 + +GLvoid resize(GLsizei, GLsizei); +GLvoid initializeGL(GLsizei, GLsizei); +GLvoid drawScene(GLvoid); +void polarView( GLdouble, GLdouble, GLdouble, GLdouble); + +int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + MSG msg; + WNDCLASS wndclass; + + /* Register the frame class */ + wndclass.style = 0; + wndclass.lpfnWndProc = (WNDPROC)MainWndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon (hInstance, szAppName); + wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); + wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wndclass.lpszMenuName = szAppName; + wndclass.lpszClassName = szAppName; + + DWORD r0 = RegisterClass (&wndclass); + if (!r0) return FALSE; + + /* Create the frame */ + ghWnd = CreateWindow (szAppName, + "Generic OpenGL Sample", + WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE, + CW_USEDEFAULT, + CW_USEDEFAULT, + WIDTH, + HEIGHT, + NULL, + NULL, + hInstance, + NULL); + + /* make sure window was created */ + if (!ghWnd) + return FALSE; + + /* show and update main window */ + ShowWindow (ghWnd, nCmdShow); + + UpdateWindow (ghWnd); + + /* animation loop */ + while (1) { + /* + * Process all pending messages + */ + + while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) == TRUE) + { + if (GetMessage(&msg, NULL, 0, 0) ) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + return TRUE; + } + } + drawScene(); + } +} + +/* main window procedure */ +LONG WINAPI MainWndProc ( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + LONG lRet = 1; + PAINTSTRUCT ps; + RECT rect; + + switch (uMsg) { + + case WM_CREATE: + ghDC = GetDC(hWnd); + if (!bSetupPixelFormat(ghDC)) + PostQuitMessage (0); + + ghRC = wglCreateContext(ghDC); + wglMakeCurrent(ghDC, ghRC); + GetClientRect(hWnd, &rect); + initializeGL(rect.right, rect.bottom); + break; + + case WM_PAINT: + BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + break; + + case WM_SIZE: + GetClientRect(hWnd, &rect); + resize(rect.right, rect.bottom); + break; + + case WM_CLOSE: + if (ghRC) + wglDeleteContext(ghRC); + if (ghDC) + ReleaseDC(hWnd, ghDC); + ghRC = 0; + ghDC = 0; + + DestroyWindow (hWnd); + break; + + case WM_DESTROY: + if (ghRC) + wglDeleteContext(ghRC); + if (ghDC) + ReleaseDC(hWnd, ghDC); + + PostQuitMessage (0); + break; + + case WM_KEYDOWN: + switch (wParam) { + case VK_LEFT: + longinc += 0.5F; + break; + case VK_RIGHT: + longinc -= 0.5F; + break; + case VK_UP: + latinc += 0.5F; + break; + case VK_DOWN: + latinc -= 0.5F; + break; + } + + default: + lRet = (LONG)DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + return lRet; +} + +BOOL bSetupPixelFormat(HDC hdc) +{ + PIXELFORMATDESCRIPTOR pfd, *ppfd; + int pixelformat; + + ppfd = &pfd; + + ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR); + ppfd->nVersion = 1; + ppfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | + PFD_DOUBLEBUFFER; + ppfd->dwLayerMask = PFD_MAIN_PLANE; + ppfd->iPixelType = PFD_TYPE_COLORINDEX; + ppfd->cColorBits = 8; + ppfd->cDepthBits = 16; + ppfd->cAccumBits = 0; + ppfd->cStencilBits = 0; + + pixelformat = ChoosePixelFormat(hdc, ppfd); + + if ( (pixelformat = ChoosePixelFormat(hdc, ppfd)) == 0 ) + { + MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK); + return FALSE; + } + + if (SetPixelFormat(hdc, pixelformat, ppfd) == FALSE) + { + MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK); + return FALSE; + } + + return TRUE; +} + +/* OpenGL code */ + +GLvoid resize( GLsizei width, GLsizei height ) +{ + GLfloat aspect; + + glViewport( 0, 0, width, height ); + + aspect = (GLfloat) width / height; + + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + //gluPerspective( 45.0, aspect, 3.0, 7.0 ); + //glMatrixMode( GL_MODELVIEW ); +} + +#if 0 +GLvoid createObjects() +{ + GLUquadricObj *quadObj; + + glNewList(GLOBE, GL_COMPILE); + quadObj = gluNewQuadric (); + gluQuadricDrawStyle (quadObj, GLU_LINE); + gluSphere (quadObj, 1.5, 16, 16); + glEndList(); + + glNewList(CONE, GL_COMPILE); + quadObj = gluNewQuadric (); + gluQuadricDrawStyle (quadObj, GLU_FILL); + gluQuadricNormals (quadObj, GLU_SMOOTH); + gluCylinder(quadObj, 0.3, 0.0, 0.6, 15, 10); + glEndList(); + + glNewList(CYLINDER, GL_COMPILE); + glPushMatrix (); + glRotatef ((GLfloat)90.0, (GLfloat)1.0, (GLfloat)0.0, (GLfloat)0.0); + glTranslatef ((GLfloat)0.0, (GLfloat)0.0, (GLfloat)-1.0); + quadObj = gluNewQuadric (); + gluQuadricDrawStyle (quadObj, GLU_FILL); + gluQuadricNormals (quadObj, GLU_SMOOTH); + gluCylinder (quadObj, 0.3, 0.3, 0.6, 12, 2); + glPopMatrix (); + glEndList(); +} +#endif + +GLvoid initializeGL(GLsizei width, GLsizei height) +{ + GLfloat maxObjectSize, aspect; + GLdouble near_plane, far_plane; + + glClearIndex( (GLfloat)BLACK_INDEX); + glClearDepth( 1.0 ); + + glEnable(GL_DEPTH_TEST); + + glMatrixMode( GL_PROJECTION ); + aspect = (GLfloat) width / height; + //gluPerspective( 45.0, aspect, 3.0, 7.0 ); + glMatrixMode( GL_MODELVIEW ); + + near_plane = 3.0; + far_plane = 7.0; + maxObjectSize = 3.0F; + radius = near_plane + maxObjectSize/2.0; + + latitude = 0.0F; + longitude = 0.0F; + latinc = 6.0F; + longinc = 2.5F; + + //createObjects(); +} + +void polarView(GLdouble radius_, GLdouble twist, GLdouble latitude_, + GLdouble longitude_) +{ + glTranslated(0.0, 0.0, -radius_); + glRotated(-twist, 0.0, 0.0, 1.0); + glRotated(-latitude_, 1.0, 0.0, 0.0); + glRotated(longitude_, 0.0, 0.0, 1.0); + +} + +GLvoid drawScene(GLvoid) +{ + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + glPushMatrix(); + + latitude += latinc; + longitude += longinc; + + polarView( radius, 0, latitude, longitude ); + + glIndexi(RED_INDEX); + glCallList(CONE); + + glIndexi(BLUE_INDEX); + glCallList(GLOBE); + + glIndexi(GREEN_INDEX); + glPushMatrix(); + glTranslatef(0.8F, -0.65F, 0.0F); + glRotatef(30.0F, 1.0F, 0.5F, 1.0F); + glCallList(CYLINDER); + glPopMatrix(); + + glPopMatrix(); + + SWAPBUFFERS; +} + +#if 0 +#include "windows.h" +#include + +# define assert_always (*((volatile int*)0) = 0xFFFF) + +LRESULT CALLBACK +window_event_handler(HWND h, UINT m, WPARAM w, LPARAM l) +{ + switch (m) + { + case WM_CLOSE: { return DefWindowProc(h, m, w, l); } break; + case WM_DESTROY: { return DefWindowProc(h, m, w, l); } break; + default: { return DefWindowProc(h, m, w, l); } break; + } +} + +INT WINAPI +WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + PSTR lpCmdLine, + INT nCmdShow) +{ + WNDCLASSEX window_class = {0}; + HWND window_handle = NULL; + HDC dc = NULL; + HGLRC rc = NULL; + + window_class = {}; + window_class.cbSize = sizeof(WNDCLASSEX); + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.lpfnWndProc = window_event_handler; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = hInstance; + window_class.hIcon = NULL; + window_class.hCursor = NULL; + window_class.hbrBackground = NULL; + window_class.lpszMenuName = 0; + window_class.lpszClassName = "my_window_class"; // TODO(PS): does this need to be different from the window itself? + window_class.hIconSm = NULL; + + if (RegisterClassEx(&window_class)) + { + window_handle = CreateWindowEx( + 0, + window_class.lpszClassName, + "my_window", + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, + CW_USEDEFAULT, + 600, + 400, + 0, + 0, + hInstance, + 0 + ); + dc = GetDC(window_handle); + } + else + { + assert_always; + } + + // Setup pixel format + PIXELFORMATDESCRIPTOR pixel_format_desc = { 0 }; + 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 = 32; + pixel_format_desc.cAlphaBits = 8; + pixel_format_desc.cDepthBits = 0; + pixel_format_desc.iPixelType = PFD_TYPE_RGBA; + pixel_format_desc.dwLayerMask = PFD_MAIN_PLANE; + + int pixel_fmt = ChoosePixelFormat(dc, &pixel_format_desc); + if (!pixel_fmt) { assert_always; } + if (!SetPixelFormat(dc, pixel_fmt, &pixel_format_desc)) + { + assert_always; + } + + // Create rendering context + rc = wglCreateContext(dc); + wglMakeCurrent(dc, rc); + + return 0; +} +#endif \ No newline at end of file diff --git a/src_v2/user_space/user_space_incenter.cpp b/src_v2/user_space/user_space_incenter.cpp new file mode 100644 index 0000000..180161b --- /dev/null +++ b/src_v2/user_space/user_space_incenter.cpp @@ -0,0 +1,57 @@ + +internal App_Init_Desc +incenter_get_init_desc() +{ + App_Init_Desc result = {}; + result.assembly_cap = 4; + return result; +} + +internal void +incenter_init(App_State* state) +{ + return; + + // create a fake sculpture + Assembly_Handle ah = assembly_add(&state->assemblies, lit_str("test"), 3000, 100); + + r32 scale = 1; + + // strips + for (int strip_x = 0; strip_x < 10; strip_x++) + { + for (int strip_y = 0; strip_y < 10; strip_y++) + { + if (strip_x == 5 && strip_y == 7) + { + int x= 5; + } + Assembly_Strip* strip = assembly_add_strip(&state->assemblies, ah, 30); + + // leds go up + for (int led_z = 0; led_z < 30; led_z++) + { + v4 pos = { strip_x * scale, strip_y * scale, led_z * scale, 1 }; + assembly_add_led(&state->assemblies, ah, strip, pos); + } + } + } +} + +internal void +incenter_frame_prepare(App_State* state) +{ + +} + +internal void +incenter_frame(App_State* state) +{ + +} + +internal void +incenter_cleanup(App_State* state) +{ + +} \ No newline at end of file From 6775ea26d3db64575143ac2cc0dde08740df7b52 Mon Sep 17 00:00:00 2001 From: PS Date: Tue, 29 Mar 2022 18:09:50 +0200 Subject: [PATCH 117/151] textures, window fixes --- build/build.sh | 3 +- build/build_.sh | 2 + run_tree/wasm/debug/lumenarium.wasm | Bin 49390 -> 57484 bytes .../wasm/debug/lumenarium_wasm_imports.js | 209 ++++++++++++++-- run_tree/win32/intel/debug/debug.rdbg | Bin 1349 -> 1206 bytes run_tree/win32/intel/debug/err.txt | 1 + .../intel/debug/lumenarium_first_win32.obj | Bin 134583 -> 160688 bytes src/app/editor/foldhaus_editor.cpp | 236 +++++++++--------- src_v2/editor/lumenarium_editor.cpp | 102 +++++++- src_v2/editor/lumenarium_editor.h | 12 + src_v2/editor/lumenarium_editor_renderer.cpp | 87 +------ src_v2/editor/lumenarium_editor_renderer.h | 4 +- src_v2/editor/lumenarium_ui.h | 36 +++ src_v2/lumenarium_first.cpp | 97 +------ src_v2/lumenarium_first.h | 5 +- src_v2/lumenarium_string.cpp | 3 +- src_v2/platform/lumenarium_assert.h | 28 ++- src_v2/platform/lumenarium_platform.h | 20 +- .../lumenarium_platform_common_includes.h | 10 +- .../platform/wasm/lumenarium_first_wasm.cpp | 17 +- .../platform/wasm/lumenarium_wasm_imports.js | 209 ++++++++++++++-- .../platform/wasm/lumenarium_wasm_webgl.cpp | 94 ++++++- .../platform/win32/lumenarium_first_win32.cpp | 14 ++ .../win32/lumenarium_win32_graphics.cpp | 85 ++++++- .../win32/lumenarium_win32_window.cpp | 9 +- 25 files changed, 899 insertions(+), 384 deletions(-) create mode 100644 run_tree/win32/intel/debug/err.txt create mode 100644 src_v2/editor/lumenarium_editor.h create mode 100644 src_v2/editor/lumenarium_ui.h diff --git a/build/build.sh b/build/build.sh index 9be186f..d2ffa2d 100644 --- a/build/build.sh +++ b/build/build.sh @@ -1,2 +1,3 @@ SCRIPT_REL_DIR=$(dirname "${BASH_SOURCE[0]}") -$SCRIPT_REL_DIR/build_.sh debug win32 intel \ No newline at end of file +$SCRIPT_REL_DIR/build_.sh debug win32 intel +$SCRIPT_REL_DIR/build_.sh debug wasm intel \ No newline at end of file diff --git a/build/build_.sh b/build/build_.sh index b70899a..b7d0978 100644 --- a/build/build_.sh +++ b/build/build_.sh @@ -126,6 +126,7 @@ add_flag CompilerFlags_win32 "-wd4505" # add_flag CompilerFlags_win32 "-wd4100" # add_flag CompilerFlags_win32 "-wd4189" # add_flag CompilerFlags_win32 "-wd4702" # +add_flag CompilerFlags_win32 "-wd4996" # _CRT_SECURE_NO_WARNINGS CompilerFlags_osx="" @@ -143,6 +144,7 @@ CompilerFlags_DEBUG_win32="" add_flag CompilerFlags_DEBUG_win32 "-Od" # add_flag CompilerFlags_DEBUG_win32 "-Zi" # add_flag CompilerFlags_DEBUG_win32 "-DDEBUG" # +add_flag CompilerFlags_DEBUG_win32 "-DPRINT_ASSERTS" CompilerFlags_DEBUG="-O0" add_flag CompilerFlags_DEBUG "-g" # diff --git a/run_tree/wasm/debug/lumenarium.wasm b/run_tree/wasm/debug/lumenarium.wasm index 898b757ab1bdd748ff42ca3a34a8134640ba3f36..2960abaa7bee9e5c533b9753267595ea916b789b 100644 GIT binary patch delta 28617 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 8 ) & 0xff); + result[2] = (((v - result[1]) >> 16) & 0xff); + result[3] = (((v - result[2]) >> 24) & 0xff); + return result; +} + +function byte_array_32_to_u32 (arr) +{ + // NOTE(PS): the '>>>' operators in this function deal with the fact + // that bit shift operators convert numbers to s32's. The >>> just + // converts them back to u32s + let r0 = ((arr[0] & 0xff) << 0 ); + let r1 = ((arr[1] & 0xff) << 8 ); + let r2 = ((arr[2] & 0xff) << 16); + let r3 = (((arr[3] & 0xff) << 24) >>> 0); + let result = (r0 | r1 | r2 | r3) >>> 0; + return result; +} + +function put_u32 (ptr, value) +{ + let src = u32_to_byte_array_32(value); + wasm_write_bytes(lumenarium_wasm_instance, src, ptr, 4); +} + var lumenarium_wasm_imports = { memset: (dst, size, value) => { @@ -122,12 +151,46 @@ var lumenarium_wasm_imports = { let string = wasm_read_string(lumenarium_wasm_instance, str_base, len); console.log(string); }, + + wasm_get_canvas_dim: (w_ptr, h_ptr) => { + const canvas = document.querySelector("#gl_canvas"); + + let w_view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, w_ptr, 4); + let w = canvas.width; + let wb = u32_to_byte_array_32(w); + for (let i = 0; i < 4; i++) w_view[i] = wb[i]; + + let h_view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, h_ptr, 4); + let h = canvas.height; + let hb = u32_to_byte_array_32(h); + for (let i = 0; i < 4; i++) h_view[i] = hb[i]; + }, }; /////////////////////////////////////// // Web GL Imports let gl = null; +let gl_error = false; + +function glErrorReport(outer_args) { + const err = gl.getError(); + if (err == gl.NO_ERROR) return; + + gl_error = true; + let msg = ""; + switch (err) { + case gl.NO_ERROR: { msg = "NO_ERROR"; } break; + case gl.INVALID_ENUM: { msg = "INVALID_ENUM"; } break; + case gl.INVALID_VALUE: { msg = "INVALID_VALUE"; } break; + case gl.INVALID_OPERATION: { msg = "INVALID_OPERATION"; } break; + case gl.INVALID_FRAMEBUFFER_OPERATION: { msg = "INVALID_FRAMEBUFFER_OPERATION"; } break; + case gl.OUT_OF_MEMORY: { msg = "OUT_OF_MEMORY"; } break; + case gl.CONTEXT_LOST_WEBGL: { msg = "CONTEXT_LOST_WEBGL"; } break; + default: { msg = "Uknown error"; } break; + } + console.error(`WebGL Error: ${msg} ${err}`, outer_args); +} // NOTE(PS): it seems like its not enough to set // the values of imports to gl.function @@ -135,16 +198,37 @@ let gl = null; // instead we need to wrap them for some reason. // Not sure why function glClearColor (r, g, b, a) { return gl.clearColor(r,g,b,a); } -function glEnable(v) { return gl.enable(v); } -function glDisable(v) { return gl.disable(v); } -function glBlendFunc(a,b) { return gl.blendFunc(a,b); } +function glEnable(v) { + const r = gl.enable(v); + glErrorReport(arguments); + return r; +} +function glDisable(v) { + const r = gl.disable(v); + glErrorReport(arguments); + return r; +} +function glBlendFunc(a,b) { + const r = gl.blendFunc(a,b); + glErrorReport(arguments); + return r; +} function glViewport(xmin, ymin, xmax, ymax) { return gl.viewport(xmin,ymin,xmax,ymax); } -function glDepthFunc(v) { return gl.depthFunc(v); } -function glClear(mask) { return gl.clear(mask); } +function glDepthFunc(v) { + const r = gl.depthFunc(v); + glErrorReport(arguments); + return r; +} +function glClear(mask) { + const r = gl.clear(mask); + glErrorReport(arguments); + return r; +} let glBuffers = []; let glShaders = []; let glPrograms = []; +let glTextures = []; function gl_get_managed_resource(arr, id) { if (id == 0) return null; return arr[id - 1]; @@ -152,55 +236,76 @@ function gl_get_managed_resource(arr, id) { function gl_get_buffer(id) { return gl_get_managed_resource(glBuffers, id); } function gl_get_shader(id) { return gl_get_managed_resource(glShaders, id); } function gl_get_program(id) { return gl_get_managed_resource(glPrograms, id); } +function gl_get_texture(id) { return gl_get_managed_resource(glTextures, id); } function glCreateBuffer() { let buffer = gl.createBuffer(); + glErrorReport(arguments); let new_len = glBuffers.push(buffer); return new_len; } + function glBindBuffer(buffer_kind, buffer_id) { - return gl.bindBuffer(buffer_kind, gl_get_buffer(buffer_id)); + const r = gl.bindBuffer(buffer_kind, gl_get_buffer(buffer_id)); + glErrorReport(arguments); + return r; } + function glBufferData(target, size, ptr, usage) { let data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, size); - return gl.bufferData(target, data, usage); + const r = gl.bufferData(target, data, usage); + glErrorReport(arguments); + return r; } + function glCreateShader(kind) { let shader = gl.createShader(kind); + glErrorReport(arguments); let new_len = glShaders.push(shader); return new_len; } + function glShaderSource(shader_id, shader_code, shader_code_len) { let str = wasm_read_string(lumenarium_wasm_instance, shader_code, shader_code_len); - console.error("For some reason, str isn't getting the correct data out of here", str); - return gl.shaderSource(gl_get_shader(shader_id), str); + const r = gl.shaderSource(gl_get_shader(shader_id), str); + glErrorReport(arguments); + return r; } + function glCompileShader(shader_id) { let s = gl_get_shader(shader_id); let r = gl.compileShader(s); + glErrorReport(arguments); let m = gl.getShaderInfoLog(s); + glErrorReport(arguments); if (m.length > 0) { console.error("glCompileShader: \n\n" + m); } } + function glCreateProgram() { let prog = gl.createProgram(); + glErrorReport(arguments); let new_len = glPrograms.push(prog); return new_len; } + function glAttachShader(program, shader) { let s = gl_get_shader(shader); let p = gl_get_program(program); - return gl.attachShader(p, s); + const r = gl.attachShader(p, s); + glErrorReport(arguments); + return r; } + function glLinkProgram(program) { let p = gl_get_program(program); @@ -210,43 +315,94 @@ function glLinkProgram(program) console.error("Failed to compile WebGL program. \n\n"+info); } } + function glUseProgram(program) { let p = gl_get_program(program); - return gl.useProgram(p); + const r = gl.useProgram(p); + glErrorReport(arguments); + return r; } + function glGetAttribLocation(program, name, name_len) { let str = wasm_read_string(lumenarium_wasm_instance, name, name_len); - return gl.getAttribLocation(gl_get_program(program), str); + const r = gl.getAttribLocation(gl_get_program(program), str); + glErrorReport(arguments); + return r; } + function glVertexAttribPointer(attr, size, type, normalized, stride, pointer) { - return gl.vertexAttribPointer(attr, size, type, normalized, stride, pointer); + const r = gl.vertexAttribPointer(attr, size, type, normalized, stride, pointer); + glErrorReport(arguments); + return r; } + function glEnableVertexAttribArray(index) { - return gl.enableVertexAttribArray(index); + const r = gl.enableVertexAttribArray(index); + glErrorReport(arguments); + return r; } + function glDrawElements(type, index_count, ele_type, indices) { - return gl.drawElements(type, index_count, ele_type, indices); + const r = gl.drawElements(type, index_count, ele_type, indices); + glErrorReport(arguments); + return r; +} + +function glGenTextures(count, ids_ptr, ids_size) +{ + for (let i = 0; i < count; i++) + { + const tex = gl.createTexture(); + glErrorReport(arguments); + let new_len = glTextures.push(tex); + put_u32(ids_ptr + (i * 4), new_len); + } +} + +function glBindTexture(slot, id) +{ + let tex = gl_get_texture(id); + const r = gl.bindTexture(slot, tex); + glErrorReport(arguments); + return r; +} + +function glTexParameteri(slot, param, value) +{ + const r = gl.texParameteri(slot, param, value); + glErrorReport(arguments); + return r; +} + +function glTexImage2D(target, level, internalformat, width, height, border, format, type, data_ptr, data_size) +{ + const data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, data_ptr, data_size); + const r = gl.texImage2D(target, level, internalformat, width, height, border, format, type, data); + glErrorReport(arguments); + return r; +} + +function glTexSubImage2D(target, level, offsetx, offsety, width, height, format, type, data_ptr, data_size) +{ + const data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, data_ptr, data_size); + const r = gl.texSubImage2D(target, level, offsetx, offsety, width, height, format, type, data); + glErrorReport(arguments); + return r; } function webgl_add_imports (canvas_selector, imports) { const canvas = document.querySelector(canvas_selector); if (!canvas) return console.error("no canvas"); - gl = canvas.getContext("webgl"); + gl = canvas.getContext("webgl2"); if (gl === null) return console.error("no webgl ctx"); - console.log( - gl.FLOAT.toString(16), "\n", - gl.UNSIGNED_INT.toString(16), "\n" - ); - - - + imports.glHadError = () => { return gl_error; }; imports.glClearColor = glClearColor; imports.glEnable = glEnable; imports.glDisable = glDisable; @@ -269,6 +425,11 @@ function webgl_add_imports (canvas_selector, imports) { imports.glVertexAttribPointer = glVertexAttribPointer; imports.glEnableVertexAttribArray = glEnableVertexAttribArray; imports.glDrawElements = glDrawElements; - + imports.glGenTextures = glGenTextures; + imports.glBindTexture = glBindTexture; + imports.glTexParameteri = glTexParameteri; + imports.glTexImage2D = glTexImage2D; + imports.glTexSubImage2D = glTexSubImage2D; + imports.glBindTexture = glBindTexture; return imports; } \ No newline at end of file diff --git a/run_tree/win32/intel/debug/debug.rdbg b/run_tree/win32/intel/debug/debug.rdbg index 0f16f81b1dfd750060cf056d8d209882ec828e87..f78c41edc47c99c5665b943ccc884228d58a7102 100644 GIT binary patch delta 220 zcmX@gwT*K^6rFwoYcf3=lq=fB9Qvr t;>Hwv5D-)FQpal9Hn0=*-y32U$!f3$Su9@=RuAwFj~UCOfdQ0sz9$Il2G< delta 265 zcmdnSd6a8H6r=ceZ77gd6E`+>!Dm=v&yiI->QrR0}`)G;wY03(nJfa=lV z^}!~psRk6~0Ah9^PAW}HOD!rE0y5Kc^wM*ji&7IyQk@{8lczEV3V=+O^Y~o}!XWl! X6&6!&5Ceo68962wve>hNtYriMX6-~> diff --git a/run_tree/win32/intel/debug/err.txt b/run_tree/win32/intel/debug/err.txt new file mode 100644 index 0000000..9002a22 --- /dev/null +++ b/run_tree/win32/intel/debug/err.txt @@ -0,0 +1 @@ +OpenGL Version: 3.3.0 - Build 27.20.100.9778 diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj index 895b93cf864f72c26e1bd471d5b60ca8972521e3..f70d1299c12b73a147d011c4127b17a69051d413 100644 GIT binary patch literal 160688 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
b&lK5?mal&n2!v@_2>U!m#RwsfqO`~ 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 IBH#h_02(HRT>t<8 diff --git a/src/app/editor/foldhaus_editor.cpp b/src/app/editor/foldhaus_editor.cpp index 3764b25..8db76a9 100644 --- a/src/app/editor/foldhaus_editor.cpp +++ b/src/app/editor/foldhaus_editor.cpp @@ -8,147 +8,147 @@ internal void Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue, mouse_state Mouse, context Context) { - DEBUG_TRACK_FUNCTION; + DEBUG_TRACK_FUNCTION; + + b32 MouseInputHandled = HandleMousePanelInteraction(&State->PanelSystem, State->WindowBounds, Mouse, State); + + gs_string TextInputString = PushString(State->Transient, 32); + + panel* ActivePanel = PanelSystem_GetPanelContainingPoint(&State->PanelSystem, Mouse.Pos); + if (ActivePanel) + { + panel_definition ActiveDef = State->PanelSystem.PanelDefs[ActivePanel->TypeIndex]; - b32 MouseInputHandled = HandleMousePanelInteraction(&State->PanelSystem, State->WindowBounds, Mouse, State); - - gs_string TextInputString = PushString(State->Transient, 32); - - panel* ActivePanel = PanelSystem_GetPanelContainingPoint(&State->PanelSystem, Mouse.Pos); - if (ActivePanel) + input_command_registry ActiveCommands = {}; + if (State->Modes.ActiveModesCount > 0) { - panel_definition ActiveDef = State->PanelSystem.PanelDefs[ActivePanel->TypeIndex]; - - input_command_registry ActiveCommands = {}; - if (State->Modes.ActiveModesCount > 0) - { - ActiveCommands = State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].Commands; - } - else if (ActiveDef.InputCommands) - { - ActiveCommands.Commands = ActiveDef.InputCommands; - ActiveCommands.Size = sizeof(*ActiveDef.InputCommands) / sizeof(ActiveDef.InputCommands[0]); - ActiveCommands.Used = ActiveCommands.Size; - } - - // Fill up the command queue - for (s32 EventIdx = 0; EventIdx < InputQueue.QueueUsed; EventIdx++) - { - input_entry Event = InputQueue.Entries[EventIdx]; - - bool IsMouseEvent = (Event.Key == KeyCode_MouseLeftButton || - Event.Key == KeyCode_MouseMiddleButton || - Event.Key == KeyCode_MouseRightButton); - if (IsMouseEvent && MouseInputHandled) - { - continue; - } - - // NOTE(Peter): These are in the order Down, Up, Held because we want to privalege - // Down and Up over Held. In other words, we don't want to call a Held command on the - // frame when the button was released, even if the command is registered to both events - if (KeyTransitionedDown(Event)) - { - if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue)) - { - char KeyASCII = KeyCodeToChar(Event.Key); - if (KeyASCII) - { - OutChar(&TextInputString, KeyASCII); - } - } - } - else if (KeyTransitionedUp(Event)) - { - FindAndPushExistingCommand(ActiveCommands, Event, Command_Ended, &State->CommandQueue); - } - else if (KeyHeldDown(Event)) - { - if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue)) - { - char KeyASCII = KeyCodeToChar(Event.Key); - if (KeyASCII) - { - OutChar(&TextInputString, KeyASCII); - } - } - } - } + ActiveCommands = State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].Commands; + } + else if (ActiveDef.InputCommands) + { + ActiveCommands.Commands = ActiveDef.InputCommands; + ActiveCommands.Size = sizeof(*ActiveDef.InputCommands) / sizeof(ActiveDef.InputCommands[0]); + ActiveCommands.Used = ActiveCommands.Size; } - // Execute all commands in CommandQueue - for (s32 CommandIdx = State->CommandQueue.Used - 1; CommandIdx >= 0; CommandIdx--) + // Fill up the command queue + for (s32 EventIdx = 0; EventIdx < InputQueue.QueueUsed; EventIdx++) { - command_queue_entry* Entry = &State->CommandQueue.Commands[CommandIdx]; - if (Entry->Command.Proc) + input_entry Event = InputQueue.Entries[EventIdx]; + + bool IsMouseEvent = (Event.Key == KeyCode_MouseLeftButton || + Event.Key == KeyCode_MouseMiddleButton || + Event.Key == KeyCode_MouseRightButton); + if (IsMouseEvent && MouseInputHandled) + { + continue; + } + + // NOTE(Peter): These are in the order Down, Up, Held because we want to privalege + // Down and Up over Held. In other words, we don't want to call a Held command on the + // frame when the button was released, even if the command is registered to both events + if (KeyTransitionedDown(Event)) + { + if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue)) { - Entry->Command.Proc(State, Entry->Event, Mouse, Context, ActivePanel); + char KeyASCII = KeyCodeToChar(Event.Key); + if (KeyASCII) + { + OutChar(&TextInputString, KeyASCII); + } } - else + } + else if (KeyTransitionedUp(Event)) + { + FindAndPushExistingCommand(ActiveCommands, Event, Command_Ended, &State->CommandQueue); + } + else if (KeyHeldDown(Event)) + { + if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue)) { - EndCurrentOperationMode(State); + char KeyASCII = KeyCodeToChar(Event.Key); + if (KeyASCII) + { + OutChar(&TextInputString, KeyASCII); + } } + } } - - State->Interface.TempInputString = TextInputString.ConstString; - - ClearCommandQueue(&State->CommandQueue); + } + + // Execute all commands in CommandQueue + for (s32 CommandIdx = State->CommandQueue.Used - 1; CommandIdx >= 0; CommandIdx--) + { + command_queue_entry* Entry = &State->CommandQueue.Commands[CommandIdx]; + if (Entry->Command.Proc) + { + Entry->Command.Proc(State, Entry->Event, Mouse, Context, ActivePanel); + } + else + { + EndCurrentOperationMode(State); + } + } + + State->Interface.TempInputString = TextInputString.ConstString; + + ClearCommandQueue(&State->CommandQueue); } internal void Editor_Update(app_state* State, context* Context, input_queue InputQueue) { - Context->Mouse.CursorType = CursorType_Arrow; - State->WindowBounds = Context->WindowBounds; - State->Interface.Mouse = Context->Mouse; - - State->Interface.HotWidgetFramesSinceUpdate += 1; - if (State->Interface.HotWidgetFramesSinceUpdate > 1) - { - State->Interface.HotWidget = {}; - } - - Assert(State->Interface.PerFrameMemory && - (u64)State->Interface.PerFrameMemory != 0x5); - - PanelSystem_UpdateLayout(&State->PanelSystem, State->WindowBounds); - Editor_HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context); + Context->Mouse.CursorType = CursorType_Arrow; + State->WindowBounds = Context->WindowBounds; + State->Interface.Mouse = Context->Mouse; + + State->Interface.HotWidgetFramesSinceUpdate += 1; + if (State->Interface.HotWidgetFramesSinceUpdate > 1) + { + State->Interface.HotWidget = {}; + } + + Assert(State->Interface.PerFrameMemory && + (u64)State->Interface.PerFrameMemory != 0x5); + + PanelSystem_UpdateLayout(&State->PanelSystem, State->WindowBounds); + Editor_HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context); } internal void Editor_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer) { - State->Interface.WindowBounds = Context->WindowBounds; - PushRenderOrthographic(RenderBuffer, State->WindowBounds); - PushRenderClearScreen(RenderBuffer); + State->Interface.WindowBounds = Context->WindowBounds; + PushRenderOrthographic(RenderBuffer, State->WindowBounds); + PushRenderClearScreen(RenderBuffer); + + + ui_InterfaceReset(&State->Interface); + State->Interface.RenderBuffer = RenderBuffer; + ui_PushLayout(&State->Interface, Context->WindowBounds, LayoutDirection_TopDown, MakeString("Editor Layout")); + { + DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context); - - ui_InterfaceReset(&State->Interface); - State->Interface.RenderBuffer = RenderBuffer; - ui_PushLayout(&State->Interface, Context->WindowBounds, LayoutDirection_TopDown, MakeString("Editor Layout")); + for (s32 m = 0; m < State->Modes.ActiveModesCount; m++) { - DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context); - - for (s32 m = 0; m < State->Modes.ActiveModesCount; m++) - { - operation_mode OperationMode = State->Modes.ActiveModes[m]; - if (OperationMode.Render != 0) - { - OperationMode.Render(State, RenderBuffer, OperationMode, Context->Mouse, *Context); - } - } + operation_mode OperationMode = State->Modes.ActiveModes[m]; + if (OperationMode.Render != 0) + { + OperationMode.Render(State, RenderBuffer, OperationMode, Context->Mouse, *Context); + } } - ui_PopLayout(&State->Interface, MakeString("Editor Layout")); - - - // Draw the Interface - if (State->Interface.DrawOrderRoot != 0) - { - ui_widget* Widget = State->Interface.DrawOrderRoot; - Editor_DrawWidgetList(State, Context, RenderBuffer, Widget, Context->WindowBounds); - } - - Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext); + } + ui_PopLayout(&State->Interface, MakeString("Editor Layout")); + + + // Draw the Interface + if (State->Interface.DrawOrderRoot != 0) + { + ui_widget* Widget = State->Interface.DrawOrderRoot; + Editor_DrawWidgetList(State, Context, RenderBuffer, Widget, Context->WindowBounds); + } + + Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext); } diff --git a/src_v2/editor/lumenarium_editor.cpp b/src_v2/editor/lumenarium_editor.cpp index 8fc0d0e..af166ce 100644 --- a/src_v2/editor/lumenarium_editor.cpp +++ b/src_v2/editor/lumenarium_editor.cpp @@ -1,8 +1,101 @@ +static r32 z_ = 0; +static r32 r_ = 0.3f; +static r32 quad_verts[] = { + -r_, -r_, z_, 1.0f, 0, 0, + r_, -r_, z_, 1.0f, 1, 0, + r_, r_, z_, 1.0f, 1, 1, + -r_, r_, z_, 1.0f, 0, 1, +}; + +static u32 quad_indices[] = { + 0, 1, 2, + 0, 2, 3, +}; + +static String shader_code_vert_win32 = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec4 coordinates;\n" + "layout (location = 1) in vec2 uv;\n" + "out vec2 tex_coord;\n" + "void main(void) {\n" + " gl_Position = coordinates;\n" + " tex_coord = uv;\n" + "}" + ); + +static String shader_code_vert_wasm = lit_str( + "precision highp float;\n" + "attribute vec4 coordinates;\n" + "attribute vec2 uv;\n" + "varying vec2 tex_coord;\n" + "void main(void) {\n" + " gl_Position = coordinates;\n" + " tex_coord = uv;\n" + "}"); + +static String shader_code_frag_win32 = lit_str( + "#version 330 core\n" + "in vec2 tex_coord;\n" + "out vec4 FragColor;\n" + "uniform sampler2D texture;\n" + "void main(void) {\n" + "// FragColor = vec4(1,tex_coord.x,tex_coord.y,1);\n" + " FragColor = texture(texture, tex_coord);\n" + "}" + ); + +static String shader_code_frag_wasm = lit_str( + "precision highp float;\n" + "varying vec2 tex_coord;\n" + "uniform sampler2D texture;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(texture, tex_coord);\n" + " // vec4(1, tex_coord.x, tex_coord.y, 1);\n" + "}"); + +static u32 pix[] = { + 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 0xFF000000, + 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 0xFF000000, +}; + +void make_quad(Platform_Geometry_Buffer* geo, Platform_Shader* shd, Platform_Texture* tex) +{ + // TODO(PS): TEMP +#if defined(PLATFORM_win32) + String shader_code_vert = shader_code_vert_win32; + String shader_code_frag = shader_code_frag_win32; +#elif defined(PLATFORM_wasm) + String shader_code_vert = shader_code_vert_wasm; + String shader_code_frag = shader_code_frag_wasm; +#endif + + *geo = platform_geometry_buffer_create( + quad_verts, 24, quad_indices, 6 + ); + + String attribs[] = { + lit_str("coordinates"), + lit_str("uv"), + }; + *shd = platform_shader_create( + shader_code_vert, shader_code_frag, attribs, 2 + ); + + platform_vertex_attrib_pointer(*geo, *shd, 4, shd->attrs[0], 6, 0); + platform_vertex_attrib_pointer(*geo, *shd, 2, shd->attrs[1], 6, 4); + + *tex = platform_texture_create((u8*)pix, 4, 4, 4); +} + internal void ed_init(App_State* state) { - + Editor* editor = allocator_alloc_struct(permanent, Editor); + state->editor = editor; + make_quad(&editor->renderer.geo, &editor->renderer.shd, &editor->renderer.tex); } internal void @@ -14,6 +107,13 @@ ed_frame_prepare(App_State* state) internal void ed_frame(App_State* state) { + for (u32 i = 0; i < 16; i++) + { + if (i % 2 == 1) continue; + pix[i] += 1; + } + platform_texture_update(state->editor->renderer.tex, (u8*)pix, 4, 4, 4); + edr_render(state); } diff --git a/src_v2/editor/lumenarium_editor.h b/src_v2/editor/lumenarium_editor.h new file mode 100644 index 0000000..08ab6df --- /dev/null +++ b/src_v2/editor/lumenarium_editor.h @@ -0,0 +1,12 @@ +/* date = March 27th 2022 0:50 pm */ + +#ifndef LUMENARIUM_EDITOR_H +#define LUMENARIUM_EDITOR_H + +struct Editor +{ + v2 window_dim; + Editor_Renderer renderer; +}; + +#endif //LUMENARIUM_EDITOR_H diff --git a/src_v2/editor/lumenarium_editor_renderer.cpp b/src_v2/editor/lumenarium_editor_renderer.cpp index 53b443f..deedaa7 100644 --- a/src_v2/editor/lumenarium_editor_renderer.cpp +++ b/src_v2/editor/lumenarium_editor_renderer.cpp @@ -1,82 +1,4 @@ -internal void -edr_init(App_State* state) -{ - v4 quad_verts[] = { - -0.5f, 0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.0f, 1.0f, - 00.5f, -0.5f, 0.0f, 1.0f, - 00.5f, 0.5f, 0.0f, 1.0f, - }; - - u32 quad_indices[] = { - 3, 2, 1, - 3, 1, 0, - }; - - char* shader_code_vert = - "#version 140\n" - "attribute vec4 coordinates;\n" - "void main(void) {\n" - " gl_Position = coordinates;\n" - "}"; - - char* shader_code_frag = - "#version 140\n" - "void main(void) {\n" - " gl_FragColor = vec4(1, 0, 1, 1);\n" - "}"; - -#if 0 - /* ======= Geometry =======*/ - - glCreateBuffers(1, &buffer_vertex); - glBindBuffer(GL_ARRAY_BUFFER, buffer_vertex); - glBufferData(GL_ARRAY_BUFFER, quad_verts, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, NULL); - - glCreateBuffer(1, &buffer_index); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_index); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, quad_indices, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); - - /* ======= Shaders =======*/ - - shader_vert = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(shader_vertex, shader_code_vert); - glCompileShader(shader_vert); - - shader_frag = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(shader_frag, shader_code_frag); - glCompileShader(shader_frag); - - shader_prog = glCreateProgram(); - glAttachShader(shader_prog, shader_vert); - glAttachShader(shader_prog, shader_frag); - glLinkProgram(shader_prog); - glUseProgram(shader_prog); - - /* ======= Associating shaders to buffer objects =======*/ - - glBindBuffer(GL_ARRAY_BUFFER, buffer_vertex); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_index); - coord = glGetAttribLocation(shader_prog, "coordinates"); - glVertexAttribPointer(coord, 4, GL_FLOAT, false, 0, 0); - glEnableVertexAttribArray(coord); -#endif -} - -internal void -edr_render_quad() -{ -#if 0 - glBindBuffer(GL_ARRAY_BUFFER, buffer_vertex); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_index); - glEnableVertexAttribArray(coord); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); -#endif -} - internal void edr_render(App_State* state) @@ -84,11 +6,12 @@ edr_render(App_State* state) Platform_Graphics_Frame_Desc desc = {}; desc.clear_color = { 0.1f, 0.1f, 0.1f, 1 }; desc.viewport_min = { 0, 0 }; - desc.viewport_max = { 1600, 900 }; + desc.viewport_max = state->editor->window_dim; platform_frame_begin(desc); platform_frame_clear(); -#if 0 - edr_render_quad(); -#endif + platform_geometry_bind(state->editor->renderer.geo); + platform_texture_bind(state->editor->renderer.tex); + platform_shader_bind(state->editor->renderer.shd); + platform_geometry_draw(state->editor->renderer.geo); } \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_renderer.h b/src_v2/editor/lumenarium_editor_renderer.h index 2be94b0..bebff8e 100644 --- a/src_v2/editor/lumenarium_editor_renderer.h +++ b/src_v2/editor/lumenarium_editor_renderer.h @@ -5,7 +5,9 @@ struct Editor_Renderer { - + Platform_Shader shd; + Platform_Geometry_Buffer geo; + Platform_Texture tex; }; #endif //LUMENARIUM_EDITOR_RENDERER_H diff --git a/src_v2/editor/lumenarium_ui.h b/src_v2/editor/lumenarium_ui.h new file mode 100644 index 0000000..72c6af8 --- /dev/null +++ b/src_v2/editor/lumenarium_ui.h @@ -0,0 +1,36 @@ +/* date = March 28th 2022 10:52 pm */ + +#ifndef LUMENARIUM_UI_H +#define LUMENARIUM_UI_H + +struct Font_Glyph +{ + u32 code_point; + v2 uv_min; + v2 uv_max; +}; + +struct Font_Glyph_Table +{ + Font_Glyph* values; + u32 cap; + u32 len; +}; + +struct Font_Bitmap +{ + u8* pixels; + u32 width; + u32 height; + u32 stride; + + Platform_Texture texture; +}; + +struct Font +{ + Font_Glyph_Table glyphs; + Font_Bitmap bitmap; +}; + +#endif //LUMENARIUM_UI_H diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.cpp index 9e81109..003f8d5 100644 --- a/src_v2/lumenarium_first.cpp +++ b/src_v2/lumenarium_first.cpp @@ -1,73 +1,6 @@ #include "lumenarium_first.h" #include "user_space/user_space_incenter.cpp" -Platform_Geometry_Buffer quad0; -Platform_Shader shader0; - -static r32 z_ = 0; -static r32 r_ = 0.3f; -static r32 quad_verts[] = { - -r_, -r_, z_, 1.0f, - r_, -r_, z_, 1.0f, - r_, r_, z_, 1.0f, - -r_, r_, z_, 1.0f, -}; - -static u32 quad_indices[] = { - 0, 1, 2, - 0, 2, 3, -}; - -static String shader_code_vert_win32 = lit_str( - "#version 330 core\n" - "layout (location = 0) in vec4 coordinates;\n" - "void main(void) {\n" - " gl_Position = coordinates;\n" - "}" - ); - -static String shader_code_vert_wasm = lit_str( - "attribute vec4 coordinates;\n" - "void main(void) {\n" - " gl_Position = coordinates;\n" - "}"); - -static String shader_code_frag_win32 = lit_str( - "#version 330 core\n" - "out vec4 FragColor;\n" - "void main(void) {\n" - " FragColor = vec4(1,0,1,1);\n" - "}" - ); - -static String shader_code_frag_wasm = lit_str( - "void main(void) {\n" - " gl_FragColor = vec4(1, 0, 1, 1);\n" - "}"); - -void make_quad() -{ - // TODO(PS): TEMP -#if defined(PLATFORM_win32) - String shader_code_vert = shader_code_vert_win32; - String shader_code_frag = shader_code_frag_win32; -#elif defined(PLATFORM_wasm) - String shader_code_vert = shader_code_vert_wasm; - String shader_code_frag = shader_code_frag_wasm; -#endif - - quad0 = platform_geometry_buffer_create( - quad_verts, 16, quad_indices, 6 - ); - - String attribs[] = { lit_str("coordinates") }; - shader0 = platform_shader_create( - shader_code_vert, shader_code_frag, attribs, 1 - ); - - platform_vertex_attrib_pointer(quad0, shader0, 0); -} - internal App_State* lumenarium_init() { @@ -76,7 +9,6 @@ lumenarium_init() permanent = bump_allocator_create_reserve(MB(4)); scratch = bump_allocator_create_reserve(KB(64)); - run_tests(); App_Init_Desc desc = incenter_get_init_desc(); @@ -84,19 +16,16 @@ lumenarium_init() state = allocator_alloc_struct(permanent, App_State); add_flag(state->flags, AppState_IsRunning); + add_flag(state->flags, AppState_RunEditor); state->input_state = input_state_create(); en_init(state, desc); - if (!has_flag(state->flags, AppState_NoEditor)) + if (has_flag(state->flags, AppState_RunEditor)) { ed_init(state); } incenter_init(state); - - - make_quad(); - return state; } @@ -108,7 +37,7 @@ lumenarium_frame_prepare(App_State* state) input_state_swap_frames(&state->input_state); en_frame_prepare(state); - if (!has_flag(state->flags, AppState_NoEditor)) + if (has_flag(state->flags, AppState_RunEditor)) { ed_frame_prepare(state); } @@ -119,23 +48,9 @@ internal void lumenarium_frame(App_State* state) { en_frame(state); - if (!has_flag(state->flags, AppState_NoEditor)) + if (has_flag(state->flags, AppState_RunEditor)) { - //ed_frame(state); - - Platform_Graphics_Frame_Desc desc = {}; - desc.clear_color = { 0.1f, 0.1f, 0.1f, 1 }; - desc.viewport_min = { 0, 0 }; - desc.viewport_max = { 1600, 900 }; - platform_frame_begin(desc); - platform_frame_clear(); - - - platform_geometry_bind(quad0); - platform_shader_bind(shader0); - platform_geometry_draw(quad0); - - + ed_frame(state); } incenter_frame(state); } @@ -176,7 +91,7 @@ lumenarium_cleanup(App_State* state) { incenter_cleanup(state); en_cleanup(state); - if (!has_flag(state->flags, AppState_NoEditor)) + if (has_flag(state->flags, AppState_RunEditor)) { ed_cleanup(state); } diff --git a/src_v2/lumenarium_first.h b/src_v2/lumenarium_first.h index 392008c..441ef69 100644 --- a/src_v2/lumenarium_first.h +++ b/src_v2/lumenarium_first.h @@ -15,6 +15,7 @@ typedef struct App_State App_State; // Editor #include "editor/lumenarium_editor_renderer.h" +#include "editor/lumenarium_editor.h" ////////////////////////////////////////////// // Lumenarium Runtime Environment @@ -36,7 +37,7 @@ enum { AppState_None = 0, AppState_IsRunning = 1, - AppState_NoEditor = 2, + AppState_RunEditor = 2, }; struct App_Init_Desc @@ -50,6 +51,8 @@ struct App_State Input_State input_state; Assembly_Array assemblies; + + Editor* editor; }; #include "engine/lumenarium_engine_assembly.cpp" diff --git a/src_v2/lumenarium_string.cpp b/src_v2/lumenarium_string.cpp index 83f4ad1..157f2c6 100644 --- a/src_v2/lumenarium_string.cpp +++ b/src_v2/lumenarium_string.cpp @@ -7,7 +7,8 @@ c_str_len(char* s) return result; } -#define str_varg(str) (int)(str).len, (char*)(str).str +#define str_varg(s) (int)(s).len, (char*)(s).str +#define str_expand(s) (char*)(s).str, (u64)(s).len #define lit_str(s) String{ (u8*)(s), (u64)sizeof(s)-1, (u64)sizeof(s)-1 } internal String diff --git a/src_v2/platform/lumenarium_assert.h b/src_v2/platform/lumenarium_assert.h index 69e837b..213d041 100644 --- a/src_v2/platform/lumenarium_assert.h +++ b/src_v2/platform/lumenarium_assert.h @@ -3,27 +3,47 @@ #ifndef LUMENARIUM_ASSERT_H #define LUMENARIUM_ASSERT_H +#if defined(PRINT_ASSERTS) +# include +# define err_write(s,...) err_write_(s,__VA_ARGS__) +static FILE* file_err; +void err_write_(char* fmt, ...) { + if (!file_err) return; + va_list args; + va_start(args, fmt); + vfprintf(file_err, fmt, args); + va_end(args); +} +void open_err_file() { file_err = fopen("./err.txt", "wb"); } +void close_err_file() { fclose(file_err); } +#else +# define err_write(s,...) +void open_err_file() {} +void close_err_file() {} +#endif + #if !defined(PLATFORM_wasm) + // 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) #else -WASM_EXTERN void wasm_assert_always(char* file, u32 file_len, u32 line); +WASM_EXTERN void wasm_assert_always(char* file, unsigned int file_len, unsigned int line); # define assert_always wasm_assert_always(__FILE__, sizeof(__FILE__), __LINE__) #endif // defined(PLATFORM_WASM) #ifdef USE_ASSERTS -# define assert(c) if (!(c)) { assert_always; } +# define assert(c) if (!(c)) { err_write("Assert Hit: %s:%d\n", __FILE__, (u32)__LINE__); close_err_file(); 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 +# define invalid_code_path assert(0); // 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; +# define invalid_default_case default: { assert(0); } break; #else # define assert(c) diff --git a/src_v2/platform/lumenarium_platform.h b/src_v2/platform/lumenarium_platform.h index 67cf492..a96a830 100644 --- a/src_v2/platform/lumenarium_platform.h +++ b/src_v2/platform/lumenarium_platform.h @@ -309,6 +309,13 @@ struct Platform_Geometry_Buffer u32 indices_len; }; +struct Platform_Texture +{ + u32 id; + + u32 w, h, s; +}; + struct Platform_Graphics_Frame_Desc { v4 clear_color; @@ -319,18 +326,23 @@ struct Platform_Graphics_Frame_Desc void platform_frame_begin(Platform_Graphics_Frame_Desc desc); void platform_frame_clear(); +// Geometry Platform_Geometry_Buffer platform_geometry_buffer_create(r32* vertices, u32 vertices_len, u32* indices, u32 indices_len); Platform_Shader platform_shader_create( String code_vert, String code_frag, String* attribs, u32 attribs_len ); -void platform_vertex_attrib_pointer( - Platform_Geometry_Buffer geo, Platform_Shader shader, u32 attrib_index - ); +// Shaders void platform_geometry_bind(Platform_Geometry_Buffer geo); void platform_shader_bind(Platform_Shader shader); void platform_geometry_draw(Platform_Geometry_Buffer geo); void platform_vertex_attrib_pointer( - Platform_Geometry_Buffer geo, Platform_Shader shader, u32 attr_index + Platform_Geometry_Buffer geo, Platform_Shader shader, u32 count, u32 attr_index, u32 stride, u32 offset ); + +// Textures +Platform_Texture platform_texture_create(u8* pixels, u32 width, u32 height, u32 stride); +void platform_texture_bind(Platform_Texture tex); +void platform_texture_update(Platform_Texture tex, u8* new_pixels, u32 width, u32 height, u32 stride); + #endif //LUMENARIUM_PLATFORM_H diff --git a/src_v2/platform/lumenarium_platform_common_includes.h b/src_v2/platform/lumenarium_platform_common_includes.h index 421b694..802889c 100644 --- a/src_v2/platform/lumenarium_platform_common_includes.h +++ b/src_v2/platform/lumenarium_platform_common_includes.h @@ -13,9 +13,13 @@ #include "lumenarium_assert.h" -#include "glcorearb.h" -#include "glext.h" -#include "wglext.h" +// NOTE(PS): only need the opengl extension headers +// when running on a platform that is using opengl 3.3+ +#if !defined(PLATFORM_wasm) +# include "glcorearb.h" +# include "glext.h" +# include "wglext.h" +#endif #if 0 #define HMM_SINF sin diff --git a/src_v2/platform/wasm/lumenarium_first_wasm.cpp b/src_v2/platform/wasm/lumenarium_first_wasm.cpp index e4285a0..8446aa6 100644 --- a/src_v2/platform/wasm/lumenarium_first_wasm.cpp +++ b/src_v2/platform/wasm/lumenarium_first_wasm.cpp @@ -25,6 +25,8 @@ WASM_EXTERN void print(const char* text, int len); typedef void wasm_animation_frame_cb(u32 time_elapsed); WASM_EXTERN void wasm_request_animation_frame(wasm_animation_frame_cb* cb); +WASM_EXTERN void wasm_get_canvas_dim(u32* w_ptr, u32* h_ptr); + EXTERN_C_BEGIN; int @@ -46,14 +48,25 @@ update(u32 time_elapsed) lumenarium_frame(wasm_app_state); // TODO(PS): check for app running flags - wasm_request_animation_frame(update); + if (!glHadError()) + { + wasm_request_animation_frame(update); + } } WASM_EXPORT int main(void) { wasm_app_state = lumenarium_init(); - //wasm_request_animation_frame(update); + if (has_flag(wasm_app_state->flags, AppState_RunEditor)) + { + u32 w, h; + wasm_get_canvas_dim(&w, &h); + wasm_app_state->editor->window_dim = v2{ + (r32)w, (r32)h + }; + } + wasm_request_animation_frame(update); return 0; #if 0 diff --git a/src_v2/platform/wasm/lumenarium_wasm_imports.js b/src_v2/platform/wasm/lumenarium_wasm_imports.js index 299fa8a..29d372c 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_imports.js +++ b/src_v2/platform/wasm/lumenarium_wasm_imports.js @@ -34,6 +34,35 @@ function wasm_get_proc(inst, proc_ptr) function fract (v) { return v % 1; } +function u32_to_byte_array_32 (v) +{ + let result = [0, 0, 0, 0]; + result[0] = (v & 0xff); + result[1] = (((v - result[0]) >> 8 ) & 0xff); + result[2] = (((v - result[1]) >> 16) & 0xff); + result[3] = (((v - result[2]) >> 24) & 0xff); + return result; +} + +function byte_array_32_to_u32 (arr) +{ + // NOTE(PS): the '>>>' operators in this function deal with the fact + // that bit shift operators convert numbers to s32's. The >>> just + // converts them back to u32s + let r0 = ((arr[0] & 0xff) << 0 ); + let r1 = ((arr[1] & 0xff) << 8 ); + let r2 = ((arr[2] & 0xff) << 16); + let r3 = (((arr[3] & 0xff) << 24) >>> 0); + let result = (r0 | r1 | r2 | r3) >>> 0; + return result; +} + +function put_u32 (ptr, value) +{ + let src = u32_to_byte_array_32(value); + wasm_write_bytes(lumenarium_wasm_instance, src, ptr, 4); +} + var lumenarium_wasm_imports = { memset: (dst, size, value) => { @@ -122,12 +151,46 @@ var lumenarium_wasm_imports = { let string = wasm_read_string(lumenarium_wasm_instance, str_base, len); console.log(string); }, + + wasm_get_canvas_dim: (w_ptr, h_ptr) => { + const canvas = document.querySelector("#gl_canvas"); + + let w_view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, w_ptr, 4); + let w = canvas.width; + let wb = u32_to_byte_array_32(w); + for (let i = 0; i < 4; i++) w_view[i] = wb[i]; + + let h_view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, h_ptr, 4); + let h = canvas.height; + let hb = u32_to_byte_array_32(h); + for (let i = 0; i < 4; i++) h_view[i] = hb[i]; + }, }; /////////////////////////////////////// // Web GL Imports let gl = null; +let gl_error = false; + +function glErrorReport(outer_args) { + const err = gl.getError(); + if (err == gl.NO_ERROR) return; + + gl_error = true; + let msg = ""; + switch (err) { + case gl.NO_ERROR: { msg = "NO_ERROR"; } break; + case gl.INVALID_ENUM: { msg = "INVALID_ENUM"; } break; + case gl.INVALID_VALUE: { msg = "INVALID_VALUE"; } break; + case gl.INVALID_OPERATION: { msg = "INVALID_OPERATION"; } break; + case gl.INVALID_FRAMEBUFFER_OPERATION: { msg = "INVALID_FRAMEBUFFER_OPERATION"; } break; + case gl.OUT_OF_MEMORY: { msg = "OUT_OF_MEMORY"; } break; + case gl.CONTEXT_LOST_WEBGL: { msg = "CONTEXT_LOST_WEBGL"; } break; + default: { msg = "Uknown error"; } break; + } + console.error(`WebGL Error: ${msg} ${err}`, outer_args); +} // NOTE(PS): it seems like its not enough to set // the values of imports to gl.function @@ -135,16 +198,37 @@ let gl = null; // instead we need to wrap them for some reason. // Not sure why function glClearColor (r, g, b, a) { return gl.clearColor(r,g,b,a); } -function glEnable(v) { return gl.enable(v); } -function glDisable(v) { return gl.disable(v); } -function glBlendFunc(a,b) { return gl.blendFunc(a,b); } +function glEnable(v) { + const r = gl.enable(v); + glErrorReport(arguments); + return r; +} +function glDisable(v) { + const r = gl.disable(v); + glErrorReport(arguments); + return r; +} +function glBlendFunc(a,b) { + const r = gl.blendFunc(a,b); + glErrorReport(arguments); + return r; +} function glViewport(xmin, ymin, xmax, ymax) { return gl.viewport(xmin,ymin,xmax,ymax); } -function glDepthFunc(v) { return gl.depthFunc(v); } -function glClear(mask) { return gl.clear(mask); } +function glDepthFunc(v) { + const r = gl.depthFunc(v); + glErrorReport(arguments); + return r; +} +function glClear(mask) { + const r = gl.clear(mask); + glErrorReport(arguments); + return r; +} let glBuffers = []; let glShaders = []; let glPrograms = []; +let glTextures = []; function gl_get_managed_resource(arr, id) { if (id == 0) return null; return arr[id - 1]; @@ -152,55 +236,76 @@ function gl_get_managed_resource(arr, id) { function gl_get_buffer(id) { return gl_get_managed_resource(glBuffers, id); } function gl_get_shader(id) { return gl_get_managed_resource(glShaders, id); } function gl_get_program(id) { return gl_get_managed_resource(glPrograms, id); } +function gl_get_texture(id) { return gl_get_managed_resource(glTextures, id); } function glCreateBuffer() { let buffer = gl.createBuffer(); + glErrorReport(arguments); let new_len = glBuffers.push(buffer); return new_len; } + function glBindBuffer(buffer_kind, buffer_id) { - return gl.bindBuffer(buffer_kind, gl_get_buffer(buffer_id)); + const r = gl.bindBuffer(buffer_kind, gl_get_buffer(buffer_id)); + glErrorReport(arguments); + return r; } + function glBufferData(target, size, ptr, usage) { let data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, size); - return gl.bufferData(target, data, usage); + const r = gl.bufferData(target, data, usage); + glErrorReport(arguments); + return r; } + function glCreateShader(kind) { let shader = gl.createShader(kind); + glErrorReport(arguments); let new_len = glShaders.push(shader); return new_len; } + function glShaderSource(shader_id, shader_code, shader_code_len) { let str = wasm_read_string(lumenarium_wasm_instance, shader_code, shader_code_len); - console.error("For some reason, str isn't getting the correct data out of here", str); - return gl.shaderSource(gl_get_shader(shader_id), str); + const r = gl.shaderSource(gl_get_shader(shader_id), str); + glErrorReport(arguments); + return r; } + function glCompileShader(shader_id) { let s = gl_get_shader(shader_id); let r = gl.compileShader(s); + glErrorReport(arguments); let m = gl.getShaderInfoLog(s); + glErrorReport(arguments); if (m.length > 0) { console.error("glCompileShader: \n\n" + m); } } + function glCreateProgram() { let prog = gl.createProgram(); + glErrorReport(arguments); let new_len = glPrograms.push(prog); return new_len; } + function glAttachShader(program, shader) { let s = gl_get_shader(shader); let p = gl_get_program(program); - return gl.attachShader(p, s); + const r = gl.attachShader(p, s); + glErrorReport(arguments); + return r; } + function glLinkProgram(program) { let p = gl_get_program(program); @@ -210,43 +315,94 @@ function glLinkProgram(program) console.error("Failed to compile WebGL program. \n\n"+info); } } + function glUseProgram(program) { let p = gl_get_program(program); - return gl.useProgram(p); + const r = gl.useProgram(p); + glErrorReport(arguments); + return r; } + function glGetAttribLocation(program, name, name_len) { let str = wasm_read_string(lumenarium_wasm_instance, name, name_len); - return gl.getAttribLocation(gl_get_program(program), str); + const r = gl.getAttribLocation(gl_get_program(program), str); + glErrorReport(arguments); + return r; } + function glVertexAttribPointer(attr, size, type, normalized, stride, pointer) { - return gl.vertexAttribPointer(attr, size, type, normalized, stride, pointer); + const r = gl.vertexAttribPointer(attr, size, type, normalized, stride, pointer); + glErrorReport(arguments); + return r; } + function glEnableVertexAttribArray(index) { - return gl.enableVertexAttribArray(index); + const r = gl.enableVertexAttribArray(index); + glErrorReport(arguments); + return r; } + function glDrawElements(type, index_count, ele_type, indices) { - return gl.drawElements(type, index_count, ele_type, indices); + const r = gl.drawElements(type, index_count, ele_type, indices); + glErrorReport(arguments); + return r; +} + +function glGenTextures(count, ids_ptr, ids_size) +{ + for (let i = 0; i < count; i++) + { + const tex = gl.createTexture(); + glErrorReport(arguments); + let new_len = glTextures.push(tex); + put_u32(ids_ptr + (i * 4), new_len); + } +} + +function glBindTexture(slot, id) +{ + let tex = gl_get_texture(id); + const r = gl.bindTexture(slot, tex); + glErrorReport(arguments); + return r; +} + +function glTexParameteri(slot, param, value) +{ + const r = gl.texParameteri(slot, param, value); + glErrorReport(arguments); + return r; +} + +function glTexImage2D(target, level, internalformat, width, height, border, format, type, data_ptr, data_size) +{ + const data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, data_ptr, data_size); + const r = gl.texImage2D(target, level, internalformat, width, height, border, format, type, data); + glErrorReport(arguments); + return r; +} + +function glTexSubImage2D(target, level, offsetx, offsety, width, height, format, type, data_ptr, data_size) +{ + const data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, data_ptr, data_size); + const r = gl.texSubImage2D(target, level, offsetx, offsety, width, height, format, type, data); + glErrorReport(arguments); + return r; } function webgl_add_imports (canvas_selector, imports) { const canvas = document.querySelector(canvas_selector); if (!canvas) return console.error("no canvas"); - gl = canvas.getContext("webgl"); + gl = canvas.getContext("webgl2"); if (gl === null) return console.error("no webgl ctx"); - console.log( - gl.FLOAT.toString(16), "\n", - gl.UNSIGNED_INT.toString(16), "\n" - ); - - - + imports.glHadError = () => { return gl_error; }; imports.glClearColor = glClearColor; imports.glEnable = glEnable; imports.glDisable = glDisable; @@ -269,6 +425,11 @@ function webgl_add_imports (canvas_selector, imports) { imports.glVertexAttribPointer = glVertexAttribPointer; imports.glEnableVertexAttribArray = glEnableVertexAttribArray; imports.glDrawElements = glDrawElements; - + imports.glGenTextures = glGenTextures; + imports.glBindTexture = glBindTexture; + imports.glTexParameteri = glTexParameteri; + imports.glTexImage2D = glTexImage2D; + imports.glTexSubImage2D = glTexSubImage2D; + imports.glBindTexture = glBindTexture; return imports; } \ No newline at end of file diff --git a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp index d164900..88be506 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp +++ b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp @@ -6,6 +6,7 @@ // TODO(PS): you guessed the data types and names of ALL of this // fix it! +typedef int GLint; typedef unsigned int GLuint; typedef float GLfloat; typedef unsigned int GLenum; @@ -35,9 +36,19 @@ typedef unsigned int GLsizei; #define GL_FRAGMENT_SHADER 0x8b30 #define GL_VERTEX_SHADER 0x8b31 #define GL_TRIANGLES 0x0004 -#define GL_UNSIGNED_INT 0x1406 -#define GL_FLOAT 0x1405 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_REPEAT 0x2901 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_LINEAR 0x2601 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_UNSIGNED_BYTE 0x1401 +WASM_EXTERN bool glHadError(); WASM_EXTERN void glClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a); WASM_EXTERN void glEnable(GLuint i); WASM_EXTERN void glDisable(GLuint i); @@ -60,6 +71,12 @@ WASM_EXTERN GLuint glGetAttribLocation(GLuint program, const char* name, GLuint WASM_EXTERN void glVertexAttribPointer(GLuint attr, GLuint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); WASM_EXTERN void glEnableVertexAttribArray(GLuint index); WASM_EXTERN void glDrawElements(GLenum type, GLuint count, GLenum ele_type, void* indices); +WASM_EXTERN void glGenTextures(GLuint count, GLuint* ids, u32 ids_size); +WASM_EXTERN void glBindTexture(GLenum slot, GLuint id); +WASM_EXTERN void glTexParameteri(GLenum slot, GLenum param, GLenum value); +WASM_EXTERN void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * data, u32 data_size); +WASM_EXTERN void glBindTexture(GLenum target, GLuint id); +WASM_EXTERN void glTexSubImage2D(GLenum target, GLint level, GLuint offsetx, GLuint offsety, GLuint w, GLuint h, GLenum format, GLenum type, void* ptr, u32 ptr_size); Platform_Geometry_Buffer platform_geometry_buffer_create( @@ -97,13 +114,11 @@ platform_shader_create( Platform_Shader result = {}; GLuint shader_vert = glCreateShader(GL_VERTEX_SHADER); - GLuint vert_len = (GLuint)code_vert.len; - glShaderSource(shader_vert, (char*)&code_vert.str, vert_len); + glShaderSource(shader_vert, str_expand(code_vert)); glCompileShader(shader_vert); GLuint shader_frag = glCreateShader(GL_FRAGMENT_SHADER); - GLuint frag_len = (GLuint)code_frag.len; - glShaderSource(shader_frag, (char*)&code_frag.str, frag_len); + glShaderSource(shader_frag, str_expand(code_frag)); glCompileShader(shader_frag); result.id = (GLuint)glCreateProgram(); @@ -138,7 +153,7 @@ void platform_shader_bind(Platform_Shader shader) { glUseProgram(shader.id); - for (GLuint i = 0; i < PLATFORM_SHADER_MAX_ATTRS && shader.attrs[i] != PLATFORM_SHADER_MAX_ATTRS; i++) + for (GLuint i = 0; i < PLATFORM_SHADER_MAX_ATTRS && shader.attrs[i] != PLATFORM_SHADER_ATTR_LAST; i++) { glEnableVertexAttribArray(shader.attrs[i]); } @@ -152,11 +167,70 @@ platform_geometry_draw( } void platform_vertex_attrib_pointer( - Platform_Geometry_Buffer geo, Platform_Shader shader, GLuint attr_index + Platform_Geometry_Buffer geo, Platform_Shader shader, u32 count, u32 attr_index, u32 stride, u32 offset ){ - platform_shader_bind(shader); + //platform_shader_bind(shader); platform_geometry_bind(geo); - glVertexAttribPointer(shader.attrs[attr_index], 4, GL_FLOAT, false, 0, 0); + glVertexAttribPointer(shader.attrs[attr_index], count, GL_FLOAT, false, stride * sizeof(float), (void*)(offset * sizeof(float))); +} + +Platform_Texture +platform_texture_create(u8* pixels, u32 width, u32 height, u32 stride) +{ + Platform_Texture result = {}; + glGenTextures(1, &result.id, sizeof(u32)); + glBindTexture(GL_TEXTURE_2D, result.id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + width, + height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + pixels, + (width * height) * sizeof(u32) + ); + + result.w = width; + result.h = height; + result.s = stride; + + return result; +} + +void +platform_texture_update(Platform_Texture tex, u8* new_pixels, u32 width, u32 height, u32 stride) +{ + // NOTE(PS): this function simply replaces the entire image + // we can write a more granular version if we need it + + assert(tex.w == width && tex.h == height && tex.s == stride); + platform_texture_bind(tex); + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, 0, // offset + width, height, + GL_RGBA, + GL_UNSIGNED_BYTE, + new_pixels, + width * height * sizeof(u32) + ); +} + + +void +platform_texture_bind(Platform_Texture tex) +{ + glBindTexture(GL_TEXTURE_2D, tex.id); } void diff --git a/src_v2/platform/win32/lumenarium_first_win32.cpp b/src_v2/platform/win32/lumenarium_first_win32.cpp index 252ce0e..d27f084 100644 --- a/src_v2/platform/win32/lumenarium_first_win32.cpp +++ b/src_v2/platform/win32/lumenarium_first_win32.cpp @@ -180,6 +180,8 @@ WinMain( PSTR lpCmdLine, INT nCmdShow) { + open_err_file(); + // Window Setup win32_window_create( &win32_main_window, @@ -189,6 +191,7 @@ WinMain( 900, win32_window_event_handler ); + win32_window_update_dim(&win32_main_window); win32_time_init(); win32_files_init(); @@ -230,6 +233,15 @@ WinMain( // using invalid resources if (!running || !has_flag(state->flags, AppState_IsRunning)) continue; + // Update window size + if (has_flag(state->flags, AppState_RunEditor)) + { + state->editor->window_dim = v2{ + (r32)win32_main_window.info.width, + (r32)win32_main_window.info.height + }; + } + lumenarium_frame(state); SwapBuffers(win32_main_window.dc); @@ -261,6 +273,8 @@ WinMain( // windows cleanup UnregisterClass(win32_main_window.window_class.lpszClassName, hInstance); + + close_err_file(); return 0; } diff --git a/src_v2/platform/win32/lumenarium_win32_graphics.cpp b/src_v2/platform/win32/lumenarium_win32_graphics.cpp index 59e04dc..848d91d 100644 --- a/src_v2/platform/win32/lumenarium_win32_graphics.cpp +++ b/src_v2/platform/win32/lumenarium_win32_graphics.cpp @@ -1,12 +1,16 @@ -#define win32_gl_no_error() win32_gl_no_error_() -void win32_gl_no_error_() { +#define win32_gl_no_error() win32_gl_no_error_(__FILE__, __LINE__) +void win32_gl_no_error_(char* file, u32 line) { u32 error = glGetError(); char* str = 0; if (error) { str = win32_gl_error_to_string(error); } - assert(error == 0); + if (error != 0) + { + err_write("OpenGL error: %s:%d\n\t%s :: %d\n", file, line, str, error); + invalid_code_path; + } } Platform_Geometry_Buffer @@ -64,7 +68,7 @@ platform_shader_create( { // errors GLint shader_vert_compiled; gl.glGetShaderiv(shader_vert, GL_COMPILE_STATUS, &shader_vert_compiled); - if (shader_vert_compiled != GL_TRUE) + if (!shader_vert_compiled) { GLsizei log_length = 0; GLchar message[1024]; @@ -79,7 +83,7 @@ platform_shader_create( { // errors GLint shader_frag_compiled; gl.glGetShaderiv(shader_frag, GL_COMPILE_STATUS, &shader_frag_compiled); - if (shader_frag_compiled != GL_TRUE) + if (!shader_frag_compiled) { GLsizei log_length = 0; GLchar message[1024]; @@ -122,7 +126,7 @@ platform_shader_create( void platform_geometry_bind(Platform_Geometry_Buffer geo) { - gl.glBindVertexArray(geo.buffer_id_vertices); + gl.glBindVertexArray(geo.buffer_id_vao); win32_gl_no_error(); gl.glBindBuffer(GL_ARRAY_BUFFER, geo.buffer_id_vertices); @@ -155,15 +159,76 @@ platform_geometry_draw( } void platform_vertex_attrib_pointer( - Platform_Geometry_Buffer geo, Platform_Shader shader, u32 attr_index + Platform_Geometry_Buffer geo, Platform_Shader shader, GLuint count, GLuint attr_index, GLuint stride, GLuint offset ){ platform_geometry_bind(geo); - gl.glVertexAttribPointer(shader.attrs[attr_index], 4, GL_FLOAT, false, 0, 0); + gl.glVertexAttribPointer(shader.attrs[attr_index], count, GL_FLOAT, false, stride * sizeof(float), (void*)(offset * sizeof(float))); win32_gl_no_error(); gl.glEnableVertexAttribArray(shader.attrs[attr_index]); win32_gl_no_error(); } +Platform_Texture +platform_texture_create(u8* pixels, u32 width, u32 height, u32 stride) +{ + Platform_Texture result = {}; + glGenTextures(1, &result.id); + win32_gl_no_error(); + + glBindTexture(GL_TEXTURE_2D, result.id); + win32_gl_no_error(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + win32_gl_no_error(); + + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + width, + height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + pixels + ); + win32_gl_no_error(); + + result.w = width; + result.h = height; + result.s = stride; + + return result; +} + +void +platform_texture_update(Platform_Texture tex, u8* new_pixels, u32 width, u32 height, u32 stride) +{ + // NOTE(PS): this function simply replaces the entire image + // we can write a more granular version if we need it + + assert(tex.w == width && tex.h == height && tex.s == stride); + platform_texture_bind(tex); + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, 0, // offset + width, height, + GL_RGBA, + GL_UNSIGNED_BYTE, + new_pixels + ); +} + +void +platform_texture_bind(Platform_Texture tex) +{ + glBindTexture(GL_TEXTURE_2D, tex.id); + win32_gl_no_error(); +} void platform_frame_begin(Platform_Graphics_Frame_Desc desc) @@ -180,10 +245,10 @@ platform_frame_begin(Platform_Graphics_Frame_Desc desc) glDisable(GL_CULL_FACE); - //glDisable(GL_TEXTURE_2D); - glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); + + win32_gl_no_error(); } void diff --git a/src_v2/platform/win32/lumenarium_win32_window.cpp b/src_v2/platform/win32/lumenarium_win32_window.cpp index cf37008..54ca09f 100644 --- a/src_v2/platform/win32/lumenarium_win32_window.cpp +++ b/src_v2/platform/win32/lumenarium_win32_window.cpp @@ -96,7 +96,6 @@ win32_window_create( hinstance, 0 ); - return true; } return false; @@ -333,12 +332,8 @@ win32_window_opengl_ctx_create_ext(HDC dc, Win32_Window_OpenGL_Info* info) invalid_code_path; } -#if 0 - //char* version_string = (char*)glGetString(GL_VERSION); - OutputDebugStringA("OpenGL Version: "); - OutputDebugStringA(version_string); - OutputDebugStringA("\n"); -#endif + char* version_string = (char*)glGetString(GL_VERSION); + err_write("OpenGL Version: %s\n", version_string); } internal void From 6e80c811cf73d9b301ce796b734de5811d6e314c Mon Sep 17 00:00:00 2001 From: PS Date: Wed, 6 Apr 2022 20:29:32 +0200 Subject: [PATCH 118/151] Paged memory allocator, and async file system --- admin.txt | 18 +- build/build_.sh | 2 +- run_tree/data/font.ttf | Bin 0 -> 158080 bytes run_tree/wasm/debug/lumenarium.wasm | Bin 57484 -> 0 bytes run_tree/win32/intel/debug/debug.rdbg | Bin 1206 -> 1488 bytes .../intel/debug/lumenarium_first_win32.obj | Bin 160688 -> 246446 bytes src_v2/editor/lumenarium_editor.cpp | 26 +- src_v2/editor/lumenarium_editor.h | 1 + src_v2/editor/lumenarium_editor_renderer.cpp | 13 +- src_v2/editor/lumenarium_editor_ui.cpp | 452 ++++++++++++++++++ src_v2/editor/lumenarium_editor_ui.h | 175 +++++++ src_v2/editor/lumenarium_interface.cpp | 1 - src_v2/editor/lumenarium_ui.h | 36 -- src_v2/engine/lumenarium_engine.cpp | 50 ++ src_v2/engine/lumenarium_engine_assembly.h | 3 + src_v2/engine/lumenarium_engine_output.cpp | 24 + src_v2/engine/lumenarium_engine_output.h | 67 +++ .../engine/output/lumenarium_output_sacn.cpp | 207 ++++++++ src_v2/engine/output/lumenarium_output_sacn.h | 22 + .../engine/output/lumenarium_output_uart.cpp | 12 + src_v2/engine/output/lumenarium_output_uart.h | 9 + src_v2/libs/HandmadeMath.h | 19 + src_v2/lumenarium_first.cpp | 20 +- src_v2/lumenarium_first.h | 15 +- src_v2/lumenarium_hash.cpp | 58 +++ src_v2/lumenarium_input.cpp | 34 +- src_v2/lumenarium_memory.cpp | 387 +++++++++++++-- src_v2/lumenarium_memory.h | 45 ++ src_v2/lumenarium_string.cpp | 26 +- src_v2/lumenarium_tests.cpp | 86 ++++ src_v2/lumenarium_texture_atlas.cpp | 128 +++++ src_v2/lumenarium_types.h | 200 +++++++- src_v2/platform/lumenarium_assert.h | 7 +- src_v2/platform/lumenarium_platform.h | 150 +++++- .../platform/wasm/lumenarium_wasm_webgl.cpp | 29 +- .../platform/win32/lumenarium_first_win32.cpp | 15 + .../platform/win32/lumenarium_win32_file.cpp | 33 ++ .../win32/lumenarium_win32_graphics.cpp | 83 +++- .../win32/lumenarium_win32_memory.cpp | 8 +- .../platform/win32/lumenarium_win32_opengl.h | 7 +- .../win32/lumenarium_win32_window.cpp | 3 + src_v2/user_space/user_space_incenter.cpp | 2 - 42 files changed, 2334 insertions(+), 139 deletions(-) create mode 100644 run_tree/data/font.ttf delete mode 100644 run_tree/wasm/debug/lumenarium.wasm create mode 100644 src_v2/editor/lumenarium_editor_ui.cpp create mode 100644 src_v2/editor/lumenarium_editor_ui.h delete mode 100644 src_v2/editor/lumenarium_interface.cpp delete mode 100644 src_v2/editor/lumenarium_ui.h create mode 100644 src_v2/engine/lumenarium_engine_output.cpp create mode 100644 src_v2/engine/lumenarium_engine_output.h create mode 100644 src_v2/engine/output/lumenarium_output_sacn.cpp create mode 100644 src_v2/engine/output/lumenarium_output_sacn.h create mode 100644 src_v2/engine/output/lumenarium_output_uart.cpp create mode 100644 src_v2/engine/output/lumenarium_output_uart.h create mode 100644 src_v2/lumenarium_hash.cpp create mode 100644 src_v2/lumenarium_memory.h create mode 100644 src_v2/lumenarium_texture_atlas.cpp diff --git a/admin.txt b/admin.txt index 8586538..98ecab0 100644 --- a/admin.txt +++ b/admin.txt @@ -7,14 +7,20 @@ ## TODO -1. Upgrade Lumenarium's plubming - 1. switch over to compiling with clang & bash based build scripts - 2. better platform layer separation +1. Upgrade Lumenarium's plumbing + x. switch over to compiling with clang & bash based build scripts + x. better platform layer separation 3. osx and webgl layers, possibly linux? - 4. remove dll compiling, just build all in one go + x. remove dll compiling, just build all in one go 5. improve ui - 1. default to using a basic layout object - 2. improve widget handling (see a_trick_of_fate) + [ ] get widgets and widget ids working + - see a trick of fate + [ ] clip widgets to regions + [ ] text rendering + [ ] interaction + [ ] layout manager + - do layout the way youre doing styling - optional pointer to a struct + and fallback on some global default 2. Incenter 1. Sculpture generation from list of lat-long coordinates diff --git a/build/build_.sh b/build/build_.sh index b7d0978..81df051 100644 --- a/build/build_.sh +++ b/build/build_.sh @@ -144,7 +144,7 @@ CompilerFlags_DEBUG_win32="" add_flag CompilerFlags_DEBUG_win32 "-Od" # add_flag CompilerFlags_DEBUG_win32 "-Zi" # add_flag CompilerFlags_DEBUG_win32 "-DDEBUG" # -add_flag CompilerFlags_DEBUG_win32 "-DPRINT_ASSERTS" +# add_flag CompilerFlags_DEBUG_win32 "-DPRINT_ASSERTS" CompilerFlags_DEBUG="-O0" add_flag CompilerFlags_DEBUG "-g" # diff --git a/run_tree/data/font.ttf b/run_tree/data/font.ttf new file mode 100644 index 0000000000000000000000000000000000000000..06aafc067310fba0d33b0a133654475ac73388ab GIT binary patch literal 158080 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

~51kCJv>^F~QVhnxG5_ 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 Y9YEr9)1Y)}VZFzZ{KL)vpS;8W0h1O{P5=M^ diff --git a/run_tree/win32/intel/debug/debug.rdbg b/run_tree/win32/intel/debug/debug.rdbg index f78c41edc47c99c5665b943ccc884228d58a7102..24dbe61ef213c4152686ca14906596a6c51d642e 100644 GIT binary patch delta 356 zcmdnSd4YRE7%MLW1B1ilhfL~|=QHvORFsq!rN$?g*3mLdChcb5nEk ziz-37{lH>6ObXb<#LF}DQu50|>KK8}1OX5YH$R9E$OB<^AkNJ%El!OukO4A_OA<>` zb?q|q3Q9}j!5qD`qQuBA#R-inXBMf8|y^V817z;N81H3=oAU2+Shl!KXck)J{$SfxQ z$t+AElMgVu0I}xe2u5*Mz2t%d&&dZFHGn*oiBG-kfu=F=05Qne#RCO0^U`&I6dO>yD7CmWXYw4DJVu7eW~?TY6IeMIc_ufq NdI4Fitc*Yvi~vS@Gd=(S diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj index f70d1299c12b73a147d011c4127b17a69051d413..848a54baeada618f9cba1429eba09517b66f8569 100644 GIT binary patch literal 246446 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[0], 6, 0); @@ -90,30 +90,38 @@ void make_quad(Platform_Geometry_Buffer* geo, Platform_Shader* shd, Platform_Tex *tex = platform_texture_create((u8*)pix, 4, 4, 4); } +internal void +ed_load_font_cb(Platform_File_Async_Job_Args result) +{ + s32 x = 5; +} + internal void ed_init(App_State* state) { Editor* editor = allocator_alloc_struct(permanent, Editor); state->editor = editor; + state->editor->ui = ui_create(4096, 4096, state->input_state, permanent); + + // make the default quad for us to draw with + // TODO(PS): this might be unnecessary with the per-frame buffer we use now make_quad(&editor->renderer.geo, &editor->renderer.shd, &editor->renderer.tex); + + platform_file_async_read(lit_str("data/font.ttf"), ed_load_font_cb); + } internal void ed_frame_prepare(App_State* state) { - + ui_frame_prepare(&state->editor->ui, state->editor->window_dim); } internal void ed_frame(App_State* state) { - for (u32 i = 0; i < 16; i++) - { - if (i % 2 == 1) continue; - pix[i] += 1; - } - platform_texture_update(state->editor->renderer.tex, (u8*)pix, 4, 4, 4); - + edr_render_begin(state); + ui_draw(&state->editor->ui); edr_render(state); } diff --git a/src_v2/editor/lumenarium_editor.h b/src_v2/editor/lumenarium_editor.h index 08ab6df..9cd17c2 100644 --- a/src_v2/editor/lumenarium_editor.h +++ b/src_v2/editor/lumenarium_editor.h @@ -7,6 +7,7 @@ struct Editor { v2 window_dim; Editor_Renderer renderer; + UI ui; }; #endif //LUMENARIUM_EDITOR_H diff --git a/src_v2/editor/lumenarium_editor_renderer.cpp b/src_v2/editor/lumenarium_editor_renderer.cpp index deedaa7..67d9e34 100644 --- a/src_v2/editor/lumenarium_editor_renderer.cpp +++ b/src_v2/editor/lumenarium_editor_renderer.cpp @@ -1,7 +1,6 @@ - internal void -edr_render(App_State* state) +edr_render_begin(App_State* state) { Platform_Graphics_Frame_Desc desc = {}; desc.clear_color = { 0.1f, 0.1f, 0.1f, 1 }; @@ -9,9 +8,15 @@ edr_render(App_State* state) desc.viewport_max = state->editor->window_dim; platform_frame_begin(desc); platform_frame_clear(); - +} + +internal void +edr_render(App_State* state) +{ +#if 0 platform_geometry_bind(state->editor->renderer.geo); - platform_texture_bind(state->editor->renderer.tex); + platform_texture_bind(state->editor->ui.atlas_texture); platform_shader_bind(state->editor->renderer.shd); platform_geometry_draw(state->editor->renderer.geo); +#endif } \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_ui.cpp b/src_v2/editor/lumenarium_editor_ui.cpp new file mode 100644 index 0000000..75ffc24 --- /dev/null +++ b/src_v2/editor/lumenarium_editor_ui.cpp @@ -0,0 +1,452 @@ +#define WHITE_SPRITE_ID 511 + +static String ui_shader_vert_win32 = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec4 a_pos;\n" + "layout (location = 1) in vec2 a_uv;\n" + "layout (location = 2) in vec4 a_color;\n" + "out vec2 uv;\n" + "out vec4 color;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * a_pos;\n" + " uv = a_uv;\n" + " color = a_color;\n" + "}" + ); + +static String ui_shader_frag_win32 = lit_str( + "#version 330 core\n" + "in vec2 uv;\n" + "in vec4 color;\n" + "out vec4 FragColor;\n" + "uniform sampler2D texture;\n" + "void main(void) {\n" + " FragColor = texture(texture, uv) * color;\n" + " if (FragColor.w <= 0.01f) discard;\n" + "}" + ); + +internal UI +ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) +{ + UI result = {}; + result.input = input; + + // Widgets + result.widgets.free = allocator_alloc_array(a, UI_Widget, widget_pool_cap); + result.widgets.free_cap = widget_pool_cap; + + // Per Frame Vertex Buffer + result.verts_cap = verts_cap; + result.verts = allocator_alloc_array(a, UI_Vertex, verts_cap); + result.indices_cap = verts_cap * 2; + result.indices = allocator_alloc_array(a, u32, result.indices_cap); + + result.per_frame_buffer = platform_geometry_buffer_create( + (r32*)result.verts, + result.verts_cap, + result.indices, + result.indices_cap + ); + + String attrs[] = { lit_str("a_pos"), lit_str("a_uv"), lit_str("a_color") }; + String uniforms[] = { lit_str("proj") }; + result.shader = platform_shader_create( + ui_shader_vert_win32, + ui_shader_frag_win32, + attrs, 3, + uniforms, 1 + ); + + platform_vertex_attrib_pointer( + result.per_frame_buffer, result.shader, 4, result.shader.attrs[0], 10, 0 + ); + platform_vertex_attrib_pointer( + result.per_frame_buffer, result.shader, 2, result.shader.attrs[1], 10, 4 + ); + platform_vertex_attrib_pointer( + result.per_frame_buffer, result.shader, 4, result.shader.attrs[2], 10, 6 + ); + + // Texture Atlas + result.atlas = texture_atlas_create(1024, 1024, 512, permanent); + result.atlas_texture = platform_texture_create(result.atlas.pixels, 1024, 1024, 1024); + + u32 white_sprite[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + ui_sprite_register(&result, (u8*)white_sprite, 2, 2, WHITE_SPRITE_ID); + + return result; +} + +internal u32 +ui_vert_push(UI* ui, v3 p, v2 t, v4 c) +{ + assert(ui->verts_len < ui->verts_cap); + u32 index = ui->verts_len++; + + ui->verts[index].pos = {p.x, p.y, p.z, 1}; + ui->verts[index].uv = t; + ui->verts[index].color = c; + + return index; +} + +internal void +ui_index_push(UI* ui, u32 i) +{ + assert(ui->indices_len < ui->indices_cap); + ui->indices[ui->indices_len++] = i; +} + +internal void +ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c) +{ + u32 bl = ui_vert_push(ui, pmin, tmin, c); + u32 br = ui_vert_push(ui, v3{pmax.x, pmin.y, pmin.z}, v2{tmax.x,tmin.y}, c); + u32 tr = ui_vert_push(ui, pmax, tmax, c); + u32 tl = ui_vert_push(ui, v3{pmin.x, pmax.y, pmin.z}, v2{tmin.x,tmax.y}, c); + + ui_index_push(ui, bl); + ui_index_push(ui, br); + ui_index_push(ui, tr); + + ui_index_push(ui, bl); + ui_index_push(ui, tr); + ui_index_push(ui, tl); +} + +internal void +ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id) +{ + texture_atlas_register(&ui->atlas, pixels, w, h, id); + platform_texture_update(ui->atlas_texture, ui->atlas.pixels, ui->atlas.width, ui->atlas.height, ui->atlas.width); +} + +internal void +ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color) +{ + v4 uv = texture_atlas_sprite_get_uvs(&ui->atlas, id); + ui_quad_push(ui, pmin, pmax, uv.xy, uv.zw, color); +} + +internal void +ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id) +{ + ui_sprite_push(ui, pmin, pmax, id, v4{1,1,1,1}); +} + +internal void +ui_frame_prepare(UI* ui, v2 window_dim) +{ + ui->verts_len = 0; + ui->indices_len = 0; + + ui->widgets.free_len = 0; + ui->widgets.active_parent = 0; + ui->widgets.root = ui_widget_pool_push(&ui->widgets, lit_str("root")); + ui->widgets.active_parent = ui->widgets.root; + + v2 half_d = window_dim * 0.5f; + ui->proj = HMM_Orthographic(0, window_dim.x, window_dim.y, 0, 0.01f, 100); + + if (ui->widget_next_hot.value != 0) + { + ui->widget_next_hot_frames += 1; + if (ui->widget_hot_frames > 1) + { + ui->widget_next_hot = UI_Widget_Id{0}; + } + } + if (ui->widget_hot.value != 0) + { + ui->widget_hot_frames += 1; + if (ui->widget_hot_frames > 1) ui->widget_hot = UI_Widget_Id{0}; + } +} + +global bool show = false; + +internal void +ui_draw(UI* ui) +{ + UI_Widget_Desc d0 = { + { + (UIWidgetStyle_Bg), + WHITE_V4, + BLACK_V4, + WHITE_SPRITE_ID, + }, + lit_str("Hi there!"), + v2{ 32.0f, 32.0f }, + v2{ 128.0f, 64.0f }, + }; + UI_Widget_Result r0 = ui_widget_push(ui, d0); + + UI_Widget_Desc d1 = d0; + d1.style.flags |= UIWidgetStyle_Outline | UIWidgetStyle_MouseClick; + d1.style.color_bg = PINK_V4; + d1.style.color_fg = GREEN_V4; + d1.p_min = v2{ 512, 32 }; + d1.p_max = v2{ 640, 128 }; + d1.string = lit_str("Hello"); + UI_Widget_Result r1 = ui_widget_push(ui, d1); + bool clicked_r1 = has_flag(r1.flags, UIWidgetResult_MouseLeft_WentUp); + if (clicked_r1) show = !show; + + UI_Widget_Result r2 = {}; + if (show) + { + UI_Widget_Desc d2 = d1; + d1.string = lit_str("Hello There"); + d1.p_min = v2{ 560, 64 }; + d1.p_max = v2{ 700, 256 }; + r2 = ui_widget_push(ui, d1); + } + + bool clicked_r2 = has_flag(r2.flags, UIWidgetResult_MouseLeft_WentUp); + assert( + (!clicked_r1 && !clicked_r2) || + (clicked_r1 && !clicked_r2) || + (!clicked_r1 && clicked_r2) + ); + + u32 widget_count = ui->widgets.free_len; + r32 range_min = -10; + r32 range_max = -1; + r32 range_step = (range_max - range_min) / (r32)widget_count; + ui_widgets_to_geometry_recursive(ui, ui->widgets.root, -10, range_step); + + platform_geometry_buffer_update( + &ui->per_frame_buffer, + (r32*)ui->verts, + 0, + ui->verts_len * 10, + ui->indices, + 0, + ui->indices_len + ); + platform_shader_bind(ui->shader); + platform_set_uniform(ui->shader, 0, ui->proj); + platform_texture_bind(ui->atlas_texture); + platform_geometry_bind(ui->per_frame_buffer); + platform_geometry_draw(ui->per_frame_buffer, ui->indices_len); +} + +//////////////////////////////////////////// +// Widgets + +internal UI_Widget_Id +ui_widget_id_create(String string, u32 index) +{ + assert(string.len != 0 && string.str != 0); + UI_Widget_Id result = {}; + result.value = hash_djb2_to_u32(string); + result.index = index; + return result; +} + +internal UI_Widget* +ui_widget_pool_push(UI_Widget_Pool* pool, String string) +{ + assert(pool->free_len < pool->free_cap); + UI_Widget* result = pool->free + pool->free_len++; + + result->id = ui_widget_id_create(string, pool->free_len); + result->parent = 0; + result->next = 0; + result->child_first = 0; + result->child_last = 0; + + if (pool->active_parent) + { + result->parent = pool->active_parent; + sll_push( + pool->active_parent->child_first, + pool->active_parent->child_last, + result + ); + } + + return result; +} + +internal void +ui_widget_pool_pop(UI_Widget_Pool* pool) +{ + if (pool->active_parent->parent) + { + pool->active_parent = pool->active_parent->parent; + } +} + +internal bool +ui_widget_id_equals(UI_Widget_Id a, UI_Widget_Id b) +{ + return (a.value == b.value); +} + +internal bool +ui_widget_id_is_valid(UI_Widget_Id h) +{ + return h.value != 0; +} + +internal void +ui_widget_next_hot_set(UI* ui, UI_Widget* w) +{ + ui->widget_next_hot = w->id; + ui->widget_next_hot_frames = 0; +} + +internal void +ui_widget_hot_set(UI* ui, UI_Widget* w) +{ + ui->widget_hot = w->id; + ui->widget_hot_frames = 0; +} + +internal UI_Widget_Result +ui_widget_push(UI* ui, UI_Widget_Desc desc) +{ + UI_Widget_Result result = {}; + + UI_Widget* w = ui_widget_pool_push(&ui->widgets, desc.string); + w->desc = desc; + + if (has_flag(desc.style.flags, UIWidgetStyle_MouseClick)) + { + // CASES: + // Mouse Over | Mouse Clicked | Is Next Hot | Response + // f | f | t | clear next hot + // f | f | f | do nothing + // f | t | f | do nothing + // t | f | f | beome next hot + // t | t | f | become next hot + // t | t | t | become hot + + v2 mouse_p = ui->input->frame_hot->mouse_pos; + bool mouse_over = ( + mouse_p.x >= desc.p_min.x && mouse_p.x <= desc.p_max.x && + mouse_p.y >= desc.p_min.y && mouse_p.y <= desc.p_max.y + ); + + if (mouse_over) + { + if (ui_widget_id_equals(w->id, ui->widget_next_hot)) + { + if (input_key_is_down(ui->input, KeyCode_MouseLeftButton)) + { + ui_widget_hot_set(ui, w); + result.flags |= UIWidgetResult_MouseLeft_IsDown; + } + if (input_key_went_up(ui->input, KeyCode_MouseLeftButton)) + { + result.flags |= UIWidgetResult_MouseLeft_WentUp; + ui->widget_hot = UI_Widget_Id{0}; + } + } + else if ((w->id.index >= ui->widget_next_hot.index) && ui->widget_hot.value == 0) + { + ui_widget_next_hot_set(ui, w); + } + } + else + { + if (ui_widget_id_equals(w->id, ui->widget_next_hot)) + { + ui->widget_next_hot = UI_Widget_Id{0}; + } + } + } + + return result; +} + +internal void +ui_widget_pop(UI* ui, UI_Widget* widget) +{ + assert(ui_widget_id_equals(widget->id, ui->widgets.active_parent->id)); + ui_widget_pool_pop(&ui->widgets); +} + +internal r32 +ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_step) +{ + r32 z_at = z_start; + for (UI_Widget* child = widget->child_first; child != 0; child = child->next) + { + UI_Widget_Desc desc = child->desc; + v3 bg_min = v2_to_v3(desc.p_min, z_at); + v3 bg_max = v2_to_v3(desc.p_max, z_at); + + v4 color_fg = desc.style.color_fg; + v4 color_bg = desc.style.color_bg; + if (ui_widget_id_equals(ui->widget_next_hot, child->id)) + { + color_fg = desc.style.color_bg; + color_bg = desc.style.color_fg; + } + if (ui_widget_id_equals(ui->widget_hot, child->id)) + { + color_fg = desc.style.color_fg; + color_bg = desc.style.color_bg; + } + + if (has_flag(child->desc.style.flags, UIWidgetStyle_Outline)) + { + ui_sprite_push(ui, bg_min, bg_max, WHITE_SPRITE_ID, color_fg); + bg_min += v3{ 3, 3, 0}; + bg_max -= v3{ 3, 3, 0}; + } + + if (has_flag(child->desc.style.flags, UIWidgetStyle_Bg)) + { + bg_min.z += z_step; + bg_max.z += z_step; + ui_sprite_push(ui, bg_min, bg_max, desc.style.sprite, color_bg); + } + + if (has_flag(child->desc.style.flags, UIWidgetStyle_Text)) + { + // TODO(PS): + } + + if (child->child_first) + { + z_at = ui_widgets_to_geometry_recursive(ui, child, z_at + z_step, z_step); + } + + z_at += z_step; + } + return z_at; +} + +/////////////////////////////////////////// +// Specific Widget Implementations + +global UI_Style_Sheet ui_default_style_sheet = { + { + + } +}; + +internal UI_Widget_Style +ui_get_style(UI* ui, UI_Widget_Kind kind) +{ + if (ui->style_sheet) return ui->style_sheet->styles[kind]; + return ui_default_style_sheet.styles[kind]; +} + +internal void +ui_text(UI* ui, String string) +{ + +} + +internal bool +ui_button(UI* ui, String string) +{ + return false; +} + diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h new file mode 100644 index 0000000..d2254c2 --- /dev/null +++ b/src_v2/editor/lumenarium_editor_ui.h @@ -0,0 +1,175 @@ +/* date = March 28th 2022 10:52 pm */ + +#ifndef LUMENARIUM_UI_H +#define LUMENARIUM_UI_H + +///////////////////////////////////////////////////////////// +// Interface + +struct UI_Vertex +{ + v4 pos; + v2 uv; + v4 color; +}; + +#define UI_WIDGET_ID_VALID_BIT 1 << 31 + +union UI_Widget_Id +{ + // equality of widget id's only relies on the value field + // which is a hash of the widget's string + u32 value; + + // this struct tracks the index of the widget only to be able + // to override next hot in cases where an earlier element became + // next hot that is in the same location as the current one + u32 index; +}; + +typedef u32 UI_Widget_Style_Flags; +enum +{ + UIWidgetStyle_None = 0, + UIWidgetStyle_Bg = 1, + UIWidgetStyle_Text = 2, + UIWidgetStyle_Outline = 4, + UIWidgetStyle_MouseClick = 8, + UIWidgetStyle_MouseDrag = 16, +}; + +// akin to a css class, could be used to style multiple +// elements +struct UI_Widget_Style +{ + UI_Widget_Style_Flags flags; + v4 color_bg; + v4 color_fg; + + u32 sprite; +}; + +// combination of style info and per-instance data +struct UI_Widget_Desc +{ + UI_Widget_Style style; + String string; + v2 p_min; + v2 p_max; +}; + +struct UI_Widget +{ + UI_Widget_Id id; + UI_Widget_Desc desc; + + UI_Widget* parent; + UI_Widget* next; + UI_Widget* child_first; + UI_Widget* child_last; +}; + +typedef u32 UI_Widget_Result_Flags; +enum +{ + UIWidgetResult_None = 0, + UIWidgetResult_MouseLeft_IsDown = 1, + UIWidgetResult_MouseLeft_WentUp = 2, +}; + +struct UI_Widget_Result +{ + UI_Widget_Result_Flags flags; +}; + +enum UI_Widget_Kind +{ + UIWidget_Text, + + // Buttons + UIWidget_Button, + UIWidget_Toggle, + UIWidget_Menu, + UIWidget_Dropdown, + + // Sliders + UIWidget_HSlider, + UIWidget_VSlider, + UIWidget_HScroll, + UIWidget_VScroll, + + // Panels + UIWidget_Window, + + UIWidget_Count, +}; + +struct UI_Style_Sheet +{ + UI_Widget_Style styles[UIWidget_Count]; +}; + +struct UI_Widget_Pool +{ + UI_Widget* free; + u32 free_cap; + u32 free_len; + + UI_Widget* root; + UI_Widget* active_parent; +}; + +struct UI +{ + UI_Vertex* verts; + u32 verts_len; + u32 verts_cap; + + u32* indices; + u32 indices_len; + u32 indices_cap; + + Texture_Atlas atlas; + + UI_Widget_Pool widgets; + UI_Style_Sheet* style_sheet; + + UI_Widget_Id widget_next_hot; + UI_Widget_Id widget_hot; + + // frames since these values were set + u16 widget_next_hot_frames; + u16 widget_hot_frames; + + Input_State* input; + + m44 proj; + Platform_Shader shader; + Platform_Texture atlas_texture; + Platform_Geometry_Buffer per_frame_buffer; +}; + +// Interface + +internal UI ui_create(); +internal void ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c); +internal void ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id); +internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color); +internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id); +internal void ui_draw(UI* ui); + +// Widgets + +internal UI_Widget_Id ui_widget_id_create(u32 index_in_parent, String string); +internal bool ui_widget_id_equals(UI_Widget_Id a, UI_Widget_Id b); +internal bool ui_widget_id_is_valid(UI_Widget_Id h); + +internal UI_Widget* ui_widget_pool_push(UI_Widget_Pool* pool, String string); +internal void ui_widget_pool_pop(UI_Widget_Pool* pool); + +internal UI_Widget_Result ui_widget_push(UI* ui, UI_Widget_Desc desc); +internal void ui_widget_pop(UI* ui, UI_Widget* widget); + +internal r32 ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_step); + +#endif //LUMENARIUM_UI_H diff --git a/src_v2/editor/lumenarium_interface.cpp b/src_v2/editor/lumenarium_interface.cpp deleted file mode 100644 index ab0c014..0000000 --- a/src_v2/editor/lumenarium_interface.cpp +++ /dev/null @@ -1 +0,0 @@ -// \ No newline at end of file diff --git a/src_v2/editor/lumenarium_ui.h b/src_v2/editor/lumenarium_ui.h deleted file mode 100644 index 72c6af8..0000000 --- a/src_v2/editor/lumenarium_ui.h +++ /dev/null @@ -1,36 +0,0 @@ -/* date = March 28th 2022 10:52 pm */ - -#ifndef LUMENARIUM_UI_H -#define LUMENARIUM_UI_H - -struct Font_Glyph -{ - u32 code_point; - v2 uv_min; - v2 uv_max; -}; - -struct Font_Glyph_Table -{ - Font_Glyph* values; - u32 cap; - u32 len; -}; - -struct Font_Bitmap -{ - u8* pixels; - u32 width; - u32 height; - u32 stride; - - Platform_Texture texture; -}; - -struct Font -{ - Font_Glyph_Table glyphs; - Font_Bitmap bitmap; -}; - -#endif //LUMENARIUM_UI_H diff --git a/src_v2/engine/lumenarium_engine.cpp b/src_v2/engine/lumenarium_engine.cpp index 9870e8b..2f9bb08 100644 --- a/src_v2/engine/lumenarium_engine.cpp +++ b/src_v2/engine/lumenarium_engine.cpp @@ -3,6 +3,10 @@ internal void en_init(App_State* state, App_Init_Desc desc) { state->assemblies = assembly_array_create(permanent, desc.assembly_cap); + + Output* o = &state->output; + register_output_method(o, OutputData_NetworkSACN, output_network_sacn_build, output_network_sacn_init()); + register_output_method(o, OutputData_ComUART, output_network_sacn_build, output_com_uart_init()); } internal void @@ -15,6 +19,52 @@ internal void en_frame(App_State* state) { + /////////////////////////////////////// + // Output Data + + Output_Data_Queue output_queue = {}; + output_queue.a = scratch; + + // build output data buffers + Output_Methods methods; + Assembly_Array assemblies = state->assemblies; + for (u32 i = 0; i < assemblies.len; i++) + { + Assembly_Strip_Array strips = assemblies.strip_arrays[i]; + for (u32 j = 0; j < strips.len; j++) + { + Assembly_Strip* strip = strips.strips + j; + Build_Output_Data_Buffer* method_proc = methods.procs[strip->output_kind]; + u8* method_data = methods.method_data[strip->output_kind]; + if (method_proc == 0) continue; + method_proc(state, i, strip, method_data, &output_queue); + } + } + + // output the buffers + // TODO(PS): we could sort these first if switchig between output + // types is time consuming + for (Output_Data* at = output_queue.first; at != 0; at = at->next) + { + // NOTE(PS): we can overload each switch case as more methods come in + // ie. OutputData_NetworkSACN and OutputData_NetworkArtNet could use + // the same case since at this point they just need to push data out + // over a socket + switch (at->kind) + { + case OutputData_NetworkSACN: + { + // TODO(PS): platform network io + } break; + + case OutputData_ComUART: + { + // TODO(PS): platform com io + } break; + + invalid_code_path; + } + } } internal void diff --git a/src_v2/engine/lumenarium_engine_assembly.h b/src_v2/engine/lumenarium_engine_assembly.h index f105ff9..2c94fac 100644 --- a/src_v2/engine/lumenarium_engine_assembly.h +++ b/src_v2/engine/lumenarium_engine_assembly.h @@ -42,6 +42,9 @@ struct Assembly_Strip u32 pixels_len; // array of indices into the Assembly_Pixel_Buffer for the same assembly u32* pixels; + + Output_Data_Kind output_kind; + u32 sacn_universe; }; struct Assembly_Strip_Array diff --git a/src_v2/engine/lumenarium_engine_output.cpp b/src_v2/engine/lumenarium_engine_output.cpp new file mode 100644 index 0000000..92c23ad --- /dev/null +++ b/src_v2/engine/lumenarium_engine_output.cpp @@ -0,0 +1,24 @@ + +internal void +register_output_method(Output* output, Output_Data_Kind kind, Build_Output_Data_Buffer* proc, u8* method_data) +{ + output->methods.procs[kind] = proc; + output->methods.method_data[kind] = method_data; +} + +internal Output_Data* +output_data_queue_push(Output_Data_Queue* q, u32 size, Output_Data_Kind kind) +{ + Output_Data* d = allocator_alloc_struct(q->a, Output_Data); + d->kind = kind; + d->data.size = size; + d->data.base = allocator_alloc(q->a, size); + return d; +} + +internal void +output_data_set_network_addr(Output_Data* d, u32 send_addr, u32 port) +{ + d->network.v4_addr = send_addr; + d->network.port = port; +} diff --git a/src_v2/engine/lumenarium_engine_output.h b/src_v2/engine/lumenarium_engine_output.h new file mode 100644 index 0000000..d8b9a76 --- /dev/null +++ b/src_v2/engine/lumenarium_engine_output.h @@ -0,0 +1,67 @@ +/* date = March 30th 2022 9:23 am */ + +#ifndef LUMENARIUM_ENGINE_OUTPUT_H +#define LUMENARIUM_ENGINE_OUTPUT_H + +typedef u8 Output_Data_Kind; +enum +{ + OutputData_Invalid = 0, + OutputData_NetworkSACN, + OutputData_ComUART, + OutputData_Count, +}; + +struct Output_Data_Network +{ + // Platform_Socket_Handle socket; + u32 v4_addr; + u32 port; +}; + +struct Output_Data_Com +{ + String port; +}; + +struct Output_Data +{ + Output_Data_Kind kind; + union + { + Output_Data_Network network; + Output_Data_Com com; + }; + Data data; + + Output_Data* next; +}; + +struct Output_Data_Queue +{ + Output_Data* first; + Output_Data* last; + u32 len; + Allocator* a; +}; + +typedef void Build_Output_Data_Buffer(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue); + +struct Output_Methods +{ + Build_Output_Data_Buffer* procs[OutputData_Count]; + u8* method_data[OutputData_Count]; +}; + +struct Output +{ + Output_Methods methods; +}; + +internal void register_output_method(Output* output, Output_Data_Kind kind, Build_Output_Data_Buffer* proc, u8* method_data); + +internal Output_Data* output_data_queue_push(Output_Data_Queue* q, u32 size, Output_Data_Kind kind); + +internal void output_data_set_network_addr(Output_Data* d, u32 send_addr, u32 port); + +#endif //LUMENARIUM_ENGINE_OUTPUT_H diff --git a/src_v2/engine/output/lumenarium_output_sacn.cpp b/src_v2/engine/output/lumenarium_output_sacn.cpp new file mode 100644 index 0000000..c7f961a --- /dev/null +++ b/src_v2/engine/output/lumenarium_output_sacn.cpp @@ -0,0 +1,207 @@ +#define SACN_DEFAULT_PORT 5568 + + +// a description of the address space being used +#define SACN_PREAMBLE_SIZE_ADDR 0 +#define SACN_POSTAMBLE_SIZE_ADDR 2 +#define SACN_ACN_IDENTIFIER_ADDR 4 +#define SACN_ROOT_FLAGS_AND_LEN_ADDR 16 +#define SACN_ROOT_VECTOR_ADDR 18 +#define SACN_CID_ADDR 22 +#define SACN_FRAMING_FLAGS_AND_LEN_ADDR 38 +#define SACN_FRAMING_VECTOR_ADDR 40 +#define SACN_SOURCE_NAME_ADDR 44 +#define SACN_PRIORITY_ADDR 108 +#define SACN_RESERVED_ADDR 109 +#define SACN_SEQ_NUM_ADDR 111 +#define SACN_OPTIONS_ADDR 112 +#define SACN_UNIVERSE_ADDR 113 +#define SACN_DMP_FLAGS_AND_LEN_ADDR 115 +#define SACN_DMP_VECTOR_ADDR 117 +#define SACN_DMP_ADDRESS_AND_DATA_ADDR 118 +#define SACN_FIRST_PROPERTY_ADDRESS_ADDR 119 +#define SACN_ADDRESS_INC_ADDR 121 +#define SACN_PROP_COUNT_ADDR 123 +#define SACN_START_CODE_ADDR 125 +#define SACN_PROP_VALUES_ADDR (SACN_START_CODE_ADDR + 1) + +// Common Sizes +#define SACN_BUFFER_HEADER_SIZE 126 +#define SACN_BUFFER_BODY_SIZE 512 +#define SACN_BUFFER_SIZE (SACN_BUFFER_HEADER_SIZE + SACN_BUFFER_BODY_SIZE) + +#define SACN_SOURCE_NAME_SIZE 64 +#define SACN_ACN_IDENTIFIER_SIZE 12 +#define SACN_RLP_PREAMBLE_SIZE 16 +#define SACN_RLP_POSTAMBLE_SIZE 0 + +// Data Definitions +#define SACN_ACN_IDENTIFIER lit_str("ASC-E1.17\0\0\0") +#define SACN_ROOT_VECTOR 4 +#define SACN_FRAMING_VECTOR 2 +#define SACN_DMP_VECTOR 2 +#define SACN_ADDRESS_AND_DATA_FORMAT 0xa1 +#define SACN_ADDR_INC 1 +#define SACN_DMP_FIRST_PROPERTY_ADDRESS_FORCE 0 +#define SACN_RESERVED_VALUE 0 + +#define SACN_VHD_L_FLAG 0x80 +#define SACN_VHD_V_FLAG 0x40 +#define SACN_VHD_H_FLAG 0x20 +#define SACN_VHD_D_FLAG 0x10 + +#define SACN_VHD_MAXFLAGBYTES 7 //The maximum amount of bytes used to pack the flags, len, and vector +#define SACN_VHD_MAXLEN 0x0fffff //The maximum packet length is 20 bytes long +#define SACN_VHD_MAXMINLENGTH 4095 //The highest length that will fit in the "smallest" length pack + + +internal void +sacn_vhd_pack_flags(Data_Writer* w, b8 inherit_vec, b8 inherit_head, b8 inherit_data) +{ + u8 last_byte = dw_get_u8(w) & 0x8F; + w->at -= 1; + + if (!inherit_vec) { last_byte |= SACN_VHD_V_FLAG; } + if (!inherit_head) { last_byte |= SACN_VHD_H_FLAG; } + if (!inherit_data) { last_byte |= SACN_VHD_D_FLAG; } + + dw_put_u8(w, last_byte); +} + +internal void +sacn_vhd_pack_len(Data_Writer* w, u32 len, b8 include_len) +{ + u32 len_adjusted = len; + if (include_len) + { + if (len + 1 > SACN_VHD_MAXMINLENGTH) + { + len_adjusted += 2; + } + else + { + len_adjusted += 1; + } + } + + // Mask out the length bits to keep flags intact + u8 last_byte = dw_get_u8(w) & 0x70; + w->at -= 1; + + if (len_adjusted > SACN_VHD_MAXMINLENGTH) last_byte |= SACN_VHD_L_FLAG; + + u8* pack_buffer = (u8*)&len_adjusted; + if (len_adjusted <= SACN_VHD_MAXMINLENGTH) + { + last_byte |= (pack_buffer[1] & 0x0f); + dw_put_u8(w, last_byte); + dw_put_u8(w, pack_buffer[0]); + } + else + { + last_byte |= (pack_buffer[2] & 0x0f); + dw_put_u8(w, pack_buffer[1]); + dw_put_u8(w, pack_buffer[0]); + } +} + +internal void +sacn_fill_buffer_header(Output_Data* d, u16 universe, Sacn* sacn) +{ + assert(d && d->data.size > 0); + + // TODO(PS): these should be passed in? + u16 slot_count = 0; + u8 start_code = 0; + u8 priority = 0; + u16 reserved = 0; + u8 options = 0; + + Data_Writer w = {}; + w.data = d->data; + + dw_put_u16_b(&w, SACN_RLP_PREAMBLE_SIZE); + dw_put_u16_b(&w, SACN_RLP_POSTAMBLE_SIZE); + dw_put_str(&w, SACN_ACN_IDENTIFIER); + + sacn_vhd_pack_flags(&w, false, false, false); + sacn_vhd_pack_len(&w, SACN_BUFFER_HEADER_SIZE - SACN_RLP_PREAMBLE_SIZE + slot_count, false); + + dw_put_u32_b(&w, SACN_ROOT_VECTOR); + + for (u32 i = 0; i < SACN_CID_BYTES; i++) dw_put_u8(&w, sacn->cid.bytes[i]); + + sacn_vhd_pack_flags(&w, false, false, false); + sacn_vhd_pack_len(&w, SACN_BUFFER_HEADER_SIZE - SACN_FRAMING_FLAGS_AND_LEN_ADDR + slot_count, false); + + dw_put_u32_b(&w, SACN_FRAMING_VECTOR); + dw_put_str(&w, sacn->source_name); + + dw_put_u8(&w, priority); + dw_put_u16_b(&w, reserved); + dw_put_u8(&w, 0); // Sequence Number starts at 0, gets updated + dw_put_u8(&w, options); + dw_put_u16_b(&w, universe); + + sacn_vhd_pack_flags(&w, false, false, false); + sacn_vhd_pack_len(&w, SACN_BUFFER_HEADER_SIZE - SACN_DMP_FLAGS_AND_LEN_ADDR + slot_count, false); + + dw_put_u8(&w, SACN_DMP_VECTOR); + dw_put_u8(&w, SACN_ADDRESS_AND_DATA_FORMAT); + dw_put_u8(&w, 0); // dmp first priority addr + dw_put_u8(&w, SACN_ADDR_INC); // dmp address increment + dw_put_u16_b(&w, slot_count + 1); + dw_put_u8(&w, start_code); + + assert(w.at == SACN_BUFFER_HEADER_SIZE); +} + +internal void +sacn_fill_buffer_body(Output_Data* d, u32* leds_placed) +{ + +} + +internal Sacn_Cid +sacn_string_to_cid(String str) +{ + return {}; +} + +internal u32 +sacn_universe_to_send_addr(u32 universe) +{ + return 0; +} + +internal u8* +output_network_sacn_init() +{ + Sacn* result = allocator_alloc_struct(permanent, Sacn); + // TODO(PS): get platform send socket + + String cid_str = lit_str("{67F9D986-544E-4abb-8986-D5F79382586C}"); + result->cid = sacn_string_to_cid(cid_str); + + return (u8*)result; +} + +internal void +output_network_sacn_build(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue) +{ + Sacn* sacn = (Sacn*)method_data; + + u16 universe = (u16)strip->sacn_universe; + u32 send_port = SACN_DEFAULT_PORT; + for (u32 leds_placed = 0; leds_placed < strip->pixels_len;) + { + u32 v4_send_addr = sacn_universe_to_send_addr(universe); + + Output_Data* d = output_data_queue_push(queue, SACN_BUFFER_SIZE, OutputData_NetworkSACN); + output_data_set_network_addr(d, v4_send_addr, send_port); + + sacn_fill_buffer_header(d, universe, sacn); + sacn_fill_buffer_body(d, &leds_placed); + universe += 1; + } +} diff --git a/src_v2/engine/output/lumenarium_output_sacn.h b/src_v2/engine/output/lumenarium_output_sacn.h new file mode 100644 index 0000000..568020a --- /dev/null +++ b/src_v2/engine/output/lumenarium_output_sacn.h @@ -0,0 +1,22 @@ +/* date = March 30th 2022 9:55 am */ + +#ifndef LUMENARIUM_OUTPUT_SACN_H +#define LUMENARIUM_OUTPUT_SACN_H + +#define SACN_CID_BYTES 16 +struct Sacn_Cid +{ + u8 bytes[SACN_CID_BYTES]; +}; + +struct Sacn +{ + Sacn_Cid cid; + s32 sequence_iter; + String source_name; +}; + +internal u8* output_network_sacn_init(); +internal void output_network_sacn_build(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue); + +#endif //LUMENARIUM_OUTPUT_SACN_H diff --git a/src_v2/engine/output/lumenarium_output_uart.cpp b/src_v2/engine/output/lumenarium_output_uart.cpp new file mode 100644 index 0000000..cc34f65 --- /dev/null +++ b/src_v2/engine/output/lumenarium_output_uart.cpp @@ -0,0 +1,12 @@ + +internal u8* +output_com_uart_init() +{ + return 0; +} + +internal void +output_com_uart_build(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue) +{ + +} diff --git a/src_v2/engine/output/lumenarium_output_uart.h b/src_v2/engine/output/lumenarium_output_uart.h new file mode 100644 index 0000000..c23a6aa --- /dev/null +++ b/src_v2/engine/output/lumenarium_output_uart.h @@ -0,0 +1,9 @@ +/* date = March 30th 2022 9:57 am */ + +#ifndef LUMENARIUM_OUTPUT_UART_H +#define LUMENARIUM_OUTPUT_UART_H + +internal u8* output_com_uart_init(); +internal void output_com_uart_build(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue); + +#endif //LUMENARIUM_OUTPUT_UART_H diff --git a/src_v2/libs/HandmadeMath.h b/src_v2/libs/HandmadeMath.h index c80d4c3..8f71b31 100644 --- a/src_v2/libs/HandmadeMath.h +++ b/src_v2/libs/HandmadeMath.h @@ -462,6 +462,12 @@ extern "C" float Ignored0_; float Ignored1_; }; + struct + { + hmm_vec2 xy; + float Ignored0b_; + float Ignored1b_; + }; struct { @@ -469,6 +475,12 @@ extern "C" hmm_vec2 YZ; float Ignored3_; }; + struct + { + float Ignored2b_; + hmm_vec2 yz; + float Ignored3b_; + }; struct { @@ -477,6 +489,13 @@ extern "C" hmm_vec2 ZW; }; + struct + { + float Ignored4b_; + float Ignored5b_; + hmm_vec2 zw; + }; + float Elements[4]; } hmm_vec4; diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.cpp index 003f8d5..e4f4e0d 100644 --- a/src_v2/lumenarium_first.cpp +++ b/src_v2/lumenarium_first.cpp @@ -6,8 +6,9 @@ lumenarium_init() { App_State* state = 0; - permanent = bump_allocator_create_reserve(MB(4)); + permanent = bump_allocator_create_reserve(GB(1)); scratch = bump_allocator_create_reserve(KB(64)); + platform_file_jobs_init(); run_tests(); @@ -18,7 +19,7 @@ lumenarium_init() add_flag(state->flags, AppState_IsRunning); add_flag(state->flags, AppState_RunEditor); - state->input_state = input_state_create(); + state->input_state = input_state_create(permanent); en_init(state, desc); if (has_flag(state->flags, AppState_RunEditor)) @@ -34,7 +35,7 @@ lumenarium_frame_prepare(App_State* state) { allocator_clear(scratch); - input_state_swap_frames(&state->input_state); + input_state_swap_frames(state->input_state); en_frame_prepare(state); if (has_flag(state->flags, AppState_RunEditor)) @@ -42,12 +43,14 @@ lumenarium_frame_prepare(App_State* state) ed_frame_prepare(state); } incenter_frame_prepare(state); + + platform_file_async_jobs_do_work(4); } internal void lumenarium_frame(App_State* state) { - en_frame(state); + //en_frame(state); if (has_flag(state->flags, AppState_RunEditor)) { ed_frame(state); @@ -58,7 +61,7 @@ lumenarium_frame(App_State* state) internal void lumenarium_event(Platform_Window_Event evt, App_State* state) { - Input_Frame* frame = state->input_state.frame_hot; + Input_Frame* frame = state->input_state->frame_hot; switch (evt.kind) { case WindowEvent_MouseScroll: @@ -66,6 +69,13 @@ lumenarium_event(Platform_Window_Event evt, App_State* state) frame->mouse_scroll = evt.scroll_amt; } break; + case WindowEvent_MouseMoved: + { + v2 mouse_pos_old = frame->mouse_pos; + frame->mouse_pos = v2{ (r32)evt.mouse_x, (r32)evt.mouse_y }; + state->input_state->mouse_pos_delta = frame->mouse_pos - mouse_pos_old; + } break; + case WindowEvent_ButtonDown: case WindowEvent_ButtonUp: { diff --git a/src_v2/lumenarium_first.h b/src_v2/lumenarium_first.h index 441ef69..1d6c20c 100644 --- a/src_v2/lumenarium_first.h +++ b/src_v2/lumenarium_first.h @@ -9,11 +9,18 @@ typedef struct App_State App_State; #include "lumenarium_memory.cpp" #include "lumenarium_string.cpp" #include "lumenarium_input.cpp" +#include "lumenarium_texture_atlas.cpp" +#include "lumenarium_hash.cpp" // Engine +typedef struct Assembly_Strip Assembly_Strip; +#include "engine/lumenarium_engine_output.h" #include "engine/lumenarium_engine_assembly.h" +#include "engine/output/lumenarium_output_uart.h" +#include "engine/output/lumenarium_output_sacn.h" // Editor +#include "editor/lumenarium_editor_ui.h" #include "editor/lumenarium_editor_renderer.h" #include "editor/lumenarium_editor.h" @@ -49,15 +56,21 @@ struct App_State { App_State_Flags flags; - Input_State input_state; + Input_State* input_state; + Assembly_Array assemblies; + Output output; Editor* editor; }; #include "engine/lumenarium_engine_assembly.cpp" #include "engine/lumenarium_engine.cpp" +#include "engine/lumenarium_engine_output.cpp" +#include "engine/output/lumenarium_output_uart.cpp" +#include "engine/output/lumenarium_output_sacn.cpp" +#include "editor/lumenarium_editor_ui.cpp" #include "editor/lumenarium_editor_renderer.cpp" #include "editor/lumenarium_editor.cpp" diff --git a/src_v2/lumenarium_hash.cpp b/src_v2/lumenarium_hash.cpp new file mode 100644 index 0000000..d219110 --- /dev/null +++ b/src_v2/lumenarium_hash.cpp @@ -0,0 +1,58 @@ +/* date = April 1st 2022 7:29 pm */ + +#ifndef LUMENARIUM_HASH_CPP +#define LUMENARIUM_HASH_CPP + +// +// DJB2 +// Source: http://www.cse.yorku.ca/~oz/hash.html + +internal u32 +hash_djb2_to_u32(char* str, u64 len) +{ + u32 result = 5381; + for (u64 i = 0; i < len; i++) + { + result = ((result << 5) + result) + (u8)str[i]; + } + return result; +} + +internal u32 +hash_djb2_to_u32(char* str) +{ + u64 len = c_str_len(str); + return hash_djb2_to_u32(str, len); +} + +internal u32 +hash_djb2_to_u32(String str) +{ + return hash_djb2_to_u32((char*)str.str, str.len); +} + +internal u64 +hash_djb2_to_u64(char* str, u64 len) +{ + u64 result = 5381; + for (u64 i = 0; i < len; i++) + { + result = ((result << 5) + result) + (u8)str[i]; + } + return result; +} + +internal u64 +hash_djb2_to_u64(char* str) +{ + u64 len = c_str_len(str); + return hash_djb2_to_u64(str, len); +} + +internal u64 +hash_djb2_to_u64(String str) +{ + return hash_djb2_to_u64((char*)str.str, str.len); +} + +#endif //LUMENARIUM_HASH_CPP diff --git a/src_v2/lumenarium_input.cpp b/src_v2/lumenarium_input.cpp index 8937018..9b6344b 100644 --- a/src_v2/lumenarium_input.cpp +++ b/src_v2/lumenarium_input.cpp @@ -24,15 +24,30 @@ struct Input_State #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) +#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() +internal Input_State* +input_state_create(Allocator* a) { - Input_State result = {}; - result.frame_hot = result.frames + 0; - result.frame_cold = result.frames + 1; + Input_State* result = allocator_alloc_struct(a, Input_State); + result->frame_hot = result->frames + 0; + result->frame_cold = result->frames + 1; + return result; +} + +internal Platform_Key_Flags +input_key_advance(Platform_Key_Flags flag) +{ + Platform_Key_Flags result = flag; + if (key_is_down(flag)) + { + add_flag(result, KeyFlag_State_WasDown); + } + if (key_is_up(flag)) + { + rem_flag(result, KeyFlag_State_WasDown); + } return result; } @@ -46,7 +61,10 @@ input_state_swap_frames(Input_State* input_state) // 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]; + for (u32 i = 0; i < KeyCode_Count; i++) + { + hot_key_flags[i] = input_key_advance(cold_key_flags[i]); + } input_state->frame_hot->string_input_len = 0; } diff --git a/src_v2/lumenarium_memory.cpp b/src_v2/lumenarium_memory.cpp index f6a3421..103396f 100644 --- a/src_v2/lumenarium_memory.cpp +++ b/src_v2/lumenarium_memory.cpp @@ -32,21 +32,25 @@ 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)) +# 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)) +# 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)) +internal void memory_zero(u8* base, u64 size) { memory_zero_(base, size); } +internal void memory_copy(u8* from, u8* to, u64 size) { + memory_copy_(from, to, size); +} + u64 -size_to_pages(u64 size) +round_size_to_page_multiple(u64 size, u64 page_size) { - u64 page_size = platform_page_size(); u64 rem = size % page_size; if (rem != 0 || size < page_size) { @@ -56,6 +60,13 @@ size_to_pages(u64 size) return size; } +u64 +round_size_to_page_multiple(u64 size) +{ + u64 page_size = platform_page_size(); + return round_size_to_page_multiple(size, page_size); +} + ///////////////////////////////////////// // Allocator // @@ -64,37 +75,14 @@ size_to_pages(u64 size) // 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 +internal void +allocator_destroy_(Allocator* allocator, u64 custom_data_size) { - 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) + zero_struct(*allocator); + u64 size = sizeof(Allocator) + custom_data_size; + platform_mem_decommit((u8*)allocator, size); + platform_mem_release((u8*)allocator, size); +} ///////////////////////////////////////// // Bump Allocator @@ -105,13 +93,12 @@ struct Allocator_Bump u64 at; u64 size_committed; u64 size_reserved; + u64 page_size; }; internal u8* -bump_allocator_alloc(Allocator* allocator, u64 size) +bump_allocator_alloc_inner(Allocator* allocator, Allocator_Bump* bump, u64 size) { - Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; - u64 at_after = bump->at + size; // TODO(PS): align up to 8 bytes @@ -119,8 +106,15 @@ bump_allocator_alloc(Allocator* allocator, u64 size) { // 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 (new_size == 0) + { + if (bump->page_size == 0) bump->page_size = platform_page_size(); + new_size = bump->page_size; + } + if (new_size < at_after) + { + new_size = round_size_to_page_multiple(at_after, bump->page_size); + } if (allocator->parent) { @@ -140,7 +134,7 @@ bump_allocator_alloc(Allocator* allocator, u64 size) { if (new_size <= bump->size_reserved) { - u64 next_page = size_to_pages(bump->at); + u64 next_page = round_size_to_page_multiple(bump->at); if (bump->at == 0 && bump->size_committed == 0) next_page = 0; u64 commit_amt = new_size - next_page; u8* new_page = platform_mem_commit(bump->base + next_page, commit_amt); @@ -162,6 +156,14 @@ bump_allocator_alloc(Allocator* allocator, u64 size) return result; } +internal u8* +bump_allocator_alloc(Allocator* allocator, u64 size) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; + u8* result = bump_allocator_alloc_inner(allocator, bump, size); + return result; +} + internal u8* bump_allocator_realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size) { @@ -178,6 +180,21 @@ bump_allocator_clear(Allocator* allocator) bump->at = 0; } +internal void +bump_allocator_destroy_(Allocator_Bump* bump) +{ + platform_mem_decommit(bump->base, bump->size_committed); + platform_mem_release(bump->base, bump->size_reserved); +} + +internal void +bump_allocator_destroy(Allocator* allocator) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; + bump_allocator_destroy_(bump); + allocator_destroy_(allocator, sizeof(Allocator_Bump)); +} + internal Allocator* bump_allocator_create_() { @@ -195,6 +212,7 @@ bump_allocator_create_() result->alloc = bump_allocator_alloc; result->realloc = bump_allocator_realloc; result->clear = bump_allocator_clear; + result->destroy = bump_allocator_destroy; result->allocator_data = (u8*)bump; return result; @@ -206,7 +224,7 @@ 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); + u64 reserve_pages = round_size_to_page_multiple(reserve_size); bump->base = platform_mem_reserve(reserve_pages); if (bump->base != 0) bump->size_reserved = reserve_pages; @@ -229,3 +247,286 @@ bump_allocator_create_child(Allocator* parent, u64 init_size) return result; } + +///////////////////////////////////////// +// Paged Allocator + +struct Allocator_Paged_Free_Region +{ + u64 pages; + Allocator_Paged_Free_Region* prev; + Allocator_Paged_Free_Region* next; +}; + +struct Allocator_Paged +{ + Allocator_Bump bump; + Allocator_Paged_Free_Region* free_first; +}; + +internal u8* +paged_allocator_alloc(Allocator* allocator, u64 size) +{ + // 1. Find the number of pages we need + // 2. Find a run of free pages that we can use + // If found, + // remove those pages from the run they are in + // return those pages of memory + // 3. Commit pages on the end + + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + if (paged->bump.page_size == 0) paged->bump.page_size = platform_page_size(); + + u64 rounded_size = round_size_to_page_multiple(size, paged->bump.page_size); + u64 pages_needed = rounded_size / paged->bump.page_size; + + u8* result = 0; + + // Find free pages + if (paged->free_first) + { + Allocator_Paged_Free_Region* found = 0; + for (Allocator_Paged_Free_Region* at = paged->free_first; at != 0; at = at->next) + { + // NOTE(PS): this set of conditions checks to see if is bigger than what + // we need. If it is, we also check to see if this is smaller than any + // region we've found before. And we abort the search if this region + // perfectly fits the size needed. + // + // This should make sure that we are always choosing the closest fit we + // can. I'm not sure this is the best strategy for dealing with fragmentation + // but its a decent first pass + if (at->pages >= pages_needed) + { + if (!found || (found->pages > at->pages)) + { + found = at; + if (found->pages == pages_needed) break; + } + } + } + + if (found) + { + result = (u8*)found; + if (found->pages > pages_needed) + { + Allocator_Paged_Free_Region* region_after = (Allocator_Paged_Free_Region*)(result + rounded_size); + if (found->prev != 0) found->prev->next = region_after; + region_after = found->next; + } + else + { + if (found->prev != 0) found->prev->next = found->next; + } + } + } + + if (!result) + { + result = bump_allocator_alloc_inner(allocator, &paged->bump, size); + } + + return result; +} + +#define region_end(r,page_size) ((u8*)(r) + ((r)->pages * page_size)) + +internal void +paged_region_insert( + Allocator_Paged_Free_Region* before, + Allocator_Paged_Free_Region* new_region, + Allocator_Paged_Free_Region* after, + u64 page_size + ){ + assert(after == 0 || before < after); + assert(before < new_region); + assert(after == 0 || new_region < after); + assert(new_region->prev == 0 && new_region->next == 0); + + u8* before_end = region_end(before, page_size); + u8* new_region_end = region_end(new_region, page_size); + + // Before + if (before_end == (u8*)new_region) + { + // merge the regions + before->pages += new_region->pages; + new_region = before; + assert(new_region_end == region_end(new_region, page_size)); + } + else + { + assert(before_end < (u8*)new_region); + before->next = new_region; + new_region->prev = before; + } + + // After + if (after != 0) + { + if (new_region_end == (u8*)after) + { + // merge the regions + new_region->pages += after->pages; + u8* a = region_end(after, page_size); + u8* b = region_end(new_region, page_size); + assert(a == b); + } + else + { + assert(new_region_end < (u8*)after); + new_region->next = after; + after->prev = new_region; + } + } +} + +internal void +paged_allocator_free(Allocator* allocator, u8* base, u64 size) +{ + // Figure out which page base is the base of, assert its the base + // figure out how many pages size represents. + // create a free range + // stick it in between contiguous free ranges + // if the ranges before or after meet this new one, merge them all + + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + + u64 page_base_rel = (base - paged->bump.base); + assert((page_base_rel % paged->bump.page_size) == 0); + u64 page_index = page_base_rel / paged->bump.page_size; + u64 size_pages_mult = round_size_to_page_multiple(size, paged->bump.page_size); + assert((size_pages_mult % paged->bump.page_size) == 0); + u64 page_count = size_pages_mult / paged->bump.page_size; + + Allocator_Paged_Free_Region* region = (Allocator_Paged_Free_Region*)base; + zero_struct(*region); + region->pages = page_count; + + Allocator_Paged_Free_Region* prev = 0; + Allocator_Paged_Free_Region* next = 0; + for (Allocator_Paged_Free_Region* at = paged->free_first; at != 0; at = at->next) + { + if (at < region) + { + prev = at; + next = at->next; + if (next != 0) + { + assert(next > region); + assert((u8*)next >= ((u8*)region + size_pages_mult)); + } + } + } + + if (prev && next) + { + // found a region to insert into + paged_region_insert(prev, region, next, paged->bump.page_size); + } + else if (prev) + { + // got to the end and all were before the free region in memory + paged_region_insert(prev, region, 0, paged->bump.page_size); + } + else + { + // free list is empty + paged->free_first = region; + } +} + +internal u8* +paged_allocator_realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size) +{ + // TODO(PS): + // Process: + // 1. Figure out which page base starts on + // 2. Find if there is a free region after base that is big enough to house + // the new size + // 3. If there is a free region, pull the needed memory out of it + // 4. Otherwise, alloc new_size, copy base into it, and free base + + // TODO(PS): you could do a simple version where you just always alloc, copy, free + return 0; +} + +internal void +paged_allocator_clear(Allocator* allocator) +{ + if (!allocator->allocator_data) return; + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + paged->bump.at = 0; + paged->free_first = 0; +} + +internal void +paged_allocator_destroy(Allocator* allocator) +{ + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + bump_allocator_destroy_(&paged->bump); + allocator_destroy_(allocator, sizeof(Allocator_Paged)); +} + +internal Allocator* +paged_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 = paged_allocator_alloc; + result->free = paged_allocator_free; + result->realloc = paged_allocator_realloc; + result->clear = paged_allocator_clear; + result->destroy = paged_allocator_destroy; + result->allocator_data = (u8*)bump; + + return result; +} + +internal Allocator* +paged_allocator_create_reserve(u64 reserve_size, u64 page_size) +{ + Allocator* result = paged_allocator_create_(); + Allocator_Paged* paged = (Allocator_Paged*)result->allocator_data; + + u64 reserve_pages = round_size_to_page_multiple(reserve_size); + paged->bump.page_size = page_size; + paged->bump.base = platform_mem_reserve(reserve_pages); + if (paged->bump.base != 0) paged->bump.size_reserved = reserve_pages; + + return result; +} + +internal Allocator* +paged_allocator_create_reserve(u64 reserve_size) +{ + u64 page_size = platform_page_size(); + return paged_allocator_create_reserve(reserve_size, page_size); +} + +internal Allocator* +paged_allocator_create_child(Allocator* parent, u64 init_size) +{ + Allocator* result = bump_allocator_create_(); + result->parent = parent; + + Allocator_Paged* paged = (Allocator_Paged*)result->allocator_data; + paged->bump.base = allocator_alloc(result->parent, init_size); + if (paged->bump.base != 0) + { + paged->bump.size_reserved = init_size; + paged->bump.size_committed = init_size; + } + + return result; +} diff --git a/src_v2/lumenarium_memory.h b/src_v2/lumenarium_memory.h new file mode 100644 index 0000000..8b6a79f --- /dev/null +++ b/src_v2/lumenarium_memory.h @@ -0,0 +1,45 @@ +/* date = April 6th 2022 7:55 pm */ + +#ifndef LUMENARIUM_MEMORY_H +#define LUMENARIUM_MEMORY_H + +///////////////////////////////////////// +// Allocator + +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); +typedef void Allocator_Destroy(Allocator* allocator); + +struct Allocator +{ + Allocator_Alloc* alloc; + Allocator_Free* free; + Allocator_Realloc* realloc; + Allocator_Clear* clear; + Allocator_Destroy* destroy; + + 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) +#define allocator_destroy(a) (a)->destroy(a) + +internal Allocator* paged_allocator_create_reserve(u64 reserve_size, u64 page_size); +#endif //LUMENARIUM_MEMORY_H diff --git a/src_v2/lumenarium_string.cpp b/src_v2/lumenarium_string.cpp index 157f2c6..adff229 100644 --- a/src_v2/lumenarium_string.cpp +++ b/src_v2/lumenarium_string.cpp @@ -54,6 +54,16 @@ char_to_forward_slash(u8 c) // Note that these don't actually modify any memory // just return structures that let you view it differently +internal String +string_create(u8* str, u64 len, u64 cap) +{ + String result = {}; + result.str = str; + result.len = len; + result.cap = cap; + return result; +} + internal String string_substring(String s, u64 min, u64 max) { @@ -213,15 +223,25 @@ string_chop_last_slash(String s) ///////////////////////////////////// // String Modifications +internal u64 +string_copy_to(String* dest, String src) +{ + u64 len_to_copy = dest->cap < src.len ? dest->cap : src.len; + memory_copy(src.str, dest->str, len_to_copy); + u64 null_term_index = len_to_copy; + if (null_term_index >= dest->cap) null_term_index -= 1; + dest->str[null_term_index] = 0; + dest->len = null_term_index; + return null_term_index; +} + 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; + string_copy_to(&result, s); return result; } diff --git a/src_v2/lumenarium_tests.cpp b/src_v2/lumenarium_tests.cpp index 7472451..a7f99f5 100644 --- a/src_v2/lumenarium_tests.cpp +++ b/src_v2/lumenarium_tests.cpp @@ -6,6 +6,90 @@ thread_proc(Platform_Thread_Data* td) return {}; } +void +memory_allocator_tests(Allocator* a, bool run_free_tests) +{ + // TestGroup("Allocator Push") + { + for (u32 i = 0; i < 3; i++) + { + u8* buf0 = allocator_alloc(a, 256); + buf0[0] = 200; + buf0[255] = 199; + assert(buf0[0] == 200); + assert(buf0[255] == 199); + + u8* buf1 = allocator_alloc(a, 256); + buf1[0] = 201; + buf1[255] = 202; + assert(buf1 >= (buf0 + 256)); + assert(buf0[0] == 200); + assert(buf0[255] == 199); + assert(buf1[0] == 201); + assert(buf1[255] == 202); + + allocator_clear(a); + } + } + + // TestGroup("Allocator Free") + if (run_free_tests) + { + for (u32 i = 0; i < 3; i++) + { + u8* buf0 = allocator_alloc(a, KB(4)); + u8* buf1 = allocator_alloc(a, KB(4)); + u8* buf2 = allocator_alloc(a, KB(4)); + u8* buf3 = allocator_alloc(a, KB(4)); + u8* buf4 = allocator_alloc(a, KB(4)); + assert((buf1 - buf0) >= KB(4)); + assert((buf2 - buf0) >= KB(8)); + assert((buf3 - buf0) >= KB(12)); + assert((buf4 - buf0) >= KB(16)); + + allocator_free(a, buf1, KB(4)); + allocator_free(a, buf2, KB(4)); + u8* buf5 = allocator_alloc(a, KB(7)); + // buf5 should get put in the place of buf1 since buf1 and 2 get + // merged + assert(buf5 == buf1); + + allocator_free(a, buf4, KB(4)); + allocator_free(a, buf3, KB(4)); + allocator_free(a, buf0, KB(4)); + u8* buf6 = allocator_alloc(a, KB(4)); + assert(buf0 == buf6); + + allocator_clear(a); + } + } +} + +void +memory_tests() +{ + // TestGroup("Platform Allocation") + { + u8* base = platform_mem_reserve(GB(32)); + platform_mem_commit(base, KB(4)); + base[4095] = 200; + assert(base[4095] == 200); + platform_mem_commit(base + KB(4), KB(4)); + base[5000] = 200; + assert(base[5000] == 200); + platform_mem_decommit(base, KB(8)); + platform_mem_release(base, GB(32)); + } + + Allocator* bump = bump_allocator_create_reserve(KB(32)); + memory_allocator_tests(bump, false); + allocator_destroy(bump); + + Allocator* paged = paged_allocator_create_reserve(KB(32)); + memory_allocator_tests(paged, true); + allocator_destroy(paged); +} + internal void run_tests() { @@ -29,6 +113,8 @@ run_tests() assert(a1[i] == (100 + i)); } + memory_tests(); + #if defined(PLATFORM_wasm) // NOTE(PS): the tests below this point don't make sense on a web assembly // platform diff --git a/src_v2/lumenarium_texture_atlas.cpp b/src_v2/lumenarium_texture_atlas.cpp new file mode 100644 index 0000000..e56888f --- /dev/null +++ b/src_v2/lumenarium_texture_atlas.cpp @@ -0,0 +1,128 @@ +/* date = March 31st 2022 8:30 pm */ + +#ifndef LUMENARIUM_TEXTURE_ATLAS_CPP +#define LUMENARIUM_TEXTURE_ATLAS_CPP + +struct Texture_Atlas_Sprite +{ + u16 min_x; + u16 min_y; + u16 max_x; + u16 max_y; +}; + +struct Texture_Atlas +{ + u8* pixels; + u16 width; + u16 height; + + u16 next_x; + u16 next_y; + u16 y_used; + + u32* ids; + Texture_Atlas_Sprite* sprites; + u32 cap; + u32 used; +}; + +internal Texture_Atlas +texture_atlas_create(u32 width, u32 height, u32 cap, Allocator* allocator) +{ + Texture_Atlas result = {}; + result.pixels = allocator_alloc_array(allocator, u8, width * height * 4); + result.width = (u16)width; + result.height = (u16)height; + for (u32 i = 0; i < width * height; i++) { + u8* base = result.pixels + (i * 4); + *(u32*)base = 0xFFFFFFFF; + } + + result.ids = allocator_alloc_array(allocator, u32, cap); + result.sprites = allocator_alloc_array(allocator, Texture_Atlas_Sprite, cap); + result.cap = cap; + hash_table_init(result.ids, cap); + return result; +} + +internal void +texture_atlas_register(Texture_Atlas* ta, u8* pixels, u32 width, u32 height, u32 id) +{ + u16 min_x = ta->next_x; + u16 min_y = ta->next_y; + u16 max_x = min_x + (u16)width; + u16 max_y = min_y + (u16)height; + + // TODO(PS): if the sprite won't fit in this row, then we need to shift it to + // the next one + + // copy the data + for (u16 y = 0; y < height; y++) + { + u16 src_row = (y * (u16)width) * 4; + u16 dst_row = (((y + min_y) * ta->width) + min_x) * 4; + for (u16 x = 0; x < width; x++) + { + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + } + } + + // register a new slot + u32 index = hash_table_register(ta->ids, ta->cap, id); + + Texture_Atlas_Sprite* sprite = ta->sprites + index; + sprite->min_x = min_x; + sprite->min_y = min_y; + sprite->max_x = max_x; + sprite->max_y = max_y; + + // Prepare for next registration + if (max_y > ta->y_used) + { + ta->y_used = max_y; + } + + ta->next_x = max_x + 1; + if (ta->next_x > ta->width) + { + ta->next_x = 0; + ta->next_y = ta->y_used; + } +} + +internal Texture_Atlas_Sprite +texture_atlas_sprite_get(Texture_Atlas* ta, u32 id) +{ + Texture_Atlas_Sprite result = {}; + u32 index = hash_table_find(ta->ids, ta->cap, id); + if (index == ta->cap) return result; + result = ta->sprites[index]; + return result; +} + +internal v4 +texture_atlas_sprite_get_uvs(Texture_Atlas* ta, u32 id) +{ + Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(ta, id); + v4 result = {}; + + // uv min + result.x = (r32)sprite.min_x / (r32)ta->width; + result.y = (r32)sprite.min_y / (r32)ta->height; + + // uv max + result.z = (r32)sprite.max_x / (r32)ta->width; + result.w = (r32)sprite.max_y / (r32)ta->height; + + // inset + v2 half_texel = v2{1.0f / ta->width, 1.0f / ta->height}; + result.xy += half_texel; + result.zw -= half_texel; + + return result; +} +#endif //LUMENARIUM_TEXTURE_ATLAS_CPP diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h index e9eac22..d26977a 100644 --- a/src_v2/lumenarium_types.h +++ b/src_v2/lumenarium_types.h @@ -37,16 +37,149 @@ typedef s64 b64; typedef float r32; typedef double r64; +#define get_byte(value, byte_index) ((value >> (8 * byte_index)) & 0xFF) + struct Data { u8* base; u64 size; }; +internal Data +data_create(u8* base, u64 size) +{ + Data result = {}; + result.base = base; + result.size = size; + return result; +} + +internal void memory_zero(u8* base, u64 size); +internal void memory_copy(u8* from, u8* to, u64 size); + +////////////////////////////////////////////// +// 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; +}; + +internal String string_create(u8* str, u64 len, u64 cap); +internal u64 string_copy_to(String* dest, String src); + +////////////////////////////////////////////// +// Data Writer + +struct Data_Writer +{ + Data data; + u64 at; +}; + +// NOTE(PS): functions ending in _b treat data in the Data_Writer as big endian +// order (network ordering) where functions ending in _l treat data into little +// endian order +// It is always assumed that values not in the Data_Writer (ie the other args +// to the function or the functions return value) are in little endian order + +internal void +dw_put_u8(Data_Writer* w, u8 b) +{ + if (w->at < w->data.size) + { + w->data.base[w->at++] = b; + } +} + +internal u8 +dw_get_u8(Data_Writer* w) +{ + u8 result = 0; + if (w->at < w->data.size) + { + result = w->data.base[w->at++]; + } + return result; +} + +internal void +dw_put_u16_b(Data_Writer* w, u16 b) +{ + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 0)); +} + +internal void +dw_put_u16_l(Data_Writer* w, u16 b) +{ + dw_put_u8(w, get_byte(b, 0)); + dw_put_u8(w, get_byte(b, 1)); +} + +internal void +dw_put_u32_b(Data_Writer* w, u32 b) +{ + dw_put_u8(w, get_byte(b, 3)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 0)); +} + +internal void +dw_put_u32_l(Data_Writer* w, u32 b) +{ + dw_put_u8(w, get_byte(b, 0)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 3)); +} + +internal void +dw_put_u64_b(Data_Writer* w, u64 b) +{ + dw_put_u8(w, get_byte(b, 7)); + dw_put_u8(w, get_byte(b, 6)); + dw_put_u8(w, get_byte(b, 5)); + dw_put_u8(w, get_byte(b, 4)); + dw_put_u8(w, get_byte(b, 3)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 0)); +} + +internal void +dw_put_u64_l(Data_Writer* w, u64 b) +{ + dw_put_u8(w, get_byte(b, 0)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 3)); + dw_put_u8(w, get_byte(b, 4)); + dw_put_u8(w, get_byte(b, 5)); + dw_put_u8(w, get_byte(b, 6)); + dw_put_u8(w, get_byte(b, 7)); +} + +internal void +dw_put_str(Data_Writer* w, String str) +{ + for (u64 i = 0; i < str.len; i++) + { + dw_put_u8(w, str.str[i]); + } +} + +// TODO(PS): get functions + #define Bytes(x) (x) #define KB(x) ((x) << 10) #define MB(x) ((x) << 20) -#define GB(x) ((x) << 30) +#define GB(x) (((u64)x) << 30) #define TB(x) (((u64)x) << 40) #define has_flag(data, flag) (((data) & (flag)) != 0) @@ -65,17 +198,66 @@ else { (last)->next = (ele); } \ // TODO(PS): Stack, Queue, DLL ops ////////////////////////////////////////////// -// String +// Hash Table +// +// Rather than define a data structure, to allow the most flexibility, +// this is just a set of functions that can be integrated into other +// routines. +// In general, they expect you to track a u32* of ids and a u32 capacity -// NOTE(PS): even though this has a len and cap, it should always be -// null terminated -struct String +internal void +hash_table_init(u32* ids, u32 cap) { - u8* str; - u64 len; - u64 cap; -}; + for (u32 i = 0; i < cap; i++) ids[i] = 0; +} + +internal u32 +hash_table_find_(u32* ids, u32 cap, u32 start_id, u32 target_value) +{ + u32 index = start_id % cap; + u32 start = index; + do { + if (ids[index] == target_value) break; + index = (index + 1) % cap; + } while (index != start); + return index; +} + +internal u32 +hash_table_register(u32* ids, u32 cap, u32 new_id) +{ + u32 index = hash_table_find_(ids, cap, new_id, 0); + if (ids[index] != 0) return cap; + ids[index] = new_id; + return index; +} + +internal u32 +hash_table_find(u32* ids, u32 cap, u32 value) +{ + u32 result = hash_table_find_(ids, cap, value, value); + if (ids[result] != value) return cap; + return result; +} + +////////////////////////////////////////////// +// Vector Extensions + +#define v2_to_v3(xy,z) v3{(xy).x, (xy).y, (z)} + +////////////////////////////////////////////// +// Color Constants + +#define WHITE_V4 v4{1,1,1,1} +#define BLACK_V4 v4{0,0,0,1} +#define RED_V4 v4{1,0,0,1} +#define GREEN_V4 v4{0,1,0,1} +#define BLUE_V4 v4{0,0,1,1} +#define YELLOW_V4 v4{1,1,0,1} +#define TEAL_V4 v4{0,1,1,1} +#define PINK_V4 v4{1,0,1,1} typedef struct Allocator Allocator; + #endif //LUMENARIUM_TYPES_H diff --git a/src_v2/platform/lumenarium_assert.h b/src_v2/platform/lumenarium_assert.h index 213d041..be84e78 100644 --- a/src_v2/platform/lumenarium_assert.h +++ b/src_v2/platform/lumenarium_assert.h @@ -34,7 +34,12 @@ WASM_EXTERN void wasm_assert_always(char* file, unsigned int file_len, unsigned #endif // defined(PLATFORM_WASM) #ifdef USE_ASSERTS -# define assert(c) if (!(c)) { err_write("Assert Hit: %s:%d\n", __FILE__, (u32)__LINE__); close_err_file(); assert_always; } +# define assert(c) \ +if (!(c)) { \ +err_write("Assert Hit: %s:%d\n", __FILE__, (u32)__LINE__); \ +close_err_file(); \ +assert_always; \ +} // useful for catching cases that you aren't sure you'll hit, but // want to be alerted when they happen diff --git a/src_v2/platform/lumenarium_platform.h b/src_v2/platform/lumenarium_platform.h index a96a830..0f50ddf 100644 --- a/src_v2/platform/lumenarium_platform.h +++ b/src_v2/platform/lumenarium_platform.h @@ -80,6 +80,148 @@ bool platform_file_write_all(Platform_File_Handle file_handle, Data file_data); String platform_get_exe_path(Allocator* allocator); bool platform_pwd_set(String path); +// For Cross Platform File Operations use these: + +typedef u32 Platform_File_Async_Job_Flags; +enum +{ + PlatformFileAsyncJob_Invalid = 0, + PlatformFileAsyncJob_Read = 1, + PlatformFileAsyncJob_Write = 2, + PlatformFileAsyncJob_InFlight = 4, + PlatformFileAsyncJob_Success = 8, + PlatformFileAsyncJob_Failed = 16, +}; + +struct Platform_File_Async_Job_Args +{ + String path; + Data data; + Platform_File_Async_Job_Flags flags; + u32 error; +}; + +typedef void Platform_File_Async_Cb(Platform_File_Async_Job_Args args); + +struct Platform_File_Async_Job +{ + Data job_memory; + Platform_File_Async_Job_Args args; + Platform_File_Async_Cb* cb; +}; + +global Allocator* platform_file_jobs_arena = 0; +#define PLATFORM_FILE_ASYNC_MAX_JOBS 32 +global Platform_File_Async_Job platform_file_async_jobs[PLATFORM_FILE_ASYNC_MAX_JOBS]; +global u32 platform_file_async_jobs_len = 0; + +void +platform_file_jobs_init() +{ + platform_file_jobs_arena = paged_allocator_create_reserve(MB(4), 256); +} + +bool +platform_file_async_job_add(Platform_File_Async_Job job) +{ + if (platform_file_async_jobs_len >= PLATFORM_FILE_ASYNC_MAX_JOBS) return false; + + // Copy data to job local memory + u64 size_needed = job.args.path.len + job.args.data.size + 1; + u8* job_mem = allocator_alloc(platform_file_jobs_arena, size_needed); + String job_path = string_create(job_mem, 0, job.args.path.len + 1); + u64 copied = string_copy_to(&job_path, job.args.path); + Data job_data = data_create(job_mem + job_path.cap + 1, size_needed - (job_path.cap + 1)); + memory_copy(job.args.data.base, job_data.base, job.args.data.size); + job.args.path = job_path; + job.args.data = job_data; + job.job_memory = data_create(job_mem, size_needed); + + platform_file_async_jobs[platform_file_async_jobs_len++] = job; + return true; +} + +Platform_File_Async_Job +platform_file_async_job_rem(u64 index) +{ + assert(index < platform_file_async_jobs_len); + Platform_File_Async_Job result = platform_file_async_jobs[index]; + + platform_file_async_jobs_len -= 1; + if (platform_file_async_jobs_len > 0) + { + u32 last_job = platform_file_async_jobs_len; + platform_file_async_jobs[index] = platform_file_async_jobs[last_job]; + } + + return result; +} + +bool +platform_file_async_read(String path, Platform_File_Async_Cb* cb) +{ + Platform_File_Async_Job job = {}; + job.args.path = path; + job.args.flags = ( + PlatformFileAsyncJob_Read | + PlatformFileAsyncJob_InFlight + ); + job.cb = cb; + bool result = platform_file_async_job_add(job); + return result; +} + +bool +platform_file_async_write(String path, Data data, Platform_File_Async_Cb* cb) +{ + Platform_File_Async_Job job = {}; + job.args.path = path; + job.args.data = data; + job.args.flags = ( + PlatformFileAsyncJob_Write | + PlatformFileAsyncJob_InFlight + ); + job.cb = cb; + bool result = platform_file_async_job_add(job); + return result; +} + +void +platform_file_async_job_complete(Platform_File_Async_Job* job) +{ + job->cb(job->args); + allocator_free(platform_file_jobs_arena, job->job_memory.base, job->job_memory.size); + if (has_flag(job->args.flags, PlatformFileAsyncJob_Write)) + { + allocator_free(platform_file_jobs_arena, job->args.data.base, job->args.data.size); + } +} + +void platform_file_async_work_on_job(Platform_File_Async_Job* job); + +void +platform_file_async_jobs_do_work(u64 max_jobs) +{ + u64 to_do = max_jobs; + if (max_jobs > platform_file_async_jobs_len) to_do = platform_file_async_jobs_len; + + Platform_File_Async_Job_Flags completed = ( + PlatformFileAsyncJob_Success | + PlatformFileAsyncJob_Failed + ); + + for (u64 i = to_do - 1; i < to_do; i--) + { + Platform_File_Async_Job* job = platform_file_async_jobs + i; + platform_file_async_work_on_job(job); + if (has_flag(job->args.flags, completed)) + { + platform_file_async_job_complete(job); + platform_file_async_job_rem(i); + } + } +} + typedef u32 Platform_Enum_Dir_Flags; enum { @@ -100,6 +242,7 @@ enum Platform_Window_Event_Kind WindowEvent_Invalid = 0, WindowEvent_MouseScroll, + WindowEvent_MouseMoved, WindowEvent_ButtonDown, WindowEvent_ButtonUp, WindowEvent_Char, @@ -299,6 +442,7 @@ struct Platform_Shader { u32 id; u32 attrs[PLATFORM_SHADER_MAX_ATTRS]; + u32 uniforms[PLATFORM_SHADER_MAX_ATTRS]; }; struct Platform_Geometry_Buffer @@ -306,6 +450,7 @@ struct Platform_Geometry_Buffer u32 buffer_id_vao; u32 buffer_id_vertices; u32 buffer_id_indices; + u32 vertices_len; u32 indices_len; }; @@ -329,16 +474,19 @@ void platform_frame_clear(); // Geometry Platform_Geometry_Buffer platform_geometry_buffer_create(r32* vertices, u32 vertices_len, u32* indices, u32 indices_len); Platform_Shader platform_shader_create( - String code_vert, String code_frag, String* attribs, u32 attribs_len + String code_vert, String code_frag, String* attribs, u32 attribs_len, String* uniforms, u32 uniforms_len ); +void platform_geometry_buffer_update(Platform_Geometry_Buffer* buffer, r32* verts, u32 verts_offset, u32 verts_len, u32* indices, u32 indices_offset, u32 indices_len); // Shaders void platform_geometry_bind(Platform_Geometry_Buffer geo); void platform_shader_bind(Platform_Shader shader); +void platform_geometry_draw(Platform_Geometry_Buffer geo, u32 indices); void platform_geometry_draw(Platform_Geometry_Buffer geo); void platform_vertex_attrib_pointer( Platform_Geometry_Buffer geo, Platform_Shader shader, u32 count, u32 attr_index, u32 stride, u32 offset ); +void platform_set_uniform(Platform_Shader shader, u32 index, m44 u); // Textures Platform_Texture platform_texture_create(u8* pixels, u32 width, u32 height, u32 stride); diff --git a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp index 88be506..0d04110 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp +++ b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp @@ -20,8 +20,9 @@ typedef unsigned int GLsizei; // I resorted to hard coding these rather than passing them in because // passing them in didn't seem to be working. +#define GL_FALSE 0 +#define GL_TRUE 1 #define GL_TEXTURE_2D 0x0DE1 - #define GL_DEPTH_BUFFER_BIT 0x00000100 #define GL_STENCIL_BUFFER_BIT 0x00000400 #define GL_COLOR_BUFFER_BIT 0x00004000 @@ -68,6 +69,7 @@ WASM_EXTERN void glAttachShader(GLuint program, GLuint shader); WASM_EXTERN void glLinkProgram(GLuint program); WASM_EXTERN void glUseProgram(GLuint program); WASM_EXTERN GLuint glGetAttribLocation(GLuint program, const char* name, GLuint name_len); +WASM_EXTERN GLuint glGetUniformLocation(GLuint program, const char* name, u32 len); WASM_EXTERN void glVertexAttribPointer(GLuint attr, GLuint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); WASM_EXTERN void glEnableVertexAttribArray(GLuint index); WASM_EXTERN void glDrawElements(GLenum type, GLuint count, GLenum ele_type, void* indices); @@ -109,7 +111,7 @@ platform_geometry_buffer_create( Platform_Shader platform_shader_create( - String code_vert, String code_frag, String* attrs, GLuint attrs_len + String code_vert, String code_frag, String* attrs, GLuint attrs_len, String* uniforms, GLuint uniforms_len ){ Platform_Shader result = {}; @@ -139,6 +141,16 @@ platform_shader_create( } result.attrs[attrs_len] = PLATFORM_SHADER_ATTR_LAST; + assert(uniforms_len < PLATFORM_SHADER_MAX_ATTRS); + for (GLuint i = 0; i < uniforms_len; i++) + { + s32 len = (s32)uniforms[i].len; + result.uniforms[i] = glGetUniformLocation( + result.id, (char*)uniforms[i].str, uniforms[i].len + ); + } + result.uniforms[uniforms_len] = PLATFORM_SHADER_ATTR_LAST; + return result; } @@ -159,6 +171,19 @@ platform_shader_bind(Platform_Shader shader) } } +void +platform_set_uniform(Platform_Shader shader, u32 index, m44 u) +{ + glUniformMatrix4fv(shader.uniforms[index], 1, GL_FALSE, &u.Elements[0]); +} + +void +platform_geometry_draw( + Platform_Geometry_Buffer geo, u32 indices + ){ + glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_INT, 0); +} + void platform_geometry_draw( Platform_Geometry_Buffer geo diff --git a/src_v2/platform/win32/lumenarium_first_win32.cpp b/src_v2/platform/win32/lumenarium_first_win32.cpp index d27f084..a18e2d3 100644 --- a/src_v2/platform/win32/lumenarium_first_win32.cpp +++ b/src_v2/platform/win32/lumenarium_first_win32.cpp @@ -7,6 +7,7 @@ #include #include "../../lumenarium_types.h" +#include "../../lumenarium_memory.h" #include "../lumenarium_platform.h" #include "../../lumenarium_first.cpp" @@ -215,6 +216,20 @@ WinMain( lumenarium_event(evt, state); } + // Get the position of the mouse every frame + { + POINT mouse_p; + GetCursorPos(&mouse_p); + ScreenToClient(win32_main_window.window_handle, &mouse_p); + + Platform_Window_Event evt = {}; + evt.kind = WindowEvent_MouseMoved; + evt.mouse_x = mouse_p.x; + evt.mouse_y = mouse_p.y; + + lumenarium_event(evt, state); + } + // Pass Window Events to the runtime MSG window_msg; while (PeekMessageA( diff --git a/src_v2/platform/win32/lumenarium_win32_file.cpp b/src_v2/platform/win32/lumenarium_win32_file.cpp index 344e785..6db4fc4 100644 --- a/src_v2/platform/win32/lumenarium_win32_file.cpp +++ b/src_v2/platform/win32/lumenarium_win32_file.cpp @@ -256,3 +256,36 @@ platform_pwd_set(String path) if (!result) win32_get_last_error(); return result; } + +void +platform_file_async_work_on_job(Platform_File_Async_Job* job) +{ + Platform_File_Handle file = {}; + if (has_flag(job->args.flags, PlatformFileAsyncJob_Read)) + { + file = platform_file_open(job->args.path, FileAccess_Read, FileCreate_OpenExisting); + Data result = platform_file_read_all(file, platform_file_jobs_arena); + if (result.base != 0) + { + job->args.data = result; + add_flag(job->args.flags, PlatformFileAsyncJob_Success); + } + else + { + add_flag(job->args.flags, PlatformFileAsyncJob_Failed); + } + } + else if (has_flag(job->args.flags, PlatformFileAsyncJob_Write)) + { + file = platform_file_open(job->args.path, FileAccess_Write, FileCreate_OpenAlways); + if (platform_file_write_all(file, job->args.data)) + { + add_flag(job->args.flags, PlatformFileAsyncJob_Success); + } + else + { + add_flag(job->args.flags, PlatformFileAsyncJob_Failed); + } + } + platform_file_close(file); +} \ No newline at end of file diff --git a/src_v2/platform/win32/lumenarium_win32_graphics.cpp b/src_v2/platform/win32/lumenarium_win32_graphics.cpp index 848d91d..cc9bd19 100644 --- a/src_v2/platform/win32/lumenarium_win32_graphics.cpp +++ b/src_v2/platform/win32/lumenarium_win32_graphics.cpp @@ -39,6 +39,7 @@ platform_geometry_buffer_create( GL_ARRAY_BUFFER, sizeof(r32) * vertices_len, vertices, GL_STATIC_DRAW ); win32_gl_no_error(); + result.vertices_len = vertices_len; // Indices gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.buffer_id_indices); @@ -56,9 +57,61 @@ platform_geometry_buffer_create( return result; } +void platform_geometry_buffer_update( + Platform_Geometry_Buffer* buffer, + r32* verts, + u32 verts_offset, + u32 verts_len, + u32* indices, + u32 indices_offset, + u32 indices_len + ){ + gl.glBindVertexArray(buffer->buffer_id_vao); + gl.glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer_id_vertices); + win32_gl_no_error(); + + if (verts_len > buffer->vertices_len) + { + // NOTE(PS): this is because we're going to delete the old buffer and + // create a new one. In order to do that and not lose data, the update + // function needs to have been passed all the buffer's data + assert(verts_offset == 0); + gl.glBufferData( + GL_ARRAY_BUFFER, verts_len * sizeof(r32), (void*)verts, GL_STATIC_DRAW + ); + } + else + { + gl.glBufferSubData( + GL_ARRAY_BUFFER, verts_offset * sizeof(r32), verts_len * sizeof(r32), (void*)verts + ); + } + win32_gl_no_error(); + + gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->buffer_id_indices); + win32_gl_no_error(); + if (indices_len > buffer->indices_len) + { + // NOTE(PS): this is because we're going to delete the old buffer and + // create a new one. In order to do that and not lose data, the update + // function needs to have been passed all the buffer's data + assert(indices_offset == 0); + gl.glBufferData( + GL_ELEMENT_ARRAY_BUFFER, indices_len * sizeof(u32), (void*)indices, GL_STATIC_DRAW + ); + } + else + { + gl.glBufferSubData( + GL_ELEMENT_ARRAY_BUFFER, indices_offset * sizeof(u32), indices_len * sizeof(u32), (void*)indices + ); + } + win32_gl_no_error(); +} + Platform_Shader platform_shader_create( - String code_vert, String code_frag, String* attrs, u32 attrs_len + String code_vert, String code_frag, String* attrs, u32 attrs_len, String* uniforms, u32 uniforms_len ){ Platform_Shader result = {}; @@ -117,12 +170,29 @@ platform_shader_create( result.attrs[i] = gl.glGetAttribLocation( result.id, (char*)attrs[i].str ); + win32_gl_no_error(); } result.attrs[attrs_len] = PLATFORM_SHADER_ATTR_LAST; + assert(uniforms_len < PLATFORM_SHADER_MAX_ATTRS); + for (GLuint i = 0; i < uniforms_len; i++) + { + s32 len = (s32)uniforms[i].len; + result.uniforms[i] = gl.glGetUniformLocation( + result.id, (char*)uniforms[i].str + ); + } + result.uniforms[uniforms_len] = PLATFORM_SHADER_ATTR_LAST; + return result; } +void +platform_set_uniform(Platform_Shader shader, u32 index, m44 u) +{ + gl.glUniformMatrix4fv(shader.uniforms[index], 1, GL_FALSE, (r32*)u.Elements); +} + void platform_geometry_bind(Platform_Geometry_Buffer geo) { @@ -150,12 +220,19 @@ platform_shader_bind(Platform_Shader shader) } } +void +platform_geometry_draw( + Platform_Geometry_Buffer geo, u32 indices + ){ + glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_INT, 0); + win32_gl_no_error(); +} + void platform_geometry_draw( Platform_Geometry_Buffer geo ){ - glDrawElements(GL_TRIANGLES, geo.indices_len, GL_UNSIGNED_INT, 0); - win32_gl_no_error(); + platform_geometry_draw(geo, geo.indices_len); } void platform_vertex_attrib_pointer( diff --git a/src_v2/platform/win32/lumenarium_win32_memory.cpp b/src_v2/platform/win32/lumenarium_win32_memory.cpp index 8a82089..3c39c44 100644 --- a/src_v2/platform/win32/lumenarium_win32_memory.cpp +++ b/src_v2/platform/win32/lumenarium_win32_memory.cpp @@ -5,7 +5,7 @@ u64 platform_page_size() { return WIN32_PAGE_SIZE; } u8* platform_mem_reserve(u64 size) { - size_t size_cvt = (size_t)size_to_pages(size); + size_t size_cvt = (size_t)round_size_to_page_multiple(size); DWORD alloc_type = MEM_RESERVE; DWORD protection = PAGE_READWRITE; u8* result = (u8*)VirtualAlloc(0, size_cvt, alloc_type, protection); @@ -16,7 +16,7 @@ platform_mem_reserve(u64 size) u8* platform_mem_commit(u8* base, u64 size) { - size_t size_cvt = (size_t)size_to_pages(size); + size_t size_cvt = (size_t)round_size_to_page_multiple(size); DWORD alloc_type = MEM_COMMIT; DWORD protection = PAGE_READWRITE; u8* result = (u8*)VirtualAlloc(base, size_cvt, alloc_type, protection); @@ -37,7 +37,9 @@ bool platform_mem_release(u8* base, u64 size) { DWORD free_type = MEM_RELEASE; - bool result = VirtualFree(base, size, free_type); + // according to the docs, if we're releasing memory, the size must be + // zero as its implied VirtualFree will free the entire region + bool result = VirtualFree(base, 0, free_type); if (!result) win32_get_last_error(); return result; } diff --git a/src_v2/platform/win32/lumenarium_win32_opengl.h b/src_v2/platform/win32/lumenarium_win32_opengl.h index 9ad2cee..29526bb 100644 --- a/src_v2/platform/win32/lumenarium_win32_opengl.h +++ b/src_v2/platform/win32/lumenarium_win32_opengl.h @@ -17,6 +17,7 @@ typedef void proc_glBindVertexArray(GLuint array); typedef void proc_glGenBuffers (GLsizei n, GLuint *buffers); typedef void proc_glBindBuffer(GLenum target, GLuint buffer); typedef void proc_glBufferData(GLenum target, size_t size, const void *data, GLenum usage); +typedef void proc_glBufferSubData(GLenum target, size_t offset, size_t size, const void* data); typedef GLuint proc_glCreateShader(GLenum type); typedef void proc_glShaderSource(GLuint shader, u32 count, const char* const* string, const GLint *length); typedef void proc_glCompileShader(GLuint shader); @@ -31,7 +32,8 @@ typedef void proc_glGetShaderiv(GLuint shader, GLenum ele, GLint* value_out); typedef void proc_glGetShaderInfoLog(GLuint shader, GLuint buf_len, GLsizei* len_out, GLchar* buf); typedef void proc_glGetProgramiv(GLuint program, GLenum ele, GLint* value_out); typedef void proc_glGetProgramInfoLog(GLuint program, GLuint cap, GLsizei* len_out, GLchar* buf); - +typedef GLuint proc_glGetUniformLocation(GLuint program, const char* name); +typedef void proc_glUniformMatrix4fv(GLuint uniform, GLuint count, GLenum normalize, GLfloat* elements); struct Win32_OpenGL_Extensions { proc_wglGetExtensionsStringARB* wglGetExtensionsStringARB; @@ -43,6 +45,7 @@ struct Win32_OpenGL_Extensions proc_glGenBuffers* glGenBuffers; proc_glBindBuffer* glBindBuffer; proc_glBufferData* glBufferData; + proc_glBufferSubData* glBufferSubData; proc_glCreateShader* glCreateShader; proc_glShaderSource* glShaderSource; proc_glCompileShader* glCompileShader; @@ -57,6 +60,8 @@ struct Win32_OpenGL_Extensions proc_glGetShaderInfoLog* glGetShaderInfoLog; proc_glGetProgramiv* glGetProgramiv; proc_glGetProgramInfoLog* glGetProgramInfoLog; + proc_glGetUniformLocation* glGetUniformLocation; + proc_glUniformMatrix4fv* glUniformMatrix4fv; }; //////////////////////////////////////// diff --git a/src_v2/platform/win32/lumenarium_win32_window.cpp b/src_v2/platform/win32/lumenarium_win32_window.cpp index 54ca09f..bc5030c 100644 --- a/src_v2/platform/win32/lumenarium_win32_window.cpp +++ b/src_v2/platform/win32/lumenarium_win32_window.cpp @@ -381,6 +381,7 @@ win32_window_opengl_get_wgl_ext(HINSTANCE hinstance) wgl_load_ext(result, glGenBuffers); wgl_load_ext(result, glBindBuffer); wgl_load_ext(result, glBufferData); + wgl_load_ext(result, glBufferSubData); wgl_load_ext(result, glCreateShader); wgl_load_ext(result, glShaderSource); wgl_load_ext(result, glCompileShader); @@ -395,6 +396,8 @@ win32_window_opengl_get_wgl_ext(HINSTANCE hinstance) wgl_load_ext(result, glGetShaderInfoLog); wgl_load_ext(result, glGetProgramiv); wgl_load_ext(result, glGetProgramInfoLog); + wgl_load_ext(result, glGetUniformLocation); + wgl_load_ext(result, glUniformMatrix4fv); } wglMakeCurrent(NULL, NULL); diff --git a/src_v2/user_space/user_space_incenter.cpp b/src_v2/user_space/user_space_incenter.cpp index 180161b..124f476 100644 --- a/src_v2/user_space/user_space_incenter.cpp +++ b/src_v2/user_space/user_space_incenter.cpp @@ -10,8 +10,6 @@ incenter_get_init_desc() internal void incenter_init(App_State* state) { - return; - // create a fake sculpture Assembly_Handle ah = assembly_add(&state->assemblies, lit_str("test"), 3000, 100); From d7c708e5d49ba1942f8e61d6051470f66ea6865c Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 7 Apr 2022 14:02:12 +0200 Subject: [PATCH 119/151] text rendering --- .../intel/debug/lumenarium_first_win32.obj | Bin 246446 -> 439282 bytes src_v2/editor/lumenarium_editor.cpp | 39 +- src_v2/editor/lumenarium_editor_ui.cpp | 96 +- src_v2/editor/lumenarium_editor_ui.h | 2 + src_v2/libs/stb_truetype.h | 4853 +++++++++++++++++ src_v2/lumenarium_first.cpp | 2 +- src_v2/lumenarium_texture_atlas.cpp | 140 +- src_v2/lumenarium_types.h | 1 + src_v2/platform/lumenarium_assert.h | 9 +- src_v2/platform/lumenarium_platform.h | 10 +- .../lumenarium_platform_common_includes.h | 16 +- .../platform/wasm/lumenarium_wasm_webgl.cpp | 4 +- .../win32/lumenarium_win32_graphics.cpp | 4 +- 13 files changed, 5096 insertions(+), 80 deletions(-) create mode 100644 src_v2/libs/stb_truetype.h diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj index 848a54baeada618f9cba1429eba09517b66f8569..a1f03174444ba1f278b233da4c3b35ca29e4a473 100644 GIT binary patch literal 439282 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 z

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 NC!TxeDe^7&{{tGsrMUnA delta 75537 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
Hhw%`N@{41#Fh!uVUw=}fei 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 diff --git a/src_v2/editor/lumenarium_editor.cpp b/src_v2/editor/lumenarium_editor.cpp index 9d32da7..96edf05 100644 --- a/src_v2/editor/lumenarium_editor.cpp +++ b/src_v2/editor/lumenarium_editor.cpp @@ -91,9 +91,44 @@ void make_quad(Platform_Geometry_Buffer* geo, Platform_Shader* shd, Platform_Tex } internal void -ed_load_font_cb(Platform_File_Async_Job_Args result) +ed_load_font_cb(Platform_File_Async_Job_Args result, u8* user_data) { - s32 x = 5; + App_State* state = (App_State*)user_data; + UI* ui = &state->editor->ui; + + u8* f = result.data.base; + stbtt_fontinfo font; + if (!stbtt_InitFont(&font, f, stbtt_GetFontOffsetForIndex(f, 0))) + { + invalid_code_path; + } + + r32 scale = stbtt_ScaleForPixelHeight(&font, 18.0f); + s32 ascent, descent, line_gap; + stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap); + ui->font_ascent = (r32)ascent * scale; + ui->font_descent = (r32)descent * scale; + ui->font_line_gap = (r32)line_gap * scale; + if (ui->font_line_gap == 0) ui->font_line_gap = 5; + + String c = lit_str("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-=_+[]{}\\|;:'\",<.>/?`~"); + for (u64 i = 0; i < c.len; i++) + { + s32 w, h, xoff, yoff; + u32 id = (u32)c.str[i]; + u8* bp = stbtt_GetCodepointBitmap(&font, 0, scale, (char)c.str[i], &w, &h, &xoff, &yoff); + s32 x0, y0, x1, y1; + stbtt_GetCodepointBitmapBoxSubpixel(&font, (char)c.str[i], scale, scale, 0, 0, &x0, &y0, &x1, &y1); + + v2 offset = v2{ 0, (r32)y0 }; + texture_atlas_register(&state->editor->ui.atlas, bp, (u32)w, (u32)h, id, offset, TextureAtlasRegistration_PixelFormat_Alpha); + stbtt_FreeBitmap(bp, 0); + } + + Texture_Atlas_Sprite m_sprite = texture_atlas_sprite_get(&state->editor->ui.atlas, (u32)'m'); + ui->font_space_width = (r32)(m_sprite.max_x - m_sprite.min_x); + + platform_texture_update(ui->atlas_texture, ui->atlas.pixels, 1024, 1024, 1024); } internal void diff --git a/src_v2/editor/lumenarium_editor_ui.cpp b/src_v2/editor/lumenarium_editor_ui.cpp index 75ffc24..5e397cf 100644 --- a/src_v2/editor/lumenarium_editor_ui.cpp +++ b/src_v2/editor/lumenarium_editor_ui.cpp @@ -23,7 +23,6 @@ static String ui_shader_frag_win32 = lit_str( "uniform sampler2D texture;\n" "void main(void) {\n" " FragColor = texture(texture, uv) * color;\n" - " if (FragColor.w <= 0.01f) discard;\n" "}" ); @@ -73,8 +72,13 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) result.atlas = texture_atlas_create(1024, 1024, 512, permanent); result.atlas_texture = platform_texture_create(result.atlas.pixels, 1024, 1024, 1024); - u32 white_sprite[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; - ui_sprite_register(&result, (u8*)white_sprite, 2, 2, WHITE_SPRITE_ID); + u32 white_sprite[] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + }; + ui_sprite_register(&result, (u8*)white_sprite, 4, 4, WHITE_SPRITE_ID); return result; } @@ -119,14 +123,17 @@ ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c) internal void ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id) { - texture_atlas_register(&ui->atlas, pixels, w, h, id); + texture_atlas_register(&ui->atlas, pixels, w, h, id, v2{0,0}, TextureAtlasRegistration_PixelFormat_RGBA); platform_texture_update(ui->atlas_texture, ui->atlas.pixels, ui->atlas.width, ui->atlas.height, ui->atlas.width); } internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color) { - v4 uv = texture_atlas_sprite_get_uvs(&ui->atlas, id); + Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(&ui->atlas, id); + v4 uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite); + pmin.XY += sprite.draw_offset; + pmax.XY += sprite.draw_offset; ui_quad_push(ui, pmin, pmax, uv.xy, uv.zw, color); } @@ -136,6 +143,37 @@ ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id) ui_sprite_push(ui, pmin, pmax, id, v4{1,1,1,1}); } +struct UI_Char_Draw_Cmd +{ + v4 uv; + v3 pmin; + v3 pmax; + v3 baseline_after; +}; + +internal UI_Char_Draw_Cmd +ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint) +{ + UI_Char_Draw_Cmd result = {}; + + Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(&ui->atlas, codepoint); + result.uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite); + + v3 dim = v3{ + (r32)(sprite.max_x - sprite.min_x), + (r32)(sprite.max_y - sprite.min_y), + 0, + }; + result.pmin = at; + result.pmin.XY += sprite.draw_offset; + result.pmin = v3_floor(result.pmin); + result.pmax = result.pmin + dim; + + result.baseline_after = v3{ result.pmax.x, at.y, at.z }; + + return result; +} + internal void ui_frame_prepare(UI* ui, v2 window_dim) { @@ -184,12 +222,12 @@ ui_draw(UI* ui) UI_Widget_Result r0 = ui_widget_push(ui, d0); UI_Widget_Desc d1 = d0; - d1.style.flags |= UIWidgetStyle_Outline | UIWidgetStyle_MouseClick; - d1.style.color_bg = PINK_V4; + d1.style.flags |= UIWidgetStyle_Outline | UIWidgetStyle_MouseClick | UIWidgetStyle_Text; + d1.style.color_bg = PINK_V4;//{ 0.1f, 0.1f, 0.1f, 1.0f }; // d1.style.color_fg = GREEN_V4; d1.p_min = v2{ 512, 32 }; d1.p_max = v2{ 640, 128 }; - d1.string = lit_str("Hello"); + d1.string = lit_str("Hello there my friend, what's going on?"); UI_Widget_Result r1 = ui_widget_push(ui, d1); bool clicked_r1 = has_flag(r1.flags, UIWidgetResult_MouseLeft_WentUp); if (clicked_r1) show = !show; @@ -214,7 +252,7 @@ ui_draw(UI* ui) u32 widget_count = ui->widgets.free_len; r32 range_min = -10; r32 range_max = -1; - r32 range_step = (range_max - range_min) / (r32)widget_count; + r32 range_step = (range_max - range_min) / (r32)(widget_count * 4); ui_widgets_to_geometry_recursive(ui, ui->widgets.root, -10, range_step); platform_geometry_buffer_update( @@ -402,14 +440,48 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s if (has_flag(child->desc.style.flags, UIWidgetStyle_Bg)) { - bg_min.z += z_step; - bg_max.z += z_step; + z_at += z_step; + bg_min.z = z_at; + bg_max.z = z_at; ui_sprite_push(ui, bg_min, bg_max, desc.style.sprite, color_bg); } if (has_flag(child->desc.style.flags, UIWidgetStyle_Text)) { - // TODO(PS): + z_at += z_step + z_step; + r32 space_width = ui->font_space_width; + r32 to_baseline = ui->font_line_gap + ui->font_ascent; + v3 line_offset = { 5, 3 + to_baseline, 0 }; + r32 baseline_x_start = desc.p_min.x + line_offset.x; + r32 baseline_y_start = desc.p_min.y + line_offset.y; + v3 baseline = { baseline_x_start, baseline_y_start, z_at }; + for (u64 i = 0; i < child->desc.string.len; i++) + { + u8 at = child->desc.string.str[i]; + UI_Char_Draw_Cmd cmd = {}; + if (!char_is_space(at)) + { + cmd = ui_sprite_char_get_draw_cmd(ui, baseline, (u32)at); + } + else + { + cmd.baseline_after = baseline; + cmd.baseline_after.x += space_width; + } + + if (cmd.baseline_after.x >= desc.p_max.x - 5) + { + baseline.x = baseline_x_start; + baseline.y += ui->font_ascent + ui->font_descent + ui->font_line_gap; + cmd = ui_sprite_char_get_draw_cmd(ui, baseline, (u32)at); + } + + if (!char_is_space(at)) + { + ui_quad_push(ui, cmd.pmin, cmd.pmax, cmd.uv.xy, cmd.uv.zw, color_fg); + } + baseline = cmd.baseline_after; + } } if (child->child_first) diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h index d2254c2..23eadc8 100644 --- a/src_v2/editor/lumenarium_editor_ui.h +++ b/src_v2/editor/lumenarium_editor_ui.h @@ -130,6 +130,7 @@ struct UI u32 indices_cap; Texture_Atlas atlas; + r32 font_ascent, font_descent, font_line_gap, font_space_width; UI_Widget_Pool widgets; UI_Style_Sheet* style_sheet; @@ -156,6 +157,7 @@ internal void ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c); internal void ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id); internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color); internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id); +internal v3 ui_sprite_char_push(UI* ui, v2 at, u32 codepoint, v4 color); internal void ui_draw(UI* ui); // Widgets diff --git a/src_v2/libs/stb_truetype.h b/src_v2/libs/stb_truetype.h new file mode 100644 index 0000000..3ffe4de --- /dev/null +++ b/src_v2/libs/stb_truetype.h @@ -0,0 +1,4853 @@ +// stb_truetype.h - v1.19 - public domain +// authored from 2009-2016 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya +// Daniel Ribeiro Maciel +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket Fabian "ryg" Giesen +// Cass Everitt Martins Mozeiko +// stoiko (Haemimont Games) Cap Petschulat +// Brian Hook Omar Cornut +// Walter van Niftrik github:aloucks +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. github:oyvindjam +// Brian Costabile github:vassvik +// +// VERSION HISTORY +// +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// See end of file for license information. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversampling() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to = 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype, e.g. if you don't +//// link with the C runtime library. + +#ifdef STB_TRUETYPE_IMPLEMENTATION +// #define your own (u)stbtt_int8/16/32 before including to override this +#ifndef stbtt_uint8 +typedef unsigned char stbtt_uint8; +typedef signed char stbtt_int8; +typedef unsigned short stbtt_uint16; +typedef signed short stbtt_int16; +typedef unsigned int stbtt_uint32; +typedef signed int stbtt_int32; +#endif + +typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; +typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + +// e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h +#ifndef STBTT_ifloor +#include +#define STBTT_ifloor(x) ((int) floor(x)) +#define STBTT_iceil(x) ((int) ceil(x)) +#endif + +#ifndef STBTT_sqrt +#include +#define STBTT_sqrt(x) sqrt(x) +#define STBTT_pow(x,y) pow(x,y) +#endif + +#ifndef STBTT_fmod +#include +#define STBTT_fmod(x,y) fmod(x,y) +#endif + +#ifndef STBTT_cos +#include +#define STBTT_cos(x) cos(x) +#define STBTT_acos(x) acos(x) +#endif + +#ifndef STBTT_fabs +#include +#define STBTT_fabs(x) fabs(x) +#endif + +// #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h +#ifndef STBTT_malloc +#include +#define STBTT_malloc(x,u) ((void)(u),malloc(x)) +#define STBTT_free(x,u) ((void)(u),free(x)) +#endif + +#ifndef STBTT_assert +#include +#define STBTT_assert(x) assert(x) +#endif + +#ifndef STBTT_strlen +#include +#define STBTT_strlen(x) strlen(x) +#endif + +#ifndef STBTT_memcpy +#include +#define STBTT_memcpy memcpy +#define STBTT_memset memset +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + // private structure + typedef struct + { + unsigned char *data; + int cursor; + int size; + } stbtt__buf; + + ////////////////////////////////////////////////////////////////////////////// + // + // TEXTURE BAKING API + // + // If you use this API, you only have to call two functions ever. + // + + typedef struct + { + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + } stbtt_bakedchar; + + STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long + // if return is positive, the first unused row of the bitmap + // if return is negative, returns the negative of the number of characters that fit + // if return is 0, no characters fit and no rows were used + // This uses a very crappy packing. + + typedef struct + { + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right + } stbtt_aligned_quad; + + STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier + // Call GetBakedQuad with char_index = 'character - first_char', and it + // creates the quad you need to draw and advances the current position. + // + // The coordinate system used assumes y increases downwards. + // + // Characters will extend both above and below the current position; + // see discussion of "BASELINE" above. + // + // It's inefficient; you might want to c&p it and optimize it. + + + + ////////////////////////////////////////////////////////////////////////////// + // + // NEW TEXTURE BAKING API + // + // This provides options for packing multiple fonts into one atlas, not + // perfectly but better than nothing. + + typedef struct + { + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; + } stbtt_packedchar; + + typedef struct stbtt_pack_context stbtt_pack_context; + typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION + typedef struct stbrp_rect stbrp_rect; +#endif + + STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); + // Initializes a packing context stored in the passed-in stbtt_pack_context. + // Future calls using this context will pack characters into the bitmap passed + // in here: a 1-channel bitmap that is width * height. stride_in_bytes is + // the distance from one row to the next (or 0 to mean they are packed tightly + // together). "padding" is the amount of padding to leave between each + // character (normally you want '1' for bitmaps you'll use as textures with + // bilinear filtering). + // + // Returns 0 on failure, 1 on success. + + STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); + // Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + + STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); + // Creates character bitmaps from the font_index'th font found in fontdata (use + // font_index=0 if you don't know what that is). It creates num_chars_in_range + // bitmaps for characters with unicode values starting at first_unicode_char_in_range + // and increasing. Data for how to render them is stored in chardata_for_range; + // pass these to stbtt_GetPackedQuad to get back renderable quads. + // + // font_size is the full height of the character from ascender to descender, + // as computed by stbtt_ScaleForPixelHeight. To use a point size as computed + // by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() + // and pass that result as 'font_size': + // ..., 20 , ... // font max minus min y is 20 pixels tall + // ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + + typedef struct + { + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally + } stbtt_pack_range; + + STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); + // Creates character bitmaps from multiple ranges of characters stored in + // ranges. This will usually create a better-packed bitmap than multiple + // calls to stbtt_PackFontRange. Note that you can call this multiple + // times within a single PackBegin/PackEnd. + + STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); + // Oversampling a font increases the quality by allowing higher-quality subpixel + // positioning, and is especially valuable at smaller text sizes. + // + // This function sets the amount of oversampling for all following calls to + // stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given + // pack context. The default (no oversampling) is achieved by h_oversample=1 + // and v_oversample=1. The total number of pixels required is + // h_oversample*v_oversample larger than the default; for example, 2x2 + // oversampling requires 4x the storage of 1x1. For best results, render + // oversampled textures with bilinear filtering. Look at the readme in + // stb/tests/oversample for information about oversampled fonts + // + // To use with PackFontRangesGather etc., you must set it before calls + // call to PackFontRangesGatherRects. + + STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + + STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); + STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); + STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); + // Calling these functions in sequence is roughly equivalent to calling + // stbtt_PackFontRanges(). If you more control over the packing of multiple + // fonts, or if you want to pack custom data into a font texture, take a look + // at the source to of stbtt_PackFontRanges() and create a custom version + // using these functions, e.g. call GatherRects multiple times, + // building up a single array of rects, then call PackRects once, + // then call RenderIntoRects repeatedly. This may result in a + // better packing than calling PackFontRanges multiple times + // (or it may not). + + // this is an opaque structure that you shouldn't mess with which holds + // all the context needed from PackBegin to PackEnd. + struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; + }; + + ////////////////////////////////////////////////////////////////////////////// + // + // FONT LOADING + // + // + + STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); + // This function will determine the number of fonts in a font file. TrueType + // collection (.ttc) files may contain multiple fonts, while TrueType font + // (.ttf) files only contain one font. The number of fonts can be used for + // indexing with the previous function where the index is between zero and one + // less than the total fonts. If an error occurs, -1 is returned. + + STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); + // Each .ttf/.ttc file may have more than one font. Each font has a sequential + // index number starting from 0. Call this function to get the font offset for + // a given index; it returns -1 if the index is out of range. A regular .ttf + // file will only define one font and it always be at offset 0, so it will + // return '0' for index 0, and -1 for all other indices. + + // The following structure is defined publically so you can declare one on + // the stack or as a global or etc, but you should treat it as opaque. + struct stbtt_fontinfo + { + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict + }; + + STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); + // Given an offset into the file that defines a font, this function builds + // the necessary cached info for the rest of the system. You must allocate + // the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't + // need to do anything special to free it, because the contents are pure + // value data with no additional data structures. Returns 0 on failure. + + + ////////////////////////////////////////////////////////////////////////////// + // + // CHARACTER TO GLYPH-INDEX CONVERSIOn + + STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); + // If you're going to perform multiple operations on the same character + // and you want a speed-up, call this function with the character you're + // going to process, then use glyph-based functions instead of the + // codepoint-based functions. + + + ////////////////////////////////////////////////////////////////////////////// + // + // CHARACTER PROPERTIES + // + + STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); + // computes a scale factor to produce a font whose "height" is 'pixels' tall. + // Height is measured as the distance from the highest ascender to the lowest + // descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics + // and computing: + // scale = pixels / (ascent - descent) + // so if you prefer to measure height by the ascent only, use a similar calculation. + + STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); + // computes a scale factor to produce a font whose EM size is mapped to + // 'pixels' tall. This is probably what traditional APIs compute, but + // I'm not positive. + + STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); + // ascent is the coordinate above the baseline the font extends; descent + // is the coordinate below the baseline the font extends (i.e. it is typically negative) + // lineGap is the spacing between one row's descent and the next row's ascent... + // so you should advance the vertical position by "*ascent - *descent + *lineGap" + // these are expressed in unscaled coordinates, so you must multiply by + // the scale factor for a given size + + STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); + // analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 + // table (specific to MS/Windows TTF files). + // + // Returns 1 on success (table present), 0 on failure. + + STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); + // the bounding box around all possible characters + + STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); + // leftSideBearing is the offset from the current horizontal position to the left edge of the character + // advanceWidth is the offset from the current horizontal position to the next horizontal position + // these are expressed in unscaled coordinates + + STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); + // an additional amount to add to the 'advance' value between ch1 and ch2 + + STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); + // Gets the bounding box of the visible part of the glyph, in unscaled coordinates + + STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); + STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); + STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + // as above, but takes one or more glyph indices for greater efficiency + + + ////////////////////////////////////////////////////////////////////////////// + // + // GLYPH SHAPES (you probably don't need these, but they have to go before + // the bitmaps for C declaration-order reasons) + // + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) +#define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + + STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); + // returns non-zero if nothing is drawn for this glyph + + STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); + STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); + // returns # of vertices and fills *vertices with the pointer to them + // these are expressed in "unscaled" coordinates + // + // The shape is a series of countours. Each one starts with + // a STBTT_moveto, then consists of a series of mixed + // STBTT_lineto and STBTT_curveto segments. A lineto + // draws a line from previous endpoint to its x,y; a curveto + // draws a quadratic bezier from previous endpoint to + // its x,y, using cx,cy as the bezier control point. + + STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); + // frees the data allocated above + + ////////////////////////////////////////////////////////////////////////////// + // + // BITMAP RENDERING + // + + STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); + // frees the bitmap allocated below + + STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); + // allocates a large-enough single-channel 8bpp bitmap and renders the + // specified character/glyph at the specified scale into it, with + // antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). + // *width & *height are filled out with the width & height of the bitmap, + // which is stored left-to-right, top-to-bottom. + // + // xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + + STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); + // the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel + // shift for the character + + STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); + // the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap + // in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap + // is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the + // width and height and positioning info for it first. + + STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); + // same as stbtt_MakeCodepointBitmap, but you can specify a subpixel + // shift for the character + + STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); + // same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering + // is performed (see stbtt_PackSetOversampling) + + STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); + // get the bbox of the bitmap centered around the glyph origin; so the + // bitmap width is ix1-ix0, height is iy1-iy0, and location to place + // the bitmap top left is (leftSideBearing*scale,iy0). + // (Note that the bitmap uses y-increases-down, but the shape uses + // y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + + STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + // same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel + // shift for the character + + // the following functions are equivalent to the above functions, but operate + // on glyph indices instead of Unicode codepoints (for efficiency) + STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); + STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); + STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); + STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); + STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); + STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); + STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + + // @TODO: don't expose this structure + typedef struct + { + int w,h,stride; + unsigned char *pixels; + } stbtt__bitmap; + + // rasterize a shape with quadratic beziers into a bitmap + STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + + ////////////////////////////////////////////////////////////////////////////// + // + // Signed Distance Function (or Field) rendering + + STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); + // frees the SDF bitmap allocated below + + STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); + STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); + // These functions compute a discretized SDF field for a single character, suitable for storing + // in a single-channel texture, sampling with bilinear filtering, and testing against + // larger than some threshhold to produce scalable fonts. + // info -- the font + // scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap + // glyph/codepoint -- the character to generate the SDF for + // padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), + // which allows effects like bit outlines + // onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) + // pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) + // if positive, > onedge_value is inside; if negative, < onedge_value is inside + // width,height -- output height & width of the SDF bitmap (including padding) + // xoff,yoff -- output origin of the character + // return value -- a 2D array of bytes 0..255, width*height in size + // + // pixel_dist_scale & onedge_value are a scale & bias that allows you to make + // optimal use of the limited 0..255 for your application, trading off precision + // and special effects. SDF values outside the range 0..255 are clamped to 0..255. + // + // Example: + // scale = stbtt_ScaleForPixelHeight(22) + // padding = 5 + // onedge_value = 180 + // pixel_dist_scale = 180/5.0 = 36.0 + // + // This will create an SDF bitmap in which the character is about 22 pixels + // high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled + // shape, sample the SDF at each pixel and fill the pixel if the SDF value + // is greater than or equal to 180/255. (You'll actually want to antialias, + // which is beyond the scope of this example.) Additionally, you can compute + // offset outlines (e.g. to stroke the character border inside & outside, + // or only outside). For example, to fill outside the character up to 3 SDF + // pixels, you would compare against (180-36.0*3)/255 = 72/255. The above + // choice of variables maps a range from 5 pixels outside the shape to + // 2 pixels inside the shape to 0..255; this is intended primarily for apply + // outside effects only (the interior range is needed to allow proper + // antialiasing of the font at *smaller* sizes) + // + // The function computes the SDF analytically at each SDF pixel, not by e.g. + // building a higher-res bitmap and approximating it. In theory the quality + // should be as high as possible for an SDF of this size & representation, but + // unclear if this is true in practice (perhaps building a higher-res bitmap + // and computing from that can allow drop-out prevention). + // + // The algorithm has not been optimized at all, so expect it to be slow + // if computing lots of characters or very large sizes. + + + + ////////////////////////////////////////////////////////////////////////////// + // + // Finding the right font... + // + // You should really just solve this offline, keep your own tables + // of what font is what, and don't try to get it out of the .ttf file. + // That's because getting it out of the .ttf file is really hard, because + // the names in the file can appear in many possible encodings, in many + // possible languages, and e.g. if you need a case-insensitive comparison, + // the details of that depend on the encoding & language in a complex way + // (actually underspecified in truetype, but also gigantic). + // + // But you can use the provided functions in two possible ways: + // stbtt_FindMatchingFont() will use *case-sensitive* comparisons on + // unicode-encoded names to try to find the font you want; + // you can run this before calling stbtt_InitFont() + // + // stbtt_GetFontNameString() lets you get any of the various strings + // from the file yourself and do your own comparisons on them. + // You have to have called stbtt_InitFont() first. + + + STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); + // returns the offset (not index) of the font that matches, or -1 if none + // if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". + // if you use any other flag, use a font name like "Arial"; this checks + // the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + + STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); + // returns 1/0 whether the first string interpreted as utf8 is identical to + // the second string interpreted as big-endian utf16... useful for strings from next func + + STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); + // returns the string (which may be big-endian double byte, e.g. for unicode) + // and puts the length in bytes in *length. + // + // some of the values for the IDs are below; for more see the truetype spec: + // http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html + // http://www.microsoft.com/typography/otspec/name.htm + + enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 + }; + + enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 + }; + + enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 + }; + + enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 + }; + + enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D + }; + + enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 + }; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // fallthrough + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch(coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + // Binary search. + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + } break; + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + // Binary search. + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + } break; + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch(classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + + classDefTable = classDef1ValueArray + 2 * glyphCount; + } break; + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + // Binary search. + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + + classDefTable = classRangeRecords + 6 * classRangeCount; + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + } break; + } + + return -1; +} + +// Define to STBTT_assert(x) if you want to break on unimplemented formats. +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } break; + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + STBTT_assert(glyph1class < class1Count); + STBTT_assert(glyph2class < class2Count); + + // TODO: Support more formats. + STBTT_GPOS_TODO_assert(valueFormat1 == 4); + if (valueFormat1 != 4) return 0; + STBTT_GPOS_TODO_assert(valueFormat2 == 0); + if (valueFormat2 != 0) return 0; + + if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { + stbtt_uint8 *class1Records = table + 16; + stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); + stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + break; + }; + } + } + break; + }; + + default: + // TODO: Implement other stuff. + break; + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + + if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; +#if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; +#elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = sy1 - sy0; + STBTT_assert(x >= 0 && x < len); + scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; + scanline_fill[x] += e->direction * height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = (x1+1 - x0) * dy + y_top; + + sign = e->direction; + // area of the rectangle covered from y0..y_crossing + area = sign * (y_crossing-sy0); + // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) + scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * (x2 - (x1+1)); + + STBTT_assert(STBTT_fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); + + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clearly-defined pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + STBTT_assert(z->ey >= scan_y_top); + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshhold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; + error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) STBTT_sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + orig[0] = x; + orig[1] = y; + + // make sure y never passes through a vertex of the shape + y_frac = (float) STBTT_fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + c*x^2 + b*x + a = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + // if one scale is 0, use same scale for both + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; // if both scales are 0, return NULL + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve + float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (verts[i].type == STBTT_vline) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3],px,py,t,it; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.cpp index e4f4e0d..6804a52 100644 --- a/src_v2/lumenarium_first.cpp +++ b/src_v2/lumenarium_first.cpp @@ -44,7 +44,7 @@ lumenarium_frame_prepare(App_State* state) } incenter_frame_prepare(state); - platform_file_async_jobs_do_work(4); + platform_file_async_jobs_do_work(4, (u8*)state); } internal void diff --git a/src_v2/lumenarium_texture_atlas.cpp b/src_v2/lumenarium_texture_atlas.cpp index e56888f..c60b7fe 100644 --- a/src_v2/lumenarium_texture_atlas.cpp +++ b/src_v2/lumenarium_texture_atlas.cpp @@ -9,17 +9,19 @@ struct Texture_Atlas_Sprite u16 min_y; u16 max_x; u16 max_y; + + v2 draw_offset; }; struct Texture_Atlas { u8* pixels; - u16 width; - u16 height; + u32 width; + u32 height; - u16 next_x; - u16 next_y; - u16 y_used; + u32 next_x; + u32 next_y; + u32 y_used; u32* ids; Texture_Atlas_Sprite* sprites; @@ -36,7 +38,7 @@ texture_atlas_create(u32 width, u32 height, u32 cap, Allocator* allocator) result.height = (u16)height; for (u32 i = 0; i < width * height; i++) { u8* base = result.pixels + (i * 4); - *(u32*)base = 0xFFFFFFFF; + *(u32*)base = 0x00FFFFFF; } result.ids = allocator_alloc_array(allocator, u32, cap); @@ -46,52 +48,109 @@ texture_atlas_create(u32 width, u32 height, u32 cap, Allocator* allocator) return result; } -internal void -texture_atlas_register(Texture_Atlas* ta, u8* pixels, u32 width, u32 height, u32 id) +enum Texture_Atlas_Registration_Flags { - u16 min_x = ta->next_x; - u16 min_y = ta->next_y; - u16 max_x = min_x + (u16)width; - u16 max_y = min_y + (u16)height; + TextureAtlasRegistration_None = 0, + TextureAtlasRegistration_PixelFormat_RGBA = 1, + TextureAtlasRegistration_PixelFormat_Alpha = 2, +}; + +internal void +texture_atlas_register(Texture_Atlas* ta, u8* pixels, u32 width, u32 height, u32 id, v2 draw_offset, u32 flags) +{ + if (ta->next_x > ta->width || (ta->next_x + width + 2) > ta->width) + { + ta->next_x = 0; + ta->next_y = ta->y_used; + } - // TODO(PS): if the sprite won't fit in this row, then we need to shift it to - // the next one + u32 min_x = ta->next_x + 1; + u32 min_y = ta->next_y + 1; + u32 max_x = min_x + width; + u32 max_y = min_y + height; // copy the data - for (u16 y = 0; y < height; y++) + if (has_flag(flags, TextureAtlasRegistration_PixelFormat_RGBA)) { - u16 src_row = (y * (u16)width) * 4; - u16 dst_row = (((y + min_y) * ta->width) + min_x) * 4; - for (u16 x = 0; x < width; x++) + for (u32 y = 0; y < height; y++) { - ta->pixels[dst_row++] = pixels[src_row++]; - ta->pixels[dst_row++] = pixels[src_row++]; - ta->pixels[dst_row++] = pixels[src_row++]; - ta->pixels[dst_row++] = pixels[src_row++]; + u32 src_row = (y * width) * 4; + u32 dst_row = (((y + min_y) * ta->width) + min_x) * 4; + for (u32 x = 0; x < width; x++) + { + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + } } } + else if (has_flag(flags, TextureAtlasRegistration_PixelFormat_Alpha)) + { + for (u32 y = 0; y < height; y++) + { + u32 src_row = y * width; + u32 dst_row = (((y + min_y) * ta->width) + min_x) * 4; + for (u32 x = 0; x < width; x++) + { + ta->pixels[dst_row++] = 0xFF; + ta->pixels[dst_row++] = 0xFF; + ta->pixels[dst_row++] = 0xFF; + ta->pixels[dst_row++] = pixels[src_row++]; + } + } + } + + // copy nearest pixels to the border + u32 pi_width = ta->width; + u32 pi_stride = 4; +#define PIXEL_INDEX(x,y) ((((y) * pi_width) + (x)) * pi_stride) +#define COPY_PIXEL(db,sb) \ +ta->pixels[(db) + 0] = ta->pixels[(sb) + 0]; \ +ta->pixels[(db) + 1] = ta->pixels[(sb) + 1]; \ +ta->pixels[(db) + 2] = ta->pixels[(sb) + 2]; \ +ta->pixels[(db) + 3] = ta->pixels[(sb) + 3]; + + for (u32 x = 0; x < width; x++) + { + u32 top = PIXEL_INDEX(min_x + x, min_y - 1); + u32 top_near = PIXEL_INDEX(min_x + x, min_y); + u32 bot = PIXEL_INDEX(min_x + x, max_y); + u32 bot_near = PIXEL_INDEX(min_x + x, max_y - 1); + COPY_PIXEL(top, top_near); + COPY_PIXEL(bot, bot_near); + } + + for (u32 y = 0; y < height + 2; y++) + { + u32 left = PIXEL_INDEX(min_x - 1, min_y + y - 1); + u32 left_near = PIXEL_INDEX(min_x, min_y + y - 1); + u32 right = PIXEL_INDEX(max_x, min_y + y - 1); + u32 right_near = PIXEL_INDEX(max_x - 1, min_y + y - 1); + COPY_PIXEL(left, left_near); + COPY_PIXEL(right, right_near); + } + +#undef PIXEL_INDEX +#undef COPY_PIXEL // register a new slot u32 index = hash_table_register(ta->ids, ta->cap, id); Texture_Atlas_Sprite* sprite = ta->sprites + index; - sprite->min_x = min_x; - sprite->min_y = min_y; - sprite->max_x = max_x; - sprite->max_y = max_y; + sprite->min_x = (u16)min_x; + sprite->min_y = (u16)min_y; + sprite->max_x = (u16)max_x; + sprite->max_y = (u16)max_y; + sprite->draw_offset = draw_offset; // Prepare for next registration if (max_y > ta->y_used) { - ta->y_used = max_y; + ta->y_used = max_y + 2; } - ta->next_x = max_x + 1; - if (ta->next_x > ta->width) - { - ta->next_x = 0; - ta->next_y = ta->y_used; - } + ta->next_x = max_x + 2; } internal Texture_Atlas_Sprite @@ -105,9 +164,8 @@ texture_atlas_sprite_get(Texture_Atlas* ta, u32 id) } internal v4 -texture_atlas_sprite_get_uvs(Texture_Atlas* ta, u32 id) +texture_atlas_sprite_get_uvs(Texture_Atlas* ta, Texture_Atlas_Sprite sprite) { - Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(ta, id); v4 result = {}; // uv min @@ -118,11 +176,13 @@ texture_atlas_sprite_get_uvs(Texture_Atlas* ta, u32 id) result.z = (r32)sprite.max_x / (r32)ta->width; result.w = (r32)sprite.max_y / (r32)ta->height; - // inset - v2 half_texel = v2{1.0f / ta->width, 1.0f / ta->height}; - result.xy += half_texel; - result.zw -= half_texel; - return result; } + +internal v4 +texture_atlas_sprite_get_uvs(Texture_Atlas* ta, u32 id) +{ + Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(ta, id); + return texture_atlas_sprite_get_uvs(ta, sprite); +} #endif //LUMENARIUM_TEXTURE_ATLAS_CPP diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h index d26977a..bf829e9 100644 --- a/src_v2/lumenarium_types.h +++ b/src_v2/lumenarium_types.h @@ -244,6 +244,7 @@ hash_table_find(u32* ids, u32 cap, u32 value) // Vector Extensions #define v2_to_v3(xy,z) v3{(xy).x, (xy).y, (z)} +#define v3_floor(v) v3{ floorf(v.x), floorf(v.y), floorf(v.z) } ////////////////////////////////////////////// // Color Constants diff --git a/src_v2/platform/lumenarium_assert.h b/src_v2/platform/lumenarium_assert.h index be84e78..9210788 100644 --- a/src_v2/platform/lumenarium_assert.h +++ b/src_v2/platform/lumenarium_assert.h @@ -26,7 +26,7 @@ void close_err_file() {} // 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) +# define assert_always (*((volatile int*)0) = 0xFFFF) #else WASM_EXTERN void wasm_assert_always(char* file, unsigned int file_len, unsigned int line); @@ -34,12 +34,13 @@ WASM_EXTERN void wasm_assert_always(char* file, unsigned int file_len, unsigned #endif // defined(PLATFORM_WASM) #ifdef USE_ASSERTS -# define assert(c) \ +# define assert(c) do { \ if (!(c)) { \ -err_write("Assert Hit: %s:%d\n", __FILE__, (u32)__LINE__); \ +err_write("Assert Hit: %s:%u\n", __FILE__, (u32)__LINE__); \ close_err_file(); \ assert_always; \ -} +} \ +} while(false) // useful for catching cases that you aren't sure you'll hit, but // want to be alerted when they happen diff --git a/src_v2/platform/lumenarium_platform.h b/src_v2/platform/lumenarium_platform.h index 0f50ddf..4f06e9c 100644 --- a/src_v2/platform/lumenarium_platform.h +++ b/src_v2/platform/lumenarium_platform.h @@ -101,7 +101,7 @@ struct Platform_File_Async_Job_Args u32 error; }; -typedef void Platform_File_Async_Cb(Platform_File_Async_Job_Args args); +typedef void Platform_File_Async_Cb(Platform_File_Async_Job_Args args, u8* user_data); struct Platform_File_Async_Job { @@ -187,9 +187,9 @@ platform_file_async_write(String path, Data data, Platform_File_Async_Cb* cb) } void -platform_file_async_job_complete(Platform_File_Async_Job* job) +platform_file_async_job_complete(Platform_File_Async_Job* job, u8* user_data) { - job->cb(job->args); + job->cb(job->args, user_data); allocator_free(platform_file_jobs_arena, job->job_memory.base, job->job_memory.size); if (has_flag(job->args.flags, PlatformFileAsyncJob_Write)) { @@ -200,7 +200,7 @@ platform_file_async_job_complete(Platform_File_Async_Job* job) void platform_file_async_work_on_job(Platform_File_Async_Job* job); void -platform_file_async_jobs_do_work(u64 max_jobs) +platform_file_async_jobs_do_work(u64 max_jobs, u8* user_data) { u64 to_do = max_jobs; if (max_jobs > platform_file_async_jobs_len) to_do = platform_file_async_jobs_len; @@ -216,7 +216,7 @@ platform_file_async_jobs_do_work(u64 max_jobs) platform_file_async_work_on_job(job); if (has_flag(job->args.flags, completed)) { - platform_file_async_job_complete(job); + platform_file_async_job_complete(job, user_data); platform_file_async_job_rem(i); } } diff --git a/src_v2/platform/lumenarium_platform_common_includes.h b/src_v2/platform/lumenarium_platform_common_includes.h index 802889c..1b6799a 100644 --- a/src_v2/platform/lumenarium_platform_common_includes.h +++ b/src_v2/platform/lumenarium_platform_common_includes.h @@ -13,6 +13,10 @@ #include "lumenarium_assert.h" +#define STB_TRUETYPE_IMPLEMENTATION +#define STBTT_assert(x) assert(x) +#include "../libs/stb_truetype.h" + // NOTE(PS): only need the opengl extension headers // when running on a platform that is using opengl 3.3+ #if !defined(PLATFORM_wasm) @@ -21,18 +25,6 @@ # include "wglext.h" #endif -#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 diff --git a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp index 0d04110..55a8efd 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp +++ b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp @@ -206,8 +206,8 @@ platform_texture_create(u8* pixels, u32 width, u32 height, u32 stride) glGenTextures(1, &result.id, sizeof(u32)); glBindTexture(GL_TEXTURE_2D, result.id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); diff --git a/src_v2/platform/win32/lumenarium_win32_graphics.cpp b/src_v2/platform/win32/lumenarium_win32_graphics.cpp index cc9bd19..cb02e6b 100644 --- a/src_v2/platform/win32/lumenarium_win32_graphics.cpp +++ b/src_v2/platform/win32/lumenarium_win32_graphics.cpp @@ -255,9 +255,9 @@ platform_texture_create(u8* pixels, u32 width, u32 height, u32 stride) glBindTexture(GL_TEXTURE_2D, result.id); win32_gl_no_error(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); win32_gl_no_error(); From c9709a7eac2d3fdd2a2961f06340d973886d1364 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 7 Apr 2022 15:48:16 +0200 Subject: [PATCH 120/151] ui elements + layout --- .../intel/debug/lumenarium_first_win32.obj | Bin 439282 -> 450459 bytes src_v2/editor/lumenarium_editor.cpp | 26 +++ src_v2/editor/lumenarium_editor_ui.cpp | 185 +++++++++++++----- src_v2/editor/lumenarium_editor_ui.h | 41 +++- src_v2/lumenarium_types.h | 1 + 5 files changed, 198 insertions(+), 55 deletions(-) diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj index a1f03174444ba1f278b233da4c3b35ca29e4a473..a60b65b42de5e3e0d04fbe4b00a1967adefe2e02 100644 GIT binary patch delta 93269 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 IhPe&?54EP~tN;K2 delta 83114 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 diff --git a/src_v2/editor/lumenarium_editor.cpp b/src_v2/editor/lumenarium_editor.cpp index 96edf05..7329a79 100644 --- a/src_v2/editor/lumenarium_editor.cpp +++ b/src_v2/editor/lumenarium_editor.cpp @@ -155,6 +155,32 @@ ed_frame_prepare(App_State* state) internal void ed_frame(App_State* state) { + UI* ui = &state->editor->ui; + + UI_Layout layout = {}; + layout.bounds_min = v2{ 500, 200 }; + layout.bounds_max = v2{ 700, 500 }; + layout.row_height = ui->font_ascent + ui->font_descent + ui->font_line_gap + 15; + layout.row_gap = 2; + layout.col_gap = 2; + layout.at = layout.bounds_min; + ui->layout = &layout; + + + ui_text(ui, lit_str("Hi there!")); + show = ui_toggle(ui, lit_str("my toggle"), show); + if (show) + { + ui_layout_row_begin(ui, 2); + { + ui_button(ui, lit_str("Sup")); + ui_button(ui, lit_str("you there")); + } + ui_layout_row_end(ui); + } + + ui_button(ui, lit_str("Hi there my good sir")); + edr_render_begin(state); ui_draw(&state->editor->ui); edr_render(state); diff --git a/src_v2/editor/lumenarium_editor_ui.cpp b/src_v2/editor/lumenarium_editor_ui.cpp index 5e397cf..647919b 100644 --- a/src_v2/editor/lumenarium_editor_ui.cpp +++ b/src_v2/editor/lumenarium_editor_ui.cpp @@ -80,6 +80,7 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) }; ui_sprite_register(&result, (u8*)white_sprite, 4, 4, WHITE_SPRITE_ID); + ui_create_default_style_sheet(); return result; } @@ -166,7 +167,7 @@ ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint) }; result.pmin = at; result.pmin.XY += sprite.draw_offset; - result.pmin = v3_floor(result.pmin); + result.pmin.XY = v2_floor(result.pmin.XY); result.pmax = result.pmin + dim; result.baseline_after = v3{ result.pmax.x, at.y, at.z }; @@ -208,47 +209,6 @@ global bool show = false; internal void ui_draw(UI* ui) { - UI_Widget_Desc d0 = { - { - (UIWidgetStyle_Bg), - WHITE_V4, - BLACK_V4, - WHITE_SPRITE_ID, - }, - lit_str("Hi there!"), - v2{ 32.0f, 32.0f }, - v2{ 128.0f, 64.0f }, - }; - UI_Widget_Result r0 = ui_widget_push(ui, d0); - - UI_Widget_Desc d1 = d0; - d1.style.flags |= UIWidgetStyle_Outline | UIWidgetStyle_MouseClick | UIWidgetStyle_Text; - d1.style.color_bg = PINK_V4;//{ 0.1f, 0.1f, 0.1f, 1.0f }; // - d1.style.color_fg = GREEN_V4; - d1.p_min = v2{ 512, 32 }; - d1.p_max = v2{ 640, 128 }; - d1.string = lit_str("Hello there my friend, what's going on?"); - UI_Widget_Result r1 = ui_widget_push(ui, d1); - bool clicked_r1 = has_flag(r1.flags, UIWidgetResult_MouseLeft_WentUp); - if (clicked_r1) show = !show; - - UI_Widget_Result r2 = {}; - if (show) - { - UI_Widget_Desc d2 = d1; - d1.string = lit_str("Hello There"); - d1.p_min = v2{ 560, 64 }; - d1.p_max = v2{ 700, 256 }; - r2 = ui_widget_push(ui, d1); - } - - bool clicked_r2 = has_flag(r2.flags, UIWidgetResult_MouseLeft_WentUp); - assert( - (!clicked_r1 && !clicked_r2) || - (clicked_r1 && !clicked_r2) || - (!clicked_r1 && clicked_r2) - ); - u32 widget_count = ui->widgets.free_len; r32 range_min = -10; r32 range_max = -1; @@ -434,21 +394,22 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s if (has_flag(child->desc.style.flags, UIWidgetStyle_Outline)) { ui_sprite_push(ui, bg_min, bg_max, WHITE_SPRITE_ID, color_fg); - bg_min += v3{ 3, 3, 0}; - bg_max -= v3{ 3, 3, 0}; + z_at += z_step; + bg_min += v3{ 1, 1, 0}; + bg_max -= v3{ 1, 1, 0}; + } if (has_flag(child->desc.style.flags, UIWidgetStyle_Bg)) { - z_at += z_step; bg_min.z = z_at; bg_max.z = z_at; ui_sprite_push(ui, bg_min, bg_max, desc.style.sprite, color_bg); + z_at += z_step; } - if (has_flag(child->desc.style.flags, UIWidgetStyle_Text)) + if (has_flag(child->desc.style.flags, UIWidgetStyle_TextWrap | UIWidgetStyle_TextClip)) { - z_at += z_step + z_step; r32 space_width = ui->font_space_width; r32 to_baseline = ui->font_line_gap + ui->font_ascent; v3 line_offset = { 5, 3 + to_baseline, 0 }; @@ -471,6 +432,8 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s if (cmd.baseline_after.x >= desc.p_max.x - 5) { + if (has_flag(child->desc.style.flags, UIWidgetStyle_TextClip)) break; + baseline.x = baseline_x_start; baseline.y += ui->font_ascent + ui->font_descent + ui->font_line_gap; cmd = ui_sprite_char_get_draw_cmd(ui, baseline, (u32)at); @@ -495,12 +458,101 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s } /////////////////////////////////////////// -// Specific Widget Implementations +// Layout Manager -global UI_Style_Sheet ui_default_style_sheet = { +internal void +ui_layout_row_begin(UI_Layout* layout, u32 cols) +{ + layout->mode = UILayout_Rows; + layout->cols = cols; +} +internal void +ui_layout_row_begin(UI* ui, u32 cols) { ui_layout_row_begin(ui->layout, cols); } + +internal void +ui_layout_row_end(UI_Layout* layout) +{ + layout->mode = UILayout_Columns; +} +internal void +ui_layout_row_end(UI* ui) { ui_layout_row_end(ui->layout); } + +internal UI_Layout_Bounds +ui_layout_get_next(UI_Layout* layout) +{ + UI_Layout_Bounds result = {}; + switch (layout->mode) { + case UILayout_Columns: + { + result.min = layout->at; + result.max = v2{ layout->bounds_max.x, layout->at.y + layout->row_height }; + layout->at = v2{ layout->bounds_min.x, result.max.y + layout->row_gap}; + } break; + case UILayout_Rows: + { + r32 col_width = (layout->bounds_max.x - layout->bounds_min.x) / layout->cols; + col_width -= (layout->cols - 1) * layout->col_gap; + result.min = layout->at; + result.max = v2{ layout->at.x + col_width, layout->at.y + layout->row_height }; + layout->at = v2{ result.max.x + layout->col_gap, layout->at.y }; + if (layout->at.x >= layout->bounds_max.x) + { + layout->at = v2{ + layout->bounds_min.x, + layout->at.y + layout->row_height + layout->row_gap + }; + } + } break; + + invalid_default_case; } + return result; +} +internal UI_Layout_Bounds +ui_layout_get_next(UI* ui) { return ui_layout_get_next(ui->layout); } + + +/////////////////////////////////////////// +// Specific Widget Implementations +// +// These all rely on a layout manager to make calling them simpler + +global UI_Style_Sheet ui_default_style_sheet = {}; + +internal void +ui_create_default_style_sheet() +{ + ui_default_style_sheet.styles[UIWidget_Text] = { + (UIWidgetStyle_TextWrap), v4{0,0,0,0}, WHITE_V4, WHITE_SPRITE_ID + }; + ui_default_style_sheet.styles[UIWidget_Button] = { + (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseClick), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + }; + ui_default_style_sheet.styles[UIWidget_Toggle] = { + (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_MouseClick), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + }; + ui_default_style_sheet.styles[UIWidget_Menu] = ui_default_style_sheet.styles[UIWidget_Toggle]; + ui_default_style_sheet.styles[UIWidget_Dropdown] = ui_default_style_sheet.styles[UIWidget_Toggle]; + + ui_default_style_sheet.styles[UIWidget_HSlider] = { + (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDrag), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + }; + ui_default_style_sheet.styles[UIWidget_VSlider] = { + (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDrag), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + }; + ui_default_style_sheet.styles[UIWidget_HScroll] = { + (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDrag), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + }; + ui_default_style_sheet.styles[UIWidget_VScroll] = { + (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDrag), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + }; + + ui_default_style_sheet.styles[UIWidget_Window] = { + (UIWidgetStyle_TextWrap), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + }; + }; internal UI_Widget_Style @@ -510,15 +562,46 @@ ui_get_style(UI* ui, UI_Widget_Kind kind) return ui_default_style_sheet.styles[kind]; } +internal UI_Widget_Desc +ui_layout_next_widget(UI* ui, UI_Widget_Kind kind) +{ + UI_Layout_Bounds b = ui_layout_get_next(ui); + UI_Widget_Desc d = {}; + d.p_min = b.min; + d.p_max = b.max; + d.style = ui_get_style(ui, kind); + return d; +} + internal void ui_text(UI* ui, String string) { - + UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Text); + d.string = string; + ui_widget_push(ui, d); } internal bool ui_button(UI* ui, String string) { - return false; + UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Button); + d.string = string; + UI_Widget_Result r = ui_widget_push(ui, d); + return has_flag(r.flags, UIWidgetResult_MouseLeft_WentUp); } +internal bool +ui_toggle(UI* ui, String string, bool value) +{ + UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Button); + if (value) { + v4 t = d.style.color_fg; + d.style.color_fg = d.style.color_bg; + d.style.color_bg = t; + } + d.string = string; + UI_Widget_Result r = ui_widget_push(ui, d); + bool result = value; + if (has_flag(r.flags, UIWidgetResult_MouseLeft_WentUp)) result = !result; + return result; +} diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h index 23eadc8..cd8be05 100644 --- a/src_v2/editor/lumenarium_editor_ui.h +++ b/src_v2/editor/lumenarium_editor_ui.h @@ -32,10 +32,11 @@ enum { UIWidgetStyle_None = 0, UIWidgetStyle_Bg = 1, - UIWidgetStyle_Text = 2, - UIWidgetStyle_Outline = 4, - UIWidgetStyle_MouseClick = 8, - UIWidgetStyle_MouseDrag = 16, + UIWidgetStyle_TextClip = 2, + UIWidgetStyle_TextWrap = 4, + UIWidgetStyle_Outline = 8, + UIWidgetStyle_MouseClick = 16, + UIWidgetStyle_MouseDrag = 32, }; // akin to a css class, could be used to style multiple @@ -119,6 +120,34 @@ struct UI_Widget_Pool UI_Widget* active_parent; }; +enum UI_Layout_Mode +{ + // each element takes up a whole row + UILayout_Columns, + + // each element takes up one column in the row. If you overflow, + // the layout manager overflows to the next row + UILayout_Rows, +}; + +struct UI_Layout +{ + v2 bounds_min; + v2 bounds_max; + r32 row_height; + r32 row_gap; + r32 col_gap; + v2 at; + UI_Layout_Mode mode; + u32 cols; +}; + +struct UI_Layout_Bounds +{ + v2 min; + v2 max; +}; + struct UI { UI_Vertex* verts; @@ -138,6 +167,8 @@ struct UI UI_Widget_Id widget_next_hot; UI_Widget_Id widget_hot; + UI_Layout* layout; + // frames since these values were set u16 widget_next_hot_frames; u16 widget_hot_frames; @@ -162,6 +193,8 @@ internal void ui_draw(UI* ui); // Widgets +internal void ui_create_default_style_sheet(); + internal UI_Widget_Id ui_widget_id_create(u32 index_in_parent, String string); internal bool ui_widget_id_equals(UI_Widget_Id a, UI_Widget_Id b); internal bool ui_widget_id_is_valid(UI_Widget_Id h); diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h index bf829e9..91dc9c1 100644 --- a/src_v2/lumenarium_types.h +++ b/src_v2/lumenarium_types.h @@ -244,6 +244,7 @@ hash_table_find(u32* ids, u32 cap, u32 value) // Vector Extensions #define v2_to_v3(xy,z) v3{(xy).x, (xy).y, (z)} +#define v2_floor(v) v2{ floorf(v.x), floorf(v.y) } #define v3_floor(v) v3{ floorf(v.x), floorf(v.y), floorf(v.z) } ////////////////////////////////////////////// From 0e7a1b5536c968cec0f8a2e6280c809c7ff3ffa3 Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 7 Apr 2022 19:51:15 +0200 Subject: [PATCH 121/151] scroll bars --- .../intel/debug/lumenarium_first_win32.obj | Bin 450459 -> 473884 bytes src_v2/editor/lumenarium_editor.cpp | 38 ++- src_v2/editor/lumenarium_editor_ui.cpp | 283 ++++++++++++++++-- src_v2/editor/lumenarium_editor_ui.h | 38 ++- src_v2/lumenarium_first.cpp | 6 + src_v2/lumenarium_string.cpp | 22 ++ src_v2/lumenarium_types.h | 7 + .../platform/win32/lumenarium_first_win32.cpp | 17 ++ 8 files changed, 371 insertions(+), 40 deletions(-) diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj index a60b65b42de5e3e0d04fbe4b00a1967adefe2e02..7c9356ef88532028ab2955e1e738319a8f81a6b5 100644 GIT binary patch delta 121448 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

8$P-A^PR`PwlL+z=NG0A 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%0D5vWCq1kS`sQu9sfmS&-BBwwaY!Lx1W0H7q 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 Nfuj0FpXz-Q{~K~H3k3iG delta 102810 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 uGcA-oQ8}qTnxsTMMGxW?geditor->ui, state->editor->window_dim); } +global r32 p = 0.3f; + internal void ed_frame(App_State* state) { @@ -160,14 +162,11 @@ ed_frame(App_State* state) UI_Layout layout = {}; layout.bounds_min = v2{ 500, 200 }; layout.bounds_max = v2{ 700, 500 }; - layout.row_height = ui->font_ascent + ui->font_descent + ui->font_line_gap + 15; - layout.row_gap = 2; - layout.col_gap = 2; + ui_layout_set_row_info(ui, &layout); layout.at = layout.bounds_min; - ui->layout = &layout; + ui_layout_push(ui, &layout); - - ui_text(ui, lit_str("Hi there!")); + ui_text_f(ui, "Hi there! %d", 1000); show = ui_toggle(ui, lit_str("my toggle"), show); if (show) { @@ -178,9 +177,34 @@ ed_frame(App_State* state) } ui_layout_row_end(ui); } - ui_button(ui, lit_str("Hi there my good sir")); + ui_scroll_view_begin(ui, lit_str("scroll area"), v2{800, 200}, v2{1000,500}, 8); + { + ui_button(ui, lit_str("Sup")); + ui_button(ui, lit_str("you there")); + ui_button(ui, lit_str("Sup")); + ui_button(ui, lit_str("you there")); + + ui_button(ui, lit_str("Sup")); + ui_button(ui, lit_str("you there")); + ui_button(ui, lit_str("Sup")); + ui_button(ui, lit_str("you there")); + + ui_button(ui, lit_str("Sup")); + ui_button(ui, lit_str("you there")); + ui_button(ui, lit_str("Sup")); + ui_button(ui, lit_str("you there")); + + ui_button(ui, lit_str("Sup")); + ui_button(ui, lit_str("you there")); + ui_button(ui, lit_str("Sup")); + ui_button(ui, lit_str("I'm lastf;")); + } + ui_scroll_view_end(ui); + + ui_layout_pop(ui); + edr_render_begin(state); ui_draw(&state->editor->ui); edr_render(state); diff --git a/src_v2/editor/lumenarium_editor_ui.cpp b/src_v2/editor/lumenarium_editor_ui.cpp index 647919b..08985c1 100644 --- a/src_v2/editor/lumenarium_editor_ui.cpp +++ b/src_v2/editor/lumenarium_editor_ui.cpp @@ -35,6 +35,9 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) // Widgets result.widgets.free = allocator_alloc_array(a, UI_Widget, widget_pool_cap); result.widgets.free_cap = widget_pool_cap; + result.widgets.states_cap = 3 * widget_pool_cap; + result.widgets.states = allocator_alloc_array(a, UI_Widget_State, result.widgets.states_cap); + result.widgets.states_hash = allocator_alloc_array(a, u32, result.widgets.states_cap); // Per Frame Vertex Buffer result.verts_cap = verts_cap; @@ -192,15 +195,12 @@ ui_frame_prepare(UI* ui, v2 window_dim) if (ui->widget_next_hot.value != 0) { ui->widget_next_hot_frames += 1; - if (ui->widget_hot_frames > 1) - { - ui->widget_next_hot = UI_Widget_Id{0}; - } + if (ui->widget_next_hot_frames > 1) ui_widget_next_hot_set(ui, 0); } if (ui->widget_hot.value != 0) { ui->widget_hot_frames += 1; - if (ui->widget_hot_frames > 1) ui->widget_hot = UI_Widget_Id{0}; + if (ui->widget_hot_frames > 1) ui_widget_hot_set(ui, 0); } } @@ -229,6 +229,8 @@ ui_draw(UI* ui) platform_texture_bind(ui->atlas_texture); platform_geometry_bind(ui->per_frame_buffer); platform_geometry_draw(ui->per_frame_buffer, ui->indices_len); + + OutputDebugStringA("Frame\n\n"); } //////////////////////////////////////////// @@ -244,6 +246,20 @@ ui_widget_id_create(String string, u32 index) return result; } +internal UI_Widget_State* +ui_widget_state_get(UI_Widget_Pool* pool, UI_Widget_Id id) +{ + u32 index = hash_table_find(pool->states_hash, pool->states_cap, id.value); + assert(index != pool->states_cap); + UI_Widget_State* result = pool->states + index; + return result; +} +internal UI_Widget_State* +ui_widget_state_get(UI* ui, UI_Widget_Id id) +{ + return ui_widget_state_get(&ui->widgets, id); +} + internal UI_Widget* ui_widget_pool_push(UI_Widget_Pool* pool, String string) { @@ -256,6 +272,11 @@ ui_widget_pool_push(UI_Widget_Pool* pool, String string) result->child_first = 0; result->child_last = 0; + u32 index = hash_table_register(pool->states_hash, pool->states_cap, result->id.value); + assert(index != pool->states_cap); + UI_Widget_State* state = pool->states + index; + zero_struct(*state); + if (pool->active_parent) { result->parent = pool->active_parent; @@ -293,14 +314,22 @@ ui_widget_id_is_valid(UI_Widget_Id h) internal void ui_widget_next_hot_set(UI* ui, UI_Widget* w) { - ui->widget_next_hot = w->id; + if (w) { + ui->widget_next_hot = w->id; + } else { + ui->widget_next_hot = UI_Widget_Id{0}; + } ui->widget_next_hot_frames = 0; } internal void ui_widget_hot_set(UI* ui, UI_Widget* w) { - ui->widget_hot = w->id; + if (w) { + ui->widget_hot = w->id; + } else { + ui->widget_hot = UI_Widget_Id{0}; + } ui->widget_hot_frames = 0; } @@ -308,11 +337,26 @@ internal UI_Widget_Result ui_widget_push(UI* ui, UI_Widget_Desc desc) { UI_Widget_Result result = {}; + v2 dim = desc.p_max - desc.p_min; + if (dim.x == 0 || dim.y == 0) return result; UI_Widget* w = ui_widget_pool_push(&ui->widgets, desc.string); w->desc = desc; + result.id = w->id; - if (has_flag(desc.style.flags, UIWidgetStyle_MouseClick)) + UI_Widget_State* state = ui_widget_state_get(ui, w->id); + w->desc.fill_pct = state->scroll; + + v2 mouse_p = ui->input->frame_hot->mouse_pos; + bool mouse_over = ( + mouse_p.x >= desc.p_min.x && mouse_p.x <= desc.p_max.x && + mouse_p.y >= desc.p_min.y && mouse_p.y <= desc.p_max.y + ); + + UI_Widget_Style_Flags flags = desc.style.flags; + UI_Widget_Style_Flags mask_drag = (UIWidgetStyle_MouseDragH | UIWidgetStyle_MouseDragV); + UI_Widget_Style_Flags mask_hover = (mask_drag | UIWidgetStyle_MouseClick); + if (has_flag(flags, mask_hover)) { // CASES: // Mouse Over | Mouse Clicked | Is Next Hot | Response @@ -323,12 +367,6 @@ ui_widget_push(UI* ui, UI_Widget_Desc desc) // t | t | f | become next hot // t | t | t | become hot - v2 mouse_p = ui->input->frame_hot->mouse_pos; - bool mouse_over = ( - mouse_p.x >= desc.p_min.x && mouse_p.x <= desc.p_max.x && - mouse_p.y >= desc.p_min.y && mouse_p.y <= desc.p_max.y - ); - if (mouse_over) { if (ui_widget_id_equals(w->id, ui->widget_next_hot)) @@ -341,10 +379,11 @@ ui_widget_push(UI* ui, UI_Widget_Desc desc) if (input_key_went_up(ui->input, KeyCode_MouseLeftButton)) { result.flags |= UIWidgetResult_MouseLeft_WentUp; - ui->widget_hot = UI_Widget_Id{0}; + ui_widget_hot_set(ui, 0); } } - else if ((w->id.index >= ui->widget_next_hot.index) && ui->widget_hot.value == 0) + + if ((w->id.index >= ui->widget_next_hot.index) && ui->widget_hot.value == 0) { ui_widget_next_hot_set(ui, w); } @@ -353,11 +392,37 @@ ui_widget_push(UI* ui, UI_Widget_Desc desc) { if (ui_widget_id_equals(w->id, ui->widget_next_hot)) { - ui->widget_next_hot = UI_Widget_Id{0}; + ui_widget_next_hot_set(ui, 0); } } } + if(ui_widget_id_equals(w->id, ui->widget_hot)) + { + if (input_key_is_down(ui->input, KeyCode_MouseLeftButton)) + { + ui_widget_next_hot_set(ui, w); + ui_widget_hot_set(ui, w); + result.flags |= UIWidgetResult_MouseLeft_IsDown; + } + + if (has_flag(flags, mask_drag)) + { + v2 drag_pct_mask = { + has_flag(flags, UIWidgetStyle_MouseDragH) ? 1.0f : 0.0f, + has_flag(flags, UIWidgetStyle_MouseDragV) ? 1.0f : 0.0f + }; + v2 drag = ui->input->frame_hot->mouse_pos - w->desc.p_min; + drag = v2{ clamp(0, drag.x, w->desc.p_max.x), clamp(0, drag.y, w->desc.p_max.y) }; + drag *= drag_pct_mask; + v2 drag_pct = drag / dim; + drag_pct = v2{ clamp(0, drag_pct.x, 1), clamp(0, drag_pct.y, 1) }; + result.drag = drag_pct; + + state->scroll = drag_pct; + } + } + return result; } @@ -387,8 +452,8 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s } if (ui_widget_id_equals(ui->widget_hot, child->id)) { - color_fg = desc.style.color_fg; - color_bg = desc.style.color_bg; + //color_fg = desc.style.color_fg; + //color_bg = desc.style.color_bg; } if (has_flag(child->desc.style.flags, UIWidgetStyle_Outline)) @@ -397,7 +462,6 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s z_at += z_step; bg_min += v3{ 1, 1, 0}; bg_max -= v3{ 1, 1, 0}; - } if (has_flag(child->desc.style.flags, UIWidgetStyle_Bg)) @@ -408,6 +472,45 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s z_at += z_step; } + if (has_flag(child->desc.style.flags, (UIWidgetStyle_FillH | UIWidgetStyle_FillV))) + { + v3 fill_min = {}; + v3 fill_max = {}; + if (has_flag(child->desc.style.flags, UIWidgetStyle_FillH)) + { + r32 fill_x = HMM_Lerp(bg_min.x, child->desc.fill_pct.x, bg_max.x); + + if (has_flag(child->desc.style.flags, UIWidgetStyle_LineInsteadOfFill)) + { + fill_min = v3{ fill_x, bg_min.y, z_at }; + fill_max = v3{ fill_x + 1, bg_max.y, z_at }; + } + else + { + fill_min = bg_min; + fill_max = v3{ fill_x, bg_max.y, z_at }; + } + } + else if (has_flag(child->desc.style.flags, UIWidgetStyle_FillV)) + { + r32 fill_y = HMM_Lerp(bg_min.y, child->desc.fill_pct.y, bg_max.y); + + if (has_flag(child->desc.style.flags, UIWidgetStyle_LineInsteadOfFill)) + { + fill_min = v3{ bg_min.x, fill_y, z_at }; + fill_max = v3{ bg_max.x, fill_y + 1, z_at }; + } + else + { + fill_min = bg_min; + fill_max = v3{ bg_max.x, fill_y, z_at }; + } + } + + ui_sprite_push(ui, fill_min, fill_max, WHITE_SPRITE_ID, color_fg); + z_at += z_step; + } + if (has_flag(child->desc.style.flags, UIWidgetStyle_TextWrap | UIWidgetStyle_TextClip)) { r32 space_width = ui->font_space_width; @@ -460,6 +563,38 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s /////////////////////////////////////////// // Layout Manager +internal void +ui_layout_set_row_info(UI* ui, UI_Layout* l) +{ + l->row_height = (ui->font_ascent + ui->font_descent + ui->font_line_gap + 15); + l->row_gap = 2; + l->col_gap = 2; +} + +internal void +ui_layout_push(UI* ui, UI_Layout* layout) +{ + if (ui->layout) + { + layout->parent = ui->layout; + ui->layout = layout; + } + else + { + ui->layout = layout; + layout->parent = 0; + } +} + +internal void +ui_layout_pop(UI* ui) +{ + if (ui->layout) + { + ui->layout = ui->layout->parent; + } +} + internal void ui_layout_row_begin(UI_Layout* layout, u32 cols) { @@ -481,6 +616,12 @@ internal UI_Layout_Bounds ui_layout_get_next(UI_Layout* layout) { UI_Layout_Bounds result = {}; + if (layout->at.x >= layout->bounds_max.x || layout->at.y >= layout->bounds_max.y || + layout->at.y + layout->row_height >= layout->bounds_max.y) + { + return result; + } + switch (layout->mode) { case UILayout_Columns: @@ -508,6 +649,14 @@ ui_layout_get_next(UI_Layout* layout) invalid_default_case; } + + if (result.min.x < layout->bounds_min.x || result.min.y < layout->bounds_min.y || + result.max.x < layout->bounds_min.x || result.max.y < layout->bounds_min.y) + { + result.min = {}; + result.max = {}; + } + return result; } internal UI_Layout_Bounds @@ -537,16 +686,16 @@ ui_create_default_style_sheet() ui_default_style_sheet.styles[UIWidget_Dropdown] = ui_default_style_sheet.styles[UIWidget_Toggle]; ui_default_style_sheet.styles[UIWidget_HSlider] = { - (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDrag), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragH | UIWidgetStyle_FillH ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; ui_default_style_sheet.styles[UIWidget_VSlider] = { - (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDrag), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragV | UIWidgetStyle_FillV ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; ui_default_style_sheet.styles[UIWidget_HScroll] = { - (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDrag), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragH | UIWidgetStyle_FillH | UIWidgetStyle_LineInsteadOfFill ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; ui_default_style_sheet.styles[UIWidget_VScroll] = { - (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDrag), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID + (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragV | UIWidgetStyle_FillV | UIWidgetStyle_LineInsteadOfFill ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; ui_default_style_sheet.styles[UIWidget_Window] = { @@ -581,6 +730,17 @@ ui_text(UI* ui, String string) ui_widget_push(ui, d); } +internal void +ui_text_f(UI* ui, char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + String string = string_fv(scratch, fmt, args); + va_end(args); + + return ui_text(ui, string); +} + internal bool ui_button(UI* ui, String string) { @@ -605,3 +765,78 @@ ui_toggle(UI* ui, String string, bool value) if (has_flag(r.flags, UIWidgetResult_MouseLeft_WentUp)) result = !result; return result; } + +internal UI_Layout* +ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 rows) +{ + r32 scroll_bar_dim = 15; + v2 scroll_bars_area = v2{0, 0}; + v2 scroll_area_min = bounds_min; + v2 scroll_area_max = bounds_max - scroll_bars_area; + v2 scroll_area_dim = scroll_area_max - scroll_area_min; + + v2 scroll_offset = {}; + r32 rows_avail = floorf(scroll_area_dim.y / ui->layout->row_height); + if (rows > rows_avail) + { + scroll_bars_area = v2{ scroll_bar_dim, 0}; + scroll_area_min = bounds_min; + scroll_area_max = bounds_max - scroll_bars_area; + scroll_area_dim = scroll_area_max - scroll_area_min; + + UI_Widget_Desc vscroll_d = {}; + vscroll_d.p_min = { bounds_max.x - scroll_bar_dim, bounds_min.y }; + vscroll_d.p_max = { bounds_max.x, bounds_max.y }; + vscroll_d.style = ui_get_style(ui, UIWidget_VScroll); + vscroll_d.string = string_f(scratch, "%.*s_vscroll", str_varg(string)); + UI_Widget_Result r = ui_widget_push(ui, vscroll_d); + + UI_Widget_State* vscroll_state = ui_widget_state_get(ui, r.id); + scroll_offset.y = vscroll_state->scroll.y; + } + + r32 rows_scroll_to = max(0, rows - (rows_avail - 1)); + r32 y_scroll_dist = rows_scroll_to * ui->layout->row_height; + + scroll_offset *= v2{ 0, y_scroll_dist }; + + UI_Layout* layout = allocator_alloc_struct(scratch, UI_Layout); + layout->mode = UILayout_Columns; + layout->bounds_min = scroll_area_min; + layout->bounds_max = scroll_area_max; + ui_layout_set_row_info(ui, layout); + layout->at = bounds_min - scroll_offset; + ui_layout_push(ui, layout); + + return layout; +} + +internal void +ui_scroll_view_end(UI* ui) +{ + ui_layout_pop(ui); +} + +#if 0 +internal bool +ui_dropdown_begin(UI* ui, String string, bool state) +{ + bool result = ui_toggle(ui, string, state); + UI_Layout* layout = allocator_alloc_struct(scratch, UI_Layout); + zero_struct(*layout); + if (result) + { + ui_scroll_view_begin(ui, layout); + } + return result; +} + +internal void +ui_dropdown_end(UI* ui, bool state) +{ + if (state) + { + ui_scroll_view_end(ui); + } +} +#endif \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h index cd8be05..4e53539 100644 --- a/src_v2/editor/lumenarium_editor_ui.h +++ b/src_v2/editor/lumenarium_editor_ui.h @@ -30,13 +30,17 @@ union UI_Widget_Id typedef u32 UI_Widget_Style_Flags; enum { - UIWidgetStyle_None = 0, - UIWidgetStyle_Bg = 1, - UIWidgetStyle_TextClip = 2, - UIWidgetStyle_TextWrap = 4, - UIWidgetStyle_Outline = 8, - UIWidgetStyle_MouseClick = 16, - UIWidgetStyle_MouseDrag = 32, + UIWidgetStyle_None = 0, + UIWidgetStyle_Bg = 1 << 0, + UIWidgetStyle_TextClip = 1 << 1, + UIWidgetStyle_TextWrap = 1 << 2, + UIWidgetStyle_Outline = 1 << 3, + UIWidgetStyle_MouseClick = 1 << 4, + UIWidgetStyle_MouseDragH = 1 << 5, + UIWidgetStyle_MouseDragV = 1 << 6, + UIWidgetStyle_FillH = 1 << 7, + UIWidgetStyle_FillV = 1 << 8, + UIWidgetStyle_LineInsteadOfFill = 1 << 9, }; // akin to a css class, could be used to style multiple @@ -46,7 +50,6 @@ struct UI_Widget_Style UI_Widget_Style_Flags flags; v4 color_bg; v4 color_fg; - u32 sprite; }; @@ -54,6 +57,7 @@ struct UI_Widget_Style struct UI_Widget_Desc { UI_Widget_Style style; + v2 fill_pct; String string; v2 p_min; v2 p_max; @@ -80,7 +84,9 @@ enum struct UI_Widget_Result { + UI_Widget_Id id; UI_Widget_Result_Flags flags; + v2 drag; }; enum UI_Widget_Kind @@ -110,6 +116,11 @@ struct UI_Style_Sheet UI_Widget_Style styles[UIWidget_Count]; }; +struct UI_Widget_State +{ + v2 scroll; +}; + struct UI_Widget_Pool { UI_Widget* free; @@ -118,6 +129,10 @@ struct UI_Widget_Pool UI_Widget* root; UI_Widget* active_parent; + + UI_Widget_State* states; + u32* states_hash; + u32 states_cap; }; enum UI_Layout_Mode @@ -132,13 +147,15 @@ enum UI_Layout_Mode struct UI_Layout { + UI_Layout_Mode mode; + UI_Layout* parent; + v2 bounds_min; v2 bounds_max; r32 row_height; r32 row_gap; r32 col_gap; v2 at; - UI_Layout_Mode mode; u32 cols; }; @@ -199,6 +216,9 @@ internal UI_Widget_Id ui_widget_id_create(u32 index_in_parent, String string internal bool ui_widget_id_equals(UI_Widget_Id a, UI_Widget_Id b); internal bool ui_widget_id_is_valid(UI_Widget_Id h); +internal void ui_widget_next_hot_set(UI* ui, UI_Widget* w); +internal void ui_widget_hot_set(UI* ui, UI_Widget* w); + internal UI_Widget* ui_widget_pool_push(UI_Widget_Pool* pool, String string); internal void ui_widget_pool_pop(UI_Widget_Pool* pool); diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.cpp index 6804a52..ea5f7d5 100644 --- a/src_v2/lumenarium_first.cpp +++ b/src_v2/lumenarium_first.cpp @@ -80,6 +80,12 @@ lumenarium_event(Platform_Window_Event evt, App_State* state) case WindowEvent_ButtonUp: { frame->key_flags[evt.key_code] = evt.key_flags; + if (evt.key_code == KeyCode_MouseLeftButton) + { + state->input_state->mouse_pos_down = v2{ + (r32)evt.mouse_x, (r32)evt.mouse_y + }; + } } break; case WindowEvent_Char: diff --git a/src_v2/lumenarium_string.cpp b/src_v2/lumenarium_string.cpp index adff229..e9f2a8b 100644 --- a/src_v2/lumenarium_string.cpp +++ b/src_v2/lumenarium_string.cpp @@ -245,3 +245,25 @@ string_copy(String s, Allocator* a) return result; } +internal String +string_fv(Allocator* a, char* fmt, va_list args) +{ + va_list args1; + va_copy(args1, args); + s32 needed = vsnprintf(0, 0, fmt, args); + String result = allocator_alloc_string(a, needed + 1); + result.len = vsnprintf((char*)result.str, result.cap, fmt, args1); + result.str[result.len] = 0; + va_end(args1); + return result; +} + +internal String +string_f(Allocator* a, char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + String result = string_fv(a, fmt, args); + va_end(args); + return result; +} \ No newline at end of file diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h index 91dc9c1..8851563 100644 --- a/src_v2/lumenarium_types.h +++ b/src_v2/lumenarium_types.h @@ -57,6 +57,13 @@ data_create(u8* base, u64 size) internal void memory_zero(u8* base, u64 size); internal void memory_copy(u8* from, u8* to, u64 size); +////////////////////////////////////////////// +// Math + +#define lerp(a,t,b) (a) + ((1.0f - (t)) * (b)) +#define clamp(r0,v,r1) min((r1),max((r0),(v))) +#define lerp_clamp(a,t,b) clamp((a),lerp((a),(t),(b)),(b)) + ////////////////////////////////////////////// // String diff --git a/src_v2/platform/win32/lumenarium_first_win32.cpp b/src_v2/platform/win32/lumenarium_first_win32.cpp index a18e2d3..9dc68a8 100644 --- a/src_v2/platform/win32/lumenarium_first_win32.cpp +++ b/src_v2/platform/win32/lumenarium_first_win32.cpp @@ -71,6 +71,17 @@ win32_button_event(Platform_Key_Code key, bool is_down, bool was_down) return evt; } +internal void +win32_set_mouse_pos_evt(Platform_Window_Event* evt) +{ + POINT mouse_p; + GetCursorPos(&mouse_p); + ScreenToClient(win32_main_window.window_handle, &mouse_p); + + evt->mouse_x = mouse_p.x; + evt->mouse_y = mouse_p.y; +} + internal void win32_window_handle_event(MSG msg, Win32_Window* win, App_State* state) { @@ -90,6 +101,7 @@ win32_window_handle_event(MSG msg, Win32_Window* win, App_State* state) KeyCode_MouseLeftButton, true, false ); + win32_set_mouse_pos_evt(&evt); lumenarium_event(evt, state); win32_mouse_capture(win); }break; @@ -100,6 +112,7 @@ win32_window_handle_event(MSG msg, Win32_Window* win, App_State* state) KeyCode_MouseMiddleButton, true, false ); + win32_set_mouse_pos_evt(&evt); lumenarium_event(evt, state); win32_mouse_capture(win); }break; @@ -110,6 +123,7 @@ win32_window_handle_event(MSG msg, Win32_Window* win, App_State* state) KeyCode_MouseRightButton, true, false ); + win32_set_mouse_pos_evt(&evt); lumenarium_event(evt, state); win32_mouse_capture(win); }break; @@ -120,6 +134,7 @@ win32_window_handle_event(MSG msg, Win32_Window* win, App_State* state) KeyCode_MouseLeftButton, false, true ); + win32_set_mouse_pos_evt(&evt); lumenarium_event(evt, state); win32_mouse_release(win); }break; @@ -130,6 +145,7 @@ win32_window_handle_event(MSG msg, Win32_Window* win, App_State* state) KeyCode_MouseMiddleButton, false, true ); + win32_set_mouse_pos_evt(&evt); lumenarium_event(evt, state); win32_mouse_release(win); }break; @@ -140,6 +156,7 @@ win32_window_handle_event(MSG msg, Win32_Window* win, App_State* state) KeyCode_MouseRightButton, false, true ); + win32_set_mouse_pos_evt(&evt); lumenarium_event(evt, state); win32_mouse_release(win); }break; From 09db50e3d4f86ee7db99eb95459a79316823aa87 Mon Sep 17 00:00:00 2001 From: PS Date: Tue, 12 Apr 2022 07:53:32 +0200 Subject: [PATCH 122/151] scratch memory, sculpture rendering, wasm updates --- .../wasm/debug/lumenarium_wasm_imports.js | 76 +- run_tree/win32/intel/debug/debug.rdbg | Bin 1488 -> 2147 bytes .../intel/debug/lumenarium_first_win32.obj | Bin 473884 -> 595328 bytes src/app/editor/foldhaus_panel.h | 478 ++--- src_v2/editor/lumenarium_editor.cpp | 223 +- src_v2/editor/lumenarium_editor.h | 10 + ...lumenarium_editor_sculpture_visualizer.cpp | 100 + src_v2/editor/lumenarium_editor_ui.cpp | 225 +- src_v2/editor/lumenarium_editor_ui.h | 18 +- src_v2/engine/lumenarium_engine.cpp | 3 +- src_v2/engine/lumenarium_engine_assembly.cpp | 19 + src_v2/libs/stb_sprintf.h | 1906 +++++++++++++++++ src_v2/lumenarium_bsp.h | 492 +++++ src_v2/lumenarium_first.cpp | 17 +- src_v2/lumenarium_first.h | 10 +- src_v2/lumenarium_geometry.h | 268 +++ src_v2/lumenarium_memory.cpp | 32 + src_v2/lumenarium_random.h | 47 + src_v2/lumenarium_string.cpp | 4 +- src_v2/lumenarium_tests.cpp | 67 +- src_v2/lumenarium_types.h | 69 + src_v2/platform/lumenarium_platform.h | 12 +- .../lumenarium_platform_common_includes.h | 3 + .../platform/wasm/lumenarium_first_wasm.cpp | 1 + src_v2/platform/wasm/lumenarium_wasm_file.cpp | 11 + .../platform/wasm/lumenarium_wasm_imports.js | 76 +- .../platform/wasm/lumenarium_wasm_webgl.cpp | 52 +- .../platform/win32/lumenarium_first_win32.cpp | 4 +- .../win32/lumenarium_win32_graphics.cpp | 19 +- src_v2/user_space/user_space_incenter.cpp | 58 +- 30 files changed, 3874 insertions(+), 426 deletions(-) create mode 100644 src_v2/editor/lumenarium_editor_sculpture_visualizer.cpp create mode 100644 src_v2/libs/stb_sprintf.h create mode 100644 src_v2/lumenarium_bsp.h create mode 100644 src_v2/lumenarium_geometry.h create mode 100644 src_v2/lumenarium_random.h diff --git a/run_tree/wasm/debug/lumenarium_wasm_imports.js b/run_tree/wasm/debug/lumenarium_wasm_imports.js index 29d372c..ff42920 100644 --- a/run_tree/wasm/debug/lumenarium_wasm_imports.js +++ b/run_tree/wasm/debug/lumenarium_wasm_imports.js @@ -92,20 +92,69 @@ var lumenarium_wasm_imports = { }, wasm_mem_grow: (new_size) => { - let pages = new_size / WASM_PAGE_SIZE; + let new_size_ = new_size >>> 0; + let pages = new_size_ / WASM_PAGE_SIZE; let pages_rem = fract(pages); if (pages_rem > 0) pages = Math.floor(pages) + 1; let size_before = lumenarium_wasm_instance.exports.memory.buffer.byteLength; let old_page_count = lumenarium_wasm_instance.exports.memory.grow(pages); console.log("mem_grow\n", - "req size: ", new_size, "\n", + "req size: ", new_size_, "\n", "old size: ", (old_page_count * WASM_PAGE_SIZE), "\n", "old size: ", size_before, "\n", "grew by: ", (pages * WASM_PAGE_SIZE), "\n", "new size: ", lumenarium_wasm_instance.exports.memory.buffer.byteLength, ""); }, + malloc: (size) => { + + }, + + free: (base) => { + + }, + + sin: Math.sin, + sinf: Math.sin, + cos: Math.cos, + cosf: Math.cos, + tan: Math.tan, + tanf: Math.tan, + asin: Math.asin, + asinf: Math.asin, + acos: Math.acos, + acosf: Math.acos, + atan: Math.atan, + atanf: Math.atan, + pow: Math.pow, + powf: Math.pow, + fmodf: (f,d) => { return f % d; }, + strlen: (ptr) => { + let len = 0; + let len_checked = 0; + let len_to_check = 256; + let found_end = false; + while (true) + { + let string = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, len_checked); + for (let i = len_checked; i < len_to_check; i++) + { + if (string[i] == 0) + { + len = i; + break; + } + } + len_checked *= 2; + } + return len_checked; + }, + + wasm_platform_file_async_work_on_job: (path, path_len, data, data_size, read, write) => { + + }, + wasm_performance_now: () => { return performance.now(); }, @@ -260,6 +309,14 @@ function glBufferData(target, size, ptr, usage) return r; } +function glBufferSubData(target, offset, size, ptr) +{ + let data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, size); + const r = gl.bufferSubData(target, offset, data, 0, size); + glErrorReport(arguments); + return r; +} + function glCreateShader(kind) { let shader = gl.createShader(kind); @@ -395,6 +452,17 @@ function glTexSubImage2D(target, level, offsetx, offsety, width, height, format, return r; } +function glGetUniformLocation(program, name, name_len) +{ + // TODO(PS): complete + return 0; +} + +function glUniformMatrix4fv() +{ + // TODO(PS): +} + function webgl_add_imports (canvas_selector, imports) { const canvas = document.querySelector(canvas_selector); if (!canvas) return console.error("no canvas"); @@ -414,6 +482,7 @@ function webgl_add_imports (canvas_selector, imports) { imports.glCreateBuffer = glCreateBuffer; imports.glBindBuffer = glBindBuffer; imports.glBufferData = glBufferData; + imports.glBufferSubData = glBufferSubData; imports.glCreateShader = glCreateShader; imports.glShaderSource = glShaderSource; imports.glCompileShader = glCompileShader; @@ -431,5 +500,8 @@ function webgl_add_imports (canvas_selector, imports) { imports.glTexImage2D = glTexImage2D; imports.glTexSubImage2D = glTexSubImage2D; imports.glBindTexture = glBindTexture; + imports.glGetUniformLocation = glGetUniformLocation; + imports.glUniformMatrix4fv = glUniformMatrix4fv; + return imports; } \ No newline at end of file diff --git a/run_tree/win32/intel/debug/debug.rdbg b/run_tree/win32/intel/debug/debug.rdbg index 24dbe61ef213c4152686ca14906596a6c51d642e..3304be4bd665ee11a87f027ea2e3ec724a195e10 100644 GIT binary patch delta 451 zcmXv~%SyvQ6m6PDlxkH7Do7yon4YI|{fAYRqo zkxoFcjJ6;uPmv_VT||Q44SIgYG6*B`W$?f*9V-$S4>Ws&~<QL@v9Vw<7$)kNNv&#G#kHmt1`U6xTtwR&@ws+`*JaR6OPG XbI+|WU#w`4+At^z;!bOco;Ldn*@KLk delta 263 zcmaDXaDjV57%MLW1B1ilkId?m{h7HZ-(?k?oXqOZ=mr*XWL0AH1hW<}N>1iw<(cfl zqB*&TReZ7oi^t>-%pr`vKv_rbw9KO7lKAq>JY%ED@yrgBlbMZK!D5roGMZ18W#(e^ z1FCbM{D29@>SN;KWdzy`0wCG~Yzxo@$t9`9CB=Hl1qFOSVGw2q;@tew;?(#886dN` zB(Wq_*Df=!ptK|&%+X6LO3Y1-&&V%f1d2{TYQ VJvD#wS+-slJ=0|mCdaYQ1^@<0PEP;; diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj index 7c9356ef88532028ab2955e1e738319a8f81a6b5..6284fe59da7efa8e6fc798566618ac00b32ca5e6 100644 GIT binary patch literal 595328 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 diff --git a/src/app/editor/foldhaus_panel.h b/src/app/editor/foldhaus_panel.h index aec8cb9..b8a59a8 100644 --- a/src/app/editor/foldhaus_panel.h +++ b/src/app/editor/foldhaus_panel.h @@ -11,11 +11,11 @@ enum panel_split_direction { - PanelSplit_NoSplit, - PanelSplit_Horizontal, - PanelSplit_Vertical, - - PanelSplit_Count, + PanelSplit_NoSplit, + PanelSplit_Horizontal, + PanelSplit_Vertical, + + PanelSplit_Count, }; typedef struct panel panel; @@ -25,32 +25,32 @@ typedef PANEL_MODAL_OVERRIDE_CALLBACK(panel_modal_override_callback); struct panel { - s32 TypeIndex; - gs_data StateMemory; - - panel* ModalOverride; - panel* IsModalOverrideFor; - panel_modal_override_callback* ModalOverrideCB; - - rect2 Bounds; - panel_split_direction SplitDirection; - r32 SplitPercent; - - panel* Parent; - - union{ - panel* Left; - panel* Top; - }; - union{ - panel* Right; - panel* Bottom; - }; + s32 TypeIndex; + gs_data StateMemory; + + panel* ModalOverride; + panel* IsModalOverrideFor; + panel_modal_override_callback* ModalOverrideCB; + + rect2 Bounds; + panel_split_direction SplitDirection; + r32 SplitPercent; + + panel* Parent; + + union{ + panel* Left; + panel* Top; + }; + union{ + panel* Right; + panel* Bottom; + }; }; struct free_panel { - free_panel* Next; + free_panel* Next; }; #define PANEL_INIT_PROC(name) void name(panel* Panel, app_state* State, context Context) @@ -65,25 +65,25 @@ typedef PANEL_RENDER_PROC(panel_render_proc); // NOTE(Peter): This is used by the meta system to generate panel type info struct panel_definition { - char* PanelName; - s32 PanelNameLength; - panel_init_proc* Init; - panel_cleanup_proc* Cleanup; - panel_render_proc* Render; - input_command* InputCommands; - s32 InputCommandsCount; + char* PanelName; + s32 PanelNameLength; + panel_init_proc* Init; + panel_cleanup_proc* Cleanup; + panel_render_proc* Render; + input_command* InputCommands; + s32 InputCommandsCount; }; #define PANELS_MAX 16 struct panel_system { - panel_definition* PanelDefs; - u32 PanelDefsCount; - - panel* Panels; - u32 PanelsUsed; - - free_panel* FreeList; + panel_definition* PanelDefs; + u32 PanelDefsCount; + + panel* Panels; + u32 PanelsUsed; + + free_panel* FreeList; }; ///////////////////////////////// @@ -95,164 +95,164 @@ struct panel_system internal void PanelSystem_Init(panel_system* PanelSystem, panel_definition* PanelDefs, u32 PanelDefsCount, gs_memory_arena* Storage) { - PanelSystem->FreeList = 0; - PanelSystem->PanelDefs = PanelDefs; - PanelSystem->PanelDefsCount = PanelDefsCount; - - PanelSystem->Panels = PushArray(Storage, panel, PANELS_MAX); + PanelSystem->FreeList = 0; + PanelSystem->PanelDefs = PanelDefs; + PanelSystem->PanelDefsCount = PanelDefsCount; + + PanelSystem->Panels = PushArray(Storage, panel, PANELS_MAX); } internal panel* PanelSystem_TakePanel(panel_system* PanelSystem) { - panel* FreeEntry = 0; - if (PanelSystem->FreeList != 0) - { - free_panel* FreePanel = PanelSystem->FreeList; - PanelSystem->FreeList = FreePanel->Next; - FreeEntry = (panel*)FreePanel; - } - else - { - Assert(PanelSystem->PanelsUsed < PANELS_MAX); - FreeEntry = PanelSystem->Panels + PanelSystem->PanelsUsed++; - } - return FreeEntry; + panel* FreeEntry = 0; + if (PanelSystem->FreeList != 0) + { + free_panel* FreePanel = PanelSystem->FreeList; + PanelSystem->FreeList = FreePanel->Next; + FreeEntry = (panel*)FreePanel; + } + else + { + Assert(PanelSystem->PanelsUsed < PANELS_MAX); + FreeEntry = PanelSystem->Panels + PanelSystem->PanelsUsed++; + } + return FreeEntry; } internal void PanelSystem_FreePanel(panel* Panel, panel_system* PanelSystem) { - Assert(Panel >= PanelSystem->Panels && Panel <= PanelSystem->Panels + PANELS_MAX); - - free_panel* FreeEntry = (free_panel*)Panel; - FreeEntry->Next = PanelSystem->FreeList; - PanelSystem->FreeList = FreeEntry; + Assert(Panel >= PanelSystem->Panels && Panel <= PanelSystem->Panels + PANELS_MAX); + + free_panel* FreeEntry = (free_panel*)Panel; + FreeEntry->Next = PanelSystem->FreeList; + PanelSystem->FreeList = FreeEntry; } internal void PanelSystem_FreePanelRecursive(panel* Panel, panel_system* PanelSystem) { - if (Panel->SplitDirection != PanelSplit_NoSplit) - { - PanelSystem_FreePanelRecursive(Panel->Left, PanelSystem); - PanelSystem_FreePanelRecursive(Panel->Right, PanelSystem); - } - PanelSystem_FreePanel(Panel, PanelSystem); + if (Panel->SplitDirection != PanelSplit_NoSplit) + { + PanelSystem_FreePanelRecursive(Panel->Left, PanelSystem); + PanelSystem_FreePanelRecursive(Panel->Right, PanelSystem); + } + PanelSystem_FreePanel(Panel, PanelSystem); } internal panel* Panel_GetModalOverride(panel* Panel) { - panel* Result = Panel; - if (Panel->ModalOverride != 0) - { - Result = Panel_GetModalOverride(Panel->ModalOverride); - } - return Result; + panel* Result = Panel; + if (Panel->ModalOverride != 0) + { + Result = Panel_GetModalOverride(Panel->ModalOverride); + } + return Result; } internal void Panel_PushModalOverride(panel* Root, panel* Override, panel_modal_override_callback* Callback) { - Root->ModalOverride = Override; - Root->ModalOverrideCB = Callback; - Override->IsModalOverrideFor = Root; - Override->Bounds = Root->Bounds; + Root->ModalOverride = Override; + Root->ModalOverrideCB = Callback; + Override->IsModalOverrideFor = Root; + Override->Bounds = Root->Bounds; } internal void Panel_PopModalOverride(panel* Parent, panel_system* System) { - // TODO(pjs): Free the overrided panel - PanelSystem_FreePanel(Parent->ModalOverride, System); - Parent->ModalOverride = 0; + // TODO(pjs): Free the overrided panel + PanelSystem_FreePanel(Parent->ModalOverride, System); + Parent->ModalOverride = 0; } internal void Panel_SetCurrentType(panel* Panel, panel_system* System, s32 NewPanelType, gs_data TypeStateMemory, app_state* State, context Context) { - s32 OldTypeIndex = Panel->TypeIndex; - - Panel->TypeIndex = NewPanelType; - Panel->StateMemory = TypeStateMemory; - - if(OldTypeIndex >= 0) - { - System->PanelDefs[OldTypeIndex].Cleanup(Panel, State); - } + s32 OldTypeIndex = Panel->TypeIndex; + + Panel->TypeIndex = NewPanelType; + Panel->StateMemory = TypeStateMemory; + + if(OldTypeIndex >= 0) + { + System->PanelDefs[OldTypeIndex].Cleanup(Panel, State); + } } internal void Panel_SetType(panel* Panel, panel_system* System, s32 NewPanelTypeIndex, app_state* State, context Context) { - gs_data EmptyStateData = {0}; - Panel_SetCurrentType(Panel, System, NewPanelTypeIndex, EmptyStateData, State, Context); - System->PanelDefs[NewPanelTypeIndex].Init(Panel, State, Context); + gs_data EmptyStateData = {0}; + Panel_SetCurrentType(Panel, System, NewPanelTypeIndex, EmptyStateData, State, Context); + System->PanelDefs[NewPanelTypeIndex].Init(Panel, State, Context); } #define Panel_GetStateStruct(p, type) (type*)Panel_GetStateMemory((p), sizeof(type)).Memory internal gs_data Panel_GetStateMemory(panel* Panel, u64 Size) { - Assert(Panel->StateMemory.Size == Size); - gs_data Result = Panel->StateMemory; - return Result; + Assert(Panel->StateMemory.Size == Size); + gs_data Result = Panel->StateMemory; + return Result; } internal panel* PanelSystem_PushPanel(panel_system* PanelSystem, s32 PanelTypeIndex, app_state* State, context Context) { - panel* Panel = PanelSystem_TakePanel(PanelSystem); - Panel_SetType(Panel, PanelSystem, PanelTypeIndex, State, Context); - return Panel; + panel* Panel = PanelSystem_TakePanel(PanelSystem); + Panel_SetType(Panel, PanelSystem, PanelTypeIndex, State, Context); + return Panel; } internal void SplitPanel(panel* Parent, r32 Percent, panel_split_direction SplitDirection, panel_system* PanelSystem, app_state* State, context Context) { - if (Percent >= 0.0f && Percent <= 1.0f) - { - Parent->SplitDirection = SplitDirection; - Parent->SplitPercent = Percent; - - s32 ParentTypeIndex = Parent->TypeIndex; - gs_data ParentStateMemory = Parent->StateMemory; - - Parent->Left = PanelSystem_TakePanel(PanelSystem); - Panel_SetCurrentType(Parent->Left, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context); - Parent->Left->Parent = Parent; - - Parent->Right = PanelSystem_TakePanel(PanelSystem); - Panel_SetCurrentType(Parent->Right, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context); - Parent->Right->Parent = Parent; - } + if (Percent >= 0.0f && Percent <= 1.0f) + { + Parent->SplitDirection = SplitDirection; + Parent->SplitPercent = Percent; + + s32 ParentTypeIndex = Parent->TypeIndex; + gs_data ParentStateMemory = Parent->StateMemory; + + Parent->Left = PanelSystem_TakePanel(PanelSystem); + Panel_SetCurrentType(Parent->Left, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context); + Parent->Left->Parent = Parent; + + Parent->Right = PanelSystem_TakePanel(PanelSystem); + Panel_SetCurrentType(Parent->Right, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context); + Parent->Right->Parent = Parent; + } } internal void SplitPanelVertically(panel* Parent, r32 Percent, panel_system* PanelSystem, app_state* State, context Context) { - SplitPanel(Parent, Percent, PanelSplit_Vertical, PanelSystem, State, Context); + SplitPanel(Parent, Percent, PanelSplit_Vertical, PanelSystem, State, Context); } internal void SplitPanelHorizontally(panel* Parent, r32 Percent, panel_system* PanelSystem, app_state* State, context Context) { - SplitPanel(Parent, Percent, PanelSplit_Horizontal, PanelSystem, State, Context); + SplitPanel(Parent, Percent, PanelSplit_Horizontal, PanelSystem, State, Context); } internal void ConsolidatePanelsKeepOne(panel* Parent, panel* PanelToKeep, panel_system* PanelSystem) { - panel* LeftChild = Parent->Left; - panel* RightChild = Parent->Right; - - panel* PanelToDestroy = PanelToKeep == LeftChild ? RightChild : LeftChild; - - *Parent = *PanelToKeep; - - PanelSystem_FreePanel(PanelToKeep, PanelSystem); - PanelSystem_FreePanelRecursive(PanelToDestroy, PanelSystem); + panel* LeftChild = Parent->Left; + panel* RightChild = Parent->Right; + + panel* PanelToDestroy = PanelToKeep == LeftChild ? RightChild : LeftChild; + + *Parent = *PanelToKeep; + + PanelSystem_FreePanel(PanelToKeep, PanelSystem); + PanelSystem_FreePanelRecursive(PanelToDestroy, PanelSystem); } ///////////////////////////////// @@ -264,178 +264,178 @@ ConsolidatePanelsKeepOne(panel* Parent, panel* PanelToKeep, panel_system* PanelS internal rect2 GetTopPanelBounds(panel* Panel) { - rect2 Result = {}; - Result.Min = v2{ - Panel->Bounds.Min.x, - LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y) - }; - Result.Max = Panel->Bounds.Max; - return Result; + rect2 Result = {}; + Result.Min = v2{ + Panel->Bounds.Min.x, + LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y) + }; + Result.Max = Panel->Bounds.Max; + return Result; } internal rect2 GetBottomPanelBounds(panel* Panel) { - rect2 Result = {}; - Result.Min = Panel->Bounds.Min; - Result.Max = v2{ - Panel->Bounds.Max.x, - LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y) - }; - return Result; + rect2 Result = {}; + Result.Min = Panel->Bounds.Min; + Result.Max = v2{ + Panel->Bounds.Max.x, + LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y) + }; + return Result; } internal rect2 GetRightPanelBounds(panel* Panel) { - rect2 Result = {}; - Result.Min = v2{ - LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x), - Panel->Bounds.Min.y - }; - Result.Max = Panel->Bounds.Max; - return Result; + rect2 Result = {}; + Result.Min = v2{ + LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x), + Panel->Bounds.Min.y + }; + Result.Max = Panel->Bounds.Max; + return Result; } internal rect2 GetLeftPanelBounds(panel* Panel) { - rect2 Result = {}; - Result.Min = Panel->Bounds.Min; - Result.Max = v2{ - LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x), - Panel->Bounds.Max.y - }; - return Result; + rect2 Result = {}; + Result.Min = Panel->Bounds.Min; + Result.Max = v2{ + LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x), + Panel->Bounds.Max.y + }; + return Result; } internal rect2 GetTopPanelBounds(panel* Panel, rect2 PanelBounds) { - rect2 Result = {}; - Result.Min = v2{ - PanelBounds.Min.x, - LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y) - }; - Result.Max = PanelBounds.Max; - return Result; + rect2 Result = {}; + Result.Min = v2{ + PanelBounds.Min.x, + LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y) + }; + Result.Max = PanelBounds.Max; + return Result; } internal rect2 GetBottomPanelBounds(panel* Panel, rect2 PanelBounds) { - rect2 Result = {}; - Result.Min = PanelBounds.Min; - Result.Max = v2{ - PanelBounds.Max.x, - LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y) - }; - return Result; + rect2 Result = {}; + Result.Min = PanelBounds.Min; + Result.Max = v2{ + PanelBounds.Max.x, + LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y) + }; + return Result; } internal rect2 GetRightPanelBounds(panel* Panel, rect2 PanelBounds) { - rect2 Result = {}; - Result.Min = v2{ - LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x), - PanelBounds.Min.y - }; - Result.Max = PanelBounds.Max; - return Result; + rect2 Result = {}; + Result.Min = v2{ + LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x), + PanelBounds.Min.y + }; + Result.Max = PanelBounds.Max; + return Result; } internal rect2 GetLeftPanelBounds(panel* Panel, rect2 PanelBounds) { - rect2 Result = {}; - Result.Min = PanelBounds.Min; - Result.Max = v2{ - LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x), - PanelBounds.Max.y - }; - return Result; + rect2 Result = {}; + Result.Min = PanelBounds.Min; + Result.Max = v2{ + LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x), + PanelBounds.Max.y + }; + return Result; } internal void Panel_UpdateLayout(panel* Panel, rect2 Bounds) { - Panel->Bounds = Bounds; - - if (Panel->SplitDirection != PanelSplit_NoSplit) + Panel->Bounds = Bounds; + + if (Panel->SplitDirection != PanelSplit_NoSplit) + { + rect2 LeftOrTopBounds = {}; + rect2 RightOrBottomBounds = {}; + switch (Panel->SplitDirection) { - rect2 LeftOrTopBounds = {}; - rect2 RightOrBottomBounds = {}; - switch (Panel->SplitDirection) - { - case PanelSplit_Horizontal: - { - LeftOrTopBounds = GetTopPanelBounds(Panel); - RightOrBottomBounds = GetBottomPanelBounds(Panel); - } break; - - case PanelSplit_Vertical: - { - LeftOrTopBounds = GetLeftPanelBounds(Panel); - RightOrBottomBounds = GetRightPanelBounds(Panel); - } break; - - InvalidDefaultCase; - } - - Panel_UpdateLayout(Panel->Left, LeftOrTopBounds); - Panel_UpdateLayout(Panel->Right, RightOrBottomBounds); + case PanelSplit_Horizontal: + { + LeftOrTopBounds = GetTopPanelBounds(Panel); + RightOrBottomBounds = GetBottomPanelBounds(Panel); + } break; + + case PanelSplit_Vertical: + { + LeftOrTopBounds = GetLeftPanelBounds(Panel); + RightOrBottomBounds = GetRightPanelBounds(Panel); + } break; + + InvalidDefaultCase; } + + Panel_UpdateLayout(Panel->Left, LeftOrTopBounds); + Panel_UpdateLayout(Panel->Right, RightOrBottomBounds); + } } internal void PanelSystem_UpdateLayout(panel_system* System, rect2 WindowBounds) { - panel* Root = System->Panels; - Panel_UpdateLayout(Root, WindowBounds); + panel* Root = System->Panels; + Panel_UpdateLayout(Root, WindowBounds); } internal panel* GetPanelContainingPoint(panel* Panel, v2 Point) { - panel* Result = 0; - - if (PointIsInRect(Panel->Bounds, Point)) + panel* Result = 0; + + if (PointIsInRect(Panel->Bounds, Point)) + { + switch (Panel->SplitDirection) { - switch (Panel->SplitDirection) + case PanelSplit_NoSplit: + { + Result = Panel; + }break; + + case PanelSplit_Vertical: + case PanelSplit_Horizontal: + {asdfasdfasdfasdfasdf + if (PointIsInRect(Panel->Left->Bounds, Point)) { - case PanelSplit_NoSplit: - { - Result = Panel; - }break; - - case PanelSplit_Vertical: - case PanelSplit_Horizontal: - { - if (PointIsInRect(Panel->Left->Bounds, Point)) - { - Result = GetPanelContainingPoint(Panel->Left, Point); - } - else if (PointIsInRect(Panel->Right->Bounds, Point)) - { - Result = GetPanelContainingPoint(Panel->Right, Point); - } - }break; - - InvalidDefaultCase; + Result = GetPanelContainingPoint(Panel->Left, Point); } + else if (PointIsInRect(Panel->Right->Bounds, Point)) + { + Result = GetPanelContainingPoint(Panel->Right, Point); + } + }break; + + InvalidDefaultCase; } - - return Result; + } + + return Result; } internal panel* PanelSystem_GetPanelContainingPoint(panel_system* System, v2 Point) { - panel* Result = 0; - panel* Root = System->Panels; - Result = GetPanelContainingPoint(Root, Point); - return Result; + panel* Result = 0; + panel* Root = System->Panels; + Result = GetPanelContainingPoint(Root, Point); + return Result; } #define FOLDHAUS_PANEL_H diff --git a/src_v2/editor/lumenarium_editor.cpp b/src_v2/editor/lumenarium_editor.cpp index 4157692..1aba45d 100644 --- a/src_v2/editor/lumenarium_editor.cpp +++ b/src_v2/editor/lumenarium_editor.cpp @@ -131,12 +131,72 @@ ed_load_font_cb(Platform_File_Async_Job_Args result, u8* user_data) platform_texture_update(ui->atlas_texture, ui->atlas.pixels, 1024, 1024, 1024); } +internal void +ed_draw_panel(u8* user_data, BSP_Node_Id id, BSP_Node node, BSP_Area area) +{ + App_State* state = (App_State*)user_data; + UI* ui = &state->editor->ui; + scratch_get(scratch); + + UI_Layout title_layout = {}; + ui_layout_set_row_info(ui, &title_layout); + title_layout.bounds_min = v2{ area.min.x, area.max.y - title_layout.row_height }; + title_layout.bounds_max = area.max; + title_layout.at = title_layout.bounds_min; + + UI_Layout panel_layout = {}; + ui_layout_set_row_info(ui, &panel_layout); + panel_layout.bounds_min = area.min; + panel_layout.bounds_max = v2{ area.max.x, title_layout.bounds_max.y }; + panel_layout.at = panel_layout.bounds_min; + + ui_layout_push(ui, &panel_layout); + + String title = {}; + switch (node.user_data) + { + case 0: + { + title = lit_str("None"); + } break; + + case 1: + { + ed_sculpture_visualizer(state); + title = lit_str("Sculpture"); + } break; + + invalid_default_case; + } + ui_layout_pop(ui); + + ui_layout_push(ui, &title_layout); + UI_Widget_Desc bg = {}; + bg.style.flags = UIWidgetStyle_Bg; + bg.style.color_bg = v4{.4f,.4f,.4f,1}; + bg.style.sprite = WHITE_SPRITE_ID; + bg.string = string_f(scratch.a, "%.*s_%u_title_bg", str_varg(title), id.value); + bg.p_min = title_layout.bounds_min; + bg.p_max = title_layout.bounds_max; + UI_Widget_Result r = ui_widget_push(ui, bg); + ui_layout_row_begin(&title_layout, 4); + { + ui_text(ui, title, BLACK_V4); + } + ui_layout_row_end(&title_layout); + ui_widget_pop(ui, r.id); + ui_layout_pop(ui); +} + internal void ed_init(App_State* state) { Editor* editor = allocator_alloc_struct(permanent, Editor); state->editor = editor; - state->editor->ui = ui_create(4096, 4096, state->input_state, permanent); + editor->ui = ui_create(4096, 4096, state->input_state, permanent); + editor->ui.draw_panel_cb = ed_draw_panel; + editor->ui.draw_panel_cb_data = (u8*)state; + //bsp_split(&editor->ui.panels, editor->ui.panels.root, 700, BSPSplit_YAxis, 0, 1); // make the default quad for us to draw with // TODO(PS): this might be unnecessary with the per-frame buffer we use now @@ -144,6 +204,119 @@ ed_init(App_State* state) platform_file_async_read(lit_str("data/font.ttf"), ed_load_font_cb); + ed_sculpture_visualizer_init(state); +} + +internal void +ed_sculpture_updated(App_State* state, r32 scale, r32 led_size) +{ + Editor* ed = state->editor; + if (!ed) return; + + scratch_get(scratch); + + // NOTE(PS): we need to know the total number of leds in order to give them + // texture coordinates + u32 leds_count = 0; + for (u32 a = 0; a < state->assemblies.len; a++) + { + Assembly_Pixel_Buffer pixels = state->assemblies.pixel_buffers[a]; + leds_count += pixels.len; + } + + // round up to a texture whose sides are powers of two + u32 pixels_dim = (u32)floorf(sqrtf((r32)leds_count)); + pixels_dim = round_up_to_pow2(pixels_dim); + u32 pixels_count = pixels_dim * pixels_dim; + r32 texel_dim = 1 / (r32)pixels_dim; + + // NOTE(PS): Rebuild the sculpture geometry to point to the new + // sculpture. + Geo_Vertex_Buffer_Storage storage = ( + GeoVertexBufferStorage_Position | + GeoVertexBufferStorage_TexCoord + ); + u32 verts_cap = leds_count * 4; + u32 indices_cap = leds_count * 6; + Geo_Quad_Buffer_Builder geo = geo_quad_buffer_builder_create(scratch.a, verts_cap, storage, indices_cap); + r32 r = led_size; + + u32 pixels_created = 0; + for (u32 a = 0; a < state->assemblies.len; a++) + { + Assembly_Pixel_Buffer pixels = state->assemblies.pixel_buffers[a]; + for (u32 p = 0; p < pixels.len; p++) + { + v3 c = pixels.positions[p].xyz; + c *= scale; + + u32 pixel_count = pixels_created++; + u32 pixel_x = pixel_count % pixels_dim; + u32 pixel_y = pixel_count / pixels_dim; + r32 texel_x_min = (r32)pixel_x / (r32)pixels_dim; + r32 texel_y_min = (r32)pixel_y / (r32)pixels_dim; + r32 texel_x_max = texel_x_min + texel_dim; + r32 texel_y_max = texel_y_min + texel_dim; + + v2 t0 = v2{texel_x_min, texel_y_min}; + v2 t1 = v2{texel_x_max, texel_y_min}; + v2 t2 = v2{texel_x_max, texel_y_max}; + v2 t3 = v2{texel_x_min, texel_y_max}; + + v3 p0 = c + v3{ -r, -r, 0 }; + v3 p1 = c + v3{ r, -r, 0 }; + v3 p2 = c + v3{ r, r, 0 }; + v3 p3 = c + v3{ -r, r, 0 }; + geo_quad_buffer_builder_push(&geo, p0, p1, p2, p3, t0, t1, t2, t3); + } + } + + if (ed->sculpture_geo.indices_len != 0) + { + invalid_code_path; + // TODO(PS): destroy the old geometry buffer or update it + } + ed->sculpture_geo = platform_geometry_buffer_create( + geo.buffer_vertex.values, + geo.buffer_vertex.len, + geo.buffer_index.values, + geo.buffer_index.len + ); + + platform_vertex_attrib_pointer( + ed->sculpture_geo, ed->sculpture_shd, 3, ed->sculpture_shd.attrs[0], 5, 0 + ); + platform_vertex_attrib_pointer( + ed->sculpture_geo, ed->sculpture_shd, 2, ed->sculpture_shd.attrs[1], 5, 3 + ); + + // TODO(PS): make this have enough pixels for the sculpture + // TODO(PS): map leds to pixels + + if (ed->sculpture_tex.w != 0) + { + invalid_code_path; + // TODO(PS): destroy the old texture + } + + u32* pixels = allocator_alloc_array(scratch.a, u32, pixels_count); + for (u32 y = 0; y < pixels_dim; y++) + { + for (u32 x = 0; x < pixels_dim; x++) + { + r32 rp = (r32)y / (r32)pixels_dim; + r32 bp = (r32)x / (r32)pixels_dim; + u8 rb = (u8)(255 * rp); + u8 bb = (u8)(255 * bp); + u32 c = ( + 0xFF0000FF | + (rb << 8) | + (bb << 16) + ); + pixels[(y * pixels_dim) + x] = c; + } + } + ed->sculpture_tex = platform_texture_create((u8*)pixels, pixels_dim, pixels_dim, pixels_dim); } internal void @@ -152,59 +325,11 @@ ed_frame_prepare(App_State* state) ui_frame_prepare(&state->editor->ui, state->editor->window_dim); } -global r32 p = 0.3f; - internal void ed_frame(App_State* state) { UI* ui = &state->editor->ui; - UI_Layout layout = {}; - layout.bounds_min = v2{ 500, 200 }; - layout.bounds_max = v2{ 700, 500 }; - ui_layout_set_row_info(ui, &layout); - layout.at = layout.bounds_min; - ui_layout_push(ui, &layout); - - ui_text_f(ui, "Hi there! %d", 1000); - show = ui_toggle(ui, lit_str("my toggle"), show); - if (show) - { - ui_layout_row_begin(ui, 2); - { - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - } - ui_layout_row_end(ui); - } - ui_button(ui, lit_str("Hi there my good sir")); - - ui_scroll_view_begin(ui, lit_str("scroll area"), v2{800, 200}, v2{1000,500}, 8); - { - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("I'm lastf;")); - } - ui_scroll_view_end(ui); - - ui_layout_pop(ui); - edr_render_begin(state); ui_draw(&state->editor->ui); edr_render(state); diff --git a/src_v2/editor/lumenarium_editor.h b/src_v2/editor/lumenarium_editor.h index 9cd17c2..b623912 100644 --- a/src_v2/editor/lumenarium_editor.h +++ b/src_v2/editor/lumenarium_editor.h @@ -8,6 +8,16 @@ struct Editor v2 window_dim; Editor_Renderer renderer; UI ui; + + v3 camera_pos; + + Platform_Geometry_Buffer sculpture_geo; + Platform_Shader sculpture_shd; + Platform_Texture sculpture_tex; }; +// NOTE(PS): call this any time sculpture data is updated if +// you want to see the sculpture in the visualizer +internal void ed_sculpture_updated(App_State* state); + #endif //LUMENARIUM_EDITOR_H diff --git a/src_v2/editor/lumenarium_editor_sculpture_visualizer.cpp b/src_v2/editor/lumenarium_editor_sculpture_visualizer.cpp new file mode 100644 index 0000000..8a0de1d --- /dev/null +++ b/src_v2/editor/lumenarium_editor_sculpture_visualizer.cpp @@ -0,0 +1,100 @@ + +static String sculpture_shd_vert_win32 = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec3 a_pos;\n" + "layout (location = 1) in vec2 a_uv;\n" + "out vec2 uv;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + "}" + ); + +static String sculpture_shd_vert_wasm = lit_str( + "precision highp float;\n" + "attribute vec3 a_pos;\n" + "attribute vec2 a_uv;\n" + "varying vec2 uv;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + "}" + ); + +static String sculpture_shd_frag_win32 = lit_str( + "#version 330 core\n" + "in vec2 uv;\n" + "out vec4 FragColor;\n" + "uniform sampler2D texture;\n" + "void main(void) {\n" + " FragColor = texture(texture, uv);\n" + "}" + ); + +static String sculpture_shd_frag_wasm = lit_str( + "precision highp float;\n" + "varying vec2 uv;\n" + "uniform sampler2D texture;\n" + "void main(void) {\n" + " //gl_FragColor = texture2D(texture, uv) * color;\n" + " gl_FragColor = vec4(uv.x,1,uv.y,1);\n" + "}" + ); + +internal void +ed_sculpture_visualizer_init(App_State* state) +{ + Editor* editor = state->editor; + + +#if defined(PLATFORM_win32) + String vert = sculpture_shd_vert_win32; + String frag = sculpture_shd_frag_win32; +#elif defined(PLATFORM_wasm) + String vert = sculpture_shd_vert_wasm; + String frag = sculpture_shd_frag_wasm; +#endif + + String attrs[] = { lit_str("a_pos"), lit_str("a_uv") }; + String uniforms[] = { lit_str("proj") }; + editor->sculpture_shd = platform_shader_create( + vert, frag, attrs, 2, uniforms, 1 + ); +} + +r32 cam_theta = 0; + +internal void +ed_sculpture_visualizer(App_State* state) +{ + Editor* ed = state->editor; + + // Set the viewport to the current layout's region so that the sculpture + // never overlaps any other ui elements + UI_Layout l = *ed->ui.layout; + v2 view_dim = l.bounds_max - l.bounds_min; + glViewport( + (s32)l.bounds_min.x, + (s32)l.bounds_min.y, + (s32)view_dim.x, + (s32)view_dim.y + ); + + // TODO(PS): TEMPORARY CAMERA CODE + cam_theta += 0.05f; + v3 camera_pos = v3{sinf(cam_theta) * 50, 0, cosf(cam_theta) * 50}; + r32 aspect = view_dim.x / view_dim.y; + m44 proj = HMM_Perspective(45.0, aspect, 0.01f, 500); + m44 view = HMM_LookAt(camera_pos, v3{0,0,0}, v3{0,1,0}); + + platform_shader_bind(ed->sculpture_shd); + platform_set_uniform(ed->sculpture_shd, 0, proj * view); + platform_texture_bind(ed->sculpture_tex); + platform_geometry_bind(ed->sculpture_geo); + platform_geometry_draw(ed->sculpture_geo, ed->sculpture_geo.indices_len); + + // reset the viewport for all other rendering + glViewport(0, 0, (s32)ed->window_dim.x, (s32)ed->window_dim.y); +} \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_ui.cpp b/src_v2/editor/lumenarium_editor_ui.cpp index 08985c1..44bccc5 100644 --- a/src_v2/editor/lumenarium_editor_ui.cpp +++ b/src_v2/editor/lumenarium_editor_ui.cpp @@ -2,19 +2,34 @@ static String ui_shader_vert_win32 = lit_str( "#version 330 core\n" - "layout (location = 0) in vec4 a_pos;\n" + "layout (location = 0) in vec3 a_pos;\n" "layout (location = 1) in vec2 a_uv;\n" "layout (location = 2) in vec4 a_color;\n" "out vec2 uv;\n" "out vec4 color;\n" "uniform mat4 proj;\n" "void main(void) {\n" - " gl_Position = proj * a_pos;\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" " uv = a_uv;\n" " color = a_color;\n" "}" ); +static String ui_shader_vert_wasm = lit_str( + "precision highp float;\n" + "attribute vec3 a_pos;\n" + "attribute vec2 a_uv;\n" + "attribute vec4 a_color;\n" + "varying vec2 uv;\n" + "varying vec4 color;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + " color = a_color;\n" + "}" + ); + static String ui_shader_frag_win32 = lit_str( "#version 330 core\n" "in vec2 uv;\n" @@ -26,10 +41,21 @@ static String ui_shader_frag_win32 = lit_str( "}" ); +static String ui_shader_frag_wasm = lit_str( + "precision highp float;\n" + "varying vec2 uv;\n" + "varying vec4 color;\n" + "uniform sampler2D texture;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(texture, uv) * color;\n" + "}" + ); + internal UI ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) { UI result = {}; + zero_struct(result); result.input = input; // Widgets @@ -39,36 +65,46 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) result.widgets.states = allocator_alloc_array(a, UI_Widget_State, result.widgets.states_cap); result.widgets.states_hash = allocator_alloc_array(a, u32, result.widgets.states_cap); - // Per Frame Vertex Buffer - result.verts_cap = verts_cap; - result.verts = allocator_alloc_array(a, UI_Vertex, verts_cap); - result.indices_cap = verts_cap * 2; - result.indices = allocator_alloc_array(a, u32, result.indices_cap); + result.panels = bsp_create(a, 32); + result.panels.root = bsp_push(&result.panels, {0}, {v2{},v2{1400, 800}}, 1); + + // Per Frame Vertex Buffer + Geo_Vertex_Buffer_Storage storage = ( + GeoVertexBufferStorage_Position | + GeoVertexBufferStorage_TexCoord | + GeoVertexBufferStorage_Color + ); + result.geo = geo_quad_buffer_builder_create(a, verts_cap, storage, verts_cap * 2); result.per_frame_buffer = platform_geometry_buffer_create( - (r32*)result.verts, - result.verts_cap, - result.indices, - result.indices_cap + result.geo.buffer_vertex.values, + result.geo.buffer_vertex.cap, + result.geo.buffer_index.values, + result.geo.buffer_index.cap ); +#if defined(PLATFORM_win32) + String vert = ui_shader_vert_win32; + String frag = ui_shader_frag_win32; +#elif defined(PLATFORM_wasm) + String vert = ui_shader_vert_wasm; + String frag = ui_shader_frag_wasm; +#endif + String attrs[] = { lit_str("a_pos"), lit_str("a_uv"), lit_str("a_color") }; String uniforms[] = { lit_str("proj") }; result.shader = platform_shader_create( - ui_shader_vert_win32, - ui_shader_frag_win32, - attrs, 3, - uniforms, 1 + vert, frag, attrs, 3, uniforms, 1 ); platform_vertex_attrib_pointer( - result.per_frame_buffer, result.shader, 4, result.shader.attrs[0], 10, 0 + result.per_frame_buffer, result.shader, 3, result.shader.attrs[0], 9, 0 ); platform_vertex_attrib_pointer( - result.per_frame_buffer, result.shader, 2, result.shader.attrs[1], 10, 4 + result.per_frame_buffer, result.shader, 2, result.shader.attrs[1], 9, 3 ); platform_vertex_attrib_pointer( - result.per_frame_buffer, result.shader, 4, result.shader.attrs[2], 10, 6 + result.per_frame_buffer, result.shader, 4, result.shader.attrs[2], 9, 5 ); // Texture Atlas @@ -87,41 +123,18 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) return result; } -internal u32 -ui_vert_push(UI* ui, v3 p, v2 t, v4 c) -{ - assert(ui->verts_len < ui->verts_cap); - u32 index = ui->verts_len++; - - ui->verts[index].pos = {p.x, p.y, p.z, 1}; - ui->verts[index].uv = t; - ui->verts[index].color = c; - - return index; -} - -internal void -ui_index_push(UI* ui, u32 i) -{ - assert(ui->indices_len < ui->indices_cap); - ui->indices[ui->indices_len++] = i; -} - internal void ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c) { - u32 bl = ui_vert_push(ui, pmin, tmin, c); - u32 br = ui_vert_push(ui, v3{pmax.x, pmin.y, pmin.z}, v2{tmax.x,tmin.y}, c); - u32 tr = ui_vert_push(ui, pmax, tmax, c); - u32 tl = ui_vert_push(ui, v3{pmin.x, pmax.y, pmin.z}, v2{tmin.x,tmax.y}, c); - - ui_index_push(ui, bl); - ui_index_push(ui, br); - ui_index_push(ui, tr); - - ui_index_push(ui, bl); - ui_index_push(ui, tr); - ui_index_push(ui, tl); + v3 p0 = pmin; + v3 p1 = v3{pmax.x, pmin.y, pmin.z}; + v3 p2 = pmax; + v3 p3 = v3{pmin.x, pmax.y, pmin.z}; + v2 t0 = tmin; + v2 t1 = v2{tmax.x,tmin.y}; + v2 t2 = tmax; + v2 t3 = v2{tmin.x,tmax.y}; + geo_quad_buffer_builder_push(&ui->geo, p0, p1, p2, p3, t0, t1, t2, t3, c); } internal void @@ -159,6 +172,7 @@ internal UI_Char_Draw_Cmd ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint) { UI_Char_Draw_Cmd result = {}; + zero_struct(result); Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(&ui->atlas, codepoint); result.uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite); @@ -181,14 +195,23 @@ ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint) internal void ui_frame_prepare(UI* ui, v2 window_dim) { - ui->verts_len = 0; - ui->indices_len = 0; + ui->geo.buffer_vertex.len = 0; + ui->geo.buffer_index.len = 0; ui->widgets.free_len = 0; ui->widgets.active_parent = 0; ui->widgets.root = ui_widget_pool_push(&ui->widgets, lit_str("root")); ui->widgets.active_parent = ui->widgets.root; + BSP_Node* panel_root = bsp_get(&ui->panels, ui->panels.root); + if (window_dim.x != 0 && window_dim.y != 0 && window_dim != panel_root->area.max) + { + BSP_Area area = {}; + area.min = v2{0,0}; + area.max = window_dim; + bsp_node_area_update(&ui->panels, ui->panels.root, area); + } + v2 half_d = window_dim * 0.5f; ui->proj = HMM_Orthographic(0, window_dim.x, window_dim.y, 0, 0.01f, 100); @@ -206,9 +229,42 @@ ui_frame_prepare(UI* ui, v2 window_dim) global bool show = false; +internal void +ui_draw_panel(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data) +{ + if (node->split.kind != BSPSplit_None) return; + UI* ui = (UI*)user_data; + BSP_Area area = node->area; + + if (ui->draw_panel_cb) ui->draw_panel_cb(ui->draw_panel_cb_data, id, *node, area); + + r32 z = -1; + v3 l0p0 = v3{ area.min.x, area.min.y, z }; // left side + v3 l0p1 = v3{ area.min.x + 1, area.max.y, z }; + v3 l1p0 = v3{ area.max.x - 1, area.min.y, z }; // right side + v3 l1p1 = v3{ area.max.x, area.max.y, z }; + v3 l2p0 = v3{ area.min.x, area.min.y , z }; // bottom side + v3 l2p1 = v3{ area.max.x, area.min.y + 1, z }; + v3 l3p0 = v3{ area.min.x, area.max.y , z }; // top side + v3 l3p1 = v3{ area.max.x, area.max.y + 1, z }; + u32 sid = WHITE_SPRITE_ID; + v4 c = WHITE_V4; + if (rect2_contains(area.min, area.max, ui->input->frame_hot->mouse_pos)) + { + c = PINK_V4; + } + + ui_sprite_push(ui, l0p0, l0p1, sid, c); + ui_sprite_push(ui, l1p0, l1p1, sid, c); + ui_sprite_push(ui, l2p0, l2p1, sid, c); + ui_sprite_push(ui, l3p0, l3p1, sid, c); +} + internal void ui_draw(UI* ui) { + bsp_walk_inorder(&ui->panels, ui->panels.root, ui_draw_panel, (u8*)ui); + u32 widget_count = ui->widgets.free_len; r32 range_min = -10; r32 range_max = -1; @@ -217,20 +273,18 @@ ui_draw(UI* ui) platform_geometry_buffer_update( &ui->per_frame_buffer, - (r32*)ui->verts, + (r32*)ui->geo.buffer_vertex.values, 0, - ui->verts_len * 10, - ui->indices, + ui->geo.buffer_vertex.len * ui->geo.buffer_vertex.stride, + ui->geo.buffer_index.values, 0, - ui->indices_len + ui->geo.buffer_index.len ); platform_shader_bind(ui->shader); platform_set_uniform(ui->shader, 0, ui->proj); platform_texture_bind(ui->atlas_texture); platform_geometry_bind(ui->per_frame_buffer); - platform_geometry_draw(ui->per_frame_buffer, ui->indices_len); - - OutputDebugStringA("Frame\n\n"); + platform_geometry_draw(ui->per_frame_buffer, ui->geo.buffer_index.len); } //////////////////////////////////////////// @@ -241,6 +295,7 @@ ui_widget_id_create(String string, u32 index) { assert(string.len != 0 && string.str != 0); UI_Widget_Id result = {}; + zero_struct(result); result.value = hash_djb2_to_u32(string); result.index = index; return result; @@ -272,7 +327,11 @@ ui_widget_pool_push(UI_Widget_Pool* pool, String string) result->child_first = 0; result->child_last = 0; - u32 index = hash_table_register(pool->states_hash, pool->states_cap, result->id.value); + u32 index = hash_table_find(pool->states_hash, pool->states_cap, result->id.value); + if (index == pool->states_cap) + { + index = hash_table_register(pool->states_hash, pool->states_cap, result->id.value); + } assert(index != pool->states_cap); UI_Widget_State* state = pool->states + index; zero_struct(*state); @@ -286,6 +345,7 @@ ui_widget_pool_push(UI_Widget_Pool* pool, String string) result ); } + pool->active_parent = result; return result; } @@ -337,6 +397,7 @@ internal UI_Widget_Result ui_widget_push(UI* ui, UI_Widget_Desc desc) { UI_Widget_Result result = {}; + zero_struct(result); v2 dim = desc.p_max - desc.p_min; if (dim.x == 0 || dim.y == 0) return result; @@ -427,9 +488,10 @@ ui_widget_push(UI* ui, UI_Widget_Desc desc) } internal void -ui_widget_pop(UI* ui, UI_Widget* widget) +ui_widget_pop(UI* ui, UI_Widget_Id widget_id) { - assert(ui_widget_id_equals(widget->id, ui->widgets.active_parent->id)); + if (!ui_widget_id_is_valid(widget_id)) return; + assert(ui_widget_id_equals(widget_id, ui->widgets.active_parent->id)); ui_widget_pool_pop(&ui->widgets); } @@ -475,7 +537,9 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s if (has_flag(child->desc.style.flags, (UIWidgetStyle_FillH | UIWidgetStyle_FillV))) { v3 fill_min = {}; + zero_struct(fill_min); v3 fill_max = {}; + zero_struct(fill_max); if (has_flag(child->desc.style.flags, UIWidgetStyle_FillH)) { r32 fill_x = HMM_Lerp(bg_min.x, child->desc.fill_pct.x, bg_max.x); @@ -523,6 +587,7 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s { u8 at = child->desc.string.str[i]; UI_Char_Draw_Cmd cmd = {}; + zero_struct(cmd); if (!char_is_space(at)) { cmd = ui_sprite_char_get_draw_cmd(ui, baseline, (u32)at); @@ -616,8 +681,9 @@ internal UI_Layout_Bounds ui_layout_get_next(UI_Layout* layout) { UI_Layout_Bounds result = {}; + zero_struct(result); if (layout->at.x >= layout->bounds_max.x || layout->at.y >= layout->bounds_max.y || - layout->at.y + layout->row_height >= layout->bounds_max.y) + layout->at.y + layout->row_height > layout->bounds_max.y) { return result; } @@ -653,8 +719,8 @@ ui_layout_get_next(UI_Layout* layout) if (result.min.x < layout->bounds_min.x || result.min.y < layout->bounds_min.y || result.max.x < layout->bounds_min.x || result.max.y < layout->bounds_min.y) { - result.min = {}; - result.max = {}; + zero_struct(result.min); + zero_struct(result.max); } return result; @@ -716,26 +782,40 @@ ui_layout_next_widget(UI* ui, UI_Widget_Kind kind) { UI_Layout_Bounds b = ui_layout_get_next(ui); UI_Widget_Desc d = {}; + zero_struct(d); d.p_min = b.min; d.p_max = b.max; d.style = ui_get_style(ui, kind); return d; } +internal void +ui_text(UI* ui, String string, v4 color) +{ + UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Text); + d.string = string; + d.style.color_fg = color; + UI_Widget_Result r = ui_widget_push(ui, d); + ui_widget_pop(ui, r.id); +} + internal void ui_text(UI* ui, String string) { UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Text); d.string = string; - ui_widget_push(ui, d); + UI_Widget_Result r = ui_widget_push(ui, d); + ui_widget_pop(ui, r.id); } internal void ui_text_f(UI* ui, char* fmt, ...) { + scratch_get(scratch); + va_list args; va_start(args, fmt); - String string = string_fv(scratch, fmt, args); + String string = string_fv(scratch.a, fmt, args); va_end(args); return ui_text(ui, string); @@ -747,6 +827,7 @@ ui_button(UI* ui, String string) UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Button); d.string = string; UI_Widget_Result r = ui_widget_push(ui, d); + ui_widget_pop(ui, r.id); return has_flag(r.flags, UIWidgetResult_MouseLeft_WentUp); } @@ -761,6 +842,7 @@ ui_toggle(UI* ui, String string, bool value) } d.string = string; UI_Widget_Result r = ui_widget_push(ui, d); + ui_widget_pop(ui, r.id); bool result = value; if (has_flag(r.flags, UIWidgetResult_MouseLeft_WentUp)) result = !result; return result; @@ -769,6 +851,8 @@ ui_toggle(UI* ui, String string, bool value) internal UI_Layout* ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 rows) { + scratch_get(scratch); + r32 scroll_bar_dim = 15; v2 scroll_bars_area = v2{0, 0}; v2 scroll_area_min = bounds_min; @@ -776,6 +860,7 @@ ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 ro v2 scroll_area_dim = scroll_area_max - scroll_area_min; v2 scroll_offset = {}; + zero_struct(scroll_offset); r32 rows_avail = floorf(scroll_area_dim.y / ui->layout->row_height); if (rows > rows_avail) { @@ -785,11 +870,13 @@ ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 ro scroll_area_dim = scroll_area_max - scroll_area_min; UI_Widget_Desc vscroll_d = {}; + zero_struct(vscroll_d); vscroll_d.p_min = { bounds_max.x - scroll_bar_dim, bounds_min.y }; vscroll_d.p_max = { bounds_max.x, bounds_max.y }; vscroll_d.style = ui_get_style(ui, UIWidget_VScroll); - vscroll_d.string = string_f(scratch, "%.*s_vscroll", str_varg(string)); + vscroll_d.string = string_f(scratch.a, "%.*s_vscroll", str_varg(string)); UI_Widget_Result r = ui_widget_push(ui, vscroll_d); + ui_widget_pop(ui, r.id); UI_Widget_State* vscroll_state = ui_widget_state_get(ui, r.id); scroll_offset.y = vscroll_state->scroll.y; @@ -800,7 +887,7 @@ ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 ro scroll_offset *= v2{ 0, y_scroll_dist }; - UI_Layout* layout = allocator_alloc_struct(scratch, UI_Layout); + UI_Layout* layout = allocator_alloc_struct(scratch.a, UI_Layout); layout->mode = UILayout_Columns; layout->bounds_min = scroll_area_min; layout->bounds_max = scroll_area_max; diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h index 4e53539..6a9b4e7 100644 --- a/src_v2/editor/lumenarium_editor_ui.h +++ b/src_v2/editor/lumenarium_editor_ui.h @@ -165,15 +165,11 @@ struct UI_Layout_Bounds v2 max; }; +typedef void UI_Draw_Panel_Cb(u8* user_data, BSP_Node_Id id, BSP_Node node, BSP_Area area); + struct UI { - UI_Vertex* verts; - u32 verts_len; - u32 verts_cap; - - u32* indices; - u32 indices_len; - u32 indices_cap; + Geo_Quad_Buffer_Builder geo; Texture_Atlas atlas; r32 font_ascent, font_descent, font_line_gap, font_space_width; @@ -186,6 +182,10 @@ struct UI UI_Layout* layout; + BSP panels; + UI_Draw_Panel_Cb* draw_panel_cb; + u8* draw_panel_cb_data; + // frames since these values were set u16 widget_next_hot_frames; u16 widget_hot_frames; @@ -206,7 +206,7 @@ internal void ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id); internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color); internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id); internal v3 ui_sprite_char_push(UI* ui, v2 at, u32 codepoint, v4 color); -internal void ui_draw(UI* ui); +internal void ui_draw(App_State* state); // Widgets @@ -223,7 +223,7 @@ internal UI_Widget* ui_widget_pool_push(UI_Widget_Pool* pool, String string); internal void ui_widget_pool_pop(UI_Widget_Pool* pool); internal UI_Widget_Result ui_widget_push(UI* ui, UI_Widget_Desc desc); -internal void ui_widget_pop(UI* ui, UI_Widget* widget); +internal void ui_widget_pop(UI* ui, UI_Widget_Id widget_id); internal r32 ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_step); diff --git a/src_v2/engine/lumenarium_engine.cpp b/src_v2/engine/lumenarium_engine.cpp index 2f9bb08..93f3e1e 100644 --- a/src_v2/engine/lumenarium_engine.cpp +++ b/src_v2/engine/lumenarium_engine.cpp @@ -18,7 +18,7 @@ en_frame_prepare(App_State* state) internal void en_frame(App_State* state) { - +#if 0 /////////////////////////////////////// // Output Data @@ -65,6 +65,7 @@ en_frame(App_State* state) invalid_code_path; } } +#endif } internal void diff --git a/src_v2/engine/lumenarium_engine_assembly.cpp b/src_v2/engine/lumenarium_engine_assembly.cpp index 1e58678..156b3fc 100644 --- a/src_v2/engine/lumenarium_engine_assembly.cpp +++ b/src_v2/engine/lumenarium_engine_assembly.cpp @@ -100,3 +100,22 @@ assembly_add_led( assert(strip->pixels_len < strip->pixels_cap); strip->pixels[strip->pixels_len++] = pixel_index; } + +void +assembly_strip_create_leds( + Assembly_Array* a, + Assembly_Handle h, + Assembly_Strip* strip, + v3 start, v3 end, + u32 led_count + ){ + v3 delta_total = end - start; + v3 delta_step = delta_total / (r32)led_count; + + for (u32 i = 0; i < led_count; i++) + { + v4 pos = {0,0,0,1}; + pos.XYZ = start + ((r32)i * delta_step); + assembly_add_led(a, h, strip, pos); + } +} \ No newline at end of file diff --git a/src_v2/libs/stb_sprintf.h b/src_v2/libs/stb_sprintf.h new file mode 100644 index 0000000..64bce7e --- /dev/null +++ b/src_v2/libs/stb_sprintf.h @@ -0,0 +1,1906 @@ +// stb_sprintf - v1.10 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE +#define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) + typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); + Convert into a buffer, calling back every STB_SPRINTF_MIN chars. + Your callback can then copy the chars out, print them or whatever. + This function is actually the workhorse for everything else. + The buffer you pass in must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + +#if defined(__clang__) +#if defined(__has_feature) && defined(__has_attribute) +#if __has_feature(address_sanitizer) +#if __has_attribute(__no_sanitize__) +#define STBSP__ASAN __attribute__((__no_sanitize__("address"))) +#elif __has_attribute(__no_sanitize_address__) +#define STBSP__ASAN __attribute__((__no_sanitize_address__)) +#elif __has_attribute(__no_address_safety_analysis__) +#define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) +#endif +#endif +#endif +#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ +#define STBSP__ASAN __attribute__((__no_sanitize_address__)) +#endif +#endif + +#ifndef STBSP__ASAN +#define STBSP__ASAN +#endif + +#ifdef STB_SPRINTF_STATIC +#define STBSP__PUBLICDEC static +#define STBSP__PUBLICDEF static STBSP__ASAN +#else +#ifdef __cplusplus +#define STBSP__PUBLICDEC extern "C" +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN +#else +#define STBSP__PUBLICDEC extern +#define STBSP__PUBLICDEF STBSP__ASAN +#endif +#endif + +#if defined(__has_attribute) +#if __has_attribute(format) +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) +#endif +#endif + +#ifndef STBSP__ATTRIBUTE_FORMAT +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) +#endif + +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + +#include // for va_arg(), va_list() +#include // size_t, ptrdiff_t + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + +#ifndef STB_SPRINTF_DECORATE +#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names +#endif + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + +#define stbsp__uint32 unsigned int +#define stbsp__int32 signed int + +#ifdef _MSC_VER +#define stbsp__uint64 unsigned __int64 +#define stbsp__int64 signed __int64 +#else +#define stbsp__uint64 unsigned long long +#define stbsp__int64 signed long long +#endif +#define stbsp__uint16 unsigned short + +#ifndef stbsp__uintptr +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) +#define stbsp__uintptr stbsp__uint64 +#else +#define stbsp__uintptr stbsp__uint32 +#endif +#endif + +#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define STB_SPRINTF_MSVC_MODE +#endif +#endif + +#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses +#define STBSP__UNALIGNED(code) +#else +#define STBSP__UNALIGNED(code) code +#endif + +#ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); +#define STBSP__SPECIAL 0x7000 +#endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = +{ + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899" +}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) +{ + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) +{ + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) +{ + char const * sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) +{ + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff +#define stbsp__chk_cb_bufL(bytes) \ +{ \ +int len = (int)(bf - buf); \ +if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ +tlen += len; \ +if (0 == (bf = buf = callback(buf, user, len))) \ +goto done; \ +} \ +} +#define stbsp__chk_cb_buf(bytes) \ +{ \ +if (callback) { \ +stbsp__chk_cb_bufL(bytes); \ +} \ +} +#define stbsp__flush_cb() \ +{ \ +stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ +} // flush if there is even one byte in the buffer +#define stbsp__cb_buf_clamp(cl, v) \ +cl = v; \ +if (callback) { \ +int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ +if (cl > lg) \ +cl = lg; \ +} + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; +#ifdef STB_SPRINTF_NOUNALIGNED + if(((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else +#endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { +#define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; +#ifndef STB_SPRINTF_NOFLOAT + double fv; +#endif + stbsp__int32 dp; + char const *sn; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + +#ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; +#else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); + // add leading chars + +#ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; +#else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; +#endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; +#ifdef STB_SPRINTF_MSVC_MODE + n = 5; +#else + n = (dp >= 100) ? 5 : 4; +#endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; +#endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + +#ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } +#endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } + endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + + done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) +{ + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can +} + +static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) +{ + stbsp__context * c = (stbsp__context*)user; + (void) sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) +{ + stbsp__context c; + + if ( (count == 0) && !buf ) + { + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + } + else + { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + + // zero-terminate + l = (int)( c.buf - buf ); + if ( l >= count ) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) +{ + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +#ifndef STB_SPRINTF_NOFLOAT + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) \ +{ \ +int cn; \ +for (cn = 0; cn < 8; cn++) \ +((char *)&dest)[cn] = ((char *)&src)[cn]; \ +} + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) +{ + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64) b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 +}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 +}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 +}; +static double const stbsp__top[13] = { + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 +}; +static double const stbsp__negtop[13] = { + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 +}; +static double const stbsp__toperr[13] = { + 8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282 +}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317 +}; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U +}; +#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) +#else +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL +}; +#define stbsp__tento19th (1000000000000000000ULL) +#endif + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ +{ \ +double ahi = 0, alo, bhi = 0, blo; \ +stbsp__int64 bt; \ +oh = xh * yh; \ +STBSP__COPYFP(bt, xh); \ +bt &= ((~(stbsp__uint64)0) << 27); \ +STBSP__COPYFP(ahi, bt); \ +alo = xh - ahi; \ +STBSP__COPYFP(bt, yh); \ +bt &= ((~(stbsp__uint64)0) << 27); \ +STBSP__COPYFP(bhi, bt); \ +blo = yh - bhi; \ +ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ +} + +#define stbsp__ddtoS64(ob, xh, xl) \ +{ \ +double ahi = 0, alo, vh, t; \ +ob = (stbsp__int64)xh; \ +vh = (double)ob; \ +ahi = (xh - vh); \ +t = (ahi - xh); \ +alo = (xh - (ahi - t)) - (vh + t); \ +ob += (stbsp__int64)(ahi + alo + xl); \ +} + +#define stbsp__ddrenorm(oh, ol) \ +{ \ +double s; \ +s = oh + ol; \ +ol = ol - (s - oh); \ +oh = s; \ +} + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special values +// returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) +{ + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64) bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64) bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +#endif // STB_SPRINTF_NOFLOAT + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/src_v2/lumenarium_bsp.h b/src_v2/lumenarium_bsp.h new file mode 100644 index 0000000..bd18720 --- /dev/null +++ b/src_v2/lumenarium_bsp.h @@ -0,0 +1,492 @@ +/* date = April 11th 2022 9:57 am */ + +#ifndef LUMENARIUM_BSP_H +#define LUMENARIUM_BSP_H + +// NOTE(PS): Functionality Notes +// - there must always be a root node that contains the area of the tree as a whole +// - a node with no children has not been split + +#define BTREE_NODE_ID_VALID_BIT (1 << 31) +struct BSP_Node_Id +{ + u32 value; +}; + +enum BSP_Split_Kind +{ + BSPSplit_XAxis = 1, + BSPSplit_YAxis = 0, + BSPSplit_ZAxis = 2, + BSPSplit_None = 3, +}; + +struct BSP_Split +{ + BSP_Split_Kind kind; + r32 value; +}; + +enum BSP_Split_Update_Flags +{ + BSPSplitUpdate_None = 0, + BSPSplitUpdate_FreeZeroAreaChildren = 1, +}; + +enum BSP_Child_Selector +{ + // NOTE(PS): these values are intentionally overlapping since + // they access the data structure of the B-Tree in a particular + // way. ie. left and top are the same space in memory as are + // right and bottom + BSPChild_Left = 0, + BSPChild_Top = 0, + + BSPChild_Right = 1, + BSPChild_Bottom = 1, +}; + +struct BSP_Area +{ + v2 min; + v2 max; +}; + +struct BSP_Node +{ + union + { + BSP_Node_Id parent; + BSP_Node_Id next_free; + }; + + BSP_Split split; + + union + { + BSP_Node_Id children[2]; + struct + { + union + { + BSP_Node_Id left; + BSP_Node_Id top; + }; + union + { + BSP_Node_Id right; + BSP_Node_Id bottom; + }; + }; + }; + u32 user_data; + + BSP_Area area; +}; + +struct BSP +{ + BSP_Node* nodes; + u32 nodes_cap; + u32 nodes_len; + + BSP_Node_Id root; + BSP_Node_Id free_first; +}; + +typedef void BSP_Walk_Cb(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data); + +internal BSP bsp_create(Allocator* allocator, u32 cap); + +internal BSP_Node* bsp_get(BSP* tree, BSP_Node_Id id); +internal BSP_Node_Id bsp_push(BSP* tree, BSP_Node_Id parent, BSP_Area area, u32 user_data); +internal void bsp_free(BSP* tree, BSP_Node_Id id); +internal void bsp_free_cb(BSP* tree, BSP_Node_Id id, BSP* node, u8* user_data); + +union BSP_Split_Result +{ + BSP_Node_Id children[2]; + struct + { + union + { + BSP_Node_Id left; + BSP_Node_Id top; + }; + union + { + BSP_Node_Id right; + BSP_Node_Id bottom; + }; + }; +}; + +internal BSP_Split_Result bsp_split(BSP* tree, BSP_Node_Id id, r32 split, BSP_Split_Kind kind, u32 user_data_0, u32 user_data_1); +internal void bsp_join_recursive(BSP* tree, BSP_Node* parent, BSP_Child_Selector keep); + +// left, parent, right +internal void bsp_walk_inorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* cb, u8* user_data); +// parent, left right +internal void bsp_walk_preorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* cb, u8* user_data); +// parent, right, parent +internal void bsp_walk_postorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* cb, u8* user_data); + +internal void bsp_node_update_child_areas(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data); +internal void bsp_node_area_update(BSP* tree, BSP_Node_Id id, BSP_Area new_area); +internal void bsp_child_split_update(BSP* tree, BSP_Node_Id node, u32 new_split); + +/////////////////////////////////////////////////// +// IMPLEMENTATION + + +internal BSP +bsp_create(Allocator* allocator, u32 cap) +{ + BSP result = {}; + zero_struct(result); + result.nodes_cap = cap; + result.nodes = allocator_alloc_array(allocator, BSP_Node, cap); + return result; +} + +#define bsp_node_id_is_valid(id) (has_flag(id.value, BTREE_NODE_ID_VALID_BIT)) +#define bsp_node_id_equals(a,b) (a.value == b.value) +#define bsp_node_id_to_index(id) (id.value & (u32)(~BTREE_NODE_ID_VALID_BIT)) + +internal BSP_Node* +bsp_get(BSP* tree, BSP_Node_Id id) +{ + if (!bsp_node_id_is_valid(id)) return 0; + u32 index = bsp_node_id_to_index(id); + if (index > tree->nodes_len) return 0; + return tree->nodes + index; +} + +internal BSP_Node_Id +bsp_push(BSP* tree, BSP_Node_Id parent_id, BSP_Area area, u32 user_data) +{ + BSP_Node_Id result = BSP_Node_Id{0}; + BSP_Node* node = 0; + + if (tree->nodes_len >= tree->nodes_cap) + { + if (bsp_node_id_is_valid(tree->free_first)) + { + result = tree->free_first; + node = bsp_get(tree, result); + tree->free_first = node->next_free; + zero_struct(node->parent); + } + } + else + { + result.value = tree->nodes_len++; + assert(!has_flag(result.value, BTREE_NODE_ID_VALID_BIT)); + add_flag(result.value, BTREE_NODE_ID_VALID_BIT); + node = tree->nodes + bsp_node_id_to_index(result); + } + + if (bsp_node_id_is_valid(result)) + { + node->split.kind = BSPSplit_None; + node->parent = parent_id; + node->area = area; + node->user_data = user_data; + } + + return result; +} + +internal void +bsp_free_(BSP* tree, BSP_Node_Id id, BSP_Node* now_free) +{ + if (bsp_node_id_is_valid(now_free->parent)) + { + BSP_Node* parent = bsp_get(tree, now_free->parent); + if (bsp_node_id_equals(parent->children[0], id)) + { + zero_struct(parent->children[0]); + } + else if (bsp_node_id_equals(parent->children[1], id)) + { + zero_struct(parent->children[1]); + } + else + { + // NOTE(PS): in this case, a child node had a reference to + // a parent that didn't have a reference back to the child + // this means the tree itself is messed up + invalid_code_path; + } + } + + zero_struct(*now_free); + now_free->next_free = tree->free_first; + tree->free_first = id; +} + +internal void +bsp_free(BSP* tree, BSP_Node_Id id) +{ + BSP_Node* now_free = bsp_get(tree, id); + bsp_free_(tree, id, now_free); +} + +internal void +bsp_free_cb(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data) +{ + bsp_free_(tree, id, node); +} + +internal BSP_Split_Result +bsp_split(BSP* tree, BSP_Node_Id node_id, r32 split, BSP_Split_Kind kind, u32 user_data_0, u32 user_data_1) +{ + BSP_Node* node = bsp_get(tree, node_id); + split = clamp(node->area.min.Elements[kind], split, node->area.max.Elements[kind]); + node->split.value = split; + node->split.kind = kind; + node->children[0] = bsp_push(tree, node_id, {}, user_data_0); + node->children[1] = bsp_push(tree, node_id, {}, user_data_1); + bsp_node_update_child_areas(tree, node_id, node, 0); + + BSP_Split_Result result = {}; + result.children[0] = node->children[0]; + result.children[1] = node->children[1]; + return result; +} + +internal void +bsp_join_recursive(BSP* tree, BSP_Node_Id parent_id, BSP_Child_Selector keep) +{ + BSP_Node* parent = bsp_get(tree, parent_id); + BSP_Node keep_node = *bsp_get(tree, parent->children[keep]); + bsp_walk_preorder(tree, parent->children[0], bsp_free_cb, 0); + bsp_walk_preorder(tree, parent->children[1], bsp_free_cb, 0); + parent->user_data = keep_node.user_data; + zero_struct(parent->children[0]); + zero_struct(parent->children[1]); +} + +// NOTE(PS): the other three walk functions all require allocation of a stack +// while this is fast with our scratch allocator, there are cases where, for +// correctness, we walk a tree that is very likely to be a single node. In +// those cases, we can avoid allocating anything by just visiting the single +// node and returning early. +// This function provides that functionality for all walk functions +internal bool +bsp_walk_single_node_check(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_data) +{ + BSP_Node* node = bsp_get(tree, first); + if (node->split.kind == BSPSplit_None) + { + visit(tree, first, node, user_data); + return true; + } + return false; +} + +// left, parent, right +internal void +bsp_walk_inorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_data) +{ + if (!bsp_node_id_is_valid(first)) return; + if (bsp_walk_single_node_check(tree, first, visit, user_data)) return; + scratch_get(scratch); + BSP_Node_Id* stack = allocator_alloc_array(scratch.a, BSP_Node_Id, tree->nodes_len); + u32 stack_len = 0; + memory_zero_array(stack, BSP_Node_Id, tree->nodes_len); + + BSP_Node_Id at = first; + while (true) + { + if (bsp_node_id_is_valid(at)) + { + stack[stack_len++] = at; + BSP_Node* n = bsp_get(tree, at); + at = n->children[0]; + } + else + { + if (stack_len == 0) break; + + at = stack[--stack_len]; + BSP_Node* n = bsp_get(tree, at); + visit(tree, at, n, user_data); + at = n->children[1]; + } + } +} + +// parent, left right +internal void +bsp_walk_preorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_data) +{ + if (bsp_walk_single_node_check(tree, first, visit, user_data)) return; + scratch_get(scratch); + BSP_Node_Id* stack = allocator_alloc_array(scratch.a, BSP_Node_Id, tree->nodes_len); + u32 stack_len = 0; + memory_zero_array(stack, BSP_Node_Id, tree->nodes_len); + + BSP_Node_Id at = first; + + while (true) + { + while (bsp_node_id_is_valid(at)) + { + BSP_Node* n = bsp_get(tree, at); + visit(tree, at, n, user_data); + stack[stack_len++] = at; + at = n->children[0]; + } + + if (!bsp_node_id_is_valid(at) && stack_len == 0) break; + + at = stack[--stack_len]; + BSP_Node* n = bsp_get(tree, at); + at = n->children[1]; + } +} + +// parent, right, parent +internal void +bsp_walk_postorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_data) +{ + if (bsp_walk_single_node_check(tree, first, visit, user_data)) return; + scratch_get(scratch); + BSP_Node_Id* stack = allocator_alloc_array(scratch.a, BSP_Node_Id, tree->nodes_len); + u32 stack_len = 0; + memory_zero_array(stack, BSP_Node_Id, tree->nodes_len); + + BSP_Node_Id at = first; + while (true) + { + if (bsp_node_id_is_valid(at)) + { + BSP_Node* n = bsp_get(tree, at); + if (bsp_node_id_is_valid(n->children[1])) stack[stack_len++] = n->children[1]; + stack[stack_len++] = at; + at = n->children[0]; + } + else + { + if (stack_len == 0) break; + + at = stack[--stack_len]; + BSP_Node* n = bsp_get(tree, at); + assert(n != 0); + + if (bsp_node_id_is_valid(n->children[1]) && bsp_node_id_equals(n->children[1], stack[stack_len - 1])) + { + BSP_Node_Id at_temp = stack[stack_len - 1]; + stack[stack_len - 1] = at; + at = at_temp; + } + else + { + visit(tree, at, n, user_data); + zero_struct(at); + } + } + } +} + +internal void +bsp_node_update_child_areas(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data) +{ + // assume that node's area is correct. Using that, clamp its split as appropriate + // and then update its children. If a child has an area of zero, rely on flags + // to determine behavior + + if (node->split.kind == BSPSplit_None) return; + + BSP_Split_Update_Flags flags = (BSP_Split_Update_Flags)0; + if (user_data) flags = *(BSP_Split_Update_Flags*)user_data; + + BSP_Split_Kind kind = node->split.kind; + + BSP_Node* node_min = bsp_get(tree, node->children[0]); + BSP_Node* node_max = bsp_get(tree, node->children[1]); + node_min->area = node->area; + node_max->area = node->area; + node_min->area.max.Elements[kind] = node->split.value; + node_max->area.min.Elements[kind] = node->split.value; + + if (has_flag(flags, BSPSplitUpdate_FreeZeroAreaChildren)) + { + bool free_children = false; + if (node_min->area.max.Elements[kind] <= node_min->area.min.Elements[kind]) + { + node->user_data = node_max->user_data; + free_children= true; + } + else if (node_max->area.max.Elements[kind] <= node_max->area.min.Elements[kind]) + { + node->user_data = node_min->user_data; + free_children= true; + } + + if (free_children) + { + bsp_walk_postorder(tree, node->children[0], bsp_free_cb, 0); + bsp_walk_postorder(tree, node->children[1], bsp_free_cb, 0); + } + + } + + // NOTE(PS): no need to recurse, this function assumes its either being + // called on a particular node or its the callback of one of the tree + // walk functions +} + +internal void +bsp_node_area_update(BSP* tree, BSP_Node_Id node_id, BSP_Area area) +{ + BSP_Node* node = bsp_get(tree, node_id); + node->area = area; + BSP_Split_Update_Flags flags = BSPSplitUpdate_FreeZeroAreaChildren; + bsp_walk_preorder(tree, node_id, bsp_node_update_child_areas, (u8*)&flags); +} + +internal void +bsp_child_split_update(BSP* tree, BSP_Node_Id node_id, r32 new_split, BSP_Split_Update_Flags flags) +{ + BSP_Node* node = bsp_get(tree, node_id); + node->split.value = new_split; + bsp_walk_preorder(tree, node_id, bsp_node_update_child_areas, (u8*)&flags); +} + +#if defined(DEBUG) + +internal void +bsp_tests() +{ + scratch_get(scratch); + BSP tree = bsp_create(scratch.a, 256); + tree.root = bsp_push(&tree, {0}, {{0,0},{512,512}}, 0); + + BSP_Split_Result r0 = bsp_split(&tree, tree.root, 256, BSPSplit_YAxis, 0, 0); + BSP_Node* root = bsp_get(&tree, tree.root); + BSP_Node* n0 = bsp_get(&tree, r0.children[0]); + BSP_Node* n1 = bsp_get(&tree, r0.children[1]); + assert(root != 0 && n0 != 0 && n1 != 0); + assert(n0->area.min == root->area.min); + assert(n0->area.max.x == 256 && n0->area.max.y == root->area.max.y); + assert(n1->area.max == root->area.max); + assert(n1->area.min.x == 256 && n0->area.min.y == root->area.min.y); + assert(n0->split.kind == BSPSplit_None); + assert(n1->split.kind == BSPSplit_None); + assert(root->split.kind == BSPSplit_YAxis); + + BSP_Split_Result r1 = bsp_split(&tree, root->children[0], 32, BSPSplit_YAxis, 0, 0); + BSP_Split_Result r2 = bsp_split(&tree, r1.children[1], 64, BSPSplit_XAxis, 0, 0); + + bsp_walk_postorder(&tree, root->children[0], bsp_free_cb, 0); +} + +#else + +#define bsp_tests() + +#endif + +#endif //LUMENARIUM_BSP_H diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.cpp index ea5f7d5..b4b0b36 100644 --- a/src_v2/lumenarium_first.cpp +++ b/src_v2/lumenarium_first.cpp @@ -6,12 +6,12 @@ lumenarium_init() { App_State* state = 0; - permanent = bump_allocator_create_reserve(GB(1)); - scratch = bump_allocator_create_reserve(KB(64)); + permanent = bump_allocator_create_reserve(GB(2)); + scratch_ = bump_allocator_create_reserve(GB(8)); platform_file_jobs_init(); run_tests(); - + scratch_get(scratch); App_Init_Desc desc = incenter_get_init_desc(); // TODO(PS): make sure the values make sense in desc @@ -21,6 +21,13 @@ lumenarium_init() state->input_state = input_state_create(permanent); + String exe_file_path = platform_get_exe_path(scratch.a); + 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; + String run_tree_path = string_get_prefix(exe_file_path, run_tree_end); + String run_tree_path_nullterm = string_copy(run_tree_path, scratch.a); + platform_pwd_set(run_tree_path_nullterm); + en_init(state, desc); if (has_flag(state->flags, AppState_RunEditor)) { @@ -33,7 +40,7 @@ lumenarium_init() internal void lumenarium_frame_prepare(App_State* state) { - allocator_clear(scratch); + allocator_clear(scratch_); input_state_swap_frames(state->input_state); @@ -50,7 +57,7 @@ lumenarium_frame_prepare(App_State* state) internal void lumenarium_frame(App_State* state) { - //en_frame(state); + en_frame(state); if (has_flag(state->flags, AppState_RunEditor)) { ed_frame(state); diff --git a/src_v2/lumenarium_first.h b/src_v2/lumenarium_first.h index 1d6c20c..85a5171 100644 --- a/src_v2/lumenarium_first.h +++ b/src_v2/lumenarium_first.h @@ -8,9 +8,17 @@ typedef struct App_State App_State; // Environment #include "lumenarium_memory.cpp" #include "lumenarium_string.cpp" +#include "lumenarium_random.h" #include "lumenarium_input.cpp" #include "lumenarium_texture_atlas.cpp" #include "lumenarium_hash.cpp" +#include "lumenarium_geometry.h" + +global Allocator* scratch_; // gets reset at frame boundaries + +#define scratch_get(ident) Allocator_Scratch ident = Allocator_Scratch(scratch_) + +#include "lumenarium_bsp.h" // Engine typedef struct Assembly_Strip Assembly_Strip; @@ -28,7 +36,6 @@ typedef struct Assembly_Strip Assembly_Strip; // Lumenarium Runtime Environment global Allocator* permanent; -global Allocator* scratch; // gets reset at frame boundaries #if defined(DEBUG) # include "lumenarium_tests.cpp" @@ -72,6 +79,7 @@ struct App_State #include "editor/lumenarium_editor_ui.cpp" #include "editor/lumenarium_editor_renderer.cpp" +#include "editor/lumenarium_editor_sculpture_visualizer.cpp" #include "editor/lumenarium_editor.cpp" diff --git a/src_v2/lumenarium_geometry.h b/src_v2/lumenarium_geometry.h new file mode 100644 index 0000000..abfe1ce --- /dev/null +++ b/src_v2/lumenarium_geometry.h @@ -0,0 +1,268 @@ +/* date = April 11th 2022 3:22 pm */ + +#ifndef LUMENARIUM_GEOMETRY_H +#define LUMENARIUM_GEOMETRY_H + +// Utility functions for working with 3d geometry + +// NOTE(PS): the Buffer vs Buffer_Builder distinction +// lets a static sized buffer exist, and be auto 'exported' or 'available' +// from a larger data structure that also stores enough data to incrementally +// build the buffer + +// NOTE(PS): @IMPORTANT!!! +// at the moment this builder doesn't support allowing for the elements +// of a vertex to be in an order other than position tex color +// and in any other packing than tightly packed +typedef u32 Geo_Vertex_Buffer_Storage; +enum +{ + GeoVertexBufferStorage_None = 0, + GeoVertexBufferStorage_Position = 1, + GeoVertexBufferStorage_TexCoord = 2, + GeoVertexBufferStorage_Color = 4 +}; + +struct Geo_Vertex_Buffer +{ + r32* values; + u32 len; + Geo_Vertex_Buffer_Storage storage; + u32 stride; +}; + +struct Geo_Vertex_Buffer_Builder +{ + union + { + Geo_Vertex_Buffer buffer; + struct + { + // NOTE(PS): @Maintainence + // this must always match the layout of Geo_Vertex_Buffer + r32* values; + u32 len; + Geo_Vertex_Buffer_Storage storage; + u32 stride; + }; + }; + u32 cap; +}; + +struct Geo_Index_Buffer +{ + u32* values; + u32 len; +}; + +struct Geo_Index_Buffer_Builder +{ + union + { + Geo_Index_Buffer buffer; + struct + { + // NOTE(PS): @Maintainence + // this must always match the layout of Geo_Index_Buffer + u32* values; + u32 len; + }; + }; + u32 cap; +}; + +struct Geo_Quad_Buffer +{ + Geo_Vertex_Buffer buffer_vertex; + Geo_Index_Buffer buffer_index; +}; + +struct Geo_Quad_Buffer_Builder +{ + Geo_Vertex_Buffer_Builder buffer_vertex; + Geo_Index_Buffer_Builder buffer_index; +}; + +internal Geo_Vertex_Buffer_Builder geo_vertex_buffer_builder_create(Allocator* a, u32 cap); +internal Geo_Index_Buffer_Builder geo_index_buffer_builder_create(Allocator* a, u32 cap); +internal Geo_Quad_Buffer_Builder geo_quad_buffer_builder_create(Allocator* a, u32 vertex_cap, u32 index_cap); + +// Vertex Buffer +internal u32 geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t, v4 c); +internal u32 geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t); +internal u32 geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v); + +// Index Buffer +internal u32 geo_index_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, u32 i); +internal void geo_index_buffer_builder_push_tri(Geo_Vertex_Buffer_Builder* b, u32 i0, u32 i1, u32 i2); +internal void geo_index_buffer_builder_push_quad(Geo_Vertex_Buffer_Builder* b, u32 i0, u32 i1, u32 i2, u32 i3); + +// Quad Buffer +internal void geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3, v4 c); +internal void geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3); +internal void geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3); +internal Geo_Quad_Buffer geo_quad_buffer_builder_get_static_buffer(Geo_Quad_Buffer_Builder* b); + +///////////////////////////////////////////// +// Implementation + +internal u32 +geo_vertex_buffer_builder_stride(Geo_Vertex_Buffer_Storage storage) +{ + u32 result = 0; + result += has_flag(storage, GeoVertexBufferStorage_Position) ? 3 : 0; + result += has_flag(storage, GeoVertexBufferStorage_TexCoord) ? 2 : 0; + result += has_flag(storage, GeoVertexBufferStorage_Color) ? 4 : 0; + return result; +} + +internal Geo_Vertex_Buffer_Builder +geo_vertex_buffer_builder_create(Allocator* a, u32 cap, Geo_Vertex_Buffer_Storage storage) +{ + u32 stride = geo_vertex_buffer_builder_stride(storage); + u32 size = cap * stride; + + Geo_Vertex_Buffer_Builder result = {}; + zero_struct(result); + result.cap = cap; + result.storage = storage; + result.stride = stride; + result.values = allocator_alloc_array(a, r32, size); + + return result; +} + +internal Geo_Index_Buffer_Builder +geo_index_buffer_builder_create(Allocator* a, u32 cap) +{ + + Geo_Index_Buffer_Builder result = {}; + zero_struct(result); + result.cap = cap; + result.values = allocator_alloc_array(a, u32, cap); + return result; +} + +internal Geo_Quad_Buffer_Builder +geo_quad_buffer_builder_create(Allocator* a, u32 vertex_cap, Geo_Vertex_Buffer_Storage storage, u32 index_cap) +{ + Geo_Quad_Buffer_Builder result = {}; + result.buffer_vertex = geo_vertex_buffer_builder_create(a, vertex_cap, storage); + result.buffer_index = geo_index_buffer_builder_create(a, index_cap); + return result; +} + +// Vertex Buffer + +internal u32 +geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t, v4 c) +{ + assert(b->len < b->cap); + u32 result = b->len++; + u32 offset = result * b->stride; + if (has_flag(b->storage, GeoVertexBufferStorage_Position)) + { + b->values[offset++] = v.x; + b->values[offset++] = v.y; + b->values[offset++] = v.z; + } + if (has_flag(b->storage, GeoVertexBufferStorage_TexCoord)) + { + b->values[offset++] = t.x; + b->values[offset++] = t.y; + } + if (has_flag(b->storage, GeoVertexBufferStorage_Color)) + { + b->values[offset++] = c.x; + b->values[offset++] = c.y; + b->values[offset++] = c.z; + b->values[offset++] = c.w; + } + return result; +} + +internal u32 +geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t) +{ + return geo_vertex_buffer_builder_push(b, v, t, v4{0}); +} + +internal u32 +geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v) +{ + return geo_vertex_buffer_builder_push(b, v, v2{0}, v4{0}); +} + + +// Index Buffer + +internal u32 +geo_index_buffer_builder_push(Geo_Index_Buffer_Builder* b, u32 i) +{ + assert(b->len < b->cap); + u32 result = b->len++; + b->values[result] = i; + return result; +} + +internal void +geo_index_buffer_builder_push_tri(Geo_Index_Buffer_Builder* b, u32 i0, u32 i1, u32 i2) +{ + geo_index_buffer_builder_push(b, i0); + geo_index_buffer_builder_push(b, i1); + geo_index_buffer_builder_push(b, i2); +} + +internal void +geo_index_buffer_builder_push_quad(Geo_Index_Buffer_Builder* b, u32 i0, u32 i1, u32 i2, u32 i3) +{ + geo_index_buffer_builder_push_tri(b, i0, i1, i2); + geo_index_buffer_builder_push_tri(b, i0, i2, i3); +} + + +// Quad Buffer + +internal void +geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3, v4 c) +{ + u32 i0 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p0, t0, c); + u32 i1 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p1, t1, c); + u32 i2 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p2, t2, c); + u32 i3 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p3, t3, c); + + geo_index_buffer_builder_push_quad(&b->buffer_index, i0, i1, i2, i3); +} + +internal void +geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3) +{ + u32 i0 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p0, t0, v4{}); + u32 i1 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p1, t1, v4{}); + u32 i2 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p2, t2, v4{}); + u32 i3 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p3, t3, v4{}); + + geo_index_buffer_builder_push_quad(&b->buffer_index, i0, i1, i2, i3); +} + +internal void +geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3) +{ + u32 i0 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p0, v2{}, v4{}); + u32 i1 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p1, v2{}, v4{}); + u32 i2 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p2, v2{}, v4{}); + u32 i3 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p3, v2{}, v4{}); + + geo_index_buffer_builder_push_quad(&b->buffer_index, i0, i1, i2, i3); +} + +internal Geo_Quad_Buffer +geo_quad_buffer_builder_get_static_buffer(Geo_Quad_Buffer_Builder* b) +{ + Geo_Quad_Buffer result = {}; + result.buffer_vertex = b->buffer_vertex.buffer; + result.buffer_index = b->buffer_index.buffer; + return result; +} + +#endif //LUMENARIUM_GEOMETRY_H diff --git a/src_v2/lumenarium_memory.cpp b/src_v2/lumenarium_memory.cpp index 103396f..a133df8 100644 --- a/src_v2/lumenarium_memory.cpp +++ b/src_v2/lumenarium_memory.cpp @@ -195,6 +195,16 @@ bump_allocator_destroy(Allocator* allocator) allocator_destroy_(allocator, sizeof(Allocator_Bump)); } +internal void +bump_allocator_rewind(Allocator* allocator, u64 to_point) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; +#if defined(DEBUG) + memory_zero(bump->base + to_point, bump->at - to_point); +#endif + bump->at = to_point; +} + internal Allocator* bump_allocator_create_() { @@ -248,6 +258,28 @@ bump_allocator_create_child(Allocator* parent, u64 init_size) return result; } + +///////////////////////////////////////// +// Scratch Allocator + +struct Allocator_Scratch +{ + Allocator* a; + u64 at_before; + + Allocator_Scratch(Allocator* allocator) + { + this->a = allocator; + Allocator_Bump* bump = (Allocator_Bump*)this->a->allocator_data; + this->at_before = bump->at; + } + + ~Allocator_Scratch() + { + bump_allocator_rewind(this->a, this->at_before); + } +}; + ///////////////////////////////////////// // Paged Allocator diff --git a/src_v2/lumenarium_random.h b/src_v2/lumenarium_random.h new file mode 100644 index 0000000..625a842 --- /dev/null +++ b/src_v2/lumenarium_random.h @@ -0,0 +1,47 @@ +/* date = April 11th 2022 6:11 pm */ + +#ifndef LUMENARIUM_RANDOM_H +#define LUMENARIUM_RANDOM_H + +struct Random_Series +{ + u32 last_value; +}; + +internal Random_Series +random_series_create(u32 seed) +{ + Random_Series result = {}; + result.last_value = seed; + return result; +} + +internal u32 +random_series_next(Random_Series* s) +{ + u32 result = s->last_value; + result ^= result << 13; + result ^= result >> 17; + result ^= result << 5; + s->last_value = result; + return result; +} + +internal r32 +random_series_next_unilateral(Random_Series* s) +{ + r32 result = random_series_next(s) / (r32)(0xFFFFFFFF); + return result; +} + +internal r32 +random_series_next_bilateral(Random_Series* s) +{ + r32 result = random_series_next(s) / (r32)(0xFFFFFFFF); + result = (result * 2.0f) - 1.0f; + return result; +} + + + +#endif //LUMENARIUM_RANDOM_H diff --git a/src_v2/lumenarium_string.cpp b/src_v2/lumenarium_string.cpp index e9f2a8b..6c6ed7e 100644 --- a/src_v2/lumenarium_string.cpp +++ b/src_v2/lumenarium_string.cpp @@ -250,9 +250,9 @@ string_fv(Allocator* a, char* fmt, va_list args) { va_list args1; va_copy(args1, args); - s32 needed = vsnprintf(0, 0, fmt, args); + s32 needed = stbsp_vsnprintf(0, 0, fmt, args); String result = allocator_alloc_string(a, needed + 1); - result.len = vsnprintf((char*)result.str, result.cap, fmt, args1); + result.len = stbsp_vsnprintf((char*)result.str, (int)result.cap, fmt, args1); result.str[result.len] = 0; va_end(args1); return result; diff --git a/src_v2/lumenarium_tests.cpp b/src_v2/lumenarium_tests.cpp index a7f99f5..6379b7b 100644 --- a/src_v2/lumenarium_tests.cpp +++ b/src_v2/lumenarium_tests.cpp @@ -70,7 +70,12 @@ memory_tests() { // TestGroup("Platform Allocation") { - u8* base = platform_mem_reserve(GB(32)); + u64 size = GB(32); +#if defined(PLATFORM_wasm) + size = KB(4); +#endif + + u8* base = platform_mem_reserve(size); platform_mem_commit(base, KB(4)); base[4095] = 200; assert(base[4095] == 200); @@ -85,18 +90,54 @@ memory_tests() memory_allocator_tests(bump, false); allocator_destroy(bump); - Allocator* paged = paged_allocator_create_reserve(KB(32)); + Allocator* paged = paged_allocator_create_reserve(KB(32), KB(4)); memory_allocator_tests(paged, true); allocator_destroy(paged); } +enum test_flags +{ + TestNone = 0, + Test1 = 1, + Test2 = 2, + Test3 = 4, + Test4 = 8, +}; + internal void run_tests() { + scratch_get(scratch); + + // basic + + u8 b = TestNone; + assert(!has_flag(b, TestNone)); + assert(!has_flag(b, Test1)); + add_flag(b, Test1); + assert(has_flag(b, Test1)); + assert(!has_flag(b, Test2)); + add_flag(b, Test2); + assert(has_flag(b, Test1)); + assert(has_flag(b, Test2)); + assert(has_flag(b, Test1 | Test2)); + add_flag(b, Test4); + assert(has_flag(b, Test1)); + assert(has_flag(b, Test2)); + assert(has_flag(b, Test4)); + assert(has_flag(b, Test1 | Test2 | Test4)); + assert(!has_flag(b, Test3)); + rem_flag(b, Test2); + assert(has_flag(b, Test1)); + assert(!has_flag(b, Test2)); + assert(has_flag(b, Test4)); + assert(has_flag(b, Test1 | Test4)); + assert(!has_flag(b, Test3)); + // memory tests - u8* a0 = allocator_alloc_array(scratch, u8, 32); - u8* a1 = allocator_alloc_array(scratch, u8, 32); + u8* a0 = allocator_alloc_array(scratch.a, u8, 32); + u8* a1 = allocator_alloc_array(scratch.a, u8, 32); assert(a0 != a1); assert((a0 + 32) <= a1); @@ -113,7 +154,15 @@ run_tests() assert(a1[i] == (100 + i)); } + + assert(round_up_to_pow2(1) == 1); + assert(round_up_to_pow2(3) == 4); + assert(round_up_to_pow2(29) == 32); + assert(round_up_to_pow2(32) == 32); + assert(round_up_to_pow2(120) == 128); + memory_tests(); + bsp_tests(); #if defined(PLATFORM_wasm) // NOTE(PS): the tests below this point don't make sense on a web assembly @@ -123,21 +172,21 @@ run_tests() // testing strings and exe path - String exe_file_path = platform_get_exe_path(scratch); + String exe_file_path = platform_get_exe_path(scratch.a); 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); + String run_tree_path_nullterm = string_copy(run_tree_path, scratch.a); 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); + Platform_File_Info i = platform_file_get_info(f, scratch.a); - Data d0 = platform_file_read_all(f, scratch); + Data d0 = platform_file_read_all(f, scratch.a); assert(d0.size > 0); String s = lit_str("foooooooooobbbbbbaaaarrrrrr"); @@ -163,6 +212,4 @@ run_tests() } #endif - allocator_clear(scratch); - } \ No newline at end of file diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h index 8851563..988b636 100644 --- a/src_v2/lumenarium_types.h +++ b/src_v2/lumenarium_types.h @@ -37,6 +37,41 @@ typedef s64 b64; typedef float r32; typedef double r64; +#define u8_max 0xFF +#define u16_max 0xFFFF +#define u32_max 0xFFFFFFFF +#define u64_max 0xFFFFFFFFFFFFFFFF + +#define s8_max 127 +#define s16_max 32767 +#define s32_max 2147483647 +#define s64_max 9223372036854775807 + +#define s8_min -127 - 1 +#define s16_min -32767 - 1 +#define s32_min -2147483647 - 1 +#define s64_min -9223372036854775807 - 1 + +#define r32_max 3.402823466e+38f +#define r32_min -3.402823466e+38f +#define r32_smallest_positive 1.1754943508e-38f +#define r32_epsilon 5.96046448e-8f +#define r32_pi 3.14159265359f +#define r32_half_pi 1.5707963267f +#define r32_tau 6.28318530717f + +#define r64_max 1.79769313486231e+308 +#define r64_min -1.79769313486231e+308 +#define r64_smallest_positive 4.94065645841247e-324 +#define r64_epsilon 1.11022302462515650e-16 +#define r64_pi 3.14159265359 +#define r64_half_pi 1.5707963267 +#define r64_tau 6.28318530717 + +#define NanosToSeconds 1 / 10000000.0 +#define SecondsToNanos 10000000.0 + + #define get_byte(value, byte_index) ((value >> (8 * byte_index)) & 0xFF) struct Data @@ -54,12 +89,21 @@ data_create(u8* base, u64 size) return result; } +#define memory_zero_array(b,t,c) memory_zero((u8*)(b), sizeof(t) * (c)) internal void memory_zero(u8* base, u64 size); internal void memory_copy(u8* from, u8* to, u64 size); ////////////////////////////////////////////// // Math +#ifndef max +# define max(a,b) (a) > (b) ? (a) : (b) +#endif + +#ifndef min +# define min(a,b) (a) > (b) ? (b) : (a) +#endif + #define lerp(a,t,b) (a) + ((1.0f - (t)) * (b)) #define clamp(r0,v,r1) min((r1),max((r0),(v))) #define lerp_clamp(a,t,b) clamp((a),lerp((a),(t),(b)),(b)) @@ -247,6 +291,22 @@ hash_table_find(u32* ids, u32 cap, u32 value) return result; } +////////////////////////////////////////////// +// Math + +internal u32 +round_up_to_pow2(u32 v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + ////////////////////////////////////////////// // Vector Extensions @@ -254,6 +314,15 @@ hash_table_find(u32* ids, u32 cap, u32 value) #define v2_floor(v) v2{ floorf(v.x), floorf(v.y) } #define v3_floor(v) v3{ floorf(v.x), floorf(v.y), floorf(v.z) } +internal bool +rect2_contains(v2 min, v2 max, v2 point) +{ + return ( + min.x <= point.x && min.y <= point.y && + max.x >= point.x && max.y >= point.y + ); +} + ////////////////////////////////////////////// // Color Constants diff --git a/src_v2/platform/lumenarium_platform.h b/src_v2/platform/lumenarium_platform.h index 4f06e9c..fff914d 100644 --- a/src_v2/platform/lumenarium_platform.h +++ b/src_v2/platform/lumenarium_platform.h @@ -85,12 +85,12 @@ bool platform_pwd_set(String path); typedef u32 Platform_File_Async_Job_Flags; enum { - PlatformFileAsyncJob_Invalid = 0, - PlatformFileAsyncJob_Read = 1, - PlatformFileAsyncJob_Write = 2, - PlatformFileAsyncJob_InFlight = 4, - PlatformFileAsyncJob_Success = 8, - PlatformFileAsyncJob_Failed = 16, + PlatformFileAsyncJob_Invalid = 0, + PlatformFileAsyncJob_Read = 1 << 0, + PlatformFileAsyncJob_Write = 1 << 1, + PlatformFileAsyncJob_InFlight = 1 << 2, + PlatformFileAsyncJob_Success = 1 << 3, + PlatformFileAsyncJob_Failed = 1 << 4, }; struct Platform_File_Async_Job_Args diff --git a/src_v2/platform/lumenarium_platform_common_includes.h b/src_v2/platform/lumenarium_platform_common_includes.h index 1b6799a..f6e0496 100644 --- a/src_v2/platform/lumenarium_platform_common_includes.h +++ b/src_v2/platform/lumenarium_platform_common_includes.h @@ -17,6 +17,9 @@ #define STBTT_assert(x) assert(x) #include "../libs/stb_truetype.h" +#define STB_SPRINTF_IMPLEMENTATION +#include "../libs/stb_sprintf.h" + // NOTE(PS): only need the opengl extension headers // when running on a platform that is using opengl 3.3+ #if !defined(PLATFORM_wasm) diff --git a/src_v2/platform/wasm/lumenarium_first_wasm.cpp b/src_v2/platform/wasm/lumenarium_first_wasm.cpp index 8446aa6..6e60e26 100644 --- a/src_v2/platform/wasm/lumenarium_first_wasm.cpp +++ b/src_v2/platform/wasm/lumenarium_first_wasm.cpp @@ -9,6 +9,7 @@ #include "../lumenarium_platform_common_includes.h" #include "../../lumenarium_types.h" +#include "../../lumenarium_memory.h" #include "../lumenarium_platform.h" #include "../../lumenarium_first.cpp" diff --git a/src_v2/platform/wasm/lumenarium_wasm_file.cpp b/src_v2/platform/wasm/lumenarium_wasm_file.cpp index ef11ded..4cf4aad 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_file.cpp +++ b/src_v2/platform/wasm/lumenarium_wasm_file.cpp @@ -1,5 +1,6 @@ WASM_EXTERN u32 wasm_fetch(char* file_path, u32 file_path_len, u8* dest, u32 size); +WASM_EXTERN void wasm_platform_file_async_work_on_job(char* path, u32 path_len, u8* data, u32 data_len, bool read, bool write); Platform_File_Handle platform_file_open(String path, Platform_File_Access_Flags flags_access, Platform_File_Create_Flags flags_create) @@ -43,3 +44,13 @@ platform_pwd_set(String path) return false; } +void +platform_file_async_work_on_job(Platform_File_Async_Job* job) +{ + wasm_platform_file_async_work_on_job( + (char*)job->args.path.str, job->args.path.len, + job->args.data.base, job->args.data.size, + has_flag(job->args.flags, PlatformFileAsyncJob_Read), + has_flag(job->args.flags, PlatformFileAsyncJob_Write) + ); +} \ No newline at end of file diff --git a/src_v2/platform/wasm/lumenarium_wasm_imports.js b/src_v2/platform/wasm/lumenarium_wasm_imports.js index 29d372c..ff42920 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_imports.js +++ b/src_v2/platform/wasm/lumenarium_wasm_imports.js @@ -92,20 +92,69 @@ var lumenarium_wasm_imports = { }, wasm_mem_grow: (new_size) => { - let pages = new_size / WASM_PAGE_SIZE; + let new_size_ = new_size >>> 0; + let pages = new_size_ / WASM_PAGE_SIZE; let pages_rem = fract(pages); if (pages_rem > 0) pages = Math.floor(pages) + 1; let size_before = lumenarium_wasm_instance.exports.memory.buffer.byteLength; let old_page_count = lumenarium_wasm_instance.exports.memory.grow(pages); console.log("mem_grow\n", - "req size: ", new_size, "\n", + "req size: ", new_size_, "\n", "old size: ", (old_page_count * WASM_PAGE_SIZE), "\n", "old size: ", size_before, "\n", "grew by: ", (pages * WASM_PAGE_SIZE), "\n", "new size: ", lumenarium_wasm_instance.exports.memory.buffer.byteLength, ""); }, + malloc: (size) => { + + }, + + free: (base) => { + + }, + + sin: Math.sin, + sinf: Math.sin, + cos: Math.cos, + cosf: Math.cos, + tan: Math.tan, + tanf: Math.tan, + asin: Math.asin, + asinf: Math.asin, + acos: Math.acos, + acosf: Math.acos, + atan: Math.atan, + atanf: Math.atan, + pow: Math.pow, + powf: Math.pow, + fmodf: (f,d) => { return f % d; }, + strlen: (ptr) => { + let len = 0; + let len_checked = 0; + let len_to_check = 256; + let found_end = false; + while (true) + { + let string = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, len_checked); + for (let i = len_checked; i < len_to_check; i++) + { + if (string[i] == 0) + { + len = i; + break; + } + } + len_checked *= 2; + } + return len_checked; + }, + + wasm_platform_file_async_work_on_job: (path, path_len, data, data_size, read, write) => { + + }, + wasm_performance_now: () => { return performance.now(); }, @@ -260,6 +309,14 @@ function glBufferData(target, size, ptr, usage) return r; } +function glBufferSubData(target, offset, size, ptr) +{ + let data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, size); + const r = gl.bufferSubData(target, offset, data, 0, size); + glErrorReport(arguments); + return r; +} + function glCreateShader(kind) { let shader = gl.createShader(kind); @@ -395,6 +452,17 @@ function glTexSubImage2D(target, level, offsetx, offsety, width, height, format, return r; } +function glGetUniformLocation(program, name, name_len) +{ + // TODO(PS): complete + return 0; +} + +function glUniformMatrix4fv() +{ + // TODO(PS): +} + function webgl_add_imports (canvas_selector, imports) { const canvas = document.querySelector(canvas_selector); if (!canvas) return console.error("no canvas"); @@ -414,6 +482,7 @@ function webgl_add_imports (canvas_selector, imports) { imports.glCreateBuffer = glCreateBuffer; imports.glBindBuffer = glBindBuffer; imports.glBufferData = glBufferData; + imports.glBufferSubData = glBufferSubData; imports.glCreateShader = glCreateShader; imports.glShaderSource = glShaderSource; imports.glCompileShader = glCompileShader; @@ -431,5 +500,8 @@ function webgl_add_imports (canvas_selector, imports) { imports.glTexImage2D = glTexImage2D; imports.glTexSubImage2D = glTexSubImage2D; imports.glBindTexture = glBindTexture; + imports.glGetUniformLocation = glGetUniformLocation; + imports.glUniformMatrix4fv = glUniformMatrix4fv; + return imports; } \ No newline at end of file diff --git a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp index 55a8efd..e17ff7f 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp +++ b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp @@ -48,6 +48,7 @@ typedef unsigned int GLsizei; #define GL_RGB 0x1907 #define GL_RGBA 0x1908 #define GL_UNSIGNED_BYTE 0x1401 +#define GL_CLAMP_TO_EDGE 0x812F WASM_EXTERN bool glHadError(); WASM_EXTERN void glClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a); @@ -61,6 +62,7 @@ WASM_EXTERN void glClear(GLuint i); WASM_EXTERN GLuint glCreateBuffer(); WASM_EXTERN void glBindBuffer(GLenum buffer_kind, GLuint buffer_id); WASM_EXTERN void glBufferData(GLenum target, size_t size, const void* data, GLenum usage); +WASM_EXTERN void glBufferSubData(GLenum target, size_t offset, size_t size, const void* data); WASM_EXTERN GLuint glCreateShader(GLenum kind); WASM_EXTERN GLuint glShaderSource(GLuint shader_id, char* shader_code, GLuint shader_code_len); WASM_EXTERN void glCompileShader(GLuint shader_id); @@ -70,6 +72,7 @@ WASM_EXTERN void glLinkProgram(GLuint program); WASM_EXTERN void glUseProgram(GLuint program); WASM_EXTERN GLuint glGetAttribLocation(GLuint program, const char* name, GLuint name_len); WASM_EXTERN GLuint glGetUniformLocation(GLuint program, const char* name, u32 len); +WASM_EXTERN void glUniformMatrix4fv(GLuint uniform, GLuint count, GLenum normalize, GLfloat* elements); WASM_EXTERN void glVertexAttribPointer(GLuint attr, GLuint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); WASM_EXTERN void glEnableVertexAttribArray(GLuint index); WASM_EXTERN void glDrawElements(GLenum type, GLuint count, GLenum ele_type, void* indices); @@ -109,6 +112,53 @@ platform_geometry_buffer_create( return result; } +void +platform_geometry_buffer_update( + Platform_Geometry_Buffer* buffer, + r32* verts, + u32 verts_offset, + u32 verts_len, + u32* indices, + u32 indices_offset, + u32 indices_len + ){ + glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer_id_vertices); + if (verts_len > buffer->vertices_len) + { + // NOTE(PS): this is because we're going to delete the old buffer and + // create a new one. In order to do that and not lose data, the update + // function needs to have been passed all the buffer's data + assert(verts_offset == 0); + glBufferData( + GL_ARRAY_BUFFER, verts_len * sizeof(r32), (void*)verts, GL_STATIC_DRAW + ); + } + else + { + glBufferSubData( + GL_ARRAY_BUFFER, verts_offset * sizeof(r32), verts_len * sizeof(r32), (void*)verts + ); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->buffer_id_indices); + if (indices_len > buffer->indices_len) + { + // NOTE(PS): this is because we're going to delete the old buffer and + // create a new one. In order to do that and not lose data, the update + // function needs to have been passed all the buffer's data + assert(indices_offset == 0); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, indices_len * sizeof(u32), (void*)indices, GL_STATIC_DRAW + ); + } + else + { + glBufferSubData( + GL_ELEMENT_ARRAY_BUFFER, indices_offset * sizeof(u32), indices_len * sizeof(u32), (void*)indices + ); + } +} + Platform_Shader platform_shader_create( String code_vert, String code_frag, String* attrs, GLuint attrs_len, String* uniforms, GLuint uniforms_len @@ -174,7 +224,7 @@ platform_shader_bind(Platform_Shader shader) void platform_set_uniform(Platform_Shader shader, u32 index, m44 u) { - glUniformMatrix4fv(shader.uniforms[index], 1, GL_FALSE, &u.Elements[0]); + glUniformMatrix4fv(shader.uniforms[index], 1, GL_FALSE, (r32*)u.Elements); } void diff --git a/src_v2/platform/win32/lumenarium_first_win32.cpp b/src_v2/platform/win32/lumenarium_first_win32.cpp index 9dc68a8..df682e2 100644 --- a/src_v2/platform/win32/lumenarium_first_win32.cpp +++ b/src_v2/platform/win32/lumenarium_first_win32.cpp @@ -205,8 +205,8 @@ WinMain( &win32_main_window, hInstance, "Lumenariumtest0", - 1600, - 900, + 1400, + 800, win32_window_event_handler ); win32_window_update_dim(&win32_main_window); diff --git a/src_v2/platform/win32/lumenarium_win32_graphics.cpp b/src_v2/platform/win32/lumenarium_win32_graphics.cpp index cb02e6b..354bf93 100644 --- a/src_v2/platform/win32/lumenarium_win32_graphics.cpp +++ b/src_v2/platform/win32/lumenarium_win32_graphics.cpp @@ -57,15 +57,16 @@ platform_geometry_buffer_create( return result; } -void platform_geometry_buffer_update( - Platform_Geometry_Buffer* buffer, - r32* verts, - u32 verts_offset, - u32 verts_len, - u32* indices, - u32 indices_offset, - u32 indices_len - ){ +void +platform_geometry_buffer_update( + Platform_Geometry_Buffer* buffer, + r32* verts, + u32 verts_offset, + u32 verts_len, + u32* indices, + u32 indices_offset, + u32 indices_len + ){ gl.glBindVertexArray(buffer->buffer_id_vao); gl.glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer_id_vertices); win32_gl_no_error(); diff --git a/src_v2/user_space/user_space_incenter.cpp b/src_v2/user_space/user_space_incenter.cpp index 124f476..8a894c2 100644 --- a/src_v2/user_space/user_space_incenter.cpp +++ b/src_v2/user_space/user_space_incenter.cpp @@ -7,33 +7,53 @@ incenter_get_init_desc() return result; } +#define INCENTER_METER 1.0f +#define INCENTER_FOOT 0.3048f +#define INCENTER_METERS(count) (count) * INCENTER_METER +#define INCENTER_FEET(count) (count) * INCENTER_FOOT +#define INCENTER_PER_METER(count) INCENTER_METER / (r32)(count) + internal void incenter_init(App_State* state) { // create a fake sculpture - Assembly_Handle ah = assembly_add(&state->assemblies, lit_str("test"), 3000, 100); + Assembly_Handle ah = assembly_add(&state->assemblies, lit_str("test"), 5043, 41); - r32 scale = 1; + scratch_get(scratch); + Allocator* s = scratch.a; - // strips - for (int strip_x = 0; strip_x < 10; strip_x++) + v3 start_p = v3{0, 0, 0}; + + Assembly_Strip* vertical_strip = assembly_add_strip(&state->assemblies, ah, 123); + assembly_strip_create_leds( + &state->assemblies, + ah, + vertical_strip, + start_p, + v3{0, INCENTER_FEET(-6.5f), 0}, + 123 + ); + + r32 radius = INCENTER_FEET(10); + + Random_Series rand = random_series_create(hash_djb2_to_u32("slkdjfalksdjf")); + for (u32 i = 0; i < 40; i++) { - for (int strip_y = 0; strip_y < 10; strip_y++) - { - if (strip_x == 5 && strip_y == 7) - { - int x= 5; - } - Assembly_Strip* strip = assembly_add_strip(&state->assemblies, ah, 30); - - // leds go up - for (int led_z = 0; led_z < 30; led_z++) - { - v4 pos = { strip_x * scale, strip_y * scale, led_z * scale, 1 }; - assembly_add_led(&state->assemblies, ah, strip, pos); - } - } + Assembly_Strip* strip = assembly_add_strip(&state->assemblies, ah, 123); + + r32 theta = random_series_next_unilateral(&rand) * r32_tau; + r32 phi = random_series_next_unilateral(&rand) * r32_tau; + + // spherical to cartesian conversion + v3 end_p = { + radius * sinf(phi) * cosf(theta), + radius * sinf(phi) * sinf(theta), + radius * cosf(phi) + }; + assembly_strip_create_leds(&state->assemblies, ah, strip, start_p, end_p, 123); } + + ed_sculpture_updated(state, 5, 0.025f); } internal void From f6ab76ee2ac8b91249391f832a88afadd19d9678 Mon Sep 17 00:00:00 2001 From: PS Date: Tue, 12 Apr 2022 08:01:17 +0200 Subject: [PATCH 123/151] scratch memory improvements --- .../intel/debug/lumenarium_first_win32.obj | Bin 595328 -> 595431 bytes src_v2/lumenarium_first.cpp | 4 ++-- src_v2/lumenarium_first.h | 6 ++---- src_v2/lumenarium_memory.cpp | 4 +++- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj index 6284fe59da7efa8e6fc798566618ac00b32ca5e6..314cb7c455c52224a6e50c223fe398217f0b01ed 100644 GIT binary patch delta 39221 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

K zkj^pi4_Npe8ovYl18MM~ebwNP>2%iVbPj_xYGFw_N3HO~*GT7X;tpAHYqT6S$T5^g z4rtFCIqFE~?srJ%0QkEs{8Eiy3jVG%_|P^R`~jWLES=6iu=*`5NoT(mzV908+(O(Q zE3QY&;X#g`G;%YX;|C#+HWH>tz4OTpW+ z%bz(+T|b=ME@g8Dm9$I$VCp*GO}i92NSh?@rcFAL<+Mp1UZ+j!v}8Y*<=7|Z>acCJ z?5Q>Z^t(j<6Lry02l*oXcWRq-fHr9iImeRQq@b4bFO8hi*SC^?ZI%p@_ieSYTvJx9 zI`0oyZ3g(N&1fd>dMoZkmeY1kApd$Tzuk60yYi1toenqG;^TJaXC0dZtR@RfxHLBeUAo5eNh=V28XE|`=}fHlIzARx^ArU z=>A&jD`y{sM|omjgz59!iPPX-w1~1;6drcyp&{xwVc#*_Zo*AnN4P;J+;#EZMOewB zOCIvdldx^1EU(nW?l5J4sYdLCSa>%wE1QGx=dJMP7CQCz+()oEYLdo9j?GaMyYdfC zy$zjHSlAp39o{ow%~@E&d(H|!qj?W$-X{q^ZG}Ioc^`%MbQ<2!-Zs1^KSDcNg3=zHaz;k^Tc#`bWVUv#^B!SWWCj6Moc6KjSZz z{vqP*v*I*r{*Cb8mxe#IXAItuPJfY3{~%bqEiB=`+X_EurQh)HC(eKsXNBg!0{#PO z_(OZj@Gm0${Z{(7fz@YW3ID#D*aj1Ro0a~>I{jV5*(D{G^{Eot~ed(hx@ z>GYpi;OKq_Se+J@@b9$3cUb8+{9B3BX2qG({HNgGmWDsH`wagRYop;-EB))iYO%0{ ze@jhlnF+sM^WUZGUL)a~tng!+_ZYmJ((s1nGFXi|?R`4!bzn7ESi-x(3SXyr4{F|P z2w!Q1-==wQgLh>b-q3zNrQ4i7(!It?cNtja7MAcXuZf*D;mZ=~j@P>);*?l%S~UL_ z_?M*N5ABG-E7EB%*J&>RYlVd+{8w1v3leFM^Uotrz7?lT^Dl#cej5JJzG3*6lm0v_ z{W)OeT3EtAw2AInaL3@SkDt#2jbC%lJ~- z{WDZ9U65&xR_@x)KEl6#qDJn}xvmI)J1?3Pr!0{whS=y7|dADcq#nGE5R!uDMw z>u!Q$ub!^0h-<3#ryp8E2PQMkWK=Utve}5YO&~_NSKArXp9w+TRU~RLo zg#R`xd`}|nasHje>9XRS_c(R@d=2L%rQr{4i{XEvCMtU;;&nU>R)>Wp{5z^+K@&b~ zrQftMEyQWH;vCofkHfz;4S#448N3#q{$ZW|X0X;A13_`R{CqesCyaq;J+pff1w$?a-IHW zo&Hj=$}BA5UuK0bwbF0+uOLp56{kt_Z-RdjaZ}rPXqSGZ>v1#bUty(xDOd#-mhdmA zj-55(mumh!y6)u?KF5kXC z8RE=Yai;Zt)M@sH&ZXfGt>56yD7*^JA$98gG+1XWEa88~3O}v+llD0ODdL>4;vCTY z55WIK8vfA!!0gbvdN)X%-Bz4R&A$@wZY;m!-gSoOE3twdFsfxWJ z7~&_|f=sL>mAyyGX6PW!5#n@Oan3n&_MvmEJLuFhrTUDZzXqnPjgovDxh zv&-S*-=b}jR`B;|wGy1GNyaiK1aT?sa8iezs@+$QSR*!^JFWWa{rXRy1#t_0f$HvTZV8$snq8Q8ai zU2b7_YV1z1%QIj@e-i9K*VPg#539VCxkg`5G%e14tbE&S@>Cw65K`~vVR zvG}dm{MN&-MDt745$J0LuMS>CRcc;W?CxT(o`Z&mn(Hpre3oiH1s0zg&8G%F1sV82 z|1IHD3ZJE|fDipH!SBb{yoLP8XFX>w zb%=Rydq2kxx!X~~^sK$UyB!H9Ya}1C_nPK@gta%mH}^6&`3~!Vif7j9uuf@@mo-x_vo> z;Zsdn-$;H5FKO2^$sz00B>diAUHu)_UXs?e+{f&mzXxD@etcb&Uib91>~-&s1pDx5 z$R2?E*aOh-iJg@_0PZF14e+SFR}Hk&;a&QFHGA*|_5aiS->v`mau3Xq{$I$QDEst( z?jr~f>;HiTk?^Sg@8+JIG5GG~?u7%MBwY_4*Y9Sn)j|Dk)><9*IBTtbhxNm$K$NE+U7~Dwd&1D>{Ej-{^9oin%Jhknpk)nzeZ0iJmGQHTQzxN>yEf1o11yM zy^+o9c~&ooaMoIEUGKuk=2rQh71`V--?Jl|!}8r1+1%lA)@OBkV%OTAki-6jaF-|c z7V8(@{D%coEv)$Yp_LX6K2T{-Lii=)zROgZ@L&GG#xduImRmSo8m9}K@(l7o zKWyW$K3cW+r4~+`#%TkmGy@Lw5gUhgUadhYvT&L;PBS<~8E~KvS~xkJW6J)asOn1# zEUY?>RRny zu%JJdid6_!1gtCzD_>*fgO!y53wi@s0rc`L=QOXp%N0FE8|88>iFg)r&I;c`&D8&c z#5a1HrE#;s3F+)u*xgz?#T_1^th}3Q&IjT?yS6oo|I6Vs0u)=esm! z7npoYtMkx5XXN957(aKsoj+*F*Qv2O!8({hKIo5ISaF>nvvArpP8&F58E~LC**He$ zhb^3DjnfRya0VRcRW^>%`5_CZPUF;pGn4@bdZ~qzr1OIoR=LJ12Wv0`7WCUvv5d|S zSXf0Gs|c)t3|K->#WFhIXJO@QtbDNgGGIaf<=^Y}0XNj7W5y2{M>dYB7tI#VL5*_|oaPKT(6`t)rd~8!IKvue7@WooIM9O@PEx(7v#psTbuIR+q-=0;@a&7WDU}VwrkTYGJi$tTwPp zGhji#Jr&E;iy{lFSz|SWRg?h>x(}=Xy6NASTrYld44rRH(fK-!SqCQH(&{|)Ul3R6 zJpaSRmd+Pg^3`ap8n6m7$Orwjg%#KNd<&;sW@6ERB@~))mI_Y5XI8=zp4uWpw_6g>^xn zOL+m`?}ZFl(El(M%jo<$3u{JW&46_-0~YkoR4k+OGZxkfjdcR7nG9IaH>6@2ou9U_ zCNpg#gu0NwNtC+qwQ=%Kb0ooD_UM!VO49j*7FL(W>H_Ov1}y0Rl8R+?e$2vZ(^zd_jb*@s{z@vA z(fMHut65_;gEgE13;N%tVi}zuvasqjRvlPF8L*)5OT{ueKWJf7^|F?w|*ZDpR zCtu^_gVUD*2l^*$9Ha9+7EYGN$pWV*0}k|B8^`E;mxXgd`|>YTVRvT0fqtKblce(< z7S@c$ngOdL0~YjxR4k+OZ5GxEjdcR7whUO%7o}nuoo}(QCNw-S0}k{*wQ-Ej zms>bp8m9}K@(ei8`)nMe^Q9I}o5pDar!)f&^#5VuB=SVrfw zEUbKul@C@{1}x~efE7SD{RfhD{>|^pT~{^CXVk>(dBm0KtXl3)=e)r@k%RB{S+)7H zC#kDO&4I{SL_X#gc*cC|^pp7n_0D{PdNQA&p8u(z%qOUK<`Z~Evzbd+#ykS^iSd2# zq2ut(Fb{bLGF|0fqsvb(;+z(b%tOY{3>fj=!5J;gLoydB=K}2Ce#b>Q>vX@@I+ua@ zHnF*6{%gdW;ampje@p(1c;yVa$1W~^s%m^s?QP$7FmJmkcRtwX%8k6N&t-X;b6LEf zNq^o6^gSALPwl6{`t-%Z&vFOt%EE$EA9IZh_AF;h-wdy<&<^*Sd zgY!T>3Fa*4I%I>lve*^9{Pe?|0pfMe0NL(AcYK^Ln4&xAN|NrNGsVm??%$r!9a-Zg z=g!=xWJY(MsEP#_y1psrfH=BwyAuaJjL#8B9Q5ceiKD`O=3+7NB>z=;+(X_Ka(<5Y z_LMk;({X0W`?-76Iu9of^X5ulm^&94)1BS2l(>@b>Wq&kT(R5L94~T;4v?oRK6BA3 zewuSeatv?I3*iiqSUngjuGf**K4(MnMb6Q7JBdu=YdaiZ^y~%Sm*>le1M@j?mMEMZ0h9c<_`5PdFQ!>J1Tm( zBZ;|N|2Cm{Bb)jp4EHYe%RA2j`OZ1kyLieyOM~*AyOVaycb-G?-4~JjE|wqReCuJJ z)i*}`qw>!A)?@O1Q^bFOCv)WfgYwLa_zw%tyCRz=^c_q`uj3A;$;@{!r9D5oI&rU3 z^7+%wdC9BDlbj7hQ*u^z4rij8^QlYrI_IH3Okc42h&eOcIa9kI+P$Zpd&VB&Zl^~$ zOIyy=D_!W0e6eb1YF)ve^Ixo*KEJNusycJ;-n-qAm*o3L+>v#hwY?5r>zKEGO>ny1 zkx_ZR;M{q?I_k_%ullh%w>P#b=A4tAB>SzN^k-^Uu$};{Ri9Gdn{u9+=;S?G67sN) zZK#R0p537<=)*C$b7uB&%ddmZbC-Cm6kQYj2~DCy$2haUg0*Cq+_4hYUu_VqLiOw} zE>`_tMd%V?+rSHWIfFX|kG0ZEMRz;V+cwUN-^}^0oGt5urtpOhFPT@S@2sc~aAy|( z!!77*YYJ9tb?gQsTMM!gx9V2z#~$^Jt9!D?JmcxsO<(#R_QF^V;cCX^PD#&_s&Vfv zRpaMga?Z^c{{CH_@xY*GT%GySXz?A^{;6U7Q(e_gUuB;M2Tjgxxd0E9XN`n0bEyM5 z?f`LxZmmInR?bs^CUO6RxRMtlo7~BT4}VR9_+r+Iz2GUx^`H24Ylpkj#`dcW!GD{xO$Fk~J zKXLHi5{~jCo`*PI!nfy=Z+%YM{56zcoi4v33!_=f)r?$(ohKJGk*m+hRYVw5ew8|I zC2@tGnkHxx_j4vLWhVLUud5kv@1y)`YQ_VUpP%xR^N*=JPTuG1ykBa`xkmF^1Fvi7 zS%cxV)RMDE$1NhR&{O4vCUSnr#LY)ec&NP3(|Mmy-sf8KNZ#jihYxWA`Q&{bc~3l( z_cu!3_v^gB#s zzDL>KuEMPGoKKB3h0l4TCD_Tnt?E35ZVCG(i96uvM&O*j2akK(;^{rA_YAVI_8R}?{&V* zJ!{g2$7LRJkB6o#USpcq7`&!3@PfYC$W0yd4{;A3e5-ucJ$UR9cu3Z8$sPg1q)k8c zdJ9L&+u!4M(tp&Fsb6FDgLO26Owh|EeUxW#7c%uA6ZqAXwd=jyQ8?!vmo??|8?nG# z?fA~Rk*4L8b3J7qqRdyw|7S*;?&SF-^}M4QHG17|D)4s@5n8KRyd(*}G@aeZyp z7|metJ#Ctx|JTd9?cjerO~Y22nlw%mIKvraf&On6j`A&;bPZWpbsDQqWXgaA{g}Z* z2gArE=_v z-57N+a)_tyw_y(n|LaNi1W0YxMQN3J0=cLKI_>hK)hAps&Ni+J6rwa2F6 zwQO}~UZ}$=_ZeMwGvIWjDLZKD{x4)uV}dX;+xu1qbo>G0vp;2C*+WzJe@XmS;%7I%T2C@;)g`=YDlxu}77qDo4(#9tK0gHj;kT{*&%igjM&&FcvH! zyp)}!-`7JK&Um9c+j^TqXS`9tmOU}WPjSD^8Y}%hTK*p7U!&zu?Z2VFAo5EY9~PXd zSnJ57W) zX<9{`9MXhdR1^mkj?zhWwmJQSD(6xcdrYk~sj&w8c|>ONC{&8BlyHBo)crNI0gHK7 zy2e8*QsS&YH$|pO30E~Pa|Xsnu3bc*J3%>t=U?F(=ME{yM$We|Y+pQK=jj48H|;=+ zkt?74Hf?W#E{6ijLFlP%2sDZNF%uUoe^J$VU`5q<+o(U3@N^v0c;?JM_k7FE;VNdvh=TM+x_oeg^dOQT?oTo*heEk^kh58vHQP&4v2T zBK#U5;@WcvBQEYNLIyX!xEqj5_T%`G%}u*@>4>A}K72o_J;m3fp7?syvs*u3&`;Vd zMgOsW#`F`Pj-unkQBQn0JfqnmkF<~7_zSta2pwS$ZYVeNe1Z@8QuZ~)?{{Y}k@5*U zzFqAPW^N&_2h;=E>-5*8NuBYGU-y*-_=A~0u>G?#50MlHKky!jLpbrzDu1x}(8*KV zhpzmp_;CNkgvCc*ys%20ajfw1<0}6xeq0;>6#m-X#5s#kb{7BbN_@BW-lU{^pYVr2 zIQ#UjpVJQpsjsqUXN0@Vvd0S|awn7W$-d3Kf$YDt^EB9~-_8CR+6cMJDL~t&!Wq9T z?ZyuB$wk@sBa@%^U>W_{Qt3OmtD_*pT^-P+eb_|1vbl)%rGOvpYke>J=5g-wYo`wV z7yZwZIj&d-cXo_%XU92oypenqU*WoHN7vP!m|VFJDrfX9?(Mkond$SJxVJ;rTxk!PL8CF z`$WN2{d`K@*dhD$_#P(g{)wTf{Sy=C_fJgobMBVPKJAL%3+a`)Ho~FLNqHY?tU|BT z@6-DP@+Fn8_ttp%2F`dA<-3r5STi1{e4qM2Bsk|u;t}M`IaQ9Pe9w7e{#;EnWqV%l zk$V5usoSM&d6#p^f+zJJscCzUR93?04n25|H6nBDlbT^K&@{gR5Bupo_{&`p|1l4C znme*-m&duwYmoO|eDAx3j=z0K-g)lx#5N5J4WIj{(0Go?`@+bk1AIFuH2xnJS{8nD zk8{@bQNE!se}%q?4y;CB#J2HZ1GG+hVyn;_(FG}c*(WAzLvTDpxn_HqQ5TX3u;y``KN^(EEI!@AG{B(6idJpYxvg%$zy%>&$$9 zp3lmq>BLT-&Q9+6l5{KY=DEAmancK|XC36eaQdNXIPA`*ADvs0?w?2Vm-URyWzNWm+~0>IGyT8S1uHs#SGs@huKYgyl44x~ z%kuE|;mF@9SbPSUgO%~F`ftj+>ZQDpne~++CvO)N^8%Pd%FmC9yw+keXA;cqau*Qw z8Rx#lzS6)O6H~(87&&zub#de~lB^kMoF@TMPmOXa*BKl%O{m91f|NqQ)lSk5) zd&?5Y6P!!Txex7(=JgDD9Vn7l=#vo9{?T)pqv?nvf2;X*urybCUA z#(T#?W`E#-q$_gL^=xfI^EW~M_|4dwzb=vgzD=L-CPox@WO1I`3-KI)C;lPlj<#XN z8wRhX2v6i&gy(?r#AmoX%a$$hG;O5GDTkQi$G~qYf{*;?{d{G8p%cEmLo-qD;PonA zFL?Du@Q}9)-%jO=uX6eBTI%Cl>)=NeKLUPj5q#tgh4|LP_kQ?RJ9sUM*8*O35j^BK z3g3F=i!T-%xcD1S)`%ULwkh_u?Jn*)4OAts-qf0NcCt>>s3VQaB55H1kE676aHL(v zuPjZKU5^i%UrIdq-KnzRo15cv{D0-{RANrPmsdPr8+cdz>IYlzc~hXpa1g)1UZ?n| z%o^h*h|nkpub%r|5NaymzfJ)<(XqyzjG&k=UlmR7r%0WeOU1;mmGZYE0@|c&j_Cj z?U?{RW!3V>dR|0#Wl5kVik|q%>)(vez-J0)W8-Ye31WI4xbf< zkK~uJ#_4a7e}gY|ew;cv;o$F4{5{|^&nxIFkw=BM)G>2Z?nX*+pLNRkcj6lwupPNW z&hu-dia81<>lXzvk#7*pfsT~Cx4waTmOBb=lRG{n9Vtg*DjJr9ebB*-(<^Y zih2AVrw!}`Z^FT=SG;=gCW_!8Ujkl~y)d)4plx8ZCh*zE0$DqjdR<9gynKvkKDLn$ z$}Blbx?@hdm6~oP>5dgi7x{&>VvdrJ5howSJLKFIcOjH*%NHVymx|yaKMh_qNj|pm z40R!6hp}ky|GH}}Rw`Dw{?Yf%(bK3Pr2k)rz zJqq8AMff70CcV6NEq>Ja)$)pqkKjkecBb%kr#N@sZ}+?JP6b}yR2%%~z%O+LZy64} znf)f>HM}-)P}4t1`sfyH6UcwtmOuYX9EaTtOu}ISzb1=M97oPwc+;B3G--4f$s_Vd zZ5r@5F&Ly}VlN)f&O1!oi)fv6I%Ozz(upsy`Vrc#jN8`ntc=^*e9yLVhL30EY>v4b z&l$I^LoVf)*q`;oUfcj}GTDd?Gqy=KY5ag2D>0;b93qeCnsRjaaFEG7LhwyJHB%0D zyw$C`-RO$kKLo~)UPJbKm1M7$%Xe54saa9R@ z|LeZQylN(!$7mg~8}%ByQSD%~D6bZH@oXM1WWwtv%d6VSQ?2UOqAPNLo{))dnd(OC zBWdB&Ul;M_RmKmNQiuJFcPboSHHuvWc7|_=HQObJB@pIHh=}z~lFVc*qli zC%gpXq!Y(*CXnTN7OoG&b=Ki}La|SPJzIn;^039Gypm`6{t-Cu(Kvxq4&JolO@nu; z2p;k~v~1;_wRyI-JD9%Fu{AR$JMN_6#x)!-k_PfOXd1HKPdq7n+0WdawYj&+yTGKZ z$dAE4;^2>J9!AN-ks|oW|NW1(y%fsBP=P!ga?(g@8cEVPR3r`L=QRzJ2QaxGmiRTF z4Nhyl&N%oR6@Mf6Gez)`pA`IQY*6YGTvH}zSflMDofhI!rX0*p#q0!gst6|XKLb!8+jpCIu02jV&6-ZLa4+tU_X&5Y!#!}ZeN4UT z)}t$OzduGM{&=_5rSDCWhUsIfG)_R`10-f~!r|n`2cVOOi%f7oW^pGRPL;~35>6ub zb3!J%ThQ%i&ppbXyNprn{@#WqjEN3uA9f{x-_-rRF^8WUgTS+SX&{p{1}wiZhu^&J z$<2oun-<`QOmshpuBq3<)a%u7y8jCMdja{rcej4;rGA&OZpQj>Yu8ttx*lw8mYW(Xt$JIGtDA^WdVBhl@;b zgBEwl;dDxMPoXPvKPP0OJ9mV-K0}*3&0a2ewywV*dm%?6rhP<@P}fJ5U(DfmQqwp| z8a$hq1~N(Gd!%97N6g`OSalDhD{?bn8)nVH1?1Nznl8=mpSqQNke=i zc~1(??r=KHUcBttCEMsP_aqMF0Ulo{q7McAXW$)k_z9&vaGL86W+2de+u(fe}VA6a(oc(O*513dd z*&AtKO+CS0OZoi^dm<(uq$9q618LZpn8x>EYn4u(>NQXGNy!UF<)<$h@k* zl)4XvGyeNxzY3REcgP*6OF3&7oHY8nm&|*ay>~mC*b3q-+;evF?(EfaMzh3uceZmc zyPU!61z*m$@m~9vr9Ro?YL)jx^WN-~7aQ>c@f1#M#KpH}%m1afhEsBbatqWwr?6XT; z$7R;yha$+>H~$0n%}sp8s%(74z3tika}#IL88CN`u3^7?4g2LA*e{p8^?P~td&D4_ z|9O7diYHGm+a$lwE+hbSZBZVHE}pTvo!TxtX_q4Rw@YNA`@HBjquWV&Hc^fnvHeqwD^3z;dV+Y< zDszz|>3*lxaWZCG#GTC3cVF8ldQZo#khQKzI4 zIL6*Ad%N37v!AqL_y!}#XMId~z@eY#qHUa;?Ke1E6X3}G(&hL()21i0^0LqAUNL*U zTLcdsiC-D{KddX!S4nw9tFhl2xQV|j=EHe5uWXP>*$7WluC-3Ny8ce&{&Gboy2Dl% ztfZ7}4YsqxdlspUJ+2tS@&5=u_a5WV{gXVXjXZ+ce*{Y9XHPj&@`FTh~yRh2nkiV_&0h zH2%3w@(GsfpDV>b^ZT{>=dyjKE>rjp?|f`x0?Z=uK=+g8tIx?@-XZSorVn(^%gcFZ z<10(>A=fRG+kp=CzxY#$A#>|(5TAbz+58{jBTGzdQtK;^M4l#%0%v_o7W^Z=qaWWO z{_t@*^ZWJNCgak6#UJ)xH!1OCZl2UXdT0B^&zaw?Kg6#Ye@MUgBe+Dx&-F?jsz1bM zJs${s{UUK~Wx?QrKSVBdAa=I78yll-54Q2QF_1}YXFQ2-yo^5_!Y}@>l4Uc$gkN-H z%Z8}?zr-fDQ122ubo~t*c5TLIa@X>qUD4UO!O^U*N+ts-&kl#w4u`PAdM!`C9U`A0 zAHprc|6+H8$#6>I->~ykvTvb&;;z411n<`c4}IC+J%>%Vt_*xO90En+! zFLA#)<2G5Nr)~bK%ZE1EbsxO&FJ8WyG+#}N=PPD$s?djzx3^1MO;k8|wTf2@UIo0x zU--)jc{g~by@F?BN}ebFePt-{o|%cOPnb3qlzI=h#!IOO*)J{y6CYdVj3WZr;(cI= zOAX`SBJh zTfWD)(U!0|6QjwzeW5Ld7TOYW8G{TC)0W6nGR7b6OZvR^(w1oR>lst^eVSZ>CadF-_g8N1`^iB0_sC{Z!+Qq%^4onVmR@?LirtiwevYWLp ze1d1I?wUcT+LpWLgG|;8{^~IGPM#9@5>IzT?Q#Sg61l%0LngZ4w7Ol{ae??485^`S zCXl@`!>PsLohX9>1VAb2%i9a zWXvIJnu9zeeVydn%X3WeW8mBJ^0!6gqHD?vY{{=#gWTxM_4^d34;-6EKMwK_TO8rK zF`P~!YiFM&P{tl;kAv5%c)j59Y(XD^{DT$`PSGClkjdJCna|e+7V_Dxc-`RD6~RNk z*5cWG%Dlcw@$jKuxwR=?8+iE4g8Y!*YVqI|ZPNV8dW7V+R&iZl+o-sW;ELZX)As&UHDZ3}P`+qv>}t=kIHvt#8-)vZKVcvjZDp4xHiv;R~|T*7mko;}d{WY6>4e{VP_XMm~G_g)wH z?9)4b;bpl~DEQ{5x}Vzdk!N{d3V(gi%DK{~p1bwgoO_CP=siU{7Qd(HihmulJo)6it`TH12>7^;PVzfiOKWE;lZ~|%KbWK z?C`?7CgWA1oUuy0Nx$25iE90B$Kf@h>^S@_kDa~+-ci2CYa*=u>%BX~?s;Q}?MoKM z4#*|`aj+_q*<8t57cqOuY2uG+^*t#wddJPdV0K;NP%yLQF#pRq;t2mA6?tfK%W;wM zc>>ulqX94o$59vEpKEdyg>KNryGQZX_%G>z=f)guoM&ql_5MMP@APxiBc6{sf z*YK^BUE%`ISijtiS83t_Yg5SS3A;m&k&fUy+UYOk{Jl9_ zJmqkUD1HQdbo20$3I3xNpBOfeTZ`(ppeu4eH)Nvw%T|}TzG%f=>EvO!mEM((h3?Ad zw#VVttoY5~qnn41Oz=Ns@%K2~>Q%QMU6K2_Arsy8=tk$;vDv#)a!xT>bysSTcQUV` zziA|Hb2Vp&&3KCW=uJGEJl~#4P$p7O8`@c;c+AxK*6f*WhkKo-Uq||jl&fHxI-hX3 zSF3I{x+3>;M<(3MtSDUG0pQP=KI9%s7y?N5(`Mk0~Ch7g1k$n3caJZdO{43~iSAab3zoEv=wV`~ z4pDa8HC2b-S%u$OiQn1K?`tdF4$o;#YkF~>_u90&9iDqscMrNE_wz(1Jl|(^@fmWK z*0hyTZ7XAZQm)Z)EyGTS+iu0*4gMlN=yr?W>2TYoy4%ndxt|*{;Z}`qY@Bi%BSvtP zwt^pW+e$5C&%|l1?>_1~Mtu(`*A|ECh^99}dOV+(9x_Sqdds!N;kr?EH=-+YKUZX; z`^q8p31CGBXe(!ED>3T3uRvR=cewQ`e(&Pk{@UW#JKQ=|w-a5F`?(<#Zhs=W@NwJ9 zgA$)|O3SW^vTLU7TD0tH9exo_BSIQHpO*$QNh59f)jIqdRksmck^A`}6WxPW7cA3O zTB!48%C5=RR<`@vN~OcIUel^yoac6%R;9zUT6L?@6}g`$GT}LFb)`+iHQTRf{1LI6 zrmd7}8J0TSsuaHpe01~hkqQ1ri(l$+b7M6{?&pR~bT^6%Y4};+_E)9fRweN66iTODyiZQ$Ht&hr8h9?;7vFDzR8_ z<1BwV38t*4c%0mOs%$y=IU)b{SJW>EC%zk-IjAw1XB}QgHIGN7+_miT@VNiS;!(fc zkCNp@U*hqaQJfiYtgZQRkf$t;@Z!7h3WQmUk@(P6tYw~ZxLsp>=wXW~^-A2PDKq*w zvj%m-!P~7Icf;{Sk-Q@RjK#C{Nk1PQfgj^b?vq)tyK%)E2k&SRJmeD=PwI3;`7Kv| zhaJ3O#Ty3ia1lJ@af@f`R`~V8kM)P2|1tY3^2!9)IM7SGl({7fCQUn1*C zi6IB`f?{3(bEpU=@;|YdQpc>L8;-0gnK{USgLhi-PJ_p@dHx=m`1{|ncmt$q`-&5) zdjegN`}+!HqWfD`SLSlyW&NkdfgX~%JN0o`x%D~Rjw}9g@E4iOeca;rIo#YhMv?ou zAro#x=*A8c4{-<%#4!5ja#PahQZF)(&H7gA!ip52<3->#8wvOLAE z_h_B0)qHn4c)JyEH+Vdo#~qn)ztiG%lBTVban&71SLFVDBNN?sSY4?T@JyWyX`Kvf zos1~AHiz4o;*Tv}CvUR&Z4Ni~m+z5%P4 zOP#zxoisT-2Q;k#(rVIj@Yf0QEB{@6gYnl|C-2sL*E@JUiq``k&*qgYGU0yK;?+C( z?o{1QbVcs36J(2R#qGN`8vDm5K{eIVasIf50fURaZs z7(&m+s}!pWtkNP_$ae_VWy*4vd(zKyzZ96|yi@p-tWj^KZ<2c^KFOZT=KjE>d=9o_ zLwvgXJgX%Z23?6&d_?i`Vqy5*x1Ww`yy&zE(gQg-`P?7^4}Bx^Nr_L@Lu^%&y+0>JiPPz zzz4pugYP@^J9iv>gE*OQlprg~${Mc=d_eAjl6OYTlsx%N!}5wHiCUK6J*hdjNKDQ!}o_S93B^|-%{wVkEsB7IT`S*jIiC+;; zU*vA7psXFSFCzOgvM2Ktds`-Nycb?#GEcnv>P=ZL++W2r=dN>j*nG0zk@L=mTe9}W zJy)rV+(l*DgwIybA^)E6xDFode*~CUm~xhMDf2H$x>67F{kav-e4g*GJaP~3pU=Ku zz`eigz>ZsU>yo;ay}AdfPpKEVb4&K;o?`FqY3D94Nk{xC@laBqMc(1D>E&ba%--)s z8L)R}_S?WS`&Eo{-8wkXacdT{<0H>3wqNyS2lL!pX{U^Fh2uHdkJ|T>?MFS~V#aNQ_yKSl_^;l=rL40x3yP{KmOufi!i@^X6*|x`*|LW#7MjA+~n= z%4{r*;}azQ#rO!jhc&FlPuG4y3N4?b);fuW2;#t1TY4Z3=jEvw{IrvSA-voYD5q#u72R`wQX^9^ado}gX z|Heiu9K0IETO>|K?Cc$uZv|=EwpgjUm2eTc-$s#%?kdsUi2ZaqaWbJmHcm$3Wn?~A zC|*W%x_`WQnKFr&LB}_~+FFKBkvN&0anuBy8AlN-_JJ62GX2EKaLywyR;CsHIlj8& zIxj9E8e?5`fOXYmF#Q1MejgkTrXOOx%JbVFuAht_rMx8uc(xZrtK7>(kx1Me*}%=Zti(qbo20$3H~;VKjv_| zs_o+{?L*{#ZpcJ;Q0f(1l=VqjH|rmU7w5+d^*60Nn}EBNgYlL0#a>xl(e$p6-Zo9o z@6(aL$8setDF-uOoK;^V;|5!{mlf|ac*8~Tkl$wUz>3b|8^LX-O`E=fF^utHNeB0= z;+_RJSp*mP4Hnn(6hCnkJXsr!Id~@(?<9D!B6!F%zpwTqWi9>s(Ja>peZ{IW`m*=Z zmmPNcvLlLn1YGeIh5AV3|J&k9{tttR%=BgAyL%m;hZOG+c)dmNkpGp%bMwvlQ?Fj8 z6=xb8o+-!+dD`L#FV3!-dXY7M>9aYz>fuc(-V}H|TM!TV(-zP20xLSM_3N%j?NPiv z;L%?e#6$jD7EkJf^RxZqS|8N2S05u<-)(^fyL8u-+KS*K|Fp%I{Ifn}>V-br%lEMI z99}%%gBHilcc1cVc6hmCw&o(dkbltP39mk_7i`efOSj@SI=CB^=SFxo7Qsc{ZgDM7 zFy$<(m)|bM=>msm3f2qqw_6;kmu~8%Q|pEL_UeUoCDX2J9iHyGQf(1@cRp0R$*T{{xF-|r$5-G86KAI#tb_tK|;w^Ho6 zM#m+Lz0A0zu0R~G=uG{1;}R3ci;izxk{8Fz*kIud>MY#t_@s_{U_2MC$seCArwnq& zC+>NQBLQ?SYPW?Y}zR={SEiOoDFHUn>SKeg3$&rWm~&_yP?4_n;`?dmvf3QXyP zVn@O`d*G%mv^d;cKZS0dJtGtR4_o{ep0V8A`IyN4Z2_6+c3E9GN#7{*F+1;dVuXR}US?EHkypC1Q#xy2EVa()7tcYXptXT}BfPPw_~ zDC#xueq7}L_O#ZiRIPZGPX6n(e(I>7$|88kzbbgW)D3-dv>v|I;k2A*sI&dB@Vy!^>j}}S z0{&2RyM8=>$b9IHa#qIY58Zj1=Y#Co&tsg+j}GAflB}f- zd24B(Df=ya-a5+3yo;54|7C39_+2>@X1-gWhabxEdHAEpWi5?0{t42ikIl$9N8TS` zd|s2|^Ma1gLoV}iIg2T0f92d=62E19UX`9v#~#c)tY&eax9oCC+44u#T4G zOeSZ>v-A5p_}cd0tv1k&4OE5m+dneX{>291mZ;+yt6QtOwdji6?>mr*?oU}=uw?%& ziH-Ir@oNLQe(h1lnTxJJEW+>gHjN60-!-m31VtB4?i$(KSS!DczhsTn8@#8m?oKX*Kijqk+9Cww-JZ5bPHZ#8F+b8LJ_Ia(X<(eJijr~kG#{ zkalBioHq0PB4>{W>GL+zzQx}sH>!okpZi?^c> z+BC39Pq#^RWxY@2etyV=-#u0re!|W4vGfUMZqJxDEPVuHTGRgUH6FgZpSwuey~E;5 z8?|LurQBp+Rpfqd$b{P}bYqNN`x(2+9G z?U3KaXZp|1t(W%pV!%5yXWl8obJ`9U?hqJ)qiu(k`R(vdc;@sYJC=I=NNf!IA158> zPJ;MB{chXP&VcxlO#fb$Yg>}FK#6O*NS_g;-z(z|a@i9KkzN$J$YhQa-==MA6#Zb} z>mt9%{!y7bZss?UCw5X6qx{7JtlQj zAHrM4_+_tj{B>C(@!kWPC+63g!@tM*^uWr$5pMAT`ne=NU>J^L;Y&Z#Vy z_qDD1c=*5lIqwUR--VC7-z3?iY2nNA_~Y9?>heb~*<;!fV>q5~=Ux`Uz^59%-32hZ z!R1r1(0^p;j^Q z^?4Zhms?zn8q%)KVw43dus_PTQuV8>{!8!GF;xm5VA@XtUnqCG&^Bhu$^%tp+Z$M- z34B1>g~-b^4Os(C(&h(;vBOH{57^8n*#j_lm|%y*ZS<5sdtg}n$Mf{_rX7k7&lmDB z>uEFP?*kv4)!S#6GidG< ziOZcLKfii1J`v3J>&MzGw4LkhUJotUMN&Bwjb$&u>Fk;2lJ3 z+L?z-dC`x=M}wwqzQj1ToOcG5uXxqGd#D_Hy7+fX4wXGRH78{&=_U^H7vuirwam)~ z$dm1d&SLIk%dWn6pOi9cPaGN%BJ*YBlLMY5L7h`a_!j5a~A; zNgw%g(q|8ZaTT@*2mE=qo;pLR@|!gtih7cLJh?aO-+q^So;oA=*7nTJrR<9gp+~u0 z!Y?GdC~s>Y-I{(k>GO;kr}*syd5PqoblHE&L>o!JE0Ssm1X2~G?Y(1&^yU6N&J3rp z%hoaK>;-tpyXzB8;FG@G-Dt{#GD_4?9-QY*)H>xMbyy2e^o%{$@P6R60q!{F`AVMO z$MaRb=d0Q?v-bH)o`1JK&zX~kV?1Bsd%l8q3fSj4FLGzAK7WXjBSa5%9 z>Q}H8Dfei1Nh;CZk>cGJq(fd$Deu!xI_}wu(?!xj9?SAhB~E2|iyvqwR?gI~U>^rt z%JGDQE#-KESOA;nUoX5F$0hb= zc}qF2!k=#Wn9rX!2DARO5xfHaRB#IV(+Rk3ryh1Y+@v0Ma~J6EkoE-JmQx4hr|gFE zUGe?&pZl}#2G~^2coMrYc^n6)ADo>&oSos!dBGV6=LYUMcmwMPR%cXo#(X+s;mqHP zjyyL?zR^nzOFi*@MEKHPxA}A!r<}Dq!;D!*!kP8rL&VmSn$D0_9m4hw^MJoGEJ!XC5yJeBJO9PQ9AWMop*Br`s3K?6c`?)O5NvogUw_J@^rM zwwFA2Q(pL~dd5gzS`kgFQ`745>34C5hfS+f(^|)~;^W$U&$fj#kJx9|@hoFGdFQY3 zw=KSBTZk!=XCv|qyzsXkr+Veoh_2W`lTWvavAa#Dk#wX!Yk5{U)%l*SqdnPY>$BtG z_2PS_oU4^vjZdd0ocS-JQ_URLMRxli=Q=)1RpU%}Y85^gtWdZoGd zu3nV2FL{=}?Z>&Zrc~+=9lN$$w#xL`GEV)BcfabChq5jN0d(@w~XAc{A&3pE+kGy;K*tcC6%jlYmtmjhy1;;YT zzx5@xUH(sEzk}@gZpLnQahF4qwwR=?nRhN?D;{>CeN+E93B2C}JBr}Kd#U~&k%8Nz0a?O+$LX*>8OV>_(f?276gjXjR-xcg!F zf`Yb#yj^Suo(b$FDK_1M?NIN=c4{0vcYlCq3*sSvo5cg0_4d>tcEbE`!KSO#rmLNG zM8DmkTW)cL7kuQ7Mb--zaNPCCMfm)m{p62!D)yMD%<>rot0A~-@THrctl_@KlzXP* zaq)xlJpTEb?3d??+3$<2Q^YI6sQ~XZ^sfV~Ab+oR$bHgFh#Th)F!7gagBM?yd!MwN zg^+hixpzv(@nLdr^l|dX-OibpG3-X!m? zCXKNe)H_hWBv>WXE#v+J?_BZT1Ml4Q-ZI|Xb@93T=%;<lWqAD-(^Z=dxz;cyx9y&fXj4Xnep$-U~1BrvctA*N;z-`Qdsv zzDfE-(oTr&^SgQX9PQlmy)D`XT4)12Q?M-|-w0MecFQ_?=1%6Og1;7-!3J0E8f|!q zcL5gj>y?VVlzFUE*W%B^g42|>a~T`l`M-lXXPX|k?EqhXP3?f^inW7%*q7h_H<7-3 zpL&k{7r0NonEi*qk+UdIa}FlQ{&|+an%J}y{}$nSG;*@yqn z+?U&Au_{Q$cwWD{|DY$!~}U^EkefT{lw=Kx-a*=aL@I@&Uyr6NN+9T>x^+_yef0c_UxVi zZd!8Zzxi(GmOYGbi5W36>w9|_d~fDS+A7~o>5bk6+9IGDW4_>%d) zxszDpZ|{)tE#uYAO@ZwG9P>~!&TT>VTKjX1MPy%A_UAsyyt3&29O;U`XjsL%2lG(R zHe_wJKAia?`H(qf+0t<4aj9E|A60=ia z?ROG=*+-K)S9KwjyYz5eWnldy!`%! z0BamPlW62w_!A?Vx|iqeJ2N(rcl(|WFHdgl4|)aLDDnp-&r&A*pJWao>qs(3P99~B zOB|<%=dRH#g7Sy+I-}FR%ob7*!xl1GM=Mwp7z%JeU3glVNs%M_zS?(jq$XWsS zX=avS->*E<@igh0ctdwC=BDkv+X@Z`o*L^pK-qqSI|$630i-Q;)j^(Dl1G_`&5-w} z`D?hbVwudFERk%R}CIWxiYgjgNnvGM4q^akxhL9><0YzQ@Xq z4;DY4qz%#UZ608KFoqvz962}~$gU6K|IPX!e%`DJ((k_UB;;E0`iaUxPDQvMZi+fLtG>6E3Ee`PpxRMMtzk+Ha}>ol;xIY_=5ZdkTU z_7@W)$V&^X*^5rsHLmsWoUHYjcA)JfCvLwq-*0~hUO8i`l7;ucNL$hN_=jab-#8hk zZJ72rdB?U<@BKpZ;bsx!WE6>ZwI%QterIB9clK_*Tclku7vS9Lq% zs_o9W3VFPbvcUGGJ<2|%?3dm_dCPl|RtAHUj0Lh|D(ru;F_l?6q#b{cKGeiDulV*{ zS6?=Ee+_>667P?zcy!i_=U>yl@IE*h|KG`dLx~~M#|Jd5Vm$*N;I$p8Z|XQA^^GkD z9tvls@#(V8Dg0Rv;w&F=;$55t>n0ZXR^qT|gPwk$wwXR`4;|C?+&L{W8OPr#_~6M{ z#nWX>c8?vCiQFFxj7)Ujp}Mjs1=lof&zlRcC;we?PMmg@aNC$WhN>xmjZCm_w!CO# zIqkq5GtnOMxFHk$a`c%OW+d25ghIn$Rzk}WqWWkNd8WUyaibs{WIS;;X@@}i+XM4>;yI& zPW7;tCfJ-C%7m6ab~=TQJTGG_*=M=~J>C=7Ec-EcpljdVyaxM_IHiQ|PO5l_u^D<2 zi(hPPCSzCh%suY%+0}2~MN;C$Rnz|7D8AH=)ymOB)+h9Y3i^Z)?*)u)3}x1|JMoacAv51<;N6l9yjyZT zzF|FOT){kcec#mS^}Ls1J@1oTUwbYqzdKt`6%SE=yd#Qdx0Y`SOvX9qxEh-hyS;ekZ%*wYh zXYO5+HkeId_Ji5%U=Atf5SYzHFp)2pI_kYE9dF`YuQ#*q!MmX=D%mfyeCxnI26nxJ z-J{q&VAmJHMtP zd@Gc1rGs6k*mYo67Qse-%JQvHzP$UylTbbO9jDJ_fp zm;?Mt2|mNt--Tc%amm3xTav;?-<3EE_9ex3_FtmNb_nMS{3dgp6D91q1l|=t(UD@D z|8Vrpfe%~?5%Y~*$Fb)iwk-a43pxprk#55(@^glC`8{!#{E2POIvkE`{*J?8Ryp|d zhdilemuSN;oaL@X>@&K*WZ`W6wBk&I!!rfTNpu~Y=>CrMN_*$gapibI;yK}X(&0Fz z*i&GiEW#0ayTv}yp0)7-t=Ge9Ge;cEQNLIP^B55K24~rS0?Dtb|%c-|!r`}o=rv)6IDaaYQhtmfR-=obP3-4*4fZtm9?RNMz zDQ*+EyNmEc{x_B%xGQgf8}BSJwmJspB$(q4X0>8ggE?LV6ZtnR<^<)-`$m#u)YrHu_cX>-l!S61DkNiGc z9$i^%Vyqrs34U}$xwnD65AG2MdqS}%z>XBbM&1Ip_|51@Bpq+VXE!j`Eu$}}zB^k! z@OORcn|MCQr#5NZY=#4VH__s77*-C$aA+yQ0r`zKf1R|cX4)p>h<-_5`lO!Ff^Y3r zyk78lwqP4Y{sspx+JlW+-&&(wYvC%swa($%t=QdQ*A?N4{L<&tH`S_dt=2lI1amu> zRSss8Vm5(URRk0H-&)KX+Eg{|w9@gdyi+1k>R?tWW)+yFMKF<{29q-ptZjqIyCJM^ zJ%HTSUlRY?MV%$D$Q%;xR~f_G@n1;O36aiKoh$kKL*&zzyZ9|RTY83fE=V7H8BDRs zIS2Ey#(rNWR(q}pCh|#(*#-YI+|hNJ{V(j&*u$ydf<2s8ywl+EY(aZK{%IHQRQrNG zoMnC~Txa1b_Hf?edQ!1Zf_=USSL6d0`z-USS@t+(OgTjvoOZA{yI{s;oIi-4E`o*p zA+Y)-&sUgJPQg?A!=JKg2-g#Em3DU0!QZR+d%-_h1RuGFbAoxOjMcfP&gB0HnEhZL zbuh;ja~#a0MKF=q!!61kW$`1-opr4K=W@^FC|u!dxXx%fA0(Z2(mCX$lhkyQq;sf9 zI>@iLWzda}I7m55|J~)-Lbu{{gTpfgZ2`H96YX+rVUKd$tF|!ZaOBK{sXxv=EOut$ zJHM^IVK3z?c2KG1yA#aqFei%S8~G{4j8?X%<-DgiR-X@K>uwb6NwCKp z>&%5yCKjO3_s}1i&zMOS~SAL0gPHdwrsOy~P1KD-X^WYZP!+Q$7oOMpID}0UTr)HJcEczlZ z#0&YyY+m`_>^Gm#@?-tZ*ksbl>siG-3udxN`62&BFgdrAmNU*l8BZ`zz02A@>6m@` zJ_q->;vN^SMR1Yd?{bw`0`8j+%i7RvS4 zmpWY6!nMW09ah|7a9fJtBLDX%w0(*V$QYZoPgAaqTCPnF=73@jfZ0?86Zr)&WsOkc z-EFzv&OM?srjoV9{<=Ws{r`c@^IhW70+jR5%J-m&2YRtWV z!K@*QZg1p2wT5`TtRaHots%0mC2NSj`|r1wv4+K(V(UeTWwZMUQ?KZdpWZ5 zkC%V`af!x+CPRol<9J&*UB{v#rGZiCxPcX8-cZc(dkT%QD`b zTOJaZZOX^4ovvHh!~A*DkMdpOBjVV#*(<4c_DbA6OytrQWlwH%Gkch{mBD)c@Cym) z3oCUGvr6|ctFK`XlXjA~hq*40^6cMjJC$nx{xK`^W#q%Gz4Je7>8ZggK3TW+aH_Q( zRnv|Z!TFax;LxU%)!|G64ECToI}mvIC1T>at2zJdfILeZyr!6Gp2P05bz$wkn>=Cn z=IlUK;j;s;CvUHo$a^2IBVHgl>Ds#NhfA!0*sPovkTIY8UI4LKiHrCe`O4n=Puw(T z*5n=Fa`y0P-Z$WtkvzNOBQHN~p8d$nWoyHk=a&S&mer$8+(MfG-+h0!X%oEH;R}lY z(k+x3_~r})`0o1z%(KJ{OxkDV3oHkQZy3l5JdTr(hWv$!=Hlb~1E&KkvbL&Cwadyi!zQ@;Q_G+79zhMF{ z+_B2N3bYx*^!J5y!b0}p?Rb>A`>({Vl|KDxB^`{MJ-pNwOr?j$D7tvg~nGdEIR z_L)X(L+)xZbF*IGv%TCUW1sEeIWW6!2A5b8)<-7gZ1X4apAGxkGcvBpdG8XoOP`&& z@>`_U6{byv&9f_cwtt2`I(X+984C+f;S!x@KWuqm@=^9wx`^}IM1Pc=Ce29J1`F-I zen+rlSqzD>6+5qk7yB@$|EKK5f*obt7$==olx;a?l&B_8jkDW2!5e6ut$WW)CZWdr?{q^G7K);)df$v zPbNuOB%4*g#aI3<#Pgspv2(J&DHz;)k&&{ezxv)kN*11qKtiADRblj%2Do{_@I3b%m5r^|E@=5)K6KzslTmJ z>_cp6uav{EmIHlWf0dVRwjB7aAKj>=JHm5Sq*+Nh1_GJYO9C0e{waRvjxl2^UVEdD zqP=b1iH+YwdlSDx*)Lz;UYaT|cgGJklK~lLAZq|8A$=M=8)=taw96#zvI2XN{lbdn z*-wdml)Ej!Zr+4mfykq&+I45*he^dW5HEA7Nops6ShoK7u_-7 zQOf!?`ojCjulv3niC4SGognAvS6%|kz5_}6D&C31yOCDBOl;uGW{;nGKKI$cXE_s> zxkx`XBe81qe^1kgx<2bEV$H7boIE4X&d?_|!g+>%^b&nj19_4D&DA+aU-o@R=MsGu z_f?_Gdygbek9-%3(-S_XUn8B><Z*`m7;|;1 zvvsBQnQg;h^~p0$;M8WZ*WhagY3~ihQ_^no&q2$xb=No+X|T$2l(~Yby5$p4deU`;_^?4}&(w(Mw0hK_*>m7mPWgqxiA;K+4FiT?{SQ z#Rk&#Vjbz@&6o%KdftmwckV((E^92w0m>^zS@rQZEaM#FE3ma_r`iU6_%+!^cVXN3 z{(qx>NGCDMUk~*He$G0I*hepS_}k}t!X`dW?u9Tq^na$`pxt7D)&B zbHcq7EbP(77H($@wwksPzZ^)tQT(he1NuMHztZNs{74yKzh4u+owOtR#4@>u27bZi z^dYpv-{v=qXRso)2U)|w#yuQ&9fIHU)*+C|I>Z-+ukgWsJ>6Q(Yb|*dxql4aqqmh%-3mexor(wd|Z9-FO#Ok9Ljw5n&#xqltUA3t&wuV zP7>I&X=~U6cWq|yS&#!>!r&*Fo}@GDhtN?q~O_nfR!FxBkDegqT|X&XlJ6 zPnV{1`hcdAtX+QgvD5!RxkvdPZ|cC0lw|vW+LDDnV0+a&PT( z`5O;q63r#qdGAi*<|ZVTju^Y$8e_LdV(ggr?v>9albfe_X21NlbaL}F|Idh={|}0+ zbaL|{`OfEI`F`Ex<|Fc*&!aqhTx4aFn@@<0&y&bbiL88b^J$Usc?Q{8kyT7?o)sCN z=aF3y*|N#amqN6oWs}j%@_GGabWT2Rn2gTz+5Wo8=oR^V{UmW6JYO}rIaHF_Tw0QC zZx#IiTIa7SOLFGxcK%(#n1xu+)8(5aMv!hbU%_S*L;O{h&}Va}Q#I|#uEk4VTHTR(agyh%I?UZ! z@d507phMco*G(UKqBK=jMxR{C@65gMCtI0Q{UC6xJ@d^+UVS35Gh*6BbTpLd9}8LR z?3k0^{#vldB4(d8IT|rz+11P=9z9-~dh}>%%J4hdnM%wqNg1BQp=?~=h=zkY`9EyRYWpoX|X+AP;?D z6zWruKO%X6pWrkuWt?=pll9t)WeLhl+flZj;qA?T;X;hz@;K%BWzG`C(FsUw5BIW{ z1=iNakCdjOM>H4rK-_{&kBi&BP-=3^Ju&2ZNLuV>> zuru|*bZ093b5L5 z_)OrlkM6N~VxMYHXDT_eBxP)-&A}Yk{EU;Iwj%jKei^*@2>Bsh#+IJlmj$zS|2dl< zewX^+Fv;1^#~$lptg=<+@rf2EP02?~B=e78M)#8sbgjHuFD&i1UmIcyM#=ppOri>3} zdEZMJ+qKleDcUUUeKT!iaJT$*rnX2sF8|A#-~9Luw+sfa9~_*>%1zrY|BITzoqV6@ zOhswi(&kMYm$UlgwC|k-N@0Fni8>m;zdN3=CU?P8?#XOX?^=S27 z=|REgU20v&ljD0cNNOCi3ML^RV*0 z+L0b4EyMQ(hc7YpUVFHrv9(u-twpaOU*uOlt!;__C63<47)G~|{|oS)cVhPD6mt&D z`64lU$iHhbk0{>@@D+S1-xnQwAWq-odtR~6gNoX44A;mfb)|nz$$bZ#h;b)U@-o)MS#z&uWu%;Dj z8mvWp^oK0gDblk(dXMVvL09B{|A$Qc-+HSn^??rz%e@+-CE0g>jPWV)IpZbS{^Nwh zZMWj@2H)0&8*_q8@Yj0y4!3QpyG?Wpa6=}#b?C;%JJS7Q9dP1&2$=4>Ki(q!$WiS_ z`byIMu@c@Jq+Aa>Tt_s$5z^!Nys|(h>AlHvJ?wDZsJa``6}i70k%?}()jh3t&3T8x z#(OorUeW`rpp7H{mkISH{2%QpVB>Ar z_tE+ z<14l~T(4^E_0;MJBpGXLV=Q#yQI|cnTZ86iLS} z@aNk2TAz)d4K4IPvzp#4>49a^^V=fwCMUh<*@c+=sI-|Aq4ZDk9*3J<%oAW*AKvGb z<#FYA9DaRT9)5nvODz^QnuM=s-@OjjVZ}NO7SH9CEix(FfBBTQ=U&pY_C2k-)98xa zUoXf+_wTJP96bA;2xWcvZa&3_?@^oScDU_V{QXWnxIP>_!T+Yk?{>KDRNbBEirmi) zndp86-PoQ;x_@^h9i51zrM_MJ{_4MD-)Gdmhp_Kq?3*(x#=hGeuH%~CIO*|xUV6wR zz2CE3+Z?V*)lH%+az9sOqWjxc_mbK-=UWEPV&B8q_K?rM@6WYwGggT?Ha?(f4v;2z zCQZK$A^#O8&1lSLy7vi9fAT z8?RNnsdBhADt;sQwjNv?M<)2!S^O%8Tb1fop(}DfH)Nvw@^8vJ%CPYoY@D+=l8$TR zSFT{==hepNgX#V&!F23uFl*x>hij##S4nz2pO+poN$)c0nK^mL;X0@B`*ZEgZwhcl zCc59Yx9@?~I=>@Gko~_MB`zT~_?d;G>(z4VmD7-Qv%&uVHm(Rd*I$ zk^9RLndnYi-ILVuaUF}E2xjZ}B%k8PILl*v!zG8?S;apKKDv4M$OM1F;$L#O9ar7s z=!)FW4VmaZYIX55N%}l9PM%h7Gs^9tay#pAJF56c!C%C0{i4M`>u}qzy8F=;xt|*{ z;r1b`i+v>VR~y)`8XOO%Hn8?IxHFjQ;@v{YiC}hqanj*7rTA0eFJf(=M1IjI_+=k#L_lX>FxQ#0QDER2+;Uf!zf3w9u;&6+pZVX+K z`?(<#-3qIF2yWmRZe4Jq+>+hOX~yByr?`FKqLYVLnsEKhf|a4HlZtWKPP0O`%SCMJUt1X z;Z&oXYL(M&hf|~CHiC;z9xgJ$ow2yP9ZuD%TaB*B{hW}A?&r}RtZh$iu4zwgX=zUl zHMggJsk=S(%U$iMhlkoz{mJ%JY@j{$z(jlM!JX}?hsOCk*q(lHrak?@H2#h=ef=lf z8AtNB2j2{*3fc{K45sAnG~)9)KOM+$E+h4prGeCK%+Kq1ht;a(fz;X?1F3i197w(W z)`Z(|O>mKeKt2U5#J0b;6&jbWX(?AYrb?q}b_tY?purp$f^ z;|Ga>WzA%3BV%Rrxm-V42a)e)o;B&no&)RH z-B;LisO-p8JZ$#kvilRPmwWpYtbv>T3D)r|c~@~^zVj3R4jypk| zIxW?DDWzUUwO;&V59Du`I^D+a|J0sZ9SXc_B9_H13zELv!*DskdN}tsTn?w=tna(` zhzE!RSc|@U|DxdpKUsSngxlcS>n7z+vkeai&A!Hlt-;Bi;7FNx{f(@dqw_jzU;Hok z*(C?am#o($oqWw|?8q#9l11``JhM~Vv|x!pJsL=vc%YbrCF#b(nIF2(a$i=mSL0?j zI?o=_bdQkk#vvNv-yoRaw81RQQ_2&a~vWnX3(Ilqq)-*S{^#kQo( z)=)kb*q7`nG*U+8#AdP|*(&whKpDy%Q09O3vn3YU)O!~^C;3i1lvkG1%5xf?600EX z(O>V#zal(IJ37aBA_B**aHiqA?J44=Qst9D6Q^AMx!~mEvZvEy;!s2fq zP2nD$r+mK>lyz**)(6G5TgQkS{0*M#=eGxXDFf1of%7~3E#+_bi&xD)n&f)}Y1w;> zHjtj=I|0VMU~=!#pWpRbF zXd+)Nl*LPwMGIx|l9t5}wJe$!%A#BJnkkEBEsLvSYoTmeG=#jexJ$}{-yUe8EJ!1w zW$_T7KkJqSX?bNqdR|$8>6Hbzwk*thhW3@^#!q)^p1a9&U6DK^Un|^e`Mn`x+Wlwx zeeJ$U+kF!_@btI)ccSmM`~C^Wr$Zf?7<(iCxI}HMr6g;!lW;7hUfM%u|2x{!k?H3- zdG-PRPE!VnP_`Y&U8DV#f&6&0M$K0ve0VmmpFt)uV{edriC?TDZ+1Les=B4!V#mtQsfjwQ@KgeDsZDs4D?5oFXh>NM{$ca@tz`G@)$b-b$Tq50D*?W)D z-h#BPe^MUhOO0)2%5T@tyY=%S{rrS}{;q!hseXP}KZ%dHllE19yMEqnKDU+;1F?cO z_UBkO8x^lkX-Ie?O#_aF%ti6x4&^@HW<0bdq z5I&zNXO2dMhu%fHntFW-%%XRWI`4Fia+gW;G;1YiSSw*&?Sa`)`oZ&|^h2x<9(V&b zc`1}Szj_Yn)WWj>JEa)tjn6Sd_k|A&IeLz7!dgPAQA{LdMwEtUMw8p3^? zk=jzt|EzV~N8H$!TK=yS`I5;k_59D;%zea>ZE52FW|1$Q+|t7T>qO4~ZTue*dFkYq zPX6x-X5U@U8r)*FW2PStB8?}ME+;(xN z_6B03jQs8e`37Q^Jo!U`$qmFp$@4k#Pb|onxj0X&91OwT@IM%5ayOhAN4$;yd!6-) z!@6D}Ynzt~_(5db8LJ=)_*o%j-<9+;tyX#yh2Le$!^N+w8L#i@n^tCHxD0~;NNHQPdnVERCfwpk^8wJ z6WuneTM4(TaI1jZ6?o12xXGHpNr&5B#or4)x_S7>g5cL%{F4s13DuoISLA+f$VB%h zt6L4X(hkn~!Y$O1+Dw^ozAU>=aopjwQ*n2Ki%uRcGQka6+~W?XQPmwqSLA+9$V7MU zH?;lK!l@EYtch-}@NqigaN4H0+rUL94;Pu>{-eb`;&2*L-63>E?&pL|bpO)o*2Ae9 zPIYjq@^L!ka7rp}5?plhaFGe_A6eW(4yQiV?L$}Oeon|l_W-)lX8Mc)d`)dfD#=>- zU`(*bj*zno3@C_2nVO&?;~!i>Xtqo%u&bQke&zh*hlIGnFx zy=Oa|6IK`772jso6`OQjv02v@TUb|=woq){2i?3fK_+E#zr~+&xHW3N8p)T){pE>F zbR$-`32w9l!>vlWRl`Zna7;MmRj;`9;G&avR zRib11Ah^pbkgFF zINZ*v?pbt2?&pR~Y~zouZVTM-N5(deD>v@RF}A^ai`S=~QQR}&qLYVLxtlb&gh^TG! zsBQEv*v0^9C9XQQvC+w^ySBqKd3=#c{U&W*H#&KB*IPvHFMnjB`(LcC^a=Fsru-+g z{CBHu>`~k3cDU`=vfNKuF48x2Sp05>+fLQpiLS`~+>nWFyvypg!Hs^wa2r-`-1TE@ zqs`$ouDIjiqLYVI7T|NLS=;DR+vrx?Xj-t1Z+`;YD8-jpztQaEHK}})@a37jydsnO{SFw$Z!|l3Wi8FL z1J=OgoKv1IGSU4Tt6NX`Gqy72-=^iyJ?_SDbgFICJKR{8Gx)5>E!Iar>)|`x)~W6~ z(JjCYnb^i3Slv#z(LWn*+#h6o8uteo+o*Opv1VpCvDOtwCoj*)q<){UxYZ7)TGg#Z zSLA+sMJBqBqx&HD0X@KdK(S72BZ6JD@z;cHP|w$D8(#>rZb4rX?aJCjfca;CZ$~QD z(~)|hyCd~rS4V33QsRiav5mg=ob{R2yaOlNLta>q`IzK|-^*US&bvST^>S8%-@Wth z%md9@3-dnn$vn?|GOshA%;V&ueR_}RokbUInvW^X9x?zSmyD^s+Uu5h-k_|~kV$p1*v$4|@o z*}OA7>w>96VyBYtVb1unCbL%>|-y6wB+2iR}XH#fL}Ci-=81kJ0w4Sn2+wZKxw8aYGtbgq-X+hD!fyy{c*<(MfQ7XulVBDQn4v?tS>L)8RX(Ky*m^b>lz|F3| zlEKA~&5`1HTuL0jN^e1bkNrfaza z&RTBJ@#)AVR(5clwS+Oo2Lt@Eewy5^Yq_kUnl)V3QO){ox2~tMR{EOjsjOQTTTf+u zw1@T4Vb(iS8(L}K?t14&%E4O~)b-Azk)mS}8GntFXURumBmJz^9aY^?bVcqTiy#x- zU$wf7JItCQnXx&skl@ zFLIANe#^xEdher^^?+uFQ&Mr0;G&antwwcP~GEs@sRI$o-s>=$_X=~T>4Fl&ooBLB8vHbqj2 z8Zd8W4TCeY%sD>r(;Ks#%Yqt5(HP=4`i4gE@J;^xRKbz)n)j|Ro8Jn}Z>7VnUb)qS zRat}^@@F)^vR<1YzuZ5xT-J~iEhYSQq&UBn&Hq*WWjpx~IcZdC8kMBMeAU#azf6%o zDQV0zR=G-lyqx@hy!@7Ub4kk7S2JnMY8rEz#yq~&wlOybavuLFHsenN`MBmIF-yNZ zN8dy`=1lJi#l8Tx)ZZls`>d|_oMpY|QW0$Azhtp5z@Pf#9T(X$IjekU9n9m3c^u5y zBACcG3g!vQt$7{74ag$Zv3P zqXWdt9+q`D&V7NIIOyQHF^30>;35B)?c`V5qVSY;yf)>@Sl#0}?cnw*&t7;=7r{mT z9S1ks22aK$fsy4N#7zEW{6wsSS-gm{p2d1?G4WOys+T>lnXdL-C5TtPOQJHgr|zHdpyw zU&UqsD4+Z9@%JAJnXG=WU-$lT|Iq8J@7eG9 z%xBL0o;fpv{F4r@oNpfOb@EWRgSVh~3*dEU!9(8S;F&x$tvuTuo*fRZJNMd=1s8cU zxS?szrEd46O^c>YtApvzakgf`M7}||w(vV?bN+@`C)0*@I#s^i`A(5%N*m;FuyN*p zlQztCnzU(j;@sr$bLTmmvhYLxS_fC=1LbU}RQaxV@Z7o2`Yd?JfA)x$Z{aEByF+=_ zI6OByxc!>u{iONkEV#&j;@~nyjz?>p@?GWNwJTmbcvV^OkU!(#nexs2ps{C}!?WDM zb>|+-v*02>;o!;~nw(c>>?!R-iG%0P7nWqfL;fWP&zvn@tUL=Hp6=L@@v5oc7)h6JjNP~`}RKTj{7Dk1NJvh)QNGXjg_MlX&A zIwp5q@8_K840VE>BYgw5FgQ6Mc~Sb`7w$>k4}IZQ>MQ!6$8z43I(PHv({D;*KK)_t zdj4=`Oyn;=%-YXAVBH!__CK}%{o(gtdBXUV`IG(c^IMYsj2E_$&ocC}1jp@T+4#zN z)e_$eoF8iI!1H${^XvtDtmI74O!0jI`QL#lI6vVmPq+WgdECZ#4Qz8iHhyX5d~A!~ zNk2}`5B;9%U%>ZF`f;2$n!K+(hwnL8%zl5Kyd?I&Ksul2e6fstkk5#%m`k{j6HKgU zti?It_^)k0_VWJZx!LZS;z!+lK$@F;%h{>PbH&L6CnUYn&q*CKoXG>6tDDR}J74_G z55D}wQPP|{(#@ISna&G;T=e+n0(R55z9{EBi*1Y^=c}4@AU$lnjh(RVK6J%D@4TGb z&-|U-?JnmNU-~EVfP~BY1#D(;u$#eIK8{jW-U$EP6?t0tewjS`J`>ju@y&;@qwrOooRSwS z&aS3xm88mn(b?bn;x?<3)B0jX9G(5IPll88hGaPV-{8^t;EVTJo&6u+4)zbexcvk+ z$RQ23s4ecn77||0>sRXLeBi}e)up^7F5bgjU0ikEnbf&(m&bm0F;`}F-Wg0JIo@bE zUU(Zi*y6(5Jvwg>CW_EG|2EPMI~*mSJw>@c`t*nYB5z~hp)JgrzLmQ8Xb!d_9mkQI zJaH@ef5S4?`JKu6%?mrp{~N%P`bYN+%Dn3f$bQ;SIJZjM5!}@0m%R3&7hiabls^ZT zGVbQh^VCo4rCd?JoaapYG+boo1@|qKDe^7fZZg~%XG{Gx%eVlRE-{$v07(chqauS&KV`=uT7 zlW%<^FHjEdf2Z6vQOEDvrtJ6g`)Ypod-8R%9oWkE_UwOix4;SR3!qML%cI;a@P{To zQUzhoRwi2i0naSc$B5{>VDGi8+j*~OO<}4td$+&UVsK);;3`i zv8;2McRQC#f7aYT9%ZeJKD_zO&a2YDeUviq;|}uAz*o*%mUVbJm)We9)jDfs`_%#_4j8fyW4orUcQ(5 z;)Bw66aL5xI1jy&wKaq3FBy@#!>rRu-00iN{s}kl%DK3gK1JN5e@ooN*Fe_g3?Bc> zx<|!X`o+IS|MdrgOW!qb6LU<{W}dKM+iLE&6|RCEQ0!cX6YDP9WbDs+NoOv)8TwVS zS7BPP14-;W|BXegPdu~_TL}Mf2R6-P?WzVFR}m-dBJ&nI{e(v!|1RpRcjcqc@3Pin z&P(LCe11!<%b;WJ{l~J_vRUSof-Ba;JLQ~sk%Nae)o5=i7EScaj(!_!A;-<$TwgeAHr>okvtAQsy(U$6Bu_Bg~oGeg2ho2lLs6 zzpXp^_1)GNtl^mY;;r};z(2%$=K$jy)>%w_!8*~iskL>-ryhA{KXd7Qti$v&-ih+Z zynb|0*I{aO9ftLXAind=VX-cQe>`h7#!ud?)8H4+I?W!|XoC32hghSzgg<+C%P+B3 z)9S3%h;O>}eZSrMrcdMF{*Q}msDBJhx zoa1S}`#NX2ab9N^_mS`(+Yx8JkUP+$*f7$@ z*vIO&scsv(B6s=~k4gDLChIIqpH$s$T?48~t^wI~mdLO_5g7*izp!@kLmNhUUE(g8 z!xubRw>U&uDs|%9pqv}vT&kR}qbxi9p%C(?EgtI@cU34JYYY_|1Hab&rhjVj3c>rF z;^l%D29Gt2luc!hx}Yv`#D33ORW!FQvGYD(B1)VZ$)8bo9YVh6n}g2x1=InJoUdC+ zUaFveSaGMRe19dF7Okro z>YlxM+fCiGN$v%?pKrr_7h>(>^FQGHR`ev_gf96K8~8r8hZCKAyN6TKYEi<+7E7M+ zmx761mg^Kbq(QsF_^g9>YNKEDz?*poKc;5bj0Qho^TexwK``A zXP$R#F5y(v<(RuhO_;Ojvv+32x&mt=+oT<0JtMVGz?-M$f{8bQA38-lGfTaEHW)k0 zeI-A57#mR@jE#nzd@!t-!(j4z#K`+l6BkTP8ja`hUM*^Pl8P9gnyJU z$QP00@UZ!aKi>F<@H#^JjXT_0 z6<_?{$1~s~6a2rh_(#Y$2Oa%d)yKCYwbtOq?O;;>DEi?Be7Ngb7l2=2T`-<=OE6yk z2-841 zmvC*Kn^)a=bVcsXbI3&ZZRm!3=pPPHe!-LSX3u}kVQh6bV=GD1e9~0*y{@NTwB<00 zT*?c5%4GY?SSS_#84X|j)2~uqUSqjO9qzNLJBzN!z1)#WeqJTIV5d00`nJ;72gqxz z3kk>2abG;lnu{sR?GCq7ihl}xzD>hNCiqwSv@Ewf+@@4_3SE(VxgitXpIKe_iJfG< zrmKnkbsWD{^01tv*+stUTDNMXtA%=JB-uAl*>6(AnRM*u&C_7Vop9DlU4bvTsUMr1 z_^w#HQO;hhCgvFhi+S}he%DfWmr>{BQ078!`lF`pXAd?VAd#R4VcA>>E(@ly|qUX_V|U`;}>d=T*WPLa7z`p z6x@O=xX6nHH<#bR^x9+353xrF<)ekPq>QiznL7Kx_1~k;`BsSE#pn7a!9yp_=bGPB zW5?T*W5@ja^tnf$pVQ~p%`=wA_~9m*yL0wGi(gQ-vE^F{d&NAGbLUcsG8}I8rRtn^ z_So#2@!2ur<9YkuZSG&nlli3+w~lmDSGwWKT}TJ}5brQGo@ zzKL?Dj2R;~yK_p;9Zbk&jXK&&o~Pd2(?P$YnZI&hqN|to44cbb&n0}4yUFj}CwwE_ z)jTO%-F5zvZpv17t={8G8SdsTi;0?}iozv-~XDk zX8g9=vANr7rx*Ws#)b0RZoU=&y)_Rf$8J%|eyScA)x0!HUXrpSb?SAVGK>5R#9QJL zsU|+u8C{e&#(pu=Zc?67c+5j>lYXnrL-B1|9gR%dzCnv8Wy!XiZg~;8w|++^y1#36 z;T3J6{hXnGr!EQ0o+j%0UDWftsOQ_M=fmuK4qfNt4t8IHJ9F5F>2R%9uGLqM`xZ%a z%BiV){K-D{e}0nt3kfsSM;iChX9*|oD=g<*nL~_F&xyTuRQN_JS^L6Pn^N&CSKr`r z!sR}Kz{$r-xy(Hxx zn+;=&EOSe5h5w53@601}k7%5I6Y=&5r_8^*c0}eXlV$$xBXSR)8F23rWjaud@`_I~Wp?yx7M=pJnt~SbiD|QlFQQvIeM_H$AZ})Sb z3FX_&^H9ED(I=PkpYq9d=Xq}NjdbCI%UsOf9kiIlGcQV5sc>2~oK}aUw+;edbPY$TgLn_p=L*jU?KM ze5b_|?#=jNAWQkR{2G0OO%~7cqYszjSEYT!65n#XYn5j$JWJHRUY^MBba2^kWzOR@ z{punI%iRxPp*ev)>;^m*|$F+MycEAO*6 z^@My&o%5^I6O`8*WRIe)C-TYXW62gCU;3on zCv*APm!CMs`ti~IA7-6-=aYw?-ua~e9qYJjX05qv=F3}deDc@v%lOXk`tVcuFl(&f zZu!WQGPY(N?@4@8R^Y$z9`eoo^yi(m*P~CrQT;SN^cShMS5Ll?_1CXDVZ87j^37`r zoEVXdq-LFF15E1)(OI5-Pe|X=KoEf;R5IAT+O%RV{TY)A*_vrwS};9G%Wr<%>Svd zZX&Gj6Bg^AsjxO^SQ`jSd>1nGcf=3m18!J15!RiAh20Kezhu1Hh*#TkSg#_ivxMcW zo1TB8hQ%I|^J}t%h5X%w#ky)-d~ST3eB3kFh|f`md)4<}v%G)hFn!)5tbdOC61ygt zH)V{IC+jp{r`?+*zS7qndCf@Ylxf#eWrni_rJegms@*%T@3!5WUT*iQ;T7V&bDDPV zptgHsS=v41GQTEwWAC1#uRBQ_IL2QOechA3WM8*e`?`JfU&#aA{X7TwlfLdC&mryW z4)gyj^>q&<`?^2n>>ugpUduX1#=b7|Q#S28-FdIx%;^zA{-a*%IpQMw5~CBuVVpP} zCQi(8b@v4m^t)rb=ZV)DUm|*%zj?lE=Xr)F{e3Ab0%s`u)Bky^qvd!MV}MwrrGJxUprJ4pSX z%Ky^WICoOg$=p9qo@uWPCi3Z1^Nq~oZF-Zn#h~bKe~kX`CdQM?a}XlWP&b8;-z2(2 z{7&9DpZ8*o$*agsJ}mPs<25Rt>}k9T-WrKZ8F@!BWewpk_mZE`F}6EJ{*b&~$dmMv zJ87e*coy@Nyk6pj>Go-^66U3k(+?y(S)bokf_;jyRguYOtYPGjiw}Ugn_bdttj?rq zqwJ+24So6C>4mM^3w()F;ItPc?JQ>&`N>DmAUh^&=9C4AL(0B|YLCLJ+vB_FTU$Kp zd-3g@u@>9r^WAwXeG^GfS&tD;%v(JafscQCh2is6iIdvQ*eT%Hr9i_jxO&(}HSACT z-dgswF_(gCV}}CayAv{NEzX>5%6NbnM0$Y*RkJeO)~iK_)JZZ%>~r#t)U$o;7nZ!yLtc-{ca)WNJgMhJZyos|LORNR`26d2jtzWi zBLcaP%=yp_w~~L_lKN7CSLv>IDsMlK~frQt<9v;zY zWGq-qJeuTN?ldM`<1-{YL|^Llv*`aC_4>1dUroNnXXyND_Q`;mich8Ts`A*OioHNK zE|tVZ>_DBE(kt~Pc~Lh@KU;jfR@kvTnE1NYFGFAU^%trRb*=Q5SIjGmEvPTQEIK9F zq7Yk@qEmz|-k`SVr%ZQeDC;68-GAk>j*h3SC;O3h?q&j;WtqD<0Z%zE{H0Ii9DC_$ zeBS6IA9ebm=ke1NU+1Iv{u-O9FPiM>{O*n`PjLPx*wkaRk$fBD|2y#8%;@J9B`m2w z?4HgS=wAzNK7MKWw9D(c2R0ACG<=mG3n1If_ae(DOsUWF>vCh_`$iZKUGQ_qyL`|4 z(JkVSzw<@$Z_CHO&EojLF=wJ8vzWJl`O+u09Fw!n)BPU{y!G63dCybNT_#T$|3~h^ zy<+UMyq6r`A?&q$cR9Wqp(>Txemj02kCHEZoDJGY_#xiq?3~U;?YA$v{dUKX3AyYw zjb5QYc$qx3#NRY|5C4(d_W4G3pYtVlpX96%>ZIK>Jn^U4eM+9(5k1TE1kX8na#!?e zp7#k+)K&7RYbZiBfk;I!ph0@+0(z&QV`ttI$Q- zm`kKrvfq~MU-R2DAH(i5e1pEUebJNH^dfV>guR{qb^bc7m+7O(T!x(g<_|M(yqbG; z(A}2o%d>_!(wsl?)t+^7|2Si4-wA(Q<|0BZ0nTEokNair!OVFD;tw|kV!|mxxY4Hi zShT|*Gi#7jDSn&qk4yF|ds!c!B5s-bmB>E}2iA9Df<+xh8F20*ZVbf!MdoH~Sj2J1 zcD**@0gDOmDCZk? z4e=zOr`hBRb0xxSz`-w6+Z2L7p!nV~GV&LCu+5cV(l+>@ySAB=boga$bBQxsghQXh zL2T0(Oq?Q&tJN9!K}0uIXH)U81ctY3tet*>S-ouTeL|q;1}wPyd{Jm+GIB zr&y=GzLRoLE8`cwEuf6q{#ZWcEae~Eq~B8hebWD*BTw*s-d^8`%(YX$!~(1EfhSB` zZ>`7!=YomHZ9LJHy2^~(JBVADxJ7*{^Gr0D80GuW>0~&O4s_7lejWAN7qwnHmaH>P zy+(T>{myoD9SzpjtR zM&ZsnYXmH}PU=+d%+n<6SNUD?W`l=s17`sUhLqbRhT*gZAIVg?#XnWf#Sp&?Q3F_79dz8C=R8E+ycWI#|M`RKG6)i?Kv!5&fUyI_VRA`>5JC zANxKahXDeXJdRoRezC4-b=nIveX{tbd1J(t`9h<&iw_u$D*LU!X4B+ljvU zo&3E`ewTCse?>XteM;gXa^Z!)ubdwuyq;BF6*k?Gr=9aurFN>qPDNmN(+t@G!7nuU zn>vf>AJdQhGHW9EoW%0-eGi5BKR_5JPQp>b36Lg~4LP@I1>f9YV#wGXJjN2j_iv?s zIf*aOgD*d^u8KQ_7D$hIep{rxQ1*Yy*zq>%n?00&sjH4*n?2M+UlSSm*gWa;T+4bg z|8MHLo^bibU024Q-wJ^%ekPxh@WD>=GvRkT2FVBaF8WpZb@~kJbDusBC7*dGOq~=v z>h2$|&F*J1O4uvrC2}&%OWa5wUFt{auZKC?kM_XD?;O*2yS7X_5uzM*7=L+V^h0dfz*r zzN^%S=b1mxyXhYnJ2|s&J@!0yJGKX3*5YL@LH3Qw9K(t{(ndN-8c}~nT7zcaSY*hT zkoi1Q=M3^aY3%l&7h35*HxfUvL-q8Z8&a6b{xiS*;N#0A@2X$!I}5hdKNWKK z3UynGPrtSs{ge;ttBmai@;R`>V9GkR+`rP9>yL4kL83jkF7~;fFmEQj`hDiCM48vT zfL~~Z+@V5U)71}`Ql5N^k4?-xnOika=4>1PDsLoON&KrEULDG-1720i%UzE}CcZ3_ zHqMpAuh7viSAFpjDa@dcO!N<<|6o&)b2EdnT@Arldns`&28()-_1Q$;H}e?lGROLV zat~5dF!B2j5LePIpY*Z)4za@>>{U;td#GAE znH=92O2|7h|1WFwZrOfR<`|KQugMQ z%9PCOWSd_&1}C%cDBGUH{VUJs%oCrpy_Df`;wNSJA>T;n1m)4$=hQi?@3st2Qii8h zX3OxDmf`h}%cavXt4~U%q2l;=i{W3ouezq z-}%fC{ z-&=t1d;}jA{@)~ZgYBa}|55s<2eBFUNR{bY9ShVl7T~vxWg7X<&=oz=AOGSPn}=dw z?WrVO(?6pgP2tUIS(>FRkqYeuLh&ZRqx@v#hy2gL3w1N!M||^s{?+F7*V4zQ4)`o`X%EbNF7{}oTt*um{4vEJ z1HUH=KJqVFzWh(ynzSQzb&BV(;tYdRp9KfG@G*Yv)aiGvf+M~oA!L!!WI9*eZ`$Tf zk!d(@*1D$F2}kOhTE0a`#$%>lr@rx%4uW-`ixqaTsuioogH;nuyic$Oz^VoZEWvpv zvM%fvg_q2yOJ3>%udCdbXrxZqNnSO6HkA$@{nl+V4wil^Iwq~$dL5bg%Df9~lQ*f? zQ}(0nFuK&!-ahYR;`hh5qI9pS;)8#@Nzl!JfMxj)x+LTnep2Pj;s{E8fYl^RAR zVGL#n1DS;3xBQA6e)MOJ&FIT|`@e=C--_;HH~fa+P|jZ|epmPdt@HWfYjvN=cjR}{ zn(-HXF~-%TZ@&097IN+(=`8hb-o9jC@!NbW-)y1Hl<)JApQn%8S!DX+vEMs&<%!*e z_`vcTbM7%&JL^1*e*k$$`nP+?-%dX_=C+0FrJswQ^mD&QIHH%<&*k^DelB${WipXZ zU2C4ywNgg&sbhKW&7*$3iLf7a+Ax`u%GPH717WY|=kCu-pO^lFRU@4XzGNHezj37V zoTrUsY+%B(ZRABC_a%GU$V=KrzUz^>KNh?4-gyaoozXUO-f1KEU9;RqBJaFN{y#^V zT)@ur{Lya8o<7q)j`L(LTF%T~OPd#(6#42AX@l49%N=3=NpjEr^wrxy(+@uBV?88) z{33nf0{X%Qx<0pIomm%~EMN}0u`PMl^@h5ntg&s(tl@1S&DnoASFm%MZ=CP;CBKW3 zW*ZLqM)-cNk+#&}?^LW49{Iz1p7vShsW}&be3?Ask-2s`H&D)djGUs5qrI2;`sggO zInu}a4@_vg`3idmUqw3XB5#Qj>cf@a?!W5jRNKk~YfF4=d4?M-+G2~YN?jyd5l zFT2h7c+)K`xt&2|2pjjvefBIoHzoQH|0JP*KyG|x1j`8;7snU(c#|90BQi!uj$N9h3j z@NHZw9?CUyjs|NG%poUE(&kWqe9v;GPBLqfJsLLsu8MW^1Nt1DD_S>Qp>Cp{&S(eZ ze`Yaz!Q>1k($b`>jPcBPDe7R(E9N|y(JYwA$1LUy^-l+FzMRj=d6&$;C$U?>-Ur_{ z2V3IU#(aU*ZH3SE*tJpP*o4j@bebKVapf}(pXMxlkoSSf7zAH5?&jTtr{Km;EPvF}flljs}H98lAiF?8O zEZ^S?-ecf3qEFd1Yid#YB4#YPk-WE=e#M^Lk!UUUex-hu+oz}}Pt(u46)q+(*AjNA zlSXd)U#e;3>LU~V*NJ`&x|@T^vElu>W^DNL8Pa6RNfXLR{x_5DN`7qFXK4V8OS?%6 zz8%F5Vt?VYfp1fLu=!rvRpP%%($|!^LgLH+a)yV=U&Rjh8qHrd@)&jb^%Rrx6Nj5E?b-Z zGdNs*ocoIYq0i-gJ$KnZ^!j%h|4=u4@eeicwoO0oY0sy%J!db}IDG#9=pTB&{^~ zlR49E(r;w0@@irJmWC-lsS>XEqptm^;{!<_*!p^k59D^fzen<_`e|6`g1sXO4kVtkr&Luz~)+8n_DFOX=j4P0t0M1gu9vk~)?exq zdm8H#T{Zk&^e4Jn1IfJ)m*i|G_W3UQlY1YQ@ZqZSkAyDs&b)5-6`suLb_W8y^UUSR zxVF1M-gy?v`|6SIB6;UoEbqA^-6erUNXEOR@_x-ocUd6uGxh*~w4C=>+CxzpF#CnQ zdnop-Gi!R2_cF%4ma*#B=*#XQKk+_yFJsti86)4zc-Y9p_S@-Y**-J>k=S!cpC^*f z-4~YkEJj;b#ZvjdZB?>fXuCVsd5Qi{rn(6E$4C>gbEE-V*JIz!*t_-a*q+6@1YBZW z-2wJpVDAq8qI@^cI`s97XBPa+Wog|FgTHq2nYN3agk}7==pUC`j_rJNn(z-Xc0c2UFJrJXb%~Ag z-D$%3crdZ;I@U)dUZOLrI&&VKxw^!gE%t0(q5{3hNq)+tY{>637VV2=XbTj9z7$0KNSN!Mk9W?VV`NSjTb0cYl zUX*XvFn`fCy(+fje#SnJnEl;*Q@k3LSECar@0bRhuiH3X0h4z#&s(3f>@U`!I1S+N zO~y1qp287syoc(y#AP3ABY9u$bTDP$pnutJwTfE{?!hd$$UkHGjVeEE6)Mh&%h_tF zxRxtcIatG4u#kV+VhO+EEoKjE#X9Z*oW46A$^~=K!7NhDA}|NDU?Tsh#gurHCZXId z@h-t<&;O2ahI9!iZU9_#GTIaQJr*}W8R1>h#roQXE(ey==Zc>DcBLNwruxiy$hTTN z;mLdGa{Y4p^pHLqsiS(G_%A8$61crtcp|^i;`V6zkcUIFfp{e9V9hJmJXp~zSjgXG zv4r1j{mS&22D9D4Jf)bYz--TgiToOiDe;!{nXZp_3BIJyL(AzS`5@Bj;2&4~it@uPTmlzs%(rH5RCcxv{jOmX2?=7D2O zrcI}A(n;_oo%VUssoKGhDt;9F>MZ!kKW6a@meVQJ>ZDJb;x3qj;q0a(#P|D3Y)&!QEu@h?w+oV6o+Cl1$ z@wB!!nD}RlCH&6St*qZpgUP&C%6->Pj%TqU@*6Cc@N4p< zi`>t3*uiX6%tkN|XTe1Na)*|0n=ahR)g}0nE+>}L#gw0e4*oBBKi3Z|eu-0lDxLIk z&ja9_jOl|sg(Ez94^=wpGoks_?Hv^;Vkh-{&mZ5XgPgS<%c~Izs~-Maf>ND z*(b4<^LNC4rk{NY%sz+bWu5D~%v@Jr7M{re0L)M+^B_{kT?*1J)+g4IMi=hFf8sdf z`0pp@hfnx?KVRp|84;hP2hg!&K9hb?=3mi46+IfFhj(SN$7LE%19D+f~7@+QIKu z{BH28GvFf=e4oXyf}cC*+obwU=$B>CM<)6|e~|Tv7S4_09H#J~*bP4;c&_?pFuwZR z!FbPgzAtoe_*!x+@dTv$e(g>3@6Hzsp|_IPNmAJ6ix+Mu#kTZtT6GEa%!-y<$PVr z;Q(^(KTT9pM#bN^v(Vt}t)zb4#@c5k?VowlUop=SeeToeL;4Ks^Zw+sg1N8t=yz6A zx0cth@b^Xjtq@_#y6FLlBj2R0oAUdwy>7}pBI~AmAC*1CFHx3dy{MlsvdkqPg2T$W z*cGGbIF%hEzc$UU%l-2guL_e-Sl_wPIN8a z&0aD7I#wkjfz^rINR!>sRXp(n?&isS@$MdZ=h?dozhIHE54WHH2Sm>QgZw`v^3@}| zhegKo0J2e$u~+w?$asz+J1nwmMs^?J|8bG?|Aff$Ms`oiJI^V3&mY-+T;6$3%X`7d z?vwJ)b4K3Rut!+ldCsn4p6$Ak&>YWwg(IQUtCD_MX9(jedxMQ%*8Hkh@XK2E$#VUx zQodT`S@8`KzpIoC8yY{6ulPoWg7{?N%WV9+N?%C&b3NmgC(mlQ?Z@~F>>Qip4(!Fp z)>att@KjlF+x$TZ$U9u|&ZMY<^;;0CwT zPE6)4&&N$E&J;L&lMx4b3I~iR@1e^R zF2R@nHf6=mV^^>)6{&IXdlbJ1{F*HI$P>FXeR?$h_Y?niCw)2;rvn_m$(TOKQ#itt z_fWf&KINp({qU=F__Zl+8@QEOaFKuC@+((HX5bgET{z|*v9$1xljPK=J z(PDc{_Sf5hRoS#>v~D{?P4WTN}0=swtRPyB)Ud*Zul?}@i}+!GJC-xCkD@i$E#qC8y7 zTI(9l<|x3ASbTxRAMh;W@C!M~489S0%*yp1dWuNYH}I(Dm8tZJ}EHSS(4AXc`*ug4S zta9O%1q(Uz2Fv{fxb4IqCa(=TSjCD}3|4oB^h754rO{#y5}qvwxvHCsuE@RViA;3g zX?5Ws{S1@WF6Ac6!6Hv72TQrha?tN^3n+d-;+cUPGQq#u;`ck;F6Jid-ir|F6jBZH^sID%MG` z+B7cSe2)C27t7}J76)rev8KRk$%2LaPc0VpViazveBR_>9Z{?!U|pq7ecWO-5uVNG zqpCZKuE@RVgG|zApVftf>95GVSHI@-0nO)wns*u;ZU+?q0Ql&p;Ug3Lh{bPkxb>-S zAG#vM4sNaD)`DA-1sC~h zYZHlARYTm=h4g6+ZmHrHIk=UITM2Ga7F^^%3u(EGf?EnM_|jK4eV<(HK>3dpIJhN> zTLNxD7F^`twYbDx#+i+`1Y^;q)yX#GN?^Hso*i_`zZG;&6B4OQ^vnf8OOh@ zvuAZ|y^Q~uj|um#j!AphJ{?G0C-IzS-j{Db|CiKd=ympNjfH!+#zNzamH4gVY;vwP zV82EFX}(QyKdIbj9PXpaeH899S-2zr&z*2TndH8H!#h&rk5oSIR-A5dY#H&|0Qp71 zxtyHaSu5>F%WB5ujEyGXc7k#|>2QlGW)#fHEZmU)z+y_-nW#?$H-g)=dU?L6S#g@d zv1P=|4f)gHgqz4SEnDOAo$P(?yf(=31*#iWva2KMN-E zE-=G_M_C9h-JR+e)*+8fQZC3}V$;+b>!nr8d0|m~kQebm=G%<6Mc!ZKnei%c!sJ2c_g zc~ZlhBs{*!XlLXvf??_dY!T%>G~w8}N$uS1#A94>$H8sR5)b6hS==Va&V$&b!ND3; ztWmHUvS1 z5qUFs;c|S9Ywk|g;qd&A0f^WgAJMjYfWPUsBW92=(EkQw5A z&WZPF#Xb%8xh%Fs{vC&7XvSm9DKO_9%#(_F63qE5n8^P`YzgmV9ldPJeaKrpb@V5Z zrP+5rr}}7h~28&jjqVOV;y9oyUyw!fyZIZ?=3mWF=8uE86&pkB*%!8 z4!5Y{N5Mxo4Ii1{=Ue3ZK90jQf>J#zU$JKA8_JdrE#bt4hJ+2 z-u43df0OtRo8K8b-c{Ss=_N$n4Cc7QXbBcKm%@@p)X*c3eDXK}#U7ci79LS^)!IvjgMHJm8nfRQmTA%EHlC)7cD+x|o;c%1DYsd8-5u9)p$ zR%PLie8R=#Zh#eIy#_GL9n5CMYzDJD3nua}f*ES4k4s#9%%?tFu-f#)A_bJyV)mR$ z-=4iO=AN2T2VZ=NO6wAR!l{@&mL+x6YxpeUm$#oiwC3!6e2G34aK~D!CdjcTX>}?GmS1`jgZVkpOtyB@(G! zzbXGAzd4H%pNUxhKELs?$p5TAbx+rToMFx$mbalFW*>@ReETWR%c?-j<4sE`i(Eu zE$1V*uRs{mrru)hvV?7IU*WXso<>*X-Zl@J=oX9a9C`a(9cORhHwmW9x7mHLe|b&P zPGP>0xG%QyJvJ*KUUpAx0dY&6Nl*Oj{@7AEg8}=5CEpz4`~kvSKtCdOr%eh~lFw)S z=B~-eIVaw~f*`#!M*`$r;o$;1U8v=_zo)OAehrx_#*GIHa^bTQ?$*Y7WRw?<^-7E z2XoTF>{iTfFekHMBL9fx*#gfA&Jz2$*4MHhMX=eEV)`ma9PDPrZU*~E7Hs4Vmao{6 zeJRnyoFhh`#k2=iflO$C0r$YX_B~7ckjR!rv5mq^~VKI{V}BV zN2%5y2OJKS8n;R(eKXV_ueBTw5Pw^L6sm3^x+3@1AIPNs$Q9kw@GhnP7)q}{zLl%> z2j58jvG75@m->TvrRopjma0F9pRGTnPJRBXSDtv`jmbJy%A!ff9`4Vn;|}|WI6tD- zm)Jp>>FSfS)EG1L`{?I7HYm_|6cCSr4DmoF@%UTeCFQW6I7A)&dG)uS$KO7hK_8ju z|E1OMrCpLb<9hNm_m2l(%X5v#_FyoKkmp4HmuV2>&G7})5gQQ-&XM%J4p?4AWn-Ww^7ZF?N)@GCS$3aSz2vCw<3`xX8L*G3Q@O*X+On(K>XRHPL}8Y_U|u0AwF%w z)$8Y0urGNwIp-4YEubDT&#*r4Pd?XBr<=1R@!K!hn3SzJ|HHQ11#mq?n^oxHS{O|H z58+xsTHZ-I;74Eezql9VB6;Zy?TDTW+F7~I0*UEi3%Xe#p7JTvWIbXc}qA$LSeTok( z`r;2R`Qqj-KhCAi+s9qj)b)9CUjO&Gl$ExB5j47vo2?=M~7*X$WxIfU*bN$e`F8! z^L>&zLUiGu4IACq?Br+oVp&K+JO zdrCM{N%oA~dkbw{+gLMuN_u#Y-FxTqxk)=@o{YU6GM+GJlV@vJn#hCR`UIKOakonT zB2IE=XR0n5*1Bkzx=7?+U1XyBdaFwt6FL`+N5Kp)1mks#WrOrz45xb1$<}d0iaP{u zmbOZ80~WX5;pEo&BKLAaCc6LrevMBHoG!tMaKaZooZPmgPdW9$37s@tWPT3vkH3_Wx+@OW-m{%r?eroRgnS*uT=3$ z!ISn!%8HjS@>dIA>Sn_iEJ^$GfBxS~B>kno8*L^1=_7%?qJ26qceu#!^hJM_yJHKa z-9jhTZc(?Y-bep}^Bg2z+2#!SHjNuH;r7$_X?{58#LJz>6S+5D z$VB&fs|!|?_{lvKyXQGOoIV=+Y!Z!(8+Otcj>5eW?$J5rdD^#}zLRSIN$h_Wp5L%x zopyMRsqPrMBKPt{COi+JYwrJ)II|Z%!2RPt#kVEGT)eU2fS)rJ{PA|?0(TAjlWVP~ z9F7MyoP&gePFgs~B%Ci=j;9=s{i@rKuE@O{k%{h*)uo(9;n{^fL&J6P-C&2AXKd$8 zo?QcV@%@yu0m|7v%Go2=`Qlw+bI0(e#&y_tO3(E;?r`tZ@cRfqOTFNy?0vw7f862T zp}HODirmW`ndok}x>K}&!ku~{By$#x{&+~*W6soRl>9mFGxM3LvfZxu?ch&jz(*$d z8!i4gZSR;9w+7X3KwsKKFF$0WzYhH{XYzy^nRA9;fc5?y&h)6nM>SHWd7z9uP)^$4 z2+vU`j2aE2hA{XxEevE5Mv3J(3J+U%F$ZbtF6IK|o}4sYWTLx9_?2Tbv0FL!eU#O$ zxSQuk@8upQ@`KnmLR~3&b`R;Ex-aq}Cl4^LGjSMl;!vVIO5l;D{uLfSBplN=3^{Sg zRoz^4MeemRGSPk3>JDlip#C*^fbpuy1B_Ws9_S+v^gG-FiXQ;KKLb88!T*NE?;{T| z=1kf6oc8O^(KqSNppQ)Szl?sEu}_F`kDCXMu?H|xsd?ZeW9%6nqjfl8oYpW-69(U= z*#Mb@@kPtA10FUHOsnoRx+3@551Hr=3BM|ACU%=)zUt)4`(4W}U)la!&jBVcnA8b;r;Zxi=2T zME4%6OCFQHff+L}ry7(xfH_s;XEUt*gGPtjLB&4^KDuf6$OQi_7QfNq_DjsEitbvg zi~Xc;V7M`tYPdy}TeqhDW`|q9a_fg1x@p{y`N7Y#_?sPW?W)_3uE@Q1LngY3-_&x~ zLfHY&aI05t4O(u>DL0i4FUBLLZec8v8gIINw#p>-g2gVU+>|)_wW?o>en|#y$b{RR z)u$gM+{}D$iO%U?k3aa#s^X@Ba;6fR-gvQn&^is59(d~TeONn81KboZ1p-&?|b zZv*qa_00EnGv6C!zPE$<-eJlQbHAZM{+fdcS!++*$MOpEy~V4vyj4@y>gy6R_q&_^ zjI7IimOf2a9pg)$?i^)jfH5H7N?$137*N(`&`az{>L(hh$G7vIcQz+Amsu!dLNGjI z!bCn}2F8T;jsmF=I#O5r3Zd%xgzFNBzmMJJ!oska@}m z83*!gWbD@@GRBF`BIDVDtW{);6Wc__vmIH7$X1Q)iR!qK@$M^*8yWMa<~ZGPBl+Yf zoXxX?vw2eUp5z(saAqdl zVtZTemelWMsXmtSD{{*nd0Ohwv(3~~*e=pb-m<#As@sdM$h~zOGSQtwH#A84ly8Ye zhG^r;w#1|0hD-4)EZMSRj24+7jaqVIvX{&FmoOIp68hmv^uW0B&)J*RLs-~|xsknc zwz(M_wQPyCPct@ZARKsx7{7-ZyPL65jT7G<<=g{jzD?tdOzN4h*!b4)jg4=U>NcS( za<*iSDS?1uF{IF6xopRrsOVvC&2y8^PU-jY^egnZvVD!)m-bPu4z@=~CwKtWn(> zbVcsviA;F5qANa0Vh1}m`U*ZSkw&#)33Ec6%iCVYJW+WdIW{VGI96*o)r5miS~$og zoMy|h*x^{Ly2a><+{+P}=x#xm{<}&0P2@98i_L95xEEX7yR4+%wZ4vKjxg7WOOf&^f={mUS&d(+mIZVF0rFKgF4(LxKe#s< z2*gtPBUdqU!QgijCocx_f4^VTteN}~sE>6a>+%QpHu~3e1^s2Eq05e6$`$P+U!jl8 z_XhJi4=>~?Ot7N7a|gll-m68{82i;ek4>KR$H+C77P z=^xM@Z`#g#*E;@A(I5W^b?q#Dw>gK$gz}hx$DHzT>sw^f_xhNP(=2_rX-9uh^#{?P z&Y+J>^mkePQ`lPU4BwFWVYD(X7e9cdAFjhJ(34x4YY!% z+{>8?+x0;@pkaH_LINB7T+Gj@f`key-u8$v|qnOzrHsM9`dKbBR`n? zhh^>5-c9^PjKrRDTBjHs$W-Gi0LwsMV*wFt(6#HpUoE%GqI_;_q}s$G$BN{*>ZRfsbxl zUPLDNzishb;9&EyJ8l-aHyl za&2(9jw;ttxbl5kc*rEY_gk(F4%a@_?L$}OUarVQcN@Cl4)Q<;WsdP%c!2R-KX=g3 z&bw#X+`xCHjkjfwypXzM^AKgPntY8cQseONRSvyys8J5yvWNUtmVZ>!QhdW&owZMQ z?9DeOE?ykuDID67DDR=x;BtFh2EXg!SMKm@QGPA(E6>6Y`MHLv#0#Y@%H_NBb~GUgv-10oW9%f_ME=k z@%CvQZ@=S_xjy0%;=S`Uc0a7+?IX^3+wa^{j=Xb@zQ-)-bBeS&!e4_Qe=i+xH_Diq zadwl8qj}00TgKWg^3IcSwi#o~Slf=VWxO4g?-*-$^E+ec`#8&YPp`;UkL>9~)-N*t z9}rpY$euxDLn7nvF}kh;lb0T2OnC2Ctlm?M_nr0NXFTh{KXBHApQntvWBP-nuWcJ@bst;pmaOA4 zsc(PUL^&XRBFu-RbO&_|GsqZ50y1b;AwJa1?GG9&WPUQ{!;!QT!h8 z(M`ig<_CYw;@3FbI#jmpt=p{irmWyndoj6-BD~qz6y1aN5|nd47WD84Q+|-8Qc=<9^em- zd*Ha6aaxqMbJ-i7AJ%h$YR%rKz0zJ>16~(++(Q|QG*NfWQ7_R)h|*4^@_xN?tA`uk z7;fHn6M3$UR}?(n&76IWv*&HI;%o+oZ!+Q_PvJCz!+WU4xf`g6y5uy3fb>g;9!+1Rw-BoS+I~lZ?V8J91G!S`v_OGj}UNh3lz5i z+&~swbF!OK={BI@4G>;S zmUKfdIHnC9z$Uz#x**#W;27HuV-wz!Yokuta(zJfCL<2=6b`(jyoc(YvNft@>!4%XTIE*@zk^w9i~Q4; z-zc_~w3o6~jBT0wN#&z*#VQADI13i?{T8boe#PvmgQt|O=)B|yUDF?QaElbT2wZeb z+}*heWc$eHVT(IR*tUOoMaN!OWZa;fTwP?M+iZ2CaG(t~_Gf;_#1r2RQ)lDbVf+pH z9B!8t|1$XKrg1|i_?s<$pTo_avk|$se}GJMH(1?nxPfQ5%_z51%57G;F)x&g`@C|S zha0+S_{aqRH5Q*ap_J}P)jcWk%D@eo=&rK5y>RPc-JEl&yN}P z>~QK=+xon;lLasw;Z}MDFE;Oms)A?jW4NGn|^06LU6(QMmoaGUZebCmBN&JDjQ&w;Eh@(r}T`~dpzLE!qgRuvPg0YT4 z{7EK)vELaF#y)-o*(^RKr|>D6VGi;VbCDONf6CuLFd_5pX+9-e{ALfK>^r!VG07c_ zNp59KQqGv<7WNUonSF#ezrhzPyU`b0x6T*ac#ALg)(T(jEw}q(RU6rNcqjV~H?i;V z-Po#*eTQ3pvGyG8ryigkJ&Yg97XQd@+R}XdzCO!3!*1GK^Q7-$p7d|bb7XlgZ1+AR z+bjNJ{I<6%oMmoo6nir__OFf9ErjLHjolzVDg2(A8@om4!nS6d8%yLfpJC?An7`l| z%V*w#=iWT#EN+7LHg{j#Il|I?aZllw^U^2knIo$^w)Fm|&0dBCd{+3G?9W}EGaJMI z`LHk1egwZ5AAX3k7w-Buj&zQb&tzZ1M*g4h$z0cS);Fe;eyVx5b6u1AZs)q_W0`#k z*K#&7VY5#;A^odP`U%Fjgnk0yeEWP3|cUWzAx{%#w~jS>e9O3(6!#LiJ&NlcyCZ*{ z#f5+9G<(mwg9&Mm>%jNDp7V<6Go-lADdrrQd~djVF_GV3G2t4b-iRWTv71>ZZ+AGJ zQoK{(wJV-C?#O?#UDHS6&ic5C`w@+M3vu5_8E$oOrxkY^+}12Sk^h~=mAD^C?z@w{ z+o`yZ>fWg)2ixseG-bg?{x=p|Y%r=ep#N&-Fd7`Z1IqOPTpJY6YXjut4qgR*7RCnM zYJ<(1{Mxq)A(LgI(V&$*9u;xa`oB(dBnl1!1uz~phj&_s`$L&$`(5mE)@#_OVA}pd2aA4zu_x_vs$99_ z4|qvkQ*E&diHjY(xa&qD_qP4WME7RV9ib99Fh{tCrh7#yDZ>s`fedgi|j{`*&1)EAOMtqkc5)-@3PtbW%sU;dc&u+P_{; z``5R;7d)|z_K$bD|CT+F$@Y)>EY<$`oxR}5WqrSkI(BzAwrl5)@+oJKY^M(rsn<4; zx*+LWscj*3%`3Kr&HoSV1*hIj<)JCfe^cZ?TL-!M51I67|CsnooFXlxnazK_s@sdM z$i4Xwndtt3)orAVNt*y>nEvF7z2G$tryj-a0T-P#Tx5d#u*I!$IJsjPk$X8I6W!mI z^sI*u?Vf}Yo(snPv{OFnY13x&O^|OQa5cUfHqP#t#@fqU_K}OOu@~49Uo$4pwsvmg z=*2-^WpRWn>*i5pb(CLozEHE%KDVgN=+k%dt%wN`DsgP+mIXLT-uY{bRX}()UlgitA-W>><~wAf`yJ73 zBTqC@mcaAY#c6e~eB1o1)xBmfH9EHL&A&y*9(U^4gTAtkJ-%Vb0;c~>r zg8Rxk_`2mfn04zA`>4b0qi$y(^)B{NKfpff2RZZ7tkoaSzK{Co&DiV|zu(GpmgfeZ zb3EU|^EA)5@jS!x?L6mszLVzy&%1b@`F@^P zlKZx)w;#_Np?zRo+_VoHuNx6xee!6^CY=-dZpTNgo15S5_=xrMM`hpk`bXw|7u$q* zmpO$_*7eQ!sL}NWSiL+xLf*+-h4>-vW*$Z6`J)$UA6SPs?ZYKsa^Lo%o*A-4yKq_C ziYxrj8k6x2$YtH0bu8l_P{{wRlNn!p*2$84y73Pv<^M8~;}^iXrR?W6J_1$zU#;u^ ztoy!VyR-R~*ZF6SD1be;P+2@_MroZ6S zA+Db;x@q+QGU4`|#b4kX%gtST61leyK_H657c(~n2emmuGJE-^v z!ACa@ADJKg4_W+E4!2>|9Y$B=UT(-l_g<@847by8D}vh``{S{h@jDPsCml{hiaP`@ zI%&Ab1h>ZGo^&|%t8PEKBKLAaCc3v;-BLKs!>I&LXFQyaJDmCyw+~!&(r}Rp?)4V; zxWlPib-U3Oxt9|%(G6JLayXrXQyH8VJe(#SPEo~;f{RWXE;7OW=~k^PCLK;~s@sOH z$i1A9iSF}Ow+c>|;8Y2xiw-B5n;LgGwJL5axag$eB9n33cP;L?!>LJio6r@xmlHD4 z{R^v0T_|haU02{!T}L~@Sqn>?wXm4stc5XuB05GK#&nJCu*11g!p_gdXTI4o+PxzwLrV`E>sS!2tzH@#Z%tHDP%4Ii1LQ`q8< zI^0TBw*+00d$}PK-Day>1vkp3>CXq0Tdtm!Fz8#Z%ZnAa7+iGHaFIzmz02YbI-IU( zU-An56_I;6Arsx((S0D7^AG}@i=g}E*~28h&52IZI(=R~dA1*WuU6duS|{gdETKbN zDf<~iH90Yp4|+8p^f~cz*E7*ccY#K z4;bH@wN}RXCLb`)H~sf+huebUFMxlQI`JPZez(KTosSf`mm4z42hUku$p_SjCLc^H zw<(?9X>&NuD5n`XT_qnJx43N%rwP?%Ehd}Kz2t*GLHB_v)@LS}Lt#8CV_6wzhkAdt zeDKtF$Ol)Pd{C|Vph@#VvlFjz<;EIOCpu~NLMCbXX&bL*CtjnfJBqHzy|zXsx*xZ? z(*DqoFt)}o-{b>)^G!Y&)O^5PN~#~_&Z}Of9r}pHZ*aKvDYrhjiQLN#ndE~et1J0{ zHpp;mS8g4e4{97v?p(=L^1(YSZjHmKO*yr}N#tHm$Rr9(ACzl8sC44x&flVw#toUIWx&R((ur4t#;bvNiQF46WTN}; zTeLo)t&Y-XGqz@q!Q_Mgo3?k4kFvP_$LHD2<=Nctx)3sF>56119Ki>)JZ43kt z+A`FK#M$NezeEr#u~dLr%?k1O*m zxunc5NgEQr_cqw+fjh!3$*gx>A<8#V~r zg^cx}Vn2R(z-I9xXkOrs!HdYV|NgzMe(X(l;a&ohO8x1>1KYU2v&5gO`ddrsa*D;@##p<7Sv=ThERv;C{L=+)n8ni**jmvT z*t^sq8KS4fviik%TTlj`EuIybu@_mxT9)QYhB`0cGc@7ONZ^eqG3SP}dhB0Tnx(!; zR|mCv`qlZy#HOLX8XmDR_>4dtu)^XSWn z=AI#FQJxK-iab$w!*U@ruToaE?3?=ol~Ld)40{^W5?Or|!i5M&XIng92ewyihP-c= zVxo6IKBdT?Xb^9>FI(?>en)1xBu9}fOZ0fBP?ZB@*6{D>1mf$_A^5_(VG|4q7LN=2 zcqDhM0|%vT{kP!xS3{(gjid)p1@!>w!_1bI4WJ>9ptwiQi=#alBY4x+8Oa9u@H%}G zvY~MGr$UyGA>2#h4@7)3!ZPZ0UlJ=aTx9V;UP-a|V(|6qb%n68rF^ZAb05>kyN~J< z-9Ej}ooAGsb>Zx|F;VDL9b|S1(#{S9iV|Zio@o)Rz9|kc_&=9e{WrsQWa=Q3MPYyg z_fq%+fWMRaP^Lb`;#s4@;aB1DCio$Hs*lUaFD2C70sl8rT=Y9q!mNbLES_A-hv>mS zkZy{_GXh^g3afi3{GrTvv?0(L+k&@5N5KEB6gKPIRqO z@Y|syanLcf4x>&4Dz^X@511;)?o)xvmjFuyjCia*6R3P0VK3M?|sdZ^E&I!52)N8fgkNz*=b zVE*9Mr?|;a`t3!&)VEPT;T^>i^F9!5^l2Syc3_NTe-Z7S@a?e660GA*(SA4#cG^bF zS6xZ09c|5xz1Vh(cU=X=S!C9J8nD)VqWyU!d&N$CuasgO0na7-L|z1^@=)CG5r;Aq zb$5z1^Od+#xGuzX;QjRvbs0@2wzk5CzE3@c~&Qd?{ zhM+;T9Z1^}#)@V@UTslmr;=Pp?W-h$RTcvF0`@=_;r)iUFve_xENA^y@V5C)LE{+e zWD)eGL27!!o5>R0z;B8x_4HgSxqM@ot3rpmKs_A;+Vj zGv;+6zevPUy%{=#akKV{xLhf!u3Bn*!kfbquh7u`c9N*`a+ys~>uw(C(74%)@=*O4 z_iO4bQ}TC&Lnn|=5Ar6RC_#Tvjxtl9Ul)xusK4%)F*hiCnAe++_AnTmNMmnf8uXFL zle`^54j(Yc5pAn2jpG!VfaAyn&dZi!8%v+a!#S{ZEA$`47(I`zuecX=HIUDUEd%+K zN6M4rNH)p-XF>mUq94H~Odx$k**pIrq_&r!f%CNp_ll`D4nO}kAmLapl1O+Rle3mK{_V1rACB_7LAv_z=nz$s1KS`nIt)y z=#{fgye&BjbspZn+l36M4O5$D%)#r+f&EGc>fV7h!$2RR===<&PJGC3JiVobI&u6D zbwX`H)n^0z#tYTY?*(u7p?+xWr!if-zejyzktf)1+I4bMK6rDW4U&$$tng;ii@x&5 z^o8DaBz;leW;U|i4^>(plf=J4PjUVNKW4$L^%vNe{M$KMP)OPyd^`^v56>u7|GdE+=e(BLu46!KJ^1( zN}C?foGogbj)zXtoC57JmA6^kQ$Ro6txx4+0q$kDccZK&CD0`UD}v4@`jBR?POf|5 zzk$ljP$x3_U}Zi$hsKP_T37~~ljj!gVVoA8$mTS>AlmjtfIDD!pnOTU?BM?t_@;cX zK)zno%^8GA=3@}1v4i%7+fYVX${Ls3!0N3in-@G&noEE~_@pa7z)tDXngTkx*A9Mt zCl*a6d?WlPp(7617Ktw9+kgLb$hi#iE=8NhItJ%*sU8}h7ww6moHURB6zz)fK455k zyxzL_fouc*RjT3|I@F`LLgX%gn7xRAlOivF_Z$mnn zm3q41N+ygsnzr$4ltI;f=@C))n*h_cQ~Ajt4>jmBw7NYJx^1O4b`sPa00}*rO@J!72Sh1Xd_LaiM%VDh8qTrbxNSN!u*kXDhX0B|=8o)CE z*L+dLcmr^G8krSal{C2}Si^DJNpt1c?7?ZK$M7@*>5-17k731jB|SsiCLXo*MfGzv z^pAxxXCCl2Ph-ublam2ch`y1iX8~oC>c{#@H*4Y<~|64Hr++BOn zE+w>2ys>aU`ojIp8CaL_9_3HEC;u|JN|sE!6#k*vNAa;aG)^R+c}GGR=OZSqV*bdyEO{jmIZn=2eKElYjl zna8n7^aWQSeR;FcD<*YJ^+wvKveu3%-e(%7OhlYb!J{$D@#5x+^0>k2$58qvwO>)v zcOw1%hAE>oap>dyJ<-fBS6ljgOQgPB#0B=V;;v1iPt8Es)}-(eZrSDi72Cy}om0G9 z*G@?xI>?*z{=M#1;PLfu48b=)Skb1GsY^*u=N#K&eqG^Ecr|SvOqa`f&G7VcjFI-y zIeZN=X9CXApbpSS?>3_TrM0jnKnDo+N2M*xqt{k+V7%={e{~J;so}n zbQVA61pY(#2zDD_H2*kRCEDfIDCV(`W6o~W;ZD#dKDurkUWK;PPB69qZd-R#AH}(D zO|HV(azEzsy)^HFo;dsOFN5xsLWfFR=k9ug+s8h>a_|`-UcTkS)4R}j6`?HDziM@O z{4WJRl%I6x#)@~4-eF4Pa-e$R@1Wq!xh)BIBQjaD9dfed3OPliOz78(I);mIcM;+- z?@+^^BFqq$pIKXBg>0lbBCj6+M+ebN{4CQk&pQZP_eJ>^Bx76*;ScrKoH7L8p<|Bi zVL_XH!R}%!)=y>N9rMX{%$dK-09|Y0RsN1ZU^j)WqptGX5%>27JfXJ`ZL16auS#BVZy)X*M5qUcmRjuh!*4qzk>HLi*si4f+m!_#Jqp zeoAXYEyp~)4fBCqCfCt;Olway?B!wmKv;sDY zy%B@WJxQjc7N9yRlYA`=~)V47h6)-dy5; z6Fj7%pD^T=7LyG#Do*K<3K{3O}$S$iY)+Gc|G zG|(=U)@G`6G@4)H{#eo4CrbQ>Ax9Oi)j13J(1wW*>ERN83GitiMd4}LtRC{Lq%zbf zWhju=WrAiA*&z*(pGndchpjL1j{rY*q?4Cnjf1)%-53sGH9hS7e)iA2Jw!{6!G9bM;oKR23ir06D7EW+4l(A5-j+M#vZ zj`wzqsrHIixHnB@&D5X0e~r=~q2D>}Pvw2QE$I>bmnUU=XYIXxx1p~^dnmFc&7=K_ zL>v01y}H!nM(8c|S-bP0_Zs|#z;F3)V8R*nBetmNe7$VVPjEgD8;*6Q&Tq@SO28JU zICm0Er}x_muNJVK>CUGJwh6GyuMx1r+0MrZmK5f<%`8{w9#vyotKz*f#u=w0kwPq1+j*ektOp*PNnKB3wAC%~TgnL=-(b0xt_BmFk_ zY=z$C&U*>=7GQt6PN8?D^KODw$bQ>f*VF$Ii{uQ^@75Ch6Tm;2Bj8sE_$>rqZ1CHT zRtWgd1^i}$9|ipETmfGr;8g@)8il@P@Omb&F7mx|ccsH7lYwNES~4-$Aicf(HTJCtx4#Ye5zIRno~F597myD1#qtaBcHM(2{1 zc*C{3=+}JAC95!&M{+xXS3eBv@l>}Cc7ibQ+R`7GAYf_kGvMcpjy0X3^$5pH zb4$QcVLq(w%VS`BLjJkCuy6N6pfYzit8amu4c^E$`6SA*V_o=hEB5T>*FDhZ)n~Z> z1G;^Xg`k;%vZuSh1dRF`U+wbAxjopcgRMi>596|+{LA&(P3hql|6^wZ?UZk^l4l3{ ze9AKq^34DY`6lU*FWibA%;$QL))!$ZK7;>orwX@v-3d1q|cs(cbSuH(OsExG%Ggi zv!jpd4YQ)+S@9X@!%^_M$S>7bV_(ImmuC@<0ddb$pE^VyzayDLHl#}odZ6a*qP!D^ zxwZKl&jl(e58C^4ARj}xG-p}{tKXN5v9^IFI&}E&XGG}$d>!DnC?$-&+Aamp7Nsk; z#RcJwgqIWy58x#MFD(e?c$vZQ0A42Wa)NM)9Bg& z-j%@9=p4t>=-L3@)xgu}9LLk>+5p~c;AwP@<7sql0PhCiX>^X`X>@G>?`Gg>bdKX` zbZr3dR^VxLj^k-`Z2<31;AwP@<7sql0PjBFX>^X`X>`qo{+{dAYF#mn$xE@WHliQ3 z{5LSnc{#?FMd#bx4Buua&>Y5;I8Utqai)RRk2GgaVS4Ap4Kw{N%%js+8Mc3f@z;xS zne0AeufUjuIpFRO_0C*r`*gq8ERS0`!1?MXPTMiiZqKoXOMR*y)9gzx7 zgr4(r7hp!f>}eughcNabMP8!>>{h@SVD^b3ycl6S&Mw4LAJH~MEH7yEeLa*jRH0cuokR??T?D^H3%CK<~Yv@SSet3tTpX_6yYlo zW(d=Kh`lLb#el(iUDp8Tr+f<_F!kg&4f!fgmk!#aMR$r;F7KM0Nf{G zPkD5h$Gh6HwE_P9#i-Y4o!^6S+v?R@S?9#2JiuQkc(M-j!wfftJrvH;`ESe$^V~2d z%rozbFpuT?1m2zw$J;CJL>sjfBy&C0h2j6$2zxRAOTxK5%l$+r9rnw?dsJs{dz0Hf zLv8pB>+si*AO4f;r+n!j^sm4r8=-Y=F`PT*eYqfqnWt z?R&`7KXhZy@i69oB_`H1#70&uoLc#bS~Z(=_Hwd zK1SF>slQg&)Fk^~T!i@u^wETM5;HKq$E%Hh+#eXm%W zo}R~#r7-wY<0nu!Hty(IC4Di4!LJ&gO5wz~S~XlkVeqepucB~j+)=-heg=h69yR0IPMz2O}Msy92<ZtV zhBhH=Icl3%`LV4$Kkj#h7%$QG38x#nW;RjpeK!keI1YZgZqlWb<3iJZ9R*2v;P@*!9)#Ye^&$bY9c z-VoPw8nC4lA02mq{P&6Y0eKN@Wn?kQtdabyBa2nJRi6ewYY|ptS3=>92rKf-qwpq# z6&Xqteh6Vjj$YUrXzxcoBcb-oU5{p`WBy!3XCPvsSLzsY19S;%p&fZ%obG@t`QLNy&RZ%Bmext4E5wX}?#^CwvGVXkUmu*flvf2K@!r z4zyomhTU? zfpmxV{DYc@Z7uS^I>Cgq6o%}U*|P4tElj%YX=9!>C+~xr99ie(Vbi@<_?5Uy#2u;* z#x1kv{6f(S7RwTQ3fLW;8A3j-kcqlB=qV_<1H1@(r*cL(H0}}bOmn^|nCoFJSwwRV zZP~k__cc3n?g&lC%Z)zUINUW`8s0WLH2wp~VW>6fFIS3nhYx4@x-cGmk9iTDpC%hI z=?Lx-ReXhWJhVpfoyI;L>c@Lp@^`~dd>HoHBgpePj3rjo6Rm^QeCe!!x(-t7<7UW$ z>kIHehxXeKUMNlIKGTGI1)pu;vmA3el2HfFy0xDz@wemr1dUfy&=0ijjCayqB3ZKd zbEV|oR_MD8XC$DZz;Zmb6GoZhmClUz{?d#-wfRL{y;mV_8{R=&Rv-{3qd3;>%kT34+&^#Rbe$lzGpJFeoQmumx zN*%N*b@7$iD#uVg3!DyYqxF~Ce?WfzhH$Z`(T6y-UUp*rGgQ5(^wf6K zW)pq5WWpOInm+u?)93g1<+o+?b0ISJkfmm^Z>{$6sti8@4>H#WnfKu&xAXe{Unq1Q z1D(f)rt{`cpwqQjp@V&A>B*t#y!;dBIIIdC>=#Sxho;l|6X=xRtkA)JvE&$<&aZz0 z9r-4O4)&R)%|p{!^%LlH%~$AP|6F=_XgYO2fsW%wg%0+)rIw-T%pZb|b}X{ZQ|ux* zuZyz}MY$Gs{uumS{Pm=lGzMPs_MHONNScCx!*K2XH+5}fD@Fb5H1 zSZ}HrKSJ9t#w*Vw*9Y5hIe4Q!lK9iBV*%y}G-od975r(($}GXlAiI&;#$@p1xS@^n zWafQ?uwLRqO?ZrmlIvKde|vw6A)YvLlZ2}R9| z+CH-w_Ax_LG?iB^KTG_7Q|{E#pH}Y9f1h9O=YMLsHT6m5o;y^1qKv#gGycbNh1ia^ zULneZ@sHnUBHg5OH`@7Y^@qW>bKgK22CwJT_H*Y2;;CNcYlXg28->mns_iHX<>U1} zTiz}oPjKckvYR@dP{&AhttxbMaK52-o#YAscX_)15iC#X1^89VbNL10f2=(JNzs%i zFZq8ek9?`%ALkuCGax6f<3{s#Gx{EWW*p~rNY72MeM;3jzjiJge=0bxc3l*tQxigm z)78*n)ucmWeWRB9+)1^K4qj&&3I$yg`0GNOGvOSP@mLh*N5L^!rO^u-q2u+ryZ(fI zeF^hE9rpS4*zd=ki~GZIZbWhh%2M8=KDsSANjYnr(f8OWk)MpaJ*G@Cr;{1(JjJpi z>^(bAV*g{pzWB>1od^3O`Ix_yN3ujYIo0zggr!xNZ>PQcPjJSL{VK{YCqLpLJXy-z zE`OlM{cX#x!|^4%bg|Md>|yWnMwTL6hOi!C9m3NQz6#-Rgu@WNde`AAI;K+IJqKs^ z8QzQQlNuv-;Wp&ddvFf|?Wov+Jr6@Cn`6da`!qAowP7tn`|dLKymKFc?G$(4%xKr9 zysjs5Q`w0Q+@saR=-yfrD{OCU%9q4R^-fpk>emu?;0+w4daWdAR&TjaVFemk%4HRW2&+`oX)lC1QNLK#> z*5h>kVd0vL?LJ+06T^Sx$UgZL_@MoyCqL3V*XXnPev%vW*-5A9Ohb0lA82l?@cbBX z@4?v%HT;YcCRucy2~3cq#d$0STYVYXc40f0v&4BUwj{-QCBgK1zdS~q$70W>Aq1bYpz?czKZTQbp^MzFF-zx;wYkHwz3+?hzQ zw*h-ioX28Eu5`u_Y_9B=-wvL~VstN&;Qs>rU%~TO1jow(+t?C=U+xW_$0GPyTA%j< z{zLFQ7Qs&v{GKS>`M?>Pb@TZdni*#k2hL(wqu)jw!ddLA(5|MVeU+h|m7=}Tne4yt zeF?Q~hKcq)K*!J*q(k<-8t6}G-&zzU+M)wv1eMRHN5sl4()(qXxoj7p zUxj?8Bfm1}P^pXCCiV;41Rt-J`DY{QNu8%vqmD8jBmrykWcRg_+|;z8w0I5d*65Eb*Wf-U=6scFaE7TG zw(?%Ay$NSiV`;H`oR`z&+*e#peiQt7>!A+!cNQVt80vpbN*i8z-KhDz^=XSH>4Eb&Xt{G>WWW0H+<*fwg#)yYm z!>7OhH{dkY)J?{J=%W_JvZdWVvBoxqeVn(NnT+7ulw+-(yeB;w@0Tn={wP1%nm-D-GwvFNfl}*iiR*uhmHX%Omnb&WQA;2)=qx)pTJoG_anH}58Vfs#$NhNJz3mlOE%W>(LsA- z&xzo?M8)m;kHDpQ$4U+EXW&EVznFLQ6ub_bT=3to$zKWGwn4WYXiJM>1GDX$37Zqn zK}$=XUW71XOP*#K)*Q86^%U&CT{};Dlk)h2T^brsYn@OVVDn*-AL@bMtCh+AhqK&{ zu-6aTI^R5DD)pTpTOj+5HRlesAAx<(r)T}G9b#VMLVoI+>!;ZFo`SqCLOG~^9ix^< zw&lod#nZS?^|k}|Q--1!Y71%mcTmRZreJ++2$6AUI^QS3bkUl0zYIx7^DsJF2)iDC z@5M_l+AY~$5=8uydobJ@_Mq5bzZ=k&mZz7SsknDZk%6>bY zo4v)zn!ijgF_T|Ho7aIiJ-!HhNAj@mfcA`cZmINKVP3;GZ06!dQ;b}%8{eMv2 zQ?RRc>DJSEcV`#w<`AFK@m24S87KBGI&f#_6y{`vV~UZRk{fdSx{l8D+i*`K7JM4P zJ5P@^x;*yYXJc_j7c`P#*Cad#Y~W&!Cf=baS7gX#g*u`*(7`>O;9V}%0k=hir#Zxv zM0pTh&}-`t?EV&fV-K@E^t^)^YdY&A&O;kXvi@eARcETW?TGsB20Xdt>xr(``T*9vJ}!6shsh(PpA!;Y}2G8bf=eeOB!jf zmF9DCXaq$vj%#i2jAz0F^KKT8n>fBy0cePP`w3b{LI4 zOnZc-KaP#ciJcM?=NM%%Z@Q$^{Ix!&@N>LJ(*gb73)hQxnHY4C-rT`^DoJ=}B}s<< ze1o@kzJ=_L=@a-}@Ou_B=Vr76E80~x=;p0&xg_t^mbm<|n7ESlt#OwjpGEY}4RohI ziQcG*svCQQbHQc0gy{MRi+QY0=5N;IF~h8})1|Cxyj_vcqGv6_`o6Ss?SeH(TawVQ zAXgV#SAg@u`MTLFkI=bngpKg$v+GtaLHIN17_~Q&UkvmL{Y*RcjotBf&yp0z=Q=vQ zy<#Ec*aDsDzKk`$JRAQ$z&-lk?qpmxGx0VIm3b)LCYjLQF2$295)`?ROh_KzL8nMI zq=%|ZIxzm$<1Lc0;FZd(%Ap!^$YW7;d6f+d#yPGPIY`As8nTkH)NIo-ATCN;RSTQ3+1c$U-f4{o@8yvC}6>T<^n8zSI18<<=E)!q|yfwor4MvMu#yzUSi!A24 zRZ&?o-l*vWeG}e?X@HCSP98Hp;Lq}64B=zg|DUv>-?-0FFH82!1MW=~F1LHBVU>A% zVgH)<&HdK*%_vI^W1boK%=8%yv#5-@%KUc*1HZUOMe+1T3YEbG82a@@6JW&uz&rrq zt|!E}N_MCN7;_BLbdLdW)?ad-t*HLH!E-j1PTONa8a*lv%-QHoK8C%wP@B`D5IQAd z&(~857k!3Dojdv-$>MiLoyoyD&Gp{}hK1snAsyP7XFA*++VopR`VLKc%Mj_+{A^pY z(q-^n4LY&|x&vCYpDb_2Iv9SEow_ExZZGJ_Vaa~X=qn?_i3=>3ER9_dl+-%gx@DIX><;I)0p!!tT|X` zeh%NW#kvEwzJ4h;{%+#233*|qQ;c^TH0>E0a`ec!e% zH6&b=Bd42kyt;y%HoVOwDgTb6vk0@1honcDuvTRzbI!F~k0$3hR!&-e2KSmMkAN<< zw*YH{lXLM-MKbfB8pAxh(Jo0oCgewV-`ns`nR@rV4*Yc>UVX#Bk8l*^p~@-%TUB-1 z`8JTL$eZ-wA1KR(WJYy2lupR?XkUE)hVe7oB+u7DhvfMhxsay^?ojfa2L5s}C-CZ< zvG);t>qOQ6Vc<*5VS;g?HZD^--bX(4(&JN^l#9GpW^0I%-^dryR zmx8~%$avnLsPQkU@nLNDXv9l~!SOCNUe9);ep4vFR@iZ>^@2~8&JN&Vf|ue&f7D>K zS0tibBn$apGyN*xhfzmVrVi9G#zoF=oR87DwV;PV__c-XHY~ z4?fSKm^G6gByUai$nD}? zzsvB3#X-cYI^CknuA(x1e6d&y`u1+DXxscMe<$J?*&Z%piS!mv7tU~ZNJ2Jvv*$8O zzbWMH8RIZfHfqnd(jcA9pyRVP4AAXBefi2a41^`nwhUM54{05mw5Xd(El;foTXPhh z0Iu8-%m?>jDs8VnUQvF}^PK+@K}d<6ZG4a_CYva zISB8F2>LDILHc%mkVh*E!aM#O$Hx<%SAWoNJQMV@zPRg2zJz1-2jLz5IMG&!x3fVw ziw)90Z~k+&n4=h>2Q)|g7Wbvp{^i4Cybt7X%4<*`j5$ThRiaOEU`&vee%Jy!pW!VJ z>Kho^y$`zRI0*X%bck#q=r6gh(VKNc^}8OCC)SR^em8X8Kd(gO+op`wG?$_Nmh#?< zys3|E`wV@E($7*G2CQA}FV+9{s^xrgAWyOPpvJengT8>b;UGL4?I4t&52pm@4nht= zy9n|01bDJQK3$Ll>__c1epB0}{zc2vFY?hJDd!NW&GYd9XSQ1vJzF|ZA3=VoUWPhH zxP~$Ru*rfiAJq>Zm*{Q9@7WB5yC+ zehb>Zc3mHOrf>%EKsRY99JM@oN}IS6xE&Y3Rr|BUkQ1G045gP1S!wlAwN=V@mz-yN ztBhq6Xf8{856zh0?6?p2G|={Kx|Ajv@2$|fjqH!4FLPiYg#D1)AE=JVwg?+yMHBk@ zu2s7 zcL&HX!Dhtgd&p0Lh%Z( zoo7XSwI=>CidXp0Q{o#m@efnHQlqC32fzIy-99*M0$*}A^GLSAaA|~C=bgK2=(~e~ zb@BWhh0TztoTJFL(zFH#9sVKki1-w`EGWw7alG(cmcrxqvy^U}>`f{8D zFUP#C2mJ+XAcgV+%oBBexLIcQEwVAfh53QM8~hyxFGtEG=ec26Cs6;A%;&}f>xH{} zFxCKW8-=sMI7jUJE6%*|xiaQf*b9lKxth{9(mh4Be>%Ak@7w#tcv$$gSogK9Tgdn8 z51dOF*srH~0qGy=3w!p1?-8E6>rJs=k9h1EHj|y*_dDd@PxGo|_xuc;?|23F2PJ$9 z!nB8QKV+%xgPLPRy*tp~w(Q7y0IBL%NE8biVBnxm3m z!d^l3#^JkYZqf2t_>6D;bSEH=PFm|0a$3B(Xift|PARKSqC!Z6xdfcIm z!@Gguz**Zcg=nx(!*8;tv3^?x&IY2NulWT3t9XL!?m?c^I#ppF@T@&=>XQVYWE(SJ zoXT}r>TgF~(mVPtoXs{c{7(V*NNnhj3qJ|x^PeWnJFmrlYMmjb$tvZ3C}l9ulN$Y@ zypV_UY2?*|{{#%sxfu8SK)d$i3(#rRdoZWP-P@KVo-g6Yv&yIshsKB9rM?H}Ck(5Y z=K-WMvyFI{nBG{!o5$D}sL_O5>HiHX{(E}Q-N2WIGY{5DDN-K(&qwY^{Bw?6Gwdhi zUd%kVk~@}pZXtI9x0R(x#duGS!c&>&W^zlI=O(xV^sizbTKh>enCAv9{AbKlLGXX- zJ!H!mz@NiB*Am>N_sk@Bm!52KDN?82bF~)lB|X^>gXwnbJy#Om>w3==a^KW@F4x9; z^qz?X|Bc>Lq=i4iJeO(1^~`gTHheE5yI?S%70fe28*YGZYQr0tCtDkCWS(?w_(9BN zwc&Zplb{VdnFswsFu%vC-xGb1dhbYjQ_Q`|J}vjsnmu^uMyt=Z-b}8eq|Y;;&oj_} zsybnX?wK`V%**gEkf6>`AI9p@m*xh~Lu>C=sd@U2V(+3pY7kx;`Uh4YF$k6j81H3OC5BlJNkJsOWg*A1pGGlgvv|1}7;Eq5U;Lm#2SsV|_2)3 z?*YFmj_q(Jw>LkP9_;Viwej5|{$)-4ZxJ7Ak5=jVJ{R~~5`y$vH1vF*iE_vz#J^j# zd_4-hCy>vDXv?1p+Iuy;G-_ze|5nQD2sgi@!Lb8Js}m}(j!!bvF$XW2rrf7n1Q+eN zvP9oEDIeb&gpHWD?ttqByezD5(g=KOm{f;8h zM@@H4NV=iQE5Dz~@A6T3sXSEP7Ptu4*Fo18p+CGBt|Z6uvY7Z9F4(SA{ZV--L>Y)b zdXrOyodJL9T!G5h`GP24ZU}!Ds$<{ZMIA?G1obBY`Gn$MaR1|Nkv>_Io(+*+)dAaE z0)MIo|76ek`xd4HLbs@mt;8Fcsw{eF9tAtF+V+N$d--35-02(^okwEN=HIDevEYQc+I7WoZP7Hpyp@Yf04f%yF*euSp1_z6xhe&2=L z=I?-4gml_DjY_w?1Lfm;9I)r14XAoUXBtBJC)ksqMf1GR(^xb9f$nL8{!p08NAbS2 zV3};M3!W!yc;2kx*|z7xJg)^_7SgGF%C8Ap?}ENse=C49RGqbXKo{c$=Gsa8T?ouQ zdQfLR%wO8nw<2)zbm#FV(iz@g2iunG;h|}$b>4?^ z@OGuXC$UAes~JPkVtBJ(O-J%)F(KiEkno6*a7suxJtS-l37bN~*&*Su!C{rRLnsqN z|DlG7SJnR+#o^yTYM7S;d)b4+*rV2lRlgT$Rexb-(BDfmv4^0>5zV3M{(vhp9d(Vf ztdg7hM0y{f=e4+T{9j@t@!mJ|;H`DxcavU)&V8vrq;~{he?mWsc?;TuPkHZ_^uP#y zRNYhg_}GVeG3EtE)GgKqxJxJM_8``hDh~0AH%>WiD{LoHjG!&!9aa@5AGD_lbU8crNO#V4O2d$bC2>?(Z(3cqV{uY8C>8Ix(xf_h3}A;@4z?< zd14%`e*tpY3z^;l_j8$zy%z1~E844CHFzA{8dCNPjiZf_-50|!j@~2Ir0RIME+lF%NF#Qp?2T}=ud*sP>wL(}=;KmWI!9gPYd*t(?UL(}Q`33SST zrO<(mOnP8wIv@Q6I`RgE4s2_ZeP}vw4narNb9tQ@{}$s;r>f(g|7gblwIaS2{iGWI z65@yIBiroioJPD0AkjJDXRC8)k4l~YVZX7zdXZ??JHVH=k5TEXux-dk_1}y9Ri16D z1W!+Cc-jnH?L1`e17eM5{qIcLM{VyKNpHa8&G(3CzirffoX45=p%r1w<=btA^ZnK1 z6d2;I${bgntCT4GGQ7h@^Ks&d;_gD8c~-IJ42`oPj`|HPPb&Z4Kbl$fGI*l-&Hl52 zZTILb{+H2bi#{`Gml-N;$|IRo&ESt{jsHhdl^$#0W~4s}dJgOxv|%68UW)&0fwp-x zYxcuNrt*6r68Cn?ba=~HkNXW_xaSa#`wkH(3;q#zzl?tY>70)&6>@9CSw9DCffFV6 zZpVFP{QZf0_9j&k-DJ?E{PPgr_l|g@X`S*GZw%sSABfTru8eRc_B06Jc6~yXUc#AT z_#0lwowqmER_yzmNdGL~iz){%*MO(1!P`~faXRkt;6GSpI_K$8G-h8oo@7mQJO7+i z)jLYO(@NpT5GFV69pTP{%qW}!_+cgQZoF&u8PZHaSsB)9WSh0$Nc^X{KL$Lw7m?~d zgfP}gMY@xMZ>mGuqj-nlc(bqcZ52*$_olnM01u_PKZNF9h34YpYFa_l1Goisu=9Wl z7c^fUg64}MG~Ei#ecz~Q1m?(3L^zaLNKCAmm%B~@()4#|LY>;1oo z^q9ZD%PubSpTrnMc-!`nE(~)&3|tC7`X>sfxi=%s-wz(fIK3`QLKRc$y!8)|Ib^a< zNpmm4|LjruONYGZt>p}UX8a4dD->GxOUnE|K*p5cnRw>E2XM-(9xmw`<-g$fNmcYd zUAI2deHQ;Wp*^b_#H;0M_+3)f_mRw1+9~o|j5w-SN_*r*N=x)p-IVV_#1Y*1iO`kF zh^M-or%ChAU22*P_jQ0(^-s#4Qy7xe|+_jyD*;`|5{y3#S=tJ ziGO*7;6p8c-s4GCN6=n=tUul~aq7mva-Uy3nUu zRrzj4`A}~wz=H~FP+;mA$WV6+!|4DzFXDjxkQ@zl2N=X`Ihry#gDCSmPEdW?LrHzv*&oV#m3 zx0O&Ds-tb7tEM&N2h-wgQ6-G;L%y#||Fj|*JB(^MvipWZuBu*J(5Fy*%0tx0rMR;Z zN8wb+oBG3agpYvtkH!l5dZVIw8|XlPN_aZEa3#2O&nHGnpO%O^MSa>ZFQGd3q0R~4 zg1(g8&ecg(l7j0+UrKrRqYRYi4{*JwafS{1cBKER-nBL-Rh>i}rC0NpRzVlni~7*9 zhKjE#ZCte9hcosjjGf~kGt4>LN5G{zsD?{tZdt2HL-~=e%>bP4q0_mLspRMLvlRE` z@Ke2&M2mS|HvXx2cnRIjNa6eZdb_>iI)x^|E`k4E=&~0$R`Am05c$x5!#LmQZ^@M! zxsF$K5Wg6&2xe5`svYPX%CJ`p{TTt-y#gF{oa=a2q;I%4sp=)bDP1-EG~Vq}!d~=^ z1n*v!RMiI9;iy@h{xb*@+!vlu6{d{IC4hya{7(b#kb6VLDV)b>!G2>m_BTqK@c%Wy zWyBla+JOJz*ej0KnwJ}1h2N<7|FWT?7I%&~e9wl8x6H!tim+F>Ze}Yezm^}uZfaSq zJ7UPmOG^I1mMY5}n-#ocvca&jI*d2JB`3;bKn@`3Zhl|0<_*Htflk z#bRHxgZwy~OK@*r*iGJpY5k4S_>XKj@NZxqSET)*IkM$MPejKFSGf0tE0g&RF`)Mr zTTuf%*G9JDKsek#vlT81uLKhf``-0L%dl|QrTI*88VtN67aW}rxAYF)6xnT zxqp^c9Dr*cg}D6KS9=KdqO@YhD7ZRlMGf3WiaTt0)kUycX+>ip+{0`|;~2Pq0xt2D z16nn-NyyH9Mn;&=X0P%G~T45&Kv!XtX6jvH~t_HB4LeRbw_b7m82!nqy>VxQ< zl~(ka;NB{&FrZG&W{Qh`bq4&d2;>E~hrbxnjRC9^37^)HURp=H1oDViQPz z)OF>dBm6D^KhNq$AEW-8?!fW*f%6q~rk9T)A^)dS&xg^uQtg_O&g@Yh-HT{mne2WF z@m8s2BEjS%@xgifoTREp5l8UOdNKFef-t4gj-f-Hfm;ha8f!iXWbpE|DC_YraNc4s zCm8Cz?et7P)}6n1DC^v|&w{)(+#==%OF_2w$4ndx|ZY4_n(8F*iiSs*Qs&}-Z7>6C@x?rE&mer zP4kbye)>1>`R^PM{kFsLd=BEnn#%D}n4%XrgM@!X$_ChyG5;n!pD(^D1^qhdfP{9G z10K>(3b`}A7`N8~M~OdpJ>=byCgv|D;HzWQ&i};oapD=cD(({C+A$u}*hzC8f|tM^ zay#HOZqj(A!fpc$@5Q)r#_D)jy5(#K^s=E&v^N@G>CKiZ;B*gy4Rg;0Or494g8x1~ z2B*2_!~aX}ALhP+Itdmy#LFKy`@KC-x ze=(TvLg3`xs)Y@6j|5ERyANjE?|%4~ae1e>&vKkpH*Bf zugIg~bENZOT}SC=ycO*4x-n<&zkl$%9cV{e9ZYr~0jwhnV>WDgIwRf-ppUgKT?Tlo2JfE;+o5}hpOKu^(1jaA z;@?x_w~F}dLgEjo@i>!7>90Z9S10aE_1uOtlsL~AMQ>A0mFJFuYli#v5{u_S_&39U zY`o6ZJ6dubEnqJFo3YoE&UyDrc#02utz~PAd#+hqe00{@;@<1l7Jof=ZSk=i*A{=X zaBcCoi`Eu@w`6Vc@!QrGd+Xq?3v+$DU++5h8tA<&xq4n;t|R-!SWonPux*g8=i@&M z*Fe{(Jt=aocmzCCKh&FxF*l4Ir}L`mknhB2Mc+?zh4e^j|HIse2}jR*3vm}-{Ie_F z4IOFrBfSoE&cfXacM))^u}-9aL2O1?4r6oZUThck015V>f~)#@JodM{aHiRHE|Bn6 zt!*;i7;O3h{kKuVKaBSy{ex0{7tS=ly+iO-^Ftt^7XLXGcqtr@@V8^H^AP5C<-NK& zxy7vBgmcVhl--oql}UEJ8hc{}-Y~AU;{5VLf1urnI0%SCQ@?`4p4_&w?Ml&4 zseSJe;Lv9*=OIVi9|redm}^(6eHWdf7k!t$*Ghig zcj^j*LwKkvJMPk!Ea0iXQ5?*cx~-;2Hr_~hq(7x2l? z`!3*Gr$Fka(4>0nqr zU^OAI+W~6~f#JWM{C_W6dW!)&5Q6t}zy=Ux_R3=$kUc|2PxK zul&oC-f5AQ|93`_U-@TeB>Yz0EdRSX^bhA~oJAOV|4e2$JB~B>v=(6x;JuXnMdXeFNbAXisEoB)pZ{ z^awvk={Ms2m(cWpQTlqokE!VatJS7Q_)&yutQdhlg4`156!i~kxr;D&QT=>ff%~ZR z#?w&mPpbEI@*>$pD%*7mPcNYT(*L*U{jNmlfEoWiH{wnWy*b6C{HN*dt~_$n>FzS_ zH-itdqvahAY@`2l6RjzrwO7Y_XCfViDgJW6X#Prff;%wAcj{m_$6DfZz^xdUX)s~R%K`j52??^T)teqLS{w1Q;oY}ZrEG10Go00=viZOV)%~gAf`?g<4e^kM zcDJoYjG-iN;-{1Nfh$`fdML34m7go&rm08@Et z(eShmw7us9PjrXEiu(tlcVzjyU6@0uI;4HKl-f`(=0(0cl)3M^oIaMooDIvx{AMl6 z^D%VTg8yw>E10wMmqGYJ8f)-Q-N{9mzu=?wgbripfFeS+=C$jgX!(%s&lx z(4wK6JCa#F@h^jB9CX6*%hg*NA)lkze;qS+jqBp#eQj$VWd0ra_Tu{--^@gIJP-X; zu9V&DMcT$VxyhlI-!9x>>05+&*x-)0BK}FVweQE!+y!$^)YZ%2XYq+u?_x}_2 z_n{1Jcv}GNG9>^w507C*^ri!q-|-8vZZ#Z8tm+vp><>L%@MGRj@tXiAx3L$nF*+Cd z3l&(;mjshsGHexObD;AGWMlWffcquj#kYm^(_9H_6!dk&-0B=CAM$(!_lxd9A4B}C z!9I>|G}_?(_q@6hxRyX*0{L6vr+F{>i@UsfydA3#h}Qg zKMC)rt)Tzu(jC)u_g?{{{kz3I@!WQ^pNG@j)W+`vuemIbg64A z_yE5}JREQF?>r*P^&sHTyY}vAF>Y@9EU=B_d`M=yw}Q64TD0rOL7VIVhrbu?W(&eB zl1-p>4b}Ij*=QfAYkFIc;s2%Hy%vw1aPSXA$zrb{-bfDJUXkx!;M2H5X-m2V>`#EH z_7E%NNptSQ&rlmobLTP|BrVU$$47Z= zf?iXZY>>6gBu{37tdfGG25;Cqh*im1-$9EEZgqk`Zq zcw2^K9N5b|RBt51ySdD84g;m(8C4-)*uM~G@ zNL&o!DE~kp&3u~d0KASwo#EkT)KQvy9^8`@_rB;;iI<=8xD@wnxKvkN*n{hN#o}p% z%cLckuPnh^EY*1P6ncd++i}BlY*KOUX}VVPRtb*Q)SOcr*s{_o;zPwvJ-plFp2TTl8IE_dAq7!g&ZAX9)dx z{Zr9qioOl({tM!Zz;82jiTBw`d%h>1Inh@fr#{|_d4?VBxgmljVjr{OF5G+SBV7dU zLhwTCVbaMqz4CtYWx+AD1p3{7|3Zvy3oypb$5>Z|G4Dp4xw-*w-_FBZIpRO$`AARa z#^~Li3!NKN=aSS9^ErpE#FMVb#}YG>o=fodkVtwZe*G}8@SZx)betPdD%&nm+R6JnoHn6?Py0>9~N1WKaIyH z@ef+kr^n#(c^}rA#0S|^R>%z%7!%_&kq5O0vZ(}g>3l6q?T-5H2a%4-Lv&=8L2tv> zcVfO-gEA2>1o!I69+B$ykv~%SdpZ6sIc80{FxK^-EzsdzM$CP{>*L_ndM+?w0(8xW zIhzT#YP`|oVGrk{PhieHpkc=z_G!JvL*-HTs=osKQJOE~eIdF#qQksck1q^gI6k6{ zvRAxD{J?*TyD9Ep!5!+%27ec+OvmSXl#d&D){S_rTf@uafOY&p|DVS?YW&s;)BDda zr8k*yU-eQQc+logRz5$wG#Ln%Y(t(xad8W8LU!L7h{7*_J8B zvuoj!93;FS_X2c*+F35f9Gs)7%mCi~O5d5S^qqJU$vI7*-fKtyN^O(oyH|n+&BK2K zS7uoQeW#1}otYetvoFx6O!wo!@p8Lvru#{bAJTV5v;K1hdb0y-ReGl*8g@@wi=4&y z*WX{P%sj0Ik_J6az&bau~zCCo`{glNoh0oadHD6I+`0~o#aLbO#_ z6Kv52ycO99*%%-L`R9i1-gO;QP#05sFy}QGn14TwvncCW;$3g?zX3VXcs^7gOLO&} zg3>$aEHhtcmeGFi`Td{zpSgC>qW*^3BGm=&=OCZ-<+*d2o<%pHkG%>wYI|Kbb5hg# znUkBoM88DmP-#!C4(V{V5%NuSC*iK84}KH=BNDqJckT;?7MF``E%>)FtutdFkLi#B zxp8>=I9G}r^X;S^nS0@W1NV3MpY22Vc)Wkj`*WnYrZrMx@3*6OWZn(`A}OKQ3qRx^ zbt#XF@BIehX@GwRcaap&ai<~8afFTdpYA(SLew|-@9sBJV$_RpWA0tOB@S=reS^32 zI%30Sf1{7V`@FGuk5+ac*Bj91M?tTmdD%+BI;Z6Q+jwW*tF3qDnX2RR*0Xa*^a+Ja zPOg5xq4vn2IElx_4;h!Yk)2COVu{}-vcz|=ex|k84%oZUE-LXh?!jGBOmsZHhf(G_ zDWPd5J~KX9ig`N$ynY8>H%G-Z*;m}TN79Yv@A)O7&36F5zQ(xxIDS2BUr@h?-uWZ= z9Kc=*fsF<19ezG0uD4T}Gk?u>D%pK0>ez_74jrokWx_Ty)R_zF!$>cv4<{Wuh5MAR zt|Yxl<$Ci9Xw#hO6z0Z9M_F7x=)vA3{96xt5UuC|)-(sP)=8o8Z}E2FUR}(XW6*{Aa(u}$yj;+p=Zi)s2?7su=T@4DEgy;2O~SJ$O4Ry2*)nOHKU) z4F5Rb`T!fLb;U({j0%iwrXA3yjs?a&Oaps~!^ZBx`_s+H z==_JqaiY<7TVfS#MBJ7mT>(Gn;|=d-3&ytOu=D3F>K!!}hW--o3^A_9B9GAj9FU$8 zo*Y(+b>!xO|C)9}Cz+*$k9n<=F#aPyYlIjBdAaV)tirmXm)7O?c`=_x9D7#aS5Hi= zvH_pg!~~aZW&GZw9k`NB?>U6GuQ>j11%BRy#HyQsua56FqP0uJH54UQ%~SGb50t&G z{%_ubbvWnI9)wu|!yKow1akxF0X^Q%U7n+}>pgqa{FViAjrgZ3Prp0}V}Kd4Hp{~t zL0YEw=-}#>>rrpwTrmCL;i+R}=MRQyOc{C(hWBM5S4YJ8a;1AhB*X5D6034hwq+6eN)jAoc6EJ6@d0ZO$MjEHShi+4ysB34fZqqz&A<8{;IfHMieF+-W z|9zG%tIZ&*291+X7y#L-0%Uy5Rh6@LY2r?xkt> z&4`y|)PZ#r*6NgBIpmD9A01q_;=M3+&t?qDNAs{S+-W5IPtOImZG}9@R-4D$kJ#VJ z1KzRmuieo*M!KV?K!<;Z=wYA6zs2;DQ=1>dnzH&2%$Wc>Hqc{IiRU7ehirSK=e3w$ zQyoR~I;zWwL0v3Cd;vcDKpoZbI+E}I0~L=##I{tfKya+BUT%D*G>Jy$o5X zvZ8q$-HBcPxquVSO(8fEa4x*oWbO%g>*Ze9SLlDrE?w?8%;Ud91$;;Q0^OKnrvE9O z+ia6_GyK;ndS!jF#NYXe#XkpOTAz?ix@VU9e}*s{meJ3OG5^oXfPEmvJq<9byX(Ls z1AlMf-a|BaDt|@LH4LAR{p#di>H~6zXZTs_{C+88KGG(;|3CKL1Td=VY#+Zv5R|hly;F2JO*)myBNeH_LG=L7)noN>OW+0oB35!}uKxuGk#id%cN~{aU)>;JxwV%Yc z7Hw-Qt!p=;wg$2NtcoR8%>Q}LId|sFWPl{ve*OKwk2cPG-?P8x-OhW?J@@;AK~Gh; z2lYhav8sL6ZwG!L=JIsJY3E`-tI!|P&t{i$$T^ybIof#gy^Ftl@_mbca`OF)|9tWT zizlHR)=KUmzNP%pqp~k8AFtRw(>7Y%2j2->!Fu*UC-S#BbGUaj%G~|&=-q)S1zqzY zE5-@9D;Z;HK|S&Q3D+>xNi4e!^2XneNyEJZQ3v66$Yi22pQ?AT4W}$)Lq6OeBF@aU zpzTCu60K{6&I2#*ML%1xuh)Wg0r~5Xs7u5ZEIuART)b;SJoV3S@Jl^M+Dg8S-t+iV z+*PH^HS2o;`6+LfT(pmn^%2iHEMEN)#uZ-{--hWj?vr}7-8S5dJU7$kFpipeLTPGB z4RwBWKN~(DzD+s+@w%w2^BXFpXUo=M@{B0Xz=&tar)8^m97z|pnmY9Xw84IFgI-|1 z?U}=GYh0Cam~a5_1_kfIToG4@vbUlP%ie-t;^t=zzpYf|YsUR<LBUqziS zoILzCBM=bdavx6jvj2XokCWK{z+vL3+c~D>iNn1gD&MB#V@a=zk!IOgD;6X2n!|5P zM%~ujwH`|MF2?+t7#n#p{x^Q!S;t4Guz%sh$MSb3zMt;RMITC6g%4YN@K_uVvaXgd z4Zm#^`m+VHeACTRmIILGDbQ`Jpxb8R8xA(;GE1h-u}U6OhkH%bzX5X90+|jdnP#2s zDU?sid-$-|D990HJj#pAvwxJVXX3!&-eHQ4w&ZBJ6eY{sM#_AxCqn*_F9UQ62w9HS zXVFZj72DW8YWQs<)jYPo%Xt~l_95my`HDrqNwS?hB5CPx7x{;>N*iSL@rBprJM4uA z(!KADjg>vt+c`X1Z^t{b-aj0rUc~ew_3l8uiTn@V>1x@p(P`w5#=|LJE5KcG(K+y8 z4%l|b+iVYeA80SIs`0Sx`NAW~xxNWZD^zJuBCV8ZzbHInLz>ZYNsWf)nfRt! z#4r6*y7vdHN9q0MH)Y*zR(aT>J70(GM_s4>($;nA^ETMeSbv2)8&CJnWQ{(5l=Qg; zb;i@3Hdm*6wW53TAJV;B63{(kP_*8OznArHI7+=b-7i4BiDH+eSxb`Sf5Po=$U0Y{ zPU?9<@7IVM+2-%jj(qP{_CKoUXQ(mwUQPFUF@~eHySF=C$Q|3TAuoAzk=E|Smu0*2 zj?%6iU$os*5{^-hA^d5&)EDqm8XKjIKL{CLsQBwt$oOd~1(z9EH?Thjbe^Ec!F2DL zX#W|=8}&%MO*4d6HAveW;_7x z9z5$Q_^vj@jbm+^B}XrRlrH4R;`NDp)4dVCk*B<^InBDmN{ht*EB{8;j<<*K_-*yi-dnL>XE{(<4jN>~HbRx>B!7`qAK)q#qT)_w}9f(BcJ%uZcFawV`RP?XwgJ#blQ1@p#rLe_%T_fO z;%v{Pd%vT`TKbln#~-D8Zv&404fAyDk>zeu`C7&EsJ46F#(S@juYp*%T!%E;_R>|^ z!uGbKUdrBPRc7y>(<64a^+~1~TQ?$2*Gbkl*y!&hijnHNcJBh^Z>aYxZ+V(^4nUl6 zbX$;ZewuVC&;E?T-KRWX(DD_@$H{ZdYoEvaj$?uc)^Tj@Pq5smwu@1QI&Lg%%UIYJ zrnT%(zil1-1*Ws!Wyr%Z%KHY!;B~B~;u}kHPQ37@22|D#tpT zMSCOL3V`dxzC3ZkqFBF2pZmilxED;t{Qe8y1pA~a@mu+Q`jp!5Sgkv$0cB(P((j>@1#)d_|8G~_;SvK5-Z<*j5Y4YsLme#Bn| zTdv;|+k&*W5Ld_kZ1K%#%j)OH7_+1~=Rls5ej980cp80D_0#Rmui_3u+qUADP*3uJ zZNHOa;+8ogKFx$jBkn=lH;6}AQ2waN7 zwcpFWjcU6WxXyQMk77UZgN%Q?^8vr*X-g@cFbRJ+t9z+=a$rK?$-xOcUve+x<~sak z0A@UDkka$Xd((ygZ0U2xqgJ7x`ZIM@6UM|}`OTjJI6IJpGX?{3*5CxJVelQ_%W?0_ zj~TCs`cCuDie1M%`4ifJ%OZN-c3Zl)0b{WIox;21xN3nbU3J-E8~mM8$RX*_`W=~8 zg)~ctwVV>GFAAV9n78@PbT9pYi;>rQAA8$VGOgndxpsMLvV0$V8S+@Z#UAJ|mbW3# z`7De5G(O|@T7hrBNt1u1lZTn!{%zT}w;+QvfoD9P_(I@k;JH-2x5o2%KOZIg&+iK{ z&pOb6zw7ZU_u9rGEug$2d4sD9$v5&G=$-wXPrO6_wc z$$hSyP~M6Iv(JIYrOhJTdL!;dL0`dl>KoTGHr&(SlC-&H2g^1dX4&7~AlG#7qkh=a z9kjh%&%MX~f|drxW;?-n$>K@tdlK+}_8G?eH^$?MV;cE2zI~lP*F3^LtPy?iU7haz zHQHl;_CROwxBZ%QFXt;V&)3R%ehxT`XZAlJ^ZGWXd-ni;i;A_hqAcJ2Wj)P+xret4 z>7}bYSpP-+MD|gq>E4H;G_`F>_dbL&tfQkP-TUmr!-Sq?%>TYovThT)faw#Fz706i zigjAQdfnT%Q~t2lRdI0J73p5i=>VMj;F%9=FM%@NEBQ8tMcym+U6t-_QhjZ|Jl*>> zz?K{oY?FN~Y)bd81>T}(VY8%X2sp$~-5dw$NM5Z0&XV)?4H6ds4t;xY8{~=h?D;Df z{U#eWjxm;(i-)^BUtW{SI&MWfhd1Hd)p&Qf`&8^nzhyl5W&_TdLY}4FAKSJGW$5Fa zU^W$>(DcI)zIVHCDe5o4+PLFX?5$pbbK`$Vx|DI)->EpA|849m9t>SPVCz5eo}zUQ zg0vig@zIZbW4N?=^FLg%8*zke(ya6ELnCw9z9!u}6nTl?f3uvsb*m|(AdEMgCIA6`Y&EVG`iMk5A z3er{{DIB@-2=O-Hp8&ofZ9H(}kMLfTz2N&MD}H%px_4uGQ}M&dgR#e?O}sSSTZT3s zVA_<}w8GkS5%=7Nv_r5RpL`(CP`ra@#J?Y*9GIAsPrvH}#0&aTP*0nt!vDpy5 zNiY*B1(+MQLH?@&-qI7zc&2|I0B$4VejhpO~-V zT0^q`MH=ox;F!M9~+409SAWf>TuPu=@C4N5kuUgiChWx!O zX}3623jAcP4C#M5o~`n>#p&LOR@p06*^RR72;lePEKDcPTkjoIusf*j$ATwtAAWRy zb{n4c{n|pj(=rs_tiyYmZmBnM*B1EV4B)VS$i4vcus99r_$G^<{xEQ*cwdC?x>2Xvf;vjO)ZpC7e#{UH@{fwP`^J=2GPet6G|vbY`JihdJ$^>}~#1(NTHXT1Ma z{JxGq%kIqoc~qx$tVs7}gZ`87_X_e@yyF9Y0CfLLqK+|b&oK?{HDjO6bEx}Pw#|LE z;^ERKKiWRq7Njr0U!r|Bt1TPmIewq*G1VUZolmeY%d@N0CFF7Tmuse0(KnuPJP-PP zA@uwL===H5`}1(VcP?zh9N2@|*oWgcF_VPN(PMzr`8S~s+JI6^{uaq`6Hl63vDVEa zV&khUdzC6n-rl|^O4Gvg-_%_CPI(TXf(h6V?s6*Knb<=R*Scg@9=rPI-LX^_ndF*Jn;LF6U%?z2ASYkTHVx{BinFxOVGxegNX`%fU>1{x7{W4 zz6~7ZUZ0a!&@TO~qsJqo-;%=_A)za_DLvdgIol^n(g`VIFFh#Yk2jh zNA=e>A>I33;CSwVc7x|0c(#|eBYK`8y4Lt6>i?^2H*|$OXOQTQ8v2Vox5#!jqi*sk zdApQ8w>axC>YxqhLa87B_F+^%rh+cVI)_k*blhEjZ2gG)`Qr8CYV?8aQ$Ie6eRsTT zJnB|OYugyw@3C!V$U`0~#QgF35FXN*u z+wmpb6*%Di8%1mg>x{9H@kRHI7@JB?z`7_|@C)7?6H?TKoc^-w& zQTdMj9e)1S43|gEjDu~=>+JUfq3sB3GY1&XzNmX ze3WXBdh}Fd{6WgD?mrpu``KT{8*jtiB>Rp?z1{*J;oH!O)Vb?N;hl%`P#)(9edDD5 z=UpvRkY)q@&%?Wrtp7CF1goyAQP(r7F5FGClWX%9ygwhmHqXP_+@im)D-C|Y__n`t zT>-jU>$#4@Y2HrY=#K~RUT-8n@LZR7sQjd<4LI;b@$ZR4e%h)ceDl!qpJJOdVFT)&i9b{M5PF>F zS?F*265qwIH9o5y(qy|f#@6>RZjAA}z_qLr?@$166m(9VtY&OcI|;C{woS{(y=QOBpCzdjb* z^J(5Az*%GYJ78Tm(?3sSn^?yIRgQ9O{5Qsf-{v>cyu@2)CW$Nkn@sC?UFzHpr0cN( zAMn2i?it+eVZ&bME#Su{fqez`+hv<%<;ziG%Wit@{>lEJiOeVCSt0@m%;|JUcSr3esg`g9e4!z zMO$}9+KT%3Y)9u2wsDKrMqhc~_DjgWXeW9%=#|4_d6{io@j2S)0I!5lo;0v@eKT-1 zaqX6(-FO;W>&c6eSD*X#pFt1do4}UNxk%I%yG~q$`r`LuITs6%mt!hb`l}UnEd(wZ z=T11c=K>~QSZhx1mr8#A5zcK#?;Izuvc7DsJ_9ompy z-h?lQvjF?VJ`;Yi$5ebGeji6!y`RmGr+FtM?GfO0+VY(0J%YB!ejoQvlq1ht{(9ke z(!AdRPP}hx@iAz88*nQ?BgA=+Fiya=1md*fYim1IqD|u zcy9HTb7d@VZ-O)E+rVd}mv3j9H;giWQs-5<&%!h4ygMcXv18^b0MGHaa7UUqh`bq~ z(O%?FK^~F*j7)ESIL+%rx~TWjH18#VEnVMnbDH-eJoDWMOV1WQl;&LloPo92#uo~Y z6d=yi^n&e(nYT*3@7w%h;Ss*~+w`LC2zBOs_`!?;TlU~XoaJx9I3G+khV$KmQutcj z-+K;tIe*c3m=)WxgPsL>K@m@~WdtMso$p@oFnA>if2@Bx`bFAKfxh_&_3gl4qIeVY z9**lr2fs;=xMRGYe|7-~5 zCh`5oY=?8u@};4XJB|+_hR=5I{w(RzPTWQI@ifI7bKT<1*4W^K>ktq5p@B5+)pp~o z`JO@Yj{Cy9(!2*zp8egMCGY3v9gFz>)Yb#oPdtD#-p%J4BmRQDUG}{Ua{zgR&1@?E zI{y9%`oMQ3!aA}8^%kHE+v^-L_S%%6ZY4<4xAxw;q(6f10FO@=tNj4fn7cIFCj9aIAg$Lu&8WS_9ss z=7Ie)?vrx!>J{`i#t$5~lhy1Dz6a33D`0%_3ets+d zSg!SYxsLn_@J)jZVHa1dp^Y0a&dRL-UdF~pw-p0!8N{`%@%N8#22S_sW{G_C>3aJ3 z*8YCHyjk&6&K-Hbv-_sv7JO55`+(%Zr;g^Hw6Xlbf7--;$2D=^6lLF=Zj^wDb7|LZQEP;W<#Ju{bEL(u3j|#-Zpy!96XM^=`Vq>(&bnz2 zR}7N!yFKnbj%M&dCv+|A+ux$-37LEtWqHP%?{Ummc9HL9ZRedu!uN{CZn?hrHpa_3 z_H34Q*udxCK^??(tWWda4meS{YgIV|Jixp*1O_ANJJO&y*=Fzvd%8QjJ@f2 z8J~$d$ct>h;BrO3>NGF$)|d@{Xx(spGqNw_q}Kat3S<_6D4`BdMZn zF7nV0a!;V+NUEK6SbD>OG|qD}>XLRSRt5v_%l1>+SL{xGp6?XGj*c(5jCL#er}B9q zo=rS&Ka#qXbyH7m|5Ubf;GYtgVj2(9W|`nEVY`m%LnL9(LEha$pXARNbA-R4+RvA9pl5;0ct_C*G)loePsx8+*j4z696Qf- zif@acpNw&LZ?HX>lD(<;`GyBV2SysZ51<|RC!^XArPf>YoA?h2Tl4g@7rJl#EzWUs zuD-)QVEz-`cTd`~?TKA-?ya`>;NF`^+e>aK-eR?lv2>p~;K2^~Q_L&RxXC?q@^KUH z7G+x(;J%vukWpKg9Fx_rR`lz4(D@O*OEJD&_@T`6n|S;o*{5Fuub)fgm^1z`M&4-)Cnb$&1aXQ7Q` z#6E#Da93QivHq@aiQs(!V(>q-aMzK=0|KcPkf%{gHjgupsWeqHZZ}MgihI%gm-EOBNjUZ zG#rYvl}Yk`*C(V++P>+O!x^AA{n#Mt5$}aV=Xc^+=uE`ueFtP)HIPSZ|C962duV6k zzPN+Xa~&wNo!`5|-9pb__D8+4;L0hxwtyC_dp~Gl z<*AO(1G>cLxh_5r_6_3md@nvv7S6DNzL7b@_&VN_^ttkR`Ch*DUA+YlL~Xlm1a15I z;EhFy)1UWX_a=ExY75RRJ_z~4T5@X}XgY=C;$2agANIw>x-@g3k-ZRU@8LUW)9`x` z_Y|CgIoN`8n34WszDS$*kRC%D+}FcA#z)=DuK`b#0N(at_ws8eNxByRz7A(S?J-|JFNzKg!$yqb8IVUJtZ`59!n>|D8@+Bzl8%l9Hs z!Of+!Y~ovAE$4#wLGy0hugJFBGtu@)W9z0sYw@$#A1Ilq1 zyl+D1TgIaqAH4Gc@0+zD)|8EMjF%r+Kf$ zGyRkX{Oa#?F&1XM_ZA;}<$ZChke_4NduJ>?*f;2w9oF4Jh2x^*DOYWIMO)H$W!#kZ zz`%UQ+wu<7Pk!pWeuy~lK41^>42t@gOf#mAgKUg!y9s#GmSe#^`pV~u+UEE*0sEeZ zey~ib;{Tl=3`1R`1%3nMs#JW>dvqJ=xqbDDq^B#ag=xox&@{25g>Fm9+{*rOJF`T}OCk`J!2P3yCTYbUGuDuak9ZS=K zr=_lK1#R0-gsfhGdrpSqYyh4E*En}KtMvPscG38*Qq%>UonvSv?h=VBd|K)c+r=Aq^~BXmT=P>>Hd}}bsrppDO~kE<;Dr2NN!(=; zXM0MHX+3dQMsOlun7EA*Ts_B8N8DzK>v&R@T}9kA64(Bu?6aS^Z%ACL!j%$tqr^2o zDf|2t;=VP$D{#?D-l4uDa0z@R{+6xm3g9 z0Bxl#-CXP&kaF!7{FdRD=|-C24xn7=x%d`i>SpLGoV7)M%9XK8j{C*u#9F~ko`8ZF zWqU}@*B(66f4li-(oS3P9!p;LBHgkt6Cagy-GaPqYuiv`XC3MafEH{M_x_9T4{e9v zv*6_+Vy-e?4;=mq@+704lqH*sgXnX{Xk&MLf3Y?e{VjM*(*GXx7cqR%U&j7eK5(P+ zsO6d-mE~?uP%hH$DMxSD{GlA%RSC)mZa;dvrH@GZu1QcX((dU;Z@1tFavmxZln>nF z+&vI>EuOvs#LE}F5T!5A0&Rauj=vM@!NU6`Y|ZT{YQ7ks3xH2KR~x`n)C1f8(7oe! z=urdo-2lB2KmVOCM(b_9H6^M z;d_q4x@0ugB`0i!zr*jj3tN<2tkx&^?%Y2s)L3S&?An8V^M2QiQT#T6v181F!Y;le z)!r)k$D;WQz~PcUL%XEFtMyj{qPy_!o04>xdiZqq)z!&>~x@Xf_8{5GK7 zIq1isD+_K4d_~n)#QIKZ+c;3_iOyyCCIsr*S($PNVwt<+^Ob4&wh!u=K1)we?U_DF zPaoDZ{T@9X-ztu8|5-hKOwaU5gQDZu?lgAu-M{2Kyl;kYAU}!qG2YJ|&hI{Sf^K&w zB_Dhj=cG@%{EEZz?Ywznw4PIKV^;gHhKtWrrnMC~GS<_A^&9S)TW!K;TL5|o)%vv= z>k4t64*nePq=~O*KytLLj1N!Dd5tn2H$QrwCmQ6Ik}gX_Z;kkRFk)n$-bd^Wl2%Xq(LDb|W72!FCbYrg<7zTh`QSnK1v z1Xz!zoXC52Q&vypy$V?Ca_!QIwM#PY=iQ>k0_e9D;2UfU4H+BY^F%!3?$u(>YXm#=OZl>e-%hi!7pvp$$;Z&upK_(^LIvR!29W#m)|Gr{BBgg zwl+&0^56I_L57C6ZIpQq0M9#%NK1b?^srqJ#o_#J|^ z@L!=%YCW5aA6Bu4EcEB$l~Z?J?<~xG2K?WO_ygiG*@)42aF8V%-zOV|`;UkJH_D}h zmhYonI@Yd#U759O=ul&L?WvoK|F+V;3uoVlzlU}5>d#Kh8I1hzA^-Q0cD-t63)a%9 z^u3XG5NX#T&06F0dDZ&NUBSCUA}+B7Yx#x7H6rfAx!sRGP0^m|v!{%_O1$4inu>S{ z>UeL&RgpRwQ(;^00zVkK;ym=@Jj8GUYjwSfd!ezfO+9U%hj*6neQnP30mxluHolDs z8x_1YBw`!nb#pvoOlKKK=39B7x3HebK7hDFCgKW=6N!A|Y!|cLf?(#S!S7;BYTU#f zoFV&i5N)hJ2k{H=!NIZMag=5MI#CzDYn6gNn?nZ;k1swNl zMZcGD{ey2j&$D#}?g;JPo+8hEupg~g54n@S{M!R~|JQBb@8&m?g&xHBNFXzo-s{AA z??94mE7#hiu%@1jd5@Qiy`=k9QMnkBAWsYO9M~AmlZrg^uaWYw4Z5BFPDfDsC4Yr{ z1h%L&oC9In#8pzy`~_*;Z|VH5vGupmXCv{=o}J62yiu0In+pz)0)EZ4q1{~naec_M z3!mY;;ykC&37Yz(f5o@Bu&aC_-!Hbw{SWY4OPv2cpZ?|AD9sr|05F4z6W{7<^a#@89ZJU$qX)|rAjldp*CzQV7`v2Z_Z{^e18 zyTV&B{Z&8dzEy84K9<07hsVlsTm2jL3GOQ~Mjk|;mp5M_=iI8#@(-8)qy8$zG1+g<%Z9~B$NUQXl^TAbEixvyYsE8? zhskZUP^Bt&+{NV z3Tq_(JmT&e-&Kn8z8cAMbBVi0;#zAYzZVjBe+0LbG(DTRwg^t>i5%h{j^Kok=OpgY z2u_rpM%?3wTik`X1@u|qe)ONeZHQa&_u-XY8T@?|>Cgvz@MmHS7XOhqe3i0qdw^$b z;Ce}?m^^!tX2k`3tK^tBAs@%P=i}}jC!inTnc@wgi(a=*W7j96#n@Pe{C#);_&Y9t zcTv(yA1%S%8JA=KC`s-eW&C-tIM1*h-$l$g&fbx&O6rmZ=oRXQ1sO$|=b5uJt$md# zSjSxhojrR+Vb^3^^589pWS=rxS9Y}k&-p0*y&?98Qn1&saO$i~d~Yu^6YzoQNtp}y zn>H}h+E>av8G8%#C*dpV{$x9zA4Fd69r5fb+sgbP+7`Y8y)y%45o>Z6R!DlzM&2zb zvkHIjo_PZL41X`9FmnU^sy*;w=trh#xDMd3?#!N&j(slpf6b+G9-YWH6?-9n#a_aT zsOw?)1zGS99)h2C7JM%5p?nYiS0;SCHuzPd{Abn%~o4^SwPLCLWV2FelsNaB!@poGGPyI$>{MNII@slnY-&xsJm^>@fn7B!-kJ%2s zqq(!=Vo8tviXRVP?LywNzFYa=ZwE)ti_qUZa`_^x8L*#(y+-o$Lh#km&c}@Ti<|?4 z@iN5Yx!*#4{FA?T-|D-huxldZgWvoKJUVOl-LU_x|IlDOKR#>sGk7kAP1z4yikF*i zKD4Yb*m~utJDSip51wf^N-wc>g&!TTyYcY>yZ1r{OD`zwn$)xr_xX)#l zOdTiojPyMYYkz~Wc=~C&4Y)w@Qlk7K-ziwDE|hdXg!OfzF?ar^A7lRE|J<0z_hQVg zMbRv7uo}j^#H24e+#@W6|*(?oqYOqIx^ELQf4W6aJb2WIO1~1m& zVhz4PgD=wHOEkDlgDW+7l?K;pa7csKXz*nkjBiLs#=cR5H*4@U8vG3nzEOj}rNOsr z@Ld{wj|Shb!EGA+um(S>!H;Y3lN$VA8vLvVKc~SjXz+^~yibE)(cm{U_>UU=rUw5- zgWuKQze)HJTzr@Qfd+r9!JlaGXBs>J?}&o`McF|boT|aYGzXKAoggFPCIdsQO+#F`@l!^TD6G7YZO;8hx2tHB`+UZcTRYVbx4-mJmbXz({Q z_(l!>mImLh!FOr!JsNz!2DfSO!y5dk20yOBPipY98vL9Fzo5Y{YVbY{eno@d(BMC6 z@S7U^7Y%+_ga4+%A87E$8vKa{51JL7$5ag-rokgLc#H<0q`?z3c#;O6p}|u$I7@?_ z8tl>Fd<{NVgJ)^*Tn%2R!HYGxSc5On;EOc)5)CfX;7Sc%rNOlt9Ma%#Xz-01{4EW> zU4!q^;CnRqehqHZ;DY3z`Fpi1iTIKy@1aL zYy`TyZvb2fcsXDc7xf@!ZzMlyGcti+RNCGBH1eDR_}75hPA1ZS{a-fY=NH?IA$N3l zFM0~mJ!~M1y0g1`KHwVwuLJzvE}L-`;1Fz!aeOL$lD8OFe4}d69>B?f ze~5aj@9*yZ=y98I5AXrN`vDiA&WmvY<}U!7fSUmC0z3+EC*XSlzly#Qz5(z@fSVrd z?!E%B3wgc=H~`oHyaVuZz(K%|04@MLR^^!rcooY27Wi?1*8v{)P%K{@-idO+4+Q>g z!1n?+0Fy5elQnidZ8ILb2>soPdBB+7_=?TA4e&U?uLIr=cp%_k0)GF-?(X-|PCMZG zn=uc7cLLrEcrV}w0XN-(k7on^4&bM6#XJDs3V8W#vR{`O*M9oR#IBiW+AP^NU>1H2 zqu`qb`0t)W8SfKcx?;u4{O!fRre5aLtJ2Hpu%72waDU1~>jU-0KkRFWf7nYD|2nZy z<-c@XZpwe#Hp`p^v+y=9{xyB;Ht}n89HQDVj2CX7A%5Q;yqxlHbgr2{mcQxu{kD_8 z+d_-Sae@mjOaJg9{5Cyz@0HiHk@mU|Hc!UyHD6sfryPw`Jn-1#`w@=m+%jOt1y?U$o_5bph{I&Q1Bgi2? z+;j6uHTW&~^PTp4@O#6ApFI5ve%)srT0Nx5Xxg8%{>Ozy#^?%(a-=D*!DFCX>r zypGGRn)&I2f8Te0+0vQQicUWGrst%Ye-FIXvh(oU=U)HUo!kEL+#~1C|H0F@fAH#M z=jLB$fAbsb=bU@XD`#{L&KiF1Q$Kq3^*>$lyK`=-Sl_YX$bIJwe|K8*w{N}loQ-Q1 z1fD(p>~root9A1oR}DI6%mwe<^@F5+XFt8=wOb$Ecjwu+{&w!%L&K`h{$A0(m%bWu zp8dUjk1u(C(~%jIXWclY_0FHoSaQd()E#f#IO8vuj7>N5E|EOYyP=6(a(pYF-ZkOs zgJ0e??CDRgT=jJLrq`bO%>R$KeXWl_RWYG!Q}a9DcxvjM_jZ+Ebl!}~k8Ha6K*#7A zH!qs@r^)tTKRIL9L(hcgKKA6)>;L3mo*H_xE#v&lpZ>?Z{LP&ozklY2uDo0RwC%q2 zzkG4`JLhalNqJy#-nK*Y#%#)2zx&%?|I5K=cbuM=X-`dl^ogOn|8`yNpp55U-F4zQ zcTIS#_?leLpI^Ig-twy4OP<*O#NBIV<^~`5`RkR9pFGirp^2g++q&uPA=A^hS1b)Bt*Juv^3g*{Pqq%4Zhzvj2cCb! z*4KVn`Pc8y-1Yq#?Ku^9pL6|#bN}|J|Ey~!{_IR=)&X1Fw}Y>LIkPP9)4L`;`@MTF zD=43|@YnC%ePQd@|K2oX#2q7EU%YkF;)}nvar#HU{MNH~4DYDx_~@Ok=dYV`!}d_> z!PJ&ycl(HMOj^8Y<%mzLruy~!@xqRu4Vbob_H(Y~@7*`(iUk#Szx~kOYiBJ!ZE?mk z-@P_@*>yjB_E6@zzrOH_-xWSJf5R;ofAr6vXKiV?*mut0ZytW-H{sU^g-Qzx4i4QrYv;i^ zsgJkqZko~gLiL!-UYeBk-CqqGmvzG+Pw{WRo;7#l6Lp!d5C8SxskeAP{6qD5-(55E zx(D9;Q~qh61{<5-xxek!?f#p-`rfw5zSD=CF#gi}e^&kKx5xhOQ0_?==6yEuo_-Ut za9Po`IgTtxPNc7W2gqClR5AW4)6lDmy2U@E+u$$$)2l);nP}hiznH}1K1^TH;14xS zugCvGb*ud4;fCpp8f*NuzEH5SW_m-Yykw1YdVRGoTv->YnO@hhZhCbjxuh}}Y6zDA znN@BWL(|8ca$3gp>>0-B@uy5QPQCtzy9|drC;yy+qPYte*_RZr@P5TGY@m*<6o0B` z*t<5839nna+ix;?r1XP;XzYwr9-i;aR=SIwTgaPiXO3%o{l?%6Yo z=AD{4<7+pP1<|j0LEL!z{(0AAgh=_Tf8r!`Onq&G@?-f0KYq z#(`Js-;!n1vAxy*f3?0EHrMZewT;!){Vt4xCFh+#Z~5n_pk(#uE8lnd%*vXM|NAR~ z;kwZDs*tZf5G-%drB)`DSV>ud&oMk>-lAoT%sHV@T`1o?y&<*FRh^zSr5~lD%|x2c zKfS@6W}aS=nu2chz8|W5T?3R^wZEjU-d`)Li8gUeD5WqQ_Se*hgSAy=xXxsG5Tf^k zN|{^jt7<@zM*P3`-H!AruK(rLbq)T$3_z4e0gC`t{%}dKwz5vtf&Ya3zUq2kIM9!- zi9Q~q{)VtGJdJeonE_vIMYZ1y2Yg{O*kIPyg@LRHhG38CLhH@C5FR-{eeXpaePV{! zo7&>agB%qlzUu1y>IT1mbzcY_nXybuWBNg|thoW2`%jntBe*M~tk_JCEv-|U} zJODQCPeD~?-lCEPi!Uf#v|x4#{<~sMU#rr}&sn_U{Js{4evh=jWa*sZ!sQE=;1ddg#!8cEIZY(oh{7rkXyJu&Npkg0DQ#&*s5D zi-NVQ``bGC4-35h7Z>~+#t=fmvPE^}zHqRvZ?arftz<`3v9vB&8}1VuR8`NZ^_5lo z^;(1=_i`pEe_=`dAkHcE`JYyP0oq@|I&sL4mI{e>qlEGukqDKpZX3}L+SOsRJ23@UVHSZ?@(3s`p-3g0d%16 ztwu`(YQpQHPko10`$K&*7;#O^Te@Uf9I8)`MRVtxeJT)_xyFZhT*xc+`QR&xt?j+ZugvN zZeLm1v|K*U&Yqi-=gM_v=X#31*0Wr1F%|HOr&re1!bpYtL+QzD;Ml_{GF>jaSzZ_N zr&jyc*ENRC3~CO#OXfMIJ<|-zt!sdN6Rk3%Ifb>jGtlf*(~&uYxf<7? zOQMml9pA9@9G@JC?3n7M+ngKnRTb4$*M*{;ZSd99SNlWG*(Q8|L>-QlL3ijTG=}^c z_??OlW?KDrOcnn_$NM?Q70VbGT=WJae_iKI^*VXphcOc&{D{ri>r)fBr5F8_oTQMc4HOuX7{0Rd3V&r)Ah>FEbxmzu{iWFPZd|i=-TKQ4XBEw!Gk4zn1q;txbpGNcOTS#a zZ25`{F7&Rv$nJ2u+}WO-+`NeelTSPSk~5}cWKKJ$e8r6XGbc?wJL}x( zGfR7Ms-mZSe_{Hw&n`wk1)*;WA#N^vj$hYr5p^D6-l$6N1=mMPCGbFcsjZJ9pD{|w z1x*Dj9;ekT{iR86@4dZ$+f*E7d`IncFQ`6!I{efGq)(`Agj1_$7Vd20nyhv34Dqvm zD!kgyVOYcdhJMGU$f^z1mA>lL4Hc{Uvv@%HP+i^UYTseHoMyJi^qA%OPN&;$IElzL z9oaDYHT5M}o`f6n9XUC6)8jUsZX{tvPlQ8eMOgThZ0L~SU>(wI~5vdNDnroM-@=>;k>T^Y43)UH)5gN{SWjh?K zDc5w`BT3c%+NyA%fr(CBDl6JWk7^rhAWeuPBgO5)86TOFYX;FJS2lVsx`b!+Nj!@_ zG2J1bMW>jqp7T7CFjZwW%#dd~Ja&@M1>YeQ4wn1b2bbMYW4Bt$amBWTXS5VwF2`-g z)}Q0CDiTC=<#}>6B8tZ-5yfMah3}0QvYVTPDf!u*$8h12Ru_(rq^#b~EIw ztwOtw__=bsvh#Ebif3)E#IrV6;u(Fx(CuPo8uHz)ylla&=u$4uDgfioamrEWqCa(^ zU{$``nJt)~?1ddb`7G+mF)zgsow+ViM=r*6DaIt81qa~Ssh;zk;0n|j2!ijB#ckw} z?>KCaS()$hxFB5+8OlzC)-I1b+jQoTT$0!lkt;=p?8*!-yObHSt9XVCD0<6hm!v=0 z73l_&Ma!r&TvMM9x;n^s=$^+c!y~AQ(cuw23D?zE1k1w`OQ|M0+d>B`pb40t!$1uV zv%CQTUq6sQIC8{rpjiSwcs(JAD>R8n0X2|4+pMfWEC=z9%1W8wv~y@^qq3sGU(M4S zkrZkbv~6dfT@Gf4)KSMokS-!~Oee?_MS6gQ?s8$!K%y^oe(VF95YuQTH>(4qxN@~N z+^jAatj@Z~G_s@{V}gE&G=Kw&$VN#P4aokv^N7qdp?IP>J#HZLfOKoEcu?GJx0~6Z zRiw@w^v7*?TdLS&R^T~LX$LV!?mV|4v`H?8gwz~+j?^7_W*Hd_2(T}EonzJq@W?J> z_QZ3wc#a6XU8#YF{JdNvPbxzYyFqNY3jGfyE4Dz zRfmz70a=#-sq-*E4Bd*=k#~v8;zYq9R#f8@O$gn|mD7pxq7W>-iBnUdut{!(r8oVx z6__!Q1pm)AEBw`AUrD$w>;sSB*+EuCid&^Lg#FMLXo#YXA23%Pm<>du;4-J(gv8>o z3y>tXXeCFg8IV}g!xeT-B7bFAC>{2&7z_EZWPVg>kv1l;Bovunp8SeFt9cMc zo#{OOlrOjw9*O9P&~Cy<(dSv}FpMdWW}vYeBS7b&rzkW~IZk*WE|b0ui)L#@oo=Ma zp|L^P0Xfqi(V#nrLt`~1OY;qJ3amrkeVGwUs2Rh~C=jO<+FlVeN zd9ou8oZYPT`zs)?jvQsfbIfudm_%>?=yL%9y+^kpgqoBgLKXahf^km|$8*K1Q4a<0PIc@KYsTT*;QyRafN0u@w_}lp#=-=$=s12+IXsAFtow*9+;0_QNG*tw%rHJ#uDe3&!b@ zGdtIS6wpebWu>}uEiViy>l(^^P$`Z)Oq+1TfLkBM(HTRX38sZSh7>SyuzYnxiO|Ea zW$@p?Zf+>2wJ=`N3E@*fJW=FOsOQj~aQe|nq`}mP3Zcqr$AmP}cLnj>P)*J}YkI8- z6;zk1U(9U|PRhc~0v`x-L8a$0q1N;0VF_mnnh=E>%R=mR`D!NRXwD5&M1CNpb0eG- zkx3c~q~QX*ng!oN>|BC$ z4bqi`5usHTG^h^+*WhSd33mG68Zt#v3tV7%-BcTrV3xD)%#mU!$AlyVOHfk6T%5`!rKH%WKz7I2ReZvcVeo=QXqB^4!HA3Q8qk*=7i-&w`EJ#@q`lD z2`FN4Z#-G(;bGL$sH3AXg-ZB_5WMXgEEN)(dAwi76mV*fdUNs-6%y_alj1C=-Ex-b zeK(YgNC)^v8pVhZf{;C^1qII$D&vKERoAgJiq8ucLeo84hnirIF?XmX1VKiYT;={b z;Fv+bAz0$TnT1@mhQvH97%NKFP>-2;pjHHhmA%_#=5fVO*<~(Yz5@{zhaK8IlC2Rq z_@P+AL*Ys51+qK1On1@}D2oPSI=JYEsuI11p^2>3oKOlPEy739Gzv1)++-wSYP2*g zx3jTQ%?2}}9Z*zDgIqJs18k3SkXrbKLY#Y)W8aaMgK?uMt(|{eNqrFU60i}L(Lw}- zk#m2KNG`-SCd2mmmgZwd8;XkbHnjV+EVKh8EyxqXB`K6wCb3!;I}P z0cIjP!#N-;(x~B?me+%yogf0kII?7npki$SvMRd9)S3&%p-PrK1?3S_E-K&-tdyv? zdvq%g9E3D#>i9I5ooOyJNBD(^c;N{i4cR>509=UVSc0Z^5|&$(q__!<8gx0pe~7T; z$$5u029LvDz;P1PhQ|hv2h&VB0}mnts>pLmPLoV&&7T`}$oYetfzw)8oHBGnzf@`w zcMg~tW(<8!pl}3FgCTnA7l<^vAdCr@ZlDi_EglglMM#UYEFKXjMNCT=6q+@)9>#1@ zwbc7Uerzw`#1cA*4q%rar(6x1dRbZoUzo`L*49<{nU~Y8x{p&{$~{ZieKAlWjM>0XU@>@vQbn_qB7(uWiC9Om?x80y-f|Z4ml&C0tWtDQ7;zO1!tmhC zgiA#1$z{c!BDGj;!)jOiEAVPjwXd9u=sdM20OE2T!HA5qV@H`SGGDC7Q7-_?1l=Wo zv*3cvlhPsu5x){&#i~Yxg1Cf>xSZ&Ac@Xb8g+pLM=z9z`x_m-Or2%5w#9|-qp=$hB zdI%6{6eStCI>2N=Q$Z|IL85`6Qs5KJ5*-cbS;1Va2qqXZL5)IZgqU2IUQ3VUh}{4L z;A8}tb04Ezk&rFb6oL*2Ut+_=^q?}qB|5QOFz7IvU;~I8JYXC`L9iclp_GQhROdy2 zg=E-~1j8XZRFDXpU+}n ziO=X06(hHt(Tp%E*(P_K@g$4}(h%RplTgS=lTYAh`kgAIjeSq<4s%kl$6+z2%VUU5 z6QRPm--|I*`;pd8&36G>jR&Nm5+GP9c?a7a)3^QZej!nTaGh%!-h2ZHc6l zAw5|kmhmNoWJ1sqcnTmHbS%?)N(c86z;B5Yp)b~6imG@A_JUX( zIZH%L5g|P&EwqAKR2r(b(qO_wfDR&u5EO2@2#JG2G%j?JX@|sdZ|rjmp%4f*K>aT^ zCn>BwoM;)vLsEj7z>w)HqB}a%QpZzai3U`#PADZHAjnY<9_ujZ=Uj+cj}#aR>CCe7 zm@=>zpqd@^#~U6O%oR+Du$q|Sm{Pg<(A~mnQbN`uM#j*tWM)WK2%FK)+-w8$fO!NJ zB2FiI#^@BM&;{$7Ky-jd)z;jqur`l=SaXZg+~%w=50}*V8dmf0Ld5e$Oxq3qmy?<+ zG6I3IVSE^Qg6WU}E#-)V1Z)U7osmZ11Yi_UiNN+;6FIW84dG>>Zl3Dlp<<4awgx_& z3qt{;i^LEf6)&PHU0X20as%^l#HK>rgiv9~#UQk^IcL3JOe&+tMNsShkkEth7R(j( zLOjr8k2x1BIFFVXCm!DaX1A%PO7{H#4U}cYRYQk!*GhAXq%kpy&SH^Il>;T z-ysE8_DSY);3z><^FpHV$b~COV$C_W+oY7jHp9<=x|QOIN3aOAhYU00(TPVG5n=A} z=)xl$MS8Vlz4TxoMC5EY`%!hXcluJwp|it=AI>8 zJdqilg34hCBXP~p*dn5VPK)z72tk1DRUiY%SiVPCnoA_Ac|F2`7!rgBt|A>#6pBs5 z3XXv!wPcD*#Dp+WL<=!`*#{o$;9j&4M*POZ0 z1ic{5U^_hHF(;z>;D~Uv=%5)E`VQ@xhqS_Z^Yn|gQ~kBe%qJ*l4}$7L^V^fzMN z3#|c}}T=qQKP1KCoA9rJfB4w_l%qtq| zxynWVg-BrKRe?o}6bh`#a9%9p(~y_YN6sk65#t&<9EMmGQr=_fDA-6U0zKx=-CsD= ztPBK2U!(4$18M+Clpm^ym?WO!lG}IjNlxf5SUtr`N1uPtTAP264+Ft)FnI{|iD{rU zBzMyR5qZ&xm@zpRCk446y@Pp#ix~Og+U>fWCFoG0evKKOCf2+K`e` zud;#0e2^J2rPjJbSX!Q2Q=vU!K*3*)Cb%39kXyEiQaw0N$*IW9{YVf~32K5# zcUl@ljb2hLh7>eLVbX}bk`JJf&=}~pS#C)dD2sDrXiVw}JmL@-%&Aloc*Hrg$mSd# zUHR&eGZ%I6ICHRyz+8$a2i5?}{Y07*D>>zT;tA!HQ-RUzR#QAmsv08FAxQo&^Pu3F zoGp)tgQvyW)pL#hUR}wz=y`=@b+D{qdPBGj_YXAsc`O|_RpPOszN7@FW`cDUUt9|$ zRBgC2+5isX*7%G7&H&@D(Q39LSQQM{`*5LKRdv4`KmN^}yhO698lCAc1&`l^cKK%s zPIK2bgtL^-vz~iE!TJ(;4-HO;S2Sed$e)4mR7p7C$3fN4*(>e-V09B|gY}MQ$(mq8 zqqv(Vgd2tX-i}tael;=aDUPZ%^rKENB2K=C{1y5YsuClQzl1|LQs)n0x}xpyV14va ztb-fkpG6*V{1d~04s=BVsSB*HRICWIg zzj>jKh`Wp$!}ZnI^Oz&HpTx0hi@l*a!r_t<+y*wUdVM|bW2^TsU!KM5ZQ`=<5I){K zhXm9$H1MUDl66^l$;DCv^>u4;fu;3ShS&F$QmXY=^?1U)g<3M+#)*B>lC?B|Oz1oR zgCO{SLSB%{8RS9LHP|47)$ecpJ!sw^QI%cQs^W{74O!*&^^xfir#J+*pK^;fph)3F zO_y&m>fEBYk+tzkjMlclLN=ui zXGQvC;l=r^gxJc2*v1}Mb%e=i43@~3dA{)Bltij-=$GP=TyZpFp>~a4XiD0sEU`}? z8Eq7AZPs(Zc!}SKw?d#9)?-7tA>hY_u0r|x8rIjAmk1S1r5%Th)QVZa%@h?S+!ZY; zFGD|~Rtq!p#Zxj`ebgwbX(Medjzzx`<`i5TAwYjc9q0XVX1pGURHjEkuqn5FVe%-g z;r=TUC7ilY2yu;_WU)0V_Vj^YP`d{HK?Qa}(1CSfd0QT@J1Shdz_hPB-Ja`>=0W_X zHk>GBiM&+Sy1MD~VAkouNHLX&_tMA`eZkF8WkGn#>v`Sv@vA{hE>ev}0@c=>Wjy^NZcvFG zhee`LT^UaM)Z(2!yueWr8IV;?Bpcq>#G2DA^9Ouuf*6aUW~7;EMdzD__OwWQGT{)z zvM#`+RQW?|mmfID_*VPP^2QL4@nQ2Y9KeQ`yqZ$p*&gum(sqjyQ9-~6RsB{?4QqY%)(fjR zn9=`w{9#kng>@S^4jjd9fhMTY>ZqFKV|52}2`l ztI3Z7tT!ct8qti$;8;a#)g?`8`qruC0lztK(cBBM5HAbi4kYVB{piJ~75EyVAL@E9kC335ktCFtIWE9ybvXxb9Y0VcM_UxTar*%GAQ z5*2D`wAxtfTjL8>^IqQs87!%Z%UEbypP`x7P>)NaDud`eFsHb9NpZfpV)1#4mt445Qd8g}11B+Y7XYZE)TG^I7yoiA?S28M zXM3Id&ROfmwCkFx_qC+ zug~{6riwbF<+Bw+RiM%%HRQ--RN<5m>Tpe!Z^4!2Onx*{nedXqo zWu|wkVG*Z1*I6Nm1FDFBf;1pb?yO*VcAYu52%9iVLvTCa7zkUwDBp@C8mwTLE^Yc99M6MhNg&N<&@C)0pm$T^>jS8`*x1FTIkJ0^E{V%ecA#}W%U^+GP(y_x!KhuxEC z>M=TV9#n6=uOg9XR5b=G{8T2f3PCcx#-zs`h?>br5qIoB`oP24i3NPIlyB3SXc&E* zwPTLjlGOifQTO0HYNGl6pC;3Ph|u%mCz8kE^py!R#hHu6bCw-jFce3f1!Jeo=`MVBNRJntIO7;6(#MwsyHj)>-A*706?LZU`@h@P!N=&W60jsF&elO63etQ& zb&ktnB;zaMaFRL0!q)w-r%uF8!gjy-r}O>)dg|czpLpI8M6!5ineZZME|=mnTDrF# z?^4Fgd@n>p`MZ7g>6{5>5)SV&YWibX_J3aw-4<2jBr(!M5K}YSQI9s_phrm)-|HT_ zR4ierb&s#0d$XFr_InR8v55Qc*0BF+s|h>aO_tQrH0&2koPIcB{~|k--Cuvz?mRXN zou#pUgyH}7{_4MoOp#@KU)31MbWCRYKQgKChD(x3x7(1p3-LkAYR2IUD}40`qmil9 zinYH?6|ZW=vx?xvu6_PJywv-(E?#v{sI@Qhhw2iq4-<{`nCtRBJMEU^<+9^2qt-hs z1atJh^-&k}V<-u{Zbx6$ozHF;;S<~oB*&Ml8HI{EQ;bh}PRC%*FMd6Be75P)bt|sQ z(BXf0*Io8$Cc1#UcKK`UYN=%YHkKA>*G?s-WAl70Yz5`3u#dF=6x^v0RSC!+H@HiH)M? z#zqFM`AfWb7E|UtZk*U(DTUYys)I5@n5P!53pOY(U9ges3ksgcP$Vuo+UI=*6+W;? zidd=uj?;#h-K6Kp7V3%_2{44vwu(NjcITT{`&#_(4!B(8Tv>yzktdPlQzT(%|H%V#++ ziqB@vE;P?yGOy6|F3q0iHJ4&9y$6#yxOLa3{XL6dia?Q_zS!L?sgxqT(F+|Fw_y{v zeUXs6qDux>WUKGkTN)Or%KAg9K5zF~q)NO8V#P9JJz{LzDf@c#P0=_~#hKgVkVC3Ugp;CL1tClXq*P6KbtMC0lz4vd&_-JH`5y7~<Xd81(7eIb#n2KB*A~BJ6W{83|BzR^YNw# z;cXW+;$%}oFRG7rjWmkbD@meQw019N6(?yg^MhE zWKEH4v4>0edbwgv;XKx&!dIa*g`?6k%VOkHIpzkuqhZdPHO-FK_PqQgN7RC4*WhQS~-8x*rW*0&Aia5H(u-E_3#ZR3x&8bkK zk8H(aRtRKX2*>#w%sJITyteEMTyTyG9n8aZy|sl6L7%yx zwmd8G31KUe_l0Q6$Pjw5{EhRxjvcLwPVg~7)4LXjE~V8B3SBGC%){<^K`7$W!&bq>KCCMwdQ#=oY^uj2cD4xBcfA!8B9nFDYJ- zNQ&GDCAt39++qdVVng43FdJ3{aS?A#@q7Uy7GRf}M7ycu;Y@WY{ zYh&~O*ot(?i+ZZkxfhyyY29kP6&zfkL?5lIkB z=oNC(+@;IR|Bt7Vm5LQn0V@^-tymGY3Rk0oBBB(n%DwW}|M$!}^V{9u z?&g8st6nzw%%0yo&OFYXIdkUB{N~ra7Tf&)w(|EMN++w>h{KvL@^4F`g0uQu6(O^* zs;=dgFf1)H-bN#*V^!6QH!U3I0d18u>j;<&?L{NmA+>p~$ZI0br337F4iO7eo;e4t z?Pd*RXPow(NV6}|kg;@d{Ua;zb?;!54vFWrAm=1|Rs-hxo=cuJ)uz6-qU*iOX^o@> zYR?1Nu06f-T60nmXH)!5Ck>3I(>RZjWeIe&q|k&m?cTs#E8J+w=^5E{WoCg!gBJx( zf)Uq$;@a0Odjl^4aT=tCpU3%~+>`9^re*qBhgXw|m}7F1H%v3u1Lr=FgKO+Y%w%gd z#vaoMd_X||X^zQmM9)8<5l(K;ZA5`parVo=GaE%~>S}WB0LCJ6`kQ+T9+kz2)%&~u zdt2ZCD+?Dr$fgw$4Px7?ZU`A2lTBi4kVrqq_=(U`?|m*h}2JX9(6* zCq@~wnVHLBz4W4@zi*|#_YEa3v$?DZ-!u^URcKf`i1rfaG;=V~zBkDj5pW)SQ{2D0 z?<8zq(VQs8gUmH$<^;WMw9v}9eR~>PXzaVtoSJ%`0Vi`S+pLZyP<{pMT6AX7KC+nm zwgPMsx=^!=jG1$qyAcds>hNVc2Q!W1Ydfi7tzbD$Zf5BY(g;49NR@@7puRzX3K&1;E&n8qLTt@5FTmrMaEKA>0?ZuGelOjKJEE zH216JiXE89eJ#h)dvZ!OV-lSH%@XrUn1dmVZCSrrGV_^t!v*0Q<^r7cHX@|}SzfaC zHS+>abybjG(ZxsKY0xYy&OK)LV9qD9iZM^FarIx)%!aSi>4VT#Q`W1eIb=F367jb| zx}-4Fat}o3nE7p@(N&D*K)5~|ttA{p&8!bZ8^JRNORbGE^Rca&_dm=CgMw2;&Js=5 z>mC0LMe>fwH;FK+o%1__xf2sMvvdH@TXhaLs$nL}o_F##fXtD3S+CVe0-I>&xIHov zkp^*%z7xsBr|bER-Dx+D3J^}uQ<*Ub!ZF=w$(F@VhIl!4IK8F!(xYOyiBHLftWf6M zpdONK{n%1;+qWf{{oKw7eUKwQnD+PLjjglr znFO2-#hbQ6{u2O#<65IJ$7;k>Wbf?GZe!GLRsKM6+AZp5cq>8_5i_+kR?B{)o>*vV zi4O&}RH*T<>1d};Ds0`F?HlQZI!VUxEMBS3Q)fAV#fb9-n~cJit*eRvnB$w&HO@Dw z?YF4$>TAKbs9)@>!8Q4JJZ6bX=bgZ_I2%2^0$1?(hkYoRFJ7AY)nN0&d5!Ze>i;n^ zL6qmRQqGL>FG{9{TIeSDYpj>eeWU)bTiLaGWfy(>F5;(SMVTqs7K<2mFK$EGUO3L` zx`pL6e3xn6Kph_ptCiO*>2OEQNr`w8U#iGl3zefU&3%cIStm@hMuaG0ou%61ceu>z zU>&{`Q0UZ2;RV8rdDo(_ys^b^gHb2+@^9u1awBD{t3`pg`=7oJ5X1FAy|_ak`{Zr- zEEY^n5G=1N_~MWz`-`3^|BZEfCk4S9nkSa6b5O85zS&B)mWY>qt-XDwDC-C?iKBet z(REM7uc5KmMYEms|796&=F>lUG&71(9>bMrW|rIavH-8H<8xn_^kk53=xFa~&0;P8 zrmE_b+3=a8R#rD+z5Sx@pkak5W4_9G2zX4xN8ck(hllg}n#-DyPW=(E0%zl6OtA!+c@VBfxk_^U%gZ{B}(i*Wy z<0(9Y&sN+_rx-d+4Mi7F84hyj&-n{1O(t+r=ACrg%rHy6pf1D%Bpl?sV?Y$)0jw9H zBhh)eA_gl5r3y9O?$Oyd)<&_pd5$fqZLBsX?o3O1r6n68>7cFG=4mlG#8t)|D{rHr{vg7ijIoK6V z22Mtv*~~bzAs_T$Ro!&HQPsP9YqFOOv*21&&HupxD+U4>2JHp!9ND}2fGF#& z&6?gcFKCk=7NopMj`Ww8>3v6 z_{fYbLnEufsety@|7*%4xm*?VYT;FinI9cm&ObylH7z$^@)*o{-+Wn5@IvhMbj2$(su{1vF87v*7!>LxGRP~IMdWoN&vnj=r zo7gIwr+QjDPGA4eS$F-5=t?$$>iGmP$df~$VC4K!e`g$@^48{+9I-U_T^m`T{#)); z+LSU^-8on(NTwEZk+J?_Fx1TwZ?Pmd_e=6yoh;CM^;fO)COi9O?>VkiroF13zj12T zsr?ag zj=so&WxW=TIQ9n9xcQ7`AuY2=K&Z>eb=^1uXY+~K*0K3z z?x)O*ifZrh*aX!l2>6WpbiH5Hc-I?`jq58{daYPWFZ^Dz6Plq>C}RZHY_}{!qhXTN zb^Z-C)VVo?f|2n>oC!=^c{U& z=03wMF?d~cPQ#+QaT-z3n@(cMlJcLumgZfVPYi+n;o_he9cCI3W+5|2fnZt=L$e+n zIZX0@vgLL4cqs^fH8r*=P+I<$&NrXb-F4Q=9)EXR_RN^0qW<4Gl0OR#b&&TTl&Rg*DXOgrSS#r>#eki6)lRMY4aawg9n4EbyRyo2TDeNG!ay!GkVapN<_budb9ji{+U6p6ngu274D9OQj)EJDW zh8#m)G@%zQ30$^1rpGZx1#j>23R- ztQPGAw!Lqb?;+SNZj?o|d?ECo-C}d(&cP}F@b)(@uV#rOUqZ#UI}hpN*Sa{Qj!~2K z{Vl}S4%@!$L((C4vyR|KWvu+UCIp<fzu|rv7;||KK^9f@jaXFC zvTCrguA#oBss>lGkxsOXNYP`B#M9dBtK~wyyn$f9S_kdH7!9&Ma!N}pG8x!SciIk| z^N+}h+_uXg))s7xbEqoI3!1Qtx1$=D^KmR&xj_o z!m^k62VP|{V2j7hu}V##Wu;GXcJ*ez4sQ0WtLkz!xDufpZxEOPhwarY!xf~H<|w`U z5!sY|^^?62z#>Xt+F>sQ{%?L-Sw@~sr+T4NEuV;EI^oN%X)E)=jLfpYFUIZ68r_hs zmL{VY^BQfD$y!?xnt8R_+t6$rKMKk-BuodbLOec zrz&S_c{Wxy=j~1Z4${54k*|QQtHZ~)@K1mvp=9{<*u{|03Iv8A4STCloJm zvX0BWuYUCh zAAOC*)Be_3Dxx-wMmGjy&HlCVAYGJA-7pv*jfK^wjjOG6M~tWyvhZ$J zs5Kl;I+KUvI2B1a)|kGUwlLDBjVoiLBe-=SZJ7*Lj^V=96khg4&Iv1-#_38s?^Qv( zCmr^u(*3SIQqJCPt%a zRA@M!!dS)j6i9;valHRYuU%8c1~u)GCTwkrpmEct>WE={VfbIj`0+u1Fy@aBoNtAQ z0He{cW{m+WLk(S*2$5z4Ez@WxNwY(!UoaJqrBom4iep2O5RM{)xQXF$d?*6XQYEV9 zfMvyyCY1*3ClZm^ke>=SwUsL5v?pnylORmOe!SrXMi-Hj08xm>C?}`JQ5p`^c^SM6 z?>NO(PO?FZUS+aO0k%xj&C2Aq30F%J(NS+6f_Vu3fpN_i;UuivNva{rjnnNNnR8;wBkRyGWQFE)$sl15eT>Us zC>1aB??fu9y)?iSn<0@OH#-k= z@uqsZN5YX5%TQzek&)mKm^C;aA5A+t7zack*NIAo z^r3hxhM~ctE$H8{v6l&P#+sgG)V&aRDV&PoJ_hY#Ckbb?c6WdTdZOi##w@JmGz~c` znT#jZ%ATICo@W30b!V*WI%}PO!}_&nw662(P+PRp=MYdSLru*G#Dz9*vjpQAz)TIr zkcfBa7?viDG#72q>_*fIMI$t{Pab=+LIe`cnrnhb6Bg_`d7kr^>$jRV1D8DJPieHAWpg!4+#uWQVUb@ z(PYS)1KRpnBt)uYIG9Fht5!fCiCXPEQrBS&+F?1Km19u3u!=p7CRcDSumLU29b(S78s*^YgN|PEgAQ)3^}p3~PL?mP74CG{m>X6XR{8$t3z6 zak^VLf&Nl%FZT)3X*Cbs#33eEwYC!xXAFewlPMk^9H)RKiSc)Bmz%#b29|n zy3vsVE1Ai$ss@XbnlYVgk!NKtWut_%byZbrT`%{!6e$A~ub$I%$_gST0H~iB571^T zJs}ZOrkGUdJd>Z`>$0s_V12LZ=vqarIWRgn2x{l}x)N4w_1X@6g=dT+RtQISN5-sZ z8``lkKa@0cWzP>F7Bn3itC4&LvjvZ1Q8#T$J#Yt;@ezN5&S2}2F%5!flTL&UX9O^D z2V^Q$@ZxhAfe|YmjAjRRB4Q*Mil^+rH}>j9?Hkud29m+#_!~9psHanJJaaUdOkYS} zpU4{Pg*&Nm($-EYAHy8okcTIu5uzDViu!d^#M+E?UYphiHKu4JXo*2L%BfjF;o`p( zO}B;Gn^7Y&xduaBy^o3dC1I3?_@tjVA@%0y3b=aCV8A-rd zUA>ZHA5^Y%FgZl4PJh<&LZ|WDx~wcnOnJ2@SCQ^ndvdkby+@!5n-q9$1KMTS9~_8p zvSw9CJQlUlv6hS{H$xXVDN6|t(T7zM$@m~v_*ig39FC=R!c2AY%k0IhYLPL3KIKKc z2TFu3sNv9aps6_Nsbv_{cue40M^BKbp~_$ll9yP4T}Pa(QMa3B^B$f8K%hoV4<#*-;%>K4^qW1%Vm7(fEx$WR~%`xY7o)zCys zA%Q`B2xwRri94ZmEQxwq2L_=0V|u6HW~~j;d~Xj@4$hFt$(+DKw}E|}2?uBM0UI9u8zO}PJ*Uu&gJuN^&lY&Kp6KJDsPt;}yn6gRFw-@$e{(+W=t9*vAKqupm zwnaddBon|gKZ`}l5mbPG1aG3T8HLOM#)hKp4$@|kWV5NU3~#C|DWy=xB@dBDGvy)t zXg3zUK{`t|HC3yS9yS)TxIs^ZNfas_^hY`^gygVsqAN@f>Hal|rCt7YSS@d|ppc^Z zwAjfcw>uPzb&wtFJkzO=({$vNX$IR2=fU!Jj(Ko@=w77tpjo_+;8jY@a$3}rk(TJd zvn5T*Nz06&6eLfs;fENGtv+~a_OHWNF^rH&0g=qam^F1=X8ev|W-vw2#t3m!L75S? zf?z;lH>p!5A?Y9z=+`YEHC1%P3!8}E$BueJdHknyDefA8=A$o$x%)t|-;3BZyN~DLKf!Pxzqi0L(Y34|i z*p~Q4a@sQqse7DC>VTXJDOkcKIYwL?L$VhQECr!x=WnOUFh%YZQXql7{L*u)h^Oh& zL2ZK-mV3ECxj4sXb zK^mLQiuz$)WPld1;bs^RtYmXKo>+_BmS~sWy+}1{yA7n)CT&XV##NA^aRee+vx=lv zUVv*-C{1)wVU8i)vRBtKo@~wwAj30fJ8fKcvPcSyG&YWsIxR9_bPejyWj8B~Eh;S4 z7e|6LjB}_(a1`&sqVym{0XhWDZ__L23*#|M!CpSBpwL^%5Pfi4L863`k%Shp&O(pC^0K}ow?4SGC6><1ve^z!HB%q; zTIn0T|8|*o<#W+QT zVKZG{d?Tum(}}<)(a;y4Igesxhb+vR{9askxjE(Z@*#j!FO0cKB&N5#X&u*#?Lr3w zVf2Gs)D~K{V8Y6&FVSM9HXTiy37XXo#V|a5QIr|Y6p`4dwH^ktR+0@c`4h!hsAP8o zO@Ypo&FB_t3JpE(XUK|dB%8j|mZO(7d5G&1FqP+2EH?=@u#)YOa4YodL>lZj8a^YE z?j45KL=7O*51HC?ag2$CE1QD&&537@ZH4f`x*7}=jgGGsvm}y2LPB;%(J8rXi|dA? ztDw82hSvs1V<8sVSyVtJr;8?ZGTi6X1UPwNlyH4)xIoeZp_F(9cwofR-I|ws(Ktc` zb72?0Fk;a-T|M1r&X#ab=7dt>!r~S!=P8g)&)q??!eVsi(%eqf6$)V`3;xR=OEjJ6 zS(bJWGW1xV(GPN}l#?9!3x&)kFeYTMRA_jewHcRPVCiAcj_G)xHc@F&YG%b0F=Dti zGmWT?7Dg7WQxsO^nyotHunELztm~buR3~jHLk{B_jg1hMS`g3#jVIWgXo$AsE?As* zNMTZBnP&p1b~>sJ>f$Ol_l{5G|<6%0{lGMGVM)iPfjYe~k&x zYjq+S9kReu-d3q-PY!g#P7}@sbi{Nsn^ihKG!(UHM~+7bY6ztkCy$^>VV7aWxH*_4 zq~d|Wos??UX^2~B$1qz_;*&R47}L%MI%B0a@kFgn`A{#jcV{34M=kDfZl4;}10TvA z)fr1My|yRS4h)#?!6@vDufPd2R0(~SGh}W0td0(DX38|H&3d4)IM$kl3BtG}0YpUN zGzjPfA7^{BDQPEax6mIM3ftj4%F)4DIk1S!4P%+)ls24=!bA}d2@U#^+|E|Wh?MR; zJ)yE(u<38cFNoNl3=ZMw2hF(PHly{1EzUHO>S9|Q4jXE=TNMosrIbDWH1i(uU6x$`&3S^Ft@}qBpHo7;rda0fwa6nWi-zY(ztLLz)%#nq%RV z-C5M7&^@W3lq@%BKJI{I&?7q+8AG}a+6b^QE?{HS1By29X?TTTfFz4fJ2xZO|7Ozm zq5Xw$JP7Qbl@fYC5e|qXOQ|AuKzC*f3}^JP)}Z0H0vpnxl`Xa?%yi12mR)w|@Untr zyd*#`>BR~<$h4UUo5;Ra94f?SV2v`<5gpJTNF~*NA7}g!N5gWZ{BJ?>qC8h~Iqlmy%unDgiPowrY7_wFu-;CZ0%Og(w zfwwR+P204*fo@03PK5W6_@Qx*4o>&MJcuZqFAC}NZW0-ex5aThGZtPkZqJLvS)IY; zM({Qv@ib(G86!W7(bxzQQ&J%E@>nOu>X-81XK+I%OJ&M5;j z5H?1A<`kMkd~|4db#y#IQ4%ULPb&Uk;7#W+8Mk#NY_*4?r)=%jPbuu2eUhpoXp zvLF-RdRP_ov0lV$l$+Ds53=%avH2w8rEkO?9LU5AxpcnutakUb=0$VWTDwwfrD z53z=v%q22vDMpGJS8tF+D+_wyBbZ8Hd34q=Zj^-h5RtRe6e6>CJHoL}=amqq=&U(F z*jMv)cDIOhwu4A9lCb1xOsfghJGw7m-qG9NTHEOw1+QUffm`%ZC|FQv?VY6@t?yga zWP}0|W)u%nGI+|8Fj~|>FcBTVM|!Gfn&|mkf@OB#2;`@eb)n-T=y~JELR=p@)FFa_ z;(GN|jYK9WIA#a&MBWjD_5$@z&joh6R((FLha@KY71g$NBiN5J-Y{mpoY)Js%9POx zX)@D?WYnXq7>dP7jvBoW6A~HhF!#4YINQo)Gb=O&2$D*VM=e$b z(aPS8kFv=fZkjZD4C|*g!4y`jSPyUmv-*YR279p(F%}bMZ+o)i^gOM%rdg5tLqrci$7eJ%Osvo!L6m4>ILOK$+R|$KXxb$C1*;Htieiw^dUnZKu0ZpQ zM6uAansLStbA5A|=spTDftfF?)7#ms8l}_3ikA1R7>_`^#i)gJ?!|@&>^Go^9_JcQ zry(OJDWSUia&)0)I$ntCjU=!DqxF?E7)?y;d1rJWP1s}&sMfzV(6DjNnkyAVr{RQ( zNDo6^46+n02L^Q_3?|$qtp;cgwN;=M5!?%zptWUp6xOm-v)+Azy$Oxy$2F0m;pTxz zI@N6@dr?2EaZ(yk05)eJQ81rrIyb{Sid=$8DCjWE_tKUw*>VXTrC@N&N2^tE^o8BeNjspo6~6!|`-)B2Lp6_V72iX`!z=R-nD|46Q4L)odgw ze!T;ZAwxD434(fqalRJsdi0>l>73B@2@fm~G^gy$Dx4$vRHa z8qlVo$Sf`RGUHrSXfQZ7(2ylW3Z~Lf5QnZO6^ zU0%UL){>zPHb&oP{d6VbE?1 z7N(Cl$Zi%Lo?T-_F-0qS%v!9QX%xq33WqY6X*3q7HU{OC)d|RfW_i7r)27dfXCbNA z;GA+*bCYVRsUz52-Jn`(Yvd7us->D1Jqb@60+~OO8WjR@7CY5>14MFce_g z0iS7y$%xw_sHdSJn{q-Ba3;5mHe|ABPPH_3%8Suy>CEl1S#>R#N*YPYiWZaA*vzI7 zFif)xX51>pQ&lqNuVZtGlZUBkg;28gpzfN2d5QQqI~}4zc7>Brsd^8B2rw;~Xcm|; z(JZsFm&=r)@N0gzwH({Aj%IM2#-FKjWCuu6DsWWshH7n%R5WZyAaToSr)+9DEyDtD z%3`WKT2eBb2QpM6l<8>eC~~^QZD&AnOIl`&LAN>6v)Q{qdf;&shAGJlR#8sP@m(Cz zf9GwAyp574o_`qxi}{f$0|-eZxjFL#_--fA|sLWHeG!2+fF;xnR~upau8Lm7p1kd za7!bDgW>SS5MuVgC4nG>#bXZ5N)#+k!7PDGbAvWvoOiOkR#8=&`KgF#>5)psD9=ZL3MMI2BNxZMiemFNA;yeK4++~23^jxXjUcfS!4p)ou z?85UgfL;poEB9i+vND8`|73+%#@SVW+lME9Mt*9cy5F_byO4qo=|^$flq%l~_yWrF zxRh((Lgnd3p(h@pJpD&2m*-VV?O3BcyE>I??>gmqegoiJlxOX*ay@fC(j}EEky2{u z70P$q70R>c0|@^BDt5hc4PUR+@Quo~>o(=N;x6RF~3Q@-%eRQax- zDRuYXl*^Bz4)|PZX{F1Ru5_t;4s&^q^Se~txh|K_a;d#>mupShr50{M{FScq$tzu+ zbH3p6dA{U2y6?-t`-;nT(N|sSs7G9`@FOnI#P?h-|D!I?W&2&OyMOFD#QzxZA9I!8 z__)i}J%x0?bh++%8u|a1%hmq>TYVG{uI?M$D*Z|KAqPL{_H=*FtuET* zc0IGlU9KK*yLLa|R?qyS;v(5 zrj9977rmy;wfFck&od{Mxh^`XOl_?%bB&!+rXE~T<~iq#GS@ksW$LIvnae*?=DFyC zGS{vP%GBdq%3QZzS?2NIT;_W4<}y!WSDDLqSDEMeJ!P)p2g*Eq_LjN&A1(9T{dk$$ z;`6wM=X*Tui#)DVDm|Y4M|xZbUheTEj`tk0^>{p=;PH%|^@U^tg8Y3atEFly|_RrvB)0KlMkCTG~+Vd2o5T+WMw)PkKeUYg=1+xv#z4 zwX3~c?HVd~?TwVHQ!XoaJ@f8zwPky`>!Od9t0(R)cinwoxr%r0$Vr7Nv>{S&iv8KYc zrK3V!)K%dMcUP!|XIHo`I=e!tiz{57ODfdSpQvy>byJ1s##<{~+it5UzxB2Xb@it! z%J+Y|LY=s$!u7=0E7XqfR=93`1h_x1C_nn=73$!DigM3$z}?_24{z|Qt1WNY-Z8Iw z{=;6^vTMBMr(EN8U38826+5o+zI+d!hd<(V@BOHE-m;H-)iWRWR_y+yx2*3*uj}d? zz3xYE^s2o#dEJ-Y>{WN)>~%kXv-f4)w|Q0Fr@Up++r93I+r6%%cX-`vc6ikuz~N7O z%MN_n>u$fpTXx)My$hmW@>V47@y=UwpI5zbpVzhjD_%ABfY-I{8_4S$-udbwZ^e#> zyk+g*0`9lGuBL~*WmEgSYT^;Ed-o$=SO53C3l{#+tG4a;E~puH2uWRdL-ud@D zj<)=#*S+OGy=8Sj^{QKc>UHn@skiJPo~x#i$1l9@7oPCCwmj)|r+?{n?f9kFy$`VO zSKhLtp7N^1Q{IKkp7yHl-+1S3|1Huy>vipX)~ilD;4NG8oVTLo_g;1ML9ctqLGMv} z{^V6hJ@0ir^%vy%0`mN;*S+j-Xv^QwPUTZOl+S&X%jc>r_qprJeXi%dK6jJP=i1@( zx%c{f>YRB#cmF(}Ixx?-;G9aI@-6Wlxp#@L;=qwUwe~1q#YHdkxvzMcuj1%e`W7rb z*5~#g=TlS1`xYK|qEGER(dVvwov-r5D&L~98sEaDwLbMstpQs&gXrm&bM%1 zy{~et(O0qmKYXsGr=Yx3d>E&``Fl_GdCxx0r|vn;cf>@CPd(A%TlnA#Us+Wf>fGil z+uiO{RV#h&@JgS$VwKOmb(K#&4)_#c-D;oLx5lUX)}ZcyJJ$Hhj_UBakL&Q2U39u{ z@!eWeDlxgMt*^j~nXlL9UfSy`ySvxt zIzZ2TKG&K)-@I+>eeNCWeTP4>-nVq&IX*Rcj<0O%xxRT@&jU`t=Uy1_E#4FGl^xgb zJ1iRXRkRHF%9=vHLo35R@3t`NY5A6HkDy-X`@GvX`pP^}&@76!jriv88S%OIji8<} z-~6h$PwkKUyxj@kVqel%Hk<^VQlMYTH*ajzSGI4HPc7W+^A3;s%K9!uoi6lM?6}D1 zYP#6xUVE`mJqq{);MgVTqf3111wi$7Us?2>DDRys?xITG$LxH_fEmcpqjJw;Q1z3XFcn(v$7_Deja)qm}aDk@?*ZNgvW#2W>2A68COi@!!-tw8Vv{wgQx1vUt56iEF|@yi7^3;Yj( zrwBY%;F|=VCa^_dtH2ck+XS`?Tq$ssz|{iR2<#Aey1+98t`*oRaGk&|f!zYn6xbuM zS74vO^#V5tJWJr&0^cm~Edt*v@En2X3Or9>Kw!VXpuhovA%S6mmcT)QLjs2dMg&4# z#b4#bMuAa*BLZUr;{p=`FA$g%m=c&4I4W?Hz|8{31da>5P~h7HUL^2hftLt;yTA#7 z?+|#Yz;_C~OyIi&UM}$601b$H9hXh_N@WTSH z5qPb@Z2~_c@H&AX6}VmC#{^z4@CJdC0zWSB69PXe@J4|*3A|b0Edp;9c$>ga3A|n4 z4uPK*_!)tB2;3>~vjXoF_&I^Q1b$xNT>`%#@NR)$6!;~9Ulw?ezn@TPSQ&@K2CP0}G`s6xQ-Qm(TQ4%D_Tt3x)ari}86}C}m)ww1vX@|C{kW5@}$e zw1vVZHGY*Umq-JrB;4d@p#Lva-lU>lp+5uJH}~JaxBh-L4~0%BmfOmPv#rVBTmD-c zseHd$Fkh)R;mP=2hbmPM|Gg3qyNNm^%MUw8jz0kZ;;i_7hri3M)Ztn2_m(MjOqTyE z4*xvhBSuwRJ34$lj-{6x!ae}lun%Ha>dPjxo=4LkfX_^Ev+ zege+lmlWw(#`Dceu{l|A;q;e@*-vJX3!d{~36u zG{%36!%y=&)zQT7l6WIe4ngM=PZ0S$_*Xgnz3@{XneyHaKkQ!q4_oShjyT{01M>kKQXVev+*u3S_@l4~#_%Fb7c7D_rqW78>ZXeZ`+H3qYwyD0xPieFLB#)@RCZ5JS`F|<- zQC|@~jGyFV8=Pt1Rd^N zu2TJOQEqzI;#~iN7pS~SRj0!%)msH#D)1`;yDK@rLj-?U;B_6u=9lT~vGebH-mN@zf9W=ZwEZ;;B#Q&l$g8;*a9!y!f}3FMB;^t|YNUj&-zoR4`YK8uD)^C}_7n5%*C7yH^`g7vHb%yv0 zB%ZPJ@z3ya{og1FocZ4@@x}P3c|PMC`V3!7{z`SXz$t-M+c^AIfzN-O{bzqd(rd_- zXpy> z^|odQ&G<}bKh611lbG|VREZx@Ouq>(3h8_#ouOTL1fLhYu8yD|j{jcv8V--WF*p2h zNuO%S4S%_WZ*ru6gM`1s5x!c&-|q;INqBohZvK}@eI_00H%WSD_`4$_=lP@I{XBHAT_~iliTxa3?=*FB1O6BKhnu68@}&AML2;aYu1`o#n2V@G3|8 z%O(6xj_{94c!wkWixTdn+nRz6_onArJqUd9X$2R!9%y`c$eDwH^kj z>yw#>Ont7<;VpW;IuU{7vj0xj^I9T1Xxe8#Vu?mo*K+^1iQckP%aLyVPBzt-{s@wMbc6>b{BMr%_e=PR z4Y~PTC*f6&@LP+7?~-t*{&>HHuXW_}2MKrTah1n&yWZwV-z4E*)!~_bC3?_1N!=3f z%r7Y6KXl~x9tr=gBm8y=f8G)P1qoM;xpZRsS1P7=rDFPm|1^H3V)|vE(aRR-d@9u{ zf!y9o#qFz9XGu8MyHasID;3uZTqN}%-lM;}^?OI%?7=UE6K|8t{%dvU!BbnPJsG_0 zwDjzUqjz5GuH}BoP;^DkMpTiSLpFtoNfl^UDDp9Kr=qg_%-}) z_}h#ZGd|4tG~?HdOEV7baVPoL3OqsJF#?we^a=dy3EWRV75H6&dj#Gk@RI^RB=B7V zlL7|>zFFWI0$T*0B=8u4hY55Gd`8CAV*-y8`B^FO1wYsG34sp_{EEQO2>hr(qkkE_ z&FFPTPciF|PaV$reqP|W1wJRR;s}nvLHt#spPVZ&E^u7nEduWm_^`mY3I5~a|Es{4 zzl!sJy};!H*9Z&>ObUFvz?%i$DR7U#p9?%7@V^AkKZeVx6!=B0})0>c6?5@_rN z9~1vg0zV`09)S-E{EomM2>g}6g95$BGTmM#@FanC0^cm~e1S=UmkYc`;LQSe3;eFY zCj=f8XzV`6h`r@a0^0?iCvdYsW0$#7{I>}Fg1`p^?i2U}fxi&=w7`#uJouQv9RlwZ zc#pt`1b#>0lLDU=Sau@Q>u7=X0#^z=OW^qeFBbSgfwu{~OW*?n9~Jmxfxi^^g1{qR z$K{?Ruv_4H0+RwS68K($*9p8?;7)<}34B)I3j&wCUfL({4FVel4hc*OoDg`uz!wBA zego%IEwEAGUV%Rp_@uz+1uhml*((ITR$!yR(*^bmGB5=ZaC@M3`<5_rA9+XUV#@Y@1^E%14P#*TQj z*b`qb@J#~WDljE*o4}6?+$Hcqfl~s_``(Y}iMw**djjtkz2#Bye_!Aa1pZLqet|y{ z_+x>O34C1Oe+vAGz@G}768JNLKNt85flmm0Qs6HI{z~9e0?m8s=KXZ@zWT2v{%L`~ zair&TRa)O6ou?9xbnD+qIs<33)S zny#Y!cAD!$bZ+!>viT2q18fqGAnrfEUR@GM5*3{P4*45V6Hqp{}96p`oF%p{ZecV^w2yV@+di zV_jo?V?$$OV^iburmCjurkbYOrn;v3riP}*rlzLl%TdMUD1JGzUJkV72y!%QmR?%# z{5^mD`aaAmwMa5DaQ6!&1*+7CBy$7jnttN{aXvimj66|l@~;%WH820Uwq;V^B3GsM z2%>?LF1m@ON?j!32F^8oi<|SgL-H}uUq<0O)h!Zkpm=8ui0sA@(wa4tZ>6$27+!ih z!yC?Ec=1|>t2!ButYdg}7sKvuhOsjlZs}q8sa}Q~`WQYh@N?_gzi$J>Kb^(!m2YO) z{uYLjw=yh0hvAa~C(mX7ThC+oXMrCIu>Vv24DSsxd~Sf@@nMGD7Q?p=_QzJuZTr3@S2$?z$GpT3O!$#*f_emTQ?-p%mw zD;U1;9)>5pk74%~hLI~7UV9b8d*9FSH(MDV{XvGEA7Xgv)eOJzVTRwihT(nJGW`BF zhA(`C;cGt1uyH%X#K#!kbv?uP+`#axNrv%{GrZ;#4DbCU!~Po?{z2fqH?jY3H#0ot zR)&e&7~c3PhTpxN;g@$X{QjpI-tZZQZ@Yux^*b3}{#k}M-^uXnpJVv+E{2QlVp#J9 zh8yl?c-a>je*8-ezx8E?f4qm`5%)1{`3l42ZiXNHD#JVPXZW43F&x{&u<-$ggI{NO z?Kc>H^+ATs4>3Ian+)$482A?Z58KPI@!Jf84>SD8cNpHckKvEL%kbNeF#Ov07=H0l zhEIH-;ZJ_RaQ_b(mi>s~8-C1i;4y~peVk#{e=>Yl;F6!Rzidk2&loQHIl~_aEPI0e z$3DsMj9)TL|BB)Erx-r=YlaJc!?5ldhUfm4;pM+$xa(PlKR>{5(eD{H|AFDaL53gv zBg5T)V)*Mn6ZFIJ-|-H6=T@zU`c1Uzzm)W{gUEA?QaoZK3J^tuT;bDqY5z_K{=`1+zny|VC9tsw{%!~Uq}1OMyh(w@ z+P}|%-|{5W51gX^S_BrupK{<&2>xv8f53s?|D(d?yO(E=KT|8I9Do^r{>y1Muu7jX zqSzMt!Qbn0@clYW|D4X_*97dA`qNsU{#x{}QpMU|Dfp)R{_k@CO$gqkz$c27?|0x& zUB~!D1^R2zze=su_o(NO-zo>bdW7-Sgdk2bd}|T>7AJm^>1X)1MFh#lBKX}7{FW5s zJL~`UBKZ9deDzE2f4YB}{#x{Jar!45_$`8Oz8f(U{)7X6Lhu($xijH!b>Q!wWBHQ~ zeDy1C|Ldjveu2gKZ>IylU+}vHZ(;`g-46UI!GD$DwakFO&w=lMitB%z;PuadKjpxm z5d7*{;vaC}PYM2OW{I!Ppz-3jN|AyOd z?p2+s{0Rqszu;3Y^w*+)i_>qb1AkKR>02K3*P?%m<4-#9rv!ht{Ik=6?|+8dKU@6W z4*Y(>KSb(1Q~mcj@F(YpKjpxmnj`)J2fqKe-2U0}k6N2M{`&=gw(`Hyfj>D%e7^&K zYL4}pd68zcHZ>Iy_e}LQXT)!2YzjizD`vrft z{lCwFKPmXLrT>%ze@gIYE58po@cqxtt^KOgG5!UgRvYx!qJN9`f29L|qKN*x`9HGP zFMi1tnuh~_Qp%t0{9Wb1-(95qM~al+;=rGh@@K0*bvy7|e$VuuZTOWii(S)P^ z{y#ANW{bbof!{wz{7DD?{)V_V07x_sa?qf0|qSyB+xbg71-`X+~->`O)vd zpPVCp!h!GqG1q@m%HJ*U4LS0|FQNF-p#y(P@b8usQv#P4!Qbk@-~AYuzte%=UIc&A zfj{|pVf>yV_&Xi=6aUHh!&3gFz)%tV-41;JPYUB-Py~OU1AliB{7Z}APdV`We_FWw zD~sSCaNth~|G1^yEdq=2pX$mT|0-SBe)e3E@+%$qe*OK^Dm7dE#qYr9@2^(rZ?jR= zXR3dd1AlTAwWms-mCDBd(5bVVKh+e|f42I6i?e)b|7`V#ZU_EUk@{D>X_ocZ-;c%l z5o$dCTJ&#m{juLsev7ohTQty*; zch6A%grofaBIWO#W%*P3`@8w}|6=2BtD}6s<2$)6Gt__5fzRKc&3u1%Cj6Za{K+Ei z|N1Q3KOy|%w0}IV@zEH)e`#tZjRP?HtN)|i|63($x4?rr<@+U6sol={3;kwmKiucQ zpA!7p>c3MCeEyy>pWCELwn$Bj_5T3}{*=&vjo?iPyz{i#jsN~Ng~!h~XNxcA&lbzR zOTWNk^;g|gUdDcH&Z%83_1-P87=EP#-<(f-R`C2Yl<#-o^EtLEHQVy59QZ9V|813W zl}w~h=FrbCwL||p@caM9^T%xYr`v%)DfmQH`fJg@)5vcJ{*>U)*8ZJv;J0kz`QNF( zzEGt76At_d!8Zy(i!8(|TeA7rFRj9fLkGTJBUUN;-ZK47=-=Y)pLF0)7U6$?PWdg8 zYX%tkGgUJh-=zL6 zPQQH)d~?3islOK+zf%r;K8IPQZk2MUW+?xF17CTFPi8-VsWWrsPmACS+s!zBDjoO} zb1dKQz~B9j!sDm4wJ`rzIq{3kpV#K#w+K};!0->BAF5RTPZC{l&xk(HQmOjI&*ys( zE`C0*Q>muJ&*yJ|f1}{*^CZ9*Kc9~QzWDjP3-HCy=U0G#6XWxF6yS@W&zAsS{Cr*n z_~PgDAHctv@%cOl@Ws#PGk`CCK5qeh@$>l!;NQadd>#V$;^*@Xz!yKCR{*~8>+=V| zzm@U%JOS{<&*uYxZ{qcSJMhKN`}e@Vjq!Ou9{A$t{dM4rpZCjwFMi(t2L7iQpZBwY zFMi&i2EO=tzZv-A=lx^g-_H2F9}Il)^ZqXI#n1b-z!yL7zXE@U;OqTR;ESL4M}aSX z-tPpy_<8>l_@5Shy`Knt@$>#7@Ws#jg}@g-@Bab+GlH-8FMi&i0KWKnzXAB-@7MNo;D3(sc|QR7;%ECl z@Ws#edfu_~K{#8t}!>_A=m$pY313{}SV~Jq!5aXZsZJ z#n1L8;ESK_N5KEG)L+|!fG>Ww?*L!?Y_9>n_}Tsf{CgOm?J2+)KifxuZ{oGR1Nh=+ z`vvgtWqhUW5x^He+ZTXu{1QJUeqR3r|31d&^*r#!&+BvGi=WrqDBq;l>o?$kh4Fbk z41DqP`WE=&=k+S^#n0Qtz)y`D+9kzg}MfU;MmY z0>1cp{e$xFm-6*{IjFz*d3^$W@$-5E_$Iy9|AGHC#^?0_@Ws#iJ@CcP`a1B%&-yp; z_elAs{^Do-82H97@lz(f*585ufZ)4W@97sm>zBY6KkJJqU;M290srfa&-xtdFMig~ zfG>X5w}5Z_T7LrmHweG6s( zgnJmy=T*Lw;m(8G3inO8=i!d?DPK3-6>xXL;SO!zqIt@PJ6C*zaQJqCPt8}pD!3lF z55hePw`hSXYlpiOZV%i+xK}MyWqok(h1&)9bGSu^sInDs=fhnE_e;1_7OApJ;P%3~ z7pt;k;o9LYhr55Vs(1?SPm7gzS*4nPcBQJ=1a~u@4+8p^sEVtXsCjoSQRP2?b04Za z$HJWg_ZGM@xJTeB4^y6IxIwtBa9@IZ0Q?r1JE^T?)77NL7B^QL4NFt_LmwcQxFX;P$~i4)-+Nf5Z7+ zrpk|i^TREJdlTF`xO3o!;Vy)`{AJ2@HQb$Wd*OZp_up_!U#?uQhdUkaJh+SCu7=wQ z_Xym&%2W~Ii z<8Z%&bNf~Kp>W5+HNdTc+WBnDGFLK8Vg6Qws>N^b#yEe zjRce9E61!5XTH8?ZEl(#D-|D2hOAV3Bx!}x@#J_;5){e#ofSzBcZ982Ix-lsl9@b^ zpgoulcE=;Jw3S>HPp%7&=q4~4qF0T^Lg`36HcgVwU?j%rDdYBtPLF!$(IpdQXpxIx zLOTY#lU53~&5iCA0#YcF_Khd3j&|~_8yy+2l3ZNIZ*rrM9xG}EQ@Uk6qp`K|cmlCh zZym`Yi0FuQ#=|;QZ#o%?4YkEb62WA4fbMFdW*es0bpNrOW3 z@DR4viVdZQbCPui$08%6BfXIeE%eIDSSTJweJBY!m@-`(Or^Vns0XSTj|r_1hwN5G zqE_#CDs7GQSP3R%kh&1qt6M`CjDl6#BdJ6*IKCe90RJE*#^|44gFZ&IULKv-@ z@hR=fO<;juEeq^%kR6Dzvo;PW?`VP;BL$}Dpn<D;AB!4DYOsq%@&v6bY6djjOvLAy=<8Z3(6}_QiAJg@O`RfCW)ki7Zf!vx3P) zL6+?35+rVaM{E$q8d0PI9bIcW+S{#7k&qUyoxyZy7$a(B)EWWhz!_NyK!@&RJYgk? z>vF?I^6NmsMe4Qjp$LeJK`ZI=qe#>^utBa(C$o3XD|kjEeUlV z5=Oluxk^ZnHH0p-l1vmskhWknIuHzPoGxvTH4+EQ6i(V38n(itQHzA6$lBbzsC#uk zm?Aqzqv=S3rcPa9O=1w0I1`PrDc*s)1~J90v&O)99bH`m=VQvV$74qqHwg1(ENv#A zwpH03*$Ylb|08cxw1nvHmuC~D0_%D)+Y>-TBLgVg3iqsD(H4)!lf95hsPDSoc55(* z>Ptg;;ZKf?n5t?TW@ zM!?a5p=f*{7)3YP(FtfxfoMD! zt}YnZP$;yfP+(o5z@|chf$Zw(@>A#)E3X0^npXoIoL2=MnpX!MnpX)OnpF!>H;@_* zhU1$9Rk*WCXpSYI$Sld)i<-GGRy>Kn##R8 z0vQqy1+uE7(@JoLUOA~Vl*tLMb<{#K$xtjOwX=GXI#a>X*bJPt32Ga$=}06sLUTj_ z;tY%$Oh7;dLQ#yTKy@us+e#+m(4_+b=6j@s)C>et38;?gLEEd_j<~>JFoJ0o3G9%e zQOE=i3L`fNn_wsox({4vCF3f9DFt){91cfNP$(KF6;`Lf&!7r~qY@U5V!8r*hNG$R z5fvC5i6h%Vt_U8`bNEHM7>z^G6_Hq&q{k>}w(v%wOOTpRo;K*oC`ul*tRr8$l}Hbp zG?-_3rGxNfaC2q~9g41unV8j9Ov*rehN2`>DH#czIXcQzyG7-pj^0N;j5HPnkfmUQBS z4rnDY=$LU5@y#lLA25~S8uU1pG~vLIl};tB5KSS7l)#PFcmP_go-ne*!l*N;(k5&K z#2ACjPokn1D5?s`{6Q47||7@3N}pZyhmlJvMCia#cobUh(?%FF?WNCX=}Bf-&Sxl16buD0>c$5J+DKf!Bh%M z6)S1QNbl%cae7;zZEb6BZ{RrO(x*ErFBsZO)SmVoDC@E4lfi)@;E2pt7}}s6lf}T1 zq!Zj$Fc4ZA1dt;&FL?5T#bnZ&Mk5EnC}D1GUOGKu^FpDrgf>EBYDHh?MVgh7qav9> zo`(UgC@*ZrniZ+AUS;Lwb{6C=4Krt*^dNEsu85>Zf(b1Iv53#Z(Ghu}yxz| zB1w`4)D?$fEL$T3(eZ$~>(Bx9mm{?-#eAffT~{BasjhacQ0|f8fTeAJR$v(74a4w@ zuA%7QW_9OL`d(OfN`2FXP6?>@lqvUEKz$$0xy`N0x$dZTf|O@!gSycTLB_H@puT(9 zP&A+g8?_Bmi)>5JA5QADo)0J%i>$vMo>rF~QKlx&!52Umc$m8q$7RGHd#E`D~> z&)##D($%Ak)eU6?QUUeD!^+ePi1Lu$L)8uF_@9@-z7|kVEz&kAbOIZtw6>&4c->K; zb$ZxJ2i3NfsQr1!B0ZqCdWO|QmFR-eScG~|}XcN$%Ey(g6js2?m; z^H!;MmP2(>yUIJ%`@OwT$jBV0w)uK!NtYzTerH~1aHFMuHLsgi%`ePf0pBVt$#?{- z`xc53aed#argR*?)eV7x$Pnh^NHC`MEmUrZ1@(b)($OCDDfjB=cw(3>#CMdN#VBQ{ zzPYeWU53U~w8226c~AX!xlXQjR_GMp@KWjFGeo<~hxjSV$-O1Ebv zi41j{7mG{XuB#S$)b09rK{uCMXMqPi^Dt z*>2nafqCj$(BT=R^{88S8;s9oFmOrZ)AO@1@UiiSfU)JPgt6s*`aPh3_h8r|C;fZb zwlY*tlU3^Xp7}NdNeFHq%+%u}^VRM1L5yu>E|}plMPlzdLr&?=zpK-+iB<-(7bg6VD1P@?+{#3szzP>|D@mrMqbL z6&!)u0(o@T0_yu7vS6rt7NFqWg!#l?jsGxJBBdT!psoWeuZE^)CCS{9Qh!>|gRy0y zzteFJd0_#K1b)7Bp}GPcvhTY(mm56D@$Sdy_rP!I_v#nu_d)%8Q2*ZZ&T@p@xSf8t zUSEOVZ8y;GZvA`XP4s-@Zu;GFkba-|BX)yPv!5;8lu}!;(@AS|3%dxkW=W~b4-qBs zvxgXU@ZLi_>foOUOQJq*M}6TCH;zqiC~Z$5F~dOUpnEQa>^hR<|z-VK2e#r%{TJkO&u1y^u7~OO(eLQrM+h&X-n&F?UE)!f{eS}Z=--1srYLL@?p(r) zFsKkvP7cNVgjG8VU!r!dRAo}%{Y#|2KUt!SdxCPqmk!Va_@1gqUGaO$Z9tE<7nX$d z68}TK+MT%e0@1v7A ziczkYmhW9U983k0@%V_^x^##-?V6=9m%`eO06s=Vb#*qVrGzwhn$zuo?~ z+y8d^pKjN+bKiA6&UIbqT<7fU>~yXf^RB&m!6LpFeLv`2r#x}8+_!cdlRnQW7<$HH z`U54cvh((^^W6Iy{lSkOC3EnDc8L=nKJ&VTSIsdN@P5+y&brYV3v%dB3+^vctB#+t zcz#3K)h3W zIz6iI;>iVt^fSe&bH{;4_Bmw(dJ@&&{p;*Jetzey z8+fVn!oW%HYY?SFrtovRPln8J_Ku|MMz)9k+)9s{cmncvQYJm+&g4JL$yDaNYqO?1 z2TsU#?o82_ujJXs5@*Hf&QtW@vV>lOq8In+rz1T}r!SW;xWcJFYLWBRKw8s#=-9j7 z`HT6b!YBK=zr4$@h0_+f!J<6(_vOEa$e*@n($%?`Uml|$RwLY72p8-8p5{bMp}%&x zm;PG6t{I)XM$l6MdVhx#;r{6q^UJTw^vhE}_f_hy*=bvTK8dc(hdH>4{`Mv-eciS6 zQbwNhP=ER}H$98SZFuMLbmw|%5MAM|Pt*L7?k^_O7X7=ox!-U;?N84T@=nXnc2s2>_7WEY^D)bDc2TyjsO~`h>95B=QWzgx)Gx!y|b;q7N&;0{=&NTP+m$~!0mc7K;lBBDdJcWPPlOH(lpK&Y4&>v0c@AB@( zIS)@dd-_@PmdtT}nM6O|u5|(f7B0w@KhDSotUh+}+{JU8>XYg5k*)^q9vdKkWOp7t zhVJ>Ld<`s}lj~FspgY!!zWIxnV)^$S??jv?Kkb}P2Jqj>+`r&{Hh>%$t7C*-kfg z?OMLXx^;Ys^yr&&U9R)Sz{{_8Hq*{l@GO6qUsKyUa8CZ^&UX4<6@8C>sJK(;!Pfa{ zApJ(TZLrhpboce&*9Y;9@ZQmrow}pxWg+_g%lYnTxf=}U%Y0Kao#!Xf)xh72akpWzKB)>F?fwDIeRX+l z;$)}f7}~b3soRIrX5BeVHnQuxuMXqw?Bb?jUEBML?1C>H>%RBF`E(?<=a3le z+d7fW(@m*WS!JJR^xCi3g8bLY`t zIJ@o*+mh&~3C%^j^OrpDoTcs`cF&@}r`8Rm6X~b&F20;#3y({F;d>(`JBafg zL)|}R%T&JeEOAzkb|R3ys@W5x=`s2jUdfI&9@qRj$m8xiT}xj-jCQ?W`GUK?wU6!# zIZL{DfL6HTH}^7VY+Myxb38Llu970UcZS+|&gH-JxZWKX|H0?OGuN%{1X?UCVfMK5ul7*4w_ZP#%3R$4cF>Us2Dix-CTtKoQqo6|sT zRR>>w(O>RzJKbgN=W)dWUZOeo$HTi;`3njIxA|zckUK8EozI8eapAo_3i%0+^Uv_B z=bTHw<2v7rbRyi0ExiS9j?*#1{V_#-mS0EUN09q-@*mp63)k@s)$X{{9oLlcciL&({mOlQWY?9ufiAiHS)Q%l z9Tz^wGcR%88|l>1qy{3bc{z{U%6%U93jTqc3jS8bOK$lxH(gIl3zOV8IngVD7tmwS zs#EAqsO~dPXH$|s;PAuLpJUwzr(fI$r>FJ7>3e!`D%rr7TIr74H@eH&#N)DR9#_4= zOPhY(wU_g6y?c_JN9c}I`zC!e%6;1EtR2Mhrmp*dUKIXm5P;k$g{rS7=g9oKxo-)Z}h7bMTP-xxh-L7u#Y%=!KVC&K*^+c0|i zeENG=&Sdw`(?8P17T5Xo$LaUPmaXn;Z==iUGu^2_ZpMN<+JSZ-$6M^|J&v!1aPlN) z)tIyC1psnSbeY@;4UW>-hd=Z)S)R!KN9vag%{U@5Jo$ zJVY1RK{L}0*2Lo)cU=AzpRaSr9qzdJYyQs4?|59+%omQG?bGA2|8;G^dU{6EOta7< z%1$0vy5lx?T(yf+-buPT=XNKZ*<4OpZI)>JFeL6ULfAn z=zMj;l05f?WBKdbKDv|+nuE5k)#u@-8jI=K=x_Ahs#gAP`OiFF{*QYZ^7k5NbqYNn z#VyMB{p~05uX_)k`0wwsCVM} z>ux>eT<4us_kFzo{(|$sc;olimE+HM){dv&{;r}wgwkstU!^$_{CsxpiSkf@TkoM$ z=;k=UD_d81G~I<}I-iWyz3!!P?vq9L;coXh1lv!xo-jHUsgs?rC+JfW_knxUM0&2O z-*Mia_`mqQXZOVH|Lw04dne9t?xFRU=ht{Hy?boev+VEb7qiX>j^=CpK^_N7X;UKT z$^XMAb-UFXCpZ!M=<7Ut5*}+-o=QKQ)^TCJR^a~d-g=Vz<^Z_{zfYq_uyVfqq8E8w zM*m%a_JYvSo+Zu?^#6TUzQdPst#j8nJ+XVOcz&6W5D+_LP--1*;A)Q|Ga^JM+JT+ko{b@6g z|EN$pLASVqwz%jqUflA>d0gm@Yo6fqr6q$scyumJr5o`n{$?o1a zJ1(u@^DXYU{3SkL@(Pa&D&6n9-64Sc@V9oKId-1YGKDP4MJl{@!F zUT^(x15W1z>(`ebcvs!jHIMV;L~Dj`Cdz$n{Y3ZovsWiNJ1D8I<_ljQ;&F>RZrsf0 z+uU)-dwjnB10ENC=+0Be!+)&Ts(ycr$m+@zOzJNbe$4`@2u-2YNSK1?-)vw}l z&0`ctPMbODTzbRN)XXX8J71q>zSYA0)_arn4IC~D(wzv|@6+i`VOP*^IHQ-!)5yCg z<57s;pE26Ge==?Diz8?Om|i(K&wOvs#>w=W6#XT82EE$pglOhUnwf44cU;Y(H)GyH z{wvZyla2R0=~A6{(sB1|PB)h81o-t{v#w76Z*{HKA9t(&N~ayiU4x?a?%L4UiRfD9 z*Xd3nU4FRxciYpQyXd>iE4Zj|$Bphd@DhKg%pF&}&gX-LqiGy=$Bn=7`Brya(LUO7 z=p7mUaq$VsUT^5&I!~RaGa{qH6NqZ;Qx)T{i6}cG&}Vt?;)wFt7H>&Jc1h2s9>q~@ zQ5_MDp6u#_asr;5(x|}T*x+Hqi{ok)8rpS6vNo4KeEBnV5 zC&XqKq|UEDr1YSaq}rY>@m1$FCN@Pa_Y80DkyCScqc<_&tvN2G)RQ_Rsd{=KqSh1k z)JKG(Xt76UmP7}8up1R0JZ@|+uur;r~nxe-w46KeVj;iz2_bnSz=BbNnI;=Rd zZ9s0*L7fq$-p;7T*oO0)BAcS}GV44GOAhYvWYSN+E3M1-0O}+D*A{rwKql&%Jb;pz*Q{1z0Ky^$}kIJapUd>UZ5xM1_z?AZcR!>_> zwYNPYcx;;|9JzdagQv3B!kV51eOC6U8QlN?-M*E&^y#4vAJhwbkPwN-kKiO zo*KF~YP zZ$Y2(qf=sizU27Q9?jlx?~tbx7>0jEcjPj@u zO)cq>IBsFcGj5!}EHW=TJ=I^|bN1|y!^y4YLpDf86#sPUvG(z+)lC$`bt$!W>s@_fFe`MxAyXXMI=)N&79n4WyT zJYSK=n>wR4#-CZ^DUF$35fMXIY_CFZHQifk=pxgK532W;(As)um3o>zWs$YsVsC0j zLPJE0r__^E1`oS&MuG9ou2IyySL!4sVsliA>*(ZZO8dHK~5u??P*i0X(6y6&O_ z-sHTPQqQ=!=;)OC2yb-sxU!hkS#6#`L_Y0L{@REJx;AqgX_TLxnih&mr&F6}eolUN zW`;Mq+*3(gm|qp?Z;Oau9@!X?+7!`FS3)*jZfdqSc2;t9^sLnA=sa(0bY7h&CwkVb z`PIGBXEzG8+U%1K?gusI^R*4ygwdZS}A+CA+aXT&M=hcu7l)QqLK;L+n*3S4xO zcsx9NoOlYHJwbdiJUm^z1omc%?}L-B5kCXhEfa5sI|{_#!!-|x|A6Dxi2Ix>>znwR zcmTY-S)2?P|0Yg{Q#~Gc`)0v8bYOel-fLjrXz?9zQ@;3VIORU^D{%C~;!okaC&e9b zMo@fIs;qCuf5gYbX&c4UVc#Zk0H((kcl}nuGkz4m0LRhWuXz36f|vaw-UbIe5$^Oh z*cTy=8ZYbPJx4qgPCi#W1D+u*XF#gVih^8Q=gCO#Za|4Td!u8og$ua8M^azF8OIIh2VKAbdA zybO*RBz_nUpCx`4UUrUnBb+*2{1IFSe+ReH=SlGGw-2tGB|dbbY|pZb#G~PKzjz{C zG*^59+&WLZ01jO)z5!l1Uwki|f2Ftt_AU~a!?n5MYB-Q5-U`pYLHwQiCUHBQd%L)I znru(uonjx{x?DU4j=x_#8LoRkJR9~GiSyu<@N&50A(_4!4y_b#fHPN#KY(Y@oALPi z{}%Q=A>Icsds-YbN!Gs-9tlU&`{uaMfYUdKbKs_p;-zreCh@&+{yX9_*w-L_6Rw3n zg;zGp^gVFycjDjS=w|Vur^)(u{3;#-&;Lz)JY4ga_!Ky;Q+x)TL!WKpq3xRu`y;%r zm%~-P#jD}MvEtX^`SkJ&_uJs~RPm46!=A~qzFzvxo990gu7{6vY-PmeaP9%|2XJ#_4|jk07VhXF-UnCo6vs`G?d$9%PKIN9i!{1RNeBPJ>(EGvUgyGW|k$)->@|aBik}8JuyE_;I*kj(8&+mm{u+Yp)PD!$k|l zJu_r|^K!+*;Nok<)8Oc9#TUYr@U`kkWcuB3_)+m=aNR0#6`cCAxE{{06o=u~SH=6_ z!VksK(`0>@eIz~Ll%Ws3L?xD9?(r%#jV&%)7Xh~I`YGsRos`g6oP;f3dk z|AfZqVrg@gBJK4l(~;#y`Je?h^Nh)8JF!WpFl3|M^6I-ooB;@trziT; z1#bIMrhl*gS^Nh){8w>wmaKnsySP6b`dgd=mmd&kz-3O9dwre@&x#SxhGSyI3*iL# z7C7xlnSM9iK2ZEH>>VUt1BZ_hSHelD;?LpA#p0c?bG^6|PACxfJX^Lm2Oa{id|0NB zf$P?a&xUJX5YL7)UlHE|cWf3H!$~#b=ipTMHF!4s4!jI*fXm^Xa7v?G-d;HI8}R{n zVT<^XY}vk~KGE*&lLV(8DxL_}jTK)5&pt_fFYJfkhD+dxbL8^d;PLS63DPfxtKqde z|LHQl7EU-*yd93C&kf|~!@J<7bH#ta^)tm$=gRWqt`v`fXTul5`S5LU-BmLGT6osg z;!ojf*qJVuA6O*QN5IW+DqNc@(=Ue?UL$@2E{ETNL)Xgmui)e*;(c%?JYa?_e+E1S zo(*3K=fL;CmGE=$@;tfxdbkMQ12@43pC`*}fk(nA*GYc{9DTj`YIp|xFzkmb;c|F8 z9KTfh{cs{YaHcH32%Z3Y^JV@^;brhcaMcYm{YAJI{tPa;QKs+J`EL>*dcG{LeVLg4 zD}ej+r1WNSCOrEVaW0$#Kc=StHpTnvMtCK>1CG8;rvC-U!KcoWja7t((!;_cZ9hv7fr@|R_LuM1^)v9E}a zhU4KA;aqqcocy}X?}z8VBVG#UY!=@Q*TBVa6Z|}!QX})f2`~RZ+z97=DBca{!w2AH z@KG1Z_7uR!!7Jg@*)_CrGW|6;*d(rn17C@^!@jS@KWY#E4*S26>E4TFeVwqlFPsJs zg#++JI0VmP_oVIpUiwSmAbdF-h8Mv;`s`TV-s`o8Z_ysU3r_n%=6?VVz>mTq_$k=g zDf6$@9$v3KT%|qyw)VTE{}A@W+u$(V1p8WK{vY4~yhrDU|Af=%|J(8QJD14y7leIq z7(N#E{Ur0B2K(VNwTEYD4_~bPZs{-A9$u_HoUc86oA#~J->W_Ru=a3?_V60*f0q75 z?cojD!y)bA547JS{ioW)Uuq9GYY+dd{V&r0p*?&+dpP=1xjn-0A+WDa`hKt<9s&p9 zB<6?&DFF&JVw>_Q>=P;j{?xHti$DO|at?|Df~3 zd*A^4C;MQ>2~O$lEeJn+zfSzEAqEwJ#JOe7VdYhR4Aj@Fj5C{WAZ(a2Z?Ck`{4m_5Ke)^@D%mq($9o_Pl)M1$;PYo$K{_QTWQAbhL#aIyC5q<>L+xK?|3r}l7Ec(3+w^wqfj;6bpjLi(|=A3h5X!a3T*H);Qp^eeQ7pVb}?X%BDL{$=U+ zXb<;TB0_=N5`ZHiZyg++6pgsJQ_Lb7Vsy$q%J^a1)@ZpPb`@bsvnXn(e z3=YCKz+t#Z``4sjr#<|p_V8!g!>!uWClT@EV`Q$Zk00&_2jMYr7|wuwRnlLoJ$$|P z@cr7urP{wP{U+_5qf`a3&mt7s6rqF74?vMR@z4(H`EYJ-khO z_$TeFrKkTZ>i#_O!-;SZJ{1nb=fb`>q`y~tc(wL$t@iLY+P^8icL|mc_lJXU8XSht zhkb8Jzes!dPVM36w1>B9PamDa*WXXt!=5}@pCB9$hvDO3-`mp9g#B*3GfFuWUXgZt#m`Z%?+z60P`_ypJoPlJ=;OW`#55jY!u5%$A1a4!5c z9DqCFBDntzvVK8$0$c%K42R$)a6No49EMlJZEzLrd?4HZ2^$N5 z_-5D#uY{A~=ixN?9XK1_0sG-LI2VpuChHr32f{`0iEt3kge%~=a0tE;u7@9j!*Chg z2EPS6AIbKA4#&bj!#>!1v#f72+#gPZkB772X|Nym!@2OSZ~%T3E`lrJAp8+r0e=UF z;C*mCJn|M<-!MD}ZiDk-r%ty2K{ys(3;WJ?IvV1>$6r2ku!vT0I zTm)YN2jT1B3b+ss!Oy_;@J2WcZ-v|7J+QMywm&)`>l+In1N-39;bizSI1RoX&W1~1 zKm0nJ3)jN|_(!-1c5ajP3BpIh74Ru=2tEg{hv&m#I0(1F8(`;S+5WHLSon9?2lu;O z)+ZSr1E;|=;cWP7*bf)Lx$qNk01m-L@GdwAJ9o(PE8rvH5Ih>LhfjyY@Itr^eiU{- zk?ns0j)iMrAN(bp4F3wJ!SMyMe%bJGupgcR=fW4m0XP5`!B4|M_%*l!{ss=gop3#T z@}06iVK^IZgRg*{dfEP4;8^$(*aw%x$?#@44gLbohFf7jeAr#IJ_G3GM)*kBzg3n$ z1a>|Zr>L*(<31mr2B$3%UkC@_tKblPllFNs|6SU{PpPk$>94>6xCRd0Cev$ShmIBL zaU7?SJaC{xA08!cC+E=j^)&46?w&xtoXX4Jz2GvQhc9EaJCgJF)AU5cd^V8=KMj0h zXVJ*dpp!c*d$gQigI>+kxgW#!uhu@n>P>TB zhyFA4YVDJ)-Zb}}=zl=3*4}INrnz^HlWl89ul}F*Ui9AOa{H;Z4@Joeu(N1nnwPKt zf2<#Rwf1$^`kUrn|G!xxdiDRb*Z-e33cXtUWqgKC{Ds%wG%vsLWSKS=y_)Ce{V#(C zK51{7d*3NC?QHaF?JFej>P>TBk3I*zTKhnxTmYNb-!%97|M8ZhS8LzcqkC_fd;R}= z51?0TKdxu@-Zc0AGi3Wq(W|xhOWw8prnwKH--KSReLA0^6MxB~k!kMr{~LaSUd{9K z?Kk`w8qrC6)7_f$)b^I?)Cpu9*xWn^ai(ajL z0-vE1f8q5v&HXaE-gw$W=+*o^UjJ+w_{8SkH1~DrU(oG`wNJ3_Kc=~Fxlb;;2EAJQ zCTsgmbFcqj^=tHM?diXpxhFQSziIAguaM>MMX%Pr&FW2apZTcteeRY0N3A{mch+vp zH|?(fD(Q!!uYt91l)UTuH_d%XvGh~XtF7_uKmX}_pRs$ zqE~AlAJcvLrn&dh6AGR;6}?(}^ZL%BZ<^+QKKgUftF?EmpMR#^{!>~0HQK}4o6Gxe z%df-y%h9W~4_eosX#|I@w_{Y>;~?Te#j zh1tCQrg{A{!?OG<(W|vj^>*(~bFZIYa1(m9_Dy_-PW*+JZ<_m@oignK^lF};um5@) z_@upQ?o)Qjv?tN4wJ+f_MsJ$?`WES{(5tnlm#e$=rn%S8YuJHat^LZr-FwsA>*qWC zj9#sM{6XD&)7Ij$ z`3)1%tF@o+*8ldVx!2FHxCFgg`+95tHO+n7SXq89dbRe6*3Vzl+{aCj{x0-t?NhAv zH_d$p`bW{LwNJ8o)7U# z)%-o)f2u~%h)!(oO>?iGH`0b)t^KS+-Mp(e&Aom;Nz4PX|EjfbvbNte_puMk`uotU zwO=la?^?cT?u*f%gkG)vaH}`XeF*&&^lI&!t>@pSxi4KI>whVFwe~sI@=bHEpXYKN zdbRfS=UMl}&Z3cN?)CFuR-#vHKg(Kw)7&QpW&JmzS8LDv5TDq*eAC?P=goYIUah@j zE#I`;_jg`@)7EI_Z;eumYX<~|Rf-}5MXwf5%YRTh2IH1|RD<>=MgS0CH` z`ZvwJeqPX<=+)Y1^BFqv7hb+;?)CG9K0~kO`FURpS&tv4x!2Dl+KpbVz4>|pFW)rx z`uRos(W|vDiKUgG6PtU}+|vOA-D!su$^NI-KGUuL?M-u^gWiW;t$hNYp%Z`M<(uYS zKW}LadNt3_*I$qZK4~9xCFWi~pD7EyT6_Aq5cj0LY3>Vp%J$Dguh!n&US5CG+}ESO z4!v4?^ZvrUY3^g9WchcZS8HEpY%lkwxvxb3IC{1AUOq!7{=&U!?yI6@`RmcEd49hB z=hMI^?M-v<>m_|1dbRe$CGYA@bDx2}6}?*fLaR5;eFOTx(W|vLZ?7!+rfGNmd&~Ow zen|FzAN)V<7h?XS(5tl{XRW_!UVbV1Wb|t7%dN)`)7)ntB zt?S=3_YLTuN3YgCz-Q>hUwHYZxu0>YEPo?=+)XMThCuibDuX#mcJ9dTKib*_A||W0bL-^yC1z;`xNW(*EIK4 zDbn{{A@@JE_Ssf%n)~RJr5}J^t$mKQ{ieBJc&hXx(W|v@wU%$1`&ntyk3+B4KHj*! zvgn(pxz9y^I(oJC$yRTg`yl%H=+*zzz8?L3=+)YLt?SP;FTde5+5TemYVFhe$Of=^ z`%QBnpCx?-dbReg*78krub;>GCVI8@NqmM*{DqfqntT2H#;?$;d49hC`S}c;_zU-@ zxi6Y7>%Skpn&;;}&su-e+w7rk2hIzB@u{=&;Q&3*bjS^sO$ zt9gFD{tK+1f2O&wy;Ax+b^BrMldacpOmp9Hjr6O~tF=#zp_QN$o7dko_xkyuub@|J zAGBUSH_g3%p6I*i)!KJhk6)&_*UukqM6cG~Tpq8#Y3^qf$@c$%Uaft;yY>HWziIC4 z(EpBJt$n%Go8~_MAz6OUm2&@4Yi~Z@^7@SALMX%PLK2FU&v3dPXbFZHldkK2A_9@o&XPWz!_`KPx(5tmyZf(D5?h}Ht z{sHuA?H%j>W7_T4N&kfQu=c~P?KjQ+jB@FpMX%Pr*y>Gl&j%~#c{ia~Yfm2!>YmtH zG&0S7{j1V{j9#t1`FPL0Y3|djr2h)NTKh&mLnr>iy=m?f@cF=P=+!(wKmIw^?PuEU zt7Z8;ACdb{0<3+tb^Dp-UO$gG9=%%o25b4Ix!2DxJ_fy7`&QltIZyyNlc z)!G-k^}oGo?vra}`!mq1wWmW2x+gX--!%8L>!iN`y;}QrYx$aJoIYq&7XI? zeAC<~d?Ni4^lI(R*GsuK&3)ch=?l@TwGUY9Z<_md^rh(4+Rr{%E`ZI;H_d(ax3c`# zb^BrM7h2ChOmnZFZ(W04t$nL?|256MejaucdbRd#*8Xdnd;R?EU(l$Z$*CudNt3_eH_Ph(%v-p&Tq2(W6`U%4{=N< z?L)4_+=tMgj$W;Of%W{wH23;>v5Mz6O0MdbReA*8XRj`;xx0{8!PdwNJ9HKhxan z=euu0uhxD(pP>_f$)b^I?)CHFKSQtP`Fa0qrh!k|o913WKYkZ_wf6DW{ns@2`g!x6 z=+)YX-KGD#eAC?P=hMg1`?mQ0r`A4Wv|Io?i$?iGm!E}R&GYm1Uql0+v^UMYe!l*t=+)Z$to1j|y?!45_2|{wn?J93 z{Y`VPpWlB!dbRd7*8Xprd;PrswdmE_*Lu5eziICE_yF&qS8JbXZNF*m^>_l?(5tnd zZLPm)?)CTsKciP`-yo~sb^Dp-UXNF>AH7=pay~;R{*px_)7M=Jhwty&i8N4ZT|XDr@ASrn%SSIm|<^*4})+&FgR4?Q#5v>$QiqkF%bCo914R7qJ|@TKfcR`%QDN$Cr2t zy;^&})tlyCk4I67Uah_Pd^wB0X_|XIe#N`!)!J9`89MP7?oD&A$Gg~$Ud{9K{=1UT z(22irZ<>2OK1LgQHP6p|3JrYH-Zb}mJdNnbDTbFatuI1{~E`)EExC;pN}Bh%dL@jxy{ujcvr`m3{+Z<>2Oe#mv` z)!NUt?tiAa*W-=ck6x|)I9dljv3dPXbFarI38GhPKg0U{#WeSNJd?N3tF=$Het$8| zy&nIh3B6kTV(b2AntMH7N(Xwi_I2*o|GWLBx!2>X^m$zNU$yr1@#5}@okb(l-0SgJ z2BKGMUv^0M-Zb}m{FYSo>aJe$uG_E7F!y@AmvhmpwXd_TKhxan@nJ4UuhzcFdjGa* z?)7*wx1(2UU(9Fd#9y*#WSV(k-)jU7nf7-0Sf12iAk5{u6y;^(o_eEa5Y3}v- zHXG2ZwV!SM{4veF9uMa|^lI&w@fkYt7hb+;?)CUNAEQ_E{Ji~%*5ik1?)7*(U!zxR z-)Y_crn&dy_&jas)!NUp?!TtF*W>vlJR$cVwe}4oX(Q;w&Z3cN?)CUTC!$wt-^6F= z#9z2K&AlEk=q&VVo}aJ3CTsgmbFar2x&pmg`#ib~KCyZErn#?5mE*GSYoBfPrn%SSHN}+3{-@Typ3l&Uzhu$KH1~RZr(@8od4Bi)XT5%& zXqbCF9#krNwf3`(`FZ)Kx!2=Ior_+ry^q#|Pi*de3_7v7*W*oHg2OzSeg1YVA|4<(uYSkH_^RdbRe2*78krugCBD6TMn{^Y67;^i9*;>+!yN zJt_OYTKf#^{l})c*W-irL$B6;rM3O0x!2=~oq%4geY0HsUDv;9?)CU%Q_-unZ|5^~ z;xAb=GR?gnuk1qfYMwulh6L;J-!%7ne6zXe)!K&-qYI%Eo0o5zdp#c7LiB3wo2}O$ zO>?iuPrC`dT6@|p+!LFZZ<>2O-r5TEYVGTe?B1K^UXRcABzm>>%OvmGe$(9R@!VcP zuhu^O(C*7O&AlG~?M?J*?fEqsKJk|<8ky!^j~DkLdNt3_w|~3!^UE~%dVIMC^lI(P zt^1E@?)7+d-=J4(pKR@arn%SS*R`NmYfpzCbWiLo8ky!^k9XIBUaftj_595=_j-K1 zn5X2rRcoJN-TzHJ!Khxan@%qk1uhu@#y8cXa zugCYh7`~A2=+)ZC zS<5%gy&j*i2EAJQ6l?oUbFarUY(lTrzQtO;Y3}v-hkv10YoBdBe=yCx9xw5*r)B?D zYj19E7Jbt+_j-KA5$M(0XISfRntMGS<2dwc?PK^1o%jnc-!%7n{Kj$*W*22fL^V=`FxX?Z<>2OKIB65YVGr7@m$I2RO|k4ntMH7=xFq6?dk1x?ung6Bh%dL@kO)HtF^DR zw%;`OdOXq`^lI(Rzqj%7O>?iuFU?1<*4})-75ApO*W;Z&j$W<3`Fx3c)7b?m@5C-Wehnz~<$fc6%JJwdZQtwn|w0BtAnY z{=&U!?)CVt{Z~t`=K1;Yr?iu%U!!#dNt3_{qlh{q7$2Y)7c|n|ssT>+yR(TP?ks=jVPQ4SZsA zZ<>2O-fzol>D4?x_vzN-muc?x_`rXnS8H#+-oVQ@&AlE^xYvJV`D*P~T93b`x!2+y}pp;z<#eEk*h89MP7?oD&A$3vcs zUd{7!pG*Uvv^UMY9zXdk^lI&W9MegApDQuR|?Y?}|-0ShH-$k$1KGu5u$TatQyz4K}tF=##=)Qc@ z-0ShNe?hO--h97F7Jbt+_j){S?=y1$S8HD_i|@MrO>?iu-#!w(TKjZs{Y|^e$ML#H z=<;Fh&BtqAf79IS@x8~RS8H!Bk9*VH>+!(PL$B7}e7xk|H1~S^@OkLf+LuIjzx_;e zug4p|5xrV_b9;IDrn%SSli!D4t$o0{{Y-PO$1^WQuhzca>P>U6$3NeQUakGgNVxzu zufJ*T^?2zG=+)YHSiNcP_4w+$(W|x3iR-?6)7#XQEeY-$Iwc zCpItNH1~S^`HRr2wKunyd(-an19E)y`MP{q`&{exH_g2s-~M*=YVCd2^%pm%anNn&;<5=f~2BPTHI1ep$8jN3D_lU#)$awf~vsKJgvtN1<11-_f`G z@=bG}KyT>bdCx(w)_#WMUF&a}`&{(bp;v34Y4xVLuSQ>tUaft!wf~vszUEU|{|)HX z+9&cEI`Nk*8ky$4Wt;RLqgV6%eEU^f%Qww^>F3h#La)}|N0-4THZR}Dpc9+>8ua^g z{b8QY%Wt58Pi*c@b6>n&mfvTsY@1qp?0#LnY3|F=4?(ZizSVmCFwOnSFJ<|u=+)Y% zyKDdN`kUsy5&hZd)!N5fub-LbzU@a@{v7mb?H#YI06U9EhTZ;W>6dB`Yd_AIpL^5X z5C2vAd(o@4H}@CrO>^JeE`14lwf0RtyRW}#?n8e|zaG6>d-L|<<(uZd{DAbe=+)YH z%Hq5BAJg2IIZ^JkCiH6UE3D`5rn#RLBmGYFYVFO}8+rXra~~5ceH(hU_U8W1y=m?f z&^zno{-f63{JcKyO>>`iq%8kX^lI%Zt-rsT=DvNP^!?GRwKvz7mv5ST?;z=ip;v2f z?l0V%=01Fk^e3ZNYoB>U_x;Z__erVJXP{SWzx;&my=m?%7fXL7dbReMJ-hd&xp%IY z{zmj_?GvoW57XQy6i8o$UaftGEWT_1H_d$x`p40$wNJL*KWv)&l@H7EH=tK*KhApp zg=y~V)=K{_dbReQ*7awa``Q6BaLq)8OTB0A3A;;Eim$FZjQgJ_HBh zS~v{X!@g#jzfpTQtUcTUr~M%Fx4{9p0}jE?bFx0pPMJR%PJ<7D18_e$1P{@Em-I=n zA3hNd!xLa%i_AY24#3$uKYRh4_M^;y861Rf*ZJZ5VBb$Ne<|#TU(gB$f3%0Ao|oIh_p|h|upd4O4#I=sFg#NGJ<_LW50BR# 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 BcH#g4 literal 0 HcmV?d00001 diff --git a/run_tree/osx/intel/debug/lumenarium b/run_tree/osx/intel/debug/lumenarium new file mode 100755 index 0000000000000000000000000000000000000000..b6ea3bdef823360a92c6effc5ae3c28b7539ad89 GIT binary patch literal 186055 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 literal 0 HcmV?d00001 diff --git a/run_tree/text.txt b/run_tree/text.txt index ccfbc2907e133e5420b8c0917ace2b28be40e2bd..fc361877f3e89159f1110c72438a64a25f5e05fd 100644 GIT binary patch delta 18 OcmY$@oFK!?fD8Z-zX1pU delta 14 RcmdN?ogl-Rm{ +// #include +// #include + +#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 + +#if defined(DEBUG) +# define USE_ASSERTS 1 +#endif + +#include "lumenarium_core_assert.h" + +#define STB_TRUETYPE_IMPLEMENTATION +#define STB_SPRINTF_IMPLEMENTATION +#define STBTT_assert(x) assert(x) +#include "../libs/stb_truetype.h" +#include "../libs/stb_sprintf.h" + +#define HANDMADE_MATH_IMPLEMENTATION +#define HANDMADE_MATH_STATIC +#include "../libs/HandmadeMath.h" +typedef hmm_v2 v2; +typedef hmm_v3 v3; +typedef hmm_v4 v4; +typedef hmm_mat4 m44; + +#include "lumenarium_core_types.h" +#include "lumenarium_core_memory.h" +#include "lumenarium_core_string.h" +#include "lumenarium_core_hash.h" +#include "lumenarium_core_random.h" + +#include "lumenarium_core_file.h" +#include "lumenarium_core_window.h" +#include "lumenarium_core_time.h" +#include "lumenarium_core_threads.h" +#include "lumenarium_core_socket.h" \ No newline at end of file diff --git a/src_v2/core/lumenarium_core_assert.h b/src_v2/core/lumenarium_core_assert.h new file mode 100644 index 0000000..ca7896d --- /dev/null +++ b/src_v2/core/lumenarium_core_assert.h @@ -0,0 +1,60 @@ +/* date = March 26th 2022 3:42 pm */ + +#ifndef LUMENARIUM_CORE_ASSERT_H +#define LUMENARIUM_CORE_ASSERT_H + +#if defined(PRINT_ASSERTS) +# include +# define err_write(s,...) err_write_(s,__VA_ARGS__) +static FILE* file_err; +void err_write_(char* fmt, ...) { + if (!file_err) return; + va_list args; + va_start(args, fmt); + vfprintf(file_err, fmt, args); + va_end(args); +} +void open_err_file() { file_err = fopen("./err.txt", "wb"); } +void close_err_file() { fclose(file_err); } +#else +# define err_write(s,...) +void open_err_file() {} +void close_err_file() {} +#endif + +#if !defined(PLATFORM_wasm) + +// 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 int*)0) = 0xFFFF) + +#else +WASM_EXTERN void wasm_assert_always(char* file, unsigned int file_len, unsigned int line); +# define assert_always wasm_assert_always(__FILE__, sizeof(__FILE__), __LINE__) +#endif // defined(PLATFORM_WASM) + +#ifdef USE_ASSERTS +# define assert(c) do { \ +if (!(c)) { \ +err_write("Assert Hit: %s:%u\n", __FILE__, (u32)__LINE__); \ +close_err_file(); \ +assert_always; \ +} \ +} while(false) + +// 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(0); + +// 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(0); } break; + +#else +# define assert(c) +# define invalid_code_path +# define invalid_default_case default: { } break; +#endif + +#endif //LUMENARIUM_CORE_ASSERT_H diff --git a/src_v2/core/lumenarium_core_file.h b/src_v2/core/lumenarium_core_file.h new file mode 100644 index 0000000..6113182 --- /dev/null +++ b/src_v2/core/lumenarium_core_file.h @@ -0,0 +1,252 @@ +#if !defined(LUMENARIUM_CORE_FILE_H) +#define LUMENARIUM_CORE_FILE_H + +typedef struct File_Handle File_Handle; +struct File_Handle +{ + u64 value; +}; + +typedef u32 File_Access_Flags; +enum +{ + FileAccess_None = 0, + FileAccess_Read = 1, + FileAccess_Write = 2, +}; + +typedef u32 File_Create_Flags; +enum +{ + // these match https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + FileCreate_None = 0, + + // Creates a new file, only if it does not already exist. + // If the file exists, an error is returned + FileCreate_New = 1, + + // Creates a new file, always. + // If the specified file exists and is writable, the file is overwritten + FileCreate_CreateAlways = 2, + + // Opens a file or device, only if it exists. + FileCreate_OpenExisting = 3, + + // Opens a file, always. + FileCreate_OpenAlways = 4, +}; + +typedef u32 File_Flags; +enum +{ + FileFlag_IsFile = 0, + FileFlag_IsDir = 1, +}; + +typedef struct File_Info File_Info; +struct File_Info +{ + String path; + String path_abs; + u64 size; + u64 time_created; + u64 time_last_write; + File_Flags flags; +}; + +typedef struct File_Info_List_Ele File_Info_List_Ele; +struct File_Info_List_Ele +{ + File_Info info; + File_Info_List_Ele* next; +}; + +typedef struct File_Info_List File_Info_List; +struct File_Info_List +{ + File_Info_List_Ele* first; + File_Info_List_Ele* last; +}; + +// For Cross Platform File Operations use these: + +typedef u32 File_Async_Job_Flags; +enum +{ + FileAsyncJob_Invalid = 0, + FileAsyncJob_Read = 1 << 0, + FileAsyncJob_Write = 1 << 1, + FileAsyncJob_InFlight = 1 << 2, + FileAsyncJob_Success = 1 << 3, + FileAsyncJob_Failed = 1 << 4, +}; + +typedef struct File_Async_Job_Args File_Async_Job_Args; +struct File_Async_Job_Args +{ + String path; + Data data; + File_Async_Job_Flags flags; + u32 error; +}; + +typedef void File_Async_Cb(File_Async_Job_Args args, u8* user_data); + +typedef struct File_Async_Job File_Async_Job; +struct File_Async_Job +{ + Data job_memory; + File_Async_Job_Args args; + File_Async_Cb* cb; +}; + +typedef void File_Async_Job_System_Do_Job(File_Async_Job* job); + +typedef struct File_Async_Job_System File_Async_Job_System; +struct File_Async_Job_System +{ + File_Async_Job_System_Do_Job* do_job; +}; + +global Allocator* file_jobs_arena = 0; +#define FILE_ASYNC_MAX_JOBS 32 +global File_Async_Job file_async_jobs[FILE_ASYNC_MAX_JOBS]; +global u32 file_async_jobs_len = 0; + +internal File_Async_Job_System file_async_jobs_init(File_Async_Job_System_Do_Job* do_job); +internal bool file_async_job_add(File_Async_Job job); +internal File_Async_Job file_async_job_rem(u64 index); +internal bool file_async_read(String path, File_Async_Cb* cb); +internal bool file_async_write(String path, Data data, File_Async_Cb* cb); +internal void file_async_job_complete(File_Async_Job* job, u8* user_data); +internal void file_async_jobs_do_work(File_Async_Job_System* system, u64 max_jobs, u8* user_data); +internal void file_async_jobs_do_work(File_Async_Job_System* system, u64 max_jobs, u8* user_data); + +typedef u32 Platform_Enum_Dir_Flags; +enum +{ + EnumDir_Recursive = 1, + EnumDir_IncludeDirectories = 2, +}; + +/////////////////////////////////////// +/////////////////////////////////////// +// IMPLEMENTATION +/////////////////////////////////////// +/////////////////////////////////////// + +internal File_Async_Job_System +file_async_jobs_init(File_Async_Job_System_Do_Job* do_job) +{ + file_jobs_arena = paged_allocator_create_reserve(MB(4), 256); + File_Async_Job_System result = {}; + result.do_job = do_job; + return result; +} + +internal bool +file_async_job_add(File_Async_Job job) +{ + if (file_async_jobs_len >= FILE_ASYNC_MAX_JOBS) return false; + + // Copy data to job local memory + u64 size_needed = job.args.path.len + job.args.data.size + 1; + u8* job_mem = allocator_alloc(file_jobs_arena, size_needed); + String job_path = string_create(job_mem, 0, job.args.path.len + 1); + u64 copied = string_copy_to(&job_path, job.args.path); + Data job_data = data_create(job_mem + job_path.cap + 1, size_needed - (job_path.cap + 1)); + memory_copy(job.args.data.base, job_data.base, job.args.data.size); + job.args.path = job_path; + job.args.data = job_data; + job.job_memory = data_create(job_mem, size_needed); + + file_async_jobs[file_async_jobs_len++] = job; + return true; +} + +internal File_Async_Job +file_async_job_rem(u64 index) +{ + assert(index < file_async_jobs_len); + File_Async_Job result = file_async_jobs[index]; + + file_async_jobs_len -= 1; + if (file_async_jobs_len > 0) + { + u32 last_job = file_async_jobs_len; + file_async_jobs[index] = file_async_jobs[last_job]; + } + + return result; +} + +internal bool +file_async_read(String path, File_Async_Cb* cb) +{ + File_Async_Job job = {}; + job.args.path = path; + job.args.flags = ( + FileAsyncJob_Read | + FileAsyncJob_InFlight + ); + job.cb = cb; + bool result = file_async_job_add(job); + return result; +} + +internal bool +file_async_write(String path, Data data, File_Async_Cb* cb) +{ + File_Async_Job job = {}; + job.args.path = path; + job.args.data = data; + job.args.flags = ( + FileAsyncJob_Write | + FileAsyncJob_InFlight + ); + job.cb = cb; + bool result = file_async_job_add(job); + return result; +} + +internal void +file_async_job_complete(File_Async_Job* job, u8* user_data) +{ + job->cb(job->args, user_data); + allocator_free(file_jobs_arena, job->job_memory.base, job->job_memory.size); + if (has_flag(job->args.flags, FileAsyncJob_Write)) + { + allocator_free(file_jobs_arena, job->args.data.base, job->args.data.size); + } +} + +internal void +file_async_jobs_do_work(File_Async_Job_System* system, u64 max_jobs, u8* user_data) +{ + assert(system->do_job); + + u64 to_do = max_jobs; + if (max_jobs > file_async_jobs_len) to_do = file_async_jobs_len; + + File_Async_Job_Flags completed = ( + FileAsyncJob_Success | + FileAsyncJob_Failed + ); + + for (u64 i = to_do - 1; i < to_do; i--) + { + File_Async_Job* job = file_async_jobs + i; + system->do_job(job); + if (has_flag(job->args.flags, completed)) + { + if (has_flag_exact(job->args.flags, FileAsyncJob_Success)) + { + file_async_job_complete(job, user_data); + } + file_async_job_rem(i); + } + } +} + + +#endif // LUMENARIUM_CORE_FILE_H \ No newline at end of file diff --git a/src_v2/lumenarium_hash.cpp b/src_v2/core/lumenarium_core_hash.h similarity index 50% rename from src_v2/lumenarium_hash.cpp rename to src_v2/core/lumenarium_core_hash.h index d219110..dba3a11 100644 --- a/src_v2/lumenarium_hash.cpp +++ b/src_v2/core/lumenarium_core_hash.h @@ -1,14 +1,14 @@ /* date = April 1st 2022 7:29 pm */ -#ifndef LUMENARIUM_HASH_CPP -#define LUMENARIUM_HASH_CPP +#ifndef LUMENARIUM_CORE_HASH_H +#define LUMENARIUM_CORE_HASH_H // // DJB2 // Source: http://www.cse.yorku.ca/~oz/hash.html internal u32 -hash_djb2_to_u32(char* str, u64 len) +hash_djb2_str_to_u32(char* str, u64 len) { u32 result = 5381; for (u64 i = 0; i < len; i++) @@ -19,20 +19,20 @@ hash_djb2_to_u32(char* str, u64 len) } internal u32 -hash_djb2_to_u32(char* str) +hash_djb2_cstr_to_u32(char* str) { u64 len = c_str_len(str); - return hash_djb2_to_u32(str, len); + return hash_djb2_str_to_u32(str, len); } internal u32 -hash_djb2_to_u32(String str) +hash_djb2_string_to_u32(String str) { - return hash_djb2_to_u32((char*)str.str, str.len); + return hash_djb2_str_to_u32((char*)str.str, str.len); } internal u64 -hash_djb2_to_u64(char* str, u64 len) +hash_djb2_str_to_u64(char* str, u64 len) { u64 result = 5381; for (u64 i = 0; i < len; i++) @@ -43,16 +43,16 @@ hash_djb2_to_u64(char* str, u64 len) } internal u64 -hash_djb2_to_u64(char* str) +hash_djb2_cstr_to_u64(char* str) { u64 len = c_str_len(str); - return hash_djb2_to_u64(str, len); + return hash_djb2_str_to_u64(str, len); } internal u64 -hash_djb2_to_u64(String str) +hash_djb2_string_to_u64(String str) { - return hash_djb2_to_u64((char*)str.str, str.len); + return hash_djb2_str_to_u64((char*)str.str, str.len); } -#endif //LUMENARIUM_HASH_CPP +#endif //LUMENARIUM_HASH_H diff --git a/src_v2/core/lumenarium_core_memory.h b/src_v2/core/lumenarium_core_memory.h new file mode 100644 index 0000000..2f4e7ef --- /dev/null +++ b/src_v2/core/lumenarium_core_memory.h @@ -0,0 +1,802 @@ +#ifndef LUMENARIUM_CORE_MEMORY_H +#define LUMENARIUM_CORE_MEMORY_H + +#if !defined(OS_MEM_PAGE_SIZE) +# error "You must define OS_MEM_PAGE_SIZE for core_memory.h to compile properly" +#endif + +////////////////////////////////////////////// +// Memory Helpers + +#define Bytes(x) (x) +#define KB(x) ((x) << 10) +#define MB(x) ((x) << 20) +#define GB(x) (((u64)x) << 30) +#define TB(x) (((u64)x) << 40) + +#define memory_zero_array(b,t,c) memory_zero((u8*)(b), sizeof(t) * (c)) +internal void memory_zero(u8* base, u64 size); +internal void memory_copy(u8* from, u8* to, u64 size); + +////////////////////////////////////////////// +// Data + +#define get_byte(value, byte_index) ((value >> (8 * byte_index)) & 0xFF) + +typedef struct Data Data; +struct Data +{ + u8* base; + u64 size; +}; + +internal Data +data_create(u8* base, u64 size) +{ + Data result = {}; + result.base = base; + result.size = size; + return result; +} + +////////////////////////////////////////////// +// Data Writer + +typedef struct Data_Writer Data_Writer; +struct Data_Writer +{ + Data data; + u64 at; +}; + +// NOTE(PS): functions ending in _b treat data in the Data_Writer as big endian +// order (network ordering) where functions ending in _l treat data into little +// endian order +// It is always assumed that values not in the Data_Writer (ie the other args +// to the function or the functions return value) are in little endian order + +internal u8 dw_get_u8(Data_Writer* w); +// TODO(PS): get functions + +internal void dw_put_u8(Data_Writer* w, u8 b); +internal void dw_put_u16_b(Data_Writer* w, u16 b); +internal void dw_put_u16_l(Data_Writer* w, u16 b); +internal void dw_put_u32_b(Data_Writer* w, u32 b); +internal void dw_put_u32_l(Data_Writer* w, u32 b); +internal void dw_put_u64_b(Data_Writer* w, u64 b); +internal void dw_put_u64_l(Data_Writer* w, u64 b); + +////////////////////////////////////////////// +// Allocator + +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); +typedef void Allocator_Destroy(Allocator* allocator); + +struct Allocator +{ + Allocator_Alloc* alloc; + Allocator_Free* free; + Allocator_Realloc* realloc; + Allocator_Clear* clear; + Allocator_Destroy* destroy; + + 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) +#define allocator_destroy(a) (a)->destroy(a) + +internal Allocator* paged_allocator_create_reserve(u64 reserve_size, u64 page_size); + +////////////////////////////////////////////// +////////////////////////////////////////////// +// IMPLEMENTATION +////////////////////////////////////////////// +////////////////////////////////////////////// + +///////////////////////////////////////// +// 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)) + +internal void memory_zero(u8* base, u64 size) { memory_zero_(base, size); } +internal void memory_copy(u8* from, u8* to, u64 size) { + memory_copy_(from, to, size); +} + +// @Maintenance - if the function only has one argument, it should become +// round_to_os_page_multiple +u64 +round_size_to_page_multiple(u64 size, u64 page_size) +{ + u64 rem = size % page_size; + if (rem != 0 || size < page_size) + { + u64 grow = page_size - rem; + size += grow; + } + return size; +} + +u64 +round_size_to_os_page_multiple(u64 size) +{ + return round_size_to_page_multiple(size, OS_MEM_PAGE_SIZE); +} + +///////////////////////////////////////// +// Data Writer + +internal void +dw_put_u8(Data_Writer* w, u8 b) +{ + if (w->at < w->data.size) + { + w->data.base[w->at++] = b; + } +} + +internal u8 +dw_get_u8(Data_Writer* w) +{ + u8 result = 0; + if (w->at < w->data.size) + { + result = w->data.base[w->at++]; + } + return result; +} + +internal void +dw_put_u16_b(Data_Writer* w, u16 b) +{ + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 0)); +} + +internal void +dw_put_u16_l(Data_Writer* w, u16 b) +{ + dw_put_u8(w, get_byte(b, 0)); + dw_put_u8(w, get_byte(b, 1)); +} + +internal void +dw_put_u32_b(Data_Writer* w, u32 b) +{ + dw_put_u8(w, get_byte(b, 3)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 0)); +} + +internal void +dw_put_u32_l(Data_Writer* w, u32 b) +{ + dw_put_u8(w, get_byte(b, 0)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 3)); +} + +internal void +dw_put_u64_b(Data_Writer* w, u64 b) +{ + dw_put_u8(w, get_byte(b, 7)); + dw_put_u8(w, get_byte(b, 6)); + dw_put_u8(w, get_byte(b, 5)); + dw_put_u8(w, get_byte(b, 4)); + dw_put_u8(w, get_byte(b, 3)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 0)); +} + +internal void +dw_put_u64_l(Data_Writer* w, u64 b) +{ + dw_put_u8(w, get_byte(b, 0)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 3)); + dw_put_u8(w, get_byte(b, 4)); + dw_put_u8(w, get_byte(b, 5)); + dw_put_u8(w, get_byte(b, 6)); + dw_put_u8(w, get_byte(b, 7)); +} + +//////////////////////////////////////// +// 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 + +internal void +allocator_destroy_(Allocator* allocator, u64 custom_data_size) +{ + zero_struct(*allocator); + u64 size = sizeof(Allocator) + custom_data_size; + os_mem_decommit((u8*)allocator, size); + os_mem_release((u8*)allocator, size); +} + +///////////////////////////////////////// +// Bump Allocator + +typedef struct Allocator_Bump Allocator_Bump; +struct Allocator_Bump +{ + u8* base; + u64 at; + u64 size_committed; + u64 size_reserved; + u64 page_size; + u64 high_water_mark; +}; + +#if defined(DEBUG) +# define bump_allocator_validate(a) bump_allocator_validate_(a) +#else +# define bump_allocator_validate(a) +#endif +internal void +bump_allocator_validate_(Allocator* allocator) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; + assert(bump != 0); + if (bump->size_reserved > 0) + { + assert(bump->base != 0); + } + assert(bump->at <= bump->size_committed); + assert(bump->size_committed <= bump->size_reserved); + assert(bump->page_size > 0); + assert(bump->high_water_mark <= bump->size_reserved); +} + +internal u8* +bump_allocator_alloc_inner(Allocator* allocator, Allocator_Bump* bump, u64 size) +{ + bump_allocator_validate(allocator); + 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) + { + if (bump->page_size == 0) bump->page_size = OS_MEM_PAGE_SIZE; + new_size = bump->page_size; + } + if (new_size < at_after) + { + new_size = round_size_to_page_multiple(at_after, bump->page_size); + } + + 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 = round_size_to_os_page_multiple(bump->at); + if (bump->at == 0 && bump->size_committed == 0) next_page = 0; + u64 commit_amt = new_size - next_page; + u8* new_page = os_mem_commit(bump->base + next_page, commit_amt); + if (new_page != 0) + { + bump->size_committed = new_size; + } + } + else + { + invalid_code_path; // out of reserved memory + } + } + } + + u8* result = bump->base + bump->at; + bump->at = at_after; + bump->high_water_mark = max(bump->at, bump->high_water_mark); + + bump_allocator_validate(allocator); + return result; +} + +internal u8* +bump_allocator_alloc(Allocator* allocator, u64 size) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; + u8* result = bump_allocator_alloc_inner(allocator, bump, size); + 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; + bump_allocator_validate(allocator); +} + +internal void +bump_allocator_destroy_(Allocator_Bump* bump) +{ + os_mem_decommit(bump->base, bump->size_committed); + os_mem_release(bump->base, bump->size_reserved); +} + +internal void +bump_allocator_destroy(Allocator* allocator) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; + bump_allocator_destroy_(bump); + allocator_destroy_(allocator, sizeof(Allocator_Bump)); +} + +internal void +bump_allocator_rewind(Allocator* allocator, u64 to_point) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; + bump_allocator_validate(allocator); +#if defined(DEBUG) + memory_zero(bump->base + to_point, bump->at - to_point); +#endif + bump->at = to_point; + bump_allocator_validate(allocator); +} + +internal Allocator* +bump_allocator_create_(u64 page_size) +{ + u64 size_needed = sizeof(Allocator) + sizeof(Allocator_Bump); + + u8* base = os_mem_reserve(size_needed); + base = os_mem_commit(base, size_needed); + + Allocator* result = (Allocator*)base; + zero_struct(*result); + + Allocator_Bump* bump = (Allocator_Bump*)base + sizeof(Allocator); + zero_struct(*bump); + bump->page_size = page_size; + + result->alloc = bump_allocator_alloc; + result->realloc = bump_allocator_realloc; + result->clear = bump_allocator_clear; + result->destroy = bump_allocator_destroy; + result->allocator_data = (u8*)bump; + + bump_allocator_validate(result); + return result; +} + +internal Allocator* +bump_allocator_create_reserve(u64 reserve_size) +{ + Allocator* result = bump_allocator_create_(OS_MEM_PAGE_SIZE); + Allocator_Bump* bump = (Allocator_Bump*)result->allocator_data; + + u64 reserve_pages = round_size_to_os_page_multiple(reserve_size); + bump->base = os_mem_reserve(reserve_pages); + if (bump->base != 0) bump->size_reserved = reserve_pages; + + bump_allocator_validate(result); + return result; +} + +internal Allocator* +bump_allocator_create_child(Allocator* parent, u64 init_size) +{ + Allocator* result = bump_allocator_create_(OS_MEM_PAGE_SIZE); + result->parent = parent; + + Allocator_Bump* bump = (Allocator_Bump*)result->allocator_data; + zero_struct(*bump); + bump->base = allocator_alloc(result->parent, init_size); + if (bump->base != 0) + { + bump->size_reserved = init_size; + bump->size_committed = init_size; + } + + bump_allocator_validate(result); + return result; +} + + +///////////////////////////////////////// +// Scratch Allocator + +typedef struct Allocator_Scratch Allocator_Scratch; +struct Allocator_Scratch +{ + Allocator* a; + u64 at_before; +}; + +internal Allocator_Scratch +allocator_scratch_begin(Allocator* allocator) +{ + Allocator_Scratch result = {}; + result.a = allocator; + Allocator_Bump* bump = (Allocator_Bump*)result.a->allocator_data; + result.at_before = bump->at; + return result; +} + +internal void +allocator_scratch_end(Allocator_Scratch* scratch) +{ + bump_allocator_rewind(scratch->a, scratch->at_before); + zero_struct(*scratch); +} + +///////////////////////////////////////// +// Paged Allocator + +typedef struct Allocator_Paged_Free_Region Allocator_Paged_Free_Region; +struct Allocator_Paged_Free_Region +{ + u64 pages; + Allocator_Paged_Free_Region* prev; + Allocator_Paged_Free_Region* next; +}; + +typedef struct Allocator_Paged Allocator_Paged; +struct Allocator_Paged +{ + Allocator_Bump bump; + Allocator_Paged_Free_Region* free_first; +}; + +internal u8* +paged_allocator_alloc(Allocator* allocator, u64 size) +{ + // 1. Find the number of pages we need + // 2. Find a run of free pages that we can use + // If found, + // remove those pages from the run they are in + // return those pages of memory + // 3. Commit pages on the end + + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + if (paged->bump.page_size == 0) paged->bump.page_size = OS_MEM_PAGE_SIZE; + + u64 rounded_size = round_size_to_page_multiple(size, paged->bump.page_size); + u64 pages_needed = rounded_size / paged->bump.page_size; + + u8* result = 0; + + // Find free pages + if (paged->free_first) + { + Allocator_Paged_Free_Region* found = 0; + for (Allocator_Paged_Free_Region* at = paged->free_first; at != 0; at = at->next) + { + // NOTE(PS): this set of conditions checks to see if is bigger than what + // we need. If it is, we also check to see if this is smaller than any + // region we've found before. And we abort the search if this region + // perfectly fits the size needed. + // + // This should make sure that we are always choosing the closest fit we + // can. I'm not sure this is the best strategy for dealing with fragmentation + // but its a decent first pass + if (at->pages >= pages_needed) + { + if (!found || (found->pages > at->pages)) + { + found = at; + if (found->pages == pages_needed) break; + } + } + } + + if (found) + { + result = (u8*)found; + if (found->pages > pages_needed) + { + Allocator_Paged_Free_Region* region_after = (Allocator_Paged_Free_Region*)(result + rounded_size); + if (found->prev != 0) found->prev->next = region_after; + region_after = found->next; + } + else + { + if (found->prev != 0) found->prev->next = found->next; + } + } + } + + if (!result) + { + result = bump_allocator_alloc_inner(allocator, &paged->bump, size); + } + + return result; +} + +#define region_end(r,page_size) ((u8*)(r) + ((r)->pages * page_size)) + +internal void +paged_region_insert( + Allocator_Paged_Free_Region* before, + Allocator_Paged_Free_Region* new_region, + Allocator_Paged_Free_Region* after, + u64 page_size + ){ + assert(after == 0 || before < after); + assert(before < new_region); + assert(after == 0 || new_region < after); + assert(new_region->prev == 0 && new_region->next == 0); + + u8* before_end = region_end(before, page_size); + u8* new_region_end = region_end(new_region, page_size); + + // Before + if (before_end == (u8*)new_region) + { + // merge the regions + before->pages += new_region->pages; + new_region = before; + assert(new_region_end == region_end(new_region, page_size)); + } + else + { + assert(before_end < (u8*)new_region); + before->next = new_region; + new_region->prev = before; + } + + // After + if (after != 0) + { + if (new_region_end == (u8*)after) + { + // merge the regions + new_region->pages += after->pages; + u8* a = region_end(after, page_size); + u8* b = region_end(new_region, page_size); + assert(a == b); + } + else + { + assert(new_region_end < (u8*)after); + new_region->next = after; + after->prev = new_region; + } + } +} + +internal void +paged_allocator_free(Allocator* allocator, u8* base, u64 size) +{ + // Figure out which page base is the base of, assert its the base + // figure out how many pages size represents. + // create a free range + // stick it in between contiguous free ranges + // if the ranges before or after meet this new one, merge them all + + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + + u64 page_base_rel = (base - paged->bump.base); + assert((page_base_rel % paged->bump.page_size) == 0); + u64 page_index = page_base_rel / paged->bump.page_size; + u64 size_pages_mult = round_size_to_page_multiple(size, paged->bump.page_size); + assert((size_pages_mult % paged->bump.page_size) == 0); + u64 page_count = size_pages_mult / paged->bump.page_size; + + Allocator_Paged_Free_Region* region = (Allocator_Paged_Free_Region*)base; + zero_struct(*region); + region->pages = page_count; + + Allocator_Paged_Free_Region* prev = 0; + Allocator_Paged_Free_Region* next = 0; + for (Allocator_Paged_Free_Region* at = paged->free_first; at != 0; at = at->next) + { + if (at < region) + { + prev = at; + next = at->next; + if (next != 0) + { + assert(next > region); + assert((u8*)next >= ((u8*)region + size_pages_mult)); + } + } + } + + if (prev && next) + { + // found a region to insert into + paged_region_insert(prev, region, next, paged->bump.page_size); + } + else if (prev) + { + // got to the end and all were before the free region in memory + paged_region_insert(prev, region, 0, paged->bump.page_size); + } + else + { + // free list is empty + paged->free_first = region; + } +} + +internal u8* +paged_allocator_realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size) +{ + // TODO(PS): + // Process: + // 1. Figure out which page base starts on + // 2. Find if there is a free region after base that is big enough to house + // the new size + // 3. If there is a free region, pull the needed memory out of it + // 4. Otherwise, alloc new_size, copy base into it, and free base + + // TODO(PS): you could do a simple version where you just always alloc, copy, free + return 0; +} + +internal void +paged_allocator_clear(Allocator* allocator) +{ + if (!allocator->allocator_data) return; + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + paged->bump.at = 0; + paged->free_first = 0; +} + +internal void +paged_allocator_destroy(Allocator* allocator) +{ + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + bump_allocator_destroy_(&paged->bump); + allocator_destroy_(allocator, sizeof(Allocator_Paged)); +} + +internal Allocator* +paged_allocator_create_() +{ + u64 size_needed = sizeof(Allocator) + sizeof(Allocator_Bump); + + u8* base = os_mem_reserve(size_needed); + base = os_mem_commit(base, size_needed); + + Allocator* result = (Allocator*)base; + zero_struct(*result); + + Allocator_Paged* paged = (Allocator_Paged*)base + sizeof(Allocator); + zero_struct(*paged); + + result->alloc = paged_allocator_alloc; + result->free = paged_allocator_free; + result->realloc = paged_allocator_realloc; + result->clear = paged_allocator_clear; + result->destroy = paged_allocator_destroy; + result->allocator_data = (u8*)paged; + + return result; +} + +internal Allocator* +paged_allocator_create_reserve(u64 reserve_size, u64 page_size) +{ + Allocator* result = paged_allocator_create_(); + Allocator_Paged* paged = (Allocator_Paged*)result->allocator_data; + + u64 reserve_pages = round_size_to_os_page_multiple(reserve_size); + paged->bump.page_size = page_size; + paged->bump.base = os_mem_reserve(reserve_pages); + if (paged->bump.base != 0) paged->bump.size_reserved = reserve_pages; + + return result; +} + +internal Allocator* +paged_allocator_create_reserve_os_page_size(u64 reserve_size) +{ + u64 page_size = OS_MEM_PAGE_SIZE; + return paged_allocator_create_reserve(reserve_size, page_size); +} + +internal Allocator* +paged_allocator_create_child(Allocator* parent, u64 init_size) +{ + Allocator* result = bump_allocator_create_(OS_MEM_PAGE_SIZE); + result->parent = parent; + + Allocator_Paged* paged = (Allocator_Paged*)result->allocator_data; + paged->bump.base = allocator_alloc(result->parent, init_size); + if (paged->bump.base != 0) + { + paged->bump.size_reserved = init_size; + paged->bump.size_committed = init_size; + } + + return result; +} + +#endif \ No newline at end of file diff --git a/src_v2/lumenarium_random.h b/src_v2/core/lumenarium_core_random.h similarity index 94% rename from src_v2/lumenarium_random.h rename to src_v2/core/lumenarium_core_random.h index 625a842..cffb7bd 100644 --- a/src_v2/lumenarium_random.h +++ b/src_v2/core/lumenarium_core_random.h @@ -3,6 +3,7 @@ #ifndef LUMENARIUM_RANDOM_H #define LUMENARIUM_RANDOM_H +typedef struct Random_Series Random_Series; struct Random_Series { u32 last_value; diff --git a/src_v2/core/lumenarium_core_socket.h b/src_v2/core/lumenarium_core_socket.h new file mode 100644 index 0000000..c59b228 --- /dev/null +++ b/src_v2/core/lumenarium_core_socket.h @@ -0,0 +1,8 @@ +#if !defined(LUMENARIUM_CORE_SOCKET_H) +#define LUMENARIUM_CORE_SOCKET_H + +typedef struct { u64 value; } Socket_Handle; + +// TODO + +#endif // LUMENARIUM_CORE_SOCKET_H \ No newline at end of file diff --git a/src_v2/lumenarium_string.cpp b/src_v2/core/lumenarium_core_string.h similarity index 87% rename from src_v2/lumenarium_string.cpp rename to src_v2/core/lumenarium_core_string.h index 6c6ed7e..92a81ef 100644 --- a/src_v2/lumenarium_string.cpp +++ b/src_v2/core/lumenarium_core_string.h @@ -1,3 +1,24 @@ +////////////////////////////////////////////// +// String + +// NOTE(PS): even though this has a len and cap, it should always be +// null terminated +typedef struct String String; +struct String +{ + u8* str; + u64 len; + u64 cap; +}; + +internal String string_create(u8* str, u64 len, u64 cap); +internal u64 string_copy_to(String* dest, String src); + +////////////////////////////////////////////// +////////////////////////////////////////////// +// IMPLEMENTATION +////////////////////////////////////////////// +////////////////////////////////////////////// internal u64 c_str_len(char* s) @@ -9,7 +30,7 @@ c_str_len(char* s) #define str_varg(s) (int)(s).len, (char*)(s).str #define str_expand(s) (char*)(s).str, (u64)(s).len -#define lit_str(s) String{ (u8*)(s), (u64)sizeof(s)-1, (u64)sizeof(s)-1 } +#define lit_str(s) (String){ (u8*)(s), (u64)sizeof(s)-1, (u64)sizeof(s)-1 } internal String allocator_alloc_string(Allocator* a, u64 cap) @@ -266,4 +287,13 @@ string_f(Allocator* a, char* fmt, ...) String result = string_fv(a, fmt, args); va_end(args); return result; +} + +internal void +dw_put_str(Data_Writer* w, String str) +{ + for (u64 i = 0; i < str.len; i++) + { + dw_put_u8(w, str.str[i]); + } } \ No newline at end of file diff --git a/src_v2/core/lumenarium_core_threads.h b/src_v2/core/lumenarium_core_threads.h new file mode 100644 index 0000000..203ded3 --- /dev/null +++ b/src_v2/core/lumenarium_core_threads.h @@ -0,0 +1,21 @@ +#if !defined(LUMENARIUM_CORE_THREADS_H) +#define LUMENARIUM_CORE_THREADS_H + +typedef struct Thread_Handle Thread_Handle; +struct Thread_Handle { u64 value; }; + +typedef struct Thread_Result Thread_Result; +struct Thread_Result { u32 code; }; + +typedef struct Thread_Data Thread_Data; +typedef Thread_Result Thread_Proc(Thread_Data* data); +struct Thread_Data +{ + Thread_Handle thread_handle; + u32 thread_id; + Thread_Proc* thread_proc; + Allocator* thread_memory; + u8* user_data; +}; + +#endif // LUMENARIUM_CORE_THREADS_H \ No newline at end of file diff --git a/src_v2/core/lumenarium_core_time.h b/src_v2/core/lumenarium_core_time.h new file mode 100644 index 0000000..5a47360 --- /dev/null +++ b/src_v2/core/lumenarium_core_time.h @@ -0,0 +1,39 @@ +#if !defined(LUMENARIUM_CORE_TIME_H) +#define LUMENARIUM_CORE_TIME_H + +typedef struct { s64 value; } Ticks; + +internal Ticks get_ticks_elapsed(Ticks start, Ticks end); +internal r64 ticks_to_seconds(Ticks t, r64 ticks_per_second); +internal r64 get_seconds_elapsed(Ticks start, Ticks end, r64 ticks_per_second); + +/////////////////////////////////////// +/////////////////////////////////////// +// Implementation +/////////////////////////////////////// +/////////////////////////////////////// + +internal Ticks +get_ticks_elapsed(Ticks start, Ticks end) +{ + Ticks result = {}; + result.value = end.value - start.value; + return result; +} + +internal r64 +ticks_to_seconds(Ticks t, r64 ticks_per_second) +{ + r64 result = t.value / ticks_per_second; + return result; +} + +internal r64 +get_seconds_elapsed(Ticks start, Ticks end, r64 ticks_per_second) +{ + Ticks diff = get_ticks_elapsed(start, end); + return ticks_to_seconds(diff, ticks_per_second); +} + + +#endif // LUMENARIUM_CORE_TIME_H \ No newline at end of file diff --git a/src_v2/core/lumenarium_core_types.h b/src_v2/core/lumenarium_core_types.h new file mode 100644 index 0000000..46bb7b1 --- /dev/null +++ b/src_v2/core/lumenarium_core_types.h @@ -0,0 +1,215 @@ +#ifndef LUMENARIUM_CORE_TYPES_H +#define LUMENARIUM_CORE_TYPES_H + +////////////////////////////////////////////// +// Explicit Names for static keyword +#define internal static +#define local_persist static +#define global static +#define local_const static const +#define global_const static const +#define external extern "C" + +#define STMT(x) do {(x);}while(false) + +////////////////////////////////////////////// +// Integer Sizing +#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 // !defined(GUESS_INTS) + +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 // defined(GUESS_INTS) + +////////////////////////////////////////////// +// Basic Types + +typedef s8 b8; +typedef s32 b32; +typedef s64 b64; + +typedef float r32; +typedef double r64; + +////////////////////////////////////////////// +// Basic Type Constants + +#define u8_max 0xFF +#define u16_max 0xFFFF +#define u32_max 0xFFFFFFFF +#define u64_max 0xFFFFFFFFFFFFFFFF + +#define s8_max 127 +#define s16_max 32767 +#define s32_max 2147483647 +#define s64_max 9223372036854775807 + +#define s8_min -127 - 1 +#define s16_min -32767 - 1 +#define s32_min -2147483647 - 1 +#define s64_min -9223372036854775807 - 1 + +#define r32_max 3.402823466e+38f +#define r32_min -3.402823466e+38f +#define r32_smallest_positive 1.1754943508e-38f +#define r32_epsilon 5.96046448e-8f +#define r32_pi 3.14159265359f +#define r32_half_pi 1.5707963267f +#define r32_tau 6.28318530717f + +#define r64_max 1.79769313486231e+308 +#define r64_min -1.79769313486231e+308 +#define r64_smallest_positive 4.94065645841247e-324 +#define r64_epsilon 1.11022302462515650e-16 +#define r64_pi 3.14159265359 +#define r64_half_pi 1.5707963267 +#define r64_tau 6.28318530717 + +#define NanosToSeconds 1 / 10000000.0 +#define SecondsToNanos 10000000.0 + +////////////////////////////////////////////// +// Math + +#ifndef max +# define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef min +# define min(a,b) ((a) > (b) ? (b) : (a)) +#endif + +#define lerp(a,t,b) (a) + ((1.0f - (t)) * (b)) +#define clamp(r0,v,r1) min((r1),max((r0),(v))) +#define lerp_clamp(a,t,b) clamp((a),lerp((a),(t),(b)),(b)) + +internal u32 +round_up_to_pow2_u32(u32 v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +////////////////////////////////////////////// +// Flags + +#define has_flag(data, flag) (((data) & (flag)) != 0) +#define has_flag_exact(data, flag) (((data) & (flag)) == (flag)) +#define add_flag(data, flag) STMT((data) |= (flag)) +#define rem_flag(data, flag) STMT((data) &= (~(flag))) + +////////////////////////////////////////////// +// 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 + +////////////////////////////////////////////// +// Hash Table +// +// Rather than define a data structure, to allow the most flexibility, +// this is just a set of functions that can be integrated into other +// routines. +// In general, they expect you to track a u32* of ids and a u32 capacity + +internal void +hash_table_init(u32* ids, u32 cap) +{ + for (u32 i = 0; i < cap; i++) ids[i] = 0; +} + +internal u32 +hash_table_find_(u32* ids, u32 cap, u32 start_id, u32 target_value) +{ + u32 index = start_id % cap; + u32 start = index; + do { + if (ids[index] == target_value) break; + index = (index + 1) % cap; + } while (index != start); + return index; +} + +internal u32 +hash_table_register(u32* ids, u32 cap, u32 new_id) +{ + u32 index = hash_table_find_(ids, cap, new_id, 0); + if (ids[index] != 0) return cap; + ids[index] = new_id; + return index; +} + +internal u32 +hash_table_find(u32* ids, u32 cap, u32 value) +{ + u32 result = hash_table_find_(ids, cap, value, value); + if (ids[result] != value) return cap; + return result; +} + +////////////////////////////////////////////// +// Predeclaring Memory Functions + +u64 round_size_to_page_multiple(u64 size, u64 page_size); +u64 round_size_to_os_page_multiple(u64 size); + +////////////////////////////////////////////// +// Vector Extensions + +#if defined(HANDMADE_MATH_IMPLEMENTATION) + +#define v2_to_v3(xy,z) (v3){(xy).x, (xy).y, (z)} +#define v2_floor(v) (v2){ floorf(v.x), floorf(v.y) } +#define v3_floor(v) (v3){ floorf(v.x), floorf(v.y), floorf(v.z) } + +internal bool +rect2_contains(v2 min, v2 max, v2 point) +{ + return ( + min.x <= point.x && min.y <= point.y && + max.x >= point.x && max.y >= point.y + ); +} + +#endif // defined(HANDMADE_MATH_IMPLEMENTATION) + +////////////////////////////////////////////// +// Color Constants + +#define WHITE_V4 (v4){1,1,1,1} +#define BLACK_V4 (v4){0,0,0,1} +#define RED_V4 (v4){1,0,0,1} +#define GREEN_V4 (v4){0,1,0,1} +#define BLUE_V4 (v4){0,0,1,1} +#define YELLOW_V4 (v4){1,1,0,1} +#define TEAL_V4 (v4){0,1,1,1} +#define PINK_V4 (v4){1,0,1,1} + +#endif \ No newline at end of file diff --git a/src_v2/core/lumenarium_core_window.h b/src_v2/core/lumenarium_core_window.h new file mode 100644 index 0000000..704366f --- /dev/null +++ b/src_v2/core/lumenarium_core_window.h @@ -0,0 +1,235 @@ +#if !defined(LUMENARIUM_CORE_WINDOW_H) +#define LUMENARIUM_CORE_WINDOW_H + +typedef u32 Window_Event_Kind; +enum +{ + WindowEvent_Invalid = 0, + + WindowEvent_MouseScroll, + WindowEvent_MouseMoved, + WindowEvent_ButtonDown, + WindowEvent_ButtonUp, + WindowEvent_Char, + WindowEvent_WindowClosed, + + WindowEvent_Count, +}; + +typedef u32 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 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, +}; + +typedef struct Window_Event Window_Event; +struct Window_Event +{ + Window_Event_Kind kind; + Key_Code key_code; + Key_Flags key_flags; + s32 mouse_x; + s32 mouse_y; + s32 scroll_amt; + char char_value; +}; + +typedef u32 Cursor_Kind; +enum +{ + Cursor_Arrow, + Cursor_Pointer, + Cursor_Loading, + Cursor_HArrows, + Cursor_VArrows, + Cursor_DTopLeftArrows, + Cursor_DTopRightArrows, + Cursor_Count, +}; + +#define INPUT_FRAME_STRING_LENGTH 32 +typedef struct Input_Frame Input_Frame; +struct Input_Frame +{ + Key_Flags key_flags[KeyCode_Count]; + + char string_input[INPUT_FRAME_STRING_LENGTH]; + u32 string_input_len; + + v2 mouse_pos; + s32 mouse_scroll; +}; + +typedef struct Input_State Input_State; +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(Allocator* a); +internal Key_Flags input_key_advance(Key_Flags flag); +internal void input_state_swap_frames(Input_State* input_state); +internal bool input_key_is_down(Input_State* input_state, Key_Code key); +internal bool input_key_went_down(Input_State* input_state, Key_Code key); +internal bool input_key_is_up(Input_State* input_state, Key_Code key); +internal bool input_key_went_up(Input_State* input_state, Key_Code key); + +////////////////////////////////////////// +////////////////////////////////////////// +// IMPLEMENTATION +////////////////////////////////////////// +////////////////////////////////////////// + +internal Input_State* +input_state_create(Allocator* a) +{ + Input_State* result = allocator_alloc_struct(a, Input_State); + result->frame_hot = result->frames + 0; + result->frame_cold = result->frames + 1; + return result; +} + +internal Key_Flags +input_key_advance(Key_Flags flag) +{ + Key_Flags result = flag; + if (key_is_down(flag)) + { + add_flag(result, KeyFlag_State_WasDown); + } + if (key_is_up(flag)) + { + rem_flag(result, KeyFlag_State_WasDown); + } + 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 + Key_Flags* hot_key_flags = input_state->frame_hot->key_flags; + Key_Flags* cold_key_flags = input_state->frame_cold->key_flags; + for (u32 i = 0; i < KeyCode_Count; i++) + { + hot_key_flags[i] = input_key_advance(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, Key_Code key) +{ + 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, Key_Code key) +{ + 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, Key_Code key) +{ + 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, Key_Code key) +{ + Key_Flags flags = input_state->frame_hot->key_flags[key]; + return !key_is_down(flags) && key_was_down(flags); +} +#endif // LUMENARIUM_CORE_WINDOW_H \ No newline at end of file diff --git a/src_v2/editor/graphics/lumenarium_editor_graphics.h b/src_v2/editor/graphics/lumenarium_editor_graphics.h new file mode 100644 index 0000000..9e50c91 --- /dev/null +++ b/src_v2/editor/graphics/lumenarium_editor_graphics.h @@ -0,0 +1,419 @@ +#if !defined(LUMENARIUM_EDITOR_GRAPHICS_H) +#define LUMENARIUM_EDITOR_GRAPHICS_H + +#define os_gl_no_error() os_gl_no_error_((char*)__FILE__, (u32)__LINE__) +void os_gl_no_error_(char* file, u32 line); + +#define GL_NULL (u32)0 + +#define SHADER_MAX_ATTRS 8 +#define SHADER_ATTR_LAST (u32)(1 << 31) +typedef struct Shader Shader; +struct Shader +{ + u32 id; + u32 attrs[SHADER_MAX_ATTRS]; + u32 uniforms[SHADER_MAX_ATTRS]; +}; + +typedef struct XPlatform_Shader_Program_Src XPlatform_Shader_Program_Src; +struct XPlatform_Shader_Program_Src +{ + String win32_vert; + String win32_frag; + + String osx_vert; + String osx_frag; + + String wasm_vert; + String wasm_frag; +}; + +String xplatform_shader_program_get_vert(XPlatform_Shader_Program_Src src); +String xplatform_shader_program_get_frag(XPlatform_Shader_Program_Src src); + +typedef struct Geometry_Buffer Geometry_Buffer; +struct Geometry_Buffer +{ + u32 buffer_id_vao; + u32 buffer_id_vertices; + u32 buffer_id_indices; + u32 vertices_len; + u32 indices_len; +}; + +typedef struct Texture Texture; +struct Texture +{ + u32 id; + + u32 w, h, s; +}; + +typedef struct Graphics_Frame_Desc Graphics_Frame_Desc; +struct Graphics_Frame_Desc +{ + v4 clear_color; + v2 viewport_min; + v2 viewport_max; +}; + +void frame_begin(Graphics_Frame_Desc desc); +void frame_clear(); + +// Geometry +Geometry_Buffer geometry_buffer_create(r32* vertices, u32 vertices_len, u32* indices, u32 indices_len); +Shader shader_create(String code_vert, String code_frag, String* attribs, u32 attribs_len, String* uniforms, u32 uniforms_len); +void geometry_buffer_update(Geometry_Buffer* buffer, r32* verts, u32 verts_offset, u32 verts_len, u32* indices, u32 indices_offset, u32 indices_len); + +// Shaders +void geometry_bind(Geometry_Buffer geo); +void shader_bind(Shader shader); +void geometry_drawi(Geometry_Buffer geo, u32 indices); +void geometry_draw(Geometry_Buffer geo); +void vertex_attrib_pointer(Geometry_Buffer geo, Shader shader, u32 count, u32 attr_index, u32 stride, u32 offset); +void set_uniform(Shader shader, u32 index, m44 u); + +// Textures +Texture texture_create(u8* pixels, u32 width, u32 height, u32 stride); +void texture_bind(Texture tex); +void texture_update(Texture tex, u8* new_pixels, u32 width, u32 height, u32 stride); + +////////////////////////////////////////// +////////////////////////////////////////// +// IMPLEMENTATION +////////////////////////////////////////// +////////////////////////////////////////// + +Geometry_Buffer +geometry_buffer_create( + r32* vertices, u32 vertices_len, + u32* indices, u32 indices_len +){ + Geometry_Buffer result = {}; + + gl.glGenVertexArrays(1, &result.buffer_id_vao); + os_gl_no_error(); + + GLuint buffers[2]; + gl.glGenBuffers(2, (GLuint*)buffers); + os_gl_no_error(); + + result.buffer_id_vertices = buffers[0]; + result.buffer_id_indices = buffers[1]; + + // Vertices + gl.glBindVertexArray(result.buffer_id_vao); + gl.glBindBuffer(GL_ARRAY_BUFFER, result.buffer_id_vertices); + os_gl_no_error(); + + gl.glBufferData(GL_ARRAY_BUFFER, sizeof(r32) * vertices_len, vertices, GL_STATIC_DRAW); + os_gl_no_error(); + result.vertices_len = vertices_len; + + // Indices + gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.buffer_id_indices); + os_gl_no_error(); + + gl.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(u32) * indices_len, indices, GL_STATIC_DRAW); + os_gl_no_error(); + result.indices_len = indices_len; + + gl.glBindBuffer(GL_ARRAY_BUFFER, GL_NULL); + gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_NULL); + + return result; +} + +void +geometry_buffer_update( + Geometry_Buffer* buffer, + r32* verts, + u32 verts_offset, + u32 verts_len, + u32* indices, + u32 indices_offset, + u32 indices_len +){ + gl.glBindVertexArray(buffer->buffer_id_vao); + gl.glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer_id_vertices); + os_gl_no_error(); + + if (verts_len > buffer->vertices_len) + { + // NOTE(PS): this is because we're going to delete the old buffer and + // create a new one. In order to do that and not lose data, the update + // function needs to have been passed all the buffer's data + assert(verts_offset == 0); + gl.glBufferData(GL_ARRAY_BUFFER, verts_len * sizeof(r32), (void*)verts, GL_STATIC_DRAW); + } + else + { + gl.glBufferSubData(GL_ARRAY_BUFFER, verts_offset * sizeof(r32), verts_len * sizeof(r32), (void*)verts); + } + os_gl_no_error(); + + gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->buffer_id_indices); + os_gl_no_error(); + if (indices_len > buffer->indices_len) + { + // NOTE(PS): this is because we're going to delete the old buffer and + // create a new one. In order to do that and not lose data, the update + // function needs to have been passed all the buffer's data + assert(indices_offset == 0); + gl.glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_len * sizeof(u32), (void*)indices, GL_STATIC_DRAW); + } + else + { + gl.glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indices_offset * sizeof(u32), indices_len * sizeof(u32), (void*)indices); + } + os_gl_no_error(); +} + +Shader +shader_create(String code_vert, String code_frag, String* attrs, u32 attrs_len, String* uniforms, u32 uniforms_len){ + Shader result = {}; + + GLuint shader_vert = gl.glCreateShader(GL_VERTEX_SHADER); + s32* code_vert_len = (s32*)&code_vert.len; + gl.glShaderSource(shader_vert, 1, (const char**)&code_vert.str, code_vert_len); + gl.glCompileShader(shader_vert); + { // errors + GLint shader_vert_compiled; + gl.glGetShaderiv(shader_vert, GL_COMPILE_STATUS, &shader_vert_compiled); + if (!shader_vert_compiled) + { + GLsizei log_length = 0; + GLchar message[1024]; + gl.glGetShaderInfoLog(shader_vert, 1024, &log_length, message); + printf("GLSL Error: %s\n", message); + invalid_code_path; + } + } + + GLuint shader_frag = gl.glCreateShader(GL_FRAGMENT_SHADER); + s32* code_frag_len = (s32*)&code_frag.len; + gl.glShaderSource(shader_frag, 1, (const char**)&code_frag.str, code_frag_len); + gl.glCompileShader(shader_frag); + { // errors + GLint shader_frag_compiled; + gl.glGetShaderiv(shader_frag, GL_COMPILE_STATUS, &shader_frag_compiled); + if (!shader_frag_compiled) + { + GLsizei log_length = 0; + GLchar message[1024]; + gl.glGetShaderInfoLog(shader_frag, 1024, &log_length, message); + printf("GLSL Error: %s\n", message); + printf("%.*s\n", str_varg(code_frag)); + invalid_code_path; + } + } + + result.id = (u32)gl.glCreateProgram(); + gl.glAttachShader(result.id, shader_vert); + gl.glAttachShader(result.id, shader_frag); + gl.glLinkProgram(result.id); + + GLint program_linked; + gl.glGetProgramiv(result.id, GL_LINK_STATUS, &program_linked); + if (program_linked != GL_TRUE) + { + GLsizei log_length = 0; + GLchar message[1024]; + gl.glGetProgramInfoLog(result.id, 1024, &log_length, message); + printf("GLSL Error: %s\n", message); + invalid_code_path; + } + + gl.glUseProgram(result.id); + + // TODO(PS): delete the vert and frag programs + + assert(attrs_len < SHADER_MAX_ATTRS); + for (u32 i = 0; i < attrs_len; i++) + { + result.attrs[i] = gl.glGetAttribLocation(result.id, (char*)attrs[i].str); + os_gl_no_error(); + } + result.attrs[attrs_len] = SHADER_ATTR_LAST; + + assert(uniforms_len < SHADER_MAX_ATTRS); + for (GLuint i = 0; i < uniforms_len; i++) + { + s32 len = (s32)uniforms[i].len; + result.uniforms[i] = gl.glGetUniformLocation(result.id, (char*)uniforms[i].str); + } + result.uniforms[uniforms_len] = SHADER_ATTR_LAST; + + return result; +} + +void +set_uniform(Shader shader, u32 index, m44 u) +{ + gl.glUniformMatrix4fv(shader.uniforms[index], 1, GL_FALSE, (r32*)u.Elements); +} + +void +geometry_bind(Geometry_Buffer geo) +{ + gl.glBindVertexArray(geo.buffer_id_vao); + os_gl_no_error(); + + gl.glBindBuffer(GL_ARRAY_BUFFER, geo.buffer_id_vertices); + os_gl_no_error(); + + gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geo.buffer_id_indices); + os_gl_no_error(); +} + +void +shader_bind(Shader shader) +{ + gl.glUseProgram(shader.id); + os_gl_no_error(); + for (u32 i = 0; + ((i < SHADER_MAX_ATTRS) && (shader.attrs[i] != SHADER_ATTR_LAST)); + i++) + { + gl.glEnableVertexAttribArray(shader.attrs[i]); + os_gl_no_error(); + } +} + +void +geometry_drawi(Geometry_Buffer geo, u32 indices){ + glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_INT, 0); + os_gl_no_error(); +} + +void +geometry_draw(Geometry_Buffer geo){ + geometry_drawi(geo, geo.indices_len); +} + +void vertex_attrib_pointer(Geometry_Buffer geo, Shader shader, GLuint count, GLuint attr_index, GLuint stride, GLuint offset){ + geometry_bind(geo); + gl.glVertexAttribPointer(shader.attrs[attr_index], count, GL_FLOAT, false, stride * sizeof(float), (void*)(offset * sizeof(float))); + os_gl_no_error(); + gl.glEnableVertexAttribArray(shader.attrs[attr_index]); + os_gl_no_error(); +} + +Texture +texture_create(u8* pixels, u32 width, u32 height, u32 stride) +{ + Texture result = {}; + glGenTextures(1, &result.id); + os_gl_no_error(); + + glBindTexture(GL_TEXTURE_2D, result.id); + os_gl_no_error(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + os_gl_no_error(); + + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + width, + height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + pixels + ); + os_gl_no_error(); + + result.w = width; + result.h = height; + result.s = stride; + + return result; +} + +void +texture_update(Texture tex, u8* new_pixels, u32 width, u32 height, u32 stride) +{ + // NOTE(PS): this function simply replaces the entire image + // we can write a more granular version if we need it + + assert(tex.w == width && tex.h == height && tex.s == stride); + texture_bind(tex); + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, 0, // offset + width, height, + GL_RGBA, + GL_UNSIGNED_BYTE, + new_pixels + ); + os_gl_no_error(); +} + +void +texture_bind(Texture tex) +{ + glBindTexture(GL_TEXTURE_2D, tex.id); + os_gl_no_error(); +} + +void +frame_begin(Graphics_Frame_Desc desc) +{ + v4 cc = desc.clear_color; + glClearColor(cc.r, cc.g, cc.b, cc.a); + + v2 vmin = desc.viewport_min; + v2 vmax = desc.viewport_max; + glViewport((GLsizei)vmin.x, (GLsizei)vmin.y, (GLint)vmax.x, (GLint)vmax.y); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDisable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + + os_gl_no_error(); +} + +void +frame_clear() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + + +String +xplatform_shader_program_get_vert(XPlatform_Shader_Program_Src src) +{ +#if defined(PLATFORM_win32) + return src.win32_vert; +#elif defined(PLATFORM_osx) + return src.osx_vert; +#elif defined(PLATFORM_wasm) + return src.wasm_vert; +#endif +} + +String +xplatform_shader_program_get_frag(XPlatform_Shader_Program_Src src) +{ +#if defined(PLATFORM_win32) + return src.win32_frag; +#elif defined(PLATFORM_osx) + return src.osx_frag; +#elif defined(PLATFORM_wasm) + return src.wasm_frag; +#endif +} + + +#endif // LUMENARIUM_EDITOR_GRAPHICS_H \ No newline at end of file diff --git a/src_v2/editor/graphics/lumenarium_editor_opengl.h b/src_v2/editor/graphics/lumenarium_editor_opengl.h new file mode 100644 index 0000000..caaa02f --- /dev/null +++ b/src_v2/editor/graphics/lumenarium_editor_opengl.h @@ -0,0 +1,76 @@ +/* date = March 24th 2022 6:05 pm */ + +#ifndef LUMENARIUM_EDITOR_OPENGL_H +#define LUMENARIUM_EDITOR_OPENGL_H + +// glext.h - https://github.com/KhronosGroup/OpenGL-Registry/blob/main/api/GL/glext.h +// wglext.h - + +void os_gl_no_error(); + +// OpenGL 3.3+ Context Creation +#if defined(PLATFORM_win32) +typedef const char *WINAPI proc_wglGetExtensionsStringARB(HDC hdc); +typedef BOOL proc_wglChoosePixelFormatARB(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +typedef HGLRC proc_wglCreateContextAttribsARB(HDC hDC, HGLRC hshareContext, const int *attribList); +#endif + +// OpenGL 3.3+ Extensions +typedef void proc_glGenVertexArrays(GLsizei n, GLuint *arrays); +typedef void proc_glBindVertexArray(GLuint array); +typedef void proc_glGenBuffers (GLsizei n, GLuint *buffers); +typedef void proc_glBindBuffer(GLenum target, GLuint buffer); +typedef void proc_glBufferData(GLenum target, size_t size, const void *data, GLenum usage); +typedef void proc_glBufferSubData(GLenum target, size_t offset, size_t size, const void* data); +typedef GLuint proc_glCreateShader(GLenum type); +typedef void proc_glShaderSource(GLuint shader, u32 count, const char* const* string, const GLint *length); +typedef void proc_glCompileShader(GLuint shader); +typedef GLuint proc_glCreateProgram(void); +typedef void proc_glAttachShader(GLuint program, GLuint shader); +typedef void proc_glLinkProgram(GLuint program); +typedef void proc_glUseProgram(GLuint program); +typedef GLuint proc_glGetAttribLocation(GLuint program, const char* name); +typedef void proc_glVertexAttribPointer(GLuint attr, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +typedef void proc_glEnableVertexAttribArray(GLuint index); +typedef void proc_glGetShaderiv(GLuint shader, GLenum ele, GLint* value_out); +typedef void proc_glGetShaderInfoLog(GLuint shader, GLuint buf_len, GLsizei* len_out, GLchar* buf); +typedef void proc_glGetProgramiv(GLuint program, GLenum ele, GLint* value_out); +typedef void proc_glGetProgramInfoLog(GLuint program, GLuint cap, GLsizei* len_out, GLchar* buf); +typedef GLuint proc_glGetUniformLocation(GLuint program, const char* name); +typedef void proc_glUniformMatrix4fv(GLuint uniform, GLuint count, GLenum normalize, GLfloat* elements); +typedef struct OpenGL_Extensions OpenGL_Extensions; +struct OpenGL_Extensions +{ + #if defined(PLATFORM_win32) + proc_wglGetExtensionsStringARB* wglGetExtensionsStringARB; + proc_wglChoosePixelFormatARB* wglChoosePixelFormatARB; + proc_wglCreateContextAttribsARB* wglCreateContextAttribsARB; + #endif + + proc_glGenVertexArrays* glGenVertexArrays; + proc_glBindVertexArray* glBindVertexArray; + proc_glGenBuffers* glGenBuffers; + proc_glBindBuffer* glBindBuffer; + proc_glBufferData* glBufferData; + proc_glBufferSubData* glBufferSubData; + proc_glCreateShader* glCreateShader; + proc_glShaderSource* glShaderSource; + proc_glCompileShader* glCompileShader; + proc_glCreateProgram* glCreateProgram; + proc_glAttachShader* glAttachShader; + proc_glLinkProgram* glLinkProgram; + proc_glUseProgram* glUseProgram; + proc_glGetAttribLocation* glGetAttribLocation; + proc_glVertexAttribPointer* glVertexAttribPointer; + proc_glEnableVertexAttribArray* glEnableVertexAttribArray; + proc_glGetShaderiv* glGetShaderiv; + proc_glGetShaderInfoLog* glGetShaderInfoLog; + proc_glGetProgramiv* glGetProgramiv; + proc_glGetProgramInfoLog* glGetProgramInfoLog; + proc_glGetUniformLocation* glGetUniformLocation; + proc_glUniformMatrix4fv* glUniformMatrix4fv; +}; + +global OpenGL_Extensions gl = {}; + +#endif //LUMENARIUM_EDITOR_OPENGL_H \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor.c b/src_v2/editor/lumenarium_editor.c new file mode 100644 index 0000000..610f2cc --- /dev/null +++ b/src_v2/editor/lumenarium_editor.c @@ -0,0 +1,327 @@ +internal void +ed_load_font_cb(File_Async_Job_Args result, u8* user_data) +{ + App_State* state = (App_State*)user_data; + UI* ui = &state->editor->ui; + + u8* f = result.data.base; + stbtt_fontinfo font; + if (!stbtt_InitFont(&font, f, stbtt_GetFontOffsetForIndex(f, 0))) + { + invalid_code_path; + } + + r32 scale = stbtt_ScaleForPixelHeight(&font, 18.0f); + s32 ascent, descent, line_gap; + stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap); + ui->font_ascent = (r32)ascent * scale; + ui->font_descent = (r32)descent * scale; + ui->font_line_gap = (r32)line_gap * scale; + if (ui->font_line_gap == 0) ui->font_line_gap = 5; + + String c = lit_str("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-=_+[]{}\\|;:'\",<.>/?`~"); + for (u64 i = 0; i < c.len; i++) + { + s32 w, h, xoff, yoff; + u32 id = (u32)c.str[i]; + u8* bp = stbtt_GetCodepointBitmap(&font, 0, scale, (char)c.str[i], &w, &h, &xoff, &yoff); + s32 x0, y0, x1, y1; + stbtt_GetCodepointBitmapBoxSubpixel(&font, (char)c.str[i], scale, scale, 0, 0, &x0, &y0, &x1, &y1); + + v2 offset = (v2){ 0, (r32)y0 }; + texture_atlas_register(&state->editor->ui.atlas, bp, (u32)w, (u32)h, id, offset, TextureAtlasRegistration_PixelFormat_Alpha); + stbtt_FreeBitmap(bp, 0); + } + + Texture_Atlas_Sprite m_sprite = texture_atlas_sprite_get(&state->editor->ui.atlas, (u32)'m'); + ui->font_space_width = (r32)(m_sprite.max_x - m_sprite.min_x); + + texture_update(ui->atlas_texture, ui->atlas.pixels, 1024, 1024, 1024); +} + +internal void +ed_draw_panel(u8* user_data, BSP_Node_Id id, BSP_Node node, BSP_Area area) +{ + App_State* state = (App_State*)user_data; + UI* ui = &state->editor->ui; + scratch_get(scratch); + + UI_Layout title_layout = {}; + ui_layout_set_row_info(ui, &title_layout); + title_layout.bounds_min = (v2){ area.min.x, area.max.y - title_layout.row_height }; + title_layout.bounds_max = area.max; + title_layout.at = title_layout.bounds_min; + + UI_Layout panel_layout = {}; + ui_layout_set_row_info(ui, &panel_layout); + panel_layout.bounds_min = area.min; + panel_layout.bounds_max = (v2){ area.max.x, title_layout.bounds_max.y }; + panel_layout.at = panel_layout.bounds_min; + + ui_layout_push(ui, &panel_layout); + + String title = {}; + switch (node.user_data) + { + case 0: + { + title = lit_str("None"); + } break; + + case 1: + { + ed_sculpture_visualizer(state); + title = lit_str("Sculpture"); + } break; + + invalid_default_case; + } + ui_layout_pop(ui); + + ui_layout_push(ui, &title_layout); + UI_Widget_Desc bg = {}; + bg.style.flags = UIWidgetStyle_Bg; + bg.style.color_bg = (v4){.4f,.4f,.4f,1}; + bg.style.sprite = WHITE_SPRITE_ID; + bg.string = string_f(scratch.a, "%.*s_%u_title_bg", str_varg(title), id.value); + bg.p_min = title_layout.bounds_min; + bg.p_max = title_layout.bounds_max; + UI_Widget_Result r = ui_widget_push(ui, bg); + ui_layout_row_begin(&title_layout, 4); + { + ui_textc(ui, title, BLACK_V4); + } + ui_layout_row_end(&title_layout); + ui_widget_pop(ui, r.id); + ui_layout_pop(ui); + + scratch_release(scratch); +} + +Geometry_Buffer b; +Shader shd; +Texture tex; + +internal void +ed_init(App_State* state) +{ + lumenarium_env_validate(); + + Editor* editor = allocator_alloc_struct(permanent, Editor); + zero_struct(*editor); + state->editor = editor; + editor->content_scale = (v2){1,1}; + editor->ui = ui_create(4096, 4096, state->input_state, permanent); + editor->ui.draw_panel_cb = ed_draw_panel; + editor->ui.draw_panel_cb_data = (u8*)state; + //bsp_split(&editor->ui.panels, editor->ui.panels.root, 700, BSPSplit_YAxis, 0, 1); + + file_async_read(lit_str("data/font.ttf"), ed_load_font_cb); + + r32 w = 1400; + r32 h = 700; + r32 z = -1; + r32 tri[] = { + 0, 0, 0, 0, 0, + w, 0, z, 1, 0, + w, h, z, 1, 1, + 0, h, z, 0, 1, + }; + u32 indices[] = { 0, 1, 2, 0, 2, 3 }; + + String shd_v = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec3 pos;\n" + "layout (location = 1) in vec2 auv;\n" + "out vec2 uv;\n" + "uniform mat4 proj;\n" + "void main(void) { \n" + " gl_Position = proj * vec4(pos, 1); \n" + " uv = auv;\n" + "}\n" + ); + String shd_f = lit_str( + "#version 330 core\n" + "in vec2 uv;\n" + "out vec4 FragColor;\n" + "uniform sampler2D tex;\n" + "void main(void) { FragColor = texture(tex, uv) * vec4(1,1,1,1); }" + ); + String shd_a[] = { lit_str("pos"), lit_str("auv") }; + String shd_u[] = { lit_str("proj") }; + b = geometry_buffer_create( + tri, sizeof(tri) / sizeof(r32), + indices, 6 + ); + shd = shader_create(shd_v, shd_f, shd_a, 2, shd_u, 1); + vertex_attrib_pointer(b, shd, 3, shd.attrs[0], 5, 0); + vertex_attrib_pointer(b, shd, 2, shd.attrs[1], 5, 3); + + u32 tex_pix[] = { 0xFFFFFFFF, 0xFF00FFFF, 0xFFFFFF00, 0xFFFF00FF }; + tex = texture_create((u8*)tex_pix, 2, 2, 2); + + ed_sculpture_visualizer_init(state); + lumenarium_env_validate(); +} + +internal u8* +ed_leds_to_texture(App_State* state, Allocator_Scratch* scratch, u32 pixels_dim) +{ + u32 at = 0; + u32* pixels = allocator_alloc_array(scratch->a, u32, pixels_dim * pixels_dim); + for (u32 a = 0; a < state->assemblies.len; a++) + { + Assembly_Pixel_Buffer leds = state->assemblies.pixel_buffers[a]; + for (u32 p = 0; p < leds.len; p++) + { + Assembly_Pixel led = leds.pixels[p]; + u32 index = at++; + pixels[index] = ( + led.r << 0 | + led.g << 8 | + led.b << 16 | + 0xFF << 24 + ); + } + } + return (u8*)pixels; +} + +internal void +ed_sculpture_updated(App_State* state, r32 scale, r32 led_size) +{ + lumenarium_env_validate(); + Editor* ed = state->editor; + if (!ed) return; + + scratch_get(scratch); + + // NOTE(PS): we need to know the total number of leds in order to give them + // texture coordinates + u32 leds_count = 0; + for (u32 a = 0; a < state->assemblies.len; a++) + { + Assembly_Pixel_Buffer pixels = state->assemblies.pixel_buffers[a]; + leds_count += pixels.len; + } + + // round up to a texture whose sides are powers of two + u32 pixels_dim = (u32)floorf(sqrtf((r32)leds_count)); + pixels_dim = round_up_to_pow2_u32(pixels_dim); + u32 pixels_count = pixels_dim * pixels_dim; + r32 texel_dim = 1 / (r32)pixels_dim; + + // NOTE(PS): Rebuild the sculpture geometry to point to the new + // sculpture. + Geo_Vertex_Buffer_Storage storage = ( + GeoVertexBufferStorage_Position | + GeoVertexBufferStorage_TexCoord + ); + + u32 verts_cap = leds_count * 4; + u32 indices_cap = leds_count * 6; + Geo_Quad_Buffer_Builder geo = geo_quad_buffer_builder_create(scratch.a, verts_cap, storage, indices_cap); + geo_vertex_buffers_validate(&geo.buffer_vertex); + + r32 r = led_size; + u32 pixels_created = 0; + for (u32 a = 0; a < state->assemblies.len; a++) + { + Assembly_Pixel_Buffer pixels = state->assemblies.pixel_buffers[a]; + for (u32 p = 0; p < pixels.len; p++) + { + v3 c = pixels.positions[p].xyz; + c = HMM_MultiplyVec3f(c, scale); + + u32 pixel_count = pixels_created++; + u32 pixel_x = pixel_count % pixels_dim; + u32 pixel_y = pixel_count / pixels_dim; + r32 texel_x_min = (r32)pixel_x / (r32)pixels_dim; + r32 texel_y_min = (r32)pixel_y / (r32)pixels_dim; + r32 texel_x_max = texel_x_min + texel_dim; + r32 texel_y_max = texel_y_min + texel_dim; + + v2 t0 = (v2){texel_x_min, texel_y_min}; + v2 t1 = (v2){texel_x_max, texel_y_min}; + v2 t2 = (v2){texel_x_max, texel_y_max}; + v2 t3 = (v2){texel_x_min, texel_y_max}; + + v3 p0 = HMM_AddVec3(c, (v3){ -r, -r, 0 }); + v3 p1 = HMM_AddVec3(c, (v3){ r, -r, 0 }); + v3 p2 = HMM_AddVec3(c, (v3){ r, r, 0 }); + v3 p3 = HMM_AddVec3(c, (v3){ -r, r, 0 }); + geo_quad_buffer_builder_push_vt(&geo, p0, p1, p2, p3, t0, t1, t2, t3); + if (p == 1008) + { + s32 x = 5; + } + } + } + geo_vertex_buffers_validate(&geo.buffer_vertex); + + if (ed->sculpture_geo.indices_len != 0) + { + invalid_code_path; + // TODO(PS): destroy the old geometry buffer or update it + } + ed->sculpture_geo = geometry_buffer_create( + geo.buffer_vertex.values, + geo.buffer_vertex.len, + geo.buffer_index.values, + geo.buffer_index.len + ); + + vertex_attrib_pointer(ed->sculpture_geo, ed->sculpture_shd, 3, ed->sculpture_shd.attrs[0], 5, 0); + vertex_attrib_pointer(ed->sculpture_geo, ed->sculpture_shd, 2, ed->sculpture_shd.attrs[1], 5, 3); + + // TODO(PS): make this have enough pixels for the sculpture + // TODO(PS): map leds to pixels + + if (ed->sculpture_tex.w != 0) + { + invalid_code_path; + // TODO(PS): destroy the old texture + } + + u8* pixels = ed_leds_to_texture(state, &scratch, pixels_dim); + ed->sculpture_tex = texture_create(pixels, pixels_dim, pixels_dim, pixels_dim); + + scratch_release(scratch); + lumenarium_env_validate(); +} + +internal void +ed_frame_prepare(App_State* state) +{ + lumenarium_env_validate(); + ui_frame_prepare(&state->editor->ui, state->editor->window_dim); + lumenarium_env_validate(); +} + +internal void +ed_frame(App_State* state) +{ + lumenarium_env_validate(); + Editor* ed = state->editor; + UI* ui = &ed->ui; + + { + scratch_get(scratch); + u32 w = ed->sculpture_tex.w; + u8* pixels = ed_leds_to_texture(state, &scratch, w); + texture_update(ed->sculpture_tex, pixels, w, w, w); + scratch_release(scratch); + } + + edr_render_begin(state); + ui_draw(&state->editor->ui); + edr_render(state); + lumenarium_env_validate(); +} + +internal void +ed_cleanup(App_State* state) +{ + lumenarium_env_validate(); +} + diff --git a/src_v2/editor/lumenarium_editor.cpp b/src_v2/editor/lumenarium_editor.cpp deleted file mode 100644 index 833730d..0000000 --- a/src_v2/editor/lumenarium_editor.cpp +++ /dev/null @@ -1,376 +0,0 @@ - -static r32 z_ = 0; -static r32 r_ = 0.3f; -static r32 quad_verts[] = { - -r_, -r_, z_, 1.0f, 0, 0, - r_, -r_, z_, 1.0f, 1, 0, - r_, r_, z_, 1.0f, 1, 1, - -r_, r_, z_, 1.0f, 0, 1, -}; - -static u32 quad_indices[] = { - 0, 1, 2, - 0, 2, 3, -}; - -static String shader_code_vert_win32 = lit_str( - "#version 330 core\n" - "layout (location = 0) in vec4 coordinates;\n" - "layout (location = 1) in vec2 uv;\n" - "out vec2 tex_coord;\n" - "void main(void) {\n" - " gl_Position = coordinates;\n" - " tex_coord = uv;\n" - "}" - ); - -static String shader_code_vert_wasm = lit_str( - "precision highp float;\n" - "attribute vec4 coordinates;\n" - "attribute vec2 uv;\n" - "varying vec2 tex_coord;\n" - "void main(void) {\n" - " gl_Position = coordinates;\n" - " tex_coord = uv;\n" - "}"); - -static String shader_code_frag_win32 = lit_str( - "#version 330 core\n" - "in vec2 tex_coord;\n" - "out vec4 FragColor;\n" - "uniform sampler2D texture;\n" - "void main(void) {\n" - "// FragColor = vec4(1,tex_coord.x,tex_coord.y,1);\n" - " FragColor = texture(texture, tex_coord);\n" - "}" - ); - -static String shader_code_frag_wasm = lit_str( - "precision highp float;\n" - "varying vec2 tex_coord;\n" - "uniform sampler2D texture;\n" - "void main(void) {\n" - " gl_FragColor = texture2D(texture, tex_coord);\n" - " // vec4(1, tex_coord.x, tex_coord.y, 1);\n" - "}"); - -static u32 pix[] = { - 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 0xFF000000, - 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 0xFF000000, -}; - -void make_quad(Platform_Geometry_Buffer* geo, Platform_Shader* shd, Platform_Texture* tex) -{ - // TODO(PS): TEMP -#if defined(PLATFORM_win32) - String shader_code_vert = shader_code_vert_win32; - String shader_code_frag = shader_code_frag_win32; -#elif defined(PLATFORM_wasm) - String shader_code_vert = shader_code_vert_wasm; - String shader_code_frag = shader_code_frag_wasm; -#endif - - *geo = platform_geometry_buffer_create( - quad_verts, 24, quad_indices, 6 - ); - - String attribs[] = { - lit_str("coordinates"), - lit_str("uv"), - }; - *shd = platform_shader_create( - shader_code_vert, shader_code_frag, attribs, 2, 0, 0 - ); - - platform_vertex_attrib_pointer(*geo, *shd, 4, shd->attrs[0], 6, 0); - platform_vertex_attrib_pointer(*geo, *shd, 2, shd->attrs[1], 6, 4); - - *tex = platform_texture_create((u8*)pix, 4, 4, 4); -} - -internal void -ed_load_font_cb(Platform_File_Async_Job_Args result, u8* user_data) -{ - App_State* state = (App_State*)user_data; - UI* ui = &state->editor->ui; - - u8* f = result.data.base; - stbtt_fontinfo font; - if (!stbtt_InitFont(&font, f, stbtt_GetFontOffsetForIndex(f, 0))) - { - invalid_code_path; - } - - r32 scale = stbtt_ScaleForPixelHeight(&font, 18.0f); - s32 ascent, descent, line_gap; - stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap); - ui->font_ascent = (r32)ascent * scale; - ui->font_descent = (r32)descent * scale; - ui->font_line_gap = (r32)line_gap * scale; - if (ui->font_line_gap == 0) ui->font_line_gap = 5; - - String c = lit_str("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-=_+[]{}\\|;:'\",<.>/?`~"); - for (u64 i = 0; i < c.len; i++) - { - s32 w, h, xoff, yoff; - u32 id = (u32)c.str[i]; - u8* bp = stbtt_GetCodepointBitmap(&font, 0, scale, (char)c.str[i], &w, &h, &xoff, &yoff); - s32 x0, y0, x1, y1; - stbtt_GetCodepointBitmapBoxSubpixel(&font, (char)c.str[i], scale, scale, 0, 0, &x0, &y0, &x1, &y1); - - v2 offset = v2{ 0, (r32)y0 }; - texture_atlas_register(&state->editor->ui.atlas, bp, (u32)w, (u32)h, id, offset, TextureAtlasRegistration_PixelFormat_Alpha); - stbtt_FreeBitmap(bp, 0); - } - - Texture_Atlas_Sprite m_sprite = texture_atlas_sprite_get(&state->editor->ui.atlas, (u32)'m'); - ui->font_space_width = (r32)(m_sprite.max_x - m_sprite.min_x); - - platform_texture_update(ui->atlas_texture, ui->atlas.pixels, 1024, 1024, 1024); -} - -internal void -ed_draw_panel(u8* user_data, BSP_Node_Id id, BSP_Node node, BSP_Area area) -{ - App_State* state = (App_State*)user_data; - UI* ui = &state->editor->ui; - scratch_get(scratch); - - UI_Layout title_layout = {}; - ui_layout_set_row_info(ui, &title_layout); - title_layout.bounds_min = v2{ area.min.x, area.max.y - title_layout.row_height }; - title_layout.bounds_max = area.max; - title_layout.at = title_layout.bounds_min; - - UI_Layout panel_layout = {}; - ui_layout_set_row_info(ui, &panel_layout); - panel_layout.bounds_min = area.min; - panel_layout.bounds_max = v2{ area.max.x, title_layout.bounds_max.y }; - panel_layout.at = panel_layout.bounds_min; - - ui_layout_push(ui, &panel_layout); - - String title = {}; - switch (node.user_data) - { - case 0: - { - title = lit_str("None"); - } break; - - case 1: - { - ed_sculpture_visualizer(state); - title = lit_str("Sculpture"); - } break; - - invalid_default_case; - } - ui_layout_pop(ui); - - ui_layout_push(ui, &title_layout); - UI_Widget_Desc bg = {}; - bg.style.flags = UIWidgetStyle_Bg; - bg.style.color_bg = v4{.4f,.4f,.4f,1}; - bg.style.sprite = WHITE_SPRITE_ID; - bg.string = string_f(scratch.a, "%.*s_%u_title_bg", str_varg(title), id.value); - bg.p_min = title_layout.bounds_min; - bg.p_max = title_layout.bounds_max; - UI_Widget_Result r = ui_widget_push(ui, bg); - ui_layout_row_begin(&title_layout, 4); - { - ui_text(ui, title, BLACK_V4); - } - ui_layout_row_end(&title_layout); - ui_widget_pop(ui, r.id); - ui_layout_pop(ui); -} - -internal void -ed_init(App_State* state) -{ - Editor* editor = allocator_alloc_struct(permanent, Editor); - state->editor = editor; - editor->ui = ui_create(4096, 4096, state->input_state, permanent); - editor->ui.draw_panel_cb = ed_draw_panel; - editor->ui.draw_panel_cb_data = (u8*)state; - //bsp_split(&editor->ui.panels, editor->ui.panels.root, 700, BSPSplit_YAxis, 0, 1); - - // make the default quad for us to draw with - // TODO(PS): this might be unnecessary with the per-frame buffer we use now - make_quad(&editor->renderer.geo, &editor->renderer.shd, &editor->renderer.tex); - - platform_file_async_read(lit_str("data/font.ttf"), ed_load_font_cb); - - ed_sculpture_visualizer_init(state); -} - -internal u8* -ed_leds_to_texture(App_State* state, Allocator_Scratch* scratch, u32 pixels_dim) -{ - u32 at = 0; - u32* pixels = allocator_alloc_array(scratch->a, u32, pixels_dim * pixels_dim); - for (u32 a = 0; a < state->assemblies.len; a++) - { - Assembly_Pixel_Buffer leds = state->assemblies.pixel_buffers[a]; - for (u32 p = 0; p < leds.len; p++) - { - Assembly_Pixel led = leds.pixels[p]; - u32 index = at++; - pixels[index] = ( - led.r << 0 | - led.g << 8 | - led.b << 16 | - 0xFF << 24 - ); - } - } -#if 0 - for (u32 y = 0; y < pixels_dim; y++) - { - for (u32 x = 0; x < pixels_dim; x++) - { - r32 rp = (r32)y / (r32)pixels_dim; - r32 bp = (r32)x / (r32)pixels_dim; - u8 rb = (u8)(255 * rp); - u8 bb = (u8)(255 * bp); - u32 c = ( - 0xFF0000FF | - (rb << 8) | - (bb << 16) - ); - pixels[(y * pixels_dim) + x] = c; - } - } -#endif - return (u8*)pixels; -} - -internal void -ed_sculpture_updated(App_State* state, r32 scale, r32 led_size) -{ - Editor* ed = state->editor; - if (!ed) return; - - scratch_get(scratch); - - // NOTE(PS): we need to know the total number of leds in order to give them - // texture coordinates - u32 leds_count = 0; - for (u32 a = 0; a < state->assemblies.len; a++) - { - Assembly_Pixel_Buffer pixels = state->assemblies.pixel_buffers[a]; - leds_count += pixels.len; - } - - // round up to a texture whose sides are powers of two - u32 pixels_dim = (u32)floorf(sqrtf((r32)leds_count)); - pixels_dim = round_up_to_pow2(pixels_dim); - u32 pixels_count = pixels_dim * pixels_dim; - r32 texel_dim = 1 / (r32)pixels_dim; - - // NOTE(PS): Rebuild the sculpture geometry to point to the new - // sculpture. - Geo_Vertex_Buffer_Storage storage = ( - GeoVertexBufferStorage_Position | - GeoVertexBufferStorage_TexCoord - ); - u32 verts_cap = leds_count * 4; - u32 indices_cap = leds_count * 6; - Geo_Quad_Buffer_Builder geo = geo_quad_buffer_builder_create(scratch.a, verts_cap, storage, indices_cap); - r32 r = led_size; - - u32 pixels_created = 0; - for (u32 a = 0; a < state->assemblies.len; a++) - { - Assembly_Pixel_Buffer pixels = state->assemblies.pixel_buffers[a]; - for (u32 p = 0; p < pixels.len; p++) - { - v3 c = pixels.positions[p].xyz; - c *= scale; - - u32 pixel_count = pixels_created++; - u32 pixel_x = pixel_count % pixels_dim; - u32 pixel_y = pixel_count / pixels_dim; - r32 texel_x_min = (r32)pixel_x / (r32)pixels_dim; - r32 texel_y_min = (r32)pixel_y / (r32)pixels_dim; - r32 texel_x_max = texel_x_min + texel_dim; - r32 texel_y_max = texel_y_min + texel_dim; - - v2 t0 = v2{texel_x_min, texel_y_min}; - v2 t1 = v2{texel_x_max, texel_y_min}; - v2 t2 = v2{texel_x_max, texel_y_max}; - v2 t3 = v2{texel_x_min, texel_y_max}; - - v3 p0 = c + v3{ -r, -r, 0 }; - v3 p1 = c + v3{ r, -r, 0 }; - v3 p2 = c + v3{ r, r, 0 }; - v3 p3 = c + v3{ -r, r, 0 }; - geo_quad_buffer_builder_push(&geo, p0, p1, p2, p3, t0, t1, t2, t3); - } - } - - if (ed->sculpture_geo.indices_len != 0) - { - invalid_code_path; - // TODO(PS): destroy the old geometry buffer or update it - } - ed->sculpture_geo = platform_geometry_buffer_create( - geo.buffer_vertex.values, - geo.buffer_vertex.len, - geo.buffer_index.values, - geo.buffer_index.len - ); - - platform_vertex_attrib_pointer( - ed->sculpture_geo, ed->sculpture_shd, 3, ed->sculpture_shd.attrs[0], 5, 0 - ); - platform_vertex_attrib_pointer( - ed->sculpture_geo, ed->sculpture_shd, 2, ed->sculpture_shd.attrs[1], 5, 3 - ); - - // TODO(PS): make this have enough pixels for the sculpture - // TODO(PS): map leds to pixels - - if (ed->sculpture_tex.w != 0) - { - invalid_code_path; - // TODO(PS): destroy the old texture - } - - u8* pixels = ed_leds_to_texture(state, &scratch, pixels_dim); - ed->sculpture_tex = platform_texture_create(pixels, pixels_dim, pixels_dim, pixels_dim); -} - -internal void -ed_frame_prepare(App_State* state) -{ - ui_frame_prepare(&state->editor->ui, state->editor->window_dim); -} - -internal void -ed_frame(App_State* state) -{ - Editor* ed = state->editor; - UI* ui = &ed->ui; - - { - scratch_get(scratch); - u32 w = ed->sculpture_tex.w; - u8* pixels = ed_leds_to_texture(state, &scratch, w); - platform_texture_update(ed->sculpture_tex, pixels, w, w, w); - } - - edr_render_begin(state); - ui_draw(&state->editor->ui); - edr_render(state); -} - -internal void -ed_cleanup(App_State* state) -{ - -} - diff --git a/src_v2/editor/lumenarium_editor.h b/src_v2/editor/lumenarium_editor.h index b623912..f275a99 100644 --- a/src_v2/editor/lumenarium_editor.h +++ b/src_v2/editor/lumenarium_editor.h @@ -3,21 +3,23 @@ #ifndef LUMENARIUM_EDITOR_H #define LUMENARIUM_EDITOR_H +typedef struct Editor Editor; struct Editor { + v2 content_scale; v2 window_dim; Editor_Renderer renderer; UI ui; v3 camera_pos; - Platform_Geometry_Buffer sculpture_geo; - Platform_Shader sculpture_shd; - Platform_Texture sculpture_tex; + Geometry_Buffer sculpture_geo; + Shader sculpture_shd; + Texture sculpture_tex; }; // NOTE(PS): call this any time sculpture data is updated if // you want to see the sculpture in the visualizer -internal void ed_sculpture_updated(App_State* state); +internal void ed_sculpture_updated(App_State* state, r32 scale, r32 led_size); #endif //LUMENARIUM_EDITOR_H diff --git a/src_v2/editor/lumenarium_editor_renderer.c b/src_v2/editor/lumenarium_editor_renderer.c new file mode 100644 index 0000000..fa1714b --- /dev/null +++ b/src_v2/editor/lumenarium_editor_renderer.c @@ -0,0 +1,18 @@ + +internal void +edr_render_begin(App_State* state) +{ + Graphics_Frame_Desc desc = {}; + desc.clear_color = (v4){ 0.1f, 0.1f, 0.1f, 1 }; + desc.viewport_min = (v2){ 0, 0 }; + v2 wd = state->editor->window_dim; + v2 cs = state->editor->content_scale; + desc.viewport_max = HMM_MultiplyVec2(wd, cs); + frame_begin(desc); + frame_clear(); +} + +internal void +edr_render(App_State* state) +{ +} \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_renderer.cpp b/src_v2/editor/lumenarium_editor_renderer.cpp deleted file mode 100644 index 67d9e34..0000000 --- a/src_v2/editor/lumenarium_editor_renderer.cpp +++ /dev/null @@ -1,22 +0,0 @@ - -internal void -edr_render_begin(App_State* state) -{ - Platform_Graphics_Frame_Desc desc = {}; - desc.clear_color = { 0.1f, 0.1f, 0.1f, 1 }; - desc.viewport_min = { 0, 0 }; - desc.viewport_max = state->editor->window_dim; - platform_frame_begin(desc); - platform_frame_clear(); -} - -internal void -edr_render(App_State* state) -{ -#if 0 - platform_geometry_bind(state->editor->renderer.geo); - platform_texture_bind(state->editor->ui.atlas_texture); - platform_shader_bind(state->editor->renderer.shd); - platform_geometry_draw(state->editor->renderer.geo); -#endif -} \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_renderer.h b/src_v2/editor/lumenarium_editor_renderer.h index bebff8e..4129499 100644 --- a/src_v2/editor/lumenarium_editor_renderer.h +++ b/src_v2/editor/lumenarium_editor_renderer.h @@ -3,11 +3,9 @@ #ifndef LUMENARIUM_EDITOR_RENDERER_H #define LUMENARIUM_EDITOR_RENDERER_H +typedef struct Editor_Renderer Editor_Renderer; struct Editor_Renderer { - Platform_Shader shd; - Platform_Geometry_Buffer geo; - Platform_Texture tex; }; #endif //LUMENARIUM_EDITOR_RENDERER_H diff --git a/src_v2/editor/lumenarium_editor_sculpture_visualizer.c b/src_v2/editor/lumenarium_editor_sculpture_visualizer.c new file mode 100644 index 0000000..b70efda --- /dev/null +++ b/src_v2/editor/lumenarium_editor_sculpture_visualizer.c @@ -0,0 +1,60 @@ +#include "lumenarium_editor_sculpture_visualizer_shaders.h" + +internal void +ed_sculpture_visualizer_init(App_State* state) +{ + Editor* editor = state->editor; + + String vert = xplatform_shader_program_get_vert(sculpture_shd); + String frag = xplatform_shader_program_get_frag(sculpture_shd); + + String attrs[] = { lit_str("a_pos"), lit_str("a_uv") }; + String uniforms[] = { lit_str("proj") }; + editor->sculpture_shd = shader_create(vert, frag, attrs, 2, uniforms, 1); +} + +r32 cam_theta = 0; + +internal void +ed_sculpture_visualizer(App_State* state) +{ + Editor* ed = state->editor; + + // Set the viewport to the current layout's region so that the sculpture + // never overlaps any other ui elements + UI_Layout l = *ed->ui.layout; + v2 view_dim = HMM_SubtractVec2(l.bounds_max, l.bounds_min); + v2 view_min = l.bounds_min; + v2 view_max = l.bounds_max; + v2 view_min_scaled = HMM_MultiplyVec2(view_min, ed->content_scale); + v2 view_dim_scaled = HMM_MultiplyVec2(view_dim, ed->content_scale); + glViewport( + (s32)view_min_scaled.x, + (s32)view_min_scaled.y, + (u32)view_dim_scaled.x, + (u32)view_dim_scaled.y + ); + + // TODO(PS): TEMPORARY CAMERA CODE + cam_theta += 0.01f; + r32 cam_r = 100; + v3 camera_pos = (v3){sinf(cam_theta) * cam_r, 25, cosf(cam_theta) * cam_r}; + r32 aspect = view_dim.x / view_dim.y; + m44 proj = HMM_Perspective(45.0, aspect, 0.01f, 500); + m44 view = HMM_LookAt(camera_pos, (v3){0,0,0}, (v3){0,1,0}); + + shader_bind(ed->sculpture_shd); + set_uniform(ed->sculpture_shd, 0, HMM_MultiplyMat4(proj, view)); + texture_bind(ed->sculpture_tex); + geometry_bind(ed->sculpture_geo); + + u32 i = 1008; + u32 j = 2868; + u32 k = ed->sculpture_geo.indices_len; + u32 h = (i * 6) + 3; + geometry_drawi(ed->sculpture_geo, k); + + // reset the viewport for all other rendering + v2 wds = HMM_MultiplyVec2(ed->window_dim, ed->content_scale); + glViewport(0, 0, (s32)wds.x, (s32)wds.y); +} \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_sculpture_visualizer.cpp b/src_v2/editor/lumenarium_editor_sculpture_visualizer.cpp deleted file mode 100644 index 40e86ac..0000000 --- a/src_v2/editor/lumenarium_editor_sculpture_visualizer.cpp +++ /dev/null @@ -1,100 +0,0 @@ - -static String sculpture_shd_vert_win32 = lit_str( - "#version 330 core\n" - "layout (location = 0) in vec3 a_pos;\n" - "layout (location = 1) in vec2 a_uv;\n" - "out vec2 uv;\n" - "uniform mat4 proj;\n" - "void main(void) {\n" - " gl_Position = proj * vec4(a_pos, 1.0);\n" - " uv = a_uv;\n" - "}" - ); - -static String sculpture_shd_vert_wasm = lit_str( - "precision highp float;\n" - "attribute vec3 a_pos;\n" - "attribute vec2 a_uv;\n" - "varying vec2 uv;\n" - "uniform mat4 proj;\n" - "void main(void) {\n" - " gl_Position = proj * vec4(a_pos, 1.0);\n" - " uv = a_uv;\n" - "}" - ); - -static String sculpture_shd_frag_win32 = lit_str( - "#version 330 core\n" - "in vec2 uv;\n" - "out vec4 FragColor;\n" - "uniform sampler2D texture;\n" - "void main(void) {\n" - " FragColor = texture(texture, uv);\n" - "}" - ); - -static String sculpture_shd_frag_wasm = lit_str( - "precision highp float;\n" - "varying vec2 uv;\n" - "uniform sampler2D texture;\n" - "void main(void) {\n" - " //gl_FragColor = texture2D(texture, uv) * color;\n" - " gl_FragColor = vec4(uv.x,1,uv.y,1);\n" - "}" - ); - -internal void -ed_sculpture_visualizer_init(App_State* state) -{ - Editor* editor = state->editor; - - -#if defined(PLATFORM_win32) - String vert = sculpture_shd_vert_win32; - String frag = sculpture_shd_frag_win32; -#elif defined(PLATFORM_wasm) - String vert = sculpture_shd_vert_wasm; - String frag = sculpture_shd_frag_wasm; -#endif - - String attrs[] = { lit_str("a_pos"), lit_str("a_uv") }; - String uniforms[] = { lit_str("proj") }; - editor->sculpture_shd = platform_shader_create( - vert, frag, attrs, 2, uniforms, 1 - ); -} - -r32 cam_theta = 0; - -internal void -ed_sculpture_visualizer(App_State* state) -{ - Editor* ed = state->editor; - - // Set the viewport to the current layout's region so that the sculpture - // never overlaps any other ui elements - UI_Layout l = *ed->ui.layout; - v2 view_dim = l.bounds_max - l.bounds_min; - glViewport( - (s32)l.bounds_min.x, - (s32)l.bounds_min.y, - (s32)view_dim.x, - (s32)view_dim.y - ); - - // TODO(PS): TEMPORARY CAMERA CODE - //cam_theta += 0.05f; - v3 camera_pos = v3{sinf(cam_theta) * 50, 0, cosf(cam_theta) * 50}; - r32 aspect = view_dim.x / view_dim.y; - m44 proj = HMM_Perspective(45.0, aspect, 0.01f, 500); - m44 view = HMM_LookAt(camera_pos, v3{0,0,0}, v3{0,1,0}); - - platform_shader_bind(ed->sculpture_shd); - platform_set_uniform(ed->sculpture_shd, 0, proj * view); - platform_texture_bind(ed->sculpture_tex); - platform_geometry_bind(ed->sculpture_geo); - platform_geometry_draw(ed->sculpture_geo, ed->sculpture_geo.indices_len); - - // reset the viewport for all other rendering - glViewport(0, 0, (s32)ed->window_dim.x, (s32)ed->window_dim.y); -} \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_sculpture_visualizer_shaders.h b/src_v2/editor/lumenarium_editor_sculpture_visualizer_shaders.h new file mode 100644 index 0000000..07444ca --- /dev/null +++ b/src_v2/editor/lumenarium_editor_sculpture_visualizer_shaders.h @@ -0,0 +1,65 @@ + +global XPlatform_Shader_Program_Src sculpture_shd = { + .win32_vert = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec3 a_pos;\n" + "layout (location = 1) in vec2 a_uv;\n" + "out vec2 uv;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + "}" + ), + .win32_frag = lit_str( + "#version 330 core\n" + "in vec2 uv;\n" + "out vec4 FragColor;\n" + "uniform sampler2D tex;\n" + "void main(void) {\n" + " FragColor = texture(tex, uv);\n" + "}" + ), + + .osx_vert = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec3 a_pos;\n" + "layout (location = 1) in vec2 a_uv;\n" + "out vec2 uv;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + "}" + ), + .osx_frag = lit_str( + "#version 330 core\n" + "in vec2 uv;\n" + "out vec4 FragColor;\n" + "uniform sampler2D tex;\n" + "void main(void) {\n" + " FragColor = texture(tex, uv);\n" + "}" + ), + + .wasm_vert = lit_str( + "precision highp float;\n" + "attribute vec3 a_pos;\n" + "attribute vec2 a_uv;\n" + "varying vec2 uv;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + "}" + ), + .wasm_frag = lit_str( + "precision highp float;\n" + "varying vec2 uv;\n" + "uniform sampler2D tex;\n" + "void main(void) {\n" + " //gl_FragColor = texture2D(tex, uv) * color;\n" + " gl_FragColor = vec4(uv.x,1,uv.y,1);\n" + "}" + ), +}; \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_ui.cpp b/src_v2/editor/lumenarium_editor_ui.c similarity index 66% rename from src_v2/editor/lumenarium_editor_ui.cpp rename to src_v2/editor/lumenarium_editor_ui.c index 44bccc5..894c11e 100644 --- a/src_v2/editor/lumenarium_editor_ui.cpp +++ b/src_v2/editor/lumenarium_editor_ui.c @@ -1,55 +1,6 @@ #define WHITE_SPRITE_ID 511 -static String ui_shader_vert_win32 = lit_str( - "#version 330 core\n" - "layout (location = 0) in vec3 a_pos;\n" - "layout (location = 1) in vec2 a_uv;\n" - "layout (location = 2) in vec4 a_color;\n" - "out vec2 uv;\n" - "out vec4 color;\n" - "uniform mat4 proj;\n" - "void main(void) {\n" - " gl_Position = proj * vec4(a_pos, 1.0);\n" - " uv = a_uv;\n" - " color = a_color;\n" - "}" - ); - -static String ui_shader_vert_wasm = lit_str( - "precision highp float;\n" - "attribute vec3 a_pos;\n" - "attribute vec2 a_uv;\n" - "attribute vec4 a_color;\n" - "varying vec2 uv;\n" - "varying vec4 color;\n" - "uniform mat4 proj;\n" - "void main(void) {\n" - " gl_Position = proj * vec4(a_pos, 1.0);\n" - " uv = a_uv;\n" - " color = a_color;\n" - "}" - ); - -static String ui_shader_frag_win32 = lit_str( - "#version 330 core\n" - "in vec2 uv;\n" - "in vec4 color;\n" - "out vec4 FragColor;\n" - "uniform sampler2D texture;\n" - "void main(void) {\n" - " FragColor = texture(texture, uv) * color;\n" - "}" - ); - -static String ui_shader_frag_wasm = lit_str( - "precision highp float;\n" - "varying vec2 uv;\n" - "varying vec4 color;\n" - "uniform sampler2D texture;\n" - "void main(void) {\n" - " gl_FragColor = texture2D(texture, uv) * color;\n" - "}" - ); +#include "lumenarium_editor_ui_shaders.h" internal UI ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) @@ -66,7 +17,7 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) result.widgets.states_hash = allocator_alloc_array(a, u32, result.widgets.states_cap); result.panels = bsp_create(a, 32); - result.panels.root = bsp_push(&result.panels, {0}, {v2{},v2{1400, 800}}, 1); + result.panels.root = bsp_push(&result.panels, (BSP_Node_Id){0}, (BSP_Area){(v2){},(v2){1400, 800}}, 1); // Per Frame Vertex Buffer Geo_Vertex_Buffer_Storage storage = ( @@ -76,40 +27,27 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) ); result.geo = geo_quad_buffer_builder_create(a, verts_cap, storage, verts_cap * 2); - result.per_frame_buffer = platform_geometry_buffer_create( - result.geo.buffer_vertex.values, - result.geo.buffer_vertex.cap, - result.geo.buffer_index.values, - result.geo.buffer_index.cap - ); + result.per_frame_buffer = geometry_buffer_create( + result.geo.buffer_vertex.values, + result.geo.buffer_vertex.cap, + result.geo.buffer_index.values, + result.geo.buffer_index.cap + ); -#if defined(PLATFORM_win32) - String vert = ui_shader_vert_win32; - String frag = ui_shader_frag_win32; -#elif defined(PLATFORM_wasm) - String vert = ui_shader_vert_wasm; - String frag = ui_shader_frag_wasm; -#endif + String vert = xplatform_shader_program_get_vert(ui_shader); + String frag = xplatform_shader_program_get_frag(ui_shader); String attrs[] = { lit_str("a_pos"), lit_str("a_uv"), lit_str("a_color") }; String uniforms[] = { lit_str("proj") }; - result.shader = platform_shader_create( - vert, frag, attrs, 3, uniforms, 1 - ); + result.shader = shader_create(vert, frag, attrs, 3, uniforms, 1); - platform_vertex_attrib_pointer( - result.per_frame_buffer, result.shader, 3, result.shader.attrs[0], 9, 0 - ); - platform_vertex_attrib_pointer( - result.per_frame_buffer, result.shader, 2, result.shader.attrs[1], 9, 3 - ); - platform_vertex_attrib_pointer( - result.per_frame_buffer, result.shader, 4, result.shader.attrs[2], 9, 5 - ); + vertex_attrib_pointer(result.per_frame_buffer, result.shader, 3, result.shader.attrs[0], 9, 0); + vertex_attrib_pointer(result.per_frame_buffer, result.shader, 2, result.shader.attrs[1], 9, 3); + vertex_attrib_pointer(result.per_frame_buffer, result.shader, 4, result.shader.attrs[2], 9, 5); // Texture Atlas result.atlas = texture_atlas_create(1024, 1024, 512, permanent); - result.atlas_texture = platform_texture_create(result.atlas.pixels, 1024, 1024, 1024); + result.atlas_texture = texture_create(result.atlas.pixels, 1024, 1024, 1024); u32 white_sprite[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, @@ -127,39 +65,40 @@ internal void ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c) { v3 p0 = pmin; - v3 p1 = v3{pmax.x, pmin.y, pmin.z}; + v3 p1 = (v3){pmax.x, pmin.y, pmin.z}; v3 p2 = pmax; - v3 p3 = v3{pmin.x, pmax.y, pmin.z}; + v3 p3 = (v3){pmin.x, pmax.y, pmin.z}; v2 t0 = tmin; - v2 t1 = v2{tmax.x,tmin.y}; + v2 t1 = (v2){tmax.x,tmin.y}; v2 t2 = tmax; - v2 t3 = v2{tmin.x,tmax.y}; - geo_quad_buffer_builder_push(&ui->geo, p0, p1, p2, p3, t0, t1, t2, t3, c); + v2 t3 = (v2){tmin.x,tmax.y}; + geo_quad_buffer_builder_push_vtc(&ui->geo, p0, p1, p2, p3, t0, t1, t2, t3, c); } internal void ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id) { - texture_atlas_register(&ui->atlas, pixels, w, h, id, v2{0,0}, TextureAtlasRegistration_PixelFormat_RGBA); - platform_texture_update(ui->atlas_texture, ui->atlas.pixels, ui->atlas.width, ui->atlas.height, ui->atlas.width); + texture_atlas_register(&ui->atlas, pixels, w, h, id, (v2){0,0}, TextureAtlasRegistration_PixelFormat_RGBA); + texture_update(ui->atlas_texture, ui->atlas.pixels, ui->atlas.width, ui->atlas.height, ui->atlas.width); } internal void -ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color) +ui_sprite_push_color(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color) { Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(&ui->atlas, id); v4 uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite); - pmin.XY += sprite.draw_offset; - pmax.XY += sprite.draw_offset; + pmin.XY = HMM_AddVec2(pmin.XY, sprite.draw_offset); + pmax.XY = HMM_AddVec2(pmax.XY, sprite.draw_offset); ui_quad_push(ui, pmin, pmax, uv.xy, uv.zw, color); } internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id) { - ui_sprite_push(ui, pmin, pmax, id, v4{1,1,1,1}); + ui_sprite_push_color(ui, pmin, pmax, id, (v4){1,1,1,1}); } +typedef struct UI_Char_Draw_Cmd UI_Char_Draw_Cmd; struct UI_Char_Draw_Cmd { v4 uv; @@ -177,17 +116,17 @@ ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint) Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(&ui->atlas, codepoint); result.uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite); - v3 dim = v3{ + v3 dim = (v3){ (r32)(sprite.max_x - sprite.min_x), (r32)(sprite.max_y - sprite.min_y), 0, }; result.pmin = at; - result.pmin.XY += sprite.draw_offset; + result.pmin.XY = HMM_AddVec2(result.pmin.XY, sprite.draw_offset); result.pmin.XY = v2_floor(result.pmin.XY); - result.pmax = result.pmin + dim; + result.pmax = HMM_AddVec3(result.pmin, dim); - result.baseline_after = v3{ result.pmax.x, at.y, at.z }; + result.baseline_after = (v3){ result.pmax.x, at.y, at.z }; return result; } @@ -204,15 +143,15 @@ ui_frame_prepare(UI* ui, v2 window_dim) ui->widgets.active_parent = ui->widgets.root; BSP_Node* panel_root = bsp_get(&ui->panels, ui->panels.root); - if (window_dim.x != 0 && window_dim.y != 0 && window_dim != panel_root->area.max) + if (window_dim.x != 0 && window_dim.y != 0 && !HMM_EqualsVec2(window_dim, panel_root->area.max)) { BSP_Area area = {}; - area.min = v2{0,0}; + area.min = (v2){0,0}; area.max = window_dim; bsp_node_area_update(&ui->panels, ui->panels.root, area); } - v2 half_d = window_dim * 0.5f; + v2 half_d = HMM_MultiplyVec2f(window_dim, 0.5f); ui->proj = HMM_Orthographic(0, window_dim.x, window_dim.y, 0, 0.01f, 100); if (ui->widget_next_hot.value != 0) @@ -237,16 +176,16 @@ ui_draw_panel(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data) BSP_Area area = node->area; if (ui->draw_panel_cb) ui->draw_panel_cb(ui->draw_panel_cb_data, id, *node, area); - + r32 z = -1; - v3 l0p0 = v3{ area.min.x, area.min.y, z }; // left side - v3 l0p1 = v3{ area.min.x + 1, area.max.y, z }; - v3 l1p0 = v3{ area.max.x - 1, area.min.y, z }; // right side - v3 l1p1 = v3{ area.max.x, area.max.y, z }; - v3 l2p0 = v3{ area.min.x, area.min.y , z }; // bottom side - v3 l2p1 = v3{ area.max.x, area.min.y + 1, z }; - v3 l3p0 = v3{ area.min.x, area.max.y , z }; // top side - v3 l3p1 = v3{ area.max.x, area.max.y + 1, z }; + v3 l0p0 = (v3){ area.min.x, area.min.y, z }; // left side + v3 l0p1 = (v3){ area.min.x + 1, area.max.y, z }; + v3 l1p0 = (v3){ area.max.x - 1, area.min.y, z }; // right side + v3 l1p1 = (v3){ area.max.x, area.max.y, z }; + v3 l2p0 = (v3){ area.min.x, area.min.y , z }; // bottom side + v3 l2p1 = (v3){ area.max.x, area.min.y + 1, z }; + v3 l3p0 = (v3){ area.min.x, area.max.y , z }; // top side + v3 l3p1 = (v3){ area.max.x, area.max.y + 1, z }; u32 sid = WHITE_SPRITE_ID; v4 c = WHITE_V4; if (rect2_contains(area.min, area.max, ui->input->frame_hot->mouse_pos)) @@ -254,37 +193,39 @@ ui_draw_panel(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data) c = PINK_V4; } - ui_sprite_push(ui, l0p0, l0p1, sid, c); - ui_sprite_push(ui, l1p0, l1p1, sid, c); - ui_sprite_push(ui, l2p0, l2p1, sid, c); - ui_sprite_push(ui, l3p0, l3p1, sid, c); + #if 0 + ui_sprite_push_color(ui, l0p0, l0p1, sid, c); + ui_sprite_push_color(ui, l1p0, l1p1, sid, c); + ui_sprite_push_color(ui, l2p0, l2p1, sid, c); + ui_sprite_push_color(ui, l3p0, l3p1, sid, c); + #endif } internal void ui_draw(UI* ui) { bsp_walk_inorder(&ui->panels, ui->panels.root, ui_draw_panel, (u8*)ui); - + u32 widget_count = ui->widgets.free_len; r32 range_min = -10; r32 range_max = -1; r32 range_step = (range_max - range_min) / (r32)(widget_count * 4); ui_widgets_to_geometry_recursive(ui, ui->widgets.root, -10, range_step); - platform_geometry_buffer_update( - &ui->per_frame_buffer, - (r32*)ui->geo.buffer_vertex.values, - 0, - ui->geo.buffer_vertex.len * ui->geo.buffer_vertex.stride, - ui->geo.buffer_index.values, - 0, - ui->geo.buffer_index.len - ); - platform_shader_bind(ui->shader); - platform_set_uniform(ui->shader, 0, ui->proj); - platform_texture_bind(ui->atlas_texture); - platform_geometry_bind(ui->per_frame_buffer); - platform_geometry_draw(ui->per_frame_buffer, ui->geo.buffer_index.len); + geometry_buffer_update( + &ui->per_frame_buffer, + (r32*)ui->geo.buffer_vertex.values, + 0, + ui->geo.buffer_vertex.len * ui->geo.buffer_vertex.stride, + ui->geo.buffer_index.values, + 0, + ui->geo.buffer_index.len + ); + shader_bind(ui->shader); + set_uniform(ui->shader, 0, ui->proj); + texture_bind(ui->atlas_texture); + geometry_bind(ui->per_frame_buffer); + geometry_drawi(ui->per_frame_buffer, ui->geo.buffer_index.len); } //////////////////////////////////////////// @@ -296,13 +237,13 @@ ui_widget_id_create(String string, u32 index) assert(string.len != 0 && string.str != 0); UI_Widget_Id result = {}; zero_struct(result); - result.value = hash_djb2_to_u32(string); + result.value = hash_djb2_string_to_u32(string); result.index = index; return result; } internal UI_Widget_State* -ui_widget_state_get(UI_Widget_Pool* pool, UI_Widget_Id id) +ui_widget_pool_state_get(UI_Widget_Pool* pool, UI_Widget_Id id) { u32 index = hash_table_find(pool->states_hash, pool->states_cap, id.value); assert(index != pool->states_cap); @@ -312,7 +253,7 @@ ui_widget_state_get(UI_Widget_Pool* pool, UI_Widget_Id id) internal UI_Widget_State* ui_widget_state_get(UI* ui, UI_Widget_Id id) { - return ui_widget_state_get(&ui->widgets, id); + return ui_widget_pool_state_get(&ui->widgets, id); } internal UI_Widget* @@ -377,7 +318,7 @@ ui_widget_next_hot_set(UI* ui, UI_Widget* w) if (w) { ui->widget_next_hot = w->id; } else { - ui->widget_next_hot = UI_Widget_Id{0}; + ui->widget_next_hot = (UI_Widget_Id){0}; } ui->widget_next_hot_frames = 0; } @@ -388,7 +329,7 @@ ui_widget_hot_set(UI* ui, UI_Widget* w) if (w) { ui->widget_hot = w->id; } else { - ui->widget_hot = UI_Widget_Id{0}; + ui->widget_hot = (UI_Widget_Id){0}; } ui->widget_hot_frames = 0; } @@ -398,7 +339,7 @@ ui_widget_push(UI* ui, UI_Widget_Desc desc) { UI_Widget_Result result = {}; zero_struct(result); - v2 dim = desc.p_max - desc.p_min; + v2 dim = HMM_SubtractVec2(desc.p_max, desc.p_min); if (dim.x == 0 || dim.y == 0) return result; UI_Widget* w = ui_widget_pool_push(&ui->widgets, desc.string); @@ -410,9 +351,9 @@ ui_widget_push(UI* ui, UI_Widget_Desc desc) v2 mouse_p = ui->input->frame_hot->mouse_pos; bool mouse_over = ( - mouse_p.x >= desc.p_min.x && mouse_p.x <= desc.p_max.x && - mouse_p.y >= desc.p_min.y && mouse_p.y <= desc.p_max.y - ); + mouse_p.x >= desc.p_min.x && mouse_p.x <= desc.p_max.x && + mouse_p.y >= desc.p_min.y && mouse_p.y <= desc.p_max.y + ); UI_Widget_Style_Flags flags = desc.style.flags; UI_Widget_Style_Flags mask_drag = (UIWidgetStyle_MouseDragH | UIWidgetStyle_MouseDragV); @@ -473,11 +414,12 @@ ui_widget_push(UI* ui, UI_Widget_Desc desc) has_flag(flags, UIWidgetStyle_MouseDragH) ? 1.0f : 0.0f, has_flag(flags, UIWidgetStyle_MouseDragV) ? 1.0f : 0.0f }; - v2 drag = ui->input->frame_hot->mouse_pos - w->desc.p_min; - drag = v2{ clamp(0, drag.x, w->desc.p_max.x), clamp(0, drag.y, w->desc.p_max.y) }; - drag *= drag_pct_mask; - v2 drag_pct = drag / dim; - drag_pct = v2{ clamp(0, drag_pct.x, 1), clamp(0, drag_pct.y, 1) }; + v2 mp = ui->input->frame_hot->mouse_pos; + v2 drag = HMM_SubtractVec2(mp, w->desc.p_min); + drag = (v2){ clamp(0, drag.x, w->desc.p_max.x), clamp(0, drag.y, w->desc.p_max.y) }; + drag = HMM_MultiplyVec2(drag, drag_pct_mask); + v2 drag_pct = HMM_DivideVec2(drag, dim); + drag_pct = (v2){ clamp(0, drag_pct.x, 1), clamp(0, drag_pct.y, 1) }; result.drag = drag_pct; state->scroll = drag_pct; @@ -520,17 +462,17 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s if (has_flag(child->desc.style.flags, UIWidgetStyle_Outline)) { - ui_sprite_push(ui, bg_min, bg_max, WHITE_SPRITE_ID, color_fg); + ui_sprite_push_color(ui, bg_min, bg_max, WHITE_SPRITE_ID, color_fg); z_at += z_step; - bg_min += v3{ 1, 1, 0}; - bg_max -= v3{ 1, 1, 0}; + bg_min = HMM_AddVec3(bg_min, (v3){ 1, 1, 0}); + bg_max = HMM_SubtractVec3(bg_max, (v3){ 1, 1, 0}); } if (has_flag(child->desc.style.flags, UIWidgetStyle_Bg)) { bg_min.z = z_at; bg_max.z = z_at; - ui_sprite_push(ui, bg_min, bg_max, desc.style.sprite, color_bg); + ui_sprite_push_color(ui, bg_min, bg_max, desc.style.sprite, color_bg); z_at += z_step; } @@ -546,13 +488,13 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s if (has_flag(child->desc.style.flags, UIWidgetStyle_LineInsteadOfFill)) { - fill_min = v3{ fill_x, bg_min.y, z_at }; - fill_max = v3{ fill_x + 1, bg_max.y, z_at }; + fill_min = (v3){ fill_x, bg_min.y, z_at }; + fill_max = (v3){ fill_x + 1, bg_max.y, z_at }; } else { fill_min = bg_min; - fill_max = v3{ fill_x, bg_max.y, z_at }; + fill_max = (v3){ fill_x, bg_max.y, z_at }; } } else if (has_flag(child->desc.style.flags, UIWidgetStyle_FillV)) @@ -561,17 +503,17 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s if (has_flag(child->desc.style.flags, UIWidgetStyle_LineInsteadOfFill)) { - fill_min = v3{ bg_min.x, fill_y, z_at }; - fill_max = v3{ bg_max.x, fill_y + 1, z_at }; + fill_min = (v3){ bg_min.x, fill_y, z_at }; + fill_max = (v3){ bg_max.x, fill_y + 1, z_at }; } else { fill_min = bg_min; - fill_max = v3{ bg_max.x, fill_y, z_at }; + fill_max = (v3){ bg_max.x, fill_y, z_at }; } } - ui_sprite_push(ui, fill_min, fill_max, WHITE_SPRITE_ID, color_fg); + ui_sprite_push_color(ui, fill_min, fill_max, WHITE_SPRITE_ID, color_fg); z_at += z_step; } @@ -667,7 +609,7 @@ ui_layout_row_begin(UI_Layout* layout, u32 cols) layout->cols = cols; } internal void -ui_layout_row_begin(UI* ui, u32 cols) { ui_layout_row_begin(ui->layout, cols); } +ui_row_begin(UI* ui, u32 cols) { ui_layout_row_begin(ui->layout, cols); } internal void ui_layout_row_end(UI_Layout* layout) @@ -675,7 +617,7 @@ ui_layout_row_end(UI_Layout* layout) layout->mode = UILayout_Columns; } internal void -ui_layout_row_end(UI* ui) { ui_layout_row_end(ui->layout); } +ui_row_end(UI* ui) { ui_layout_row_end(ui->layout); } internal UI_Layout_Bounds ui_layout_get_next(UI_Layout* layout) @@ -693,8 +635,8 @@ ui_layout_get_next(UI_Layout* layout) case UILayout_Columns: { result.min = layout->at; - result.max = v2{ layout->bounds_max.x, layout->at.y + layout->row_height }; - layout->at = v2{ layout->bounds_min.x, result.max.y + layout->row_gap}; + result.max = (v2){ layout->bounds_max.x, layout->at.y + layout->row_height }; + layout->at = (v2){ layout->bounds_min.x, result.max.y + layout->row_gap}; } break; case UILayout_Rows: @@ -702,11 +644,11 @@ ui_layout_get_next(UI_Layout* layout) r32 col_width = (layout->bounds_max.x - layout->bounds_min.x) / layout->cols; col_width -= (layout->cols - 1) * layout->col_gap; result.min = layout->at; - result.max = v2{ layout->at.x + col_width, layout->at.y + layout->row_height }; - layout->at = v2{ result.max.x + layout->col_gap, layout->at.y }; + result.max = (v2){ layout->at.x + col_width, layout->at.y + layout->row_height }; + layout->at = (v2){ result.max.x + layout->col_gap, layout->at.y }; if (layout->at.x >= layout->bounds_max.x) { - layout->at = v2{ + layout->at = (v2){ layout->bounds_min.x, layout->at.y + layout->row_height + layout->row_gap }; @@ -725,8 +667,6 @@ ui_layout_get_next(UI_Layout* layout) return result; } -internal UI_Layout_Bounds -ui_layout_get_next(UI* ui) { return ui_layout_get_next(ui->layout); } /////////////////////////////////////////// @@ -739,32 +679,32 @@ global UI_Style_Sheet ui_default_style_sheet = {}; internal void ui_create_default_style_sheet() { - ui_default_style_sheet.styles[UIWidget_Text] = { - (UIWidgetStyle_TextWrap), v4{0,0,0,0}, WHITE_V4, WHITE_SPRITE_ID + ui_default_style_sheet.styles[UIWidget_Text] = (UI_Widget_Style){ + (UIWidgetStyle_TextWrap), (v4){0,0,0,0}, WHITE_V4, WHITE_SPRITE_ID }; - ui_default_style_sheet.styles[UIWidget_Button] = { + ui_default_style_sheet.styles[UIWidget_Button] = (UI_Widget_Style){ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseClick), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; - ui_default_style_sheet.styles[UIWidget_Toggle] = { + ui_default_style_sheet.styles[UIWidget_Toggle] = (UI_Widget_Style){ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_MouseClick), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; ui_default_style_sheet.styles[UIWidget_Menu] = ui_default_style_sheet.styles[UIWidget_Toggle]; ui_default_style_sheet.styles[UIWidget_Dropdown] = ui_default_style_sheet.styles[UIWidget_Toggle]; - ui_default_style_sheet.styles[UIWidget_HSlider] = { + ui_default_style_sheet.styles[UIWidget_HSlider] = (UI_Widget_Style){ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragH | UIWidgetStyle_FillH ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; - ui_default_style_sheet.styles[UIWidget_VSlider] = { + ui_default_style_sheet.styles[UIWidget_VSlider] = (UI_Widget_Style){ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragV | UIWidgetStyle_FillV ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; - ui_default_style_sheet.styles[UIWidget_HScroll] = { + ui_default_style_sheet.styles[UIWidget_HScroll] = (UI_Widget_Style){ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragH | UIWidgetStyle_FillH | UIWidgetStyle_LineInsteadOfFill ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; - ui_default_style_sheet.styles[UIWidget_VScroll] = { + ui_default_style_sheet.styles[UIWidget_VScroll] = (UI_Widget_Style) { (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragV | UIWidgetStyle_FillV | UIWidgetStyle_LineInsteadOfFill ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; - ui_default_style_sheet.styles[UIWidget_Window] = { + ui_default_style_sheet.styles[UIWidget_Window] = (UI_Widget_Style){ (UIWidgetStyle_TextWrap), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID }; @@ -780,7 +720,7 @@ ui_get_style(UI* ui, UI_Widget_Kind kind) internal UI_Widget_Desc ui_layout_next_widget(UI* ui, UI_Widget_Kind kind) { - UI_Layout_Bounds b = ui_layout_get_next(ui); + UI_Layout_Bounds b = ui_layout_get_next(ui->layout); UI_Widget_Desc d = {}; zero_struct(d); d.p_min = b.min; @@ -790,7 +730,7 @@ ui_layout_next_widget(UI* ui, UI_Widget_Kind kind) } internal void -ui_text(UI* ui, String string, v4 color) +ui_textc(UI* ui, String string, v4 color) { UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Text); d.string = string; @@ -818,7 +758,8 @@ ui_text_f(UI* ui, char* fmt, ...) String string = string_fv(scratch.a, fmt, args); va_end(args); - return ui_text(ui, string); + ui_text(ui, string); + scratch_release(scratch); } internal bool @@ -854,25 +795,25 @@ ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 ro scratch_get(scratch); r32 scroll_bar_dim = 15; - v2 scroll_bars_area = v2{0, 0}; + v2 scroll_bars_area = (v2){0, 0}; v2 scroll_area_min = bounds_min; - v2 scroll_area_max = bounds_max - scroll_bars_area; - v2 scroll_area_dim = scroll_area_max - scroll_area_min; + v2 scroll_area_max = HMM_SubtractVec2(bounds_max, scroll_bars_area); + v2 scroll_area_dim = HMM_SubtractVec2(scroll_area_max, scroll_area_min); v2 scroll_offset = {}; zero_struct(scroll_offset); r32 rows_avail = floorf(scroll_area_dim.y / ui->layout->row_height); if (rows > rows_avail) { - scroll_bars_area = v2{ scroll_bar_dim, 0}; + scroll_bars_area = (v2){ scroll_bar_dim, 0}; scroll_area_min = bounds_min; - scroll_area_max = bounds_max - scroll_bars_area; - scroll_area_dim = scroll_area_max - scroll_area_min; + scroll_area_max = HMM_SubtractVec2(bounds_max, scroll_bars_area); + scroll_area_dim = HMM_SubtractVec2(scroll_area_max, scroll_area_min); UI_Widget_Desc vscroll_d = {}; zero_struct(vscroll_d); - vscroll_d.p_min = { bounds_max.x - scroll_bar_dim, bounds_min.y }; - vscroll_d.p_max = { bounds_max.x, bounds_max.y }; + vscroll_d.p_min = (v2){ bounds_max.x - scroll_bar_dim, bounds_min.y }; + vscroll_d.p_max = (v2){ bounds_max.x, bounds_max.y }; vscroll_d.style = ui_get_style(ui, UIWidget_VScroll); vscroll_d.string = string_f(scratch.a, "%.*s_vscroll", str_varg(string)); UI_Widget_Result r = ui_widget_push(ui, vscroll_d); @@ -885,16 +826,17 @@ ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 ro r32 rows_scroll_to = max(0, rows - (rows_avail - 1)); r32 y_scroll_dist = rows_scroll_to * ui->layout->row_height; - scroll_offset *= v2{ 0, y_scroll_dist }; + scroll_offset = HMM_MultiplyVec2(scroll_offset, (v2){ 0, y_scroll_dist }); UI_Layout* layout = allocator_alloc_struct(scratch.a, UI_Layout); layout->mode = UILayout_Columns; layout->bounds_min = scroll_area_min; layout->bounds_max = scroll_area_max; ui_layout_set_row_info(ui, layout); - layout->at = bounds_min - scroll_offset; + layout->at = HMM_SubtractVec2(bounds_min, scroll_offset); ui_layout_push(ui, layout); + scratch_release(scratch); return layout; } diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h index 6a9b4e7..2bea01e 100644 --- a/src_v2/editor/lumenarium_editor_ui.h +++ b/src_v2/editor/lumenarium_editor_ui.h @@ -6,6 +6,7 @@ ///////////////////////////////////////////////////////////// // Interface +typedef struct UI_Vertex UI_Vertex; struct UI_Vertex { v4 pos; @@ -15,6 +16,7 @@ struct UI_Vertex #define UI_WIDGET_ID_VALID_BIT 1 << 31 +typedef union UI_Widget_Id UI_Widget_Id; union UI_Widget_Id { // equality of widget id's only relies on the value field @@ -45,6 +47,7 @@ enum // akin to a css class, could be used to style multiple // elements +typedef struct UI_Widget_Style UI_Widget_Style; struct UI_Widget_Style { UI_Widget_Style_Flags flags; @@ -54,6 +57,7 @@ struct UI_Widget_Style }; // combination of style info and per-instance data +typedef struct UI_Widget_Desc UI_Widget_Desc; struct UI_Widget_Desc { UI_Widget_Style style; @@ -63,6 +67,7 @@ struct UI_Widget_Desc v2 p_max; }; +typedef struct UI_Widget UI_Widget; struct UI_Widget { UI_Widget_Id id; @@ -82,6 +87,7 @@ enum UIWidgetResult_MouseLeft_WentUp = 2, }; +typedef struct UI_Widget_Result UI_Widget_Result; struct UI_Widget_Result { UI_Widget_Id id; @@ -89,7 +95,8 @@ struct UI_Widget_Result v2 drag; }; -enum UI_Widget_Kind +typedef u32 UI_Widget_Kind; +enum { UIWidget_Text, @@ -111,16 +118,19 @@ enum UI_Widget_Kind UIWidget_Count, }; +typedef struct UI_Style_Sheet UI_Style_Sheet; struct UI_Style_Sheet { UI_Widget_Style styles[UIWidget_Count]; }; +typedef struct UI_Widget_State UI_Widget_State; struct UI_Widget_State { v2 scroll; }; +typedef struct UI_Widget_Pool UI_Widget_Pool; struct UI_Widget_Pool { UI_Widget* free; @@ -135,7 +145,8 @@ struct UI_Widget_Pool u32 states_cap; }; -enum UI_Layout_Mode +typedef u8 UI_Layout_Mode; +enum { // each element takes up a whole row UILayout_Columns, @@ -145,6 +156,7 @@ enum UI_Layout_Mode UILayout_Rows, }; +typedef struct UI_Layout UI_Layout; struct UI_Layout { UI_Layout_Mode mode; @@ -159,6 +171,7 @@ struct UI_Layout u32 cols; }; +typedef struct UI_Layout_Bounds UI_Layout_Bounds; struct UI_Layout_Bounds { v2 min; @@ -167,6 +180,7 @@ struct UI_Layout_Bounds typedef void UI_Draw_Panel_Cb(u8* user_data, BSP_Node_Id id, BSP_Node node, BSP_Area area); +typedef struct UI UI; struct UI { Geo_Quad_Buffer_Builder geo; @@ -193,9 +207,9 @@ struct UI Input_State* input; m44 proj; - Platform_Shader shader; - Platform_Texture atlas_texture; - Platform_Geometry_Buffer per_frame_buffer; + Shader shader; + Texture atlas_texture; + Geometry_Buffer per_frame_buffer; }; // Interface @@ -203,16 +217,16 @@ struct UI internal UI ui_create(); internal void ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c); internal void ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id); -internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color); +internal void ui_sprite_push_color(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color); internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id); internal v3 ui_sprite_char_push(UI* ui, v2 at, u32 codepoint, v4 color); -internal void ui_draw(App_State* state); +internal void ui_draw(UI* ui); // Widgets internal void ui_create_default_style_sheet(); -internal UI_Widget_Id ui_widget_id_create(u32 index_in_parent, String string); +internal UI_Widget_Id ui_widget_id_create(String string, u32 index_in_parent); internal bool ui_widget_id_equals(UI_Widget_Id a, UI_Widget_Id b); internal bool ui_widget_id_is_valid(UI_Widget_Id h); diff --git a/src_v2/editor/lumenarium_editor_ui_shaders.h b/src_v2/editor/lumenarium_editor_ui_shaders.h new file mode 100644 index 0000000..1db4635 --- /dev/null +++ b/src_v2/editor/lumenarium_editor_ui_shaders.h @@ -0,0 +1,75 @@ +global XPlatform_Shader_Program_Src ui_shader = { + .win32_vert = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec3 a_pos;\n" + "layout (location = 1) in vec2 a_uv;\n" + "layout (location = 2) in vec4 a_color;\n" + "out vec2 uv;\n" + "out vec4 color;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + " color = a_color;\n" + "}" + ), + .win32_frag = lit_str( + "#version 330 core\n" + "in vec2 uv;\n" + "in vec4 color;\n" + "out vec4 FragColor;\n" + "uniform sampler2D tex;\n" + "void main(void) {\n" + " FragColor = texture(tex, uv) * color;\n" + "}" + ), + + .osx_vert = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec3 a_pos;\n" + "layout (location = 1) in vec2 a_uv;\n" + "layout (location = 2) in vec4 a_color;\n" + "out vec2 uv;\n" + "out vec4 color;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + " color = a_color;\n" + "}" + ), + .osx_frag = lit_str( + "#version 330 core\n" + "in vec2 uv;\n" + "in vec4 color;\n" + "out vec4 FragColor;\n" + "uniform sampler2D tex;\n" + "void main(void) {\n" + " FragColor = texture(tex, uv) * color;\n" + "}" + ), + + .wasm_vert = lit_str( + "precision highp float;\n" + "attribute vec3 a_pos;\n" + "attribute vec2 a_uv;\n" + "attribute vec4 a_color;\n" + "varying vec2 uv;\n" + "varying vec4 color;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + " color = a_color;\n" + "}" + ), + .wasm_frag = lit_str( + "precision highp float;\n" + "varying vec2 uv;\n" + "varying vec4 color;\n" + "uniform sampler2D tex;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(tex, uv) * color;\n" + "}" + ), +}; \ No newline at end of file diff --git a/src_v2/engine/lumenarium_engine.cpp b/src_v2/engine/lumenarium_engine.c similarity index 92% rename from src_v2/engine/lumenarium_engine.cpp rename to src_v2/engine/lumenarium_engine.c index 2f0704f..747a923 100644 --- a/src_v2/engine/lumenarium_engine.cpp +++ b/src_v2/engine/lumenarium_engine.c @@ -2,18 +2,22 @@ internal void en_init(App_State* state, App_Init_Desc desc) { + lumenarium_env_validate(); + state->assemblies = assembly_array_create(permanent, desc.assembly_cap); Output* o = &state->output; register_output_method(o, OutputData_Invalid, 0, 0); register_output_method(o, OutputData_NetworkSACN, output_network_sacn_build, output_network_sacn_init()); register_output_method(o, OutputData_ComUART, output_network_sacn_build, output_com_uart_init()); + + lumenarium_env_validate(); } internal void en_frame_prepare(App_State* state) { - + lumenarium_env_validate(); } global r32 tt = 0; @@ -21,6 +25,8 @@ global r32 tt = 0; internal void en_frame(App_State* state) { + lumenarium_env_validate(); + scratch_get(scratch); Assembly_Array assemblies = state->assemblies; @@ -87,11 +93,14 @@ en_frame(App_State* state) invalid_code_path; } } + + scratch_release(scratch); + lumenarium_env_validate(); } internal void en_cleanup(App_State* state) { - + lumenarium_env_validate(); } diff --git a/src_v2/engine/lumenarium_engine_assembly.cpp b/src_v2/engine/lumenarium_engine_assembly.c similarity index 82% rename from src_v2/engine/lumenarium_engine_assembly.cpp rename to src_v2/engine/lumenarium_engine_assembly.c index 156b3fc..db10fba 100644 --- a/src_v2/engine/lumenarium_engine_assembly.cpp +++ b/src_v2/engine/lumenarium_engine_assembly.c @@ -2,12 +2,13 @@ Assembly_Array assembly_array_create(Allocator* allocator, u32 cap) { - Assembly_Array result = {}; - result.cap = cap; - result.names = allocator_alloc_array(allocator, String, cap); - result.pixel_buffers = allocator_alloc_array(allocator, Assembly_Pixel_Buffer, cap); - result.strip_arrays = allocator_alloc_array(allocator, Assembly_Strip_Array, cap); - result.allocator = allocator; + Assembly_Array result = { + .cap = cap, + .names = allocator_alloc_array(allocator, String, cap), + .pixel_buffers = allocator_alloc_array(allocator, Assembly_Pixel_Buffer, cap), + .strip_arrays = allocator_alloc_array(allocator, Assembly_Strip_Array, cap), + .allocator = allocator, + }; return result; } @@ -49,11 +50,13 @@ assembly_add(Assembly_Array* a, String name, u32 pixels_cap, u32 strips_cap) a->names[index] = name; Assembly_Pixel_Buffer* pixel_buffer = a->pixel_buffers + index; + zero_struct(*pixel_buffer); pixel_buffer->cap = pixels_cap; pixel_buffer->pixels = allocator_alloc_array(a->allocator, Assembly_Pixel, pixels_cap); pixel_buffer->positions = allocator_alloc_array(a->allocator, v4, pixels_cap); Assembly_Strip_Array* strip_array = a->strip_arrays + index; + zero_struct(*strip_array); strip_array->cap = strips_cap; strip_array->strips = allocator_alloc_array(a->allocator, Assembly_Strip, strips_cap); @@ -94,7 +97,7 @@ assembly_add_led( assert(pixel_buffer->len < pixel_buffer->cap); u32 pixel_index = pixel_buffer->len++; - pixel_buffer->pixels[pixel_index] = {}; + pixel_buffer->pixels[pixel_index] = (Assembly_Pixel){}; pixel_buffer->positions[pixel_index] = position; assert(strip->pixels_len < strip->pixels_cap); @@ -109,13 +112,14 @@ assembly_strip_create_leds( v3 start, v3 end, u32 led_count ){ - v3 delta_total = end - start; - v3 delta_step = delta_total / (r32)led_count; + v3 delta_total = HMM_SubtractVec3(end, start); + v3 delta_step = HMM_MultiplyVec3f(delta_total, 1.0f / (r32)led_count); for (u32 i = 0; i < led_count; i++) { v4 pos = {0,0,0,1}; - pos.XYZ = start + ((r32)i * delta_step); + v3 offset = HMM_MultiplyVec3f(delta_step, (r32)i); + pos.XYZ = HMM_AddVec3(start, offset); assembly_add_led(a, h, strip, pos); } } \ No newline at end of file diff --git a/src_v2/engine/lumenarium_engine_assembly.h b/src_v2/engine/lumenarium_engine_assembly.h index 2c94fac..8dbed50 100644 --- a/src_v2/engine/lumenarium_engine_assembly.h +++ b/src_v2/engine/lumenarium_engine_assembly.h @@ -13,11 +13,13 @@ // more than 128 sculptures in a scene - but who knows, maybe someday O.o? #define ASSEMBLY_HANDLE_VALID_BIT (1 << 31) #define ASSEMBLY_HANDLE_INDEX_MASK ~ASSEMBLY_HANDLE_VALID_BIT +typedef struct Assembly_Handle Assembly_Handle; struct Assembly_Handle { u32 value; }; +typedef union Assembly_Pixel Assembly_Pixel; union Assembly_Pixel { struct { @@ -28,6 +30,7 @@ union Assembly_Pixel u8 channels[3]; }; +typedef struct Assembly_Pixel_Buffer Assembly_Pixel_Buffer; struct Assembly_Pixel_Buffer { u32 cap; @@ -36,6 +39,7 @@ struct Assembly_Pixel_Buffer v4* positions; }; +typedef struct Assembly_Strip Assembly_Strip; struct Assembly_Strip { u32 pixels_cap; @@ -47,6 +51,7 @@ struct Assembly_Strip u32 sacn_universe; }; +typedef struct Assembly_Strip_Array Assembly_Strip_Array; struct Assembly_Strip_Array { u32 cap; @@ -54,6 +59,7 @@ struct Assembly_Strip_Array Assembly_Strip* strips; }; +typedef struct Assembly_Array Assembly_Array; struct Assembly_Array { u32 cap; diff --git a/src_v2/engine/lumenarium_engine_output.cpp b/src_v2/engine/lumenarium_engine_output.c similarity index 100% rename from src_v2/engine/lumenarium_engine_output.cpp rename to src_v2/engine/lumenarium_engine_output.c diff --git a/src_v2/engine/lumenarium_engine_output.h b/src_v2/engine/lumenarium_engine_output.h index d8b9a76..3d3ff56 100644 --- a/src_v2/engine/lumenarium_engine_output.h +++ b/src_v2/engine/lumenarium_engine_output.h @@ -12,6 +12,7 @@ enum OutputData_Count, }; +typedef struct Output_Data_Network Output_Data_Network; struct Output_Data_Network { // Platform_Socket_Handle socket; @@ -19,11 +20,13 @@ struct Output_Data_Network u32 port; }; +typedef struct Output_Data_Com Output_Data_Com; struct Output_Data_Com { String port; }; +typedef struct Output_Data Output_Data; struct Output_Data { Output_Data_Kind kind; @@ -37,6 +40,7 @@ struct Output_Data Output_Data* next; }; +typedef struct Output_Data_Queue Output_Data_Queue; struct Output_Data_Queue { Output_Data* first; @@ -47,12 +51,14 @@ struct Output_Data_Queue typedef void Build_Output_Data_Buffer(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue); +typedef struct Output_Methods Output_Methods; struct Output_Methods { Build_Output_Data_Buffer* procs[OutputData_Count]; u8* method_data[OutputData_Count]; }; +typedef struct Output Output; struct Output { Output_Methods methods; diff --git a/src_v2/engine/output/lumenarium_output_sacn.cpp b/src_v2/engine/output/lumenarium_output_sacn.c similarity index 99% rename from src_v2/engine/output/lumenarium_output_sacn.cpp rename to src_v2/engine/output/lumenarium_output_sacn.c index c7f961a..ada76ca 100644 --- a/src_v2/engine/output/lumenarium_output_sacn.cpp +++ b/src_v2/engine/output/lumenarium_output_sacn.c @@ -165,7 +165,7 @@ sacn_fill_buffer_body(Output_Data* d, u32* leds_placed) internal Sacn_Cid sacn_string_to_cid(String str) { - return {}; + return (Sacn_Cid){}; } internal u32 diff --git a/src_v2/engine/output/lumenarium_output_sacn.h b/src_v2/engine/output/lumenarium_output_sacn.h index 568020a..0f762c7 100644 --- a/src_v2/engine/output/lumenarium_output_sacn.h +++ b/src_v2/engine/output/lumenarium_output_sacn.h @@ -4,11 +4,13 @@ #define LUMENARIUM_OUTPUT_SACN_H #define SACN_CID_BYTES 16 +typedef struct Sacn_Cid Sacn_Cid; struct Sacn_Cid { u8 bytes[SACN_CID_BYTES]; }; +typedef struct Sacn Sacn; struct Sacn { Sacn_Cid cid; diff --git a/src_v2/engine/output/lumenarium_output_uart.cpp b/src_v2/engine/output/lumenarium_output_uart.c similarity index 100% rename from src_v2/engine/output/lumenarium_output_uart.cpp rename to src_v2/engine/output/lumenarium_output_uart.c diff --git a/src_v2/libs/HandmadeMath.h b/src_v2/libs/HandmadeMath.h index 8f71b31..63f883b 100644 --- a/src_v2/libs/HandmadeMath.h +++ b/src_v2/libs/HandmadeMath.h @@ -2122,7 +2122,7 @@ HMM_AverageVec3(hmm_vec3* Arr, int Len) hmm_vec3 Total = {}; for (int i = 0; i < Len; i++) { - Total += HMM_AddVec3(Total, Arr[i]); + Total = HMM_AddVec3(Total, Arr[i]); } hmm_vec3 Result = {}; diff --git a/src_v2/libs/glfw_osx/include/GLFW/glfw3.h b/src_v2/libs/glfw_osx/include/GLFW/glfw3.h new file mode 100644 index 0000000..d074de1 --- /dev/null +++ b/src_v2/libs/glfw_osx/include/GLFW/glfw3.h @@ -0,0 +1,5913 @@ +/************************************************************************* + * GLFW 3.3 - www.glfw.org + * A library for OpenGL, window and input + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2019 Camilla Löwy + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_h_ +#define _glfw3_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @file glfw3.h + * @brief The header of the GLFW 3 API. + * + * This is the header file of the GLFW 3 API. It defines all its types and + * declares all its functions. + * + * For more information about how to use this file, see @ref build_include. + */ +/*! @defgroup context Context reference + * @brief Functions and types related to OpenGL and OpenGL ES contexts. + * + * This is the reference documentation for OpenGL and OpenGL ES context related + * functions. For more task-oriented information, see the @ref context_guide. + */ +/*! @defgroup vulkan Vulkan support reference + * @brief Functions and types related to Vulkan. + * + * This is the reference documentation for Vulkan related functions and types. + * For more task-oriented information, see the @ref vulkan_guide. + */ +/*! @defgroup init Initialization, version and error reference + * @brief Functions and types related to initialization and error handling. + * + * This is the reference documentation for initialization and termination of + * the library, version management and error handling. For more task-oriented + * information, see the @ref intro_guide. + */ +/*! @defgroup input Input reference + * @brief Functions and types related to input handling. + * + * This is the reference documentation for input related functions and types. + * For more task-oriented information, see the @ref input_guide. + */ +/*! @defgroup monitor Monitor reference + * @brief Functions and types related to monitors. + * + * This is the reference documentation for monitor related functions and types. + * For more task-oriented information, see the @ref monitor_guide. + */ +/*! @defgroup window Window reference + * @brief Functions and types related to windows. + * + * This is the reference documentation for window related functions and types, + * including creation, deletion and event polling. For more task-oriented + * information, see the @ref window_guide. + */ + + +/************************************************************************* + * Compiler- and platform-specific preprocessor work + *************************************************************************/ + +/* If we are we on Windows, we want a single define for it. + */ +#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)) + #define _WIN32 +#endif /* _WIN32 */ + +/* Include because most Windows GLU headers need wchar_t and + * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +/* Include because it is needed by Vulkan and related functions. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +#if defined(GLFW_INCLUDE_VULKAN) + #include +#endif /* Vulkan header */ + +/* The Vulkan header may have indirectly included windows.h (because of + * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it. + */ + +/* It is customary to use APIENTRY for OpenGL function pointer declarations on + * all platforms. Additionally, the Windows OpenGL header needs APIENTRY. + */ +#if !defined(APIENTRY) + #if defined(_WIN32) + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif + #define GLFW_APIENTRY_DEFINED +#endif /* APIENTRY */ + +/* Some Windows OpenGL headers need this. + */ +#if !defined(WINGDIAPI) && defined(_WIN32) + #define WINGDIAPI __declspec(dllimport) + #define GLFW_WINGDIAPI_DEFINED +#endif /* WINGDIAPI */ + +/* Some Windows GLU headers need this. + */ +#if !defined(CALLBACK) && defined(_WIN32) + #define CALLBACK __stdcall + #define GLFW_CALLBACK_DEFINED +#endif /* CALLBACK */ + +/* Include the chosen OpenGL or OpenGL ES headers. + */ +#if defined(GLFW_INCLUDE_ES1) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES2) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES3) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES31) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES32) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_GLCOREARB) + + #if defined(__APPLE__) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif /*GLFW_INCLUDE_GLEXT*/ + + #else /*__APPLE__*/ + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + + #endif /*__APPLE__*/ + +#elif defined(GLFW_INCLUDE_GLU) + + #if defined(__APPLE__) + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #else /*__APPLE__*/ + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #endif /*__APPLE__*/ + +#elif !defined(GLFW_INCLUDE_NONE) && \ + !defined(__gl_h_) && \ + !defined(__gles1_gl_h_) && \ + !defined(__gles2_gl2_h_) && \ + !defined(__gles2_gl3_h_) && \ + !defined(__gles2_gl31_h_) && \ + !defined(__gles2_gl32_h_) && \ + !defined(__gl_glcorearb_h_) && \ + !defined(__gl2_h_) /*legacy*/ && \ + !defined(__gl3_h_) /*legacy*/ && \ + !defined(__gl31_h_) /*legacy*/ && \ + !defined(__gl32_h_) /*legacy*/ && \ + !defined(__glcorearb_h_) /*legacy*/ && \ + !defined(__GL_H__) /*non-standard*/ && \ + !defined(__gltypes_h_) /*non-standard*/ && \ + !defined(__glee_h_) /*non-standard*/ + + #if defined(__APPLE__) + + #if !defined(GLFW_INCLUDE_GLEXT) + #define GL_GLEXT_LEGACY + #endif + #include + + #else /*__APPLE__*/ + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + + #endif /*__APPLE__*/ + +#endif /* OpenGL and OpenGL ES headers */ + +#if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) + /* GLFW_DLL must be defined by applications that are linking against the DLL + * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW + * configuration header when compiling the DLL version of the library. + */ + #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined" +#endif + +/* GLFWAPI is used to declare public API functions for export + * from the DLL / shared library / dynamic library. + */ +#if defined(_WIN32) && defined(_GLFW_BUILD_DLL) + /* We are building GLFW as a Win32 DLL */ + #define GLFWAPI __declspec(dllexport) +#elif defined(_WIN32) && defined(GLFW_DLL) + /* We are calling GLFW as a Win32 DLL */ + #define GLFWAPI __declspec(dllimport) +#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) + /* We are building GLFW as a shared / dynamic library */ + #define GLFWAPI __attribute__((visibility("default"))) +#else + /* We are building or calling GLFW as a static library */ + #define GLFWAPI +#endif + + +/************************************************************************* + * GLFW API tokens + *************************************************************************/ + +/*! @name GLFW version macros + * @{ */ +/*! @brief The major version number of the GLFW header. + * + * The major version number of the GLFW header. This is incremented when the + * API is changed in non-compatible ways. + * @ingroup init + */ +#define GLFW_VERSION_MAJOR 3 +/*! @brief The minor version number of the GLFW header. + * + * The minor version number of the GLFW header. This is incremented when + * features are added to the API but it remains backward-compatible. + * @ingroup init + */ +#define GLFW_VERSION_MINOR 3 +/*! @brief The revision number of the GLFW header. + * + * The revision number of the GLFW header. This is incremented when a bug fix + * release is made that does not contain any API changes. + * @ingroup init + */ +#define GLFW_VERSION_REVISION 7 +/*! @} */ + +/*! @brief One. + * + * This is only semantic sugar for the number 1. You can instead use `1` or + * `true` or `_True` or `GL_TRUE` or `VK_TRUE` or anything else that is equal + * to one. + * + * @ingroup init + */ +#define GLFW_TRUE 1 +/*! @brief Zero. + * + * This is only semantic sugar for the number 0. You can instead use `0` or + * `false` or `_False` or `GL_FALSE` or `VK_FALSE` or anything else that is + * equal to zero. + * + * @ingroup init + */ +#define GLFW_FALSE 0 + +/*! @name Key and button actions + * @{ */ +/*! @brief The key or mouse button was released. + * + * The key or mouse button was released. + * + * @ingroup input + */ +#define GLFW_RELEASE 0 +/*! @brief The key or mouse button was pressed. + * + * The key or mouse button was pressed. + * + * @ingroup input + */ +#define GLFW_PRESS 1 +/*! @brief The key was held down until it repeated. + * + * The key was held down until it repeated. + * + * @ingroup input + */ +#define GLFW_REPEAT 2 +/*! @} */ + +/*! @defgroup hat_state Joystick hat states + * @brief Joystick hat states. + * + * See [joystick hat input](@ref joystick_hat) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_HAT_CENTERED 0 +#define GLFW_HAT_UP 1 +#define GLFW_HAT_RIGHT 2 +#define GLFW_HAT_DOWN 4 +#define GLFW_HAT_LEFT 8 +#define GLFW_HAT_RIGHT_UP (GLFW_HAT_RIGHT | GLFW_HAT_UP) +#define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN) +#define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP) +#define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN) +/*! @} */ + +/*! @defgroup keys Keyboard keys + * @brief Keyboard key IDs. + * + * See [key input](@ref input_key) for how these are used. + * + * These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60), + * but re-arranged to map to 7-bit ASCII for printable keys (function keys are + * put in the 256+ range). + * + * The naming of the key codes follow these rules: + * - The US keyboard layout is used + * - Names of printable alpha-numeric characters are used (e.g. "A", "R", + * "3", etc.) + * - For non-alphanumeric characters, Unicode:ish names are used (e.g. + * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not + * correspond to the Unicode standard (usually for brevity) + * - Keys that lack a clear US mapping are named "WORLD_x" + * - For non-printable keys, custom names are used (e.g. "F4", + * "BACKSPACE", etc.) + * + * @ingroup input + * @{ + */ + +/* The unknown key */ +#define GLFW_KEY_UNKNOWN -1 + +/* Printable keys */ +#define GLFW_KEY_SPACE 32 +#define GLFW_KEY_APOSTROPHE 39 /* ' */ +#define GLFW_KEY_COMMA 44 /* , */ +#define GLFW_KEY_MINUS 45 /* - */ +#define GLFW_KEY_PERIOD 46 /* . */ +#define GLFW_KEY_SLASH 47 /* / */ +#define GLFW_KEY_0 48 +#define GLFW_KEY_1 49 +#define GLFW_KEY_2 50 +#define GLFW_KEY_3 51 +#define GLFW_KEY_4 52 +#define GLFW_KEY_5 53 +#define GLFW_KEY_6 54 +#define GLFW_KEY_7 55 +#define GLFW_KEY_8 56 +#define GLFW_KEY_9 57 +#define GLFW_KEY_SEMICOLON 59 /* ; */ +#define GLFW_KEY_EQUAL 61 /* = */ +#define GLFW_KEY_A 65 +#define GLFW_KEY_B 66 +#define GLFW_KEY_C 67 +#define GLFW_KEY_D 68 +#define GLFW_KEY_E 69 +#define GLFW_KEY_F 70 +#define GLFW_KEY_G 71 +#define GLFW_KEY_H 72 +#define GLFW_KEY_I 73 +#define GLFW_KEY_J 74 +#define GLFW_KEY_K 75 +#define GLFW_KEY_L 76 +#define GLFW_KEY_M 77 +#define GLFW_KEY_N 78 +#define GLFW_KEY_O 79 +#define GLFW_KEY_P 80 +#define GLFW_KEY_Q 81 +#define GLFW_KEY_R 82 +#define GLFW_KEY_S 83 +#define GLFW_KEY_T 84 +#define GLFW_KEY_U 85 +#define GLFW_KEY_V 86 +#define GLFW_KEY_W 87 +#define GLFW_KEY_X 88 +#define GLFW_KEY_Y 89 +#define GLFW_KEY_Z 90 +#define GLFW_KEY_LEFT_BRACKET 91 /* [ */ +#define GLFW_KEY_BACKSLASH 92 /* \ */ +#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */ +#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */ +#define GLFW_KEY_WORLD_1 161 /* non-US #1 */ +#define GLFW_KEY_WORLD_2 162 /* non-US #2 */ + +/* Function keys */ +#define GLFW_KEY_ESCAPE 256 +#define GLFW_KEY_ENTER 257 +#define GLFW_KEY_TAB 258 +#define GLFW_KEY_BACKSPACE 259 +#define GLFW_KEY_INSERT 260 +#define GLFW_KEY_DELETE 261 +#define GLFW_KEY_RIGHT 262 +#define GLFW_KEY_LEFT 263 +#define GLFW_KEY_DOWN 264 +#define GLFW_KEY_UP 265 +#define GLFW_KEY_PAGE_UP 266 +#define GLFW_KEY_PAGE_DOWN 267 +#define GLFW_KEY_HOME 268 +#define GLFW_KEY_END 269 +#define GLFW_KEY_CAPS_LOCK 280 +#define GLFW_KEY_SCROLL_LOCK 281 +#define GLFW_KEY_NUM_LOCK 282 +#define GLFW_KEY_PRINT_SCREEN 283 +#define GLFW_KEY_PAUSE 284 +#define GLFW_KEY_F1 290 +#define GLFW_KEY_F2 291 +#define GLFW_KEY_F3 292 +#define GLFW_KEY_F4 293 +#define GLFW_KEY_F5 294 +#define GLFW_KEY_F6 295 +#define GLFW_KEY_F7 296 +#define GLFW_KEY_F8 297 +#define GLFW_KEY_F9 298 +#define GLFW_KEY_F10 299 +#define GLFW_KEY_F11 300 +#define GLFW_KEY_F12 301 +#define GLFW_KEY_F13 302 +#define GLFW_KEY_F14 303 +#define GLFW_KEY_F15 304 +#define GLFW_KEY_F16 305 +#define GLFW_KEY_F17 306 +#define GLFW_KEY_F18 307 +#define GLFW_KEY_F19 308 +#define GLFW_KEY_F20 309 +#define GLFW_KEY_F21 310 +#define GLFW_KEY_F22 311 +#define GLFW_KEY_F23 312 +#define GLFW_KEY_F24 313 +#define GLFW_KEY_F25 314 +#define GLFW_KEY_KP_0 320 +#define GLFW_KEY_KP_1 321 +#define GLFW_KEY_KP_2 322 +#define GLFW_KEY_KP_3 323 +#define GLFW_KEY_KP_4 324 +#define GLFW_KEY_KP_5 325 +#define GLFW_KEY_KP_6 326 +#define GLFW_KEY_KP_7 327 +#define GLFW_KEY_KP_8 328 +#define GLFW_KEY_KP_9 329 +#define GLFW_KEY_KP_DECIMAL 330 +#define GLFW_KEY_KP_DIVIDE 331 +#define GLFW_KEY_KP_MULTIPLY 332 +#define GLFW_KEY_KP_SUBTRACT 333 +#define GLFW_KEY_KP_ADD 334 +#define GLFW_KEY_KP_ENTER 335 +#define GLFW_KEY_KP_EQUAL 336 +#define GLFW_KEY_LEFT_SHIFT 340 +#define GLFW_KEY_LEFT_CONTROL 341 +#define GLFW_KEY_LEFT_ALT 342 +#define GLFW_KEY_LEFT_SUPER 343 +#define GLFW_KEY_RIGHT_SHIFT 344 +#define GLFW_KEY_RIGHT_CONTROL 345 +#define GLFW_KEY_RIGHT_ALT 346 +#define GLFW_KEY_RIGHT_SUPER 347 +#define GLFW_KEY_MENU 348 + +#define GLFW_KEY_LAST GLFW_KEY_MENU + +/*! @} */ + +/*! @defgroup mods Modifier key flags + * @brief Modifier key flags. + * + * See [key input](@ref input_key) for how these are used. + * + * @ingroup input + * @{ */ + +/*! @brief If this bit is set one or more Shift keys were held down. + * + * If this bit is set one or more Shift keys were held down. + */ +#define GLFW_MOD_SHIFT 0x0001 +/*! @brief If this bit is set one or more Control keys were held down. + * + * If this bit is set one or more Control keys were held down. + */ +#define GLFW_MOD_CONTROL 0x0002 +/*! @brief If this bit is set one or more Alt keys were held down. + * + * If this bit is set one or more Alt keys were held down. + */ +#define GLFW_MOD_ALT 0x0004 +/*! @brief If this bit is set one or more Super keys were held down. + * + * If this bit is set one or more Super keys were held down. + */ +#define GLFW_MOD_SUPER 0x0008 +/*! @brief If this bit is set the Caps Lock key is enabled. + * + * If this bit is set the Caps Lock key is enabled and the @ref + * GLFW_LOCK_KEY_MODS input mode is set. + */ +#define GLFW_MOD_CAPS_LOCK 0x0010 +/*! @brief If this bit is set the Num Lock key is enabled. + * + * If this bit is set the Num Lock key is enabled and the @ref + * GLFW_LOCK_KEY_MODS input mode is set. + */ +#define GLFW_MOD_NUM_LOCK 0x0020 + +/*! @} */ + +/*! @defgroup buttons Mouse buttons + * @brief Mouse button IDs. + * + * See [mouse button input](@ref input_mouse_button) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_MOUSE_BUTTON_1 0 +#define GLFW_MOUSE_BUTTON_2 1 +#define GLFW_MOUSE_BUTTON_3 2 +#define GLFW_MOUSE_BUTTON_4 3 +#define GLFW_MOUSE_BUTTON_5 4 +#define GLFW_MOUSE_BUTTON_6 5 +#define GLFW_MOUSE_BUTTON_7 6 +#define GLFW_MOUSE_BUTTON_8 7 +#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8 +#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1 +#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2 +#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3 +/*! @} */ + +/*! @defgroup joysticks Joysticks + * @brief Joystick IDs. + * + * See [joystick input](@ref joystick) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_JOYSTICK_1 0 +#define GLFW_JOYSTICK_2 1 +#define GLFW_JOYSTICK_3 2 +#define GLFW_JOYSTICK_4 3 +#define GLFW_JOYSTICK_5 4 +#define GLFW_JOYSTICK_6 5 +#define GLFW_JOYSTICK_7 6 +#define GLFW_JOYSTICK_8 7 +#define GLFW_JOYSTICK_9 8 +#define GLFW_JOYSTICK_10 9 +#define GLFW_JOYSTICK_11 10 +#define GLFW_JOYSTICK_12 11 +#define GLFW_JOYSTICK_13 12 +#define GLFW_JOYSTICK_14 13 +#define GLFW_JOYSTICK_15 14 +#define GLFW_JOYSTICK_16 15 +#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 +/*! @} */ + +/*! @defgroup gamepad_buttons Gamepad buttons + * @brief Gamepad buttons. + * + * See @ref gamepad for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_GAMEPAD_BUTTON_A 0 +#define GLFW_GAMEPAD_BUTTON_B 1 +#define GLFW_GAMEPAD_BUTTON_X 2 +#define GLFW_GAMEPAD_BUTTON_Y 3 +#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER 4 +#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER 5 +#define GLFW_GAMEPAD_BUTTON_BACK 6 +#define GLFW_GAMEPAD_BUTTON_START 7 +#define GLFW_GAMEPAD_BUTTON_GUIDE 8 +#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB 9 +#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB 10 +#define GLFW_GAMEPAD_BUTTON_DPAD_UP 11 +#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT 12 +#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN 13 +#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT 14 +#define GLFW_GAMEPAD_BUTTON_LAST GLFW_GAMEPAD_BUTTON_DPAD_LEFT + +#define GLFW_GAMEPAD_BUTTON_CROSS GLFW_GAMEPAD_BUTTON_A +#define GLFW_GAMEPAD_BUTTON_CIRCLE GLFW_GAMEPAD_BUTTON_B +#define GLFW_GAMEPAD_BUTTON_SQUARE GLFW_GAMEPAD_BUTTON_X +#define GLFW_GAMEPAD_BUTTON_TRIANGLE GLFW_GAMEPAD_BUTTON_Y +/*! @} */ + +/*! @defgroup gamepad_axes Gamepad axes + * @brief Gamepad axes. + * + * See @ref gamepad for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_GAMEPAD_AXIS_LEFT_X 0 +#define GLFW_GAMEPAD_AXIS_LEFT_Y 1 +#define GLFW_GAMEPAD_AXIS_RIGHT_X 2 +#define GLFW_GAMEPAD_AXIS_RIGHT_Y 3 +#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER 4 +#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5 +#define GLFW_GAMEPAD_AXIS_LAST GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER +/*! @} */ + +/*! @defgroup errors Error codes + * @brief Error codes. + * + * See [error handling](@ref error_handling) for how these are used. + * + * @ingroup init + * @{ */ +/*! @brief No error has occurred. + * + * No error has occurred. + * + * @analysis Yay. + */ +#define GLFW_NO_ERROR 0 +/*! @brief GLFW has not been initialized. + * + * This occurs if a GLFW function was called that must not be called unless the + * library is [initialized](@ref intro_init). + * + * @analysis Application programmer error. Initialize GLFW before calling any + * function that requires initialization. + */ +#define GLFW_NOT_INITIALIZED 0x00010001 +/*! @brief No context is current for this thread. + * + * This occurs if a GLFW function was called that needs and operates on the + * current OpenGL or OpenGL ES context but no context is current on the calling + * thread. One such function is @ref glfwSwapInterval. + * + * @analysis Application programmer error. Ensure a context is current before + * calling functions that require a current context. + */ +#define GLFW_NO_CURRENT_CONTEXT 0x00010002 +/*! @brief One of the arguments to the function was an invalid enum value. + * + * One of the arguments to the function was an invalid enum value, for example + * requesting @ref GLFW_RED_BITS with @ref glfwGetWindowAttrib. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_INVALID_ENUM 0x00010003 +/*! @brief One of the arguments to the function was an invalid value. + * + * One of the arguments to the function was an invalid value, for example + * requesting a non-existent OpenGL or OpenGL ES version like 2.7. + * + * Requesting a valid but unavailable OpenGL or OpenGL ES version will instead + * result in a @ref GLFW_VERSION_UNAVAILABLE error. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_INVALID_VALUE 0x00010004 +/*! @brief A memory allocation failed. + * + * A memory allocation failed. + * + * @analysis A bug in GLFW or the underlying operating system. Report the bug + * to our [issue tracker](https://github.com/glfw/glfw/issues). + */ +#define GLFW_OUT_OF_MEMORY 0x00010005 +/*! @brief GLFW could not find support for the requested API on the system. + * + * GLFW could not find support for the requested API on the system. + * + * @analysis The installed graphics driver does not support the requested + * API, or does not support it via the chosen context creation backend. + * Below are a few examples. + * + * @par + * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only + * supports OpenGL ES via EGL, while Nvidia and Intel only support it via + * a WGL or GLX extension. macOS does not provide OpenGL ES at all. The Mesa + * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary + * driver. Older graphics drivers do not support Vulkan. + */ +#define GLFW_API_UNAVAILABLE 0x00010006 +/*! @brief The requested OpenGL or OpenGL ES version is not available. + * + * The requested OpenGL or OpenGL ES version (including any requested context + * or framebuffer hints) is not available on this machine. + * + * @analysis The machine does not support your requirements. If your + * application is sufficiently flexible, downgrade your requirements and try + * again. Otherwise, inform the user that their machine does not match your + * requirements. + * + * @par + * Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0 + * comes out before the 4.x series gets that far, also fail with this error and + * not @ref GLFW_INVALID_VALUE, because GLFW cannot know what future versions + * will exist. + */ +#define GLFW_VERSION_UNAVAILABLE 0x00010007 +/*! @brief A platform-specific error occurred that does not match any of the + * more specific categories. + * + * A platform-specific error occurred that does not match any of the more + * specific categories. + * + * @analysis A bug or configuration error in GLFW, the underlying operating + * system or its drivers, or a lack of required resources. Report the issue to + * our [issue tracker](https://github.com/glfw/glfw/issues). + */ +#define GLFW_PLATFORM_ERROR 0x00010008 +/*! @brief The requested format is not supported or available. + * + * If emitted during window creation, the requested pixel format is not + * supported. + * + * If emitted when querying the clipboard, the contents of the clipboard could + * not be converted to the requested format. + * + * @analysis If emitted during window creation, one or more + * [hard constraints](@ref window_hints_hard) did not match any of the + * available pixel formats. If your application is sufficiently flexible, + * downgrade your requirements and try again. Otherwise, inform the user that + * their machine does not match your requirements. + * + * @par + * If emitted when querying the clipboard, ignore the error or report it to + * the user, as appropriate. + */ +#define GLFW_FORMAT_UNAVAILABLE 0x00010009 +/*! @brief The specified window does not have an OpenGL or OpenGL ES context. + * + * A window that does not have an OpenGL or OpenGL ES context was passed to + * a function that requires it to have one. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_NO_WINDOW_CONTEXT 0x0001000A +/*! @} */ + +/*! @addtogroup window + * @{ */ +/*! @brief Input focus window hint and attribute + * + * Input focus [window hint](@ref GLFW_FOCUSED_hint) or + * [window attribute](@ref GLFW_FOCUSED_attrib). + */ +#define GLFW_FOCUSED 0x00020001 +/*! @brief Window iconification window attribute + * + * Window iconification [window attribute](@ref GLFW_ICONIFIED_attrib). + */ +#define GLFW_ICONIFIED 0x00020002 +/*! @brief Window resize-ability window hint and attribute + * + * Window resize-ability [window hint](@ref GLFW_RESIZABLE_hint) and + * [window attribute](@ref GLFW_RESIZABLE_attrib). + */ +#define GLFW_RESIZABLE 0x00020003 +/*! @brief Window visibility window hint and attribute + * + * Window visibility [window hint](@ref GLFW_VISIBLE_hint) and + * [window attribute](@ref GLFW_VISIBLE_attrib). + */ +#define GLFW_VISIBLE 0x00020004 +/*! @brief Window decoration window hint and attribute + * + * Window decoration [window hint](@ref GLFW_DECORATED_hint) and + * [window attribute](@ref GLFW_DECORATED_attrib). + */ +#define GLFW_DECORATED 0x00020005 +/*! @brief Window auto-iconification window hint and attribute + * + * Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) and + * [window attribute](@ref GLFW_AUTO_ICONIFY_attrib). + */ +#define GLFW_AUTO_ICONIFY 0x00020006 +/*! @brief Window decoration window hint and attribute + * + * Window decoration [window hint](@ref GLFW_FLOATING_hint) and + * [window attribute](@ref GLFW_FLOATING_attrib). + */ +#define GLFW_FLOATING 0x00020007 +/*! @brief Window maximization window hint and attribute + * + * Window maximization [window hint](@ref GLFW_MAXIMIZED_hint) and + * [window attribute](@ref GLFW_MAXIMIZED_attrib). + */ +#define GLFW_MAXIMIZED 0x00020008 +/*! @brief Cursor centering window hint + * + * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). + */ +#define GLFW_CENTER_CURSOR 0x00020009 +/*! @brief Window framebuffer transparency hint and attribute + * + * Window framebuffer transparency + * [window hint](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint) and + * [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib). + */ +#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A +/*! @brief Mouse cursor hover window attribute. + * + * Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib). + */ +#define GLFW_HOVERED 0x0002000B +/*! @brief Input focus on calling show window hint and attribute + * + * Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or + * [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib). + */ +#define GLFW_FOCUS_ON_SHOW 0x0002000C + +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). + */ +#define GLFW_RED_BITS 0x00021001 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_GREEN_BITS). + */ +#define GLFW_GREEN_BITS 0x00021002 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_BLUE_BITS). + */ +#define GLFW_BLUE_BITS 0x00021003 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ALPHA_BITS). + */ +#define GLFW_ALPHA_BITS 0x00021004 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_DEPTH_BITS). + */ +#define GLFW_DEPTH_BITS 0x00021005 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_STENCIL_BITS). + */ +#define GLFW_STENCIL_BITS 0x00021006 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_RED_BITS). + */ +#define GLFW_ACCUM_RED_BITS 0x00021007 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_GREEN_BITS). + */ +#define GLFW_ACCUM_GREEN_BITS 0x00021008 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_BLUE_BITS). + */ +#define GLFW_ACCUM_BLUE_BITS 0x00021009 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_ALPHA_BITS). + */ +#define GLFW_ACCUM_ALPHA_BITS 0x0002100A +/*! @brief Framebuffer auxiliary buffer hint. + * + * Framebuffer auxiliary buffer [hint](@ref GLFW_AUX_BUFFERS). + */ +#define GLFW_AUX_BUFFERS 0x0002100B +/*! @brief OpenGL stereoscopic rendering hint. + * + * OpenGL stereoscopic rendering [hint](@ref GLFW_STEREO). + */ +#define GLFW_STEREO 0x0002100C +/*! @brief Framebuffer MSAA samples hint. + * + * Framebuffer MSAA samples [hint](@ref GLFW_SAMPLES). + */ +#define GLFW_SAMPLES 0x0002100D +/*! @brief Framebuffer sRGB hint. + * + * Framebuffer sRGB [hint](@ref GLFW_SRGB_CAPABLE). + */ +#define GLFW_SRGB_CAPABLE 0x0002100E +/*! @brief Monitor refresh rate hint. + * + * Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE). + */ +#define GLFW_REFRESH_RATE 0x0002100F +/*! @brief Framebuffer double buffering hint. + * + * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). + */ +#define GLFW_DOUBLEBUFFER 0x00021010 + +/*! @brief Context client API hint and attribute. + * + * Context client API [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ +#define GLFW_CLIENT_API 0x00022001 +/*! @brief Context client API major version hint and attribute. + * + * Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib). + */ +#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 +/*! @brief Context client API minor version hint and attribute. + * + * Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib). + */ +#define GLFW_CONTEXT_VERSION_MINOR 0x00022003 +/*! @brief Context client API revision number attribute. + * + * Context client API revision number + * [attribute](@ref GLFW_CONTEXT_REVISION_attrib). + */ +#define GLFW_CONTEXT_REVISION 0x00022004 +/*! @brief Context robustness hint and attribute. + * + * Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint) + * and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib). + */ +#define GLFW_CONTEXT_ROBUSTNESS 0x00022005 +/*! @brief OpenGL forward-compatibility hint and attribute. + * + * OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) + * and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib). + */ +#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 +/*! @brief Debug mode context hint and attribute. + * + * Debug mode context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and + * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib). + */ +#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 +/*! @brief OpenGL profile hint and attribute. + * + * OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and + * [attribute](@ref GLFW_OPENGL_PROFILE_attrib). + */ +#define GLFW_OPENGL_PROFILE 0x00022008 +/*! @brief Context flush-on-release hint and attribute. + * + * Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and + * [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib). + */ +#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 +/*! @brief Context error suppression hint and attribute. + * + * Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and + * [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib). + */ +#define GLFW_CONTEXT_NO_ERROR 0x0002200A +/*! @brief Context creation API hint and attribute. + * + * Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and + * [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib). + */ +#define GLFW_CONTEXT_CREATION_API 0x0002200B +/*! @brief Window content area scaling window + * [window hint](@ref GLFW_SCALE_TO_MONITOR). + */ +#define GLFW_SCALE_TO_MONITOR 0x0002200C +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint). + */ +#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint). + */ +#define GLFW_COCOA_FRAME_NAME 0x00023002 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint). + */ +#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ +#define GLFW_X11_CLASS_NAME 0x00024001 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ +#define GLFW_X11_INSTANCE_NAME 0x00024002 +/*! @} */ + +#define GLFW_NO_API 0 +#define GLFW_OPENGL_API 0x00030001 +#define GLFW_OPENGL_ES_API 0x00030002 + +#define GLFW_NO_ROBUSTNESS 0 +#define GLFW_NO_RESET_NOTIFICATION 0x00031001 +#define GLFW_LOSE_CONTEXT_ON_RESET 0x00031002 + +#define GLFW_OPENGL_ANY_PROFILE 0 +#define GLFW_OPENGL_CORE_PROFILE 0x00032001 +#define GLFW_OPENGL_COMPAT_PROFILE 0x00032002 + +#define GLFW_CURSOR 0x00033001 +#define GLFW_STICKY_KEYS 0x00033002 +#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 +#define GLFW_LOCK_KEY_MODS 0x00033004 +#define GLFW_RAW_MOUSE_MOTION 0x00033005 + +#define GLFW_CURSOR_NORMAL 0x00034001 +#define GLFW_CURSOR_HIDDEN 0x00034002 +#define GLFW_CURSOR_DISABLED 0x00034003 + +#define GLFW_ANY_RELEASE_BEHAVIOR 0 +#define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001 +#define GLFW_RELEASE_BEHAVIOR_NONE 0x00035002 + +#define GLFW_NATIVE_CONTEXT_API 0x00036001 +#define GLFW_EGL_CONTEXT_API 0x00036002 +#define GLFW_OSMESA_CONTEXT_API 0x00036003 + +/*! @defgroup shapes Standard cursor shapes + * @brief Standard system cursor shapes. + * + * See [standard cursor creation](@ref cursor_standard) for how these are used. + * + * @ingroup input + * @{ */ + +/*! @brief The regular arrow cursor shape. + * + * The regular arrow cursor. + */ +#define GLFW_ARROW_CURSOR 0x00036001 +/*! @brief The text input I-beam cursor shape. + * + * The text input I-beam cursor shape. + */ +#define GLFW_IBEAM_CURSOR 0x00036002 +/*! @brief The crosshair shape. + * + * The crosshair shape. + */ +#define GLFW_CROSSHAIR_CURSOR 0x00036003 +/*! @brief The hand shape. + * + * The hand shape. + */ +#define GLFW_HAND_CURSOR 0x00036004 +/*! @brief The horizontal resize arrow shape. + * + * The horizontal resize arrow shape. + */ +#define GLFW_HRESIZE_CURSOR 0x00036005 +/*! @brief The vertical resize arrow shape. + * + * The vertical resize arrow shape. + */ +#define GLFW_VRESIZE_CURSOR 0x00036006 +/*! @} */ + +#define GLFW_CONNECTED 0x00040001 +#define GLFW_DISCONNECTED 0x00040002 + +/*! @addtogroup init + * @{ */ +/*! @brief Joystick hat buttons init hint. + * + * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS). + */ +#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint). + */ +#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). + */ +#define GLFW_COCOA_MENUBAR 0x00051002 +/*! @} */ + +#define GLFW_DONT_CARE -1 + + +/************************************************************************* + * GLFW API types + *************************************************************************/ + +/*! @brief Client API function pointer type. + * + * Generic function pointer used for returning client API function pointers + * without forcing a cast from a regular pointer. + * + * @sa @ref context_glext + * @sa @ref glfwGetProcAddress + * + * @since Added in version 3.0. + * + * @ingroup context + */ +typedef void (*GLFWglproc)(void); + +/*! @brief Vulkan API function pointer type. + * + * Generic function pointer used for returning Vulkan API function pointers + * without forcing a cast from a regular pointer. + * + * @sa @ref vulkan_proc + * @sa @ref glfwGetInstanceProcAddress + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +typedef void (*GLFWvkproc)(void); + +/*! @brief Opaque monitor object. + * + * Opaque monitor object. + * + * @see @ref monitor_object + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef struct GLFWmonitor GLFWmonitor; + +/*! @brief Opaque window object. + * + * Opaque window object. + * + * @see @ref window_object + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef struct GLFWwindow GLFWwindow; + +/*! @brief Opaque cursor object. + * + * Opaque cursor object. + * + * @see @ref cursor_object + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef struct GLFWcursor GLFWcursor; + +/*! @brief The function pointer type for error callbacks. + * + * This is the function pointer type for error callbacks. An error callback + * function has the following signature: + * @code + * void callback_name(int error_code, const char* description) + * @endcode + * + * @param[in] error_code An [error code](@ref errors). Future releases may add + * more error codes. + * @param[in] description A UTF-8 encoded string describing the error. + * + * @pointer_lifetime The error description string is valid until the callback + * function returns. + * + * @sa @ref error_handling + * @sa @ref glfwSetErrorCallback + * + * @since Added in version 3.0. + * + * @ingroup init + */ +typedef void (* GLFWerrorfun)(int error_code, const char* description); + +/*! @brief The function pointer type for window position callbacks. + * + * This is the function pointer type for window position callbacks. A window + * position callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int xpos, int ypos) + * @endcode + * + * @param[in] window The window that was moved. + * @param[in] xpos The new x-coordinate, in screen coordinates, of the + * upper-left corner of the content area of the window. + * @param[in] ypos The new y-coordinate, in screen coordinates, of the + * upper-left corner of the content area of the window. + * + * @sa @ref window_pos + * @sa @ref glfwSetWindowPosCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowposfun)(GLFWwindow* window, int xpos, int ypos); + +/*! @brief The function pointer type for window size callbacks. + * + * This is the function pointer type for window size callbacks. A window size + * callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int width, int height) + * @endcode + * + * @param[in] window The window that was resized. + * @param[in] width The new width, in screen coordinates, of the window. + * @param[in] height The new height, in screen coordinates, of the window. + * + * @sa @ref window_size + * @sa @ref glfwSetWindowSizeCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowsizefun)(GLFWwindow* window, int width, int height); + +/*! @brief The function pointer type for window close callbacks. + * + * This is the function pointer type for window close callbacks. A window + * close callback function has the following signature: + * @code + * void function_name(GLFWwindow* window) + * @endcode + * + * @param[in] window The window that the user attempted to close. + * + * @sa @ref window_close + * @sa @ref glfwSetWindowCloseCallback + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowclosefun)(GLFWwindow* window); + +/*! @brief The function pointer type for window content refresh callbacks. + * + * This is the function pointer type for window content refresh callbacks. + * A window content refresh callback function has the following signature: + * @code + * void function_name(GLFWwindow* window); + * @endcode + * + * @param[in] window The window whose content needs to be refreshed. + * + * @sa @ref window_refresh + * @sa @ref glfwSetWindowRefreshCallback + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowrefreshfun)(GLFWwindow* window); + +/*! @brief The function pointer type for window focus callbacks. + * + * This is the function pointer type for window focus callbacks. A window + * focus callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode + * + * @param[in] window The window that gained or lost input focus. + * @param[in] focused `GLFW_TRUE` if the window was given input focus, or + * `GLFW_FALSE` if it lost it. + * + * @sa @ref window_focus + * @sa @ref glfwSetWindowFocusCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowfocusfun)(GLFWwindow* window, int focused); + +/*! @brief The function pointer type for window iconify callbacks. + * + * This is the function pointer type for window iconify callbacks. A window + * iconify callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode + * + * @param[in] window The window that was iconified or restored. + * @param[in] iconified `GLFW_TRUE` if the window was iconified, or + * `GLFW_FALSE` if it was restored. + * + * @sa @ref window_iconify + * @sa @ref glfwSetWindowIconifyCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowiconifyfun)(GLFWwindow* window, int iconified); + +/*! @brief The function pointer type for window maximize callbacks. + * + * This is the function pointer type for window maximize callbacks. A window + * maximize callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode + * + * @param[in] window The window that was maximized or restored. + * @param[in] maximized `GLFW_TRUE` if the window was maximized, or + * `GLFW_FALSE` if it was restored. + * + * @sa @ref window_maximize + * @sa glfwSetWindowMaximizeCallback + * + * @since Added in version 3.3. + * + * @ingroup window + */ +typedef void (* GLFWwindowmaximizefun)(GLFWwindow* window, int maximized); + +/*! @brief The function pointer type for framebuffer size callbacks. + * + * This is the function pointer type for framebuffer size callbacks. + * A framebuffer size callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * + * @param[in] window The window whose framebuffer was resized. + * @param[in] width The new width, in pixels, of the framebuffer. + * @param[in] height The new height, in pixels, of the framebuffer. + * + * @sa @ref window_fbsize + * @sa @ref glfwSetFramebufferSizeCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWframebuffersizefun)(GLFWwindow* window, int width, int height); + +/*! @brief The function pointer type for window content scale callbacks. + * + * This is the function pointer type for window content scale callbacks. + * A window content scale callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode + * + * @param[in] window The window whose content scale changed. + * @param[in] xscale The new x-axis content scale of the window. + * @param[in] yscale The new y-axis content scale of the window. + * + * @sa @ref window_scale + * @sa @ref glfwSetWindowContentScaleCallback + * + * @since Added in version 3.3. + * + * @ingroup window + */ +typedef void (* GLFWwindowcontentscalefun)(GLFWwindow* window, float xscale, float yscale); + +/*! @brief The function pointer type for mouse button callbacks. + * + * This is the function pointer type for mouse button callback functions. + * A mouse button callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] button The [mouse button](@ref buttons) that was pressed or + * released. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases + * may add more actions. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_mouse_button + * @sa @ref glfwSetMouseButtonCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle and modifier mask parameters. + * + * @ingroup input + */ +typedef void (* GLFWmousebuttonfun)(GLFWwindow* window, int button, int action, int mods); + +/*! @brief The function pointer type for cursor position callbacks. + * + * This is the function pointer type for cursor position callbacks. A cursor + * position callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] xpos The new cursor x-coordinate, relative to the left edge of + * the content area. + * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the + * content area. + * + * @sa @ref cursor_pos + * @sa @ref glfwSetCursorPosCallback + * + * @since Added in version 3.0. Replaces `GLFWmouseposfun`. + * + * @ingroup input + */ +typedef void (* GLFWcursorposfun)(GLFWwindow* window, double xpos, double ypos); + +/*! @brief The function pointer type for cursor enter/leave callbacks. + * + * This is the function pointer type for cursor enter/leave callbacks. + * A cursor enter/leave callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] entered `GLFW_TRUE` if the cursor entered the window's content + * area, or `GLFW_FALSE` if it left it. + * + * @sa @ref cursor_enter + * @sa @ref glfwSetCursorEnterCallback + * + * @since Added in version 3.0. + * + * @ingroup input + */ +typedef void (* GLFWcursorenterfun)(GLFWwindow* window, int entered); + +/*! @brief The function pointer type for scroll callbacks. + * + * This is the function pointer type for scroll callbacks. A scroll callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] xoffset The scroll offset along the x-axis. + * @param[in] yoffset The scroll offset along the y-axis. + * + * @sa @ref scrolling + * @sa @ref glfwSetScrollCallback + * + * @since Added in version 3.0. Replaces `GLFWmousewheelfun`. + * + * @ingroup input + */ +typedef void (* GLFWscrollfun)(GLFWwindow* window, double xoffset, double yoffset); + +/*! @brief The function pointer type for keyboard key callbacks. + * + * This is the function pointer type for keyboard key callbacks. A keyboard + * key callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] key The [keyboard key](@ref keys) that was pressed or released. + * @param[in] scancode The system-specific scancode of the key. + * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future + * releases may add more actions. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_key + * @sa @ref glfwSetKeyCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle, scancode and modifier mask parameters. + * + * @ingroup input + */ +typedef void (* GLFWkeyfun)(GLFWwindow* window, int key, int scancode, int action, int mods); + +/*! @brief The function pointer type for Unicode character callbacks. + * + * This is the function pointer type for Unicode character callbacks. + * A Unicode character callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * + * @sa @ref input_char + * @sa @ref glfwSetCharCallback + * + * @since Added in version 2.4. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +typedef void (* GLFWcharfun)(GLFWwindow* window, unsigned int codepoint); + +/*! @brief The function pointer type for Unicode character with modifiers + * callbacks. + * + * This is the function pointer type for Unicode character with modifiers + * callbacks. It is called for each input character, regardless of what + * modifier keys are held down. A Unicode character with modifiers callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_char + * @sa @ref glfwSetCharModsCallback + * + * @deprecated Scheduled for removal in version 4.0. + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int mods); + +/*! @brief The function pointer type for path drop callbacks. + * + * This is the function pointer type for path drop callbacks. A path drop + * callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] path_count The number of dropped paths. + * @param[in] paths The UTF-8 encoded file and/or directory path names. + * + * @pointer_lifetime The path array and its strings are valid until the + * callback function returns. + * + * @sa @ref path_drop + * @sa @ref glfwSetDropCallback + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef void (* GLFWdropfun)(GLFWwindow* window, int path_count, const char* paths[]); + +/*! @brief The function pointer type for monitor configuration callbacks. + * + * This is the function pointer type for monitor configuration callbacks. + * A monitor callback function has the following signature: + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode + * + * @param[in] monitor The monitor that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. + * + * @sa @ref monitor_event + * @sa @ref glfwSetMonitorCallback + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event); + +/*! @brief The function pointer type for joystick configuration callbacks. + * + * This is the function pointer type for joystick configuration callbacks. + * A joystick configuration callback function has the following signature: + * @code + * void function_name(int jid, int event) + * @endcode + * + * @param[in] jid The joystick that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. + * + * @sa @ref joystick_event + * @sa @ref glfwSetJoystickCallback + * + * @since Added in version 3.2. + * + * @ingroup input + */ +typedef void (* GLFWjoystickfun)(int jid, int event); + +/*! @brief Video mode type. + * + * This describes a single video mode. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoMode + * @sa @ref glfwGetVideoModes + * + * @since Added in version 1.0. + * @glfw3 Added refresh rate member. + * + * @ingroup monitor + */ +typedef struct GLFWvidmode +{ + /*! The width, in screen coordinates, of the video mode. + */ + int width; + /*! The height, in screen coordinates, of the video mode. + */ + int height; + /*! The bit depth of the red channel of the video mode. + */ + int redBits; + /*! The bit depth of the green channel of the video mode. + */ + int greenBits; + /*! The bit depth of the blue channel of the video mode. + */ + int blueBits; + /*! The refresh rate, in Hz, of the video mode. + */ + int refreshRate; +} GLFWvidmode; + +/*! @brief Gamma ramp. + * + * This describes the gamma ramp for a monitor. + * + * @sa @ref monitor_gamma + * @sa @ref glfwGetGammaRamp + * @sa @ref glfwSetGammaRamp + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef struct GLFWgammaramp +{ + /*! An array of value describing the response of the red channel. + */ + unsigned short* red; + /*! An array of value describing the response of the green channel. + */ + unsigned short* green; + /*! An array of value describing the response of the blue channel. + */ + unsigned short* blue; + /*! The number of elements in each array. + */ + unsigned int size; +} GLFWgammaramp; + +/*! @brief Image data. + * + * This describes a single 2D image. See the documentation for each related + * function what the expected pixel format is. + * + * @sa @ref cursor_custom + * @sa @ref window_icon + * + * @since Added in version 2.1. + * @glfw3 Removed format and bytes-per-pixel members. + * + * @ingroup window + */ +typedef struct GLFWimage +{ + /*! The width, in pixels, of this image. + */ + int width; + /*! The height, in pixels, of this image. + */ + int height; + /*! The pixel data of this image, arranged left-to-right, top-to-bottom. + */ + unsigned char* pixels; +} GLFWimage; + +/*! @brief Gamepad input state + * + * This describes the input state of a gamepad. + * + * @sa @ref gamepad + * @sa @ref glfwGetGamepadState + * + * @since Added in version 3.3. + * + * @ingroup input + */ +typedef struct GLFWgamepadstate +{ + /*! The states of each [gamepad button](@ref gamepad_buttons), `GLFW_PRESS` + * or `GLFW_RELEASE`. + */ + unsigned char buttons[15]; + /*! The states of each [gamepad axis](@ref gamepad_axes), in the range -1.0 + * to 1.0 inclusive. + */ + float axes[6]; +} GLFWgamepadstate; + + +/************************************************************************* + * GLFW API functions + *************************************************************************/ + +/*! @brief Initializes the GLFW library. + * + * This function initializes the GLFW library. Before most GLFW functions can + * be used, GLFW must be initialized, and before an application terminates GLFW + * should be terminated in order to free any resources allocated during or + * after initialization. + * + * If this function fails, it calls @ref glfwTerminate before returning. If it + * succeeds, you should call @ref glfwTerminate before the application exits. + * + * Additional calls to this function after successful initialization but before + * termination will return `GLFW_TRUE` immediately. + * + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * + * @remark @macos This function will change the current directory of the + * application to the `Contents/Resources` subdirectory of the application's + * bundle, if present. This can be disabled with the @ref + * GLFW_COCOA_CHDIR_RESOURCES init hint. + * + * @remark @x11 This function will set the `LC_CTYPE` category of the + * application locale according to the current environment if that category is + * still "C". This is because the "C" locale breaks Unicode text input. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref intro_init + * @sa @ref glfwTerminate + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI int glfwInit(void); + +/*! @brief Terminates the GLFW library. + * + * This function destroys all remaining windows and cursors, restores any + * modified gamma ramps and frees any other allocated resources. Once this + * function is called, you must again call @ref glfwInit successfully before + * you will be able to use most GLFW functions. + * + * If GLFW has been successfully initialized, this function should be called + * before the application exits. If initialization fails, there is no need to + * call this function, as it is called by @ref glfwInit before it returns + * failure. + * + * This function has no effect if GLFW is not initialized. + * + * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * + * @remark This function may be called before @ref glfwInit. + * + * @warning The contexts of any remaining windows must not be current on any + * other thread when this function is called. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref intro_init + * @sa @ref glfwInit + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI void glfwTerminate(void); + +/*! @brief Sets the specified init hint to the desired value. + * + * This function sets hints for the next initialization of GLFW. + * + * The values you set hints to are never reset by GLFW, but they only take + * effect during initialization. Once GLFW has been initialized, any values + * you set will be ignored until the library is terminated and initialized + * again. + * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * + * @param[in] hint The [init hint](@ref init_hints) to set. + * @param[in] value The new value of the init hint. + * + * @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref + * GLFW_INVALID_VALUE. + * + * @remarks This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa init_hints + * @sa glfwInit + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI void glfwInitHint(int hint, int value); + +/*! @brief Retrieves the version of the GLFW library. + * + * This function retrieves the major, minor and revision numbers of the GLFW + * library. It is intended for when you are using GLFW as a shared library and + * want to ensure that you are using the minimum required version. + * + * Any or all of the version arguments may be `NULL`. + * + * @param[out] major Where to store the major version number, or `NULL`. + * @param[out] minor Where to store the minor version number, or `NULL`. + * @param[out] rev Where to store the revision number, or `NULL`. + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref intro_version + * @sa @ref glfwGetVersionString + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); + +/*! @brief Returns a string describing the compile-time configuration. + * + * This function returns the compile-time generated + * [version string](@ref intro_version_string) of the GLFW library binary. It + * describes the version, platform, compiler and any platform-specific + * compile-time options. It should not be confused with the OpenGL or OpenGL + * ES version string, queried with `glGetString`. + * + * __Do not use the version string__ to parse the GLFW library version. The + * @ref glfwGetVersion function provides the version of the running library + * binary in numerical format. + * + * @return The ASCII encoded GLFW version string. + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @pointer_lifetime The returned string is static and compile-time generated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref intro_version + * @sa @ref glfwGetVersion + * + * @since Added in version 3.0. + * + * @ingroup init + */ +GLFWAPI const char* glfwGetVersionString(void); + +/*! @brief Returns and clears the last error for the calling thread. + * + * This function returns and clears the [error code](@ref errors) of the last + * error that occurred on the calling thread, and optionally a UTF-8 encoded + * human-readable description of it. If no error has occurred since the last + * call, it returns @ref GLFW_NO_ERROR (zero) and the description pointer is + * set to `NULL`. + * + * @param[in] description Where to store the error description pointer, or `NULL`. + * @return The last error code for the calling thread, or @ref GLFW_NO_ERROR + * (zero). + * + * @errors None. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * next error occurs or the library is terminated. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref error_handling + * @sa @ref glfwSetErrorCallback + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI int glfwGetError(const char** description); + +/*! @brief Sets the error callback. + * + * This function sets the error callback, which is called with an error code + * and a human-readable description each time a GLFW error occurs. + * + * The error code is set before the callback is called. Calling @ref + * glfwGetError from the error callback will return the same value as the error + * code argument. + * + * The error callback is called on the thread where the error occurred. If you + * are using GLFW from multiple threads, your error callback needs to be + * written accordingly. + * + * Because the description string may have been generated specifically for that + * error, it is not guaranteed to be valid after the callback has returned. If + * you wish to use it after the callback returns, you need to make a copy. + * + * Once set, the error callback remains set even after the library has been + * terminated. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set. + * + * @callback_signature + * @code + * void callback_name(int error_code, const char* description) + * @endcode + * For more information about the callback parameters, see the + * [callback pointer type](@ref GLFWerrorfun). + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref error_handling + * @sa @ref glfwGetError + * + * @since Added in version 3.0. + * + * @ingroup init + */ +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback); + +/*! @brief Returns the currently connected monitors. + * + * This function returns an array of handles for all currently connected + * monitors. The primary monitor is always first in the returned array. If no + * monitors were found, this function returns `NULL`. + * + * @param[out] count Where to store the number of monitors in the returned + * array. This is set to zero if an error occurred. + * @return An array of monitor handles, or `NULL` if no monitors were found or + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * monitor configuration changes or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_monitors + * @sa @ref monitor_event + * @sa @ref glfwGetPrimaryMonitor + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); + +/*! @brief Returns the primary monitor. + * + * This function returns the primary monitor. This is usually the monitor + * where elements like the task bar or global menu bar are located. + * + * @return The primary monitor, or `NULL` if no monitors were found or if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @remark The primary monitor is always first in the array returned by @ref + * glfwGetMonitors. + * + * @sa @ref monitor_monitors + * @sa @ref glfwGetMonitors + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); + +/*! @brief Returns the position of the monitor's viewport on the virtual screen. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the specified monitor. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); + +/*! @brief Retrieves the work area of the monitor. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the work area of the specified monitor along with the work area + * size in screen coordinates. The work area is defined as the area of the + * monitor not occluded by the operating system task bar where present. If no + * task bar exists then the work area is the monitor resolution in screen + * coordinates. + * + * Any or all of the position and size arguments may be `NULL`. If an error + * occurs, all non-`NULL` position and size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * @param[out] width Where to store the monitor width, or `NULL`. + * @param[out] height Where to store the monitor height, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_workarea + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); + +/*! @brief Returns the physical size of the monitor. + * + * This function returns the size, in millimetres, of the display area of the + * specified monitor. + * + * Some systems do not provide accurate monitor size information, either + * because the monitor + * [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data) + * data is incorrect or because the driver does not report it accurately. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] widthMM Where to store the width, in millimetres, of the + * monitor's display area, or `NULL`. + * @param[out] heightMM Where to store the height, in millimetres, of the + * monitor's display area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @win32 On Windows 8 and earlier the physical size is calculated from + * the current resolution and system DPI instead of querying the monitor EDID data. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); + +/*! @brief Retrieves the content scale for the specified monitor. + * + * This function retrieves the content scale for the specified monitor. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. + * + * The content scale may depend on both the monitor resolution and pixel + * density and on user settings. It may be very different from the raw DPI + * calculated from the physical size and current resolution. + * + * @param[in] monitor The monitor to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale); + +/*! @brief Returns the name of the specified monitor. + * + * This function returns a human-readable name, encoded as UTF-8, of the + * specified monitor. The name typically reflects the make and model of the + * monitor and is not guaranteed to be unique among the connected monitors. + * + * @param[in] monitor The monitor to query. + * @return The UTF-8 encoded name of the monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); + +/*! @brief Sets the user pointer of the specified monitor. + * + * This function sets the user-defined pointer of the specified monitor. The + * current value is retained until the monitor is disconnected. The initial + * value is `NULL`. + * + * This function may be called from the monitor callback, even for a monitor + * that is being disconnected. + * + * @param[in] monitor The monitor whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref monitor_userptr + * @sa @ref glfwGetMonitorUserPointer + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* monitor, void* pointer); + +/*! @brief Returns the user pointer of the specified monitor. + * + * This function returns the current value of the user-defined pointer of the + * specified monitor. The initial value is `NULL`. + * + * This function may be called from the monitor callback, even for a monitor + * that is being disconnected. + * + * @param[in] monitor The monitor whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref monitor_userptr + * @sa @ref glfwSetMonitorUserPointer + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); + +/*! @brief Sets the monitor configuration callback. + * + * This function sets the monitor configuration callback, or removes the + * currently set callback. This is called when a monitor is connected to or + * disconnected from the system. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmonitorfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_event + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun callback); + +/*! @brief Returns the available video modes for the specified monitor. + * + * This function returns an array of all video modes supported by the specified + * monitor. The returned array is sorted in ascending order, first by color + * bit depth (the sum of all channel depths), then by resolution area (the + * product of width and height), then resolution width and finally by refresh + * rate. + * + * @param[in] monitor The monitor to query. + * @param[out] count Where to store the number of video modes in the returned + * array. This is set to zero if an error occurred. + * @return An array of video modes, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected, this function is called again for that monitor or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoMode + * + * @since Added in version 1.0. + * @glfw3 Changed to return an array of modes for a specific monitor. + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); + +/*! @brief Returns the current mode of the specified monitor. + * + * This function returns the current video mode of the specified monitor. If + * you have created a full screen window for that monitor, the return value + * will depend on whether that window is iconified. + * + * @param[in] monitor The monitor to query. + * @return The current mode of the monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoModes + * + * @since Added in version 3.0. Replaces `glfwGetDesktopMode`. + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); + +/*! @brief Generates a gamma ramp and sets it for the specified monitor. + * + * This function generates an appropriately sized gamma ramp from the specified + * exponent and then calls @ref glfwSetGammaRamp with it. The value must be + * a finite number greater than zero. + * + * The software controlled gamma ramp is applied _in addition_ to the hardware + * gamma correction, which today is usually an approximation of sRGB gamma. + * This means that setting a perfectly linear ramp, or gamma 1.0, will produce + * the default (usually sRGB-like) behavior. + * + * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref + * GLFW_SRGB_CAPABLE hint. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] gamma The desired exponent. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark @wayland Gamma handling is a privileged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); + +/*! @brief Returns the current gamma ramp for the specified monitor. + * + * This function returns the current gamma ramp of the specified monitor. + * + * @param[in] monitor The monitor to query. + * @return The current gamma ramp, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland Gamma handling is a privileged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while + * returning `NULL`. + * + * @pointer_lifetime The returned structure and its arrays are allocated and + * freed by GLFW. You should not free them yourself. They are valid until the + * specified monitor is disconnected, this function is called again for that + * monitor or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); + +/*! @brief Sets the current gamma ramp for the specified monitor. + * + * This function sets the current gamma ramp for the specified monitor. The + * original gamma ramp for that monitor is saved by GLFW the first time this + * function is called and is restored by @ref glfwTerminate. + * + * The software controlled gamma ramp is applied _in addition_ to the hardware + * gamma correction, which today is usually an approximation of sRGB gamma. + * This means that setting a perfectly linear ramp, or gamma 1.0, will produce + * the default (usually sRGB-like) behavior. + * + * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref + * GLFW_SRGB_CAPABLE hint. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] ramp The gamma ramp to use. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark The size of the specified gamma ramp should match the size of the + * current ramp for that monitor. + * + * @remark @win32 The gamma ramp size must be 256. + * + * @remark @wayland Gamma handling is a privileged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified gamma ramp is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +/*! @brief Resets all window hints to their default values. + * + * This function resets all window hints to their + * [default values](@ref window_hints_values). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwWindowHint + * @sa @ref glfwWindowHintString + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwDefaultWindowHints(void); + +/*! @brief Sets the specified window hint to the desired value. + * + * This function sets hints for the next call to @ref glfwCreateWindow. The + * hints, once set, retain their values until changed by a call to this + * function or @ref glfwDefaultWindowHints, or until the library is terminated. + * + * Only integer value hints can be set with this function. String value hints + * are set with @ref glfwWindowHintString. + * + * This function does not check whether the specified hint values are valid. + * If you set hints to invalid values this will instead be reported by the next + * call to @ref glfwCreateWindow. + * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * + * @param[in] hint The [window hint](@ref window_hints) to set. + * @param[in] value The new value of the window hint. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwWindowHintString + * @sa @ref glfwDefaultWindowHints + * + * @since Added in version 3.0. Replaces `glfwOpenWindowHint`. + * + * @ingroup window + */ +GLFWAPI void glfwWindowHint(int hint, int value); + +/*! @brief Sets the specified window hint to the desired value. + * + * This function sets hints for the next call to @ref glfwCreateWindow. The + * hints, once set, retain their values until changed by a call to this + * function or @ref glfwDefaultWindowHints, or until the library is terminated. + * + * Only string type hints can be set with this function. Integer value hints + * are set with @ref glfwWindowHint. + * + * This function does not check whether the specified hint values are valid. + * If you set hints to invalid values this will instead be reported by the next + * call to @ref glfwCreateWindow. + * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * + * @param[in] hint The [window hint](@ref window_hints) to set. + * @param[in] value The new value of the window hint. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwWindowHint + * @sa @ref glfwDefaultWindowHints + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwWindowHintString(int hint, const char* value); + +/*! @brief Creates a window and its associated context. + * + * This function creates a window and its associated OpenGL or OpenGL ES + * context. Most of the options controlling how the window and its context + * should be created are specified with [window hints](@ref window_hints). + * + * Successful creation does not change which context is current. Before you + * can use the newly created context, you need to + * [make it current](@ref context_current). For information about the `share` + * parameter, see @ref context_sharing. + * + * The created window, framebuffer and context may differ from what you + * requested, as not all parameters and hints are + * [hard constraints](@ref window_hints_hard). This includes the size of the + * window, especially for full screen windows. To query the actual attributes + * of the created window, framebuffer and context, see @ref + * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize. + * + * To create a full screen window, you need to specify the monitor the window + * will cover. If no monitor is specified, the window will be windowed mode. + * Unless you have a way for the user to choose a specific monitor, it is + * recommended that you pick the primary monitor. For more information on how + * to query connected monitors, see @ref monitor_monitors. + * + * For full screen windows, the specified size becomes the resolution of the + * window's _desired video mode_. As long as a full screen window is not + * iconified, the supported video mode most closely matching the desired video + * mode is set for the specified monitor. For more information about full + * screen windows, including the creation of so called _windowed full screen_ + * or _borderless full screen_ windows, see @ref window_windowed_full_screen. + * + * Once you have created the window, you can switch it between windowed and + * full screen mode with @ref glfwSetWindowMonitor. This will not affect its + * OpenGL or OpenGL ES context. + * + * By default, newly created windows use the placement recommended by the + * window system. To create the window at a specific position, make it + * initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window + * hint, set its [position](@ref window_pos) and then [show](@ref window_hide) + * it. + * + * As long as at least one full screen window is not iconified, the screensaver + * is prohibited from starting. + * + * Window systems put limits on window sizes. Very large or very small window + * dimensions may be overridden by the window system on creation. Check the + * actual [size](@ref window_size) after creation. + * + * The [swap interval](@ref buffer_swap) is not set during window creation and + * the initial value may vary depending on driver settings and defaults. + * + * @param[in] width The desired width, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] height The desired height, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] title The initial, UTF-8 encoded window title. + * @param[in] monitor The monitor to use for full screen mode, or `NULL` for + * windowed mode. + * @param[in] share The window whose context to share resources with, or `NULL` + * to not share resources. + * @return The handle of the created window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref + * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @win32 Window creation will fail if the Microsoft GDI software + * OpenGL implementation is the only one available. + * + * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it + * will be set as the initial icon for the window. If no such icon is present, + * the `IDI_APPLICATION` icon will be used instead. To set a different icon, + * see @ref glfwSetWindowIcon. + * + * @remark @win32 The context to share resources with must not be current on + * any other thread. + * + * @remark @macos The OS only supports forward-compatible core profile contexts + * for OpenGL versions 3.2 and later. Before creating an OpenGL context of + * version 3.2 or later you must set the + * [GLFW_OPENGL_FORWARD_COMPAT](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) and + * [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint) hints accordingly. + * OpenGL 3.0 and 3.1 contexts are not supported at all on macOS. + * + * @remark @macos The GLFW window has no icon, as it is not a document + * window, but the dock icon will be the same as the application bundle's icon. + * For more information on bundles, see the + * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) + * in the Mac Developer Library. + * + * @remark @macos The first time a window is created the menu bar is created. + * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu + * bar. Otherwise a minimal menu bar is created manually with common commands + * like Hide, Quit and About. The About entry opens a minimal about dialog + * with information from the application's bundle. Menu bar creation can be + * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint. + * + * @remark @macos On OS X 10.10 and later the window frame will not be rendered + * at full resolution on Retina displays unless the + * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) + * hint is `GLFW_TRUE` and the `NSHighResolutionCapable` key is enabled in the + * application bundle's `Info.plist`. For more information, see + * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) + * in the Mac Developer Library. The GLFW test and example programs use + * a custom `Info.plist` template for this, which can be found as + * `CMake/MacOSXBundleInfo.plist.in` in the source tree. + * + * @remark @macos When activating frame autosaving with + * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified + * window size and position may be overridden by previously saved values. + * + * @remark @x11 Some window managers will not respect the placement of + * initially hidden windows. + * + * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for + * a window to reach its requested state. This means you may not be able to + * query the final size, position or other attributes directly after window + * creation. + * + * @remark @x11 The class part of the `WM_CLASS` window property will by + * default be set to the window title passed to this function. The instance + * part will use the contents of the `RESOURCE_NAME` environment variable, if + * present and not empty, or fall back to the window title. Set the + * [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and + * [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to + * override this. + * + * @remark @wayland Compositors should implement the xdg-decoration protocol + * for GLFW to decorate the window properly. If this protocol isn't + * supported, or if the compositor prefers client-side decorations, a very + * simple fallback frame will be drawn using the wp_viewporter protocol. A + * compositor can still emit close, maximize or fullscreen events, using for + * instance a keybind mechanism. If neither of these protocols is supported, + * the window won't be decorated. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size or refresh rate. + * + * @remark @wayland Screensaver inhibition requires the idle-inhibit protocol + * to be implemented in the user's compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_creation + * @sa @ref glfwDestroyWindow + * + * @since Added in version 3.0. Replaces `glfwOpenWindow`. + * + * @ingroup window + */ +GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share); + +/*! @brief Destroys the specified window and its context. + * + * This function destroys the specified window and its context. On calling + * this function, no further callbacks will be called for that window. + * + * If the context of the specified window is current on the main thread, it is + * detached before being destroyed. + * + * @param[in] window The window to destroy. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @note The context of the specified window must not be current on any other + * thread when this function is called. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_creation + * @sa @ref glfwCreateWindow + * + * @since Added in version 3.0. Replaces `glfwCloseWindow`. + * + * @ingroup window + */ +GLFWAPI void glfwDestroyWindow(GLFWwindow* window); + +/*! @brief Checks the close flag of the specified window. + * + * This function returns the value of the close flag of the specified window. + * + * @param[in] window The window to query. + * @return The value of the close flag. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_close + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); + +/*! @brief Sets the close flag of the specified window. + * + * This function sets the value of the close flag of the specified window. + * This can be used to override the user's attempt to close the window, or + * to signal that it should be closed. + * + * @param[in] window The window whose flag to change. + * @param[in] value The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_close + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); + +/*! @brief Sets the title of the specified window. + * + * This function sets the window title, encoded as UTF-8, of the specified + * window. + * + * @param[in] window The window whose title to change. + * @param[in] title The UTF-8 encoded window title. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @macos The window title will not be updated until the next time you + * process events. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_title + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); + +/*! @brief Sets the icon for the specified window. + * + * This function sets the icon of the specified window. If passed an array of + * candidate images, those of or closest to the sizes desired by the system are + * selected. If no images are specified, the window reverts to its default + * icon. + * + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. + * + * The desired image sizes varies depending on platform and system settings. + * The selected images will be rescaled as needed. Good sizes include 16x16, + * 32x32 and 48x48. + * + * @param[in] window The window whose icon to set. + * @param[in] count The number of images in the specified array, or zero to + * revert to the default window icon. + * @param[in] images The images to create the icon from. This is ignored if + * count is zero. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified image data is copied before this function + * returns. + * + * @remark @macos The GLFW window has no icon, as it is not a document + * window, so this function does nothing. The dock icon will be the same as + * the application bundle's icon. For more information on bundles, see the + * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) + * in the Mac Developer Library. + * + * @remark @wayland There is no existing protocol to change an icon, the + * window will thus inherit the one defined in the application's desktop file. + * This function always emits @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_icon + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); + +/*! @brief Retrieves the position of the content area of the specified window. + * + * This function retrieves the position, in screen coordinates, of the + * upper-left corner of the content area of the specified window. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] window The window to query. + * @param[out] xpos Where to store the x-coordinate of the upper-left corner of + * the content area, or `NULL`. + * @param[out] ypos Where to store the y-coordinate of the upper-left corner of + * the content area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no way for an application to retrieve the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * @sa @ref glfwSetWindowPos + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); + +/*! @brief Sets the position of the content area of the specified window. + * + * This function sets the position, in screen coordinates, of the upper-left + * corner of the content area of the specified windowed mode window. If the + * window is a full screen window, this function does nothing. + * + * __Do not use this function__ to move an already visible window unless you + * have very good reasons for doing so, as it will confuse and annoy the user. + * + * The window manager may put limits on what positions are allowed. GLFW + * cannot and should not override these limits. + * + * @param[in] window The window to query. + * @param[in] xpos The x-coordinate of the upper-left corner of the content area. + * @param[in] ypos The y-coordinate of the upper-left corner of the content area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no way for an application to set the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * @sa @ref glfwGetWindowPos + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); + +/*! @brief Retrieves the size of the content area of the specified window. + * + * This function retrieves the size, in screen coordinates, of the content area + * of the specified window. If you wish to retrieve the size of the + * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose size to retrieve. + * @param[out] width Where to store the width, in screen coordinates, of the + * content area, or `NULL`. + * @param[out] height Where to store the height, in screen coordinates, of the + * content area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * @sa @ref glfwSetWindowSize + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Sets the size limits of the specified window. + * + * This function sets the size limits of the content area of the specified + * window. If the window is full screen, the size limits only take effect + * once it is made windowed. If the window is not resizable, this function + * does nothing. + * + * The size limits are applied immediately to a windowed mode window and may + * cause it to be resized. + * + * The maximum dimensions must be greater than or equal to the minimum + * dimensions and all must be greater than or equal to zero. + * + * @param[in] window The window to set limits for. + * @param[in] minwidth The minimum width, in screen coordinates, of the content + * area, or `GLFW_DONT_CARE`. + * @param[in] minheight The minimum height, in screen coordinates, of the + * content area, or `GLFW_DONT_CARE`. + * @param[in] maxwidth The maximum width, in screen coordinates, of the content + * area, or `GLFW_DONT_CARE`. + * @param[in] maxheight The maximum height, in screen coordinates, of the + * content area, or `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @remark @wayland The size limits will not be applied until the window is + * actually resized, either by the user or by the compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa @ref glfwSetWindowAspectRatio + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); + +/*! @brief Sets the aspect ratio of the specified window. + * + * This function sets the required aspect ratio of the content area of the + * specified window. If the window is full screen, the aspect ratio only takes + * effect once it is made windowed. If the window is not resizable, this + * function does nothing. + * + * The aspect ratio is specified as a numerator and a denominator and both + * values must be greater than zero. For example, the common 16:9 aspect ratio + * is specified as 16 and 9, respectively. + * + * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect + * ratio limit is disabled. + * + * The aspect ratio is applied immediately to a windowed mode window and may + * cause it to be resized. + * + * @param[in] window The window to set limits for. + * @param[in] numer The numerator of the desired aspect ratio, or + * `GLFW_DONT_CARE`. + * @param[in] denom The denominator of the desired aspect ratio, or + * `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @remark @wayland The aspect ratio will not be applied until the window is + * actually resized, either by the user or by the compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa @ref glfwSetWindowSizeLimits + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); + +/*! @brief Sets the size of the content area of the specified window. + * + * This function sets the size, in screen coordinates, of the content area of + * the specified window. + * + * For full screen windows, this function updates the resolution of its desired + * video mode and switches to the video mode closest to it, without affecting + * the window's context. As the context is unaffected, the bit depths of the + * framebuffer remain unchanged. + * + * If you wish to update the refresh rate of the desired video mode in addition + * to its resolution, see @ref glfwSetWindowMonitor. + * + * The window manager may put limits on what sizes are allowed. GLFW cannot + * and should not override these limits. + * + * @param[in] window The window to resize. + * @param[in] width The desired width, in screen coordinates, of the window + * content area. + * @param[in] height The desired height, in screen coordinates, of the window + * content area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * @sa @ref glfwGetWindowSize + * @sa @ref glfwSetWindowMonitor + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); + +/*! @brief Retrieves the size of the framebuffer of the specified window. + * + * This function retrieves the size, in pixels, of the framebuffer of the + * specified window. If you wish to retrieve the size of the window in screen + * coordinates, see @ref glfwGetWindowSize. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose framebuffer to query. + * @param[out] width Where to store the width, in pixels, of the framebuffer, + * or `NULL`. + * @param[out] height Where to store the height, in pixels, of the framebuffer, + * or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_fbsize + * @sa @ref glfwSetFramebufferSizeCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Retrieves the size of the frame of the window. + * + * This function retrieves the size, in screen coordinates, of each edge of the + * frame of the specified window. This size includes the title bar, if the + * window has one. The size of the frame may vary depending on the + * [window-related hints](@ref window_hints_wnd) used to create it. + * + * Because this function retrieves the size of each window frame edge and not + * the offset along a particular coordinate axis, the retrieved values will + * always be zero or positive. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose frame size to query. + * @param[out] left Where to store the size, in screen coordinates, of the left + * edge of the window frame, or `NULL`. + * @param[out] top Where to store the size, in screen coordinates, of the top + * edge of the window frame, or `NULL`. + * @param[out] right Where to store the size, in screen coordinates, of the + * right edge of the window frame, or `NULL`. + * @param[out] bottom Where to store the size, in screen coordinates, of the + * bottom edge of the window frame, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * + * @since Added in version 3.1. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); + +/*! @brief Retrieves the content scale for the specified window. + * + * This function retrieves the content scale for the specified window. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. + * + * On systems where each monitors can have its own content scale, the window + * content scale will depend on which monitor the system considers the window + * to be on. + * + * @param[in] window The window to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwSetWindowContentScaleCallback + * @sa @ref glfwGetMonitorContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale); + +/*! @brief Returns the opacity of the whole window. + * + * This function returns the opacity of the window, including any decorations. + * + * The opacity (or alpha) value is a positive finite number between zero and + * one, where zero is fully transparent and one is fully opaque. If the system + * does not support whole window transparency, this function always returns one. + * + * The initial opacity value for newly created windows is one. + * + * @param[in] window The window to query. + * @return The opacity value of the specified window. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_transparency + * @sa @ref glfwSetWindowOpacity + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window); + +/*! @brief Sets the opacity of the whole window. + * + * This function sets the opacity of the window, including any decorations. + * + * The opacity (or alpha) value is a positive finite number between zero and + * one, where zero is fully transparent and one is fully opaque. + * + * The initial opacity value for newly created windows is one. + * + * A window created with framebuffer transparency may not use whole window + * transparency. The results of doing this are undefined. + * + * @param[in] window The window to set the opacity for. + * @param[in] opacity The desired opacity of the specified window. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_transparency + * @sa @ref glfwGetWindowOpacity + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); + +/*! @brief Iconifies the specified window. + * + * This function iconifies (minimizes) the specified window if it was + * previously restored. If the window is already iconified, this function does + * nothing. + * + * If the specified window is a full screen window, the original monitor + * resolution is restored until the window is restored. + * + * @param[in] window The window to iconify. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no concept of iconification in wl_shell, this + * function will emit @ref GLFW_PLATFORM_ERROR when using this deprecated + * protocol. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwRestoreWindow + * @sa @ref glfwMaximizeWindow + * + * @since Added in version 2.1. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwIconifyWindow(GLFWwindow* window); + +/*! @brief Restores the specified window. + * + * This function restores the specified window if it was previously iconified + * (minimized) or maximized. If the window is already restored, this function + * does nothing. + * + * If the specified window is a full screen window, the resolution chosen for + * the window is restored on the selected monitor. + * + * @param[in] window The window to restore. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwIconifyWindow + * @sa @ref glfwMaximizeWindow + * + * @since Added in version 2.1. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwRestoreWindow(GLFWwindow* window); + +/*! @brief Maximizes the specified window. + * + * This function maximizes the specified window if it was previously not + * maximized. If the window is already maximized, this function does nothing. + * + * If the specified window is a full screen window, this function does nothing. + * + * @param[in] window The window to maximize. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @par Thread Safety + * This function may only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwIconifyWindow + * @sa @ref glfwRestoreWindow + * + * @since Added in GLFW 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); + +/*! @brief Makes the specified window visible. + * + * This function makes the specified window visible if it was previously + * hidden. If the window is already visible or is in full screen mode, this + * function does nothing. + * + * By default, windowed mode windows are focused when shown + * Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint + * to change this behavior for all newly created windows, or change the + * behavior for an existing window with @ref glfwSetWindowAttrib. + * + * @param[in] window The window to make visible. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland Because Wayland wants every frame of the desktop to be + * complete, this function does not immediately make the window visible. + * Instead it will become visible the next time the window framebuffer is + * updated after this call. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hide + * @sa @ref glfwHideWindow + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwShowWindow(GLFWwindow* window); + +/*! @brief Hides the specified window. + * + * This function hides the specified window if it was previously visible. If + * the window is already hidden or is in full screen mode, this function does + * nothing. + * + * @param[in] window The window to hide. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hide + * @sa @ref glfwShowWindow + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwHideWindow(GLFWwindow* window); + +/*! @brief Brings the specified window to front and sets input focus. + * + * This function brings the specified window to front and sets input focus. + * The window should already be visible and not iconified. + * + * By default, both windowed and full screen mode windows are focused when + * initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to + * disable this behavior. + * + * Also by default, windowed mode windows are focused when shown + * with @ref glfwShowWindow. Set the + * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior. + * + * __Do not use this function__ to steal focus from other applications unless + * you are certain that is what the user wants. Focus stealing can be + * extremely disruptive. + * + * For a less disruptive way of getting the user's attention, see + * [attention requests](@ref window_attention). + * + * @param[in] window The window to give input focus. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland It is not possible for an application to bring its windows + * to front, this function will always emit @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_focus + * @sa @ref window_attention + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwFocusWindow(GLFWwindow* window); + +/*! @brief Requests user attention to the specified window. + * + * This function requests user attention to the specified window. On + * platforms where this is not supported, attention is requested to the + * application as a whole. + * + * Once the user has given attention, usually by focusing the window or + * application, the system will end the request automatically. + * + * @param[in] window The window to request attention to. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @macos Attention is requested to the application as a whole, not the + * specific window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attention + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window); + +/*! @brief Returns the monitor that the window uses for full screen mode. + * + * This function returns the handle of the monitor that the specified window is + * in full screen on. + * + * @param[in] window The window to query. + * @return The monitor, or `NULL` if the window is in windowed mode or an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_monitor + * @sa @ref glfwSetWindowMonitor + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); + +/*! @brief Sets the mode, monitor, video mode and placement of a window. + * + * This function sets the monitor that the window uses for full screen mode or, + * if the monitor is `NULL`, makes it windowed mode. + * + * When setting a monitor, this function updates the width, height and refresh + * rate of the desired video mode and switches to the video mode closest to it. + * The window position is ignored when setting a monitor. + * + * When the monitor is `NULL`, the position, width and height are used to + * place the window content area. The refresh rate is ignored when no monitor + * is specified. + * + * If you only wish to update the resolution of a full screen window or the + * size of a windowed mode window, see @ref glfwSetWindowSize. + * + * When a window transitions from full screen to windowed mode, this function + * restores any previous window settings such as whether it is decorated, + * floating, resizable, has size or aspect ratio limits, etc. + * + * @param[in] window The window whose monitor, size or video mode to set. + * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. + * @param[in] xpos The desired x-coordinate of the upper-left corner of the + * content area. + * @param[in] ypos The desired y-coordinate of the upper-left corner of the + * content area. + * @param[in] width The desired with, in screen coordinates, of the content + * area or video mode. + * @param[in] height The desired height, in screen coordinates, of the content + * area or video mode. + * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, + * or `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark The OpenGL or OpenGL ES context will not be destroyed or otherwise + * affected by any resizing or mode switching, although you may need to update + * your viewport if the framebuffer size has changed. + * + * @remark @wayland The desired window position is ignored, as there is no way + * for an application to set this property. + * + * @remark @wayland Setting the window to full screen will not attempt to + * change the mode, no matter what the requested size or refresh rate. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_monitor + * @sa @ref window_full_screen + * @sa @ref glfwGetWindowMonitor + * @sa @ref glfwSetWindowSize + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); + +/*! @brief Returns an attribute of the specified window. + * + * This function returns the value of an attribute of the specified window or + * its OpenGL or OpenGL ES context. + * + * @param[in] window The window to query. + * @param[in] attrib The [window attribute](@ref window_attribs) whose value to + * return. + * @return The value of the attribute, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @remark Framebuffer related hints are not window attributes. See @ref + * window_attribs_fb for more information. + * + * @remark Zero is a valid value for many window and context related + * attributes so you cannot use a return value of zero as an indication of + * errors. However, this function should not fail as long as it is passed + * valid arguments and the library has been [initialized](@ref intro_init). + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attribs + * @sa @ref glfwSetWindowAttrib + * + * @since Added in version 3.0. Replaces `glfwGetWindowParam` and + * `glfwGetGLVersion`. + * + * @ingroup window + */ +GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); + +/*! @brief Sets an attribute of the specified window. + * + * This function sets the value of an attribute of the specified window. + * + * The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), + * [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), + * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib), + * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and + * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib). + * + * Some of these attributes are ignored for full screen windows. The new + * value will take effect if the window is later made windowed. + * + * Some of these attributes are ignored for windowed mode windows. The new + * value will take effect if the window is later made full screen. + * + * @param[in] window The window to set the attribute for. + * @param[in] attrib A supported window attribute. + * @param[in] value `GLFW_TRUE` or `GLFW_FALSE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark Calling @ref glfwGetWindowAttrib will always return the latest + * value, even if that value is ignored by the current mode of the window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attribs + * @sa @ref glfwGetWindowAttrib + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* window, int attrib, int value); + +/*! @brief Sets the user pointer of the specified window. + * + * This function sets the user-defined pointer of the specified window. The + * current value is retained until the window is destroyed. The initial value + * is `NULL`. + * + * @param[in] window The window whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_userptr + * @sa @ref glfwGetWindowUserPointer + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); + +/*! @brief Returns the user pointer of the specified window. + * + * This function returns the current value of the user-defined pointer of the + * specified window. The initial value is `NULL`. + * + * @param[in] window The window whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_userptr + * @sa @ref glfwSetWindowUserPointer + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); + +/*! @brief Sets the position callback for the specified window. + * + * This function sets the position callback of the specified window, which is + * called when the window is moved. The callback is provided with the + * position, in screen coordinates, of the upper-left corner of the content + * area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int xpos, int ypos) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowposfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland This callback will never be called, as there is no way for + * an application to know its global position. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback); + +/*! @brief Sets the size callback for the specified window. + * + * This function sets the size callback of the specified window, which is + * called when the window is resized. The callback is provided with the size, + * in screen coordinates, of the content area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowsizefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback); + +/*! @brief Sets the close callback for the specified window. + * + * This function sets the close callback of the specified window, which is + * called when the user attempts to close the window, for example by clicking + * the close widget in the title bar. + * + * The close flag is set before this callback is called, but you can modify it + * at any time with @ref glfwSetWindowShouldClose. + * + * The close callback is not triggered by @ref glfwDestroyWindow. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowclosefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @macos Selecting Quit from the application menu will trigger the + * close callback for all windows. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_close + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback); + +/*! @brief Sets the refresh callback for the specified window. + * + * This function sets the refresh callback of the specified window, which is + * called when the content area of the window needs to be redrawn, for example + * if the window has been exposed after having been covered by another window. + * + * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where + * the window contents are saved off-screen, this callback may be called only + * very infrequently or never at all. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowrefreshfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_refresh + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback); + +/*! @brief Sets the focus callback for the specified window. + * + * This function sets the focus callback of the specified window, which is + * called when the window gains or loses input focus. + * + * After the focus callback is called for a window that lost input focus, + * synthetic key and mouse button release events will be generated for all such + * that had been pressed. For more information, see @ref glfwSetKeyCallback + * and @ref glfwSetMouseButtonCallback. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowfocusfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_focus + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback); + +/*! @brief Sets the iconify callback for the specified window. + * + * This function sets the iconification callback of the specified window, which + * is called when the window is iconified or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowiconifyfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland The wl_shell protocol has no concept of iconification, + * this callback will never be called when using this deprecated protocol. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun callback); + +/*! @brief Sets the maximize callback for the specified window. + * + * This function sets the maximization callback of the specified window, which + * is called when the window is maximized or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowmaximizefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_maximize + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback); + +/*! @brief Sets the framebuffer resize callback for the specified window. + * + * This function sets the framebuffer resize callback of the specified window, + * which is called when the framebuffer of the specified window is resized. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWframebuffersizefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_fbsize + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun callback); + +/*! @brief Sets the window content scale callback for the specified window. + * + * This function sets the window content scale callback of the specified window, + * which is called when the content scale of the specified window changes. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowcontentscalefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun callback); + +/*! @brief Processes all pending events. + * + * This function processes only those events that are already in the event + * queue and then returns immediately. Processing events will cause the window + * and input callbacks associated with those events to be called. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. + * + * Event processing is not required for joystick input to work. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 1.0. + * + * @ingroup window + */ +GLFWAPI void glfwPollEvents(void); + +/*! @brief Waits until events are queued and processes them. + * + * This function puts the calling thread to sleep until at least one event is + * available in the event queue. Once one or more events are available, + * it behaves exactly like @ref glfwPollEvents, i.e. the events in the queue + * are processed and the function then returns immediately. Processing events + * will cause the window and input callbacks associated with those events to be + * called. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. + * + * Event processing is not required for joystick input to work. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 2.5. + * + * @ingroup window + */ +GLFWAPI void glfwWaitEvents(void); + +/*! @brief Waits with timeout until events are queued and processes them. + * + * This function puts the calling thread to sleep until at least one event is + * available in the event queue, or until the specified timeout is reached. If + * one or more events are available, it behaves exactly like @ref + * glfwPollEvents, i.e. the events in the queue are processed and the function + * then returns immediately. Processing events will cause the window and input + * callbacks associated with those events to be called. + * + * The timeout value must be a positive finite number. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. + * + * Event processing is not required for joystick input to work. + * + * @param[in] timeout The maximum amount of time, in seconds, to wait. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEvents + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwWaitEventsTimeout(double timeout); + +/*! @brief Posts an empty event to the event queue. + * + * This function posts an empty event from the current thread to the event + * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref events + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 3.1. + * + * @ingroup window + */ +GLFWAPI void glfwPostEmptyEvent(void); + +/*! @brief Returns the value of an input option for the specified window. + * + * This function returns the value of an input option for the specified window. + * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. + * + * @param[in] window The window to query. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); + +/*! @brief Sets an input option for the specified window. + * + * This function sets an input mode option for the specified window. The mode + * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. + * + * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor + * modes: + * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. + * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the + * content area of the window but does not restrict the cursor from leaving. + * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual + * and unlimited cursor movement. This is useful for implementing for + * example 3D camera controls. + * + * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to + * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are + * enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS` + * the next time it is called even if the key had been released before the + * call. This is useful when you are only interested in whether keys have been + * pressed but not when or in which order. + * + * If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either + * `GLFW_TRUE` to enable sticky mouse buttons, or `GLFW_FALSE` to disable it. + * If sticky mouse buttons are enabled, a mouse button press will ensure that + * @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even + * if the mouse button had been released before the call. This is useful when + * you are only interested in whether mouse buttons have been pressed but not + * when or in which order. + * + * If the mode is `GLFW_LOCK_KEY_MODS`, the value must be either `GLFW_TRUE` to + * enable lock key modifier bits, or `GLFW_FALSE` to disable them. If enabled, + * callbacks that receive modifier bits will also have the @ref + * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on, + * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on. + * + * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE` + * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is + * disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported, + * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref + * glfwRawMouseMotionSupported to check for support. + * + * @param[in] window The window whose input mode to set. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. + * @param[in] value The new value of the specified input mode. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref glfwGetInputMode + * + * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`. + * + * @ingroup input + */ +GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); + +/*! @brief Returns whether raw mouse motion is supported. + * + * This function returns whether raw mouse motion is supported on the current + * system. This status does not change after GLFW has been initialized so you + * only need to check this once. If you attempt to enable raw motion on + * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted. + * + * Raw mouse motion is closer to the actual motion of the mouse across + * a surface. It is not affected by the scaling and acceleration applied to + * the motion of the desktop cursor. That processing is suitable for a cursor + * while raw motion is better for controlling for example a 3D camera. Because + * of this, raw mouse motion is only provided when the cursor is disabled. + * + * @return `GLFW_TRUE` if raw mouse motion is supported on the current machine, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref raw_mouse_motion + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwRawMouseMotionSupported(void); + +/*! @brief Returns the layout-specific name of the specified printable key. + * + * This function returns the name of the specified printable key, encoded as + * UTF-8. This is typically the character that key would produce without any + * modifier keys, intended for displaying key bindings to the user. For dead + * keys, it is typically the diacritic it would add to a character. + * + * __Do not use this function__ for [text input](@ref input_char). You will + * break text input for many languages even if it happens to work for yours. + * + * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used to identify the key, + * otherwise the scancode is ignored. If you specify a non-printable key, or + * `GLFW_KEY_UNKNOWN` and a scancode that maps to a non-printable key, this + * function returns `NULL` but does not emit an error. + * + * This behavior allows you to always pass in the arguments in the + * [key callback](@ref input_key) without modification. + * + * The printable keys are: + * - `GLFW_KEY_APOSTROPHE` + * - `GLFW_KEY_COMMA` + * - `GLFW_KEY_MINUS` + * - `GLFW_KEY_PERIOD` + * - `GLFW_KEY_SLASH` + * - `GLFW_KEY_SEMICOLON` + * - `GLFW_KEY_EQUAL` + * - `GLFW_KEY_LEFT_BRACKET` + * - `GLFW_KEY_RIGHT_BRACKET` + * - `GLFW_KEY_BACKSLASH` + * - `GLFW_KEY_WORLD_1` + * - `GLFW_KEY_WORLD_2` + * - `GLFW_KEY_0` to `GLFW_KEY_9` + * - `GLFW_KEY_A` to `GLFW_KEY_Z` + * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9` + * - `GLFW_KEY_KP_DECIMAL` + * - `GLFW_KEY_KP_DIVIDE` + * - `GLFW_KEY_KP_MULTIPLY` + * - `GLFW_KEY_KP_SUBTRACT` + * - `GLFW_KEY_KP_ADD` + * - `GLFW_KEY_KP_EQUAL` + * + * Names for printable keys depend on keyboard layout, while names for + * non-printable keys are the same across layouts but depend on the application + * language and should be localized along with other user interface text. + * + * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. + * @param[in] scancode The scancode of the key to query. + * @return The UTF-8 encoded, layout-specific name of the key, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark The contents of the returned string may change when a keyboard + * layout change event is received. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key_name + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetKeyName(int key, int scancode); + +/*! @brief Returns the platform-specific scancode of the specified key. + * + * This function returns the platform-specific scancode of the specified key. + * + * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this + * method will return `-1`. + * + * @param[in] key Any [named key](@ref keys). + * @return The platform-specific scancode for the key, or `-1` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref input_key + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetKeyScancode(int key); + +/*! @brief Returns the last reported state of a keyboard key for the specified + * window. + * + * This function returns the last state reported for the specified key to the + * specified window. The returned state is one of `GLFW_PRESS` or + * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to + * the key callback. + * + * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns + * `GLFW_PRESS` the first time you call it for a key that was pressed, even if + * that key has already been released. + * + * The key functions deal with physical keys, with [key tokens](@ref keys) + * named after their use on the standard US keyboard layout. If you want to + * input text, use the Unicode character callback instead. + * + * The [modifier key bit masks](@ref mods) are not key tokens and cannot be + * used with this function. + * + * __Do not use this function__ to implement [text input](@ref input_char). + * + * @param[in] window The desired window. + * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is + * not a valid key for this function. + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +GLFWAPI int glfwGetKey(GLFWwindow* window, int key); + +/*! @brief Returns the last reported state of a mouse button for the specified + * window. + * + * This function returns the last state reported for the specified mouse button + * to the specified window. The returned state is one of `GLFW_PRESS` or + * `GLFW_RELEASE`. + * + * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function + * returns `GLFW_PRESS` the first time you call it for a mouse button that was + * pressed, even if that mouse button has already been released. + * + * @param[in] window The desired window. + * @param[in] button The desired [mouse button](@ref buttons). + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_mouse_button + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); + +/*! @brief Retrieves the position of the cursor relative to the content area of + * the window. + * + * This function returns the position of the cursor, in screen coordinates, + * relative to the upper-left corner of the content area of the specified + * window. + * + * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor + * position is unbounded and limited only by the minimum and maximum values of + * a `double`. + * + * The coordinate can be converted to their integer equivalents with the + * `floor` function. Casting directly to an integer type works for positive + * coordinates, but fails for negative ones. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] window The desired window. + * @param[out] xpos Where to store the cursor x-coordinate, relative to the + * left edge of the content area, or `NULL`. + * @param[out] ypos Where to store the cursor y-coordinate, relative to the to + * top edge of the content area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * @sa @ref glfwSetCursorPos + * + * @since Added in version 3.0. Replaces `glfwGetMousePos`. + * + * @ingroup input + */ +GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); + +/*! @brief Sets the position of the cursor, relative to the content area of the + * window. + * + * This function sets the position, in screen coordinates, of the cursor + * relative to the upper-left corner of the content area of the specified + * window. The window must have input focus. If the window does not have + * input focus when this function is called, it fails silently. + * + * __Do not use this function__ to implement things like camera controls. GLFW + * already provides the `GLFW_CURSOR_DISABLED` cursor mode that hides the + * cursor, transparently re-centers it and provides unconstrained cursor + * motion. See @ref glfwSetInputMode for more information. + * + * If the cursor mode is `GLFW_CURSOR_DISABLED` then the cursor position is + * unconstrained and limited only by the minimum and maximum values of + * a `double`. + * + * @param[in] window The desired window. + * @param[in] xpos The desired x-coordinate, relative to the left edge of the + * content area. + * @param[in] ypos The desired y-coordinate, relative to the top edge of the + * content area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland This function will only work when the cursor mode is + * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * @sa @ref glfwGetCursorPos + * + * @since Added in version 3.0. Replaces `glfwSetMousePos`. + * + * @ingroup input + */ +GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); + +/*! @brief Creates a custom cursor. + * + * Creates a new custom cursor image that can be set for a window with @ref + * glfwSetCursor. The cursor can be destroyed with @ref glfwDestroyCursor. + * Any remaining cursors are destroyed by @ref glfwTerminate. + * + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. + * + * The cursor hotspot is specified in pixels, relative to the upper-left corner + * of the cursor image. Like all other coordinate systems in GLFW, the X-axis + * points to the right and the Y-axis points down. + * + * @param[in] image The desired cursor image. + * @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot. + * @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot. + * @return The handle of the created cursor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified image data is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwDestroyCursor + * @sa @ref glfwCreateStandardCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot); + +/*! @brief Creates a cursor with a standard shape. + * + * Returns a cursor with a [standard shape](@ref shapes), that can be set for + * a window with @ref glfwSetCursor. + * + * @param[in] shape One of the [standard shapes](@ref shapes). + * @return A new cursor ready to use or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwCreateCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); + +/*! @brief Destroys a cursor. + * + * This function destroys a cursor previously created with @ref + * glfwCreateCursor. Any remaining cursors will be destroyed by @ref + * glfwTerminate. + * + * If the specified cursor is current for any window, that window will be + * reverted to the default cursor. This does not affect the cursor mode. + * + * @param[in] cursor The cursor object to destroy. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwCreateCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); + +/*! @brief Sets the cursor for the window. + * + * This function sets the cursor image to be used when the cursor is over the + * content area of the specified window. The set cursor will only be visible + * when the [cursor mode](@ref cursor_mode) of the window is + * `GLFW_CURSOR_NORMAL`. + * + * On some platforms, the set cursor may not be visible unless the window also + * has input focus. + * + * @param[in] window The window to set the cursor for. + * @param[in] cursor The cursor to set, or `NULL` to switch back to the default + * arrow cursor. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); + +/*! @brief Sets the key callback. + * + * This function sets the key callback of the specified window, which is called + * when a key is pressed, repeated or released. + * + * The key functions deal with physical keys, with layout independent + * [key tokens](@ref keys) named after their values in the standard US keyboard + * layout. If you want to input text, use the + * [character callback](@ref glfwSetCharCallback) instead. + * + * When a window loses input focus, it will generate synthetic key release + * events for all pressed keys. You can tell these events from user-generated + * events by the fact that the synthetic ones are generated after the focus + * loss event has been processed, i.e. after the + * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. + * + * The scancode of a key is specific to that platform or sometimes even to that + * machine. Scancodes are intended to allow users to bind keys that don't have + * a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their + * state is not saved and so it cannot be queried with @ref glfwGetKey. + * + * Sometimes GLFW needs to generate synthetic key events, in which case the + * scancode may be zero. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new key callback, or `NULL` to remove the currently + * set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWkeyfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback); + +/*! @brief Sets the Unicode character callback. + * + * This function sets the character callback of the specified window, which is + * called when a Unicode character is input. + * + * The character callback is intended for Unicode text input. As it deals with + * characters, it is keyboard layout dependent, whereas the + * [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1 + * to physical keys, as a key may produce zero, one or more characters. If you + * want to know whether a specific physical key was pressed or released, see + * the key callback instead. + * + * The character callback behaves as system text input normally does and will + * not be called if modifier keys are held down that would prevent normal text + * input on that platform, for example a Super (Command) key on macOS or Alt key + * on Windows. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_char + * + * @since Added in version 2.4. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun callback); + +/*! @brief Sets the Unicode character with modifiers callback. + * + * This function sets the character with modifiers callback of the specified + * window, which is called when a Unicode character is input regardless of what + * modifier keys are used. + * + * The character with modifiers callback is intended for implementing custom + * Unicode character input. For regular Unicode text input, see the + * [character callback](@ref glfwSetCharCallback). Like the character + * callback, the character with modifiers callback deals with characters and is + * keyboard layout dependent. Characters do not map 1:1 to physical keys, as + * a key may produce zero, one or more characters. If you want to know whether + * a specific physical key was pressed or released, see the + * [key callback](@ref glfwSetKeyCallback) instead. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * [error](@ref error_handling) occurred. + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharmodsfun). + * + * @deprecated Scheduled for removal in version 4.0. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_char + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun callback); + +/*! @brief Sets the mouse button callback. + * + * This function sets the mouse button callback of the specified window, which + * is called when a mouse button is pressed or released. + * + * When a window loses input focus, it will generate synthetic mouse button + * release events for all pressed mouse buttons. You can tell these events + * from user-generated events by the fact that the synthetic ones are generated + * after the focus loss event has been processed, i.e. after the + * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmousebuttonfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_mouse_button + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback); + +/*! @brief Sets the cursor position callback. + * + * This function sets the cursor position callback of the specified window, + * which is called when the cursor is moved. The callback is provided with the + * position, in screen coordinates, relative to the upper-left corner of the + * content area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorposfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * + * @since Added in version 3.0. Replaces `glfwSetMousePosCallback`. + * + * @ingroup input + */ +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback); + +/*! @brief Sets the cursor enter/leave callback. + * + * This function sets the cursor boundary crossing callback of the specified + * window, which is called when the cursor enters or leaves the content area of + * the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorenterfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_enter + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun callback); + +/*! @brief Sets the scroll callback. + * + * This function sets the scroll callback of the specified window, which is + * called when a scrolling device is used, such as a mouse wheel or scrolling + * area of a touchpad. + * + * The scroll callback receives all scrolling input, like that from a mouse + * wheel or a touchpad scrolling area. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new scroll callback, or `NULL` to remove the + * currently set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWscrollfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref scrolling + * + * @since Added in version 3.0. Replaces `glfwSetMouseWheelCallback`. + * + * @ingroup input + */ +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback); + +/*! @brief Sets the path drop callback. + * + * This function sets the path drop callback of the specified window, which is + * called when one or more dragged paths are dropped on the window. + * + * Because the path array and its strings may have been generated specifically + * for that event, they are not guaranteed to be valid after the callback has + * returned. If you wish to use them after the callback returns, you need to + * make a deep copy. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new file drop callback, or `NULL` to remove the + * currently set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWdropfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland File drop is currently unimplemented. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref path_drop + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback); + +/*! @brief Returns whether the specified joystick is present. + * + * This function returns whether the specified joystick is present. + * + * There is no need to call this function before other functions that accept + * a joystick ID, as they all check for presence before performing any other + * work. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick + * + * @since Added in version 3.0. Replaces `glfwGetJoystickParam`. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickPresent(int jid); + +/*! @brief Returns the values of all axes of the specified joystick. + * + * This function returns the values of all axes of the specified joystick. + * Each element in the array is a value between -1.0 and 1.0. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of axis values in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of axis values, or `NULL` if the joystick is not present or + * an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_axis + * + * @since Added in version 3.0. Replaces `glfwGetJoystickPos`. + * + * @ingroup input + */ +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); + +/*! @brief Returns the state of all buttons of the specified joystick. + * + * This function returns the state of all buttons of the specified joystick. + * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. + * + * For backward compatibility with earlier versions that did not have @ref + * glfwGetJoystickHats, the button array also includes all hats, each + * represented as four buttons. The hats are in the same order as returned by + * __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and + * _left_. To disable these extra buttons, set the @ref + * GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of button states in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of button states, or `NULL` if the joystick is not present + * or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_button + * + * @since Added in version 2.2. + * @glfw3 Changed to return a dynamic array. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); + +/*! @brief Returns the state of all hats of the specified joystick. + * + * This function returns the state of all hats of the specified joystick. + * Each element in the array is one of the following values: + * + * Name | Value + * ---- | ----- + * `GLFW_HAT_CENTERED` | 0 + * `GLFW_HAT_UP` | 1 + * `GLFW_HAT_RIGHT` | 2 + * `GLFW_HAT_DOWN` | 4 + * `GLFW_HAT_LEFT` | 8 + * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` + * `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` + * `GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` + * `GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` + * + * The diagonal directions are bitwise combinations of the primary (up, right, + * down and left) directions and you can test for these individually by ANDing + * it with the corresponding direction. + * + * @code + * if (hats[2] & GLFW_HAT_RIGHT) + * { + * // State of hat 2 could be right-up, right or right-down + * } + * @endcode + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of hat states in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of hat states, or `NULL` if the joystick is not present + * or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, this function is called again for that joystick or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_hat + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); + +/*! @brief Returns the name of the specified joystick. + * + * This function returns the name, encoded as UTF-8, of the specified joystick. + * The returned string is allocated and freed by GLFW. You should not free it + * yourself. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick + * is not present or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_name + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickName(int jid); + +/*! @brief Returns the SDL compatible GUID of the specified joystick. + * + * This function returns the SDL compatible GUID, as a UTF-8 encoded + * hexadecimal string, of the specified joystick. The returned string is + * allocated and freed by GLFW. You should not free it yourself. + * + * The GUID is what connects a joystick to a gamepad mapping. A connected + * joystick will always have a GUID even if there is no gamepad mapping + * assigned to it. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * The GUID uses the format introduced in SDL 2.0.5. This GUID tries to + * uniquely identify the make and model of a joystick but does not identify + * a specific unit, e.g. all wired Xbox 360 controllers will have the same + * GUID on that platform. The GUID for a unit may vary between platforms + * depending on what hardware information the platform specific APIs provide. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded GUID of the joystick, or `NULL` if the joystick + * is not present or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickGUID(int jid); + +/*! @brief Sets the user pointer of the specified joystick. + * + * This function sets the user-defined pointer of the specified joystick. The + * current value is retained until the joystick is disconnected. The initial + * value is `NULL`. + * + * This function may be called from the joystick callback, even for a joystick + * that is being disconnected. + * + * @param[in] jid The joystick whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref joystick_userptr + * @sa @ref glfwGetJoystickUserPointer + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer); + +/*! @brief Returns the user pointer of the specified joystick. + * + * This function returns the current value of the user-defined pointer of the + * specified joystick. The initial value is `NULL`. + * + * This function may be called from the joystick callback, even for a joystick + * that is being disconnected. + * + * @param[in] jid The joystick whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref joystick_userptr + * @sa @ref glfwSetJoystickUserPointer + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI void* glfwGetJoystickUserPointer(int jid); + +/*! @brief Returns whether the specified joystick has a gamepad mapping. + * + * This function returns whether the specified joystick is both present and has + * a gamepad mapping. + * + * If the specified joystick is present but does not have a gamepad mapping + * this function will return `GLFW_FALSE` but will not generate an error. Call + * @ref glfwJoystickPresent to check if a joystick is present regardless of + * whether it has a mapping. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return `GLFW_TRUE` if a joystick is both present and has a gamepad mapping, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwGetGamepadState + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickIsGamepad(int jid); + +/*! @brief Sets the joystick configuration callback. + * + * This function sets the joystick configuration callback, or removes the + * currently set callback. This is called when a joystick is connected to or + * disconnected from the system. + * + * For joystick connection and disconnection events to be delivered on all + * platforms, you need to call one of the [event processing](@ref events) + * functions. Joystick disconnection may also be detected and the callback + * called by joystick functions. The function will then return whatever it + * returns if the joystick is not present. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(int jid, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWjoystickfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_event + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback); + +/*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. + * + * This function parses the specified ASCII encoded string and updates the + * internal list with any gamepad mappings it finds. This string may + * contain either a single gamepad mapping or many mappings separated by + * newlines. The parser supports the full format of the `gamecontrollerdb.txt` + * source file including empty lines and comments. + * + * See @ref gamepad_mapping for a description of the format. + * + * If there is already a gamepad mapping for a given GUID in the internal list, + * it will be replaced by the one passed to this function. If the library is + * terminated and re-initialized the internal list will revert to the built-in + * default. + * + * @param[in] string The string containing the gamepad mappings. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_VALUE. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwJoystickIsGamepad + * @sa @ref glfwGetGamepadName + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwUpdateGamepadMappings(const char* string); + +/*! @brief Returns the human-readable gamepad name for the specified joystick. + * + * This function returns the human-readable name of the gamepad from the + * gamepad mapping assigned to the specified joystick. + * + * If the specified joystick is not present or does not have a gamepad mapping + * this function will return `NULL` but will not generate an error. Call + * @ref glfwJoystickPresent to check whether it is present regardless of + * whether it has a mapping. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded name of the gamepad, or `NULL` if the + * joystick is not present, does not have a mapping or an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref GLFW_INVALID_ENUM. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, the gamepad mappings are updated or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwJoystickIsGamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetGamepadName(int jid); + +/*! @brief Retrieves the state of the specified joystick remapped as a gamepad. + * + * This function retrieves the state of the specified joystick remapped to + * an Xbox-like gamepad. + * + * If the specified joystick is not present or does not have a gamepad mapping + * this function will return `GLFW_FALSE` but will not generate an error. Call + * @ref glfwJoystickPresent to check whether it is present regardless of + * whether it has a mapping. + * + * The Guide button may not be available for input as it is often hooked by the + * system or the Steam client. + * + * Not all devices have all the buttons or axes provided by @ref + * GLFWgamepadstate. Unavailable buttons and axes will always report + * `GLFW_RELEASE` and 0.0 respectively. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] state The gamepad input state of the joystick. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if no joystick is + * connected, it has no gamepad mapping or an [error](@ref error_handling) + * occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwUpdateGamepadMappings + * @sa @ref glfwJoystickIsGamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); + +/*! @brief Sets the clipboard to the specified string. + * + * This function sets the system clipboard to the specified, UTF-8 encoded + * string. + * + * @param[in] window Deprecated. Any valid window or `NULL`. + * @param[in] string A UTF-8 encoded string. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa @ref glfwGetClipboardString + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); + +/*! @brief Returns the contents of the clipboard as a string. + * + * This function returns the contents of the system clipboard, if it contains + * or is convertible to a UTF-8 encoded string. If the clipboard is empty or + * if its contents cannot be converted, `NULL` is returned and a @ref + * GLFW_FORMAT_UNAVAILABLE error is generated. + * + * @param[in] window Deprecated. Any valid window or `NULL`. + * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the next call to @ref + * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa @ref glfwSetClipboardString + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); + +/*! @brief Returns the GLFW time. + * + * This function returns the current GLFW time, in seconds. Unless the time + * has been set using @ref glfwSetTime it measures time elapsed since GLFW was + * initialized. + * + * This function and @ref glfwSetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. + * + * The resolution of the timer is system dependent, but is usually on the order + * of a few micro- or nanoseconds. It uses the highest-resolution monotonic + * time source on each supported platform. + * + * @return The current time, in seconds, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Reading and + * writing of the internal base time is not atomic, so it needs to be + * externally synchronized with calls to @ref glfwSetTime. + * + * @sa @ref time + * + * @since Added in version 1.0. + * + * @ingroup input + */ +GLFWAPI double glfwGetTime(void); + +/*! @brief Sets the GLFW time. + * + * This function sets the current GLFW time, in seconds. The value must be + * a positive finite number less than or equal to 18446744073.0, which is + * approximately 584.5 years. + * + * This function and @ref glfwGetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. + * + * @param[in] time The new value, in seconds. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_VALUE. + * + * @remark The upper limit of GLFW time is calculated as + * floor((264 - 1) / 109) and is due to implementations + * storing nanoseconds in 64 bits. The limit may be increased in the future. + * + * @thread_safety This function may be called from any thread. Reading and + * writing of the internal base time is not atomic, so it needs to be + * externally synchronized with calls to @ref glfwGetTime. + * + * @sa @ref time + * + * @since Added in version 2.2. + * + * @ingroup input + */ +GLFWAPI void glfwSetTime(double time); + +/*! @brief Returns the current value of the raw timer. + * + * This function returns the current value of the raw timer, measured in + * 1 / frequency seconds. To get the frequency, call @ref + * glfwGetTimerFrequency. + * + * @return The value of the timer, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref time + * @sa @ref glfwGetTimerFrequency + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI uint64_t glfwGetTimerValue(void); + +/*! @brief Returns the frequency, in Hz, of the raw timer. + * + * This function returns the frequency, in Hz, of the raw timer. + * + * @return The frequency of the timer, in Hz, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref time + * @sa @ref glfwGetTimerValue + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI uint64_t glfwGetTimerFrequency(void); + +/*! @brief Makes the context of the specified window current for the calling + * thread. + * + * This function makes the OpenGL or OpenGL ES context of the specified window + * current on the calling thread. A context must only be made current on + * a single thread at a time and each thread can have only a single current + * context at a time. + * + * When moving a context between threads, you must make it non-current on the + * old thread before making it current on the new one. + * + * By default, making a context non-current implicitly forces a pipeline flush. + * On machines that support `GL_KHR_context_flush_control`, you can control + * whether a context performs this flush by setting the + * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) + * hint. + * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * + * @param[in] window The window whose context to make current, or `NULL` to + * detach the current context. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_current + * @sa @ref glfwGetCurrentContext + * + * @since Added in version 3.0. + * + * @ingroup context + */ +GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); + +/*! @brief Returns the window whose context is current on the calling thread. + * + * This function returns the window whose OpenGL or OpenGL ES context is + * current on the calling thread. + * + * @return The window whose context is current, or `NULL` if no window's + * context is current. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_current + * @sa @ref glfwMakeContextCurrent + * + * @since Added in version 3.0. + * + * @ingroup context + */ +GLFWAPI GLFWwindow* glfwGetCurrentContext(void); + +/*! @brief Swaps the front and back buffers of the specified window. + * + * This function swaps the front and back buffers of the specified window when + * rendering with OpenGL or OpenGL ES. If the swap interval is greater than + * zero, the GPU driver waits the specified number of screen updates before + * swapping the buffers. + * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see `vkQueuePresentKHR` instead. + * + * @param[in] window The window whose buffers to swap. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark __EGL:__ The context of the specified window must be current on the + * calling thread. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref buffer_swap + * @sa @ref glfwSwapInterval + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSwapBuffers(GLFWwindow* window); + +/*! @brief Sets the swap interval for the current context. + * + * This function sets the swap interval for the current OpenGL or OpenGL ES + * context, i.e. the number of screen updates to wait from the time @ref + * glfwSwapBuffers was called before swapping the buffers and returning. This + * is sometimes called _vertical synchronization_, _vertical retrace + * synchronization_ or just _vsync_. + * + * A context that supports either of the `WGL_EXT_swap_control_tear` and + * `GLX_EXT_swap_control_tear` extensions also accepts _negative_ swap + * intervals, which allows the driver to swap immediately even if a frame + * arrives a little bit late. You can check for these extensions with @ref + * glfwExtensionSupported. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see the present mode of your swapchain instead. + * + * @param[in] interval The minimum number of screen updates to wait for + * until the buffers are swapped by @ref glfwSwapBuffers. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark This function is not called during context creation, leaving the + * swap interval set to whatever is the default on that platform. This is done + * because some swap interval extensions used by GLFW do not allow the swap + * interval to be reset to zero once it has been set to a non-zero value. + * + * @remark Some GPU drivers do not honor the requested swap interval, either + * because of a user setting that overrides the application's request or due to + * bugs in the driver. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref buffer_swap + * @sa @ref glfwSwapBuffers + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI void glfwSwapInterval(int interval); + +/*! @brief Returns whether the specified extension is available. + * + * This function returns whether the specified + * [API extension](@ref context_glext) is supported by the current OpenGL or + * OpenGL ES context. It searches both for client API extension and context + * creation API extensions. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * As this functions retrieves and searches one or more extension strings each + * call, it is recommended that you cache its results if it is going to be used + * frequently. The extension strings will not change during the lifetime of + * a context, so there is no danger in doing this. + * + * This function does not apply to Vulkan. If you are using Vulkan, see @ref + * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties` + * and `vkEnumerateDeviceExtensionProperties` instead. + * + * @param[in] extension The ASCII encoded name of the extension. + * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE` + * otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_glext + * @sa @ref glfwGetProcAddress + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI int glfwExtensionSupported(const char* extension); + +/*! @brief Returns the address of the specified function for the current + * context. + * + * This function returns the address of the specified OpenGL or OpenGL ES + * [core or extension function](@ref context_glext), if it is supported + * by the current context. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and + * `vkGetDeviceProcAddr` instead. + * + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark The address of a given function is not guaranteed to be the same + * between contexts. + * + * @remark This function may return a non-`NULL` address despite the + * associated version or extension not being available. Always check the + * context version or extension string first. + * + * @pointer_lifetime The returned function pointer is valid until the context + * is destroyed or the library is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_glext + * @sa @ref glfwExtensionSupported + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); + +/*! @brief Returns whether the Vulkan loader and an ICD have been found. + * + * This function returns whether the Vulkan loader and any minimally functional + * ICD have been found. + * + * The availability of a Vulkan loader and even an ICD does not by itself guarantee that + * surface creation or even instance creation is possible. Call @ref + * glfwGetRequiredInstanceExtensions to check whether the extensions necessary for Vulkan + * surface creation are available and @ref glfwGetPhysicalDevicePresentationSupport to + * check whether a queue family of a physical device supports image presentation. + * + * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` + * otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_support + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI int glfwVulkanSupported(void); + +/*! @brief Returns the Vulkan instance extensions required by GLFW. + * + * This function returns an array of names of Vulkan instance extensions required + * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the + * list will always contain `VK_KHR_surface`, so if you don't require any + * additional extensions you can pass this list directly to the + * `VkInstanceCreateInfo` struct. + * + * If Vulkan is not available on the machine, this function returns `NULL` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is at least minimally available. + * + * If Vulkan is available but no set of extensions allowing window surface + * creation was found, this function returns `NULL`. You may still use Vulkan + * for off-screen rendering and compute work. + * + * @param[out] count Where to store the number of extensions in the returned + * array. This is set to zero if an error occurred. + * @return An array of ASCII encoded extension names, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_API_UNAVAILABLE. + * + * @remark Additional extensions may be required by future versions of GLFW. + * You should check if any extensions you wish to enable are already in the + * returned array, as it is an error to specify an extension more than once in + * the `VkInstanceCreateInfo` struct. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * library is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_ext + * @sa @ref glfwCreateWindowSurface + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count); + +#if defined(VK_VERSION_1_0) + +/*! @brief Returns the address of the specified Vulkan instance function. + * + * This function returns the address of the specified Vulkan core or extension + * function for the specified instance. If instance is set to `NULL` it can + * return any function exported from the Vulkan loader, including at least the + * following functions: + * + * - `vkEnumerateInstanceExtensionProperties` + * - `vkEnumerateInstanceLayerProperties` + * - `vkCreateInstance` + * - `vkGetInstanceProcAddr` + * + * If Vulkan is not available on the machine, this function returns `NULL` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is at least minimally available. + * + * This function is equivalent to calling `vkGetInstanceProcAddr` with + * a platform-specific query of the Vulkan loader as a fallback. + * + * @param[in] instance The Vulkan instance to query, or `NULL` to retrieve + * functions related to instance creation. + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_API_UNAVAILABLE. + * + * @pointer_lifetime The returned function pointer is valid until the library + * is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_proc + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname); + +/*! @brief Returns whether the specified queue family can present images. + * + * This function returns whether the specified queue family of the specified + * physical device supports presentation to the platform GLFW was built for. + * + * If Vulkan or the required window surface creation instance extensions are + * not available on the machine, or if the specified instance was not created + * with the required extensions, this function returns `GLFW_FALSE` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is at least minimally available and @ref + * glfwGetRequiredInstanceExtensions to check what instance extensions are + * required. + * + * @param[in] instance The instance that the physical device belongs to. + * @param[in] device The physical device that the queue family belongs to. + * @param[in] queuefamily The index of the queue family to query. + * @return `GLFW_TRUE` if the queue family supports presentation, or + * `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. + * + * @remark @macos This function currently always returns `GLFW_TRUE`, as the + * `VK_MVK_macos_surface` and `VK_EXT_metal_surface` extensions do not provide + * a `vkGetPhysicalDevice*PresentationSupport` type function. + * + * @thread_safety This function may be called from any thread. For + * synchronization details of Vulkan objects, see the Vulkan specification. + * + * @sa @ref vulkan_present + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); + +/*! @brief Creates a Vulkan surface for the specified window. + * + * This function creates a Vulkan surface for the specified window. + * + * If the Vulkan loader or at least one minimally functional ICD were not found, + * this function returns `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref + * GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported to check whether + * Vulkan is at least minimally available. + * + * If the required window surface creation instance extensions are not + * available or if the specified instance was not created with these extensions + * enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref + * glfwGetRequiredInstanceExtensions to check what instance extensions are + * required. + * + * The window surface cannot be shared with another API so the window must + * have been created with the [client api hint](@ref GLFW_CLIENT_API_attrib) + * set to `GLFW_NO_API` otherwise it generates a @ref GLFW_INVALID_VALUE error + * and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`. + * + * The window surface must be destroyed before the specified Vulkan instance. + * It is the responsibility of the caller to destroy the window surface. GLFW + * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the + * surface. + * + * @param[in] instance The Vulkan instance to create the surface in. + * @param[in] window The window to create the surface for. + * @param[in] allocator The allocator to use, or `NULL` to use the default + * allocator. + * @param[out] surface Where to store the handle of the surface. This is set + * to `VK_NULL_HANDLE` if an error occurred. + * @return `VK_SUCCESS` if successful, or a Vulkan error code if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_API_UNAVAILABLE, @ref GLFW_PLATFORM_ERROR and @ref GLFW_INVALID_VALUE + * + * @remark If an error occurs before the creation call is made, GLFW returns + * the Vulkan error code most appropriate for the error. Appropriate use of + * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should + * eliminate almost all occurrences of these errors. + * + * @remark @macos GLFW prefers the `VK_EXT_metal_surface` extension, with the + * `VK_MVK_macos_surface` extension as a fallback. The name of the selected + * extension, if any, is included in the array returned by @ref + * glfwGetRequiredInstanceExtensions. + * + * @remark @macos This function creates and sets a `CAMetalLayer` instance for + * the window content view, which is required for MoltenVK to function. + * + * @thread_safety This function may be called from any thread. For + * synchronization details of Vulkan objects, see the Vulkan specification. + * + * @sa @ref vulkan_surface + * @sa @ref glfwGetRequiredInstanceExtensions + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + +#endif /*VK_VERSION_1_0*/ + + +/************************************************************************* + * Global definition cleanup + *************************************************************************/ + +/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ + +#ifdef GLFW_WINGDIAPI_DEFINED + #undef WINGDIAPI + #undef GLFW_WINGDIAPI_DEFINED +#endif + +#ifdef GLFW_CALLBACK_DEFINED + #undef CALLBACK + #undef GLFW_CALLBACK_DEFINED +#endif + +/* Some OpenGL related headers need GLAPIENTRY, but it is unconditionally + * defined by some gl.h variants (OpenBSD) so define it after if needed. + */ +#ifndef GLAPIENTRY + #define GLAPIENTRY APIENTRY +#endif + +/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_h_ */ + diff --git a/src_v2/libs/glfw_osx/include/GLFW/glfw3native.h b/src_v2/libs/glfw_osx/include/GLFW/glfw3native.h new file mode 100644 index 0000000..fe74c73 --- /dev/null +++ b/src_v2/libs/glfw_osx/include/GLFW/glfw3native.h @@ -0,0 +1,594 @@ +/************************************************************************* + * GLFW 3.3 - www.glfw.org + * A library for OpenGL, window and input + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2018 Camilla Löwy + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_native_h_ +#define _glfw3_native_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @file glfw3native.h + * @brief The header of the native access functions. + * + * This is the header file of the native access functions. See @ref native for + * more information. + */ +/*! @defgroup native Native access + * @brief Functions related to accessing native handles. + * + * **By using the native access functions you assert that you know what you're + * doing and how to fix problems caused by using them. If you don't, you + * shouldn't be using them.** + * + * Before the inclusion of @ref glfw3native.h, you may define zero or more + * window system API macro and zero or more context creation API macros. + * + * The chosen backends must match those the library was compiled for. Failure + * to do this will cause a link-time error. + * + * The available window API macros are: + * * `GLFW_EXPOSE_NATIVE_WIN32` + * * `GLFW_EXPOSE_NATIVE_COCOA` + * * `GLFW_EXPOSE_NATIVE_X11` + * * `GLFW_EXPOSE_NATIVE_WAYLAND` + * + * The available context API macros are: + * * `GLFW_EXPOSE_NATIVE_WGL` + * * `GLFW_EXPOSE_NATIVE_NSGL` + * * `GLFW_EXPOSE_NATIVE_GLX` + * * `GLFW_EXPOSE_NATIVE_EGL` + * * `GLFW_EXPOSE_NATIVE_OSMESA` + * + * These macros select which of the native access functions that are declared + * and which platform-specific headers to include. It is then up your (by + * definition platform-specific) code to handle which of these should be + * defined. + */ + + +/************************************************************************* + * System headers and types + *************************************************************************/ + +#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) + // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for + // example to allow applications to correctly declare a GL_KHR_debug callback) + // but windows.h assumes no one will define APIENTRY before it does + #if defined(GLFW_APIENTRY_DEFINED) + #undef APIENTRY + #undef GLFW_APIENTRY_DEFINED + #endif + #include +#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) + #if defined(__OBJC__) + #import + #else + #include + typedef void* id; + #endif +#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) + #include + #include +#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) + #include +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WGL) + /* WGL is declared by windows.h */ +#endif +#if defined(GLFW_EXPOSE_NATIVE_NSGL) + /* NSGL is declared by Cocoa.h */ +#endif +#if defined(GLFW_EXPOSE_NATIVE_GLX) + #include +#endif +#if defined(GLFW_EXPOSE_NATIVE_EGL) + #include +#endif +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) + #include +#endif + + +/************************************************************************* + * Functions + *************************************************************************/ + +#if defined(GLFW_EXPOSE_NATIVE_WIN32) +/*! @brief Returns the adapter device name of the specified monitor. + * + * @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`) + * of the specified monitor, or `NULL` if an [error](@ref error_handling) + * occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); + +/*! @brief Returns the display device name of the specified monitor. + * + * @return The UTF-8 encoded display device name (for example + * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); + +/*! @brief Returns the `HWND` of the specified window. + * + * @return The `HWND` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WGL) +/*! @brief Returns the `HGLRC` of the specified window. + * + * @return The `HGLRC` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_COCOA) +/*! @brief Returns the `CGDirectDisplayID` of the specified monitor. + * + * @return The `CGDirectDisplayID` of the specified monitor, or + * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the `NSWindow` of the specified window. + * + * @return The `NSWindow` of the specified window, or `nil` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_NSGL) +/*! @brief Returns the `NSOpenGLContext` of the specified window. + * + * @return The `NSOpenGLContext` of the specified window, or `nil` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_X11) +/*! @brief Returns the `Display` used by GLFW. + * + * @return The `Display` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI Display* glfwGetX11Display(void); + +/*! @brief Returns the `RRCrtc` of the specified monitor. + * + * @return The `RRCrtc` of the specified monitor, or `None` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); + +/*! @brief Returns the `RROutput` of the specified monitor. + * + * @return The `RROutput` of the specified monitor, or `None` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); + +/*! @brief Returns the `Window` of the specified window. + * + * @return The `Window` of the specified window, or `None` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI Window glfwGetX11Window(GLFWwindow* window); + +/*! @brief Sets the current primary selection to the specified string. + * + * @param[in] string A UTF-8 encoded string. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa glfwGetX11SelectionString + * @sa glfwSetClipboardString + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI void glfwSetX11SelectionString(const char* string); + +/*! @brief Returns the contents of the current primary selection as a string. + * + * If the selection is empty or if its contents cannot be converted, `NULL` + * is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated. + * + * @return The contents of the selection as a UTF-8 encoded string, or `NULL` + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the next call to @ref + * glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the + * library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa glfwSetX11SelectionString + * @sa glfwGetClipboardString + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetX11SelectionString(void); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_GLX) +/*! @brief Returns the `GLXContext` of the specified window. + * + * @return The `GLXContext` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); + +/*! @brief Returns the `GLXWindow` of the specified window. + * + * @return The `GLXWindow` of the specified window, or `None` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WAYLAND) +/*! @brief Returns the `struct wl_display*` used by GLFW. + * + * @return The `struct wl_display*` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); + +/*! @brief Returns the `struct wl_output*` of the specified monitor. + * + * @return The `struct wl_output*` of the specified monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the main `struct wl_surface*` of the specified window. + * + * @return The main `struct wl_surface*` of the specified window, or `NULL` if + * an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_EGL) +/*! @brief Returns the `EGLDisplay` used by GLFW. + * + * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLDisplay glfwGetEGLDisplay(void); + +/*! @brief Returns the `EGLContext` of the specified window. + * + * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); + +/*! @brief Returns the `EGLSurface` of the specified window. + * + * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) +/*! @brief Retrieves the color buffer associated with the specified window. + * + * @param[in] window The window whose color buffer to retrieve. + * @param[out] width Where to store the width of the color buffer, or `NULL`. + * @param[out] height Where to store the height of the color buffer, or `NULL`. + * @param[out] format Where to store the OSMesa pixel format of the color + * buffer, or `NULL`. + * @param[out] buffer Where to store the address of the color buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer); + +/*! @brief Retrieves the depth buffer associated with the specified window. + * + * @param[in] window The window whose depth buffer to retrieve. + * @param[out] width Where to store the width of the depth buffer, or `NULL`. + * @param[out] height Where to store the height of the depth buffer, or `NULL`. + * @param[out] bytesPerValue Where to store the number of bytes per depth + * buffer element, or `NULL`. + * @param[out] buffer Where to store the address of the depth buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer); + +/*! @brief Returns the `OSMesaContext` of the specified window. + * + * @return The `OSMesaContext` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_native_h_ */ + diff --git a/src_v2/libs/glfw_osx/lib-arm64/libglfw.3.dylib b/src_v2/libs/glfw_osx/lib-arm64/libglfw.3.dylib new file mode 100755 index 0000000000000000000000000000000000000000..5fbb9fb2374d68643159d206597debf39547c58e GIT binary patch literal 233228 zcmeEvdvsJqws)QGJWeMe?}U(G(n%20K?O7*QIPHgH3^Cmf)jDZbihX^K_NUG11cSY z+9CL8!6%L+=xDmV%;*?q>`^ZX=)FPjbw+&6OYML&(;>b<9%&@g-)~o)lRjx0uXnBQ z`{P@yvsS07cJ11=YuBz_?>c`t_0_);7?T+OV(=S}-y`D~yN@Lkh)uxHrf00KuI7r` z8V(x!&#ns_js}!|aY}4m-I`nOS`$PKr8jB0h+8gmRv3lHP>y2uU~1xL^_sOySC4{1 z={>SW(BXvw7KOF+WQ88e&i@3xI@>MFSKhJ;kTdA56ZEo3NcejMVJ$s&27fD-HZH5X z{gyShmy-LlndRyEvln}QL_-!pB3-iIFu1U0M@9K$Ps zpts@<@TLBR(p&ixks?b;NZ}|IIgDFZ$IH8HdHw3uchuj0ODH|Z4T8`{C9N7(Nh#T= zLGU*!KO%RA{Nk2~6wRcd_*26o6rwu-YUr(LS$QVC^{WNF23b&0!)K?r_Ku%7+;MYV z!yUJ-;Pl6~k1y5>dJ`3)Q8=`WDs6J97)3y+tD9%3vDB4cyI_%MZFVJ2Pt=TO1z>cD_5;pvmzY5Clz{G3OT3KYOAALs&~s zgUP)Lz16oYUv<0wBZej#~6xPy1-xIE0xqTvvq4bLWBIpeZa*V=SdH{!` zw|XtQi*WRee--r9L=^rJ^cwE0r{3qx@=gn(M`4izM?d4ABEOsK*VOa4(DIrUdXI>1 zgHbqyzcDoH>PAV0rT53b32y94yQT0C(_6OU_S;w7p=J`w-*txsy_FZM7(##5kixba zgnlet9UKjuDZk!K!A+K`&q%4|FO(j&vkLs`>aMO_F#n2qmBJb`W>!-5B9i}!aDvhp zh>n&PhEi-Aeq=8=Tu=CVW?r?jevNJF@`k0iEx+~W7hZhf%`NaVRsdBde8MS0f+9)J zo>XK?(B4=*`xZoB&d{?*(3nX|M7yX!&#pmW)_gsiHk+|{z^=oO{&GNves=h`!~G2b z&+<2XrJf}LC-g4|0LtgT zz|`w+S+%+WgO{lm5yh#TNjD8F{UF`SYv(tt2~U?tjncKjH_GqIGFY6)f0*A@w_Lem z?Hy<^U^9FManGYIkQo_`p>F*pRyz&7(dq(N&ctqr1L;?{BL?jTAKtuu&2}C3ikw8QO z5eY;j5RpJc0uc#BBoL86L;?{BL?jTAKtuu&2}C3ikw8QO5eY;j5RpJc0uc#BBoL86 zL;?{BL?jTAKtuu&2}C3ikw8QO5eY;j5RpJc0uc#BBoL86L;?{BL?jTAKtuu&2}C3i zkw8QO5eY;j5RpJc0uc#BBoL86L;?{BL?jTAKtuu&2}C3ikw8QO5eY;j5RpJc0uc#B zBoL86L;?{BL?jTAKtuu&2}C3ikw8QO5eY;j5Rt(DcM=G+vG+&TORkY}=9JUT{%*#4 z49x7QjAkXWROXS%jWT-%16!-x1AA5(Bt!h%ajbc1G^>$;XG>xwCT6d0V4bV-Btw%I z@Jda%FBW){EGB&Mpn1N;YE07bCFHMXtY$9Ue}kK6V9ojCM0hBH)ife~G3c42S&HvG z=IZ))d1ana|ItA`+lDV)RFAB;xWKowS6}zB4dEi-;xYXc315o&a~K?-p7~pQjL(D2yh>Ts zvTqp))Wm@wd=w#lZVGFTL%0~>_=#rEmx1loTOjY{QbPO=$fpeX6Aj{RF2cSScVt&c zavaIhqGvUua)8VRUY4g3Uyq03Yf3bmVMsQ6ToNlGxi3eZXw&AuB6!9p3C@tUY$QGS zMNkirKFVZX*9sc)B#L|NjJSNn8Br!}opIT`BI@)R$cDmwuLxN_iSQ7GT_S!P!ZPZ0 zZz?M>oNx9(Ua8~oOTcePuPep}G19-(C%V7VC%F&nQ`|ni&Yf?RoK4Z}h%rUzR1;)& zA<`}g1WHm8%$~V1thpl*F!(>0So7E8cVz1zle#Ftfjbrc0O0RsJd~|ZH+wdyaQIbt zQW5-+J=MoF@=M80x5NLnlolsrFqn%R>_`4Bz$N7KzVdnVx*kfNI2i+(6O32g{; zrgz4%no00~BSp>s=ANXSD73j9QEa*$>Cc6K6N{ez@a(9ZC|LuCUxlMg(~SnuK$)iN zqUKM(Hzfz<;pr!#JY_6u{=#{>9F&E_3Q(4%OvhomEM9gFFND90=>;5msM08ee-sWq z7=^<>3WpAi!r@opkb}q*a!A(c=BHeloReH)_B_H^$uzimI@UZr)0urxFS$RL5}MkM z1ZpJc4TS^JxF$Wqt&jtS=j!5`+P)3IFT3Y5d8iA1J9H!wI;PfP+|fYIqktt*+fw8> za6C}+3ScRK5s&pJ12u0V>;)aNBLThc{ZqH;UBh)zt`k3rb{)Gm#x=4&)-|$Eavn#% z$Mnp7vW;C-1bT9m&g0W1T_iJ|=dOp-xLuKdxv@G^FL!wLneG*{*tYM0PyXfb%X+g1 z-~T9a=wMHEtOTD9>2buAc??ly9qn)|V1vc>P!r@P5d`t3!&WZTG2 zcqg-zE|+Md&+6DV$4rag`m|{8gm2H4`~)*TE!q!<6-fT}T#Mh8%6ib&+*Y_|Z(B9R znPt{<0x;j;FoZEX57{j>Iz0 z;8e-wo5EbxkQdbxLp{YP^->hiX5hP+>D@Z?d0T}X4}(sJQ^*gWWl5)cYj`Xe_tI7o zmnX$F)l2P9dvjUJWg6PwTq5ecLS{46x?2o7)Nc-5 zF@y9GWzT;=$m5h0l<%(;8OXzdk`JKoHk1wDK;*h_`vKjzaotaIldhYQPTMZ<(Ilb& zPEqflAwRSk^etDRyJlCn)%3WyBe0*SkNXQwsW?QGkW=sT)0n|Ye$rk-r z)o`E&U*k#N9m5trR-p4(w}`TA0=^Oa&VUY&kxNxj{*~}kT|w40rmKbD3co@x<*~^+ z59<*0R)U@x^i=s;>w>OjRVw@kFnJZRTysQLOWj{+Qc^ z45$rLn`X?x>&sDW_Bc@Yj$(}sQFOjisS_XayI3dc#PPqV6KV^pJ{#aSo~?d13Eu8R z{ZQXeefqI${~h*Jj5EnL_S_P*n|7VtQ2^c?XoI99uPVG1>HlYasaN!c#uvw~-P>(s zc^|7hs<0Y~rK9e9B*n(6aKiP$wz6|EcHAI%R^zTh8r9GHhjQut;W`OArb9k@lqJeF zqL-YuI#CXTWcHX~yQpjilDA~&_-ZD~A4PIDmv$qL`Vd)$%_ln$rL^gbnX^-E(@D@t z8dIP>X7Dx(+YUQDpwHlaff2TPK%eG*&z#Ro5U(zi*_&*N5Dc?=V*NeJ2iLe*p zH3(DRflt+u{VbKGoQv{WS+f;o^MYqe^K;-3KIw`Nu;aSSj(|=cvV&jW(YjfLZ-oCC zbi{EH>1-M=>&W_fkaIcYU4}M|@qW3)+w;JB(ViH}N#poWnQ8;VDkEoJ0NqLr>JQo@O*X z((&{OtkkZgXK35Rqqe@Nejc6+osDA7eBhbj=j|4*DsM`|jKv8slX~ZPW9@TTSMO}^bFH&yBCcP-qdv>= zt1ZhRsrasF zdVihJZx(Id<$ZwdV$QzV-mM#Frz5Tcd2`;s)4c|~l_V>CTQI$1%Y)V3N|}nRB7F?t z-3bp@I}~0W3S6a2X&qD2vMQ*L{7|%+g)`VT>SJJ|cNpP={ZG zHu2HFH@y~Zr-xvw{qAo2=`h8)#wFL9AUBNVhiKddJ#mh#FNf}wL5D1^(<|QMcIz(U zQ!_^3<(rh2)eqZMg0hf()#~u8g`zx^pLECl)lEoW@l7h{k?N@-2K8jX_&1e#4B2d( z9dhb@SEPwYnPAsT%0lZMA^Rw90p<=@v}z9JntcF z-5cj$l7@aUgg?~Zw)8N3hxR$X*@6drQgQ`r*))fNcZ?@{E|%E06F}Fxv)tbs2<)b? z_1SX29dZ913po4)F9F<9eH9-!%*PrL#tjE%0>;N`kjQM%(It zq1>O=vUb?^7tBll6)36K>3O}qQHXgf`r!|<1W(p~S^UM2>xO?x{tdb)kM;g4|6~f^ zALrkYn&EjdnQg1iK^U^I9)(Q?Z{X*TGJ_61t@q~u*Zwqk2A?$VB3V8InW=Jpiu^AM z_#5!6bvZnT?A6$Qk@P`MhrUA}ehnVUPHERJD=<#)#&{r)$xYNB)7(=HdwCdbL5Y_j zC)o9ur9UTlNT<}bL=O8j3~XR5%&W(|^-9r~SW(9`4tf1wfhV-IFc0XlAx>t@Q?K}o z;9m;A4{(&jLuFWyGq(RD8)^m)Wa_6f+Y-{U9O!$!J)%x(AtxH|lO8bWH|8)N=*Dhs z+i<)f^mQ~o5a?l8`;w_L+0(uzJ2emMZIh%;*_{}pz%SyVHzcFbHZ#}GnhU|-cj#A& zPD=~(?qki*p-zwM^?aR1e#YXLLA!mO*;57Db3waI+L*15(P&&d12WzCw8eiAa#Z13 zoil?EZJ79w9Ol%Y^+%LdI7T8A`1enpb5G-{K@KMDNU zkxpKNIS%TAbYo1sQdZqD^N87K&25F;bi?GY&+Vi>dQ-h;Su^=vIY0S*d1T%Prrc+%4Y~imirr(#QPX3c-_QP>{|3>L z6U=_y{`^_wub9YE4Mw}C_-^x7)BgO9`HZC|Aikr)G%G$@T10-+J?5?FO9>qv(X2Gy zpyMzeHek#&PQNIgaHRN-qHzdgpF!79%xQ<#ZBH)RQ--~|3$A}Q+eY?m(J@gDE9}m1 zdxhVY`k04F>D&7n*ACwXTZ{HkVoP02`xPlR*rq+Yj3Y+qE!nKy1<-p9{*A!z{8wPc zN!Srv+&n&CwiYBiU&I=Yb)C*{%f3RumZm#*5=^J}+lsFgu$@`XUl43FV3%GcUj+jB>$kZVDD*CMt|izzfc^ejh2G`Pl?1Dn{kC_m z^K?U2@&sXb8wvgw!2hyPz%LWq4EU)<0=_}OYYDz84z^{i z-43+HzJGc}7S?1YU>=u^K0gP2|3r)%&%vHk?C6|}Y+c2FC5dsPK6}{b8aIt#16U{Q zca6J_{BfIf&cJh$%Z5P(h2yvBoQI#&xqRIs-tfH6IrN;~HFzHL+qwmgp-1P8Q{sK+ zMZ4rLkft!k<#=)2N}^#nrgQqvi*;4JDDpEz=@FJ)z894Ewa`d?7; zOxHW#QQ~~(#k=H5deBdB+0^tUL~op{NKHSF!ig^5FBN(<6i#wEeyQYlGli2~6~9#I z_voEfir;r$ic5Zr=%>156@Lxkr@8u{SNPvV;dGbnd4=9K3TL>Ao>yf=;Y`=Sa{`}i z-v+d^JUcrg(VE0Yb7|fytho#J)gEKsWsQm2<%>1@Gwx$f%T6^-hIt#Q0`v=ZgsCs3 zb&G$V4m`mS?$$5igo*r1=QP%XC+1U_Q+4D-tlSM*exbuy6N$peAoO zYwmsgM4*9~Z9>jQV5NUle z=F*dTgFEj&so4y<=XFWVM$}XH9HR&hDAJ!Y@$0f1SlhB95;I+;#HP>Tb z#iy6&6OI9KFOp3SlgIF7VjYNdi9rw4ypICTlE=HX`5R9MYA6rd`*R>4L$tJT?gZAn zHw}GlD@$?cSSi+!N8xRN+v1ck_G+9+Nw@H9Hd@ie+NiZ>Z} z8lB^K8eJR3yAXI9o#S{KT^q%l2|SI?aXgK#jpAJnJdMtAJdLi6;#~eN}!J#?e{#7`A_c{@06snbv*o0X@tqN_Ky&cjoCchVs!bEQ6k3 zpTK%(z1n_1;ib8^%D{_fCHBWf_+f;N2y>hl1Z*>4_Bi1DR)p_Im?6w@{zJf80kdMi z!v3)c--EDMR&f3yV0QtAz33AA2@zg{aBr-F6Qk$+tN_dim_1X3n-Io6q{wTsfZYlh z1I#{CgqI?0#~CKh&y@o9Gr+vqkFj4b!Z#pnMVRBHK8V|9z1*{Y>tR*&k^-nxaeLO?` zY1hbx>%Ph!qVb7EQp?A|PN!(f=l_oWOIt`M&2O5v7X zCEUsi;oeXw+=eRQw#*l9`_;mI>Kfs?76{jKt#J2WC)__T6z*WPaQ%z8n}xDq&YW(5 zn+w+jou)n5O1Rl@X$>I@ZadsexGuP~R=OWZ0t`uOjTl_%AgOc*4A%=wxC2a_k<}soUP>YoFS6Pinsm^YE*Y-kNCkSCN}(_E*5A zHA3sg*j?6*G5AH}7lof5KOKG&er%UjXP={8-@*Fy``SG&qCbE=$AcLAS&G=UgL=ya z?0I7n`&0c*Qu6X#CfDbdAzX&A1%6tSTXKGIJr4Bv_*%R6mWO#1{p6pgX35ZhU*M@~ z2hOe)F|6m~Tw4XkunprA`5Z2EJ)hle9=FUJ4cH8sAEBjsH*fb}=abTM!E zGz=cY`jj>u! zC%8-;w8w${--0ZRMeoJ>os{0xn~pity;vvQ8S7lSH`Ylq|9pyA4<&o8&Z$ZEx15jh z2=uWC^CV_qe2-Tf|CB#4p3A35$j6N~umk6-e6c&S8&-)kHD;WP>&d}-9@?b!3wABF z7n6lK=USqV^k(3kqt4J_Y3-k#zr}dRl-W4Y7R4?po~=(T=5BOiBX?sHaYzGjL*ilX z#wQj>Q+QlrBX^S$-P}z{Jj~s+#Nrr=&q!?K?)XGEcPAts=59`6G0vD2&ps!yk-K?` zjo-)4oDJUUuwKaXKOH-hc&j^?!qJI`5zoUW3WFatd;x_GJUx$}PGRt;#?PSexWvP! zl=P((2ES@}4uw+^8`ZFd!r)&GUqRuF#KV3ieI(MIH#_F!HF`FqL048vXNipH?;B!--%NgtM+t5v zf1>c)$&c|T!8^&1u_yU^$&c|S`Mu=FSd;v))ioPvjCm(Rn-FU`YMWR2v8_8Z?$^cW zFVXf1X8^ipJ7s}xOHE$PkLyoK9@1$oy`9Ba_eMMJOA~7WR_HX%{Rf9>9er%RqxznK zvqouR9Yc%rx8cBe>(}Oy`Y%|Lv?c#S6LfY^W|x?tvjzjZBnXdAG$Cw)u2hbJ8xk8h zJU+2qfiuuCKu25%8=xEe;Wli%O2`Z6EF10@evEZ$8a4~Rk^Bz{znT1x3O|?Wqp=t# zo+-BtI$fz+hZ4>$Zos@kt;YeJd)$CItXh|S6vli)t>k5KEip2C>3 zsdcr)~B!_wGNl1=5w*qIw{jV&D`AA&Vfd~BkD{C8;M4T*y%09!@z@rnD%f2W8a zl^4O*#g>xH+R1-!Y^f@@`V-)1Bf^U8EEK*UVMTuV6yA)mB14J747$Lzgg?!ubzzHX<~g!;+d+fw5Q~&V$k#d!9ME8T-{W zcGzCnP^{xf(AmCjnzi@q|Z%%5K z4|&RVq&XhdjD3|F>epoSYhJX6u8{=Zf6!jcnLy~oE* zIW+J36A65Lpw0UqC)N53(&c%N`{BIN-g@RT#`i|*;Hr8eUzD2FO|r+BqXFmT8enhe z%w0X?P>(bWdoM=DMwXJF_Fmwp{SQ?Zdrt<&`%RFoUV4A9P`X`v{z1*dcOmk?JfR3@ zDGU=k7s$GAA7#>Q&l>Zsx%nS8w}Th4-l?1s4vl*ZJkywOHpY6GOP0`>LtFL%=zYV^+}lIb@p8jv z8_#hqkk08|5E}mxRpA{I>k&JqA)~)B1#ovST6VzYLh8^hMndBTqTa+cUKTk><>Vm%8a7F@p?AP{b zbbhN2=dyMb6Fl85ftSmWej9LmMgl!Jm&wz&B0c5Vk2PVn9Ot7PCt%mqu-dl~&2v$I z+A!4%>0jvin;AL5^7y#iAz$3tp>a6&{o?bmeu}-U8nq4zlsf2E>Y$U?L7LkI*_)9* zRF0u~7B~@jg63bU{eb-Z(q9sisT~X>POX=nnE!;U7nPpcZi+I54VQ|T|0|6R|NDtE z?R|e*DnAz@V-H!{CPCgZx6|6T`U!ZDxjqEr&anR}3Y{lG=gIJN-u@AEDv}gB*mstm z2~X$MA3;YRr_jNEv9u{Xovt52r$0`igZ*O35uVP&KZ1@!R_I`#S=thw&OJYZPDPAD z2m9yJBjM>Z{Rld8ltKsl+)`(FIyZ!&qwR})l4vJ5Bjni+8oFdisj`N>Y$0*paKA2yUB`j61|i~h=!F_OaD54TC+jcg?G zr&s#|j1g$e+_mS-emY0+GG^UKZDTTca$Midc`|X^Ai0SzTG#l`46#1t)2A16-WMCT z8z#*2G~i4RGnDzY_E)6`d4$ae@n_g2D%au@KdxK@ubo-0zaQuF51ZeZa-q+qwJ$1{ z1?6&5xlmRrmm7CUsB8w5&19PA!JN2+)`a2~$8E1F#rl{bE}qJ(mY=2k-zoQFU;nsr z=f84hxnKOT<<`_Em3vXR`a~IdeNOmamMdiKD9tCzgZ_`-XCmFCb2r-YYo9yVcJ3T4 z!`S(p+J5dhOFY%9e6`S5YNOEkVznLRpnSaE7s%V?BgxJ@hWV@B@wD1Us&iGLqhs?8 zUDw$<@IRNw@_ev7WoO}6EzhNAiT|PU{4-rsp8T}`Re2nL75tMe!#S=JuH#12b`xw5 zKQoT=I;7`CSo@Ufb$)ICm~=eYuU7myNT(r$4yUW3!|F+g#Qa7r_vvG58y!2(YX4Z! zEdqc2Xmdq4hh+RJE^L221RA0J_30JA$G*OVai0$R{Ce#7SBQ?CtVL`uDxD=3NJqEW32$q+Qs<-sO!gL%1Aa zJ;FMK=OKIr!qEsvA$;YogO~Nrp}YqVEEqPtpExYF$Lzvw$T_QV4+8C|)PX$@Lmyjc z!e0Aa6VA0^E<*e6GWNXl9>dxx?!KAOu8Z>fpU%r*M|*MijmDjHwjl+4(zrVBbkY$A z)>#4v5{GGx*xMGf%i9)>A9!ff;|Jc_B>cd$-af~BNbEb%*$lniUcL2VoGAn>uVLma z%t4(4@TX!->OB=G$xd7B8UH|)zb=+Fzl8ZXoqxD-!-VZV-NX(i(Oyludl2bqKk1oI z^v(_XiF`lFjq&Uy$LUPN#E#$4*jC~B3*fHC*$XxNr4lAt^q&mOkmJR9EQYoEa$4KP z+PRz~&SSA<>CVdurq}!BDdId9doIgaO0W*VE)(am*rAEe5`v{g`Q`cIJQgd=cTOeP zcED~F=dswbbDhNmD~$HbP2xNjd(PxMmtZdewqBgaVuvnp8VPoBj9=~$=doDfMa~?8 zy$;xRaUP2;o9WCXSb3~peo36iV$WUbOd;62fW0oxW3fY*I}-@DNcPL`2G3*hdx-@9 zE8u?(p2s3MBlt3dUmgmc$0GPCnx78={(bN~7Qv4Ze03b|eBcaC+YS5-O=VZ&=oy-N z*ln~SoW;HZ?P?y{S2@~Q8QL42$^H+%FQK;YDAB%0=@@nd>CpO~0d*K^TYG*h+M)w} zMFngM&9A7xIHXx~pgDu3<;=d%_Z@f#-=Q|0?5M3;^b3QasrCtvr$PTP*7E7o#299w zVY}Q?hWQpd;>B8ID4lS$&*1;%ApbrM|81JF80UYr;Qw{OGsY=n`y5qPa1Qs_Rlr8FNa@&#b3=N9*ZI(7yut%tLP4kRhTnVf3T)6%^454X>t7a8Li3AnSII5ODxHllc%|_mgJ6( z`^ri;VBH$_xMlakWngt<52Y;G?rwYD_AvH8Khr533GdtCXs9nQU_ z_2e&tA8$Q0;r>n?(oG@zi~BJMV+)w2zXTdppiu=HjSY>nXs?9gZCE!c2LCuuUz#_- zjxglA8Mr2#ZIV-%Q_GtL=f;SK`RC00;J<*=(ak z!nePx&i?krYB~AxT-xV!0biD~I^>q}Qe%T{Hv3cly{yOxzKe3LjkDg!N@LEvWyl}p zM_cp90k?8(IbWw{kilHYgxV;b#qPrT_ltShz=xia@_4s=)UUJcHY3)^xGMAdBVUH`NqUlTM4%ug_0}f9X zdtbL>-Hdc-6mAsoUC`-M(5rBBPxU@$5z;xZ)|5P zVeM}**8ci+^Z0ohx`*3~J1~Z5wy@xyd$!U#U>xqY)L|VE=T~rV4(BJ0?0vNPBeV`q zcWQT|EoA&F(9=CRxZXFQ?6=c>s+)~$+uzeHCh|*Y^E&XR$1euIL;2WuKzqhJH`oKt za08CBruG9RfFF!C^E8u@{+}rCajdKM>o(DOcV|ED<`AFKk$XP4XS&$C=*69#;~0|> zPEmr~k=B|w+<&;rZ^J!}ap2Pk-g$bY(dD!EXCqxdXry6Xlkgl^0~cd7@eW0WB10}K z)Dgvj4({m$?{c9I_*yi0T1Y%e$YV0m1-hGwgt~UD@#j>7%+llvM&<^9VhiQ*7k0i3mx#MOhBswOWO`9()GkvK~DE=Jp z(ey&U_rUeyT_y${q&Ii)o=Pg-SxJ?lKVRdmoo^t!uk^|MF8J!D%((^az>0QN54!oA zIxozBtuwJ8Dk0IbsVi|B@~NYDZlF8Osq{uoT+{UHolB(C9insD2pi!qVArl&hVW<5 zF=}rluLS57>`V{Y#(^ZeXIVPq>uWl_z4~Ryu@gG83ht{5nEy-MqyM*^jLW79Z^KZT z!|683g!XnRo@9}%$c1D=^7s}yMY5sxp~|Ee{ckhgBAE_esm!Vz>LG`G7T1(t)4F6j zONp8Yki!PZVOdQZm&4NJjZ5b05}Vpc4k&jyTnZcE zFJsrO+Xp#N|4clFqnxF6Tlq?y1*MpAifbM`6?I+7yFb{^GWT(yvFZ=;I-Lpl$ zEt-4_fUn9+&ChmOaQvmEJw8M}D!#8&;7exFC#t-}hoSA5m6GK^T`=>R@(+9yz8(i> zih3Mf^BcsP-(dJPzpp_fbj>eZA2wH0AHIuIvV7o)=8)8{rK4Z#fsOCpY4+s69;oH{ z1hju>-%;%&aCe07MfYONMtwy2p#bkAX#bP?iOEw%KjFKOZRnq6Rd$yQ&V6AUNIu_5%+krf<)5tIMO{KxYa-22L zIa|LG=Lu+?z@E5ZcqimSW3o>}lo@gpW1Uvu z5}o6iD~(5a8RnK*-W}D$-*m4sB%hxvXBFjob%nXzc$-O*3UgT&=Ak%?@B-3FdXx!s zRaRumy_)OMtX#*sOYS;}drg!_K$kI8h`GVBMR=zojrosHVV>P+mn5GeRYUPBsXp}@In6=dqz6{;bT*k$-G;1FRg?`Ih7t_0EB^RULuSGr3*v|TS{B=}@F2Igo0D1i(&TnUI z_Y#Bfd-1kf*XQxm2 z#>TtUcs<*V`c0?&y0DHj@F&5iN@oY~Fu+Uq!XC96?bW|RxkwiBU#t8o-v?1gRHk0k zG5ST$Z`Wr+Rw|tbL5JGt%Uo_%elz;2F8E=8@a6{9@tNzxs3$(p^CRy+LtnOiDrjDc zG`l|)@;HICClxr|LCoHl;rTc<%2^tVcR68)=)FJMJNOIoGU7hG2xquIM%at`72)yj z0|=XORwn!13=gf7W&dXeL@f3b+7L&2khV3$Bln1R{ifj!ivx&Pb=vsrlv*m&@(aXV z(6{IQ>h3MC@pmG=qP2(fSqis4ri|eWPIswC%?fJwaNBCN1iwM$1zd!kx#Jx(2S?8_WmyVQOq|K2=?@`bEya z1^HUP4D!@_BAAC+!z(+f=oskP{}J??4+Z_b13@}QZxGH@{iL*i3)1iXOOVITPl9mv zaWJj@!=T^#UeIs;Lolz-KL+9K{UA;A`$4$*iy-|o#y{UB^D&AMdO%~uZ*X5qwJ#rk z#cd#mpJ9VBrr35QnfHAT^a-+Jhs~h#8Q$_B+rZH7eb7b60jys@hiDB1_LA!wy;&E| z?s`O?m^%jTZs@%Kqpw81-AZ3gV;Qoyl=mLwO*XdsGuRNt&Qco&tVgw%>RVxIIiDHL zQ|vvc@tyC%7VtJ4gujgY1EKs}KPdR=AC*IJU4(dg8a$aHpMJ;z>qkA*e^c8f`=aIP zmIBx#qE~JRss)nQ^Vn?<*ENv=;r0X z?L7;wYR?WrPIRU*l-@+hN;@V{*DB?emNVAgYR0h{G?t~khiw?&?6?#6G|={Ky7Uei z@2$|hjn*GYUlwA05bKA0{ekL;))ujbSlt0Te*|_B?{ISbPS_~A*Gg&4uupWCmHb}v zsn>oxIDQoO={z7pT6iGPIRl`^&Z zMfxsHyo2JEat>gPg!1px#M>!eDZ3f*YFSCA_13X(vFL9e8`iBE`*w@&uCZbL-DB}3 z``EW%q~&A7Mc_-G$UKs5EL@r-=6R=Agugo&XiMVfC~Pl}Q_fN34F9QLv^yE+C~UaP zc|x&II49pcA8TKq~Q@2Q2ny;ytwQdgpkP>~rKHi{DpB@siWKOy|_u@J=Ys%O1Ni&2(}R^M99zHP3S9 zti{DC=vavn<&)On-7VnoanASR>@fRY@@G!NTx^PYgqiiH<-eAD25&Tb4tL%W)380^_zp*bA(I6w7xrPuz9UCYjkEm5nhj zj1T++;O`)KIaDq=Pmjkuf$U2f9~+O(7gi3UuL0aP8E1oWj@b8~IP=2C%4zOA?1jYB zSWU5wbWc&WPseU#uGT%GKP>)I%=^0AZshy*`=1*(x?fM@0@6R!7xwJO-XlD{;%%{C zk9h1EZliT}->;GX2#u@K+&4_X`HtUV{Xq%ej4s9PetXvG+>3Nd z(5*u_*KCYp*IIM!bEvJ@HZl*wN!GpcX|Yz1JCuodH!vDF8(U`+4f~>mpIS57h^-oD z17YVI{-XETo~Cv8AWv$YsxS|D)}A-D{YvmjYhwoVQ+W<^^K#TBy`%5K*=z&D_Y`oC z#0GopzD|rgJ239N8vChDhJ+5Ql=rbTfq9nte$GS`NAkI%1?qQz0k z!#p>UYhj+B!X2f51@q9{PpV{|>$UJ}n5UZHf7W|wEn^gaA@f{KaF^awMeZ&=t;MBF zeR|K8TD({Ew0;;&w_ESIobcY%duEgSw%&88Hr}K6%p~|P^_~(f{4wU4rVTeU&-vQ$ zT1M-F!F*bnXOcGD3fGEcfTyqI~CwP7doz%B&ydy4FyutDm* zBg->l>`m*_@(|70gLiJU`s{lmsLz$q=StX5RVS>_J(DJkaT&e^64V*8VXPUpG%t7_ zxUMs`#!!@%QWTt;R11U5*eNBwc=yrJ7E<_!$r zH3(f>?nhb%8==D0xupC^GC#8*p`Rt1YTJJU{Hi#eHrgY-Hz^lTlX9P%Xb?G`Oxj|1;%c)f*9&-g)bJ&f@Y}$o zCVcawW(U(Xy#yCq5hi_df{DuHzx3q8FNJPpJ%*T4f}u4o6I z5qOJ3^gzZpGjLY2M5TEc!t=qK;!H z1obBw`Gn%1b^qfokv>h6o`p%T>VWS~fj>use{Ar~eT(k3Lbs@mt-~9bsw@U+9EEjY zwe5wId;e-7cRGhf=aJa+`MKlBw)Bo&+wNK?>hwdIDevE8o`TN7RO3a z7Hp!9^4AC4(fAc2ev+oF_y|rg{*JS^&0hnr1nIP68kKJUa+HtnabP_UZ9vrEgI*2p2@b^vN3jp{!p08NAbSQV3~Xkg6CNpp0{Xt_Sw$P^G4w1Af3vm<2FI- zkD#yCUkh-;)mhQ4po{(jW9?M_E(FFNgQznf#xLFKTM;<_OyeMzA^iFqV`Y5o1^Q#| zJpPn)hTH34+cL$5r=iyQFv`K(mGYiMexqntm0@TxyxFg&Bl)w0kZ^KHcv46>JtUkJ z5;lf}i$cN^L&8yG!zyndqD&0-Lk$zJs{b>J!?%IdFfRx8vd4t6N39L3elOCh{^IPQ ze~4&e4?&G1n&Ilc^7`Z~)HTksN^Y`=^gh7g>xmcfZ;73Y_r9SARi{K-4ITTEJ*0O8 zu>J%)ig63tgHL(ymh`{~epKC4`S_C$<6?{pjHp}84g8iHMcp32TvEj$Uh&2$r)|aB ziIgB{%Xo)X#VG)7DVdEaAMNE6KXj%;N*8HtINPMAxtP*mUa5vDpU|;K|23kGF_cBM zb7~n};1Rlv^~1B@A@84$eirgXKid2f7>|CmO3X>s{;(}1eblySQ;a!S&Jy%fs$J;6QW-bkoH3POE!!&44wZ%N3Pl#l zXUgK%5Ltw#b(ANId!Sfrk*wkG11<&)8SyE>-AYO;K7Ip3b-*L8oGtLI-PB(wgvezW?X{ zEN6M9LI-PI(p}-{4E_i@{TC~Autp}`9iGl7KZ1^9x$&anCX$x?* zh$Fk9;9j`(K647B(}uE)y>8HcN8tHt%wvw)wF}rt-T#7Wa0`b$H8IkNXW#xaSa!`wlTE3%(Jz zUdA_Dbk2-CA-8Uv^>bh?FjHcW^k!bc-=BE(`J`H++YGvte?7t*$B8$9%M*p$h&b8@ zqBMkiE5bF{(;)nAXewkED@Q)SuBgOp??oK7|0laI5k7R5+ z&DggLb2VCFwSu_hi6B zY37H}JV&8fFq&4-91pk|b+A+(OEV=5&4dt|vO;rXteRHP)B)Zpv6*k7J&^9}n3=yH zPvs>!S$>gJ>qi{PfOKnXj7X2M{I|?3#&Tlc*E#m6UIm zdkexsPpKRe+zy1P?5Ico((eV$(|C_~KtIvls^Ah${Ub@W>y>zwk1vqNUBIFE@`saZ z)%4oLO* zxX*$5IVtkIgy<=C`0-y8YI|X4Der*?s1DQIQxVVWBOYxC;VBA@Ld5SqA!uw;XsJ9E zC^+T!!~TTCk!-Swrc$Tl;SbuL3GOr{?Q@%g^i{f)HeQof4oMrOq}8{9_v3=M=M^4R z8sAT%yBlWzMqOw1F#MfL{HyR2A8PsYTa#)Jp}qW&J@!o+yDqTk%OrkIp$Kza+tCcq zudoMYLmxx({O*qlwf(TERz<$~tCH{(zx}``$XA8+H4B*9m;Eu6{-B_5Q|`Uc9XQ%= zw&6YqYgsegA>$nz>^s|Vmjw4ehUR0wq|&0k z7~LC_F!v!nM!wh^6K4xfuMpo;qcl`UPk^qP*04XA7H5lUZa^O4&LGe?tw_d>|5VE{ zadmaEAL%gb#uDpL{0d>y3-&ZGgf45#HWggxd#~ z?)fw->9605Iz@fjFfO4w_o2=S-@aAgx89soTc_X}0i(QcM40lVvUpG63>)_C#^~L$ zq}pmFO|Mg-1K4x>M1AO3YjqW+O^o;ZaK^p}edk4x8OEGFli*Sv)WfASx4mAGhVmm_ zs|1|xq0_mLIppW#vvl{R@Ke3j55!$Z{7=L;6%Q_>yBXjcS6{2pB-n-UuZ1pq zfinU>4ViwMmLB>fxvUE?x6ZJ&-xl9*^(HMg#wP=5fW^513** zj}FH49(6@~kGis%-;e-$@358z;JNN+E&HS4{yS@NQFtBT;Lr5}YZ)Z?Z&-^d9d0q; zD68u|)?z4x`w!q7?FU?wI*+>Ydyh6u@*W*5z>{i(^C)YnoP=*17{S8{<{{pC8>#GU z!~_{to(uRp%+n6P>shJAMee^#E&Jh`CL^w3+-rjb`<2vEIT@}_YH5JmPH_hfuek`; zD7CZ~!#&7a+NZ$%J#dMyT;QU9T>p@`%+0qU--5)``-!ikQj3XjPl@{IycKa}v8NjV z8!QIxJ8+Kzc!ntWm!dw1&MB#7un6w0Qi}m~YBEt={A-o)yJCb%en#ACAFYvt{f0{ zh0asd?~PkYIz!{`zI_7DlsV^zhxlCpex6lEn^eC|xBtmtKR{=Cc^?w;eLD4g*dM{W zc1}rW_9(xuSv0OpbAOL`tJFD@U=`bfW7dI7l4_43j^KUp(^%(Qgei@-4-MDFVe+g96)91 z1YTcbng74x--G_58+NV&b;ifVmx}q!hrp-4;UXwd8J*n2sac}TH2-k+Xe_5x>DR{?} z>f;HVo$Gx?)HjVk0{iIOyl1|1Kysf$Jde-b^umDFVLQM=gCIftP0)Tord2aP8=isqdt* z4#6$Z&ESIhI>R>AW=xGK*xfzug==ed9r&pz<1lkWMA%6BQ? zp?tUgaxCA)z!?|=8}B|BFqQ9NoLyA;o&vbacM-?Qa8tRoeCzHOeCy$=e9OQI$9D?g zqkQA7YnAVKz{&n;<=wYqEZ?WWSAM4!mhL_Yn9BD%8oo#1U&G~{?moqFGTc-yrk{`Y zfpIAEqjcXXu)Y}OPAb~b1Bl~z7X)GV0hWfehk-u;K1o;mSF5_327SQyo0N2bdY$^Vt`dne*P!P+XkJCinKho7NNQ78QV$UM{o>6sRWcX0W5 zS_``rFz;0C-#n=5Tblb2V7)o?eN~*3G~&Gg`mwH6YXGm;;Qbk4J9O{hbCRIXwYd4mDxoBhQS3lWU`t^+) zOTVexSo-a)~rAA5iVdr-ku{X8D~+x^dDtey7nk z3vUc|d=L9=l<*DXeMtYH)ZC9V&GRz_Uk%>}k{j`zV}X~>@d$rA_Bubr*sfwow=l1i zH5cI=vk7G{%J0vnb-f0Ado|uLZnWb3@{RsLj}dVaI_f7f?2P z68R^%NjL16JCT1O`LQ-A*2gBg9|zv=kmvU}!@C`On?3N8eEtJ)vMV_3$=5WVxLnvN z)%G3&4vhyM<#-wH%?cj%TU0-4Ji$+ZHl2}g9mUBUgF|ql+X9#BlW5!tzxcjdrh7H~ zUl1H^VgNdE5P2WMTHf#efitw%CAl(clU@0cMJx6kF30#!{f?ZeGHBCUVFLqgF+cbQ zmb6LOJj#dK-kXqBUvsc+o(3QGF9N$KLq@#4+%(pHVXR%F+Abr`%?sP5AF`64+b;c3 zC;7SU(hqsb&uy1}$cVG`!gc|l{M>c{pZwf*0iXQbb^#yf?}hCGKKZ%r0zUb$j)gS?)(`?)4p@5# z4BzeK-+R&0TMF3z5WJrOHW&h<^PdKcp|oii0cHw;EdZ=C1Xc-HLkMghVC^9=oFC=i zlhNiu=Rfy{;LQMRFa$OYFztNd;sru4=sTk(hPeX1EsFbp_`Y+N^IUTA4beh!3-YlR zM)Bv;Tabu1InUwo7dUfx{6$W*X~fTTj_2{0l6^(`%W=gWwrPU+9%nN7m2Y{{J1w&E z{mwY@E8pylh2N^1@Bd>ieZx5(XA#2hpUDho$8iRq<|6EFe3uXSWLujsCZP2->%Fu# zp6+%6{w&%9trZe%GvTrHp#-cKQuwzBTQ{2hn?lnAM(JAte@IOa*g9=`gkPZa_nZA| zLem3A>6-z+S4|ICqc%Olk0VTd#U$7WaxKs)vJYyxOE7j({d``5`>6EBQ@Hmh)%!a6 zv1}%l?OKJWm(YIc`&;yWR|<5%gzugkai@mfoMKYJv-EaXKDoHdOmBD*AGD5^e=zU_ zeW#mf%?7PKIyO{=bQGreO97+tE8Pk1MIYa%!@4=<5}yNZMZZjAh&1IA=u_Qln%mLG^d>U@AYgw)nq#s& zlj8Re{#TgT-cj#C;(fd2#-B>e38S%|Xc@f^-Yd-kN4t#G}?3;Zx5O{*?1ZYG9 zcL&M_V_!owsowN}(cFla4e!2nDP`*hoZ+k{mCXk}sP2!SBY2n(*$@wzXm{hi!bX$4 ziJv~=2eKfU=jF2_^7O}_mXF{2!NEq~ES0BsRh~f83!0-mAxu1d2AIlIr-r9C(Dt4d zJkcErEAAhJ-jU_+c3}*q>X7!`Qg!Xvm#(fU=MJ-E=9I1#UqZj@4oOf*;K%bm;4|0C(W+xMLW13pny)K+l zRe4tL@;(V%I%{R(=dBD)u){QdeU!@30KXA!h5U48%7kAW;^^B6KGd`M701)Otom5A zmyFv_ugJvy^LXq-XJJ2j0`{e|u}7Zc8rc`)Bwplk>bQTx@WHJ*C-SML@j(iBIQF91 zL;Zd$U@ZvKz7ydPYye{z8L<9BrH_?1d1l{Zel#6#gbYq${<*k=77yLrk;a;de;G6r zp%adu-}`7gpyHir1wfPEa@d1!;{ zSHE^YaLs|h4DxruPvc(Lixply-j3BriTgAM5^D#}Gy5&bgXS>@q2Gt}ct>`s*&n5| zS6>FW7wd{NR`XqE_UC{Vz$M24nhwZsL#yV{(-@t}_aMX}~Am ztltOloqDX<<7|Wv^+@%U3i%O?!amgRAofdrg(w%`SWj@8Q1?dU5eI%Z2Le6wQ!Jhh zQg##R%VOvT?492YThMai-VdxzEw5F9FM5B&gl~YXL3jq%H+gyD%Unkg2YyRYCoxWB7ilcaPa)CmehOQ8L@Bi8qo%mr>-q2l&*lP+BQpz`Tu9-?p-55I{p$vOkH>8>66pe70OA{g!fZS%b@VD+eH8+fC4At}G# zARXFh^+yy2pEnSnpl?Kmbeu`TMnp7umAuqD$A5xxplX2i5fttIZ@8;Z}1(E zaSqG+_rMH4`jzY)As^BUE@R9AEni>`h%%oCeD9$^3Dp^$qfpLbR1>@tZ_AL31ACZ< z>WyT$lFKa9-3-@-O%k75rn~F2M4l?WG|&_E1sfR>mw-6RKM=?? zouG99UPq$N@NhHgDATZ%`maD%@yd)nbLX&J^V%kUOU zJ>EQpUZKo(+;#ne?%9LyFL_$nnPkD|6ok!KM<#t~RbVE-I%!Y$CwR{Ue#uDozH1fv zsVpo??Ej7+O#Blb^}+moYT(jZM=9b+XU>IN@@-)Edz3%I`3QIB3H^BUQ_*Hhz6tF9 z55$*%-)+z(ZnKs4yt;rnVJnW1jkjW)VMlvzjbSO+$E;p~dr!lpi@+7-%5fhC{cgST ze)1>rqkGO4==aF_8_~BdK_7Pm`np>5c|XCKtLyRh?P9!@Bfdkv7wPHT7`@wbwsT|Z zSn>m)aWMg~OJW>ol={MD1F8t~on944*n{y4)&ABw@Dv})8Czt6>XzNMd zG&W*D-3}H;;h_MfNeG0raU*xekk9s0e0u9z>H6!pM!<3V@`7BcVN|bB=CV%E_XZlxu4TwZU7 z%C!Z4Ri_??KU9y_+{CyZVV#2OQA2@_>(OT5QhkNqK^`o8d-?Q<81L*G`@KZcxch&FiOUchli^9Slj?9VLXe57+7qj|y22ycMg z)$pBzimZ&LbC0J%nRXP3mYr5yK=kn zUdU?ri{Q7xPi;fxk>2U}0{4vu3*Wds^m{N`+MlVkAHukkvE7zq2EGTX_La~44ewAO zj>cN`b&0iq0)FMhoeOLepIe~92X0ENJplM);KiKPkjuRT(XU zjEEnC^OFFOe@(IR-pQDb2oz`Lga`rR6WUI-hwIDt0j34txPbcb}>}7Yhu-R9` zB{@iVKdu6J)XwtI=inSw%>>}BS8V4*#dhLNB)%b1hq2=N%>%J-PJlK)+-3t;^yozkkXDS**iFGO2~bzm*p zfVU#sAsYi^Apgv;-P_hX8+9>f5My40f%*4QKZ~+XC*JjDe*u^0__Lmi8!Rlloz61z zd1g87_nvA0)bGrtf)?2uYKv4C+|EHhS$E|vVtN+e3LAS6aJ9X*RbA4tscKfo-(i>N z94hUpH6b2n8zJ8ecPj2$`rt3ZcSOdu7!v_nc#EuP8%Fs9G?Z{pUf1Q*(`&0Rm3;kXp$Qwy-222{>4Pl9yzX8IsI|nF%D=pCoP#;sR>xQd^0(8sbv4*or@` zq%Os3t##>cOVr=LL7>I8#ESWU&t2Yo^D^0#TKfY&#yRiq_uO;OIrrRi@4aaZ?#}xg z?#|n5A5rkP=mgx)YsY=GF&*zl$3l0A16{?lzBxy9UXb`F)tC5Kn=dg*8)r~Bv$42SBE}_NqkJ;tVbHVnGj+7(Ljo%KZfQ{-2L~|9y#{fF4G*TwuE#D*A;DN;gpZ!F#h8n#NTy-4mR5+GCCldw@1gvKNJ-o@5i_CG$!pLU27xc9EV)Y5d6!tI+OL+qKPjBx9 zu80o0(O=Sko{o#z`V8ha>3_7IxC(wH#CJYmzvP2s$U5>ZQP}y$r*NJ_VYVug7fZH{ znbJyYA%)%v?+iPG`{WxP50F5j>p`ZVH{@ALFs)5f@-LwZ^hQ+SLgkDYsLMp~OEde3XP`--K1fTwReZH()2 zq?a<)LuEb4<7|lgk*HhwM&6V1z4=_M!&w`fjj%F=LB}a|LN~B|BN}(-HcpFbj^6Wx ztS@AwRq>`O%im~(DF{=sHcNz#U<*a>iNbGGV>HH%`vuec4&OaYc7EfVWJ+Wm4EM_d zudSo{;+4(`5f0l%j&XeheG8$#^i6-QV+pSl4z{2lq)$x;ZYjPN=}33DBpf*kFpm1uVHkE(R!#C+=q{ES3_9Ldl zg|~MH+FBN&y`j*v7x`a7uHDC5;Vf`q%rFjtt!O(g6Dk zy{Ei0YI-VcPTT&0KiU`Qf{uAx)fl!;89#k$&x7b6;kN7{XHVyQ^Lu`XZ(5%aPP&R0 z^xThcW%ATx%3SFGr%i=@U_uA(Pyw$V-%*G*Drny?aPA=#nR_&Q*o5=2i?}n_P zauo1A`t2Mu#uY@KyFQw@$1^jxX9?ztG7|4xpf9Z`$Eu&u8isfh<=sDpm!ZXsah0PC z);`S1NO>+6XRvc8^E}S@F|Jj}lLXwgqV7m}BCTr~&jT&)Lpxisuh)uo0nyh5D2w=4 zv!vsH+SyqX3Mc-Pf?wiugj-Ix#yyYE;;t(5yr%M!ke<j4X?bts|aq>}Nyg!`q}$u-6%Lo!(F(e70uCvvVrl z@PT39r+~)^SsnVFoyltAsUuMbwR;En0;OvoGv>C2bxB7F_5j{2U?o=I|2KBmhoD)? z`#ydt-4gql+sZ^b=i5~F_>My&E+$>HL!4<)MvHRZvb)~QQ{W%aT-=4UbgqNS>U@*u z{}aNnK4ER2k!si|y9AFkkGs@;??j%yx(Y8hCT7fSipRtH6?)C?qV_*6+PGviwcm4; z!hR~kVn&T|y)W>k{h8zIal}a(D{V2<=9rOVZi_+La_kb{u)7|ZjQ42JH=;%BuXWt1 zj1QuyeW9ZR={sHf?5=O44Q1;>M!Ph&fGV+Wv0G{N%LPfWBcKVU8QLNNLoM zc{y#M-CBW1=V2pXHS$rq{g@N<+jYq9Dn~k+%N{nDjq)VU%($XF?LGF}#*1;(o~Ch$ z>R5>~V!pBb)o9!4r$!fzOC$QDxk@rfw(%Fg=V#bkUbedyq8-+9yFW6@ZQIMs{c1nu z!ltK}yBp<3(m&`!o$djIwEoN#*QKHC-=#&hD~PIfCzZKF{;V$o!B+ zcm5u-A7xGdrLAZB^ESxOaD9bnwhw*|oAC1!j-U5&H;)zXBy-JpuN8Rje8%qjEAG^> zmYa0YD7R}jFZYdp$~EJCHOh@-yCj^ocH0^K6Kwk#FLNKtBtB1JZ4Xlz&f52>hiv>4 z;j&NTYw-E6iaxac)b9E*`p{p!``$L{bs{a%W{0WXt_OL&Kj^1k-an(>Ga~kp_aW3} z=llYC%7=-3j_<@AUn=NpI_CJDuBw8(X;s54R2x#RbOANK#QCz_UVHpDGMoLsx*PFXiC4!UgFl$vSr^h;^4!Wv7RIgQpd zN-M#$rZr)`n*JM!q)(XFXf%z#V0g!lhod?`0Yv z&(S$Z7wOFriN{BOWxp*7{OC;djm?LhoGyFm%`)N(=%4dDcGo}gP4Euz5c<`=ZFhZy zFw$@6JF%VTdsn3Ex{u@J?zeI774vHZ)-CTKj%0h;x(rsXjinji@wUjb?-zDM&fZP= zVY@|~nJ39N*ht@rWFs~6+I>gjxZWeqSH6z>W@#SMyry^Ir+*T?o_v2M;qFuNFMwXa z^!b>D>elCR-f@!mKqK|7WgG9y3g%J z_n622=vv$hCTxC}BG1Rwk?%t0@4@l$>e|9_aW&GDEr03-^*2#HkKV!SroLQ`xarD> z_0gCINt>6yItl%rR5C6NZ<*kYH17Md2yHuxe~oftqIMMj3cr(aMluOyzX&*z z&6Mv|alGx`&hbY2VJ6|nLPOmjhWAy{kaju8E4>A1r6JFc?6*D2Xo&Ayt9pOPM+k3N z3mP)d1G426pf6TinVJ4Ouj6f{cj9P$NaL%qun8Tb5f&@LY$7ZQVcmbVJ%atj_mckc z*Z1fxPg`u+v@`JsU)}ZU>7HrM)4kKkzvTO%!Rzps1eokmqXeIidC<=EXUWgW9<>hb zG=CG1YDS;vm%sTF1>b>a_!x|Uufa&HVepRcWw>|dX|h)sy3>+Z#C#U(n8$aL4A`Xe z`4-^#*LZJU>N_o0Q~xJ)yoj)}b(@abpzoAn9ugk9uHtdeB2MzK)|bNh#ooKbJY8>h zksk0k(#rGLs)*~V=WCa@&f@2>A4VFfTQu+B`8xx4*Zq_i`)O2nt}OR5)BHy~@ePW1 zHuJW<1={{D!pR;_;hmf9uG{flCeE#qf1U^5)P8zjh|(Md4(PXs(HrfxO@Uo_a>rJr zqkXq6_?`7S-W1rBb(G!`dMax2!A|%r5PoPb(caLxz!B|%JvB;s_t9(ax}$kh@MszG z(calpQR5G`V9$*3`gMfKb(!~BgFlh)2 zS1j@{`~tq>=iIM|Z2r*`c-dvwiu!$a*Qa>%h;T(^%3pKY?bp-%!&+C^!Kr`m z;ti!J_giCyO zUc~YFGQwa#O{ctV9v=1r!sI-6R`Ias5Qetx+=2N-^6dG`m%o|;8Ampj*NexcWv<*1 zM`ipL>5pEiZ2vt7p?5m=q~B6@z8Qp{Ddsbm`;$AaL>|&{Myk!lBby%%;Jw>d9zyxK zSQ~du$KL8U;5Yt8^m?-ECS`4fzU?{sCg$5);P1+Bus4k}jAgL@;@k5%?CqyGrneF#1{g4~93k-xBjX?b7|@a~T{9xcj`k894tj!S+c^5!Ht?DHFQ! z25b!IG8TU`ZROMA@kepZ4ZJP$QEw~C_h1~Z0DfbTrxo|mwBz2i);RbUzz3}xJof!>te_h_zvKV z%!I_Fyp7VAWB;qYx58hycq06(DQ#yZ?0>8*XHV`aYmPa`uQ^8HPJ~ZHcHt1hh-G(VH%6Zwcmgd`O9sgu$ zDbML0e?>g?VZWE-{w;iWLDwa|wjXfwL$;pw9N1<9c#~ird@tZjCHv(BJ)wQYzoB%( zw%S(2!+wph+eBC{#)j&!U1E2=fNdRq1bySQj&l))^+U#Lj6=aGh{u~O=JQE_Eozp1Nf(LOh+0?JBbL#IKFZ|D`Qf}*%XKRTCmTS`C;#^R5$Ij6_4XO zd4KzCTM(a*zexLRvaU{ylXahMoT!iVosY3EOTMebONhp)y|iZPMY+_!W0x%he_skd zpAUY&1blxn{CgKcHY|iZSb%*vdJ{96`6QWbb;R@EMHwUm%H;e@<^85`!nw4yzCAA7 z&PRFQ6nTlZyVn~ywWaVe-H&*^Tn8`rkCt-bkI~2WM(ue4>8OtOEnFt?J+ui2oshe< zhbD2CID?n_EYj2XH0yh>6PRB|YmSr20qP6g<0A8Qox=0|gz|~`z3NmhyBX_;@k(pt3}wYv36HB z^87(jUe2xN_nusae00~Bv}=IIZ+aVjJO?xtLR>E70r60(i+GbCVN{09Uo$}dO2o0e zpQ4`(C*eMiv~I_I=`-HaAGiSdb^SSw_p56(@yziZD-cJxkz>#`m4`1zxD0C%Ve=8j z=Y^q9r=wodv--D38gI!ZBR}zq9fA+r;_R;32y2akjgkJ6%BID#y>u_LKW-bV+){+U zTzxN{`>oPm`VKL#5+{P*gg#AkeJaY6aV--d|5=;EZ5`4^I5Hy63||NZd1=n<742;q zVRzXOMt%>(`^oQte0xcD82$_fZVuz!fWv8fz$>_)L8LosNM9tsMXKj*^p%YTXuFJb zZszMS;h+tEp~N5m_P)U%HV>zz#-Ar%_xs>xR=^IT1jcfTDy0Yl9G#GCAxfC7urT` zPsCW>fV7kj>-ZE}lhb)tigQK-FQ6&%&%fG~MnRgpke1dcJ5WxGD9aXQcaeRR^0wig z-k7K(H!<4~))@_Y|BQVjvQ1T?Pm$Kj(uY+u^@G~CL)2Y{Uvu4UpC-62(d#g1uK?v! zd$eP?|7olb>l@#+;t1&$L}-WE_k14mnCF0YD8ZGBFgYg@@qGclhdL*cP~X8>kF&jt zNaw2Mn{sj;MYh*zq7BLqg}&I0^OFc8`&<%yH{_m%avzUx`5?hH8S%GbZk|22Y+5Vk zXE)|57U{dsg1=5hwg-PTIM39`{L5cQxPrYwB^&%Dd)>4cF}KM#p8Qeh+nC?s@8NP@ z@?7nErws1Le+YV-8JD&<6I_Q8Cih(=PFuPXTz^G4Y#YP&)qff7FzX^z-%BXV%;Q@Q zC%ArvFtYVDgZ^6;B{S(TKfe_t=(y|839jdmX1~y6}D^9+5;`Tr7J^RZsN&QkYgP--EbD5N^^_o%6eibm zZ5aPW2qS&mgLA!ldW6%ue4a>8IPH9cjYDw`g&{r3syw`TD0TU@V+NL0-$wju{0*eb zYrg>>v-r$S6ZMg-8V!6j0XH$=@!e4q<7425A8R{v+GdnH4}YrAAB~*&tNKsxd?~^82Bn{bGrnR@B%)5T!+6lvH=|qccpdRFt_61m zQy*SNm@c0YM)dOn!u0k3^E~W1gq>liBm>D;A2;yUg9l-*^4yX z>r3N&AL`KC{Ty%i-3a46XED}gi?A+Rh;`WltjoCFocl)ZtG#hdI1i+`e?9V=c}Ls6 z1lJ7+qd4mS6?)l*UyYfMQo3(g%5XlN;QDV<8S95oMvbKm+{5XrHI-2|gfcF+l+p5V zg6r$1G72M<5!vrX=~yiC(L7d&cJS-mo8Y2w=`%@TJ6`2+ZBKEYOLNL>8%RWW4#J+o z-5xgVb$$!<*evws&W;3E8sey&#GevvP3$-y-&WsC8uv*4mawfC{~7c~<45cC3HT;D zn20iH?9BUqsBk-oZ>(vg7lG>($4R_#n+HdP8QgAAl{=`0hqo7ShJ5Do)BtLtOb z&+9UDEUL#$BhbDo+8=Cz`X}pWvc!^Y~FYSEEf-7SUHEnrTKJ zDwE2fzt$hxncZO3|Hd-A!48}sXXW3O;JOTHcZ=`l$N5~bwI{e55KeXhN*D8XtbTWN z*S(y_LYKOUYy>q(NBmcosa=!cszTTc|0MYyZU?z20xqflxacRWy)hlUy|4X+x3^ie zujTdxS0U0;o76{nTf6S&d^aEA)Lxm^^Jk;Y&NeppS$pRp-Cpo@YO_q+*__~-i}3GR z+Dy2fi8fpPO>v$sti7$@=j|>1LwI{_xKH;iq^0)m$5_&D+jkROIAcxk60>z0*#O>z zE(c$LSJ*xiezC_?JQ}|fkk{N!+np#6abpl}#x41&;$2D5>A>f4KZc!@Xjba$omVEf z{)sSl-d4(C;P@kitpbi<=RHPt0$NMJPRo4Ttvu_^1X>qG_UWQ~L}+}ePU&<1dz4MI zNq(!V4|3gnUj!fY{h%|3uR9W4uOQD~gug27vycxu-5rwz+cBkS{sB)*^zn0~O#+Vg zA$=^;u=wxucp8tV5YNiJHNo`+V9D#X%Mx6V;hWAzNIu)SEy498geh2yUGWR&v0T`B zntx$Crp{f*&il5!ZvL$(S{VwrZ&@gBv8h=#&^=KF2b|(1E2Pkhh{vz3%DD6>8 zJG$side|MUe3Hg#J<6r=@#ELbE4!{raMdG>Y!xI2D*09%M|PpyT;w7Abe}qTYwWXE6z{`ZMiXqY zIIlOM<4oKu6s?N;Kah^m?^lQ>#-Jba9@{N@ zVs+mz8`ff64uspAW;pc{X*cJF=k$f0vpZ-I!_bbJc7o5%g`9Y0 zB>3Ak2s;mde?b{#qm(&R#> zQ2j6CTOFM{Tb{#9{JwfhBKCw?IU&3~f%qO#ZX&+v-Jz#N8D*efD&r}9?}eV)|=HgboDcT+t6F+?-?MY*wEx>8zTD+}33-_!pjg8-$Yn#r_ z*zEmpK0eO61lJanLG8~Sr94XhB+Y2&{*lU~&i6+%T9bRw^Ej;)Ckx)V1ALF+uO`9O zD9TVrD387e{y^b7Hu3njb(sG$K3aM79U0%mEC13Wod2_4HFKUA-|}st~}WP zSbjrCO^c5#Gl@T)FUt^eQ7cPuIS@uXm*m>F#2TS3pRWsMAkMljko}_hFJ*%DOx9`0 z2RWNg^6>RX`220Roa1N#9dv`&QhBOR;9E;@B_J>Pc+)wKB|)GD5czUJ1C-WzDeQkpOLCTcy(8uM6Y>n? zpWJ?v;QjEOcnxJqnNU_O=)slXqHyA|RAN=Ho(o)z1)4VG3|YznvMBXMD#Krt}(k9E}tC?fe+wL}&Now_Et>&bZ#-#hsAj zW<5yyE;k^(b&h`*VUnLVgNCU7mX!%E3YUHE#P{v^rnv6bSbg8liE#2Ix(RX3QA#Go zcNI}P>5i$19oLC6KtH=b6!*OEh+ETBBg%4S^0Zgr`|KC^9aB5vruNX?f-&I9Nsv9* z8%Wh&9nE}cvXO@5Ange_UmIOd^+~>wf;bxIDwGvz4tC$kX*afW)tSx0V6=nECO*{~;C0Vf&wpc8Whcoj6|}`<*VB>TNcaG9i_XE(y$7U| z&$)$SoEO9X@?-9E>_j|x<8G?giSTj}{#AriSYlR!E1UWTAA>U34M{$A2lDn{ zUZw9BJivB-g6n*dzH1KQc1p)Ad>?%TJ~EOYETA#N{eM)J2(zVgJCKU7_L~KsfTLL4 z^Az*O(w?rDIIiiuGJ9JD?Ias_?`GT1*o-TSpAX&_I51w>a{%=~Kbg?-3(nICe_bzx z^Y@?sqW9L<;Kxm4^-uU;@Ue=lGf>@~zvN>f>)wldZ}hqgzg4_N){VaOo)@*V3;GnL z@Du?fJT9ln*({C?Laf^)W;LS{~#0x%_#4E?>T@mtSDW zcH5~W+gE@#mcvfJY-jJ4+)ru?d=__N{$VY-y#qL%N&TX`qA)(x78UE#86%X8rHDI( zchF|z_aN>mI2U8E1%8-%dof;wn?r~X!wv51p)|?|y`|TJCe{Mp@qTaV)-yTYa{*rm zU(dS;fA96`V=xnU$k3ftFQEU|gJ0sV;F(KtCr>>3OEj~8Y|JGz#?9WDpInPN6Y$k98C7`^3DX@WfLg-;l13 z=HZ1lHV@k;VeJO}bE4CUyQ|@ki}Kk#K^qbu;`8Jlv@36Hsk9cG)Nu*QD8Eqb zr=F7FqH_@!QMvPN?5(fX3qkw9c`xo)q`EuDpziU?_AAf8-Jg^WGRXrOP5VY~Vm(G_ zy6o8hV`Vsd%7y%GL6{R^?a}<*2hX{-9@rShQuy7>M8simlLyiPb$JQ zk$#-`&Vm0j`X_0}dx1eaxqs^|{a`d?%zd1LFcx9)1<2 z;(KjT6}Ssto7Vl7@<`Hqe|?Yco3+8#g!iVIy)0(i%dhM^r~5tj9(WASGArw+C7}

rY5addjcyYd+IOwlI0_&1!q4`{MQ?J@sKpemFj;ZQw1t<=sKfWAS=_Iz?SS zW_5Av$_2zYA@1^GIUIeYyKVi2afN!8R3Lm>I?4C7dn?A zo2_dCYWuBd2jwXf^#9>YG|wio@XeT4W$Zogi5)j1-Da8((5>WN0gImgcJS=e=pPQ#sSb&Fh z-N@5Trmzh>tn)^m?o>3`{BJzF;`Y5}W zhiNzRe)Le-4Lr;#!aks|n|N5FXv^Oz?Aud&JQuH_JJff3E`_c{zvXLsJoxoit?9wx zh~3_GYkCe~4dL~z>DfuYfi*o|`rUwd`rU+htZ}@TBc6V*Ks^0kgLwMA7V+@o^WK1X z`n?J9_|3&1=E^IyMk%{+YR`Urdr((w(KW?dRP5F*_$|jT#Ve;M=^o@OyKqfUTii9^ zSMaq(dYV@|uH*fF={dGmNGF;AgP72{nUBL>e3Slm%g?!-mUJ)Y>psLw`OUv!iLYp8*AW~e1VqS%kkEY^E}4jR$;j={cj!HLCW~TvV%UC3JKmkUOy~|}^Van2 zMZ4*K*Q5#bHi5ExQoa*n9&d|o;PfML{tJZRO#CaaqfHr@U!JvVdftk*vGpqK^lXm) zBd>YSt*7Gt=|bC1@{zScuY7hSKTA&OsXodh&!5t`Q682y7H4UZwg>ISUd{UWaSE<) zZ+-=5Sc`u%?waB>{033)LbT(<%X4q`d|i~6N9BE`J8*FLIm{CsnKT;KRJ&GBRU#@}s@$6LkL`hRAQpVT-0%uz-^+EbN1boMW1F3y|b z4dlnMKF0anarEv(H}G~>bj-nb;3xf+%f4~cTF;xKjdG^jCe>(I!&%dmo9gl$o1EE- z^&9S)t5Kn|bo{=Dq1zbjE(5 zG~GugXFZ2B;L%L3OFEo}xk3I}L>D;Q2cMOxuqRFK*!{EKTj}m#nuiyim5aCXlnIhn z$<~$u+M<1#H2mHHo2Lu;8__>nvwDJbr`nX7M4UxZCh)%W;BF}uYcfxDxGcb0w{+)Q zd|!oR0j-mFj8Vq5{TFZNN6(7&Vf4jENgv67BSznc>q?^a;VR70Qq0v7%vn5}T)h(K zYl^T|9Le;_TvPqIu(bh#8*7&s+|RqkWD6j@ zEf;UFEmgQ}fW8y)jk{NiX&|eYtW)aV?|Cgc|V6(qz-OhVS+)-j{v&r87?QyPLu|JV`mU@R zT=VJZtazk9g!B(0?s`$r7ObV?NcTqELBw5$IJw5B?{!Tx9`oK6V0MWuSj#U}uGR1A z+K)EPG<}oK9y|UjcD{>n%Iqa51C2lh3a|-;ql-V%g~Nxu;F+%n)xd3g~q-% z@o9A#&Me`5Z5rnTn0GTW@HQr7l=s#EvuzNq@3?XFCHrS7$Ck)6;9FQvWE_B9VFv69 zWG7`@rm~y^hxxa!VZ3wx8)$}sQDV~7odZKlR@LiOYQ4MS@f<|EZVHv zM~pduJgPV86VH{r-Dm#D+4B~{Xup=VyO7pD%I>ViwjR%S0(;tHx!(u1LwhFXPWq+4 zX2AQuX?w62{sf#4;yn_~8Ois$vEDlnZQD+3?Fm>@pM`O^&WnA(`}03D=0!q;G_6Rp zv(rcuhcw-n^7*g>yq)x&whOslav1X?M#jMpgyK3ca+5G~4H?$RNjb`X!??YeF3Y$f-7~$nn(MOK5~$xFJk9$p+c)83 z+*hKEKZrJ$Ca&aTF3XepVd*oyldr&9jPOJ_lHs|^^xx1rYDp(AGwOP-hf=szS54m= zUhhXekZncSuY!yv99@htYX&UKrna|ydrc47GGv$vGCf@Raj%~GF*nx8e<1WKxUdanQ zkHt?(B6@%w#C&#{*+Q1|wT0kf|FH7NrXuYj?^)ukb;Ue7KwZoG*7avgdFHx)Yl$zwf*u8ofOZPH`b?9LZ%KHL^J*0;fQP^`7_J|(F@^(_#k72j?9_$w2XP$e}e){cz z-GY7}TGNw6zmFgu{9rHsRP;g8AJK;GtGsP{5l*&&>p7mn((FT=v8QVZ zKkD6mJK6!7Dc%gcn9DZf_(%7098(^m@9U7qn!YzL`qv*6;_i&guz!Se)YR{!>%`jZ z%4vh{9kqRx^O7L=3h{>gq`Vo+)CDu-zRFCjW{$-^7xcfx3_gyjNSBPgki*zZcnM`a1ifGm^n?4Mr_F-SMSCa@K>wNn z9j^m=70dr=Z^k=FpPxTt>pUCoXaU@Xb43fy=`a#=+-eoq32;Tt%XaOM#< z{%$G%#IJ7Sm*2w1Pk2fC?wTHF%={Tj;+1TDO!eR$%}3i(I6n3ZdOU!&3(=N*w{quY zhHNB#^Vns}v1Y)2680L2p5L+29W`{fuMOc*pe5Yz`?Rv1=;7Np@i9=yUIu$S?Y9s= ze&*fYTeVA_J&Bkf^yZJ}k@g8ZlQKa9uskLT}s3g2arDf=Ny51@_@OPt=e%O`xN z8EtzN-y}E6F17W99*Nr1@Z+dG`!EN~Ry%vnY`y}z!-Nhx`%U=F6|!`%kfrO6l8iI_ ziKNd?usa1N}bHEA=4$DjkB#JOK6;d1Dcx; zqd%K34woTxf1q{WBc_$(lgSgT$H+Fm4)UHg#QHi?pFPc=q0bNh`}#a}2z_==G5Tzk!QG994ECDt zDq->-<0$n6x%Y9bvwwO2ktL33u>R{fe;LL7E{jaC!vv2t!ACAO(!XqicbVXACb-@N z7nxv(2_9>LkE}73Z-RH3;B6+j-UJt!V224FYl4rsOy!&4T_$*&39dK6MJCu`g2$TR zBNv&52H^IA1 z@HP`%Z-R?Vu)_q8HNi(pP34>5T_$*&39dK6MJCu`g2$TRBPFKtP4F%gyv+pHo8Tf7 z>@dM&P4JOoQ~4%%mkHixg6mCikqLH~;ISt7$VyZBCU}<#-e!X9O>mJ3c9`I?CiqB^ zseBW>%LH#T!SyD%$OJn~@K_Ukq|j8p3EpLbx0&F26I^719VU3J2|iL_D&GX}GQrzS zaJ>mGGQkcLJk|ssSz#*Q1n)Az+e~o12`)0h4ih}q1Rq&$D&GX}GQrzSaJ>mGGQkcL zJk|ssS!OEV1n)Az+XOuBR4z-8EPiSy$%1WjVwfx_Qj{q@kiRN6TL}IL@FD@f57;N* zBY>|LFzre06fpVFzaU_`8}P7z-v%5T!^`Xfe4c>+40wTn$(PbCV6xT2uE*N&I^f#{ z{0iU~1x&X3PX$c(;o@#Cmi~Fbr2-~f{RRQkJ9pO$crV}|2>40BzZCG}fZr4FPXH_U zM|ju`_$vZ_7;vV59|F8W!1n{L5pX-;s|5T5zz+%dZot14@LhmE74V&a&$98lZU+qe zFRS<4fMJJZFugr}t$?=yzE{BHBlUuSuLb;90e=(l#{#|zFdo)r<={@#$ym2A_;SD( z2>3Fs#<#hnY*%AiN20Rr;RVs5P;By6h z9^m-`J{$0V2^epcPi_=2`BQycz^4IzSin;NzbN2|fDZ`xRKO##h@-kr0X$y734n1f zlZD3uP80Ab!1Dzh4S1!16~JWzJ_eg@y?{Rkyjj5i0DPl>KLC7}fZqlDkbwUN_(=gD z1pG?@cLTm+%Dip z0Php<4*?$%@B@IcpTWxC3HUq#-wSwwfbRiZE#Mu1uN3fi0pBCwI{;%phm~_H;5P(( z3*a6BZwKsv0hegt2Ee5P-U=A^9<%UPz*_};HQ+}CdY0Ifm&>MX`AAU?-P+rG`YM}})T1aL+}7JW1u&K80yC_| z3*}n@djPutQ+btu7XYU6$^bh6A3CZimjFH;a6H0y0FD71M|tn)?fqb-O?m%a+QW0@M(Z|K)AF5{yE^~a83;Y9s_s@;4;9A0bc-N zv>fohe*kZQvjIN=cpu=i0dE%YSipBuntRcnLYwmYBj^|4ZooSbehc8&0Nc>cZGiUz z{vpCC%>#f>1-u*ZZGct4NAC-#i$;Y11ZBRB@Jhh<1Ktmq=mO$b$z6>Nwhz$W8&DqF zvIy~wfNukQKHyfsR{=f?@XI&$_SOTQ47g}J#sP3V;C8@E0QdeK{kRDi;{tvk@SV3{ z9030naQe4;d-Hu8v|4YaTH*JF+>If%BH-3SUcXOu7Uips#&aCmE=5mM>vj7=kzxw# z-M+=k)eY`Ikn*16opX*?&n6=41O6&+ty>nFCn^-dK~)R5Rll#cNv(8Od429mwaOn* zEd{5|NmVsprK*+tH@KBW{=h~pP&vE8UssPtmwRiyp(fQE96~V;Q!%o8qOOG{{q#ek zDBv$|2!?#_U{I~|SLz*-#puc0wQeowR?FQUZG#tm5!lqLnVq*nRZL&YO<$@~UAx#F z%EzEoy8|2eS4k+~^;IhieW5^;TJQH_ECOzXxiN&)?ec)X#_f|u%E1V-Tmz*v^>~29 z=Ty#N|0xB29YIz828@Q+XB{VA%s?so)TtD^*EhI>Aq>1_&>1x)tJVIHUN0xGJ~>Q< z_M3^}m1EMYEMC6WSv-GjrMtYLdab`9RNoMiVPQli1C61lkcF<&wc#PlmK3iQP0)y6fsOy9H$#2?wB9fostu)2ta@qe?w)b@B2=7pb-0 z@_>fkVXDy>dVSuI7tC{$yAu9aZm=&Fp<_!9lw=HApt_-s#+w+M+t*O1)@uQD6X@1? zCGg(hW(kbALW}_hw$5GW4>VCtn9Fji5SK=+4L)sy=B?GrYuynNNKRl$=v3=#wGbLp zH#=DGuJBfQD^zzNfX4YN*od=knJS~CpiN6L)F6=`b5{_tpkIlCg~i2%#X0J#f@KAT z7Zq@9vM{~lgiO^#EKRUUN1B8E%dT>`86dtLa~&!4ot&EQOHo&q%vYECn}Q*4MNP7n zQ=Xb!o>QKZT&`8rI2!iHo zE8T&d@^t=9MWajy`vNbm21Cv%&t#vCIT{ad%F$AiS(!%u3=tp-5b=5mSv(p=q;d#l zq$LMKS|F5DJ~z2Br#vmWDW}|#EJrD4g;t>!mZ+{|MIxsnJGGJ_2UubM7;1o=?D^i% z0>8Q_FU_GA1^k2>V1-KKs6lr=7jAg>8QM{DS{QCLj<^AFBRVe&Naq-kxX}daBI70{ zg^-g@$VnRta#9>=*&I89a{jHz$rzk)20@NM*6~4(BQql+<*Z0^5Yd2Mh;lv`tz?H$ z2S}Ty*?`oM8BPx6S#nzNp=a|VeJDepk>bdVH1z15x)`jtUaO2WHL4rDm2P4t;S7Nj zy@^N%?T8pjJxh9cM}|Se8IdP=0iWM$YNT!qbJPaxwVbJ6?x~)cks5KT|GUxjC7JYT z87ZkW9a2)YayBh2;-X+-$-omsP}D34J0VVILjX!?I*Y*&rn3=mojMlbXqnQ3&jd#* zYdV@8VJ1}iRo?b5TGm0w7%CHxGsq(Azz-Q}J|88shX$EOm?;7$Ja%+7Ddi;X z^i?8L9D->Yv_qt$(@qJ~B1v2pX_aBxwpFFH&iNrs4WaK2Tc=BnFp^MsONi4CAlbjT z7N$$AT4ti&LLk#5v<;0`SI`aASXl@;Q#gd>c0q0`m%kvYz$}`keE)Q zNtid%IGQXB=^}3cMt0cYot0WW%xFYZV#WGdiOR}q*|)IZgs*+R7+Mn9A4clyfa~&MEA3M8N^_)1?VSywv`_Wlwqf#u&(M_!T6fBD*`4NaF!M=Ts_0*~ zNhm<=X8jYI(*VSI{MS>1lTDwdOS>vr$G+rEPjgg|f|{B||HnU&7yTqVu^Hf3^Jo`v z1q{lBaf?Bi=maFrXEF?~VlieIZLO;_T>=i#BCE-^Njt=ssBNHCzU+6TbE=i>)LF8s z#H=+RR~{N>M$cl)W!)Y&1iMJM6+JuLGAPF{^5U5dS?Zv6Vs{mv#L}n^N@C0^+1cm~ z3N4+HQQC1bo`+$?VjUg!zJf3xNR+}>Dj8cEhd|}2;e5?OC`lQ}ewsd)uaxosbedV< zYMgI7>$FYS;9rgnCp8Z{_}JR4jUab3uzWGDlZ}6b68xX5 zlX*?$?f}&6%4)b;(V{S~0iI0}wWwjrHNufDuQ-ar)glXbu?nb&?iQCzrE!S*4^}?R z=9ijU#chA^yP*Cjqu>5OwYqvvnp)gY2NxyPZ2JB}S<9@?&wFR12!_tEHHW^jKSbs$ z1yg!vid-mi5fy#BvYS*A%DF(fBtMomP2)= zWjZ3y3ofPSO1-Zf{gM5L6O!DAE>9tEFYH5?L#LJ!nyV!JD9IMjj=oqL#JS7r=V44J zV-cp4NcJGQv2e&q&&q&NDLpGQC9^U!Bg=tbrYkdal@PiJZ$gU+Te1JA`@Idm)~ax% zaN9)+U!H=BjOoL0!_>S6pM{J*pD2=~WYviW5=x(+;Y8Gl^~LEB`Aw$uqQqt|n{?uT z1~IwH{f#P3JTj_RTJRP|UjN6ZpYSuysuZS=3_)Vj1hO~)cmANdu-04W^=Tov%hEiA zNDFhcG{jmY1x5<%VW=#67TpLx#G%LLKJS4sSGYbRIwH78Kdl#%X80CrJ}LB83mIbY zU%?{v1jFrCAHU!%$pQjyx6c{$YHGf(VoqcaVQI-bE}T;D!Vs3f7R_s5Yn?H`0~+fb zm5wT!&e9cF@IpuO5`VzU*Sa`1f*D;`hqE(KLDL2v>tWjLgic|MY@d;*8DLg9`V*!) zA`Kg6f3DGkFHa;^G8k?cyW=9FKdhvyNkt`TGd}-{WneL(cXH^KUOmfk#S8bUC&UZ^ zFX>+xUWOqsC0>q;gp5EhjA)XV6c*=4nnmd_C8hnVsYM#v!cE`95F4@pi^bc-c#gvq zGe~K0l%=0U8IY7N{2Ca(@IOvvi`{j!HdarP72##Bp{bfURFS_1haA;Xe?x%Ih~dD{ z0_+c9+xNH_6$Vk(EOwO7tHVg1lW9yK(Npl8BIg423zA8aNqnifOs4QC17$=&(!X9n zMMlyv#FZ?-!itZp^fDodrV!^VL`jQ^O4Op|*yjI_<-f-jPwxI^9M*K+|5y}7l@kOr+eG$TTb+y#3z!I3=ysl9PC!9)x+an>sMub1r?XsXF$V6`k!}4rnBe z(0V?R_14po*VNe>d{fly+2swP5d1MrmOx8SA_=rX)?xzRLu_!CTX@fiO;=7V;5;~% zI~$uDYSQu<$+P$p5G%`64ma^LxwG}~>|_SB@Dx%J!%R-%gsF9nDLJaJN*mES)(5k- zN@I`gM3&Gw7{!=7QG6UaVWswPyJ3V?u^0(v2N>Kanwp*(ZU>MSk)>-&m{rf}DvKG* zcIUpht?w^n;ld`_fGi?Y*!EH~E2NIeO=8Ra)!vZ1!lN!|zy%1-3c3m9^RHJT%|U~7 zDORpzzYzQkZaay9AHJ1$5PWx^EBu%E<~!=cZ)xdKxe!o9$tDR^L>$jrK`UlIah3=(n~lPyfg zIx)8ObTTp~9A6#iWW;?`$PAZCn@9g{in`poA`(U?sX$n)2@#ioX}T|uv86PiZ=jAP zIoSjUH`tPX^h$0Yp1@2T*UPYzDBws^o#ZsN0z{Ojg*Nf+1GYt6T=>OoGfDFJQkwJ( zgk=vAVJEnGPa-TkT^MDg4K$p>O6Z`XTDnjznRSxN=tc}SEwj^MRN?<3O-l>XPH;+- zj*0rcNz#Y_fA9@{b#duzY+jK}6vhMNnlkA@uNy6xGOpjA#_f9gU1(NK-D393;0}E3 zR<>{{frsJkqNpH{$Hn^mf>y|V^#UX*xAPq#R1uF#sQYPFz;DOeFadWFlG z^w?ysiPTtFUZ|6-34qCak$Eg~Bav*>P_`GW&WFMViLMJ(!F&R2hzfwTC1uH(BhSR? z-Cdzod z+%O`kpfZ(H0ernBOA%CHM#~9nUy}!LQXCpTqMJ4ln!b3a%rA+VEj5Fm6mVyoj-EnL zCF_&yBCl7?19u5xs)ZjE2s88RZfu;72=+poFOt0s*&$joo?)O!&oa1s(%}0aa)v>{ z33rSS!^tYP<8NT3kBfY^U`A<#gkhZ#3>Qq;%whptZlzna=~QmPH_M$q8$i-!Uht)K zD_@fC_9#TKG_Y&zg9*;Wz3UlgLf`u`Rt54~v!A z+)K8?U19hQvMHIT=7l^BwVnoD;K>b7efAC2aWb65`nXH>QH88(imqWn`5IhiWw^O< zTE8tp?&q%XRs{S(e^p2=!HKPlaQifTLvhl!T0Iv(S`*V~q+5+J73n*>L)#eDSwr86 zVzpaj400+$C?Zm{WTlAxMkarWXZou(#uZZ4CHV_zkRn^RKF=VDIXr|T)se?g~|{|7B?E;~pI*QNE;T%1nc9f`3^VVsl@r{{;&R7cVSS$xB8hw<1FX>ueFz z?z3$uo7vjnyWMkB`CX>z<>}lpEKMAM2uxd#Dha8tZr|`AZUZpL~?-zgGK(0?tt?o^32!T>pn#$7hqyX zeUV4kJ@tPL&9F7I{wxvUo;sFQz|aqJNZW)}_ZfWQG&n!(gEOfZ^&z;EIFrlm46y)D zN#}lF5PD*guFhYO?=+E?y1|h$hZ{b_)XJ2s;jS+wiE)L|iyqp6LdfV<938c$>i{@K0If);l z6^S445qZm##80}Fe#o1=50m^?@{*x!jt=ZC715{^E=mUHB@#y!>AJCmO*71~lQnZeY(%>yd!ar|B*YDI;P}RcNS98=%ICakuiHQ80zLp+TxsC-Y>~db#jJY!d|uR zBkc6U-osp}40u&Nf8$iHRflpfwq>0nPBiN!4Co>-#0y#tisJfZN@f}}4+(AHBz~lr zOLIuG8KWkFpNqV;u&0Ik5YUOCElKw>aqlLdX83<{v6~)&@cL@{aa0`8f~FhE_znjQ zT!PRIt9JNmeDJO7J`I(YJ)@x!?tiefQg?IGpADyLf2Hw@ZV?tQ?(q&7t^M51&&1`m z47n+|9EkrD0@A&~U)#V8>}0tuG0}qytoyyUN1k z{5U$4hYKRP-(NkBu2tb~Q*p;#m?Cz9aJ)33qNeN0JfDdrnr|aq4YA`@=mX<(_ouJ& z-JkH?9^pqc;Ni>cloaMQukYv!K9F4EhKpl~7Q)SEB!w8yA_m3$o6Fg~JG8B4Ia#9b zM)6H$gI8JZ2Jh>k*XJ@G%UeTW*3vJE8QhtvG;<&isd8~q0t76>rRbT%+%D$2UQWz#l27GZvzLuSuB3{eZ$ALYFi~DVu?MBzR(NK8+CerR^ zI3ZY!L3-P;>tQ?vJAkj5gWvh;s0za)8HV@aTXd z%+n5X^UgY*gkGbc-Xnt67bC-VN0D23dI^!1GvSKf(6?PoTT}gDNqpXy6KtUpzG@rS`B}kulA~v#j$q?^KllK9_{Y%o1n0Fz!yB1aiuREv~ zcDY}6<)NkSS`vkOU5r=mya|-hPVA5d4kC0?hySj)2sM?X-Y=9bTj0V zveXl32!`skAl|GOS55kGF>SS)His$oY?4T&e%OZ?2GA#jPo6#*`mXu=IF`$YTf|bp z;-)YJJf)Bq-hc)Vi6@6!+VI3tE{>-a98d0&jat20gbS3yHPkPjyuKu+JRNP0fyiqS zo^!(EYJoaVXzaL=&WI(A>H6yN+ysMyN2K@}h)vHDQG1cI5X1~AH8^>dSMrmBDH%F> ziD~GsbG!MuJi1gloUE924YVcBGDezIgDB!cqzuthH$p2pUo9O*D}`7czHR4o?^~d) z;_M^Ct^U8{(v4nUpGTPbxiEaF)E7P&@`NqtP`Ga)jOw`RWFD$MW=*I&+#DxLu(}$9 z&{#YQ5!{e%R7sU#i1(7~=V!!0RU9^A>!;+^-U<+WBRjm#HuTjJYRDJ0r>{>o$?0_! zRIC>D1iHO%i1JVGah6y_ix)!w-945rcNR)H?)}f2o5DGccnKBT?tDu3f31s!>qs?u z2)9PbH6-tpVdW1uA;4dsW`}t?^pW@$qXAs~CH0GmF`RP>wWu#*2qpVv#nej>f1H?)1`g-#l1@e z6{lM6#S+d=;l1SSO`q77J85u@4NIU;^i(m>C?vv=b6oKUUkAK1t&b5+NQLAR{DD`o z7|_|H;a15Ilzj3j&cYJ&ad5e3?MM&T;7%gSa)LlkIBc()3|HVzjH1}?hj)Yd>L-06 zAQfGGA@G0mX=O3>6?#4`vw40#z8djWxPNTN(+YVO=vO#B~Pe z-d1rfHI#7P4()TO!dn(#;q3B8$jFQx0uu|W!8gX`d`#2i+qj&ubMzI$BVvF{Ey6aG zPAqb(v_8JfrX}I)Vqiw z2Xcv+B5wF#Q%~@7gTwcchgcGPHpVWE61vXKdAH=t7REjVJFHlh$pWm5U3`B್ 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�kR8JMANBP>I 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 z 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 g-2Hv)DBImdo0k1hKXk3jn#>4T#gIu1 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⋙|fZw>+QP7uAfhTlcKS1G+Rz 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 z z5Ji5msqu6mNC3XDf)yPl_TkAXB zX5cYaR*&YiD(=2#W2vZH-?Y|DB>H2B))BwJckh<>jSYX9ST|~fVHG)~k?(Y`w0x^- zlJ8}putxY%1#H#q@G@FnmYJ{#T9L69*VHUOn%y|-OYo9!Qy6}W<9b`A4V}Q?h;|x8 z?`5^EIhBc*<(7t*SBY^*>2;_H@){x!m#%FKZ!k=2N|ne4gGZpvwI@r z>T~LdMk`bNc42raTZT1;kkLrm@!GnHA<8rrI*BQ7kt9(ug`Y%5XZd22|B?i7U71Ym z-d@tthQ7GN%$B)`VZvHB)0EC@)%2T3%DRbV>D+ z;w9xUwzw)`YLaR%S$Peb1Ug=0?$a8I(~Fv@S&7HBHQY;H-`>=QR$7yvR};#cQS5Xn&c;wni!3r4k6_9??e6pg?QH`40Jfts7(_+eLE!egT#Z%^Y(R7@#$ z6Z6>CJs>uW^2gvACnXi3n~syA9eRd}Rs^>xu2}BXPe-GOIvWP!n7S1ex7N4TMekZ8 zT93SN89ue+J=9yx$8G*2f?0vDRhC^=v{V5-X7M6H`jfF1DR? zFi|GaT!*77THW+|bmCJrGfvIl-aRQy(sx*EDTOfyB<%>8Z!^Ne!#(b|DD{y&!blw! zo?GMM=M~~AjTQMP7!s(sc}XgY4vXV7eA5((WC<+uF2|n2B?XR zLhYdV3`yo1s>d%n?WoXj0tC9ERe@oHQs|G+g)m~=uWUdui#*?2wUT%S_h zfHzKtzBF|Oq=IH`Mg%l`&-8=aj-z2lx*nqVocS6AW6WhmGDy5{nbpxoGMWX56M4x3#A0`RTu8sY*~Oqjo8C$GM3UrU7rf z&@;Y`6T}9#zQn`?(GhQaKyzO*o!nM+e@4fYp{7$8~1XG&+WGt19|eBft;~(+(~5?u>w6u4&acC)j&ZMJLXT z@wv}Rs+;{oAKhgV1(9fE7^ZN9A&hj_B(j2vGE#Y23rE#nb=_#~vXJNVA@q}nj1EmL zPwpjQ`fUz#j6>qg#a_jvwHOsOW>~U?-%(RBR`ku>7%Tp+9T%ayLCPeBamF#C(YjpO zw4`@GzFU1|I{@44VxdpS6?6=A(i0-$lx9*R7F_iCx zVi^=GmG(a@y*fN|zzN_F7q$vG1x9Mf5&yMePYJJm(s5}lft#(_SmHO=r+qJ!=Uwok zJ!}_pmSYdd7Bc?D_d;Der!zyi4q`8c_o8j@b-4FJbFf#3SE2Gb1$$kXw?XV7z%o9s zIFor`V0(#mEZ@k)yPUjc;*Uw;+U^wPk$Ty9(Qh7bAb`Eku!IoN_dCzRZ~if!T>N2~ z{9nF?yPcQ*c|+Pm|15UyPCu^jqwUzHG0rL6iE}e%rKc^}nNA#2xO4yTJxe3QPqvRs z8+X)2M?2SI&A&PwdW{**th1f2rAYcEN2s?Sb4 zG?%~Y9H$y*^<4Wc;3?yrYfnAe(g!9u)z={WIj~oFgfnU1Sq_i1=sVSM?ndVB#SWyU z|Gc5-A*B26!O=CZHiFKEE`R=^@9avuG63 zHQBlLXyCt+E^nMTn&>m9z-L3stDE;b|2bSe9_hh0i{mbuS_7ZWL|B}rK5Np1!ksu7_uAfx;Nkc0(wuAG{mg?Wj)G5W#()pq z^YD8Z{GU(0>%`H=74E#n<>R-@&&bAa_@3pF!{2F{Yu4iTyJ-`T`gvOBC7C&eJ5OOe zw1?l(a2FQtoQA*Qv@vUbe#BkaN^0!n_K$a}r{VW|X=5&V@1~5C#-PnTIL68CLijWA zdy{j-C7-)+%t>R?J$(Fj`N&glU>s~9Pr0MUT#|e9q?3>z6Mh=i!Ul#hB4<>Pmhk2(P_g z(ie>yzoz@(Fn*^$e36rW;4Apug*q}3b<8cp@$U~;e;&LH@LWIs{;$K;-ve(FcuZs6 zhr`v6gC2q%_9MfiN4@m%>d_A!su}ap-!4Dmp%0dfedwPz9Qn{cpGmv>uZZ`9(aysk zb~}05D2wzlqjv5am64b3jM|Alx^K$p!jICwe(TcXN2m7=jXwTiwmY2r^RW9neRSbY z+_z?J9)o{lw6j@$Gx_9ENk?O7xG=?>af{qY=E*(msluT=daH$2dPgTYI<`SUA+Zl=_9~ z&JX?up4(^aOzT?uRs1mzLrAli&-}PDah6MD0qTbDMXuTB5w zMVD?KHEQRVPD{W0{=D?NH!n!P`=hTp4}I)u=dQ2%$2haf;IHlD(N5FS*RXvRjdiNe z0q;L1qJE_vwdMufWA1=2k3zoJq&auv4%&Ao@Ew#%4eHCrwB9cbo#;$@82Y^v7wp`B zUfM(Z&UGGQo>)!}%E|nO&FrI`*%(S3w|!LMPHe;KI~#WX1Us&6j8~3hxh0aFk~V%# zU0UQzLnoV4aYon$yw%5C3yxc55F9Nv2)$D=+v>2r}MTMv-WezdLa#~Ynl+0fa1B6J=p zoY&|~nPcdaN9}a&Z^1qK|6%WI;G?R}yzk5;WD+0-2pSM!P_zLP%uFU<$Y=rq0}g^D zB3jdA@&OqLNl1c%QjLmj6l|fUEnC?(R=N!qc4-&8(9$+4?HgNm7cK45t^00h*?pt6 zUE0z%u~^>!bIxn;srPSJ$`%C73o^#GU_c_mf&ewB3?j6Rqm7DOc^mAr?N-G9OVL+E=Q@|MoJUpIvA*9qO9^c?AWA?~x53v}6{ z^dDsgZObhvuZvM`7oq$X?|W#`l2V9O!U_T$Y;hUjjG1E6BQRc2K%2;pX%veEPb`^a_!ll5b^&nNB*& z(&L~hEov|OEe^)eRy8BQ@)e-QM?l>3re3+9#TCcct6zkQ=5Kn`L|J@%6=x*jp#P->~P9@v&|{X zV>`+q=?L}>NPTh1x^gIe39-IV|Ki;87rQ*hj2|l=l^@<|YA1Mga2>1sDQ+L?R(vp8 z>#z!=dz4$W4+|Zy^jA2bV@^EBg}lV=d)wvM3UNN8yp#sJkd{i6n1FqNrm$ zag>+Bf-Zzn8{&4O&Zl}Hj>~kmv8z*+>C2##)TcmsoGi+0(Lg2Y>C>*sqAl>Cu0HLW zB-)q4M8glAozRDSzTf&wzjX z)$+NbJpuo}1K;Yoy?8g+n~1he@cJ#gdr$1B=>q_apD?b6#16hqDnA@2n!)9CLnbcph-_&1Qx z$bNk6_@sY>rSJP~LNXR`+@I3uP_#~=+!=yXQfkbKkz~q z>88?gxM@uED*4m>>OSb?5k&O6jRi+~fR6N{0c+WyH0-)he%5lT(jwgsqt3AFb|sam z4EfGfS+PDt`RQj_#wOOV(LA%0p6MU_nWj8seLyl1`LsMcR-7db;)rN<%CV;!i?zhr z%yim&*5+%o@>d%=jywWAMhKtf4ud0o>(+B^c2AXr>eFtbZ@w^S;f5Y3Vp?)M zMC5C0=88W9hixkXiLXV_0rD}QIaJK3XBxa>2ru52n9!yS&sv;OkqLmA<+@WgI+6pWy!5TT`7ok+j(|q;hLmx_r^bsF%kXA+8hZXRx3p!QY zSSUXNAWz5O1MP7XUY$oBS}zZ$uM4%{GXhEyP#&!(s-&uvXiKX)Qk;&&f5++I%M~Im ztcDXGwZZqly6{pH~Sv~QhIwhd^d-$0HRNs znE}iNPGhTZ3WVOn0dm-t$v{##+8{}}<4Cn~oV6nsiI&dPrEZ`LQXGjd3g!0pKP5{&3e_UcGc37BAm|Y1_ru;6F?u87}x>yBi=S(3jEt72I#;+7!)!Kfv5+9^CbrS>^}<#RSkM1 z2GZDn91toObW04phI#}l6g(v{&;x`j1dmA!90d}ehk-)w5(8BKP(I2Si-AJFB$n?% z$;T@V(&warfgM1IK8W!$VPGo|A_;Dm7)S%sb5em|El7um0RwanGWnkZBIzL2TR?G6 z2`Z-`os|O&6mz^nAnL8)e2Ia6)FX8Nn?NKP?2{Nc!T$RhyMVYQxL0Dp0|I~aL0o~o z(tk`hV<%%9VLiyOWM3B{&a_Jv=tSd1~xy_&qkZ2fyv(c8p7Kc8=SN-=pKY z@!K|j+jxYI@4@fU@!j~{oxCSG!MP)OCw>nlAI5K2@_zjGC!fdf@ykwJmf+lwd(p1Lx@xoukeG`Dllw9aXWJMA3YJFn`v zD#7{YRcBEVJdT$l&VwOA;rc4%jiVuqu~2rpK8=5()x2XV#mE0*p3iJ!I)iD<6NvDK znI^rV_{W&;VETGapZ5GxIDMahuIo6xYK~9m^icTbi86c%hd;ydr!oBxOyAA%?`Qg1 z4sU1rUZy|8^b;K4heA&meaKz#%QT$@LG-7X{xZjpXZl~5{wb%og6Ucg-@x>ZOyA7u z4KO{C=`ozYUvT`XO#hDQZ*uzdWp?7Hh{KODeFM{NOw*YmbX7Cm#B>fn?{-v%bh(-S z4AZAM{&$%EB!|Dq_g68U#NprI_-B~Dhr^Y=+(0xc1U1i0hAKMGNArRGOhYa-(xxjl{H<&iiI6xkBz>^ zMqh`>*7yf(;or8=&)euHZ8R?g(W@n}WQ0sHfA^X>CZ1ebA+13_I8{>PrUS z_-)@P-qM0G1-3@hbMzR(+$$v+p6e@M*5`V)OFMk|I#9c`!J_ z=-Y8R%#Pt#4l_!GORU$~fE`JERa6ugm2uw4OXaI6Tg8y;brfZ-i^vb@p-j@!O?lb* zqoc;@x|eUf$;O{u9G2gWB{ z$5UjPv$^o2Cb<{)odD8J6z?F~!JC1d7{qiTVcenDJHbnwbx+Tkg@25%Mt$9U-m$SA zb97+8z=OE~vJc;Ue#S=3!IhY%ZIZtWH>VUV!_X3QQxof)?y)njH8yrZE*M*hIrk@^4%dh4@|Px#-CpRJ z@#B(z*s-#GTxWA%wn1mW<5|Y88*#sK8;n(Bg{V(ADmmZ#foh1zO9RshAH3r?PB0%BhwEn<@r4I^ zKaL}IcD*pa_o+wbW9*z-f@cfff9s^-zTh2NpIWlx$(3EUJdJ^W*SJrvY`5{M(v|lY zVy>Qs(dPU<%_a6ajpsdh|BgqbjL8pU`<*{_V*cnukF4y*yy|IMM*{tDU;oI;AAzQM zIih>6h=Y za9}p%hBX_%I1OEcp6q)2frZeW1<;|QedimGiTNo?%c{#Ne?2f0_>mWyN3-fMs)L;^ z;D?^)X!w}Wb$O4mi{4Xs4Bk^B);UZxXzsqF%_b*QP7!izqxo*6iGPtVR6Kt7vyZH- zglteJ?3}6mkrwgO>XhkLzL+#X>D&1uztk9f+vgS)&&C;}(b%#rV1V~d$dd9$X;k8U zbYHmeJkjKCLzvZ{-b*{?$7z1}R;&fY{P3&vuF?FYs<}zb2k&Zkw$Xg>FXo_Z^&p)| zTQ>FA{X5nl{po@~A?kSOPmY0yoN-4gKekll^`Va?4Lme) zvXCqB@(^TE`5Vl=fj8pEZAcxWur}fu{L}gxk|p^mxeE7}O@HgVeEWgO`A;b)=({i* z8L3oHS=XqRU_C|`)-&QyVK#Wg1Zm?C2)(n7VpWb_f&@#A`WCafP1MfJ%Y9+LO1&mrwwro=r`7? z9LaVV&s)oeybpS;!Y^DnWMGY>+iUEqL%P$hna| zG0iUieQE<$Mj0$8qbDWe8P7NJqgov;_wB`g3&Z9p1R3$tb23>H6dn;SmbG{ z54F;^j7-|a6J8Up_K0tWdm5`n9z?b*52L6d@l9m=^Z0%+MBSps`#d}~r9@!+Ft3oN z{4%ea5G7%H>M*UOWAu9cC+-W?6_=p-6UdKN92t?GV@J<*#=|a6hR}_DTIGpSSxT5YS zdof+<5~A+k%;9`vRQOg7--`QV@lSSbx(4IVKt+pwXYf4Y5mJ0$-~MV4CuSKC-ZnPXUr$944<|mc+m`AU&55roLbr z(}bV{WFLnt_7YNf9Mgmpeh%^`|FaSU`+?|>XYK_G{v-x=0MWO~Yy-{#(iwl~M`hMA zt_Gr>+)GIJkv%;h;Vammko-%SCZzo41G5p&56l6k1L?jrMmisx?i(jD5C`-k{7q!4 zAI~GC@Lqc1amHRkqE7*@L%icaibqJ#I|`(DgcOg?V<(!B!s%Rg)VF&HDSSV09@5#% z{)FV;$uuF|w;OmC_YqQf2N3<#%$@8{Nb$BZO-S)JGfhb0D}W^b`M?68AD9752Yv`R z3y85)rUyvxosjfx8qone}g;=KmE0r7f(q*uoz2D*WyKZk&H-$99i4yJc9-3FY6c&mZe z!i-!dv3x#|@|iC&;0Mk}yjeh!10jWbm?k7Sqyf>GX2t=tfag#nko?X7d-1*qiJxAk z2`L|^fO&{_448?(ZXlIgLW+NsX+nyBi2Z4A4$)+nMPEPIDN)QDVGJwgjer64C&fHe zlf*y}h_*RcA~8@1M81OaB?de|R6W6I5(9BS6ycyDG0>073`HY&PGX=}R&GI>cfzX< zkbN5>2zE;h90uan;30{D4j}%5IF^$z(2Ad%knbRn-b0DR^4CyYNbVuPW&E?>&99&x3N%Mc;j(a5@8*t`9SPKhr^`Kf`o@>Ag(P zW%|2J)AxrdyzkE-9W zh0|~0^lFI4zp~fe!Qo0Ci;1?Rmq)ba`RPQ1cV(}76wkj5ek#3qxUKUhp8zjJt9}sr z&w^I{rZ%QkKkER;SN*atFs=F-^qpo(PxV`{-dfPquG6&}|6-m6e9#5^hoBE3Pjp?y z@%uSG)iV^olf%b?2C1*pHX7?SBf_a1TjQ_7omM)_7LGb8BK(&&`bpdKuC(1x^NiN? zzi7K3bxcI~H*NQivC%Kt(wk+wpXT|j&-<~BruyF+{!<%mpI3Uoc7K!Yd6bqly_C@uKD0a8JTHnUo}E(9 zkX|y8H6fe5g>N`v;6FRN`{TU&EWePdN9YK!=w8_Hwu5s#QwQ{2d$nH$RK z(Kxy%;=tR8Nw$cCiY%gtI6=ZX6Gw6IXr7l7`J*=`P7D^5Vg*cF4h^chzqXkVV8h+3 z)G5NaQ%vKDJN3qIbv~l?OCW1PVLros~jN>aqG^R+8-Ddg^JqiO%c*pd_>TD)%nfx7H;?vh=Bx2*GL^-) z^f>$a5yQ=+W&K=6rST?RhyP2?Q>FaL5E}udulXzH4TqTb+T17O7IJ^c_=QYwwaY={ zM7kna#8_jjn{y`-(n6a{k-G$8IKtovG%C!cP^LMd+Xj)G=MpvLyzBI zcw{Nkz8PsRa6B?q@4LaCp&ZyxANgF-z|UYaRsL4{av}Jz(!_^jXVE|r;?uqrqHmsN zY()7iqdbH;57#&zor*O5w6{DF@=JF(3*xpF4a@>RZMe_fihXOayVjBHtl^wj=efeS z3q6(7ABNnV{p1c!@1*&-ZPPb6+9RLu+)*^J0Q?ZI#E+}9Xka1eN~Y007V~b-UoIVR zlB^B)58N|TUU9!&{KMJPKff|@`nQ{nk0xHJ?svx(4j4a~d6d#}k1HH-zC1IZ+$GbD ziHV-JBXhPEJ~jL0ncHv0^38Dw-yWWwKQ`V`O77VoEqp4&F>X7)Ia@F`(JA}{ort-x z@vhRb6vr`kd-{0LI6J}_o+Hw>zuQC8u~%zy+sbEv(Ay50yMT=m^E!nCm6&6=9B!Jk zs+;)fBZedO=@-^*>Zf>Zka^w2JLn9G33Zr<_>Ob(uRYLPnxA<7>gf2Lf4Y$K2h3~K zB^8T3kd;@bh&i6h`<(-IQx{3Us#NLcbPd$aSuFj&m?r(6fZx1Zq~Fh`NxzA419eMF zIK7$D?{o0GdkLp^t@OJ(exPpMQciES^m`6|_utCtWlBHan1Q+{f}EaD`h5d_pIgT1 zoW}qDh}5I?1F{U6%C6 z?sLy_A^mY?xS!G+Z_>p?C!2H`(U+NY4bdqkeaM>rgnjN~l>S6Byp`flGU;tZr=Rlb!o(#}kuDhe=j33T3%HDpU9C~yQ^y$OUtGl6JcVXUm zHRkOS#NN+d*A>`fIr%v!pEJ<;_JthjC(m5?gVWgl2U>d|;zj!PISUI)#*s;|ZRLyu zIE&?v7s_TlZ*1%YP6KZ+Wu)#wKECj1{I4r<4$EEZxAynCrU*IqLl!d4Dad=8@T0n> zZ{6nn8L#8~73^PiyRHzjpy!vkrfpA)FC5tY)`dg#+yZ`PH|l(P=1j{@_Y{ZpLGdjfIf=jAtAhJ$Q#R9(Qbf27A>@5EoqQ;phvU^HIScBNP5;L! z-b9nu@g^eP<)*)gH`Szdys3yc-Sijnt~O~M?`p)m&h!`Y=9sjOHwW?Nn*Ji5&!lxc zAL8Yj{vzHylh*O(A>K`djkyj7;Z zhv(q~-bYM-5wFUmb-XIXTVwi*ce`EMqWnS!>&76iMdu!ezg5?IRJ>%wv*?_NXVEns?{dVm=$wdW z(KQ`!I^tP$PQ~Q-s}Rqkb0VHa*L1v(Af83%L_CYG>3C}p z&!Tf8o<-MmyavRx=$wdW(KQ|KUc|HLoQP-9wK~-ILa!cl7N|DagL<^^PZuy%WOVdb zM6a)d-4*NM3J20B+#%Qc;G8O2D?)wdRKv9^_pzk|`_PY0`)K0R|AzLr7wxiItHv3J=nR6kk5N2%W<%##!{7E9llFiXasJNqYfD0$ubVU` ztz?|E&uYZu^W$dq7M1n0{>- zYqoveqz%y2A2Pmc`eEJR#$MNFKSyo6f%j)!57%EI*TcQF_M7PIM4v12J6GIuzsK13 z4;XXOnD`uYzzyA?zI0+@u{diaampfzvlmM&xJBZU5{Y*$kr-Yoam%d|9}i0Wn`ILB z-6rwKa)~dmkoc3^CBD8=;y|guG~@+iW^8?Idv!W+HgtL#a0w857gNcG0GmeY<3RKU zQ}+QOfYg_P6M?S-Vd6+lL_aPWI2%YdgeAZvU>JzJralhD^hfGGAob5)29hja2PR$J zwsPLgq^DP3ld_BYKP6-1j`TiQ_|(I2Q`ssU>l`=&y6eFQpE6#^-9A(KPjaGvIORD_ zU+EmU^UAm*tFDhba>tEvM+$$NBFfXxF5`5rnI!bo87Jvp?*A1&Kt@{uw4kH>!;{$2QY;@^RP<3OdeEnr>Cg|(=! zSkEn?`r$Ork@*?=e)No=xr#EeLf|svROlm)l=|t0J2ZU(=mnsQ;8yDe)wmAniT<^9 zot@G(=$kmRtnb3#tZc`5gy{y>?&7S%67*qZU1jWg!nStT*jgB|-maI=FC*Si4safe zxfV~H&Ec5gJMA*p+Qo`zz&>TY&w$M(f`+{&g6=PhpzSiV<}V%fggt-l=r5lWWe?~1 zh&nkspM4iTD`e76>zQ$;&(&$@i#~|8dXCif-KiKeJ%}}dUrg9l^R+d0oP@;yw>v~#A;;YGZYbO;9}GZJzkoCI`Vzh{HQX%ENh`!zhlj3&JrDO* zo-y!MS(K|HW7o|^@xSh#jI%49iQ9`F?wLPxho|BC`8cF7&d8fH-<3Q^V0`jAfeFbt zcnkiC$!`c8n>;6;=<&(x1YVZ>oWKdmZwQ=}JZB7rPflJZ@QUQ;1WrkQL*SLkb8v3h zocUKJuM;>UdEFlqa1INSCeYk)EQRih0(mH6A@z>-%Ku*;^)W zlvd@h3f~T$!L;{|nR91DXJ2<4d9$IjiHSy@=^vjw8}w}G%905G#N@E>ADjF*`x{6n z5jwIDbRu-)WnlP`Tco^jc4qiv(v7}OS@;R*_K^D%(p^aIPf53s>8BFVPmGq^Lr!Ob zdWR02MfniM73w{n##xpRVGOI@1p z^y;CigaVSyl0PKmKC~vGAVNOmw~oRSk`u|@U=2@9e*O3GYo_qA$uE=pUKy_CMSk}u z6p+jwC-;L11xjwA--Dk=K(p+Mi2fL8mfuXGp8(A=bP)Xs&@9JZ*c$!;{M*mHqIRba z{n*pPgiHDU9EUy(wj+%cw(P$8kVsqtDR7v<~)k#NR$6 zfO!Y9=j<_VpYcn_#wBpezMFjBU~Oa{p6PDGJ->_Fh)(As`?k?hgu2;&UIFzpS%If^03A%uGj%)NLF z8N7p8b^JSo?X;%*yFA ze-xhXcJ>N7eN!de99D*~lOZ##wQ_nb>jf4lPI&zqgSNKo{U&N`@cK1H8!q5nK`W2; zFyfc`e83KjCH>Uru;%?V^gjH>=^O0#i8N7Xd#>7do8zjk+w9?#ZmcmW2CF;&fVrAJ z>_6#2d+=NIi|9;TvJsPxIGnO?OkX9@arB%^MeUwG)Pq5-U*dS3N=yS{kY zuGdi(QCAGia7_BeGtl=sA88Fs_Jk@xH6zbC zq_{n(@7u7ta0`7f!v;-fH`3XC5z@iDvt!RiNC)%9j%^nq9n2Ry z+Al&nm}hqExCrTB{@k(iBBX_UwC_ z>@MgJ_>Gwj(3DaK2#03Mf(lb!5V)_3?^My8-Y+sbG zBFLM{e;e{j`FalLgHzrTk+<2ii;ti&^pi~}>5inQmlnW2mY6h_@~iT1O!zCxx8;56 zeB0kA@+jWtlu_o(W;;^rFzV2L^>NvubvMKGU!g1JSj}EXOI?Z#cT*V(N`0j=3Z0*$ z%8{toNuO_XKkfc?%B~s0Zrc7i`!iytYq#sX@A5Nq(x~#I+D@v=mOxHI$33&3o{hR^ zA9Oqs>vc%a(_#B`gq#C5*+;djlup=|j!4&%ju9dqlH(iIU!iv*&Rl za9(@*RN1F<8#u>)yy3^(^R9E4|B!X8vFixtMXp9&jI-`0xF=0M@(s|AkIs9V=I%v) zKAtq-p7iM>6wmFL`n3B;u2*^!3J?73vZ4dd@s0zS!#>cPumJQz&@RwU(8Zt^fsO|q z2YT^=pWWCUpl84S>TUgruO#<79v^c6U$_Xg;B0G@qk?wKc_hAI+&&v~?KjWHx;BhO zXx`n8Iqw;thV2yJGnkF~EPZCr=VnYce$$Qb8A!XJT!TUV>KW%R`*l0)EEir)?k5{@ z@1tW5^gbGofAH|A3;*ElQ3w9Pv#Tx8`*S((Kx;ExZL+THgWrsBZa&7KyH3MRGU$El zf`96yJC0oO@udS53C6~+U_4IiA3prhl&AZg)3zH9nyZoPM>_F-_xzh{*F&ypVm|3P z^k?&aM{63UZT}keZP8a2XOkmc?9s>B0+BSyqUR46a@=F(dMpFBdhs4$J9l3x*JByg zay^#eat*kzm+P^NXXSb|qAlrSK#-6T#se^T&~A5vd0X#x6Acd#x-(1mhnyaJuTN`8P#$< zma#Bl!2K1u9?N)EuE#Qd0Kad_^;pKw<$5fm)IH$-fw>;b;BQSBKZXBK&GlH?zdCWi z{R?wFmiGSz{QqdK$I|{SNdq3}PS>M%MXjL;q25Lr!nq5JP_Bwmz80dKEkJpr^B2A+ z>?OAH9Wx!W@6mg**R2>Ri!=M%X?%q`g~nIZm-)HH=3pH&#OTYQUlrXh)UA~5!s|%W z+9q_Ouhr+8B>OP8CqC`ovH;^Xi`ito-jnL5h43DP z+~D^*`e_vJ`a9fDuR}hUV$4kAx7HUX?HUXJ*>{Y6I_Ke(UAZr$?8+-j!I*r~b3uoD z`}W5c6g&jGHR|KChwzOK^!dsj!WyO!Y~{be*qh=!@%Vy*$}Q`TKJny>1x1dlF~>D? z`u1Hf7KF&14mZBvwH{~nRN%hrss6=TIH1eWzNCHyX)Hw=OOeL9@Vb1OE1~c@*iGhu zf1I6BFyplGYXi@H0&!++ALO)_@PsK)b=f0VU>B*a~^X`!%F8<4@xkO`GhT8sS*?31r^dP$TXWa9Yf!obYE z?#pKGO}T95shJ-k**G4y`9Vew@I&v)kuq1{Y4BWvG!l`g?<376;c&j&k(e|Cd8&kL z?q8RG_$P?71^913s)z0cLZ)I~T8;O~#)@=@9q&WlgV}eX|FYll`ZeEzP0svAgSCIx zPGt-H*S)wDHYcouc2s|!Xq?*kd1FfDbXBfizviWSTR*7-W#tBYIE{7eHb85g*N%R< zo9usB%iSLLtyk+_`0j7g7xevxY=Op?DyMHy^$6^HeJ0pdDw?sIi*WUKq<3G7=fT)BC6&Mr#XU*Aw6H>m@|FCEKezuCQQs7UsAnl&pUf zx9{pmbmiQWYr1>(^&;s zUZOL6OB%eMJ4zRL)VJ$1Li4J88CCPDyct#a#`(Oe{EV6=9NW<_uP$q@AMX0vHLc_( z|K{-8IzjkAY-%EJZw5tZ!NKqKHMPz2s{E2}4O3d)EJ0yI*4nicm@CN5^Fq>p%e;^` zL!@cmpTi!U0Eg=o@?@Xml9C239;y6%f{;^;rK~7*&`8-H!C||N{2P)FDjgz;T2~X;jpkS zj&QPab7E@Fvg|BU8t4mY&b!i;{0QxUYEv;AQaf@Z)uAd+6$|<5MY-^gRbGg85xBR=fp1Hx5@JD@x{u;DCK^E1 znqDv5XiPH^ZZA5v#?e-!hc5|PKEXQJj!_b78a3lKXBs#pMRMQX9vcGp6`ju2|B~fz-VOkn(vq!5t=%#4wTu0TVVhBUE z?(h}#NJbN0?ljq&2f_M#UBkZLMiW`yTo#L^hEG235VE_Qt^oO zohp^RsugrhGskV?m}5v!hDkLtQthSb6Wb6eQj5?{6X8l|G1W+y{5+a&7+#I? zSVmfAYT!3QnSrTMHhL^Ho|+m&Y9lCdBNN)FieB!>WLBN)6Z+*1sm=@Q*@EIC&$8vW z6nR#!$jMyoSy8{KCA$5jsk>3l?=$&FwIv5)*&JcWr8NWoOeD%2OsSS$mOI zttj<-7SrT!lg0jm>iwO~7!M|T%G=CO~LJC&lNfL{1QOrEZ9^c3Bim0v}b z_Pf)d$dTPp|WF;+clDr zZm*<>BD+Pl+Qr^L#awRjpz1V^u>XURkFfdqd~2lb59=-{{~5@4WAj?is=3*om76wH z)z^BgrtkN(YPtU1*S(oYLWnbB%%LatM_Bo)hRL7nRRcxYM3J$;rWaQ0mR~O7Vg0De zp^i4aeJn^a=V7aIcu`Z8KXP;t^2+AisvJ=xV4uCC=Vf)F3}@*vMZQvxY|+=t7h?+F zP=kt44c8RjHL6)A{m9CoJh!wqHhC5-$jr*i@~qCz&5B(vWGh{u`h8W%kIKI{8&2_J zP~*^QFU&(%p--(^XgPQoq?2r0*%2+JVHS7U*Lj!)${d7g70Dl_Z_EpF{CPPrD*5wr zy}32HIeA$?(N`AYDkX7Q=D8w9?0@a+-i9A*RcCpn?ZPXEr_ds^_z2mEZr-K_n;N~l zrbwxhy-z$;qx9}8oS1!LJvbdv;`rtutTnkjA>KnyfWcoYhibt_!m=N*bHPa;%Gw zS))X6*br*K%4SdY(C>PbHaqT9m~S@f%~J`mb~y%XOtm0Q8|Gk3qlX`!D5>NK*@)^+ zHW7nK#qUX9QJOvb-H%v?ZYFdlM{eoWw_H@Y$W`?um=Vau|DMTZ6b+`5%SCA+W9W-e znv9a=E0@G7MShr)vj5!Lq6}@3rteWS8!7>t#oJQxT!bQKs?zWx^IsAfgyiRS4MJY{ zky>_3?FJefdoHOJ$)&*Tsak5N6^-lH*EV>{8aFjJVBCmL>lR~v0Mot~#i%e$XU%3u zdAEHSRp;cI1&DKsI%h>uvFAN%lT;<~zOH3*c|{pCBZiRv{0&rWA&sKBQVH0q;)~jP zS%gGINY*Q)NsCvMdR7E6&HqGpLAHt5Eh!jF*s55V^3*zLVK>VqJL8ahBGtS^ zu4(Cz{D&88TC*lpPm9E(auBhS-I9UP?sJK!zX$Mdik)?N)3ag%dsVqvYn4Np?roP{kfr}_) zLsk*FY}@nZR;xayG>HWp*M?hbtLr?)n?m)TqUzRgW5c^2uMDla6sktD?}>bdx1Ge0 zkDSW8Bp*>3+vNkxs+^j+B%he_5uW(T(DGS~1^uek@}w7w&+=Xb6VqRBNz2zYkr0+e zQrN?ONm5x^y37@@q;hz9fvHnif%!qMtk(>xLJ*9pLaWWf7SYH0U`*?2H8KmF935yi zl514R4CkZCqrbF9J!l^hvC&EOeQnl+m_xv9y_UzEQW|n?pq7#&bZ7WIP4|)Q5uO9QzMyPurEGjVIu3?Rj}>=8({}P*^=^9$x(aabat18)`qJ+x0UKV!U!x3 zNuxhnZmQqEXkA|ZEX$LIpZ`!L3>}=vh4B$GxkJqOn-YmyBA?Hd zQ8o!-RC@$PtEG1aVlqp3$ac$b>rMNl1>f54M73mU$voFfetW&7mfIr{uF_ClV;+oa zCb_y^)J*Uk;_#jrf)jn72xXFTqam9`os{q*>~LaA?}I1BntZuRwz{_3TsJ6+B2dP=cl z>&~!P8;YH_YdzOt!)<7b=+UTUHEb%yd*BeBiK?M`yKNA_6;^>ht1koqvkt@?!d)d{$cK-9^uwtwc*@n-$ngr z6I0ak=)VRrWthG>FO70Et}w^r`qQlo?07OgIe zwdy4H0O6hNwTNBcsM3f2u2s0hPmrrx*6ZaCyng@ThSu5!91m1_U$_^{Yhh|a z#WFkQ#-R^v-`*}k=!VdVjN5DyT+w)6ZFA8j;aQKh*K3L_OMt05iWVLn_cZu1G^6gB z4Q7dmT&ZIl1&n;8q-+!RzR&RcI)-2U6D2d%F^W>SOEOc#?HnF}d;N0V7aBb-q^p(` zFDbI9mgn9q?_6p4jObQ+^F}+qR4OJrM5g&FLd9%7OlkTWvhx%#xKrd}gA!XBpH zLdpllp}7?*ma(FEg=bZ5bIYa{bDPXXS&Xkl6?@&BrEwdk^?TP_3#snGm9Zk+9BQZw zc{aB;*KVk#Zbj@C{GE+4B0Gk*N@majl&L|s{bgb z&STVYKf>5%wdL#62VyJkNXl`*+hJ46FcGcC(S6%fLr9SJhVFX$`NIpld!zzKn3oIK5U>MW`-chP~ zE+0a}>VT@OG~3d=30)8-KaAVu@J*WqFnCaBY*1q->e_i#r0+Ce<<|#naP?)HPUVYN zxfA1mU0g@-*#vb+A1A=35uL)2pG^>Fs$qVO&N&>?YN{$Ui+s4KT#^szijoiN5p~Lw zl8@i6KeU=Ws*?P3)+HmE9F6Q3DV5=VYZdyjw>JvArb9G6NRLN2@s?D``KudGppY;(Ru8MXw_9#WQkG9tTqSQ?J1?o#4 zYa;fWXRHj}k8?nlH{2h>YB6g3BL=k}jFrIIKCiUW;*c%w7Rf3}3TH*Ap|!5IIdo|u z9agonq`bpQ>I1%dPD@JG+@w}komyG6Wa%A$nQ_LOc-y3We8g@+@nt z3fBwsN<_6Z`dJ$up#D|PRBB17qwZ{~6e3fLI>^|w2FH-0yLm~sWKFKlmz29YWrbc^ z+k9`RKB}?Po4rRlQW^57dijl0J<1rtnW;3BpET22W^{}V4Mk&w-qqn4%m+`rqvY6pFV`VLU1R~t9{+-x;wUJED zaKOODif&r9BS+)Io2a80RhB)I(HQ4H*zA0InUnr(I34?|j9+w$u&{n*g55|%_Pb_3 z7l+ph;d*g6ki58oti89fev>e;ll?Yw*7LoeW`CdbCzQBg#2=x!{!)i)y-x(D7C^13 zU1O`{8nJa~Ar6R?>;9UsSxt5-#9?F+9kCBcjhD?`RKM=b%aT|cYPb(e4Kd@jya5$o z?O^>?Ir|ei+r!InhTMFa@AZmR^Lj>~>w%2YS}bua4YlIrGirrIokbOtSl?VF&fTHf z$hNaY&qm2fWwU0roedt{Nr^g)$M&ru)P9^FSU0d}OPES?hgzgu#RnyzffcgkoMldiA(S2?( zsy4txHVUKR4Hjxhas;2lmS?_Zt%;B$+GEkUiMDP9mcVH}QPVnFUq-*mOm$K9438G5 zSV17ys1Nn_2Q}W6Ha2V#N3N9CHnfO=-v_oqQ!=VHjle?fGE5oOQBCdltnz8iQ?o88 zhNIl=@J^<1-zP1Zl9Xj?Y7j~3z3SpKp^eQSY`N8V)y|rNNV_LiJ=EZQx5%DlMD6>* z87r$z6Zy4#iX2z78KteYp$(o@;pSF4A!@K@YE(U>$i{+46mr9nY&oTqeNW8nKRB{n zXXndo>NowrJJ)hHD9c})TYxXF`0{e{vVCgN12mk9zU^HPvZIF9#&DlpZ;j^OyCYnO>0cw~OT9T&YMG&IDa-1bH?_2G2({pw z^?cN10}iHL>&c!gI`yKEs80Q;H!%#+pFlfHp}H_ckNJ5Sf@vjCA8yencZfPnrx3G! ziI9_*R^bh4@KAEfs#P{T-pVEAv`osWw)DPGlQ=*rvWNQKtJnKw0L1ErrFlcB*J6Cm z2_IK$-XIlCY&X&svC=W!U%i-#K?~Yofa&UmmAmUuq0R6X8gN zYeO+nrMe6wJeOSWpW%(F*lZ-mPZ_Ji)lm4&V)MF~(D#%QBk$FmzAkL4rq^9i9xdt! z+TJ(9_D|td^QR5y79r$(tU zq|B7H{f;OOdd5}8qq9crJtLdZ089Oq_H)4)%{~MkRORGhP({P4HF^Hr9AB0XN3v0) zl|iCdS-YXJRhWH6y^zC|uHIUQ-h(n4SUz$}Ln|6X`P2}~Zac8IKU_~(s{*g;sR#=; z$~n3!1u;$7gXhDGbLM0|?5ENQJ7camWIZ}h;6`Oim~DlO(VXe&8vW_gsC>!QC9K6h z+qqc0*eNoXvU<}6+jb@mj4AXi z=<0#M|ISYRAVqBHS?2!64uB_Ol_?BZu#1MyC zL`*1su+*)_{_$l|EJH6*FUp~82KAzoA++nAuBU)jMw-Bem@LEyZrDzPTG=J- zU$(hIOP|3!I7BZR7~$RnrCC~55k0O;va$|$w5&q!RIff*-74&DmE~{LI%R&Ua-^1L zWo45U_6WP}?~~fyn$k~V@<6mYxA{P`Kt{TQoX6U1=~2SK`TS|f!@?oi(j$z2OoWH zXl(VgY-*wdPHSsC*aX^I2j3PfMz7tV(^_A<#dB>~lQIqdSgggLAKBE{5*Eo{yT)_v z8hsZgxEg9g%{3m5X5)7qFM#(fDW+$QPtrHGhH=0+5^AaswPF*=1|wut8JmqQ2F<1# zV&2RUHK}3k#uyP+Y8ga5680BPGWi?4Y!FB$7XYOq;=lfX`ylGC0b`y1^Da2ZCNJv_}JeNrSu-Jj0kWrKuoZ_WcVqh4`JKxZyzT71c!4y6_x+WnKJ!TfLQX+zgmX(4-%er&CvI|`Nf<-v{>cq)G*;) z*UIz@B}Bd7J`P_k!=v(7c%2Lw?ESTK_Q{YGMvQ_mA^I)&zI4n_&dzu_4NE0>Cd`B-q<}z_!$l#Ed6QRFbo!6#^IIn zF;V;%BYc9x$jk>$0j2{}97g>tiRI%s+yG8R_*w8n;crSTKMbV%4gx8BKakSf%D5Sr zZWzH6Ckb*TFvxcrCC#!!V`+(}9$38W5@$OqCco3%wBelvutENb$>n6yML71*H2j zBnI3-x-U**`9bI(-PZ=B`|or=Si`u2aXw=@V=AM;_$FQ!rFV+)7~?_4J&f&)^}sCfbq_EDm<7BZ z7zado%RKub!x#^`3rOJxa1O8+LVz>)n#90SAj#pd#Pa<>%72%{@>U?_KPbcNOC*+O zvHv;LGj#u1iREX26n{VaQ$2<7@_Hb>k9-bKV@v@eso*$?fj3d$DZM_4<$Hj1UmcLr zEnzGK(tYzK2Brb&zBGyDM^XRMecOO^Un`K}H%SZxffT<)V)?PFW&9mLioX>|@i$8h zlmRLJ3W?=!&Xn;F0V)1oAjR*L7}yG=_?sn`-vcCm(t#9z9Ag}i?lU9?dOh;K*Cdu7 z1ycMD_TS0=t?VCU{}T4kVE;JwH`xCSDsf8ZD3JI%EU|nK`?s=x6Z@C3e+K)fv;R2u zKZE`OrPC|1{1}kZ-^2d9*?$N7w*hA$yotl9e}enVgTVg>eJupi{quoPgvNGxv$lK*BPR5RErF;FJM>sLrD9|xp(=h2UaN(TES22KJ+dJ@aKfma~jULeJ9 zXKVw~eOo04Rs-q2GKuBWfD}Ikh_PCx0i<-!q{(=_63csl6z?eTD&T$&@8a--m&@=@ zAXGfKM`BFt#m*v9^=fq1pSGKm2{knYctSe^o;_uhwb&y~OqAnDZ( z#%+v2pcDF347>s1e#TTF=~Eozn<ySJK4Us#Dq|eu*<^WN zFXM5>ql{gQyBW7JwlY>Su3((cn8BFF=w|H2_>$V2{Xmjam&Ed&Kx#*~0j~kpyA8t& zEMq*IAn71*F8uR>nZR>nWP8>Nr1t6rkit8F)E@1WSiTKN@39F8l?>KP4AcNoB!ZO^ z11o^D5pNcd(whdP_-PUYXXB-RACUadNDQ0;LUn^DB?fwcSAaeyF>n|N)eIhz80Z2{ z2EA8epaYl+dZ)xdI}j=u+$J%w83J z2Z&b`JSH)42nZ1d4@wN|1tRHSr^G-95P!j)5(Di(7wByg1Dk>LK3gT0`+-zmrZL7b zzKNU2|0H8KBaKJMe>dYc#wNyl7)u!aj2^~ujQvQ2(&@#I19*_J6Zmi7m&PT)KnIZY zd#A+mb|97GHYj4U@!1{z*E5Q0_pu61Je6>3D^yM5qKE*0+8NMH?Rx%9U#4* zZv%G&zXhcCa}-GL=bOMb;5UGqfk%K%z{9{g;6DK?fnNu%20jm50XzgO0sbSf5coA< zKJcr+EZ{+4I`AKW9^l^t(|`wnDZu?eH}ES!1Na=UAMa-$@GP(k*a!SFuow7uz*E34 z0Z#z;0(*eZ0!cr<2s{jY26zzI3G4!X0oV!rJdpI}Z-G03dw}i0r-5z2&jB|B{|49u z+zqS)eim2>{0wk4@YBE*zz$#u@F`#+@KeBi;4WYma3?Sw_(`A#_z7Sd@JV0_a0k#0 z+zvE=PXI|T9|xWVwgdZs9|w}2ehhdD_!#g6a2v1(_$aU&_z3VY@L?e7a~rS=_z;lv z`a$4s;75U^&s%|{&kq3GfcFD81GfO1fSZAJ!25uezpaEP5B)e)DcotX(>;tX^_5#-c$zE9tJOQi$ zQu(U}b_1(`hk+sBL0~1Y3s?c{1bzg#8(0q93A_i`4*W2%4R|+jGw?286L2-K4tOW9 z61WPu8h8hA1+WZQ0xSg<0#^d_fwu#*fGdFMz~w*>@HSu?a2YTK7zDb3w*n2|QeeMf z7)yX>fhE8`;4Q#j;9}q@;3D7&U@@=C?HvxA81HhfY z`M`GIjleeGJm6+v0k8>}53B>`0V{#Iz}3JU;0mB0SOUxj76N@h++%owSwQ?Jrvq;Q zdVq6*X~0Zi3NQoc23`*Y7;}K01W_N4p+*NH8AJU6!fwV+#x}-E#zIC9qruo4&-XKS zGPW^RG8Qs=7!AhWIKH2;ld+AllChA{!)P$}y7+#^PR2IIO2$G)52L}@i*h0I&)CV> z##qT%$mn4-7<+NExSz3;v5m2kv5?WjXfXETy}&u9o3WFzjj@ukkkP{k5cj#AJ+9-f z1m~Xk&Um+TXM9I|g7eJSzOf0;lVeZe_t@AT{2m&67{7bRcHwvD*be+|8{3ZGb4mS4 zh@aGl-;+tF@Ovz&2fqiC4&iriQWt(Zl6K>FTT(lI&n5OJB7R~YeorKx#P6}h9{e6m zJcQrQ#J%|4nb?8fZHeuPajtXje)oBIf^+w{J>wFbZ;m^Q-`;U&#>KgIj_(-1d%WA( zb>;pm-Ogh(dS=AA4$e9>>+mdaGV2t6_g>p|E#h6*aoz6g5}Ze`>&EY~5A}Qq>13SE zK#nszGvi!c-u>Q#-Z)pU_l&m>c+z*u_nI%kdEVES9p^fceKPx0c7n4%`#e$plm1hF zrn3D+@rb3XWs5SxASOzcfQ+srl7CD?L2hj;Tsd2hvy%iALn{= z{@MBGfV%^G0-XUoFK{N%2kg3O|4j#PigTU4>D*2Iz{58my}A45I9FTYw!-$pI9E^M z@xl|pokbl*yNle;*NS?J5}a)dwk>cw`xl&F0I4n9yU^`CQGBu(d={TC#v5IHd~t&F z?Ba8a-Ok-5drFY|lI{|>vv0|pOA?$rmhM~%x!t<|R=4x)t>^ID6FeStI}a~Ax(xg* z+rRAKvIOUA%X&ebEIn2FT4|hXPg!T#-ZIF!tfvg}D?1DKwpHz`5}f;29bDyhzIo@_ zJ0XG9yI04#Uc0CFo-_Bvx!TKjlgC7rd-x+qKLW{pr2iw3Ma9{Qa}{x}y_H>+ z`zuKk(FR?H$_0w7uKQ5FiB|Jyk1?(0%_gE^N=6xc= zf0F6{87JvoOs6sZ6w^gxW%zEUo025`d8WURDCxaS{|oc;Ri@uY{YcOM4$~iJ{$6JK zDdz7a(_dx&e!}#>Ge4)9b}`+@bO!VHKTLm^`THZ&PcVP(pG)~1Vg8bt?qmK^nJz|! zPyAlV^a|$pYNi{Q-&sr_L0o!2%8vdZ#do65Nev6G5ApLpaaz(((*2h3O&tFS$Nwrn zuZ!b$LFA(l@g(?4f^o0tll=P8$kO#izJD?rVY+_6{QZ{cbIjjSG}d%o!}QP5h|{%_ z`Awn*8`s0k@AsHK$o!r`XMnDG{QjKiEYP)>`SYL?L01j)cbw@bnZE{fHt6~~^EVrv z5W4=8`B{w430=ud*B1)X2rz$76-m0D`TNEKNk7f}{pUhSU(5VGP%LRL^Y`IJl3vdI zUA|b-o0-2?n0|)&+i;5v|334RSR&~G=Et){(&H1gZ=lAG@)Ac9jC%ICPMkdqOg(Q6s^LJO3r2m`w*-b8yA-JH<-U0)=Bzx=I4KyPGkDOdKrET^Y@W@Nx#DUU9&;b z|H1t9G5ts8XJVrapT)GVNz%)hzm*#$eJ}IZ+$`xYF@Jk7*rMws^Ygt{NxPW-&rOo{ zF@NJRc%$nm^OwC@(ml*y2!lPk{+s#v6b6BGUB>iF4@f$n`Rm^*=@9dm`JkkCGJlIO z*re+_%#XiKkjAf=pIaZ6^i@nZKO*TbGk<^csHFdq`8%*p(yuT-|NWSx-(r3;J}&8N zn7+Vtkoo)Pb{YNv^Y>qmOZqwHZ`^iC|AhI;-XUo>(?4eVQ_NrZNg4im<}dpbl0M4( zZTh67|BLzg+D=I)GyUJYB%R0nO?^tzYnZ=VIwbvR=Fj_SNgrc=ZvBj;f14oNgUO$j z^zsx*|C;F!Gry^SBg3~azvr3$Eb}|DQUR%DyD->xr6 z`d;Suc&DVl#QeVXjHGX1{;qgd(l;`HMSCTE7xTCNOOk$s`P=b#lKu+w_w_GJ`Xuwy z*ClBe)9&Xa?PLDZzar@n^S5Zfq<1oZzhZhS^LOBY48M-~d+qNfUCjKAKPc%9%wOJD zCH)!Z?;z9PVScuMO@{x9`N{uBNngeEkC+ZJe~%xM;SVr>2cDO7JoEQ&UzhX*=I^q@ zlD>iY^By3YpD;f+d`r@9rtki?r1O}+vhPTG4fA(f zx1`T9KU-dq^m*pznHMEJmFZJ2NqQmkN5)LL>Y2a4WBSv~U+wo~_%Y_^5 z^ho+@MZYZR+nK*@-n%GpDo8F9nbXcf0p!I=I@1nk@TI+-y8ob={DwX z;wzH=2j=hgS0(*_nV*M$DCzM`KX+2n^O?UN{z%f_WB$(nSkkXDKXXn=`YiLa`X`c} z!t}O(m-GVW@8C}*y`K3y^_rwV$NahfucTjQescd)(*4X&MX#h!QhIT);rv|EZ&P~l zunG1_+KDjO@eQ?(@F~x?&)jFvkaJX7m z`58{Xa;8iVbCM!GwJz-+`F*H$WYjFs^VGVu8#z7qEP4MIn18iyjFP4JYMtcu9AB-w zY-d`nvz*KFQR^nBbNFG-ZziXw)=m90=TEJ(TF?Bdb!F>0{s|8pxW;j~S{M3Bem`oR z;@wQEb&2tuKCMHd>om(pt&42o_-dWu3y4GcRqHm%o=3D=r?{8tb`Pb7ITFsFTF3e( zhZkNa!yjdNs&#r99In<~9${Loll+mo-y`pz!0FGrLDCN}|7u;{XCZHR>FY5YJ%IbI z;Wdb9rJKQ@m7Z#&m)q#?BC<99D#+JL6Je$I*q%@GvDWZn8$Z3a^uB7NX&%se|7_@k zmA=Xr|11Kn;s0TKK2^)s@CCN?du-`1x6z-qJ#Vj#-foNki0%2Pjw7D`h%NqaZ1Nv# zMQ2rUC9g=7Cy2#l9%-k#gI`E zZdpZpP8ZeJZa_g=+}LaeMR2qNPp}1{=+@f2it6hdt8IQlv?41D>uIZ|NER7HwXiT$ zUoZB1DnI!KP#~j2@Di3a*2D-9Wmvqlm^Vdv((8-zw7iO_Fe%6%pkh@dl_?v8UV=YHq3Js=%mN6K<#xZ&Uk-1S-~S2yI@&Y=8(i zC_?dsr0>MNjrWy?@2}PBDe7OOw+UW$POM?fJ=Z=cr>&d@UNXgtNKg}a{kMfIfl8dx z)Be2D>QF;9Hbh6(!nXSC;nF4sSue&X9I_^R?4-dGoZV>GI}kPt)bi(VnKu z>(`#93&+E%r<*RXPkWj!e3wbZw79agr*?P?@>E3A<;~ZgrpueBJx!N4S9_W+Z;tjf zU0%QTG+o|o?PGJxur|I%$YfsbV^=VJj<@IV$iz`ceYKONVM`uX8y!qN`y1aSX({y=rwWsOw=4emT z<@IY%)8)<9o~Fy|)1Icw>(!nXSC;mKqjf`*U**MHpq-}6o3A}hmp4y)nl5jy_B37I z9PMekyngLzy1d!i({y=#+S7D-z1q{_%F>>2AY0jHwzRWAyS(|@X}Y|5+S7F5JK8EG z)8)<4o~Fy|*Pf=!o2@-fm)EB~O_$fJJuR**?Fk36B7lC>L%0W z&DEZ!%bTM;O_$fNJx!N4TYH)=uTOiLF0WU6nl2m>u2{mUnaT+Va_Cj0w?I2hmp5N~ znl5~&N8Mz)yt&%bba`{Mr|I(gwWsOwW@}H=<@IS#)8+MQPt%33YAKey7N^KzmQla7 zY>79&f*pC}5Mw(u&)R9a@I^lrZo2RlS>=1Na`_(uPKinj4$vR?lseiQ)HO-}>dBV4@Z#4*G%l#Spe%4;;9r;6LAL01>|CPuc|O z?&CO$h8c#I9@6qVaaB~b)^6rGR`RH*C|k6;Oomy1kHH>K7o`VNj9Bz9xQ#Q~h*Vca zMKwn3G^mi5Jw26Zv@IS;uW>_DsJgXcQv*K2QBzS7B-5I{`k60US4dYc z(j({Q%cYFqj7XB6n}&ZW78w0$VGu4dqhbomD7-?fixxLgyi=h4K-$lA7761HTK6h0 zn7n8irbur(@c*;-{_$0w_5J^SPC|0Rj}RdID5Y?qX`8fALq&~@+nxZep*^K(NL#FA zoREYjB_tsUG_-U*ZPCS+eGH0|DYK`JZK0zZo9Rp@YP3w#jXA8$vB`HHD*6eEThv$+ zh4Oj6@9TY^+~*`A)pdV-_wafo=XJlY_jSFm_x0nxuIs*j66CLKuB7?C`No63BZK$) zk6eB{?a1XbU-);rA5ny?V+CT3PY^w}d<^S@_ z;^F)K*2{hVwLOa_Sv&qQ(<<+Mvv@dbl6BvbC!YBDg|xtnldaJ3X5XwI#c^*1t^KQm z);9|#SwAVha;2O!$$c65v){bZ)p=uRcoFWN!v5Slt^KcGZ|#q#&3=)H9r*sN;sSa0 zSy84lqGf6PXYen)bF*-pX53=`8pYx@v41@?2|9j=i+@GiUU+nYm?O|WQ{yu+t zkLc<9$rCbMdEgvz?tRlY{Bx3A+~iLUJU8p9MMHf*ylc4DXT3~T=LXT;@sA(>V%dsd z9q2;_d*_D5T{}yt;%T#9JeQXHqUF2&+evA~ax3A9pUaQ0A$2BjmyU-n7l% zMyLBkCm&%ra*@}0`O3@xh2GILt84JyFOf&SBbV1$`z3$GO=L@G*ukxej~KMhTa6Ka z#!HcAwcn-qn3tdVQe?fMMX$?`7o*cxJCTZ{37;*`#5Ff8426}qLh)J zqeGDN_uXLfAU8at;2in$`t)eo<;Pd!ra7VyXupnIN7V0^^&X0*x~yK-y(e9 z%~BqUwG2f4)^HM?kwQv>hbC(^2d7zB!08Pq2UdgR@c$bhI*c-Y){Fu%A*;f z%h}eA3#YA`n%#b5sNmUKR~KANn|kwiCt1sTDc@fP2=7%Tau@H7-MY9tmc1l>YIgD77g*x1Zj2o z(+@p~-{PF79$Ms|w(a^$SIU?8*FW~XUtK8|I>_@9>)ywzp#Ot1Chwd3uEl*;mz1${ z)1$Ifu%n7?7ydMp&oM1CpIdJYUz{H5Ir>@4Nt?TVam9)ASY=z%BsAHiiKIo2ri-MB zq{jedO47!gCcT%hl<&xvI8Srp?50B{>9E+Eylrv$Qx7e-W=yG^94c6B1@2zF$9L^& z(xEurf9)31VGZfG$JQ{f5wQvgnunHtIJmML+Us4E^3pnd<%7m2x-Dnn<%8%1+J$9S_kUUtmhWH+f1yesRGq(Cd6zQwn@ju*=%^ zl+P;93Wf>-Ph?$t^U_ekwkIZE8}Lop_7&vk`zCLD3Jm0idJ6c*{oQfe5P4VsM`csK z7e!gR=ZnsN^~Gn)j=cC{bJi0DpT7C>liw=19QjrOaj9c!avlE#@}HG;x5RM~^#Xe} z%hy@xJIR_M{6$&T4v9lPcGT-L^05>BFF*d}BbRBX7C}??I|9DYaF#FB^D1plmTzrO z7WUTV;^E7Wf2C90$DgrollD&Pcqx~zy(`BneU2VZ+FeZ# zubi>;PE9Wfua@{tls;a$;@%fBZK79~v1GSP+Jz=gf7dVh-cdk)<)c?0e)BKfH5|M! znf5F|yF9uq1Skt$-dJsz^M4Wz4YCF+IJJC-&(MdZ|>$iHu%~-O%eFU%GQZ8n7`1haFzLU4FlKPI! za&#Z*%LxBUn*W;@u20)Ov^kyr$RzqD8T4f`>FfITyM5a}f2gNl-kSxn^?_h;VIb$& zOv`uELhJ8?3-9sMZhd9R!hYUGYnnA9o@QP4kzb2x6Xm_6Kb}ax%a6Yw`F9e|9Y=;X zuRU_%`Ze1LU)<$Ndn#!b&0aqw{vu=b$=(;WqyDHZadi5S^4@1!D|Y@eM7oXib)5c> z-&!%3_*yx)E}R#CzHI2fI-9d{mM#p;%U>AxU%#+3!@AGsyMCcHZ}CFkb)D-aOkWyt z3M^h|Y!`d%lT=CET(e-a$DIeUxH^D4x_~*A+Juj0kl0Nx_ zSNreeBU{ue{b}?SOkcJa{yF-jEl%V>U@dAB5A0%uGbJ>{g4 ziDQg3lK4s)cKe@KUUACLm}zCw#q^=YowuB)`ghO1eYkS__R1h9;xEvzTGBv_QihP4{1U?-l|J1Q@GVU^9lseR zzfk$1@xoWWhCj-G_XP6$m0vWD{y`cfF?j|?$zQ8{$v@1f_(zqm`iF6x%SuLc)`A&?5SewD$B98mlZpoj3!fQ<1hJEhpCcnlPOM-5g))xHfB|FximTW!!T z1tr`NC~_|`AhZ;`2vU}ob%D$Hll>vWZwDn@Sx+n2rdSJ#{E*@*kW3C28MM1Tkx41tB-T)o~g@4dsMFbT28^PsZ81x~p#GqXS-iUn;D00q||KdMxutN58iTysc zZ&&*!wJ%kBS;sBQocq0fWeCMpvXT1E&)#}zfbvH%HON@QMH%z zRm5Ke%md4nU#k42$`69q!_QTIj`HO^7V&=q6n%OPR&=U;RPFbuy`1kL=~xI#I_86t zj(G;{Tu{=npUjecKBL&DxDOQndlmO6R)g=vz62D1xoV%TI7lGk@1o*)#nXy%9*6Kx zD9U>mggywq18fB;D$7Dj(`S?Y>dX3hzit6#neD25b+ik4y=&yIh^m||2hq8L&PDq4zh^x-k1S23m-Rg5Tx6oZO@<1gUP z%V!UBpfeEgU&@Y75BPhhpP0@*;G6?F2XokcoO3$o3>clUcSidR_6g58IpY*KFk^7W z5Ez?zcxLC!^t6*RPt80%GvIHZwQp8>T71^|S^cvz{e8J7bJ>eL`_Syz?0`QyXYU;L zDbG31@A-NC^U~9fUUTf4u4~fMj?eF%e_}p+vagl%vc&w5^!4u3mHZLfZ4Vj8boP0= za{wU8v=aDaBfh~;lqxN<#890$?%n>#*HpgSe$OFXk?*z#=aqKb$5TqX?cuXZHw7H=*0U<# zZLhzf;k)hMA82@m>i&1tz1u(F%tt4Dw?A-;x_8^R9(C`whfgZsZQm}af499_qx!k+ z>*tg&^`DsQ`Nt|@J`3Tccc#$!$n)~`{Y&_l?MYs~>;v%9b_#tm#r>b9$ZtrYf0d%o z@)TO;c)a0Nrucs}CHxyw=&z@^|6mGDU6drhA;rIh?G4|vj@MZ}q>G@O2{f||NtXy& zn|E*#g}d-pRsymRX?taHb93cxceSds7%<6NSBl+ZX5?IV?kY4^R+%so3vyYPrfwap zq;*&+JQe*wU3E>PtjtOkj&m>1nf7X~@miVnQZgNuWba)si;&Xhy6u(CyH%}}1yhL_ zB(uG#s6Hgck{!9JFq-3}W3ppMV)D9Ib&#~QH95UUu~65^o-#h3M#sd%QaK)*)U5EZ z#4(appEayTGfO<18XtAn9sU}N5LHZR?e3Pks>*t^KruCRT_MLuV2yFWs@Ls}yG9oC zsg$JEgRX*RO&lu%>t#8gXzymlND)?PJcXziUNN&@qw8 zyLQ%Zt87S`Rv&X-eI9j^733cy^XlzM6^>(__>q0ZVv58M#--^#GE6k`A@)}1)d%!D zSmZe(XE^6riJ&Or5=Q318(L&Ez?jr|byp7F9h0x^k&>feqIr6sLE%#~QI` zjFGu)>pPQ&Bg&I;V2iOw=HVS)EZti17jy?oQv_-WS5d{gC0crT<`5+C4ML}9WV3z27b}6;}<-$t|oW>E3{RXdGVcbw? zvj!s07`~zBz9_nc-n{az`Tsb(XMlOj`Cp&i^Pixrvy_8bAD`V5=z6-$N(($E?q7pX zIdtZotbaW|QHzvPcI_mD<9`DIPai*I<+D~fLY=!ToFPXFp2#*Y{F#6jw|qvHg<@#)HOCBxv8eP zwT>?-SCu>2Q^tLQt&PFv8df3J?5aWTZzzFHpUu0u>Ci+Y$SO$_Y_N(*wyq4`FjlbR zd1^84@7{cON!jL>oy}V-t7@zV?%sS4|Jy688e6=UEWl)wYHMYE!g9mTs;Zin7OOb8 zm7Soy`rwO8KNF#1_)MG1#)5@2Mac zCOY!j{mLd$6sm1(;ljw8?Tw@<4jQXQ6qAjBj?&dNyXuJCRb9yj(>nG8K4$zkZVgs8 z1RJ+JQd1=fu(Q6}DTiBXMpDDkrB0GY_76JVmT9!Bh(cwH>~$?CE$SN|mJc{d+$Q~) z3#v+zep_m48gN4&TsN-;C2&_;RY4pk;^S-_=0?lvV14Co-VnX_vdL@-mT;0Zu9x>n zGH=A+NTE}|i90isoNZ-0>(1sHH-HgG60H$wyK0){y%D|53p2_?aLaBd+p3%Ea4Z@U zZX*Sy`O&%-$#(TCQ$EqsZVliAxV8+L2z<8G_tw{>?@O|Z6-SFf_3I`0JRu$LpUK->^(IA`mf?R7)$))HxyVynol{`f@Vamm)=u%ZnzCTKt zZ><&WH3Dw1BS{o|w63+*1n;Wn6twEPmL^Js6ON>KrAPflwTKeND?dlxw~;^t0QhCJ!OG(cq<61KT#3grFK)D%nd-+0sY=P9BqX z4a>A-6ql|eS((eOW8bzE8dHRw3SM5h8fQ!M9c5+rmaPolzy9v^_dd8j_`v<)yNlNc z9os~mY=%A(S?H7l5tu03rUo-jhg*@4=SkvPM`AchqtPV}PH~fbuBxw->I?0IEmVoH z-XFg6!OiRM-7FFEn6RI5;gQev1V`+}$TuZMDMQF_wc7O3cwAZ*;k3XdRA>8l@Kg$0_zUSY?ypPN`$b8gM@J7oDvsOjW z-V2KTCdG{)--2P89{}wYpv))bf_zKY=NPQuo6_-j7Q7MnXAIiC;Ja~u5EOrVK=IdR zu%a6L9qfxh30KbX7k6^rh`8%FSaAXremf|9IbUD+P0BA-{#xZ1Dqqee!Ci%%zn_O* zay~m~=YZEkPc>+#gG-<##Xx(A#9s_e-&fGS2nM0)dkeBY$eC9RpEYQo2AN+BpE79o zf!9HwFlZkK7eIF#w2y&I35Aauv=4&}U4&x>?Smj=QQ-py?R_9+GTd&^j)L?-!+Q+c ztss5naFap17Njp9t~O|I0{Ny0%lr>$%eswO&|!o2T5u+G$e>*WGL{rxVbGom=0FDw z+Hp!g?@#!wL0jgV7#a)54B9de%8*aE-Jrb}WZWejHE3@F-%CE0f|8H&-bp^K1>b{x zA;>%A%#+$+I<(-R&){ir3i3`FwEMux&@w*?+A=@Nw{p1Kpec#hFcBV)u6l=5s;{dHyN}`!4=rA1y_Q^Me31okwH5MN_s9ZSP=)| zXY?w@6r+j}#gJl95petk{0oAa{-RZx{!Nige`_0OS^K(uoDuCi!|$MPh~Gp0n4g(X ze?Pyc(oS<4_KCDUe!J6-r!j+?ek%QRdZxd9(mu|{j!xRkZ*0vUD}RoT2j?q2 zQ~7c(lf>t5d5OjBQhLAAf1>mwO3VDQxR>(~#avc;s?xV<_}|s>>u;*Qvc5&ke=5C2 z=_%^}eMW#J{Q;$arSxq|&s2HmHM~6<-yaK~e>eT_Q1`cK z`YcrXVWoSOcFWtxg{DGr$Je=w(3$V9#$L=2|1$sXnk?nJ{ha%h9-^`mQ^UWwcl#j{ z4WZqB$5Tp22vZE>iw@uI_drU$kud+7LLY(Y@_ zd2((ae+ko{;$G*j5>s35wBB0h+f+|;a(cviWX9;puD9Strz_nd4R?%0KJlHYGsaKK zOi#Dkb(0wO7-Qb}DsJ7$VMnZYQkGqs&Y?in^$ZhmYL7aoYc*YPwJ5J?<}?;Q++5r0 z`j*|>m5G+dy3F~?cMp@PuCL-VnW@T=Sx9q+lIhgz7f4FFjp;&?$%LSZ&F69Q=^--q z6lO4?-Z(QuQR_Fj`*S@9Z6r?8NIZBBtw@|ok$CVN0FgN1A@MMB!h+{mg~YiDiHDJM z3p|Gvc#kD8o1sU}0q|`8A8(ug$nJX2miqBF&yVa`_iR)jZy)-|_HXZ=Z#N~`=FRjh z8=u+IecQ-J?vee|o=ww*yi?=O+ukGjrSw!_{-vhM3fO z+YXhJqdsbhh*qtRjZQZ!SNy9y>F{Av<;7HK){eW`NBmvCwPPCi zv$S;PywYC$1#@54Ghbchd*+o@{%20CI`>@KOM!PVKXy0k5T5zUs&miyU$QbQUW)%d z*W>-H*N#t*+JPSx5BFa$eBb_fm4Cm#)|An~56&w1#RhBmXN%ZB&b}roBb2cfLE`6Y zHEx{s1ivaCM(<(kSH9snh3sp)C3M*erCkbqXHCzA4~BYv_*|$bE0$R>oA~$tMX2Y( z=a{R@^A*H5<1S!b?*B*VdCZww?PVpbkGR18GWL_+9N1&^OfH>N5U`>j3j{NcFb_X` z4*gz7m+!IOL-gsDdAwQ9KBJ_)R~GiQH?of~oi!X^XD^|%@5t=aqV7n{|I*$h_8kp= z(2{O(VhsrE8(!;t=7U3gGB|q@Wnbd$zQB)U{$0u+`L+M@<7>&oHRR=L^0fHKrN+NE zbA|9lKRL6Y|E~!DvaDY)cKx4cFQw&ZMy7pSLd z2_v5~8^n$K&fq7JK3+TbS>7S9u8d!Gv>*prJ^kqV;<%tg~W#k`k`CpFY0(+u5skA@lk9Sx;C!d+=9a zeYgz2tntX&7UPVCIuHE9>~ zPqpB?oPEc+zV})^5ut6Xhc(@~zGACq6PS>Hht;!D?AKX64|whGv3h9ea((}n)>bBmCV~Po9^4in8gYKV#w|#Wh`1xpO#opCh;^1{t;tbP{#Otl_rERBt zzvZ(4yy3MIULcRQ8yTs3N!b4#*|o^_wNIi)ogAOB$^`YCvmWO5bZdExwqP?D0gKay z-m`@NC!vG%Tb~Ex*sn<+dQTqzPg=fhKcfD7-tuqz3y@7D$|!LtuT;NLg$r zU-D%=<23eH)7H2=(eK=pk+w97yP&Rnblo_48DL!&?VxKX{=PSBVt?bsx1Tm%;w$ly z_{Ko!9os1Do2fsSfxf^rCoZ&kNpW$@k5`uf`t`q=te=!|;yc3|KlDy|M}OTsPIWJR z)h8)`Yr*G1Z}}=2qdcejpUC}K@1K;dG1tbir`~-hua@os&I<71);hd+;WVjRddEK} zz)fTC`{CM28Sp+&6qjFz-#)>+>Fv{a<@V#x>F4qexaCI9hf3AoJu}MfZ`IDHUrPB& z?OS1*RWl`o3eW zb6xH9y^x=@CtuzLQ(uqN6XVJ7=Cku&(f*zDw0~pCvI5EBT2oW#oD@1Qh0aZ(=cUlW z6goeJUXVhkC)3_IS=1jcEoH@hmNITlP3Bpg3*_>}t;KmiUfO+5eLuYR7Uwazw8-%0 zy~%^nFyAmeI;+i$RTbW#o`kJI;+`f!oYw=Lw4mUFp&i&RBJyykiz|aqBrZ4x%GvnRdp$b8Frqy zQO}w)n7nS=B`tC$PK&Yp_DNmESc@;><9qV0o!2w29L8vT)o+6w#$bH4Z-X4hczpG5 zgB->beNAtJ9LB7Ct#5-I#=3lMZ-X4h$b5U=204ta`J!)Kj<@WVNZIw&L8?3a-DBOTz1|(CWQw@4Rb$Y1soCt#=iz zx_!<2?@n8PU%9n#$&EMPdfU5KzHhaaJvW$_jalZNildA7^hBIC4#i2~)N(UQu(0ih z!lI31vdl5F&XP{~~5Q`C;Wtxx*-B2>Er_HBgqtjN&t{=9qaPgPWt$Z?D=M z#no8pTd3+IkQNFA@!HkmMsfwL&*rWM!&$AfDlEjSmulzj29~Hlo$`6jizc>Nk zT^Cb0j{jA3!o)CEC1zCk%mp}RJpXbYjhONDKd$@%<7`y?kIDoaCg%K(PMl>fPY5$OULn_t`oww@W(2@ z^~xVF{C&zFPk#3V{?901>yc6Eb2`T;o@?rvQT%=um5Ld!{JZx*9yQKJ`9C$&F!BtH zDxZVOA5VTElLlf+42+WBHUYnP0)8OZ2p&&<=>+_P6Y%2`@bhP1J^ZE#_}$768Zo2N z_sj(RA?3Fk|D)2kevV;`C%+wjIqO>YfRyoNO(5g>_2Lh#s0G`w-vo-gRfQ_YQIYDbJada?KvCLk#iE1c=Q>pII8x0)jq2B z)oQ;=?TgetsP>lHUz%z39{|hI^R%Js<-IE-UME4$gjseJ6n+fkRSzFBXdh7fdc_Ec z%Hd50?G<1d`W70j$OT2tAoYUS4;ZXC4Q?0tU_B`J>WaI15HI0cgZ4(XUkf(F&jE=_ zc&b7BJoS;tjT@}!1s}s*7x+Q26Wk+m!6(2{@Nw_}i`yl<4p73|XVBgQ?#4a>5=3~D zLAwML_X|Kogy$Kw`>8J_{PPAYPJ`l3&W(5oJg$7HcX3zI1y*B!40OU*zSP^oZ&&+0 zU>)w7lrQzZ@M}SlTMF(1OF)s6Yxwm!1}kK-KE2IlmuPQi6z{A(9suU9LYJ)PyC-$ez)@FJiiEhId@9fwC`?Y&?t_SMSY2qGd}ZqN>^{Ym;oBCpS2 zMK>t&j)E0f~r2bA9qR>9w+{5IuRgFCQ~fFdWPeCgkb|01|x z2b6z_{$vCE0Z_u1{-yZu2Sx5#a3^?5`O-fXzMQA!=&$yjYJX7e+tj{Q?Ki1?IY^R( zOAXo~a4+(gD$WB%Zo1NPj+p4xWw7EPDEtl(C*get?R^<$pNHJ*jFm0>n^^Z3mi}%pLEc=@Hv-|1qi~REjD^7#rE(Y=!K4j3Y2cN`#6PPA)4cZ}4{I4=t5d_8m zRPZS51~1MUm^BC< z%8lh7=4{5}xxKk3z@gkrxzX9~S&f|YIQ!)6Q?uF8Gv^GaHSU|+F*iM}d+zbMy>r=v zG_RYp8_&**&pQtux+ZqbVa{+odCjS7PJ@Hj3|(^xJT|{;em7@2#^;}(kKAhyT^qX= z8P^`awii5e?b&PNV8?<33l1(ouLT1O2El#TbzFDgI^11%{JLK7{B`};UA&H6NDB`w zWT(=?IKTTAbu3Cxi!VOExPLL!l8zli47{TM5vASq{FKntb?&-n zIoDG5=eXl?cagS2yW?nb?yk^Fu@_TJqbRgH4)+6fZ@5lh*x|e5gg2}Fsp@|naTWjW zINWE+JE7fi$+ha<9f$mVc*3t&|FS~b6heNyL5Z_XHVLDA>iiy9t zcgG?88lU6JpQ`cQqx2_LUw7Q$-N+FC7nOfr^>@d`KBD2(5{?*Ym&N_yOhbQ9^>fEj zzJ;~$rQa*&0p&|STFm=YzI;cDY18<--@)&uY>0p9zl-To|I*JE^B8$8eD}M4SoLwo z$)x`#e0SXCQ|i8#k03FBtF-&we^JA8#~~h7+8sCihSKghgtW6F-yL_@qT##aG_V}L zJ1+Kljj!}G#RSy9JMOYXX?L7tljfK755+vB=`a0UF`L!BJ1+CE%0I6BUFu)@onpSE z`nlsoeiRh_-Ep(o>R-P5#mv(D7*e`cX?L7=Si^V6b!DHW$g9`z{*3gJG;qxx?7g(S zH(vSy^!Cy(p|6+z1cJPDcZ&S!q>q>XM-X26^C|vUrSQL!qR)>~3n__zC?&j~rRevA6#uFF1J|d>+m{lbvXu0a{i$C4W~K07N{P?Ek>6hT z&!)JS{B( z_QY<*6gD%64OLCMEe>1S?%>EKN#k-I$EVb7#lZ-JtJ#f;Yisb{xZ5~P(JzH3%BiR^ zou}}``G(rYN5^#QWr#!P()BT2CUL~+Ju>6T1tep-PGyP*@1D$*7$&pCF?&F{!*p!n zDLipr!ZFBWDl&>;lKVjpw7Nr%rfMl?rc{o;NiGuO@trNNCZIL4D^<42qNxgX&dVub zqS?@#;oN%1_NLa|PDm*sq%uvCZmV%l-fe5;2K#2zOYwB4ocOzC=hm$?&E?IN4J~qX zbHb5FHz%{)g0;1hGqa4&qMPms9*JQ}7+!`bRoYlzZ;~p-p_d^J6R(jRNgSqZNKj+H zXro+sbWa?Hosc*BEbL^K1fF;uM?1bctGwRTvdqk-YX%fMQ|>LgVO1mIQBw~oHi?|j zCt*8wyc6ssl@qoj^+NJd*Cz8u9cP`0SQ2N;V>Qi6n}{XQ{HBd#jD!3xvF;CW;yT7o80#_a(304`nsxZDUs58 zZcXCA?W91RS700-Wn>(VTL#9K>DFrFc$QK`qR7 zYnAJg9k%f=FZ~8{iL$mlEf2^+Ty9O%*s!$@4-Cf^zb+kk0w!QFF6#@srpk>U|ddlUF)cm))VD_ zET#$Sfw~swV7zgvP#Qr_EtKQxM&+E7vE+G`bHaO4tdm)(DP{s5!;PojTU#jy#<{04 zq)MPaL$I9dS1o#Z-s*>URyK3mLX!LBa+b~1?BDsG&77%V9PqHFblvjX#KxQn;XQ8Qokeoq zg7IP=kjaDbQb-3HGwyi{E5<%=Va4*o#CZ!gISB4JjBxG*h%t@hDo2%+8 zTUr|E8>XJ$5S(G0u2vu2@`33w5_hk0<++ywY~H-KxV(78h0K$SIi=$zGOj4`jqsuV zGl!G>qw+ZPvC?6r$}3u6+{e`0sU=3<5R*!|YUc%&$8ol`5?v~ka*nqhadAfFy_<1S zF-_MSbbrT*ugH^wMn}oZ){MPbGvUu}btM@bM_x+}XO(Vkk^D_gzoMnaU#FBc%&FT< zd=COR>JW~wAGLAWjycCln z&pCg#s&(_uhDZ6l-Ml%x*6Ti?@}gRvW2#>e{d7n?smM^=ov8@poYYM@FC~hge;L|t1XGHt<7T2C?_N2ExSjp~kvH!ejxuzP z-vdv+IeGZWj48vlH-3Qow{QP;1Ukr`%i5biFkE??+QE6{HQi8CTHhzs{EKOW$` z5C8tz$bA`ko!RVz<-CeH(7n)Zc!{rEoWD(IfBmni3|G`#^*cxwNxjG_b<>v z!kMSx%~71KmWdkSlp?5m7&XUjS6_7MA#^KwS+`EdP89=dly$1|)I zBldH#H*)_wGDGCS+buKZb83zQQuh5;X!v!?j>Ol^7m2f^fs`%t9$EW^XOU)ua#jb= zb9p|OX6`u=TGH1|FW&hV<8v)X9(%r>yY5}tuWOnJEqQSb_}(;&`>E$S_o%#{c8#1# z^xAn)^blE5%F?emt4PB8#dOYe!QKr^%9_ZUk1Qi&{)@kyVhzjv1>(*dub*XFGUV)( zSK&#%ieD*{=FW=QFS_B1y~I~g%8;9vuAgfXevF zxL=E$$DKS7zmf(cVM^Mh>gnZ69dM4j$o*-tGD%ZcW~!h0gz*qzoF`7=?rM6A3`u*h z?qligrj?t1BKK!=C5_~roK4!z*Ro{7l>9I}^Ip5*oSRM=Ro%UHnj;TCQU`vUbnfL} z{D>Rpjv|SNld>GgjXaBv0nTjd&ou3!H~xfc^rgLW)4`3?>uWeqjPjG`DL<)hT=|j) zZhJSDo7X4b?Z}rpNBGhnrR%f!ouv78`o#~G{hTucW1NX~jx%^xKWFp{z|Z=J50y<` z94-6dBL!_)-8={N`D-64d*lXvKK`Mye<(1|M>3*iM}F7Z&3Bx0PF8G-|58?Fv@9zl z)_i28)l(OUmc2Te=b2VdK^FG%e5cj(D$hr+x1+o7dYj;5|cr*6I^~g7f%k|(7a374mb|f$M(<6&} ze_EFp|LKJ#bB7DW&Bv_W>++&y7lP>UhMWoJ#@)JHKpL&P0sDWjdOpJQkWb=Y0F8QBe1&pO;?rSrUn{17?6^zFWo z4{k*sd0J2)yeZx5cs`OrSl|W6E-R5Y9}!+Q@e*7~yngthve=SmSJBSo1}0hi7x~hE zVo@*uFeC8XU3G(Pk6gL(p1bVO$9|YG`MHw1g0^4qtrE2Ko%kttAqIGF&&w6&a^}HE zTNGb%&8WK(<=u`i`9RWH4)Ggtmuh$O7K7}Oul!% zao4c4A?^B|8$ds=E^d7t!`)ZN9|_~jv=^P!b0y@N+b|cC8`v5jtrM{4_*tgoZ#<$jYhwo0GTz6AaQ_sy`mOK_WamQ{|rA!2`t*Nhh zm{}QX{f4n^?{#j`(r`tAeEnCe-w0$(i0_2o$dK-daj)M<0G=d#`k%iw|4ULp@-y*r zj1ed5UW>Nqf4vzm4J3VZis65K8vHhbzfFPProeAg;Qx;)Fkb!rcJ{ljvOME|Jjve9 zcKGe+2mX&o_`ewd3lN-pI$4jkrID3a&H}CtjXRsGYSx&$M^{?ajqACmleGXVt({Fw zyVqFTx$$&0S1z-tjB8nI?`&*lm6_vdW#iUz)<>+go2Ei0{-n%3f~*1BEJcLJ+~>#BBRiL;efUCSLic2?G}w44r(% z${TUt%EBd7Yp!W&YHX;+ZbT*SCMIoQ)pNsdja9i*7N?tM!&)P^I9rc3Hq=;L@LFqe zX>LukiDY$6OI34Sle2oZn$_D6vz|X;;W#e8Z)us=NBQ;8UT@{0CF?hsHJnS9mMvWa zGeu72$d{Q}qnNmAX{F`it(4oD?k@|I7OA#(a&7Y1wqrOhui4%-%7Y2`UI~G0oAabR)?Ya6S!-BZ)BGf4^x z*4OK;U(OQmWNvj`by>~BEK{jzcHX`1jjc7jqui)%zPVc@Va(NG<&E1=At@8_Ef3f8MwvJ;lLH?OU$F6LhLrdEpI&g#4CS~t|Py+a~! z54Df1i%RxWO$8()Fh&q6;7J9Pyc%1)%a)rYP4F|yP8{CfP&J;0YRbU&8Yf#vNgtOd z9w}VhF+aA)ae0ntvDXLx+UhOx!q9J%zf{rm9l?_w>vq_6Rz&9s;b{9 zcdCzmm!x@eTJ*J)gOT*OzaepLd6G&=&XKPvi<0i|)v37eschLs-RjgFE3Jl|(q79;u)0wi=ju9o7L5&+ z&CZ+cb~$LnDbp=0tE3f{z6Rdu^~n3Sw#M22)I!^BYF5_*S%ziSaf@E}Zovaf##hoW zA+dRnWs&L1MrR~bItJTWP@ZsfHJ-ZFIMVT;o3S>bx@9C1b+oRj-mr6v=@2CPM|3@$ zRhkkNS*qJ(Wt>pk>Gw0)_rUe{W)-KpuM)m>TXB+*Jzf8;H9dSwK(-D#3W zq@n}W!%C~Mxtd&Sre37%If+-ig(lc(I>j$L%_>O{2}n{((1;|#>Z)q!S4mT6-e>u} zm+RA;J;j6)PeCENBP!jaWyQ&GYo}8g6xY`$d*Wp=H%3Yt*3mvwojZ{faoX;hu(ca` z56&uEcQ#YaC3=ewH8!GcWiwmm9 zH?QCD5M4<7iDZwVC2sF@i3B>*7jy-TD7(vOB)X!}>LlvE6>jw4q2KBih3HcxI^!NC zEwR@G(5ShBOw}PgnWWm6 z=wH>*TNIV3heV*8&!f~2Em^nTblWBjcMZL>W^qM{8b6}m-VcsLx3Ah2x_zVwyw6jp z#!$wSqa&#%Uapq5DM4P%@tV7x?v~rta^gIav)*2~6zSiZ-==^!1>DxyYN*>{)z@v= zwX=R(Wy7t@Z>`=9Wv!7dQ7eP;c~(;$Y;6qE9i-NqvsWxP9It49sd!bX> z9kR%hinT_*A6tqWs!JN{tM6!#Pp@k0owvU8*7pRRV1ny6ygwYgga3P}pIRzM2hNB^ za~&f#L3#+z=uoh}ayN~Au&$wrUo+zKYk0fAp-#Tfg7S%Ct+~^TAg!%yX+o?F6eaAE zh&$T&fNXVkFgkO~K~Jk;Ih~K-4IyiT_Ae~?f)I;Z^W^j|S}Z{1WgZsBP{7vIEiff6nq~z!(S~H zA0M|$Zq?jcvaXz-=$6K|(J3T@W5ERf)~(Xpf3&fAo3*Wc-3FR)x+tx?HMQspg`HHF znw?9{HmqrE+O50(;W_zY9C*g=#2q_ZJvOdXD|`=(ySG%!$L=_q;W=1bx-Q6-+glph z_U?|^_anvmTP7oK+2wEM@HdqooH)4B ziJcf1A&BjPZ%HcX^sE*0c(*jw~i?t{bjCuk( zw8Dt+JeK?c<%h@NuhI(P1bp6&QSoy(Pv#LmqDJxaSkNqH6aQm{A60%K%(46*(+YL0 zJ%J)u&d+|#vclpHw2y)@ALrJB2f+@Iv&ok213v@q1wRW$!QJ@VW6*8`IfpykYS69* z#a#p}z<#6B7?#YqtNb}zUS|3?*LUGA=re!r3USw+Ajbbv7cwqo~rf%umO8Hw-2<3sNkECGYD?Se!!r89^8R_ z9IVG)&M^mVIe+mn?EApo;2y9Hew#r%A6ySD=N5wYRPY|?fI<5J1wYJRhe3OY2K;X5 zL4)>1@IL5%gLWJ&g+6Q0J_z0geZZhC=Y2nq9;-mfuNB}n_=N`Te9$SU1}lQ#Bk&g( zv~xl6mjg<^P zA6Sk334?YoSOtCDpxp(EyQAP1>^lwGhrvo{IX4xwJ(u&%L0ir<7yDjCSrGpL_+19=V`_gC+=#uL zX9?Pe!3UvZ2JL;Iyua-REB1mSSI&)m0C#N$Z8;bHe&{BHwwyOv4qa=|j(}31Y%*9O z=cbFCFnAsAN(|a-)qWMY0Q(|?_6l$VbfG~zAC&rN0VwswJcAXNXosX8xd@7Vzrl() zDEW6x?T@OxoWK6Z*tdg{uT3C(y_eO4C8EDUI|52Rm4hO;)L_K|@RQif2aUw<5{(mB zaS@dGodxei&KZODDYcjW+#T4TFlfuUxRTCY1}lz&pTK_%6uEK^rO4d}iriXo4gRYQ z+8e<=*q4K=u`e}fhe2^4Qd|KR!!I;wF9k!;`3CJg@OJ2+L3;rx{--LYgL&|!5f=La z+H=lX2wya4_k%X{d4o3lj~#cX6#GE&e?sXla2511gZ5GIcc42B+J`|2N6t}Mi9P$} z1nmy+z0ms%+U;Nw^j?E@6cm3=inZW-;8z=T&eNCl-Utm=l&ZTh$mm3@c zpuGwdxhueHu`e`eFID>mik9Lai<(y8Zor@|=h?m!y5FE32SuKo-+CMNa$Y}Zp9I;T z5biT*p8&;Om*P=S(y>$N7&r%7&i4oH4sbd2K7)2USO~q>pdAH8UMu(x?3)Za_X3E$ z+zW6k_Hr)(XqSV_pi2$fVNl%3JpoIxFEVJa0B?aVG-&68;%)(WGxqZg+Bu-aZ>sVy zr5ksHpu}sypnXy8<6r^yXARnN&aucnsn`cf{7)FPyTE+tV+QS`ptwH_-h_S3pnVXO za&W+4#XeBn?N!_ZN{l4H zm#Td}xCHw=gZ2V&G4woxb}lICl>>^LOKFxBgk~?gpgjPJeZS%vQ1m{fbT=sJ(`C>; z2J#&cK5EeJ1jT<0T!{T4gZ2SX(x<~<#a>X{?E$6UZZl}Ns(n2u^>(d6I|7Oxn+)0; z!THeT2JI41yMKxd!d2YJbUZ_=BMM>sR_LI1l=a zL0j&@6FDb9k<)9??pFIw#TY1Z4k+CYvWF+U*Py*e?VCW6Bj<&KwwyCA^2-&&pvVas zwB;Og;jaL5u`e`eF9rFg59b@S^T3(VL4)=JkngJSJcD*FNY_7{W6%zOA~zkRD;>5B z+Gl;nz8jRTVV6PsD3}f1Y0##R?c_6k9j87#0E#@)-lAy@?^QYq(&Y@()(G0PnT}lA zMnSs?q)QjBH)z*_A}0dU#R+dRXm12XZn;6b1eA7et-*>QDE;&tQ2Y;A&Uat2Vz%PIs!M_7b!5&chi``%m_?*e8r&RJjEQvfTE>1gz_SPKvDW>LQ6kQ z@QmUq(1Pv*ub^Kq$o&o3-QYCv7&sN|1gC&8P~1tsZZdR-Vmp`x9R)>R8yEnaz)Y|f z%m5?cByb~`4wiyxU!EJ;x3@HrSuT}r_0#)gHAqy zzk-(jq0rJl{3R&;LZOd=!=UsBg+2&MzP2kJ1%CnEq;xI#2DJ1G#eFIGb1>di5|sYe55XevAHhQK0vH5e1?PeNU_faL{J+ox6!IT{{owaO=~tZx zPl4Y9dqI)c4ZaRa|4HOY|LGjqp|tdu;?Qm2zk^NScfklK`fUXN11tf>e+c~lz(S?- zLF%ULc}nMi-+{J3kvBv;{%>GEcm|aI&A)=Dz;AbS-!adLt?7zO_dYy|!kA@EPZBE>@RPoVR`7r-F+JU9>hV=xC4xdBkZw?IjcAu4;RuLnS>ultpbgHm5h zf8h(@DNyR`KClPu1*N|31VwHP{9CYH=_p8Bl-&eMc(vf?z>T29yA=EzFa%2ci@;-G zzVd_MUqa`Aj^5x)&_h0xE(73SK*vGhp8;P4`#?#jUa%8921-0T!9NENf|72unbx0y zQKj3!!_d_EPC7=w&q9}iFMuUr3@idAT?@g_fI(2wcOG~M41l7K1%3*oZgAqyJ8B(- zri?rKP$sQEgpPq90^!MhhS_Z(MJ~Gud1cJgTv7=d00ZUnCdOTqbI33v?{0;QZ3fyB|tr@3H0^vAFdg8RXFUEblF{L}e4?;(kZUP^IjwoFUegHb8bRkIDaOypgmjnGEXeoby z`r`rUI4Jy6AboD99&^HnE`yFKzXL3Vjw;;*-U}U3x)i(zI;3>anK1LyaT#d>0{t(=$O(SU@D4k=v-{vXOuQ0W|SpOz~L zuM--bobrANdy)6|U@dq6ECIg?O1b(nm=As)lydYk7yv(^Sc@D;r+pRok3dWKE%1Zj zbI=n00#N)(c?6vDC+WvuhNPQdP*Dh>DZ+oP51d@>76{s(hu`|Fg?ce!Sq8sccdTWxg-4mztQx) z>6!kE=>z=koz%{Ke!Y`U@Ox&`S$+>>9L(_hdoxaCaGzer8J^E)_h)lcU-oH!k7al9 z+nIedn^3Y3^L%mIz%<-U>*r>_W7E2L?wod%=L6FY%3XicIJ$|EbBRdG4NkoabYcyLdi0`4G=%v*KBq{u5b!{2t5d%0k<$!&&KR2eJ-k9fIl) zTnzaArvqmKKL5$UX`W98PVw6n=niE14+lDVJ{UN}?@;EYOwv8GJJat!nt6=hLz%Hm zZr;m0ka-YlDB}`o)Sq!N!{^r9GlTKgZuhsT%0k$t%&Wj_RT^jw;}rc1Kf&8Y-bMg+&^=G=l+=& zX9oOdXU2K%n|YGo?wQBo9-Gb zH0u)2a<3v%xnB{fvo7*`c2*qj(cELX+(eig&qa&*m*)HZhv#?l+dh9Eza7^cxW?}v zoI5lZzjFuY;&<*vp3lzh=lSg1IKKzx9_07foUS?PX@}=@&N({A=RY*(@Ep=@PK@7u zb2tvszjw|)p7+ja=XY@S&}<~lzBt?GkI(MsIX?S5zb9t*VRL-;37(J7?&Y_0_EBtN zvpac?%|6VHj|XNS97Lfl74)MEpK|8+#*A8OedEHU&WbC@Go8R8+PVjs3 zx>MZNIB?zIbwk&YQVY8m66V75{GMKTW+AsLEQ;YXDJ6-trBH>CP}?pnjIRsO594c(-4f#zSE($CxkANe7b|0j}u zgg>DA?-Kd&Bg(H-`BC-%gvf^r?*wRbEW_w(56E`3qHkukxQ2`N)ea{f3kuX!r2! zN!LT8{~7gvh-c^h&%_PpJ}K|mZCq;TkiIVwrKf5AGNA51s`(qz^8a_@-%t8ePQ?7R zgzusMPUJz$dm-izrTjvlQu=f9{y?8m`dhOM9as7ji=gRS4=CNcB$@uS@@24I%tBDy z=O|q(?+z*cj~x@)&Ef~9}!LeS0sOt zpQHQ(*BSZUD!*OI3;cP?@6__ttNb5IdchAWf5}wizfbu)L_X<33;AzenW5 zKcoD6=NdY$^g)pizhCJgtv^E=zxQRspGtk9bdQ$bsM3ET`3pa$^k<~}K=&%0oeNF+ z*o7v&z990U|5fR4P;RBX=aI%@vNV4Wsy_d!=@nD@yQ+Vu($ls67*hI2q7U)0v^@Mk z>*IjZGsHjP_i6fcNq(l2zFHo8rTz2J9pZly?SbGuc9uU7ramH$c65BVQZ`nc4;(A7#acHxxwLfnb@%c+Lnr2Mnk$@}15 zUh{Eu5&usqf9_3&{;<;d8lUHs_G^6pQ0X^FSMmR8mzMey`p=bqQ|b@sZl!xQ{2=w5 zn8gx5ba-Cl`vFaV4p?>!b91KtQ1^4>{Us0LT3-$zH=8=wy(i^9>K2J#0J&neY5KgX z;WtTo5Z>2R{@+V|h<@K!{`aMQfd5V9|83Clf2I7-NqpgdSNSh$d}r{UiaDzB%~$&C zQlBG#wbD~H|7w)ZRsEh;x>)n;1*IQT{koOzQ2lPPrb2OYL`tLoRQ^!r3V_-`sb z85BC7awFz8)vrwHNt%95N-t3TKB;t`rr#HpZdLvMAA9cuA60ecjo+CGFbRk#;h!Q5 zf2=_SNeB|CQzs-~zzCQGMO#dUWFQ0iXZ|1v>WH=*i@OV!?y{}FHMZ=QP`XQ7d`q@; zjjip{)^@3~ExSv%0ckggR;<(}7Ml0_J?A+yckbMY*zSJb-}`PaOrCqrbIx;~^PJ~A z=iGbGdCoZrKOp?&2!GweUjTZ->!;Ui{uc`V4YB|C3jUuUH}bbl!oT47hVVfNf0OnW zVV?WM>t|xmhN7Olel7O=LlRyr{4JL7_2duv@00Mu!e4`gccUGN|G0#=kY9v9E8%vO z%kW_dR|~(#CH%1P`?iGZh2JaCZ+UGKey2E}37-=CRw3c~#a^wD@D;+}pG&wz`1_`Wx!#)e-jeV$!ryf=-*`miR|Prn+A##_@jHrs!Rr?i z?vZe&gmb_rum8x<>Gw(ah~Upl_*vF}IKCat*6CxcAM~kT+V3w|AA|=aypsGOoN>KQ ze=X&MaISutCjF8pi6m_-K^;yq5dPhPQqWKz9ZZs;eVI@_@Lx(ko=zz{8H)f zI|W}Z{rfoy?__?^`x8k&EeB!n`!&H|C-&nd36BtaaZbWh+24?VkL2Gf_TV>y|K}St zy-Yw}pBH_&LBa=RJ~dCm?@0T7M8XrO|DgYz(BCckxL?A{h29EDUr%~S{}sU>Bt3)= zN%)^+d|V~z7m?mD?17N*DAGgvZb?60`1_>bn}yzY1^=Ybdrt7bV|ye2j|BfR+Z*9m zCH!sCw*!*?da3W%C48Ug>%U3*PSKz53;s!=_nP4Ug!DlF=Mp|9wNJ`_+M~l}zIclGD>|%@;J->d!e)MXRR+RW zcE|16cd}4E(wO~f4*}2cLDY%Y-{8lvmB|q1nSzg9slx%thG=tL0zG^9JK*0Qt{;{h zUgijMJ-LjCOYxK7s|4mrY~chF~Ad-@hf zc$y>p*A9AF4u1aA5v~GpJN>_R@LTFn#{bZf{y|5$#F2lYqdu7qdS7$!JJW%u+S>Vh z+@TNLcVx%^%E3>8BmdowaDyZN8Atf99N}Vze)2u3z5IW0gnbTq|AnKzK1Y2IJNO&o zNMGfU??)W@t;!XPmK9YNl@^wlS5AjJqJ&QzoK?vKl2cO9Q-a`>UjpraLAg+r)8PfX zvM$^TM_u(eeZ6&YeN*ce<@YOvp2QX{ft$)A_(Y&v#+C3!TGUt%x14Z`;UKq^KFliX zBlU3I0F>f?dgaQp6)P)CmzGzc4%$mt60TxJ(TdW_rOV4J3YQlxuDo+`Mahcd^2!w} z2TiY&RaV@)a&h?wqMP8sAzs6piU#?@gyaoM*Qm5`(c)6pN^{PpNlxI3a+H?j4^1Bp zEy7eSr`!kOIPBV*Xmd)l*gfEg$F#VxQ{-qnHX>P}QV{k!+mZHcWh!OGW*P*XB zuC>P-ceIVom2>kdBYC$}dg#1L{M2}Eo0A9@klb=OG--gFo7TwfiqaxdPb1p4mRiw6 zVZ;qW zIg)ruOIM{zsIFFQS9WPmxrm@W->Bjv<*= zn;x1Acv7X;%9V}qMy&kdDo;rEB=iC_2%7$-#51?Ol{pNUXl=FNm$`LI<)&8UR|%au zsV8u#lx`kv*aQ#4;JT6L$Ln0xa07~{b#PS*1h_*FrYlXjgrxJNBKpuSi>!+@M;fYC z3Ls%Is%tk^u5W|8P3hhBE$hqS?lZ+&Y4@U5T_w(vrif}P&Dr6VlD2$t>Efb_6=jvs zk;P?;F*27|BE9M|Xt9-*%M0&ZTv=FAQ5KJ;f>hpJSf*4Y9;-)DmD=QwC)BYvDXLk5 zt;Fe-)~t`F>11|~U2$8T;@owO5YQ(l1rm)#+vTGP8oI-!rGfCHBJ0 zFOaUe;>o4X+JkDO#*A<_msQ3A{Te74XO!>|Unh=ITb7qgBvo};BZk;$^~OYO+?1!H zn4uzCA8C%0D1|pJHR|21oUt)z!@{)}eYoEZ-W%bGG{S7A%W~Qocl}iyZC1Wuw_rY+ zoTRXAbF?lBSF{ONW-5)URh(72W3%d7>zft~qUE!57c6ks)Y%F#yQQ^xc3#2YB!HIA zlC12r6+)AkpDs;qT8u*I+_wzQ0MPCXD1;_4k7?9xuDUhjp{*Csc0s6KJ3B8wkz3s+ z=hlhRBJ$=A&OXrY>??$3KQA>69|tOD=cdH~p{|+l6xOf`T+cQixR34y{d>Bi99^g{cs3wB{TBTxFRHw6g^@!NK$8 z-IAJ5#FZ8U#9*yDZ%}kD&`w5mfePVP^ZdaH0_{#vA>0VgH3ZWsQ7%%r(~M_zZH2tN z)O@Coq+)cnU9t!wH#Lh8bHPZXF^gN3=cLk-9Ka0Hn#hK(G2f~yTDi2Ka{gdRa~B|) zCgm`~3Zaci0n=z3sBYb?SRzf{{J~2E+P%cIh`d`2#k6gY6z)XRBJ$>?%aZGY(QT*@ zS|<5vbEm~9gw8$JRLE5$bAfi&Ml;}o5WUMwmnAnXh6w0ME_1r6pF$+JwNd4GVYZp; zY?WTuZ%#acL#uMrVl*?ih|&VOYGv+%bQp!ur57-by3JL$$#u{I17eUKls7+JF}W@n zEfs}uljALe7L^OMdtC?5mp9LlP1`Au!bw^5*kpB8zr6f}{M4VA(^(zAceD*T1^x}*K)3*;@W$1fp(J6f=Y|X%S+8-G&~)K zS*YQI@{$L%lNY1)aZHld$77wMo0ATct1wD3NCKAfMa2Q#^g3?=5!yPdTQ@CDq{%DD zA5<-YcGgf!TOnNfRo?u;2?FgTs70m_ZUk>J1k-92QaEX9CKbYs=Dc({aub*oy=NXb zV)=$acZCJoox8#F<;_i(FE=d)h(RrVDn-)|QZZVBIK@HrgS7bERE+5YgEE7BgRwd% zm8@nc7nngZYIf&J9|1JO0B>CyFLwbEu6-m;j=X|&Idaot6hd3K`Dt^f#VCZ%eaqaz zDxV9qvog9d6hf1jH#iBPg+z+}6QgP5#}hg1Sgs33bKrsy4V!BiO54AY!o6B92qBu6 zE=z7&3=uT>h8@@5oCmZ!FS#xlQQ6ci!sVQFnB24&&19Y^IG~$W=Pe*YwNdqFFE=elAzUm#-n_K55}1^}U~1;h zUcN!6tvFJ+S6m_7*u>#Bu5F#G5N-_>lzyxX8;(<-!aIsTk9SZ12}-|8i$MNuZrv=^817=4vj} zXcMPylUq+q28h90FE6ziqyFhI%tF-~oG{St{O1mWG1bYNlQwsHjN)pJC_JE>mgg-X zLPiDNx`|Asap@v?1#(Duhe5^5&+}63unN=nAJrH|G@u5?Agm`3vxM2yZ(Bfq8RsfykRf zEU4U)pAQHwRYagFtD1*(Rc?@v&$H`iH)@*v`_J#a`8zBbS4%ee2g{?a!&swf1@Obg zZ6@>;m-fQ?YhEWz8VO_Br^O$ZORKB0lFP#+08m+3v3PYwB5dy3W!*FBk>m;NH+hH1 z3@b)puFA@4uH`Vn6nciv0ZCDXHR4!0n)U)ce38bY5J}(I_ik9&53_nm%EsGq;yaaO+$!)2SNJJ>TU7W8K#ezb`X^=Bhp zk1u<_$BLDKhuz?P=XhVJ-|`LbhX8givf}txi0>Y29eT%S?ax8_-eFce4cc~RwIgjT z!#eb9q&+>&+Ql*oJ-Uo{8CR4X_Z!6h%xk@!F+TKOw}&p(tesQfYOcpOX8&p5*!?U! zIc}Va>+y}=e+p@c--K;3J(Bi8b#KyW{N?c6F$mAS;MwbW_$&6h{9nU&VY0k0d|-KJ z4=V3gC2x~P%iHqZ(SHi3v`Ls^K zDecX6J?c^Iooim`{{N3j9pF8EXDlTnZg8s4?{ie9Hud=8U{b*OWuky!k>BBZX zw4l2ItE1U1Klr3I+I_L{A3EP#?*Uye==)Gk2KpuXW|3#tz~e)IWa`1bxzBe^-@rzz zYhZ4oFJ?Zx2z>g^g{E2+@A!QCGlvwZaQ76eBJ)pqKl)r1@5dCYdyDt~pT6%1;Qso> zDn7*fFX?;iXR64Yw?xIqc>fK3AK?As+f{t`B;5Z%-&gT|Rf(q0`=9Fjj_YtAS*q#t z{!M+~GtsK>w=L_R4Ble6x6r1rC9jyarJgzCXcL@qJvUo%y)e4ceXVuqRFQX=HxMiH z@n=2POgS6g@~OUcY@o&qlzi_Jef#(n5-pGq~ zg|a;j+SpFG^EZ&G*qT>fgA6fsQ+AAFl}X2YwvQeEE@+$aiL`j{Ok>Jodm4Iul9sg3 z@E(2Q6m;w~%6?7SZ}iG5JIAaZ)j0*Scno%mY4`c=>vP(`$6y0ti*{Ywx)}DU73gIVj}to{J2UY7hae;3j9oMKt7i!8gV?UXP~0oq3Oeg7t%xTwe3QB-otj$=62wob}>Id`m3fYIqt4nn@xG{uKL_~4Np(haOxBd*IqC98G@fB_&mYiBKU=Z|A>PA3ERu6 zy#5_Z7e8#fUt_y}G;v?$)pLtSy~F!2Z7oDU_@>f(JMVrg)KxOf%I93y+J^Zs_@-@z z-Kxljj#NDo!rVsYA;94rrCp3u>8$K?BL)U4Ot@;hOMb+q(q-+R6z`nnE~!$|Nf%XA=1^bN1(La(lsRom+-{TDIDj z@>!wkp-_J}#`qV#p?*F~P{vglmpO+xnicAwZ28Z@S8v64NAf=N+!Yvuu+2W3bHbm~ zjt|rFGikqvajiSc>c^P6-{h&s7}Gt{>OYIPW5{#C@4v;~Zr}ImcE__`wxPH0>QEQz zc!&Qwe>r?>KXCdd&t9YJru9PUgtXzas2hxZ1!!?b5)Z=WKPeE)A%<^@0z3 z+us4%Q1`~7-gf%=1t~m5}28!@*O} zMa|QHk|%Gd%Q=3WomN;6-BELC>X3czOPw)egqr(4KB*56&AZH)vK{qdn})pBysEO) z_?X9GJ0|u|6Qt(d+LnEj#`n8q1F@N@C>ANEU(5;mG zoW%3W0MZS=T3+%MgZ?m2YJQ#@%j*DsZuxn>Ew7OJoAUb6Dh9q7ZfWTEaWUMT1TDmFAu1VrUfJ|2acs1Zn0s{hho-O130{McJ z;Xddc;dz0*0(%6W5O_@BQGpoGRldUl4+`7|xCHIDSHm?thnDnX0_y~>7FaBBj=+fm z{Q@tbqcVSwz?TIc7Pwa+&;Me&Z31geJo+8^VgCbM(+)^I-Uc`quoV#hOPe$d)&Wv) zs{k?gDE*j*!J7c7mpK})2>>obx)Ff416~*=?JAJxrn0?`1Lgo8k?=tY^ZYB~cS?90 zAi7NHW(|W)g0BT+yRHPB4j2NQ2bd3tF>qE6An9ZSE(h!$3jZ~rb40@+&lg>Y_!!^< zz&gPBfFA=a0IUF<30Mkv10c_<`Ve3aAiKzP4TJQv#`qC{d4Lx&AQl1k0z$P*&uAFj z0|?bB-K}A8HQ+4#=Q&f9cL)$oTY9U8!2-eayeZ-XfM~kXi5dpS3*Iky3lJhN<$0Tc zLE76~hKcb^;b7-&24~ z0qX!!b#*QzpgISV=_@3Cj)ry9B|H%jgWxQl?|3U9CkwX$o`-$D3;EvAaLpOOn-PB! z@FRf70a3-$V;TmJ0tOM^4G7jtk7yV?1W3I&00^5st3$$j1U@A&1~?t~R=^tpYXQMZ zX^n=#Dv7TE#Q)Nj8me<1Z$kR%2orMLngPf}72y9O1`)tDeSqZ%U(j$(FCh8q5qMnS zQ9$x>1dx0j1SB5^Gz{*O_-6o7MCl$4gE+fJm0u^Y0+4)^0FsYl4cCMK$;Yi4u9*W! zK5_(36o|7-6dxQ%$wwc?JMwWs!{B*9#-9NsA3YidUk0Qc4h!r6Bp0(+Wuas|74We2s={J_bnrTdm<5(BQejvx)&%B0i+y8kEWLx->^ab-p9@Zal(( zYYqb96WYiQz(s)M13rC5#w6SXNSAw65(aIaUpg`*;W>bKe>XB9;ZcBi))@()==A`i z{Gr_fI|RlARtXFV3WqC4qq(8@4evbL(G|sUYw6|EaP|v&UqNR zZ|H%c8NN4$oF8%lXQUh(avXn;4Cx+%b0Kg>%1NA+!V^IJ-j|0R!`UD_E#wGJ4LP29 z0_Trh%#7h&l4r6y@b^eoH~#J(z6WQToEhGWzk5gQ!|5kSuIR>TDSaa^j?D1w8+BmR zL7WhBe$)m0Jvpjpl;8W*RXcGy$X=X$G0YpA_yo?jc+J`TfmQ@cULO9W`ew593UHYtMcr_kCdA*Lb%x~tM z*F%XJ<~YaeuSpMa<~uX(G4pSh{9A?I9tqEs@L>u6RMMZ2@JkGX-VdlCxV|Xi_axlH z{NTxaCohxmZmG{dAP@O7^GxMu67=>;evUu({7*{#%)Iwb)(3H;B>k_Y{00gCP{Q;d z$m@F2v(>*w!exU09n;(7TO@e;gXZ` z;b%DVo)>fqlg438Sck#*s=Iz+0( z%I$}7)=?5hw2d#i*|;;@T3w5EL{eZ2+>dF8I*ABSuh`NQNqC}zPm!WA{g#-jVU1$W-gq<0h5+MQyFEjqni$ zA4NJf8jo&+(za+EB?YcfF2*WRhVkm7J&7Tv2}`|N*U{6DTgejVCKohkU1Lkch)Gf| zfrlYSl+vF>oLvxQk@ZoyT7zcsjbd4(z7Y<~*pO?%Cp`aAgA&2?IgP3%(l(P;BoQ_~ zoglfQa=pCG)SYro=o0*?DRpA3e(7ksQIdi?B2^Z|(dil*I8H&SE?ffPy{5$3E0OR$ zrAtbLE=5|k^o$JdL>G-V)ghU^TAK8oZQ?yjdV(X?QJ8weo1$Zh z_rBsz3cXa+QVaxr?%0$S$fXTB?)UOMRSkV;tae=!99P-P_bBA3BL#}L%#amY&jgZe z!K$Vj^#9x8)v766bNebJmUY+k!=e^or}0U_efhCf=p5^YM+v=GbaJa5^z4^YL_!bVyvQBb=_aqs#oAAG zRSA(QJX}+RJHg)wKukj&{0+WtxFlN>S|OF|20izXE zM-_!Pf8KB1@L`dcX^%O0+2FXG^&NEGFxJ1SD<=FEfFAjaO^dHxO%f?>z9##B|4|<7^?oZoFt|$Fmi3#Mcckz&(F#r^Wd38RLUq1HNJSzCWh^V&hn> z`5A|`KjX0mXad$iT!VER*LGg|*3hIi#QS^``c8k!y9+fe^WfVv%G-V63n8qh%HIDN z;u!udzV9*(<4(z%E67`|L(cR((RJ&?p{KJi751Ny?Phx#+Tx~+PKSckZ_toOsA z{%etbqSSXh?nz^Uz_EZ#!#Yg%Tzjqx@-}PJdgq7w$86cw$2yqxgQu{j4mynW;(C3? zDOrb2z9(DPZ0x|=hi9+`alkwATp`vF8s3!7*!UxFRlgm0p6f|2HWqdEq8<04ozVBQ z8OLxh+V?Eh?DbAVy%6u8V(prNwhy3;Eb#lufq_GJTvgchsOOrE)R$Gz4Xn@T-;cEm zTi$)-)vAqKlsx?tJC@Iw_}ubVgy(s=b{zE?zNnKtgWr4=zA_|h+;zQruny(aa4ln| zqnwZXt>-@l+6eE&4}C?iTR#1c?l%l$P@0F=E!k#GVF}uL@)gzt_&8Y6^Mck37~gzP zNVgN^0GpxKz%xJB!7#5q?q{-Iz6$c}1OD{q*$2n&pCUSc`Zb2f$HVdVgC1hd%tk!3 zCffAtIxdWMt_5%ZiuxE{(MQYvg<-=d=m(&$c3!|^yzZ3Ag|h>PJf6*yrs8MHWu0mv zYro~`x@MH8Ys^zzf2r5Xm^R`16V}%ssG4-@7XuZWweH7hpOSjg9x>g~%-&c0p7noS z_49#w{aEJ%bG-9DpS6p6p=69TtA%HxxL&uatuP1@}#jDh_Uki0T$fKU)+>?D zK#>>YqObnF^%+Pv1nGw&-!P1c{?1E2?=G|UjcL^yDQ}^&<&;hB)1ga@r+%CEV}CUF zew;>^>8Y8-HDWoQ_G^Aq?VEwVer3;S>ry8A(%ET4W|*{zae-^(bbBjZs^;1!y$`0x zZ{=5^{P=i`vRFRX;(Z@$W68&O*vc*b$I37!UU?15V1M8m@B#05wf0uoU0FLE?{n6K zEZU?Ou|6(}Iwg4F`sp#MjGkeMd)8})tzH=GF1s@te1F09eWPxg;3rThxV-+s`V&3)tjV8m$nw8>>}{62(l=I9KKzL za3Pzd(XEp{jjpnPCHWn?mZsR zhRn92J;A#x?LWi36Q671c2&kows$vhe%L_|<|qs=o%p%swG%(T+!_CA@Ddsyon(d9 zPJ&JT#-|Xj8jq8X5w1d*GQG652)ryp+p=9>Qtg^v=Q1DzA7tR&{~O4Hwu$8{J^XX5 ztwr0r@%5m{rx!9A>#+_6a%#~ZAHf>lu;PU@{8qsvJg0Y2mN^*fjV%4RPsq|;58Y&B zfgd+seu6pS&(Ox&PJ8kE;zQp~Z>x<>gPrNeI@hiaw7IdDMi(dtk5AhX_Or$6GQq}zeLGnqrItMFa3D-$yCugUyu&*R;?~U9jnk%FaPP<2O$(w|v&{ z$Hcyy{`&Z``8%FiRP6SD{K6l{kXn|g7dbA^?E#g@tCU%WBY*~zuxSLx@> z5s+muWI%}Tpm$C7jGXz_{AaIu0q~cAe?QEc|7Z9qeE*}b-sKs&<5AC5=ia*E*=w3` zU+Ecjt_Sx$9xLl+g}d_HFA=^C@#g_6Jy)uh} za`?H1DLLVhQ=YIczUCWsWA%kcUVUuCYe_hd!d>Z#oAL?k;;5^wtKPcGy6T5`e&(~+ zvsj;xet{*7u~)w288Q4y{C1+w8$F|TEW>XGetyr0?~ekn=fUe!StEADwlo~^cyE|w z`KMrwf5D_~q<+nO=n=x^{VITiB=EkpoDAd^tJ$T`2>w(W< zJ~>?U0M9h9grN@%9~f%g^@4ZA%r~J2lYJv*z5$4H#^#ser|@`YeA74n#xG$#{hxWR z*zrT}$Q>_xN9_2Sccg0bA9%0W@q%ZB(u23q=Ep{k+3_su`Udpi1?b0{fN!DwpYmLh zbpY^-n;v<31Z4XLWE+DXyn!|c{r$hh{O2v`!g=Vz+vs=H2hS6si&OTFx_QcpDGgID zLLY`ccB|(T$#JKABX67ny?AlMbGM%Qcmj9IcZG^)KH|{+SA7Y;r$i4P^^8$^K%KwB zH+=Ztd4~^gM7u8YTnQbx5;}0@j=%Q~kL$trvxe{31$-#_z&7i|lt+|4U{6QYmYa?Q zB#wJLx}i_q_XUnvwzw~=xZER8K@SEXE9yZn>dX1hx3c^@UP9mI{Lk$5PS~B%Nn>4K z-ib4oF54n;=~OcDA2M{rCDgzsLxu(5oC=v@stotCs@+h zj1QfWn)0@_8nw`BYiNnCZ-~_31Cstww62*)jli|TCVbp21J#N~eicP=3$0Djmgw5L z$P$%=A7|^L4@GL!!6P{Q2S;huv{W>jGj))hAAxn53$0ZZOA6!zSiEZgtv5K`M@ddr z@XOYMqHse)V{71nwn+1q0BYHaPtt)F)Fcvk5aMiXV~7H`+%i1a*t`)ZvxJ>#lV!1V zMRByHsV=+)hn+M)f|djgjPEv(c-f7xwAhrn1jj8!Y67i|fpu^U5op1AO886}Xu|j7 z0FFBg)M)aCp5#~U5NW7fRc2lOO2Km-Hm_&!bK94YfyooE6Zm0% znv(yd;01wFlCK`YuSPV!3#8=ddU#%Y@${c?obU;tv3B2HXo+4R{9S;W=W~ z%YfSecLFk9J0R1Y0KbjI16Bea0A%{zfaQSgfXsI*AoFpb#WKJnQ2b`VCjjpO+zhx1 zunrKipT!M;cLSCJE&$ZS^pEk}AMr83BYu<%@mNO1`@R??Sj8kmY?0kaS7_Nq0OT z>16;i-AN48%K-7NT$TSaAk*yyWInDFW%@e6X223amU}B8%bf$rb{!AMb{z%Cc0B7>p5>nyahNG@!anK7`yjy?~VeGl0~K-GG$mX22%EDnQEdV}O+7Y7K)Gfb0h=0bx2yOEnCJ09pSz zfaEh9@IF9VzWV|3jIa3a6?hboa(oF8V`FKzhQUJ;e-IERru2Y@!M%VrNWTY=^?d@6 z<+K8_o;851XB8k!N-59#1q_w~vK}RX?ANynEC7T_D9zU}I9=lXfFDJ=XJD6^z6o#* zV4Z}yKZ5ai|HE&>r61EU$bAxw4*@<5m;;yvJoiZe1_OYZ2v5{7NV|`&Q<|+|@FK<$ z=079wB;YXMPiPo?36Ojo2CM+YcQK{!PXW?C!~iM(8bGLKX_bb-j{!neN>^(bTnUIK zDJ|77$o&$ia%r)K!CL{rQfYyPLGG8p|I(W@407MZ7WBjE8m`FzdJjev6i zX8@88ej@{bHvo0?i-kk3txAR{Su`cEbOmA%OvbjAVQd z(i3(I><}0eSS2tdFdz^><@0-M>oR=Pb8hl_d%S179`6Z2FaDmy-%tclS6y(w|m%2*iLa^*g^b#*?-JG%zMIr5`Pb69>xZXp3F1&dp_#|wpJX^ zI)T3zhsUs~V&{n6_}e$);)o32$t!xUID;(~y<^`P>-Fv#_slr2H#Y7G{M|GDnepIg z!oCUEKrvzGgbd%Y>yKZ5;(FA1EIJ$a6PW9lP=2oGH|uSlMJHtVJd7o;OXw^N=fLRl zDrJ6z=ScXIP#}g2B>WsYCBq>JPiB7hVWIaGN#7^opGtZfbzav>*e~IKC4Zn{*4s3Y zKZFlsaNu<}(<99{C4D~m1O9};OZq+u|BU4${Y6Rt4*5g)RSBP!aIvJHDD+li5aaa` zp|@GWwWNnUdn8Pw#qfBOp8O$PAmJXNe~*;cBl4!-L0(=7kC*TZB9DNC_X+>)5`*)IlytGF^!bK8ZE9IH>Q(PZTd71EgQa{r^%a|VJ zt&;p(h)1|V!W$@0gddggC#1ZOOE_2Ze^$a(l7F9s|4Hieza$JtIEtS`B9F6@|9gVx zJdEZ2M8e+?`Tx75=e}6h=bVINOpowPY5%`u`3QeR!e5pAWfK0TaKUhmpCajdBt7R*D!<@oN!UC; zwo3Z$5jy{uguZ#c3Zg#DZ?5$cHqV#bk3_tAUY#WQ&GXlnkkOW%V{B4*68N{{|IvZZ zci^4-9bQ1&+4FC8gn!`(k8^~-gUt5)cRRxGIm*8u^0TL(>frY^)ZdQ(l>;Ai&|l)9 z|7Q;VS{-=Kr|k4I9eB=b?f7Cx`g95+QILq(7twh?>h2-+L0f7ag)-2+!3yJ zq$h1VeZCK{hhKHb!?}N|-cjGEBmJK{@R%be(a&;}hwoKM_!39{osRT#9qIqWfsZ-l z_X$V(gAV!^9P;_8gWvld;oS~;f8(e>=jnENe$G)I7trf{4meG=p|yF7UUXly1g{Ha z<4)1-@&*#C%yT^!vTq_Vn;w>~TZ#9~cw3ofe^9(W zT=kDkC6Va*S|nDysO=!CqN*fp%Sl>pkK}S<@XAJR*)gIKQg|s^7hZ2iaH|n$N-dbo zg=G9JV1tvhz3uT!SCs1)yktT4E=i2NyhV5oTjRFTN7#1MhZ|y&F-idOC?1oH0@YjY zc%+hu+F&KShvH&bg(dRxCOsh={USRhnzE=fv5F)&8P&95kC8eWJlw`(lItSjmdMJ+ z#=4b_X7iER?t{HBs-eol@qI}K1(t27ID1Q53R~(T5fCc6UG}g@!z{rO%{X`&62>1p4^$2Kmg=*uc#zI;-*lFHXU6OuGKQ0J*2^Z?f(BDUCF&Z>>#L zsC%L{jCK`BTMIMDY4B_$vvVk}Dn?I|l3_z#w7#vr93N%y)n#!*bz=?PX(!rCGfX`5 zC3-W8i0>TI?PiN{bOfB%jy_mhhR;sy-7D0Wr}(y^c!N*}60x>3?r%rqisUPr!woI? zK(T0xGCgDz<)%ZX>8<$2!f!a`)%b*C4w{d1!9FVV$dFwW@q#qMNj=^{rCSx%He%D6 zEjC^T6^uIY2cK<^8gRc+I1;dYEVH>%lBI9HNG z4`ZKBMd&teY_7joorZq9{`_RxB;2yG0;7r=2k`kzHUins#!h?YbbJ$3{6-xutoIiU zdh#yL_#NczfcUw*Zd;Dj(XP6Pw1=mrL&VQc)tjB(=RD0pPqxIqu0d*$*z4u8X)68s zhH1|ZOoteB>zF#PPD$#0j+2XTG0V*#tWu5*XzCy^O`coPT=aW3PU0FwNNLa68gvU= zx^tD%9joMWG?G5r0(;>GVJ^NuPB!1g_r)cStHBX6gAho6K#bW~=ce814hq4cAA^*Y z_IQt6zTm^f{dsc&iA&DSzzH?(`}5|{pYJ#~gQp`jK4`Y*xt*86Zy8pArRjZny_gH9 zyLwtQW)Jh=GGRQP^SyF>57*`H%R3%ICS0V4DZ8~9FMS_m`59IWf8&>I__l?U&)+a~ zmPv}b+v7=HZ)9H?Hi?E6-nox1P^hz=m)wUz504DioWHfKNfKuVW?x<#9uD004USr* zQ}aD7uYDz&Uf>J5-iR_Ng%^5h_YDSw-fLH={lB8u%-mhGq{}Xu~X<(;k^;B z9DGb+du;xqW&J|MB>%C=uT|~7cJOyzeHp+vDXDLJO>+J;{6#l~@%0gxlfSC9I=>N8 zUv0cyHNx>%_*3$8)0ZZp*CF}*QC;7CUs2!j7;B1BDbPRgl)wqAzMSnbs#?eYiBxPK z=-IE-`o7OT&WO%kB}C@bY5q#Ss=gE1Nvr$H%6M0kaPq#@J_o&>LT@wZNfRZ}6!|qZ zH?}s`;ThQ39;?^s9oEA+sEH;uF}^hox^H#V*Rb#oLSiP? zx#OFbwH<-(*DdQ{aq_;^*C9~;79rhDNCVUHfiLHt1+KkE+o`pG*w@*~d#6K2 zFln87)lj`|-Osg%*WJtghUdcSlPUJV0B7w@PLDk@`&FLWBKRKmY>XkTr`Fd+-c|Yo z$?45rfm+h~QZi1Z-&XVuYj5C6>qPke5cbX~9%G5PD#Z1mj6&>j@NDyStp)VPY~{5k z(gvnT{>_Ov3vmJLr<#Iw{hn4|mlx2x*@v~3dN0S>Y2Sjzb6sX4%>5Yd`zEaY*^q1d z&{Vm`7A4oMHASx3=N>~n%)S!hDKoD3-~G3Vyau1p3%x%m8}$z4V6U9o$51;d6MHI{ zpTEG1Qzumw-~<%xbKqW!u~_G9)|OMoX1|;J-iyoIAM)cQ9`$~%1dlr0Ci^b-VQv0b z)xL|%sEU$$7d`F+_R@Q_WmSx3wC zl+~voj~?u=o}&7X_My*oZ_fRC{kfT*`m@u#=u3%xPxkUG>;nsbMfcNM((~v&S=^6H zJ>P&mF>IsZs*N^I8ewarSiFrksy6a;qs?qGwXWN@eCll+|0uIH|Og8QFb|TeYvqS?ANph?Atw%6YH+%Ll$;f^7AT67Pbv*fp*gNoWi~k*jsf**XYR;R9jHCRex95|04EEvRv*d zqD)Rn*cvzClC=PPcCMX(J$613-f@RQJSl#e!=f;QSdaqsfaPyCzZFHQX0m#AniT#Jp1mLuG$+z{a90S$Qlyr z#}J3TE>^yCPXYJ8(I&akF!bE=pbRA&XSq3$T|3Hl%j32eP1!u?;!*I~jpO15oTKR)tTuaGDTfZ&1)VO|)wUt0 z&)h#M_fGknye3_hq@!LKT{Yz@K5+IA!*)8Si>zJKqz~{+9^&o!v8O2R=YhT+)Hz~e zBkk>O_N!1{sdE)$Ltz~ShZC`R+sQ(?8xs4fP&HhPKe?MqD+AceQy0M*{ zFs0p5?`#kGYwf$N`Ob#cKBEVybGDtH;U^pXJc+(M8nj!`muu0NZ-i~z0NZw(Cu^ez zwyZZzpMDhmsvUj0R{FGQ7kioLN9f}TU1S=2+p;a(^0}pBU#?YsIYH0J%&l&tr5&}d zcvTqh1>s3fTorLI7hduJXSLs7Z_TKhG$m*4q!Fl_ zb6(1E$TdI5BH9(un*0M#|K#s_Fn%WQXQj^mzH;Z7``;mV(sjz+ZND+=?1#Lm|B3o* z9ig31#Od)gS#Rh=XUu-hf3lr7`KUYpUFGWfC6}98maaqGUSOVrWjQ;aM<4G*A73W= zH5q+;dS>V$JWwkgbN0;_(BGe*D-hX zV6V3q`>PZ4S7$$R>iROwb2dWXsnbrJd3Fnp-!LhQd$`AMoHU$}X`jF|R^Y}-e5SG5 zc|UVfjD51HNX-wj4;J>r$M_-V^P|xP(`QL5>j~dChPV5^5yIRzeBBrIe2Qh*o(%)$ z+0OQCXd$l3Jlo;fP(9n>+0Z_|@qC|iGxhw~EuQ_AJW=mC#!*KHe4HD29DC-q?$922 zVUOU)^-!WNGQR}RCXRN3_fFm6{KllQkM-IfXYc>qnONCtu)%mu{0eDsY_HldFHYB< zhjRn>aW-L-P#3iyHQl~uyR9WXHQ#{mC-OK3;U3sxbLQ3Li@pc&daYut6}x(uF1^2rsyvf_+z#Ap*b5JzDrLJ-7Inw8CG|?Tfj*1z3}o+=(R=H^y^sCD@98M}5$O26 z9cBLvIBpuw83gT@aqqUzS^JkTw$|v7)`t(KLy2+5sYA~z9ZL9`(eXW!+3g#W3mbNLRJ>)<{4uB z$ml5N9OMV{AB<7wpLWJGjyR@qmT%WjoCgRVHpRy_@=t!8dceNTcza(=r!$|H=Q5_n zcLHTgd2iaOYQrt@zGmw2elj&>;}d}L6G+*DS4G>wD_;NTdK>7Zt)6uq@4>zB)#Ss^ zv>E&O<@Rq!`AU997fe5Jrg4*Jw@q$FS4}^+%N@LRcBuCqu~)wPNa?HdFvjs6%fFOe zHTRagHf3>)n-6*SJ#Ouqfct^Tc(41K)p@!Fz9D^lNcblm z7|3tWfSLvp_M_r4#KHqm20S{eJwmJ5<9>@Xj8A12g7+3JF z@sPYtb^z~WC|CH{EE@x#s3tArVt9`jy7Jyjg`@AiWRDMBIRSAKF7@JF z=27T9(;58sQJN3bJq5Q`c6zUnTq=@%=aZ0o oX7%!QO$d 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

1uXGuvmka}>AFZ09I$ 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 I>F?zK2mTEdE&u=k literal 0 HcmV?d00001 diff --git a/src_v2/libs/glfw_osx/lib-x86_64/libglfw.3.dylib b/src_v2/libs/glfw_osx/lib-x86_64/libglfw.3.dylib new file mode 100755 index 0000000000000000000000000000000000000000..392b8b473e18b990de8f8816bc02462ad936f9c9 GIT binary patch literal 231576 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 literal 0 HcmV?d00001 diff --git a/src_v2/libs/glfw_osx/lib-x86_64/libglfw3.a b/src_v2/libs/glfw_osx/lib-x86_64/libglfw3.a new file mode 100644 index 0000000000000000000000000000000000000000..0c113d7b3e7b2667ae103a0f80ea0461d95366eb GIT binary patch literal 281576 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 >= tree->nodes_cap) @@ -188,6 +197,7 @@ bsp_push(BSP* tree, BSP_Node_Id parent_id, BSP_Area area, u32 user_data) if (bsp_node_id_is_valid(result)) { + zero_struct(*node); node->split.kind = BSPSplit_None; node->parent = parent_id; node->area = area; @@ -245,8 +255,8 @@ bsp_split(BSP* tree, BSP_Node_Id node_id, r32 split, BSP_Split_Kind kind, u32 us split = clamp(node->area.min.Elements[kind], split, node->area.max.Elements[kind]); node->split.value = split; node->split.kind = kind; - node->children[0] = bsp_push(tree, node_id, {}, user_data_0); - node->children[1] = bsp_push(tree, node_id, {}, user_data_1); + node->children[0] = bsp_push(tree, node_id, (BSP_Area){}, user_data_0); + node->children[1] = bsp_push(tree, node_id, (BSP_Area){}, user_data_1); bsp_node_update_child_areas(tree, node_id, node, 0); BSP_Split_Result result = {}; @@ -260,8 +270,8 @@ bsp_join_recursive(BSP* tree, BSP_Node_Id parent_id, BSP_Child_Selector keep) { BSP_Node* parent = bsp_get(tree, parent_id); BSP_Node keep_node = *bsp_get(tree, parent->children[keep]); - bsp_walk_preorder(tree, parent->children[0], bsp_free_cb, 0); - bsp_walk_preorder(tree, parent->children[1], bsp_free_cb, 0); + bsp_walk_preorder(tree, parent->children[0], (BSP_Walk_Cb*)bsp_free_cb, 0); + bsp_walk_preorder(tree, parent->children[1], (BSP_Walk_Cb*)bsp_free_cb, 0); parent->user_data = keep_node.user_data; zero_struct(parent->children[0]); zero_struct(parent->children[1]); @@ -315,6 +325,7 @@ bsp_walk_inorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_data at = n->children[1]; } } + scratch_release(scratch); } // parent, left right @@ -345,6 +356,7 @@ bsp_walk_preorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_dat BSP_Node* n = bsp_get(tree, at); at = n->children[1]; } + scratch_release(scratch); } // parent, right, parent @@ -388,6 +400,7 @@ bsp_walk_postorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_da } } } + scratch_release(scratch); } internal void @@ -462,16 +475,16 @@ bsp_tests() { scratch_get(scratch); BSP tree = bsp_create(scratch.a, 256); - tree.root = bsp_push(&tree, {0}, {{0,0},{512,512}}, 0); + tree.root = bsp_push(&tree, (BSP_Node_Id){0}, (BSP_Area){ (v2){0,0},(v2){512,512}}, 0); BSP_Split_Result r0 = bsp_split(&tree, tree.root, 256, BSPSplit_YAxis, 0, 0); BSP_Node* root = bsp_get(&tree, tree.root); BSP_Node* n0 = bsp_get(&tree, r0.children[0]); BSP_Node* n1 = bsp_get(&tree, r0.children[1]); assert(root != 0 && n0 != 0 && n1 != 0); - assert(n0->area.min == root->area.min); + assert(HMM_EqualsVec2(n0->area.min, root->area.min)); assert(n0->area.max.x == 256 && n0->area.max.y == root->area.max.y); - assert(n1->area.max == root->area.max); + assert(HMM_EqualsVec2(n1->area.max, root->area.max)); assert(n1->area.min.x == 256 && n0->area.min.y == root->area.min.y); assert(n0->split.kind == BSPSplit_None); assert(n1->split.kind == BSPSplit_None); @@ -481,6 +494,7 @@ bsp_tests() BSP_Split_Result r2 = bsp_split(&tree, r1.children[1], 64, BSPSplit_XAxis, 0, 0); bsp_walk_postorder(&tree, root->children[0], bsp_free_cb, 0); + scratch_release(scratch); } #else diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.c similarity index 62% rename from src_v2/lumenarium_first.cpp rename to src_v2/lumenarium_first.c index fa40abd..8f97144 100644 --- a/src_v2/lumenarium_first.cpp +++ b/src_v2/lumenarium_first.c @@ -7,8 +7,7 @@ lumenarium_init() App_State* state = 0; permanent = bump_allocator_create_reserve(GB(2)); - global_scratch_ = bump_allocator_create_reserve(GB(8)); - platform_file_jobs_init(); + global_scratch_ = bump_allocator_create_reserve(GB(4)); run_tests(); scratch_get(scratch); @@ -18,22 +17,22 @@ lumenarium_init() state = allocator_alloc_struct(permanent, App_State); add_flag(state->flags, AppState_IsRunning); add_flag(state->flags, AppState_RunEditor); + add_flag(state->flags, AppState_RunUserSpace); + state->file_async_job_system = os_file_jobs_init(); state->input_state = input_state_create(permanent); - String exe_file_path = platform_get_exe_path(scratch.a); + String exe_file_path = os_get_exe_path(scratch.a); 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; String run_tree_path = string_get_prefix(exe_file_path, run_tree_end); String run_tree_path_nullterm = string_copy(run_tree_path, scratch.a); - platform_pwd_set(run_tree_path_nullterm); - + os_pwd_set(run_tree_path_nullterm); + en_init(state, desc); - if (has_flag(state->flags, AppState_RunEditor)) - { - ed_init(state); - } - incenter_init(state); + if (has_flag(state->flags, AppState_RunEditor)) ed_init(state); + if (has_flag(state->flags, AppState_RunUserSpace)) incenter_init(state); + scratch_release(scratch); return state; } @@ -43,30 +42,24 @@ lumenarium_frame_prepare(App_State* state) allocator_clear(global_scratch_); input_state_swap_frames(state->input_state); - + en_frame_prepare(state); - if (has_flag(state->flags, AppState_RunEditor)) - { - ed_frame_prepare(state); - } - incenter_frame_prepare(state); + if (has_flag(state->flags, AppState_RunEditor)) ed_frame_prepare(state); + if (has_flag(state->flags, AppState_RunUserSpace)) incenter_frame_prepare(state); - platform_file_async_jobs_do_work(4, (u8*)state); + file_async_jobs_do_work(&state->file_async_job_system, 4, (u8*)state); } internal void lumenarium_frame(App_State* state) { en_frame(state); - if (has_flag(state->flags, AppState_RunEditor)) - { - ed_frame(state); - } - incenter_frame(state); + if (has_flag(state->flags, AppState_RunEditor)) ed_frame(state); + if (has_flag(state->flags, AppState_RunUserSpace)) incenter_frame(state); } internal void -lumenarium_event(Platform_Window_Event evt, App_State* state) +lumenarium_event(Window_Event evt, App_State* state) { Input_Frame* frame = state->input_state->frame_hot; switch (evt.kind) @@ -79,8 +72,8 @@ lumenarium_event(Platform_Window_Event evt, App_State* state) case WindowEvent_MouseMoved: { v2 mouse_pos_old = frame->mouse_pos; - frame->mouse_pos = v2{ (r32)evt.mouse_x, (r32)evt.mouse_y }; - state->input_state->mouse_pos_delta = frame->mouse_pos - mouse_pos_old; + frame->mouse_pos = (v2){ (r32)evt.mouse_x, (r32)evt.mouse_y }; + state->input_state->mouse_pos_delta = HMM_SubtractVec2(frame->mouse_pos, mouse_pos_old); } break; case WindowEvent_ButtonDown: @@ -89,7 +82,7 @@ lumenarium_event(Platform_Window_Event evt, App_State* state) frame->key_flags[evt.key_code] = evt.key_flags; if (evt.key_code == KeyCode_MouseLeftButton) { - state->input_state->mouse_pos_down = v2{ + state->input_state->mouse_pos_down = (v2){ (r32)evt.mouse_x, (r32)evt.mouse_y }; } @@ -112,10 +105,7 @@ lumenarium_event(Platform_Window_Event evt, App_State* state) internal void lumenarium_cleanup(App_State* state) { - incenter_cleanup(state); + if (has_flag(state->flags, AppState_RunUserSpace)) incenter_cleanup(state); en_cleanup(state); - if (has_flag(state->flags, AppState_RunEditor)) - { - ed_cleanup(state); - } + if (has_flag(state->flags, AppState_RunEditor)) ed_cleanup(state); } diff --git a/src_v2/lumenarium_first.h b/src_v2/lumenarium_first.h index 2dbf1b8..92d2067 100644 --- a/src_v2/lumenarium_first.h +++ b/src_v2/lumenarium_first.h @@ -6,16 +6,13 @@ typedef struct App_State App_State; // Environment -#include "lumenarium_memory.cpp" -#include "lumenarium_string.cpp" -#include "lumenarium_random.h" -#include "lumenarium_input.cpp" -#include "lumenarium_texture_atlas.cpp" -#include "lumenarium_hash.cpp" +#include "lumenarium_texture_atlas.c" #include "lumenarium_geometry.h" global Allocator* global_scratch_; // gets reset at frame boundaries -#define scratch_get(ident) Allocator_Scratch ident = Allocator_Scratch(global_scratch_) +// TODO make sure all scratch_get's have a release +#define scratch_get(ident) Allocator_Scratch ident = allocator_scratch_begin(global_scratch_) +#define scratch_release(ident) allocator_scratch_end(&ident) #include "lumenarium_bsp.h" // Engine @@ -26,6 +23,8 @@ typedef struct Assembly_Strip Assembly_Strip; #include "engine/output/lumenarium_output_sacn.h" // Editor +#include "editor/graphics/lumenarium_editor_opengl.h" +#include "editor/graphics/lumenarium_editor_graphics.h" #include "editor/lumenarium_editor_ui.h" #include "editor/lumenarium_editor_renderer.h" #include "editor/lumenarium_editor.h" @@ -37,10 +36,22 @@ global Allocator* permanent; #if defined(DEBUG) # include "lumenarium_tests.cpp" +#define lumenarium_env_validate() lumenarium_env_validate_() #else # define run_tests() +#define lumenarium_env_validate() #endif +internal void +lumenarium_env_validate_() +{ + bump_allocator_validate(permanent); + bump_allocator_validate(global_scratch_); + bump_allocator_validate(file_jobs_arena); +} + + + ////////////////////////////////////////////// // Lumenarium State @@ -50,16 +61,20 @@ enum AppState_None = 0, AppState_IsRunning = 1, AppState_RunEditor = 2, + AppState_RunUserSpace = 4, }; +typedef struct App_Init_Desc App_Init_Desc; struct App_Init_Desc { u32 assembly_cap; }; +typedef struct App_State App_State; struct App_State { App_State_Flags flags; + File_Async_Job_System file_async_job_system; Input_State* input_state; @@ -69,16 +84,16 @@ struct App_State Editor* editor; }; -#include "engine/lumenarium_engine_assembly.cpp" -#include "engine/lumenarium_engine.cpp" -#include "engine/lumenarium_engine_output.cpp" -#include "engine/output/lumenarium_output_uart.cpp" -#include "engine/output/lumenarium_output_sacn.cpp" -#include "editor/lumenarium_editor_ui.cpp" -#include "editor/lumenarium_editor_renderer.cpp" -#include "editor/lumenarium_editor_sculpture_visualizer.cpp" -#include "editor/lumenarium_editor.cpp" +#include "engine/lumenarium_engine_assembly.c" +#include "engine/lumenarium_engine.c" +#include "engine/lumenarium_engine_output.c" +#include "engine/output/lumenarium_output_uart.c" +#include "engine/output/lumenarium_output_sacn.c" +#include "editor/lumenarium_editor_ui.c" +#include "editor/lumenarium_editor_renderer.c" +#include "editor/lumenarium_editor_sculpture_visualizer.c" +#include "editor/lumenarium_editor.c" #endif //LUMENARIUM_FIRST_H diff --git a/src_v2/lumenarium_geometry.h b/src_v2/lumenarium_geometry.h index abfe1ce..a2d6428 100644 --- a/src_v2/lumenarium_geometry.h +++ b/src_v2/lumenarium_geometry.h @@ -23,6 +23,7 @@ enum GeoVertexBufferStorage_Color = 4 }; +typedef struct Geo_Vertex_Buffer Geo_Vertex_Buffer; struct Geo_Vertex_Buffer { r32* values; @@ -31,6 +32,7 @@ struct Geo_Vertex_Buffer u32 stride; }; +typedef struct Geo_Vertex_Buffer_Builder Geo_Vertex_Buffer_Builder; struct Geo_Vertex_Buffer_Builder { union @@ -49,12 +51,14 @@ struct Geo_Vertex_Buffer_Builder u32 cap; }; +typedef struct Geo_Index_Buffer Geo_Index_Buffer; struct Geo_Index_Buffer { u32* values; u32 len; }; +typedef struct Geo_Index_Buffer_Builder Geo_Index_Buffer_Builder; struct Geo_Index_Buffer_Builder { union @@ -71,36 +75,38 @@ struct Geo_Index_Buffer_Builder u32 cap; }; +typedef struct Geo_Quad_Buffer Geo_Quad_Buffer; struct Geo_Quad_Buffer { Geo_Vertex_Buffer buffer_vertex; Geo_Index_Buffer buffer_index; }; +typedef struct Geo_Quad_Buffer_Builder Geo_Quad_Buffer_Builder; struct Geo_Quad_Buffer_Builder { Geo_Vertex_Buffer_Builder buffer_vertex; Geo_Index_Buffer_Builder buffer_index; }; -internal Geo_Vertex_Buffer_Builder geo_vertex_buffer_builder_create(Allocator* a, u32 cap); +internal Geo_Vertex_Buffer_Builder geo_vertex_buffer_builder_create(Allocator* a, u32 cap, Geo_Vertex_Buffer_Storage storage); internal Geo_Index_Buffer_Builder geo_index_buffer_builder_create(Allocator* a, u32 cap); -internal Geo_Quad_Buffer_Builder geo_quad_buffer_builder_create(Allocator* a, u32 vertex_cap, u32 index_cap); +internal Geo_Quad_Buffer_Builder ggeo_quad_buffer_builder_create(Allocator* a, u32 vertex_cap, Geo_Vertex_Buffer_Storage storage, u32 index_cap); // Vertex Buffer -internal u32 geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t, v4 c); -internal u32 geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t); -internal u32 geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v); +internal u32 geo_vertex_buffer_builder_push_vtc(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t, v4 c); +internal u32 geo_vertex_buffer_builder_push_vt(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t); +internal u32 geo_vertex_buffer_builder_push_v(Geo_Vertex_Buffer_Builder* b, v3 v); // Index Buffer -internal u32 geo_index_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, u32 i); -internal void geo_index_buffer_builder_push_tri(Geo_Vertex_Buffer_Builder* b, u32 i0, u32 i1, u32 i2); -internal void geo_index_buffer_builder_push_quad(Geo_Vertex_Buffer_Builder* b, u32 i0, u32 i1, u32 i2, u32 i3); +internal u32 geo_index_buffer_builder_push(Geo_Index_Buffer_Builder* b, u32 i); +internal void geo_index_buffer_builder_push_tri(Geo_Index_Buffer_Builder* b, u32 i0, u32 i1, u32 i2); +internal void geo_index_buffer_builder_push_quad(Geo_Index_Buffer_Builder* b, u32 i0, u32 i1, u32 i2, u32 i3); // Quad Buffer -internal void geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3, v4 c); -internal void geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3); -internal void geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3); +internal void geo_quad_buffer_builder_push_vtc(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3, v4 c); +internal void geo_quad_buffer_builder_push_vt(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3); +internal void geo_quad_buffer_builder_push_v(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3); internal Geo_Quad_Buffer geo_quad_buffer_builder_get_static_buffer(Geo_Quad_Buffer_Builder* b); ///////////////////////////////////////////// @@ -116,26 +122,61 @@ geo_vertex_buffer_builder_stride(Geo_Vertex_Buffer_Storage storage) return result; } +#ifdef DEBUG +# define geo_vertex_buffers_validate(g) geo_vertex_buffers_validate_(g) +#else +# define geo_vertex_buffers_validate(g) +#endif + +void +geo_vertex_buffers_validate_(Geo_Vertex_Buffer_Builder* b) +{ + // before start + u32* sentinel = (u32*)(b->values - 2); + assert(sentinel[0] == 0xF0F0F0F0); + assert(sentinel[1] == 0x0F0F0F0F); + + // after end + sentinel = (u32*)(b->values + (b->cap * b->stride)); + assert(sentinel[0] == 0xFFFF000F); + assert(sentinel[1] == 0xF000FFFF); +} + internal Geo_Vertex_Buffer_Builder geo_vertex_buffer_builder_create(Allocator* a, u32 cap, Geo_Vertex_Buffer_Storage storage) { u32 stride = geo_vertex_buffer_builder_stride(storage); u32 size = cap * stride; + u32 alloc_size = size; +#ifdef DEBUG + alloc_size += 4; +#endif Geo_Vertex_Buffer_Builder result = {}; zero_struct(result); result.cap = cap; result.storage = storage; result.stride = stride; - result.values = allocator_alloc_array(a, r32, size); + result.values = allocator_alloc_array(a, r32, alloc_size); + +#ifdef DEBUG + u32* sentinel = (u32*)result.values; + sentinel[0] = 0xF0F0F0F0; + sentinel[1] = 0x0F0F0F0F; + sentinel = (u32*)(result.values + alloc_size - 2); + result.values += 2; + sentinel[0] = 0xFFFF000F; + sentinel[1] = 0xF000FFFF; +#endif + + geo_vertex_buffers_validate(&result); return result; } internal Geo_Index_Buffer_Builder geo_index_buffer_builder_create(Allocator* a, u32 cap) { - Geo_Index_Buffer_Builder result = {}; zero_struct(result); result.cap = cap; @@ -155,42 +196,46 @@ geo_quad_buffer_builder_create(Allocator* a, u32 vertex_cap, Geo_Vertex_Buffer_S // Vertex Buffer internal u32 -geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t, v4 c) +geo_vertex_buffer_builder_push_vtc(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t, v4 c) { + geo_vertex_buffers_validate(b); assert(b->len < b->cap); u32 result = b->len++; u32 offset = result * b->stride; + assert((offset + b->stride) <= (b->cap * b->stride)); + r32* at = b->values + offset; if (has_flag(b->storage, GeoVertexBufferStorage_Position)) { - b->values[offset++] = v.x; - b->values[offset++] = v.y; - b->values[offset++] = v.z; + *at++ = v.x; + *at++ = v.y; + *at++ = v.z; } if (has_flag(b->storage, GeoVertexBufferStorage_TexCoord)) { - b->values[offset++] = t.x; - b->values[offset++] = t.y; + *at++ = t.x; + *at++ = t.y; } if (has_flag(b->storage, GeoVertexBufferStorage_Color)) { - b->values[offset++] = c.x; - b->values[offset++] = c.y; - b->values[offset++] = c.z; - b->values[offset++] = c.w; + *at++ = c.x; + *at++ = c.y; + *at++ = c.z; + *at++ = c.w; } + geo_vertex_buffers_validate(b); return result; } internal u32 -geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t) +geo_vertex_buffer_builder_push_vt(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t) { - return geo_vertex_buffer_builder_push(b, v, t, v4{0}); + return geo_vertex_buffer_builder_push_vtc(b, v, t, (v4){0}); } internal u32 -geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v) +geo_vertex_buffer_builder_push_v(Geo_Vertex_Buffer_Builder* b, v3 v) { - return geo_vertex_buffer_builder_push(b, v, v2{0}, v4{0}); + return geo_vertex_buffer_builder_push_vtc(b, v, (v2){0}, (v4){0}); } @@ -224,34 +269,34 @@ geo_index_buffer_builder_push_quad(Geo_Index_Buffer_Builder* b, u32 i0, u32 i1, // Quad Buffer internal void -geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3, v4 c) +geo_quad_buffer_builder_push_vtc(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3, v4 c) { - u32 i0 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p0, t0, c); - u32 i1 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p1, t1, c); - u32 i2 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p2, t2, c); - u32 i3 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p3, t3, c); + u32 i0 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p0, t0, c); + u32 i1 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p1, t1, c); + u32 i2 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p2, t2, c); + u32 i3 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p3, t3, c); geo_index_buffer_builder_push_quad(&b->buffer_index, i0, i1, i2, i3); } internal void -geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3) +geo_quad_buffer_builder_push_vt(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3) { - u32 i0 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p0, t0, v4{}); - u32 i1 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p1, t1, v4{}); - u32 i2 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p2, t2, v4{}); - u32 i3 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p3, t3, v4{}); + u32 i0 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p0, t0, (v4){}); + u32 i1 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p1, t1, (v4){}); + u32 i2 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p2, t2, (v4){}); + u32 i3 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p3, t3, (v4){}); geo_index_buffer_builder_push_quad(&b->buffer_index, i0, i1, i2, i3); } internal void -geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3) +geo_quad_buffer_builder_push_v(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3) { - u32 i0 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p0, v2{}, v4{}); - u32 i1 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p1, v2{}, v4{}); - u32 i2 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p2, v2{}, v4{}); - u32 i3 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p3, v2{}, v4{}); + u32 i0 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p0, (v2){}, (v4){}); + u32 i1 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p1, (v2){}, (v4){}); + u32 i2 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p2, (v2){}, (v4){}); + u32 i3 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p3, (v2){}, (v4){}); geo_index_buffer_builder_push_quad(&b->buffer_index, i0, i1, i2, i3); } diff --git a/src_v2/lumenarium_input.cpp b/src_v2/lumenarium_input.cpp index 9b6344b..d983076 100644 --- a/src_v2/lumenarium_input.cpp +++ b/src_v2/lumenarium_input.cpp @@ -1,4 +1,7 @@ + +// TODO(PS) @DEPRECATE - new os layer + #define INPUT_FRAME_STRING_LENGTH 32 struct Input_Frame { diff --git a/src_v2/lumenarium_memory.cpp b/src_v2/lumenarium_memory.cpp index ea32b7b..71e4f94 100644 --- a/src_v2/lumenarium_memory.cpp +++ b/src_v2/lumenarium_memory.cpp @@ -1,3 +1,4 @@ +// TODO(PS) @DEPRECATE - new os layer ///////////////////////////////////////// // Memory Functions @@ -467,6 +468,7 @@ paged_allocator_free(Allocator* allocator, u8* base, u64 size) else { // free list is empty + region->next = 0; paged->free_first = region; } } diff --git a/src_v2/lumenarium_memory.h b/src_v2/lumenarium_memory.h index 8b6a79f..b18a813 100644 --- a/src_v2/lumenarium_memory.h +++ b/src_v2/lumenarium_memory.h @@ -1,3 +1,5 @@ +// TODO(PS) @DEPRECATE - new os layer + /* date = April 6th 2022 7:55 pm */ #ifndef LUMENARIUM_MEMORY_H diff --git a/src_v2/lumenarium_tests.cpp b/src_v2/lumenarium_tests.cpp index 6379b7b..1c08d84 100644 --- a/src_v2/lumenarium_tests.cpp +++ b/src_v2/lumenarium_tests.cpp @@ -1,9 +1,9 @@ -Platform_Thread_Result -thread_proc(Platform_Thread_Data* td) +Thread_Result +thread_proc(Thread_Data* td) { //Sleep(100); - return {}; + return (Thread_Result){}; } void @@ -75,15 +75,15 @@ memory_tests() size = KB(4); #endif - u8* base = platform_mem_reserve(size); - platform_mem_commit(base, KB(4)); + u8* base = os_mem_reserve(size); + os_mem_commit(base, KB(4)); base[4095] = 200; assert(base[4095] == 200); - platform_mem_commit(base + KB(4), KB(4)); + os_mem_commit(base + KB(4), KB(4)); base[5000] = 200; assert(base[5000] == 200); - platform_mem_decommit(base, KB(8)); - platform_mem_release(base, GB(32)); + os_mem_decommit(base, KB(8)); + os_mem_release(base, GB(32)); } Allocator* bump = bump_allocator_create_reserve(KB(32)); @@ -155,11 +155,11 @@ run_tests() } - assert(round_up_to_pow2(1) == 1); - assert(round_up_to_pow2(3) == 4); - assert(round_up_to_pow2(29) == 32); - assert(round_up_to_pow2(32) == 32); - assert(round_up_to_pow2(120) == 128); + assert(round_up_to_pow2_u32(1) == 1); + assert(round_up_to_pow2_u32(3) == 4); + assert(round_up_to_pow2_u32(29) == 32); + assert(round_up_to_pow2_u32(32) == 32); + assert(round_up_to_pow2_u32(120) == 128); memory_tests(); bsp_tests(); @@ -172,7 +172,7 @@ run_tests() // testing strings and exe path - String exe_file_path = platform_get_exe_path(scratch.a); + String exe_file_path = os_get_exe_path(scratch.a); 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; @@ -180,18 +180,18 @@ run_tests() String run_tree_path = string_get_prefix(exe_file_path, run_tree_end); String run_tree_path_nullterm = string_copy(run_tree_path, scratch.a); assert(run_tree_path_nullterm.len > 0); - assert(platform_pwd_set(run_tree_path_nullterm)); + assert(os_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.a); + File_Handle f = os_file_open(lit_str("text.txt"), FileAccess_Read | FileAccess_Write, FileCreate_OpenExisting); + File_Info i = os_file_get_info(f, scratch.a); - Data d0 = platform_file_read_all(f, scratch.a); + Data d0 = os_file_read_all(f, scratch.a); assert(d0.size > 0); String s = lit_str("foooooooooobbbbbbaaaarrrrrr"); Data d1 = { s.str, s.len }; - bool r = platform_file_write_all(f, d1); + bool r = os_file_write_all(f, d1); assert(r); #if 0 @@ -211,5 +211,5 @@ run_tests() platform_thread_end(threads[j]); } #endif - + scratch_release(scratch); } \ No newline at end of file diff --git a/src_v2/lumenarium_texture_atlas.cpp b/src_v2/lumenarium_texture_atlas.c similarity index 96% rename from src_v2/lumenarium_texture_atlas.cpp rename to src_v2/lumenarium_texture_atlas.c index c60b7fe..92cc764 100644 --- a/src_v2/lumenarium_texture_atlas.cpp +++ b/src_v2/lumenarium_texture_atlas.c @@ -3,6 +3,7 @@ #ifndef LUMENARIUM_TEXTURE_ATLAS_CPP #define LUMENARIUM_TEXTURE_ATLAS_CPP +typedef struct Texture_Atlas_Sprite Texture_Atlas_Sprite; struct Texture_Atlas_Sprite { u16 min_x; @@ -13,6 +14,7 @@ struct Texture_Atlas_Sprite v2 draw_offset; }; +typedef struct Texture_Atlas Texture_Atlas; struct Texture_Atlas { u8* pixels; @@ -38,7 +40,7 @@ texture_atlas_create(u32 width, u32 height, u32 cap, Allocator* allocator) result.height = (u16)height; for (u32 i = 0; i < width * height; i++) { u8* base = result.pixels + (i * 4); - *(u32*)base = 0x00FFFFFF; + *(u32*)base = 0xFF00FFFF; } result.ids = allocator_alloc_array(allocator, u32, cap); @@ -180,7 +182,7 @@ texture_atlas_sprite_get_uvs(Texture_Atlas* ta, Texture_Atlas_Sprite sprite) } internal v4 -texture_atlas_sprite_get_uvs(Texture_Atlas* ta, u32 id) +texture_atlas_sprite_id_get_uvs(Texture_Atlas* ta, u32 id) { Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(ta, id); return texture_atlas_sprite_get_uvs(ta, sprite); diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h index 988b636..85e32dc 100644 --- a/src_v2/lumenarium_types.h +++ b/src_v2/lumenarium_types.h @@ -1,3 +1,5 @@ +// TODO(PS) @DEPRECATE - new os layer + /* date = March 22nd 2022 2:08 am */ #ifndef LUMENARIUM_TYPES_H diff --git a/src_v2/platform/lumenarium_os.h b/src_v2/platform/lumenarium_os.h new file mode 100644 index 0000000..b83cf91 --- /dev/null +++ b/src_v2/platform/lumenarium_os.h @@ -0,0 +1,58 @@ +#if !defined(LUMENARIUM_OS) +#define LUMENARIUM_OS + +/////////////////////////////////////// +// Memory + +u8* os_mem_reserve(u64 size); +u8* os_mem_commit(u8* base, u64 size); +bool os_mem_decommit(u8* base, u64 size); +bool os_mem_release(u8* base, u64 size); + +/////////////////////////////////////// +// File I/O + +File_Async_Job_System os_file_jobs_init(); +File_Handle os_file_open(String path, File_Access_Flags flags_access, File_Create_Flags flags_create); +void os_file_close(File_Handle file_handle); +File_Info os_file_get_info(File_Handle file_handle, Allocator* allocator); +Data os_file_read_all(File_Handle file_handle, Allocator* allocator); +bool os_file_write_all(File_Handle file_handle, Data file_data); + +String os_get_exe_path(Allocator* allocator); +bool os_pwd_set(String path); + +File_Info_List os_dir_enum(String path, Platform_Enum_Dir_Flags flags, Allocator* allocator); + +// Asnyc Jobs +void os_file_async_work_on_job(File_Async_Job* job); + +/////////////////////////////////////// +// Time + +Ticks os_get_ticks(); +r64 os_get_ticks_per_second(); + +/////////////////////////////////////// +// Threads + +Thread_Handle os_thread_begin(Thread_Proc* proc, u8* user_data); +void os_thread_end(Thread_Handle thread_handle); + +u32 os_interlocked_increment(volatile u32* value); +u32 os_interlocked_cmp_exchg(volatile u32* dest, u32 new_value, u32 old_value); + +/////////////////////////////////////// +// Network Access + +Socket_Handle os_socket_create(); +bool os_socket_bind(); +bool os_socket_connect(); +bool os_socket_close(); +Data os_socket_recv(); +s32 os_Socket_set_listening(); +s32 os_Socket_send(); +s32 os_Socket_send_to(); +s32 os_Socket_set_opt(); + +#endif // LUMENARIUM_OS \ No newline at end of file diff --git a/src_v2/platform/osx/lumenarium_first_osx.c b/src_v2/platform/osx/lumenarium_first_osx.c new file mode 100644 index 0000000..ccbc76b --- /dev/null +++ b/src_v2/platform/osx/lumenarium_first_osx.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#include + +#include "lumenarium_osx_memory.h" +#include "../../core/lumenarium_core.h" +#include "../lumenarium_os.h" +#include "../../lumenarium_first.c" + +#undef internal +#undef external + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../libs/glfw_osx/include/GLFW/glfw3.h" + +#define osx_err_print(sub_proc) osx_err_print_((char*)__FUNCTION__, (char*)(sub_proc), errno) +void +osx_err_print_(char* proc, char* sub_proc, s32 errsv) +{ + printf("Error: %s:%s - %d\n\t%s\n\n", proc, sub_proc, errsv, strerror(errsv)); +} + +#define OS_FILE_HANDLE_TYPE s32 +#define OS_FILE_MAX_PATH PATH_MAX +#define OS_FILE_INVALID_HANDLE -1 +#include "../shared/lumenarium_shared_file_tracker.h" +#include "../shared/lumenarium_shared_file_async_work_on_job.h" +#include "lumenarium_osx_file.h" +#include "lumenarium_osx_time.h" +#include "lumenarium_osx_graphics.h" + +void osx_tests() +{ + Ticks t0 = os_get_ticks(); + + // File Tests + File_Handle file = os_file_open(lit_str("text.txt"), FileAccess_Read | FileAccess_Write, FileCreate_OpenAlways); + File_Info info = os_file_get_info(file, global_scratch_); + Data d = os_file_read_all(file, global_scratch_); + os_file_write_all(file, d); + os_file_close(file); + + // Path tests + String path_exe = os_get_exe_path(global_scratch_); + printf("%.*s\n", str_varg(path_exe)); + String path = string_chop_last_slash(path_exe); + String path0 = string_copy(path, global_scratch_); + os_pwd_set(path0); + + Ticks t1 = os_get_ticks(); + Ticks td = get_ticks_elapsed(t0, t1); + r64 sd = ticks_to_seconds(td, os_get_ticks_per_second()); +} + +void +glfw_error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +global u8* app_state_data = 0; + +global Key_Code glfw_key_translation_table[] = { + +}; + +Key_Code +osx_translate_key(int glfw_key) +{ + // TODO: turn this into an actual key_code + return (Key_Code)glfw_key; +} + +Key_Code +osx_translate_mouse_button(int glfw_button) +{ + switch (glfw_button) + { + case GLFW_MOUSE_BUTTON_LEFT: return KeyCode_MouseLeftButton; break; + case GLFW_MOUSE_BUTTON_RIGHT: return KeyCode_MouseRightButton; break; + case GLFW_MOUSE_BUTTON_MIDDLE: return KeyCode_MouseMiddleButton; break; + invalid_default_case; + } + return 0; +} + +void +button_event(Key_Code key, int action, int mods) +{ + Window_Event evt = { + .kind = WindowEvent_ButtonDown, + .key_code = key, + }; + + if (has_flag(mods, GLFW_MOD_SHIFT)) add_flag(evt.key_flags, KeyFlag_Mod_Shift); + if (has_flag(mods, GLFW_MOD_CONTROL)) add_flag(evt.key_flags, KeyFlag_Mod_Shift); + if (has_flag(mods, GLFW_MOD_ALT)) add_flag(evt.key_flags, KeyFlag_Mod_Shift); + + switch (action) + { + case GLFW_PRESS: { evt.key_flags = KeyFlag_State_IsDown; } break; + case GLFW_REPEAT: { + evt.key_flags = KeyFlag_State_IsDown | KeyFlag_State_WasDown; + } break; + case GLFW_RELEASE: { + evt.key_flags = KeyFlag_State_WasDown; + } break; + invalid_default_case; + } + lumenarium_event(evt, (App_State*)app_state_data); +} + +void +key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + Key_Code kc = osx_translate_key(key); + button_event(kc, action, mods); +} + +void +cursor_position_callback(GLFWwindow* window, double xpos, double ypos) +{ + Window_Event evt = { + .kind = WindowEvent_MouseMoved, + .mouse_x = (u32)xpos, + .mouse_y = (u32)ypos, + }; + lumenarium_event(evt, (App_State*)app_state_data); +} + +void +mouse_button_callback(GLFWwindow* window, int button, int action, int mods) +{ + Key_Code kc = osx_translate_mouse_button(button); + button_event(kc, action, mods); +} + +void +scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + Window_Event evt = {}; + evt.kind = WindowEvent_MouseScroll; + evt.scroll_amt = xoffset; + lumenarium_event(evt, (App_State*)app_state_data); +} + +int main (int arg_count, char** args) +{ + // osx_tests(); + + if (!glfwInit()) + { + printf("Error: Could not initialize glfw.\n"); + return 1; + } + glfwSetErrorCallback(glfw_error_callback); + + glfwWindowHint(GLFW_DOUBLEBUFFER, true); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 1); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + GLFWwindow* window = glfwCreateWindow(1400, 700, "Lumenarium", NULL, NULL); + if (!window) + { + printf("Error: Unable to create a glfw window\n"); + return 1; + } + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + gl = osx_load_opengl_ext(); + + // Input Callbacks + glfwSetKeyCallback(window, key_callback); + glfwSetMouseButtonCallback(window, mouse_button_callback); + glfwSetCursorPosCallback(window, cursor_position_callback); + glfwSetScrollCallback(window, scroll_callback); + + App_State* state = lumenarium_init(); + app_state_data = (u8*)state; + + if (has_flag(state->flags, AppState_RunEditor)) + { + float xscale, yscale; + glfwGetWindowContentScale(window, &xscale, &yscale); + state->editor->content_scale = (v2){ xscale, yscale }; + } + + bool running = true; + r64 target_seconds_per_frame = 1.0 / 30.0f; + Ticks ticks_start = os_get_ticks(); + while(!glfwWindowShouldClose(window) && running && has_flag(state->flags, AppState_IsRunning)) { + + if (has_flag(state->flags, AppState_RunEditor)) + { + s32 w, h; + glfwGetWindowSize(window, &w, &h); + state->editor->window_dim = (v2){ (r32)w, (r32)h }; + } + + lumenarium_frame_prepare(state); + lumenarium_frame(state); + lumenarium_env_validate(); + + glfwSwapBuffers(window); + glfwPollEvents(); + + Ticks ticks_end = os_get_ticks(); + r64 seconds_elapsed = get_seconds_elapsed(ticks_start, ticks_end, os_get_ticks_per_second()); + while (seconds_elapsed < target_seconds_per_frame) + { + u32 sleep_time = (u32)(1000.0f * (target_seconds_per_frame - seconds_elapsed)); + usleep(sleep_time); + + ticks_end = os_get_ticks(); + seconds_elapsed = get_seconds_elapsed(ticks_start, ticks_end, os_get_ticks_per_second()); + } + ticks_start = ticks_end; + } + + lumenarium_cleanup(state); + glfwDestroyWindow(window); + glfwTerminate(); + return 0; +} \ No newline at end of file diff --git a/src_v2/platform/osx/lumenarium_first_osx.cpp b/src_v2/platform/osx/lumenarium_first_osx.cpp deleted file mode 100644 index 49d4820..0000000 --- a/src_v2/platform/osx/lumenarium_first_osx.cpp +++ /dev/null @@ -1,28 +0,0 @@ - -#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_file.h b/src_v2/platform/osx/lumenarium_osx_file.h new file mode 100644 index 0000000..d33d261 --- /dev/null +++ b/src_v2/platform/osx/lumenarium_osx_file.h @@ -0,0 +1,213 @@ +File_Async_Job_System +os_file_jobs_init() +{ + open_files_init(); + File_Async_Job_System result = file_async_jobs_init(os_file_async_work_on_job); + return result; +} + +File_Handle +os_file_open(String path, File_Access_Flags flags_access, File_Create_Flags flags_create) +{ + File_Handle result = {}; + + s32 flags = 0; + if (has_flag_exact(flags_access, (FileAccess_Read | FileAccess_Write))) + { + add_flag(flags, O_RDWR); + } + else + { + if (has_flag(flags_access, FileAccess_Read)) + { + add_flag(flags, O_RDONLY); + } + else if (has_flag(flags_access, FileAccess_Write)) + { + add_flag(flags, O_WRONLY); + } + else + { + return result; + } + } + + switch (flags_create) + { + case FileCreate_New: { add_flag(flags, O_CREAT | O_EXCL ); } break; + case FileCreate_CreateAlways: { add_flag(flags, O_CREAT); } break; + case FileCreate_OpenExisting: { /* add_flag(flags, O_); */ } break; + case FileCreate_OpenAlways: { /* add_flag(flags, O_); */ } break; + invalid_default_case; + } + + s32 file_handle = open((char*)path.str, flags); + if (file_handle >= 0) + { + result = open_files_put_handle(file_handle, path); + } + else + { + s32 errsv = errno; + printf("Error: os_file_open - %d\n", errsv); + printf("\tAttempting to open: %.*s\n", str_varg(path)); + printf("\tFlags: %u %u\n", flags_access, flags_create); + } + return result; +} + +void +os_file_close(File_Handle file_handle) +{ + s32 os_handle = open_files_get_handle(file_handle); + if (close(os_handle) != -1) + { + open_files_rem_file(file_handle); + } + else + { + s32 errsv = errno; + printf("Error: os_file_close - %d\n", errsv); + } +} + +File_Info +os_file_get_info(File_Handle file_handle, Allocator* allocator) +{ + File_Info info = {}; + s32 os_handle = open_files_get_handle(file_handle); + if (os_handle != -1) + { + String path = open_files_get_path(file_handle); + + struct stat os_info = {}; + if (fstat(os_handle, &os_info) != -1) + { + info.path = string_copy(path, allocator); + info.path_abs = allocator_alloc_string(allocator, PATH_MAX); + if (realpath((char*)path.str, (char*)info.path_abs.str) != 0) + { + info.path_abs.len = c_str_len((char*)info.path_abs.str); + } + else + { + s32 errsv = errno; + printf("Error - os_file_get_info - %d - realpath\n", errsv); + } + info.size = (u64)os_info.st_size; + if (S_ISDIR(os_info.st_mode)) + { + add_flag(info.flags, FileFlag_IsDir); + } + else if (!S_ISREG(os_info.st_mode)) + { + printf("Error - os_file_get_info - stat-ing a handle that is not a directory or a file\n"); + } + } + else + { + s32 errsv = errno; + printf("Error: os_file_get_info - %d\n", errsv); + } + } + return info; +} + +Data +os_file_read_all(File_Handle file_handle, Allocator* allocator) +{ + Data result = {}; + s32 os_handle = open_files_get_handle(file_handle); + if (os_handle == -1) return result; + + // get file size + s32 offset = lseek(os_handle, 0, SEEK_END); + if (offset == -1) + { + s32 errsv = errno; + printf("Error: os_file_read_all:lseek - %d\n", errsv); + return result; + } + lseek(os_handle, 0, SEEK_SET); + + result.base = allocator_alloc(allocator, offset + 1); + result.size = offset + 1; + + s32 bytes_read = read(os_handle, result.base, result.size); + if (bytes_read == (result.size - 1)) + { + result.base[bytes_read] = 0; // null term + } + else if (bytes_read >= 0) + { + printf("Error: os_file_read:read - whole file not read.\n"); + } + else if (bytes_read == -1) + { + s32 errsv = errno; + printf("Error: os_file_read_all:read - %d\n", errsv); + } + + return result; +} + +bool +os_file_write_all(File_Handle file_handle, Data file_data) +{ + s32 os_handle = open_files_get_handle(file_handle); + if (os_handle == -1) return false; + + lseek(os_handle, 0, SEEK_SET); + s32 size_written = write(os_handle, file_data.base, file_data.size); + if (size_written > 0 && size_written != file_data.size) + { + printf("Error: os_file_write_all:write - whole file not written\n"); + return true; + } + else if (size_written < 0) + { + osx_err_print("write"); + return false; + } + + return true; +} + +String +os_get_exe_path(Allocator* allocator) +{ + u32 needed = 0; + _NSGetExecutablePath(0, &needed); + + String result = allocator_alloc_string(allocator, needed + 1); + + u32 cap = (u64)result.cap; + s32 r = _NSGetExecutablePath((char*)result.str, &cap); + if (r == 0) + { + result.len = cap; + result.str[result.len] = 0; + } + + return result; +} + +bool +os_pwd_set(String path) +{ + s32 result = chdir((char*)path.str); + if (result == -1) + { + osx_err_print("chdir"); + return false; + } + return true; +} + +#if 0 +File_Info_List +os_dir_enum(String path, Platform_Enum_Dir_Flags flags, Allocator* allocator) +{ + +} +#endif diff --git a/src_v2/platform/osx/lumenarium_osx_graphics.h b/src_v2/platform/osx/lumenarium_osx_graphics.h new file mode 100644 index 0000000..289051f --- /dev/null +++ b/src_v2/platform/osx/lumenarium_osx_graphics.h @@ -0,0 +1,58 @@ +#define OSX_GL_ERROR_CASE(e) case e: { result = #e; } break +char* +osx_gl_error_to_string(u32 error) +{ + char* result = 0; + switch (error) + { + OSX_GL_ERROR_CASE(GL_INVALID_VALUE); + OSX_GL_ERROR_CASE(GL_INVALID_ENUM ); + OSX_GL_ERROR_CASE(GL_INVALID_OPERATION); + default: { result = "unknown"; } + } + return result; +} + +void +os_gl_no_error_(char* file, u32 line) { + u32 error = glGetError(); + char* str = 0; + if (error) { + str = osx_gl_error_to_string(error); + } + if (error != 0) + { + fprintf(stderr, "OpenGL error: %s:%d\n\t%s :: %d\n", file, line, str, error); + invalid_code_path; + } +} + +#define load_ext(r,n) r.n = (proc_##n*)glfwGetProcAddress(#n); assert((r.n) != 0) +OpenGL_Extensions +osx_load_opengl_ext() +{ + OpenGL_Extensions result = {}; + load_ext(result, glGenVertexArrays); + load_ext(result, glBindVertexArray); + load_ext(result, glGenBuffers); + load_ext(result, glBindBuffer); + load_ext(result, glBufferData); + load_ext(result, glBufferSubData); + load_ext(result, glCreateShader); + load_ext(result, glShaderSource); + load_ext(result, glCompileShader); + load_ext(result, glCreateProgram); + load_ext(result, glAttachShader); + load_ext(result, glLinkProgram); + load_ext(result, glUseProgram); + load_ext(result, glGetAttribLocation); + load_ext(result, glVertexAttribPointer); + load_ext(result, glEnableVertexAttribArray); + load_ext(result, glGetShaderiv); + load_ext(result, glGetShaderInfoLog); + load_ext(result, glGetProgramiv); + load_ext(result, glGetProgramInfoLog); + load_ext(result, glGetUniformLocation); + load_ext(result, glUniformMatrix4fv); + return result; +} \ 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 deleted file mode 100644 index 0ae2d54..0000000 --- a/src_v2/platform/osx/lumenarium_osx_memory.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#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/osx/lumenarium_osx_memory.h b/src_v2/platform/osx/lumenarium_osx_memory.h new file mode 100644 index 0000000..388b9f9 --- /dev/null +++ b/src_v2/platform/osx/lumenarium_osx_memory.h @@ -0,0 +1,41 @@ +// TODO(PS): come back and do this properly +#define OS_MEM_PAGE_SIZE 4096 + +size_t +osx_round_to_page_size(uint64_t size) +{ + uint64_t rem = size % OS_MEM_PAGE_SIZE; + if (rem != 0 || size < OS_MEM_PAGE_SIZE) + { + uint64_t grow = OS_MEM_PAGE_SIZE - rem; + size += grow; + } + return (size_t)size; +} + +uint8_t* +os_mem_reserve(uint64_t size) +{ + size_t size_cvt = osx_round_to_page_size(size); + uint8_t* result = (uint8_t*)malloc(size_cvt); + return result; +} + +uint8_t* +os_mem_commit(uint8_t* base, uint64_t size) +{ + return base; +} + +bool +os_mem_decommit(uint8_t* base, uint64_t size) +{ + return 1; // true +} + +bool +os_mem_release(uint8_t* base, uint64_t size) +{ + free(base); + return 1; // true +} \ No newline at end of file diff --git a/src_v2/platform/osx/lumenarium_osx_time.cpp b/src_v2/platform/osx/lumenarium_osx_time.cpp deleted file mode 100644 index 39c1331..0000000 --- a/src_v2/platform/osx/lumenarium_osx_time.cpp +++ /dev/null @@ -1,4 +0,0 @@ - -// I think this SO post might be helpful -// https://stackoverflow.com/questions/30575995/multi-platform-equivalent-to-queryperformancecounter - diff --git a/src_v2/platform/osx/lumenarium_osx_time.h b/src_v2/platform/osx/lumenarium_osx_time.h new file mode 100644 index 0000000..eedc719 --- /dev/null +++ b/src_v2/platform/osx/lumenarium_osx_time.h @@ -0,0 +1,22 @@ + +// I think this SO post might be helpful +// https://stackoverflow.com/questions/30575995/multi-platform-equivalent-to-queryperformancecounter + +Ticks +os_get_ticks() +{ + Ticks result = { + .value = mach_absolute_time() + }; + return result; +} + +r64 +os_get_ticks_per_second() +{ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + r64 to_nanos = (r64)info.numer / (r64)info.denom; + r64 to_secs = to_nanos / 10e9; + return to_secs; +} \ No newline at end of file diff --git a/src_v2/platform/shared/lumenarium_shared_file_async_work_on_job.h b/src_v2/platform/shared/lumenarium_shared_file_async_work_on_job.h new file mode 100644 index 0000000..cfe29f6 --- /dev/null +++ b/src_v2/platform/shared/lumenarium_shared_file_async_work_on_job.h @@ -0,0 +1,36 @@ +#if defined(PLATFORM_win32) || defined(PLATFORM_osx) + +void +os_file_async_work_on_job(File_Async_Job* job) +{ + File_Handle file = {}; + if (has_flag(job->args.flags, FileAsyncJob_Read)) + { + file = os_file_open(job->args.path, FileAccess_Read, FileCreate_OpenExisting); + Data result = os_file_read_all(file, file_jobs_arena); + if (result.base != 0) + { + job->args.data = result; + add_flag(job->args.flags, FileAsyncJob_Success); + } + else + { + add_flag(job->args.flags, FileAsyncJob_Failed); + } + } + else if (has_flag(job->args.flags, FileAsyncJob_Write)) + { + file = os_file_open(job->args.path, FileAccess_Write, FileCreate_OpenAlways); + if (os_file_write_all(file, job->args.data)) + { + add_flag(job->args.flags, FileAsyncJob_Success); + } + else + { + add_flag(job->args.flags, FileAsyncJob_Failed); + } + } + os_file_close(file); +} + +#endif // defined(win32) || defined(osx) \ No newline at end of file diff --git a/src_v2/platform/shared/lumenarium_shared_file_tracker.h b/src_v2/platform/shared/lumenarium_shared_file_tracker.h new file mode 100644 index 0000000..779e445 --- /dev/null +++ b/src_v2/platform/shared/lumenarium_shared_file_tracker.h @@ -0,0 +1,106 @@ +#if !defined(LUMENARIUM_SHARED_FILE_TRACKER_H) +#define LUMENARIUM_SHARED_FILE_TRACKER_H + +#if !defined(OS_FILE_HANDLE_TYPE) +# error "You must define an OS_FILE_HANDLE_TYPE" +#endif + +#if !defined(OS_FILE_MAX_PATH) +# error "You must define an OS_FILE_MAX_PATH" +#endif + +#if !defined(OS_FILE_INVALID_HANDLE) +# error "You must define an OS_FILE_INVALID_HANDLE" +#endif + +#define open_files_cap 32 +global u64 open_files_len = 1; // zero is invalid +global char open_file_paths[open_files_cap][OS_FILE_MAX_PATH]; +global u64 open_file_paths_len[open_files_cap]; +global OS_FILE_HANDLE_TYPE open_files[open_files_cap]; + +void open_files_init(); +bool open_files_has_room(); +OS_FILE_HANDLE_TYPE open_files_get_handle(File_Handle handle); +String open_files_get_path(File_Handle handle); +File_Handle open_files_put_handle(OS_FILE_HANDLE_TYPE os_handle, String path); +void open_files_rem_file(File_Handle handle); + +//////////////////////////////////////////////// +// IMPLEMENTATION + +void +open_files_init() +{ + for (u32 i = 0; i < open_files_cap; i++) + { + open_files[i] = OS_FILE_INVALID_HANDLE; + } +} + +bool open_files_has_room() { return open_files_len < open_files_cap; } + +OS_FILE_HANDLE_TYPE +open_files_get_handle(File_Handle handle) +{ + assert(handle.value < open_files_len); + return open_files[handle.value]; +} + +String +open_files_get_path(File_Handle handle) +{ + assert(handle.value < open_files_len); + String result = { + .str = (u8*)open_file_paths[handle.value], + .len = open_file_paths_len[handle.value], + .cap = open_file_paths_len[handle.value], + }; + return result; +} + +File_Handle +open_files_put_handle(OS_FILE_HANDLE_TYPE os_handle, String path) +{ + assert(path.len < OS_FILE_MAX_PATH); + + File_Handle result = {}; + if (os_handle != OS_FILE_INVALID_HANDLE) + { + if (open_files_has_room()) + { + result.value = open_files_len++; + } + else + { + // search for emtpy index + for (u32 i = 1; i < open_files_cap; i++) + { + if (open_files[i] == OS_FILE_INVALID_HANDLE) + { + result.value = i; + } + } + } + + if (result.value != 0) + { + open_files[result.value] = os_handle; + memory_copy(path.str, (u8*)open_file_paths[result.value], path.len); + open_file_paths[result.value][path.len] = 0; // null term + open_file_paths_len[result.value] = path.len; + } + } + return result; +} + +void +open_files_rem_file(File_Handle handle) +{ + assert(handle.value < open_files_len); + open_files[handle.value] = OS_FILE_INVALID_HANDLE;; +} + + + +#endif // LUMENARIUM_SHARED_FILE_TRACKER_H \ 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 index df682e2..e90418e 100644 --- a/src_v2/platform/win32/lumenarium_first_win32.cpp +++ b/src_v2/platform/win32/lumenarium_first_win32.cpp @@ -268,7 +268,7 @@ WinMain( // Update window size if (has_flag(state->flags, AppState_RunEditor)) { - state->editor->window_dim = v2{ + state->editor->window_dim = (v2){ (r32)win32_main_window.info.width, (r32)win32_main_window.info.height }; diff --git a/src_v2/platform/win32/lumenarium_win32_file.cpp b/src_v2/platform/win32/lumenarium_win32_file.cpp index 6db4fc4..5ca09e3 100644 --- a/src_v2/platform/win32/lumenarium_win32_file.cpp +++ b/src_v2/platform/win32/lumenarium_win32_file.cpp @@ -255,37 +255,4 @@ platform_pwd_set(String path) bool result = SetCurrentDirectory((char*)path.str); if (!result) win32_get_last_error(); return result; -} - -void -platform_file_async_work_on_job(Platform_File_Async_Job* job) -{ - Platform_File_Handle file = {}; - if (has_flag(job->args.flags, PlatformFileAsyncJob_Read)) - { - file = platform_file_open(job->args.path, FileAccess_Read, FileCreate_OpenExisting); - Data result = platform_file_read_all(file, platform_file_jobs_arena); - if (result.base != 0) - { - job->args.data = result; - add_flag(job->args.flags, PlatformFileAsyncJob_Success); - } - else - { - add_flag(job->args.flags, PlatformFileAsyncJob_Failed); - } - } - else if (has_flag(job->args.flags, PlatformFileAsyncJob_Write)) - { - file = platform_file_open(job->args.path, FileAccess_Write, FileCreate_OpenAlways); - if (platform_file_write_all(file, job->args.data)) - { - add_flag(job->args.flags, PlatformFileAsyncJob_Success); - } - else - { - add_flag(job->args.flags, PlatformFileAsyncJob_Failed); - } - } - platform_file_close(file); } \ No newline at end of file diff --git a/src_v2/scratch/lumenarium b/src_v2/scratch/lumenarium new file mode 100755 index 0000000000000000000000000000000000000000..3c26db2ad3deebc549e2809fdc2194faa4051ce0 GIT binary patch literal 34023 zcmeI5e{2**6vyA)wRojq4~kYrLnVly(5--4{Kb}1`BkAgsy3RixnB3$v-gAUT`9Fy zSE~s^qSk2O50w~W6jK5cNN8dMm?$wu1^>`QA{I^bsxc7_#;d{e`*!!r_CWnZ<6rN= zyLoSB-rJea4E#4UpN#$Z#}pzDLA=lz&_FTKQ&b6y=t}4+sHQd6uB?BcKDeH1CyEt3 zvFZYk^K3;&O>3%eZkpI^#j6wR$nh~0Vl8r-H7%`gNjnaT!hDDBv?nv@42pf8*SHjs zIc~$yv{bsQB{g1En6Gu2osSu?u|9Fe%%IpHB@I4ptPi*^r4cK zu?C=;)|PZF1N@8Ut3h8&R6E}BXc7$8yqw=>4Xf9!tzWS^zemi1alvQ#&e;$=9%reo z`XxjrkB?XRIEuxNe;j@_d}eb6)c-iqG?a}`;0FnKJsP1ben+|QWauT(zmh8uKlLJw zm!hr`%KJj;N*PKl+M=}f#9HCA%!Kkd_W!=%?%4}&Dj%-eGxMPthu>L*x=JX91mS;~ zQ!Ja=6CLy3fM08VmQOSf^W!|c&)ve`ZH96i&%@5>A!S2PDy_$rhG>fsGJ2G|jZj?g zP8uC4W&I{Sv9h5mlCM$j(~VR#nMf(MP8+gZn76{p{Wp2*lHp|N;%TiW9-Ng|ML9lR zZ#lejhg~upidh6fal?v+MGF^IRpSqMMqAJg!sDuej>pq1{I`Mm=Gk+kHTA0H?zp*# zv3Ndiulsz*=l#KQ4V2p(`-yhgi^A0+LO=)z0U;m+gn$qb0zyCt2mv7=1cZPP5CTF# z2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+gn$qb0zyCt2mv7=1cZPP5CTF# z2nYcoAOwVf5V(LquFiYnT(5ukIiGg`=T-OlFH^IAkD7h6f7WlgWi&K$I}M$lNm(i~ zgAt+^(5G`7QPVs!gBi51vyXZ|NBiy?_&WD`@U+kSmT#{aeDZ!X_#XOfK+H%5Wiu6O zb}P}re4L6nziED&DW~j6xteX}er1%+m7;GckD=(yat}4JJzAz_zehRWGkNzuN$1hWdF#aJo^WFX0Z+=h753l0eBcb$kTCV&E0U;m+gn$qb0zyCt z2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+gn$qb0zyCt z2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@{~3WwWNL)Qp#0Xk`2F~?UUVh z%?dph>aoq$iD=rk8k4bD{boG@SJP7IP`E=2w|8ifP&78NrmZVvw2~GN#bU`Y2BA?g zd9`@Erf<>1U1?ow*F&wkLD$AZ(F9$`7aaKLP_HxWyzv9VEY&79GA~^bD)E4K)6_kAGhD?v~fERtIUJjAs}3RIkXH$K0n{_0N1{j z0d`e^k2^9ZwAU5*LE9f+PM$0Bjr-jNKEHck3gvgz`F(qS&z?tjvHCKn4Fy$VaV}(! z)dBneVfmGNQ+Tvq*@VAF@*zE(PATVa%}`QCSlhfvNrjD2I^3>MT2G~`!lY~t8A>D> z!!AiFx2Bc$$Sob|O&y8F)h*TCk($n%J3Hb=TimFIv?Y`#h2s|9y7;E0HF{N&w;m?v z$k?eVM?iK5T;R222I{c9o__KIx7S)~Y*fP4DVCHF^*2^}E!0lc-xX@U(Gxp=AIMl{ z)7fdqk8S;V(g#b{zWCtJyJ*MMH5-3vJbh2i+`~Pel<#?LaDQ|1{n5ygqR&s9=G!XS8W2KaBQHx&GkMf6D)F+iT~3^zoe)zuvaJ d=-Y2LRzKYL=assemblies, ah, 123); assembly_strip_create_leds( - &state->assemblies, - ah, - vertical_strip, - start_p, - v3{0, INCENTER_FEET(-6.5f), 0}, - 123 - ); + &state->assemblies, + ah, + vertical_strip, + start_p, + (v3){0, INCENTER_FEET(-6.5f), 0}, + 123 + ); r32 radius = INCENTER_FEET(10); - Random_Series rand = random_series_create(hash_djb2_to_u32("slkdjfalksdjf")); + Random_Series rand = random_series_create(hash_djb2_cstr_to_u32("slfalksdjf")); for (u32 i = 0; i < 40; i++) { Assembly_Strip* strip = assembly_add_strip(&state->assemblies, ah, 123); @@ -53,7 +53,9 @@ incenter_init(App_State* state) assembly_strip_create_leds(&state->assemblies, ah, strip, start_p, end_p, 123); } - ed_sculpture_updated(state, 5, 0.025f); + r32 rad = 0.05f; + ed_sculpture_updated(state, 10, rad); + scratch_release(scratch); } internal void From b680f90c240c7ed5e8ce961d1fe44267099869aa Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sat, 16 Apr 2022 05:14:55 -0700 Subject: [PATCH 127/151] OSX KeyCode translation table, fixed rendering bug --- build/build.sh | 2 +- run_tree/osx/arm64/debug/lumenarium | Bin 854999 -> 856087 bytes run_tree/osx/intel/debug/lumenarium | Bin 186055 -> 839191 bytes src_v2/core/lumenarium_core_window.h | 9 ++ .../graphics/lumenarium_editor_graphics.h | 8 +- src_v2/editor/lumenarium_editor.c | 12 +- .../lumenarium_editor_sculpture_visualizer.c | 22 ++- src_v2/editor/lumenarium_editor_ui.c | 4 +- src_v2/lumenarium_first.c | 28 +++- src_v2/lumenarium_geometry.h | 4 +- src_v2/platform/osx/lumenarium_first_osx.c | 130 +++++++++++++++++- src_v2/user_space/user_space_incenter.cpp | 2 +- 12 files changed, 198 insertions(+), 23 deletions(-) diff --git a/build/build.sh b/build/build.sh index bb270e8..5d60d30 100755 --- a/build/build.sh +++ b/build/build.sh @@ -1,3 +1,3 @@ SCRIPT_REL_DIR=$(dirname "${BASH_SOURCE[0]}") -$SCRIPT_REL_DIR/build_.sh debug osx arm64 +$SCRIPT_REL_DIR/build_.sh debug osx intel # $SCRIPT_REL_DIR/build_.sh debug wasm intel \ No newline at end of file diff --git a/run_tree/osx/arm64/debug/lumenarium b/run_tree/osx/arm64/debug/lumenarium index c44bd32c293f14891a5825ffb0d4ad83f25b9755..a2c1759e06233c3a3cc08b4b08ce1c7a8ac249c8 100755 GIT binary patch delta 167719 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Ȁ?dLcHRUCDc1D2QiY@rD;Yk+GcC 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%0AtJDxr|rV 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 OnsiaUC~E8c<9`9ai7nv( delta 162201 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%0D3YJ?}llm6)Yx#t@)I0tJqVGWeQH+7@=gBsm)KO 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 JEgWt(`+wB(q{jdN literal 186055 zcmeFa3v^sZmG6C8ZQ;{aJhp5Hzx9YPmh1%M*zw~gxo)Qk@dFYf8}hO(jDgq@!8nE$ zYfOloCKD%y2_k`n1SYcOOj-`}!8d_L_d*&cqnS${yzE>a7rq%vXGS2WEy@cP%+f>2s=f?b@|#*WSB!JP>il3}92!<}0t?T$pXjKV5{ye=i5r z{;5<`(?>tB@1rB-i}8K=3a>GPyFGk*SG)56COED6XC9iG_I~tZ@7tSCD#my8Z$pMT z@9>f6J&4cs@xep$uc>Lr?ho%BA+Q+VFTdvD`|1P_Uhn>+kD>4fe0x87+pZ7a>?arF zYfpLj*7`{F9{d-iRq0JlAN%m_yFUE>rd=QY;BNC?*dM35J$y6#f_g8;5yY>4ec0f6 z+uF@*n_|~oy~*oc`p-}j^`ZU#bo}=)$@&w_RQ{TpZr(k@HNBU{_Zizf@Tt80hsZih z?F+x6YZb5_oxU-KFJ%Cv&e40rp&2ifJcv%IqTly zI2EM5OXc|YUWnG;ojm`H--}IZ>HmGTz0BEBBJ0mctKm=MbIHf{-nQgJyWY3t{VgAQ zKkxcCmp{?5`05SMmNl>WTx0gn1vMBH2jq;nPw`@9Z z(|z9U-m(2dhJ$c%)7##0@kJNE<*ob&KX<(kF#d_IRs7}4JJs|q?X|pDNz?SVToEky zX3B`4HCH1qu&BX*mwf2oeEUJ=b5beyj% z-z|^6dO^t9oF&Z#*1$}3a@mPdQ=a!j8E~b61#jEa;iexB_c$kp_Lp@I@!lS<9&C>{ z4fYQYKa>Wyo;PiA9!!HH4UVDxS9AjJm5V_a>AhhuZ#XtOz20eU(BX+8^p1 z0ybE70(Dr}q5W$+Ep4LD&=@PDoZC4RE^BKKR}UWUsY)ioO@p*Imk3u4n&(TRsYK6w z(zgwE_H0c$v~^F&d9b}>{=P)V)_s{W=MtMIJ~$DlZ5R3OWzMujjB-8OlAW=w$%fg^ z9`c(P!N(xHJKJgN?D?ytyKC2~L{D|{Jt1etaeg=REBxf|36&R?;X;PCciB3^Bea?N zYn`_BHm_%%SATeGQgw5E!Dd4b@5g$!BpX7LDwO{B#WXdDrftb%zyZ_T&{Dlg?OvA0 zTb;bnr<3-0bVi&#y^gc_7=0XZT2E$%A8V`+IVrWJ*1}ZMmRY0QGT+DR-VvIf=$N-J z$RCIHoFB}O7v)3iWb)g06s0e9+9n3e#mMi0538NZOw37TV$oD)jYHpVOS)^LdzztL zwEo(mJ)~u3l1HCs*F;m|YiDfBKEsD+8VBa_WAVZ@se)w{uI3F+npBGpS?ZWqQ3bX;Rhy&zZ0CU(>l$qZKz1l z?w5y$XZm$k+WND!Pvxm|Q>NL&nVIgl$;&%5&>U@9&sbvI&vj_mH`T7`Ub|H9%A&pu z_6vM$>+JAocPZ;4_r>_0PF!nxOJ$?T1` zBsBKaS5v%pX?zB8HGHgU(^z=!Q(LFd*26Uw?&XYC`u=vGrYyKsPGevIy6qV7(hBQ1 z7rh^(9-|Au-z5AR(@UK_t?|(E9 zkGalf`#wOrXmrc(t;j4a;>B90ExRz9x-Zn)I8foFY+rK~$%6wG(NwywG`!%{UZ)a$ zB3i0}lU&w$_1^{k8Ssj(T7S%BtG&EVKhG^^tU*8bpcM9XfTnysjPoU`XD;>lJPM(g zGIOIT^#lD{Ve3|3aW}>c<>@)XbBZyb@ym464hZKT3M9`d%uw?|{ZO+P2xu)2 zX`XwL{XTGPk?!>Q7EJG(;H7(f3)<%pHgf_tGnBWPebb#>Klc9=^_>>Y;if^=h5ldO z&hugVuWx!3m?$t&V4}Ipp?d{=W%KV<-%muwCrXB#JyuU36Eo0Bxpg7uYt}YrC$<`$ zEL;09I*Rc%Jqrx=rR1-aghh@&ZDD~iu+UNx7W;aag$2gILUT!2S=ci_Xo%MduqCbphD??&gr zKGDm&#+sFbM=yh48td_GgFE4k#v=aOa%0!9UEHlR0~^sOJ0@Gz37lw?&Mko7h)iG? z^6)&%26O^v;Nx2h;jaQ7naacSEIrx@oQJ;%{uSx@9>2{Wvth|zxO1onpE4Zq*O0XYZ9gUi}35cgS-s^wCx;g9h(xPY$J@tPLMvd>Xs5pr#@tU1rM~+Y26i%Ti^H zu0@`IqVlrADo@|lSXxv+JQ=foY@KZQKdZbqh6?M4?`JD(b^Ui$p8jz|j;UWdHY;BT zU*dqzKlG95C*|om6OI}^?8$qqa~R#=`PTtm5s>$O^u;M;{51N)=r?qRmFv0mq0w)Q zr6b5}KjZoo@_$-73)+VE2gXwgU1#-N0^MWvSvme_U%32OKYyqAtI~LN@XuyGHivc^ zd3Ymyw(uV$Kd6iF6*g=9$(fYIe#P4=b?;P4c z107o4mgotcpr3Qt%wINKj*SQ*ALs;p{M_N5xyiChCl{GbI`u`$om_Ru$sO;Rm2^lu z+;da%QfOs-!@pTcw=!z-6Y=YlqQldn^OD=c=ulvc?8P58bC?a#+}=NDU!wmf`vm*W zOR=@U9N#-@)$#bucoa8<?BUL?0dIu1`?&n^@ah}EC;!RCmq>(XG7q~hDIC^s zm0ysEksf;}`CO>=q;p5)(XQ)_f1YEGm~-LhPUf-4d*&sp;luHsHztqon6)YqyAIuO z9ej#3Ct{@afTL$5ZP5gLvK`m2dN$--f^RL}w;3Ao6%z0$v6nn&){0Si;O3I98J(1s zu5m;1(Pyn<4CPd2vHCam9_C-yiKi9Mmk7IK^Ij9&zo*IyYPUJK~{xRg_NqD>= zR8jE)a|(`Hl-8-LUJ- zK9_N)xp)S?s!xmir~!C8E*s#3#+QMoFSr@d=+a`oZlFv7Z^3<5n#Eu61!Z63P-Df? zrF^5e)yF*#CkMijW2)yE{DZ!e%$**U{po}6{qVh(@r$i%l#T0(dOA>g-00Of`bKda ze1vCy7tpEb!vuLo@5E;&WBxeDuCGkcuh05@Ej@ZR8Zz{?`b@kk6K~b;@K*0(-cON# znzZfE_c_s~b|T;6e+F4M_7z&|R6q4v89P_KectU@Q1tSLUn`+!daTOm{*TRJDhzx{C4qk6?bRFuzi~SH0K=cRm+h2613V zGte?B-{SD|H5MNFcYF!rh!Om^di00rbIWEK`sM`ccfX%#422d}IkRf3+I!}AwTI^y z(0e>SKY2VhKec8XS=lIC`s{Zi8nG9OBJye~XV=9qrbL26=RmU)Rp^ zRi>_gp^SyM^!CmzU6<WCG#OK?9f1s5L;_uW!(J%w7EST^o#66 zhCX@-J($4e`~776PUnt>M}PKi6OYJc7UGwNST{nJq`MC~#3p!_uh0&R`V@csYiWMJ z$h-XV)r=M4lq_7Nm>=+I@PsIn!0ut^PnO}=hn?2XepEQflP$7xAsg5WzRZr+R1kKT zU`x>P3DUEq3lIIA6D=)DL-ss6m{VY5jlK!Zot8mAKlc0KnTj*o`O@>u33ulT^%Ga^ z&Jf;u=x$&xCfvf8ppA*1E#8^}^E{V%p0R7pZ;|OU&wwk<9D8X0=FZ>k-bB2p+{~ZX zK+|wIOw6N#n8(N()sb-3V0uR=A16|bPqCst=F_c|F|oZ0>c_TZZX%8nciR4(bj6Wu zyscuD^I(*9iOeC7XPP&C7#PKeG;h+cXzZgb^MQQ1p_R^qt<0@6n;5J39MIp~4*eGv z@k05UFB6|Mv0rcAYGYy>i4lao7(pTll9~GcnuLG>0)X#iUo5_?LKl(P+5ah$9E#SPMp; z199W{1mYf3iF;HM|D4d4c#60Oee66lVKB5F`MuIfxzD_9kol|Pn3t$Os&8;oS`XE8 zggoY%xk>zPSlX)fjzf{PWrNPzD+VWfZB~rHtgG0vSNUaKU=%A3`*7f2Zejjs5QbRp zze|p(uYJe-eG$qX+p%?@Vil3KVd{zv{wkk8Z(nt-CpT^_@l37BA%~L|c#raD z2KntfX6+-MWcc@#g^$hJ*Gby3*!BBr;7w?KG__$3K5m$KT^K!%f4df)@Dq*w9oO%Z z+^BD)Cyb0hbL(Vh^LLdm+_dqF;7 zWe?tMP#o#{q#fJ$s%*cPm+ePC&?fnu@|W&XUeGUI;^Z>F0fx2dR?#OuB6mrR=hvbw zL0Nf9^ioguWl!g5?e|}PQy6nrdH2d_Dsv|^%U`A4n#b8Vj@6kT(waW=Q~%wYUozii zO)tmVZ>k~UR1PqYw(Dck!Lo^sqW>${s>_`)xH4GhePMV? z9qYoR2kLrWdo5^_yB7O76B)xV$Wne_2RfUzRp|u%x{M#zUvk9!a|4vOGTb-ljrsH% z+PKE+5568SL-V3*i`pRFfX|_Ruaqqs##b@Eh|+8Qbad4ZNVoE>U*`tt=xgyg-o+Zr zgwZkku(!s-8WrO@uy%1#tK`klZPKv^$jBvHe_by=(H6>HeO1f(!ixne~(A| zbooJA*ZKitNo^dpE*SKIe4h6~m*wrV@YXzI8!exwfKO?%q1LDS_OEFV?bSPT$JDdD zd+WTc7puP&^Uv2G@C$K&JCOmdxmW2mSvY!c`1s<6@&%y&I_yQj#$OrlXzd9$M zx*yubYo|PFenXYf6ms&_D_Y;&#ymg2Zf*PKO_B+Cvrg^w$4z#kZDXkI4CA-h_bdrJ z8z0}o!Z*AMx|0UxVe@Phs*W`}t;1+X#O74G+;CyVn6e8hwdur@m7#F3&P9 z;FX=5oRUn$1M`#le*d;PZF~KG&^oXBtR&nQ*e7WF%lH3F!X0+nnk<|z10~@)owj#d zIKThTHYVF+)m;U67Wnit24g;+yuXj!chc4d3&XhkAHWzrdAO!oda^y%)P)BbwnD*eI(bU7Z zcF$Q4<$T#3+J7a!#MtZqmiNVbhAfYVKdUwGdfcPKYzmof) zP5b@7j@UF?KYI_54d|eMf_o4?Wa|H~ZQ~2YZR}AS-@smKPlh#i567zpkHo5yhr`G* zaoi4KzDHy8yZVLvWj_V70NNsP9pw(8UPF6}&P{K+3to}M+%h^KV- z??B%1JNjZq?y>pXhy!d(YHTastvL57{EVy+!A z;-t%1`&q)e;9}MVDZi0ef&7s#zDszbxkvaN+W&U;fLpm4y)V0+v8uQi?HoKBo>q=GQO}mfd2EOtSW3O<_D)>*X%K-U0b_m-;pV z94bq?*-!f~*azj=Jx+RN9A9!AeKM8!LH5#CViw-sgD=sR;uu5AonSaAoN`wViP}Eu;1p zwe^-czHJ<%t(tGRlbu%+?8QDqe@t?m&upli-RAPyu=OoZieHDEQ1uTDKSF~m6R|DH zEOQg_u`|4Fuz|SX0Bx>sS(FU&OmNJzjo+Frt~vC_sB)!DxHntlW<~xt7K5W5A z{$i~yY(e(6BD$nOdIe9N>4dOND1YdG%oDE$A=;u-O{DJMh z>TGSp56sj5$F{A~Yj?@Eo$uwTZDY0L4W;sYo=ha~mp)JM5!x7k-QvwU@kTZapG&fr zZ^IwRo8Y)#%(y?R>}&i=M=+Kr3l@1vFupY>lEYR2KOcF zyM15gk9Ijf=a2o$=zjl|u2O5p;hCx*xrme7t8~@~`9bk}ub( zo-Ijq&vNN}Ct6#T*ZaN=FnWKD(_Ih$v{x~&gV58RF;8#aB0Uu69K_<*RHS8BlK7PL zK!x^oXMi*9M{m6*9Z6n9{9vC#Ate?5Msh_eo-GwgcaAIVs;X3H5ldj%2H~`HJEw>o`zk$6gN|#Sd z%**Ua)m${Y(5uHO$Jf+*o%bH%T{e?=#b);KYp;1OyQDRBA8oR-Rb43>wj>W~J>+%Z z`V_p>KEu}odpEGfbw<#W>Z^L{HEmE^)TRo4RsR&)i@k7m?dm#=Z8!cPHduaSF)n9U zLzgWhKCAt`YiP6aA@gNC-Y}+i@&$YQ)$&CG=LAa4 zGw@k%HE@A>MsS=mM%#NBoAOy=%rU%inmmHv(t#a6#n?Q}I=138_-4tzI;U+KJ}Bce z)!wtM%dM%Y+Ad!WzeafUtF*Pqgv~!pzI>;R&>^4VYUv!x%1^R*^)BD&NDN;#j4%75 z)7IbPw4Hj2c}$1XmSX>d_-Sps=5gX*R(cIvulUw5zCt3rb?~6(Inx2sw>j($S-`k? z3vnv(?Dy{^?u_3fU0tky*{hGf_0KmbU%s|CUt~Ruc_Oyoo^w!H`No>Fl{({K=PWJf z@aK(A@2N_5#H*4=OX&5}*cT(av`y==_q;|s(0?|q*x&A>fBWfQ?R(Z6vcw%&E z59g(rTPudj94M8D@hklij;7%A@(g^f=eHOB^r35|N3^e_13hvy#$E{I2memYwU7rR>-nAN z&jf8VafvW?{VX!T*^uO4W80qy&l~KF&mBy}<`M^o?j;(Z9{uJl9D307?P9-g(UWGL zw@mF}?zd9y;P+B~=}*=d@Wr<#Gcn-Ng_*1P4R0CLdQ&2P(;#O~a$dV{N-n3}@ITjw z{p?2uP9Y2FWwd=|w7c(8_<;R59b=w%74y7>%n=qxbI95;eCS*jIa!47Qy+0s-kGJq z9)S4B9RBt2q>uSQ|3c&hz5f#9;AMP-Eb%zWv!NA!*t2R4_yuL?GVuiclhS*b_oMWq z9XEZ3?L{^naLN==5TC{G!Xv*k)Xr{%5b5>3`<= zrvK~w{)dlNk97S~aS-}<3jK?&HDiuFXqx-?)%iS9ecq0dbI2{~B}?y5cl^Ry^fpwf*aYJXJF9n(&38nY5$<{{m?^_H|h9M>GI=%&mxnYPi&EIZJsr6Vw{YE zuk`R37|m}jyyj=6;LkVsb1wJ{E_9`QOxa&Y{vh?blr?$i(tMtLJ>@~usB*LYJnA1+ zZoZet8cnx+R`JTjQEC6kIlK{lW6qJqy;vH4TJmVm{G^SYwaV9j2jdvqps|!i=c$eI zi?TC`fA}$<8o{U!(TzLhCqeg4?Sb>(WfyukdgEUC@$arz;mYvgLg3Y&z58gZ z<}F$W_0~QE@l*V3@t3iZ!oNyst&O!6Z(SEU>O(xEi?Vty=b7JXY>4@t?Ek_m)&sot z>mq+${L$Xi13v$m=j1-YbKx36(OLra_xa<G^5Fkw z@mga={MGo&r}J#m;dcQorR)Im$vQ<+XGAq`)?VgBY%BLlcysjoD!n;+XY5PKN#*{z z0Oo*c>?C^|yItlE-|=mk-tW-f3H%(Lm6m-|n&JbW_tWLupaZ*QU))+`h5kJT?h0^k zxTE4x?Y*i$zhjCl!)w zVwdbqVQh?s%Vn3frW6|xoch76Q_)_0+f;}?%deGV@7PiAJQahMSZF1T^2=CB0n)etTVb;s$o97Di3?HGt!o_D$9C=_#YpNm~;HPAn zaU~x`I%ZUPXech9=Qr|SFfU#PtRI(pOmV5$R>en%qs~v>raAEv?fVGE+ed77f>^NK zpX$wzs_=QMG)GPKWBag$#;?fIR?#V4csyJ^$UM;-r+)hfh-r%-ePQ=ld(1gubOHN# z@7chfR?Rn5p4d13p@~Ji>;XT6?_pxmYnP9TMQc5dF>mIHs`o;zAJgxOd6>0j@)kun zb4Xo_p1Wn$q|l^h{4QvmRH6BeU|%>n6Gu&*fd-w4+CVuY(;;WI%4lzn8+mSy zi6K!YpGO(PS7_1syoZs!KP6qeUMF3*{fX(i_jS_sjz2M7ov)KF?cM!T_M;g)uS32h z?^eg(qWus7d1st1|K2Z(Y!dJhWI4eYh&;Dzl_&po zmq~S{^~|l*UsBJ^%hv|3~!P+3EP1^ci}Uan)9=>t15N?7@QbI;C~pyVrTT z?rC)0N6>RMWzu!bYka-Clf446FXql7^l$_p(3Ne%kJ0>9x>@VTp{X^-$J9Ox#UGi6 zF=zGXH9cAfoWvfGvbIE>a{`~1xh;1@;CtEiK+O*tz}p$0my8gbZIJICW1j%e^3xOH zdE9frU(x(w!=llCxYm%`d*imF9Mp z^#2b;S5Jbb^|elZZee}dhP(Yd=0&y*werXPn9AYU+%A(&drZ1`waMvL8y(uKc#f(6 z8b1xVFOins=yV?s>#jWJP4sv1e8$d6ZmAV7wz3cMi^)m&=XUKXQ^)+e4nCgGUhit% z=T>6QF{r8!r1q4l(>{)eavsL zInSN(`GCJ}`k%RoeDwj%F=$792wQ@0j9nQ?EBDjN18I{@T66v6K$@AqO=_-Z-eKyK zpZ}t_w)ClAdG*@#_V`U+f6+Ilzli-DCe6yEombg4i&GCUpJHyc2U$`-3XkdU^NA-z z_lA(?kDF)ct)SnVXL~lb7CWi7H9)iEYXf!SyEk88)(A<5Zj+8oD7}t!;eHe4UoV}f zX{((B{-x%8&}iC1T`uGLIrbST?e)^Evk;=W7Mk^Y0W_*?9W)8=1n@qrbvj!QwEvgX z1Kx*OA2Tu@f)@V_ta*pe=3Qr4p%JUxf)Z}sQG!LsjFf!ujafVd<)%ge$3sw9N z{ElLYOZ@$aF?0y&z1Mj0I+GtEf3Y94+~maudp8F16(gj)?Q_v0eR{YQF2y0iWng{U zOzf}}UgC?W@EPneGv_wpH|J$|*0|I&dgNcQ4Z(T!mw8t_NBcz<(QoU}ZLFgt8+fiq zm$4s&`{y>W$IZXXs;BV2H?v26isq@_KJ#AI3>1g=)=Gls==^vYYaZ-HP8lB;`yqc+ z_Czri?5g6XsVq1KDhlHa`91>%+DsX(bEu4stGVz8d^wGIgNuGHiOcq}a3LeA|DE76 z=VwSOMMM2A_KYbnfYa*!dh`yqFqehq2I5Tl{SN0Brc=+MNh&WIX{XU+kDYN-JZ`*Iqi`diBlGy zhinfMXTg3S!8U7;z|q*eq++I-I{J7C^)Hbv-y*xvyofu1%w9p{S!?emmK2E1**4yy zGiKyxmmmWsF6h}Lc%REIW(^`>->Mm-7FN6$k1d?FTgb<`VbBrh#KYNojYW+m))!OX z6+da8d8e)B4xojj+xx~?407@Vw2x3-v|8KKY5%g`T1vsh>uQ$f25)p0 zg!M;ny-{mZ8DjZny*%u#E7~>uHzCV0v(^@f{lA{IwyTQP)U?hP3#_SWjcuX7rWTNA z^_$k|GUe=P@YjJdw0o?zHQ56BwC7r1GjOy`{w;A4`QY8@u>V}{r>maxeLH3AT;;EM zVe<^U(q{)?w3cUKuvPPs$x?LNI)ikE%sQZt9~uhESvo|k>}_EEFkip&ir4A1b|^co zJlSFBb?rJ`a1F0`oi13ec%3d-?scrw{ciW)abDaz<1ySb@kFA(>_ldvle@S0uDrF# zTx=fuaONeCaA#h}Q*&2oo-kpllPkZ-$-SY$$xU1!m@Uj_W_6k0IKMsD2liTB@8!k4 zv|4nH%~xzoGFE-1llzAWteH@b`}Vq&=B(uotnikkaI#;y%ld{#UJIS*2$!8WI=$?~ z<8@^xwy|d?5xd^r*Jkca&{^V6-MxfP57zM{bqMz{?)U4YjC|G3*sP>^r;K^$thIUP z3^DH&ot)=2@8C1<+~uUZ;W(r1m*<^4b52_L!ZSI~g}=gjWR|*gA6^6R^~?uUw|h|_ zFH9cq`Micep78UY&sz}46Ro`4JkFGxd7^k{`F4ap-88_ttk?|Jw7mQG>>8r>fj##& zbfWlN=|JOyN$1}_@!~(&v~t~%bUx)a%P-rLi1Xa@Zg|E!JiSkMq^T|VV4Xdm%ikAb zY@7D|v;Gj`?q$Kq=YGYY0^w zsx6P+Q2?uUGIrW~rhBk)4|YtP@#$th)CrumwV|Pdp4Q(BRqHTe8nnn?aHi!2k!7`4}&Yb$&3NXpY}88ULTz?K1{mi+$I)5ns}u9 zbzfu1=`z>CHez}&tn2lYE^IX7MqC=k@>1kwtE_ZCj$d8jBZtT)J zqt~~}Lyp4a#jZuaT3ZT}(* z>f^D}HO?aVZ=D){jJpf)v!u@wVeU-}b7vsBMq_Z`R(v*egznf3+*jFr>_fg>eBzUh z_r9g`w$9j8#_fBOH6{+`?R!awL*1R=QkwXX9+vG~+ik|8;y%CSeVFqR>NmarhWFpH zU+Fi*p3ngy#`MkFhxMB7UntZKlXe8;lKwlFT&7>~;z`mY=j!Y98R&RLdji)Qo8HTF zAJ2A-_MYJ#y7k`2b3f0)G0MFb-RYNUSL|KL%e%aOuBom1N5-hqgZFT?$EF%PbT%FL zjPBC%b=)!fZ3DW}-c?w;>z0ywj{7sZ6=aBR$^fBMqEPB;vA1yn#?%e*)!hYN6^~1?; z`*&H6rI*q@PWO3%zEfW}EM5HQn09+PSZ_~!mqv>y!7zbnM(eyWbet#2w(*C>E ze)>Kg^UfcpN!PRPS?lfbU>-+*nXyw`W=)_B&*?Rkz0^;mte(@C^7|CO>eFuo>f~Mf zUbP1*O}d`B3#PmGc%aTwWtIiX@T|SAI=g1@P*%_ErRe597H_-<*=&)R2W z+i7?qx);({q6?Vb;_`KY@;tMj)WB=6O8QCOi)mKds{Jx*GtcZf&9~FkDZ1zSb(ShW zGfS&{5XD;clgx@OwgA0NrtF9{Sf)It&cL+mYTkn4SB#T z+Y7yB%-`?9=5@d!Dv=Dk%RbZPoAXnBY<>4-;v@8B05?BVqNpIP`loL{preS)F?1Y_!g zR^nn^>l6p0j-{&Ob@0Ad{iQx=qJF_!-l?zth(5^Uy9s>v2k;g3MIP=t;F^HDiTbWX z-^1T4h8Uhw#a_Se(eYEe(>^VWa<^~ zw+|RQ4*JQH{Pcg*`!)O|zn?#o&KS|J(z&NV4m^P?}%r=7EK+*Uv0;Km;RH_ zmw4)?Rf->KeJbzQBm3=f=5KLt{w5o(dAQOd=sTrncKi1mV{1Q6zTS1FO)!exn6l35 zD&qdRW6YPHWFE%8#^xu9r)ckyxeJ{+7v|t)-1*5TZZr2q z!_RWX`6ySAZu(?S*JSaZG6!e;Wni}>C{`O_^4 zVuKd9nVSlp_+UYr)^h|)oKQ5T@f(8i$opuM-ZMA*X9aq$GtcrJGB^A2cKG!R)}Bmx zZ2PG4D$``o8i0%EG&WuL2b*0e)lr9-{8}2wuJOeGupIyr0T`9N)UUf#`IUk4 zJZpb};4_!g(8Y7viFVfe%w6{2Ht*c=pguD; zq@TR_ye`q{!SYNWn&&Xjw9zXwuPb*kXWwOuD07%IbpOVCbh7p^Ngs`&Z~hQ@mHgd5 zhWs7U-Ur4z^H=jt2OJp{in~ci${=!4*KsX{r5QQrjO8v%nehS-IT{3G|P^Ky}f$I zrr~2~|A74B$hD8%7}R5?48F4|6AzT>8M};ccQ`{bJIS}B%pLIB3lX%}1vbAzXG|Kg z_t&!L#k*Uy;(ucE;{|KX#I!k^&G~HBm6~-+=fHm5^~_ZkGFMsX&3~KOAFk(uc`vdq9A@5&JgYv@XySv!WNe1^p)FC>1>8i( z%=HWCBA(Mrdz_D~Gz#S#e zd6!X z^;a-gtoPTjWbf7X@)10F7`x78VIofVPM<%c&m9)Xp}psU^<(Z~c&du^du0Wr?!D(8#ugs0A6B$$II-|*vS2( z1?RrwMQ0Dgj`q&x-VOf>&IPiTJhXoz-vRKyyUBPt5`$LuWa!N5<8ke6Wq&gMy4ky; z=Sp@5znR}Mep7GfD@ug`40TNK-xyjQVkouOc_p6sPL zlZxIqc(I-Omcti>6B_`|=3ed=HTz5Ej=)gX(09M!*pDf=!`kcA!&z5sfW>9tpz&_O zVG{(m-JItDZh8?McUtZj+;k7F7C*B95BG=eC13X$OV61#ZFr%5!m3AdQ2@`~#WxEd zeVLV95O32j-)!bgP4%GG)q?v6pJM$?{UDhd+J6<_M=0^lOY-&2P~D+u?jfbEr)bA% z?AA-@;+K&fBa^WvV48s8K0exgiZ-3broDttemQKv85_7WBUWDcO~N|=Zhp=hbH5bx zh521&lbu`#{d+V%FZpis*|hyF+@97@6z6< zD}TLOeH}tKW*N_>fAtMU|L)|q=)!zD_a-;^cP1Y|_vO>MFS)_LEBP7eI*n0u<<;0S zHyYgo9r?8NN^6boIiS7mfSTqbd8v0Vy1vD(JJ(l$ zOLs8KXScAPZ615iqQ#Y!NX@PU*X}$pi#Uqd(HVR=EJD{Z#%*#n4{;M zt?1huoSgOwM{eNE^j6N&lsm}y)}(o^bW$#9%)@gIcT{lp`egl^h}&%>?Hc;;O3JS% z@4&N{)$rXo2OQepiVyJ1D;`|YHke_|1IOGmS&)}l(e|pJ7Xg3GY$sKa-?gGG8_b_y zl>hw|ZN0(#+M@hZE86}~Fn>W&{@{wX6T$q3qWtq#wmlcjzo;m`W@Xzme!lqQER`PL znoQ{aKH94^cYZX5Z6A4G5KSdCCT)I0G-Z4y#`!h+c8pE0HDh)JZuSV=mB69v4eWY! zz4q#vZ|pM1J|3P~$m8WJ+a89-_8xtI@I6Ue*YZGJ8SJh4m3fH16MnLHv6K619sOJ9 z_3s(kzJ=`l4~LH7@8>FJJGn{oDKnYx1lIDMK*`1u?77M;@nwSjbs_A%69HyH>*ea- z>@p|kA{(91A-^o-96kAYm09MOiBM)TWy-zp4;q=MeERZs=6I~JBVYGiwwCsOycS)t zGMd}Lud&NKhvqxeR)9K2&AFx2#!NcKc_(mr#a}L~nnR5~4 z*{i5~Kf}TqDBBsI3T!2?-@mN0Cv+maMs)neWe1At$li(Gjnh8y;!-EqqWnvJ8rJZ8 zJNf(3CGqcG=FD!ryq0_IFQC4@dz@59UD=7NfKLP0cTY6+OigQJ2m3s)2&5f&6KTuJ zPFx;HJFK)=*@;!E3z`j&uY$hIY0p~Pb4fH8Ud?;~z7R*tF~86C?iCH+xy+g0db#-? zCG+<8qC>QA{VT|nzTIhM1lx~Kd0led1Y$ekk@Uw&KTNvmm$^>v#}|0@GspDWKhw#r zh9=!HF1iNP59AHpO#RKO|J#@4<)&I^4zEKt<%eCD)SWO{;j4z;Cg67n{)NjNWax0* zuXBo%`=+gPieKk+CpS-gm`=ItD0h>}J%5>Ve(U9;p+K*!TiNy^ep@^Kr+mnRc6{C- z*))ES`Ch@HpYhEU%F0J?pkM3H_rCFKc`e^bX?izu&Dh6onYqNrDQn>5-{ap(E~+ol zea+MvmyhZBR|ER~GUatfuc6YZe3HCL%xSdFqJ6j7C(ymnm3<;=<}x1yujn!J693La zSNkHd(UuOuS~_mD@c8xk7T!0Brjy^mcb*iN^y|*z^V@n%S?o@e+8}$Z_VN98!vosz zXTI(c&75r*Rqk4q!-f}^Q@cH1hj|_TjiDF5xR;FRHsMyedfKe|8s@ipZF$aVTL|8Q z{#fnk&LrX$o{v<#CKU9MbSGXfwp?eNG^f$o3Fb4S&qIYZkN58c3sVMJ<34}gTgmwg z%E7zix-Stt*00i9gP!GEiGBmeyPYpRw#4%tEKIrhr#{er3GfcU3(r;u=1>8D#C~VM zeDh?YN5A==+C=7K6HBz=EM@hLJ9MVuK@i7x-pbesLsQ7%4wlE}5DS`CaPA|fTGaH{epf1^)5-3d3s84qm0LA_rtnPLtX z=i7s?FgRj3UkRl$XV4!%b8?xN`TZ%sKc!50^s_t5xf4sz`u4}? zoIPvd|0eO&Pg}=)@oN#@U*r3I-1V9H8RG|B$n}}bqu_mXt>L{Ja@n_t|HXUeE2Jrg zpn56CejjK;M#ytQYktTc%p7|Xb5YLtd_%ZRU3?=1+>1EtJwTq}hoO;nLht{UjC*i= zn;5(gQBSyjk^4+&TIK=oz4PcN8#|94y^Xpza8~GIew(uo7+NOLj^+PLI4D;t?;n&0 zt?Tr?hd>*%+>zr(y*Z%EcgQxtpRZXwz(Hq9@Z_}_{)$FD2Wi%Kb9ojzM<^4FHH)mAd2|sUVD7r%B6VCPK*A2}uu*?hiX0~WOzvz9| zC|rM+ujh@U^1hMJn>Q*i6p&T7lWzq`2K&`7%w6&0v&etue)x3%+EfPq2l+L}FB_5U zqx%}3p%1QeQuu?L^Zjy~v~N8&N9kJYOoW^jnJ3q#)@zNIJ8`r0UxUgk)}Zo5b!0@l z?c`%+v*c;5$KyTy{C_+dGw+(apYXm1F$(WwZ_Jx-l$kaH`yG4>`3ZVg z+2@c4D{DUd#Iw**AnWDyMIC+dPtL2EH-t75tMU4x@`6!);g5%0emrzACOWVKJ^c2V zJD|V5Q{OA^d~=&k%T$wxoE&ucb{@a_c7%-!=$ZA9T<^+gD$F^>^zG=fo{6i6JNT1s zyNQ>&1t_jJPnvo&T4ZY)UGf&YzJ^$0b7tOP1x!l<^ zNwUOqg!ql?^q!0?clr11^Q2x;z=*2a4I z9>C#KR8nSj<~TN9^}V{@+{dQ=F?JbwF|f+}#mDosWN5eHC-Iw6u$0X_0WFfDz4Z59 zXurhSvkN{Z(4i)cysXl(*MFn&5O~jEBaNOTKZ4Jl1qV8K`5!{#tJ1-=iM>#(Gf&Vy z@jvA8FHsH+PflE&RT_OD8pMCe`c6O3z#pEn=aB9DWll=p_%rEkA(NhvOf2CJF470U zwTt(I>Qm+h?qT|rJjJceo`+@p`n9O19gF7=PbG!r%H~Q`DYn3_cLbhFB~%mEZ-P2#$Ts@cj8y7K0A*5@wI0{D0FeU zjxqIOd0QWJ_A|EjyCue!m8aX09iG*1k|`r+v?)uv3w~sOkIu2*YsU&{S>EaY@8xd; z`pM{@Kasu>kE{-Pnz-mFn|cp&a*r=3*u+dYy25pyJ(;P@{EGc>IoZ#h%p=@snUnTC z(%NqH@?APVfz9oWM~9r6?Nz7gANyV#-Ojm|?N#q24WD#|V3eo562^XmH&SEpRA}C0 zbR+#h-;T2Br${f>K{q0U#r8cYqf5^rqr*B=7L-xHPruBW732SCK7dTS*ywuM<8#Pr zfxWUasrDdGxB7CMw=u)$f?>YvA)SZ**SsJ5@pbX3WZl3TJx94;$%g2ixv%yl8T;|A z2^T|S%HYym7rHG>hBE)^msv-B=feMM!7}dr@k5NxYlG3LJX@V=bS`khueuVx{*J&m z;(DdWd!QZW8%RFB0q`guC|+so1!eX}(x1r6y3ku!7#?p#o`JgsxsF6e?6bag;`_tq z`%K#FAiJrtYIQ@L^VP%Z8}K~~zRYeTZc9+DPIBFURL|%12Og*@Oj|DnaF#6J)r(VCXi+Muir1JyUCY~TuWVl z^1k%@te`K?(H7snm2S)JBXVHJS*NcneEFy_;|%^;Ir@*-H`yV^S+gBy#*f$7niySS zZ0RfE)n=?Y?^-de=T~B@1MA)~Ytys3C&tK#A73I(ak-s3)6o59^{Hof zU+G!RJf+h3%l^HrlQo8XJoz#|IC~k_)tuIrPdKwYo(IaiAN0ypSssPFc#6wQ|(8FizbjPB3tw*!)*N5s?jPeooV^8+u<1vSIq0P@vHQ0FGE7*Wn(6^f7 z8k_1G8)V`R$dBg08SI;(gYgSLE*XY5(zWx&cYl5>JFxK%che&INwqJ`*@!+H2v;22 zz}+Uu?R|N4_~AYU!)Iz#P5&%zAVO1I{1y>o7J;?OZZZZUviXD44OW0 zLsMU6uf%5Q8avN|&&J0yI!AI1oD+KAU&$|Q8uEB2-k3Rs`a^wJOI+<{+6-)@=8Gpg z|E~R4YFk|G4x~+rJom^3?k?k=yEDd?iU!g|qmAJxeOUa1PWpZ&=NWS92gbaSsR8uu zz2~(yZsgwaW@5@ZAGV>TdXs-2evA5ao$h6mT?59{!}GI4!(1L#>Dr01U{ zgS7owjREnGSQ}-$cqi|k{dJA~#gE=>V0@p~=pVs|7I<8vJx|gN|H1Z{{s%AoFnqSY z^=W9mhrgiz&R(Mji^s09g&Kb$z9(J+kC7+I3oyk#1!c@yk+FI5I}INBk+h9xqhFP- zxSL{?9rjoCv34fEk#z>^rUghWwkEjKP_VLCR{4D!*>iGTYPrq60Y5wX@EXHMau%h6ZQP-{_tu;rteOUNbT< z<{BC{2{_iXk85oEa}}OdHcNW2U4lcNmYemp;GE@W=vv7R>x3@8LcL@WSmcE=c?5QC&CA0<#qA+8_+>pvfl{snCCo?C36QFn&nFfE>UVO z1rPA0M%H4*E1$n+E>)`iPf_nvMeXN(%=VL4-2P>>->k)Iu7sb5PG(-k^H%n0$X`dM z6Pio)&^}~xfIQ9R&`l}qcjcnU3+OGg-^24erw8U~JZGjukMH-pjL~1{{vC~h*CV@M zp}hF|l>lFP&hyn9BPNb!=+vAF_-D^0!*;Y}Y z=9n=zYTjAY*V3B>W1s|mW<9x-3_J;so-C39-p7;y@``2PZ}HI{KHC_0f;!HnXF7m+ zg1+hq^cByHC*+3T;27Zjb&Ubw%oqU2m}B5E>V2%J{k)Iae)5Xje;e&LV?cUcb^v{! zh%o-8_a6oSx!V2^FptvqhXQTqS!G!hGx&_{5N_TjV`t>oVB?qrr%d06z!8))<=I$| zUq<+JhpTWH{&_YM9}~T-_-m0|nYyGyC^shm9-!U_iulL-nEWHJn12_*zo$f32Yj0Z z-}u!T$aC`T5HR=C{zC!2@vO2sn9=a|nD4&xoZr{hOdFbM z!_dSLKaTe?+dy7%8_Zsfui!U>H@GK3=a_enbr;8!Xlgh-wcsv}1?+cR=QvIc2=(`V zp1&A)XD*e`Q~rhiJL`b?^a*}h<^SLIm!TbL{&qt9f5)HrsCpUu=2v}jHah-cjQj-Y zGr*HzlKt%(?=Om@rDx{v`81pNhyHGc&~ufs^{XuxGPqR#r`(bHl~#=7$G|$^ULL?N zo(L`oulk4CE1RCq{?ls4&vfQo>=DjXXzY3GO=dh-o4a?Gn{;9X&k~!{a~61P>ly8n zxJ2XLlw-^a2W5;-1MknJcl3@<>+sL@$Y;^G(w^Y_-cFv~P{X9i)X$uZO#P~2H}}zZ zU!%DmZuv46H@LJeZq@|Bt-Uzfiw$jgI$iX9W+Hn7NQX9XqyNXC@fFcn)F$pCL_gW@ z1;How_4mRn_C62ko_6k-AL4wJp3V0#i9hW*rnuU~R_@+lzpo$T9^gG&=Zx$JnP_-Z znW3D~_Y>jcccP!wd4c*6ywm&8M;~w-qMv2Y(W~M!>8gYDJ6}u_N6*uxeaY~Vv(mXt z*wc;TsT*3gBFnqgBWc2m+Fs@}65xjaVMUC`ff>q@U3TUWMi_)hd!?C*NSeBYP8(YHoj=P$)m z^R1Uek1s+lW)b370yi9xx~xyt_=Et2Gl}w$1rr_|^&BI@TI=P9+@=we{`@ zwf*Y??x9%Bcvu!4a-DsDm7qUGuZh>zIYUbCtz%uEdki=yKg7MAL%pP#_i8_%w7%-- zQ0*%2MgUIm=Dpr~=WLc&ZXxCB$zS6AMofM5F=Nfvd+*P`c5>GbfB36NSFacUC4Of3 zKzj^NdbT8=bp}`H?2EgLxV_pL_Rh8l&OZZFp|ngGe-;@vu-lzIT8qsx4%L_1Q>k_~ zEHM3YW(W1GBMxiR=`XVv%*YgDqM_v$(Rrp$^EUEKz2Gp<*LwT-7tj~lZ=(8C9{A=m z&QuD7t-+Z8 zA$Y91Y}zDf$jAKayneHEDRye;B3-&DQ-|+f=GhUARmJ=}sdIqzv&H;1me(of51gT2 z^{AiNqp3cVtx`SDYW$MElewYyofI$b-m894oA7(pMpIT}oU)4ZD=$06leMVY$2&g1 zq0Pq0{W7LL%?q>--^jG|Pe}Ceoi%h1&pE*s>m2VK&O1BN>E8Fhi_X&M3{f@=eb_hQ zIKo*X?i0zK3UlU8XDB%%8THQxVIR4(X5<`l!MT`N^Q##wI zvO4Fv+!^`qOaY$u^V%xGgRhdyf=6<0#lk?-O~pv$Mx#g={9(hLuDw!xgo%jdg$p0xR*6*Ykq^w-yewz&WS1KbFgEe|wI-QQWBPVURt~4iaK2Ohnqde;A5N;M{uU)ePN##19x|5&tmnN%49=(zHQ5s?v{rt zMNe3=Nxq&9F3xe6#KoD1Jg!{%DC8=XEe$feTIpsrM z9LK`Allk^zc{CNB*4D_qUna&gpz|)w5rxC8d*w)c+hBZN zTO)SIl=-J%y}&O*?;6~ZXzIPfjhzyGFA5GB?nh4xzvhgpLw7TIc(>vQuleC~z-gRV zI`DtF-)?JieWiz2Ft)t%RF~2U?Fe^Jp$85iM??GH&OLCR?z(po_9LLb)Hg2uqVZ+U z&T@`d_i**k<$fdj(B5w({^@(;mWO7%Q?Jr24@JWx(5}6bN^dZBDAF8Z93{Ayi!r|Z zz;}zjCoH`I53r%BTD~uIB-}Kp{t$nKZ<1tQFy~VjI0Pfu3;6BuVFmYb)hjqllh1qQ zeN1_bJwsC^{K7ViCUl9pUt-HxG*L$N_0bpV$5J$5p9~B#WoVLrBUsgY9rXrveSuHY zbMXn;UHE!uS-q(8XwI69~?0YTL zX43D($120eqOQJv?w%s%&Y2s7FVx!F*xzvhzLsNTQSuq(43_irH@C?LtPeT4wa^(V z=Cx0^W9i065`OOfPpuctD}9=8hvtddkU%1e;X%TIzFL!bkz`XFCBDq$(7I@=Qb<1z5!KWrp zWAsojePHQUUOqk09+Q62h~Bbz-1T1CTJ_Cdznx9UHGXEhY?kDSI{S9e-kYfNogTc` z-m~}x;7LL(<#`hw2mnDzyA|(HUvm?Sq+bVTN*TnE>Uqtae@DJ$xw)54- z`WC)zdjAMbHy6{i37R%RlVG5UZvy0L8rT@kogwe%>pW+*w8@OL<1ARVljgxY( z^=Srf062Zyw1D0==p|kBst!Xd^_{{GQhOrgi?$%G!Le<9{=@KcxaZ;me}6vxAwSRh zHvNp{gTBr_Obp3=H~2r}v>oedO4hX8lGK>i{6+m+;P(V@$Um|;r1z!Ui544qTqJ&n z8GrH>(WRjYo_!R)y5c<7vv10qM_xgFJtKE+71W39NserNe@40Ew4)RHUFdE=rtk?< z#(xzJ^s~up*_DjY&+0o9rv)a%{3t=3Rz4#7J*RsT8i2RF5M96K+(tsU70;m_?Q1CZ zp)GI4&%?mBLdTKTyBfpT{DFGs!PDRvt8LQXA#92hdiwG-dM+L2jvwAL)16e-_?{zk zTILV*~K>JC*jvy}lRiow26mn>a7Zygj)9+x7QeJB&O$r86wHtaueDD_%Wm+WEH5{5&T> zuY5G_u`#}zq-Zzsw?nvKG`j>dub1lzhPk*xU3;ugZWqAJ_@1OMXbl_i|B4(m{NXx0W zH(Y+~4Yl#czVI84Eu^j1*4#~*+bN?vE)(z*eMlL8tNHEGJ&sL$^H=vh2HG3&i@f-A z^Xe#D@93m~zpNA84%A z?|WMtxAS)%&#gNTG`^AFnhzgnoTJ}pYvUAtryBZ3=D|bzk25ZuHmBxbV~G5r{m+dj)qN=)PImd(GoRHGIFA@nPmE$elZza>OU3 zw==VN9ty9PJOaPktqt!{zkln6!hSdTL!3L9@8usKm0zMh;(CfH*4z4cUZ-c($Fu5} z?m*Vg9HGu*qi`0|bfi>%fTl-^^QptMX@N)cXG`U)e1n&NUva*8hz$`B#XrMe>0x;N zIC?m-w`tYEN@vCK9`4WW*=l%SR$0V*=J2{#NacGOC#(apUNFL|e0qFd$#liKO2ISV z8LM6J zJNDVclc22(e)ReNo8~3K{9foXd=Bs@)EYOktMy3vBKl^zzX^>s7ty6 z-uGc|3hJVLrP2?>JJTl?-XB9s)7mk2{tcopbC!mo=ZnWlk<3UgjP0JEyxrLDb=arT z_w%};r%&o`U-9W_a7uqm*D40|JnxEQn{hj|hOxu*(4G0Q`u`QvK)zERZByBAk|+4c zp1V#)6#MeWqD^ym?OJ8Wyy_ulanjs%hOh6{|H+eICEYwj+K*4r-DbBLIYrRzDLu5byuME48wXWM2(|Y+Le7c+#;+ z^oPA~SiZ2a^%2k38`^kQe9qo2EPq9DCaoD@pHl{g@t}Sa?i!vILsDPwVw~E(kt{mu zV_-CXO2J10eQDs;=YpT?(co4RlSY=Y^W5`ymio!JvX-x3@~!&)zAW%bjckOBY#6`B z`Z9sBm0=yt^J%^Qvv@uVZ{>$uIu$pwbeb_wABtAnx0=g;;kiQj@!@i3fZGMUXx~7; z-$K8ufAYN7*cI>TOYvTPYR1`Fc&eCco(I5-2Wfr_#&SSL^X=@Z;v3w2yCPQ2w<)T- zWLM;;+jg%|yP0QXWM_~O`Rm2BiayD<=nc02hNAWlkRO!w%tWtkl$HKVacK( zla}EM)~&+P7I@GC?^f4GcvoGb1Nioyf4+>{w=XmLPxT{v!TMJh)lXRuhWdUE?0uBc z*l4B?4$%jSdlbsch>x4+7rZjx`hZs*d|TmYysGPQc*x!IgGa)|+#%daVzcH zIw-m5Mc1VH=0`8z3+Y?TcP*AgyLUxbaBJhH5k7%BRpX$BqTP4uRJ(%A6V%`HZ_@#MSFEsT*M}BSW4{1kzQ9JY=Y=_>*Y6tb$d)8&| zW$UC9ujQ^;t!Kz)McL=&ysa>P`c3jT^QSn%NBD&=BY3bUYe~!dDeNg+=kK>LxZdyK zV!Y)#fm2`8pG&cun-w?Gw^j{Y`v}})|37LNH&t7;2I1nDvm@M_a!W9Kt}l#HPJ8z!?@1~3@VO}CM-?@&J2$D zNgNpd=c%Pr-30af&iT*z&-ptiZ@+b`?!9&E)~$Vcf!7mHKpR}jxtVb6OT#xlAUo<8 z*%rz)Ag@`6a=xDJ2YGVOMLFER1)pzF&m2SFntM9@Y22^k8w|Myg^MPvLCL;SiHvs= z_Dtxts*fu}s~9 zI%XC2w+v8yh8m7{OPl)(>|3#XNS?&S7=ZPC&vygsd)9}NAL135a|`G%Sp1Mq)YUEX z^aS%nKZ^CJ;)^q)@p|nZeif8`m!~WO`<(`c3yJ4}8}%mI0jsZK*@Wz?q+%`y^sI+@ zCYWhC`c7b4I5)-l8rnl0?(2V*?Sc>^uS1+up#v}0!l&JWyagWb@soXU%-ga4U|zh% zyntKZD*!o>7tjg$r6WFukyhd}UqCDOW5MpT+~^}iKK2El2h2F9gMQk|L73Nrt-w#^ zFV1X3m%X7A7t(?|LF5`CX^9gL{|NYVo&93WIWWES-vWN|&>QSRj(GxX+0qql+3YD` zKU?1A3*K(|EgWlZ(#qR&Z&%R~K0` zPOQ3pEZ6$t5Rb>{IV^6p+OOB_*F*aCVm}plm`ANzx{87#!sIkyax8w9M8GqCS6;Hbx`x#EWSH~I!JwIh-u@#5`3QvdtRYC z^vks^ckp12O3gK1i*~ga`rI*V4KdljPw|!Cp|uJCJ-L$wTt(BKh)eJf1D&9dNCK zY=wD5yWar((HH|FUn|*Hu5fVwB*Mz!7U&by8(TnsiMHTEnJq_M%lR&rHJ&B^2;)`{ z)+@M|ym2VrrjAdFlk0M93x36W+`*S+$Afrf8}Nh*H|usifkXYUuEqGExvlWTOFjm# zqm(!B+d$kS-wzM=hlyl7_S`;gmV3X={5aBf@=1)(xyPUZ^Uth5#-si~|D_Yx_#N~i zC&0cA^PYB&1JUkt4V`$aPzOC|(u+PV`?CJ@Mh*6-_b6~iTKrt#w}O$;m#dpc!Pd!sE%;lftKBP~KT(WK~QS0c~OUO247t+DD zv9V(b%9`5O)(LVT-l_`REt|1*sbVL@m;Uvfz%V?qu!~fI}rzASvSJ|p9L)Q z0b^$L;gK$}>^`;yktMP(&UCYXA=7qzNZGx>XWL=;%RTd&?jGRvMYs$0D)IMUP=>tU z-n+-pvO}kFKfOiAy=OL`ito~rIsOkA4+YijtkYW|z2&GY@NKlp^AVRPOeB^d%?!H- zVJxRE`m@X+4>AANtm}!&)>&q*0!~0XVzmjMKvsczLCij{3}qZ^1ZJ#ewb2$n$Ew0p zzK?U~w_rbX_EWY&>z4!N{{s{`qaR0VXCh40v_`D5gD4ZrO!+Y4v!euUN4;=5GngG#y%CHuO7j`@u509Jm9?^jS)NXuM& zgAjH37h~mjtUBU*5AJJDVhqvLkl~a~LV7t)t1JD3TyJO?h4u#h6MVz)WWy#=hIRFQ zR|5zAHsHL4?_{hR+lcv5Z)f5xIO()S8AQBwoHqlG0PNHFA{zS#kUx)67Us_Y)})%uBRas5d-VH)7j}v^06)=KLty zq$Ji6D2v#ujQR%oPupbu(uR6KKcWtPvi?s++spM%*8iwON5(R4YmLsbf7+1g-(Wv2 z=Np=OAzx&hjyjKVaNH4(a&G1s*uRYhF3Ppc!vK3vmE$nA{}Fhc|HJp6lVQ6_l$r9w z9-Erb_olq2|80*~7%s;T!?8Y!GAR&uEc9p6%>H0J>e_nLwSVOJ6MVc#YdzA9eXvz* zBRc`#Uw>gwvz&)SnT(E$ZwTDtgkCRrVi6>i2!eZU6eFb)kO=3merj4 zrFD^2Uak0&DRWC@+RCr;zG818@c2#9*d7~ZiUaAlZmdnK9^Yf*%-(RzyBu*w3S->U zQ(>phEfY@jo0#Bx8sp&8c3NwK$2T0`BYs)V&nwy26Y;>-*NGKF%U*yD(Z*1h$}x}! zIz*nn^5t&m-}kZZ-I*DVKU3jbgbe!y6@v=zR?*>%h5o4R2Rs1@!CbCt3E zc?bO`H9mbQ zg=5BhQLoDJ3+glWXPhTFe&txkwRboAC+rUxzkf>H&gkoQMqlJww2xPy|9!>DN$leQ zUZwE`cr^5Nmo0*9N0+8FH7t?in{^^+U*Zwyf!~j+OLdji4ZvJ$K5*wiCrBH{zN#@QNt934H8X0_?IxO7u&mQPu8QqgK-eQ zncP%Lf2<#0Ot_0cy?qtFMSUgayjtn=6V2mXwwHLevMrbTy=7Dx&rh+<-hw{g7W5aS zY_)oh0Cg+Jh2`!q$Fj|2Ux9rK<`w$#`!+%6SH{48*>6Ohz_ASb1Ku#%W*&PBeIWJ$ z=oc4`_7rtH`*QxZKIkEqdrc4z{R6%STZ;Zcq}pqPIXhWba{Ywk0p>0Hss1@v;?fW8 zne6l7K05R})qTafJ@ez%@=RVC^#6^! z5q%!UD-ecx#7-3`Gu$KhEZryx{d$XC=;%b&`FIM4V&6TABxYmeDVp3%@E)H_JU*EUQ6T^%~mJ zo|R^9iRIc8i}AJ)RjDZJD-2F*&&uV-?e1AwXxz0uD)WuIv`1x*ai{j+IarkzcaO@& z#$DUJGQ+q_yH_qW?$qv;*BCd>Sz^qm^>BXWJmbbW>bb^^Z`1N#VX0sD`IT57)A-#g zXBc;Bx5}x;o!YGuV@-*JdpWBn7*ZQH6O?g|kFdjW+HKQ8m)I(?r#9+ei$zs;;~pcbx*GQ= zQPtVFhl?uCgGl_L>U$c}JxEkx%%}V@);0NyI$hr9DRD4Q<~BH(3p;(&Oq^fQXG~L@ z*EgZJ?akjt>vi%Bd7&-Say#Z2d@)ZBg*D9 zoq1UH`zuG39X#_4_FuZTx?e20eD%DN%l`n~JOmoFVRG-(8uW=J?%@$-lR#HKu;lV9 z@bD5W1A+Yb;-Vq&^FS~;|!GN@o3XdtCD?e zL)kugus`;Al#=e)I zzNvY?)^)%QUX!Xv`_Hu3yHlDz{5I&U9@v0tBqLy%>54QdBXzwXE>chELwuFapi6?n0yqPWGQHO{dI-WbZrErYJg87UM z!n6CO0cVC%Be72?tPE#w*;m>gkA8nV(#C$07=nJuJg3UlUvR$T^2RB+FDVrdzQJ^6 z44y$wm3Ii{K6+w(Bpm%4xDU?5_xj>tcW|$B!yZSa@Z3%Fw^z{*YKX)gNA1E#_0sWPs}2Fi{!NVq4yWrVvD9E3B@cM#_V z{3ggb=msE6(jwgB70a$eUYuFtRQ-xHNTcWv8$y^DVHFwfz2dX%>PEovkllfMC-|Rd z^MJqM(K(n0!pMVk;;_hn0ko~pLEGvaw2a>Y?bD#e22YtTTW;w$Tj z(l~rol}yJOSm8T=YZ$L~HN-*|6Lo#Ah#iGHlNsT1a(N$X#T3wjeL+N`;( z_gjjW@;_|i<5~jZ3#MhgA57~&X6kKC5bZ(8`ihYi_xBi?=r_v9YWcsYW%abh`i9mK zDj52b@O%w}r-ak6#{|4d!#wbG7V2AdfW7!0 zt+Ay#?3H2r#dOhqI1T$v=3`IBJnYSwld_X%$&X;q48yN8=?1;gS#TN#+i5`C7LW$) zwFyWA_T5O>qyc+(ev!;WEt%mygnd2Si&HlS55{tbv0sMy>4h$s&T#09d46G#{7)m_ zrkH#~f6nBa^rPN7H|NiE-UIzJHf+-3LVVJV_}#`0orM`U_);d6TV?azls*qZp1ww& z-p%qzKTDo?)CJ7b{~I!W>k5>Kwk^vg%89YnrMiwfjeC54PA52cvHl;#qn%r6l738+ z<{JiiN#fTI46_|X!8u+*uq^a@ZRaJ6rnDsip z>9{GpECur56tXo!w%E^( zN?xSvM>^oJ%|C)TrEb6*_bPQ_U*bte_g`*6pZNxiLGMQY1$}wc!QW2pf&NQJvmau_ z+5H!mzn;x?N;@4_dFkcV`1Zig@>;x~=l;bx)pZZzor?E!c$eb6iQ#xZ&G5O^b&rdk zl(Ftb)Vt54y?7A&9G^?sNt?qw-p&m@o|*X-WMx{8^g`P~+)coJ8f^-2`=<1cIsABv z#QPX{uTcM#4|iFZI{0gyCjs(3%=jS(<)nO+lk!nc%6C7*As^*U#rs}{<4w71@xF`U zS4jEGQ}HfsPDlO2foVB%f6C4mbQ+BOI0r4$aOf_i7xEt|P1*SjaPBbj_ivW})&GtB zl$-KXUdm5-DZi!fU^%7EyNjLp7KGGyefL0p`(;J?zJ~6fiThQ|*N~s@XOIueL3f3b ze@L_Zcf#-giteFvriZpu58DygqgfBzk#E?De8W!ouDcC*kefCm{gEf~jeL-Aq(kD} z1iaheuIUpaY8J-S-;}bmH{8A9=6BeBhcZ|PKY{cz&$)L&;uD4}rp!s1TCfA`ijL)q67k5o{Iq*n3H!gSdzOi1vBteh$ZvH@|0egcTwPDZS#;xp%W}U8 z;|+&(u;>%xIbMM?Hv?h>_cNanJ0pNk9KCO#!}FZS#`yYt;Csf3j>739@nreh)m&e4 ze~WAn##r^Gsb^QlSF=897=Z72V9diY*_Kn-!-=#tEE$h;AA`gh7 z8d=71;i>$$u@sa_#~Cw;EXhb1sU0D{Vi_ zNi|Ro_N^Evzny|ULL>G}IQ*Um_Dj*tyAH#UM((TPeu(D1OM!cSxj&|Xa}!?BasHvL zN~3IhaX(QcZC1}+9K$=?The^?!Yil`c@I+qaO-daoz53l5;chFuv-WSR_!u5MH!R)2 z=h@-ju!Yum4!c`#J%ZV!w;>i-bJw0pqNN z#DT6+25{a%=?=C*dF4KShAW=%gKp(n0^^5tagU3Mlj$;XQLl_6jBoG`=Nmj*@eMw$ z@m_^=KO)oFd=C3eE$3GwuolHL((24V_uq>CI4@p{H7(4Amb;%E>k%a#aL=MF)2ydC zA2=QRSy+B6YH{CU?flBA*t@bJ_1a2Fhd$L++<%S!REog^{ZQCBanavewhDc9iQ67` z#XvUf?X7wQWfbQb(Emg|f%#J2uZgts`DlHU_2@5QML!jy)~er z-Q%7ttjSiA=W5cX_5j_rxX)10MWRiRaH{5m`lG_VppxlBm4`jFbdv^hqWtq7RgU3s zKO^D9UBKr)9|zA6>Y^|Dv;zdz80C2Y`Q8XzF&=YmlTw;q!TKo6HrGmVmvWQn4gda_ zkHa|o5Y|z5gU8wR$8^krpMCCC>ILhdoLlBOX`COa!h8WBukIjCbI*)Gw|x|r`q;-ii`W~KQ!b*k$- z>I-+?@vI@_Nol$fX*FTZb)jW%LG_g^w_a7Iaj(Ws5sSVI;*$PtwLi)d{cG5kBYzEz zzgz>4_RstdF4tc`)_{5sHf^6-;ht-p?Wxjxt<|@`v1be8Guf`}eIu~_F=scpuZw!w z%DxEqS3>_Rn|C1nQfDX^p5ESp7+Waaax4Yi^1J}b1lBmLz4(?)?1NC3Mc^abMx7r# zpFq2n`Sh7v-E+u18{%5Vvw&zzJIS!Wy4ANo7}n@sw${+V-*XMVzXLhRe?Dze=51`G z=f50zS4zC$@pt#KLD(BE`JrF+q2}R*Kk5N#SCSXo8t|G*Ua%pL6T1|=q&(*tUg)o} zybzE1Q;u?44teF?V9L0>IeZnu@tqhMPFpPu(ml_pme;1?jEbeZAbVo_M_FLg{x){4 zvM0!gJGkLq5XATA@M&+Jw#Z|*zLSsT1#XmS8Bd&-YgX1iIXgd5H&Ld8z&CY9ENqhb z%<{{)8@MMPyyV{aAlchc_c|W-8IB?xXG7$-kS;}9(7o#<<#RA@Z(YK&D$k{ohIgP- z?<|)AIt!N1t@wWm0&9|GyxA!@44d32E**D5%TsZO^ z?T*=p$u-aVG%@-h@{;!=;_kpI+?!O@kcN9*VQXB*u;a94Z}oR#Tbxe0pa`HS&bvz!2V zIksS(o`vBH&%HE2x2F8DKf*m)9H-EB(08k(ZLlv9jj%fOU3ku@4rhz%YSliZbnxv1 zzMDEad;5}iE$(r*cn-37vM3p$*K_0?4NQ`H?)avyT4Izxmr{qmd`(}vE9zrJ}h73c2J z7ut&SvMy&`!glF>+6&4B_OVO5YEVMV?DZc zoBT~xV4F-jXb^@x#;=Oj3uTL;{q z?85}5jr~^ga*(D4j0bKx_QKnY^Dtk@_B22T=nKp7FXuE^Ude;|1i0Vaj|)A}Ki}vD zs#3Z+D^7-)Dzq9oS^)dVJp#AU_3y& z-bllRv;o$CUg!dElMhGHo)D+I(I@LH)IlD8qjwzcQ)D?Hd`nnI!s7vBx}($Ip&!mo zk#;@yWd^kOrGfhvh(lZW40v?Y))xiSST?i;c2|M?3MjuVw3m&{Tgb~Zc<(5;hr#m*_>k@<(8+d~^~x`h$%iRf;Bihd;{eGuJDSo*~Bl6S;4v! z_1_8)>JQ9wVa$v3jHx|kJ-Efq^OoMFAL4rqq(fa*hJI3$9A~PqNDp*fQQMPu(lu?Z z#r?jJ9eLDU;Jf3a5C_(ER$|ZY%8F9*yawadYt$G6HYo4Q((z(y4(&^GIOo4~IL4|m zm7JR;%yGdCxDUW~r&#If!MK)+m3}$V)}0vu+gghAMTmp*VN9DGy9|J>^p&=@M z*nY^l4dAG;#gr{Lm#g|YwbprN^er}M`}q)Y`Rxbgk@EWM4cJdC>iVUK$JkE|@nJs_ zhOE_uGZc(HkY&j4lhilnfLG9lW?<}P;H??gu0R_dNq?R#G2q^ojkVWQJ5jMQeLPsJiR%+{Z~*5K`^31S0C&Y& z1?DDkegO1XC-mU%mbkt#hx!r+ZiYj4@-cK=2f^41ca|Uw^OvMODDlY;^D#)5yzd0_ znCv5Z#^Zb}%3>VKJ8dn6X-7KI&yx3+ps%_Uch&9eE#(6}*Do=DGlcVfZjAHLhm`lv zAfAnoV-wOd0kVZ5O@}czI-zU7m`w;f0{JuI`p0AdJ`DMAcP8`zeL@Bo+PUrM9~{OU zr{Re?NjYCi{TzZ$$dkONha*UjpBKUi+q|9xFV-jI#Xhd%q*T=buO4`}BaD9N$FQtN zbFC)M!CrlxepPQ+^Zjn_?~laXv8!xLs@RKmyQu>89O{K8jAwBc6?t5WJVst`LSAo1 zUO!XXv-;W8p4HE}dscJKb7N_bN~xn-%o*dZ8O&i;GhcXTkA&TTkxtCFD!8_LHSWKG z?P$0Za4KNnD0;wdz`#-TCfv}Uufr8wH=j1L0Q?tJ^DdeV?giBw@t8bS>xAhs8*1mn z9_LGc%z5H|ruo$qJbhz&!yeW8VLI#&c&Wg{L;8(w;NfWrPoyl0;l8Z$+N&Y^)iUn- z`LHKE!EsC6IhZG(hkU|AIl15JARdVez17X)d_3lPVY8esDWa{;;#@q^1)IE--;2iB z3U|9GUB>l}VcFsxLvnp^24sQGH&U-ilk}TH-KcWt_e+Hza5hrE2Jd2~;~J#f@*6{b z@N-*!qmYIfkUJG=Fto$rHwAtdTYf{~2b_arEI-r{SP$BHaFpeTI-_nl{9yNfeyBt0 zhQbdv?e{~SBEr#EMgLr;AL%>@ISwJ-!=OI`{I6mEe?ytu4t-INEO+QLl|?yX=VX)% z>dx?}11`(Dyl)Bd9EO}nAlI`<|8q!hOFTw^hnx%4d^lEM*^{;l8hi(a^Qld7@Hgcz z9&VPu(>KjPTdntrqi(kM4x4qbdh~0#p2fZh-RPJH%m)YTw{=JEjl;S1=J|%0ld#36 z`Z<_qe;V@~k7J(WVa#(ph&ik&=rfJMb1|M&JfrYrpg%KD%jr1FayfTr;FZ1Rc%|;t zoeKhYHRpDKT!Tat#}y5K#`!b28wR2M08PA8g1#u`@&>@~!4#|+gE!tXEFAR^`Zrvc zc5p8j*Nkuvc~j%lB~|y}-a2zf8{3+Oi~yRY5nUnE65d}8TI@BE>rt?0zn|vEJHQ$? z;ofTWVe251N=HeR3vxTC_o!doU;Mx!^z;56Ex(ymbqG2}ovCpH@0cLnA>gs^%RKhr zn_EOd?4h4Rl)Y=Du#m<+%)F1=!fkg@N=+fah zVjjljC=-VeMi~Uwd*og+l=Cxbu!}UE$84X_PkdkX&6*n1N~*X=4S9kxkGc@}(!Su2 zyPm4+jX&!KlwGs;gY{}dzTP8&I@O69EN!e#)Qhgz>~qKMwMi>npTn$(LmYGDtVd<~W4GzGVIl4udVStfAaNXU7&I4b=JNuu;1X zF#9rSZ)e8~9Cu?*(i0#5_*t~wRo&?m?Mv zaEmHAwq%+(P6XWGPEm6bOjGN?Gzsh(tizpuo@n3<*XaQc)5H4{Dbq3Nfp>m-oJgDz z&}T7$9~T{%e&W|*J~ozn28TC?2gkuVQ^S|~{onFt-;Q&TM%SEIlljp;D2+NlO&&~y zt~jQ^I6&DQc&KlWT6rHC$m1+p%!x?-B2M`YSnxs{D9bj+^K<)ye~YrTq;JR@pl`(e zlR81T!4KBAohFn)zl^W5%mJ47;6hfjKE=5`Bi@E#kLr6Me|lB+L;L}A&#Cz49okLYVJZ9Q4XLoxROoPu(jh(|!m(>p zV`_6g(7s}!KYX_y`@l-Z#lr?0VY9G-CemWx4Ia{C?1Q%ADCi|@l|xxaVf-gx6SA%f zF4M3D>Ja0_5glnGl;Pq)8IDWZp!b=>T^FfoqJ@lK zIOrFEmSbSbV#3<(LPMd`lM~-AFt5(2rw#=GXJRD5rAIpd0=Hc^jj`ZNG5(kskR) zS})%0ckV(z!}#-zae3){=y|@JZ>YmKy`^oLE9*CMVI@`O7T`lCA)eB9S)KE&^-@JoACvrJG% z=0hXeJd-E*{+n#`;!&nzp)Y)^LFNVLV&t3-NppHF?&7c6y&@P>tX1z zj`IFSg%oD&Z`*x zNFI7`d>pn{JF6y~=Dv8IAD-HR{Yn{;F3NrskI~I01tb ze!{uVP5o&3?$hPMPP>%X($AlML0o?T&itUXGcG%Ceo0&}|AM&C=UC5m16!~DbZI^R zWL#R$+%J4iJ!>7_4;+q}Y@70@>wgf}Ex#bHV&Fs|u3(w``JrXHj=XgF!n|Kq57b3$ zv)K;u48?Cxb4+9H0j>?)x61oZIi4au*IFCcFXwy--qO!4lW|h zqPHmP583N<+Op8k=e@{XOCvc9<@^KZBUm=YQNPLi`#FX{ndQ0{>#y-h_X)^p`)!8b@3#l8 zGg5y7eLub(^y5g&_GCv89{JcfT*tt^=1z4Sw~!V*NIMDspXhjPe;?`xj)m$`zj2IW z!VZG3zrO@t26j+~b1hA_eXQrToOMXAKTa)gZcDroCfEOT-+_7w;0;+MZ`AveH}E+o zW1Jl0n{mY|^mQq>o`Z=t{?yUZARVzTG4vj^Sq3-V;$i$a-kBdnpJ34&JlMZKe&ffu zFA1XUZPD6s-)qs^{)~T25D%QMl4Dmp{l|cJuJjKIqBm(ba%Sm#vH86Vd8EEOD_iyF z3Hmy$OSp$9xW6#r9BGR@N7^_)g^iZ}yfE@`PlJ)s*2Nl&kHP(^bpQRu7Rpdide~p3 ztuyT|^s{K=n1hh88}PZt4eU3aOAi72im=v%b@C?G!BU?XFL11mb*r76CuLdW+@WLi zjrju^j<*c!(!944V~h^X`vF{g8&a_jgt{C4oWE*1n2K*eV~y!B)|!rBy#Rg3rdaSn zzZ+}G;0KxMM>qZYAWY6vpq$q&5j!z2bT&TxTEx!){+Jf<;nyO5n%L<@eHtte>sSZ! zpda5P=W;l2LV4z+j4wf5kb$uo`|jGmK26sL^AL`(df-U>ISQZazY?B>^$v^)&PBf-)`g|!tybt z8GfGSLc@mELwmy50X#-9jB*fWc_KgFQ^2+DgOJ&YN>KV`+9>b;KrR!W6FHVH_;|K>|jMq!suZ1UVHyE!w^v8br0LVr=?gAM7MnBAU5n=V{ z+sSaWYpl2Hq4Ps{TpZtFPlOrEaj%U@6LAjWAuQ`-mI>&P>wu^O)ZPY!OPi)`o3LtL zVBanJ#KiMlHB#QGCf&Jkqh6NoRdA!8u0x*|d&k&!9qsu8?yg`tA{?GFdMeH=O1BsH z6QW#6ciWWFYv7h?94e|ZSOzq`7xBek_1~lAd>6_r`=Ewbe0YuY$2s$6fAEd>D*y3_ z*N=}l;@c{JuCJN6rUu8AYsG~&rp367e{fvJ{~U1*vEoACK&2fzL4C4}vdXxALN7fn zda0LHmaOPAwkWId50=&VpF>ve!9Ih$z#LiCnOc2LBvq|dG$0Sq4vprUdFmM#HOG^V z{hxs25f9GcDK~gy9Vya(j>YcVbNqw3^MkM*tXs%)Q$1mOXp6VXwLgp%IA_Uqs6|+N zS_u2#d9=mULHm>{>{m#V`_O;`S&vVs9$B^u`wHva7}tQd(T#cpxKGpL z6&<*q6^}RV2|VYLC--g!@LK4)zigoL|ajzZ5o81u>$vw={HU?>Z4Eq2KjkG<;`=klu8h@`8oH@jOe0Y0!9vk+DJv8i3EUTm2_(y4f-22lT z?f{+`ORq6JI|juUFUnS#FqA3mk3$)|M(Vk7P>C!LjqS0g2*8I1mDm{(I_do)05=GfUjnl~VAWwRGgK$QRvUvuF z>E|3U`jY{C#*4C<1_yHt#Hr`L3R|AbMA;;b(-5qSt3=sFCJeej-B=aJIu~Vi3v@z$ zrX658MfvK)`&aGo>1gLUN7NWuQYF7byb5g@+8W|ePpY0$;c~qkd>awQ^>Qg^3pyYB z5$^T2>Id${^903f%O%f{>wMv!j!BqPYA$cqUTM^=^1S;{oP9<6dkA*QGo!SrsXbwH zXisIGt=nPN(G5efe}5>>AULjNa;_hFVeAM?xF&i%G!^WXh5{NrzF)iK_2%e4YKo>za4uCwS6@3_vQ zs{q|oT0XCMT%NCf9=1+-y(J1BNSHcfdrY07Et5Ly1)b4mac5aex+=5yVvoa8=znE| zid#^+Y?`$T&@8@iNX zPvR;0otKZqX>%VE=46*`W&e1|D%>~MihJ_pIl-fVdB+>p&(xkl+?^!rI^3fq_eQ;j zvQXxGpy4&{zl9tb2sgYk$Sb^=7vRRet(tiem-Qy-$cu8;zk@sblhb2x?)UVWH1JDP z{36(=e{PjO4Lr-ymy;&PQZOUlnLifE!c8nr(o%)Gg?wJ>9GA>0ZQzy9jw7vY%HZc!AcR=YSh) zEndVWFVOkz>2aMm4TGroWvzB&`Eo;lE#6;0gcU$*^ITPx6ExZAaQ0 zZ3MP=<|gD@5o`pwjdP$+);UgWvyE&*K753J$VX^1O+E2Z3F-~2&H>z*>Tb3X;!7BAwG7wDvoFs(Ofecu4t-T}XN6hCPrRvM&@ zyi?L_BX9@ENjmZd4{0N|oVDOvtN2PAsST17aAT<@CvnLObkasFIi!sgX_@np50Jl} zFl}U$@@JY3=36##17P5o{H`Z#h2y@))0j&-O&dFkxGG5Ic(QdB*H)gn+ZHKtjBj}{<%+j_q3voRNUJqKl9t1DY z`fUer;~|R|amfpG(soduFn6xntNE~<`@!#i#V??KK^mm(++WgcJ8%ccNjmZd4{1BL zocDt7y^622oqL1i1l+jEl9RaP1v+Uvrp!YQX*=_@%yW=ukRS8#f%Oag4$iS`Wj4Y0h)vGX0>Fc|KI<`B20)2fXH3yylRX)12pk8xL8$h)Z6elX;GGU~iVn z^D&TZ7WmCl{A8Y6X^?q7tE4&4;SP|KbmR>lGS6)}XMpbv#aHI}j37AyH*T`zBrbV@ zPUg8Khs^U)TIS)%6UdJ>mB9J}eg}tJc{~O%aQt~r*a|oGMI+*2f0Ffs!ELEdXt8bS ztL>w23-$rOv)Yzk$iFTDZOeAg_`ZQ{OBd*^i>0?N)SG21fEzPJv#k)9dIO!bmG;_J zFmKI!$v98oZ>KtvmuR*Xz>SA2Uc@CY(D`kpm)3iC*h+iwYp?iOZHr8Uw3YTH&9(w} zfSjZwZ}5<|V#^r`zLAQrw3WypIRQ6rvg9N#d4W#aifUV=9MV?0Xqh`Ae;_~BaszDz zepoB<=XZT~z`!y2FJUX(*oxJ*Y>%(+_rGde0{SLyZ9nc7><50bO+tBX_)4zj$hxTE zbLBpjzhKLm)YfVby*qG^i3i`8;n{S~k9sTQy$13g8-72vqlS@>*dGpCjKo?izAy9a z*O-T7o&{jV>d7 z1Ne|fTsIZ>gWyjY0@lZC&#_mj8)U|Q9(g7Nd^lH)`RMhSBbVQUJ#F9X7NnC6&gX3FPpP#o1$?H#16ihGPZ4w>?+qY~xqdt;^1HI+ zu>ffcSZ_K9AJWxay+y0C?gZrJDraIykNgu7wLy*RNPssQi+%4cqUT>iv z#Q9i;JD}@u0D1#Ixu>i%_`9+9s}y|bMtnS16$>|cV=sVw_kln5Ok$n#;!ntjdlqDT z2yfuGj}a#KV8M?3ZZE#oPMzy8+{+`=Rw3s$1>+%9QZ^67q+s-mIpF$PyE#gx6h9n*`>~r7wm`8UQIs4g*VG2 zai|;Uatp_~NI&zBwh#v15=J;-+LY8oXJxx@A2Bv+-5Jn=Z3*g;_JnkIr>t=M{h+s= z@C&f97Rnv%i}k}(h?9Eo)MM@oX)!eb(Kko3epwaacztjE? z4KvNm3#>)UI;kGVclxwrBFK&c?Bk(+|i+07Ty2!x22b59Q{_t-vP<4|D zUp7?LO$JU+P<0ddl1A1|cn8!;rDvBvY2PUK{MNYKvuMgsDawfc9vABthOvxH`;_U{ zUY7;qhFD1x&sA!!_-A*d6Uy@`Q~r9@L5Q?l9P$Xl0Whj<1(%}HqqAKH8LYC03W;&_6qHacYWY)y^}RK$Imidhp^K( zS$1D*t;@l_SkJRw^yiJ#H`e9c(6`o!yf3E|_u#;9Ao9TLeg84Nhw~Wa&xAdCaW^l^ zHrluf)k!V$oE5~=;QDzo|84tsm+aeh7-bIWl{SgG z@eK0k40OnN?e=CW&%&dQARhC40_sA-(r)l({Y%|5U$C#wpFhM!dgsOk%ck@1DDs>0 zro`I_yqI7-!`m&IzNFpIM*Vw;aRwbQ>(PVYN15HwAJ>Sv9xH7QxCY0BP#eN<*a3CU@LgwxYd*lS z`Meg4M;<6+Ypk}9w*9+cJjREIW#cGp&Fp11ZK7I^^DXlVAlPz z?ra5HXS~oo9qlzl*1$DwS;>u(jaj2b_dcUguIH|;={f4I4)MSUYV zMe56e4p_h5_E*)1H0^}63GimRO2Jpv+2UJi7p2S(e|>W=&YF&v_5gp%&GtK>?W}D+ zZ_2(p+x3l*e^Y9&$~TAiO7=j0)rQhmVz}2u))#C)82>?>AEn<>$j*9Vy7Z5@*Mw(b zJ(giC%Nr2RI)gZUoCtX*KwT=nQ{6> z?2G!usTpU!7cJ{gl;IT6ppQB-4(k^ckejkdxxr(q5Tn?Z($5o)JK2zqKcP-!I}v;i zgL%PzCGUvk+BWNW^S$Z2scVyG2xD8$w!)+}AY8XOrXSl#THwk&1W(y#M_#ZEm;1)R z>taVVU5q@s*ejr0=v21ps;p8D4Ig7Z#zBX1!1M2yvTTs!Cfeaf>@|4vWtE4<4mV(b zgwzrId8aPprd^vfY{cFOnJ)NK2G$9l?CX+QzUYpJ8~MJp0sEXi*xy|7T}*OCE$-lT zc}6nNNW=0XuwFm&W7RM9;?CE`)}qP|o5HZxm@B}&;*H={@!hrLJ1}0ZKZ(MI*5vPv zXyzYv7XN8Be|I4N2@G@pLjH(53pPSsDLX-$rJeLTSqVF-csVT@w$;HdH&OUxN|XzI zc2}=Yq`Q^nj#(mgRn1xo;Hrp8BzO6Fv(qgzt}k zMAu)`d>?n5@WLyu_!#dJ8OHFl{}^V6H@h8;jJ*KzFH^r8+{zJ!jS2iKzf)nROhAi> zzmkV=@{WJ?-=jF-@BPQd$oJt$-j^#x!}U|U@E!Hc{sz9w3uku4;6bF^81>!Nc$d7r z`PTaw$*%l=-PsrKd#+hADFcyYKKJsgb?9O?+#S~UoqG^~KbcOv`=t;P;;Iz+O1WRX z*VL-_btmP!Wc3B(8NR1OhJ3I7^kMnddW%gfcTGSrBpk)}#`Wjt;+=BjiG+=KuZ!7T z{R!SlzxsY@n^aL!H)>_=_*7AT;Es7o*QbhiXD-Rz^?a%rSGMr*l~`F=`^t)*FLs_d z`=19tIeh+%i4S~yX{(QpmrQ)@QT|n3d{d_vrqtiK`r=btzTJ1}qS+S@N$qj$;K=hY z{x<8-npaL9zUaQczf%6g+uJXi`eOBC|N8XCi!NN7aPT)PCtdX0Pkz-9c|r6=Z|wZ^ zv#)OceB5s{SJvNl`k8UjM~AF>^xrf}HUZTnuG^;ih{;BG3<4V2W=Vp#B|9;AOrK46>J$l#I$KKk}e{_68 zgy*H#+E)EzZCCFc>Rx;L`iqmZpZnmm#mj$u z?Xs6Tq`W*NZcO?WgZuqy;-ZmZ?(Z@)9)CRXwH`mb{KMz_2Pa(Q`P*H0w2$k%t6=iL zy;J&LbLFY=hyJrFW?yXWHE$%o(QW63W$*V{d!p0lkCc@jZZkBhB6D`3`}!r3BI>2@ z-LG$GH8kP1m!Es@FV4!k_{!gn*)=Nj>2ddMnf#BJe1q=k{my`47c@FsAI<*kviL=# zPd(M=t!I^ zYX`2YD2zB3vBs0MtHW>l%q+dC!;dso@!!+y-*F9jW#Zez=YI1{tDC1~K7IK4cki7r zv)|0PH#gnunX~r4-ugcNqCN9&{(Sr!Q*Zn2f)oG#;DWo07Nn1hy#M4Uf9|t5;=VhQ zonsBxjqA{F*`!;x=Xbra_=-R6TV8zo@Z#G#9vr)J`wx$fKHbB8tBg!69G`Nm?Yk3R zIW{Tc)vZ+}V;bJiIq$~3eJPF(cI(3xz6Y=Qro3-@|2E;>mOcAU&Zm!d`TYCj^XHBJPsfcv z&YClHn8-`d^@+UYIXNP2#-&r{inOKtopQ;XOJXM#7UmaT7~8)nLiE32Ab*P#G-qN! zVCcCd`qQpE#h2AL4M#|?$q#XgB6 zU1xKqcd;)oLYze*BnABoZpch4@)e7WEGR-O$(iEI`?asI*tcSQVPX2pBKSL}O|XK9E4?SC7pMDyIm;JmI2B1L_RYyk&-5`S<(QMdyfDKDFf~8704nhZ zE9}{Y`AZ7Za}f@MO3%ph2Vat%x3oF%iXvY#FvV94uqQntnZTiCCW?3XfQMwfO17%_Tv1)TuTt`b7?vR zT#>Z+dI41zUr@ZFSX?26VQIMP6>Q)bVaBT^?% zjupA-D+8`)nOu5dnL%X3m-IeZ9@w_ z7kGT0d5N5q+l_Y{ah=%V&?Vdp@q?a>^C8}+glL8PAB1QvoOP{*SkYFvD!SvFhcUv{ zwIA??i?9kDcJF$zXniUL-(8#_+y@X(=5!HOJRRRpoF&2*&Jxb{Swd7^A>1YNglorD zLae@8wBEcxxK^w{+_wr>!73p(mkQT<^ltEdRj18bq+^E&t9(_2ufPeDQPz7+_)>%)_!4o~31@blXq8nb#O@;^Z2u9YrBS$cA4S@Z31<&By-xhQ zXf^fk7$|=WU4Dmjd?(ydILtoylyFUraKiRSKtXLBX8}G;bgHxC>^aYIC*XqB{pUMQ zzwVBBx3}Z&+Q$)33~*du401%lP^a~_1jqRz!4Z4LJMOp%PDI0GM_f6@3BPih<2*9W z5hrFk-VZN#Jh2NM5tR*o*^ZNw?T8O^o$xhz@Y{nERVk3CT<0^gNc}~XFuRcQ5@i!n zB{Ek5W9~%ytIlauNh=K_Tctm3igfIf@*Sbyk^0?6ztIuJUsOpu{Z_r-s1n$n^pDnW zoV}EANB!=k-#E*Hzo?S)^jr3c0e97KH~l_ezhQ3piz=br^NllB@{Rcp`R=9PvHIOx zzx(KSU;T!;;xDSCzkXxQO~R@>A5{{k;duQXsNaM18|M%37gaJuzcHsPVOcQYpP=DH z`4%PW75WWnAf$ni20|JLX&|J5kOo2;2x%asfsh758VG41q=AqILK+BZAf$ni20|JL zX&|J5kOo2;2x%asfsh758VG41q=AqILK+BZAf$ni20|JLX&|J5kOo2;2x%asfsh75 z8VG41q=AqILK+BZAf$ni20|JLX&|J5kOo2;2x%asfsh758uQAQHjZkqlYC7OB^!D(uffwMvfRYB6-BqJ%^=OcdhL_4=b>G4NZ@C>xZ%6|n4TLlh(m+T9Aq|8y5Yj+M z10fBBG!W82NCP1agftM+Ku7~24TLlh(m+T9Aq|8y5Yj+M10fBBG!W82NCP1agftM+ zKu7~24TLlh(m+T9Aq|8y5Yj+M10fBBG!W82NCP1agftM+Ku7~24TLlh(m+T9Aq|8y z5Yj+M10fBBG!W9j|H~SPg>XFK-xY`Ahv99XjSmNG{<<0m-qM}YN{E_fY`WYoJbmy? z#DV~o>#+!NSib9lC;zuUwYxB!ovJQ4Hx>-i+%I5^D^^qP(BlVIq55nv#7W* zf2AEd<&v~1zT(-1`5EIgGYfr1MG6ys7Sm~Y*~Q9#c79IIr0aco23%g0Wr3G1Ctljj zIV4}<%UE8Vz9`2xJH0pyzG*%sWm;OUFE^uLWm-noQhEa_@)aAXs5m`iDS@=b>Df69 zZ}wTTJiRbeq@`!%L&}V-%xt7NBPYMehZntkD;WLa+{N@xOH~LK+fBC#=Gt(wrId{%X0Sy=Gynzdv5otFRW*7TRS#r`uxM-7> zn*{|kPkm`w;N~k7k2(3`DJMhR?Jg4A-MQj{wuSl2^D@(lvTuaA`DvoD?Gj%;Qe3z) zO`L9by|1v?w<0aQxVSKT5yXRJVDe-8Vo?&cn3_owZ%3^t$Vo3=oL`umR+Pm^kb?Xr z2uxc78xe=2(u<0Gxr=hZ@PTL)WFcHd;&}%NP7{B33D=4=@harK+anql7plbi+^`2_7x#hS9Q)x&&$lAdJC2pi@Q39iEVeF>Ij2^ z(?o@9aW=w4MaL!LnN9^aAicg~@o^{NhHcflGV*hCVTPZ(GJV=v54nnbh1dJU$1aps znCstMVPfr_s6xbU`TK?ZJ&9i-C1eDm)Xi5>ns^fxLd9JObJNyYjM5~+R(F^7Ni&Yu9!PHxi}xci6he%WiLTN%TCV|kG2smltJ-H7z<{ZN4Tcs ztSrcymY3;UAx?x{nqHidm7TYQ48?fP`pz7s}<^4nY2&A={Ul}#E#$dw|p0W zEBDCXy%g-%M)LPsPhf$A-XAxAhWYq_;RNDvX^8Pi#t4*rWX`|Vp8thd>K$A?uE!td`A37{zCjY zqTHji{VA{3!V4Z~;op$`8=|-=(EA=#a0TQoHhaZZVEW;|ctjnTuK%1&*L`UyPBl~f z=oK$VSQKl$;sI}%I00Pze#PGdb^P7`9e+>B-`zh@JgHVK-#)M3hO|$K&Gn#HoB-E~ zla4dH(1&8hD(SfhX#GcWUHxyFT$74*5uysz&6pb^#P5K-ncV?V`5*pPm!KOUPOai^ z-R=C{U5bh-Y=Un|cAoeyViF2LQ^Xu!@hsGbMd`T(In13i5zPC`)AN@2#2rv##a*Oa zeJ_72@ylxNH<98{qzIdW3fxzCxi16O{buClC|^G0cX7V*`9mZNgK~cmDfWWbZgfP# z#21_SyKTGt-NE0wO8%~|;%|-o-HxsbY~uI)-S%((?l{HYFXZq35_C?)yZ7+7<}v=B zdfY7~+uG*(BJpt>;YM9knJ#g=>+xr+p^69O6iO;aa|UF`7j2 zczaf*pTQ$TcK5`K?Uxs1qTeT8X%D5{PwH*xD*D6!*nSZyzC!5c-!k-zC;40c6o1z} zP0VcZkM`mO5Gu>*`HlSDyn#Wg-;^dEjmly_f=zOUD2Wzs9s7445fno5Ouq9YQan)Qtx zkq3VulT-L*?aIE0Sl5v~H}Mcc*T2os!}7Q89lC4Y2WIQ^qLq0WX*c8-E=|kNOS>+A zk@$B<*%w^ZN#+}hfw-%a?AxW$eP1Wldw#GJaEqV;$oXg|3Mc!OPj^C8ACUd-Kk~O^ zFMp5!nZFGm^LPIz{N4R2#bo#F^-eh~b*eM@eJ5Iu4eLO*B%LB zVW%SF;!>tAtajD7w!0VZaJKiTbG~uiToHM~RpQL9bQ+ur*N4u{bK_3AGE1Dy zR7vZ&ciovMoy>A)y=%3r#>vb)>|X1howU|D5q7{6^@TTT_MXV(cilU}k_H!)ht&-@ z(SA+%8Yk|gJM(a>ZLWTYo#Snj);MG54qiRG#PzOIhe#fARy$urUh;%%>~7a3W2Y|2 zid*Yka!GQ9`=qPVGqIq;NlM=C9Cx-k3FS`Lu3dZVX}!%=ykff(H+!?YYlE}h^`R?k zVb;{*6%)J8zImG~Y3_E{Hh0u<*GVTQA!VC;#e(EDPC>sb!#?zUstH>cHv544 zM62W)&-&KYu7ne=6CLMncCGhZS>r7z2}}9LeZ19y*89)j<4n9{>e%wIiL*a+9&w*= z$BrHQMOZZkxYca9+X~SR?`_l6xV)`~_iOk(4WH6*FAc|DqVPv*c;TfAzC^?QFyIGU zk*VRi*DCmC4fjY_@Dm!|y;#B38m_ob!5?XO>M{i%*6`fx75v{CPP#+E-gYWIm1`8- zQ^T?MDL7HXZ5~kYbPZ=dqTmG@PIy_tD>U5i4F#{&aAl2xpVx4Y-3s2T;haAx_+J{H z{kDQ5+bj8s|ES=>8czO`g0IqWW|M*oG`#jl1>dRRe*aeRBO0#I@HP#9<8q|_-_vlM zTfuc2j_s!4Ga6p6;a*WnzPYg~JW<2d8lI@(9=%ogWg2eNU%}UEc=aF!7i+j+h=NyX zxIx2@YIx!WDtxnsw~bJ6wT3fCD)=J}uhVe7hAYOY@PBA{zlJ^0Dm@1-QsKQcTym*` zM`(E4Oa)KYuxFNn=V>@a!+9F6)9@V{UN~FfKc(TRmnryl4S#dFfcU#LCNp=wF*B^!%t{9PQzp8sqkbCKceBO8m_ofgxk$mO z8ZORM@D&=~t>Ih^Ke1Sa-=X2C>lFNmhNo)yH4U%9yddS@ui+kr3O=skD~lBD?xf^h zqv7*3e5zQ5Cu#WjtqPv5;fc2?_*xBTl_>Z|4d1+4!K*a9U&Bvnc>Ucf{6!6O4wLf! z-oR@W{I-TCYWNckC;Uc*H)y!LOu?NxtMshEz?S@mX!yg83ZA6lCpIZ~zJ}{GT%_T_ zn^pMT8ZOrG1`Ti5@Jf9)NmDH4;VX&ey4`FYxq44C%9DjUkr@FGUfTFfiY|* z%!z#ROV)5S;7t1KzllNce87uAdB^T67ZHU#F2K*W3Cg_{;r%R_;r;L|#KS+rGx4*K z0R9M*zD=rfzoqF7%y7~tbaB-y?|L zhDpCd)Bj7;8`#h%1ku|t=_@q7tDDlFfen3F5WNkPew(I`*7OGcrS#i1eQ!-~U_*aO zF9Z>ui60x5@$3HQC=(;L{(FASo$VbUMg^s6+zfermwI`GHWO#Ij|=}musy{0!X z@mYRS=)fOeq_<(xoAJT(n%=;~Cq1fL`NtROZJ6{?vlQ=Ln%=;~C;dV^{4?|mC8S`| z7ijten%=;Meust6_-&Z66lwyN9MXFvCfoLkIr&BE1chzI>qyOVacPCO+wJ#=}2DZ^NWFw zBB=b@FzL4Q%KugXnFT^fj8kUeg=c&^H9p+c4?Pc=+F%-oS=Fjt>0sH4{HJO!_@b zRam?3%Ki*YeC9uv&*h(?w_(yhk)!D2G`)chy=^ay--b!wHCNH6YI*}3`nf^z+c4>K zG<~L~H?X0P3!=AS(y!O_%Qd}$4gJk@;EyjUzXg*%Dqn>?r0ESzH0m!C5C06k4U@iD z)4!(lm5yA#rs=LZ(u`zJSct}CcT+|arIF4 zV_-vnDoimT%=m4X^k%-Mi>5cQp)YX<(%UfU_pem)$7^~68+tpx8NUsa{*igr>B|Y=k1*r!cdK&0qv;LIaHhW#9r)vm^fpZT z7n!I$;3MTSagJ=D5l{003 z8BJbVMPhiX!Lz>kOy#M`+x?C5TMd5DJ+Az1l&2=Y-h2PD8hn!(jvu5vHTkx#_~HJu z8a(UA$16`w-rg@!eyfB0XS)3V!u(Z}Z|NOBtHBR`!142xrw)0q{QmhjRr#gLQv-F5HTXWW9lu|BYVw~*y(K2-RQYFhkpEH_ zSM4l!{<3|8{GOmrDau<7p6wrvl&2;?*(<-*;1|B&)_tM!)FB^~KAOK=p2388@GW0- zacRm^lef?NR0*^i{2=8sl&2>DBNk-B4|uD=r_a32uT0Z#8(f?@dykntYnaTMhmWZ692!JT-ZHze4_2gI}inYUQcPmuu(>01e)1 z@NAFVqC7SEhFFjZKj5tf&-TmRb=>i%MtYoU@^}g+@>YXqduOG(j;AI+(c`TK&-T&N zl&2>Df%p8h8vMlLT>X19<*CW{^zyeF{CedtRGylAZ7j%ypH%s0HF&n?rq*@aPmT20 zfA)L>Z#8(f|Bg_entWF*$b=v8R)fze=eGamx-Nfeq(}aCd%;@`ewp(3C{In^mKVI$ z;P)s$S9xmkDN+VZ(BQ2G-=VxK|Fd;n`Kgf}N=hpQQ#ZOj0qaN)!-W{U-@juQM^3>$*`zG?Y8u?FDzL)aUN`LTMd4*@-397K3aaK@|Sb_RUa*1Nh*|(cC7N$sA1rD*vnozvmQ}|AWd?leh2N;H?JF{#FZ>rzT(ImEUUc?4Pw(d1~@w zJl<;X$DiiP|8M1~$v^4wR)b%md>Ogf)iavBeO{$Xpw-|%SH8CL)a3hk`CARX<>{{c zt(B)H|1cJ0!VmJd8vJH?p+a0&<*5-SLFlFsOwi!12ESAJzRFXR-|q2N2l=1j^3P=c zs>vr~K_>hlf2+Y)I?M56l&3~|l)onyWWo=4tHHBB-&EzPksf@O%$Ueq4W9k~9#x*2 z{NR9&@>YXqf5GRJrzUUnL;0-+KT-P|u2P1?!KZpWv0>YVe;Z-%)vL^7ej-{H+Gh{-u{IPffn1m%r8EJ8A#a4CSfGr+K{9 z;HN4-MtN%T_IgG6tp>lcf!qIoQ=XdqLM+IHAMjR#zfnG@5%-Yt)CiMPA|W3OGT{fj z)!=tFa{N-|sgWN1k1}H-Z#DSsjUB&Ad1~@G4i9;&!KZ0|;CGd$CZFu_R)c4M;_b>) zlkep5R)b%u`5#iAn!MfLsS;>4c=lgTY~ZdRYVu>e{H+Gh{?2D8PfdQ1$6F1a{i9nc zPffn9$6F14g8W0;PRdi0pX~8ggD;ol_{)^1CZF%|R)a56{wn3E$>SWv1f44XtOh?r z`4P%flOGt+QQm6sJC&cPJT>`p9&a`HJ<3m2o|=451)x!WtHF0_>Gt1a%2Sii!GcWq z0dF;U_J@B;d1|D`zFH?c5-iAsAMjR#r+q+W<*AV#`_FD~kiUdsLOgid6Vz0m zn!Hp`1rvV2TMeG}2g%A)BR%r3?e!nC8a(Y4x+zahz6lm&!VmJd8a(YAu2P;F>5;#^ z-+;FoJnbQJm8T}(R@Q+D8obruX+Lp;^3>$#0G5gToIpkQKkY51D^E@SMw1@-TaEl_ zpYeq9)a18&yw%`o&#_8*YV!7b1NmDGp7tMGm8T}Z&dc9w@U$2CUU_Qrc6*V()!=Dg zQmvu8eyGV$_42nGJnd1=P@bB62Q0{hALMT}c-pTtRh}B@asJu-z*`NT_AVDGPfdP~ ztOFA?_&E?VL4&7#OdsW`5q~u2KkaD-D^E>65eqWm2l-o#{AquauRJx_lUj9~tr~S~2%2Sj7z~ik3 zKVgu&|8L~>t0r&nw`F}R)eQ~+d<{2$=mxi%5ODz+QZdq?9N|m^2x6F z;rU~AQ2s)9{hp^h?dixDVL>MRAb+dD)86hP<*AV#=kK1%@=qr6R)eQ~-XP_v$)`Cy z`&4iEFU8a(X_ zA5)&1e2JI8)!=E5xJr3y^7&r=R)eSg;wI&($tQUC532+I4tM^1L0&caPG0_2gQtDu zcgjjOHSE=o$#?L0tHINrb+z);nl{#-a1n? z?X8QUE6N7S0|{lFsG9cH_o$}5^#awjw|+@A?X5ScroHv2(3NDxYG1ugHSMi;siwVk znPx71vf5jpLap}I4OP?Lx}E4cvU*O~27VhL45!_R$0cGh*gi2=7aeVH#j%CntnHPp zRkQuGhibMbr>karv6#&SJllJxsb>4@Jk@N^d3cd*o`1P`yb`1W`Ckds@cCq zhFJ*Gv%kzEs#$+2!xjYb?2qt@YW7dVcMQtU{sv!AmviMgpqlpFGOTTop6z>eRkOXW zwQ9B>{!umCBXQqBer%sBRL%A>^y@}E+rQ?iW_!>Q)ofo`shaIQn;5U{VcS%*J?jV6 zY@ezk*FN%Nd&TLh*?!PSHQNKasAm0sU)8kNPghO*`vTRpr@vV>#|OAaHQPHLRL%C^ zMXFh!U80)p1MjNl_yymRZ|RQTkE+=puvR&@z3gAuN;TWNFI3I`V?9)}Ki2@&?4OpY zn*D_*sAm67jBJPf%l@qQt7iYyg{s+Ka|QLA?tby6YW9c!Ts8YgSCV>stpCZ2UHUUs zv;TH$)wJhJQO*AS160%AGfVZ#o80;)s6Ke3)6-ScK60sQ+FNc`P5Za~s%cMtQUzCD z+6OmPO?%^>s%gKFp_=ym6I9c_cDicX8~j5x?N8RJrakdzs%anmvufHKR+ncH_9yL6 zo2aHeYcJKbZyceT_Wswarv3PI)pG{B{kK>(?Mqgw=6D0|siytFF4eSWsUXiXl#li~ zHC5BzzA@wV_+6s9gC4&u)wD01s+#tWe^*WW$u+8JPqVIvm`jbZ*|*uvTEAz zoU6Kt+9O@4y6YsD{%Xe0aQZscw7;6Gn)YljsHT1Go2qH=yhAnZr>j(PVv-s1CdN(bFqEEu*?(|I447 z-uLvEp5E!{L!PdBeC+yadb)w9lRe$h)4e=>wWsquJ>Kc?c-`#j>7IVv(@Q+P+|wnV ze$~@&c={brf9+`*pcXqnEbl>2%ZQZ;pUC{F%v)vtOlCaOw#od3%wNhZ<5?zrCG*!Z z|6As7WR}q_6L!eFQ|4VV@0R&{nfJ)NSLS^(%h;C*Kg#@*%==|NAoD?)f0p@>%)iK- zAkU-YWG*9fS((epTwdl1GFOxt&(q2>SCP4@%*V@IP39A1K2hdGnSakVd?#i2zp@>; zLh6VUfzo5wN5X;R(}%*R0Ed+qg~4OJ5I5|rNo9wXjRtercn&OEDkdCfHX4J$Yon_4 zAhwZ=aD;d9c_0H0hocKSDXWYfMmP+1qYQ`A4L&T=4p;2_Cq&1T7rzM@h<`{hR<8;- zV6P0ouS59fVkBpHFu=gh{&>v{j~UjPfo>>g zs+fVGgTejH#$hnrV36HH$v+%>*QIiU?ISoCu+|2KBlp_CV6r&b_m@k&?SYt0MtJP&-=onA>vE{$;}Ap z;#wmJ#QNVCd3#&4Y3iPM~B$R&<{lXV0e0n##pBaaYJKwyh$z* zadxRmaM;+GogDyic61OPe_?6ONizMN$v5G-FJIq(7}?tpYpAymEy;2>_eeC&DYWHn(}hP;}KOTZj)k0jr} zIXOdQmq#PL;3375D*DOjZs(G{er({ye&`Y6Q+oA^$RoC>AU&h#@?jY*5+Vrc<-7om zMTq#%Ly$N~bn*I?imy>|F&O=%GaqO8j?Wz~Ur0z2EnBdJ9R{CIkqj)!9trk^OP`gO z9}#Cj@(~fZ1PVUag0HiPcyY`x#O`%ccC2%mA}67i2cNzP5pQn<9&W@lkbGIWlPgkC zoEy{$uoN1ck(nunR(#+f&oJ?j;!NnaF#&}mvh%}$jKYkfF_D6ia<$0O_JhT_RlMMg z3|zM{>9dCh8#^3c;Ua~_L&Sd+ODqKlLc#!-XYjF%Z!&S+#-v)_P`j;)-5%#$Q8ybv zLEv_atH>R8t?WcO=G-yC296FjPLvV)KEOcv>~k*&3<$O*b}NQvXA}j&a=Ayh4F$yZ z1-m|^nKleSG4sVKWb{A)#DPFvgKR+fRu_pL$lxjpR}pV_b;B2nlw2;6?t%X!ZnQ)M zPlJo)G>jG&m1E8wAJfUFcA=b^xb@+PhMC!LbK{enVS&^?3>_Zto*hKWr5YO@B_gAW z;X^3!kg3PUL}urT*DV{DC0;7?)eop9$Dx(0JlhmhF5esta~Ve@QMegy9jE}yEsmb@ zE;P87qbZ%o$c&t9@$u!hK^&5fDvsXOq-ou3X@sJf9yym<#tKKE=2?$PPl;!zSeX?P@So?Pnop*Pb~ z;kxnO%?7vpz!Rw#t~H2W=umOCDc)$~uUqz1E_P3RM($=oPJvH1U5|tv=2l-iLW}9A zcTQvd&~U5#2;D3{INUB5Q6P1x;*LwsPq;XfqZB?F^!^v<;0;G^7}2|O5L`MWIN-s- z#sQS$CT~HZ9HJ)>qH>1~FD=D2SD0856y>?ITGNZP32dwvCF!#BqR)DH{~4Li6|?p# zV@R_sajAy$Qq~_ETFl_c(Cp#aMeYPP)w97vak#t@MZ?Up9O$Afxh;i}F2FrXg9mc( zC>Cd=xl*d(IeA0mRirRO+!BgI-Grq6awH0q^5w4}?;5nPk4#F)3FGB?Um%ZX32l~-=uM;0W@G(0ycEh*l5nzm@2IkZ)qX7cPr-Dmtt&7i(De5Lu#^{=S+ zmLnUytfksgY#O zN=(IxJ3|{Ijna7xZ1J!bhwwoL_Daa>Wq%&lTl6*Y(#8* z98V7y+K=8~^?@53%iK{77+eGKAupO#>8l#Ks{jx=69pj{Qq~$H$m=V9VDmK); z8vpKE5VI%#OSL0hLxFZ|K%E(QVoz^3#FVu zzoCZ4y#XG+mD5yUgkGCjs}bAgf5bN1*vQfizK7{P5q6A_&K2@0rSeH#Oh><`TtAUg9bs%2mzYY>@Iy>oT+@%We9hR(QR|jEi~@(x9+&Xf`mO|D zc3u}5Q|B7LCYCG5c4h~yEbbHg=<8->4S-{M&6w^hLF8}mK{G~fG*T6gJuo~+RxXzm zj-_4)EaQJvAB^?HcA<&sU3ILUID!gZ95+6ewbC@I5r3?nINUwmj@1(%t0z8IPb_uY zL5=gVdgA{N^~9#At6I%4x!O3 zq0IWb>XbW}k`g-?etkCB5?ZeEZP4!e6G$`Rbp|ODf(~W8d=` zN&WNIADaKf*==vCbVZA_;q8;(Tzb=%>-(jye)*JLyOw`*Q{r3Go|}DEzw%?JZ=Lk< z$xqE%ac`5PfuCG*+#S>QH+bZWpIXfuF#MvrH+7r2uGdLTr|-FTSl8Rfy?x@Iis{eJ zeeTwlJJ%-WTsL#n@|myi{oBO$qyN0-mQVlGWA>n|z4e!N=+yDA{qOr}QJsbRCOmy$ z(RDkA47hsb{rAlpTljF-uiqK={55;$Rcya{dFGDJpRTT0bM-gvN8kCzvT5I3J$m)p z?6tRdS+?>|4|f>Zy4RL{d9OAoUz|R(Zg$><842GtZ#BQgX_p@Q_TB@hO}u6M;N;Vm zJ#gnWvr`v8T)phFR&Be!*8ivTDpuNj#lg+DF8Sf3cxbi|!b7 zL+!H5N4-|1*ALYi_j&%xJuTN2lnio~>efmW=c5OI&T>YDyK6}ZU0~`CSja2`~S)Z&;nmM@nzb@Omc+22Z z{#q_SW$TO8=GFY^lXLgiAG)Ji@!nxif3>op%Y#3RKI^mMn;t*sij-^LI(60`pU<9K z+^zXzb85F3zx$+j+r4qYlJ-B|*`>nddR+$8oZUC8eYI(C>?&NG)~Mvs&iCe?7D;T< zVcWDy<@5Ufz5CjXM~n8H-t5tlsU36L6fVepzs?OEd(3zsd-g-GocPB^*Iiw9%sWp` zOTF!s&OMW-)OdRNdyBFok^OJ1zP0OLY781Wd{d(n=5L&GUWL9px+h=Or03R+_1k54 zY1^sSyf2>XTj$V#@ikw5_PQ6>H>@_dcmDeQX*+LkSK;HbuP(ZIJ8#)-JbZNu>kBd+pi$ZS~GcpU;1MOY05gUTi#T>zX~g4vqdWab%@g zcfOrcy<^X+>38pH{q-m5nH{Rn?6hI(x3^8XzH{1b@4P=|LF<(H>*{yyQvc;EpIvzR zHSOQO_vi6%o>6w%tZ|cj|G7q5L1b#J`%akk%5BXbn)`dNuV3TetI=ldnxuA5HFW_aLsKmd|JQEt&^`UTk*B$&MDqh^3(Q8rx#^Z zUhqtteIHC&ll4lEJ!P+&QLVw>ubZ)xwoZK=1ocI}s5IuzMnH9O}(@n_@KygjeyaSNvo o{$O13(hm~PNf}V@_0i+bT>Q(Uh2P)2VMepo3l~fodct4+2iR%Af&c&j diff --git a/src_v2/core/lumenarium_core_window.h b/src_v2/core/lumenarium_core_window.h index 704366f..ae2e6a7 100644 --- a/src_v2/core/lumenarium_core_window.h +++ b/src_v2/core/lumenarium_core_window.h @@ -168,6 +168,15 @@ input_state_create(Allocator* a) Input_State* result = allocator_alloc_struct(a, Input_State); result->frame_hot = result->frames + 0; result->frame_cold = result->frames + 1; + + // Clear the new hot input frame + Key_Flags* hot_key_flags = result->frame_hot->key_flags; + Key_Flags* cold_key_flags = result->frame_cold->key_flags; + for (u32 i = 0; i < KeyCode_Count; i++) + { + hot_key_flags[i] = 0; + cold_key_flags[i] = 0; + } return result; } diff --git a/src_v2/editor/graphics/lumenarium_editor_graphics.h b/src_v2/editor/graphics/lumenarium_editor_graphics.h index 9e50c91..b405dc4 100644 --- a/src_v2/editor/graphics/lumenarium_editor_graphics.h +++ b/src_v2/editor/graphics/lumenarium_editor_graphics.h @@ -69,7 +69,7 @@ void geometry_buffer_update(Geometry_Buffer* buffer, r32* verts, u32 verts_offse // Shaders void geometry_bind(Geometry_Buffer geo); void shader_bind(Shader shader); -void geometry_drawi(Geometry_Buffer geo, u32 indices); +void geometry_drawi(Geometry_Buffer geo, u32 indices, u32 offset); void geometry_draw(Geometry_Buffer geo); void vertex_attrib_pointer(Geometry_Buffer geo, Shader shader, u32 count, u32 attr_index, u32 stride, u32 offset); void set_uniform(Shader shader, u32 index, m44 u); @@ -282,14 +282,14 @@ shader_bind(Shader shader) } void -geometry_drawi(Geometry_Buffer geo, u32 indices){ - glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_INT, 0); +geometry_drawi(Geometry_Buffer geo, u32 indices, u32 offset){ + glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_INT, (void*)(offset * sizeof(u32))); os_gl_no_error(); } void geometry_draw(Geometry_Buffer geo){ - geometry_drawi(geo, geo.indices_len); + geometry_drawi(geo, geo.indices_len, 0); } void vertex_attrib_pointer(Geometry_Buffer geo, Shader shader, GLuint count, GLuint attr_index, GLuint stride, GLuint offset){ diff --git a/src_v2/editor/lumenarium_editor.c b/src_v2/editor/lumenarium_editor.c index 610f2cc..5e95092 100644 --- a/src_v2/editor/lumenarium_editor.c +++ b/src_v2/editor/lumenarium_editor.c @@ -266,11 +266,17 @@ ed_sculpture_updated(App_State* state, r32 scale, r32 led_size) } ed->sculpture_geo = geometry_buffer_create( geo.buffer_vertex.values, - geo.buffer_vertex.len, + geo.buffer_vertex.len * geo.buffer_vertex.stride, geo.buffer_index.values, geo.buffer_index.len ); - + + for (u32 i = 0; i < 6; i++) + { + u32 index = geo.buffer_index.values[1008 + i]; + r32* values = geo.buffer_vertex.values + (index * 5); + printf("%d -> %f %f %f, %f %f\n", index, values[0], values[1], values[2], values[3], values[4]); + } vertex_attrib_pointer(ed->sculpture_geo, ed->sculpture_shd, 3, ed->sculpture_shd.attrs[0], 5, 0); vertex_attrib_pointer(ed->sculpture_geo, ed->sculpture_shd, 2, ed->sculpture_shd.attrs[1], 5, 3); @@ -278,7 +284,7 @@ ed_sculpture_updated(App_State* state, r32 scale, r32 led_size) // TODO(PS): map leds to pixels if (ed->sculpture_tex.w != 0) - { + { invalid_code_path; // TODO(PS): destroy the old texture } diff --git a/src_v2/editor/lumenarium_editor_sculpture_visualizer.c b/src_v2/editor/lumenarium_editor_sculpture_visualizer.c index b70efda..e1bded3 100644 --- a/src_v2/editor/lumenarium_editor_sculpture_visualizer.c +++ b/src_v2/editor/lumenarium_editor_sculpture_visualizer.c @@ -14,12 +14,32 @@ ed_sculpture_visualizer_init(App_State* state) } r32 cam_theta = 0; +u32 offset = 0; internal void ed_sculpture_visualizer(App_State* state) { Editor* ed = state->editor; + Input_State* in = state->input_state; + u32 delta = 1; + if (input_key_is_down(in, KeyCode_LeftShift) || input_key_is_down(in, KeyCode_RightShift)) + { + delta = 100; + } + if (input_key_went_down(in, KeyCode_UpArrow)) + { + offset += delta; + printf("%d\n", offset); + } + if (input_key_went_down(in, KeyCode_DownArrow)) + { + offset -= delta; + printf("%d\n", offset); + } + offset = clamp(0, offset, ed->sculpture_geo.indices_len); + + // Set the viewport to the current layout's region so that the sculpture // never overlaps any other ui elements UI_Layout l = *ed->ui.layout; @@ -52,7 +72,7 @@ ed_sculpture_visualizer(App_State* state) u32 j = 2868; u32 k = ed->sculpture_geo.indices_len; u32 h = (i * 6) + 3; - geometry_drawi(ed->sculpture_geo, k); + geometry_drawi(ed->sculpture_geo, k, 0); // reset the viewport for all other rendering v2 wds = HMM_MultiplyVec2(ed->window_dim, ed->content_scale); diff --git a/src_v2/editor/lumenarium_editor_ui.c b/src_v2/editor/lumenarium_editor_ui.c index 894c11e..e79c162 100644 --- a/src_v2/editor/lumenarium_editor_ui.c +++ b/src_v2/editor/lumenarium_editor_ui.c @@ -29,7 +29,7 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) result.per_frame_buffer = geometry_buffer_create( result.geo.buffer_vertex.values, - result.geo.buffer_vertex.cap, + result.geo.buffer_vertex.cap * result.geo.buffer_vertex.stride, result.geo.buffer_index.values, result.geo.buffer_index.cap ); @@ -225,7 +225,7 @@ ui_draw(UI* ui) set_uniform(ui->shader, 0, ui->proj); texture_bind(ui->atlas_texture); geometry_bind(ui->per_frame_buffer); - geometry_drawi(ui->per_frame_buffer, ui->geo.buffer_index.len); + geometry_drawi(ui->per_frame_buffer, ui->geo.buffer_index.len, 0); } //////////////////////////////////////////// diff --git a/src_v2/lumenarium_first.c b/src_v2/lumenarium_first.c index 8f97144..b2b16f8 100644 --- a/src_v2/lumenarium_first.c +++ b/src_v2/lumenarium_first.c @@ -9,7 +9,7 @@ lumenarium_init() permanent = bump_allocator_create_reserve(GB(2)); global_scratch_ = bump_allocator_create_reserve(GB(4)); - run_tests(); + //run_tests(); scratch_get(scratch); App_Init_Desc desc = incenter_get_init_desc(); // TODO(PS): make sure the values make sense in desc @@ -24,10 +24,28 @@ lumenarium_init() String exe_file_path = os_get_exe_path(scratch.a); 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; - String run_tree_path = string_get_prefix(exe_file_path, run_tree_end); - String run_tree_path_nullterm = string_copy(run_tree_path, scratch.a); - os_pwd_set(run_tree_path_nullterm); + if (run_tree_start >= exe_file_path.cap) + { + u64 exe_path_start = string_find_substring(exe_file_path, lit_str("lumenarium"), 0, StringMatch_FindLast); + if (exe_path_start < exe_file_path.cap) + { + String run_tree_path = string_get_prefix(exe_file_path, exe_path_start); + String run_tree_path_nullterm = string_copy(run_tree_path, scratch.a); + os_pwd_set(run_tree_path_nullterm); + } + else + { + printf("Unable to set working directory\n"); + } + } + else + { + u64 run_tree_end = run_tree_start + lit_str("run_tree").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.a); + os_pwd_set(run_tree_path_nullterm); + } + en_init(state, desc); if (has_flag(state->flags, AppState_RunEditor)) ed_init(state); diff --git a/src_v2/lumenarium_geometry.h b/src_v2/lumenarium_geometry.h index a2d6428..533ebba 100644 --- a/src_v2/lumenarium_geometry.h +++ b/src_v2/lumenarium_geometry.h @@ -288,9 +288,11 @@ geo_quad_buffer_builder_push_vt(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, u32 i3 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p3, t3, (v4){}); geo_index_buffer_builder_push_quad(&b->buffer_index, i0, i1, i2, i3); + // u32 base = 2869; + // geo_index_buffer_builder_push_quad(&b->buffer_index, base + 0, base + 1, base + 2, base + 3); } -internal void +internal void geo_quad_buffer_builder_push_v(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3) { u32 i0 = geo_vertex_buffer_builder_push_vtc(&b->buffer_vertex, p0, (v2){}, (v4){}); diff --git a/src_v2/platform/osx/lumenarium_first_osx.c b/src_v2/platform/osx/lumenarium_first_osx.c index ccbc76b..65fb37e 100644 --- a/src_v2/platform/osx/lumenarium_first_osx.c +++ b/src_v2/platform/osx/lumenarium_first_osx.c @@ -75,14 +75,134 @@ glfw_error_callback(int error, const char* description) global u8* app_state_data = 0; global Key_Code glfw_key_translation_table[] = { - + [GLFW_KEY_SPACE] = KeyCode_Space, + [GLFW_KEY_APOSTROPHE] = KeyCode_SingleQuote, + [GLFW_KEY_COMMA] = KeyCode_Comma, + [GLFW_KEY_MINUS] = KeyCode_Minus, + [GLFW_KEY_PERIOD] = KeyCode_Period, + [GLFW_KEY_SLASH] = KeyCode_ForwardSlash, + [GLFW_KEY_0] = KeyCode_0, + [GLFW_KEY_1] = KeyCode_1, + [GLFW_KEY_2] = KeyCode_2, + [GLFW_KEY_3] = KeyCode_3, + [GLFW_KEY_4] = KeyCode_4, + [GLFW_KEY_5] = KeyCode_5, + [GLFW_KEY_6] = KeyCode_6, + [GLFW_KEY_7] = KeyCode_7, + [GLFW_KEY_8] = KeyCode_8, + [GLFW_KEY_9] = KeyCode_9, + [GLFW_KEY_SEMICOLON] = KeyCode_SemiColon, + [GLFW_KEY_EQUAL] = KeyCode_Equals, + [GLFW_KEY_A] = KeyCode_A, + [GLFW_KEY_B] = KeyCode_B, + [GLFW_KEY_C] = KeyCode_C, + [GLFW_KEY_D] = KeyCode_D, + [GLFW_KEY_E] = KeyCode_E, + [GLFW_KEY_F] = KeyCode_F, + [GLFW_KEY_G] = KeyCode_G, + [GLFW_KEY_H] = KeyCode_H, + [GLFW_KEY_I] = KeyCode_I, + [GLFW_KEY_J] = KeyCode_J, + [GLFW_KEY_K] = KeyCode_K, + [GLFW_KEY_L] = KeyCode_L, + [GLFW_KEY_M] = KeyCode_M, + [GLFW_KEY_N] = KeyCode_N, + [GLFW_KEY_O] = KeyCode_O, + [GLFW_KEY_P] = KeyCode_P, + [GLFW_KEY_Q] = KeyCode_Q, + [GLFW_KEY_R] = KeyCode_R, + [GLFW_KEY_S] = KeyCode_S, + [GLFW_KEY_T] = KeyCode_T, + [GLFW_KEY_U] = KeyCode_U, + [GLFW_KEY_V] = KeyCode_V, + [GLFW_KEY_W] = KeyCode_W, + [GLFW_KEY_X] = KeyCode_X, + [GLFW_KEY_Y] = KeyCode_Y, + [GLFW_KEY_Z] = KeyCode_Z, + [GLFW_KEY_LEFT_BRACKET] = KeyCode_LeftBracket, + [GLFW_KEY_BACKSLASH] = KeyCode_Backslash, + [GLFW_KEY_RIGHT_BRACKET] = KeyCode_RightBrace, + [GLFW_KEY_GRAVE_ACCENT] = KeyCode_Invalid, + [GLFW_KEY_WORLD_1] = KeyCode_Invalid, + [GLFW_KEY_WORLD_2] = KeyCode_Invalid, + [GLFW_KEY_ESCAPE] = KeyCode_Esc, + [GLFW_KEY_ENTER] = KeyCode_Enter, + [GLFW_KEY_TAB] = KeyCode_Tab, + [GLFW_KEY_BACKSPACE] = KeyCode_Backspace, + [GLFW_KEY_INSERT] = KeyCode_Invalid, + [GLFW_KEY_DELETE] = KeyCode_Delete, + [GLFW_KEY_RIGHT] = KeyCode_RightArrow, + [GLFW_KEY_LEFT] = KeyCode_LeftArrow, + [GLFW_KEY_DOWN] = KeyCode_DownArrow, + [GLFW_KEY_UP] = KeyCode_UpArrow, + [GLFW_KEY_PAGE_UP] = KeyCode_PageUp, + [GLFW_KEY_PAGE_DOWN] = KeyCode_PageDown, + [GLFW_KEY_HOME] = KeyCode_Invalid, + [GLFW_KEY_END] = KeyCode_Invalid, + [GLFW_KEY_CAPS_LOCK] = KeyCode_CapsLock, + [GLFW_KEY_SCROLL_LOCK] = KeyCode_Invalid, + [GLFW_KEY_NUM_LOCK] = KeyCode_Invalid, + [GLFW_KEY_PRINT_SCREEN] = KeyCode_Invalid, + [GLFW_KEY_PAUSE] = KeyCode_Invalid, + [GLFW_KEY_F1] = KeyCode_F1, + [GLFW_KEY_F2] = KeyCode_F2, + [GLFW_KEY_F3] = KeyCode_F3, + [GLFW_KEY_F4] = KeyCode_F4, + [GLFW_KEY_F5] = KeyCode_F5, + [GLFW_KEY_F6] = KeyCode_F6, + [GLFW_KEY_F7] = KeyCode_F7, + [GLFW_KEY_F8] = KeyCode_F8, + [GLFW_KEY_F9] = KeyCode_F9, + [GLFW_KEY_F10] = KeyCode_Invalid, + [GLFW_KEY_F11] = KeyCode_Invalid, + [GLFW_KEY_F12] = KeyCode_Invalid, + [GLFW_KEY_F13] = KeyCode_Invalid, + [GLFW_KEY_F14] = KeyCode_Invalid, + [GLFW_KEY_F15] = KeyCode_Invalid, + [GLFW_KEY_F16] = KeyCode_Invalid, + [GLFW_KEY_F17] = KeyCode_Invalid, + [GLFW_KEY_F18] = KeyCode_Invalid, + [GLFW_KEY_F19] = KeyCode_Invalid, + [GLFW_KEY_F20] = KeyCode_Invalid, + [GLFW_KEY_F21] = KeyCode_Invalid, + [GLFW_KEY_F22] = KeyCode_Invalid, + [GLFW_KEY_F23] = KeyCode_Invalid, + [GLFW_KEY_F24] = KeyCode_Invalid, + [GLFW_KEY_F25] = KeyCode_Invalid, + [GLFW_KEY_KP_0] = KeyCode_Invalid, + [GLFW_KEY_KP_1] = KeyCode_Invalid, + [GLFW_KEY_KP_2] = KeyCode_Invalid, + [GLFW_KEY_KP_3] = KeyCode_Invalid, + [GLFW_KEY_KP_4] = KeyCode_Invalid, + [GLFW_KEY_KP_5] = KeyCode_Invalid, + [GLFW_KEY_KP_6] = KeyCode_Invalid, + [GLFW_KEY_KP_7] = KeyCode_Invalid, + [GLFW_KEY_KP_8] = KeyCode_Invalid, + [GLFW_KEY_KP_9] = KeyCode_Invalid, + [GLFW_KEY_KP_DECIMAL] = KeyCode_Invalid, + [GLFW_KEY_KP_DIVIDE] = KeyCode_Invalid, + [GLFW_KEY_KP_MULTIPLY] = KeyCode_Invalid, + [GLFW_KEY_KP_SUBTRACT] = KeyCode_Invalid, + [GLFW_KEY_KP_ADD] = KeyCode_Invalid, + [GLFW_KEY_KP_ENTER] = KeyCode_Invalid, + [GLFW_KEY_KP_EQUAL] = KeyCode_Invalid, + [GLFW_KEY_LEFT_SHIFT] = KeyCode_LeftShift, + [GLFW_KEY_LEFT_CONTROL] = KeyCode_LeftCtrl, + [GLFW_KEY_LEFT_ALT] = KeyCode_Alt, + [GLFW_KEY_LEFT_SUPER] = KeyCode_Invalid, + [GLFW_KEY_RIGHT_SHIFT] = KeyCode_RightShift, + [GLFW_KEY_RIGHT_CONTROL] = KeyCode_RightCtrl, + [GLFW_KEY_RIGHT_ALT] = KeyCode_Alt, + [GLFW_KEY_RIGHT_SUPER] = KeyCode_Invalid, + [GLFW_KEY_MENU] = KeyCode_Invalid, + }; Key_Code osx_translate_key(int glfw_key) { // TODO: turn this into an actual key_code - return (Key_Code)glfw_key; + return glfw_key_translation_table[glfw_key]; } Key_Code @@ -204,6 +324,8 @@ int main (int arg_count, char** args) r64 target_seconds_per_frame = 1.0 / 30.0f; Ticks ticks_start = os_get_ticks(); while(!glfwWindowShouldClose(window) && running && has_flag(state->flags, AppState_IsRunning)) { + lumenarium_frame_prepare(state); + glfwPollEvents(); if (has_flag(state->flags, AppState_RunEditor)) { @@ -212,13 +334,11 @@ int main (int arg_count, char** args) state->editor->window_dim = (v2){ (r32)w, (r32)h }; } - lumenarium_frame_prepare(state); lumenarium_frame(state); lumenarium_env_validate(); glfwSwapBuffers(window); - glfwPollEvents(); - + Ticks ticks_end = os_get_ticks(); r64 seconds_elapsed = get_seconds_elapsed(ticks_start, ticks_end, os_get_ticks_per_second()); while (seconds_elapsed < target_seconds_per_frame) diff --git a/src_v2/user_space/user_space_incenter.cpp b/src_v2/user_space/user_space_incenter.cpp index 35fd847..876fbe5 100644 --- a/src_v2/user_space/user_space_incenter.cpp +++ b/src_v2/user_space/user_space_incenter.cpp @@ -54,7 +54,7 @@ incenter_init(App_State* state) } r32 rad = 0.05f; - ed_sculpture_updated(state, 10, rad); + ed_sculpture_updated(state, 5, rad); scratch_release(scratch); } From d7520367e237a61871c2b166fe16fb5b29ff47e4 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sat, 16 Apr 2022 06:24:51 -0700 Subject: [PATCH 128/151] building for different architectures on osx --- build/build.sh | 2 +- build/build_.sh | 11 +++++++++++ run_tree/osx/arm64/prod/lumenarium | Bin 0 -> 330407 bytes run_tree/osx/arm64/prod/lumenarium_arm64 | Bin 0 -> 330407 bytes run_tree/osx/intel/debug/lumenarium | Bin 839191 -> 998824 bytes run_tree/osx/intel/prod/lumenarium | Bin 0 -> 345672 bytes 6 files changed, 12 insertions(+), 1 deletion(-) create mode 100755 run_tree/osx/arm64/prod/lumenarium create mode 100755 run_tree/osx/arm64/prod/lumenarium_arm64 create mode 100755 run_tree/osx/intel/prod/lumenarium diff --git a/build/build.sh b/build/build.sh index 5d60d30..7a321c5 100755 --- a/build/build.sh +++ b/build/build.sh @@ -1,3 +1,3 @@ SCRIPT_REL_DIR=$(dirname "${BASH_SOURCE[0]}") -$SCRIPT_REL_DIR/build_.sh debug osx intel +$SCRIPT_REL_DIR/build_.sh prod osx arm64 # $SCRIPT_REL_DIR/build_.sh debug wasm intel \ No newline at end of file diff --git a/build/build_.sh b/build/build_.sh index b42d9a6..63c6bf2 100755 --- a/build/build_.sh +++ b/build/build_.sh @@ -210,6 +210,17 @@ then LinkerFlags=$LinkerFlags_osx LinkerLibs=$LinkerLibs_osx + if [ "${ARCH}" == "arm64" ] + then + CompilerFlags="${CompilerFlags} -arch arm64" + elif [ "${ARCH}" == "intel" ] + then + CompilerFlags="${CompilerFlags} -arch x86_64" + else + echo "ERROR: Unrecognized Arch: ${ARCH}" + exit 0 + fi + elif [ "${PLATFORM}" == "wasm" ] then Compiler=$Compiler_wasm diff --git a/run_tree/osx/arm64/prod/lumenarium b/run_tree/osx/arm64/prod/lumenarium new file mode 100755 index 0000000000000000000000000000000000000000..e3c8be856d99139980f323fbf15db2d9642d5e5f GIT binary patch literal 330407 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 literal 0 HcmV?d00001 diff --git a/run_tree/osx/arm64/prod/lumenarium_arm64 b/run_tree/osx/arm64/prod/lumenarium_arm64 new file mode 100755 index 0000000000000000000000000000000000000000..e3c8be856d99139980f323fbf15db2d9642d5e5f GIT binary patch literal 330407 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 literal 0 HcmV?d00001 diff --git a/run_tree/osx/intel/debug/lumenarium b/run_tree/osx/intel/debug/lumenarium index 35e9ebbf711b02b5430c4925e428d293b7baa97c..9cce34ee3751ab4677cec97a9b18cc2e06a76312 100755 GIT binary patch literal 998824 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%0D7b2a4-Eyan66sxIV*P@!3>n05h^E!sE7-K}nX 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 z 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 Y#@V%5ur*V#Z6;AT8!-xbaISnx?(w*s%uf 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 k7ax= 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

CRSJwNi(e?cFL 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 z%0D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%0D3YJ?}llm6)Yx#t@)I0tJqVGWeQH+7@=gBsm)KO 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 JEgWt(`+wB(q{jdN diff --git a/run_tree/osx/intel/prod/lumenarium b/run_tree/osx/intel/prod/lumenarium new file mode 100755 index 0000000000000000000000000000000000000000..9ed88e21c63ce937542780691af620fe8d62ca08 GIT binary patch literal 345672 zcmd?Sdwf(ymcZZLX&`|>bu=hCFh&J8L5zgO86r57#%}1=j^G$183U-1jLwQO&<&_O z0-XTYwv~A}!_JN~?Cv=0&gu@k>i{#Ok`9m;7!pLD3IS2l29<|~mnOgOse3yw7DwhZpfXCK#D zv!=Qyd%y4f-V~kOGBT(cLu1)i`N7b`^E;Dz!|VUPL&G~J1ma)f*?h9u zk}Y_BtppR^tVd@*K6my!(t5)?*=)jl&MF|!CZ-mEOYkds@`?Ff4ekxE?6?VUs>>`d z&pp~}flXXy%_^B6ocGuhKd=&d!)rWi!u!W&GlM*Lht~sFvdo(Gz zK6(7H`N7`s3R6sYnN~4*?gh`17&C(fZ|=M&gHK)#UYP}Nhy_ocd%^30pat)-IS>6{ zwy;WX{MK6VEVi@B^Z&*tk5Zutr(0Ed7N3~hVzcqA|K*t%!BPJDn~#)#{GnO%pPKXV zlaJ4u8=Uu7;U)N2;B{{&V})xqf8en>D&Z~R7kP5efun}y`LJG^e7^sNPN{_Mx+ z%`QbP_lEaikqOUb@rgWlhlki*u3t5u2*lp-=FT$VowE>;=kD-Gz8t*yA=LZj;63`F zg`Wjap8p1Tk1cpeMvF_^yU2o*?rK(dcoy27FGWwyne*fmiG<$m zZ9Z?pYySI04pSZyPrc!ZJ~r*0fd8Jmy&k`%jcs{-ED5tpl$luhs!d3e+8x#nHe1DFnVJ~Q!mV@S*3&jd+nd#b)Sd8fuPl6Tz&1-)A)aPqF{iGdi`c)^~ zaoly$;W|_oa1(t(znCP+o|sMlJod0Ib_qUDrn?zJZuvU;x#!OPD+t|rtNG4q=cC>6 zc`OfA|MRbdd*_q$W>0!D^aKim-ne{&d*-=JJ&vToNJZZNR?p;l56yk_v6A_Jy_Q4{ zlT4aUJ=$XrA+TTDZ-VQ6v*#^%tYr4r)RV~3t)2(D@9alSw^8=5;k&P=*`2p`y$7!F zbx%GyMoLdU5eq!q-JMHpwwuVM#np9XLRWcS zZdNl^zW1|xy8C{2!F_?q50X8RWn-sA(=!zYl6IFPcEqLX!m$gvr_}Sa_1kK<+Td~Y zjZf#vW5kvpY42b}37=0@6&{k@k-Jh=T}7RG{#!idRJOXh@H3inV@tnf)Mr&SU9eaA zN-p?f#~r@t&}Ht(;%i-MXYSdB@3|wMYtvP2?nxD{Mp=fd>_%g5Y*~FyW&A^)m$!FN zoqAzE_g)a}>yI57?N@c~==4E%stt=LqyjT}vf5BQp}#x!K{9Ec2?O{oo-i={LAx*I zV!(b>ZA`y5cGRWny*ZV+r`>AGzEXF}7H>3oU25=qH2ZtWzSz;xK2@(aR=#2nc|69! z)*lEt`f74Oi&aI`uXAcR_WIn@YNeO7tOk1<>7FtDW6dsA8_yxlW1MWg)z|c~$2c3X zA2%Aq&FyOAgmJOsG&$#B?pc+;#bZC|jy}-gHmd26i}uiBPu96UpLf_PTNggk9F_zG&(<0#T>`?r5q*ZCsq}fEcu6XK6); zDu14=e*2eMd+cq#l7@i&NWgwRV2^pgHreF{T{rkHA(P9E(tK6dz{sS~W$r6wm{{sk7;nAL7_TBVdIS7_%ggKU{mG) zN)a`^Qe{=xq|I3s8Ro^s#aB}?nhQB>HTpHBZDeT%6E2PBrIHIdPjZo*zUYerDPSBo z4z>L1PP?t5G8poeXh=SzhW>H-vTFF>^5PB$$n^CY6)gfH`l3KG%ROS2yTcQCP2jeE z$DLa|JbmdcP;>aW@Ok^P8~qW_@MM2U!SEqI5v(&6b;0aN>VHQH{@V$)>vNkz6a6*X zg7oHm;xSr$%ePA7h0rrBQg29|hy2X5$Bv{#Q=gJshuTmuFDZ6BC7M1xw<-7*Mcqk( z;TaaBMH>UgAIH%+qYfEPrX$BD`(qy=jSZ#ViZ=E9T#S-Q0Gi)_B2qYifY0!*pQJWE zGM{Y6$hPC|s59sbSK50L;<@b(o6m@myAkcG%JZq>uBdZ9Ir|5UQvv%apAmhKL|=GI zf1mME9=CpkK9ICzeZYQ>Y>t32d!5HP>O*^=B&rdIN<_mCxBDWYMxVXG7p_kA8%-z= zr$4KS|2Z8w2Xl6{4(-7&WmO%khknNwt{LRdJsYQvDWEy7%e%CQ)leFR4ZR8ITQZcl-o6v)i$}N7 zJWxIrybka~N4ITp0)0zHH|RS}==5k#r8j4nH>WPA&S&JW(ZB0!DyBkHVP7v5$%{(; zTU+BTlhm9JUu4F*xF{5_@sY=M1n}avU|D984^mEx%-!FA|k5g;P@Rb)!byX!mf;?xr(y z>hd<&J;stUI(U7+@UDqYwHrHg_Zf{U+AO0}L^Fmx7AV;tF!uZC-ayuV{`(@+8a=RL z|5UiNzuFk+@9?P2AMqD+=f*;dD=GtqMqZU?AQ#~!WhjHu+==3Rm8-wxeQAux2$cmQ zp<3FpeqwZNPSdA>NPcA?GHuPB(Fy&VKJ%HC`l!*YwA#}@HL27X+rQLU+}}Z~gZ-VJ zNT`gvK>xn+X@GXO)!0Li{)szk3r_Wyyel<%87!y>us1b*7KqHKyfZp9sj1b6uG7@q zMQOy8U~h*b-*D&q|(4;tb#4lusaBM0Pv zWCjB94ziRgZG8?bs*{p@ktK~_eRZi3xEi4OwQ}R+rZOHThcuXi>)qkwc5iuys`vuk z8&{f6Z};kOVcOI&{%}mg|0hbjtL;=Utr7gjj)}Z!^1xz2ehKv~9z#9TDvdqyH5wO5 z?!u+C#&b1{0z1-D5oQp(s;>}C^G3WCL=xD+fS2H6lB-w6LOQ3%*wdN?YSecrYE(N$ zs%=Za_8B`(-M!P7wNtpd)ClHkJUVG|-eSKDBW@nszYWH)hpzG3KXNyZQZCDB`LI(Z|>sF3)tNff>e=H!*Psq+5J+#-Jz+Cd3AW6ApX-6UZ?w08uj zYrz%`8viAU{*rB?P>}|)Sou$NK6^dtF%?)Q6jlB$a#lM!n-9kZ;n*5ae`)x^__qa+ zamXL8?w?aBpwVvw-NyT{Z-y%Wd)_ka$yi6PVVZ4NYWGJcIo0$!GB`qg7*$)Rs0}8! z4^?kfl5j?itaPilCh@Y4`&tV0Z5@fux~C6TK&&}ArFLy+wvChemSVD!kjR^it+2om z`^ZHN$tugcUS;L434MpBywFp^maA()#T?E>G5L+^Gs@Mco`F_n)>2>{uqac;=q=+( zd@u}*0xpfklGWxqvmz=h8u(L+h{o&zOFwuioWD+GX}uHQE%o&Vpf>s(Qtv$g0T>nQ z%$L-{i~M!e0?jJH$impFrFHRT;dTN!2>td`le05r7b#6r?;gg}ZuOE0LiTFmK9x12 zj7*IxORGUMzeXn+vZ)3P5ua(wv}|bW=wMua-X_|_*sn7GD8y5l5Vxq>qcTHfZk5?^ z#;uAP&ZxrLGahwsTxGr~?NOOEWU3+4l67R-DVcUAGQ|QB%|FJL@VS)vDyL3m&R(xF z=d6kU2uG1|Abhl+uVUAqU^JUgd74DkW;Fq7k4IJSv2k;lo5ON5d%agxALB0TH4zXV zK7k{v+vKN1GUTt}u1+#k$WQ|+wQf~iYx75^K8ngn%%{}Gr`MLMC-#=At>62dU4CD8 z$Zu5=Z+^*}<2*h5ioAJvgS>foYf`BicjGQzH}d*3l74tdUjOifLp^XnZZ7B?@2UrO zQR`_{#PlQcf-1Tw<&IHqSE)xmQNz#HiOr0XTs~#k;PPdXdf-H%KWEufKN^67Q3ncAcY2J^2*I)NJ@kv*GQ3 z3vqwlN!(Yvi2LVG;?{N%caK@eF|&@-x(-KQ^!r>a!E>tO?->;n($p!hS|SmKCF}SJ zmGPscg{u|{jC+v{kD>GVjC#{%sP|>n2Mnz*157o-RegO~+bw^E+I-gq^c9LH#_gAA z0X5xHL#VyX!^r*tqn{P;-U+wA zrI!(W;5C|H1*CxP0^14F)Gw;=ZAqyGI&^9^uk-ac68cvW;&F>lEc&$ixIa(PvgUju~eL_ zjU~YYqg`f^KEob=_!QS}q)=~mks^uv_;<+AHAU;3&oi43pUBHM8uE=Azp=w-yd%^3 z;CyrNWO(*6qI%c+YP^jQ%U0uEC)TzvI&ED`@>n~adoVb_h%q%!isWxH2IN;93l2;? zSGtYV;rUqRsfm;~+=j}pXbbk&1-x6`M&C^WFa8lfc$5~VtLG1L8<{>Xe8H|>*vWk$ zGCA2-GGz!;(8#ikie160eIJk^h3;|aPzXh)3*ikaS& z`D>&zPfDdV-b$-8D_h2o5eJLcG;lj>e{^Sm(h=j_#uqU0XzYK$3=c$Ri3vV-KxwQxxis8v52g9-+uhIJo#gI+ zw?mc7T#xLd$gIi<$WpEkSkBimKQyQD%NA0Avi z{r3uhN)p|%4_)pQ=Et#)oVL~h;Tk-q=RLU%%SKI%P8ihG;VU`pvmeA8^#HE1Ay?oy z$bEa>ezkHgWy3Xgqn*41LoFxo_|pp;vHUmAg?&v`0^N zYJJ9%O#x%ZdSlww)*BQ2E^3Ye)-G9aO7Sp0^olA^7Hx(mrU{Ghbl{qzi+A3M(|x>5 zU`oU19HDJIdd53_v1V!nxOW}8WPQHlF$3{_s|NVEpWL!JkX^AM~}d=SRo( z1GW>n-RS9uk5>c9kEZsWNb2ZBCBX-&${zYNxqc+GB-MaGzZ$OY$f;``q}L}H{ES1n zrz8n7mt+U2^khs_3#ux3z zm0qz+mEX-f{4~-zG{QzVxs7^H?jH3*j=aGS&~)Bc!V(GYxS@Gc(=}Z+HSrw0rmx1- zll$osh117qp!Cw}_-XV4uLIYZ-Z6&5tS$EK;fr`I_KM1o#SEst=U}OS1zWj=kKQm# z-J>T(c%Z}QovPx8*g}B1M^A!E;jD^6QfNM{@l5LPhnjv>n{PkKb8>eEd_tfJ=T){2 zHrIEcrQ0~=$*ol{+$%4mW9`i4OBw)QyxI}_z@@h5zO!td?Wldu$?)J}WuXZH7@Abe_jz<$Tb@hR?#0@ zDTBh#awH`T3O`r&1xY@xp4XGYtCx6t60 zQ6EwprkQcrW3;t=lP7)Kjvc0l5m_^cPX-nO*_qI5eN6P(eN+8LgWuRIqtd=(BCIHa zlKY0Uw32oMCLI*7H>$JtEV(Sw{f+Ml11kGCfd3@ z2|6rywB8}*uJx7NNjV0g#cRGIeT~+`Dd*fqKPh&D`8sB>^yXX+-%#EMd{GOc&ao@H zHN&EhkAlOx29XT`n7$ zrSP8NUH6?R{)GM!`%0>cEOQ|YfpJc2u??TGXymQJ)itTDgA@6~H7@*o$mH*NV|2L7 zCXAaB`q*8zc%1#d&{4TMZVerfYtop|E__6RaUx+uxGNZ!;zyC>m(=B_H0fs~@n~{B z)&|rYHz1I*7lKZc)(_SNy`+1_dCW#xt=4AHxXHW;j+-% zb$X*=x6%ViH|z9~I=$9y*rdul&F^MEOrakNlKTKU*_l90Ql#gSbqt>!lg1Z z)_r~1DnAtEm3)P$emrO7W1^$;b*>F})@PiZ5#RVHtgp!Ds2WZ z(xx#9OUvKEk9T*CcOU(V!S$Y8u%7q%qWSLyq9~+j{??W!SfZ0zlD1+ia7ejkP}ntl zcW|gVdGqdL!nT!J8Z+_R`&Xtg7ppXqFt*8+Pi3xYJAyalRNm~}YUZaMMO9>vb!B%O zPHl@1vmi>AE{OdFq9t7S3842b2gu6Rw{^6b&V6ez1z2~$+CLja=dc!f(qhS5^B?Bxve`jSgumsY&N)feg0uQ7>fEh~H8#+r&s z(uBGg4bQtXu*HnG{Y;XLi^ksYh2;5om@$On|3wbtT)6q1KRV@!QuP@Ql_zi8xRFo^ z_2jV4T$xXk%+O)ZKh%aFiOc9C_$A{Z@fmRurxBN0^zx6y@$;!XJ|>#RJtGT$ zQIJC8a*ma%gCCTtcKkU(GLFmn&`S7{<#&83Rp&zg#OrgVYHkhh+i_dX-8YRJ0tnR7 z_k6kMf}?%Ld2z9K_YGiWUyWloHD_1ZIJ^2;mD|3XN1P9_ju0z4Z?1tF-il+Y{9D(_ z>U%S5z^@3=q7y2h0Ihs!HIKMvAJmuVXf7?Y|5M!9`W}ysywN_2Gv5>tuLb1Us3_xd zM$L%9Oi}3vv;u-wgi||BKX>(n`z1!LOjS$fCVD234Q_NBEl5^Dfo2G zXVm69aaN0mht-Qa`yvP2?&CIDO-HClHgk)+<`ZsP)aE2Ly^Xt*B;rU1m67N04ZAdd zaFga7&bngskHQ)`yINSFK*ijV4+grZD78wnDAH|)SVw(Hx4{{~SWIdudR1hhVy$2-D)=S@v#V=$&(gX;^tM66L~(13 z$-jvF5CF&DgHkf`l5}+I)Xx5OtIho^4a)6gi$b-8F92#< zvD72sIBogHr>zT3`~Pd;Pfmz{+ z$;FhtPfnZIGXi4FNwQ)kJefYmEK{A~%RLoBlec3x%&iqQUCT{O)HEqeW1^+gv^*!J zhS9@zUv5q4^YEMNpouLwNKL;bDCSX?77FH|GGUE16Q?$ztmPzIRN{op@xlD*8YbHoH~YBNaBcI9Z57bK8ls!|UtM-NK-TB>GAY;(m7(?{~^2 zwLxX?4>_K}{%Q2Z&Sm(Rip4sH(W%+{-Rc+HBu^mMsWRVLn4~hB{6=L!P2S>DllQ4r zKA@@0?QvO~=-r^Fx|7c%!6cV$AJB)M|9`K2_iGLPH@0v4W!fjJ4QlemOWPMPKJysu zEgus8S_0{IBtrUj8Q` zs;9!HXJ{ChvC~0Q(X@LsX1@!RhHv$#O z5sihLTUY#cDM0uUsDL)M$BSb5^61N~izh7%#2k(mJ9$M~c)+epHSK zig*0eP8B`I-X=0WStJ=>S!J_zIitj8}~$JbVgyy%tg9oAy4BuKva24*O z&B>3zitqo6H6&tw*CiwuS&23G2}Fk{A-{xYOXcN-2nBe4 zKbfB^G-1xDVpcm{L|SJa;ht8^l$Fo4;^C>-H7ff(T98VH5r_U=_-Ez^^74`4h;8PF zX8xiN5RTm>7w?sK1;dkb>cUUEShtAr>?(+gq98e4E}r2-pQW*Y z@v_{;{~I-kb%8V-`@b|@lldxI8h;Ex=B#251S&epPvLP%aO;4U;wo+miE}9G~oG$nZ%+Pg9X=OX3?^ET)4+yf?-sc{sJN-KgD_t82jhN(O}G*)Mt6aPjA+@{M#~J z6?6J$#QIB~xykbTU@CLYV;+?aUx=eiJ^NevonDDR>{+m#7sRgr8rAGp*!43Q#63h>&%zC-LO$)Yj!)%SlHmJ-4@TXe& z+kpaA7QTy{R3_bCe4LC&RVM4^#rSMyYGQpZbNlQj6)q=xd<46;I{o#T?;#0=hd|@e zAu3Y_+G6>^he9>gfd1l)s`|pN3b>!vrtUwfvOiZVUZ;H&@eNg(!X4d4 zt10o0D%hv)2iN!1ie(g{(R&s=#qT={=2LJMQ=L(mzcW~qAN3B)0u?LSqGqZ+oLkcB z;*;yDsnaN!EF>kT@Ok(olvX75KbCQSM5}*LYnH?Z$lm-(fevu zK09kvHX$KIIAngNRy`@tW~hV8ZdBnP^Bn)29~whv6{Uf2`bOcxja?eDbPZK#O{=m$ z>tsPy@n={^J^MPpen?cl5LU$+Hz(JJP*rx5U{xyfjC4ku%KVh;%oF?+exfqZNn*Ph zTs3otB!YPMp#}43+P(#kbN@+*W=puNZZSVEirQD%yVa_CGgWx>UA5{Bc}mEJ#jx#a z)pGG(4ondFkmrpi97!jrj)|3IEBe+)$qF8Cq!o?P$<_iszRg?Q2~t#_iCD)lz0 zRb%C;SG{d&)s+l%DtiY_M9$T!JpFh`g=cdUZzb5AIu0%9OMM3yrc2#wWxdISXVl6{ zv&(hc%caYeYtc}CwCSw7S=M(*u-g8hJoUn%L9KcQ)aWji`GIcx%ldIYdsVoJHwxau zMVg7eW5TnTXf#idK&>o>Krjx{TKta6JjDf>D>`k;FkZr$r(u>eCU>=xs#Z-9y3WwW zva5Cb_UZO*Q7eB#s?b-he9?TbX}p^e2t4LW`>ej*qgK6Trdl*!FHgPN_o-U-OWh#2 zx>?hBtA2c6g+G+`eI`$t7dndxrx6oXbXH}4W!=v_D;x(;wlnO0+{Gm;4+x9tR_|4r zpLexdB7KUxnw_~DR+=}0j+~caz3DDJDmxw;t0HG;Z0jgdi7VD}D+E{}dzZR|%k7$;Qt3XNnU zQ}IO#v;yTb0N*kQgM<599Qs-$&_{`G8MQHfP$W~t^6f7ES#XoZZF@}Q1O~{-h(Y^6 zj{btr!0ynzL4sY1youkb@i}SXGmk|zvw-wQ@frREpHBs!rT=W=Gf!W8;4@wqGnE_y z`IYp>-1C__5N3V>iO!s$z=B%Cmlc zyEAm2u&p&-?Cfl@kaYKXt+_<+>`<$!%_jzU^wg?NJOOmUXp0L7n1d+A=$(Yl5iSr6 z80%$3+YXr*rnin$nI~0dyeq9cAF()5yb}oZCLf9TirG>~SF|B$FRE%Lw9eS#8rNp7w~ zkhS`InfI2wLyljVPjey17iyKE%bpT>7Yz(GMBc5Q2N5k3X2;72M}yuqK# zg8;!iRIZoJX<9LL7o9@bsh8WC!ttVd8%#z|3{oo}5Ta{*F&Z%#iKt z>7v&Z#@oF9n!)1}HQKe(h-MYOho^3gUkuL0GK*jaQZ(YJmo(lZ=IAR%yDA(m-8Fn; z{Hk8%M0udl3L$*a9d5JS9k-d~gz^)l+yo>6fx1mb&B$&d&UI**$^>v?ma3aUkg3do zXwl3srTNJDXJ+#gW7x_Gy7{8r-^GE&MG&U>da6L5VKn2w!m1YK*S-1QM~>0PR`Zt; z)6l*7=s(?jA?Apue`PlR34QI+{K=H-wCXXn>j2Ve)-ytOs)2{?iZ}yQ{lA&Jv-4w+rJRb?-ZyWJ+$|qOcb~4YY!BECdIG?9%Pw+`X?&;%z_-q ztStwiF<20_kI}D+VRD9#;~QMyZsH)y%R?MGRAardIy=jBYQj z5Czst8Kaj>WlY`^U6Yb|k78QSE1?LCTl6U5j_CcuQNN%Cqka+p3Am~3fq(c%hi%>< z2Ikvd=1zq#p49x*g|b%7QQlE@;BtK>rWzCn@RK$mUQdDOy>z)Ot{hd_Tj%v*Oq&0# z&T6@-;!jkhEJO};Td;03VZCjll`3e-k2P^ttA3<0F(=PzRS8es%!I#b0xf7|pIeXv zqX^;tm-#d3%N%-sTI(3IGp&SqH&XAOdBcEUQpH!IM?Pmn2;hvwYd^!m7h_3DC~$LSILm=DM_)Ng_dE-NHXS+H5IwhRK_{O6ac&=my*jst^_$Rk!@S zc(B|mzt$i5p35DXM{wsK&PwE0+v-%Js&R2m;Qm-`x)7Z6kUCSu!cAS@lVuVg6Rwhjs9?LSpJ}s%d z#}D4(lzir@r}#R*cwQd$dRM10?w|gkqhlz4@9{T*Sg`Kxk$8t0@n@ynrSTQ)A72f<0!IEiHdnslmk5U4 zEF98wiF-+#wp*DSy{x@l1ULAi(<)mQ#za57DB=C|k;;5|uH2!#RMkFg+3oa-n)aT$ zABk@a4So7rX6+2#p(~zt5z1HD@@3Q^yYURMc(PuSvWE8!2@T+`#w!>RptD0R)VA6e z&EG+Q&g%r|Y=UeOHdoVVhUq*<7Sog9U2S0jH?aKXnkFiezzQoy=cGi^Y%;Tg?@Lk> zIbnYts?r&r<58o|SpZ+psWf3(VEV(@qfXxJ-C;p7i(4i8OVmzl|C~1b=(69ql1&5a z*rND~JFAWTwyQY`!SFsuxc6FVmH1`Z+&UJ&>?c8|+wkI|+ZA+}q0vbmBRA(DVi9DC zIylR1d=3l4z~@@N4cQ1iS&Kg}=*2q=VAwBI>5DE|O+)%@`0gQK(j+ z6*8Cz3-S=-={9y4@A!=`{02c0)7CT6wcN2;I(w@{h#IeK{E%K=OD}Jtmw!etzY#DP zyV_cgG)ON8r&t}bRyvEO6vd=XRMjm8t5*8=jn1BNNywMPUAbnXq4m}#iPfS5D_gJ0 zspH5D>y?J1a|1eH%z4F_vDRbE{#C?VDe=q)gPFv%1aFjm!cbW-=ril5LaalPX2oCAbH$?m)Q(8Xaltv)9!5)#fbxdn$JslbeWwtyB8ZhB*0#exLy*}#zjy;bt zr_pos9GBk8W3Y$>|HmQe4zFIQ@X{YH0qGLpt{Ln@iM7a@aQLEQaXOofBB9;v?2Y8_ za2xwtb`rkki7WwFN3XYVa?2?kuf(Viq1#!{r?ZA>-p<-@MZuVc1GYpVrrFGTL^6Y> zB(wD?c%6=?L@F(okrKG(nZ6X_ZJ!B;rEw&}*353bX!)Oe&}%Bh@)|Ypx!ZWxPg5iL znVJeW=et;SZ`p}un`oHF_>|)uJjQuxSQ}ob$TU~@f@ARz8r}KM6Y;uQuAyDL{691e zEiz2EEUU3w!_-JIRl2vUTTooL(BjLC55)gL3Nwnbn{a!LYPYe|ZR|$4852aX3pQz% z3?y3VHBMfRJ?U=f+C2vFKYH+F&;1%-7yq|W#vX}V>>&u3uz$Cv#%;`^diEcUv%(lJ z)1>9ytGIl_%;t2j<2@!^T_R?lhs1_^S9r+~TTt&H$FOkq8Hg!>afRv}xy#2259?J! z^!lzUEPg?9jQETpn{Ing#;{iWS)*jC>ta$<7<@qtl+VdDtV(Bk;6pZilGH5r+YRDn zn*NCt`&5d3!7k2E_|N+mdH+Kvr{aC(RZ-{^cHtICYt`ndDGi}%GZ^!3;esf`6M zII?itQ+VX3$OW^`napM^KdO+~EX&L{ z7AEH#KN!s}NVlUv*%x_+qg&W=HY5H&Ja!SQQ+~tk$qV`sgSw#qj4j5(4DJ@T8PkXBvxs`| zKlEg;ID3AKC9e<-|NPyUn}%%VS;GM>F5-N0DzOXK`iyDoHL83N#ivA5e{yCb=6OAHv2SBoEVu(3g&m#uusva>gz8^Ii1=dm%p50n zy(8tUq!1z=VAqVfdGA!#PLq#nh$UL1rVspgF;6;SEFergkVBYwj4*KyI1r9QU(b~D zOkVLs9?F=s`jPsKNYU_#tBdQepBP?T&x-K#(=qr0#%_K<5%XT1YI|?frL& z=!qcg$f>(IpYe$yNRBcnk{N4Pj(V#&qpe=;eTTZFc$Ky`D|6az4(&FZ$l9V9K9G(&^FH_3KNUV*@!w*NTboUH3ulwc2HYqIZ$z<{ zqTV+!)+$?W-=>Z6ie12z&^)XwcBjpPntBV%qdmsZOYDtt!hTjY#2qevU zMULI^jGwkimA?dNk1=hBc>+UpNu!)Bvn60uwInac6%_7BTKFAbbl@to-BsfJ6Z>8| zn!`&f5&3=WL|*SfYpB(-cE}MWY|cuC;W=}uF8&ZV=KM->tJR0eBF7Fsq(H)&II()p}YR?^wS&jCXQJb^NefD$rYBEXSqCdASw1Y#};y;tg67hl- zHiF4t{^R$*|GirML#Ye8oAm>~ppVU{4u1sv;BAf3MM(m_0Pv?dfsHNj>JRVuVjo}c zUQIrk5&LtiLv``pJ>hq?c7O$5S{$6YrVD;w3%)aaysrhn-e`)&9q!eEJ~kq2$r|iq z4<7-M#RD6ozCPUMtqu-sG)`rmw>KH}oVO-e=)bfSI844lxLWEnuzA1=zIs=4a*1%)fzEf zZW!od-&1J(l3Er098=V2N1(FKaU9?^#<{b5$1iz9mA^-N>)0+PHBKh5$-0CYqm%7c zsozmb*pt(avTC{t)BfnBj>`h*_!_{Da7}xtI)1MP+GB5B>6#TJx|ko#oSv`4(JAQ$7kud4E)z``>UFt&G78TrP$0b^IoyNgj7QP1%9@VTUg3sD<#kTik& z%PqCB9<{MQpV?Ss#wN8oNpuAj)e|@ynNypHFHjrC7&UMp%yLI#w9pa$KxAZ?aLOs8 zq3P4S)!JNRUhPTh-X4yn@v|M1 zpzP~=Hmg3qU5gj|+Pgk+19hvfH+*Mz_(ntQQ%6t8jjh2huS#pJ!`Fph~wD;7X44Swsk@m$kH{Ujimqe>lftpFiGknap%il?phohrXi%4V;*MA&@B1&F-Yz1o@Pi!QWx=D(5roL3MK zXHs;gojivF#?CdD5zC3Io&BPdI<-=#9$4+X4n}bV?4S8_&mgZ&my_2Ns%hje4_RGz ziM%d9Ar3I>>=i6eJPGX)tZDDM$mHRM_qSq%);diiw4x9b0?Pn~g9A9;k3&Sth?;zq zHlJt zj_KZ?YWlF071}$w@!=%s$4== zeIlo!$-|6m@ai(zdPSa@&64kD3CL^jih9_efX?$6`M>fLjgOHGb;V;Eh)$>?G*X+( z+HD&@0gheN5czUP`ms4lBHT+S8qS;Cktt4Y0yjC_k?E=SM(#Z~5xG6tWsm9fzH0M! zlM-0BMf$`rIx5X`lPj{sGJRROa|wb)TRDlvYB)*al_=a|W|kTA9Ncq7tRuUo;a)1N zw_C^fR7sA&O-z}^F`Oun&W>h1bzu4+2Tm30Y@VBjFg<)Fl5E<`F>No0w+_`d^H1Pv zcX(O4jlI-l@he)V8bBlIW)cHrqZp%FtO^qUg9cl{OnPN=l zU2MZ|a7QK~PrJR51;cB+dTU3GSN!BSEf4#QGnnshV7@;Wi0UJQu;ACUEc%mnvd}!OtP%?`Yw9usRbvX2#)g3nr8V+`uUS4W%^4NI`c~=4bcOKy;@r5jw~H6+yrmA zvZ}D{v(7VUComT@1Or+j(FnNfwUdYwh1|yemOJ7SWDN|?S(tZYXC2F&{*rr!U>cwI z*=yX9g}|QfYMII!rP?^7p)|H7nL@**kXmsqeDJ>%oKCnv{4{-IsjrB49iD=Wi1)W# zhRb@hVe7Xoz6j|%RSxsZGXLbh{zBT3%KeXJEUxn-k&bZI(**m`9t6ZNgw z&ox8xbC0O<&&lnHEIj9qItO`jE0^BjuDR3nO++4Wxs3->S)PbY{?fkBliRrTs@B`Z zUX47EiZ3sby6h&8vFwB~>tszo(LWxJhOpzYygS)F{!Z7@pSX=XU9HbiwL5pWiptJT z+`2}SC+lc{DcV^!JhG|rXj1UjyQ35GI3J^(W$AR^*#kM9`&(EwW=VHqcU+#s_ zC;luBygipRFE@}CW0wczoYiRXVr{?Hu6MPI z)3QGDM*`LO=q7V~!v(h-(YICu&CDtKK56>ur0MIErteE)dvLYWx4dQ@@BO(aLm$eH zw^3lTfxW}hp|a~ONt~QHZCw;gN*_(ROxf@Dxvzx7$ePJAA+O~wvuafyre;68iy|}d z@Fbx;Gr=Fn89wut?{bQLAo`k_@>#(6h>83^kRMftzm_w!Bl)|rd0P5?qFofv%g*0G z5Rh4w^gq&3#HW$j`peCq5283ZF-Qd|9UdRAHY)o+pS3$CzNkxbEK(H!&1I7 zQT}nW{F7$+60`h6Qa&K%>!f_0*SKeh;Cf>MSDa+u5}>K{Zvx&3jnAVdyxBeQc|_oO z1RkqqlZN&Hg|F%@nkYx8-zFeadcymG3GZ3cvmJp0X)x@$?%9VX%w&u1Ur48O-`lBD7%8=dh`eUVYYX&Nx!LPd#9M~ zy$^VFFTLx?sl(hJ?UfB?Bbbjf9x%n%H>lGqxsmgu2gsGfvyMXveMrm&M7KwqSob7L zU1);axIdlsO%BN#z!_HNkw-O?#%dA*DMmqeG~dZ7>p!yWLHOPzz*eu(F%go^37wb- z73~j&j>*-rDD<9O;p*Mk?&IZjuMAH2f{Z_b6a$=`Fr(^QS0?y7LXxWY*gpo9vEdji z12t5fMa4#4=ecGqJ?!Ohv!oD9I>7-B>#Vc>!74fNEcixiO^tUwjV9{ibwS-)mbc8u z9z7t#PAgf2{Ky#oNK-yl`MacQnYc65Dl$4LbWCJ)Qs{k=(Tva@>4VP3sPdP|@Ba%w z@-Kr^=j9v&)_+#6;aRR_T=R|l^YV?EJ{(>H93YbF3PkhF4|*`CDgS^h9B(t$q5jYi zO-~M2CDGEEd1V~qHY=wxPrDMuSI3z(%UPG}xK6$BGad;_!fJFso7z9VibqyWukn{m zlH+HR#^|%7V7zg9xd)D^WUa;5P3f`D$^7&C##z1~aTaGwE2ki8aB4$pf3=~Gv*SjA~LFXCgTS9LEP>%!5%%?eYuMp0Ri{K&o*bN?BBuy~<2?i?+@1sEJkX z=MiC4-Ol`S5 zKu?2vm?(4uXO~0o;6l)|T0WaHR^y+g?S%=FRkmK+^+w17NjU1UR;czDtE@}s=%lMi zF7%;u0G|@G2=^zBG25akni4~&7<=tI`GyT`(?n4dYXIU0a?FDdqu!Ik0jK$ui*7u?~xA_tNNSR;$I zWXM5i1^yC7YIe5c+S1G{WYM@cur}5RT~qsma&DDYt4q+i8zx*4z2CtG*p!Bpox#78 z&$m=9$uxP0Q=y#wTfZqKyP(ClfuG+v6_=rsE8#%r?0a)-j07 z;K^dC!;@0C=(?ObD7N@o&9b>Q%dYg7)X6t>Vn;LBS_i+@i39ka2mQu^M~PKh$0Yyt zx6+`F?8URSZ2bxzj^;}k)4I3B7B!P!Gm|&zV+N-!X|JQH;;^UNxnuqRy*)O+F1vM9O!471CGh&8?Rue-(WbDE<;+dc2LOE=Ii= z2hFnC7zj&J%~WTyJ9?KR<)rx$jGQ)k#SxJ(r(JNT*thY8+H0XIAv|iz`3i;~-+8Uy z1-FA#;mO^pqPw_3#TlVrh3?I%Z1et#1uBtsbhv&mvo)VKOiWDkamVb06KPnZj4{v3 zdrWIz?6V9J?RW+R^%za@wE|enA1+~>0=#R@yd0$NLrXR1LoT8ss$TMq?gZl4_P zDlSX+Gf_(d;n+v;-dT7L>GvCY#P%C{f|zJ8Crl%@Str%6Vn$WUX|=X0xn5`xuyZy` zb4E^G`7XXac8b54-`F5SKX8OD?%a!B1~fHgCk*6KKGr1~+_~rd9N`p|VbfoNBHZ9) zairQ#N8~_{LrVsbG+~T8>US6oDK+MaAt(p^%2!fNR?!X|M2-eM>&ZP#IHa1gMK^!T zX6ZbwL8ZSux%*Ufx!@sc6ya(u)br)`v-?qWgMB59=BSx|h1#$yWiQdv!7-E(CDi$b zKG4Zp`Y7XABWIE%`exEpzp=EC50H&z zpG4Q}+wzy(f1jKd#6fO@9Qx}{{oQ8|T~jF^pxUCvp=^9GbgJwbdl1v^8AnOz!?I_R zN<)Vuck^YY<>%!?mceAX+HIkF+*1lob!us)zoa_i38$)B4al;8W&Mt@lhH>0do0^E zLt#Y0+k(O)UM$VK4;{77t!=NCoKKPa@Yd1Y1A=sX7~Vy`8{>6xB^t@q=Z(k2myNTL zAU}rVXevICR6lcQ3A!Rx!h8!5#H-b#X;HQ-7ku>fEyZK^&p zMluyhnY5`A?xbtbh0+Ss)y8yXy83bVOOQy~RKi;d6hZjn|Chu@N#xr<19{J@Bd$v9^`7ge%TE-di^N5mT@^}sx*rU3eY%+4+nBb)6U0{0 zGix~2HAk8r{gM?O>jO$P6+r|>`VW_KnJ*fZN4(0PlO+(1%9&rv{4_<&kbmVq^Tj+e zXf^p~DV3N8FBZqG8tE+qabnSELuWL$8nq9Ha)k!f>8vNHrdtNfi?3_d0oz@#B}S(4 zW{ss;1=Vyri&2NrX^c)C%Vga;8%`kZ?HHTsy9F)2Wl6R4Ku)DaBr>Ju&%fb7D5Q~HMEp9F>0 zZ*|uXY&C)LW;U^?BamT&^yhJ>MxVk<#|&BC|3|QarOGZk zLUn5M?IvID%DBuU<5HgLv+@cbyjU2JI*>LKy40=Bb#2;qSX5c z7w0q-!G}d`Br9Uinm!~0(I_Nr3ck5lqN!ANy{ecjt%>;gjZfgGdwVWlp~x6JYKrz+ zl~T#bLu&IpBrQ?@aUPK|9SW&7k1-DdY$FjIMet->Q44=(`9lB;DOdbC*VFt_NN=zt zVq3Nx7O}xX&M)Lp3=83(i?l@6@*9uv8RK~SH7)&$9ccHHH&-m8p!$O;DMm#yPrx2FRA9^2klAcgV|}S`;2=q1|83?8 zp4iCV%#M-|ILc$Un)ac(|LDT2C^q<$5~--Fv5zV^s-`uOPFwW%K04ck@o()<5h;N8 zrOKNt1b@gS4=U^B(Hf1S5kJ=O(q4A*%%>@YtgW{s{426(<%>k6h=;HCmDH_v4&eJy z9A)-Lp}LB0=a(&q$JpRB@*~;&G~zbkh2IouBk=HXIl9E$+QQ4U*}KU%vzhIM@9|hP zEQ`D>cxhB9`yS%9m8Mq+E<#KlFTk8=oyhC7S6kC$UV}rEPvdP<<-c)?t7Jo(pC=l$& z$9y(V=x1EaI-{moUo7EcM-(w~Iw+K%R4Ly_(cAWFvk*IZM)S_J)Um|*#yZ8K zu$J;iqJi&XW=`56^9R1$MM!CUERFQ4&7OXz`7Q3}kW9N z@rLG0{3G*dX#|zT%$CW=>InG*qWTRT0nZd~b7#p%Xv9{S$v`S9jX%r=BCGjuVD`~y zAfo5HZ8nz533_{7s%Y?c2y|q(GV8wA^X1pV zqmV#GlB-VeQ;5tK>z$u$9BS@{G6=v6XFD11M3*`z*3@7Fa9l_(!S=EhxKK{GDUz+j zg-EoV<{(FN6wg8BVZ2GChHKcZx<1)7TqHxBn4Rx~w+wTws zXu%c28@rjUtz&9tgw{ztx59KP+xvQ=l`B`US%WF0PTiz_E??nW7frv51)BIs2w>%l zKR`Ci-lyZzroD=}(g-(W0?By94mpxtw@d)>-m07D+cuf$BPyj~WL&aUey8njy@vgn z`c5=3E8m0i`Hpe<{$$aDn?1uh_fq8aq_p^?PH`m;>D67J(n4!^SIi!7NL+MCHih$8 zqZ!VOzlN~yOY!$r_5t+^zMLFPp;tel)_Xrj(|U$qI{`(z5eAyzP!%B*81*=Zo2p`; zuG6wvngz^0`8h)8%%HpB>LNLD;kcZ3q>4mbI3%P1n(Ig<;#7*#*V7pvtCezy7+E-G zKnJZb`I%^la34RgT-r2vWY%78X1vbL0g{CyQEw~d>(y49L1kpK26%<3 zHZ{-dhGPl2q|LN<25ih0L|N}J7o z#=X+%p-q68u?M*Mpftlg`2c_y)XEPLX!%fHvo6cdGV_duIcuQshgwur@nOh@I+W>J z;hZ;+jzU-^ZOJPT2qBiQ_!Z0d{t9s^C3?e^zFuVyp8tTTyf2~rSLA&MR3H%APLL`Z zyhPqsh!b2rE1p-qv%Uxe<{}07K`aWjI8FiH?7nnl6Y^J|(Cj7>j%TYi#A2}5%mk|x zax%o=_9Dod^=6rHO85aiPDp+Cq6yRTUj^#GA!Y&7;P}268>iL1*3%ZgNf_g=&v$1y zY%P=Sb=Xd;?^l9yp9bKb;r=w|Q9g)_wYWQ9rd3o@+i4E-mKUcnXj7zg1{vu05o4*3 z>up21%5cEFHlMj_)6=Y>s86}B!Il52K2=Vr>H4Dprjh8I*Q7qXoLw| z-5G|n>qto`Qjpfd5`P{4tG3gaJ4x3Rqc=pYxCYNH6iSH+pl4kN*r+YdATvjGELcJ| zxK{}28rTH)w!Al0#AM)C6cbXY>9N)z9fl}^<#f2exJ86c_(Tqq(uM+NN|vVffARUD z%i#wiD3R=2KAR5ka2)xHFVf6lR$M3*mzWioCo0aWSflZzX8rkbJR1wL)O4cO;DGN} z3Bnmt?q?s=2)2xqjBH>lt(qg)!YDv?Br6VIM&lXY~0(br$X*R+^Pk;K=v znkAl!$V=^J&oSNh*5%T8$YdEt47QYccNp9K1|tw1!bb~;*Q(JTLfV2ySGzA-t~;RP z=E~o~tdM>95em(CUHxH;ujHg`5sdkvyeOO}2J=<&X{W$d zU7u}~w0=zgY0-v)tNOCWD<5R6$}$!7NTw9tAuP796=vAYm|ZK}!x00-h*rWe-qq&E zMZ}{-_}CjX$L8KFzk1*88gi_`Un8&ilo`Q+;)jvLSN`WIe7T&X;nk$vRP#wQKKA0(A(%DZTA>cmm>{ua%Fd%G>$FktI>M-b z+miEKM@N@y+pQhk+TTsE*He068HsbuY?io3&?McCC~1_~>ED{!MBgwPcYCV?v(-rN zx*l)LGfAYN<#_2?us_bLT6fl-tW$SJonvw9;SPSCxL}XUzo?g3xM?O9=>Oii3ChIgJc8|G-|Yow)EXX4{dpsUT$N?H`?hQ3YTC*hsu8yu$Hcw#c?*8$8trea6GR`Y`LRajk z?2es5(jfXGI`xaB_F)>|d5H9AM90pgK8#GBMgPk9*T|%OUN-&irRn#?UfFsj*uk)n%0Z^;Xdv^WkXH`ey^9gEqku|oiP%3rktQ)} zY5=J?5`*Der%xp9Sf+{<8s2(cdIq9u5xZRlh5el-hDJJKK^^0qV`5~sBXO>ZVN~}A zVWD*$&5+3Yclmf8P3(z!BHA%quy&4GjUH0yr%HHP?Z8Rs*_YK0zlu-#@#*pF!Q)`X zabLh(i|Hr7`N~a9mVlhvXZ5*3+oEFf2dr|rwo*ly->Q-8UH-;;hs-m1CCVxT5VZO^ z;gy4y=d?j+5Y{{bj5=|g&!t-RE=dK7vwr`&x!bGwGgr*rdp3V_S0s)eJzBFSG;!G3M)d_`DJ);Fi)Dm=%;`8v zWWZ)W?OY&eaLtX=E2$WFy}gNM&f?IJBr?YGDXhZW4)VUs6!O<8@Rs-u3pQ9|jAf$}*7%ml!AoZbmdMBrOv!sV=v<%pcnOCNj@HfU zK5T3A5)S?Z@=d8nGgp~(TL3*`lv>FJ6R~_g!eFwMwmNP_A19GRv{IlkGJt9C0yG=#9MNmL@$nydW8OCKj{Ok#kUcVwsnciz<6Rp8>h zHDHa-sIcZZgNCoI)g!6;hfg58%vP?sG3kg`ZY(o=C}7qtkF*U6CZGR!-Y?ia_#QnA zMh4@u9iwPDBex;nsFBC9B5l$YS<)5tflN#v)zKB@bOj)9)d4R=;Rg24ek8oYdu{%DUz^;`)d zjt2Fpj96$hYJN`A_zkL!50nS3;lE_NPOYBfBdwcOD;n`})q1=bcx>g~0R-lw$zYt$ z;Kt)KFdmdCb4;@wC8cWYO2Rtg9RIOIKX!EbqwdaY@&XMLe}W%hZN*yMR(PnaYF)d| z?Mb;6;kPEOg=oN)aEVO4B~wcU!lK*1K zpQ1LjaH;Qc{~1D?T`Ke1(WrG=k_?4iKT+-MbNp7gnX5>Ya^G)xV;W0vDi(C5KIZb3 zG(Bg8|LBcu-($m#MOjNQ=U+RTJA#c|7CO{g-9C=8(#MD%0+|9jZhS8jJfTtq>9>ob zy(5Xl;uEBPK5pV|1;YVzwnoxA`8220}%EmW-;%fn@0@wPGTz$vd}+f~wdbEhgA+ zsTTKJ3ltY+sn^V6j};5Q`4t3Qkm8b};rlB74VCzglGON(v@tF|WLyZx`T<|HxruMZ zr3W!tq4FUUw>dtmYg%T>fmc!&?OL2DpEK9 z{;f8a!Kj`^dg(W6WMjlJpHRK_;^*~y_q9x}9kdQjuCKu&o+Zq?P?mcn2+OFrBQ|Y_ z;=MjOU#4%rrRDfCUr|h@i6-K-%S`m2Bc$Gl$@sQv)LOG0<=adkkcGbKU7Rtv^De|Lj|uuQ%5gZAP$C zc~8bvvvS}&sFxm%9Jt=BpRuWx3#nz?`UzD=)!HH#2Fo0^69=v(QbL40C3S=rbCog_ z7af{4%f+4qmx_5A?(?%O^!|q}FFH`Qy0|u|)0n6^hnr268*gnJ)sNB|V9PaH#487a zrRXjvpk8)$jOyih?84dXRT2A`@{vc7_^V*)SD}7_$9C{&@C{sBEGV`IhIi;fDJ1?o zMIPG{N*|now?dkmVb8^ry1iVYu8(IjqN-*OY%Vq)sS~a31@-y$#-go$UT{~quC$j6 zdls<~mqDOrQX3e`Ok%JISxm0EHS#)&npf4OxO7723+KhpPV-$5p8(Z=6^Nr1n*vYC z?DAP7xxDc|PpDBB96zC=YUW{Rqx7S@x^iDb)4qQ?BS$l)Ujq4%>4|Z zX|v~_&gd&biy}vl%QD+V&SeBeZCb5*r!g>f_hrwRwrsdgB+(bOpO#9*~c} zi!(J{licV$M{W7PznsUv%Izc%A!as1Z_jc47&_86<~m)%j?*irz==%bLAjq^+XQQ8}oBZYhG0sRe;l3jmZyd}+-A778}xIPO;72A)cX++?@qScv}Y+B9i z_(KJq$CioUkD^kF&@JoI3fLFJw}p#}dd=px&AyfBD0E@uu=a8HFVT0csleNN=f0Oe}mQet_l1{3XhT*S#B&QTrbB|FZesW_&NlS!N-I?gZc zIMY*cZqRXDa0G{SoZE>5N&%z1E78-Y6JlMBtDu3TjRNOaPY2fUhHDoqyeK6!FeN{wI|56 z1RYB(8}#RV-oB447TBUSQZ9nudbKMeY{sI5Z+jN4D zbb>=?I=^1E`u7F23l|GDITHrJHU%fETA&I5JdF9Gj9(|Odn zk1~t)R;?(eWu?5@%HpFHFio<4D&9yLS{i-5i0o;!nPx8%322UNUurmN^B;*f=WtQ39?iNUOs&~_g5$=XVV(%nm@HO?)~UnQ zARNr(v}e8^R=%iu*CxOXM21uNr1k+O?Df?YM#1-$GHlC?ns>oPuSfP^fNM4)FS6`n zgD{_`s+hba3tEIvJZ!!jC1J9Xeu%u6K$|D^*n8%Ag7z#}|| zn+*IdfeoWGY4Ju^+(TinvQ2mRijXmHSt6Ff;C^^{ICcg$DT-G5hQ2QQ1|#wXbdc&H zOO!hCaz*5jE97ZC7kL=Z>1uzN*I<3j%SE1t|4Ai9>gz0d-;OKxp7eUXo4Zl{dF|O* zn=PVmyBEq!k4%3hhz-K{D^MHA9hUmy^6YA|C(qNJ=6%8GKjUk-wiErm_CaQ^KuzBNo6eQOHE|{`eJ)mTGEvWR;7xOMDDAMe1|b&ThTVJ zc|>95GseZ_cvFs>geQXHhswMhF-CS|maK1QVWx5(#&r7>>He34BcB4xxjF3A*aU{sgGg0pO9a{4-bvftdBlD}%C2S-{|+KgJP5lSrvqySzUQrr&7K*ew7dS0d76j^yV{I{qgPpwiI`)V)yY7yLwudNXhSF;`x|6absG3#WT zx~bjV+=HJnXzn|$wZtZM``^?3k`_A0vzUSDfZokHQOI6bA$xU@bfGNfrX)K~gX}&_ zQ65e0))t!$(7S;W3j$``FWiY-W)6>IR6zcqm+Lj@f|D20b?{8*LI0G4HdU-Z>Sb;T z)W-;EJ=5ppCUz6VvF{T_+iX%gP>5qI#!L1@j=GHM$H_mn9!TyT3FA=+qg+$C`KT`R z5ZRJ-;!Ni@XF~Q6N6V@jpwKU8Nw(mosjWPAF1VfT+$#rj|8wePlG>Wa-g^&k9>dH{ zgQwGtYBrsx)+ken!Ti=!GEa-PY9h;)N>k;yC42Ikefo4HXp}dw06@JihwRF}bAcjW zW9XBVPX=CUzMm)aJq3^nU&egT`s3f32_v}rwW58($80)@D*GZ=F`+D%Llw!N{<_V@ z*;7CMr4TIhfKa(ZuGyDI4s}@!nhqnlazAz-)uz>Fou3J{;QtdW+FZfFT&s4XOgUL@ zW82Pysj^=o$F>|p*H_ePQ+r0R%)DOI(R?qNkQ0<-+NVidVa7xZW51kk4dZp*MrZsp zupmMF3-#R+=eWc%#>yFNH%PPL>*c&9w5~N<*utmD5xX?bdf`$^_Azf2kN8U|6tWDj zQ@S_2i|u)Qwt&pFP?WC+s&&%ckCjgCa7S2qgHhwZF9$0H@*ai)%2CXFvLli+((MP?8#ljoEp$lnbH<|6Yx|Z6 z0|rtp?1qc>fDv6nl{2T`M867I^;{TJLrh^Im-SRBdOS6*R|LIYkwvM~s@k(hy@TSCO0z+Y>b|%rMr-sNs9q z<8r%tgHdm(VLwSq4f{!nl+#;k*jsAYuhxHqXZ+2}T83;0FFq<{~i<0-fMQiVxra8+8deg~p6xZ!r0cUp} zKEL&IXG2D@8gy?YLSr>Q*h`0GG>mZCK}SQyI_R(x1^+y=VMInT=4y_FdXrWMZ6~N< zgz$SSAex)KSZdNd92*Ur!?s(Wo7ABm^RnK34qA?`1mtg#8h&;0zw@;tLI1IZnOEqo z?ICv8#OflwIlVPtdF9kr^5``$_gCaWzhW!8t4-tmW}}{7V%p)J z8s4JS-qJT_V8$rkfgeh4zFgTFFKI@53szb`y~QrUt_<_G+}UPJLyYRVppNLcKGvASM>W*n70Gru;(g z7L_xCiaztsZIr0K(zZF7$j#6~Kq{nnN0$nLFjZRbzEzo_85O9!IK2(7f_!_n7}baA za*)UjO_ab4Z-cMEX@{~)`Y0KCiG*fKXoeliHmNQdnlAyFoZ+r=so~!~Am>+@pQ^Hs zxMokV4}g8LR2}IuJ4^8``?rj;3JTG1f?L@Mw^CW)62W;sdB+Bw1+U@!6xp+@yyz1+ zn!_x-Kfh2dypMTgnkmr|_O8SR7taFebQR`XvBPHyn}X(p=XQx`^&3eta!6ce6k z-r5{dSa6nZ3U3QIHx_Y}@v-cl?3S7_23?wz8jA6dC?m!f(_!F4%=QrLX@K#o2~O>| z(z2Ppg!rBE8I;daO(%Q|ruPdvXIUhqbEZW?GG%BA+cF>gIwJonZGUV(COdKGNok#! zalY!r)44%!w?ljmY1_xT8c*&0(Ma8GHU$+3yXh~sn?BVZ|6cn`t4)7K_gAOQ@_($o z;^)zOK}-@s{JA;Aln?=Q{qEin<$d*K;S*~1&AG1il(UKDaPIM1{?r(+ww-N}Qbng?imWd%>!^@R2r)n_qZ0v5wZ=pT}vn9{O)38<10b+dyt z1=QgXz+WfBKPN{?u4$kShXl?@hOZ%9QNdzG1#5t-@u0AniO;+54QIHwrhQH&U|BAQ zgW+-Us|hVH-S0(u#qO|rVWwlLgc#*3^wQ;9lhl{;TIDo9C;dm#axkiUtHP>DLowt+ zW{8!Bl95rq+3>yNt<5X&VbK7qCB~xMvlN`A(|1%&C-BXP)Ey1K5pO1a5E(sf(*_Y% zv~~6r%)mmM#?vA8K2y1QT_CzZfIf>=Bb|CfMr4>Uu;rsqKyU9S8%~vwc`fjubGvZK zkSeAnw5h+zMgO&ST9pXu8M~NtWG-7xNfdCd(xf|JzSVj*KhV}&3e~u&HDp>Ulsk;C zsZ`GtZbZUj!5I-l` z|LydE+XerBp$F|PJpb7COOnn%BhzbN|IX3tAFv8@PkWP;N-KTuk~P_;o3m`XiSE0t zXYlW=R}?m5Tt{+02SOEB_A~VPa)Hir0J#M{P z#+nm3!In#{EB4J8B-(G3x@67Pj|(_U&@Q!oD4a zoV{v$e}G-PsFAS)musu@%Q5j^-Au|3+-iC0(Q;$lQEokO9^jgNX8VAOp6E4ml**~# zYxxgDe$1vhG2z2jGF&Rm_bPZH&GMu3bO4n50z;s5VhZK4R!&X|Ws!6$i`SfhENP)% ziR}oS(WG%-i?kEm8=dA+_#redC(jU5rqle9&)8IN69&n_zPcxSEX)bCz$7E zW7|`<)IE#w`||P?6#0U$r);Ntj#tn!1syND;GSwL-E$gt28cdcKEXUn`J8QZqbvL* zg0IlU*tzM6*Bx|ztaj7&pJrhIVZNj{)GitWxIKK`aQ4XQMg@@*yc6DD;an9&Aj{)I zCt=>Q!bH~XxtASC{4px(E2@M4-pXZ>=g1A#`Ue?wLO4O9I$O!{xA^xsg`f5TM&4O9I$RQ2Cb z-G9S${|%%6zNG&eRR67){u_XBgDs`=GwDVp<+1y(yXwF0(tpyGJ#q})e`|FAl`^zN zXi#3%Kwo;TyEwFN=n}e+yC&QyU=XAp15y3qqC;G2h?lMj(_e3Si{9}&-(*%#zlF{F zaq;trpO?cWgcIq(${cu%$`j>XsvNqo@M5f`0VpIzD%{tKnT*d#aQ_FK>$_D}Y9h6@i&Iha5>`T(|=&paixlP$~L5FgoL z)tn1~Xgx{z+xEbT1lsaF_-7_OJC{L8_Bqugsr4XTKI*OQ+j0LQlF?_?c=*3fzZL)7 z-h=Z~WBH}i)mU!kP_eT;p||Kmqk5H)d8l2{j=3v^^YxZ~I0v&@t+bif@O>z9`bwHt zZSL<%ZSIS`<~HP7oPeIL0@>VO+rGKCL!rvfWT>}&b8m;j+3sLOD#5^6X>9E}fbEr$ z{$hWxic7#SD#mE`HJEAAguQ)>Uv3q2>A)nWbmJRyQ1H_|F6j-V*D9~aC=#5{2j|LrCr&$i`c2*@d%tR zv`vo8IecrM6{G01?_ilk`~@d{o0=*p!q~*V!+~i#|Mb1{1+IfBGvmh{d8fOCI~pq7 zQEK|he!6|X|4^mGI7MP4<+(ERmZZi1zdiqsnrKe9TYHMi{_^HB@i>c3|`&c4Q`!T;`j%qO$hDx;J6*yU6;7z-dl?ekG4qSu~? zlGL7v5i(#RYT4UfDlGD^KF7?!oYeR@ZvIJAR*Sv3j%-3Tknf#w)q^N2HGQt2M5fPl zDc{_Ks4YH{3=rwt!pQ04%dLB1g+D{$c$LdO9935uVk>y&m86m#sOOLoW8`rL1#hbk zmV*J#wld4*j;v&+)KE5lriN2l7{c_qo!Oz--DJdFMDl8-dC05o6fJ@rV+}h`*v|Nq zq+`qftNC(>z+?5ml)RuCxP6M;f9NL)7%p0N!xFx!LNI>J$*8t|F3-a zQja0v|EvF({;#sjEw;Zm{112};~k5yzzg`lhW}L%Lis;vyNBBTB>aEny9~PeKc!2S zpH19@w5ya~ggaTjz>dwMg7Dk<{|o=gu<>hc{L|t~`NF?+u;+l`-oXL$O~1KeYQVU! zUfXjYz$xe5*;9=#v&!Hx&X%hdp@GBY6TVHwXyIx|RyiULzm+>gKInP+O~cFG)M@Zg zTo7QpGJyCT;8x&9(E$>-oawYgZfpdMrQ0u>ev!6|jm{O10j8~&NGbj64g@)L`_`(J z=)>ibl<`a1D<7|F{c* zd76mG|Ae>juZUj?S~u6PgI^JZkjAAD%7OWErX%&hEpQ0tiv91g`9H}R*L?Gu)zqUt z-aT?4cQG;|biwLxX^LY3B9iE7JoE?Fgfs>b>ELaCo1C@<+6Ke)$Cfzyf+tIs~w!I^_yGHFlXlv|9 zR9{S?VD%oTQo%yZK}}pi!$^^NYw$>i0lazzT}&?)p8c3xbuo3Eh&kwKyQD>^_{12V zozlgG6kw(8z~|#gI^;P?h*f;@)WKR%pTV6<1x$C zBkOxG8~dMP)<=)5N~!#WFG2TBzS6jFQ-vf(^zFng{J+T+mGiN7sjjNj5kUp;Wt_@@ zE^Os1UHL8CDXR~5MTs_^FGYvCm8=i!N295}6)BZ>{~og*?t~j!*zsK~ZzJ%OG1T?m=1`tC z=&wc~?~7r|VNL6Q@p!cf>Vd^;_g}u_*(X-z2bR7&D}GK%_TTyjmVNo(4PytH-n1>5 z=#R>q9hrgs$TFLFgmHzxz9|#TxKCaGIclZxvmys(`^|MTH{HBKaw2rNME>x-1}Sr$r&sl4?(PKlcP9&Wo&lI@mIz+1?qSA@!q^i zu8f&YMa`j~(Uk5Kzs*8&kGke6TQtG{*!yAc6rrc~ugY)+&zDvXWy9Ra1+gh+{mgeO zL`+$3?yM*}9Ec>^jOrVuR_49D9(}{Y_(kHeZ?c_|go+|o9|F+a;uXa5qQhb|#m|cg z{wU8mL@S!hYj%dXdN;T@oJ$qO&f@KnO^-;2r?YK&ysY2; zxx3`(O0C>q1|0Lim}nl&p`e9T{y z2%lT?e)ycmnw@M#$Ovv@K@6wg8ZJlg+%MRIFLwAfWS}!qU=g4BR13fDNowAo@ z?jaF_+sOmQWt+IgKV>DGMM=_FBS|{ue^XliPm({T)`{aQ+QXeO40Hr}n4_NUzzHZq z3UbtZ*%{JsQuA0ihnh>>l=-IE!^pAPN58h$;k*(KYLt7@O5~dz`7v`3VbO8frIAYh*IO@+1I(a5(aM3!`pUp*- zTn08(n4QHfrdCcw>fC8NA~N6s4tA%q^J$~^IKnOSd1E>Lr-xfwWj&PgP4aGwNI+{c_{hY^N@!k>Qrg#Vn4(7PJAxe{O_HV z8`ujRU)T{=`~P`*o_<=#=_yUEr{~v)ooUn4o{s@}YUL|*bcMc6ivNZ(QuIsCuDVI5 z#U3H)q?O_nTN2fxg5i%G%$|Od-*WZxm+raiZX74`M(bVE%1hp_;6}mz<^XS!VL%6c z-w{$m}$VnK9k-1txRTkhy7$7g(9VWM&#NGmU8in=CNq zWxR^QUE${tdxbCulG7b8j{xQw0aR)O^(}G;n3A^AAMp4b0?Ov1o6OLzYKC@|^CZ-0 z!`lO;9P^#XLbY#2Z=?D#nPD`vs&JcX-!ajgE@R9b45ZX$SCp;{^VU5nw#t0GnIHB3 zj34_*UF8-`ekd0=eBgdwv1o417We*rdNdT(vyDYnzusTE-_M54?`*>olS*&4oh=){ zC8F;sx7O`=+#C}?Dz3su#tXMdxRjUZH8Xed>5eLgd0<1Vz$vE3CaqNYS)+e%UDh(J z4?3g9$VP4Fqr5TQT2ylXpAJ*>C(T|Ny1@;~(7{Ho zB(i!yIJWmm5{_mVbE8L>#?jQv;aIL%QS+mO=c-7iBR2S;(=pt^5yYH4`t};-3>U#Z z56*)H{K0T1K_U0>3_jtL0G9}uuFvWs4wkm80XSGp?^flN`mKTI3Bd9UW5>Q^G9_|1 zu%s^b-~lJlaFa+B90||o_fz<*z&b7bRb&{Q6#lY+rSN&;ki!3kgTfD=B9)cG|1JQ_ zvz#sLlIv52ccUP9Idddt?%py-W=Dr6Jd?p7I>WX6VRjJ-&rb!P^S%q8^Q64v8>2wL zg3m$fs_^-N#^=@)KBsAXekl0dn!@MjZBA5}KjltXhAKa3e!gU2eX9JEb@@M#@pY!sdkX^u_S1>!+U6X#GW9y z({^}5$~hj7M+7W*{49ybV>oDs4|*lr;THn1Jiljyvt(upkI(l>JIrva&M4WJ@LW#u zszt;z3J){tmnsv7A8~Z}B(7G%>i14#uws+QB#qz49;*GG*G2-`t5K|+;e62CNNcp7 zujklldX9Ak{I-BSIJSx-N3D7I7T}{U?o?e?2*7*~jdwN0Bwjq9vW~Sc7ukhIU&Y3t z$qaZC%Z9ux9x#t6Q=*q9JQL<*VmBw8oCsIrjh)#6W1A`n#%BsfcEg|2j`67imWG%9 zRRbVK1B2|8lamea5o9gTIUF}Ec_`KJcYf>Sym(U1jA15MPExbsV|x^Ayboj+5J$Dy zeV6E_s?-JhDDRK5Q61~UJPC=p30D7cazbrScz!3TuS%w#gG?nfS~tG5@>P;laGoI! zMaeYI|L`PnzD@v~&!F)_hIdwGJ74407rX&`;FA;~}ouF~R z`{#MdMmt~QJzVhqOA_ynb0z-suUQN_PM;3^L&x=P?yf=?0$a0Y6-L+5r!*J5jDz>h zgBEYLMzi+yl(J=itCzab@HnGkaYnmY_P9{7nAnY@%fQ94yd)~r{-N27dF;?yya{Gq z;WitU0jGp#HgKQ0KjFCt2P|_FDe*6xt5W?5VB~dyIl@C4Yu*wEf5a0fR&JZ^=wi7g zXvTGZY<(X+k;6K3H>==iD!#l}<(92*c!L+BvhDJrehJTkdjy3q6P`94DHOi6Q8+6N zg&%i-ktE=rrin8t?4)>mff!^_Dg`enVg~TX{LmYBh7S!%4YZ--IC3e z4=xm(@bn~vdqJQg0#Q*&_R3A0eXafuz;4c+OFUZhHT5qnc|>`zf6Oy7&H*lEPnQ~C_%z?*swJW5HYk#XxI z$CkezV4Eg8mk1CMC*J;z{iRG}S$(5ujEzQe7% z`GM$w0)^G(VC6M^PZh}aoXv1=+2L*%;nTj6jlNe3Bq=lC8@C{@mnIMA+6Xgb%A~JO z%Q0Nrfgd&7qO)rTFa;*7!C$gI;rZfjF;r^rr?9tcO>$#zI^h`zJl1_rvOUr`MOhQN zQ@Kt^To(Zgspu&VAr%|x3cBbrg{&}ZPD1FSld|ceEcjB*DNX{@cE)V=EXsZuP^aYe zVY2L`?5KdH?8n3*Wlzv$Z!nU>qecLh=g}^dEyjm++1qzZ*-6q~A!VQ3q3pEP`((-c zLQP-u+t)%vz`ZE)RB<4&Q5K5vze_c#{`eFM85)J2g2IVO6m4Tu6z&y=w8AA)Vl;P2Zn70_5rE~H-4TVxUD66UNfaJr8^bzs z7btv7NT;pvE$!GzE9@4qw8BAg2nw&_0ELTFD8vL{c@7|DPiqCALcyIxfk05`Dkx0< zE)=Fq5y$guw+L8Jm?;iHVX%}K&3!2+*$OuZ!1CPH5rz6zX@$Hb3Ule9gy(@UD11ps zr>*cM?brzlF#!t---ts{cm)S26sJ)5SOAviYfPV|wZbI|h2BXNM0q;l$rKcB{Vo)4 zl_HLBg&PDcDBLLyL7~5t7|nhD#AGX6D*($gts@GrelD%hSECS`lkilBDEf1C`MUTp>aL2yBs@oFfW?g+u-Ksx zQXBdkrIh3MOp^sHSX?X)!D5OO8qIx%?`8ZCH>X}07 z4FOo5O&n7!Q5vKoWnll4!Y3NeSEwjNN>;~)2`Yi_LM0$Y9goTg0ShYQ#Gz5im6D^m zwOy0#GeiKEXY4VkEQod752nj&?Z5Bhm7F(^ud&3d>F!w+(%!ad8;@D^RB`kCs?<}6{!xhWFd_?fU|u*Q$yNwLlj#in(sQ?k8ScWGgJmmZ{T zT2H666D0g(22c9d8Teu%fW<{AEItu{<=Mw6(310$SaeJmzX+UC@;R@xZ{_7^$GG95 zyyj++$sZQkR}r=xF^j4sUokIfVk7wJwADrkp2t&)p#qjx8!ZlLwQNBznj7hoY_)*` zusov#-vLQ{W54q{1$(8Js>Fp8K9L>bu549O~KVx#Sl6CqJDw>py9gPH_LLfCcw|i9>Log#+Bbb|rCtRsfb~5$7FCZb{+pq@c8B zEm==%np?_|AYGN#rUO+h+O$sxywaw5A1SxLp5_JZ)4ZpEPX(RR#c`5aBfP3XfW1pI zg5vlTiWwTko`T}_Nfg`D#4C5GEsM{g&RbJ0b{XJU6+i7QmWEjsrF25b?-sBS@)~hy zLhjQDEzeB0*_{G_|2>r6W|QJ?eVopVC*|?-!irlSlF8%eir)N8>Kl85sJ!=bZ$3Za z0)DP2AD_s8T`H~&IM`*B>xexlUY`Uk5Z9gJdQ4o<6G!YGT(L*^>9nqL znAJ&LEdfhieV2+uT8V-^|NJray?(}@S+G1{{lejyd_#O z-fvzeYZa$OU1Y0RE$frrKfG5_agz#nmrpn@JV~F`QK{LhakTIW(5lTS93By8&?+dy zP+HLZG|@9)j>;CTWZTUd(`T4E!GBbRp(A`vJ}T3)!TqlJX13MGP8SsI@gwIwiNu`L z{#M~5@4bvSuV`ka5QT~8Wn8TrYRqtBm&j{k0IN@V zA8MQj{Ma8I?;c}TmT<<#!CwzoNkw!v>-kl6BRTpS29mp^fh*pV@$)d`?I?9W);uEowUoiTAr`lp^;(j zFbX}F!n*`5*In(~>-jz`n(^Wy8SseTNhJ7|{|}4_ygMh#i$ikW>u#L80 zX5LGfVda|p>M#}3=gIB;!6B%*k9UX6&9UEXlNmbv4h}(-X@kAyRxY(-^k4O@4EuQ7 zN5TMU{otv_U8vs8$y#_*CW4+hS+(|en*E(%f6MG|q5XB+-?kgoD>bS4;*Sh}W^$$@ z-*{L~GSg0X?fHZ{@f#$Gx)$5#>72(}x&328U~m$R{Pf*g>!k+4sV+( z<#Q?3&VQHq7elq}@D>#=lT??#QH6`Xu*R=Rg>zru4u4IBa}aHZzo5e9s8*+cMuqoC zg+H#sMR{6>KcvDj_hF~6QsF{yba+UGV_n7$ze$CQCa?~lsKT)*WrvSf;i6EY!+k0o zPRkA-tit=G!p~RXr>4U5Re0Z2c%Jx|_S61#k>S}Y{%P93A~O6i4;&>zIUK)-c_2`o zb#cB%xSe~B}Y!16W82wRpg(z&u-M(P7Bp^B)=c9iKxnP_Pf0bzHB^VHulLHu# zh^!?Zo*seHeMV#jo)Tu&9+WvMoXtVlugGVap<+9I(8|4s^zCDOJ{CIra#uF5ggcs; z=@fBsVnTx9&$)7iLY**>33omzj2zVJq6I?!T@p=iA6ud)tw(v3@P#%XdDKO zg`%|4JzqhK1&aP*(5O?;G6fwk&}M94jOv!XKx=;*CB+^M*`+2GC z9tVs5gf$voVDP?rXE=9XeMWdJ^7it`@QEq*hocJ$4%qcil zdo#PsVut?|&MH8W^q+2#4d)hMHv{8D3D|mI0A>JC#Vi$S34E!*qXXyckU_RT_82HI z4MUu5k1X_?mlvzYrA{0>xb?OrLXX5XC3bbP%r7|)&}B|fmH90&UFJ2q%mjdC5O~K@ zp~#duN|iZ-W4p_XrBK8Hj_BCIsLo|YR%f?nMpnDcv4j0qznibibc~1&dntAr1!77* z)N@3m>m}(r*1y3_2-#-E-rQujz_S#jmPR`${vpXaoOjs#Cuj0rme)44s1#zTZH0Q4 z?BfM6XZS?bJUBB#_XVvR+!&$A;3>4oQD?Y2&pJW$TTjr>J2Bu2;76&@6w0C74YM)H zjK)tpfQoBL60=6m4~SPmJTD9#tmE)^0C8{+bP!MgTL5sL%Uyb4X8{vRXtTD-^*zZy zBa32X^hFC6dn1Pvp{oNT75a=VvNUt-0gCmTDxj;uSbg{$bhSgJ5Y z9v3n9(a`&-$6v@(_5k}BVzC#=u%_<5e*VY3i%^Me3ylGdDT2l+g2piLY#l_<-Th;K z+6ex=d9QIaA;jv($qA=VeWe$;rlTumGgq-^DFg!r?MznkJPhnaFV`Bl_8L)`7`7}{ zO};WRb7A{_CnHml>DVACPG)5f%F#<&{4#PqroNYtPjYO#$xt}9P4;)0{r!{u{e}I# z%l=+te@F3MF4}6bKT;&U?r$tZT*EBRDuV&xG8iv-&WTLGEnmO=y@@Yt%T2TRn3&wo z9rtr_jb&u!PPs=pS$T7B(oPP@bMMv8d#C?ajL6~YDDzMjcW6{KJM~E!nqqP&Wo#IH~ z$RL+)e9;)Khu3&3{BE%Hwx&R2V`4;uE1x#U-4@|;Vion~VVX>tOJ&aRx1`K(JwMieojnPD#GwpC zZ5#<($Hz`hg54TCNb=(ySW*=m|EiJqM$dNaaUkv>-%!5sue#xkBS*NKw{f)e59&}% zte3xKn)fFu6osR189iy6U3x+Iq}B0nfMY2P#oZPR4Y!D)PumQT*W^-u2K8dRc zwCb$?O13HruPNg!)#SmNUFaJ!-zW?YS;&Gu7^T?3$UuBUUJy_nyZpiV0xK3+v%p#e zRwl6V0^4I0<_rcc znAf_^Njr(Y6C<&)JmL#`A1&WWKWD2;7}9g8eXP>Q7W#^|L9CG2EMTl6{bRe{ffz@z zW4AvRZI%%>{5m%8(C%5B>-=Z^`aQ})u$#^4c{mJSF|4zKKyI(Kdb%u`1go^f%X?B{WaQO zjQ>js4{JZ{*zn!rzd`%UlKv~S-=FlCYyWusUr79c+CKq*tN72>{>k`b;`eC(P55_- zKTG?k;omF%Z(ot>j+}-6u=qbw{)jXfAk_rxHK;ZXv|NLvb%~`a{x=PJT!U0o{ziiq zYLIHK2Q)~?iB|8Fr=?T!kR<;#cUKh=VrRXr#$z*hagVGDV%Y`mf_L0&6z(5$-)N8nHtpKV&eLuH#BoA4zZ0E z*-|y1dBX?9_&^9iPoh8oFdQBJfOY30>6eE&T7ZKT)lJ0-UqBIZ?PrXz+eXcQ$x>NY zfrU;nfHF|t7U8}l8)fcGy#tpj#M~JD75{7eSe6nN-90bo#Hs@mLs?Y^ei+V#!>uUY z&r;3*$ix2V@P(V$P*e>YiW=26xZtCSUJ`EZCCX+cTDYHTDY-%)Y-sfKmo^&_!2*xl z&-H`II6-N>G8erm=-iZ)BIH32#6DSVjvFejB?Vu~Qx4O4Im?!h+u_EdU{7al(9QB3 z?3sas&|` z0x7(WhGd-F$tTIfZYNFbetsAY&9OP+n7gWq>ZN#ZC~D+0v;k@0*6!$=ArUEUG^&5e zI+AMY2AX=GzjTw_eUU~tM(_0(v6VmI3}ZJt=b{d6EvCVPr3m^q=~N9O)6l8*BvU<^ zOf{b#3PoEVvLzbpFnp~lgXCSvaTdmM?-Q`uaLT^&l z<+h7-3zK))MSgQwG5NC|@0InKG+)Vj{7`Z|<~J7xkDpE;ZJAE^o3!M8_U`DNOi7;B2hcNflwYXevhB9W- zjgjjpe#!>uN!0K5N6lHpGLIt0nwyZz%wZmHKq%5!HD6NmmyUui8hu+&e}&)uTXT~#aGSYt94tuaQv15Mk@Yi<7Mk16nBDX=ytgM1Z#B3H*{ zu|A(C#5`{jjkWb_)^AP?IDA=+lYlBo#3lauFM!e4Mvp4{Tl+G8e{_tSFa^Unfzfkr zEwLqk=ul7D`}Y>Q(sx;u$Z(0hmPtj+{^e}VT{cgHU>-~MN$t!gTCcuC>ox1vJ4j{f zjFI)6zMZWq^P^N{T>Z9M4@wp=TNpIWE_EWu)w$NK2 zeky|nGE&|gTm6z4yWl~4MBxyRzc8#~S^q*5s9Jxy8})qUioMW{!^acyliojwH(Po0 zQT%vVycyz^1t=B~Z)VCH!b>Nw<;-yTEDBooS_Uo0V;6O?16c$K0!@M&Pr$!U`YNGt z*P&;tyji_o5+S5SgAnuALFn!Ed9pLKep@*DPiDWi6ZOTj?Ge=fkB~L zcpurKydT=3Z)oq|Rp?WX%Uwhw{cx*nUEP#-i)3cPrm2l`SvI@mvfjx}bkKUC3W*P} zs>khbEni0XbA03v=R$Fk6&&A99Ox5F+qFuHk4P!9r|J?k|0*T1Yj!m!clJ%hK{vQ_6#ZEWL$5KXMxiJU!7q2=(!hsLw>+4M>Lm+Rj8tCQs`Cinl_mS&FAOS zl0ox=alCA^bQY=9vcE6(z%7zxV^wySq-5in3N6|AoBe&+{=Q~^SJ~gU?C(bV`vG6w zMNKPJ7bQF8-;!+n;zmJ9Bpacd#ib-0=FgN9-Z=Lc+SwcD)7qKesbnLl%Kk3NhW{NV zrj=D67lG_(4l9R`1G4OKno+jUit_laS~;Tho3--#x!s8}q{`47m{XQ7KgIG>CO_ll zXR`cElb_lAR7rCAzGZ(uu)nfm3jDDB%^`pB_qD(0+uy-_L5~N>WmXXxoYe$_bDLc+ zoZIsffblLY*1xVhmh)$9aQV#V)5Ih)QG>*r~c9S*Fk&05j<5l@lR>3Y| z#_Mh67A{e4DW5l>uea(boFR+nN3f)-3>rwx(8=c7A32mgy$>dQ`zm?KGn^E0UW}gm zTSnwDJn}BZTL1pdeka^3c_1(8Dd#hJIkh~#loXEa<|UrY>4TE|Rx+F~wnS?yvz^Mt z@k@4wa%JLDG7;o5;dKhZ$oq~4S8w{NG4^bo775k-ips<(azduAxf}R;AynSc@A7qp!he!) z*7Wf2vrB7=bLq1__y*sVS4ys`F6QEJWsya;qd+P z?Rp#UzJC>;fdHtYbhi;Xk--JSA2den89i^<0B==-7fAMX7((n8jB2qk6^I;V2w_rr zfEq$m1}KqRu^K{G;*kzm{ytOisiODhr$jCab^2@j9;1fY2Nms$B3T=Ud9z`#Fj5ufK-K1p8QLj)!C5_Uf} zT%t$&?OY9|nJj%}ZWD!=hIG%WWo4<5#?itGK-OpGBj_s>F&n~}!N_eS4hzNT3G#(_ z{YtVd#+~Suq*KtxDbUA&d6}PtllhJ(!A+9D8}0I3hEaVp=hmdXLeh?Bnkd5Iw`OH8 z^W%&C;C1$N=8ej|@5Zt`$)K@DUa7)Swaiflo0aSc zN0nWqR0SLm3><~cFnXxB0sM_MHvv>EgIO~TCy-eJ;SZ+JrN*V15sr7Iavo(rV#M}f6#|D_fFD0y~L_%8)4g+Cz< zDSV*NLoAJOR3SP{`SAMoiW>|1~n`oqk6c^D@L_D6B9 zJOoraZRsIYrIR{eBw(rYrQ(n}J9M4z*^+GO3j|DMcMCC>u>vKdumM{p)Ynb{b8Lrx8S^co zDD$aWq2!Al($4%tf9C_R*HLAbmtE6yfT-#V617p;n0GJ(SUA+t+fqrmM1EoyVhNNi zb}~Zz=7L>(?EO^v3mg-dFO;A8@>7L?wMt51xeyGLJa~y9;a!{%qM{@@v7_wKcn3#f zL@hzgr!D5fjH@!DuSglOtJbj|W?A)_sv0)(SvB}sI>x|w7(KEc#D}du@;;SP{AShz zDmZtj3eK$(Uym#^6-()*nOU_8o-1&IALi4T^*Fzhk>U2-rr@4m$j9C94(01NpAW0b zo$@hN8l#8j*E;M;4SbeQW7gCB{uOMD9=QuuaMqtCSYbJq0!aOwg69gH^8c9%{;TSM ztQU2#zzKde6+9Lh2dS6pV1X07DitgZP4G$`EO3I~=F^z9fnTYdz1dnS433C25--ch z%o-;9(x@e)-fzxrBcGtTMEH`(aH+!S#)A(^-qGPZmNQsIS&$vmlIyPvUH8MOciiqb zvm48@Ll=fZQ(;=BlA+&-dTt`2HQi|rycP#%it8EG*NfA1ZSptzHf2jTtlOL1bmtRr zW~|$rI0X!DDwJSZ_!*bJO@oE~0ms219lcD>tT;et+%(oq=9gd@5jUwUYOV)@?`PT< z(!N`@uNt4Qe#;rn@{Cbq&6E5BxRj0^yhdh@ww6_x)dwG?6cbL_yX3IX)XQ{X)`dE^ zY#p{yhxNusf_y%~=mm$~m8;{=?~#pZVb+KE2y2n%)r^$g5AYyT!crlNUiD^m>_ybU zI1X9Qpx1nr*Bk{0qxW$}j}yT3I;aQ{L!tf}@jq-#6p*%rC zspi-`&l$_5ytD68D^d|*l~l4Koe8a}Qp*BeUZqy&^7)*#YaR$*Nf@A$;3s|2*PfD-cW#__ zBn~0e8{VW_qq*bOCkeHK5D0Y-IzMf( znE0Zc>L%cSq}WIC={4$9gb#WL6}nYH!apF>Uy^C-!1Qa_$!(V0C7dgvavR$cM|T|K zaM&@xWyQKAE^_7tE;XF8q%9iFFNoUK#DZ*Zly1_^4bS5%C}x1^-ocZPN19%Rxba9Y z;&Bz={x^Ezp*l$Old2Os=Z>T&5+2VG+T#H*>U7P1fL83J8|3&$bwgAf(hWfzRK2k| zS@pXGV0q@T&q?cscmJWeqyH|YRQGq4>XuxO=Z?AxSUTS$4k>jL4NIv%N|lnSWB%`QT)9ZLHLgJ7w{fH|R*K%~6^_mPS zlkr`cCVvG&erx`!zBq?D;-ILhKFBg*IH~ z`$-C(Hs8Zl$qkO6XixS5uhOq-FJ?xHfq!k>p|@+D&2yv26Zyn3di>h0K2c#0VCW)y zq;c4Ab3Nk~5n>zF>(M9oMo0IpFpqME>J=fO!#i|T?->TV^ImgYU&0+#UUQXq?oR5& zot07fXd!(f@%&cb0PVI*cqB7Nw0w5M?oh|x*%i^-3!(Oq7hgXYw>|pFu)~0|ub<;3 zR`NjfzQM~*8LGGWzJ9bf{^FsS4Uu~%{kX4kJA&gK_KlT(vOKE9YG18#DFUKWZDD2A zsSuoIWi63DtSOexj+Pbk&sBk%GQ1#;H`F^j8tm;-kA*)q8L(N|+ZD9V5BSYj7UCV# z=-Pb`S!3A|!OcpSDqO#Rg_)JbRDM`sY@_R_)OxHn@hDenYDBc;kIovrOxK)S zMMtx3LgzDE>RWPsB`s!seY{6Wz1bu-`l1M%jwnGf=x}s2)#MQ7Z6y{#V4t{Z*Q_>1*TUI;PWj739w9rlU7u6!QfQ zs*zN&d=>i@iDq2&g>ltRqp;r}iEA{}ZRZ+}ij47gavs?D65y+IE9pA%zJ@p506i}? zgDTwz8YeZ=b;g8^#A~4UI64`Uvyx704n!|291*RIyJ-FqW++aFh2nJBupc-0E>RtJ ziF8;nPKU|mCOQlq{peU%^djp%r#aNjdlBQmk+yrn^P{SNl&{dJ83*?qh+Z`fi;}4s z+N&EgEt+-SA~mvg7nT)!qnGt9d0$D)F&1r9@1grDKr3<}duG>?I+V=ofS65@mM)wK zDYXy3fh}JQ?iTF{Sa)7eHQAtWyxCEH30=8zAwSo=z|YiXer|7}-raW@;xFUpu43_j zvj^un{NKk|p7#DhfxY)N&UdTDxq;W)j$)5}+GZ89Q9?daA@51ZoBp{Cf*&W_ZQ+Li zV8VS`{caqXji1(Zg@)Ysj#yK)Tx9{vRgYSLi}U$RPPXfMBV|s6zhO%=m>v)4Nj?jw zxySCky048N)qzO2x~_EQ`E{7X}pu8THgH9o45iw<|@=XGlJ%qh}ATpp_~puvc_Mz^v6X)aT!Jn)HwAS z?z2B--TT^ST=Q`tI;Rka`vRYECq~2P+^-OS)?no?%x!`_5`gC{0OII(CXSLi4nKJ` z#e7|cdF#nq|4iiVKBxi~gCp#mpveOQ(aQZUMo?%6wUOx&1c1)L`k>YK$C%m7#{Shp zz~yLeTRBgt$HoOqrJrQh?}gj=c^eaN+t*&>>NZ~Q8*)Y;7{#l%D*NO%bz9FQllrw_ zZqv%U)F`Bfhn%rDUw*$SHVSupjR{|PjcdL&3LAjsypd@XzJtB`cZ|Z%431-s!gXXn z;bZwZ!~;X}-N;Xmf+uCsc#@lOS7(WT*pu>m>ys`pdPAJPC$r^K`DBiKLQlH+On9H@ z-Nf;Y0Ck#)r0Fa^Q4I+wP&s#u6?`(sXZ_46!Q^ai;A-UWtO?GMX5YVCKgqDBWq8d2 zzq5WKk4*A$Dc=NFWJOk_PGFB(UQye@+UN>Y{_FDsrH71~Kg;erY6(&GMFV}=GE3`w zo#<4mt|Ux)DPBT6EAI*Te%T?R(u?8q*u=ldx-Xk0fAlFC?Pf%XGF-kyGI~TlP)j;` z0S<@dS_I;5SGD{Gt7);HFb{sRTD+CjoJs#=y*MXU=iq!_oYz;oaqi@J`FvO?j%r`X zitYZB@TrSd{YM$9oSxA+HT~x~{Y2;V)_AS0(ouJ&3?Q<4g{E()Jm*J&kP8dbvO<9I&2L4X3t$7YeeE5!&f?=P?TD zWaBYkz_RgpQXI1Jn1ll~wyaE|F;4)N=Lw|xX&aAE|A<0j^tFOE(1?y?t2bZz#hq!m z$HMSmo^QDSi#^}>cA7}Sb$^K@{JZ<=sefW*F8bu_(pTTN-#hz!8sJgz(hYE??0?k` zAh`{mp*!tDYFSI8whmLBW=9HXqh#s3;TaN1cEfYTAzf95qkT90@`_{|O$AOH&14&v z)>SM1ptppNoCjh@@My#sM$kD~jnX&f`)J>e8*@97GV&9XP(kGzaR@4};IL!RT}3HW z-WRy#*~MNit-A&ZD*q3A?;amjbuRp8a$|ylJ%IpGB7`=!2_hy6!Whtu%)pFJ04qqS zfV33qskI`^09qgklR!3`LE4IXdc@MV+Uk3>dfKC4t1=-#0;q(G$i-?9#TiFy1m&va z_kGshlNr$Vob&m-@8|RW@k29v-S6vJ&wAE#c}IKhq#X3XpAG#pQNQXz30$2 z6lytp>vn=4bYkZe`@x0scAtU5{@`J$EB++t7)OEhU4KL05uJw~b)Q~eJdC~W(;q4x z#TVRMS|?W$DQnF1T2=3$AOqb*GXHTPylm#!D?p)Hcw7Zv`mfK%K(jzVRQJ}AVU zlOU-tMr78N_CT$n3{hZ_f2!LeFUKij6^dg&Qpv@vghrz6QXo|v?pL=o^HenYq4fA| z_k)56Ms7$oIh>?9oqN4Xb-_r85L92iQv?@b(W zEYHN_1dOgSQ@=Gw3y@=^V;@=G#Ut53T}L|p#x)zU|C;Y}u--eRNU8!~ma6`HRUD>h zRbry}3yc09CDweuOvpJyXhNHT<7zHzn45f9?rw>4UY* z4<%zE>2RN(Ps!j*ErG0g%6;D+#`|!eEaizyq8>TQJ<@?^|1Tx?tBw40$tx9(&=1xg zDbicpqV@x|M?bCKE?kl??nGw4G4~qu7iX8y zCtM@t>uafdO1}K_89Gny#MC{;hxM1%Vv}Eg*frC76>-VHxaRtdm*no?GX5jNr~EM7 zqT1SK{m>e{Ei`I{EieFnaJ@~m(WBu%jvfPvk4Th$TyyV*(RZYNNZ^x7pB5PSkJ6{< z$AdlM>H6N5%oWlU{gBV7dtMxSye*k+IB2!%fG`q%*QawwTQoN<&>h_*i(oCk3v(dh ziF3VrcR|MioVqSuuL?ii&c|i^tK3Q(4&R@zA5a=oTorX66QV7@1z1)d z0qKwd)P`og!_f?W9}Q+}#!raThF-OkAo1RT7uMR!BCW){#t&@`<~_h|6vDtzmBC30 zs{)?^N1{?;l3B}55bIiI4S8-`fyC7=wq<6RiXybqX9ep9wCw&pH)};8QN$tvu*`!r zRNee^>v2Wl>Ye@kCK(7=m`EfnzGlh~qcy*lLr~>_oTiniIxs^sAy&p{bAN?`W89R_ z%Rs)%SdhyNGCG(M#O)CG0Nhq4s}yBf7}}0rSno1sz#QDa;av12i{~pQkWmKibui!1{m(ZW^O;$~AIJszFD)|8TjTR{0`U|wYn+hz4 zt3YlVW4V6hxtA3s-E{?=GX7-++ED*85Wu6{ezc+PI-G}b#xgj5#N_HQF=af)$b4C? zAT`0^%vxhwlY_Bn6#jDj2{5dVOb$lBp*C`Ai&=G! zKD8hKnDo<1o|NT?z8A<8Fb7D;5v_!X>YX@Wr#E?ZMKZIB)>&7K zQ^I~R42Q&xVUIh3rz*U(6CE|k>-3mJBIv)6Wtum}`zMDK z`&o$RHg7~C27&~6c$Ky&ik%bf#+Z>$6$B1HWe-f?gc$=id<)p{ZAez|BBdUrfQFF5 z&9|A(+bY?fgMTO6^IF9G?VVHXC&!FTN3Vb}y0;28#s3L6vi|f0%Az6jWIc^Ts~7%1 zF}|0{U!UI-M$_T=D>9zD(F{2t+wDv(b?^44InCD-piHLfI^`Jtzzj5OL(3)7j*Q8|4Z89 zVqs1MRU236GZvyF;e`U09?mkHg2=HhvpSy1V0~6+mHbAaV_sG}evqa$va%cCe}A3{ z^6G0H;@87PD%aGk}iF&Ly zQA?{v{NZ_*aKy^3(5L?mP~nlQZ50$J$Lu10 zCPPg+r1gDUD~W3PF>PDCSnC6-1r`0zLRf0g5MxMW=K0}M21=V6)v6V-4hSzmKr7iA z4Lq#I#N6I}6>NyklrZy5kp-5*4rYibO|Wl%(jQB-IHhPvkfb|Bk%0Xh>I{8UuTuKlzMJ z>cVeqQ1^X(vs*vn(c7b$(1BIA^tp+rI5}AWEQ499EI~D`NwykLW-7 z@l6+vx9n`AKN$M&Jq@2|^?q6`=I6&4!=3>X&euDvi_3DUNL#*y-}1w?KhMkvZuJ?< zPve!n^b!}_F$IkGEk!=#Rk`W$+E>J(JeP6T9KE6ValEV(GQP0Z@L>r^oJeyQX(cL1 zD=`zmqvPPgu(-3$>V9krJJ9MsI2WkyQBd8-+o@byqBV>lK6a06({L`ag08Ptu_=?$ zFds0SY_e$+m}a}A*}i16Z>8BLaB}2nj;a6iW@8X6(eQY=`S7gXH-t20fwv16RO*ay ztynI1<)-#@AcZ_I3GD7MltUJLyVYmBxSm*ovIY_q{rfa_O!)EtWWasgU0lX^Fox&G zLna!_3+dRA$OwRcmhXXp7ro{Yxz{aUPg>!V0<%~?Q)XLpte$IrAC@JI|8X`!nqrko z;-!kkY188rLB1j@UzwG!(#khc^8GWyuJLL;NnYg-`EO;{WlLX>ZR17g=%$M0=wDaS zuaEgL38Dj8T|YKyv)4e`{Fvv9cv*79))3Z0m`P&qm(-uTR}u=%`du7goyE^4=&}1H zAl&Ces!UPmdkr-jW1uywH43fTged0Ilkj;Zpoa-Du6RrabHT_9^r9C_)62V>UfgwN zBxt!KXwP72*zm6Ng^+H9TtID3=|--kmTnYCtBoGhpGkB>lYnraBI(9JM$hE=x!aIu ztW(j+^O*^uCq}mt0%NY+t|K_P7_#k*un^3-+KHYHm=~0)4pGz5in<1WX}c8?A*9D4 z7MiKV!_ng%+;g;{t=jTQ@y<>*PLJ1A>@=|r-=kIByf&YSevd(j*XsxWxxSbJtOYM+ zDIj&kt@kro6x(CX^rcH{%$zkWx6D!e_(uT~EQK+6-J@9O1afIfsEkpevaJ21CQ4`} z_o%CYMAH4X4|%^d702w6^BSzh-LC_Ibf~5^ z?!Fz!qoZ?uGu9W|f=5LaQGE`~_$QkYM}J({s8DcAuQ4KT-6yd>lke4sImm-}+V0?| zD$koFjs1)tRIaEoy1yNz%n6-3(8u2cfGHm8g%0S@>s~L`85}f1q2VQ#mYDg zlyMj+<1j$RL3G}rjp>l+8GJ*lzjX&&;^a04;s7JUK&ZZ*PVQB;9=|poDsLb1#(qn- z=norNVOmqn&Gh=3H(zFK{Di%D|MT;~ogoZUw*`)DnOs;|E=lK7T}AMq=I z%OAe#1kqZ_@OI#&N>E7@iZWbGT8(Oj$1Z>Wv*zhg1^vvSF#te!E6PI!3;=lSw zv>Z|gGbkZ)C7|V5b8!pU<`Wx83ZV$TBC}zR$2A1G!eh<8e}UT9*bE0DwZNWby(O;p zd}m;|bGP26`I}IX>TS{+WYUBmflu@}y?s;;WzJ+uyjok`{Spc}KuIvBICp8`cHvaw z2FGp)i|{yF#a0%U>HA|#@IcGcR{uMRg`Ot^yy_Jh77C1IK*ylObZwQP;zt!Tai>v) z)(z6HKrZ-{VVo^=8LUWvt~m{I;~OG;Dy@7!bDmqA*{~{ShX&V(s4YrP6PmPJhskmI-Xc39VFx#wRlQ#bO}3`@uvWg^d2mU0X3~x77!4~0IlG~E*PVHbq}n5!o8Idl+Hp7jyX%!W zD1qbl>u=aLYmGOOLf&Q&so|g7xns#7kwWDimw&!N5tK;gY>Nh{*l#GPCt7Y2ur;i9 z5P(&ye~$UG_OKA!%)Q&9wwRO_`>8pfvXNHc3~f#~A@j=2k(uv|%FtHGg!8oJ4fi<3 zNP1^wLQ!t*Y-gssX{#m?$#ki$!FR0%X%wIVC3ToH{$!y!tKlN`%?o!hx+D)5TmCdN zUAV;Z7s;GVNdPASsoYYeFm1}^M^_ z?;>RRFk#zYI*6 zkX7b5L*W-c!T#cWq5I268zKOy$rS>OY4sKCKH1lWEY6oHadEaQbbXpYA;MfvH(;VG z1dj;L+TaSE&eWC#2~j4Rc=#qPekxaJb5BlP zUt81J4BOeSACMO=)F&zCFL$d|yB+YMH=*7SzD*PdSm;&~Bs6?NTxXF_7f|n@#Qx%4hhpu0~m4^js&ITFRHn; zE1oFHpRy5KHs>2lF7eLV4>($x?}(?D??HPQ!4hjmy&+MVcA{V=iMVni`U`TF3j*v?RH><1 z>qERI2qygCUNBYPtQL+m8xM8n!I#(}4-vj#=n z4|ESYAQ7vv1d!$z9ojzIn3aY0zp#8dYtO;8u#X6sSL(;|js1ilS<7IqDL^-5pdDEW zHT^?~Lh7)K6ORi}5iO(1=*9c+Jl_m`hitpGi5rlKpoedj&G?WC4d2QuisLcmMhh_R zCzK`zb(1k62QVWBkCco{4{g<}V9caSU?pJ-D_Lt@s1m31lh)zvWj?Cvc`4^hzfzsu6mzCE2U>INYAv=7p&A5bue$8xvR3%iI}X|-x1l~_%$j%!n{8eOMwvl1KeMqZDwZkh?g;xD8Z?Muu zt@>|TNnD$B)hm!#k?{EH3vKA9C$!C9*%s#TrYh}-_vFB*$$uw{_}R;=1TmQMjy7qR zHu;=(`x%Ng$3yO0eUTaJ`>{;s(`_zkyt}`hpf30N&c#S_d z@`={cK0)@4pI3T~`ChNSIobn^f|Fh@Eg%o=e3F+UYmlwG(Sz?FXF>5;#i5|Yc-&l2 zutw4(YNc}RO-Jk~%_ry*Bn!r9FAlRq;LLf$t#2pY;+_pBYbs-N7<0*W+&im@OogDc z5nIzyJGxNmLwB>NQ3wI$ysI~nqo?9(;4x6EzmlvjOggCiy~xis$wR%|Zw@Z*9xtcg zQgW$T@+h<95A9NJu32s&!R1+DgGDfCt2?>p&Mq z0-*x*zq*HG39l93a2p6z3o2TeMEL&?T>;`l|7XW!;(U~+F-1@Kkkx!q&xGhAeQ_^N z^hMos+~z$;KB&v&FQRT5|F8CJmB8KP?F0N&P}lHP*D#faqI(*MJa%?+sqf7hJrO5# zO*?&#je)?=R)xlTeC(D4Aa zafY%igp+X{SueO9{y#TrNHYj@%lh<}$sX7DWbH?H-iPhJOF!=H+{duo`BCyany_j7 zE&8Oh^OyWR!=I$-jaurZe1G_<>A##(Bs}paWh}G)26Qmg{7G0X-(P>$+4&T1`Fo!{ zgDLAdzO6vaUX=ILDe|7CY^=M@2#hau3??Y1p&$2JftGAbgpaH0Fu{ zQYY}NZAlIUAfQ|~l=kPex`@)ZdCCvFo%@&cCj%ItP=~t3^Gu;@Givb%>JUI#%yGAK zkG|WbP1w<}hm&H50^3U3&%?l#pL9ESFX`tJt;Ux^Tf*dz)=8M05nB?a|0fwIKJbCXNBJmS`Yxsj%$4nbtmewsu3cQ4*x zp{Y_paFbg<1kp>H5a>$|itC;%E@!h#-%FSlUQL5kdwZDe4gM@9V!(+uBJ+@+%toa- z+oX9nNO}E|5%C_$=B+7@=qI|Gr*F%7M;iAR)i{+qxLH4*Xj(6`Y27GFx|y?C-|x|$ zYaJ8Ly_7S?UHXpRk<+@U7|oESI~ET_rw*-k9$)Mrz1PO&@>r1<1~lxzOmF6fy@!wa zUIK)^)6UP2k#loQacDyi1$#XpWSe{QwV@v<9%JTL&dbWSK-+%r^@ znHJ!X8&|yKl6YTzLZLVp_fWyzoD&L`UJ~!;*2fj&tVt#=B3RQW6p)xpp(GAsV{lv% zb}3~)!T1or27`Yk-dV6f=$l1=+grMs|F^mOs#{FoId0wESFSOA)ip-7S{CQHGT}^{ zlF#Go^@ExmTJ>f@aE>FGJV(o$LKh)5(Ss)G!R-I5Juq8?J%WGK4;#0!i~EttB&~nb z5fd&2(;md_w{f(Llk;VQ9GH-Ls*wgq7*xyqv4qBP+$$#x5^_V{lrDD2&u;nIAwSLZ zSgY3)G9$t&0p^sQL2FT8JsnJ29%~cD|#9`!no*gGc%CNFGnH3%Xi{ zQIy)V`xkbvYYH;Udh7J&@U1zuXEPQK5JWa&BC^}k^@VnD*-X2wHS4E2Vmd8+YnG(? zHmOb>{Cc|5rn;tw&N6lsSbOI3z^K|Yg<8X}8BI#m!oR?QK{p{7(GXQKK+(coFA3kC zp--_3R%>_XP=GYzhu-k`uCxNz6;u&aHxD*Hnn&_{y9+vj{+Wb=8t(=^Q@WG`@!*KP zslC9R(?*E`@DDEO8Agx76uWnpToi`cb2?HrYA4@sl|48NZ25TR)+cZD>Q#{z&-=6= zU5%at>Kk6N(HpKh@G2zmG}8pvyNtAV*7UcPid$iFF+2xgH`3~nh{3?nr$t-|4lWwV z*`}W_eP?lj>Wp*S;yje#4&Yf3__#Tabh+3XsDia&a+^22q?JnEe-+|4mc03iKA_5d zUy8jIr|kf=0hB~)6LvL3IOTTfZ}0}Gg@qH{JL@w#nRy*AVm3Kbxjmd;(w(4D*Fh%G z_vfJaznwC950n@PEOMqYa+%q%<(#&iayzk?M$s(|@X>f%HnCP<@~7s1C6}T}$XN;f z*cKEW{Xa&zyE-jVncg3gnOmCB!9u0V!t1mbPvo4E1F3?Xa$X*=Vg|%7>7uWdAJ)%n zFP>{U=4krRoR{ea_F%HUz9F%*c92(tbmXqH%XFtXbS~_i~iO{#v z?o(<=PU)vgcj>1&o4zd_JySElT1Dq;D)ce-hrUdg9F^o%}cHdyV8X; zY_D87tS_s0phe$@t%AIPK`#AGl4+~tDKkEXt*E_t(uGAmhDH_suD;7%`l)`}ofBPj zH4I?R=;FZ&UvoKIv1+{dTfSLu`ZCbj$4tR`67R*}F3R>9`NcvEEI%ohkM+qBuO4j0 zHrxnUQ;Hf+%2V!^zfuKE5W$F=KVddH0MU)M{^m=+^XU9s-jr;H=QXFNb@6-5dqK(_ z0eny9Tic+`X=<`>l+`~cbS6D;9ls!p4^gnTVM2P-5r?)N)1wcDRKS!>kssY*+u=OE zG=PPYS=+P??s3`ixo&O4xO9+PSmj%3eqah1Eq9jfsT!goq`LH0kF%-930`H}eFlK0 zR6*o_?>kCF)U zXj(~|R&oz=TY@lv5NK1cxO*-Xtm?hm)IRr#YuY|gAkczdpHS0!Zd<*gIG$(B!5M<@ zuMslS)IFquJOnLj=sVi-W74b=K{DO~cW`9QzDHeYr&85H3IO#$xy1pvpi$;Q)7Ma5 zt$xT2U1}y11kq_OpJ;D!mnb)FRZ-w+!S^Y431vI7dP=b<%%G0ystCU@-j=iw-J+FD zU&FVV?ujmj>h1KgmVno_5*Zw<23$X=jqXl<9SuVps&7HD_z1I3J?u9tb;Migj*n!7Cu9ev&5I`uZKaD_7q zf!A8SkVlYrE&+?1*3LBWiP75ATFEJ##~F~m=s}vwfKB~`&VNkXB`G3RQ%{=V)yEi( zDp>`e5kao+e{x*AmBinFpD%yaAILJ{R4R$mscB-5Te3=V=o?YFIuf}^EmgEr(vj3E zj#DbiVXS6IbEbVv+Z@x`MMLA-@^|Se<(-wVE3UuWTqLK=g1GbJpa z;qNf#+SL8%4SrsQA0YgoHuW8J+cM+5K2%Hi7;WwK5m>D@_3sSG=a7lj!YcWadd?F$ z^-IQxL0>Opw3UTt>0JJbxa!kpg3=|m#O4(io9k3R6un6iD?IC2BDtRDdopsh;;dTJ zQ>~4)W*zHf@h0Y|VTM4<{?;<-L4WdcTjV!&dB_^*vhHKz>+Eln|BUk~bRN1sLLt09 z$m)PZ690A*BCLVk7Th(um0CUeF@fjWYF0F7Q^#(ZG{-pPM{{e%<$o|!SScUg3|A6d zM#EI#qzwe_27h@_d!%{fq2M@t@Mma4+q8$9%l3lM_%zR(B8bSt$0H7oN=nLn1SM<{ z<30y3B99WVmG)NBay3**yIqjd7oL!>Fe3%u zttQXf6P`yKNBt81>sKImz}i;NIDwBqOasRede1=2K-`hP@DuN>)1=(Q-%BEI?wA0^ zf=pb?$MkmLr|$4f*bI&>xsLV^OtgQXYSTh#|3DhmP1--u2)5}9t@d|Q`s_JBT%{-d zwsRimF>UoPm`qc4BYHbdpZ=~I?gu6OqJt+nL2~}A>^5qA%@r8R9XG06Ygk4vYHJX- zU0hp}o)%nCTa%F%oV$@Z!#K>S34w}Hf#45Tj_8NttwMx@ViHEl)isc8%M4-#?D^6Z&|EJ2|E9IWcvurx2ZYG{IqHm(9D0YUQGPO;i;u2cKm z8MF6Emk=+CUMri$>WN1ypQ0{GDh~Kk$a&|-Ts##c~kS~|BLkq*S4e|Q` z$m>8Cm&%6a_=PkW|4&&Tu!ME;Ah2)>N&MkEY*xM#8f>gmI+>bc*{-^#2>ny}#}a;% ziU_@TqpNO5t%^9LHC$yDoF1!|Ik4b|OLpytUslaJ;HvACd!W|vTYjs;w`EvW9oNDq z2m#sqA;{kQ98<5+o$lkp!*X49+tP~He>s*5*g&^_qAA|1u30c{SoE|8!FIWNEw12} zPE>B_=#6zdYZI~Sad3;8RzaD6&vu25(bP92*2NWH!32-(HR%Qf&G(6W&5V0nt6#>v z#eY;4o}X?t@Z^%)Y5Uvc4OFS7G{rOPcDCcE9l`5WEzn>YrO**d+HRI~FjyqTk8oWu zO2JDQ`i$6h)RAJiIyuA1^Jo%O-_QLCTK%>o=*vr#*%!A} z=4SMRBIR~Cm><7e0;PmRSYRs4b3oVqQ_Md)_iOdPVmQ3U9S(8@@H^kt00Y%o1@zJ|fS*RTyz(uIDQ_$ia$INjLH6Pwn*o$q8+hrO~og!90E5y}?2=lnv>)gdWZiY=#YzXF@5f1l2 zH?HHSPH`_MJ=`2x-dMX==jP{SMu9Lg2ufhcqBl4{qlegkyWfEkqPK+WzM(T1t3u_^ zD3KNzA+4%1Fe!tP!smARZ7iVYP74TheipdNr{9NOhw*kFL@MW7fzduj)34ubpWZA5 zT<$aSsJDn5J0v4HzMaT19mC0U%ceK`Z4nSJ2aj(_=dJ@EoDNl@Y!|+(TnkOwqa+mr?+K{x!aW4E%X4)zk%0rMGnd7 zFlP?Zh)e?b<6H6WV1}aC#07tk-r+AjO(86;(>^CF?55LIjAUYI$+nTDb%v!S_R(Z% ziAl70uD{~vEG=)Sf~7@>!)s))ic7p1W9%47V`;sijD6|^Nk7d)rfd8X{iCKk%lsKX zr-Qf9!Ci?Cs!c`*=hMNHDIK)-*ZD*TPkWOcJZ*JwzjP3fbdd3*gNeQ4bDlEy&iSu( zQ1(u;gY#7f=cjaV{)HX};KB}GWFMV5++|Ipac@~w#uC6umD$rr)l|Ob zv%MDVS7pqmr|$=Q`dU2ZRK9FNk}v1q;4^|u4FzY#2{9 zp#(|~f~-2`@`wTx9wkND51VlX@g7wbvkMkp;tMU|Kmkz9MjNmPq0}m%glce!)mu~z zu(7I2m=vvE9@>=krc2RI40LVPLZ(LB+;Tr4)Om2hy&|e|3j1Gv8=4!idH!BTpcneN za<6U7MX4x!snE8NPZINA!!QAbzbS+G{A(L0m4AgEuWM~MASO<_7VBRqcfpZ?(Mqyn zZ}nvGO>=2=;V>fAh4Ju{F?n4#twps%eQt*m=50#YB!y8|M4irvsMDVKPpoR^CTx@L zS;OU~H&>ThBXlZ?snUdzVSr^AZ-3uu2J=C}C|9)-GW)C2X#Qb#T|q z^M*dE1vgXnNcRM8V5qvw=qQtk^Quoz~p$9V4wEAp-AV;K6=Wuz;ZTuuZUf5N>N812X`Br$A zdAHN*4}$*XkdD z=WUxsT=zS7%(r8ISH$12I=o|^Lw*Y6rzjStiZ#kOuNgT%n(CQ{{aPCVF26w9;S{PI zu7tyvaNL@3Oieg`kZ}Ans5{+9Ayc|sD#6ta7;=#W+ogzO*j@M9Q6qY zGHhn0k0l)PxT2aNFD}XvNjP4^p+-qtb^xa}K3eEKJmwf_%f#I^PTI1MC7l{8ZCMO= zOahWamNMm9sFNLWLBOet13ona(DGys9-N!(zm>Vgmd$8sZs=_l<}bFT2DVv&>8XLj zqvcxZFV08}+-L=6rUpJ^1!koNuD1fSQv)Be0=uOKHd=x9)WF47U`}e_Tr049YM@B{ zNjon|4HR}<3Cv9m6owxO?2#H+X$AI74IE|#YF3~*ub~oxzqSLkWdjH{*R~cC_cyF> zEtD@2DQmnsvF$HqUgiywpHlfLmmg<*KKND2bkBb`)$>-WXBQr1Ce)nIA63OO4hM83 zaEZMFUntV02b5S^X(~e&3b31FW7USKFL5QuELG(U!^Mf^rxMhul$A$iF2V zze_lt#bGV{4LG|NeiNR=!WVbS!rv+BtcBl-J0=0C@J`TjYASJRtw!`1({~lt$Fnk_zm-VBf*Jz756vJ>u`xknb%7a^J-3{j45Dx z$2_0>+$uj)`O!kZfk7#LXL9(>`RE`?mcmyAjGh_a9sWE>@iP1tg zP=Un8hT~B9ENTU0avSl-42g9+Y`i^hPKN>}2S{j>%2GB)Fdh}K!3?;nE8saZz|$4* z2Qxr09W#sc3xkwHy8<4$IK_izz=*CCE6f1S8LNz%lmImWoO1ICi!IKkI!Ed@ix`F+A+MqIXCfa~cA8L%AFnkX)sGAI zLrC@GjW}F74;qdu0w61p{CT~~TPH;+F~UmXD`6RS6Ts_0Oe^_#x?0#TX3QQMH&q2dZk!wzpFr-#slKBY3As+G6NrIrB6EWiridZUXFz4 zN(_0R@Nepy{0UDIV#h;16T<>x>0`*w=p>)atRrg%H87%Cy?CN$+_J{wkA^@@c(-bh zI<#$(Bj};pCMyA30G80!9&H-0Nr$wl!qZMY)je*d01q;exOXbmgSQ^Udzfd2!+iJf z_kJ+_=T}i@kJS^Ut{KA1)^`s-Vr@kI^>9$g9JhL|qW<@!!mQ6FwMXmw5pQFEQ*Typ zLs6W15bq8a#nhcRK9*O<7%vY;wP_#V|45q_*GfLurhZQIM0eJCwMeJ3P^)>RyA{Qg zLn*ve^^j&=O|vdPH9yiu1n^>rHc8h?KytCCreCs)O~@6!4n zk#>-XziDw2eZeF>&z2|nZD}X`V$*h7q636|B4IHcGE>ZuH28015zz>8XAP&@#4@K- zIwU#YsPyuSLSEx!w&_`iR-#_x&d~g;4yfi&{eUhaL!chv&f(^z?k%lEUi79O(@ID= z^(!;99Vbtj)gMy&+mwEQKDDX#PT|pMO1qZ7kNqhE0h0H){HSM3acm*qRvA9oP~pel zX5F%Ul@fSUQO)l=BuI1oF%9U%_hC!_`{SZsFI7>^PepT3adEUSBxVM_K}L+PDK?Vd zWC=q^$DfpuacKEZN|PiYEUmqguJ;)|o=44Xhmg~_e$YB;Q<2tZr&jW;43h#-uXK=9 z>-(kFt6kD;9-)==$YiZ)Q+ElREO}cdmc=YCn{ASV-7z(5f*P!K3L>t)x%0nlT>ftM{>Cw&H{Rkw@QlUV$+lq2=!vag+SF z#iT@^Ei7-0>1e$UNmSo9tydGfc&0SCRK0-mLR-rl+ZvbgYCdCzfhCEw6!CQSxnWm2 zL;j*@-r95o5!&c*IC`UV_%XHcED|0ip|~oq)*xUBr8G0{gi*W}H?N?v^)g4BeuVD= zu+`v0$kQ0^y%SJn1619J15ovs*$PzEQH2Etlb}tjA0&dIPJT?iJ`Pi_PcN!G$FqY{ zO21niq8aBr4m9J8nJqlqVi5KV2?+Peo^DH3@N=u%Qt&fZMnx(3ks+{bO2MoSgi-@0 zCIH4xVyB_ni!N;f>ZbiA3(0SEDBVOM(Fy#R!;%jUYMGHOr6m)jNtOYxlqP#6SoG)* z5QjAR7HOf;W6h5eO}b%+RPUm?HWScES2tFHeM#|{!dR5rQQZB*^6(i4YQ5q(b{ULwsTwBS-@ zikVL(%bw3f^E21{%;dxATFZwrX|Pg$V482HQkYD#u9Cfzr%XY&G)y94ClR1t`oSta zN|)|Y5?ktIC)pw?&`<@6m9U9SfvO5w;7Z(OL~A8VMFK)isx7DplRz=!ccE@7c{Pk) zjr5UEZ`r0^{%kaW`epj8;bC)E7w~h}5aD!Sr_$rl|E2(veGbmdxis#?yjuyLS&Y|9qC9C=>w-z~pE0L%c#^$fPGdtE5mlU& z@it}9n2WQQ?3EIF#VJixFq(bmIbM2Q4W}{8HbdWD*i#$2V_^=yR&D6P1=&`zzMDnM zfieqS1rQ0V4P-J0!MnMh{xjvCrx;|p<_ICHzgMXGdbmU8%8{n3EDg8gi+>yOibwr` z01S<**sKTevD73M3}Tw;Ys1INkR&`|*RmKSgvFqeCEB1itY5!pjaQ%kD1Yl^Mte5| zn05vvYdu)WahBO){9eQJytT$W>!Am#1*Z0UiP@nN03>`$|7dCLV;j-4e)RGw>7`LQ zQ+nEsJtTf}$%&E|uTjty{W3ONmrMv$bbwF9z(HaD8f< zPUh^F5JWE)+q2;h)i!nJyoG+7ZKYs2gGGd_&%SXAjoHoHzTG~jDFV~|R9O$i@8ayv zYpA6`gbDJL7p}*X(2>KsoeXL%sW}q?U7L5vM(OiCO4KIp)+V=VxAW|H&qm3)R&epS zXU-a4Y%$6ep?Ia^Ho_ax(z%dqfncv0Rc)voc9}*C=d(RUsaeepfIpOk`a)mM(CYs; z#dyP6GdY+%+N+~76+6!P=yGVSvNmMhV?pX&+EKL+#VO&*ufYx6<_~+G_lK+gGy2Ep z&I1*R}kFv(9qVu|vfo7tlK<{B-E-{+>V|?!rp{ z{R!-4#4&3&YQ;Ug#2Dk}iuNO~k!e$01U$ltu9XejwGENdCTkp7`Wu;y+Evm4W1`n< zYV| z{X&s+gd`XAZW+hhD_0$33cUPN@*(bL;?57>WYb|_wR&Lq)ShF>cs9CiNVY!L zoZ*R}QIWv6UACP!g$vg~=7z839Q?P2<_^ziw`|MIW3y=C+d6OBmRT-N330;=ObdlM zm(iwonfgI@!^LamsDilzWXi8ES#Q3-N}Jso(_tYw4Xg8<$x=_`h*MTqV19>vR?hMZ}^eHp;Gy3E4I*n8XtT|`ZZ;a006dv}!VQ5KDUBC=ze)rI3nm$m5 zmM-;f%;zna=1=M_!{Km+r**eA<`-CDG8`%_JsC#x^BtaW?lqE@Cps(bHJQS2VsGSm zLzQ=p&B}WX!<$GWD@V$^hT%bn)aO5j&0EzmCkzfLCLS12vm9XlMm-Fjh2e}mWf5_kkqeGP}z9C&n#U$x8K6_5_4!Rry|-%Lm*hico!-Je#LnK-6`BCd0#4EO$a{%0kx_ybx##U*z~QJkI|qK>kUugYiW3mU$SuPdZkUG z!=wLnoqT%Ctd^^TQoMlvYAM05pN)QS{CuZ>*0Yie)i*X@%UHY0t$*m%-*I*37mEo; zxIXk5n2`vMlGpAL$x6K8wN@onW+msQq*fyBi-xEnRwpge)m>U&*Gh1*44D1SFGc5Y z;0jmWSLXp0R?Dlj317zZy!yI`N-mkCWpwB>$jo%;!LQXN9m=OVqe1exOWR$}t%zuB z^*-v_mYIo2j2a!EKGtsfjjW+l%y~d$IoL-8QR=qH#HTEh=$YZ5A*MWn#a{=?IJugi zs+n3zlQwO0IcmQSYeSnwU~)fA1TSvah8|V2E)q6_68+)n4!^OYmT0%}pq#Lt@clNo zzDb-wxNZ}|`Sx-8DEox)Fgtq4XHw)Ca7_D5^~>r18$ln92cMS`6r;pv`u-$D!2XTf zIVuz=O6;G{Fm)5xJB-YbjyUdV&9mFNBcR2-k75ee6_g!oMU7TMwrM@GW7tbl* z6I;u9s_%E}qbSpBtXNM|(CW~V*@}bZyy8*$fb3!oC%elU4#sanP0AXMcdlR0^Xn5A zC0E%alEr6?nRpAEx0>7Q9hIuTUYz~;o8{o=?jnBXmk|-LkL35K_7Z-7WG_%ju?>OE z9W^>`;}0@CCa2lwslzje@p0ycy3ixnsn5H{C!VRd>ieCoZq0qnZ){jk3AY%T$D~D& zv^S-(Bz?vPA;5wSJ}woY1ZU`>c~@yecm5S$+Po`>SpA>4pzqz{*Fw)on!_HWzw2c4 z++co8gowKQTK(_w8Y?3F)*lZ1qVwnmEL7slHOlXse30|*nh!7p`Nj*bbRK0j5 zxg17BTnb|CxNVX!kpHULYPHtzeIT~WNUu=kYYifJW(~v1<ne!Ux=P*E zlF$xtw&t0EJ0YO{469*tw@(y9Rh6LPpxAS?1v}7|e7Otn9PQnmK4XRSlby9*M)~*{ z&GA$FL`bWqs~QcZ+MSDu>yFUt8dt^mTy6Or{Afd*nyb~{ zi_Jc)pHlCbAhF-z0+lB zaVH>Eg?pe}C|uy+@sgIYxl*+7(_#q)5~XmY6-f}qoBa60&O2|X*Mh`BQXW_<4bTsn29SJ4PZ26G0=vZmQjXptc)338Wqq4+ z6#l0%ZFg?$4ziY?RHX>X$ay$-nTO33R#Uxl#S>gO({IH5*yQ%8Idf_$Fmz=hUc z-0XD^rpT*|1$RNG-l@_%NPpa;IcDU_@0@%gw%uMRzhjGCmRS9T$+%jlKJIF`tW zBEg27lIMV$R&yY$hqpR~W>MXv@j9UblzfEj(p6gslevKIu7;{a4b=rhUjH422{V>5 zzJcPqEVjk3-N!kPR|G~T2Ghc4jm#D5_u$C%wc|$VP=fwwd=6#l<99rzs1X>4CZv6 zZ%X7fEitSzY>DAk13x+PbS*<=ioDfF7WIEx4%48YX%$K#NYj)(u_xI!;8$`6&0#S( zt~xf1(?K4SYEyXE8qD8} z^Ai0jJE%Is9|SVCkCLyWe_uwcGR;Y4hp4Gl`^1`1Yc8M4*6R0z%2pZUD-{FZc05cQ zFAYGj9~CbqCw7poWlu4e{^CV^&^I%KPY6*+V*t@2^gVl4WA z5vgc(hi^S)CX>^W2Jkq`Bbre7%q7TL`~<)S?sk0M)2@?a?de*>-ziag;imcY!d)CN zKa0TacS%P?eWwXtEr$9OT->_Pf^TZ|g8C&uz$sA9s6xqkXZM_PB6;%%qJ3~F%{1?{ zUL)TQ6yWk2`z0|+0F8Zv+S5f^{b+S=UR<@DcM8AN>XF0zXZ7*aWz{D}e1SUoWl~?6 zJtj04 zuMIxH>U$65O~OxU8%~-49udzO@0iO|Es3LSxf#52Bo=DvelzmC&?ga89*jbvl7q5% zL=IX6qV3J{1VQP@eu@lp2_|8$AGB9(;4^Ym2-NzUn)_vc<3eZ-;a*(H#g8=U8aY3r ze)^AN4GYbo7MeKJx&zPw)vV)0LOtLG<11q;Dd>Vx`klNyq>hrd;jDQgekf|$8Jg`f z1UpU8EM01oa5YMVANDqVR7U@i!kw{KdLqVgkNaBmFVfQW0n}Y z9>PcM>E4WPCpYsjCM>HbH?!%=WD5G8I=MMBkgesFk;!eKHbpcoBr-y_PG4XlK|f8cdKJLc0>DGELM}6Qu&xL|)fb zmJBVg9Fd%`s5B|@8lrn9bLB+=xfcfL!!GoHEyu+PC~^dU@$0u!v#KtAx`vBI9fhpw{qLB4fgbL<-pv-&jJ68DDZi3D!tz4S$j%{+v?8 zMAeAu3mPHyPz6|6-l9Ys7*ws{50cyBi^5d#_5E&DxqZUn-qnTY&D?{?eLuOIqz5YZ zW0L#V{H(G5S^Vvg|AfNOQ-q^_9NH+jKw4miF(os+tn)Wtc4E_Y{%oV42!#z~s_M?# z(lXKl_tc$D=c_dcx%Yn6XoMHY6@X;v&&oBxY}F1wE;tyHs#Bx)zrXro35Z zj!ZMhrIO>fW?5G!%ZmRgbT)$?o=rDf)`Ef;d*zF^(3#97f5i@F_|eXH>FK%j`InOr zrA=qjwZ|frMcXQC&)b4mQq2eoi}yv)?XFObLlFA2={3Cx|6#KFSQ#Llb<41QD#MmO zPi#IE8B>at>@UI3C4>7Q{HDa7nkl57LSmvu0rzjjeTG@W{RAh7H5&TnSe(G*ags_u zllLH*pD&poW84sgj1l;0@$92S-osivZQ^jfB){?EWDy?yAY$oCz?sHu^NOuLF2EEL zTJl=j((KUEA!&hQ%q$Ay8n!RBhnD7Y*gw?FW0FG@-4TEcduT7@w{iI>0g(X6{{$FQ zN{EL{SK>Uh)b6$&Dm^56-;kK(^+J-BnaTPYcaGv2wPFTlhSqKkou&G2RDWe(?YTY1 zotdF?odNrmGcvX34%-g#7GnAB%8$Yrt^N#CeI=BI=eE1Z9k|4OHA0v2z!tfRZAe)*3_(Cf0)=Q*1RM$9&QIwbJkNxmQ1DjLp)qL?F?_jcPlcG{C%YakrH0DqaEkKYV>X5`~as*Nkm;8`l|^Yt`>mN*D$1% zibnYjppmI_jTaa1w4J{aN4#*8P3`e`E^GM{*0QV;^%DSaTaFm1{|hzMSe{pib~qUDs}dbI~>{vtnI zn4^P8L4!vTyWqlDdBx5!V+RmBN5#^*w7_ReOzG zKK^h>nBvI4nfzoD5ib~BfnXt(TdPl}i5??cZt-@PvrP;C8v$-ZWv^3%D$^i$e#R>6 zFvU<>Hsj;f+#P^`ZZeRO==IP`V(rqa@bGszYl*R&F-644WQ~NrsU{0NnKp^g;P_Yu zmAUFh6-ylyzm=R65S5Qvyj93y9x9}e6KpG91M;0%HFh%ccbg??^+JDlg`Bg5xXHnTO}lFZ#cMb(|~2H++WF} zM$yw#wm|omknjbPG3}}JAc{OjPoU`N9|>b{d$~f3i}TYYRe_c2q-UR9{Dqd^6}lm# z=8_=H1)d1|Xz`7zn(v^YuB)1t(u*>r^Z_r~#9<5ep_(tqZe?qE1Sb z=rxx8JTe@}ovXuHpe-B?^^5`G%2~OQ;b7Z8&p@a?} zJSKI~-VND9)?4UQV$nAAXQVB#!Fs=$1nQtn)nv6XoznDBF%6(GbEPrM7(Cj4wQ9(@ z)P@k+bM%+eie4LS6KO@(#&kYEfepTRgbc|}?4QYWJWp9kZoNi)!c%_Af#>$lK*%Jl}eQyF*AHueQ z=w0mmU$RdV?YvyFeY?C<#jKYYT6tx%l@cC%7W#%b5C*TOy=h7Mu;3#T+2VVLv2{lR zK>n3j6!vVA)_OBW_peA#i?03xx9rH3M7nB4sixqP96F(mc4}nHJd9sW>E+8v()qCL znb4AbSXp|PnDWD^qr-W*DT6aXo>(95+KyYL1ib~Gtmyg61dzj{Nf0_#DyHvS{6_2% zRgTqdMK`0P{mn(1F2F!&1_V`^L4sw*Y6wzQQBkD~4P!M^aW1KPwo#7+-N^AStLJh2 zy!giZuW_C9q`e!sLmd$3Rk&aPB+d8h(edFS-}XA21BYsV-dPmb7e7c|Dws(HdUKrZ zI{f%ese9*YXfhTK$pejb=BtGhk6P~wUG#c2A3psHrT{Ty8H_@AS?;Nct|gF+G+i^TK&t*|o8z$G)O{ z$>Oa2q}J(`R*B!y&n4Qlh_x1tA2(;WUqVMg=lbeoJqoRPN@|dmqV&j!8UxYklDx00 z@^_Y-JuYGECMxJB6|A%>C;;A=*MZLUcU@FNk*lkQ==GGHsL0f(5ISG8uzO|Wh>bG% z)0^RjgI>R|Q157pX6Hs4yCDO6V`c3dwUvzvZ5t`sOw&o45=oPr+un#qY7!UUm|NR8 z&X$rseIrZEO7BQpI82iD%Iz@I=UVAg@}{Tc&DaQVAg`Lj#ClTe@pg2loR8}L1O-`S zQ)?#rMIUo%$woaHy+h*k0}8Z!ZN9Xjkq2_Mq1zwGJfTf*f{eoJg%a6j|4aF)@ppbh zyu4q^>(v3;=MsIWYl~oYha04X4?HDh>JuKx$6`{c#0itiMDhr>S^?BuaKJcS_=eh(xN;_|pXar(FxB_^?4Gu}5H?+wo?1A>z&??T2@{U)gQZ+pW~8=73?J1~1*T5L6V2}y6h zppY_r@p;~jMfc3U=iYhuGi(jVDBinqLNHKt@0_B02#}t|CYrggzo4SRWJTN5_@u=| z0GL`vnYElTn|j3sxw<8D#m)$&(UEo41qrld0#w~n#5@`HH_W8}uq<dXp_{{0YH%nj?A5*G8n;(K-OwJh7G^N=0M`i$3DJ?mG5vI^eDOwke^21Ye@x3L`B@Jte_-wHZS=OL-GUM zeq-1HP*cHg(gJ&Z;TIz~B+utS0_0DP(G{eM?tpjH+mdyql+3@DtWwKGg29y}c-l;G z8E!K{f#r%`c{9VZJSdqyXaCASGQF-gYo|yaV6`smRlL#JQid`5Pi9n*V}JDDZzTL* z;$#V5{ugo+v{Qm^S3#rS757KD)wFXSMxWDcU&k*)qnAj{f$ZtAFiR{IK~VILy3TY* zR$AaP{cK&QO-(7cvIKrm!4YfH9H+~8LVm_4nV<7tKPQEB^R}oF3yprcR5~D6$elOP=j9(! zG|b}=85~__JHU0p6&Y!U%iiMRIxLkR4jg4+gC;cPdUd40MQ2h9dSu}H z_W$5;zwsM34a9}R0dyorLhZXkHH#n(dLq~ea_N!juFj*4t5hj*yOo*Kc6%~k5M91q z^r_n>qONA%B>hBqOkL>opm{gDL{$)~i5}$YPtUgp`oc=rzRML_x+o3RTC8dP9es1> zZitDZ$ZC0+N)`#g2}g{Hf69T-*`9P;_8DNQ;{r^fkw6CVsUx^1biUg{rwkH2{zp`o z(8mS|H^=pSMyMu`7R+$zZF+~hvA$caOo_2V7#k^SdLX^C-Ruufe~&~NVJx+`iRB0J zcbNX&h^G!jCT4}i^Lj%LNx4(as2oRO56?YP+( zRuRnxX<_!^VqPw>HgDazN&tVL2MbBE16}R$EdWwGh(%hUHZLzY(~!Q zp&}pUV@8TAdcPT2%QFi(pW88^|4d((_VdP>UNVaClg;|`i}cbRc7m~nH>xW7qU_`y$9lJFuG*yC}Pq^g#P z&s0QJBPa1%Gjxqt_dH4+tCZKXo<<*0iBR(!&RT4SK2PWeX6Qyj?=eFogvQKJd7_v> zs32wwdD1}FZ&C08SMAcGA;I~u$7J(29yV2DjY@CH+=gn!XRNzRrX$omh(az^MZX)p z#w?^};c!T2wWBhI1c!JHB^^wqDx0nheItCpyF_#h4{Sc$JG}MTVg`jm^;U1E@)CQv zgVK$xpME!S%fjf`B7hVKFVd0hjua;TzX+tF6SU+P??Zxpg>pe*B1wQP_$rY2K4OJ_ zw2}luYOC9WfG zqavh*kuA_^!_BpoQ5hLW1;=G@neX5zi>zTyz-7XspeW!nc-uINsDPsM`#Gm>ci#?S zzQ5n|y#Kv#o~LuG&Z#XhDTd0HDDt-eWsQ>Rd72H~H24%2My)Ts$bw*R5?6%zJ9 zE>cLf2O_5twNoP=$Ylz7$OGYi31MnHkQ)^;%>%hzA(jVnpF(Oqkg*DpKA=3q;bet8 z?19uMBerXKYAelP{?B*$O45t?tv^<$P*q&y+USq zApcT`?35?c+M*EI*-k)qC`9gsBp_cZM7HG}o6*Be2@3p5|9G#l;A$cB-ct*8IW62&{Ihr#aT#?_va2JJr)HYwmP0 z0;`?sX`VGVyBLAhPW3d?nrmE)z-p&@nrqGFE=FLrQ$5YLrqsm< zsEN54fz@hvNOY=Cz{Ga)&uKj9J+hGXRqFl?g2|ZU*&>)Cs~GlsACarsL2Dv6>i)ucT1(T-56>>d_SHEf@7C7gd=;Ep<`jUDP!x)Cw1MlZ#^8Gr{~C7gge-PD`QI zxu`Q;6e3w7*1Ims@1p*L-z|*|F6xIaCd(UBsP|pehc0SC3iY9jTIQmp&A9|WaZ%5^ zs3|Gbr!H!mqAKrlWBl6%-*3QIy5JaK9!zL-t}*a_E?k&bc)-9(#Zkx&AL$U0u6ZzwzpN+F$*V8(3G+{%R>*39M^ols;Xru9E%LZ@XSy z7yGN%x?WugbMM0S>e|If-a?P4d+#p?RjU;R(lt1DW6wX|^wtZP_a(sRAKYV}t? zj~7jq-_v%AEl$p2G6Lb!qdm3QD<1lpvHK*ROgv2wh$~B6zZX}wxNZ?wYjKSb7cx9b zm*UD1SFyN`5Z49bVwLP`;_mpWg>A)ky13X0K!HnK?ZkDoxLD)+nsUU&f}VlJVUyQ~ zCG4KTY^f`jM~wsPt9yzYgs<$@J`Nuou8V)|)*$>UT-cZhQw?PxtK07iFNv!O{Lb8N>SHT{IbLw9W?S`~$+5 zA{qqI*&ynilR*gefAVUYup^r}@(e-qr_!p4V9pcG{;fSZ$9W8ncy7I;V;M&`rwYe1 zDFY#yAy8@L>G}{Rel)ZjdnZ3Qmok*?@t;|;n{C1Te4_5bMhk%QA8@P}GC`V~OGfuc z_59dA#+)CYYVARb^k1Synl<(eCV=6RvBUwKedC0t3t7$?r8|~$xu2u&*^>p@UGJ;r zfZ5@8e*6q@zJ24Urab$`%BHsVjpLeH+q3qQk>tt7^fwUv<>KZpems91XJx4G9S1l) zjb{IRv|(r@dZu2U?vTBcXuH4A29s>bx#4nQIny6Lkp^2_IA#KUj&{uX--KZF4M$2a z`nc|fQUWin#%sw-PO4>6U++2CS4aW;lc^b--A()0W@ur$gG1ICb%g^*`K!kO5N~8E z>AfXl0e51v-#tq1#NOBLeZEZ{*e_=*Juf)rr!Pt0U;Qd^zV}bMzU_B};jTbs<4FIc zji{3Dy=Na`xjOPXQJY+ntsz;UsnV~jo!{YE+ z11nWyA;E+-aGd&`mzJwoRm5YNk-Y=_Gu9TqAziI^aD&^bi>*DP z&WoN^$qsGr9|=YjHDf;@8dfdd3K6*MtGs+@JREn!|3}7iiF&pfPq});3=VOwQqL#G zbDes&8_zB3*#o@A{=?oyakQL=I(w<=7>>8i!1jw079Ogw}$RTUYgF!2D> z8uM3Q3M?kZy4QxK5J8yYi>y$Xcz~Hwj4V=^cz~J0jK~F!wPEo9Gi4ePGqr2O;sIug zHu9pv!~@KfaO7Eqi3gY|=*W`_6Av&`-fH1?ZCE_OOtD8S4JRI8rt~B7B+^W+stn(?Wlq^%{}~*=Q+{rPVLO#&JpWZOFVIUON?9r$mGnCWvhx+$?~m;z4j69 zC$^V1;yLdYIi4cjdZcF2UmYbi4nwn-vjsc$E?jjF{!XNyQ}D|n>Db}?Z}*&F%W$oG z@Dl!S7&`z-f&@iUYG3X+8S`}zJ#ET(!|D)7C@w70gLoVQhE;|TVUCxIq6NNp88KyZ zdoE_r1sMDJLyvZ`3)=;ZLD(gjSMIp*Ws0_e)lIxYB=z@QLgsfC1GMJYm2nFPY z?O!M#kqY_eocygwGOJ4Mh?6O<%u?$a_n= zgOoj^adMdT)0Oa2bi48*TC}p+A;;%pC+r1r+p~5{`WPEJqiIkoOVfERX()J+wyr*Z8nq+~`atmA@27RYai~6@x)H3=|)Mt8&`r|EhXBuz8zt<d{wcq7RV<({(B#!^4u)_11P~rj)i}!g)(0t zpY-^T96tVHPkhyWuolQCJ^qlx#~<&Bui6pT0{NuJKkD%D`+4H4Hi)%AKI!q_d;jqH z&raoEwJ)p%@=1^XNQ?1lFH7CnCtE*<<9RyzKi=Rb6pTeUo?0O7$Nz{-Y)lAZ@OT_$ob#A4}qN19x?}g%IJ*A>7^N29!E*><4)@P)F+5Y3uqa?tsI%|Y`1gNOeOnTPWK(poS7w{L1*-opg) ze^TDc|GpbiwZe`o#|DwEU8E3S>=`U=;rdr0*jqp|GE$LSUazP6v-Nn-^xNNo8 zxm>v%9Id`kc4~rttOFg=8a(=7@bhG=Lrd=WY5bgZ2tOzOAM>-KLP|~eY54E_&|llZ ziHvq92XFmX^A5=Kjc?}ELBi8tD`JmtkTGA|gO1m4XB?iwNkRK*rO<>Ks#KpJ3JGr! zp$V1R4;t^%WBi?|YF*Ys`J|6OM?oRc-BROsQhq@PUUpJB?w{Busp&$wp8{1;lE=HoM;-7IjRxzl_a`Jl106darx!@FV>5kDI4sv7Z?bLqb^61%qgEP=8XUn-GA zb7Au~tjSUaJBV3gFO&0@gsvE>`vj6p%~zC*W#|{hn<$^s=#nl~#Nph3C?uT;yR@tN zOQ)><5ip1J@zA!Wk8>bC1j6g4uM zYBQscbtx&A%~=o=k>qv%tbTF<7vjb)cjHb^`@cT zL2$4@E^J|b>DGLH`R;1%T1WTZHW=|lw)K7f8LMOfJ*>cC zDC3`BJ?*c=$NqCGw4aV*n>~Z~mU(H^Zx*Ef`AeHcXFoSSFTuuz}V(6sX=fsc3G7|Lle_(WXZe z<8O+QM(hA(L0|L@Un(UBimg)xw7 z(Qc^vDbqY{?w>zxyRb4JYfb5N*9%n*98kNARTt^OU8()3W^LJYnL}KoL;m^Gg$g;z z{iAJq!)NQHWVXabw%D)v-?PQGQA^pn7#=kF&dsvL zuA8T}Bz7WOyA_j`tzT%h&Z;#gTg%1sC-F$OIw!Ja7&v^kIw!LwF0v)65P>C#x0<;^ zZHf`~(G>p&N3tna?ZGF8@>7in_C{pi1}9DBsTTOX1k;y4q;E;2M4o<7Oj@40NjAP+ zZSvGa_iV?;W=MePccPN$j}$)I1- z2qfsY`t=6wRU8&36<;}Q5J)qp#!+U=#WRrEp zPZ#a+U*GO)TDrhC1b?$Wc*M$J_f4hNvr;awicx+nGbA&SfW-E%M1+eLJcrz1w#Iqt z(*%xv0xFfe#Czhq46m-;#rUXwJP*ND)(zF4n(vL_k-$h7biS_og;rhi6d$NsG< zPw$@@I?+G>vJ4t!@4CVvy(YJ7$`ikM@+M_3l=-rw4BHR4cG9i~mZ)+@z-m|&sL~m&n z6GHdCjfv>8!tucny5&2tPk~DRuy>q$A4Q{qcSU&X>z(>6It~$JW4-rA9xuBbB?PmW zDzm=oy^fbgoNpe-08FUS9>#<9JDi=IlQcbRZ4&*b@8s%Ibr<-A#UQ4jA69p%y`;;2 zVKmxjji~WBIcNryTGa|<*Z$_xTTQ?+YeK_*GIte(xcJp5ErKJ4NwF!y6K74p?MH+> zR=tJ=?PZ2)hj)eN-gGE;eP34CzfC0%0M+q&`pVFEO=kw>LDc%3L^>F)es@2zlH4WV z^MQi7_8Cm-iw6Hw-Tr^>kLsy?SO8A-fE+R2AVav0-WEddi29%7~V zlKc5=olhr-Y|td-|0{=zS`Fsa#S#vON!|GGY}kzaK64uWVr#DG0ULEEwQWKPy5|zH z{uE21{*u`a+7~&Sh>!YGjy(yW4Qf?1a&L$CvRCj@Eu{VoDSJ$gePwQO_wCkq z`#$uSz1MxE^})XF{<1w8@2RahzdbmQ%OKYJeINPDHe`HYA7c;B>cQJx|Jc~0{2grG zWo#(62X-j2)|PbVIC0Ouk9e8mgFyI~cwk>+F&AYqhdXsFJcW)ZE{yr7{vIcw$QZr? zh}?yv1Z!kdufP$LwGbDO(c%l*{L*`+W?KeLlS?<>i-{5sER+9DCr_u91grxk@`hLQ z3|GEp568 zPu)_G-0j=qFZsEo&>8(mD7(0D@8}xbp~rBAqQyMVJhf8Tnd&mC8FDMgFnW5Te<;5{ zAIh)4TC5k?gL8_ze|Q*<-;;X!E|9VNFdY9}-2F3agVgH!1jj!K#}6BhBf{}?;n*%N z+~S`)S~#vU1!=0ldqe0qJnUt+ zwg+LjafLlR3+uFv-5Wz~i@UD~zMULM{3 z_xd(c&P+W&#*CcP-kv6NspUtIkeM`n&#dMxnIO%nmzSFNeBf1=Qa;vKp(IBQc9l-b zcE|K%fkLQx_l~?2l|R==fwGA521W{8ZU?z(AjN$#%p=8B#QwcdM0B_O4-=m|_^mm1 z^nms7RXmPXrFYx4F&udO}!)7Bgq}S$Kgl)Mt6X_%Werbw?_0WPU z825CH5tR6RRtnF&62~U>9$8I;chp+oCm42XDyUZI+=s>y{sqt zui5`}^@F0>zi}y@c*s{_|)X!K`y}t0uLlc zXYG^}?d9@uCURMT9H}^Xzj3QLcpGkHE0)|f;+L<4r;vo#f%FvFM-4(afaWzAm78QC z5L+*at<}}Yn;_U9%RU3?1%u@BmL)8357o>E#g@M2ft~ZZ@G6$5>(mrF0cD4+e6#Zu zOvv_{?8dWQAe(~OUu=ZyH{O)Jfu40E2U=P8mWJ14xJ;B5elq%%rh&;$j115%wXGjp zqS3BJ&?@-b9Pj$k8|X;_VS&L4&E>a{U!7IafGBsamU89Af$;LJWXl)gm}o&A4rw^& z(r{=u==4c6q)WETo4S(D@I*SSqUxM}&~7>^$)3{#zbGQ@TStwg@tTycjlTM&Uzi4S z6&<=Px?m^CCTei8#I+Y|4gSNpwFaNZZB_x|O$hXI9=6vBUnP24k-AcU$@cV4_Gk3w zX~DYqHtW&hSB58b^i>woO;>+zxVRKO1QfJ91u?&lG$be8p-e$sAevLwc2)oVEscee zDEpgR;!+ZdIE%EBWucH0ftaLqWqinbn0=H1@DO0}k085vI%9;dlB*rNn+baXnrXD? zssG9FEz2f|Go}F+tnf_2%R(~WIk&49;IH0BD0uw%CArgF@06PG3r+aSn?U8lK1Nru zr4XL$g4Jv&*U~n|8>KTWhX&G1t57E>h>k0b7+USigY9c98lPxwUOFJ4cQ)xa#pv2;OT2*$P}TJaUm z{E-cKNNn4SX^Fi+{MdX@_QcK0vf$k5LSzn1#df7w+gurcTry3?7Lhj@XCN{&!GX!! zjS!Kh--D=b9)%ed)`gGZ(C2AVwioW%TpsyP<^2p^_CGnXwI?<-9akT&BLS&a4rPP| zdUd?A9D;IOn@N3;_(yn2_z%Q#c9RUCQw->G0ezyN(6ah)gG;OFpY`F@3HM7p^WES) zycX+EVQD#%7*_>nzo6p=6q>?H2ZgV%0|FK(|WL`R1xb05$yj=iN_0W zTRo{~^s=_$pYq0D?}q;erOSit2ZjEvxwXNJ4HfZEp>qpk!Kx|oW6r&8_2iCI;%(aA zwz{%-N_^0nx2+B_1!V_bR(uEWm3<8DP#@sq4BYqI+gNwahU%1f)4Nag6HB{>Cg0a3 z4BwX|4Zf>f{{kAI_^))or@P;MRGz>WxL?ctUgds&BoBhGcfWsezuR6g@Sojp4;S9o z{SI=!FH1VPG}S78)A&v2H4=T7!>gC+k}aZm~9w%uYT2&5fHht=9WVC4Ht(+y5Y?C%3yPU2IIUn8do17)cGF z#W4^E;!o-rqXrK{XNhP*`XCt35*s2*SIJnN%HO`fr%TX@(^pr1Q~em;H;Z;U76xR; z`Fu8GN{wo-=JzEuCt#sJhRIEC=|f+lCc*+du}dC1^)E?LSgnLkr#cg(%>yuC47E4Y zfiv?(!XC}gV7*iig?`q+RBXc8+t|DGe_*>|DYOYwV&9r?U^1l-0mT zo%73U(q`y#O~<6l<4Xb3ZCw@Ia>Zy+GBnDhX3L7S|MU`K#ri1>aQT`;+m7jqh*9C*n6D7aE^L zz{h+q@nrKJ-$%w*qrONJACg}t-9Ywpb+&yM767_Cyvy+=nTU+1q$Gk=p>6=rHfY0@ zZoCLu8kC}^7_?zi-v$yii%EmN#h?w}k)TUFD}f$p&?Y01^Iddy0^P-+P0k{xyXe*l zbhbg8tXAiPRvE$_FYKB+x84r1m!{*jUDNQt;8{#*y&o<J2lk(eTrSRbL&9b}Q6TpxS&=eA11c6QND<|~YnvT?>S(2ftKvx@$l z$S`b~2CQG0#~)z`)ThWF`4~sJ?2&B>@+dLOBVN`T5yaLJPHg{N(oqZmb>rV~-rCB$ zA;^1$y+HZ9&*-e5Xr6x5VkiL~nhh5yx(zkaADU~tH{s>P@4koX@Q?IQj*J3;)>@4peaY`rLeVr%IWe1g_9Gx?NRe;+5l=u%0=S^a0D4|hH} z(K%$9@Zg-YOh|Lx;UPUvk?Ri6imTYVwP&$4zL%{mTLb&pku1nq1N+*M9Ch>$*bhlz zS_22j+sl%aH4xWq#pc+NIqJx@BXiY}XGh*tM+ZAnr;d*G11DsV2|LnP{oQy7weEZW z0r}~76@eYjFzI;P zXgFEhO~a9wk_%-v#F#CmkXmuA3NL*@6Wl2AqN5Pnm%gR|jsbT#O8_)2a9&+P14!)O z==%6NOu%qr=dXTQnl@K*1;lE=HoLT>5Q`2F(2s@_;duQ4^zJS zmoQ;{6*t?QdO6uD+!|`rJMLWfoGH>Uj4jkgkePiBf!zBK=6;9-{xN7{U>N&Jn6s2C z)3E~bkeriTSoup(Q?#f5N&Y&G|3If^bA#UGsm`!Jmrt-h+~{)Z@1|>KvM^f^(eXDe zlT0{QEhb;xvhCH9y@8|Z!&PLA*6a)FF{8Syu_37`PWDO^_749<=+5|gC`Ox768L+5>N(Uu9Tz)HdJ79Oj2`SbH7sU zsW8)|`aRw^-B3&1@9K2)LKpl(I`|3dEkz_a%Hyu|xW}d3)2h;O;xu;D}Lrs$wq&L}qeFjM^VW76)d6ok=vK(xpncd7!jCB2hUxeQi{J_Uq|HfDF zlQ8e{`w$WD7UJB_@Av%Vp6vtt>DKY5g>mfxfNp5Ql|pIl#kouAlrmbc<1j2PV& z;bsQkXZXpr*Twu+@q3%!NBlnH=V{OHZZz$=x3uS016uhie@l~+ZwZfx(VRk8Eq|Jv z@-Rdz?%pnnORi&M-tvqH6|n(-Ws&l3G@0xh)xym!~9ziu%3$+pJQ;*;IFBKCAz3d!}>yKZ)b_(kdA`?V0>7$v625MI3d{a2@`yctYO^+(o_$@zlx&T>N@ z5e!demTRAurv?I7#4d&pH$R6<@5@G0D&+Ea_ok&6X_nqnH)O6gwx@MEVZY z!((uF4>f5?9Ih>ugn8wwoxJRo6WKWKl1SaSi+KmEvLgpJ@yerl9PS#9aK>F#xM9N3 zKw;g4!PeCytU=dXm)%rs4f>tQ-x+E7b7qM!Q1IbX?lQ?*fksPgBhc{V9L#8Q5Wlh_ zb_Ej}q25o1T3>7ezD&Th{b|bc6HKy+zD!qZgWT^Geqj-QC!LGF#o5-?cUYJGzNNCh zK9OpSRM&IA)}%Tmk*aXkmoVP_@WY;`TU2 z?6Z(Pw0|9zzXk6l+e??xX@rKQ@yGp#RLECY1SfOi^dz8ckLMsfAfqSxPlX%)IJ22Q z`eZip|ILIyHoqeFmX`qc<(?4=x}#n6XSW5+&7l5)C+W3IrT^fip7bkXuS&3_LR=#K zPu@-D?1c23jc+mh-`(&n_P-!e=zq{L{Fw6(HwI729WV{)L|0Hs7LF@J9YJ&v6)zu& z>^Po%=q)LkYa*}|_bGj^PAUGcP3M0x;&3WPBESE6$KUxn+Ob2l`_QN2HwnM~-FpuiZcuU$UkIGaBMM?O8siiu9rU z;9opLIT1dSAv0k+$1Sd>#%ezw{G?a!e+?0v)^O8RNG`Q!<;r)SJu6SXf467JLsU&K z+Os;y_ZfRuNBPdMXCb-ynxfqBm2bqJCC_!m9*nb1JWHNqirt6PP4^bk<$Xg{!Eiy3 zDkwKrDiLnXiv{%CAy$}*^_p;N?Qmo6uDiu@}arwSr+! z>5#m3hC6x7)_+CpBA|t1b1S=8V{=W+6DnfsiD`|^Yw{P4?NE87HMWE44Q&t=$9Ak( zRT1k>xlDf)UfJV_qevg$&hK;AFR;Y#2i%e$ZEq(^jzr@zXx|u2Z$%6LEFG$)<9zpg zoAH00iQP@K3C=BH%sEEi_m8`QimjEH z)YtrjVG?5{eOWJS(142AL1xWVXH3X5%s&-w`W&T>?VL8*9bgAJ6-E26k$;jg>>E3$ z=eL0<|1G~7zlkj%DKEJXUPsgB;9EZFlqRK7O7N@kU-iVtDe>i#9{<9_$G_VXA3;Il z%O^el!6&^rNlU+nC%*0$SqtQo9)H>4<2Ti(^1suhFQ4@IlMWyMRZo1!#FtNc{0k2s z|87tG-6p<#(&Hce)8X^)iND{(mrr{9WrvU7v^JH0AB!ow#dgf zr;P-IiPEgS5`JPT{Iw6Hg+H;y@a1m!6B0VvqfZfi?6X%Hkzj2)jV&{@2UcNz+EtLQ zh^+#anje2%V{!|>monCRdhcHdm)ibL_ODKF6N59o{c=>slNBdAK{jUCrVxPq;mkSK zIC(ntTq!`s$#&#vLHmuH6jUF7e5k=mV8P{?(Vv5+f(h{3)CR{Z!nk?Q)14c#mHT3& zZ{k%LN{)9ul)r=)bmei${hA%l)~_fIWAkHo@^uoAJqx2hLaltcTJo5rabB5Su(>Pu zO6{WC-iEcdl%H!|q+{Z7!fz>)D%zQz4BOziU07`KJL)PAe6D^o=>;c>Za zN63CPPs}VhQxYlId77s66P7y+xghBSWAb?hGce5e^W|gJ2jYl8tB5#BBFY)k7SS{v z9JJf@Cvwm}f|!3LW?*4win%N^`yK&D^M{-GFzI=WGTyaDuxT)e7kny7ClZ6b50$;M z)>9&E{CzJU)w9fQ(10bVkmrXqfgK5+>MD!n2-z;0(Onq*rp)eoB54KUo8`>oRC*m_p)EFc5Gi3goHi#YhdOCsKZVzsPwG|HR zu9Z^w)-Fu6$2XMOXBC*bp-2Pq52P)anz&Ho^o4ByQ>A)760BWtzIdYf`?=muK6|e# zwHn=McG0V6C}H*Ube+)S1w|qqsT6i-4MC)6BLM*)_>I!aC1Yi>DXb~9`wvjg)WE+j zcxz=@G}|IM4hb|sQJIPTE?5Opwg|IQ6#eUrcxd`I9#K3_+QiJnZ}+kFXy z#6~2;QhI~`C2d(sroG|yF#-WFyhbHPqUib>UJDJc*Mjo!`nY(b+5MBW6VXuSmNx$l z3GWcYU2dz8cF&f!U2rNHFbqu794jvs;^$JGKO5B6D;)Gy_4 zh^&?LqWNFSxTcvYkO0x_z4UZLXh|0@1+6+0^>GvR9mAbiQ8E<1KUs@|it8YmrhvK= zQsP95b`Z*W?Rgo?+bv!#g-{6oa-l>|us$touALO$>D~Vj`LWa89HiEVcQc!^2=qB` z-Y1LQTwmq!6zuIvQ=0z!ooM45nLN`W*Da0(P>z?M2uQQ@IE=Z0C( z5OuYwLX0{p0HFQ8-kBpfdULesOqolpwY@hwqZGZGe&Ae*6I1Gy$I`EzOYk%WsOw8= zu;@9QvwA9643E4e&4&=Cg0CZ3z~Uv7xYGe=N%a@ZKW`a~7?ZVZ<)@J;PLEyHyg-E;wlg6^{WpyI_%tSkil)5RWdMe-`(1t|f!Z;!>Ea zBnJ!6^_}PIN=Nz*Q@IbU?-?62&}ETF2r@Dx`yCt zEd(10c5TKr3VaXu2z9N*b)C8v;_9r0E6u8v!mc3l5$ejpHAu+h;s|DxDsU$Ohp1~S zu1f{|HIBg|wH1r-^7{-3OWcWEk5AX;V{9bf0Pf94-x#bJy9<^H6Sj)e)_~)O2t5yJfz|6q8+W#&I%+?uh3PCqb6~dA`aXB;SAy z9wV9Gkgv1m`Ji=_EvPtNM_pI;JLExZm%iu=UUj8YtE~trq07EeiB#a^;GDB z`%D;xEjAPiUZn^HP#@NGg$4U@nZe~~v;%~$FcVZi;>Ppe)$|tU(OjK*f979LX;wAu zWcuBgROReJyM=>}&VgG+oabh?+!1=>Z! zbfh(48Shpp_Gr5~Z%CG>_=H(I{{#u4=!Jh0?&%ftY3?Eh)|bqYVS_-wVp>hsz4Fnp z4T-Q15kO>;=&(7#$ zC`v%Sa>fCnaHtFCc$^ZEYusNdTnOO#*11~)#hqIvdWzj4hTY4O>0kO_yx2K@d7c>xg+5H3{F1w3SLP@eKa3O%P z!5vOJ4HS2>qsS_wF*JacRA2A-Nk8zQ z4^zi?(p3WB@c1i{k{$=_f7mGSgFLOh=s zPmrF(yA{SWqOEvV8P6TL;+bze;}}0syfdC@M~UZY<6jb~wd@q~=0 z;b`&PVLV%p5zlqT<8%ji45Z5W|rB7T+A_UE-)AA-qIYC<)Z zMI5ve9QpB|82O)&DO>*tRAwqS&@H?3;{llkv$riiDZB*?gD2Z@w>nV7`Cf!q>m-nXSe< z<3sa(Mzo|b8Z@BCAWExdZDj!ZNJ z=Z`Y{u=!N)L|LZFF!jhFY7{$fO(!SyQ%b|DG07cw?jlHO;c7pIA!W2+tl#BaFEHut z_0FG#g0;$708#caLJcyYynrWM$7y@3v0Uog=gh{>W1hI4PGH)OeU|)ek%nZB)ywR^ zR|)4kqXQem`)~EvjK@QeFVccc(;#kGM&;1Cu`3A|4PV0l@cyIxHN`mI$}FStKfi)- z62Tun2Oy#S)uPX?lJv69RGjm&aBY1^2Fk2nR#0c8KRhbB#ks=m2b#HodE#De+#Q*; z;*)vN*dA^w~1G#m=ul88tF@?jxV($bmpm3_7|O3B!cOcbYE#X+&w|d zn3va`IVb=I?WnYvpfznbB~oV1-mjlIrjO6ek>b}Kq%5Da6*~-!segFgjHxnyAB_ks zL#<~BcLq^Z)8(<1d-&D;+Vby@9-O#u{y+FFB+PW^C4N2e%P-;o|G(*F@qbe`iLy+5 zWj~KOvFK&boVbRjk(>}W8jqO}KQkUPA?`6AGavwy`LH1QwTQe=knFX#h`d6O%)>1r3j~R1(IT>=Adx;=L>_?Cw~$6!L~a)( z(n*U*cHDe#A+5BCd_j=94rrke*nafAg*4M*%)11MbkicTT#!gREh2jf@~FcgW$)rG z|6!1Ofv5K0b${P}i544OEapm`EfOudSS-#uZyUEf0q86@?!FYC^M-ND1W*fF#S2ktp;^Z6SuusW$bbbcYRpg@@GyRxUOF8?zeMaTpsagPZ7x@9`yqyKp z2#PvUU+*KHrC9C~63kuC2%fq7Kea-VV+ezCCJVf9>*UP|z%jYq`BAZHNVn7Tp9%LIk2u0dnGjb+@Lg zG{3>_>;2V#lM?LSz=B4sC)BxC6xL%af%`*ZNnu3xRzwO!0E*?S3CxySyoPpXWq*}F z8c8K6#dj2NES&e}`QoBzV zd0qZ)GLD6S7nQZw@H9P5Fs<1iC?log%fL5r{tIDYECKTKDxF42xL0$0?SYh-SxTGeX~L>>&LMH6T3~wW1D^ zT#!xUM?QqjVDz%MwLiR5EJ3#!)28>j*poo9W&PQHOh{AzLA%e}!TN_fN}l)&+LdpU z*))G%Gk?cs{?5((-J1D(HS_oN`ok+QbhzFhkv>LCMbjjj`*!b*$^HU=8`8qB%%H!9 z9Qt?ATDp_GR`y+%1LtM-JjuC?V<(tad1-ez9 zHu6d`R#c-D=5SVP>*k5`ofc^14oD5L`|qT*;7CZJ9aRF$aV`Uk1`&!E_r99+?u~C^ zPG2>JgGDWo%89y!FMLsW4o!D6X_9C$hw*&pdF9&8q(5>VF)<)iW}A99b>vocc&P(_ zWPXOU<A=dNz3ZCtQ{+ZY$Fe9LXl3R3 zrFQOWhKb5=?0Hi0g|FvP2O&!7Wf==f>$6r%--#_@o#>6P?d8u^YEW0J5yQG#^d)wm zS*+fC6<9HY*3~4?FCxK0$BVza zFqsJl{nYk=zxpyz!hIDH7{{g3Tz*#*RFB1AV(~in% zT_tY^b7E282bSP56BLy+gkGdP_miMf3bIMr2ajYfIxd5ehhq5$bY%<61lIZKE3=>Z z>qJe_*}%8yIAOdV7bmN`?u3Ec?$j!E$1AsINCj69MjvC3zvn#ZJ$z?Kx0XTuN^Vn_ zb|0{dpFu&RMr0*h`zRi1z4R4nd!<&5G+~K!1?&av3D5YP7f9UQYg>v_l#zQg75XFp zz?O%<_GSK;TVI#hzk5b_d_#DgOlA{JN{iwS0F24myZ>aG*6)Z^W+jH|><0!s z9hESVbrfe*S~Blz$T9%S+GjRcvM>-=9j3t>nsO3hWTcU>+ACsa?wKc}rto(L7EN+R z-&s})h1)BSE4BN7EQ^3|bUDL*?G-OlA3J3qvA>o;-034(wzI$EqHp3fx}|-%<&u$_!`m{3D0XLA!F#sCp;*Li(3_p6w?^B$6^TkMZF%3xLkLQbtHnQrRDD#Z?` zJ5FaIBxv$=%*#iSX;i&lav{amZ8M9ld!7#JW`u}4L92W==X~O{N?sEZdmc0$?i<(@ z@mC&YKWdJ22M!=7&ZKWT%U`9;X?NI|AxNmvWtJnQ$wYQHv+LwqldRpw5sJ<(NBkEj z^N&s5pgw4Cm?&9TUs9*y7pk!sl5?(ilc<6*9$U&NFZ|J8eYU1YU)k&2A=E|xx86C2 zG`kKYGS8HUHi`XPGF_D@tOm(bM}kEPv#7T7?u?gA;P0 zv8w>lEdAC0gpo4)w#L%%k}k52LHG+`)LRA*=TQiWF>is_m&^qzw)?VYi{KEQIM=xv zJeASio;X+UJaSsWH$NA=+;hBooB!%B{8#_S423}g7u%t^3F^q4w7N>5yg^-gwh-8c zoW-}@+kAOqB}stso;qkF1Q@i&@|E%O$#Jj|ULEu-mpu9hoK&cteq*W0IY>zbt&#?I zJS1oHh)aD^DB(nbYU`_4xazjOqRusv(yKU~3uuDrtxwvgKuWCBORRgdQjM?F8lo?o zNW}-NOF9URG0Px;(R6!?7h${Hn z+05RMse-Si1S4zE+iN+n#PB$`*kR3Skn z?BN@<7YhMrrntE-f?NK`p|SLzxXsX%OsWX>YI{TVz5U? zqmdsofH>#!n+@&I3r+J=`E}cS-*VnWv)(DS-bckp#@%e8XO+w;wdW@?ehL{c@ML`4 z&5VLcbmdqw7Tu$np6hItx@G!mBgH<~%0z^%Jk4c&M2S3TkzI%!C0TGfpC&D*CskGQ z-ztt?Fvqz==%$~^o7`y*NipOK zo9S$JA%~ks0(NWm&CU$k6^*jSo6dO863xkG@&Rn}sqx3*@M@5pKDgio7}Ayjat zoUA@9*JhiJh5(TpTv#Vy`kcd2!D!Zipj}=Uw1+VgmaWViDGhfd&2?pLA3}soEM3XB z*4`^2ai~Fd@9^6~eRVWu>YPaugYh>p`a2gvOJuu}%>$zo?(yknj#EXUFbE_96Mbf)U)v%q-Pi61zA`={|8#iBe@)}}{_EJ(>Fe7t zGT{H9!+=1vEL(Ap9D)l(b6ZKQLopY#c+D#1bo`a_!luf*OENwl6z$)Gd^|fi+F=Cw zx@K@R>)P_}pY+})bHxnyy{F^IorNLZ20HF{BdtEy2BLjVVU)_to$rBO1N5Xrpd*Yh z`AKMzjK!@@<7jZAI2KY3(!V`Zo##9uVNX!r(J8fl3|fq?6%yTARbt=PabUFn?@W6b zHCnsxplF{iywf4$=?SIwMC!JjiXBG9{*pDuFBQ9&|=_Iar+<6vpwK5Cs^0IE1BEugNf@FQA) z)brc^NHrllGlHkY50wK2|C#^dEP-lopZlj~ zut0P|zCL4>yBy5rp3s44|6^gYeeQBtVxMGF=pV48 zte>5??=uqH=cA;KccizASjGL#)(?9Bx`%CUwC32egkt)=-L=?U}(lXX5c?=y)l zyNOPaeaY{b%dC>!C~>~mq|=(?91uq^Ph`T~W)1VBx~P=OSMp z;f#GiBn=Su@?fS$DA5T2nvOWJ3`WQ26fU>}Or_t_uXEc!5-}ExkQhkJWlYn@n*206 zy(K?NF^Rtb{K2nOK50Zg+3A%}<~lwi$TVIjv6?!&`qD)^cVaTzZ7q;sxHedda~j03D#=K!osuXf!Z%9H z%0$fk2aj<<;|ype#Eyr=ch&+4gsahdQgF_RLM*)cj_XqI2h(2uTV;G2lgb$LXtFXI zr+F*mE4J3P=%S(B9L)US98cd8fF0UxtON zn19h)mDQCDE7Zfiuz*;|Y`xuih){rzq3S4#>6FRrw5pLXw-6>N`p!*=zE?`wAu_sh z*9tc5Rdi(O{IN6a_mGV)9mRysN_HK`#1eWM{l5HT%*jxF*HdjHvaxvR#u7nIMb3}! zA?g&-JW~A{6{L+YHknpR>z`5SH2u}G;TnvN>FLb;RtOFZ z+H>8t!ffZmyG(v(%Ra(R$@v^9Cn>D#KQmw_%IRz8EJ{Yic?#(TH_}!qB$iL9LK>Nj za;?br$nSZ>pxS;|ozQRax97;rUhMYWo2gt9OkV`(nL|AP*N3&$u?w=@c@9eeZuTja zF>D0TtVT7vkf-B|w&!Sew}<2up-Kh~8ES%s`$M@RbM_sr1)5{l)n~qJe5Q@Y+EPlX zF)O(~kTGjSDG!fLo{%|!!3rbPof;yd!oFDOJKHH1+Ai;rCdQXRG}8IIwB6P@8$dS= zU?6)!;a5CzaS&{b19d-s0+K1hU zvJ#R;+CFS3>~k(AS)p{v{c-^4{O1msOw#ztk$(LbXq4Ks4AZ11A zmoIdVf)?DB(}nn?3;&Q$BfpS|gx67u<}%tss=MYblW6sdm*Oe42Mv|^=KP>Nj$Tsm ztMa~s&G)832>-O@qR}Sy_lj8q#>5sus_8WFR~URh@Ep**KmZPC?1DGMZ`I=tuO2~T z3f$ zNkgqR)I&(GoU}Vt6Uk~zoy?L%tQ`u^eq|aSRSn+?nVyDBMA;a&RrSC$T)~4Tl3oFo zY&_`3>sPWfbd)i7)6K14r|fnWnTpf%k8*}+2>el#=--Cio^7}t3#kO3*$*0YrVFqt zU@N;sfKp&~K`Ui#;#Ox1iBQ;x@TO6DU{b#toEs*IbfnUcb5+klEAa z_kZ)7_5}OOzs%r~=lR;jHhIi&=h>_mjdv$g)9VzzI)!s6b{sf79NRY1! zA>d_Tfc7^{@3d5UJ9oP2`45*~?~QI^XQd_9Or8pV-3vqVG-5i~YKrPoJL{q;R(lly@|f$EqD7p?7k1_P)`KQ8$A)hyleX#2{kluRd z;obW1iT4)>hIQ6dYu#LUG`tnOIG1sB$lF?7=LBAofVgz-4?oKHcw;P1-rLvef<>H z>`-4i(-gc8SVzar&D$pRR%{*AoF&RDc7T(LRB}3>K{^Bt^7kyHQ^;h0H}?+>KjU=> zRDNzU{M^}$pWpm1{Pf!vsnY3d6iWaipO-e%)~2PIDDOmiPeZDCdb`J`(qos~E;`PnHgvKjw8ut^Q$>c5t*FMd4@^!qF_o|yqvBx?PIdVD#v;LX13An z{ug@i{lSj{kL-2mlE)cM1d+|^_&2OUqBfoEU4iJnx9$wnuh?ag)#weZn>YvcUyH~CDyA~bEyeF&a?EbSGL+u~*i+7rOH?X3-JG^`p9v_c^*|ToKB|F&o8g3im&H;8K1(C=Os|B;zb=S`8 zL#f4X6%klYdltLy%9+Kk;G9PwM!m2|@Sbnb%U+}^vGLKSBK9glQe7Y^@0t3Ng#`6A znb4Qo9WIh9r>D~J?%O8$EV!x4&C>@o0x>}m|45`7u~8G1A%Xo802Iji1k~<}aF>4p zk!66CCC#E6MK&rp2apKWQiI90?4quyjM1aH_+l>2UTS4YB1cMm(bW@6QS4l?lFDAI zBan{Yw~{2}8=VnCoBKq`yeH)ck@phsr6KcdVp#)?OD7AtalAt2Y|34fC2w4hvwNHt^VW>5O?R2=PzfSTO{R?Dq} zyb^R`JkatOT53_E6D9v;q(v1^6iU_>^269$ zY#o^Lxmbb+|FGb#FF{Z^MLq_A(E|f3zHJ`}f9Vfc+XE}U$qj^e<&_+9(7aj7ad{)Q z*6emYwj=6nMKi|EI;!;6_j_-vT9U`UBJY#TSk-hDB|Cbomh5XY#Xkj+g{@Nb0qy=r!75B|*(w9r%UqgE6!$kYE;YuJBh943 z3}17~c^3w&6~0&*b&e~~7rWTFXva%ClG&Kp7~RdU*Z+rplILgneavqczvh1Le3ocW zB92k=6u`lhAp};O;rg9qb+LJo2Ng|IApxMpVHvll)|o*Vey9_5nmX{5uHk-wN3Y ztLrAljD2)*^ldnnORT11Sv1W=c`wYO?R(Zo-tldOj&Hv}oEaRMMu=b}$TNh#7%G!==j!_XS@F=agAZWV;oSMowptCi!nji2**L;i_NfJM0M$wz7T@wVblT&|KBInZuCR zSJ~^I!#T%XYr9;SI97@&e4v#v^HJs>_Y$2nA{7rVHezOCR zXs=$hce5#hPqts?Bh)*P7`tX0HqLsfPYCcgqSscl0H%5r1%gX7_aY$NyHwYhyLRStq+C1G*18JY>Z$CIMu^*IP{O)=( zS^kWP=u6N^kk-)S3rRNnQefx9HW|L@0XM^^NJ6rQSYNVP3W;9!vdo$R{}V`jMYE;5 z*#*xLO3SAKhiK>`v53*kW5E5#fa3YN&=d_=>Nx@WNzz3PPACUuoRIytxp<1L{VSaI zhzRUV_z^tv0!L7Z6BpVgn~SIIMl%2&pB5^1*Lko|#;$#t+!i`I)kDzNp4i~4ysf^Z zQOGkMBY+ln;fD2uF5JV&>LO6R6&a9&$%mC~y6_%E(s=%ja7G3eBX3x{yNX6_k#8T% zgICaPm`msAj55SRn^$Xhm7qHD1kE7m>8we4)LRMC{}#&lB>*vcCAmnSBLC z5}mk}!-(T>%6zh0))&8c4m`kR5JZkeNXv%4oRc?$P_vZwaXt7jh)P^ zwA?AXf)DPImEkh6SLX}S#IipbBULMHqF>#A@+CU+ zG<8B5Y41gPV<9GuPc%Y@u_}^#7II9zi<83kkr5iHee@>2{`NEO5TbU`jlw33<^PFn zERtEYRPDDP9|%vz)Z)>Yc#{`17$Sqw4)3xYAvXnL-xb{!C5d*ekkU1asJdXU=?m2i zCX3oaiXcojbyr$8_>_KSF=U#s=ebCa3h6lc`GljSj^abPJ^3#7^XetJXu-wgEn$<% z-(T)&gz92FvXvFL-x2Zs(bBivvkg^>?Z_dDI3DSYgY*ta{cHl#{vj9DUQucYu=4oS zvB*aRTqJ`X`+rac-$i)JgePyX9>PDhN3anlST|qgu_-cdQ}=QZ&<|vJG}ND2*tuPr zE5*>S-2R){s4se$(uBLS2rUz8elEfBSHJ`lAL-g+G1u<&!~J|@z@XaZN}tyDBqt9A zM`+le$-Y%1J8=QKyl=pIR95O}6f}na(BD^id5WS|5!?V$S#v?9_LI6H$a68d!^82bnvo&GeA#Apa~esAVRlb!XDuz4Z=~h&3k(W|sVX?sW2wD}OlSlSl@#eJ?;s8Z!y#+A zywFU-%jFx0vLn}tG_jai3G;?pn4hY4=K@t;&yLz~1$QEjrt`;mCP9@a8_W1=u-t=1 zn+YunYe`_(4Lb#5aW*z=2?M75iPb<@6d#HdJbh8?ks3-SlGf77Lzb3T`gYgY1#ZiJ zoaN`#V+a*9pu5AWD<0p8)HvT7naDanTV*bJ-h)6sgN{gowOYD{N^$aQ(wD!NYL*4` zj+xyAHOQ&ruil+TVX`ar)_ zDIBz$`i9qt*8yS3{v%1un(RM*O&(RnY`#*q?`%*As|zjtrLx~oRS*V}Qm=Q+F$QeH zHe1rmXIH6Cxde^qpY;Fu-FF+RhS4(-Z#6wS=;=4ahfVYc0w@~&-xm-i|16l%Uw{-C zgo_cG@J9?I5ZJ06520qDCfjSfcbAz{Qh-*k?l}`GDr~@@`H9Ho7UTkoJp(1|xgXQd zr_lK}tzBQ|d9_b5p#jNr!a-YtmHV5 z`fomh!iAwx>OMpv>ahWvC|P1VrE)E8gC#2@`T#la7a##Jj)%s+3?_#lFi@#1A3w%V zGa7`SgVPMieChgm=w)b?L1)IpOPPYixNMi6T&YOYIYI`qA{D18UAdm?zvk4 z36=GsviJO)`I1vJjdC`o`m4}72~sKzv~NP~4SOa)S0b#QmhJTRBUkil2QulOlpCVX zcq{c|By?L#SqN;0XV)t_Pk?!U!zEN68He4afLV(AEqR!L3G>%B1e+FLPbrJ9u`qnA z+$GJld{nS~vjesH6tOb^Ns~X{^Gl0<3H|uxuR!|=GwWw?=Wi9r&D#JiSAwZW_PQ4k zswl|%-Jrm1Usd!tjni_AZP2c=RC^_N3vb^eklzM?@wPSer#a{!+&|jO84-B#7 zo(myVO}eI;h9Mem zN}~bWk0#QR7x*kS_92f#Xw|;^c?p~gn*;*?je6zsN2r1}HfqyfSoEn9O1AD_cK?RYG5H{M zFQo)2#!0#a(K5P7@YJsN`V&*BIgiq4M>WnyHTowVgwo7-@AoIC;AHV0UkSnlz?SXo zpY*)ph?Dqi8BYF6d|HC%5k)kRZamWMAw|Pzc+d%BMxRU2ora4=zJ^RB#kDU2d4!nzK-kK}UZa0NCgfqjI0Y+m6v{;(N$q%qDHY1P4Vc1(eia@nbcI>yR1|7wbvGhM zCw&J!?RsUUZTzb;&GC;}qCLuwitwUs)Y;RpE;voD3o0z$m|9EO0^cXlhyn>emk4mqHnG9zc=`z0aXAs0 zbIFSHTpA+Q&LWP01j{QoQ5dJkK%xj@chyA7fzu%yHgpAwY)HK}hhflR!+SY=E4Q(& zv#k~Di!qQ@=q3u?hze(~hw8V#4^|#f-=iCx^xoFry(r2_@2S6y2N-1AQfsLu7B9#{ z*jWfet+%1Sq2{x1CTl=s>hBZw%(V5l@ZN!nZ6eMw;+Vj1B9PPy*!(Ye+AlMX=ygnU|&?A|e#5u)^XNcMik zF9!}}?;r|b_z8La3-0nfoZWp$v2I)PXq8=$&x7nJuT8eRjzwZu{SS5L*9HKy{BKLQ zyA*W9JiSTTBc`6jXI`)vvt=dKoit=Sk^H`KK<)lXCnE<4*eqq?j$K5y>w8W535n_k z#INbDy{!W3%(Ct$%E-P6(p7(|6^XDs!CJCe&*RYYEQCE`6kpq<;tNW@DL?-dg`4xQ$%KNq{|N2E zQiiFBu(TeBAOb#D6<>WMp8b26$dX7@!ONikyA^W4m+qiB+lG z62U^(64S%jhU7?tn1=e8*P!H9qQf((D-aWek>tS-P8W$-9)$UYHSgSNCgFJ}2!fOw zKoCeK1b4OSe%}_sRk5D|W(qd0D%c~}n*cw*%XTjy zw0FxidyRSHNG(L4Q-6a5w~&;C&N)x?5PU2EL02txcf>x91F%64FGO2hO_AGc*WwPI z)EU4K*}HxJM8BA=;31T@6;yaC$7;p5ifyf+Kzl11p*9!z2)MgiK}BR}1ur-ZHX?C= z6OTx&^595i(bOw2zZ|5gsinXS)y}zyF33Kw>EiSaj5{nZ5SLvGrxvoHDJ=@Tp0ws% zOw@bkwcs{tiPnkJ%6b-t=>q7<$P7KBMu}+VPGtHcWD5INk#sp-WKC(#&6vi|X$}=> zR;o0?jA?wF=GiZRbP&lp3c7k#8PwuRghF#)iYffvh(cB@CK-z(j9bWGzaeMF2Bl#8 z9VFlMGeMd zv6cR=LDJjou{ua|@FkT~NfL;Sp7`re0m`1~e?OZ#@wd>r7~=cy@EyCaiCROxgPuU@ zG40BZ`w)i`;dm}O-2j_Eh024b`f4f-yRu2+P+JzIr=oN^DWCu!*CR2;#wH96hYSr0 zvN!d95vEa}5ojUBsQ%>RHZ&P9nPQR$2Nms4t%s%;=WM@CJ$oav!g3}A0=sPKoPSh< z>#s&Uz*wR&6mmD;MM#Dvtluoh77JEJ4O64LsA&|5NRQU`1 z`HaY0%1OK&Rz>?glE?7HV-vmGx-O!{ux%Jpf&#(L8#?j+Ny|_Q&t@Q$J&9IX0+FeY z6Z+822((aSctzZaeu2jA^<~tJma6nC+;=^`n-GleD4wM$p*fsSm2k+{4fWzUg95I> z4(~Q_Wg-<1%7K3__x@3({gbXjL8b4#0Lzz)8&apra(c(?UFcMd9*h!%&>Fmx+S5)Y_LTg6iPC!h4gHS#Tu<27Gj$n) zStmUHPl!>|lZBvF2qF>JRD&uVC>BMa{`56)IN5Nfs{V(hQcLf9wa*1_+MiQTgM4?Y z9bp9U-ZArCbzX#`MV;^XFRJrH{9aNnYWfD+0jc#z_(+3rH?HpNDfukkxHjXb!YAr| z7e#bc8VhyM+a4(@{xym~#+H7hNnPE?b+_T(uk!5!K+R8}%S+vd5K;GQ>YVVtezB_i z8+TCM4@TCYa;pg`dwiuRk*Eq8CP9;r zcbhfix`unigDTNX^I(Wt@)Q$3hiMn);M?gN?3!x99}q7M<%j z(rQGqCfiBxo+S3%QC52iw3xSYth21F2-H@B#9Tn?2R|Z6@&4M>!&s!*GY9D(6zRW* zP6s+ppbUt_`A$TJFd^U<1@Gj2N|^vjO{mB%08eMMd2$hB_YzEsCbaH`Z1N{S_7AUR zAMqTXolV)xp-i7(>_WgF5rDss{(cms{jQA=;t>ifr-|$*-Z-cm|bB(PqblTU$DU>`glUCoI82{=|M}A%sd{{f^YN$eRW&%ltqV*38fXBWdwL6!sj?=td6aEhLfR zqw?%ZU4$wE^7W9pF&q5*49;vl*=?YTwApw0n4dy5PV^e%2)RO(fL&(l3s!5h*B_4P zeMiEvc$<5?5PyEw!=ohiB>$!3ge8AD3InBo(u}jPAqSlJ<km<2_Vm%m50>DgDl1h;z^c8#1mf0S06z4eFl$U?ok z>jNAEN_w{)m5;zBgDM(6Ab(r==ItKeYd}!gzaeL+70UX*x*SMKcaT)|V#Nzql{LNR zJ(B&i*XgjE?w8r}gXqo9`m#kAj4o?PJq)bauM5-noPqSB?cz7tMc|P@g z4n_}dTJ~-GG)sScrFr)sa4O^9k7Yon7Eq7(jOkb?pu`tI)Ub3l%_f1T=Az0(pQ{2m zq;dSaRim`*t?SC@hC8SIou!X~jWBGZx|d)l(wkM-8=88VTHQ^guH;1`g+0FKzE5L$ zdIidc)L9)kyf!d*bWP8sEp-U)J-(NKj%Zi^5U;o`1*QdM;spHe9p0xe&>JooRo0(- zfp$R_pbDwA>yc87L|n^5eZ}T3=TJ&EUajtFA6wRQcv<#O>PByWVN_M%*Z7_)peps0 zd;!$~Yp$W{&;_NxrTHyU-c##Fqx|E|Tv$!zJ4(F629iqcDVajaIUNtEQ!Ag5b6DCO z@b@2Z75Emn-@?!WmSS_qqOktk=sS9h$^&K#51Lrrb?I1G177zCw5Z!sKSW2;QoLh{ zlT0ja`cNUS^{1{tT=2}xAMXFg`~6+-jajXO^%F2)>wiO4*7p7vP}$W)Td^Mn)!Gw* z!NOR8ESUg?+<6QM#mao@siR>MfMd5x=85=46d#+6N(#lhhu&x4{qy2|8oi&0_hZDn zPVa}~{Zr!oSiJX7I;WOK440X>8nqaQujq&VNo5Ft&PdS5%|Kdd2&#->wYeZC?Rc)h zb3GnxKJEW09*e}t^*AA)UxX}J*2KXdn4XMBS=W4zYOt$@`bzANu1BN+=}CS9veEwi zluzVhQKr{`A^nrCMINqg@b7#V4G|l4n8CuS-JSXr(t|#VgE)4nZSYt`SOObS-`{Sh z`O*?-?9YR|V&4XzhXT@UgI`xUU>p4R34pk78d>#EdVhi1$PPfFhR$v9Z*tvtA&$8X zew9f53Wq#srlx-2ZSV_3cs{}*MBRc2Y=-qidM6WBVrGY)2K-959p1e@%I6P3i85w9 z@=*l&H>b*eDtQ@AKua{_4U*tR92#8Ruwx%{|9twU*K`|Lnk-o=BL3t%h_5#I@Hq7S zQVb~woOItTjm&dI-1pEsYP9-(Y{r6Si^twuskx{I3W}p3WivitFHprPc5E^A-^6b) zKNx2=!^)4M?U{=4 z{r-_Wz7K(vf`QrVJK-q%c;Af#)SaiJmAq$qfIunw6BOlxIY7e=+a|&GYG>kzuFa3Z zr}5&%IApo!D11G!Yx5)gbrRwqg0J8ie@6LL-Pu(b{u%6b?fj#ty8B7qlJQ1$_Xnxl z&{Q7MC65riuue&h?wxcg`9)reVmW-?xW#F(rWLRyUJ*4BjDGLF6jb@U^U&paWjC2nt)w^g)Dh2d)?2u zRlld{3zncDklcX}kWV(<_UW(BChZKBzNZa&>^?%;!1movX>buNGF4Rl-3k5uTiyZL zq@4S%6A=P~l$uzHeINr%?701AE|J^Wo!kr=WfN8UsqTk?qtl?LpMqt16zo?8(}3KT zx)fzIBHn`)B&oq7@W61sI!N zx5JaO4HimO+uu18vhbKFEh6CCQ&pdPyy}pLP>cUQ6INy28#hqjL3455!<7GdecPte ztU&Q=#Cw3^^{l0M&MO)yzE}^$Z4;1o-o$cL3aJ5Bj3Zx{>REuwn+;$dw&NhiB`ndA zknpPO3c$1GV8mE#+S-<;PMraE(aIt=H$zAMak zcm2+>>h~#I{l0`bz)0)Yi~5aG^(*&ePb}B*OrW~%C@)1539RC1sCD!v)~}$i7;#Agbn8T> zj1ns!9Z%< z#SI@g1pZ<@nGIE9)@4}q_02%sJ$^n(X7S}09iOEmj|EkmFT^Xvw0MP zk?NAi=|uYV&_-zKk^^d8+eURi-F@|SlxJDbFZ_=SNq%~;!dM#9xs6V=#F*w5FBY0 zS-?U-b=k21YxcTZa5gA%Kb~{n$S$AF5o`)mP zzJ&(=ZotVf zLL#Zm2J&5q8hW2Y)`wHpoLP0x^_;NB_c+#uk(t_`cm*XzHn^4*)j@Y)_rl3L*5?3% zqJF+xDM9ab^yS`HsLC|ic&3!(_XmlA;f|r}iN&M7mm(@^jsVIPWlBaK5#K?$FGdDv z1m?cTbIhN`TfKODiihwO%f;Jd%Axy;mEvuRcxw@FusxD(YVwlFWpPf!TA>`r0bHGwCroH#Cz(!w_&9K>GUu7+f_7Jx@M8n;BsxTIdsB6GQ;o_ynBGBwL?{{4ghZL$*GQXpqWNDHK0|-OBZ(0qYIazt~niroRM< zdy4MU4o2%dpW*V+t!oQOc-YMB3ca`UIDrQ!NV@3l>yEfpJsa>z{Tto#CNPSmCyn`b4aF!Tuix8N)T7+zNgv_Oom!a)ca7A>%~IZb!&y3V9WMT-Du`a)i7$4k5o2 zA+I_@UZ;@zMabKZkUbRgO%bBeB7zBn6oqUMA!8jOf1;3x2$|prd745Ni;!a-A&*f= zu?W!}ArDZ9E<&bJNY@tX3=ozuumo?^0eCx~-g{93cL}SM@(uy-X0Qf z)MUKfE#9b^c*FEhpde@hu5RgvzOkzV1u$B^K!Lq?t)=(gudJg#U`$`3W)L^y;GQiM z{lO(rS@(a1VFzM^DO1U4K#p$EI6(tbLqnz+#D`A)qY(9S)>*m!{N6f z8vXiK;A8(+2ua6iPw_R@t7lZ*0DgQ!F_HD7w(mLF75rHa?o`3YK(kRW9&vkpPomzW zv6*zTcz@B^j*}gg&0deAaQgec07uRLdQVv3C8ojnG*o*M6H=F9K>=-5Z80ZZJm2E^ zBuM<*9_B+9if&XWdIDG;q35L^?CtN~zL^npXF^=Ih--ScIrFphJ(s_NP5@$GN0$}h zBm?lkzZ4L#@QrRJoW34K!pfbBq*hF-h8aW8Toxmbuj$#e5kq><(SX-_zz3{_rf+?} zgRBCbI!cunz4i9$01a3UHfCM7nX(GpLSNYOkPP%-7rF}-z{PlU85*usqD!B|Nf%(U z3m`m!hpK9b+|b%?>;-?Ak-35jsw z9Zw|e3Cp`H-WqL-H#tMRv9@@h%MkA{TfC_m;^8(_?ni3W$r<7uWsB#{5YGdk5!&>W z7E!Z`7Ip{UDwROSJx>H%30AstPM`waKlgA}RbV}LKP;Yl-$0+$_DuP+tY^RjjSAv>rOb##b32NE5mxfcXZ#xuT7}})6 z2AR%51wbVp$Q4wA=jxw<7|3&VA|^J?;nr_DOU+)l4q29UJwQwoQ}&~#;7Z>9|H`@? zvH-Lz0Wy)ic*+Da;mX=p*0XrZ@d(k;f9tMC{L9}RMgH~IQrgWJ(sT>nRrF^Yf{;P> z`c9O;gHhoIluS8{<%rC&`$(Sx0~8zpv&_}{)q)YU<%^c@Ax1DA6b-F^Wt3zDCy4jG zcvpmO8c0_e6I<`jJqJj#2F={CIA(WEy=>4NDVxl=9b2sz&fa0R8 z=iDh+ZoPQQVO{TKB@bWUpB%StXCiO?)5!&U0;WcS0MmUl-#81s2kENb!x!AE(S83t zIK82N-Ol6_rn>3_4jNU_y{?z^Ls&6C4wtEzdA%=!yRQ|5uIm937_@}1tfywml*`{4 zRgY$8U;a)O{wDQ%Z_v?Nl9`n9>8to)DWAHxRc$FkhV-CS+>3HNNlJ=&5ylBI3Uprz zMPWuyq@ATZ51;|k-qTB?s;e_!OXz?Y$xAr(p=$Fj$hDhu=8PFD%*aKk9u!JDv&^bp zfvBjOR4}c_8!{ug2FnsAbe=~a;QSzQ(_h!6r)xOEnc8zL0}JhOwgTFXl|UG)F=z$J zyJ*D?suhIJwcQVHz&i@OfeOrCzX(mk%_?_#v2=L~Q%=0yE#4-eTdH=fAB#VTi}Sn1 zz|UTP1HA!t#%5LZ-omkZ1<}o}*j3XryYGt>?xX>$XNvKUoLO4>ygU444xg{WPsAws z)My?hq{76gpg%;iCcO%pfx&Zx_$(#7f8d&(zHKc0-9iFg}0hN_K-`M~Vuq_9#*1wth1vF~jo$I${U%S0*)-RL4#%D1Jt$?Z#9m=G&`|?c! zpo#z5GZ7qtKXz@Vk2p#Pyoo>bj%mX?pf7bxplI|~Pf2^n2=%biEw#T%;?Wf=Mc4)r zQ|gu;7jJC{W8G5UpP=1AP_AINth zre2KKJzrtO=>8p1?qp6`|Mhl~y}E(BW6Mn0T?#8{P7j6A)-e)IulVToPLNZXq1TRvqybfM5SRugE z?MHpkesFZT-gW~ajB~y1x;IF0kOz6$7MRTW$e1lMz0?oUNh3+p4^ioZ=!Z(3nVR}7 zuFtAL@}q_ilHaSZ+bbzn1TB5jamdirH+g76@6b23ftHA&e)b^%Yj+n#VbVVh0}yz2 z5h9S5sZ<0PQE<-|j@9c+A~1Eb2q|@hv{T4oBBaU@av_Dhh9wddR__Q2QOGV4vfL38 zq!609Ak9ig$O;Piwg_pVkghFMMJ?flj|v8BfsYE(px^A;LK$m`BjKZrXfY9FZ~gy} z(Aeuc8M)ECT9Vf5Un8kZG?wPak04g+G%9Qd!wKoNzMqJsvp|<@Q}Qn$Y2UR7verwf ze^2OvKHbU^W8jZiAhBg9rQR<-ixMy(8!Kg${&j#wTT9`Nlt2R$YDPup4lEi)G;<{S! zXRT+>gl@hpcgOmAFc!vy&wx(&(g6ya}Vo!H!FEG zsPqwCm*Vb;QY8Ho2gr*rRro{eLR~vYcdxsp z25*!;xvl%LuI*Ug;=>3KtU(f?6y$}eD0ypOE7AXJFeL&R(X|f1>&FPUINDkUwBl%A z6mQEZy2;-pN4r9VNsa~;HRaNVn9R}k)l2{)cBFkyCODtS214+uqgQ8%>;QwEcskUm1tT*BQia~qSpq11ngX7g8VCt7~j zW^N{q=)eV^yDMhjbqC&)<12bDd8uOdmb>wim{o-Zg_X#-CV+d)1DCu6fw4OKCvU?3 z5?bmp2!PT6lUPMZewa)$VuqdIYL79@ROoe-jZ3OWUvV6R_&pDtf-3 z6qIBBc;YB@4HGb^k&Ho(dLtI&%6law104Di#vx zWUAx^wa~wGKgq{0zmD_7x#ZsDGw7pTRlT);tbq+~|3;!%fYrG)2OWb$4r#OdONmeQ z&@`ZGWMjI6fg&kT0r&jjj0z_SV2TTH)y>Z^@dTATVByKk`j zCcAI5`!2ihv%8mFEkN-{u{)aGgV`O+?l^W2WA|`&C$M`2yGOBmG`q*LdmOvRvwI@D zI=hqEox<+P>`r6%RCaUNox!e$-CTC_*)3qVh}~j#PiOZGc1ziv!)^t;bJ?w8cRsr{ z?AEbc&+a027qh#R-R10_!|u84p3m+|b{p9ZvfIRN3%eJv8)i4cZX3HXb`$Kjv)jS$ z8g?&c_fmG(vin7L*RgvAyBpZOiruT(y@uV5?0%WuYuUY?-5c2b8oM{M`*n6VvHMMS zzs>G<*}aY3+u8jgyFX_44tDQk_ilE7%I;=%?_>9Vb{}MS8@ms&`v|*_vilgjkF)zr zc7Mh0Q|vy??lbKEj@@1C{(;>;vioOtceDEL?z`;1&+cA!wIJg^yQA4XnBB4Lj$`*Qb`NKF0=q}Bdlb7zvwJMN$FX}nyC<@% zvpbpHDeRui?lg8!WjBZ28SHx4&1E;A-2!%t*e!+&-qw46lu+Pd+-G zz2z6=n?}CBk?%zE9Rj(v?e{g8Z5lkc14I~Zz|z8lDQ6#1?p-%RqYBj0@Tts&pp$#*6B z7Ll(A`kKBf@>P=W4Dy{tzI^hXPrg&h*GfK}eBIRr5Bb(Yv)H$re7BMBcjSAP ze7_{$-^uq7`Hq4R*teN{KJxvTd<)3;ZSqCQ_cii$lW!yWZX@3n;K7nAQ5^2NwE z4pMbr3;8}zzVpdfLB7T0YbM`(@^z7~lziVJUjg~Hk}rpRe(d3Jh?_H?ZT6)O$D*5gt-(Si12lD-qe0$0FH2KaX2t7tVl6m_cBp>V_K>x@` zbFseL$yZChP2^iezU#@ij(k^>@5ki(BKe*rUkCYUb)+vszLP+g`-0@d781}u@`cD( zPrj?jHvc8VKkg`6fc*>pO~kr;=|R`A#RFM!qHFdlUBL zEi^ytdxd-($oC@oZYSR^^8J>4zarnOmC;|LG#BVRuG?jYYX@_m*Ds>2y5zf4l9H zGhaO^u@j%x;@O5j6t@)Ls_~~3557&GbPsnTSwo6~{x*vaMK9{|- zGDc@+r$6z_ng=v(Qz`zIbZ^)Iqi`MRCg9PL$6Cy%+sd9e@|iQV(&H;M?J<0N z3}GAbY}%HEAIdg=^kRSTy4L=HZe5VlQ*j9`tD~9@wlgtkhrn zjJCOcqs2S87XQ>=dI4wDz=!0I(%K*Oo&K&p6aB?M^pX7i1TdfKrd-!8>fKPbDuk(w?mD->BthHP1&kN6f z>wf!#x}2dNs*jFrN<~)ErM^)8L7%vfwyG$EOTZ@l<3G}&H}QzHJib;gS@K(I3x2z| zuF-nlfA@3mP(J#KO`}kM!hx!aiTt}&#AE#RcsyQDt|!lv@9}vGJcXVj&n&OU>-FY( z^St?9pSQqU=q>Wj%Jt-Wb8~a^a`SV2xdpj}xkb6N@;rIoyxhFJy!UyxszUz9(~=ka-cxxPGKzR%|?@D=)se6tEX1>S<(g1mzK0$)Kv zL195r!K^}0p|>!%Ft0Gb&{tScSXfw8IIGB0n6&4j0&6&k{UK@hro$9M9Q!&cQ>! z4)I63Q2teaNWlmqtr19y@yy0U1GWSYAwG3}DIN?e4Tzy(@Mu5|trE{%JoE5W;Q_Jc zy$=iUw+2rwo;o}W@zmo<;<*@40?&cp0SJ5?AV9nw(z!!h_;fOHqg5H{tW*?4(QjYcONDHYzIMe)_3`qs`6m8uJn@nHe}}*MC;xNRVddJ|-G!YGmz8UqcCBBQ z-(IdgR@dCR{Rib*$(L5XwETtg(yQ<3IN>LgD(e6K;_qHMZb8MDe_MO-ldrBN^31=l z-AKQC&%EfUAJ2PX&AKz+|NcLou5GM8b9(vldtNL!?#zF*{AI&EZ@+X#@89m(^wy(y zoKf|Y`@irb}?Pkg8A@|UmKl5_L7-wB+vVD?FO zbY0u`!lc>P)=Yo?B+pa#&EEWjt%y(?7rKl)n0V}Tl0#qNWJ;$=P!F_ z)^*Qs`cCH)zq|M%+1 z!P6dncJrr7zJB6ei>@jv`0ricnYXyP==`7U{@FL$&nyak?-##sO1}HEv+g{i^sedC zX8V`qO!-wsW5MWyUJnLty2<;q>P}@tiUCNk#h?uIXQy>!^j3AY97V+XZ2AF55b^PdOZck973 zJwLnadpl`KRQx)3pR)N!v$q!pzfscr{ki{pr*YafkqC`oUv8 zbLytlP22jdp0N#A|JOtRJmrk1mRXQ_kJ;>JASUemkv}_M>m~^WdJX%P!OUFC&qPe`NWXwGxkfCt7NMWPL~+l^s9kCvaw%DJn(PPFQa@cr!4jX^CAm-YuTNRxW=>@$ddMV}~Ut;=8 zbk*a{&CB-{6c)|$HwJ=6Q%9Rfp2qOqCH zF@IZ2C=j0+HWRH7K`T_yj23NPO+$@d8H+_@#rjmJgs09p6|VWAcxqgqu1^h)I|PB| z_h@`YQz&e#h_)FK5v(0jmPi<_ZHZ8%Sx-cD3PFaP*&%bo{^mH|lK4L$k3cvYH)JMU zEc`0}W+Sm86lscbFr1j-54ZUfEdw%fL>v%`WWg2waJV=eH;h&GvWm^3%sAwcQB*Zn z$3h8%GSJwk;d|Ta;EK4B&;l*NPz*p^QC+vRtfsnR1>7Z-_79bHOKR=!3+pQvl`XDb zSf?c;t0K|W5p5x0HLpfDR3{V*aO=eYS#X@z9G+)HmKw2y(NPwQ`8(tIGA9%X+CGUm zbCOL>Ko}V;-sputSNIct^Q9r#DBsJ`+=S84;tv`z1Pf0?G#LvRcrA~%wxOTRU=h1M z76k&eA{>40544!UHKE8VD-hUgz05Nbh#U(w)tu(QK5GGTQv}*CfY1M$r4oz`Ra(WBod+lW2=b#QtL+rJMx>_VO`mgn33p6 zSmR)vW(_^9&R?e?OYNj~fE#Vm^YT1;AR05qh5en;WI~@7maWuFbk8YzD5AF;fjqq} z8lRmmidRL+)&0r#+2be+B*@k2B`K<_xB3(LdK(DA>~ZbUP!R8-$TaeuqF<{DVsmuwDYGe7vK@h3$R*>_)L+z86fA3zug{J7o6Do&Xw0fi z+~3+3He$IIIzW}KLS`BiN6<{hjA{6rg9@CY>gCPhf2i0c8tSN_S{l_`^RM976?pfr z2vEsss+t+poW+p9MhR)-kns zAVA$FU@?2#C27E6?>XVm7YWUp?^8#jE<<#sgaEoQW&}b!99lxnEp2*JIOF49Jdgd5`Pt`K(#q#bPnt6I{F6_ecFOdU6{mgS zyo)Y5_u|>bpP!s_`iwJXp1JbUG?=;H2I`2(;y!lwP{Yd{{AR~k1Yj6FYNkp<8fk0~ zf>@5hk%tij8tEEcY8=^6ZumT8pMf<BfIl2sV?Z8gT5VQz2@6#umotAwVhp&3&{W-=KF z{?DM6nU>1}<>$EWj~RM267JM7rwT=kpbk+^cV(P60|Oxv)cuXoc0-#Rjji^_g3|+# zWg&SshQgslCxLb7Y&^DX6bNKll?~uVLilsGLxCt3ZA``!5hEVgTcbf^0F-3IVn*2T zWB$`Lqe9K9Js)Y&luQrI#bG?AEWFib);c`I8qXGPFxOg66o@o!5e)HerGsgG9I z)0fKSi&r?mET~!pNsdR-il%Tf-okIOXc*mD4s?iV<^BlKnpXcRLk}clF&a>-Lx~m) zTQOn4*)f@_ zgFXauf|3{!78pmCNu`-C;%^0j1h5(nj8>gg5S_X}P|b>%0b?bsWtD8BW@MFG++yhS zYUVD}!=c6)2;b2YSg$Qmg!2vG(Fx10lz5} z5Sj`-8er7qX4wj(xwYV1?($@Mj9rPu5`Ps4Di?2+%DSy=qXFDJJ;;? zKsTB}kc%4&__d?hFTQsc`@K1wrh$5^TIyJ);={Z&^mn{NN<}zm~Ln^CqqGlh>4RR2&A_m>5vVPJ!vM%cQ#}M zIP6P5!0W{L)^pP}V}!kyvDIp%e!PczfiE}hQ2%#>>0>hJ^L*Z18V%lDe?hxvjxYNK= zB(ckSte@JYZMR729`nN(8dBaJu}qhnrY9lrHV~!Hz}Y{#8s;mgx<;Z|Ljcppv@MC& z6wocuxNALtmhu#asP51NdN{KQuN0bG?18KPQB&BD+ib!E>vA3*_I_2Ew|{`M4}#dg zh!6SpRbKVvW=5gYY^ZM{{J&ma{TG4BoVJgYi~&p;k?8=Kq!dI!Rt0cTCfjINh5W8M@sJe9_&EE4?BuP`-+@mH@8npP8pDM|Iep}Mw_j$y{|ajtrvCqPioEhHB|pcnngDZ^qwR4=Dhz*;Dj3F0n=FyRSMT#RHWT;*iU zQrfy_XC?<6rbgDU>ZbG%AEUYfSH-m7lP|DVk<+q!fQQ0*N=1{xlz(6=_7l&8NShYJzT;wffhuV8pE3l!usetG>$c#(KE zr=m=+T{y2yUtaH*=WQdW(Q#!%F=du5|&MArKl0| zHG+}JuLOvkVv&owxEWMhbBjxr&^S!_2Qwem`Q_#|3Edy;E(j%Z(C%ogSzkIMPhXU5 zg^7}GSABn^q~){^PkW~$3kIiS&S5U>50m&R#*|;+RTD)aV3V@Ig%@`1R=zOek@(T* zBSD*b9|lR*I&7m)8Z|ZMJ7*Vuh)iw{Jzxf$>lXC95?!dl9&=7nq|hUeWxXPvQ{-lv zRQQ9EQh1vbTV|4xLv4;HqHX$|In!~Pi@rRsz>|Jl2rfNK$$gD10*dy-2uZC&H+sq1 z3+vF0kf{}i79zEpLo==rr(1sUCE}&4FJ3$A_P^Gc62C)rU`_ zm{Nhvi@}^fu2+Ubt)Yk?+ctO{f~7eOEiJZ2Gci8LdOv{?h}f(sQ1h#5wtEFSXf)seuA^cKQO zleb?erP+jGOn+UHS7vRU)xjB+b)KN7iH5W?1(vkXlT#Iqg~VK!_WWSScPsYJKm^Up z+}9)2*-4qg>e&H3?-n@*W2Pn`Egj}yrqM%BPZU&q3>zkSf<+7lm3)0#eM8zm;J&a)8GpuO-|LqMb+s>QN9jz`B!6$ zQnWc$-y>ifDg&3s+eUcy!w|DjX=sq;9{?GElrQZX7+!D$md!I-X>P0^Kr4a^&hJz| z8-=QmuEHiqeK8I)p*>>QFjRr{0WACO7p1}w!kSBu@?m8dMRN+Q0VG=rnp0m^p?^d? zNfC*UHI*rl7G=PRG(h^-GpO`H8Uedf8MsLCe&t>^Ao18?xU&}S_=uU%$TlBTJ>7|3SrY0GQwbU$oU^rGpF$wUJ77&c9yt0n0eTE?}3 zkyg0Skk&JD(N!P|BpPfKn2yB_eOk>aIj4y!AZC`i!rv+O@zk5kUbf}!< z4pa9WQ&Ci@m9~P-MhvBERmvXKh(alIFvi#$v1mUu!j0`tyScvm02e=Qfqah~?F}k8bJvBbm7HAj?Q13Z!n&TM$q(rcjCDusNIu7--Qek~jdN zEI>!DefarGx-n>>E`_R@>_-Bhp>-!|;3Jmu4#0<1v_@t9+7mV}f~nip6&1q;kiGO=zN z6omj73x$?jgUumheKMByOf<3voR}S$Xe4Y@NDY@ui%0)%jJni4Ba%udB|x~e32CQ* zd8RFowWO5MHqeBU8f=n+TV%=HdZm^RKfy#CTkm-%3Mi5iCk0N`0*T7~i8W&RfR~6D zE&OPfnG||_ELD0Iz>0FRktz$#3pJ9f06$qTvc;lM5~)fJXL-SGd??>^s1QDF7&4 zQlT0-YEPWm+**G#&I_z-FxvE)m+ z|4<_g0*=!#-T{*(yy9;`WPpi$kt9ZWBizOj)V}`?f(g03qlI(yJaHA=k4V@J59C|oj();8= zF<2z5WCKRPvK!N zD5uTS64ZKbZ72|n#-mLMy#YJ6mf^g3*oI=KZL@wd{`fmtqETiwQdMNG><({X)Fcf9 z2Z~#7;TvLCgcL*+Ybo@~^+vT)R~v2iGgZjdtEwxglhSLqK1?UcF+7Yd)#>|Crgv|! zu>`g>ws<4lNuBH7Np0?;E*oSQ^}cQm&K-Ehqm5L$&jcQZZFFVVFxS2AD@XILgNqOa*z6P4FwkHQ3BWl=xp)xo}?PLY=H+bTTWlSg=VJG3@qj zLD^2$hFO(HL&3u#Sat$V(%iqFD2U3@jL=NXRT+ zLqJW<4(f1IKsu|F^b>@A*=v!W-k9XKK#3DP{Tq6LTv1uCR|@cE|I_UNxL8DQz!~~j zCojj2s}FO zY4BrcMl6{PripM`>bPbB!{5lEbQ5mbXXuI3(DZNs%#>g>0O1b6Oij0aasuwn7j|D@ zdUB91t*)ppv!RyW?!g6qtoU?@mEJ;3viIo>8X*u_>MI9|xg<=X`m#?m(-?~h79b|7 z!;vse8@XXhnC2Ezc~BhKEu&aYeMP;#6gMU%yAL}t(ll!ZYq`Pofmq%s&-WbH{e)eOUw3-SYE zfCT;G>=*z=;sEB0kdgR`()0=|7o?nXV9|85M~B~7Gl_}KBWy`tp;xK6t(G*%mMje= z{H9zxV7l*6w%K+0rm`))!RK(sv7?tv6X;8i_o(~B1DKt;*?WXD zm5fK#i#tx$Ty;3-V!P%ka!0e7Lq-#UVII(GVU&x__5xFR~4wd78NMZNa zhOKK&INMa7apw@kJ|Pq@Pl~AdrZ6u?VuL@j8m5L=@md%G;WGx4SH5oO$b zS>*My)x5c)FX=!|g8>uA27dx4pAi>gnneVP*Pk2t+#On0bL}iKSEIzDvPG+0XM+#4 z&>L_VkL#`>5Nl-@#R_*;l@??&Qf`z7C4j*)EM?0)_NMWPt+oSg9H{40%MI?OwP7qP znajX@C=7Fwj5n^R+p=@L^0sWVANWGtfA2-O9&zTYr^x|F@MC6f%W93gZ}gPP-V~<419w5=F*U1!)pwj=OX=KqeC(=2>a#JV2O#Dg2S+E@2p9 zXc0n2T(1kYHaf0sQ&I{+MhYpkg+y8tuxgLCzoyr!sQT~sO=&O=7}ni>1KMmuhyBoI zWkQjj3TJBlV5ftaY0{dOUOK#1=M{-&nmzr0yVYVf$dhjr%)*T;xrM&Myj*4U01T(0 z@9OJ8cGQqUCB=rfc85%7z_?W4G1-n86iPXEr+6+2ZsYm{!g|%--k^> z_~quBszBNDb863?UcYczj0%r@2`7)Cba-+rmw;2PfRoX%+TW(v;{YY6g!<8=*T+PaXM)X>3BBgxIwxGN z7HbuV#@mes;Fu||?$1Rq%=!owO;i~^vYAOTVi%AZTxw|aY7B~lTMa|( z%cDb;ooMA+5lA?y>5ZJ%Ds;aThqx|ygC#a za0$~O2Zj$9`<$JjcGwCEh4U61SSLg$dshumYeL)+vnZK^+te6{#^K0BQbS&-QX<1J z&m}ka&&Z9c*lfh}r<|ps01&=|H?Q-8zTQ9;`KZ?P&B3N9y{X@mvqdw4sqY)+J_NJH zg>n)tZwUQ&w^*6nxiDqFx4&?fS5O>z6DpS7#gHESSQi)7QDX8iPK{FY2+=4@%kNYn zz+Rt5hkZB<;P@`30ZjcB_RE1Wl6?qrQspbeq>83hO@;XdzFbc(j$|X5D2G6?$Y_lw zSnbPn!6$bhm|N>0Jt(Dt?UGZPT9L{iS52YZx&t@=k#xe@b~%VygN<EHNrPi-ID$FQb7e;(lQdl}U?nbm&3Icr)NVie$(#sa62*shm=l5jn^!B# zk!QlmC*hPQEaC*5aI_t|06Jd<$U_k>OAa$AZ!)3*Cb5%a?gr61)!F zxy%@(#$_-lCk&>*K$lY=0F-7PTp`bNGvr)S!buYP1ArO;DA)%Ij_8#qeDKhd^xUHG z1Nb4P1p7wVl~Tf#x#jwfRHM2G`!G~va#l7NVV8vd!x45h{!D18^H2^ztGL!57)08_ zRf6gidme{^4Vp$L8L(?#?J0njP8C>&#ln#r4Y<|{Z8C#(aENI%FwDIN3bRaDIV7$F zu(D5gOj!B%6|X*7Vioqbit#sMoU*Q}9IobBY1st99;Vp-F`@3>LU9Caem*YW!Y@~; zBbSH#(b|wW?ll9o+ILDK4}dsn$XCd-f(puB?vN=$HU=5n97Rom(ab{~dwlPZDD@` zn?_nSzt*UAXq_4ork7 z9h6Po&=gHZf?E5kc}Ai-LeL5r*qaq73kGBE=)owgA}Nj0!AH}kPt?AuGLme?sRIc^ zWmwsP16SkN?2DY+j93EJm1f>^{n#fR)DuxXhFy3e1G>F^&f&tO5$gnYie{jivRFNU zMkb2&sp*OxEK!`}HzWfABOcev^d5+s^;~>-wQyR+6_AvJNu`#g+1h#Y8fkK!sYOag0?gPXRQD5XJsa+ICG9Yhu_VPS9u%p>Y{w zRYx$sF#Oku^wB2WAJLr}X^t*nBhJuw5F1Y#1@vBL?7 zE=f)sfI=`vIgT2KX*f{lS?Mg?ag!@Yv?hbLG8w7>lcsUAtlZjhv?Ku?^%g^LKmxt7 zlhGm=gLXScHAJ~lI=y3c2&6LdQvQ)kEX_xu_ixIgQ0*i{&paapQ?fguxJXj_<_}t zs0v5cffmpT#&elLn>q9`F8zT-xHCrwG1mjYB9LHUE>H(tg1SK!N_~pFQ6}*me8y?b ze&9QSifS(#po+CXq~m1g7E!#a9`{Hv6bF6pPD=JYfe{8NR*+*>Y-MD zGmzEa8BHc!9Sj2z&~<{60(~GFiC}05(&pEfu4)i~SWvYnhq@O8FAl31(Z_&YWV*zR zvifR(fEleAX#$0rPScPxVzFpUt6a2b;i6)FN!^0Fh0E&nrAulSl-23{)g-NS9Rez) zpvibZSZLz5pkRUnDAq7oBElUq!(!0L=AsRZZUn7BI7CAWwb$byi3plkGk4jFx`ivK zK}tT52_--3u(5F&(t@L<-9m*uC=MP00^nlfG6o&#-kNMx>@H3At+;bOa|}OGsD#es z8!WSUU=^cn{y-=J*~F4EWchwF6a!KZ5Q}0)6F}EOxiTMC;2_dZ`P5?k)Ebeb76~%kV#)M8XoRt~GGS#*k4=#_XDHq;t%|G>gcJ`Dk_ zF-o$P7!nwwBo@Y_$ymS`L9!*0P=G|qpg)1q=FWjU5;iKtNUg&dG`~4sDm#ooGQmWM z5KX7HNmyuQjToW&)KZPkg8*ZTYgM=%gg&$g-$Ej+ajl^R!=o%5*3L?TmLT{-Z5fXl zt(GteLX+02ofD0=YI7Lt5S0*nZVbnDYn3=fKBmu2;#&jy!-!~28Ze(>Onq6bEiwcw z;uF)H*eY$+;_3z|Idw{4EigK3m=0AC z&=6i8ZR;#g#$xDqgmJfs2z*m+gXj~)V=)iiB)+IzwVGN4ENBca1`8RaW=JeXcV}y3 zG#nooOCco*C0Yeb6iecunDuz)z8#&Po0q;r;qBd83A<+CIbm=Nn}-oMCYY+ zc7Lq8LX!dT(aFsqY<0=jMk8kB=*h+8B)5Ze9mKDxL)j?ea37CHt7{N_E{l`{6kE@U zoeBXF3;^OM#sj1ogBcPbWn7?2x&+kMe(-8{ToTzJb{Rp&|LTQuXh6$B3`Uc$>aPhN z`Q3uj04mZ1TnDk{A)>QHPrK!VV&tj(WO^>!h^$!Bpj9uNOQ_kHY-$3u9d8TUjL5v2 zYTUxpK>>3lM%ITqjBq*Hv1;H~lFUu^09ZlLp`jWYh+ih%an0#6#^iC^6pOa%ZDfMY z10zEMDNMQ(EM2eye0zoDq#9mv8^(%OBjFF*Kh`3k)gOq)&5tu1cuqUBCe#@7$2w;+ z>GE6vN1M0Kjay4%Q@DN!b@XNgFJxWXmRN z%p6avU)WH+JPn)Wk=&8c7uPfl8mARpzr#l7F$0V=he0Ri)!1|7>6f94aiUu!Uc{51 zaruj!beMuR=}3TC3masPiy?6({IO=5bn3R*1;^1%QC7}JRC#$bJS2MN&G545-3m!q z#lWr&2$wUW9vqM9_c~4lxIUiW{Cx!XU+if3t|*MwEssgDJ>>$Z||bD9aj=xCLoP zN?PziJ0b=v&u4{jOhTB^90rdB)oltjm$9Jd_!O6KiX4mRl`O2oZ_b_#inrK^K~^Pc z4KH&*hDk<-SBy@SL9n6^6_~}wqMdS3*}mA~h(tngu|=%)uQDY4rF69OWDb*Tt|`4% zlLUi_1_hr*suOJX0?6qVEd}9cEf8rx7%Z0f$&!kk5CnBFIj9!eqLADN!~}w}EGd}^ zk5*S^khFwe!JwEb2Gd*7VJlQmAvn$F6_Mx)IVY0g&0!=7M`Lja>IT)FQ=uv=p#N9_ zj0~*sL%oHDK{8aqvXB)`xCf|(i^P#o97?2KP6Jm!_6K*T&&4bZ5xX}BsrYPx$&H*y zg>D1-xFa?aBQ^)W0)d@w*kYs$mY6NukRgVoh<4%$+@4oc+0gD?QC=%Yf%=qd9~D-+ zxclXN4^fG+QHvDO0)=sCSF8>Way0OG1{*8ZFa@`?66g{ks8ViCreaWPtyrhU;ev|L ztkX&xRVp{qnk6G37}4ekNlm&nD?`n!7ojgDfT(Ez+g%*31H3Kb#zxEr3Fkd_{y4$&x~y$XW+zvPsN0=k-$i z10fM7`7~pGPP!jfML?1y4M2`A$fC>`=_r0d`FDRHuSYlf|ik8B{YO4?%B7n1|>O?nRmos=0eB zc2a`NF{vjVEx|!>OJd0}Lye#~D34R`LkK67KJXRmb+{Bm5t%p=l9m`;lf$g>+X~L0 zilB87!ltw|BW(DAfD+wgover?gUF9Mw}5oNGBU&!8lEnkT-^diE-4IIi7U`CP7GM6 z8fFwpdLv%bF=7$oV3U>LlxaQ-79E#Cl8=^4&MM;DIi&c)3?7_AFiM#gA1jQuM2q{Q zok5D^c8Z;WN2C(vu65krHzyhVdRZ^Qz0-?j#d z5{QM`m>dT&UvI`JFh49lD`%0m_#|>jMJPnS$vKO1$~7Blf`$w$JEpjb3Rq;T7%Vrk zWKj(O=>9qvB7gd?X(BH+TH6LB5CszoFNq{V2`DKCM9CP)m7^rf&?8X1EU9+Z2dB0~ z5(BaUv6oBh`(tvk@*Bl{G=Cu7R>aa0sY9AY3>K7YV9B z+r!2pgP>T9u3H+4)9Q{$0ZP zr(xV+$x$eb;{+I?rCS_bg(^5Y5vhqZERGJH$6#d#3~)`cEzVx9qnrj|0}yY3E;okA zw8@+1aSd24bRiH%Kfpz8p=k>kmZQD|i%M1eSW$gd+@U~CzP59ZZCpm4aeMrtJy6%-P* zGmK7gqAgAv4$p<`5^t&TCnEttvTaa6B{Kz6a?;y7YJxNazEQ*_k(M-3(|o1KbASV_ z26yX#+y@P#gg`DV!lzF-Xc$+I`^?=E(UXp^6gfS)Mau;X6q@IHKPh1`x}7jLV=W8> zFp~xT4IE3vPMFKm;(-N^C6;{PsFEW(@|O@&YrtU0K&e1Wov|8cU0~{Aa>qn;F{`MU zlq#+mCqxXES!_gGWng5{JVnE-oYAT_3Y|cd#yT%##cOFj8FUz@XsiOMWI{j;8b0Wp zXoyzeC|H;~#K9B=%~Kz!b{td=^n{`mMJ#M2oj2qFA#8w+(;Ti?sD6u)Ln~BZW~5AE zGOnyHgat6{3haaj)M-vEazraIMvP!XvQcnfQ>a)R6)n2chj2{Q+Phf<(44AEjO8iW3f7plOGKNS!v-zLi4lStLa2q|5hN)r zGR)~zc1aRcVqpA#_TB@!ttxvLKf1OPrz?aKx+#GG0Yrch0wHqjI5DIMJB9fTU@5j@ zi`bU1ef#WvuB2-#%v=Ar*8i>b zoUC)VzPVU{dtOLdj)Z;=}Xh~bCHi+`bTiE7vD@Oqrguq=~F^~HsXK%y7C=3+g zX0gHSF3x8)`iQ75JUyUtzhK+nv|kvpwkyzzFF$C+MQt;)-j*N-8j8;1y*PYpsH0b7xD`E-Wfi%snZhs4O>VJg!I2pj&paXEf;=%tqiF?E-s@x%kGdDKr`>OFxA>IWRb$$ylDd)Fh4YaRM z(^pOKkF_dRzZ3@l zMs3Iksq8&WJ?iMe0Y!y@9AC+rh)~I~u$5$@+0^*(*!CV zT^88h(XYStY-d(C>Kd9BYK#673OgvY_AZzl&6!;{-gF93%uhUMlEFik@Muwoj*0Am zdZdSXwuv6Ug)O543lN@Gn}vc4q2vw5LR^1ys5#;T(KYI!8cK#0SYoqyAg}Ml>;=<1 zJr>w{)%x?a2I?_Uu1K~$H^Tl=9=2fAE0P|kr%d@-Aq{5w8`(MNywLCw56v;r9`rs~ zu)ei3O#P^t_hCSy4Lgke)##k<$>ju|(DY)EXl!YFkf(ykWn&&uFvZPHn?|=`{cBnv ziq$IC1DwHva-p%oUMz&njtN(9d$41D9@eX4+>^`?wufa7MZ+6cCsr|ih|B{}@cEe; z23E`;VU+erTYx8j$V;`2XxOCw3%f#iQ`CtbT8}P8%N5K#L+w~-1t;JWKaBMgTFCC* z=q51owP<}in7VHIm{`&Bo+ri~nBAh)qIYh@3lHqyz$Ut#YkyjYHaTe$s*A5^E;NC@ zE=2M=B3OXY`bsj4EXMS>(>*^%zG*jLAN?GQ88$w%=0pWhX!t@!dJkh>G_oiy2Nq~0 zG$tG+t>)7hYEOYChj1)pgw~e6cI=i#C+N3N*l$AS`RBAyYukkRp;*)x>}o{%u*QjM zLJYi|L63s*Oxt+^wnq_4pbHapY|J;(D_z>lCGUs@jgyu;Isx$800T+59E@T$N{pR8 zZf96!70*BN3zB;J&UZpvIMx^m)9{6N_={_Fr?2WKBfs(+v#uysv!O1*`VBam4DF!^ z3)364^Xd3gj|OBpR>YkXq0ZH2(Oxd*BiNq~v=_|NT7sB#bp?46%-h%0wMZ|vAl$_p zQdE$%&cR#?(-EGXp@x}}pOy06pPH{fvZM;+!@fE7l-MxlJ#q?OI#>LXb)2F#plw0v zvvkLoZ|5?FMg_+UGz5t*1w(0b03UU=1ZUHp`vP>4;SL$x5qJ%zt>|IV@k|clL*fwa zsDrKhQQB;Z;#+oI*u>@OJ|z`zrQnli=rn${V(tKw6HEJ-^E0P>Et0CK_d9fo zFx8`c=zb(R0YztT;b{Im8r3EvIY^S+l0-Y)Q>QobhvV#7=ZU&dR2+J5Cn1Yfig}pb z_8OV~#6jL>(YLeHg6$Zh75yw&teasJpV9Qrj8ooy$I4GIm3Kc+9zmp?~b&kMK4tFiM0M+nwt}SFik00#L;^J5q)zJI($E!N|qrpxG)=u7*GDd zLj)!tKqen1AkK$)orH`mD&l}~IOmLB$Q0O|QfboEL!x!*liM8&(%P$*WRlcMMpKa4 z{7WJDFxl=k;A)AUl9J#4+OgO;J$!0gflIqRNV~RR4<-JZoj#&MaJ{Qgtoj`U8DOJk zqEX-vmHx}1?By~ADD|~xv3Yl_pGhM)KF0r}#L+%L7ipnJmCmT1tuOp!BopkUb z@1%ScIHaIb^{^$W*>xaqHS)4C+d87$m~zcwn7DNX%_|0-=X}W)z6H_^k40#v)L-xv zWnvLJF{J+;y(n}%sVoY$V1g5z$lMb}wSzZ{4Q$o}wfH2>KaX!d>d*V`cVP2TEiECA ztC?p&5)j8R>1fCQB31hCaH7|HVi6OcRZKj35&tii@Vk2mL!qM=op|E$lO{St?};S@ znW{A+Hm{d+8d|WRrR79)Vs^u&4nYiypG7w7vtVfp7Ie5YHs~#kiH9G4BDGC6Ck-Tv z_SeGz`e}z!5k$Xl2Am?k8ES;e;N?(3i4{bT7D$5v4L!%#Or@E{L{L3VnuvnOZ-X7Dy(kC+txx!|^e>j$?V9Q`9|>0iqA!VwiNmvXsw zP?js;Dsv4VRPM?RRcboqTZKhn2 z&6LV*u3Y}DRe2l{XSP!n?kc6Ks+22N4NsGm%TueAw^q60QIpMfOPLa(Ak-cqe!f->xl<9w8OlnW0p3~ga~9-hBm8F(&U30P z^}JH)=OKGRmHA#mT(2ru_H|X}e*Qokb2%2n=mxymB2 zXwK!bQf00RPo+!w27=M#vfKfeO3inb`CD8n8FZDWTV1YH$fe@PxXQilt_pvr%jJo< zR0aWLyId97MK0x8;>6=|v z{1%rha+j;jeK*2c1>P#~?nM~)Ax-xo?)zNj-Us3TA>g!2RXyyo5)ZqS_Yqfl>JgWU zJccwq;c{hPby>OBTxFTJUCRHC%SyiMQtm8xS(hvRo~vAafG|IBS)PxOkB?mC*|iAk zQ{?wkm#b=>tK9dMt3rJbzu&to^@FP{`-4j*Hn?2LpInyrXP5H*>?%+G?6R_;-d|iU zs*O2iSqZ3TEX#72S;|{xS?MyXJW_61sdCGe>}QoF`&+K6ftJb+vdS}qtqR`|;GtGo zb{PD-E!08F@@;0R^hnFfjkHu`Tg%F9YbpPBmZho?b``?j0rDLz%eymJqY(BeU{JM- zRqo%#a>d73R(1@++!JB%ZbuETs;xT&Ze=Rf{;LfL~|1GSjTGNWG;}^_D9= z*D6cQvs7{(^p5~;LL7eR`z^~q->Qf-TV?4M#2>WEBdu0hsugK$waWb=#2-R@$5<+N z4E!vFpM{o{T4+_I+L5jfq_e}aA{|ym7Qa;|!s@ijePQ4T@UfuBTCQBTRi0XeuohXa z%o6aIBJ8D>iZ8WXRmUL>$02Q?>Uhf)IRSBo%y@X}O6TlZE&zHc~%dLvsa;waDsa0Ne8S-|SrLvb<6^VZ! zZI@eR>Yr9c@}E|@Ckb8>>AT8O?yD_V{2Hq~ajjLBxfcGeM;dNGI5$|zw*s=8EGvBz ze&1}BC2vOA-HdeNw|6DtUTLZ1N`!k0!nwtAMg9ew{mUxP-fmgmJK*OI__@QXaNlWJ z{yXveUhwa;T>t{=c^N<8&X!kG7KU4UIR{hHSp=e$7&(OVpWBvuqR)+rX>OU&s ztdnpIz5Vyz!yh8)sStfbM+jer6lLg0;rADPL!Ux-OIdnM_=cYAV*UxjH}rp3e-Nq| zg@2obW9Ywye}nK16{sppX8G^qOddkZze^Th6R~TO@cweEjgm2QlS_;fB z`KmMmp++7OIn6B!{~39Q$cGmS@!QCs6#2*!@?)Oh^o}SY|BJ{6myn+&@=Z#}uMoMr zg#0#TQzxD_5Xa6AnwSN%*XCkNh zXW>5+|JQ#I|4)Aq{x2do{f3FdgztKi%fD3lZ7T9o^=lWAm$L6@kq2DG>}s~(uoC_!i@a3*Izi;6>hIYiFV#LS z5_u{6T`BTX_8qk?NTE9l9V2v{P_NK_AF)5T(5-~-EOalS^1T_f~;p3Pl(6is;{9PENw zw9v6a4;ES{)FCJOyR#HCcTaQqxk7>@qdzdmP!1^PydfO zJx6|1oUgLi83%qW=2r^8#i75Y@WT%N{=z@e!9P;?V}C3Tzg^OEu0y{?^qu@};XCv7 zeU8Ju)DceLtKReVw-1?rtwa9|3Eydli-doRL;ot_-{s(6Uqb(8;XCbrxA2|uQh8F{ ze58b&{w|%)jxh1PBzh*EHG)m}Zwofz8Eoun(q+U zetijkQwjdk68tMm@E(?BqPq|LE#~H#m{hcSw{L6)J`a7KWsOK@aIQVo1 zB&GF!2jAbt{1+Ykl<+@u@}sQ(n}e@#rX+>GiOly*dXmDgf{f%#AjoCAcP`k%4qG+%N39DR5(Hu_{wI%{g%PuQNTH!wBw3BrHh!Jj4kFCF|7g}?ca z#o;Uy{&o)j#U=Px3*XG^OnOqnpWq1RJ>i@AoYDVH_#ub>E^9e`w>kKx-5w%xXLz%O z|G2~7V&T8(;9n~Ij~)E$gul+gH}*I7Hug34H1;#p*yl$HXTP5~UnajMe^Z2S(rwad z(q+;?b&viPo)1Ns_@l)UHvjf^wBHqtz1K6NPxjpn&e?vmQvc7A`fl=@UCiy}0^ytd z`fw&IrRgcLqv6Me|AvE~5Pr_VPYU1lDcjA&n-cyI;hT1fb6|D6TMOUxGa2FU<M^e<%I9-4}Hs6-`TH5 zgzxNE-Dj}Av)}Ux-`P(_gzxP4;=*_KlL_HF$Az5mo#TT4EKW}q>K2Vr@jn>9mWh0t z*z;m3cQd{vL_SC4D;#n}p~t~0`cd%D^hcGmxgRq9(b{>v_e-VvwZk3#T0s=O{-l15 z_SyWBUeg~%#4b?>KQ4RQ^dh0x2{rqN_XvJS=x0KI5jsTXb6W`=Ep&gO6NFX^ zJyfVq=mMeRrC&4sTZ7UyB;t2BB9p_y+i1O zLLU?Qg3vWWPnULfme9+DCWYQ4^d6xP2z^QD+d{tcEuJ55P`vIvx3h%$1Z>0ZVooC`-F5#{DgLp^&srPx#USl{Uny1k}qQ+^4 zJPsJd{m<;dOphJH^xUCL+lMjT-_7*M;Y|Ow3DZd#t$)8jW|dgELac?z=71BerAuozR!JXMA22(?fS)`mxZHcVv9&PE1$s%rrZS>886f zoiv*1F}pEM?9Mc`2h+?Lrfc_Pn%#@(5D!z=K1@AhnNHuA>52O>y={M{OU5ysbpX>p zjb|D@km>0Ym|l4h(-#kBT5$-|-6k;|;bl6$n(0xKnVwq1bVV)Gwkb?^sbl(@(95SX z4oqWuRz1_34`uqF!di@bh zAN&*3TmFaXV}EA)`H@UFIhyG%O-!5oOs@+tT{53(T{F|8TbQ02WV&(z(^;)d-xs>F zjq$n=(>)e4ZE9zFK?l=xC)1n4OdpFdJ@;6q$8<40C(5)t#`L0Yrgtr3`ubv~zb;|A z!*NWf9M3d*0@K8aOz-;((+~g3wBjVDd!NiS@HeI>ox=30Q<*+^8q?O(neMWT>6|l| zo^~eF)LBe-JDX|wIZXd0bmqB?t@D`fl3?olJJV(7Gri>krmHVxdjCaCZ@-x7ic6Tj zw4CX)mok0wGN#}BgXvcPWP11&OqX8Cbo(UJcZ8~|8GmEwHH?3~mg(cyiTrw|!&fk! za3j;^o0y(;Gt;M6GX3cmraRrnbb5+u_rI84b34-)?qK@solJMTo9W^AFgHnrcWL5O)=i!7~fV;E$*+^3ie9?o@vE$6Rx4Ze$C-$O6aXE!8hTLp0V-pCppr; z+%ZnBXz1O)9sYkgzM1^=(LM|GS)k7XeHQ4mK%WKrEYN3xJ`40&pw9w*7U;7;p9T6X z&}V@@3-no_&jNiG=(E88ixwEZQvT~* zH4)F;nuaM2)&Ir+$qkeJ+f+x~|Ap#*6I?Gk{ynQBRgua_m48_E5jB+Jh{wNmbz^Nq zb!23((N4~D{chv=`07UbUpsq>&RwnlQPO$>7py;g5_N9jfmvSJpgG*xdossSsvKPZ|2y~E*!S-NtEEMjn4Yky?1v*=U zGsCgaf>3jSnCusU`Rw?KHEZ&rHBB|ss~a1ecIF(l8xPU=q@D_sL$QuPq`o828f*w6 z_shswjzDy-ReXFelJ2f(81`E&Ci}e?-%}O^J7X%I*@%jcUd2cDj9^Bvvs=aE8}UmT z^|4?_FPWbe33g7ME>=Z-=p}Ki;tMSaw%3KbIs!;&ZX=Q9%X{N_ZbUT=c5Xc3v%64V zQ75Xqf&mqG_ssj;P;jw|r^|Xu=Aqh#AzS;4q}7#-mm9SiQ)XA^D*y+wqDP*JwM>9j ziOs26*9I2^y4zz7-JR3J;mC||OHg(7lECV&uE0_iP4`l*YSCb*S@oz(wIN-l16@lu zZb4M+UP^RMG}tAHi>i20u9})^>QL0^1v+Eg5Np~4(P%?(L9i>>i3Z!Xs$beeRcv8R zU3EM9jzBDI0#otCI%7(Q#?0>ac1j6+i9I9%+AJ)f#;=Wm&Z2!XiZv=eNRZDl8urE& z<_6lkg9R#E_Ap;{41LV}ZuCad*+kd&V*Sb8oh|LX+kZxPEHJ-4*dv^cwz0x~rv|N= zy392_s#Lzx&Ft=&PdXl$$I+LVKC+j#KD!Mq0sVGCYu~De|5@{oL8pkiW8HhF3B9;3 z8;wl9A77PtAhC+no5J&tX>JO3L}E*ungh*ku+i#YO~I!JR5?@@X6OU2#`JMHLzj>J_yJj3VIcvQ&^%<|Q@AU@Ry_Kr(k;HZ_R;zZ*Rz*dvSBI;3h0 z5iP+bB(2Iz8^c{OEk=b?(a~DKgk39`oI(r{hf-rf z086wCK~l@EuO;=kOY^wSb83jatdL!ph6D>5FuDh#+Azz7!`&@(DaRB>vPgjLr@oVW z9Ti={BI)VE9g#qn4l7l}*Y%Bxt47)#0$o)?qb+~|2_+Vt8K%lZ@uv$_k*$4J z=xO%UxD+$44op?Jyksh*qHA4SpaD*;Za%gfRk$`3jkE`r)&*KHtHGGD$kxk=UNjnR z4pCzacSnQOoh_Qy=;@3v91ZadOGP)Zx`a0w4YWl!wYa6D6TWV2?d^i7h_uqO9!doo zWPn7G0;QI?%Zs>E105ZK+0^62v#4f~Y;CbD7;{)VS0ta)8H(kj-cTf)i~bw^7LQ^GIbNnqThC-0 zbw^py&U?tvUrfjBqFt*;8@nS^SEHD+*HeeMD1hk%ik`ipY$TPK4t7Q{SDCz27dGiE zw{@^y2z8oTJv|iFMVVwVbruo^cStLk&r@4tgFtlQ>~N7N^)8N%njYInJ=wwEEiG;G$4-kQ&yEK&-h9 zJ;s#wUqR;zip7_%6QzVcQCMy(%m{RX*Ojk^NvDX4p8{Rs6f^`| zQGsY4O%^uRrCH~E%v{M!YU5rSf*oPh;zA#*oIV;cw`%Eb57O8p(`E`e-9rzxswf>Z zukCPKW%O2W}~Fju5xG${HF<(Rn7Y}75GzO#YmtKh~fG?!QP;jX%_ za7TVnURE*E+cc*JD+JxT+NGyWRPe?Jl8~Rn(I80yF3$^*_|{IDW+gSdY5sA+t}v;s zV2r&Ftki*1j5~rI%@NFk+jNnyvYF9f%rMi;jD|2~M;Bnn1bZ(O!fN41u-y?`V6-#B zwYOmw1VXy2TSI~Xf*`+H0%&Vke-T`(VOvW`CoQK@dzhz18#H2X@qX~u(%x)j%NV1{ z>q?$r7+s2O02qeQg7z@yH>puaxP?S33|K%Yr4eU?@G6b5bSIG30PD%v&h;~A)lM0= zA8}Xr8}4mtZJ&%4IPKeX^Q#ngf2%z~EQdKs%HvPqli z!&I;X`)pkyihYI6Z|t65pr2ws?Jz{J4k6W5{bBj`XfUWNRhm!-mRU`K`O$DYR;^93 z5U&MR+alu3$3(a(gwcS)$gs%NkTn|W%1Y9#Ky*RcG;0N;+LAbzhfR?{3|Z*hXB-JO z^v7CKuiFCM=qZAF^6d+U+kIg@ZHiZxl6OaSYg=Y3V8f|{`WuYsw3IVb#e}UOD?^O@ z!LG3s`EsWy-GJDh+gVBUxI*m|ib8wFYC+NU)hU21&NM zP_{6L7OocoB+58NEieO5>DCmAI#5zenlOotb%ph8Guu-d!t{y4T-#Hmw`C#9^%Qk? z8sUaQAquZB3-JMDhHf6}mk~G?yNo)z1R=dmfC!cgFjXaKFiO5{=i2IqdG#}!rW`T5 zaVDj7g_hC`o=}PqdNww%2Kjy!#=4q2bXCb1$cv;k21X;gFUktGJKVLx zK<7X)=LB|2a2ssQ;l&41%3@s-+%f}Wx)(_ZjD@>(=enG5w>|q$5?mDR%;#kVaTa%l zbUEXX$C_|1)37FWg)6Kcay6@4%cAP*vJUmgkS;O}OmZArX}C#UJhW9^IBXH7RHzGR zN!WHm)zoWxwcZV;`q{mJx``%r*YG7+ZN(N~1GOpIhE+^gQv?GNgj6}3)Jwy$5f|*3 z-@dd-eKmZ(x^oL%FU^eO*)8-Qk@~1wSsiUnLA_ZKY-+=15Ox>-?P_gbuvmS*1syM- zklL4B==7V^1!W5sP@{jXR#{7$)Vs*o9hR!#Tp$Ax%#U<#)U6hZ0x3e;o79;jTHBj+ z(NHd7W?GB>YqR!nphY)Bl425Z&t@_8z-DDCw=K?d#8BC!UbI@(Y3>DBf;OoiM?@CG zREYiWo2fDk0bg3p;f@Zph*Qd1g7Oo8$z##dGpK&Ic&UGGS(!?1r^-}@9$9+iw!^sz zlspPp-B?B>+N2UA%2a%NL`~xzC70!2lwpzHq@EnA*QqEDUa0EvR8QVs+5$F@wFP4V z^#cB)EI~oU=Bvxf+tdm-ilVzSM5Uyjb2oQ|+uQRV-`zr4F_>pXc{I|bo*%3R)TwhS zFzrweR@AHI{Tnf4)AC(iTG>GFJi2Jj_0)hFfrUYp9pIyhZ<^?yWErDhg0N zIvAEiX8P4&J*#V>I;EdhSNHYP8vp4}v9}!FjUB|V22z3qTsoN7akCbXUQ|6WSd}-b z8~ROFr}RGz!$Wn;q5y3VKiq%Xj3BB*b5uRiUs-JdOy}?FS6ip9L)`IEFl3{88@_V= zY`uGm0=VD!`kZ`S312DrvcgN$@BQ>k7WHW*LYl(73;Hwf(g7%@Y2Zb5cBatQq`JL7 zdRLvT(-BN|8Y)WaP^vPA;gOk5a1IhQ*ChCqO1(3IsyrFdU069wHw_dQ8}ppOv<25F zbsZt*=DKDzj#cL&J=u8_}K+(qM4Nw;%@995Nh)0Y5Z3Ari zO#{^j0}J#Y8lWB{TOAGkL=zslCRMJoSGn-a>1XnK{ADaxB1zfF1MT`EoK#_u=MPlZ z4lGxx>s>Bv|DoxjUaQx@$*rJJ<2O=xc7oCa)u-^C*L-21dK=2|n@BmelAeiM_477* z=JYe0LLlXnvF7Sj*9@A1`hMe}#$ap~I@V~QBZ6gptGaCvm30HX;Zt`FDp$!n$ba%K zdS>uMPh^i01)&-hRD%;{(9XZ`zEdS-+3Q=-=IKZ1&u9VE)QL_$)X}2O z9NLV%1RExXB7B5~xF}3Zj}{$f?fLJa;V2IvHtS({NUhc$@I=oQZHi($E~wrZN~2{K z;^b45I&Al#7M(n-J-irn*)a6Ci}8NADT=pIZR*@%$OZHeyMD%>roe(>b=5E$<<@Mb ztR@+A=XWo_c2KklOYKk#)`LxI^)O7zDeKQ6l%)1KU%ff31^9)P~UHg!g+yGmU)rVq=#{Jx4T7siV;>qTJ5Gc z=IRZElUSpT@eVx`@6t2*zBY{d)UCdPLi%$Z*cbH7en}!)Ej6iS!_`-{s!T$>X1Jv7 z#^E~X+lIquPWxG}1KmJA=j-jhQUpx=BlmTC)ift|LHU z>rzn9Y=Xju1u*-05z@>rI-&ZR`=tz1khSFJH%gR_L~Ymv;j7=k&4CLaztb}nFUK=} zGER!F&=cuLN3;c^OWmt(^7)iaywg~yAV(uBGR(=oMX zQ$17Ezu(!ESKA+M3f(Ajjp@y2n^NNRN-c-_5S#UIrq0?d7NYl~mu!X{T&cYz>6uNIyPD8)(1kRIQN?bCJf|h8tF# zRZ3eVP3rGkAm-~J8ZNb&wifhM=&UVf1l09gO<58PcGBi6?ZaXbfzrrqMe~}!Z{4WY zY*Vi`Y&SdD)e-8X_Hp_44eFzvYIwnP{;1lZdSKL{VZ3oblGRVU%+aj~8}jJoT4aEX zs!K=D?QUNfD2O0FW}eFKIW5$J?JxbJKC@3Yy&6XwEd#TFd} zote57xk^I_vuG9W;)41^iYK>FAJoFow`s9{rc(4f@h|TPrzQS8*=;}wiQ=9^>hNif z4*jbwZAJCaZm2^W5R0W>Ux+@x`i`pPR#Dmru+@>5>koh#es-C)T^laKg$&PLa z>Q`MY_4=EK=Aa#SRGl`)By;aNTB7k#qTiEz>6zJ>|0d=c@2tid!Dv9OsK&_ErS9FP zJrv=Ok2nKwj1D)418V(v^p7xuG5L$zU_p-eWa{~yQH{?f3oH&q)YsdfVSTcFTX?a= z|JXjtn#|*vTDC1FD*XQA+-+e!_(CN(4}PsK^}x1mOQW<0&%42xWYODLy|*F_SDilw zlj-K_mKJ)OqTbjx)PXnKGs5VWXrFI)m@`2k=J3?LZ%cPXeZTF34)qISOP)_$TJE2@ zU0?|o{iuLh@KAmikOZBrx@J4{aA)tR229c0WZ!H@h46b-jXHG)ylBF5N&RC7l*m;( z)Z$fxTC;;)Fz@Uj*}Y|q)7R5U z^ap0teAK%;)u>l?M&4{`Vi#oV!Cg3$Hh0}FlusLew~L+MFLu>c@UzkS-K08e56RDp zy&T!MJ+DJ=7r%-!m&B7stuA%Z4*c=h{X4)4S8LfdB%{|y_-1XPx_bvM+w;+k(^^C| z?wK8?P&HC7!dv`W@|MxhoPH**>t9yABotLY?J!yQnkv5IGj=pVukFa4o4R%nli4WR>P_P)ztq{?KZ#n8y4pJh&lgSOxwsldM*)@@U+UzYOwGJy zH*F>TWk^)ri@c<;zAR9^#a6wlNc9e=X8uD-PCP?)K^4{I_xeusjZwW~x?yK^%g!(mgb3{o zdZyl_&`=^`%%^tdB6$I6OTMMk_BI8@CHei%RIV5A;^;$O9*tEKO75gl>ZegCw0HWu z?27UJE~<=seRa>MM&zpnb7f(CH%jW&a^x=iE~NyMD(pzUF-m`)#!_lL=nnQ&mUvrd zyuFOJm&@wRyid>E2mM{T?%1)f-38hBkOH<@x1%kzL~XbV8d+#S@1r79*cxx`!X|kN ztuqT9Xnsu3tbQgx(ZBK3D@Xl_$@#n1N2TVeD@Q|rEu{Ubmjt8gxn0@N*yY+4)fV~M zRhLqB*Qs;xa`)q1=U_)kKA2JGz;EJH3P1fBJ>y@{GxeqBtfLqUwND>yC;0++%zQ;^ zi5xw%`kDM%bABR^G{I5#jP3|DV2!Gl@1ZQ6jMbx2M3^T%hX7JP>v(>lXYPzjJdgRctE-C37AWB?7%+!VS zOzUUjBKn=t&(y{AJNHj|Cay#T{i5p3-F@of-REIui_NBh`q%Dyn8Ig(&DgY35A3ef z{^;(7*7%G{3*He?l2VE4dGqcv1nF_|9w@}GB89YmCafFN-*u@Wow^A_@BPfH))Lw_59jk`ag z56T>ZEZOx{JmGy0jnn|`P7q1YO`yB5%T z_J%R)IW(N~Dlq%&X{&m73^m`6#%RNQKBk@rG4=JB8R~~IHEQ{uwE_BmSlthQiTiZ$ z570BGpNR)4c=`y4_6*P7Q%3C7P)E$67P-5b3A% zGoznb{fs|O{Ol7H5zn~4-!l-3@dp^{@_m)1huCxWYHY){c}tCci+J^3$mQxvGn`%y zn0QWy|2)DO)Tqwbdv+*>3Uuw>lw)<%-eiSKJ=H4XnT4f4eTj4GJ~Pz1eF`hq-^bRd z8^_8AGj^qTb*#3ejo%uJF)*K$EA}SSq&&z2;zl|$^h{?eU6?G`p57m;Dk%vUcyvMB z9h#)C4ve?ZTiex^Ruzx6Zqk^YS* z0+cU;)W!QoV|t@kS5S5PzVQDs>198zbkS5JjM?Z)a1)U%txg} zX^N6KfR=l!_sg$kKiZF0xu5TcwdUR9=BTyf>eR{uFqauG%iC8Etd$ph*G*_pKOdw& z)5|Y#o;|^VGWsWA(QXFFdjLaj{ofG@pHUKkf>o^qxKt<@=+~@G@LdF=(z@ zHqO+#&koSjAl=$Jw*&%ds%ao;83?~jwd=Gh7G zcN6(2+$+$7_n8Oim!E9G%e|SB=9@49y+2`qZ z`UTCwW<8?9E3onTwcMo>R7JiFzcOX`(u9Vh*{1qxVvYLuA+zzu;1pys@j3-%(tPHD zQu23CLLT3SsDe`Y*nzqiTYXTzNBioaqJ*zM$aJ|MAb*+nDV5nT=$W`=AVQH~&zK;S z!1yG`Wb(90l%j7YvH8zL%CeUXbeTafh2-R}8;Iai*CV)c3GPV~+}Yaq2JxMM@5BvS z`$lLF3mw$LldXr%Z@I{A4j&cPwTk;L(i;!CZEy2 z^)vpw{;i+s4E;{yoea$MqUP&o>LtzB&&*2$?e6ugLtv*@wCq(%w_d?e$=!8`WBGCg z)s5F|9rdbHVfiF(YikeD>$7!-sPo|^@j3;be21R#cj=jZPyc?Op1BY8?@#EN*3ZOe zn*TXHvtQCPwN6X*Gxv?=e5*~2wW!KWLUsNP{TR=$Kc1AIUw=Wde@FUB{Y>j;V!aMc zKQsE7)z8%T#Lwwx{0I7-(9ay6SR>MM_X$0te8#qId&I zy^hZV@Yi>OUaHpHbLw3Lm%dwjTSd?Gy_A6Zxz!E$`?smn>*lFTCKvvFh-Uph+5D}F zh7Z&zi(7mxK8R^WOWbQQPb1T`tw^&F`p9W7^~B_IwIMwm5s3WD$+5z};`(+n_N4IQ zc{X-C)sIk)KRg_1TBe@~JXQI$U^~4Zx~B$zC1UwEQmQmyzty~y_+@SWBvSj*{n1nIEsI4`MHk3(ZsG53~Q^$@sOaFPB;>6y~cHToHU zj5t|-emercKh)2)`nl$fO~Ls>KT~gRO5bhdw)J@2^ep`yx@awoS)RHOzZ%TxX1+|L+5wreOh(Lb->@Abkj;?0%CVcmE|RnGE6=qF`` zOh*6Q$qLE@Vn-kz54?C%C?vnv;{)`=CCf4{FAQSRfnXs^xZGZUa3PD!65tv2#iJ^$w$|5W7j1_mK%QG&rjs@cLdUJy)sY%O^ zVuU5!ZVDv@Qzk0>uzt=;Sl$eL=UhJ2K(F6T1p?C{cz3ok61V(rU&NOl=<}fO$~; zdB&wigFf@IOHrd)@dZ=gR+g#-Zr{aHNze=Tvec2F%lEdles-p%(ynr~&EG8L20aqr zK8=LD;{d$(K{_Hw@j2lq2~Hs$V5^G+SG~gcM#0H<7(Ze31-~sgGuNg4e)=A12s;HREQ%?i(0)82|XTh05KWfIIO${j zh+yw*#xDr=AHn#2!RG$O!4o-tbMNB*g3W!4M+!FgES@CT+^=|xU~{kHn+D5$iWP@Q zeTZ@VTNo_&$5jh9_bDzCZ0?u)r@?Zs+$zE5KDn0#n|tIo2sZb}?J$YsH}}R(5p3>@ zYY}Yji91!WxgYLk!RB7Lrv#h(;64*=WD~I3jq3U~_NUOM(+3A6m=d~z8AKCl~&ewx$su;7f~7Y%-x z z91^U)WxPaiO7NM2{ok?tQo$L)Hw*TzXZbzGzu+ebS16VFf#q)r&I$gOFji=3%vDe< z>Nmm5F^3^MWU5lz4T>v2=17EVfk{6u_(+lujjQ+omY*ninc$lRCj`Gj{;}RM_t<On|pNnAI9OE z`vMLYZ0-x_7CgFf(fu(^+L`{`njdF=l%!9KYsr$ey0H|Jcz<{rR1 z1e^PEGK9B4`OfF?-W5Dv@H)W<3jRf~SMcB&?0=fzZGgAiBCb}na(D*_Huq#V2{!k` z{*C;PM15~#{nG`%8{V7S`u7V?irwC36K1#5A zTh33+V3D6A*dzD`!5P76gSTV%Sm4eh1?(1$(4ERrcrfBqY2Y48DN%Ckplmo+mhd zgQUmc1&l8iOn+fO`MJm7n;E|-IPwq1-x_=j=s-nI4$_kg0s>-j}x40;qWdp`fZHy@=>S9 z|4+uR2(G%E@%Mt=H!|LAAnW_kk&*p8f+LqOo-R0d1>>M#|02dG3C>9Uy3qKS^2Vzz zot~ule@bxbLiUfBCt4nv!}wRhUMat=2C+OX>GcZsTq)@>Si(O^uv_ZG)yBWn$NL0V zNqM{~II)t$``X}k#)AiQ{3(fljNpuvPqknr^{Yj&cLn=DRd8DHb%Na&v;1Mf5y?+h zuwUZ)U2v7?Z#9I|la=;xkl>t@*F3?V%Q*a{f)f`pzS!uW&-gaM5!pX^QgB?#?>)ik zd93dm%JF9;eOn2R-^B6>g8kPqo@?;+jJpIUMV=5`CGuMZXGQ+B(U<+1cLgUNtJPq6>F2CruQkYHaU<5L9Z#2(9y{56)}D%dUK?PG!y z(m%c?I4A!XrTqWgokYFYA`&oj$Z*q7c z!Rfac|JCRVzSPLYzK;s_Nc?XIcFTHXo#4z?9NuuO-6(xNsejuERx+R7UvQ=$>rXTI zdA8>fMqlbrr(pjG){hHLiG3~*?34PxLa=)e)_>IKOZ|IOaC&c+e{KAWet)dJDE*Pq zEZS1;-`7Ckb{-dsr^GO6v11g8h%N|HlP; zB)-=T-k0P5RB%@CuY%K3U$??u2IVg;{>KScg6jqQzv1|f7Mv8kSa7bK<2%n_>F=&E z{=a4a_X$>CF@8pHmB>F9oE7}D;JB3kaEvvS-qcu*Zy&))sSlF`yEkX~;exZ$Uo;E$ zOM5;+a7Oxr<$|jue=7w?q<%dnSP6bta8~qx7MzsyZ?YAqHz)Ofd%;TT-+@Ms_r1uL znkhK4594EuT=IL4!6Ls=uvhBi{enHB|Ayd*;2#AmsjtJf=Jds-evT5HlJE`?oD==S zja=dn3(oG!={-Ym)ozS07wi%Jdj$JLo)PSp_&yXIm-v1Y?3emDbQ?~uU&g2H1*Zh> zW8~7Fyn>Tb-hVb&`lpD|m-Z1CoDh78VDHYHpBn_bCBF{{P6>WlaFxt|J{6o5{DWZc zmR$dT6&!h==MS4~EBO<=tKfvpZw?`hI`*N=r|Lu=m-)~f!6_Gq-!3>U_$0vz!50hm z3cf*bPVj?*{rxz+mjuTJuM?acDETq|2QePD9p@(}@*M^HD_QOlTs464WWh@G=Lk-q z(Np_aVEnro#{@?NpCdT4mhE|kV4v9M7UN&=gT}w$=LA;?en&7qy3p}|X0X`jC&4L^ zZ?Zk-H!FBo!8xo$DE*TJCpY2n4i)T``f-$Czu={UBQpQJRIo?z-GbdxpPn@OUvYdd z3$FT_@q0%93&uIZs88)Oe_t>1%w;SeRK@v8Wf^ZRIQtgk-32G#W_*xfuhiee1m`5Y zqm2IxSwAW`CGtxIC&m7^2#zmj{l|@eALG}I{|gv@Z1A3pe-P|Hg7KgoI6sMbj7J%~ zH{;2I-GW;M$7i$r3?tu}aZ+$oK=sHRgEma+W43J{9ABZ?DLl3oY?ax!EsPZ-;kX+JkKb` zqXcIKdj;qI!tzGJDal`(;3_Gk@A`#*dzA%lfiRY|0Kc5KQTVv;QwKKmEee^_in+Ng)DzYa8C5U6r4DQ<%3}- zN{>Itcpt$T(Vs3jC*^aL;JB2}Uj%yvUrHG5H6`u!3Xxa+!r`wp`WqPEFF5-X<7W*1 zp795Q)4wwAw+p8?DR?(vBpLm10C4s zzzZGtBnM79@ZAplr~|*`z#lnq&VjAb#p&JLfp>M_$qu~0fsb?Ga~wG3z|T1FI>!A? zc@Eo6r_aU*IPf$FUf{rS2R_e%S32-R4*Z$}=Nxzl_oH_Dc6Q(i4lMnyt>5mDpX9)o zIq+=`{DcF)>A-`y-?if#?Z93KKHPzib>Q>zL&8b{&NoesYCyF2j0x1?P;fXcL%OvY^Qgj1E1@_*D)Sw?0Y|BJN!2s_!kG> zVxQvl?8VrQe~JSi>Cj)w*tW+x4t$eC{~-r{m9d@vuO0G%V~f)_igBgM@BWO-4W8k^ z%?=!8Y^V1khx`f$zQKX-a^OcD_+r z;JFU`X9sR^;C2U&I`DB09CzT;9XR2@7d!BEjA?4E|J~`p4?FPF4!p*JKXTxl1FQYC z{q6V$I`EbbJj#Lhb>LbDp5wp)#&-Q%+fexQLD6?|SHWEkcMaUNaM!`%vjBAi+zPlG;ckMv8Ez%qEpWHO-3FI}`xo5p zaCgAn2}j@B{TuFWxOd^QaPPx?0QVu>M{pm*eFC=@?n}6Ja9_dY;J$|Y2JTz9@8H(M zeGm5o+y=NG;eLYq8SWRjU*Ud(`yEbU#_58y;BZQ`Du=6p>j&2#4rlKw`j*cPHymyg zIGho!Hig46)oOFNE#OANZ3(wE+%|CA!fgk)JzN#s4si4h;7)Km!;OO51#VZk(Qv!L z?GCpG+!(k$;r4>t8_ol_58PO|ec|Za!TsUJ!5siM9_~Q632+C&9Sk=S?hv@UkjJ~> z?t#OrWpy9i{csP!eG2y(9R9@zKzAVAAh^MBL*RzOZ3Q<8uCIRg)$jis>vxqZzUYwt zuDrLu@b0Nq_=ny-rSj+EJq7Zo-lYWgpLerD!QaCd#J5+$U#o9aujr55H>xRr7vDpN zlWS6C{=A;moPVBQ+HOwGg1m_Rof<{4t?$p@7bpLHs7eI}}XqG=)1K0#CxA_wf-?5N=v9*|0B6-pq5Y<;vtFMK%> zlqJq{U71^Ojx00i{MerMAg;q4#45H%@8`{Ou=P0UCZBpjh=k6jvk%|R>zRn?G^9MY zAd}?1C~w3t2Rr6{$kA$sfm0W8_i~pVBw>A4$P|4FWucmVgh&BX9~D}F`DoDshVuZV z0+Gp4VcdqN&$#SGSI%kdMM9@w7O?4T(*jsrCURIP>=W)XjYQXf5p{B1MW{z^}DJ?iwt1vy%U+}qdJ$&)8bUoPSV7eXxbG%H?7<$P!M@>h;^aw|v=_B0U zjy7gdsgsgf>XVyN_!urZuBbN=pH$SF#BsDyZz^=yQE&XB(kxC2i_9@C#h&G;uwGLu zvfh#_qMnHnL9eONh11&Vvv)b4V$@UCqZ-q2!AB#@(QQ5a*n^>?0gz8~%A2!sK*lw0ZG8Xx+C#6UmxYwGtd&< z1H6@L{SXyrx@U|%`$u?^%oiLV*CREol0QAJgo1sfTnV{8wyuP%cziT<(oSCQHQg5X ztm(EWfBs#Gn0sjowr7&bsdpuO^O1ZGVg4+`VnP1U%wqnWf&&{Jk{+i!7R%@5kH0G= zFz0+a$gP(Q-7ORH2=4`MN@t~v!F}T z=&WTml7=4hZQ;un08ciHd3-v(iJ@Rb7Pb5(7)Hljo>F+3f{~fN zMW4*?WSVpOoqT=Nual{d@^>w&zO)mus>r2Imp*b8ReHUnD-X0Jkc)L%R64fogjwq!Z?(}Z9od3eK_4(JZ~Z zF~9WL{yJl(73J^OC}PX4LToI$x>Ty0^lXM-bkUOzpl}&Pu{xJH#4DcUJ33_9|Kc1M zENHYx9K)VJ0bj?5ITLL<+ULv{GK;P!u?4+#?i7lwaVQ=e>YdA;Vds}NMcT+~58a}R zflL~TZyYjQM=#zZDBVf3<&i)~mko}i%Ld03a*v>lpht~@C-jp&^i4l$E=e*;7JhHR z=v#HT-|7*Z{nHXEdNNQ##!J7V zBuH~B;lZpSN{QucdXsrhWKr*6Tfy!f3OOahH7gHiQE~W%w|W%EobPY!NQhM;7803d zWsyx}HCe>#Sv?Atms%4yqM5tTI8|WSSDnEN#g_3WEA zn{Wp#MS`W1^Wscfq*pe|hLjq8Ii}6YU!7^Q^qreF!W%ihdei3QZ{Q>r1!-TNX&8Kk zW)Uj9J5vh^Z+ImJU1BablN)GXRc;tvIQ~2k#e7Bd=@ve^z?`tT@f^RG=pvE})iv(r zPIb-4Wd=pps%vI1SF~$>OyBOwKkXYnQQau|B~5q-Eth^4)**#UQwy)Hr-ahQ@AkdD z4!J`ZbzFl^ce>k`q?^0!c}*!7+nc3iv<)lxXly>NOD8`C7atq0x%Jp!x${`yqNqc@ zDC$&T+>GG>@!)cH$Mx#M-RK?bqEm++px|=#0&dYQ>xPeav~pc`0jCjndly1|LAo%e z24nf#*YiB*<=Z7CbXEAqg!UcWC6zX+(R=9A%7-5mXaeI5QFZ<5--r01mdtRc;ytpV4M*+9k@Wp~&Oj@8&Qd(qe z?V%S#Yku!W)}mqqGs|JzncW@pgI%*0kj1dDv@J+Cpa)wH?+$SLM7yDvA*5xLusUd8 z8fnuvFc}`N23mrVFowlqi}fOMW_}|o`Rn2(iKMIJ?-@%0ySEs65%&n4a?h8|OLWAb zTjvWP*`h&THs2f5wk7$rRdHcq{^I#wy^%WKJm0I>c~yOZ5~gOeaOF!BR}Hq)%{L-y z2t;GSE=N=nFCXWw+#u7J z8*H`%kr=R=0{S*HY*V62;L47sB@uabZw7`)SGW}$mU@iF&LvX5Q56bSHPzJVt3dIQ zQ?Ro|UV93UcNFm=UEd32G<6TtP1RS_z|j7xu{ffe$PK>{*IR1=3{q1!8N*^bR=AO+ z)!j=%?IB#2J7r0*nFF8GfKP%7b?`bS+}+h2jMj#_aGx7yb44nMlEa%9inZ0Z;6jwp zf)LrngaL)xKrA3vQ=*V~hp9kZ*WKAnm$_|3Wd?4I}G~67*T{?QrT;18CDQ`$%=@&A3|Esfe>up;(0O6D;|>X4!x|NDM3v&*|^w=c0cJG(oaot^74>qbyJ zDT4dO0aS9c%smM^hpnBaPCp@~^Qakxd)ZKz;lVC#Z0A^1=T(*@)LoisWg=ePYmXs( zvHF~ayDlZJgNsnP2(df6Yy6(Zx-9A1Ewv-xF7b4&Y7! zDYEMe_D!*qOVHW04u9Mvs%Fqj6xbwC_&C7*R>@7RlhE6?KsS-{cBYuh|zXBNBU=AHDIp{M=p0HImM}jP0j( z5M6%v+c%vB%QeayV=>lB8~&b~@x zveEf2S#q8D7Wp*Qy<9W=Z9~oA0KC4lzu;`#Ce`gRR2?pE7>zx3tBZeJ2L7(L<=R~4 zg34QlcM>?EFA0 z+{`egyDE0Hd^D6-?}Hgk@JHCDcbih&u#;+Kb+K_M_HwFtfYD0nsL2f080_+pgMscJ zBs(_3Mwv&CNccIeqef(M48t*T`-=@am_%;&8v!jD>cHJE1D5L^)A~)Fq52JexNnzi zF11BO5{Mal5;kMRkA#H9Cd@7}?$}j0x}|m3#Xs{<^w2IRb-*)@8r`d7=rg&O8Rtjv zM7TdQ7qt=u8%e*)kEARMImIq{!014(_Ce;6r;7BId#chrDyl8v6eLNr%SkouM>sDJ z!8S~>^dK~lU5#2YP|RfP&Y;-*76r-vaFmQO+Pn{Xa$!!$lxLa?E+YOu#3^*T#Hu#M znR|*eY$?Vk&Z-yo%Qh?ImA3nWdF5YDyLkxB>M%KFj07Q zNh^;T?vF$!@^DG=lkwaSr!{t&R+Bvru7L$-z{~L2nP=$VW!JfH`I6*r4sDR7H16** z;Hn!wV43yl78jqRD+Ia@K;+-S5Rp0%*PR8AM1L9LA(qPtb04^@xujUooDFeEW=a5y z$_slGd zY%-{SaR68e6GQ)3GWu{N=VN4(p|Iwq%|722MRr>tT`F9|hPq;(nAqht*QBbc5=Ckz zOI!BUZq2GYJokhno0K2=Fux5<=ne&Hf`1S)FJp(JDkzEnrJ^Uckwyo6lsbtVT2Csrz1h(uvMthb({z_CTf2#urEi(8_7CYx+i#GB@#+B$Vvly8oeA`y6JEa-@>C)IHMRR3iG#0ZFoZ9P~&;A^T79fxhXL;W+q|_~6XckS+L-0)tpj{dvy3`h zT0Pu^I28X2$;|!#_mtyO(1CUiPBD`#AvtB#(G=n{ zVM}F4UT(sdX&htd$!1YyMQLM9vZG6k=29lH_C;MKSH*Ka(@J><^K}~G+u?MZi0E|J zQfgF6*vaWTC^Iy)SSJWUi6-UzOdXV&nOdw9GbuN(f264S(<~viMvyv<@PuSF>R195 zw7<)~rAajaX1$xt_GFgXVP`pGreO3vT*;55l}KEaJjPndjvNRn)lI!NE` zc)>Y_5SnaBwGeRb$jw+#GdUOspT34AKFv-lU(f&Wgh~BBsMsXC_InO<5+4z z26K+ET9zNjQ~mXet}#Ul?hw>GdwLYmpH@NrSrs%M-G2nunV<{ub9mVJt$j}0fdy=Ost=!BYOvDN^*s?vX5|X9V@;qjn%e`AMZ5z&bg!jQ>xxPPNUS0>_{<6AS zPas(pI5jGsn%aR0B{>-F>)hIe=US=whf z`#OPd>pY-nrjzm3vvBPeX{N)(lFneCSl`(m5aW1$^1p)WRw5d?m>G0}Xw&LG9D79` zUq$}){BCi6al`rx&C}=uF`h=6lK^(g^fsI8WODs_$AQS4=rAoVogm0Fa)RR%SSQ3| z9<$Hu3ETmrCom?cv*qyQpCGJ{G?=Gp)T}F+Zcg-A`cSM6Rw9*Zi!&;OzI#e=k|1}- z^fl?Li3mwR$TtRLDoxP+_MFh%y{4HR(8C)o?hX^ulG6Dt^@IoLXc`T`SJ>EAJ>sv& z^vPT0yp35MOd5_hWeq-^K5)jH5<(Kh9C0I&Np6YMofrsAV4Q42&K-d9!nXnyjy`Z5 m)V>?gq1%~SAc;OPG2#Y@gJ#9fYgeWdsQzy9Gy4A{p8XeTu7=?N literal 0 HcmV?d00001 From 1b7a9c68415323eb565084a1d5ce5730f382952f Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sun, 17 Apr 2022 07:24:22 -0700 Subject: [PATCH 129/151] Bloom on sculpture --- build/build.sh | 2 +- run_tree/osx/arm64/debug/lumenarium | Bin 856087 -> 860199 bytes run_tree/osx/arm64/prod/lumenarium | Bin 330407 -> 330439 bytes .../graphics/lumenarium_editor_graphics.h | 69 +++++++++--- src_v2/editor/lumenarium_editor.c | 26 ++--- src_v2/editor/lumenarium_editor.h | 7 ++ .../lumenarium_editor_sculpture_visualizer.c | 100 ++++++++++++++---- ...rium_editor_sculpture_visualizer_shaders.h | 73 ++++++++++++- src_v2/editor/lumenarium_editor_ui.c | 10 +- src_v2/editor/lumenarium_editor_ui.h | 1 + src_v2/editor/lumenarium_editor_ui_shaders.h | 3 +- src_v2/lumenarium_first.c | 4 +- src_v2/platform/osx/lumenarium_first_osx.c | 20 ++-- 13 files changed, 246 insertions(+), 69 deletions(-) diff --git a/build/build.sh b/build/build.sh index 7a321c5..bb270e8 100755 --- a/build/build.sh +++ b/build/build.sh @@ -1,3 +1,3 @@ SCRIPT_REL_DIR=$(dirname "${BASH_SOURCE[0]}") -$SCRIPT_REL_DIR/build_.sh prod osx arm64 +$SCRIPT_REL_DIR/build_.sh debug osx arm64 # $SCRIPT_REL_DIR/build_.sh debug wasm intel \ No newline at end of file diff --git a/run_tree/osx/arm64/debug/lumenarium b/run_tree/osx/arm64/debug/lumenarium index a2c1759e06233c3a3cc08b4b08ce1c7a8ac249c8..e424b1339f5894e0ecfd55d8d5be22e429ef2554 100755 GIT binary patch delta 222102 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%0D^gu?petA;B4N-}f~Enp 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 @ABzu5Eq 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%0Dw})22m_`qZ_^F*4MyW-Iv$E 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 diff --git a/run_tree/osx/arm64/prod/lumenarium b/run_tree/osx/arm64/prod/lumenarium index e3c8be856d99139980f323fbf15db2d9642d5e5f..7eb20576543b1f415891ca8682a05c4cbe055f3e 100755 GIT binary patch delta 21933 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 delta 21552 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 = (r32)ascent * scale; @@ -19,6 +19,8 @@ ed_load_font_cb(File_Async_Job_Args result, u8* user_data) ui->font_line_gap = (r32)line_gap * scale; if (ui->font_line_gap == 0) ui->font_line_gap = 5; + ui->font_texture_scale = 2; + scale *= ui->font_texture_scale; String c = lit_str("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-=_+[]{}\\|;:'\",<.>/?`~"); for (u64 i = 0; i < c.len; i++) { @@ -28,7 +30,7 @@ ed_load_font_cb(File_Async_Job_Args result, u8* user_data) s32 x0, y0, x1, y1; stbtt_GetCodepointBitmapBoxSubpixel(&font, (char)c.str[i], scale, scale, 0, 0, &x0, &y0, &x1, &y1); - v2 offset = (v2){ 0, (r32)y0 }; + v2 offset = (v2){ 0, (r32)y0 / ui->font_texture_scale }; texture_atlas_register(&state->editor->ui.atlas, bp, (u32)w, (u32)h, id, offset, TextureAtlasRegistration_PixelFormat_Alpha); stbtt_FreeBitmap(bp, 0); } @@ -103,14 +105,15 @@ Shader shd; Texture tex; internal void -ed_init(App_State* state) +ed_init(App_State* state, Editor_Desc* desc) { lumenarium_env_validate(); Editor* editor = allocator_alloc_struct(permanent, Editor); zero_struct(*editor); state->editor = editor; - editor->content_scale = (v2){1,1}; + editor->window_dim = desc->init_window_dim; + editor->content_scale = desc->content_scale; editor->ui = ui_create(4096, 4096, state->input_state, permanent); editor->ui.draw_panel_cb = ed_draw_panel; editor->ui.draw_panel_cb_data = (u8*)state; @@ -158,7 +161,7 @@ ed_init(App_State* state) vertex_attrib_pointer(b, shd, 2, shd.attrs[1], 5, 3); u32 tex_pix[] = { 0xFFFFFFFF, 0xFF00FFFF, 0xFFFFFF00, 0xFFFF00FF }; - tex = texture_create((u8*)tex_pix, 2, 2, 2); + tex = texture_create(texture_desc_default(2, 2), (u8*)tex_pix); ed_sculpture_visualizer_init(state); lumenarium_env_validate(); @@ -270,27 +273,20 @@ ed_sculpture_updated(App_State* state, r32 scale, r32 led_size) geo.buffer_index.values, geo.buffer_index.len ); - - for (u32 i = 0; i < 6; i++) - { - u32 index = geo.buffer_index.values[1008 + i]; - r32* values = geo.buffer_vertex.values + (index * 5); - printf("%d -> %f %f %f, %f %f\n", index, values[0], values[1], values[2], values[3], values[4]); - } vertex_attrib_pointer(ed->sculpture_geo, ed->sculpture_shd, 3, ed->sculpture_shd.attrs[0], 5, 0); vertex_attrib_pointer(ed->sculpture_geo, ed->sculpture_shd, 2, ed->sculpture_shd.attrs[1], 5, 3); // TODO(PS): make this have enough pixels for the sculpture // TODO(PS): map leds to pixels - if (ed->sculpture_tex.w != 0) + if (ed->sculpture_tex.desc.w != 0) { invalid_code_path; // TODO(PS): destroy the old texture } u8* pixels = ed_leds_to_texture(state, &scratch, pixels_dim); - ed->sculpture_tex = texture_create(pixels, pixels_dim, pixels_dim, pixels_dim); + ed->sculpture_tex = texture_create(texture_desc_default(pixels_dim, pixels_dim), pixels); scratch_release(scratch); lumenarium_env_validate(); @@ -313,7 +309,7 @@ ed_frame(App_State* state) { scratch_get(scratch); - u32 w = ed->sculpture_tex.w; + u32 w = ed->sculpture_tex.desc.w; u8* pixels = ed_leds_to_texture(state, &scratch, w); texture_update(ed->sculpture_tex, pixels, w, w, w); scratch_release(scratch); diff --git a/src_v2/editor/lumenarium_editor.h b/src_v2/editor/lumenarium_editor.h index f275a99..380751f 100644 --- a/src_v2/editor/lumenarium_editor.h +++ b/src_v2/editor/lumenarium_editor.h @@ -3,6 +3,13 @@ #ifndef LUMENARIUM_EDITOR_H #define LUMENARIUM_EDITOR_H +typedef struct Editor_Desc Editor_Desc; +struct Editor_Desc +{ + v2 content_scale; + v2 init_window_dim; +}; + typedef struct Editor Editor; struct Editor { diff --git a/src_v2/editor/lumenarium_editor_sculpture_visualizer.c b/src_v2/editor/lumenarium_editor_sculpture_visualizer.c index e1bded3..d7edfff 100644 --- a/src_v2/editor/lumenarium_editor_sculpture_visualizer.c +++ b/src_v2/editor/lumenarium_editor_sculpture_visualizer.c @@ -1,5 +1,11 @@ #include "lumenarium_editor_sculpture_visualizer_shaders.h" +u32 fbo; +Texture fbo_tex_c; +u32 fbo_rbo; +Geometry_Buffer fs_quad; +Shader fs_shd; + internal void ed_sculpture_visualizer_init(App_State* state) { @@ -11,6 +17,64 @@ ed_sculpture_visualizer_init(App_State* state) String attrs[] = { lit_str("a_pos"), lit_str("a_uv") }; String uniforms[] = { lit_str("proj") }; editor->sculpture_shd = shader_create(vert, frag, attrs, 2, uniforms, 1); + + { + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + v2 wds = HMM_MultiplyVec2(editor->window_dim, editor->content_scale); + s32 w = (s32)wds.x; + s32 h = (s32)wds.y; + fbo_tex_c = texture_create( + (Texture_Desc){ + .w = w, .h = h, .s = w, + .min_filter = GL_LINEAR, + .mag_filter = GL_LINEAR, + .fmt_internal = GL_RGBA, + .fmt_data = GL_RGBA, + }, + 0 + ); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_tex_c.id, 0); + + glGenRenderbuffers(1, &fbo_rbo); + glBindRenderbuffer(GL_RENDERBUFFER, fbo_rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo_rbo); + + u32 status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + u32 complete = GL_FRAMEBUFFER_COMPLETE; + if (status != complete) + { + #define GL_ENUM_ERROR_CASE(e, msg) case e: { printf("Error: %s - %s\n", #e, msg); } break + switch (status) { + GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_UNDEFINED, ""); + GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, ""); + GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT, ""); + GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER, ""); + GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER, ""); + GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_UNSUPPORTED, ""); + GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE, ""); + //GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS, ""); + default: { + os_gl_no_error(); + } break; + } + printf("Error: unable to complete framebuffer\n"); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + String vert = xplatform_shader_program_get_vert(sculpture_comp_shd); + String frag = xplatform_shader_program_get_frag(sculpture_comp_shd); + String shd_a[] = { lit_str("a_pos"), lit_str("a_uv") }; + String shd_u[] = { lit_str("proj") }; + fs_shd = shader_create(vert, frag, shd_a, 2, shd_u, 1); + + fs_quad = unit_quad_create(); + vertex_attrib_pointer(fs_quad, fs_shd, 3, fs_shd.attrs[0], 5, 0); + vertex_attrib_pointer(fs_quad, fs_shd, 2, fs_shd.attrs[1], 5, 3); + } } r32 cam_theta = 0; @@ -21,24 +85,11 @@ ed_sculpture_visualizer(App_State* state) { Editor* ed = state->editor; - Input_State* in = state->input_state; - u32 delta = 1; - if (input_key_is_down(in, KeyCode_LeftShift) || input_key_is_down(in, KeyCode_RightShift)) - { - delta = 100; - } - if (input_key_went_down(in, KeyCode_UpArrow)) - { - offset += delta; - printf("%d\n", offset); - } - if (input_key_went_down(in, KeyCode_DownArrow)) - { - offset -= delta; - printf("%d\n", offset); - } - offset = clamp(0, offset, ed->sculpture_geo.indices_len); - + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + os_gl_no_error(); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); // Set the viewport to the current layout's region so that the sculpture // never overlaps any other ui elements @@ -75,6 +126,19 @@ ed_sculpture_visualizer(App_State* state) geometry_drawi(ed->sculpture_geo, k, 0); // reset the viewport for all other rendering + glBindFramebuffer(GL_FRAMEBUFFER, 0); v2 wds = HMM_MultiplyVec2(ed->window_dim, ed->content_scale); glViewport(0, 0, (s32)wds.x, (s32)wds.y); + + m44 ortho = HMM_Orthographic(0, ed->window_dim.x, ed->window_dim.y, 0, 0.01f, 200.0f); + m44 scale = HMM_Scale((v3){ed->window_dim.x / 2, ed->window_dim.y / 2, 100}); + m44 pos = HMM_Translate((v3){ed->window_dim.x / 2, ed->window_dim.y / 2, -99}); + m44 model = HMM_MultiplyMat4(pos, scale); + m44 mvp = HMM_MultiplyMat4(ortho, model); + + shader_bind(fs_shd); + set_uniform(fs_shd, 0, mvp); + texture_bind(fbo_tex_c); + geometry_bind(fs_quad); + geometry_drawi(fs_quad, 6, 0); } \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_sculpture_visualizer_shaders.h b/src_v2/editor/lumenarium_editor_sculpture_visualizer_shaders.h index 07444ca..314d56a 100644 --- a/src_v2/editor/lumenarium_editor_sculpture_visualizer_shaders.h +++ b/src_v2/editor/lumenarium_editor_sculpture_visualizer_shaders.h @@ -62,4 +62,75 @@ global XPlatform_Shader_Program_Src sculpture_shd = { " gl_FragColor = vec4(uv.x,1,uv.y,1);\n" "}" ), -}; \ No newline at end of file +}; + + + +global XPlatform_Shader_Program_Src sculpture_comp_shd = { + .win32_vert = lit_str("" + ), + .win32_frag = lit_str( + "" + ), + + .osx_vert = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec3 a_pos;\n" + "layout (location = 1) in vec2 a_uv;\n" + "out vec2 uv;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + "}" + ), + .osx_frag = lit_str( + "#version 330 core\n" + "in vec2 uv;\n" + "out vec4 FragColor;\n" + "uniform sampler2D tex;\n" + "float normpdf(in float x, in float sigma) { return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma; }\n" + "void main(void) {\n" + " vec4 orig_p = texture(tex, uv);\n" + " vec2 tex_size = textureSize(tex, 0);\n" + " vec2 tex_offset = 1.0 / tex_size;\n" + " " + " const int m_size = 15;\n // must be odd\n" + " const int k_size = (m_size - 1) / 2;\n" + " float kernel[m_size];\n" + " float sigma = 7.0;\n" + " float z = 0;\n" + " for (int i = 0; i <= k_size; i++) {\n" + " float v = normpdf(float(i), sigma);\n" + " kernel[k_size + i] = v; kernel[k_size - i] = v;\n" + " }\n" + " for (int i = 0; i < m_size; i++) {\n" + " z += kernel[i];\n" + " }\n" + " vec3 bloom_acc = vec3(0);\n" + " for (int i = -k_size; i <= k_size; i++) {\n" + " for (int j = -k_size; j <= k_size; j++) {\n" + " vec2 uvp = uv + (vec2(float(i), float(j)) / tex_size);\n" + " bloom_acc += kernel[k_size + j] * kernel[k_size + i] * texture(tex, uvp).xyz;\n" + " }\n" + " }\n" + " vec3 bloom_color = bloom_acc / (z * z);\n" + " vec3 final_color = orig_p.xyz + bloom_color;\n" + // tone mapping + " float exposure = 1.0;\n" + " float gamma = 2.2;\n" + " vec3 result = vec3(1.0) - exp(-final_color * exposure);\n" + // also gamma correct while we're at it + " result = pow(result, vec3(1.0 / gamma));\n" + " FragColor = vec4(result, 1.0);\n" + "}" + ), + + .wasm_vert = lit_str( + "" + ), + .wasm_frag = lit_str( + "" + ), +}; + diff --git a/src_v2/editor/lumenarium_editor_ui.c b/src_v2/editor/lumenarium_editor_ui.c index e79c162..1a8cdf9 100644 --- a/src_v2/editor/lumenarium_editor_ui.c +++ b/src_v2/editor/lumenarium_editor_ui.c @@ -47,7 +47,7 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) // Texture Atlas result.atlas = texture_atlas_create(1024, 1024, 512, permanent); - result.atlas_texture = texture_create(result.atlas.pixels, 1024, 1024, 1024); + result.atlas_texture = texture_create(texture_desc_default(1024, 1024), result.atlas.pixels); u32 white_sprite[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, @@ -117,8 +117,8 @@ ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint) result.uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite); v3 dim = (v3){ - (r32)(sprite.max_x - sprite.min_x), - (r32)(sprite.max_y - sprite.min_y), + (r32)(sprite.max_x - sprite.min_x) / ui->font_texture_scale, + (r32)(sprite.max_y - sprite.min_y) / ui->font_texture_scale, 0, }; result.pmin = at; @@ -126,7 +126,7 @@ ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint) result.pmin.XY = v2_floor(result.pmin.XY); result.pmax = HMM_AddVec3(result.pmin, dim); - result.baseline_after = (v3){ result.pmax.x, at.y, at.z }; + result.baseline_after = (v3){ result.pmax.x + 1.5f, at.y, at.z }; return result; } @@ -193,12 +193,10 @@ ui_draw_panel(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data) c = PINK_V4; } - #if 0 ui_sprite_push_color(ui, l0p0, l0p1, sid, c); ui_sprite_push_color(ui, l1p0, l1p1, sid, c); ui_sprite_push_color(ui, l2p0, l2p1, sid, c); ui_sprite_push_color(ui, l3p0, l3p1, sid, c); - #endif } internal void diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h index 2bea01e..76e9feb 100644 --- a/src_v2/editor/lumenarium_editor_ui.h +++ b/src_v2/editor/lumenarium_editor_ui.h @@ -187,6 +187,7 @@ struct UI Texture_Atlas atlas; r32 font_ascent, font_descent, font_line_gap, font_space_width; + r32 font_texture_scale; UI_Widget_Pool widgets; UI_Style_Sheet* style_sheet; diff --git a/src_v2/editor/lumenarium_editor_ui_shaders.h b/src_v2/editor/lumenarium_editor_ui_shaders.h index 1db4635..49e4167 100644 --- a/src_v2/editor/lumenarium_editor_ui_shaders.h +++ b/src_v2/editor/lumenarium_editor_ui_shaders.h @@ -72,4 +72,5 @@ global XPlatform_Shader_Program_Src ui_shader = { " gl_FragColor = texture2D(tex, uv) * color;\n" "}" ), -}; \ No newline at end of file +}; + diff --git a/src_v2/lumenarium_first.c b/src_v2/lumenarium_first.c index b2b16f8..5178b03 100644 --- a/src_v2/lumenarium_first.c +++ b/src_v2/lumenarium_first.c @@ -2,7 +2,7 @@ #include "user_space/user_space_incenter.cpp" internal App_State* -lumenarium_init() +lumenarium_init(Editor_Desc* ed_desc) { App_State* state = 0; @@ -48,7 +48,7 @@ lumenarium_init() en_init(state, desc); - if (has_flag(state->flags, AppState_RunEditor)) ed_init(state); + if (has_flag(state->flags, AppState_RunEditor)) ed_init(state, ed_desc); if (has_flag(state->flags, AppState_RunUserSpace)) incenter_init(state); scratch_release(scratch); return state; diff --git a/src_v2/platform/osx/lumenarium_first_osx.c b/src_v2/platform/osx/lumenarium_first_osx.c index 65fb37e..220bc7b 100644 --- a/src_v2/platform/osx/lumenarium_first_osx.c +++ b/src_v2/platform/osx/lumenarium_first_osx.c @@ -289,12 +289,15 @@ int main (int arg_count, char** args) } glfwSetErrorCallback(glfw_error_callback); + s32 init_window_width = 1400; + s32 init_window_height = 700; + glfwWindowHint(GLFW_DOUBLEBUFFER, true); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 1); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - GLFWwindow* window = glfwCreateWindow(1400, 700, "Lumenarium", NULL, NULL); + GLFWwindow* window = glfwCreateWindow(init_window_width, init_window_width, "Lumenarium", NULL, NULL); if (!window) { printf("Error: Unable to create a glfw window\n"); @@ -310,15 +313,14 @@ int main (int arg_count, char** args) glfwSetCursorPosCallback(window, cursor_position_callback); glfwSetScrollCallback(window, scroll_callback); - App_State* state = lumenarium_init(); - app_state_data = (u8*)state; + Editor_Desc ed_desc = {}; + float xscale, yscale; + glfwGetWindowContentScale(window, &xscale, &yscale); + ed_desc.content_scale = (v2){ xscale, yscale }; + ed_desc.init_window_dim = (v2){init_window_width, init_window_height}; - if (has_flag(state->flags, AppState_RunEditor)) - { - float xscale, yscale; - glfwGetWindowContentScale(window, &xscale, &yscale); - state->editor->content_scale = (v2){ xscale, yscale }; - } + App_State* state = lumenarium_init(&ed_desc); + app_state_data = (u8*)state; bool running = true; r64 target_seconds_per_frame = 1.0 / 30.0f; From 8c904d5c1c6c62352d6066b242d313d9aa4e9411 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sun, 17 Apr 2022 07:53:00 -0700 Subject: [PATCH 130/151] visualizer experiments --- run_tree/osx/arm64/debug/lumenarium | Bin 860199 -> 860199 bytes src_v2/editor/lumenarium_editor.c | 6 +----- .../lumenarium_editor_sculpture_visualizer.c | 2 +- ...rium_editor_sculpture_visualizer_shaders.h | 1 - 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/run_tree/osx/arm64/debug/lumenarium b/run_tree/osx/arm64/debug/lumenarium index e424b1339f5894e0ecfd55d8d5be22e429ef2554..17af5b049cbfa91cf7a4827a559556cf6a750a23 100755 GIT binary patch delta 76466 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