1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include "LegacyMeshDrawer.h"
4 #include "Game/Camera.h"
5 #include "Map/SMF/SMFReadMap.h"
6 #include "Map/SMF/SMFGroundDrawer.h"
7 #include "Rendering/GlobalRendering.h"
8 #include "Rendering/ProjectileDrawer.h"
9 #include "Rendering/GL/myGL.h"
10 #include "Rendering/GL/VertexArray.h"
11 #include "System/Config/ConfigHandler.h"
12 #include "System/FastMath.h"
13 #include "System/myMath.h"
14 #include "System/Util.h"
15 
16 #define CLAMP(i) Clamp((i), 0, smfReadMap->maxHeightMapIdx)
17 
CLegacyMeshDrawer(CSMFReadMap * rm,CSMFGroundDrawer * gd)18 CLegacyMeshDrawer::CLegacyMeshDrawer(CSMFReadMap* rm, CSMFGroundDrawer* gd)
19 	: smfReadMap(rm)
20 	, smfGroundDrawer(gd)
21 	, viewRadius(4)
22 	, neededLod(4)
23 	//, waterDrawn(false)
24 {
25 }
26 
~CLegacyMeshDrawer()27 CLegacyMeshDrawer::~CLegacyMeshDrawer()
28 {
29 }
30 
31 
32 
33 
DrawVertexAQ(CVertexArray * ma,int x,int y)34 void CLegacyMeshDrawer::DrawVertexAQ(CVertexArray* ma, int x, int y)
35 {
36 	// don't send the normals as vertex attributes
37 	// (DLOD'ed triangles mess with interpolation)
38 	// const float3& n = readMap->vertexNormals[(y * smfReadMap->heightMapSizeX) + x];
39 
40 	DrawVertexAQ(ma, x, y, GetVisibleVertexHeight(y * smfReadMap->heightMapSizeX + x));
41 }
42 
DrawVertexAQ(CVertexArray * ma,int x,int y,float height)43 void CLegacyMeshDrawer::DrawVertexAQ(CVertexArray* ma, int x, int y, float height)
44 {
45 	//if (waterDrawn && height < 0.0f) {
46 	//	height *= 2.0f;
47 	//}
48 
49 	ma->AddVertexQ0(x * SQUARE_SIZE, height, y * SQUARE_SIZE);
50 }
51 
52 
DrawGroundVertexArrayQ(CVertexArray * & ma)53 void CLegacyMeshDrawer::DrawGroundVertexArrayQ(CVertexArray*& ma)
54 {
55 	ma->DrawArray0(GL_TRIANGLE_STRIP);
56 	ma = GetVertexArray();
57 }
58 
59 
60 
BigTexSquareRowVisible(const CCamera * cam,int bty) const61 bool CLegacyMeshDrawer::BigTexSquareRowVisible(const CCamera* cam, int bty) const
62 {
63 	const int minz =  bty * smfReadMap->bigTexSize;
64 	const int maxz = minz + smfReadMap->bigTexSize;
65 	const float miny = readMap->GetCurrMinHeight();
66 	const float maxy = math::fabs(cam->GetPos().y);
67 
68 	const float3 mins(                   0, miny, minz);
69 	const float3 maxs(smfReadMap->mapSizeX, maxy, maxz);
70 
71 	return (cam->InView(mins, maxs));
72 }
73 
74 
75 
FindRange(const CCamera * cam,int & xs,int & xe,int y,int lod)76 void CLegacyMeshDrawer::FindRange(const CCamera* cam, int& xs, int& xe, int y, int lod)
77 {
78 	int xt0, xt1;
79 
80 	const std::vector<CCamera::FrustumLine> negSides = cam->GetNegFrustumSides();
81 	const std::vector<CCamera::FrustumLine> posSides = cam->GetPosFrustumSides();
82 
83 	std::vector<CCamera::FrustumLine>::const_iterator fli;
84 
85 	for (fli = negSides.begin(); fli != negSides.end(); ++fli) {
86 		const float xtf = fli->base + fli->dir * y;
87 		xt0 = (int)xtf;
88 		xt1 = (int)(xtf + fli->dir * lod);
89 
90 		if (xt0 > xt1)
91 			xt0 = xt1;
92 
93 		xt0 = xt0 / lod * lod - lod;
94 
95 		if (xt0 > xs)
96 			xs = xt0;
97 	}
98 	for (fli = posSides.begin(); fli != posSides.end(); ++fli) {
99 		const float xtf = fli->base + fli->dir * y;
100 		xt0 = (int)xtf;
101 		xt1 = (int)(xtf + fli->dir * lod);
102 
103 		if (xt0 < xt1)
104 			xt0 = xt1;
105 
106 		xt0 = xt0 / lod * lod + lod;
107 
108 		if (xt0 < xe)
109 			xe = xt0;
110 	}
111 }
112 
113 
114 
DoDrawGroundRow(const CCamera * cam,int bty)115 void CLegacyMeshDrawer::DoDrawGroundRow(const CCamera* cam, int bty)
116 {
117 	if (!BigTexSquareRowVisible(cam, bty)) {
118 		//! skip this entire row of squares if we can't see it
119 		return;
120 	}
121 
122 	CVertexArray* ma = GetVertexArray();
123 
124 	bool inStrip = false;
125 	float x0, x1;
126 	int x,y;
127 	int sx = 0;
128 	int ex = smfReadMap->numBigTexX;
129 
130 	//! only process the necessary big squares in the x direction
131 	const int bigSquareSizeY = bty * smfReadMap->bigSquareSize;
132 
133 	const std::vector<CCamera::FrustumLine> negSides = cam->GetNegFrustumSides();
134 	const std::vector<CCamera::FrustumLine> posSides = cam->GetPosFrustumSides();
135 
136 	std::vector<CCamera::FrustumLine>::const_iterator fli;
137 
138 	for (fli = negSides.begin(); fli != negSides.end(); ++fli) {
139 		x0 = fli->base + fli->dir * bigSquareSizeY;
140 		x1 = x0 + fli->dir * smfReadMap->bigSquareSize;
141 
142 		if (x0 > x1)
143 			x0 = x1;
144 
145 		x0 /= smfReadMap->bigSquareSize;
146 
147 		if (x0 > sx)
148 			sx = (int) x0;
149 	}
150 	for (fli = posSides.begin(); fli != posSides.end(); ++fli) {
151 		x0 = fli->base + fli->dir * bigSquareSizeY + smfReadMap->bigSquareSize;
152 		x1 = x0 + fli->dir * smfReadMap->bigSquareSize;
153 
154 		if (x0 < x1)
155 			x0 = x1;
156 
157 		x0 /= smfReadMap->bigSquareSize;
158 
159 		if (x0 < ex)
160 			ex = (int) x0;
161 	}
162 
163 	if (sx > ex)
164 		return;
165 
166 	const float cx2 = cam2->GetPos().x / SQUARE_SIZE;
167 	const float cy2 = cam2->GetPos().z / SQUARE_SIZE;
168 
169 	for (int btx = sx; btx < ex; ++btx) {
170 		ma->Initialize();
171 
172 		for (int lod = 1; lod < neededLod; lod <<= 1) {
173 			float oldcamxpart = 0.0f;
174 			float oldcamypart = 0.0f;
175 
176 			const int hlod = lod >> 1;
177 			const int dlod = lod << 1;
178 
179 			int cx = cx2;
180 			int cy = cy2;
181 
182 			if (lod > 1) {
183 				int cxo = (cx / hlod) * hlod;
184 				int cyo = (cy / hlod) * hlod;
185 				float cx2o = (cxo / lod) * lod;
186 				float cy2o = (cyo / lod) * lod;
187 				oldcamxpart = (cx2 - cx2o) / lod;
188 				oldcamypart = (cy2 - cy2o) / lod;
189 			}
190 
191 			cx = (cx / lod) * lod;
192 			cy = (cy / lod) * lod;
193 
194 			const int ysquaremod = (cy % dlod) / lod;
195 			const int xsquaremod = (cx % dlod) / lod;
196 
197 			const float camxpart = (cx2 - ((cx / dlod) * dlod)) / dlod;
198 			const float camypart = (cy2 - ((cy / dlod) * dlod)) / dlod;
199 
200 			const float mcxp  = 1.0f - camxpart, mcyp  = 1.0f - camypart;
201 			const float hcxp  = 0.5f * camxpart, hcyp  = 0.5f * camypart;
202 			const float hmcxp = 0.5f * mcxp,     hmcyp = 0.5f * mcyp;
203 
204 			const float mocxp  = 1.0f - oldcamxpart, mocyp  = 1.0f - oldcamypart;
205 			const float hocxp  = 0.5f * oldcamxpart, hocyp  = 0.5f * oldcamypart;
206 			const float hmocxp = 0.5f * mocxp,       hmocyp = 0.5f * mocyp;
207 
208 			const int minty = bty * smfReadMap->bigSquareSize, maxty = minty + smfReadMap->bigSquareSize;
209 			const int mintx = btx * smfReadMap->bigSquareSize, maxtx = mintx + smfReadMap->bigSquareSize;
210 
211 			const int minly = cy + (-viewRadius + 3 - ysquaremod) * lod;
212 			const int maxly = cy + ( viewRadius - 1 - ysquaremod) * lod;
213 			const int minlx = cx + (-viewRadius + 3 - xsquaremod) * lod;
214 			const int maxlx = cx + ( viewRadius - 1 - xsquaremod) * lod;
215 
216 			const int xstart = std::max(minlx, mintx), xend = std::min(maxlx, maxtx);
217 			const int ystart = std::max(minly, minty), yend = std::min(maxly, maxty);
218 
219 			const int vrhlod = viewRadius * hlod;
220 
221 			for (y = ystart; y < yend; y += lod) {
222 				int xs = xstart;
223 				int xe = xend;
224 
225 				FindRange(cam2, /*inout*/ xs, /*inout*/ xe, y, lod);
226 
227 				// If FindRange modifies (xs, xe) to a (less then) empty range,
228 				// continue to the next row.
229 				// If we'd continue, nloop (below) would become negative and we'd
230 				// allocate a vertex array with negative size.  (mantis #1415)
231 				if (xe < xs) continue;
232 
233 				int ylod = y + lod;
234 				int yhlod = y + hlod;
235 				int nloop = (xe - xs) / lod + 1;
236 
237 				ma->EnlargeArrays(52 * nloop);
238 
239 				int yhdx = y * smfReadMap->heightMapSizeX;
240 				int ylhdx = yhdx + lod * smfReadMap->heightMapSizeX;
241 				int yhhdx = yhdx + hlod * smfReadMap->heightMapSizeX;
242 
243 				for (x = xs; x < xe; x += lod) {
244 					int xlod = x + lod;
245 					int xhlod = x + hlod;
246 					//! info: all triangle quads start in the top left corner
247 					if ((lod == 1) ||
248 						(x > cx + vrhlod) || (x < cx - vrhlod) ||
249 						(y > cy + vrhlod) || (y < cy - vrhlod)) {
250 						//! normal terrain (all vertices in one LOD)
251 						if (!inStrip) {
252 							DrawVertexAQ(ma, x, y);
253 							DrawVertexAQ(ma, x, ylod);
254 							inStrip = true;
255 						}
256 
257 						DrawVertexAQ(ma, xlod, y);
258 						DrawVertexAQ(ma, xlod, ylod);
259 					} else {
260 						//! border between 2 different LODs
261 						if ((x >= cx + vrhlod)) {
262 							//! lower LOD to the right
263 							int idx1 = CLAMP(yhdx + x),  idx1LOD = CLAMP(idx1 + lod), idx1HLOD = CLAMP(idx1 + hlod);
264 							int idx2 = CLAMP(ylhdx + x), idx2LOD = CLAMP(idx2 + lod), idx2HLOD = CLAMP(idx2 + hlod);
265 							int idx3 = CLAMP(yhhdx + x),                              idx3HLOD = CLAMP(idx3 + hlod);
266 							float h1 = (GetVisibleVertexHeight(idx1) + GetVisibleVertexHeight(idx2)) * hmocxp + GetVisibleVertexHeight(idx3) * oldcamxpart;
267 							float h2 = (GetVisibleVertexHeight(idx1) + GetVisibleVertexHeight(idx1LOD)) * hmocxp + GetVisibleVertexHeight(idx1HLOD) * oldcamxpart;
268 							float h3 = (GetVisibleVertexHeight(idx2) + GetVisibleVertexHeight(idx1LOD)) * hmocxp + GetVisibleVertexHeight(idx3HLOD) * oldcamxpart;
269 							float h4 = (GetVisibleVertexHeight(idx2) + GetVisibleVertexHeight(idx2LOD)) * hmocxp + GetVisibleVertexHeight(idx2HLOD) * oldcamxpart;
270 
271 							if (inStrip) {
272 								ma->EndStrip();
273 								inStrip = false;
274 							}
275 
276 							DrawVertexAQ(ma, x, y);
277 							DrawVertexAQ(ma, x, yhlod, h1);
278 							DrawVertexAQ(ma, xhlod, y, h2);
279 							DrawVertexAQ(ma, xhlod, yhlod, h3);
280 							ma->EndStrip();
281 							DrawVertexAQ(ma, x, yhlod, h1);
282 							DrawVertexAQ(ma, x, ylod);
283 							DrawVertexAQ(ma, xhlod, yhlod, h3);
284 							DrawVertexAQ(ma, xhlod, ylod, h4);
285 							ma->EndStrip();
286 							DrawVertexAQ(ma, xhlod, ylod, h4);
287 							DrawVertexAQ(ma, xlod, ylod);
288 							DrawVertexAQ(ma, xhlod, yhlod, h3);
289 							DrawVertexAQ(ma, xlod, y);
290 							DrawVertexAQ(ma, xhlod, y, h2);
291 							ma->EndStrip();
292 						}
293 						else if ((x <= cx - vrhlod)) {
294 							//! lower LOD to the left
295 							int idx1 = CLAMP(yhdx + x),  idx1LOD = CLAMP(idx1 + lod), idx1HLOD = CLAMP(idx1 + hlod);
296 							int idx2 = CLAMP(ylhdx + x), idx2LOD = CLAMP(idx2 + lod), idx2HLOD = CLAMP(idx2 + hlod);
297 							int idx3 = CLAMP(yhhdx + x), idx3LOD = CLAMP(idx3 + lod), idx3HLOD = CLAMP(idx3 + hlod);
298 							float h1 = (GetVisibleVertexHeight(idx1LOD) + GetVisibleVertexHeight(idx2LOD)) * hocxp + GetVisibleVertexHeight(idx3LOD ) * mocxp;
299 							float h2 = (GetVisibleVertexHeight(idx1   ) + GetVisibleVertexHeight(idx1LOD)) * hocxp + GetVisibleVertexHeight(idx1HLOD) * mocxp;
300 							float h3 = (GetVisibleVertexHeight(idx2   ) + GetVisibleVertexHeight(idx1LOD)) * hocxp + GetVisibleVertexHeight(idx3HLOD) * mocxp;
301 							float h4 = (GetVisibleVertexHeight(idx2   ) + GetVisibleVertexHeight(idx2LOD)) * hocxp + GetVisibleVertexHeight(idx2HLOD) * mocxp;
302 
303 							if (inStrip) {
304 								ma->EndStrip();
305 								inStrip = false;
306 							}
307 
308 							DrawVertexAQ(ma, xlod, yhlod, h1);
309 							DrawVertexAQ(ma, xlod, y);
310 							DrawVertexAQ(ma, xhlod, yhlod, h3);
311 							DrawVertexAQ(ma, xhlod, y, h2);
312 							ma->EndStrip();
313 							DrawVertexAQ(ma, xlod, ylod);
314 							DrawVertexAQ(ma, xlod, yhlod, h1);
315 							DrawVertexAQ(ma, xhlod, ylod, h4);
316 							DrawVertexAQ(ma, xhlod, yhlod, h3);
317 							ma->EndStrip();
318 							DrawVertexAQ(ma, xhlod, y, h2);
319 							DrawVertexAQ(ma, x, y);
320 							DrawVertexAQ(ma, xhlod, yhlod, h3);
321 							DrawVertexAQ(ma, x, ylod);
322 							DrawVertexAQ(ma, xhlod, ylod, h4);
323 							ma->EndStrip();
324 						}
325 
326 						if ((y >= cy + vrhlod)) {
327 							//! lower LOD above
328 							int idx1 = yhdx + x,  idx1LOD = CLAMP(idx1 + lod), idx1HLOD = CLAMP(idx1 + hlod);
329 							int idx2 = ylhdx + x, idx2LOD = CLAMP(idx2 + lod);
330 							int idx3 = yhhdx + x, idx3LOD = CLAMP(idx3 + lod), idx3HLOD = CLAMP(idx3 + hlod);
331 							float h1 = (GetVisibleVertexHeight(idx1   ) + GetVisibleVertexHeight(idx1LOD)) * hmocyp + GetVisibleVertexHeight(idx1HLOD) * oldcamypart;
332 							float h2 = (GetVisibleVertexHeight(idx1   ) + GetVisibleVertexHeight(idx2   )) * hmocyp + GetVisibleVertexHeight(idx3    ) * oldcamypart;
333 							float h3 = (GetVisibleVertexHeight(idx2   ) + GetVisibleVertexHeight(idx1LOD)) * hmocyp + GetVisibleVertexHeight(idx3HLOD) * oldcamypart;
334 							float h4 = (GetVisibleVertexHeight(idx2LOD) + GetVisibleVertexHeight(idx1LOD)) * hmocyp + GetVisibleVertexHeight(idx3LOD ) * oldcamypart;
335 
336 							if (inStrip) {
337 								ma->EndStrip();
338 								inStrip = false;
339 							}
340 
341 							DrawVertexAQ(ma, x, y);
342 							DrawVertexAQ(ma, x, yhlod, h2);
343 							DrawVertexAQ(ma, xhlod, y, h1);
344 							DrawVertexAQ(ma, xhlod, yhlod, h3);
345 							DrawVertexAQ(ma, xlod, y);
346 							DrawVertexAQ(ma, xlod, yhlod, h4);
347 							ma->EndStrip();
348 							DrawVertexAQ(ma, x, yhlod, h2);
349 							DrawVertexAQ(ma, x, ylod);
350 							DrawVertexAQ(ma, xhlod, yhlod, h3);
351 							DrawVertexAQ(ma, xlod, ylod);
352 							DrawVertexAQ(ma, xlod, yhlod, h4);
353 							ma->EndStrip();
354 						}
355 						else if ((y <= cy - vrhlod)) {
356 							//! lower LOD beneath
357 							int idx1 = CLAMP(yhdx + x),  idx1LOD = CLAMP(idx1 + lod);
358 							int idx2 = CLAMP(ylhdx + x), idx2LOD = CLAMP(idx2 + lod), idx2HLOD = CLAMP(idx2 + hlod);
359 							int idx3 = CLAMP(yhhdx + x), idx3LOD = CLAMP(idx3 + lod), idx3HLOD = CLAMP(idx3 + hlod);
360 							float h1 = (GetVisibleVertexHeight(idx2   ) + GetVisibleVertexHeight(idx2LOD)) * hocyp + GetVisibleVertexHeight(idx2HLOD) * mocyp;
361 							float h2 = (GetVisibleVertexHeight(idx1   ) + GetVisibleVertexHeight(idx2   )) * hocyp + GetVisibleVertexHeight(idx3    ) * mocyp;
362 							float h3 = (GetVisibleVertexHeight(idx2   ) + GetVisibleVertexHeight(idx1LOD)) * hocyp + GetVisibleVertexHeight(idx3HLOD) * mocyp;
363 							float h4 = (GetVisibleVertexHeight(idx2LOD) + GetVisibleVertexHeight(idx1LOD)) * hocyp + GetVisibleVertexHeight(idx3LOD ) * mocyp;
364 
365 							if (inStrip) {
366 								ma->EndStrip();
367 								inStrip = false;
368 							}
369 
370 							DrawVertexAQ(ma, x, yhlod, h2);
371 							DrawVertexAQ(ma, x, ylod);
372 							DrawVertexAQ(ma, xhlod, yhlod, h3);
373 							DrawVertexAQ(ma, xhlod, ylod, h1);
374 							DrawVertexAQ(ma, xlod, yhlod, h4);
375 							DrawVertexAQ(ma, xlod, ylod);
376 							ma->EndStrip();
377 							DrawVertexAQ(ma, xlod, yhlod, h4);
378 							DrawVertexAQ(ma, xlod, y);
379 							DrawVertexAQ(ma, xhlod, yhlod, h3);
380 							DrawVertexAQ(ma, x, y);
381 							DrawVertexAQ(ma, x, yhlod, h2);
382 							ma->EndStrip();
383 						}
384 					}
385 				}
386 
387 				if (inStrip) {
388 					ma->EndStrip();
389 					inStrip = false;
390 				}
391 			} //for (y = ystart; y < yend; y += lod)
392 
393 			const int yst = std::max(ystart - lod, minty);
394 			const int yed = std::min(yend + lod, maxty);
395 			int nloop = (yed - yst) / lod + 1;
396 
397 			if (nloop > 0)
398 				ma->EnlargeArrays(8 * nloop);
399 
400 			//! rita yttre begr?snings yta mot n?ta lod
401 			if (maxlx < maxtx && maxlx >= mintx) {
402 				x = maxlx;
403 				int xlod = x + lod;
404 				for (y = yst; y < yed; y += lod) {
405 					DrawVertexAQ(ma, x, y);
406 					DrawVertexAQ(ma, x, y + lod);
407 
408 					if (y % dlod) {
409 						const int idx1 = CLAMP((y      ) * smfReadMap->heightMapSizeX + x), idx1LOD = CLAMP(idx1 + lod);
410 						const int idx2 = CLAMP((y + lod) * smfReadMap->heightMapSizeX + x), idx2LOD = CLAMP(idx2 + lod);
411 						const int idx3 = CLAMP((y - lod) * smfReadMap->heightMapSizeX + x), idx3LOD = CLAMP(idx3 + lod);
412 						const float h = (GetVisibleVertexHeight(idx3LOD) + GetVisibleVertexHeight(idx2LOD)) * hmcxp +	GetVisibleVertexHeight(idx1LOD) * camxpart;
413 						DrawVertexAQ(ma, xlod, y, h);
414 						DrawVertexAQ(ma, xlod, y + lod);
415 					} else {
416 						const int idx1 = CLAMP((y       ) * smfReadMap->heightMapSizeX + x), idx1LOD = CLAMP(idx1 + lod);
417 						const int idx2 = CLAMP((y +  lod) * smfReadMap->heightMapSizeX + x), idx2LOD = CLAMP(idx2 + lod);
418 						const int idx3 = CLAMP((y + dlod) * smfReadMap->heightMapSizeX + x), idx3LOD = CLAMP(idx3 + lod);
419 						const float h = (GetVisibleVertexHeight(idx1LOD) + GetVisibleVertexHeight(idx3LOD)) * hmcxp + GetVisibleVertexHeight(idx2LOD) * camxpart;
420 						DrawVertexAQ(ma, xlod, y);
421 						DrawVertexAQ(ma, xlod, y + lod, h);
422 					}
423 					ma->EndStrip();
424 				}
425 			}
426 
427 			if (minlx > mintx && minlx < maxtx) {
428 				x = minlx - lod;
429 				int xlod = x + lod;
430 				for (y = yst; y < yed; y += lod) {
431 					if (y % dlod) {
432 						int idx1 = CLAMP((y      ) * smfReadMap->heightMapSizeX + x);
433 						int idx2 = CLAMP((y + lod) * smfReadMap->heightMapSizeX + x);
434 						int idx3 = CLAMP((y - lod) * smfReadMap->heightMapSizeX + x);
435 						float h = (GetVisibleVertexHeight(idx3) + GetVisibleVertexHeight(idx2)) * hcxp + GetVisibleVertexHeight(idx1) * mcxp;
436 						DrawVertexAQ(ma, x, y, h);
437 						DrawVertexAQ(ma, x, y + lod);
438 					} else {
439 						int idx1 = CLAMP((y       ) * smfReadMap->heightMapSizeX + x);
440 						int idx2 = CLAMP((y +  lod) * smfReadMap->heightMapSizeX + x);
441 						int idx3 = CLAMP((y + dlod) * smfReadMap->heightMapSizeX + x);
442 						float h = (GetVisibleVertexHeight(idx1) + GetVisibleVertexHeight(idx3)) * hcxp + GetVisibleVertexHeight(idx2) * mcxp;
443 						DrawVertexAQ(ma, x, y);
444 						DrawVertexAQ(ma, x, y + lod, h);
445 					}
446 					DrawVertexAQ(ma, xlod, y);
447 					DrawVertexAQ(ma, xlod, y + lod);
448 					ma->EndStrip();
449 				}
450 			}
451 
452 			if (maxly < maxty && maxly > minty) {
453 				y = maxly;
454 				int xs = std::max(xstart - lod, mintx);
455 				int xe = std::min(xend + lod,   maxtx);
456 				FindRange(cam2, xs, xe, y, lod);
457 
458 				if (xs < xe) {
459 					x = xs;
460 					int ylod = y + lod;
461 					int nloop = (xe - xs) / lod + 2; //! one extra for if statment
462 					int ylhdx = (y + lod) * smfReadMap->heightMapSizeX;
463 
464 					ma->EnlargeArrays(2 * nloop);
465 
466 					if (x % dlod) {
467 						int idx2 = CLAMP(ylhdx + x), idx2PLOD = CLAMP(idx2 + lod), idx2MLOD = CLAMP(idx2 - lod);
468 						float h = (GetVisibleVertexHeight(idx2MLOD) + GetVisibleVertexHeight(idx2PLOD)) * hmcyp + GetVisibleVertexHeight(idx2) * camypart;
469 						DrawVertexAQ(ma, x, y);
470 						DrawVertexAQ(ma, x, ylod, h);
471 					} else {
472 						DrawVertexAQ(ma, x, y);
473 						DrawVertexAQ(ma, x, ylod);
474 					}
475 					for (x = xs; x < xe; x += lod) {
476 						if (x % dlod) {
477 							DrawVertexAQ(ma, x + lod, y);
478 							DrawVertexAQ(ma, x + lod, ylod);
479 						} else {
480 							int idx2 = CLAMP(ylhdx + x), idx2PLOD  = CLAMP(idx2 +  lod), idx2PLOD2 = CLAMP(idx2 + dlod);
481 							float h = (GetVisibleVertexHeight(idx2PLOD2) + GetVisibleVertexHeight(idx2)) * hmcyp + GetVisibleVertexHeight(idx2PLOD) * camypart;
482 							DrawVertexAQ(ma, x + lod, y);
483 							DrawVertexAQ(ma, x + lod, ylod, h);
484 						}
485 					}
486 					ma->EndStrip();
487 				}
488 			}
489 
490 			if (minly > minty && minly < maxty) {
491 				y = minly - lod;
492 				int xs = std::max(xstart - lod, mintx);
493 				int xe = std::min(xend + lod,   maxtx);
494 				FindRange(cam2, xs, xe, y, lod);
495 
496 				if (xs < xe) {
497 					x = xs;
498 					int ylod = y + lod;
499 					int yhdx = y * smfReadMap->heightMapSizeX;
500 					int nloop = (xe - xs) / lod + 2; //! one extra for if statment
501 
502 					ma->EnlargeArrays(2 * nloop);
503 
504 					if (x % dlod) {
505 						int idx1 = CLAMP(yhdx + x), idx1PLOD = CLAMP(idx1 + lod), idx1MLOD = CLAMP(idx1 - lod);
506 						float h = (GetVisibleVertexHeight(idx1MLOD) + GetVisibleVertexHeight(idx1PLOD)) * hcyp + GetVisibleVertexHeight(idx1) * mcyp;
507 						DrawVertexAQ(ma, x, y, h);
508 						DrawVertexAQ(ma, x, ylod);
509 					} else {
510 						DrawVertexAQ(ma, x, y);
511 						DrawVertexAQ(ma, x, ylod);
512 					}
513 
514 					for (x = xs; x < xe; x+= lod) {
515 						if (x % dlod) {
516 							DrawVertexAQ(ma, x + lod, y);
517 							DrawVertexAQ(ma, x + lod, ylod);
518 						} else {
519 							int idx1 = CLAMP(yhdx + x), idx1PLOD  = CLAMP(idx1 +  lod), idx1PLOD2 = CLAMP(idx1 + dlod);
520 							float h = (GetVisibleVertexHeight(idx1PLOD2) + GetVisibleVertexHeight(idx1)) * hcyp + GetVisibleVertexHeight(idx1PLOD) * mcyp;
521 							DrawVertexAQ(ma, x + lod, y, h);
522 							DrawVertexAQ(ma, x + lod, ylod);
523 						}
524 					}
525 					ma->EndStrip();
526 				}
527 			}
528 
529 		} //for (int lod = 1; lod < neededLod; lod <<= 1)
530 
531 		smfGroundDrawer->SetupBigSquare(btx, bty);
532 		DrawGroundVertexArrayQ(ma);
533 	}
534 }
535 
536 
UpdateLODParams(const DrawPass::e & drawPass)537 void CLegacyMeshDrawer::UpdateLODParams(const DrawPass::e& drawPass)
538 {
539 	// Get current ground detail (draw pass dependent)
540 	viewRadius  = smfGroundDrawer->GetGroundDetail(drawPass);
541 
542 	// Take FOV into account
543 	viewRadius  = int(viewRadius * fastmath::apxsqrt(45.0f / camera->GetFov()));
544 
545 	// Clamp it to mapsize dependent minimum, else we get holes in the terrain
546 	viewRadius  = std::max(std::max(smfReadMap->numBigTexY, smfReadMap->numBigTexX) + 1, viewRadius);
547 
548 	// we need a multiple of 2
549 	viewRadius += (viewRadius & 1);
550 
551 	// Compute count of LODs needed/visible
552 	neededLod   = std::max(1, int((globalRendering->viewRange * 0.125f) / viewRadius) << 1);
553 	neededLod   = std::min(neededLod, std::min(gs->mapx, gs->mapy));
554 }
555 
556 
DrawMesh(const DrawPass::e & drawPass)557 void CLegacyMeshDrawer::DrawMesh(const DrawPass::e& drawPass)
558 {
559 	if (drawPass == DrawPass::Shadow) {
560 		DrawShadowMesh();
561 		return;
562 	}
563 
564 	UpdateLODParams(drawPass);
565 
566 	//waterDrawn = (drawPass == DrawPass::WaterReflection);
567 
568 	{ // profiler scope
569 		{
570 			int camBty = math::floor(cam2->GetPos().z / (smfReadMap->bigSquareSize * SQUARE_SIZE));
571 			camBty = std::max(0, std::min(smfReadMap->numBigTexY - 1, camBty));
572 
573 			//! try to render in "front to back" (so start with the camera nearest BigGroundLines)
574 			for (int bty = camBty; bty >= 0; --bty) {
575 				DoDrawGroundRow(cam2, bty);
576 			}
577 			for (int bty = camBty + 1; bty < smfReadMap->numBigTexY; ++bty) {
578 				DoDrawGroundRow(cam2, bty);
579 			}
580 		}
581 	}
582 }
583 
584 
DoDrawGroundShadowLOD(int nlod)585 void CLegacyMeshDrawer::DoDrawGroundShadowLOD(int nlod) {
586 	CVertexArray* ma = GetVertexArray();
587 	ma->Initialize();
588 
589 	bool inStrip = false;
590 	int x,y;
591 	int lod = 1 << nlod;
592 
593 	float cx2 = camera->GetPos().x / SQUARE_SIZE;
594 	float cy2 = camera->GetPos().z / SQUARE_SIZE;
595 
596 	float oldcamxpart = 0.0f;
597 	float oldcamypart = 0.0f;
598 
599 	int hlod = lod >> 1;
600 	int dlod = lod << 1;
601 
602 	int cx = (int)cx2;
603 	int cy = (int)cy2;
604 	if (lod > 1) {
605 		int cxo = (cx / hlod) * hlod;
606 		int cyo = (cy / hlod) * hlod;
607 		float cx2o = (cxo / lod) * lod;
608 		float cy2o = (cyo / lod) * lod;
609 		oldcamxpart = (cx2 - cx2o) / lod;
610 		oldcamypart = (cy2 - cy2o) / lod;
611 	}
612 
613 	cx = (cx / lod) * lod;
614 	cy = (cy / lod) * lod;
615 	const int ysquaremod = (cy % dlod) / lod;
616 	const int xsquaremod = (cx % dlod) / lod;
617 
618 	const float camxpart = (cx2 - (cx / dlod) * dlod) / dlod;
619 	const float camypart = (cy2 - (cy / dlod) * dlod) / dlod;
620 
621 	const int minty = 0, maxty = gs->mapy;
622 	const int mintx = 0, maxtx = gs->mapx;
623 
624 	const int minly = cy + (-viewRadius + 3 - ysquaremod) * lod, maxly = cy + ( viewRadius - 1 - ysquaremod) * lod;
625 	const int minlx = cx + (-viewRadius + 3 - xsquaremod) * lod, maxlx = cx + ( viewRadius - 1 - xsquaremod) * lod;
626 
627 	const int xstart = std::max(minlx, mintx), xend   = std::min(maxlx, maxtx);
628 	const int ystart = std::max(minly, minty), yend   = std::min(maxly, maxty);
629 
630 	const int lhdx = lod * smfReadMap->heightMapSizeX;
631 	const int hhdx = hlod * smfReadMap->heightMapSizeX;
632 	const int dhdx = dlod * smfReadMap->heightMapSizeX;
633 
634 	const float mcxp  = 1.0f - camxpart, mcyp  = 1.0f - camypart;
635 	const float hcxp  = 0.5f * camxpart, hcyp  = 0.5f * camypart;
636 	const float hmcxp = 0.5f * mcxp,     hmcyp = 0.5f * mcyp;
637 
638 	const float mocxp  = 1.0f - oldcamxpart, mocyp  = 1.0f - oldcamypart;
639 	const float hocxp  = 0.5f * oldcamxpart, hocyp  = 0.5f * oldcamypart;
640 	const float hmocxp = 0.5f * mocxp,       hmocyp = 0.5f * mocyp;
641 
642 	const int vrhlod = viewRadius * hlod;
643 
644 	for (y = ystart; y < yend; y += lod) {
645 		int xs = xstart;
646 		int xe = xend;
647 
648 		if (xe < xs) continue;
649 
650 		int ylod = y + lod;
651 		int yhlod = y + hlod;
652 		int ydx = y * smfReadMap->heightMapSizeX;
653 		int nloop = (xe - xs) / lod + 1;
654 
655 		ma->EnlargeArrays(52 * nloop);
656 
657 		for (x = xs; x < xe; x += lod) {
658 			int xlod = x + lod;
659 			int xhlod = x + hlod;
660 			if ((lod == 1) ||
661 				(x > cx + vrhlod) || (x < cx - vrhlod) ||
662 				(y > cy + vrhlod) || (y < cy - vrhlod)) {
663 					if (!inStrip) {
664 						DrawVertexAQ(ma, x, y   );
665 						DrawVertexAQ(ma, x, ylod);
666 						inStrip = true;
667 					}
668 					DrawVertexAQ(ma, xlod, y   );
669 					DrawVertexAQ(ma, xlod, ylod);
670 			}
671 			else {  //! inre begr?sning mot f?eg?nde lod
672 				int yhdx=ydx+x;
673 				int ylhdx=yhdx+lhdx;
674 				int yhhdx=yhdx+hhdx;
675 
676 				if ( x>= cx + vrhlod) {
677 					const float h1 = (GetVisibleVertexHeight(yhdx ) + GetVisibleVertexHeight(ylhdx    )) * hmocxp + GetVisibleVertexHeight(yhhdx     ) * oldcamxpart;
678 					const float h2 = (GetVisibleVertexHeight(yhdx ) + GetVisibleVertexHeight(yhdx+lod )) * hmocxp + GetVisibleVertexHeight(yhdx+hlod ) * oldcamxpart;
679 					const float h3 = (GetVisibleVertexHeight(ylhdx) + GetVisibleVertexHeight(yhdx+lod )) * hmocxp + GetVisibleVertexHeight(yhhdx+hlod) * oldcamxpart;
680 					const float h4 = (GetVisibleVertexHeight(ylhdx) + GetVisibleVertexHeight(ylhdx+lod)) * hmocxp + GetVisibleVertexHeight(ylhdx+hlod) * oldcamxpart;
681 
682 					if(inStrip){
683 						ma->EndStrip();
684 						inStrip=false;
685 					}
686 					DrawVertexAQ(ma, x,y);
687 					DrawVertexAQ(ma, x,yhlod,h1);
688 					DrawVertexAQ(ma, xhlod,y,h2);
689 					DrawVertexAQ(ma, xhlod,yhlod,h3);
690 					ma->EndStrip();
691 					DrawVertexAQ(ma, x,yhlod,h1);
692 					DrawVertexAQ(ma, x,ylod);
693 					DrawVertexAQ(ma, xhlod,yhlod,h3);
694 					DrawVertexAQ(ma, xhlod,ylod,h4);
695 					ma->EndStrip();
696 					DrawVertexAQ(ma, xhlod,ylod,h4);
697 					DrawVertexAQ(ma, xlod,ylod);
698 					DrawVertexAQ(ma, xhlod,yhlod,h3);
699 					DrawVertexAQ(ma, xlod,y);
700 					DrawVertexAQ(ma, xhlod,y,h2);
701 					ma->EndStrip();
702 				}
703 				if (x <= cx - vrhlod) {
704 					const float h1 = (GetVisibleVertexHeight(yhdx+lod) + GetVisibleVertexHeight(ylhdx+lod)) * hocxp + GetVisibleVertexHeight(yhhdx+lod ) * mocxp;
705 					const float h2 = (GetVisibleVertexHeight(yhdx    ) + GetVisibleVertexHeight(yhdx+lod )) * hocxp + GetVisibleVertexHeight(yhdx+hlod ) * mocxp;
706 					const float h3 = (GetVisibleVertexHeight(ylhdx   ) + GetVisibleVertexHeight(yhdx+lod )) * hocxp + GetVisibleVertexHeight(yhhdx+hlod) * mocxp;
707 					const float h4 = (GetVisibleVertexHeight(ylhdx   ) + GetVisibleVertexHeight(ylhdx+lod)) * hocxp + GetVisibleVertexHeight(ylhdx+hlod) * mocxp;
708 
709 					if(inStrip){
710 						ma->EndStrip();
711 						inStrip=false;
712 					}
713 					DrawVertexAQ(ma, xlod,yhlod,h1);
714 					DrawVertexAQ(ma, xlod,y);
715 					DrawVertexAQ(ma, xhlod,yhlod,h3);
716 					DrawVertexAQ(ma, xhlod,y,h2);
717 					ma->EndStrip();
718 					DrawVertexAQ(ma, xlod,ylod);
719 					DrawVertexAQ(ma, xlod,yhlod,h1);
720 					DrawVertexAQ(ma, xhlod,ylod,h4);
721 					DrawVertexAQ(ma, xhlod,yhlod,h3);
722 					ma->EndStrip();
723 					DrawVertexAQ(ma, xhlod,y,h2);
724 					DrawVertexAQ(ma, x,y);
725 					DrawVertexAQ(ma, xhlod,yhlod,h3);
726 					DrawVertexAQ(ma, x,ylod);
727 					DrawVertexAQ(ma, xhlod,ylod,h4);
728 					ma->EndStrip();
729 				}
730 				if (y >= cy + vrhlod) {
731 					const float h1 = (GetVisibleVertexHeight(yhdx     ) + GetVisibleVertexHeight(yhdx+lod)) * hmocyp + GetVisibleVertexHeight(yhdx+hlod ) * oldcamypart;
732 					const float h2 = (GetVisibleVertexHeight(yhdx     ) + GetVisibleVertexHeight(ylhdx   )) * hmocyp + GetVisibleVertexHeight(yhhdx     ) * oldcamypart;
733 					const float h3 = (GetVisibleVertexHeight(ylhdx    ) + GetVisibleVertexHeight(yhdx+lod)) * hmocyp + GetVisibleVertexHeight(yhhdx+hlod) * oldcamypart;
734 					const float h4 = (GetVisibleVertexHeight(ylhdx+lod) + GetVisibleVertexHeight(yhdx+lod)) * hmocyp + GetVisibleVertexHeight(yhhdx+lod ) * oldcamypart;
735 
736 					if(inStrip){
737 						ma->EndStrip();
738 						inStrip=false;
739 					}
740 					DrawVertexAQ(ma, x,y);
741 					DrawVertexAQ(ma, x,yhlod,h2);
742 					DrawVertexAQ(ma, xhlod,y,h1);
743 					DrawVertexAQ(ma, xhlod,yhlod,h3);
744 					DrawVertexAQ(ma, xlod,y);
745 					DrawVertexAQ(ma, xlod,yhlod,h4);
746 					ma->EndStrip();
747 					DrawVertexAQ(ma, x,yhlod,h2);
748 					DrawVertexAQ(ma, x,ylod);
749 					DrawVertexAQ(ma, xhlod,yhlod,h3);
750 					DrawVertexAQ(ma, xlod,ylod);
751 					DrawVertexAQ(ma, xlod,yhlod,h4);
752 					ma->EndStrip();
753 				}
754 				if (y <= cy - vrhlod) {
755 					const float h1 = (GetVisibleVertexHeight(ylhdx    ) + GetVisibleVertexHeight(ylhdx+lod)) * hocyp + GetVisibleVertexHeight(ylhdx+hlod) * mocyp;
756 					const float h2 = (GetVisibleVertexHeight(yhdx     ) + GetVisibleVertexHeight(ylhdx    )) * hocyp + GetVisibleVertexHeight(yhhdx     ) * mocyp;
757 					const float h3 = (GetVisibleVertexHeight(ylhdx    ) + GetVisibleVertexHeight(yhdx+lod )) * hocyp + GetVisibleVertexHeight(yhhdx+hlod) * mocyp;
758 					const float h4 = (GetVisibleVertexHeight(ylhdx+lod) + GetVisibleVertexHeight(yhdx+lod )) * hocyp + GetVisibleVertexHeight(yhhdx+lod ) * mocyp;
759 
760 					if (inStrip) {
761 						ma->EndStrip();
762 						inStrip = false;
763 					}
764 					DrawVertexAQ(ma, x,yhlod,h2);
765 					DrawVertexAQ(ma, x,ylod);
766 					DrawVertexAQ(ma, xhlod,yhlod,h3);
767 					DrawVertexAQ(ma, xhlod,ylod,h1);
768 					DrawVertexAQ(ma, xlod,yhlod,h4);
769 					DrawVertexAQ(ma, xlod,ylod);
770 					ma->EndStrip();
771 					DrawVertexAQ(ma, xlod,yhlod,h4);
772 					DrawVertexAQ(ma, xlod,y);
773 					DrawVertexAQ(ma, xhlod,yhlod,h3);
774 					DrawVertexAQ(ma, x,y);
775 					DrawVertexAQ(ma, x,yhlod,h2);
776 					ma->EndStrip();
777 				}
778 			}
779 		}
780 		if (inStrip) {
781 			ma->EndStrip();
782 			inStrip=false;
783 		}
784 	}
785 
786 	int yst = std::max(ystart - lod, minty);
787 	int yed = std::min(yend + lod, maxty);
788 	int nloop = (yed - yst) / lod + 1;
789 	ma->EnlargeArrays(8 * nloop);
790 
791 	//!rita yttre begr?snings yta mot n?ta lod
792 	if (maxlx < maxtx && maxlx >= mintx) {
793 		x = maxlx;
794 		const int xlod = x + lod;
795 
796 		for (y = yst; y < yed; y += lod) {
797 			DrawVertexAQ(ma, x, y      );
798 			DrawVertexAQ(ma, x, y + lod);
799 			const int yhdx = y * smfReadMap->heightMapSizeX + x;
800 
801 			if (y % dlod) {
802 				const float h = (GetVisibleVertexHeight(yhdx - lhdx + lod) + GetVisibleVertexHeight(yhdx + lhdx + lod)) * hmcxp + GetVisibleVertexHeight(yhdx+lod) * camxpart;
803 				DrawVertexAQ(ma, xlod, y, h);
804 				DrawVertexAQ(ma, xlod, y + lod);
805 			} else {
806 				const float h = (GetVisibleVertexHeight(yhdx+lod) + GetVisibleVertexHeight(yhdx+dhdx+lod)) * hmcxp + GetVisibleVertexHeight(yhdx+lhdx+lod) * camxpart;
807 				DrawVertexAQ(ma, xlod,y);
808 				DrawVertexAQ(ma, xlod,y+lod,h);
809 			}
810 			ma->EndStrip();
811 		}
812 	}
813 
814 	if (minlx > mintx && minlx < maxtx) {
815 		x = minlx - lod;
816 		const int xlod = x + lod;
817 
818 		for(y = yst; y < yed; y += lod) {
819 			int yhdx = y * smfReadMap->heightMapSizeX + x;
820 			if(y%dlod){
821 				const float h = (GetVisibleVertexHeight(yhdx-lhdx) + GetVisibleVertexHeight(yhdx+lhdx)) * hcxp + GetVisibleVertexHeight(yhdx) * mcxp;
822 				DrawVertexAQ(ma, x,y,h);
823 				DrawVertexAQ(ma, x,y+lod);
824 			} else {
825 				const float h = (GetVisibleVertexHeight(yhdx) + GetVisibleVertexHeight(yhdx+dhdx)) * hcxp + GetVisibleVertexHeight(yhdx+lhdx) * mcxp;
826 				DrawVertexAQ(ma, x,y);
827 				DrawVertexAQ(ma, x,y+lod,h);
828 			}
829 			DrawVertexAQ(ma, xlod,y);
830 			DrawVertexAQ(ma, xlod,y+lod);
831 			ma->EndStrip();
832 		}
833 	}
834 	if (maxly < maxty && maxly > minty) {
835 		y = maxly;
836 		const int xs = std::max(xstart -lod, mintx);
837 		const int xe = std::min(xend + lod, maxtx);
838 
839 		if (xs < xe) {
840 			x = xs;
841 			const int ylod = y + lod;
842 			const int ydx = y * smfReadMap->heightMapSizeX;
843 			const int nloop = (xe - xs) / lod + 2; //! two extra for if statment
844 
845 			ma->EnlargeArrays(2 * nloop);
846 
847 			if (x % dlod) {
848 				const int ylhdx = ydx + x + lhdx;
849 				const float h = (GetVisibleVertexHeight(ylhdx-lod) + GetVisibleVertexHeight(ylhdx+lod)) * hmcyp + GetVisibleVertexHeight(ylhdx) * camypart;
850 				DrawVertexAQ(ma, x, y);
851 				DrawVertexAQ(ma, x, ylod, h);
852 			} else {
853 				DrawVertexAQ(ma, x, y);
854 				DrawVertexAQ(ma, x, ylod);
855 			}
856 
857 			for (x = xs; x < xe; x += lod) {
858 				if (x % dlod) {
859 					DrawVertexAQ(ma, x + lod, y);
860 					DrawVertexAQ(ma, x + lod, ylod);
861 				} else {
862 					DrawVertexAQ(ma, x+lod,y);
863 					const int ylhdx = ydx + x + lhdx;
864 					const float h = (GetVisibleVertexHeight(ylhdx+dlod) + GetVisibleVertexHeight(ylhdx)) * hmcyp + GetVisibleVertexHeight(ylhdx+lod) * camypart;
865 					DrawVertexAQ(ma, x+lod,ylod,h);
866 				}
867 			}
868 			ma->EndStrip();
869 		}
870 	}
871 	if (minly > minty && minly < maxty) {
872 		y = minly - lod;
873 		const int xs = std::max(xstart - lod, mintx);
874 		const int xe = std::min(xend + lod, maxtx);
875 
876 		if (xs < xe) {
877 			x = xs;
878 			const int ylod = y + lod;
879 			const int ydx = y * smfReadMap->heightMapSizeX;
880 			const int nloop = (xe - xs) / lod + 2; //! two extra for if statment
881 
882 			ma->EnlargeArrays(2 * nloop);
883 
884 			if (x % dlod) {
885 				const int yhdx = ydx + x;
886 				const float h = (GetVisibleVertexHeight(yhdx-lod) + GetVisibleVertexHeight(yhdx + lod)) * hcyp + GetVisibleVertexHeight(yhdx) * mcyp;
887 				DrawVertexAQ(ma, x, y, h);
888 				DrawVertexAQ(ma, x, ylod);
889 			} else {
890 				DrawVertexAQ(ma, x, y);
891 				DrawVertexAQ(ma, x, ylod);
892 			}
893 
894 			for (x = xs; x < xe; x += lod) {
895 				if (x % dlod) {
896 					DrawVertexAQ(ma, x + lod, y);
897 					DrawVertexAQ(ma, x + lod, ylod);
898 				} else {
899 					const int yhdx = ydx + x;
900 					const float h = (GetVisibleVertexHeight(yhdx+dlod) + GetVisibleVertexHeight(yhdx)) * hcyp + GetVisibleVertexHeight(yhdx+lod) * mcyp;
901 					DrawVertexAQ(ma, x + lod, y, h);
902 					DrawVertexAQ(ma, x + lod, ylod);
903 				}
904 			}
905 			ma->EndStrip();
906 		}
907 	}
908 	DrawGroundVertexArrayQ(ma);
909 }
910 
911 
DrawShadowMesh()912 void CLegacyMeshDrawer::DrawShadowMesh()
913 {
914 	{ // profiler scope
915 		const int NUM_LODS = 4;
916 		{
917 			for (int nlod = 0; nlod < NUM_LODS + 1; ++nlod) {
918 				DoDrawGroundShadowLOD(nlod);
919 			}
920 		}
921 	}
922 }
923