diff options
-rw-r--r-- | createcalls.cpp | 97 | ||||
-rw-r--r-- | debug.h | 11 | ||||
-rw-r--r-- | ffmpeg_backend.cpp | 44 | ||||
-rw-r--r-- | main.cpp | 84 | ||||
-rw-r--r-- | main.h | 355 | ||||
-rw-r--r-- | memory.cpp | 53 | ||||
-rw-r--r-- | memory.h | 64 | ||||
-rw-r--r-- | my_imgui_internal_widgets.cpp | 30 | ||||
-rw-r--r-- | my_imgui_internal_widgets.h | 2 | ||||
-rw-r--r-- | my_imgui_widgets.cpp | 313 | ||||
-rw-r--r-- | strings.cpp | 9 | ||||
-rw-r--r-- | undo.h | 2 |
12 files changed, 617 insertions, 447 deletions
diff --git a/createcalls.cpp b/createcalls.cpp index fb949a9..3bd13f8 100644 --- a/createcalls.cpp +++ b/createcalls.cpp @@ -1,29 +1,13 @@ -static void -IncrementFrame(project_data *File, int16 Amount) { - if ((File->CurrentFrame <= 0 && Amount < File->StartFrame) || (File->CurrentFrame >= File->EndFrame)) { - File->CurrentFrame = 0; - } else { - File->CurrentFrame += Amount; - } -} - -static void -PostMsg(project_state *State, char *msg) -{ - State->MsgTime = 120; - State->Msg = msg; -} static bool32 Source_Generate(project_data *File, project_state *State, memory *Memory, void *TempString) { - Assert(File->NumberOfSources < MAX_SOURCES); - source *Source = &File->Source[File->NumberOfSources]; - bool32 IsVideo = 0; - - void *Path = String_GenerateFromChar(Memory, (char *)TempString); + Assert(File->Source_Count < MAX_SOURCES); + bool32 IsVideo = 0; if (AV_IsFileSupported((char *)Path, &IsVideo)) { + block_source *Source = + uint16 = String_AddToFile(Memory, (char *)TempString); if (IsVideo) Source->SourceType = source_type_video; else @@ -36,12 +20,29 @@ Source_Generate(project_data *File, project_state *State, memory *Memory, void * return 1; } else { void *Address = Memory_Rewind(Memory, STRING_SIZE, F_Strings); - PostMsg(State, "File open fail..."); + PostMsg(State, "File not supported..."); } return 0; } +#if 0 +static void +IncrementFrame(project_data *File, int16 Amount) { + if ((File->CurrentFrame <= 0 && Amount < File->StartFrame) || (File->CurrentFrame >= File->EndFrame)) { + File->CurrentFrame = 0; + } else { + File->CurrentFrame += Amount; + } +} + +static void +PostMsg(project_state *State, char *msg) +{ + State->MsgTime = 120; + State->Msg = msg; +} + static property_channel InitFloatProperty(char *Name, real32 Val, real32 ScrubVal, real32 MinVal = PROPERTY_REAL_MIN, real32 MaxVal = PROPERTY_REAL_MAX) { property_channel Property = {}; @@ -294,9 +295,18 @@ LoadTestFootage(project_data *File, project_state *State, memory *Memory) Source_Generate(File, State, Memory, SourceString); Layer_CreateFromSource(File, State, Memory, &File->Source[0]); - Keyframe_Insert(&File->Layer[0]->x, Memory, 00, -10); - Keyframe_Insert(&File->Layer[0]->x, Memory, 01, 0); - Keyframe_Insert(&File->Layer[0]->x, Memory, 05, 10); + Keyframe_Insert(&File->Layer[0]->x, Memory, 01, -10); + Keyframe_Insert(&File->Layer[0]->x, Memory, 10, -5); + Keyframe_Insert(&File->Layer[0]->x, Memory, 23, 0); + Keyframe_Insert(&File->Layer[0]->x, Memory, 34, 5); + + for (int i = 0; i < 5; i++) { + keyframe *Keyframe = KeyframeLookup(&File->Layer[0]->x, i); + Keyframe->TangentLeft = V2(-3, 0); + Keyframe->TangentRight = V2(1.5, 0); + Keyframe->Type = bezier; + } + File->Layer[0]->x.IsToggled = true; SelectLayer(File->Layer[0], State, 0); // AddEffect(File->Layer[0], Memory, 1); @@ -434,43 +444,4 @@ CreateDemoScene(project_data *File, project_state *State, memory *Memory) Layer3->x.IsToggled = true; Layer3->y.IsToggled = true; } - -#if 0 -static void -CreateGrid(project_data *File, memory *Memory) { - uint16 Amount = 8; - real32 XInc = File->Width / Amount; - real32 YInc = File->Height / Amount; - for (int16 j = 0; j < 8; j++) { - for (int16 i = 0; i < 8; i++) { - project_layer *Layer = CreateSolidLayer(File, Memory, 400, 400, V4(0.6, 0.3, 0.4, 1.0)); - Layer->x.CurrentValue.f = (XInc*i); - Layer->y.CurrentValue.f = (XInc*j); - Layer->opacity.CurrentValue.f = 0.25; - Layer->StartFrame = 0; - Layer->EndFrame = File->EndFrame; - Keyframe_Insert(&Layer->rotation, Memory, i, 0); - Keyframe_Insert(&Layer->rotation, Memory, 40+i, 360); - } - } -} #endif - -/* -{ - Layer->RenderInfo = AllocateMemory(Memory, sizeof(source_image), P_SourceData); - Layer->RenderInfo = AllocateMemory(Memory, sizeof(source_video), P_SourceData); - source_image *Source = (source_image *)Layer->RenderInfo; - Source->Raster = LoadImage(Memory, filename); - Layer->EndFrame = File.EndFrame; - Layer->x.CurrentValue.f = 1280/2; - Layer->y.CurrentValue.f = 720/2; - Layer->StartFrame = 0; - - - Layer->x.CurrentValue.f = 1280/2; - Layer->y.CurrentValue.f = 720/2; - Layer->StartFrame = 0; - Layer->EndFrame = File.EndFrame; -} -*/ @@ -51,17 +51,6 @@ DebugWatchVar(char *Name, void *Address, valtype Type) { Debug.Temp.WatchedProperties++; } -static void -DebugPrintMemoryUsage(memory Memory) -{ - for (int i = 0; i < 8; i++) { - memory_table Table = Memory.Slot[i]; - printf("%s: %li bytes, %li kb\n", Table.Name, Table.CurrentPosition, Table.CurrentPosition / 1024); - } -} - - - #else #define Assert(Expression) diff --git a/ffmpeg_backend.cpp b/ffmpeg_backend.cpp index 89c0c91..34abf35 100644 --- a/ffmpeg_backend.cpp +++ b/ffmpeg_backend.cpp @@ -112,7 +112,7 @@ void AV_Dealloc(av_info *AV) av_frame_free(&AV->VideoFrame); }; -void AV_Init(source *Source, av_info *AV, memory *Memory) +void AV_Init(block_source *Source, av_info *AV, memory *Memory) { *AV = {}; @@ -197,21 +197,21 @@ void AV_Init(source *Source, av_info *AV, memory *Memory) fprintf(stderr, "Libav error: (%s)\n", av_err2str(err)); } - Source->Info.BytesPerPixel = 4; - Source->Info.FPS = (real32)AV->VideoStream->r_frame_rate.num / AV->VideoStream->r_frame_rate.den; - Source->Info.Width = AV->VideoCodecContext->width; - Source->Info.Height = AV->VideoCodecContext->height; + Source->BytesPerPixel = 4; + Source->FPS = (real32)AV->VideoStream->r_frame_rate.num / AV->VideoStream->r_frame_rate.den; + Source->Width = AV->VideoCodecContext->width; + Source->Height = AV->VideoCodecContext->height; }; -void AV_GetPTSAverage(source *Source, av_info *AV, int32 *err) +void AV_GetPTSAverage(block_source *Source, av_info *AV, int32 *err) { // TODO(fox): This PTS average isn't exact and causes an occasional // frame skip. See libav remarks in forum for more details. if (AV->VideoStream->duration == 1) { - Source->Info.AvgPTSPerFrame = 1; + Source->AvgPTSPerFrame = 1; return; } @@ -234,13 +234,14 @@ void AV_GetPTSAverage(source *Source, av_info *AV, int32 *err) } } - Source->Info.AvgPTSPerFrame = (real32)AvgPTSPerSecond / (int32)(FPS + 0.5f); - printf("Avg PTS per sec: %.06f, Avg PTS per frame: %.06f\n", AvgPTSPerSecond, Source->Info.AvgPTSPerFrame); + Source->AvgPTSPerFrame = (real32)AvgPTSPerSecond / (int32)(FPS + 0.5f); + printf("Avg PTS per sec: %.06f, Avg PTS per frame: %.06f\n", AvgPTSPerSecond, Source->AvgPTSPerFrame); av_seek_frame(AV->FileFormatContext, -1, 0, AVSEEK_FLAG_BACKWARD); } -cached_bitmap * AV_LoadStill(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory) +#if 0 +cached_bitmap * AV_LoadStill(block_source *Source, layer_bitmap_info *BitmapInfo, memory *Memory) { av_info *AV = (av_info *)BitmapInfo->AVInfo; int32 *CurrentlyRenderedFrame = &BitmapInfo->CurrentFrame; @@ -254,9 +255,9 @@ cached_bitmap * AV_LoadStill(source *Source, layer_bitmap_info *BitmapInfo, memo while (err >= 0) { if (AV_TryFrame(AV, &err)) { - uint16 Width = Source->Info.Width; - uint16 Height = Source->Info.Height; - uint16 BytesPerPixel = Source->Info.BytesPerPixel; + uint16 Width = Source->Width; + uint16 Height = Source->Height; + uint16 BytesPerPixel = Source->BytesPerPixel; int32 Pitch = Width*BytesPerPixel; cached_bitmap *Bitmap = Memory_RollingBitmap(Memory, Source, 0); @@ -295,10 +296,10 @@ cached_bitmap * AV_LoadVideoFrame(source *Source, layer_bitmap_info *BitmapInfo, int32 err = 0; - if (!Source->Info.AvgPTSPerFrame) { + if (!Source->AvgPTSPerFrame) { AV_GetPTSAverage(Source, AV, &err); } - Assert(Source->Info.AvgPTSPerFrame); + Assert(Source->AvgPTSPerFrame); int32 FrameToSeek = TimelineFrame - BitmapInfo->FrameOffset; if (*CurrentlyRenderedFrame == FrameToSeek || FrameToSeek < 0) @@ -309,7 +310,7 @@ cached_bitmap * AV_LoadVideoFrame(source *Source, layer_bitmap_info *BitmapInfo, // This function only seeks to the nearest "keyframe." if (*CurrentlyRenderedFrame != FrameToSeek - 1) { - int64 SeekSeconds = (int64)(FrameToSeek / (int32)(Source->Info.FPS + 0.5f) * AV_TIME_BASE); + int64 SeekSeconds = (int64)(FrameToSeek / (int32)(Source->FPS + 0.5f) * AV_TIME_BASE); av_seek_frame(AV->FileFormatContext, -1, SeekSeconds, AVSEEK_FLAG_BACKWARD); printf("Seek activated\n"); } else if (*CurrentlyRenderedFrame < 0) { @@ -318,7 +319,7 @@ cached_bitmap * AV_LoadVideoFrame(source *Source, layer_bitmap_info *BitmapInfo, *CurrentlyRenderedFrame = FrameToSeek; - int64 SeekPTS = (int64)(Source->Info.AvgPTSPerFrame*FrameToSeek + 0.5f); + int64 SeekPTS = (int64)(Source->AvgPTSPerFrame*FrameToSeek + 0.5f); while (err >= 0) { if (AV_TryFrame(AV, &err)) { @@ -332,7 +333,7 @@ cached_bitmap * AV_LoadVideoFrame(source *Source, layer_bitmap_info *BitmapInfo, } int64 Difference = AV->VideoFrame->pts - SeekPTS; - if (abs(Difference) < Source->Info.AvgPTSPerFrame) + if (abs(Difference) < Source->AvgPTSPerFrame) { if (AV->PreviousPTS == -1) { AV->PreviousPTS = AV->VideoFrame->pts; @@ -342,9 +343,9 @@ cached_bitmap * AV_LoadVideoFrame(source *Source, layer_bitmap_info *BitmapInfo, AV->PreviousPTS = AV->VideoFrame->pts; } - uint16 Width = Source->Info.Width; - uint16 Height = Source->Info.Height; - uint16 BytesPerPixel = Source->Info.BytesPerPixel; + uint16 Width = Source->Width; + uint16 Height = Source->Height; + uint16 BytesPerPixel = Source->BytesPerPixel; int32 Pitch = Width*BytesPerPixel; cached_bitmap *Bitmap = Memory_RollingBitmap(Memory, Source, FrameToSeek); @@ -381,3 +382,4 @@ cached_bitmap * AV_LoadVideoFrame(source *Source, layer_bitmap_info *BitmapInfo, } return 0; } +#endif @@ -36,9 +36,12 @@ #include "defines.h" #include "my_math.h" #include "structs.h" + +#include "memory.h" #include "main.h" + #include "debug.h" -#include "functions.h" +// #include "functions.h" // #include "sharebuffer.h" SDL_atomic_t CurrentEntry; @@ -48,29 +51,32 @@ static bool32 IsRendering = false; static instruction_mode InstructionMode = instruction_mode_scalar; static uint32 RandomGlobalIncrement = 0; -render_entry Entries[256]; +// render_entry Entries[256]; SDL_Thread *thread[8]; SDL_sem *Semaphore; #include "memory.cpp" +#include "strings.cpp" +#include "createcalls.cpp" +#include "ffmpeg_backend.cpp" +#if 0 #include "effects.cpp" #include "keyframes.cpp" #include "layer.cpp" -#include "strings.cpp" #include "bezier.cpp" #if THREADED #include "threading.cpp" #endif #include "prenderer.cpp" -#include "ffmpeg_backend.cpp" #include "bitmap_calls.cpp" -#include "createcalls.cpp" #include "my_imgui_widgets.cpp" #include "gl_calls.cpp" #include "undo.cpp" +#endif +#if 0 static void MainFunction(main_sdl *Main, memory *Memory, project_state *State, project_data *File, @@ -99,12 +105,13 @@ MainFunction(main_sdl *Main, memory *Memory, State->UpdateKeyframes = false; QueueCurrentFrame(File, CompBuffer, State); } +#endif int main(int argc, char *argv[]) { global_memory GlobalMemory = {}; - GlobalMemory.Size = ((uint64)4 * 1024 * 1024 * 1024); + GlobalMemory.Size = ((uint64)1 * 1024 * 1024 * 1024); GlobalMemory.CurrentPosition = 0; #if WINDOWS @@ -120,23 +127,19 @@ int main(int argc, char *argv[]) { memory Memory = {}; + Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, P_AVInfo, "Image/video headers"); + Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_UndoBuffer, "Undo buffer"); + Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, P_MiscCache, "Misc persistent"); - InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_AVInfo, "Image/video headers"); - InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_VectorPoints, "Vector Points"); - InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_UndoBuffer, "Undo buffer"); - InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_MiscCache, "Misc cache"); - - InitMemoryTable(&GlobalMemory, &Memory, 15 * 1024 * 1024, F_Layers, "Layers"); - InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Effects, "Effects"); - InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Keyframes, "Keyframe blocks"); - InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Strings, "Strings"); - - InitMemoryTable(&GlobalMemory, &Memory, (uint64)200 * 1024 * 1024, B_LayerBitmaps, "Layer buffer"); - InitMemoryTable(&GlobalMemory, &Memory, (uint64)200 * 1024 * 1024, B_LoadedBitmaps, "Loaded bitmap buffer"); + Memory_InitTable(&GlobalMemory, &Memory, sizeof(project_data), F_File, "File", sizeof(project_data)); + Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Precomps, "Precomps", sizeof(block_composition)); + // Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Layers, "Layers"); + Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Sources, "Sources", sizeof(block_source)); + // Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Effects, "Effects"); + // Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Keyframes, "Keyframe blocks"); + Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Strings, "Strings"); - Memory.Scratch = AllocateMemory(&Memory, (uint64)64*1024*1024, B_LayerBitmaps); - - project_state State = {}; + // Memory_InitTable(&GlobalMemory, &Memory, (uint64)200 * 1024 * 1024, B_CachedBitmaps, "Cached bitmap buffer"); #if ARM InstructionMode = instruction_mode_neon; @@ -149,14 +152,21 @@ int main(int argc, char *argv[]) { } #endif - project_data File = {}; - File.Width = 1920; - File.Height = 1080; - File.NumberOfFrames = 120; - File.FPS = 24; - File.CurrentFrame = 1; - File.StartFrame = 0; - File.EndFrame = 100; + project_state *State = (project_state *)Memory.Slot[P_MiscCache].Address; + project_data *File = (project_data *)Memory_Block_AllocateAddress(&Memory, F_File); + block_composition *MainComp = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps); + + MainComp->Width = 1280; + MainComp->Height = 720; + MainComp->FPS = 24; + MainComp->BytesPerPixel = 4; + + MainComp->Frame_Count = 48; + MainComp->Frame_End = 48; + + Source_Generate(File, State, Memory, "../asset/a.jpg"); + +#if 0 #if DEBUG @@ -296,7 +306,7 @@ int main(int argc, char *argv[]) { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); - ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable; + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_NavEnableSetMousePos; (void)io; // NOTE(fox): Instead of constructing the position of the windows on @@ -344,11 +354,23 @@ int main(int argc, char *argv[]) { State.IsRunning = false; } + if (UI.WantSetPos) { + ImGui::GetIO().WantSetMousePos = true; + io.MousePos = UI.SetPos; + } + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); + if (UI.WantSetPos) { + ImGui_WrapMouseFinish(&UI, io.MousePos); + io.MouseDelta = {}; + UI.WantSetPos = false; + } + if (!io.WantCaptureKeyboard) ImGui_ProcessInputs(&File, &State, &CompBuffer, &Memory, &UI, io); @@ -427,5 +449,7 @@ int main(int argc, char *argv[]) { ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); SDL_Quit(); +#endif + return 0; } @@ -8,119 +8,6 @@ enum instruction_mode { #endif }; -enum memory_table_list { - - // F = file attributes - // P = persistent data, but not file-based - // B = cached data, often cleared - - P_AVInfo, - P_VectorPoints, - P_UndoBuffer, - P_MiscCache, - - // The structs that these four blocks correspond to (project_layer, effect, - // keyframe_block, char[STRING_SIZE]) are guranteed equal size. - F_Layers, - F_Effects, - F_Keyframes, - F_Strings, - - B_LayerBitmaps, - B_LoadedBitmaps, -}; - -struct memory_table { - char *Name; - void *Address; - uint64 CurrentPosition; - uint64 Size; - uint32 NumberOfPointers; - uint32 PointerIndex; -}; - -struct global_memory { - void *Address; - uint64 CurrentPosition; - uint64 Size; -}; - -struct source; - -struct cached_bitmap { - source *SourceOwner; // Which source it belongs to. Currently used to dereference the bitmap. - void *Data; // Unpacked data loaded from the source file. - uint32 Frame; // What frame it is. -}; - -struct action_shift_data { - void *StartingAddress; - uint32 Size; - uint16 NumberOf; - uint16 Index; - int16 Direction; -}; - -enum action_type { - action_type_change_u16, - action_type_change_i16, - action_type_change_u32, - action_type_change_i32, - action_type_change_r32, - action_type_change_u64, - action_type_change_ptr, - action_type_change_string, - action_type_shift_keyframe, - action_type_shift_bezier, - action_type_shift, - action_type_storedata -}; - -enum action_entry_type { - action_entry_layerinit, - action_entry_default -}; - -struct action_entry { - char *Name; - action_entry_type Type; - void *ExtraPointer; - uint16 NumberOfActions; -}; - -struct action_entries { - action_entry Entry[1024]; - uint16 Index; - uint16 NumberOfEntries; -}; - -struct memory { - memory_table Slot[16]; - cached_bitmap Bitmap[4096]; - void *Scratch; // 64 MB of data - action_entries Action; -}; - -struct property_channel; -struct project_layer; - -enum keyframe_type -{ - linear, - bezier, - hold -}; - -// NOTE(fox): One val slot holds 16 bytes of data. - -enum var_type -{ - type_real, - type_color, - type_blendmode -}; - - static char* BlendmodeNames[] = { "Normal", "Multiply", @@ -153,57 +40,72 @@ enum blend_mode }; -union val { - v4 col; - real32 f; - blend_mode blendmode; +enum interpolation_type +{ + interpolation_type_linear, + interpolation_type_bezier, + interpolation_type_hold }; -struct keyframe { - val Value; - // NOTE(fox): Frame values are relative to the layer's FrameOffset! This is - // done to reduce the footprint of layer moving in the undo tree. - int32 FrameNumber; - keyframe_type Type; - bool32 IsSelected; - // The X coordinate for the tangent is in keyframes, and the Y is in units. - // Probably should think of something smarter. - v2 TangentLeft; - v2 TangentRight; - // NOTE(fox): We need some sort of unique constant to give to ImGui in - // order for dragging to work. - uint32 ImguiID; +struct bezier_point { + v2 Pos[3]; + interpolation_type Type; + uint16 ImguiID; + uint8 IsSelected; }; -struct keyframe_block { - keyframe Keyframe[MAX_KEYFRAMES_PER_BLOCK]; +struct block_bezier { + uint8 Occupied; + bezier_point Point[MAX_KEYFRAMES_PER_BLOCK]; }; -struct property_channel { - char *Name; - keyframe_block *KeyframeBlock[MAX_KEYFRAME_BLOCKS]; - uint16 NumberOfKeyframeBlocks; - uint16 NumberOfSelectedKeyframes; - uint16 NumberOfTotalKeyframes; - val CurrentValue; - val MaxVal; - val MinVal; - val ScrubVal; // increment when dragging on sliders, etc. - var_type VarType; +struct project_state +{ + bool32 UpdateKeyframes = 1; + bool32 UpdateFrame = 1; // only refreshes frame; set UpdateKeyframes to update animation + bool32 DebugDisableCache = 1; - bool32 IsToggled; + // tool Tool = tool_default; + // pen_state Pen = {}; + + bool32 IsRunning = 1; + bool32 IsPlaying; + + int16 MostRecentlySelectedLayer = -1; + // selection_type RecentSelectionType = selection_none; + + bool32 IsInteracting; + real32 InteractCache[6]; + // interact_type InteractType; + + int32 MsgTime; // currently in "frames" + char *Msg; + + ImGuiTextFilter filter; // This filter API is pretty ballin'. + bool32 RerouteEffects; // Allows shift+space hotkey to gain focus on the effects panel. }; -struct property_header +struct project_data { - char *Name; - val Value; - var_type VarType; - val MinVal; - val MaxVal; + uint8 Occupied; + uint16 Layer_Count; + uint16 Source_Count; + uint16 PrincipalCompIndex; }; -// Information about a particular file. +struct block_composition +{ + uint8 Occupied; + uint16 Width; + uint16 Height; + uint16 FPS; + uint16 BytesPerPixel; + + uint32 Frame_Count; + int32 Frame_Current; + int32 Frame_Start; + int32 Frame_End; +}; enum source_type { source_type_none, @@ -211,10 +113,11 @@ enum source_type { source_type_image }; -// Probably best to consider source_info a part of the file data so we don't -// have to re-check each source on every project load. (except for AVInfo) +struct block_source +{ + uint8 Occupied; -struct source_info { + uint16 Block_String_Index; // Image and video uint16 Width; uint16 Height; @@ -223,15 +126,41 @@ struct source_info { // Video only real32 FPS; real32 AvgPTSPerFrame; // set by Libav -}; -struct source { - char *Path; source_type SourceType; - source_info Info; }; -// Bitmaps from files are loaded into these temporary cache blocks. +struct property_header +{ + char *Name; + real32 DefaultVal; + real32 MinVal; + real32 MaxVal; +}; + +struct property_channel { + char *Name; + uint16 Block_Bezier_Index[MAX_KEYFRAME_BLOCKS]; + uint16 Block_Bezier_Count; + uint16 Keyframe_Count; + + real32 CurrentValue; + real32 MaxVal; + real32 MinVal; + real32 ScrubVal; // increment when dragging on sliders, etc. + + bool32 IsToggled; +}; + + + +#if 0 + +struct cached_bitmap { + uint32 SourceIndex; // Which source it belongs to. Currently used to dereference the bitmap. + void *Data; // Unpacked data loaded from the source file. + uint32 Frame; // What frame it is. +}; struct gl_effect_layer { bool32 Initialized; @@ -250,16 +179,15 @@ struct layer_bitmap_info { gl_effect_layer Test; gl_effect_layer TestM; - // TODO(fox): Find a better place to store this. Either give effects a more - // fleshed-out API to add things to a struct like this or integrate into ImGui. - void *HistogramVals; // 256*5 floats (all channel average + RGBA). - uint16 LevelsSelector; // Which channel is currently active - // Video only int32 CurrentFrame = -1; // The last frame number rendered to the bitmap. -1 gurantees a load upon layer creation. void *AVInfo; // Internal data containing current frame info }; + +// Bitmaps from files are loaded into these temporary cache blocks. + + // NOTE(fox): I use the term "comp" (composition) to mean the canvas that is // being rendered to, since it's what I'm used to from AE. struct comp_buffer { @@ -347,51 +275,6 @@ struct project_layer { }; -// NOTE(fox): I have no idea how people normally do selection; currently I'm -// treating it more "immediate." Instead of updating a selection state as -// things are selected, I'm just calling functions that go through all the -// layers and keyframes to look for the IsSelected bool. If this causes -// lag I'll switch it to the former. - -struct temp_layer_list { - int16 LayerIndex[MAX_LAYERS]; -}; - -struct temp_keyframe_list { - keyframe *SelectedKeyframe[50]; - uint16 Amount; -}; - -struct project_data -{ - uint16 Width; - uint16 Height; - uint16 FPS; - uint32 NumberOfFrames; - int32 StartFrame; - int32 EndFrame; - int32 CurrentFrame; - - // NOTE(fox): Currently I'm handling layer sorting by just saying that - // their order in memory is the order of the index and manually moving - // their positions. - - project_layer *Layer[MAX_LAYERS]; - uint16 NumberOfSelectedLayers; - uint16 NumberOfLayers; - - source Source[MAX_SOURCES]; - uint32 SourceSelected; - uint16 NumberOfSources; -}; - -enum transforms_hotkey_interact { - sliding_position, - sliding_anchorpoint, - sliding_scale, - sliding_rotation -}; - struct main_sdl { SDL_Texture *Texture; @@ -426,37 +309,13 @@ struct pen_state { bool32 IsActive; }; -struct project_state +enum interact_type { - bool32 UpdateKeyframes = 1; - bool32 UpdateFrame = 1; // only refreshes frame; set UpdateKeyframes to update animation - bool32 DebugDisableCache = 1; + interact_type_keyframe_move, + interact_type_keyframe_rotate, + interact_type_keyframe_scale +} - tool Tool = tool_default; - pen_state Pen = {}; - - uint16 LayersToRender[MAX_LAYERS]; - uint16 NumberOfLayersToRender; - - bool32 IsRunning = 1; - bool32 IsPlaying; - bool32 DemoButton = 1; - bool32 GridButton = 1; - - uint16 NumberOfSelectedLayers; - int16 MostRecentlySelectedLayer = -1; // convenience for the properties panel - selection_type RecentSelectionType = selection_none; - - bool32 IsInteracting; - real32 InteractCache[4]; // we need to store the initial position in order to record it in the undo tree - transforms_hotkey_interact TransformsHotkeyInteract; - - int32 MsgTime; // currently in "frames" - char *Msg; - - ImGuiTextFilter filter; // This filter API is pretty ballin'. - bool32 RerouteEffects; // Allows shift+space hotkey to gain focus on the effects panel. -}; struct brush_tool { @@ -489,16 +348,21 @@ struct ui // Under 1 is zoomed in! real32 TimelinePercentZoomed = 1.0f; - real32 TimelinePercentOffset = 0.0f; + real32 TimelinePercentOffset = 0.3f; - real32 Default_Y_TimelinePercentZoomed = 2.0f; - real32 Default_Y_TimelinePercentOffset = 0.5f; + real32 Default_Y_TimelinePercentZoomed = 1.2f; + real32 Default_Y_TimelinePercentOffset = 0.0f; real32 Y_TimelinePercentZoomed; real32 Y_TimelinePercentOffset; bool32 IsDragging; + bool32 IsTransforming; + int32 Wrap_X = 0; + int32 Wrap_Y = 0; real32 TempVal; + real32 TempVal_X; + real32 OldVal[4]; real32 Y_MaxVal; real32 Y_MinVal; @@ -506,6 +370,11 @@ struct ui real32 Display_Y_MaxVal; real32 Display_Y_MinVal; + bool32 WantSetPos = false; + ImVec2 SetPos; + real32 InitPos; + int32 WrapDirection; + // Note that I don't use "zoom" to mean the scale in relation to the // original (i.e. default = 1.0f); it's the literal screen size in pixels // of the composition in the UI. @@ -622,3 +491,5 @@ struct render_entry { rectangle RenderRegion; }; +#endif + @@ -1,18 +1,58 @@ - static void -InitMemoryTable(global_memory *GlobalMemory, memory *Memory, uint64 Size, memory_table_list TableName, char *Name) { +Memory_InitTable(global_memory *GlobalMemory, memory *Memory, uint64 Size, memory_table_list TableName, char *Name, uint64 Block_ElementSize = 0) { memory_table *Table = &Memory->Slot[TableName]; Table->Name = Name; Table->Address = (ptrsize *)((uint8 *)GlobalMemory->Address + GlobalMemory->CurrentPosition); Table->Size = Size; + Table->Block_ElementSize = Block_ElementSize; GlobalMemory->CurrentPosition += Size; } -// NOTE(fox): Currently memory acts like simple stack that can only grow forwards. -// Undos/deletes create "holes." Once someone undos/deletes enough to trigger -// the limit or we start caring more about space, I'll revamp the system to -// keep track of free holes and allow those to be returned. +void Memory_Zero(uint8 *Address_Write, uint64 Size) +{ + uint64 i = 0; + while (i < Size) { + *(Address_Write + i) = 0; + i++; + } +} + +static uint32 +Memory_Block_AllocateNew(memory *Memory, memory_table_list TableName) +{ + memory_table *Table = &Memory->Slot[TableName]; + Assert(Table->Block_ElementSize != 0); + bool32 Empty = 0; + uint32 Index = 0; + uint8 *Address_Playhead = (uint8 *)Table->Address; + while (*Address_Playhead != 0) { + Address_Playhead += Table->Block_ElementSize; + Index++; + } + Memory_Zero(Address_Playhead, Table->Block_ElementSize); + *Address_Playhead = 1; + + return Index; +} + +static void * +Memory_Block_AddressAtIndex(memory *Memory, memory_table_list TableName, uint32 Index) +{ + memory_table *Table = &Memory->Slot[TableName]; + Assert(Table->Block_ElementSize != 0); + uint8 *Address = (uint8 *)Table->Address + (Table->Block_ElementSize * Index); + Assert(*Address == 1); + return (void *)Address; +} +static void * +Memory_Block_AllocateAddress(memory *Memory, memory_table_list TableName) +{ + uint16 FileIndex = Memory_Block_AllocateNew(Memory, TableName); + return Memory_Block_AddressAtIndex(Memory, F_File, FileIndex); +} + +#if 0 static void* AllocateMemory(memory *Memory, uint64 Size, memory_table_list TableName) { void *Address; @@ -174,3 +214,4 @@ Memory_RollingBitmap(memory *Memory, source *Source, uint32 FrameToSeek) Debug_Memory_Assert_Cohesion(Memory, Table); return Bitmap; } +#endif diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..47c737e --- /dev/null +++ b/memory.h @@ -0,0 +1,64 @@ +enum memory_table_list { + + P_AVInfo, + P_UndoBuffer, + P_MiscCache, + + F_File, + F_Precomps, + F_Layers, + F_Sources, + F_Properties, + F_Keyframes, + F_Effects, + F_Nodes, + F_Strings, + + B_CachedBitmaps, +}; + +struct memory_table { + char *Name; + void *Address; + uint64 Size; + uint32 Block_ElementSize; +}; + +struct global_memory { + void *Address; + uint64 CurrentPosition; + uint64 Size; +}; + +enum history_action_type { + action_type_swap, + action_type_shift +}; + +struct history_action { + memory_table_list TableName; + history_action_type Type; + uint64 Size; + uint64 ByteOffset; + uint64 ShiftAmount; // Only for type_shift + int16 Direction; // Only for type_shift +}; + +struct history_entry { + char *Name; + uint16 NumberOfActions; +}; + +struct history_entry_list { + history_entry Entry[256]; + history_action Action[1024]; + uint16 NumberOfEntries; + uint16 EntryPlayhead; +}; + +struct memory { + memory_table Slot[16]; + history_entry_list History; + bool32 IsFileSaved; +}; + diff --git a/my_imgui_internal_widgets.cpp b/my_imgui_internal_widgets.cpp index d398ca1..1c16b65 100644 --- a/my_imgui_internal_widgets.cpp +++ b/my_imgui_internal_widgets.cpp @@ -1,13 +1,41 @@ #include "my_imgui_internal_widgets.h" - #include "imgui.h" #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif #include "imgui_internal.h" +// In the regular call (GetMouseDragDelta) after the threshold is passed for +// the first time, it returns true on every delta change even if you call +// ResetMouseDragDelta, which makes routines like changing the frame number +// every time the mouse is dragged past a certain distance not work how you'd +// expect. This function uses the actual mouse delta instead of the stored +// "max" value. +ImVec2 ImGui::GetLocalMouseDragDelta(ImGuiMouseButton button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[button]) : ImVec2(0.0f, 0.0f); + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) + if (ImLengthSqr(delta_from_click_pos) >= lock_threshold * lock_threshold) + if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) + return g.IO.MousePos - g.IO.MouseClickedPos[button]; + return ImVec2(0.0f, 0.0f); +} + +// To make the above scenario work we also have to be able to subtract from the +// delta; simply resetting it will cause misalignment from the mouse. +void ImGui::SetLocalMouseDragDelta(ImGuiMouseButton button, ImVec2 DeltaOffset) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + g.IO.MouseClickedPos[button] += DeltaOffset; +} + // A modded version of ScalarSlider allowing for the minimum and maximum parts // of the slider to be draggable by two other buttons. p_mid is from range -1 // to 1, and s_min and max are from 0-1. diff --git a/my_imgui_internal_widgets.h b/my_imgui_internal_widgets.h index 52fdc74..2841c2d 100644 --- a/my_imgui_internal_widgets.h +++ b/my_imgui_internal_widgets.h @@ -6,6 +6,8 @@ // NOTE(fox): Appending to the standard ImGui namespace so I don't have to convert all the functions to ImGui::Function() namespace ImGui { + IMGUI_API ImVec2 GetLocalMouseDragDelta(ImGuiMouseButton button, float lock_threshold); + IMGUI_API void SetLocalMouseDragDelta(ImGuiMouseButton button, ImVec2 DeltaOffset); IMGUI_API bool SliderLevels(const char* label, const char* label2, const char* label3, void* p_data, void* p_min, void* p_max); IMGUI_API bool TestLine(ImVec2 P1, ImVec2 P2); IMGUI_API bool BezierInteractive(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3); diff --git a/my_imgui_widgets.cpp b/my_imgui_widgets.cpp index d237b3c..a688283 100644 --- a/my_imgui_widgets.cpp +++ b/my_imgui_widgets.cpp @@ -72,6 +72,59 @@ ImGui_KeyframeDragging(project_data *File, project_state *State, ui *UI, propert } } +// NOTE(fox): We have to do a bit of hackery here to tell how many times the +// mouse has been wrapped during a drag, since it doesn't seem like we can rely +// on SDL_WarpMouseGlobal to update on the first frame of a WantSetPos request. + +static void +ImGui_WrapMouse(ui *UI, ImVec2 MousePos, ImVec2 Min, ImVec2 Max, int Direction = 3) +{ + if (Direction & 1) { + if (MousePos.x < Min.x) { + UI->WantSetPos = true; + UI->SetPos = ImVec2(Max.x - 5, MousePos.y); + UI->InitPos = MousePos.x; + UI->WrapDirection = 0; + } + if (MousePos.x > Max.x) { + UI->WantSetPos = true; + UI->SetPos = ImVec2(Min.x + 5, MousePos.y); + UI->InitPos = MousePos.x; + UI->WrapDirection = 1; + } + } + if (Direction & 2) { + if (MousePos.y < Min.y) { + UI->WantSetPos = true; + UI->SetPos = ImVec2(MousePos.x, Max.y - 5); + UI->InitPos = MousePos.y; + UI->WrapDirection = 2; + } + if (MousePos.y > Max.y) { + UI->WantSetPos = true; + UI->SetPos = ImVec2(MousePos.x, Min.y + 5); + UI->InitPos = MousePos.y; + UI->WrapDirection = 3; + } + } +} + +static void +ImGui_WrapMouseFinish(ui *UI, ImVec2 MousePos) +{ + if (UI->WrapDirection == 0) { + if (MousePos.x < UI->InitPos) UI->Wrap_X--; + } else if (UI->WrapDirection == 1) { + if (MousePos.x > UI->InitPos) UI->Wrap_X++; + } else if (UI->WrapDirection == 2) { + if (MousePos.y < UI->InitPos) UI->Wrap_Y--; + } else if (UI->WrapDirection == 3) { + if (MousePos.y > UI->InitPos) UI->Wrap_Y++; + } else { + Assert(0); + } +} + static void ImGui_InteractSliderProperty(project_state *State, memory *Memory, property_channel *Property) { @@ -256,24 +309,6 @@ ImGui_TimelineIncrementDraw(project_data *File, ui *UI, ImDrawList *draw_list, RightmostEdge = true; } } - -#if 0 - x = -0.25; - bool32 LeftmostEdge = false; - while (!LeftmostEdge) { - ImVec2 Min = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + x*TimelineZoomSize, TimelineStartingPos.y); - ImVec2 Max = ImVec2(Min.x + 2, WindowMaxAbs.y); - if (Min.x > TimelineAbsolutePos.x) { - draw_list->AddLine(Min, Max, LineColor); - char buf2[6]; - sprintf(buf2, "%.2f", x); - draw_list->AddText(ImVec2(Min.x, TimelineAbsolutePos.y), IM_COL32(200, 200, 200, 130), buf2); - x -= 0.25; - } else { - LeftmostEdge = true; - } - } -#endif } static void @@ -325,37 +360,6 @@ ImGui_TimelineIncrementDraw2(project_data *File, ImDrawList *draw_list, real32 M LeftmostEdge = true; } } -#if 0 - while (!RightmostEdge) { - ImVec2 Min = ImVec2(TimelineAbsolutePos.x, TimelineAbsolutePos.y + TimelineZoomSize + TimelineMoveSize - x*TimelineZoomSize); - ImVec2 Max = ImVec2(TimelineAbsolutePos.x + TimelineSizeWithBorder.x, Min.y + 2); - if (Min.y > TimelineAbsolutePos.y) { - draw_list->AddLine(Min, Max, LineColor); - char buf2[6]; - sprintf(buf2, "%.2f", (x / Increment) + MinVal_Y); - draw_list->AddText(ImVec2(TimelineAbsolutePos.x, Min.y), IM_COL32(200, 200, 200, 130), buf2); - x += Increment; - } else { - RightmostEdge = true; - } - } - - x = -Increment; - bool32 LeftmostEdge = false; - while (!LeftmostEdge) { - ImVec2 Min = ImVec2(TimelineAbsolutePos.x, TimelineAbsolutePos.y + TimelineZoomSize + TimelineMoveSize - x*TimelineZoomSize); - ImVec2 Max = ImVec2(TimelineAbsolutePos.x + TimelineSizeWithBorder.x, Min.y + 2); - if (Min.y < TimelineAbsolutePos.y + TimelineSizeWithBorder.y) { - draw_list->AddLine(Min, Max, LineColor); - char buf2[6]; - sprintf(buf2, "%.2f", (x / Increment) + MinVal_Y); - draw_list->AddText(ImVec2(TimelineAbsolutePos.x, Min.y), IM_COL32(200, 200, 200, 130), buf2); - x -= Increment; - } else { - LeftmostEdge = true; - } - } -#endif } static void @@ -1175,6 +1179,13 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, sprintf(buf2, "%.2i:%.2i", File->CurrentFrame / File->FPS, File->CurrentFrame % File->FPS); ImGui::Text(buf2); /* + if (UI->IsDragging) { + ImGui::SameLine(); + sprintf(buf2, "X: %.3f, Y: %.3f", + ImGui::Text(buf2); + } + */ + /* ImGui::Button("V", TopbarButtonSize); ImGui::SameLine(); ImGui::Button("V", TopbarButtonSize); ImGui::SameLine(); ImGui::Button("V", TopbarButtonSize); ImGui::SameLine(); @@ -1259,6 +1270,11 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, MinVal_Y = (Keyframe->Value.f < MinVal_Y) ? Keyframe->Value.f : MinVal_Y; } + keyframe *FirstKeyframe = KeyframeLookup(Property, 0); + keyframe *LastKeyframe = KeyframeLookup(Property, Property->NumberOfTotalKeyframes - 1); + real32 MinVal_X = (Layer->BitmapInfo.FrameOffset + FirstKeyframe->FrameNumber); + real32 MaxVal_X = (Layer->BitmapInfo.FrameOffset + LastKeyframe->FrameNumber); + UI->Y_MaxVal = MaxVal_Y; UI->Y_MinVal = MinVal_Y; @@ -1275,39 +1291,72 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, DebugWatchVar("offset: ", &Y_TimelinePercentOffset, d_float); DebugWatchVar("zoom: ", &Y_TimelinePercentZoomed, d_float); + real32 Ratio_Graph_X = (MaxVal_X - MinVal_X) / File->NumberOfFrames; + real32 TimelineZoomSize = TimelineSizeWithBorder.x / UI->TimelinePercentZoomed; + real32 TimelineMoveSize = TimelineSizeWithBorder.x * UI->TimelinePercentOffset / UI->TimelinePercentZoomed; + real32 Y_TimelineZoomSize = TimelineSizeWithBorder.y / Y_TimelinePercentZoomed; + real32 Y_TimelineMoveSize = TimelineSizeWithBorder.y * Y_TimelinePercentOffset / Y_TimelinePercentZoomed; + for (int b = 0; b < Property->NumberOfTotalKeyframes; b++) { ImGui::PushID(b); - real32 TimelineZoomSize = TimelineSizeWithBorder.x / UI->TimelinePercentZoomed; - real32 TimelineMoveSize = TimelineSizeWithBorder.x * UI->TimelinePercentOffset / UI->TimelinePercentZoomed; - real32 Y_TimelineZoomSize = TimelineSizeWithBorder.y / Y_TimelinePercentZoomed; - real32 Y_TimelineMoveSize = TimelineSizeWithBorder.y * Y_TimelinePercentOffset / Y_TimelinePercentZoomed; keyframe *Keyframe = KeyframeLookup(Property, b); // Only used for drawing the bezier. - keyframe *NextKeyframe = (b != Property->NumberOfTotalKeyframes - 1) ? KeyframeLookup(Property, b) : NULL; + keyframe *NextKeyframe = (b != Property->NumberOfTotalKeyframes - 1) ? KeyframeLookup(Property, b + 1) : NULL; - real32 Ratio_X = (real32)(Layer->BitmapInfo.FrameOffset + Keyframe->FrameNumber) / File->NumberOfFrames; - real32 Ratio_Y = (Keyframe->Value.f - MinVal_Y) / (MaxVal_Y - MinVal_Y); + real32 Increment_X = (real32)1 / File->NumberOfFrames; + real32 UI_FrameDistance = Increment_X*TimelineZoomSize; - ImVec2 KeyframePos = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Ratio_X*TimelineZoomSize, - TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Ratio_Y)*Y_TimelineZoomSize); - ImGui::SetCursorScreenPos(KeyframePos); + int32 Keyframe_X = (Layer->BitmapInfo.FrameOffset + Keyframe->FrameNumber); + real32 Keyframe_Y = Keyframe->Value.f; + + real32 Ratio_X_Mid = (real32)Keyframe_X / File->NumberOfFrames; + real32 Ratio_Y_Mid = (Keyframe_Y - MinVal_Y) / (MaxVal_Y - MinVal_Y); + + ImVec2 KeyframePos_Mid = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Ratio_X_Mid*TimelineZoomSize, + TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Ratio_Y_Mid)*Y_TimelineZoomSize); + + ImGui::SetCursorScreenPos(KeyframePos_Mid); ImGui::Button("##keyframe", ImVec2(FontHeight, FontHeight)); + if (ImGui::IsItemHovered() && ImGui::IsKeyPressed(ImGuiKey_R)) { + UI->TempVal = Keyframe->Value.f; + UI->TempVal_X = Keyframe->FrameNumber; + } + if (ImGui::IsItemActivated()) { UI->IsDragging = true; UI->TempVal = Keyframe->Value.f; + UI->TempVal_X = Keyframe->FrameNumber; } if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) { - real32 MouseDeltaRatio = -io.MouseDelta.y / TimelineSizeWithBorder.y * Y_TimelinePercentZoomed; - if (io.KeyShift) - Ratio_Y = (real32)((int32)(Ratio_Y * 1000) / 10) / 100; - real32 Test = ((MaxVal_Y - MinVal_Y) * (Ratio_Y + MouseDeltaRatio)) + MinVal_Y; - DebugWatchVar("RatioY", &Ratio_Y, d_float); - DebugWatchVar("NewRatioY", &UI->Y_MaxVal, d_float); - Keyframe->Value.f = Test; + ImVec2 DragDelta = ImGui::GetMouseDragDelta(); + DragDelta = DragDelta + (ImVec2(UI->Wrap_X, UI->Wrap_Y) * TimelineSize); + DebugWatchVar("DragX", &DragDelta.x, d_float); + DebugWatchVar("DragY", &DragDelta.y, d_float); + DebugWatchVar("Wrap_X", &UI->Wrap_X, d_int); + DebugWatchVar("Wrap_Y", &UI->Wrap_Y, d_int); + real32 MouseDeltaRatio = -DragDelta.y / TimelineSizeWithBorder.y * Y_TimelinePercentZoomed; + ImVec2 Increment = ImVec2(DragDelta.x / UI_FrameDistance, ((MaxVal_Y - MinVal_Y) * MouseDeltaRatio)); + + // The plus 0.5 * X_Direction is for making the frame jump happen + // when the cursor is between two frames rather than when passing one. + real32 X_Direction = (Increment.x > 0) ? fabsf(Increment.x) / Increment.x : 0; + + Keyframe->FrameNumber = UI->TempVal_X + (int32)(Increment.x + 0.5*X_Direction); + Keyframe->Value.f = UI->TempVal + Increment.y; + + if (io.KeyShift) { + bool32 RestrainAxis = (fabsf(DragDelta.x) > fabsf(DragDelta.y)); + if (RestrainAxis) { + Keyframe->Value.f = UI->TempVal; + } else { + Keyframe->FrameNumber = UI->TempVal_X; + } + } + ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder); } // TODO(fox): This is kind of a mess. I built the graph around the @@ -1317,7 +1366,7 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, if (ImGui::IsItemDeactivated()) { if ((UI->TempVal >= MaxVal_Y) || Keyframe->Value.f == UI->Y_MaxVal) { - real32 Min = ((Ratio_Y <= 0.0f) && (UI->TempVal >= MaxVal_Y)) ? MinVal_Y : UI->Y_MinVal; + real32 Min = ((Ratio_Y_Mid <= 0.0f) && (UI->TempVal >= MaxVal_Y)) ? MinVal_Y : UI->Y_MinVal; real32 RealRatio_Y = (UI->Y_MaxVal - UI->Y_MinVal) / (MaxVal_Y - MinVal_Y); UI->Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed / RealRatio_Y; UI->Y_TimelinePercentOffset = (1.0f/RealRatio_Y + UI->Y_TimelinePercentOffset/RealRatio_Y - 1.0f); @@ -1327,6 +1376,67 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, UI->Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed / (1 - RealRatio_Y); } UI->IsDragging = false; + UI->Wrap_X = 0; + UI->Wrap_Y = 0; + } + + ImU32 col = ImGui::GetColorU32(ImGuiCol_ScrollbarGrab); + + ImVec2 Handle_Pos[2] = {}; + + if (Keyframe->Type == bezier) { + ImVec2 Handle_Ratio[2] = {}; + + Handle_Ratio[0] = ImVec2((real32)(Keyframe_X + Keyframe->TangentLeft.x) / File->NumberOfFrames, + (Keyframe_Y + Keyframe->TangentLeft.y - MinVal_Y) / (MaxVal_Y - MinVal_Y)); + Handle_Ratio[1] = ImVec2((real32)(Keyframe_X + Keyframe->TangentRight.x) / File->NumberOfFrames, + (Keyframe_Y + Keyframe->TangentRight.y - MinVal_Y) / (MaxVal_Y - MinVal_Y)); + + Handle_Pos[0] = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Handle_Ratio[0].x*TimelineZoomSize, + TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Handle_Ratio[0].y)*Y_TimelineZoomSize); + Handle_Pos[1] = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Handle_Ratio[1].x*TimelineZoomSize, + TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Handle_Ratio[1].y)*Y_TimelineZoomSize); + + draw_list->AddLine(KeyframePos_Mid, Handle_Pos[0], col, 1.0f); + draw_list->AddLine(KeyframePos_Mid, Handle_Pos[1], col, 1.0f); + + for (int i = 0; i < 2; i++) { + ImGui::SetCursorScreenPos(Handle_Pos[i]); + ImGui::Button((i == 0) ? "##keyframe_left" : "##keyframe_right", ImVec2(FontHeight, FontHeight)); + v2 *Tangent = (i == 0) ? &Keyframe->TangentLeft : &Keyframe->TangentRight; + + if (ImGui::IsItemActivated()) { + UI->IsDragging = true; + UI->TempVal_X = Tangent->x; + UI->TempVal = Tangent->y; + } + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + ImVec2 DragDelta = ImGui::GetMouseDragDelta(); + DragDelta = DragDelta + (ImVec2(UI->Wrap_X, UI->Wrap_Y) * TimelineSize); + ImVec2 MouseDeltaRatio = (ImVec2(1, -1) * DragDelta) / TimelineSizeWithBorder * ImVec2(UI->TimelinePercentZoomed / Ratio_Graph_X, Y_TimelinePercentZoomed); + real32 NewPos_X = ((MaxVal_X - MinVal_X) * MouseDeltaRatio.x); + real32 NewPos_Y = (io.KeyShift) ? 0 : ((MaxVal_Y - MinVal_Y) * MouseDeltaRatio.y); + *Tangent = V2(UI->TempVal_X, UI->TempVal) + V2(NewPos_X, NewPos_Y); + ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder); + } + + if (ImGui::IsItemDeactivated()) { + UI->IsDragging = false; + UI->Wrap_X = 0; + UI->Wrap_Y = 0; + } + } + } + + if (NextKeyframe) { + real32 Ratio_X_2 = (real32)(Layer->BitmapInfo.FrameOffset + NextKeyframe->FrameNumber) / File->NumberOfFrames; + real32 Ratio_Y_2 = (NextKeyframe->Value.f - MinVal_Y) / (MaxVal_Y - MinVal_Y); + + ImVec2 NextKeyframePos = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Ratio_X_2*TimelineZoomSize, + TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Ratio_Y_2)*Y_TimelineZoomSize); + draw_list->AddLine(KeyframePos_Mid, NextKeyframePos, col, 1.0f); } ImGui::PopID(); @@ -1371,6 +1481,7 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, BarH_Offset = Min(BarH_Offset, TimelineSize.x - BarH_SizeUI - BarHandleSize*4); ImVec2 BarH_PosUI = TimelineAbsolutePos + ImVec2(BarH_Offset, TimelineSize.y - BarThickness); + bool32 BarHeld = false; ImGui::SetCursorScreenPos(BarH_PosUI); ImGui::Button("##scrollbarleft", ImVec2(BarHandleSize, BarThickness)); @@ -1381,6 +1492,7 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, UI->TimelinePercentZoomed -= MouseDelta.x; UI->TimelinePercentOffset -= MouseDelta.x; } + BarHeld = true; } ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0)); @@ -1389,6 +1501,7 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) { UI->TimelinePercentOffset -= MouseDelta.x; + BarHeld = true; } ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0) + ImVec2(BarH_SizeUI, 0)); @@ -1399,6 +1512,11 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, if ((UI->TimelinePercentZoomed + MouseDelta.x) > BarMinZoom) { UI->TimelinePercentZoomed += MouseDelta.x; } + BarHeld = true; + } + + if (BarHeld) { + ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, 1); } Assert(UI->TimelinePercentZoomed > BarMinZoom); @@ -1423,6 +1541,7 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, BarV_Offset = Min(BarV_Offset, BarV_MaxSize - BarV_SizeUI - BarHandleSize*4); ImVec2 BarV_PosUI = TimelineAbsolutePos + ImVec2(TimelineSize.x - BarThickness, BarV_Offset); + BarHeld = false; ImGui::SetCursorScreenPos(BarV_PosUI); ImGui::Button("##h-scrollbarleft", ImVec2(BarThickness, BarHandleSize)); @@ -1431,14 +1550,21 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, { UI->Y_TimelinePercentZoomed -= MouseDelta.y; UI->Y_TimelinePercentOffset -= MouseDelta.y; + BarHeld = true; } ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize)); - ImGui::Button("##h-scrollbarhori", ImVec2(BarThickness, BarV_SizeUI)); + ImGui::Button("##h-scrollbar", ImVec2(BarThickness, BarV_SizeUI)); + + if (ImGui::IsItemHovered() && io.MouseWheel) + { + UI->Y_TimelinePercentOffset -= io.MouseWheel/10; + } if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) { UI->Y_TimelinePercentOffset -= MouseDelta.y; + BarHeld = true; } ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize) + ImVec2(0, BarV_SizeUI)); @@ -1447,15 +1573,58 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) { UI->Y_TimelinePercentZoomed += MouseDelta.y; + BarHeld = true; } UI->Y_TimelinePercentZoomed = Max(UI->Y_TimelinePercentZoomed, 0.01); + if (BarHeld) { + ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, 2); + } + draw_list->PopClipRect(); ImGui::PopClipRect(); ImGui::PopStyleVar(); + if (io.MouseWheel) { + // NOTE(fox): Change this if any other action is added when hovering over the bar area. + bool32 BarHovering_H = TestRectangle(TimelineAbsolutePos + ImVec2(0, TimelineSize.y - BarThickness), + TimelineAbsolutePos + ImVec2(TimelineSize.x, TimelineSize.y), + io.MousePos); + bool32 BarHovering_V = TestRectangle(TimelineAbsolutePos + ImVec2(TimelineSize.x - BarThickness, 0), + TimelineAbsolutePos + ImVec2(TimelineSize.x, TimelineSize.y), + io.MousePos); + if (BarHovering_H && io.MouseWheel) { + UI->TimelinePercentOffset -= io.MouseWheel/15; + } else if (BarHovering_V && io.MouseWheel) { + UI->Y_TimelinePercentOffset -= io.MouseWheel/15; + } else { + real32 Increment = 0.1; + bool32 Direction = (io.MouseWheel > 0) ? 1 : -1; + real32 Offset = (io.MousePos.y - (TimelineAbsolutePos.y + Y_TimelineMoveSize)) / Y_TimelineZoomSize; + real32 X_Offset = (io.MousePos.x - (TimelineAbsolutePos.x + TimelineMoveSize)) / TimelineZoomSize; + DebugWatchVar("X Offset", &X_Offset, d_float); + if (io.KeyShift) { + UI->Y_TimelinePercentOffset += Increment*Direction; + } else if (io.KeyCtrl) { + UI->TimelinePercentOffset += Increment*Direction*0.3; + } else { + if (Direction == 1) { + UI->Y_TimelinePercentZoomed -= (UI->Y_TimelinePercentZoomed * Increment); + UI->Y_TimelinePercentOffset -= (UI->Y_TimelinePercentOffset * Increment) + Offset*Increment; + UI->TimelinePercentZoomed -= (UI->TimelinePercentZoomed * Increment); + UI->TimelinePercentOffset -= (UI->TimelinePercentOffset * Increment) + X_Offset*Increment; + } else { + UI->Y_TimelinePercentOffset = ((UI->Y_TimelinePercentOffset + Offset*Increment) / (1.0f - Increment)); + UI->Y_TimelinePercentZoomed = (UI->Y_TimelinePercentZoomed / (1.0f - Increment)); + UI->TimelinePercentOffset = ((UI->TimelinePercentOffset + X_Offset*Increment) / (1.0f - Increment)); + UI->TimelinePercentZoomed = (UI->TimelinePercentZoomed / (1.0f - Increment)); + } + } + } + } + // General timeline interaction ImGui::SetCursorScreenPos(TimelineAbsolutePos); @@ -1550,14 +1719,20 @@ ImGui_ProcessInputs(project_data *File, project_state *State, comp_buffer *CompB // ImGui::SaveIniSettingsToDisk("asda"); #endif if (ImGui::IsKeyPressed(ImGuiKey_R)) { + /* UI->Y_TimelinePercentZoomed = UI->Default_Y_TimelinePercentZoomed; UI->Y_TimelinePercentOffset = UI->Default_Y_TimelinePercentOffset; keyframe *Keyframe = KeyframeLookup(&File->Layer[0]->x, 0); Keyframe->Value.f = -10; Keyframe = KeyframeLookup(&File->Layer[0]->x, 1); - Keyframe->Value.f = 0; + Keyframe->Value.f = -5; Keyframe = KeyframeLookup(&File->Layer[0]->x, 2); + Keyframe->Value.f = 0; + Keyframe = KeyframeLookup(&File->Layer[0]->x, 3); + Keyframe->Value.f = 5; + Keyframe = KeyframeLookup(&File->Layer[0]->x, 4); Keyframe->Value.f = 10; + */ } if (ImGui::IsKeyPressed(ImGuiKey_D)) { diff --git a/strings.cpp b/strings.cpp index 6caedd0..b8560d1 100644 --- a/strings.cpp +++ b/strings.cpp @@ -23,14 +23,15 @@ CopyStrings(void *Dest, void *Data) } } -static void * -String_GenerateFromChar(memory *Memory, char *Char) +static uint16 +String_AddToFile(memory *Memory, char *Char) { - void *Address = AllocateMemory(Memory, STRING_SIZE, F_Strings); + uint16 FileIndex = Memory_Block_AllocateNew(Memory, F_Strings); + void *Address = Memory_Block_AddressAtIndex(Memory, F_Strings, FileIndex); uint16 i = 0; while (Char[i] != '\0') { *((char *)Address + i) = Char[i]; i++; } - return Address; + return FileIndex; } @@ -0,0 +1,2 @@ + + |