2
0
Эх сурвалжийг харах

Tweaking lens flare. Adding bits on .zip file support. Start adding support for compute based optimizations on IS

Panagiotis Christopoulos Charitos 12 жил өмнө
parent
commit
83f3759fc7

+ 1 - 19
CMakeLists.txt

@@ -47,10 +47,6 @@ ELSE()
 	SET(ANKI_MATH_SIMD "NONE")
 	SET(ANKI_MATH_SIMD "NONE")
 ENDIF()
 ENDIF()
 
 
-# libz and libpng
-OPTION(ANKI_SYSTEM_LIBZ "Use the system's libz" OFF)
-OPTION(ANKI_SYSTEM_LIBPNG "Use the system's libpng" OFF)
-
 # Take a wild guess on the windowing system
 # Take a wild guess on the windowing system
 IF(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
 IF(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
 	SET(_WIN_BACKEND "GLXX11")
 	SET(_WIN_BACKEND "GLXX11")
@@ -111,19 +107,6 @@ IF(ANKI_VALGRIND_HAPPY)
 	ADD_DEFINITIONS("-DGLIBCXX_FORCE_NEW")
 	ADD_DEFINITIONS("-DGLIBCXX_FORCE_NEW")
 ENDIF()
 ENDIF()
 
 
-# libz and libpng
-IF(ANKI_SYSTEM_LIBZ)
-	SET(_ANKI_LIBZ "z")
-ELSE()
-	SET(_ANKI_LIBZ "ankiz")
-ENDIF()
-
-IF(ANKI_SYSTEM_LIBPNG)
-	SET(_ANKI_LIBPNG "png")
-ELSE()
-	SET(_ANKI_LIBPNG "ankipng")
-ENDIF()
-
 # Disable GLU in GLEW
 # Disable GLU in GLEW
 ADD_DEFINITIONS(-DGLEW_NO_GLU)
 ADD_DEFINITIONS(-DGLEW_NO_GLU)
 
 
@@ -205,8 +188,7 @@ INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/anki/Config.h" DESTINATION "${INCLUDE
 #
 #
 # Include & lib directories
 # Include & lib directories
 #
 #
-INCLUDE_DIRECTORIES("extern/tinyxml2/include" "extern/lua" "extern/png" "extern/bullet" "include"
-	"${CMAKE_CURRENT_BINARY_DIR}")
+INCLUDE_DIRECTORIES("extern/tinyxml2/include" "extern/lua" "extern/png" "extern/z" "extern/bullet" "include" "${CMAKE_CURRENT_BINARY_DIR}")
 
 
 IF(ANKI_WINDOW_BACKEND STREQUAL "GLXX11" OR ANKI_WINDOW_BACKEND STREQUAL "MACOS")
 IF(ANKI_WINDOW_BACKEND STREQUAL "GLXX11" OR ANKI_WINDOW_BACKEND STREQUAL "MACOS")
 	INCLUDE_DIRECTORIES("extern/GLEW/include")
 	INCLUDE_DIRECTORIES("extern/GLEW/include")

+ 1 - 1
countlines.sh

@@ -1 +1 @@
-wc -l `find -name '*.h' -o -name '*.hpp' -o -name '*.c' -o -name '*.cpp' -o -name '*.glsl' -o -name '*.py'`
+wc -l `find ./src ./include ./tests ./testapp ./tools ./shaders -name '*.h' -o -name '*.hpp' -o -name '*.c' -o -name '*.cpp' -o -name '*.glsl' -o -name '*.py'`

BIN
engine_data/lens_dirt.ankitex


BIN
engine_data/noise.ankitex


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

@@ -101,6 +101,10 @@ private:
 	/// Do the actual pass
 	/// Do the actual pass
 	void lightPass();
 	void lightPass();
 
 
+	/// Reject occluded lights. This pass issues a compute job to reject lights
+	/// from the tiles
+	void rejectLights();
+
 	/// Prepare GL for rendering
 	/// Prepare GL for rendering
 	void setState();
 	void setState();
 };
 };

+ 9 - 1
include/anki/renderer/Lf.h

@@ -30,10 +30,18 @@ public:
 	}
 	}
 
 
 private:
 private:
-	ShaderProgramResourcePointer drawProg;
+	/// Pseudo lens flare program
+	ShaderProgramResourcePointer pseudoProg;
+	/// Real lens flare program
+	ShaderProgramResourcePointer realProg;
 	Texture fai;
 	Texture fai;
 	Fbo fbo;
 	Fbo fbo;
 	TextureResourcePointer lensDirtTex;
 	TextureResourcePointer lensDirtTex;
+	U8 maxFlaresPerLight;
+	U8 maxLightsWithFlares;
+	Ubo flareDataUbo;
+	const ShaderProgramUniformBlock* ublock;
+	
 
 
 	void initInternal(const RendererInitializer& initializer);
 	void initInternal(const RendererInitializer& initializer);
 };
 };

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

@@ -36,8 +36,7 @@ public:
 	/// @}
 	/// @}
 
 
 	void init(const RendererInitializer& initializer);
 	void init(const RendererInitializer& initializer);
-	void run(Light* shadowCasters[], U32 shadowCastersCount, 
-		Array<U32,  MAX_SHADOW_CASTERS>& shadowmapLayers);
+	void run(Light* shadowCasters[], U32 shadowCastersCount);
 
 
 private:
 private:
 	Texture sm2DArrayTex;
 	Texture sm2DArrayTex;

+ 35 - 3
include/anki/scene/Light.h

@@ -101,13 +101,37 @@ public:
 
 
 	Bool hasLensFlare() const
 	Bool hasLensFlare() const
 	{
 	{
-		return lensFlareTex.isLoaded();
+		return flaresTex.isLoaded();
 	}
 	}
 
 
 	const Texture& getLensFlareTexture() const
 	const Texture& getLensFlareTexture() const
 	{
 	{
 		ANKI_ASSERT(hasLensFlare());
 		ANKI_ASSERT(hasLensFlare());
-		return *lensFlareTex;
+		return *flaresTex;
+	}
+	const Vec2& getLensFlaresSize() const
+	{
+		return flaresSize;
+	}
+	void setLensFlaresSize(const Vec2& val)
+	{
+		flaresSize = val;
+	}
+	const Vec2& getLensFlaresStretchMultiplier() const
+	{
+		return flaresStretchMultiplier;
+	}
+	void setLensFlaresStretchMultiplier(const Vec2& val)
+	{
+		flaresStretchMultiplier = val;
+	}
+	F32 getLensFlaresAlpha() const
+	{
+		return flaresAlpha;
+	}
+	void setLensFlaresAlpha(F32 val)
+	{
+		flaresAlpha = val;
 	}
 	}
 	/// @}
 	/// @}
 
 
@@ -125,7 +149,15 @@ private:
 	LightType type;
 	LightType type;
 	Vec4 color = Vec4(1.0);
 	Vec4 color = Vec4(1.0);
 	Vec4 specColor = Vec4(1.0);
 	Vec4 specColor = Vec4(1.0);
-	TextureResourcePointer lensFlareTex;
+
+	/// @name Flare struff
+	/// @{
+	TextureResourcePointer flaresTex;
+	Vec2 flaresSize = Vec2(0.2);
+	Vec2 flaresStretchMultiplier = Vec2(1.0);
+	F32 flaresAlpha = 1.0;
+	/// @}
+
 	Bool8 shadow = false;
 	Bool8 shadow = false;
 	U8 shadowMapIndex = 0xFF; ///< Used by the renderer
 	U8 shadowMapIndex = 0xFF; ///< Used by the renderer
 };
 };

+ 9 - 1
include/anki/util/File.h

@@ -120,11 +120,19 @@ public:
 	/// @}
 	/// @}
 
 
 private:
 private:
-	U8 flags; ///< All the flags
+	U8 flags; ///< All the flags. Initialy zero and set on open
 	void* file; ///< A native type
 	void* file; ///< A native type
 
 
 	/// Get the current machine's endianness
 	/// Get the current machine's endianness
 	static Endianness getMachineEndianness();
 	static Endianness getMachineEndianness();
+
+	/// Open a C file
+	void openCFile(const char* filename, U8 flags);
+
+	/// Open an archive and the file inside
+	/// @param[in] archive The filename of the archive
+	/// @param[in] archived The filename of the file inside the archive
+	void openZipFile(const char* archive, const char* archived, U8 flags);
 };
 };
 
 
 /// Directory exists?
 /// Directory exists?

+ 28 - 13
shaders/PpsLfPass.glsl → shaders/PpsLfPseudoPass.glsl

@@ -1,4 +1,5 @@
-// XXX
+// Screen space lens flare. Used the technique from here
+// http://john-chapman-graphics.blogspot.no/2013/02/pseudo-lens-flare.html
 
 
 #pragma anki start vertexShader
 #pragma anki start vertexShader
 
 
@@ -7,10 +8,11 @@
 #pragma anki start fragmentShader
 #pragma anki start fragmentShader
 
 
 #define MAX_GHOSTS 4
 #define MAX_GHOSTS 4
-#define FMAX_GHOSTS (float(MAX_GHOSTS))
 #define GHOST_DISPERSAL (0.7)
 #define GHOST_DISPERSAL (0.7)
 #define HALO_WIDTH 0.4
 #define HALO_WIDTH 0.4
-#define DISTORTION 4.0
+#define CHROMATIC_DISTORTION 4.0
+#define ENABLE_CHROMATIC_DISTORTION 1
+#define ENABLE_HALO 1
 
 
 uniform sampler2D tex;
 uniform sampler2D tex;
 uniform sampler2D lensDirtTex;
 uniform sampler2D lensDirtTex;
@@ -23,12 +25,16 @@ vec3 textureDistorted(
 	in sampler2D tex,
 	in sampler2D tex,
 	in vec2 texcoord,
 	in vec2 texcoord,
 	in vec2 direction, // direction of distortion
 	in vec2 direction, // direction of distortion
-	in vec3 distortion ) // per-channel distortion factor  
+	in vec3 distortion) // per-channel distortion factor  
 {
 {
+#if ENABLE_CHROMATIC_DISTORTION
 	return vec3(
 	return vec3(
 		texture(tex, texcoord + direction * distortion.r).r,
 		texture(tex, texcoord + direction * distortion.r).r,
 		texture(tex, texcoord + direction * distortion.g).g,
 		texture(tex, texcoord + direction * distortion.g).g,
 		texture(tex, texcoord + direction * distortion.b).b);
 		texture(tex, texcoord + direction * distortion.b).b);
+#else
+	return texture(tex, texcoord).rgb;
+#endif
 }
 }
 
 
 void main()
 void main()
@@ -41,28 +47,37 @@ void main()
 
 
 	const vec2 texelSize = 1.0 / vec2(TEX_DIMENSIONS);
 	const vec2 texelSize = 1.0 / vec2(TEX_DIMENSIONS);
 
 
-	const vec3 distortion = 
-		vec3(-texelSize.x * DISTORTION, 0.0, texelSize.x * DISTORTION);
+	const vec3 distortion = vec3(
+		-texelSize.x * CHROMATIC_DISTORTION, 
+		0.0, 
+		texelSize.x * CHROMATIC_DISTORTION);
+
+	const float lenOfHalf = length(vec2(0.5));
+
+	vec2 direction = normalize(ghostVec);
 
 
-	// sample ghosts:  
 	vec3 result = vec3(0.0);
 	vec3 result = vec3(0.0);
+
+	// sample ghosts:  
 	for(int i = 0; i < MAX_GHOSTS; ++i) 
 	for(int i = 0; i < MAX_GHOSTS; ++i) 
 	{ 
 	{ 
 		vec2 offset = fract(texcoord + ghostVec * float(i));
 		vec2 offset = fract(texcoord + ghostVec * float(i));
 
 
-		float weight = length(vec2(0.5) - offset) / length(vec2(0.5));
+		float weight = length(vec2(0.5) - offset) / lenOfHalf;
 		weight = pow(1.0 - weight, 10.0);
 		weight = pow(1.0 - weight, 10.0);
 
 
-		result += textureDistorted(tex, offset, offset, distortion) * weight;
+		result += textureDistorted(tex, offset, direction, distortion) * weight;
 	}
 	}
 
 
-	// sample halo:
+	// sample halo
+#if ENABLE_HALO
 	vec2 haloVec = normalize(ghostVec) * HALO_WIDTH;
 	vec2 haloVec = normalize(ghostVec) * HALO_WIDTH;
 	float weight = 
 	float weight = 
-		length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5));
-	weight = pow(1.0 - weight, 5.0);
-	result += textureDistorted(tex, texcoord + haloVec, haloVec, distortion) 
+		length(vec2(0.5) - fract(texcoord + haloVec)) / lenOfHalf;
+	weight = pow(1.0 - weight, 20.0);
+	result += textureDistorted(tex, texcoord + haloVec, direction, distortion) 
 		* weight;
 		* weight;
+#endif
 
 
 	// lens dirt
 	// lens dirt
 	result *= texture(lensDirtTex, vTexCoords).rgb;
 	result *= texture(lensDirtTex, vTexCoords).rgb;

+ 57 - 0
shaders/PpsLfSpritePass.glsl

@@ -0,0 +1,57 @@
+// Draw sprite lens flare
+
+#pragma anki start vertexShader
+
+// Per flare information
+struct Flare
+{
+	vec4 posScale; // xy: Position, z: Scale, w: Scale again
+	vec4 alphaDepth; // x: alpha, y: texture depth
+};
+
+// The block contains data for all flares
+layout(std140) uniform flaresBlock
+{
+	Flare flares[MAX_FLARES];
+};
+
+layout(location = 0) in vec2 position;
+
+out vec3 vTexCoords;
+out flat float vAlpha;
+
+void main()
+{
+	Flare flare = flares[gl_InstanceID];
+
+	vTexCoords = vec3((position * 0.5) + 0.5, flare.alphaDepth.y);
+
+	vec4 posScale = flare.posScale;
+	gl_Position = vec4(position * posScale.zw + posScale.xy , 0.0, 1.0);
+
+	vAlpha = flare.alphaDepth.x;
+}
+
+#pragma anki start fragmentShader
+
+#if SINGLE_FLARE
+uniform sampler2D image;
+#else
+uniform sampler2DArray images;
+#endif
+
+in vec3 vTexCoords;
+in flat float vAlpha;
+
+out vec3 fColor;
+
+void main()
+{
+#if SINGLE_FLARE
+	vec3 col = texture(images, vTexCoords.st).rgb;
+#else
+	vec3 col = texture(images, vTexCoords).rgb;
+#endif
+
+	fColor = col * vAlpha;
+}

+ 1 - 1
src/CMakeLists.txt

@@ -22,7 +22,7 @@ ELSE()
 	SET(_GL GLESv2 EGL mali)
 	SET(_GL GLESv2 EGL mali)
 ENDIF()
 ENDIF()
 
 
-TARGET_LINK_LIBRARIES(anki ${ANKI_LIBS} ankitinyxml2 ankilua ankibullet ${_ANKI_LIBPNG} ${ANKI_GPERFTOOLS_LIBS} pthread ${_GL})
+TARGET_LINK_LIBRARIES(anki ${ANKI_LIBS} ankitinyxml2 ankilua ankibullet ankiz ankipng ${ANKI_GPERFTOOLS_LIBS} pthread ${_GL})
 
 
 SET_TARGET_PROPERTIES(anki PROPERTIES LINKER_LANGUAGE CXX)
 SET_TARGET_PROPERTIES(anki PROPERTIES LINKER_LANGUAGE CXX)
 
 

+ 2 - 1
src/gl/Texture.cpp

@@ -299,7 +299,8 @@ void Texture::create(const Initializer& init)
 		case GL_TEXTURE_2D_ARRAY:
 		case GL_TEXTURE_2D_ARRAY:
 		case GL_TEXTURE_3D:
 		case GL_TEXTURE_3D:
 			{
 			{
-				ANKI_ASSERT(depth > 1);
+				//ANKI_ASSERT(depth > 1);
+				ANKI_ASSERT(depth > 0);
 
 
 				// Gather the data
 				// Gather the data
 				Vector<U8> data;
 				Vector<U8> data;

+ 49 - 42
src/renderer/Is.cpp

@@ -28,6 +28,7 @@ static const U TILES_Y_COUNT = ANKI_RENDERER_TILES_Y_COUNT;
 static const U TILES_COUNT = TILES_X_COUNT * TILES_Y_COUNT;
 static const U TILES_COUNT = TILES_X_COUNT * TILES_Y_COUNT;
 
 
 //==============================================================================
 //==============================================================================
+/// Check if the prev ground light vector is almost equal to the current
 static Bool groundVectorsEqual(const Vec3& prev, const Vec3& crnt)
 static Bool groundVectorsEqual(const Vec3& prev, const Vec3& crnt)
 {
 {
 	Bool out = true;
 	Bool out = true;
@@ -40,6 +41,14 @@ static Bool groundVectorsEqual(const Vec3& prev, const Vec3& crnt)
 	return out;
 	return out;
 }
 }
 
 
+//==============================================================================
+/// Clamp a value
+template<typename T, typename Y>
+void clamp(T& in, Y limit)
+{
+	in = std::min(in, (T)limit);
+}
+
 //==============================================================================
 //==============================================================================
 // Shader structs and block representations. All positions and directions in
 // Shader structs and block representations. All positions and directions in
 // viewspace
 // viewspace
@@ -57,15 +66,7 @@ struct Tile
 
 
 struct Tiles
 struct Tiles
 {
 {
-	Tile tiles[TILES_Y_COUNT][TILES_X_COUNT];
-};
-
-struct Tilegrid
-{
-	Plane planesX[TILES_X_COUNT - 1];
-	Plane planesY[TILES_Y_COUNT - 1];
-	Plane planesNear[TILES_Y_COUNT][TILES_X_COUNT];
-	Plane planesFar[TILES_Y_COUNT][TILES_X_COUNT];
+	Array<Array<Tile, TILES_X_COUNT>, TILES_Y_COUNT> tiles;
 };
 };
 
 
 struct Light
 struct Light
@@ -151,12 +152,12 @@ struct WriteLightsJob: ThreadJob
 	std::atomic<U32>* spotLightsCount = nullptr;
 	std::atomic<U32>* spotLightsCount = nullptr;
 	std::atomic<U32>* spotTexLightsCount = nullptr;
 	std::atomic<U32>* spotTexLightsCount = nullptr;
 		
 		
-	std::atomic<U32> 
-		(*tilePointLightsCount)[TILES_Y_COUNT][TILES_X_COUNT] = nullptr;
-	std::atomic<U32> 
-		(*tileSpotLightsCount)[TILES_Y_COUNT][TILES_X_COUNT] = nullptr;
-	std::atomic<U32> 
-		(*tileSpotTexLightsCount)[TILES_Y_COUNT][TILES_X_COUNT] = nullptr;
+	Array<Array<std::atomic<U32>, TILES_X_COUNT>, TILES_Y_COUNT>*
+		tilePointLightsCount = nullptr;
+	Array<Array<std::atomic<U32>, TILES_X_COUNT>, TILES_Y_COUNT>*
+		tileSpotLightsCount = nullptr;
+	Array<Array<std::atomic<U32>, TILES_X_COUNT>, TILES_Y_COUNT>*
+		tileSpotTexLightsCount = nullptr;
 
 
 	Tiler* tiler = nullptr;
 	Tiler* tiler = nullptr;
 	Is* is = nullptr;
 	Is* is = nullptr;
@@ -528,16 +529,6 @@ void Is::initInternal(const RendererInitializer& initializer)
 			GL_DYNAMIC_DRAW);
 			GL_DYNAMIC_DRAW);
 	}
 	}
 
 
-	// tilegrid
-	if(gpuPath)
-	{
-		tilegridBuffer.create(
-			GL_SHADER_STORAGE_BUFFER, 
-			sizeof(shader::Tilegrid), 
-			nullptr, 
-			GL_STATIC_DRAW);
-	}
-
 	ANKI_LOGI("Creating BOs: lights: " << calcLigthsUboSize() << "B tiles: "
 	ANKI_LOGI("Creating BOs: lights: " << calcLigthsUboSize() << "B tiles: "
 		<< sizeof(shader::Tiles) << "B");
 		<< sizeof(shader::Tiles) << "B");
 
 
@@ -585,6 +576,18 @@ void Is::initInternal(const RendererInitializer& initializer)
 	}
 	}
 }
 }
 
 
+//==============================================================================
+void Is::rejectLights()
+{
+#if ANKI_GL == ANKI_GL_DESKTOP
+	if(!useCompute())
+	{
+		return;
+	}
+
+#endif
+}
+
 //==============================================================================
 //==============================================================================
 void Is::lightPass()
 void Is::lightPass()
 {
 {
@@ -630,16 +633,15 @@ void Is::lightPass()
 		}
 		}
 	}
 	}
 
 
-	// Sanitize the counts
-	visiblePointLightsCount = visiblePointLightsCount % MAX_POINT_LIGHTS;
-	visibleSpotLightsCount = visibleSpotLightsCount % MAX_SPOT_LIGHTS;
-	visibleSpotTexLightsCount = visibleSpotTexLightsCount % MAX_SPOT_TEX_LIGHTS;
+	// Sanitize the counters
+	clamp(visiblePointLightsCount, MAX_POINT_LIGHTS);
+	clamp(visibleSpotLightsCount, MAX_SPOT_LIGHTS);
+	clamp(visibleSpotTexLightsCount, MAX_SPOT_TEX_LIGHTS);
 
 
 	//
 	//
 	// Do shadows pass
 	// Do shadows pass
 	//
 	//
-	Array<U32, Sm::MAX_SHADOW_CASTERS> shadowmapLayers;
-	sm.run(&shadowCasters[0], visibleSpotTexLightsCount, shadowmapLayers);
+	sm.run(&shadowCasters[0], visibleSpotTexLightsCount);
 
 
 	//
 	//
 	// Write the lights and tiles UBOs
 	// Write the lights and tiles UBOs
@@ -664,7 +666,7 @@ void Is::lightPass()
 	ANKI_ASSERT(spotTexLightsOffset + spotTexLightsSize <= calcLigthsUboSize());
 	ANKI_ASSERT(spotTexLightsOffset + spotTexLightsSize <= calcLigthsUboSize());
 
 
 	// Fire the super jobs
 	// Fire the super jobs
-	WriteLightsJob jobs[ThreadPool::MAX_THREADS];
+	Array<WriteLightsJob, ThreadPool::MAX_THREADS> jobs;
 
 
 	U8 clientBuffer[MIN_LIGHTS_UBO_SIZE * 2]; // Aproximate size
 	U8 clientBuffer[MIN_LIGHTS_UBO_SIZE * 2]; // Aproximate size
 	ANKI_ASSERT(MIN_LIGHTS_UBO_SIZE * 2 >= calcLigthsUboSize());
 	ANKI_ASSERT(MIN_LIGHTS_UBO_SIZE * 2 >= calcLigthsUboSize());
@@ -674,12 +676,12 @@ void Is::lightPass()
 	std::atomic<U32> spotLightsAtomicCount(0);
 	std::atomic<U32> spotLightsAtomicCount(0);
 	std::atomic<U32> spotTexLightsAtomicCount(0);
 	std::atomic<U32> spotTexLightsAtomicCount(0);
 
 
-	std::atomic<U32> 
-		tilePointLightsCount[TILES_Y_COUNT][TILES_X_COUNT];
-	std::atomic<U32> 
-		tileSpotLightsCount[TILES_Y_COUNT][TILES_X_COUNT];
-	std::atomic<U32> 
-		tileSpotTexLightsCount[TILES_Y_COUNT][TILES_X_COUNT];
+	Array<Array<std::atomic<U32>, TILES_X_COUNT>, TILES_Y_COUNT>
+		tilePointLightsCount;
+	Array<Array<std::atomic<U32>, TILES_X_COUNT>, TILES_Y_COUNT>
+		tileSpotLightsCount;
+	Array<Array<std::atomic<U32>, TILES_X_COUNT>, TILES_Y_COUNT>
+		tileSpotTexLightsCount;
 
 
 	for(U y = 0; y < TILES_Y_COUNT; y++)
 	for(U y = 0; y < TILES_Y_COUNT; y++)
 	{
 	{
@@ -733,14 +735,19 @@ void Is::lightPass()
 		for(U x = 0; x < TILES_X_COUNT; x++)
 		for(U x = 0; x < TILES_X_COUNT; x++)
 		{
 		{
 			clientTiles.tiles[y][x].lightsCount[0] = 
 			clientTiles.tiles[y][x].lightsCount[0] = 
-				tilePointLightsCount[y][x].load() % MAX_POINT_LIGHTS_PER_TILE;
+				tilePointLightsCount[y][x].load();
+			clamp(clientTiles.tiles[y][x].lightsCount[0], 
+				MAX_POINT_LIGHTS_PER_TILE);
 
 
 			clientTiles.tiles[y][x].lightsCount[2] = 
 			clientTiles.tiles[y][x].lightsCount[2] = 
-				tileSpotLightsCount[y][x].load() % MAX_SPOT_LIGHTS_PER_TILE;
+				tileSpotLightsCount[y][x].load();
+			clamp(clientTiles.tiles[y][x].lightsCount[2], 
+				MAX_SPOT_LIGHTS_PER_TILE);
 
 
 			clientTiles.tiles[y][x].lightsCount[3] = 
 			clientTiles.tiles[y][x].lightsCount[3] = 
-				tileSpotTexLightsCount[y][x].load() 
-				% MAX_SPOT_TEX_LIGHTS_PER_TILE;
+				tileSpotTexLightsCount[y][x].load();
+			clamp(clientTiles.tiles[y][x].lightsCount[3], 
+				MAX_SPOT_TEX_LIGHTS_PER_TILE);
 		}
 		}
 	}
 	}
 
 

+ 180 - 6
src/renderer/Lf.cpp

@@ -69,13 +69,35 @@ void Lf::initInternal(const RendererInitializer& initializer)
 		return;
 		return;
 	}
 	}
 
 
-	// Load the shader
+	maxFlaresPerLight = initializer.pps.lf.maxFlaresPerLight;
+	maxLightsWithFlares = initializer.pps.lf.maxLightsWithFlares;
+
+	// Load program 1
 	std::string pps = "#define TEX_DIMENSIONS vec2(" 
 	std::string pps = "#define TEX_DIMENSIONS vec2(" 
 		+ std::to_string(r->getPps().getHdr().getFai().getWidth()) + ".0, "
 		+ std::to_string(r->getPps().getHdr().getFai().getWidth()) + ".0, "
 		+ std::to_string(r->getPps().getHdr().getFai().getHeight()) + ".0)\n";
 		+ std::to_string(r->getPps().getHdr().getFai().getHeight()) + ".0)\n";
 
 
-	drawProg.load(ShaderProgramResource::createSrcCodeToCache(
-		"shaders/PpsLfPass.glsl", pps.c_str()).c_str());
+	pseudoProg.load(ShaderProgramResource::createSrcCodeToCache(
+		"shaders/PpsLfPseudoPass.glsl", pps.c_str()).c_str());
+
+	// Load program 2
+	pps = "#define MAX_FLARES "
+		+ std::to_string(maxFlaresPerLight * maxLightsWithFlares) + "\n";
+	std::string fname = ShaderProgramResource::createSrcCodeToCache(
+		"shaders/PpsLfSpritePass.glsl", pps.c_str());
+	realProg.load(fname.c_str());
+
+	ublock = &realProg->findUniformBlock("flaresBlock");
+	ublock->setBinding(0);
+
+	PtrSize blockSize = sizeof(Flare) * maxFlaresPerLight * maxLightsWithFlares;
+	if(ublock->getSize() != blockSize)
+	{
+		throw ANKI_EXCEPTION("Incorrect block size");
+	}
+
+	// Init UBO
+	flareDataUbo.create(blockSize, nullptr);
 
 
 	// Create the FAI
 	// Create the FAI
 	fai.create2dFai(r->getPps().getHdr().getFai().getWidth(), 
 	fai.create2dFai(r->getPps().getHdr().getFai().getWidth(), 
@@ -98,13 +120,17 @@ void Lf::run()
 {
 {
 	ANKI_ASSERT(enabled);
 	ANKI_ASSERT(enabled);
 
 
+	//
+	// First pass
+	//
+
 	// Set the common state
 	// Set the common state
 	const Texture& inTex = r->getPps().getHdr().getFai();
 	const Texture& inTex = r->getPps().getHdr().getFai();
 
 
 	fbo.bind();
 	fbo.bind();
-	drawProg->bind();
-	drawProg->findUniformVariable("tex").set(inTex);
-	drawProg->findUniformVariable("lensDirtTex").set(*lensDirtTex);
+	pseudoProg->bind();
+	pseudoProg->findUniformVariable("tex").set(inTex);
+	pseudoProg->findUniformVariable("lensDirtTex").set(*lensDirtTex);
 
 
 	GlStateSingleton::get().setViewport(
 	GlStateSingleton::get().setViewport(
 		0, 0, inTex.getWidth(), inTex.getHeight());
 		0, 0, inTex.getWidth(), inTex.getHeight());
@@ -112,6 +138,154 @@ void Lf::run()
 	GlStateSingleton::get().disable(GL_BLEND);
 	GlStateSingleton::get().disable(GL_BLEND);
 
 
 	r->drawQuad();
 	r->drawQuad();
+
+	//
+	// Rest of the passes
+	//
+
+	// Retrieve some things
+	SceneGraph& scene = r->getSceneGraph();
+	Camera& cam = scene.getActiveCamera();
+	VisibilityTestResults& vi = *cam.getVisibilityTestResults();
+
+	// Iterate the visible light and get those that have lens flare
+	Array<Light*, ANKI_MAX_LIGHTS_WITH_FLARE> lights;
+	U lightsCount = 0;
+	for(auto it = vi.getLightsBegin(); it != vi.getLightsEnd(); ++it)
+	{
+		SceneNode& sn = *(*it).node;
+		ANKI_ASSERT(sn.getLight());
+		Light* light = sn.getLight();
+
+		if(light->hasLensFlare())
+		{
+			lights[lightsCount % maxLightsWithFlares] = light;
+			++lightsCount;
+		}
+	}
+
+	// Early exit
+	if(lightsCount == 0)
+	{
+		return;
+	}
+
+	lightsCount = lightsCount % (maxLightsWithFlares + 1);
+
+	// Sort the lights using their lens flare texture
+	std::sort(lights.begin(), lights.begin() + lightsCount, LightSortFunctor());
+
+	// Write the UBO and get the groups
+	//
+	Array<Flare, ANKI_MAX_FLARES> flares;
+	U flaresCount = 0;
+
+	// Contains the number of flares per flare texture
+	Array<U, ANKI_MAX_LIGHTS_WITH_FLARE> groups;
+	Array<const Texture*, ANKI_MAX_LIGHTS_WITH_FLARE> texes;
+	U groupsCount = 0;
+
+	GLuint lastTexId = 0;
+
+	// Iterate all lights and update the flares as well as the groups
+	while(lightsCount-- != 0)
+	{
+		Light& light = *lights[lightsCount];
+		const Texture& tex = light.getLensFlareTexture();
+		const U depth = tex.getDepth();
+
+		// Transform
+		Vec3 posWorld = light.getWorldTransform().getOrigin();
+		Vec4 posClip = cam.getViewProjectionMatrix() * Vec4(posWorld, 1.0);
+
+		if(posClip.x() > posClip.w() || posClip.x() < -posClip.w()
+			|| posClip.y() > posClip.w() || posClip.y() < -posClip.w())
+		{
+			continue;
+		}
+
+		Vec2 posNdc = posClip.xy() / posClip.w();
+
+		Vec2 dir = -posNdc;
+		F32 len = dir.getLength();
+		dir /= len; // Normalize dir
+
+		// New group?
+		if(lastTexId != tex.getGlId())
+		{
+			texes[groupsCount] = &tex;
+			groups[groupsCount] = 0;
+			lastTexId = tex.getGlId();
+
+			++groupsCount;
+		}
+
+		// First flare 
+		F32 stretchFactor = 1.0 - posNdc.getLength();
+		stretchFactor *= stretchFactor;
+
+		Vec2 stretch = light.getLensFlaresStretchMultiplier() * stretchFactor;
+
+		flares[flaresCount].pos = posNdc;
+		flares[flaresCount].scale = 
+			light.getLensFlaresSize() * Vec2(1.0, r->getAspectRatio()) 
+			* stretch;
+		flares[flaresCount].depth = 0.0;
+		flares[flaresCount].alpha = light.getLensFlaresAlpha() * stretchFactor;
+		++flaresCount;
+		++groups[groupsCount - 1];
+
+		// The rest of the flares
+		for(U d = 1; d < depth; d++)
+		{
+			// Write the "flares"
+			F32 factor = d / ((F32)depth - 1.0);
+
+			F32 flen = len * 2.0 * factor;
+
+			flares[flaresCount].pos = posNdc + dir * flen;
+
+			flares[flaresCount].scale = 
+				light.getLensFlaresSize() * Vec2(1.0, r->getAspectRatio())
+				* ((len - flen) * 2.0);
+
+			flares[flaresCount].depth = d;
+
+			flares[flaresCount].alpha = light.getLensFlaresAlpha();
+
+			// Advance
+			++flaresCount;
+			++groups[groupsCount - 1];
+		}
+	}
+
+	// Time to render
+	//
+
+	// Write the buffer
+	flareDataUbo.write(&flares[0], 0, sizeof(Flare) * flaresCount);
+
+	// Set the common state
+	realProg->bind();
+	GlStateSingleton::get().disable(GL_DEPTH_TEST);
+	GlStateSingleton::get().enable(GL_BLEND);
+	GlStateSingleton::get().setBlendFunctions(GL_ONE, GL_ONE);
+
+	PtrSize offset = 0;
+	for(U i = 0; i < groupsCount; i++)
+	{
+		const Texture& tex = *texes[i];
+		U instances = groups[i];
+		PtrSize buffSize = sizeof(Flare) * instances;
+
+		realProg->findUniformVariable("images").set(tex);
+
+		flareDataUbo.setBindingRange(0, offset, buffSize);
+		
+		r->drawQuadInstanced(instances);
+
+		offset += buffSize;
+	}
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 2 - 3
src/renderer/Sm.cpp

@@ -92,8 +92,7 @@ void Sm::afterDraw()
 }
 }
 
 
 //==============================================================================
 //==============================================================================
-void Sm::run(Light* shadowCasters[], U32 shadowCastersCount, 
-	Array<U32,  MAX_SHADOW_CASTERS>& shadowmapLayers)
+void Sm::run(Light* shadowCasters[], U32 shadowCastersCount)
 {
 {
 	ANKI_ASSERT(enabled);
 	ANKI_ASSERT(enabled);
 
 
@@ -104,7 +103,7 @@ void Sm::run(Light* shadowCasters[], U32 shadowCastersCount,
 	{
 	{
 		Shadowmap* sm = doLight(*shadowCasters[i]);
 		Shadowmap* sm = doLight(*shadowCasters[i]);
 		ANKI_ASSERT(sm != nullptr);
 		ANKI_ASSERT(sm != nullptr);
-		shadowmapLayers[i] = sm->layerId;
+		(void)sm;
 	}
 	}
 
 
 	afterDraw();
 	afterDraw();

+ 1 - 1
src/resource/TextureResource.cpp

@@ -41,7 +41,7 @@ void TextureResource::load(const Image& img)
 		|| img.getTextureType() == Image::TT_3D)
 		|| img.getTextureType() == Image::TT_3D)
 	{
 	{
 		init.depth = img.getDepth();
 		init.depth = img.getDepth();
-		ANKI_ASSERT(init.depth > 1);
+		//ANKI_ASSERT(init.depth > 1);
 	}
 	}
 	else
 	else
 	{
 	{

+ 1 - 1
src/scene/Light.cpp

@@ -23,7 +23,7 @@ Light::Light(
 
 
 	if(lensFlareFile)
 	if(lensFlareFile)
 	{
 	{
-		lensFlareTex.load(lensFlareFile);
+		flaresTex.load(lensFlareFile);
 	}
 	}
 
 
 	addNewProperty(new ReadWritePointerProperty<Vec4>("color", &color));
 	addNewProperty(new ReadWritePointerProperty<Vec4>("color", &color));

+ 88 - 4
src/util/File.cpp

@@ -4,6 +4,7 @@
 #include <fstream>
 #include <fstream>
 #include <cstring>
 #include <cstring>
 #include <cstdarg>
 #include <cstdarg>
+#include <contrib/minizip/unzip.h>
 
 
 namespace anki {
 namespace anki {
 
 
@@ -56,6 +57,53 @@ void File::open(const char* filename, U8 flags_)
 
 
 	ANKI_ASSERT((flags_ & OF_READ) != (flags_ & OF_WRITE));
 	ANKI_ASSERT((flags_ & OF_READ) != (flags_ & OF_WRITE));
 
 
+	//
+	// In the following lines determine the file type and open it
+	//
+
+	const char* aext = ".ankizip";
+	const char* ptrToArchiveExt = strstr(filename, aext);
+
+	if(ptrToArchiveExt == nullptr)
+	{
+		// It's a C file
+		openCFile(filename, flags_);
+	}
+	else
+	{
+		// Maybe it's a file in a zipped archive
+
+		PtrSize fnameLen = strlen(filename);
+		PtrSize extLen = strlen(aext);
+		PtrSize archLen = (PtrSize)(ptrToArchiveExt - filename) + extLen;
+
+		if(archLen + 1 >= fnameLen)
+		{
+			throw ANKI_EXCEPTION("Wrong file inside the archive");
+		}
+
+		std::string archive(filename, archLen);
+
+		if(directoryExists(archive.c_str()))
+		{
+			// It's a directory so failback to C file
+			openCFile(filename, flags_);
+		}
+		else
+		{
+			// It's a ziped file
+
+			std::string filenameInArchive(
+				filename + archLen + 1, fnameLen - archLen);
+
+			openZipFile(archive.c_str(), filenameInArchive.c_str(), flags_);
+		}
+	}
+}
+
+//==============================================================================
+void File::openCFile(const char* filename, U8 flags_)
+{
 	const char* openMode;
 	const char* openMode;
 
 
 	if(flags_ & OF_READ)
 	if(flags_ & OF_READ)
@@ -78,25 +126,61 @@ void File::open(const char* filename, U8 flags_)
 
 
 	// Open
 	// Open
 	file = (FILE*)fopen(filename, openMode);
 	file = (FILE*)fopen(filename, openMode);
-	if(!file)
+	if(file == nullptr)
 	{
 	{
 		throw ANKI_EXCEPTION("Failed to open file");
 		throw ANKI_EXCEPTION("Failed to open file");
 	}
 	}
 
 
-	flags |= flags_ | FT_C;
+	flags = flags_ | FT_C;
 
 
-	// Check endianness
+	// If the open() DIDN'T provided us the file endianess 
 	if((flags_ & (E_BIG_ENDIAN | E_LITTLE_ENDIAN)) == 0)
 	if((flags_ & (E_BIG_ENDIAN | E_LITTLE_ENDIAN)) == 0)
 	{
 	{
-		// Set the default endianness
+		// Set the machine's endianness
 		flags |= getMachineEndianness();
 		flags |= getMachineEndianness();
 	}
 	}
 	else
 	else
 	{
 	{
+		// Else just make sure that just one of the flags is set
 		ANKI_ASSERT((flags_ & E_BIG_ENDIAN) != (flags_ & E_LITTLE_ENDIAN));
 		ANKI_ASSERT((flags_ & E_BIG_ENDIAN) != (flags_ & E_LITTLE_ENDIAN));
 	}
 	}
 }
 }
 
 
+//==============================================================================
+void File::openZipFile(const char* archive, const char* archived, U8 flags_)
+{
+	if(flags_ & OF_WRITE)
+	{
+		throw ANKI_EXCEPTION("Cannot write inside archives");
+	}
+
+	// Open archive
+	unzFile zfile = unzOpen(archive);
+	if(zfile == nullptr)
+	{
+		throw ANKI_EXCEPTION("Failed to open file");
+	}
+
+	// Locate archived
+	const int caseSensitive = 1;
+	if(unzLocateFile(zfile, archived, caseSensitive) != UNZ_OK)
+	{
+		unzClose(zfile);
+		throw ANKI_EXCEPTION("Failed to locate file in archive");
+	}
+
+	// Open file
+	if(unzOpenCurrentFile(zfile) != UNZ_OK )
+	{
+		unzClose(zfile);
+		throw ANKI_EXCEPTION("unzOpenCurrentFile failed");
+	}
+
+	static_assert(sizeof(file) == sizeof(zfile), "See file");
+	file = (void*)zfile;
+	flags = flags_ | FT_C;
+}
+
 //==============================================================================
 //==============================================================================
 void File::close()
 void File::close()
 {
 {

+ 7 - 3
testapp/Main.cpp

@@ -201,11 +201,13 @@ void init()
 		PointLight* point =
 		PointLight* point =
 			new PointLight(("vase_plight" + std::to_string(i)).c_str(),
 			new PointLight(("vase_plight" + std::to_string(i)).c_str(),
 			&scene, nullptr, Movable::MF_NONE, 
 			&scene, nullptr, Movable::MF_NONE, 
-			(i == 0) ? "data/textures/lens_flare/flares1.ankitex" : nullptr);
+			(i != 100) ? "data/textures/lens_flare/flares0.ankitex" : nullptr);
 		point->setRadius(2.0);
 		point->setRadius(2.0);
 		point->setLocalOrigin(lightPos);
 		point->setLocalOrigin(lightPos);
 		point->setDiffuseColor(Vec4(3.0, 0.2, 0.0, 0.0));
 		point->setDiffuseColor(Vec4(3.0, 0.2, 0.0, 0.0));
 		point->setSpecularColor(Vec4(1.0, 1.0, 0.0, 0.0));
 		point->setSpecularColor(Vec4(1.0, 1.0, 0.0, 0.0));
+		point->setLensFlaresStretchMultiplier(Vec2(10.0, 1.0));
+		point->setLensFlaresAlpha(1.0);
 
 
 		LightEventData eventData;
 		LightEventData eventData;
 		eventData.light = point;
 		eventData.light = point;
@@ -424,8 +426,10 @@ void mainLoopExtra()
 
 
 	if(in.getMousePosition() != Vec2(0.0))
 	if(in.getMousePosition() != Vec2(0.0))
 	{
 	{
-		mover->rotateLocalY(-ang * in.getMousePosition().x() * mouseSensivity *
-			MainRendererSingleton::get().getAspectRatio());
+		F32 angY = -ang * in.getMousePosition().x() * mouseSensivity *
+			MainRendererSingleton::get().getAspectRatio();
+
+		mover->rotateLocalY(angY);
 		mover->rotateLocalX(ang * in.getMousePosition().y() * mouseSensivity);
 		mover->rotateLocalX(ang * in.getMousePosition().y() * mouseSensivity);
 	}
 	}
 
 

+ 2 - 1
tools/texture/ankitexture.py

@@ -607,7 +607,8 @@ def main():
 		raise Exception("Not enough images for cube generation")
 		raise Exception("Not enough images for cube generation")
 
 
 	if (typ == TT_3D or typ == TT_2D_ARRAY) and len(in_files) < 2:
 	if (typ == TT_3D or typ == TT_2D_ARRAY) and len(in_files) < 2:
-		raise Exception("Not enough images for 2DArray/3D texture")
+		#raise Exception("Not enough images for 2DArray/3D texture")
+		printw("Not enough images for 2DArray/3D texture")
 
 
 	if typ == TT_2D and len(in_files) != 1:
 	if typ == TT_2D and len(in_files) != 1:
 		raise Exception("Only one image for 2D textures needed")
 		raise Exception("Only one image for 2D textures needed")