1 module unecht.core.components.camera;
2 
3 import unecht.core.components.misc;
4 import unecht.core.components.renderer;
5 import unecht.core.component;
6 import unecht.core.object;
7 import unecht.core.componentManager;
8 import unecht.core.entity;
9 
10 import unecht.core.types;
11 import unecht.core.math.ray;
12 
13 import gl3n.linalg;
14 
15 //TODO: create mixin and automation
16 version(UEIncludeEditor)
17 @EditorInspector("UECamera")
18 static class UECameraInspector : IComponentEditor
19 {
20 	/// render custom inspector for UECamera
21 	override bool render(UEObject _component)
22 	{
23 		auto thisT = cast(UECamera)_component;
24 
25 		import derelict.imgui.imgui;
26 		import unecht.core.components.internal.gui;
27 		import std.format;
28 
29 		igColorEdit4("clearColor",thisT.clearColor.vector,true);
30 		UEGui.DragFloat("fov",thisT.fieldOfView,1,360);
31 		UEGui.DragFloat("near",thisT.clipNear,0.01f);
32 		UEGui.DragFloat("far",thisT.clipFar,0.01f);
33 
34 		igCheckbox("isOrthographic",&thisT.isOrthographic);
35 		if(thisT.isOrthographic)
36 		{
37 			UEGui.DragFloat("orthoSize",thisT.orthoSize,0.01f);
38 		}
39 
40 		//TODO: impl
41 		return false;
42 	}
43 
44 	mixin UERegisterInspector!UECameraInspector;
45 }
46 
47 //TODO: add properties and make matrix updates implicit
48 /// camera component - handles rendering the world through its perspective
49 final class UECamera : UEComponent
50 {
51 	mixin(UERegisterObject!());
52 
53 	/// return projection matrix multiplied by look matrix
54 	@property auto projectionLook() const { return matProjection * matLook; }
55 
56 	@Serialize{
57 		float fieldOfView = 60;
58 		float clipNear = 1;
59 		float clipFar = 1000;
60 
61 		vec4 clearColor = vec4(0,0,0,1);
62 		bool clearBitColor = true;
63 		bool clearBitDepth = true;
64 		int visibleLayers = UECameraDefaultLayers;
65 
66 		bool isOrthographic=false;
67 		float orthoSize=1;
68 
69 		UERect viewport;
70 	}
71 
72 	///
73 	public ray screenToRay(vec2 screenPos)
74 	{
75 		import unecht.ue:ue;
76 
77 		UESize viewportSize = UESize(
78 			cast(int)(viewport.size.x * ue.application.windowSize.width),
79 			cast(int)(viewport.size.y * ue.application.windowSize.height));
80 
81 		float x = (2.0f * screenPos.x) / viewportSize.width - 1.0f;
82 		float y = (2.0f * screenPos.y) / viewportSize.height - 1.0f;
83 
84 		auto mouseClip = vec4 (x, -y, 1, 1);
85 
86 		auto matUnproj = matProjection.inverse();
87 
88 		auto mouseWorld = matUnproj * mouseClip;
89 
90 		mouseWorld /= (mouseWorld.w);
91 
92 		auto dir = mouseWorld.xyz;
93 
94 		dir.normalize();
95 
96 		dir = dir.xyz * matLook.get_rotation();
97 
98 		return ray(entity.sceneNode.position, dir);
99 	}
100 
101 	///
102 	private void updateLook()
103 	{
104 		auto lookat = entity.sceneNode.position + entity.sceneNode.forward;
105 
106 		matLook = mat4.look_at(entity.sceneNode.position, lookat, entity.sceneNode.up);
107 	}
108 
109 	///
110 	private void updateProjection()
111 	{
112 		if(!isOrthographic)
113 		{
114 			import unecht.ue:ue;
115 			auto w = ue.application.framebufferSize.width;
116 			auto h = ue.application.framebufferSize.height;
117 			matProjection = mat4.perspective(w, h, fieldOfView, clipNear, clipFar);
118 		}
119 		else
120 		{
121 			matProjection = mat4.orthographic(-(orthoSize/2),(orthoSize/2),-(orthoSize/2),(orthoSize/2),clipNear,clipFar);
122 		}
123 	}
124 
125 	/// render all renderables through the perspective of this camera
126 	///TODO: note: needs to be moved aways
127 	public void render()
128 	{
129 		import unecht.ue:ue;
130 		import derelict.opengl3.gl3;
131 
132 		updateProjection();
133 		updateLook();
134 
135 		int clearBits = 0;
136 		if(clearBitColor) clearBits |= GL_COLOR_BUFFER_BIT;
137 		if(clearBitDepth) clearBits |= GL_DEPTH_BUFFER_BIT;
138 
139 		if(clearBits!=0)
140 		{
141 			glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
142 			glClear(clearBits);
143 		}
144 
145 		UESize viewportSize = UESize(
146 			cast(int)(viewport.size.x * ue.application.framebufferSize.width),
147 			cast(int)(viewport.size.y * ue.application.framebufferSize.height));
148 		glViewport(viewport.pos.left,viewport.pos.top,viewportSize.width,viewportSize.height);
149 
150 		auto renderers = ue.scene.gatherAllComponents!UERenderer;
151 
152 		foreach(r; renderers)
153 		{
154 			if(r.enabled && r.sceneNode.enabled)
155 			{
156 				import unecht.core.stdex;
157 				if(testBit(visibleLayers, r.entity.layer))
158 					r.render(this);
159 			}
160 		}
161 	}
162 
163 private:
164 	mat4 matProjection = mat4.identity;
165 	mat4 matLook = mat4.identity;
166 }