Panagiotis Christopoulos Charitos 6 лет назад
Родитель
Сommit
f29b4875b7

+ 1 - 0
src/anki/Util.h

@@ -39,6 +39,7 @@
 #include <anki/util/ObjectAllocator.h>
 #include <anki/util/Tracer.h>
 #include <anki/util/Serializer.h>
+#include <anki/util/Xml.h>
 
 /// @defgroup util Utilities (like STL)
 

+ 5 - 0
src/anki/math/Mat.h

@@ -1530,6 +1530,11 @@ public:
 	{
 		(*this) = getIdentity();
 	}
+
+	static constexpr U8 getSize()
+	{
+		return U8(I * J);
+	}
 	/// @}
 
 protected:

+ 5 - 0
src/anki/math/Vec.h

@@ -3035,6 +3035,11 @@ public:
 		ANKI_ASSERT(data);
 		memcpy(this, data, sizeof(*this));
 	}
+
+	static constexpr U8 getSize()
+	{
+		return U8(N);
+	}
 	/// @}
 
 private:

+ 1 - 1
src/anki/misc/ConfigSet.cpp

@@ -4,7 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/misc/ConfigSet.h>
-#include <anki/misc/Xml.h>
+#include <anki/util/Xml.h>
 #include <anki/util/Logger.h>
 #include <anki/util/File.h>
 

+ 2 - 2
src/anki/misc/ConfigSet.h

@@ -38,7 +38,7 @@ public:
 	void set(const CString& name, F64 value);
 	/// @}
 
-	/// @name Find an option and return it's value.
+	/// @name Find an option and return its value.
 	/// @{
 	F64 getNumberF64(const CString& name) const;
 	F32 getNumberF32(const CString& name) const;
@@ -66,7 +66,7 @@ private:
 		String m_name;
 		String m_strVal;
 		String m_helpMsg;
-		F64 m_fVal = 0.0;
+		F64 m_fVal = 0.0f;
 		U8 m_type = 0; ///< 0: string, 1: float
 
 		Option() = default;

+ 0 - 315
src/anki/misc/Xml.cpp

@@ -1,315 +0,0 @@
-// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/misc/Xml.h>
-#include <anki/util/File.h>
-#include <anki/util/Logger.h>
-
-namespace anki
-{
-
-ANKI_USE_RESULT Error XmlElement::check() const
-{
-	Error err = Error::NONE;
-	if(m_el == nullptr)
-	{
-		ANKI_MISC_LOGE("Empty element");
-		err = Error::USER_DATA;
-	}
-	return err;
-}
-
-Error XmlElement::getText(CString& out) const
-{
-	Error err = check();
-	if(!err && m_el->GetText())
-	{
-		out = CString(m_el->GetText());
-	}
-	else
-	{
-		out = CString();
-	}
-
-	return err;
-}
-
-Error XmlElement::getMat3(Mat3& out) const
-{
-	DynamicArrayAuto<F32> arr(m_alloc);
-	Error err = getNumbers(arr);
-
-	if(!err && arr.getSize() != 9)
-	{
-		ANKI_MISC_LOGE("Expecting 9 elements for Mat3");
-		err = Error::USER_DATA;
-	}
-
-	if(!err)
-	{
-		for(U32 i = 0; i < 9 && !err; i++)
-		{
-			out[i] = arr[i];
-		}
-	}
-
-	if(err)
-	{
-		ANKI_MISC_LOGE("Failed to return Mat3. Element: %s", m_el->Value());
-	}
-
-	return err;
-}
-
-Error XmlElement::getMat4(Mat4& out) const
-{
-	DynamicArrayAuto<F32> arr(m_alloc);
-	Error err = getNumbers(arr);
-
-	if(!err && arr.getSize() != 16)
-	{
-		ANKI_MISC_LOGE("Expecting 16 elements for Mat4");
-		err = Error::USER_DATA;
-	}
-
-	if(!err)
-	{
-		for(U32 i = 0; i < 16 && !err; i++)
-		{
-			out[i] = arr[i];
-		}
-	}
-
-	if(err)
-	{
-		ANKI_MISC_LOGE("Failed to return Mat4. Element: %s", m_el->Value());
-	}
-
-	return err;
-}
-
-Error XmlElement::getVec2(Vec2& out) const
-{
-	DynamicArrayAuto<F32> arr(m_alloc);
-	Error err = getNumbers(arr);
-
-	if(!err && arr.getSize() != 2)
-	{
-		ANKI_MISC_LOGE("Expecting 2 elements for Vec2");
-		err = Error::USER_DATA;
-	}
-
-	if(!err)
-	{
-		for(U32 i = 0; i < 2; i++)
-		{
-			out[i] = arr[i];
-		}
-	}
-
-	if(err)
-	{
-		ANKI_MISC_LOGE("Failed to return Vec2. Element: %s", m_el->Value());
-	}
-
-	return err;
-}
-
-Error XmlElement::getVec3(Vec3& out) const
-{
-	DynamicArrayAuto<F32> arr(m_alloc);
-	Error err = getNumbers(arr);
-
-	if(!err && arr.getSize() != 3)
-	{
-		ANKI_MISC_LOGE("Expecting 3 elements for Vec3");
-		err = Error::USER_DATA;
-	}
-
-	if(!err)
-	{
-		for(U32 i = 0; i < 3; i++)
-		{
-			out[i] = arr[i];
-		}
-	}
-
-	if(err)
-	{
-		ANKI_MISC_LOGE("Failed to return Vec3. Element: %s", m_el->Value());
-	}
-
-	return err;
-}
-
-Error XmlElement::getVec4(Vec4& out) const
-{
-	DynamicArrayAuto<F32> arr(m_alloc);
-	Error err = getNumbers(arr);
-
-	if(!err && arr.getSize() != 4)
-	{
-		ANKI_MISC_LOGE("Expecting 4 elements for Vec4");
-		err = Error::USER_DATA;
-	}
-
-	if(!err)
-	{
-		for(U32 i = 0; i < 4; i++)
-		{
-			out[i] = arr[i];
-		}
-	}
-
-	if(err)
-	{
-		ANKI_MISC_LOGE("Failed to return Vec4. Element: %s", m_el->Value());
-	}
-
-	return err;
-}
-
-Error XmlElement::getChildElementOptional(const CString& name, XmlElement& out) const
-{
-	Error err = check();
-	if(!err)
-	{
-		out = XmlElement(m_el->FirstChildElement(&name[0]), m_alloc);
-	}
-	else
-	{
-		out = XmlElement();
-	}
-
-	return err;
-}
-
-Error XmlElement::getChildElement(const CString& name, XmlElement& out) const
-{
-	Error err = check();
-	if(err)
-	{
-		out = XmlElement();
-		return err;
-	}
-
-	err = getChildElementOptional(name, out);
-	if(err)
-	{
-		return err;
-	}
-
-	if(!out)
-	{
-		ANKI_MISC_LOGE("Cannot find tag \"%s\"", &name[0]);
-		err = Error::USER_DATA;
-	}
-
-	return err;
-}
-
-Error XmlElement::getNextSiblingElement(const CString& name, XmlElement& out) const
-{
-	Error err = check();
-	if(!err)
-	{
-		out = XmlElement(m_el->NextSiblingElement(&name[0]), m_alloc);
-	}
-	else
-	{
-		out = XmlElement();
-	}
-
-	return err;
-}
-
-Error XmlElement::getSiblingElementsCount(U32& out) const
-{
-	ANKI_CHECK(check());
-	const tinyxml2::XMLElement* el = m_el;
-
-	I count = -1;
-	do
-	{
-		el = el->NextSiblingElement(m_el->Name());
-		++count;
-	} while(el);
-
-	out = U32(count);
-
-	return Error::NONE;
-}
-
-Error XmlElement::getAttributeTextOptional(const CString& name, CString& out, Bool& attribPresent) const
-{
-	ANKI_CHECK(check());
-
-	const tinyxml2::XMLAttribute* attrib = m_el->FindAttribute(&name[0]);
-	if(!attrib)
-	{
-		attribPresent = false;
-		return Error::NONE;
-	}
-
-	attribPresent = true;
-
-	const char* value = attrib->Value();
-	if(value)
-	{
-		out = value;
-	}
-	else
-	{
-		out = CString();
-	}
-
-	return Error::NONE;
-}
-
-CString XmlDocument::XML_HEADER = R"(<?xml version="1.0" encoding="UTF-8" ?>)";
-
-Error XmlDocument::loadFile(const CString& filename, GenericMemoryPoolAllocator<U8> alloc)
-{
-	File file;
-	ANKI_CHECK(file.open(filename, FileOpenFlag::READ));
-
-	StringAuto text(alloc);
-	ANKI_CHECK(file.readAllText(text));
-
-	ANKI_CHECK(parse(text.toCString(), alloc));
-
-	return Error::NONE;
-}
-
-Error XmlDocument::parse(const CString& xmlText, GenericMemoryPoolAllocator<U8> alloc)
-{
-	m_alloc = alloc;
-
-	if(m_doc.Parse(&xmlText[0]))
-	{
-		ANKI_MISC_LOGE(
-			"Cannot parse file. Reason: %s", ((m_doc.GetErrorStr1() == nullptr) ? "unknown" : m_doc.GetErrorStr1()));
-
-		return Error::USER_DATA;
-	}
-
-	return Error::NONE;
-}
-
-ANKI_USE_RESULT Error XmlDocument::getChildElement(const CString& name, XmlElement& out) const
-{
-	Error err = Error::NONE;
-	out = XmlElement(m_doc.FirstChildElement(&name[0]), m_alloc);
-
-	if(!out)
-	{
-		ANKI_MISC_LOGE("Cannot find tag \"%s\"", &name[0]);
-		err = Error::USER_DATA;
-	}
-
-	return err;
-}
-
-} // end namespace anki

+ 0 - 347
src/anki/misc/Xml.h

@@ -1,347 +0,0 @@
-// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/misc/Common.h>
-#include <anki/util/String.h>
-#include <anki/util/DynamicArray.h>
-#include <anki/util/StringList.h>
-#include <anki/Math.h>
-#include <tinyxml2.h>
-#if !ANKI_TINYXML2
-#	error "Wrong tinyxml2 included"
-#endif
-
-namespace anki
-{
-
-/// @addtogroup misc
-/// @{
-
-/// XML element.
-class XmlElement
-{
-	friend class XmlDocument;
-
-public:
-	XmlElement()
-		: m_el(nullptr)
-	{
-	}
-
-	XmlElement(const XmlElement& b)
-		: m_el(b.m_el)
-		, m_alloc(b.m_alloc)
-	{
-	}
-
-	XmlElement(const tinyxml2::XMLElement* el, GenericMemoryPoolAllocator<U8> alloc)
-		: m_el(el)
-		, m_alloc(alloc)
-	{
-	}
-
-	/// If element has something return true
-	operator Bool() const
-	{
-		return m_el != nullptr;
-	}
-
-	/// Copy
-	XmlElement& operator=(const XmlElement& b)
-	{
-		m_el = b.m_el;
-		m_alloc = b.m_alloc;
-		return *this;
-	}
-
-	/// Return the text inside a tag. May return empty string.
-	ANKI_USE_RESULT Error getText(CString& out) const;
-
-	/// Return the text inside as a number.
-	template<typename T>
-	ANKI_USE_RESULT Error getNumber(T& out) const
-	{
-		ANKI_CHECK(check());
-
-		const char* txt = m_el->GetText();
-		if(txt != nullptr)
-		{
-			ANKI_CHECK(CString(txt).toNumber(out));
-		}
-		else
-		{
-			ANKI_MISC_LOGE("Failed to return number. Element: %s", m_el->Value());
-			return Error::USER_DATA;
-		}
-
-		return Error::NONE;
-	}
-
-	/// Get a number of numbers.
-	template<typename T>
-	ANKI_USE_RESULT Error getNumbers(DynamicArrayAuto<T>& out) const
-	{
-		CString txt;
-		ANKI_CHECK(getText(txt));
-
-		if(txt)
-		{
-			return parseNumbers(txt, out);
-		}
-		else
-		{
-			out = DynamicArrayAuto<T>(m_alloc);
-			return Error::NONE;
-		}
-	}
-
-	/// Return the text inside as a Mat4
-	ANKI_USE_RESULT Error getMat4(Mat4& out) const;
-
-	/// Return the text inside as a Mat3
-	ANKI_USE_RESULT Error getMat3(Mat3& out) const;
-
-	/// Return the text inside as a Vec2
-	ANKI_USE_RESULT Error getVec2(Vec2& out) const;
-
-	/// Return the text inside as a Vec3
-	ANKI_USE_RESULT Error getVec3(Vec3& out) const;
-
-	/// Return the text inside as a Vec4
-	ANKI_USE_RESULT Error getVec4(Vec4& out) const;
-
-	/// Get a child element quietly
-	ANKI_USE_RESULT Error getChildElementOptional(const CString& name, XmlElement& out) const;
-
-	/// Get a child element and print error if not found
-	ANKI_USE_RESULT Error getChildElement(const CString& name, XmlElement& out) const;
-
-	/// Get the next element with the same name. Returns empty XmlElement if it reached the end of the list.
-	ANKI_USE_RESULT Error getNextSiblingElement(const CString& name, XmlElement& out) const;
-
-	/// Get the number of sibling elements of this node.
-	/// @note The sibling elements share the same name.
-	ANKI_USE_RESULT Error getSiblingElementsCount(U32& out) const;
-
-	/// @name Get attributes optional
-	/// @{
-
-	/// Get value of a string attribute. May return empty string.
-	/// @param name The name of the attribute.
-	/// @param out The value of the attribute.
-	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
-	ANKI_USE_RESULT Error getAttributeTextOptional(const CString& name, CString& out, Bool& attribPresent) const;
-
-	/// Get the attribute's value as a series of numbers.
-	/// @param name The name of the attribute.
-	/// @param out The value of the attribute.
-	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
-	template<typename T>
-	ANKI_USE_RESULT Error getAttributeNumbersOptional(
-		const CString& name, DynamicArrayAuto<T>& out, Bool& attribPresent) const
-	{
-		CString txtVal;
-		ANKI_CHECK(getAttributeTextOptional(name, txtVal, attribPresent));
-
-		if(txtVal && attribPresent)
-		{
-			return parseNumbers(txtVal, out);
-		}
-		else
-		{
-			return Error::NONE;
-		}
-	}
-
-	/// Get the attribute's value as a number.
-	/// @param name The name of the attribute.
-	/// @param out The value of the attribute.
-	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
-	template<typename T>
-	ANKI_USE_RESULT Error getAttributeNumberOptional(const CString& name, T& out, Bool& attribPresent) const
-	{
-		DynamicArrayAuto<T> arr(m_alloc);
-		ANKI_CHECK(getAttributeNumbersOptional(name, arr, attribPresent));
-
-		if(attribPresent)
-		{
-			if(arr.getSize() != 1)
-			{
-				ANKI_MISC_LOGE("Expecting one element for attrib %s", &name[0]);
-				return Error::USER_DATA;
-			}
-
-			out = arr[0];
-		}
-
-		return Error::NONE;
-	}
-
-	/// Get the attribute's value as a vector.
-	/// @param name The name of the attribute.
-	/// @param out The value of the attribute.
-	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
-	template<typename T>
-	ANKI_USE_RESULT Error getAttributeVectorOptional(const CString& name, T& out, Bool& attribPresent) const
-	{
-		DynamicArrayAuto<F32> arr(m_alloc);
-		ANKI_CHECK(getAttributeNumbersOptional(name, arr, attribPresent));
-
-		if(attribPresent)
-		{
-			if(arr.getSize() != sizeof(T) / sizeof(out[0]))
-			{
-				ANKI_MISC_LOGE("Expecting %u elements for attrib %s", sizeof(T) / sizeof(out[0]), &name[0]);
-				return Error::USER_DATA;
-			}
-
-			U count = 0;
-			for(F32 v : arr)
-			{
-				out[count++] = typename T::Scalar(v);
-			}
-		}
-
-		return Error::NONE;
-	}
-
-	/// Get the attribute's value as a matrix.
-	/// @param name The name of the attribute.
-	/// @param out The value of the attribute.
-	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
-	template<typename T>
-	ANKI_USE_RESULT Error getAttributeMatrixOptional(const CString& name, T& out, Bool& attribPresent) const
-	{
-		return getAttributeVectorOptional(name, out, attribPresent);
-	}
-	/// @}
-
-	/// @name Get attributes
-	/// @{
-
-	/// Get value of a string attribute. May return empty string.
-	/// @param name The name of the attribute.
-	/// @param out The value of the attribute.
-	ANKI_USE_RESULT Error getAttributeText(const CString& name, CString& out) const
-	{
-		Bool found;
-		ANKI_CHECK(getAttributeTextOptional(name, out, found));
-		return throwAttribNotFoundError(name, found);
-	}
-
-	/// Get the attribute's value as a series of numbers.
-	/// @param name The name of the attribute.
-	/// @param out The value of the attribute.
-	template<typename T>
-	ANKI_USE_RESULT Error getAttributeNumbers(const CString& name, DynamicArrayAuto<T>& out) const
-	{
-		Bool found;
-		ANKI_CHECK(getAttributeNumbersOptional(name, out, found));
-		return throwAttribNotFoundError(name, found);
-	}
-
-	/// Get the attribute's value as a number.
-	/// @param name The name of the attribute.
-	/// @param out The value of the attribute.
-	template<typename T>
-	ANKI_USE_RESULT Error getAttributeNumber(const CString& name, T& out) const
-	{
-		Bool found;
-		ANKI_CHECK(getAttributeNumberOptional(name, out, found));
-		return throwAttribNotFoundError(name, found);
-	}
-
-	/// Get the attribute's value as a vector.
-	/// @param name The name of the attribute.
-	/// @param out The value of the attribute.
-	template<typename T>
-	ANKI_USE_RESULT Error getAttributeVector(const CString& name, T& out) const
-	{
-		Bool found;
-		ANKI_CHECK(getAttributeVectorOptional(name, out, found));
-		return throwAttribNotFoundError(name, found);
-	}
-
-	/// Get the attribute's value as a matrix.
-	/// @param name The name of the attribute.
-	/// @param out The value of the attribute.
-	template<typename T>
-	ANKI_USE_RESULT Error getAttributeMatrix(const CString& name, T& out) const
-	{
-		return getAttributeVector(name, out);
-	}
-	/// @}
-
-private:
-	const tinyxml2::XMLElement* m_el;
-	GenericMemoryPoolAllocator<U8> m_alloc;
-
-	ANKI_USE_RESULT Error check() const;
-
-	template<typename T>
-	ANKI_USE_RESULT Error parseNumbers(CString txt, DynamicArrayAuto<T>& out) const
-	{
-		ANKI_ASSERT(txt);
-		ANKI_ASSERT(m_el);
-
-		StringListAuto list(m_alloc);
-		list.splitString(txt, ' ');
-
-		out = DynamicArrayAuto<T>(m_alloc);
-		out.create(U32(list.getSize()));
-
-		Error err = Error::NONE;
-		auto it = list.getBegin();
-		auto end = list.getEnd();
-		U32 i = 0;
-		while(it != end && !err)
-		{
-			err = it->toNumber(out[i++]);
-			++it;
-		}
-
-		if(err)
-		{
-			ANKI_MISC_LOGE("Failed to parse floats. Element: %s", m_el->Value());
-		}
-
-		return err;
-	}
-
-	ANKI_USE_RESULT Error throwAttribNotFoundError(CString attrib, Bool found) const
-	{
-		if(!found)
-		{
-			ANKI_MISC_LOGE("Attribute not found \"%s\"", &attrib[0]);
-			return Error::USER_DATA;
-		}
-		else
-		{
-			return Error::NONE;
-		}
-	}
-};
-
-/// XML document
-class XmlDocument
-{
-public:
-	static CString XML_HEADER;
-
-	ANKI_USE_RESULT Error loadFile(const CString& filename, GenericMemoryPoolAllocator<U8> alloc);
-
-	ANKI_USE_RESULT Error parse(const CString& xmlText, GenericMemoryPoolAllocator<U8> alloc);
-
-	ANKI_USE_RESULT Error getChildElement(const CString& name, XmlElement& out) const;
-
-private:
-	tinyxml2::XMLDocument m_doc;
-	GenericMemoryPoolAllocator<U8> m_alloc;
-};
-/// @}
-
-} // end namespace anki

+ 3 - 3
src/anki/resource/AnimationResource.cpp

@@ -4,7 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/resource/AnimationResource.h>
-#include <anki/misc/Xml.h>
+#include <anki/util/Xml.h>
 
 namespace anki
 {
@@ -98,7 +98,7 @@ Error AnimationResource::load(const ResourceFilename& filename, Bool async)
 
 				// <value>
 				ANKI_CHECK(keyEl.getChildElement("value", el));
-				ANKI_CHECK(el.getVec3(key.m_value));
+				ANKI_CHECK(el.getNumbers(key.m_value));
 
 				// Check ident
 				if(key.m_value == Vec3(0.0))
@@ -137,7 +137,7 @@ Error AnimationResource::load(const ResourceFilename& filename, Bool async)
 				// <value>
 				Vec4 tmp2;
 				ANKI_CHECK(keyEl.getChildElement("value", el));
-				ANKI_CHECK(el.getVec4(tmp2));
+				ANKI_CHECK(el.getNumbers(tmp2));
 				key.m_value = Quat(tmp2);
 
 				// Check ident

+ 2 - 2
src/anki/resource/CollisionResource.cpp

@@ -7,7 +7,7 @@
 #include <anki/resource/ResourceManager.h>
 #include <anki/resource/MeshLoader.h>
 #include <anki/physics/PhysicsWorld.h>
-#include <anki/misc/Xml.h>
+#include <anki/util/Xml.h>
 
 namespace anki
 {
@@ -39,7 +39,7 @@ Error CollisionResource::load(const ResourceFilename& filename, Bool async)
 	else if(type == "box")
 	{
 		Vec3 extend;
-		ANKI_CHECK(valEl.getVec3(extend));
+		ANKI_CHECK(valEl.getNumbers(extend));
 		m_physicsShape = physics.newInstance<PhysicsBox>(extend);
 	}
 	else if(type == "staticMesh")

+ 21 - 21
src/anki/resource/MaterialResource.cpp

@@ -5,7 +5,7 @@
 
 #include <anki/resource/MaterialResource.h>
 #include <anki/resource/ResourceManager.h>
-#include <anki/misc/Xml.h>
+#include <anki/util/Xml.h>
 
 namespace anki
 {
@@ -342,37 +342,37 @@ Error MaterialResource::parseInputs(XmlElement inputsEl, Bool async)
 				ANKI_CHECK(inputEl.getAttributeNumber("value", constVal.m_int));
 				break;
 			case ShaderVariableDataType::IVEC2:
-				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_ivec2));
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_ivec2));
 				break;
 			case ShaderVariableDataType::IVEC3:
-				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_ivec3));
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_ivec3));
 				break;
 			case ShaderVariableDataType::IVEC4:
-				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_ivec4));
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_ivec4));
 				break;
 			case ShaderVariableDataType::UINT:
 				ANKI_CHECK(inputEl.getAttributeNumber("value", constVal.m_uint));
 				break;
 			case ShaderVariableDataType::UVEC2:
-				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_uvec2));
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_uvec2));
 				break;
 			case ShaderVariableDataType::UVEC3:
-				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_uvec3));
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_uvec3));
 				break;
 			case ShaderVariableDataType::UVEC4:
-				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_uvec4));
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_uvec4));
 				break;
 			case ShaderVariableDataType::FLOAT:
 				ANKI_CHECK(inputEl.getAttributeNumber("value", constVal.m_float));
 				break;
 			case ShaderVariableDataType::VEC2:
-				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_vec2));
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_vec2));
 				break;
 			case ShaderVariableDataType::VEC3:
-				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_vec3));
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_vec3));
 				break;
 			case ShaderVariableDataType::VEC4:
-				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_vec4));
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_vec4));
 				break;
 			default:
 				ANKI_ASSERT(0);
@@ -442,43 +442,43 @@ Error MaterialResource::parseInputs(XmlElement inputsEl, Bool async)
 					ANKI_CHECK(inputEl.getAttributeNumber("value", mtlVar.m_int));
 					break;
 				case ShaderVariableDataType::IVEC2:
-					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_ivec2));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_ivec2));
 					break;
 				case ShaderVariableDataType::IVEC3:
-					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_ivec3));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_ivec3));
 					break;
 				case ShaderVariableDataType::IVEC4:
-					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_ivec4));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_ivec4));
 					break;
 				case ShaderVariableDataType::UINT:
 					ANKI_CHECK(inputEl.getAttributeNumber("value", mtlVar.m_uint));
 					break;
 				case ShaderVariableDataType::UVEC2:
-					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_uvec2));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_uvec2));
 					break;
 				case ShaderVariableDataType::UVEC3:
-					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_uvec3));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_uvec3));
 					break;
 				case ShaderVariableDataType::UVEC4:
-					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_uvec4));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_uvec4));
 					break;
 				case ShaderVariableDataType::FLOAT:
 					ANKI_CHECK(inputEl.getAttributeNumber("value", mtlVar.m_float));
 					break;
 				case ShaderVariableDataType::VEC2:
-					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_vec2));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_vec2));
 					break;
 				case ShaderVariableDataType::VEC3:
-					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_vec3));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_vec3));
 					break;
 				case ShaderVariableDataType::VEC4:
-					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_vec4));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_vec4));
 					break;
 				case ShaderVariableDataType::MAT3:
-					ANKI_CHECK(inputEl.getAttributeMatrix("value", mtlVar.m_mat3));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_mat3));
 					break;
 				case ShaderVariableDataType::MAT4:
-					ANKI_CHECK(inputEl.getAttributeMatrix("value", mtlVar.m_mat4));
+					ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_mat4));
 					break;
 				case ShaderVariableDataType::TEXTURE_2D:
 				case ShaderVariableDataType::TEXTURE_2D_ARRAY:

+ 1 - 1
src/anki/resource/MeshResource.cpp

@@ -8,7 +8,7 @@
 #include <anki/resource/MeshLoader.h>
 #include <anki/resource/AsyncLoader.h>
 #include <anki/util/Functions.h>
-#include <anki/misc/Xml.h>
+#include <anki/util/Xml.h>
 
 namespace anki
 {

+ 1 - 1
src/anki/resource/ModelResource.cpp

@@ -8,7 +8,7 @@
 #include <anki/resource/MaterialResource.h>
 #include <anki/resource/MeshResource.h>
 #include <anki/resource/MeshLoader.h>
-#include <anki/misc/Xml.h>
+#include <anki/util/Xml.h>
 #include <anki/util/Logger.h>
 
 namespace anki

+ 2 - 2
src/anki/resource/ParticleEmitterResource.cpp

@@ -7,7 +7,7 @@
 #include <anki/resource/ResourceManager.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/util/StringList.h>
-#include <anki/misc/Xml.h>
+#include <anki/util/Xml.h>
 #include <cstring>
 
 namespace anki
@@ -22,7 +22,7 @@ static ANKI_USE_RESULT Error getXmlVal(const XmlElement& el, const CString& tag,
 template<>
 ANKI_USE_RESULT Error getXmlVal(const XmlElement& el, const CString& tag, Vec3& out, Bool& found)
 {
-	return el.getAttributeVectorOptional(tag, out, found);
+	return el.getAttributeNumbersOptional(tag, out, found);
 }
 
 ParticleEmitterProperties& ParticleEmitterProperties::operator=(const ParticleEmitterProperties& b)

+ 1 - 1
src/anki/resource/ResourceObject.cpp

@@ -5,7 +5,7 @@
 
 #include <anki/resource/ResourceObject.h>
 #include <anki/resource/ResourceManager.h>
-#include <anki/misc/Xml.h>
+#include <anki/util/Xml.h>
 
 namespace anki
 {

+ 3 - 3
src/anki/resource/SkeletonResource.cpp

@@ -4,7 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/resource/SkeletonResource.h>
-#include <anki/misc/Xml.h>
+#include <anki/util/Xml.h>
 #include <anki/util/StringList.h>
 
 namespace anki
@@ -59,12 +59,12 @@ Error SkeletonResource::load(const ResourceFilename& filename, Bool async)
 		// <transform>
 		XmlElement trfEl;
 		ANKI_CHECK(boneEl.getChildElement("transform", trfEl));
-		ANKI_CHECK(trfEl.getMat4(bone.m_transform));
+		ANKI_CHECK(trfEl.getNumbers(bone.m_transform));
 
 		// <boneTransform>
 		XmlElement btrfEl;
 		ANKI_CHECK(boneEl.getChildElement("boneTransform", btrfEl));
-		ANKI_CHECK(btrfEl.getMat4(bone.m_vertTrf));
+		ANKI_CHECK(btrfEl.getNumbers(bone.m_vertTrf));
 
 		// <parent>
 		XmlElement parentEl;

+ 2 - 2
src/anki/resource/TextureAtlasResource.cpp

@@ -5,7 +5,7 @@
 
 #include <anki/resource/TextureAtlasResource.h>
 #include <anki/resource/ResourceManager.h>
-#include <anki/misc/Xml.h>
+#include <anki/util/Xml.h>
 
 namespace anki
 {
@@ -105,7 +105,7 @@ Error TextureAtlasResource::load(const ResourceFilename& filename, Bool async)
 
 		ANKI_CHECK(subTexEl.getChildElement("uv", el));
 		Vec4 uv;
-		ANKI_CHECK(el.getVec4(uv));
+		ANKI_CHECK(el.getNumbers(uv));
 		m_subTexes[subTexesCount].m_uv = {{uv[0], uv[1], uv[2], uv[3]}};
 
 		names += name.getLength() + 1;

+ 1 - 1
src/anki/util/CMakeLists.txt

@@ -1,5 +1,5 @@
 set(SOURCES Assert.cpp Functions.cpp File.cpp Filesystem.cpp Memory.cpp System.cpp HighRezTimer.cpp ThreadPool.cpp
-	ThreadHive.cpp Hash.cpp Logger.cpp String.cpp StringList.cpp Tracer.cpp Serializer.cpp)
+	ThreadHive.cpp Hash.cpp Logger.cpp String.cpp StringList.cpp Tracer.cpp Serializer.cpp Xml.cpp)
 
 if(LINUX OR ANDROID OR MACOS)
 	set(SOURCES ${SOURCES} HighRezTimerPosix.cpp FilesystemPosix.cpp ThreadPosix.cpp)

+ 173 - 0
src/anki/util/Xml.cpp

@@ -0,0 +1,173 @@
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/util/Xml.h>
+#include <anki/util/File.h>
+#include <anki/util/Logger.h>
+
+namespace anki
+{
+
+Error XmlElement::check() const
+{
+	Error err = Error::NONE;
+	if(m_el == nullptr)
+	{
+		ANKI_UTIL_LOGE("Empty element");
+		err = Error::USER_DATA;
+	}
+
+	return err;
+}
+
+Error XmlElement::getText(CString& out) const
+{
+	ANKI_CHECK(check());
+	out = (m_el->GetText()) ? CString(m_el->GetText()) : CString();
+	return Error::NONE;
+}
+
+Error XmlElement::getChildElementOptional(CString name, XmlElement& out) const
+{
+	const Error err = check();
+	if(!err)
+	{
+		out = XmlElement(m_el->FirstChildElement(&name[0]), m_alloc);
+	}
+	else
+	{
+		out = XmlElement();
+	}
+
+	return err;
+}
+
+Error XmlElement::getChildElement(CString name, XmlElement& out) const
+{
+	Error err = check();
+	if(err)
+	{
+		out = XmlElement();
+		return err;
+	}
+
+	err = getChildElementOptional(name, out);
+	if(err)
+	{
+		return err;
+	}
+
+	if(!out)
+	{
+		ANKI_UTIL_LOGE("Cannot find tag: %s", &name[0]);
+		err = Error::USER_DATA;
+	}
+
+	return err;
+}
+
+Error XmlElement::getNextSiblingElement(CString name, XmlElement& out) const
+{
+	const Error err = check();
+	if(!err)
+	{
+		out = XmlElement(m_el->NextSiblingElement(&name[0]), m_alloc);
+	}
+	else
+	{
+		out = XmlElement();
+	}
+
+	return err;
+}
+
+Error XmlElement::getSiblingElementsCount(U32& out) const
+{
+	ANKI_CHECK(check());
+	const tinyxml2::XMLElement* el = m_el;
+
+	out = 0;
+	do
+	{
+		el = el->NextSiblingElement(m_el->Name());
+		++out;
+	} while(el);
+
+	out -= 1;
+
+	return Error::NONE;
+}
+
+Error XmlElement::getAttributeTextOptional(CString name, CString& out, Bool& attribPresent) const
+{
+	ANKI_CHECK(check());
+
+	const tinyxml2::XMLAttribute* attrib = m_el->FindAttribute(&name[0]);
+	if(!attrib)
+	{
+		attribPresent = false;
+		return Error::NONE;
+	}
+
+	attribPresent = true;
+
+	const char* value = attrib->Value();
+	if(value)
+	{
+		out = value;
+	}
+	else
+	{
+		out = CString();
+	}
+
+	return Error::NONE;
+}
+
+CString XmlDocument::XML_HEADER = R"(<?xml version="1.0" encoding="UTF-8" ?>)";
+
+Error XmlDocument::loadFile(CString filename, GenericMemoryPoolAllocator<U8> alloc)
+{
+	File file;
+	ANKI_CHECK(file.open(filename, FileOpenFlag::READ));
+
+	StringAuto text(alloc);
+	ANKI_CHECK(file.readAllText(text));
+
+	ANKI_CHECK(parse(text.toCString(), alloc));
+
+	return Error::NONE;
+}
+
+Error XmlDocument::parse(CString xmlText, GenericMemoryPoolAllocator<U8> alloc)
+{
+	m_alloc = alloc;
+
+	if(m_doc.Parse(&xmlText[0]))
+	{
+		ANKI_UTIL_LOGE(
+			"Cannot parse file. Reason: %s", ((m_doc.GetErrorStr1() == nullptr) ? "unknown" : m_doc.GetErrorStr1()));
+
+		return Error::USER_DATA;
+	}
+
+	return Error::NONE;
+}
+
+ANKI_USE_RESULT Error XmlDocument::getChildElement(CString name, XmlElement& out) const
+{
+	Error err = Error::NONE;
+	out = XmlElement(m_doc.FirstChildElement(&name[0]), m_alloc);
+
+	if(!out)
+	{
+		ANKI_UTIL_LOGE("Cannot find tag \"%s\"", &name[0]);
+		err = Error::USER_DATA;
+	}
+
+	return err;
+}
+
+} // end namespace anki

+ 216 - 0
src/anki/util/Xml.h

@@ -0,0 +1,216 @@
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/util/DynamicArray.h>
+#include <anki/util/String.h>
+#include <tinyxml2.h>
+#if !ANKI_TINYXML2
+#	error "Wrong tinyxml2 included"
+#endif
+
+namespace anki
+{
+
+/// @addtogroup util_file
+/// @{
+
+/// XML element.
+class XmlElement
+{
+	friend class XmlDocument;
+
+public:
+	XmlElement()
+		: m_el(nullptr)
+	{
+	}
+
+	XmlElement(const XmlElement& b)
+		: m_el(b.m_el)
+		, m_alloc(b.m_alloc)
+	{
+	}
+
+	/// If element has something return true
+	operator Bool() const
+	{
+		return m_el != nullptr;
+	}
+
+	/// Copy
+	XmlElement& operator=(const XmlElement& b)
+	{
+		m_el = b.m_el;
+		m_alloc = b.m_alloc;
+		return *this;
+	}
+
+	/// Return the text inside a tag. May return empty string.
+	ANKI_USE_RESULT Error getText(CString& out) const;
+
+	/// Return the text inside as a number.
+	template<typename T>
+	ANKI_USE_RESULT Error getNumber(T& out) const;
+
+	/// Get a number of numbers.
+	template<typename T>
+	ANKI_USE_RESULT Error getNumbers(DynamicArrayAuto<T>& out) const;
+
+	/// Get a fixed number of numbers.
+	/// @tparam TArray A type that should have operator[] and getSize() methods implemented.
+	template<typename TArray>
+	ANKI_USE_RESULT Error getNumbers(TArray& out) const;
+
+	/// Get a child element quietly
+	ANKI_USE_RESULT Error getChildElementOptional(CString name, XmlElement& out) const;
+
+	/// Get a child element and print error if not found
+	ANKI_USE_RESULT Error getChildElement(CString name, XmlElement& out) const;
+
+	/// Get the next element with the same name. Returns empty XmlElement if it reached the end of the list.
+	ANKI_USE_RESULT Error getNextSiblingElement(CString name, XmlElement& out) const;
+
+	/// Get the number of sibling elements of this node.
+	/// @note The sibling elements share the same name.
+	ANKI_USE_RESULT Error getSiblingElementsCount(U32& out) const;
+
+	/// @name Get attributes optional
+	/// @{
+
+	/// Get value of a string attribute. May return empty string.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
+	ANKI_USE_RESULT Error getAttributeTextOptional(CString name, CString& out, Bool& attribPresent) const;
+
+	/// Get the attribute's value as a series of numbers.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeNumbersOptional(
+		CString name, DynamicArrayAuto<T>& out, Bool& attribPresent) const;
+
+	/// Get the attribute's value as a series of numbers.
+	/// @tparam TArray A type that should have operator[] and getSize() methods implemented.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
+	template<typename TArray>
+	ANKI_USE_RESULT Error getAttributeNumbersOptional(CString name, TArray& out, Bool& attribPresent) const;
+
+	/// Get the attribute's value as a number.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeNumberOptional(CString name, T& out, Bool& attribPresent) const;
+	/// @}
+
+	/// @name Get attributes
+	/// @{
+
+	/// Get value of a string attribute. May return empty string.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	ANKI_USE_RESULT Error getAttributeText(CString name, CString& out) const
+	{
+		Bool found;
+		ANKI_CHECK(getAttributeTextOptional(name, out, found));
+		return throwAttribNotFoundError(name, found);
+	}
+
+	/// Get the attribute's value as a series of numbers.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeNumbers(CString name, DynamicArrayAuto<T>& out) const
+	{
+		Bool found;
+		ANKI_CHECK(getAttributeNumbersOptional(name, out, found));
+		return throwAttribNotFoundError(name, found);
+	}
+
+	/// Get the attribute's value as a series of numbers.
+	/// @tparam TArray A type that should have operator[] and getSize() methods implemented.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	template<typename TArray>
+	ANKI_USE_RESULT Error getAttributeNumbers(CString name, TArray& out) const
+	{
+		Bool found;
+		ANKI_CHECK(getAttributeNumbersOptional(name, out, found));
+		return throwAttribNotFoundError(name, found);
+	}
+
+	/// Get the attribute's value as a number.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeNumber(CString name, T& out) const
+	{
+		Bool found;
+		ANKI_CHECK(getAttributeNumberOptional(name, out, found));
+		return throwAttribNotFoundError(name, found);
+	}
+	/// @}
+
+private:
+	const tinyxml2::XMLElement* m_el;
+	GenericMemoryPoolAllocator<U8> m_alloc;
+
+	XmlElement(const tinyxml2::XMLElement* el, GenericMemoryPoolAllocator<U8> alloc)
+		: m_el(el)
+		, m_alloc(alloc)
+	{
+	}
+
+	ANKI_USE_RESULT Error check() const;
+
+	template<typename T>
+	ANKI_USE_RESULT Error parseNumbers(CString txt, DynamicArrayAuto<T>& out) const;
+
+	template<typename TArray>
+	ANKI_USE_RESULT Error parseNumbers(CString txt, TArray& out) const;
+
+	ANKI_USE_RESULT Error throwAttribNotFoundError(CString attrib, Bool found) const
+	{
+		if(!found)
+		{
+			ANKI_UTIL_LOGE("Attribute not found \"%s\"", &attrib[0]);
+			return Error::USER_DATA;
+		}
+		else
+		{
+			return Error::NONE;
+		}
+	}
+};
+
+/// XML document.
+class XmlDocument
+{
+public:
+	static CString XML_HEADER;
+
+	/// Parse from a file.
+	ANKI_USE_RESULT Error loadFile(CString filename, GenericMemoryPoolAllocator<U8> alloc);
+
+	/// Parse from a CString.
+	ANKI_USE_RESULT Error parse(CString xmlText, GenericMemoryPoolAllocator<U8> alloc);
+
+	ANKI_USE_RESULT Error getChildElement(CString name, XmlElement& out) const;
+
+private:
+	tinyxml2::XMLDocument m_doc;
+	GenericMemoryPoolAllocator<U8> m_alloc;
+};
+/// @}
+
+} // end namespace anki
+
+#include <anki/util/Xml.inl.h>

+ 172 - 0
src/anki/util/Xml.inl.h

@@ -0,0 +1,172 @@
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/util/Xml.h>
+#include <anki/util/StringList.h>
+
+namespace anki
+{
+
+template<typename T>
+Error XmlElement::getNumber(T& out) const
+{
+	ANKI_CHECK(check());
+
+	const char* txt = m_el->GetText();
+	if(txt != nullptr)
+	{
+		ANKI_CHECK(CString(txt).toNumber(out));
+	}
+	else
+	{
+		ANKI_UTIL_LOGE("Failed to return number. Element: %s", m_el->Value());
+		return Error::USER_DATA;
+	}
+
+	return Error::NONE;
+}
+
+template<typename T>
+Error XmlElement::getNumbers(DynamicArrayAuto<T>& out) const
+{
+	CString txt;
+	ANKI_CHECK(getText(txt));
+
+	if(txt)
+	{
+		return parseNumbers(txt, out);
+	}
+	else
+	{
+		out.destroy();
+		return Error::NONE;
+	}
+}
+
+template<typename TArray>
+Error XmlElement::getNumbers(TArray& out) const
+{
+	CString txt;
+	ANKI_CHECK(getText(txt));
+	return parseNumbers(txt, out);
+}
+
+template<typename T>
+Error XmlElement::getAttributeNumbersOptional(CString name, DynamicArrayAuto<T>& out, Bool& attribPresent) const
+{
+	CString txtVal;
+	ANKI_CHECK(getAttributeTextOptional(name, txtVal, attribPresent));
+
+	if(txtVal && attribPresent)
+	{
+		return parseNumbers(txtVal, out);
+	}
+	else
+	{
+		return Error::NONE;
+	}
+}
+
+template<typename TArray>
+Error XmlElement::getAttributeNumbersOptional(CString name, TArray& out, Bool& attribPresent) const
+{
+	CString txtVal;
+	ANKI_CHECK(getAttributeTextOptional(name, txtVal, attribPresent));
+
+	if(txtVal && attribPresent)
+	{
+		return parseNumbers(txtVal, out);
+	}
+	else
+	{
+		return Error::NONE;
+	}
+}
+
+template<typename T>
+Error XmlElement::getAttributeNumberOptional(CString name, T& out, Bool& attribPresent) const
+{
+	DynamicArrayAuto<T> arr(m_alloc);
+	ANKI_CHECK(getAttributeNumbersOptional(name, arr, attribPresent));
+
+	if(attribPresent)
+	{
+		if(arr.getSize() != 1)
+		{
+			ANKI_UTIL_LOGE("Expecting one element for attrib: %s", &name[0]);
+			return Error::USER_DATA;
+		}
+
+		out = arr[0];
+	}
+
+	return Error::NONE;
+}
+
+template<typename T>
+Error XmlElement::parseNumbers(CString txt, DynamicArrayAuto<T>& out) const
+{
+	ANKI_ASSERT(txt);
+	ANKI_ASSERT(m_el);
+
+	StringListAuto list(m_alloc);
+	list.splitString(txt, ' ');
+
+	out.destroy();
+	out.create(U32(list.getSize()));
+
+	Error err = Error::NONE;
+	auto it = list.getBegin();
+	auto end = list.getEnd();
+	U32 i = 0;
+	while(it != end && !err)
+	{
+		err = it->toNumber(out[i++]);
+		++it;
+	}
+
+	if(err)
+	{
+		ANKI_UTIL_LOGE("Failed to covert to numbers the element: %s", m_el->Value());
+	}
+
+	return err;
+}
+
+template<typename TArray>
+Error XmlElement::parseNumbers(CString txt, TArray& out) const
+{
+	ANKI_ASSERT(!txt.isEmpty());
+	ANKI_ASSERT(m_el);
+
+	StringListAuto list(m_alloc);
+	list.splitString(txt, ' ');
+	const PtrSize listSize = list.getSize();
+
+	if(listSize != out.getSize())
+	{
+		ANKI_UTIL_LOGE("Wrong number of elements for element: %s", m_el->Value());
+		return Error::USER_DATA;
+	}
+
+	Error err = Error::NONE;
+	auto it = list.getBegin();
+	auto end = list.getEnd();
+	U32 i = 0;
+	while(it != end && !err)
+	{
+		err = it->toNumber(out[i++]);
+		++it;
+	}
+
+	if(err)
+	{
+		ANKI_UTIL_LOGE("Failed to covert to numbers the element: %s", m_el->Value());
+	}
+
+	return err;
+}
+
+} // end namespace anki