1 /*
2  Copyright (c) 2013 yvt
3 
4  This file is part of OpenSpades.
5 
6  OpenSpades is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OpenSpades is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OpenSpades.  If not, see <http://www.gnu.org/licenses/>.
18 
19  */
20 
21 #include "GLSoftSpriteRenderer.h"
22 #include <Core/Debug.h>
23 #include "GLFramebufferManager.h"
24 #include "GLImage.h"
25 #include "GLProfiler.h"
26 #include "GLProgram.h"
27 #include "GLQuadRenderer.h"
28 #include "GLRenderer.h"
29 #include "IGLDevice.h"
30 #include "SWFeatureLevel.h" // for fastRcp
31 
32 namespace spades {
33 	namespace draw {
34 
GLSoftSpriteRenderer(GLRenderer * renderer)35 		GLSoftSpriteRenderer::GLSoftSpriteRenderer(GLRenderer *renderer)
36 		    : renderer(renderer),
37 		      device(renderer->GetGLDevice()),
38 		      settings(renderer->GetSettings()),
39 		      projectionViewMatrix("projectionViewMatrix"),
40 		      rightVector("rightVector"),
41 		      upVector("upVector"),
42 		      frontVector("frontVector"),
43 		      viewOriginVector("viewOriginVector"),
44 		      texture("mainTexture"),
45 		      depthTexture("depthTexture"),
46 		      viewMatrix("viewMatrix"),
47 		      fogDistance("fogDistance"),
48 		      fogColor("fogColor"),
49 		      zNearFar("zNearFar"),
50 		      positionAttribute("positionAttribute"),
51 		      spritePosAttribute("spritePosAttribute"),
52 		      colorAttribute("colorAttribute") {
53 			SPADES_MARK_FUNCTION();
54 
55 			program = renderer->RegisterProgram("Shaders/SoftSprite.program");
56 		}
57 
~GLSoftSpriteRenderer()58 		GLSoftSpriteRenderer::~GLSoftSpriteRenderer() { SPADES_MARK_FUNCTION(); }
59 
Add(spades::draw::GLImage * img,spades::Vector3 center,float rad,float ang,Vector4 color)60 		void GLSoftSpriteRenderer::Add(spades::draw::GLImage *img, spades::Vector3 center,
61 		                               float rad, float ang, Vector4 color) {
62 			SPADES_MARK_FUNCTION_DEBUG();
63 			const client::SceneDefinition &def = renderer->GetSceneDef();
64 			Sprite spr;
65 			spr.image = img;
66 			spr.center = center;
67 			spr.radius = rad;
68 			spr.angle = ang;
69 			if (settings.r_hdr) {
70 				// linearize color
71 				if (color.x > color.w || color.y > color.w || color.z > color.w) {
72 					// emissive material
73 					color.x *= color.x;
74 					color.y *= color.y;
75 					color.z *= color.z;
76 				} else {
77 					// scattering/absorptive material
78 					float rcp = fastRcp(color.w + .01);
79 					color.x *= color.x * rcp;
80 					color.y *= color.y * rcp;
81 					color.z *= color.z * rcp;
82 				}
83 			}
84 			spr.color = color;
85 			spr.area = rad * rad * 4.f /
86 			           std::max(Vector3::Dot(center - def.viewOrigin, def.viewAxis[2]), 0.01f);
87 			sprites.push_back(spr);
88 		}
89 
Clear()90 		void GLSoftSpriteRenderer::Clear() {
91 			SPADES_MARK_FUNCTION();
92 			sprites.clear();
93 		}
94 
LayerForSprite(const Sprite & spr)95 		float GLSoftSpriteRenderer::LayerForSprite(const Sprite &spr) {
96 			float v = (spr.area - thresLow) / thresRange;
97 			if (v < 0.f)
98 				v = 0.f;
99 			if (v > 1.f)
100 				v = 1.f;
101 			return v;
102 		}
103 
Render()104 		void GLSoftSpriteRenderer::Render() {
105 			SPADES_MARK_FUNCTION();
106 			lastImage = NULL;
107 			program->Use();
108 
109 			device->Enable(IGLDevice::Blend, true);
110 			device->BlendFunc(IGLDevice::One, IGLDevice::OneMinusSrcAlpha);
111 
112 			projectionViewMatrix(program);
113 			rightVector(program);
114 			frontVector(program);
115 			viewOriginVector(program);
116 			upVector(program);
117 			texture(program);
118 			depthTexture(program);
119 			viewMatrix(program);
120 			fogDistance(program);
121 			fogColor(program);
122 			zNearFar(program);
123 
124 			positionAttribute(program);
125 			spritePosAttribute(program);
126 			colorAttribute(program);
127 
128 			projectionViewMatrix.SetValue(renderer->GetProjectionViewMatrix());
129 			viewMatrix.SetValue(renderer->GetViewMatrix());
130 
131 			fogDistance.SetValue(renderer->GetFogDistance());
132 
133 			Vector3 fogCol = renderer->GetFogColor();
134 			fogCol *= fogCol; // linearize
135 			fogColor.SetValue(fogCol.x, fogCol.y, fogCol.z);
136 
137 			const client::SceneDefinition &def = renderer->GetSceneDef();
138 			rightVector.SetValue(def.viewAxis[0].x, def.viewAxis[0].y, def.viewAxis[0].z);
139 			upVector.SetValue(def.viewAxis[1].x, def.viewAxis[1].y, def.viewAxis[1].z);
140 			frontVector.SetValue(def.viewAxis[2].x, def.viewAxis[2].y, def.viewAxis[2].z);
141 
142 			viewOriginVector.SetValue(def.viewOrigin.x, def.viewOrigin.y, def.viewOrigin.z);
143 			texture.SetValue(0);
144 			depthTexture.SetValue(1);
145 			zNearFar.SetValue(def.zNear, def.zFar);
146 
147 			device->ActiveTexture(1);
148 			device->BindTexture(IGLDevice::Texture2D,
149 			                    renderer->GetFramebufferManager()->GetDepthTexture());
150 			device->ActiveTexture(0);
151 
152 			device->EnableVertexAttribArray(positionAttribute(), true);
153 			device->EnableVertexAttribArray(spritePosAttribute(), true);
154 			device->EnableVertexAttribArray(colorAttribute(), true);
155 
156 			thresLow = tanf(def.fovX * .5f) * tanf(def.fovY * .5f) * 1.8f;
157 			thresRange = thresLow * .5f;
158 
159 			// full-resolution sprites
160 			{
161 				GLProfiler::Context measure(renderer->GetGLProfiler(), "Full Resolution");
162 				for (size_t i = 0; i < sprites.size(); i++) {
163 					Sprite &spr = sprites[i];
164 					float layer = LayerForSprite(spr);
165 					if (layer == 1.f)
166 						continue;
167 					if (spr.image != lastImage) {
168 						Flush();
169 						lastImage = spr.image;
170 						SPAssert(vertices.empty());
171 					}
172 
173 					Vertex v;
174 					v.x = spr.center.x;
175 					v.y = spr.center.y;
176 					v.z = spr.center.z;
177 					v.radius = spr.radius;
178 					v.angle = spr.angle;
179 					v.r = spr.color.x;
180 					v.g = spr.color.y;
181 					v.b = spr.color.z;
182 					v.a = spr.color.w;
183 
184 					float fade = 1.f - layer;
185 					v.r *= fade;
186 					v.g *= fade;
187 					v.b *= fade;
188 					v.a *= fade;
189 
190 					uint32_t idx = (uint32_t)vertices.size();
191 					v.sx = -1;
192 					v.sy = -1;
193 					vertices.push_back(v);
194 					v.sx = 1;
195 					v.sy = -1;
196 					vertices.push_back(v);
197 					v.sx = -1;
198 					v.sy = 1;
199 					vertices.push_back(v);
200 					v.sx = 1;
201 					v.sy = 1;
202 					vertices.push_back(v);
203 
204 					indices.push_back(idx);
205 					indices.push_back(idx + 1);
206 					indices.push_back(idx + 2);
207 					indices.push_back(idx + 1);
208 					indices.push_back(idx + 3);
209 					indices.push_back(idx + 2);
210 				}
211 
212 				Flush();
213 			}
214 
215 			// low-res sprites
216 			IGLDevice::UInteger lastFb = device->GetInteger(IGLDevice::FramebufferBinding);
217 			int sW = device->ScreenWidth(), sH = device->ScreenHeight();
218 			int lW = (sW + 3) / 4, lH = (sH + 3) / 4;
219 			int numLowResSprites = 0;
220 			GLColorBuffer buf = renderer->GetFramebufferManager()->CreateBufferHandle(lW, lH, true);
221 			device->BindFramebuffer(IGLDevice::Framebuffer, buf.GetFramebuffer());
222 			device->ClearColor(0.f, 0.f, 0.f, 0.f);
223 			device->Clear(IGLDevice::ColorBufferBit);
224 			device->BlendFunc(IGLDevice::One, IGLDevice::OneMinusSrcAlpha);
225 			device->Viewport(0, 0, lW, lH);
226 			{
227 				GLProfiler::Context measure(renderer->GetGLProfiler(), "Low Resolution");
228 				for (size_t i = 0; i < sprites.size(); i++) {
229 					Sprite &spr = sprites[i];
230 					float layer = LayerForSprite(spr);
231 					if (layer == 0.f)
232 						continue;
233 					if (spr.image != lastImage) {
234 						Flush();
235 						lastImage = spr.image;
236 						SPAssert(vertices.empty());
237 					}
238 
239 					numLowResSprites++;
240 
241 					Vertex v;
242 					v.x = spr.center.x;
243 					v.y = spr.center.y;
244 					v.z = spr.center.z;
245 					v.radius = spr.radius;
246 					v.angle = spr.angle;
247 					v.r = spr.color.x;
248 					v.g = spr.color.y;
249 					v.b = spr.color.z;
250 					v.a = spr.color.w;
251 
252 					float fade = layer;
253 					v.r *= fade;
254 					v.g *= fade;
255 					v.b *= fade;
256 					v.a *= fade;
257 
258 					uint32_t idx = (uint32_t)vertices.size();
259 					v.sx = -1;
260 					v.sy = -1;
261 					vertices.push_back(v);
262 					v.sx = 1;
263 					v.sy = -1;
264 					vertices.push_back(v);
265 					v.sx = -1;
266 					v.sy = 1;
267 					vertices.push_back(v);
268 					v.sx = 1;
269 					v.sy = 1;
270 					vertices.push_back(v);
271 
272 					indices.push_back(idx);
273 					indices.push_back(idx + 1);
274 					indices.push_back(idx + 2);
275 					indices.push_back(idx + 1);
276 					indices.push_back(idx + 3);
277 					indices.push_back(idx + 2);
278 				}
279 				Flush();
280 			}
281 
282 			// finalize
283 
284 			device->ActiveTexture(1);
285 			device->BindTexture(IGLDevice::Texture2D, 0);
286 			device->ActiveTexture(0);
287 			device->BindTexture(IGLDevice::Texture2D, 0);
288 			device->EnableVertexAttribArray(positionAttribute(), false);
289 			device->EnableVertexAttribArray(spritePosAttribute(), false);
290 			device->EnableVertexAttribArray(colorAttribute(), false);
291 
292 			// composite downsampled sprite
293 			device->BlendFunc(IGLDevice::One, IGLDevice::OneMinusSrcAlpha);
294 			if (numLowResSprites > 0) {
295 				GLProfiler::Context measure(renderer->GetGLProfiler(), "Finalize");
296 				GLQuadRenderer qr(device);
297 
298 				// do gaussian blur
299 				GLProgram *program =
300 				  renderer->RegisterProgram("Shaders/PostFilters/Gauss1D.program");
301 				static GLProgramAttribute blur_positionAttribute("positionAttribute");
302 				static GLProgramUniform blur_textureUniform("mainTexture");
303 				static GLProgramUniform blur_unitShift("unitShift");
304 				program->Use();
305 				blur_positionAttribute(program);
306 				blur_textureUniform(program);
307 				blur_unitShift(program);
308 				blur_textureUniform.SetValue(0);
309 				device->ActiveTexture(0);
310 				qr.SetCoordAttributeIndex(blur_positionAttribute());
311 				device->Enable(IGLDevice::Blend, false);
312 
313 				// x-direction
314 				GLColorBuffer buf2 =
315 				  renderer->GetFramebufferManager()->CreateBufferHandle(lW, lH, true);
316 				device->BindTexture(IGLDevice::Texture2D, buf.GetTexture());
317 				device->BindFramebuffer(IGLDevice::Framebuffer, buf2.GetFramebuffer());
318 				blur_unitShift.SetValue(1.f / lW, 0.f);
319 				qr.Draw();
320 				buf.Release();
321 
322 				// x-direction
323 				GLColorBuffer buf3 =
324 				  renderer->GetFramebufferManager()->CreateBufferHandle(lW, lH, true);
325 				device->BindTexture(IGLDevice::Texture2D, buf2.GetTexture());
326 				device->BindFramebuffer(IGLDevice::Framebuffer, buf3.GetFramebuffer());
327 				blur_unitShift.SetValue(0.f, 1.f / lH);
328 				qr.Draw();
329 				buf2.Release();
330 
331 				buf = buf3;
332 
333 				device->Enable(IGLDevice::Blend, true);
334 
335 				// composite
336 				program = renderer->RegisterProgram("Shaders/PostFilters/PassThrough.program");
337 				static GLProgramAttribute positionAttribute("positionAttribute");
338 				static GLProgramUniform colorUniform("colorUniform");
339 				static GLProgramUniform textureUniform("mainTexture");
340 				static GLProgramUniform texCoordRange("texCoordRange");
341 
342 				positionAttribute(program);
343 				textureUniform(program);
344 				texCoordRange(program);
345 				colorUniform(program);
346 
347 				program->Use();
348 
349 				textureUniform.SetValue(0);
350 				texCoordRange.SetValue(0.f, 0.f, 1.f, 1.f);
351 				colorUniform.SetValue(1.f, 1.f, 1.f, 1.f);
352 
353 				qr.SetCoordAttributeIndex(positionAttribute());
354 				device->BindFramebuffer(IGLDevice::Framebuffer, lastFb);
355 				device->BindTexture(IGLDevice::Texture2D, buf.GetTexture());
356 				device->Viewport(0, 0, sW, sH);
357 				qr.Draw();
358 				device->BindTexture(IGLDevice::Texture2D, 0);
359 
360 			} else {
361 				device->Viewport(0, 0, sW, sH);
362 
363 				device->BindFramebuffer(IGLDevice::Framebuffer, lastFb);
364 			}
365 
366 			buf.Release();
367 		}
368 
Flush()369 		void GLSoftSpriteRenderer::Flush() {
370 			SPADES_MARK_FUNCTION_DEBUG();
371 
372 			if (vertices.empty())
373 				return;
374 
375 			device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::FloatType, false,
376 			                            sizeof(Vertex), &(vertices[0].x));
377 			device->VertexAttribPointer(spritePosAttribute(), 4, IGLDevice::FloatType, false,
378 			                            sizeof(Vertex), &(vertices[0].sx));
379 			device->VertexAttribPointer(colorAttribute(), 4, IGLDevice::FloatType, false,
380 			                            sizeof(Vertex), &(vertices[0].r));
381 
382 			SPAssert(lastImage);
383 			lastImage->Bind(IGLDevice::Texture2D);
384 
385 			device->DrawElements(IGLDevice::Triangles,
386 			                     static_cast<IGLDevice::Sizei>(indices.size()),
387 			                     IGLDevice::UnsignedInt, indices.data());
388 
389 			vertices.clear();
390 			indices.clear();
391 		}
392 	}
393 }
394