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 "GLSparseShadowMapRenderer.h"
22 #include <Core/Debug.h>
23 #include <Core/Exception.h>
24 #include <Core/Settings.h>
25 #include "GLModel.h"
26 #include "GLModelRenderer.h"
27 #include "GLProfiler.h"
28 #include "GLRenderer.h"
29 #include "GLRenderer.h"
30 #include "IGLDevice.h"
31 
32 namespace spades {
33 	namespace draw {
34 
GLSparseShadowMapRenderer(GLRenderer * r)35 		GLSparseShadowMapRenderer::GLSparseShadowMapRenderer(GLRenderer *r)
36 		    : IGLShadowMapRenderer(r), device(r->GetGLDevice()), settings(r->GetSettings()) {
37 			SPADES_MARK_FUNCTION();
38 
39 			textureSize = settings.r_shadowMapSize;
40 			if ((int)textureSize > 4096) {
41 				SPLog("r_shadowMapSize is too large; changed to 4096");
42 				settings.r_shadowMapSize = textureSize = 4096;
43 			}
44 
45 			for (minLod = 0; (Tiles << minLod) < textureSize; minLod++)
46 				;
47 			// minLod = std::max(0, minLod - 2);
48 			minLod = 0;
49 			maxLod = 15;
50 
51 			colorTexture = device->GenTexture();
52 			device->BindTexture(IGLDevice::Texture2D, colorTexture);
53 			device->TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::RGB, textureSize, textureSize, 0,
54 			                   IGLDevice::RGB, IGLDevice::UnsignedByte, NULL);
55 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter,
56 			                     IGLDevice::Linear);
57 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter,
58 			                     IGLDevice::Linear);
59 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS,
60 			                     IGLDevice::ClampToEdge);
61 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT,
62 			                     IGLDevice::ClampToEdge);
63 
64 			texture = device->GenTexture();
65 			device->BindTexture(IGLDevice::Texture2D, texture);
66 			device->TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::DepthComponent24, textureSize,
67 			                   textureSize, 0, IGLDevice::DepthComponent, IGLDevice::UnsignedInt,
68 			                   NULL);
69 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter,
70 			                     IGLDevice::Linear);
71 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter,
72 			                     IGLDevice::Linear);
73 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS,
74 			                     IGLDevice::ClampToEdge);
75 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT,
76 			                     IGLDevice::ClampToEdge);
77 
78 			pagetableTexture = device->GenTexture();
79 			device->BindTexture(IGLDevice::Texture2D, pagetableTexture);
80 			device->TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::RGBA8, Tiles, Tiles, 0,
81 			                   IGLDevice::BGRA, IGLDevice::UnsignedByte, NULL);
82 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter,
83 			                     IGLDevice::Nearest);
84 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter,
85 			                     IGLDevice::Nearest);
86 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS,
87 			                     IGLDevice::ClampToEdge);
88 			device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT,
89 			                     IGLDevice::ClampToEdge);
90 
91 			framebuffer = device->GenFramebuffer();
92 			device->BindFramebuffer(IGLDevice::Framebuffer, framebuffer);
93 			device->FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0,
94 			                             IGLDevice::Texture2D, colorTexture, 0);
95 			device->FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::DepthAttachment,
96 			                             IGLDevice::Texture2D, texture, 0);
97 
98 			device->BindFramebuffer(IGLDevice::Framebuffer, 0);
99 		}
100 
~GLSparseShadowMapRenderer()101 		GLSparseShadowMapRenderer::~GLSparseShadowMapRenderer() {
102 			SPADES_MARK_FUNCTION();
103 
104 			device->DeleteTexture(texture);
105 			device->DeleteTexture(pagetableTexture);
106 			device->DeleteFramebuffer(framebuffer);
107 			device->DeleteTexture(colorTexture);
108 		}
109 
110 #define Segment GLSSMRSegment
111 
112 		struct Segment {
113 			float low, high;
114 			bool empty;
Segmentspades::draw::Segment115 			Segment() : empty(true) {}
Segmentspades::draw::Segment116 			Segment(float l, float h) : low(std::min(l, h)), high(std::max(l, h)), empty(false) {}
operator +=spades::draw::Segment117 			void operator+=(const Segment &seg) {
118 				if (seg.empty)
119 					return;
120 				if (empty) {
121 					low = seg.low;
122 					high = seg.high;
123 					empty = false;
124 				} else {
125 					low = std::min(low, seg.low);
126 					high = std::max(high, seg.high);
127 				}
128 			}
operator +=spades::draw::Segment129 			void operator+=(float v) {
130 				if (empty) {
131 					low = high = v;
132 				} else {
133 					if (v < low)
134 						low = v;
135 					if (v > high)
136 						high = v;
137 				}
138 			}
139 		};
140 
FrustrumCoord(const client::SceneDefinition & def,float x,float y,float z)141 		static Vector3 FrustrumCoord(const client::SceneDefinition &def, float x, float y,
142 		                             float z) {
143 			x *= z;
144 			y *= z;
145 			return def.viewOrigin + def.viewAxis[0] * x + def.viewAxis[1] * y + def.viewAxis[2] * z;
146 		}
147 
ZRange(Vector3 base,Vector3 dir,Plane3 plane1,Plane3 plane2)148 		static Segment ZRange(Vector3 base, Vector3 dir, Plane3 plane1, Plane3 plane2) {
149 			return Segment(plane1.GetDistanceTo(base) / Vector3::Dot(dir, plane1.n),
150 			               plane2.GetDistanceTo(base) / Vector3::Dot(dir, plane2.n));
151 		}
152 
BuildMatrix(float near,float far)153 		void GLSparseShadowMapRenderer::BuildMatrix(float near, float far) {
154 			// TODO: variable light direction?
155 			Vector3 lightDir = MakeVector3(0, -1, -1).Normalize();
156 			// set better up dir?
157 			Vector3 up = MakeVector3(0, 0, 1);
158 			Vector3 side = Vector3::Cross(up, lightDir).Normalize();
159 			up = Vector3::Cross(lightDir, side).Normalize();
160 
161 			// build frustrum
162 			client::SceneDefinition def = GetRenderer()->GetSceneDef();
163 			Vector3 frustrum[8];
164 			float tanX = tanf(def.fovX * .5f);
165 			float tanY = tanf(def.fovY * .5f);
166 
167 			frustrum[0] = FrustrumCoord(def, tanX, tanY, near);
168 			frustrum[1] = FrustrumCoord(def, tanX, -tanY, near);
169 			frustrum[2] = FrustrumCoord(def, -tanX, tanY, near);
170 			frustrum[3] = FrustrumCoord(def, -tanX, -tanY, near);
171 			frustrum[4] = FrustrumCoord(def, tanX, tanY, far);
172 			frustrum[5] = FrustrumCoord(def, tanX, -tanY, far);
173 			frustrum[6] = FrustrumCoord(def, -tanX, tanY, far);
174 			frustrum[7] = FrustrumCoord(def, -tanX, -tanY, far);
175 
176 			// compute frustrum's x,y boundary
177 			float minX, maxX, minY, maxY;
178 			minX = maxX = Vector3::Dot(frustrum[0], side);
179 			minY = maxY = Vector3::Dot(frustrum[0], up);
180 			for (int i = 1; i < 8; i++) {
181 				float x = Vector3::Dot(frustrum[i], side);
182 				float y = Vector3::Dot(frustrum[i], up);
183 				if (x < minX)
184 					minX = x;
185 				if (x > maxX)
186 					maxX = x;
187 				if (y < minY)
188 					minY = y;
189 				if (y > maxY)
190 					maxY = y;
191 			}
192 
193 			// compute frustrum's z boundary
194 			Segment seg;
195 			Plane3 plane1(0, 0, 1, -4.f);
196 			Plane3 plane2(0, 0, 1, 64.f);
197 			seg += ZRange(side * minX + up * minY, lightDir, plane1, plane2);
198 			seg += ZRange(side * minX + up * maxY, lightDir, plane1, plane2);
199 			seg += ZRange(side * maxX + up * minY, lightDir, plane1, plane2);
200 			seg += ZRange(side * maxX + up * maxY, lightDir, plane1, plane2);
201 
202 			for (int i = 1; i < 8; i++) {
203 				seg += Vector3::Dot(frustrum[i], lightDir);
204 			}
205 
206 			// build frustrum obb
207 			Vector3 origin = side * minX + up * minY + lightDir * seg.low;
208 			Vector3 axis1 = side * (maxX - minX);
209 			Vector3 axis2 = up * (maxY - minY);
210 			Vector3 axis3 = lightDir * (seg.high - seg.low);
211 
212 			obb = OBB3(Matrix4::FromAxis(axis1, axis2, axis3, origin));
213 			vpWidth = 2.f / axis1.GetLength();
214 			vpHeight = 2.f / axis2.GetLength();
215 
216 			// convert to projectionview matrix
217 			matrix = obb.m.InversedFast();
218 
219 			matrix = Matrix4::Scale(2.f) * matrix;
220 			matrix = Matrix4::Translate(-1, -1, -1) * matrix;
221 
222 			// scale a little big for padding
223 			matrix = Matrix4::Scale(.98f) * matrix;
224 			//
225 			matrix = Matrix4::Scale(1, 1, -1) * matrix;
226 
227 // make sure frustrums in range
228 #ifndef NDEBUG
229 			for (int i = 0; i < 8; i++) {
230 				Vector4 v = matrix * frustrum[i];
231 				SPAssert(v.x >= -1.f);
232 				SPAssert(v.y >= -1.f);
233 				// SPAssert(v.z >= -1.f);
234 				SPAssert(v.x < 1.f);
235 				SPAssert(v.y < 1.f);
236 				// SPAssert(v.z < 1.f);
237 			}
238 #endif
239 		}
240 
Render()241 		void GLSparseShadowMapRenderer::Render() {
242 			SPADES_MARK_FUNCTION();
243 
244 			IGLDevice::Integer lastFb = device->GetInteger(IGLDevice::FramebufferBinding);
245 
246 			// client::SceneDefinition def = GetRenderer()->GetSceneDef();
247 
248 			float nearDist = 0.f;
249 			float farDist = 150.f;
250 
251 			BuildMatrix(nearDist, farDist);
252 
253 			device->BindFramebuffer(IGLDevice::Framebuffer, framebuffer);
254 			device->Viewport(0, 0, textureSize, textureSize);
255 			device->ClearDepth(1.f);
256 			device->Clear(IGLDevice::DepthBufferBit);
257 
258 			RenderShadowMapPass();
259 
260 			device->BindFramebuffer(IGLDevice::Framebuffer, lastFb);
261 
262 			device->Viewport(0, 0, device->ScreenWidth(), device->ScreenHeight());
263 		}
264 
265 #pragma mark - Sparse Processor
266 
267 		static const size_t NoGroup = (size_t)-1;
268 		static const size_t NoInstance = (size_t)-1;
269 		static const size_t NoNode = (size_t)-1;
270 		static const size_t GroupNodeFlag = NoNode ^ (NoNode >> 1);
271 
272 		struct GLSparseShadowMapRenderer::Internal {
273 			GLSparseShadowMapRenderer *renderer;
274 			Vector3 cameraShadowCoord;
275 
276 			typedef int LodUnit;
277 
278 			struct Instance {
279 				GLModel *model;
280 				const client::ModelRenderParam *param;
281 				IntVector3 tile1, tile2; // int aabb2
282 				size_t prev, next;       // instance in the same group
283 			};
284 
285 			/** All Instances. Sorted by model somehow. */
286 			std::vector<Instance> allInstances;
287 
288 			struct Group {
289 				IntVector3 tile1, tile2; // int aabb2
290 				size_t firstInstance, lastInstance;
291 				LodUnit lod;
292 				bool valid;
293 
294 				// shadow map texture coordinate
295 				int rawX, rawY, rawW, rawH;
296 			};
297 
298 			std::vector<Group> groups;
299 
300 			// packing node
301 			struct Node {
302 				size_t child1, child2;
303 				int pos;
304 				bool horizontal; // true: x=pos, false: y=pos
305 			};
306 
307 			std::vector<Node> nodes;
308 			int lodBias;
309 
310 			int mapSize;
311 
312 			size_t groupMap[Tiles][Tiles];
313 
ShadowMapToTileCoordspades::draw::GLSparseShadowMapRenderer::Internal314 			static IntVector3 ShadowMapToTileCoord(Vector3 vec) {
315 				vec.x = (vec.x * (float)(Tiles / 2)) + (float)(Tiles / 2);
316 				vec.y = (vec.y * (float)(Tiles / 2)) + (float)(Tiles / 2);
317 
318 				IntVector3 v;
319 				v.x = (int)floorf(vec.x);
320 				v.y = (int)floorf(vec.y);
321 				;
322 				return v;
323 			}
324 
TileToShadowMapCoordspades::draw::GLSparseShadowMapRenderer::Internal325 			static Vector3 TileToShadowMapCoord(IntVector3 tile) {
326 				Vector3 v;
327 				v.x = (float)tile.x * (2.f / (float)Tiles) - 1.f;
328 				v.y = (float)tile.y * (2.f / (float)Tiles) - 1.f;
329 				return v;
330 			}
ComputeLodspades::draw::GLSparseShadowMapRenderer::Internal331 			LodUnit ComputeLod(Vector3 shadowCoord) {
332 				float dx = shadowCoord.x - cameraShadowCoord.x;
333 				float dy = shadowCoord.y - cameraShadowCoord.y;
334 				float sq = dx * dx + dy * dy;
335 				float lod = 1.f / (sqrtf(sq) + .01f);
336 				int iv = (int)(lod * 200.f);
337 				if (iv < 1)
338 					iv = 1;
339 				if (iv > 65536)
340 					iv = 65535;
341 
342 				int ld = 0;
343 				while (iv > 0) {
344 					ld++;
345 					iv >>= 1;
346 				}
347 				return ld;
348 			}
349 
FillGroupMapspades::draw::GLSparseShadowMapRenderer::Internal350 			void FillGroupMap(IntVector3 tile1, IntVector3 tile2, size_t val) {
351 				for (int x = tile1.x; x < tile2.x; x++)
352 					for (int y = tile1.y; y < tile2.y; y++)
353 						groupMap[x][y] = val;
354 			}
355 
356 			/** Combines `dest` and `src`. Group `src` will be
357 			 * no longer valid. */
CombineGroupspades::draw::GLSparseShadowMapRenderer::Internal358 			void CombineGroup(size_t dest, size_t src) {
359 				Group &g1 = groups[dest];
360 				Group &g2 = groups[src];
361 				FillGroupMap(g2.tile1, g2.tile2, dest);
362 
363 				// extend the area
364 				g1.tile1.x = std::min(g1.tile1.x, g2.tile1.x);
365 				g1.tile1.y = std::min(g1.tile1.y, g2.tile1.y);
366 				g1.tile2.x = std::max(g1.tile2.x, g2.tile2.x);
367 				g1.tile2.y = std::max(g1.tile2.y, g2.tile2.y);
368 
369 				g1.lod = std::max(g1.lod, g2.lod);
370 
371 				// combine the instance list
372 				// [g1] + [g2] -> [g1, g2]
373 				Instance &destLast = allInstances[g1.lastInstance];
374 				Instance &srcFirst = allInstances[g2.firstInstance];
375 				SPAssert(destLast.next == NoInstance);
376 				SPAssert(srcFirst.prev == NoInstance);
377 				destLast.next = g2.firstInstance;
378 				srcFirst.prev = g1.lastInstance;
379 				g1.lastInstance = g2.lastInstance;
380 
381 				// make sure the area is filled with the group `dest`
382 				IntVector3 tile1 = g1.tile1, tile2 = g1.tile2;
383 				for (int x = tile1.x; x < tile2.x; x++)
384 					for (int y = tile1.y; y < tile2.y; y++) {
385 						size_t g = groupMap[x][y];
386 						SPAssert(g != src);
387 						if (g != NoGroup && g != dest) {
388 							CombineGroup(dest, g);
389 							SPAssert(groupMap[x][y] == dest);
390 						} else {
391 							groupMap[x][y] = dest;
392 						}
393 					}
394 
395 				g2.valid = false;
396 			}
397 
398 			/** Finds an group in the specified range (tile1, tile2).
399 			 * If one was not found, creates a new group.
400 			 * If more than one was found, combines all groups. */
RegisterGroupspades::draw::GLSparseShadowMapRenderer::Internal401 			size_t RegisterGroup(IntVector3 tile1, IntVector3 tile2) {
402 				size_t foundGroup = NoGroup;
403 				for (int x = tile1.x; x < tile2.x; x++)
404 					for (int y = tile1.y; y < tile2.y; y++) {
405 						size_t g = groupMap[x][y];
406 						if (g == NoGroup)
407 							continue;
408 						if (g == foundGroup)
409 							continue;
410 						if (foundGroup == NoGroup) {
411 							foundGroup = g;
412 							break;
413 						}
414 						// another group found.
415 						// this should be combined with `foundGroup`.
416 						CombineGroup(foundGroup, g);
417 						SPAssert(groupMap[x][y] == foundGroup);
418 					}
419 
420 				if (foundGroup == NoGroup) {
421 					// new group here.
422 					Group g;
423 					g.tile1 = tile1;
424 					g.tile2 = tile2;
425 					g.firstInstance = NoInstance;
426 					g.lastInstance = NoInstance;
427 					g.lod = -100; // lod is an int
428 					g.valid = true;
429 					groups.push_back(g);
430 
431 					size_t id = groups.size() - 1;
432 					FillGroupMap(tile1, tile2, id);
433 					return id;
434 				} else {
435 					// instance is added to an existing group.
436 					bool extended = false;
437 					Group &g = groups[foundGroup];
438 					if (tile1.x < g.tile1.x) {
439 						extended = true;
440 						g.tile1.x = tile1.x;
441 					}
442 					if (tile1.y < g.tile1.y) {
443 						extended = true;
444 						g.tile1.y = tile1.y;
445 					}
446 					if (tile2.x > g.tile2.x) {
447 						extended = true;
448 						g.tile2.x = tile2.x;
449 					}
450 					if (tile2.y > g.tile2.y) {
451 						extended = true;
452 						g.tile2.y = tile2.y;
453 					}
454 
455 					if (extended) {
456 						SPAssert(foundGroup != NoGroup);
457 						for (int x = g.tile1.x; x < g.tile2.x; x++)
458 							for (int y = g.tile1.y; y < g.tile2.y; y++) {
459 								size_t g = groupMap[x][y];
460 								if (g == NoGroup) {
461 									groupMap[x][y] = foundGroup;
462 									continue;
463 								}
464 								if (g == foundGroup)
465 									continue;
466 								// another group found.
467 								// this should be combined with `foundGroup`.
468 								CombineGroup(foundGroup, g);
469 								SPAssert(groupMap[x][y] == foundGroup);
470 							}
471 					}
472 
473 					// don't add instance here
474 
475 					return foundGroup;
476 				}
477 			}
478 
Internalspades::draw::GLSparseShadowMapRenderer::Internal479 			Internal(GLSparseShadowMapRenderer *r) : renderer(r) {
480 
481 				GLProfiler::Context profiler(r->GetRenderer()->GetGLProfiler(), "Sparse Page Table Generation");
482 
483 				cameraShadowCoord = r->GetRenderer()->GetSceneDef().viewOrigin;
484 				cameraShadowCoord = (r->matrix * cameraShadowCoord).GetXYZ();
485 
486 				// clear group maps
487 				for (size_t x = 0; x < Tiles; x++)
488 					for (size_t y = 0; y < Tiles; y++)
489 						groupMap[x][y] = NoGroup;
490 
491 				const std::vector<GLModelRenderer::RenderModel> &rmodels =
492 				  renderer->GetRenderer()->GetModelRenderer()->models;
493 				allInstances.reserve(256);
494 				groups.reserve(64);
495 				nodes.reserve(256);
496 
497 				for (std::vector<GLModelRenderer::RenderModel>::const_iterator it = rmodels.begin();
498 				     it != rmodels.end(); it++) {
499 					const GLModelRenderer::RenderModel &rmodel = *it;
500 					Instance inst;
501 
502 					inst.model = rmodel.model;
503 					OBB3 modelBounds = inst.model->GetBoundingBox();
504 					for (size_t i = 0; i < rmodel.params.size(); i++) {
505 						inst.param = &(rmodel.params[i]);
506 
507 						if (inst.param->depthHack)
508 							continue;
509 
510 						OBB3 instWorldBoundsOBB = inst.param->matrix * modelBounds;
511 						// w should be 1, so this should wor
512 						OBB3 instBoundsOBB = r->matrix * instWorldBoundsOBB;
513 						AABB3 instBounds = instBoundsOBB.GetBoundingAABB();
514 
515 						// frustrum(?) cull
516 						if (instBounds.max.x < -1.f || instBounds.max.y < -1.f ||
517 						    instBounds.min.x > 1.f || instBounds.min.y > 1.f)
518 							continue;
519 
520 						inst.tile1 = ShadowMapToTileCoord(instBounds.min);
521 						inst.tile2 = ShadowMapToTileCoord(instBounds.max);
522 						inst.tile2.x++;
523 						inst.tile2.y++;
524 
525 						if (inst.tile1.x < 0)
526 							inst.tile1.x = 0;
527 						if (inst.tile1.y < 0)
528 							inst.tile1.y = 0;
529 						if (inst.tile2.x > Tiles)
530 							inst.tile2.x = Tiles;
531 						if (inst.tile2.y > Tiles)
532 							inst.tile2.y = Tiles;
533 
534 						if (inst.tile1.x >= inst.tile2.x)
535 							continue;
536 						if (inst.tile1.y >= inst.tile2.y)
537 							continue;
538 
539 						size_t instId = allInstances.size();
540 						size_t grp = RegisterGroup(inst.tile1, inst.tile2);
541 						Group &g = groups[grp];
542 						SPAssert(g.valid);
543 						if (g.lastInstance == NoInstance) {
544 							// this is new group.
545 							g.firstInstance = instId;
546 							g.lastInstance = instId;
547 							inst.prev = NoInstance;
548 							inst.next = NoInstance;
549 						} else {
550 							// adding this instance to the group
551 							Instance &oldLast = allInstances[g.lastInstance];
552 							SPAssert(oldLast.next == NoInstance);
553 							oldLast.next = instId;
554 							inst.prev = g.lastInstance;
555 							inst.next = NoInstance;
556 							g.lastInstance = instId;
557 						}
558 
559 						g.lod = std::max(g.lod, ComputeLod(instBoundsOBB.m.GetOrigin()));
560 
561 						allInstances.push_back(inst);
562 					}
563 				}
564 
565 				mapSize = r->settings.r_shadowMapSize;
566 			}
567 
AddGroupToNodespades::draw::GLSparseShadowMapRenderer::Internal568 			bool AddGroupToNode(size_t &nodeRef, int nx, int ny, int nw, int nh, size_t gId) {
569 				Group &g = groups[gId];
570 				if (nodeRef == NoNode) {
571 					// empty node, try putting here
572 					int w = g.tile2.x - g.tile1.x;
573 					int h = g.tile2.y - g.tile1.y;
574 					int lod = g.lod;
575 					int minLod = renderer->minLod;
576 					int maxLod = renderer->maxLod;
577 					lod += lodBias;
578 					if (lod < minLod)
579 						lod = minLod;
580 					if (lod > maxLod)
581 						lod = maxLod;
582 					w <<= lod;
583 					h <<= lod;
584 					w += 2;
585 					h += 2; // safety margin
586 					if (w > nw || h > nh) {
587 						return false;
588 					}
589 
590 					g.rawX = nx + 1;
591 					g.rawY = ny + 1;
592 					g.rawW = w - 1;
593 					g.rawH = h - 1;
594 
595 					// how fits
596 					if (w == nw && h == nh) {
597 						// completely fits
598 						nodeRef = gId | GroupNodeFlag;
599 					} else if (w == nw) {
600 						Node node;
601 						node.child1 = gId | GroupNodeFlag;
602 						node.child2 = NoNode;
603 						node.horizontal = false;
604 						node.pos = ny + h;
605 						nodeRef = nodes.size();
606 						nodes.push_back(node);
607 					} else if (h == nh) {
608 						Node node;
609 						node.child1 = gId | GroupNodeFlag;
610 						node.child2 = NoNode;
611 						node.horizontal = true;
612 						node.pos = nx + w;
613 						nodeRef = nodes.size();
614 						nodes.push_back(node);
615 					} else {
616 						Node node;
617 						node.child1 = gId | GroupNodeFlag;
618 						node.child2 = NoNode;
619 						node.horizontal = true;
620 						node.pos = nx + w;
621 						size_t subnode = nodes.size();
622 						nodes.push_back(node);
623 
624 						node.child1 = subnode;
625 						node.child2 = NoNode;
626 						node.horizontal = false;
627 						node.pos = ny + h;
628 						nodeRef = nodes.size();
629 						nodes.push_back(node);
630 					}
631 
632 					return true;
633 				} else {
634 					if (nodeRef & GroupNodeFlag)
635 						return false;
636 					Node &node = nodes[nodeRef];
637 					if (node.horizontal) {
638 						if (AddGroupToNode(node.child1, nx, ny, node.pos - nx, nh, gId))
639 							return true;
640 						if (AddGroupToNode(node.child2, node.pos, ny, nx + nw - node.pos, nh, gId))
641 							return true;
642 					} else {
643 						if (AddGroupToNode(node.child1, nx, ny, nw, node.pos - ny, gId))
644 							return true;
645 						if (AddGroupToNode(node.child2, nx, node.pos, nw, ny + nh - node.pos, gId))
646 							return true;
647 					}
648 					return false;
649 				}
650 			}
651 
TryPackspades::draw::GLSparseShadowMapRenderer::Internal652 			bool TryPack() {
653 				size_t rootNode = NoNode;
654 				nodes.clear();
655 				for (size_t i = 0; i < groups.size(); i++) {
656 					if (!AddGroupToNode(rootNode, 0, 0, mapSize, mapSize, i))
657 						return false;
658 				}
659 				return true;
660 			}
661 
Packspades::draw::GLSparseShadowMapRenderer::Internal662 			void Pack() {
663 				if (groups.empty())
664 					return;
665 
666 				GLProfiler::Context profiler(renderer->GetRenderer()->GetGLProfiler(), "Pack [%d group(s)]", (int)groups.size());
667 
668 				lodBias = 100;
669 				if (TryPack()) {
670 					// try to make it more detail
671 					do {
672 						lodBias++;
673 					} while (TryPack() && lodBias < 140);
674 					lodBias--;
675 					TryPack();
676 				} else {
677 					// try to succeed packing by making it more rough
678 					do {
679 						lodBias--;
680 					} while (!TryPack());
681 				}
682 			}
683 		};
684 
685 		struct GLSparseShadowMapRenderer::ModelRenderer {
686 			std::vector<client::ModelRenderParam> params;
687 			GLModel *lastModel;
688 
ModelRendererspades::draw::GLSparseShadowMapRenderer::ModelRenderer689 			ModelRenderer() {
690 				params.resize(64);
691 				lastModel = NULL;
692 			}
693 
Flushspades::draw::GLSparseShadowMapRenderer::ModelRenderer694 			void Flush() {
695 				if (lastModel) {
696 					lastModel->RenderShadowMapPass(params);
697 					params.clear();
698 					lastModel = NULL;
699 				}
700 			}
701 
RenderModelspades::draw::GLSparseShadowMapRenderer::ModelRenderer702 			void RenderModel(GLModel *m, const client::ModelRenderParam &param) {
703 				if (m != lastModel)
704 					Flush();
705 				lastModel = m;
706 				params.push_back(param);
707 			}
708 		};
709 
RenderShadowMapPass()710 		void GLSparseShadowMapRenderer::RenderShadowMapPass() {
711 			Internal itnl(this);
712 			itnl.Pack();
713 
714 			{
715 				GLProfiler::Context profiler(GetRenderer()->GetGLProfiler(), "Page Table Generation");
716 				for (int x = 0; x < Tiles; x++) {
717 					for (int y = 0; y < Tiles; y++) {
718 						size_t val = itnl.groupMap[x][y];
719 						uint32_t out;
720 
721 						if (val == NoGroup) {
722 							out = 0xffffffff;
723 						} else {
724 							Internal::Group &g = itnl.groups[val];
725 							int lod = g.lod;
726 							lod += itnl.lodBias;
727 							if (lod < minLod)
728 								lod = minLod;
729 							if (lod > maxLod)
730 								lod = maxLod;
731 							int ix = x - g.tile1.x;
732 							int iy = y - g.tile1.y;
733 							ix <<= lod;
734 							iy <<= lod;
735 							ix += g.rawX;
736 							iy += g.rawY;
737 
738 							unsigned int rr, gg, bb, aa;
739 							rr = (ix & 255);
740 							gg = (iy & 255);
741 							bb = (ix >> 8) | ((iy >> 8) << 4);
742 							aa = lod; // 1 << (lod - minLod);
743 							out = bb | (gg << 8) | (rr << 16) | (aa << 24);
744 						}
745 
746 						pagetable[y][x] = out;
747 					}
748 				}
749 			}
750 
751 			{
752 				GLProfiler::Context profiler(GetRenderer()->GetGLProfiler(), "Page Table Upload");
753 				device->BindTexture(IGLDevice::Texture2D, pagetableTexture);
754 				device->TexSubImage2D(IGLDevice::Texture2D, 0, 0, 0, Tiles, Tiles, IGLDevice::BGRA,
755 				                      IGLDevice::UnsignedByte, pagetable);
756 			}
757 
758 			Matrix4 baseMatrix = matrix;
759 			{
760 				GLProfiler::Context profiler(GetRenderer()->GetGLProfiler(), "Shadow Maps [%d group(s)]", (int)itnl.groups.size());
761 				ModelRenderer mrend;
762 				for (size_t i = 0; i < itnl.groups.size(); i++) {
763 					Internal::Group &g = itnl.groups[i];
764 					device->Viewport(g.rawX, g.rawY, g.rawW, g.rawH);
765 
766 					Vector3 dest1 = Internal::TileToShadowMapCoord(g.tile1);
767 					Vector3 dest2 = Internal::TileToShadowMapCoord(g.tile2);
768 
769 					Matrix4 mat;
770 					mat = Matrix4::Translate((dest1.x + dest2.x) * -.5f, (dest1.y + dest2.y) * -.5f,
771 					                         0.f);
772 					mat =
773 					  Matrix4::Scale(2.f / (dest2.x - dest1.x), 2.f / (dest2.y - dest1.y), 1.f) *
774 					  mat;
775 					mat = mat * baseMatrix;
776 
777 					matrix = mat;
778 
779 					for (size_t mId = g.firstInstance; mId != NoInstance;
780 					     mId = itnl.allInstances[mId].next) {
781 						Internal::Instance &inst = itnl.allInstances[mId];
782 						mrend.RenderModel(inst.model, *inst.param);
783 
784 						Vector3 v =
785 						  inst.model->GetBoundingBox().min + inst.model->GetBoundingBox().max;
786 						v *= .5f;
787 						v = (inst.param->matrix * v).GetXYZ();
788 						{
789 							v = (baseMatrix * v).GetXYZ();
790 							SPAssert(v.x >= -1.2f);
791 							SPAssert(v.y >= -1.2f);
792 							SPAssert(v.x <= 1.2f);
793 							SPAssert(v.y <= 1.2f);
794 						}
795 					}
796 					mrend.Flush();
797 				}
798 			}
799 
800 			matrix = baseMatrix;
801 		}
802 
Cull(const spades::AABB3 &)803 		bool GLSparseShadowMapRenderer::Cull(const spades::AABB3 &) {
804 
805 			// TODO: who uses this?
806 			SPNotImplemented();
807 			return true;
808 		}
809 
SphereCull(const spades::Vector3 & center,float rad)810 		bool GLSparseShadowMapRenderer::SphereCull(const spades::Vector3 &center, float rad) {
811 			return true; // for models, this is already done
812 			             /*
813 			             Vector4 vw = matrix * center;
814 			             float xx = fabsf(vw.x);
815 			             float yy = fabsf(vw.y);
816 			             float rx = rad * vpWidth;
817 			             float ry = rad * vpHeight;
818 
819 			             return xx < (1.f + rx) && yy < (1.f + ry);*/
820 		}
821 	}
822 }
823