From bc5375149c0ecb416848a2d3657ea41ae97177b3 Mon Sep 17 00:00:00 2001 From: Fox Caminiti Date: Wed, 10 Aug 2022 21:24:03 -0400 Subject: path rasterization started with opengl --- bezier.cpp | 157 ++++++++++++++++++++++++++++++++++ createcalls.cpp | 194 ++++++++++++++++++++++++++++++++++++------ defines.h | 1 + effects.cpp | 12 ++- functions.h | 8 ++ gl_calls.cpp | 134 +++++++++++++++++++---------- main.cpp | 6 +- main.h | 37 ++++++-- my_imgui_internal_widgets.cpp | 104 +++++----------------- my_imgui_internal_widgets.h | 5 +- my_imgui_widgets.cpp | 147 ++++++++++++++++++-------------- my_math.h | 22 +++++ 12 files changed, 594 insertions(+), 233 deletions(-) create mode 100644 bezier.cpp diff --git a/bezier.cpp b/bezier.cpp new file mode 100644 index 0000000..4e0cb5c --- /dev/null +++ b/bezier.cpp @@ -0,0 +1,157 @@ +// A modified version of the bezier code in ImGui with extra features for bitmap and path interaction. + +// Function to convert a ratio back into a point for the bezier handles. +v2 Line_RatioToPoint(v2 a, v2 b, real32 ratio) +{ + v2 ab_dir = b - a; + real32 ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y; + real32 dot = ratio*ab_len_sqr; + return a + ab_dir * dot / V2(ab_len_sqr); +} + +// The ratio here is just the dot product divided by the squared length. +v2 Bezier_LineClosestPoint2(v2 a, v2 b, v2 p, real32 *ratio) +{ + v2 ap = p - a; + v2 ab_dir = b - a; + real32 dot = ap.x * ab_dir.x + ap.y * ab_dir.y; + if (dot < 0.0f) { + *ratio = 0.0f; + return a; + } + real32 ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y; + if (dot > ab_len_sqr) { + *ratio = 1.0f; + return b; + } + *ratio = dot / ab_len_sqr; + return a + ab_dir * dot / ab_len_sqr; +} + +// Following the algorithm, we take the ratio from the _leftmost_ point in each +// subdivision of the cubic spline until we're within tess_tol, and then we +// interpolate it with the subdivision's rightmost point in the ClosestPoint call. +// The pow(0.5, level) represents the ratio of the next subdivision's leftmost +// point (AKA the rightmost point of the current subdivision). + +// finds the point closest to p and also fills out its ratio along the curve +static void Bezier_CubicClosestPointCasteljauStep(v2 p, v2 *p_closest, real32 ratio, real32 *r_closest, v2 *p_last, real32 *p_closest_dist2, + 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)) + { + v2 p_current = V2(x4, y4); + real32 added_ratio; + v2 p_line = Bezier_LineClosestPoint2(*p_last, p_current, p, &added_ratio); + real32 dist2 = LengthSq(p - p_line); + if (dist2 < *p_closest_dist2) + { + *p_closest = p_line; + *p_closest_dist2 = dist2; + *r_closest = ratio + pow(0.5, level)*added_ratio; + } + *p_last = p_current; + } + 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_CubicClosestPointCasteljauStep(p, p_closest, ratio, r_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); + Bezier_CubicClosestPointCasteljauStep(p, p_closest, ratio + pow(0.5, level+1), r_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); + } +} + +// finds the min/max bounds of the curve +static void Bezier_CubicMinMaxCasteljauStep(v2 *p_min, v2 *p_max, 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)) + { + v2 p_current = V2(x4, y4); + if (p_current.x < p_min->x) { + p_min->x = p_current.x; + } + if (p_current.y < p_min->y) { + p_min->y = p_current.y; + } + if (p_current.x > p_max->x) { + p_max->x = p_current.x; + } + if (p_current.y > p_max->y) { + p_max->y = p_current.y; + } + } + 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_CubicMinMaxCasteljauStep(p_min, p_max, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); + Bezier_CubicMinMaxCasteljauStep(p_min, p_max, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); + } +} + +// 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; + v2 p_last = p1; + v2 p_closest; + real32 p_closest_dist2 = FLT_MAX; + real32 ratio = 0; + Bezier_CubicClosestPointCasteljauStep(p, &p_closest, 0, &ratio, &p_last, &p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0); + 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); +} diff --git a/createcalls.cpp b/createcalls.cpp index 5144cf0..4ddaa7e 100644 --- a/createcalls.cpp +++ b/createcalls.cpp @@ -180,6 +180,96 @@ STB_LoadStill(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory) return Bitmap; } +static void +Mask_RasterizePoints(mask *Mask) +{ + Mask->NumberOfVerts = 0; + for (int i = 0; i < Mask->NumberOfPoints; i++) { + mask_point Point0 = Mask->Point[i]; + mask_point Point1 = Mask->Point[i+1]; + if (i+1 == Mask->NumberOfPoints) + Point1 = Mask->Point[0]; + + if (Point0.HandleBezier || Point1.HandleBezier) { + Bezier_CubicCalcPoints(Point0.Pos, Point0.Pos + Point0.TangentRight, Point1.Pos + Point1.TangentLeft, Point1.Pos, + Mask->TriangulatedPointCache, &Mask->NumberOfVerts); + } else { + real32 *Data = (real32 *)Mask->TriangulatedPointCache + Mask->NumberOfVerts*3; + *(Data++) = Point0.Pos.x; + *(Data++) = Point0.Pos.y; + *(Data++) = 0; + Mask->NumberOfVerts += 1; + } + } +} + +static void +Mask_TriangulateAndRasterize(memory *Memory, project_layer *Layer, mask *Mask) +{ + if (!Mask->TriangulatedPointCache) { + Mask->TriangulatedPointCache = AllocateMemory(Memory, 50*1024, P_VectorPoints); + } + Mask_RasterizePoints(Mask); + + gl_vertex_shader VertData; + gl_effect_layer Test = Layer->BitmapInfo.Test; + + glBindFramebuffer(GL_FRAMEBUFFER, Test.FramebufferObject); + + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + glStencilFunc(GL_ALWAYS, 1, 0xFF); // always write + glStencilMask(0xff); // allow writing; ANDs any writes to the stencil buffer with this + + glUseProgram(MaskShaderProgram); + + // secondary VBO + glGenVertexArrays(1, &VertData.VertexArrayObject); + glBindVertexArray(VertData.VertexArrayObject); + glBindBuffer(GL_ARRAY_BUFFER, VertData.VertexBufferObject); + glBufferData(GL_ARRAY_BUFFER, Mask->NumberOfVerts*3*sizeof(real32), Mask->TriangulatedPointCache, GL_STREAM_DRAW); + + // position attribute + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + int Scale = glGetUniformLocation(MaskShaderProgram, "CompDimensions"); + glUniform3f(Scale, (real32)Layer->Source->Info.Width, (real32)Layer->Source->Info.Height, 0); + + glDrawArrays(GL_TRIANGLE_FAN, 0, Mask->NumberOfVerts); + + glBindVertexArray(0); + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilMask(0x00); // disables stencil writing + + glBindRenderbuffer(GL_RENDERBUFFER, Test.Color_Renderbuffer); + glUseProgram(DefaultShaderProgram); + + // // Switch to main buffer + glBindBuffer(GL_ARRAY_BUFFER, DefaultVerts.VertexBufferObject); + glBufferData(GL_ARRAY_BUFFER, sizeof(DefaultVertices), 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); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + + glDisable(GL_STENCIL_TEST); + glStencilMask(0xFF); + glStencilFunc(GL_ALWAYS, 0, 0xFF); + + uint8 *Data = (uint8 *)Layer->BitmapInfo.BitmapBuffer; + glReadPixels(0, 0, Layer->Source->Info.Width, Layer->Source->Info.Height, GL_RGBA, GL_UNSIGNED_BYTE, &Data[0]); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + static void Layer_UpdateBitmap(project_layer *Layer, memory *Memory, int32 CurrentFrame) { @@ -199,7 +289,16 @@ Layer_UpdateBitmap(project_layer *Layer, memory *Memory, int32 CurrentFrame) { void *DestBuffer = BitmapInfo->BitmapBuffer; uint64 Size = Bitmap_CalcUnpackedBytes(Source->Info.Width, Source->Info.Height, Source->Info.BytesPerPixel); Bitmap_CopyToPointer(Bitmap->Data, DestBuffer, BytesPerPixel, Size); + TestGL_InitTexture(&BitmapInfo->Test, DestBuffer, Width, Height); + + if (Layer->NumberOfMasks) { + for (int i = 0; i < Layer->NumberOfMasks; i++) { + mask *Mask = &Layer->Mask[i]; + Mask_TriangulateAndRasterize(Memory, Layer, Mask); + } + } + for (int i = 0; i < Layer->NumberOfEffects; i++) { if (Layer->Effect[i]->IsActive) @@ -255,6 +354,25 @@ Layer_LocalToScreenSpace(project_layer *Layer, ui *UI, comp_buffer CompBuffer, v return ImVec2(ScreenPoint.x, ScreenPoint.y); } +static v2 +Layer_ScreenSpaceToLocal(project_layer *Layer, ui *UI, comp_buffer CompBuffer, ImVec2 ViewportMin, ImVec2 Point) +{ + source *Source = Layer->Source; + v2 CompUV = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, Point); + v2 LayerUV = CompUVToLayerUV(Layer, &CompBuffer, CompUV); + return V2(LayerUV.x * Source->Info.Width, LayerUV.y * Source->Info.Height); +} + +static void +Mask_AddPointToLine(mask *Mask, uint16 Index, v2 Pos) +{ + for (int i = Mask->NumberOfPoints - 1; i > Index; i--) { + Mask->Point[i+1] = Mask->Point[i]; + } + mask_point *PointToAdd = &Mask->Point[Index+1]; + PointToAdd->Pos = Pos; + Mask->NumberOfPoints++; +} static void Mask_PushPoint(mask *Mask, v2 Pos) @@ -265,22 +383,31 @@ Mask_PushPoint(mask *Mask, v2 Pos) } static void -Mask_AddPointToCurve(mask *Mask, ImVec2 Pos, ImVec2 TangentLeft, ImVec2 TangentRight, - ImVec2 Ratio0, ImVec2 Ratio1, uint16 Index) +Mask_AddPointToCurve(mask *Mask, uint16 Index, real32 ratio) { - mask_point *Point = &Mask->Point[Index]; - mask_point *NextPoint = &Mask->Point[Index+1]; + mask_point *Point0 = &Mask->Point[Index]; + mask_point *Point1 = &Mask->Point[Index+1]; + + v2 Point0_Pos_Right = Point0->Pos + Point0->TangentRight; + v2 Point1_Pos_Left = Point1->Pos + Point1->TangentLeft; + v2 Handle0_Half = Line_RatioToPoint(Point0->Pos, Point0_Pos_Right, ratio); + v2 Handle1_Half = Line_RatioToPoint(Point1_Pos_Left, Point1->Pos, ratio); + v2 Top_Half = Line_RatioToPoint(Point0_Pos_Right, Point1_Pos_Left, ratio); + v2 NewHandleLeft = Line_RatioToPoint(Handle0_Half, Top_Half, ratio); + v2 NewHandleRight = Line_RatioToPoint(Top_Half, Handle1_Half, ratio); + v2 NewPos = Line_RatioToPoint(NewHandleLeft, NewHandleRight, ratio); - Point->TangentRight = -V2(Ratio0); - NextPoint->TangentLeft = -V2(Ratio1); + Point0->TangentRight = -(Point0->Pos - Handle0_Half); + Point1->TangentLeft = -(Point1->Pos - Handle1_Half); for (int i = Mask->NumberOfPoints - 1; i > Index; i--) { Mask->Point[i+1] = Mask->Point[i]; } mask_point *PointToAdd = &Mask->Point[Index+1]; - PointToAdd->Pos = {Pos.x, Pos.y}; - PointToAdd->TangentLeft = {-TangentLeft.x, -TangentLeft.y}; - PointToAdd->TangentRight = {-TangentRight.x, -TangentRight.y}; + + PointToAdd->Pos = NewPos; + PointToAdd->TangentLeft = -(NewPos - NewHandleLeft); + PointToAdd->TangentRight = -(NewPos - NewHandleRight); Mask->NumberOfPoints++; } @@ -292,25 +419,36 @@ LoadTestFootage(project_data *File, project_state *State, memory *Memory) source *Source = &File->Source[0]; Layer_CreateFromSource(File, State, Memory, Source); SelectLayer(File->Layer[0], State, 0); - AddEffect(File->Layer[0], Memory, 3); - - // mask *Mask = &File->Layer[0]->Mask[0]; - // File->Layer[0]->NumberOfMasks = 1; - // Mask->Point[0].Pos = V2(200, 200); - // Mask->Point[1].Pos = V2(1280, 0); - // Mask->Point[2].Pos = V2(1280, 720); - // Mask->Point[3].Pos = V2(0, 720); - - // Mask->Point[0].TangentLeft = V2(-50, 0); - // Mask->Point[1].TangentLeft = V2(-50, 0); - // Mask->Point[2].TangentLeft = V2(-50, 0); - // Mask->Point[3].TangentLeft = V2(-50, 0); - - // Mask->Point[0].TangentRight = V2(50, 0); - // Mask->Point[1].TangentRight = V2(50, 0); - // Mask->Point[2].TangentRight = V2(50, 0); - // Mask->Point[3].TangentRight = V2(50, 0); - // Mask->NumberOfPoints = 4; + // AddEffect(File->Layer[0], Memory, 3); + + mask *Mask = &File->Layer[0]->Mask[0]; + File->Layer[0]->NumberOfMasks = 1; + Mask->Point[0].Pos = V2(200, 200); + Mask->Point[1].Pos = V2(200, 400); + Mask->Point[2].Pos = V2(200, 520); + Mask->Point[3].Pos = V2(1080, 520); + Mask->Point[4].Pos = V2(1080, 200); + + Mask->Point[0].TangentLeft = V2(-50, 0); + Mask->Point[1].TangentLeft = V2(-50, 0); + Mask->Point[2].TangentLeft = V2(-50, 0); + Mask->Point[3].TangentLeft = V2(-50, 0); + Mask->Point[4].TangentLeft = V2(-50, 0); + + Mask->Point[0].TangentRight = V2(50, 0); + Mask->Point[1].TangentRight = V2(50, 0); + Mask->Point[2].TangentRight = V2(50, 0); + Mask->Point[3].TangentRight = V2(50, 0); + Mask->Point[4].TangentRight = V2(50, 0); + + Mask->Point[0].HandleBezier = true; + Mask->Point[1].HandleBezier = true; + Mask->Point[2].HandleBezier = true; + Mask->Point[3].HandleBezier = true; + Mask->Point[4].HandleBezier = true; + + + Mask->NumberOfPoints = 5; // property_channel *Property = &File->Layer[0]->x; // ManualKeyframeInsertF(Property, Memory, 1, 500); diff --git a/defines.h b/defines.h index 947a8f0..b87fc6d 100644 --- a/defines.h +++ b/defines.h @@ -21,6 +21,7 @@ typedef double real64; #define NORMALIZED_REAL_MIN { 0.0f } #define NORMALIZED_REAL_MAX { 1.0f } +#define TESS_TOL 1.5f // Level of tesselation for bezier calculations; ImGui's default value // All of these MIN/MAX values are arbitrarily chosen; they can probably be // increased if the user requires it. diff --git a/effects.cpp b/effects.cpp index d006941..67f0887 100644 --- a/effects.cpp +++ b/effects.cpp @@ -201,7 +201,8 @@ Levels(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory, property_c Assert(&BitmapInfo->Test); gl_effect_layer Test = BitmapInfo->Test; - glBindRenderbuffer(GL_RENDERBUFFER, Test.RBO); + // glBindRenderbuffer(GL_RENDERBUFFER, Test.RBO); + /* glUseProgram(TGL.ShaderProgram); int vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "Start"); @@ -216,6 +217,7 @@ Levels(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory, property_c glUniform4f(vertexColorLocation, Mid.r, Mid.g, Mid.b, Mid.a); vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "EndCol"); glUniform4f(vertexColorLocation, End.r, End.g, End.b, End.a); + */ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); @@ -233,13 +235,15 @@ GaussianBlur(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory, prop real32 Radius = Property[0].CurrentValue.f; gl_effect_layer *Test = &BitmapInfo->Test; - glBindRenderbuffer(GL_RENDERBUFFER, Test->RBO); + // glBindRenderbuffer(GL_RENDERBUFFER, Test->RBO); + /* glUseProgram(TGL.ShaderProgram); int vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "Radius"); glUniform1f(vertexColorLocation, Radius + 1.60f); vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "Direction"); glUniform2f(vertexColorLocation, 1.0f, 0.0f); + */ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); @@ -250,11 +254,13 @@ GaussianBlur(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory, prop glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, &Data[0]); TestGL_InitTexture(&BitmapInfo->Test, Data, Width, Height); - glBindRenderbuffer(GL_RENDERBUFFER, Test->RBO); + // glBindRenderbuffer(GL_RENDERBUFFER, Test->RBO); + /* glUseProgram(TGL.ShaderProgram); vertexColorLocation = glGetUniformLocation(TGL.ShaderProgram, "Direction"); glUniform2f(vertexColorLocation, 0.0f, 1.0f); + */ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, &Data[0]); diff --git a/functions.h b/functions.h index 89f8955..81f18ee 100644 --- a/functions.h +++ b/functions.h @@ -23,4 +23,12 @@ static cached_bitmap * AV_LoadVideoFrame(source *Source, memory *Memory, int32 T static cached_bitmap * Cache_CheckBitmap(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory, int32 TimelineFrame); static void TestGL_InitTexture(gl_effect_layer *Test, void *Data, uint16 Width, uint16 Height); +static gl_effect TestGL_MaskInitVerts(mask Mask); +static void TestGL_InitVerts(); +static gl_vertex_shader TestGL_MaskUploadVerts(project_layer *Layer, mask *Mask); + +static v2 Line_RatioToPoint(v2 a, v2 b, float ratio); +static v2 ImGui_ScreenPointToCompUV(ImVec2 ViewportMin, ImVec2 CompPos, ImVec2 CompZoom, ImVec2 MousePos); + +void Bezier_CubicCalcPoints(v2 p1, v2 p2, v2 p3, v2 p4, void *Data, uint32 *Increment); diff --git a/gl_calls.cpp b/gl_calls.cpp index 85ce0b3..4474b44 100644 --- a/gl_calls.cpp +++ b/gl_calls.cpp @@ -1,4 +1,4 @@ -const char *vertexShaderSource = "#version 330 core\n" +const char *DefaultVertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "layout (location = 1) in vec2 aTexCoord;\n" "out vec2 TexCoord;\n" @@ -7,7 +7,26 @@ const char *vertexShaderSource = "#version 330 core\n" " gl_Position = vec4(aPos, 1.0);\n" " TexCoord = aTexCoord;\n" "}\0"; - +const char *MaskVertexShaderSource = "#version 330 core\n" +"layout (location = 0) in vec3 aPos;\n" +"layout (location = 1) in vec2 aTexCoord;\n" +"out vec2 TexCoord;\n" +"uniform vec3 CompDimensions;\n" +"void main()\n" +"{\n" +" gl_Position = vec4(vec2(aPos.x / CompDimensions.x, aPos.y / CompDimensions.y) * 2 - 1.0f, 0.0f, 1.0);\n" +" TexCoord = aTexCoord;\n" +"}\0"; +const char *DefaultFragmentShaderSource = "#version 330 core\n" +"out vec4 FragColor;\n" +"in vec2 TexCoord;\n" +"uniform sampler2D Texture;\n" +"void main()\n" +"{\n" +"vec4 ColTest = texture(Texture, TexCoord);\n" +"ColTest.r += 0.5f;\n" +"FragColor = ColTest;\n" +"}\0"; #if 0 const char *fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" @@ -30,7 +49,6 @@ const char *fragmentShaderSource = "#version 330 core\n" "vec4 ValG = 1.0f / (End - Start) * (ColorG - Start);\n" "FragColor = clamp(ValG, 0.0f, 1.0f);\n" "}\0"; -#else const char *fragmentShaderSource = "#version 330 core\n" "uniform float Radius;\n" "uniform vec2 Direction;\n" @@ -57,78 +75,95 @@ const char *fragmentShaderSource = "#version 330 core\n" #endif -static void TestGL_InitShader() { - TGL.VertexShader = glCreateShader(GL_VERTEX_SHADER); +static void TestGL_InitDefaultShader() { + DefaultVertexShader = glCreateShader(GL_VERTEX_SHADER); - // We have to compile our shaders before executing them. - glShaderSource(TGL.VertexShader, 1, &vertexShaderSource, NULL); - glCompileShader(TGL.VertexShader); + glShaderSource(DefaultVertexShader, 1, &DefaultVertexShaderSource, NULL); + glCompileShader(DefaultVertexShader); int success; char infoLog[512]; - glGetShaderiv(TGL.VertexShader, GL_COMPILE_STATUS, &success); + glGetShaderiv(DefaultVertexShader, GL_COMPILE_STATUS, &success); if(!success) { - glGetShaderInfoLog(TGL.VertexShader, 512, NULL, infoLog); + glGetShaderInfoLog(DefaultVertexShader, 512, NULL, infoLog); printf("Vertex shader fail:\n %s", infoLog); } - TGL.FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + uint32 DefaultFragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(TGL.FragmentShader, 1, &fragmentShaderSource, NULL); - glCompileShader(TGL.FragmentShader); + glShaderSource(DefaultFragmentShader, 1, &DefaultFragmentShaderSource, NULL); + glCompileShader(DefaultFragmentShader); - glGetShaderiv(TGL.FragmentShader, GL_COMPILE_STATUS, &success); + glGetShaderiv(DefaultFragmentShader, GL_COMPILE_STATUS, &success); if(!success) { - glGetShaderInfoLog(TGL.FragmentShader, 512, NULL, infoLog); + glGetShaderInfoLog(DefaultFragmentShader, 512, NULL, infoLog); printf("Fragment shader fail:\n %s", infoLog); } // Shader programs link both types of shaders together. - TGL.ShaderProgram = glCreateProgram(); + DefaultShaderProgram = glCreateProgram(); + + glAttachShader(DefaultShaderProgram, DefaultVertexShader); + glAttachShader(DefaultShaderProgram, DefaultFragmentShader); + glLinkProgram(DefaultShaderProgram); + + glGetProgramiv(DefaultShaderProgram, GL_LINK_STATUS, &success); + if(!success) { + glGetProgramInfoLog(DefaultShaderProgram, 512, NULL, infoLog); + printf("Shader linkage fail:\n %s", infoLog); + } + + uint32 MaskVertexShader = glCreateShader(GL_VERTEX_SHADER); - glAttachShader(TGL.ShaderProgram, TGL.VertexShader); - glAttachShader(TGL.ShaderProgram, TGL.FragmentShader); - glLinkProgram(TGL.ShaderProgram); + glShaderSource(MaskVertexShader, 1, &MaskVertexShaderSource, NULL); + glCompileShader(MaskVertexShader); - glGetProgramiv(TGL.ShaderProgram, GL_LINK_STATUS, &success); + glGetShaderiv(MaskVertexShader, GL_COMPILE_STATUS, &success); if(!success) { - glGetProgramInfoLog(TGL.ShaderProgram, 512, NULL, infoLog); + glGetProgramInfoLog(DefaultShaderProgram, 512, NULL, infoLog); printf("Shader linkage fail:\n %s", infoLog); } - // The shaders are no longer needed by anything once they're linked. - glDeleteShader(TGL.VertexShader); - glDeleteShader(TGL.FragmentShader); + MaskShaderProgram = glCreateProgram(); + glAttachShader(MaskShaderProgram, MaskVertexShader); + glAttachShader(MaskShaderProgram, DefaultFragmentShader); + glLinkProgram(MaskShaderProgram); + + glGetProgramiv(MaskShaderProgram, GL_LINK_STATUS, &success); + if(!success) { + glGetProgramInfoLog(MaskShaderProgram, 512, NULL, infoLog); + printf("Shader linkage fail:\n %s", infoLog); + } + + // Default vertex shader is still needed to link to other effects. + glDeleteShader(DefaultFragmentShader); + glDeleteShader(MaskVertexShader); } -static void TestGL_InitVerts() { +static void TestGL_InitDefaultVerts() { - float GLVertices[] = { - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, - }; unsigned int GLIndices[] = { 0, 1, 3, 1, 2, 3 }; // Indices! - glGenBuffers(1, &TGL.EBO); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, TGL.EBO); + 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, &TGL.VBO); + glGenBuffers(1, &DefaultVerts.VertexBufferObject); // Our vertices need to be stored in this buffer. - glBindBuffer(GL_ARRAY_BUFFER, TGL.VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(GLVertices), GLVertices, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, DefaultVerts.VertexBufferObject); + glBufferData(GL_ARRAY_BUFFER, sizeof(DefaultVertices), DefaultVertices, GL_STATIC_DRAW); // position attribute glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); @@ -150,21 +185,30 @@ TestGL_InitTexture(gl_effect_layer *Test, void *Data, uint16 Width, uint16 Heigh glViewport(0, 0, Width, Height); - glGenFramebuffers(1, &Test->FBO); - glGenRenderbuffers(1, &Test->RBO); + glGenFramebuffers(1, &Test->FramebufferObject); - glBindRenderbuffer(GL_RENDERBUFFER, Test->RBO); + glGenRenderbuffers(1, &Test->Color_Renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, Test->Color_Renderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, Width, Height); - glBindFramebuffer(GL_FRAMEBUFFER, Test->FBO); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, Test->RBO); + GLuint Stencil_Renderbuffer = 0; + glGenRenderbuffers(1, &Stencil_Renderbuffer); + glBindRenderbuffer( GL_RENDERBUFFER, (GLuint)Stencil_Renderbuffer ); + glRenderbufferStorage( GL_RENDERBUFFER, GL_STENCIL_INDEX8, Width, Height ); + + glBindFramebuffer(GL_FRAMEBUFFER, Test->FramebufferObject); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, Test->Color_Renderbuffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, Stencil_Renderbuffer); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + printf("incomplete framebuffer"); + Assert(0); + } - // Non-POT textures with RGB instead of RGBA doesn't seem to work, but - // since I don't plan on storing bitmaps that way (AVX2 is much more - // efficient with RGBA) it doesn't really matter. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, Data); - // glGenerateMipmap(GL_TEXTURE_2D); glBindFramebuffer(GL_FRAMEBUFFER, 0); } diff --git a/main.cpp b/main.cpp index da67593..84d3559 100644 --- a/main.cpp +++ b/main.cpp @@ -62,6 +62,7 @@ SDL_sem *Semaphore; #include "keyframes.cpp" #include "layer.cpp" #include "strings.cpp" +#include "bezier.cpp" #if THREADED #include "threading.cpp" #else @@ -227,6 +228,7 @@ int main(int argc, char *argv[]) { InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_AVInfo, "Image/video headers"); InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_SourceBitmapTable, "Source bitmap tables"); + InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_VectorPoints, "Vector Points"); InitMemoryTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_MiscCache, "Misc cache"); InitMemoryTable(&GlobalMemory, &Memory, 15 * 1024 * 1024, F_Layers, "Layers"); @@ -378,8 +380,8 @@ int main(int argc, char *argv[]) { return -1; } - TestGL_InitShader(); - TestGL_InitVerts(); + TestGL_InitDefaultShader(); + TestGL_InitDefaultVerts(); LoadTestFootage(&File, &State, &Memory); diff --git a/main.h b/main.h index 8a6c7ca..e77042f 100644 --- a/main.h +++ b/main.h @@ -30,6 +30,7 @@ enum memory_table_list { P_AVInfo, P_SourceBitmapTable, + P_VectorPoints, P_MiscCache, // don't put this in final F_Layers, @@ -127,23 +128,39 @@ enum blend_mode blend_difference }; -struct gl_vertex_shader { -}; struct gl_effect { - uint32 FragmentShader; uint32 ShaderProgram; - uint32 VertexShader; - uint32 EBO; - uint32 VBO; }; -static gl_effect TGL; +struct gl_vertex_shader { + uint32 VertexArrayObject; + uint32 VertexBufferObject; +}; + +struct default_gl_vertex_object { + uint32 VertexArrayObject; + uint32 VertexBufferObject; + uint32 ElementBufferObject; +}; + +static default_gl_vertex_object DefaultVerts; +static uint32 DefaultVertexShader; +static uint32 DefaultShaderProgram; +static uint32 MaskShaderProgram; + +float DefaultVertices[] = { + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, +}; + struct gl_effect_layer { GLuint Texture; - GLuint FBO; - GLuint RBO; + GLuint FramebufferObject; + uint32 Color_Renderbuffer; }; static gl_vertex_shader GL_Vertices; @@ -313,6 +330,8 @@ struct mask_point { struct mask { mask_point Point[16]; uint16 NumberOfPoints; + void *TriangulatedPointCache; + uint32 NumberOfVerts; }; struct project_layer { diff --git a/my_imgui_internal_widgets.cpp b/my_imgui_internal_widgets.cpp index c654e36..705b898 100644 --- a/my_imgui_internal_widgets.cpp +++ b/my_imgui_internal_widgets.cpp @@ -164,95 +164,44 @@ bool ImGui::TestLine(ImVec2 p0, ImVec2 p1) return Toggle; } -// Slightly modified version of the bezier closest point lookup code that -// additionally outputs the ratio of the closest point along the curve for use -// in constructing the handles of new points. +// Currently not used anywhere outside of debug UI. I'll keep it in case +// there's a place where doing this operation in UI space is useful. Note that +// the version of this function in bezier.cpp differs in precision by about +// 0.001. - -// The ratio here is just the dot product divided by the squared length. -ImVec2 ImLineClosestPoint2(const ImVec2& a, const ImVec2& b, const ImVec2& p, float& ratio) +ImVec2 ImGui::RatioToPoint(ImVec2 a, ImVec2 b, float ratio) { - ImVec2 ap = p - a; ImVec2 ab_dir = b - a; - float dot = ap.x * ab_dir.x + ap.y * ab_dir.y; - if (dot < 0.0f) { - ratio = 0.0f; - return a; - } float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y; - if (dot > ab_len_sqr) { - ratio = 1.0f; - return b; - } - ratio = dot / ab_len_sqr; + float dot = ratio*ab_len_sqr; return a + ab_dir * dot / ab_len_sqr; } -// Function to convert a ratio back into a point for the bezier handles. -ImVec2 ImGui::RatioToPoint(const ImVec2& a, const ImVec2& b, float ratio) +// Returns true when cursor is close to the curve but not too close to the +// beginning/end points. Basically just a wrapper for that ClosestPoint +// function. +bool ImGui::BezierInteractive(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3) { - ImVec2 ab_dir = b - a; - float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y; - float dot = ratio*ab_len_sqr; - return a + ab_dir * dot / ab_len_sqr; -} + bool hovered = false; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; -// Following the algorithm, we take the ratio from the _leftmost_ point in each -// subdivision of the cubic spline until we're within tess_tol, and then we -// interpolate it with the subdivision's rightmost point in the ClosestPoint call. -// The pow(0.5, level) represents the ratio of the next subdivision's leftmost -// point (AKA the rightmost point of the current subdivision). + ImVec2 point = ImBezierCubicClosestPointCasteljau(p0, p1, p2, p3, g.IO.MousePos, GetStyle().CurveTessellationTol); -static void ImBezierCubicClosestPointCasteljauStep2(const ImVec2& p, ImVec2& p_closest, float ratio, float& r_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) -{ - float dx = x4 - x1; - float dy = y4 - y1; - float d2 = ((x2 - x4) * dy - (y2 - y4) * dx); - float 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)) - { - ImVec2 p_current(x4, y4); - float added_ratio; - ImVec2 p_line = ImLineClosestPoint2(p_last, p_current, p, added_ratio); - float dist2 = ImLengthSqr(p - p_line); - if (dist2 < p_closest_dist2) - { - p_closest = p_line; - p_closest_dist2 = dist2; - r_closest = ratio + pow(0.5, level)*added_ratio; - } - p_last = p_current; - } - else if (level < 10) + if (abs(g.IO.MousePos.x - point.x) < 3 && abs(g.IO.MousePos.y - point.y) < 3 && + abs(p0.x - point.x) > 3 && abs(p0.y - point.y) > 3 && + abs(p1.x - point.x) > 3 && abs(p1.y - point.y) > 3) { - float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f; - float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f; - float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f; - float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f; - float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f; - float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f; - ImBezierCubicClosestPointCasteljauStep2(p, p_closest, ratio, r_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); - ImBezierCubicClosestPointCasteljauStep2(p, p_closest, ratio + pow(0.5, level+1), r_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); + hovered = true; } -} -ImVec2 ImBezierCubicClosestPointCasteljau2(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float& ratio, float tess_tol) -{ - IM_ASSERT(tess_tol > 0.0f); - ImVec2 p_last = p1; - ImVec2 p_closest; - float p_closest_dist2 = FLT_MAX; - float r_closest; - ImBezierCubicClosestPointCasteljauStep2(p, p_closest, 0, r_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0); - ratio = r_closest; - return p_closest; + return hovered; } -// Returns true when cursor is close to the curve but not too close to the beginning/end points. -bool ImGui::BezierInteractive(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3, float& ratio) +bool ImGui::LineInteractive(ImVec2 p0, ImVec2 p1) { bool hovered = false; @@ -261,21 +210,14 @@ bool ImGui::BezierInteractive(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3, float& if (window->SkipItems) return false; - ImDrawList* draw_list = window->DrawList; - ImU32 col = ImGui::GetColorU32(ImGuiCol_ScrollbarGrab); - - ImVec2 point = ImBezierCubicClosestPointCasteljau2(p0, p1, p2, p3, g.IO.MousePos, ratio, GetStyle().CurveTessellationTol); - + ImVec2 point = ImLineClosestPoint(p0, p1, g.IO.MousePos); if (abs(g.IO.MousePos.x - point.x) < 3 && abs(g.IO.MousePos.y - point.y) < 3 && abs(p0.x - point.x) > 3 && abs(p0.y - point.y) > 3 && abs(p1.x - point.x) > 3 && abs(p1.y - point.y) > 3) { - col = GetColorU32(ImGuiCol_Button); hovered = true; } - draw_list->AddBezierCubic(p0, p1, p2, p3, col, 2.0f, 0); - return hovered; } diff --git a/my_imgui_internal_widgets.h b/my_imgui_internal_widgets.h index 1e0a212..52fdc74 100644 --- a/my_imgui_internal_widgets.h +++ b/my_imgui_internal_widgets.h @@ -8,7 +8,8 @@ namespace ImGui { IMGUI_API bool SliderLevels(const char* label, const char* label2, const char* label3, void* p_data, void* p_min, void* p_max); IMGUI_API bool TestLine(ImVec2 P1, ImVec2 P2); - IMGUI_API bool BezierInteractive(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3, float& ratio); - IMGUI_API ImVec2 RatioToPoint(const ImVec2& a, const ImVec2& b, float ratio); + IMGUI_API bool BezierInteractive(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3); + IMGUI_API bool LineInteractive(ImVec2 p0, ImVec2 p1); + IMGUI_API ImVec2 RatioToPoint(ImVec2 a, ImVec2 b, float ratio); } diff --git a/my_imgui_widgets.cpp b/my_imgui_widgets.cpp index 6f785de..e334f63 100644 --- a/my_imgui_widgets.cpp +++ b/my_imgui_widgets.cpp @@ -71,14 +71,14 @@ ImGui_KeyframeDragging(project_data *File, project_state *State, ui *UI, propert } // Returns a normalized UV position of the composition -static ImVec2 +static v2 ImGui_ScreenPointToCompUV(ImVec2 ViewportMin, ImVec2 CompPos, ImVec2 CompZoom, ImVec2 MousePos) { ImVec2 LocalMousePos = MousePos - ViewportMin; ImVec2 LocalCompPos = CompPos - ViewportMin; ImVec2 MouseScreenUV = LocalMousePos - LocalCompPos; ImVec2 Result = MouseScreenUV / CompZoom; - return Result; + return V2(Result); } static void @@ -391,21 +391,24 @@ ImGui_Viewport(project_data File, project_state *State, ui *UI, comp_buffer Comp if (p+1 == Mask->NumberOfPoints) Point1 = &Mask->Point[0]; - ImVec2 Point0_Pos = ImVec2(Point0->Pos.x, Point0->Pos.y); - ImVec2 Point0_Pos_Left = Point0_Pos + ImVec2(Point0->TangentLeft.x, Point0->TangentLeft.y); - ImVec2 Point0_Pos_Right = Point0_Pos + ImVec2(Point0->TangentRight.x, Point0->TangentRight.y); + // NOTE(fox): I want to keep operations in local space under the v2 data + // type and operations in screen space under ImVec2. + + v2 Point0_Pos = Point0->Pos; + v2 Point0_Pos_Left = Point0_Pos + Point0->TangentLeft; + v2 Point0_Pos_Right = Point0_Pos + Point0->TangentRight; - ImVec2 Point1_Pos = ImVec2(Point1->Pos.x, Point1->Pos.y); - ImVec2 Point1_Pos_Left = Point1_Pos + ImVec2(Point1->TangentLeft.x, Point1->TangentLeft.y); - ImVec2 Point1_Pos_Right = Point1_Pos + ImVec2(Point1->TangentRight.x, Point1->TangentRight.y); + v2 Point1_Pos = Point1->Pos; + v2 Point1_Pos_Left = Point1_Pos + Point1->TangentLeft; + v2 Point1_Pos_Right = Point1_Pos + Point1->TangentRight; - ImVec2 Point0_ScreenPos = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, V2(Point0_Pos)); - ImVec2 Point0_ScreenPos_Left = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, V2(Point0_Pos_Left)); - ImVec2 Point0_ScreenPos_Right = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, V2(Point0_Pos_Right)); + ImVec2 Point0_ScreenPos = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, Point0_Pos); + ImVec2 Point0_ScreenPos_Left = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, Point0_Pos_Left); + ImVec2 Point0_ScreenPos_Right = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, Point0_Pos_Right); - ImVec2 Point1_ScreenPos = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, V2(Point1_Pos)); - ImVec2 Point1_ScreenPos_Left = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, V2(Point1_Pos_Left)); - ImVec2 Point1_ScreenPos_Right = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, V2(Point1_Pos_Right)); + ImVec2 Point1_ScreenPos = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, Point1_Pos); + ImVec2 Point1_ScreenPos_Left = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, Point1_Pos_Left); + ImVec2 Point1_ScreenPos_Right = Layer_LocalToScreenSpace(Layer, UI, CompBuffer, Point1_Pos_Right); ImGui::PushID(p); @@ -414,15 +417,19 @@ ImGui_Viewport(project_data File, project_state *State, ui *UI, comp_buffer Comp // The handle itself draw_list->AddNgon(Point0_ScreenPos, 10, col, 8, 5.0f); - draw_list->AddNgon(Point0_ScreenPos_Left, 10, col, 8, 5.0f); - draw_list->AddNgon(Point0_ScreenPos_Right, 10, col, 8, 5.0f); - draw_list->AddLine(Point0_ScreenPos, Point0_ScreenPos_Left, col, 2.0f); - draw_list->AddLine(Point0_ScreenPos, Point0_ScreenPos_Right, col, 2.0f); + // draw_list->AddNgon(Point0_ScreenPos_Left, 10, col, 8, 5.0f); + // draw_list->AddNgon(Point0_ScreenPos_Right, 10, col, 8, 5.0f); + // draw_list->AddLine(Point0_ScreenPos, Point0_ScreenPos_Left, col, 2.0f); + // draw_list->AddLine(Point0_ScreenPos, Point0_ScreenPos_Right, col, 2.0f); - ImU32 col2 = ImGui::GetColorU32(ImGuiCol_Button); + int max = 1; - for (int b = 0; b < 3; b++) + if (Point0->HandleBezier) { + max = 3; + } + + for (int b = 0; b < max; b++) { ImGui::PushID(b); if (b == 0) { @@ -462,11 +469,11 @@ ImGui_Viewport(project_data File, project_state *State, ui *UI, comp_buffer Comp Point0->TangentRight.x -= YAxis.x; Point0->TangentRight.y += YAxis.y; } + State->UpdateFrame = true; } ImGui::PopID(); } - // The bezier path if (Mask->NumberOfPoints == 1) { @@ -474,49 +481,63 @@ ImGui_Viewport(project_data File, project_state *State, ui *UI, comp_buffer Comp continue; } - // Ratio of the point along the curve. See internal for more info. - float ratio; + ImU32 col2 = ImGui::GetColorU32(ImGuiCol_Button); - if (ImGui::BezierInteractive(Point0_ScreenPos, Point0_ScreenPos_Right, - Point1_ScreenPos_Left, Point1_ScreenPos, ratio) && - State->Tool == tool_pen) - { - // Using a button like this may be kinda janky, but it gives us access - // to all of ButtonBehavior and the ID system without having to rewrite it. - ImGui::SetCursorScreenPos(io.MousePos - ImVec2(5,5)); - ImGui::Button("maskbezier", ImVec2(10, 10)); - - if(ImGui::IsItemHovered()) { - // ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); - draw_list->AddNgon(io.MousePos, 2, col, 8, 5.0f); - ImVec2 RatioLeft = ImGui::RatioToPoint(Point0_ScreenPos, Point0_ScreenPos_Right, ratio); - ImVec2 RatioTop = ImGui::RatioToPoint(Point0_ScreenPos_Right, Point1_ScreenPos_Left, ratio); - ImVec2 RatioRight = ImGui::RatioToPoint(Point1_ScreenPos_Left, Point1_ScreenPos, ratio); - ImVec2 TangentLeft = ImGui::RatioToPoint(RatioLeft, RatioTop, ratio); - ImVec2 TangentRight = ImGui::RatioToPoint(RatioTop, RatioRight, ratio); - draw_list->AddLine(RatioLeft, RatioTop, col, 2.0f); - draw_list->AddLine(RatioRight, RatioTop, col, 2.0f); - draw_list->AddLine(TangentLeft, TangentRight, col, 2.0f); - } - if(ImGui::IsItemActivated() && io.KeyCtrl) { - ImVec2 Ratio0 = ImGui::RatioToPoint(Point0_Pos, Point0_Pos_Right, ratio); - ImVec2 RatioTop = ImGui::RatioToPoint(Point0_Pos_Right, Point1_Pos_Left, ratio); - ImVec2 Ratio1 = ImGui::RatioToPoint(Point1_Pos_Left, Point1_Pos, ratio); - ImVec2 TangentLeft = ImGui::RatioToPoint(Ratio0, RatioTop, ratio); - ImVec2 TangentRight = ImGui::RatioToPoint(RatioTop, Ratio1, ratio); - ImVec2 Point = ImGui::RatioToPoint(TangentLeft, TangentRight, ratio); - Mask_AddPointToCurve(Mask, Point, Point - TangentLeft, Point - TangentRight, - Point0_Pos - Ratio0, Point1_Pos - Ratio1, p); - } + if (Point0->HandleBezier || Point1->HandleBezier) { + draw_list->AddBezierCubic(Point0_ScreenPos, Point0_ScreenPos_Right, + Point1_ScreenPos_Left, Point1_ScreenPos, col2, 6.0f, 0); + } else { + draw_list->AddLine(Point0_ScreenPos, Point1_ScreenPos, col2, 6.0f); } - // DebugWatchVar("ratio", &ratio, d_float); - - // if (ImGui::TestLine(PointScreenPos[0], PointScreenPos[1])) { - // } + if (Point0->HandleBezier && Point1->HandleBezier) { + if (ImGui::BezierInteractive(Point0_ScreenPos, Point0_ScreenPos_Right, + Point1_ScreenPos_Left, Point1_ScreenPos) && + State->Tool == tool_pen) + { + // Using a button like this may be kinda janky, but it gives us access + // to all of ButtonBehavior and the ID system without having to write on top of ImGui's. + ImGui::SetCursorScreenPos(io.MousePos - ImVec2(5,5)); + ImGui::Button("maskbezier", ImVec2(10, 10)); + if (ImGui::IsItemHovered()) { +#if DEBUG + v2 LayerPoint = Layer_ScreenSpaceToLocal(Layer, UI, CompBuffer, ViewportMin, io.MousePos); + real32 ratio = Bezier_CubicRatioOfPoint(Point0_Pos, Point0_Pos_Right, Point1_Pos_Left, Point1_Pos, LayerPoint); + draw_list->AddNgon(io.MousePos, 2, col, 8, 5.0f); + ImVec2 RatioLeft = ImGui::RatioToPoint(Point0_ScreenPos, Point0_ScreenPos_Right, ratio); + ImVec2 RatioRight = ImGui::RatioToPoint(Point1_ScreenPos_Left, Point1_ScreenPos, ratio); + ImVec2 RatioTop = ImGui::RatioToPoint(Point0_ScreenPos_Right, Point1_ScreenPos_Left, ratio); + ImVec2 TangentLeft = ImGui::RatioToPoint(RatioLeft, RatioTop, ratio); + ImVec2 TangentRight = ImGui::RatioToPoint(RatioTop, RatioRight, ratio); + draw_list->AddLine(RatioLeft, RatioTop, col, 2.0f); + draw_list->AddLine(RatioRight, RatioTop, col, 2.0f); + draw_list->AddLine(TangentLeft, TangentRight, col, 2.0f); +#endif + } + if (ImGui::IsItemActivated() && io.KeyCtrl) { + v2 LayerPoint = Layer_ScreenSpaceToLocal(Layer, UI, CompBuffer, ViewportMin, io.MousePos); + real32 ratio = Bezier_CubicRatioOfPoint(Point0_Pos, Point0_Pos_Right, Point1_Pos_Left, Point1_Pos, LayerPoint); + Mask_AddPointToCurve(Mask, p, ratio); + } + } + } else { + if (ImGui::LineInteractive(Point0_ScreenPos, Point1_ScreenPos) && + State->Tool == tool_pen) + { + ImGui::SetCursorScreenPos(io.MousePos - ImVec2(5,5)); + ImGui::Button("maskline", ImVec2(10, 10)); + if (ImGui::IsItemHovered()) { + draw_list->AddNgon(io.MousePos, 2, col, 8, 5.0f); + } + if (ImGui::IsItemActivated() && io.KeyCtrl) { + v2 LayerPoint = Layer_ScreenSpaceToLocal(Layer, UI, CompBuffer, ViewportMin, io.MousePos); + Mask_AddPointToLine(Mask, p, LayerPoint); + } + } + } ImGui::PopID(); } ImGui::PopID(); @@ -543,6 +564,7 @@ ImGui_Viewport(project_data File, project_state *State, ui *UI, comp_buffer Comp bool32 IsActive = ImGui::IsItemActive(); bool32 IsActivated = ImGui::IsItemActivated(); + /* if (State->MostRecentlySelectedLayer > -1) { project_layer *Layer = File.Layer[State->MostRecentlySelectedLayer]; @@ -555,15 +577,13 @@ ImGui_Viewport(project_data File, project_state *State, ui *UI, comp_buffer Comp if (State->Pen.IsActive && !ImGui::IsKeyDown(ImGuiKey_Z)) { if (IsActivated) { - v2 CompUV = V2(ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos)); - v2 LayerUV = CompUVToLayerUV(Layer, &CompBuffer, CompUV); - v2 LayerPos = LayerUV * V2(Layer->Source->Info.Width, Layer->Source->Info.Height); + v2 LayerPos = Layer_ScreenSpaceToLocal(Layer, UI, CompBuffer, ViewportMin, io.MousePos); Mask_PushPoint(&Layer->Mask[Layer->NumberOfMasks-1], LayerPos); } if (IsActive) { mask *Mask = &Layer->Mask[Layer->NumberOfMasks-1]; mask_point *CurrentPoint = &Mask->Point[Mask->NumberOfPoints-1]; - v2 CompUV = V2(ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos)); + v2 CompUV = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos); v2 LayerUV = CompUVToLayerUV(Layer, &CompBuffer, CompUV); v2 LayerPos = LayerUV * V2(Layer->Source->Info.Width, Layer->Source->Info.Height); v2 OffsetPos = CurrentPoint->Pos - LayerPos; @@ -575,11 +595,12 @@ ImGui_Viewport(project_data File, project_state *State, ui *UI, comp_buffer Comp } } } + */ if (IsHovered && IsActivated && ImGui::IsMouseDown(ImGuiMouseButton_Left)) { // Point to zoom in on if Z is held - UI->TempZoomRatio = V2(ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos)); + UI->TempZoomRatio = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos); DebugWatchVar("MouseScreenUV", &UI->TempZoomRatio.x, d_float); DebugWatchVar("MouseScreenUV", &UI->TempZoomRatio.y, d_float); diff --git a/my_math.h b/my_math.h index b95e71d..cf491f1 100644 --- a/my_math.h +++ b/my_math.h @@ -46,6 +46,16 @@ inline v2 V2(real32 x, real32 y) return(Result); } +inline v2 V2(real32 x) +{ + v2 Result; + + Result.x = x; + Result.y = x; + + return(Result); +} + inline v2 V2(ImVec2 A) { v2 Result; @@ -186,6 +196,18 @@ v2i operator+(v2i A, v2i B) return(Result); } +v2 operator/(v2 A, real32 B) +{ + v2 Result; + + Result.x = A.x / B; + Result.y = A.y / B; + + return Result; +} + + + v4 operator+(v4 A, v4 B) { v4 Result; -- cgit v1.2.3