diff --git a/README.md b/README.md index 2350f97..ddbba99 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,19 @@ ![Lumenarium Banner](./docs/images/splash.png) ## Build Lumenarium -Building Lumenarium requires having MSVC installed (sorry, Windows only for now!). At the moment, there are bunch of errors from the meta program but the program will still compile -1. Run build_meta.bat -2. Run build.bat -3. Launch win32_foldhaus.exe (locaated in the build folder) +Building Lumenarium requires having MSVC installed (sorry, Windows only for now!). +1. clone the repo onto your computer +2. Run the appropriate build batch file + - for Windows: use `build\build_app_msvc_win32_debug.bat` + - other platforms coming soon + +## Run Lumenarium +Windows - Debug +1. Just run `win32_msvc\debug\win32_foldhaus.exe` + +## Debug Lumenarium +###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? Lumenarium is our custom build light and motion control software. It allows us to design light and motion patterns for our sculptures, visualize the output long before the sculpture is built, and iterate in real time to achieve the best visual results quickly. @@ -26,7 +35,7 @@ The following features are under active development for integration in Lumenariu *** ### Sculpture File Format -Sculptures are specified via a structured text file so that it is easy to represent new sculptures, simply as strips of LEDs. +Sculptures are specified via a structured text file so that it is easy to represent new sculptures, simply as strips of LEDs. Documentation coming soon. @@ -36,13 +45,13 @@ Lumenarium supports SACN output, and ArtNet is in development, via a DMX system *** ### Live Visualization -We don't always have access to our physical sculptures while working on the lights and motion for them. Having a visualization of the sculpture lets us see what the patterns we develop will look like when running on the actual sculpture. +We don't always have access to our physical sculptures while working on the lights and motion for them. Having a visualization of the sculpture lets us see what the patterns we develop will look like when running on the actual sculpture. The visualization runs in real time, and mirrors the DMX output the sculptures will receive, so any changes made in the software are immediately reflected, both in the visualization and in the data sent over the network. *** ### Animation Timeline -One goal of Lumenarium is to enable fine-grained control over procedural light and motion patterns. Patterns can be arranged on a timeline for manual sequencing. +One goal of Lumenarium is to enable fine-grained control over procedural light and motion patterns. Patterns can be arranged on a timeline for manual sequencing. Coming Soon * Pattern Fading/Cross fading @@ -58,6 +67,6 @@ Coming Soon *** ### Live Compilation Reload -One of the most fulfilling parts of working on these sculptures is getting to iterate live on the lights and motion in front of the physical sculpture while on active display. While Lumenarium is designed to enable everyone to shape the visual identity of our sculptures, it also allows for low-level, fine grained control over the visual output as well. The software is split into a platform layer (currently Windows only, though there are plans to support Mac, and possibly Raspberry Pi), and an application library. The application library can be recompiled and hot loaded while the software is running to facilitate the fastest possible iteration time. +One of the most fulfilling parts of working on these sculptures is getting to iterate live on the lights and motion in front of the physical sculpture while on active display. While Lumenarium is designed to enable everyone to shape the visual identity of our sculptures, it also allows for low-level, fine grained control over the visual output as well. The software is split into a platform layer (currently Windows only, though there are plans to support Mac, and possibly Raspberry Pi), and an application library. The application library can be recompiled and hot loaded while the software is running to facilitate the fastest possible iteration time. To see how this works in detail see [Handmade Hero](https://guide.handmadehero.org/code/day022/) diff --git a/build/_postbuild_win32.bat b/build/_postbuild_win32.bat index 32c9169..ebc3e0a 100644 --- a/build/_postbuild_win32.bat +++ b/build/_postbuild_win32.bat @@ -2,8 +2,8 @@ IF NOT "%PrebuildCalled%"=="1" GOTO error -C:\apps\ctime\ctime.exe -end %StatsPath%\%StatsFile% %LastError% -C:\apps\ctime\ctime.exe -stats %StatsPath%\%StatsFile% +IF EXIST %CTIMEPATH% ( call C:\apps\ctime\ctime.exe -end %StatsPath%\%StatsFile% %LastError% ) +IF EXIST %CTIMEPATH% ( call C:\apps\ctime\ctime.exe -stats %StatsPath%\%StatsFile% ) set PrebuildCalled=0 GOTO:eof diff --git a/build/_prebuild_win32.bat b/build/_prebuild_win32.bat index 9d8c31e..9680699 100644 --- a/build/_prebuild_win32.bat +++ b/build/_prebuild_win32.bat @@ -1,6 +1,6 @@ @echo off -REM This file takes two arguments +REM This file takes three arguments REM 1 = "app" or "meta" REM 2 = "debug" or "release" REM 3 = "msvc" or "clang" @@ -22,7 +22,9 @@ set StatsFile=%1_win32_%3_%2_build_time.ctm IF NOT EXIST %BuildPath% mkdir %BuildPath% IF NOT EXIST %StatsPath% mkdir %StatsPath% -C:\apps\ctime\ctime.exe -begin %StatsPath%\%StatsFile% +set CTimePath=C:\apps\ctime +IF EXIST %CTIMEPATH% ( call C:\apps\ctime\ctime.exe -begin %StatsPath%\%StatsFile% ) + echo. echo BUILDING TO %BuildPath% echo STATS IN %StatsPath%\%StatsFile% diff --git a/build/build_app_clang_win32_debug.bat b/build/build_app_clang_win32_debug.bat index 30c5b8b..20a1746 100644 --- a/build/build_app_clang_win32_debug.bat +++ b/build/build_app_clang_win32_debug.bat @@ -1,32 +1,26 @@ @echo off -set ProjectDevFolder=%~dp0 -set ProjectDevPath=%ProjectDevFolder:~0,-1% - -pushd %ProjectDevPath% - -IF NOT EXIST .\build_clang\ mkdir .\build_clang - -C:\programs\ctime\ctime.exe -begin %ProjectDevPath%\build\win32_foldhaus_clang_build_time.ctm +SET MyPath=%~dp0 +SET MyPath=%MyPath:~0,-1% +call %MyPath%\_prebuild_win32.bat app debug clang set CommonCompilerFlags=-std=c++11 -Wno-writable-strings -Wno-unused-value -Wno-varargs -Wno-switch -Wno-microsoft-enum-forward-reference -DDEBUG=1 -pushd .\build_clang\ +pushd %BuildPath% -REM Run the Preprocessor -foldhaus_meta.exe ..\src\foldhaus_app.cpp +del *.pdb > NUL 2> NUL echo WAITING FOR PDB TO WRITE > lock.tmp -clang %CommonCompilerFlags% ..\src\foldhaus_app.cpp -shared +clang++ %CommonCompilerFlags% %SourceCodePath%\foldhaus_app.cpp -shared -o set LastError=%ERRORLEVEL% del lock.tmp -clang %CommonCompilerFlags% ..\src\win32_foldhaus.cpp -o win32_foldhaus.exe user32.lib winmm.lib gdi32.lib opengl32.lib dsound.lib Ws2_32.lib Comdlg32.lib +clang++ -c %CommonCompilerFlags% %SourceCodePath%\platform_win32\win32_foldhaus.cpp +link win32_foldhaus.o user32.lib winmm.lib gdi32.lib opengl32.lib dsound.lib Ws2_32.lib Comdlg32.lib -C:\programs\ctime\ctime.exe -end %ProjectDevPath%\build\win32_foldhaus_clang_build_time.ctm %LastError% -REM C:\programs\ctime\ctime.exe -stats %ProjectDevPath%\build\win32_foldhaus_clang_build_time.ctm popd +call %MyPath%\_postbuild_win32.bat diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat index 673a299..d69d5db 100644 --- a/build/build_app_msvc_win32_debug.bat +++ b/build/build_app_msvc_win32_debug.bat @@ -2,32 +2,38 @@ SET MyPath=%~dp0 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- -set CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -O2 %CommonCompilerFlags% -set CommonLinkerFlags= -opt:ref -incremental:no +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 DLLExports=/EXPORT:InitializeApplication /EXPORT:UpdateAndRender /EXPORT:CleanupApplication /EXPORT:ReloadStaticData +SET CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -I%CommonLibs% -O2 %CommonCompilerFlags% + +SET CommonLinkerFlags= -opt:ref -incremental:no + +SET DLLExports=/EXPORT:InitializeApplication /EXPORT:UpdateAndRender /EXPORT:CleanupApplication /EXPORT:ReloadStaticData pushd %BuildPath% del *.pdb > NUL 2> NUL -REM Run the Preprocessor -REM %MetaProgramPath%\foldhaus_meta.exe %SourceCodePath%\foldhaus_app.cpp - echo WAITING FOR PDB TO WRITE > lock.tmp cl %CommonCompilerFlags% %SourceCodePath%\foldhaus_app.cpp /Fefoldhaus.dll /LD /link %CommonLinkerFlags% %DLLExports% -set LastError=%ERRORLEVEL% +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 + +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\sculpture_gen\gen_blumen_lumen.cpp /Fegen_blumen_lumen.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib + popd call %MyPath%\_postbuild_win32.bat \ No newline at end of file diff --git a/build/setup_cl.bat b/build/setup_cl.bat new file mode 100644 index 0000000..bae8867 --- /dev/null +++ b/build/setup_cl.bat @@ -0,0 +1,40 @@ +@echo off + +ECHO SETUP CL + +SET "LIB=" + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio 12.0 +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\vcvarsall.bat" x64)) + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio 11.0 +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\vcvarsall.bat" x64)) + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio 10.0 +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\vcvarsall.bat" x64)) + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio 13.0 +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\vcvarsall.bat" x64)) + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio 14.0 +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\vcvarsall.bat" x64)) + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x64)) + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x64)) + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x64)) + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x64)) + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x64)) + +SET VC_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x64)) + +ECHO SETUP CL COMPLETE \ No newline at end of file diff --git a/debug.bat b/debug.bat new file mode 100644 index 0000000..ecdc8cc --- /dev/null +++ b/debug.bat @@ -0,0 +1,2 @@ +@echo off +call remedybg.bat ./app_run_tree/win32_msvc/debug/session.rdbg \ No newline at end of file diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..d8708cf --- /dev/null +++ b/run.bat @@ -0,0 +1,4 @@ +@echo off +pushd app_run_tree +start win32_msvc\debug\win32_foldhaus.exe +popd \ No newline at end of file diff --git a/src/app/blumen_lumen.cpp b/src/app/blumen_lumen.cpp new file mode 100644 index 0000000..323b8ae --- /dev/null +++ b/src/app/blumen_lumen.cpp @@ -0,0 +1,280 @@ +// +// File: blumen_lumen.cpp +// Author: Peter Slattery +// Creation Date: 2021-01-23 +// +#ifndef BLUMEN_LUMEN_CPP + +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; + + 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 + 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; + } + } + } +#endif + + while (Data->OutgoingMsgQueue->ReadHead != Data->OutgoingMsgQueue->WriteHead) + { + u32 ReadIndex = Data->OutgoingMsgQueue->ReadHead++; + if (Data->OutgoingMsgQueue->ReadHead >= BLUMEN_MESSAGE_QUEUE_COUNT) + { + Data->OutgoingMsgQueue->ReadHead = 0; + } + + Msg = Data->OutgoingMsgQueue->Buffers[ReadIndex]; + u32 Address = WeathermanIPV4; + u32 Port = WeathermanPort; + s32 Flags = 0; + SocketSend(Data->SocketManager, Data->ListenSocket, Address, Port, Msg, Flags); + } + } + } + + CloseSocket(Data->SocketManager, Data->ListenSocket); +} + +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, 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); +} + +internal pixel +TEMP_Saturate(pixel P) +{ + v4 CRGB = v4{ (r32)P.R / 255.f, (r32)P.G / 255.f, (r32)P.B / 255.f, 1.f }; + v4 CHSV = RGBToHSV(CRGB); + if (CHSV.g > .3f) + { + CHSV.g = 1; + CRGB = HSVToRGB(CHSV); + } + return V4ToRGBPixel(CRGB); +} + +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->MicListenJobData.Running = &BLState->Running; + BLState->MicListenJobData.SocketManager = Context.SocketManager; + BLState->MicListenJobData.MicPacketBuffer = &BLState->MicPacketBuffer; + 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); + + gs_const_string SculpturePath = ConstString("data/test_blumen.fold"); + LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); + + { // 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_AddLayer(&Anim0, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); + + Animation_AddBlock(&Anim0, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(15), 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_AddLayer(&Anim1, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); + + Animation_AddBlock(&Anim1, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(12), 0); + + 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_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); + + Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(10), 0); + + AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); + + State->AnimationSystem.TimelineShouldAdvance = true; + } // End Animation Playground + + 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]); + } + + return Result; +} + +internal void +BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) +{ + blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; + + MotorTimeElapsed += Context->DeltaTime; + + gs_string BlueString = MakeString("blue"); + gs_string GreenString = MakeString("green"); + gs_string ILoveYouString = MakeString("i_love_you"); + + 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; + } + } + + if (MotorTimeElapsed > 60) + { + // NOTE(pjs): + MotorTimeElapsed = 0; + u8 Position = LastPosition; + if (LastPosition == 2) + { + LastPosition = 1; + } + else + { + LastPosition = 2; + } + + if ((BLState->OutgoingMsgQueue.WriteHead >= BLState->OutgoingMsgQueue.ReadHead) || + (BLState->OutgoingMsgQueue.WriteHead < BLState->OutgoingMsgQueue.ReadHead)) + { + u32 WriteIndex = BLState->OutgoingMsgQueue.WriteHead; + + 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; + + // 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; + } + } + } +} + +US_CUSTOM_CLEANUP(BlumenLumen_CustomCleanup) +{ + 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.CustomCleanup = BlumenLumen_CustomCleanup; + return Result; +} + +#define BLUMEN_LUMEN_CPP +#endif // BLUMEN_LUMEN_CPP \ No newline at end of file diff --git a/src/app/blumen_lumen.h b/src/app/blumen_lumen.h new file mode 100644 index 0000000..0ea9db5 --- /dev/null +++ b/src/app/blumen_lumen.h @@ -0,0 +1,119 @@ +// +// File: blumen_lumen.h +// Author: Peter Slattery +// Creation Date: 2021-01-15 +// +#ifndef BLUMEN_LUMEN_H + +typedef struct motor_packet +{ + u8 FlowerPositions[3]; +} motor_packet; + +#pragma pack(push, 1) +struct microphone_packet +{ + b8 ChangeAnimation; + char AnimationFileName[32]; + b8 SetLayer; + char LayerName[32]; + r32 LayerOpacity; + b8 SetLayerParamColor; + char LayerParamColor[7]; + r32 OverrideDuration; +}; +#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 +{ + bool* Running; + + platform_socket_manager* SocketManager; + packet_ringbuffer* MicPacketBuffer; + platform_socket_handle_ ListenSocket; + + blumen_network_msg_queue* OutgoingMsgQueue; +}; + +struct blumen_lumen_state +{ + bool Running; + + packet_ringbuffer MicPacketBuffer; + blumen_network_msg_queue OutgoingMsgQueue; + + temp_job_req JobReq; + + + platform_thread_handle MicListenThread; + mic_listen_job_data MicListenJobData; +}; + + + + + + + + + + + + + + + + + + +// If you change anything, exit lumenarium if its running +// then in this application hit f1 to compile then +// go to remedybg (the debugger) and hit f5 + + +// don't touch this +u8 LastPosition = 1; + +u8 ClosedValue = 1; +u8 OpenValue = 2; + + +r64 MotorTimeElapsed = 0; +r64 OpenClosePeriod = 15.0f; + + + + + + + + + + + + + + + + + + + + + + + + + + +#define BLUMEN_LUMEN_H +#endif // BLUMEN_LUMEN_H \ No newline at end of file diff --git a/src/app/editor/foldhaus_editor.cpp b/src/app/editor/foldhaus_editor.cpp index 5d08c1f..6d71ca3 100644 --- a/src/app/editor/foldhaus_editor.cpp +++ b/src/app/editor/foldhaus_editor.cpp @@ -12,6 +12,8 @@ Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue 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) { @@ -47,7 +49,14 @@ Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue // frame when the button was released, even if the command is registered to both events if (KeyTransitionedDown(Event)) { - FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue); + if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue)) + { + char KeyASCII = KeyCodeToChar(Event.Key); + if (KeyASCII) + { + OutChar(&TextInputString, KeyASCII); + } + } } else if (KeyTransitionedUp(Event)) { @@ -55,7 +64,14 @@ Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue } else if (KeyHeldDown(Event)) { - FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue); + if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue)) + { + char KeyASCII = KeyCodeToChar(Event.Key); + if (KeyASCII) + { + OutChar(&TextInputString, KeyASCII); + } + } } } } @@ -74,6 +90,8 @@ Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue } } + State->Interface.TempInputString = TextInputString.ConstString; + ClearCommandQueue(&State->CommandQueue); } @@ -84,6 +102,15 @@ Editor_Update(app_state* State, context* Context, input_queue InputQueue) 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); } @@ -91,88 +118,38 @@ Editor_Update(app_state* State, context* Context, input_queue InputQueue) internal void Editor_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer) { + State->Interface.WindowBounds = Context->WindowBounds; PushRenderOrthographic(RenderBuffer, State->WindowBounds); PushRenderClearScreen(RenderBuffer); + ui_InterfaceReset(&State->Interface); State->Interface.RenderBuffer = RenderBuffer; - - ui_layout Layout = ui_CreateLayout(&State->Interface, Context->WindowBounds); - ui_PushLayout(&State->Interface, Layout); - - DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context); - - for (s32 m = 0; m < State->Modes.ActiveModesCount; m++) + ui_PushLayout(&State->Interface, Context->WindowBounds, LayoutDirection_TopDown, MakeString("Editor Layout")); { - operation_mode OperationMode = State->Modes.ActiveModes[m]; - if (OperationMode.Render != 0) + DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context); + + for (s32 m = 0; m < State->Modes.ActiveModesCount; m++) { - 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")); - ui_PopLayout(&State->Interface); // Draw the Interface - for (u32 i = 0; i < State->Interface.WidgetsCount; i++) + if (State->Interface.DrawOrderRoot != 0) { - ui_widget Widget = State->Interface.Widgets[i]; - - if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground)) - { - v4 Color = State->Interface.Style.ButtonColor_Inactive; - if (ui_WidgetIdsEqual(Widget.Id, State->Interface.HotWidget)) - { - Color = State->Interface.Style.ButtonColor_Active; - } - if (ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget)) - { - Color = State->Interface.Style.ButtonColor_Selected; - } - PushRenderQuad2D(RenderBuffer, Widget.Bounds.Min, Widget.Bounds.Max, Color); - } - - if (Widget.String.Length > 0) - { - v4 Color = State->Interface.Style.TextColor; - render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, - Widget.String.Length, - State->Interface.Style.Font->BitmapMemory, - State->Interface.Style.Font->BitmapTextureHandle, - State->Interface.Style.Font->BitmapWidth, - State->Interface.Style.Font->BitmapHeight, - State->Interface.Style.Font->BitmapBytesPerPixel, - State->Interface.Style.Font->BitmapStride); - - v2 RegisterPosition = Widget.Bounds.Min + State->Interface.Style.Margin; - - switch (Widget.Alignment) - { - case Align_Left: - { - RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, Color); - }break; - - case Align_Right: - { - RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, Color); - }break; - - InvalidDefaultCase; - } - } - - if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline)) - { - // TODO(pjs): replace these with values from the style - r32 Thickness = 1.0f; - v4 Color = WhiteV4; - PushRenderBoundingBox2D(RenderBuffer, Widget.Bounds.Min, Widget.Bounds.Max, Thickness, Color); - } + ui_widget Widget = *State->Interface.DrawOrderRoot; + Editor_DrawWidget(State, Context, RenderBuffer, Widget, Context->WindowBounds); } Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext); - Context->GeneralWorkQueue->ResetWorkQueue(Context->GeneralWorkQueue); + ResetWorkQueue(Context->GeneralWorkQueue); } diff --git a/src/app/editor/foldhaus_editor_draw.h b/src/app/editor/foldhaus_editor_draw.h new file mode 100644 index 0000000..00c7949 --- /dev/null +++ b/src/app/editor/foldhaus_editor_draw.h @@ -0,0 +1,151 @@ +// +// File: foldhaus_editor_draw.h +// Author: Peter Slattery +// Creation Date: 2021-01-16 +// +#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) +{ + gs_string Temp = PushString(State->Transient, 256); + PrintF(&Temp, "%d", Widget.Id.Id); + render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, + Widget.String.Length, + State->Interface.Style.Font->BitmapMemory, + State->Interface.Style.Font->BitmapTextureHandle, + State->Interface.Style.Font->BitmapWidth, + State->Interface.Style.Font->BitmapHeight, + State->Interface.Style.Font->BitmapBytesPerPixel, + State->Interface.Style.Font->BitmapStride); + + v2 RegisterPosition = Widget.Bounds.Min + State->Interface.Style.Margin; + + switch (Widget.Alignment) + { + case Align_Left: + { + RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color); + }break; + + case Align_Right: + { + RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color); + }break; + + InvalidDefaultCase; + } +} + +enum widget_fill_dir +{ + WidgetFill_Horizontal = 0, + WidgetFill_Vertical = 1, +}; + +internal rect2 +Editor_GetWidgetFillBounds(ui_widget Widget) +{ + Assert(ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill) || ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawVerticalFill)); + + rect2 Result = {}; + + widget_fill_dir Dir = WidgetFill_Horizontal; + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill)) { Dir = WidgetFill_Vertical; } + widget_fill_dir OtherDir = (widget_fill_dir)(WidgetFill_Vertical - Dir); + + Result.Min.E[Dir] = Widget.Bounds.Min.E[Dir]; + Result.Max.E[Dir] = Widget.Bounds.Max.E[Dir]; + r32 FillToPoint = LerpR32(Widget.FillPercent, Widget.Bounds.Min.E[OtherDir], Widget.Bounds.Max.E[OtherDir]); + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawFillReversed)) + { + Result.Min.E[OtherDir] = FillToPoint; + Result.Max.E[OtherDir] = Widget.Bounds.Max.E[OtherDir]; + } + else if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawFillAsHandle)) + { + Result.Min.E[OtherDir] = FillToPoint - 5; + Result.Max.E[OtherDir] = FillToPoint + 5; + } + else + { + Result.Min.E[OtherDir] = Widget.Bounds.Min.E[OtherDir]; + Result.Max.E[OtherDir] = FillToPoint; + } + + return Result; +} + +internal void +Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ParentClipBounds) +{ + rect2 WidgetParentUnion = Widget.Bounds; + WidgetParentUnion = Rect2Union(Widget.Bounds, ParentClipBounds); + + if (!Widget.Parent || (Rect2Area(WidgetParentUnion) > 0)) + { + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground)) + { + v4 Color = State->Interface.Style.ButtonColor_Inactive; + if (ui_WidgetIdsEqual(Widget.Id, State->Interface.HotWidget)) + { + Color = State->Interface.Style.ButtonColor_Active; + } + if (ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget)) + { + Color = State->Interface.Style.ButtonColor_Selected; + } + PushRenderQuad2DClipped(RenderBuffer, Widget.Bounds, WidgetParentUnion, Color); + } + + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0) + { + v4 Color = State->Interface.Style.TextColor; + Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, Color); + } + + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill) || + ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawVerticalFill)) + { + v4 Color = State->Interface.Style.ButtonColor_Selected; + if (ui_WidgetIdsEqual(Widget.Id, State->Interface.HotWidget) || + ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget)) + { + Color = WhiteV4; + } + + rect2 FillBounds = Editor_GetWidgetFillBounds(Widget); + rect2 ClippedFillBounds = Rect2Union(FillBounds, WidgetParentUnion); + PushRenderQuad2D(RenderBuffer, ClippedFillBounds, Color); + + 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); + } + } + + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline)) + { + // TODO(pjs): replace these with values from the style + r32 Thickness = 1.0f; + v4 Color = WhiteV4; + 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); + } +} + + +#define FOLDHAUS_EDITOR_DRAW_H +#endif // FOLDHAUS_EDITOR_DRAW_H \ No newline at end of file diff --git a/src/app/editor/foldhaus_interface.cpp b/src/app/editor/foldhaus_interface.cpp index 7a83912..d194260 100644 --- a/src/app/editor/foldhaus_interface.cpp +++ b/src/app/editor/foldhaus_interface.cpp @@ -98,38 +98,38 @@ FOLDHAUS_INPUT_COMMAND_PROC(EndDragPanelBorderOperation) if (OpState->PanelEditMode == PanelEdit_Modify) { - if (Panel->SplitDirection == PanelSplit_Horizontal) + if (OpState->Panel->SplitDirection == PanelSplit_Horizontal) { r32 NewSplitY = Mouse.Pos.y; if (NewSplitY <= PanelBounds.Min.y) { - ConsolidatePanelsKeepOne(Panel, Panel->Top, &State->PanelSystem); + ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Top, &State->PanelSystem); } else if (NewSplitY >= PanelBounds.Max.y) { - ConsolidatePanelsKeepOne(Panel, Panel->Bottom, &State->PanelSystem); + ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Bottom, &State->PanelSystem); } else { - Panel->SplitPercent = (NewSplitY - PanelBounds.Min.y) / Rect2Height(PanelBounds); - Panel_UpdateLayout(Panel, PanelBounds); + OpState->Panel->SplitPercent = (NewSplitY - PanelBounds.Min.y) / Rect2Height(PanelBounds); + Panel_UpdateLayout(OpState->Panel, PanelBounds); } } - else if (Panel->SplitDirection == PanelSplit_Vertical) + else if (OpState->Panel->SplitDirection == PanelSplit_Vertical) { r32 NewSplitX = Mouse.Pos.x; if (NewSplitX <= PanelBounds.Min.x) { - ConsolidatePanelsKeepOne(Panel, Panel->Right, &State->PanelSystem); + ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Right, &State->PanelSystem); } else if (NewSplitX >= PanelBounds.Max.x) { - ConsolidatePanelsKeepOne(Panel, Panel->Left, &State->PanelSystem); + ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Left, &State->PanelSystem); } else { - Panel->SplitPercent = (NewSplitX - PanelBounds.Min.x) / Rect2Width(PanelBounds); - Panel_UpdateLayout(Panel, PanelBounds); + OpState->Panel->SplitPercent = (NewSplitX - PanelBounds.Min.x) / Rect2Width(PanelBounds); + Panel_UpdateLayout(OpState->Panel, PanelBounds); } } } @@ -140,11 +140,11 @@ FOLDHAUS_INPUT_COMMAND_PROC(EndDragPanelBorderOperation) r32 SplitY = LerpR32(OpState->Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y); if (Mouse.Pos.y > SplitY) { - ConsolidatePanelsKeepOne(Panel, Panel->Bottom, &State->PanelSystem); + ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Bottom, &State->PanelSystem); } else { - ConsolidatePanelsKeepOne(Panel, Panel->Top, &State->PanelSystem); + ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Top, &State->PanelSystem); } } else if (OpState->PanelEdgeDirection == PanelSplit_Vertical) @@ -152,11 +152,11 @@ FOLDHAUS_INPUT_COMMAND_PROC(EndDragPanelBorderOperation) r32 SplitX = LerpR32(OpState->Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x); if (Mouse.Pos.x > SplitX) { - ConsolidatePanelsKeepOne(Panel, Panel->Left, &State->PanelSystem); + ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Left, &State->PanelSystem); } else { - ConsolidatePanelsKeepOne(Panel, Panel->Right, &State->PanelSystem); + ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Right, &State->PanelSystem); } } } @@ -338,14 +338,16 @@ HandleMousePanelInteraction(panel_system* PanelSystem, rect2 WindowBounds, mouse } internal void -DrawPanelBorder(panel Panel, v2 PanelMin, v2 PanelMax, v4 Color, mouse_state* Mouse, render_command_buffer* RenderBuffer) +DrawPanelBorder(panel Panel, v2 PanelMin, v2 PanelMax, mouse_state* Mouse, render_command_buffer* RenderBuffer) { r32 MouseLeftEdgeDistance = Abs(Mouse->Pos.x - PanelMin.x); r32 MouseRightEdgeDistance = Abs(Mouse->Pos.x - PanelMax.x); r32 MouseTopEdgeDistance = Abs(Mouse->Pos.y - PanelMax.y); r32 MouseBottomEdgeDistance = Abs(Mouse->Pos.y - PanelMin.y); + v4 Color = BlackV4; PushRenderBoundingBox2D(RenderBuffer, PanelMin, PanelMax, 1, Color); + v4 HighlightColor = v4{.3f, .3f, .3f, 1.f}; r32 HighlightThickness = 1; if (MouseLeftEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE) @@ -353,28 +355,28 @@ DrawPanelBorder(panel Panel, v2 PanelMin, v2 PanelMax, v4 Color, mouse_state* Mo v2 LeftEdgeMin = PanelMin; v2 LeftEdgeMax = v2{PanelMin.x + HighlightThickness, PanelMax.y}; PushRenderQuad2D(RenderBuffer, LeftEdgeMin, LeftEdgeMax, HighlightColor); - Mouse->CursorType = CursorType_HorizontalArrows; + Mouse->CursorType = CursorType_HArrows; } else if (MouseRightEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE) { v2 RightEdgeMin = v2{PanelMax.x - HighlightThickness, PanelMin.y}; v2 RightEdgeMax = PanelMax; PushRenderQuad2D(RenderBuffer, RightEdgeMin, RightEdgeMax, HighlightColor); - Mouse->CursorType = CursorType_HorizontalArrows; + Mouse->CursorType = CursorType_HArrows; } else if (MouseTopEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE) { v2 TopEdgeMin = v2{PanelMin.x, PanelMax.y - HighlightThickness}; v2 TopEdgeMax = PanelMax; PushRenderQuad2D(RenderBuffer, TopEdgeMin, TopEdgeMax, HighlightColor); - Mouse->CursorType = CursorType_VerticalArrows; + Mouse->CursorType = CursorType_VArrows; } else if (MouseBottomEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE) { v2 BottomEdgeMin = PanelMin; v2 BottomEdgeMax = v2{PanelMax.x, PanelMin.y + HighlightThickness}; PushRenderQuad2D(RenderBuffer, BottomEdgeMin, BottomEdgeMax, HighlightColor); - Mouse->CursorType = CursorType_VerticalArrows; + Mouse->CursorType = CursorType_VArrows; } } @@ -386,7 +388,8 @@ DrawPanelFooter(panel* Panel, render_command_buffer* RenderBuffer, rect2 FooterB rect2 PanelSelectBtnBounds = MakeRect2MinDim(FooterBounds.Min + v2{30, 1}, v2{100, 23}); - if (ui_BeginDropdown(&State->Interface, MakeString("Select"), PanelSelectBtnBounds)) + panel_definition CurrentDef = State->PanelSystem.PanelDefs[Panel->TypeIndex]; + if (ui_BeginDropdown(&State->Interface, MakeString(CurrentDef.PanelName, CurrentDef.PanelNameLength), PanelSelectBtnBounds)) { for (s32 i = 0; i < GlobalPanelDefsCount; i++) { @@ -427,6 +430,7 @@ RenderPanel(panel* Panel, rect2 PanelBounds, rect2 WindowBounds, render_command_ internal void DrawPanelRecursive(panel* Panel, render_command_buffer* RenderBuffer, mouse_state* Mouse, app_state* State, context Context) { + rect2 Bounds = Panel->Bounds; switch (Panel->SplitDirection) { case PanelSplit_Horizontal: @@ -439,11 +443,9 @@ DrawPanelRecursive(panel* Panel, render_command_buffer* RenderBuffer, mouse_stat case PanelSplit_NoSplit: { panel* OverridePanel = Panel_GetModalOverride(Panel); - RenderPanel(OverridePanel, OverridePanel->Bounds, State->WindowBounds, RenderBuffer, State, Context, *Mouse); - v4 BorderColor = v4{0, 0, 0, 1}; - + RenderPanel(OverridePanel, Bounds, State->WindowBounds, RenderBuffer, State, Context, *Mouse); PushRenderOrthographic(RenderBuffer, State->WindowBounds); - DrawPanelBorder(*OverridePanel, OverridePanel->Bounds.Min, OverridePanel->Bounds.Max, BorderColor, Mouse, RenderBuffer); + DrawPanelBorder(*OverridePanel, Bounds.Min, Bounds.Max, Mouse, RenderBuffer); }break; InvalidDefaultCase; diff --git a/src/app/editor/foldhaus_operation_mode.h b/src/app/editor/foldhaus_operation_mode.h index e45606a..81e12fe 100644 --- a/src/app/editor/foldhaus_operation_mode.h +++ b/src/app/editor/foldhaus_operation_mode.h @@ -40,7 +40,7 @@ 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 = 16; // TODO(Peter): Static number of modes that can be active simultaneously + Result.ModeMemoryPagesFreeList.CountMax = 32; // TODO(Peter): Static number of modes that can be active simultaneously Result.ModeMemoryPagesFreeList.Data = PushArray(Storage, gs_data, Result.ModeMemoryPagesFreeList.CountMax); for (u32 Page = 0; Page < Result.ModeMemoryPagesFreeList.CountMax; Page++) { diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 9bedf30..92ee12c 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -34,7 +34,7 @@ GetXPositionFromFrameInAnimationPanel (u32 Frame, rect2 PanelBounds, frame_range } internal handle -AddAnimationBlockAtCurrentTime (u32 AnimationProcHandle, u32 LayerHandle, animation_system* System) +AddAnimationBlockAtCurrentTime (animation_pattern_handle AnimationProcHandle, u32 LayerHandle, animation_system* System) { u32 NewBlockStart = System->CurrentFrame; u32 NewBlockEnd = NewBlockStart + SecondsToFrames(3, *System); @@ -240,14 +240,13 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); operation_mode* DragAnimationBlockMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationBlockCommands, UpdateDragAnimationBlock); + animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle); drag_animation_block_state* OpState = CreateOperationState(DragAnimationBlockMode, &State->Modes, drag_animation_block_state); OpState->TimelineBounds = TimelineBounds; OpState->BlockHandle = BlockHandle; OpState->VisibleRange = VisibleRange; - - animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle); OpState->ClipRange = SelectedBlock->Range; } // ------------------- @@ -261,7 +260,8 @@ FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand) frame_range Range = ActiveAnim->PlayableRange; u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, Panel->Bounds, Range); - handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), 4, TimelineState->SelectedAnimationLayer); + animation_pattern_handle PatternHandle = Patterns_IndexToHandle(4); + handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), PatternHandle, TimelineState->SelectedAnimationLayer); TimelineState->SelectedBlockHandle = NewBlockHandle; } @@ -303,10 +303,7 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r r32 BarWidth = Rect2Width(BarBounds); // Mouse clicked inside frame nubmer bar -> change current frame on timeline - // TODO(pjs): both of these functions can get wrapped in a MouseClickedRect - // and an alternate MouseIsDraggingRect - if (MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState) && - PointIsInRect(BarBounds, Interface.Mouse.DownPos)) + if (ui_MouseClickedRect(Interface, BarBounds)) { StartDragTimeMarker(BarBounds, VisibleFrames, State); } @@ -579,8 +576,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, GlobalAnimationPatternsCount, GlobalAnimationPatterns); + 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; @@ -588,20 +587,20 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) } internal void -DrawAnimationPatternList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem) +DrawAnimationPatternList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem, animation_pattern_array Patterns) { - ui_layout Layout = ui_CreateLayout(Interface, PanelBounds); - ui_PushLayout(Interface, Layout); - for (s32 i = 0; i < GlobalAnimationPatternsCount; i++) + ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("AnimClips Layout")); + for (u32 i = 0; i < Patterns.Count; i++) { - animation_pattern Pattern = GlobalAnimationPatterns[i]; + animation_pattern Pattern = Patterns.Values[i]; gs_string PatternName = MakeString(Pattern.Name, Pattern.NameLength); - if (ui_LayoutListEntry(Interface, &Layout, PatternName, i)) + if (ui_Button(Interface, PatternName)) { - AddAnimationBlockAtCurrentTime(i + 1, SelectedAnimationLayerHandle, AnimationSystem); + animation_pattern_handle PatternHandle = Patterns_IndexToHandle(i); + AddAnimationBlockAtCurrentTime(PatternHandle, SelectedAnimationLayerHandle, AnimationSystem); } } - ui_PopLayout(Interface); + ui_PopLayout(Interface, MakeString("AnimClips Layout")); } internal void @@ -609,11 +608,10 @@ PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Pan { animation_system* AnimSystem = &State->AnimationSystem; ui_interface* Interface = &State->Interface; - ui_layout Layout = ui_CreateLayout(Interface, Bounds); - ui_PushLayout(Interface, Layout); + ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("PlayBar Layout")); - ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]); - ui_StartRow(&State->Interface, 4); + ui_FillRect(Interface, Bounds, Interface->Style.PanelBG); + ui_BeginRow(&State->Interface, 4); { if (ui_Button(Interface, MakeString("Pause"))) { @@ -630,15 +628,9 @@ PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Pan AnimSystem->TimelineShouldAdvance = false; AnimSystem->CurrentFrame = 0; } - - if (ui_Button(Interface, MakeString("Load"))) - { - panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context); - Panel_PushModalOverride(Panel, FileBrowser, LoadAnimationFileCallback); - } } ui_EndRow(&State->Interface); - ui_PopLayout(&State->Interface); + ui_PopLayout(&State->Interface, MakeString("PlayBar Layout")); } internal void @@ -646,10 +638,14 @@ FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_ { ui_interface* Interface = &State->Interface; gs_string TempString = PushString(State->Transient, 256); - frame_range VisibleFrames = TimelineState->VisibleRange; + // :FrameRange + // frame_range VisibleFrames = TimelineState->VisibleRange; + animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + frame_range VisibleFrames = ActiveAnim.PlayableRange; + s32 VisibleFrameCount = VisibleFrames.Max - VisibleFrames.Min; - ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]); + ui_FillRect(Interface, Bounds, Interface->Style.PanelBG); // Frame Ticks u32 TickCount = 10; @@ -691,12 +687,12 @@ FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_ } internal void -LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) +LayerList_Render(animation_timeline_state* TimelineState, 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.PanelBGColors[0]); + ui_FillRect(Interface, Bounds, Interface->Style.PanelBG); v2 LayerDim = { Rect2Width(Bounds), LAYER_HEIGHT }; rect2 LayerBounds = {0}; @@ -726,9 +722,15 @@ internal void TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { ui_interface* Interface = &State->Interface; - frame_range ViewRange = TimelineState->VisibleRange; - 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; + handle SelectedBlockHandle = TimelineState->SelectedBlockHandle; s32 CurrentFrame = State->AnimationSystem.CurrentFrame; @@ -791,35 +793,95 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c } internal void -AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) +AnimInfoView_Render(animation_timeline_state* TimelineState, 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_layout Layout = ui_CreateLayout(Interface, Bounds); - ui_PushLayout(Interface, Layout); + ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout")); - ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBGColors[0]); + ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBG); - ui_StartRow(&State->Interface, 2); + if (ui_BeginLabeledDropdown(Interface, MakeString("Active Animation"), ActiveAnim->Name)) { - ui_DrawString(Interface, MakeString("Active Animation")); - if (ui_BeginDropdown(Interface, ActiveAnim->Name)) + for (u32 i = 0; i < AnimSystem->Animations.Count; i++) { - for (u32 i = 0; i < AnimSystem->Animations.Count; i++) + animation Animation = AnimSystem->Animations.Values[i]; + if (ui_Button(Interface, Animation.Name)) { - animation Animation = AnimSystem->Animations.Values[i]; - if (ui_Button(Interface, Animation.Name)) + AnimSystem->ActiveAnimationIndex = i; + } + } + } + ui_EndLabeledDropdown(&State->Interface); + + ui_BeginRow(Interface, 3); + { + if (ui_Button(Interface, MakeString("New"))) + { + animation NewAnim = {}; + NewAnim.Name = PushString(State->AnimationSystem.Storage, 256); + + u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); + State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex; + } + if (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]; + gs_string FileText = AnimSerializer_Serialize(ActiveAnimation, State->Patterns, State->Transient); + if (WriteEntireFile(Context.ThreadContext.FileHandler, ActiveAnimation.FileInfo.Path, StringToData(FileText))) + { + InvalidCodePath; + } + } + if (ui_Button(Interface, MakeString("Load"))) + { + panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context); + Panel_PushModalOverride(Panel, FileBrowser, LoadAnimationFileCallback); + } + + } + ui_EndRow(Interface); + + ui_TextEntry(Interface, MakeString("Anim Name"), &ActiveAnim->Name); + + ui_Label(Interface, MakeString("Frame Range")); + ui_BeginRow(Interface, 3); + { + 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))) + { + for (u32 i = 0; i < State->Patterns.Count; i++) + { + animation_pattern Pattern = State->Patterns.Values[i]; + if (ui_Button(Interface, MakeString(Pattern.Name, Pattern.NameLength))) { - AnimSystem->ActiveAnimationIndex = i; + SelectedBlock->AnimationProcHandle = Patterns_IndexToHandle(i); } } } - ui_EndDropdown(Interface); + ui_EndLabeledDropdown(Interface); } - ui_EndRow(&State->Interface); - ui_PopLayout(Interface); + + ui_PopLayout(Interface, MakeString("AnimInfo Layout")); } internal void @@ -835,31 +897,26 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* { animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state); + ui_FillRect(&State->Interface, PanelBounds, v4{.1f,.1f,.1f,1.f}); + rect2 TimelineBounds, InfoBounds; - RectVSplit(PanelBounds, 300, &InfoBounds, &TimelineBounds); + RectVSplitAtDistanceFromLeft(PanelBounds, 300, &InfoBounds, &TimelineBounds); - rect2 AnimInfoBounds, SelectionInfoBounds; - RectHSplitAtPercent(InfoBounds, .65f, &AnimInfoBounds, &SelectionInfoBounds); + rect2 LayersPanelBounds, TimeRangePanelBounds; + RectVSplitAtDistanceFromLeft(TimelineBounds, 200, &LayersPanelBounds, &TimeRangePanelBounds); - { // Timeline - rect2 LayersPanelBounds, TimeRangePanelBounds; - RectVSplitAtDistanceFromLeft(TimelineBounds, 200, &LayersPanelBounds, &TimeRangePanelBounds); - - r32 TitleBarHeight = State->Interface.Style.RowHeight; - // These are the actual rects we will draw in - rect2 PlayBarBounds, FrameCountBounds; - rect2 LayersBounds, TimeRangeBounds; - RectHSplitAtDistanceFromTop(LayersPanelBounds, TitleBarHeight, &PlayBarBounds, &LayersBounds); - RectHSplitAtDistanceFromTop(TimeRangePanelBounds, TitleBarHeight, &FrameCountBounds, &TimeRangeBounds); - - PlayBar_Render(TimelineState, PlayBarBounds, Panel, RenderBuffer, State, Context); - FrameCount_Render(TimelineState, FrameCountBounds, RenderBuffer, State, Context); - LayerList_Render(TimelineState, LayersBounds, RenderBuffer, State, Context); - TimeRange_Render(TimelineState, TimeRangeBounds, RenderBuffer, State, Context); - } + r32 TitleBarHeight = State->Interface.Style.RowHeight; + // These are the actual rects we will draw in + rect2 PlayBarBounds, FrameCountBounds; + rect2 LayersBounds, TimeRangeBounds; + RectHSplitAtDistanceFromTop(LayersPanelBounds, TitleBarHeight, &PlayBarBounds, &LayersBounds); + RectHSplitAtDistanceFromTop(TimeRangePanelBounds, TitleBarHeight, &FrameCountBounds, &TimeRangeBounds); - AnimInfoView_Render(TimelineState, AnimInfoBounds, RenderBuffer, State, Context); - SelectionInfoView_Render(TimelineState, SelectionInfoBounds, RenderBuffer, State, Context); + 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); } #define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H diff --git a/src/app/editor/panels/foldhaus_panel_assembly_debug.h b/src/app/editor/panels/foldhaus_panel_assembly_debug.h new file mode 100644 index 0000000..2f4f1a2 --- /dev/null +++ b/src/app/editor/panels/foldhaus_panel_assembly_debug.h @@ -0,0 +1,116 @@ +// +// File: foldhaus_panel_assembly_debug.h +// Author: Peter Slattery +// Creation Date: 2021-01-15 +// +#ifndef FOLDHAUS_PANEL_ASSEMBLY_DEBUG_H + +GSMetaTag(panel_init); +GSMetaTag(panel_type_file_view); +internal void +AssemblyDebug_Init(panel* Panel, app_state* State, context Context) +{ +} + +GSMetaTag(panel_cleanup); +GSMetaTag(panel_type_file_view); +internal void +AssemblyDebug_Cleanup(panel* Panel, app_state* State) +{ + +} + +// TODO(pjs): This is really blumen specific +#define FSC(f,c) FlowerStripToChannel((f), (c)) +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; +} + +GSMetaTag(panel_render); +GSMetaTag(panel_type_file_view); +internal void +AssemblyDebug_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) +{ + 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)) + { + 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); + + InterfaceAssert(Interface->PerFrameMemory); + }break; + } + + ui_RangeSlider(Interface, MakeString("Test"), .5f, 0, 1); + + ui_PopLayout(Interface, MakeString("Assembly Debug Layout")); +} + +#define FOLDHAUS_PANEL_ASSEMBLY_DEBUG_H +#endif // FOLDHAUS_PANEL_ASSEMBLY_DEBUG_H \ No newline at end of file diff --git a/src/app/editor/panels/foldhaus_panel_file_view.h b/src/app/editor/panels/foldhaus_panel_file_view.h index f4427c0..eaf5013 100644 --- a/src/app/editor/panels/foldhaus_panel_file_view.h +++ b/src/app/editor/panels/foldhaus_panel_file_view.h @@ -5,8 +5,16 @@ // #ifndef FOLDHAUS_PANEL_FILE_VIEW_H +enum file_view_mode +{ + FileViewMode_Load, + FileViewMode_Save, +}; + struct file_view_state { + file_view_mode Mode; + gs_string WorkingDirectory; gs_memory_arena FileNamesArena; gs_file_info_array FileNames; @@ -14,9 +22,18 @@ struct file_view_state 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; +} + 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) @@ -30,7 +47,7 @@ global input_command* FileView_Commands = 0; s32 FileView_CommandsCount = 0; internal void -FileViewUpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_state* State, context Context) +FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_state* State, context Context) { ClearArena(&State->FileNamesArena); @@ -43,23 +60,20 @@ FileViewUpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_state u32 SecondLastSlashIndex = FindLast(SanitizedDirectory, LastSlashIndex - 1, '\\'); SanitizedDirectory = Substring(SanitizedDirectory, 0, SecondLastSlashIndex); } - else if (StringsEqual(LastDir, ConstString("."))) + else if (StringsEqual(LastDir, ConstString(".")) && LastDir.Length > 1) { SanitizedDirectory = Substring(SanitizedDirectory, 0, LastSlashIndex); } - State->WorkingDirectory = PushString(&State->FileNamesArena, WorkingDirectory.Length + 2); - PrintF(&State->WorkingDirectory, "%S", SanitizedDirectory); - if (State->WorkingDirectory.Str[State->WorkingDirectory.Length - 1] != '\\') + gs_file_info NewWorkingDirectory = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDirectory); + if (NewWorkingDirectory.IsDirectory) { - AppendPrintF(&State->WorkingDirectory, "\\"); + // 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); + + State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, State->WorkingDirectory.ConstString, EnumerateDirectory_IncludeDirectories); } - if (State->WorkingDirectory.Str[State->WorkingDirectory.Length - 1] != '*') - { - AppendPrintF(&State->WorkingDirectory, "*"); - } - - State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, State->WorkingDirectory.ConstString, EnumerateDirectory_IncludeDirectories); } GSMetaTag(panel_init); @@ -71,7 +85,10 @@ FileView_Init(panel* Panel, app_state* State, context Context) file_view_state* FileViewState = PushStruct(&State->Permanent, file_view_state); Panel->StateMemory = StructToData(FileViewState, file_view_state); FileViewState->FileNamesArena = CreateMemoryArena(Context.ThreadContext.Allocator); - FileViewUpdateWorkingDirectory(ConstString("."), FileViewState, Context); + + // TODO(pjs): this shouldn't be stored in permanent + FileViewState->WorkingDirectory = PushString(&State->Permanent, 256); + FileView_UpdateWorkingDirectory(ConstString("."), FileViewState, Context); } GSMetaTag(panel_cleanup); @@ -88,44 +105,61 @@ 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_layout Layout = ui_CreateLayout(&State->Interface, PanelBounds); - ui_PushLayout(&State->Interface, Layout); + Assert(FileViewState->Mode == FileViewMode_Save); - if (ui_Button(&State->Interface, MakeString("Exit"))) + ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout")); { - FileView_Exit_(Panel, State, Context); - } - - // Header - ui_DrawString(&State->Interface, FileViewState->WorkingDirectory); - - // File Display - for (u32 i = 0; i < FileViewState->FileNames.Count; i++) - { - gs_file_info File = FileViewState->FileNames.Values[i]; - - u32 LastSlashIndex = FindLast(File.Path, '\\'); - 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, &Layout, PathString, i)) + if (ui_Button(&State->Interface, MakeString("Exit"))) { - if (File.IsDirectory) + FileView_Exit_(Panel, State, Context); + } + + // Header + if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->WorkingDirectory)) + { + // if last character is a slash, update pwd, and clear the filter string + // otherwise update the filter string + gs_string Pwd = FileViewState->WorkingDirectory; + char LastChar = Pwd.Str[Pwd.Length - 1]; + if (LastChar == '\\' || LastChar == '/') { - FileViewUpdateWorkingDirectory(File.Path, FileViewState, Context); + FileView_UpdateWorkingDirectory(Pwd.ConstString, FileViewState, Context); } else { - FileViewState->SelectedFile = File; - FileView_Exit_(Panel, State, Context); + } } + + // 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, '\\'); + 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; + FileView_Exit_(Panel, State, Context); + } + } + } + ui_EndList(&State->Interface); } - - ui_PopLayout(&State->Interface); + ui_PopLayout(&State->Interface, MakeString("FileView Layout")); } - #define FOLDHAUS_PANEL_FILE_VIEW_H #endif // FOLDHAUS_PANEL_FILE_VIEW_H \ No newline at end of file diff --git a/src/app/editor/panels/foldhaus_panel_hierarchy.h b/src/app/editor/panels/foldhaus_panel_hierarchy.h index 54b6b8c..13d45fe 100644 --- a/src/app/editor/panels/foldhaus_panel_hierarchy.h +++ b/src/app/editor/panels/foldhaus_panel_hierarchy.h @@ -38,50 +38,44 @@ GSMetaTag(panel_type_hierarchy); internal void HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { - ui_layout Layout = ui_CreateLayout(&State->Interface, PanelBounds); - ui_PushLayout(&State->Interface, Layout); - gs_string TempString = PushString(State->Transient, 256); - u32 LineCount = (u32)(Rect2Height(PanelBounds) / Layout.RowHeight) + 1; - u32 AssembliesToDraw = Min(LineCount, State->Assemblies.Count); - rect2* LineBounds = PushArray(State->Transient, rect2, LineCount); - // Fill in alternating color rows for the backgrounds - for (u32 Line = 0; Line < LineCount; Line++) + ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("Hierarchy Layout")); + ui_BeginList(&State->Interface, MakeString("Hierarchy List"), 10, State->Assemblies.Count + 1); { - LineBounds[Line] = ui_ReserveElementBounds(&Layout); - v4 ListItemBGColor = ui_GetListItemBGColor(State->Interface.Style, Line); - ui_FillRect(&State->Interface, LineBounds[Line], ListItemBGColor); - } - - for (u32 AssemblyIndex = 0; AssemblyIndex < AssembliesToDraw; AssemblyIndex++) - { - assembly Assembly = State->Assemblies.Values[AssemblyIndex]; - PrintF(&TempString, "%S", Assembly.Name); - - ui_StartRow(&State->Interface, 2); + ui_column_spec Cols[2] = { + ui_column_spec{ UIColumnSize_Fill, 0 }, + ui_column_spec{ UIColumnSize_MaxWidth, 128 } + }; + for (u32 i = 0; i < State->Assemblies.Count; i++) { - ui_DrawString(&State->Interface, TempString); - if (ui_LayoutListButton(&State->Interface, &Layout, MakeString("X"), AssemblyIndex)) + ui_BeginRow(&State->Interface, 2, &Cols[0]); + + assembly Assembly = State->Assemblies.Values[i]; + PrintF(&TempString, "%S", Assembly.Name); + + ui_Label(&State->Interface, TempString); + if (ui_Button(&State->Interface, MakeString("X"))) { - UnloadAssembly(AssemblyIndex, State, Context); + UnloadAssembly(i, State, Context); } + + ui_EndRow(&State->Interface); + } + + + ui_BeginRow(&State->Interface, 2, &Cols[0]); + ui_Label(&State->Interface, MakeString(" ")); + if (ui_Button(&State->Interface, MakeString("+ Add Assembly"))) + { + panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context); + FileView_SetMode(FileBrowser, FileViewMode_Save); + Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback); } ui_EndRow(&State->Interface); } - - if (AssembliesToDraw < LineCount) - { - // NOTE(Peter): Add assembly button - PrintF(&TempString, "+ Add Assembly"); - if (ui_ListButton(&State->Interface, TempString, LineBounds[AssembliesToDraw], AssembliesToDraw)) - { - panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context); - Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback); - } - } - - ui_PopLayout(&State->Interface); + ui_EndList(&State->Interface); + ui_PopLayout(&State->Interface, MakeString("Hierarchy Layout")); } diff --git a/src/app/editor/panels/foldhaus_panel_profiler.h b/src/app/editor/panels/foldhaus_panel_profiler.h index 0f38547..405b50f 100644 --- a/src/app/editor/panels/foldhaus_panel_profiler.h +++ b/src/app/editor/panels/foldhaus_panel_profiler.h @@ -25,86 +25,117 @@ ProfilerView_Cleanup(panel* Panel, app_state* State) } internal void -RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_layout Layout, debug_frame* VisibleFrame, gs_memory_arena* Memory) +RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Transient) { - v4 ThreadColors[] = { - v4{.73f, .33f, .83f, 1}, - v4{0, .50f, .50f, 1}, - v4{.83f, 0, 0, 1}, - v4{.33f, .49f, .83f, 1}, - v4{.74f, .40f, .25f, 1}, - }; - - rect2 Bounds = ui_LayoutRemaining(Layout); + rect2 Bounds = ui_LayoutRemaining(*Layout); r32 Width = Rect2Width(Bounds); - r32 DepthHeight = 64; + r32 DepthHeight = 32; s64 FrameStartCycles = VisibleFrame->FrameStartCycles; - s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles; + r32 FrameTotalCycles = (r32)(VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles); - debug_scope_record_list* ThreadScopeCalls = GetScopeListForThreadInFrame(GlobalDebugServices, - VisibleFrame); + r32 NextThreadTop = Bounds.Max.y; - scope_record* HotRecord = 0; - scope_name* HotRecordName = 0; - - char Backbuffer[256]; - gs_string String = MakeString(Backbuffer, 0, 256); - for (s32 i = 0; i < ThreadScopeCalls->Count; i++) + for (s32 t = 0; t < VisibleFrame->ThreadCount; t++) { - scope_record* Record = ThreadScopeCalls->Calls + i; - scope_name* Name = GetOrAddNameHashEntry(VisibleFrame, Record->NameHash); - r32 PercentStart = (r32)(Record->StartCycles - FrameStartCycles) / (r32)FrameTotalCycles; - r32 PercentEnd = (r32)(Record->EndCycles - FrameStartCycles) / (r32)FrameTotalCycles; + debug_scope_record_list ThreadCalls = VisibleFrame->ThreadCalls[t]; - r32 PixelStart = Bounds.Min.x + (Width * PercentStart); - r32 PixelEnd = Bounds.Min.x + (Width * PercentEnd); - r32 MinY = Bounds.Max.y - ((Record->CallDepth + 1) * DepthHeight); - rect2 ScopeBounds = { - v2{ PixelStart, MinY }, - v2{ PixelEnd, MinY + (DepthHeight - 4) } - }; - if (Rect2Width(ScopeBounds) >= 1) + 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++) { - v4 Color = ThreadColors[0]; - if (PointIsInRect(ScopeBounds, Interface->Mouse.Pos)) - { - Color = GreenV4; - HotRecord = Record; - HotRecordName = Name; - } + 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; - ui_FillRect(Interface, ScopeBounds, Color); - ui_OutlineRect(Interface, ScopeBounds, 1, BlackV4); + 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); + } } - } - - if (HotRecord != 0) - { - PrintF(&String, "%S : %d - %d", HotRecordName->Name, HotRecord->StartCycles, HotRecord->EndCycles); - rect2 TextBounds = MakeRect2MinDim(Interface->Mouse.Pos, v2{256, 32}); - ui_DrawString(Interface, String, TextBounds); + NextThreadTop = ThreadScopeMin; } } internal void -RenderProfiler_ListVisualization(ui_interface* Interface, ui_layout Layout, debug_frame* VisibleFrame, gs_memory_arena* Memory) +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); - r32 ColumnWidths[] = {256, 128, 128, 128, 128}; - ui_StartRow(Interface, 5, &ColumnWidths[0]); + 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_DrawString(Interface, MakeString("Procedure")); - ui_DrawString(Interface, MakeString("% Frame")); - ui_DrawString(Interface, MakeString("Seconds")); - ui_DrawString(Interface, MakeString("Cycles")); - ui_DrawString(Interface, MakeString("Calls")); + 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) + { + CountedScopes += 1; + } + } + + 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]; @@ -112,26 +143,24 @@ RenderProfiler_ListVisualization(ui_interface* Interface, ui_layout Layout, debu { collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n; - ui_StartRow(Interface, 5, &ColumnWidths[0]); - { - PrintF(&String, "%S", NameEntry.Name); - ui_DrawString(Interface, String); - - PrintF(&String, "%f%%", CollatedRecord->PercentFrameTime); - ui_DrawString(Interface, String); - - PrintF(&String, "%fs", CollatedRecord->TotalSeconds); - ui_DrawString(Interface, String); - - PrintF(&String, "%dcy", CollatedRecord->TotalCycles); - ui_DrawString(Interface, String); - - PrintF(&String, "%d", CollatedRecord->CallCount); - ui_DrawString(Interface, String); - } - ui_EndRow(Interface); + 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); } GSMetaTag(panel_render); @@ -179,23 +208,22 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices); - ui_layout Layout = ui_CreateLayout(&State->Interface, ProcListBounds); - ui_PushLayout(&State->Interface, Layout); + ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown, MakeString("Profiler Layout")); - ui_StartRow(&State->Interface, 4); + 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_DrawString(&State->Interface, String); + ui_Label(&State->Interface, String); PrintF(&String, "Total Cycles: %lld", FrameTotalCycles); - ui_DrawString(&State->Interface, String); + 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_ReserveElementBounds(&Layout); + ui_ReserveBounds(&State->Interface, Layout, true); if (ui_Button(&State->Interface, MakeString("Resume Recording"))) { @@ -204,7 +232,7 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend } ui_EndRow(&State->Interface); - ui_StartRow(&State->Interface, 8); + ui_BeginRow(&State->Interface, 8); { if (ui_Button(&State->Interface, MakeString("Scope View"))) { @@ -226,7 +254,7 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory); } - ui_PopLayout(&State->Interface); + 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 ee4fecb..6d4c803 100644 --- a/src/app/editor/panels/foldhaus_panel_sculpture_view.h +++ b/src/app/editor/panels/foldhaus_panel_sculpture_view.h @@ -5,6 +5,12 @@ // #ifndef FOLDHAUS_PANEL_SCULPTURE_VIEW_H +// Definitions + +#define PIXEL_TO_WORLD_SCALE 0.01f + +// + struct sculpture_view_panel_state { camera Camera; @@ -24,8 +30,8 @@ OPERATION_RENDER_PROC(Update3DViewMouseRotate) v2 TotalDeltaPos = Mouse.Pos - Mouse.DownPos; - m44 XRotation = M44RotationX(-TotalDeltaPos.y * State->PixelsToWorldScale); - m44 YRotation = M44RotationY(TotalDeltaPos.x * State->PixelsToWorldScale); + 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; @@ -226,14 +232,10 @@ SculptureView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren v2 LedOnScreenPosition = SculptureView_WorldToScreenPosition(LedPosition, PanelState->Camera, PanelBounds); gs_string Tempgs_string = PushString(State->Transient, 256); - PrintF(&Tempgs_string, "%f %f", LedOnScreenPosition.x, LedOnScreenPosition.y); + 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); - - v2 BoxHalfDim = v2{ 25, 25 }; - v2 BoxMin = LedOnScreenPosition - BoxHalfDim; - v2 BoxMax = LedOnScreenPosition + BoxHalfDim; - PushRenderBoundingBox2D(RenderBuffer, BoxMin, BoxMax, 2.0f, TealV4); } Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); } diff --git a/src/app/editor/panels/foldhaus_panel_types.cpp b/src/app/editor/panels/foldhaus_panel_types.cpp index 9c006fa..ea56618 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 = 6; +global s32 GlobalPanelDefsCount = 7; 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 }, @@ -12,6 +12,7 @@ global panel_definition GlobalPanelDefs[] = { { "Dmx View", 8, DMXView_Init, DMXView_Cleanup, DMXView_Render, DMXView_Commands, DMXView_CommandsCount }, { "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 }, }; #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 7b1164b..9177cdd 100644 --- a/src/app/editor/panels/foldhaus_panel_types.h +++ b/src/app/editor/panels/foldhaus_panel_types.h @@ -10,8 +10,8 @@ enum panel_type { PanelType_AnimationTimeline, PanelType_DMXView, PanelType_HierarchyView, - PanelType_NodeGraph, PanelType_ProfilerView, + PanelType_AssemblyDebug, }; #define FOLDHAUS_PANEL_TYPES_H #endif // FOLDHAUS_PANEL_TYPES_H \ 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 0f160a0..d93f3f1 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) +#define ANIMATION_PROC(name) void name(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) typedef ANIMATION_PROC(animation_proc); struct frame_range @@ -14,6 +14,11 @@ struct frame_range s32 Max; }; +struct animation_pattern_handle +{ + s32 IndexPlusOne; +}; + // NOTE(pjs): An animation block is a time range paired with an // animation_pattern (see below). While a timeline's current time // is within the range of a block, that particular block's animation @@ -21,7 +26,7 @@ struct frame_range struct animation_block { frame_range Range; - u32 AnimationProcHandle; + animation_pattern_handle AnimationProcHandle; u32 Layer; }; @@ -76,6 +81,9 @@ struct animation 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_array @@ -132,6 +140,13 @@ struct animation_pattern animation_proc* Proc; }; +struct animation_pattern_array +{ + animation_pattern* Values; + u32 Count; + u32 CountMax; +}; + // Serialization enum animation_field @@ -185,6 +200,55 @@ global gs_const_string AnimationFieldStrings[] = { ConstString("animation_name"),// AnimField_BlockAnimName }; + +////////////////////////// +// +// Patterns List + +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; +} + +#define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc))) +internal void +Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength) +{ + Assert(Array->Count < Array->CountMax); + + animation_pattern Pattern = {0}; + Pattern.Name = Name; + Pattern.NameLength = NameLength; + Pattern.Proc = Proc; + + Array->Values[Array->Count++] = Pattern; +} + +internal animation_pattern_handle +Patterns_IndexToHandle(s32 Index) +{ + animation_pattern_handle Result = {}; + Result.IndexPlusOne = Index + 1; + 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; +} + ////////////////////////// // // Anim Block Array @@ -291,7 +355,7 @@ AnimationArray_Push(animation_array* Array, animation Value) // Animation internal handle -Animation_AddBlock(animation* Animation, u32 StartFrame, s32 EndFrame, u32 AnimationProcHandle, u32 LayerIndex) +Animation_AddBlock(animation* Animation, u32 StartFrame, s32 EndFrame, animation_pattern_handle AnimationProcHandle, u32 LayerIndex) { Assert(LayerIndex < Animation->Layers.Count); @@ -420,6 +484,24 @@ ClampFrameToRange(s32 Frame, frame_range Range) // System +struct animation_system_desc +{ + 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; + + return Result; +} + internal animation* AnimationSystem_GetActiveAnimation(animation_system* System) { diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index 83dc221..8915d59 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -81,22 +81,22 @@ LedBlend_GetProc(blend_mode BlendMode) } internal void -AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, assembly Assembly, animation_pattern* Patterns, gs_memory_arena* Transient) +AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, assembly Assembly, animation_pattern_array Patterns, gs_memory_arena* Transient, + u8* UserData) { u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min; r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame; - // :AnimProcHandle - u32 AnimationProcIndex = Block.AnimationProcHandle - 1; - animation_proc* AnimationProc = Patterns[AnimationProcIndex].Proc; - AnimationProc(Buffer, Assembly, SecondsIntoBlock, Transient); + animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle); + Pattern.Proc(Buffer, Assembly, SecondsIntoBlock, Transient, UserData); } internal void AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Assemblies, led_system* LedSystem, - animation_pattern* Patterns, - gs_memory_arena* Transient) + animation_pattern_array Patterns, + gs_memory_arena* Transient, + u8* UserData) { s32 CurrentFrame = System->CurrentFrame; r32 FrameTime = CurrentFrame * System->SecondsPerFrame; @@ -150,14 +150,14 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse { led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer; animation_block Block = LayerFrame.Hot; - AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient); + 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); + AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient, UserData); } } @@ -194,6 +194,8 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse } } } + + System->LastUpdatedFrame = System->CurrentFrame; } #define FOLDHAUS_ANIMATION_RENDERER_CPP diff --git a/src/app/engine/animation/foldhaus_animation_serializer.cpp b/src/app/engine/animation/foldhaus_animation_serializer.cpp index 8607b0f..7e688f2 100644 --- a/src/app/engine/animation/foldhaus_animation_serializer.cpp +++ b/src/app/engine/animation/foldhaus_animation_serializer.cpp @@ -6,7 +6,7 @@ #ifndef FOLDHAUS_ANIMATION_SERIALIZER_CPP internal gs_string -AnimSerializer_Serialize(animation Anim, animation_pattern* GlobalClips, gs_memory_arena* Arena) +AnimSerializer_Serialize(animation Anim, animation_pattern_array Patterns, gs_memory_arena* Arena) { serializer Serializer = {0}; Serializer.String = PushString(Arena, 4096); @@ -45,10 +45,7 @@ AnimSerializer_Serialize(animation Anim, animation_pattern* GlobalClips, gs_memo // TODO(pjs): Handle free'd animation blocks animation_block AnimationBlockAt = Anim.Blocks_.Values[i]; - // TODO(pjs): Systematize the AnimationProcHandle - // :AnimProcHandle - u32 AnimationProcIndex = AnimationBlockAt.AnimationProcHandle - 1; - animation_pattern Animation = GlobalClips[AnimationProcIndex]; + animation_pattern Animation = Patterns_GetPattern(Patterns, AnimationBlockAt.AnimationProcHandle); Serializer_OpenStruct(&Serializer, AnimField_Block); { @@ -70,7 +67,7 @@ AnimSerializer_Serialize(animation Anim, animation_pattern* GlobalClips, gs_memo } internal animation -AnimParser_Parse(gs_string File, gs_memory_arena* Arena, u32 AnimClipsCount, animation_pattern* AnimClips) +AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array AnimPatterns) { animation Result = {0}; @@ -162,12 +159,13 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, u32 AnimClipsCount, ani // TODO(pjs): AnimName -> Animation Proc Handle gs_string AnimName = Parser_ReadStringValue(&Parser, AnimField_BlockAnimName); - Block.AnimationProcHandle = 0; - for (u32 i = 0; i < AnimClipsCount; i++) + Block.AnimationProcHandle = {0}; + for (u32 i = 0; i < AnimPatterns.Count; i++) { - if (StringEqualsCharArray(AnimName.ConstString, AnimClips[i].Name, CStringLength(AnimClips[i].Name))) + animation_pattern Pattern = AnimPatterns.Values[i]; + if (StringEqualsCharArray(AnimName.ConstString, Pattern.Name, Pattern.NameLength)) { - Block.AnimationProcHandle = i + 1; + Block.AnimationProcHandle = Patterns_IndexToHandle(i); break; } } diff --git a/src/app/engine/assembly/foldhaus_assembly.cpp b/src/app/engine/assembly/foldhaus_assembly.cpp index 1d68fdb..4ae1a6c 100644 --- a/src/app/engine/assembly/foldhaus_assembly.cpp +++ b/src/app/engine/assembly/foldhaus_assembly.cpp @@ -72,7 +72,7 @@ AssemblyArray_Filter(assembly_array Array, assembly_array_filter_proc* Filter, g /////////////////////////// internal led_system -LedSystemInitialize(gs_allocator PlatformMemory, u32 BuffersMax) +LedSystem_Create(gs_allocator PlatformMemory, u32 BuffersMax) { led_system Result = {}; Result.PlatformMemory = PlatformMemory; @@ -136,7 +136,7 @@ LedBufferSetLed(led_buffer* Buffer, u32 Led, v4 Position) } internal u32 -Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* StripAt, strip_gen_data GenData, v4 RootPosition, u32 LedStartIndex) +Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* StripAt, strip_gen_data GenData, v4 RootPosition, u32 LedStartIndex, u32 LedLUTStartIndex) { u32 LedsAdded = 0; @@ -154,7 +154,7 @@ Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* Str s32 LedIndex = LedStartIndex + LedsAdded++; v4 LedPosition = WS_StripStart + (SingleStep * Step); LedBufferSetLed(LedBuffer, LedIndex, LedPosition); - StripAt->LedLUT[Step] = LedIndex; + StripAt->LedLUT[Step + LedLUTStartIndex] = LedIndex; } }break; @@ -164,7 +164,7 @@ Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* Str 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 += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, SegmentGenData, RootPosition, LedStartIndex + LedsAdded, LedsAdded); } }break; @@ -190,7 +190,7 @@ ConstructAssemblyFromDefinition (assembly* Assembly, led_system* LedSystem) StripAt->LedLUT = PushArray(&Assembly->Arena, u32, StripAt->LedCount); strip_gen_data GenData = StripAt->GenerationData; - LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, GenData, RootPosition, LedsAdded); + LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, GenData, RootPosition, LedsAdded, 0); } } @@ -208,7 +208,8 @@ LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena assembly* NewAssembly = AssemblyArray_Take(Assemblies); NewAssembly->Arena = CreateMemoryArena(Context.ThreadContext.Allocator); - if (ParseAssemblyFile(NewAssembly, FileName, AssemblyFileText, Scratch)) + parser AssemblyParser = ParseAssemblyFile(NewAssembly, FileName, AssemblyFileText, Scratch); + if (AssemblyParser.Success) { ConstructAssemblyFromDefinition(NewAssembly, LedSystem); } @@ -217,6 +218,14 @@ LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena FreeMemoryArena(&NewAssembly->Arena); Assemblies->Count -= 1; } + + for (parser_error* ErrorAt = AssemblyParser.ErrorsRoot; + ErrorAt != 0; + ErrorAt = ErrorAt->Next) + { + OutputDebugString(ErrorAt->Message.Str); + } + } else { @@ -256,18 +265,9 @@ AssemblyStripsGetWithTagValue(assembly Assembly, gs_const_string TagName, gs_con for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) { v2_strip StripAt = Assembly.Strips[StripIndex]; - for (u32 j = 0; j < StripAt.TagsCount; j++) + if (AssemblyStrip_HasTagValue(StripAt, NameHash, ValueHash)) { - v2_tag TagAt = StripAt.Tags[j]; - 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.StripIndices[Result.Count++] = StripIndex; - } - } + Result.StripIndices[Result.Count++] = StripIndex; } } diff --git a/src/app/engine/assembly/foldhaus_assembly.h b/src/app/engine/assembly/foldhaus_assembly.h index f1d7ba9..677d4ff 100644 --- a/src/app/engine/assembly/foldhaus_assembly.h +++ b/src/app/engine/assembly/foldhaus_assembly.h @@ -160,7 +160,6 @@ struct assembly_array assembly* Values; }; - internal led_buffer* LedSystemGetBuffer(led_system* System, u32 Index) { @@ -205,5 +204,34 @@ StripGenData_CountLeds(strip_gen_data Data) return Result; } +internal bool +AssemblyStrip_HasTagValue(v2_strip Strip, u64 NameHash, u64 ValueHash) +{ + bool Result = false; + for (u32 i = 0; i < Strip.TagsCount; i++) + { + 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; + } + } + } + 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); +} + #define FOLDHAUS_ASSEMBLY_H #endif // FOLDHAUS_ASSEMBLY_H \ No newline at end of file diff --git a/src/app/engine/assembly/foldhaus_assembly_debug.h b/src/app/engine/assembly/foldhaus_assembly_debug.h new file mode 100644 index 0000000..07a3685 --- /dev/null +++ b/src/app/engine/assembly/foldhaus_assembly_debug.h @@ -0,0 +1,213 @@ +// +// File: foldhaus_assembly_debug.h +// Author: Peter Slattery +// Creation Date: 2021-01-15 +// +#ifndef FOLDHAUS_ASSEMBLY_DEBUG_H + +enum override_type +{ + ADS_Override_None, + + ADS_Override_Strip, + ADS_Override_SoloStrip, + ADS_Override_AllRed, + ADS_Override_AllGreen, + ADS_Override_AllBlue, + ADS_Override_AllOff, + ADS_Override_AllWhite, + ADS_Override_TagWhite, + ADS_Override_TagStripWhite, + ADS_Override_ChannelWhite, + + ADS_Override_Count, +}; + +global gs_const_string OverrideTypeStrings[] = { + LitString("Override_None"), + LitString("Override_Strip"), + LitString("Override_SoloStrip" ), + LitString("Override_AllRed" ), + LitString("Override_AllGreen" ), + LitString("Override_AllBlue" ), + LitString("ADS_Override_AllOff" ), + LitString("ADS_Override_AllWhite" ), + LitString("ADS_Override_TagWhite" ), + LitString("ADS_Override_TagStripWhite" ), + LitString("ADS_Override_ChannelWhite," ), + LitString("Override_Count"), +}; + +struct assembly_debug_state +{ + override_type Override; + + u32 TargetAssembly; + u32 TargetStrip; + + gs_string TagName; + gs_string TagValue; + + pixel TargetColor; + + u32 TargetChannel; + + u8 Brightness; +}; + +internal assembly_debug_state +AssemblyDebug_Create(gs_memory_arena* Storage) +{ + assembly_debug_state Result = {}; + Result.TagName = PushString(Storage, 256); + Result.TagValue = PushString(Storage, 256); + return Result; +} + +internal void +AssemblyDebug_OverrideStripWithColor(v2_strip Strip, led_buffer LedBuffer, pixel Color) +{ + for (u32 i = 0; i < Strip.LedCount; i++) + { + u32 LedIdx = Strip.LedLUT[i]; + LedBuffer.Colors[LedIdx] = Color; + } +} + +internal void +AssemblyDebug_OverrideWithColor(assembly Assembly, led_buffer LedBuffer, pixel Color) +{ + for (u32 s = 0; s < Assembly.StripCount; s++) + { + v2_strip Strip = Assembly.Strips[s]; + AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, Color); + } +} + +internal void +AssemblyDebug_OverrideTagValueWithColor(assembly Assembly, led_buffer LedBuffer, pixel Color, gs_string TagName, gs_string TagValue) +{ + u64 NameHash = HashDJB2ToU32(StringExpand(TagName)); + u64 ValueHash = HashDJB2ToU32(StringExpand(TagValue)); + + for (u32 s = 0; s < Assembly.StripCount; s++) + { + v2_strip Strip = Assembly.Strips[s]; + if (AssemblyStrip_HasTagValue(Strip, NameHash, ValueHash)) + { + AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, Color); + } + } +} + +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}; + + assembly Assembly = Assemblies.Values[State.TargetAssembly]; + led_buffer LedBuffer = LedSystem.Buffers[Assembly.LedBufferIndex]; + + u8 V = State.Brightness; + + switch (State.Override) + { + case ADS_Override_Strip: + { + v2_strip Strip = Assembly.Strips[State.TargetStrip]; + AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, State.TargetColor); + }break; + + case ADS_Override_SoloStrip: + { + for (u32 s = 0; s < Assembly.StripCount; s++) + { + v2_strip Strip = Assembly.Strips[s]; + + pixel Color = pixel{0,0,0}; + if (s == State.TargetStrip) + { + Color = State.TargetColor; + } + + AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, Color); + } + }break; + + case ADS_Override_AllRed: + { + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{V, 0, 0}); + }break; + + case ADS_Override_AllGreen: + { + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, V, 0}); + }break; + + case ADS_Override_AllBlue: + { + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, V}); + }break; + + case ADS_Override_AllOff: + { + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0}); + }break; + + case ADS_Override_AllWhite: + { + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{V, V, V}); + }break; + + case ADS_Override_TagWhite: + { + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0}); + AssemblyDebug_OverrideTagValueWithColor(Assembly, LedBuffer, pixel{255, 255, 255}, State.TagName, State.TagValue); + + }break; + + case ADS_Override_TagStripWhite: + { + u64 NameHash = HashDJB2ToU32(StringExpand(State.TagName)); + u64 ValueHash = HashDJB2ToU32(StringExpand(State.TagValue)); + + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0}); + + v2_strip Strip = Assembly.Strips[State.TargetStrip]; + if (AssemblyStrip_HasTagValue(Strip, NameHash, ValueHash)) + { + AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, + pixel{255, 255, 255}); + } + }break; + + case ADS_Override_ChannelWhite: + { + AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0}); + for (u32 s = 0; s < Assembly.StripCount; s++) + { + v2_strip Strip = Assembly.Strips[s]; + if (Strip.UARTAddr.Channel == State.TargetChannel) + { + AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, pixel{255, 255, 255}); + } + } + }break; + + case ADS_Override_None: + { + }break; + + InvalidDefaultCase; + } + + if (State.Override ) + { + + } +} + + +#define FOLDHAUS_ASSEMBLY_DEBUG_H +#endif // FOLDHAUS_ASSEMBLY_DEBUG_H \ No newline at end of file diff --git a/src/app/engine/assembly/foldhaus_assembly_parser.cpp b/src/app/engine/assembly/foldhaus_assembly_parser.cpp index ff2cbc0..5024fb6 100644 --- a/src/app/engine/assembly/foldhaus_assembly_parser.cpp +++ b/src/app/engine/assembly/foldhaus_assembly_parser.cpp @@ -225,7 +225,7 @@ AssemblyParser_ReadSequence(parser* Parser, assembly* Assembly) internal strip_gen_data AssemblyParser_ReadStripGenData(parser* Parser, assembly* Assembly) { - strip_gen_data Result = {0}; + strip_gen_data Result = {}; if (Parser_ReadOpenStruct(Parser, AssemblyField_Segment)) { @@ -258,12 +258,13 @@ AssemblyParser_ReadStripGenData(parser* Parser, assembly* Assembly) return Result; } -internal bool +internal parser ParseAssemblyFile(assembly* Assembly, gs_const_string FileName, gs_string FileText, gs_memory_arena* Transient) { Assembly->LedCountTotal = 0; parser Parser = {0}; + Parser.FileName = FileName; Parser.String = FileText; Parser.Identifiers = &AssemblyFieldIdentifiers[0]; Parser.IdentifiersCount = AssemblyField_Count; @@ -271,6 +272,7 @@ ParseAssemblyFile(assembly* Assembly, gs_const_string FileName, gs_string FileTe Parser.LineStart = Parser.At; Parser.Arena = &Assembly->Arena; Parser.Transient = Transient; + Parser.Success = true; Assembly->Name = Parser_ReadStringValue(&Parser, AssemblyField_AssemblyName); Assembly->Scale = Parser_ReadR32Value(&Parser, AssemblyField_AssemblyScale); @@ -298,6 +300,7 @@ ParseAssemblyFile(assembly* Assembly, gs_const_string FileName, gs_string FileTe else { Parser_PushErrorF(&Parser, "Invalid output mode specified for assembly."); + Parser.Success = false; } for (u32 i = 0; i < Assembly->StripCount; i++) @@ -316,16 +319,17 @@ ParseAssemblyFile(assembly* Assembly, gs_const_string FileName, gs_string FileTe if (!Parser_ReadCloseStruct(&Parser)) { Parser_PushErrorF(&Parser, "Strip struct doesn't close where expected"); + Parser.Success = false; } } else { Parser_PushErrorF(&Parser, "Expected a strip struct but none was found"); + Parser.Success = false; } } - // TODO(pjs): invalidate the file if its incorrect - return true; //Tokenizer.ParsingIsValid; + return Parser; } #define FOLDHAUS_ASSEMBLY_PARSER_CPP diff --git a/src/app/engine/foldhaus_addressed_data.h b/src/app/engine/foldhaus_addressed_data.h index 47b70d7..c27bd59 100644 --- a/src/app/engine/foldhaus_addressed_data.h +++ b/src/app/engine/foldhaus_addressed_data.h @@ -95,5 +95,14 @@ AddressedDataBuffer_SetCOMPort(addressed_data_buffer* Buffer, gs_const_string Co 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); + return Result; +} + #define FOLDHAUS_ADDRESSED_DATA_H #endif // FOLDHAUS_ADDRESSED_DATA_H \ No newline at end of file diff --git a/src/app/engine/foldhaus_serializer.h b/src/app/engine/foldhaus_serializer.h index 06f9d71..34ac831 100644 --- a/src/app/engine/foldhaus_serializer.h +++ b/src/app/engine/foldhaus_serializer.h @@ -135,7 +135,7 @@ struct parser_error { gs_string Message; - gs_string FileName; + gs_const_string FileName; u32 LineNumber; parser_error* Next; @@ -143,7 +143,7 @@ struct parser_error struct parser { - gs_string FileName; + gs_const_string FileName; gs_string String; @@ -160,6 +160,8 @@ struct parser parser_error* ErrorsRoot; parser_error* ErrorsHead; + + bool Success; }; internal void @@ -170,13 +172,17 @@ Parser_PushErrorF(parser* Parser, char* Format, ...) Error->LineNumber = Parser->Line; Error->Message = PushString(Parser->Transient, 1024); - PrintF(&Error->Message, "File: %S Line: %d - ", Error->FileName, Error->LineNumber); + PrintF(&Error->Message, "Error:\n"); va_list Args; va_start(Args, Format); PrintFArgsList(&Error->Message, Format, Args); va_end(Args); + AppendPrintF(&Error->Message, "\n\tFile: %S\n\tLine: %d\n", + Error->FileName, Error->LineNumber); + NullTerminate(&Error->Message); + SLLPushOrInit(Parser->ErrorsRoot, Parser->ErrorsHead, Error); } @@ -202,8 +208,27 @@ Parser_AdvanceChar(parser* P) { P->Line += 1; P->LineStart = P->At + 1; + + if ((P->At[0] == '\n' && P->At[1] == '\r') || + (P->At[0] == '\r' && P->At[1] == '\n')) + { + P->At++; + P->At++; + } + else if (P->At[0] == '\n') + { + P->At++; + } + else + { + // TODO(pjs): Not sure this is actually invalid + InvalidCodePath; + } + } + else + { + P->At++; } - P->At++; } internal void diff --git a/src/app/engine/uart/foldhaus_uart.cpp b/src/app/engine/uart/foldhaus_uart.cpp index d523f6d..f18360f 100644 --- a/src/app/engine/uart/foldhaus_uart.cpp +++ b/src/app/engine/uart/foldhaus_uart.cpp @@ -27,10 +27,15 @@ UART_SetChannelBuffer_Create(gs_memory_cursor* WriteCursor, uart_channel Channel u8* OutputPixel = PushArrayOnCursor(WriteCursor, u8, 3); // TODO(pjs): Use the Output mask +#if 1 OutputPixel[0] = Color.R; OutputPixel[1] = Color.G; OutputPixel[2] = Color.B; - +#else + OutputPixel[0] = 255; + OutputPixel[1] = 255; + OutputPixel[2] = 255; +#endif if (Channel->ElementsCount == 4) { // TODO(pjs): Calculate white from the RGB components? @@ -56,7 +61,7 @@ UART_DrawAll_Create(gs_memory_cursor* WriteCursor) } internal void -UART_BuildOutputData(addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem) +UART_BuildOutputData(addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem, gs_memory_arena* Transient) { uart_channel ChannelSettings = {0}; ChannelSettings.ElementsCount = 3; @@ -64,29 +69,102 @@ UART_BuildOutputData(addressed_data_buffer_list* Output, assembly_array Assembli // 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 = sizeof(uart_header) + sizeof(uart_channel) + sizeof(uart_footer); + 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); - u32 TotalBufferSize = MessageBaseSize * Assembly.StripCount; // SetChannelBuffer messages - TotalBufferSize += MessageBaseSize; // DrawAll message - TotalBufferSize += ChannelSettings.ElementsCount * Assembly.LedCountTotal; // pixels * channels per pixel + struct strips_to_data_buffer + { + gs_const_string ComPort; + + u32* StripIndices; + u32 StripIndicesCount; + u32 StripIndicesCountMax; + + u64 LedCount; + + u8** ChannelsStart; + + strips_to_data_buffer* Next; + }; - addressed_data_buffer* Buffer = AddressedDataBufferList_Push(Output, TotalBufferSize); - AddressedDataBuffer_SetCOMPort(Buffer, Assembly.UARTComPort.ConstString); - gs_memory_cursor WriteCursor = CreateMemoryCursor(Buffer->Data); + 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]; - ChannelSettings.PixelsCount = StripAt.LedCount; - UART_SetChannelBuffer_Create(&WriteCursor, ChannelSettings, StripAt, *LedBuffer); + + // 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; } - UART_DrawAll_Create(&WriteCursor); + 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); + } } } diff --git a/src/app/engine/uart/foldhaus_uart.h b/src/app/engine/uart/foldhaus_uart.h index f866efc..63067ba 100644 --- a/src/app/engine/uart/foldhaus_uart.h +++ b/src/app/engine/uart/foldhaus_uart.h @@ -35,6 +35,8 @@ struct uart_footer #pragma pack(pop) +#define UART_MESSAGE_MIN_SIZE sizeof(uart_header) + sizeof(uart_channel) + sizeof(uart_footer) + global u32 UART_CRCTable[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d diff --git a/src/app/engine/user_space.cpp b/src/app/engine/user_space.cpp new file mode 100644 index 0000000..752435a --- /dev/null +++ b/src/app/engine/user_space.cpp @@ -0,0 +1,46 @@ +// +// File: userspace.cpp +// Author: Peter Slattery +// Creation Date: 2021-01-30 +// +#ifndef USERSPACE_CPP + +internal void +US_LoadPatterns(user_space_desc* Desc, app_state* State, context Context) +{ + if (Desc->LoadPatterns) + { + Desc->LoadPatterns(State); + } +} + +internal void +US_CustomInit(user_space_desc* Desc, app_state* State, context Context) +{ + if (Desc->CustomInit) + { + Desc->UserData = Desc->CustomInit(State, Context); + } +} + +internal void +US_CustomUpdate(user_space_desc* Desc, app_state* State, context* Context) +{ + if (Desc->CustomUpdate) + { + Desc->CustomUpdate(Desc->UserData, State, Context); + } +} + +internal void +US_CustomCleanup(user_space_desc* Desc, app_state* State, context Context) +{ + if (Desc->CustomCleanup) + { + Desc->CustomCleanup(Desc->UserData, State, Context); + } +} + + +#define USERSPACE_CPP +#endif // USERSPACE_CPP \ No newline at end of file diff --git a/src/app/engine/user_space.h b/src/app/engine/user_space.h new file mode 100644 index 0000000..8dc5f8a --- /dev/null +++ b/src/app/engine/user_space.h @@ -0,0 +1,31 @@ +// +// File: userspace.h +// Author: Peter Slattery +// Creation Date: 2021-01-30 +// +#ifndef USERSPACE_H + +#define US_LOAD_PATTERNS(name) void name(app_state* State) +typedef US_LOAD_PATTERNS(us_load_patterns_proc); + +#define US_CUSTOM_INIT(name) gs_data name (app_state* State, context Context) +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_CLEANUP(name) void name(gs_data UserData, app_state* State, context Context) +typedef US_CUSTOM_CLEANUP(us_custom_cleanup_proc); + +typedef struct user_space_desc +{ + us_load_patterns_proc* LoadPatterns; + us_custom_init_proc* CustomInit; + us_custom_update_proc* CustomUpdate; + us_custom_cleanup_proc* CustomCleanup; + + gs_data UserData; +} user_space_desc; + +#define USERSPACE_H +#endif // USERSPACE_H \ No newline at end of file diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 68bd4e2..47590c0 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -8,8 +8,6 @@ #include "foldhaus_platform.h" #include "foldhaus_app.h" -//////////////////////////////////////////////////////////////////////// - RELOAD_STATIC_DATA(ReloadStaticData) { app_state* State = (app_state*)Context.MemoryBase; @@ -17,6 +15,8 @@ RELOAD_STATIC_DATA(ReloadStaticData) GlobalDebugServices = DebugServices; State->PanelSystem.PanelDefs = GlobalPanelDefs; State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount; + + US_LoadPatterns(&State->UserSpaceDesc, State, Context); } INITIALIZE_APPLICATION(InitializeApplication) @@ -26,133 +26,69 @@ INITIALIZE_APPLICATION(InitializeApplication) State->Permanent = CreateMemoryArena(Context.ThreadContext.Allocator); State->Transient = Context.ThreadContext.Transient; - State->Assemblies = AssemblyArray_Create(8, &State->Permanent); - State->GlobalLog = PushStruct(State->Transient, event_log); - *State->GlobalLog = {0}; + State->GlobalLog = PushStruct(&State->Permanent, event_log); State->CommandQueue = CommandQueue_Create(&State->Permanent, 32); - // TODO(Peter): put in InitializeInterface? - r32 FontSize = 14; - { - gs_file FontFile = ReadEntireFile(Context.ThreadContext.FileHandler, ConstString("data/Anonymous Pro.ttf")); - if (FileNoError(FontFile)) - { - bitmap_font* Font = PushStruct(&State->Permanent, bitmap_font); - - Font->BitmapWidth = 512; - Font->BitmapHeight = 512; - Font->BitmapBytesPerPixel = 4; - Font->BitmapMemory = PushArray(&State->Permanent, u8, Font->BitmapWidth * Font->BitmapHeight * Font->BitmapBytesPerPixel); - Font->BitmapStride = Font->BitmapWidth * Font->BitmapBytesPerPixel; - ZeroMemoryBlock(Font->BitmapMemory, Font->BitmapStride * Font->BitmapHeight); - - platform_font_info FontInfo = Context.PlatformGetFontInfo("Anonymous Pro", FontSize, FontWeight_Normal, false, false, false); - Font->PixelHeight = FontInfo.PixelHeight; - Font->Ascent = FontInfo.Ascent; - Font->Descent = FontInfo.Descent; - Font->Leading = FontInfo.Leading; - Font->MaxCharWidth = FontInfo.MaxCharWidth; - - Font->CodepointDictionarySize = (FontInfo.CodepointOnePastLast - FontInfo.CodepointStart); - Font->CodepointDictionaryCount = 0; - Font->CodepointKeys = PushArray(&State->Permanent, char, Font->CodepointDictionarySize); - Font->CodepointValues = PushArray(&State->Permanent, codepoint_bitmap, Font->CodepointDictionarySize); - - for (s32 Codepoint = FontInfo.CodepointStart; - Codepoint < FontInfo.CodepointOnePastLast; - Codepoint++) - { - - u32 CodepointX, CodepointY; - GetNextCodepointOffset(Font, &CodepointX, &CodepointY); - - u32 CodepointW, CodepointH; - Context.PlatformDrawFontCodepoint( - Font->BitmapMemory, - Font->BitmapWidth, - Font->BitmapHeight, - CodepointX, CodepointY, - Codepoint, FontInfo, - &CodepointW, &CodepointH); - - AddCodepointToFont(Font, Codepoint, 0, 0, CodepointW, CodepointH, CodepointX, CodepointY); - } - - State->Interface.Style.Font = Font; - - Font->BitmapTextureHandle = Context.PlatformGetGPUTextureHandle(Font->BitmapMemory, - Font->BitmapWidth, Font->BitmapHeight); - } - else - { - LogError(State->GlobalLog, "Unable to load font"); - } - } + animation_system_desc AnimSysDesc = {}; + AnimSysDesc.Storage = &State->Permanent; + AnimSysDesc.AnimArrayCount = 32; + AnimSysDesc.SecondsPerFrame = 1.0f / 24.0f; + State->AnimationSystem = AnimationSystem_Init(AnimSysDesc); - State->Interface.Style.FontSize = FontSize; - State->Interface.Style.PanelBGColors[0] = v4{.3f, .3f, .3f, 1}; - State->Interface.Style.PanelBGColors[1] = v4{.4f, .4f, .4f, 1}; - State->Interface.Style.PanelBGColors[2] = v4{.5f, .5f, .5f, 1}; - State->Interface.Style.PanelBGColors[3] = v4{.6f, .6f, .6f, 1}; - State->Interface.Style.ButtonColor_Inactive = BlackV4; - State->Interface.Style.ButtonColor_Active = v4{.1f, .1f, .1f, 1}; - State->Interface.Style.ButtonColor_Selected = v4{.3f, .3f, .3f, 1}; - State->Interface.Style.TextColor = WhiteV4; - State->Interface.Style.ListBGColors[0] = v4{ .16f, .16f, .16f, 1.f }; - State->Interface.Style.ListBGColors[1] = v4{ .18f, .18f, .18f, 1.f }; - State->Interface.Style.ListBGHover = v4{ .22f, .22f, .22f, 1.f }; - State->Interface.Style.ListBGSelected = v4{.44f, .44f, .44f, 1.f }; - State->Interface.Style.Margin = v2{5, 5}; - State->Interface.Style.RowHeight = ui_GetTextLineHeight(State->Interface); - - State->Interface.WidgetsCountMax = 4096; - State->Interface.Widgets = PushArray(&State->Permanent, ui_widget, State->Interface.WidgetsCountMax); + 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->SACN = SACN_Initialize(Context); - State->LedSystem = LedSystemInitialize(Context.ThreadContext.Allocator, 128); - -#if 1 - gs_const_string SculpturePath = ConstString("data/blumen_lumen_silver_spring.fold"); - LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); -#endif - - State->PixelsToWorldScale = .01f; + State->LedSystem = LedSystem_Create(Context.ThreadContext.Allocator, 128); + State->AssemblyDebugState = AssemblyDebug_Create(&State->Permanent); + State->AssemblyDebugState.Brightness = 255; + State->AssemblyDebugState.Override = ADS_Override_AllRed; GlobalDebugServices->Interface.RenderSculpture = true; - ReloadStaticData(Context, GlobalDebugServices); + PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); + { + 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_ProfilerView, State, Context); + + panel* Hierarchy = LeftPanel->Left; + Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_HierarchyView, State, Context); + + } State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext); - { // Animation PLAYGROUND - State->AnimationSystem = {}; - State->AnimationSystem.Storage = &State->Permanent; - State->AnimationSystem.Animations = AnimationArray_Create(State->AnimationSystem.Storage, 32); - - State->AnimationSystem.SecondsPerFrame = 1.f / 24.f; - - animation Anim = {0}; - Anim.Name = PushStringF(&State->Permanent, 256, "test_anim_one"); - Anim.Layers = AnimLayerArray_Create(State->AnimationSystem.Storage, 8); - Anim.Blocks_ = AnimBlockArray_Create(State->AnimationSystem.Storage, 8); - Anim.PlayableRange.Min = 0; - Anim.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); - Animation_AddLayer(&Anim, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddLayer(&Anim, MakeString("Color Layer"), BlendMode_Multiply, &State->AnimationSystem); - Animation_AddLayer(&Anim, MakeString("Sparkles"), BlendMode_Add, &State->AnimationSystem); - - Animation_AddBlock(&Anim, 22, 123, 2, 0); - - AnimationArray_Push(&State->AnimationSystem.Animations, Anim); - } // End Animation Playground + State->UserSpaceDesc = BlumenLumen_UserSpaceCreate(); - - PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); - PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context); + ReloadStaticData(Context, GlobalDebugServices); + US_CustomInit(&State->UserSpaceDesc, State, Context); } UPDATE_AND_RENDER(UpdateAndRender) @@ -171,42 +107,34 @@ UPDATE_AND_RENDER(UpdateAndRender) AnimationSystem_Update(&State->AnimationSystem); if (AnimationSystem_NeedsRender(State->AnimationSystem)) { - State->AnimationSystem.LastUpdatedFrame = State->AnimationSystem.CurrentFrame; AnimationSystem_RenderToLedBuffers(&State->AnimationSystem, State->Assemblies, &State->LedSystem, - GlobalAnimationPatterns, - State->Transient); + State->Patterns, + State->Transient, + State->UserSpaceDesc.UserData.Memory); } - { - // 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); - } + US_CustomUpdate(&State->UserSpaceDesc, State, Context); + + AssemblyDebug_OverrideOutput(State->AssemblyDebugState, + State->Assemblies, + State->LedSystem); Editor_Render(State, Context, RenderBuffer); - // Checking for overflows -#if 0 - { - DEBUG_TRACK_SCOPE(OverflowChecks); - AssertAllocationsNoOverflow(State->Permanent); - for (u32 i = 0; i < State->Assemblies.Count; i++) - { - assembly* Assembly = &State->Assemblies.Values[i]; - AssertAllocationsNoOverflow(Assembly->Arena); - } - } -#endif + // 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); } CLEANUP_APPLICATION(CleanupApplication) { app_state* State = (app_state*)Context.MemoryBase; + US_CustomCleanup(&State->UserSpaceDesc, State, Context); SACN_Cleanup(&State->SACN, Context); } diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index d8b7bbe..43ded68 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -19,6 +19,7 @@ #include "engine/assembly/foldhaus_assembly.h" #include "engine/assembly/foldhaus_assembly_parser.cpp" +#include "engine/assembly/foldhaus_assembly_debug.h" #include "engine/sacn/foldhaus_sacn.h" #include "engine/uart/foldhaus_uart.h" @@ -40,6 +41,9 @@ typedef struct panel panel; #include "engine/animation/foldhaus_animation_serializer.cpp" #include "engine/animation/foldhaus_animation_renderer.cpp" +#include "engine/user_space.h" +#include "blumen_lumen.h" + struct app_state { gs_memory_arena Permanent; @@ -51,8 +55,10 @@ struct app_state streaming_acn SACN; led_system LedSystem; assembly_array Assemblies; + assembly_debug_state AssemblyDebugState; animation_system AnimationSystem; event_log* GlobalLog; + animation_pattern_array Patterns; // Interface // @@ -65,155 +71,17 @@ struct app_state panel_system PanelSystem; panel* HotPanel; - r32 PixelsToWorldScale; + user_space_desc UserSpaceDesc; }; internal void OpenColorPicker(app_state* State, v4* Address); #include "engine/assembly/foldhaus_assembly.cpp" -// BEGIN TEMPORARY PATTERNS -internal void -TestPatternOne(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient) -{ - 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 }; - } - } -#if 0 - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) - { - v4 LedPosition = Leds->Positions[LedIndex]; - float PercentX = RemapClampedR32(LedPosition.x, -150.0f, 150.0f, 0.0f, 1.0f); - float PercentY = RemapClampedR32(LedPosition.y, -150.0f, 150.0f, 0.0f, 1.0f); - Leds->Colors[LedIndex].R = (u8)(PercentX * 255); - Leds->Colors[LedIndex].G = (u8)(PercentY * 255); - } -#endif - -} +#include "engine/user_space.cpp" -internal void -TestPatternTwo(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient) -{ - 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 = 0; LedIndex < Leds->LedCount; 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, assembly Assembly, r32 Time, gs_memory_arena* Transient) -{ - 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 = 0; LedIndex < Leds->LedCount; 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; - } -} - -internal void -Pattern_AllGreen(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient) -{ - for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) - { - Leds->Colors[LedIndex].R = 0; - Leds->Colors[LedIndex].B = 255; - Leds->Colors[LedIndex].G = 255; - } -} - -// END TEMPORARY PATTERNS +#include "patterns/blumen_patterns.h" +#include "blumen_lumen.cpp" internal void EndCurrentOperationMode(app_state* State) @@ -221,14 +89,6 @@ EndCurrentOperationMode(app_state* State) DeactivateCurrentOperationMode(&State->Modes); } -s32 GlobalAnimationPatternsCount = 4; -animation_pattern GlobalAnimationPatterns[] = { - { "Test Pattern One", 16, TestPatternOne }, - { "Test Pattern Two", 16, TestPatternTwo }, - { "Test Pattern Three", 18, TestPatternThree }, - { "Pattern_AllGreen", 16, Pattern_AllGreen }, -}; - #include "editor/panels/foldhaus_panel_types.h" #include "editor/panels/foldhaus_panel_file_view.h" @@ -237,15 +97,12 @@ animation_pattern GlobalAnimationPatterns[] = { #include "editor/panels/foldhaus_panel_dmx_view.h" #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_types.cpp" -//#include "generated/foldhaus_panels_generated.h" #include "editor/foldhaus_interface.cpp" - -#include "../meta/gs_meta_include.cpp" - +#include "editor/foldhaus_editor_draw.h" #include "editor/foldhaus_editor.cpp" #define FOLDHAUS_APP_H diff --git a/src/app/foldhaus_debug.h b/src/app/foldhaus_debug.h index ca26188..7acdfcc 100644 --- a/src/app/foldhaus_debug.h +++ b/src/app/foldhaus_debug.h @@ -54,6 +54,7 @@ struct debug_frame s64 FrameEndCycles; s32 ScopeNamesMax; + s32 ScopeNamesCount; scope_name* ScopeNamesHash; s32 ThreadCount; @@ -302,6 +303,7 @@ EndDebugFrame (debug_services* Services) CollateThreadScopeCalls(ClosingFrame->ThreadCalls + t, ClosingFrame); } + s32 ScopeNamesCount = 0; for (s32 n = 0; n < ClosingFrame->ScopeNamesMax; n++) { if (ClosingFrame->ScopeNamesHash[n].Hash != 0) @@ -310,8 +312,10 @@ EndDebugFrame (debug_services* Services) 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; Services->CurrentDebugFrame = (Services->CurrentDebugFrame + 1) % DEBUG_FRAME_COUNT; StartDebugFrame(&Services->Frames[Services->CurrentDebugFrame], Services); diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index d40a71a..7f10ab0 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -5,10 +5,10 @@ // #ifndef FOLDHAUS_PLATFORM_H +// TODO Remove +#include #include -#include // TODO Remove - #include "..\gs_libs\gs_types.h" #include "..\gs_libs\gs_types.cpp" @@ -50,6 +50,22 @@ typedef struct context context; // Application Functions +// TODO(pjs): TEMP +typedef void temp_job_req_proc(gs_thread_context* Ctx, u8* Memory); +struct temp_job_req +{ + 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; +}; + #define INITIALIZE_APPLICATION(name) void name(context Context) typedef INITIALIZE_APPLICATION(initialize_application); @@ -148,7 +164,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) { @@ -190,6 +206,9 @@ struct context update_and_render* UpdateAndRender; cleanup_application* CleanupApplication; + platform_thread_manager* ThreadManager; + platform_socket_manager* SocketManager; + // Platform Services gs_work_queue* GeneralWorkQueue; @@ -200,6 +219,5 @@ struct context platform_get_socket_handle* PlatformGetSocketHandle; }; - #define FOLDHAUS_PLATFORM_H #endif // FOLDHAUS_PLATFORM_H \ No newline at end of file diff --git a/src/app/foldhaus_renderer.cpp b/src/app/foldhaus_renderer.cpp index ece4ee7..bfd6522 100644 --- a/src/app/foldhaus_renderer.cpp +++ b/src/app/foldhaus_renderer.cpp @@ -15,6 +15,14 @@ AllocateRenderCommandBuffer (u8* Memory, s32 Size, renderer_realloc* Realloc) Result.Realloc = Realloc; return Result; } +internal render_command_buffer +AllocateRenderCommandBuffer(u32 MemorySize, + gs_memory_arena* Arena, + renderer_realloc* Realloc) +{ + u8* Memory = PushSize(Arena, MemorySize); + return AllocateRenderCommandBuffer(Memory, MemorySize, Realloc); +} internal void Render3DQuadBatch (u8* CommandData, s32 TriCount) diff --git a/src/app/foldhaus_renderer.h b/src/app/foldhaus_renderer.h index e316c71..a40c0f4 100644 --- a/src/app/foldhaus_renderer.h +++ b/src/app/foldhaus_renderer.h @@ -589,6 +589,21 @@ PushRenderQuad2D (render_command_buffer* Buffer, v2 Min, v2 Max, v4 Color) 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); +} + +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); +} + internal void PushRenderQuad2D(render_command_buffer* Buffer, v2 P0, v2 P1, v2 P2, v2 P3, v4 Color) { diff --git a/src/app/interface.h b/src/app/interface.h index d390069..f0942a7 100644 --- a/src/app/interface.h +++ b/src/app/interface.h @@ -5,13 +5,7 @@ // #ifndef INTERFACE_H -// Widget Capabilities -// - string -// - background -// - outline -// - active (mouse is interacting) -// - hot (mouse could be about to interact) -// - retained state - if a toggle is active, or a drop down is open +#define InterfaceAssert(IMemPtr) Assert(IMemPtr && (u64)IMemPtr != 0x5 && (u64)IMemPtr != 0xC) enum gs_string_alignment { @@ -21,22 +15,60 @@ enum gs_string_alignment }; internal void -DrawCharacter_ (render_quad_batch_constructor* BatchConstructor, r32 MinX, r32 MinY, codepoint_bitmap CodepointInfo, v4 Color) +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; +} + +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, - v2{(r32)AlignedMinX, (r32)AlignedMinY}, v2{(r32)AlignedMaxX, (r32)AlignedMinY}, - v2{(r32)AlignedMaxX, (r32)AlignedMaxY}, v2{(r32)AlignedMinX, (r32)AlignedMaxY}, - CodepointInfo.UVMin, CodepointInfo.UVMax, + 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, v4 Color) +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]; @@ -44,7 +76,7 @@ DrawCharacterLeftAligned (render_quad_batch_constructor* BatchConstructor, char // NOTE(Peter): r32 MinX = Position.x + CodepointInfo.XOffset; r32 MinY = Position.y + CodepointInfo.YOffset; - DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, Color); + DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color); // NOTE(Peter): v2 PointAfterCharacter = v2{Position.x + CodepointInfo.Width, Position.y}; @@ -52,7 +84,7 @@ DrawCharacterLeftAligned (render_quad_batch_constructor* BatchConstructor, char } internal v2 -DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, v4 Color) +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]; @@ -60,7 +92,7 @@ DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char // NOTE(Peter): r32 MinX = Position.x - (CodepointInfo.XOffset + CodepointInfo.Width); r32 MinY = Position.y + CodepointInfo.YOffset + CodepointInfo.YOffset; - DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, Color); + DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color); // NOTE(Peter): v2 PointAfterCharacter = v2{Position.x-(CodepointInfo.Width + CodepointInfo.XOffset), Position.y}; @@ -68,13 +100,13 @@ DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char } internal v2 -DrawStringLeftAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, v4 Color) +DrawStringLeftAligned (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; for (s32 i = 0; i < Length; i++) { - v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, Color); + v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color); RegisterPosition.x = PositionAfterCharacter.x; C++; } @@ -82,13 +114,13 @@ DrawStringLeftAligned (render_quad_batch_constructor* BatchConstructor, s32 Leng } internal v2 -DrawStringRightAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, v4 Color) +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, Color); + v2 PositionAfterCharacter = DrawCharacterRightAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color); RegisterPosition.x = PositionAfterCharacter.x; C--; } @@ -109,14 +141,22 @@ DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* F 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(&BatchConstructor, StringExpand(String), RegisterPosition, Font, Color); + RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color); } else if (Alignment == Align_Right) { - RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, Color); + RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color); } else { @@ -136,6 +176,7 @@ DrawCursor (render_quad_batch_constructor* BatchConstructor, v2 Position, v4 Col 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) { @@ -187,21 +228,42 @@ DrawStringWithCursor (render_command_buffer* RenderBuffer, gs_string String, s32 LowerRight.x = RegisterPosition.x; return LowerRight; } - -// TODO(pjs): remove the need for htis (go thru and remove code that's in the #else block of #ifdef EXTERNAL_RENDERER s -#define EXTERNAL_RENDERER +#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, }; struct ui_widget_id { - u64 Index; - u64 LayoutId; + u64 Id; + u64 ZIndex; +}; + +enum ui_layout_direction +{ + LayoutDirection_TopDown, + LayoutDirection_BottomUp, + LayoutDirection_Inherit, +}; + +struct ui_column +{ + r32 XMin; + r32 XMax; }; struct ui_widget @@ -213,17 +275,47 @@ struct ui_widget rect2 Bounds; u64 Flags; - bool RetainedState; + + 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; }; struct interface_config { - v4 PanelBGColors[4]; + v4 PanelBG; // TODO(pjs): Turn these into _Default, _Hot, _Active v4 ButtonColor_Inactive, ButtonColor_Active, ButtonColor_Selected; @@ -241,75 +333,158 @@ struct interface_config r32 RowHeight; }; -enum ui_layout_direction -{ - LayoutDirection_TopDown, - LayoutDirection_BottomUp, -}; - -struct ui_layout -{ - u64 Id; - - rect2 Bounds; - v2 Margin; - r32 RowHeight; - r32 RowYAt; - - ui_layout_direction FillDirection; - - b32 DrawHorizontal; - u32 ColumnsMax; - r32* ColumnWidths; - u32 ColumnsCount; - - // 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; -}; - 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; }; 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; + render_command_buffer* RenderBuffer; ui_widget* Widgets; u64 WidgetsCount; u64 WidgetsCountMax; - ui_widget_id HotWidget; - ui_widget_id ActiveWidget; + ui_widget* DrawOrderHead; + ui_widget* DrawOrderRoot; -#define LAYOUT_COUNT_MAX 8 - ui_layout LayoutStack[LAYOUT_COUNT_MAX]; - u64 LayoutStackCount; - u64 LayoutIdAcc; + 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; }; internal void ui_InterfaceReset(ui_interface* Interface) { Interface->WidgetsCount = 0; - Interface->LayoutStackCount = 0; - Interface->LayoutIdAcc = 0; + Interface->DrawOrderHead = 0; + Interface->DrawOrderRoot = 0; + ClearArena(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] = {0}; + } + } + + Interface->LastActiveWidget = Interface->ActiveWidget; } internal bool ui_WidgetIdsEqual(ui_widget_id A, ui_widget_id B) { - bool Result = (A.Index == B.Index) && (A.LayoutId == B.LayoutId); + 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; +} + +internal void +ui_WidgetClearFlag(ui_widget* Widget, u64 Flag) +{ + 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; +} + +internal void +ui_WidgetSetChildrenPopover(ui_widget* Widget) +{ + 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)) + { + Result = Parent; + } + 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; +} + +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) + { + Result = ui_WidgetGetWidgetWithId(At, Id); + if (Result != 0) + { + break; + } + } + return Result; } @@ -321,6 +496,7 @@ ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id) { if (ui_WidgetIdsEqual(Interface->RetainedState[i].Id, Id)) { + Interface->RetainedState[i].FramesSinceAccess = 0; Result = Interface->RetainedState + i; break; } @@ -329,11 +505,63 @@ ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id) } internal ui_widget_retained_state* -ui_CreateRetainedState(ui_interface* Interface, ui_widget_id Id) +ui_CreateRetainedState(ui_interface* Interface, ui_widget* Widget) { u64 Index = Interface->RetainedStateCount++; ui_widget_retained_state* Result = Interface->RetainedState + Index; - Result->Id = Id; + Result->Id = Widget->Id; + 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; +} + +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; } @@ -351,200 +579,462 @@ ui_MouseClickedRect(ui_interface Interface, rect2 Rect) // Layout -static ui_layout -ui_CreateLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDirection = LayoutDirection_TopDown) +static rect2 +ui_ReserveBounds(ui_interface* Interface, ui_widget* Widget, bool Inset) { - ui_layout Result = {0}; - Result.Bounds = Bounds; - Result.Margin = Interface->Style.Margin; - Result.RowHeight = Interface->Style.RowHeight; - Result.FillDirection = FillDirection; - switch(FillDirection) + 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) { - case LayoutDirection_BottomUp: - { - Result.RowYAt = Bounds.Min.y; - }break; - - case LayoutDirection_TopDown: - { - Result.RowYAt = Bounds.Max.y - Result.RowHeight; - }break; + Bounds.Min.x += Widget->Margin.x; + Bounds.Min.y += Widget->Margin.y; + Bounds.Max.x -= Widget->Margin.x; + Bounds.Max.y -= Widget->Margin.y; } - Result.Id = ++Interface->LayoutIdAcc; - - return Result; -} - -static void -ui_PushLayout(ui_interface* Interface, ui_layout Layout) -{ - Assert(Interface->LayoutStackCount < LAYOUT_COUNT_MAX); - Interface->LayoutStack[Interface->LayoutStackCount++] = Layout; -} - -static void -ui_PopLayout(ui_interface* Interface) -{ - Assert(Interface->LayoutStackCount > 0); - Interface->LayoutStackCount -= 1; -} - -static void -ui_StartRow(ui_interface* Interface, u32 ColumnsMax = 0) -{ - u64 LayoutIdx = Interface->LayoutStackCount - 1; - Interface->LayoutStack[LayoutIdx].DrawHorizontal = true; - Interface->LayoutStack[LayoutIdx].ColumnsMax = ColumnsMax; - Interface->LayoutStack[LayoutIdx].ColumnWidths = 0; - Interface->LayoutStack[LayoutIdx].ColumnsCount = 0; -} - -static void -ui_StartRow(ui_interface* Interface, u32 ColumnsMax, r32* ColumnWidths) -{ - u64 LayoutIdx = Interface->LayoutStackCount - 1; - Interface->LayoutStack[LayoutIdx].DrawHorizontal = true; - Interface->LayoutStack[LayoutIdx].ColumnsMax = ColumnsMax; - Interface->LayoutStack[LayoutIdx].ColumnWidths = ColumnWidths; - Interface->LayoutStack[LayoutIdx].ColumnsCount = 0; -} - -static void -ui_EndRow(ui_interface* Interface) -{ - u64 LayoutIdx = Interface->LayoutStackCount - 1; - Interface->LayoutStack[LayoutIdx].DrawHorizontal = false; - Interface->LayoutStack[LayoutIdx].ColumnWidths = 0; - Interface->LayoutStack[LayoutIdx].RowYAt -= Interface->LayoutStack[LayoutIdx].RowHeight; -} - -static b32 -ui_TryReserveElementBounds(ui_layout* Layout, rect2* Bounds) -{ - b32 Result = true; - if (!Layout->DrawHorizontal) + if (Widget->ChildCount == 0) { - Bounds->Min = { Layout->Bounds.Min.x, Layout->RowYAt }; - Bounds->Max = { Layout->Bounds.Max.x, Bounds->Min.y + Layout->RowHeight }; - - switch (Layout->FillDirection) + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (State) + { + Bounds = Rect2Translate(Bounds, State->ChildrenDrawOffset); + } + } + + return Bounds; +} + +internal void +ui_CommitBounds(ui_widget* Parent, rect2 Bounds) +{ + u32 ColumnIndex = Parent->ChildCount % Parent->ColumnsCount; + if (ColumnIndex == 0) + { + switch (Parent->FillDirection) { case LayoutDirection_BottomUp: { - Layout->RowYAt += Layout->RowHeight; + Parent->RowYAt = Bounds.Max.y; + if (ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren)) + { + Parent->Bounds.Max.y = Parent->RowYAt; + } }break; case LayoutDirection_TopDown: { - Layout->RowYAt -= Layout->RowHeight; + 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) + { + 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; + } +} + +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; +} + +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; + } +} + +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) + { + Result->FillDirection = Result->Parent->FillDirection; + } + else + { + Result->FillDirection = FillDir; + } + + switch(Result->FillDirection) + { + case LayoutDirection_BottomUp: + { + Result->RowYAt = Bounds.Min.y; + }break; + + case LayoutDirection_TopDown: + { + Result->RowYAt = Bounds.Max.y - Result->RowHeight; + }break; + + 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; +} + +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; +} + +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) + { + 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.Min.x += Interface->Style.Margin.x; + Bounds.Max.x -= Interface->Style.Margin.x; + } + + 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) + { + Extents.x = Min(Extents.x, Child->Bounds.Min.y); + Extents.y = Max(Extents.y, Child->Bounds.Max.y); + } + + switch(Parent->FillDirection) + { + 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; + } +} + +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* + Assert(StringsEqual(Layout->String, LayoutName)); + + 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 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; +} + +enum ui_column_size_rule +{ + UIColumnSize_Fixed, + UIColumnSize_Percent, + UIColumnSize_Fill, + UIColumnSize_MaxWidth, +}; + +struct ui_column_spec +{ + 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); + + // 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; + + 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) + { + Column->XMax = Spec.Width; + } + else + { + Column->XMax = RemainingSpace; + } + RemainingSpace -= Column->XMax; }break; InvalidDefaultCase; } } - else + + 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++) { - if (Layout->ColumnsMax > 0) + ui_column_spec Spec = ColumnRules[i]; + ui_column* Column = Layout->Columns + i; + + r32 ColumnWidth = 0; + switch (Spec.Rule) { - Assert(Layout->ColumnsCount < Layout->ColumnsMax); - if (Layout->ColumnWidths != 0) + case UIColumnSize_Fixed: + case UIColumnSize_Percent: + case UIColumnSize_MaxWidth: { - v2 Min = { Layout->Bounds.Min.x, Layout->RowYAt }; - for (u32 i = 0; i < Layout->ColumnsCount; i++) - { - Min.x += Layout->ColumnWidths[i]; - } - Bounds->Min = Min; - Bounds->Max = Bounds->Min + v2{ Layout->ColumnWidths[Layout->ColumnsCount], Layout->RowHeight }; - } - else + ColumnWidth = Column->XMax; + }break; + + case UIColumnSize_Fill: { - r32 ElementWidth = Rect2Width(Layout->Bounds) / Layout->ColumnsMax; - Bounds->Min = { - Layout->Bounds.Min.x + (ElementWidth * Layout->ColumnsCount) + Layout->Margin.x, - Layout->RowYAt - }; - Bounds->Max = { - Bounds->Min.x + ElementWidth - Layout->Margin.x, - Bounds->Min.y + Layout->RowHeight - }; - } - Layout->ColumnsCount++; - } - else - { - Result = false; + ColumnWidth = FillColumnWidth; + }break; } + + Column->XMin = ColumnStartX ; + Column->XMax = Column->XMin + Max(0, ColumnWidth); + ColumnStartX = Column->XMax; } - return Result; + + return Layout; } -static rect2 -ui_ReserveElementBounds(ui_layout* Layout) +static void +ui_EndRow(ui_interface* Interface) { - rect2 Bounds = {0}; - if (!ui_TryReserveElementBounds(Layout, &Bounds)) - { - InvalidCodePath; - } - return Bounds; + ui_PopLayout(Interface, MakeString("Row")); } static rect2 -ui_LayoutRemaining(ui_layout Layout) +ui_LayoutRemaining(ui_widget Layout) { rect2 Result = Layout.Bounds; Result.Max.y = Layout.RowYAt; - if (Layout.DrawHorizontal) - { - Result.Max.y -= Layout.RowHeight; - } return Result; } // Widgets -internal ui_widget -ui_CreateWidget(gs_string String) -{ - ui_widget Result = {}; - Result.String = String; - Result.Alignment = Align_Left; - return Result; -} - -internal void -ui_WidgetSetFlag(ui_widget* Widget, u64 Flag) -{ - 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; -} - internal ui_eval_result ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds) { ui_eval_result Result = {}; - Assert(Interface->WidgetsCount < Interface->WidgetsCountMax); - Widget->Id.Index = Interface->WidgetsCount++; - Widget->Id.LayoutId = Interface->LayoutStack[Interface->LayoutStackCount - 1].Id; - Widget->Bounds = Bounds; - Interface->Widgets[Widget->Id.Index] = *Widget; + 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 (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id)) + { + Result.Clicked = true; + Interface->ActiveWidget = Widget->Id; + } + } + + if (Interface->HotWidget.ZIndex == 0 || + Interface->HotWidget.ZIndex <= Widget->Id.ZIndex) + { + 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; + } + + if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable) && + Interface->TempInputString.Length > 0) + { + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + + for (u32 i = 0; i < Interface->TempInputString.Length; i++) + { + if (Interface->TempInputString.Str[i] == '\b' && + State->EditString.Length > 0) + { + State->EditString.Length -= 1; + } + else + { + OutChar(&State->EditString, Interface->TempInputString.Str[i]); + } + } + } + } + +#if 0 + // if you can click it if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Clickable)) { - if (PointIsInRect(Widget->Bounds, Interface->Mouse.Pos)) + // 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)) { @@ -552,30 +1042,66 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds) Interface->ActiveWidget = Widget->Id; } - if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id) && - MouseButtonTransitionedUp(Interface->Mouse.LeftButtonState)) + 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 = {}; } - - Interface->HotWidget = Widget->Id; } } +#endif + Assert(Widget->Parent != 0); return Result; } internal ui_eval_result ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget) { - rect2 Bounds = {0}; - ui_layout* Layout = Interface->LayoutStack + Interface->LayoutStackCount - 1; - if (!ui_TryReserveElementBounds(Layout, &Bounds)) - { - // TODO(pjs): This isn't invalid, but Idk when we'd hit this case yet - InvalidCodePath; - } - + ui_widget* Layout = Interface->ActiveLayout; + rect2 Bounds = ui_ReserveBounds(Interface, Layout, true); return ui_EvaluateWidget(Interface, Widget, Bounds); } @@ -603,39 +1129,121 @@ ui_OutlineRect(ui_interface* Interface, rect2 Bounds, r32 Thickness, v4 Color) } internal void -ui_DrawString(ui_interface* Interface, gs_string String, rect2 Bounds, gs_string_alignment Alignment = Align_Left) +ui_Label(ui_interface* Interface, gs_string String, rect2 Bounds, gs_string_alignment Alignment = Align_Left) { DEBUG_TRACK_FUNCTION; - ui_widget Widget = ui_CreateWidget(String); - Widget.Bounds = Bounds; - ui_EvaluateWidget(Interface, &Widget); + ui_widget* Widget = ui_CreateWidget(Interface, String); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_EvaluateWidget(Interface, Widget, Bounds); } internal void -ui_DrawString(ui_interface* Interface, gs_string String, gs_string_alignment Alignment = Align_Left) +ui_Label(ui_interface* Interface, gs_string String, gs_string_alignment Alignment = Align_Left) { DEBUG_TRACK_FUNCTION; - ui_widget Widget = ui_CreateWidget(String); - ui_EvaluateWidget(Interface, &Widget); + ui_widget* Widget = ui_CreateWidget(Interface, String); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_EvaluateWidget(Interface, Widget); } -static b32 +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); +} + +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; +} + +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; +} + +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; +} + +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; +} + +internal b32 ui_Button(ui_interface* Interface, gs_string Text) { - ui_widget Widget = ui_CreateWidget(Text); - ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground); - ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget); + ui_widget* Widget = ui_CreateButtonWidget(Interface, Text); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); return Result.Clicked; } -static b32 +internal b32 ui_Button(ui_interface* Interface, gs_string Text, rect2 Bounds) { - ui_widget Widget = ui_CreateWidget(Text); - ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground); - ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget, Bounds); + ui_widget* Widget = ui_CreateButtonWidget(Interface, Text); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds); return Result.Clicked; } @@ -666,42 +1274,29 @@ ui_GetListItemColors(ui_interface* Interface, u32 ListItemIndex) static b32 ui_ListButton(ui_interface* Interface, gs_string Text, rect2 Bounds, u32 ListItemIndex) { - ui_widget Widget = ui_CreateWidget(Text); - ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground); - ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable); + 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); + Widget->Bounds = Bounds; + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); return Result.Clicked; } static b32 -ui_LayoutListButton(ui_interface* Interface, ui_layout* Layout, gs_string Text, u32 ListItemIndex) +ui_LayoutListButton(ui_interface* Interface, gs_string Text, u32 ListItemIndex) { + // TODO(pjs): Reimplement alternating colors return ui_Button(Interface, Text); } -static b32 -ui_LayoutListEntry(ui_interface* Interface, ui_layout* Layout, gs_string Text, u32 Index) -{ - rect2 Bounds = {0}; - if (!ui_TryReserveElementBounds(Layout, &Bounds)) - { - // TODO(Peter): this isn't really invalid, but I don't have a concrete use case - // for it yet. This should only fire if the Layout component is drawing a row, - // but if you're in row mode during a list, what should happen? - // Punting this till I have a use case - InvalidCodePath; - } - return ui_Button(Interface, Text, Bounds); -} - internal bool -ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result EvalResult) +ui_EvaluateDropdown(ui_interface* Interface, ui_widget* Widget, ui_eval_result EvalResult) { - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget.Id); - if (!State) { - State = ui_CreateRetainedState(Interface, Widget.Id); + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (!State) + { + State = ui_CreateRetainedState(Interface, Widget); } if (EvalResult.Clicked) @@ -711,10 +1306,10 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev if (State->Value) { - ui_layout ParentLayout = Interface->LayoutStack[Interface->LayoutStackCount - 1]; + ui_widget ParentLayout = *Interface->ActiveLayout; - r32 SpaceAbove = ParentLayout.Bounds.Max.y - Widget.Bounds.Max.y; - r32 SpaceBelow = Widget.Bounds.Min.y - ParentLayout.Bounds.Min.y; + 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 = {}; @@ -723,8 +1318,8 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev r32 ParentLayoutMaxY = ParentLayout.Bounds.Max.y; Direction = LayoutDirection_BottomUp; MenuBounds = rect2{ - v2{ Widget.Bounds.Min.x, Widget.Bounds.Max.y }, - v2{ Widget.Bounds.Max.x, ParentLayoutMaxY } + v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, Widget->Bounds.Max.y }, + v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, ParentLayoutMaxY } }; } else @@ -732,14 +1327,16 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev r32 ParentLayoutMinY = ParentLayout.Bounds.Min.y; Direction = LayoutDirection_TopDown; MenuBounds = rect2{ - v2{ Widget.Bounds.Min.x, ParentLayoutMinY }, - v2{ Widget.Bounds.Max.x, Widget.Bounds.Min.y } + v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, ParentLayoutMinY }, + v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, Widget->Bounds.Min.y } }; } - ui_layout Layout = ui_CreateLayout(Interface, MenuBounds, Direction); - Layout.WidgetReference = Widget.Id; - ui_PushLayout(Interface, Layout); + 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; @@ -748,234 +1345,285 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev internal bool ui_BeginDropdown(ui_interface* Interface, gs_string Text, rect2 Bounds) { - ui_widget Widget = ui_CreateWidget(Text); - ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground); - ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget, 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); } internal bool ui_BeginDropdown(ui_interface* Interface, gs_string Text) { - ui_widget Widget = ui_CreateWidget(Text); - ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable); - ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground); - ui_eval_result Result = ui_EvaluateWidget(Interface, &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); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); return ui_EvaluateDropdown(Interface, Widget, Result); } internal void ui_EndDropdown(ui_interface* Interface) { - ui_layout Layout = Interface->LayoutStack[Interface->LayoutStackCount - 1]; - ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout.WidgetReference); + ui_widget* Layout = Interface->ActiveLayout; + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->WidgetReference); if (State) { if (State->Value) { - ui_PopLayout(Interface); + ui_PopLayout(Interface, MakeString("DropdownLayout")); } } } -// -// OLD -// - -enum selection_state -{ - Selection_None, - Selection_Selected, - Selection_Deselected, -}; - -struct interface_list -{ - rect2 ListBounds; - - v2 ListElementDimensions; - v2 ElementLabelIndent; - - v4 TextColor; - v4* LineBGColors; - s32 LineBGColorsCount; - v4 LineBGHoverColor; - - s32 ListElementsCount; -}; - -internal rect2 -DrawListElementBackground(interface_list* List, mouse_state Mouse, render_command_buffer* RenderBuffer) -{ - rect2 LineBounds = {}; - LineBounds.Min = v2{ - List->ListBounds.Min.x, - List->ListBounds.Max.y - (List->ListElementDimensions.y * (List->ListElementsCount + 1)) - }; - LineBounds.Max = LineBounds.Min + List->ListElementDimensions; - - v4 Color = List->LineBGColors[List->ListElementsCount % List->LineBGColorsCount]; - if (PointIsInRect(LineBounds, Mouse.Pos)) - { - Color = List->LineBGHoverColor; - } - - PushRenderQuad2D(RenderBuffer, LineBounds.Min, LineBounds.Max, Color); - return LineBounds; -} - -internal rect2 -DrawListElement(gs_string Label, interface_list* List, mouse_state Mouse, render_command_buffer* RenderBuffer, interface_config Interface) -{ - rect2 Bounds = DrawListElementBackground(List, Mouse, RenderBuffer); - - v2 LabelPosition = Bounds.Min + List->ElementLabelIndent; - DrawString(RenderBuffer, Label, Interface.Font, LabelPosition, List->TextColor); - - List->ListElementsCount++; - return Bounds; -} - - internal r32 -EvaluateColorChannelSlider (render_command_buffer* RenderBuffer, v4 ChannelMask, v2 Min, v2 Max, r32 Current, mouse_state Mouse) +ui_EvaluateRangeSlider(ui_interface* Interface, ui_widget* Widget, ui_eval_result EvalResult, r32 Value, r32 MinValue, r32 MaxValue) { - r32 Result = Current; + r32 NewValue = Value; + ui_widget_retained_state* State = ui_GetOrCreateRetainedState(Interface, Widget); - // TODO(Peter): Can this come from outside the function? Would rather pass rect around than min/max - rect2 Rect = rect2{ Min, Max }; - - render_quad_batch_constructor Batch = PushRenderQuad2DBatch(RenderBuffer, 2); - - v4 LeftColor = ChannelMask * 0; - LeftColor.a = 1.f; - v4 RightColor = ChannelMask; - PushQuad2DOnBatch(&Batch, - RectBottomLeft(Rect), RectBottomRight(Rect), - RectTopRight(Rect), RectTopLeft(Rect), - v2{0, 0}, v2{1, 0}, v2{1, 1}, v2{0, 1}, - LeftColor, RightColor, RightColor, LeftColor); - - if (MouseButtonTransitionedDown(Mouse.LeftButtonState)) + if (EvalResult.Clicked) { - if (PointIsInRect(Rect, Mouse.DownPos)) - { - Result = ((r32)Mouse.Pos.x - Min.x) / (Max.x - Min.x); - Result = Clamp01(Result); - } + State->InitialValueR32 = Value; } - r32 DragBarWidth = 8; - v2 DragBarMin = v2{ - LerpR32(Result, Min.x, Max.x) - (DragBarWidth / 2), - Min.y - 2 - }; - v2 DragBarMax = DragBarMin + v2{DragBarWidth, (Max.y - Min.y) + 4}; + if (EvalResult.Held) + { + r32 Percent = (Interface->Mouse.Pos.x - Widget->Bounds.Min.x) / Rect2Width(Widget->Bounds); + NewValue = LerpR32(Percent, MinValue, MaxValue); + } - PushQuad2DOnBatch(&Batch, DragBarMin, DragBarMax, v4{.3f, .3f, .3f, 1.f}); + 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; +} + +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); +} + +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); +} + +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; } -internal b32 -EvaluateColorPicker (render_command_buffer* RenderBuffer, v4* Value, v2 PanelMin, interface_config Config, mouse_state Mouse) +internal void +ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows, u32 ElementCount) { - b32 ShouldClose = false; - - v2 PanelMax = v2{400, 500}; - // TODO(Peter): Can this get passed from outside? rather pass rect2 than min/max pairs - rect2 PanelRect = rect2{PanelMin, PanelMax}; - if (MouseButtonTransitionedDown(Mouse.LeftButtonState) && !PointIsInRect(PanelRect, Mouse.Pos)) + if (ElementCount < ViewportRows) { - ShouldClose = true; - } - else - { - PushRenderQuad2D(RenderBuffer, PanelRect.Min, PanelRect.Max, v4{.5f, .5f, .5f, 1.f}); - - v2 TitleMin = v2{PanelRect.Min.x + 5, PanelRect.Max.y - (Config.Font->PixelHeight + 5)}; - DrawString(RenderBuffer, MakeString("Color Picker"), Config.Font, - TitleMin, WhiteV4); - - v2 SliderDim = v2{(PanelMax.x - PanelMin.x) - 20, 32}; - // channel sliders - v2 SliderMin = TitleMin - v2{0, SliderDim.y + 10}; - Value->r = EvaluateColorChannelSlider(RenderBuffer, RedV4, SliderMin, SliderMin + SliderDim, Value->r, Mouse); - SliderMin.y -= SliderDim.y + 10; - Value->g = EvaluateColorChannelSlider(RenderBuffer, GreenV4, SliderMin, SliderMin + SliderDim, Value->g, Mouse); - SliderMin.y -= SliderDim.y + 10; - Value->b = EvaluateColorChannelSlider(RenderBuffer, BlueV4, SliderMin, SliderMin + SliderDim, Value->b, Mouse); - SliderMin.y -= SliderDim.y + 10; - Value->a = EvaluateColorChannelSlider(RenderBuffer, WhiteV4, SliderMin, SliderMin + SliderDim, Value->a, Mouse); - - // Output Color Display - SliderMin.y -= 100; - PushRenderQuad2D(RenderBuffer, SliderMin, SliderMin + v2{75, 75}, *Value); + ViewportRows = ElementCount; } - return ShouldClose; + 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: + { + 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_WidgetClearFlag(ViewportLayout, UIWidgetFlag_ExpandsToFitChildren); + + 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; + ViewportState->ChildrenDrawOffset.y = ((1.0f - State->InitialValueR32) * (r32)(ScrollableElements)) * ViewportLayout->RowHeight; } -struct search_lister_result +internal void +ui_EndList(ui_interface* Interface) { - s32 HotItem; - s32 SelectedItem; - b32 ShouldRemainOpen; -}; + // Pop the Viewport Layout + ui_PopLayout(Interface, MakeString("Contents")); + // Pop the actual list layout + ui_EndRow(Interface); +} -typedef gs_string search_lister_get_list_item_at_offset(u8* ListMemory, s32 ListLength, gs_string Searchgs_string, s32 Offset); - -internal search_lister_result -EvaluateSearchLister (ui_interface* Interface, v2 TopLeft, v2 Dimension, gs_string Title, - gs_string* ItemList, s32* ListLUT, s32 ListLength, - s32 HotItem, - gs_string* Searchgs_string, s32 Searchgs_stringCursorPosition) +internal void +ui_BeginMousePopup(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Text) { - search_lister_result Result = {}; - Result.ShouldRemainOpen = true; - Result.HotItem = HotItem; + 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")); +} + +// +internal void +ui_BeginLabelRow(ui_interface* Interface, gs_string Label, u32 Count = 2) +{ + 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; +} + +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; +} + +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); +} + + +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; +} + +internal bool +ui_BeginLabeledDropdown(ui_interface* Interface, gs_string Label, gs_string DropdownValue) +{ + ui_BeginLabelRow(Interface, Label); + return ui_BeginDropdown(Interface, DropdownValue); +} + +internal void +ui_EndLabeledDropdown(ui_interface* 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; - // TODO(Peter): Was tired. Nothing wrong with the code below - InvalidCodePath; -#if 0 - // Title Bar - rect2 TitleBarBounds = rect2{v2{TopLeft.x, TopLeft.y - 30}, v2{TopLeft.x + 300, TopLeft.y}}; - ui_FillRect(Interface, TitleBarBounds, v4{.3f, .3f, .3f, 1.f}); - ui_Drawgs_string(Interface, Title, TitleBarBounds, Interface->Style.TextColor); + 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); - MakeStringBuffer(Debuggs_string, 256); - PrintF(&Debuggs_string, "Hot Item: %d | Filtered Items: %d", HotItem, ListLength); - rect2 DebugBounds = MakeRectMinWidth(v2{ TopLeft.x + 256, TopLeft.y - 25}, v2{256, Interface->Style.LineHeight}); - ui_Drawgs_string(Interface, Debuggs_string, DebugBounds, Interface->Style.TextColor); + Result.Style.RowHeight = ui_GetTextLineHeight(Result) + (2 * Result.Style.Margin.y); - // Search Bar - PushRenderQuad2D(RenderBuffer, v2{TopLeft.x, TopLeft.y - 30}, v2{TopLeft.x + 300, TopLeft.y}, v4{.3f, .3f, .3f, 1.f}); - Drawgs_stringWithCursor(RenderBuffer, *Searchgs_string, Searchgs_stringCursorPosition, Font, v2{TopLeft.x, TopLeft.y - 25}, WhiteV4, GreenV4); - TopLeft.y -= 30; + Result.WidgetsCountMax = 4096; + Result.Widgets = PushArray(Permanent, ui_widget, Result.WidgetsCountMax); + Result.PerFrameMemory = PushStruct(Permanent, gs_memory_arena); + *Result.PerFrameMemory = CreateMemoryArena(Context.ThreadContext.Allocator); + InterfaceAssert(Result.PerFrameMemory); - for (s32 i = 0; i < ListLength; i++) - { - s32 FilteredIndex = ListLUT[i]; - gs_string ListItemgs_string = ItemList[FilteredIndex]; - - v2 Min = v2{TopLeft.x, TopLeft.y - 30}; - v2 Max = Min + Dimension - v2{0, Config.Margin.y}; - - v4 ButtonColor = Config.ButtonColor_Inactive; - if (i == HotItem) - { - ButtonColor = Config.ButtonColor_Active; - } - - if (ui_Button(Interface, ListItemgs_string, rect2{Min, Max})) - { - Result.SelectedItem = i; - } - - TopLeft.y -= 30; - } -#endif + Result.Permanent = Permanent; return Result; } diff --git a/src/app/interface_test.cpp b/src/app/interface_test.cpp new file mode 100644 index 0000000..6eba9aa --- /dev/null +++ b/src/app/interface_test.cpp @@ -0,0 +1,107 @@ +// +// File: interface_test.cpp +// Author: Peter Slattery +// Creation Date: 2020-11-15 +// +#ifndef INTERFACE_TEST_CPP + +global r32 TestSlider_Value = 5; +global r32 TestSlider_Min = 0; +global r32 TestSlider_Max = 10; +global bool TestToggle = true; +global r64 TestTextEntry = 3.1415f; + +internal void +InterfaceTest_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer) +{ + ui_InterfaceReset(&State->Interface); + State->Interface.RenderBuffer = RenderBuffer; + State->Interface.WindowBounds = Context->WindowBounds; + + gs_string A = MakeString("TestRender Layout"); + +#if 0 + ui_PushLayout(&State->Interface, A); + { +#if 1 + TestTextEntry = ui_TextEntryR64(&State->Interface, MakeString("Spacer"), TestTextEntry); + ui_Button(&State->Interface, MakeString("A")); + TestSlider_Value = ui_RangeSlider(&State->Interface, MakeString("TestSlider"), TestSlider_Value, TestSlider_Min, TestSlider_Max); +#elif 0 + ui_PushLayout(&State->Interface, MakeString("Outer")); + { + for (u32 i = 0; i < 3; i++) + { + ui_Button(&State->Interface, MakeString("A")); + } + } + ui_PopLayout(&State->Interface); + + ui_BeginRow(&State->Interface, 2); + { + ui_PushLayout(&State->Interface, MakeString("TestLayout")); + { + for (u32 i = 0; i < 5; i++) + { + ui_Button(&State->Interface, MakeString("TestButon")); + } + } + ui_PopLayout(&State->Interface); + + ui_PushLayout(&State->Interface, MakeString("TestLayout")); + { + ui_Button(&State->Interface, MakeString("TestButon")); + TestToggle = ui_Toggle(&State->Interface, MakeString("Toggle"), TestToggle); + TestSlider_Value = ui_RangeSlider(&State->Interface, MakeString("TestSlider"), TestSlider_Value, TestSlider_Min, TestSlider_Max); + if (ui_BeginDropdown(&State->Interface, MakeString("TestDropdown"))) + { + ui_Button(&State->Interface, MakeString("TestButon")); + ui_Button(&State->Interface, MakeString("TestButon")); + ui_Button(&State->Interface, MakeString("TestButon")); + } + ui_EndDropdown(&State->Interface); + } + ui_PopLayout(&State->Interface); + } + ui_EndRow(&State->Interface); + + ui_PushLayout(&State->Interface, MakeString("Outer")); + { + for (u32 i = 0; i < 3; i++) + { + ui_Button(&State->Interface, MakeString("B")); + } + } + ui_PopLayout(&State->Interface); + +#else + ui_BeginList(&State->Interface, MakeString("Test List"), 10); + { + for (u32 i = 0; i < 32; i++) + { + ui_Button(&State->Interface, MakeString("Option")); + } + } + ui_EndList(&State->Interface); +#endif + + ui_PushOverlayLayout(&State->Interface, rect2{25, 25, 400, 200}, LayoutDirection_TopDown, MakeString("t")); + { + ui_Label(&State->Interface, PushStringF(State->Interface.PerFrameMemory, 256, "Mouse Pos - %f %f", State->Interface.Mouse.Pos.x, State->Interface.Mouse.Pos.y)); + Assert(State->Interface.PerFrameMemory->CursorList); + ui_Label(&State->Interface, PushStringF(State->Interface.PerFrameMemory, 256, "Hot - %lld | Active - %lld", + State->Interface.HotWidget.Id, State->Interface.ActiveWidget.Id)); + Assert(State->Interface.PerFrameMemory->CursorList); + ui_Label(&State->Interface, PushStringF(State->Interface.PerFrameMemory, 256, "Last Active - %lld", + State->Interface.LastActiveWidget.Id)); + Assert(State->Interface.PerFrameMemory->CursorList); + } + ui_PopLayout(&State->Interface); + } + ui_PopLayout(&State->Interface); +#endif +} + + +#define INTERFACE_TEST_CPP +#endif // INTERFACE_TEST_CPP \ No newline at end of file diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h new file mode 100644 index 0000000..4345524 --- /dev/null +++ b/src/app/patterns/blumen_patterns.h @@ -0,0 +1,730 @@ +// +// File: blumen_patterns.h +// Author: Peter Slattery +// Creation Date: 2021-01-15 +// +#ifndef BLUMEN_PATTERNS_H + +#define FLOWER_COLORS_COUNT 12 + +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 } +}; +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 }, +}; +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 }, +}; + +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 +GetColor(pixel* 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; + + pixel Result = PixelMix(PercentLower, Colors[LowerIndex], Colors[HigherIndex]); + + return Result; +} + +internal void +SolidColorPattern(led_buffer* Leds, pixel Color) +{ + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + { + Leds->Colors[LedIndex] = Color; + } +} + +internal void +Pattern_Blue(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + pixel Blue = pixel{0, 0, 255}; + SolidColorPattern(Leds, Blue); +} + +internal void +Pattern_Green(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + pixel Green = pixel{0, 255, 0}; + SolidColorPattern(Leds, Green); +} + +internal void +Pattern_FlowerColors(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + 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); + + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; 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, 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, 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 = 0; LedIndex < Leds->LedCount; 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, 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 = 0; LedIndex < Leds->LedCount; 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 = {}; + + 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; + Assert(H); + //if ( isNaN(h) ) + //H = 0; + 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_HueShift(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + r32 Height = SinR32(Time) * 25; + + r32 CycleLength = 5.0f; + r32 CycleProgress = FractR32(Time / CycleLength); + r32 CycleBlend = (SinR32(Time) * .5f) + .5f; + + v4 HSV = { CycleProgress * 360, 1, 1, 1 }; + v4 RGB = HSVToRGB(HSV); + + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + { + v4 Pos = Leds->Positions[LedIndex]; + r32 Dist = Pos.y - Height; + + //v4 HSV = { (ModR32(Dist, 25) / 25) * 360, 1, 1, 1 }; + //v4 RGB = HSVToRGB(HSV); + + u8 R = (u8)(RGB.x * 255); + u8 G = (u8)(RGB.y * 255); + u8 B = (u8)(RGB.z * 255); + + Leds->Colors[LedIndex].R = R; + Leds->Colors[LedIndex].G = G; + Leds->Colors[LedIndex].B = B; + } +} + +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) +{ + r32 HueBase = ModR32(Time * 50, 360); + + r32 CycleLength = 5.0f; + r32 CycleProgress = FractR32(Time / CycleLength); + r32 CycleBlend = (SinR32(Time) * .5f) + .5f; + + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++) + { + v4 Pos = Leds->Positions[LedIndex]; + r32 Hue = HueBase + Pos.y + Pos.x; + v4 HSV = { Hue, 1, 1, 1 }; + v4 RGB = HSVToRGB(HSV); + + Leds->Colors[LedIndex] = V4ToRGBPixel(RGB); + } +} + +internal void +Pattern_AllGreen(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) +{ + for (u32 LedIndex = 0; LedIndex < Leds->LedCount; 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; + } + + } +} + +internal r32 +PatternHash(r32 Seed) +{ + return FractR32(Seed * 17.0 * FractR32(Seed * 0.3183099)); +} + +internal void +Pattern_Spots(led_buffer* Leds, 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 = 0; LedIndex < Leds->LedCount; 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, 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++) + { + v2 Vector = v2{ + Leds->Positions[LedIndex].x, + Leds->Positions[LedIndex].z + }; + Vector = V2Normalize(Vector); + + r32 Angle = V2Dot(RefVector, Vector); + + v4 HSV = { (Angle * 30) + (Time * 10) + Leds->Positions[LedIndex].y, 1, 1, 1 }; + v4 RGB = HSVToRGB(HSV); + + Leds->Colors[LedIndex] = V4ToRGBPixel(RGB); + } +} + +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) +{ + 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, 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 = 0; LedIndex < Leds->LedCount; 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, 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, 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 + pixel* 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]; + + pixel 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); + + FinalColor = BottomColor; + + Leds->Colors[LedIndex] = FinalColor; + } + } +} + +r32 TLastFrame = 0; +pixel* FAC = &FlowerAColors[0]; +pixel* FBC = &FlowerBColors[0]; +pixel* 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; + FAC = FBC; + FBC = FCC; + FCC = Temp; + } + TLastFrame = Time; + + for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) + { + v2_strip Strip = Assembly.Strips[StripIndex]; + + // Each flower different + pixel* 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++) + { + u32 LedIndex = Strip.LedLUT[i]; + v4 P = Leds->Positions[LedIndex]; + + r32 T = ModR32(P.y + CycleT, 200) / 200.f; + T = Clamp01(T); + + Leds->Colors[LedIndex] = GetColor(Colors, FLOWER_COLORS_COUNT, T); + } + } +} +#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 f3d12c5..df968c8 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -23,9 +23,12 @@ #include "win32_foldhaus_work_queue.h" #include "win32_foldhaus_serial.h" #include "win32_foldhaus_socket.h" +#include "win32_foldhaus_mouse.h" #include "../foldhaus_renderer.cpp" +#include "win32_test_code.cpp" + global b32 Running = false; global b32 WindowIsActive = false; @@ -187,7 +190,7 @@ HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, false, true, ShiftDown, AltDown, CtrlDown, false); - Mouse->LeftButtonState = KeyState_IsDown & ~KeyState_WasDown; + Mouse->LeftButtonState |= KeyState_IsDown; Mouse->DownPos = Mouse->Pos; // :Win32MouseEventCapture @@ -237,7 +240,7 @@ HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, true, false, ShiftDown, AltDown, CtrlDown, false); - Mouse->LeftButtonState = ~KeyState_IsDown & KeyState_WasDown; + Mouse->LeftButtonState &= ~KeyState_IsDown; // :Win32MouseEventCapture ReleaseCapture(); @@ -276,6 +279,7 @@ HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse case WM_KEYDOWN: case WM_KEYUP: { +#if 0 int VirtualKey = (int)Message.wParam; key_code Key = Win32GetKeyCode(VirtualKey, true, false); s32 KeyIndex = (int)Key; @@ -287,6 +291,27 @@ HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse 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); @@ -347,100 +372,155 @@ Win32Realloc(u8* Buf, s32 OldSize, s32 NewSize) return NewMemory; } -// NOTE(Peter): Only meant to take one of the values specified below: -// IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, -// IDC_ICON, IDC_NO, IDC_SIZE, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, -// IDC_SIZEWE, IDC_UPARROW, IDC_WAIT -internal HCURSOR -Win32LoadSystemCursor(char* CursorIdentifier) -{ - u32 Error = 0; - HCURSOR Result = LoadCursorA(NULL, CursorIdentifier); - if (Result == NULL) - { - Error = GetLastError(); - InvalidCodePath; - } - return Result; -} - internal void -Win32_SendAddressedDataBuffers(gs_thread_context Context, addressed_data_buffer_list OutputData) +Win32_SendAddressedDataBuffer(gs_thread_context Context, addressed_data_buffer* BufferAt) { DEBUG_TRACK_FUNCTION; u32 BuffersSent = 0; + u32 DataSizeSent = 0; - for (addressed_data_buffer* BufferAt = OutputData.Root; - BufferAt != 0; - BufferAt = BufferAt->Next) + switch(BufferAt->AddressType) { - 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) { - 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); + if (SerialPort != INVALID_HANDLE_VALUE) { - HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 2000000, 8, NOPARITY, 1); - if (SerialPort != INVALID_HANDLE_VALUE) + if (Win32SerialPort_Write(SerialPort, BufferAt->Data)) { - if (Win32SerialPort_Write(SerialPort, BufferAt->Data)) - { - BuffersSent += 1; - } + BuffersSent += 1; + DataSizeSent += BufferAt->Data.Size; + } + else + { + Win32SerialArray_Close(BufferAt->ComPort); } } - else - { - OutputDebugStringA("Skipping data buffer because its COM Port isn't set"); - } - }break; - - InvalidDefaultCase; - } + } + else + { +#if 0 + OutputDebugStringA("Skipping data buffer because its COM Port isn't set"); +#endif + } + }break; + + InvalidDefaultCase; } - - gs_string OutputStr = AllocatorAllocString(Context.Allocator, 256); - PrintF(&OutputStr, "Buffers Sent: %d\n", BuffersSent); - NullTerminate(&OutputStr); - OutputDebugStringA(OutputStr.Str); } internal void -Win32_SendAddressedDataBuffers_Job(gs_thread_context Context, gs_data Arg) +Win32_SendAddressedDataBuffer_Job(gs_thread_context Context, gs_data Arg) { - addressed_data_buffer_list* OutputData = (addressed_data_buffer_list*)Arg.Memory; - Win32_SendAddressedDataBuffers(Context, *OutputData); + addressed_data_buffer* OutputData = (addressed_data_buffer*)Arg.Memory; + Win32_SendAddressedDataBuffer(Context, OutputData); } -#pragma pack(push, 1) -struct test_microphone_packet +internal bool +ReloadAndLinkDLL(win32_dll_refresh* DLL, context* Context, gs_work_queue* WorkQueue, bool ShouldError) { - b8 ChangeAnimation; - char AnimationFileName[32]; - b8 SetLayer; - char LayerName[32]; - r32 LayerOpacity; - b8 SetLayerParamColor; - char LayerParamColor[7]; - r32 OverrideDuration; -}; -#pragma pack(pop) + bool Success = false; + if (HotLoadDLL(DLL)) + { + SetApplicationLinks(Context, *DLL, WorkQueue); + Context->ReloadStaticData(*Context, GlobalDebugServices); + Success = true; + } + else if(ShouldError) + { + OutputDebugStringA("Unable to load application DLL at startup.\nAborting\n"); + } + return Success; +} -inline u32 -UpackB4(const u8* ptr) +internal gs_const_string +GetExePath(HINSTANCE HInstance, gs_thread_context ThreadContext) { - return (u32)(ptr[3] | (ptr[2] << 8) | (ptr[1] << 16) | (ptr[0] << 24)); + 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; + } + + 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; + + 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); + } + } + + if (WorkingDirectory.Length > 0) + { + OutputDebugStringA("Setting Working Directory\n"); + OutputDebugStringA(WorkingDirectory.Str); + + Result = SetCurrentDirectory(WorkingDirectory.Str); + if (!Result) + { + u32 Error = GetLastError(); + InvalidCodePath; + } + } + else + { + OutputDebugStringA("Error, no data folder found\n"); + } + + return Result; } int WINAPI @@ -453,35 +533,7 @@ WinMain ( { gs_thread_context ThreadContext = Win32CreateThreadContext(); -#if 0 - // NOTE(pjs): UART TEST CODE - // NOTE(pjs): UART TEST CODE - // NOTE(pjs): UART TEST CODE - - u32 LedCount = 48; - u32 MessageBaseSize = sizeof(uart_header) + sizeof(uart_channel) + sizeof(uart_footer); - MessageBaseSize += sizeof(u8) * 3 * LedCount; - gs_data MessageBuffer = PushSizeToData(ThreadContext.Transient); - - gs_memory_cursor WriteCursor = CreateMemoryCursor(MessageBuffer); - - uart_header* Header = PushStructOnCursor(WriteCursor, uart_header); - UART_FillHeader(Header, Strip.UARTAddr.Channel, UART_SET_CHANNEL_WS2812); - uart_channel* Channel = PushStructOnCursor(WriteCursor, uart_channel); - *Channel = ChannelSettings; - - for (u32 i = 0; i < LedCount; i++) - { - u8* OutputPixel = PushArrayOnCursor(WriteCursor, u8, 3); - OutputPixel[Channel->RedIndex] = (u8)(i); - OutputPixel[Channel->GreenIndex] = 0; - OutputPixel[Channel->BlueIndex] = 0; - } - - uart_footer* Footer = PushStructOnCursor(WriteCursor, uart_footer); - UART_FillFooter(Footer, (u8*)Header); - -#endif + if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1; MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents); Win32UpdateWindowDimension(&MainWindow); @@ -492,14 +544,10 @@ WinMain ( OpenGLWindowInfo.DepthBits = 0; CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow); - s32 InitialMemorySize = MB(64); - u8* InitialMemory = (u8*)Win32Alloc(InitialMemorySize, 0); context Context = {}; Context.ThreadContext = ThreadContext; - Context.MemorySize = InitialMemorySize; - Context.MemoryBase = InitialMemory; - Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}}; - Context.Mouse = {0}; + Context.MemorySize = MB(64); + Context.MemoryBase = (u8*)Win32Alloc(Context.MemorySize, 0); gs_memory_arena PlatformPermanent = CreateMemoryArena(Context.ThreadContext.Allocator); @@ -509,109 +557,43 @@ WinMain ( r32 LastFrameSecondsElapsed = 0.0f; GlobalDebugServices = PushStruct(&PlatformPermanent, debug_services); - s32 DebugThreadCount = PLATFORM_THREAD_COUNT + 1; InitDebugServices(GlobalDebugServices, PerformanceCountFrequency, DEBUGAlloc, Win32Realloc, GetWallClock, Win32GetThreadId, - DebugThreadCount); + PLATFORM_THREAD_COUNT + 1); - input_queue InputQueue; - { - s32 InputQueueMemorySize = sizeof(input_entry) * 32; - u8* InputQueueMemory = (u8*)Win32Alloc(InputQueueMemorySize, 0); - InputQueue = InitializeInputQueue(InputQueueMemory, InputQueueMemorySize); - } + input_queue InputQueue = InputQueue_Create(&PlatformPermanent, 32); - // - // Set up worker threads - const s32 WorkerThreadCount = PLATFORM_THREAD_COUNT; - worker_thread_info* WorkerThreads = 0; - if (PLATFORM_THREAD_COUNT > 0) - { - WorkerThreads = PushArray(&PlatformPermanent, worker_thread_info, PLATFORM_THREAD_COUNT); - } - - HANDLE WorkQueueSemaphoreHandle = CreateSemaphoreEx(0, 0, PLATFORM_THREAD_COUNT, 0, 0, SEMAPHORE_ALL_ACCESS); - - gs_work_queue WorkQueue = {}; - WorkQueue.SemaphoreHandle = &WorkQueueSemaphoreHandle; - WorkQueue.JobsMax = 512; - WorkQueue.Jobs = PushArray(&PlatformPermanent, gs_threaded_job, WorkQueue.JobsMax); - WorkQueue.NextJobIndex = 0; - WorkQueue.PushWorkOnQueue = Win32PushWorkOnQueue; - WorkQueue.CompleteQueueWork = Win32DoQueueWorkUntilDone; - WorkQueue.ResetWorkQueue = ResetWorkQueue; - - for (s32 i = 0; i < PLATFORM_THREAD_COUNT; i++) - { - // ID = 0 is reserved for this thread - WorkerThreads[i].Queue = &WorkQueue; - WorkerThreads[i].Handle = CreateThread(0, 0, &WorkerThreadProc, (void*)&WorkerThreads[i], 0, 0); - } - - // Cursors - HCURSOR CursorArrow = Win32LoadSystemCursor(IDC_ARROW); - HCURSOR CursorPointer = Win32LoadSystemCursor(IDC_HAND); - HCURSOR CursorLoading = Win32LoadSystemCursor(IDC_WAIT); - HCURSOR CursorHorizontalArrows = Win32LoadSystemCursor(IDC_SIZEWE); - HCURSOR CursorVerticalArrows = Win32LoadSystemCursor(IDC_SIZENS); - HCURSOR CursorDiagonalTopLeftArrows = Win32LoadSystemCursor(IDC_SIZENWSE); - HCURSOR CursorDiagonalTopRightArrows = Win32LoadSystemCursor(IDC_SIZENESW); + Win32WorkQueue_Init(&PlatformPermanent, PLATFORM_THREAD_COUNT); // Platform functions - Context.GeneralWorkQueue = &WorkQueue; + 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 (HotLoadDLL(&DLLRefresh)) - { - SetApplicationLinks(&Context, DLLRefresh, &WorkQueue); - Context.ReloadStaticData(Context, GlobalDebugServices); - } - else - { - MessageBox(MainWindow.Handle, "Unable to load application DLL at startup.\nAborting\n", "Set Up Error", MB_OK); - return -1; - } + if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true)) { return -1; } - WSADATA WSAData; - WSAStartup(MAKEWORD(2, 2), &WSAData); - Win32Sockets = Win32SocketArray_Create(16, &PlatformPermanent); + Mouse_Init(); -#if 0 - // NOTE(pjs): SOCKET READING TEST CODE - // NOTE(pjs): SOCKET READING TEST CODE - // NOTE(pjs): SOCKET READING TEST CODE - - win32_socket TestSocket = Win32Socket_ConnectToAddress("127.0.0.1", "20185"); - test_microphone_packet* Recv = 0; - while (true) - { - gs_data Data = Win32Socket_Receive(&TestSocket, ThreadContext.Transient); - if (Data.Size > 0) - { - OutputDebugStringA("Received\n"); - Recv = (test_microphone_packet*)Data.Memory; - } - ClearArena(ThreadContext.Transient); - } -#endif + Win32SocketSystem_Init(&PlatformPermanent); Win32SerialArray_Create(ThreadContext); - s32 RenderMemorySize = MB(12); - u8* RenderMemory = PushSize(&PlatformPermanent, RenderMemorySize); - render_command_buffer RenderBuffer = AllocateRenderCommandBuffer(RenderMemory, RenderMemorySize, Win32Realloc); + render_command_buffer RenderBuffer = AllocateRenderCommandBuffer(MB(12), &PlatformPermanent, Win32Realloc); - addressed_data_buffer_list OutputData = {}; - OutputData.Arena = AllocatorAllocStruct(Context.ThreadContext.Allocator, gs_memory_arena); - *OutputData.Arena = CreateMemoryArena(Context.ThreadContext.Allocator); + addressed_data_buffer_list OutputData = AddressedDataBufferList_Create(ThreadContext); Context.InitializeApplication(Context); @@ -626,24 +608,12 @@ WinMain ( DEBUG_TRACK_SCOPE(MainLoop); ResetInputQueue(&InputQueue); - if (HotLoadDLL(&DLLRefresh)) - { - SetApplicationLinks(&Context, DLLRefresh, &WorkQueue); - Context.ReloadStaticData(Context, GlobalDebugServices); - } + + ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false); AddressedDataBufferList_Clear(&OutputData); - { // Mouse Position - POINT MousePos; - GetCursorPos (&MousePos); - ScreenToClient(MainWindow.Handle, &MousePos); - - Context.Mouse.Scroll = 0; - Context.Mouse.OldPos = Context.Mouse.Pos; - Context.Mouse.Pos = v2{(r32)MousePos.x, (r32)MainWindow.Height - MousePos.y}; - Context.Mouse.DeltaPos = Context.Mouse.Pos - Context.Mouse.OldPos; - } + Mouse_Update(MainWindow, &Context); MSG Message; while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE)) @@ -659,59 +629,42 @@ 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); + } + } + RenderCommandBuffer(RenderBuffer); ClearRenderBuffer(&RenderBuffer); - if (true) - { - gs_data ProcArg = {}; - ProcArg.Memory = (u8*)&OutputData; - ProcArg.Size = sizeof(OutputData); - Win32PushWorkOnQueue(&WorkQueue, Win32_SendAddressedDataBuffers_Job, ProcArg, ConstString("Send UART Data")); - } - - Context.Mouse.LeftButtonState = GetMouseButtonStateAdvanced(Context.Mouse.LeftButtonState); - Context.Mouse.MiddleButtonState = GetMouseButtonStateAdvanced(Context.Mouse.MiddleButtonState); - Context.Mouse.RightButtonState = GetMouseButtonStateAdvanced(Context.Mouse.RightButtonState); - - switch (Context.Mouse.CursorType) - { - case CursorType_Arrow: - { - SetCursor(CursorArrow); - }break; - case CursorType_Pointer: - { - SetCursor(CursorPointer); - }break; - case CursorType_Loading: - { - SetCursor(CursorLoading); - }break; - case CursorType_HorizontalArrows: - { - SetCursor(CursorHorizontalArrows); - }break; - case CursorType_VerticalArrows: - { - SetCursor(CursorVerticalArrows); - }break; - case CursorType_DiagonalTopLeftArrows: - { - SetCursor(CursorDiagonalTopLeftArrows); - }break; - case CursorType_DiagonalTopRightArrows: - { - SetCursor(CursorDiagonalTopRightArrows); - }break; - InvalidDefaultCase; - } + Mouse_Advance(&Context); HDC DeviceContext = GetDC(MainWindow.Handle); SwapBuffers(DeviceContext); ReleaseDC(MainWindow.Handle, DeviceContext); - //Win32DoQueueWorkUntilDone(&WorkQueue, Context.ThreadContext); + Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext); s64 FinishedWorkTime = GetWallClock(); r32 SecondsElapsed = GetSecondsElapsed(LastFrameEnd, FinishedWorkTime, PerformanceCountFrequency); @@ -730,20 +683,10 @@ WinMain ( Context.CleanupApplication(Context); - for (s32 SocketIdx = 0; SocketIdx < Win32Sockets.Count; SocketIdx++) - { - Win32Socket_Close(Win32Sockets.Values + SocketIdx); - } + Win32WorkQueue_Cleanup(); + //Win32_TestCode_SocketReading_Cleanup(); - s32 CleanupResult = 0; - do { - CleanupResult = WSACleanup(); - }while(CleanupResult == SOCKET_ERROR); - - for (s32 Thread = 0; Thread < PLATFORM_THREAD_COUNT; Thread++) - { - TerminateThread(WorkerThreads[Thread].Handle, 0); - } + Win32SocketSystem_Cleanup(); return 0; } diff --git a/src/app/platform_win32/win32_foldhaus_fileio.h b/src/app/platform_win32/win32_foldhaus_fileio.h index 2c4059c..95163ea 100644 --- a/src/app/platform_win32/win32_foldhaus_fileio.h +++ b/src/app/platform_win32/win32_foldhaus_fileio.h @@ -46,6 +46,20 @@ GET_FILE_INFO(Win32GetFileInfo) } CloseHandle(FileHandle); } + else + { + DWORD FileAttr = GetFileAttributes(Path.Str); + if (FileAttr != INVALID_FILE_ATTRIBUTES && + (FileAttr & FILE_ATTRIBUTE_DIRECTORY)) + { + Result.Path = Path; + Result.IsDirectory = true; + } + else + { + // Path is not a file or directory + } + } return Result; } @@ -183,7 +197,8 @@ Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* { u32 FilesCount = 0; - u32 IndexOfLastSlash = FindLastFromSet(Path, "\\/"); + s64 IndexOfLastSlash = FindLastFromSet(Path, "\\/"); + Assert(IndexOfLastSlash >= 0); gs_const_string SearchPath = Substring(Path, 0, IndexOfLastSlash + 1); WIN32_FIND_DATA FindFileData; diff --git a/src/app/platform_win32/win32_foldhaus_mouse.h b/src/app/platform_win32/win32_foldhaus_mouse.h new file mode 100644 index 0000000..0dbc3d4 --- /dev/null +++ b/src/app/platform_win32/win32_foldhaus_mouse.h @@ -0,0 +1,118 @@ +// +// File: win32_mouse.h +// Author: Peter Slattery +// Creation Date: 2021-01-10 +// +#ifndef WIN32_MOUSE_H + +HCURSOR CursorArrow; +HCURSOR CursorPointer; +HCURSOR CursorLoading; +HCURSOR CursorHArrows; +HCURSOR CursorVArrows; +HCURSOR CursorDTopLeftArrows; +HCURSOR CursorDTopRightArrows; + +HCURSOR CurrentCursor; + +// NOTE(Peter): Only meant to take one of the values specified below: +// IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, +// IDC_ICON, IDC_NO, IDC_SIZE, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, +// IDC_SIZEWE, IDC_UPARROW, IDC_WAIT +internal HCURSOR +Win32LoadSystemCursor(char* CursorIdentifier) +{ + u32 Error = 0; + HCURSOR Result = LoadCursorA(NULL, CursorIdentifier); + if (Result == NULL) + { + Error = GetLastError(); + InvalidCodePath; + } + return Result; +} + +internal void +Mouse_Init() +{ + CursorArrow = Win32LoadSystemCursor(IDC_ARROW); + CursorPointer = Win32LoadSystemCursor(IDC_HAND); + CursorLoading = Win32LoadSystemCursor(IDC_WAIT); + CursorHArrows = Win32LoadSystemCursor(IDC_SIZEWE); + CursorVArrows = Win32LoadSystemCursor(IDC_SIZENS); + CursorDTopLeftArrows = Win32LoadSystemCursor(IDC_SIZENWSE); + CursorDTopRightArrows = Win32LoadSystemCursor(IDC_SIZENESW); +} + +internal void +Mouse_Update(window Window, context* Context) +{ + POINT Pos; + GetCursorPos (&Pos); + ScreenToClient(Window.Handle, &Pos); + + Context->Mouse.Scroll = 0; + Context->Mouse.OldPos = Context->Mouse.Pos; + Context->Mouse.Pos = v2{(r32)Pos.x, (r32)Window.Height - Pos.y}; + Context->Mouse.DeltaPos = Context->Mouse.Pos - Context->Mouse.OldPos; + + if (KeyIsDown(Context->Mouse.LeftButtonState)) + { + SetKeyWasDown(Context->Mouse.LeftButtonState); + } + else + { + SetKeyWasUp(Context->Mouse.LeftButtonState); + } + + if (KeyIsDown(Context->Mouse.MiddleButtonState)) + { + SetKeyWasDown(Context->Mouse.MiddleButtonState); + } + else + { + SetKeyWasUp(Context->Mouse.MiddleButtonState); + } + + + if (KeyIsDown(Context->Mouse.RightButtonState)) + { + SetKeyWasDown(Context->Mouse.RightButtonState); + } + else + { + SetKeyWasUp(Context->Mouse.RightButtonState); + } +} + + +internal void +Mouse_Advance(context* Context) +{ + Context->Mouse.LeftButtonState = GetMouseButtonStateAdvanced(Context->Mouse.LeftButtonState); + Context->Mouse.MiddleButtonState = GetMouseButtonStateAdvanced(Context->Mouse.MiddleButtonState); + Context->Mouse.RightButtonState = GetMouseButtonStateAdvanced(Context->Mouse.RightButtonState); + + HCURSOR NewCursor = 0; + switch (Context->Mouse.CursorType) + { + case CursorType_Arrow: { NewCursor = CursorArrow; } break; + case CursorType_Pointer: { NewCursor = CursorPointer; }break; + case CursorType_Loading: { NewCursor = CursorLoading; }break; + case CursorType_HArrows: { NewCursor = CursorHArrows; }break; + case CursorType_VArrows: { NewCursor = CursorVArrows; }break; + case CursorType_DTopLeftArrows: { NewCursor = CursorDTopLeftArrows; }break; + case CursorType_DTopRightArrows: { NewCursor = CursorDTopRightArrows; }break; + + InvalidDefaultCase; + } + if (NewCursor != CurrentCursor) + { + CurrentCursor = NewCursor; + SetCursor(NewCursor); + } +} + + +#define WIN32_MOUSE_H +#endif // WIN32_MOUSE_H \ No newline at end of file diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h index 0368547..71b4c99 100644 --- a/src/app/platform_win32/win32_foldhaus_serial.h +++ b/src/app/platform_win32/win32_foldhaus_serial.h @@ -6,9 +6,9 @@ #ifndef WIN32_SERIAL_H global u32 Win32SerialHandlesCountMax; -global u32 Win32SerialHandlesCount; global HANDLE* Win32SerialHandles; global gs_string* Win32SerialPortNames; +global s32* Win32SerialPortFilled; DCB Win32SerialPort_GetState(HANDLE ComPortHandle) @@ -137,7 +137,18 @@ Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer) { OutputDebugStringA("Error: Unable to write to port\n"); s32 Error = GetLastError(); - //InvalidCodePath; + 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_INVALID_HANDLE: + InvalidDefaultCase; + } } return Success; @@ -183,12 +194,15 @@ Win32SerialArray_Create(gs_thread_context Context) DEBUG_TRACK_FUNCTION; Win32SerialHandlesCountMax = 32; - Win32SerialHandlesCount = 0; + Win32SerialHandles = AllocatorAllocArray(Context.Allocator, HANDLE, Win32SerialHandlesCountMax); Win32SerialPortNames = AllocatorAllocArray(Context.Allocator, gs_string, Win32SerialHandlesCountMax); + Win32SerialPortFilled = AllocatorAllocArray(Context.Allocator, s32, Win32SerialHandlesCountMax); + for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) { Win32SerialPortNames[i] = AllocatorAllocString(Context.Allocator, 256); + Win32SerialPortFilled[i] = 0; } } @@ -197,10 +211,28 @@ Win32SerialArray_Push(HANDLE SerialHandle, gs_const_string PortName) { DEBUG_TRACK_FUNCTION; - Assert(Win32SerialHandlesCount < Win32SerialHandlesCountMax); - u32 Index = Win32SerialHandlesCount++; - Win32SerialHandles[Index] = SerialHandle; - PrintF(&Win32SerialPortNames[Index], "%S", PortName); + bool Found = false; + for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) + { + bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + i, 1, 0); + if (!WasFilled) + { + Win32SerialHandles[i] = SerialHandle; + PrintF(&Win32SerialPortNames[i], "%S", PortName); + Found = true; + break; + } + } + 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; } HANDLE @@ -209,9 +241,10 @@ Win32SerialArray_Get(gs_const_string PortName) DEBUG_TRACK_FUNCTION; HANDLE PortHandle = INVALID_HANDLE_VALUE; - for (u32 i = 0; i < Win32SerialHandlesCount; i++) + for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) { - if (StringsEqual(Win32SerialPortNames[i].ConstString, PortName)) + if (Win32SerialPortFilled[i] && + StringsEqual(Win32SerialPortNames[i].ConstString, PortName)) { PortHandle = Win32SerialHandles[i]; break; @@ -239,5 +272,19 @@ Win32SerialArray_GetOrOpen(gs_const_string PortName, u32 BaudRate, u8 ByteSize, return PortHandle; } +void +Win32SerialArray_Close(gs_const_string PortName) +{ + for (u32 i = 0; i < Win32SerialHandlesCountMax; i++) + { + if (Win32SerialPortFilled[i] && StringsEqual(Win32SerialPortNames[i].ConstString, PortName)) + { + Win32SerialPort_Close(Win32SerialHandles[i]); + Win32SerialArray_Pop(i); + break; + } + } +} + #define WIN32_SERIAL_H #endif // WIN32_SERIAL_H \ No newline at end of file diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index a95b806..b42c8c9 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -17,6 +17,9 @@ struct win32_socket_array s32 Count; }; +global WSADATA WSAData; +global win32_socket_array Win32Sockets; + ////////////////////// // // Win32 Socket Array @@ -50,9 +53,7 @@ Win32SocketArray_Get(win32_socket_array Array, s32 Index) ////////////////////// // -// Win32 Socket System - -global win32_socket_array Win32Sockets; +// Win32 Sockets internal win32_socket Win32Socket_Create(s32 AddressFamily, s32 Type, s32 Protocol) @@ -130,6 +131,206 @@ Win32Socket_ConnectToAddress(char* Address, char* DefaultPort) return Result; } +internal bool +Win32ConnectSocket(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 = 1; + 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) + { +#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 + { + Error = WSAGetLastError(); + InvalidCodePath; + } + + if (!Result) + { + Assert(Socket->PlatformHandle == 0); + } + + freeaddrinfo(PotentialConnections); + return Result; +} + +internal bool +Win32CloseSocket(platform_socket* Socket) +{ + SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; + closesocket(*Win32Sock); + Win32Free((u8*)Socket->PlatformHandle, sizeof(SOCKET)); + *Socket = {}; + return true; +} + +internal bool +Win32SocketQueryStatus(platform_socket* Socket) +{ + SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; + bool Result = (*Win32Sock != INVALID_SOCKET); + return Result; +} + +internal u32 +Win32SocketPeek(platform_socket* Socket) +{ + u32 Result = 0; + s32 Flags = MSG_PEEK; + SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle; + char Temp[4]; + u32 TempSize = 4; + + s32 BytesQueued = recv(*Win32Sock, Temp, TempSize, Flags); + if (BytesQueued != SOCKET_ERROR) + { + Result = (u32)BytesQueued; + } + else + { + // TODO(pjs): Error handling + s32 Error = WSAGetLastError(); + switch (Error) + { + case WSAEWOULDBLOCK: + case WSAENOTCONN: + { + }break; + + InvalidDefaultCase; + } + } + return Result; +} + +internal gs_data +Win32SocketReceive(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 +#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); + 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 +} + +internal s32 +Win32SocketSend(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)); + + if (LengthSent == SOCKET_ERROR) + { + s32 Error = WSAGetLastError(); + if (Error == 10051) + { + } + if (Error == 10053) + { + // TODO(pjs): WSAECONNABORTED + InvalidCodePath; + } + else + { + // TODO(Peter): :ErrorLogging + InvalidCodePath; + } + } + + return LengthSent; +} + internal s32 Win32Socket_SetOption(win32_socket* Socket, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength) { @@ -227,7 +428,18 @@ Win32Socket_Receive(win32_socket* Socket, gs_memory_arena* Storage) { // TODO(pjs): Error logging s32 Error = WSAGetLastError(); - InvalidCodePath; + switch (Error) + { + case WSAECONNABORTED: + case WSANOTINITIALISED: + break; + + case WSAENOTCONN: + { + + }break; + InvalidDefaultCase; + } } Result.Size = BytesReceived; return Result; @@ -241,6 +453,38 @@ Win32Socket_Close(win32_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); + } +} + +////////////////////// +// +// Win32 Socket System + +internal void +Win32SocketSystem_Init(gs_memory_arena* 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); +} + PLATFORM_GET_SOCKET_HANDLE(Win32GetSocketHandle) { s32 Result = Win32SocketArray_Take(&Win32Sockets); diff --git a/src/app/platform_win32/win32_foldhaus_work_queue.h b/src/app/platform_win32/win32_foldhaus_work_queue.h index 2db58f3..b3a2a36 100644 --- a/src/app/platform_win32/win32_foldhaus_work_queue.h +++ b/src/app/platform_win32/win32_foldhaus_work_queue.h @@ -18,6 +18,16 @@ struct worker_thread_info gs_work_queue* Queue; }; +struct win32_work_queue +{ + u32 ThreadCount; + worker_thread_info* Threads; + gs_work_queue WorkQueue; +}; + +worker_thread_info* WorkerThreads; +win32_work_queue Win32WorkQueue; + internal s32 Win32GetThreadId() { @@ -46,6 +56,8 @@ Win32CreateThreadContext(gs_memory_arena* Transient = 0) Win32EnumerateDirectory, Result.Transient); + Result.DebugOutput.Print = Win32DebugPrint; + return Result; } @@ -151,5 +163,90 @@ WorkerThreadProc (LPVOID InputThreadInfo) 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; +} + +CREATE_THREAD(Win32CreateThread) +{ + Thread->Proc = Proc; + Thread->UserData = UserData; + + // TODO(pjs): ugh, allocation out in the middle of nowhere + HANDLE* ThreadHandle = (HANDLE*)Win32Alloc(sizeof(HANDLE), 0); + *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; +} + +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); + } +} + +internal void +Win32WorkQueue_Cleanup() +{ + u32 Error = 0; + for (u32 Thread = 0; Thread < Win32WorkQueue.ThreadCount; Thread++) + { + u32 Success = TerminateThread(Win32WorkQueue.Threads[Thread].Handle, 0); + if (!Success) + { + Error = GetLastError(); + InvalidCodePath; + } + } +} + #define WIN32_FOLDHAUS_WORK_QUEUE_H #endif // WIN32_FOLDHAUS_WORK_QUEUE_H \ No newline at end of file diff --git a/src/app/platform_win32/win32_test_code.cpp b/src/app/platform_win32/win32_test_code.cpp new file mode 100644 index 0000000..8c23153 --- /dev/null +++ b/src/app/platform_win32/win32_test_code.cpp @@ -0,0 +1,86 @@ +// +// File: win32_test_code.cpp +// Author: Peter Slattery +// Creation Date: 2021-01-10 +// +#ifndef WIN32_TEST_CODE_CPP + +#if 0 +internal void +Win32_TestCode_UART(gs_thread_context ThreadContext) +{ + u32 LedCount = 48; + u32 MessageBaseSize = sizeof(uart_header) + sizeof(uart_channel) + sizeof(uart_footer); + MessageBaseSize += sizeof(u8) * 3 * LedCount; + gs_data MessageBuffer = PushSizeToData(ThreadContext.Transient); + + gs_memory_cursor WriteCursor = CreateMemoryCursor(MessageBuffer); + + uart_header* Header = PushStructOnCursor(WriteCursor, uart_header); + UART_FillHeader(Header, Strip.UARTAddr.Channel, UART_SET_CHANNEL_WS2812); + uart_channel* Channel = PushStructOnCursor(WriteCursor, uart_channel); + *Channel = ChannelSettings; + + for (u32 i = 0; i < LedCount; i++) + { + u8* OutputPixel = PushArrayOnCursor(WriteCursor, u8, 3); + OutputPixel[Channel->RedIndex] = (u8)(i); + OutputPixel[Channel->GreenIndex] = 0; + OutputPixel[Channel->BlueIndex] = 0; + } + + uart_footer* Footer = PushStructOnCursor(WriteCursor, uart_footer); + UART_FillFooter(Footer, (u8*)Header); +} +#endif + +win32_socket ListenSocket; +HANDLE ListenThread; + +DWORD WINAPI +Win32_TestCode_ListenThreadProc(LPVOID ThreadData) +{ + gs_thread_context Ctx = Win32CreateThreadContext(); + + temp_job_req* Req = (temp_job_req*)ThreadData; + + while (true) + { + Req->Proc(&Ctx, Req->Memory); + } +} + +internal void +Win32_TestCode_SocketReading(gs_thread_context ThreadContext, temp_job_req* Req) +{ + ListenSocket = Win32Socket_ConnectToAddress("127.0.0.1", "20185"); + u8* Arg = (u8*)Req; + ListenThread = CreateThread(0, 0, &Win32_TestCode_ListenThreadProc, Arg, 0, 0); +} + +internal void +BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) +{ + packet_ringbuffer* MicPacketBuffer = (packet_ringbuffer*)UserData; + + gs_data Data = Win32Socket_Receive(&ListenSocket, Ctx->Transient); + if (Data.Size > 0) + { + OutputDebugStringA("Listened"); + MicPacketBuffer->Values[MicPacketBuffer->WriteHead++] = Data; + if (MicPacketBuffer->WriteHead >= PACKETS_MAX) + { + MicPacketBuffer->WriteHead = 0; + } + } +} + +internal void +Win32_TestCode_SocketReading_Cleanup() +{ + TerminateThread(ListenThread, 0); + Win32Socket_Close(&ListenSocket); +} + +#define WIN32_TEST_CODE_CPP +#endif // WIN32_TEST_CODE_CPP \ No newline at end of file diff --git a/src/gs_libs/gs_font.h b/src/gs_libs/gs_font.h index c901bf7..e26a681 100644 --- a/src/gs_libs/gs_font.h +++ b/src/gs_libs/gs_font.h @@ -5,8 +5,6 @@ // #ifndef GS_FONT_H -#ifndef GS_FONT_H - struct codepoint_bitmap { s32 XOffset, YOffset; @@ -37,20 +35,6 @@ struct bitmap_font s32 BitmapTextureHandle; }; -internal bitmap_font -InitializeTextFont (s32 CodepointCount, u8* CodepointMemory, s32 CodepointMemorySize) -{ - bitmap_font Result = {}; - - Result.CodepointDictionarySize = CodepointCount; - Result.CodepointDictionaryCount = 0; - Assert(CodepointMemorySize >= (sizeof(char) + sizeof(codepoint_bitmap)) * CodepointCount); - Result.CodepointKeys = (char*)CodepointMemory; - Result.CodepointValues = (codepoint_bitmap*)(CodepointMemory + (sizeof(char) * CodepointCount)); - - return Result; -} - #define GLYPH_SKIRT 1 internal void GetNextCodepointOffset (bitmap_font* Font, u32* X, u32* Y) @@ -126,8 +110,58 @@ NewLineYOffset (bitmap_font Font) return Result; } -#define GS_FONT_H -#endif + +internal bitmap_font +TextFont_Create(gs_file FontFile, u32 BitmapDim, u32 FontSize, context Context, gs_memory_arena* Arena) +{ + bitmap_font Result = {}; + Assert(FileNoError(FontFile)); + + Result.BitmapWidth = BitmapDim; + Result.BitmapHeight = BitmapDim; + Result.BitmapBytesPerPixel = 4; + Result.BitmapStride = Result.BitmapWidth * Result.BitmapBytesPerPixel; + u32 BitmapSize = Result.BitmapWidth * Result.BitmapHeight * Result.BitmapBytesPerPixel; + Result.BitmapMemory = PushArray(Arena, u8, BitmapSize); + ZeroMemoryBlock(Result.BitmapMemory, BitmapSize); + + platform_font_info FontInfo = Context.PlatformGetFontInfo("Anonymous Pro", FontSize, FontWeight_Normal, false, false, false); + Result.PixelHeight = FontInfo.PixelHeight; + Result.Ascent = FontInfo.Ascent; + Result.Descent = FontInfo.Descent; + Result.Leading = FontInfo.Leading; + Result.MaxCharWidth = FontInfo.MaxCharWidth; + + Result.CodepointDictionarySize = (FontInfo.CodepointOnePastLast - FontInfo.CodepointStart); + Result.CodepointDictionaryCount = 0; + Result.CodepointKeys = PushArray(Arena, char, Result.CodepointDictionarySize); + Result.CodepointValues = PushArray(Arena, codepoint_bitmap, Result.CodepointDictionarySize); + + for (s32 Codepoint = FontInfo.CodepointStart; + Codepoint < FontInfo.CodepointOnePastLast; + Codepoint++) + { + + u32 CodepointX, CodepointY; + GetNextCodepointOffset(&Result, &CodepointX, &CodepointY); + + u32 CodepointW, CodepointH; + Context.PlatformDrawFontCodepoint( + Result.BitmapMemory, + Result.BitmapWidth, + Result.BitmapHeight, + CodepointX, CodepointY, + Codepoint, FontInfo, + &CodepointW, &CodepointH); + + AddCodepointToFont(&Result, Codepoint, 0, 0, CodepointW, CodepointH, CodepointX, CodepointY); + } + + Result.BitmapTextureHandle = Context.PlatformGetGPUTextureHandle(Result.BitmapMemory, + Result.BitmapWidth, Result.BitmapHeight); + + return Result; +} #define GS_FONT_H #endif // GS_FONT_H \ No newline at end of file diff --git a/src/gs_libs/gs_input.h b/src/gs_libs/gs_input.h index d4a83f0..4a696b3 100644 --- a/src/gs_libs/gs_input.h +++ b/src/gs_libs/gs_input.h @@ -15,32 +15,32 @@ enum key_code KeyCode_CapsLock, KeyCode_LeftShift, KeyCode_RightShift, KeyCode_LeftCtrl, KeyCode_RightCtrl, - KeyCode_Fn, - KeyCode_Alt, + 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, + 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_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_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_Num0, KeyCode_Num1, KeyCode_Num2, KeyCode_Num3, KeyCode_Num4, KeyCode_Num5, KeyCode_Num6, KeyCode_Num7, KeyCode_Num8, KeyCode_Num9, // Symbols @@ -48,7 +48,7 @@ enum key_code 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_ForwardSlash, KeyCode_Backslash, KeyCode_Pipe, KeyCode_Comma, KeyCode_Period, KeyCode_QuestionMark, KeyCode_LessThan, KeyCode_GreaterThan, KeyCode_Tilde, KeyCode_BackQuote, // Arrows @@ -77,9 +77,9 @@ CharacterFromKeyCode (key_code Code) 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_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; @@ -92,7 +92,7 @@ CharacterFromKeyCode (key_code Code) 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_p: { Result = 'p'; }break; case KeyCode_q: { Result = 'q'; }break; case KeyCode_r: { Result = 'r'; }break; case KeyCode_s: { Result = 's'; }break; @@ -100,7 +100,7 @@ CharacterFromKeyCode (key_code Code) 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_x: { Result = 'x'; }break; case KeyCode_y: { Result = 'y'; }break; case KeyCode_z: { Result = 'z'; }break; @@ -119,7 +119,7 @@ CharacterFromKeyCode (key_code Code) 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_P: { Result = 'P'; }break; case KeyCode_Q: { Result = 'Q'; }break; case KeyCode_R: { Result = 'R'; }break; case KeyCode_S: { Result = 'S'; }break; @@ -127,7 +127,7 @@ CharacterFromKeyCode (key_code Code) 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_X: { Result = 'X'; }break; case KeyCode_Y: { Result = 'Y'; }break; case KeyCode_Z: { Result = 'Z'; }break; @@ -148,7 +148,7 @@ CharacterFromKeyCode (key_code Code) 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_Num5: { Result = '5'; }break; case KeyCode_Num6: { Result = '6'; }break; case KeyCode_Num7: { Result = '7'; }break; case KeyCode_Num8: { Result = '8'; }break; @@ -181,7 +181,7 @@ CharacterFromKeyCode (key_code Code) case KeyCode_Backslash: { Result = '\\'; }break; case KeyCode_Pipe: { Result = '|'; }break; case KeyCode_Comma: { Result = ','; }break; - case KeyCode_Period: { Result = '.'; }break; + case KeyCode_Period: { Result = '.'; }break; case KeyCode_QuestionMark: { Result = '?'; }break; case KeyCode_LessThan: { Result = '<'; }break; case KeyCode_GreaterThan: { Result = '>'; }break; @@ -227,6 +227,11 @@ enum key_state_flags #define KeyWasDown(event) ((event & KeyState_WasDown) > 0) #define KeyIsDown(event) ((event & KeyState_IsDown) > 0) +#define SetKeyDown(key) (key |= KeyState_IsDown) +#define SetKeyWasDown(key) (key |= KeyState_WasDown) +#define SetKeyUp(key) (key &= ~KeyState_IsDown) +#define SetKeyWasUp(key) (key &= ~KeyState_WasDown) + struct input_entry { key_code Key; @@ -246,10 +251,10 @@ enum cursor_type CursorType_Arrow, CursorType_Pointer, CursorType_Loading, - CursorType_HorizontalArrows, - CursorType_VerticalArrows, - CursorType_DiagonalTopLeftArrows, - CursorType_DiagonalTopRightArrows, + CursorType_HArrows, + CursorType_VArrows, + CursorType_DTopLeftArrows, + CursorType_DTopRightArrows, CursorType_Count, }; @@ -266,11 +271,13 @@ struct mouse_state b32 MiddleButtonState; b32 RightButtonState; + + cursor_type CursorType; }; internal input_queue -InitializeInputQueue (u8* Memory, s32 MemorySize) +InputQueue_Create (u8* Memory, s32 MemorySize) { input_queue Result = {}; s32 EntriesCount = MemorySize / sizeof(input_entry); @@ -279,6 +286,18 @@ InitializeInputQueue (u8* Memory, s32 MemorySize) 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; +} internal void ResetInputQueue (input_queue* Queue) @@ -327,7 +346,7 @@ KeyTransitionedUp (input Input, key_code Key) } internal void -AddInputEventEntry (input_queue* Queue, key_code Key, +AddInputEventEntry (input_queue* Queue, key_code Key, b32 WasDown, b32 IsDown, b32 ShiftDown, b32 AltDown, b32 CtrlDown, b32 SysDown) { Assert(Queue->QueueUsed < Queue->QueueSize); @@ -392,11 +411,259 @@ GetMouseButtonStateAdvanced (b32 ButtonState) !((ButtonState & KeyState_IsDown) > 0)) { Result= 0; - } - else if (ButtonState & KeyState_IsDown) - { - Result |= KeyState_WasDown; } + else if (ButtonState & KeyState_IsDown) + { + Result |= KeyState_WasDown; + } + return Result; +} + +internal char +KeyCodeToChar(key_code Code) +{ + 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; + + 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; + } + + return Result; +} + +internal bool +KeyCodeHasChar(key_code Code) +{ + bool Result = KeyCodeToChar(Code) != 0; + return Result; +} + + +internal key_code +CharToKeyCode(char C) +{ + 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; + + 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; + } + return Result; } diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index 05467cc..197c270 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -441,6 +441,21 @@ SqrtU32(u32 V) return sqrt(V); } +internal r32 +ModR32(r32 Value, r32 Int) +{ + 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; +} + internal r32 SinR32(r32 Rad) { @@ -670,34 +685,34 @@ V4Cross(v4 A, v4 B) } internal v2 -V2Lerp(v2 A, v2 B, r32 T) +V2Lerp(r32 T, v2 A, v2 B) { v2 Result = v2{ - LerpR32(A.x, B.x, T), - LerpR32(A.y, B.y, T), + LerpR32(T, A.x, B.x), + LerpR32(T, A.y, B.y), }; return Result; } internal v3 -V3Lerp(v3 A, v3 B, r32 T) +V3Lerp(r32 T, v3 A, v3 B) { v3 Result = v3{ - LerpR32(A.x, B.x, T), - LerpR32(A.y, B.y, T), - LerpR32(A.z, B.z, T), + LerpR32(T, A.x, B.x), + LerpR32(T, A.y, B.y), + LerpR32(T, A.z, B.z), }; return Result; } internal v4 -V4Lerp(v4 A, v4 B, r32 T) +V4Lerp(r32 T, v4 A, v4 B) { v4 Result = v4{ - LerpR32(A.x, B.x, T), - LerpR32(A.y, B.y, T), - LerpR32(A.z, B.z, T), - LerpR32(A.w, B.w, T), + 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; } @@ -927,6 +942,10 @@ Range2Union(range2 A, range2 B) 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 @@ -949,6 +968,41 @@ Rect2GetRectLocalPoint(rect2 Rect, v2 Point) return Result; } +internal r32 +Rect2Area(rect2 Rect) +{ + r32 Result = Rect2Width(Rect) * Rect2Height(Rect); + return Result; +} + +internal v2 +Rect2BottomLeft(rect2 Rect) +{ + v2 Result = Rect.Min; + return Result; +} + +internal v2 +Rect2BottomRight(rect2 Rect) +{ + v2 Result = v2{ Rect.Max.x, Rect.Min.y }; + return Result; +} + +internal v2 +Rect2TopRight(rect2 Rect) +{ + v2 Result = Rect.Max; + return Result; +} + +internal v2 +Rect2TopLeft(rect2 Rect) +{ + v2 Result = v2{ Rect.Min.x, Rect.Max.y }; + return Result; +} + /////////////////////////// // // Ray @@ -1386,7 +1440,12 @@ CharArrayLength (char* CS) internal bool IsNullTerminated(gs_const_string String) { - return (String.Str[String.Length] == 0); + bool Result = false; + if (String.Str) + { + Result = (String.Str[String.Length] == 0); + } + return Result; } internal bool IsNullTerminated(gs_string String) @@ -1514,12 +1573,12 @@ FindFirstFromSet(gs_const_string String, char* SetArray) return Result; } -internal u64 +internal s64 FindLastFromSet(gs_const_string String, char* SetArray) { gs_const_string Set = ConstString(SetArray); - u64 Result = String.Length - 1; - for(s64 At = Result; At >= 0; At--) + 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++) @@ -1538,6 +1597,21 @@ FindLastFromSet(gs_const_string String, char* SetArray) return Result; } +internal bool +StringContains(gs_const_string Str, char C) +{ + bool Result = false; + for (u32 i = 0; i < Str.Length; i++) + { + if (Str.Str[i] == C) + { + Result = true; + break; + } + } + return Result; +} + internal bool StringsEqualUpToLength(gs_const_string A, gs_const_string B, u64 Length) { @@ -1641,30 +1715,66 @@ CharToUInt(char C, u64 Base) return CharToUInt(C, GetCharSetForBase(Base)); } -internal u64 -ParseUInt(gs_const_string String, u64 Base = 10, u64* ParsedLength = 0) +struct parse_uint_result { - u64 Result = 0; + 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); - u64 i = 0; - for (; i < String.Length; i++) + + bool StringIsValid = true; + for (u32 i = 0; i < String.Length; i++) { - u64 CharIndex = FindFirst(CharSet, String.Str[i]); - if (CharIndex < CharSet.Length) - { - Result = CharToUInt(String.Str[i], CharSet) + (Result * Base); - } - else + if (!StringContains(CharSet, String.Str[i])) { + StringIsValid = false; break; } } - if (ParsedLength != 0) + + if (StringIsValid) { - *ParsedLength = i; + 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; } + +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; +} internal u64 ParseUInt(u64 Length, char* String, u64 Base = 10, u64* ParsedLength = 0) { @@ -1702,39 +1812,75 @@ ParseInt(char* String, u64 Base = 10, u64* ParsedLength = 0) return ParseInt(LitString(String), Base, ParsedLength); } +struct parse_float_result +{ + 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++) + { + if (!IsNumericDecimal(String.Str[i]) && String.Str[i] != '-') + { + StringIsValid = false; + break; + } + } + + if (StringIsValid) + { + u64 DecimalIndex = FindFirst(String, '.'); + u64 TempParsedLength = 0; + u64 PlacesAfterPoint = 0; + + gs_const_string IntegerString = GetStringBefore(String, DecimalIndex); + gs_const_string 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 < String.Length) { Result.ParsedLength += 1; } + + Result.Success = true; + } + + return Result; +} + internal r64 ParseFloat(gs_const_string String, u64* ParsedLength = 0) { - - u64 DecimalIndex = FindFirst(String, '.'); - u64 TempParsedLength = 0; - u64 PlacesAfterPoint = 0; - - gs_const_string IntegerString = GetStringBefore(String, DecimalIndex); - gs_const_string DecimalString = GetStringAfter(String, DecimalIndex + 1); - - r32 Polarity = 1; - if (IntegerString.Str[0] == '-') - { - IntegerString = GetStringAfter(IntegerString, 1); - Polarity = -1; - } - r64 Result = (r64)ParseInt(IntegerString, 10, &TempParsedLength); - - if (TempParsedLength == IntegerString.Length) - { - r64 AfterPoint = (r64)ParseUInt(DecimalString, 10, &PlacesAfterPoint); - r64 Decimal = (AfterPoint / PowR64(10, PlacesAfterPoint)); - Result = Result + Decimal; - Result *= Polarity; - } - + parse_float_result Result = ValidateAndParseFloat(String); + Assert(Result.Success); if (ParsedLength != 0) { - *ParsedLength = TempParsedLength + PlacesAfterPoint; - if (DecimalIndex < String.Length) { *ParsedLength += 1; } + *ParsedLength = Result.ParsedLength; } - return Result; + return Result.Value; } internal r64 ParseFloat(char* String, u64* ParsedLength = 0) @@ -1956,11 +2102,16 @@ PrintFArgsList (gs_string* String, char* Format, va_list Args) 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; - AssertMessage("ParseInt assumes whole string is an integer"); - Precision = (s32)ParseInt(FormatAt, 10, &Parsed); + Precision = (s32)ParseInt(PrecisionStr, 10, &Parsed); FormatAt += Parsed; } else if (FormatAt[0] == '*') @@ -1979,31 +2130,31 @@ PrintFArgsList (gs_string* String, char* Format, va_list Args) if (FormatAt[0] == 'h' && FormatAt[1] == 'h') { LengthSpecified = true; - LengthSpecified = 1; + Length = 1; FormatAt += 2; } else if (FormatAt[0] == 'h') { LengthSpecified = true; - LengthSpecified = 2; + Length = 2; FormatAt++; } else if (FormatAt[0] == 'l' && FormatAt[1] == 'l') { LengthSpecified = true; - LengthSpecified = 8; + Length = 8; FormatAt += 2; } else if (FormatAt[0] == 'l') { LengthSpecified = true; - LengthSpecified = 4; + Length = 4; FormatAt++; } else if (FormatAt[0] == 'j') { LengthSpecified = true; - LengthSpecified = 8; + Length = 8; FormatAt++; } else if (FormatAt[0] == 'z') @@ -2034,7 +2185,7 @@ PrintFArgsList (gs_string* String, char* Format, va_list Args) } else if (FormatAt[0] == 'u') { - u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + u64 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); U64ToASCII(&StringRemaining, UnsignedInt, 10, Base10Chars); } else if (FormatAt[0] == 'o') @@ -2475,6 +2626,19 @@ PushStringF(gs_memory_arena* Arena, u32 MaxLength, char* Format, ...) 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) { @@ -3107,10 +3271,299 @@ TimeHandlerGetSecondsElapsed(gs_time_handler TimeHandler, s64 StartCycles, s64 E return Result; } +////////////////////////// +// +// Thread Manager + +CREATE_THREAD(CreateThreadStub) +{ + return {}; +} + + +KILL_THREAD(KillThreadStub) +{ + 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; +} + +internal platform_thread_handle +CreateThread(platform_thread_manager* Manager, thread_proc_* Proc, u8* Arg) +{ + platform_thread_handle Result = {}; + + for (u32 i = 1; i < THREADS_MAX; i++) + { + if (!Manager->ThreadsUsed[i]) + { + Result.Index = i; + break; + } + } + Assert(Result.Index != 0); + + Manager->ThreadsUsed[Result.Index] = true; + Manager->CreateThreadProc(&Manager->Threads[Result.Index], Proc, Arg); + + 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; +} + + +////////////////////////// +// +// Socket Manager + +CONNECT_SOCKET(PlatformConnectSocket_Stub) +{ + return false; +} + +CLOSE_SOCKET(PlatformCloseSocket_Stub) +{ + return false; +} + +SOCKET_QUERY_STATUS(PlatformSocketQueryStatus_Stub) +{ + return false; +} + +SOCKET_PEEK(PlatformSocketPeek_Stub) +{ + return 0; +} + +SOCKET_RECEIVE(PlatformSocketRecieve_Stub) +{ + return {}; +} + +SOCKET_SEND(PlatformSocketSend_Stub) +{ + return false; +} + +internal platform_socket_manager +CreatePlatformSocketManager(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 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 platform_socket* +SocketManagerGetSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle) +{ + platform_socket* Result = 0; + if (Manager->SocketsUsed[Handle.Index]) + { + platform_socket* Socket = &Manager->Sockets[Handle.Index]; + if (Socket->PlatformHandle != 0) + { + Result = Socket; + } + } + 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(Socket); + } + 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++) + { + if (!Manager->SocketsUsed[i]) + { + Result.Index = i; + Manager->SocketsUsed[i] = true; + break; + } + } + + Assert(Result.Index != 0); + platform_socket* Socket = &Manager->Sockets[Result.Index]; + CopyArray(Addr, Socket->Addr, char, CStringLength(Addr) + 1); + CopyArray(Port, Socket->Port, char, CStringLength(Port) + 1); + + bool Success = Manager->ConnectSocketProc(Socket); + Assert(Success); + + 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; +} + +// 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) +{ + bool Result = false; + platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle); + if (Socket) + { + Result = Manager->SocketQueryStatusProc(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(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(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(Socket, Address, Port, Data, Flags); + Result = (SizeSent == Data.Size); + } + return Result; +} + /////////////////////////// // // Hashes +internal u32 +HashAppendDJB2ToU32(u32 Hash, u8 Byte) +{ + 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; +} + internal u32 HashDJB2ToU32(char* String) { diff --git a/src/gs_libs/gs_types.h b/src/gs_libs/gs_types.h index e647f5c..e6ab857 100644 --- a/src/gs_libs/gs_types.h +++ b/src/gs_libs/gs_types.h @@ -9,6 +9,7 @@ # 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(_MSC_VER) @@ -574,7 +575,8 @@ CStringLength(char* Str) { char* At = Str; while (*At) { At++; } - return PointerDifference(At, Str); + u64 Result = PointerDifference(At, Str); + return Result; } #define StringExpand(str) (int)(str).Length, (str).Str @@ -1004,16 +1006,52 @@ struct gs_thread_context gs_memory_arena* Transient; }; -// Threads & Work Queue +// Threads + +typedef struct platform_thread_handle +{ + u32 Index; +} platform_thread_handle; + +typedef struct platform_thread_manager platform_thread_manager; + +#define THREAD_PROC_(name) void name(gs_thread_context* Ctx, u8* UserData) +typedef THREAD_PROC_(thread_proc_); + +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) +typedef CREATE_THREAD(platform_create_thread); + +#define KILL_THREAD(name) bool name(platform_thread* Thread) +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; +} platform_thread_manager; + +// Work Queue typedef struct gs_work_queue gs_work_queue; -struct gs_worker_thread +typedef struct gs_worker_thread { 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) typedef THREAD_PROC(thread_proc); @@ -1047,8 +1085,57 @@ struct gs_work_queue // Work Queue push_work_on_queue* PushWorkOnQueue; complete_queue_work* CompleteQueueWork; - reset_work_queue* ResetWorkQueue; }; +// Sockets + +typedef struct platform_socket_handle_ +{ + u32 Index; +} platform_socket_handle_; + +typedef struct platform_socket +{ + char Addr[128]; + char Port[32]; + u8* PlatformHandle; +} platform_socket; + +#define CONNECT_SOCKET(name) bool name(platform_socket* Socket) +typedef CONNECT_SOCKET(platform_connect_socket); + +#define CLOSE_SOCKET(name) bool name(platform_socket* Socket) +typedef CLOSE_SOCKET(platform_close_socket); + +#define SOCKET_QUERY_STATUS(name) bool name(platform_socket* Socket) +typedef SOCKET_QUERY_STATUS(platform_socket_query_status); + +#define SOCKET_PEEK(name) u32 name(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) +typedef SOCKET_RECEIVE(platform_socket_receive); + +#define SOCKET_SEND(name) s32 name(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags) +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; +} platform_socket_manager; + #define GS_TYPES_H #endif // GS_TYPES_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 new file mode 100644 index 0000000..dce5c4a --- /dev/null +++ b/src/sculpture_gen/gen_blumen_lumen.cpp @@ -0,0 +1,227 @@ +// +// File: gen_blumen_lumen.cpp +// Author: Peter Slattery +// Creation Date: 2021-01-06 +// +#ifndef GEN_BLUMEN_LUMEN_CPP + +#include +#include + +#include "../gs_libs/gs_types.h" +#include "../gs_libs/gs_types.cpp" +#include "../app/platform_win32/win32_foldhaus_utils.h" +#include "../app/platform_win32/win32_foldhaus_memory.h" +#include "../app/platform_win32/win32_foldhaus_fileio.h" +#include "../app/platform_win32/win32_foldhaus_work_queue.h" + +#include "sculpture_gen.h" + +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; +} 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; + + 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); + } + +} + +typedef struct +{ + 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} + Desc.Pos; + BloomStemInner.CenterEnd = v3{0, .9f, 0} + Desc.Pos; + 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} + Desc.Pos; + BloomStemOuter.CenterEnd = v3{0, .9f, 0} + Desc.Pos; + 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} + Desc.Pos; + FlowerStem.CenterEnd = v3{0, .5f, 0} + Desc.Pos; + 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; +} + +// Just for brevity, no real function provided +#define FSC(f,c) FlowerStripToChannel((f), (c)) +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; +} + +int main(int ArgCount, char** Args) +{ + gs_thread_context Ctx = Win32CreateThreadContext(); + + gs_string OutputBuffer = PushString(Ctx.Transient, MB(4)); + + WriteAssemblyUARTOpen(&OutputBuffer, + "Blumen Lumen - Silver Spring", + 100, + v3{0, 0, 0}, + 63, + ""); + + 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{-1, 0, 0}; + F0.ComPort = "\\\\.\\COM4"; + F0.FlowerTagValue = "left"; + F0.StemChannels = StemChannels; + F0.BloomOuterChannels = BloomOuterChannels; + F0.BloomInnerChannels = BloomInnerChannels; + StripCount += BuildFlower(&OutputBuffer, F0); + + flower_desc F1 = {}; + F1.Pos = v3{0, 0, 0}; + F1.ComPort = "\\\\.\\COM5"; + F1.FlowerTagValue = "center"; + F1.StemChannels = StemChannels; + F1.BloomInnerChannels = BloomInnerChannels; + F1.BloomOuterChannels = BloomOuterChannels; + StripCount += BuildFlower(&OutputBuffer, F1); + + flower_desc F2 = {}; + F2.Pos = v3{1, 0, 0}; + F2.ComPort = "\\\\.\\COM6"; + F2.FlowerTagValue = "right"; + F2.StemChannels = StemChannels; + F2.BloomInnerChannels = BloomInnerChannels; + F2.BloomOuterChannels = BloomOuterChannels; + StripCount += BuildFlower(&OutputBuffer, F2); + + printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str); + //printf("%d\n", StripCount); + + return 0; +} + + +#define GEN_BLUMEN_LUMEN_CPP +#endif // GEN_BLUMEN_LUMEN_CPP \ No newline at end of file diff --git a/src/sculpture_gen/sculpture_gen.h b/src/sculpture_gen/sculpture_gen.h new file mode 100644 index 0000000..bbba18c --- /dev/null +++ b/src/sculpture_gen/sculpture_gen.h @@ -0,0 +1,106 @@ +// +// File: sculpture_gen.h +// Author: Peter Slattery +// Creation Date: 2021-01-06 +// +#ifndef SCULPTURE_GEN_H + +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); +} + +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); + } +} + +internal void +WriteLedStripOpen(gs_string* Buffer, u32 Channel, char* ComPort) +{ + 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"); +} + +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); +} + +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"); +} + +internal void +WriteSegmentSequenceClose(gs_string* Buffer) +{ + WriteIndented(Buffer, 2, "};\n"); + WriteIndented(Buffer, 1, "};\n"); +} + +internal void +WriteSegmentTagsOpen(gs_string* Buffer, u32 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"); + +} + +internal void +WriteSegmentTagsClose(gs_string* Buffer) +{ +} + +internal void +WriteLedStripClose(gs_string* Buffer) +{ + WriteIndented(Buffer, 0, "};\n"); +} + +#define SCULPTURE_GEN_H +#endif // SCULPTURE_GEN_H \ No newline at end of file diff --git a/src/todo.txt b/src/todo.txt index 3dcb1f0..c2ef7b1 100644 --- a/src/todo.txt +++ b/src/todo.txt @@ -43,7 +43,6 @@ STREAM #1: 3D Overhaul - :ErrorLogging - Animation System - - blending between animation - layers - layer masks by sculpture - layer masks by tag / value diff --git a/src/todo_done.txt b/src/todo_done.txt index 6aae2d4..ac67406 100644 --- a/src/todo_done.txt +++ b/src/todo_done.txt @@ -1,3 +1,5 @@ +x blending between animation + x saving animation timelines x Buckets & Lists x On second thought, I just really don't like these. Lets get rid of them, and put custom structures in place diff --git a/test_motor_header.h b/test_motor_header.h new file mode 100644 index 0000000..3a443b3 --- /dev/null +++ b/test_motor_header.h @@ -0,0 +1,19 @@ +// +// 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 new file mode 100644 index 0000000..a046c21 --- /dev/null +++ b/test_sound_interface_packet.h @@ -0,0 +1,29 @@ +// +// 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