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 "GLLongSpriteRenderer.h"
22 #include <Core/Debug.h>
23 #include "GLImage.h"
24 #include "GLProgram.h"
25 #include "GLRenderer.h"
26 #include "IGLDevice.h"
27 #include "SWFeatureLevel.h"
28 #include <Core/Settings.h>
29 
30 namespace spades {
31 	namespace draw {
32 
GLLongSpriteRenderer(GLRenderer * renderer)33 		GLLongSpriteRenderer::GLLongSpriteRenderer(GLRenderer *renderer)
34 		    : renderer(renderer),
35 		      device(renderer->GetGLDevice()),
36 		      settings(renderer->GetSettings()),
37 		      projectionViewMatrix("projectionViewMatrix"),
38 		      rightVector("rightVector"),
39 		      upVector("upVector"),
40 		      texture("mainTexture"),
41 		      viewMatrix("viewMatrix"),
42 		      fogDistance("fogDistance"),
43 		      fogColor("fogColor"),
44 		      viewOriginVector("viewOriginVector"),
45 		      positionAttribute("positionAttribute"),
46 		      texCoordAttribute("texCoordAttribute"),
47 		      colorAttribute("colorAttribute") {
48 			SPADES_MARK_FUNCTION();
49 
50 			program = renderer->RegisterProgram("Shaders/LongSprite.program");
51 		}
52 
~GLLongSpriteRenderer()53 		GLLongSpriteRenderer::~GLLongSpriteRenderer() { SPADES_MARK_FUNCTION(); }
54 
Add(spades::draw::GLImage * img,spades::Vector3 p1,spades::Vector3 p2,float rad,Vector4 color)55 		void GLLongSpriteRenderer::Add(spades::draw::GLImage *img, spades::Vector3 p1,
56 		                               spades::Vector3 p2, float rad, Vector4 color) {
57 			SPADES_MARK_FUNCTION_DEBUG();
58 			Sprite spr;
59 			spr.image = img;
60 			spr.start = p1;
61 			spr.end = p2;
62 			spr.radius = rad;
63 			if (settings.r_hdr) {
64 				// linearize color
65 				if (color.x > color.w || color.y > color.w || color.z > color.w) {
66 					// emissive material
67 					color.x *= color.x;
68 					color.y *= color.y;
69 					color.z *= color.z;
70 				} else {
71 					// scattering/absorptive material
72 					float rcp = fastRcp(color.w + .01);
73 					color.x *= color.x * rcp;
74 					color.y *= color.y * rcp;
75 					color.z *= color.z * rcp;
76 				}
77 			}
78 			spr.color = color;
79 			sprites.push_back(spr);
80 		}
81 
Clear()82 		void GLLongSpriteRenderer::Clear() {
83 			SPADES_MARK_FUNCTION();
84 			sprites.clear();
85 		}
86 
Render()87 		void GLLongSpriteRenderer::Render() {
88 			SPADES_MARK_FUNCTION();
89 			lastImage = NULL;
90 			program->Use();
91 
92 			projectionViewMatrix(program);
93 			rightVector(program);
94 			upVector(program);
95 			texture(program);
96 			viewMatrix(program);
97 			fogDistance(program);
98 			fogColor(program);
99 			viewOriginVector(program);
100 
101 			positionAttribute(program);
102 			texCoordAttribute(program);
103 			colorAttribute(program);
104 
105 			projectionViewMatrix.SetValue(renderer->GetProjectionViewMatrix());
106 			viewMatrix.SetValue(renderer->GetViewMatrix());
107 
108 			fogDistance.SetValue(renderer->GetFogDistance());
109 
110 			const auto &viewOrigin = renderer->GetSceneDef().viewOrigin;
111 			viewOriginVector.SetValue(viewOrigin.x, viewOrigin.y, viewOrigin.z);
112 
113 			Vector3 fogCol = renderer->GetFogColor();
114 			fogColor.SetValue(fogCol.x, fogCol.y, fogCol.z);
115 
116 			const client::SceneDefinition &def = renderer->GetSceneDef();
117 			rightVector.SetValue(def.viewAxis[0].x, def.viewAxis[0].y, def.viewAxis[0].z);
118 			upVector.SetValue(def.viewAxis[1].x, def.viewAxis[1].y, def.viewAxis[1].z);
119 			texture.SetValue(0);
120 
121 			device->ActiveTexture(0);
122 
123 			device->EnableVertexAttribArray(positionAttribute(), true);
124 			device->EnableVertexAttribArray(texCoordAttribute(), true);
125 			device->EnableVertexAttribArray(colorAttribute(), true);
126 
127 			for (size_t i = 0; i < sprites.size(); i++) {
128 				Sprite &spr = sprites[i];
129 				if (spr.image != lastImage) {
130 					Flush();
131 					lastImage = spr.image;
132 					SPAssert(vertices.empty());
133 				}
134 
135 				Vertex v;
136 				v.r = spr.color.x;
137 				v.g = spr.color.y;
138 				v.b = spr.color.z;
139 				v.a = spr.color.w;
140 
141 				uint32_t idx = (uint32_t)vertices.size();
142 
143 				// clip by view plane
144 				{
145 					float d1 = Vector3::Dot(spr.start - def.viewOrigin, def.viewAxis[2]);
146 					float d2 = Vector3::Dot(spr.end - def.viewOrigin, def.viewAxis[2]);
147 					const float clipPlane = .1f;
148 					if (d1 < clipPlane && d2 < clipPlane)
149 						continue;
150 					if (d1 > clipPlane || d2 > clipPlane) {
151 						if (d1 < clipPlane) {
152 							float per = (clipPlane - d1) / (d2 - d1);
153 							spr.start = Mix(spr.start, spr.end, per);
154 						} else if (d2 < clipPlane) {
155 							float per = (clipPlane - d1) / (d2 - d1);
156 							spr.end = Mix(spr.start, spr.end, per);
157 						}
158 					}
159 				}
160 
161 				// calculate view position
162 				Vector3 view1 = spr.start - def.viewOrigin;
163 				Vector3 view2 = spr.end - def.viewOrigin;
164 				view1 = MakeVector3(Vector3::Dot(view1, def.viewAxis[0]),
165 				                    Vector3::Dot(view1, def.viewAxis[1]),
166 				                    Vector3::Dot(view1, def.viewAxis[2]));
167 				view2 = MakeVector3(Vector3::Dot(view2, def.viewAxis[0]),
168 				                    Vector3::Dot(view2, def.viewAxis[1]),
169 				                    Vector3::Dot(view2, def.viewAxis[2]));
170 
171 				// transform to screen
172 				Vector2 scr1 = MakeVector2(view1.x / view1.z, view1.y / view1.z);
173 				Vector2 scr2 = MakeVector2(view2.x / view2.z, view2.y / view2.z);
174 
175 				Vector3 vecX = def.viewAxis[0] * spr.radius;
176 				Vector3 vecY = def.viewAxis[1] * spr.radius;
177 				float normalThreshold = spr.radius * 0.5f / ((view1.z + view2.z) * .5f);
178 				if ((scr2 - scr1).GetPoweredLength() < normalThreshold * normalThreshold) {
179 					// too short in screen; normal sprite
180 					v = spr.start - vecX - vecY;
181 					v.u = 0;
182 					v.v = 0;
183 					vertices.push_back(v);
184 
185 					v = spr.start + vecX - vecY;
186 					v.u = 1;
187 					v.v = 0;
188 					vertices.push_back(v);
189 
190 					v = spr.start - vecX + vecY;
191 					v.u = 0;
192 					v.v = 1;
193 					vertices.push_back(v);
194 
195 					v = spr.start + vecX + vecY;
196 					v.u = 1;
197 					v.v = 1;
198 					vertices.push_back(v);
199 
200 					indices.push_back(idx);
201 					indices.push_back(idx + 1);
202 					indices.push_back(idx + 2);
203 					indices.push_back(idx + 1);
204 					indices.push_back(idx + 3);
205 					indices.push_back(idx + 2);
206 				} else {
207 					Vector2 scrDir = (scr2 - scr1).Normalize();
208 					Vector2 normDir = {scrDir.y, -scrDir.x};
209 					Vector3 vecU = vecX * normDir.x + vecY * normDir.y;
210 					Vector3 vecV = vecX * scrDir.x + vecY * scrDir.y;
211 
212 					v = spr.start - vecU - vecV;
213 					v.u = 0;
214 					v.v = 0;
215 					vertices.push_back(v);
216 
217 					v = spr.start + vecU - vecV;
218 					v.u = 1;
219 					v.v = 0;
220 					vertices.push_back(v);
221 
222 					v = spr.start - vecU;
223 					v.u = 0;
224 					v.v = 0.5f;
225 					vertices.push_back(v);
226 
227 					v = spr.start + vecU;
228 					v.u = 1;
229 					v.v = 0.5f;
230 					vertices.push_back(v);
231 
232 					v = spr.end - vecU;
233 					v.u = 0;
234 					v.v = 0.5f;
235 					vertices.push_back(v);
236 
237 					v = spr.end + vecU;
238 					v.u = 1;
239 					v.v = 0.5f;
240 					vertices.push_back(v);
241 
242 					v = spr.end - vecU + vecV;
243 					v.u = 0;
244 					v.v = 1;
245 					vertices.push_back(v);
246 
247 					v = spr.end + vecU + vecV;
248 					v.u = 1;
249 					v.v = 1;
250 					vertices.push_back(v);
251 
252 					indices.push_back(idx);
253 					indices.push_back(idx + 1);
254 					indices.push_back(idx + 2);
255 					indices.push_back(idx + 1);
256 					indices.push_back(idx + 3);
257 					indices.push_back(idx + 2);
258 
259 					indices.push_back(idx + 2);
260 					indices.push_back(idx + 2 + 1);
261 					indices.push_back(idx + 2 + 2);
262 					indices.push_back(idx + 2 + 1);
263 					indices.push_back(idx + 2 + 3);
264 					indices.push_back(idx + 2 + 2);
265 
266 					indices.push_back(idx + 4);
267 					indices.push_back(idx + 4 + 1);
268 					indices.push_back(idx + 4 + 2);
269 					indices.push_back(idx + 4 + 1);
270 					indices.push_back(idx + 4 + 3);
271 					indices.push_back(idx + 4 + 2);
272 
273 					idx = (uint32_t)vertices.size();
274 
275 					v = spr.start - vecU + vecV;
276 					v.u = 0;
277 					v.v = 0;
278 					vertices.push_back(v);
279 
280 					v = spr.start + vecU + vecV;
281 					v.u = 1;
282 					v.v = 0;
283 					vertices.push_back(v);
284 
285 					v = spr.end - vecU - vecV;
286 					v.u = 0;
287 					v.v = 1;
288 					vertices.push_back(v);
289 
290 					v = spr.end + vecU - vecV;
291 					v.u = 1;
292 					v.v = 1;
293 					vertices.push_back(v);
294 
295 					v.r = v.g = v.b = v.a = 0.f;
296 
297 					v = spr.start - vecU;
298 					v.u = 0;
299 					v.v = 0.5f;
300 					vertices.push_back(v);
301 
302 					v = spr.start + vecU;
303 					v.u = 1;
304 					v.v = 0.5f;
305 					vertices.push_back(v);
306 
307 					v = spr.end - vecU;
308 					v.u = 0;
309 					v.v = 0.5f;
310 					vertices.push_back(v);
311 
312 					v = spr.end + vecU;
313 					v.u = 1;
314 					v.v = 0.5f;
315 					vertices.push_back(v);
316 
317 					indices.push_back(idx);
318 					indices.push_back(idx + 1);
319 					indices.push_back(idx + 2 + 2);
320 					indices.push_back(idx + 1);
321 					indices.push_back(idx + 2 + 3);
322 					indices.push_back(idx + 2 + 2);
323 
324 					indices.push_back(idx + 2);
325 					indices.push_back(idx + 2 + 1);
326 					indices.push_back(idx + 4 + 2);
327 					indices.push_back(idx + 2 + 1);
328 					indices.push_back(idx + 4 + 3);
329 					indices.push_back(idx + 4 + 2);
330 				}
331 			}
332 
333 			Flush();
334 
335 			device->EnableVertexAttribArray(positionAttribute(), false);
336 			device->EnableVertexAttribArray(texCoordAttribute(), false);
337 			device->EnableVertexAttribArray(colorAttribute(), false);
338 		}
339 
Flush()340 		void GLLongSpriteRenderer::Flush() {
341 			SPADES_MARK_FUNCTION_DEBUG();
342 
343 			if (vertices.empty())
344 				return;
345 
346 			device->VertexAttribPointer(positionAttribute(), 3, IGLDevice::FloatType, false,
347 			                            sizeof(Vertex), &(vertices[0].x));
348 			device->VertexAttribPointer(texCoordAttribute(), 2, IGLDevice::FloatType, false,
349 			                            sizeof(Vertex), &(vertices[0].u));
350 			device->VertexAttribPointer(colorAttribute(), 4, IGLDevice::FloatType, false,
351 			                            sizeof(Vertex), &(vertices[0].r));
352 
353 			SPAssert(lastImage);
354 			lastImage->Bind(IGLDevice::Texture2D);
355 
356 			device->DrawElements(IGLDevice::Triangles,
357 			                     static_cast<IGLDevice::Sizei>(indices.size()),
358 			                     IGLDevice::UnsignedInt, indices.data());
359 
360 			vertices.clear();
361 			indices.clear();
362 		}
363 	}
364 }
365