瀏覽代碼

Exporters and other

Panagiotis Christopoulos Charitos 12 年之前
父節點
當前提交
c02ad6f030

+ 19 - 10
CMakeLists.txt

@@ -16,7 +16,15 @@ OPTION(ANKI_STRIP "Srip the symbols from the executables" OFF)
 
 # Because we want different compiler flags for each platform
 SET(ANKI_CPU "X86" CACHE STRING "The CPU arch (X86 or ARM)")
-SET(ANKI_CPU_ADDR_SPACE "0" CACHE STRING "The CPU architecture (0 or 32 or 64). If zero go native")
+
+# Take a wild guess on CPU address space
+IF(${ANKI_CPU} STREQUAL "ARM")
+	SET(_ADDR_SPACE "32")
+ELSE()
+	SET(_ADDR_SPACE "0")
+ENDIF()
+
+SET(ANKI_CPU_ADDR_SPACE "${_ADDR_SPACE}" CACHE STRING "The CPU architecture (0 or 32 or 64). If zero go native")
 OPTION(ANKI_ENABLE_MATH_SIMD "Enable or not math SIMD optimizations" ON)
 
 # libz and libpng
@@ -37,7 +45,7 @@ SET(ANKI_WINDOW_BACKEND "${_WIN_BACKEND}" CACHE STRING "The window backend (GLXX
 #
 
 # CPU
-MESSAGE("++ AnKi CPU: ${ANKI_CPU}")
+MESSAGE("++ Build for CPU: ${ANKI_CPU}")
 
 IF(ANKI_CPU STREQUAL "X86")
 	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4 ")
@@ -53,17 +61,18 @@ IF(ANKI_ENABLE_MATH_SIMD)
 	ELSE()
 		MESSAGE(FATAL "Wrong ANKI_CPU set")
 	ENDIF()
+
+	MESSAGE("++ SIMD: true")
 ELSE()
 	SET(ANKI_MATH_SIMD "NONE")
+	MESSAGE("++ SIMD: false")
 ENDIF()
 
-MESSAGE("++ AnKi SIMD: ${ANKI_MATH_SIMD}")
-
 # CPU address space
 IF(ANKI_CPU_ADDR_SPACE STREQUAL "0")
-	MESSAGE("++ AnKi CPU address space: 0 (Native)")
+	MESSAGE("++ CPU address space: 0 (Native)")
 ELSE()
-	MESSAGE("++ AnKi CPU address space: ${ANKI_CPU_ADDR_SPACE}")
+	MESSAGE("++ CPU address space: ${ANKI_CPU_ADDR_SPACE}")
 ENDIF()
 
 IF(NOT ANKI_CPU_ADDR_SPACE STREQUAL "0")
@@ -78,7 +87,7 @@ IF(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
 ELSEIF(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
 	SET(ANKI_PLATFORM "WINDOWS")
 ENDIF()
-MESSAGE("++ AnKi platform: ${ANKI_PLATFORM}")
+MESSAGE("++ Platform: ${ANKI_PLATFORM}")
 
 # libz and libpng
 IF(ANKI_SYSTEM_LIBZ)
@@ -98,7 +107,7 @@ ELSE()
 ENDIF()
 
 # Window backend
-MESSAGE("++ AnKi window backend: ${ANKI_WINDOW_BACKEND}")
+MESSAGE("++ Window backend: ${ANKI_WINDOW_BACKEND}")
 
 #
 # Common compiler flags
@@ -184,9 +193,9 @@ SET(ANKI_VERSION_MINOR 1)
 MESSAGE("++ AnKi version: ${ANKI_VERSION_MAJOR}.${ANKI_VERSION_MINOR}")
 
 IF(CMAKE_BUILD_TYPE STREQUAL Debug)
-	MESSAGE("++ AnKi debug: true")
+	MESSAGE("++ Debug build: true")
 ELSE()
-	MESSAGE("++ AnKi debug: false")
+	MESSAGE("++ Debug build: false")
 ENDIF()
 
 SET(ANKI_GCC_TO_STRING_WORKAROUND "0" CACHE STRING "Enable workaround for C++11 GCC bug (0 or 1)")

+ 11 - 3
include/anki/gl/Texture.h

@@ -2,12 +2,14 @@
 #define ANKI_GL_TEXTURE_H
 
 #include "anki/util/Assert.h"
+#include "anki/util/Array.h"
 #include "anki/util/Singleton.h"
 #include "anki/util/Vector.h"
 #include "anki/util/StdTypes.h"
 #include "anki/util/NonCopyable.h"
 #include "anki/gl/Ogl.h"
 #include <cstdlib>
+#include <cstring>
 #include <limits>
 #include <thread>
 
@@ -147,8 +149,9 @@ class Texture: public NonCopyable
 	friend class TextureManager;
 
 public:
-	/// Texture filtering type
-	enum TextureFilteringType
+	// A fake enum: Texture filtering type
+	typedef U8 TextureFilteringType;
+	enum
 	{
 		TFT_NEAREST,
 		TFT_LINEAR,
@@ -166,12 +169,17 @@ public:
 		GLenum format = GL_NONE;
 		/// The type of the data. Not relevant if data is zero
 		GLenum type = GL_NONE; 
-		const void* data = nullptr;
+		Array<const void*, 256> data; ///< Data per layer/face
 		Bool mipmapping = false;
 		TextureFilteringType filteringType = TFT_NEAREST;
 		Bool repeat = true;
 		I anisotropyLevel = 0;
 		PtrSize dataSize = 0; ///< For compressed textures
+
+		Initializer()
+		{
+			memset(&data[0], 0, sizeof(data));
+		}
 	};
 
 	/// @name Constructors/Destructor

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

@@ -48,12 +48,12 @@ public:
 
 	/// @name Accessors
 	/// @{
-	uint32_t getWidth() const
+	U32 getWidth() const
 	{
 		return width;
 	}
 
-	uint32_t getHeight() const
+	U32 getHeight() const
 	{
 		return height;
 	}
@@ -63,7 +63,7 @@ public:
 		return type;
 	}
 
-	const uint8_t* getData() const
+	const U8* getData() const
 	{
 		return &data[0];
 	}
@@ -86,16 +86,16 @@ public:
 	void load(const char* filename);
 
 private:
-	uint32_t width = 0; ///< Image width
-	uint32_t height = 0; ///< Image height
+	U32 width = 0; ///< Image width
+	U32 height = 0; ///< Image height
 	ColorType type; ///< Image color type
-	Vector<uint8_t> data; ///< Image data
+	Vector<U8> data; ///< Image data
 	DataCompression dataCompression;
 
 	/// @name TGA headers
 	/// @{
-	static uint8_t tgaHeaderUncompressed[12];
-	static uint8_t tgaHeaderCompressed[12];
+	static U8 tgaHeaderUncompressed[12];
+	static U8 tgaHeaderCompressed[12];
 	/// @}
 
 	/// Load a TGA
@@ -107,13 +107,13 @@ private:
 	/// @param[in] fs The input
 	/// @param[out] bpp Bits per pixel
 	/// @exception Exception
-	void loadUncompressedTga(std::fstream& fs, uint32_t& bpp);
+	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, uint32_t& bpp);
+	void loadCompressedTga(std::fstream& fs, U32& bpp);
 
 	/// Load PNG. Dont throw exception because libpng is in C
 	/// @param[in] filename The file to load
@@ -126,6 +126,48 @@ private:
 	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];
+	}
+
+private:
+	Vector<Image> images;
+	MultiImageType type;
+
+	void loadCubemap(const char* filename);
+};
+
 } // end namespace anki
 
 #endif

+ 13 - 12
include/anki/resource/Material.h

@@ -272,6 +272,19 @@ protected:
 /// 	[<wireframe>0 | 1</wireframe>]
 ///
 /// 	<shaderProgram>
+///
+///			[<inputs> (3)
+///				<input>
+///					<name>xx</name>
+///					<type>any glsl type</type>
+///					<value> (4)
+///						[a_series_of_numbers |
+///						path/to/image.tga]
+///					</value>
+///					[<const>0 | 1</const>] (5)
+///				</input>
+///			</inputs>]
+///
 /// 		<shader> (2)
 /// 			<type>vertex | tc | te | geometry | fragment</type>
 ///
@@ -280,18 +293,6 @@ protected:
 /// 				<include>path/to/file2.glsl</include>
 /// 			</includes>
 ///
-/// 			[<inputs> (3)
-/// 				<input>
-/// 					<name>xx</name>
-/// 					<type>any glsl type</type>
-/// 					<value> (4)
-/// 						[a_series_of_numbers |
-/// 						path/to/image.tga]
-/// 					</value>
-/// 					[<const>0 | 1</const>] (5)
-/// 				</input>
-/// 			</inputs>]
-///
 /// 			<operations>
 /// 				<operation>
 /// 					<id>x</id>

+ 13 - 6
include/anki/scene/Light.h

@@ -79,17 +79,23 @@ public:
 		specColor = x;
 	}
 
-	bool getShadowEnabled() const
+	Bool getShadowEnabled() const
 	{
 		return shadow;
 	}
-	bool& getShadowEnabled()
+	void setShadowEnabled(const Bool x)
 	{
-		return shadow;
+		shadow = x;
+	}
+
+	U getShadowMapIndex() const
+	{
+		return (U)shadowMapIndex;
 	}
-	void setShadowEnabled(const bool x)
+	void setShadowMapIndex(const U i)
 	{
-		shadow = x;
+		ANKI_ASSERT(i < 0xFF);
+		shadowMapIndex = (U8)i;
 	}
 	/// @}
 
@@ -107,7 +113,8 @@ private:
 	LightType type;
 	Vec4 color = Vec4(1.0);
 	Vec4 specColor = Vec4(1.0);
-	bool shadow = false;
+	Bool shadow = false;
+	U8 shadowMapIndex = 0xFF; ///< Used by the renderer
 };
 
 /// Point light

+ 0 - 1
shaders/IsLp.glsl

@@ -317,7 +317,6 @@ void main()
 		}
 	}
 
-
 #if GROUND_LIGHT
 	fColor += max(dot(normal, groundLightDir.xyz), 0.0) 
 		* vec3(0.1, 0.1, 0.0);

+ 29 - 5
src/gl/Texture.cpp

@@ -250,13 +250,19 @@ void Texture::create(const Initializer& init)
 		if(isLayeredTarget(target))
 		{
 			glCompressedTexImage3D(target, 0, internalFormat,
-				width, height, depth, 0, init.dataSize, init.data);
+				width, height, depth, 0, init.dataSize, init.data[0]);
+		}
+		else if(target == GL_TEXTURE_CUBE_MAP)
+		{
+			ANKI_ASSERT(depth == 0);
+			
+			ANKI_ASSERT(0 && "TODO");
 		}
 		else
 		{
 			ANKI_ASSERT(depth == 0);
 			glCompressedTexImage2D(target, 0, internalFormat,
-				width, height, 0, init.dataSize, init.data);
+				width, height, 0, init.dataSize, init.data[0]);
 		}
 	}
 	else
@@ -264,13 +270,31 @@ void Texture::create(const Initializer& init)
 		if(isLayeredTarget(target))
 		{
 			glTexImage3D(target, 0, internalFormat, width, height, depth,
-				0, format, type, init.data);
+				0, format, type, init.data[0]);
+		}
+		else if(target == GL_TEXTURE_CUBE_MAP)
+		{
+			ANKI_ASSERT(depth == 0);
+
+			U32 dataMask = 0;
+
+			for(U i = 0; i < 6; i++)
+			{
+				glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 
+					internalFormat, width,
+					height, 0, format, type, init.data[i]);
+
+				dataMask |= (init.data[i] != nullptr) ? (1 << i) : 0;
+			}
+
+			// Or all the faces have data or none
+			ANKI_ASSERT(dataMask == 0 || dataMask == 0x3F);
 		}
 		else
 		{
 			ANKI_ASSERT(depth == 0);
 			glTexImage2D(target, 0, internalFormat, width,
-				height, 0, format, type, init.data);
+				height, 0, format, type, init.data[0]);
 		}
 	}
 
@@ -288,7 +312,7 @@ void Texture::create(const Initializer& init)
 		glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 	}
 
-	if(init.mipmapping && init.data)
+	if(init.mipmapping && init.data[0])
 	{
 		glGenerateMipmap(target);
 	}

+ 3 - 3
src/renderer/Is.cpp

@@ -292,9 +292,9 @@ struct WriteLightsJob: ThreadJob
 				cam->getViewMatrix());
 		baseslight->posRadius = Vec4(pos, light.getDistance());
 
-		// Diff color. don't set the shadowmap ID now
+		// Diff color and shadowmap ID now
 		baseslight->diffuseColorShadowmapId = 
-			Vec4(light.getDiffuseColor().xyz(), (F32)i);
+			Vec4(light.getDiffuseColor().xyz(), (F32)light.getShadowMapIndex());
 
 		// Spec color
 		baseslight->specularColorTexId = light.getSpecularColor();
@@ -642,7 +642,7 @@ void Is::lightPass()
 	sm.run(&shadowCasters[0], visibleSpotTexLightsCount, shadowmapLayers);
 
 	//
-	// Write the ligths and tiles UBOs
+	// Write the lights and tiles UBOs
 	//
 
 	// Get the offsets and sizes of each uniform block

+ 1 - 1
src/renderer/Renderer.cpp

@@ -186,7 +186,7 @@ void Renderer::createFai(U32 width, U32 height, int internalFormat,
 	init.internalFormat = internalFormat;
 	init.format = format;
 	init.type = type;
-	init.data = NULL;
+	init.data[0] = nullptr;
 	init.mipmapping = false;
 	init.filteringType = Texture::TFT_NEAREST;
 	init.repeat = false;

+ 1 - 0
src/renderer/Sm.cpp

@@ -193,6 +193,7 @@ Sm::Shadowmap* Sm::doLight(Light& light)
 	}
 
 	sm.timestamp = Timestamp::getTimestamp();
+	light.setShadowMapIndex(&sm - &sms[0]);
 
 	//
 	// Render

+ 67 - 6
src/resource/Image.cpp

@@ -3,15 +3,20 @@
 #include "anki/core/Logger.h"
 #include "anki/util/Filesystem.h"
 #include "anki/util/Assert.h"
+#include "anki/util/Array.h"
+#include "anki/misc/Xml.h"
 #include <png.h>
 #include <fstream>
 
 namespace anki {
 
+//==============================================================================
+// Image                                                                       =
 //==============================================================================
 
-uint8_t Image::tgaHeaderUncompressed[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-uint8_t Image::tgaHeaderCompressed[12] = {0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+//==============================================================================
+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)
@@ -51,10 +56,10 @@ void Image::load(const char* filename)
 }
 
 //==============================================================================
-void Image::loadUncompressedTga(std::fstream& fs, uint32_t& bpp)
+void Image::loadUncompressedTga(std::fstream& fs, U32& bpp)
 {
 	// read the info from header
-	uint8_t header6[6];
+	U8 header6[6];
 	fs.read((char*)&header6[0], sizeof(header6));
 	if(fs.gcount() != sizeof(header6))
 	{
@@ -91,7 +96,7 @@ void Image::loadUncompressedTga(std::fstream& fs, uint32_t& bpp)
 }
 
 //==============================================================================
-void Image::loadCompressedTga(std::fstream& fs, uint32_t& bpp)
+void Image::loadCompressedTga(std::fstream& fs, U32& bpp)
 {
 	unsigned char header6[6];
 	fs.read((char*)&header6[0], sizeof(header6));
@@ -116,7 +121,7 @@ void Image::loadCompressedTga(std::fstream& fs, uint32_t& bpp)
 	uint pixelcount = height * width;
 	uint currentpixel = 0;
 	uint currentbyte = 0;
-	uint8_t colorbuffer[4];
+	U8 colorbuffer[4];
 
 	do
 	{
@@ -685,4 +690,60 @@ void Image::loadDds(const char* filename)
 	in.close();
 }
 
+//==============================================================================
+// MultiImage                                                                  =
+//==============================================================================
+
+//==============================================================================
+void MultiImage::load(const char* filename)
+{
+	// get the extension
+	const char* ext = getFileExtension(filename);
+	ANKI_ASSERT(ext);
+
+	// load from this extension
+	try
+	{
+		if(strcmp(ext, "cubemap") == 0)
+		{
+			type = MIT_CUBE;
+			loadCubemap(filename);
+		}
+		else
+		{
+			// Single image
+			type = MIT_SINGLE;
+			images.resize(1);
+			images[0].load(filename);
+		}
+	}
+	catch(std::exception& e)
+	{
+		throw ANKI_EXCEPTION("File " + filename) << e;
+	}
+}
+
+//==============================================================================
+void MultiImage::loadCubemap(const char* filename)
+{
+	// Resize
+	images.resize(6);
+
+	// Load XML
+	XmlDocument doc;
+	doc.loadFile(filename);
+
+	XmlElement rootEl = doc.getChildElement("cubemap");
+	static Array<const char*, 6> tags = {{
+		"positiveX", "negativeX", 
+		"positiveY", "negativeY", 
+		"positiveZ", "negativeZ"}};
+
+	for(U i = 0; i < 6; i++)
+	{
+		XmlElement el = rootEl.getChildElement(tags[i]);
+		images[i].load(el.getText());
+	}
+}
+
 } // end namespace anki

+ 8 - 2
src/resource/MaterialShaderProgramCreator.cpp

@@ -292,8 +292,14 @@ void MaterialShaderProgramCreator::parseOperationTag(
 			// Search for all the inputs and mark the appropriate
 			for(U i = 0; i < inputs.size(); i++)
 			{
-				// Check at least the first part of the string
-				if(std::string(argEl.getText()).find(inputs[i]->name) == 0)
+				// Check that the first part of the string is equal to the 
+				// variable and the following char is '['
+				std::string text = argEl.getText();
+				const std::string& name = inputs[i]->name;
+				if(text == name 
+					|| (text.find(name) == 0 
+						&& text.size() > name.size()
+						&& text[name.size()] == '['))
 				{
 					inputs[i]->shaders |= (U32)shader;
 					break;

+ 2 - 2
src/resource/TextureResource.cpp

@@ -97,7 +97,7 @@ void TextureResource::load(const Image& img)
 #endif
 	}
 
-	init.data = img.getData();
+	init.data[0] = img.getData();
 	init.mipmapping = TextureManagerSingleton::get().getMipmappingEnabled();
 	init.filteringType = init.mipmapping ? TFT_TRILINEAR : TFT_LINEAR;
 	init.repeat = true;
@@ -106,4 +106,4 @@ void TextureResource::load(const Image& img)
 	create(init);
 }
 
-} // end namespace
+} // end namespace anki

+ 3 - 3
src/scene/StaticGeometryNode.cpp

@@ -31,7 +31,7 @@ StaticGeometryPatchNode::StaticGeometryPatchNode(
 	ANKI_ASSERT(modelPatch);
 	Renderable::init(*this);
 
-	// For all submeshes create a StaticGeometrySp[atialNode
+	// For all submeshes create a StaticGeometrySp[atial
 	if(modelPatch->getSubMeshesCount() > 1)
 	{
 		spatialProtected.subSpatials.resize(modelPatch->getSubMeshesCount());
@@ -74,8 +74,8 @@ StaticGeometryNode::StaticGeometryNode(const char* filename,
 	{
 		std::string name_ = name + std::to_string(i);
 
-		StaticGeometryPatchNode* node = 
-			ANKI_NEW(StaticGeometryPatchNode, getSceneAllocator(),
+		StaticGeometryPatchNode* node = ANKI_NEW(
+			StaticGeometryPatchNode, getSceneAllocator(),
 			patch, name_.c_str(), scene);
 
 		patches.push_back(node);

+ 3 - 3
testapp/Main.cpp

@@ -251,7 +251,7 @@ void init()
 		0.7));
 #endif
 
-#if 1
+#if 0
 	StaticGeometryNode* sponzaModel = new StaticGeometryNode(
 		//"data/maps/sponza/sponza_no_bmeshes.mdl",
 		//"data/maps/sponza/sponza.mdl",
@@ -500,8 +500,8 @@ void mainLoop()
 void initSubsystems(int argc, char* argv[])
 {
 #if ANKI_GL == ANKI_GL_DESKTOP
-	U32 glmajor = 4;
-	U32 glminor = 2;
+	U32 glmajor = 3;
+	U32 glminor = 3;
 #else
 	U32 glmajor = 3;
 	U32 glminor = 0;

+ 53 - 28
tools/2anki/Main.cpp

@@ -158,9 +158,13 @@ struct Vw
 };
 
 //==============================================================================
-void exportMesh(const aiMesh& mesh, const Config& config)
+void exportMesh(
+	const aiMesh& mesh, 
+	const std::string* name_,
+	const aiMatrix4x4* transform,
+	const Config& config)
 {
-	std::string name = mesh.mName.C_Str();
+	std::string name = (name_) ? *name_ : mesh.mName.C_Str();
 	std::fstream file;
 	LOGI("Exporting mesh %s\n", name.c_str());
 
@@ -184,6 +188,12 @@ void exportMesh(const aiMesh& mesh, const Config& config)
 	{
 		aiVector3D pos = mesh.mVertices[i];
 
+		// Transform
+		if(transform)
+		{
+			pos = (*transform) * pos;
+		}
+
 		// flip
 		if(config.flipyz)
 		{
@@ -345,7 +355,8 @@ void exportSkeleton(const aiMesh& mesh, const Config& config)
 }
 
 //==============================================================================
-void exportMaterial(const aiMaterial& mtl, const Config& config)
+void exportMaterial(const aiScene& scene, const aiMaterial& mtl, 
+	const Config& config)
 {
 	std::string diffTex;
 	std::string normTex;
@@ -623,20 +634,39 @@ void exportAnimation(const aiAnimation& anim, uint32_t index,
 }
 
 //==============================================================================
-void exportNode(const aiScene& scene, const aiNode* node, const Config& config)
+void exportNode(
+	const aiScene& scene, 
+	const aiNode* node, 
+	const Config& config,
+	std::fstream& file)
 {
 	if(node == nullptr)
 	{
 		return;
 	}
 
-	// Write the .mdl
-	exportModel(scene, *node, config);
+	for(uint32_t i = 0; i < node->mNumMeshes; i++)
+	{
+		const aiMesh& mesh = *scene.mMeshes[node->mMeshes[i]];
+
+		std::string name = node->mName.C_Str() + std::to_string(i);
+
+		exportMesh(mesh, &name, &node->mTransformation, config);
+
+		exportMaterial(scene, *scene.mMaterials[mesh.mMaterialIndex], config);
+
+		file << "\t<modelPatch>\n";
+		file << "\t\t<mesh>" << config.outDir << name << ".mesh\n";
+		aiString ainame;
+		scene.mMaterials[mesh.mMaterialIndex]->Get(AI_MATKEY_NAME, ainame);
+		file << "\t\t<material>" << ainame.C_Str() << ".mtl\n";
+		file << "\t</modelPatch>\n";
+	}
 	
 	// Go to children
 	for(uint32_t i = 0; i < node->mNumChildren; i++)
 	{
-		exportNode(scene, node->mChildren[i], config);
+		exportNode(scene, node->mChildren[i], config, file);
 	}
 }
 
@@ -645,31 +675,26 @@ void exportScene(const aiScene& scene, const Config& config)
 {
 	LOGI("Exporting scene to %s\n", config.outDir.c_str());
 
-	// Meshes and skeletons
-	for(uint32_t i = 0; i < scene.mNumMeshes; i++)
-	{
-		exportMesh(*scene.mMeshes[i], config);
+	// Open file
+	std::fstream file;
+	file.open(config.outDir + "scene.scene", std::ios::out);
 
-		if(scene.mMeshes[i]->HasBones())
-		{
-			exportSkeleton(*scene.mMeshes[i], config);
-		}
-	}
+	// Write some stuff
+	file << xmlHeader << "\n";
+	file << "<scene>\n";
 
-	// Materials
-	for(uint32_t i = 0; i < scene.mNumMaterials; i++)
-	{
-		exportMaterial(*scene.mMaterials[i], config);
-	}
+	// TODO The sectors/portals
 
-	// The nodes
-	exportNode(scene, scene.mRootNode, config);
+	// Geometry
+	std::fstream modelFile;
+	modelFile.open(config.outDir + "static_geometry.mdl", std::ios::out);
+	modelFile << xmlHeader << "\n";
+	modelFile << "<model>\n";
+	exportNode(scene, scene.mRootNode, config, modelFile);
+	modelFile << "</model>\n";
 
-	// The animations
-	for(uint32_t i = 0; i < scene.mNumAnimations; i++)
-	{
-		exportAnimation(*scene.mAnimations[i], i, scene, config);	
-	}
+	// End
+	file << "</scene>\n";
 
 	LOGI("Done exporting scene!\n");
 }

+ 9 - 11
tools/2anki/diffNormTemplateMtl.h

@@ -6,17 +6,20 @@ R"(<?xml version="1.0" encoding="UTF-8" ?>
 	<passes>COLOR DEPTH</passes>
 
 	<shaderProgram>
+		<inputs>
+			<input><type>mat4</type><name>modelViewProjectionMat</name><value></value></input>
+			<input><type>mat3</type><name>normalMat</name><value></value></input>
+			<input><type>vec2</type><name>specular</name><value>1.0 90.0</value></input>
+
+			<input><type>sampler2D</type><name>diffuseMap</name><value>%diffuseMap%</value></input>
+			<input><type>sampler2D</type><name>normalMap</name><value>%normalMap%</value></input>
+		</inputs>
+
 		<shader>
 			<type>vertex</type>
 			<includes>
 				<include>shaders/MsCommonVert.glsl</include>
 			</includes>
-		
-			<inputs>
-				<input><type>mat4</type><name>modelViewProjectionMat</name><value></value></input>
-				<input><type>mat3</type><name>normalMat</name><value></value></input>
-				<input><type>vec2</type><name>specular</name><value>1.0 90.0</value></input>
-			</inputs>
 
 			<operations>
 				<operation>
@@ -42,11 +45,6 @@ R"(<?xml version="1.0" encoding="UTF-8" ?>
 				<include>shaders/MsCommonFrag.glsl</include>
 			</includes>
 			
-			<inputs>
-				<input><type>sampler2D</type><name>diffuseMap</name><value>%diffuseMap%</value></input>
-				<input><type>sampler2D</type><name>normalMap</name><value>%normalMap%</value></input>
-			</inputs>
-			
 			<operations>
 				<operation>
 					<id>0</id>

+ 7 - 10
tools/2anki/diffTemplateMtl.h

@@ -6,17 +6,18 @@ R"(<?xml version="1.0" encoding="UTF-8" ?>
 	<passes>COLOR DEPTH</passes>
 
 	<shaderProgram>
+		<inputs>
+			<input><type>mat4</type><name>modelViewProjectionMat</name><value></value></input>
+			<input><type>mat3</type><name>normalMat</name><value></value></input>
+			<input><type>vec2</type><name>specular</name><value>1.0 90.0</value></input>
+			<input><type>sampler2D</type><name>diffuseMap</name><value>%diffuseMap%</value></input>
+		</inputs>
+
 		<shader>
 			<type>vertex</type>
 			<includes>
 				<include>shaders/MsCommonVert.glsl</include>
 			</includes>
-		
-			<inputs>
-				<input><type>mat4</type><name>modelViewProjectionMat</name><value></value></input>
-				<input><type>mat3</type><name>normalMat</name><value></value></input>
-				<input><type>vec2</type><name>specular</name><value>1.0 90.0</value></input>
-			</inputs>
 
 			<operations>
 				<operation>
@@ -42,10 +43,6 @@ R"(<?xml version="1.0" encoding="UTF-8" ?>
 				<include>shaders/MsCommonFrag.glsl</include>
 			</includes>
 			
-			<inputs>
-				<input><type>sampler2D</type><name>diffuseMap</name><value>%diffuseMap%</value></input>
-			</inputs>
-			
 			<operations>
 				<operation>
 					<id>0</id>