summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFox Caminiti <fox@foxcam.net>2023-01-03 16:40:57 -0500
committerFox Caminiti <fox@foxcam.net>2023-01-03 16:40:57 -0500
commita37ea807e93886e6a6ebc22a878a5649e97f015a (patch)
treec5af6ddc8544d97e38276bc1b83dbf1b29a8180b
parent375c120d30456738897c4bd775e38aa1db7d239c (diff)
shape layer work
-rw-r--r--src/bezier.cpp76
-rw-r--r--src/createcalls.cpp36
-rw-r--r--src/gl_calls.cpp121
-rw-r--r--src/imgui_ui.cpp664
-rw-r--r--src/imgui_ui_stable_diffusion.cpp2
-rw-r--r--src/imgui_ui_viewport.cpp715
-rw-r--r--src/include/functions.h5
-rw-r--r--src/include/gl_calls.h1
-rw-r--r--src/include/keybinds.h1
-rw-r--r--src/include/main.h31
-rw-r--r--src/layer.cpp32
-rw-r--r--src/main.cpp129
-rw-r--r--src/nanovg.cpp247
13 files changed, 1332 insertions, 728 deletions
diff --git a/src/bezier.cpp b/src/bezier.cpp
index 6fca5cc..ac89ed3 100644
--- a/src/bezier.cpp
+++ b/src/bezier.cpp
@@ -102,6 +102,46 @@ Bezier_Add(memory *Memory, memory_table_list TableName, property_channel *Proper
}
}
+// return all points
+static void Bezier_CubicCalcPointsCasteljauStep(void *Data, uint32 Size, uint32 *Increment, real32 x1, real32 y1, real32 x2, real32 y2, real32 x3, real32 y3, real32 x4, real32 y4, real32 tess_tol, int level)
+{
+ real32 dx = x4 - x1;
+ real32 dy = y4 - y1;
+ real32 d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
+ real32 d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
+ d2 = (d2 >= 0) ? d2 : -d2;
+ d3 = (d3 >= 0) ? d3 : -d3;
+ if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
+ {
+ real32 *Address = (real32 *)((uint8 *)Data + *Increment*Size);
+ *Address = x4;
+ *(Address + 1) = y4;
+ *(Address + 2) = 0;
+ *Increment += 1;
+ }
+ else if (level < 10)
+ {
+ real32 x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
+ real32 x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
+ real32 x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
+ real32 x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
+ real32 x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
+ real32 x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
+ Bezier_CubicCalcPointsCasteljauStep(Data, Size, Increment, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
+ Bezier_CubicCalcPointsCasteljauStep(Data, Size, Increment, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
+ }
+}
+
+uint32 Bezier_CubicCalcPoints(v2 p1, v2 p2, v2 p3, v2 p4, void *Data, uint32 Size)
+{
+ uint32 Increment = 0;
+ real32 tess_tol = TESS_TOL;
+ void *Pointer = Data;
+ Bezier_CubicCalcPointsCasteljauStep(Pointer, Size, &Increment, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);
+ return Increment;
+}
+
+
#if 0
// A modified version of the bezier code in ImGui with extra features for bitmap and path interaction.
@@ -214,35 +254,6 @@ static void Bezier_CubicMinMaxCasteljauStep(v2 *p_min, v2 *p_max, real32 x1, rea
}
}
-// return all points
-static void Bezier_CubicCalcPointsCasteljauStep(void *Data, uint32 *Increment, real32 x1, real32 y1, real32 x2, real32 y2, real32 x3, real32 y3, real32 x4, real32 y4, real32 tess_tol, int level)
-{
- real32 dx = x4 - x1;
- real32 dy = y4 - y1;
- real32 d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
- real32 d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
- d2 = (d2 >= 0) ? d2 : -d2;
- d3 = (d3 >= 0) ? d3 : -d3;
- if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
- {
- *((real32 *)Data + *Increment*3) = x4;
- *((real32 *)Data + *Increment*3 + 1) = y4;
- *((real32 *)Data + *Increment*3 + 2) = 0;
- *Increment += 1;
- }
- else if (level < 10)
- {
- real32 x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
- real32 x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
- real32 x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
- real32 x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
- real32 x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
- real32 x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
- Bezier_CubicCalcPointsCasteljauStep(Data, Increment, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
- Bezier_CubicCalcPointsCasteljauStep(Data, Increment, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
- }
-}
-
real32 Bezier_CubicRatioOfPoint(v2 p1, v2 p2, v2 p3, v2 p4, v2 p)
{
real32 tess_tol = TESS_TOL;
@@ -254,13 +265,6 @@ real32 Bezier_CubicRatioOfPoint(v2 p1, v2 p2, v2 p3, v2 p4, v2 p)
return ratio;
}
-void Bezier_CubicCalcPoints(v2 p1, v2 p2, v2 p3, v2 p4, void *Data, uint32 *Increment)
-{
- real32 tess_tol = TESS_TOL;
- void *Pointer = Data;
- Bezier_CubicCalcPointsCasteljauStep(Pointer, Increment, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);
-}
-
// These functions will become more generalized as shapes are added.
static void
diff --git a/src/createcalls.cpp b/src/createcalls.cpp
index 8b0ebf7..4bdd6d7 100644
--- a/src/createcalls.cpp
+++ b/src/createcalls.cpp
@@ -610,6 +610,42 @@ Property_IsGraphSelected(memory *Memory, property_channel *Property, uint16 *Arr
}
static void
+Project_ShapeLayer_New(project_data *File, project_state *State, memory *Memory, v2 Point, v2 Vector)
+{
+ int TopOffset = Layer_GetTopOffset(File, Memory);
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ Layer_DeselectAll(File, State, Memory);
+ History_Entry_Commit(Memory, "New shape layer");
+ block_layer *Layer = Layer_Init(File, Memory);
+ Layer->IsShapeLayer = true;
+ Layer->x.CurrentValue = MainComp->Width / 2;
+ Layer->y.CurrentValue = MainComp->Height / 2;
+ if (File->Layer_Count == 1) {
+ Layer->Vertical_Offset = 11 - 1;
+ } else {
+ Layer->Vertical_Offset = TopOffset - 1;
+ }
+ Layer->Frame_Start = MainComp->Frame_Start;
+ Layer->Frame_End = MainComp->Frame_End;
+ Layer_Select(Memory, State, Memory_Block_LazyIndexAtAddress(Memory, F_Layers, Layer));
+
+ 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;
+ v2 BezierPoint[3] = { Point, V2(000, 000), V2(000, 000) };
+ interpolation_type Type = interpolation_type_linear;
+ if (Vector.x) {
+ BezierPoint[1] = Vector;
+ BezierPoint[2] = Vector * -1;
+ Type = interpolation_type_bezier;
+ }
+ Bezier->Point[0] = { 1, { BezierPoint[0], BezierPoint[1], BezierPoint[2] }, Type, 0 };
+
+ History_Entry_End(Memory);
+ State->UpdateFrame = true;
+}
+
+static void
Project_PaintLayer_New(project_data *File, project_state *State, memory *Memory)
{
int TopOffset = Layer_GetTopOffset(File, Memory);
diff --git a/src/gl_calls.cpp b/src/gl_calls.cpp
index 1cb408a..5007148 100644
--- a/src/gl_calls.cpp
+++ b/src/gl_calls.cpp
@@ -1,17 +1,27 @@
#include "gl_calls.h"
const char *DefaultVertexShaderSource = "#version 330 core\n"
-"layout (location = 0) in vec3 aPos;\n"
+"layout (location = 0) in vec3 Point;\n"
"layout (location = 1) in vec2 aTexCoord;\n"
"out vec2 TexCoord;\n"
"uniform int VertexMode;\n"
"uniform vec3 CompDimensions;\n"
+"uniform vec2 LayerDimensions;\n"
+"uniform vec2 Pos;\n"
+"uniform vec2 Anchor;\n"
+"uniform float Rad;\n"
+"uniform float Scale;\n"
"void main()\n"
"{\n"
" if (VertexMode == 0) {\n"
-" gl_Position = vec4(aPos, 1.0);\n"
+" gl_Position = vec4(Point, 1.0);\n"
"} else {\n"
-" gl_Position = vec4(vec2(aPos.x / CompDimensions.x, aPos.y / CompDimensions.y) * 2 - 1.0f, 0.0f, 1.0);\n"
+" vec2 XRotation = vec2(cos(Rad), sin(Rad));\n"
+" vec2 YRotation = vec2(sin(Rad), -cos(Rad));\n"
+" vec2 XAxis = (Point.x - (Anchor.x * LayerDimensions.x)) * Scale * XRotation;\n"
+" vec2 YAxis = (Point.y - (Anchor.y * LayerDimensions.y)) * -Scale * YRotation;\n"
+" vec2 LocalPoint = Pos + vec2(XAxis + YAxis);\n"
+" gl_Position = vec4(vec2(LocalPoint.x / CompDimensions.x, LocalPoint.y / CompDimensions.y) * 2 - 1.0f, 0.0f, 1.0);\n"
"}\n"
" TexCoord = aTexCoord;\n"
"}\0";
@@ -83,25 +93,24 @@ static void GL_InitDefaultVerts() {
1, 2, 3
};
- // Indices!
glGenVertexArrays(1, &DefaultVerts.VertexArrayObject);
-
glGenBuffers(1, &DefaultVerts.ElementBufferObject);
+
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, DefaultVerts.ElementBufferObject);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLIndices), GLIndices,
GL_STATIC_DRAW);
glGenBuffers(1, &DefaultVerts.VertexBufferObject);
- // Our vertices need to be stored in this buffer.
glBindBuffer(GL_ARRAY_BUFFER, DefaultVerts.VertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(GL_DefaultVertices), GL_DefaultVertices, GL_STATIC_DRAW);
- // position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
- // texture coordinate (note the last parameter's offset)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
+
+ glGenVertexArrays(1, &ShapeVerts.VertexArrayObject);
+ glGenBuffers(1, &ShapeVerts.VertexBufferObject);
}
void
@@ -156,6 +165,102 @@ GL_DeleteHWBuffer(gl_effect_layer *Test)
}
void
+GL_RasterizeShape(gl_effect_layer *TestL, gl_effect_layer *TestM, void *PointData, uint32 GL_PointCount,
+ layer_transforms T, int Width, int Height, int BytesPerPixel, void *EffectBitmapAddress, int L_Width, int L_Height)
+{
+ glBindTexture(GL_TEXTURE_2D, TestL->Texture);
+ int ByteFlag = (BytesPerPixel == 4) ? GL_RGBA : GL_RGBA16;
+ int ByteFlag2 = (BytesPerPixel == 4) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT;
+ glTexImage2D(GL_TEXTURE_2D, 0, ByteFlag, Width, Height, 0, GL_RGBA,
+ ByteFlag2, EffectBitmapAddress);
+
+ GL_UpdateTexture(TestL, EffectBitmapAddress, Width, Height, BytesPerPixel, 0);
+ GL_UpdateTexture(TestM, EffectBitmapAddress, Width, Height, BytesPerPixel, 1);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, TestM->FramebufferObject);
+
+ // stencil buffer
+ glEnable(GL_STENCIL_TEST);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ glStencilFunc(GL_ALWAYS, 0, 0xFF);
+ glStencilMask(0xff);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+
+ glUseProgram(DefaultShaderProgram);
+
+ int Uniform = glGetUniformLocation(DefaultShaderProgram, "VertexMode");
+ glUniform1i(Uniform, 1);
+ Uniform = glGetUniformLocation(DefaultShaderProgram, "FragmentMode");
+ glUniform1i(Uniform, 1);
+ Uniform = glGetUniformLocation(DefaultShaderProgram, "CompDimensions");
+ glUniform3f(Uniform, Width, Height, 0);
+ Uniform = glGetUniformLocation(DefaultShaderProgram, "LayerDimensions");
+ glUniform2f(Uniform, L_Width, L_Height);
+ Uniform = glGetUniformLocation(DefaultShaderProgram, "Pos");
+ glUniform2f(Uniform, T.x, T.y);
+ Uniform = glGetUniformLocation(DefaultShaderProgram, "Anchor");
+ glUniform2f(Uniform, T.ax, T.ay);
+ real32 Rad = (T.rotation * (PI / 180));
+ Uniform = glGetUniformLocation(DefaultShaderProgram, "Rad");
+ glUniform1f(Uniform, Rad);
+ Uniform = glGetUniformLocation(DefaultShaderProgram, "Scale");
+ glUniform1f(Uniform, T.scale);
+
+ glBindBuffer(GL_ARRAY_BUFFER, ShapeVerts.VertexBufferObject);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(real32) * 4 * GL_PointCount, PointData, GL_STATIC_DRAW);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
+ glEnableVertexAttribArray(1);
+
+ // stencil buffer
+ glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
+ glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
+ glDisable(GL_CULL_FACE);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, GL_PointCount);
+
+ // stencil buffer
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+ // default verts
+ glBindVertexArray(0);
+ Uniform = glGetUniformLocation(DefaultShaderProgram, "VertexMode");
+ glUniform1i(Uniform, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, DefaultVerts.VertexBufferObject);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GL_DefaultVertices), GL_DefaultVertices, GL_STATIC_DRAW);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
+ glEnableVertexAttribArray(1);
+
+ // stencil buffer
+ glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
+ glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
+
+ glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_INT, 0);
+
+ // stencil buffer
+ glDisable(GL_STENCIL_TEST);
+ glStencilMask(0xFF);
+ glStencilFunc(GL_ALWAYS, 0, 0xFF);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, TestM->FramebufferObject);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, TestL->FramebufferObject);
+ glBlitFramebuffer(0, 0, Width, Height, 0, 0, Width, Height,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBindFramebuffer(GL_FRAMEBUFFER, TestL->FramebufferObject);
+
+ glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, EffectBitmapAddress);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ // GL_DeleteHWBuffer(TestL);
+ // GL_DeleteHWBuffer(TestM);
+}
+
+void
GL_UpdateTexture(gl_effect_layer *Test, void *Data, uint16 Width, uint16 Height, uint16 BytesPerPixel, bool32 Multisample)
{
glViewport(0, 0, Width, Height);
diff --git a/src/imgui_ui.cpp b/src/imgui_ui.cpp
index 77a6712..1feb1e5 100644
--- a/src/imgui_ui.cpp
+++ b/src/imgui_ui.cpp
@@ -7,6 +7,7 @@
#include "imgui_ui_properties.cpp"
#include "imgui_ui_timeline.cpp"
+#include "imgui_ui_viewport.cpp"
#if DEBUG
#include "imgui_ui_debug.cpp"
@@ -92,7 +93,7 @@ ImGui_File(project_data *File, project_state *State, memory *Memory, ImGuiIO io,
if (ImGui::BeginPopup("sourcecontext")) {
if (ImGui::MenuItem("Create layer from source")) {
- State->HotkeyInput = hotkey_newlayerfromsource;
+ State->HotkeyInput = hotkey_newlayer_source;
}
ImGui::EndPopup();
}
@@ -115,6 +116,27 @@ ImGui_File(project_data *File, project_state *State, memory *Memory, ImGuiIO io,
ImGui::End();
}
+
+static void
+ImGui_Opt_Shape(shape_options *Opt)
+{
+ // TODO(fox): Combine with RGBA function?
+ char *Names[3] = { "Fill only", "Stroke only", "Fill and stroke" };
+ if (ImGui::BeginListBox("Shape render type")) {
+ for (int i = 0; i < 3; i++) {
+ if (ImGui::Selectable(Names[i], (Opt->Visibility == i))) {
+ Opt->Visibility = i;
+ }
+ }
+ ImGui::EndListBox();
+ }
+ ImGui::ColorEdit4("Fill color", (real32 *)&Opt->FillCol, ImGuiColorEditFlags_Float);
+ ImGui::ColorEdit4("Stroke color", (real32 *)&Opt->StrokeCol, ImGuiColorEditFlags_Float);
+ real32 StrokeWidthMin = 0;
+ real32 StrokeWidthMax = 1024;
+ ImGui::DragScalar("Stroke width", ImGuiDataType_Float, &Opt->StrokeWidth, 1, &StrokeWidthMin, &StrokeWidthMax, "%.3f");
+}
+
static void
ImGui_ColorPanel(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io)
{
@@ -173,646 +195,13 @@ ImGui_ColorPanel(project_data *File, project_state *State, ui *UI, memory *Memor
Brush_CalcBitmapAlphaFromSize(Memory, &State->Brush, 4);
State_BindBrushTexture(Memory, &State->Brush, 4);
}
+ } else if (State->Tool == tool_pen && State->MostRecentlySelectedLayer == -1) {
+ ImGui_Opt_Shape(&State->Pen.Opt);
}
ImGui::End();
}
-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;
- if (!Layer->IsPrecomp) {
- block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index);
- Width = Source->Width;
- Height = Source->Height;
- } else {
- block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, Layer->Block_Source_Index);
- Width = Comp->Width;
- Height = Comp->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);
- }
-
- 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, 255);
- 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))
- {
- // Point to zoom in on if Z is held
- State->TempZoomRatio = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos);
-
- if (State->Tool == tool_brush && State->Interact_Active != interact_type_brush && !ImGui::IsKeyDown(ImGuiKey_Z)) {
- 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_newpaintlayer;
- }
- }
-
- // Layer selection
- if (!ImGui::IsKeyDown(ImGuiKey_Z) && 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 (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();
-}
-
#include "keybinds.h"
static void
@@ -1051,6 +440,9 @@ ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Me
if (ImGui::IsKeyPressed(ImGuiKey_B)) {
State->Tool = tool_brush;
}
+ if (ImGui::IsKeyPressed(ImGuiKey_D)) {
+ State->Tool = tool_pen;
+ }
// NOTE(fox): File data not tracked on undo tree!
if (ImGui::IsKeyPressed(ImGuiKey_N)) {
if (io.KeyShift) {
diff --git a/src/imgui_ui_stable_diffusion.cpp b/src/imgui_ui_stable_diffusion.cpp
index a5c93c6..e6ef8cc 100644
--- a/src/imgui_ui_stable_diffusion.cpp
+++ b/src/imgui_ui_stable_diffusion.cpp
@@ -68,7 +68,7 @@ ImGui_SD_Thumbnail(project_data *File, project_state *State, ui *UI, memory *Mem
}
if (ImGui::BeginPopup("temptosource")) {
if (ImGui::MenuItem("Create layer from source")) {
- State->HotkeyInput = hotkey_newlayerfromsource;
+ State->HotkeyInput = hotkey_newlayer_source;
}
ImGui::EndPopup();
}
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();
+}
diff --git a/src/include/functions.h b/src/include/functions.h
index 23d741e..53920cb 100644
--- a/src/include/functions.h
+++ b/src/include/functions.h
@@ -16,6 +16,11 @@ static void Bezier_Interact_Evaluate(project_state *State, bezier_poin
// NOTE(fox): GraphZoomHeight and Y_Increment don't have to be specified if the Y value isn't needed, i.e. in Property_SortAll().
static void Bezier_Add(memory *Memory, memory_table_list TableName, property_channel *Property, bezier_point PointData, uint16 *ArrayLocation);
+uint32 Bezier_CubicCalcPoints(v2 p1, v2 p2, v2 p3, v2 p4, void *Data, uint32 Size);
+
+
+void GL_RasterizeShape(gl_effect_layer *TestL, gl_effect_layer *TestM, void *PointData, uint32 GL_PointCount,
+ layer_transforms T, int Width, int Height, int BytesPerPixel, void *EffectBitmapAddress, int L_Width, int L_Height);
static void ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io, sorted_file Sorted);
static void ImGui_PropertiesPanel(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, sorted_property_array *SortedPropertyStart, uint16 *SortedKeyframeArray);
diff --git a/src/include/gl_calls.h b/src/include/gl_calls.h
index fa0a00c..3c2bef0 100644
--- a/src/include/gl_calls.h
+++ b/src/include/gl_calls.h
@@ -10,6 +10,7 @@ struct gl_vertex_shader {
};
static default_gl_vertex_object DefaultVerts;
+static default_gl_vertex_object ShapeVerts;
static gl_vertex_shader GL_DefaultVertexObjects;
static uint32 DefaultVertexShader;
static uint32 DefaultShaderProgram;
diff --git a/src/include/keybinds.h b/src/include/keybinds.h
index 3fe5f64..7ea0499 100644
--- a/src/include/keybinds.h
+++ b/src/include/keybinds.h
@@ -30,6 +30,7 @@ static shortcut_entry ShortcutArray[] {
{ ImGuiKey_E, Mod_None, key_mode_all, "Step forward one frame" },
{ ImGuiKey_V, Mod_None, key_mode_all, "Move tool" },
{ ImGuiKey_B, Mod_None, key_mode_all, "Brush tool" },
+ { ImGuiKey_D, Mod_None, key_mode_all, "Pen tool" },
{ ImGuiKey_Space, Mod_None, key_mode_all, "Play scene" },
{ ImGuiKey_Delete, Mod_None, key_mode_all, "Delete selection (WIP)" },
{ ImGuiKey_S, Mod_Ctrl, key_mode_all, "Save" },
diff --git a/src/include/main.h b/src/include/main.h
index fd295cf..8590904 100644
--- a/src/include/main.h
+++ b/src/include/main.h
@@ -183,6 +183,23 @@ struct sorted_file
uint64 Source_SortSize;
};
+struct shape_options {
+ int Visibility;
+ v4 FillCol = {1, 1, 1, 1};
+ v4 StrokeCol = {1, 1, 1, 1};
+ float StrokeWidth;
+};
+
+struct shape_layer {
+ uint16 Block_Bezier_Index[MAX_KEYFRAME_BLOCKS];
+ uint16 Block_Bezier_Count;
+ uint16 Point_Count;
+ bool32 IsClosed;
+ int Width;
+ int Height;
+ shape_options Opt;
+};
+
enum timeline_mode
{
timeline_mode_default,
@@ -190,7 +207,7 @@ enum timeline_mode
};
struct pen_state {
- bool32 IsActive;
+ shape_options Opt;
};
struct brush_state
@@ -251,8 +268,9 @@ struct interact_transform
enum hotkey_input
{
hotkey_none,
- hotkey_newpaintlayer,
- hotkey_newlayerfromsource,
+ hotkey_newlayer_source,
+ hotkey_newlayer_paint,
+ hotkey_newlayer_shape,
hotkey_deletelayer,
hotkey_undo,
hotkey_redo,
@@ -360,6 +378,7 @@ struct project_state
int32 PreviewSource = -1;
hotkey_input HotkeyInput;
+ real32 HotkeyExtra[4]; // bloat?
void *Dump1;
void *Dump2;
@@ -564,6 +583,7 @@ struct block_layer {
uint8 Occupied;
bool32 IsPrecomp;
+ bool32 IsShapeLayer;
bool32 Precomp_Toggled;
// NOTE(fox): References a precomp index if IsPrecomp is true, not a source index.
uint16 Block_Source_Index;
@@ -571,12 +591,11 @@ struct block_layer {
// References the precomp that the layer belongs to.
uint16 Block_Composition_Index;
- uint16 Block_Mask_Index[MAX_MASKS];
- uint16 Block_Mask_Count;
-
uint16 Block_Effect_Index[MAX_EFFECTS];
uint16 Block_Effect_Count;
+ shape_layer Shape;
+
blend_mode BlendMode;
union
diff --git a/src/layer.cpp b/src/layer.cpp
index 41b52cc..c3d3d16 100644
--- a/src/layer.cpp
+++ b/src/layer.cpp
@@ -94,7 +94,7 @@ Layer_GetTopOffset(project_data *File, memory *Memory)
static void
Layer_UpdateMasksEffects(project_state *State, block_layer *Layer, memory *Memory, void *EffectBitmapAddress,
- int Width, int Height, int BytesPerPixel)
+ int Width, int Height, int BytesPerPixel, int *L_Width, int *L_Height)
{
uint64 Size = Width*Height*BytesPerPixel;
@@ -106,6 +106,22 @@ Layer_UpdateMasksEffects(project_state *State, block_layer *Layer, memory *Memor
GL_UpdateTexture(&TestL, EffectBitmapAddress, Width, Height, BytesPerPixel, 0);
GL_UpdateTexture(&TestM, EffectBitmapAddress, Width, Height, BytesPerPixel, 1);
+ if (Layer->IsShapeLayer) {
+ block_bezier *Bezier = (block_bezier *)Memory_Block_AddressAtIndex(Memory, F_Bezier, Layer->Shape.Block_Bezier_Index[0]);
+ void *Data = Memory_PushScratch(Memory, sizeof(nvg_point) * 128);
+ uint32 NumberOfVerts = NVG_FlattenPath(Memory, Bezier, 3, (nvg_point *)Data, L_Width, L_Height);
+ void *Data2 = Memory_PushScratch(Memory, sizeof(real32) * 3 * 256);
+ uint32 GL_PointCount = NVG_ExpandStroke(Memory, Bezier, NumberOfVerts, (nvg_point *)Data, (real32 *)Data2);
+
+ layer_transforms T = Layer_GetTransforms(Layer);
+ GL_RasterizeShape(&TestL, &TestM, Data2, GL_PointCount, T, Width, Height, BytesPerPixel, EffectBitmapAddress, *L_Width, *L_Height);
+
+ Memory_PopScratch(Memory, sizeof(real32) * 3 * 256);
+ Memory_PopScratch(Memory, sizeof(nvg_point) * 128);
+
+ // Bitmap_StencilAlpha(SourceBitmapAddress, EffectBitmapAddress, Source->BytesPerPixel, Size);
+ }
+
for (int i = 0; i < Layer->Block_Effect_Count; i++)
{
block_effect Effect = *(block_effect *)Memory_Block_AddressAtIndex(Memory, F_Effects, Layer->Block_Effect_Index[i]);
@@ -140,20 +156,6 @@ Layer_UpdateMasksEffects(project_state *State, block_layer *Layer, memory *Memor
Memory_PopScratch(Memory, Size);
}
}
- /*
- if (Layer->NumberOfMasks) {
- for (int i = 0; i < Layer->NumberOfMasks; i++) {
- file_mask_header *MaskHeader = (file_mask_header *)((uint8 *)Layer + sizeof(file_layer) + MaskOffset);
- if (MaskHeader->IsClosed && MaskHeader->IsToggled) {
- mask_point *Point = (mask_point *)((uint8 *)MaskHeader + sizeof(file_mask_header));
- Mask_TriangulateAndRasterize(TestM, TestL, Memory, MaskHeader, Point, Source->Width, Source->Height, Source->BytesPerPixel, EffectBitmapAddress);
- }
- }
- Bitmap_StencilAlpha(SourceBitmapAddress, EffectBitmapAddress, Source->BytesPerPixel, Size);
- }
-
- Layer->OutputBitmapLocation = EffectBitmapAddress;
- */
GL_DeleteHWBuffer(&TestL);
GL_DeleteHWBuffer(&TestM);
diff --git a/src/main.cpp b/src/main.cpp
index 88bc3ef..9856143 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -85,6 +85,7 @@ uint32 BitmapFill = 0x00000001;
#include "undo.cpp"
#include "io.cpp"
#include "sorted.cpp"
+#include "nanovg.cpp"
#include "layer.cpp"
#include "strings.cpp"
#include "threading.cpp"
@@ -352,7 +353,16 @@ Render_Comp(project_data *File, project_state *State, memory *Memory, sorted_fil
void *RenderAddress = NULL; // result of masking, effects, and intermediate paint stroke
int Width = 0, Height = 0, BytesPerPixel = 0;
- if (!Layer->IsPrecomp) {
+ 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) {
@@ -413,20 +423,13 @@ Render_Comp(project_data *File, project_state *State, memory *Memory, sorted_fil
Width = Source->Width;
Height = Source->Height;
BytesPerPixel = Source->BytesPerPixel;
- } else {
- block_composition *Precomp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, Layer->Block_Source_Index);
- 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;
}
- Assert(BitmapAddress);
-
uint64 ScratchSize = Width * Height * BytesPerPixel;
RenderAddress = Memory_PushScratch(Memory, ScratchSize);
- Memory_Copy((uint8 *)RenderAddress, (uint8 *)BitmapAddress, 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?
@@ -436,18 +439,28 @@ Render_Comp(project_data *File, project_state *State, memory *Memory, sorted_fil
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->Block_Effect_Count || Layer->Block_Mask_Count) {
- Layer_UpdateMasksEffects(State, Layer, Memory, RenderAddress, Width, Height, BytesPerPixel);
- }
- 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};
+ if (Layer->IsShapeLayer) {
+ Layer_UpdateMasksEffects(State, Layer, Memory, RenderAddress, Comp->Width, Comp->Height, Comp->BytesPerPixel, &Width, &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);
+ 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);
+ // }
+
}
}
@@ -750,8 +763,13 @@ int main(int argc, char *argv[]) {
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);
@@ -771,10 +789,55 @@ int main(int argc, char *argv[]) {
State->UpdateFrame = true;
// State->MostRecentlySelectedLayer = 0;
#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);
+ // 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
@@ -809,8 +872,22 @@ int main(int argc, char *argv[]) {
// NextEffect->Index -= 1;
// History_Entry_End(Memory);
// } break;
- case hotkey_newpaintlayer: { Project_PaintLayer_New(File, State, &Memory); } break;
- case hotkey_newlayerfromsource:
+ case hotkey_newlayer_paint: {
+ Project_PaintLayer_New(File, State, &Memory);
+ } break;
+ case hotkey_newlayer_shape:
+ {
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(&Memory, F_Precomps, File->PrincipalCompIndex);
+ v2 Point = State->TempZoomRatio * V2(MainComp->Width, MainComp->Height);
+ v2 Vector = V2(0, 0);
+ if (State->HotkeyExtra[0] != 0) {
+ Vector = V2(State->HotkeyExtra[0], State->HotkeyExtra[1]);
+ State->HotkeyExtra[0] = 0;
+ State->HotkeyExtra[1] = 0;
+ }
+ Project_ShapeLayer_New(File, State, &Memory, Point, Vector);
+ } break;
+ case hotkey_newlayer_source:
{
Source_UICreateButton(File, State, &Memory);
State->UpdateKeyframes = true;
diff --git a/src/nanovg.cpp b/src/nanovg.cpp
new file mode 100644
index 0000000..06b2910
--- /dev/null
+++ b/src/nanovg.cpp
@@ -0,0 +1,247 @@
+
+enum nvg_point_flags
+{
+ NVG_PT_CORNER = 0x01,
+ NVG_PT_LEFT = 0x02,
+ NVG_PT_BEVEL = 0x04,
+ NVG_PR_INNERBEVEL = 0x08,
+};
+
+struct nvg_point
+{
+ real32 x;
+ real32 y;
+ real32 dx;
+ real32 dy;
+ real32 Length;
+ real32 dmx;
+ real32 dmy;
+ uint8 Flags;
+};
+
+static real32
+NVG_Normalize(real32 *x, float* y)
+{
+ real32 d = sqrtf((*x)*(*x) + (*y)*(*y));
+ if (d > 1e-6f) {
+ real32 id = 1.0f / d;
+ *x *= id;
+ *y *= id;
+ }
+ return d;
+}
+
+static real32 *
+NVG_Point(real32 *StrokeData, real32 x, real32 y, real32 u, real32 v)
+{
+ *(v4 *)StrokeData = V4(x, y, u, v);
+ return StrokeData + 4;
+}
+
+static void NVG_ChooseBevel(int bevel, nvg_point *p0, nvg_point *p1, float w,
+ float* x0, float* y0, float* x1, float* y1)
+{
+ if (bevel) {
+ *x0 = p1->x + p0->dy * w;
+ *y0 = p1->y - p0->dx * w;
+ *x1 = p1->x + p1->dy * w;
+ *y1 = p1->y - p1->dx * w;
+ } else {
+ *x0 = p1->x + p1->dmx * w;
+ *y0 = p1->y + p1->dmy * w;
+ *x1 = p1->x + p1->dmx * w;
+ *y1 = p1->y + p1->dmy * w;
+ }
+}
+
+static int NVG_Clampi(int a, int mn, int mx) { return a < mn ? mn : (a > mx ? mx : a); }
+
+static real32 * NVG_RoundJoin(nvg_point *Point, nvg_point *NextPoint, real32 *StrokeData,
+ float lw, float rw, float lu, float ru, int ncap)
+{
+ int i, n;
+ float dlx0 = Point->dy;
+ float dly0 = -Point->dx;
+ float dlx1 = NextPoint->dy;
+ float dly1 = -NextPoint->dx;
+
+ if (NextPoint->Flags & NVG_PT_LEFT) {
+ float lx0,ly0,lx1,ly1,a0,a1;
+ NVG_ChooseBevel(NextPoint->Flags & NVG_PR_INNERBEVEL, Point, NextPoint, lw, &lx0,&ly0, &lx1,&ly1);
+ a0 = atan2f(-dly0, -dlx0);
+ a1 = atan2f(-dly1, -dlx1);
+ if (a1 > a0) a1 -= PI*2;
+
+ StrokeData = NVG_Point(StrokeData, lx0, ly0, 0, 0);
+ StrokeData = NVG_Point(StrokeData, NextPoint->x - dlx0*rw, NextPoint->y - dly0*rw, 0, 0);
+
+ n = NVG_Clampi((int)ceilf(((a0 - a1) / PI) * ncap), 2, ncap);
+ for (i = 0; i < n; i++) {
+ float u = i/(float)(n-1);
+ float a = a0 + u*(a1-a0);
+ float rx = NextPoint->x + cosf(a) * rw;
+ float ry = NextPoint->y + sinf(a) * rw;
+ StrokeData = NVG_Point(StrokeData, NextPoint->x, NextPoint->y, 0, 0);
+ StrokeData = NVG_Point(StrokeData, rx, ry, 0, 0);
+ }
+
+ StrokeData = NVG_Point(StrokeData, lx1, ly1, 0, 0);
+ StrokeData = NVG_Point(StrokeData, NextPoint->x - dlx1*rw, NextPoint->y - dly1*rw, 0, 0);
+ }
+ return StrokeData;
+}
+
+static real32 * NVG_RoundCap(nvg_point * Point, real32 *StrokeData,
+ float dx, float dy, float w, int ncap,
+ float u0, float u1, int Mode)
+{
+ int i;
+ float px = Point->x;
+ float py = Point->y;
+ float dlx = dy;
+ float dly = -dx;
+ float Flip = (Mode == 0) ? 1 : -1;
+ if (Mode != 0) {
+ StrokeData = NVG_Point(StrokeData, px + dlx*w, py + dly*w, 0, 0);
+ StrokeData = NVG_Point(StrokeData, px - dlx*w, py - dly*w, 0, 0);
+ }
+ for (i = 0; i < ncap; i++) {
+ float a = i/(float)(ncap-1)*PI;
+ float ax = cosf(a) * w, ay = sinf(a) * w;
+ v2 OuterPoint = V2(px - dlx*ax - dx*ay*Flip, py - dly*ax - dy*ay*Flip);
+ v2 InnerPoint = V2(px, py);
+ if (Mode == 0) {
+ StrokeData = NVG_Point(StrokeData, OuterPoint.x, OuterPoint.y, 0, 0);
+ StrokeData = NVG_Point(StrokeData, InnerPoint.x, InnerPoint.y, 0, 0);
+ } else {
+ StrokeData = NVG_Point(StrokeData, InnerPoint.x, InnerPoint.y, 0, 0);
+ StrokeData = NVG_Point(StrokeData, OuterPoint.x, OuterPoint.y, 0, 0);
+ }
+ }
+ if (Mode == 0) {
+ StrokeData = NVG_Point(StrokeData, px + dlx*w, py + dly*w, 0, 0);
+ StrokeData = NVG_Point(StrokeData, px - dlx*w, py - dly*w, 0, 0);
+ }
+ return 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(void *Memory, block_bezier *Bezier, int PointCount, nvg_point *PointData, int *Width, int *Height)
+{
+ uint32 NumberOfVerts = 0;
+ nvg_point *PointPlayhead = PointData;
+ for (int i = 0; i < PointCount; i++) {
+ if (i == 0 || Bezier->Point[i].Type == interpolation_type_linear) {
+ *(v2 *)PointPlayhead = Bezier->Point[i].Pos[0];
+ if (i != 0 && i != (PointCount - 1)) {
+ PointPlayhead->Flags |= NVG_PT_CORNER;
+ }
+ PointPlayhead++;
+ NumberOfVerts++;
+ } else if (Bezier->Point[i].Type == interpolation_type_bezier) {
+ v2 Pos[4] = { Bezier->Point[i].Pos[0], Bezier->Point[i].Pos[1], Bezier->Point[i+1].Pos[2], Bezier->Point[i+1].Pos[0] };
+ Pos[1] = Pos[1] + Pos[0];
+ Pos[2] = Pos[2] + Pos[3];
+ NumberOfVerts += Bezier_CubicCalcPoints(Pos[3], Pos[2], Pos[1], Pos[0], PointPlayhead, sizeof(nvg_point));
+ // The point at the end is also returned, so we remove it.
+ NumberOfVerts--;
+ PointPlayhead--;
+ } else {
+ Assert(0);
+ }
+ }
+ nvg_point *Point = &PointData[NumberOfVerts - 1];
+ nvg_point *NextPoint = PointData;
+ v2 Min = V2(10000, 10000);
+ v2 Max = V2(-10000, -10000);
+ for (int i = 0; i < NumberOfVerts; i++) {
+ Point->dx = NextPoint->x - Point->x;
+ Point->dy = NextPoint->y - Point->y;
+ Point->Length = NVG_Normalize(&Point->dx, &Point->dy);
+ if (Point->x > Max.x)
+ Max.x = Point->x;
+ if (Point->x < Min.x)
+ Min.x = Point->x;
+ if (Point->y > Max.y)
+ Max.y = Point->y;
+ if (Point->y < Min.y)
+ Min.y = Point->y;
+ Point = NextPoint++;
+ }
+ *Width = Max.x - Min.x;
+ *Height = Max.y - Min.y;
+ return NumberOfVerts;
+}
+
+real32 MiterLimit = 2.4f;
+
+static uint32
+NVG_ExpandStroke(void *Memory, block_bezier *Bezier, int NumberOfVerts, nvg_point *PointData, real32 *StrokeData)
+{
+ real32 Width = 50 * 0.5;
+ nvg_point *Point = PointData;
+ nvg_point *NextPoint = &PointData[1];
+ int ncap = 12;
+ real32 *StartingStrokeData = StrokeData;
+
+ StrokeData = NVG_RoundCap(Point, StrokeData, Point->dx, Point->dy, Width, ncap, 0.5, 0.5, 0);
+
+ for (int i = 1; i < (NumberOfVerts - 1); i++) {
+
+ real32 dlx0, dly0, dlx1, dly1, dmr2, cross, limit;
+ dlx0 = Point->dy;
+ dly0 = -Point->dx;
+ dlx1 = NextPoint->dy;
+ dly1 = -NextPoint->dx;
+
+ // Calculate extrusions
+ NextPoint->dmx = (dlx0 + dlx1) * 0.5f;
+ NextPoint->dmy = (dly0 + dly1) * 0.5f;
+ dmr2 = NextPoint->dmx*NextPoint->dmx + NextPoint->dmy*NextPoint->dmy;
+ if (dmr2 > 0.000001f) {
+ float scale = 1.0f / dmr2;
+ if (scale > 600.0f) {
+ scale = 600.0f;
+ }
+ NextPoint->dmx *= scale;
+ NextPoint->dmy *= scale;
+ }
+
+ // Keep track of left turns.
+ cross = NextPoint->dx * Point->dy - Point->dx * NextPoint->dy;
+ if (cross > 0.0f) {
+ NextPoint->Flags |= NVG_PT_LEFT;
+ }
+
+ // Calculate if we should use bevel or miter for inner join.
+ // limit = nvg__maxf(1.01f, nvg__minf(p0->len, p1->len) * iw);
+ // if ((dmr2 * limit*limit) < 1.0f)
+ // p1->flags |= NVG_PR_INNERBEVEL;
+
+ // Check to see if the corner needs to be beveled.
+ if (NextPoint->Flags & NVG_PT_CORNER) {
+ // if ((dmr2 * MiterLimit*MiterLimit) < 1.0f) r // || lineJoin == NVG_BEVEL || lineJoin == NVG_ROUND) {
+ NextPoint->Flags |= NVG_PT_BEVEL;
+ // }
+ }
+
+ if ((NextPoint->Flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) {
+ // if (lineJoin == NVG_ROUND) {
+ StrokeData = NVG_RoundJoin(Point, NextPoint, StrokeData, Width, Width, 0.5, 0.5, ncap);
+ // }
+ } else {
+ StrokeData = NVG_Point(StrokeData, NextPoint->x + (NextPoint->dmx * Width), NextPoint->y + (NextPoint->dmy * Width), 0, 0);
+ StrokeData = NVG_Point(StrokeData, NextPoint->x - (NextPoint->dmx * Width), NextPoint->y - (NextPoint->dmy * Width), 0, 0);
+ }
+
+ Point = NextPoint++;
+ }
+
+ StrokeData = NVG_RoundCap(NextPoint, StrokeData, Point->dx, Point->dy, Width, ncap, 0.5, 0.5, 1);
+
+ int GL_PointCount = (StrokeData - StartingStrokeData) / 4;
+
+ return GL_PointCount;
+}