From d5309819e648d0d633f171dff86d7c1816d5f40f Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sun, 8 Mar 2020 14:44:28 -0700 Subject: [PATCH] Implemented the structure for different layers to have blend modes. We now copy the led buffer for each assembly once per layer that has an active animation in it. The animations edit these temporary buffers, which are then blended together after all layers have created buffers, for each assembly. --- src/animation/foldhaus_animation.h | 14 ++- src/foldhaus_app.cpp | 126 +++++++++++++++++++-- src/foldhaus_app.h | 21 +++- src/foldhaus_assembly.cpp | 24 ++-- src/foldhaus_assembly.h | 11 ++ src/generated/foldhaus_nodes_generated.h | 12 +- src/panels/foldhaus_panel_sculpture_view.h | 8 +- todo.txt | 1 + 8 files changed, 179 insertions(+), 38 deletions(-) diff --git a/src/animation/foldhaus_animation.h b/src/animation/foldhaus_animation.h index b975592..c5b0434 100644 --- a/src/animation/foldhaus_animation.h +++ b/src/animation/foldhaus_animation.h @@ -5,7 +5,7 @@ // #ifndef FOLDHAUS_ANIMATION -#define ANIMATION_PROC(name) void name(assembly* Assembly, r32 Time) +#define ANIMATION_PROC(name) void name(assembly_led_buffer* Assembly, r32 Time) typedef ANIMATION_PROC(animation_proc); struct frame_range @@ -21,9 +21,18 @@ struct animation_block u32 Layer; }; +enum blend_mode +{ + BlendMode_Overwrite, + BlendMode_Add, + BlendMode_Multiply, + BlendMode_Count, +}; + struct anim_layer { string Name; + blend_mode BlendMode; }; #define ANIMATION_SYSTEM_LAYERS_MAX 128 @@ -124,7 +133,7 @@ RemoveAnimationBlock(gs_list_handle AnimationBlockHandle, animation_system* Anim // Layers internal u32 -AddLayer (string Name, animation_system* AnimationSystem) +AddLayer (string Name, animation_system* AnimationSystem, blend_mode BlendMode = BlendMode_Overwrite) { // TODO(Peter): If this assert fires its time to make the layer buffer system // resizable. @@ -136,6 +145,7 @@ AddLayer (string Name, animation_system* AnimationSystem) *NewLayer = {0}; NewLayer->Name = MakeString(PushArray(&AnimationSystem->Storage, char, Name.Length), Name.Length); CopyStringTo(Name, &NewLayer->Name); + NewLayer->BlendMode = BlendMode; return Result; } diff --git a/src/foldhaus_app.cpp b/src/foldhaus_app.cpp index a62d462..3209387 100644 --- a/src/foldhaus_app.cpp +++ b/src/foldhaus_app.cpp @@ -74,6 +74,7 @@ INITIALIZE_APPLICATION(InitializeApplication) State->Permanent.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; State->Permanent.Realloc = (gs_memory_realloc*)Context.PlatformRealloc; State->Transient = {}; + State->Transient.FindAddressRule = FindAddress_InLastBufferOnly; State->Transient.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; State->Transient.Realloc = (gs_memory_realloc*)Context.PlatformRealloc; @@ -192,9 +193,9 @@ INITIALIZE_APPLICATION(InitializeApplication) State->AnimationSystem.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); State->AnimationSystem.LayersMax = 32; State->AnimationSystem.Layers = PushArray(&State->Permanent, anim_layer, State->AnimationSystem.LayersMax); - AddLayer(MakeStringLiteral("Base Layer"), &State->AnimationSystem); - AddLayer(MakeStringLiteral("Color Layer"), &State->AnimationSystem); - AddLayer(MakeStringLiteral("Sparkles"), &State->AnimationSystem); + AddLayer(MakeStringLiteral("Base Layer"), &State->AnimationSystem, BlendMode_Overwrite); + AddLayer(MakeStringLiteral("Color Layer"), &State->AnimationSystem, BlendMode_Multiply); + AddLayer(MakeStringLiteral("Sparkles"), &State->AnimationSystem, BlendMode_Add); } // End Animation Playground @@ -296,8 +297,8 @@ CreateDMXBuffers(assembly Assembly, s32 BufferHeaderSize, memory_arena* Arena) LEDIdx < LEDUniverseRange.RangeOnePastLast; LEDIdx++) { - led LED = Assembly.LEDs[LEDIdx]; - pixel Color = Assembly.Colors[LED.Index]; + led LED = Assembly.LEDBuffer.LEDs[LEDIdx]; + pixel Color = Assembly.LEDBuffer.Colors[LED.Index]; DestChannel[0] = Color.R; @@ -356,6 +357,103 @@ UPDATE_AND_RENDER(UpdateAndRender) CurrentBlocks[Block.Layer] = Block; } +#if 1 + assembly_led_buffer* LayerLEDBuffers = PushArray(&State->Transient, assembly_led_buffer, CurrentBlocksMax); + + for (u32 AssemblyIndex = 0; AssemblyIndex < State->ActiveAssemblyIndecies.Used; AssemblyIndex++) + { + gs_list_handle AssemblyHandle = *State->ActiveAssemblyIndecies.GetElementAtIndex(AssemblyIndex); + assembly* Assembly = State->AssemblyList.GetElementWithHandle(AssemblyHandle); + + arena_snapshot ResetAssemblyMemorySnapshot = TakeSnapshotOfArena(&State->Transient); + + for (u32 Layer = 0; Layer < CurrentBlocksMax; Layer++) + { + if (!CurrentBlocksFilled[Layer]) { continue; } + animation_block Block = CurrentBlocks[Layer]; + + // Prep Temp Buffer + LayerLEDBuffers[Layer] = Assembly->LEDBuffer; + LayerLEDBuffers[Layer].Colors = PushArray(&State->Transient, pixel, Assembly->LEDBuffer.LEDCount); + + u32 FramesIntoBlock = CurrentFrame - Block.Range.Min; + r32 SecondsIntoBlock = FramesIntoBlock * State->AnimationSystem.SecondsPerFrame; + // TODO(Peter): Temporary + switch(Block.AnimationProcHandle) + { + case 1: + { + TestPatternOne(&LayerLEDBuffers[Layer], SecondsIntoBlock); + }break; + + case 2: + { + TestPatternTwo(&LayerLEDBuffers[Layer], SecondsIntoBlock); + }break; + + case 3: + { + TestPatternThree(&LayerLEDBuffers[Layer], SecondsIntoBlock); + }break; + + // NOTE(Peter): Zero is invalid + InvalidDefaultCase; + } + } + + // Consolidate Temp Buffers + // We do this in reverse order so that they go from top to bottom + for (u32 Layer = 0; Layer < CurrentBlocksMax; Layer++) + { + if (!CurrentBlocksFilled[Layer]) { continue; } + + switch (State->AnimationSystem.Layers[Layer].BlendMode) + { + case BlendMode_Overwrite: + { + for (u32 LED = 0; LED < Assembly->LEDBuffer.LEDCount; LED++) + { + Assembly->LEDBuffer.Colors[LED] = LayerLEDBuffers[Layer].Colors[LED]; + } + }break; + + case BlendMode_Add: + { + for (u32 LED = 0; LED < Assembly->LEDBuffer.LEDCount; LED++) + { + u32 R = (u32)Assembly->LEDBuffer.Colors[LED].R + (u32)LayerLEDBuffers[Layer].Colors[LED].R; + u32 G = (u32)Assembly->LEDBuffer.Colors[LED].G + (u32)LayerLEDBuffers[Layer].Colors[LED].G; + u32 B = (u32)Assembly->LEDBuffer.Colors[LED].B + (u32)LayerLEDBuffers[Layer].Colors[LED].B; + + Assembly->LEDBuffer.Colors[LED].R = (u8)GSMin(R, (u32)255); + Assembly->LEDBuffer.Colors[LED].G = (u8)GSMin(G, (u32)255); + Assembly->LEDBuffer.Colors[LED].B = (u8)GSMin(B, (u32)255); + } + }break; + + case BlendMode_Multiply: + { + for (u32 LED = 0; LED < Assembly->LEDBuffer.LEDCount; LED++) + { + r32 DR = (r32)Assembly->LEDBuffer.Colors[LED].R / 255.f; + r32 DG = (r32)Assembly->LEDBuffer.Colors[LED].G / 255.f; + r32 DB = (r32)Assembly->LEDBuffer.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; + + Assembly->LEDBuffer.Colors[LED].R = (u8)((DR * SR) * 255.f); + Assembly->LEDBuffer.Colors[LED].G = (u8)((DG * SG) * 255.f); + Assembly->LEDBuffer.Colors[LED].B = (u8)((DB * SB) * 255.f); + } + }break; + } + } + + ClearArenaToSnapshot(&State->Transient, ResetAssemblyMemorySnapshot); + } +#else for (s32 Layer = CurrentBlocksMax - 1; Layer >= 0; Layer--) { if (!CurrentBlocksFilled[Layer]) { continue; } @@ -365,6 +463,11 @@ UPDATE_AND_RENDER(UpdateAndRender) gs_list_handle AssemblyHandle = *State->ActiveAssemblyIndecies.GetElementAtIndex(j); assembly* Assembly = State->AssemblyList.GetElementWithHandle(AssemblyHandle); + // TEmporary + assembly_led_buffer TempBuffer = Assembly->LEDBuffer; + TempBuffer.Colors = PushArray(&State->Transient, pixel, TempBuffer.LEDCount); + GSZeroArray(TempBuffer.Colors, pixel, TempBuffer.LEDCount); + u32 FramesIntoBlock = CurrentFrame - Block.Range.Min; r32 SecondsIntoBlock = FramesIntoBlock * State->AnimationSystem.SecondsPerFrame; // TODO(Peter): Temporary @@ -372,24 +475,31 @@ UPDATE_AND_RENDER(UpdateAndRender) { case 1: { - TestPatternOne(Assembly, SecondsIntoBlock); + TestPatternOne(&TempBuffer, SecondsIntoBlock); }break; case 2: { - TestPatternTwo(Assembly, SecondsIntoBlock); + TestPatternTwo(&TempBuffer, SecondsIntoBlock); }break; case 3: { - TestPatternThree(Assembly, SecondsIntoBlock); + TestPatternThree(&TempBuffer, SecondsIntoBlock); }break; // NOTE(Peter): Zero is invalid InvalidDefaultCase; } + + // Temporary + for (u32 i = 0; i < TempBuffer.LEDCount; i++) + { + Assembly->LEDBuffer.Colors[i] = TempBuffer.Colors[i]; + } } } +#endif } s32 HeaderSize = State->NetworkProtocolHeaderSize; diff --git a/src/foldhaus_app.h b/src/foldhaus_app.h index 840af74..c64769b 100644 --- a/src/foldhaus_app.h +++ b/src/foldhaus_app.h @@ -96,19 +96,28 @@ internal void OpenColorPicker(app_state* State, v4* Address); // BEGIN TEMPORARY PATTERNS internal void -TestPatternOne(assembly* Assembly, r32 Time) +TestPatternOne(assembly_led_buffer* Assembly, r32 Time) { for (u32 LEDIdx = 0; LEDIdx < Assembly->LEDCount; LEDIdx++) { led LED = Assembly->LEDs[LEDIdx]; - Assembly->Colors[LED.Index].R = 255; - Assembly->Colors[LED.Index].B = 255; - Assembly->Colors[LED.Index].G = 255; + if (LED.Position.x < 0) + { + Assembly->Colors[LED.Index].R = 255; + Assembly->Colors[LED.Index].B = 255; + Assembly->Colors[LED.Index].G = 255; + } + else + { + Assembly->Colors[LED.Index].R = 0; + Assembly->Colors[LED.Index].B = 0; + Assembly->Colors[LED.Index].G = 0; + } } } internal void -TestPatternTwo(assembly* Assembly, r32 Time) +TestPatternTwo(assembly_led_buffer* Assembly, r32 Time) { r32 PeriodicTime = (Time / PI) * 2; @@ -162,7 +171,7 @@ TestPatternTwo(assembly* Assembly, r32 Time) } internal void -TestPatternThree(assembly* Assembly, r32 Time) +TestPatternThree(assembly_led_buffer* Assembly, r32 Time) { v4 GreenCenter = v4{0, 0, 150, 1}; r32 GreenRadius = GSAbs(GSSin(Time)) * 200; diff --git a/src/foldhaus_assembly.cpp b/src/foldhaus_assembly.cpp index 1fb23e3..0e17226 100644 --- a/src/foldhaus_assembly.cpp +++ b/src/foldhaus_assembly.cpp @@ -21,9 +21,9 @@ ConstructAssemblyFromDefinition (assembly_definition Definition, // NOTE(Peter): Setting this to zero so we can check at the end of the loop that creates leds // and make sure we created to correct number. By the time this function returns it should be // the case that: (Assembly.LEDCount == Definition.TotalLEDCount) - Assembly.LEDCount = 0; - Assembly.Colors = PushArray(&Assembly.Arena, pixel, Definition.TotalLEDCount); - Assembly.LEDs = PushArray(&Assembly.Arena, led, Definition.TotalLEDCount); + Assembly.LEDBuffer.LEDCount = 0; + Assembly.LEDBuffer.Colors = PushArray(&Assembly.Arena, pixel, Definition.TotalLEDCount); + Assembly.LEDBuffer.LEDs = PushArray(&Assembly.Arena, led, Definition.TotalLEDCount); Assembly.LEDUniverseMapCount = Definition.LEDStripCount; Assembly.LEDUniverseMap = PushArray(&Assembly.Arena, leds_in_universe_range, Definition.LEDStripCount); @@ -34,8 +34,8 @@ ConstructAssemblyFromDefinition (assembly_definition Definition, leds_in_universe_range* LEDUniverseRange = Assembly.LEDUniverseMap + StripIdx; LEDUniverseRange->Universe = StripDef.StartUniverse; - LEDUniverseRange->RangeStart = Assembly.LEDCount; - LEDUniverseRange->RangeOnePastLast = Assembly.LEDCount + StripDef.LEDsPerStrip; + LEDUniverseRange->RangeStart = Assembly.LEDBuffer.LEDCount; + LEDUniverseRange->RangeOnePastLast = Assembly.LEDBuffer.LEDCount + StripDef.LEDsPerStrip; // NOTE(Peter): this should be a switch on the type, but we only have one for // now. The assert is to remind you to create more cases when necessary @@ -45,19 +45,19 @@ ConstructAssemblyFromDefinition (assembly_definition Definition, v4 WS_StripEnd = RootPosition + V4(StripDef.InterpolatePositionEnd * Scale, 1); s32 LEDsInStripCount = StripDef.LEDsPerStrip; - Assert(Assembly.LEDCount + LEDsInStripCount <= Definition.TotalLEDCount); + Assert(Assembly.LEDBuffer.LEDCount + LEDsInStripCount <= Definition.TotalLEDCount); v4 SingleStep = (WS_StripEnd - WS_StripStart) / (r32)LEDsInStripCount; for (s32 Step = 0; Step < LEDsInStripCount; Step++) { - s32 LEDIndex = Assembly.LEDCount++; - Assembly.LEDs[LEDIndex].Position = WS_StripStart + (SingleStep * Step); - Assembly.LEDs[LEDIndex].Index = LEDIndex; + s32 LEDIndex = Assembly.LEDBuffer.LEDCount++; + Assembly.LEDBuffer.LEDs[LEDIndex].Position = WS_StripStart + (SingleStep * Step); + Assembly.LEDBuffer.LEDs[LEDIndex].Index = LEDIndex; } } // NOTE(Peter): Did we create the correct number of LEDs? - Assert(Assembly.LEDCount == Definition.TotalLEDCount); + Assert(Assembly.LEDBuffer.LEDCount == Definition.TotalLEDCount); return Assembly; } @@ -89,7 +89,7 @@ LoadAssembly (app_state* State, context Context, char* Path) gs_list_handle NewAssemblyHandle = State->AssemblyList.PushElementOnList(NewAssembly); State->ActiveAssemblyIndecies.PushElementOnList(NewAssemblyHandle); - State->TotalLEDsCount += NewAssembly.LEDCount; + State->TotalLEDsCount += NewAssembly.LEDBuffer.LEDCount; Context.PlatformFree(AssemblyFile.Base, AssemblyFile.Size); } @@ -103,7 +103,7 @@ internal void UnloadAssembly (u32 AssemblyIndex, app_state* State, context Context) { assembly* Assembly = State->AssemblyList.GetElementAtIndex(AssemblyIndex); - State->TotalLEDsCount -= Assembly->LEDCount; + State->TotalLEDsCount -= Assembly->LEDBuffer.LEDCount; FreeMemoryArena(&Assembly->Arena, (gs_memory_free*)Context.PlatformFree); State->AssemblyList.FreeElementAtIndex(AssemblyIndex); diff --git a/src/foldhaus_assembly.h b/src/foldhaus_assembly.h index f53084b..5852583 100644 --- a/src/foldhaus_assembly.h +++ b/src/foldhaus_assembly.h @@ -33,6 +33,13 @@ struct leds_in_universe_range s32 Universe; }; +struct assembly_led_buffer +{ + u32 LEDCount; + pixel* Colors; + led* LEDs; +}; + struct assembly { memory_arena Arena; @@ -40,9 +47,13 @@ struct assembly string Name; string FilePath; + assembly_led_buffer LEDBuffer; + +#if 0 u32 LEDCount; pixel* Colors; led* LEDs; +#endif u32 LEDUniverseMapCount; leds_in_universe_range* LEDUniverseMap; diff --git a/src/generated/foldhaus_nodes_generated.h b/src/generated/foldhaus_nodes_generated.h index f72b4bb..8e9f0f8 100644 --- a/src/generated/foldhaus_nodes_generated.h +++ b/src/generated/foldhaus_nodes_generated.h @@ -1,28 +1,28 @@ enum node_type { - NodeType_RevolvingDiscs, NodeType_SolidColorProc, + NodeType_RevolvingDiscs, NodeType_VerticalColorFadeProc, NodeType_Count, }; static node_specification_ NodeSpecifications[] = { -{ NodeType_RevolvingDiscs, {"RevolvingDiscs", 14}, gsm_StructType_revolving_discs_data }, { NodeType_SolidColorProc, {"SolidColorProc", 14}, gsm_StructType_solid_color_data }, +{ NodeType_RevolvingDiscs, {"RevolvingDiscs", 14}, gsm_StructType_revolving_discs_data }, { NodeType_VerticalColorFadeProc, {"VerticalColorFadeProc", 21}, gsm_StructType_vertical_color_fade_data }, }; void CallNodeProc(node_type Type, u8* NodeData) { switch(Type) { - case NodeType_RevolvingDiscs: - { - RevolvingDiscs((revolving_discs_data*)NodeData); - } break; case NodeType_SolidColorProc: { SolidColorProc((solid_color_data*)NodeData); } break; + case NodeType_RevolvingDiscs: + { + RevolvingDiscs((revolving_discs_data*)NodeData); + } break; case NodeType_VerticalColorFadeProc: { VerticalColorFadeProc((vertical_color_fade_data*)NodeData); diff --git a/src/panels/foldhaus_panel_sculpture_view.h b/src/panels/foldhaus_panel_sculpture_view.h index 8b3acb1..880b2f8 100644 --- a/src/panels/foldhaus_panel_sculpture_view.h +++ b/src/panels/foldhaus_panel_sculpture_view.h @@ -160,15 +160,15 @@ SculptureView_Render(panel Panel, rect PanelBounds, render_command_buffer* Rende { gs_list_handle AssemblyHandle = *State->ActiveAssemblyIndecies.GetElementAtIndex(i); assembly Assembly = *State->AssemblyList.GetElementWithHandle(AssemblyHandle); - u32 JobsNeeded = IntegerDivideRoundUp(Assembly.LEDCount, MaxLEDsPerJob); + u32 JobsNeeded = IntegerDivideRoundUp(Assembly.LEDBuffer.LEDCount, MaxLEDsPerJob); for (u32 Job = 0; Job < JobsNeeded; Job++) { draw_leds_job_data* JobData = PushStruct(&State->Transient, draw_leds_job_data); - JobData->LEDs = Assembly.LEDs; - JobData->Colors = Assembly.Colors; + JobData->LEDs = Assembly.LEDBuffer.LEDs; + JobData->Colors = Assembly.LEDBuffer.Colors; JobData->StartIndex = Job * MaxLEDsPerJob; - JobData->OnePastLastIndex = GSMin(JobData->StartIndex + MaxLEDsPerJob, Assembly.LEDCount); + JobData->OnePastLastIndex = GSMin(JobData->StartIndex + MaxLEDsPerJob, Assembly.LEDBuffer.LEDCount); JobData->Batch = &RenderLEDsBatch; JobData->FaceCameraMatrix; JobData->ModelViewMatrix = ModelViewMatrix; diff --git a/todo.txt b/todo.txt index 06de4c0..9cc8e05 100644 --- a/todo.txt +++ b/todo.txt @@ -86,6 +86,7 @@ Ground Up Reengineering - - setting start and end of timeline (how long is a loop) - clips can have parameters that drive them? + - clips cannot overlap eachother on the same layer - Node System - automatic node layout