From 0a6e95f855afad36481baa9ed72b39ba6e297df6 Mon Sep 17 00:00:00 2001 From: Fox Caminiti Date: Sat, 5 Nov 2022 11:07:29 -0400 Subject: many changes --- createcalls.cpp | 389 +++++++++++++++++-- debug.h | 2 +- defines.h | 1 + functions.h | 14 +- gl_calls.cpp | 14 + imgui_ops.h | 10 + main.cpp | 211 ++++++---- main.h | 135 +++++-- memory.cpp | 77 +++- memory.h | 3 +- my_imgui_widgets.cpp | 1056 +++++++++++++++++++++++++++++++++++++++++++------- prenderer.cpp | 176 +++++++-- 12 files changed, 1781 insertions(+), 307 deletions(-) diff --git a/createcalls.cpp b/createcalls.cpp index 5c569fb..c018eaa 100644 --- a/createcalls.cpp +++ b/createcalls.cpp @@ -6,12 +6,45 @@ PostMsg(project_state *State, char *msg) } static uint16 +Source_Generate_Blank(project_data *File, project_state *State, memory *Memory, uint16 Width, uint16 Height, uint16 BytesPerPixel) +{ + Assert(File->Source_Count < MAX_SOURCES); + uint16 Index = Memory_Block_AllocateNew(Memory, F_Sources); + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Index); + Source->Occupied = 1; + Source->Width = Width; + Source->Height = Height; + Source->BytesPerPixel= BytesPerPixel; + Source->Type = source_type_principal; + Source->Bitmap_Index = Memory_Block_PrincipalBitmap_AllocateNew(File, State, Memory); + Source->Path_String_Index = String_AddToFile(Memory, "test"); + // History_Action_Swap(Memory, F_File, sizeof(File->Source_Count), &File->Source_Count); + File->Source_Count++; + return Index; +} + +static bool32 Source_IsFileSupported(char *Path, bool32 *IsVideo) { + return stbi_info(Path, NULL, NULL, NULL); + // AV_IsFileSupported(Path, IsVideo) +} + +static void +Source_Delete(project_data *File, memory *Memory, uint32 Index) +{ + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Index); + History_Action_Block_Swap(Memory, F_Sources, Source); + Source->Occupied = 0; + History_Action_Swap(Memory, F_File, sizeof(File->Source_Count), &File->Source_Count); + File->Source_Count--; +} + +static int16 Source_Generate(project_data *File, project_state *State, memory *Memory, void *TempString) { Assert(File->Source_Count < MAX_SOURCES); bool32 IsVideo = 0; - if (AV_IsFileSupported((char *)TempString, &IsVideo)) { + if (Source_IsFileSupported((char *)TempString, &IsVideo)) { uint16 Index = Memory_Block_AllocateNew(Memory, F_Sources); block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Index); History_Entry_Commit(Memory, "Add source"); @@ -19,10 +52,8 @@ Source_Generate(project_data *File, project_state *State, memory *Memory, void * Source->Occupied = 1; Source->Path_String_Index = String_AddToFile(Memory, (char *)TempString); - if (IsVideo) - Source->Type = source_type_video; - else - Source->Type = source_type_image; + Assert(!IsVideo); + Source->Type = source_type_file; History_Action_Swap(Memory, F_File, sizeof(File->Source_Count), &File->Source_Count); File->Source_Count++; @@ -109,6 +140,7 @@ Keyframe_Interact_Evaluate(memory *Memory, project_state *State, uint8 IsSelecte } } +/* static void Layer_Interact_Evaluate(memory *Memory, project_state *State, uint16 Layer_Index_Physical, sorted_comp_info SortedCompInfo, sorted_layer *SortedLayerInfo, int32 *Frame_Start, int32 *Frame_End, real32 *Vertical_Offset) @@ -146,8 +178,26 @@ Layer_Interact_Evaluate(memory *Memory, project_state *State, uint16 Layer_Index *Frame_End += (int32)(State->Interact_Offset[0] * Side[1]); if (*Frame_End <= *Frame_Start) *Frame_End = *Frame_Start + 1; + } else if (State->Interact_Active == interact_type_viewport_transform) { + int i = SortedCompInfo.LayerCount - 1; + while (i >= 0) { + sorted_layer SortEntry = SortedLayerInfo[i]; + uint32 Index_Physical = SortEntry.Block_Layer_Index; + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); + i--; + } } } +*/ + +static void +Layer_Select(memory *Memory, project_state *State, int32 i) +{ + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + Layer->IsSelected = true; + State->MostRecentlySelectedLayer = i; + // State->RecentSelectionType = selection_layer; +} // TODO(fox): Precomps! void Layer_DeselectAll(memory *Memory, uint32 LayerCount) { @@ -158,6 +208,15 @@ void Layer_DeselectAll(memory *Memory, uint32 LayerCount) { } } +void Source_DeselectAll(project_data *File, memory *Memory) +{ + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Sources, File->Source_Count, &h, &c, &i)) { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i); + Source->IsSelected = 0; + } +} + static sorted_layer * Layer_GetSortedArray(sorted_layer *LayerArrayStart, sorted_comp_info *SortedCompStart, uint32 TargetComp) { @@ -169,6 +228,35 @@ Layer_GetSortedArray(sorted_layer *LayerArrayStart, sorted_comp_info *SortedComp return LayerArrayStart + LayerOffset; } +int32 Layer_TestSelection(memory *Memory, project_state *State, ui *UI, sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray, uint16 PrincipalIndex) +{ + block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, PrincipalIndex); + sorted_comp_info SortedCompInfo = SortedCompArray[PrincipalIndex]; + sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, PrincipalIndex); + int32 LayerIndex = -1; + // for (int i = 0; i < SortedCompInfo.LayerCount; i++) { + for (int i = SortedCompInfo.LayerCount - 1; i >= 0; i--) { + sorted_layer SortEntry = SortedLayerInfo[i]; + uint32 Index_Physical = SortEntry.Block_Layer_Index; + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); + layer_transforms T = Layer_GetTransforms(Layer); + v2 UV = T_CompUVToLayerUV(T, Comp->Width, Comp->Height, Source->Width, Source->Height, UI->TempZoomRatio); + if (UV.x <= 1.0f && UV.x >= 0.0f && UV.y <= 1.0f && UV.y >= 0.0f && !Layer->IsSelected) + { + LayerIndex = Index_Physical; + break; + } + // if (Layer->IsPrecomp) { + // Layer_RecursiveDeselect(Memory, SortedCompArray, SortedLayerArray, TargetIndex, Layer->Block_Source_Index); + // } + // if (Layer->Block_Composition_Index != TargetIndex) { + // Layer->IsSelected = false; + // } + } + return LayerIndex; +} + void Layer_RecursiveDeselect(memory *Memory, sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray, uint16 TargetIndex, uint16 PrincipalIndex) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, PrincipalIndex); @@ -187,16 +275,35 @@ void Layer_RecursiveDeselect(memory *Memory, sorted_comp_info *SortedCompArray, } } -void Layer_SortAll(memory *Memory, sorted_layer *LayerArrayStart, sorted_comp_info *CompStart, uint32 LayerCount, uint32 CompCount) +void Layer_Sort_CheckPrev(memory *Memory, int i, int Direction, sorted_layer *SortedLayerInfo, sorted_comp_info SortedCompInfo, int *EntriesPassed, sorted_layer *LayerEntry) { - for (uint32 i = 0; i < LayerCount; i++) { + int PrevOffsetIndex = i + Direction + (*EntriesPassed * Direction); + bool32 OutOfBounds = (Direction > 0) ? (PrevOffsetIndex > (SortedCompInfo.LayerCount - 1)) : (PrevOffsetIndex < 0); + if (!OutOfBounds) { + sorted_layer *PrevLayerEntry = &SortedLayerInfo[PrevOffsetIndex]; + // block_layer *PrevLayer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, PrevLayerEntry->Block_Layer_Index); + // real32 PrevOffset = PrevLayer->Vertical_Offset; + real32 PrevOffset = PrevLayerEntry->SortedOffset; + if (PrevOffset == (LayerEntry->SortedOffset - Direction)) { + LayerEntry->SortedOffset -= Direction; + (*EntriesPassed)++; + Layer_Sort_CheckPrev(Memory, i, Direction, SortedLayerInfo, SortedCompInfo, EntriesPassed, LayerEntry); + } + } +} + + +void Layer_SortAll(project_data *File, project_state *State, memory *Memory, sorted_layer *LayerArrayStart, sorted_comp_info *CompStart, uint32 LayerCount, uint32 CompCount) +{ + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); Assert(Layer->Block_Composition_Index < CompCount); CompStart[Layer->Block_Composition_Index].LayerCount++; } - for (uint32 i = 0; i < LayerCount; i++) { - // SortedLayerArray->Block_Layer_Index = 0; + h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); sorted_comp_info *SortedCompInfo = &CompStart[Layer->Block_Composition_Index]; sorted_layer *SortedLayerInfo = Layer_GetSortedArray(LayerArrayStart, CompStart, Layer->Block_Composition_Index); @@ -218,19 +325,63 @@ void Layer_SortAll(memory *Memory, sorted_layer *LayerArrayStart, sorted_comp_in } sorted_layer *LayerEntry = SortedLayerInfo + SortedIndex_Playhead; LayerEntry->Block_Layer_Index = i; + LayerEntry->SortedOffset = Layer->Vertical_Offset; SortedCompInfo->CurrentSortIndex++; } - // Assert(CompStart[0].LayerCount == 3); - // Assert(CompStart[1].LayerCount == 2); - // Assert(LayerArrayStart[0].Block_Layer_Index == 0); - // Assert(LayerArrayStart[1].Block_Layer_Index == 2); - // Assert(LayerArrayStart[2].Block_Layer_Index == 4); - // Assert(LayerArrayStart[4].Block_Layer_Index == 1); - // Assert(LayerArrayStart[5].Block_Layer_Index == 3); + if (State->Interact_Active == interact_type_layer_move) { + int32 Offset = (int32)State->Interact_Offset[1]; + bool32 Direction = (Offset > 0) ? 1 : -1; + for (uint32 c = 0; c < CompCount; c++) { + sorted_comp_info *SortedCompInfo = &CompStart[c]; + if (!SortedCompInfo->LayerCount) + continue; + sorted_layer *SortedLayerInfo = Layer_GetSortedArray(LayerArrayStart, CompStart, c); + int i = (Direction > 0) ? SortedCompInfo->LayerCount - 1 : 0; + bool32 Case = 1; + while (Case) { + int32 EntriesPassed = 0; + sorted_layer *LayerEntry = &SortedLayerInfo[i]; + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, LayerEntry->Block_Layer_Index); + Assert(LayerEntry->SortedOffset == Layer->Vertical_Offset); + if (Layer->IsSelected) { + int32 SpacesToMove = Offset * Direction; + while (SpacesToMove) { + Layer_Sort_CheckPrev(Memory, i, Direction, SortedLayerInfo, *SortedCompInfo, &EntriesPassed, LayerEntry); + LayerEntry->SortedOffset -= Direction; + SpacesToMove--; + } + } + int b = 0; + while (b < EntriesPassed) { + sorted_layer *FrontEntry = &SortedLayerInfo[i+(b*Direction)]; + sorted_layer *BackEntry = &SortedLayerInfo[i+((b+1)*Direction)]; + sorted_layer Swap = *FrontEntry; + *FrontEntry = *BackEntry; + *BackEntry = Swap; + b++; + } + i -= Direction; + Case = (Direction > 0) ? (i >= 0) : (i < SortedCompInfo->LayerCount); + } + } + } +} + +static void +Layer_Delete(project_data *File, memory *Memory, uint32 Index) +{ + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index); + History_Action_Block_Swap(Memory, F_Layers, Layer); + Layer->Occupied = 0; + History_Action_Swap(Memory, F_File, sizeof(File->Layer_Count), &File->Layer_Count); + File->Layer_Count--; } block_layer * Layer_Init(project_data *File, memory *Memory) { + if (File->Layer_Count + 1 > MAX_LAYERS) { + Assert(0); + } block_layer *Layer = (block_layer *)Memory_Block_AllocateAddress(Memory, F_Layers); History_Action_Block_Swap(Memory, F_Layers, Layer); @@ -259,22 +410,210 @@ block_layer * Layer_Init(project_data *File, memory *Memory) return Layer; } +void Source_UICreateButton(project_data *File, project_state *State, memory *Memory, + sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray) +{ + block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); + sorted_comp_info *SortedCompInfo = &SortedCompArray[File->PrincipalCompIndex]; + int32 TopOffset; + if (!File->Layer_Count) { + TopOffset = 11; + } else { + sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, File->PrincipalCompIndex); + block_layer *TopLayer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, SortedLayerInfo[SortedCompInfo->LayerCount - 1].Block_Layer_Index); + TopOffset = TopLayer->Vertical_Offset; + } + bool32 CommitAction = 0; + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Sources, File->Source_Count, &h, &c, &i)) { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i); + if (Source->IsSelected) { + if (!CommitAction) { + History_Entry_Commit(Memory, "Create layer from source"); + CommitAction = 1; + } + block_layer *Layer = Layer_Init(File, Memory); + Layer->Block_Source_Index = i; + Layer->x.CurrentValue = Source->Width / 2; + Layer->y.CurrentValue = Source->Height / 2; + Layer->Vertical_Offset = TopOffset-1; + Layer->Frame_End = Comp->Frame_End; + TopOffset--; + } + State->UpdateFrame = true; + } + if (CommitAction) + History_Entry_End(Memory); + Source_DeselectAll(File, Memory); +} + + +// Helper for working with different bit depths. +static render_byte_info +Bitmap_ByteInfo(uint32 BytesPerPixel) { + render_byte_info Byte = {}; + if (BytesPerPixel == 4) { + Byte.MaskPixel = 0xFF; + Byte.ByteOffset = 1; + Byte.Normalized = 1 / 255.0f; + Byte.Bits = 255; + } else if (BytesPerPixel == 8) { + Byte.MaskPixel = 0xFFFF; + Byte.ByteOffset = 2; + Byte.Normalized = 1 / 65535.0f; + Byte.Bits = 65535; + } else { + Byte.MaskPixel = 0xFFFFFFFF; + Byte.ByteOffset = 4; + Byte.Normalized = 1 / 4294967295.0f; + Byte.Bits = 4294967295; + Assert(0); + } + return Byte; +} + +// TODO(fox): Make separate full-size bitmap that gets scaled on the GPU instead of this static void -Layer_CreateFromSource(project_data *File, project_state *State, memory *Memory, uint16 SourceIndex, int32 Frame_End) +State_BindBrushTexture(memory *Memory, brush_state *Brush, uint32 BytesPerPixel) { - if (File->Layer_Count + 1 > MAX_LAYERS) { - Assert(0); + GL_GenAndBindTexture(&Brush->GLTexture, Brush->Size, Brush->Size, BytesPerPixel, Brush->PaintBuffer); +} + +static void +Brush_CalcBitmapAlphaFromSize(memory *Memory, brush_state *Brush, uint32 BytesPerPixel) +{ + real32 BrushLength = Brush->Size; + real32 BrushCenter = BrushLength / 2; + real32 MaxLength = BrushCenter; + void *BrushAddress = Brush->PaintBuffer; + + render_byte_info Byte = Bitmap_ByteInfo(BytesPerPixel); + + for (int Y = 0; Y < (int)BrushLength; Y++) { + for (int X = 0; X < (int)BrushLength; X++) { + uint8 *PixelAddress = (uint8 *)BrushAddress + (Y * BytesPerPixel*(int)BrushLength) + (X * BytesPerPixel); + uint32 *R_DestAddress = (uint32 *)(PixelAddress + Byte.ByteOffset*0); + // uint32 *G_DestAddress = (uint32 *)(PixelAddress + Byte.ByteOffset*1); + // uint32 *B_DestAddress = (uint32 *)(PixelAddress + Byte.ByteOffset*2); + uint32 *A_DestAddress = (uint32 *)(PixelAddress + Byte.ByteOffset*3); + v2 Pos = V2(BrushCenter - X, BrushCenter - Y); + real32 L = sqrt(LengthSq(Pos)); + real32 Gradient = Ceil(L, MaxLength) / MaxLength; + Gradient = pow(Gradient, Brush->Hardness); + *R_DestAddress = (*R_DestAddress & ~Byte.MaskPixel) | Byte.Bits; // brush preview is red + *A_DestAddress = (*A_DestAddress & ~Byte.MaskPixel) | (uint32)((1.0f - Gradient)*Byte.Bits); + } } - History_Entry_Commit(Memory,"Add layer from source"); - block_layer *Layer = Layer_Init(File, Memory); - Layer->Block_Source_Index = SourceIndex; - Layer->Frame_End = Frame_End; - History_Entry_End(Memory); +} - State->UpdateFrame = true; +// TODO(fox): We could merge this exactly with the standard rendering code. +static void +PaintTest(memory *Memory, block_source *Source, brush_state *Brush, void *Address, v2 LayerPos, uint32 BytesPerPixel, v4 Color) +{ + uint32 BrushLength = (uint32)Brush->Size; + + rectangle RenderRegion = { 0, 0, Source->Width, Source->Height }; + rectangle BrushPos = { (int32)(LayerPos.x - (BrushLength / 2)), + (int32)(LayerPos.y - (BrushLength / 2)), + (int32)(LayerPos.x + (BrushLength / 2)), + (int32)(LayerPos.y + (BrushLength / 2)) }; + rectangle LayerBounds = ClipRectangle(BrushPos, RenderRegion); + + if (BrushPos.Min.x < Brush->CacheBounds.Min.x) + Brush->CacheBounds.Min.x = BrushPos.Min.x; + if (BrushPos.Min.y < Brush->CacheBounds.Min.y) + Brush->CacheBounds.Min.y = BrushPos.Min.y; + if (BrushPos.Max.x > Brush->CacheBounds.Max.x) + Brush->CacheBounds.Max.x = BrushPos.Max.x; + if (BrushPos.Max.y > Brush->CacheBounds.Max.y) + Brush->CacheBounds.Max.y = BrushPos.Max.y; + + uint32 LayerPitch = Source->Width*Source->BytesPerPixel; + uint32 BrushPitch = (int)BrushLength * BytesPerPixel; + + render_byte_info LayerBits = Bitmap_ByteInfo(Source->BytesPerPixel); + render_byte_info BrushBits = Bitmap_ByteInfo(BytesPerPixel); + + int32 ExtraX = 0; + int32 ExtraY = 0; + if (BrushPos.Min.x < 0) { + ExtraX = BrushPos.Min.x; + } + if (BrushPos.Min.y < 0) { + ExtraY = BrushPos.Min.y; + } + + void *BrushBuffer = Brush->PaintBuffer; + + // ImGui's color picker works in sRGB, so we need to convert to linear. + // real32 R_Brush = (Color.r >= 0.04045) ? pow((Color.r + 0.055) / (1 + 0.055), 2.4) : Color.r / 12.92; + // real32 G_Brush = (Color.g >= 0.04045) ? pow((Color.g + 0.055) / (1 + 0.055), 2.4) : Color.g / 12.92; + // real32 B_Brush = (Color.b >= 0.04045) ? pow((Color.b + 0.055) / (1 + 0.055), 2.4) : Color.b / 12.92; + // real32 A_Brush = (Color.a >= 0.04045) ? pow((Color.a + 0.055) / (1 + 0.055), 2.4) : Color.a / 12.92; + real32 R_Brush = Color.r; + real32 G_Brush = Color.g; + real32 B_Brush = Color.b; + real32 A_Brush = Color.a; + + uint8 *BrushRow = (uint8 *)BrushBuffer; + for (int32 Y = LayerBounds.Min.y; Y < LayerBounds.Max.y; Y++) { + for (int32 X = LayerBounds.Min.x; X < LayerBounds.Max.x; X++) { + + uint32 Offset = Y*LayerPitch + X*Source->BytesPerPixel; + uint8 *LayerPixel = (uint8 *)Address + Offset; + + uint32 *R_DestAddress = (uint32 *)(LayerPixel + LayerBits.ByteOffset * 0); + uint32 *G_DestAddress = (uint32 *)(LayerPixel + LayerBits.ByteOffset * 1); + uint32 *B_DestAddress = (uint32 *)(LayerPixel + LayerBits.ByteOffset * 2); + uint32 *A_DestAddress = (uint32 *)(LayerPixel + LayerBits.ByteOffset * 3); + + real32 R_Layer = (real32)(*R_DestAddress & LayerBits.MaskPixel) * LayerBits.Normalized; + real32 G_Layer = (real32)(*G_DestAddress & LayerBits.MaskPixel) * LayerBits.Normalized; + real32 B_Layer = (real32)(*B_DestAddress & LayerBits.MaskPixel) * LayerBits.Normalized; + real32 A_Layer = (real32)(*A_DestAddress & LayerBits.MaskPixel) * LayerBits.Normalized; + + int32 TrueX = (X - LayerBounds.Min.x) - ExtraX; + int32 TrueY = (Y - LayerBounds.Min.y) - ExtraY; + + // Assert(TrueX >= 0 && TrueX < BrushLength); + // Assert(TrueY >= 0 && TrueY < BrushLength); + + uint8 *BrushPixel = (uint8 *)BrushBuffer + (TrueY * BrushPitch) + (TrueX * BytesPerPixel); + + real32 Brush_BitmapAlpha = (real32)((*(uint32 *)(BrushPixel + BrushBits.ByteOffset*3)) & BrushBits.MaskPixel) * BrushBits.Normalized; + + real32 BrushAlpha = Brush_BitmapAlpha * A_Brush; + real32 LayerAlpha = A_Layer; + + real32 A_Blend = LayerAlpha + BrushAlpha; + + real32 R_Blend = (R_Layer * (1.0f - BrushAlpha)) + (R_Brush * BrushAlpha); + real32 G_Blend = (G_Layer * (1.0f - BrushAlpha)) + (G_Brush * BrushAlpha); + real32 B_Blend = (B_Layer * (1.0f - BrushAlpha)) + (B_Brush * BrushAlpha); + + /* + R_Blend = (R_Brush * (1.0f - LayerAlpha)) + (R_Blend * LayerAlpha); + G_Blend = (G_Brush * (1.0f - LayerAlpha)) + (G_Blend * LayerAlpha); + B_Blend = (B_Brush * (1.0f - LayerAlpha)) + (B_Blend * LayerAlpha); + */ + + Assert(R_Blend <= 1.0f); + + uint32 R_Out = (uint32)(Normalize(R_Blend) * LayerBits.Bits); + uint32 G_Out = (uint32)(Normalize(G_Blend) * LayerBits.Bits); + uint32 B_Out = (uint32)(Normalize(B_Blend) * LayerBits.Bits); + uint32 A_Out = (uint32)(Normalize(A_Blend) * LayerBits.Bits); + + *R_DestAddress = (*R_DestAddress & ~LayerBits.MaskPixel) | R_Out; + *G_DestAddress = (*G_DestAddress & ~LayerBits.MaskPixel) | G_Out; + *B_DestAddress = (*B_DestAddress & ~LayerBits.MaskPixel) | B_Out; + *A_DestAddress = (*A_DestAddress & ~LayerBits.MaskPixel) | A_Out; + } + } } + #if 0 static void IncrementFrame(project_data *File, int16 Amount) { diff --git a/debug.h b/debug.h index cac3d39..202eb20 100644 --- a/debug.h +++ b/debug.h @@ -27,7 +27,7 @@ struct debug_temp struct project_debug { debug_temp Temp; - bool32 ToggleWindow; + bool32 ToggleWindow = 1; uint64 PixelCountTransparent; uint64 PixelCountRendered; uint64 PixelCountChecked; diff --git a/defines.h b/defines.h index ccadb78..f2d58f2 100644 --- a/defines.h +++ b/defines.h @@ -35,6 +35,7 @@ typedef uint64 ptrsize; // is there a compiler variable for 32 vs 64 bit like #define MAX_PROPERTIES_PER_EFFECT 16 #define MAX_KEYFRAME_BLOCKS 64 #define MAX_KEYFRAMES_PER_BLOCK 32 // max keyframes on a single channel is 2048 +#define MAX_BITMAP_CHUNKS 64 // Up to 2048x2048-size textures #define MAX_SELECTED_PROPERTIES 16 diff --git a/functions.h b/functions.h index 07cfc25..d4a4cfa 100644 --- a/functions.h +++ b/functions.h @@ -1,10 +1,22 @@ -static bool32 AV_IsFileSupported(char *filename, bool32 *IsVideo); +// static bool32 AV_IsFileSupported(char *filename, bool32 *IsVideo); static void Arbitrary_WriteInto(uint8 *Address_Read, uint8 *Address_Write, uint64 Size); static void Arbitrary_Zero(uint8 *Address_Write, uint64 Size); static void Arbitrary_SwapData(memory *Memory, uint8 *Address_0, uint8 *Address_1, uint64 Size); static void Arbitrary_ShiftData(uint8 *Address_Start, uint8 *Address_End, uint64 ShiftAmount, int32 Direction); +static v2 T_CompUVToLayerUV(layer_transforms T, uint32 FileWidth, uint32 FileHeight, uint32 SourceWidth, uint32 SourceHeight, v2 CompUV); + +static void Interact_Transform_Begin(project_data *File, memory *Memory, project_state *State, ImVec2 OGPos); + +static v2 Transform_ScreenSpaceToLocal(layer_transforms T, uint32 FileWidth, uint32 FileHeight, uint32 SourceWidth, uint32 SourceHeight, ui UI, ImVec2 ViewportMin, ImVec2 Point); +static ImVec2 Layer_LocalToScreenSpace(project_state *State, block_layer *Layer, ui *UI, real32 SourceWidth, real32 SourceHeight, real32 CompWidth, real32 CompHeight, v2 Point); + +static void Transform_ApplyInteractive(interact_transform Interact, real32 *OutputX, real32 *OutputY, real32 *OutputRotation, real32 *OutputScale); + +static layer_transforms Layer_GetTransforms(block_layer *Layer); +void GL_GenAndBindTexture(GLuint *GLTexture, int Width, int Height, int BytesPerPixel, void *BufferAddress); + static real32 Bezier_SolveYForX(v2 Point_P0, v2 Point_P1, v2 Point_P2, v2 Point_P3, real32 X); # if 0 diff --git a/gl_calls.cpp b/gl_calls.cpp index b08615a..6f760b6 100644 --- a/gl_calls.cpp +++ b/gl_calls.cpp @@ -154,6 +154,20 @@ static void GL_InitDefaultVerts() { glEnableVertexAttribArray(1); } +void +GL_GenAndBindTexture(GLuint *GLTexture, int Width, int Height, int BytesPerPixel, void *BufferAddress) +{ + glGenTextures(1, GLTexture); + glBindTexture(GL_TEXTURE_2D, *GLTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + int ByteFlag = (BytesPerPixel == 4) ? GL_RGBA : GL_RGBA16; + int ByteFlag2 = (BytesPerPixel == 4) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT; + glTexImage2D(GL_TEXTURE_2D, 0, ByteFlag, Width, Height, 0, GL_RGBA, ByteFlag2, BufferAddress); +} + #if 0 void GL_InitTexture(gl_effect_layer *Test) diff --git a/imgui_ops.h b/imgui_ops.h index e2627ab..2bf6335 100644 --- a/imgui_ops.h +++ b/imgui_ops.h @@ -70,6 +70,16 @@ ImVec2 operator/(ImVec2 A, real32 B) return Result; } +ImVec2 operator/(real32 A, ImVec2 B) +{ + ImVec2 Result; + + Result.x = A / B.x; + Result.y = A / B.y; + + return Result; +} + inline bool32 IsRectTouching(ImVec2 Min1, ImVec2 Max1, ImVec2 Min2, ImVec2 Max2) { diff --git a/main.cpp b/main.cpp index 7920727..0a5e9ed 100644 --- a/main.cpp +++ b/main.cpp @@ -66,7 +66,7 @@ static uint32 RandomGlobalIncrement = 0; #include "threading.cpp" #endif #include "createcalls.cpp" -#include "ffmpeg_backend.cpp" +// #include "ffmpeg_backend.cpp" #include "my_imgui_widgets.cpp" #include "prenderer.cpp" #include "gl_calls.cpp" @@ -125,39 +125,36 @@ Main_InputTest(project_data *File, project_state *State, memory *Memory, ui *UI, UI->Warp_WantSetPos = false; } - ImGui::DockSpaceOverViewport(); - if (!io.WantCaptureKeyboard) ImGui_ProcessInputs(File, State, UI, Memory, io); -#if 0 - - ImGui_Viewport(File, &State, &UI, &Memory, CompBuffer, io, textureID); - - ImGui_File(&File, &State, &Memory, &UI, io); - - ImGui_EffectsPanel(&File, &State, &Memory, &UI, io); - - ImGui_PropertiesPanel(&File, &State, &UI, &Memory, io); - - // ImGui_Graph(&File, &State, &Memory, &UI, io); - -#if DEBUG - ImGui_DebugUndoTree(&File, &Memory); -#endif - -#endif + uint64 SortSize = (sizeof(sorted_comp_info) * File->Comp_Count) + (sizeof(sorted_layer) * File->Layer_Count); + void *SortedArray = Memory_PushScratch(Memory, SortSize); + Arbitrary_Zero((uint8 *)SortedArray, SortSize); + sorted_comp_info *SortedCompArray = (sorted_comp_info *)SortedArray; + sorted_layer *SortedLayerArray = (sorted_layer *)((uint8 *)SortedArray + (sizeof(sorted_comp_info) * File->Comp_Count)); + Layer_SortAll(File, State, Memory, SortedLayerArray, SortedCompArray, File->Layer_Count, File->Comp_Count); - ImGui_Viewport(File, State, UI, Memory, io, textureID); - ImGui_Timeline(File, State, Memory, UI, io); - ImGui_File(File, State, Memory, io); + ImGui::DockSpaceOverViewport(); if (Debug.ToggleWindow) { ImGui::ShowDemoWindow(); ImGui_DebugMemoryViewer(Memory, State); + ImGui_DebugUndoTree(Memory, State); } - // ImGui::ShowDemoWindow(); + // if (State->Initializing == 3) + // Source_UICreateButton(File, State, Memory, SortedCompArray, SortedLayerArray); + + + ImGui_Viewport(File, State, UI, Memory, io, textureID, SortedCompArray, SortedLayerArray); + ImGui_Timeline(File, State, Memory, UI, io, SortedCompArray, SortedLayerArray); + ImGui_File(File, State, Memory, io, SortedCompArray, SortedLayerArray); + ImGui_PropertiesPanel(File, State, UI, Memory, io); + ImGui_ColorPanel(File, State, UI, Memory, io); + ImGui_Menu(File, State, UI, Memory, io); + + Memory_PopScratch(Memory, SortSize); #if DEBUG Debug.Temp = {}; #endif @@ -165,20 +162,28 @@ Main_InputTest(project_data *File, project_state *State, memory *Memory, ui *UI, ImGui::EndFrame(); } +static layer_transforms +Layer_GetTransforms(block_layer *Layer) { + return { Layer->x.CurrentValue, Layer->y.CurrentValue, Layer->ax.CurrentValue, Layer->ay.CurrentValue, Layer->rotation.CurrentValue, Layer->scale.CurrentValue }; +} + static void * Render_Comp(project_data *File, project_state *State, memory *Memory, ImGuiIO io, sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray, uint32 CompIndex) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); - cache_entry *Entry_Main = Memory_Cache_Search(State, Memory, State->Render.Entry, cache_entry_type_comp, CompIndex, State->Frame_Current); + cache_entry *Entry_Main = Memory_Cache_Search(State, Memory, cache_entry_type_comp, CompIndex, State->Frame_Current); void *CompBuffer = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry_Main->Block_StartIndex); + uint64 Size = Comp->Width * Comp->Height * Comp->BytesPerPixel; + Arbitrary_Zero((uint8 *)CompBuffer, Size); - if (Entry_Main->IsCached) - return CompBuffer; + // if (Entry_Main->IsCached) + // return CompBuffer; uint64 Comp_TimeStart = GetTime(); sorted_comp_info *SortedCompInfo = &SortedCompArray[CompIndex]; sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, CompIndex); + for (int i = 0; i < SortedCompInfo->LayerCount; i++) { sorted_layer SortEntry = SortedLayerInfo[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; @@ -190,33 +195,36 @@ Render_Comp(project_data *File, project_state *State, memory *Memory, ImGuiIO io void *BitmapAddress = NULL; if (!Layer->IsPrecomp) { block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); - cache_entry *Entry = Memory_Cache_Search(State, Memory, State->Render.Entry, cache_entry_type_source, Layer->Block_Source_Index, 0); - - Assert(Source->Type == source_type_image); - if (!Entry->IsCached) { - uint64 Src_TimeStart = GetTime(); - block_string *Name = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Path_String_Index); - int w = 0, h = 0; - void *temp = stbi_load(Name->Char, &w, &h, NULL, 4); - Source->Width = w; - Source->Height = h; - Source->BytesPerPixel = 4; - uint64 Size = Source->Width * Source->Height * Source->BytesPerPixel; - void *Source_Address = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry->Block_StartIndex); - Arbitrary_WriteInto((uint8 *)temp, (uint8 *)Source_Address, Size); - stbi_image_free(temp); - BitmapState->ToUpdate = false; - BitmapState->CurrentFrame = 0; - Entry->CycleTime = GetTime() - Src_TimeStart; - Layer->x.CurrentValue = (Layer->Block_Source_Index == 0) ? 200 : Comp->Width/2; - Layer->y.CurrentValue = Comp->Height/2; - Entry->IsCached = true; + if (Source->Type == source_type_principal) { + BitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index); + } else { + cache_entry *Entry = Memory_Cache_Search(State, Memory, cache_entry_type_source, Layer->Block_Source_Index, 0); + if (!Entry->IsCached) { + uint64 Src_TimeStart = GetTime(); + block_string *Name = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Path_String_Index); + int w = 0, h = 0; + void *temp = stbi_load(Name->Char, &w, &h, NULL, 4); + Source->Width = w; + Source->Height = h; + Source->BytesPerPixel = 4; + uint64 Size = Source->Width * Source->Height * Source->BytesPerPixel; + void *Source_Address = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry->Block_StartIndex); + Arbitrary_WriteInto((uint8 *)temp, (uint8 *)Source_Address, Size); + stbi_image_free(temp); + BitmapState->ToUpdate = false; + BitmapState->CurrentFrame = 0; + Entry->CycleTime = GetTime() - Src_TimeStart; + Layer->x.CurrentValue = Comp->Width/2; + Layer->y.CurrentValue = Comp->Height/2; + Entry->IsCached = true; + } + BitmapAddress = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry->Block_StartIndex); } - BitmapAddress = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry->Block_StartIndex); + } else { block_composition *Precomp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, Layer->Block_Source_Index); - cache_entry *Entry = Memory_Cache_Search(State, Memory, State->Render.Entry, cache_entry_type_comp, Layer->Block_Source_Index, State->Frame_Current); + cache_entry *Entry = Memory_Cache_Search(State, Memory, cache_entry_type_comp, Layer->Block_Source_Index, State->Frame_Current); if (!Entry->IsCached) { uint64 Src_TimeStart = GetTime(); Render_Comp(File, State, Memory, io, SortedCompArray, SortedLayerArray, Layer->Block_Source_Index); @@ -307,7 +315,7 @@ Main_Renderer(project_data *File, project_state *State, memory *Memory, SDL_Wind Arbitrary_Zero((uint8 *)SortedArray, SortSize); sorted_comp_info *SortedCompArray = (sorted_comp_info *)SortedArray; sorted_layer *SortedLayerArray = (sorted_layer *)((uint8 *)SortedArray + (sizeof(sorted_comp_info) * File->Comp_Count)); - Layer_SortAll(Memory, SortedLayerArray, SortedCompArray, File->Layer_Count, File->Comp_Count); + Layer_SortAll(File, State, Memory, SortedLayerArray, SortedCompArray, File->Layer_Count, File->Comp_Count); void *MainCompBuffer = Render_Comp(File, State, Memory, io, SortedCompArray, SortedLayerArray, File->PrincipalCompIndex); @@ -343,6 +351,9 @@ int main(int argc, char *argv[]) { 0); #endif + // 1 meg per block + BitmapBlockSize = 1024 * 1024; + memory Memory = {}; Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, P_AVInfo, "Image/video headers"); @@ -355,10 +366,12 @@ int main(int argc, char *argv[]) { Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Sources, "Sources", sizeof(block_source)); Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Bezier, "Bezier paths (keyframes, masks)", sizeof(block_bezier)); Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Strings, "Strings", sizeof(block_string)); + Memory_InitTable(&GlobalMemory, &Memory, (uint64)50 * 1024 * 1024, F_PrincipalBitmaps, "Principal bitmap data", BitmapBlockSize); Memory_InitTable(&GlobalMemory, &Memory, (uint64)64 * 1024 * 1024, B_ScratchSpace, "Scratch"); Memory_InitTable(&GlobalMemory, &Memory, (uint64)200 * 1024 * 1024, B_CachedBitmaps, "Cached bitmap buffer"); + #if ARM InstructionMode = instruction_mode_neon; #else @@ -370,21 +383,30 @@ int main(int argc, char *argv[]) { } #endif + project_state *State = (project_state *)Memory.Slot[P_MiscCache].Address; *State = {}; project_data *File = (project_data *)Memory_Block_AllocateAddress(&Memory, F_File); *File = {}; File->Occupied = 1; + State->Brush.PaintBuffer = Memory.Slot[B_ScratchSpace].Address; + uint64 ScratchPaintSize = 2048*2048*4; + Memory.ScratchPos += ScratchPaintSize; + ui UI = {}; UI.Test = ImDrawListSplitter(); + // int ToolCount = (int)tool_count; + // for (int i = 0; i < ToolCount; i++) { + // glBindTexture(GL_TEXTURE_2D, textureID); + // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MainComp->Width, MainComp->Height, 0, GL_RGBA, ByteFlag2, MainCompBuffer); + // } + block_composition *MainComp = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps); - // MainComp->Width = 3840; - // MainComp->Height = 2160; - MainComp->Width = 1280; - MainComp->Height = 720; + MainComp->Width = 1024; + MainComp->Height = 512; MainComp->FPS = 24; MainComp->BytesPerPixel = 4; MainComp->Frame_Count = 48; @@ -392,6 +414,9 @@ int main(int argc, char *argv[]) { MainComp->Occupied = 1; MainComp->Name_String_Index = String_AddToFile(&Memory, "Main comp"); + File->Comp_Count = 1; + + /* block_composition *Comp2 = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps); Comp2->Width = 500; Comp2->Height = 500; @@ -401,16 +426,16 @@ int main(int argc, char *argv[]) { Comp2->Frame_End = 48; Comp2->Occupied = 1; Comp2->Name_String_Index = String_AddToFile(&Memory, "Another comp"); + */ - File->Comp_Count = 2; - - // 1 MB for 4, 2 MB for 8 - BitmapBlockSize = (MainComp->BytesPerPixel / 4) * 1024 * 1024; + File->Comp_Count = 1; { uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/a.jpg"); block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 0); + Source->IsSelected = true; + /* { Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1); @@ -480,30 +505,39 @@ int main(int argc, char *argv[]) { Bezier3->Point[1].Occupied = true; Bezier3->Point[2].Occupied = true; } - // { - // Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); - // block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1); - // Layer->Vertical_Offset = 1; - // Layer->Col[0] = 1; - // Layer->Col[1] = 0; - // Layer->Col[2] = 0; - // Layer->Block_Composition_Index = 1; - // } + */ + /* + { + Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1); + Layer->Vertical_Offset = 2; + Layer->IsSelected = true; + Layer->ColIndex = 2; + Layer->Block_Composition_Index = 0; + } + { + Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1); + Layer->Vertical_Offset = 5; + Layer->ColIndex = 0; + Layer->Block_Composition_Index = 0; + } + */ } { uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/b.jpg"); block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 1); -#if 0 + Source->IsSelected = true; + /* { Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1); - Layer->Vertical_Offset = 5; - Layer->Col[0] = 0; - Layer->Col[1] = 0; - Layer->Col[2] = 1; - Layer->Block_Composition_Index = 0; Layer->IsSelected = true; + Layer->Vertical_Offset = 3; + Layer->ColIndex = 1; + Layer->Block_Composition_Index = 0; + // Layer->IsSelected = true; property_channel *Property = &Layer->x; Property->Block_Bezier_Index[0] = Memory_Block_AllocateNew(&Memory, F_Bezier); @@ -545,21 +579,22 @@ int main(int argc, char *argv[]) { Bezier2->Point[0].Occupied = true; Bezier2->Point[1].Occupied = true; } -#endif + */ } { uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/c.jpg"); block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 2); - // { - // Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); - // block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1); - // Layer->Vertical_Offset = 0; - // Layer->Col[0] = 0; - // Layer->Col[1] = 1; - // Layer->Col[2] = 0; - // Layer->Block_Composition_Index = 1; - // } + Source->IsSelected = true; + /* + { + Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1); + Layer->Vertical_Offset = 1; + Layer->ColIndex = 3; + Layer->Block_Composition_Index = 0; + } + */ // { // Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); // block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, File->Layer_Count - 1); @@ -600,7 +635,6 @@ int main(int argc, char *argv[]) { SDL_Init(SDL_INIT_VIDEO); - Semaphore = SDL_CreateSemaphore(0); #if THREADED @@ -703,8 +737,15 @@ int main(int argc, char *argv[]) { ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + Brush_CalcBitmapAlphaFromSize(&Memory, &State->Brush, 4); + State_BindBrushTexture(&Memory, &State->Brush, 4); + + while (State->IsRunning) { + // State->Interact_Active = interact_type_layer_move; + // State->Interact_Offset[1] = -3.0f; + Main_InputTest(File, State, &Memory, &UI, window, textureID); if (State->IsPlaying) { diff --git a/main.h b/main.h index 7e07597..1b386ce 100644 --- a/main.h +++ b/main.h @@ -125,6 +125,7 @@ struct sorted_comp_info struct sorted_layer { uint16 Block_Layer_Index; + real32 SortedOffset; }; enum timeline_mode @@ -144,10 +145,11 @@ struct ui ImVec2 TimelinePercentZoomed; ImVec2 TimelinePercentOffset; - timeline_mode TimelineMode = timeline_mode_graph; + uint32 InteractTransformMode; // Whether a drag on the Shift+T UI is scale (1), rotation (2), or position (3). + + timeline_mode TimelineMode; bool32 BoxSelect; - ImVec2 DragDelta_Prev; // TODO(fox): Make native ImGui? focused_window FocusedWindow; // Convenience for adding window-specific hotkeys. @@ -160,23 +162,27 @@ struct ui real32 Warp_PositionInitial; int32 Warp_Direction; + v4 Color = {1, 1, 1, 1}; + v4 AltColor = {0, 0, 0, 1}; + bool32 IsPrimary; + ImU32 LayerColors[16] = { - 0x00050506, - 0x00806754, - 0x002d3f66, - 0x0044546e, - 0x00556780, - 0x005d7392, - 0x007e7b7e, - 0x00828282, - 0x00434344, - 0x00AB8A71, - 0x003C5588, - 0x005B7193, - 0x00728AAB, - 0x007C9AC3, - 0x00A9A5A8, - 0x00c0c0c0 + 0xff0000ff, + 0xff00ff00, + 0xffff0000, + 0xff44546e, + 0xff556780, + 0xff5d7392, + 0xff7e7b7e, + 0xff828282, + 0xff434344, + 0xffAB8A71, + 0xff3C5588, + 0xff5B7193, + 0xff728AAB, + 0xff7C9AC3, + 0xffA9A5A8, + 0xffc0c0c0 }; }; @@ -184,14 +190,70 @@ struct pen_state { bool32 IsActive; }; +enum brush_type +{ + brush_normal, + brush_wacky1, + brush_wacky2, + brush_wacky3, + brush_amount +}; + +char *BrushNames[brush_amount] = { + "brush_normal", + "brush_wacky1", + "brush_wacky2", + "brush_wacky3" +}; + +struct brush_state +{ + ImVec2 UIPos; // Initial position when ctrl is held + real32 Size = 15; // Maxes at 1024 for now + real32 Hardness = 1.0f; // From 1 to 100 + real32 Spacing = 1.0f; + brush_type Type = brush_normal; + GLuint GLTexture; + void *PaintBuffer; + rectangle CacheBounds; +}; + enum interact_type { interact_type_none, interact_type_layer_move, interact_type_layer_timeadjust, + interact_type_viewport_transform, interact_type_keyframe_move, interact_type_keyframe_scale, interact_type_keyframe_rotate, + interact_type_newlayer_paint +}; + +char *ToolName[] { + "Move", + "Crop", + "Brush", + "Pen" +}; + +enum tool { + tool_default, + tool_crop, + tool_brush, + tool_pen, + tool_count +}; + +struct interact_transform +{ + v2 Min; + v2 Max; + real32 Radians; + v2 Position; + real32 Scale = 1.0f; + ImVec2 OGPos; + uint32 TransformMode; }; struct project_state @@ -202,9 +264,13 @@ struct project_state render_state Render; - int32 Frame_Current; - // tool Tool = tool_default; + int32 Frame_Current; + tool Tool = tool_default; + // GLuint ToolIconTex[(int)tool_count]; pen_state Pen = {}; + brush_state Brush; + + char DummyName2[512]; bool32 IsRunning = 1; bool32 IsPlaying; @@ -214,7 +280,7 @@ struct project_state // selection_type RecentSelectionType = selection_none; interact_type Interact_Active; - real32 Interact_Offset[4]; + real32 Interact_Offset[12]; void *Interact_Address; int32 Initializing = 3; @@ -253,25 +319,36 @@ struct block_composition enum source_type { source_type_none, - source_type_video, - source_type_image + source_type_principal, + source_type_file +}; + +struct layer_transforms +{ + real32 x; + real32 y; + real32 ax; + real32 ay; + real32 rotation; + real32 scale; }; struct block_source { uint8 Occupied; + bool32 IsSelected; + uint16 Path_String_Index; uint16 Name_String_Index; - // Image and video + + // Only used for type_principal + uint16 Bitmap_Index; + uint16 Width; uint16 Height; uint16 BytesPerPixel; - // Video only - real32 FPS; - real32 AvgPTSPerFrame; // set by Libav - source_type Type; }; @@ -353,7 +430,7 @@ struct block_layer { real32 Vertical_Offset; real32 Vertical_Height = 1; - real32 Col[3]; + uint16 ColIndex; }; struct render_byte_info { diff --git a/memory.cpp b/memory.cpp index 3037eb0..6e70055 100644 --- a/memory.cpp +++ b/memory.cpp @@ -41,6 +41,58 @@ Memory_Block_AllocateAddress(memory *Memory, memory_table_list TableName) return Memory_Block_AddressAtIndex(Memory, TableName, FileIndex); } +// IMPORTANT(fox): All block data has to start with a uint8 Occupied variable! +static bool32 +Block_Loop(memory *Memory, memory_table_list TableName, uint32 TotalCount, int *HasIncremented, int *CurrentCount, int *Index) +{ + for (;;) { + if (*CurrentCount == TotalCount) { + return 0; + } + if (*HasIncremented) { + *HasIncremented = 0; + (*Index)++; + } + uint8 *Occupied = (uint8 *)Memory_Block_AddressAtIndex(Memory, TableName, *Index); + if (*Occupied) { + *HasIncremented = 1; + (*CurrentCount)++; + return 1; + } + (*Index)++; + Assert(*CurrentCount <= TotalCount); + Assert(*Index <= TotalCount*100); // This can get triggered normally if 100+ items are added and the first 99 in memory are deleted. + } + Assert(0); + return 0; +} + +static uint32 +Memory_Block_PrincipalBitmap_AllocateNew(project_data *File, project_state *State, memory *Memory) +{ + uint32 LastVal = 0; + uint32 LastBlock = 0; + uint32 c = 0; + uint32 r = 0; + while (r < File->Source_Count) { + block_source Source = *(block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, c); + if (Source.Occupied != 0) { + LastBlock = (Source.Bitmap_Index > LastBlock) ? Source.Bitmap_Index : LastBlock; + LastVal = r; + r++; + } + c++; + } + + block_source Source = *(block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, r); + uint32 BlockSize = ((Source.Width * Source.Height * Source.BytesPerPixel) / BitmapBlockSize) + 1; + + uint32 Blocks_Max = Memory->Slot[B_CachedBitmaps].Size / BitmapBlockSize; + Assert(Blocks_Max > (LastBlock + BlockSize)); + return LastBlock + BlockSize; + +} + static uint32 Memory_Block_Bitmap_AllocateNew(project_state *State, memory *Memory, cache_entry Entry, uint64 NewSize) { @@ -95,17 +147,37 @@ Memory_Block_Bitmap_AllocateNew(project_state *State, memory *Memory, cache_entr } +static void +Memory_Cache_Invalidate(project_state *State, memory *Memory, cache_entry_type Type, uint32 TypeInfo, uint32 TypeInfo_Sub) +{ + cache_entry *EntryArray = State->Render.Entry; + int c = 0; + int count = Memory->EntryCount; + while (count != 0) { + if (EntryArray[c].Type == Type && + EntryArray[c].TypeInfo == TypeInfo) { + EntryArray[c].IsOccupied = false; + Memory->EntryCount--; + } + c++; + count--; + } +} + static cache_entry * -Memory_Cache_Search(project_state *State, memory *Memory, cache_entry *EntryArray, cache_entry_type Type, uint32 TypeInfo, uint32 TypeInfo_Sub) +Memory_Cache_Search(project_state *State, memory *Memory, cache_entry_type Type, uint32 TypeInfo, uint32 TypeInfo_Sub) { + cache_entry *EntryArray = State->Render.Entry; int c = 0; - while (EntryArray[c].IsOccupied != 0) { + int count = Memory->EntryCount; + while (count != 0) { if (EntryArray[c].Type == Type && EntryArray[c].TypeInfo == TypeInfo && EntryArray[c].TypeInfo_Sub == TypeInfo_Sub) { return &EntryArray[c]; } c++; + count--; } if (c != 0) EntryArray[c].Block_StartIndex = Memory_Block_Bitmap_AllocateNew(State, Memory, EntryArray[c], 0); @@ -113,6 +185,7 @@ Memory_Cache_Search(project_state *State, memory *Memory, cache_entry *EntryArra EntryArray[c].Type = Type; EntryArray[c].TypeInfo = TypeInfo; EntryArray[c].TypeInfo_Sub = TypeInfo_Sub; + Memory->EntryCount++; return &EntryArray[c]; } diff --git a/memory.h b/memory.h index 3148138..2067e16 100644 --- a/memory.h +++ b/memory.h @@ -12,9 +12,9 @@ enum memory_table_list { F_Bezier, F_Effects, F_Strings, + F_PrincipalBitmaps, B_ScratchSpace, - // B_CachedBitmapInfo, B_CachedBitmaps, }; @@ -61,6 +61,7 @@ struct memory { memory_table Slot[16]; history_entry_list History; uint64 ScratchPos; + uint32 EntryCount; bool32 IsFileSaved; }; diff --git a/my_imgui_widgets.cpp b/my_imgui_widgets.cpp index c5a4049..e1e9ffc 100644 --- a/my_imgui_widgets.cpp +++ b/my_imgui_widgets.cpp @@ -3,6 +3,72 @@ #include "imgui_helper_widgets.cpp" +static void +ImGui_InteractSliderProperty(project_state *State, memory *Memory, property_channel *Property) +{ + ImGui::DragScalar(Property->Name, ImGuiDataType_Float, &Property->CurrentValue, + Property->ScrubVal, &Property->MinVal, &Property->MaxVal, "%f"); + /* + if (ImGui::IsItemActivated()) { + State->InteractCache[0] = Property->CurrentValue.f; + } + if (ImGui::IsItemActive()) { + State->UpdateFrame = true; + } + if (ImGui::IsItemDeactivatedAfterEdit()) { + if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { + Property->CurrentValue.f = State->InteractCache[0]; + } else { + History_Entry_Commit(Memory, action_entry_default, "Tranforms interact"); + History_Action_Change(Memory, &Property->CurrentValue.f, &State->InteractCache[0], + &Property->CurrentValue.f, action_type_change_r32); + History_Entry_End(Memory); + } + State->UpdateFrame = true; + } + */ +} + +static void +ImGui_PropertiesPanel(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io) +{ + bool32 Display = 1; + block_layer *Layer = NULL; + if (State->MostRecentlySelectedLayer > -1) { + Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, State->MostRecentlySelectedLayer); + if (!Layer->Occupied) + Display = 0; + } else { + Display = 0; + } + if (Display) { + block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Layer->Block_String_Index); + char buf[256]; + sprintf(buf, "Properties: %s###Properties", String->Char); + ImGui::Begin(buf); + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) + UI->FocusedWindow = focus_properties; + ImGui::Text("Transform"); + for (int h = 0; h < AmountOf(Layer->Property); h++) { + property_channel *Property = &Layer->Property[h]; + ImGui::PushID(Property); + ImGui::Button("K"); + // if (ImGui::Button("K")) + // Keyframe_Insert(Property, Memory, File->CurrentFrame, Property->CurrentValue.f); + ImGui::SameLine(); + ImGui_InteractSliderProperty(State, Memory, Property); + ImGui::PopID(); + } + ImGui::End(); + } else { + ImGui::Begin("Properties: empty###Properties"); + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) + UI->FocusedWindow = focus_properties; + ImGui::End(); + } +} + +#if DEBUG static void ImGui_DebugMemoryViewer(memory *Memory, project_state *State) { @@ -53,23 +119,103 @@ ImGui_DebugMemoryViewer(memory *Memory, project_state *State) } static void -ImGui_File(project_data *File, project_state *State, memory *Memory, ImGuiIO io) +ImGui_DebugUndoTree(memory *Memory, project_state *State) +{ + ImGui::Begin("undotree"); + for (int i = 0; i < Memory->History.NumberOfEntries; i++) { + history_entry Entry = Memory->History.Entry[i]; + bool32 CurrentPos = (i < Memory->History.EntryPlayhead); + ImGui::MenuItem(Entry.Name, NULL, CurrentPos); + } + ImGui::End(); +} +#endif + +static void +ImGui_File(project_data *File, project_state *State, memory *Memory, ImGuiIO io, + sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray) { ImGui::Begin("Files"); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + if (!ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) + Source_DeselectAll(File, Memory); + else if (ImGui::IsKeyPressed(ImGuiKey_Backspace)) { + + /* + uint64 SortSize = (sizeof(uint16) * File->Comp_Count); + void *SortedArray = Memory_PushScratch(Memory, SortSize); + uint16 *SelectedSourceIndex = (uint16 *)SortedArray; + int SelectedSourceCount = 0; + + int h = 0, c = 0, i = 0; + int SourceCount = File->Source_Count; + while (Block_Loop(Memory, F_Sources, SourceCount, &h, &c, &i)) { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i); + if (Source->IsSelected) { + SelectedSourceIndex[SelectedSourceCount] = i; + SelectedSourceCount++; + } + } + + h = 0, c = 0, i = 0; + int LayerCount = File->Layer_Count; + while (Block_Loop(Memory, F_Layers, LayerCount, &h, &c, &i)) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + for (int b = 0; b < SelectedSourceCount; b++) { + if (SelectedSourceIndex[b] == Layer->Block_Source_Index) { + } + } + } + + Memory_PopScratch(Memory, SortSize); + */ + + /* + bool32 CommitAction = 0; + while (Block_Loop(Memory, F_Sources, SourceCount, &h, &c, &i)) { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i); + if (Source->IsSelected) { + if (!CommitAction) { + History_Entry_Commit(Memory, "Delete source"); + CommitAction = 1; + } + Source_Delete(File, Memory, i); + } + } + if (CommitAction) + History_Entry_End(Memory); + */ + } + for (int c = 0; c < File->Comp_Count; c++) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, c); block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Comp->Name_String_Index); ImGui::Text(String->Char); } - for (int c = 0; c < File->Source_Count; c++) { - block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, c); + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Sources, File->Source_Count, &h, &c, &i)) { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i); block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Path_String_Index); - ImGui::Text(String->Char); + ImGui::Selectable(String->Char, Source->IsSelected); + if (ImGui::IsItemClicked() || ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + if (!io.KeyShift && !Source->IsSelected) { + Source_DeselectAll(File, Memory); + } + Source->IsSelected = 1; + } + ImGui::OpenPopupOnItemClick("sourcecontext", ImGuiPopupFlags_MouseButtonRight); + } + + if (ImGui::BeginPopup("sourcecontext")) { + if (ImGui::MenuItem("Create layer from source")) { + Source_UICreateButton(File, State, Memory, SortedCompArray, SortedLayerArray); + } + ImGui::EndPopup(); } #if DEBUG + for (int i = 0; i < Debug.Temp.WatchedProperties; i++) { if (Debug.Temp.DebugPropertyType[i] == d_float) { ImGui::Text("%s: %f", Debug.Temp.String[i], Debug.Temp.Val[i].f); @@ -84,7 +230,410 @@ ImGui_File(project_data *File, project_state *State, memory *Memory, ImGuiIO io) } static void -ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io, GLuint textureID) +ImGui_ColorPanel(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io) +{ + ImGuiStyle& style = ImGui::GetStyle(); + + ImGui::Begin("Colors"); + + ImGuiColorEditFlags flags_primary = ImGuiColorEditFlags_AlphaPreview | + ImGuiColorEditFlags_Float; + ImGuiColorEditFlags flags_picker = ImGuiColorEditFlags_PickerHueBar | + ImGuiColorEditFlags_AlphaBar | + ImGuiColorEditFlags_NoSmallPreview | + ImGuiColorEditFlags_NoSidePreview | + ImGuiColorEditFlags_DisplayRGB | + ImGuiColorEditFlags_DisplayHSV | + ImGuiColorEditFlags_DisplayHex; + + // Dim window if it's not active so there's not a big saturation square in + // the corner of my vision while I'm editing. Personal preference. + real32 AlphaMult = (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) ? 1.0f : 0.3f; + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, style.Alpha * AlphaMult); + + ImGui::ColorPicker4("##maincolorpicker", &UI->Color.r, flags_primary | flags_picker); + + ImGui::PopStyleVar(); + + if (ImGui::ColorButton("##primarycolor", *(ImVec4*)&UI->Color.r, flags_primary, ImVec2(20, 20))) + { + v4 Temp = UI->Color; + UI->Color = UI->AltColor; + UI->AltColor = Temp; + } + if (ImGui::ColorButton("##secondarycolor", *(ImVec4*)&UI->AltColor.r, flags_primary, ImVec2(20, 20))) + { + v4 Temp = UI->Color; + UI->Color = UI->AltColor; + UI->AltColor = Temp; + } + + if (State->Tool == tool_brush) { + real32 BrushSizeMin = 0; + real32 BrushSizeMax = 1024; + real32 BrushHardnessMin = 0.5; + real32 BrushHardnessMax = 100; + real32 BrushSpacingMin = 0.1; + real32 BrushSpacingMax = 100; + if (ImGui::DragScalar("Size", ImGuiDataType_Float, &State->Brush.Size, 1, &BrushSizeMin, &BrushSizeMax, "%.3f")) { + Brush_CalcBitmapAlphaFromSize(Memory, &State->Brush, 4); + State_BindBrushTexture(Memory, &State->Brush, 4); + } + if (ImGui::DragScalar("Hardness", ImGuiDataType_Float, &State->Brush.Hardness, 1, &BrushHardnessMin, &BrushHardnessMax, "%.3f", ImGuiSliderFlags_Logarithmic)) { + Brush_CalcBitmapAlphaFromSize(Memory, &State->Brush, 4); + State_BindBrushTexture(Memory, &State->Brush, 4); + } + if (ImGui::DragScalar("Spacing", ImGuiDataType_Float, &State->Brush.Spacing, 1, &BrushSpacingMin, &BrushSpacingMax, "%.3f", ImGuiSliderFlags_Logarithmic)) { + Brush_CalcBitmapAlphaFromSize(Memory, &State->Brush, 4); + State_BindBrushTexture(Memory, &State->Brush, 4); + } + ImGui::Button(BrushNames[State->Brush.Type]); + ImGui::OpenPopupOnItemClick("brush_picker", ImGuiPopupFlags_MouseButtonLeft); + if (ImGui::BeginPopup("brush_picker")) { + for (int16 b = 0; b < brush_amount; b++) { + if (ImGui::MenuItem(BrushNames[b], NULL, false, State->Brush.Type != b)) { + State->Brush.Type = (brush_type)b; + } + } + ImGui::EndPopup(); + } + } + + ImGui::End(); +} + +static void +ImGui_Toolbar(project_state *State, ImDrawList *draw_list) +{ + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + // ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(50, 50, 50, 0)); + + real32 IconSize = ImGui::GetFontSize() * 4; + int ToolCount = (int)tool_count; + ImVec2 ButtonSize(IconSize, IconSize); + ImVec2 WindowSize(IconSize, IconSize * ToolCount); + ImGui::BeginChild("Toolbar", WindowSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar); + for (int i = 0; i < ToolCount; i++) { + ImGui::PushID(i); + // draw_list->AddImage((void *)(intptr_t)State->ToolIconTex[i], Min, Max); + if ((int)State->Tool == i) { + ImVec2 Min = ImGui::GetCursorScreenPos(); + ImVec2 Max = Min + ButtonSize; + draw_list->AddRectFilled(Min, Max, IM_COL32(255, 255, 255, 128)); + } + ImGui::Button(ToolName[i], ButtonSize); + if (ImGui::IsItemActivated()) { + State->Tool = (tool)i; + } + ImGui::PopID(); + } + ImGui::EndChild(); + + // ImGui::PopStyleColor(); + ImGui::PopStyleVar(2); +} + +static void +ImGui_RenderUIBrush(project_state *State, memory *Memory, ImVec2 ViewportMin, ImVec2 ViewportMax, ImVec2 CompZoom, ImGuiIO io, uint16 Width, uint16 Height) +{ + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + if (State->Tool == tool_brush) { + + if (ImGui::IsKeyPressed(ImGuiKey_ModAlt, false)) { + State->Brush.UIPos = io.MousePos; + } + + ImVec2 CompScale = CompZoom / ImVec2(Width, Height); + ImVec2 BrushSize = CompScale * State->Brush.Size; + ImVec2 MinBounds = State->Brush.UIPos - BrushSize/2; + ImVec2 MaxBounds = MinBounds + BrushSize; + + if (io.KeyAlt) { + draw_list->PushClipRect(ViewportMin, ViewportMax, true); + draw_list->AddImage((void *)(intptr_t)State->Brush.GLTexture, MinBounds, MaxBounds, ImVec2(0, 0), ImVec2(1, 1), 1); + draw_list->PopClipRect(); + ImGui::SetCursorScreenPos(State->Brush.UIPos); + char buf[256]; + sprintf(buf, "Size: %.1f, Hardness: %.1f", State->Brush.Size, State->Brush.Hardness); + ImGui::Text(buf); + if (io.MouseDelta.x || io.MouseDelta.y) { + ImVec2 Delta = io.MouseDelta; + State->Brush.Size += Delta.x; + State->Brush.Hardness += Delta.y*State->Brush.Hardness/100; + if (State->Brush.Size < 0) + State->Brush.Size = 0; + if (State->Brush.Size > 1024) + State->Brush.Size = 1024; + if (State->Brush.Hardness < 0.5) + State->Brush.Hardness = 0.5; + if (State->Brush.Hardness > 100) + State->Brush.Hardness = 100; + Brush_CalcBitmapAlphaFromSize(Memory, &State->Brush, 4); + State_BindBrushTexture(Memory, &State->Brush, 4); + } + } + } +} + +static void +ImGui_TransformUI(project_data *File, project_state *State, memory *Memory, ui *UI, ImDrawList *draw_list, ImGuiIO &io, interact_transform *Interact, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight) +{ + v2 InteractMin = Interact->Min + Interact->Position; + v2 InteractMax = Interact->Max + Interact->Position; + + v2 BoxLength = InteractMax - InteractMin; + v2 Center = InteractMax - (BoxLength/2); + + real32 Point0X = Center.x - InteractMin.x; + real32 Point0Y = Center.y - InteractMin.y; + + real32 Rad = Interact->Radians; + + v2 XAxis = (Point0X * Interact->Scale)*V2(cos(Rad), sin(Rad)); + v2 YAxis = (Point0Y * -Interact->Scale)*V2(sin(Rad), -cos(Rad)); + + // Points are clockwise starting from the top left. + real32 X0 = -XAxis.x - YAxis.x + Center.x; + real32 Y0 = -XAxis.y - YAxis.y + Center.y; + real32 X1 = X0 + XAxis.x*2; + real32 Y1 = Y0 + XAxis.y*2; + real32 X2 = X1 + YAxis.x*2; + real32 Y2 = Y1 + YAxis.y*2; + real32 X3 = X2 - XAxis.x*2; + real32 Y3 = Y2 - XAxis.y*2; + + // Midway points. + real32 Mid_X0 = X0 + XAxis.x; + real32 Mid_Y0 = Y0 + XAxis.y; + real32 Mid_X1 = X1 + YAxis.x; + real32 Mid_Y1 = Y1 + YAxis.y; + real32 Mid_X2 = X2 - XAxis.x; + real32 Mid_Y2 = Y2 - XAxis.y; + real32 Mid_X3 = X3 - YAxis.x; + real32 Mid_Y3 = Y3 - YAxis.y; + + ImVec2 CompScale = UI->CompZoom / ImVec2(CompWidth, CompHeight); + + ImVec2 P[4]; + P[0] = ImVec2(X0, Y0)*CompScale + UI->CompPos; + P[1] = ImVec2(X1, Y1)*CompScale + UI->CompPos; + P[2] = ImVec2(X2, Y2)*CompScale + UI->CompPos; + P[3] = ImVec2(X3, Y3)*CompScale + UI->CompPos; + + ImVec2 Mid_P[4]; + Mid_P[0] = ImVec2(Mid_X0, Mid_Y0)*CompScale + UI->CompPos; + Mid_P[1] = ImVec2(Mid_X1, Mid_Y1)*CompScale + UI->CompPos; + Mid_P[2] = ImVec2(Mid_X2, Mid_Y2)*CompScale + UI->CompPos; + Mid_P[3] = ImVec2(Mid_X3, Mid_Y3)*CompScale + UI->CompPos; + + ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text); + draw_list->AddLine(P[0], P[1], wcol, 2.0f); + draw_list->AddLine(P[1], P[2], wcol, 2.0f); + draw_list->AddLine(P[2], P[3], wcol, 2.0f); + draw_list->AddLine(P[3], P[0], wcol, 2.0f); + + v2 XAxis2 = (BoxLength*CompScale.x)*V2(cos(Rad), sin(Rad)); + v2 YAxis2 = (BoxLength*CompScale.y)*V2(sin(Rad), -cos(Rad)); + + v2 XAxisPerp = (1.0f / LengthSq(XAxis))*XAxis; + v2 YAxisPerp = (1.0f / LengthSq(YAxis))*YAxis; + + // real32 LocalX = ((io.MousePos.x - UI->CompPos.x) - Center.x) ; + // real32 LocalY = ((io.MousePos.y - UI->CompPos.y) - Center.y) ; + layer_transforms BoxTransforms = { Center.x, Center.y, 0.5, 0.5, (real32)(Interact->Radians / (PI / 180)), Interact->Scale }; + v2 LayerPoint = Transform_ScreenSpaceToLocal(BoxTransforms, CompWidth, CompHeight, BoxLength.x, BoxLength.y, *UI, ViewportMin, io.MousePos); + + real32 U = LayerPoint.x / BoxLength.x; + real32 V = LayerPoint.y / BoxLength.y; + + ImVec2 ScaleHandleSize(50, 50); + + bool32 OtherActions = ImGui::IsKeyDown(ImGuiKey_Z); + + // First do the halfway scale points, since they don't need UVs considered: + for (int i = 0; i < 4; i++) { + ImGui::SetCursorScreenPos(Mid_P[i] - ScaleHandleSize/2); + ImGui::PushID(i); + + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertFloat4ToU32(ImVec4(0.6f, 0.0f, 0.3f, 1.0f))); + ImGui::Button("##ScaleMids", ScaleHandleSize); + ImGui::PopStyleColor(); + + if (ImGui::IsItemActivated() && !OtherActions) { + UI->InteractTransformMode = 1; + } + + if (UI->InteractTransformMode == 1 && ImGui::IsItemActive()) + { + uint32 side = i; + if (side == 0) { + Interact->Scale -= io.MouseDelta.y / BoxLength.y; + Interact->Position.y += io.MouseDelta.y / 2; + } else if (side == 1) { + Interact->Scale += io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + } else if (side == 2) { + Interact->Scale += io.MouseDelta.y / BoxLength.y; + Interact->Position.y += io.MouseDelta.y / 2; + } else if (side == 3) { + Interact->Scale -= io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + } + } + ImGui::PopID(); + } + + bool32 InBounds = false; + // Scale if cursor is on button within the UV, rotate if outside UV, and position if a non-button is dragged. + if (U >= 0.0f && U <= 1.0f && V >= 0.0f && V <= 1.0f) + { + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertFloat4ToU32(ImVec4(0.6f, 0.0f, 0.3f, 1.0f))); + InBounds = true; + } + + for (int i = 0; i < 4; i++) { + ImGui::SetCursorScreenPos(P[i] - ScaleHandleSize/2); + ImGui::PushID(i); + ImGui::Button("##ScaleRotateCorners", ScaleHandleSize); + + if (ImGui::IsItemActivated() && !OtherActions) { + if (InBounds) + UI->InteractTransformMode = 1; + else + UI->InteractTransformMode = 2; + } + + // Scale part + if (UI->InteractTransformMode == 1 && ImGui::IsItemActive()) + { + // TODO(fox): Corner dragging scale only works in the X + // axis. Mostly feels right when dragged how you expect, + // but I'll fix it if someone complains. + uint32 side = i; + if (side == 0) { + Interact->Scale -= io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + Interact->Position.y += io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2; + } else if (side == 1) { + Interact->Scale += io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + Interact->Position.y -= io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2; + } else if (side == 2) { + Interact->Scale += io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + Interact->Position.y += io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2; + } else if (side == 3) { + Interact->Scale -= io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + Interact->Position.y -= io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2; + } + } + + // Rotation part + if (UI->InteractTransformMode == 2 && ImGui::IsItemActive()) + { + real32 LocalX = (io.MousePos.x - UI->CompPos.x)/CompScale.x - InteractMin.x - (BoxLength.x/2); + real32 LocalY = (io.MousePos.y - UI->CompPos.y)/CompScale.y - InteractMin.y - (BoxLength.y/2); + + real32 Slope_Mouse = LocalY/LocalX; + real32 Slope_Corner = 0; + real32 Slope_Flipped = 0; + real32 Dot = 0; + + // TODO(fox) learn basic geometry to do this properly + + // We find the angle between the direction of whichever corner the + // mouse is grabbing (Slope_Corner) and the mouse's current + // position (Slope_Mouse) to get ExtraRadians. The calculation only + // works between -90 and 90, so I take the dot product of the + // opposite edge of the corner and add the extra degrees when it's negative. + + v2 SlopeDot = V2(BoxLength.x, BoxLength.y); + // top left clockwise + uint32 side = i; + if (side == 0) { + Slope_Corner = BoxLength.y / BoxLength.x; + Slope_Flipped = -BoxLength.x / BoxLength.y; + Dot = LocalX * -SlopeDot.x + LocalY * -SlopeDot.y; + } else if (side == 1) { + Slope_Corner = -BoxLength.y / BoxLength.x; + Slope_Flipped = BoxLength.x / BoxLength.y; + Dot = LocalX * SlopeDot.x + LocalY * -SlopeDot.y; + } else if (side == 2) { + Slope_Corner = BoxLength.y / BoxLength.x; + Slope_Flipped = -BoxLength.x / BoxLength.y; + Dot = LocalX * SlopeDot.x + LocalY * SlopeDot.y; + } else if (side == 3) { + Slope_Corner = -BoxLength.y / BoxLength.x; + Slope_Flipped = BoxLength.x / BoxLength.y; + Dot = LocalX * -SlopeDot.x + LocalY * SlopeDot.y; + } + + Interact->Radians = atan((Slope_Mouse - Slope_Corner) / (1 + Slope_Mouse * Slope_Corner)); + real32 ExtraRadians2 = atan((Slope_Mouse - Slope_Flipped) / (1 + Slope_Mouse * Slope_Flipped)); + + if (Dot < 0) { + if (Interact->Radians < 0) { + Interact->Radians = (90 * (PI / 180)) + ExtraRadians2; + } else { + Interact->Radians = (-90 * (PI / 180)) + ExtraRadians2; + } + } + } + + ImGui::PopID(); + } + + if (!UI->InteractTransformMode && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && InBounds && !OtherActions) + UI->InteractTransformMode = 3; + + if (UI->InteractTransformMode == 3) { + Interact->Position.x += (real32)io.MouseDelta.x/CompScale.x; + Interact->Position.y += (real32)io.MouseDelta.y/CompScale.y; + } + + if (UI->InteractTransformMode) + { + if (io.MouseDelta.x || io.MouseDelta.y) + State->UpdateFrame = true; + if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) + UI->InteractTransformMode = 0; + } + + if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { + State->UpdateFrame = true; + } + + // Second condition so you don't have to reach for Enter. + if (ImGui::IsKeyPressed(ImGuiKey_Enter) || (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && io.KeyCtrl)) { + int h = 0, c = 0, i = 0; + History_Entry_Commit(Memory, "Transform layers"); + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + if (Layer->IsSelected) { + History_Action_Swap(Memory, F_File, sizeof(Layer->x.CurrentValue), &Layer->x.CurrentValue); + History_Action_Swap(Memory, F_File, sizeof(Layer->y.CurrentValue), &Layer->y.CurrentValue); + History_Action_Swap(Memory, F_File, sizeof(Layer->scale.CurrentValue), &Layer->scale.CurrentValue); + History_Action_Swap(Memory, F_File, sizeof(Layer->rotation.CurrentValue), &Layer->rotation.CurrentValue); + Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &Layer->x.CurrentValue, &Layer->y.CurrentValue, &Layer->rotation.CurrentValue, &Layer->scale.CurrentValue); + } + } + History_Entry_End(Memory); + State->Interact_Active = interact_type_none; + State->UpdateFrame = true; + } + + if (InBounds == true) { + ImGui::PopStyleColor(); + } + +} + +static void +ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io, GLuint textureID, + sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray) { bool open = true; ImGui::Begin("Viewport", &open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); @@ -119,6 +668,42 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, draw_list->AddImage((void *)(intptr_t)textureID, CompPosMin, CompPosMax); draw_list->PopClipRect(); + ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text); + // UI+interaction for layer + if (State->MostRecentlySelectedLayer > -1) + { + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) + { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + if (Layer->IsSelected) { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); + + // Anchor point UI + v2 CenterPoint = V2(Source->Width*Layer->ax.CurrentValue, Source->Height*Layer->ay.CurrentValue); + ImVec2 ScreenAP = Layer_LocalToScreenSpace(State, Layer, UI, Source->Width, Source->Height, MainComp->Width, MainComp->Height, CenterPoint); + draw_list->AddNgon(ScreenAP, 20, wcol, 8, 10.0f); + + // Bounding box UI + ImVec2 P1 = Layer_LocalToScreenSpace(State, Layer, UI, Source->Width, Source->Height, MainComp->Width, MainComp->Height, V2(0, 0)); + ImVec2 P2 = Layer_LocalToScreenSpace(State, Layer, UI, Source->Width, Source->Height, MainComp->Width, MainComp->Height, V2(Source->Width, 0)); + ImVec2 P3 = Layer_LocalToScreenSpace(State, Layer, UI, Source->Width, Source->Height, MainComp->Width, MainComp->Height, V2(0, Source->Height)); + ImVec2 P4 = Layer_LocalToScreenSpace(State, Layer, UI, Source->Width, Source->Height, MainComp->Width, MainComp->Height, V2(Source->Width, Source->Height)); + draw_list->AddLine(P1, P2, wcol, 2.0f); + draw_list->AddLine(P2, P4, wcol, 2.0f); + draw_list->AddLine(P1, P3, wcol, 2.0f); + draw_list->AddLine(P3, P4, wcol, 2.0f); + + } + } + + if (State->Interact_Active == interact_type_viewport_transform) { + ImGui_TransformUI(File, State, Memory, UI, draw_list, io, (interact_transform *)&State->Interact_Offset[0], ViewportMin, MainComp->Width, MainComp->Height); + } + } + + + // Interactions for dragging and zooming ImGui::SetCursorScreenPos(ViewportMin); @@ -133,29 +718,83 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, // Point to zoom in on if Z is held UI->TempZoomRatio = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos); + if (State->Tool == tool_brush && File->Layer_Count != 2) { + State->Interact_Active = interact_type_newlayer_paint; + History_Entry_Commit(Memory,"Paint new layer"); + uint16 SourceIndex = Source_Generate_Blank(File, State, Memory, MainComp->Width, MainComp->Height, MainComp->BytesPerPixel); + Assert(0); + // Layer_CreateFromSource(File, State, Memory, SourceIndex, MainComp->Frame_End); + History_Entry_End(Memory); + } + // Layer selection - /* - if (!ImGui::IsKeyDown(ImGuiKey_Z) || !State->Pen.IsActive) { - for (int i = File.NumberOfLayers - 1; i >= 0; i--) { - project_layer *Layer = File.Layer[i]; - if (!io.KeyShift) DeselectAllLayers(&File, State); - v2 LayerUV = CompUVToLayerUV(Layer, &CompBuffer, UI->TempZoomRatio); - if (TestUV(LayerUV) && !Layer->IsSelected) - { - SelectLayer(Layer, State, i); - break; - } - } + if (!ImGui::IsKeyDown(ImGuiKey_Z) && State->Tool == tool_default && State->Interact_Active == interact_type_none) { + int32 Selection = Layer_TestSelection(Memory, State, UI, SortedCompArray, SortedLayerArray, File->PrincipalCompIndex); + if (!io.KeyShift && State->Interact_Active == interact_type_none) + Layer_DeselectAll(Memory, File->Layer_Count); + if (Selection != -1) + Layer_Select(Memory, State, Selection); } - */ } + /* + if (State->Interact_Active == interact_type_viewport_transform) { + interact_transform *Interact = (interact_transform *)&State->Interact_Offset[0]; + ImVec2 DragDelta = io.MousePos - Interact->OGPos; + Interact->Position = V2(DragDelta.x, DragDelta.y); + if (io.MouseDelta.x || io.MouseDelta.y) + State->UpdateFrame = true; + } + */ + if (IsActive && ImGui::IsMouseDragging(ImGuiMouseButton_Right, -1.0f)) { UI->CompPos.x += io.MouseDelta.x; UI->CompPos.y += io.MouseDelta.y; } + bool32 OtherActions = ImGui::IsKeyDown(ImGuiKey_Z) || ImGui::IsMouseDown(ImGuiMouseButton_Right); + if (State->Tool == tool_brush) { + if (IsActive && !OtherActions) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, 0); + layer_transforms T_Layer = Layer_GetTransforms(Layer); + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); + void *SourceBitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index); + ImVec2 MouseDelta = io.MouseDelta; + real32 Delta = MouseDelta.x + MouseDelta.y; + if (Delta != 0.0f) { + real32 DeltaDistance = sqrt(MouseDelta.x * MouseDelta.x + MouseDelta.y * MouseDelta.y); + real32 DeltaSlope = MouseDelta.y / MouseDelta.x; + for (real32 i = 0; i < DeltaDistance; i += State->Brush.Spacing) { + ImVec2 MousePos; + if (State->Brush.Type == brush_normal) { + MousePos = io.MousePos - (MouseDelta * (i / DeltaDistance)); + } else if (State->Brush.Type == brush_wacky1) { + MousePos = io.MousePos + (io.MousePos * (i / MouseDelta)); + } else if (State->Brush.Type == brush_wacky2) { + MousePos = io.MousePos - (MouseDelta / (i / DeltaDistance)); + } else if (State->Brush.Type == brush_wacky3) { + MousePos = io.MousePos - (MouseDelta * (i / ImVec2(MouseDelta.y, MouseDelta.x))); + } else { + Assert(0); + } + v2 LayerPos = Transform_ScreenSpaceToLocal(T_Layer, MainComp->Width, MainComp->Height, Source->Width, Source->Height, *UI, ViewportMin, MousePos); + PaintTest(Memory, Source, &State->Brush, SourceBitmapAddress, LayerPos, 4, UI->Color); + } + } else if (IsActivated) { + ImVec2 MousePos = io.MousePos; + v2 LayerPos = Transform_ScreenSpaceToLocal(T_Layer, MainComp->Width, MainComp->Height, Source->Width, Source->Height, *UI, ViewportMin, MousePos); + PaintTest(Memory, Source, &State->Brush, SourceBitmapAddress, LayerPos, 4, UI->Color); + } + Memory_Cache_Invalidate(State, Memory, cache_entry_type_comp, Layer->Block_Composition_Index, 0); + State->UpdateFrame = true; + } + + if (IsDeactivated) { + State->Interact_Active = interact_type_none; + } + } + if (IsActive && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1.0f) && ImGui::IsKeyDown(ImGuiKey_Z)) { real32 Distance = io.MouseDelta.x + io.MouseDelta.y; @@ -175,8 +814,12 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, State->MsgTime--; } - ImGui::End(); + ImGui::SetCursorScreenPos(ViewportMin); + ImGui_Toolbar(State, draw_list); + ImGui_RenderUIBrush(State, Memory, ViewportMin, ViewportMax, UI->CompPos, io, MainComp->Width, MainComp->Height); + + ImGui::End(); /* for (int i = 0; i < AmountOf(Layer->Property); i++) { @@ -192,10 +835,11 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, } static void -ImGui_TimelineHorizontalIncrementDraw(ui *UI, ImDrawList *draw_list, ImVec2 TimelineSizeWithBorder, ImVec2 TimelineAbsolutePos, block_composition MainComp, +ImGui_TimelineHorizontalIncrementDraw(project_state *State, ui *UI, ImDrawList *draw_list, ImVec2 TimelineSizeWithBorder, ImVec2 TimelineAbsolutePos, block_composition MainComp, ImVec2 TimelineZoomSize, ImVec2 TimelineMoveSize) { uint32 LineColor = IM_COL32(200, 200, 200, 40); + uint32 PlayheadColor = IM_COL32(000, 000, 200, 160); Assert(TimelineZoomSize.x > 0.0f); @@ -226,6 +870,10 @@ ImGui_TimelineHorizontalIncrementDraw(ui *UI, ImDrawList *draw_list, ImVec2 Time RightmostEdge = true; } } + + ImVec2 Min = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize.x + ((real32)State->Frame_Current / MainComp.Frame_Count)*TimelineZoomSize.x, TimelineAbsolutePos.y); + ImVec2 Max = ImVec2(Min.x + 2, TimelineAbsolutePos.y + TimelineSizeWithBorder.y); + draw_list->AddLine(Min, Max, PlayheadColor); } @@ -235,16 +883,17 @@ ImGui_GraphInfo(project_data *File, project_state *State, memory *Memory, ui *UI bool open = true; ImGui::Begin("Graph info", &open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); - for (int a = 0; a < File->Layer_Count; a++) + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { - block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, a); + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); if (!Layer->IsSelected) continue; block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Layer->Block_String_Index); ImGui::Text(String->Char); - ImGui::PushID(a); + ImGui::PushID(i); for (int h = 0; h < AmountOf(Layer->Property); h++) { property_channel *Property = &Layer->Property[h]; if (Property->Block_Bezier_Count) { @@ -267,9 +916,10 @@ ImGui_Timeline_DrawGraph(project_data *File, project_state *State, memory *Memor { UI->Test.Split(draw_list, 2); - for (int a = 0; a < File->Layer_Count; a++) + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { - block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, a); + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); if (!Layer->IsSelected) continue; @@ -285,7 +935,7 @@ ImGui_Timeline_DrawGraph(project_data *File, project_state *State, memory *Memor ImVec2 Layer_ScreenPos_Max = TimelineAbsolutePos + TimelineMoveSize + ((Layer_LocalPos_Ratio + Layer_LocalSize_Ratio) * TimelineZoomSize); ImVec2 Layer_ScreenSize = Layer_ScreenPos_Max - Layer_ScreenPos_Min; - ImGui::PushID(a); + ImGui::PushID(i); ImU32 col = IM_COL32(255, 255, 255, 255); @@ -304,6 +954,8 @@ ImGui_Timeline_DrawGraph(project_data *File, project_state *State, memory *Memor State->Interact_Active == interact_type_keyframe_rotate || State->Interact_Active == interact_type_keyframe_scale)) { + Assert(0); + // Memory_Cache_Invalidate(State, Memory, cache_entry_type_comp, Lay } for (int h = 0; h < AmountOf(Layer->Property); h++) { @@ -445,9 +1097,9 @@ ImGui_Timeline_DrawPrecomp(project_data *File, project_state *State, memory *Mem int32 Frame_Start = Layer->Frame_Start; int32 Frame_End = Layer->Frame_End; - real32 Vertical_Offset = Layer->Vertical_Offset; - if (Layer->IsSelected) - Layer_Interact_Evaluate(Memory, State, Index_Physical, SortedCompInfo, SortedLayerInfo, &Frame_Start, &Frame_End, &Vertical_Offset); + real32 Vertical_Offset = SortEntry.SortedOffset; + // if (Layer->IsSelected) + // Layer_Interact_Evaluate(Memory, State, Index_Physical, SortedCompInfo, SortedLayerInfo, &Frame_Start, &Frame_End, &Vertical_Offset); ImVec2 Layer_LocalPos = ImVec2(Frame_Start, Vertical_Offset); ImVec2 Layer_LocalSize = ImVec2(Frame_End - Frame_Start, Layer->Vertical_Height); @@ -489,8 +1141,7 @@ ImGui_Timeline_DrawPrecomp(project_data *File, project_state *State, memory *Mem if (ImGui::IsItemActivated()) { if (!Layer->IsSelected) { if (!io.KeyShift) Layer_DeselectAll(Memory, File->Layer_Count); - State->MostRecentlySelectedLayer = i; - Layer->IsSelected = true; + Layer_Select(Memory, State, Index_Physical); } } if (ImGui::IsItemActive()) { @@ -506,15 +1157,19 @@ ImGui_Timeline_DrawPrecomp(project_data *File, project_state *State, memory *Mem } if (ImGui::IsItemDeactivated()) { if (State->Interact_Active == interact_type_layer_timeadjust) { - for (int a = 0; a < File->Layer_Count; a++) { - block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, a); - if (Layer->IsSelected) { - Layer_Interact_Evaluate(Memory, State, Index_Physical, SortedCompInfo, SortedLayerInfo, &Layer->Frame_Start, &Layer->Frame_End, &Layer->Vertical_Offset); - } + Assert(0); + /* + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + // if (Layer->IsSelected) { + // Layer_Interact_Evaluate(Memory, State, Index_Physical, SortedCompInfo, SortedLayerInfo, &Layer->Frame_Start, &Layer->Frame_End, &Layer->Vertical_Offset); + // } } State->Interact_Active = interact_type_none; State->Interact_Offset[0] = 0; State->Interact_Offset[1] = 0; + */ } } @@ -527,8 +1182,7 @@ ImGui_Timeline_DrawPrecomp(project_data *File, project_state *State, memory *Mem if (ImGui::IsItemActivated()) { if (!Layer->IsSelected) { if (!io.KeyShift) Layer_DeselectAll(Memory, File->Layer_Count); - State->MostRecentlySelectedLayer = i; - Layer->IsSelected = true; + Layer_Select(Memory, State, Index_Physical); } } @@ -544,32 +1198,32 @@ ImGui_Timeline_DrawPrecomp(project_data *File, project_state *State, memory *Mem ImVec2 DragDelta = ImGui::GetMouseDragDelta(); DragDelta = DragDelta + (ImVec2(UI->Warp_X, UI->Warp_Y) * TimelineSize); - State->Interact_Offset[0] = (DragDelta.x / TimelineSizeWithBorder.x * UI->TimelinePercentZoomed.x) * Comp->Frame_Count; - State->Interact_Offset[1] = (DragDelta.y / TimelineSizeWithBorder.y * UI->TimelinePercentZoomed.y) * LayerIncrement; + ImVec2 Offset_Old = ImVec2(State->Interact_Offset[0], State->Interact_Offset[1]); + ImVec2 Offset_New = (DragDelta / TimelineSizeWithBorder * UI->TimelinePercentZoomed) * ImVec2(Comp->Frame_Count, -LayerIncrement); - /* - if (UI->DragDelta_Prev.x != 0) { - ImVec2 Offset_Old = (UI->DragDelta_Prev / TimelineSizeWithBorder * UI->TimelinePercentZoomed) * ImVec2(MainComp->Frame_Count, LayerIncrement); - if ((int32)State->Interact_Offset[1] != (int32)Offset_Old.y) - State->UpdateFrame = true; - } + // if (((int32)Offset_Old.x != (int32)Offset_New.x) || ((int32)Offset_Old.y != (int32)Offset_New.y)) + State->UpdateFrame = true; - UI->DragDelta_Prev = DragDelta; - */ + State->Interact_Offset[0] = Offset_New.x; + State->Interact_Offset[1] = Offset_New.y; } } if (ImGui::IsItemDeactivated()) { if (State->Interact_Active == interact_type_layer_move) { - for (int a = 0; a < File->Layer_Count; a++) { - block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, a); + History_Entry_Commit(Memory, "Move layers"); + for (int i = 0; i < SortedCompInfo.LayerCount; i++) + { + sorted_layer SortEntry = SortedLayerInfo[i]; + uint32 Index_Physical = SortEntry.Block_Layer_Index; + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); if (Layer->IsSelected) { - Layer_Interact_Evaluate(Memory, State, Index_Physical, SortedCompInfo, SortedLayerInfo, &Layer->Frame_Start, &Layer->Frame_End, &Layer->Vertical_Offset); + History_Action_Swap(Memory, F_File, sizeof(Layer->Vertical_Offset), &Layer->Vertical_Offset); + Layer->Vertical_Offset = SortEntry.SortedOffset; } } State->Interact_Active = interact_type_none; - State->Interact_Offset[0] = 0; - State->Interact_Offset[1] = 0; + History_Entry_End(Memory); } } ImGui::PopID(); @@ -588,9 +1242,9 @@ ImGui_Timeline_DrawPrecomp(project_data *File, project_state *State, memory *Mem int32 Frame_Start = Layer->Frame_Start; int32 Frame_End = Layer->Frame_End; - real32 Vertical_Offset = Layer->Vertical_Offset; - if (Layer->IsSelected) - Layer_Interact_Evaluate(Memory, State, Index_Physical, SortedCompInfo, SortedLayerInfo, &Frame_Start, &Frame_End, &Vertical_Offset); + real32 Vertical_Offset = SortEntry.SortedOffset; + // if (Layer->IsSelected) + // Layer_Interact_Evaluate(Memory, State, Index_Physical, SortedCompInfo, SortedLayerInfo, &Frame_Start, &Frame_End, &Vertical_Offset); ImVec2 Layer_LocalPos = ImVec2(Frame_Start, Vertical_Offset); ImVec2 Layer_LocalSize = ImVec2(Frame_End - Frame_Start, Layer->Vertical_Height); @@ -643,9 +1297,9 @@ ImGui_Timeline_DrawPrecomp(project_data *File, project_state *State, memory *Mem int32 Frame_Start = Layer->Frame_Start; int32 Frame_End = Layer->Frame_End; - real32 Vertical_Offset = Layer->Vertical_Offset; - if (Layer->IsSelected) - Layer_Interact_Evaluate(Memory, State, Index_Physical, SortedCompInfo, SortedLayerInfo, &Frame_Start, &Frame_End, &Vertical_Offset); + real32 Vertical_Offset = SortEntry.SortedOffset; + // if (Layer->IsSelected) + // Layer_Interact_Evaluate(Memory, State, Index_Physical, SortedCompInfo, SortedLayerInfo, &Frame_Start, &Frame_End, &Vertical_Offset); ImVec2 Layer_LocalPos = ImVec2(Frame_Start, Vertical_Offset); ImVec2 Layer_LocalSize = ImVec2(Frame_End - Frame_Start, Layer->Vertical_Height); @@ -658,10 +1312,10 @@ ImGui_Timeline_DrawPrecomp(project_data *File, project_state *State, memory *Mem ImU32 LayerColor = 0; ImU32 BorderColor = 0; if (UI->TimelineMode == timeline_mode_graph) { - LayerColor = ImColor(Layer->Col[0], Layer->Col[1], Layer->Col[2], 0.2f); + LayerColor = ImColor(UI->LayerColors[Layer->ColIndex]); BorderColor = ImColor(0.3, 0.3, 0.3, 1.0f); } else { - LayerColor = ImColor(Layer->Col[0], Layer->Col[1], Layer->Col[2], 1.0f); + LayerColor = ImColor(UI->LayerColors[Layer->ColIndex]); BorderColor = ImColor(0.2, 0.2, 0.2, 1.0f); } @@ -680,7 +1334,7 @@ ImGui_Timeline_DrawPrecomp(project_data *File, project_state *State, memory *Mem } static void -ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io) +ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io, sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray) { if (UI->TimelineMode == timeline_mode_graph) ImGui_GraphInfo(File, State, Memory, UI, io); @@ -745,16 +1399,10 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, ImVec2 TimelineZoomSize = TimelineSizeWithBorder / UI->TimelinePercentZoomed; ImVec2 TimelineMoveSize = TimelineSizeWithBorder * UI->TimelinePercentOffset / UI->TimelinePercentZoomed; - ImGui_TimelineHorizontalIncrementDraw(UI, draw_list, TimelineSizeWithBorder, TimelineAbsolutePos, *MainComp, TimelineZoomSize, TimelineMoveSize); + ImGui_TimelineHorizontalIncrementDraw(State, UI, draw_list, TimelineSizeWithBorder, TimelineAbsolutePos, *MainComp, TimelineZoomSize, TimelineMoveSize); ImVec2 Increment = ImVec2((real32)1 / MainComp->Frame_Count, (real32)1 / LayerIncrement); - uint64 SortSize = (sizeof(sorted_comp_info) * File->Comp_Count) + (sizeof(sorted_layer) * File->Layer_Count); - void *SortedArray = Memory_PushScratch(Memory, SortSize); - Arbitrary_Zero((uint8 *)SortedArray, SortSize); - sorted_comp_info *SortedCompArray = (sorted_comp_info *)SortedArray; - sorted_layer *SortedLayerArray = (sorted_layer *)((uint8 *)SortedArray + (sizeof(sorted_comp_info) * File->Comp_Count)); - Layer_SortAll(Memory, SortedLayerArray, SortedCompArray, File->Layer_Count, File->Comp_Count); ImGui_Timeline_DrawPrecomp(File, State, Memory, UI, io, draw_list, File->PrincipalCompIndex, Increment, TimelineAbsolutePos, TimelineMoveSize, TimelineZoomSize, @@ -772,8 +1420,6 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, // Memory_PopScratch(Memory, Keyframe_SortSize); } - Memory_PopScratch(Memory, SortSize); - ImVec2 MouseDelta = io.MouseDelta / TimelineSize; real32 BarHandleSize = FontHeight; @@ -912,6 +1558,23 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, bool32 LeftClick = ImGui::IsMouseDown(ImGuiMouseButton_Left); bool32 RightClick = ImGui::IsMouseDown(ImGuiMouseButton_Right); + ImGui::OpenPopupOnItemClick("layercolor", ImGuiPopupFlags_MouseButtonRight); + if (ImGui::BeginPopup("layercolor")) { + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + for (int c = 0; c < AmountOf(UI->LayerColors); c++) { + ImGui::PushID(c); + ImGui::PushStyleColor(ImGuiCol_Button, UI->LayerColors[c]); + real32 Size = ImGui::GetFontSize() * 2; + ImVec2 ColSize(Size, Size); + ImGui::Button("##test", ColSize); + if ((c+1) % 4) { ImGui::SameLine(); } + ImGui::PopStyleColor(); + ImGui::PopID(); + } + ImGui::PopStyleVar(); + ImGui::EndPopup(); + } + if (IsHovered && io.MouseWheel) { real32 Increment = 0.1; bool32 Direction = (io.MouseWheel > 0) ? 1 : -1; @@ -936,8 +1599,9 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, if (State->Interact_Active == interact_type_keyframe_move || State->Interact_Active == interact_type_keyframe_rotate || State->Interact_Active == interact_type_keyframe_scale) { - for (int a = 0; a < File->Layer_Count; a++) { - block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, a); + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); if (!Layer->IsSelected) continue; for (int h = 0; h < AmountOf(Layer->Property); h++) { @@ -998,9 +1662,31 @@ ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Me if (ImGui::IsKeyPressed(ImGuiKey_Q)) { State->IsRunning = false; } - if (ImGui::IsKeyPressed(ImGuiKey_A)) { + if (ImGui::IsKeyPressed(ImGuiKey_W)) { + block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); + State->Frame_Current = ((State->Frame_Current - 1) < 0) ? 0 : State->Frame_Current - 1; State->UpdateFrame = true; } + if (ImGui::IsKeyPressed(ImGuiKey_E)) { + block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); + State->Frame_Current = ((State->Frame_Current + 1) >= MainComp->Frame_Count) ? 0 : State->Frame_Current + 1; + State->UpdateFrame = true; + } + if (ImGui::IsKeyPressed(ImGuiKey_X)) { + v4 Temp = UI->Color; + UI->Color = UI->AltColor; + UI->AltColor = Temp; + } + + if (ImGui::IsKeyPressed(ImGuiKey_V)) { + State->Tool = tool_default; + } + if (ImGui::IsKeyPressed(ImGuiKey_B)) { + State->Tool = tool_brush; + } + if (ImGui::IsKeyPressed(ImGuiKey_T)) { + Interact_Transform_Begin(File, Memory, State, io.MousePos); + } if (UI->TimelineMode == timeline_mode_graph) { if (ImGui::IsKeyPressed(ImGuiKey_G)) { State->Interact_Offset[2] = io.MousePos.x; @@ -1031,22 +1717,174 @@ ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Me State->IsPlaying ^= 1; } if (ImGui::IsKeyPressed(ImGuiKey_T)) { - for (int a = 0; a < File->Layer_Count; a++) { - block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, a); + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); if (Layer->IsSelected && Layer->IsPrecomp) { Layer->Precomp_Toggled ^= 1; } } } + + if (ImGui::IsKeyPressed(ImGuiKey_Delete)) + { + bool32 CommitAction = 0; + int h = 0, c = 0, i = 0; + int LayerCount = File->Layer_Count; + while (Block_Loop(Memory, F_Layers, LayerCount, &h, &c, &i)) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + if (Layer->IsSelected) { + if (!CommitAction) { + History_Entry_Commit(Memory, "Delete source"); + CommitAction = 1; + } + Layer_Delete(File, Memory, i); + } + } + if (CommitAction) { + History_Entry_End(Memory); + State->UpdateFrame = true; + State->MostRecentlySelectedLayer = -1; + } + } + #if DEBUG - if (ImGui::IsKeyPressed(ImGuiKey_W)) + if (ImGui::IsKeyPressed(ImGuiKey_1)) { Debug.ToggleWindow ^= 1; } #endif + bool32 mod_key = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; + if (mod_key) { + /* + if (ImGui::IsKeyPressed(ImGuiKey_S)) { + if (io.KeyShift) { + State->ImGuiPopups = popup_saveas; + } else { + if (State->Context[0].Filename[0] == '\0') { + State->ImGuiPopups = popup_saveas; + } else { + if (File_SaveAs(State, Memory, 0, State->Context[0].Filename)) { + PostMsg(State, "File saved!"); + } else { + PostMsg(State, "File save failed..."); + } + } + } + } + if (ImGui::IsKeyPressed(ImGuiKey_N)) { + if (io.KeyShift) + State->ImGuiPopups = popup_newfile; + else + State->ImGuiPopups = popup_newlayer; + } + */ + if (ImGui::IsKeyPressed(ImGuiKey_Z)) { + if (io.KeyShift) { + History_Redo(Memory); + State->UpdateFrame = true; + } else { + History_Undo(Memory); + State->UpdateFrame = true; + } + } + } + +} + +static void +ImGui_Menu(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io) +{ + bool open = true; + ImGui::Begin("Menu", &open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar); + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("Save", "Ctrl+S")) + { + // if (File_SaveAs(State, Memory, 0, State->Context[0].Filename)) { + if (0) { + PostMsg(State, "File saved!"); + } else { + PostMsg(State, "File save failed..."); + } + } + /* + if (ImGui::MenuItem("New project file", "Ctrl+Shift+N")) + { + State->ImGuiPopups = popup_newfile; + } + if (ImGui::BeginMenu("Open file")) + { + ImGui::InputText("Filename", State->DummyName, 512); + if (ImGui::IsItemDeactivated() && ImGui::IsKeyPressed(ImGuiKey_Enter)) { + File_Open(State, Memory, State->DummyName); + State->Context[Index].UpdateFrame = true; + } + ImGui::EndMenu(); + } + if (ImGui::MenuItem("Save as", "Ctrl+Shift+S")) + { + State->ImGuiPopups = popup_saveas; + } + */ + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Layer")) + { + if (ImGui::BeginMenu("Import source from file")) + { + ImGui::InputText("Path to image", State->DummyName2, 512); + if (ImGui::IsItemDeactivated() && ImGui::IsKeyPressed(ImGuiKey_Enter)) { + int SourceIndex = Source_Generate(File, State, Memory, (void *)State->DummyName2); + State->UpdateFrame = true; + } + ImGui::EndMenu(); + } + /* + if (ImGui::MenuItem("New layer", "Ctrl+N")) + { + State->ImGuiPopups = popup_newlayer; + } + if (ImGui::BeginMenu("Layer from image path")) + { + ImGui::InputText("Path to image", State->DummyName2, 512); + if (ImGui::IsItemDeactivated() && ImGui::IsKeyPressed(ImGuiKey_Enter)) { + Layer_CreateFromFile(State, Memory, Index, State->DummyName2); + Layer_DeselectAll(State, File, &Context->UI); + layer_sorted Sorts = Layer_GetSortedList(State, File); + file_layer *Layer = (file_layer *)Sorts.Location[File->NumberOfLayers - 1]; + Layer_Select(Layer, &Context->UI, File->NumberOfLayers - 1); + State->Context[Index].UpdateFrame = true; + } + ImGui::EndMenu(); + } + if (ImGui::MenuItem("New adjustment layer", "Ctrl+Shift+Y")) + { + Layer_CreateAdjustment(State, Memory, Index); + } + if (ImGui::MenuItem("Duplicate layer", "Ctrl+Shift+Y")) + { + if (State->Context[Index].UI.MostRecentlySelectedLayer > -1) + Layer_Duplicate(State, Memory, Index, State->Context[Index].UI.MostRecentlySelectedLayer); + } + */ + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + ImGui::End(); +} + +static void +ImGui_Popups(project_state *State, ui *UI, memory *Memory, ImGuiIO io) +{ } + #if 0 real32 MaxVal_Y = -10000; real32 MinVal_Y = 10000; @@ -1931,7 +2769,7 @@ ImGui_PropertiesPanel(project_data *File, project_state *State, ui *UI, memory * static void ImGui_Viewport(project_data File, project_state *State, ui *UI, memory *Memory, comp_buffer CompBuffer, - ImGuiIO io, GLuint textureID) + ImGuiIO io, GLuint textureID, sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray) { bool open = true; ImGui::Begin("Viewport", &open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); @@ -2357,68 +3195,6 @@ ImGui_SlidingLayer(project_layer *Layer, real32 *DraggingThreshold, real32 Delta return Result; } -static void -ImGui_File(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io) -{ - ImGui::Begin("Files"); - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); -#if DEBUG - if (State->DemoButton) { - if (ImGui::Button("Generate demo scene")) { - CreateDemoScene(File, State, Memory); - State->UpdateKeyframes = true; - State->UpdateFrame = true; - State->DemoButton = false; - } - } - if (State->GridButton) { - ImGui::SameLine(); - if (ImGui::Button("Generate square grid")) { - // CreateGrid(File, Memory); - State->UpdateKeyframes = true; - State->UpdateFrame = true; - State->GridButton = false; - } - } -#endif - ImGui::Text("Sources:"); - for (int i = 0; i < File->NumberOfSources; i++) { - bool32 Test = false; - if (File->SourceSelected == i) - Test = true; - ImGui::Selectable(File->Source[i].Path, Test); - if (ImGui::IsItemClicked()) - File->SourceSelected = i; - if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) - File->SourceSelected = i; - ImGui::OpenPopupOnItemClick("sourcecontext", ImGuiPopupFlags_MouseButtonRight); - } - if (ImGui::BeginPopup("sourcecontext")) { - if (ImGui::MenuItem("Create layer from source")) { - Layer_CreateFromSource(File, State, Memory, &File->Source[File->SourceSelected]); - } - ImGui::EndPopup(); - } - static char Input[1024]; - ImGui::InputTextWithHint("##sourceinput", "Input file path of source...", Input, STRING_SIZE); - if (ImGui::IsItemDeactivated() && ImGui::IsKeyPressed(ImGuiKey_Enter)) { - Source_Generate(File, State, Memory, Input); - } -#if DEBUG - for (int i = 0; i < Debug.Temp.WatchedProperties; i++) { - if (Debug.Temp.DebugPropertyType[i] == d_float) { - ImGui::Text("%s: %f", Debug.Temp.String[i], Debug.Temp.Val[i].f); - } else if (Debug.Temp.DebugPropertyType[i] == d_int) { - ImGui::Text("%s: %i", Debug.Temp.String[i], Debug.Temp.Val[i].i); - } else if (Debug.Temp.DebugPropertyType[i] == d_uint) { - ImGui::Text("%s: %u", Debug.Temp.String[i], Debug.Temp.Val[i].u); - } - } - Debug.Temp = {}; -#endif - ImGui::End(); -} - static void ImGui_EffectsPanel(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io) { diff --git a/prenderer.cpp b/prenderer.cpp index b61290a..dcb4577 100644 --- a/prenderer.cpp +++ b/prenderer.cpp @@ -1,3 +1,152 @@ +static v2 +T_CompUVToLayerUV(layer_transforms T, uint32 FileWidth, uint32 FileHeight, uint32 SourceWidth, uint32 SourceHeight, v2 CompUV) +{ + real32 X = CompUV.x*FileWidth; + real32 Y = CompUV.y*FileHeight; + + real32 Rad = (T.rotation* (PI / 180)); + v2 XAxis = (SourceWidth * T.scale)*V2(cos(Rad), sin(Rad)); + v2 YAxis = (SourceHeight * -T.scale)*V2(sin(Rad), -cos(Rad)); + + v2 Pos = {T.x, T.y}; + v2 Origin = Pos - (XAxis * T.ax) - (YAxis * T.ay); + + v2 XAxisPerp = (1.0f / LengthSq(XAxis))*XAxis; + v2 YAxisPerp = (1.0f / LengthSq(YAxis))*YAxis; + + real32 StartVectorX = X - Origin.x; + real32 StartVectorY = Y - Origin.y; + real32 LayerU = (StartVectorX * XAxisPerp.x) + (StartVectorY * XAxisPerp.y); + real32 LayerV = (StartVectorX * YAxisPerp.x) + (StartVectorY * YAxisPerp.y); + return V2(LayerU, LayerV); +} + +static v2 +T_CompPosToLayerPos(layer_transforms T, uint32 FileWidth, uint32 FileHeight, uint32 SourceWidth, uint32 SourceHeight, v2 CompUV) +{ + v2 UV = T_CompUVToLayerUV(T, FileWidth, FileHeight, SourceWidth, SourceHeight, CompUV/V2(FileWidth, FileHeight)); + return UV*V2(SourceWidth, SourceHeight); +} + +static v2 +Transform_ScreenSpaceToLocal(layer_transforms T, uint32 FileWidth, uint32 FileHeight, uint32 SourceWidth, uint32 SourceHeight, + ui UI, ImVec2 ViewportMin, ImVec2 Point) +{ + v2 CompUV = ImGui_ScreenPointToCompUV(ViewportMin, UI.CompPos, UI.CompZoom, Point); + v2 LayerUV = T_CompUVToLayerUV(T, FileWidth, FileHeight, SourceWidth, SourceHeight, CompUV); + return V2(LayerUV.x * SourceWidth, LayerUV.y * SourceHeight); +} + +// Transform given data based on state's Interact data. +static void +Transform_ApplyInteractive(interact_transform Interact, real32 *OutputX, real32 *OutputY, real32 *OutputRotation, real32 *OutputScale) +{ + v2 BoxLength = Interact.Max - Interact.Min; + v2 Center = Interact.Max - (BoxLength/2); + + real32 Point0X = Center.x - *OutputX; + real32 Point0Y = Center.y - *OutputY; + + real32 Rad = Interact.Radians; + real32 Rotation = Interact.Radians / (PI / 180); + + v2 XAxis = (Point0X * Interact.Scale)*V2(cos(Rad), sin(Rad)); + v2 YAxis = (Point0Y * -Interact.Scale)*V2(sin(Rad), -cos(Rad)); + + real32 X0 = -XAxis.x - YAxis.x + Center.x; + real32 Y0 = -XAxis.y - YAxis.y + Center.y; + + *OutputX = X0 + Interact.Position.x; + *OutputY = Y0 + Interact.Position.y; + *OutputRotation += Rotation; + *OutputScale += Interact.Scale - 1.0f; +} + +static void +Transform_IterateOuterBounds(block_layer *Layer, block_source *Source, real32 *MinX, real32 *MinY, real32 *MaxX, real32 *MaxY) +{ + real32 Rad = (Layer->rotation.CurrentValue * (PI / 180)); + real32 s = Layer->scale.CurrentValue; + + v2 XAxis = (Source->Width * s)*V2(cos(Rad), sin(Rad)); + v2 YAxis = (Source->Height * -s)*V2(sin(Rad), -cos(Rad)); + + real32 AnchorX = Layer->ax.CurrentValue; + real32 AnchorY = Layer->ay.CurrentValue; + + v2 Pos = {Layer->x.CurrentValue, Layer->y.CurrentValue}; + v2 Origin = Pos - (XAxis * AnchorX) - (YAxis * AnchorY); + + real32 XLengthSq = 1.0f / LengthSq(XAxis); + real32 YLengthSq = 1.0f / LengthSq(YAxis); + + v2 Points[4] = {Origin, Origin + XAxis, Origin + YAxis, Origin + XAxis + YAxis}; + for (int i = 0; i < 4; i++) { + if (Points[i].x < *MinX) { *MinX = Points[i].x; } + if (Points[i].y < *MinY) { *MinY = Points[i].y; } + if (Points[i].x > *MaxX) { *MaxX = Points[i].x; } + if (Points[i].y > *MaxY) { *MaxY = Points[i].y; } + } +} + +// IMPORTANT(fox): The selection state and ordering of layers cannot change +// until this action is exited/committed! +static void +Interact_Transform_Begin(project_data *File, memory *Memory, project_state *State, ImVec2 OGPos) +{ + real32 MinX = 100000; + real32 MinY = 100000; + real32 MaxX = -100000; + real32 MaxY = -100000; + bool32 Activate = false; + // Find the max dimensions of all the selected layers. + for (int i = 0; i < File->Layer_Count; i++) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + if (!Layer->IsSelected) + continue; + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); + Transform_IterateOuterBounds(Layer, Source, &MinX, &MinY, &MaxX, &MaxY); + Activate = true; + } + if (Activate) { + State->Interact_Active = interact_type_viewport_transform; + interact_transform *Interact = (interact_transform *)&State->Interact_Offset[0]; + Interact->Min = V2(MinX, MinY); + Interact->Max = V2(MaxX, MaxY); + Interact->Position = V2(0); + Interact->Radians = 0; + Interact->Scale = 1.0f; + Interact->OGPos = OGPos; + } +} + +static ImVec2 +Layer_LocalToScreenSpace(project_state *State, block_layer *Layer, ui *UI, + real32 SourceWidth, real32 SourceHeight, real32 CompWidth, real32 CompHeight, v2 Point) +{ + real32 Rotation = Layer->rotation.CurrentValue; + real32 s = Layer->scale.CurrentValue; + real32 X = Layer->x.CurrentValue; + real32 Y = Layer->y.CurrentValue; + + if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected) { + Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &X, &Y, &Rotation, &s); + } + + real32 Rad = (Rotation * (PI / 180)); + real32 AX = Layer->ax.CurrentValue; + real32 AY = Layer->ay.CurrentValue; + + v2 XAxis = (Point.x - AX*SourceWidth) * s * V2(cos(Rad), sin(Rad)); + v2 YAxis = (Point.y - AY*SourceHeight) * -s * V2(sin(Rad), -cos(Rad)); + v2 LocalPoint = XAxis + YAxis; + v2 CompUV = V2((X + LocalPoint.x) / CompWidth, + (Y + LocalPoint.y) / CompHeight); + v2 ScreenPoint = V2(UI->CompPos.x + CompUV.x * UI->CompZoom.x, + UI->CompPos.y + CompUV.y * UI->CompZoom.y); + + return ImVec2(ScreenPoint.x, ScreenPoint.y); +} static void Fallback_RenderLayer(transform_info T, void *OutputBuffer, rectangle RenderRegion); @@ -31,29 +180,6 @@ Renderer_Check(bool32 *Test) *Test = Threading_IsActive(); } -// Helper for working with different bit depths. -static render_byte_info -Bitmap_ByteInfo(uint32 BytesPerPixel) { - render_byte_info Byte = {}; - if (BytesPerPixel == 4) { - Byte.MaskPixel = 0xFF; - Byte.ByteOffset = 1; - Byte.Normalized = 1 / 255.0f; - Byte.Bits = 255; - } else if (BytesPerPixel == 8) { - Byte.MaskPixel = 0xFFFF; - Byte.ByteOffset = 2; - Byte.Normalized = 1 / 65535.0f; - Byte.Bits = 65535; - } else { - Byte.MaskPixel = 0xFFFFFFFF; - Byte.ByteOffset = 4; - Byte.Normalized = 1 / 4294967295.0f; - Byte.Bits = 4294967295; - Assert(0); - } - return Byte; -} static transform_info Transform_Calculate(project_state *State, memory *Memory, project_data *File, block_layer *Layer, block_composition *Comp) @@ -78,6 +204,10 @@ Transform_Calculate(project_state *State, memory *Memory, project_data *File, bl real32 s = Layer->scale.CurrentValue; blend_mode BlendMode = Layer->BlendMode; + if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected) { + Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &X, &Y, &Rotation, &s); + } + /* state_file_ui *UI = &State->Context[State->CurrentFileIndex].UI; if (UI->IsInteracting == true && UI->InteractMode == interact_transforms && Layer->IsSelected && !Layer->IsAdjustment) -- cgit v1.2.3