diff options
Diffstat (limited to 'src/imgui_ui_viewport.cpp')
-rw-r--r-- | src/imgui_ui_viewport.cpp | 715 |
1 files changed, 715 insertions, 0 deletions
diff --git a/src/imgui_ui_viewport.cpp b/src/imgui_ui_viewport.cpp new file mode 100644 index 0000000..e21c1a6 --- /dev/null +++ b/src/imgui_ui_viewport.cpp @@ -0,0 +1,715 @@ + +static void +ImGui_Viewport_Toolbar(project_state *State, ImDrawList *draw_list) +{ + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + // ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(50, 50, 50, 0)); + + real32 IconSize = ImGui::GetFontSize() * 4; + int ToolCount = (int)tool_count; + ImVec2 ButtonSize(IconSize, IconSize); + ImVec2 WindowSize(IconSize, IconSize * ToolCount); + ImGui::BeginChild("Toolbar", WindowSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar); + for (int i = 0; i < ToolCount; i++) { + ImGui::PushID(i); + // draw_list->AddImage((void *)(intptr_t)State->ToolIconTex[i], Min, Max); + if ((int)State->Tool == i) { + ImVec2 Min = ImGui::GetCursorScreenPos(); + ImVec2 Max = Min + ButtonSize; + draw_list->AddRectFilled(Min, Max, IM_COL32(255, 255, 255, 128)); + } + ImGui::Button(ToolName[i], ButtonSize); + if (ImGui::IsItemActivated()) { + State->Tool = (tool)i; + } + ImGui::PopID(); + } + ImGui::EndChild(); + + // ImGui::PopStyleColor(); + ImGui::PopStyleVar(2); +} + +static void +ImGui_Viewport_BrushUI(project_state *State, memory *Memory, ImVec2 ViewportMin, ImVec2 ViewportMax, ImVec2 CompZoom, ImGuiIO io, uint16 Width, uint16 Height) +{ + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + if (State->Tool == tool_brush) { + + if (ImGui::IsKeyPressed(ImGuiKey_ModAlt, false)) { + State->Brush.UIPos = io.MousePos; + } + + ImVec2 CompScale = CompZoom / ImVec2(Width, Height); + ImVec2 BrushSize = CompScale * State->Brush.Size; + ImVec2 MinBounds = State->Brush.UIPos - BrushSize/2; + ImVec2 MaxBounds = MinBounds + BrushSize; + + // if (io.KeyCtrl) { + // ImGui::SetCursorScreenPos(State->Brush.UIPos); + // char buf[256]; + // sprintf(buf, "RGBA: %.1f, %.1f, %.1f, %.1f", State->Brush.Size, State->Brush.Hardness); + // } + + if (io.KeyAlt) { + draw_list->PushClipRect(ViewportMin, ViewportMax, true); + draw_list->AddImage((void *)(intptr_t)State->Brush.GLTexture, MinBounds, MaxBounds, ImVec2(0, 0), ImVec2(1, 1), 1); + draw_list->PopClipRect(); + ImGui::SetCursorScreenPos(State->Brush.UIPos); + char buf[256]; + sprintf(buf, "Size: %.1f, Hardness: %.1f", State->Brush.Size, State->Brush.Hardness); + ImGui::Text(buf); + if (io.MouseDelta.x || io.MouseDelta.y) { + ImVec2 Delta = io.MouseDelta; + State->Brush.Size += Delta.x; + State->Brush.Hardness += Delta.y*State->Brush.Hardness/100; + if (State->Brush.Size < 0) + State->Brush.Size = 0; + if (State->Brush.Size > 1024) + State->Brush.Size = 1024; + if (State->Brush.Hardness < 0.5) + State->Brush.Hardness = 0.5; + if (State->Brush.Hardness > 100) + State->Brush.Hardness = 100; + Brush_CalcBitmapAlphaFromSize(Memory, &State->Brush, 4); + State_BindBrushTexture(Memory, &State->Brush, 4); + } + } + } +} + +static void +ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Memory, ui *UI, ImDrawList *draw_list, ImGuiIO &io, + interact_transform *Interact, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight, uint16 *SortedKeyframeArray) +{ + v2 InteractMin = Interact->Min + Interact->Position; + v2 InteractMax = Interact->Max + Interact->Position; + + v2 BoxLength = InteractMax - InteractMin; + v2 Center = InteractMax - (BoxLength/2); + + real32 Point0X = Center.x - InteractMin.x; + real32 Point0Y = Center.y - InteractMin.y; + + real32 Rad = Interact->Radians; + + v2 XAxis = (Point0X * Interact->Scale)*V2(cos(Rad), sin(Rad)); + v2 YAxis = (Point0Y * -Interact->Scale)*V2(sin(Rad), -cos(Rad)); + + // Points are clockwise starting from the top left. + real32 X0 = -XAxis.x - YAxis.x + Center.x; + real32 Y0 = -XAxis.y - YAxis.y + Center.y; + real32 X1 = X0 + XAxis.x*2; + real32 Y1 = Y0 + XAxis.y*2; + real32 X2 = X1 + YAxis.x*2; + real32 Y2 = Y1 + YAxis.y*2; + real32 X3 = X2 - XAxis.x*2; + real32 Y3 = Y2 - XAxis.y*2; + + // Midway points. + real32 Mid_X0 = X0 + XAxis.x; + real32 Mid_Y0 = Y0 + XAxis.y; + real32 Mid_X1 = X1 + YAxis.x; + real32 Mid_Y1 = Y1 + YAxis.y; + real32 Mid_X2 = X2 - XAxis.x; + real32 Mid_Y2 = Y2 - XAxis.y; + real32 Mid_X3 = X3 - YAxis.x; + real32 Mid_Y3 = Y3 - YAxis.y; + + ImVec2 CompScale = UI->CompZoom / ImVec2(CompWidth, CompHeight); + + ImVec2 P[4]; + P[0] = ImVec2(X0, Y0)*CompScale + UI->CompPos; + P[1] = ImVec2(X1, Y1)*CompScale + UI->CompPos; + P[2] = ImVec2(X2, Y2)*CompScale + UI->CompPos; + P[3] = ImVec2(X3, Y3)*CompScale + UI->CompPos; + + ImVec2 Mid_P[4]; + Mid_P[0] = ImVec2(Mid_X0, Mid_Y0)*CompScale + UI->CompPos; + Mid_P[1] = ImVec2(Mid_X1, Mid_Y1)*CompScale + UI->CompPos; + Mid_P[2] = ImVec2(Mid_X2, Mid_Y2)*CompScale + UI->CompPos; + Mid_P[3] = ImVec2(Mid_X3, Mid_Y3)*CompScale + UI->CompPos; + + ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text); + draw_list->AddLine(P[0], P[1], wcol, 2.0f); + draw_list->AddLine(P[1], P[2], wcol, 2.0f); + draw_list->AddLine(P[2], P[3], wcol, 2.0f); + draw_list->AddLine(P[3], P[0], wcol, 2.0f); + + v2 XAxis2 = (BoxLength*CompScale.x)*V2(cos(Rad), sin(Rad)); + v2 YAxis2 = (BoxLength*CompScale.y)*V2(sin(Rad), -cos(Rad)); + + v2 XAxisPerp = (1.0f / LengthSq(XAxis))*XAxis; + v2 YAxisPerp = (1.0f / LengthSq(YAxis))*YAxis; + + // real32 LocalX = ((io.MousePos.x - UI->CompPos.x) - Center.x) ; + // real32 LocalY = ((io.MousePos.y - UI->CompPos.y) - Center.y) ; + layer_transforms BoxTransforms = { Center.x, Center.y, 0.5, 0.5, (real32)(Interact->Radians / (PI / 180)), Interact->Scale }; + v2 LayerPoint = Transform_ScreenSpaceToLocal(BoxTransforms, CompWidth, CompHeight, BoxLength.x, BoxLength.y, UI->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; + } + + // 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 (!State->InteractTransformMode && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && InBounds && !OtherActions) + 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)) + State->InteractTransformMode = 0; + } + + if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { + State->Interact_Active = interact_type_none; + State->Interact_Modifier = 0; + State->UpdateFrame = true; + Memory->PurgeCache = true; + } + + // Second condition so you don't have to reach for Enter. + if (ImGui::IsKeyPressed(ImGuiKey_Enter) || (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && io.KeyCtrl)) { + int h = 0, c = 0, i = 0; + 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 { + History_Action_Swap(Memory, F_File, sizeof(Layer->x.CurrentValue), &Layer->x.CurrentValue); + History_Action_Swap(Memory, F_File, sizeof(Layer->y.CurrentValue), &Layer->y.CurrentValue); + History_Action_Swap(Memory, F_File, sizeof(Layer->scale.CurrentValue), &Layer->scale.CurrentValue); + History_Action_Swap(Memory, F_File, sizeof(Layer->rotation.CurrentValue), &Layer->rotation.CurrentValue); + Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &Layer->x.CurrentValue, &Layer->y.CurrentValue, &Layer->rotation.CurrentValue, &Layer->scale.CurrentValue); + } + } + } + if (!io.KeyCtrl) + History_Entry_End(Memory); + State->Interact_Active = interact_type_none; + State->Interact_Modifier = 0; + State->UncommitedKeyframe = 1; + State->UpdateFrame = true; + } + + if (InBounds == true) { + ImGui::PopStyleColor(); + } + +} + +static void +ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImDrawList *draw_list, block_composition *MainComp, uint32 CompIndex, block_layer *ParentLayer[4], uint32 Recursions, + 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); + 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); + if (Layer->IsPrecomp) { + ParentLayer[Recursions] = Layer; + ImGui_Viewport_SelectedLayerUI(State, Memory, UI, draw_list, MainComp, Layer->Block_Source_Index, ParentLayer, Recursions + 1, SortedCompArray, SortedLayerArray); + } + if (Layer->IsSelected) { + uint32 Width = 0, Height = 0; + void *Data; + uint32 NumberOfVerts; + if (Layer->IsPrecomp) { + block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, Layer->Block_Source_Index); + Width = Comp->Width; + Height = Comp->Height; + } else if (Layer->IsShapeLayer) { + block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(Memory, F_Bezier, Layer->Shape.Block_Bezier_Index[0]); + Data = Memory_PushScratch(Memory, sizeof(nvg_point) * 128); + int L_Width = 0, L_Height = 0; + NumberOfVerts = NVG_FlattenPath(Memory, Bezier, 3, (nvg_point *)Data, &L_Width, &L_Height); + Width = L_Width; + Height = L_Height; + } else { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); + Width = Source->Width; + Height = Source->Height; + } + + v2 Point[5] = { V2(Width*Layer->ax.CurrentValue, Height*Layer->ay.CurrentValue), V2(0, 0), V2(Width, 0), V2(0, Height), V2(Width, Height) }; + + 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 (Layer->IsShapeLayer) { + block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(Memory, F_Bezier, Layer->Shape.Block_Bezier_Index[0]); + // for (int i = 0; i < Layer->Shape.Point_Count; i++) { + // v2 ThisPoint = Bezier->Point[i].Pos[0]; + // v2 Pos = TransformPoint(T, Width, Height, ThisPoint); + // v2 CompUV = Pos / V2(MainComp->Width, MainComp->Height); + // ImVec2 ScreenPoint = ImVec2(UI->CompPos.x + CompUV.x * UI->CompZoom.x, + // UI->CompPos.y + CompUV.y * UI->CompZoom.y); + // draw_list->AddNgon(ScreenPoint, 10, IM_COL32(10, 10, 10, 255), 8, 9.0f); + // } + + // imgui code + + void *Data2 = Memory_PushScratch(Memory, sizeof(real32) * 3 * 256); + uint32 GL_PointCount = NVG_ExpandStroke(Memory, Bezier, NumberOfVerts, (nvg_point *)Data, (real32 *)Data2); + + v2 L_Pos[4] = { Bezier->Point[0].Pos[0], Bezier->Point[0].Pos[2], Bezier->Point[1].Pos[1], Bezier->Point[1].Pos[0] }; + L_Pos[1] = L_Pos[1] + L_Pos[0]; + L_Pos[2] = L_Pos[2] + L_Pos[3]; + ImVec2 ScreenPoint[4]; + for (int i = 0; i < 4; i++) { + v2 Pos = TransformPoint(T, Width, Height, L_Pos[i]); + v2 CompUV = Pos / V2(MainComp->Width, MainComp->Height); + ScreenPoint[i] = ImVec2(UI->CompPos.x + CompUV.x * UI->CompZoom.x, + UI->CompPos.y + CompUV.y * UI->CompZoom.y); + } + draw_list->AddNgon(ScreenPoint[0], 10, IM_COL32(10, 10, 10, 255), 8, 2.0f); + draw_list->AddNgon(ScreenPoint[3], 10, IM_COL32(10, 10, 10, 255), 8, 2.0f); + draw_list->AddBezierCubic(ScreenPoint[0],ScreenPoint[1],ScreenPoint[2],ScreenPoint[3], IM_COL32(10, 10, 10, 255), 1.0f, 0); + + // test code + + for (int i = 0; i < NumberOfVerts; i++) { + nvg_point Point = *((nvg_point *)Data + i); + v2 PointPos = V2(Point.x, Point.y); + v2 Pos = TransformPoint(T, Width, Height, PointPos); + v2 CompUV = Pos / V2(MainComp->Width, MainComp->Height); + ImVec2 ScreenPoint = ImVec2(UI->CompPos.x + CompUV.x * UI->CompZoom.x, + UI->CompPos.y + CompUV.y * UI->CompZoom.y); + draw_list->AddNgon(ScreenPoint, 2, IM_COL32(00, 00, 80, 255), 8, 2.0f); + } + for (int i = 0; i < GL_PointCount; i++) { + v2 PointPos = *((v2 *)Data2 + i*2); + v2 Pos = TransformPoint(T, Width, Height, PointPos); + v2 CompUV = Pos / V2(MainComp->Width, MainComp->Height); + ImVec2 ScreenPoint = ImVec2(UI->CompPos.x + CompUV.x * UI->CompZoom.x, + UI->CompPos.y + CompUV.y * UI->CompZoom.y); + draw_list->AddNgon(ScreenPoint, 2, IM_COL32(80, 80, 10, 255), 8, 2.0f); + } + Memory_PopScratch(Memory, sizeof(real32) * 3 * 256); + Memory_PopScratch(Memory, sizeof(nvg_point) * 128); + } + + v2 NewPos[5]; + for (int i = 0; i < 5; i++) { + NewPos[i] = TransformPoint(T, Width, Height, Point[i]); + } + + int i = 0; + while (i < Recursions) { + T = Layer_GetTransforms(ParentLayer[i]); + block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, ParentLayer[i]->Block_Source_Index); + Width = Comp->Width; + Height = Comp->Height; + for (int i = 0; i < 5; i++) { + NewPos[i] = TransformPoint(T, Width, Height, NewPos[i]); + } + i++; + } + + ImVec2 ScreenPoint[5]; + for (int i = 0; i < 5; i++) { + v2 CompUV = NewPos[i] / V2(MainComp->Width, MainComp->Height); + + ScreenPoint[i] = ImVec2(UI->CompPos.x + CompUV.x * UI->CompZoom.x, + 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); + } + 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); + } + } +} + +static void +ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io, GLuint textureID, + sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, uint16 *SortedKeyframeArray) +{ + bool open = true; + ImGui::Begin("Viewport", &open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); + + + if (ImGui::IsWindowHovered(ImGuiFocusedFlags_ChildWindows)) { + State->FocusedWindow = focus_viewport; + } + + block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); + + ImVec2 ViewportMin = ImGui::GetCursorScreenPos(); + ImVec2 ViewportScale = ImGui::GetContentRegionAvail(); + ImVec2 ViewportMax = ImVec2(ViewportMin.x + ViewportScale.x, ViewportMin.y + ViewportScale.y); + + if (ViewportScale.x < 50 || ViewportScale.y < 50) { + ImGui::End(); + return; + } + + if (State->Initializing) { + UI->CompZoom = ImVec2(MainComp->Width, MainComp->Height); + UI->CompPos = ImVec2(ViewportMin.x + ((ViewportMax.x - ViewportMin.x)/2 - UI->CompZoom.x/2), + ViewportMin.y + ((ViewportMax.y - ViewportMin.y)/2 - UI->CompZoom.y/2)); + } + + ImVec2 CompPosMin = ImVec2(UI->CompPos.x, UI->CompPos.y); + ImVec2 CompPosMax = ImVec2(UI->CompPos.x + UI->CompZoom.x, UI->CompPos.y + UI->CompZoom.y); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddRectFilled(ViewportMin, ViewportMax, IM_COL32(50, 50, 50, 255)); + draw_list->AddRect(ViewportMin, ViewportMax, IM_COL32(255, 255, 255, 255)); + draw_list->AddRect(CompPosMin, CompPosMax, IM_COL32(255, 255, 255, 55)); + + real32 FontSize = ImGui::GetFontSize(); + ImGui::SetCursorScreenPos(ImVec2(ViewportMax.x - FontSize*2, ViewportMin.y + ViewportScale.y - FontSize*3.0)); + ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 80)); + if (ImGui::Button("?")) + State->ImGuiPopups = popup_keybinds; + ImGui::PopStyleColor(); + + + // Actual composition texture + draw_list->PushClipRect(ViewportMin, ViewportMax, true); + draw_list->AddImage((void *)(intptr_t)textureID, CompPosMin, CompPosMax); + draw_list->PopClipRect(); + + // UI+interaction for layer + if (State->MostRecentlySelectedLayer > -1) + { + block_layer *ParentLayer[4]; + ImGui_Viewport_SelectedLayerUI(State, Memory, UI, draw_list, MainComp, File->PrincipalCompIndex, ParentLayer, 0, SortedCompArray, SortedLayerArray); + if (State->Interact_Active == interact_type_viewport_transform) { + ImGui_Viewport_TransformUI(File, State, Memory, UI, draw_list, io, (interact_transform *)&State->Interact_Offset[0], ViewportMin, MainComp->Width, MainComp->Height, SortedKeyframeArray); + } + } + + + + // Interactions for dragging and zooming + ImGui::SetCursorScreenPos(ViewportMin); + + ImGui::InvisibleButton("canvas", ViewportScale, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); + bool32 IsHovered = ImGui::IsItemHovered(); +#if 1 + bool32 IsActive = ImGui::IsItemActive(); + bool32 IsActivated = ImGui::IsItemActivated(); + bool32 IsDeactivated = ImGui::IsItemDeactivated(); +#else + bool32 IsActive = ImGui::IsKeyDown(ImGuiKey_3); + bool32 IsActivated = ImGui::IsKeyPressed(ImGuiKey_3); + bool32 IsDeactivated = ImGui::IsKeyReleased(ImGuiKey_3); +#endif + + if (IsHovered && IsActivated && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) + { + State->TempZoomRatio = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos); + + if (!ImGui::IsKeyDown(ImGuiKey_Z)) { + if (State->Tool == tool_brush && State->Interact_Active != interact_type_brush) { + if (!io.KeyCtrl) { + 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->IsPrecomp) { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); + if (Layer->IsSelected && Source->Type == source_type_principal) { + Assert(Source->BytesPerPixel == 4); + Arbitrary_Zero((uint8 *)State->Brush.TransientBitmap, 2048*2048*4); + State->Interact_Active = interact_type_brush; + State->Brush.LayerToPaint_Index = i; + break; + } + } + } + } + if (State->Brush.LayerToPaint_Index == -1) { + State->HotkeyInput = hotkey_newlayer_paint; + } + } + // Layer selection + if (State->Tool == tool_default && State->Interact_Active == interact_type_none) { + int32 Selection = Layer_TestSelection(Memory, State, UI, SortedCompArray, SortedLayerArray, File->PrincipalCompIndex); + if (!io.KeyShift && State->Interact_Active == interact_type_none) + Layer_DeselectAll(File, State, Memory); + if (Selection != -1) + Layer_Select(Memory, State, Selection); + } + } + } + + /* + if (State->Interact_Active == interact_type_viewport_transform) { + interact_transform *Interact = (interact_transform *)&State->Interact_Offset[0]; + ImVec2 DragDelta = io.MousePos - Interact->OGPos; + Interact->Position = V2(DragDelta.x, DragDelta.y); + if (io.MouseDelta.x || io.MouseDelta.y) + State->UpdateFrame = true; + } + */ + + if (IsActive && ImGui::IsMouseDragging(ImGuiMouseButton_Right, -1.0f)) + { + UI->CompPos.x += io.MouseDelta.x; + UI->CompPos.y += io.MouseDelta.y; + } + + + bool32 OtherActions = ImGui::IsKeyDown(ImGuiKey_Z) || ImGui::IsMouseDown(ImGuiMouseButton_Right); + if (State->Tool == tool_brush && State->Interact_Active == interact_type_brush) { + Assert(State->Brush.LayerToPaint_Index != -1); + if (IsActive && !OtherActions) { + 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); + ImVec2 MouseDelta = io.MouseDelta; + real32 Delta = MouseDelta.x + MouseDelta.y; + v2 PrincipalCompUV = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos); + v2 LayerPos = Layer_TraverseForPoint(File, State, Memory, PrincipalCompUV, SortedCompArray, SortedLayerArray); + if (IsActivated) { + RenderQueue_AddBrush(State, LayerPos); + } else if (Delta != 0.0f) { + v2 PrevPos = State->Brush.PrevPos; + v2 Delta = PrevPos - LayerPos; + real32 Dist = sqrt(LengthSq(Delta)); + if (Dist > State->Brush.Spacing) { + RenderQueue_AddBrush(State, LayerPos); + } + } + State->UpdateFrame = true; + } + + if (IsDeactivated) { + RenderQueue_AddBlit(State); + } + } + if (State->Tool == tool_pen && State->Interact_Active == interact_type_none && State->MostRecentlySelectedLayer == -1) { + v2 CompUV = State->TempZoomRatio; + ImVec2 ScreenPoint = ImVec2(UI->CompPos.x + CompUV.x * UI->CompZoom.x, + UI->CompPos.y + CompUV.y * UI->CompZoom.y); + ImVec2 Vector = io.MousePos - ScreenPoint; + if (IsActive && !OtherActions) { + uint32 wcol = IM_COL32(00, 00, 80, 255); + draw_list->AddLine(ScreenPoint - Vector, io.MousePos, wcol, 2.0f); + draw_list->AddNgon(ScreenPoint, 2, wcol, 8, 2.0f); + } + if (IsDeactivated) { + State->HotkeyInput = hotkey_newlayer_shape; + if (fabs(Vector.x) > 5 && fabs(Vector.y) > 5) { + State->HotkeyExtra[0] = Vector.x; + State->HotkeyExtra[1] = Vector.y; + } + } + } + + if (ImGui::IsKeyDown(ImGuiKey_Z) && ImGui::IsWindowHovered()) { + if (IsActive) + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); + else + ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); + } + + real32 Distance = 0; + if (IsActive) { + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1.0f)) + Distance = io.MouseDelta.x + io.MouseDelta.y; + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) + Distance = 200; + } + if (Distance && ImGui::IsKeyDown(ImGuiKey_Z)) + { + if (io.KeyShift) + Distance *= -1; + UI->CompZoom.x += (Distance)*(real32)MainComp->Width/MainComp->Height; + UI->CompZoom.y += (Distance); + UI->CompPos.x -= ((Distance)*(real32)MainComp->Width/MainComp->Height)*State->TempZoomRatio.x; + UI->CompPos.y -= Distance*State->TempZoomRatio.y; + } + + ImGui::SetCursorScreenPos(ImVec2(ViewportMin.x, ViewportMin.y + ViewportScale.y - FontSize*1.5)); + + ImGui::Text("%.1f", 100.0f * (UI->CompZoom.x / MainComp->Width)); + if (State->MsgTime > 0) { + ImGui::SameLine(); + ImGui::SetCursorPosX((ViewportScale.x / 5)*4); + ImGui::Text(State->Msg); + State->MsgTime--; + } + + ImGui::SetCursorScreenPos(ViewportMin); + ImGui_Viewport_Toolbar(State, draw_list); + ImGui_Viewport_BrushUI(State, Memory, ViewportMin, ViewportMax, UI->CompPos, io, MainComp->Width, MainComp->Height); + + ImGui::End(); +} |