ソースを参照

Adding shadows to IS, adding custom bullet, enabling Async, writting a few tests

Panagiotis Christopoulos Charitos 13 年 前
コミット
319ba5bb7f
57 ファイル変更924 行追加1203 行削除
  1. 56 94
      CMakeLists.txt
  2. 1 1
      build/clean
  3. 2 4
      include/anki/Config.h.cmake
  4. 0 38
      include/anki/core/App.h
  5. 68 0
      include/anki/core/Async.h
  6. 0 87
      include/anki/core/AsyncLoader.h
  7. 9 1
      include/anki/math/MathSimd.h
  8. 0 79
      include/anki/misc/Parser.h
  9. 0 261
      include/anki/misc/Parser.inl.h
  10. 2 2
      include/anki/physics/Convertors.h
  11. 1 1
      include/anki/physics/MotionState.h
  12. 2 2
      include/anki/physics/PhysWorld.h
  13. 2 2
      include/anki/physics/RigidBody.h
  14. 2 2
      include/anki/renderer/Drawer.h
  15. 0 14
      include/anki/renderer/Is.h
  16. 2 0
      include/anki/renderer/MainRenderer.h
  17. 2 2
      include/anki/renderer/Renderer.h
  18. 5 2
      include/anki/renderer/Sm.h
  19. 0 1
      include/anki/resource/AsyncTextureResourceManager.h
  20. 9 12
      include/anki/resource/MeshLoader.h
  21. 6 5
      include/anki/resource/ResourceManager.h
  22. 15 19
      include/anki/resource/ResourceManager.inl.h
  23. 0 20
      include/anki/scene/Camera.h
  24. 18 0
      include/anki/scene/Frustumable.h
  25. 2 12
      include/anki/scene/Light.h
  26. 1 10
      include/anki/scene/ModelNode.h
  27. 0 6
      include/anki/scene/ParticleEmitterNode.h
  28. 2 2
      include/anki/scene/SkinNode.h
  29. 2 0
      include/anki/util/Assert.h
  30. 27 0
      include/anki/util/Functions.h
  31. 9 5
      include/anki/util/Observer.h
  32. 7 33
      include/anki/util/Platform.h
  33. 11 5
      include/anki/util/Vector.h
  34. 62 11
      shaders/IsLpGeneric.glsl
  35. 1 1
      src/CMakeLists.txt
  36. 22 114
      src/core/App.cpp
  37. 146 0
      src/core/Async.cpp
  38. 0 93
      src/core/AsyncLoader.cpp
  39. 3 1
      src/core/CMakeLists.txt
  40. 21 0
      src/core/NativeWindowDummy.cpp
  41. 1 1
      src/input/CMakeLists.txt
  42. 13 6
      src/input/InputX11.cpp
  43. 0 2
      src/physics/CMakeLists.txt
  44. 4 4
      src/physics/Character.cpp
  45. 1 1
      src/physics/PhysWorld.cpp
  46. 8 8
      src/renderer/Drawer.cpp
  47. 189 177
      src/renderer/Is.cpp
  48. 6 8
      src/renderer/MainRenderer.cpp
  49. 19 37
      src/renderer/Sm.cpp
  50. 1 1
      src/resource/MeshLoader.cpp
  51. 0 1
      src/resource/ShaderProgramPrePreprocessor.cpp
  52. 2 6
      src/resource/SkelAnim.cpp
  53. 0 1
      src/scene/ParticleEmitterNode.cpp
  54. 21 8
      testapp/Main.cpp
  55. 39 0
      tests/Main.cpp
  56. 48 0
      tests/core/Async.cpp
  57. 54 0
      tests/core/ThreadPool.cpp

+ 56 - 94
CMakeLists.txt

@@ -3,50 +3,47 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
 PROJECT(ANKI_PROJ)
 
 #
-# Macros
+# Options that affect anki and extern
 #
 
-# Check if INCDIR and LIBDIR dirs exist and the file ONEINCFILE as well
-MACRO(ANKI_ADD_LIB INCDIR LIBDIR ONEINCFILE)
-	IF(NOT EXISTS ${INCDIR})
-		MESSAGE(FATAL_ERROR "Directory does not exist: " ${INCDIR})
-	ENDIF()
-	
-	IF(NOT EXISTS ${LIBDIR})
-		MESSAGE(FATAL_ERROR "Directory does not exist: " ${LIBDIR})
-	ENDIF()
-	
-	IF(NOT EXISTS ${ONEINCFILE})
-		MESSAGE(FATAL_ERROR "File not found: " ${ONEINCFILE})
+# CPU
+SET(ANKI_CPU "X86" CACHE STRING "The CPU arch (X86 or ARM)")
+MESSAGE("++ AnKi CPU: ${ANKI_CPU}")
+OPTION(ANKI_ENABLE_MATH_SIMD "Enable or not math SIMD optimizations" ON)
+
+IF(ANKI_ENABLE_MATH_SIMD)
+	IF(ANKI_CPU STREQUAL "X86")
+		SET(ANKI_MATH_SIMD "SSE")
+	ELSEIF(ANKI_CPU STREQUAL "ARM")
+		SET(ANKI_MATH_SIMD "NEON")
+	ELSE()
+		MESSAGE(FATAL "Wrong ANKI_CPU set")
 	ENDIF()
-	
-	INCLUDE_DIRECTORIES(${INCDIR})
-	LINK_DIRECTORIES(${LIBDIR})
-	
-	MESSAGE("++ Found: ${ONEINCFILE}")
-	MESSAGE("++ Adding include dir: ${INCDIR}")
-	MESSAGE("++ Adding lib dir: ${LIBDIR}")
-ENDMACRO()
+ELSE()
+	SET(ANKI_MATH_SIMD "NONE")
+ENDIF()
 
-#
-# Common stuff
-#
-SET(ARCH 64 CACHE STRING "The architecture (-mXX)")
+MESSAGE("++ AnKi SIMD: ${ANKI_MATH_SIMD}")
 
-MESSAGE("++ Arch: ${ARCH}")
+# Arch
+SET(ANKI_ARCH "0" CACHE STRING "The CPU architecture (0 or 32 or 64). If zero go native")
+MESSAGE("++ AnKi arch: ${ANKI_ARCH}")
 
-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m${ARCH} ")
-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m${ARCH} ")
-SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m${ARCH} ")
+IF(NOT ANKI_ARCH STREQUAL "0")
+	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m${ANKI_ARCH} ")
+	SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m${ANKI_ARCH} ")
+	SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m${ANKI_ARCH} ")
+ENDIF()
 
-IF(CMAKE_BUILD_TYPE STREQUAL Debug)
-ELSE()
-	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ")
+# platform
+IF(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+	SET(ANKI_PLATFORM "LINUX")
+ELSEIF(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+	SET(ANKI_PLATFORM "WINDOWS")
 ENDIF()
+MESSAGE("++ AnKi platform: ${ANKI_PLATFORM}")
 
-#
-# Common options
-#
+# libz and libpng
 OPTION(ANKI_SYSTEM_LIBZ "Use the system's libz" OFF)
 OPTION(ANKI_SYSTEM_LIBPNG "Use the system's libpng" OFF)
 
@@ -66,6 +63,20 @@ ELSE()
 	MESSAGE("++ Building with AnKi libpng")
 ENDIF()
 
+# Window backend
+SET(ANKI_WINDOW_BACKEND "GLXX11" CACHE STRING "The window backend (GLXX11 or EGLX11 or DUMMY)")
+MESSAGE("++ AnKi window backend: ${ANKI_WINDOW_BACKEND}")
+
+#
+# Common compiler flags
+#
+
+# Build type
+IF(CMAKE_BUILD_TYPE STREQUAL Debug)
+ELSE()
+	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ")
+ENDIF()
+
 #
 # Install
 #
@@ -80,24 +91,6 @@ MESSAGE("++ Lib install dir: ${LIB_INSTALL_DIR}")
 #
 ADD_SUBDIRECTORY(extern)
 
-#
-# Libraries
-#
-FIND_PACKAGE(Boost 1.46 REQUIRED)
-SET(Boost_USE_STATIC_LIBS ON)
-IF(Boost_FOUND)
-	INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR})
-	ADD_DEFINITIONS("-DHAS_BOOST")
-ENDIF()
-
-#
-# Bullet (Because FIND_PACKAGE(Bullet) sucks)
-#
-SET(BULLET_INCLUDE_DIR "${ANKI_PROJ_SOURCE_DIR}/extern/include/" CACHE PATH "The directory that contains the bullet directory with the header files")
-SET(BULLET_LIBRARY_DIR "${ANKI_PROJ_SOURCE_DIR}/extern/lib${ARCH}" CACHE PATH "The directory that contains the Bullet (static) libraries")
-
-ANKI_ADD_LIB(${BULLET_INCLUDE_DIR}/bullet ${BULLET_LIBRARY_DIR} ${BULLET_INCLUDE_DIR}/bullet/btBulletCollisionCommon.h)
-
 #
 # Doxygen
 #
@@ -115,10 +108,10 @@ FIND_PACKAGE(Subversion 1.6 REQUIRED)
 
 IF(Subversion_FOUND)
 	Subversion_WC_INFO(${CMAKE_CURRENT_SOURCE_DIR} ER)
-	ADD_DEFINITIONS("-DANKI_REVISION=${ER_WC_REVISION}")
+	#ADD_DEFINITIONS("-DANKI_REVISION=${ER_WC_REVISION}")
 	SET(ANKI_REVISION ${ER_WC_REVISION})
 ELSE()
-	ADD_DEFINITIONS("-DANKI_REVISION=-1")
+	#ADD_DEFINITIONS("-DANKI_REVISION=-1")
 	SET(ANKI_REVISION "-1")
 ENDIF()
 
@@ -127,42 +120,12 @@ ENDIF()
 #
 SET(ANKI_VERSION_MAJOR 0)
 SET(ANKI_VERSION_MINOR 1)
-ADD_DEFINITIONS("-DANKI_VERSION_MAJOR=${ANKI_VERSION_MAJOR}")
-ADD_DEFINITIONS("-DANKI_VERSION_MINOR=${ANKI_VERSION_MINOR}")
+#ADD_DEFINITIONS("-DANKI_VERSION_MAJOR=${ANKI_VERSION_MAJOR}")
+#ADD_DEFINITIONS("-DANKI_VERSION_MINOR=${ANKI_VERSION_MINOR}")
 MESSAGE("++ AnKi version: ${ANKI_VERSION_MAJOR}.${ANKI_VERSION_MINOR}")
 
-SET(ANKI_WINDOW_BACKEND "GLXX11" CACHE STRING "The window backend (GLXX11 or EGLX11)")
-ADD_DEFINITIONS("-DANKI_WINDOW_BACKEND_${ANKI_WINDOW_BACKEND}")
-MESSAGE("++ AnKi window backend: ${ANKI_WINDOW_BACKEND}")
-
-SET(ANKI_CPU "X86" CACHE STRING "The CPU arch (X86 or ARM)")
-ADD_DEFINITIONS("-DANKI_CPU_${ANKI_CPU}")
-MESSAGE("++ AnKi CPU: ${ANKI_CPU}")
-
-OPTION(ANKI_ENABLE_MATH_SIMD "Enable or not math SIMD optimizations" ON)
-
-IF(ANKI_ENABLE_MATH_SIMD)
-	IF(ANKI_CPU STREQUAL "X86")
-		SET(ANKI_MATH_SIMD "SSE")
-	ELSEIF(ANKI_CPU STREQUAL "ARM")
-		SET(ANKI_MATH_SIMD "NEON")
-	ELSE()
-		MESSAGE(FATAL "Wrong ANKI_CPU set")
-	ENDIF()
-ELSE()
-	SET(ANKI_MATH_SIMD "NONE")
-ENDIF()
-
-ADD_DEFINITIONS("-DANKI_MATH_SIMD_${ANKI_MATH_SIMD}")
-MESSAGE("++ AnKi math SIMD: ${ANKI_MATH_SIMD}")
-
-IF(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-	SET(ANKI_PLATFORM "LINUX")
-ELSEIF(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
-	SET(ANKI_PLATFORM "WINDOWS")
-ENDIF()
-ADD_DEFINITIONS("-DANKI_PLATFORM_${ANKI_PLATFORM}")
-MESSAGE("++ AnKi platform: ${ANKI_PLATFORM}")
+#ADD_DEFINITIONS("-DANKI_WINDOW_BACKEND_${ANKI_WINDOW_BACKEND}")
+#ADD_DEFINITIONS("-DANKI_MATH_SIMD_${ANKI_MATH_SIMD}")
 
 CONFIGURE_FILE("include/anki/Config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/anki/Config.h")
 INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/anki/Config.h" DESTINATION "${INCLUDE_INSTALL_DIR}/anki")
@@ -188,9 +151,8 @@ ENDIF()
 #
 # Include & lib directories
 #
-INCLUDE_DIRECTORIES("extern/GLEW/include" "extern/tinyxml2/include" "extern/lua" "extern/png" "include")
-INCLUDE_DIRECTORIES("extern/include") # XXX Remove that some day
-LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/extern/lib64") # XXX Remove that some day
+INCLUDE_DIRECTORIES("extern/GLEW/include" "extern/tinyxml2/include" "extern/lua" "extern/png" "extern/bullet" "include"
+	"${CMAKE_CURRENT_BINARY_DIR}")
 
 # Add a few compiler specific stuff 
 IF(${CMAKE_CXX_COMPILER} MATCHES ".*clang\\+\\+$")	
@@ -204,9 +166,9 @@ ENDIF()
 #
 # Valgrind hacks
 #
-OPTION(VALGRIND_HAPPY "Make valgrind happy" OFF)
+OPTION(ANKI_VALGRIND_HAPPY "Make valgrind happy" OFF)
 
-IF(VALGRIND_HAPPY)
+IF(ANKI_VALGRIND_HAPPY)
 	ADD_DEFINITIONS("-DGLIBCXX_FORCE_NEW")
 ENDIF()
 

+ 1 - 1
build/clean

@@ -1,2 +1,2 @@
 #!/bin/bash
-rm -rf CMakeCache.txt CMakeFiles Makefile cmake_install.cmake src doxyfile testapp anki
+ls | xargs -I % echo % | grep -v clean | grep -v genmakefiledbg | xargs -I % rm -rf %

+ 2 - 4
include/anki/Config.h.cmake

@@ -4,9 +4,7 @@
 #define ANKI_VERSION_MINOR ${ANKI_VERSION_MINOR}
 #define ANKI_VERSION_MAJOR ${ANKI_VERSION_MAJOR}
 #define ANKI_REVISION ${ANKI_REVISION}
-#define ANKI_CPU_${ANKI_CPU}
-#define ANKI_MATH_SIMD_${ANKI_MATH_SIMD}
-#define ANKI_WINDOW_BACKEND_${ANKI_WINDOW_BACKEND}
-#define ANKI_PLATFORM_${ANKI_PLATFORM}
+#define ANKI_MATH_SIMD_${ANKI_MATH_SIMD} 1
+#define ANKI_WINDOW_BACKEND_${ANKI_WINDOW_BACKEND} 1
 
 #endif

+ 0 - 38
include/anki/core/App.h

@@ -3,7 +3,6 @@
 
 #include "anki/core/Logger.h"
 #include "anki/util/Singleton.h"
-#include <SDL/SDL.h>
 
 namespace anki {
 
@@ -39,28 +38,10 @@ public:
 	/// - call exit()
 	void quit(int code);
 
-	/// Self explanatory
-	void togleFullScreen();
-
-	/// Wrapper for an SDL function that swaps the buffers
-	void swapBuffers();
-
 	static void printAppInfo();
 
-	uint getDesktopWidth() const;
-	uint getDesktopHeight() const;
-
 	/// @name Accessors
 	/// @{
-	Camera* getActiveCam()
-	{
-		return activeCam;
-	}
-	void setActiveCam(Camera* cam)
-	{
-		activeCam = cam;
-	}
-
 	float getTimerTick() const
 	{
 		return timerTick;
@@ -74,16 +55,6 @@ public:
 		timerTick = x;
 	}
 
-	uint getWindowWidth() const
-	{
-		return windowW;
-	}
-
-	uint getWindowHeight() const
-	{
-		return windowH;
-	}
-
 	const std::string& getSettingsPath() const
 	{
 		return settingsPath;
@@ -96,20 +67,11 @@ public:
 	/// @}
 
 private:
-	uint windowW; ///< The main window width
-	uint windowH; ///< The main window height
 	/// The path that holds the configuration
 	std::string settingsPath;
 	/// This is used as a cache
 	std::string cachePath;
 	float timerTick;
-	/// Terminal coloring for Unix terminals. Default on
-	bool terminalColoringEnabled;
-	SDL_WindowID windowId;
-	SDL_GLContext glContext;
-	SDL_Surface* iconImage;
-	bool fullScreenFlag;
-	Camera* activeCam; ///< Pointer to the current camera
 
 	void parseCommandLineArgs(int argc, char* argv[]);
 

+ 68 - 0
include/anki/core/Async.h

@@ -0,0 +1,68 @@
+#ifndef ANKI_CORE_ASYNC_H
+#define ANKI_CORE_ASYNC_H
+
+#include "anki/util/StdTypes.h"
+#include "anki/util/Assert.h"
+#include <list>
+#include <thread>
+#include <condition_variable>
+#include <mutex>
+
+namespace anki {
+
+/// An asynchronous job
+struct AsyncJob
+{
+	Bool garbageCollect = false;
+
+	virtual ~AsyncJob()
+	{}
+
+	/// Call this from the thread
+	virtual void operator()() = 0;
+
+	/// Call this from the main thread after operator() is done
+	virtual void post() = 0;
+};
+
+/// Asynchronous job executor
+class Async
+{
+public:
+	Async()
+	{}
+
+	/// Do nothing
+	~Async();
+
+	void start();
+	void stop();
+	
+	void assignNewJob(AsyncJob* job)
+	{
+		ANKI_ASSERT(job != nullptr);
+		assignNewJobInternal(job);
+	}
+
+	/// Call post for the finished jobs
+	/// @param maxTime Try not spending much time on that
+	void cleanupFinishedJobs(F32 maxTime);
+
+private:
+	std::list<AsyncJob*> pendingJobs;
+	std::list<AsyncJob*> finishedJobs;
+	std::mutex pendingJobsMtx; ///< Protect jobs
+	std::mutex finishedJobsMtx; ///< Protect jobs
+	std::thread thread;
+	std::condition_variable condVar;
+	Bool started = false;
+
+	/// The thread function. It waits for some jobs to do
+	void workingFunc();
+
+	void assignNewJobInternal(AsyncJob* job);
+};
+
+} // end namespace anki
+
+#endif

+ 0 - 87
include/anki/core/AsyncLoader.h

@@ -1,87 +0,0 @@
-#ifndef ANKI_CORE_ASYNC_LOADER_H
-#define ANKI_CORE_ASYNC_LOADER_H
-
-#include <list>
-#include <string>
-#include <thread>
-#include <mutex>
-#include <SDL/SDL.h>
-
-namespace anki {
-
-/*
-/// Asynchronous loader
-///
-/// It creates a thread that loads files on demand. It accepts requests (in the
-/// form of a filename of the file to load, a pointer to a function for the way
-/// to load the file and a generic pointer for the data to load them to). Its
-/// not meant to be destroyed because of a deadlock.
-class AsyncLoader
-{
-public:
-	/// Type of the callback function that the async loader wants
-	typedef void (*LoadCallback)(const char*, void*);
-
-	/// Default constructor starts the thread
-	AsyncLoader()
-	{
-		start();
-	}
-	
-	/// Do nothing
-	~AsyncLoader()
-	{}
-
-	/// Tell me what to load, how to load it and where to store it. This
-	/// puts a new loading request in the stack
-	/// @param filename The file to load
-	/// @param loadCallback How to load the file. The function should gets
-	/// a filename (const char*) and the storage.
-	/// It can throw an exception in case of a loading error
-	/// @param storage This points to the storage that the loader will
-	/// store the data. The storage should not be destroyed from other
-	/// threads
-	void load(const char* filename, LoadCallback loadCallback,
-		void* storage);
-
-	/// Query the loader and see if its got something
-	/// @param[out] filename The file that finished loading
-	/// @param[out] storage The data are stored in this buffer
-	/// @param[out] ok Its true if the loading of the resource was ok
-	/// @return Return true if there is something that finished loading
-	bool pollForFinished(std::string& filename, void* storage, bool& ok);
-
-private:
-	/// A loading request
-	struct Request
-	{
-		std::string filename;
-		LoadCallback loadCallback;
-		void* storage;
-	};
-
-	/// It contains a few things to identify the response
-	struct Response
-	{
-		std::string filename;
-		void* storage;
-		bool ok; ///< True if the loading was successful
-	};
-
-	std::list<Request> requests;
-	std::list<Response> responses;
-	std::mutex mutexReq; ///< Protect the requests container
-	std::mutex mutexResp; ///< Protect the responses container
-	std::thread thread;
-	std::condition_variable condVar;
-
-	/// The thread function. It waits for something in the requests
-	/// container
-	void workingFunc();
-	void start(); ///< Start thread
-};
-*/
-
-} // end namespace
-
-#endif

+ 9 - 1
include/anki/math/MathSimd.h

@@ -1,8 +1,16 @@
 #ifndef ANKI_MATH_MATH_SIMD_H
 #define ANKI_MATH_MATH_SIMD_H
 
-#if defined(ANKI_MATH_SIMD_SSE)
+#include "anki/Config.h"
+
+#if ANKI_MATH_SIMD_SSE
 #	include <smmintrin.h>
+#elif ANKI_MATH_SIMD_NEON
+#	error "Not implemented yet"
+#elif ANKI_MATH_SIMD_NONE
+#	define ANKI_DUMMY_DUMMY_DUMMY 1
+#else
+#	error "See file"
 #endif
 
 #endif

+ 0 - 79
include/anki/misc/Parser.h

@@ -1,79 +0,0 @@
-#ifndef ANKI_MISC_PARSER_H
-#define ANKI_MISC_PARSER_H
-
-#include "anki/util/Exception.h"
-#include "anki/util/Scanner.h"
-
-
-namespace anki {
-
-
-/// It contains some functions and macros that are used pretty often while
-/// parsing
-namespace parser {
-
-
-/// Parser macros
-#define PARSER_EXCEPTION(x) \
-	ANKI_EXCEPTION("Parser exception (" + scanner.getScriptName() + ':' + \
-	std::to_string(scanner.getLineNumber()) + "): " + x)
-
-#define PARSER_EXCEPTION_EXPECTED(x) \
-	PARSER_EXCEPTION("Expected " + x + " and not " + \
-		scanner.getCrntToken().getInfoString())
-
-#define PARSER_EXCEPTION_UNEXPECTED() \
-	PARSER_EXCEPTION("Unexpected token " + \
-	scanner.getCrntToken().getInfoString())
-
-
-/// This template func is used for a common operation of parsing arrays of
-/// numbers
-///
-/// It parses expressions like this one: { 10 -0.2 123.e-10 -0x0FF } and stores
-/// the result in the arr array. The acceptable types (typename Type) are
-/// integer or floating point types
-///
-/// @param scanner The scanner that we will use
-/// @param bracket If true the array starts and ends with brackets eg {10 0 -1}
-/// @param signs If true the array has numbers that may contain sign
-/// @param size The count of numbers of the array wa want to parse
-/// @param arr The array that the func returns the numbers
-/// @exception Exception
-template <typename Type>
-void parseArrOfNumbers(scanner::Scanner& scanner, bool bracket, bool signs,
-	uint size, Type* arr);
-
-/// Parse a single number
-/// @param scanner The scanner that we will use
-/// @param sign If true expect sign or not
-/// @param out The output number
-template <typename Type>
-void parseNumber(scanner::Scanner& scanner, bool sign, Type& out);
-
-/// Parses a math structure (Vec3, Vec4, Mat3 etc) with leading and following
-/// brackets. Eg {0.1 0.2 0.3}
-template <typename Type>
-void parseMathVector(scanner::Scanner& scanner, Type& out);
-
-/// Parse true or false identifiers
-extern bool parseBool(scanner::Scanner& scanner);
-
-/// Parse identifier
-extern std::string parseIdentifier(scanner::Scanner& scanner,
-	const char* expectedIdentifier = NULL);
-
-/// Is identifier
-extern bool isIdentifier(const scanner::Token& token, const char* str);
-
-/// Parse string
-extern std::string parseString(scanner::Scanner& scanner);
-
-
-} // end namespace
-} // end namespace
-
-#include "anki/misc/Parser.inl.h"
-
-
-#endif

+ 0 - 261
include/anki/misc/Parser.inl.h

@@ -1,261 +0,0 @@
-#include <cstring>
-#include "anki/misc/Parser.h"
-#include "anki/util/Exception.h"
-
-
-namespace anki {
-namespace parser {
-
-
-//==============================================================================
-// parseArrOfNumbers                                                           =
-//==============================================================================
-template <typename Type>
-void parseArrOfNumbers(scanner::Scanner& scanner, bool bracket, bool signs,
-	uint size, Type* arr)
-{
-	const scanner::Token* token;
-
-	// first of all the left bracket
-	if(bracket)
-	{
-		token = &scanner.getNextToken();
-		if(token->getCode() != scanner::TC_L_BRACKET)
-		{
-			throw PARSER_EXCEPTION_EXPECTED("{");
-		}
-	}
-
-	// loop to parse numbers
-	for(uint i=0; i<size; i++)
-	{
-		token = &scanner.getNextToken();
-
-
-		// check if there is a sign in front of a number
-		bool sign = 0; // sign of the number. 0 for positive and 1 for negative
-		if(signs)
-		{
-			if(token->getCode() == scanner::TC_MINUS)
-			{
-				sign = 1;
-				token = &scanner.getNextToken();
-			}
-			else if(token->getCode() == scanner::TC_PLUS)
-			{
-				sign = 0;
-				token = &scanner.getNextToken();
-			}
-
-			// check if not number
-			if(token->getCode() != scanner::TC_NUMBER)
-			{
-				throw PARSER_EXCEPTION_EXPECTED("number");
-			}
-		} // end if signs
-
-		// put the number in the arr and do typecasting from int to float
-		Type nmbr;
-
-		if(token->getDataType() == scanner::DT_FLOAT)
-		{
-			nmbr = static_cast<Type>(token->getValue().getFloat());
-		}
-		else
-		{
-			nmbr = static_cast<Type>(token->getValue().getInt());
-		}
-
-		arr[i] = (sign==0) ? nmbr : -nmbr;
-	}
-
-	// the last thing is the right bracket
-	if(bracket)
-	{
-		token = &scanner.getNextToken();
-		if(token->getCode() != scanner::TC_R_BRACKET)
-		{
-			throw PARSER_EXCEPTION_EXPECTED("}");
-		}
-	}
-}
-
-
-//==============================================================================
-// parseNumber                                                                 =
-//==============================================================================
-template <typename Type>
-void parseNumber(scanner::Scanner& scanner, bool sign, Type& out)
-{
-	try
-	{
-		const scanner::Token* token = &scanner.getNextToken();
-		bool negative = false;
-
-		// check the sign if any
-		if(sign)
-		{
-			if(token->getCode() == scanner::TC_PLUS)
-			{
-				negative = false;
-				token = &scanner.getNextToken();
-			}
-			else if(token->getCode() == scanner::TC_MINUS)
-			{
-				negative = true;
-				token = &scanner.getNextToken();
-			}
-			else if(token->getCode() != scanner::TC_NUMBER)
-			{
-				throw PARSER_EXCEPTION_EXPECTED("number or sign");
-			}
-		}
-
-		// check the number
-		if(token->getCode() != scanner::TC_NUMBER)
-		{
-			throw PARSER_EXCEPTION_EXPECTED("number");
-		}
-
-		if(token->getDataType() == scanner::DT_FLOAT)
-		{
-			double d = negative ? -token->getValue().getFloat() :
-				token->getValue().getFloat();
-			out = static_cast<Type>(d);
-		}
-		else
-		{
-			ulong l = negative ? -token->getValue().getInt() :
-				token->getValue().getInt();
-			out = static_cast<Type>(l);
-		}
-	}
-	catch(const std::exception& e)
-	{
-		throw ANKI_EXCEPTION("Error") << e;
-	}
-}
-
-
-//==============================================================================
-// parseMathVector                                                             =
-//==============================================================================
-template <typename Type>
-void parseMathVector(scanner::Scanner& scanner, Type& out)
-{
-	try
-	{
-		const scanner::Token* token = &scanner.getNextToken();
-		uint elementsNum = sizeof(Type) / sizeof(float);
-
-		// {
-		if(token->getCode() != scanner::TC_L_BRACKET)
-		{
-			throw PARSER_EXCEPTION_EXPECTED("{");
-		}
-
-		// numbers
-		for(uint i=0; i<elementsNum; i++)
-		{
-			double d;
-			parseNumber(scanner, true, d);
-			out[i] = d;
-		}
-
-		// }
-		token = &scanner.getNextToken();
-		if(token->getCode() != scanner::TC_R_BRACKET)
-		{
-			throw PARSER_EXCEPTION_EXPECTED("}");
-		}
-	}
-	catch(const std::exception& e)
-	{
-		throw ANKI_EXCEPTION("Error") << e;
-	}
-}
-
-
-//==============================================================================
-// parseBool                                                                   =
-//==============================================================================
-inline bool parseBool(scanner::Scanner& scanner)
-{
-	const char* errMsg = "identifier true or false";
-
-	const scanner::Token* token = &scanner.getNextToken();
-	if(token->getCode() != scanner::TC_IDENTIFIER)
-	{
-		throw PARSER_EXCEPTION_EXPECTED(errMsg);
-	}
-
-	if(!strcmp(token->getValue().getString(), "true"))
-	{
-		return true;
-	}
-	else if (!strcmp(token->getValue().getString(), "false"))
-	{
-		return false;
-	}
-	else
-	{
-		throw PARSER_EXCEPTION_EXPECTED(errMsg);
-	}
-}
-
-
-//==============================================================================
-// parseIdentifier                                                             =
-//==============================================================================
-inline std::string parseIdentifier(scanner::Scanner& scanner,
-	const char* expectedIdentifier)
-{
-	const scanner::Token* token = &scanner.getNextToken();
-	if(token->getCode() != scanner::TC_IDENTIFIER)
-	{
-		if(expectedIdentifier == NULL)
-		{
-			throw PARSER_EXCEPTION_EXPECTED("identifier");
-		}
-		else
-		{
-			throw PARSER_EXCEPTION_EXPECTED("identifier " + expectedIdentifier);
-		}
-	}
-
-	if(expectedIdentifier != NULL &&
-		strcmp(token->getValue().getString(), expectedIdentifier))
-	{
-		throw PARSER_EXCEPTION_EXPECTED("identifier " + expectedIdentifier);
-	}
-
-	return token->getValue().getString();
-}
-
-
-//==============================================================================
-// isIdentifier                                                                =
-//==============================================================================
-inline bool isIdentifier(const scanner::Token& token, const char* str)
-{
-	return token.getCode() == scanner::TC_IDENTIFIER &&
-		!strcmp(token.getValue().getString(), str);
-}
-
-
-//==============================================================================
-// parseString                                                                 =
-//==============================================================================
-inline std::string parseString(scanner::Scanner& scanner)
-{
-	const scanner::Token* token = &scanner.getNextToken();
-	if(token->getCode() != scanner::TC_STRING)
-	{
-		throw PARSER_EXCEPTION_EXPECTED("string");
-	}
-	return token->getValue().getString();
-}
-
-
-} // End namespace
-} // end namespace

+ 2 - 2
include/anki/physics/Convertors.h

@@ -2,8 +2,8 @@
 #define ANKI_PHYSICS_CONVERTORS_H
 
 #include "anki/math/Math.h"
-#include <bullet/btBulletCollisionCommon.h>
-#include <bullet/btBulletDynamicsCommon.h>
+#include <btBulletCollisionCommon.h>
+#include <btBulletDynamicsCommon.h>
 
 
 namespace anki {

+ 1 - 1
include/anki/physics/MotionState.h

@@ -1,8 +1,8 @@
 #ifndef ANKI_PHYSICS_MOTION_STATE_H
 #define ANKI_PHYSICS_MOTION_STATE_H
 
-#include <bullet/LinearMath/btMotionState.h>
 #include "anki/scene/Movable.h"
+#include <LinearMath/btMotionState.h>
 
 namespace anki {
 

+ 2 - 2
include/anki/physics/PhysWorld.h

@@ -4,8 +4,8 @@
 #include "anki/physics/Convertors.h"
 #include "anki/util/Vector.h"
 #include <memory>
-#include <bullet/btBulletCollisionCommon.h>
-#include <bullet/btBulletDynamicsCommon.h>
+#include <btBulletCollisionCommon.h>
+#include <btBulletDynamicsCommon.h>
 
 
 class btIDebugDraw;

+ 2 - 2
include/anki/physics/RigidBody.h

@@ -3,8 +3,8 @@
 
 #include "anki/math/Math.h"
 #include <memory>
-#include <bullet/btBulletDynamicsCommon.h>
-#include <bullet/btBulletCollisionCommon.h>
+#include <btBulletDynamicsCommon.h>
+#include <btBulletCollisionCommon.h>
 
 namespace anki {
 

+ 2 - 2
include/anki/renderer/Drawer.h

@@ -197,7 +197,7 @@ public:
 		: r(r_)
 	{}
 
-	void render(const Camera& cam,
+	void render(const Frustumable& fr,
 		uint pass, Renderable& renderable);
 
 private:
@@ -205,7 +205,7 @@ private:
 
 	void setupShaderProg(
 		const PassLevelKey& key,
-		const Camera& cam,
+		const Frustumable& fr,
 		Renderable& renderable);
 };
 

+ 0 - 14
include/anki/renderer/Is.h

@@ -147,20 +147,6 @@ private:
 	static Bool cullLight(const PointLight& light, const Tile& tile);
 	static Bool cullLight(const SpotLight& light, const Tile& tile);
 
-	/// Update the point lights UBO
-	void writeLightUbo(ShaderPointLights& shaderLights, U32 maxShaderLights,
-		PointLight* visibleLights[], U32 visibleLightsCount, U start, U end);
-	/// Update the spot lights UBO
-	void writeLightUbo(ShaderSpotLights& shaderLights, U32 maxShaderLights,
-		SpotLight* visibleLights[], U32 visibleLightsCount, U start, U end);
-
-	/// Write the tiles UBO
-	void writeTilesUbo(
-		PointLight* visiblePointLights[], U32 visiblePointLightsCount,
-		SpotLight* visibleSpotLights[], U32 visibleSpotLightsCount,
-		ShaderTiles& shaderTiles, U32 maxLightsPerTile,
-		U32 start, U32 end);
-
 	// Do the actual pass
 	void lightPass();
 };

+ 2 - 0
include/anki/renderer/MainRenderer.h

@@ -82,6 +82,8 @@ private:
 	int screenshotJpegQuality = 90; ///< The quality of the JPEG screenshots.
 							        ///< From 0 to 100
 
+	U32 windowWidth, windowHeight;
+
 	/// The global rendering quality of the raster image. Its a percentage
 	/// of the application's window size. From 0.0(low) to 1.0(high)
 	float renderingQuality;

+ 2 - 2
include/anki/renderer/Renderer.h

@@ -80,8 +80,8 @@ struct RendererInitializer
 	} dbg;
 
 	// the globals
-	U32 width; ///< Ignored by MainRenderer
-	U32 height; ///< Ignored by MainRenderer
+	U32 width; 
+	U32 height;
 	F32 mainRendererQuality = 1.0; ///< Only for MainRenderer
 	F32 lodDistance; ///< Distance that used to calculate the LOD
 

+ 5 - 2
include/anki/renderer/Sm.h

@@ -14,6 +14,8 @@ class Light;
 class Sm: private RenderingPass
 {
 public:
+	static const U32 MAX_SHADOW_CASTERS = 8;
+
 	Sm(Renderer* r_)
 		: RenderingPass(r_)
 	{}
@@ -27,7 +29,8 @@ public:
 	/// @}
 
 	void init(const RendererInitializer& initializer);
-	void run();
+	void run(Light* shadowCasters[], U32 shadowCastersCount, 
+		Texture* shadowmaps[]);
 
 private:
 	/// Shadowmap
@@ -65,7 +68,7 @@ private:
 	/// Find the best shadowmap for that light
 	Shadowmap& bestCandidate(Light& light);
 
-	void doLight(Light& light);
+	Texture* doLight(Light& light);
 };
 
 } // end namespace anki

+ 0 - 1
include/anki/resource/AsyncTextureResourceManager.h

@@ -3,7 +3,6 @@
 
 #include "anki/resource/ResourceManager.h"
 #include "anki/resource/AsyncOperator.h"
-#include <boost/scoped_ptr.hpp>
 
 
 namespace anki {

+ 9 - 12
include/anki/resource/MeshLoader.h

@@ -2,10 +2,9 @@
 #define ANKI_RESOURCE_MESH_LOADER_H
 
 #include "anki/math/Math.h"
-#include <boost/range/iterator_range.hpp>
-#include <boost/array.hpp>
-#include <string>
 #include "anki/util/Vector.h"
+#include "anki/util/Array.h"
+#include <string>
 
 namespace anki {
 
@@ -53,17 +52,17 @@ public:
 
 		/// @todo change the vals to uint when change drivers
 		float bonesNum;
-		boost::array<float, MAX_BONES_PER_VERT> boneIds;
-		boost::array<float, MAX_BONES_PER_VERT> weights;
+		Array<float, MAX_BONES_PER_VERT> boneIds;
+		Array<float, MAX_BONES_PER_VERT> weights;
 	};
 
 	/// Triangle
 	class Triangle
 	{
-		public:
-			/// An array with the vertex indexes in the mesh class
-			boost::array<uint, 3> vertIds;
-			Vec3 normal;
+	public:
+		/// An array with the vertex indexes in the mesh class
+		Array<uint, 3> vertIds;
+		Vec3 normal;
 	};
 
 	MeshLoader(const char* filename)
@@ -145,8 +144,6 @@ private:
 	void doPostLoad();
 };
 
-
-} // end namespace
-
+} // end namespace anki
 
 #endif

+ 6 - 5
include/anki/resource/ResourceManager.h

@@ -1,7 +1,8 @@
 #ifndef ANKI_RESOURCE_RESOURCE_MANAGER_H
 #define ANKI_RESOURCE_RESOURCE_MANAGER_H
 
-#include <boost/ptr_container/ptr_vector.hpp>
+#include "anki/util/Vector.h"
+#include "anki/util/StdTypes.h"
 #include <string>
 
 namespace anki {
@@ -11,13 +12,13 @@ template<typename Type>
 struct ResourceHook
 {
 	std::string uuid; ///< Unique identifier
-	int referenceCounter;
+	U32 referenceCounter;
 	Type* resource;
 
 	~ResourceHook()
 	{}
 
-	bool operator==(const ResourceHook& b) const
+	Bool operator==(const ResourceHook& b) const
 	{
 		return uuid == b.uuid 
 			&& referenceCounter == b.referenceCounter 
@@ -32,7 +33,7 @@ class ResourceManager
 public:
 	typedef ResourceManager<Type> Self;
 	typedef ResourceHook<Type> Hook;
-	typedef boost::ptr_vector<Hook> Container;
+	typedef PtrVector<Hook> Container;
 	typedef typename Container::iterator Iterator;
 	typedef typename Container::const_iterator ConstIterator;
 
@@ -57,7 +58,7 @@ protected:
 	/// Dealocate the resource. Its separate for two reasons:
 	/// - Because we want to specialize it for the async loaded resources
 	/// - Because we cannot have the operator delete in a template body.
-	/// Apparently the compiler is to dump to decide
+	///   Apparently the compiler is to dump to decide
 	virtual void deallocRsrc(Type* rsrc);
 };
 

+ 15 - 19
include/anki/resource/ResourceManager.inl.h

@@ -6,8 +6,8 @@ namespace anki {
 
 //==============================================================================
 template<typename Type>
-void ResourceManager<Type>::allocAndLoadRsrc(
-	const char* filename, Type*& newInstance)
+void ResourceManager<Type>::
+	allocAndLoadRsrc(const char* filename, Type*& newInstance)
 {
 	newInstance = NULL;
 
@@ -34,16 +34,16 @@ void ResourceManager<Type>::allocAndLoadRsrc(
 
 //==============================================================================
 template<typename Type>
-typename ResourceManager<Type>::Hook& ResourceManager<Type>::load(
-	const char* filename)
+typename ResourceManager<Type>::Hook& ResourceManager<Type>::
+	load(const char* filename)
 {
 	Iterator it = find(filename);
 
 	// If already loaded
 	if(it != hooks.end())
 	{
-		++it->referenceCounter;
-		return *it;
+		++(*it)->referenceCounter;
+		return *(*it);
 	}
 	// else create new, load it and update the container
 	else
@@ -64,8 +64,7 @@ typename ResourceManager<Type>::Hook& ResourceManager<Type>::load(
 				delete hook;
 			}
 
-			throw ANKI_EXCEPTION("Cannot load \"" 
-				+ filename + "\"") << e;
+			throw ANKI_EXCEPTION("Cannot load: " + filename) << e;
 		}
 
 		hooks.push_back(hook);
@@ -77,10 +76,7 @@ typename ResourceManager<Type>::Hook& ResourceManager<Type>::load(
 template<typename Type>
 void ResourceManager<Type>::deallocRsrc(Type* rsrc)
 {
-	typedef char TypeMustBeComplete[sizeof(Type) ? 1 : -1];
-    (void) sizeof(TypeMustBeComplete);
-
-	delete rsrc;
+	propperDelete(rsrc);
 }
 
 //==============================================================================
@@ -97,27 +93,27 @@ void ResourceManager<Type>::unload(const Hook& hook)
 			+ hook.uuid + "\")");
 	}
 
-	ANKI_ASSERT(*it == hook);
+	ANKI_ASSERT(*(*it) == hook);
 
-	--it->referenceCounter;
+	--(*it)->referenceCounter;
 
 	// Delete the resource
-	if(it->referenceCounter == 0)
+	if((*it)->referenceCounter == 0)
 	{
-		deallocRsrc(it->resource);
+		deallocRsrc((*it)->resource);
 		hooks.erase(it);
 	}
 }
 
 //==============================================================================
 template<typename Type>
-typename ResourceManager<Type>::Iterator ResourceManager<Type>::find(
-	const char* filename)
+typename ResourceManager<Type>::Iterator ResourceManager<Type>::
+	find(const char* filename)
 {
 	Iterator it = hooks.begin();
 	for(; it != hooks.end(); it++)
 	{
-		if(it->uuid == filename)
+		if((*it)->uuid == filename)
 		{
 			break;
 		}

+ 0 - 20
include/anki/scene/Camera.h

@@ -45,26 +45,11 @@ public:
 		return type;
 	}
 
-	const Mat4& getProjectionMatrix() const
-	{
-		return projectionMat;
-	}
-
 	const Mat4& getInverseProjectionMatrix() const
 	{
 		return invProjectionMat;
 	}
 
-	const Mat4& getViewMatrix() const
-	{
-		return viewMat;
-	}
-
-	const Mat4& getViewProjectionMatrix() const
-	{
-		return viewProjectionMat;
-	}
-
 	/// Needed by the renderer
 	virtual float getNear() const = 0;
 	/// Needed by the renderer
@@ -102,8 +87,6 @@ public:
 	void lookAtPoint(const Vec3& point);
 
 protected:
-	Mat4 projectionMat = Mat4::getIdentity();
-
 	/// Used in deferred shading for the calculation of view vector (see
 	/// CalcViewVector). The reason we store this matrix here is that we
 	/// don't want it to be re-calculated all the time but only when the
@@ -126,9 +109,6 @@ protected:
 
 private:
 	CameraType type;
-
-	Mat4 viewMat = Mat4::getIdentity();
-	Mat4 viewProjectionMat = Mat4::getIdentity();
 };
 
 /// Perspective camera

+ 18 - 0
include/anki/scene/Frustumable.h

@@ -43,6 +43,21 @@ public:
 	{
 		return timestamp;
 	}
+
+	const Mat4& getProjectionMatrix() const
+	{
+		return projectionMat;
+	}
+
+	const Mat4& getViewMatrix() const
+	{
+		return viewMat;
+	}
+
+	const Mat4& getViewProjectionMatrix() const
+	{
+		return viewProjectionMat;
+	}
 	/// @}
 
 	void frustumableMarkUpdated()
@@ -65,6 +80,9 @@ public:
 protected:
 	Frustum* frustum = nullptr;
 	VisibilityInfo vinfo;
+	Mat4 projectionMat = Mat4::getIdentity();
+	Mat4 viewMat = Mat4::getIdentity();
+	Mat4 viewProjectionMat = Mat4::getIdentity();
 
 private:
 	U32 timestamp = Timestamp::getTimestamp();

+ 2 - 12
include/anki/scene/Light.h

@@ -183,16 +183,6 @@ public:
 
 	/// @name Accessors
 	/// @{
-	const Mat4& getViewMatrix() const
-	{
-		return viewMat;
-	}
-
-	const Mat4& getProjectionMatrix() const
-	{
-		return projectionMat;
-	}
-
 	Texture& getTexture()
 	{
 		return *tex;
@@ -264,6 +254,7 @@ public:
 		Movable::movableUpdate();
 		frustum.setTransform(getWorldTransform());
 		viewMat = Mat4(getWorldTransform().getInverse());
+		viewProjectionMat = projectionMat * viewMat;
 
 		spatialMarkUpdated();
 	}
@@ -276,8 +267,6 @@ public:
 
 private:
 	PerspectiveFrustum frustum;
-	Mat4 projectionMat;
-	Mat4 viewMat;
 	TextureResourcePointer tex;
 	F32 cosOuterAngle;
 	F32 cosInnerAngle;
@@ -285,6 +274,7 @@ private:
 	void frustumUpdate()
 	{
 		projectionMat = frustum.calculateProjectionMatrix();
+		viewProjectionMat = projectionMat * viewMat;
 
 		spatialMarkUpdated();
 		frustumableMarkUpdated();

+ 1 - 10
include/anki/scene/ModelNode.h

@@ -8,9 +8,6 @@
 #include "anki/resource/Resource.h"
 #include "anki/resource/Model.h"
 #include "anki/collision/Obb.h"
-#include <boost/ptr_container/ptr_vector.hpp>
-#include <boost/range/iterator_range.hpp>
-#include <boost/scoped_ptr.hpp>
 
 namespace anki {
 
@@ -99,13 +96,7 @@ private:
 class ModelNode: public SceneNode, public Movable
 {
 public:
-	typedef boost::ptr_vector<ModelPatchNode> ModelPatchNodes;
-
-	typedef boost::iterator_range<ModelPatchNodes::const_iterator>
-		ConstRangeModelPatchNodes;
-
-	typedef boost::iterator_range<ModelPatchNodes::iterator>
-		MutableRangeModelPatchNodes;
+	typedef PtrVector<ModelPatchNode> ModelPatchNodes;
 
 	/// @name Constructors/Destructor
 	/// @{

+ 0 - 6
include/anki/scene/ParticleEmitterNode.h

@@ -1,17 +1,13 @@
 #ifndef ANKI_SCENE_PARTICLE_EMITTER_NODE_H
 #define ANKI_SCENE_PARTICLE_EMITTER_NODE_H
 
-#include <boost/ptr_container/ptr_vector.hpp>
-#include <boost/scoped_ptr.hpp>
 #include <btBulletCollisionCommon.h>
 #include "anki/scene/SceneNode.h"
 #include "anki/resource/ParticleEmitterRsrc.h"
 #include "anki/resource/Resource.h"
 
-
 class btCollisionShape;
 
-
 namespace anki {
 
 /*class Particle;
@@ -47,8 +43,6 @@ inline ParticleEmitterNode::ParticleEmitterNode(Scene& scene, ulong flags,
 :	SceneNode(SNT_PARTICLE_EMITTER_NODE, scene, flags, parent)
 {}*/
 
-
 } // end namespace
 
-
 #endif

+ 2 - 2
include/anki/scene/SkinNode.h

@@ -262,7 +262,7 @@ public:
 		return boneTranslations;
 	}
 
-	const boost::ptr_vector<SkinPatchNode>& getPatchNodes() const
+	const PtrVector<SkinPatchNode>& getPatchNodes() const
 	{
 		return patches;
 	}
@@ -310,7 +310,7 @@ public:
 
 private:
 	SkinResourcePointer skin; ///< The resource
-	boost::ptr_vector<SkinPatchNode> patches;
+	PtrVector<SkinPatchNode> patches;
 	Obb visibilityShapeWSpace;
 
 	/// @name Animation stuff

+ 2 - 0
include/anki/util/Assert.h

@@ -5,6 +5,7 @@
 /// debugger) and then abort
 #if NDEBUG
 #	define ANKI_ASSERT(x) ((void)0)
+#	define ANKI_ASSERTS_ENABLED 0
 #else
 
 namespace anki {
@@ -16,6 +17,7 @@ extern void akassert(bool expr, const char* exprTxt, const char* file,
 } // end namespace
 
 #	define ANKI_ASSERT(x) akassert((x), #x, __FILE__, __LINE__, __func__)
+#	define ANKI_ASSERTS_ENABLED 1
 
 #endif
 

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

@@ -44,6 +44,33 @@ inline std::string trimString(std::string& str, const char* what = " ")
 	return out;
 }
 
+/// Delete a pointer properly 
+template<typename T>
+inline void propperDelete(T*& x)
+{
+	typedef char TypeMustBeComplete[sizeof(T) ? 1 : -1];
+  	(void) sizeof(TypeMustBeComplete);
+	delete x;
+	x = nullptr;
+}
+
+/// A simple template trick to remove the pointer from one type
+///
+/// Example:
+/// @code
+/// double a = 1234.456;
+/// RemovePointer<decltype(&a)>::Type b = a;
+/// @endcode
+/// The b is of type double
+template<typename T>
+struct RemovePointer;
+
+template<typename T>
+struct RemovePointer<T*>
+{
+	typedef T Type;
+};
+
 /// @}
 /// @}
 

+ 9 - 5
include/anki/util/Observer.h

@@ -1,7 +1,8 @@
 #ifndef ANKI_UTIL_OBSERVER_H
 #define ANKI_UTIL_OBSERVER_H
 
-#include <boost/ptr_container/ptr_vector.hpp>
+#include "anki/util/Vector.h"
+#include "anki/util/Functions.h"
 
 namespace anki {
 
@@ -41,7 +42,7 @@ class Observable
 public:
 	typedef T Value;
 	typedef Observer<Value> ObserverType;
-	typedef boost::ptr_vector<ObserverType> Container;
+	typedef PtrVector<ObserverType> Container;
 
 	/// Add a new observer. The Observable takes ownership of the
 	/// pointer and its responsible of cleaning
@@ -56,7 +57,7 @@ public:
 		for(typename Container::iterator it = observers.begin();
 			it != observers.end(); ++it)
 		{
-			(*it).notify(x);
+			(*it)->notify(x);
 		}
 	}
 
@@ -101,8 +102,11 @@ private:
 #define ANKI_EMIT this->
 
 /// Connect a signal to a slot
-#define ANKI_CONNECT(_sender, _signal, _reveiver, _slot) \
-	 (_sender)->_signal.addNewObserver(new Observing_##_slot(_reveiver))
+/// @note Use RemovePointer so you can be able to use the macro outside of the
+///       _receiver body
+#define ANKI_CONNECT(_sender, _signal, _receiver, _slot) \
+	(_sender)->_signal.addNewObserver( new \
+		RemovePointer<decltype(_receiver)>::Type::Observing_##_slot(_receiver))
 
 } // namespace anki
 

+ 7 - 33
include/anki/util/Platform.h

@@ -1,58 +1,32 @@
 #ifndef ANKI_UTIL_PLATFORM_H
 #define ANKI_UTIL_PLATFORM_H
 
-
 //
 // Compiler
 //
 
-#define ANKI_COMPILER_UNKNOWN 0
-#define ANKI_COMPILER_GCC 1
-#define ANKI_COMPILER_CLANG 2
-#define ANKI_COMPILER_INTEL 3
-#define ANKI_COMPILER_OPEN64 4
-#define ANKI_COMPILER_MICROSOFT 5
-
-
 #if defined(__GNUC__)
 #	if defined(__clang__)
-#		define ANKI_COMPILER ANKI_COMPILER_CLANG
+#		define ANKI_COMPILER_CLANG 1
 #	else
-#		define ANKI_COMPILER ANKI_COMPILER_GCC
+#		define ANKI_COMPILER_GCC 1
 #	endif
 #else
-#	define ANKI_COMPILER ANKI_COMPILER_UNKNOWN
+#	define ANKI_COMPILER_UNKNOWN
 #endif
 
 //
 // Platform
 //
 
-#define ANKI_PLATFORM_UNKNOWN 0
-#define ANKI_PLATFORM_LINUX 1
-#define ANKI_PLATFORM_WINDOWS 2
-#define ANKI_PLATFORM_APPLE 3
-
-
 #if defined(__gnu_linux__)
-#	define ANKI_PLATFORM ANKI_PLATFORM_LINUX
+#	define ANKI_PLATFORM_LINUX 1
 #elif defined(__WIN32__) || defined(_WIN32)
-#	define ANKI_PLATFORM ANKI_PLATFORM_WINDOWS
+#	define ANKI_PLATFORM_WINDOWS 1
 #elif defined(__APPLE_CC__)
-#	define ANKI_PLATFORM ANKI_PLATFORM_APPLE
+#	define ANKI_PLATFORM_APPLE 1
 #else
-#	define ANKI_PLATFORM ANKI_PLATFORM_UNKNOWN
+#	define ANKI_PLATFORM_UNKNOWN 1
 #endif
 
-//
-// Arch
-//
-
-#define ANKI_ARCH_UNKNOWN 0
-#define ANKI_ARCH_X86_32 1
-#define ANKI_ARCH_X86_64 2
-
-/// XXX
-
-
 #endif

+ 11 - 5
include/anki/util/Vector.h

@@ -2,6 +2,7 @@
 #define ANKI_UTIL_VECTOR_H
 
 #include "anki/util/Assert.h"
+#include "anki/util/Functions.h"
 #include <vector>
 
 namespace anki {
@@ -16,16 +17,21 @@ template<typename T>
 class PtrVector: public Vector<T*>
 {
 public:
+	typedef Vector<T*> Base;
+
 	~PtrVector()
 	{
-		for(typename Vector<T*>::iterator it = Vector<T*>::begin();
-			it != Vector<T*>::end(); it++)
+		for(typename Base::iterator it = Base::begin(); it != Base::end(); it++)
 		{
-			typedef char TypeMustBeComplete[sizeof(T) ? 1 : -1];
-    		(void) sizeof(TypeMustBeComplete);
-			delete *it;
+			propperDelete(*it);
 		}
 	}
+
+	typename Base::iterator erase(typename Base::iterator pos)
+	{
+		propperDelete(*pos);
+		return Base::erase(pos);
+	}
 };
 
 } // end namespace anki

+ 62 - 11
shaders/IsLpGeneric.glsl

@@ -84,7 +84,7 @@ uniform usampler2D msFai0;
 uniform sampler2D msDepthFai;
 
 uniform sampler2D lightTextures[MAX_SPOT_LIGHTS];
-uniform sampler2DShadow shadowMap[MAX_SPOT_LIGHTS];
+uniform sampler2DShadow shadowMaps[MAX_SPOT_LIGHTS];
 /// @}
 
 /// @name Varyings
@@ -154,6 +154,31 @@ vec3 doPhong(in vec3 fragPosVspace, in vec3 normal, in vec3 diffuse,
 	return (difCol + specCol) * (att * lambertTerm);
 }
 
+//==============================================================================
+float calcSpotFactor(in SpotLight light, in vec3 fragPosVspace)
+{
+	vec3 l = normalize(fragPosVspace - light.light.posAndRadius.xyz);
+
+	float costheta = dot(l, light.lightDirection.xyz);
+	float spotFactor = smoothstep(
+		light.light.diffuseColor.w, 
+		light.light.specularColor.w, 
+		costheta);
+
+	return spotFactor;
+}
+
+//==============================================================================
+float calcShadowFactor(in SpotLight light, in vec3 fragPosVspace, 
+	in sampler2DShadow shadowMap)
+{
+	vec4 texCoords4 = light.texProjectionMat * vec4(fragPosVspace, 1.0);
+	vec3 texCoords3 = texCoords4.xyz / texCoords4.w;
+	float shadowFactor = texture(shadowMap, texCoords3).r;
+
+	return shadowFactor;
+}
+
 //==============================================================================
 void main()
 {
@@ -184,24 +209,50 @@ void main()
 	// Spot lights
 	uint spotLightsCount = tiles[vInstanceId].lightsCount[1];
 
-	for(uint i = pointLightsCount; i < pointLightsCount + spotLightsCount; ++i)
+	uint opt = pointLightsCount + spotLightsCount;
+	for(uint i = pointLightsCount; i < opt; ++i)
 	{
 		uint lightId = tiles[vInstanceId].lightIndices[i / 4][i % 4];
 
 		vec3 pureColor = doPhong(fragPosVspace, normal, diffuseAndSpec.rgb, 
 			specularAll, slights[lightId].light);
 
-		const vec4 lightDirAndAng = slights[lightId].lightDirection;
+		fColor += pureColor * calcSpotFactor(slights[lightId], fragPosVspace);
+	}
 
-		vec3 l = 
-			normalize(fragPosVspace - slights[lightId].light.posAndRadius.xyz);
+	// Spot lights with shadow
+	uint spotLightsShadowCount = tiles[vInstanceId].lightsCount[2];
+	opt = pointLightsCount + spotLightsCount;
 
-		float costheta = dot(l, lightDirAndAng.xyz);
-		float spotFactor = smoothstep(slights[lightId].light.diffuseColor.w,
-			slights[lightId].light.specularColor.w, costheta);
+	for(uint i = 0; i < spotLightsShadowCount; ++i)
+	{
+		uint id = i + opt;
+		uint lightId = tiles[vInstanceId].lightIndices[id / 4][id % 4];
+
+		vec3 pureColor = doPhong(fragPosVspace, normal, diffuseAndSpec.rgb, 
+			specularAll, slights[lightId].light);
+
+		float spotFactor = calcSpotFactor(slights[lightId], fragPosVspace);
+
+		float shadowFactor = calcShadowFactor(slights[lightId], fragPosVspace,
+			shadowMaps[i]);
+
+		fColor += pureColor * (spotFactor * shadowFactor);
+	}
+
+#if 0
+	if(tiles[vInstanceId].lightsCount[2] > 0)
+	{
+		uint lightId = 0;
+
+		vec3 pureColor = doPhong(fragPosVspace, normal, diffuseAndSpec.rgb, 
+			specularAll, slights[lightId].light);
+
+		float spotFactor = calcSpotFactor(slights[lightId], fragPosVspace);
 
 		fColor += pureColor * spotFactor;
 	}
+#endif
 
 #if 0
 	float depth = texture(msDepthFai, vTexCoords).r;
@@ -217,10 +268,10 @@ void main()
 	}
 #endif
 
-#if 0
-	if(tiles[vInstanceId].lightsCount[1] > 0)
+#if 1
+	if(tiles[vInstanceId].lightsCount[0] > 0)
 	{
-		fColor += vec3(0.0, 1.0, 0.0);
+		fColor += vec3(0.0, 0.1, 0.0);
 	}
 #endif
 }

+ 1 - 1
src/CMakeLists.txt

@@ -12,7 +12,7 @@ ENDFOREACH()
 
 ADD_LIBRARY(anki Dummy.cpp)
 
-TARGET_LINK_LIBRARIES(anki ${ANKI_LIBS} BulletSoftBody BulletDynamics BulletCollision LinearMath ankiglew ankitinyxml2 ankilua ${_ANKI_LIBPNG} GL)
+TARGET_LINK_LIBRARIES(anki ${ANKI_LIBS} ankiglew ankitinyxml2 ankilua ankibullet ${_ANKI_LIBPNG} GL)
 
 SET_TARGET_PROPERTIES(anki PROPERTIES LINKER_LANGUAGE CXX)
 

+ 22 - 114
src/core/App.cpp

@@ -3,12 +3,13 @@
 #include "anki/util/Exception.h"
 #include "anki/util/Platform.h"
 #include "anki/util/Filesystem.h"
+#include "anki/Config.h"
+#include "anki/util/Platform.h"
 #include <GL/glew.h>
+#include <cstring>
 #include <sstream>
-#include <SDL/SDL.h>
 #include <iostream>
 #include <iomanip>
-#include <boost/algorithm/string.hpp>
 
 namespace anki {
 
@@ -43,6 +44,7 @@ void App::handleLoggerMessages(const Logger::Info& info)
 //==============================================================================
 void App::parseCommandLineArgs(int argc, char* argv[])
 {
+#if 0
 	for(int i = 1; i < argc; i++)
 	{
 		char* arg = argv[i];
@@ -61,16 +63,12 @@ void App::parseCommandLineArgs(int argc, char* argv[])
 			abort();
 		}
 	}
+#endif
 }
 
 //==============================================================================
 void App::init(int argc, char* argv[])
 {
-	windowW = 1280;
-	windowH = 720;
-	terminalColoringEnabled = true,
-	fullScreenFlag = false;
-
 	// send output to handleMessageHanlderMsgs
 	ANKI_CONNECT(&LoggerSingleton::get(), messageRecieved, 
 		this, handleLoggerMessages);
@@ -78,70 +76,10 @@ void App::init(int argc, char* argv[])
 	parseCommandLineArgs(argc, argv);
 	printAppInfo();
 	initDirs();
-	//initWindow();
-	//SDL_Init(SDL_INIT_INPUT)
 
-	// other
-	activeCam = NULL;
 	timerTick = 1.0 / 60.0; // in sec. 1.0 / period
 }
 
-//==============================================================================
-void App::initWindow()
-{
-#if 0
-	ANKI_LOGI("SDL window initializing...");
-
-	if(SDL_Init(SDL_INIT_VIDEO) < 0)
-	{
-		throw ANKI_EXCEPTION("Failed to init SDL_VIDEO");
-	}
-
-	// print driver name
-	const char* driverName = SDL_GetCurrentVideoDriver();
-	if(driverName != NULL)
-	{
-		ANKI_LOGI("Video driver name: " << driverName);
-	}
-
-	// set GL attribs
-	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
-	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
-	// WARNING: Set this low only in deferred shading
-	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 8);
-	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
-
-	// OpenWindow
-	windowId = SDL_CreateWindow("AnKi 3D Engine", SDL_WINDOWPOS_CENTERED,
-		SDL_WINDOWPOS_CENTERED, windowW, windowH,
-		SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
-
-	if(!windowId)
-	{
-		throw ANKI_EXCEPTION("Cannot create main window");
-	}
-
-	glContext = SDL_GL_CreateContext(windowId);
-
-	// the icon
-	iconImage = SDL_LoadBMP("gfx/icon.bmp");
-	if(iconImage == NULL)
-	{
-		ANKI_LOGW("Cannot load window icon");
-	}
-	else
-	{
-		Uint32 colorkey = SDL_MapRGB(iconImage->format, 255, 0, 255);
-		SDL_SetColorKey(iconImage, SDL_SRCCOLORKEY, colorkey);
-		//SDL_WM_SetIcon(iconImage, NULL);
-		SDL_SetWindowIcon(windowId, iconImage);
-	}
-
-	ANKI_LOGI("SDL window initialization ends");
-#endif
-}
-
 //==============================================================================
 void App::initDirs()
 {
@@ -163,25 +101,6 @@ void App::initDirs()
 	createDirectory(cachePath.c_str());
 }
 
-//==============================================================================
-void App::togleFullScreen()
-{
-#if 0
-	//SDL_WM_ToggleFullScreen(mainSurf);
-	SDL_SetWindowFullscreen(windowId, fullScreenFlag ? SDL_TRUE : SDL_FALSE);
-	fullScreenFlag = !fullScreenFlag;
-#endif
-}
-
-//==============================================================================
-void App::swapBuffers()
-{
-#if 0
-	//SDL_GL_SwapBuffers();
-	SDL_GL_SwapWindow(windowId);
-#endif
-}
-
 //==============================================================================
 void App::quit(int code)
 {
@@ -195,46 +114,35 @@ void App::quit(int code)
 }
 
 //==============================================================================
-#if !defined(ANKI_REVISION)
-#	define ANKI_REVISION "unknown"
-#endif
-
 void App::printAppInfo()
 {
 	std::stringstream msg;
 	msg << "App info: ";
-#if defined(NDEBUG)
+	msg << "Version " << ANKI_VERSION_MAJOR << "." << ANKI_VERSION_MINOR 
+		<< ", ";
+#if NDEBUG
 	msg << "Release";
 #else
 	msg << "Debug";
 #endif
 	msg << " build, ";
-	msg << "platform ID " << ANKI_PLATFORM << ", ";
-	msg << "compiler ID " << ANKI_COMPILER << ", ";
+
+	msg << "platform " << 
+#if ANKI_PLATFORM_LINUX
+	"Linux"
+#elif ANKI_PLATFORM_WINDOWS
+	"Windows"
+#elif ANKI_PLATFORM_APPLE
+	"Apple"
+#else
+#	error "See file"
+#endif
+	<< ", ";
+
 	msg << "GLEW " << glewGetString(GLEW_VERSION) << ", ";
 	msg << "build date " __DATE__ ", " << "rev " << ANKI_REVISION;
 
 	ANKI_LOGI(msg.str());
 }
 
-//==============================================================================
-uint App::getDesktopWidth() const
-{
-	/*SDL_DisplayMode mode;
-	/// @todo re-enable it
-	//SDL_GetDesktopDisplayMode(&mode);
-	return mode.w;*/
-	return 0;
-}
-
-//==============================================================================
-uint App::getDesktopHeight() const
-{
-	/*SDL_DisplayMode mode;
-	/// @todo re-enable it
-	//SDL_GetDesktopDisplayMode(&mode);
-	return mode.h;*/
-	return 0;
-}
-
-} // end namespace
+} // end namespace anki

+ 146 - 0
src/core/Async.cpp

@@ -0,0 +1,146 @@
+#include "anki/core/Async.h"
+#include "anki/core/Logger.h"
+#include "anki/util/HighRezTimer.h"
+#include "anki/util/Functions.h"
+
+#define DEBUG_ASYNC 1
+
+namespace anki {
+
+//==============================================================================
+Async::~Async()
+{
+	ANKI_ASSERT(pendingJobs.size() == 0 && finishedJobs.size() == 0);
+	ANKI_ASSERT(started == false);
+}
+
+//==============================================================================
+void Async::start()
+{
+	ANKI_ASSERT(started == false);
+	started = true;
+
+#if DEBUG_ASYNC
+	ANKI_LOGI("Starting async thread...");
+#endif
+	thread = std::thread(&Async::workingFunc, this);
+}
+
+//==============================================================================
+void Async::stop()
+{
+	ANKI_ASSERT(started == true);
+
+	assignNewJobInternal(nullptr);
+	thread.join();
+	started = false; // Do after join
+}
+
+//==============================================================================
+void Async::assignNewJobInternal(AsyncJob* job)
+{
+	ANKI_ASSERT(started == true);
+
+#if DEBUG_ASYNC
+	ANKI_LOGI("Assigning new job: " << job);
+#endif
+	pendingJobsMtx.lock();
+	pendingJobs.push_back(job);
+	pendingJobsMtx.unlock();
+
+	condVar.notify_one();
+}
+
+//==============================================================================
+void Async::workingFunc()
+{
+	while(1)
+	{
+		AsyncJob* job;
+
+		// Wait for something
+		{
+			std::unique_lock<std::mutex> lock(pendingJobsMtx);
+			while(pendingJobs.empty())
+			{
+#if DEBUG_ASYNC
+				ANKI_LOGI("Waiting...");
+#endif
+				condVar.wait(lock);
+			}
+
+			job = pendingJobs.front();
+			pendingJobs.pop_front();
+		}
+
+		if(job == nullptr)
+		{
+#if DEBUG_ASYNC
+			ANKI_LOGI("Assigned to a nullptr Job. Exiting thread");
+#endif
+			break;
+		}
+
+		// Exec the loader
+		try
+		{
+			(*job)();
+		}
+		catch(const std::exception& e)
+		{
+			ANKI_LOGE("Job failed: " << e.what());
+		}
+#if DEBUG_ASYNC
+		ANKI_LOGI("Job finished: " << job);
+#endif
+
+		// Put back the response
+		{
+			std::unique_lock<std::mutex> lock(finishedJobsMtx);
+			finishedJobs.push_back(job);
+		}
+	} // end thread loop
+
+#if DEBUG_ASYNC
+	ANKI_LOGI("Working thread exiting");
+#endif
+}
+
+//==============================================================================
+void Async::cleanupFinishedJobs(F32 maxTime)
+{
+	ANKI_ASSERT(started == true);
+	HighRezTimer tim;
+	tim.start();
+
+	while(1)
+	{
+		std::unique_lock<std::mutex> lock(finishedJobsMtx);
+
+		if(finishedJobs.size() == 0)
+		{
+			break;
+		}
+
+		AsyncJob* job = finishedJobs.front();
+#if DEBUG_ASYNC
+		ANKI_LOGI("Executing post for job: " << job);
+#endif
+		job->post();
+
+		if(job->garbageCollect)
+		{
+			propperDelete(job);
+		}
+
+		finishedJobs.pop_front();
+
+		// Leave if you passed the max time
+		if(tim.getElapsedTime() >= maxTime)
+		{
+			break;
+		}
+	}
+}
+
+} // end namespace anki

+ 0 - 93
src/core/AsyncLoader.cpp

@@ -1,93 +0,0 @@
-#include "anki/core/AsyncLoader.h"
-#include "anki/core/Logger.h"
-#include "anki/core/App.h"
-
-namespace anki {
-/*
-//==============================================================================
-void AsyncLoader::start()
-{
-	ANKI_LOGI("Starting async loader thread...");
-	thread = boost::thread(&AsyncLoader::workingFunc, this);
-}
-
-//==============================================================================
-void AsyncLoader::load(const char* filename, LoadCallback loadCallback,
-	void* storage)
-{
-	ANKI_LOGI("New load request for: " << filename);
-	mutexReq.lock();
-	Request f = {filename, loadCallback, storage};
-	requests.push_back(f);
-	mutexReq.unlock();
-
-	condVar.notify_one();
-}
-
-//==============================================================================
-void AsyncLoader::workingFunc()
-{
-	while(1)
-	{
-		Request req;
-
-		// Wait for something
-		{
-			boost::mutex::scoped_lock lock(mutexReq);
-			while(requests.empty())
-			{
-				ANKI_LOGI("Waiting...");
-				condVar.wait(lock);
-			}
-
-			req = requests.front();
-			requests.pop_front();
-		}
-
-		// Exec the loader
-		bool ok = true;
-		try
-		{
-			req.loadCallback(req.filename.c_str(), req.storage);
-			ANKI_LOGI("File \"" << req.filename << "\" loaded");
-		}
-		catch(std::exception& e)
-		{
-			ANKI_LOGE("Loading \"" << req.filename <<
-				"\" failed: " << e.what());
-			ok = false;
-		}
-
-		// Put back the response
-		{
-			boost::mutex::scoped_lock lock(mutexResp);
-			Response resp = {req.filename, req.storage, ok};
-			responses.push_back(resp);
-		}
-	} // end thread loop
-}
-
-
-//==============================================================================
-// pollForFinished                                                             =
-//==============================================================================
-bool AsyncLoader::pollForFinished(std::string& filename, void* buff, bool& ok)
-{
-	boost::mutex::scoped_lock lock(mutexResp);
-	if(responses.empty())
-	{
-		return false;
-	}
-
-	Response resp = responses.front();
-	responses.pop_front();
-	lock.unlock();
-
-	filename = resp.filename;
-	buff = resp.storage;
-	ok = resp.ok;
-	return true;
-}
-*/
-
-} // end namespace

+ 3 - 1
src/core/CMakeLists.txt

@@ -1,9 +1,11 @@
-SET(ANKI_CORE_SOURCES App.cpp AsyncLoader.cpp Logger.cpp StdinListener.cpp ThreadPool.cpp Timestamp.cpp)
+SET(ANKI_CORE_SOURCES App.cpp Async.cpp Logger.cpp StdinListener.cpp ThreadPool.cpp Timestamp.cpp)
 
 IF(ANKI_WINDOW_BACKEND STREQUAL "GLXX11")
 	SET(ANKI_CORE_SOURCES ${ANKI_CORE_SOURCES} NativeWindowGlxX11.cpp)
 ELSEIF(ANKI_WINDOW_BACKEND STREQUAL "EGLX11")
 	SET(ANKI_CORE_SOURCES ${ANKI_CORE_SOURCES} NativeWindowEglX11.cpp)
+ELSEIF(ANKI_WINDOW_BACKEND STREQUAL "DUMMY")
+	SET(ANKI_CORE_SOURCES ${ANKI_CORE_SOURCES} NativeWindowDummy.cpp)
 ELSE()
 	MESSAGE(FATAL "Unrecognized ANKI_WINDOW_BACKEND: ${ANKI_WINDOW_BACKEND}")
 ENDIF()

+ 21 - 0
src/core/NativeWindowDummy.cpp

@@ -0,0 +1,21 @@
+#include "anki/core/NativeWindow.h"
+
+namespace anki {
+
+//==============================================================================
+NativeWindow::~NativeWindow
+{}
+
+//==============================================================================
+void NativeWindow::create(NativeWindowInitializer&)
+{}
+
+//==============================================================================
+void NativeWindow::destroy()
+{}
+
+//==============================================================================
+void NativeWindow::swapBuffers()
+{}
+
+} // end namespace anki

+ 1 - 1
src/input/CMakeLists.txt

@@ -3,7 +3,7 @@ FILE(GLOB ANKI_INPUT_SOURCES Input.cpp)
 IF(ANKI_WINDOW_BACKEND STREQUAL "GLXX11" OR ANKI_WINDOW_BACKEND STREQUAL "EGLX11")
 	SET(ANKI_INPUT_SOURCES ${ANKI_INPUT_SOURCES} InputX11.cpp)
 ELSE()
-	MESSAGE(FATAL "Unrecognized ANKI_WINDOW_BACKEND: ${ANKI_WINDOW_BACKEND}")
+	SET(ANKI_INPUT_SOURCES ${ANKI_INPUT_SOURCES} InputDummy.cpp)
 ENDIF()
 
 ADD_LIBRARY(ankiinput ${ANKI_INPUT_SOURCES})

+ 13 - 6
src/input/InputX11.cpp

@@ -8,8 +8,15 @@
 #	error "See file"
 #endif
 #include <X11/XKBlib.h>
+#include <cstring>
 
-#define DEBUG_EVENTS 1
+#define DEBUG_EVENTS 0
+
+#if DEBUG_EVENTS
+#	define DBG_LOGI(x_) ANKI_LOGI(x_)
+#else
+#	define DBG_LOGI(x_) ((void)0)
+#endif
 
 namespace anki {
 
@@ -170,7 +177,7 @@ void Input::handleEvents()
 
 	NativeWindowImpl& win = nativeWindow->getNative();
 	Display* disp = win.xDisplay;
-	//ANKI_LOGI("----------------------");
+	//DBG_LOGI("----------------------");
 	while(eventsPending(disp))
 	{
 		XEvent event;
@@ -184,8 +191,8 @@ skipXNextEvent:
 		case KeyPress:
 			keysym = XLookupKeysym(&event.xkey, 0);
 			keycode = event.xkey.keycode;
-			ANKI_LOGI("Key pressed: 0x" << std::hex << (U32)keysym);
 			keys[XKEYCODE2ANKI(keysym)] = 1;
+			DBG_LOGI("Key pressed: 0x" << std::hex << (U32)keysym);
 			break;
 		case KeyRelease:
 			keycode = event.xkey.keycode;
@@ -198,12 +205,12 @@ skipXNextEvent:
 				if(event1.type == KeyPress && event1.xkey.keycode == keycode)
 				{
 					// Repeat
-					//ANKI_LOGI("Key autorepeat: 0x" << std::hex << (U32)keysym);
+					//DBG_LOGI("Key autorepeat: 0x" << std::hex << (U32)keysym);
 					//++keys[XKEYCODE2ANKI(keysym)];
 				}
 				else
 				{
-					ANKI_LOGI("Key released: 0x" << std::hex << (U32)keysym);
+					DBG_LOGI("Key released: 0x" << std::hex << (U32)keysym);
 					keys[XKEYCODE2ANKI(keysym)] = 0;
 					event = event1;
 					goto skipXNextEvent;
@@ -211,7 +218,7 @@ skipXNextEvent:
 			}
 			else
 			{
-				ANKI_LOGI("Key released #2: 0x" << std::hex << (U32)keysym);
+				DBG_LOGI("Key released #2: 0x" << std::hex << (U32)keysym);
 				keys[XKEYCODE2ANKI(keysym)] = 0;
 			}
 			break;

+ 0 - 2
src/physics/CMakeLists.txt

@@ -3,5 +3,3 @@ FILE(GLOB ANKI_PHYS_HEADERS *.h)
 
 ADD_LIBRARY(ankiphysics ${ANKI_PHYS_SOURCES} ${ANKI_PHYS_HEADERS})
 
-TARGET_LINK_LIBRARIES(ankiphysics BulletDynamics 
-	BulletCollision LinearMath)

+ 4 - 4
src/physics/Character.cpp

@@ -4,10 +4,10 @@
 #include "anki/physics/RigidBody.h"
 #include "anki/physics/PhysWorld.h"
 #include <algorithm>
-#include <bullet/btBulletCollisionCommon.h>
-#include <bullet/btBulletDynamicsCommon.h>
-#include <bullet/BulletCollision/CollisionDispatch/btGhostObject.h>
-#include <bullet/BulletDynamics/Character/btKinematicCharacterController.h>
+#include <btBulletCollisionCommon.h>
+#include <btBulletDynamicsCommon.h>
+#include <BulletCollision/CollisionDispatch/btGhostObject.h>
+#include <BulletDynamics/Character/btKinematicCharacterController.h>
 
 namespace anki {
 

+ 1 - 1
src/physics/PhysWorld.cpp

@@ -1,7 +1,7 @@
-#include <bullet/BulletCollision/CollisionDispatch/btGhostObject.h>
 #include "anki/physics/PhysWorld.h"
 #include "anki/physics/Character.h"
 #include "anki/physics/MotionState.h"
+#include <BulletCollision/CollisionDispatch/btGhostObject.h>
 
 
 namespace anki {

+ 8 - 8
src/renderer/Drawer.cpp

@@ -560,7 +560,7 @@ void uniSet<TextureResourcePointer>(
 struct SetupMaterialVariableVisitor
 {
 	PassLevelKey key;
-	const Camera* cam = nullptr;
+	const Frustumable* fr = nullptr;
 	Renderer* r = nullptr;
 	Renderable* renderable = nullptr;
 
@@ -614,7 +614,7 @@ struct SetupMaterialVariableVisitor
 		const Transform* rwtrf = renderable->getRenderableWorldTransform();
 
 		Mat4 mMat = (rwtrf) ? Mat4(*rwtrf) : Mat4::getIdentity();
-		const Mat4& vpMat = cam->getViewProjectionMatrix();
+		const Mat4& vpMat = fr->getViewProjectionMatrix();
 		Mat4 mvpMat = vpMat * mMat;
 
 		Mat4 mvMat;
@@ -631,7 +631,7 @@ struct SetupMaterialVariableVisitor
 		case BI_MODEL_VIEW_MATRIX:
 			if(!mvMatCalculated)
 			{
-				mvMat = mMat * cam->getViewMatrix();
+				mvMat = mMat * fr->getViewMatrix();
 				mvMatCalculated = true;
 			}
 			uni->set(mvMat);
@@ -639,7 +639,7 @@ struct SetupMaterialVariableVisitor
 		case BI_NORMAL_MATRIX:
 			if(!mvMatCalculated)
 			{
-				mvMat = mMat * cam->getViewMatrix();
+				mvMat = mMat * fr->getViewMatrix();
 				mvMatCalculated = true;
 			}
 			uni->set(mvMat.getRotationPart());
@@ -654,7 +654,7 @@ struct SetupMaterialVariableVisitor
 //==============================================================================
 void RenderableDrawer::setupShaderProg(
 	const PassLevelKey& key,
-	const Camera& cam,
+	const Frustumable& fr,
 	Renderable& renderable)
 {
 	const Material& mtl = renderable.getMaterial();
@@ -673,7 +673,7 @@ void RenderableDrawer::setupShaderProg(
 	
 	SetupMaterialVariableVisitor vis;
 
-	vis.cam = &cam;
+	vis.fr = &fr;
 	vis.key = key;
 	vis.renderable = &renderable;
 	vis.r = r;
@@ -687,7 +687,7 @@ void RenderableDrawer::setupShaderProg(
 }
 
 //==============================================================================
-void RenderableDrawer::render(const Camera& cam, uint pass,
+void RenderableDrawer::render(const Frustumable& fr, uint pass,
 	Renderable& renderable)
 {
 	/*float dist = (node.getWorldTransform().getOrigin() -
@@ -697,7 +697,7 @@ void RenderableDrawer::render(const Camera& cam, uint pass,
 	PassLevelKey key(pass, 0);
 
 	// Setup shader
-	setupShaderProg(key, cam, renderable);
+	setupShaderProg(key, fr, renderable);
 
 	// Render
 	uint32_t indecesNum =

+ 189 - 177
src/renderer/Is.cpp

@@ -39,8 +39,8 @@ struct ShaderSpotLights
 
 struct ShaderTile
 {
-	U32 lightsCount[2]; ///< 0: Point lights number, 1: Spot lights number
-	U32 padding[2];
+	U32 lightsCount[3]; ///< 0: Point lights number, 1: Spot lights number
+	U32 padding[1];
 	// When change this change the writeTilesUbo as well
 	Array<U32, Is::MAX_LIGHTS_PER_TILE> lightIndices; 
 };
@@ -64,49 +64,95 @@ struct ShaderCommonUniforms
 /// Job to update point lights
 struct WritePointLightsUbo: ThreadJob
 {
-	ShaderPointLights* shaderLights; ///< Mapped UBO
-	U32 maxShaderLights;
-	PointLight** visibleLights;
-	U32 visibleLightsCount;
-	Is* is;
+	ShaderPointLight* shaderLights = nullptr; ///< Mapped UBO
+	PointLight** visibleLights = nullptr;
+	U32 visibleLightsCount = 0;
+	Is* is = nullptr;
 
 	void operator()(U threadId, U threadsCount)
 	{
 		U64 start, end;
 		choseStartEnd(threadId, threadsCount, visibleLightsCount, start, end);
-		is->writeLightUbo(*shaderLights, maxShaderLights, visibleLights, 
-			visibleLightsCount, start, end);
+		
+		const Camera* cam = is->cam;
+
+		for(U64 i = start; i < end; i++)
+		{
+			ANKI_ASSERT(i < Is::MAX_POINT_LIGHTS);
+			ANKI_ASSERT(i < visibleLightsCount);
+
+			ShaderPointLight& pl = shaderLights[i];
+			const PointLight& light = *visibleLights[i];
+
+			Vec3 pos = light.getWorldTransform().getOrigin().getTransformed(
+				cam->getViewMatrix());
+
+			pl.posAndRadius = Vec4(pos, light.getRadius());
+			pl.diffuseColor = light.getDiffuseColor();
+			pl.specularColor = light.getSpecularColor();
+		}
 	}
 };
 
 /// Job to update spot lights
 struct WriteSpotLightsUbo: ThreadJob
 {
-	ShaderSpotLights* shaderLights; ///< Mapped UBO
-	U32 maxShaderLights;
-	SpotLight** visibleLights;
-	U32 visibleLightsCount;
-	Is* is;
+	ShaderSpotLight* shaderLights = nullptr; ///< Mapped UBO
+	SpotLight** visibleLights = nullptr;
+	U32 visibleLightsCount = 0;
+	Is* is = nullptr;
 
 	void operator()(U threadId, U threadsCount)
 	{
 		U64 start, end;
 		choseStartEnd(threadId, threadsCount, visibleLightsCount, start, end);
-		is->writeLightUbo(*shaderLights, maxShaderLights, visibleLights, 
-			visibleLightsCount, start, end);
+
+		const Camera* cam = is->cam;
+
+		for(U64 i = start; i < end; i++)
+		{
+			ANKI_ASSERT(i < Is::MAX_SPOT_LIGHTS);
+			ANKI_ASSERT(i < visibleLightsCount);
+
+			ShaderSpotLight& slight = shaderLights[i];
+			const SpotLight& light = *visibleLights[i];
+
+			Vec3 pos = light.getWorldTransform().getOrigin().getTransformed(
+				cam->getViewMatrix());
+
+			slight.posAndRadius = Vec4(pos, light.getDistance());
+
+			slight.diffuseColor = Vec4(light.getDiffuseColor().xyz(),
+				light.getOuterAngleCos());
+
+			slight.specularColor = Vec4(light.getSpecularColor().xyz(),
+				light.getInnerAngleCos());
+
+			Vec3 lightDir = -light.getWorldTransform().getRotation().getZAxis();
+			lightDir = cam->getViewMatrix().getRotationPart() * lightDir;
+			slight.lightDirection = Vec4(lightDir, 0.0);
+			
+			static const Mat4 biasMat4(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 
+				0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0);
+			slight.texProjectionMat = biasMat4 * light.getProjectionMatrix() *
+				Mat4::combineTransformations(light.getViewMatrix(),
+				Mat4(cam->getWorldTransform()));
+		}
 	}
 };
 
 /// A job to write the tiles UBO
 struct WriteTilesUboJob: ThreadJob
 {
-	PointLight** visiblePointLights;
-	U32 visiblePointLightsCount;
-	SpotLight** visibleSpotLights;
-	U32 visibleSpotLightsCount;
-	ShaderTiles* shaderTiles;
-	U32 maxLightsPerTile;
-	Is* is;
+	PointLight** visiblePointLights = nullptr;
+	U32 visiblePointLightsCount = 0;
+	
+	SpotLight** visibleSpotLights = nullptr;
+	U32 visibleSpotLightsCount = 0; ///< Both shadow and not
+
+	ShaderTile* shaderTiles = nullptr; ///< Mapped UBO
+	U32 maxLightsPerTile = 0;
+	Is* is = nullptr;
 
 	void operator()(U threadId, U threadsCount)
 	{
@@ -114,10 +160,82 @@ struct WriteTilesUboJob: ThreadJob
 		choseStartEnd(threadId, threadsCount, 
 			Is::TILES_X_COUNT * Is::TILES_Y_COUNT, start, end);
 
-		is->writeTilesUbo(
-			visiblePointLights, visiblePointLightsCount,
-			visibleSpotLights, visibleSpotLightsCount,
-			*shaderTiles, maxLightsPerTile, start, end);
+		for(U32 i = start; i < end; i++)
+		{
+			Is::Tile& tile = is->tiles1d[i];
+			ShaderTile& stile = shaderTiles[i];
+
+			doTile(tile, stile);
+		}
+	}
+
+	/// Do a tile
+	void doTile(Is::Tile& tile, ShaderTile& stile)
+	{
+		auto& lightIndices = stile.lightIndices;
+
+		// Point lights
+		//
+
+		U pointLightsInTileCount = 0;
+		for(U i = 0; i < visiblePointLightsCount; i++)
+		{
+			const PointLight& light = *visiblePointLights[i];
+
+			if(Is::cullLight(light, tile))
+			{
+				// Use % to avoid overflows
+				const U id = pointLightsInTileCount % Is::MAX_LIGHTS_PER_TILE;
+				lightIndices[id] = i;
+				++pointLightsInTileCount;
+			}
+		}
+
+		stile.lightsCount[0] = pointLightsInTileCount;
+
+		// Spot lights
+		//
+
+		U spotLightsInTileCount = 0;
+		U spotLightsShadowInTileCount = 0;
+		for(U i = 0; i < visibleSpotLightsCount; i++)
+		{
+			const SpotLight& light = *visibleSpotLights[i];
+
+			if(Is::cullLight(light, tile))
+			{
+				const U id = (pointLightsInTileCount + spotLightsInTileCount 
+					+ spotLightsShadowInTileCount) 
+					% Is::MAX_LIGHTS_PER_TILE;
+
+				// Use % to avoid overflows
+				lightIndices[id] = i;
+
+				if(light.getShadowEnabled())
+				{
+					++spotLightsShadowInTileCount;
+				}
+				else
+				{
+					++spotLightsInTileCount;
+				}
+			}
+		}
+
+		stile.lightsCount[1] = spotLightsInTileCount;
+		stile.lightsCount[2] = spotLightsShadowInTileCount;
+
+#if 0
+		U totalLightsInTileCount = std::min(
+			pointLightsInTileCount + spotLightsInTileCount 
+			+ spotLightsShadowInTileCount,
+			Is::MAX_LIGHTS_PER_TILE);
+
+		if(pointLightsInTileCount + spotLightsInTileCount > maxLightsPerTile)
+		{
+			ANKI_LOGW("Too many lights per tile: " << lightsInTileCount);
+		}
+#endif
 	}
 };
 
@@ -197,6 +315,7 @@ void Is::initInternal(const RendererInitializer& initializer)
 		GL_RGB, GL_UNSIGNED_INT, fai);
 	fbo.create();
 	fbo.setColorAttachments({&fai});
+	fbo.setOtherAttachment(GL_DEPTH_ATTACHMENT, r->getMs().getDepthFai());
 
 	if(!fbo.isComplete())
 	{
@@ -447,126 +566,6 @@ void Is::updateTiles4PlanesInternal(const PerspectiveCamera& cam,
 	}
 }
 
-//==============================================================================
-void Is::writeLightUbo(ShaderPointLights& shaderLights, U32 maxShaderLights,
-	PointLight* visibleLights[], U32 visibleLightsCount, U start, U end)
-{
-	for(U64 i = start; i < end; i++)
-	{
-		ANKI_ASSERT(i < maxShaderLights);
-		ShaderPointLight& pl = shaderLights.lights[i];
-		const PointLight& light = *visibleLights[i];
-
-		Vec3 pos = light.getWorldTransform().getOrigin().getTransformed(
-			cam->getViewMatrix());
-
-		pl.posAndRadius = Vec4(pos, light.getRadius());
-		pl.diffuseColor = light.getDiffuseColor();
-		pl.specularColor = light.getSpecularColor();
-	}
-}
-
-//==============================================================================
-void Is::writeLightUbo(ShaderSpotLights& shaderLights, U32 maxShaderLights,
-	SpotLight* visibleLights[], U32 visibleLightsCount, U start, U end)
-{
-	for(U64 i = start; i < end; i++)
-	{
-		ANKI_ASSERT(i < maxShaderLights);
-		ShaderSpotLight& slight = shaderLights.lights[i];
-		const SpotLight& light = *visibleLights[i];
-
-		Vec3 pos = light.getWorldTransform().getOrigin().getTransformed(
-			cam->getViewMatrix());
-
-		slight.posAndRadius = Vec4(pos, light.getDistance());
-		slight.diffuseColor = Vec4(light.getDiffuseColor().xyz(),
-			light.getOuterAngleCos());
-		slight.specularColor = Vec4(light.getSpecularColor().xyz(),
-			light.getInnerAngleCos());
-
-		Vec3 lightDir = -light.getWorldTransform().getRotation().getZAxis();
-		lightDir = cam->getViewMatrix().getRotationPart() * lightDir;
-		slight.lightDirection = Vec4(lightDir, 0.0);
-		
-		static const Mat4 biasMat4(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 
-			0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0);
-		slight.texProjectionMat = biasMat4 * light.getProjectionMatrix() *
-			Mat4::combineTransformations(light.getViewMatrix(),
-			Mat4(cam->getWorldTransform()));
-	}
-}
-
-//==============================================================================
-void Is::writeTilesUbo(
-	PointLight* visiblePointLights[], U32 visiblePointLightsCount,
-	SpotLight* visibleSpotLights[], U32 visibleSpotLightsCount,
-	ShaderTiles& shaderTiles, U32 maxLightsPerTile,
-	U32 start, U32 end)
-{
-	ANKI_ASSERT(maxLightsPerTile <= MAX_LIGHTS_PER_TILE);
-
-	for(U32 i = start; i < end; i++)
-	{
-		Tile& tile = tiles1d[i];
-		Array<U32, MAX_LIGHTS_PER_TILE> lightIndices;
-
-		// Point lights
-		//
-
-		U pointLightsInTileCount = 0;
-		for(U j = 0; j < visiblePointLightsCount; j++)
-		{
-			const PointLight& light = *visiblePointLights[j];
-
-			if(cullLight(light, tile))
-			{
-				// Use % to avoid overflows
-				lightIndices[pointLightsInTileCount % maxLightsPerTile] = j;
-				++pointLightsInTileCount;
-			}
-		}
-
-		shaderTiles.tiles[i].lightsCount[0] = pointLightsInTileCount;
-
-		// Spot ligths
-		//
-
-		U spotLightsInTileCount = 0;
-		for(U j = 0; j < visibleSpotLightsCount; j++)
-		{
-			const SpotLight& light = *visibleSpotLights[j];
-
-			if(cullLight(light, tile))
-			{
-				U id = (pointLightsInTileCount + spotLightsInTileCount) 
-					% maxLightsPerTile;
-				// Use % to avoid overflows
-				lightIndices[id] = j;
-				++spotLightsInTileCount;
-			}
-		}
-
-		shaderTiles.tiles[i].lightsCount[1] = spotLightsInTileCount;
-
-#if 0
-		if(pointLightsInTileCount + spotLightsInTileCount > maxLightsPerTile)
-		{
-			ANKI_LOGW("Too many lights per tile: " << lightsInTileCount);
-		}
-#endif
-		
-		U totalLightsInTileCount = std::min(
-			pointLightsInTileCount + spotLightsInTileCount,
-			(U)maxLightsPerTile);
-
-		memcpy(
-			&(shaderTiles.tiles[i].lightIndices[0]),
-			&lightIndices[0],
-			sizeof(U32) * totalLightsInTileCount);
-	}
-}
-
 //==============================================================================
 void Is::lightPass()
 {
@@ -578,14 +577,13 @@ void Is::lightPass()
 	Array<SpotLight*, MAX_SPOT_LIGHTS> visibleSpotLights;
 	U visibleSpotLightsCount = 0;
 
-	//
-	// Quickly get the lights
-	//
-
 	U spotsNoShadowCount = 0, spotsShadowCount = 0;
 	Array<SpotLight*, MAX_SPOT_LIGHTS> visibleSpotNoShadowLights, 
 		visibleSpotShadowLights;
 
+	//
+	// Quickly get the lights
+	//
 	for(auto it = vi.getLightsBegin(); it != vi.getLightsEnd(); ++it)
 	{
 		Light* light = (*it)->getLight();
@@ -620,7 +618,7 @@ void Is::lightPass()
 		}
 	}
 
-	visibleSpotLightsCount = spotsNoShadowCount + spotsShadowCount;
+	visibleSpotLightsCount = spotsShadowCount + spotsNoShadowCount;
 
 	if(visiblePointLightsCount > MAX_POINT_LIGHTS 
 		|| visibleSpotLightsCount > MAX_SPOT_LIGHTS)
@@ -628,29 +626,41 @@ void Is::lightPass()
 		throw ANKI_EXCEPTION("Too many visible lights");
 	}
 
-	U i;
-	for(i = 0; i < spotsNoShadowCount; i++)
+	for(U i = 0; i < spotsNoShadowCount; i++)
 	{
 		visibleSpotLights[i] = visibleSpotNoShadowLights[i];
 	}
 
-	for(; i < visibleSpotLightsCount; i++)
+	for(U i = 0; i < spotsShadowCount; i++)
 	{
-		visibleSpotLights[i] = visibleSpotShadowLights[i];
+		visibleSpotLights[i + spotsNoShadowCount] = visibleSpotShadowLights[i];
 	}
 
 	//
 	// Do shadows pass
 	//
-	
+	Array<Light*, Sm::MAX_SHADOW_CASTERS> shadowCasters;
+	for(U i = 0; i < spotsShadowCount; i++)
+	{
+		shadowCasters[i] = visibleSpotShadowLights[i];
+	}
+
+	Array<Texture*, Sm::MAX_SHADOW_CASTERS> shadowmaps;
+	sm.run(&shadowCasters[0], spotsShadowCount, &shadowmaps[0]);
+
+	// Prepare state
+	fbo.bind();
+	GlStateSingleton::get().setViewport(0, 0, r->getWidth(), r->getHeight());
+	GlStateSingleton::get().disable(GL_DEPTH_TEST);
+	GlStateSingleton::get().disable(GL_BLEND);
 
 	//
-	// Write the lights UBO
+	// Write the lights UBOs
 	//
 
-	// Map
-	ShaderPointLights* lightsMappedBuff = 
-		(ShaderPointLights*)pointLightsUbo.map(
+	// Map points
+	ShaderPointLight* lightsMappedBuff = 
+		(ShaderPointLight*)pointLightsUbo.map(
 		0, 
 		sizeof(ShaderPointLight) * visiblePointLightsCount,
 		GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
@@ -659,7 +669,6 @@ void Is::lightPass()
 	for(U i = 0; i < threadPool.getThreadsCount(); i++)
 	{
 		jobs[i].shaderLights = lightsMappedBuff;
-		jobs[i].maxShaderLights = MAX_POINT_LIGHTS;
 		jobs[i].visibleLights = &visiblePointLights[0];
 		jobs[i].visibleLightsCount = visiblePointLightsCount;
 		jobs[i].is = this;
@@ -671,17 +680,16 @@ void Is::lightPass()
 	threadPool.waitForAllJobsToFinish();
 	pointLightsUbo.unmap();
 
-	// Map
-	ShaderSpotLights* shaderSpotLights = (ShaderSpotLights*)spotLightsUbo.map(
+	// Map spots
+	ShaderSpotLight* shaderSpotLights = (ShaderSpotLight*)spotLightsUbo.map(
 		0,
-		sizeof(ShaderSpotLights) * visibleSpotLightsCount,
+		sizeof(ShaderSpotLight) * visibleSpotLightsCount,
 		GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
 
 	WriteSpotLightsUbo jobs2[ThreadPool::MAX_THREADS];
 	for(U i = 0; i < threadPool.getThreadsCount(); i++)
 	{
 		jobs2[i].shaderLights = shaderSpotLights;
-		jobs2[i].maxShaderLights = MAX_SPOT_LIGHTS;
 		jobs2[i].visibleLights = &visibleSpotLights[0];
 		jobs2[i].visibleLightsCount = visibleSpotLightsCount;
 		jobs2[i].is = this;
@@ -697,7 +705,7 @@ void Is::lightPass()
 	// Update the tiles UBO
 	//
 
-	ShaderTiles* stiles = (ShaderTiles*)tilesUbo.map(
+	ShaderTile* stiles = (ShaderTile*)tilesUbo.map(
 		GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
 
 	WriteTilesUboJob tjobs[ThreadPool::MAX_THREADS];
@@ -705,10 +713,11 @@ void Is::lightPass()
 	{
 		tjobs[i].visiblePointLights = &visiblePointLights[0];
 		tjobs[i].visiblePointLightsCount = visiblePointLightsCount;
+
 		tjobs[i].visibleSpotLights = &visibleSpotLights[0];
 		tjobs[i].visibleSpotLightsCount = visibleSpotLightsCount;
+
 		tjobs[i].shaderTiles = stiles;
-		tjobs[i].maxLightsPerTile = MAX_LIGHTS_PER_TILE;
 		tjobs[i].is = this;
 
 		threadPool.assignNewJob(i, &tjobs[i]);
@@ -728,15 +737,23 @@ void Is::lightPass()
 	lightPassProg->findUniformVariable("msDepthFai").set(
 		r->getMs().getDepthFai());
 
+	for(U i = 0; i < spotsShadowCount; i++)
+	{
+		char str[128];
+		sprintf(str, "shadowMaps[%u]", (unsigned int)i);
+
+		lightPassProg->findUniformVariable(str).set(*shadowmaps[i]);
+	}
+
+	/*GLint shadowMapsLoc = lightPassProg->findUniformVariable("msFai0");
+	shadowmaps*/
+
 	r->drawQuadInstanced(TILES_Y_COUNT * TILES_X_COUNT);
 }
 
 //==============================================================================
 void Is::run()
 {
-	GlStateSingleton::get().disable(GL_DEPTH_TEST);
-	GlStateSingleton::get().disable(GL_BLEND);
-
 	Scene& scene = r->getScene();
 	cam = &scene.getActiveCamera();
 
@@ -766,12 +783,7 @@ void Is::run()
 	// Update tiles
 	updateTiles();
 
-	// Shadows
-	sm.run();
-
-	// Do the light pass
-	fbo.bind();
-	GlStateSingleton::get().setViewport(0, 0, r->getWidth(), r->getHeight());
+	// Do the light pass including the shadow passes
 	lightPass();
 }
 

+ 6 - 8
src/renderer/MainRenderer.cpp

@@ -1,5 +1,4 @@
 #include "anki/renderer/MainRenderer.h"
-#include "anki/core/App.h"
 #include "anki/core/Logger.h"
 #include "anki/renderer/Deformer.h"
 #include "anki/util/Filesystem.h"
@@ -25,14 +24,15 @@ void MainRenderer::init(const Renderer::Initializer& initializer_)
 
 	dbgTq.reset(new Query(GL_TIME_ELAPSED));
 
+	windowWidth = initializer_.width;
+	windowHeight = initializer_.height;
+
 	// init the offscreen Renderer
 	//
 	RendererInitializer initializer = initializer_;
 	renderingQuality = initializer.mainRendererQuality;
-	initializer.width = AppSingleton::get().getWindowWidth() *
-		renderingQuality;
-	initializer.height = AppSingleton::get().getWindowHeight() *
-		renderingQuality;
+	initializer.width *= renderingQuality;
+	initializer.height *= renderingQuality;
 	Renderer::init(initializer);
 	dbg.init(initializer);
 	deformer.reset(new Deformer);
@@ -105,9 +105,7 @@ void MainRenderer::render(Scene& scene)
 	//
 	glBindFramebuffer(GL_FRAMEBUFFER, 0); // Bind the window framebuffer
 
-	GlStateSingleton::get().setViewport(
-		0, 0, AppSingleton::get().getWindowWidth(),
-		AppSingleton::get().getWindowHeight());
+	GlStateSingleton::get().setViewport(0, 0, windowWidth, windowHeight);
 	GlStateSingleton::get().disable(GL_DEPTH_TEST);
 	GlStateSingleton::get().disable(GL_BLEND);
 	sProg->bind();

+ 19 - 37
src/renderer/Sm.cpp

@@ -22,6 +22,11 @@ void Sm::init(const RendererInitializer& initializer)
 	resolution = initializer.is.sm.resolution;
 
 	// Init the shadowmaps
+	if(initializer.is.sm.maxLights > MAX_SHADOW_CASTERS)
+	{
+		throw ANKI_EXCEPTION("Too many shadow casters");
+	}
+
 	sms.resize(initializer.is.sm.maxLights);
 	for(Shadowmap& sm : sms)
 	{
@@ -69,46 +74,19 @@ void Sm::afterDraw()
 }
 
 //==============================================================================
-void Sm::run()
+void Sm::run(Light* shadowCasters[], U32 shadowCastersCount, 
+	Texture* shadowmaps[])
 {
 	ANKI_ASSERT(enabled);
 
-	Camera& cam = r->getScene().getActiveCamera();
-	VisibilityInfo& vi = cam.getFrustumable()->getVisibilityInfo();
-
 	prepareDraw();
 
-	// Get the shadow casters
-	//
-	const U MAX_SHADOW_CASTERS = 256;
-	ANKI_ASSERT(getMaxLightsCount() < MAX_SHADOW_CASTERS);
-	Array<Light*, MAX_SHADOW_CASTERS> casters;
-	U32 castersCount = 0;
-
-	for(auto it = vi.getLightsBegin(); it != vi.getLightsEnd(); ++it)
-	{
-		Light* light = (*it)->getLight();
-
-		if(light->getShadowEnabled())
-		{
-			casters[castersCount % getMaxLightsCount()] = light;
-			++castersCount;
-		}
-	}
-
-#if 1
-	if(castersCount > getMaxLightsCount())
-	{
-		ANKI_LOGW("Too many shadow casters: " << castersCount);
-	}
-#endif
-
-	castersCount = castersCount % getMaxLightsCount();
-
 	// render all
-	for(U32 i = 0; i < castersCount; i++)
+	for(U32 i = 0; i < shadowCastersCount; i++)
 	{
-		doLight(*casters[i]);
+		Texture* sm = doLight(*shadowCasters[i]);
+		ANKI_ASSERT(sm != nullptr);
+		shadowmaps[i] = sm;
 	}
 
 	afterDraw();
@@ -153,9 +131,10 @@ Sm::Shadowmap& Sm::bestCandidate(Light& light)
 }
 
 //==============================================================================
-void Sm::doLight(Light& light)
+Texture* Sm::doLight(Light& light)
 {
 	Shadowmap& sm = bestCandidate(light);
+	Texture* outSm = &sm.tex;
 
 	Frustumable* fr = light.getFrustumable();
 	ANKI_ASSERT(fr != nullptr);
@@ -189,7 +168,7 @@ void Sm::doLight(Light& light)
 
 	if(!shouldUpdate)
 	{
-		return;
+		return outSm;
 	}
 
 	sm.timestamp = Timestamp::getTimestamp();
@@ -197,11 +176,14 @@ void Sm::doLight(Light& light)
 	//
 	// Render
 	//
+	sm.fbo.bind();
+
 	for(auto it = vi.getRenderablesBegin(); it != vi.getRenderablesEnd(); ++it)
 	{
-		r->getSceneDrawer().render(r->getScene().getActiveCamera(), 1,
-			*((*it)->getRenderable()));
+		r->getSceneDrawer().render(*fr, 1, *((*it)->getRenderable()));
 	}
+
+	return outSm;
 }
 
 } // end namespace anki

+ 1 - 1
src/resource/MeshLoader.cpp

@@ -271,4 +271,4 @@ void MeshLoader::createVertTangents()
 	}
 }
 
-} // end namespace
+} // end namespace anki

+ 0 - 1
src/resource/ShaderProgramPrePreprocessor.cpp

@@ -1,5 +1,4 @@
 #include "anki/resource/ShaderProgramPrePreprocessor.h"
-#include "anki/misc/Parser.h"
 #include "anki/util/Filesystem.h"
 #include "anki/util/Exception.h"
 #include "anki/util/Functions.h"

+ 2 - 6
src/resource/SkelAnim.cpp

@@ -1,15 +1,11 @@
 #include "anki/resource/SkelAnim.h"
-#include "anki/misc/Parser.h"
-
 
 namespace anki {
 
-
-//==============================================================================
-// load                                                                        =
 //==============================================================================
 void SkelAnim::load(const char* filename)
 {
+#if 0
 	scanner::Scanner scanner(filename);
 	const scanner::Token* token;
 
@@ -66,7 +62,7 @@ void SkelAnim::load(const char* filename)
 
 
 	framesNum = keyframes[keyframes.size() - 1] + 1;
+#endif
 }
 
-
 } // end namespace

+ 0 - 1
src/scene/ParticleEmitterNode.cpp

@@ -1,4 +1,3 @@
-#include <boost/foreach.hpp>
 #include <btBulletCollisionCommon.h>
 #include <btBulletDynamicsCommon.h>
 #include "anki/scene/ParticleEmitterNode.h"

+ 21 - 8
testapp/Main.cpp

@@ -12,7 +12,6 @@
 #include "anki/resource/Material.h"
 #include "anki/scene/Scene.h"
 #include "anki/resource/SkelAnim.h"
-#include "anki/misc/Parser.h"
 #include "anki/scene/ParticleEmitterNode.h"
 #include "anki/physics/Character.h"
 #include "anki/renderer/Renderer.h"
@@ -81,9 +80,9 @@ void init()
 
 	// lights
 	Vec3 lpos(-100.0, 0.0, -50.0);
-	for(int i = 0; i < 50; i++)
+	for(int i = 0; i < 0; i++)
 	{
-		for(int j = 0; j < 10; j++)
+		for(int j = 0; j < 0; j++)
 		{
 			std::string name = "plight" + std::to_string(i) + std::to_string(j);
 
@@ -111,6 +110,18 @@ void init()
 	spot->setSpecularColor(Vec4(1.0));
 	spot->loadTexture("gfx/lights/flashlight.tga");
 	spot->setDistance(30.0);
+	spot->setShadowEnabled(false);
+
+
+	spot = new SpotLight("spot1", &scene, Movable::MF_NONE, nullptr);
+	spot->setOuterAngle(Math::toRad(45.0));
+	spot->setInnerAngle(Math::toRad(15.0));
+	spot->setLocalTransform(Transform(Vec3(5.3, 4.3, 3.0),
+		Mat3::getIdentity(), 1.0));
+	spot->setDiffuseColor(Vec4(1.0, 0.0, 0.0, 0.0));
+	spot->setSpecularColor(Vec4(0.0, 0.0, 1.0, 0.0));
+	spot->loadTexture("gfx/lights/flashlight.tga");
+	spot->setDistance(30.0);
 	spot->setShadowEnabled(true);
 #endif
 
@@ -194,11 +205,11 @@ void mainLoopExtra()
 	{
 		mover = SceneSingleton::get().findSceneNode("spot0")->getMovable();
 	}
-	/*if(in.getKey(KC_4))
+	if(in.getKey(KC_4))
 	{
-		mover = SceneSingleton::get().findSceneNode("point0")->getMovable();
+		mover = SceneSingleton::get().findSceneNode("spot1")->getMovable();
 	}
-	if(in.getKey(KC_5))
+	/*if(in.getKey(KC_5))
 	{
 		mover = SceneSingleton::get().findSceneNode("point1")->getMovable();
 	}*/
@@ -227,7 +238,7 @@ void mainLoopExtra()
 
 	if(in.getKey(KC_A)) mover->moveLocalX(-dist);
 	if(in.getKey(KC_D)) mover->moveLocalX(dist);
-	if(in.getKey(KC_LSHIFT)) mover->moveLocalY(dist);
+	if(in.getKey(KC_Z)) mover->moveLocalY(dist);
 	if(in.getKey(KC_SPACE)) mover->moveLocalY(-dist);
 	if(in.getKey(KC_W)) mover->moveLocalZ(-dist);
 	if(in.getKey(KC_S)) mover->moveLocalZ(dist);
@@ -325,7 +336,7 @@ void initSubsystems(int argc, char* argv[])
 	// Main renderer
 	RendererInitializer initializer;
 	initializer.ms.ez.enabled = true;
-	initializer.dbg.enabled = false;
+	initializer.dbg.enabled = true;
 	initializer.is.sm.bilinearEnabled = true;
 	initializer.is.sm.enabled = true;
 	initializer.is.sm.pcfEnabled = true;
@@ -342,6 +353,8 @@ void initSubsystems(int argc, char* argv[])
 	initializer.pps.bl.blurringIterationsNum = 2;
 	initializer.pps.bl.sideBlurFactor = 1.0;
 	initializer.mainRendererQuality = 1.0;
+	initializer.width = nwinit.width;
+	initializer.height = nwinit.height;
 
 	MainRendererSingleton::get().init(initializer);
 

+ 39 - 0
tests/Main.cpp

@@ -3,10 +3,49 @@
 
 using namespace anki;
 
+struct LoggerMessageHandler
+{
+	ANKI_HAS_SLOTS(LoggerMessageHandler)
+
+	void handleLoggerMessages(const Logger::Info& info)
+	{
+		std::ostream* out = NULL;
+		const char* x = NULL;
+
+		switch(info.type)
+		{
+		case Logger::LMT_NORMAL:
+			out = &std::cout;
+			x = "Info";
+			break;
+
+		case Logger::LMT_ERROR:
+			out = &std::cerr;
+			x = "Error";
+			break;
+
+		case Logger::LMT_WARNING:
+			out = &std::cerr;
+			x = "Warn";
+			break;
+		}
+
+		(*out) << "(" << info.file << ":" << info.line << " "<< info.func 
+			<< ") " << x << ": " << info.msg << std::endl;
+	}
+
+	ANKI_SLOT(handleLoggerMessages, const Logger::Info&)
+};
+
+static LoggerMessageHandler msgh;
+
 int main(int argc, char** argv)
 {
 	// Call a few singletons to avoid memory leak confusion
 	LoggerSingleton::get();
 
+	ANKI_CONNECT(&LoggerSingleton::get(), messageRecieved, 
+		&msgh, handleLoggerMessages);
+
 	return TesterSingleton::get().run(argc, argv);
 }

+ 48 - 0
tests/core/Async.cpp

@@ -0,0 +1,48 @@
+#include "tests/framework/Framework.h"
+#include "anki/core/Async.h"
+#include "anki/core/Logger.h"
+#include "anki/util/HighRezTimer.h"
+#include "anki/util/StdTypes.h"
+
+namespace anki {
+
+/// Struct for our tests
+struct TestJob: AsyncJob
+{
+	U32 result = 0;
+
+	void operator()()
+	{
+		ANKI_LOGI("-- Job " << this << " starting");
+		HighRezTimer::sleep(1.0);
+		++result;
+		ANKI_LOGI("-- Job " << this << " done");
+	}
+
+	void post()
+	{
+		ANKI_LOGI("-- Job " << this << " starting post");
+		HighRezTimer::sleep(1.0);
+		++result;
+		ANKI_LOGI("-- Job " << this << " done post");
+	}
+};
+
+} // end namespace anki
+
+ANKI_TEST(Core, Async)
+{
+	Async a;
+	TestJob pjob;
+
+	a.start();
+	a.assignNewJob(&pjob);
+
+	HighRezTimer::sleep(2.0);
+
+	a.cleanupFinishedJobs(123.0);
+	a.stop();
+
+	ANKI_TEST_EXPECT_EQ(pjob.result, 2);
+}
+

+ 54 - 0
tests/core/ThreadPool.cpp

@@ -0,0 +1,54 @@
+#include "tests/framework/Framework.h"
+#include "anki/core/ThreadPool.h"
+#include "anki/core/Logger.h"
+#include "anki/util/StdTypes.h"
+
+namespace anki {
+
+/// Struct for our tests
+struct TestJobTP: ThreadJob
+{
+	U32 in = 0;
+	U32 iterations = 0;
+
+	void operator()(U /*threadId*/, U /*threadsCount*/)
+	{
+		for(U32 i = 0; i < iterations; i++)	
+		{
+			++in;
+		}
+	}
+};
+
+} // end namespace anki
+
+ANKI_TEST(Core, ThreadPool)
+{
+	const U32 threadsCount = 4;
+	const U32 repeat = 500;
+	ThreadPool* tp = new ThreadPool;
+
+	tp->init(threadsCount);
+	TestJobTP jobs[threadsCount];
+
+	for(U32 i = 1; i < repeat; i++)
+	{
+		U32 iterations = rand() % 100000;
+
+		for(U32 j = 0; j < threadsCount; j++)
+		{
+			jobs[j].in = i;
+			jobs[j].iterations = iterations;
+
+			tp->assignNewJob(j, &jobs[j]);
+		}
+
+		tp->waitForAllJobsToFinish();
+
+		for(U32 j = 0; j < threadsCount; j++)
+		{
+			ANKI_TEST_EXPECT_EQ(jobs[j].in, i + iterations);
+		}
+	}
+}
+