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.
This commit is contained in:
Peter Slattery 2020-03-08 14:44:28 -07:00
parent f1936a016c
commit d5309819e6
8 changed files with 179 additions and 38 deletions

View File

@ -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;
}

View File

@ -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,25 +475,32 @@ 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;
dmx_buffer_list* DMXBuffers = 0;

View File

@ -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];
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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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