Ver código fonte

- Work on UI
- Adding code for HashMap
- Fixing the unit tests

Panagiotis Christopoulos Charitos 10 anos atrás
pai
commit
5a70f90013

+ 1 - 3
include/anki/resource/GenericResource.h

@@ -16,9 +16,7 @@ namespace anki {
 class GenericResource: public ResourceObject
 {
 public:
-	GenericResource(ResourceManager* manager)
-		: ResourceObject(manager)
-	{}
+	GenericResource(ResourceManager* manager);
 
 	~GenericResource();
 

+ 4 - 4
include/anki/ui/Canvas.h

@@ -31,7 +31,7 @@ public:
 
 	/// @name Input injection methods.
 	/// @{
-	void injectMouseMove(const Vec2& pos);
+	void injectMouseMove(const UVec2& pos);
 
 	void injectKeyDown(KeyCode key);
 
@@ -48,9 +48,9 @@ public:
 	/// Paint the widgets (if needed). Call after update.
 	void paint();
 
-	void setSize(const Vec2& size);
+	void setSize(const UVec2& size);
 
-	const Vec2& getSize() const
+	const UVec2& getSize() const
 	{
 		return m_size;
 	}
@@ -82,7 +82,7 @@ private:
 	Atomic<I32> m_markedForDeletionCount = {0};
 	WeakPtr<Widget> m_rootWidget;
 
-	Vec2 m_size; ///< Virtual size of canvas.
+	UVec2 m_size; ///< Virtual size of canvas.
 	Bool8 m_debugDrawEnabled = false;
 };
 /// @}

+ 8 - 0
include/anki/ui/Common.h

@@ -23,6 +23,14 @@ using UiAllocator = GenericMemoryPoolAllocator<U8>;
 /// Color.
 using Color = Vec4;
 
+/// Rectangle
+class Rect
+{
+public:
+	UVec2 m_min;
+	UVec2 m_max;
+};
+
 /// Used in widget classes.
 #define ANKI_WIDGET \
 	friend class Canvas;

+ 8 - 5
include/anki/ui/Font.h

@@ -15,15 +15,16 @@ namespace anki {
 class FontCharInfo
 {
 public:
-	Vec2 m_advance; ///< Char advance.
-	Vec2 m_size; ///< Bitmap size in pixels.
-	Vec2 m_offset; ///< Left and top bearing.
+	UVec2 m_advance; ///< Char advance.
+	Rect m_imageRect; ///< The rect inside the image atlas.
+	IVec2 m_offset; ///< Left and top bearing.
 };
 
 /// Font class.
 class Font
 {
 	friend class IntrusivePtr<Font>;
+	friend IntrusivePtr<Font>::Deleter;
 
 public:
 	Font(UiInterface* interface)
@@ -35,6 +36,7 @@ public:
 	/// Initialize the font.
 	ANKI_USE_RESULT Error init(const CString& filename, U32 fontHeight);
 
+#ifdef ANKI_BUILD
 	/// Get info for a character.
 	const FontCharInfo& getCharInfo(char c) const
 	{
@@ -45,8 +47,9 @@ public:
 	/// Get font image atlas.
 	const UiImagePtr& getImage() const
 	{
-		return m_tex;
+		return m_img;
 	}
+#endif
 
 private:
 	static const char FIRST_CHAR = ' ';
@@ -55,7 +58,7 @@ private:
 
 	WeakPtr<UiInterface> m_interface;
 	Atomic<I32> m_refcount = {0};
-	UiImagePtr m_tex;
+	UiImagePtr m_img;
 	Array<FontCharInfo, CHAR_COUNT> m_chars;
 
 	Atomic<I32>& getRefcount()

+ 19 - 17
include/anki/ui/UiInterface.h

@@ -9,9 +9,13 @@
 
 namespace anki {
 
+class UiImage;
+
 /// @addtogroup ui
 /// @{
 
+using UiImagePtr = IntrusivePtr<UiImage>;
+
 /// Interfacing UI system with external systems.
 class UiInterface
 {
@@ -30,14 +34,12 @@ public:
 	/// @name Image related methods.
 	/// @{
 	virtual ANKI_USE_RESULT Error loadImage(
-		const CString& filename, IntrusivePtr<UiImage>& img) = 0;
+		const CString& filename, UiImagePtr& img) = 0;
 
-#if 0
-	virtual ANKI_USE_RESULT Error createRgba8Image(
-		const void* data, PtrSize dataSize, const Vec2& size,
-		IntrusivePtr<UiImage>& img) = 0;
+	/// Create a 8bit image. Used for fonts.
+	virtual ANKI_USE_RESULT Error createR8Image(const SArray<U8>& data,
+		const UVec2& size, UiImagePtr& img) = 0;
 	/// @}
-#endif
 
 	/// @name Misc methods.
 	/// @{
@@ -47,12 +49,11 @@ public:
 
 	/// @name Painting related methods.
 	/// @{
-#if 0
-	virtual void drawImage(ImagePtr image, const Rect& uvs,
-		const Rect& drawingRect) = 0;
-#endif
+	virtual void drawImage(UiImagePtr image, const Rect& uvs,
+		const Rect& drawingRect, const UVec2& canvasSize) = 0;
 
-	virtual void drawLines(const SArray<Vec2>& lines, const Color& color) = 0;
+	virtual void drawLines(const SArray<UVec2>& lines, const Color& color,
+		const UVec2& canvasSize) = 0;
 	/// @}
 
 protected:
@@ -62,6 +63,9 @@ protected:
 /// UI image interface.
 class UiImage
 {
+	friend class IntrusivePtr<UiImage>;
+	friend IntrusivePtr<UiImage>::Deleter;
+
 public:
 	UiImage(UiInterface* interface)
 		: m_alloc(interface->getAllocator())
@@ -69,6 +73,10 @@ public:
 
 	virtual ~UiImage() = default;
 
+private:
+	UiAllocator m_alloc;
+	Atomic<I32> m_refcount = {0};
+
 	Atomic<I32>& getRefcount()
 	{
 		return m_refcount;
@@ -78,13 +86,7 @@ public:
 	{
 		return m_alloc;
 	}
-
-private:
-	UiAllocator m_alloc;
-	Atomic<I32> m_refcount = {0};
 };
-
-using UiImagePtr = IntrusivePtr<UiImage>;
 /// @}
 
 } // end namespace anki

+ 11 - 3
include/anki/ui/UiInterfaceImpl.h

@@ -27,7 +27,8 @@ public:
 		: UiImage(i)
 	{}
 
-	TextureResourcePtr m_texture;
+	TextureResourcePtr m_resource;
+	TexturePtr m_texture;
 };
 
 /// Implements UiInterface.
@@ -40,14 +41,21 @@ public:
 
 	ANKI_USE_RESULT Error init(GrManager* gr, ResourceManager* rc);
 
-	void drawLines(const SArray<Vec2>& positions, const Color& color) override;
-
 	ANKI_USE_RESULT Error loadImage(
 		const CString& filename, IntrusivePtr<UiImage>& img) override;
 
+	ANKI_USE_RESULT Error createR8Image(const SArray<U8>& data,
+		const UVec2& size, IntrusivePtr<UiImage>& img) override;
+
 	ANKI_USE_RESULT Error readFile(const CString& filename,
 		DArrayAuto<U8>& data) override;
 
+	void drawImage(UiImagePtr image, const Rect& uvs,
+		const Rect& drawingRect, const UVec2& canvasSize) override;
+
+	void drawLines(const SArray<UVec2>& positions, const Color& color,
+		const UVec2& canvasSize) override;
+
 	void beginRendering(CommandBufferPtr cmdb);
 
 	void endRendering();

+ 11 - 11
include/anki/ui/Widget.h

@@ -46,28 +46,28 @@ public:
 	{}
 
 	/// Set position relatively to the parent.
-	void setRelativePosition(const Vec2& pos);
+	void setRelativePosition(const UVec2& pos);
 
-	const Vec2& getRelativePosition() const
+	const UVec2& getRelativePosition() const
 	{
 		return m_posLocal;
 	}
 
-	const Vec2& getCanvasPosition() const
+	const UVec2& getCanvasPosition() const
 	{
 		return m_posCanvas;
 	}
 
 	/// Set prefered size.
-	void setSize(const Vec2& size);
+	void setSize(const UVec2& size);
 
-	const Vec2& getSize() const
+	const UVec2& getSize() const
 	{
 		return m_size;
 	}
 
 	/// Set the limts of size. The widget cannot exceede those.
-	void setSizeLimits(const Vec2& min, const Vec2& max);
+	void setSizeLimits(const UVec2& min, const UVec2& max);
 
 	void addWidget(Widget* widget)
 	{
@@ -105,12 +105,12 @@ private:
 		NEEDS_REPAINT = 1 << 1
 	};
 
-	Vec2 m_minSize = Vec2(0.0);
-	Vec2 m_maxSize = Vec2(MAX_F32);
-	Vec2 m_size = Vec2(0.0);
+	UVec2 m_minSize = UVec2(0u);
+	UVec2 m_maxSize = UVec2(MAX_U32);
+	UVec2 m_size = UVec2(0u);
 
-	Vec2 m_posLocal = Vec2(0.0); ///< Local space.
-	Vec2 m_posCanvas = Vec2(0.0); ///< World space.
+	UVec2 m_posLocal = UVec2(0u); ///< Local space.
+	UVec2 m_posCanvas = UVec2(0u); ///< World space.
 
 	Bitset<U8> m_flags;
 };

+ 164 - 64
include/anki/util/HashMap.h

@@ -3,16 +3,19 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#ifndef ANKI_UTIL_HASH_MAP_H
-#define ANKI_UTIL_HASH_MAP_H
+#pragma once
 
 #include "anki/util/Allocator.h"
 #include "anki/util/NonCopyable.h"
+#include "anki/util/Ptr.h"
+
+namespace anki {
 
 /// @addtogroup util_private
 /// @{
 
-/// HashMap node.
+/// HashMap node. It's not a traditional bucket because it doesn't contain more
+/// than one node.
 template<typename T>
 class HashMapNode
 {
@@ -21,46 +24,45 @@ public:
 
 	Value m_value;
 	U64 m_hash;
-	HashMapNode* m_prev = nullptr;
-	HashMapNode* m_next = nullptr;
+	HashMapNode* m_parent = nullptr;
+	HashMapNode* m_left = nullptr;
+	HashMapNode* m_right = nullptr;
 
 	template<typename... TArgs>
 	HashMapNode(TArgs&&... args)
-	:	m_value(std::forward<TArgs>(args)...)
+		: m_value(args...)
 	{}
 };
 
-/// HashMap bidirectional iterator.
-template<typename TNodePointer, typename TValuePointer, 
-	typename TValueReference, typename THashMapPointer>
+/// HashMap forward-only iterator.
+template<typename TNodePointer, typename TValuePointer,
+	typename TValueReference>
 class HashMapIterator
 {
 public:
-	TNodePointer m_node = nullptr;
-	THashMapPointer m_map = nullptr; ///< Used to go back from the end
+	TNodePointer m_node;
 
-	HashMapIterator() = default;
+	/// Default constructor.
+	HashMapIterator()
+		: m_node(nullptr)
+	{}
 
+	/// Copy.
 	HashMapIterator(const HashMapIterator& b)
-	:	m_node(b.m_node),
-		m_map(b.m_map)
+		: m_node(b.m_node)
 	{}
 
 	/// Allow conversion from iterator to const iterator.
-	template<typename YNodePointer, typename YValuePointer, 
-		typename YValueReference, typename YHashMap>
-	HashMapIterator(const HashMapIterator<YNodePointer, 
-		YValuePointer, YValueReference, YHashMap>& b)
-	:	m_node(b.m_node),
-		m_map(b.m_map)
+	template<typename YNodePointer, typename YValuePointer,
+		typename YValueReference>
+	HashMapIterator(const HashMapIterator<YNodePointer, YValuePointer,
+		YValueReference>& b)
+		: m_node(b.m_node)
 	{}
 
-	HashMapIterator(TNodePointer node, THashMapPointer list)
-	:	m_node(node),
-		m_map(list)
-	{
-		ANKI_ASSERT(list);
-	}
+	HashMapIterator(TNodePointer node)
+		: m_node(node)
+	{}
 
 	TValueReference operator*() const
 	{
@@ -77,7 +79,34 @@ public:
 	HashMapIterator& operator++()
 	{
 		ANKI_ASSERT(m_node);
-		m_node = m_node->m_next;
+		TNodePointer node = m_node;
+
+		if(node->m_left)
+		{
+			node = node->m_left;
+		}
+		else if(node->m_right)
+		{
+			node = node->m_right;
+		}
+		else
+		{
+			// Node without children
+			TNodePointer prevNode = node;
+			node = node->m_parent;
+			while(node)
+			{
+				if(node->m_right && node->m_right != prevNode)
+				{
+					node = node->m_right;
+					break;
+				}
+				prevNode = node;
+				node = node->m_parent;
+			}
+		}
+
+		m_node = node;
 		return *this;
 	}
 
@@ -89,16 +118,6 @@ public:
 		return out;
 	}
 
-	HashMapIterator& operator--();
-
-	HashMapIterator operator--(int)
-	{
-		ANKI_ASSERT(m_node);
-		HashMapIterator out = *this;
-		--(*this);
-		return out;
-	}
-
 	HashMapIterator operator+(U n) const
 	{
 		HashMapIterator it = *this;
@@ -109,16 +128,6 @@ public:
 		return it;
 	}
 
-	HashMapIterator operator-(U n) const
-	{
-		HashMapIterator it = *this;
-		while(n-- != 0)
-		{
-			--it;
-		}
-		return it;
-	}
-
 	HashMapIterator& operator+=(U n)
 	{
 		while(n-- != 0)
@@ -128,19 +137,8 @@ public:
 		return *this;
 	}
 
-	HashMapIterator& operator-=(U n)
-	{
-		while(n-- != 0)
-		{
-			--(*this);
-		}
-		return *this;
-	}
-
 	Bool operator==(const HashMapIterator& b) const
 	{
-		ANKI_ASSERT(m_list == b.m_list 
-			&& "Comparing iterators from different lists");
 		return m_node == b.m_node;
 	}
 
@@ -155,22 +153,29 @@ public:
 /// @{
 
 /// Hash map template.
-template<typename T>
-class HashMap
+template<typename T, typename THasher, typename TCompare,
+	typename TNode = HashMapNode<T>>
+class HashMap: public NonCopyable
 {
 public:
 	using Value = T;
-	using Node = HashMapNode<Value>;
+	using Node = TNode;
 	using Reference = Value&;
 	using ConstReference = const Value&;
 	using Pointer = Value*;
 	using ConstPointer = const Value*;
+	using Iterator = HashMapIterator<Node*, Pointer, Reference>;
+	using ConstIterator =
+		HashMapIterator<const Node*, ConstPointer, ConstReference>;
 
-	HashMap() = default;
+	/// Default constructor.
+	HashMap()
+		: m_root(nullptr)
+	{}
 
 	/// Move.
 	HashMap(HashMap&& b)
-	:	HashMap()
+		: HashMap()
 	{
 		move(b);
 	}
@@ -189,12 +194,99 @@ public:
 		return *this;
 	}
 
+	/// Destroy the list.
+	template<typename TAllocator>
+	void destroy(TAllocator alloc);
+
+	/// Get begin.
+	Iterator getBegin()
+	{
+		return Iterator(m_root);
+	}
+
+	/// Get begin.
+	ConstIterator getBegin() const
+	{
+		return ConstIterator(m_root);
+	}
+
+	/// Get end.
+	Iterator getEnd()
+	{
+		return Iterator();
+	}
+
+	/// Get end.
+	ConstIterator getEnd() const
+	{
+		return ConstIterator();
+	}
+
+	/// Get begin.
+	Iterator begin()
+	{
+		return getBegin();
+	}
+
+	/// Get begin.
+	ConstIterator begin() const
+	{
+		return getBegin();
+	}
+
+	/// Get end.
+	Iterator end()
+	{
+		return getEnd();
+	}
+
+	/// Get end.
+	ConstIterator end() const
+	{
+		return getEnd();
+	}
+
 	/// Return true if map is empty.
 	Bool isEmpty() const
 	{
 		return m_root == nullptr;
 	}
 
+	/// Copy an element in the map.
+	template<typename TAllocator>
+	void pushBack(TAllocator alloc, ConstReference x)
+	{
+		Node* node = alloc.template newInstance<Node>(x);
+		node->m_hash = THasher()(node->m_value);
+		pushBackNode(node);
+	}
+
+	/// Copy an element in the map.
+	template<typename TAllocator>
+	void pushBack(Pointer x)
+	{
+		ANKI_ASSERT(x);
+		//static_assert(typeid(x) == );
+		pushBackNode(x);
+	}
+
+	/// Construct an element inside the map.
+	template<typename TAllocator, typename... TArgs>
+	void emplaceBack(TAllocator alloc, TArgs&&... args)
+	{
+		Node* node = alloc.template newInstance<Node>(
+			std::forward<TArgs>(args)...);
+		node->m_hash = THasher()(node->m_value);
+		pushBackNode(node);
+	}
+
+	/// Erase element.
+	template<typename TAllocator>
+	void erase(TAllocator alloc, Iterator it);
+
+	/// Find item.
+	Iterator find(const Value& a);
+
 private:
 	Node* m_root = nullptr;
 
@@ -204,8 +296,16 @@ private:
 		m_root = b.m_root;
 		b.m_root = nullptr;
 	}
+
+	void pushBackNode(Node* node);
+
+	Node* findInternal(Node* node, const Value& a, U64 aHash);
+
+	template<typename TAllocator>
+	void destroyInternal(TAllocator alloc, Node* node);
 };
 /// @}
 
-#endif
+} // end namespace anki
 
+#include "anki/util/HashMap.inl.h"

+ 205 - 0
include/anki/util/HashMap.inl.h

@@ -0,0 +1,205 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+namespace anki {
+
+//==============================================================================
+template<typename T, typename THasher, typename TCompare, typename TNode>
+template<typename TAllocator>
+void HashMap<T, THasher, TCompare, TNode>::destroy(TAllocator alloc)
+{
+	if(m_root)
+	{
+		destroyInternal(alloc, m_root);
+		m_root = nullptr;
+	}
+}
+
+//==============================================================================
+template<typename T, typename THasher, typename TCompare, typename TNode>
+template<typename TAllocator>
+void HashMap<T, THasher, TCompare, TNode>::destroyInternal(
+	TAllocator alloc, Node* node)
+{
+	ANKI_ASSERT(node);
+
+	if(node->m_right)
+	{
+		destroyInternal(alloc, node->m_right);
+	}
+
+	if(node->m_left)
+	{
+		destroyInternal(alloc, node->m_left);
+	}
+
+	alloc.deleteInstance(node);
+}
+
+//==============================================================================
+template<typename T, typename THasher, typename TCompare, typename TNode>
+void HashMap<T, THasher, TCompare, TNode>::pushBackNode(Node* node)
+{
+	if(ANKI_UNLIKELY(!m_root))
+	{
+		m_root = node;
+		return;
+	}
+
+	const U64 hash = node->m_hash;
+	Node* it = m_root;
+	while(1)
+	{
+		const U64 nhash = it->m_hash;
+		if(hash > nhash)
+		{
+			// Go to right
+			if(it->m_right)
+			{
+				it = it->m_right;
+			}
+			else
+			{
+				it->m_right = node;
+				node->m_parent = it;
+				break;
+			}
+		}
+		else
+		{
+			ANKI_ASSERT(hash != nhash && "Not supported");
+			// Go to left
+			if(it->m_left)
+			{
+				it = it->m_left;
+			}
+			else
+			{
+				it->m_left = node;
+				node->m_parent = it;
+				break;
+			}
+		}
+	}
+}
+
+//==============================================================================
+template<typename T, typename THasher, typename TCompare, typename TNode>
+typename HashMap<T, THasher, TCompare, TNode>::Iterator
+	HashMap<T, THasher, TCompare, TNode>::find(const Value& a)
+{
+	if(ANKI_UNLIKELY(!m_root))
+	{
+		Iterator(nullptr);
+	}
+
+	U64 hash = THasher()(a);
+	return Iterator(findInternal(m_root, a, hash));
+}
+
+//==============================================================================
+template<typename T, typename THasher, typename TCompare, typename TNode>
+TNode* HashMap<T, THasher, TCompare, TNode>::findInternal(Node* node,
+	const Value& a, U64 aHash)
+{
+	ANKI_ASSERT(node);
+	if(node->m_hash == aHash)
+	{
+		// Found
+		ANKI_ASSERT(TCompare()(node->m_value, a)
+			&& "Doesn't accept collisions");
+		return node;
+	}
+
+	if(aHash < node->m_hash)
+	{
+		node = node->m_left;
+	}
+	else
+	{
+		node = node->m_right;
+	}
+
+	if(node)
+	{
+		return findInternal(node, a, aHash);
+	}
+
+	return node;
+}
+
+//==============================================================================
+template<typename T, typename THasher, typename TCompare, typename TNode>
+template<typename TAllocator>
+void HashMap<T, THasher, TCompare, TNode>::erase(TAllocator alloc, Iterator it)
+{
+	Node* del = it.m_node;
+	ANKI_ASSERT(del);
+	Node* parent = del->m_parent;
+	Node* left = del->m_left;
+	Node* right = del->m_right;
+
+	alloc.deleteInstance(del);
+
+	if(parent)
+	{
+		// If it has a parent then remove the connection to the parent and
+		// insert left and right like regular nodes
+
+		if(parent->m_left == del)
+		{
+			parent->m_left = nullptr;
+		}
+		else
+		{
+			ANKI_ASSERT(parent->m_right == del);
+			parent->m_right = nullptr;
+		}
+
+		if(left)
+		{
+			ANKI_ASSERT(left->m_parent == del);
+			left->m_parent = nullptr;
+			pushBackNode(left);
+		}
+
+		if(right)
+		{
+			ANKI_ASSERT(right->m_parent == del);
+			right->m_parent = nullptr;
+			pushBackNode(right);
+		}
+	}
+	else
+	{
+		// It's the root node. Make arbitrarily the left root and add the right
+
+		ANKI_ASSERT(del == m_root && "It must be the root");
+
+		if(left)
+		{
+			left->m_parent = nullptr;
+			m_root = left;
+
+			if(right)
+			{
+				right->m_parent = nullptr;
+				pushBackNode(right);
+			}
+		}
+		else
+		{
+			if(right)
+			{
+				right->m_parent = nullptr;
+			}
+
+			m_root = right;
+		}
+	}
+}
+
+} // end namespace anki
+

+ 2 - 4
include/anki/util/List.h

@@ -250,15 +250,13 @@ public:
 	/// Get end.
 	Iterator getEnd()
 	{
-		Iterator it(nullptr, this);
-		return it;
+		return Iterator(nullptr, this);
 	}
 
 	/// Get end.
 	ConstIterator getEnd() const
 	{
-		ConstIterator it(nullptr, this);
-		return it;
+		return ConstIterator(nullptr, this);
 	}
 
 	/// Get begin.

+ 2 - 1
include/anki/util/Ptr.h

@@ -222,6 +222,7 @@ class UniquePtr: public PtrBase<T>
 {
 public:
 	using Base = PtrBase<T>;
+	using Deleter = TDeleter;
 
 	UniquePtr()
 		: Base()
@@ -291,6 +292,7 @@ class IntrusivePtr: public PtrBase<T>
 
 public:
 	using Base = PtrBase<T>;
+	using Deleter = TDeleter;
 
 	IntrusivePtr()
 		: Base()
@@ -369,4 +371,3 @@ private:
 
 } // end namespace anki
 
-

+ 1 - 1
src/renderer/CMakeLists.txt

@@ -2,4 +2,4 @@ file(GLOB_RECURSE ANKI_R_SOURCES *.cpp)
 file(GLOB_RECURSE ANKI_R_HEADERS *.h)
 
 add_library(ankirenderer ${ANKI_R_SOURCES} ${ANKI_R_HEADERS})
-target_link_libraries(ankirenderer ankicore)
+target_link_libraries(ankirenderer ankicore ankiui)

+ 1 - 1
src/resource/CMakeLists.txt

@@ -2,4 +2,4 @@ file(GLOB_RECURSE ANKI_RSRC_SOURCES *.cpp)
 file(GLOB_RECURSE ANKI_RSRC_HEADERS *.h)
 
 add_library(ankiresource ${ANKI_RSRC_SOURCES} ${ANKI_RSRC_HEADERS})
-target_link_libraries(ankiresource ankiphysics ankigr)
+target_link_libraries(ankiresource ankiphysics ankigr ankirenderer)

+ 5 - 0
src/resource/GenericResource.cpp

@@ -7,6 +7,11 @@
 
 namespace anki {
 
+//==============================================================================
+GenericResource::GenericResource(ResourceManager* manager)
+	: ResourceObject(manager)
+{}
+
 //==============================================================================
 GenericResource::~GenericResource()
 {

+ 1 - 1
src/scene/CMakeLists.txt

@@ -3,4 +3,4 @@ file(GLOB_RECURSE ANKI_SCENE_HEADERS *.h)
 
 add_library(ankiscene ${ANKI_SCENE_SOURCES} ${ANKI_SCENE_HEADERS})
 
-target_link_libraries(ankiscene ankicore)
+target_link_libraries(ankiscene ankicore ankievent)

+ 1 - 0
src/ui/CMakeLists.txt

@@ -1,3 +1,4 @@
 file(GLOB ANKI_UI_SOURCES *.cpp)
 
 add_library(ankiui ${ANKI_UI_SOURCES})
+target_link_libraries(ankiui ankiresource)

+ 12 - 12
src/ui/Canvas.cpp

@@ -14,7 +14,7 @@ Canvas::Canvas(UiInterface* interface)
 {
 	m_rootWidget = newWidget<Widget>();
 
-	setSize(Vec2(128.0));
+	setSize(UVec2(128u));
 }
 
 //==============================================================================
@@ -40,15 +40,15 @@ void Canvas::paint()
 		if(m_debugDrawEnabled)
 		{
 			U c = 0;
-			const Vec2& pos = w.getCanvasPosition();
-			const Vec2& size = w.getSize();
+			const UVec2& pos = w.getCanvasPosition();
+			const UVec2& size = w.getSize();
 
-			Vec2 lb = pos / m_size * 2.0 - 1.0;
-			Vec2 rb = (pos + Vec2(size.x(), 0.0)) / m_size * 2.0 - 1.0;
-			Vec2 rt = (pos + size.x()) / m_size * 2.0 - 1.0;
-			Vec2 lt = (pos + Vec2(0.0, size.y())) / m_size * 2.0 - 1.0;
+			UVec2 lb = pos;
+			UVec2 rb = pos + UVec2(size.x(), 0u);
+			UVec2 rt = pos + size;
+			UVec2 lt = pos + UVec2(0u, size.y());
 
-			Array<Vec2, 2 * 4> positions;
+			Array<UVec2, 2 * 4> positions;
 			positions[c++] = lb;
 			positions[c++] = rb;
 			positions[c++] = rb;
@@ -59,8 +59,8 @@ void Canvas::paint()
 			positions[c++] = lb;
 
 			m_interface->drawLines(
-				SArray<Vec2>(&positions[0], positions.getSize()),
-				Vec4(1.0, 0.0, 0.0, 1.0));
+				SArray<UVec2>(&positions[0], positions.getSize()),
+				Vec4(1.0, 0.0, 0.0, 1.0), m_size);
 		}
 
 		return ErrorCode::NONE;
@@ -71,10 +71,10 @@ void Canvas::paint()
 }
 
 //==============================================================================
-void Canvas::setSize(const Vec2& size)
+void Canvas::setSize(const UVec2& size)
 {
 	m_size = size;
-	m_rootWidget->setRelativePosition(Vec2(0.0));
+	m_rootWidget->setRelativePosition(UVec2(0u));
 	m_rootWidget->setSize(size);
 }
 

+ 79 - 23
src/ui/Font.cpp

@@ -13,40 +13,25 @@ namespace anki {
 // Misc                                                                        =
 //==============================================================================
 
-static const char FIRST_CHAR = ' ';
-static const char LAST_CHAR = 127;
-
 class FtFont
 {
 public:
 	FT_Library m_lib = 0;
 	FT_Face m_face = 0;
 
-	ANKI_USE_RESULT Error calculateAtlasSize(UVec2& size);
-};
-
-//==============================================================================
-Error FtFont::calculateAtlasSize(UVec2& size)
-{
-	U width = 0, height = 0;
-
-	for(I c = FIRST_CHAR; c <= LAST_CHAR; ++c)
+	~FtFont()
 	{
-		if(FT_Load_Char(m_face, c, FT_LOAD_RENDER))
+		if(m_face)
 		{
-	    	ANKI_LOGE("Loading character '%c' failed", c);
-			return ErrorCode::USER_DATA;
+			FT_Done_Face(m_face);
 		}
 
-		width += m_face->glyph->bitmap.width;
-		height = max<U>(height, m_face->glyph->bitmap.rows);
+		if(m_lib)
+		{
+			FT_Done_FreeType(m_lib);
+		}
 	}
-
-	size.x() = width;
-	size.y() = height;
-
-	return ErrorCode::NONE;
-}
+};
 
 //==============================================================================
 // Font                                                                        =
@@ -87,6 +72,77 @@ Error Font::init(const CString& filename, U32 fontHeight)
 		return ErrorCode::FUNCTION_FAILED;
 	}
 
+	// Compute the atlas size
+	UVec2 imgSize(0u);
+	{
+		U width = 0, height = 0;
+
+		for(I c = FIRST_CHAR; c <= LAST_CHAR; ++c)
+		{
+			if(FT_Load_Char(ft.m_face, c, FT_LOAD_DEFAULT))
+			{
+				ANKI_LOGE("Loading character '%c' failed", c);
+				return ErrorCode::USER_DATA;
+			}
+
+			width += ft.m_face->glyph->bitmap.width;
+			height = max<U>(height, ft.m_face->glyph->bitmap.rows);
+		}
+
+		imgSize.x() = width;
+		imgSize.y() = height;
+	}
+
+	// Allocate bitmap for rasterization
+	DArrayAuto<U8> bitmap(getAllocator());
+	bitmap.create(imgSize.x() * imgSize.y());
+	memset(&bitmap[0], 0, bitmap.getSize());
+
+	// Rasterize image to memory and get some glyph info
+	{
+		U xOffset = 0;
+
+		for(I c = FIRST_CHAR; c <= LAST_CHAR; ++c)
+		{
+			if(FT_Load_Char(ft.m_face, c, FT_LOAD_RENDER))
+			{
+				ANKI_LOGE("Loading character '%c' failed", c);
+				return ErrorCode::USER_DATA;
+			}
+
+			const U w = ft.m_face->glyph->bitmap.width;
+			const U h = ft.m_face->glyph->bitmap.rows;
+			SArray<U8> srcBitmap(ft.m_face->glyph->bitmap.buffer, w * h);
+
+			// Copy
+			for(U y = 0; y < h; ++y)
+			{
+				for(U x = 0; x < w; ++x)
+				{
+					bitmap[y * w + (x + xOffset)] = srcBitmap[y * w + x];
+				}
+			}
+
+			// Get glyph info
+			FontCharInfo& inf = m_chars[c - FIRST_CHAR];
+			inf.m_imageRect.m_min = UVec2(xOffset, 0);
+			inf.m_imageRect.m_max = inf.m_imageRect.m_min + UVec2(w, h);
+
+			inf.m_advance = UVec2(ft.m_face->glyph->advance.x >> 6,
+				ft.m_face->glyph->advance.y >> 6);
+
+			inf.m_offset = IVec2(ft.m_face->glyph->bitmap_left,
+				ft.m_face->glyph->bitmap_top - I(h));
+
+			// Advance
+			xOffset += w;
+		}
+	}
+
+	// Create image
+	ANKI_CHECK(m_interface->createR8Image(
+		SArray<U8>(&bitmap[0], bitmap.getSize()), imgSize, m_img));
+
 	return ErrorCode::NONE;
 }
 

+ 62 - 5
src/ui/UiInterfaceImpl.cpp

@@ -133,8 +133,8 @@ void UiInterfaceImpl::endRendering()
 }
 
 //==============================================================================
-void UiInterfaceImpl::drawLines(
-	const SArray<Vec2>& positions, const Color& color)
+void UiInterfaceImpl::drawLines(const SArray<UVec2>& positions,
+	const Color& color, const UVec2& canvasSize)
 {
 	StageId stageId = StageId::LINES;
 
@@ -144,10 +144,11 @@ void UiInterfaceImpl::drawLines(
 	m_cmdb->bindResourceGroup(m_stages[StageId::LINES].m_rcGroups[m_timestamp]);
 	m_cmdb->drawArrays(positions.getSize(), 1, m_vertCounts[stageId]);
 
-	for(const Vec2& pos : positions)
+	for(const UVec2& pos : positions)
 	{
 		Vertex v;
-		v.m_pos = pos;
+		v.m_pos = Vec2(pos.x(), pos.y()) / Vec2(canvasSize.x(), canvasSize.y())
+			* 2.0 - 1.0;
 		v.m_uv = Vec2(0.0);
 		Color c = color * 255.0;
 		v.m_color = {{U8(c[0]), U8(c[1]), U8(c[2]), U8(c[3])}};
@@ -158,6 +159,15 @@ void UiInterfaceImpl::drawLines(
 	}
 }
 
+//==============================================================================
+void UiInterfaceImpl::drawImage(UiImagePtr image, const Rect& uvs,
+	const Rect& drawingRect, const UVec2& canvasSize)
+{
+	StageId stageId = StageId::TEXTURED_TRIANGLES;
+
+	ANKI_ASSERT(m_vertCounts[stageId] + 4 <= MAX_VERTS);
+}
+
 //==============================================================================
 Error UiInterfaceImpl::loadImage(
 	const CString& filename, IntrusivePtr<UiImage>& img)
@@ -165,7 +175,54 @@ Error UiInterfaceImpl::loadImage(
 	TextureResourcePtr texture;
 	ANKI_CHECK(m_rc->loadResource(filename, texture));
 	UiImageImpl* impl = getAllocator().newInstance<UiImageImpl>(this);
-	IntrusivePtr<UiImage> ptr(impl);
+	impl->m_resource = texture;
+	impl->m_texture = texture->getGrTexture();
+
+	img.reset(impl);
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+Error UiInterfaceImpl::createR8Image(const SArray<U8>& data, const UVec2& size,
+	IntrusivePtr<UiImage>& img)
+{
+	ANKI_ASSERT(data.getSize() == size.x() * size.y());
+
+	// Calc mip count
+	U s = min(size.x(), size.y());
+	U mipCount = 0;
+	while(s > 0)
+	{
+		++mipCount;
+		s /= 2;
+	}
+
+	// Allocate the texture
+	TextureInitializer tinit;
+	tinit.m_width = size.x();
+	tinit.m_height = size.y();
+	tinit.m_format = PixelFormat(ComponentFormat::R8, TransformFormat::UNORM);
+	tinit.m_mipmapsCount = mipCount;
+	tinit.m_sampling.m_minMagFilter = SamplingFilter::LINEAR;
+	tinit.m_sampling.m_mipmapFilter = SamplingFilter::LINEAR;
+
+	TexturePtr tex = m_gr->newInstance<Texture>(tinit);
+
+	// Load data
+	CommandBufferPtr cmdb = m_gr->newInstance<CommandBuffer>();
+	void* loadData = nullptr;
+	cmdb->textureUpload(tex, 0, 0, data.getSize(), loadData);
+	memcpy(loadData, &data[0], data.getSize());
+
+	// Gen mips
+	cmdb->generateMipmaps(tex);
+	cmdb->flush();
+
+	// Create the UiImage
+	UiImageImpl* impl = getAllocator().newInstance<UiImageImpl>(this);
+	impl->m_texture = tex;
+	img.reset(impl);
 
 	return ErrorCode::NONE;
 }

+ 3 - 3
src/ui/Widget.cpp

@@ -41,14 +41,14 @@ void Widget::markForDeletion()
 }
 
 //==============================================================================
-void Widget::setRelativePosition(const Vec2& pos)
+void Widget::setRelativePosition(const UVec2& pos)
 {
 	m_posLocal = pos;
 	markForRepaint();
 }
 
 //==============================================================================
-void Widget::setSizeLimits(const Vec2& min, const Vec2& max)
+void Widget::setSizeLimits(const UVec2& min, const UVec2& max)
 {
 	m_minSize = min;
 	m_maxSize = max;
@@ -56,7 +56,7 @@ void Widget::setSizeLimits(const Vec2& min, const Vec2& max)
 }
 
 //==============================================================================
-void Widget::setSize(const Vec2& size)
+void Widget::setSize(const UVec2& size)
 {
 	m_size.x() = clamp(size.x(), m_minSize.x(), m_maxSize.x());
 	m_size.y() = clamp(size.y(), m_minSize.y(), m_maxSize.y());

+ 15 - 14
tests/resource/ResourceManager.cpp

@@ -5,6 +5,7 @@
 
 #include "tests/framework/Framework.h"
 #include "anki/resource/DummyRsrc.h"
+#include "anki/resource/ResourceManager.h"
 #include "anki/core/Config.h"
 
 namespace anki {
@@ -14,7 +15,7 @@ ANKI_TEST(Resource, ResourceManager)
 {
 	// Create
 	Config config;
-	
+
 	HeapAllocator<U8> alloc(allocAligned, nullptr);
 
 	ResourceManager::Initializer rinit;
@@ -29,14 +30,14 @@ ANKI_TEST(Resource, ResourceManager)
 	// Very simple
 	{
 		DummyResourcePtr a;
-		ANKI_TEST_EXPECT_NO_ERR(a.load("blah", resources));
+		ANKI_TEST_EXPECT_NO_ERR(resources->loadResource("blah", a));
 	}
 
 	// Load a resource
 	{
 		DummyResourcePtr a;
 
-		ANKI_TEST_EXPECT_NO_ERR(a.load("blah", resources));
+		ANKI_TEST_EXPECT_NO_ERR(resources->loadResource("blah", a));
 
 		{
 			DummyResourcePtr b = a;
@@ -48,25 +49,25 @@ ANKI_TEST(Resource, ResourceManager)
 	// Load and load again
 	{
 		DummyResourcePtr a;
-		ANKI_TEST_EXPECT_NO_ERR(a.load("blah", resources));
-		auto refcount = a.getReferenceCount();
+		ANKI_TEST_EXPECT_NO_ERR(resources->loadResource("blah", a));
+		auto refcount = a->getRefcount().load();
 
 		DummyResourcePtr b;
-		ANKI_TEST_EXPECT_NO_ERR(b.load("blah", resources));
-		ANKI_TEST_EXPECT_EQ(b.getReferenceCount(), a.getReferenceCount());
-		ANKI_TEST_EXPECT_EQ(a.getReferenceCount(), refcount + 1);
+		ANKI_TEST_EXPECT_NO_ERR(resources->loadResource("blah", b));
+		ANKI_TEST_EXPECT_EQ(b->getRefcount().load(), a->getRefcount().load());
+		ANKI_TEST_EXPECT_EQ(a->getRefcount().load(), refcount + 1);
 
 		ANKI_TEST_EXPECT_EQ(&b.get(), &a.get());
 
 		// Again
 		DummyResourcePtr c;
-		ANKI_TEST_EXPECT_NO_ERR(c.load("blah", resources));
-		ANKI_TEST_EXPECT_EQ(a.getReferenceCount(), refcount + 2);
+		ANKI_TEST_EXPECT_NO_ERR(resources->loadResource("blah", c));
+		ANKI_TEST_EXPECT_EQ(a->getRefcount().load(), refcount + 2);
 
 		// Load something else
 		DummyResourcePtr d;
-		ANKI_TEST_EXPECT_NO_ERR(d.load("blih", resources));
-		ANKI_TEST_EXPECT_EQ(a.getReferenceCount(), refcount + 2);
+		ANKI_TEST_EXPECT_NO_ERR(resources->loadResource("blih", d));
+		ANKI_TEST_EXPECT_EQ(a->getRefcount().load(), refcount + 2);
 	}
 
 	// Error
@@ -74,13 +75,13 @@ ANKI_TEST(Resource, ResourceManager)
 		{
 			DummyResourcePtr a;
 			ANKI_TEST_EXPECT_EQ(
-				a.load("error", resources), ErrorCode::USER_DATA);
+				resources->loadResource("error", a), ErrorCode::USER_DATA);
 		}
 
 		{
 			DummyResourcePtr a;
 			ANKI_TEST_EXPECT_EQ(
-				a.load("error", resources), ErrorCode::USER_DATA);
+				resources->loadResource("error", a), ErrorCode::USER_DATA);
 		}
 	}
 

+ 85 - 0
tests/util/HashMap.cpp

@@ -0,0 +1,85 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "tests/framework/Framework.h"
+#include "tests/util/Foo.h"
+#include "anki/util/HashMap.h"
+#include "anki/util/HighRezTimer.h"
+#include <unordered_map>
+
+using namespace anki;
+
+class Hasher
+{
+public:
+	U64 operator()(int x)
+	{
+		return x;
+	}
+};
+
+class Compare
+{
+public:
+	Bool operator()(int a, int b)
+	{
+		return a == b;
+	}
+};
+
+ANKI_TEST(Util, HashMap)
+{
+	HeapAllocator<U8> alloc(allocAligned, nullptr);
+	int arr[] = {20, 15, 5, 1, 10, 0, 18, 6, 7, 11, 13, 3};
+	U arrSize = sizeof(arr) / sizeof(arr[0]);
+
+	// Simple
+	{
+		HashMap<int, Hasher, Compare> map;
+		map.pushBack(alloc, 20);
+		map.destroy(alloc);
+	}
+
+	// Add more and iterate
+	{
+		HashMap<int, Hasher, Compare> map;
+
+		for(U i = 0; i < arrSize; ++i)
+		{
+			map.pushBack(alloc, arr[i]);
+		}
+
+		U count = 0;
+		auto it = map.getBegin();
+		for(; it != map.getEnd(); ++it)
+		{
+			++count;
+		}
+		ANKI_TEST_EXPECT_EQ(count, arrSize);
+
+		map.destroy(alloc);
+	}
+
+	// Erase
+	{
+		HashMap<int, Hasher, Compare> map;
+
+		for(U i = 0; i < arrSize; ++i)
+		{
+			map.pushBack(alloc, arr[i]);
+		}
+
+		for(U i = arrSize - 1; i != 0; --i)
+		{
+			HashMap<int, Hasher, Compare>::Iterator it = map.find(arr[i]);
+			ANKI_TEST_EXPECT_NEQ(it, map.getEnd());
+
+			map.erase(alloc, it);
+			map.pushBack(alloc, arr[i]);
+		}
+
+		map.destroy(alloc);
+	}
+}