Add support for using the correct pipeline for each surface

This commit is contained in:
Magnus Norddahl 2023-10-30 01:17:51 +01:00
commit 04493ade52
10 changed files with 247 additions and 118 deletions

View file

@ -238,6 +238,13 @@ struct FMaterialState
}
};
struct LevelSubmeshDrawRange
{
int PipelineID;
int Start;
int Count;
};
class LevelSubmesh
{
public:
@ -260,6 +267,8 @@ public:
std::unique_ptr<TriangleMeshShape> Collision;
TArray<LevelSubmeshDrawRange> DrawList;
// Lightmap atlas
int LMTextureCount = 0;
int LMTextureSize = 0;

View file

@ -66,6 +66,7 @@ void MeshBuilder::Apply()
state.applyData.AlphaThreshold = mSurfaceUniforms.uAlphaThreshold;
state.applyData.DepthFunc = mDepthFunc;
state.applyData.FogEnabled = mFogEnabled;
state.applyData.FogColor = (mFogColor & 0xffffff) == 0;
state.applyData.BrightmapEnabled = mBrightmapEnabled;
state.applyData.TextureClamp = mTextureClamp;
state.applyData.TextureMode = mTextureMode;

View file

@ -7,24 +7,25 @@
class Mesh;
struct MeshApplyData
{
FRenderStyle RenderStyle;
int SpecialEffect;
int TextureEnabled;
float AlphaThreshold;
int DepthFunc;
int FogEnabled;
int FogColor;
int BrightmapEnabled;
int TextureClamp;
int TextureMode;
int TextureModeFlags;
};
class MeshApplyState
{
public:
struct ApplyData
{
FRenderStyle RenderStyle;
int SpecialEffect;
int TextureEnabled;
float AlphaThreshold;
int DepthFunc;
int FogEnabled;
int BrightmapEnabled;
int TextureClamp;
int TextureMode;
int TextureModeFlags;
};
ApplyData applyData;
MeshApplyData applyData;
SurfaceUniforms surfaceUniforms;
FMaterialState material;
VSMatrix textureMatrix;
@ -40,7 +41,7 @@ public:
if (material.mOverrideShader != other.material.mOverrideShader)
return material.mOverrideShader < other.material.mOverrideShader;
int result = memcmp(&applyData, &other.applyData, sizeof(ApplyData));
int result = memcmp(&applyData, &other.applyData, sizeof(MeshApplyData));
if (result != 0)
return result < 0;

View file

@ -74,6 +74,7 @@ class FModelRenderer;
struct SamplerUniform;
struct FVertexBufferAttribute;
struct HWViewpointUniforms;
struct MeshApplyData;
//
// VIDEO
@ -226,6 +227,7 @@ public:
virtual void PostProcessScene(bool swscene, int fixedcm, float flash, const std::function<void()> &afterBloomDrawEndScene2D) { if (afterBloomDrawEndScene2D) afterBloomDrawEndScene2D(); }
virtual int GetLevelMeshPipelineID(const MeshApplyData& applyData, const SurfaceUniforms& surfaceUniforms, const FMaterialState& material) { return 0; }
virtual void DrawLevelMesh(const HWViewpointUniforms& viewpoint) { }
void ScaleCoordsFromWindow(int16_t &x, int16_t &y);

View file

@ -91,11 +91,6 @@ void VkRaytrace::BeginFrame()
}
}
int VkRaytrace::GetIndexCount()
{
return Mesh->StaticMesh->MeshElements.Size();
}
void VkRaytrace::UploadMeshes(bool dynamicOnly)
{
TArray<SubmeshBufferLocation> locations(2);

View file

@ -77,7 +77,7 @@ public:
VulkanBuffer* GetSurfaceUniformsBuffer() { return SurfaceUniformsBuffer.get(); }
VulkanBuffer* GetPortalBuffer() { return PortalBuffer.get(); }
int GetIndexCount();
LevelMesh* GetMesh() { return Mesh; }
private:
struct BLAS

View file

@ -38,6 +38,7 @@
#include "hw_cvars.h"
#include "hw_skydome.h"
#include "flatvertices.h"
#include "hw_meshbuilder.h"
#include "vk_renderdevice.h"
#include "vulkan/vk_renderstate.h"
@ -70,6 +71,7 @@ FString JitCaptureStackTrace(int framesToSkip, bool includeNativeFrames, int max
EXTERN_CVAR(Int, gl_tonemap)
EXTERN_CVAR(Int, screenblocks)
EXTERN_CVAR(Bool, cl_capfps)
EXTERN_CVAR(Bool, r_skipmats)
// Physical device info
static std::vector<VulkanCompatibleDevice> SupportedDevices;
@ -611,6 +613,120 @@ int VulkanRenderDevice::GetBindlessTextureIndex(FMaterial* material, int clampmo
return static_cast<VkMaterial*>(material)->GetBindlessIndex(materialState);
}
int VulkanRenderDevice::GetLevelMeshPipelineID(const MeshApplyData& applyData, const SurfaceUniforms& surfaceUniforms, const FMaterialState& material)
{
if (levelVertexFormatIndex == -1)
{
static const std::vector<FVertexBufferAttribute> format =
{
{ 0, VATTR_VERTEX, VFmt_Float4, (int)myoffsetof(FFlatVertex, x) },
{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FFlatVertex, u) },
{ 0, VATTR_LIGHTMAP, VFmt_Float2, (int)myoffsetof(FFlatVertex, lu) },
{ 1, VATTR_UNIFORM_INDEXES, VFmt_Int, 0 }
};
levelVertexFormatIndex = GetRenderPassManager()->GetVertexFormat({ sizeof(FFlatVertex), sizeof(int32_t) }, format);
}
VkPipelineKey pipelineKey;
pipelineKey.DrawType = DT_Triangles;
pipelineKey.VertexFormat = levelVertexFormatIndex;
pipelineKey.RenderStyle = applyData.RenderStyle;
pipelineKey.DepthTest = true; // mDepthTest;
pipelineKey.DepthWrite = true; // mDepthTest && mDepthWrite;
pipelineKey.DepthFunc = applyData.DepthFunc;
pipelineKey.DepthClamp = false; // mDepthClamp;
pipelineKey.DepthBias = false; // !(mBias.mFactor == 0 && mBias.mUnits == 0);
pipelineKey.StencilTest = false; // mStencilTest;
pipelineKey.StencilPassOp = 0; // mStencilOp;
pipelineKey.ColorMask = 15; // mColorMask;
pipelineKey.CullMode = 0; // mCullMode;
pipelineKey.NumTextureLayers = material.mMaterial ? material.mMaterial->NumLayers() : 0;
pipelineKey.NumTextureLayers = max(pipelineKey.NumTextureLayers, SHADER_MIN_REQUIRED_TEXTURE_LAYERS);// Always force minimum 8 textures as the shader requires it
if (applyData.SpecialEffect > EFF_NONE)
{
pipelineKey.ShaderKey.SpecialEffect = applyData.SpecialEffect;
pipelineKey.ShaderKey.EffectState = 0;
pipelineKey.ShaderKey.AlphaTest = false;
}
else
{
int effectState = material.mOverrideShader >= 0 ? material.mOverrideShader : (material.mMaterial ? material.mMaterial->GetShaderIndex() : 0);
pipelineKey.ShaderKey.SpecialEffect = EFF_NONE;
pipelineKey.ShaderKey.EffectState = applyData.TextureEnabled ? effectState : SHADER_NoTexture;
if (r_skipmats && pipelineKey.ShaderKey.EffectState >= 3 && pipelineKey.ShaderKey.EffectState <= 4)
pipelineKey.ShaderKey.EffectState = 0;
pipelineKey.ShaderKey.AlphaTest = surfaceUniforms.uAlphaThreshold >= 0.f;
}
int tempTM = (material.mMaterial && material.mMaterial->Source()->isHardwareCanvas()) ? TM_OPAQUE : TM_NORMAL;
int f = applyData.TextureModeFlags;
if (!applyData.BrightmapEnabled) f &= ~(TEXF_Brightmap | TEXF_Glowmap);
if (applyData.TextureClamp) f |= TEXF_ClampY;
int uTextureMode = (applyData.TextureMode == TM_NORMAL && tempTM == TM_OPAQUE ? TM_OPAQUE : applyData.TextureMode) | f;
pipelineKey.ShaderKey.TextureMode = uTextureMode & 0xffff;
pipelineKey.ShaderKey.ClampY = (uTextureMode & TEXF_ClampY) != 0;
pipelineKey.ShaderKey.Brightmap = (uTextureMode & TEXF_Brightmap) != 0;
pipelineKey.ShaderKey.Detailmap = (uTextureMode & TEXF_Detailmap) != 0;
pipelineKey.ShaderKey.Glowmap = (uTextureMode & TEXF_Glowmap) != 0;
// The way GZDoom handles state is just plain insanity!
int fogset = 0;
if (applyData.FogEnabled)
{
if (applyData.FogEnabled == 2)
{
fogset = -3; // 2D rendering with 'foggy' overlay.
}
else if (applyData.FogColor)
{
fogset = gl_fogmode;
}
else
{
fogset = -gl_fogmode;
}
}
pipelineKey.ShaderKey.Simple2D = (fogset == -3);
pipelineKey.ShaderKey.FogBeforeLights = (fogset > 0);
pipelineKey.ShaderKey.FogAfterLights = (fogset < 0);
pipelineKey.ShaderKey.FogRadial = (fogset < -1 || fogset > 1);
pipelineKey.ShaderKey.SWLightRadial = (gl_fogmode == 2);
pipelineKey.ShaderKey.SWLightBanded = false; // gl_bandedswlight;
float lightlevel = surfaceUniforms.uLightLevel;
if (lightlevel < 0.0)
{
pipelineKey.ShaderKey.LightMode = 0; // Default
}
else
{
/*if (mLightMode == 5)
pipelineKey.ShaderKey.LightMode = 3; // Build
else if (mLightMode == 16)
pipelineKey.ShaderKey.LightMode = 2; // Vanilla
else*/
pipelineKey.ShaderKey.LightMode = 1; // Software
}
pipelineKey.ShaderKey.UseShadowmap = gl_light_shadows == 1;
pipelineKey.ShaderKey.UseRaytrace = gl_light_shadows == 2;
pipelineKey.ShaderKey.GBufferPass = false;
pipelineKey.ShaderKey.UseLevelMesh = true;
for (unsigned int i = 0, count = levelMeshPipelineKeys.Size(); i < count; i++)
{
if (levelMeshPipelineKeys[i] == pipelineKey)
{
return i;
}
}
levelMeshPipelineKeys.Push(pipelineKey);
return levelMeshPipelineKeys.Size() - 1;
}
void VulkanRenderDevice::DrawLevelMesh(const HWViewpointUniforms& viewpoint)
{
auto cmdbuffer = GetCommands()->GetDrawCommands();
@ -658,48 +774,11 @@ void VulkanRenderDevice::DrawLevelMesh(const HWViewpointUniforms& viewpoint)
cmdbuffer->setStencilReference(VK_STENCIL_FRONT_AND_BACK, 0);
cmdbuffer->setDepthBias(0.0f, 0.0f, 0.0f);
static const std::vector<FVertexBufferAttribute> format =
{
{ 0, VATTR_VERTEX, VFmt_Float4, (int)myoffsetof(FFlatVertex, x) },
{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FFlatVertex, u) },
{ 0, VATTR_LIGHTMAP, VFmt_Float2, (int)myoffsetof(FFlatVertex, lu) },
{ 1, VATTR_UNIFORM_INDEXES, VFmt_Int, 0 }
};
int vertexFormatIndex = GetRenderPassManager()->GetVertexFormat({ sizeof(FFlatVertex), sizeof(int32_t) }, format);
VkBuffer vertexBuffers[2] = { GetRaytrace()->GetVertexBuffer()->buffer, GetRaytrace()->GetUniformIndexBuffer()->buffer };
VkDeviceSize vertexBufferOffsets[] = { 0, 0 };
cmdbuffer->bindVertexBuffers(0, 2, vertexBuffers, vertexBufferOffsets);
cmdbuffer->bindIndexBuffer(GetRaytrace()->GetIndexBuffer()->buffer, 0, VK_INDEX_TYPE_UINT32);
VkPipelineKey pipelineKey;
pipelineKey.DrawType = DT_Triangles;
pipelineKey.VertexFormat = vertexFormatIndex;
pipelineKey.RenderStyle = DefaultRenderStyle();
pipelineKey.DepthTest = true;
pipelineKey.DepthWrite = true;
pipelineKey.DepthFunc = DF_Less;
pipelineKey.DepthClamp = false;
pipelineKey.DepthBias = false;
pipelineKey.StencilTest = false;
pipelineKey.StencilPassOp = 0;
pipelineKey.ColorMask = 15;
pipelineKey.CullMode = 0;
pipelineKey.NumTextureLayers = 0;
pipelineKey.NumTextureLayers = max(pipelineKey.NumTextureLayers, SHADER_MIN_REQUIRED_TEXTURE_LAYERS);// Always force minimum 8 textures as the shader requires it
pipelineKey.ShaderKey.SpecialEffect = EFF_NONE;
pipelineKey.ShaderKey.EffectState = SHADER_Default;
pipelineKey.ShaderKey.AlphaTest = false;
pipelineKey.ShaderKey.SWLightRadial = true;
pipelineKey.ShaderKey.LightMode = 1; // Software
pipelineKey.ShaderKey.UseShadowmap = gl_light_shadows == 1;
pipelineKey.ShaderKey.UseRaytrace = gl_light_shadows == 2;
pipelineKey.ShaderKey.GBufferPass = key.DrawBuffers > 1;
pipelineKey.ShaderKey.UseLevelMesh = true;
VulkanPipelineLayout* layout = GetRenderPassManager()->GetPipelineLayout(pipelineKey.NumTextureLayers, pipelineKey.ShaderKey.UseLevelMesh);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, passSetup->GetPipeline(pipelineKey));
auto rsbuffers = GetBufferManager()->GetRSBuffers();
memcpy(((char*)rsbuffers->Viewpoint.Data) + rsbuffers->Viewpoint.UploadIndex * rsbuffers->Viewpoint.BlockAlign, &viewpoint, sizeof(HWViewpointUniforms));
int viewpointIndex = rsbuffers->Viewpoint.UploadIndex++;
@ -710,21 +789,29 @@ void VulkanRenderDevice::DrawLevelMesh(const HWViewpointUniforms& viewpoint)
matrices.TextureMatrix.loadIdentity();
rsbuffers->MatrixBuffer->Write(matrices);
uint32_t viewpointOffset = viewpointIndex * rsbuffers->Viewpoint.BlockAlign;
uint32_t matrixOffset = rsbuffers->MatrixBuffer->Offset();
uint32_t lightsOffset = 0;
uint32_t offsets[] = { viewpointOffset, matrixOffset, lightsOffset };
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptors->GetFixedSet());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 1, descriptors->GetLevelMeshSet(), 3, offsets);
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 2, descriptors->GetBindlessSet());
auto submesh = GetRaytrace()->GetMesh()->StaticMesh.get();
for (LevelSubmeshDrawRange& range : submesh->DrawList)
{
auto& pipelineKey = levelMeshPipelineKeys[range.PipelineID];
VulkanPipelineLayout* layout = GetRenderPassManager()->GetPipelineLayout(pipelineKey.NumTextureLayers, pipelineKey.ShaderKey.UseLevelMesh);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, passSetup->GetPipeline(pipelineKey));
PushConstants pushConstants = {};
pushConstants.uDataIndex = 0;
pushConstants.uLightIndex = -1;
pushConstants.uBoneIndexBase = -1;
cmdbuffer->pushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, (uint32_t)sizeof(PushConstants), &pushConstants);
uint32_t viewpointOffset = viewpointIndex * rsbuffers->Viewpoint.BlockAlign;
uint32_t matrixOffset = rsbuffers->MatrixBuffer->Offset();
uint32_t lightsOffset = 0;
uint32_t offsets[] = { viewpointOffset, matrixOffset, lightsOffset };
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptors->GetFixedSet());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 1, descriptors->GetLevelMeshSet(), 3, offsets);
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 2, descriptors->GetBindlessSet());
cmdbuffer->drawIndexed(GetRaytrace()->GetIndexCount(), 1, 0, 0, 0);
PushConstants pushConstants = {};
pushConstants.uDataIndex = 0;
pushConstants.uLightIndex = -1;
pushConstants.uBoneIndexBase = -1;
cmdbuffer->pushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, (uint32_t)sizeof(PushConstants), &pushConstants);
cmdbuffer->drawIndexed(range.Count, 1, range.Start, 0, 0);
}
cmdbuffer->endRenderPass();
}

View file

@ -22,6 +22,7 @@ class VkHardwareDataBuffer;
class VkHardwareTexture;
class VkRenderBuffers;
class VkPostprocess;
class VkPipelineKey;
class VulkanRenderDevice : public SystemBaseFrameBuffer
{
@ -89,6 +90,7 @@ public:
int GetBindlessTextureIndex(FMaterial* material, int clampmode, int translation) override;
int GetLevelMeshPipelineID(const MeshApplyData& applyData, const SurfaceUniforms& surfaceUniforms, const FMaterialState& material) override;
void DrawLevelMesh(const HWViewpointUniforms& viewpoint) override;
private:
@ -118,6 +120,9 @@ private:
LevelMesh* levelMesh = nullptr;
bool levelMeshChanged = true;
int levelVertexFormatIndex = -1;
TArray<VkPipelineKey> levelMeshPipelineKeys;
};
class CVulkanError : public CEngineError

View file

@ -112,20 +112,31 @@ void DoomLevelSubmesh::CreateStaticSurfaces(FLevelLocals& doomMap)
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_Less);
state.AlphaFunc(Alpha_GEqual, 0.f);
state.ClearDepthBias();
state.EnableTexture(gl_texture);
state.EnableBrightmap(true);
for (HWWall& wallpart : result.list)
{
if (wallpart.texture && wallpart.texture->isMasked())
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
wallpart.DrawWall(&disp, state, false);
int pipelineID = 0;
int startVertIndex = MeshVertices.Size();
for (auto& it : state.mSortedLists)
{
const MeshApplyState& applyState = it.first;
pipelineID = screen->GetLevelMeshPipelineID(applyState.applyData, applyState.surfaceUniforms, applyState.material);
int uniformsIndex = MeshSurfaceUniforms.Size();
MeshSurfaceUniforms.Push(applyState.surfaceUniforms);
MeshSurfaceMaterials.Push(applyState.material);
@ -154,6 +165,7 @@ void DoomLevelSubmesh::CreateStaticSurfaces(FLevelLocals& doomMap)
surf.numVerts = MeshVertices.Size() - startVertIndex;
surf.plane = ToPlane(MeshVertices[startVertIndex].fPos(), MeshVertices[startVertIndex + 1].fPos(), MeshVertices[startVertIndex + 2].fPos(), MeshVertices[startVertIndex + 3].fPos());
surf.texture = wallpart.texture;
surf.PipelineID = pipelineID;
Surfaces.Push(surf);
}
}
@ -204,64 +216,80 @@ void DoomLevelSubmesh::CreateDynamicSurfaces(FLevelLocals& doomMap)
void DoomLevelSubmesh::CreateIndexes()
{
// Order indexes by pipeline
std::unordered_map<int, TArray<int>> pipelineSurfaces;
for (size_t i = 0; i < Surfaces.Size(); i++)
{
DoomLevelMeshSurface& s = Surfaces[i];
int numVerts = s.numVerts;
unsigned int pos = s.startVertIndex;
FFlatVertex* verts = &MeshVertices[pos];
DoomLevelMeshSurface* s = &Surfaces[i];
pipelineSurfaces[s->PipelineID].Push(i);
}
s.Vertices = verts;
s.startElementIndex = MeshElements.Size();
s.numElements = 0;
if (s.Type == ST_CEILING)
for (const auto& it : pipelineSurfaces)
{
LevelSubmeshDrawRange range;
range.PipelineID = it.first;
range.Start = MeshElements.Size();
for (unsigned int i : it.second)
{
for (int j = 2; j < numVerts; j++)
DoomLevelMeshSurface& s = Surfaces[i];
int numVerts = s.numVerts;
unsigned int pos = s.startVertIndex;
FFlatVertex* verts = &MeshVertices[pos];
s.Vertices = verts;
s.startElementIndex = MeshElements.Size();
s.numElements = 0;
if (s.Type == ST_CEILING)
{
if (!IsDegenerate(verts[0].fPos(), verts[j - 1].fPos(), verts[j].fPos()))
for (int j = 2; j < numVerts; j++)
{
MeshElements.Push(pos);
MeshElements.Push(pos + j - 1);
MeshElements.Push(pos + j);
if (!IsDegenerate(verts[0].fPos(), verts[j - 1].fPos(), verts[j].fPos()))
{
MeshElements.Push(pos);
MeshElements.Push(pos + j - 1);
MeshElements.Push(pos + j);
MeshSurfaceIndexes.Push((int)i);
s.numElements += 3;
}
}
}
else if (s.Type == ST_FLOOR)
{
for (int j = 2; j < numVerts; j++)
{
if (!IsDegenerate(verts[0].fPos(), verts[j - 1].fPos(), verts[j].fPos()))
{
MeshElements.Push(pos + j);
MeshElements.Push(pos + j - 1);
MeshElements.Push(pos);
MeshSurfaceIndexes.Push((int)i);
s.numElements += 3;
}
}
}
else if (s.Type == ST_MIDDLESIDE || s.Type == ST_UPPERSIDE || s.Type == ST_LOWERSIDE)
{
if (!IsDegenerate(verts[0].fPos(), verts[2].fPos(), verts[1].fPos()))
{
MeshElements.Push(pos + 0);
MeshElements.Push(pos + 1);
MeshElements.Push(pos + 2);
MeshSurfaceIndexes.Push((int)i);
s.numElements += 3;
}
if (!IsDegenerate(verts[0].fPos(), verts[2].fPos(), verts[3].fPos()))
{
MeshElements.Push(pos + 0);
MeshElements.Push(pos + 2);
MeshElements.Push(pos + 3);
MeshSurfaceIndexes.Push((int)i);
s.numElements += 3;
}
}
}
else if (s.Type == ST_FLOOR)
{
for (int j = 2; j < numVerts; j++)
{
if (!IsDegenerate(verts[0].fPos(), verts[j - 1].fPos(), verts[j].fPos()))
{
MeshElements.Push(pos + j);
MeshElements.Push(pos + j - 1);
MeshElements.Push(pos);
MeshSurfaceIndexes.Push((int)i);
s.numElements += 3;
}
}
}
else if (s.Type == ST_MIDDLESIDE || s.Type == ST_UPPERSIDE || s.Type == ST_LOWERSIDE)
{
if (!IsDegenerate(verts[0].fPos(), verts[2].fPos(), verts[1].fPos()))
{
MeshElements.Push(pos + 0);
MeshElements.Push(pos + 1);
MeshElements.Push(pos + 2);
MeshSurfaceIndexes.Push((int)i);
s.numElements += 3;
}
if (!IsDegenerate(verts[0].fPos(), verts[2].fPos(), verts[3].fPos()))
{
MeshElements.Push(pos + 0);
MeshElements.Push(pos + 2);
MeshElements.Push(pos + 3);
MeshSurfaceIndexes.Push((int)i);
s.numElements += 3;
}
}
range.Count = MeshElements.Size() - range.Start;
DrawList.Push(range);
}
}

View file

@ -34,6 +34,7 @@ struct DoomLevelMeshSurface : public LevelMeshSurface
sector_t* ControlSector = nullptr;
FFlatVertex* Vertices = nullptr;
int PipelineID = 0;
};
class DoomLevelSubmesh : public LevelSubmesh