summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFox Caminiti <fox@foxcam.net>2022-10-20 20:45:37 -0400
committerFox Caminiti <fox@foxcam.net>2022-10-20 20:45:37 -0400
commit7804c6a96d26c2e757d09f1864eb73fb81eb280f (patch)
tree0b8e3f1b6f8a6fdf79fb5ae51844f269e47d5959
parent0b0aa3b06fac0bcdeb31d5e2211d1ba149531692 (diff)
precomp recursion!
-rw-r--r--createcalls.cpp86
-rw-r--r--defines.h6
-rw-r--r--ffmpeg_backend.cpp2
-rw-r--r--functions.h5
-rw-r--r--imgui_helper_widgets.cpp68
-rw-r--r--imgui_ops.h10
-rw-r--r--main.cpp447
-rw-r--r--main.h205
-rw-r--r--memory.cpp93
-rw-r--r--memory.h1
-rw-r--r--my_imgui_widgets.cpp1773
-rw-r--r--prenderer.cpp435
-rw-r--r--threading.cpp97
13 files changed, 2248 insertions, 980 deletions
diff --git a/createcalls.cpp b/createcalls.cpp
index 2c2df61..6a830b5 100644
--- a/createcalls.cpp
+++ b/createcalls.cpp
@@ -12,17 +12,17 @@ Source_Generate(project_data *File, project_state *State, memory *Memory, void *
bool32 IsVideo = 0;
if (AV_IsFileSupported((char *)TempString, &IsVideo)) {
- uint16 Index = Memory_Block_AllocateNew(Memory, F_Strings);
+ 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");
History_Action_Block_Swap(Memory, F_Sources, Source);
Source->Occupied = 1;
- Source->Block_String_Index = String_AddToFile(Memory, (char *)TempString);
+ Source->Path_String_Index = String_AddToFile(Memory, (char *)TempString);
if (IsVideo)
- Source->SourceType = source_type_video;
+ Source->Type = source_type_video;
else
- Source->SourceType = source_type_image;
+ Source->Type = source_type_image;
History_Action_Swap(Memory, F_File, sizeof(File->Source_Count), &File->Source_Count);
File->Source_Count++;
@@ -45,6 +45,81 @@ Property_InitFloat(char *Name, real32 Val, real32 ScrubVal, real32 MinVal = PROP
return Property;
}
+static void
+Layer_Interact_Evaluate(memory *Memory, project_state *State, int32 *Frame_Start, int32 *Frame_End, real32 *Vertical_Offset)
+{
+ if (State->Interact_Active == interact_type_layer_move) {
+ *Frame_Start += (int32)(State->Interact_Offset[0] + 0);
+ *Frame_End += (int32)(State->Interact_Offset[0] + 0);
+ *Vertical_Offset += (int32)State->Interact_Offset[1];
+ } else if (State->Interact_Active == interact_type_layer_timeadjust) {
+ int Side[2] = {0};
+ Assert(State->Interact_Offset[1] == 0 || State->Interact_Offset[1] == 1);
+ Side[(int)State->Interact_Offset[1]] = 1;
+ *Frame_Start += (int32)(State->Interact_Offset[0] * Side[0]);
+ *Frame_End += (int32)(State->Interact_Offset[0] * Side[1]);
+ }
+}
+
+// TODO(fox): Precomps!
+void Layer_DeselectAll(memory *Memory, uint32 LayerCount) {
+ for (uint32 i = 0; i < LayerCount; i++) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (Layer->IsSelected)
+ Layer->IsSelected = false;
+ }
+}
+
+static sorted_layer *
+Layer_GetSortedArray(sorted_layer *LayerArrayStart, sorted_comp_info *SortedCompStart, uint32 TargetComp)
+{
+ uint32 LayerOffset = 0; int s = 0;
+ while (s < TargetComp) {
+ LayerOffset += SortedCompStart[s].LayerCount;
+ s++;
+ }
+ return LayerArrayStart + LayerOffset;
+}
+
+void Layer_SortAll(memory *Memory, sorted_layer *LayerArrayStart, sorted_comp_info *CompStart, uint32 LayerCount, uint32 CompCount)
+{
+ for (uint32 i = 0; i < LayerCount; 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;
+ 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);
+ uint32 SortedIndex_Playhead = 0;
+ while (SortedIndex_Playhead < SortedCompInfo->CurrentSortIndex) {
+ sorted_layer LayerEntry = SortedLayerInfo[SortedIndex_Playhead];
+ block_layer *TestLayer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, LayerEntry.Block_Layer_Index);
+ if (Layer->Vertical_Offset < TestLayer->Vertical_Offset) {
+ break;
+ } else {
+ SortedIndex_Playhead++;
+ }
+ }
+ if (SortedIndex_Playhead != SortedCompInfo->CurrentSortIndex) {
+ uint8 *Address_Start = (uint8 *)(SortedLayerInfo + SortedIndex_Playhead);
+ uint8 *Address_End = (uint8 *)(SortedLayerInfo + i);
+ Arbitrary_ShiftData(Address_Start, Address_End, sizeof(sorted_layer), 1);
+ }
+ sorted_layer *LayerEntry = SortedLayerInfo + SortedIndex_Playhead;
+ LayerEntry->Block_Layer_Index = i;
+ SortedCompInfo->CurrentSortIndex++;
+ }
+ Assert(CompStart[0].LayerCount == 2);
+ Assert(CompStart[1].LayerCount == 2);
+ // Assert(LayerArrayStart[0].Block_Layer_Index == 0);
+ // Assert(LayerArrayStart[1].Block_Layer_Index == 1);
+ // Assert(LayerArrayStart[2].Block_Layer_Index == 2);
+ // Assert(LayerArrayStart[3].Block_Layer_Index == 3);
+}
block_layer * Layer_Init(project_data *File, memory *Memory)
{
@@ -57,6 +132,7 @@ block_layer * Layer_Init(project_data *File, memory *Memory)
Layer->Block_String_Index = Memory_Block_AllocateNew(Memory, F_Strings);
block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Layer->Block_String_Index);
sprintf(String->Char, "Layer %i", File->Layer_Count + 1); // CSbros...
+ String->Occupied = 1;
Layer->x = Property_InitFloat("X Position", 0.0f, 1.0f);
Layer->y = Property_InitFloat("Y Position", 0.0f, 1.0f);
@@ -67,6 +143,8 @@ block_layer * Layer_Init(project_data *File, memory *Memory)
Layer->opacity = Property_InitFloat("Opacity", 1.0f, 0.005f, 0.0f, 1.0f);
Layer->time = Property_InitFloat("Frame Number", 0.0f, 1.0f, 0, 100000);
+ Layer->IsVisible = 1;
+
History_Action_Swap(Memory, F_File, sizeof(File->Layer_Count), &File->Layer_Count);
File->Layer_Count++;
diff --git a/defines.h b/defines.h
index c124f53..2ab2ba6 100644
--- a/defines.h
+++ b/defines.h
@@ -41,3 +41,9 @@ typedef uint64 ptrsize; // is there a compiler variable for 32 vs 64 bit like
#define AmountOf(Array) sizeof((Array)) / sizeof((Array)[1])
+#if ARM
+#define GetTime() Assert(0)
+#else
+#define GetTime() __rdtsc()
+#endif
+
diff --git a/ffmpeg_backend.cpp b/ffmpeg_backend.cpp
index b986d7a..a54fb51 100644
--- a/ffmpeg_backend.cpp
+++ b/ffmpeg_backend.cpp
@@ -125,7 +125,7 @@ void AV_Init(block_source *Source, av_info *AV, memory *Memory)
// The two calls below theoretically shouldn't fail since we already tested them in IsFileSupported.
AV->FileFormatContext = avformat_alloc_context();
- char *Path = (char *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Block_String_Index);
+ char *Path = (char *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Path_String_Index);
err = avformat_open_input(&AV->FileFormatContext, Path, NULL, NULL);;
if (err < 0) {
fprintf(stderr, "Libav error: (%s)\n", av_err2str(err));
diff --git a/functions.h b/functions.h
index 88278fe..4fe5a0e 100644
--- a/functions.h
+++ b/functions.h
@@ -1,5 +1,10 @@
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);
+
# if 0
// Buffer management
diff --git a/imgui_helper_widgets.cpp b/imgui_helper_widgets.cpp
new file mode 100644
index 0000000..105dad2
--- /dev/null
+++ b/imgui_helper_widgets.cpp
@@ -0,0 +1,68 @@
+// Widgets not directly related to drawing UI.
+
+// Returns a normalized UV position of the composition
+static v2
+ImGui_ScreenPointToCompUV(ImVec2 ViewportMin, ImVec2 CompPos, ImVec2 CompZoom, ImVec2 MousePos)
+{
+ ImVec2 LocalMousePos = MousePos - ViewportMin;
+ ImVec2 LocalCompPos = CompPos - ViewportMin;
+ ImVec2 MouseScreenUV = LocalMousePos - LocalCompPos;
+ ImVec2 Result = MouseScreenUV / CompZoom;
+ return V2(Result);
+}
+
+// NOTE(fox): We have to do a bit of hackery here to tell how many times the
+// mouse has been warped 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_WarpMouse(ui *UI, ImVec2 MousePos, ImVec2 Min, ImVec2 Max, int Direction = 3)
+{
+ if (Direction & 1) {
+ if (MousePos.x < Min.x) {
+ UI->Warp_WantSetPos = true;
+ UI->Warp_PositionToSet = ImVec2(Max.x - 5, MousePos.y);
+ UI->Warp_PositionInitial = MousePos.x;
+ UI->Warp_Direction = 0;
+ }
+ if (MousePos.x > Max.x) {
+ UI->Warp_WantSetPos = true;
+ UI->Warp_PositionToSet = ImVec2(Min.x + 5, MousePos.y);
+ UI->Warp_PositionInitial = MousePos.x;
+ UI->Warp_Direction = 1;
+ }
+ }
+ if (Direction & 2) {
+ if (MousePos.y < Min.y) {
+ UI->Warp_WantSetPos = true;
+ UI->Warp_PositionToSet = ImVec2(MousePos.x, Max.y - 5);
+ UI->Warp_PositionInitial = MousePos.y;
+ UI->Warp_Direction = 2;
+ }
+ if (MousePos.y > Max.y) {
+ UI->Warp_WantSetPos = true;
+ UI->Warp_PositionToSet = ImVec2(MousePos.x, Min.y + 5);
+ UI->Warp_PositionInitial = MousePos.y;
+ UI->Warp_Direction = 3;
+ }
+ }
+}
+
+// We record the initial position and the direction of the wrap, and only
+// increment the wrap amount when MousePos actually is measured to be what we expect.
+
+static void
+ImGui_WarpMouseFinish(ui *UI, ImVec2 MousePos)
+{
+ if (UI->Warp_Direction == 0) {
+ if (MousePos.x < UI->Warp_PositionInitial) UI->Warp_X--;
+ } else if (UI->Warp_Direction == 1) {
+ if (MousePos.x > UI->Warp_PositionInitial) UI->Warp_X++;
+ } else if (UI->Warp_Direction == 2) {
+ if (MousePos.y < UI->Warp_PositionInitial) UI->Warp_Y--;
+ } else if (UI->Warp_Direction == 3) {
+ if (MousePos.y > UI->Warp_PositionInitial) UI->Warp_Y++;
+ } else {
+ Assert(0);
+ }
+}
diff --git a/imgui_ops.h b/imgui_ops.h
index 629815f..2249de3 100644
--- a/imgui_ops.h
+++ b/imgui_ops.h
@@ -58,6 +58,16 @@ ImVec2 operator/(ImVec2 A, ImVec2 B)
return Result;
}
+ImVec2 operator/(ImVec2 A, real32 B)
+{
+ ImVec2 Result;
+
+ Result.x = A.x / B;
+ Result.y = A.y / B;
+
+ return Result;
+}
+
inline bool32
IsRectTouching(ImVec2 Min1, ImVec2 Max1, ImVec2 Min2, ImVec2 Max2)
{
diff --git a/main.cpp b/main.cpp
index d0349fa..d6f9e71 100644
--- a/main.cpp
+++ b/main.cpp
@@ -44,20 +44,21 @@
#include "functions.h"
// #include "sharebuffer.h"
-SDL_atomic_t Render_Interrupt;
-// SDL_atomic_t CurrentEntry;
-// SDL_atomic_t QueuedEntries;
-// SDL_atomic_t CompletedEntries;
-static bool32 IsRendering = false;
+SDL_Thread *Thread[8];
+SDL_sem *Semaphore;
+
+SDL_atomic_t CurrentEntry;
+SDL_atomic_t QueuedEntries;
+SDL_atomic_t CompletedEntries;
+
+render_entry Entries[256];
+
+static uint64 BitmapBlockSize;
static instruction_mode InstructionMode = instruction_mode_scalar;
static uint32 RandomGlobalIncrement = 0;
// render_entry Entries[256];
-// SDL_Thread *thread[8];
-SDL_Thread *MainRenderThread;
-SDL_sem *Semaphore;
-
#include "memory.cpp"
#include "undo.cpp"
#include "strings.cpp"
@@ -67,47 +68,217 @@ SDL_sem *Semaphore;
#include "createcalls.cpp"
#include "ffmpeg_backend.cpp"
#include "my_imgui_widgets.cpp"
+#include "prenderer.cpp"
#include "gl_calls.cpp"
#if 0
#include "effects.cpp"
#include "keyframes.cpp"
#include "layer.cpp"
#include "bezier.cpp"
-#include "prenderer.cpp"
#include "bitmap_calls.cpp"
#endif
+static void
+Main_RenderUI(ImGuiIO io, ImVec4 clear_color, SDL_Window *window)
+{
+ printf("Call ImGui::Render\n");
+ ImGui::Render();
+ glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
+ glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
+ glClear(GL_COLOR_BUFFER_BIT);
+ printf("Call GL renderer\n");
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+ printf("Call window swap\n");
+ SDL_GL_SwapWindow(window);
+}
+
+static void
+Main_InputTest(project_data *File, project_state *State, memory *Memory, ui *UI, SDL_Window *window, GLuint textureID)
+{
+ ImGuiIO& io = ImGui::GetIO();
+ SDL_Event event = {};
+ while (SDL_PollEvent(&event))
+ {
+ ImGui_ImplSDL2_ProcessEvent(&event);
+ if (event.type == SDL_DROPFILE) {
+ char *DropFile = event.drop.file;
+ Source_Generate(File, State, Memory, DropFile);
+ SDL_free(DropFile);
+ }
+ if (event.type == SDL_QUIT)
+ State->IsRunning = false;
+ if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
+ State->IsRunning = false;
+ }
+
+ if (UI->Warp_WantSetPos) {
+ ImGui::GetIO().WantSetMousePos = true;
+ io.MousePos = UI->Warp_PositionToSet;
+ }
+
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplSDL2_NewFrame();
+
+ ImGui::NewFrame();
+
+ if (UI->Warp_WantSetPos) {
+ ImGui_WarpMouseFinish(UI, io.MousePos);
+ io.MouseDelta = {};
+ UI->Warp_WantSetPos = false;
+ }
+
+ ImGui::DockSpaceOverViewport();
+
+ if (!io.WantCaptureKeyboard)
+ ImGui_ProcessInputs(State, 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);
+ if (Debug.ToggleWindow) {
+ ImGui::ShowDemoWindow();
+ ImGui_DebugMemoryViewer(&File, &Memory);
+ }
+#endif
+
+#endif
+
+ ImGui_Viewport(File, State, UI, Memory, io, textureID);
+ ImGui_Timeline(File, State, Memory, UI, io);
+ ImGui_File(File, State, Memory, io);
+
+ ImGui_DebugMemoryViewer(State);
+ // ImGui::ShowDemoWindow();
+
+#if DEBUG
+ Debug.Temp = {};
+#endif
+
+ ImGui::EndFrame();
+}
+
static void
-MainFunction(main_sdl *Main, memory *Memory,
- project_state *State, project_data *File,
- comp_buffer *CompBuffer)
+Render_Comp(project_data *File, project_state *State, memory *Memory, ImGuiIO io, sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray, uint32 CompIndex)
{
- Bitmap_Clear(CompBuffer->PackedBuffer, CompBuffer->Width, CompBuffer->Height, CompBuffer->BytesPerPixel);
- Bitmap_Clear(CompBuffer->UnpackedBuffer, CompBuffer->Width, CompBuffer->Height, CompBuffer->BytesPerPixel);
- for (int i = 0; i < File->NumberOfLayers; i++) {
- project_layer *Layer = File->Layer[i];
- if (Layer->StartFrame <= File->CurrentFrame &&
- Layer->EndFrame >= File->CurrentFrame)
+ 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);
+ void *CompBuffer = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry_Main->Block_StartIndex);
+ 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;
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical);
+ if (Layer->Frame_Start <= State->Frame_Current &&
+ Layer->Frame_End >= State->Frame_Current && Layer->IsVisible)
{
- if (State->UpdateKeyframes) {
- for (int p = 0; p < Layer->NumberOfEffects; p++) {
- for (int o = 0; o < Layer->Effect[p]->NumberOfProperties; o++) {
- CalculateKeyframesLinearly(File->CurrentFrame, &Layer->Effect[p]->Property[o]);
- }
+ layer_bitmap_state *BitmapState = &State->Render.Bitmap[Index_Physical];
+ 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;
}
- for (int r = 0; r < AmountOf(Layer->Property); r++) {
- CalculateKeyframesLinearly(File->CurrentFrame, &Layer->Property[r]);
+ 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);
+ if (!Entry->IsCached) {
+ uint64 Src_TimeStart = GetTime();
+ Render_Comp(File, State, Memory, io, SortedCompArray, SortedLayerArray, Layer->Block_Source_Index);
+ Layer->x.CurrentValue = (Layer->Block_Source_Index == 0) ? 200 : Comp->Width/2;
+ Layer->y.CurrentValue = Comp->Height/2;
+ Entry->CycleTime = GetTime() - Src_TimeStart;
}
+ BitmapAddress = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry->Block_StartIndex);
+ }
+ Assert(BitmapAddress);
+
+ // for (int a = 0; a < Layer->Block_Mask_Count; a++) {
+ // }
+ // for (int a = 0; a < Layer->Block_Effect_Count; a++) {
+ // }
+
+ transform_info T = Transform_Calculate(State, Memory, File, Layer, Comp);
+ T.SourceBuffer = BitmapAddress;
+ rectangle RenderRegion = {0, 0, Comp->Width, Comp->Height};
+
+ bool32 IsRendering = true;
+ Renderer_Start((void *)&T, CompBuffer, RenderRegion);
+ while (IsRendering) {
+ SDL_Delay(2);
+ Renderer_Check(&IsRendering);
+ // TODO(fox): Make interruptable if the render time gets too long.
}
- Layer_UpdateBitmap(File, Layer, Memory, File->CurrentFrame);
}
}
- State->UpdateKeyframes = false;
- QueueCurrentFrame(File, CompBuffer, State);
}
-#endif
+
+static void
+Main_Renderer(project_data *File, project_state *State, memory *Memory, SDL_Window *window, GLuint textureID, ImGuiIO io)
+{
+ State->UpdateFrame = false;
+
+ uint64 Comp_TimeStart = GetTime();
+
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ cache_entry *Entry_Main = Memory_Cache_Search(State, Memory, State->Render.Entry, cache_entry_type_comp, File->PrincipalCompIndex, State->Frame_Current);
+ void *MainCompBuffer = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry_Main->Block_StartIndex);
+
+ // NOTE(fox): All layers are given a slot here
+ 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);
+
+ Render_Comp(File, State, Memory, io, SortedCompArray, SortedLayerArray, File->PrincipalCompIndex);
+
+ Memory_PopScratch(Memory, SortSize);
+
+ glBindTexture(GL_TEXTURE_2D, textureID);
+
+ int ByteFlag2 = (MainComp->BytesPerPixel == 4) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT;
+ if (!Entry_Main->CycleTime)
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MainComp->Width, MainComp->Height, 0, GL_RGBA, ByteFlag2, MainCompBuffer);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, MainComp->Width, MainComp->Height, GL_RGBA, ByteFlag2, MainCompBuffer);
+
+ Entry_Main->CycleTime = GetTime() - Comp_TimeStart;
+
+ // TODO(fox): garbage collect AV state!
+}
int main(int argc, char *argv[]) {
@@ -131,7 +302,7 @@ int main(int argc, char *argv[]) {
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, 10 * 1024 * 1024, P_MiscCache, "Misc persistent");
+ Memory_InitTable(&GlobalMemory, &Memory, 40 * 1024 * 1024, P_MiscCache, "Misc persistent");
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));
@@ -142,7 +313,8 @@ int main(int argc, char *argv[]) {
Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Strings, "Strings", sizeof(block_string));
Memory_InitTable(&GlobalMemory, &Memory, (uint64)64 * 1024 * 1024, B_ScratchSpace, "Scratch");
- // Memory_InitTable(&GlobalMemory, &Memory, (uint64)200 * 1024 * 1024, B_CachedBitmaps, "Cached bitmap buffer");
+ // Memory_InitTable(&GlobalMemory, &Memory, (uint64)1 * 1024 * 1024, B_CachedBitmapInfo, "Cached bitmap info");
+ Memory_InitTable(&GlobalMemory, &Memory, (uint64)50 * 1024 * 1024, B_CachedBitmaps, "Cached bitmap buffer");
#if ARM
InstructionMode = instruction_mode_neon;
@@ -160,45 +332,98 @@ int main(int argc, char *argv[]) {
project_data *File = (project_data *)Memory_Block_AllocateAddress(&Memory, F_File);
*File = {};
File->Occupied = 1;
+
+ ui UI = {};
+
block_composition *MainComp = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps);
+ // MainComp->Width = 3840;
+ // MainComp->Height = 2160;
MainComp->Width = 1280;
MainComp->Height = 720;
MainComp->FPS = 24;
MainComp->BytesPerPixel = 4;
-
MainComp->Frame_Count = 48;
MainComp->Frame_End = 48;
+ MainComp->Occupied = 1;
+ MainComp->Name_String_Index = String_AddToFile(&Memory, "Main comp");
+
+ block_composition *Comp2 = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps);
+ Comp2->Width = 500;
+ Comp2->Height = 500;
+ Comp2->FPS = 24;
+ Comp2->BytesPerPixel = 4;
+ Comp2->Frame_Count = 48;
+ 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;
{
uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/a.jpg");
block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 0);
Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End);
- block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 0);
+ }
+
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 0);
- History_Undo(&Memory);
- History_Redo(&Memory);
+ {
+ uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/b.jpg");
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 1);
+
+ Layer_CreateFromSource(File, State, &Memory, 1, MainComp->Frame_End);
+ Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End);
}
- State->Render.MainCompBuffer = (void *)((uint8 *)Memory.Slot[P_MiscCache].Address + sizeof(project_state));
+ {
+ 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 *Layer1 = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 0);
+ Layer1->Vertical_Offset = 0;
+
+ block_layer *Layer2 = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 1);
+ Layer2->IsPrecomp = true;
+ Layer2->Vertical_Offset = 1;
+ Layer2->Col[0] = 1;
+
+ block_layer *Layer3 = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 2);
+ Layer3->Vertical_Offset = 0;
+ Layer3->Col[1] = 1;
+ Layer3->Block_Composition_Index = 1;
+
+ block_layer *Layer4 = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 3);
+ Layer4->Vertical_Offset = 1;
+ Layer4->Col[2] = 1;
+ Layer4->Block_Composition_Index = 1;
+
+
+ // History_Undo(&Memory);
+ // History_Redo(&Memory);
SDL_Init(SDL_INIT_VIDEO);
-#if 0
-#if THREADED
- thread_info ThreadInfo[7];
+ Semaphore = SDL_CreateSemaphore(0);
+
+#if THREADED
+ int Index[7];
for (int i = 0; i < 7; i++) {
- char str[256];
- ThreadInfo[i].Index = i;
- ThreadInfo[i].RenderInfo = &RenderInfo;
- thread[i] = SDL_CreateThread(TestThread, str, &ThreadInfo[i]);
+ Index[i] = i;
+ Thread[i] = SDL_CreateThread(TestThread, "thread", (void *)&Index[i]);
}
#endif
-#endif
// Decide GL+GLSL versions
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100
const char* glsl_version = "#version 100";
@@ -281,141 +506,37 @@ int main(int argc, char *argv[]) {
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
ImGui_ImplOpenGL3_Init(glsl_version);
- int64 i = 0;
- while (i < MainComp->Width*MainComp->Height) {
- *((uint32 *)State->Render.MainCompBuffer + i++) = 0xFF2F0000;
- }
-
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MainComp->Width, MainComp->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, State->Render.MainCompBuffer);
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
- // Semaphore = SDL_CreateSemaphore(0);
- // MainRenderThread = SDL_CreateThread(MainRenderer, "Main render thread", &State->Render);
-
while (State->IsRunning)
{
- SDL_Event event;
- while (SDL_PollEvent(&event))
- {
- ImGui_ImplSDL2_ProcessEvent(&event);
- if (event.type == SDL_DROPFILE) {
- char *DropFile = event.drop.file;
- Source_Generate(File, State, &Memory, DropFile);
- SDL_free(DropFile);
- }
- if (event.type == SDL_QUIT)
- State->IsRunning = false;
- if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
- 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;
- // }
+ printf("Call UI\n");
+ Main_InputTest(File, State, &Memory, &UI, window, textureID);
- ImGui::DockSpaceOverViewport();
-
-#if 0
- if (!io.WantCaptureKeyboard)
- ImGui_ProcessInputs(&File, &State, &CompBuffer, &Memory, &UI, io);
-
- 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_Timeline(&File, &State, &Memory, &UI, io);
- // ImGui_Graph(&File, &State, &Memory, &UI, io);
-#endif
-
- ImGui_Viewport(MainComp, textureID);
- ImGui::ShowDemoWindow();
-
-#if 0
-#if DEBUG
- ImGui_DebugUndoTree(&File, &Memory);
- if (Debug.ToggleWindow) {
- ImGui::ShowDemoWindow();
- ImGui_DebugMemoryViewer(&File, &Memory);
- }
-#endif
-
- if (UI.TemporaryUpdateOverride) {
- UI.TemporaryUpdateOverride = 0;
- State.UpdateFrame = 1;
+ if (State->UpdateFrame) {
+ printf("Call renderer\n");
+ Main_Renderer(File, State, &Memory, window, textureID, io);
}
- if (UI.Initializing)
- UI.Initializing--;
+ Assert(Debug.ScratchState == 0);
- // if (File.CurrentFrame != shmp->shared_framenumber) {
- // File.CurrentFrame = shmp->shared_framenumber;
- // }
+ printf("Call render UI\n");
+ Main_RenderUI(io, clear_color, window);
- // Right now IsRendering does nothing. I have it here if we want to
- // completely detatch the rendering updater onto its own thread so the
- // UI never lags.
-#endif
-
-#if 0
- if (State.UpdateFrame && !IsRendering) {
- MainFunction(0, &Memory, &State, &File, &CompBuffer);
- State.UpdateFrame = 0;
- }
-
-#if THREADED
- uint32 C = SDL_AtomicGet(&CompletedEntries);
- if (IsRendering) {
- while (C != 16) {
- C = SDL_AtomicGet(&CompletedEntries);
- CheckQueue(RenderInfo, 8);
- }
- C = SDL_AtomicGet(&CompletedEntries);
- if (C == 16) {
- FinishRenderAndUpload(&State, &CompBuffer, textureID);
- }
- }
-#else
- if (IsRendering) {
- FinishRenderAndUpload(&State, &CompBuffer, textureID);
- }
-#endif
-#endif
-
- ImGui::Render();
- glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
- glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
- glClear(GL_COLOR_BUFFER_BIT);
- ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
- SDL_GL_SwapWindow(window);
+ // TODO(fox): Fix things that rely on this.
+ if (State->Initializing)
+ State->Initializing--;
}
- // for (int i = 0; i < 7; i++) {
- // SDL_DetachThread(thread[i]);
- // }
- // shm_unlink("/testl");
+ for (int i = 0; i < 7; i++) {
+ SDL_DetachThread(Thread[i]);
+ }
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
diff --git a/main.h b/main.h
index 8e29917..9ccb19e 100644
--- a/main.h
+++ b/main.h
@@ -44,6 +44,30 @@ struct block_string {
char Char[1024 - sizeof(uint8)];
};
+struct bitmap_cache_status
+{
+ uint32 Block_End;
+};
+
+enum cache_entry_type
+{
+ cache_entry_type_assert,
+ cache_entry_type_comp,
+ cache_entry_type_source,
+ cache_entry_type_layer,
+};
+
+struct cache_entry
+{
+ uint8 IsOccupied;
+ uint8 IsCached;
+ uint64 CycleTime;
+ uint32 Block_StartIndex;
+ enum cache_entry_type Type;
+ uint32 TypeInfo;
+ uint32 TypeInfo_Sub;
+};
+
enum interpolation_type
{
interpolation_type_linear,
@@ -63,9 +87,76 @@ struct block_bezier {
bezier_point Point[MAX_KEYFRAMES_PER_BLOCK];
};
+struct layer_bitmap_state {
+ // Image and video
+ bool32 ToUpdate = 1;
+
+ // GL state
+ // gl_effect_layer Test;
+ // gl_effect_layer TestM;
+
+ // Video state
+ 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
+};
+
struct render_state
{
- void *MainCompBuffer;
+ struct layer_bitmap_state Bitmap[MAX_LAYERS];
+ cache_entry Entry[2048];
+};
+
+enum focused_window
+{
+ focus_viewport,
+ focus_properties,
+ focus_timeline
+};
+
+struct sorted_comp_info
+{
+ uint32 LayerCount;
+ uint32 CurrentSortIndex;
+};
+
+struct sorted_layer
+{
+ uint16 Block_Layer_Index;
+};
+
+struct ui
+{
+ ImVec2 CompZoom; // In screen pixels, not percentage.
+ ImVec2 CompPos;
+
+ // Under 1 is zoomed in!
+ ImVec2 TimelinePercentZoomed;
+ ImVec2 TimelinePercentOffset;
+
+ bool32 BoxSelect;
+ ImVec2 DragDelta_Prev; // TODO(fox): Make native ImGui?
+
+ focused_window FocusedWindow; // Convenience for adding window-specific hotkeys.
+
+ v2 TempZoomRatio = V2(1, 1);
+
+ int32 Warp_X = 0;
+ int32 Warp_Y = 0;
+ bool32 Warp_WantSetPos = false;
+ ImVec2 Warp_PositionToSet;
+ real32 Warp_PositionInitial;
+ int32 Warp_Direction;
+};
+
+struct pen_state {
+ bool32 IsActive;
+};
+
+enum interact_type
+{
+ interact_type_none,
+ interact_type_layer_move,
+ interact_type_layer_timeadjust
};
struct project_state
@@ -76,8 +167,9 @@ struct project_state
render_state Render;
+ int32 Frame_Current;
// tool Tool = tool_default;
- // pen_state Pen = {};
+ pen_state Pen = {};
bool32 IsRunning = 1;
bool32 IsPlaying;
@@ -85,9 +177,11 @@ struct project_state
int16 MostRecentlySelectedLayer = -1;
// selection_type RecentSelectionType = selection_none;
- bool32 IsInteracting;
- real32 InteractCache[6];
- // interact_type InteractType;
+ interact_type Interact_Active;
+ real32 Interact_Offset[4];
+ void *Interact_Address;
+
+ int32 Initializing = 3;
int32 MsgTime; // currently in "frames"
char *Msg;
@@ -101,19 +195,22 @@ struct project_data
uint8 Occupied;
uint16 Layer_Count;
uint16 Source_Count;
+ uint16 Comp_Count;
uint16 PrincipalCompIndex;
};
struct block_composition
{
uint8 Occupied;
+
+ uint16 Name_String_Index;
+
uint16 Width;
uint16 Height;
uint16 FPS;
uint16 BytesPerPixel;
uint32 Frame_Count;
- int32 Frame_Current;
int32 Frame_Start;
int32 Frame_End;
};
@@ -128,7 +225,8 @@ struct block_source
{
uint8 Occupied;
- uint16 Block_String_Index;
+ uint16 Path_String_Index;
+ uint16 Name_String_Index;
// Image and video
uint16 Width;
uint16 Height;
@@ -138,7 +236,7 @@ struct block_source
real32 FPS;
real32 AvgPTSPerFrame; // set by Libav
- source_type SourceType;
+ source_type Type;
};
struct property_header
@@ -163,24 +261,12 @@ struct property_channel {
bool32 IsToggled;
};
-struct layer_bitmap_state {
- // Image and video
- bool32 ToUpdate = 1;
-
- // GL state
- // gl_effect_layer Test;
- // gl_effect_layer TestM;
-
- // Video state
- 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
-};
-
struct block_layer {
uint8 Occupied;
+ bool32 IsPrecomp;
+ uint16 Block_Source_Index; // also used for precomp
uint16 Block_String_Index;
- uint16 Block_Source_Index;
uint16 Block_Composition_Index;
uint16 Block_Mask_Index[MAX_EFFECTS];
@@ -208,14 +294,60 @@ struct block_layer {
};
bool32 IsSelected;
+ bool32 IsVisible;
+ bool32 IsAdjustment;
int32 Frame_Offset;
int32 Frame_Start;
int32 Frame_End;
- uint32 LayerColor;
+ real32 Vertical_Offset;
+ real32 Vertical_Height = 1;
+
+ real32 Col[3];
};
+struct render_byte_info {
+ uint32 MaskPixel;
+ uint32 ByteOffset;
+ uint32 Bits;
+ real32 Normalized;
+};
+
+struct transform_info {
+ real32 XAxisPX;
+ real32 XAxisPY;
+ real32 YAxisPX;
+ real32 YAxisPY;
+
+ real32 LayerWidth;
+ real32 LayerHeight;
+ real32 LayerBytesPerPixel;
+ real32 LayerPitch;
+ render_byte_info LayerBits;
+
+ real32 BufferWidth;
+ real32 BufferHeight;
+ real32 BufferBytesPerPixel;
+ real32 BufferPitch;
+ render_byte_info BufferBits;
+
+ real32 LayerOpacity;
+ real32 OriginX;
+ real32 OriginY;
+ blend_mode BlendMode;
+
+ bool32 IsAdjustment;
+
+ rectangle ClipRect;
+ void *SourceBuffer;
+};
+
+struct render_entry {
+ void *RenderData;
+ void *OutputBuffer;
+ rectangle RenderRegion;
+};
#if 0
@@ -247,25 +379,6 @@ struct comp_buffer {
void *UnpackedBuffer;
};
-struct transform_info {
- real32 XAxisPX;
- real32 XAxisPY;
- real32 YAxisPX;
- real32 YAxisPY;
- real32 LayerWidth;
- real32 LayerHeight;
- uint32 FullLayerWidth;
- uint32 FullLayerHeight;
- real32 LayerOpacity;
- blend_mode BlendMode;
- real32 OriginX;
- real32 OriginY;
- uint32 BufferPitch;
- uint32 LayerPitch;
- rectangle ClipRect;
- void *SourceBuffer;
-};
-
struct mask_point {
v2 Pos;
bool32 HandleBezier;
@@ -318,14 +431,6 @@ struct pen_state {
bool32 IsActive;
};
-enum interact_type
-{
- interact_type_keyframe_move,
- interact_type_keyframe_rotate,
- interact_type_keyframe_scale
-}
-
-
struct brush_tool
{
real32 Size;
diff --git a/memory.cpp b/memory.cpp
index f502eb2..1dd7ae6 100644
--- a/memory.cpp
+++ b/memory.cpp
@@ -8,15 +8,6 @@ Memory_InitTable(global_memory *GlobalMemory, memory *Memory, uint64 Size, memor
GlobalMemory->CurrentPosition += Size;
}
-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)
{
@@ -29,7 +20,7 @@ Memory_Block_AllocateNew(memory *Memory, memory_table_list TableName)
Address_Playhead += Table->Block_ElementSize;
Index++;
}
- Memory_Zero(Address_Playhead, Table->Block_ElementSize);
+ Arbitrary_Zero(Address_Playhead, Table->Block_ElementSize);
return Index;
}
@@ -50,6 +41,88 @@ Memory_Block_AllocateAddress(memory *Memory, memory_table_list TableName)
return Memory_Block_AddressAtIndex(Memory, TableName, FileIndex);
}
+static uint32
+Memory_Block_Bitmap_AllocateNew(project_state *State, memory *Memory, cache_entry Entry, uint64 NewSize)
+{
+ uint32 LastVal = 0;
+ uint32 LastBlock = 0;
+ uint32 c = 0;
+ cache_entry *EntryArray = State->Render.Entry;
+ while (EntryArray[c].IsOccupied != 0) {
+ if (EntryArray[c].Block_StartIndex > LastBlock) {
+ LastBlock = EntryArray[c].Block_StartIndex;
+ LastVal = c;
+ }
+ c++;
+ }
+ cache_entry LastEntry = EntryArray[LastVal];
+ uint32 LastEntry_BlockCount = 0;
+ switch (EntryArray[LastVal].Type) {
+ case cache_entry_type_comp:
+ {
+ block_composition Comp = *(block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, LastEntry.TypeInfo);
+ uint64 Size = Comp.Width * Comp.Height * Comp.BytesPerPixel;
+ LastEntry_BlockCount = (Size / BitmapBlockSize) + 1;
+ } break;
+ case cache_entry_type_source:
+ {
+ block_source Source = *(block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, LastEntry.TypeInfo);
+ uint64 Size = Source.Width * Source.Height * Source.BytesPerPixel;
+ LastEntry_BlockCount = (Size / BitmapBlockSize) + 1;
+ } break;
+ case cache_entry_type_layer:
+ {
+ Assert(0);
+ } break;
+ case cache_entry_type_assert:
+ {
+ Assert(0);
+ } break;
+ default:
+ {
+ Assert(0);
+ } break;
+ }
+
+ return LastBlock + LastEntry_BlockCount;
+
+ /*
+ uint32 Blocks_Needed = (NewSize / BitmapBlockSize) + 1;
+ uint32 Blocks_Max = Memory->Slot[B_CachedBitmaps].Size / BitmapBlockSize;
+ uint32 Block_Index_Available = 0;
+ */
+
+}
+
+static cache_entry *
+Memory_Cache_Search(project_state *State, memory *Memory, cache_entry *EntryArray, cache_entry_type Type, uint32 TypeInfo, uint32 TypeInfo_Sub)
+{
+ int c = 0;
+ while (EntryArray[c].IsOccupied != 0) {
+ if (EntryArray[c].Type == Type &&
+ EntryArray[c].TypeInfo == TypeInfo &&
+ EntryArray[c].TypeInfo_Sub == TypeInfo_Sub) {
+ return &EntryArray[c];
+ }
+ c++;
+ }
+ if (c != 0)
+ EntryArray[c].Block_StartIndex = Memory_Block_Bitmap_AllocateNew(State, Memory, EntryArray[c], 0);
+ EntryArray[c].IsOccupied = true;
+ EntryArray[c].Type = Type;
+ EntryArray[c].TypeInfo = TypeInfo;
+ EntryArray[c].TypeInfo_Sub = TypeInfo_Sub;
+ return &EntryArray[c];
+}
+
+static void *
+Memory_Block_Bitmap_AddressAtIndex(memory *Memory, uint32 Index)
+{
+ memory_table *Table = &Memory->Slot[B_CachedBitmaps];
+ uint8 *Address = (uint8 *)Table->Address + Index*BitmapBlockSize;
+ return (void *)Address;
+}
+
static void *
Memory_PushScratch(memory *Memory, uint64 Size) {
memory_table *Table = &Memory->Slot[B_ScratchSpace];
diff --git a/memory.h b/memory.h
index 51b207a..6e704bf 100644
--- a/memory.h
+++ b/memory.h
@@ -15,6 +15,7 @@ enum memory_table_list {
F_Strings,
B_ScratchSpace,
+ // B_CachedBitmapInfo,
B_CachedBitmaps,
};
diff --git a/my_imgui_widgets.cpp b/my_imgui_widgets.cpp
index e8159fe..179dff2 100644
--- a/my_imgui_widgets.cpp
+++ b/my_imgui_widgets.cpp
@@ -2,32 +2,1083 @@
#include "my_imgui_internal_widgets.h"
#include "imgui_ops.h"
+#include "imgui_helper_widgets.cpp"
+
+static void
+ImGui_DebugMemoryViewer(project_state *State)
+{
+ ImGui::Begin("Memory viewer");
+ cache_entry *EntryArray = State->Render.Entry;
+ char *Type[4] = { "unassigned", "comp", "source", "layer" };
+ int c = 0;
+ while (EntryArray[c].CycleTime != 0) {
+ ImGui::Text("Type - %s, Start - %i, Info - %i", Type[EntryArray[c].Type], EntryArray[c].Block_StartIndex, EntryArray[c].TypeInfo);
+ c++;
+ }
+ ImGui::End();
+}
+
static void
-ImGui_Viewport(block_composition *MainComp, GLuint textureID)
+ImGui_File(project_data *File, project_state *State, memory *Memory, ImGuiIO io)
+{
+ ImGui::Begin("Files");
+ ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
+
+ 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);
+ block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Path_String_Index);
+ ImGui::Text(String->Char);
+ }
+
+#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);
+ }
+ }
+#endif
+ ImGui::End();
+}
+
+static void
+ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io, GLuint textureID)
{
bool open = true;
ImGui::Begin("Viewport", &open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
+ if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows))
+ UI->FocusedWindow = focus_viewport;
+
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, 0);
+
+
ImVec2 ViewportMin = ImGui::GetCursorScreenPos();
ImVec2 ViewportScale = ImGui::GetContentRegionAvail();
ImVec2 ViewportMax = ImVec2(ViewportMin.x + ViewportScale.x, ViewportMin.y + ViewportScale.y);
+ if (State->Initializing) {
+ UI->CompZoom = ImVec2(MainComp->Width, MainComp->Height);
+ UI->CompPos = ImVec2(ViewportMin.x + ((ViewportMax.x - ViewportMin.x)/2 - UI->CompZoom.x/2),
+ ViewportMin.y + ((ViewportMax.y - ViewportMin.y)/2 - UI->CompZoom.y/2));
+ }
+
+ ImVec2 CompPosMin = ImVec2(UI->CompPos.x, UI->CompPos.y);
+ ImVec2 CompPosMax = ImVec2(UI->CompPos.x + UI->CompZoom.x, UI->CompPos.y + UI->CompZoom.y);
+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddRectFilled(ViewportMin, ViewportMax, IM_COL32(50, 50, 50, 255));
draw_list->AddRect(ViewportMin, ViewportMax, IM_COL32(255, 255, 255, 255));
-
- ImVec2 CompZoom(MainComp->Width, MainComp->Height);
- ImVec2 CompPos = ViewportMin + (ViewportMax - ViewportMin)*0.5 - CompZoom*0.5;
+ draw_list->AddRect(CompPosMin, CompPosMax, IM_COL32(255, 255, 255, 55));
// Actual composition texture
draw_list->PushClipRect(ViewportMin, ViewportMax, true);
- draw_list->AddImage((void *)(intptr_t)textureID, ImVec2(CompPos.x, CompPos.y),
- ImVec2(CompPos.x + CompZoom.x, CompPos.y + CompZoom.y));
+ draw_list->AddImage((void *)(intptr_t)textureID, CompPosMin, CompPosMax);
+ draw_list->PopClipRect();
+
+ // Interactions for dragging and zooming
+ ImGui::SetCursorScreenPos(ViewportMin);
+
+ ImGui::InvisibleButton("canvas", ViewportScale, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
+ bool32 IsHovered = ImGui::IsItemHovered();
+ bool32 IsActive = ImGui::IsItemActive();
+ bool32 IsActivated = ImGui::IsItemActivated();
+ bool32 IsDeactivated = ImGui::IsItemDeactivated();
+
+ if (IsHovered && IsActivated && ImGui::IsMouseDown(ImGuiMouseButton_Left))
+ {
+ // Point to zoom in on if Z is held
+ UI->TempZoomRatio = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos);
+
+ // 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 (IsActive && ImGui::IsMouseDragging(ImGuiMouseButton_Right, -1.0f))
+ {
+ UI->CompPos.x += io.MouseDelta.x;
+ UI->CompPos.y += io.MouseDelta.y;
+ }
+
+ if (IsActive && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1.0f) && ImGui::IsKeyDown(ImGuiKey_Z))
+ {
+ real32 Distance = io.MouseDelta.x + io.MouseDelta.y;
+ UI->CompZoom.x += (Distance)*(real32)MainComp->Width/MainComp->Height;
+ UI->CompZoom.y += (Distance);
+ UI->CompPos.x -= ((Distance)*(real32)MainComp->Width/MainComp->Height)*UI->TempZoomRatio.x;
+ UI->CompPos.y -= Distance*UI->TempZoomRatio.y;
+ }
+
+ ImGui::SetCursorScreenPos(ImVec2(ViewportMin.x, ViewportMin.y + ViewportScale.y - ImGui::GetFontSize()*1.5));
+
+ ImGui::Text("%.1f", 100.0f * (UI->CompZoom.x / MainComp->Width));
+ if (State->MsgTime > 0) {
+ ImGui::SameLine();
+ ImGui::SetCursorPosX((ViewportScale.x / 5)*4);
+ ImGui::Text(State->Msg);
+ State->MsgTime--;
+ }
+
+ ImGui::End();
+
+
+ /*
+ for (int i = 0; i < AmountOf(Layer->Property); i++) {
+ ImGui::PushID(i);
+ property_channel *Property = &Layer->Property[i];
+ ImGui::DragScalar(Property->Name, ImGuiDataType_Float, &Property->CurrentValue,
+ Property->ScrubVal, &Property->MinVal, &Property->MaxVal, "%f");
+ if (ImGui::IsItemActive())
+ State->UpdateFrame = true;
+ ImGui::PopID();
+ }
+ */
+}
+
+static void
+ImGui_TimelineHorizontalIncrementDraw(ui *UI, ImDrawList *draw_list, ImVec2 TimelineSizeWithBorder, ImVec2 TimelineAbsolutePos, block_composition MainComp)
+{
+ uint32 LineColor = IM_COL32(200, 200, 200, 40);
+
+ real32 TimelineZoomSize = TimelineSizeWithBorder.x / UI->TimelinePercentZoomed.x ;
+ real32 TimelineMoveSize = TimelineSizeWithBorder.x * UI->TimelinePercentOffset.x / UI->TimelinePercentZoomed.x;
+
+ Assert(TimelineZoomSize > 0.0f);
+
+ real32 x = 0;
+ bool32 RightmostEdge = false;
+ real32 Increment = (real32)1 / MainComp.Frame_Count;
+ if (UI->TimelinePercentZoomed.x > 0.90)
+ Increment = (real32)MainComp.FPS / MainComp.Frame_Count;
+ else if (UI->TimelinePercentZoomed.x > 0.40)
+ Increment *= 2;
+
+ while (!RightmostEdge) {
+ ImVec2 Min = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + x*TimelineZoomSize, TimelineAbsolutePos.y);
+ ImVec2 Max = ImVec2(Min.x + 2, TimelineAbsolutePos.y + TimelineSizeWithBorder.y);
+ if (Min.x < TimelineAbsolutePos.x + TimelineSizeWithBorder.x) {
+ draw_list->AddLine(Min, Max, LineColor);
+ char buf2[6];
+ uint32 FrameNumber = (uint32)(x * MainComp.Frame_Count) % MainComp.FPS;
+ if (FrameNumber != 0)
+ sprintf(buf2, ":%.2i", FrameNumber);
+ else
+ sprintf(buf2, "%.2i:00", (uint32)(x * MainComp.Frame_Count) / MainComp.FPS);
+ draw_list->AddText(ImVec2(Min.x, TimelineAbsolutePos.y), IM_COL32(200, 200, 200, 130), buf2);
+ x += Increment;
+ if (x > 1.0f)
+ RightmostEdge = true;
+ } else {
+ RightmostEdge = true;
+ }
+ }
+}
+
+static void
+ImGui_Timeline_DrawLayer(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io, ImDrawList *draw_list, block_layer *Layer, block_composition *MainComp, uint16 i,
+ ImVec2 Increment, ImVec2 TimelineAbsolutePos, ImVec2 TimelineMoveSize, ImVec2 TimelineZoomSize,
+ ImVec2 TimelineSize, ImVec2 TimelineSizeWithBorder, real32 LayerIncrement,
+ sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray)
+{
+ 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, &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);
+
+ ImVec2 Layer_LocalPos_Ratio = (Layer_LocalPos * Increment);
+ ImVec2 Layer_LocalSize_Ratio = Layer_LocalSize * Increment;
+ ImVec2 Layer_ScreenPos_Min = TimelineAbsolutePos + TimelineMoveSize + (Layer_LocalPos_Ratio * TimelineZoomSize);
+ ImVec2 Layer_ScreenPos_Max = TimelineAbsolutePos + TimelineMoveSize + ((Layer_LocalPos_Ratio + Layer_LocalSize_Ratio) * TimelineZoomSize);
+ ImVec2 Layer_ScreenSize = Layer_ScreenPos_Max - Layer_ScreenPos_Min;
+
+ if (UI->BoxSelect) {
+ bool32 Test = 0;
+ if (io.MouseClickedPos[0].y < io.MousePos.y)
+ Test = (Layer_ScreenPos_Min.y >= io.MouseClickedPos[0].y && Layer_ScreenPos_Min.y <= io.MousePos.y);
+ else
+ Test = (Layer_ScreenPos_Max.y <= io.MouseClickedPos[0].y && Layer_ScreenPos_Max.y >= io.MousePos.y);
+
+ if (Test) {
+ if (!Layer->IsSelected) {
+ Layer->IsSelected = true;
+ }
+ } else if (!io.KeyShift) {
+ Layer->IsSelected = false;
+ }
+ }
+
+ draw_list->AddRectFilled(Layer_ScreenPos_Min, Layer_ScreenPos_Max,
+ ImColor(Layer->Col[0], Layer->Col[1], Layer->Col[2], 1.0f));
+ draw_list->AddRect(Layer_ScreenPos_Min, Layer_ScreenPos_Max, ImColor(1.0f, 1.0f, 1.0f, 0.5f), 2);
+
+
+ if (Layer->IsSelected) {
+ draw_list->AddRectFilled(Layer_ScreenPos_Min, Layer_ScreenPos_Max, ImColor(0.25f, 0.25f, 0.25f, 0.5f), 2);
+ draw_list->AddRect(Layer_ScreenPos_Min, Layer_ScreenPos_Max, ImColor(1.0f, 1.0f, 1.0f, 0.5f), 2);
+ }
+
+ ImVec2 ResizeSize = ImVec2(Increment.x * 0.45 * TimelineZoomSize.x, Layer_ScreenSize.y);
+ ImVec2 ResizePos[2] = { Layer_ScreenPos_Min, ImVec2(Layer_ScreenPos_Max.x - ResizeSize.x, Layer_ScreenPos_Min.y) };
+ for (int b = 0; b < 2; b++) {
+ ImGui::PushID(b);
+ ImGui::SetCursorScreenPos(ResizePos[b]);
+ ImGui::Button("##layer_resize", ResizeSize);
+
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
+ }
+ if (ImGui::IsItemActivated()) {
+ if (!Layer->IsSelected) {
+ State->MostRecentlySelectedLayer = i;
+ Layer->IsSelected = true;
+ }
+ }
+ if (ImGui::IsItemActive()) {
+ if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) {
+ State->Interact_Active = interact_type_layer_timeadjust;
+ 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) * MainComp->Frame_Count;
+ State->Interact_Offset[1] = b;
+ DebugWatchVar("Offset1", &State->Interact_Offset[0], d_float);
+ }
+ }
+ 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, &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;
+ }
+ }
+
+ ImGui::PopID();
+ }
+
+ ImGui::SetCursorScreenPos(Layer_ScreenPos_Min);
+ ImGui::InvisibleButton("##layer_mid", Layer_ScreenSize, ImGuiMouseButton_Left);
+
+ if (ImGui::IsItemActivated()) {
+ if (!Layer->IsSelected) {
+ if (!io.KeyShift) Layer_DeselectAll(Memory, File->Layer_Count);
+ State->MostRecentlySelectedLayer = i;
+ Layer->IsSelected = true;
+ }
+ }
+
+ if (ImGui::IsItemActive()) {
+ if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) {
+ State->Interact_Active = interact_type_layer_move;
+ 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) * MainComp->Frame_Count;
+ State->Interact_Offset[1] = (DragDelta.y / TimelineSizeWithBorder.y * UI->TimelinePercentZoomed.y) * 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;
+ }
+
+ UI->DragDelta_Prev = DragDelta;
+ */
+ }
+ }
+
+ 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);
+ if (Layer->IsSelected) {
+ Layer_Interact_Evaluate(Memory, State, &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;
+ }
+ }
+
+ if (Layer->IsPrecomp) {
+ ImVec2 MinClipPos = ImVec2(Layer_ScreenPos_Min.x, Layer_ScreenPos_Max.y);
+ ImVec2 MaxClipPos = ImVec2(Layer_ScreenPos_Max.x, MinClipPos.y + TimelineZoomSize.y);
+ draw_list->AddRectFilled(MinClipPos, MaxClipPos, ImColor(0.2f, 0.2f, 0.2f, 1.0f));
+
+ sorted_comp_info SortedCompInfo = SortedCompArray[Layer->Block_Source_Index];
+ sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, Layer->Block_Source_Index);
+
+ for (int a = 0; a < SortedCompInfo.LayerCount; a++)
+ {
+ ImGui::PushID(a);
+ sorted_layer SortEntry = SortedLayerInfo[a];
+ uint32 Index_Physical = SortEntry.Block_Layer_Index;
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical);
+
+ ImVec2 Layer_LocalPos_Screen = Layer_LocalPos_Ratio * TimelineZoomSize;
+ ImVec2 NestedTimelineAbsolutePos = TimelineAbsolutePos + Layer_LocalPos_Screen + ImVec2(0, Layer_ScreenSize.y);
+ ImGui_Timeline_DrawLayer(File, State, Memory, UI, io, draw_list, Layer, MainComp, a,
+ Increment, NestedTimelineAbsolutePos, TimelineMoveSize, TimelineZoomSize,
+ TimelineSize, TimelineSizeWithBorder, LayerIncrement,
+ SortedCompArray, SortedLayerArray);
+ ImGui::PopID();
+ }
+ }
+}
+
+static void
+ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io)
+{
+ ImVec2 FramePadding = ImGui::GetStyle().FramePadding;
+ ImVec2 ItemSpacing = ImGui::GetStyle().ItemSpacing;
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); // makes setting up the layout easier
+ ImGui::Begin("Timeline", NULL);
+
+ if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows))
+ UI->FocusedWindow = focus_timeline;
+
+ real32 FontHeight = ImGui::GetFontSize();
+
+ ImVec2 WindowSize = ImGui::GetWindowSize();
+
+ if (WindowSize.x < 50 || WindowSize.y < 50) {
+ ImGui::PopStyleVar(2);
+ ImGui::End();
+ return;
+ }
+
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+
+ ImVec2 WindowMinAbs = ImGui::GetWindowPos();
+ ImVec2 WindowMaxAbs = WindowMinAbs + WindowSize;
+
+ ImVec2 ButtonSize = ImVec2(FontHeight*2, FontHeight*2);
+
+ ImVec2 TimelineBorderPadding = ImVec2(FontHeight, FontHeight);
+
+ ImVec2 TimelineSize = ImVec2(WindowSize.x, WindowSize.y);
+ ImVec2 TimelineSizeWithBorder = TimelineSize - TimelineBorderPadding*2;
+ ImVec2 TimelineAbsolutePos = WindowMinAbs + TimelineBorderPadding;
+
+ ImVec2 KeyframeSize = ImVec2(FontHeight, FontHeight);
+
+ int32 FrameCount = MainComp->Frame_Count;
+
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ draw_list->AddRectFilled(WindowMinAbs, WindowMaxAbs,
+ IM_COL32(255, 255, 255, 50));
+
+
+ ImGui::BeginChild("Timeline", TimelineSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
+
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ItemSpacing.y));
+
+ ImGui::PushClipRect(TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, true);
+ draw_list->PushClipRect(TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, true);
+
+ real32 LayerIncrement = 40;
+
+ ImVec2 Val_Min(0, 0);
+ ImVec2 Val_Max(40, LayerIncrement);
+
+ if (UI->TimelinePercentZoomed.x == 0) {
+ UI->TimelinePercentZoomed = ImVec2(1, 1);
+ }
+
+ ImGui_TimelineHorizontalIncrementDraw(UI, draw_list, TimelineSizeWithBorder, TimelineAbsolutePos, *MainComp);
+
+ ImVec2 TimelineZoomSize = TimelineSizeWithBorder / UI->TimelinePercentZoomed;
+ ImVec2 TimelineMoveSize = TimelineSizeWithBorder * UI->TimelinePercentOffset / UI->TimelinePercentZoomed;
+
+ 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);
+
+ sorted_comp_info SortedCompInfo = SortedCompArray[File->PrincipalCompIndex];
+ sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, File->PrincipalCompIndex);
+
+ for (int i = 0; i < SortedCompInfo.LayerCount; i++)
+ {
+ ImGui::PushID(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);
+
+ ImGui_Timeline_DrawLayer(File, State, Memory, UI, io, draw_list, Layer, MainComp, i,
+ Increment, TimelineAbsolutePos, TimelineMoveSize, TimelineZoomSize,
+ TimelineSize, TimelineSizeWithBorder, LayerIncrement,
+ SortedCompArray, SortedLayerArray);
+
+ ImGui::PopID();
+ }
+
+ Memory_PopScratch(Memory, SortSize);
+
+ ImVec2 MouseDelta = io.MouseDelta / TimelineSize;
+
+ real32 BarHandleSize = FontHeight;
+ real32 BarThickness = 50;
+ real32 BarMinZoom = 0.01;
+
+ real32 BarH_Pos = -TimelineSizeWithBorder.x * UI->TimelinePercentOffset.x;
+ real32 BarH_Size = TimelineSizeWithBorder.x / (1 / UI->TimelinePercentZoomed.x);
+
+ // I use "UI" to denote the size/position after clipping the bar so that it
+ // doesn't go out of bounds and the handles are always selectable at the edges.
+
+ real32 BarH_Offset = Max(BarH_Pos, 0);
+
+ real32 BarH_SizeUI = (BarH_Size + BarH_Pos > TimelineSizeWithBorder.x) ?
+ TimelineSizeWithBorder.x - BarH_Pos :
+ BarH_Size + (BarH_Pos - BarH_Offset);
+
+ if (BarH_Offset == 0 && BarH_SizeUI > TimelineSizeWithBorder.x)
+ BarH_SizeUI = TimelineSizeWithBorder.x;
+
+ BarH_SizeUI = BarH_SizeUI - BarHandleSize*2;
+
+ BarH_SizeUI = Max(BarH_SizeUI, FontHeight*4);
+
+ 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));
+
+ if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)))
+ {
+ if ((UI->TimelinePercentZoomed.x - MouseDelta.x) > BarMinZoom) {
+ UI->TimelinePercentZoomed.x -= MouseDelta.x;
+ UI->TimelinePercentOffset.x -= MouseDelta.x;
+ }
+ BarHeld = true;
+ }
+
+ ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0));
+ ImGui::Button("##scrollbarhori", ImVec2(BarH_SizeUI, BarThickness));
+
+ if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))
+ {
+ UI->TimelinePercentOffset.x -= MouseDelta.x;
+ BarHeld = true;
+ }
+
+ ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0) + ImVec2(BarH_SizeUI, 0));
+ ImGui::Button("##scrollbarright", ImVec2(BarHandleSize, BarThickness));
+
+ if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)))
+ {
+ if ((UI->TimelinePercentZoomed.x + MouseDelta.x) > BarMinZoom) {
+ UI->TimelinePercentZoomed.x += MouseDelta.x;
+ }
+ BarHeld = true;
+ }
+
+ if (BarHeld) {
+ ImGui_WarpMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, 1);
+ }
+
+ Assert(UI->TimelinePercentZoomed.x > BarMinZoom);
+
+ real32 BarV_MaxSize = TimelineSizeWithBorder.y - BarThickness/2;
+ real32 BarV_Pos = -BarV_MaxSize * UI->TimelinePercentOffset.y;
+ real32 BarV_Size = BarV_MaxSize / (1 / UI->TimelinePercentZoomed.y);
+ BarV_Size = Max(BarV_Size, FontHeight*4);
+
+ real32 BarV_Offset = Max(BarV_Pos, 0);
+
+ real32 BarV_SizeUI = (BarV_Size + BarV_Pos > BarV_MaxSize) ?
+ BarV_MaxSize - BarV_Pos :
+ BarV_Size + (BarV_Pos - BarV_Offset);
+
+ if (BarV_Offset == 0 && BarV_SizeUI > BarV_MaxSize)
+ BarV_SizeUI = BarV_MaxSize;
+
+ BarV_SizeUI = BarV_SizeUI - BarHandleSize*2;
+
+ BarV_SizeUI = Max(BarV_SizeUI, FontHeight*4);
+
+ 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));
+
+ if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)))
+ {
+ UI->TimelinePercentZoomed.y -= MouseDelta.y;
+ UI->TimelinePercentOffset.y -= MouseDelta.y;
+ BarHeld = true;
+ }
+
+ ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize));
+ ImGui::Button("##h-scrollbar", ImVec2(BarThickness, BarV_SizeUI));
+
+ if (ImGui::IsItemHovered() && io.MouseWheel)
+ {
+ UI->TimelinePercentOffset.y -= io.MouseWheel/10;
+ }
+
+ if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))
+ {
+ UI->TimelinePercentOffset.y -= MouseDelta.y;
+ BarHeld = true;
+ }
+
+ ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize) + ImVec2(0, BarV_SizeUI));
+ ImGui::Button("##h-scrollbarright", ImVec2(BarThickness, BarHandleSize));
+
+ if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)))
+ {
+ UI->TimelinePercentZoomed.y += MouseDelta.y;
+ BarHeld = true;
+ }
+
+ UI->TimelinePercentZoomed.y = Max(UI->TimelinePercentZoomed.y, 0.01);
+
+ if (BarHeld) {
+ ImGui_WarpMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, 2);
+ }
+
+ ImGui::SetCursorScreenPos(TimelineAbsolutePos);
+ ImGui::InvisibleButton("TimelineMoving", TimelineSizeWithBorder, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
+ // ImGui::Button("TimelineMoving", TimelineSizeWithBorder);
+ bool32 IsHovered = ImGui::IsItemHovered();
+ bool32 IsItemActive = ImGui::IsItemActive();
+ bool32 IsItemActivated = ImGui::IsItemActivated();
+ bool32 IsItemDeactivated = ImGui::IsItemDeactivated();
+ bool32 LeftClick = ImGui::IsMouseDown(ImGuiMouseButton_Left);
+ bool32 RightClick = ImGui::IsMouseDown(ImGuiMouseButton_Right);
+
+ if (IsHovered && io.MouseWheel) {
+ real32 Increment = 0.1;
+ bool32 Direction = (io.MouseWheel > 0) ? 1 : -1;
+ ImVec2 Offset = (io.MousePos - (TimelineAbsolutePos + TimelineMoveSize)) / TimelineZoomSize;
+ if (io.KeyShift) {
+ UI->TimelinePercentOffset.y += Increment*Direction;
+ } else if (io.KeyCtrl) {
+ UI->TimelinePercentOffset.x += Increment*Direction*0.3;
+ } else {
+ if (Direction == 1) {
+ UI->TimelinePercentZoomed = UI->TimelinePercentZoomed - (UI->TimelinePercentZoomed * Increment);
+ UI->TimelinePercentOffset = UI->TimelinePercentOffset - ((UI->TimelinePercentOffset * Increment) + Offset*Increment);
+ } else {
+ UI->TimelinePercentOffset = ((UI->TimelinePercentOffset + Offset*Increment) / (1.0f - Increment));
+ UI->TimelinePercentZoomed = (UI->TimelinePercentZoomed / (1.0f - Increment));
+ }
+ }
+ }
+
+ if (IsItemActivated) {
+ if (!io.KeyShift) Layer_DeselectAll(Memory, File->Layer_Count);
+ UI->BoxSelect = true;
+ }
+ if (IsItemActive) {
+ Assert(UI->BoxSelect);
+ draw_list->AddRectFilled(io.MouseClickedPos[0], io.MousePos,
+ IM_COL32(0, 0, 200, 50));
+ }
+
+ if (IsItemDeactivated) {
+ UI->BoxSelect = false;
+ }
+
draw_list->PopClipRect();
+ ImGui::PopClipRect();
+
+ ImGui::PopStyleVar();
+
+
+ ImGui::EndChild();
+
+ ImGui::PopStyleVar(2);
ImGui::End();
}
+static void
+ImGui_ProcessInputs(project_state *State, ImGuiIO io)
+{
+ if (io.KeysData[ImGuiKey_Q].Down) {
+ State->IsRunning = false;
+ }
+ if (io.KeysData[ImGuiKey_A].Down) {
+ State->UpdateFrame = true;
+ }
+}
+
+#if 0
+ real32 MaxVal_Y = -10000;
+ real32 MinVal_Y = 10000;
+ for (int b = 0; b < Property->NumberOfTotalKeyframes; b++) {
+ keyframe *Keyframe = KeyframeLookup(Property, b);
+ MaxVal_Y = (Keyframe->Value.f > MaxVal_Y) ? Keyframe->Value.f : MaxVal_Y;
+ 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;
+
+ if (!UI->IsDragging) {
+ UI->Display_Y_MinVal = UI->Y_MinVal;
+ UI->Display_Y_MaxVal = UI->Y_MaxVal;
+ }
+
+ real32 Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed;
+ real32 Y_TimelinePercentOffset = UI->Y_TimelinePercentOffset;
+ MaxVal_Y = UI->Display_Y_MaxVal;
+ MinVal_Y = UI->Display_Y_MinVal;
+
+ 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);
+
+ keyframe *Keyframe = KeyframeLookup(Property, b);
+ // Only used for drawing the bezier.
+ keyframe *NextKeyframe = (b != Property->NumberOfTotalKeyframes - 1) ? KeyframeLookup(Property, b + 1) : NULL;
+
+ real32 Increment_X = (real32)1 / File->NumberOfFrames;
+ real32 UI_FrameDistance = Increment_X*TimelineZoomSize;
+
+ 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)))
+ {
+ 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
+ // ratios of the keyframes/timeline to make the bars straightforward,
+ // meaning the position/offset have to be transformed into a new space
+ // when a new min/max value is reached.
+
+ if (ImGui::IsItemDeactivated()) {
+ if ((UI->TempVal >= MaxVal_Y) || Keyframe->Value.f == UI->Y_MaxVal) {
+ 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);
+ } else if (UI->TempVal <= MinVal_Y || Keyframe->Value.f == UI->Y_MinVal) {
+ real32 RealRatio_Y = (UI->Y_MinVal - MinVal_Y) / (MaxVal_Y - MinVal_Y);
+ UI->Y_TimelinePercentOffset = UI->Y_TimelinePercentOffset / (1 - RealRatio_Y);
+ 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();
+ }
+
+ ImGui_TimelineIncrementDraw(File, UI, draw_list, TimelineSizeWithBorder, TimelineAbsolutePos, 0);
+ ImGui_TimelineIncrementDraw2(File, draw_list, MaxVal_Y, MinVal_Y, Y_TimelinePercentZoomed, Y_TimelinePercentOffset, TimelineSizeWithBorder, TimelineAbsolutePos, 0);
+
+#if DEBUG
+ draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.25, TimelineSizeWithBorder.y - 50),
+ 2, IM_COL32(200, 000, 200, 200), 16, 1);
+ draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.5, TimelineSizeWithBorder.y - 50),
+ 2, IM_COL32(200, 000, 200, 200), 16, 1);
+ draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.75, TimelineSizeWithBorder.y - 50),
+ 2, IM_COL32(200, 000, 200, 200), 16, 1);
+#endif
+
+#endif
+
+#if 0
+
+ ImVec2 MouseDelta = io.MouseDelta / TimelineSize;
+
+ real32 BarHandleSize = FontHeight;
+ real32 BarThickness = 50;
+ real32 BarMinZoom = 0.01;
+
+ real32 BarH_Pos = -TimelineSizeWithBorder.x * UI->TimelinePercentOffset;
+ real32 BarH_Size = TimelineSizeWithBorder.x / (1 / UI->TimelinePercentZoomed);
+
+ // I use "UI" to denote the size/position after clipping the bar so that it
+ // doesn't go out of bounds and the handles are always selectable at the edges.
+
+ real32 BarH_Offset = Max(BarH_Pos, 0);
+
+ real32 BarH_SizeUI = (BarH_Size + BarH_Pos > TimelineSizeWithBorder.x) ?
+ TimelineSizeWithBorder.x - BarH_Pos :
+ BarH_Size + (BarH_Pos - BarH_Offset);
+
+ if (BarH_Offset == 0 && BarH_SizeUI > TimelineSizeWithBorder.x)
+ BarH_SizeUI = TimelineSizeWithBorder.x;
+
+ BarH_SizeUI = BarH_SizeUI - BarHandleSize*2;
+
+ BarH_SizeUI = Max(BarH_SizeUI, FontHeight*4);
+
+ 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));
+
+ if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)))
+ {
+ if ((UI->TimelinePercentZoomed - MouseDelta.x) > BarMinZoom) {
+ UI->TimelinePercentZoomed -= MouseDelta.x;
+ UI->TimelinePercentOffset -= MouseDelta.x;
+ }
+ BarHeld = true;
+ }
+
+ ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0));
+ ImGui::Button("##scrollbarhori", ImVec2(BarH_SizeUI, BarThickness));
+
+ 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));
+ ImGui::Button("##scrollbarright", ImVec2(BarHandleSize, BarThickness));
+
+ if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)))
+ {
+ 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);
+
+ real32 BarV_MaxSize = TimelineSizeWithBorder.y - BarThickness/2;
+ real32 BarV_Pos = -BarV_MaxSize * Y_TimelinePercentOffset;
+ real32 BarV_Size = BarV_MaxSize / (1 / Y_TimelinePercentZoomed);
+ BarV_Size = Max(BarV_Size, FontHeight*4);
+
+ real32 BarV_Offset = Max(BarV_Pos, 0);
+
+ real32 BarV_SizeUI = (BarV_Size + BarV_Pos > BarV_MaxSize) ?
+ BarV_MaxSize - BarV_Pos :
+ BarV_Size + (BarV_Pos - BarV_Offset);
+
+ if (BarV_Offset == 0 && BarV_SizeUI > BarV_MaxSize)
+ BarV_SizeUI = BarV_MaxSize;
+
+ BarV_SizeUI = BarV_SizeUI - BarHandleSize*2;
+
+ BarV_SizeUI = Max(BarV_SizeUI, FontHeight*4);
+
+ 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));
+
+ if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)))
+ {
+ UI->Y_TimelinePercentZoomed -= MouseDelta.y;
+ UI->Y_TimelinePercentOffset -= MouseDelta.y;
+ BarHeld = true;
+ }
+
+ ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize));
+ 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));
+ ImGui::Button("##h-scrollbarright", ImVec2(BarThickness, BarHandleSize));
+
+ 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);
+ ImGui::InvisibleButton("TimelineMoving", TimelineSizeWithBorder, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
+ bool32 IsHovered = ImGui::IsItemHovered();
+ bool32 IsActive = ImGui::IsItemActive();
+ bool32 IsItemActivated = ImGui::IsItemActivated();
+ bool32 IsItemDeactivated = ImGui::IsItemDeactivated();
+ bool32 LeftClick = ImGui::IsMouseDown(ImGuiMouseButton_Left);
+ bool32 RightClick = ImGui::IsMouseDown(ImGuiMouseButton_Right);
+
+ if (IsActive) {
+ if (LeftClick) {
+ if (io.KeyCtrl && IsActive) {
+ real32 LocalMousePos = ImGui::GetMousePos().x - TimelineStartingPos.x;
+ real32 ZoomRatio = LocalMousePos / UI->TimelineZoom;
+ File->CurrentFrame = (int32)(ZoomRatio + 0.5);
+ State->UpdateFrame = true;
+ State->UpdateKeyframes = true;
+ } else {
+ if (IsItemActivated)
+ {
+ if (!io.KeyShift) {
+ // DeselectAllKeyframes(&State);
+ // DeselectAllLayers(File, State);
+ }
+ UI->BoxStart = ImGui::GetMousePos();
+ UI->BoxSelectActive = true;
+ }
+ if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1) )
+ {
+ UI->BoxEnd = ImGui::GetMousePos();
+ draw_list->AddRectFilled(UI->BoxStart, UI->BoxEnd,
+ IM_COL32(0, 0, 200, 50));
+ }
+ }
+ // Timeline zooming interaction
+ } else if (RightClick && IsActive) {
+ if (IsItemActivated)
+ {
+ real32 LocalMousePos = io.MousePos.x - WindowMinAbs.x - UI->TimelineSplit;
+ UI->TempZoomRatioTimeline = LocalMousePos / TimelineSize.x;
+ }
+ if (ImGui::IsMouseDragging(ImGuiMouseButton_Right, -1) )
+ {
+ UI->TimelineZoom += io.MouseDelta.x;
+ }
+ }
+ }
+ if (IsItemDeactivated) {
+ UI->BoxStart = {0, 0};
+ UI->BoxEnd = {0, 0};
+ UI->BoxSelectActive = false;
+ if (!io.KeyShift) DeselectAllLayers(File, State);
+ }
+
+ ImGui::EndChild();
+
+ ImGui::PopStyleVar(2);
+
+#endif
+
+
+
#if 0
// 0 for timeline keyframe, 1 for graph keyframe, 2 for left graph handle, 3 for right graph handle
static void
@@ -99,59 +1150,6 @@ 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)
{
@@ -176,17 +1174,6 @@ ImGui_InteractSliderProperty(project_state *State, memory *Memory, property_chan
}
}
-// Returns a normalized UV position of the composition
-static v2
-ImGui_ScreenPointToCompUV(ImVec2 ViewportMin, ImVec2 CompPos, ImVec2 CompZoom, ImVec2 MousePos)
-{
- ImVec2 LocalMousePos = MousePos - ViewportMin;
- ImVec2 LocalCompPos = CompPos - ViewportMin;
- ImVec2 MouseScreenUV = LocalMousePos - LocalCompPos;
- ImVec2 Result = MouseScreenUV / CompZoom;
- return V2(Result);
-}
-
static void
ImGui_DebugUndoTree(project_data *File, memory *Memory)
{
@@ -299,46 +1286,6 @@ ImGui_DebugMemoryViewer(project_data *File, memory *Memory)
static bool32 FU;
static void
-ImGui_TimelineIncrementDraw(project_data *File, ui *UI, ImDrawList *draw_list,
- ImVec2 TimelineSizeWithBorder, ImVec2 TimelineAbsolutePos, bool32 IsText)
-{
- uint32 LineColor = IM_COL32(200, 200, 200, 40);
-
- real32 TimelineZoomSize = TimelineSizeWithBorder.x / UI->TimelinePercentZoomed;
- real32 TimelineMoveSize = TimelineSizeWithBorder.x * UI->TimelinePercentOffset / UI->TimelinePercentZoomed;
-
- Assert(TimelineZoomSize > 0.0f);
-
- real32 x = 0;
- bool32 RightmostEdge = false;
- real32 Increment = (real32)1 / File->NumberOfFrames;
- if (UI->TimelinePercentZoomed > 0.90)
- Increment = (real32)File->FPS / File->NumberOfFrames;
- else if (UI->TimelinePercentZoomed > 0.40)
- Increment *= 2;
-
- while (!RightmostEdge) {
- ImVec2 Min = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + x*TimelineZoomSize, TimelineAbsolutePos.y);
- ImVec2 Max = ImVec2(Min.x + 2, TimelineAbsolutePos.y + TimelineSizeWithBorder.y);
- if (Min.x < TimelineAbsolutePos.x + TimelineSizeWithBorder.x) {
- draw_list->AddLine(Min, Max, LineColor);
- char buf2[6];
- uint32 FrameNumber = (uint32)(x*File->NumberOfFrames) % File->FPS;
- if (FrameNumber != 0)
- sprintf(buf2, ":%.2i", FrameNumber);
- else
- sprintf(buf2, "%.2i:00", (uint32)(x*File->NumberOfFrames) / File->FPS);
- draw_list->AddText(ImVec2(Min.x, TimelineAbsolutePos.y), IM_COL32(200, 200, 200, 130), buf2);
- x += Increment;
- if (x > 1.0f)
- RightmostEdge = true;
- } else {
- RightmostEdge = true;
- }
- }
-}
-
-static void
ImGui_TimelineIncrementDraw2(project_data *File, ImDrawList *draw_list, real32 MaxVal_Y, real32 MinVal_Y,
real32 Y_TimelinePercentZoomed, real32 Y_TimelinePercentOffset,
ImVec2 TimelineSizeWithBorder, ImVec2 TimelineAbsolutePos, bool32 IsText)
@@ -1129,612 +2076,6 @@ ImGui_EffectsPanel(project_data *File, project_state *State, memory *Memory, ui
}
-static void
-ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io)
-{
- ImVec2 FramePadding = ImGui::GetStyle().FramePadding;
- ImVec2 ItemSpacing = ImGui::GetStyle().ItemSpacing;
- ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
- ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); // makes setting up the layout easier
- ImGui::Begin("Timeline", NULL);
-
- if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows))
- UI->FocusedWindow = focus_timeline;
-
- real32 FontHeight = ImGui::GetFontSize();
-
- ImVec2 WindowSize = ImGui::GetWindowSize();
-
- if (WindowSize.x < 50 || WindowSize.y < 50) {
- ImGui::PopStyleVar(2);
- ImGui::End();
- return;
- }
-
- ImVec2 WindowMinAbs = ImGui::GetWindowPos();
- ImVec2 WindowMaxAbs = WindowMinAbs + WindowSize;
-
- ImVec2 ButtonSize = ImVec2(FontHeight*2, FontHeight*2);
-
- real32 TopbarHeight = FontHeight*2;
- ImVec2 TopbarMax = ImVec2(WindowMaxAbs.x, WindowMinAbs.y + TopbarHeight);
-
- ImVec2 TimelineBorderPadding = ImVec2(FontHeight, FontHeight);
-
- ImVec2 TopbarSize = ImVec2(WindowSize.x, TopbarHeight);
- ImVec2 TopbarButtonSize = ImVec2(TopbarHeight, TopbarHeight);
-
- // NOTE(fox): StartingPos values include X and Y scroll, primarily used for
- // the keyframes/layers. Absolute doesn't include scroll, primarily used
- // for the clip rects.
-
- ImVec2 SidebarSize = ImVec2(UI->TimelineSplit, WindowSize.y - TopbarHeight);
- ImVec2 SidebarSizeWithBorder = SidebarSize - TimelineBorderPadding*2;
- ImVec2 SidebarAbsolutePos = WindowMinAbs + ImVec2(0, TopbarSize.y) + TimelineBorderPadding;
- ImVec2 SidebarStartingPos = SidebarAbsolutePos + ImVec2(0, UI->ScrollYOffset);
-
- ImVec2 TimelineSize = ImVec2(WindowSize.x - SidebarSize.x, SidebarSize.y);
- ImVec2 TimelineSizeWithBorder = TimelineSize - TimelineBorderPadding*2;
- ImVec2 TimelineAbsolutePos = WindowMinAbs + ImVec2(SidebarSize.x, TopbarSize.y) + TimelineBorderPadding;
- ImVec2 TimelineStartingPos = SidebarStartingPos + ImVec2(SidebarSize.x + UI->ScrollXOffset, 0);
-
- // Timeline and sidebar size including the padding between them
- ImVec2 TimelineFullSize = TimelineSizeWithBorder + SidebarSizeWithBorder + ImVec2(TimelineBorderPadding.x*2, 0);
-
- ImVec2 KeyframeSize = ImVec2(FontHeight, FontHeight);
-
- ImVec2 PlayheadPos = ImVec2(TimelineStartingPos.x + UI->TimelineZoom * File->CurrentFrame, WindowMinAbs.y + TopbarSize.y/2);
-
- real32 MaxZoom = TimelineSizeWithBorder.x / (File->NumberOfFrames + 1);
-
- if (UI->Initializing) {
- UI->TimelineZoom = MaxZoom;
- }
-
- ImDrawList* draw_list = ImGui::GetWindowDrawList();
- draw_list->AddRectFilled(WindowMinAbs, WindowMaxAbs,
- IM_COL32(255, 255, 255, 50));
- draw_list->AddRectFilled(WindowMinAbs, TopbarMax,
- IM_COL32(255, 255, 255, 50));
-
-
- //
-
-
- ImGui::BeginChild("Topbar", TopbarSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
- char buf2[6];
- 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();
- */
-
- ImGui::SetCursorScreenPos(PlayheadPos);
- ImGui::Button("P", ButtonSize);
- /*
- if (ImGui::IsItemActive()) {
- if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))
- {
- UI->DraggingKeyframeThreshold += io.MouseDelta.x;
- if (abs(UI->DraggingKeyframeThreshold) >= UI->TimelineZoom) {
- int16 Increment = UI->DraggingKeyframeThreshold/UI->TimelineZoom;
- if (File->CurrentFrame + Increment <= 0)
- File->CurrentFrame = 0;
- else if (File->CurrentFrame + Increment >= File->EndFrame) {
- File->CurrentFrame = File->EndFrame;
- } else {
- File->CurrentFrame += Increment;
- }
- State->UpdateFrame = true;
- State->UpdateKeyframes = true;
- UI->DraggingKeyframeThreshold += -1*Increment*UI->TimelineZoom;
- }
- }
- }
- */
-
- ImGui::EndChild();
-
- ///
-
- ImGui::BeginChild("Sidebar", SidebarSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
-
- ImGui::SetCursorScreenPos(SidebarStartingPos);
-
- ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ItemSpacing);
-
- ImGui::PushClipRect(SidebarAbsolutePos, SidebarAbsolutePos + SidebarSizeWithBorder, true);
-
- ImGui::PopClipRect();
-
- /// Split size adjuster
-
- ImGui::SetCursorScreenPos(ImVec2(WindowMinAbs.x + UI->TimelineSplit - TimelineBorderPadding.x, TimelineAbsolutePos.y));
- ImGui::InvisibleButton("##SplitMove", ImVec2(TimelineBorderPadding.x, SidebarSizeWithBorder.y), ImGuiButtonFlags_MouseButtonLeft);
- if (ImGui::IsItemHovered()) {
- ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
- }
- if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))
- {
- UI->TimelineSplit += io.MouseDelta.x;
- }
-
-
- ImGui::PopStyleVar();
-
- ImGui::EndChild();
- ImGui::SameLine();
-
- ///
-
- ImGui::BeginChild("Timeline", TimelineSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
-
- ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ItemSpacing.y));
-
- ImGui::SetCursorScreenPos(TimelineStartingPos);
-
- ImGui::PushClipRect(TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, true);
- draw_list->PushClipRect(TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, true);
-
- project_layer *Layer = File->Layer[0];
- property_channel *Property = &Layer->x;
-
-
- real32 MaxVal_Y = -10000;
- real32 MinVal_Y = 10000;
- for (int b = 0; b < Property->NumberOfTotalKeyframes; b++) {
- keyframe *Keyframe = KeyframeLookup(Property, b);
- MaxVal_Y = (Keyframe->Value.f > MaxVal_Y) ? Keyframe->Value.f : MaxVal_Y;
- 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;
-
- if (!UI->IsDragging) {
- UI->Display_Y_MinVal = UI->Y_MinVal;
- UI->Display_Y_MaxVal = UI->Y_MaxVal;
- }
-
- real32 Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed;
- real32 Y_TimelinePercentOffset = UI->Y_TimelinePercentOffset;
- MaxVal_Y = UI->Display_Y_MaxVal;
- MinVal_Y = UI->Display_Y_MinVal;
-
- 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);
-
- keyframe *Keyframe = KeyframeLookup(Property, b);
- // Only used for drawing the bezier.
- keyframe *NextKeyframe = (b != Property->NumberOfTotalKeyframes - 1) ? KeyframeLookup(Property, b + 1) : NULL;
-
- real32 Increment_X = (real32)1 / File->NumberOfFrames;
- real32 UI_FrameDistance = Increment_X*TimelineZoomSize;
-
- 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)))
- {
- 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
- // ratios of the keyframes/timeline to make the bars straightforward,
- // meaning the position/offset have to be transformed into a new space
- // when a new min/max value is reached.
-
- if (ImGui::IsItemDeactivated()) {
- if ((UI->TempVal >= MaxVal_Y) || Keyframe->Value.f == UI->Y_MaxVal) {
- 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);
- } else if (UI->TempVal <= MinVal_Y || Keyframe->Value.f == UI->Y_MinVal) {
- real32 RealRatio_Y = (UI->Y_MinVal - MinVal_Y) / (MaxVal_Y - MinVal_Y);
- UI->Y_TimelinePercentOffset = UI->Y_TimelinePercentOffset / (1 - RealRatio_Y);
- 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();
- }
-
- ImGui_TimelineIncrementDraw(File, UI, draw_list, TimelineSizeWithBorder, TimelineAbsolutePos, 0);
- ImGui_TimelineIncrementDraw2(File, draw_list, MaxVal_Y, MinVal_Y, Y_TimelinePercentZoomed, Y_TimelinePercentOffset, TimelineSizeWithBorder, TimelineAbsolutePos, 0);
-
-#if DEBUG
- draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.25, TimelineSizeWithBorder.y - 50),
- 2, IM_COL32(200, 000, 200, 200), 16, 1);
- draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.5, TimelineSizeWithBorder.y - 50),
- 2, IM_COL32(200, 000, 200, 200), 16, 1);
- draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.75, TimelineSizeWithBorder.y - 50),
- 2, IM_COL32(200, 000, 200, 200), 16, 1);
-#endif
-
- ImVec2 MouseDelta = io.MouseDelta / TimelineSize;
-
- real32 BarHandleSize = FontHeight;
- real32 BarThickness = 50;
- real32 BarMinZoom = 0.01;
-
- real32 BarH_Pos = -TimelineSizeWithBorder.x * UI->TimelinePercentOffset;
- real32 BarH_Size = TimelineSizeWithBorder.x / (1 / UI->TimelinePercentZoomed);
-
- // I use "UI" to denote the size/position after clipping the bar so that it
- // doesn't go out of bounds and the handles are always selectable at the edges.
-
- real32 BarH_Offset = Max(BarH_Pos, 0);
-
- real32 BarH_SizeUI = (BarH_Size + BarH_Pos > TimelineSizeWithBorder.x) ?
- TimelineSizeWithBorder.x - BarH_Pos :
- BarH_Size + (BarH_Pos - BarH_Offset);
-
- if (BarH_Offset == 0 && BarH_SizeUI > TimelineSizeWithBorder.x)
- BarH_SizeUI = TimelineSizeWithBorder.x;
-
- BarH_SizeUI = BarH_SizeUI - BarHandleSize*2;
-
- BarH_SizeUI = Max(BarH_SizeUI, FontHeight*4);
-
- 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));
-
- if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)))
- {
- if ((UI->TimelinePercentZoomed - MouseDelta.x) > BarMinZoom) {
- UI->TimelinePercentZoomed -= MouseDelta.x;
- UI->TimelinePercentOffset -= MouseDelta.x;
- }
- BarHeld = true;
- }
-
- ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0));
- ImGui::Button("##scrollbarhori", ImVec2(BarH_SizeUI, BarThickness));
-
- 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));
- ImGui::Button("##scrollbarright", ImVec2(BarHandleSize, BarThickness));
-
- if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)))
- {
- 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);
-
- real32 BarV_MaxSize = TimelineSizeWithBorder.y - BarThickness/2;
- real32 BarV_Pos = -BarV_MaxSize * Y_TimelinePercentOffset;
- real32 BarV_Size = BarV_MaxSize / (1 / Y_TimelinePercentZoomed);
- BarV_Size = Max(BarV_Size, FontHeight*4);
-
- real32 BarV_Offset = Max(BarV_Pos, 0);
-
- real32 BarV_SizeUI = (BarV_Size + BarV_Pos > BarV_MaxSize) ?
- BarV_MaxSize - BarV_Pos :
- BarV_Size + (BarV_Pos - BarV_Offset);
-
- if (BarV_Offset == 0 && BarV_SizeUI > BarV_MaxSize)
- BarV_SizeUI = BarV_MaxSize;
-
- BarV_SizeUI = BarV_SizeUI - BarHandleSize*2;
-
- BarV_SizeUI = Max(BarV_SizeUI, FontHeight*4);
-
- 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));
-
- if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)))
- {
- UI->Y_TimelinePercentZoomed -= MouseDelta.y;
- UI->Y_TimelinePercentOffset -= MouseDelta.y;
- BarHeld = true;
- }
-
- ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize));
- 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));
- ImGui::Button("##h-scrollbarright", ImVec2(BarThickness, BarHandleSize));
-
- 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);
- ImGui::InvisibleButton("TimelineMoving", TimelineSizeWithBorder, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
- bool32 IsHovered = ImGui::IsItemHovered();
- bool32 IsActive = ImGui::IsItemActive();
- bool32 IsItemActivated = ImGui::IsItemActivated();
- bool32 IsItemDeactivated = ImGui::IsItemDeactivated();
- bool32 LeftClick = ImGui::IsMouseDown(ImGuiMouseButton_Left);
- bool32 RightClick = ImGui::IsMouseDown(ImGuiMouseButton_Right);
-
- if (IsActive) {
- if (LeftClick) {
- if (io.KeyCtrl && IsActive) {
- real32 LocalMousePos = ImGui::GetMousePos().x - TimelineStartingPos.x;
- real32 ZoomRatio = LocalMousePos / UI->TimelineZoom;
- File->CurrentFrame = (int32)(ZoomRatio + 0.5);
- State->UpdateFrame = true;
- State->UpdateKeyframes = true;
- } else {
- if (IsItemActivated)
- {
- if (!io.KeyShift) {
- // DeselectAllKeyframes(&State);
- // DeselectAllLayers(File, State);
- }
- UI->BoxStart = ImGui::GetMousePos();
- UI->BoxSelectActive = true;
- }
- if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1) )
- {
- UI->BoxEnd = ImGui::GetMousePos();
- draw_list->AddRectFilled(UI->BoxStart, UI->BoxEnd,
- IM_COL32(0, 0, 200, 50));
- }
- }
- // Timeline zooming interaction
- } else if (RightClick && IsActive) {
- if (IsItemActivated)
- {
- real32 LocalMousePos = io.MousePos.x - WindowMinAbs.x - UI->TimelineSplit;
- UI->TempZoomRatioTimeline = LocalMousePos / TimelineSize.x;
- }
- if (ImGui::IsMouseDragging(ImGuiMouseButton_Right, -1) )
- {
- UI->TimelineZoom += io.MouseDelta.x;
- }
- }
- }
- if (IsItemDeactivated) {
- UI->BoxStart = {0, 0};
- UI->BoxEnd = {0, 0};
- UI->BoxSelectActive = false;
- if (!io.KeyShift) DeselectAllLayers(File, State);
- }
-
- ImGui::EndChild();
-
- ImGui::PopStyleVar(2);
-
- if (IsRectTouching(WindowMinAbs, WindowMaxAbs, io.MousePos, io.MousePos + 1)) {
- real32 Multiplier = 16;
- if (io.KeyCtrl && io.MouseWheel) {
- real32 ZoomAmount = io.MouseWheel*Multiplier;
- real32 LocalMousePos = ImGui::GetMousePos().x - TimelineStartingPos.x;
- real32 ZoomRatio = LocalMousePos / UI->TimelineZoom;
- if (UI->TimelineZoom + ZoomAmount > 0) {
- UI->TimelineZoom += ZoomAmount;
- UI->ScrollXOffset -= ZoomAmount*ZoomRatio;
- }
- } else if (io.KeyShift && io.MouseWheel) {
- UI->ScrollXOffset += io.MouseWheel*Multiplier;
- } else {
- UI->ScrollXOffset += io.MouseWheelH*Multiplier;
- UI->ScrollYOffset += io.MouseWheel*Multiplier;
- }
- }
-
-
-
- ImGui::End();
-}
-
static void
ImGui_ProcessInputs(project_data *File, project_state *State, comp_buffer *CompBuffer, memory *Memory, ui *UI, ImGuiIO io)
diff --git a/prenderer.cpp b/prenderer.cpp
index 9752663..b61290a 100644
--- a/prenderer.cpp
+++ b/prenderer.cpp
@@ -1,3 +1,437 @@
+
+static void
+Fallback_RenderLayer(transform_info T, void *OutputBuffer, rectangle RenderRegion);
+
+static void
+RenderLayers(render_entry Entry) {
+ Fallback_RenderLayer(*(transform_info *)Entry.RenderData, Entry.OutputBuffer, Entry.RenderRegion);
+#if 0
+#if ARM
+ Fallback_RenderLayer(RenderData->TransformInfo[i], RenderInfo->CompBuffer, RenderRegion);
+#else
+ if (InstructionMode == instruction_mode_avx)
+ AVX2_RenderLayer(Entry.T, Entry.OutputBuffer, Entry.RenderRegion);
+ else
+ Fallback_RenderLayer(Entry.T, Entry.OutputBuffer, Entry.RenderRegion);
+#endif
+#endif
+}
+
+static void
+Renderer_Start(void *Data, void *OutputBuffer, rectangle RenderRegion)
+{
+ // CPU
+ Threading_BitmapOp(Data, OutputBuffer, RenderRegion);
+}
+
+static void
+Renderer_Check(bool32 *Test)
+{
+ // CPU
+ *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)
+{
+ transform_info TransformInfo;
+ int Width = 0, Height = 0, BytesPerPixel = 0;
+ if (!Layer->IsPrecomp) {
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index);
+ Width = Source->Width;
+ Height = Source->Height;
+ BytesPerPixel = Source->BytesPerPixel;
+ } else {
+ block_composition *Precomp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, Layer->Block_Source_Index);
+ Width = Precomp->Width;
+ Height = Precomp->Height;
+ BytesPerPixel = Precomp->BytesPerPixel;
+ }
+
+ real32 Rotation = Layer->rotation.CurrentValue;
+ real32 X = Layer->x.CurrentValue;
+ real32 Y = Layer->y.CurrentValue;
+ real32 s = Layer->scale.CurrentValue;
+ blend_mode BlendMode = Layer->BlendMode;
+
+ /*
+ state_file_ui *UI = &State->Context[State->CurrentFileIndex].UI;
+ if (UI->IsInteracting == true && UI->InteractMode == interact_transforms && Layer->IsSelected && !Layer->IsAdjustment)
+ Transform_ApplyInteractive(UI, &X, &Y, &Rotation, &s);
+
+ if (UI->IsInteractingBlendmode == true && Layer->IsSelected)
+ BlendMode = UI->InteractBlendmode;
+ */
+
+ real32 Rad = (Rotation * (PI / 180));
+ // v2 Scale = {Source->Raster.Width * s, Source->Raster.Height * s};
+
+ v2 XAxis = (Width * s)*V2(cos(Rad), sin(Rad));
+ v2 YAxis = (Height * -s)*V2(sin(Rad), -cos(Rad));
+
+ real32 AnchorX = Layer->ax.CurrentValue;
+ real32 AnchorY = Layer->ay.CurrentValue;
+
+ v2 Pos = {X, Y};
+ v2 Origin = Pos - (XAxis * AnchorX) - (YAxis * AnchorY);
+
+ real32 XLengthSq = 1.0f / LengthSq(XAxis);
+ real32 YLengthSq = 1.0f / LengthSq(YAxis);
+
+ int32 MaxX = 0;
+ int32 MaxY = 0;
+ int32 MinX = Comp->Width;
+ int32 MinY = Comp->Height;
+
+ 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; }
+ }
+
+ TransformInfo.XAxisPX = XLengthSq*XAxis.x;
+ TransformInfo.XAxisPY = XLengthSq*XAxis.y;
+ TransformInfo.YAxisPX = YLengthSq*YAxis.x;
+ TransformInfo.YAxisPY = YLengthSq*YAxis.y;
+
+ TransformInfo.BufferWidth = Comp->Width;
+ TransformInfo.BufferHeight = Comp->Height;
+ TransformInfo.BufferBytesPerPixel = Comp->BytesPerPixel;
+ TransformInfo.BufferBits = Bitmap_ByteInfo(Comp->BytesPerPixel);
+
+ TransformInfo.LayerWidth = Width;
+ TransformInfo.LayerHeight = Height;
+ TransformInfo.LayerBytesPerPixel = BytesPerPixel;
+ TransformInfo.LayerBits = Bitmap_ByteInfo(BytesPerPixel);
+
+ TransformInfo.LayerOpacity = Layer->opacity.CurrentValue;
+ TransformInfo.BlendMode = BlendMode;
+ TransformInfo.OriginX = Origin.x;
+ TransformInfo.OriginY = Origin.y;
+ TransformInfo.BufferPitch = Comp->Width*Comp->BytesPerPixel;
+ TransformInfo.LayerPitch = Width*BytesPerPixel;
+ TransformInfo.ClipRect = {MinX - (MinX & 3), MinY, MaxX + 1, MaxY + 1};
+
+ TransformInfo.IsAdjustment = Layer->IsAdjustment;
+
+ return TransformInfo;
+}
+
+static void
+Fallback_RenderLayer(transform_info T, void *OutputBuffer, rectangle RenderRegion)
+{
+ rectangle LayerBounds = ClipRectangle( T.ClipRect, RenderRegion);
+
+ Assert(LayerBounds.Max.x <= T.BufferWidth);
+ Assert(LayerBounds.Max.y <= T.BufferHeight);
+
+ for (int16 Y = LayerBounds.Min.y; Y < LayerBounds.Max.y; Y++)
+ {
+ real32 StartVectorY = (real32)Y - T.OriginY;
+
+ for (int16 X = LayerBounds.Min.x; X < LayerBounds.Max.x; X++)
+ {
+
+ real32 StartVectorX = X - T.OriginX;
+ real32 U = (StartVectorX * T.XAxisPX) + (StartVectorY * T.XAxisPY);
+ real32 V = (StartVectorX * T.YAxisPX) + (StartVectorY * T.YAxisPY);
+
+ if (U < 1.0f && U >= 0.0f && V < 1.0f && V >= 0.0f) {
+
+ real32 TexXFull = U * T.LayerWidth;
+ uint32 TexXInt = (uint32)TexXFull;
+ real32 TexX = TexXFull - TexXInt;
+
+ real32 TexYFull = V * T.LayerHeight;
+ uint32 TexYInt = (uint32)TexYFull;
+ real32 TexY = TexYFull - TexYInt;
+
+ real32 TexXInv = 1 - TexX;
+ real32 TexYInv = 1 - TexY;
+ real32 TexBothXInv = TexXInv * TexY;
+ real32 TexBothYInv = TexX * TexYInv;
+ real32 TexBoth = TexY * TexX;
+ real32 TexBothInv = TexXInv * TexYInv;
+
+ uint32 XLookup, YLookup, PixelToSeek;
+
+ uint16 LX = TexXInt;
+ uint16 LY = TexYInt;
+ uint16 LXPlus = Ceil(TexXInt+1, (uint32)T.LayerWidth - 1);
+ uint16 LYPlus = Ceil(TexYInt+1, (uint32)T.LayerHeight - 1);
+
+ uint8 *TexPTR0 = ((uint8 *)T.SourceBuffer + ((uint16)T.LayerPitch * LY) + (LX * (uint16)T.LayerBytesPerPixel));
+ uint8 *TexPTR1 = ((uint8 *)T.SourceBuffer + ((uint16)T.LayerPitch * LY) + (LXPlus * (uint16)T.LayerBytesPerPixel));
+ uint8 *TexPTR2 = ((uint8 *)T.SourceBuffer + ((uint16)T.LayerPitch * LYPlus) + (LX * (uint16)T.LayerBytesPerPixel));
+ uint8 *TexPTR3 = ((uint8 *)T.SourceBuffer + ((uint16)T.LayerPitch * LYPlus) + (LXPlus * (uint16)T.LayerBytesPerPixel));
+
+ uint32 PixelA = *(uint32 *)TexPTR0;
+ uint32 PixelB = *(uint32 *)TexPTR1;
+ uint32 PixelC = *(uint32 *)TexPTR2;
+ uint32 PixelD = *(uint32 *)TexPTR3;
+
+
+#if 0
+ real32 TexRA = (real32)(PixelA & 0xFF) * Normalized255;
+ real32 TexRB = (real32)(PixelB & 0xFF) * Normalized255;
+ real32 TexRC = (real32)(PixelC & 0xFF) * Normalized255;
+ real32 TexRD = (real32)(PixelD & 0xFF) * Normalized255;
+
+ real32 TexGA = (real32)((PixelA >> 8) & 0xFF) * Normalized255;
+ real32 TexGB = (real32)((PixelB >> 8) & 0xFF) * Normalized255;
+ real32 TexGC = (real32)((PixelC >> 8) & 0xFF) * Normalized255;
+ real32 TexGD = (real32)((PixelD >> 8) & 0xFF) * Normalized255;
+
+ real32 TexBA = (real32)((PixelA >> 16) & 0xFF) * Normalized255;
+ real32 TexBB = (real32)((PixelB >> 16) & 0xFF) * Normalized255;
+ real32 TexBC = (real32)((PixelC >> 16) & 0xFF) * Normalized255;
+ real32 TexBD = (real32)((PixelD >> 16) & 0xFF) * Normalized255;
+
+ real32 TexAA = (real32)((PixelA >> 24) & 0xFF) * Normalized255;
+ real32 TexAB = (real32)((PixelB >> 24) & 0xFF) * Normalized255;
+ real32 TexAC = (real32)((PixelC >> 24) & 0xFF) * Normalized255;
+ real32 TexAD = (real32)((PixelD >> 24) & 0xFF) * Normalized255;
+#else
+ real32 TexRA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 0) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexGA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 1) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexBA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 2) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexAA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 3) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+
+ real32 TexRB = (real32)(*(uint32 *)(TexPTR1 + T.LayerBits.ByteOffset * 0) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexGB = (real32)(*(uint32 *)(TexPTR1 + T.LayerBits.ByteOffset * 1) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexBB = (real32)(*(uint32 *)(TexPTR1 + T.LayerBits.ByteOffset * 2) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexAB = (real32)(*(uint32 *)(TexPTR1 + T.LayerBits.ByteOffset * 3) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+
+ real32 TexRC = (real32)(*(uint32 *)(TexPTR2 + T.LayerBits.ByteOffset * 0) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexGC = (real32)(*(uint32 *)(TexPTR2 + T.LayerBits.ByteOffset * 1) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexBC = (real32)(*(uint32 *)(TexPTR2 + T.LayerBits.ByteOffset * 2) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexAC = (real32)(*(uint32 *)(TexPTR2 + T.LayerBits.ByteOffset * 3) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+
+ real32 TexRD = (real32)(*(uint32 *)(TexPTR3 + T.LayerBits.ByteOffset * 0) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexGD = (real32)(*(uint32 *)(TexPTR3 + T.LayerBits.ByteOffset * 1) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexBD = (real32)(*(uint32 *)(TexPTR3 + T.LayerBits.ByteOffset * 2) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexAD = (real32)(*(uint32 *)(TexPTR3 + T.LayerBits.ByteOffset * 3) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+#if 0
+ for (int i = 0; i < 50; i++) {
+ real32 TexRA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 0) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexGA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 1) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexBA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 2) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexAA = (real32)(*(uint32 *)(TexPTR0 + T.LayerBits.ByteOffset * 3) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+
+ real32 TexRB = (real32)(*(uint32 *)(TexPTR1 + T.LayerBits.ByteOffset * 0) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexGB = (real32)(*(uint32 *)(TexPTR1 + T.LayerBits.ByteOffset * 1) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexBB = (real32)(*(uint32 *)(TexPTR1 + T.LayerBits.ByteOffset * 2) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexAB = (real32)(*(uint32 *)(TexPTR1 + T.LayerBits.ByteOffset * 3) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+
+ real32 TexRC = (real32)(*(uint32 *)(TexPTR2 + T.LayerBits.ByteOffset * 0) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexGC = (real32)(*(uint32 *)(TexPTR2 + T.LayerBits.ByteOffset * 1) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexBC = (real32)(*(uint32 *)(TexPTR2 + T.LayerBits.ByteOffset * 2) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexAC = (real32)(*(uint32 *)(TexPTR2 + T.LayerBits.ByteOffset * 3) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+
+ real32 TexRD = (real32)(*(uint32 *)(TexPTR3 + T.LayerBits.ByteOffset * 0) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexGD = (real32)(*(uint32 *)(TexPTR3 + T.LayerBits.ByteOffset * 1) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexBD = (real32)(*(uint32 *)(TexPTR3 + T.LayerBits.ByteOffset * 2) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ real32 TexAD = (real32)(*(uint32 *)(TexPTR3 + T.LayerBits.ByteOffset * 3) & T.LayerBits.MaskPixel) * T.LayerBits.Normalized;
+ }
+#endif
+#endif
+
+ real32 R_Col = (TexBothInv * TexRA) + (TexBothYInv * TexRB)
+ + (TexBothXInv * TexRC) + (TexBoth * TexRD);
+ real32 G_Col = (TexBothInv * TexGA) + (TexBothYInv * TexGB)
+ + (TexBothXInv * TexGC) + (TexBoth * TexGD);
+ real32 B_Col = (TexBothInv * TexBA) + (TexBothYInv * TexBB)
+ + (TexBothXInv * TexBC) + (TexBoth * TexBD);
+ real32 A_Col = (TexBothInv * TexAA) + (TexBothYInv * TexAB)
+ + (TexBothXInv * TexAC) + (TexBoth * TexAD);
+
+ real32 LayerAlpha = A_Col * T.LayerOpacity;
+
+ real32 R_Blend = R_Col;
+ real32 G_Blend = G_Col;
+ real32 B_Blend = B_Col;
+ real32 A_Blend = A_Col;
+
+ uint8 *DestPixel =((uint8 *)OutputBuffer + ((uint16)Y * (uint16)T.BufferPitch) + ((uint16)X * (uint16)T.BufferBytesPerPixel));
+
+ uint32 *R_DestAddress = (uint32 *)(DestPixel + T.BufferBits.ByteOffset * 0);
+ uint32 *G_DestAddress = (uint32 *)(DestPixel + T.BufferBits.ByteOffset * 1);
+ uint32 *B_DestAddress = (uint32 *)(DestPixel + T.BufferBits.ByteOffset * 2);
+ uint32 *A_DestAddress = (uint32 *)(DestPixel + T.BufferBits.ByteOffset * 3);
+
+ if (LayerAlpha != 1.0f || T.BlendMode != blend_normal) {
+
+ real32 R_Dest = (real32)(*R_DestAddress & T.BufferBits.MaskPixel) * T.BufferBits.Normalized;
+ real32 G_Dest = (real32)(*G_DestAddress & T.BufferBits.MaskPixel) * T.BufferBits.Normalized;
+ real32 B_Dest = (real32)(*B_DestAddress & T.BufferBits.MaskPixel) * T.BufferBits.Normalized;
+ real32 A_Dest = (real32)(*A_DestAddress & T.BufferBits.MaskPixel) * T.BufferBits.Normalized;
+
+ switch (T.BlendMode)
+ {
+ case blend_normal:
+ {
+ } break;
+ case blend_multiply:
+ {
+ R_Blend = R_Dest * R_Col;
+ G_Blend = G_Dest * G_Col;
+ B_Blend = B_Dest * B_Col;
+ } break;
+ case blend_colorburn:
+ {
+ // NOTE(fox): Padding to prevent actual crashing from zero division
+ R_Blend = 1.0f - ((1.0f - R_Dest) / (R_Col + 0.001f));
+ G_Blend = 1.0f - ((1.0f - G_Dest) / (G_Col + 0.001f));
+ B_Blend = 1.0f - ((1.0f - B_Dest) / (B_Col + 0.001f));
+ } break;
+ case blend_linearburn:
+ {
+ R_Blend = (R_Dest + R_Col) - 1.0f;
+ G_Blend = (G_Dest + G_Col) - 1.0f;
+ B_Blend = (B_Dest + B_Col) - 1.0f;
+ } break;
+ case blend_add:
+ {
+ R_Blend = R_Dest + R_Col;
+ G_Blend = G_Dest + G_Col;
+ B_Blend = B_Dest + B_Col;
+ } break;
+ case blend_screen:
+ {
+ R_Blend = 1.0f - ((1.0f - R_Dest) * (1.0f - R_Col));
+ G_Blend = 1.0f - ((1.0f - G_Dest) * (1.0f - G_Col));
+ B_Blend = 1.0f - ((1.0f - B_Dest) * (1.0f - B_Col));
+ } break;
+ case blend_overlay:
+ {
+ if (R_Dest < 0.5) {
+ R_Blend = 2.0f * R_Dest * R_Col;
+ } else {
+ R_Blend = 1.0f - (2.0f * (1.0f - R_Dest) * (1.0f - R_Col));
+ }
+ if (G_Dest < 0.5) {
+ G_Blend = 2.0f * G_Dest * G_Col;
+ } else {
+ G_Blend = 1.0f - (2.0f * (1.0f - G_Dest) * (1.0f - G_Col));
+ }
+ if (B_Dest < 0.5) {
+ B_Blend = 2.0f * B_Dest * B_Col;
+ } else {
+ B_Blend = 1.0f - (2.0f * (1.0f - B_Dest) * (1.0f - B_Col));
+ }
+ } break;
+ case blend_softlight:
+ {
+ // using Pegtop's equation
+ R_Blend = ((1.0f - R_Col * 2) * R_Dest * R_Dest) + (R_Col * 2 * R_Dest);
+ G_Blend = ((1.0f - G_Col * 2) * G_Dest * G_Dest) + (G_Col * 2 * G_Dest);
+ B_Blend = ((1.0f - B_Col * 2) * B_Dest * B_Dest) + (B_Col * 2 * B_Dest);
+ } break;
+ case blend_hardlight:
+ {
+ if (R_Dest > 0.5) {
+ R_Blend = 2.0f * R_Dest * R_Col;
+ } else {
+ R_Blend = 1.0f - (2.0f * (1.0f - R_Dest) * (1.0f - R_Col));
+ }
+ if (G_Dest > 0.5) {
+ G_Blend = 2.0f * G_Dest * G_Col;
+ } else {
+ G_Blend = 1.0f - (2.0f * (1.0f - G_Dest) * (1.0f - G_Col));
+ }
+ if (B_Dest > 0.5) {
+ B_Blend = 2.0f * B_Dest * B_Col;
+ } else {
+ B_Blend = 1.0f - (2.0f * (1.0f - B_Dest) * (1.0f - B_Col));
+ }
+ } break;
+ case blend_subtract:
+ {
+ R_Blend = R_Dest - R_Col;
+ G_Blend = G_Dest - G_Col;
+ B_Blend = B_Dest - B_Col;
+ } break;
+ case blend_divide:
+ {
+ R_Blend = R_Dest / (R_Col + 0.001f);
+ G_Blend = G_Dest / (G_Col + 0.001f);
+ B_Blend = B_Dest / (B_Col + 0.001f);
+ } break;
+ case blend_difference:
+ {
+ if (R_Col - R_Dest > 0) {
+ R_Blend = R_Col - R_Dest;
+ } else {
+ R_Blend = R_Dest - R_Col;
+ }
+ if (G_Col - G_Dest > 0) {
+ G_Blend = G_Col - G_Dest;
+ } else {
+ G_Blend = G_Dest - G_Col;
+ }
+ if (B_Col - B_Dest > 0) {
+ B_Blend = B_Col - B_Dest;
+ } else {
+ B_Blend = B_Dest - B_Col;
+ }
+ } break;
+ }
+
+ R_Blend = (R_Dest * (1.0f - LayerAlpha)) + (R_Blend * LayerAlpha);
+ G_Blend = (G_Dest * (1.0f - LayerAlpha)) + (G_Blend * LayerAlpha);
+ B_Blend = (B_Dest * (1.0f - LayerAlpha)) + (B_Blend * LayerAlpha);
+
+ if (T.BlendMode == blend_normal)
+ A_Blend = A_Dest + LayerAlpha;
+ else
+ A_Blend = A_Dest;
+ }
+
+ uint32 R_Out = (uint32)(Normalize(R_Blend) * T.BufferBits.Bits);
+ uint32 G_Out = (uint32)(Normalize(G_Blend) * T.BufferBits.Bits);
+ uint32 B_Out = (uint32)(Normalize(B_Blend) * T.BufferBits.Bits);
+ uint32 A_Out = (uint32)(Normalize(A_Blend) * T.BufferBits.Bits);
+
+ *R_DestAddress = (*R_DestAddress & ~T.BufferBits.MaskPixel) | R_Out;
+ *G_DestAddress = (*G_DestAddress & ~T.BufferBits.MaskPixel) | G_Out;
+ *B_DestAddress = (*B_DestAddress & ~T.BufferBits.MaskPixel) | B_Out;
+ *A_DestAddress = (*A_DestAddress & ~T.BufferBits.MaskPixel) | A_Out;
+ // *R_DestAddress = 255;
+ // *G_DestAddress = 255;
+ // *B_DestAddress = 255;
+ // *A_DestAddress = 255;
+ }
+ }
+ }
+}
+
+#if 0
static void
Layer_CalcRotatedOffset(project_layer *Layer, v2 Increment, v2 Divisor, real32 *ValueX, real32 *ValueY)
{
@@ -1380,3 +1814,4 @@ Fallback_RenderLayer(transform_info T, comp_buffer *Buffer, rectangle RenderRegi
}
}
}
+#endif
diff --git a/threading.cpp b/threading.cpp
index ff891a6..84e5463 100644
--- a/threading.cpp
+++ b/threading.cpp
@@ -1,58 +1,83 @@
-
-static int
-MainRenderer(void *ptr)
-{
- for(;;)
- {
- SDL_SemWait(Semaphore);
- }
-}
-
-#if 0
static void
-PushRect(rectangle RenderRegion)
-{
- uint32 Q = SDL_AtomicGet(&QueuedEntries);
- render_entry *Entry = Entries + Q;
- Entry->RenderRegion = RenderRegion;
- SDL_AtomicAdd(&QueuedEntries, 1);
- SDL_SemPost(Semaphore);
-}
-
-static void
-RenderLayers(render_queue *RenderInfo, rectangle RenderRegion);
+RenderLayers(render_entry Entry);
static bool32
-CheckQueue(render_queue RenderInfo, uint16 Index)
+CheckQueue(uint16 Index)
{
bool32 Result = 0;
uint32 Q = SDL_AtomicGet(&QueuedEntries);
uint32 C = SDL_AtomicGet(&CurrentEntry);
if (Q > C)
- {
- if (SDL_AtomicCAS(&CurrentEntry, C, C + 1)) {
- render_entry *Entry = Entries + C;
- Assert(Entry->RenderRegion.Max.x != 0);
- RenderLayers(&RenderInfo, Entry->RenderRegion);
- // printf("(FINISHED) Thread %i, region X%i Y%i\n", Index, Entry->RenderRegion.Min.x/240, Entry->RenderRegion.Min.y/135);
- SDL_AtomicAdd(&CompletedEntries, 1);
- Result = 1;
- }
+ {
+ if (SDL_AtomicCAS(&CurrentEntry, C, C + 1)) {
+ // printf("C: %i START: F%i Thread %i, region X%i Y%i\n", C, Entry->FrameNumber, Index, Entry->RenderRegion.Min.x, Entry->RenderRegion.Min.y);
+ RenderLayers(Entries[C]);
+ // printf("END: F%i Thread %i, region X%i Y%i\n", Entry->FrameNumber, Index, Entry->RenderRegion.Min.x, Entry->RenderRegion.Min.y);
+ SDL_AtomicAdd(&CompletedEntries, 1);
+ Result = 1;
}
+ }
return Result;
}
static int
TestThread(void *ptr)
{
- thread_info *ThreadInfo = (thread_info *)ptr;
- uint16 Index = ThreadInfo->Index;
+ uint16 Index = *(uint16 *)ptr;
for(;;)
{
- if (!CheckQueue(*ThreadInfo->RenderInfo, Index))
+ if (!CheckQueue(Index))
{
SDL_SemWait(Semaphore);
}
}
}
-#endif
+
+static bool32
+Threading_IsActive()
+{
+ uint32 C = SDL_AtomicGet(&CompletedEntries);
+ Assert(C < 17);
+ return (C == 16) ? false : true;
+}
+
+static void
+Threading_BitmapOp(void *Data, void *OutputBuffer, rectangle InitialRenderRegion)
+{
+ uint16 TileWidth = (InitialRenderRegion.Max.x - InitialRenderRegion.Min.x) / 4;
+ uint16 TileHeight = (InitialRenderRegion.Max.y - InitialRenderRegion.Min.y) / 4;
+
+ SDL_AtomicSet(&QueuedEntries, 0);
+ SDL_AtomicSet(&CurrentEntry, 0);
+ SDL_AtomicSet(&CompletedEntries, 0);
+
+ for (int y = 0; y < 4; y++) {
+ for (int x = 0; x < 4; x++) {
+ // if (x == y) {
+ rectangle RenderRegion = { TileWidth*x, TileHeight*y, TileWidth + TileWidth*x, TileHeight + TileHeight*y };
+
+ RenderRegion.Min.x -= RenderRegion.Min.x % 8;
+ RenderRegion.Min.y -= RenderRegion.Min.y % 8;
+ RenderRegion.Max.x -= RenderRegion.Max.x % 8;
+ RenderRegion.Max.y -= RenderRegion.Max.y % 8;
+
+ if (RenderRegion.Max.x > InitialRenderRegion.Max.x)
+ RenderRegion.Max.x = InitialRenderRegion.Max.x;
+ if (RenderRegion.Max.y > InitialRenderRegion.Max.y)
+ RenderRegion.Max.y = InitialRenderRegion.Max.y;
+
+ if (x == 3)
+ RenderRegion.Max.x = InitialRenderRegion.Max.x;
+ if (y == 3)
+ RenderRegion.Max.y = InitialRenderRegion.Max.y;
+
+ render_entry Entry = { Data, OutputBuffer, RenderRegion };
+
+ uint32 Q = SDL_AtomicGet(&QueuedEntries);
+ *(Entries + Q) = Entry;
+ SDL_AtomicAdd(&QueuedEntries, 1);
+ SDL_SemPost(Semaphore);
+ }
+ // }
+ }
+}