Hook up texture loading

This commit is contained in:
Magnus Norddahl 2024-03-24 07:09:44 +01:00
parent e1fe4658b5
commit a6e4290431
9 changed files with 220 additions and 30 deletions

View file

@ -59,6 +59,7 @@ class FolderFileSystemSource : public IFileSystemSource
public:
FolderFileSystemSource(const FString& foldername)
{
Basepath = foldername;
ScanFolder(foldername.GetChars());
}
@ -69,15 +70,15 @@ public:
int CheckNumForFullName(const FString& fullname) override
{
auto it = FilenameKeyToIndex.find(fullname.GetChars());
if (it == FilenameKeyToIndex.end())
auto it = FullnameKeyToIndex.find(ToFullnameKey(fullname.GetChars()));
if (it == FullnameKeyToIndex.end())
return -1;
return (int)it->second;
}
int FileLength(int lump) override
{
int64_t size = File::open_existing(Filenames[lump])->size();
int64_t size = File::open_existing(FilePath::combine(Basepath, Filenames[lump]))->size();
// For safety. The filesystem API clearly wasn't designed to handle large files.
if (size >= 0x7fffffff) throw std::runtime_error("File is too big");
return (int)size;
@ -86,7 +87,7 @@ public:
FileData ReadFile(int lump) override
{
FileData data;
data.Buffer = File::read_all_bytes(Filenames[lump]);
data.Buffer = File::read_all_bytes(FilePath::combine(Basepath, Filenames[lump]));
return data;
}
@ -95,26 +96,40 @@ public:
return Filenames[lump].c_str();
}
void ScanFolder(const std::string& foldername, int depth = 0)
static std::string ToFullnameKey(std::string name)
{
for (const std::string& filename : Directory::files(foldername))
for (char& c : name)
{
std::string fullname = FilePath::combine(foldername, filename);
FilenameKeyToIndex[fullname] = Filenames.size();
if (c == '\\')
c = '/';
if (c >= 'A' && c <= 'Z')
c = c - 'A' + 'a';
}
return name;
}
void ScanFolder(const std::string& foldername, const std::string& relativefoldername = {}, int depth = 0)
{
std::string search = FilePath::combine(foldername, "*");
for (const std::string& filename : Directory::files(search))
{
std::string fullname = FilePath::combine(relativefoldername, filename);
FullnameKeyToIndex[ToFullnameKey(fullname)] = Filenames.size();
Filenames.push_back(fullname);
}
if (depth < 16)
{
for (const std::string& subfolder : Directory::folders(foldername))
for (const std::string& subfolder : Directory::folders(search))
{
ScanFolder(FilePath::combine(foldername, subfolder), depth + 1);
ScanFolder(FilePath::combine(foldername, subfolder), FilePath::combine(relativefoldername, subfolder), depth + 1);
}
}
}
std::string Basepath;
std::vector<std::string> Filenames;
std::map<std::string, size_t> FilenameKeyToIndex;
std::map<std::string, size_t> FullnameKeyToIndex;
};
/////////////////////////////////////////////////////////////////////////////

View file

@ -7,7 +7,15 @@ FTextureManager TexMan;
FGameTexture::FGameTexture(FString name) : Name(name)
{
// To do: improve this to support subfolders?
// To do: figure out what type of data we got here instead of assuming it is a png.
int lump = fileSystem.CheckNumForFullName(name);
if (lump < 0)
lump = fileSystem.CheckNumForFullName("textures/" + name + ".png");
if (lump < 0)
lump = fileSystem.CheckNumForFullName("flats/" + name + ".png");
if (lump < 0)
{
// Not found - should we mark it as invalid or use a dummy texture?
@ -16,8 +24,6 @@ FGameTexture::FGameTexture(FString name) : Name(name)
}
else
{
// To do: figure out what type of data we got here instead of assuming it is a png.
FileData filedata = fileSystem.ReadFile(lump);
int result = decodePNG(Pixels, Width, Height, (const unsigned char*)filedata.GetMem(), fileSystem.FileLength(lump), true);
if (result == 0)

View file

@ -81,17 +81,26 @@ public:
bool isValid() const { return Valid; }
float GetDisplayWidth() const { return DisplayWidth; }
float GetDisplayHeight() const { return DisplayHeight; }
float GetScaleX() const { return ScaleX; }
float GetScaleY() const { return ScaleY; }
const void* GetImagePixels() const { return Pixels.data(); }
int GetImageWidth() const { return Width; }
int GetImageHeight() const { return Height; }
int GetTexelWidth() const { return GetImageWidth(); }
int GetTexelHeight() const { return GetImageHeight(); }
bool isHardwareCanvas() const { return false; }
bool useWorldPanning() const { return false; }
private:
FString Name;
bool Valid = true;
float DisplayWidth = 0.0f;
float DisplayHeight = 0.0f;
float ScaleX = 1.0f;
float ScaleY = 1.0f;
std::vector<unsigned char> Pixels;

View file

@ -105,6 +105,9 @@ struct IntSideDef
return TexMan.CheckForTexture(names[(int)part], ETextureType::Wall);
}
float GetTextureXOffset(WallPart part) { return 0.0f; }
float GetTextureXScale(WallPart part) { return 1.0f; }
float GetTextureYOffset(WallPart part) { return 0.0f; }
float GetTextureYScale(WallPart part) { return 1.0f; }
@ -115,6 +118,16 @@ struct IntSideDef
FVector2 V1(const FLevel& level) const;
FVector2 V2(const FLevel& level) const;
float GetTexelLength(const FLevel& level) const
{
FVector2 v1 = V1(level);
FVector2 v2 = V2(level);
double dx = v2.X - v1.X;
double dy = v2.Y - v1.Y;
int len = (int)(g_sqrt(dx * dx + dy * dy) + 0.5f);
return (float)len;
}
};
struct MapLineDef

View file

@ -316,7 +316,7 @@ void DoomLevelMesh::CreateLineHorizonSurface(FLevel& doomMap, IntSideDef* side)
verts[3].z = v2Top;
AddWallVertices(surf, verts);
SetSideTextureUVs(surf, side, WallPart::TOP, v1Top, v1Bottom, v2Top, v2Bottom);
SetSideTextureUVs(doomMap, surf, side, WallPart::TOP, v1Top, v1Bottom, v2Top, v2Bottom);
Surfaces.Push(surf);
}
@ -353,7 +353,7 @@ void DoomLevelMesh::CreateFrontWallSurface(FLevel& doomMap, IntSideDef* side)
surf.Texture = side->GetTexture(WallPart::MIDDLE);
AddWallVertices(surf, verts);
SetSideTextureUVs(surf, side, WallPart::TOP, v1Top, v1Bottom, v2Top, v2Bottom);
SetSideTextureUVs(doomMap, surf, side, WallPart::TOP, v1Top, v1Bottom, v2Top, v2Bottom);
AddSurfaceToTile(surf, doomMap, side->GetSampleDistance(WallPart::MIDDLE));
Surfaces.Push(surf);
@ -433,7 +433,7 @@ void DoomLevelMesh::CreateMidWallSurface(FLevel& doomMap, IntSideDef* side)
surf.Plane.W = -surf.Plane.W;
}
SetSideTextureUVs(surf, side, WallPart::TOP, verts[2].z, verts[0].z, verts[3].z, verts[1].z);
SetSideTextureUVs(doomMap, surf, side, WallPart::TOP, verts[2].z, verts[0].z, verts[3].z, verts[1].z);
AddSurfaceToTile(surf, doomMap, side->GetSampleDistance(WallPart::MIDDLE));
Surfaces.Push(surf);
@ -495,7 +495,7 @@ void DoomLevelMesh::Create3DFloorWallSurfaces(FLevel& doomMap, IntSideDef* side)
surf.Texture = xfloor->Line->sidedef[0]->GetTexture(WallPart::MIDDLE);
AddWallVertices(surf, verts);
SetSideTextureUVs(surf, side, WallPart::TOP, v1Top, v1Bottom, v2Top, v2Bottom);
SetSideTextureUVs(doomMap, surf, side, WallPart::TOP, v1Top, v1Bottom, v2Top, v2Bottom);
AddSurfaceToTile(surf, doomMap, side->GetSampleDistance(WallPart::MIDDLE));
Surfaces.Push(surf);
@ -539,7 +539,7 @@ void DoomLevelMesh::CreateTopWallSurface(FLevel& doomMap, IntSideDef* side)
surf.Texture = side->GetTexture(WallPart::TOP);
AddWallVertices(surf, verts);
SetSideTextureUVs(surf, side, WallPart::TOP, v1Top, v1TopBack, v2Top, v2TopBack);
SetSideTextureUVs(doomMap, surf, side, WallPart::TOP, v1Top, v1TopBack, v2Top, v2TopBack);
AddSurfaceToTile(surf, doomMap, side->GetSampleDistance(WallPart::TOP));
Surfaces.Push(surf);
@ -581,17 +581,97 @@ void DoomLevelMesh::CreateBottomWallSurface(FLevel& doomMap, IntSideDef* side)
surf.Texture = side->GetTexture(WallPart::BOTTOM);
AddWallVertices(surf, verts);
SetSideTextureUVs(surf, side, WallPart::BOTTOM, v1BottomBack, v1Bottom, v2BottomBack, v2Bottom);
SetSideTextureUVs(doomMap, surf, side, WallPart::BOTTOM, v1BottomBack, v1Bottom, v2BottomBack, v2Bottom);
AddSurfaceToTile(surf, doomMap, side->GetSampleDistance(WallPart::BOTTOM));
Surfaces.Push(surf);
}
void DoomLevelMesh::SetSideTextureUVs(DoomLevelMeshSurface& surface, IntSideDef* side, WallPart texpart, float v1TopZ, float v1BottomZ, float v2TopZ, float v2BottomZ)
struct FTexCoordInfo
{
int mRenderWidth;
int mRenderHeight;
int mWidth;
FVector2 mScale;
FVector2 mTempScale;
bool mWorldPanning;
float FloatToTexU(float v) const { return v / mRenderWidth; }
float FloatToTexV(float v) const { return v / mRenderHeight; }
float RowOffset(float rowoffset) const
{
float scale = fabsf(mScale.Y);
if (scale == 1.f || mWorldPanning) return rowoffset;
else return rowoffset / scale;
}
float TextureOffset(float textureoffset) const
{
float scale = fabsf(mScale.X);
if (scale == 1.f || mWorldPanning) return textureoffset;
else return textureoffset / scale;
}
float TextureAdjustWidth() const
{
if (mWorldPanning)
{
float tscale = fabsf(mTempScale.X);
if (tscale == 1.f) return (float)mRenderWidth;
else return mWidth / fabsf(tscale);
}
else return (float)mWidth;
}
void GetFromTexture(FGameTexture* tex, float x, float y, bool forceworldpanning)
{
if (x == 1.f)
{
mRenderWidth = xs_RoundToInt(tex->GetDisplayWidth());
mScale.X = tex->GetScaleX();
mTempScale.X = 1.f;
}
else
{
float scale_x = x * tex->GetScaleX();
mRenderWidth = xs_CeilToInt(tex->GetTexelWidth() / scale_x);
mScale.X = scale_x;
mTempScale.X = x;
}
if (y == 1.f)
{
mRenderHeight = xs_RoundToInt(tex->GetDisplayHeight());
mScale.Y = tex->GetScaleY();
mTempScale.Y = 1.f;
}
else
{
float scale_y = y * tex->GetScaleY();
mRenderHeight = xs_CeilToInt(tex->GetTexelHeight() / scale_y);
mScale.Y = scale_y;
mTempScale.Y = y;
}
if (tex->isHardwareCanvas())
{
mScale.Y = -mScale.Y;
mRenderHeight = -mRenderHeight;
}
mWorldPanning = tex->useWorldPanning() || forceworldpanning;
mWidth = tex->GetTexelWidth();
}
};
static void GetTexCoordInfo(FGameTexture* tex, FTexCoordInfo* tci, IntSideDef* side, WallPart texpos)
{
tci->GetFromTexture(tex, (float)side->GetTextureXScale(texpos), (float)side->GetTextureYScale(texpos), false/*!!(side->GetLevel()->flags3 & LEVEL3_FORCEWORLDPANNING)*/);
}
void DoomLevelMesh::SetSideTextureUVs(FLevel& doomMap, DoomLevelMeshSurface& surface, IntSideDef* side, WallPart texpart, float v1TopZ, float v1BottomZ, float v2TopZ, float v2BottomZ)
{
FFlatVertex* verts = &Mesh.Vertices[surface.MeshLocation.StartVertIndex];
#if 0
if (surface.Texture.isValid())
{
const auto gtxt = TexMan.GetGameTexture(surface.Texture);
@ -600,7 +680,7 @@ void DoomLevelMesh::SetSideTextureUVs(DoomLevelMeshSurface& surface, IntSideDef*
GetTexCoordInfo(gtxt, &tci, side, texpart);
float startU = tci.FloatToTexU(tci.TextureOffset((float)side->GetTextureXOffset(texpart)) + tci.TextureOffset((float)side->GetTextureXOffset(texpart)));
float endU = startU + tci.FloatToTexU(side->TexelLength);
float endU = startU + tci.FloatToTexU(side->GetTexelLength(doomMap));
verts[0].u = startU;
verts[1].u = endU;
@ -608,8 +688,8 @@ void DoomLevelMesh::SetSideTextureUVs(DoomLevelMeshSurface& surface, IntSideDef*
verts[3].u = endU;
// To do: the ceiling version is apparently used in some situation related to 3d floors (rover->top.isceiling)
//float offset = tci.RowOffset((float)side->GetTextureYOffset(texpart)) + tci.RowOffset((float)side->GetTextureYOffset(texpart)) + (float)side->sector->GetPlaneTexZ(PLANE_CEILING);
float offset = tci.RowOffset((float)side->GetTextureYOffset(texpart)) + tci.RowOffset((float)side->GetTextureYOffset(texpart)) + (float)side->sector->GetPlaneTexZ(PLANE_FLOOR);
//float offset = tci.RowOffset((float)side->GetTextureYOffset(texpart)) + tci.RowOffset((float)side->GetTextureYOffset(texpart)) + (float)doomMap.Sectors[side->sector].ceilingTexZ;
float offset = tci.RowOffset((float)side->GetTextureYOffset(texpart)) + tci.RowOffset((float)side->GetTextureYOffset(texpart)) + (float)doomMap.Sectors[side->sector].floorTexZ;
verts[0].v = tci.FloatToTexV(offset - v1BottomZ);
verts[1].v = tci.FloatToTexV(offset - v2BottomZ);
@ -617,7 +697,6 @@ void DoomLevelMesh::SetSideTextureUVs(DoomLevelMeshSurface& surface, IntSideDef*
verts[3].v = tci.FloatToTexV(offset - v2TopZ);
}
else
#endif
{
for (int i = 0; i < 4; i++)
{

View file

@ -83,7 +83,7 @@ private:
void CreateTopWallSurface(FLevel& doomMap, IntSideDef* side);
void CreateBottomWallSurface(FLevel& doomMap, IntSideDef* side);
void AddWallVertices(DoomLevelMeshSurface& surf, FFlatVertex* verts);
void SetSideTextureUVs(DoomLevelMeshSurface& surface, IntSideDef* side, WallPart texpart, float v1TopZ, float v1BottomZ, float v2TopZ, float v2BottomZ);
void SetSideTextureUVs(FLevel& doomMap, DoomLevelMeshSurface& surface, IntSideDef* side, WallPart texpart, float v1TopZ, float v1BottomZ, float v2TopZ, float v2BottomZ);
void CreateFloorSurface(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, X3DFloor* controlSector, int typeIndex);
void CreateCeilingSurface(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, X3DFloor* controlSector, int typeIndex);

View file

@ -102,10 +102,17 @@ int VulkanRenderDevice::GetBindlessTextureIndex(FTextureID textureID)
if (textureIndex != 0)
return textureIndex;
// To do: upload image
VulkanImageView* view = GetTextureManager()->GetNullTextureView();
VulkanImageView* view;
VulkanSampler* sampler = GetSamplerManager()->Get();
if (tex->GetImageWidth() > 0 && tex->GetImageHeight() > 0)
{
int index = GetTextureManager()->CreateGameTexture(tex->GetImageWidth(), tex->GetImageHeight(), tex->GetImagePixels());
view = GetTextureManager()->GetGameTextureView(index);
}
else
{
view = GetTextureManager()->GetNullTextureView();
}
textureIndex = GetDescriptorSetManager()->AddBindlessTextureIndex(view, sampler);
return textureIndex;
@ -477,6 +484,56 @@ void VkTextureManager::CreateNullTexture()
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
int VkTextureManager::CreateGameTexture(int width, int height, const void* pixels)
{
int index = (int)GameTextures.size();
auto texture = ImageBuilder()
.Format(VK_FORMAT_R8G8B8A8_UNORM)
.Size(width, height)
.Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT)
.DebugName("VkTextureManager.GameTexture")
.Create(fb->GetDevice());
auto textureView = ImageViewBuilder()
.Image(texture.get(), VK_FORMAT_R8G8B8A8_UNORM)
.DebugName("VkTextureManager.GameTextureView")
.Create(fb->GetDevice());
auto stagingBuffer = BufferBuilder()
.Size(width * height * sizeof(uint32_t))
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.DebugName("VkTextureManager.GameTextureStaging")
.Create(fb->GetDevice());
uint32_t* data = (uint32_t*)stagingBuffer->Map(0, width * height * sizeof(uint32_t));
memcpy(data, pixels, width * height * sizeof(uint32_t));
stagingBuffer->Unmap();
PipelineBarrier()
.AddImage(texture.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VkBufferImageCopy region = {};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageExtent.depth = 1;
region.imageExtent.width = width;
region.imageExtent.height = height;
fb->GetCommands()->GetTransferCommands()->copyBufferToImage(stagingBuffer->buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
fb->GetCommands()->TransferDeleteList->Add(std::move(stagingBuffer));
PipelineBarrier()
.AddImage(texture.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
GameTextures.push_back(std::move(texture));
GameTextureViews.push_back(std::move(textureView));
return index;
}
void VkTextureManager::CreateLightmap(int newLMTextureSize, int newLMTextureCount)
{
if (LMTextureSize == newLMTextureSize && LMTextureCount == newLMTextureCount + 1)
@ -548,7 +605,7 @@ VkSamplerManager::VkSamplerManager(VulkanRenderDevice* fb) : fb(fb)
Sampler = SamplerBuilder()
.MagFilter(VK_FILTER_NEAREST)
.MinFilter(VK_FILTER_NEAREST)
.AddressMode(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_REPEAT)
.AddressMode(VK_SAMPLER_ADDRESS_MODE_REPEAT/*VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE*/, VK_SAMPLER_ADDRESS_MODE_REPEAT/*VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE*/, VK_SAMPLER_ADDRESS_MODE_REPEAT)
.MipmapMode(VK_SAMPLER_MIPMAP_MODE_NEAREST)
.MaxLod(0.25f)
.DebugName("VkSamplerManager.Sampler")

View file

@ -166,6 +166,11 @@ public:
void CreateLightmap(int newLMTextureSize, int newLMTextureCount);
void DownloadLightmap(int arrayIndex, uint16_t* buffer);
int CreateGameTexture(int width, int height, const void* pixels);
VulkanImage* GetGameTexture(int index) { return GameTextures[index].get(); }
VulkanImageView* GetGameTextureView(int index) { return GameTextureViews[index].get(); }
VulkanImage* GetNullTexture() { return NullTexture.get(); }
VulkanImageView* GetNullTextureView() { return NullTextureView.get(); }
@ -180,6 +185,9 @@ private:
std::unique_ptr<VulkanImage> NullTexture;
std::unique_ptr<VulkanImageView> NullTextureView;
std::vector<std::unique_ptr<VulkanImage>> GameTextures;
std::vector<std::unique_ptr<VulkanImageView>> GameTextureViews;
};
class VkSamplerManager

View file

@ -56,6 +56,7 @@
#include <thread>
#include "framework/zdray.h"
#include "framework/filesystem.h"
#include "wad/wad.h"
#include "level/level.h"
#include "commandline/getopt.h"
@ -233,6 +234,8 @@ int main(int argc, char **argv)
fixSame = true;
}
// fileSystem.AddFolderSource("C:\\Development\\VkDoom\\build\\RelWithDebInfo\\ElderJamReignited");
{
FWadReader inwad(InName);
FWadWriter outwad(OutName, inwad.IsIWAD());