From 708ac91afe990eed36b5034a95c8027758251191 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 14 Nov 2020 12:19:36 -0800 Subject: [PATCH] pulled animation update and render functions out of foldhaus_app.cpp --- src/app/engine/animation/foldhaus_animation.h | 40 +++++- .../animation/foldhaus_animation_renderer.cpp | 126 ++++++++++++++++++ src/app/foldhaus_app.cpp | 105 ++------------- src/app/foldhaus_app.h | 1 + src/app/platform_win32/win32_foldhaus.cpp | 9 ++ 5 files changed, 183 insertions(+), 98 deletions(-) create mode 100644 src/app/engine/animation/foldhaus_animation_renderer.cpp diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index bbdb1a5..80117db 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -14,6 +14,10 @@ struct frame_range s32 Max; }; +// 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 +// will run struct animation_block { frame_range Range; @@ -37,6 +41,8 @@ enum blend_mode BlendMode_Count, }; +typedef pixel led_blend_proc(pixel PixelA, pixel PixelB); + global gs_const_string BlendModeStrings[] = { ConstString("Overwrite"), ConstString("Add"), @@ -57,11 +63,15 @@ struct anim_layer_array u32 CountMax; }; +// NOTE(pjs): An animation is a stack of layers, each of which +// is a timeline of animation blocks. struct animation { gs_string Name; anim_layer_array Layers; + // TODO(pjs): Pretty sure Blocks_ should be obsolete and + // Layers should contain their own blocks animation_block_array Blocks_; frame_range PlayableRange; @@ -74,6 +84,8 @@ struct animation_array u32 CountMax; }; +// NOTE(pjs): This is an evaluated frame - across all layers in an +// animation, these are the blocks that need to be run struct animation_frame { // NOTE(pjs): These are all parallel arrays of equal length @@ -102,7 +114,9 @@ struct animation_system }; -// TODO(pjs): Better name - something like animation_prototype +// NOTE(pjs): A Pattern is a named procedure which can be used as +// an element of an animation. Patterns are sequenced on a timeline +// and blended via layers to create an animation struct animation_pattern { char* Name; @@ -431,5 +445,29 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren return Result; } +internal void +AnimationSystem_Update(animation_system* System) +{ + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); + if (System->TimelineShouldAdvance) { + // TODO(Peter): Revisit this. This implies that the framerate of the animation system + // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure + System->CurrentFrame += 1; + + // Loop back to the beginning + if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) + { + System->CurrentFrame = 0; + } + } +} + +inline bool +AnimationSystem_NeedsRender(animation_system System) +{ + bool Result = (System.CurrentFrame != System.LastUpdatedFrame); + return Result; +} + #define FOLDHAUS_ANIMATION #endif // FOLDHAUS_ANIMATION \ No newline at end of file diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp new file mode 100644 index 0000000..dfd8ab6 --- /dev/null +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -0,0 +1,126 @@ +// +// File: foldhaus_animation_renderer.cpp +// Author: Peter Slattery +// Creation Date: 2020-11-14 +// +#ifndef FOLDHAUS_ANIMATION_RENDERER_CPP + +internal pixel +LedBlend_Overwrite(pixel PixelA, pixel PixelB) +{ + return PixelB; +} + +internal pixel +LedBlend_Add(pixel PixelA, pixel PixelB) +{ + pixel Result = {}; + + u32 R = (u32)PixelA.R + (u32)PixelB.R; + u32 G = (u32)PixelA.G + (u32)PixelB.G; + u32 B = (u32)PixelA.B + (u32)PixelB.B; + + Result.R = (u8)Min(R, (u32)255); + Result.G = (u8)Min(G, (u32)255); + Result.B = (u8)Min(B, (u32)255); + + return Result; +} + +internal pixel +LedBlend_Multiply(pixel PixelA, pixel PixelB) +{ + pixel Result = {}; + + r32 DR = (r32)PixelA.R / 255.f; + r32 DG = (r32)PixelA.G / 255.f; + r32 DB = (r32)PixelA.B / 255.f; + + r32 SR = (r32)PixelB.R / 255.f; + r32 SG = (r32)PixelB.G / 255.f; + r32 SB = (r32)PixelB.B / 255.f; + + Result.R = (u8)((DR * SR) * 255.f); + Result.G = (u8)((DG * SG) * 255.f); + Result.B = (u8)((DB * SB) * 255.f); + + return Result; +} + +internal pixel +LedBlend_Overlay(pixel PixelA, pixel PixelB) +{ + pixel Result = {}; + return Result; +} + +internal led_blend_proc* +LedBlend_GetProc(blend_mode BlendMode) +{ + led_blend_proc* Result = 0; + switch (BlendMode) + { + case BlendMode_Overwrite: { Result = LedBlend_Overwrite; }break; + case BlendMode_Add: { Result = LedBlend_Add; }break; + case BlendMode_Multiply: { Result = LedBlend_Multiply; }break; + InvalidDefaultCase; + } + return Result; +} + +internal void +AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Assemblies, + led_system* LedSystem, + animation_pattern* Patterns, + gs_memory_arena* Transient) +{ + s32 CurrentFrame = System->CurrentFrame; + r32 FrameTime = CurrentFrame * System->SecondsPerFrame; + + animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, Transient); + + led_buffer* LayerLEDBuffers = PushArray(Transient, led_buffer, CurrFrame.BlocksCountMax); + for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) + { + assembly* Assembly = &Assemblies.Values[AssemblyIndex]; + led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex); + + for (u32 Layer = 0; Layer < CurrFrame.BlocksCountMax; Layer++) + { + if (!CurrFrame.BlocksFilled[Layer]) { continue; } + animation_block Block = CurrFrame.Blocks[Layer]; + + // Prep Temp Buffer + LayerLEDBuffers[Layer] = *AssemblyLedBuffer; + LayerLEDBuffers[Layer].Colors = PushArray(Transient, pixel, AssemblyLedBuffer->LedCount); + + u32 FramesIntoBlock = CurrentFrame - Block.Range.Min; + r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame; + + // :AnimProcHandle + u32 AnimationProcIndex = Block.AnimationProcHandle - 1; + animation_proc* AnimationProc = Patterns[AnimationProcIndex].Proc; + AnimationProc(&LayerLEDBuffers[Layer], *Assembly, SecondsIntoBlock, Transient); + } + + // Consolidate Temp Buffers + // We do this in reverse order so that they go from top to bottom + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); + for (u32 Layer = 0; Layer < CurrFrame.BlocksCountMax; Layer++) + { + if (!CurrFrame.BlocksFilled[Layer]) { continue; } + + led_blend_proc* Blend = LedBlend_GetProc(ActiveAnim->Layers.Values[Layer].BlendMode); + Assert(Blend != 0); + for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) + { + pixel A = AssemblyLedBuffer->Colors[LED]; + pixel B = LayerLEDBuffers[Layer].Colors[LED]; + AssemblyLedBuffer->Colors[LED] = Blend(A, B); + } + } + } +} + +#define FOLDHAUS_ANIMATION_RENDERER_CPP +#endif // FOLDHAUS_ANIMATION_RENDERER_CPP \ No newline at end of file diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 8eee729..68bd4e2 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -168,104 +168,15 @@ UPDATE_AND_RENDER(UpdateAndRender) Editor_Update(State, Context, InputQueue); + AnimationSystem_Update(&State->AnimationSystem); + if (AnimationSystem_NeedsRender(State->AnimationSystem)) { - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - if (State->AnimationSystem.TimelineShouldAdvance) { - // TODO(Peter): Revisit this. This implies that the framerate of the animation system - // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure - State->AnimationSystem.CurrentFrame += 1; - - // Loop back to the beginning - if (State->AnimationSystem.CurrentFrame > ActiveAnim->PlayableRange.Max) - { - State->AnimationSystem.CurrentFrame = 0; - } - } - } - - s32 CurrentFrame = State->AnimationSystem.CurrentFrame; - if (CurrentFrame != State->AnimationSystem.LastUpdatedFrame) - { - State->AnimationSystem.LastUpdatedFrame = CurrentFrame; - r32 FrameTime = CurrentFrame * State->AnimationSystem.SecondsPerFrame; - - animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(&State->AnimationSystem, State->Transient); - - led_buffer* LayerLEDBuffers = PushArray(State->Transient, led_buffer, CurrFrame.BlocksCountMax); - for (u32 AssemblyIndex = 0; AssemblyIndex < State->Assemblies.Count; AssemblyIndex++) - { - assembly* Assembly = &State->Assemblies.Values[AssemblyIndex]; - led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(&State->LedSystem, Assembly->LedBufferIndex); - - for (u32 Layer = 0; Layer < CurrFrame.BlocksCountMax; Layer++) - { - if (!CurrFrame.BlocksFilled[Layer]) { continue; } - animation_block Block = CurrFrame.Blocks[Layer]; - - // Prep Temp Buffer - LayerLEDBuffers[Layer] = *AssemblyLedBuffer; - LayerLEDBuffers[Layer].Colors = PushArray(State->Transient, pixel, AssemblyLedBuffer->LedCount); - - u32 FramesIntoBlock = CurrentFrame - Block.Range.Min; - r32 SecondsIntoBlock = FramesIntoBlock * State->AnimationSystem.SecondsPerFrame; - - // :AnimProcHandle - u32 AnimationProcIndex = Block.AnimationProcHandle - 1; - animation_proc* AnimationProc = GlobalAnimationPatterns[AnimationProcIndex].Proc; - AnimationProc(&LayerLEDBuffers[Layer], *Assembly, SecondsIntoBlock, State->Transient); - } - - // Consolidate Temp Buffers - // We do this in reverse order so that they go from top to bottom - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - for (u32 Layer = 0; Layer < CurrFrame.BlocksCountMax; Layer++) - { - if (!CurrFrame.BlocksFilled[Layer]) { continue; } - - switch (ActiveAnim->Layers.Values[Layer].BlendMode) - { - case BlendMode_Overwrite: - { - for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) - { - AssemblyLedBuffer->Colors[LED] = LayerLEDBuffers[Layer].Colors[LED]; - } - }break; - - case BlendMode_Add: - { - for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) - { - u32 R = (u32)AssemblyLedBuffer->Colors[LED].R + (u32)LayerLEDBuffers[Layer].Colors[LED].R; - u32 G = (u32)AssemblyLedBuffer->Colors[LED].G + (u32)LayerLEDBuffers[Layer].Colors[LED].G; - u32 B = (u32)AssemblyLedBuffer->Colors[LED].B + (u32)LayerLEDBuffers[Layer].Colors[LED].B; - - AssemblyLedBuffer->Colors[LED].R = (u8)Min(R, (u32)255); - AssemblyLedBuffer->Colors[LED].G = (u8)Min(G, (u32)255); - AssemblyLedBuffer->Colors[LED].B = (u8)Min(B, (u32)255); - } - }break; - - case BlendMode_Multiply: - { - for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) - { - r32 DR = (r32)AssemblyLedBuffer->Colors[LED].R / 255.f; - r32 DG = (r32)AssemblyLedBuffer->Colors[LED].G / 255.f; - r32 DB = (r32)AssemblyLedBuffer->Colors[LED].B / 255.f; - - r32 SR = (r32)LayerLEDBuffers[Layer].Colors[LED].R / 255.f; - r32 SG = (r32)LayerLEDBuffers[Layer].Colors[LED].G / 255.f; - r32 SB = (r32)LayerLEDBuffers[Layer].Colors[LED].B / 255.f; - - AssemblyLedBuffer->Colors[LED].R = (u8)((DR * SR) * 255.f); - AssemblyLedBuffer->Colors[LED].G = (u8)((DG * SG) * 255.f); - AssemblyLedBuffer->Colors[LED].B = (u8)((DB * SB) * 255.f); - } - }break; - } - } - } + State->AnimationSystem.LastUpdatedFrame = State->AnimationSystem.CurrentFrame; + AnimationSystem_RenderToLedBuffers(&State->AnimationSystem, + State->Assemblies, + &State->LedSystem, + GlobalAnimationPatterns, + State->Transient); } { diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index 83e1de1..3939714 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -38,6 +38,7 @@ typedef struct panel panel; #include "engine/animation/foldhaus_animation.h" #include "engine/animation/foldhaus_animation_serializer.cpp" +#include "engine/animation/foldhaus_animation_renderer.cpp" struct app_state { diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index dda8da8..f3d12c5 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -454,6 +454,10 @@ 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; @@ -580,6 +584,10 @@ WinMain ( WSAStartup(MAKEWORD(2, 2), &WSAData); Win32Sockets = Win32SocketArray_Create(16, &PlatformPermanent); +#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; @@ -593,6 +601,7 @@ WinMain ( } ClearArena(ThreadContext.Transient); } +#endif Win32SerialArray_Create(ThreadContext);