diff --git a/Skeleton.sln b/Skeleton.sln
new file mode 100644
index 0000000000000000000000000000000000000000..c4d8f45f10bbd4f0e80bf0abc3401ea6df575d3c
--- /dev/null
+++ b/Skeleton.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31129.286
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Skeleton", "Skeleton\Skeleton.vcxproj", "{8B83E48F-726D-48E2-93F3-06B9AEE4BF72}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		grafika|Win32 = grafika|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{8B83E48F-726D-48E2-93F3-06B9AEE4BF72}.Debug|Win32.ActiveCfg = Debug|Win32
+		{8B83E48F-726D-48E2-93F3-06B9AEE4BF72}.Debug|Win32.Build.0 = Debug|Win32
+		{8B83E48F-726D-48E2-93F3-06B9AEE4BF72}.grafika|Win32.ActiveCfg = grafika|Win32
+		{8B83E48F-726D-48E2-93F3-06B9AEE4BF72}.grafika|Win32.Build.0 = grafika|Win32
+		{8B83E48F-726D-48E2-93F3-06B9AEE4BF72}.Release|Win32.ActiveCfg = Release|Win32
+		{8B83E48F-726D-48E2-93F3-06B9AEE4BF72}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {C711496F-4FD1-49DE-B948-AFE4B8B4F626}
+	EndGlobalSection
+EndGlobal
diff --git a/Skeleton/Skeleton.cpp b/Skeleton/Skeleton.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6b868aeaf7d183e43bfcfb3d2d4bfe8b54af4427
--- /dev/null
+++ b/Skeleton/Skeleton.cpp
@@ -0,0 +1,390 @@
+//=============================================================================================
+// Mintaprogram: Zold haromszog. Ervenyes 2019. osztol.
+//
+// A beadott program csak ebben a fajlban lehet, a fajl 1 byte-os ASCII karaktereket tartalmazhat, BOM kihuzando.
+// Tilos:
+// - mast "beincludolni", illetve mas konyvtarat hasznalni
+// - faljmuveleteket vegezni a printf-et kiveve
+// - Mashonnan atvett programresszleteket forrasmegjeloles nelkul felhasznalni es
+// - felesleges programsorokat a beadott programban hagyni!!!!!!! 
+// - felesleges kommenteket a beadott programba irni a forrasmegjelolest kommentjeit kiveve
+// ---------------------------------------------------------------------------------------------
+// A feladatot ANSI C++ nyelvu forditoprogrammal ellenorizzuk, a Visual Studio-hoz kepesti elteresekrol
+// es a leggyakoribb hibakrol (pl. ideiglenes objektumot nem lehet referencia tipusnak ertekul adni)
+// a hazibeado portal ad egy osszefoglalot.
+// ---------------------------------------------------------------------------------------------
+// A feladatmegoldasokban csak olyan OpenGL fuggvenyek hasznalhatok, amelyek az oran a feladatkiadasig elhangzottak 
+// A keretben nem szereplo GLUT fuggvenyek tiltottak.
+//
+// NYILATKOZAT
+// ---------------------------------------------------------------------------------------------
+// Nev    : Cseh Viktor
+// Neptun : GG2DP5
+// ---------------------------------------------------------------------------------------------
+// ezennel kijelentem, hogy a feladatot magam keszitettem, es ha barmilyen segitseget igenybe vettem vagy
+// mas szellemi termeket felhasznaltam, akkor a forrast es az atvett reszt kommentekben egyertelmuen jeloltem.
+// A forrasmegjeloles kotelme vonatkozik az eloadas foliakat es a targy oktatoi, illetve a
+// grafhazi doktor tanacsait kiveve barmilyen csatornan (szoban, irasban, Interneten, stb.) erkezo minden egyeb
+// informaciora (keplet, program, algoritmus, stb.). Kijelentem, hogy a forrasmegjelolessel atvett reszeket is ertem,
+// azok helyessegere matematikai bizonyitast tudok adni. Tisztaban vagyok azzal, hogy az atvett reszek nem szamitanak
+// a sajat kontribucioba, igy a feladat elfogadasarol a tobbi resz mennyisege es minosege alapjan szuletik dontes.
+// Tudomasul veszem, hogy a forrasmegjeloles kotelmenek megsertese eseten a hazifeladatra adhato pontokat
+// negativ elojellel szamoljak el es ezzel parhuzamosan eljaras is indul velem szemben.
+//=============================================================================================
+#include "framework.h"
+
+
+const char * vertexSource = R"(
+	#version 330				// Shader 3.3
+	precision highp float;		// normal floats, makes no difference on desktop computers
+
+	uniform vec3 wLookAt, wRight, wUp;
+	layout(location = 0) in vec2 cCamWindowVertex;	//Attrib Array 0
+
+	out vec3 p;
+
+	void main() {
+		gl_Position = vec4(cCamWindowVertex, 0, 1);
+		p = wLookAt + wRight * cCamWindowVertex.x + wUp * cCamWindowVertex.y;
+	}
+)";
+
+
+const char * fragmentSource = R"(
+	#version 330			// Shader 3.3
+	precision highp float;	// normal floats, makes no difference on desktop computers
+
+	const vec3 La = vec3(0.2f, 0.3f, 0.3f);
+	const vec3 Le = vec3(3.0f, 1.8f, 2.5f);
+	const vec3 lightPosition = vec3(0.5f, 0.5f, 0.25f);
+	const vec3 ka = vec3(0.5f, 0.5f, 0.5f);
+	const float shininess = 500.0f;
+	const int maxdepth = 5;
+	const float epsilon = 0.01f;
+
+	struct Hit {
+		float t;
+		vec3 position, normal;
+		int mat;
+	};
+
+	struct Ray {
+		vec3 start, dir, weight;
+	};
+
+	const int objFaces = 12;
+	
+	uniform vec3 wEye, v[20];
+	uniform int planes[objFaces * 3];
+	uniform int planesAllPoint[objFaces * 12];
+	uniform vec3 kd, ks, mirror,  gold;
+	
+
+	bool isCloseToSide(vec3 p){
+		for(int i = 0; i < objFaces; i++){
+			vec3 p1 = v[planesAllPoint[5 * i] - 1], p2 = v[planesAllPoint[5 * i + 1] - 1], p3 = v[planesAllPoint[5 * i + 2] - 1], p4 = v[planesAllPoint[5 * i + 3] - 1], p5 = v[planesAllPoint[5 * i + 4] - 1];
+			float d;
+
+			d = length(cross( p2 - p1 , p2 - p)) / length(p2 - p1);
+			if(d < 0.1f) return true;
+
+			d = length(cross( p3 - p2 , p3 - p)) / length(p3 - p2);
+
+			if(d < 0.1f)  return true;
+
+			d = length(cross( p4 - p3 , p4 - p)) / length(p4 - p3);
+			if(d < 0.1f)  return true;
+
+			d = length(cross( p5 - p4 , p5 - p)) / length(p5 - p4);
+			if(d < 0.1f)  return true;
+
+			d = length(cross( p1 - p5 , p5 - p)) / length(p1 - p5);
+			if(d < 0.1f)  return true;
+		}
+		
+		return false;
+	}
+
+	void getObjectPlane(int i, float scale, out vec3 p, out vec3 normal) {						// i-dik sikot allitja elo						
+		vec3 p1 = v[planes[3 * i] - 1], p2 = v[planes[3 * i + 1] - 1], p3 = v[planes[3 * i + 2] - 1];
+		normal = cross(p2 - p1, p3 - p1);
+		if(dot(p1, normal) < 0) normal = -normal;
+		p = p1 * scale + vec3(0.0f, 0.0f, 0.03f);
+		
+	}
+
+	Hit intersectConvexPolyhedron(Ray ray, Hit hit, float scale, int mat) {
+		for(int i=0; i < objFaces; i++) {
+			vec3 p1, normal;
+			getObjectPlane(i, scale, p1, normal);
+			float ti = abs(dot(normal, ray.dir)) > epsilon ? dot(p1 - ray.start, normal) / dot(normal, ray.dir) : -1;
+			if(ti <= epsilon || (ti > hit.t && hit.t > 0)) continue;
+			vec3 pintersect  = ray.start + ray.dir * ti;
+			bool outside  = false;
+			for(int j = 0; j < objFaces; j++){
+				if (i == j) continue;
+				vec3 p11, n;
+				getObjectPlane(j, scale, p11, n);
+				if(dot(n, pintersect - p11) > 0) {
+					outside = true;
+					break;
+				}
+			}
+			if (!outside){
+				hit.t = ti;			
+				hit.normal = normalize(normal);
+				if(isCloseToSide((pintersect - vec3(0.0f, 0.0f, 0.03f))/scale)){
+					hit.mat = 0;
+				}
+				else{
+					hit.mat = mat;
+				}
+
+				
+				hit.position = pintersect;
+			}
+		}
+		return hit;
+	}
+
+
+	vec3 gradf(vec3 r){
+		mat4 Q = mat4(1.2f, 0, 0, 0,
+					  0, 1.2f, 0, 0,
+					  0, 0, 0, -0.2f/2,
+					  0, 0, -0.2f/2, 0);
+		vec4 g = vec4(r.x, r.y, r.z, 1) * Q * 2;
+		return vec3(g.x, g.y, g.z);
+	}
+
+
+	Hit solveQuadratic(float a, float b, float c, Ray ray, Hit hit, float zmin, float zmax) {
+		float discr = b * b - 4.0f * a * c;
+		if(discr >= 0) {
+			float sqrt_discr = sqrt(discr);				
+			float t1 = (-b + sqrt_discr) / 2.0f / a;
+			vec3 p = ray.start + ray.dir * t1;
+			if(p.x * p.x + p.y * p.y + p.z * p.z > zmax || p.z < zmin) t1 = -1;
+			float t2 = (-b - sqrt_discr) /2.0f / a;
+			p = ray.start + ray.dir * t2;
+			if(p.x * p.x + p.y * p.y + p.z * p.z > zmax || p.z < zmin) t2 = -1;
+			if(t2 > 0 && (t2 < t1 || t1 < 0 )) t1 = t2;
+			if(t1 > 0 && (t1 < hit.t || hit.t < 0 )) {
+				hit.t = t1;
+				hit.position = ray.start + ray.dir * hit.t;
+				hit.normal = normalize(gradf(hit.position));
+				hit.mat = 2;
+			}
+		}
+		return hit;
+	}
+
+	Hit intersectMirascope(Ray ray, Hit hit) {
+		float a = 1.2f;
+		float b = 1.2f;
+		float c = 0.2f;
+
+		float a2 = a * ray.dir.x * ray.dir.x + b * ray.dir.y * ray.dir.y;
+		float b2 = a * 2 * ray.dir.x * ray.start.x +  b * 2 * ray.start.y * ray.dir.y - c *  ray.dir.z ;
+		float c2 = a * ray.start.x * ray.start.x +  b * ray.start.y * ray.start.y -  c * ray.start.z;
+		hit = solveQuadratic(a2, b2, c2, ray, hit, 0.0f, 0.3f * 0.3f );
+
+		return hit;
+	}
+
+	Hit firstIntersect(Ray ray) {
+		Hit bestHit;
+		bestHit.t = -1;
+		bestHit = intersectMirascope(ray, bestHit);	
+		bestHit = intersectConvexPolyhedron(ray, bestHit, 1.2f, 1);
+		if (dot(ray.dir, bestHit.normal) > 0) bestHit.normal = bestHit.normal * (-1);
+		return bestHit;
+	}
+
+	mat4 RotationMatrix(float angle, vec3 w) {
+		float c = cos(angle), s = sin(angle);
+		w = normalize(w);
+		return mat4(vec4(c * (1 - w.x*w.x) + w.x*w.x, w.x*w.y*(1 - c) + w.z*s, w.x*w.z*(1 - c) - w.y*s, 0),
+				    vec4(w.x*w.y*(1 - c) - w.z*s, c * (1 - w.y*w.y) + w.y*w.y, w.y*w.z*(1 - c) + w.x*s, 0),
+				    vec4(w.x*w.z*(1 - c) + w.y*s, w.y*w.z*(1 - c) - w.x*s, c * (1 - w.z*w.z) + w.z*w.z, 0),
+					vec4(0, 0, 0, 1));
+	}
+
+	vec3 trace(Ray ray) {
+		vec3 outRadiance = vec3(0,0,0);	
+		for(int d = 0; d < maxdepth; d++){
+			Hit hit = firstIntersect(ray);
+			if (hit.t < 0) break;
+			if (hit.mat == 0) {	
+				vec3 lightdir = normalize(lightPosition - hit.position);
+				float cosTheta = dot(hit.normal, lightdir);						
+				if (cosTheta > 0) {	
+					vec3 LeIn = Le / dot(lightPosition - hit.position, lightPosition - hit.position);
+					outRadiance += ray.weight * LeIn * kd * cosTheta;
+					vec3 halfway = normalize(-ray.dir + lightdir);
+					float cosDelta = dot(hit.normal, halfway);											
+					if(cosDelta > 0) outRadiance += ray.weight * LeIn * ks * pow(cosDelta, shininess);
+				}
+				ray.weight *= ka;
+				break;
+			}
+
+			// tukros felulet
+			if(hit.mat == 2){
+				ray.weight *= gold + (vec3(1, 1, 1) - gold) * pow(dot(-ray.dir, hit.normal), 5);
+				ray.start = hit.position + hit.normal * epsilon;
+				ray.dir = reflect(ray.dir, hit.normal);
+			}
+			else if(hit.mat == 1){
+				ray.weight *= mirror + (vec3(1, 1, 1) - mirror) * pow(dot(-ray.dir, hit.normal), 5);
+				ray.start = hit.position + hit.normal * epsilon;
+				ray.dir = reflect(ray.dir, hit.normal);
+
+				mat4 temp = RotationMatrix(72.0f * 3.14159f / 180 , hit.normal);
+				//vec3 start = vec3(hit.position + hit.normal * epsilon);
+				vec4 kimeneti = vec4(ray.start.x, ray.start.y, ray.start.z, 1) * temp;
+				ray.start = vec3(kimeneti.x, kimeneti.y, kimeneti.z);
+
+
+				
+				mat4 temp2 = RotationMatrix(72.0f * 3.14159f / 180, hit.normal);
+				vec3 start2 = vec3(reflect(ray.dir, hit.normal));
+				vec4 kimeneti2 = vec4(ray.dir.x, ray.dir.y, ray.dir.z, 1) * temp;
+				ray.dir = vec3(kimeneti2.x, kimeneti2.y, kimeneti2.z);
+				
+			}
+		}
+		outRadiance += ray.weight * La;	
+		return outRadiance;
+	}
+
+	in vec3 p;
+	out vec4 fragmentColor;
+
+	void main() {
+		Ray ray;
+		ray.start = wEye;
+		ray.dir = normalize(p - wEye);
+		ray.weight = vec3(1,1,1);
+		fragmentColor = vec4(trace(ray), 1);
+	}
+)";
+
+struct Camera2D {
+	vec3 eye, pvup, lookat, right, rvup;
+	float fov = 45 * (float)M_PI / 180;
+	
+public:
+	Camera2D() : eye(0, 1.1, 0.4), pvup(0,0,1), lookat(0,0,0) { set(); }
+
+	void set() {
+		vec3 w = eye - lookat;
+		float f = length(w);
+		right = normalize(cross(pvup, w)) * f * tanf(fov / 2);
+		rvup = normalize(cross(w, right)) * f * tanf(fov / 2);
+	}
+
+	void Animate(float t) {
+		float r = sqrtf(eye.x * eye.x + eye.y * eye.y);
+		eye = vec3(r * cosf(t) + lookat.x, r * sinf(t) + lookat.y, eye.z);
+		set();
+	}
+
+	void Step(float step) {
+		eye = normalize(eye + pvup * step) * length(eye);
+		set();
+	}
+};
+
+Camera2D camera;
+
+GPUProgram shader;
+bool move = true;
+float F(float n, float k) { return ((n - 1) * (n - 1) + k * k) / ((n + 1) * (n + 1) + k * k); }
+
+
+void onInitialization() {
+	glViewport(0, 0, windowWidth, windowHeight);
+	unsigned int vao, vbo;	
+	glGenVertexArrays(1, &vao);
+	glBindVertexArray(vao);
+
+	
+	glGenBuffers(1, &vbo);	
+	glBindBuffer(GL_ARRAY_BUFFER, vbo);
+	
+	float vertexCoords[] = { -1,-1,   1,-1,   1,1 ,   -1,1 };
+	glBufferData(GL_ARRAY_BUFFER, 
+		sizeof(vertexCoords), 
+		vertexCoords,	
+		GL_STATIC_DRAW);
+
+	glEnableVertexAttribArray(0);  
+	glVertexAttribPointer(0, 
+		2, GL_FLOAT, GL_FALSE,
+		0, NULL); 
+
+	shader.create(vertexSource, fragmentSource, "fragmentColor");
+	
+
+	const float g = 0.618f, G = 1.618f;
+	std::vector<vec3> v = {	
+		vec3(0.0f, g, G), vec3(0.0f, -g, G), vec3(0.0f,-g, -G), vec3(0.0f, g, -G), vec3(G, 0.0f, g), vec3(-G, 0.0f, g), vec3(-G, 0.0f, -g), vec3(G, 0.0f, -g), vec3(g, G, 0.0f), vec3(-g, G, 0.0f), vec3(-g, -G, 0.0f), vec3(g, -G, 0.0f),
+		vec3(1.0f,1.0f,1.0f), vec3(-1.0f,1.0f,1.0f), vec3(-1.0f,-1.0f,1.0f), vec3(1.0f, -1.0f, 1.0f), vec3(1.0f, -1.0f, -1.0f),  vec3(1.0f, 1.0f, -1.0f), vec3(-1.0f, 1.0f, -1.0f), vec3(-1.0f, -1.0f, -1.0f)
+	};
+	for (int i = 0; i < v.size(); i++) { shader.setUniform(v[i], "v[" + std::to_string(i) + "]"); }
+
+	std::vector<int> planes = {
+		1, 2, 16,   1, 13, 9,   1, 14, 6,    2, 15, 11,     3, 4, 18,    3, 17, 12,    3, 20, 7,     19, 10, 9,     16, 12, 17,     5, 8, 18,    14, 10, 19,     6, 7, 20
+	};
+	for (int j = 0; j < planes.size(); j++) { shader.setUniform(planes[j], "planes[" + std::to_string(j) + "]"); }
+
+	std::vector<int> planesAllPoint = {	
+		1, 2, 16, 5, 13,     1, 13, 9, 10, 14,     1, 14, 6, 15, 2,    2, 15, 11, 12, 16,    3, 4, 18, 8, 17,    3, 17, 12, 11, 20,    3, 20, 7, 19, 4,      19, 10, 9, 18, 4,      16, 12, 17, 8, 5,     5, 8, 18, 9, 13,     14, 10, 19,7, 6,     6, 7, 20, 11, 15
+	};
+	for (int j = 0; j < planes.size(); j++) { shader.setUniform(planesAllPoint[j], "planesAllPoint[" + std::to_string(j) + "]"); }
+
+	shader.setUniform(vec3(0.1f, 0.2f, 0.3f), "kd");
+	shader.setUniform(vec3(5, 5, 5), "ks");
+	shader.setUniform(vec3(1,1,1), "mirror");
+	shader.setUniform(vec3(F(0.17f, 3.1f), F(0.35f, 2.7f), F(1.5f, 1.9f)), "gold");	
+	
+}
+
+
+void onDisplay() {
+	glClearColor(0, 0, 0, 0); 
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	shader.setUniform(camera.eye, "wEye");
+	shader.setUniform(camera.lookat, "wLookAt");
+	shader.setUniform(camera.right, "wRight");
+	shader.setUniform(camera.rvup, "wUp");
+	glDrawArrays(GL_TRIANGLE_FAN, 0 , 4 );
+	glutSwapBuffers();
+}
+
+// Key of ASCII code pressed
+void onKeyboard(unsigned char key, int pX, int pY) {
+	if (key == 'd') glutPostRedisplay();
+	if (key == ' ') move = !move;
+}
+
+
+void onKeyboardUp(unsigned char key, int pX, int pY) {
+}
+
+
+void onMouseMotion(int pX, int pY) {
+	
+}
+
+
+void onMouse(int button, int state, int pX, int pY) {
+	
+}
+
+
+void onIdle() {
+	if(move) camera.Animate(glutGet(GLUT_ELAPSED_TIME)/1000.0f); // elapsed time since the start of the program
+	glutPostRedisplay();
+}
diff --git a/Skeleton/Skeleton.vcxproj b/Skeleton/Skeleton.vcxproj
new file mode 100644
index 0000000000000000000000000000000000000000..72b646f19328762eef35606c7fa0f352b3a20358
--- /dev/null
+++ b/Skeleton/Skeleton.vcxproj
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="grafika|Win32">
+      <Configuration>grafika</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{8B83E48F-726D-48E2-93F3-06B9AEE4BF72}</ProjectGuid>
+    <RootNamespace>Skeleton</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v142</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v142</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='grafika|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>ClangCL</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='grafika|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>$(SolutionDir)bin\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <OutDir>$(SolutionDir)bin\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='grafika|Win32'">
+    <OutDir>$(SolutionDir)bin\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\..\Libs\glew-1.13.0\include\;$(SolutionDir)..\..\Libs\freeglut\include\;$(SolutionDir)..\..\Libs\devil-1.7.8\include\;$(SolutionDir)..\..\Libs\glm\;$(SolutionDir)..\..\Common\src\</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(SolutionDir)..\..\Libs\freeglut\lib;$(SolutionDir)..\..\Libs\glew-1.13.0\lib\Release\Win32\;$(SolutionDir)..\..\Libs\devil-1.7.8\lib\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>glew32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\..\Libs\glew-1.13.0\include\;$(SolutionDir)..\..\Libs\freeglut\include\;$(SolutionDir)..\..\Libs\devil-1.7.8\include\</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>glew32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(SolutionDir)..\..\Libs\freeglut\lib;$(SolutionDir)..\..\Libs\glew-1.13.0\lib\Release\Win32\;$(SolutionDir)..\..\Libs\devil-1.7.8\lib\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='grafika|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\..\Libs\glew-1.13.0\include\;$(SolutionDir)..\..\Libs\freeglut\include\;$(SolutionDir)..\..\Libs\devil-1.7.8\include\</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>glew32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(SolutionDir)..\..\Libs\freeglut\lib;$(SolutionDir)..\..\Libs\glew-1.13.0\lib\Release\Win32\;$(SolutionDir)..\..\Libs\devil-1.7.8\lib\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="framework.cpp" />
+    <ClCompile Include="Skeleton.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="framework.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/Skeleton/Skeleton.vcxproj.filters b/Skeleton/Skeleton.vcxproj.filters
new file mode 100644
index 0000000000000000000000000000000000000000..3a610edb9e5710346cdb7aec11c5a70808b265cb
--- /dev/null
+++ b/Skeleton/Skeleton.vcxproj.filters
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="Skeleton.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="framework.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="framework.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/Skeleton/framework.cpp b/Skeleton/framework.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..425b6d553d61923baafbc8d85bf6e8b29e9b6c29
--- /dev/null
+++ b/Skeleton/framework.cpp
@@ -0,0 +1,72 @@
+//=============================================================================================
+// Collection of classes from lecture slides.
+// Framework for assignments. Valid from 2019.
+// Do not change it if you want to submit a homework.
+//=============================================================================================
+#include "framework.h"
+
+// Initialization
+void onInitialization();
+
+// Window has become invalid: Redraw
+void onDisplay();
+
+// Key of ASCII code pressed
+void onKeyboard(unsigned char key, int pX, int pY);
+
+// Key of ASCII code released
+void onKeyboardUp(unsigned char key, int pX, int pY);
+
+// Move mouse with key pressed
+void onMouseMotion(int pX, int pY);
+
+// Mouse click event
+void onMouse(int button, int state, int pX, int pY);
+
+// Idle event indicating that some time elapsed: do animation here
+void onIdle();
+
+// Entry point of the application
+int main(int argc, char * argv[]) {
+	// Initialize GLUT, Glew and OpenGL 
+	glutInit(&argc, argv);
+
+	// OpenGL major and minor versions
+	int majorVersion = 3, minorVersion = 3;
+#if !defined(__APPLE__)
+	glutInitContextVersion(majorVersion, minorVersion);
+#endif
+	glutInitWindowSize(windowWidth, windowHeight);				// Application window is initially of resolution 600x600
+	glutInitWindowPosition(100, 100);							// Relative location of the application window
+#if defined(__APPLE__)
+	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_3_2_CORE_PROFILE);  // 8 bit R,G,B,A + double buffer + depth buffer
+#else
+	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
+#endif
+	glutCreateWindow(argv[0]);
+
+#if !defined(__APPLE__)
+	glewExperimental = true;	// magic
+	glewInit();
+#endif
+	printf("GL Vendor    : %s\n", glGetString(GL_VENDOR));
+	printf("GL Renderer  : %s\n", glGetString(GL_RENDERER));
+	printf("GL Version (string)  : %s\n", glGetString(GL_VERSION));
+	glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
+	glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
+	printf("GL Version (integer) : %d.%d\n", majorVersion, minorVersion);
+	printf("GLSL Version : %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
+
+	// Initialize this program and create shaders
+	onInitialization();
+
+	glutDisplayFunc(onDisplay);                // Register event handlers
+	glutMouseFunc(onMouse);
+	glutIdleFunc(onIdle);
+	glutKeyboardFunc(onKeyboard);
+	glutKeyboardUpFunc(onKeyboardUp);
+	glutMotionFunc(onMouseMotion);
+
+	glutMainLoop();
+	return 1;
+}
diff --git a/Skeleton/framework.h b/Skeleton/framework.h
new file mode 100644
index 0000000000000000000000000000000000000000..ae35c1a35d0fe54d09f4e01f6e7dc65b5fbe243e
--- /dev/null
+++ b/Skeleton/framework.h
@@ -0,0 +1,397 @@
+//=============================================================================================
+// Collection of programs from lecture slides.
+// Framework for assignments. Valid from 2020.
+//
+// Do not change it if you want to submit a homework.
+// In the homework, file operations other than printf are prohibited.
+//=============================================================================================
+#define _USE_MATH_DEFINES		// M_PI
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <vector>
+#include <string>
+
+#if defined(__APPLE__)
+#include <GLUT/GLUT.h>
+#include <OpenGL/gl3.h>
+#else
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
+#include <windows.h>
+#endif
+#include <GL/glew.h>		// must be downloaded
+#include <GL/freeglut.h>	// must be downloaded unless you have an Apple
+#endif
+
+// Resolution of screen
+const unsigned int windowWidth = 600, windowHeight = 600;
+
+//--------------------------
+struct vec2 {
+//--------------------------
+	float x, y;
+
+	vec2(float x0 = 0, float y0 = 0) { x = x0; y = y0; }
+	vec2 operator*(float a) const { return vec2(x * a, y * a); }
+	vec2 operator/(float a) const { return vec2(x / a, y / a); }
+	vec2 operator+(const vec2& v) const { return vec2(x + v.x, y + v.y); }
+	vec2 operator-(const vec2& v) const { return vec2(x - v.x, y - v.y); }
+	vec2 operator*(const vec2& v) const { return vec2(x * v.x, y * v.y); }
+	vec2 operator-() const { return vec2(-x, -y); }
+};
+
+inline float dot(const vec2& v1, const vec2& v2) {
+	return (v1.x * v2.x + v1.y * v2.y);
+}
+
+inline float length(const vec2& v) { return sqrtf(dot(v, v)); }
+
+inline vec2 normalize(const vec2& v) { return v * (1 / length(v)); }
+
+inline vec2 operator*(float a, const vec2& v) { return vec2(v.x * a, v.y * a); }
+
+//--------------------------
+struct vec3 {
+//--------------------------
+	float x, y, z;
+
+	vec3(float x0 = 0, float y0 = 0, float z0 = 0) { x = x0; y = y0; z = z0; }
+	vec3(vec2 v) { x = v.x; y = v.y; z = 0; }
+
+	vec3 operator*(float a) const { return vec3(x * a, y * a, z * a); }
+	vec3 operator/(float a) const { return vec3(x / a, y / a, z / a); }
+	vec3 operator+(const vec3& v) const { return vec3(x + v.x, y + v.y, z + v.z); }
+	vec3 operator-(const vec3& v) const { return vec3(x - v.x, y - v.y, z - v.z); }
+	vec3 operator*(const vec3& v) const { return vec3(x * v.x, y * v.y, z * v.z); }
+	vec3 operator-()  const { return vec3(-x, -y, -z); }
+};
+
+inline float dot(const vec3& v1, const vec3& v2) { return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z); }
+
+inline float length(const vec3& v) { return sqrtf(dot(v, v)); }
+
+inline vec3 normalize(const vec3& v) { return v * (1 / length(v)); }
+
+inline vec3 cross(const vec3& v1, const vec3& v2) {
+	return vec3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x);
+}
+
+inline vec3 operator*(float a, const vec3& v) { return vec3(v.x * a, v.y * a, v.z * a); }
+
+//--------------------------
+struct vec4 {
+//--------------------------
+	float x, y, z, w;
+
+	vec4(float x0 = 0, float y0 = 0, float z0 = 0, float w0 = 0) { x = x0; y = y0; z = z0; w = w0; }
+	float& operator[](int j) { return *(&x + j); }
+	float operator[](int j) const { return *(&x + j); }
+
+	vec4 operator*(float a) const { return vec4(x * a, y * a, z * a, w * a); }
+	vec4 operator/(float d) const { return vec4(x / d, y / d, z / d, w / d); }
+	vec4 operator+(const vec4& v) const { return vec4(x + v.x, y + v.y, z + v.z, w + v.w); }
+	vec4 operator-(const vec4& v)  const { return vec4(x - v.x, y - v.y, z - v.z, w - v.w); }
+	vec4 operator*(const vec4& v) const { return vec4(x * v.x, y * v.y, z * v.z, w * v.w); }
+	void operator+=(const vec4 right) { x += right.x; y += right.y; z += right.z; w += right.w; }
+};
+
+inline float dot(const vec4& v1, const vec4& v2) {
+	return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w);
+}
+
+inline vec4 operator*(float a, const vec4& v) {
+	return vec4(v.x * a, v.y * a, v.z * a, v.w * a);
+}
+
+//---------------------------
+struct mat4 { // row-major matrix 4x4
+//---------------------------
+	vec4 rows[4];
+public:
+	mat4() {}
+	mat4(float m00, float m01, float m02, float m03,
+		float m10, float m11, float m12, float m13,
+		float m20, float m21, float m22, float m23,
+		float m30, float m31, float m32, float m33) {
+		rows[0][0] = m00; rows[0][1] = m01; rows[0][2] = m02; rows[0][3] = m03;
+		rows[1][0] = m10; rows[1][1] = m11; rows[1][2] = m12; rows[1][3] = m13;
+		rows[2][0] = m20; rows[2][1] = m21; rows[2][2] = m22; rows[2][3] = m23;
+		rows[3][0] = m30; rows[3][1] = m31; rows[3][2] = m32; rows[3][3] = m33;
+	}
+	mat4(vec4 it, vec4 jt, vec4 kt, vec4 ot) {
+		rows[0] = it; rows[1] = jt; rows[2] = kt; rows[3] = ot;
+	}
+
+	vec4& operator[](int i) { return rows[i]; }
+	vec4 operator[](int i) const { return rows[i]; }
+	operator float*() const { return (float*)this; }
+};
+
+inline vec4 operator*(const vec4& v, const mat4& mat) {
+	return v[0] * mat[0] + v[1] * mat[1] + v[2] * mat[2] + v[3] * mat[3];
+}
+
+inline mat4 operator*(const mat4& left, const mat4& right) {
+	mat4 result;
+	for (int i = 0; i < 4; i++) result.rows[i] = left.rows[i] * right;
+	return result;
+}
+
+inline mat4 TranslateMatrix(vec3 t) {
+	return mat4(vec4(1,   0,   0,   0),
+			    vec4(0,   1,   0,   0),
+				vec4(0,   0,   1,   0),
+				vec4(t.x, t.y, t.z, 1));
+}
+
+inline mat4 ScaleMatrix(vec3 s) {
+	return mat4(vec4(s.x, 0,   0,   0),
+			    vec4(0,   s.y, 0,   0),
+				vec4(0,   0,   s.z, 0),
+				vec4(0,   0,   0,   1));
+}
+
+inline mat4 RotationMatrix(float angle, vec3 w) {
+	float c = cosf(angle), s = sinf(angle);
+	w = normalize(w);
+	return mat4(vec4(c * (1 - w.x*w.x) + w.x*w.x, w.x*w.y*(1 - c) + w.z*s, w.x*w.z*(1 - c) - w.y*s, 0),
+			    vec4(w.x*w.y*(1 - c) - w.z*s, c * (1 - w.y*w.y) + w.y*w.y, w.y*w.z*(1 - c) + w.x*s, 0),
+			    vec4(w.x*w.z*(1 - c) + w.y*s, w.y*w.z*(1 - c) - w.x*s, c * (1 - w.z*w.z) + w.z*w.z, 0),
+			    vec4(0, 0, 0, 1));
+}
+
+//---------------------------
+class Texture {
+//---------------------------
+	std::vector<vec4> load(std::string pathname, bool transparent, int& width, int& height) {
+		FILE * file = fopen(pathname.c_str(), "r");
+		if (!file) {
+			printf("%s does not exist\n", pathname.c_str());
+			width = height = 0;
+			return std::vector<vec4>();
+		}
+		unsigned short bitmapFileHeader[27];					// bitmap header
+		fread(&bitmapFileHeader, 27, 2, file);
+		if (bitmapFileHeader[0] != 0x4D42) printf("Not bmp file\n");
+		if (bitmapFileHeader[14] != 24) printf("Only true color bmp files are supported\n");
+		width = bitmapFileHeader[9];
+		height = bitmapFileHeader[11];
+		unsigned int size = (unsigned long)bitmapFileHeader[17] + (unsigned long)bitmapFileHeader[18] * 65536;
+		fseek(file, 54, SEEK_SET);
+		std::vector<unsigned char> bImage(size);
+		fread(&bImage[0], 1, size, file); 	// read the pixels
+		fclose(file);
+		std::vector<vec4> image(width * height);
+		int i = 0;
+		for (unsigned int idx = 0; idx < size; idx += 3) { // Swap R and B since in BMP, the order is BGR
+			float alpha = (transparent) ? (bImage[idx] + bImage[idx + 1] + bImage[idx + 2]) / 3.0f / 256.0f : 1.0f;
+			image[i++] = vec4(bImage[idx + 2] / 256.0f, bImage[idx + 1] / 256.0f, bImage[idx] / 256.0f, alpha);
+		}
+		return image;
+	}
+
+public:
+	unsigned int textureId = 0;
+
+	Texture() { textureId = 0; }
+
+	Texture(std::string pathname, bool transparent = false) {
+		textureId = 0;
+		create(pathname, transparent);
+	}
+
+	Texture(int width, int height, const std::vector<vec4>& image, int sampling = GL_LINEAR) {
+		textureId = 0;
+		create(width, height, image, sampling);
+	}
+
+	Texture(const Texture& texture) {
+		printf("\nError: Texture resource is not copied on GPU!!!\n");
+	}
+
+	void operator=(const Texture& texture) {
+		printf("\nError: Texture resource is not copied on GPU!!!\n");
+	}
+
+	void create(std::string pathname, bool transparent = false) {
+		int width, height;
+		std::vector<vec4> image = load(pathname, transparent, width, height);
+		if (image.size() > 0) create(width, height, image);
+	}
+
+	void create(int width, int height, const std::vector<vec4>& image, int sampling = GL_LINEAR) {
+		if (textureId == 0) glGenTextures(1, &textureId);  				// id generation
+		glBindTexture(GL_TEXTURE_2D, textureId);    // binding
+
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, &image[0]); // To GPU
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, sampling); // sampling
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, sampling);
+	}
+
+	~Texture() {
+		if (textureId > 0) glDeleteTextures(1, &textureId);
+	}
+};
+
+//---------------------------
+class GPUProgram {
+//--------------------------
+	unsigned int shaderProgramId = 0;
+	unsigned int vertexShader = 0, geometryShader = 0, fragmentShader = 0;
+	bool waitError = true;
+
+	void getErrorInfo(unsigned int handle) { // shader error report
+		int logLen, written;
+		glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &logLen);
+		if (logLen > 0) {
+			std::string log(logLen, '\0');
+			glGetShaderInfoLog(handle, logLen, &written, &log[0]);
+			printf("Shader log:\n%s", log.c_str());
+			if (waitError) getchar();
+		}
+	}
+
+	bool checkShader(unsigned int shader, std::string message) { // check if shader could be compiled
+		int OK;
+		glGetShaderiv(shader, GL_COMPILE_STATUS, &OK);
+		if (!OK) {
+			printf("%s!\n", message.c_str());
+			getErrorInfo(shader);
+			return false;
+		}
+		return true;
+	}
+
+	bool checkLinking(unsigned int program) { 	// check if shader could be linked
+		int OK;
+		glGetProgramiv(program, GL_LINK_STATUS, &OK);
+		if (!OK) {
+			printf("Failed to link shader program!\n");
+			getErrorInfo(program);
+			return false;
+		}
+		return true;
+	}
+
+	int getLocation(const std::string& name) {	// get the address of a GPU uniform variable
+		int location = glGetUniformLocation(shaderProgramId, name.c_str());
+		if (location < 0) printf("uniform %s cannot be set\n", name.c_str());
+		return location;
+	}
+
+public:
+	GPUProgram(bool _waitError = true) { shaderProgramId = 0; waitError = _waitError; }
+
+	GPUProgram(const GPUProgram& program) {
+		if (program.shaderProgramId > 0) printf("\nError: GPU program is not copied on GPU!!!\n");
+	}
+
+	void operator=(const GPUProgram& program) {
+		if (program.shaderProgramId > 0) printf("\nError: GPU program is not copied on GPU!!!\n");
+	}
+
+	unsigned int getId() { return shaderProgramId; }
+
+	bool create(const char * const vertexShaderSource,
+		        const char * const fragmentShaderSource, const char * const fragmentShaderOutputName,
+		        const char * const geometryShaderSource = nullptr)
+	{
+		// Create vertex shader from string
+		if (vertexShader == 0) vertexShader = glCreateShader(GL_VERTEX_SHADER);
+		if (!vertexShader) {
+			printf("Error in vertex shader creation\n");
+			exit(1);
+		}
+		glShaderSource(vertexShader, 1, (const GLchar**)&vertexShaderSource, NULL);
+		glCompileShader(vertexShader);
+		if (!checkShader(vertexShader, "Vertex shader error")) return false;
+
+		// Create geometry shader from string if given
+		if (geometryShaderSource != nullptr) {
+			if (geometryShader == 0) geometryShader = glCreateShader(GL_GEOMETRY_SHADER);
+			if (!geometryShader) {
+				printf("Error in geometry shader creation\n");
+				exit(1);
+			}
+			glShaderSource(geometryShader, 1, (const GLchar**)&geometryShaderSource, NULL);
+			glCompileShader(geometryShader);
+			if (!checkShader(geometryShader, "Geometry shader error")) return false;
+		}
+
+		// Create fragment shader from string
+		if (fragmentShader == 0) fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+		if (!fragmentShader) {
+			printf("Error in fragment shader creation\n");
+			exit(1);
+		}
+
+		glShaderSource(fragmentShader, 1, (const GLchar**)&fragmentShaderSource, NULL);
+		glCompileShader(fragmentShader);
+		if (!checkShader(fragmentShader, "Fragment shader error")) return false;
+
+		shaderProgramId = glCreateProgram();
+		if (!shaderProgramId) {
+			printf("Error in shader program creation\n");
+			exit(1);
+		}
+		glAttachShader(shaderProgramId, vertexShader);
+		glAttachShader(shaderProgramId, fragmentShader);
+		if (geometryShader > 0) glAttachShader(shaderProgramId, geometryShader);
+
+		// Connect the fragmentColor to the frame buffer memory
+		glBindFragDataLocation(shaderProgramId, 0, fragmentShaderOutputName);	// this output goes to the frame buffer memory
+
+		// program packaging
+		glLinkProgram(shaderProgramId);
+		if (!checkLinking(shaderProgramId)) return false;
+
+		// make this program run
+		glUseProgram(shaderProgramId);
+		return true;
+	}
+
+	void Use() { 		// make this program run
+		glUseProgram(shaderProgramId);
+	}
+
+	void setUniform(int i, const std::string& name) {
+		int location = getLocation(name);
+		if (location >= 0) glUniform1i(location, i);
+	}
+
+	void setUniform(float f, const std::string& name) {
+		int location = getLocation(name);
+		if (location >= 0) glUniform1f(location, f);
+	}
+
+	void setUniform(const vec2& v, const std::string& name) {
+		int location = getLocation(name);
+		if (location >= 0) glUniform2fv(location, 1, &v.x);
+	}
+
+	void setUniform(const vec3& v, const std::string& name) {
+		int location = getLocation(name);
+		if (location >= 0) glUniform3fv(location, 1, &v.x);
+	}
+
+	void setUniform(const vec4& v, const std::string& name) {
+		int location = getLocation(name);
+		if (location >= 0) glUniform4fv(location, 1, &v.x);
+	}
+
+	void setUniform(const mat4& mat, const std::string& name) {
+		int location = getLocation(name);
+		if (location >= 0) glUniformMatrix4fv(location, 1, GL_TRUE, mat);
+	}
+
+	void setUniform(const Texture& texture, const std::string& samplerName, unsigned int textureUnit = 0) {
+		int location = getLocation(samplerName);
+		if (location >= 0) {
+			glUniform1i(location, textureUnit);
+			glActiveTexture(GL_TEXTURE0 + textureUnit);
+			glBindTexture(GL_TEXTURE_2D, texture.textureId);
+		}
+	}
+
+	~GPUProgram() { if (shaderProgramId > 0) glDeleteProgram(shaderProgramId); }
+};
diff --git a/Skeleton/grafika/Skeleton.exe.recipe b/Skeleton/grafika/Skeleton.exe.recipe
new file mode 100644
index 0000000000000000000000000000000000000000..876f6ca5ba71c84a835c7b11619d067f2714c262
--- /dev/null
+++ b/Skeleton/grafika/Skeleton.exe.recipe
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project>
+  <ProjectOutputs>
+    <ProjectOutput>
+      <FullPath>D:\Grafika\HF2\Programs\Skeleton\bin\Skeleton.exe</FullPath>
+    </ProjectOutput>
+  </ProjectOutputs>
+  <ContentFiles />
+  <SatelliteDlls />
+  <NonRecipeFileRefs />
+</Project>
\ No newline at end of file
diff --git a/Skeleton/grafika/Skeleton.tlog/CL.command.1.tlog b/Skeleton/grafika/Skeleton.tlog/CL.command.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..45f9de18dccae940a550f2de1cab35fb9c730557
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/CL.command.1.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/CL.read.1.tlog b/Skeleton/grafika/Skeleton.tlog/CL.read.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..3a3351dbe41098ef6cbc915000d451967a16e615
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/CL.read.1.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/CL.write.1.tlog b/Skeleton/grafika/Skeleton.tlog/CL.write.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..996a1997d1a3a0a47809e3ef3fdb53aeb576275a
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/CL.write.1.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/Skeleton.lastbuildstate b/Skeleton/grafika/Skeleton.tlog/Skeleton.lastbuildstate
new file mode 100644
index 0000000000000000000000000000000000000000..3bf59cc150334252c515699e15af15f4cf39fc09
--- /dev/null
+++ b/Skeleton/grafika/Skeleton.tlog/Skeleton.lastbuildstate
@@ -0,0 +1,2 @@
+PlatformToolSet=ClangCL:VCToolArchitecture=Native32Bit:VCToolsVersion=14.28.29910:TargetPlatformVersion=10.0.19041.0:
+C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\Llvm|11.0.0|grafika|Win32|D:\Grafika\HF2\Programs\Skeleton\|
diff --git a/Skeleton/grafika/Skeleton.tlog/Skeleton.write.1u.tlog b/Skeleton/grafika/Skeleton.tlog/Skeleton.write.1u.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..03c682e7a84dd6c2cdd7602a5443710c1aa3cd8a
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/Skeleton.write.1u.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/clang-cl.command.1.tlog b/Skeleton/grafika/Skeleton.tlog/clang-cl.command.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..22b0154645589e48b03d07fa8f386bf08a77a585
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/clang-cl.command.1.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/clang-cl.delete.1.tlog b/Skeleton/grafika/Skeleton.tlog/clang-cl.delete.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..fd2f74a13975d24b509d8d4d8f3f0ed035635d15
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/clang-cl.delete.1.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/clang-cl.read.1.tlog b/Skeleton/grafika/Skeleton.tlog/clang-cl.read.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..0ffaa8304fd753f0a4002a7434fd8e03eefd2dde
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/clang-cl.read.1.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/clang-cl.write.1.tlog b/Skeleton/grafika/Skeleton.tlog/clang-cl.write.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..d29c48842ff0120fec6f0b765da6e8e3abf5c7fe
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/clang-cl.write.1.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/lld-link.command.1.tlog b/Skeleton/grafika/Skeleton.tlog/lld-link.command.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..856ac8bb29b583bd146a31319eca744103e07bd7
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/lld-link.command.1.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/lld-link.delete.1.tlog b/Skeleton/grafika/Skeleton.tlog/lld-link.delete.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..221c83a08f3376c644bc80c77e3ae3cd0c7cb3e8
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/lld-link.delete.1.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/lld-link.read.1.tlog b/Skeleton/grafika/Skeleton.tlog/lld-link.read.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..b11aa723789931c3c5dcbe9c487328245524e514
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/lld-link.read.1.tlog differ
diff --git a/Skeleton/grafika/Skeleton.tlog/lld-link.write.1.tlog b/Skeleton/grafika/Skeleton.tlog/lld-link.write.1.tlog
new file mode 100644
index 0000000000000000000000000000000000000000..b2ab1c0adcbbd53f407f7e21cc1e7dbdc7ce96d7
Binary files /dev/null and b/Skeleton/grafika/Skeleton.tlog/lld-link.write.1.tlog differ