Browse Source

Added loading of ETC1 compressed textures on mobile devices. These need to be stored inside .ktx files.
Removed unneeded texture.

Lasse Öörni 13 years ago
parent
commit
d7d599be7b

BIN
Bin/Data/Textures/DiffuseMask.dds


+ 1 - 1
Docs/Reference.dox

@@ -542,7 +542,7 @@ These differences need to be observed when using the low-level rendering functio
 
 OpenGL ES 2.0 has further limitations:
 
-- Only DXT1 compressed textures will be uploaded as compressed if the EXT_texture_compression_dxt1 extension is present. Other compressed texture formats will be uploaded as uncompressed RGBA. Mobile hardware specific compression formats such as ETC or PVRTC are not yet supported.
+- Of the DXT formats, only DXT1 compressed textures will be uploaded as compressed, and only if the EXT_texture_compression_dxt1 extension is present. Other DXT formats will be uploaded as uncompressed RGBA. ETC1 compressed textures are supported through the .ktx file extension.
 
 - %Light pre-pass and deferred rendering are not supported due to missing multiple rendertarget support, and limited rendertarget formats.
 

+ 0 - 1
Docs/ScriptAPI.dox

@@ -2326,7 +2326,6 @@ Properties:<br>
 - bool hardwareDepthSupport (readonly)
 - bool hardwareShadowSupport (readonly)
 - bool hiresShadowSupport (readonly)
-- bool compressedTextureSupport (readonly)
 - bool forceSM2
 - IntVector2[]@ resolutions (readonly)
 - int[]@ multiSampleLevels (readonly)

+ 0 - 1
Engine/Engine/GraphicsAPI.cpp

@@ -788,7 +788,6 @@ static void RegisterGraphics(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Graphics", "bool get_hardwareDepthSupport() const", asMETHOD(Graphics, GetHardwareDepthSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_hardwareShadowSupport() const", asMETHOD(Graphics, GetHardwareShadowSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_hiresShadowSupport() const", asMETHOD(Graphics, GetHiresShadowSupport), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Graphics", "bool get_compressedTextureSupport() const", asMETHOD(Graphics, GetCompressedTextureSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "void set_forceSM2(bool)", asMETHOD(Graphics, SetForceSM2), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_forceSM2() const", asMETHOD(Graphics, GetForceSM2), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "Array<IntVector2>@ get_resolutions() const", asFUNCTION(GraphicsGetResolutions), asCALL_CDECL_OBJLAST);

+ 1 - 0
Engine/Engine/ResourceAPI.cpp

@@ -23,6 +23,7 @@
 
 #include "Precompiled.h"
 #include "APITemplates.h"
+#include "Image.h"
 #include "PackageFile.h"
 #include "ResourceCache.h"
 

+ 17 - 0
Engine/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -1712,6 +1712,23 @@ PODVector<int> Graphics::GetMultiSampleLevels() const
     return ret;
 }
 
+unsigned Graphics::GetFormat(CompressedFormat format) const
+{
+    switch (format)
+    {
+    case CF_DXT1:
+        return D3DFMT_DXT1;
+        
+    case CF_DXT3:
+        return D3DFMT_DXT3;
+        
+    case CF_DXT5:
+        return D3DFMT_DXT5;
+    }
+    
+    return 0;
+}
+
 VertexBuffer* Graphics::GetVertexBuffer(unsigned index) const
 {
     return index < MAX_VERTEX_STREAMS ? vertexBuffers_[index] : 0;

+ 3 - 2
Engine/Graphics/Direct3D9/D3D9Graphics.h

@@ -26,6 +26,7 @@
 #include "ArrayPtr.h"
 #include "Color.h"
 #include "HashMap.h"
+#include "Image.h"
 #include "Object.h"
 #include "Rect.h"
 #include "GraphicsDefs.h"
@@ -236,12 +237,12 @@ public:
     bool GetHiresShadowSupport() const { return hiresShadowSupport_; }
     /// Return whether stream offset is supported.
     bool GetStreamOffsetSupport() const { return streamOffsetSupport_; }
-    /// Return whether DXT texture compression is supported. Always true on Direct3D9.
-    bool GetCompressedTextureSupport() const { return true; }
     /// Return supported fullscreen resolutions.
     PODVector<IntVector2> GetResolutions() const;
     /// Return supported multisampling levels.
     PODVector<int> GetMultiSampleLevels() const;
+    /// Return hardware format for a compressed image format, or 0 if unsupported.
+    unsigned GetFormat(CompressedFormat format) const;
     /// Return vertex buffer by index.
     VertexBuffer* GetVertexBuffer(unsigned index) const;
     /// Return current index buffer.

+ 0 - 17
Engine/Graphics/Direct3D9/D3D9Texture.cpp

@@ -185,23 +185,6 @@ unsigned Texture::GetRowDataSize(int width) const
     }
 }
 
-unsigned Texture::GetDXTFormat(CompressedFormat format)
-{
-    switch (format)
-    {
-    case CF_DXT1:
-        return D3DFMT_DXT1;
-        
-    case CF_DXT3:
-        return D3DFMT_DXT3;
-        
-    case CF_DXT5:
-        return D3DFMT_DXT5;
-    }
-    
-    return 0;
-}
-
 void Texture::LoadParameters()
 {
     ResourceCache* cache = GetSubsystem<ResourceCache>();

+ 0 - 2
Engine/Graphics/Direct3D9/D3D9Texture.h

@@ -86,8 +86,6 @@ public:
     unsigned GetDataSize(int width, int height) const;
     /// Return data size in bytes for a pixel or block row.
     unsigned GetRowDataSize(int width) const;
-    /// Return API-specific DXT compressed texture format.
-    static unsigned GetDXTFormat(CompressedFormat format);
     
     /// Load parameters.
     void LoadParameters();

+ 1 - 1
Engine/Graphics/Direct3D9/D3D9Texture2D.cpp

@@ -334,7 +334,7 @@ bool Texture2D::Load(SharedPtr<Image> image, bool useAlpha)
         int width = image->GetWidth();
         int height = image->GetHeight();
         unsigned levels = image->GetNumCompressedLevels();
-        unsigned format = GetDXTFormat(image->GetCompressedFormat());
+        unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
         
         unsigned mipsToSkip = mipsToSkip_[quality];
         if (mipsToSkip >= levels)

+ 1 - 1
Engine/Graphics/Direct3D9/D3D9TextureCube.cpp

@@ -413,7 +413,7 @@ bool TextureCube::Load(CubeMapFace face, SharedPtr<Image> image, bool useAlpha)
         int width = image->GetWidth();
         int height = image->GetHeight();
         unsigned levels = image->GetNumCompressedLevels();
-        unsigned format = GetDXTFormat(image->GetCompressedFormat());
+        unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
         
         if (width != height)
         {

+ 28 - 3
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -141,7 +141,7 @@ Graphics::Graphics(Context* context_) :
     lightPrepassSupport_(false),
     deferredSupport_(false),
     hardwareDepthSupport_(false),
-    compressedTextureSupport_(false),
+    dxtTextureSupport_(false),
     numPrimitives_(0),
     numBatches_(0),
     defaultTextureFilterMode_(FILTER_BILINEAR),
@@ -300,9 +300,9 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool vsync, bool
             return false;
         }
         
-        compressedTextureSupport_ = CheckExtension("EXT_texture_compression_s3tc");
+        dxtTextureSupport_ = CheckExtension("EXT_texture_compression_s3tc");
         #else
-        compressedTextureSupport_ = CheckExtension("EXT_texture_compression_dxt1");
+        dxtTextureSupport_ = CheckExtension("EXT_texture_compression_dxt1");
         #endif
     }
     
@@ -1583,6 +1583,31 @@ PODVector<int> Graphics::GetMultiSampleLevels() const
     return ret;
 }
 
+unsigned Graphics::GetFormat(CompressedFormat format) const
+{
+    switch (format)
+    {
+    #if !defined(GL_ES_VERSION_2_0) || defined(ANDROID)
+    case CF_DXT1:
+        return dxtTextureSupport_ ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : 0;
+    #endif
+        
+    #ifndef GL_ES_VERSION_2_0
+    case CF_DXT3:
+        return dxtTextureSupport_ ? GL_COMPRESSED_RGBA_S3TC_DXT3_EXT : 0;
+        
+    case CF_DXT5:
+        return dxtTextureSupport_ ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : 0;
+    #else
+    case CF_ETC1:
+        return GL_ETC1_RGB8_OES;
+    #endif
+    }
+    
+    return 0;
+}
+
+
 VertexBuffer* Graphics::GetVertexBuffer(unsigned index) const
 {
     return index < MAX_VERTEX_STREAMS ? vertexBuffers_[index] : 0;

+ 5 - 4
Engine/Graphics/OpenGL/OGLGraphics.h

@@ -27,6 +27,7 @@
 #include "Color.h"
 #include "GraphicsDefs.h"
 #include "HashMap.h"
+#include "Image.h"
 #include "Matrix3x4.h"
 #include "Object.h"
 #include "Rect.h"
@@ -244,12 +245,12 @@ public:
     bool GetHiresShadowSupport() const { return true; }
     /// Return whether stream offset is supported. Always false on OpenGL.
     bool GetStreamOffsetSupport() const { return false; }
-    /// Return whether DXT texture compression is supported.
-    bool GetCompressedTextureSupport() const { return compressedTextureSupport_; }
     /// Return supported fullscreen resolutions.
     PODVector<IntVector2> GetResolutions() const;
     /// Return supported multisampling levels.
     PODVector<int> GetMultiSampleLevels() const;
+    /// Return hardware format for a compressed image format, or 0 if unsupported.
+    unsigned GetFormat(CompressedFormat format) const;
     /// Return vertex buffer by index.
     VertexBuffer* GetVertexBuffer(unsigned index) const;
     /// Return index buffer.
@@ -387,8 +388,8 @@ private:
     bool deferredSupport_;
     /// Hardware depth support flag.
     bool hardwareDepthSupport_;
-    /// Compressed texture support flag.
-    bool compressedTextureSupport_;
+    /// DXT format support flag.
+    bool dxtTextureSupport_;
     /// Number of primitives this frame.
     unsigned numPrimitives_;
     /// Number of batches this frame.

+ 7 - 28
Engine/Graphics/OpenGL/OGLTexture.cpp

@@ -202,9 +202,9 @@ bool Texture::IsCompressed() const
         format_ == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
     #else
     #ifdef ANDROID
-    return format_ == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+    return format_ == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || format_ == GL_ETC1_RGB8_OES;
     #else
-    return false;
+    return format_ == GL_ETC1_RGB8_OES;
     #endif
     #endif
 }
@@ -265,11 +265,14 @@ unsigned Texture::GetRowDataSize(int width) const
     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
         return ((width + 3) >> 2) * 8;
     #endif
-            
+    
     #ifndef GL_ES_VERSION_2_0
     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
         return ((width + 3) >> 2) * 16;
+    #else
+    case GL_ETC1_RGB8_OES:
+        return ((width + 3) >> 2) * 8;
     #endif
         
     default:
@@ -277,27 +280,6 @@ unsigned Texture::GetRowDataSize(int width) const
     }
 }
 
-unsigned Texture::GetDXTFormat(CompressedFormat format)
-{
-    switch (format)
-    {
-    #if !defined(GL_ES_VERSION_2_0) || defined(ANDROID)
-    case CF_DXT1:
-        return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
-    #endif
-            
-    #ifndef GL_ES_VERSION_2_0
-    case CF_DXT3:
-        return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
-        
-    case CF_DXT5:
-        return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
-    #endif
-    }
-    
-    return 0;
-}
-
 unsigned Texture::GetExternalFormat(unsigned format)
 {
     #ifndef GL_ES_VERSION_2_0
@@ -311,10 +293,7 @@ unsigned Texture::GetExternalFormat(unsigned format)
     else
         return format;
     #else
-    if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_COMPONENT16 || format == GL_DEPTH_COMPONENT24_OES)
-        return GL_DEPTH_COMPONENT;
-    else
-        return format;
+    return format;
     #endif
 }
 

+ 0 - 3
Engine/Graphics/OpenGL/OGLTexture.h

@@ -27,7 +27,6 @@
 #include "Color.h"
 #include "GPUObject.h"
 #include "GraphicsDefs.h"
-#include "Image.h"
 #include "Resource.h"
 
 static const int MAX_TEXTURE_QUALITY_LEVELS = 3;
@@ -99,8 +98,6 @@ public:
     unsigned GetDataSize(int width, int height) const;
     /// Return data size in bytes for a pixel or block row.
     unsigned GetRowDataSize(int width) const;
-    /// Return API-specific compressed texture format.
-    static unsigned GetDXTFormat(CompressedFormat format);
     /// Return the non-internal texture format corresponding to an OpenGL internal format.
     static unsigned GetExternalFormat(unsigned format);
     /// Return the data type corresponding to an OpenGL internal format.

+ 2 - 2
Engine/Graphics/OpenGL/OGLTexture2D.cpp

@@ -280,10 +280,10 @@ bool Texture2D::Load(SharedPtr<Image> image, bool useAlpha)
         int width = image->GetWidth();
         int height = image->GetHeight();
         unsigned levels = image->GetNumCompressedLevels();
-        unsigned format = GetDXTFormat(image->GetCompressedFormat());
+        unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
         bool needDecompress = false;
         
-        if (!graphics_->GetCompressedTextureSupport() || !format)
+        if (!format)
         {
             format = Graphics::GetRGBAFormat();
             needDecompress = true;

+ 2 - 2
Engine/Graphics/OpenGL/OGLTextureCube.cpp

@@ -364,7 +364,7 @@ bool TextureCube::Load(CubeMapFace face, SharedPtr<Image> image, bool useAlpha)
         int width = image->GetWidth();
         int height = image->GetHeight();
         unsigned levels = image->GetNumCompressedLevels();
-        unsigned format = GetDXTFormat(image->GetCompressedFormat());
+        unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
         bool needDecompress = false;
         
         if (width != height)
@@ -373,7 +373,7 @@ bool TextureCube::Load(CubeMapFace face, SharedPtr<Image> image, bool useAlpha)
             return false;
         }
         
-        if (!graphics_->GetCompressedTextureSupport() || !format)
+        if (!format)
         {
             format = Graphics::GetRGBAFormat();
             needDecompress = true;

+ 118 - 23
Engine/Resource/Image.cpp

@@ -162,7 +162,7 @@ struct DDSurfaceDesc2
     unsigned dwTextureStage_;
 };
 
-void CompressedLevel::Decompress(unsigned char* dest)
+bool CompressedLevel::Decompress(unsigned char* dest)
 {
     int flags = 0;
     
@@ -181,8 +181,11 @@ void CompressedLevel::Decompress(unsigned char* dest)
         break;
     }
     
-    if (data_)
-        squish::DecompressImage(dest, width_, height_, data_, flags);
+    if (!flags || !data_)
+        return false;
+    
+    squish::DecompressImage(dest, width_, height_, data_, flags);
+    return true;
 }
 
 OBJECTTYPESTATIC(Image);
@@ -206,24 +209,10 @@ void Image::RegisterObject(Context* context)
 
 bool Image::Load(Deserializer& source)
 {
-    // Check for DDS compressed format
-    if (source.ReadFileID() != "DDS ")
-    {
-        // Not DDS, use STBImage to load other image formats as uncompressed
-        source.Seek(0);
-        int width, height;
-        unsigned components;
-        unsigned char* pixelData = GetImageData(source, width, height, components);
-        if (!pixelData)
-        {
-            LOGERROR("Could not load image: " + String(stbi_failure_reason()));
-            return false;
-        }
-        SetSize(width, height, components);
-        SetData(pixelData);
-        FreeImageData(pixelData);
-    }
-    else
+    // Check for DDS or KTX compressed format
+    String fileID = source.ReadFileID();
+    
+    if (fileID == "DDS ")
     {
         // DDS compressed format
         DDSurfaceDesc2 ddsd;
@@ -250,7 +239,7 @@ bool Image::Load(Deserializer& source)
             LOGERROR("Unsupported DDS format");
             return false;
         }
-
+        
         unsigned dataSize = source.GetSize() - source.GetPosition();
         data_ = new unsigned char[dataSize];
         width_ = ddsd.dwWidth_;
@@ -261,6 +250,112 @@ bool Image::Load(Deserializer& source)
         SetMemoryUse(dataSize);
         source.Read(data_.Get(), dataSize);
     }
+    else if (fileID == "\253KTX")
+    {
+        source.Seek(12);
+        
+        unsigned endianness = source.ReadUInt();
+        unsigned type = source.ReadUInt();
+        unsigned typeSize = source.ReadUInt();
+        unsigned format = source.ReadUInt();
+        unsigned internalFormat = source.ReadUInt();
+        unsigned baseInternalFormat = source.ReadUInt();
+        unsigned width = source.ReadUInt();
+        unsigned height = source.ReadUInt();
+        unsigned depth = source.ReadUInt();
+        unsigned arrayElements = source.ReadUInt();
+        unsigned faces = source.ReadUInt();
+        unsigned mipmaps = source.ReadUInt();
+        unsigned keyValueBytes = source.ReadUInt();
+        
+        if (endianness != 0x04030201)
+        {
+            LOGERROR("Big-endian KTX files not supported");
+            return false;
+        }
+        
+        if (type != 0 || format != 0)
+        {
+            LOGERROR("Uncompressed KTX files not supported");
+            return false;
+        }
+        
+        if (faces > 1 || depth > 1)
+        {
+            LOGERROR("3D or cube KTX files not supported");
+            return false;
+        }
+        
+        if (mipmaps == 0)
+        {
+            LOGERROR("KTX files without explicitly specified mipmap count not supported");
+            return false;
+        }
+        
+        compressedFormat_ = CF_NONE;
+        switch (internalFormat)
+        {
+        case 0x83f1:
+            compressedFormat_ = CF_DXT1;
+            components_ = 4;
+            break;
+            
+        case 0x83f2:
+            compressedFormat_ = CF_DXT3;
+            components_ = 4;
+            break;
+            
+        case 0x83f3:
+            compressedFormat_ = CF_DXT5;
+            components_ = 4;
+            break;
+            
+        case 0x8d64:
+            compressedFormat_ = CF_ETC1;
+            components_ = 3;
+            break;
+        }
+        
+        if (compressedFormat_ == CF_NONE)
+        {
+            LOGERROR("Unrecognized texture format in KTX file");
+            return false;
+        }
+        
+        source.Seek(source.GetPosition() + keyValueBytes);
+        unsigned dataSize = source.GetSize() - source.GetPosition() - mipmaps * sizeof(unsigned);
+        
+        data_ = new unsigned char[dataSize];
+        width_ = width;
+        height_ = height;
+        numCompressedLevels_ = mipmaps;
+        
+        unsigned dataOffset = 0;
+        for (unsigned i = 0; i < mipmaps; ++i)
+        {
+            unsigned levelSize = source.ReadUInt();
+            source.Read(&data_[dataOffset], levelSize);
+            dataOffset += levelSize;
+        }
+        
+        SetMemoryUse(dataSize);
+    }
+    else
+    {
+        // Not DDS or KTX, use STBImage to load other image formats as uncompressed
+        source.Seek(0);
+        int width, height;
+        unsigned components;
+        unsigned char* pixelData = GetImageData(source, width, height, components);
+        if (!pixelData)
+        {
+            LOGERROR("Could not load image: " + String(stbi_failure_reason()));
+            return false;
+        }
+        SetSize(width, height, components);
+        SetData(pixelData);
+       
+    }
     
     return true;
 }
@@ -506,7 +601,7 @@ CompressedLevel Image::GetCompressedLevel(unsigned index) const
     level.compressedFormat_ = compressedFormat_;
     level.width_ = width_;
     level.height_ = height_;
-    level.blockSize_ = compressedFormat_ == CF_DXT1 ? 8 : 16;
+    level.blockSize_ = (compressedFormat_ == CF_DXT1 || compressedFormat_ == CF_ETC1) ? 8 : 16;
     unsigned i = 0;
     unsigned offset = 0;
     

+ 4 - 3
Engine/Resource/Image.h

@@ -32,7 +32,8 @@ enum CompressedFormat
     CF_NONE = 0,
     CF_DXT1,
     CF_DXT3,
-    CF_DXT5
+    CF_DXT5,
+    CF_ETC1
 };
 
 /// Compressed image mip level.
@@ -50,8 +51,8 @@ struct CompressedLevel
     {
     }
     
-    /// Decompress. The destination buffer required is width * height * 4 bytes.
-    void Decompress(unsigned char* dest);
+    /// Decompress. The destination buffer required is width * height * 4 bytes. Return true if successful.
+    bool Decompress(unsigned char* dest);
     
     /// Compressed image data.
     unsigned char* data_;