diff --git a/src/rendering/2d/v_2ddrawer.cpp b/src/rendering/2d/v_2ddrawer.cpp index 1744f12a07..b1ecd9a51c 100644 --- a/src/rendering/2d/v_2ddrawer.cpp +++ b/src/rendering/2d/v_2ddrawer.cpp @@ -35,13 +35,61 @@ EXTERN_CVAR(Float, transsouls) +IMPLEMENT_CLASS(DShape2DTransform, false, false) + +DEFINE_ACTION_FUNCTION(DShape2DTransform, Clear) +{ + PARAM_SELF_PROLOGUE(DShape2DTransform); + self->transform.Identity(); + return 0; +} + +DEFINE_ACTION_FUNCTION(DShape2DTransform, Rotate) +{ + PARAM_SELF_PROLOGUE(DShape2DTransform); + PARAM_FLOAT(angle); + self->transform = DMatrix3x3::Rotate2D(DEG2RAD(angle)) * self->transform; + return 0; +} + +DEFINE_ACTION_FUNCTION(DShape2DTransform, Scale) +{ + PARAM_SELF_PROLOGUE(DShape2DTransform); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + self->transform = DMatrix3x3::Scale2D(DVector2(x, y)) * self->transform; + return 0; +} + +DEFINE_ACTION_FUNCTION(DShape2DTransform, Translate) +{ + PARAM_SELF_PROLOGUE(DShape2DTransform); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + self->transform = DMatrix3x3::Translate2D(DVector2(x, y)) * self->transform; + return 0; +} + IMPLEMENT_CLASS(DShape2D, false, false) +DEFINE_ACTION_FUNCTION(DShape2D, SetTransform) +{ + PARAM_SELF_PROLOGUE(DShape2D); + PARAM_OBJECT(transform, DShape2DTransform); + self->transform = transform->transform; + self->dirty = true; + return 0; +} + DEFINE_ACTION_FUNCTION(DShape2D, Clear) { PARAM_SELF_PROLOGUE(DShape2D); PARAM_INT(which); - if ( which&C_Verts ) self->mVertices.Clear(); + if ( which&C_Verts ) + { + self->mVertices.Clear(); + self->dirty = true; + } if ( which&C_Coords ) self->mCoords.Clear(); if ( which&C_Indices ) self->mIndices.Clear(); return 0; @@ -53,6 +101,7 @@ DEFINE_ACTION_FUNCTION(DShape2D, PushVertex) PARAM_FLOAT(x); PARAM_FLOAT(y); self->mVertices.Push(DVector2(x,y)); + self->dirty = true; return 0; } @@ -380,13 +429,22 @@ void F2DDrawer::AddShape( FTexture *img, DShape2D *shape, DrawParms &parms ) if (!img->isHardwareCanvas() && parms.remap != nullptr && !parms.remap->Inactive) dg.mTranslation = parms.remap; + if (shape->dirty) { + if (shape->mVertices.Size() != shape->mTransformedVertices.Size()) + shape->mTransformedVertices.Resize(shape->mVertices.Size()); + for (int i = 0; i < dg.mVertCount; i++) { + shape->mTransformedVertices[i] = (shape->transform * DVector3(shape->mVertices[i], 1.0)).XY(); + } + shape->dirty = false; + } + double minx = 16383, miny = 16383, maxx = -16384, maxy = -16384; for ( int i=0; imVertices[i].X < minx ) minx = shape->mVertices[i].X; - if ( shape->mVertices[i].Y < miny ) miny = shape->mVertices[i].Y; - if ( shape->mVertices[i].X > maxx ) maxx = shape->mVertices[i].X; - if ( shape->mVertices[i].Y > maxy ) maxy = shape->mVertices[i].Y; + if ( shape->mTransformedVertices[i].X < minx ) minx = shape->mTransformedVertices[i].X; + if ( shape->mTransformedVertices[i].Y < miny ) miny = shape->mTransformedVertices[i].Y; + if ( shape->mTransformedVertices[i].X > maxx ) maxx = shape->mTransformedVertices[i].X; + if ( shape->mTransformedVertices[i].Y > maxy ) maxy = shape->mTransformedVertices[i].Y; } if (minx < (double)parms.lclip || miny < (double)parms.uclip || maxx >(double)parms.rclip || maxy >(double)parms.dclip) { @@ -402,7 +460,7 @@ void F2DDrawer::AddShape( FTexture *img, DShape2D *shape, DrawParms &parms ) dg.mVertIndex = (int)mVertices.Reserve(dg.mVertCount); TwoDVertex *ptr = &mVertices[dg.mVertIndex]; for ( int i=0; imVertices[i].X, shape->mVertices[i].Y, 0, shape->mCoords[i].X, shape->mCoords[i].Y, vertexcolor); + ptr[i].Set(shape->mTransformedVertices[i].X, shape->mTransformedVertices[i].Y, 0, shape->mCoords[i].X, shape->mCoords[i].Y, vertexcolor); dg.mIndexIndex = mIndices.Size(); dg.mIndexCount += shape->mIndices.Size(); for ( int i=0; imIndices.Size()); i+=3 ) diff --git a/src/rendering/2d/v_2ddrawer.h b/src/rendering/2d/v_2ddrawer.h index 57fa88acfe..23aae88d7c 100644 --- a/src/rendering/2d/v_2ddrawer.h +++ b/src/rendering/2d/v_2ddrawer.h @@ -9,6 +9,18 @@ struct DrawParms; +class DShape2DTransform : public DObject +{ + +DECLARE_CLASS(DShape2DTransform, DObject) +public: + DShape2DTransform() + { + transform.Identity(); + } + DMatrix3x3 transform; +}; + // intermediate struct for shape drawing enum EClearWhich @@ -23,9 +35,21 @@ class DShape2D : public DObject DECLARE_CLASS(DShape2D,DObject) public: + DShape2D() + { + transform.Identity(); + } + TArray mIndices; TArray mVertices; TArray mCoords; + + DMatrix3x3 transform; + + // dirty stores whether we need to re-apply the transformation + // otherwise it uses the cached values + bool dirty = true; + TArray mTransformedVertices; }; class F2DDrawer diff --git a/src/utility/vectors.h b/src/utility/vectors.h index d388f825e4..ac4b97642c 100644 --- a/src/utility/vectors.h +++ b/src/utility/vectors.h @@ -979,6 +979,35 @@ Outside comments: A faster version with only 10 (not 24) multiplies. TMatrix3x3(const Vector3 &axis, TAngle degrees); + static TMatrix3x3 Rotate2D(double radians) + { + double c = g_cos(radians); + double s = g_sin(radians); + TMatrix3x3 ret; + ret.Cells[0][0] = c; ret.Cells[0][1] = -s; ret.Cells[0][2] = 0; + ret.Cells[1][0] = s; ret.Cells[1][1] = c; ret.Cells[1][2] = 0; + ret.Cells[2][0] = 0; ret.Cells[2][1] = 0; ret.Cells[2][2] = 1; + return ret; + } + + static TMatrix3x3 Scale2D(TVector2 scaleVec) + { + TMatrix3x3 ret; + ret.Cells[0][0] = scaleVec.X; ret.Cells[0][1] = 0; ret.Cells[0][2] = 0; + ret.Cells[1][0] = 0; ret.Cells[1][1] = scaleVec.Y; ret.Cells[1][2] = 0; + ret.Cells[2][0] = 0; ret.Cells[2][1] = 0; ret.Cells[2][2] = 1; + return ret; + } + + static TMatrix3x3 Translate2D(TVector2 translateVec) + { + TMatrix3x3 ret; + ret.Cells[0][0] = 1; ret.Cells[0][1] = 0; ret.Cells[0][2] = translateVec.X; + ret.Cells[1][0] = 0; ret.Cells[1][1] = 1; ret.Cells[1][2] = translateVec.Y; + ret.Cells[2][0] = 0; ret.Cells[2][1] = 0; ret.Cells[2][2] = 1; + return ret; + } + void Zero() { memset (this, 0, sizeof *this); diff --git a/wadsrc/static/zscript/base.zs b/wadsrc/static/zscript/base.zs index 5a4bd11cfb..3d1af52a3f 100644 --- a/wadsrc/static/zscript/base.zs +++ b/wadsrc/static/zscript/base.zs @@ -193,6 +193,14 @@ enum DrawTextureTags DTA_Monospace, // Strings only: Use a fixed distance between characters. }; +class Shape2DTransform : Object native +{ + native void Clear(); + native void Rotate(double angle); + native void Scale(Vector2 scaleVec); + native void Translate(Vector2 translateVec); +} + class Shape2D : Object native { enum EClearWhich @@ -202,6 +210,8 @@ class Shape2D : Object native C_Indices = 4, }; + native void SetTransform(Shape2DTransform transform); + native void Clear( int which = C_Verts|C_Coords|C_Indices ); native void PushVertex( Vector2 v ); native void PushCoord( Vector2 c );