From e4acc91f56d9d88b75f60a23bd9662804d7c54b7 Mon Sep 17 00:00:00 2001 From: Fox Caminiti Date: Mon, 23 Jan 2023 23:57:37 -0500 Subject: selection upgrade wip --- src/bezier.cpp | 3 +- src/createcalls.cpp | 59 +++- src/imgui_helper_internal.cpp | 16 +- src/imgui_ui.cpp | 20 ++ src/imgui_ui_properties.cpp | 2 +- src/imgui_ui_viewport.cpp | 556 ++++++++++++++++++++++++++++++++--- src/include/all.h | 6 +- src/include/imgui_internal_widgets.h | 3 +- src/include/keybinds.h | 3 + src/include/main.h | 21 +- src/include/memory.h | 2 - src/layer.cpp | 12 +- src/main.cpp | 26 +- src/nanovg.cpp | 4 +- src/prenderer.cpp | 33 ++- 15 files changed, 683 insertions(+), 83 deletions(-) diff --git a/src/bezier.cpp b/src/bezier.cpp index 5916c42..dd3974d 100644 --- a/src/bezier.cpp +++ b/src/bezier.cpp @@ -44,9 +44,8 @@ Bezier_SolveYForX(v2 Point_P0, v2 Point_P1, v2 Point_P2, v2 Point_P3, real32 Tar static uint32 Bezier_Shape_Sort(memory *Memory, shape_layer *Shape, bezier_point *PointData, project_state *State, layer_transforms T, int Width, int Height, - int CompWidth, int CompHeight, bool32 Interact) + int CompWidth, int CompHeight, real32 Radius, bool32 Interact) { - real32 Radius = Shape->Opt.Roundness; bezier_point *PointStart = PointData; for (int i = 0; i < Shape->Point_Count; i++) { bezier_point Point = *Bezier_LookupAddress(Memory, Shape->Block_Bezier_Index, i, 1); diff --git a/src/createcalls.cpp b/src/createcalls.cpp index 800dd49..adb6ff5 100644 --- a/src/createcalls.cpp +++ b/src/createcalls.cpp @@ -450,6 +450,46 @@ void Clipboard_Store(project_data *File, project_state *State, memory *Memory, s } } +void Slide_Init(project_data *File, project_state *State, memory *Memory) +{ + // It's impossible to use the brush while this is active, so for now we can + // just use its buffer to store our data: + uint8 *InteractBuffer = (uint8 *)State->Brush.TransientBitmap; + State->Interact_Count = 0; + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + if (Layer->IsSelected) { + interact_slide_layer *Interact_Layer = (interact_slide_layer *)InteractBuffer; + *Interact_Layer = {0}; + Interact_Layer->Index = i; + InteractBuffer += sizeof(interact_slide_layer); + State->Interact_Count++; + } + } +} + +void Slide_Test(project_data *File, project_state *State, memory *Memory, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray) +{ + block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); + v2 CompDimensions = V2(MainComp->Width, MainComp->Height); + real32 Threshold = 5; + uint8 *InteractBuffer = (uint8 *)State->Brush.TransientBitmap; + for (int i = 0; i < State->Interact_Count; i++) { + interact_slide_layer *Interact_Layer = (interact_slide_layer *)InteractBuffer; + InteractBuffer += sizeof(interact_slide_layer); + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Interact_Layer->Index); + v2 CompUV = V2(State->Interact_Offset[0], State->Interact_Offset[1]); + v2 CompPos = CompUV * CompDimensions; + v2 LayerPos = Layer_TraverseForPoint(File, State, Memory, CompUV, SortedCompArray, SortedLayerArray); + int Width, Height; + Layer_GetDimensions(Memory, Layer, &Width, &Height); + if (abs(CompPos.x - LayerPos.x) > Threshold && + abs(CompPos.y - LayerPos.y) > Threshold) + Assert(0); + } +} + void Property_MinMax_X(memory *Memory, project_state *State, property_channel *Property, uint16 *ArrayLocation, real32 *Min, real32 *Max) { @@ -687,10 +727,25 @@ Project_ShapeLayer_New(project_data *File, project_state *State, memory *Memory) Layer_Select(Memory, State, Memory_Block_LazyIndexAtAddress(Memory, F_Layers, Layer)); shape_layer *Shape = &File->UI.Shape; + + if (State->Interact_Modifier == 1) { + v2 *CompPoint = (v2 *)&State->Interact_Offset[0]; + for (int i = 0; i < 4; i++) { + bezier_point PointData = { 1, { CompPoint[i], V2(0, 0), V2(0, 0) }, interpolation_type_linear, 0 }; + Bezier_Add(Memory, F_File, Shape->Block_Bezier_Index, &Shape->Block_Bezier_Count, &Shape->Point_Count, PointData); + CompPoint[i] = {0}; + } + Shape->IsClosed = true; + State->Interact_Modifier = 0; + } + + shape_options ShapeOpt = File->UI.ShapeOpt; + ShapeOpt.FillCol = File->UI.Color; + ShapeOpt.StrokeCol = File->UI.AltColor; v2 Min = {}, Max = {}; void *Data = Memory_PushScratch(Memory, sizeof(nvg_point) * 128); layer_transforms T = {}; - NVG_FlattenPath(Memory, Shape, (nvg_point *)Data, State, T, 0, 0, 0, 0, 0, &Min, &Max); + NVG_FlattenPath(Memory, Shape, ShapeOpt, (nvg_point *)Data, State, T, 0, 0, 0, 0, 0, &Min, &Max); Memory_PopScratch(Memory, sizeof(nvg_point) * 128); Shape->Width = Max.x - Min.x; Shape->Height = Max.y - Min.y; @@ -726,6 +781,8 @@ Project_ShapeLayer_New(project_data *File, project_state *State, memory *Memory) History_Action_Swap(Memory, F_Layers, sizeof(Layer->Shape), &Layer->Shape); Layer->Shape = File->UI.Shape; + History_Action_Swap(Memory, F_Layers, sizeof(Layer->ShapeOpt), &Layer->ShapeOpt); + Layer->ShapeOpt = ShapeOpt; History_Action_Swap(Memory, F_File, sizeof(File->UI.Shape), &File->UI.Shape); File->UI.Shape = {}; diff --git a/src/imgui_helper_internal.cpp b/src/imgui_helper_internal.cpp index 7d6944b..da94586 100644 --- a/src/imgui_helper_internal.cpp +++ b/src/imgui_helper_internal.cpp @@ -11,7 +11,7 @@ #include "imgui_internal.h" // NOTE(fox): This API will change in the future! -void ImGui::MyWindowSetup(ImGuiID id) +void ImGui::MyWindowSetup(ImGuiID *Placement, ImGuiID id) { ImGuiViewport *Viewport = GetMainViewport(); ImVec2 WindowMin = Viewport->WorkPos; @@ -22,24 +22,30 @@ void ImGui::MyWindowSetup(ImGuiID id) DockBuilderSetNodeSize(id, WindowSize); DockBuilderSetNodePos(id, WindowMin); + ImGuiID *DockRight = Placement; ImGuiID DockTop = ImGui::DockBuilderSplitNode(id, ImGuiDir_Down, 1.f, nullptr, &id); ImGuiID DockBottom = ImGui::DockBuilderSplitNode(DockTop, ImGuiDir_Down, 0.4f, nullptr, &DockTop); ImGuiID DockLeft = ImGui::DockBuilderSplitNode(DockTop, ImGuiDir_Left, 0.15f, nullptr, &DockTop); - ImGuiID DockRight = ImGui::DockBuilderSplitNode(DockTop, ImGuiDir_Right, 0.2f, nullptr, &DockTop); - ImGuiID DockRightBottom = ImGui::DockBuilderSplitNode(DockRight, ImGuiDir_Down, 0.2f, nullptr, &DockRight); - ImGuiID DockRightTop = ImGui::DockBuilderSplitNode(DockRight, ImGuiDir_Up, 0.6f, nullptr, &DockRight); + *DockRight = ImGui::DockBuilderSplitNode(DockTop, ImGuiDir_Right, 0.2f, nullptr, &DockTop); + ImGuiID DockRightBottom = ImGui::DockBuilderSplitNode(*DockRight, ImGuiDir_Down, 0.2f, nullptr, DockRight); + ImGuiID DockRightTop = ImGui::DockBuilderSplitNode(*DockRight, ImGuiDir_Up, 0.6f, nullptr, DockRight); ImGui::DockBuilderDockWindow("Menu", id); ImGui::DockBuilderDockWindow("Viewport", DockTop); ImGui::DockBuilderDockWindow("Timeline", DockBottom); ImGui::DockBuilderDockWindow("Properties###Properties", DockLeft); ImGui::DockBuilderDockWindow("Colors", DockRightTop); - ImGui::DockBuilderDockWindow("Files", DockRight); + ImGui::DockBuilderDockWindow("Files", *DockRight); ImGui::DockBuilderDockWindow("Effects list", DockRightBottom); ImGui::DockBuilderFinish(id); } +void ImGui::MyDockWindow(char *Window, ImGuiID Dock) +{ + ImGui::DockBuilderDockWindow(Window, Dock); +} + // A modded version of ScalarSlider allowing for the minimum and maximum parts // of the slider to be draggable by two other buttons. p_mid is from range -1 // to 1, and s_min and max are from 0-1. diff --git a/src/imgui_ui.cpp b/src/imgui_ui.cpp index 380aaf2..bfb79f3 100644 --- a/src/imgui_ui.cpp +++ b/src/imgui_ui.cpp @@ -387,6 +387,26 @@ ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Me State->Interact_Modifier = 1; else State->Interact_Modifier = 0; + } else if (io.KeyShift) { + bool32 CommitAction = 0; + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + if (Layer->IsSelected && Layer->IsShapeLayer) { + if (!CommitAction) { + History_Entry_Commit(Memory, "Swap shape colors"); + CommitAction = 1; + } + v4 Temp = Layer->ShapeOpt.FillCol; + History_Action_Swap(Memory, F_Layers, sizeof(Layer->ShapeOpt.FillCol), &Layer->ShapeOpt.FillCol); + Layer->ShapeOpt.FillCol = Layer->ShapeOpt.StrokeCol; + History_Action_Swap(Memory, F_Layers, sizeof(Layer->ShapeOpt.StrokeCol), &Layer->ShapeOpt.StrokeCol); + Layer->ShapeOpt.StrokeCol = Temp; + } + } + if (CommitAction) { + History_Entry_End(Memory); + } } else { v4 Temp = UI->Color; UI->Color = UI->AltColor; diff --git a/src/imgui_ui_properties.cpp b/src/imgui_ui_properties.cpp index 341045e..c427971 100644 --- a/src/imgui_ui_properties.cpp +++ b/src/imgui_ui_properties.cpp @@ -442,7 +442,7 @@ ImGui_PropertiesPanel(project_data *File, project_state *State, ui *UI, memory * } if (Layer->IsShapeLayer) { shape_layer *Shape = &Layer->Shape; - shape_options *Opt = &Layer->Shape.Opt; + shape_options *Opt = &Layer->ShapeOpt; // TODO(fox): Combine with RGBA function? ImGui::DragScalar("Shape width", ImGuiDataType_Float, &Shape->Width); if (ImGui::IsItemActive()) { diff --git a/src/imgui_ui_viewport.cpp b/src/imgui_ui_viewport.cpp index f3e124e..2012354 100644 --- a/src/imgui_ui_viewport.cpp +++ b/src/imgui_ui_viewport.cpp @@ -1,3 +1,4 @@ +#include "imgui.h" #if SPECIAL #include "main.h" #endif @@ -221,6 +222,456 @@ ImGui_Viewport_BrushUI(project_state *State, memory *Memory, ImVec2 ViewportMin, } } +#if 0 +static void +ImGui_Viewport_TransformUI2(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, + v2 Min, v2 Max, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight, uint16 *SortedKeyframeArray) +{ + interact_transform *Interact = (interact_transform *)&State->Interact_Offset[0]; + + v2 InteractMin = Min; + v2 InteractMax = Max; + real32 Rad = 0; + real32 Scale = 1; + + + if (State->Interact_Active == interact_type_viewport_transform_gizmo) { + InteractMin = Interact->Min + Interact->Position; + InteractMax = Interact->Max + Interact->Position; + Rad = Interact->Radians; + Scale = Interact->Scale; + } + + v2 BoxLength = InteractMax - InteractMin; + v2 Center = InteractMax - (BoxLength/2); + + real32 Point0X = Center.x - InteractMin.x; + real32 Point0Y = Center.y - InteractMin.y; + + v2 XAxis = (Point0X * Scale)*V2(cos(Rad), sin(Rad)); + v2 YAxis = (Point0Y * -Scale)*V2(sin(Rad), -cos(Rad)); + + // Points are clockwise starting from the top left. + real32 X0 = -XAxis.x - YAxis.x + Center.x; + real32 Y0 = -XAxis.y - YAxis.y + Center.y; + real32 X1 = X0 + XAxis.x*2; + real32 Y1 = Y0 + XAxis.y*2; + real32 X2 = X1 + YAxis.x*2; + real32 Y2 = Y1 + YAxis.y*2; + real32 X3 = X2 - XAxis.x*2; + real32 Y3 = Y2 - XAxis.y*2; + + // Midway points. + real32 Mid_X0 = X0 + XAxis.x; + real32 Mid_Y0 = Y0 + XAxis.y; + real32 Mid_X1 = X1 + YAxis.x; + real32 Mid_Y1 = Y1 + YAxis.y; + real32 Mid_X2 = X2 - XAxis.x; + real32 Mid_Y2 = Y2 - XAxis.y; + real32 Mid_X3 = X3 - YAxis.x; + real32 Mid_Y3 = Y3 - YAxis.y; + + ImVec2 CompScale = UI->CompZoom / ImVec2(CompWidth, CompHeight); + + ImVec2 P[4]; + P[0] = ImVec2(X0, Y0)*CompScale + UI->CompPos; + P[1] = ImVec2(X1, Y1)*CompScale + UI->CompPos; + P[2] = ImVec2(X2, Y2)*CompScale + UI->CompPos; + P[3] = ImVec2(X3, Y3)*CompScale + UI->CompPos; + + ImVec2 Mid_P[4]; + Mid_P[0] = ImVec2(Mid_X0, Mid_Y0)*CompScale + UI->CompPos; + Mid_P[1] = ImVec2(Mid_X1, Mid_Y1)*CompScale + UI->CompPos; + Mid_P[2] = ImVec2(Mid_X2, Mid_Y2)*CompScale + UI->CompPos; + Mid_P[3] = ImVec2(Mid_X3, Mid_Y3)*CompScale + UI->CompPos; + + if (State->Interact_Active == interact_type_viewport_transform_gizmo) { + ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text); + draw_list->AddLine(P[0], P[1], wcol, 2.0f); + draw_list->AddLine(P[1], P[2], wcol, 2.0f); + draw_list->AddLine(P[2], P[3], wcol, 2.0f); + draw_list->AddLine(P[3], P[0], wcol, 2.0f); + } + + v2 XAxis2 = (BoxLength*CompScale.x)*V2(cos(Rad), sin(Rad)); + v2 YAxis2 = (BoxLength*CompScale.y)*V2(sin(Rad), -cos(Rad)); + + v2 XAxisPerp = (1.0f / LengthSq(XAxis))*XAxis; + v2 YAxisPerp = (1.0f / LengthSq(YAxis))*YAxis; + + // real32 LocalX = ((io.MousePos.x - UI->CompPos.x) - Center.x) ; + // real32 LocalY = ((io.MousePos.y - UI->CompPos.y) - Center.y) ; + layer_transforms BoxTransforms = { Center.x, Center.y, 0.5, 0.5, (real32)(Rad / (PI / 180)), Scale }; + v2 LayerPoint = Transform_ScreenSpaceToLocal(BoxTransforms, CompWidth, CompHeight, BoxLength.x, BoxLength.y, UI->CompPos, UI->CompZoom, ViewportMin, io.MousePos); + + real32 U = LayerPoint.x / BoxLength.x; + real32 V = LayerPoint.y / BoxLength.y; + + bool32 OtherActions = ImGui::IsKeyDown(ImGuiKey_Z); + real32 FontSize = ImGui::GetFontSize() * 3; + real32 Delta = io.MouseDelta.x + io.MouseDelta.y; + + ImVec2 Screen_Center = IV2(Center)*CompScale + UI->CompPos; + + char *Buttons[] = { "##position", "##rotation", "##scale" }; + for (int i = 0; i < 3; i++) { + ImGui::SetCursorScreenPos(Screen_Center + ImVec2(i*FontSize, i*FontSize)); + ImGui::Button(Buttons[i], ImVec2(FontSize, FontSize)); + bool32 IsActivated = ImGui::IsItemActivated(); + bool32 IsActive = ImGui::IsItemActive(); + bool32 IsDeactivated = ImGui::IsItemDeactivated(); + if (IsActivated) { + State->Interact_Active = interact_type_viewport_transform_gizmo; + *Interact = {}; + Interact->Min = Min; + Interact->Max = Max; + } + if (IsActive && Delta != 0.0f) { + v2 Increase = V2(io.MousePos - io.MouseClickedPos[0]); + if (i == 0) { + Interact->Position = Increase; + } else if (i == 1) { + real32 LocalX = (io.MousePos.x - UI->CompPos.x)/CompScale.x - InteractMin.x - (BoxLength.x/2); + real32 LocalY = (io.MousePos.y - UI->CompPos.y)/CompScale.y - InteractMin.y - (BoxLength.y/2); + v2 SlopeDot = V2(BoxLength.x, BoxLength.y); + + real32 Slope_Mouse = LocalY/LocalX; + + real32 Slope_Corner = BoxLength.y / BoxLength.x; + real32 Slope_Flipped = -BoxLength.x / BoxLength.y; + real32 Dot = LocalX * SlopeDot.x + LocalY * SlopeDot.y; + + Interact->Radians = atan((Slope_Mouse - Slope_Corner) / (1 + Slope_Mouse * Slope_Corner)); + real32 ExtraRadians2 = atan((Slope_Mouse - Slope_Flipped) / (1 + Slope_Mouse * Slope_Flipped)); + + if (Dot < 0) { + if (Interact->Radians < 0) { + Interact->Radians = (90 * (PI / 180)) + ExtraRadians2; + } else { + Interact->Radians = (-90 * (PI / 180)) + ExtraRadians2; + } + } + } else if (i == 2) { + real32 StartX = (io.MouseClickedPos[0].x - UI->CompPos.x)/CompScale.x - InteractMin.x - (BoxLength.x/2); + real32 StartY = (io.MouseClickedPos[0].y - UI->CompPos.y)/CompScale.y - InteractMin.y - (BoxLength.y/2); + real32 LocalX = (io.MousePos.x - UI->CompPos.x)/CompScale.x - InteractMin.x - (BoxLength.x/2); + real32 LocalY = (io.MousePos.y - UI->CompPos.y)/CompScale.y - InteractMin.y - (BoxLength.y/2); + real32 Normalized = LocalX / StartX; + if (Normalized < 0.0f) + Normalized = 0.0f; + Interact->Scale = Normalized; + // printf("Local X: %.2f\n", Normalized); + } else { + Assert(0); + } + } + if (IsDeactivated) { + Assert(State->Interact_Active == interact_type_viewport_transform_gizmo); + int h = 0, c = 0, i = 0; + History_Entry_Commit(Memory, "Transform layers"); + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + if (Layer->IsSelected == 1) { + 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_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &Layer->x.CurrentValue, &Layer->y.CurrentValue, &Layer->rotation.CurrentValue, &Layer->scale.CurrentValue); + } + } + History_Entry_End(Memory); + State->Interact_Active = interact_type_none; + *Interact = {}; + State->UpdateFrame = true; + } + } +} +#endif + +static void +ImGui_Viewport_TransformUI2(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, + v2 Min, v2 Max, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight, uint16 *SortedKeyframeArray) +{ + interact_transform *Interact = (interact_transform *)&State->Interact_Offset[0]; + + v2 InteractMin = Min; + v2 InteractMax = Max; + real32 Rad = 0; + real32 Scale = 1; + + if (Interact->Min.x != 0.0f) { + InteractMin = Interact->Min + Interact->Position; + InteractMax = Interact->Max + Interact->Position; + Rad = Interact->Radians; + Scale = Interact->Scale; + } + + DebugWatchVar("Rad", &Interact->Radians, d_float); + + v2 BoxLength = InteractMax - InteractMin; + v2 Center = InteractMax - (BoxLength/2); + + real32 Point0X = Center.x - InteractMin.x; + real32 Point0Y = Center.y - InteractMin.y; + + v2 XAxis = (Point0X * Scale)*V2(cos(Rad), sin(Rad)); + v2 YAxis = (Point0Y * -Scale)*V2(sin(Rad), -cos(Rad)); + + // Points are clockwise starting from the top left. + real32 X0 = -XAxis.x - YAxis.x + Center.x; + real32 Y0 = -XAxis.y - YAxis.y + Center.y; + real32 X1 = X0 + XAxis.x*2; + real32 Y1 = Y0 + XAxis.y*2; + real32 X2 = X1 + YAxis.x*2; + real32 Y2 = Y1 + YAxis.y*2; + real32 X3 = X2 - XAxis.x*2; + real32 Y3 = Y2 - XAxis.y*2; + + // Midway points. + real32 Mid_X0 = X0 + XAxis.x; + real32 Mid_Y0 = Y0 + XAxis.y; + real32 Mid_X1 = X1 + YAxis.x; + real32 Mid_Y1 = Y1 + YAxis.y; + real32 Mid_X2 = X2 - XAxis.x; + real32 Mid_Y2 = Y2 - XAxis.y; + real32 Mid_X3 = X3 - YAxis.x; + real32 Mid_Y3 = Y3 - YAxis.y; + + ImVec2 CompScale = UI->CompZoom / ImVec2(CompWidth, CompHeight); + + ImVec2 P[4]; + P[0] = ImVec2(X0, Y0)*CompScale + UI->CompPos; + P[1] = ImVec2(X1, Y1)*CompScale + UI->CompPos; + P[2] = ImVec2(X2, Y2)*CompScale + UI->CompPos; + P[3] = ImVec2(X3, Y3)*CompScale + UI->CompPos; + + ImVec2 Mid_P[4]; + Mid_P[0] = ImVec2(Mid_X0, Mid_Y0)*CompScale + UI->CompPos; + Mid_P[1] = ImVec2(Mid_X1, Mid_Y1)*CompScale + UI->CompPos; + Mid_P[2] = ImVec2(Mid_X2, Mid_Y2)*CompScale + UI->CompPos; + Mid_P[3] = ImVec2(Mid_X3, Mid_Y3)*CompScale + UI->CompPos; + + ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text); + draw_list->AddLine(P[0], P[1], wcol, 2.0f); + draw_list->AddLine(P[1], P[2], wcol, 2.0f); + draw_list->AddLine(P[2], P[3], wcol, 2.0f); + draw_list->AddLine(P[3], P[0], wcol, 2.0f); + + v2 XAxis2 = (BoxLength*CompScale.x)*V2(cos(Rad), sin(Rad)); + v2 YAxis2 = (BoxLength*CompScale.y)*V2(sin(Rad), -cos(Rad)); + + v2 XAxisPerp = (1.0f / LengthSq(XAxis))*XAxis; + v2 YAxisPerp = (1.0f / LengthSq(YAxis))*YAxis; + + // real32 LocalX = ((io.MousePos.x - UI->CompPos.x) - Center.x) ; + // real32 LocalY = ((io.MousePos.y - UI->CompPos.y) - Center.y) ; + layer_transforms BoxTransforms = { Center.x, Center.y, 0.5, 0.5, (real32)(Rad / (PI / 180)), Scale }; + v2 LayerPoint = Transform_ScreenSpaceToLocal(BoxTransforms, CompWidth, CompHeight, BoxLength.x, BoxLength.y, UI->CompPos, UI->CompZoom, ViewportMin, io.MousePos); + + real32 U = LayerPoint.x / BoxLength.x; + real32 V = LayerPoint.y / BoxLength.y; + + ImVec2 ScaleHandleSize(50, 50); + + bool32 OtherActions = ImGui::IsKeyDown(ImGuiKey_Z); + + /* + // First do the halfway scale points, since they don't need UVs considered: + for (int i = 0; i < 4; i++) { + ImGui::SetCursorScreenPos(Mid_P[i] - ScaleHandleSize/2); + ImGui::PushID(i); + + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertFloat4ToU32(ImVec4(0.6f, 0.0f, 0.3f, 1.0f))); + ImGui::Button("##ScaleMids", ScaleHandleSize); + ImGui::PopStyleColor(); + + if (ImGui::IsItemActivated() && !OtherActions) { + State->InteractTransformMode = 1; + } + + if (State->InteractTransformMode == 1 && ImGui::IsItemActive()) + { + uint32 side = i; + if (side == 0) { + Interact->Scale -= io.MouseDelta.y / BoxLength.y; + Interact->Position.y += io.MouseDelta.y / 2; + } else if (side == 1) { + Interact->Scale += io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + } else if (side == 2) { + Interact->Scale += io.MouseDelta.y / BoxLength.y; + Interact->Position.y += io.MouseDelta.y / 2; + } else if (side == 3) { + Interact->Scale -= io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + } + } + ImGui::PopID(); + } + */ + + bool32 InBounds = false; + // Scale if cursor is on button within the UV, rotate if outside UV, and position if a non-button is dragged. + if (U >= 0.0f && U <= 1.0f && V >= 0.0f && V <= 1.0f) + { + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertFloat4ToU32(ImVec4(0.6f, 0.0f, 0.3f, 1.0f))); + InBounds = true; + } + + for (int i = 0; i < 4; i++) { + ImGui::SetCursorScreenPos(P[i] - ScaleHandleSize/2); + ImGui::PushID(i); + ImGui::Button("##ScaleRotateCorners", ScaleHandleSize); + + if (ImGui::IsItemActivated() && !OtherActions) { + if (InBounds) + State->InteractTransformMode = 1; + else + State->InteractTransformMode = 2; + State->Interact_Active = interact_type_viewport_transform_gizmo; + if (Interact->Max.x == 0) { + *Interact = {}; + Interact->Min = Min; + Interact->Max = Max; + } + } + + // Scale part + if (State->InteractTransformMode == 1 && ImGui::IsItemActive()) + { + // TODO(fox): Corner dragging scale only works in the X + // axis. Mostly feels right when dragged how you expect, + // but I'll fix it if someone complains. + uint32 side = i; + if (side == 0) { + Interact->Scale -= io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + Interact->Position.y += io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2; + } else if (side == 1) { + Interact->Scale += io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + Interact->Position.y -= io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2; + } else if (side == 2) { + Interact->Scale += io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + Interact->Position.y += io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2; + } else if (side == 3) { + Interact->Scale -= io.MouseDelta.x / BoxLength.x; + Interact->Position.x += io.MouseDelta.x / 2; + Interact->Position.y -= io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2; + } + } + + // Rotation part + if (State->InteractTransformMode == 2 && ImGui::IsItemActive()) + { + real32 LocalX = (io.MousePos.x - UI->CompPos.x)/CompScale.x - InteractMin.x - (BoxLength.x/2); + real32 LocalY = (io.MousePos.y - UI->CompPos.y)/CompScale.y - InteractMin.y - (BoxLength.y/2); + + real32 Slope_Mouse = LocalY/LocalX; + real32 Slope_Corner = 0; + real32 Slope_Flipped = 0; + real32 Dot = 0; + + // TODO(fox) learn basic geometry to do this properly + + // We find the angle between the direction of whichever corner the + // mouse is grabbing (Slope_Corner) and the mouse's current + // position (Slope_Mouse) to get ExtraRadians. The calculation only + // works between -90 and 90, so I take the dot product of the + // opposite edge of the corner and add the extra degrees when it's negative. + + v2 SlopeDot = V2(BoxLength.x, BoxLength.y); + // top left clockwise + uint32 side = i; + if (side == 0) { + Slope_Corner = BoxLength.y / BoxLength.x; + Slope_Flipped = -BoxLength.x / BoxLength.y; + Dot = LocalX * -SlopeDot.x + LocalY * -SlopeDot.y; + } else if (side == 1) { + Slope_Corner = -BoxLength.y / BoxLength.x; + Slope_Flipped = BoxLength.x / BoxLength.y; + Dot = LocalX * SlopeDot.x + LocalY * -SlopeDot.y; + } else if (side == 2) { + Slope_Corner = BoxLength.y / BoxLength.x; + Slope_Flipped = -BoxLength.x / BoxLength.y; + Dot = LocalX * SlopeDot.x + LocalY * SlopeDot.y; + } else if (side == 3) { + Slope_Corner = -BoxLength.y / BoxLength.x; + Slope_Flipped = BoxLength.x / BoxLength.y; + Dot = LocalX * -SlopeDot.x + LocalY * SlopeDot.y; + } + + Interact->Radians = atan((Slope_Mouse - Slope_Corner) / (1 + Slope_Mouse * Slope_Corner)); + real32 ExtraRadians2 = atan((Slope_Mouse - Slope_Flipped) / (1 + Slope_Mouse * Slope_Flipped)); + + if (Dot < 0) { + if (Interact->Radians < 0) { + Interact->Radians = (90 * (PI / 180)) + ExtraRadians2; + } else { + Interact->Radians = (-90 * (PI / 180)) + ExtraRadians2; + } + } + } + + ImGui::PopID(); + } + + if (InBounds && !io.KeyAlt) { + ImGui::SetCursorScreenPos(io.MousePos - ScaleHandleSize/2); + ImGui::Button("##mover", ScaleHandleSize); + if (!State->InteractTransformMode && ImGui::IsItemActivated() && !OtherActions) { + State->Interact_Active = interact_type_viewport_transform_gizmo; + if (Interact->Max.x == 0) { + *Interact = {}; + Interact->Min = Min; + Interact->Max = Max; + } + State->InteractTransformMode = 3; + } + } + + if (State->InteractTransformMode == 3) { + Interact->Position.x += (real32)io.MouseDelta.x/CompScale.x; + Interact->Position.y += (real32)io.MouseDelta.y/CompScale.y; + } + + if (State->InteractTransformMode) + { + if (io.MouseDelta.x || io.MouseDelta.y) + State->UpdateFrame = true; + if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) + { + Assert(State->Interact_Active == interact_type_viewport_transform_gizmo); + int h = 0, c = 0, i = 0; + History_Entry_Commit(Memory, "Transform layers"); + while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); + if (Layer->IsSelected == 1) { + 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_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &Layer->x.CurrentValue, &Layer->y.CurrentValue, &Layer->rotation.CurrentValue, &Layer->scale.CurrentValue); + } + } + History_Entry_End(Memory); + State->Interact_Active = interact_type_none; + // *Interact = {}; + State->UpdateFrame = true; + State->InteractTransformMode = 0; + State->Interact_Modifier = 0; + State->UncommitedKeyframe = 1; + State->UpdateFrame = true; + } + } + + if (InBounds == true) { + ImGui::PopStyleColor(); + } + +} + static void ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, interact_transform *Interact, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight, uint16 *SortedKeyframeArray) @@ -457,26 +908,27 @@ ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Mem // Second condition so you don't have to reach for Enter. if (ImGui::IsKeyPressed(ImGuiKey_Enter) || (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && io.KeyCtrl)) { int h = 0, c = 0, i = 0; - if (!io.KeyCtrl) - History_Entry_Commit(Memory, "Transform layers"); + // if (!io.KeyCtrl) + History_Entry_Commit(Memory, "Transform layers"); while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) { block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i); if (Layer->IsSelected == 1) { - if (io.KeyCtrl) { - layer_transforms T = Layer_GetTransforms(Layer); - Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale); - property_channel *Property[4] = { &Layer->x, &Layer->y, &Layer->rotation, &Layer->scale }; - real32 Val[4] = { T.x, T.y, T.rotation, T.scale }; - for (int a = 0; a < 4; a++) { - if (Property[a]->CurrentValue != Val[a]) { - History_Entry_Commit(Memory, "Add keyframe"); - bezier_point Point = { 1, {(real32)State->Frame_Current, Val[a], -1, 0, 1, 0}, interpolation_type_linear, 0, {0, 0, 0}, 0 }; - uint16 *ArrayLocation = Property_GetSortedArray(SortedKeyframeArray, State->MostRecentlySelectedLayer, h); - Bezier_Add(Memory, F_Layers, Property[a], Point, ArrayLocation); - History_Entry_End(Memory); - } - } - } else { + // if (io.KeyCtrl) { + // layer_transforms T = Layer_GetTransforms(Layer); + // Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale); + // property_channel *Property[4] = { &Layer->x, &Layer->y, &Layer->rotation, &Layer->scale }; + // real32 Val[4] = { T.x, T.y, T.rotation, T.scale }; + // for (int a = 0; a < 4; a++) { + // if (Property[a]->CurrentValue != Val[a]) { + // History_Entry_Commit(Memory, "Add keyframe"); + // bezier_point Point = { 1, {(real32)State->Frame_Current, Val[a], -1, 0, 1, 0}, interpolation_type_linear, 0, {0, 0, 0}, 0 }; + // uint16 *ArrayLocation = Property_GetSortedArray(SortedKeyframeArray, State->MostRecentlySelectedLayer, h); + // Bezier_Add(Memory, F_Layers, Property[a], Point, ArrayLocation); + // History_Entry_End(Memory); + // } + // } + // } else { + { 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); @@ -485,8 +937,8 @@ ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Mem } } } - if (!io.KeyCtrl) - History_Entry_End(Memory); + // if (!io.KeyCtrl) + History_Entry_End(Memory); State->Interact_Active = interact_type_none; State->Interact_Modifier = 0; State->UncommitedKeyframe = 1; @@ -500,12 +952,11 @@ ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Mem } static void -ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, block_composition *MainComp, uint32 CompIndex, block_layer *ParentLayer[4], uint32 Recursions, +ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, block_composition *MainComp, uint32 CompIndex, block_layer *ParentLayer[4], uint32 Recursions, v2 *Min, v2 *Max, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray) { sorted_comp_array *SortedCompStart = &SortedCompArray[CompIndex]; sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); - ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text); int LayerCount = SortedCompStart->LayerCount + SortedCompStart->FakeLayerCount; for (int i = 0; i < LayerCount; i++) { @@ -514,7 +965,7 @@ ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImG block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); if (Layer->IsPrecomp) { ParentLayer[Recursions] = Layer; - ImGui_Viewport_SelectedLayerUI(State, Memory, UI, io, draw_list, MainComp, Layer->Block_Source_Index, ParentLayer, Recursions + 1, SortedCompArray, SortedLayerArray); + ImGui_Viewport_SelectedLayerUI(State, Memory, UI, io, draw_list, MainComp, Layer->Block_Source_Index, ParentLayer, Recursions + 1, Min, Max, SortedCompArray, SortedLayerArray); } if (Layer->IsSelected) { uint32 Width = 0, Height = 0; @@ -546,7 +997,8 @@ ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImG layer_transforms T = Layer_GetTransforms(Layer); - if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected == 1) { + if ((State->Interact_Active == interact_type_viewport_transform || + State->Interact_Active == interact_type_viewport_transform_gizmo) && Layer->IsSelected == 1) { Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale); } if (State->Interact_Active == interact_type_viewport_duplicate && SortEntry.IsFake) { @@ -604,6 +1056,14 @@ ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImG i++; } + for (int i = 0; i < 4; i++) { + v2 Pos = NewPos[i+1]; + if (Pos.x < Min->x) { Min->x = Pos.x; } + if (Pos.y < Min->y) { Min->y = Pos.y; } + if (Pos.x > Max->x) { Max->x = Pos.x; } + if (Pos.y > Max->y) { Max->y = Pos.y; } + } + ImVec2 ScreenPoint[5]; for (int i = 0; i < 5; i++) { v2 CompUV = NewPos[i] / V2(MainComp->Width, MainComp->Height); @@ -612,15 +1072,18 @@ ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImG UI->CompPos.y + CompUV.y * UI->CompZoom.y); } - if (State->Tool != tool_brush) { - ImU32 wcol2 = IM_COL32(10, 10, 10, 128); - draw_list->AddNgon(ScreenPoint[0], 10, wcol2, 8, 9.0f); - draw_list->AddNgon(ScreenPoint[0], 10, wcol, 8, 5.0f); + ImU32 wcol = IM_COL32(200, 200, 255, 255); + if (State->Interact_Active != interact_type_viewport_transform_gizmo) { + if (State->Tool != tool_brush) { + ImU32 wcol2 = IM_COL32(10, 10, 10, 128); + draw_list->AddNgon(ScreenPoint[0], 10, wcol2, 8, 9.0f); + draw_list->AddNgon(ScreenPoint[0], 10, wcol, 8, 5.0f); + } + draw_list->AddLine(ScreenPoint[1], ScreenPoint[2], wcol, 1.0f); + draw_list->AddLine(ScreenPoint[2], ScreenPoint[4], wcol, 1.0f); + draw_list->AddLine(ScreenPoint[1], ScreenPoint[3], wcol, 1.0f); + draw_list->AddLine(ScreenPoint[3], ScreenPoint[4], wcol, 1.0f); } - draw_list->AddLine(ScreenPoint[1], ScreenPoint[2], wcol, 2.0f); - draw_list->AddLine(ScreenPoint[2], ScreenPoint[4], wcol, 2.0f); - draw_list->AddLine(ScreenPoint[1], ScreenPoint[3], wcol, 2.0f); - draw_list->AddLine(ScreenPoint[3], ScreenPoint[4], wcol, 2.0f); } } } @@ -693,9 +1156,13 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, if (State->MostRecentlySelectedLayer > -1) { block_layer *ParentLayer[4]; - ImGui_Viewport_SelectedLayerUI(State, Memory, UI, io, draw_list, MainComp, File->PrincipalCompIndex, ParentLayer, 0, SortedCompArray, SortedLayerArray); + // NOTE(fox): This min and max val is affected by interactive transforms! + v2 Min = V2(10000, 10000), Max = V2(-10000, -10000); + ImGui_Viewport_SelectedLayerUI(State, Memory, UI, io, draw_list, MainComp, File->PrincipalCompIndex, ParentLayer, 0, &Min, &Max, SortedCompArray, SortedLayerArray); if (State->Interact_Active == interact_type_viewport_transform) { ImGui_Viewport_TransformUI(File, State, Memory, UI, io, draw_list, (interact_transform *)&State->Interact_Offset[0], ViewportMin, MainComp->Width, MainComp->Height, SortedKeyframeArray); + } else { + ImGui_Viewport_TransformUI2(File, State, Memory, UI, io, draw_list, Min, Max, ViewportMin, MainComp->Width, MainComp->Height, SortedKeyframeArray); } } @@ -784,7 +1251,9 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, } } - if (IsHovered && IsDeactivated && !io.KeyCtrl && !io.KeyAlt && !ImGui::IsMouseDown(ImGuiMouseButton_Right && !ImGui::IsKeyDown(ImGuiKey_Z))) + if (((io.KeyAlt && IsActivated && State->MostRecentlySelectedLayer == -1) || + (IsHovered && IsDeactivated && !io.KeyCtrl && !io.KeyAlt)) && + !ImGui::IsMouseDown(ImGuiMouseButton_Right) && !ImGui::IsKeyDown(ImGuiKey_Z)) { // Layer selection if (State->Tool == tool_default && State->Interact_Active == interact_type_none) { @@ -883,6 +1352,7 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, real32 Delta = io.MouseDelta.x + io.MouseDelta.y; if (Delta && IsActive) { State->Interact_Active = interact_type_viewport_slide; + Slide_Init(File, State, Memory); } } @@ -891,10 +1361,13 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, Assert(0); } else { Assert(IsActive); + v2 PrevMouseCompPos = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePosPrev); v2 MouseCompPos = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos); - v2 DragDelta = MouseCompPos - State->LastClickedPoint; - State->Interact_Offset[0] = DragDelta.x * MainComp->Width; - State->Interact_Offset[1] = DragDelta.y * MainComp->Height; + State->Interact_Offset[0] = MouseCompPos.x; + State->Interact_Offset[1] = MouseCompPos.y; + State->Interact_Offset[3] = PrevMouseCompPos.x; + State->Interact_Offset[4] = PrevMouseCompPos.y; + Slide_Test(File, State, Memory, SortedCompArray, SortedLayerArray); } } @@ -913,16 +1386,13 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, } if (Shape->Point_Count == 0 && IsDeactivated && !OtherActions && !ImGui::IsMouseReleased(ImGuiMouseButton_Right)) { ImVec2 ScreenPoint[4] = { ScreenPointStart, ImVec2(ScreenPointStart.x, MousePos.y), MousePos, ImVec2(MousePos.x, ScreenPointStart.y) }; - History_Entry_Commit(Memory, "Add rectangle"); + v2 *CompPoint = (v2 *)&State->Interact_Offset[0]; for (int i = 0; i < 4; i++) { - v2 CompPoint = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, ScreenPoint[i]); - CompPoint = CompPoint * V2(MainComp->Width, MainComp->Height); - bezier_point PointData = { 1, { CompPoint, V2(0, 0), V2(0, 0) }, interpolation_type_linear, 0 }; - Bezier_Add(Memory, F_File, Shape->Block_Bezier_Index, &Shape->Block_Bezier_Count, &Shape->Point_Count, PointData); + CompPoint[i] = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, ScreenPoint[i]); + CompPoint[i] = CompPoint[i] * V2(MainComp->Width, MainComp->Height); } - Shape->IsClosed = true; - History_Entry_End(Memory); State->HotkeyInput = hotkey_newlayer_shape; + State->Interact_Modifier = 1; } } diff --git a/src/include/all.h b/src/include/all.h index a0368d8..9d7fe77 100644 --- a/src/include/all.h +++ b/src/include/all.h @@ -2,7 +2,7 @@ static uint32 Bezier_Shape_Sort(memory *Memory, shape_layer *Shape, bezier_point *PointData, project_state *State, layer_transforms T, int Width, int Height, - int CompWidth, int CompHeight, bool32 Interact); + int CompWidth, int CompHeight, real32 Radius, bool32 Interact); static bezier_point * Bezier_LookupAddress(memory *Memory, uint16 *Block_Bezier_Index, uint16 Index, bool32 AssertExists = 1); @@ -317,7 +317,7 @@ ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Mem interact_transform *Interact, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight, uint16 *SortedKeyframeArray); static void -ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, block_composition *MainComp, uint32 CompIndex, block_layer *ParentLayer[4], uint32 Recursions, +ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, block_composition *MainComp, uint32 CompIndex, block_layer *ParentLayer[4], uint32 Recursions, v2 *Min, v2 *Max, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray); static void @@ -460,7 +460,7 @@ static void Data_Decompress(memory *Memory, void *CompressedLocation, uint64 CompressedSize, void *BitmapLocation, uint64 ExpectedSize); static uint32 -NVG_FlattenPath(memory *Memory, shape_layer *Shape, nvg_point *PointData, +NVG_FlattenPath(memory *Memory, shape_layer *Shape, shape_options ShapeOpt, nvg_point *PointData, project_state *State, layer_transforms T, int Width, int Height, int CompWidth, int CompHeight, bool32 Interact, v2 *Min, v2 *Max); diff --git a/src/include/imgui_internal_widgets.h b/src/include/imgui_internal_widgets.h index 863e934..875d21c 100644 --- a/src/include/imgui_internal_widgets.h +++ b/src/include/imgui_internal_widgets.h @@ -6,7 +6,8 @@ // NOTE(fox): Appending to the standard ImGui namespace so I don't have to convert all the functions to ImGui::Function() namespace ImGui { - IMGUI_API void MyWindowSetup(ImGuiID id); + IMGUI_API void MyWindowSetup(ImGuiID *Placement, ImGuiID id); + IMGUI_API void MyDockWindow(char *Window, ImGuiID Dock); IMGUI_API bool SliderLevels(const char* label, const char* label2, const char* label3, void* p_data, void* p_min, void* p_max); IMGUI_API bool TestLine(ImVec2 P1, ImVec2 P2); IMGUI_API bool BezierInteractive(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3); diff --git a/src/include/keybinds.h b/src/include/keybinds.h index 7ea0499..ddacb49 100644 --- a/src/include/keybinds.h +++ b/src/include/keybinds.h @@ -45,7 +45,10 @@ static shortcut_entry ShortcutArray[] { { ImGuiKey_None, Mod_None, key_mode_viewport, "Hold right click to pan." }, { ImGuiKey_None, Mod_None, key_mode_viewport, "Hold Z and drag left click to zoom." }, { ImGuiKey_None, Mod_None, key_mode_viewport, "Press Enter or ctrl+click to commit a transform." }, + { ImGuiKey_None, Mod_None, key_mode_viewport, "Hold alt while dragging a layer to duplicate it." }, { ImGuiKey_T, Mod_None, key_mode_viewport, "Transform selected layers" }, + { ImGuiKey_D, Mod_None, key_mode_viewport, "Stepped duplicate" }, + { ImGuiKey_X, Mod_Shift, key_mode_viewport, "Swap stroke/fill colors of selected shape layers" }, { ImGuiKey_Tab, Mod_None, key_mode_timeline, "Switch between timeline and graph" }, { ImGuiKey_2, Mod_None, key_mode_timeline, "Toggle precomp view" }, diff --git a/src/include/main.h b/src/include/main.h index 3129c5d..de33a25 100644 --- a/src/include/main.h +++ b/src/include/main.h @@ -248,10 +248,10 @@ struct sorted_file struct shape_options { int Visibility; v4 FillCol = {1, 1, 1, 1}; - v4 StrokeCol = {1, 0, 0, 1}; - float StrokeWidth = 10; - nvg_line_cap LineJoinType = NVG_ROUND; - nvg_line_cap LineCapType = NVG_ROUND; + v4 StrokeCol = {0, 0, 0, 1}; + float StrokeWidth = 0; + nvg_line_cap LineJoinType = NVG_SQUARE; + nvg_line_cap LineCapType = NVG_MITER; real32 Roundness; }; @@ -265,7 +265,6 @@ struct shape_layer { // They get set once the shape becomes a shape layer! real32 Width; real32 Height; - shape_options Opt; }; enum timeline_mode @@ -301,6 +300,7 @@ enum interact_type interact_type_layer_timeadjust, interact_type_viewport_selection, interact_type_viewport_transform, + interact_type_viewport_transform_gizmo, interact_type_viewport_duplicate, interact_type_viewport_slide, interact_type_keyframe_move, @@ -341,6 +341,12 @@ struct interact_transform uint32 TransformMode; }; +struct interact_slide_layer +{ + uint16 Index; + real32 Offset[4]; +}; + enum hotkey_input { hotkey_none, @@ -483,6 +489,9 @@ struct project_state real32 Interact_Offset[12]; real32 Interact_Dup_Previous[2]; void *Interact_Address; + int Interact_Count; + + ImGuiID RightDock; // NOTE(fox): We need to keep track of when the user changes the CurrentValue of a // channel that has keyframes on it (i.e. CurrentValue will now evaluate to @@ -547,6 +556,7 @@ struct ui #endif shape_layer Shape; + shape_options ShapeOpt; ImU32 LayerColors[16] = { 0xff8b1f1f, @@ -675,6 +685,7 @@ struct block_layer { uint16 Block_Effect_Count; shape_layer Shape; + shape_options ShapeOpt; blend_mode BlendMode; diff --git a/src/include/memory.h b/src/include/memory.h index e6b7676..ae3fd61 100644 --- a/src/include/memory.h +++ b/src/include/memory.h @@ -2,7 +2,6 @@ enum memory_table_list { P_AVInfo, P_UndoBuffer, - P_MiscCache, F_File, F_Precomps, @@ -17,7 +16,6 @@ enum memory_table_list { B_Thumbnails, B_PointData, B_ScratchSpace, - B_CacheEntries, B_CachedBitmaps, M_Count diff --git a/src/layer.cpp b/src/layer.cpp index d21ab0e..c158466 100644 --- a/src/layer.cpp +++ b/src/layer.cpp @@ -112,14 +112,15 @@ Layer_UpdateMasksEffects(project_state *State, block_layer *Layer, memory *Memor if (Layer->IsShapeLayer) { shape_layer *Shape = &Layer->Shape; + shape_options ShapeOpt = Layer->ShapeOpt; void *Data = Memory_PushScratch(Memory, sizeof(nvg_point) * 128); Arbitrary_Zero((uint8 *)Data, sizeof(nvg_point) * 128); layer_transforms T = Layer_GetTransforms(Layer); v2 Min = {}, Max = {}; - uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, (nvg_point *)Data, + uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, ShapeOpt, (nvg_point *)Data, State, T, Shape->Width, Shape->Height, Width, Height, 1, &Min, &Max); void *Data_Stroke = Memory_PushScratch(Memory, sizeof(real32) * 4 * 256); - uint32 StrokeCount = NVG_ExpandStroke(Memory, NumberOfVerts, Shape->Opt.StrokeWidth, Shape->Opt.LineCapType, Shape->Opt.LineJoinType, Shape->IsClosed, (nvg_point *)Data, (real32 *)Data_Stroke); + uint32 StrokeCount = NVG_ExpandStroke(Memory, NumberOfVerts, ShapeOpt.StrokeWidth, ShapeOpt.LineCapType, ShapeOpt.LineJoinType, Shape->IsClosed, (nvg_point *)Data, (real32 *)Data_Stroke); void *Data_Fill = Memory_PushScratch(Memory, sizeof(real32) * 4 * NumberOfVerts); NVG_ExpandFill(Memory, NumberOfVerts, (nvg_point *)Data, (real32 *)Data_Fill); @@ -569,11 +570,12 @@ Layer_TestBoxSelect(memory *Memory, project_state *State, ui *UI, sorted_comp_ar nvg_point *NVGPointData = (nvg_point *)Memory_PushScratch(Memory, sizeof(nvg_point) * 1024); v2 ShapeMin = {}, ShapeMax = {}; - uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, NVGPointData, + uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, Layer->ShapeOpt, NVGPointData, State, T, Shape->Width, Shape->Height, Comp->Width, Comp->Height, 1, &ShapeMin, &ShapeMax); v2 Box[4] = { MinPos, V2(MinPos.x, MaxPos.y), MaxPos, V2(MaxPos.x, MinPos.y) }; - if (Shape_TestBoxSelect(T, Comp->Width, Comp->Height, Shape->Height, Shape->Width, Box, NVGPointData, NumberOfVerts)) - Layer->IsSelected = true; + if (Shape_TestBoxSelect(T, Comp->Width, Comp->Height, Shape->Height, Shape->Width, Box, NVGPointData, NumberOfVerts)) { + Layer_Select(Memory, State, i); + } Memory_PopScratch(Memory, sizeof(nvg_point) * 1024); } else { diff --git a/src/main.cpp b/src/main.cpp index 82f0153..3f8388e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ #include "main.h" +#include "imgui_internal_widgets.h" SDL_Thread *Thread[8]; SDL_sem *Semaphore; @@ -98,8 +99,9 @@ Main_InputTest(project_data *File, project_state *State, memory *Memory, sorted_ ImGuiID DockID = ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(), ImGuiDockNodeFlags_AutoHideTabBar); if (State->FirstFrame) { - ImGui::MyWindowSetup(DockID); - } + ImGui::MyWindowSetup(&State->RightDock, DockID); + } + ImGui::MyDockWindow("Timeline", State->RightDock); if (State->Warp_WantSetPos) { ImGui_WarpMouseFinish(State, io.MousePos); @@ -413,12 +415,16 @@ Render_UI(project_data *File, project_state *State, memory *Memory, ui *UI, ImDr Frame_End_Abs > Frame_Current && Layer->IsVisible) { shape_layer *Shape = &Layer->Shape; + shape_options ShapeOpt = Layer->ShapeOpt; void *Data = StartAddress; layer_transforms T = Layer_GetTransforms(Layer); if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected == 1) { Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale); } + if (State->Interact_Active == interact_type_viewport_transform_gizmo && Layer->IsSelected == 1) { + Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale); + } if (State->Interact_Active == interact_type_viewport_slide && Layer->IsSelected == 1) { // Transform_ApplySlide((v2 *)&State->Interact_Offset[0], &T); } @@ -429,11 +435,11 @@ Render_UI(project_data *File, project_state *State, memory *Memory, ui *UI, ImDr } v2 Min = {}, Max = {}; - uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, (nvg_point *)Data, + uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, ShapeOpt, (nvg_point *)Data, State, T, Shape->Width, Shape->Height, Comp->Width, Comp->Height, 1, &Min, &Max); StartAddress += NumberOfVerts * sizeof(nvg_point); void *Data_Stroke = StartAddress; - uint32 StrokeCount = NVG_ExpandStroke(Memory, NumberOfVerts, Shape->Opt.StrokeWidth, Shape->Opt.LineCapType, Shape->Opt.LineJoinType, Shape->IsClosed, (nvg_point *)Data, (real32 *)Data_Stroke); + uint32 StrokeCount = NVG_ExpandStroke(Memory, NumberOfVerts, ShapeOpt.StrokeWidth, ShapeOpt.LineCapType, ShapeOpt.LineJoinType, Shape->IsClosed, (nvg_point *)Data, (real32 *)Data_Stroke); StartAddress += StrokeCount * sizeof(real32) * 4; void *Data_Fill = StartAddress; NVG_ExpandFill(Memory, NumberOfVerts, (nvg_point *)Data, (real32 *)Data_Fill); @@ -447,9 +453,9 @@ Render_UI(project_data *File, project_state *State, memory *Memory, ui *UI, ImDr RenderData->LayerCount++; StartAddress += sizeof(gl_data); - int Visibility = (Shape->Opt.StrokeWidth > 0.0f) ? Shape->Opt.Visibility : 1; - *GL_Data = { Data_Stroke, StrokeCount, Shape->Opt.StrokeCol, - Data_Fill, NumberOfVerts, Shape->Opt.FillCol, + int Visibility = (ShapeOpt.StrokeWidth > 0.0f) ? ShapeOpt.Visibility : 1; + *GL_Data = { Data_Stroke, StrokeCount, ShapeOpt.StrokeCol, + Data_Fill, NumberOfVerts, ShapeOpt.FillCol, T, Shape->Width, Shape->Height, Visibility }; } } @@ -724,7 +730,6 @@ int main(int argc, char *argv[]) { 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, 40 * 1024 * 1024, P_MiscCache, "Misc persistent"); 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)); @@ -739,7 +744,6 @@ int main(int argc, char *argv[]) { 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)1 * 1024 * 1024, B_CacheEntries, "Cache entries", sizeof(cache_entry)); 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; @@ -791,7 +795,7 @@ int main(int argc, char *argv[]) { block_composition *MainComp = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps); MainComp->Width = 1280; - MainComp->Height = 720; + MainComp->Height = 1280; MainComp->FPS = 60; MainComp->BytesPerPixel = 4; MainComp->Frame_Count = 80; @@ -1157,6 +1161,8 @@ int main(int argc, char *argv[]) { 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); if (State->IsPlaying && State->HotFramePerf > 1 && FullyCached) { diff --git a/src/nanovg.cpp b/src/nanovg.cpp index b7b426f..f1d1702 100644 --- a/src/nanovg.cpp +++ b/src/nanovg.cpp @@ -177,7 +177,7 @@ static real32 * NVG_ButtCap(nvg_point *Point, real32 *StrokeData, // NOTE(fox): We only have to care about winding if we want to do HW accelerated // shape subtraction with the stencil buffer (I think). static uint32 -NVG_FlattenPath(memory *Memory, shape_layer *Shape, nvg_point *PointData, +NVG_FlattenPath(memory *Memory, shape_layer *Shape, shape_options ShapeOpt, nvg_point *PointData, project_state *State, layer_transforms T, int Width, int Height, int CompWidth, int CompHeight, bool32 Interact, v2 *Min, v2 *Max) { @@ -185,7 +185,7 @@ NVG_FlattenPath(memory *Memory, shape_layer *Shape, nvg_point *PointData, bezier_point *BezierPointData = (bezier_point *)Memory_PushScratch(Memory, sizeof(bezier_point) * 128); uint32 BezierCount = Bezier_Shape_Sort(Memory, Shape, BezierPointData, State, T, Width, Height, - CompWidth, CompHeight, Interact); + CompWidth, CompHeight, ShapeOpt.Roundness, Interact); for (int i = 0; i < BezierCount; i++) { bezier_point *Point = &BezierPointData[i]; if (i == 0 || Point->Type == interpolation_type_linear) { diff --git a/src/prenderer.cpp b/src/prenderer.cpp index 08cdadf..1cc490c 100644 --- a/src/prenderer.cpp +++ b/src/prenderer.cpp @@ -83,7 +83,8 @@ Transform_ApplyInteractive(interact_transform Interact, real32 *OutputX, real32 *OutputX = X0 + Interact.Position.x; *OutputY = Y0 + Interact.Position.y; *OutputRotation += Rotation; - *OutputScale += Interact.Scale - 1.0f; + // *OutputScale += Interact.Scale - 1.0f; + *OutputScale *= Interact.Scale; } static void @@ -222,6 +223,30 @@ Interact_Transform_Begin(project_data *File, memory *Memory, project_state *Stat */ } +static void +Interact_Transform2_Begin(project_data *File, memory *Memory, project_state *State, ImVec2 OGPos, + sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray) +{ + real32 MinX = 100000; + real32 MinY = 100000; + real32 MaxX = -100000; + real32 MaxY = -100000; + block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); + block_layer *ParentLayer[4]; + Transform_Recurse(State, Memory, MainComp, File->PrincipalCompIndex, ParentLayer, 0, + SortedCompArray, SortedLayerArray, + &MinX, &MinY, &MaxX, &MaxY); + if (MinX != 100000) { + State->Interact_Active = interact_type_viewport_transform; + interact_transform *Interact = (interact_transform *)&State->Interact_Offset[0]; + Interact->Min = V2(MinX, MinY); + Interact->Max = V2(MaxX, MaxY); + Interact->Position = V2(0); + Interact->Radians = 0; + Interact->Scale = 1.0f; + Interact->OGPos = OGPos; + } +} static v2 TransformPoint(layer_transforms T, real32 Width, real32 Height, v2 Point) @@ -263,7 +288,8 @@ Layer_LocalToScreenSpace(project_state *State, memory *Memory, block_layer *Laye layer_transforms T = Layer_GetTransforms(Layer); - if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected == 1) { + if ((State->Interact_Active == interact_type_viewport_transform || + State->Interact_Active == interact_type_viewport_transform_gizmo) && Layer->IsSelected == 1) { Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale); } @@ -364,7 +390,8 @@ Transform_Calculate(project_state *State, memory *Memory, project_data *File, bl real32 s = Layer->scale.CurrentValue; blend_mode BlendMode = Layer->BlendMode; - if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected == 1) { + if ((State->Interact_Active == interact_type_viewport_transform || + State->Interact_Active == interact_type_viewport_transform_gizmo) && Layer->IsSelected == 1) { Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &X, &Y, &Rotation, &s); } -- cgit v1.2.3