From ffa9d7e607854b8677971a813e46fc00ae969848 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Wed, 2 Mar 2016 18:06:20 +0100 Subject: [PATCH] WIP --- assets | 2 +- include/Engine/Core/ResourceManager.h | 1 + include/Engine/Rendering/DDS.h | 71 ++++++++++++ include/Engine/Rendering/Image.h | 5 + include/Engine/Rendering/Texture.h | 3 +- resources/Schema/Entities/DDS.xml | 19 ++++ resources/Schema/Entities/MovementTest.xml | 23 ++++ src/Engine/Core/Util/Logging.cpp | 4 +- src/Engine/Rendering/CubeMapPass.cpp | 6 +- src/Engine/Rendering/DDS.cpp | 121 +++++++++++++++++++++ src/Engine/Rendering/PNG.cpp | 4 +- src/Engine/Rendering/RawModelCustom.cpp | 2 +- src/Engine/Rendering/Texture.cpp | 61 +++++++++-- src/Game/Game.cpp | 3 +- 14 files changed, 306 insertions(+), 19 deletions(-) create mode 100644 include/Engine/Rendering/DDS.h create mode 100644 resources/Schema/Entities/DDS.xml create mode 100644 src/Engine/Rendering/DDS.cpp diff --git a/assets b/assets index 10a61165..e8d98c2d 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 10a611659ddaadfea6a560e707d395834855a979 +Subproject commit e8d98c2daeb51c7d84d1407593e70e439cd91638 diff --git a/include/Engine/Core/ResourceManager.h b/include/Engine/Core/ResourceManager.h index b994b5cf..00202819 100644 --- a/include/Engine/Core/ResourceManager.h +++ b/include/Engine/Core/ResourceManager.h @@ -19,6 +19,7 @@ class Resource protected: Resource() { } + virtual ~Resource() { } public: //Should be thrown in a Resource's constructor if it cannot complete because another resource is still loading. diff --git a/include/Engine/Rendering/DDS.h b/include/Engine/Rendering/DDS.h new file mode 100644 index 00000000..18e06f78 --- /dev/null +++ b/include/Engine/Rendering/DDS.h @@ -0,0 +1,71 @@ +#ifndef DDS_h__ +#define DDS_h__ + +#include + +#include "../Common.h" +#include "../Core/ResourceManager.h" +#include "Image.h" + +// DXT5 +class DDS : public Image, public Resource +{ +public: + DDS(std::string path); + ~DDS(); + + void FlipY(unsigned char* data, std::size_t rowBytes, std::size_t totalSize, std::size_t numBlocksWide); + +private: + static const uint32_t MagicNumber = 0x20534444; // "DDS " + + struct Header_t + { + uint32_t Size; + uint32_t Flags; + uint32_t Height; + uint32_t Width; + uint32_t PitchOrLinearSize; + uint32_t Depth; + uint32_t MipMapCount; + uint32_t RESERVED1[11]; + struct PixelFormat_t + { + uint32_t Size; + uint32_t Flags; + uint32_t FourCC; + uint32_t RGBBitCount; + uint32_t RBitMask; + uint32_t GBitMask; + uint32_t BBitMask; + uint32_t ABitMask; + } PixelFormat; + uint32_t Caps; + uint32_t Caps2; + uint32_t Caps3; + uint32_t Caps4; + uint32_t RESERVED2; + } m_Header; + + struct Block_t + { + struct Alpha_t + { + uint8_t Colors[2]; + uint8_t Data[6]; + } Alpha; + struct Data_t + { + uint16_t Colors[2]; + uint8_t Data[4]; + } Data; + }; + + std::size_t m_NumBlocksWide; + std::size_t m_NumBlocksHigh; + std::size_t m_RowBytes; + + void flipBlock(Block_t* block); +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/Image.h b/include/Engine/Rendering/Image.h index 19ef43c1..74e3a9e7 100644 --- a/include/Engine/Rendering/Image.h +++ b/include/Engine/Rendering/Image.h @@ -3,6 +3,8 @@ struct Image { + virtual ~Image() { } + enum class ImageFormat { Unknown, @@ -13,7 +15,10 @@ struct Image unsigned int Width = 0; unsigned int Height = 0; ImageFormat Format = ImageFormat::Unknown; + unsigned int MipMapLevels = 0; + bool Compressed = false; unsigned char* Data = nullptr; + std::size_t ByteSize = 0; }; #endif diff --git a/include/Engine/Rendering/Texture.h b/include/Engine/Rendering/Texture.h index d159e636..95acaf65 100644 --- a/include/Engine/Rendering/Texture.h +++ b/include/Engine/Rendering/Texture.h @@ -1,9 +1,11 @@ #ifndef Texture_h__ #define Texture_h__ +#include #include "../OpenGL.h" #include "BaseTexture.h" #include "PNG.h" +#include "DDS.h" class Texture : public BaseTexture { @@ -18,7 +20,6 @@ public: void Bind(GLenum textureUnit = GL_TEXTURE0); GLuint m_Texture = 0; - unsigned char* Data = nullptr; }; diff --git a/resources/Schema/Entities/DDS.xml b/resources/Schema/Entities/DDS.xml new file mode 100644 index 00000000..e7f8f210 --- /dev/null +++ b/resources/Schema/Entities/DDS.xml @@ -0,0 +1,19 @@ + + + + + + + + + Textures/Core/UnitRaptor.png + + + + + + + + + + diff --git a/resources/Schema/Entities/MovementTest.xml b/resources/Schema/Entities/MovementTest.xml index 41474568..d7bf28a6 100644 --- a/resources/Schema/Entities/MovementTest.xml +++ b/resources/Schema/Entities/MovementTest.xml @@ -118,6 +118,29 @@ + + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + + + + + + + + + + + Textures/Test/DXT5_noMipmaps.dds + + + + + + + + diff --git a/src/Engine/Core/Util/Logging.cpp b/src/Engine/Core/Util/Logging.cpp index c7612fd8..63a6f380 100644 --- a/src/Engine/Core/Util/Logging.cpp +++ b/src/Engine/Core/Util/Logging.cpp @@ -33,8 +33,8 @@ void _LOG(_LOG_LEVEL logLevel, const char* file, const char* func, unsigned int va_end(args); if (logLevel == LOG_LEVEL_ERROR) { - //std::cerr << file << ":" << line << " " << func << std::endl; - //std::cerr << _LOG_LEVEL_PREFIX[logLevel] << message << std::endl; + std::cerr << file << ":" << line << " " << func << std::endl; + std::cerr << _LOG_LEVEL_PREFIX[logLevel] << message << std::endl; } else { std::cout << _LOG_LEVEL_PREFIX[logLevel] << message << std::endl; } diff --git a/src/Engine/Rendering/CubeMapPass.cpp b/src/Engine/Rendering/CubeMapPass.cpp index e2a55b74..cabeffdb 100644 --- a/src/Engine/Rendering/CubeMapPass.cpp +++ b/src/Engine/Rendering/CubeMapPass.cpp @@ -13,8 +13,8 @@ void CubeMapPass::LoadTextures(std::string input) for (int i = 0; i < 6; i++) { std::string str; str = "Textures/Test/CubeMap/" + input + "/CubeMapTest0" + std::to_string(i) + ".png"; - Texture* img = ResourceManager::Load(str); - m_CubeMapTextures.push_back(img); + //Texture* img = ResourceManager::Load(str); + //m_CubeMapTextures.push_back(img); } GenerateCubeMapTexture(); m_PreviusCubeMapTexture = input; @@ -29,7 +29,7 @@ void CubeMapPass::GenerateCubeMapTexture() glBindTexture(GL_TEXTURE_CUBE_MAP, m_CubeMapTexture); for (int i = 0; i < 6; i++) { - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA32F, m_CubeMapTextures[0]->Width, m_CubeMapTextures[0]->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_CubeMapTextures[i]->Data); + //glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA32F, m_CubeMapTextures[0]->Width, m_CubeMapTextures[0]->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_CubeMapTextures[i]->Data); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); diff --git a/src/Engine/Rendering/DDS.cpp b/src/Engine/Rendering/DDS.cpp new file mode 100644 index 00000000..8348c9a9 --- /dev/null +++ b/src/Engine/Rendering/DDS.cpp @@ -0,0 +1,121 @@ +#include "Rendering/DDS.h" + +DDS::DDS(std::string path) +{ + std::ifstream in(path.c_str(), std::ios_base::binary | std::ios_base::ate); + if (!in.is_open()) { + throw Resource::FailedLoadingException("Failed to open file."); + } + unsigned int fileSize = static_cast(in.tellg()); + in.seekg(0, std::ios_base::beg); + + if (fileSize < sizeof(MagicNumber) + sizeof(DDS::Header_t)) { + throw Resource::FailedLoadingException("Invalid DDS file; not even big enough to contain the DDS header!"); + } + + // Read magic number + uint32_t magicNumber; + in.read(reinterpret_cast(&magicNumber), sizeof(MagicNumber)); + if (magicNumber != DDS::MagicNumber) { + throw Resource::FailedLoadingException("Invalid DDS file; magic number doesn't match."); + } + + // Read header + in.read(reinterpret_cast(&m_Header), sizeof(DDS::Header_t)); + + // Validate header + if (m_Header.Size != sizeof(DDS::Header_t) || m_Header.PixelFormat.Size != sizeof(DDS::Header_t::PixelFormat_t)) { + throw Resource::FailedLoadingException("Corrupt DDS file; header size doesn't match!"); + } + + // Make sure it's DC3 (DXT5) + char format[5]; + memcpy(&format, &m_Header.PixelFormat.FourCC, 4); + format[4] = '\0'; + if (strcmp(format, "DXT5")) { + std::stringstream ss; + ss << "Invalid DDS file; Unsupported compression format \"" << format << "\""; + throw Resource::FailedLoadingException(ss.str().c_str()); + } + + this->Width = m_Header.Width; + this->Height = m_Header.Height; + this->Format = Image::ImageFormat::RGBA; + this->Compressed = true; + if (m_Header.MipMapCount != 0) { + this->MipMapLevels = m_Header.MipMapCount - 1; + } + + // Read data + std::size_t dataSize = fileSize - sizeof(MagicNumber) - sizeof(DDS::Header_t); + this->Data = new unsigned char[dataSize]; + this->ByteSize = dataSize; + in.read(reinterpret_cast(this->Data), dataSize); + + // Flip! + unsigned int w = this->Width; + unsigned int h = this->Height; + unsigned char* mipMapData = this->Data; + for (unsigned int i = 0; i < this->MipMapLevels; ++i) { + std::size_t bpe = 16; + std::size_t numBlocksWide = std::max(1, (w + 3) / 4); + std::size_t numBlocksHigh = std::max(1, (h + 3) / 4); + std::size_t rowBytes = numBlocksWide * bpe; + std::size_t numRows = numBlocksHigh; + std::size_t numBytes = rowBytes * numBlocksHigh; + FlipY(mipMapData, rowBytes, numBytes, numBlocksWide); + w = w >> 1; + h = h >> 1; + mipMapData += numBytes; + } +} + +DDS::~DDS() +{ + if (this->Data != nullptr) { + delete[] this->Data; + this->Data = nullptr; + } +} + +void DDS::FlipY(unsigned char* data, std::size_t rowBytes, std::size_t totalSize, std::size_t numBlocksWide) +{ + std::size_t height = totalSize / rowBytes; + unsigned char* topLine = data; + unsigned char* bottomLine = data + (height - 1) * rowBytes; + for (std::size_t line = 0; line < height / 2; ++line) { + Block_t* topBlocks = reinterpret_cast(topLine); + Block_t* bottomBlocks = reinterpret_cast(bottomLine); + for (std::size_t block = 0; block < numBlocksWide; ++block) { + flipBlock(&topBlocks[block]); + flipBlock(&bottomBlocks[block]); + std::swap(topBlocks[block], bottomBlocks[block]); + } + topLine += rowBytes; + bottomLine -= rowBytes; + } +} + +void DDS::flipBlock(Block_t* block) +{ + // Extract both rows of pixel Data into a 32 bit sized values. + uint32_t pixelRow0 = block->Alpha.Data[0] + ((block->Alpha.Data[1] + (block->Alpha.Data[2] << 8)) << 8); + uint32_t pixelRow1 = block->Alpha.Data[3] + ((block->Alpha.Data[4] + (block->Alpha.Data[5] << 8)) << 8); + + // Swap the extracted row Data. These values will be read from and shifted into the final block locations. + uint32_t pixelRow0Swapped = ((pixelRow0 & 0x000fff) << 12) | ((pixelRow0 & 0xfff000) >> 12); + uint32_t pixelRow1Swapped = ((pixelRow1 & 0x000fff) << 12) | ((pixelRow1 & 0xfff000) >> 12); + + // Swapped Data from row 1 is written + block->Alpha.Data[0] = static_cast(pixelRow1Swapped & 0x0000ff); + block->Alpha.Data[1] = static_cast((pixelRow1Swapped & 0x00ff00) >> 8); + block->Alpha.Data[2] = static_cast((pixelRow1Swapped & 0xff0000) >> 16); + + // Swapped Data from row 0 is written + block->Alpha.Data[3] = static_cast(pixelRow0Swapped & 0x0000ff); + block->Alpha.Data[4] = static_cast((pixelRow0Swapped & 0x00ff00) >> 8); + block->Alpha.Data[5] = static_cast((pixelRow0Swapped & 0xff0000) >> 16); + + std::swap(block->Data.Data[0], block->Data.Data[3]); + std::swap(block->Data.Data[1], block->Data.Data[2]); +} diff --git a/src/Engine/Rendering/PNG.cpp b/src/Engine/Rendering/PNG.cpp index 409da9b1..8febc160 100644 --- a/src/Engine/Rendering/PNG.cpp +++ b/src/Engine/Rendering/PNG.cpp @@ -96,10 +96,10 @@ PNG::~PNG() void PNG::pngErrorFunction(png_structp png_ptr, png_const_charp error_msg) { - LOG_WARNING("%s", error_msg); + LOG_WARNING("libpng error: %s", error_msg); } void PNG::pngWarningFunction(png_structp png_ptr, png_const_charp warning_msg) { - LOG_WARNING("%s", warning_msg); + LOG_WARNING("libpng warning: %s", warning_msg); } diff --git a/src/Engine/Rendering/RawModelCustom.cpp b/src/Engine/Rendering/RawModelCustom.cpp index dd6a96de..f71f574d 100644 --- a/src/Engine/Rendering/RawModelCustom.cpp +++ b/src/Engine/Rendering/RawModelCustom.cpp @@ -271,7 +271,7 @@ void RawModelCustom::ReadMaterialTextureProperties(RawModelCustom::TextureProper } texture.TexturePath = "Textures/"; texture.TexturePath += (fileData + offset); - texture.TexturePath += ".png"; + texture.TexturePath += ".dds"; offset += nameLength; if (offset + sizeof(glm::vec2) > fileByteSize) { throw Resource::FailedLoadingException("Reading Material texture UVTiling failed"); diff --git a/src/Engine/Rendering/Texture.cpp b/src/Engine/Rendering/Texture.cpp index 03347044..d184036f 100644 --- a/src/Engine/Rendering/Texture.cpp +++ b/src/Engine/Rendering/Texture.cpp @@ -2,7 +2,15 @@ Texture::Texture(std::string path) { - PNG* img = ResourceManager::Load(path); //TODO: Make this threaded. Catch exeptions in all other load places. + std::string ext = boost::filesystem::path(path).extension().string(); + Image* img; + if (ext == ".png") { + img = ResourceManager::Load(path); + } else if (ext == ".dds") { + img = ResourceManager::Load(path); + } else { + throw Resource::FailedLoadingException("Texture extension is not .png nor .dds"); + } //PNG image(path); @@ -18,7 +26,6 @@ Texture::Texture(std::string path) this->Width = img->Width; this->Height = img->Height; - this->Data = img->Data; GLint format; switch (img->Format) { @@ -29,19 +36,57 @@ Texture::Texture(std::string path) format = GL_RGBA; break; } - // Construct the OpenGL texture glGenTextures(1, &m_Texture); glBindTexture(GL_TEXTURE_2D, m_Texture); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0, format, img->Width, img->Height, 0, format, GL_UNSIGNED_BYTE, img->Data); - glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - GLERROR("Texture load"); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + if (!img->Compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + } + unsigned int w = img->Width; + unsigned int h = img->Width; + unsigned char* data = img->Data; + for (int mipLevel = 0; mipLevel < 1 + img->MipMapLevels; mipLevel++) { + if (img->Compressed) { + //LOG_DEBUG("mipLevel: %i", mipLevel); + //LOG_DEBUG("w: %i", w); + //LOG_DEBUG("h: %i", h); + + std::size_t bpe = 16; + std::size_t numBlocksWide = std::max(1, (w + 3) / 4); + std::size_t numBlocksHigh = std::max(1, (h + 3) / 4); + std::size_t rowBytes = numBlocksWide * bpe; + std::size_t numRows = numBlocksHigh; + std::size_t numBytes = rowBytes * numBlocksHigh; + //LOG_DEBUG("numBytes: %i", numBytes); + glCompressedTexImage2D(GL_TEXTURE_2D, mipLevel, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, w, h, 0, numBytes, data); + data += numBytes; + } else { + glTexImage2D(GL_TEXTURE_2D, mipLevel, format, img->Width, img->Height, 0, format, GL_UNSIGNED_BYTE, img->Data); + } + + w = w >> 1; + h = h >> 1; + } + //LOG_DEBUG("REMAINDER: %i", img->ByteSize - (data - img->Data)); + // Generate mipmaps if the image file doesn't contain them + if (img->MipMapLevels == 0) { + glGenerateMipmap(GL_TEXTURE_2D); + } + + if (ext == ".png") { + ResourceManager::Release("PNG", path); + } else if (ext == ".dds") { + ResourceManager::Release("DDS", path); + } + + if (GLERROR("Texture load")) { + throw Resource::FailedLoadingException("GL texture generation failed"); + } } Texture::~Texture() diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 2946d656..8ff42ebd 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -39,7 +39,8 @@ Game::Game(int argc, char* argv[]) ResourceManager::RegisterType("Model"); ResourceManager::RegisterType("RawModel"); ResourceManager::RegisterType("Texture"); - ResourceManager::RegisterType("Png"); + ResourceManager::RegisterType("PNG"); + ResourceManager::RegisterType("DDS"); ResourceManager::RegisterType("ShaderProgram"); ResourceManager::RegisterType("EntityFile"); ResourceManager::RegisterType("FontFile");