mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-12-03 17:32:26 +00:00
Modify Shape2D so that it manages its own vertex buffer rather than using the draw list (#1249)
* - modify Shape2D so that it manages its own vertex buffer * - fix the last commit failing on Shape2Ds that were modified after AddShape was called * - make buffers an array of F2DVertexBuffers instead of an array of pointers * - fix AddShape with the same VBO but different transformation crashing the game * - formatting fixes
This commit is contained in:
parent
8b9125fcc6
commit
276cdde112
3 changed files with 185 additions and 98 deletions
|
@ -34,6 +34,8 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
#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; i<dg.mVertCount; i++ )
|
||||
if (shape->needsVertexUpload)
|
||||
{
|
||||
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; i<dg.mVertCount; i++ )
|
||||
{
|
||||
if ( shape->mVertices[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; i<dg.mVertCount; i++ )
|
||||
Set(&ptr[i], 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; i<int(shape->mIndices.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<TwoDVertex>(dg.mVertCount, true);
|
||||
for ( int i=0; i<dg.mVertCount; i++ )
|
||||
verts[i].Set(shape->mVertices[i].X, shape->mVertices[i].Y, 0, shape->mCoords[i].X, shape->mCoords[i].Y, vertexcolor);
|
||||
|
||||
for ( int i=0; i<int(shape->mIndices.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);
|
||||
}
|
||||
|
|
|
@ -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<DVector2> mVertices;
|
||||
TArray<DVector2> 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<DVector2> mTransformedVertices;
|
||||
TArray<F2DVertexBuffer> 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<IVertexBuffer *, IIndexBuffer *> GetBufferObjects() const
|
||||
{
|
||||
return std::make_pair(mVertexBuffer, mIndexBuffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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<IVertexBuffer *, IIndexBuffer *> 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);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue