diff --git a/src/common/2d/v_2ddrawer.cpp b/src/common/2d/v_2ddrawer.cpp index a3b3bd12d..157101160 100644 --- a/src/common/2d/v_2ddrawer.cpp +++ b/src/common/2d/v_2ddrawer.cpp @@ -34,6 +34,8 @@ #include #include "templates.h" +#include "v_2ddrawer.h" +#include "vectors.h" #include "vm.h" #include "c_cvars.h" #include "v_draw.h" @@ -107,7 +109,6 @@ IMPLEMENT_CLASS(DShape2D, false, false) static void Shape2D_SetTransform(DShape2D* self, DShape2DTransform *transform) { self->transform = transform->transform; - self->dirty = true; } DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, SetTransform, Shape2D_SetTransform) @@ -120,13 +121,10 @@ DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, SetTransform, Shape2D_SetTransform) static void Shape2D_Clear(DShape2D* self, int which) { - if (which & C_Verts) - { - self->mVertices.Clear(); - self->dirty = true; - } + if (which & C_Verts) self->mVertices.Clear(); if (which & C_Coords) self->mCoords.Clear(); if (which & C_Indices) self->mIndices.Clear(); + self->needsVertexUpload = true; } DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, Clear, Shape2D_Clear) @@ -140,7 +138,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, Clear, Shape2D_Clear) static void Shape2D_PushVertex(DShape2D* self, double x, double y) { self->mVertices.Push(DVector2(x, y)); - self->dirty = true; + self->needsVertexUpload = true; } DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, PushVertex, Shape2D_PushVertex) @@ -155,6 +153,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, PushVertex, Shape2D_PushVertex) static void Shape2D_PushCoord(DShape2D* self, double u, double v) { self->mCoords.Push(DVector2(u, v)); + self->needsVertexUpload = true; } DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, PushCoord, Shape2D_PushCoord) @@ -171,6 +170,7 @@ static void Shape2D_PushTriangle(DShape2D* self, int a, int b, int c) self->mIndices.Push(a); self->mIndices.Push(b); self->mIndices.Push(c); + self->needsVertexUpload = true; } DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, PushTriangle, Shape2D_PushTriangle) @@ -556,26 +556,33 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms) if (!img->isHardwareCanvas() && parms.TranslationId != -1) dg.mTranslationId = parms.TranslationId; - 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; - } - auto osave = offset; if (parms.nooffset) offset = { 0,0 }; - double minx = 16383, miny = 16383, maxx = -16384, maxy = -16384; - for ( int i=0; ineedsVertexUpload) { - 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; + shape->minx = 16383; + shape->miny = 16383; + shape->maxx = -16384; + shape->maxy = -16384; + for ( int i=0; imVertices[i].X < shape->minx ) shape->minx = shape->mVertices[i].X; + if ( shape->mVertices[i].Y < shape->miny ) shape->miny = shape->mVertices[i].Y; + if ( shape->mVertices[i].X > shape->maxx ) shape->maxx = shape->mVertices[i].X; + if ( shape->mVertices[i].Y > shape->maxy ) shape->maxy = shape->mVertices[i].Y; + } } + 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() + }; + 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 (minx < (double)parms.lclip || miny < (double)parms.uclip || maxx >(double)parms.rclip || maxy >(double)parms.dclip) { dg.mScissor[0] = parms.lclip + int(offset.X); @@ -587,24 +594,39 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms) else memset(dg.mScissor, 0, sizeof(dg.mScissor)); - dg.mVertIndex = (int)mVertices.Reserve(dg.mVertCount); - TwoDVertex *ptr = &mVertices[dg.mVertIndex]; - for ( int i=0; imTransformedVertices[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 ) + dg.useTransform = true; + dg.transform = shape->transform; + dg.transform.Cells[0][2] += offset.X; + dg.transform.Cells[1][2] += offset.Y; + dg.shape2D = shape; + dg.shape2DIndexCount = shape->mIndices.Size(); + if (shape->needsVertexUpload) { - // [MK] bail out if any indices are out of bounds - for ( int j=0; j<3; j++ ) + shape->bufIndex += 1; + + shape->buffers.Reserve(1); + auto buf = &shape->buffers[shape->bufIndex]; + + auto verts = TArray(dg.mVertCount, true); + for ( int i=0; imVertices[i].X, shape->mVertices[i].Y, 0, shape->mCoords[i].X, shape->mCoords[i].Y, vertexcolor); + + for ( int i=0; imIndices.Size()); i+=3 ) { - if ( shape->mIndices[i+j] < 0 ) - ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Triangle %u index %u is negative: %i\n", i/3, j, shape->mIndices[i+j]); - if ( shape->mIndices[i+j] >= dg.mVertCount ) - ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Triangle %u index %u: %u, max: %u\n", i/3, j, shape->mIndices[i+j], dg.mVertCount-1); + // [MK] bail out if any indices are out of bounds + for ( int j=0; j<3; j++ ) + { + if ( shape->mIndices[i+j] < 0 ) + ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Triangle %u index %u is negative: %i\n", i/3, j, shape->mIndices[i+j]); + if ( shape->mIndices[i+j] >= dg.mVertCount ) + ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Triangle %u index %u: %u, max: %u\n", i/3, j, shape->mIndices[i+j], dg.mVertCount-1); + } } - AddIndices(dg.mVertIndex, 3, shape->mIndices[i], shape->mIndices[i+1], shape->mIndices[i+2]); + + buf->UploadData(&verts[0], dg.mVertCount, &shape->mIndices[0], shape->mIndices.Size()); + shape->needsVertexUpload = false; } + dg.shape2DBufIndex = shape->bufIndex; AddCommand(&dg); offset = osave; } @@ -1014,3 +1036,16 @@ void F2DDrawer::Clear() } screenFade = 1.f; } + +F2DVertexBuffer::F2DVertexBuffer() +{ + mVertexBuffer = screen->CreateVertexBuffer(); + mIndexBuffer = screen->CreateIndexBuffer(); + + static const FVertexBufferAttribute format[] = { + { 0, VATTR_VERTEX, VFmt_Float3, (int)myoffsetof(F2DDrawer::TwoDVertex, x) }, + { 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(F2DDrawer::TwoDVertex, u) }, + { 0, VATTR_COLOR, VFmt_Byte4, (int)myoffsetof(F2DDrawer::TwoDVertex, color0) } + }; + mVertexBuffer->SetFormat(1, 3, sizeof(F2DDrawer::TwoDVertex), format); +} diff --git a/src/common/2d/v_2ddrawer.h b/src/common/2d/v_2ddrawer.h index 2e02e9160..ce94c4233 100644 --- a/src/common/2d/v_2ddrawer.h +++ b/src/common/2d/v_2ddrawer.h @@ -1,6 +1,7 @@ #ifndef __2DDRAWER_H #define __2DDRAWER_H +#include "buffers.h" #include "tarray.h" #include "vectors.h" #include "textures.h" @@ -31,6 +32,8 @@ enum EClearWhich C_Indices = 4, }; +class F2DVertexBuffer; + class DShape2D : public DObject { @@ -45,12 +48,16 @@ public: TArray mVertices; TArray mCoords; + double minx = 0.0; + double maxx = 0.0; + double miny = 0.0; + double maxy = 0.0; + DMatrix3x3 transform; - // dirty stores whether we need to re-apply the transformation - // otherwise it uses the cached values - bool dirty = true; - TArray mTransformedVertices; + TArray buffers; + bool needsVertexUpload = true; + int bufIndex = -1; }; struct F2DPolygons @@ -67,6 +74,7 @@ struct F2DPolygons }; + class F2DDrawer { public: @@ -136,6 +144,13 @@ public: uint8_t mLightLevel; uint8_t mFlags; + bool useTransform; + DMatrix3x3 transform; + + DShape2D* shape2D; + int shape2DBufIndex; + int shape2DIndexCount; + RenderCommand() { memset(this, 0, sizeof(*this)); @@ -144,6 +159,7 @@ public: // If these fields match, two draw commands can be batched. bool isCompatible(const RenderCommand &other) const { + if (shape2D != nullptr || other.shape2D != nullptr) return false; return mTexture == other.mTexture && mType == other.mType && mTranslationId == other.mTranslationId && @@ -155,8 +171,16 @@ public: mDrawMode == other.mDrawMode && mFlags == other.mFlags && mLightLevel == other.mLightLevel && - mColor1.d == other.mColor1.d; - + mColor1.d == other.mColor1.d && + useTransform == other.useTransform && + ( + !useTransform || + ( + transform[0] == other.transform[0] && + transform[1] == other.transform[1] && + transform[2] == other.transform[2] + ) + ); } }; @@ -238,5 +262,39 @@ public: bool mIsFirstPass = true; }; +//=========================================================================== +// +// Vertex buffer for 2D drawer +// +//=========================================================================== + +class F2DVertexBuffer +{ + IVertexBuffer *mVertexBuffer; + IIndexBuffer *mIndexBuffer; + + +public: + + F2DVertexBuffer(); + + ~F2DVertexBuffer() + { + delete mIndexBuffer; + delete mVertexBuffer; + } + + void UploadData(F2DDrawer::TwoDVertex *vertices, int vertcount, int *indices, int indexcount) + { + mVertexBuffer->SetData(vertcount * sizeof(*vertices), vertices, false); + mIndexBuffer->SetData(indexcount * sizeof(unsigned int), indices, false); + } + + std::pair GetBufferObjects() const + { + return std::make_pair(mVertexBuffer, mIndexBuffer); + } +}; + #endif diff --git a/src/common/rendering/hwrenderer/hw_draw2d.cpp b/src/common/rendering/hwrenderer/hw_draw2d.cpp index be1c6831e..21b7f5afc 100644 --- a/src/common/rendering/hwrenderer/hw_draw2d.cpp +++ b/src/common/rendering/hwrenderer/hw_draw2d.cpp @@ -43,51 +43,6 @@ #include "r_videoscale.h" #include "v_draw.h" - -//=========================================================================== -// -// Vertex buffer for 2D drawer -// -//=========================================================================== - -class F2DVertexBuffer -{ - IVertexBuffer *mVertexBuffer; - IIndexBuffer *mIndexBuffer; - - -public: - - F2DVertexBuffer() - { - mVertexBuffer = screen->CreateVertexBuffer(); - mIndexBuffer = screen->CreateIndexBuffer(); - - static const FVertexBufferAttribute format[] = { - { 0, VATTR_VERTEX, VFmt_Float3, (int)myoffsetof(F2DDrawer::TwoDVertex, x) }, - { 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(F2DDrawer::TwoDVertex, u) }, - { 0, VATTR_COLOR, VFmt_Byte4, (int)myoffsetof(F2DDrawer::TwoDVertex, color0) } - }; - mVertexBuffer->SetFormat(1, 3, sizeof(F2DDrawer::TwoDVertex), format); - } - ~F2DVertexBuffer() - { - delete mIndexBuffer; - delete mVertexBuffer; - } - - void UploadData(F2DDrawer::TwoDVertex *vertices, int vertcount, int *indices, int indexcount) - { - mVertexBuffer->SetData(vertcount * sizeof(*vertices), vertices, false); - mIndexBuffer->SetData(indexcount * sizeof(unsigned int), indices, false); - } - - std::pair GetBufferObjects() const - { - return std::make_pair(mVertexBuffer, mIndexBuffer); - } -}; - //=========================================================================== // // Draws the 2D stuff. This is the version for OpenGL 3 and later. @@ -174,6 +129,29 @@ void Draw2D(F2DDrawer *drawer, FRenderState &state) state.AlphaFunc(Alpha_GEqual, 0.f); + if (cmd.useTransform) + { + FLOATTYPE m[16] = { + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + }; + for (size_t i = 0; i < 2; i++) + { + for (size_t j = 0; j < 2; j++) + { + m[4 * j + i] = (FLOATTYPE) cmd.transform.Cells[i][j]; + } + } + for (size_t i = 0; i < 2; i++) + { + m[4 * 3 + i] = (FLOATTYPE) cmd.transform.Cells[i][2]; + } + state.mModelMatrix.loadMatrix(m); + state.EnableModelMatrix(true); + } + if (cmd.mTexture != nullptr && cmd.mTexture->isValid()) { auto flags = cmd.mTexture->GetUseType() >= ETextureType::Special? UF_None : cmd.mTexture->GetUseType() == ETextureType::FontChar? UF_Font : UF_Texture; @@ -200,26 +178,42 @@ void Draw2D(F2DDrawer *drawer, FRenderState &state) state.EnableTexture(false); } - switch (cmd.mType) + if (cmd.shape2D != nullptr) { - default: - case F2DDrawer::DrawTypeTriangles: - state.DrawIndexed(DT_Triangles, cmd.mIndexIndex, cmd.mIndexCount); - break; + state.SetVertexBuffer(&cmd.shape2D->buffers[cmd.shape2DBufIndex]); + state.DrawIndexed(DT_Triangles, 0, cmd.shape2DIndexCount); + state.SetVertexBuffer(&vb); + if (cmd.shape2D->bufIndex > 0 && cmd.shape2DBufIndex == cmd.shape2D->bufIndex) + { + cmd.shape2D->needsVertexUpload = true; + cmd.shape2D->buffers.Clear(); + cmd.shape2D->bufIndex = -1; + } + } + else + { + switch (cmd.mType) + { + default: + case F2DDrawer::DrawTypeTriangles: + state.DrawIndexed(DT_Triangles, cmd.mIndexIndex, cmd.mIndexCount); + break; - case F2DDrawer::DrawTypeLines: - state.Draw(DT_Lines, cmd.mVertIndex, cmd.mVertCount); - break; + case F2DDrawer::DrawTypeLines: + state.Draw(DT_Lines, cmd.mVertIndex, cmd.mVertCount); + break; - case F2DDrawer::DrawTypePoints: - state.Draw(DT_Points, cmd.mVertIndex, cmd.mVertCount); - break; + case F2DDrawer::DrawTypePoints: + state.Draw(DT_Points, cmd.mVertIndex, cmd.mVertCount); + break; + } } state.SetObjectColor(0xffffffff); state.SetObjectColor2(0); state.SetAddColor(0); state.EnableTextureMatrix(false); + state.EnableModelMatrix(false); state.SetEffect(EFF_NONE); }