diff --git a/src/framework/filesystem.cpp b/src/framework/filesystem.cpp index 8994761..570ddf1 100644 --- a/src/framework/filesystem.cpp +++ b/src/framework/filesystem.cpp @@ -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 Filenames; - std::map FilenameKeyToIndex; + std::map FullnameKeyToIndex; }; ///////////////////////////////////////////////////////////////////////////// diff --git a/src/framework/textureid.cpp b/src/framework/textureid.cpp index 1bf1763..df5ace0 100644 --- a/src/framework/textureid.cpp +++ b/src/framework/textureid.cpp @@ -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) diff --git a/src/framework/textureid.h b/src/framework/textureid.h index 77c0df3..f6eb120 100644 --- a/src/framework/textureid.h +++ b/src/framework/textureid.h @@ -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 Pixels; diff --git a/src/level/doomdata.h b/src/level/doomdata.h index 8acbbb5..1f4f2a5 100644 --- a/src/level/doomdata.h +++ b/src/level/doomdata.h @@ -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 diff --git a/src/lightmapper/doom_levelmesh.cpp b/src/lightmapper/doom_levelmesh.cpp index 35b56a3..56adeda 100644 --- a/src/lightmapper/doom_levelmesh.cpp +++ b/src/lightmapper/doom_levelmesh.cpp @@ -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++) { diff --git a/src/lightmapper/doom_levelmesh.h b/src/lightmapper/doom_levelmesh.h index acac184..82f95c4 100644 --- a/src/lightmapper/doom_levelmesh.h +++ b/src/lightmapper/doom_levelmesh.h @@ -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); diff --git a/src/lightmapper/vk_renderdevice.cpp b/src/lightmapper/vk_renderdevice.cpp index 913f824..72cb063 100644 --- a/src/lightmapper/vk_renderdevice.cpp +++ b/src/lightmapper/vk_renderdevice.cpp @@ -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, ®ion); + + 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") diff --git a/src/lightmapper/vk_renderdevice.h b/src/lightmapper/vk_renderdevice.h index 030514c..b9d6482 100644 --- a/src/lightmapper/vk_renderdevice.h +++ b/src/lightmapper/vk_renderdevice.h @@ -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 NullTexture; std::unique_ptr NullTextureView; + + std::vector> GameTextures; + std::vector> GameTextureViews; }; class VkSamplerManager diff --git a/src/main.cpp b/src/main.cpp index 0fdfb5f..84f8632 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -56,6 +56,7 @@ #include #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());