#include "main.h" #include "imgui_internal_widgets.h" 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; uint32 BitmapFill = 0x00000001; #if STABLE #include "base64.c" #include #endif #if SPECIAL #else #include "memory.cpp" #include "undo.cpp" #include "io.cpp" #include "sorted.cpp" #include "nanovg.cpp" #include "layer.cpp" #include "strings.cpp" #include "threading.cpp" #include "createcalls.cpp" #if STABLE #include "stable_diffusion.cpp" #endif #include "ffmpeg_backend.cpp" #include "imgui_helper.cpp" #include "imgui_ui_properties.cpp" #include "imgui_ui_timeline.cpp" #include "imgui_ui_viewport.cpp" #if DEBUG #include "imgui_ui_debug.cpp" #endif #if STABLE #include "imgui_ui_stable_diffusion.cpp" #endif #include "imgui_ui.cpp" #include "prenderer.cpp" #include "gl_calls.cpp" #include "bezier.cpp" #include "effects_gl_shader.cpp" #include "effects.cpp" #include "effects_constructors.cpp" #endif static void Main_RenderUI(ImGuiIO io, ImVec4 clear_color, SDL_Window *window) { 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); } // NOTE(fox): Many state changes require the frame after the input event to be rendered. static bool32 Main_InputTest(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID) { uint64 InputStart = SDL_GetPerformanceCounter(); ImGuiIO& io = ImGui::GetIO(); SDL_Event event = {}; int test = 0; // printf("%i\n", State->MenuFocused); while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_DROPFILE) { char *DropFile = event.drop.file; File_Open(File, State, Memory, DropFile); #if 0 if (State->MenuFocused) { if (File_Open(File, State, Memory, DropFile)) { State->UpdateFrame = true; } } else { Source_Generate(File, State, Memory, DropFile); } #endif 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; test++; } if (State->UpdateScreen == 0 && test == 0) return 0; if (State->Warp_WantSetPos) { ImGui::GetIO().WantSetMousePos = true; io.MousePos = State->Warp_PositionToSet; } ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); ImGuiID DockID = ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(), ImGuiDockNodeFlags_AutoHideTabBar); if (State->FirstFrame) { ImGui::MyWindowSetup(&State->RightDock, DockID); } // ImGui::MyDockWindow("Timeline", State->RightDock); if (State->Warp_WantSetPos) { ImGui_WarpMouseFinish(State, io.MousePos); io.MouseDelta = {}; State->Warp_WantSetPos = false; } if (!io.WantCaptureKeyboard) ImGui_ProcessInputs(File, State, UI, Memory, io, Sorted); // The windows don't seem to draw properly on the frame after startup, so // we need to delay a bit. if (State->FirstFrame) { ImGui::EndFrame(); return 2; } #if DEBUG if (Debug.ToggleWindow) { ImGui::ShowDemoWindow(); ImGui_DebugRenderQueue(State); ImGui_DebugMemoryViewer(Memory, State); ImGui_DebugUndoTree(Memory, State); } #endif ImGui_Timeline(File, State, Memory, UI, io, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyStart, Sorted.PropertyArray); ImGui_Viewport(File, State, UI, Memory, io, textureID, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyStart, Sorted.PropertyArray); if (File->UI.Mode == 0) { ImGui_EffectsPanel(File, State, Memory, UI, io); } else { } ImGui_PropertiesPanel(File, State, UI, Memory, io, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyStart, Sorted.PropertyArray); ImGui_File(File, State, Memory, io, Sorted.CompArray, Sorted.LayerArray); ImGui_ColorPanel(File, State, UI, Memory, io); #if STABLE if (UI->StableEnabled) { ImGui_SD_Prompt(File, State, UI, Memory, io, Sorted.CompArray, Sorted.LayerArray); ImGui_SD_Thumbnail(File, State, UI, Memory, io, Sorted.CompArray, Sorted.LayerArray, Sorted.SourceArray, Sorted.TempSourceCount); } #endif ImGui_Menu(File, State, UI, Memory, io); ImGui_Popups(File, State, UI, Memory, io); #if DEBUG Debug.Temp = {}; #endif ImGui::EndFrame(); if (Memory->PurgeCache) { Memory_Cache_Purge(File, State, Memory); Memory->PurgeCache = false; } if (test) test += 1; if (State->UpdateScreen) test = 0; uint64 InputTime = SDL_GetPerformanceCounter() - InputStart; // printf("OURUI: %.2lu\n", InputTime); return test; } static void Render_Main(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID, void *Data, void *OutputBuffer, render_type RenderType, rectangle RenderRegion) { bool32 IsRendering = true; Renderer_Start(Data, OutputBuffer, RenderType, RenderRegion); while (IsRendering) { // Main_InputTest(File, State, Memory, Sorted, UI, window, textureID); // ImGuiIO& io = ImGui::GetIO(); // Main_RenderUI(io, ImVec4(0.45f, 0.55f, 0.60f, 1.00f), window); Renderer_Check(&IsRendering, RenderType); } } static void Layer_UpdateAllKeyframes(project_data *File, project_state *State, memory *Memory, block_layer *Layer, uint16 Index_Physical, sorted_property_array *SortedProperty, uint16 *SortedKeyframe, uint16 Frame_Current) { int32 Offset = (State->Interact_Active == interact_type_keyframe_move) ? (int32)State->Interact_Offset[0] : 0; int h = 0, c = 0, p = 0; property_channel *Property = NULL; while (Layer_LoopChannels(State, Memory, &SortedProperty, &SortedKeyframe, Layer, &Property, NULL, &h, &c, &p)) { Assert(Property); if (Property->Block_Bezier_Count) { real32 MinY, MaxY; Property_MinMax_Y(Memory, State, Property, SortedProperty, &MinY, &MaxY); real32 Y_Increment = 1 / (MaxY - MinY); v2 FirstPointPos[3]; bezier_point *FirstPointAddress = Bezier_LookupAddress(Memory, Property->Block_Bezier_Index, SortedKeyframe[0]); Bezier_Interact_Evaluate(State, FirstPointAddress, FirstPointPos); v2 LastPointPos[3]; bezier_point *LastPointAddress = Bezier_LookupAddress(Memory, Property->Block_Bezier_Index, SortedKeyframe[Property->Keyframe_Count - 1]); Bezier_Interact_Evaluate(State, LastPointAddress, LastPointPos); if (FirstPointPos[0].x >= Frame_Current) { Property->CurrentValue = FirstPointPos[0].y; } else if (LastPointPos[0].x <= Frame_Current) { Property->CurrentValue = LastPointPos[0].y; } else { int KeyframeIndex = 0; for (;;) { v2 PointPos[3]; bezier_point *PointAddress = Bezier_LookupAddress(Memory, Property->Block_Bezier_Index, SortedKeyframe[KeyframeIndex + 1]); Bezier_Interact_Evaluate(State, PointAddress, PointPos, 1, Y_Increment); if (PointPos[0].x >= Frame_Current) break; KeyframeIndex++; } v2 PointPos[3]; bezier_point *PointAddress = Bezier_LookupAddress(Memory, Property->Block_Bezier_Index, SortedKeyframe[KeyframeIndex]); Bezier_Interact_Evaluate(State, PointAddress, PointPos, 1, Y_Increment); v2 NextPointPos[3]; bezier_point *NextPointAddress = Bezier_LookupAddress(Memory, Property->Block_Bezier_Index, SortedKeyframe[KeyframeIndex + 1]); Bezier_Interact_Evaluate(State, NextPointAddress, NextPointPos, 1, Y_Increment); if (PointAddress->Type == interpolation_type_hold) { Property->CurrentValue = PointPos[0].y; } else if (PointAddress->Type == interpolation_type_linear && NextPointAddress->Type == interpolation_type_linear) { real32 Ratio = (Frame_Current - PointPos[0].x) / (NextPointPos[0].x - PointPos[0].x); Property->CurrentValue = PointPos[0].y + ((NextPointPos[0].y - PointPos[0].y) * Ratio); } else { Property->CurrentValue = Bezier_SolveYForX(PointPos[0], PointPos[0] + PointPos[2], NextPointPos[0] + NextPointPos[1], NextPointPos[0], Frame_Current); } } } } } static av_info * AV_Retrieve(project_state *State, memory *Memory, uint32 SourceIndex) { av_info *AV = NULL; int h = 0, c = 0, i = 0; while (Block_Loop(Memory, P_AVInfo, State->AVCount, &h, &c, &i)) { av_info *TestAV = (av_info *)Memory_Block_AddressAtIndex(Memory, P_AVInfo, i); if (TestAV->Block_Source_Index == SourceIndex) { AV = TestAV; break; } } return AV; } static void Render_SortKeyframes(project_data *File, project_state *State, memory *Memory, sorted_comp_array *SortedCompStart, sorted_layer_array *SortedLayerStart, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, sorted_property_array *SortedPropertyStart, uint16 *SortedKeyframeArray, int Frame_Current) { for (int i = 0; i < SortedCompStart->LayerCount; i++) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); int32 Frame_Start = Layer->Frame_Start; int32 Frame_End = Layer->Frame_End; int32 Frame_Offset = Layer->Frame_Offset; if (Layer->IsSelected & 0x01) Interact_Evaluate_Layer(Memory, State, Index_Physical, *SortedCompStart, SortedLayerStart, &Frame_Start, &Frame_End, &Frame_Offset); int32 Frame_Start_Abs = Frame_Start + Frame_Offset; int32 Frame_End_Abs = Frame_End + Frame_Offset; int FrameToSeek = State->Frame_Current - Frame_Start_Abs; if (Layer->x.Keyframe_Count == 2) int b = 0; if (State->UpdateKeyframes) { if (Layer->x.Keyframe_Count == 2) int b = 0; } if (Frame_Start_Abs <= Frame_Current && Frame_End_Abs > Frame_Current && Layer->IsVisible) { if (State->UpdateKeyframes) { if (Layer->x.Keyframe_Count == 2) int b = 0; sorted_property_array *SortedLayerProperties = SortedPropertyStart + SortEntry.SortedPropertyStart; uint16 *SortedLayerKeyframes = SortedKeyframeArray + SortEntry.SortedKeyframeStart; Layer_UpdateAllKeyframes(File, State, Memory, Layer, Index_Physical, SortedLayerProperties, SortedLayerKeyframes, Frame_Current); } } } } static void GL_Test(const ImDrawList* parent_list, const ImDrawCmd* cmd) { uint64 PerfStart = SDL_GetPerformanceCounter(); gl_viewport_data *RenderData = (gl_viewport_data *)cmd->UserCallbackData; gl_effect_layer MSBuffer = {}; gl_effect_layer MSBuffer2 = {}; int err = 0; int A[4] = {}; glGetIntegerv(GL_VIEWPORT, A); GL_UpdateTexture(&MSBuffer, NULL, A[2], A[3], RenderData->BytesPerPixel, 1); GL_UpdateTexture2(&MSBuffer2, NULL, A[2], A[3], RenderData->BytesPerPixel, 0); glBindFramebuffer(GL_FRAMEBUFFER, MSBuffer.FramebufferObject); glBindTexture(GL_TEXTURE_2D, 0); glClearColor(RenderData->BGColor.r, RenderData->BGColor.g, RenderData->BGColor.b, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(DefaultShaderProgram); bool32 KeepStencil = false; int StencilLayer = 0; // stencil buffer glDisable(GL_CULL_FACE); glEnable(GL_STENCIL_TEST); glStencilMask(0xff); glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); // printf("%i\n", RenderData->LayerCount); for (int i = 0; i < RenderData->LayerCount; i++) { gl_data *Data = RenderData->LayerEntry[i]; if (Data->Type == 0) { glClearColor(0.0f, 0.0f, 0.0f, 0.0f); GL_RasterizeShape2(&MSBuffer, &MSBuffer2, Data->StrokeData, Data->FillData, Data->StrokeCount, Data->FillCount, Data->T, RenderData->Width, RenderData->Height, RenderData->BytesPerPixel, Data->BitmapData, Data->Width, Data->Height, Data->StrokeCol, Data->FillCol, Data->BlendMode, Data->RenderMode, 0, RenderData->ViewportSize, RenderData->UIPos, RenderData->UIZoom, Data->BlendMin, Data->BlendMax, StencilLayer); } else if (Data->Type == 1) { GL_BlitStencil(&MSBuffer, Data->StrokeData, Data->FillData, Data->StrokeCount, Data->FillCount, Data->T, RenderData->Width, RenderData->Height, RenderData->BytesPerPixel, Data->Width, Data->Height, Data->StrokeCol, Data->FillCol, Data->RenderMode, 0, RenderData->ViewportSize, RenderData->UIPos, RenderData->UIZoom, StencilLayer, GL_INCR); StencilLayer++; } else if (Data->Type == 2) { GL_BlitStencil(&MSBuffer, Data->StrokeData, Data->FillData, Data->StrokeCount, Data->FillCount, Data->T, RenderData->Width, RenderData->Height, RenderData->BytesPerPixel, Data->Width, Data->Height, Data->StrokeCol, Data->FillCol, Data->RenderMode, 0, RenderData->ViewportSize, RenderData->UIPos, RenderData->UIZoom, StencilLayer, GL_DECR); StencilLayer--; } else { Assert(0); } } // stencil buffer testing not needed glDisable(GL_STENCIL_TEST); glStencilMask(0xFF); glStencilFunc(GL_ALWAYS, 0, 0xFF); glBindFramebuffer(GL_READ_FRAMEBUFFER, MSBuffer.FramebufferObject); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // The multisample framebuffer allows us to conveniently clip to the comp bounds. if (RenderData->ViewportDisplay == 1) { ImVec2 MinPos(RenderData->UIPos.x, A[3] - RenderData->UIPos.y - RenderData->UIZoom.y); ImVec2 MaxPos = MinPos + RenderData->UIZoom; glBlitFramebuffer(MinPos.x, MinPos.y, MaxPos.x, MaxPos.y, MinPos.x, MinPos.y, MaxPos.x, MaxPos.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); } else { real32 FlipY = A[3] - RenderData->ViewportMax.y; real32 FlipY2 = A[3] - RenderData->ViewportMin.y; glBlitFramebuffer(RenderData->ViewportMin.x, FlipY, RenderData->ViewportMax.x, FlipY2, RenderData->ViewportMin.x, FlipY, RenderData->ViewportMax.x, FlipY2, GL_COLOR_BUFFER_BIT, GL_LINEAR); } GL_DeleteHWBuffer(&MSBuffer); GL_DeleteHWBuffer(&MSBuffer2); uint64 PerfEnd = SDL_GetPerformanceCounter() - PerfStart; // printf("OPENGL: %.2lu\n", PerfEnd); } // TODO(fox): We have five functions that essentially do this same precomp loop // that carries over transform data; wrap the loop into a function like Block_Loop. // Find the precomp the user most likely wants to insert a shape into, the // precomp that is or contains MostRecentlySelectedLayer. static void LayerIterate_DeepestPrecomp(project_state *State, memory *Memory, uint32 CompIndex, layer_transforms ExtraT, v2 Center, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, int *SelectionCount, int *SelectedLayerIndex, int *SelectedPrecompIndex) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); sorted_comp_array *SortedCompStart = &SortedCompArray[CompIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); int LayerCount = SortedCompStart->LayerCount + SortedCompStart->FakeLayerCount; for (int i = LayerCount - 1; i >= 0; i--) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); Assert(Layer->Block_Composition_Index == CompIndex); int Width = 0, Height = 0; Layer_GetDimensions(Memory, Layer, &Width, &Height); if (Layer->IsPrecomp) { // only like 20% sure how this works... layer_transforms NewExtraT = Layer_GetTransforms(Layer); v2 NewCenter = T_CompPosToLayerPos(NewExtraT, Comp->Width, Comp->Height, Width, Height, Center.x, Center.y); NewExtraT.rotation = ExtraT.rotation - NewExtraT.rotation; NewExtraT.scale = ExtraT.scale / NewExtraT.scale; Assert(0); // LayerIterate_Te(State, Memory, Layer->Block_Source_Index, NewExtraT, NewCenter, SortedCompArray, SortedLayerArray); } v2 Position = State->Interact_Transform.Position; real32 Rad = (ExtraT.rotation * (PI / 180)); v2 XAxis = Position.x * ExtraT.scale * V2(cos(Rad), sin(Rad)); v2 YAxis = Position.y * -ExtraT.scale * V2(sin(Rad), -cos(Rad)); Position = XAxis + YAxis; layer_transforms T = Layer_GetTransforms(Layer); v2 UV = T_CompUVToLayerUV(T, Comp->Width, Comp->Height, Width, Height, Center / V2(Comp->Width, Comp->Height)); if (UV.x <= 1.0f && UV.x >= 0.0f && UV.y <= 1.0f && UV.y >= 0.0f && (Layer->IsSelected & 0x01)) { *SelectionCount += 1; *SelectedLayerIndex = i; *SelectedPrecompIndex = Layer->Block_Composition_Index; } } } // We have to loop over the layers a second time when testing selection because // we want to change the behavior if the mouse is clicking on a layer that is // already selected. static void LayerIterate_SelectionStatus(project_state *State, memory *Memory, uint32 CompIndex, layer_transforms ExtraT, v2 Center, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, int *SelectionCount, int *SelectedLayerIndex, int *SelectedPrecompIndex) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); sorted_comp_array *SortedCompStart = &SortedCompArray[CompIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); int LayerCount = SortedCompStart->LayerCount + SortedCompStart->FakeLayerCount; for (int i = LayerCount - 1; i >= 0; i--) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); Assert(Layer->Block_Composition_Index == CompIndex); int Width = 0, Height = 0; Layer_GetDimensions(Memory, Layer, &Width, &Height); if (Layer->IsPrecomp) { // only like 20% sure how this works... layer_transforms NewExtraT = Layer_GetTransforms(Layer); v2 NewCenter = T_CompPosToLayerPos(NewExtraT, Comp->Width, Comp->Height, Width, Height, Center.x, Center.y); NewExtraT.rotation = ExtraT.rotation - NewExtraT.rotation; NewExtraT.scale = ExtraT.scale / NewExtraT.scale; LayerIterate_SelectionStatus(State, Memory, Layer->Block_Source_Index, NewExtraT, NewCenter, SortedCompArray, SortedLayerArray, SelectionCount, SelectedLayerIndex, SelectedPrecompIndex); } v2 Position = State->Interact_Transform.Position; real32 Rad = (ExtraT.rotation * (PI / 180)); v2 XAxis = Position.x * ExtraT.scale * V2(cos(Rad), sin(Rad)); v2 YAxis = Position.y * -ExtraT.scale * V2(sin(Rad), -cos(Rad)); Position = XAxis + YAxis; layer_transforms T = Layer_GetTransforms(Layer); v2 UV = T_CompUVToLayerUV(T, Comp->Width, Comp->Height, Width, Height, Center / V2(Comp->Width, Comp->Height)); if (UV.x <= 1.0f && UV.x >= 0.0f && UV.y <= 1.0f && UV.y >= 0.0f && (Layer->IsSelected & 0x01) && !Layer->IsLocked) { *SelectionCount += 1; *SelectedLayerIndex = i; *SelectedPrecompIndex = Layer->Block_Composition_Index; } } } static int32 LayerIterate_SelectionAct(project_state *State, memory *Memory, uint32 CompIndex, layer_transforms ExtraT, v2 Center, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, int SelectionCount, int SelectedLayerIndex, int SelectedPrecompIndex, bool32 BelowOnly) { int32 LayerIndex = -1; block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); sorted_comp_array *SortedCompStart = &SortedCompArray[CompIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); int LayerCount = SortedCompStart->LayerCount + SortedCompStart->FakeLayerCount; for (int i = LayerCount - 1; i >= 0; i--) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); Assert(Layer->Block_Composition_Index == CompIndex); int Width = 0, Height = 0; Layer_GetDimensions(Memory, Layer, &Width, &Height); if (Layer->IsPrecomp && (Layer->IsSelected & 0x03 || State->SelectionMode == 1)) { // only like 20% sure how this works... layer_transforms NewExtraT = Layer_GetTransforms(Layer); v2 NewCenter = T_CompPosToLayerPos(NewExtraT, Comp->Width, Comp->Height, Width, Height, Center.x, Center.y); NewExtraT.rotation = ExtraT.rotation - NewExtraT.rotation; NewExtraT.scale = ExtraT.scale / NewExtraT.scale; LayerIndex = LayerIterate_SelectionAct(State, Memory, Layer->Block_Source_Index, NewExtraT, NewCenter, SortedCompArray, SortedLayerArray, SelectionCount, SelectedLayerIndex, SelectedPrecompIndex, BelowOnly); if (LayerIndex == -2) { return -2; } else if (LayerIndex != -1) { Layer->IsSelected = 0x02; return LayerIndex; } } v2 Position = State->Interact_Transform.Position; real32 Rad = (ExtraT.rotation * (PI / 180)); v2 XAxis = Position.x * ExtraT.scale * V2(cos(Rad), sin(Rad)); v2 YAxis = Position.y * -ExtraT.scale * V2(sin(Rad), -cos(Rad)); Position = XAxis + YAxis; layer_transforms T = Layer_GetTransforms(Layer); v2 UV = T_CompUVToLayerUV(T, Comp->Width, Comp->Height, Width, Height, Center / V2(Comp->Width, Comp->Height)); if (UV.x <= 1.0f && UV.x >= 0.0f && UV.y <= 1.0f && UV.y >= 0.0f && !Layer->IsLocked) { if (!(Layer->IsSelected & 0x01)) { if (!BelowOnly && SelectionCount == 1) { if (i < SelectedLayerIndex || SelectedPrecompIndex != CompIndex) { LayerIndex = Index_Physical; break; } } else { LayerIndex = Index_Physical; break; } } else if (Layer->IsSelected & 0x01 && BelowOnly) { Layer->IsSelected = 0x00; State->Interact_Transform = {}; return -2; } } } return LayerIndex; } static int32 LayerIterate_TestSelection(project_state *State, memory *Memory, uint32 PrincipalIndex, layer_transforms ExtraT, v2 Center, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, bool32 BelowOnly) { int SelectionCount = 0; int SelectedLayerIndex = 0; int SelectedPrecompIndex = 0; LayerIterate_SelectionStatus(State, Memory, PrincipalIndex, ExtraT, Center, SortedCompArray, SortedLayerArray, &SelectionCount, &SelectedLayerIndex, &SelectedPrecompIndex); return LayerIterate_SelectionAct(State, Memory, PrincipalIndex, ExtraT, Center, SortedCompArray, SortedLayerArray, SelectionCount, SelectedLayerIndex, SelectedPrecompIndex, BelowOnly); } // Same loop as Render_UI and ImGui_Viewport_SelectedLayerUI except we're // finding the inverse transform-- i.e. transforming the layers locally rather // than transforming them in comp space. static void LayerIterate(project_state *State, memory *Memory, uint32 CompIndex, interact_transform Interact_Transform, layer_transforms ExtraT, v2 Center, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); sorted_comp_array *SortedCompStart = &SortedCompArray[CompIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); int LayerCount = SortedCompStart->LayerCount + SortedCompStart->FakeLayerCount; for (int i = 0; i < LayerCount; i++) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); int Width = 0, Height = 0; Layer_GetDimensions(Memory, Layer, &Width, &Height); if (Layer->IsPrecomp) { // only like 20% sure how this works... layer_transforms NewExtraT = Layer_GetTransforms(Layer); v2 NewCenter = T_CompPosToLayerPos(NewExtraT, Comp->Width, Comp->Height, Width, Height, Center.x, Center.y); NewExtraT.rotation = ExtraT.rotation - NewExtraT.rotation; NewExtraT.scale = ExtraT.scale / NewExtraT.scale; LayerIterate(State, Memory, Layer->Block_Source_Index, Interact_Transform, NewExtraT, NewCenter, SortedCompArray, SortedLayerArray); } if (Layer->IsSelected & 0x01) { #if DEBUG if (Layer->IsPrecomp) PostMsg(State, "DEBUG: Precomp transformed!"); #endif v2 Position = Interact_Transform.Position; real32 Rad = (ExtraT.rotation * (PI / 180)); v2 XAxis = Position.x * ExtraT.scale * V2(cos(Rad), sin(Rad)); v2 YAxis = Position.y * -ExtraT.scale * V2(sin(Rad), -cos(Rad)); Position = XAxis + YAxis; layer_transforms T = Layer_GetTransforms(Layer); History_Action_Swap(Memory, F_Layers, sizeof(Layer->x.CurrentValue), &Layer->x.CurrentValue); History_Action_Swap(Memory, F_Layers, sizeof(Layer->y.CurrentValue), &Layer->y.CurrentValue); History_Action_Swap(Memory, F_Layers, sizeof(Layer->scale.CurrentValue), &Layer->scale.CurrentValue); History_Action_Swap(Memory, F_Layers, sizeof(Layer->rotation.CurrentValue), &Layer->rotation.CurrentValue); Transform_ApplyLocal(Interact_Transform, Position, Center, &Layer->x.CurrentValue, &Layer->y.CurrentValue, &Layer->rotation.CurrentValue, &Layer->scale.CurrentValue); } } } static uint8 * Render_UI(project_data *File, project_state *State, memory *Memory, ui *UI, ImDrawList *draw_list, uint8 *PointBuffer, gl_viewport_data *RenderData, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, layer_transforms ExtraT, sorted_property_array *SortedPropertyStart, uint16 *SortedKeyframeArray, uint32 CompIndex, int32 Frame_Current) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); uint64 Size = Comp->Width * Comp->Height * Comp->BytesPerPixel; sorted_comp_array *SortedCompStart = &SortedCompArray[CompIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); Render_SortKeyframes(File, State, Memory, SortedCompStart, SortedLayerStart, SortedCompArray, SortedLayerArray, SortedPropertyStart, SortedKeyframeArray, Frame_Current); int LayerCount = SortedCompStart->LayerCount + SortedCompStart->FakeLayerCount; for (int i = 0; i < LayerCount; i++) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); int32 Frame_Start = Layer->Frame_Start; int32 Frame_End = Layer->Frame_End; int32 Frame_Offset = Layer->Frame_Offset; if (Layer->IsSelected & 0x01) Interact_Evaluate_Layer(Memory, State, Index_Physical, *SortedCompStart, SortedLayerStart, &Frame_Start, &Frame_End, &Frame_Offset); int32 Frame_Start_Abs = Frame_Start + Frame_Offset; int32 Frame_End_Abs = Frame_End + Frame_Offset; int FrameToSeek = State->Frame_Current - Frame_Offset; if (Frame_Start_Abs <= Frame_Current && Frame_End_Abs > Frame_Current && Layer->IsVisible) { layer_transforms T = Layer_GetTransforms(Layer); if (ExtraT.scale != 0) { T = Transform_Add(T, ExtraT, Comp->Width, Comp->Height); } T = Transform_TestInteracts(State, Layer, SortEntry, T); int Width = 0, Height = 0; Layer_GetDimensions(Memory, Layer, &Width, &Height); // quick test to see if layer is outside viewport if (CompIndex == File->PrincipalCompIndex) { int MaxDist = (Width > Height) ? Width : Height; v2 Pos = V2(T.x, T.y); v2 Min = Pos - V2(MaxDist / 2); v2 Max = Pos + V2(MaxDist / 2); ImVec2 ScreenMin = UI->CompPos + (IV2(Min) / ImVec2(Comp->Width, Comp->Height)) * UI->CompZoom; ImVec2 ScreenMax = UI->CompPos + (IV2(Max) / ImVec2(Comp->Width, Comp->Height)) * UI->CompZoom; if (ScreenMax.x < RenderData->ViewportMin.x || ScreenMin.x > RenderData->ViewportMax.x || ScreenMin.y > RenderData->ViewportMax.y || ScreenMax.y < RenderData->ViewportMin.y) continue; } if (Layer->IsShapeLayer) { void *Data = PointBuffer; shape_layer *Shape = &Layer->Shape; shape_options ShapeOpt = Layer->ShapeOpt; if (ShapeOpt.StrokeWidth == 0.0f) { if (ShapeOpt.Visibility == 0) ShapeOpt.Visibility = 1; if (ShapeOpt.Visibility == 2) continue; } bool32 IsConvex = 0; v2 Min = {}, Max = {}; uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, ShapeOpt, (nvg_point *)Data, State, T, Shape->Width, Shape->Height, Comp->Width, Comp->Height, 1, &Min, &Max, &IsConvex); PointBuffer += NumberOfVerts * sizeof(nvg_point); void *Data_Stroke = PointBuffer; void *Data_Fill = PointBuffer; uint32 StrokeCount = 0; int RenderFlags = 0; if (ShapeOpt.Visibility == 0 || ShapeOpt.Visibility == 2) { RenderFlags |= gl_renderflag_stroke; StrokeCount = NVG_ExpandStroke(Memory, NumberOfVerts, ShapeOpt.StrokeWidth, ShapeOpt.LineCapType, ShapeOpt.LineJoinType, Shape->IsClosed, (nvg_point *)Data, (real32 *)Data_Stroke, &IsConvex); PointBuffer += StrokeCount * sizeof(real32) * 4; Data_Fill = PointBuffer; } if (ShapeOpt.Visibility == 0 || ShapeOpt.Visibility == 1) { RenderFlags |= gl_renderflag_fill; NVG_ExpandFill(Memory, NumberOfVerts, (nvg_point *)Data, (real32 *)Data_Fill); PointBuffer += NumberOfVerts * sizeof(real32) * 4; } if (!IsConvex) RenderFlags |= gl_renderflag_convex; // zero to set the framebuffer to main gl_effect_layer TestL = {}; void *EffectBitmapAddress = NULL; gl_data *GL_Data = RenderData->LayerEntry[RenderData->LayerCount] = (gl_data *)PointBuffer; RenderData->LayerCount++; PointBuffer += sizeof(gl_data); *GL_Data = { 0, Data_Stroke, StrokeCount, ShapeOpt.StrokeCol, Data_Fill, NumberOfVerts, ShapeOpt.FillCol, T, Shape->Width, Shape->Height, NULL, Layer->BlendMode, RenderFlags }; } else if (Layer->IsPrecomp) { layer_transforms NewExtraT = Layer_GetTransforms(Layer); // TODO(fox): Add center point for proper scaling! NewExtraT = Transform_TestInteracts(State, Layer, SortEntry, NewExtraT); if (ExtraT.scale != 0) { NewExtraT = Transform_Add(NewExtraT, ExtraT, Comp->Width, Comp->Height); } gl_data *GL_Data = RenderData->LayerEntry[RenderData->LayerCount] = (gl_data *)PointBuffer; RenderData->LayerCount++; PointBuffer += sizeof(gl_data); GL_Data->Type = 1; GL_Data->T = NewExtraT; GL_Data->Width = Width; GL_Data->Height = Height; GL_Data->FillData = PointBuffer; real32 CompVerts[16] = { 0.f, 0.f, 0.0f, 0.0f, 0.f, (real32)Height, 0.0f, 1.0f, (real32)Width, (real32)Height, 1.0f, 1.0f, (real32)Width, 0.f, 1.0f, 0.0f }; for (int a = 0; a < 16; a++) { *(real32 *)PointBuffer = CompVerts[a]; PointBuffer += sizeof(real32); } GL_Data->FillCount = 4; PointBuffer = Render_UI(File, State, Memory, UI, draw_list, PointBuffer, RenderData, SortedCompArray, SortedLayerArray, NewExtraT, SortedPropertyStart, SortedKeyframeArray, Layer->Block_Source_Index, Frame_Current); GL_Data = RenderData->LayerEntry[RenderData->LayerCount] = (gl_data *)PointBuffer; RenderData->LayerCount++; PointBuffer += sizeof(gl_data); GL_Data->Type = 2; GL_Data->T = NewExtraT; GL_Data->Width = Width; GL_Data->Height = Height; GL_Data->FillData = PointBuffer; for (int a = 0; a < 16; a++) { *(real32 *)PointBuffer = CompVerts[a]; PointBuffer += sizeof(real32); } GL_Data->FillCount = 4; } else { // gl_data *GL_Data = RenderData->LayerEntry[RenderData->LayerCount] = (gl_data *)PointBuffer; // GL_Data->Type = 3; // RenderData->LayerCount++; gl_data *GL_Data = RenderData->LayerEntry[RenderData->LayerCount] = (gl_data *)PointBuffer; RenderData->LayerCount++; PointBuffer += sizeof(gl_data); void *BitmapAddress; block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); av_info *AV = NULL; if (Source->Type == source_type_file) { AV = AV_Retrieve(State, Memory, Layer->Block_Source_Index); if (!AV) { AV = (av_info *)Memory_Block_AllocateAddress(Memory, P_AVInfo); AV->Occupied = 1; AV->Block_Source_Index = Layer->Block_Source_Index; AV_Init(Source, AV, Memory); State->AVCount++; } } if (Source->Type == source_type_principal || Source->Type == source_type_principal_temp) { BitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0); } else { cache_entry *CacheEntry = Memory_Cache_Search(State, Memory, cache_entry_type_source, Layer->Block_Source_Index, FrameToSeek); BitmapAddress = Memory_Block_Bitmap_AddressAtIndex(Memory, CacheEntry->Block_StartIndex); if (!CacheEntry->IsCached) { AV_LoadVideoFrame(Memory, Source, AV, FrameToSeek, BitmapAddress); CacheEntry->IsCached = true; } } Assert(BitmapAddress); GL_Data->FillData = PointBuffer; GL_Data->FillCount = 4; int Blend = (Layer->BlendMode != blend_normal) ? gl_renderflag_blend : 0; v2 Min = V2(10000, 10000), Max = V2(-10000, -10000); if (Blend) { real32 CompVerts[24] = { 0.f, 0.f, 0.0f, 0.0f, -0.5f, 0.5f, 0.f, (real32)Height, 0.0f, 1.0f, -0.5f, 0.5f, (real32)Width, (real32)Height, 1.0f, 1.0f, -0.5f, 0.5f, (real32)Width, 0.f, 1.0f, 0.0f, -0.5f, 0.5f }; #if 1 real32 Rad = (T.rotation * (PI / 180)); v2 UIOffset = V2(RenderData->UIPos); v2 UIZoom = V2(RenderData->UIZoom); v2 ScreenDimensions = V2(RenderData->ViewportSize); v2 LayerDimensions = V2(Width, Height); v2 CompDimensions = V2(Comp->Width, Comp->Height); v2 Anchor = V2(T.ax, T.ay); v2 Pos = V2(T.x, T.y); real32 Scale = T.scale; v2 TexCoord = V2(0.5, 0.5); float Rada = -Rad;; v2 XRotation = V2(cos(Rada), sin(Rada)); v2 YRotation = V2(sin(Rada), -cos(Rada)); v2 XAxis = (TexCoord.x - Anchor.x) * XRotation; v2 YAxis = (TexCoord.y - Anchor.y) * YRotation; v2 CompUV = Anchor + (XAxis + YAxis); v2 ScreenPos = UIOffset + (CompUV * UIZoom); v2 ScreenUV = ScreenPos / ScreenDimensions; v2 ScreenPos2 = UIOffset + (TexCoord * UIZoom); v2 ScreenUV2 = ScreenPos / ScreenDimensions; for (int a = 0; a < 4; a++) { real32 *PointX = &CompVerts[(a*6)]; real32 *PointY = &CompVerts[(a*6)+1]; v2 Point = V2(*PointX, *PointY); v2 XRotation = V2(cos(Rad), sin(Rad)); v2 YRotation = V2(sin(Rad), -cos(Rad)); v2 XAxis = (Point.x - (Anchor.x * LayerDimensions.x)) * Scale * XRotation; v2 YAxis = (Point.y - (Anchor.y * LayerDimensions.y)) * -Scale * YRotation; v2 CompPoint = Pos + v2(XAxis + YAxis); v2 CompUV = CompPoint / CompDimensions; v2 ScreenPoint = UIOffset + (CompUV * UIZoom); v2 ScreenUV = ScreenPoint / ScreenDimensions; *PointX = ScreenUV.x; *PointY = ScreenUV.y; // if (ScreenPoint.x > Max.x) // Max.x = ScreenPoint.x; // if (ScreenPoint.x < Min.x) // Min.x = ScreenPoint.x; // if (ScreenPoint.y > Max.y) // Max.y = ScreenPoint.y; // if (ScreenPoint.y < Min.y) // Min.y = ScreenPoint.y; } #if 0 Rad = -Rad; for (int a = 0; a < 4; a++) { real32 *PointX = &CompVerts[(a*6)+2]; real32 *PointY = &CompVerts[(a*6)+3]; v2 Point = V2(*PointX, *PointY); v2 XRotation = V2(cos(Rad), sin(Rad)); v2 YRotation = V2(sin(Rad), -cos(Rad)); v2 XAxis = (Point.x - 0.5) * XRotation; v2 YAxis = (Point.y - 0.5) * YRotation; v2 CompPoint = V2(0.5, 0.5) + v2(XAxis + YAxis); *PointX = CompPoint.x; *PointY = CompPoint.y; } #endif #endif for (int a = 0; a < 24; a++) { *(real32 *)PointBuffer = CompVerts[a]; PointBuffer += sizeof(real32); } } else { real32 CompVerts[16] = { 0.f, 0.f, 0.0f, 0.0f, 0.f, (real32)Height, 0.0f, 1.0f, (real32)Width, (real32)Height, 1.0f, 1.0f, (real32)Width, 0.f, 1.0f, 0.0f }; for (int a = 0; a < 16; a++) { *(real32 *)PointBuffer = CompVerts[a]; PointBuffer += sizeof(real32); } } GL_Data->Type = 0; GL_Data->Width = Width; GL_Data->Height = Height; if (Blend) { GL_Data->BlendMin = Min; GL_Data->BlendMax = Max; } GL_Data->BlendMode = Layer->BlendMode; GL_Data->BitmapData = BitmapAddress; GL_Data->T = T; GL_Data->RenderMode = gl_renderflag_convex | gl_renderflag_fill | gl_renderflag_texture | Blend; } } } Assert((PointBuffer - (uint8 *)Memory->Slot[B_PointData].Address) < Memory->Slot[B_PointData].Size); return PointBuffer; } static void * Render_Comp(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID, ImGuiIO io, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, sorted_property_array *SortedPropertyStart, uint16 *SortedKeyframeArray, uint32 CompIndex, int32 Frame_Current) { block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); cache_entry *Entry_Main = Memory_Cache_Search(State, Memory, cache_entry_type_comp, CompIndex, Frame_Current); void *CompBuffer = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry_Main->Block_StartIndex); uint64 Size = Comp->Width * Comp->Height * Comp->BytesPerPixel; sorted_comp_array *SortedCompStart = &SortedCompArray[CompIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); // NOTE(fox): We have to re-evalute keyframes even when the frame is // cached, since we aren't caching the values and the UI needs to have // them. Make sure that the cache is always purged if keyframes are // updated. Render_SortKeyframes(File, State, Memory, SortedCompStart, SortedLayerStart, SortedCompArray, SortedLayerArray, SortedPropertyStart, SortedKeyframeArray, Frame_Current); if (Entry_Main->IsCached) return CompBuffer; Arbitrary_Zero((uint8 *)CompBuffer, Size); // uint64 Comp_TimeStart = GetCPUTime(); for (int i = 0; i < SortedCompStart->LayerCount; i++) { sorted_layer_array SortEntry = SortedLayerStart[i]; uint32 Index_Physical = SortEntry.Block_Layer_Index; block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); int32 Frame_Start = Layer->Frame_Start; int32 Frame_End = Layer->Frame_End; int32 Frame_Offset = Layer->Frame_Offset; if (Layer->IsSelected & 0x01) Interact_Evaluate_Layer(Memory, State, Index_Physical, *SortedCompStart, SortedLayerStart, &Frame_Start, &Frame_End, &Frame_Offset); int32 Frame_Start_Abs = Frame_Start + Frame_Offset; int32 Frame_End_Abs = Frame_End + Frame_Offset; int FrameToSeek = State->Frame_Current - Frame_Offset; if (Frame_Start_Abs <= Frame_Current && Frame_End_Abs > Frame_Current && Layer->IsVisible) { layer_bitmap_state *BitmapState = &State->Render.Bitmap[Index_Physical]; void *BitmapAddress = NULL; void *RenderAddress = NULL; // result of masking, effects, and intermediate paint stroke int Width = 0, Height = 0, BytesPerPixel = 0; if (Layer->IsPrecomp) { block_composition *Precomp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, Layer->Block_Source_Index); BitmapAddress = Render_Comp(File, State, Memory, Sorted, UI, window, textureID, io, SortedCompArray, SortedLayerArray, SortedPropertyStart, SortedKeyframeArray, Layer->Block_Source_Index, (int32)Layer->time.CurrentValue); Width = Precomp->Width; Height = Precomp->Height; BytesPerPixel = Precomp->BytesPerPixel; } else if (Layer->IsShapeLayer) { BytesPerPixel = 4; } else { block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); av_info *AV = NULL; if (Source->Type == source_type_file) { AV = AV_Retrieve(State, Memory, Layer->Block_Source_Index); if (!AV) { AV = (av_info *)Memory_Block_AllocateAddress(Memory, P_AVInfo); AV->Occupied = 1; AV->Block_Source_Index = Layer->Block_Source_Index; AV_Init(Source, AV, Memory); State->AVCount++; } } if (Source->HasAudio) { Assert(Source->Type == source_type_file); if (State->AudioLayerIndex == -1) State->AudioLayerIndex = Index_Physical; if (!Source->HasVideo) continue; } // if ((State->PreviewSource != -1) && Layer->IsSelected) { // Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, State->PreviewSource); // } else { // } if (Source->Type == source_type_principal || Source->Type == source_type_principal_temp) { BitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0); } else { cache_entry *CacheEntry = Memory_Cache_Search(State, Memory, cache_entry_type_source, Layer->Block_Source_Index, FrameToSeek); BitmapAddress = Memory_Block_Bitmap_AddressAtIndex(Memory, CacheEntry->Block_StartIndex); if (!CacheEntry->IsCached) { AV_LoadVideoFrame(Memory, Source, AV, FrameToSeek, BitmapAddress); CacheEntry->IsCached = true; } /* cache_entry *Entry = Memory_Cache_Search(State, Memory, cache_entry_type_source, Layer->Block_Source_Index, 0); if (!Entry->IsCached) { uint64 Src_TimeStart = GetCPUTime(); 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); Memory_Copy((uint8 *)Source_Address, (uint8 *)temp, Size); Bitmap_SRGBToLinear(Source_Address, Source->Width, Source->Height, Source->BytesPerPixel, 1); stbi_image_free(temp); BitmapState->ToUpdate = false; BitmapState->CurrentFrame = 0; Entry->CycleTime = GetCPUTime() - Src_TimeStart; Layer->x.CurrentValue = Comp->Width/2; Layer->y.CurrentValue = Comp->Height/2; Entry->IsCached = true; } BitmapAddress = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry->Block_StartIndex); */ } Width = Source->Width; Height = Source->Height; BytesPerPixel = Source->BytesPerPixel; } uint64 ScratchSize = Width * Height * BytesPerPixel; RenderAddress = Memory_PushScratch(Memory, ScratchSize); if (BitmapAddress) { Memory_Copy((uint8 *)RenderAddress, (uint8 *)BitmapAddress, ScratchSize); } if (State->Interact_Active == interact_type_brush && State->Brush.LayerToPaint_Index == Index_Physical) { // TODO(fox): Do all these extra precomputes really make a difference in the renderer? rectangle RenderRegion = { 0, 0, Width, Height }; direct_info Info = { (real32)Width, (real32)Height, (real32)BytesPerPixel, (real32)Width * BytesPerPixel, Bitmap_ByteInfo(BytesPerPixel), UI->Color.a, blend_normal, RenderRegion, State->Brush.TransientBitmap, 0, 0}; Render_Main(File, State, Memory, Sorted, UI, window, textureID, (void *)&Info, RenderAddress, render_type_notransform, State->Brush.CacheBounds); } if (Layer->IsShapeLayer) { Layer_UpdateMasksEffects(State, Layer, Memory, RenderAddress, Comp->Width, Comp->Height, Comp->BytesPerPixel); Width = Layer->Shape.Width; Height = Layer->Shape.Height; rectangle RenderRegion = { 0, 0, Comp->Width, Comp->Height }; direct_info Info = { (real32)Comp->Width, (real32)Comp->Height, (real32)Comp->BytesPerPixel, (real32)Comp->Width * Comp->BytesPerPixel, Bitmap_ByteInfo(Comp->BytesPerPixel), UI->Color.a, blend_normal, RenderRegion, RenderAddress, 0, 0}; Render_Main(File, State, Memory, Sorted, UI, window, textureID, (void *)&Info, CompBuffer, render_type_notransform, RenderRegion); } else { Assert(Width && Width <= 2048); Assert(Height && Height <= 2048); transform_info T = Transform_Calculate(State, Memory, File, Layer, Comp, Width, Height, BytesPerPixel); T.SourceBuffer = RenderAddress; rectangle RenderRegion = {0, 0, Comp->Width, Comp->Height}; Render_Main(File, State, Memory, Sorted, UI, window, textureID, (void *)&T, CompBuffer, render_type_main, RenderRegion); } Memory_PopScratch(Memory, ScratchSize); // if (Layer->Block_Effect_Count) { // || Layer->Block_Bezier_Count) { // Layer_UpdateMasksEffects(State, Layer, Memory, RenderAddress, Width, Height, BytesPerPixel); // } } } // Entry_Main->CycleTime = GetCPUTime() - Comp_TimeStart; Entry_Main->IsCached = true; if (CompIndex == File->PrincipalCompIndex && State->LastCachedFrame != State->Frame_Current) { State->CachedFrameCount++; State->LastCachedFrame = State->Frame_Current; } return CompBuffer; } static void Render_Paint(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID, ImGuiIO io, v2 LayerPos) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, State->Brush.LayerToPaint_Index); layer_transforms T_Layer = Layer_GetTransforms(Layer); block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); void *SourceBitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0); brush_info B; Brush_Info(&B, &State->Brush, Source, SourceBitmapAddress, LayerPos, UI->Color); if (State->Brush.Size >= 128) { Render_Main(File, State, Memory, Sorted, UI, window, textureID, (void *)&B, State->Brush.TransientBitmap, render_type_brush, B.LayerBounds); } else { PaintTest(B, State->Brush.TransientBitmap, B.LayerBounds); } } static void Render_Blit(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID, ImGuiIO io, v2 LayerPos) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, State->Brush.LayerToPaint_Index); layer_transforms T_Layer = Layer_GetTransforms(Layer); block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); void *SourceBitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0); rectangle RenderRegion = { 0, 0, Source->Width, Source->Height }; direct_info Info = { (real32)Source->Width, (real32)Source->Height, (real32)Source->BytesPerPixel, (real32)Source->Width * Source->BytesPerPixel, Bitmap_ByteInfo(Source->BytesPerPixel), UI->Color.a, blend_normal, RenderRegion, State->Brush.TransientBitmap, 1, 0}; Render_Main(File, State, Memory, Sorted, UI, window, textureID, (void *)&Info, SourceBitmapAddress, render_type_notransform_swap, State->Brush.CacheBounds); uint64 BitmapSize = Source->Width * Source->Height * Source->BytesPerPixel; History_Entry_Commit(Memory, "Paintbrush stroke"); History_Action_BitmapPaint(Memory, BitmapSize, SourceBitmapAddress, State->Brush.TransientBitmap, Source->BytesPerPixel); History_Entry_End(Memory); State->Brush.LayerToPaint_Index = -1; State->Brush.PrevPos = V2(-4000, -4000); State->Brush.CacheBounds = { 99999, 99999, -99999, -99999 }; State->Interact_Active = interact_type_none; State->Interact_Modifier = 0; State->UpdateFrame = true; } static void Main_Renderer(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID, ImGuiIO io) { block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); void *MainCompBuffer = Render_Comp(File, State, Memory, Sorted, UI, window, textureID, io, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyStart, Sorted.PropertyArray, File->PrincipalCompIndex, State->Frame_Current); // Bitmap_SRGBToLinear(MainCompBuffer, MainComp->Width, MainComp->Height, MainComp->BytesPerPixel, 0); glBindTexture(GL_TEXTURE_2D, textureID); int ByteFlag2 = (MainComp->BytesPerPixel == 4) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT; if (State->FirstFrame) { 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); // TODO(fox): garbage collect AV state! State->UpdateFrame = false; State->UpdateKeyframes = false; } int main(int argc, char *argv[]) { global_memory GlobalMemory = {}; GlobalMemory.Size = ((uint64)1 * 1024 * 1024 * 1024); GlobalMemory.CurrentPosition = 0; #if WINDOWS GlobalMemory.Address = VirtualAlloc(0, GlobalMemory.Size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); #else GlobalMemory.Address = mmap(0, GlobalMemory.Size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); #endif // 1 meg per block BitmapBlockSize = 1024 * 1024; memory Memory = {}; Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, P_AVInfo, "FFmpeg state", sizeof(av_info)); Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, P_UndoBuffer, "Undo buffer"); Memory_InitTable(&GlobalMemory, &Memory, sizeof(project_data), F_File, "File", sizeof(project_data)); Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, F_Precomps, "Precomps", sizeof(block_composition)); Memory_InitTable(&GlobalMemory, &Memory, 2 * 1024 * 1024, F_Layers, "Layers", sizeof(block_layer)); Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, F_Sources, "Sources", sizeof(block_source)); Memory_InitTable(&GlobalMemory, &Memory, 2 * 1024 * 1024, F_Effects, "Properties", sizeof(block_effect)); Memory_InitTable(&GlobalMemory, &Memory, 2 * 1024 * 1024, F_Properties, "Properties", sizeof(property_channel)); Memory_InitTable(&GlobalMemory, &Memory, 4 * 1024 * 1024, F_Bezier, "Bezier paths (keyframes, masks)", sizeof(block_bezier)); Memory_InitTable(&GlobalMemory, &Memory, 4 * 1024 * 1024, F_Strings, "Strings", sizeof(block_string)); Memory_InitTable(&GlobalMemory, &Memory, (uint64)100 * 1024 * 1024, F_PrincipalBitmaps, "Principal bitmap data", BitmapBlockSize); Memory_InitTable(&GlobalMemory, &Memory, (uint64)5 * 1024 * 1024, B_Thumbnails, "Thumbnails"); Memory_InitTable(&GlobalMemory, &Memory, (uint64)5 * 1024 * 1024, B_PointData, "Point data"); Memory_InitTable(&GlobalMemory, &Memory, (uint64)128 * 1024 * 1024, B_ScratchSpace, "Scratch"); Memory_InitTable(&GlobalMemory, &Memory, (uint64)700 * 1024 * 1024, B_CachedBitmaps, "Cached bitmap buffer"); uint8 *Test = (uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.Slot[B_ScratchSpace].Size + 2; *Test = 30; #if ARM InstructionMode = instruction_mode_neon; #else // if (SDL_HasSSE2()) { // InstructionMode = instruction_mode_sse; // } if (SDL_HasAVX2()) { InstructionMode = instruction_mode_avx; } #endif int SamplesPerSecond = 48000; int BytesPerSample = sizeof(int16) * 2; project_state State_ = {}; project_state *State = &State_; project_data *File = (project_data *)Memory_Block_AllocateAddress(&Memory, F_File); *File = {}; File->Occupied = 1; // NOTE(fox): Right now I'm just gonna throw all dynamic allocs that can't // be simplified to the push/pop model here; will worry about how to best // use RAM later. State->Brush.PaintBuffer = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); uint64 ScratchPaintSize = 2048*2048*4; Memory.ScratchPos += ScratchPaintSize; State->Brush.TransientBitmap = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); ScratchPaintSize = 2048*2048*4; Memory.ScratchPos += ScratchPaintSize; State->Dump1 = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); Memory.ScratchPos += ScratchPaintSize; State->Dump2 = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); uint32 AudioBufferSize = 48000 * sizeof(uint16) * 60; void *AudioData = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); Memory.ScratchPos += AudioBufferSize; State->ClipboardBuffer = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); State->ClipboardSize = 1024*1024; Memory.ScratchPos += State->ClipboardSize; State->Test = ImDrawListSplitter(); block_composition *MainComp = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps); MainComp->Width = 1280; MainComp->Height = 720; MainComp->FPS = 60; MainComp->BytesPerPixel = 4; MainComp->Frame_Count = 80; MainComp->Frame_End = 79; MainComp->Occupied = 1; MainComp->Name_String_Index = String_AddToFile(&Memory, "Main comp", 0); File->Comp_Count = 1; File->UI.BGColor= V4(0.1, 0.1, 0.1, 0.0); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); SDL_AudioSpec Audio = {0}; //AUDIO Audio.freq = 48000; Audio.format = AUDIO_S16LSB; Audio.channels = 2; Audio.samples = SamplesPerSecond * BytesPerSample / 60; SDL_OpenAudio(&Audio, 0); Assert(Audio.format == AUDIO_S16LSB); SDL_PauseAudio(1); Semaphore = SDL_CreateSemaphore(0); int Index[7]; for (int i = 0; i < 7; i++) { Index[i] = i; Thread[i] = SDL_CreateThread(TestThread, "thread", (void *)&Index[i]); } // 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"; SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); #elif defined(__APPLE__) // GL 3.2 Core + GLSL 150 const char* glsl_version = "#version 150"; SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); #else // GL 3.0 + GLSL 130 const char* glsl_version = "#version 130"; SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); #endif SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); // SDL_RenderSetScale(renderer, 2, 2) #if DEBUG #if ARM uint32 ScreenSize[2] = {(uint32)(2560/1.2), (uint32)(1600/1.2)}; #elif WINDOWS real32 ScreenSize[2] = {3840/1.6, 2160/1.6}; #else real32 ScreenSize[2] = {3840/1.2, 2160/1.2}; #endif #else real32 ScreenSize[2]; SDL_DisplayMode current; int windowtest = SDL_GetCurrentDisplayMode(0, ¤t); if (windowtest == 0) { ScreenSize[0] = current.w; ScreenSize[1] = current.h; } else { ScreenSize[0] = 1920; ScreenSize[1] = 1080; } #endif SDL_Window* window = SDL_CreateWindow("Event Tester", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, ScreenSize[0], ScreenSize[1] - 80, window_flags); SDL_GLContext gl_context = SDL_GL_CreateContext(window); SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SetSwapInterval(1); // Enable vsync if (!gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress)) { printf("Failed to initialize GLAD"); return -1; } gladInstallGLDebug(); GL_InitDefaultShader(&DefaultVertexShader, DefaultVertexShaderSource, &DefaultFragmentShader, DefaultFragmentShaderSource, &DefaultShaderProgram); GL_InitDefaultShader(&BlendVertexShader, BlendVertexShaderSource, &BlendFragmentShader, BlendFragmentShaderSource, &BlendShaderProgram); GL_InitDefaultVerts(); Effect_InitEntries(State); SDL_GL_MakeCurrent(window, gl_context); SDL_Event Event; IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_NavEnableSetMousePos; (void)io; // NOTE(fox): Window construction is done in MyWindowSetup(). #if DEBUG #else io.IniFilename = NULL; #endif // ImGui::LoadIniSettingsFromMemory(ImGuiPrefs); // ImGui::SaveIniSettingsToDisk("imgui.ini"); ImGui::StyleColorsDark(); ImGui::PushStyleColor(ImGuiCol_ModalWindowDimBg, ImGui::ColorConvertFloat4ToU32(ImVec4(0.0f, 0.0f, 0.0f, 0.1f))); ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL3_Init(glsl_version); // ImGuiPlatformIO &plat_io = ImGui::GetPlatformIO(); // printf("Scale: %.2f * 96 dpi", plat_io.Monitors[0].DpiScale); // io.FontGlobalScale = plat_io.Monitors[0].DpiScale; // printf("global: %.2f", io.FontGlobalScale); GLuint textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); #if DEBUG glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); #else glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); #endif ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); Brush_CalcBitmapAlphaFromSize(&Memory, &State->Brush, 4); State_BindBrushTexture(&Memory, &State->Brush, 4); #if STABLE curl_global_init(CURL_GLOBAL_ALL); curl_state MainHandle = {}; curl_state ProgHandle = {}; #endif #if DEBUG #if 1 sprintf(State->DummyName, "test"); if (File_Open(File, State, &Memory, State->DummyName)) { State->UpdateFrame = true; } // File->PrincipalCompIndex = 1; #else // uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/yu.webm"); // block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, SourceIndex); // Source->IsSelected = true; // Source_UICreateButton(File, State, &Memory); /* { block_layer *Layer = Layer_Init(File, &Memory); Layer->IsShapeLayer = true; Layer->Shape.Point_Count = 2; Layer->Shape.Block_Bezier_Index[0] = Memory_Block_AllocateNew(&Memory, F_Bezier); block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(&Memory, F_Bezier, Layer->Shape.Block_Bezier_Index[0], 0); Bezier->Occupied = true; Bezier->Point[0] = { 1, { V2(001, 001), V2(000, 000), V2(000, 000) }, interpolation_type_linear, 0 }; Bezier->Point[1] = { 1, { V2(300, 300), V2(-150, 000), V2(150, 000) }, interpolation_type_bezier, 0 }; // Bezier->Point[0] = { 1, { V2(200, 250), V2(000, 000), V2(000, 000) }, interpolation_type_linear, 0 }; // Bezier->Point[1] = { 1, { V2(300, 300), V2(-150, 000), V2(150, 000) }, interpolation_type_linear, 0 }; // Bezier->Point[2] = { 1, { V2(400, 250), V2(-150, 000), V2(150, 000) }, interpolation_type_linear, 0 }; Layer->x.CurrentValue = MainComp->Width/2; Layer->y.CurrentValue = MainComp->Height/2; // Layer->ax.CurrentValue = 0; // Layer->ay.CurrentValue = 0; Layer->Vertical_Offset = 10; Layer->Frame_Start = MainComp->Frame_Start; Layer->Frame_End = MainComp->Frame_End; } { block_layer *Layer = Layer_Init(File, &Memory); Layer->IsShapeLayer = true; Layer->Shape.Point_Count = 2; Layer->Shape.Block_Bezier_Index[0] = Memory_Block_AllocateNew(&Memory, F_Bezier); block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(&Memory, F_Bezier, Layer->Shape.Block_Bezier_Index[0], 0); Bezier->Occupied = true; Bezier->Point[0] = { 1, { V2(00, 000), V2(000, 000), V2(000, 000) }, interpolation_type_linear, 0 }; Bezier->Point[1] = { 1, { V2(500, 300), V2(-150, 000), V2(150, 000) }, interpolation_type_bezier, 0 }; // Bezier->Point[0] = { 1, { V2(200, 250), V2(000, 000), V2(000, 000) }, interpolation_type_linear, 0 }; // Bezier->Point[1] = { 1, { V2(300, 300), V2(-150, 000), V2(150, 000) }, interpolation_type_linear, 0 }; // Bezier->Point[2] = { 1, { V2(400, 250), V2(-150, 000), V2(150, 000) }, interpolation_type_linear, 0 }; Layer->x.CurrentValue = MainComp->Width/2; Layer->y.CurrentValue = MainComp->Height/2; // Layer->ax.CurrentValue = 0; // Layer->ay.CurrentValue = 0; Layer->Vertical_Offset = 11; Layer->IsSelected = true; State->MostRecentlySelectedLayer = 1; Layer->Frame_Start = MainComp->Frame_Start; Layer->Frame_End = MainComp->Frame_End; } */ #endif #endif while (State->IsRunning) { uint64 StartTime = GetCPUTime(); uint64 PerfFrequency = SDL_GetPerformanceFrequency(); uint64 PerfStart = SDL_GetPerformanceCounter(); #if STABLE if (State->CurlActive) { Curl_Main(File, State, &Memory, &MainHandle, &ProgHandle); } #endif // NOTE(fox): These commands affect sorting, and should not be executed // in any UI or the renderer. // TODO(fox): Even though they affect sorting there are some of them // that _need_ sort info in order to be able to be wrapped in one // HistoryEntry, so we can either create a new sort info in place or // execute the command right before the sort info used by the // UI/renderer is popped, and I'm choosing the latter. int State_ExecuteAtEnd = 0; if (State->HotkeyInput) { switch (State->HotkeyInput) { case hotkey_none: { Assert(0); } break; // case hotkey_effect_indexup: // { // History_Entry_Commit(Memory, "Swap effect order"); // History_Action_Swap(Memory, F_Effects, sizeof(Effect->Index), &Effect->Index); // Effect->Index += 1; // uint16 NextEffectIdx = Layer->Block_Effect_Index[h - AmountOf(Layer->Property) + 1]; // block_effect *NextEffect = (block_effect *)Memory_Block_AddressAtIndex(Memory, F_Effects, NextEffectIdx, 0); // History_Action_Swap(Memory, F_Effects, sizeof(NextEffect->Index), &NextEffect->Index); // NextEffect->Index -= 1; // History_Entry_End(Memory); // } break; case hotkey_newlayer_paint: { Project_PaintLayer_New(File, State, &Memory); } break; case hotkey_newlayer_shape: { State_ExecuteAtEnd = 1; } break; case hotkey_newlayer_source: { Source_UICreateButton(File, State, &Memory); State->UpdateKeyframes = true; } break; case hotkey_duplicatelayer: { State->Interact_Active = interact_type_viewport_duplicate; sorted_file Sorted = File_Sort_Push(File, State, &Memory); History_Entry_Commit(&Memory, "Duplicate layers"); Project_Layer_Duplicate(File, State, &Memory, Sorted.CompArray, Sorted.LayerArray, V2(0, 0), 0); if (!io.KeyCtrl) { interact_transform Interact = State->Interact_Transform_Prev; v2 BoxLength = Interact.Max - Interact.Min; v2 Center = Interact.Max - (BoxLength/2); if (Interact.RadianOffset != 0.0f) { v2 LocalCenter = Interact.NewCenter; real32 Rad = Interact.RadianOffset; real32 Point0X = Center.x - Interact.OGCenter.x; real32 Point0Y = Center.y - Interact.OGCenter.y; v2 XAxis = (Point0X * 1.0f)*V2(cos(Rad), sin(Rad)); v2 YAxis = (Point0Y * 1.0f)*V2(sin(Rad), -cos(Rad)); Center = Interact.OGCenter + XAxis + YAxis; } layer_transforms T = {}; T.scale = 1.0f; LayerIterate(State, &Memory, File->PrincipalCompIndex, Interact, T, Center, Sorted.CompArray, Sorted.LayerArray); } State->Interact_Transform = {}; History_Entry_End(&Memory); File_Sort_Pop(&Memory, Sorted.Layer_SortSize, Sorted.Property_SortSize, Sorted.Source_SortSize); State->Interact_Active = interact_type_none; } break; case hotkey_deletelayer: { Project_Layer_Delete(File, State, &Memory); } break; case hotkey_undo: { History_Undo(&Memory); Memory_Cache_Purge(File, State, &Memory); State->Interact_Transform = {}; State->UpdateFrame = true; State->UpdateKeyframes = true; } break; case hotkey_redo: { History_Redo(&Memory); Memory_Cache_Purge(File, State, &Memory); State->Interact_Transform = {}; State->UpdateFrame = true; State->UpdateKeyframes = true; } break; default: { Assert(0); } break; } State->HotkeyInput = hotkey_none; } sorted_file Sorted = File_Sort_Push(File, State, &Memory); if (State->FirstFrame) State->UpdateScreen = 2; // State->UpdateScreen = 1; // TODO(fox): Do the same thing with the timeline and viewport to // reduce wasted rendering further; for now I am at least pausing all // UI when no inputs happen. uint64 InputStart = GetCPUTime(); State->UpdateScreen += Main_InputTest(File, State, &Memory, Sorted, &File->UI, window, textureID); uint64 InputTime = GetCPUTime() - InputStart; if (State->IsPlaying) { block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(&Memory, F_Precomps, File->PrincipalCompIndex); Playhead_Increment(&State->Frame_Current, MainComp->Frame_Start, MainComp->Frame_End, 1); State->UpdateFrame = true; State->Interact_Transform = {}; State->UpdateKeyframes = true; State->UpdateScreen += 1; } bool32 FullyCached = (State->CachedFrameCount == (MainComp->Frame_End - MainComp->Frame_Start + 1)); if ((State->AudioLayerIndex != -1) && FullyCached) { Assert(0); block_layer *AudioLayer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, State->AudioLayerIndex); av_info *AV = AV_Retrieve(State, &Memory, AudioLayer->Block_Source_Index); int32 LayerPos = AudioLayer->Frame_Start; if (State->Frame_Current >= LayerPos) { if (State->Frame_Current == LayerPos) { SDL_ClearQueuedAudio(1); AV_SeekAudio(AV, MainComp->FPS, 0); } if (State->HotFramePerf == 1) { SDL_ClearQueuedAudio(1); int32 FrameToSeek = State->Frame_Current - LayerPos; if (FrameToSeek > -1) AV_SeekAudio(AV, MainComp->FPS, FrameToSeek); } uint32 QueuedAudioSize = SDL_GetQueuedAudioSize(1); int TargetAudioSize = SamplesPerSecond * BytesPerSample / 2; if (QueuedAudioSize < (TargetAudioSize / 2)) { int BytesToWrite = TargetAudioSize - QueuedAudioSize; printf("%i bytes in queue.\n", QueuedAudioSize); int BytePlayhead = 0; int32 InitialFrameToSeek = State->Frame_Current - LayerPos; while (BytePlayhead < BytesToWrite) { uint8 *Data = (uint8 *)AudioData + BytePlayhead; int SampleCount = AV_AudioTest(AV, Data, AudioBufferSize - BytePlayhead, MainComp->FPS, InitialFrameToSeek); int Size = SampleCount * BytesPerSample; BytePlayhead += Size; } SDL_QueueAudio(1, AudioData, BytePlayhead); printf("Queued %i bytes.\n", BytePlayhead); } } else if (SDL_GetQueuedAudioSize(1)) { SDL_ClearQueuedAudio(1); } } #if 0 if (State->UpdateFrame) { // Default queue item type simply calls the renderer on the current // frame, so no additional info is needed State->Queue.CurrentIdx++; } for (int i = 0; i < State->Queue.CurrentIdx; i++) { render_queue_item Item = State->Queue.Item[i]; State->Queue.Playhead = i; if (Item.Type == 0) { if (State->Interact_Active) { Memory_Cache_Purge(File, State, &Memory, State->Frame_Current); } // Assert(pp != State->Frame_Current); // pp = State->Frame_Current; Main_Renderer(File, State, &Memory, Sorted, &File->UI, window, textureID, io); } else if (Item.Type == 1) { Assert(State->Interact_Active == interact_type_brush); Render_Paint(File, State, &Memory, Sorted, &File->UI, window, textureID, io, Item.Pos); } else if (Item.Type == 2) { Assert(State->Interact_Active == interact_type_brush); Render_Blit(File, State, &Memory, Sorted, &File->UI, window, textureID, io, Item.Pos); } else { Assert(0); } } State->Queue.CurrentIdx = 0; State->Queue.Playhead = 0; Arbitrary_Zero((uint8 *)State->Queue.Item, sizeof(State->Queue.Item)); #endif if (State_ExecuteAtEnd) { if (State_ExecuteAtEnd == 1) { Project_ShapeLayer_New(File, State, &Memory); } State->UpdateFrame = true; } File_Sort_Pop(&Memory, Sorted.Layer_SortSize, Sorted.Property_SortSize, Sorted.Source_SortSize); // NOTE(fox): We could theoretically allow scratch to persist between // frames, but we'd have to make sure the pop order stays the same in all scenarios. Assert(Debug.ScratchState == 0); uint64 RenderStart = GetCPUTime(); bool32 UpdateScreen = 0; if (State->IsPlaying && State->HotFramePerf > 1 && FullyCached) { uint64 RenderTime = SDL_GetPerformanceCounter() - State->HotFramePerf; real64 FrameMS = (1000.0f * (real64)RenderTime) / (real64)PerfFrequency; real64 TargetMS = (1000.0f / MainComp->FPS); if (TargetMS > FrameMS) SDL_Delay((uint64)(TargetMS - FrameMS)); Main_RenderUI(io, clear_color, window); State->HotFramePerf = 1; } else { if (State->UpdateScreen) { Main_RenderUI(io, clear_color, window); State->UpdateScreen--; UpdateScreen = 1; } } uint64 RenderTime = GetCPUTime() - RenderStart; if (State->HotFramePerf == 1) { State->HotFramePerf = SDL_GetPerformanceCounter(); } if (State->Initializing) State->Initializing--; if (State->FirstFrame) State->FirstFrame = false; uint64 PerfEnd = SDL_GetPerformanceCounter(); uint64 PerfTime = PerfEnd - PerfStart; real64 FrameMS = (1000.0f * (real64)PerfTime) / (real64)PerfFrequency; real64 FPS = PerfFrequency / PerfTime; if (!UpdateScreen) { real64 TargetMS = (1000.0f / 60); if (TargetMS > FrameMS) SDL_Delay((uint64)(TargetMS - FrameMS)); } uint64 TotalTime = GetCPUTime() - StartTime; // printf("TOTAL: %.2lu, (%.2f ms) - INPUTS: %.2lu - RENDERING: %.2lu\n", PerfTime, FrameMS, InputTime, RenderTime); #if DEBUG // printf("TOTAL: %.2lu (%.2f ms) - INPUTS: %.2lu (%.2f) - RENDERING: %.2lu (%.2f)\n", TotalTime, FrameMS, InputTime, (real64)InputTime / TotalTime, RenderTime, (real64)RenderTime / TotalTime); #endif // printf("TOTAL: %.2lu, (%.2f ms) - RENDERING: %.2lu\n", PerfTime, FrameMS, PerfTime); } for (int i = 0; i < 7; i++) { SDL_DetachThread(Thread[i]); } ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); SDL_Quit(); return 0; }