Browse Source

New texture format

Panagiotis Christopoulos Charitos 12 years ago
parent
commit
172d75b3a0

+ 57 - 0
docs/drafts/texture_format.txt

@@ -0,0 +1,57 @@
+
+======
+Header
+======
+
+ANKITEX1
+width
+height
+depth
+texture type  2D | cube | 3D | 2DArray
+internal_format  RGB8 | RGBA8
+compression_formats  RAW & ETC2 & S3TC
+normal
+miplevels
+
+
+===========
+Data layout
+===========
+
+- Format A
+	- Level 0
+		- Depth 0 or face 0
+		- Depth N or face N
+	- Level N
+- Format B
+
+
+================
+Helper functions
+================
+
+PtrSize getDataSectionSize()
+
+Get The size of all the texture data. header+data_section_size == file_size
+
+
+PtrSize getCompressedFormatSize(format)
+
+Get the size of the a compressed format
+
+
+UVec2 getLevelSize(width, height, level)
+
+
+UVec2 getSurfaceSize(data_compression, level);
+
+Given a data compression format and the level get the size that the data should 
+be
+
+=============
+Sanity checks
+=============
+
+- Compressed format and no mipmap should issue a warning
+- Get the total size of the file, sub the header, calculate the size and check 
+  if they match

+ 3 - 5
include/anki/util/Util.h → include/anki/Util.h

@@ -1,16 +1,15 @@
-#ifndef ANKI_UTIL_UTIL_H
-#define ANKI_UTIL_UTIL_H
+#ifndef ANKI_UTIL_H
+#define ANKI_UTIL_H
 
 #include "anki/util/Allocator.h"
 #include "anki/util/Array.h"
 #include "anki/util/Assert.h"
 #include "anki/util/Barrier.h"
-#include "anki/util/BinaryStream.h"
 #include "anki/util/Bitset.h"
 #include "anki/util/ConstCharPtrHashMap.h"
 #include "anki/util/DynamicArray.h"
 #include "anki/util/Exception.h"
-#include "anki/util/Filesystem.h"
+#include "anki/util/File.h"
 #include "anki/util/Functions.h"
 #include "anki/util/HighRezTimer.h"
 #include "anki/util/LinuxMalinfo.h"
@@ -22,7 +21,6 @@
 #include "anki/util/StdTypes.h"
 #include "anki/util/StringList.h"
 #include "anki/util/System.h"
-#include "anki/util/Util.h"
 //#include "anki/util/Variant.h"
 #include "anki/util/Vector.h"
 #include "anki/util/Visitor.h"

+ 1 - 1
include/anki/core/Counters.h

@@ -1,6 +1,6 @@
 #include "anki/util/StdTypes.h"
 #include "anki/util/Singleton.h"
-#include "anki/util/Filesystem.h"
+#include "anki/util/File.h"
 #include "anki/util/HighRezTimer.h"
 
 namespace anki {

+ 52 - 109
include/anki/resource/Image.h

@@ -13,159 +13,102 @@ namespace anki {
 class Image
 {
 public:
+	/// Texture type
+	enum TextureType
+	{
+		TT_NONE,
+		TT_2D,
+		TT_CUBE,
+		TT_3D,
+		TT_2D_ARRAY
+	};
+
 	/// The acceptable color types of AnKi
-	enum ColorType
+	enum ColorFormat
 	{
-		CT_R, ///< Red only
-		CT_RGB, ///< RGB
-		CT_RGBA ///< RGB plus alpha
+		CF_NONE,
+		CF_RGB8, ///< RGB
+		CF_RGBA8 ///< RGB plus alpha
 	};
 
 	/// The data compression
 	enum DataCompression
 	{
 		DC_NONE,
+		DC_RAW,
 		DC_DXT1,
-		DC_DXT3,
-		DC_DXT5
+		DC_DXT5,
+		DC_ETC2
+	};
+
+	/// An image surface
+	struct Surface
+	{
+		U32 width;
+		U32 height;
+		U32 mipLevel;
+		Vector<U8> data;
 	};
 
 	/// Do nothing
 	Image()
 	{}
 
-	/// Load an image
-	/// @param[in] filename The image file to load
-	/// @exception Exception
-	Image(const char* filename)
-	{
-		load(filename);
-	}
-
 	/// Do nothing
 	~Image()
 	{}
 
 	/// @name Accessors
 	/// @{
-	U32 getWidth() const
+	ColorFormat getColorFormat() const
 	{
-		return width;
+		ANKI_ASSERT(colorFormat != CF_NONE);
+		return colorFormat;
 	}
 
-	U32 getHeight() const
+	DataCompression getCompression() const
 	{
-		return height;
+		ANKI_ASSERT(compression != DC_NONE);
+		return compression;
 	}
 
-	ColorType getColorType() const
+	U getMipLevelsCount() const
 	{
-		return type;
+		ANKI_ASSERT(mipLevels != 0);
+		return mipLevels;
 	}
 
-	const U8* getData() const
+	U getDepth() const
 	{
-		return &data[0];
+		ANKI_ASSERT(depth != 0);
+		return depth;
 	}
 
-	/// Get image size in bytes
-	size_t getDataSize() const
+	TextureType getTextureType() const
 	{
-		return getVectorSizeInBytes(data);
+		ANKI_ASSERT(textureType != TT_NONE);
+		return textureType;
 	}
 
-	DataCompression getDataCompression() const
-	{
-		return dataCompression;
-	}
+	const Surface& getSurface(U mipLevel, U depthOrFace) const;
 	/// @}
 
 	/// Load an image file
 	/// @param[in] filename The file to load
-	/// @exception Exception
 	void load(const char* filename);
 
-private:
-	U32 width = 0; ///< Image width
-	U32 height = 0; ///< Image height
-	ColorType type; ///< Image color type
-	Vector<U8> data; ///< Image data
-	DataCompression dataCompression;
-
-	/// @name TGA headers
-	/// @{
-	static U8 tgaHeaderUncompressed[12];
-	static U8 tgaHeaderCompressed[12];
-	/// @}
-
-	/// Load a TGA
-	/// @param[in] filename The file to load
-	/// @exception Exception
-	void loadTga(const char* filename);
-
-	/// Used by loadTga
-	/// @param[in] fs The input
-	/// @param[out] bpp Bits per pixel
-	/// @exception Exception
-	void loadUncompressedTga(std::fstream& fs, U32& bpp);
-
-	/// Used by loadTga
-	/// @param[in] fs The input
-	/// @param[out] bpp Bits per pixel
-	/// @exception Exception
-	void loadCompressedTga(std::fstream& fs, U32& bpp);
-
-	/// Load PNG. Dont throw exception because libpng is in C
-	/// @param[in] filename The file to load
-	/// @param[out] err The error message
-	/// @return true on success
-	bool loadPng(const char* filename, std::string& err) throw();
-
-	/// Load a DDS file
-	/// @param[in] filename The file to load
-	void loadDds(const char* filename);
-};
-
-/// A super image that loads multiple images, used in cubemaps, 3D textures
-class MultiImage
-{
-public:
-	/// The type of the image
-	enum MultiImageType
-	{
-		MIT_SINGLE,
-		MIT_CUBE,
-		MIT_ARRAY
-	};
-
-	MultiImage(const char* filename)
-	{
-		load(filename);
-	}
-
-	void load(const char* filename);
-
-	/// Get single image
-	const Image& getImage() const
-	{
-		ANKI_ASSERT(images.size() == 1);
-		ANKI_ASSERT(type == MIT_SINGLE);
-		return images[0];
-	}
-
-	/// Get face image
-	const Image& getImageFace(U face) const
-	{
-		ANKI_ASSERT(images.size() == 6);
-		ANKI_ASSERT(type == MIT_CUBE);
-		return images[face];
-	}
+	/// Load an image file
+	/// @param[in] filenames The 6 files to load
+	void loadCube(const char* filenames[6]);
 
 private:
-	Vector<Image> images;
-	MultiImageType type;
-
-	void loadCubemap(const char* filename);
+	/// [mip][depthFace]
+	Vector<Surface> surfaces;
+	U8 mipLevels = 0;
+	U8 depth = 0;
+	DataCompression compression = DC_NONE;
+	ColorFormat colorFormat = CF_NONE;
+	TextureType textureType = TT_NONE;
 };
 
 } // end namespace anki

+ 0 - 59
include/anki/util/BinaryStream.h

@@ -1,59 +0,0 @@
-#ifndef ANKI_UTIL_BINARY_STREAM_H
-#define ANKI_UTIL_BINARY_STREAM_H
-
-#include "anki/util/Exception.h"
-#include "anki/util/StdTypes.h"
-#include <iostream>
-
-namespace anki {
-
-/// Read from binary streams. You can read/write data as if it is an iostream 
-/// but it also contains methods for reading/writing binary data
-class BinaryStream: public std::iostream
-{
-public:
-	/// The 2 available byte orders
-	enum ByteOrder
-	{
-		BO_LITTLE_ENDIAN, ///< The default
-		BO_BIG_ENDIAN
-	};
-
-	/// The one and only constructor
-	/// @param sb An std::streambuf for in/out
-	/// @param byteOrder The stream's byte order
-	BinaryStream(std::streambuf* sb,
-		ByteOrder byteOrder_ = BO_LITTLE_ENDIAN)
-		: std::iostream(sb), byteOrder(byteOrder_)
-	{}
-
-	~BinaryStream();
-
-	/// Read unsigned int (32bit)
-	/// @exception Exception
-	U32 readUint();
-
-	/// Read float (32bit)
-	/// @exception Exception
-	F32 readFloat();
-
-	/// Read a string. It reads the size as an unsigned int and then it
-	/// reads the characters
-	/// @exception Exception
-	std::string readString();
-
-	/// Get machine byte order
-	/// @return The byte order of the current running platform
-	static ByteOrder getMachineByteOrder();
-
-private:
-	ByteOrder byteOrder;
-
-	/// A little hack so we dont write duplicate code
-	template<typename Type>
-	Type read32bitNumber();
-};
-
-} // end namespace
-
-#endif

+ 144 - 0
include/anki/util/File.h

@@ -0,0 +1,144 @@
+#ifndef ANKI_UTIL_FILE_H
+#define ANKI_UTIL_FILE_H
+
+#include "anki/util/StringList.h"
+#include <string>
+
+namespace anki {
+
+/// @addtogroup util
+/// @{
+/// @addtogroup filesystem
+/// @{
+
+/// An abstraction over typical files and files in ziped archives.
+class File
+{
+private:
+	/// Internal filetype
+	enum FileType
+	{
+		FT_C = 1 << 0, ///< C file
+		FT_ZIP = 1 << 1 ///< Ziped file
+	};
+
+public:
+	/// Open mode
+	enum OpenFlag
+	{
+		OF_READ = 1 << 2,
+		OF_WRITE = 1 << 3,
+		OF_APPEND = OF_WRITE | (1 << 4),
+		OF_BINARY = 1 << 5
+	};
+
+	/// The 2 available byte orders. Used in binary files.
+	enum Endianness
+	{
+		E_LITTLE_ENDIAN = 1 << 6, ///< The default
+		E_BIG_ENDIAN = 1 << 7
+	};
+
+	/// Passed to seek function
+	enum SeekOrigin
+	{
+		SO_BEGINNING = SEEK_SET,
+		SO_CURRENT = SEEK_CUR,
+		SO_END = SEEK_END
+	};
+
+	/// Default constructor
+	File()
+		: flags(0), file(nullptr)
+	{}
+
+	/// Open file
+	File(const char* filename, U8 openMask)
+		: flags(0), file(nullptr)
+	{
+		open(filename, openMask);
+	}
+
+	/// Closes the file if it's open
+	~File();
+
+	/// Open a file
+	/// @param[in] filename The file to open
+	/// @param[in] openMask The open flags. It's a combination of OpenFlag and 
+	///                     Endianness enums
+	void open(const char* filename, U8 openMask);
+
+	/// Close the file
+	void close();
+
+	/// @name Read methods
+	/// @{
+
+	/// Read data from the file
+	void read(void* buff, PtrSize size);
+
+	/// Read all the contents of a text file
+	void readAllText(std::string& out);
+
+	/// Read all the contents of a text file and return the result in lines
+	void readAllTextLines(StringList& lines);
+
+	/// Read 32bit unsigned integer. Set the endianness if the file's 
+	/// endianness is different from the machine's
+	U32 readU32();
+
+	/// Read 32bit float. Set the endianness if the file's endianness is 
+	/// different from the machine's
+	F32 readF32();
+	/// @}
+
+	/// @name Write methods
+	/// @{
+
+	/// Write data to the file
+	void write(void* buff, PtrSize size);
+
+	/// Write text
+	void writeText(const char* format, ...);
+	/// @}
+
+	/// Set the position indicator to a new position
+	/// @param offset Number of bytes to offset from origin
+	/// @param origin Position used as reference for the offset
+	void seek(PtrSize offset, SeekOrigin origin);
+
+	/// @name Public statics
+	/// @{
+
+	/// Get file extension
+	/// @param[in] filename The file to open
+	/// @return nullptr on failure and if the dot is the last character
+	static const char* getFileExtension(const char* filename);
+
+	/// File exists?
+	static Bool fileExists(const char* filename);
+	/// @}
+
+private:
+	U8 flags; ///< All the flags
+	void* file; ///< A native type
+
+	/// Get the current machine's endianness
+	static Endianness getMachineEndianness();
+};
+
+/// Directory exists?
+extern Bool directoryExists(const char* filename);
+
+/// rm -rf
+extern void removeDirectory(const char* dir);
+
+/// mkdir
+extern void createDirectory(const char* dir);
+
+/// @}
+/// @}
+
+} // end namespace anki
+
+#endif

+ 0 - 100
include/anki/util/Filesystem.h

@@ -1,100 +0,0 @@
-#ifndef ANKI_UTIL_FILESYSTEM_H
-#define ANKI_UTIL_FILESYSTEM_H
-
-#include "anki/util/StringList.h"
-#include <string>
-
-namespace anki {
-
-/// @addtogroup util
-/// @{
-/// @addtogroup filesystem
-/// @{
-
-/// An abstraction over typical files and files in ziped archives.
-class File
-{
-private:
-	/// Internal filetype
-	enum FileType
-	{
-		FT_NONE,
-		FT_C, ///< C file
-		FT_ZIP ///< Ziped file
-	};
-
-public:
-	/// Open mode
-	enum OpenFlag
-	{
-		OF_READ = 1 << 0,
-		OF_WRITE = 1 << 1,
-		OF_APPEND = OF_WRITE | (1 << 2),
-		OF_BINARY = 1 << 3
-	};
-
-	/// Default constructor
-	File()
-		: openFlags(0), file(nullptr), fileType(FT_NONE)
-	{}
-
-	/// Closes the file if it's open
-	~File();
-
-	/// Open a file
-	void open(const char* filename, U8 openMask);
-
-	/// Close the file
-	void close();
-
-	/// Read data from the file
-	PtrSize read(void* buff, PtrSize size);
-
-	/// Read all the contents of a text file
-	void readAll(std::string& txt);
-
-	/// Write data to the file
-	PtrSize write(void* buff, PtrSize size);
-
-	/// Wrtite text
-	void writeString(const char* format, ...);
-
-public:
-	U8 openFlags; ///< Mainly for assertions
-	void* file; ///< A native type
-	U8 fileType;
-};
-
-/// Get file extension
-/// @param[in] filename The file to open
-/// @return nullptr on failure and if the dot is the last character
-extern const char* getFileExtension(const char* filename);
-
-/// Open a text file and return its contents into a string
-extern std::string readFile(const char* filename);
-
-/// Open a text file and return its lines into a string vector
-extern StringList readFileLines(const char* filename);
-
-/// File exists?
-extern bool fileExists(const char* filename);
-
-/// Directory exists?
-extern bool directoryExists(const char* filename);
-
-/// rm -rf
-extern void removeDirectory(const char* dir);
-
-/// mkdir
-extern void createDirectory(const char* dir);
-
-/// Convert a POSIX path to a platform native path. It actually replaces /
-/// with \ in windows
-extern void toNativePath(const char* path);
-
-/// @}
-/// @}
-
-} // end namespace anki
-
-#endif

+ 10 - 0
include/anki/util/Functions.h

@@ -52,6 +52,16 @@ inline std::string trimString(const std::string& str, const char* what = " ")
 	return out;
 }
 
+/// Check if a number os a power of 2
+inline Bool isPowerOfTwo(U64 x)
+{
+	while(((x % 2) == 0) && x > 1)
+	{
+		x /= 2;
+	}
+	return (x == 1);
+}
+
 /// Replace substring. Substitute occurances of @a from into @a to inside the
 /// @a str string
 extern std::string replaceAllString(const std::string& str, 

+ 3 - 1
shaders/BsCommonFrag.glsl

@@ -1,4 +1,6 @@
 // Common code for all fragment shaders of BS
+#define DEFAULT_FLOAT_PRECISION mediump
+
 #pragma anki include "shaders/CommonFrag.glsl"
 #pragma anki include "shaders/MsBsCommon.glsl"
 
@@ -27,7 +29,7 @@ void writeFais(in vec4 color)
 #define particleAlpha_DEFINED
 void particleAlpha(in sampler2D tex, in float alpha)
 {
-	vec4 color = texture(tex, vTexCoords);
+	vec4 color = DEFAULT_FLOAT_PRECISION vec4(texture(tex, vTexCoords));
 	color.w *= alpha;
 	writeFais(color);
 }

+ 8 - 1
shaders/CommonFrag.glsl

@@ -1,9 +1,16 @@
 #ifndef ANKI_SHADERS_COMMON_FRAG_GLSL
 #define ANKI_SHADERS_COMMON_FRAG_GLSL
 
-#ifdef GL_ES
+#ifndef DEFAULT_FLOAT_PRECISION
 precision highp float;
+#else
+precision DEFAULT_FLOAT_PRECISION float;
+#endif
+
+#ifndef DEFAULT_INT_PRECISION
 precision highp int;
+#else
+precision DEFAULT_FLOAT_PRECISION int;
 #endif
 
 #endif

+ 3 - 2
shaders/IsLp.glsl

@@ -205,8 +205,9 @@ float calcShadowFactor(in SpotTexLight light, in vec3 fragPosVspace,
 #if PCF == 1
 	float shadowFactor = pcfLow(shadowMapArr, texCoords3);
 #else
-	float shadowFactor = texture(shadowMapArr, 
-		vec4(texCoords3.x, texCoords3.y, layer, texCoords3.z));
+	float shadowFactor = 
+		texture(shadowMapArr, 
+		vec4(texCoords3.x, texCoords3.y, layer, texCoords3.z)).r;
 #endif
 
 	return shadowFactor;

+ 11 - 9
shaders/MsCommonFrag.glsl

@@ -1,3 +1,4 @@
+#define DEFAULT_FLOAT_PRECISION mediump
 #pragma anki include "shaders/CommonFrag.glsl"
 
 //==============================================================================
@@ -7,7 +8,7 @@
 /// @name Varyings
 /// @{
 #define vTexCoords_DEFINED
-in vec2 vTexCoords;
+in highp vec2 vTexCoords;
 
 #if defined(PASS_COLOR)
 in vec3 vNormal;
@@ -46,13 +47,14 @@ layout(location = 0) out uvec2 fMsFai0;
 #if defined(PASS_COLOR)
 #	define getNormalFromTexture_DEFINED
 vec3 getNormalFromTexture(in vec3 normal, in vec3 tangent, in float tangentW,
-	in sampler2D map, in vec2 texCoords)
+	in sampler2D map, in highp vec2 texCoords)
 {
 #	if LOD > 0
 	return normalize(normal);
 #	else
 	// First read the texture
-	vec3 nAtTangentspace = (texture(map, texCoords).rgb - 0.5) * 2.0;
+	vec3 nAtTangentspace = 
+		(DEFAULT_FLOAT_PRECISION vec3(texture(map, texCoords)).rgb - 0.5) * 2.0;
 
 	vec3 n = normalize(normal);
 	vec3 t = normalize(tangent);
@@ -93,7 +95,7 @@ vec3 getEnvironmentColor(in vec3 vertPosViewSpace, in vec3 normal,
 	float m = 2.0 * length(r);
 	vec2 semTexCoords = r.xy / m + 0.5;
 
-	vec3 semCol = texture(map, semTexCoords).rgb;
+	vec3 semCol = DEFAULT_FLOAT_PRECISION vec3(texture(map, semTexCoords)).rgb;
 	return semCol;
 }
 #endif
@@ -107,11 +109,11 @@ vec3 getEnvironmentColor(in vec3 vertPosViewSpace, in vec3 normal,
 #define getDiffuseColorAndDoAlphaTesting_DEFINED
 vec3 getDiffuseColorAndDoAlphaTesting(
 	in sampler2D map,
-	in vec2 texCoords,
+	in highp vec2 texCoords,
 	in float tolerance)
 {
 #if defined(PASS_COLOR)
-	vec4 col = texture(map, texCoords);
+	vec4 col = DEFAULT_FLOAT_PRECISION vec4(texture(map, texCoords));
 	if(col.a < tolerance)
 	{
 		discard;
@@ -121,7 +123,7 @@ vec3 getDiffuseColorAndDoAlphaTesting(
 #	if LOD > 0
 	return vec3(0.0);
 #	else
-	float a = texture(map, texCoords).a;
+	float a = DEFAULT_FLOAT_PRECISION float(texture(map, texCoords).a);
 	if(a < tolerance)
 	{
 		discard;
@@ -134,9 +136,9 @@ vec3 getDiffuseColorAndDoAlphaTesting(
 /// Just read the RGB color from texture
 #if defined(PASS_COLOR)
 #	define readRgbFromTexture_DEFINED
-vec3 readRgbFromTexture(in sampler2D tex, in vec2 texCoords)
+vec3 readRgbFromTexture(in sampler2D tex, in highp vec2 texCoords)
 {
-	return texture(tex, texCoords).rgb;
+	return DEFAULT_FLOAT_PRECISION vec3(texture(tex, texCoords)).rgb;
 }
 #endif
 

+ 8 - 8
shaders/MsCommonVert.glsl

@@ -15,10 +15,10 @@ layout(location = 2) in vec4 tangent;
 /// @{
 out vec2 vTexCoords;
 #if defined(PASS_COLOR)
-out vec3 vNormal;
-out vec3 vTangent;
-out float vTangentW;
-out vec3 vVertPosViewSpace; ///< For env mapping. AKA view vector
+out mediump vec3 vNormal;
+out mediump vec3 vTangent;
+out mediump float vTangentW;
+out mediump vec3 vVertPosViewSpace; ///< For env mapping. AKA view vector
 /// Calculate it per vertex instead of per fragment
 flat out lowp float vSpecularComponent; 
 #endif
@@ -44,9 +44,9 @@ void setVaryings2(
 	in mat3 normalMat)
 {
 #if defined(PASS_COLOR)
-	vNormal = normalMat * normal;
-	vTangent = normalMat * vec3(tangent);
-	vTangentW = tangent.w;
+	vNormal = mediump vec3(normalMat * normal);
+	vTangent = mediump vec3(normalMat * vec3(tangent));
+	vTangentW = mediump float(tangent.w);
 #endif
 
 	setVaryings1(modelViewProjectionMat);
@@ -57,7 +57,7 @@ void setVaryings2(
 #define setVertPosViewSpace_DEFINED
 void setVertPosViewSpace(in mat4 modelViewMat)
 {
-	vVertPosViewSpace = vec3(modelViewMat * vec4(position, 1.0));
+	vVertPosViewSpace = mediump vec3(modelViewMat * vec4(position, 1.0));
 }
 #endif
 

+ 1 - 1
src/core/App.cpp

@@ -1,7 +1,7 @@
 #include "anki/core/App.h"
 #include "anki/core/Logger.h"
 #include "anki/util/Exception.h"
-#include "anki/util/Filesystem.h"
+#include "anki/util/File.h"
 #include "anki/util/System.h"
 #include "anki/Config.h"
 #include <cstring>

+ 13 - 14
src/core/Counters.cpp

@@ -1,6 +1,5 @@
 #include "anki/core/Counters.h"
 #include "anki/core/Timestamp.h"
-#include "anki/util/Filesystem.h"
 #include "anki/util/Array.h"
 #include <cstring>
 
@@ -49,13 +48,13 @@ CountersManager::CountersManager()
 	// Open and write the headers to the files
 	perframeFile.open("./perframe_counters.csv", File::OF_WRITE);
 
-	perframeFile.writeString("FRAME");
+	perframeFile.writeText("FRAME");
 
 	for(const CounterInfo& inf : cinfo)
 	{
 		if(inf.flags & CF_PER_FRAME)
 		{
-			perframeFile.writeString(", %s", inf.name);
+			perframeFile.writeText(", %s", inf.name);
 		}
 	}
 
@@ -69,17 +68,17 @@ CountersManager::CountersManager()
 		{
 			if(i != 0)
 			{
-				perrunFile.writeString(", %" MAX_NAME "s", inf.name);
+				perrunFile.writeText(", %" MAX_NAME "s", inf.name);
 			}
 			else
 			{
-				perrunFile.writeString("%" MAX_NAME "s", inf.name);
+				perrunFile.writeText("%" MAX_NAME "s", inf.name);
 			}
 
 			++i;
 		}
 	}
-	perrunFile.writeString("\n");
+	perrunFile.writeText("\n");
 }
 
 //==============================================================================
@@ -149,7 +148,7 @@ void CountersManager::stopTimerIncreaseCounter(Counter counter)
 void CountersManager::resolveFrame()
 {
 	// Write new line and frame no
-	perframeFile.writeString("\n%llu", getGlobTimestamp());
+	perframeFile.writeText("\n%llu", getGlobTimestamp());
 
 	U i = 0;
 	for(const CounterInfo& inf : cinfo)
@@ -158,11 +157,11 @@ void CountersManager::resolveFrame()
 		{
 			if(inf.flags & CF_U64)
 			{
-				perframeFile.writeString(", %llu", perframeValues[i]);
+				perframeFile.writeText(", %llu", perframeValues[i]);
 			}
 			else if(inf.flags & CF_F64)
 			{
-				perframeFile.writeString(", %f", *((F64*)&perframeValues[i]));
+				perframeFile.writeText(", %f", *((F64*)&perframeValues[i]));
 			}
 			else
 			{
@@ -188,23 +187,23 @@ void CountersManager::flush()
 		{
 			if(j != 0)
 			{
-				perrunFile.writeString(", ");
+				perrunFile.writeText(", ");
 			}
 
 			if(inf.flags & CF_U64)
 			{
-				perrunFile.writeString("%" MAX_NAME "llu", perrunValues[i]);
+				perrunFile.writeText("%" MAX_NAME "llu", perrunValues[i]);
 			}
 			else if(inf.flags & CF_F64)
 			{
 				if(inf.flags & CF_FPS)
 				{
-					perrunFile.writeString("%" MAX_NAME "f", 
+					perrunFile.writeText("%" MAX_NAME "f", 
 						(F64)getGlobTimestamp() / *((F64*)&perrunValues[i]));
 				}
 				else
 				{
-					perrunFile.writeString("%" MAX_NAME "f", 
+					perrunFile.writeText("%" MAX_NAME "f", 
 						*((F64*)&perrunValues[i]));
 				}
 			}
@@ -219,7 +218,7 @@ void CountersManager::flush()
 
 		++i;
 	}
-	perrunFile.writeString("\n");
+	perrunFile.writeText("\n");
 
 	// Close and flush files
 	perframeFile.close();

+ 2 - 2
src/renderer/MainRenderer.cpp

@@ -1,7 +1,7 @@
 #include "anki/renderer/MainRenderer.h"
 #include "anki/core/Logger.h"
 #include "anki/renderer/Deformer.h"
-#include "anki/util/Filesystem.h"
+#include "anki/util/File.h"
 #include "anki/core/Counters.h"
 #include <cstdlib>
 #include <cstdio>
@@ -224,7 +224,7 @@ void MainRenderer::takeScreenshotJpeg(const char* filename)
 //==============================================================================
 void MainRenderer::takeScreenshot(const char* filename)
 {
-	std::string ext = getFileExtension(filename);
+	std::string ext = File::getFileExtension(filename);
 
 	// exec from this extension
 	if(ext == "tga")

+ 207 - 320
src/resource/Image.cpp

@@ -1,7 +1,7 @@
 #include "anki/resource/Image.h"
 #include "anki/util/Exception.h"
 #include "anki/core/Logger.h"
-#include "anki/util/Filesystem.h"
+#include "anki/util/File.h"
 #include "anki/util/Assert.h"
 #include "anki/util/Array.h"
 #include "anki/misc/Xml.h"
@@ -11,52 +11,16 @@
 namespace anki {
 
 //==============================================================================
-// Image                                                                       =
-//==============================================================================
-
+// TGA                                                                         =
 //==============================================================================
-U8 Image::tgaHeaderUncompressed[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-U8 Image::tgaHeaderCompressed[12] = {0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 //==============================================================================
-void Image::load(const char* filename)
-{
-	// get the extension
-	const char* ext = getFileExtension(filename);
-	ANKI_ASSERT(ext);
-
-	// load from this extension
-	try
-	{
-		if(strcmp(ext, "tga") == 0)
-		{
-			loadTga(filename);
-		}
-		else if(strcmp(ext, "png") == 0)
-		{
-			std::string err;
-			if(!loadPng(filename, err))
-			{
-				throw ANKI_EXCEPTION(err);
-			}
-		}
-		else if(strcmp(ext, "dds") == 0)
-		{
-			loadDds(filename);
-		}
-		else
-		{
-			throw ANKI_EXCEPTION("Unsupported extension");
-		}
-	}
-	catch(std::exception& e)
-	{
-		throw ANKI_EXCEPTION("File " + filename) << e;
-	}
-}
+static U8 tgaHeaderUncompressed[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static U8 tgaHeaderCompressed[12] = {0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 //==============================================================================
-void Image::loadUncompressedTga(std::fstream& fs, U32& bpp)
+static void loadUncompressedTga(
+	std::fstream& fs, U32& width, U32& height, U32& bpp, Vector<U8>& data)
 {
 	// read the info from header
 	U8 header6[6];
@@ -76,8 +40,8 @@ void Image::loadUncompressedTga(std::fstream& fs, U32& bpp)
 	}
 
 	// read the data
-	int bytesPerPxl	= (bpp / 8);
-	int imageSize = bytesPerPxl * width * height;
+	I bytesPerPxl	= (bpp / 8);
+	I imageSize = bytesPerPxl * width * height;
 	data.resize(imageSize);
 
 	fs.read(reinterpret_cast<char*>(&data[0]), imageSize);
@@ -89,16 +53,17 @@ void Image::loadUncompressedTga(std::fstream& fs, U32& bpp)
 	// swap red with blue
 	for(int i = 0; i < int(imageSize); i += bytesPerPxl)
 	{
-		uint temp = data[i];
+		U32 temp = data[i];
 		data[i] = data[i + 2];
 		data[i + 2] = temp;
 	}
 }
 
 //==============================================================================
-void Image::loadCompressedTga(std::fstream& fs, U32& bpp)
+static void loadCompressedTga(
+	std::fstream& fs, U32& width, U32& height, U32& bpp, Vector<U8>& data)
 {
-	unsigned char header6[6];
+	U8 header6[6];
 	fs.read((char*)&header6[0], sizeof(header6));
 	if(fs.gcount() != sizeof(header6))
 	{
@@ -114,21 +79,21 @@ void Image::loadCompressedTga(std::fstream& fs, U32& bpp)
 		throw ANKI_EXCEPTION("Invalid texture information");
 	}
 
-	int bytesPerPxl = (bpp / 8);
-	int imageSize = bytesPerPxl * width * height;
+	I bytesPerPxl = (bpp / 8);
+	I imageSize = bytesPerPxl * width * height;
 	data.resize(imageSize);
 
-	uint pixelcount = height * width;
-	uint currentpixel = 0;
-	uint currentbyte = 0;
+	U32 pixelcount = height * width;
+	U32 currentpixel = 0;
+	U32 currentbyte = 0;
 	U8 colorbuffer[4];
 
 	do
 	{
-		unsigned char chunkheader = 0;
+		U8 chunkheader = 0;
 
-		fs.read((char*)&chunkheader, sizeof(unsigned char));
-		if(fs.gcount() != sizeof(unsigned char))
+		fs.read((char*)&chunkheader, sizeof(U8));
+		if(fs.gcount() != sizeof(U8))
 		{
 			throw ANKI_EXCEPTION("Cannot read RLE header");
 		}
@@ -195,12 +160,12 @@ void Image::loadCompressedTga(std::fstream& fs, U32& bpp)
 }
 
 //==============================================================================
-void Image::loadTga(const char* filename)
+static void loadTga(const char* filename, 
+	U32& width, U32& height, U32& bpp, Vector<U8>& data)
 {
 	std::fstream fs;
 	char myTgaHeader[12];
 	fs.open(filename, std::ios::in | std::ios::binary);
-	uint bpp;
 
 	if(!fs.is_open())
 	{
@@ -216,50 +181,46 @@ void Image::loadTga(const char* filename)
 
 	if(memcmp(tgaHeaderUncompressed, &myTgaHeader[0], sizeof(myTgaHeader)) == 0)
 	{
-		loadUncompressedTga(fs, bpp);
+		loadUncompressedTga(fs, width, height, bpp, data);
 	}
 	else if(memcmp(tgaHeaderCompressed, &myTgaHeader[0],
 		sizeof(myTgaHeader)) == 0)
 	{
-		loadCompressedTga(fs, bpp);
+		loadCompressedTga(fs, width, height, bpp, data);
 	}
 	else
 	{
 		throw ANKI_EXCEPTION("Invalid image header");
 	}
 
-	if(bpp == 32)
+	if(bpp != 32 && bpp != 24)
 	{
-		type = CT_RGBA;
-	}
-	else if(bpp == 24)
-	{
-		type = CT_RGB;
-	}
-	else
-	{
-		throw ANKI_EXCEPTION("Invalid bps");
+		throw ANKI_EXCEPTION("Invalid bpp");
 	}
 
 	fs.close();
-	dataCompression = DC_NONE;
 }
 
 //==============================================================================
-bool Image::loadPng(const char* filename, std::string& err) throw()
+// PNG                                                                         =
+//==============================================================================
+
+//==============================================================================
+static Bool loadPng(const char* filename, std::string& err, 
+	U32& bpp, U32& width, U32& height, Vector<U8>& data) throw()
 {
 	// All locals
 	//
-	const uint PNG_SIG_SIZE = 8; // PNG header size
+	const U32 PNG_SIG_SIZE = 8; // PNG header size
 	FILE* file = NULL;
 	png_structp pngPtr = NULL;
 	png_infop infoPtr = NULL;
-	bool ok = false;
-	size_t charsRead;
-	uint bitDepth;
-	uint channels;
-	uint rowbytes;
-	uint colorType;
+	Bool ok = false;
+	PtrSize charsRead;
+	U32 bitDepth;
+	//U32 channels;
+	U32 rowbytes;
+	U32 colorFormat;
 	Vector<png_bytep> rowPointers;
 
 	// Open file
@@ -327,11 +288,11 @@ bool Image::loadPng(const char* filename, std::string& err) throw()
 		width = png_get_image_width(pngPtr, infoPtr);
 		height = png_get_image_height(pngPtr, infoPtr);
 		bitDepth = png_get_bit_depth(pngPtr, infoPtr);
-		channels = png_get_channels(pngPtr, infoPtr);
-		colorType = png_get_color_type(pngPtr, infoPtr);
+		//channels = png_get_channels(pngPtr, infoPtr);
+		colorFormat = png_get_color_type(pngPtr, infoPtr);
 
 		// 1) Convert the color types
-		switch(colorType)
+		switch(colorFormat)
 		{
 		case PNG_COLOR_TYPE_PALETTE:
 			err = "Converting PNG_COLOR_TYPE_PALETTE to "
@@ -360,7 +321,7 @@ bool Image::loadPng(const char* filename, std::string& err) throw()
 		}
 
 		// 2) Convert the bit depths
-		if(colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)
+		if(colorFormat == PNG_COLOR_TYPE_GRAY && bitDepth < 8)
 		{
 			err = "Converting bit depth";
 #if 0
@@ -383,9 +344,9 @@ bool Image::loadPng(const char* filename, std::string& err) throw()
 
 	// Sanity checks
 	if((bitDepth != 8)
-		|| (colorType != PNG_COLOR_TYPE_GRAY
-			&& colorType != PNG_COLOR_TYPE_RGB
-			&& colorType != PNG_COLOR_TYPE_RGBA))
+		|| (colorFormat != PNG_COLOR_TYPE_GRAY
+			&& colorFormat != PNG_COLOR_TYPE_RGB
+			&& colorFormat != PNG_COLOR_TYPE_RGBA))
 	{
 		err = "Sanity checks failed";
 		goto cleanup;
@@ -399,23 +360,23 @@ bool Image::loadPng(const char* filename, std::string& err) throw()
 
 	data.resize(rowbytes * height);
 
-	for (uint i = 0; i < height; ++i)
+	for (U i = 0; i < height; ++i)
 		rowPointers[height - 1 - i] = &data[i * rowbytes];
 
 	png_read_image(pngPtr, &rowPointers[0]);
 
 	// Finalize
 	//
-	switch(colorType)
+	switch(colorFormat)
 	{
 	case PNG_COLOR_TYPE_GRAY:
-		type = CT_R;
+		bpp = 8;
 		break;
 	case PNG_COLOR_TYPE_RGB:
-		type = CT_RGB;
+		bpp = 24;
 		break;
 	case PNG_COLOR_TYPE_RGBA:
-		type = CT_RGBA;
+		bpp = 32;
 		break;
 	default:
 		err = "See file";
@@ -445,276 +406,193 @@ cleanup:
 		fclose(file);
 	}
 
-	dataCompression = DC_NONE;
 	return ok;
 }
 
 //==============================================================================
-// DDS                                                                         =
+// ANKI                                                                        =
 //==============================================================================
 
-// little-endian, of course
-#define DDS_MAGIC 0x20534444
-
-// DdsHeader.dwFlags
-#define DDSD_CAPS                   0x00000001
-#define DDSD_HEIGHT                 0x00000002
-#define DDSD_WIDTH                  0x00000004
-#define DDSD_PITCH                  0x00000008
-#define DDSD_PIXELFORMAT            0x00001000
-#define DDSD_MIPMAPCOUNT            0x00020000
-#define DDSD_LINEARSIZE             0x00080000
-#define DDSD_DEPTH                  0x00800000
-
-// DdsHeader.sPixelFormat.dwFlags
-#define DDPF_ALPHAPIXELS            0x00000001
-#define DDPF_FOURCC                 0x00000004
-#define DDPF_INDEXED                0x00000020
-#define DDPF_RGB                    0x00000040
-
-// DdsHeader.sCaps.dwCaps1
-#define DDSCAPS_COMPLEX             0x00000008
-#define DDSCAPS_TEXTURE             0x00001000
-#define DDSCAPS_MIPMAP              0x00400000
-
-// DdsHeader.sCaps.dwCaps2
-#define DDSCAPS2_CUBEMAP            0x00000200
-#define DDSCAPS2_CUBEMAP_POSITIVEX  0x00000400
-#define DDSCAPS2_CUBEMAP_NEGATIVEX  0x00000800
-#define DDSCAPS2_CUBEMAP_POSITIVEY  0x00001000
-#define DDSCAPS2_CUBEMAP_NEGATIVEY  0x00002000
-#define DDSCAPS2_CUBEMAP_POSITIVEZ  0x00004000
-#define DDSCAPS2_CUBEMAP_NEGATIVEZ  0x00008000
-#define DDSCAPS2_VOLUME             0x00200000
-
-static uint toInt(const char* x)
-{
-	return x[3] | (x[2] << 8) | (x[1] << 16) | (x[0] << 24);
-}
-
-#define D3DFMT_DXT1 toInt("1TXD") //  DXT1 compression texture format
-#define D3DFMT_DXT2 toInt("2TXD") //  DXT2 compression texture format
-#define D3DFMT_DXT3 toInt("3TXD") //  DXT3 compression texture format
-#define D3DFMT_DXT4 toInt("4TXD") //  DXT4 compression texture format
-#define D3DFMT_DXT5 toInt("5TXD") //  DXT5 compression texture format
-
-#define PF_IS_DXT1(pf) \
-  ((pf.dwFlags & DDPF_FOURCC) && \
-   (pf.dwFourCC == D3DFMT_DXT1))
-
-#define PF_IS_DXT3(pf) \
-  ((pf.dwFlags & DDPF_FOURCC) && \
-   (pf.dwFourCC == D3DFMT_DXT3))
-
-#define PF_IS_DXT5(pf) \
-  ((pf.dwFlags & DDPF_FOURCC) && \
-   (pf.dwFourCC == D3DFMT_DXT5))
-
-#define PF_IS_BGRA8(pf) \
-  ((pf.dwFlags & DDPF_RGB) && \
-   (pf.dwFlags & DDPF_ALPHAPIXELS) && \
-   (pf.dwRGBBitCount == 32) && \
-   (pf.dwRBitMask == 0xff0000) && \
-   (pf.dwGBitMask == 0xff00) && \
-   (pf.dwBBitMask == 0xff) && \
-   (pf.dwAlphaBitMask == 0xff000000U))
-
-#define PF_IS_BGR8(pf) \
-  ((pf.dwFlags & DDPF_ALPHAPIXELS) && \
-  !(pf.dwFlags & DDPF_ALPHAPIXELS) && \
-   (pf.dwRGBBitCount == 24) && \
-   (pf.dwRBitMask == 0xff0000) && \
-   (pf.dwGBitMask == 0xff00) && \
-   (pf.dwBBitMask == 0xff))
-
-#define PF_IS_BGR5A1(pf) \
-  ((pf.dwFlags & DDPF_RGB) && \
-   (pf.dwFlags & DDPF_ALPHAPIXELS) && \
-   (pf.dwRGBBitCount == 16) && \
-   (pf.dwRBitMask == 0x00007c00) && \
-   (pf.dwGBitMask == 0x000003e0) && \
-   (pf.dwBBitMask == 0x0000001f) && \
-   (pf.dwAlphaBitMask == 0x00008000))
-
-#define PF_IS_BGR565(pf) \
-  ((pf.dwFlags & DDPF_RGB) && \
-  !(pf.dwFlags & DDPF_ALPHAPIXELS) && \
-   (pf.dwRGBBitCount == 16) && \
-   (pf.dwRBitMask == 0x0000f800) && \
-   (pf.dwGBitMask == 0x000007e0) && \
-   (pf.dwBBitMask == 0x0000001f))
-
-#define PF_IS_INDEX8(pf) \
-  ((pf.dwFlags & DDPF_INDEXED) && \
-   (pf.dwRGBBitCount == 8))
-
-union DdsHeader
+//==============================================================================
+enum CompressionFormatMask
 {
-	struct
-	{
-		uint dwMagic;
-		uint dwSize;
-		uint dwFlags;
-		uint dwHeight;
-		uint dwWidth;
-		uint dwPitchOrLinearSize;
-		uint dwDepth;
-		uint dwMipMapCount;
-		uint dwReserved1[11];
-
-		//  DDPIXELFORMAT
-		struct
-		{
-			uint dwSize;
-			uint dwFlags;
-			uint dwFourCC;
-			uint dwRGBBitCount;
-			uint dwRBitMask;
-			uint dwGBitMask;
-			uint dwBBitMask;
-			uint dwAlphaBitMask;
-		} sPixelFormat;
-
-		//  DDCAPS2
-		struct
-		{
-			uint dwCaps1;
-			uint dwCaps2;
-			uint dwDDSX;
-			uint dwReserved;
-		} sCaps;
-		uint dwReserved2;
-	} data;
-	char dataArr[128];
+	COMPRESSION_NONE,
+	COMPRESSION_RAW = 1 << 0,
+	COMPRESSION_S3TC = 1 << 1,
+	COMPRESSION_ETC2 = 1 << 2
 };
 
-struct DdsLoadInfo
+struct AnkiTextureHeader
 {
-	bool compressed;
-	bool swap;
-	bool palette;
-	unsigned int divSize;
-	unsigned int blockBytes;
+	Array<U8, 8> magic;
+	U32 width;
+	U32 height;
+	U32 depth;
+	U32 type;
+	U32 colorFormat;
+	U32 compressionFormats;
+	U32 normal;
+	U32 mipLevels;
+	U8 padding[88];
 };
 
-DdsLoadInfo loadInfoDXT1 = {true, false, false, 4, 8};
-DdsLoadInfo loadInfoDXT3 = {true, false, false, 4, 16};
-DdsLoadInfo loadInfoDXT5 = {true, false, false, 4, 16};
-DdsLoadInfo loadInfoBGRA8 = {false, false, false, 1, 4};
-DdsLoadInfo loadInfoBGR8 = {false, false, false, 1, 3};
-DdsLoadInfo loadInfoBGR5A1 = {false, true, false, 1, 2};
-DdsLoadInfo loadInfoBGR565 = {false, true, false, 1, 2};
-DdsLoadInfo loadInfoIndex8 = {false, false, true, 1, 1};
+static_assert(sizeof(AnkiTextureHeader) == 128, 
+	"Check sizeof AnkiTextureHeader");
 
-void Image::loadDds(const char* filename)
+//==============================================================================
+static void loadAnkiTexture(
+	const char* filename, 
+	CompressionFormatMask preferredCompression,
+	Vector<Image::Surface>& surfaces, 
+	U8& depth, 
+	U8& mipLevels, 
+	Image::TextureType& textureType)
 {
-	std::fstream in;
-	in.open(filename, std::ios::in | std::ios::binary);
-
-	if(!in.is_open())
-	{
-		throw ANKI_EXCEPTION("Cannot open file");
-	}
+	File file(filename, 
+		File::OF_READ | File::OF_BINARY | File::E_LITTLE_ENDIAN);
 
-	// Read header
 	//
-	DdsHeader hdr;
-	in.read((char*)&hdr, sizeof(hdr));
+	// Read and check the header
+	//
+	AnkiTextureHeader header;
+	file.read(&header, sizeof(AnkiTextureHeader));
 
-	if(hdr.data.dwMagic != DDS_MAGIC || hdr.data.dwSize != 124
-		|| !(hdr.data.dwFlags & DDSD_PIXELFORMAT)
-		|| !(hdr.data.dwFlags & DDSD_CAPS))
+	if(header.width == 0 || !isPowerOfTwo(header.width)
+		|| header.height == 0 || !isPowerOfTwo(header.height))
 	{
-		throw ANKI_EXCEPTION("Incorrect DDS header");
+		throw ANKI_EXCEPTION("Incorrect width/height value");
 	}
 
-	// Determine the format
-	//
-	DdsLoadInfo * li;
-
-	if(PF_IS_DXT1(hdr.data.sPixelFormat))
+	if(header.width != header.height)
 	{
-		li = &loadInfoDXT1;
-		dataCompression = DC_DXT1;
+		throw ANKI_EXCEPTION("Only square textures are supported");
 	}
-	else if(PF_IS_DXT3(hdr.data.sPixelFormat))
+
+	if(header.depth == 0 || header.depth > 16)
 	{
-		li = &loadInfoDXT3;
-		dataCompression = DC_DXT3;
+		throw ANKI_EXCEPTION("Zero or too big depth");
 	}
-	else if(PF_IS_DXT5(hdr.data.sPixelFormat))
+
+	if(header.type < Image::TT_2D || header.type > Image::TT_2D_ARRAY)
 	{
-		li = &loadInfoDXT5;
-		dataCompression = DC_DXT5;
+		throw ANKI_EXCEPTION("Incorrect texture type");
 	}
-	else
+
+	if(header.colorFormat < Image::CF_RGB8 
+		|| header.colorFormat > Image::CF_RGBA8)
 	{
-		throw ANKI_EXCEPTION("Unsupported DDS format");
+		throw ANKI_EXCEPTION("Incorrect color format");
 	}
 
-	// Load the data
-	//
-	uint mipMapCount = (hdr.data.dwFlags & DDSD_MIPMAPCOUNT) ?
-		hdr.data.dwMipMapCount : 1;
-	if(mipMapCount != 1)
+	if((header.compressionFormats & preferredCompression) == 0)
 	{
-		throw ANKI_EXCEPTION("Currently mipmaps are not supported in DDS");
+		throw ANKI_EXCEPTION("File does not contain the wanted compression");
 	}
 
-	if(li->compressed)
+	// Check mip levels
+	U size = header.width;
+	mipLevels = 0;
+	while(size >= 4)
 	{
-		size_t size = std::max(li->divSize, hdr.data.dwWidth)
-			/ li->divSize * std::max(li->divSize, hdr.data.dwHeight)
-			/ li->divSize * li->blockBytes;
-
-		if(size != hdr.data.dwPitchOrLinearSize)
-		{
-			throw ANKI_EXCEPTION("Size in header and the calculated are "
-				"not the same");
-		}
+		++mipLevels;
+		size /= 2;
+	}
 
-		//assert( hdr.dwFlags & DDSD_LINEARSIZE );
-		data.resize(size);
-		in.read((char*)(&data[0]), size);
+	if(mipLevels != header.mipLevels)
+	{
+		throw ANKI_EXCEPTION("Incorrect number of mip levels");
+	}
 
-		if(in.fail() || in.eof())
-		{
-			throw ANKI_EXCEPTION("Error reading file data");
-		}
+	// Allocate the surfaces
+	switch(header.type)
+	{
+		case Image::TT_2D:
+			depth = 1;
+			break;
+		case Image::TT_CUBE:
+			depth = 6;
+			break;
+		case Image::TT_3D:
+		case Image::TT_2D_ARRAY:
+			depth = header.depth;
+			break;
+		default:
+			ANKI_ASSERT(0);
 	}
 
-	type = CT_RGBA;
-	width = hdr.data.dwWidth;
-	height = hdr.data.dwHeight;
+	surfaces.resize(mipLevels * depth);
 
-	in.close();
+	depth = header.depth;
 }
 
 //==============================================================================
-// MultiImage                                                                  =
+// Image                                                                       =
 //==============================================================================
 
 //==============================================================================
-void MultiImage::load(const char* filename)
+void Image::load(const char* filename)
 {
 	// get the extension
-	const char* ext = getFileExtension(filename);
+	const char* ext = File::getFileExtension(filename);
 	ANKI_ASSERT(ext);
 
 	// load from this extension
 	try
 	{
-		if(strcmp(ext, "cubemap") == 0)
+		U32 bpp;
+		textureType = TT_2D;
+		compression = DC_RAW;
+
+		if(strcmp(ext, "tga") == 0)
+		{
+			surfaces.resize(1);
+			mipLevels = 1;
+			depth = 1;
+			loadTga(filename, surfaces[0].width, surfaces[0].height, bpp, 
+				surfaces[0].data);
+
+			if(bpp == 32)
+			{
+				colorFormat = CF_RGBA8;
+			}
+			else if(bpp == 24)
+			{
+				colorFormat = CF_RGB8;
+			}
+			else
+			{
+				ANKI_ASSERT(0);
+			}
+		}
+		else if(strcmp(ext, "png") == 0)
 		{
-			type = MIT_CUBE;
-			loadCubemap(filename);
+			std::string err;
+			surfaces.resize(1);
+			mipLevels = 1;
+			depth = 1;
+			if(!loadPng(filename, err, bpp, surfaces[0].width, 
+				surfaces[0].height, surfaces[0].data))
+			{
+				throw ANKI_EXCEPTION(err);
+			}
+
+			if(bpp == 32)
+			{
+				colorFormat = CF_RGBA8;
+			}
+			else if(bpp == 24)
+			{
+				colorFormat = CF_RGB8;
+			}
+			else
+			{
+				printf("%d\n", bpp);
+				throw ANKI_EXCEPTION("Unsupported color type");
+			}
 		}
 		else
 		{
-			// Single image
-			type = MIT_SINGLE;
-			images.resize(1);
-			images[0].load(filename);
+			throw ANKI_EXCEPTION("Unsupported extension");
 		}
 	}
 	catch(std::exception& e)
@@ -724,26 +602,35 @@ void MultiImage::load(const char* filename)
 }
 
 //==============================================================================
-void MultiImage::loadCubemap(const char* filename)
+const Image::Surface& Image::getSurface(U mipLevel, U depthOrFace) const
 {
-	// Resize
-	images.resize(6);
-
-	// Load XML
-	XmlDocument doc;
-	doc.loadFile(filename);
+	ANKI_ASSERT(mipLevel < mipLevels);
 
-	XmlElement rootEl = doc.getChildElement("cubemap");
-	static Array<const char*, 6> tags = {{
-		"positiveX", "negativeX", 
-		"positiveY", "negativeY", 
-		"positiveZ", "negativeZ"}};
+	U depthSize = 0;
 
-	for(U i = 0; i < 6; i++)
+	switch(textureType)
 	{
-		XmlElement el = rootEl.getChildElement(tags[i]);
-		images[i].load(el.getText());
+	case TT_2D:
+		depthSize = 1;
+		break;
+	case TT_CUBE:
+		depthSize = 6;
+		break;
+	case TT_3D:
+	case TT_2D_ARRAY:
+		depthSize = depth;
+		break;
+	default:
+		ANKI_ASSERT(0);
 	}
+
+
+	// [mip][depthFace]
+	U index = mipLevel * depthSize + depthOrFace;
+	
+	ANKI_ASSERT(index < surfaces.size());
+
+	return surfaces[index];
 }
 
 } // end namespace anki

+ 2 - 3
src/resource/Material.cpp

@@ -1,9 +1,9 @@
 #include "anki/resource/Material.h"
 #include "anki/resource/MaterialShaderProgramCreator.h"
 #include "anki/core/App.h"
-#include "anki/util/Filesystem.h"
 #include "anki/resource/ShaderProgramResource.h"
 #include "anki/resource/TextureResource.h"
+#include "anki/util/File.h"
 #include "anki/misc/Xml.h"
 #include <functional>
 #include <algorithm>
@@ -308,10 +308,9 @@ std::string Material::createShaderProgSourceToCache(const std::string& source)
 	// Create path
 	std::string newfPathName =
 		AppSingleton::get().getCachePath() + "/mtl_" + prefix + ".glsl";
-	toNativePath(newfPathName.c_str());
 
 	// If file not exists write it
-	if(!fileExists(newfPathName.c_str()))
+	if(!File::fileExists(newfPathName.c_str()))
 	{
 		// If not create it
 		std::ofstream f(newfPathName.c_str());

+ 19 - 22
src/resource/MeshLoader.cpp

@@ -1,5 +1,5 @@
 #include "anki/resource/MeshLoader.h"
-#include "anki/util/BinaryStream.h"
+#include "anki/util/File.h"
 #include <fstream>
 #include <cstring>
 #include <unordered_map>
@@ -42,28 +42,25 @@ void MeshLoader::load(const char* filename)
 	try
 	{
 		// Open the file
-		std::fstream file(filename, std::fstream::in | std::fstream::binary);
-
-		if(!file.is_open())
-		{
-			throw ANKI_EXCEPTION("Cannot open file:" + filename);
-		}
-
-		BinaryStream bs(file.rdbuf());
+		File file(filename, 
+			File::OF_READ | File::OF_BINARY | File::E_LITTLE_ENDIAN);
 
 		// Magic word
 		char magic[8];
-		bs.read(magic, sizeof(magic));
-		if(bs.fail() || memcmp(magic, "ANKIMESH", 8))
+		file.read(magic, sizeof(magic));
+		if(memcmp(magic, "ANKIMESH", 8))
 		{
 			throw ANKI_EXCEPTION("Incorrect magic word");
 		}
 
 		// Mesh name
-		std::string meshName = bs.readString();
+		{
+			U32 strLen = file.readU32();
+			file.seek(strLen, File::SO_CURRENT);
+		}
 
 		// Verts num
-		uint vertsNum = bs.readUint();
+		U vertsNum = file.readU32();
 		vertCoords.resize(vertsNum);
 
 		// Vert coords
@@ -71,12 +68,12 @@ void MeshLoader::load(const char* filename)
 		{
 			for(U j = 0; j < 3; j++)
 			{
-				vertCoord[j] = bs.readFloat();
+				vertCoord[j] = file.readF32();
 			}
 		}
 
 		// Faces num
-		U facesNum = bs.readUint();
+		U facesNum = file.readU32();
 		tris.resize(facesNum);
 
 		// Faces IDs
@@ -84,7 +81,7 @@ void MeshLoader::load(const char* filename)
 		{
 			for(U j = 0; j < 3; j++)
 			{
-				tri.vertIds[j] = bs.readUint();
+				tri.vertIds[j] = file.readU32();
 
 				// a sanity check
 				if(tri.vertIds[j] >= vertCoords.size())
@@ -95,7 +92,7 @@ void MeshLoader::load(const char* filename)
 		}
 
 		// Tex coords num
-		U texCoordsNum = bs.readUint();
+		U texCoordsNum = file.readU32();
 		texCoords.resize(texCoordsNum);
 
 		// Tex coords
@@ -103,19 +100,19 @@ void MeshLoader::load(const char* filename)
 		{
 			for(uint i = 0; i < 2; i++)
 			{
-				texCoord[i] = bs.readFloat();
+				texCoord[i] = file.readF32();
 			}
 		}
 
 		// Vert weights num
-		U vertWeightsNum = bs.readUint();
+		U vertWeightsNum = file.readU32();
 		vertWeights.resize(vertWeightsNum);
 
 		// Vert weights
 		for(VertexWeight& vw : vertWeights)
 		{
 			// get the bone connections num
-			uint boneConnections = bs.readUint();
+			uint boneConnections = file.readU32();
 
 			// we treat as error if one vert doesnt have a bone
 			if(boneConnections < 1)
@@ -136,11 +133,11 @@ void MeshLoader::load(const char* filename)
 			for(uint i = 0; i < vw.bonesNum; i++)
 			{
 				// read bone id
-				uint boneId = bs.readUint();
+				uint boneId = file.readU32();
 				vw.boneIds[i] = boneId;
 
 				// read the weight of that bone
-				float weight = bs.readFloat();
+				float weight = file.readF32();
 				vw.weights[i] = weight;
 			}
 		} // end for all vert weights

+ 2 - 3
src/resource/Script.cpp

@@ -1,5 +1,4 @@
 #include "anki/resource/Script.h"
-#include "anki/util/Filesystem.h"
 #include "anki/util/Exception.h"
 
 namespace anki {
@@ -7,11 +6,11 @@ namespace anki {
 //==============================================================================
 void Script::load(const char* filename)
 {
-	source = readFile(filename);
+	/*source = readFile(filename);
 	if(source.length() < 1)
 	{
 		throw ANKI_EXCEPTION("Cannot load script: " + filename);
-	}
+	}*/
 }
 
 } // end namespace anki

+ 3 - 2
src/resource/ShaderProgramPrePreprocessor.cpp

@@ -1,7 +1,7 @@
 #include "anki/resource/ShaderProgramPrePreprocessor.h"
-#include "anki/util/Filesystem.h"
 #include "anki/util/Exception.h"
 #include "anki/util/Functions.h"
+#include "anki/util/File.h"
 #include <iomanip>
 #include <cstring>
 #include <iostream>
@@ -51,7 +51,8 @@ void ShaderProgramPrePreprocessor::parseFileForPragmas(
 	}
 
 	// load file in lines
-	StringList lines = readFileLines(filename.c_str());
+	StringList lines;
+	File(filename.c_str(), File::OF_READ).readAllTextLines(lines);
 	if(lines.size() < 1)
 	{
 		throw ANKI_EXCEPTION("File is empty: " + filename);

+ 4 - 3
src/resource/ShaderProgramResource.cpp

@@ -1,7 +1,7 @@
 #include "anki/resource/ShaderProgramResource.h"
 #include "anki/resource/ShaderProgramPrePreprocessor.h"
 #include "anki/core/App.h" // To get cache dir
-#include "anki/util/Filesystem.h"
+#include "anki/util/File.h"
 #include "anki/util/Exception.h"
 #include <fstream>
 #include <sstream>
@@ -96,12 +96,13 @@ std::string ShaderProgramResource::createSrcCodeToCache(
 	std::string newfPathName = AppSingleton::get().getCachePath()
 		+ "/" + suffix + ".glsl";
 
-	if(fileExists(newfPathName.c_str()))
+	if(File::fileExists(newfPathName.c_str()))
 	{
 		return newfPathName;
 	}
 
-	std::string src_ = readFile(sProgFPathName);
+	std::string src_;
+	File(sProgFPathName, File::OF_READ).readAllText(src_);
 	std::string src = preAppendedSrcCode + src_;
 
 	std::ofstream f(newfPathName.c_str());

+ 33 - 29
src/resource/TextureResource.cpp

@@ -15,7 +15,9 @@ void TextureResource::load(const char* filename)
 {
 	try
 	{
-		load(Image(filename));
+		Image img;
+		img.load(filename);
+		load(img);
 	}
 	catch(std::exception& e)
 	{
@@ -27,28 +29,17 @@ void TextureResource::load(const char* filename)
 void TextureResource::load(const Image& img)
 {
 	Initializer init;
-	init.width = img.getWidth();
-	init.height = img.getHeight();
-	init.dataSize = img.getDataSize();
+	init.width = img.getSurface(0, 0).width;
+	init.height = img.getSurface(0, 0).height;
+	init.dataSize = img.getSurface(0, 0).data.size();
 
 	Bool compressionEnabled = 
 		TextureManagerSingleton::get().getCompressionEnabled();
 	(void)compressionEnabled;
 
-	switch(img.getColorType())
+	switch(img.getColorFormat())
 	{
-	case Image::CT_R:
-#if DRIVER_CAN_COMPRESS
-		init.internalFormat = (compressionEnabled) 
-			? GL_COMPRESSED_RED : GL_RED;
-#else
-		init.internalFormat = GL_RED;
-#endif
-		init.format = GL_RED;
-		init.type = GL_UNSIGNED_BYTE;
-		break;
-
-	case Image::CT_RGB:
+	case Image::CF_RGB8:
 #if DRIVER_CAN_COMPRESS
 		init.internalFormat = (compressionEnabled) 
 			? GL_COMPRESSED_RGB : GL_RGB;
@@ -59,7 +50,7 @@ void TextureResource::load(const Image& img)
 		init.type = GL_UNSIGNED_BYTE;
 		break;
 
-	case Image::CT_RGBA:
+	case Image::CF_RGBA8:
 #if DRIVER_CAN_COMPRESS
 		init.internalFormat = (compressionEnabled) 
 			? GL_COMPRESSED_RGBA : GL_RGBA;
@@ -74,30 +65,43 @@ void TextureResource::load(const Image& img)
 		ANKI_ASSERT(0);
 	}
 
-	switch(img.getDataCompression())
+	switch(img.getCompression())
 	{
-	case Image::DC_NONE:
+	case Image::DC_RAW:
 		break;
 
-#if DRIVER_CAN_COMPRESS
+#if ANKI_GL == ANKI_GL_DESKTOP
 	case Image::DC_DXT1:
-		init.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
-		break;
-
-	case Image::DC_DXT3:
-		init.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+		if(img.getColorFormat() == Image::CF_RGB8)
+		{
+			init.internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+		}
+		else
+		{
+			ANKI_ASSERT(0 && "DXT1 should only be RGB");
+		}
 		break;
 
 	case Image::DC_DXT5:
-		init.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+		if(img.getColorFormat() == Image::CF_RGBA8)
+		{
+			init.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+		}
+		else
+		{
+			ANKI_ASSERT(0 && "DXT5 should only be RGB");
+		}
 		break;
 #else
+	case Image::DC_ETC2:
+		ANKI_ASSERT(0 && "ToDo");
+		break;
+#endif
 	default:
 		ANKI_ASSERT(0);
-#endif
 	}
 
-	init.data[0] = img.getData();
+	init.data[0] = &img.getSurface(0, 0).data[0];
 	init.mipmapping = TextureManagerSingleton::get().getMipmappingEnabled();
 	init.filteringType = init.mipmapping ? TFT_TRILINEAR : TFT_LINEAR;
 	init.repeat = true;

+ 0 - 99
src/util/BinaryStream.cpp

@@ -1,99 +0,0 @@
-#include "anki/util/BinaryStream.h"
-#include <cstring>
-#include <string>
-
-namespace anki {
-
-//==============================================================================
-BinaryStream::~BinaryStream()
-{}
-
-//==============================================================================
-template<typename Type>
-Type BinaryStream::read32bitNumber()
-{
-	// Read data
-	uint8_t c[4];
-	read((char*)c, sizeof(c));
-	if(fail())
-	{
-		throw ANKI_EXCEPTION("Failed to read stream");
-	}
-
-	// Copy it
-	ByteOrder mbo = getMachineByteOrder();
-	Type out;
-	if(mbo == byteOrder)
-	{
-		memcpy(&out, c, 4);
-	}
-	else if(mbo == BO_BIG_ENDIAN && byteOrder == BO_LITTLE_ENDIAN)
-	{
-		out = (c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
-	}
-	else
-	{
-		out = ((c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
-	}
-	return out;
-}
-
-//==============================================================================
-uint32_t BinaryStream::readUint()
-{
-	return read32bitNumber<uint32_t>();
-}
-
-//==============================================================================
-float BinaryStream::readFloat()
-{
-	return read32bitNumber<float>();
-}
-
-//==============================================================================
-std::string BinaryStream::readString()
-{
-	uint32_t size;
-	try
-	{
-		size = readUint();
-	}
-	catch(Exception& e)
-	{
-		throw ANKI_EXCEPTION("Failed to read size");
-	}
-
-	const uint32_t buffSize = 1024;
-	if((size + 1) > buffSize)
-	{
-		throw ANKI_EXCEPTION("String bigger than temp buffer");
-	}
-	
-	char buff[buffSize];
-	read(buff, size);
-	
-	if(fail())
-	{
-		throw ANKI_EXCEPTION("Failed to read " 
-			+ std::to_string(size) + " bytes");
-	}
-	buff[size] = '\0';
-
-	return std::string(buff);
-}
-
-//==============================================================================
-BinaryStream::ByteOrder BinaryStream::getMachineByteOrder()
-{
-	int num = 1;
-	if(*(char*)&num == 1)
-	{
-		return BO_LITTLE_ENDIAN;
-	}
-	else
-	{
-		return BO_BIG_ENDIAN;
-	}
-}
-
-} // end namespace

+ 2 - 2
src/util/CMakeLists.txt

@@ -1,7 +1,7 @@
-SET(ANKI_UTIL_SOURCES Assert.cpp BinaryStream.cpp Exception.cpp Functions.cpp StringList.cpp Filesystem.cpp Allocator.cpp Memory.cpp System.cpp)
+SET(ANKI_UTIL_SOURCES Assert.cpp Exception.cpp Functions.cpp StringList.cpp File.cpp Allocator.cpp Memory.cpp System.cpp)
 
 IF(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-	SET(ANKI_UTIL_SOURCES ${ANKI_UTIL_SOURCES} HighRezTimerPosix.cpp FilesystemPosix.cpp)
+	SET(ANKI_UTIL_SOURCES ${ANKI_UTIL_SOURCES} HighRezTimerPosix.cpp FilePosix.cpp)
 ELSE()
 	MESSAGE(FATAL "See file")
 ENDIF()

+ 360 - 0
src/util/File.cpp

@@ -0,0 +1,360 @@
+#include "anki/util/File.h"
+#include "anki/util/Exception.h"
+#include "anki/util/Assert.h"
+#include <fstream>
+#include <cstring>
+#include <cstdarg>
+
+namespace anki {
+
+//==============================================================================
+// File                                                                        =
+//==============================================================================
+
+//==============================================================================
+File::~File()
+{
+	if(file)
+	{
+		if(flags & FT_C)
+		{
+			fclose((FILE*)file);
+		}
+		else if(flags & FT_ZIP)
+		{
+			ANKI_ASSERT(0 && "Not implemented");
+		}
+		else
+		{
+			ANKI_ASSERT(0);
+		}
+	}
+}
+
+//==============================================================================
+File::Endianness File::getMachineEndianness()
+{
+	I32 num = 1;
+	if(*(char*)&num == 1)
+	{
+		return E_LITTLE_ENDIAN;
+	}
+	else
+	{
+		return E_BIG_ENDIAN;
+	}
+}
+
+//==============================================================================
+void File::open(const char* filename, U8 flags_)
+{
+	ANKI_ASSERT(filename);
+	ANKI_ASSERT(file == nullptr && flags == 0);
+
+	ANKI_ASSERT((flags_ & (OF_READ | OF_WRITE | OF_APPEND | OF_BINARY 
+		| E_LITTLE_ENDIAN | E_BIG_ENDIAN)) != 0);
+
+	ANKI_ASSERT((flags_ & OF_READ) != (flags_ & OF_WRITE));
+
+	const char* openMode;
+
+	if(flags_ & OF_READ)
+	{
+		openMode = "r";
+	}
+	else if(flags_ & OF_WRITE)
+	{
+		openMode = "w";
+	}
+	else if(flags_ & OF_APPEND)
+	{
+		openMode = "a+";
+	}
+	else
+	{
+		ANKI_ASSERT(0);
+		openMode = nullptr;
+	}
+
+	// Open
+	file = (FILE*)fopen(filename, openMode);
+	if(!file)
+	{
+		throw ANKI_EXCEPTION("Failed to open file");
+	}
+
+	flags |= flags_ | FT_C;
+
+	// Check endianness
+	if((flags_ & (E_BIG_ENDIAN | E_LITTLE_ENDIAN)) == 0)
+	{
+		// Set the default endianness
+		flags |= getMachineEndianness();
+	}
+	else
+	{
+		ANKI_ASSERT((flags_ & E_BIG_ENDIAN) != (flags_ & E_LITTLE_ENDIAN));
+	}
+}
+
+//==============================================================================
+void File::close()
+{
+	ANKI_ASSERT(file);
+
+	if(flags & FT_C)
+	{
+		fclose((FILE*)file);
+	}
+	else if(flags & FT_ZIP)
+	{
+		ANKI_ASSERT(0 && "Not implemented");
+	}
+	else
+	{
+		ANKI_ASSERT(0);
+	}
+
+	file = nullptr;
+	flags = 0;
+}
+
+//==============================================================================
+void File::read(void* buff, PtrSize size)
+{
+	ANKI_ASSERT(buff);
+	ANKI_ASSERT(size > 0);
+	ANKI_ASSERT(file);
+	ANKI_ASSERT(flags & OF_READ);
+
+	PtrSize readSize = 0;
+
+	if(flags & FT_C)
+	{
+		readSize = fread(buff, 1, size, (FILE*)file);
+	}
+	else if(flags & FT_ZIP)
+	{
+		ANKI_ASSERT(0 && "Not implemented");
+	}
+	else
+	{
+		ANKI_ASSERT(0);
+	}
+
+	if(size != readSize)
+	{
+		throw ANKI_EXCEPTION("File read failed");
+	}
+}
+
+//==============================================================================
+void File::readAllText(std::string& txt)
+{
+	ANKI_ASSERT(file);
+	ANKI_ASSERT(flags);
+	ANKI_ASSERT((flags & OF_BINARY) == 0 && "Should not be binary file");
+	ANKI_ASSERT(flags & OF_READ);
+
+	if(flags & FT_C)
+	{
+		// Get file size
+		fseek((FILE*)file, 0, SEEK_END);
+		PtrSize size = ftell((FILE*)file);
+		rewind((FILE*)file);
+
+		// Read and write
+		txt.resize(size);
+		PtrSize readSize = fread(&txt[0], 1, size, (FILE*)file);
+		(void)readSize;
+		ANKI_ASSERT(readSize == size);
+	}
+	else if(flags & FT_ZIP)
+	{
+		ANKI_ASSERT(0 && "Not implemented");
+	}
+	else
+	{
+		ANKI_ASSERT(0);
+	}
+}
+
+//==============================================================================
+void File::readAllTextLines(StringList& lines)
+{
+	ANKI_ASSERT(file);
+	ANKI_ASSERT(flags);
+	ANKI_ASSERT((flags & OF_BINARY) == 0 && "Should not be binary file");
+	ANKI_ASSERT(flags & OF_READ);
+
+	if(flags & FT_C)
+	{
+		char line[1024];
+
+		while(fgets(line, sizeof(line), (FILE*)file))
+		{
+			I len = strlen(line);
+
+			if(len != sizeof(line) - 1)
+			{
+				// line is big enough
+
+				line[len - 1] = '\0';
+				lines.push_back(line);
+			}
+			else
+			{
+				throw ANKI_EXCEPTION("Line bigger than temp buffer");
+			}			
+		}
+	}
+	else if(flags & FT_ZIP)
+	{
+		ANKI_ASSERT(0 && "Not implemented");
+	}
+	else
+	{
+		ANKI_ASSERT(0);
+	}
+}
+
+//==============================================================================
+U32 File::readU32()
+{
+	ANKI_ASSERT(file);
+	ANKI_ASSERT(flags);
+	ANKI_ASSERT(flags & OF_READ);
+	ANKI_ASSERT((flags & OF_BINARY) && "Should be binary file");
+	ANKI_ASSERT((flags & E_BIG_ENDIAN) != (flags & E_LITTLE_ENDIAN) 
+		&& "One of those 2 should be active");
+
+	U32 out;
+	read(&out, sizeof(out));
+	
+	// Copy it
+	Endianness machineEndianness = getMachineEndianness();
+	Endianness fileEndianness = (flags & E_BIG_ENDIAN) 
+		? E_BIG_ENDIAN : E_LITTLE_ENDIAN;
+
+	if(machineEndianness == fileEndianness)
+	{
+		// Same endianness between the file and the machine. Do nothing
+	}
+	else if(machineEndianness == E_BIG_ENDIAN 
+		&& fileEndianness == E_LITTLE_ENDIAN)
+	{
+		U8* c = (U8*)&out;
+		out = (c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
+	}
+	else
+	{
+		U8* c = (U8*)&out;
+		out = ((c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
+	}
+
+	return out;
+}
+
+//==============================================================================
+F32 File::readF32()
+{
+	F32 out;
+	U32 integer = readU32();
+
+	memcpy(&out, &integer, sizeof(F32));
+
+	return out;
+}
+
+//==============================================================================
+void File::write(void* buff, PtrSize size)
+{
+	ANKI_ASSERT(buff);
+	ANKI_ASSERT(size > 0);
+	ANKI_ASSERT(file);
+	ANKI_ASSERT(flags & OF_WRITE);
+
+	if(flags & FT_C)
+	{
+		PtrSize writeSize = 0;
+		writeSize = fwrite(buff, 1, size, (FILE*)file);
+
+		if(writeSize != size)
+		{
+			throw ANKI_EXCEPTION("Failed to write on file");
+		}
+	}
+	else if(flags & FT_ZIP)
+	{
+		ANKI_ASSERT(0 && "Not implemented");
+	}
+	else
+	{
+		ANKI_ASSERT(0);
+	}
+}
+
+//==============================================================================
+void File::writeText(const char* format, ...)
+{
+	ANKI_ASSERT(format);
+	ANKI_ASSERT(file);
+	ANKI_ASSERT(flags);
+	ANKI_ASSERT(flags & OF_WRITE);
+	ANKI_ASSERT((flags & OF_BINARY) == 0);
+
+	va_list args;
+	va_start(args, format);
+
+	if(flags & FT_C)
+	{
+		vfprintf((FILE*)file, format, args);
+	}
+	else if(flags & FT_ZIP)
+	{
+		ANKI_ASSERT(0 && "Not implemented");
+	}
+	else
+	{
+		ANKI_ASSERT(0);
+	}
+
+	va_end(args);
+}
+
+//==============================================================================
+void File::seek(PtrSize offset, SeekOrigin origin)
+{
+	ANKI_ASSERT(file);
+	ANKI_ASSERT(flags);
+
+	if(flags & FT_C)
+	{
+		fseek((FILE*)file, offset, origin);
+	}
+	else if(flags & FT_ZIP)
+	{
+		ANKI_ASSERT(0 && "Not implemented");
+	}
+	else
+	{
+		ANKI_ASSERT(0);
+	}
+}
+
+//==============================================================================
+const char* File::getFileExtension(const char* filename)
+{
+	ANKI_ASSERT(filename);
+	const char* pc = strrchr(filename, '.');
+
+	if(pc == nullptr)
+	{
+		return nullptr;
+	}
+
+	++pc;
+	return (*pc == '\0') ? nullptr : pc;
+}
+
+} // end namespace anki

+ 14 - 14
src/util/FilesystemPosix.cpp → src/util/FilePosix.cpp

@@ -1,4 +1,4 @@
-#include "anki/util/Filesystem.h"
+#include "anki/util/File.h"
 #include "anki/util/Exception.h"
 #include "anki/util/Assert.h"
 #include <fstream>
@@ -11,7 +11,11 @@
 namespace anki {
 
 //==============================================================================
-bool fileExists(const char* filename)
+// File                                                                        =
+//==============================================================================
+
+//==============================================================================
+Bool File::fileExists(const char* filename)
 {
 	ANKI_ASSERT(filename);
 	struct stat s;
@@ -26,7 +30,11 @@ bool fileExists(const char* filename)
 }
 
 //==============================================================================
-bool directoryExists(const char* filename)
+// Functions                                                                   =
+//==============================================================================
+
+//==============================================================================
+Bool directoryExists(const char* filename)
 {
 	ANKI_ASSERT(filename);
 	struct stat s;
@@ -41,10 +49,9 @@ bool directoryExists(const char* filename)
 }
 
 //==============================================================================
-static int rmDir(const char* fpath, const struct stat* sb, int /*typeflag*/,
-	struct FTW* /*ftwbuf*/)
+static int rmDirCallback(const char* fpath, const struct stat* /*sb*/, 
+	int /*typeflag*/, struct FTW* /*ftwbuf*/)
 {
-	(void)sb;
 	int rv = remove(fpath);
 
 	if(rv)
@@ -57,7 +64,7 @@ static int rmDir(const char* fpath, const struct stat* sb, int /*typeflag*/,
 
 void removeDirectory(const char* dir)
 {
-	if(nftw(dir, rmDir, 64, FTW_DEPTH | FTW_PHYS))
+	if(nftw(dir, rmDirCallback, 64, FTW_DEPTH | FTW_PHYS))
 	{
 		throw ANKI_EXCEPTION(strerror(errno) + ": " + dir);
 	}
@@ -77,11 +84,4 @@ void createDirectory(const char* dir)
 	}
 }
 
-//==============================================================================
-void toNativePath(const char* path)
-{
-	ANKI_ASSERT(path);
-	(void)path;
-}
-
 } // end namespace anki

+ 0 - 242
src/util/Filesystem.cpp

@@ -1,242 +0,0 @@
-#include "anki/util/Filesystem.h"
-#include "anki/util/Exception.h"
-#include "anki/util/Assert.h"
-#include <fstream>
-#include <cstring>
-#include <cstdarg>
-
-namespace anki {
-
-//==============================================================================
-// File                                                                        =
-//==============================================================================
-
-//==============================================================================
-File::~File()
-{
-	if(file)
-	{
-		switch(fileType)
-		{
-		case FT_C:
-			fclose((FILE*)file);
-			break;
-		case FT_ZIP:
-			ANKI_ASSERT(0 && "Not implemented");
-			break;
-		default:
-			ANKI_ASSERT(0);
-		}
-	}
-}
-
-//==============================================================================
-void File::open(const char* filename, U8 flags)
-{
-	const char* openMode;
-
-	if(flags & OF_READ)
-	{
-		openMode = "r";
-	}
-	else if(flags & OF_WRITE)
-	{
-		openMode = "w";
-	}
-	else if(flags & OF_APPEND)
-	{
-		openMode = "a+";
-	}
-	else
-	{
-		ANKI_ASSERT(0);
-		openMode = nullptr;
-	}
-	openFlags = flags;
-
-	// Open
-	file = (FILE*)fopen(filename, openMode);
-	if(!file)
-	{
-		throw ANKI_EXCEPTION("Failed to open file");
-	}
-	fileType = FT_C;
-}
-
-//==============================================================================
-void File::close()
-{
-	ANKI_ASSERT(file);
-
-	switch(fileType)
-	{
-	case FT_C:
-		fclose((FILE*)file);
-		break;
-	case FT_ZIP:
-		ANKI_ASSERT(0 && "Not implemented");
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-
-	file = nullptr;
-}
-
-//==============================================================================
-PtrSize File::read(void* buff, PtrSize size)
-{
-	ANKI_ASSERT(file);
-	ANKI_ASSERT(buff);
-	ANKI_ASSERT(size > 0);
-	ANKI_ASSERT(openFlags & OF_READ);
-
-	PtrSize readSize = 0;
-
-	switch(fileType)
-	{
-	case FT_C:
-		readSize = fread(buff, 1, size, (FILE*)file);
-		break;
-	case FT_ZIP:
-		ANKI_ASSERT(0 && "Not implemented");
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-
-	return readSize;
-}
-
-//==============================================================================
-void File::readAll(std::string& txt)
-{
-	ANKI_ASSERT(file);
-	ANKI_ASSERT((openFlags & OF_BINARY) == false 
-		&& "Should not be binary file");
-	ANKI_ASSERT(openFlags & OF_READ);
-
-	switch(fileType)
-	{
-	case FT_C:
-		{
-			fseek((FILE*)file, 0, SEEK_END);
-			PtrSize size = ftell((FILE*)file);
-			rewind((FILE*)file);
-
-			txt.resize(size);
-			PtrSize readSize = fread(&txt[0], 1, size, (FILE*)file);
-			(void)readSize;
-			ANKI_ASSERT(readSize == size);
-			break;
-		}
-	case FT_ZIP:
-		ANKI_ASSERT(0 && "Not implemented");
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-}
-
-//==============================================================================
-PtrSize File::write(void* buff, PtrSize size)
-{
-	ANKI_ASSERT(file);
-	ANKI_ASSERT(buff);
-	ANKI_ASSERT(size > 0);
-	ANKI_ASSERT(openFlags & OF_WRITE);
-
-	PtrSize writeSize = 0;
-
-	switch(fileType)
-	{
-	case FT_C:
-		writeSize = fwrite(buff, 1, size, (FILE*)file);
-		break;
-	case FT_ZIP:
-		ANKI_ASSERT(0 && "Not implemented");
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-
-	return writeSize;
-}
-
-//==============================================================================
-void File::writeString(const char* format, ...)
-{
-	ANKI_ASSERT(file);
-	ANKI_ASSERT(format);
-	ANKI_ASSERT(openFlags & OF_WRITE);
-
-	va_list args;
-	va_start(args, format);
-
-	switch(fileType)
-	{
-	case FT_C:
-		vfprintf((FILE*)file, format, args);
-		break;
-	case FT_ZIP:
-		ANKI_ASSERT(0 && "Not implemented");
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-
-	va_end(args);
-}
-
-//==============================================================================
-// Functions                                                                   =
-//==============================================================================
-
-//==============================================================================
-const char* getFileExtension(const char* filename)
-{
-	ANKI_ASSERT(filename);
-	const char* pc = strrchr(filename, '.');
-
-	if(pc == nullptr)
-	{
-		return nullptr;
-	}
-
-	++pc;
-	return (*pc == '\0') ? nullptr : pc;
-}
-
-//==============================================================================
-std::string readFile(const char* filename)
-{
-	std::ifstream file(filename);
-	if (!file.is_open())
-	{
-		throw ANKI_EXCEPTION("Cannot open file: " + filename);
-	}
-
-	return std::string((std::istreambuf_iterator<char>(file)),
-		std::istreambuf_iterator<char>());
-}
-
-//==============================================================================
-StringList readFileLines(const char* filename)
-{
-	ANKI_ASSERT(filename);
-	std::ifstream ifs(filename);
-	if(!ifs.is_open())
-	{
-		throw ANKI_EXCEPTION("Cannot open file: " + filename);
-	}
-
-	StringList lines;
-	std::string temp;
-	while(getline(ifs, temp))
-	{
-		lines.push_back(temp);
-	}
-	return lines;
-}
-
-} // end namespace anki

+ 1 - 1
testapp/Main.cpp

@@ -18,7 +18,7 @@
 #include "anki/core/StdinListener.h"
 #include "anki/resource/Model.h"
 #include "anki/core/Logger.h"
-#include "anki/util/Util.h"
+#include "anki/Util.h"
 #include "anki/resource/Skin.h"
 #include "anki/event/EventManager.h"
 #include "anki/event/MainRendererPpsHdrEvent.h"

+ 528 - 0
tools/texture/ankitexture.py

@@ -0,0 +1,528 @@
+#!/usr/bin/python
+
+import optparse
+import subprocess
+import re
+import os
+import struct
+import copy
+
+#
+# AnKi texture
+#
+
+TYPE_NONE = 0
+TYPE_2D = 1
+TYPE_CUBE = 2
+TYPE_3D = 3
+TYPE_2D_ARRAY = 4
+
+COLOR_FORMAT_NONE = 0
+COLOR_FORMAT_RGB8 = 1
+COLOR_FORMAT_RGBA8 = 2
+
+COMPRESSION_NONE = 0
+COMPRESSION_RAW = 1 << 0
+COMPRESSION_ETC2 = 1 << 1
+COMPRESSION_S3TC = 1 << 2
+
+#
+# DDS
+#
+
+# dwFlags of DDSURFACEDESC2
+DDSD_CAPS           = 0x00000001
+DDSD_HEIGHT         = 0x00000002
+DDSD_WIDTH          = 0x00000004
+DDSD_PITCH          = 0x00000008
+DDSD_PIXELFORMAT    = 0x00001000
+DDSD_MIPMAPCOUNT    = 0x00020000
+DDSD_LINEARSIZE     = 0x00080000
+DDSD_DEPTH          = 0x00800000
+
+# ddpfPixelFormat of DDSURFACEDESC2
+DDPF_ALPHAPIXELS    = 0x00000001
+DDPF_FOURCC         = 0x00000004
+DDPF_RGB            = 0x00000040
+
+# dwCaps1 of DDSCAPS2
+DDSCAPS_COMPLEX     = 0x00000008
+DDSCAPS_TEXTURE     = 0x00001000
+DDSCAPS_MIPMAP      = 0x00400000
+
+# dwCaps2 of DDSCAPS2
+DDSCAPS2_CUBEMAP            = 0x00000200
+DDSCAPS2_CUBEMAP_POSITIVEX  = 0x00000400
+DDSCAPS2_CUBEMAP_NEGATIVEX  = 0x00000800
+DDSCAPS2_CUBEMAP_POSITIVEY  = 0x00001000
+DDSCAPS2_CUBEMAP_NEGATIVEY  = 0x00002000
+DDSCAPS2_CUBEMAP_POSITIVEZ  = 0x00004000
+DDSCAPS2_CUBEMAP_NEGATIVEZ  = 0x00008000
+DDSCAPS2_VOLUME             = 0x00200000
+
+class DdsHeader:
+	""" The header of a dds file """
+
+	_fields = [
+		('dwMagic', '4s'),
+		('dwSize', 'I'),
+		('dwFlags', 'I'),
+		('dwHeight', 'I'),
+		('dwWidth', 'I'),
+		('dwPitchOrLinearSize', 'I'),
+		('dwDepth', 'I'),
+		('dwMipMapCount', 'I'),
+		('dwReserved1', '44s'),
+	
+		# Pixel format
+		('dwSize', 'I'),
+		('dwFlags', 'I'),
+		('dwFourCC', '4s'),
+		('dwRGBBitCount', 'I'),
+		('dwRBitMask', 'I'),
+		('dwGBitMask', 'I'),
+		('dwBBitMask', 'I'),
+		('dwRGBAlphaBitMask', 'I'),
+
+		('dwCaps1', 'I'),
+		('dwCaps2', 'I'),
+		('dwCapsReserved', '8s'),
+		('dwReserved2', 'I')]
+
+	def __init__(self, buff):
+		buff_format = self.get_format()
+		items = struct.unpack(buff_format, buff)
+		for field, value in map(None, self._fields, items):
+			setattr(self, field[0], value)
+
+	@classmethod
+	def get_format(cls):
+		return '<' + ''.join([f[1] for f in cls._fields])
+
+	@classmethod
+	def get_size(cls):
+		return struct.calcsize(cls.get_format())
+
+#
+# ETC2
+#
+	
+class PkmHeader:
+	""" The header of a pkm file """
+	
+	_fields = [
+		("magic", "6s"),
+		("type", "H"),
+		("width", "H"),
+		("height", "H"),
+		("origWidth", "H"),
+		("origHeight", "H")]
+
+	def __init__(self, buff):
+		buff_format = self.get_format()
+		items = struct.unpack(buff_format, buff)
+		for field, value in map(None, self._fields, items):
+			setattr(self, field[0], value)
+
+	@classmethod
+	def get_format(cls):
+		return ">" + "".join([f[1] for f in cls._fields])
+
+	@classmethod
+	def get_size(cls):
+		return struct.calcsize(cls.get_format())
+	
+#
+# Functions
+# 
+
+def is_power2(num):
+	""" Returns true if a number is a power of two """
+	return num != 0 and ((num & (num - 1)) == 0)
+
+def get_base_fname(path):
+	""" From path/to/a/file.ext return the "file" """
+	return os.path.splitext(os.path.basename(path))[0]
+
+def parse_commandline():
+	""" Parse the command line arguments """
+
+	parser = optparse.OptionParser("usage: %prog [options]")
+
+	parser.add_option("-i", "--input", dest = "inp",
+		type = "string", help = "specify the image(s) to convert. " \
+			"Seperate with :")
+	
+	parser.add_option("-d", "--tmp-dir", dest = "tmp_dir",
+		type = "string", default = False, 
+		help = "temporary directory")
+
+	parser.add_option("-t", "--type", dest = "type",
+		type = "string", default = "2D", 
+		help = "type of the image (2D or cube or 3D or 2DArray)")
+
+	parser.add_option("-f", "--fast", dest = "fast",
+		action = "store_true", default = False, 
+		help = "run the fast version of the converters")
+
+	parser.add_option("-n", "--normal", dest = "normal",
+		action = "store_true", default = False, 
+		help = "assume the texture is normal")
+
+	(options, args) = parser.parse_args()
+
+	if not options.inp or not options.tmp_dir:
+		parser.error("argument is missing")
+
+	if options.type == "2D":
+		typ = TYPE_2D
+	elif options.type == "cube":
+		typ = TYPE_CUBE
+	elif options.type == "3D":
+		typ = TYPE_3D
+	elif options.type == "2DArray":
+		typ = TYPE_2D_ARRAY
+	else:
+		parser.error("Unrecognized type: " + options.type)
+
+	return (options.inp.split(":"), options.tmp_dir, options.fast,
+			typ, options.normal)
+
+def get_internal_format_and_size(in_file):
+	""" Return the size of the input image and the internal format """
+
+	internal_format = COLOR_FORMAT_NONE
+	width = 0
+	height = 0
+
+	proc = subprocess.Popen(["identify", "-verbose" , in_file],
+			stdout=subprocess.PIPE)
+
+	stdout_str = proc.stdout.read()
+
+	reg = re.search(r"Colorspace: (.*)", stdout_str)
+	if not reg:
+		raise Exception("Cannot extract colorspace")
+
+	print("-- Colorspace: %s" % reg.group(1))
+	if reg.group(1) == "RGB":
+		internal_format = COLOR_FORMAT_RGB8
+	elif reg.group(1) == "RGBA":
+		internal_format = COLOR_FORMAT_RGBA8
+	else:
+		raise Exception("Unrecognized color format")
+
+	reg = re.search(r"Geometry: ([0-9]*)x([0-9]*)\+", stdout_str)
+
+	if not reg:
+		raise Exception("Cannot extract size")
+
+	print("-- width: %s, height: %s" % (reg.group(1), reg.group(2)))
+
+	return (internal_format, int(reg.group(1)), int(reg.group(2)))
+
+def create_mipmaps(in_file, tmp_dir, orig_size, internal_format):
+	""" Create a number of images for all mipmaps """
+
+	print("-- Generate mipmaps")
+
+	size = orig_size
+
+	mips_fnames = []
+
+	while size >= 4:
+		size_str = "%dx%d" % (size, size)
+		out_file_str = os.path.join(tmp_dir, get_base_fname(in_file)) \
+				+ "." + str(size)
+
+		print("  -- %s.(tga & ppm)" % out_file_str)
+
+		mips_fnames.append(out_file_str)
+
+		args = ["convert", in_file, "-resize", size_str, "-alpha"]
+
+		if internal_format == COLOR_FORMAT_RGB8:
+			args.append("deactivate")
+		else:
+			args.append("activate")
+
+		# ppm
+		args_ppm = copy.deepcopy(args)
+		args_ppm.append(out_file_str + ".ppm")
+		subprocess.check_call(args_ppm)
+
+		# png
+		args_tga = copy.deepcopy(args)
+		args_tga.append(out_file_str + ".tga")
+		subprocess.check_call(args_tga)
+
+		size = size / 2
+
+	return mips_fnames
+
+def create_etc_images(mips_fnames, tmp_dir, fast, internal_format):
+	""" Create the etc files """
+
+	print("-- Creating ETC images")
+
+	for fname in mips_fnames:
+		fname = fname + ".ppm"
+
+		print("  -- %s" % fname)
+
+		args = ["etcpack", fname, tmp_dir, "-c", "etc2"]
+
+		if fast:
+			args.append("-s")
+			args.append("fast")
+
+		args.append("-f")
+		if internal_format == COLOR_FORMAT_RGB8:
+			args.append("RGB")
+		else:
+			args.append("RGBA")
+
+		subprocess.check_call(args, stdout = subprocess.PIPE)
+
+def create_dds_images(mips_fnames, tmp_dir, fast, internal_format, \
+		normal):
+	""" Create the dds files """
+
+	print("-- Creating DDS images")
+
+	for fname in mips_fnames:
+		in_fname = fname + ".tga"
+		out_fname = os.path.join(tmp_dir, os.path.basename(fname) + ".dds")
+
+		print("  -- %s" % out_fname)
+
+		args = ["nvcompress", "-silent", "-nomips"]
+
+		if fast:
+			args.append("-fast")
+
+		if internal_format == COLOR_FORMAT_RGB8:
+			if not normal:
+				args.append("-bc1")
+			else:
+				args.append("-bc1n")
+		elif internal_format == COLOR_FORMAT_RGBA8:
+			if not normal:
+				args.append("-bc3")
+			else:
+				args.append("-bc3n")
+
+		args.append(in_fname)
+		args.append(out_fname)
+
+		subprocess.check_call(args, stdout = subprocess.PIPE)
+
+def write_raw(tex_file, fname, width, height, internal_format):
+	""" Append raw data to the AnKi texture file """
+
+	# Read and check the header
+	uncompressed_tga_header = struct.pack("BBBBBBBBBBBB", \
+			0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+
+	in_file = open(fname, "rb")
+	tga_header = in_file.read(12)
+	
+	if uncompressed_tga_header != tga_header:
+		raise Exception("Incorrect TGA header")
+
+	# Read the size and bpp
+	header6_buff = in_file.read(6)
+	header6 = struct.unpack("BBBBBB", header6_buff)
+
+	img_width = header6[1] * 256 + header6[0]
+	img_height = header6[3] * 256 + header6[2]
+	img_bpp = header6[4];
+
+	if (internal_format != COLOR_FORMAT_RGB8 or img_bpp != 24) \
+			and (internal_format != COLOR_FORMAT_RGBA8 or img_bpp != 32):
+		raise Exception("Unexpected bpp")
+		
+	if img_width != width or img_height != height:
+		raise Exception("Unexpected width or height")
+
+	# Read the data
+	data_size = width * height
+	if internal_format == COLOR_FORMAT_RGB8:
+		data_size *= 3
+	else:
+		data_size *= 4
+
+	data = bytearray(in_file.read(data_size))
+
+	if len(data) != data_size:
+		raise Exception("Failed to read all data")
+
+	# Swap colors
+	bpp = img_bpp / 8
+	for i in xrange(0, data_size, bpp):
+		temp = data[i];
+		data[i] = data[i + 2];
+		data[i + 2] = temp;
+
+	# Write data to tex_file
+	tex_file.write(data)
+
+def write_s3tc(out_file, fname, width, height, internal_format):
+	""" Append s3tc data to the AnKi texture file """
+
+	# Read header
+	in_file = open(fname, "rb")
+
+	dds_header = DdsHeader(in_file.read(DdsHeader.get_size()))
+
+	if dds_header.dwWidth != width or dds_header.dwHeight != height:
+		raise Exception("Incorrect width")
+
+	if internal_format == COLOR_FORMAT_RGB8 \
+			and dds_header.dwFourCC != "DXT1":
+		raise Exception("Incorrect format. Expecting DXT1")
+
+	if internal_format == COLOR_FORMAT_RGBA8 \
+			and dds_header.dwFourCC != "DXT5":
+		raise Exception("Incorrect format. Expecting DXT5")
+
+	# Read and write the data
+	if internal_format == COLOR_FORMAT_RGB8:
+		block_size = 4
+	else:
+		block_size = 8
+
+	data_size = ((width + 3) / 4) * ((height + 3) / 4) * block_size
+
+	data = in_file.read(data_size)
+	out_file.write(data)
+
+def write_etc(out_file, fname, width, height, internal_format):
+	""" Append etc2 data to the AnKi texture file """
+	
+	# Read header
+	in_file = open(fname, "rb")
+
+	pkm_header = PkmHeader(in_file.read(PkmHeader.get_size()))
+
+	if pkm_header.magic != "PKM 20":
+		raise Exception("Incorrect PKM header")
+
+	if width != pkm_header.width or height != pkm_header.height:
+		raise Exception("Incorrect PKM width or height")
+
+	# Read and write the data
+	data_size = (pkm_header.width / 4) * (pkm_header.height / 4) * 8
+
+	data = in_file.read(data_size)
+	out_file.write(data)
+
+def main():
+	""" The main """
+
+	# Parse cmd line args
+	(in_files, tmp_dir, fast, typ, normal) = parse_commandline();
+	tmp_dir = os.path.abspath(tmp_dir)
+
+	if typ == TYPE_CUBE and len(in_files) != 6:
+		raise Exception("Not enough images for cube generation")
+
+	if (typ == TYPE_3D or typ == TYPE_2D_ARRAY) and len(in_files) < 2:
+		raise Exception("Not enough images for 2DArray/3D texture")
+
+	if typ == TYPE_2D and len(in_files) != 1:
+		raise Exception("Only one image for 2D textures needed")
+
+	# Invoke app named "identify" to get internal format and width and height
+	(internal_format, width, height) = get_internal_format_and_size(in_files[0])
+
+	if width != height:
+		raise Exception("Only square images are accepted")
+
+	if not is_power2(width):
+		raise Exception("Image should power of 2")
+
+	if internal_format == COLOR_FORMAT_RGBA8 and normal:
+		raise Exception("RGBA image and normal does not make much sense")
+
+	for i in range(1, len(in_files)):
+		(internal_format_2, width_2, height_2) = \
+				get_internal_format_and_size(in_files[i])
+
+		if width != width_2 or height != height_2 \
+				or internal_format != internal_format_2:
+			raise Exception("Images are not same size and color space")
+
+	# Create images
+	for in_file in in_files:
+		mips_fnames = create_mipmaps(in_file, tmp_dir, width, internal_format)
+
+		# Create etc images
+		create_etc_images(mips_fnames, tmp_dir, fast, internal_format)
+
+		# Create dds images
+		create_dds_images(mips_fnames, tmp_dir, fast, internal_format, normal)
+
+	# Open file
+	fname = os.path.splitext(os.path.basename(in_file))[0] + ".ankitex"
+	fname = os.path.join(tmp_dir,  fname)
+	tex_file = open(fname, "wb")
+
+	# Write header
+	ak_format = "8sIIIIIIII"
+
+	buff = struct.pack(ak_format, 
+			b"ANKITEX1",
+			width,
+			height,
+			len(in_files),
+			typ,
+			internal_format,
+			COMPRESSION_RAW | COMPRESSION_S3TC | COMPRESSION_ETC2,
+			normal,
+			len(mips_fnames))
+
+	tex_file.write(buff)
+
+	# Write header padding
+	header_padding_size = 128 - struct.calcsize(ak_format)
+
+	if header_padding_size != 88:
+		raise Exception("Check the header")
+
+	for i in range(0, header_padding_size):
+		tex_file.write('\0')
+
+	# For each compression
+	for compression in range(0, 3):
+		# For each image
+		for in_file in in_files:
+			size = width
+
+			# For each level
+			while size >= 4:
+				in_base_fname = os.path.join(tmp_dir, get_base_fname(in_file)) \
+					+ "." + str(size)
+
+				# Write RAW
+				if compression == 0:
+					write_raw(tex_file, in_base_fname + ".tga", size, size, \
+							internal_format)
+
+				# Write S3TC
+				if compression == 1:
+					write_s3tc(tex_file, in_base_fname + ".dds", size, size, \
+							internal_format)
+
+				# Write ETC
+				if compression == 2:
+					write_etc(tex_file, in_base_fname + ".pkm", size, size, \
+							internal_format)
+				
+				size = size / 2
+		
+	# Done
+	print("-- Done!")
+
+if __name__ == "__main__":
+	main()