diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 85173d5..07ec227 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -88,8 +88,12 @@ struct animation_array struct animation_layer_frame { animation_block Hot; + bool HasHot; + animation_block NextHot; bool HasNextHot; + + r32 HotOpacity; }; // NOTE(pjs): This is an evaluated frame - across all layers in an @@ -97,9 +101,7 @@ struct animation_layer_frame struct animation_frame { animation_layer_frame* Layers; - b8* LayersFilled; u32 LayersCount; - u32 LayersCountMax; // NOTE(pjs): These are all parallel arrays of equal length animation_block* Blocks; @@ -438,6 +440,10 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); animation_frame Result = {0}; + Result.LayersCount = ActiveAnim->Layers.Count; + Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount); + ZeroArray(Result.Layers, animation_frame, Result.LayersCount); + Result.BlocksCountMax = ActiveAnim->Layers.Count; Result.Blocks = PushArray(Arena, animation_block, Result.BlocksCountMax); Result.BlocksFilled = PushArray(Arena, b8, Result.BlocksCountMax); @@ -449,6 +455,39 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren if (FrameIsInRange(Block.Range, System->CurrentFrame)) { + animation_layer_frame* Layer = Result.Layers + Block.Layer; + if (Layer->HasHot) + { + // NOTE(pjs): With current implementation, we don't allow + // animations to hvae more than 2 concurrent blocks in the + // timeline + Assert(!Layer->HasNextHot); + + // NOTE(pjs): Make sure that Hot comes before NextHot + if (Layer->Hot.Range.Min < Block.Range.Min) + { + Layer->NextHot = Block; + } + else + { + Layer->NextHot = Layer->Hot; + Layer->Hot = Block; + } + Layer->HasNextHot = true; + + frame_range BlendRange = {}; + BlendRange.Min = Layer->NextHot.Range.Min; + BlendRange.Max = Layer->Hot.Range.Max; + Layer->HotOpacity = 1.0f - FrameToPercentRange(System->CurrentFrame, BlendRange); + } + else + { + Layer->Hot = Block; + Layer->HotOpacity = 1.0f; + Layer->HasHot = true; + } + + // TODO(pjs): Get rid of these fields, they're redundant now Result.BlocksFilled[Block.Layer] = true; Result.Blocks[Block.Layer] = Block; Result.BlocksCount++; diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index 7839da6..1ff7783 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -11,6 +11,18 @@ LedBlend_Overwrite(pixel PixelA, pixel PixelB) return PixelB; } +internal pixel +LedBlend_Overwrite(pixel PixelA, pixel PixelB, r32 Opacity) +{ + pixel Result = {}; + + r32 BOpacity = 1.0f - Opacity; + Result.R = (u8)((PixelA.R * Opacity) + (PixelB.R * BOpacity)); + Result.G = (u8)((PixelA.G * Opacity) + (PixelB.G * BOpacity)); + Result.B = (u8)((PixelA.B * Opacity) + (PixelB.B * BOpacity)); + return Result; +} + internal pixel LedBlend_Add(pixel PixelA, pixel PixelB) { @@ -68,6 +80,18 @@ LedBlend_GetProc(blend_mode BlendMode) return Result; } +internal void +AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, assembly Assembly, animation_pattern* Patterns, gs_memory_arena* Transient) +{ + 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); +} + internal void AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Assemblies, led_system* LedSystem, @@ -80,7 +104,15 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, Transient); - led_buffer* LayerLedBuffers = PushArray(Transient, led_buffer, CurrFrame.BlocksCountMax); + // NOTE(pjs): This mirrors animation_layer_frame to account + // for overlapping + struct layer_led_buffer + { + led_buffer HotBuffer; + led_buffer NextHotBuffer; + }; + + layer_led_buffer* LayerBuffers = PushArray(Transient, layer_led_buffer, CurrFrame.LayersCount); for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) { @@ -88,32 +120,61 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex); // Create the LayerLEDBuffers - for (u32 Layer = 0; Layer < CurrFrame.BlocksCountMax; Layer++) + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) { - led_buffer TempBuffer = {}; - TempBuffer.LedCount = AssemblyLedBuffer->LedCount; - TempBuffer.Positions = AssemblyLedBuffer->Positions; - TempBuffer.Colors = PushArray(Transient, pixel, TempBuffer.LedCount); - LedBuffer_ClearToBlack(&TempBuffer); + layer_led_buffer TempBuffer = {}; + if (CurrFrame.Layers[Layer].HasHot) + { + TempBuffer.HotBuffer.LedCount = AssemblyLedBuffer->LedCount; + TempBuffer.HotBuffer.Positions = AssemblyLedBuffer->Positions; + TempBuffer.HotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount); + LedBuffer_ClearToBlack(&TempBuffer.HotBuffer); + } - LayerLedBuffers[Layer] = TempBuffer; + if (CurrFrame.Layers[Layer].HasNextHot) + { + TempBuffer.NextHotBuffer.LedCount = AssemblyLedBuffer->LedCount; + TempBuffer.NextHotBuffer.Positions = AssemblyLedBuffer->Positions; + TempBuffer.NextHotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount); + LedBuffer_ClearToBlack(&TempBuffer.NextHotBuffer); + } + + LayerBuffers[Layer] = TempBuffer; } // Render Each layer's block to the appropriate temp buffer - for (u32 Layer = 0; Layer < CurrFrame.BlocksCountMax; Layer++) + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) { - led_buffer TempBuffer = LayerLedBuffers[Layer]; + animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; + if (LayerFrame.HasHot) + { + led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer; + animation_block Block = LayerFrame.Hot; + AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient); + } - if (!CurrFrame.BlocksFilled[Layer]) { continue; } - animation_block Block = CurrFrame.Blocks[Layer]; - - u32 FramesIntoBlock = CurrentFrame - Block.Range.Min; - r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame; - - // :AnimProcHandle - u32 AnimationProcIndex = Block.AnimationProcHandle - 1; - animation_proc* AnimationProc = Patterns[AnimationProcIndex].Proc; - AnimationProc(&TempBuffer, *Assembly, SecondsIntoBlock, Transient); + if (LayerFrame.HasNextHot) + { + led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer; + animation_block Block = LayerFrame.NextHot; + AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient); + } + } + + // Blend together any layers that have a hot and next hot buffer + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; + layer_led_buffer LayerBuffer = LayerBuffers[Layer]; + if (LayerFrame.HasNextHot) + { + for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) + { + pixel A = LayerBuffer.HotBuffer.Colors[LED]; + pixel B = LayerBuffer.NextHotBuffer.Colors[LED]; + LayerBuffer.HotBuffer.Colors[LED] = LedBlend_Overwrite(A, B, LayerFrame.HotOpacity); + } + } } // Consolidate Temp Buffers @@ -127,7 +188,7 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) { pixel A = AssemblyLedBuffer->Colors[LED]; - pixel B = LayerLedBuffers[Layer].Colors[LED]; + pixel B = LayerBuffers[Layer].HotBuffer.Colors[LED]; AssemblyLedBuffer->Colors[LED] = Blend(A, B); } } diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h index 3939714..d8b7bbe 100644 --- a/src/app/foldhaus_app.h +++ b/src/app/foldhaus_app.h @@ -202,6 +202,17 @@ TestPatternThree(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* } } +internal void +Pattern_AllGreen(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient) +{ + 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 internal void @@ -210,11 +221,12 @@ EndCurrentOperationMode(app_state* State) DeactivateCurrentOperationMode(&State->Modes); } -s32 GlobalAnimationPatternsCount = 3; +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"