diff --git a/src/common/2d/v_2ddrawer.cpp b/src/common/2d/v_2ddrawer.cpp index 69ab0d530e..067d61d774 100644 --- a/src/common/2d/v_2ddrawer.cpp +++ b/src/common/2d/v_2ddrawer.cpp @@ -42,7 +42,7 @@ #include "v_video.h" #include "fcolormap.h" -static F2DDrawer drawer; +static F2DDrawer drawer = F2DDrawer(); F2DDrawer* twod = &drawer; EXTERN_CVAR(Float, transsouls) @@ -104,6 +104,33 @@ DEFINE_ACTION_FUNCTION_NATIVE(DShape2DTransform, Translate, Shape2DTransform_Tra return 0; } +static void Shape2DTransform_From2D( + DShape2DTransform* self, + double m00, double m01, double m10, double m11, double vx, double vy +) +{ + self->transform.Cells[0][0] = m00; + self->transform.Cells[0][1] = m01; + self->transform.Cells[1][0] = m10; + self->transform.Cells[1][1] = m11; + + self->transform.Cells[0][2] = vx; + self->transform.Cells[1][2] = vy; +} + +DEFINE_ACTION_FUNCTION_NATIVE(DShape2DTransform, From2D, Shape2DTransform_From2D) +{ + PARAM_SELF_PROLOGUE(DShape2DTransform); + PARAM_FLOAT(m00); + PARAM_FLOAT(m01); + PARAM_FLOAT(m10); + PARAM_FLOAT(m11); + PARAM_FLOAT(vx); + PARAM_FLOAT(vy); + Shape2DTransform_From2D(self, m00, m01, m10, m11, vx, vy); + return 0; +} + IMPLEMENT_CLASS(DShape2D, false, false) static void Shape2D_SetTransform(DShape2D* self, DShape2DTransform *transform) @@ -114,7 +141,7 @@ static void Shape2D_SetTransform(DShape2D* self, DShape2DTransform *transform) DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, SetTransform, Shape2D_SetTransform) { PARAM_SELF_PROLOGUE(DShape2D); - PARAM_OBJECT(transform, DShape2DTransform); + PARAM_OBJECT_NOT_NULL(transform, DShape2DTransform); Shape2D_SetTransform(self, transform); return 0; } @@ -462,8 +489,19 @@ void F2DDrawer::AddTexture(FGameTexture* img, DrawParms& parms) u1 = float(u1 + parms.windowleft / parms.texwidth); u2 = float(u2 - (parms.texwidth - wi) / parms.texwidth); } + auto t = this->transform; + auto tCorners = { + (t * DVector3(x, y, 1.0)).XY(), + (t * DVector3(x, y + h, 1.0)).XY(), + (t * DVector3(x + w, y, 1.0)).XY(), + (t * DVector3(x + w, y + h, 1.0)).XY() + }; + double minx = std::min_element(tCorners.begin(), tCorners.end(), [] (auto d0, auto d1) { return d0.X < d1.X; })->X; + double maxx = std::max_element(tCorners.begin(), tCorners.end(), [] (auto d0, auto d1) { return d0.X < d1.X; })->X; + double miny = std::min_element(tCorners.begin(), tCorners.end(), [] (auto d0, auto d1) { return d0.Y < d1.Y; })->Y; + double maxy = std::max_element(tCorners.begin(), tCorners.end(), [] (auto d0, auto d1) { return d0.Y < d1.Y; })->Y; - if (x < (double)parms.lclip || y < (double)parms.uclip || x + w >(double)parms.rclip || y + h >(double)parms.dclip) + if (minx < (double)parms.lclip || miny < (double)parms.uclip || maxx >(double)parms.rclip || maxy >(double)parms.dclip) { dg.mScissor[0] = parms.lclip + int(offset.X); dg.mScissor[1] = parms.uclip + int(offset.Y); @@ -479,10 +517,10 @@ void F2DDrawer::AddTexture(FGameTexture* img, DrawParms& parms) dg.mVertCount = 4; dg.mVertIndex = (int)mVertices.Reserve(4); TwoDVertex* ptr = &mVertices[dg.mVertIndex]; - Set(ptr, x, y, 0, u1, v1, vertexcolor); ptr++; - Set(ptr, x, y + h, 0, u1, v2, vertexcolor); ptr++; - Set(ptr, x + w, y, 0, u2, v1, vertexcolor); ptr++; - Set(ptr, x + w, y + h, 0, u2, v2, vertexcolor); ptr++; + ptr->Set(x, y, 0, u1, v1, vertexcolor); ptr++; + ptr->Set(x, y + h, 0, u1, v2, vertexcolor); ptr++; + ptr->Set(x + w, y, 0, u2, v1, vertexcolor); ptr++; + ptr->Set(x + w, y + h, 0, u2, v2, vertexcolor); ptr++; } else { @@ -515,12 +553,16 @@ void F2DDrawer::AddTexture(FGameTexture* img, DrawParms& parms) dg.mVertCount = 4; dg.mVertIndex = (int)mVertices.Reserve(4); TwoDVertex* ptr = &mVertices[dg.mVertIndex]; - Set(ptr, x1, y1, 0, u1, v1, vertexcolor); ptr++; - Set(ptr, x2, y2, 0, u1, v2, vertexcolor); ptr++; - Set(ptr, x3, y3, 0, u2, v1, vertexcolor); ptr++; - Set(ptr, x4, y4, 0, u2, v2, vertexcolor); ptr++; + ptr->Set(x1, y1, 0, u1, v1, vertexcolor); ptr++; + ptr->Set(x2, y2, 0, u1, v2, vertexcolor); ptr++; + ptr->Set(x3, y3, 0, u2, v1, vertexcolor); ptr++; + ptr->Set(x4, y4, 0, u2, v2, vertexcolor); ptr++; } + dg.useTransform = true; + dg.transform = this->transform; + dg.transform.Cells[0][2] += offset.X; + dg.transform.Cells[1][2] += offset.Y; dg.mIndexIndex = mIndices.Size(); dg.mIndexCount += 6; AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); @@ -599,11 +641,12 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms) if ( shape->mVertices[i].Y > shape->maxy ) shape->maxy = shape->mVertices[i].Y; } } + auto t = this->transform * shape->transform; auto tCorners = { - (shape->transform * DVector3(shape->minx, shape->miny, 1.0)).XY(), - (shape->transform * DVector3(shape->minx, shape->maxy, 1.0)).XY(), - (shape->transform * DVector3(shape->maxx, shape->miny, 1.0)).XY(), - (shape->transform * DVector3(shape->maxx, shape->maxy, 1.0)).XY() + (t * DVector3(shape->minx, shape->miny, 1.0)).XY(), + (t * DVector3(shape->minx, shape->maxy, 1.0)).XY(), + (t * DVector3(shape->maxx, shape->miny, 1.0)).XY(), + (t * DVector3(shape->maxx, shape->maxy, 1.0)).XY() }; double minx = std::min_element(tCorners.begin(), tCorners.end(), [] (auto d0, auto d1) { return d0.X < d1.X; })->X; double maxx = std::max_element(tCorners.begin(), tCorners.end(), [] (auto d0, auto d1) { return d0.X < d1.X; })->X; @@ -621,7 +664,7 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms) memset(dg.mScissor, 0, sizeof(dg.mScissor)); dg.useTransform = true; - dg.transform = shape->transform; + dg.transform = t; dg.transform.Cells[0][2] += offset.X; dg.transform.Cells[1][2] += offset.Y; dg.shape2DBufInfo = shape->bufferInfo; @@ -715,7 +758,7 @@ void F2DDrawer::AddPoly(FGameTexture *texture, FVector2 *points, int npoints, u = t * cosrot - v * sinrot; v = v * cosrot + t * sinrot; } - Set(&mVertices[poly.mVertIndex+i], points[i].X, points[i].Y, 0, u*uscale, v*vscale, color0); + mVertices[poly.mVertIndex+i].Set(points[i].X, points[i].Y, 0, u*uscale, v*vscale, color0); } poly.mIndexIndex = mIndices.Size(); @@ -736,6 +779,10 @@ void F2DDrawer::AddPoly(FGameTexture *texture, FVector2 *points, int npoints, mIndices[addr + i] = poly.mVertIndex + indices[i]; } } + poly.useTransform = true; + poly.transform = this->transform; + poly.transform.Cells[0][2] += offset.X; + poly.transform.Cells[1][2] += offset.Y; AddCommand(&poly); } @@ -773,7 +820,7 @@ void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, const u for (size_t i=0;iSet(vt[i].X, vt[i].Y, 0.f, vt[i].Z, vt[i].W, color); ptr++; } dg.mIndexIndex = mIndices.Size(); @@ -798,6 +845,10 @@ void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, const u dg.mIndexCount = (int)vtcount; } + dg.useTransform = true; + dg.transform = this->transform; + dg.transform.Cells[0][2] += offset.X; + dg.transform.Cells[1][2] += offset.Y; AddCommand(&dg); } @@ -916,18 +967,22 @@ void F2DDrawer::AddFlatFill(int left, int top, int right, int bottom, FGameTextu dg.mVertIndex = (int)mVertices.Reserve(4); auto ptr = &mVertices[dg.mVertIndex]; - Set(ptr, left, top, 0, fU1, fV1, color); ptr++; + ptr->Set(left, top, 0, fU1, fV1, color); ptr++; if (local_origin < 4) { - Set(ptr, left, bottom, 0, fU1, fV2, color); ptr++; - Set(ptr, right, top, 0, fU2, fV1, color); ptr++; + ptr->Set(left, bottom, 0, fU1, fV2, color); ptr++; + ptr->Set(right, top, 0, fU2, fV1, color); ptr++; } else { - Set(ptr, left, bottom, 0, fU2, fV1, color); ptr++; - Set(ptr, right, top, 0, fU1, fV2, color); ptr++; + ptr->Set(left, bottom, 0, fU2, fV1, color); ptr++; + ptr->Set(right, top, 0, fU1, fV2, color); ptr++; } - Set(ptr, right, bottom, 0, fU2, fV2, color); ptr++; + ptr->Set(right, bottom, 0, fU2, fV2, color); ptr++; + dg.useTransform = true; + dg.transform = this->transform; + dg.transform.Cells[0][2] += offset.X; + dg.transform.Cells[1][2] += offset.Y; dg.mIndexIndex = mIndices.Size(); dg.mIndexCount += 6; AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); @@ -950,10 +1005,14 @@ void F2DDrawer::AddColorOnlyQuad(int x1, int y1, int w, int h, PalEntry color, F dg.mVertIndex = (int)mVertices.Reserve(4); dg.mRenderStyle = style? *style : LegacyRenderStyles[STYLE_Translucent]; auto ptr = &mVertices[dg.mVertIndex]; - Set(ptr, x1, y1, 0, 0, 0, color); ptr++; - Set(ptr, x1, y1 + h, 0, 0, 0, color); ptr++; - Set(ptr, x1 + w, y1, 0, 0, 0, color); ptr++; - Set(ptr, x1 + w, y1 + h, 0, 0, 0, color); ptr++; + ptr->Set(x1, y1, 0, 0, 0, color); ptr++; + ptr->Set(x1, y1 + h, 0, 0, 0, color); ptr++; + ptr->Set(x1 + w, y1, 0, 0, 0, color); ptr++; + ptr->Set(x1 + w, y1 + h, 0, 0, 0, color); ptr++; + dg.useTransform = true; + dg.transform = this->transform; + dg.transform.Cells[0][2] += offset.X; + dg.transform.Cells[1][2] += offset.Y; dg.mIndexIndex = mIndices.Size(); dg.mIndexCount += 6; AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); @@ -998,8 +1057,12 @@ void F2DDrawer::AddLine(double x1, double y1, double x2, double y2, int clipx1, dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; dg.mVertCount = 2; dg.mVertIndex = (int)mVertices.Reserve(2); - Set(&mVertices[dg.mVertIndex], x1, y1, 0, 0, 0, p); - Set(&mVertices[dg.mVertIndex+1], x2, y2, 0, 0, 0, p); + dg.useTransform = true; + dg.transform = this->transform; + dg.transform.Cells[0][2] += offset.X; + dg.transform.Cells[1][2] += offset.Y; + mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p); + mVertices[dg.mVertIndex+1].Set(x2, y2, 0, 0, 0, p); AddCommand(&dg); } @@ -1034,10 +1097,14 @@ void F2DDrawer::AddThickLine(int x1, int y1, int x2, int y2, double thickness, u dg.mVertIndex = (int)mVertices.Reserve(4); dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; auto ptr = &mVertices[dg.mVertIndex]; - Set(ptr, corner0.X, corner0.Y, 0, 0, 0, p); ptr++; - Set(ptr, corner1.X, corner1.Y, 0, 0, 0, p); ptr++; - Set(ptr, corner2.X, corner2.Y, 0, 0, 0, p); ptr++; - Set(ptr, corner3.X, corner3.Y, 0, 0, 0, p); ptr++; + ptr->Set(corner0.X, corner0.Y, 0, 0, 0, p); ptr++; + ptr->Set(corner1.X, corner1.Y, 0, 0, 0, p); ptr++; + ptr->Set(corner2.X, corner2.Y, 0, 0, 0, p); ptr++; + ptr->Set(corner3.X, corner3.Y, 0, 0, 0, p); ptr++; + dg.useTransform = true; + dg.transform = this->transform; + dg.transform.Cells[0][2] += offset.X; + dg.transform.Cells[1][2] += offset.Y; dg.mIndexIndex = mIndices.Size(); dg.mIndexCount += 6; AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); @@ -1061,7 +1128,11 @@ void F2DDrawer::AddPixel(int x1, int y1, uint32_t color) dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; dg.mVertCount = 1; dg.mVertIndex = (int)mVertices.Reserve(1); - Set(&mVertices[dg.mVertIndex], x1, y1, 0, 0, 0, p); + mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p); + dg.useTransform = true; + dg.transform = this->transform; + dg.transform.Cells[0][2] += offset.X; + dg.transform.Cells[1][2] += offset.Y; AddCommand(&dg); } diff --git a/src/common/2d/v_2ddrawer.h b/src/common/2d/v_2ddrawer.h index 6235599781..16ca568f31 100644 --- a/src/common/2d/v_2ddrawer.h +++ b/src/common/2d/v_2ddrawer.h @@ -55,6 +55,9 @@ struct DShape2DBufferInfo; class F2DDrawer { public: + F2DDrawer() { + this->transform.Identity(); + } enum EDrawType : uint8_t { @@ -172,6 +175,7 @@ public: bool locked; // prevents clearing of the data so it can be reused multiple times (useful for screen fades) float screenFade = 1.f; DVector2 offset; + DMatrix3x3 transform; public: int fullscreenautoaspect = 3; int cliptop = -1, clipleft = -1, clipwidth = -1, clipheight = -1; @@ -229,11 +233,14 @@ public: return v; } - void Set(TwoDVertex* v, double xx, double yy, double zz, double uu, double vv, PalEntry col) + void SetTransform(const DShape2DTransform& transform) { - v->Set(xx + offset.X, yy + offset.Y, zz, uu, vv, col); + this->transform = transform.transform; + } + void ClearTransform() + { + this->transform.Identity(); } - int DrawCount() const { diff --git a/src/common/2d/v_draw.cpp b/src/common/2d/v_draw.cpp index 55521e78b1..ba9dad9e36 100644 --- a/src/common/2d/v_draw.cpp +++ b/src/common/2d/v_draw.cpp @@ -1624,4 +1624,27 @@ DEFINE_ACTION_FUNCTION(_Screen, SetOffset) PARAM_FLOAT(x); PARAM_FLOAT(y); ACTION_RETURN_VEC2(twod->SetOffset(DVector2(x, y))); -} \ No newline at end of file +} + +DEFINE_ACTION_FUNCTION(_Screen, SetTransform) +{ + PARAM_PROLOGUE; + PARAM_OBJECT_NOT_NULL(transform, DShape2DTransform); + + if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); + + twod->SetTransform(*transform); + + return 0; +} + +DEFINE_ACTION_FUNCTION(_Screen, ClearTransform) +{ + PARAM_PROLOGUE; + + if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); + + twod->ClearTransform(); + + return 0; +} diff --git a/src/common/utility/vectors.h b/src/common/utility/vectors.h index 1e5fa9f3a8..cb927b4c2d 100644 --- a/src/common/utility/vectors.h +++ b/src/common/utility/vectors.h @@ -915,6 +915,7 @@ struct TMatrix3x3 TMatrix3x3() = default; TMatrix3x3(const TMatrix3x3 &other) = default; + TMatrix3x3& operator=(const TMatrix3x3& other) = default; TMatrix3x3(const Vector3 &row1, const Vector3 &row2, const Vector3 &row3) { diff --git a/wadsrc/static/zscript/engine/base.zs b/wadsrc/static/zscript/engine/base.zs index 8ede0417ac..093f2043f1 100644 --- a/wadsrc/static/zscript/engine/base.zs +++ b/wadsrc/static/zscript/engine/base.zs @@ -469,6 +469,7 @@ class Shape2DTransform : Object native native void Rotate(double angle); native void Scale(Vector2 scaleVec); native void Translate(Vector2 translateVec); + native void From2D(double m00, double m01, double m10, double m11, double vx, double vy); } class Shape2D : Object native @@ -514,6 +515,9 @@ struct Screen native native static Vector2 SetOffset(double x, double y); native static void ClearScreen(color col = 0); native static void SetScreenFade(double factor); + + native static void SetTransform(Shape2DTransform transform); + native static void ClearTransform(); } struct Font native