Bläddra i källkod

Fixing more bugs

Panagiotis Christopoulos Charitos 13 år sedan
förälder
incheckning
599d1328f5

+ 1 - 1
CMakeLists.txt

@@ -129,7 +129,7 @@ LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/extern/lib64")
 #
 # Defines & flags
 #
-ADD_DEFINITIONS(-DANKI_MATH_INTEL_SIMD
+ADD_DEFINITIONS(#-DANKI_MATH_INTEL_SIMD
 	-Dthread_local=__thread -pedantic-errors -pedantic -ansi -Wall -Winline 
 	-W -Wwrite-strings -Wno-unused -Wfatal-errors -Werror -Wno-long-long 
 	-msse4 -std=c++0x)

+ 1 - 1
include/anki/collision/Frustum.h

@@ -106,7 +106,7 @@ protected:
 	/// Used to check against the frustum
 	std::array<Plane, FP_COUNT> planes;
 
-	Transform trf; ///< Retain the transformation
+	Transform trf = Transform::getIdentity(); ///< Retain the transformation
 
 	/// Called when a viewing variable changes. It recalculates the planes and
 	/// the other variables

+ 3 - 3
include/anki/gl/BufferObject.h

@@ -61,10 +61,10 @@ public:
 	void bind() const
 	{
 		ANKI_ASSERT(isCreated());
-		if(lastBindedBo != this)
+		//if(lastBindedBo != this)
 		{
 			glBindBuffer(target, glId);
-			lastBindedBo = this;
+			//lastBindedBo = this;
 		}
 	}
 
@@ -73,7 +73,7 @@ public:
 	{
 		ANKI_ASSERT(isCreated());
 		glBindBuffer(target, 0);
-		lastBindedBo = nullptr;
+		//lastBindedBo = nullptr;
 	}
 
 	/// Creates a new BO with the given parameters and checks if everything

+ 17 - 8
include/anki/gl/Vao.h

@@ -1,9 +1,10 @@
 #ifndef ANKI_GL_VAO_H
 #define ANKI_GL_VAO_H
 
-#include "anki/util/Assert.h"
 #include "anki/gl/GlException.h"
 #include "anki/gl/Ogl.h"
+#include "anki/util/Assert.h"
+#include "anki/util/NonCopyable.h"
 
 namespace anki {
 
@@ -11,19 +12,14 @@ class ShaderProgramAttributeVariable;
 class Vbo;
 
 /// Vertex array object. Non-copyable to avoid instantiating it in the stack
-class Vao
+class Vao: public NonCopyable
 {
 public:
-	// Non-copyable
-	Vao(const Vao&) = delete;
-	Vao& operator=(const Vao&) = delete;
-
 	/// @name Constructors/Destructor
 	/// @{
 
 	/// Default
 	Vao()
-		: glId(0)
 	{}
 
 	/// Destroy VAO from the OpenGL context
@@ -101,22 +97,32 @@ public:
 	    GLsizei stride,
 	    const GLvoid* pointer);
 
+#if !defined(NDEBUG)
+	int getAttachmentsCount() const
+	{
+		return attachments;
+	}
+#endif
+
 	/// Attach an element array buffer VBO
 	void attachElementArrayBufferVbo(const Vbo& vbo);
 
 	/// Bind it
 	void bind() const
 	{
+		ANKI_ASSERT(isCreated());
 		if(current != this)
 		{
 			glBindVertexArray(glId);
 			current = this;
 		}
+		ANKI_ASSERT(getCurrentVertexArrayBinding() == glId);
 	}
 
 	/// Unbind all VAOs
 	void unbind() const
 	{
+		ANKI_ASSERT(isCreated());
 		if(current == this)
 		{
 			glBindVertexArray(0);
@@ -126,7 +132,10 @@ public:
 
 private:
 	static thread_local const Vao* current;
-	GLuint glId; ///< The OpenGL id
+	GLuint glId = 0; ///< The OpenGL id
+#if !defined(NDEBUG)
+	int attachments = 0;
+#endif
 
 	bool isCreated() const
 	{

+ 2 - 2
include/anki/math/Mat3.inl.h

@@ -751,14 +751,14 @@ inline void Mat3::invert()
 // getZero
 inline const Mat3& Mat3::getZero()
 {
-	static Mat3 zero(0.0);
+	static const Mat3 zero(0.0);
 	return zero;
 }
 
 // getIdentity
 inline const Mat3& Mat3::getIdentity()
 {
-	static Mat3 ident(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
+	static const Mat3 ident(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
 	return ident;
 }
 

+ 3 - 3
include/anki/math/Mat4.inl.h

@@ -728,15 +728,15 @@ inline Vec3 Mat4::getTranslationPart() const
 // getIdentity
 inline const Mat4& Mat4::getIdentity()
 {
-	static Mat4 ident(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
-		0.0, 0.0, 0.0, 0.0, 1.0);
+	static const Mat4 ident(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
+		1.0, 0.0, 0.0, 0.0, 0.0, 1.0);
 	return ident;
 }
 
 // getZero
 inline const Mat4& Mat4::getZero()
 {
-	static Mat4 zero(0.0);
+	static const Mat4 zero(0.0);
 	return zero;
 }
 

+ 1 - 1
include/anki/resource/MaterialShaderProgramCreator.h

@@ -47,6 +47,6 @@ private:
 	void parseOperationTag(const boost::property_tree::ptree& pt);
 };
 
-} // end namespace
+} // end namespace anki
 
 #endif

+ 3 - 3
include/anki/scene/Camera.h

@@ -90,7 +90,7 @@ public:
 	void lookAtPoint(const Vec3& point);
 
 protected:
-	Mat4 projectionMat;
+	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
@@ -98,7 +98,7 @@ protected:
 	/// projection params (fovX, fovY, zNear, zFar) change. Fortunately
 	/// the projection params change rarely. Note that the Camera as we all
 	/// know re-calculates the matrices only when the parameters change!!
-	Mat4 invProjectionMat;
+	Mat4 invProjectionMat = Mat4::getIdentity();
 
 	/// Calculate the @a viewMat. The view matrix is the inverse world 
 	/// transformation
@@ -108,7 +108,7 @@ protected:
 	}
 
 private:
-	Mat4 viewMat;
+	Mat4 viewMat = Mat4::getIdentity();
 
 	CameraType type;
 };

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

@@ -150,7 +150,7 @@ public:
 	}
 	/// @}
 
-private:
+//private:
 	ModelResourcePointer model; ///< The resource
 	ModelPatchNodes patches;
 	Obb obb;

+ 10 - 0
shaders/MaterialFragmentVariables.glsl

@@ -1,18 +1,25 @@
 /// @name Uniforms
 /// @{
 #if defined(PASS_COLOR)
+#	define blurring_DEFINED
 uniform float blurring;
 #endif
 /// @}
 
 /// @name Varyings
 /// @{
+#define vTexCoords_DEFINED
 in vec2 vTexCoords;
+
 #if defined(PASS_COLOR)
 in vec3 vNormal;
+#	define vNormal_DEFINED
 in vec3 vTangent;
+#	define vTangent_DEFINED
 in float vTangentW;
+#	define vTangentW_DEFINED
 in vec3 vVertPosViewSpace;
+#	define vVertPosViewSpace_DEFINED
 #endif
 /// @}
 
@@ -20,7 +27,10 @@ in vec3 vVertPosViewSpace;
 /// @{
 #if defined(PASS_COLOR)
 layout(location = 0) out vec3 fMsNormalFai;
+#	define fMsNormalFai_DEFINED
 layout(location = 1) out vec3 fMsDiffuseFai;
+#	define fMsDiffuseFai_DEFINED
 layout(location = 2) out vec4 fMsSpecularFai;
+#	define fMsSpecularFai_DEFINED
 #endif
 /// @}

+ 3 - 2
src/collision/Frustum.cpp

@@ -160,6 +160,7 @@ Mat4 PerspectiveFrustum::calculateProjectionMatrix() const
 	Mat4 projectionMat;
 
 	float f = 1.0 / tan(fovY * 0.5); // f = cot(fovY/2)
+	float g = near - far;
 
 	projectionMat(0, 0) = f * fovY / fovX; // = f/aspectRatio;
 	projectionMat(0, 1) = 0.0;
@@ -171,8 +172,8 @@ Mat4 PerspectiveFrustum::calculateProjectionMatrix() const
 	projectionMat(1, 3) = 0.0;
 	projectionMat(2, 0) = 0.0;
 	projectionMat(2, 1) = 0.0;
-	projectionMat(2, 2) = (far + near) / (near - far);
-	projectionMat(2, 3) = (2.0 * far * near) / (near - far);
+	projectionMat(2, 2) = (far + near) / g;
+	projectionMat(2, 3) = (2.0 * far * near) / g;
 	projectionMat(3, 0) = 0.0;
 	projectionMat(3, 1) = 0.0;
 	projectionMat(3, 2) = -1.0;

+ 2 - 2
src/core/App.cpp

@@ -152,11 +152,11 @@ void App::initDirs()
 	cachePath = settingsPath + "/cache";
 	if(directoryExists(cachePath.c_str()))
 	{
-		ANKI_LOGI("Deleting dir \"" << cachePath << "\"");
+		ANKI_LOGI("Deleting dir: " << cachePath);
 		removeDirectory(cachePath.c_str());
 	}
 
-	ANKI_LOGI("Creating cache dir \"" << cachePath << "\"");
+	ANKI_LOGI("Creating cache dir: " << cachePath);
 	createDirectory(cachePath.c_str());
 }
 

+ 5 - 4
src/gl/ShaderProgram.cpp

@@ -290,17 +290,18 @@ GLuint ShaderProgram::createAndCompileShader(const char* sourceCode,
 	const char* preproc, GLenum type)
 {
 	uint glId = 0;
-	const char* sourceStrs[2] = {NULL, NULL};
+	const char* sourceStrs[1] = {nullptr};
 
 	// create the shader
 	glId = glCreateShader(type);
 
 	// attach the source
-	sourceStrs[1] = sourceCode;
-	sourceStrs[0] = preproc;
+	std::string fullSrc = preproc;
+	fullSrc += sourceCode;
+	sourceStrs[0] = fullSrc.c_str();
 
 	// compile
-	glShaderSource(glId, 2, sourceStrs, NULL);
+	glShaderSource(glId, 1, sourceStrs, NULL);
 	glCompileShader(glId);
 
 	int success;

+ 11 - 1
src/gl/Vao.cpp

@@ -26,16 +26,21 @@ void Vao::attachArrayBufferVbo(const Vbo& vbo, GLuint attribVarLocation,
 	ANKI_ASSERT(isCreated());
 	ANKI_ASSERT(vbo.getBufferTarget() == GL_ARRAY_BUFFER
 		&& "Only GL_ARRAY_BUFFER is accepted");
+	ANKI_ASSERT(vbo.isCreated());
 
 	bind();
 	vbo.bind();
+	glEnableVertexAttribArray(attribVarLocation);
 	glVertexAttribPointer(attribVarLocation, size, type, normalized,
 		stride, pointer);
-	glEnableVertexAttribArray(attribVarLocation);
 	vbo.unbind();
 	unbind();
 
 	ANKI_CHECK_GL_ERROR();
+
+#if !defined(NDEBUG)
+	++attachments;
+#endif
 }
 
 //==============================================================================
@@ -54,11 +59,16 @@ void Vao::attachElementArrayBufferVbo(const Vbo& vbo)
 	ANKI_ASSERT(isCreated());
 	ANKI_ASSERT(vbo.getBufferTarget() == GL_ELEMENT_ARRAY_BUFFER
 		&& "Only GL_ELEMENT_ARRAY_BUFFER is accepted");
+	ANKI_ASSERT(vbo.isCreated());
 
 	bind();
 	vbo.bind();
 	unbind();
 	ANKI_CHECK_GL_ERROR();
+
+#if !defined(NDEBUG)
+	++attachments;
+#endif
 }
 
 } // end namespace

+ 7 - 6
src/renderer/Drawer.cpp

@@ -9,6 +9,7 @@
 #include "anki/scene/Camera.h"
 #include "anki/scene/ModelNode.h"
 #include "anki/resource/TextureResource.h"
+#include "anki/renderer/Renderer.h"
 
 namespace anki {
 
@@ -526,10 +527,8 @@ void RenderableDrawer::setupShaderProg(
 		if(name == "modelViewProjectionMat")
 		{
 			Mat4 mvp = 
-				Mat4(*renderable.getRenderableWorldTransform())
-				* cam.getViewMatrix() * cam.getProjectionMatrix();
-
-			std::cout << mvp << std::endl;
+				cam.getProjectionMatrix() * cam.getViewMatrix()
+				* Mat4(*renderable.getRenderableWorldTransform());
 
 			uni->set(mvp);
 		}
@@ -558,9 +557,11 @@ void RenderableDrawer::render(const Camera& cam, uint pass,
 	uint indecesNum = 
 		renderable.getModelPatchBase().getIndecesNumber(0);
 
-	renderable.getModelPatchBase().getVao(key).bind();
+	const Vao& vao = renderable.getModelPatchBase().getVao(key);
+	ANKI_ASSERT(vao.getAttachmentsCount() > 1);
+	vao.bind();
 	glDrawElements(GL_TRIANGLES, indecesNum, GL_UNSIGNED_SHORT, 0);
-	renderable.getModelPatchBase().getVao(key).unbind();
+	vao.unbind();
 }
 
 

+ 0 - 1
src/renderer/MainRenderer.cpp

@@ -59,7 +59,6 @@ void MainRenderer::initGl()
 		glGetString(GL_SHADING_LANGUAGE_VERSION)));
 
 	// get max texture units
-	//glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAtachments);
 	glClearColor(1.0, 0.0, 1.0, 1.0);
 	glClearDepth(1.0);
 	glClearStencil(0);

+ 1 - 1
src/renderer/Ms.cpp

@@ -69,7 +69,7 @@ void Ms::run()
 		glDepthFunc(GL_EQUAL);
 	}*/
 
-	glClear(GL_COLOR_BUFFER_BIT);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
 	// render all
 	for(Renderable* node :

+ 7 - 0
src/resource/MaterialShaderProgramCreator.cpp

@@ -115,6 +115,13 @@ void MaterialShaderProgramCreator::parseShaderTag(
 		parseOperationTag(opPt);
 	} // end for all operations
 
+	if(type == "fragment")
+	{
+		srcLines.push_back("#if defined(fMsDiffuseFai_DEFINED)");
+		srcLines.push_back("fMsDiffuseFai = vec3(1.0);\n");
+		srcLines.push_back("#endif");
+	}
+
 	srcLines.push_back("}\n");
 }
 

+ 14 - 18
src/resource/MeshLoader.cpp

@@ -1,10 +1,7 @@
-#include <fstream>
-#include <cstring>
-#include <boost/lexical_cast.hpp>
-#include <boost/foreach.hpp>
-
 #include "anki/resource/MeshLoader.h"
 #include "anki/util/BinaryStream.h"
+#include <fstream>
+#include <cstring>
 
 namespace anki {
 
@@ -19,7 +16,7 @@ void MeshLoader::load(const char* filename)
 
 		if(!file.is_open())
 		{
-			throw ANKI_EXCEPTION("Cannot open file \"" + filename + "\"");
+			throw ANKI_EXCEPTION("Cannot open file :" + filename);
 		}
 
 		BinaryStream bs(file.rdbuf());
@@ -40,7 +37,7 @@ void MeshLoader::load(const char* filename)
 		vertCoords.resize(vertsNum);
 
 		// Vert coords
-		BOOST_FOREACH(Vec3& vertCoord, vertCoords)
+		for(Vec3& vertCoord : vertCoords)
 		{
 			for(uint j = 0; j < 3; j++)
 			{
@@ -53,7 +50,7 @@ void MeshLoader::load(const char* filename)
 		tris.resize(facesNum);
 
 		// Faces IDs
-		BOOST_FOREACH(Triangle& tri, tris)
+		for(Triangle& tri : tris)
 		{
 			for(uint j = 0; j < 3; j++)
 			{
@@ -72,7 +69,7 @@ void MeshLoader::load(const char* filename)
 		texCoords.resize(texCoordsNum);
 
 		// Tex coords
-		BOOST_FOREACH(Vec2& texCoord, texCoords)
+		for(Vec2& texCoord : texCoords)
 		{
 			for(uint i = 0; i < 2; i++)
 			{
@@ -85,7 +82,7 @@ void MeshLoader::load(const char* filename)
 		vertWeights.resize(vertWeightsNum);
 
 		// Vert weights
-		BOOST_FOREACH(VertexWeight& vw, vertWeights)
+		for(VertexWeight& vw : vertWeights)
 		{
 			// get the bone connections num
 			uint boneConnections = bs.readUint();
@@ -100,9 +97,8 @@ void MeshLoader::load(const char* filename)
 			if(boneConnections > VertexWeight::MAX_BONES_PER_VERT)
 			{
 				uint tmp = VertexWeight::MAX_BONES_PER_VERT;
-				throw ANKI_EXCEPTION("Cannot have more than " +
-					boost::lexical_cast<std::string>(tmp) +
-					" bones per vertex");
+				throw ANKI_EXCEPTION("Cannot have more than "
+					+ std::to_string(tmp) + " bones per vertex");
 			}
 			vw.bonesNum = boneConnections;
 
@@ -123,7 +119,7 @@ void MeshLoader::load(const char* filename)
 	}
 	catch(Exception& e)
 	{
-		throw ANKI_EXCEPTION("File \"" + filename + "\"") << e;
+		throw ANKI_EXCEPTION("Loading of file failed: " + filename) << e;
 	}
 }
 
@@ -171,7 +167,7 @@ void MeshLoader::createVertIndeces()
 //==============================================================================
 void MeshLoader::createFaceNormals()
 {
-	BOOST_FOREACH(Triangle& tri, tris)
+	for(Triangle& tri : tris)
 	{
 		const Vec3& v0 = vertCoords[tri.vertIds[0]];
 		const Vec3& v1 = vertCoords[tri.vertIds[1]];
@@ -193,19 +189,19 @@ void MeshLoader::createVertNormals()
 {
 	vertNormals.resize(vertCoords.size());
 
-	BOOST_FOREACH(Vec3& vertNormal, vertNormals)
+	for(Vec3& vertNormal : vertNormals)
 	{
 		vertNormal = Vec3(0.0, 0.0, 0.0);
 	}
 
-	BOOST_FOREACH(Triangle& tri, tris)
+	for(Triangle& tri : tris)
 	{
 		vertNormals[tri.vertIds[0]] += tri.normal;
 		vertNormals[tri.vertIds[1]] += tri.normal;
 		vertNormals[tri.vertIds[2]] += tri.normal;
 	}
 
-	BOOST_FOREACH(Vec3& vertNormal, vertNormals)
+	for(Vec3& vertNormal : vertNormals)
 	{
 		vertNormal.normalize();
 	}

+ 22 - 12
src/resource/Model.cpp

@@ -22,37 +22,47 @@ Vao* ModelPatchBase::createNewVao(const Material& mtl,
 	Vao* vao = new Vao;
 	vao->create();
 
+	const ShaderProgramAttributeVariable* attrib;
 	const ShaderProgram& prog = mtl.findShaderProgram(key);
-	if(prog.findAttributeVariableByName("position"))
+
+	attrib = prog.findAttributeVariableByName("position");
+	if(attrib)
 	{
 		const Vbo* vbo = meshb.getVbo(Mesh::VBO_POSITIONS);
-		ANKI_ASSERT(vbo != NULL);
+		ANKI_ASSERT(vbo != nullptr);
 
-		vao->attachArrayBufferVbo(*vbo, 0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+		vao->attachArrayBufferVbo(*vbo, *attrib, 3, GL_FLOAT, GL_FALSE, 0,
+			nullptr);
 	}
 
-	if(prog.findAttributeVariableByName("normal"))
+	attrib = prog.findAttributeVariableByName("normal");
+	if(attrib)
 	{
 		const Vbo* vbo = meshb.getVbo(Mesh::VBO_NORMALS);
-		ANKI_ASSERT(vbo != NULL);
+		ANKI_ASSERT(vbo != nullptr);
 
-		vao->attachArrayBufferVbo(*vbo, 1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+		vao->attachArrayBufferVbo(*vbo, *attrib, 3, GL_FLOAT, GL_FALSE, 0,
+			nullptr);
 	}
 
-	if(prog.findAttributeVariableByName("tangent"))
+	attrib = prog.findAttributeVariableByName("tangent");
+	if(attrib)
 	{
 		const Vbo* vbo = meshb.getVbo(Mesh::VBO_TANGENTS);
-		ANKI_ASSERT(vbo != NULL);
+		ANKI_ASSERT(vbo != nullptr);
 
-		vao->attachArrayBufferVbo(*vbo, 2, 4, GL_FLOAT, GL_FALSE, 0, NULL);
+		vao->attachArrayBufferVbo(*vbo, *attrib, 4, GL_FLOAT, GL_FALSE, 0,
+			nullptr);
 	}
 
-	if(prog.findAttributeVariableByName("texCoords"))
+	attrib = prog.findAttributeVariableByName("texCoords");
+	if(attrib)
 	{
 		const Vbo* vbo = meshb.getVbo(Mesh::VBO_TEX_COORDS);
-		ANKI_ASSERT(vbo != NULL);
+		ANKI_ASSERT(vbo != nullptr);
 
-		vao->attachArrayBufferVbo(*vbo, 3, 2, GL_FLOAT, GL_FALSE, 0, NULL);
+		vao->attachArrayBufferVbo(*vbo, *attrib, 2, GL_FLOAT, GL_FALSE, 0,
+			nullptr);
 	}
 
 	vao->attachElementArrayBufferVbo(*meshb.getVbo(Mesh::VBO_INDICES));

+ 3 - 0
src/util/Exception.cpp

@@ -1,5 +1,6 @@
 #include "anki/util/Exception.h"
 #include <sstream>
+#include <iostream>
 
 // Instead of throwing abort. Its easier to debug
 #define ANKI_ABORT_ON_THROW 1
@@ -13,6 +14,7 @@ Exception::Exception(const char* error, const char* file,
 	err = synthErr(error, file, line, func);
 
 #if defined(ANKI_ABORT_ON_THROW)
+	std::cerr << err << std::endl;
 	abort();
 #endif
 }
@@ -22,6 +24,7 @@ Exception::Exception(const Exception& e)
 	: err(e.err)
 {
 #if defined(ANKI_ABORT_ON_THROW)
+	std::cerr << err << std::endl;
 	abort();
 #endif
 }

+ 4 - 1
src/util/Filesystem.cpp

@@ -89,7 +89,10 @@ static int rmDir(const char* fpath, const struct stat* sb, int typeflag,
 
 void removeDirectory(const char* dir)
 {
-	nftw(dir, rmDir, 64, FTW_DEPTH | FTW_PHYS);
+	if(nftw(dir, rmDir, 64, FTW_DEPTH | FTW_PHYS))
+	{
+		throw ANKI_EXCEPTION(strerror(errno) + ": " + dir);
+	}
 }
 
 //==============================================================================

+ 85 - 3
testapp/Main.cpp

@@ -42,6 +42,8 @@
 using namespace anki;
 
 UiPainter* painter;
+ModelNode* horse;
+PerspectiveCamera* cam;
 
 //==============================================================================
 void init()
@@ -55,15 +57,16 @@ void init()
 	painter->setFont("engine-rsrc/ModernAntiqua.ttf", 25, 25);
 
 	// camera
-	PerspectiveCamera* cam = new PerspectiveCamera("main-camera", &scene,
+	cam = new PerspectiveCamera("main-camera", &scene,
 		Movable::MF_NONE, nullptr);
 	const float ang = 70.0;
 	cam->setAll(
 		MainRendererSingleton::get().getAspectRatio() * Math::toRad(ang),
 		Math::toRad(ang), 0.5, 200.0);
-	cam->moveLocalY(3.0);
+	/*cam->moveLocalY(3.0);
 	cam->moveLocalZ(5.7);
 	cam->moveLocalX(-0.3);
+	cam->lookAtPoint(Vec3(0.0));*/
 	scene.setActiveCamera(cam);
 
 	// lights
@@ -79,7 +82,7 @@ void init()
 	spot->setShadowEnabled(true);
 
 	// horse
-	ModelNode* horse = new ModelNode("meshes/horse/horse.mdl", "horse", &scene,
+	horse = new ModelNode("meshes/horse/horse.mdl", "horse", &scene,
 		Movable::MF_NONE, nullptr);
 	horse->setLocalTransform(Transform(Vec3(-2, 0, 0), Mat3::getIdentity(),
 		1.0));
@@ -104,6 +107,11 @@ void mainLoopExtra()
 		mover = &SceneSingleton::get().getActiveCamera();
 	}
 
+	if(in.getKey(SDL_SCANCODE_UP)) mover->rotateLocalX(ang);
+	if(in.getKey(SDL_SCANCODE_DOWN)) mover->rotateLocalX(-ang);
+	if(in.getKey(SDL_SCANCODE_LEFT)) mover->rotateLocalY(ang);
+	if(in.getKey(SDL_SCANCODE_RIGHT)) mover->rotateLocalY(-ang);
+
 	if(in.getKey(SDL_SCANCODE_A)) mover->moveLocalX(-dist);
 	if(in.getKey(SDL_SCANCODE_D)) mover->moveLocalX(dist);
 	if(in.getKey(SDL_SCANCODE_LSHIFT)) mover->moveLocalY(dist);
@@ -126,6 +134,53 @@ void mainLoop()
 	HighRezTimer::Scalar prevUpdateTime = HighRezTimer::getCurrentTime();
 	HighRezTimer::Scalar crntTime = prevUpdateTime;
 
+	const char* vert = R"(
+in vec3 position;
+uniform mat4 mvp;
+
+void main() {
+	gl_Position = mvp * vec4(position, 1.0);
+})";
+
+	const char* frag = R"(
+out vec3 fColor;
+void main() 
+{
+	fColor = vec3(0.0, 1.0, 0.0);
+})";
+
+	const char* trf[] = {nullptr};
+
+	ShaderProgram sprog;
+	sprog.create(vert, nullptr, nullptr, nullptr, frag, trf);
+
+	Vec3 maxPos = Vec3(0.5 * 1.0);
+	Vec3 minPos = Vec3(-0.5 * 1.0);
+
+	std::array<Vec3, 8> points = {{
+		Vec3(maxPos.x(), maxPos.y(), maxPos.z()),  // right top front
+		Vec3(minPos.x(), maxPos.y(), maxPos.z()),  // left top front
+		Vec3(minPos.x(), minPos.y(), maxPos.z()),  // left bottom front
+		Vec3(maxPos.x(), minPos.y(), maxPos.z()),  // right bottom front
+		Vec3(maxPos.x(), maxPos.y(), minPos.z()),  // right top back
+		Vec3(minPos.x(), maxPos.y(), minPos.z()),  // left top back
+		Vec3(minPos.x(), minPos.y(), minPos.z()),  // left bottom back
+		Vec3(maxPos.x(), minPos.y(), minPos.z())   // right bottom back
+	}};
+
+	std::array<uint16_t, 24> indeces = {{0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6,
+		7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7}};
+
+	Vbo posvbo, idsvbo;
+
+	posvbo.create(GL_ARRAY_BUFFER, sizeof(points), &points[0], GL_STATIC_DRAW);
+	idsvbo.create(GL_ELEMENT_ARRAY_BUFFER, sizeof(indeces), &indeces[0], GL_STATIC_DRAW);
+
+	Vao vao;
+	vao.create();
+	vao.attachArrayBufferVbo(posvbo, 0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
+	vao.attachElementArrayBufferVbo(idsvbo);
+
 	while(1)
 	{
 		HighRezTimer timer;
@@ -142,12 +197,39 @@ void mainLoop()
 		EventManagerSingleton::get().updateAllEvents(prevUpdateTime, crntTime);
 		MainRendererSingleton::get().render(SceneSingleton::get());
 
+		Fbo::unbindAll();
+
+		/*glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+		glDisable(GL_DEPTH_TEST);*/
+
+		sprog.bind();
+		sprog.findUniformVariableByName("mvp")->set(
+			cam->getProjectionMatrix() * cam->getViewMatrix());
+		//horse->model->getModelPatches()[0].getVao(PassLevelKey(0, 0)).bind();
+		vao.bind();
+
+		//int indeces = horse->model->getModelPatches()[0].getIndecesNumber(0);
+		int indeces = 24;
+		glDrawElements(GL_LINES,
+			indeces,
+			GL_UNSIGNED_SHORT, 0);
+
 		if(InputSingleton::get().getKey(SDL_SCANCODE_ESCAPE))
 		{
 			break;
 		}
 
 		AppSingleton::get().swapBuffers();
+
+		// Sleep
+		//
+		timer.stop();
+		if(timer.getElapsedTime() < AppSingleton::get().getTimerTick())
+		{
+			SDL_Delay((AppSingleton::get().getTimerTick()
+				- timer.getElapsedTime()) * 1000.0);
+		}
+
 	}
 
 	ANKI_LOGI("Exiting main loop (" << mainLoopTimer.getElapsedTime() << " sec)");