1 /************************************************************************************
2 
3 	AstroMenace
4 	Hardcore 3D space scroll-shooter with spaceship upgrade possibilities.
5 	Copyright (c) 2006-2019 Mikhail Kurinnoi, Viewizard
6 
7 
8 	AstroMenace is free software: you can redistribute it and/or modify
9 	it under the terms of the GNU General Public License as published by
10 	the Free Software Foundation, either version 3 of the License, or
11 	(at your option) any later version.
12 
13 	AstroMenace is distributed in the hope that it will be useful,
14 	but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 	GNU General Public License for more details.
17 
18 	You should have received a copy of the GNU General Public License
19 	along with AstroMenace. If not, see <https://www.gnu.org/licenses/>.
20 
21 
22 	Website: https://viewizard.com/
23 	Project: https://github.com/viewizard/astromenace
24 	E-mail: viewizard@viewizard.com
25 
26 *************************************************************************************/
27 
28 // TODO provide more rendering optimization in DrawObjectStatus()
29 
30 // TODO codestyle should be fixed
31 
32 #include "object3d.h"
33 #include "../config/config.h"
34 #include "../gfx/shadow_map.h"
35 #include "../script/script.h"
36 #include "../game.h" // FIXME "game.h" should be replaced by individual headers
37 
38 // NOTE switch to nested namespace definition (namespace A::B::C { ... }) (since C++17)
39 namespace viewizard {
40 namespace astromenace {
41 
42 namespace {
43 
44 eRenderBoundingBoxes BBRenderMode{eRenderBoundingBoxes::None};
45 
46 } // unnamed namespace
47 
48 // FIXME should be fixed, don't allow global scope interaction for local variables
49 extern std::weak_ptr<cGLSL> GLSLShaderType1;
50 extern std::weak_ptr<cGLSL> GLSLShaderType2;
51 extern std::weak_ptr<cGLSL> GLSLShaderType3;
52 
53 
54 /*
55  * Set chunk location.
56  */
SetChunkLocation(const sVECTOR3D & NewLocation,unsigned ChunkNum)57 void cObject3D::SetChunkLocation(const sVECTOR3D &NewLocation, unsigned ChunkNum)
58 {
59 	if (!HitBB.empty()) {
60 		float OldInvRotationMatTmp[9];
61 		memcpy(OldInvRotationMatTmp, CurrentRotationMat, 9 * sizeof(CurrentRotationMat[0]));
62 		vw_Matrix33InverseRotate(OldInvRotationMatTmp);
63 
64 		vw_Matrix33CalcPoint(HitBB[ChunkNum].Location, OldInvRotationMatTmp);
65 		HitBB[ChunkNum].Location -= Chunks[ChunkNum].Location - NewLocation;
66 		vw_Matrix33CalcPoint(HitBB[ChunkNum].Location, CurrentRotationMat);
67 
68 		float MinX = 10000.0f;
69 		float MaxX = -10000.0f;
70 		float MinY = 10000.0f;
71 		float MaxY = -10000.0f;
72 		float MinZ = 10000.0f;
73 		float MaxZ = -10000.0f;
74 
75 		for (unsigned int i = 0; i < Chunks.size(); i++) {
76 			vw_Matrix33CalcPoint(HitBB[i].Location, OldInvRotationMatTmp);
77 
78 			for (int j = 0; j < 8; j++) {
79 				vw_Matrix33CalcPoint(HitBB[i].Box[j], OldInvRotationMatTmp);
80 
81 				if (MinX > HitBB[i].Box[j].x + HitBB[i].Location.x)
82 					MinX = HitBB[i].Box[j].x + HitBB[i].Location.x;
83 				if (MaxX < HitBB[i].Box[j].x + HitBB[i].Location.x)
84 					MaxX = HitBB[i].Box[j].x + HitBB[i].Location.x;
85 				if (MinY > HitBB[i].Box[j].y + HitBB[i].Location.y)
86 					MinY = HitBB[i].Box[j].y + HitBB[i].Location.y;
87 				if (MaxY < HitBB[i].Box[j].y + HitBB[i].Location.y)
88 					MaxY = HitBB[i].Box[j].y + HitBB[i].Location.y;
89 				if (MinZ > HitBB[i].Box[j].z + HitBB[i].Location.z)
90 					MinZ = HitBB[i].Box[j].z + HitBB[i].Location.z;
91 				if (MaxZ < HitBB[i].Box[j].z + HitBB[i].Location.z)
92 					MaxZ = HitBB[i].Box[j].z + HitBB[i].Location.z;
93 
94 				vw_Matrix33CalcPoint(HitBB[i].Box[j], CurrentRotationMat);
95 			}
96 			vw_Matrix33CalcPoint(HitBB[i].Location, CurrentRotationMat);
97 		}
98 
99 		OBB.Box[0] = sVECTOR3D{MaxX, MaxY, MaxZ};
100 		OBB.Box[1] = sVECTOR3D{MinX, MaxY, MaxZ};
101 		OBB.Box[2] = sVECTOR3D{MinX, MaxY, MinZ};
102 		OBB.Box[3] = sVECTOR3D{MaxX, MaxY, MinZ};
103 		OBB.Box[4] = sVECTOR3D{MaxX, MinY, MaxZ};
104 		OBB.Box[5] = sVECTOR3D{MinX, MinY, MaxZ};
105 		OBB.Box[6] = sVECTOR3D{MinX, MinY, MinZ};
106 		OBB.Box[7] = sVECTOR3D{MaxX, MinY, MinZ};
107 
108 		Width = fabsf(MaxX - MinX);
109 		Height = fabsf(MaxY - MinY);
110 		Length = fabsf(MaxZ - MinZ);
111 
112 		float Width2 = Width / 2.0f;
113 		float Length2 = Length / 2.0f;
114 		float Height2 = Height / 2.0f;
115 		Radius = vw_sqrtf(Width2 * Width2 + Length2 * Length2 + Height2 * Height2);
116 
117 		OBB.Location.x = (MaxX + MinX) / 2.0f;
118 		OBB.Location.y = (MaxY + MinY) / 2.0f;
119 		OBB.Location.z = (MaxZ + MinZ) / 2.0f;
120 
121 		for (int k = 0; k < 8; k++) {
122 			OBB.Box[k] -= OBB.Location;
123 			vw_Matrix33CalcPoint(OBB.Box[k], CurrentRotationMat);
124 		}
125 		vw_Matrix33CalcPoint(OBB.Location, CurrentRotationMat);
126 
127 		MinX = MaxX = OBB.Box[0].x + OBB.Location.x;
128 		MinY = MaxY = OBB.Box[0].y + OBB.Location.y;
129 		MinZ = MaxZ = OBB.Box[0].z + OBB.Location.z;
130 		for (int j = 0; j < 8; j++) {
131 			if (MinX > OBB.Box[j].x + OBB.Location.x)
132 				MinX = OBB.Box[j].x + OBB.Location.x;
133 			if (MinY > OBB.Box[j].y + OBB.Location.y)
134 				MinY = OBB.Box[j].y + OBB.Location.y;
135 			if (MinZ > OBB.Box[j].z + OBB.Location.z)
136 				MinZ = OBB.Box[j].z + OBB.Location.z;
137 			if (MaxX < OBB.Box[j].x + OBB.Location.x)
138 				MaxX = OBB.Box[j].x + OBB.Location.x;
139 			if (MaxY < OBB.Box[j].y + OBB.Location.y)
140 				MaxY = OBB.Box[j].y + OBB.Location.y;
141 			if (MaxZ < OBB.Box[j].z + OBB.Location.z)
142 				MaxZ = OBB.Box[j].z + OBB.Location.z;
143 		}
144 		AABB[0] = sVECTOR3D{MaxX, MaxY, MaxZ};
145 		AABB[1] = sVECTOR3D{MinX, MaxY, MaxZ};
146 		AABB[2] = sVECTOR3D{MinX, MaxY, MinZ};
147 		AABB[3] = sVECTOR3D{MaxX, MaxY, MinZ};
148 		AABB[4] = sVECTOR3D{MaxX, MinY, MaxZ};
149 		AABB[5] = sVECTOR3D{MinX, MinY, MaxZ};
150 		AABB[6] = sVECTOR3D{MinX, MinY, MinZ};
151 		AABB[7] = sVECTOR3D{MaxX, MinY, MinZ};
152 	}
153 
154 	Chunks[ChunkNum].Location = NewLocation;
155 }
156 
157 /*
158  * Set chunk rotation.
159  */
SetChunkRotation(const sVECTOR3D & NewRotation,unsigned ChunkNum)160 void cObject3D::SetChunkRotation(const sVECTOR3D &NewRotation, unsigned ChunkNum)
161 {
162 	if (!HitBB.empty()) {
163 		float CurrentRotationMatTmp2[9];
164 		vw_Matrix33CreateRotate(CurrentRotationMatTmp2, NewRotation);
165 
166 		float OldInvRotationMatTmp2[9];
167 		vw_Matrix33CreateRotate(OldInvRotationMatTmp2, Chunks[ChunkNum].Rotation);
168 		vw_Matrix33InverseRotate(OldInvRotationMatTmp2);
169 
170 		float OldInvRotationMatTmp[9];
171 		memcpy(OldInvRotationMatTmp, CurrentRotationMat, 9 * sizeof(CurrentRotationMat[0]));
172 		vw_Matrix33InverseRotate(OldInvRotationMatTmp);
173 
174 		vw_Matrix33CalcPoint(HitBB[ChunkNum].Location, OldInvRotationMatTmp);
175 		HitBB[ChunkNum].Location -= Chunks[ChunkNum].Location;
176 		vw_Matrix33CalcPoint(HitBB[ChunkNum].Location, OldInvRotationMatTmp2);
177 		vw_Matrix33CalcPoint(HitBB[ChunkNum].Location, CurrentRotationMatTmp2);
178 		HitBB[ChunkNum].Location += Chunks[ChunkNum].Location;
179 		vw_Matrix33CalcPoint(HitBB[ChunkNum].Location, CurrentRotationMat);
180 		for (int j = 0; j < 8; j++) {
181 			vw_Matrix33CalcPoint(HitBB[ChunkNum].Box[j], OldInvRotationMatTmp);
182 			vw_Matrix33CalcPoint(HitBB[ChunkNum].Box[j], OldInvRotationMatTmp2);
183 			vw_Matrix33CalcPoint(HitBB[ChunkNum].Box[j], CurrentRotationMatTmp2);
184 			vw_Matrix33CalcPoint(HitBB[ChunkNum].Box[j], CurrentRotationMat);
185 		}
186 
187 		float MinX = 10000.0f;
188 		float MaxX = -10000.0f;
189 		float MinY = 10000.0f;
190 		float MaxY = -10000.0f;
191 		float MinZ = 10000.0f;
192 		float MaxZ = -10000.0f;
193 
194 		for (unsigned int i = 0; i < Chunks.size(); i++) {
195 			vw_Matrix33CalcPoint(HitBB[i].Location, OldInvRotationMatTmp);
196 
197 			for (int j = 0; j < 8; j++) {
198 				vw_Matrix33CalcPoint(HitBB[i].Box[j], OldInvRotationMatTmp);
199 
200 				if (MinX > HitBB[i].Box[j].x + HitBB[i].Location.x)
201 					MinX = HitBB[i].Box[j].x + HitBB[i].Location.x;
202 				if (MaxX < HitBB[i].Box[j].x + HitBB[i].Location.x)
203 					MaxX = HitBB[i].Box[j].x + HitBB[i].Location.x;
204 				if (MinY > HitBB[i].Box[j].y + HitBB[i].Location.y)
205 					MinY = HitBB[i].Box[j].y + HitBB[i].Location.y;
206 				if (MaxY < HitBB[i].Box[j].y + HitBB[i].Location.y)
207 					MaxY = HitBB[i].Box[j].y + HitBB[i].Location.y;
208 				if (MinZ > HitBB[i].Box[j].z + HitBB[i].Location.z)
209 					MinZ = HitBB[i].Box[j].z + HitBB[i].Location.z;
210 				if (MaxZ < HitBB[i].Box[j].z + HitBB[i].Location.z)
211 					MaxZ = HitBB[i].Box[j].z + HitBB[i].Location.z;
212 
213 				vw_Matrix33CalcPoint(HitBB[i].Box[j], CurrentRotationMat);
214 			}
215 			vw_Matrix33CalcPoint(HitBB[i].Location, CurrentRotationMat);
216 		}
217 
218 		OBB.Box[0] = sVECTOR3D{MaxX, MaxY, MaxZ};
219 		OBB.Box[1] = sVECTOR3D{MinX, MaxY, MaxZ};
220 		OBB.Box[2] = sVECTOR3D{MinX, MaxY, MinZ};
221 		OBB.Box[3] = sVECTOR3D{MaxX, MaxY, MinZ};
222 		OBB.Box[4] = sVECTOR3D{MaxX, MinY, MaxZ};
223 		OBB.Box[5] = sVECTOR3D{MinX, MinY, MaxZ};
224 		OBB.Box[6] = sVECTOR3D{MinX, MinY, MinZ};
225 		OBB.Box[7] = sVECTOR3D{MaxX, MinY, MinZ};
226 
227 		Width = fabsf(MaxX - MinX);
228 		Height = fabsf(MaxY - MinY);
229 		Length = fabsf(MaxZ - MinZ);
230 
231 		float Width2 = Width / 2.0f;
232 		float Length2 = Length / 2.0f;
233 		float Height2 = Height / 2.0f;
234 		Radius = vw_sqrtf(Width2 * Width2 + Length2 * Length2 + Height2 * Height2);
235 
236 		OBB.Location.x = (MaxX + MinX) / 2.0f;
237 		OBB.Location.y = (MaxY + MinY) / 2.0f;
238 		OBB.Location.z = (MaxZ + MinZ) / 2.0f;
239 
240 		for (int k = 0; k < 8; k++) {
241 			OBB.Box[k] -= OBB.Location;
242 			vw_Matrix33CalcPoint(OBB.Box[k], CurrentRotationMat);
243 		}
244 		vw_Matrix33CalcPoint(OBB.Location, CurrentRotationMat);
245 
246 		MinX = MaxX = OBB.Box[0].x + OBB.Location.x;
247 		MinY = MaxY = OBB.Box[0].y + OBB.Location.y;
248 		MinZ = MaxZ = OBB.Box[0].z + OBB.Location.z;
249 		for (int j = 0; j < 8; j++) {
250 			if (MinX > OBB.Box[j].x + OBB.Location.x)
251 				MinX = OBB.Box[j].x + OBB.Location.x;
252 			if (MinY > OBB.Box[j].y + OBB.Location.y)
253 				MinY = OBB.Box[j].y + OBB.Location.y;
254 			if (MinZ > OBB.Box[j].z + OBB.Location.z)
255 				MinZ = OBB.Box[j].z + OBB.Location.z;
256 			if (MaxX < OBB.Box[j].x + OBB.Location.x)
257 				MaxX = OBB.Box[j].x + OBB.Location.x;
258 			if (MaxY < OBB.Box[j].y + OBB.Location.y)
259 				MaxY = OBB.Box[j].y + OBB.Location.y;
260 			if (MaxZ < OBB.Box[j].z + OBB.Location.z)
261 				MaxZ = OBB.Box[j].z + OBB.Location.z;
262 		}
263 		AABB[0] = sVECTOR3D{MaxX, MaxY, MaxZ};
264 		AABB[1] = sVECTOR3D{MinX, MaxY, MaxZ};
265 		AABB[2] = sVECTOR3D{MinX, MaxY, MinZ};
266 		AABB[3] = sVECTOR3D{MaxX, MaxY, MinZ};
267 		AABB[4] = sVECTOR3D{MaxX, MinY, MaxZ};
268 		AABB[5] = sVECTOR3D{MinX, MinY, MaxZ};
269 		AABB[6] = sVECTOR3D{MinX, MinY, MinZ};
270 		AABB[7] = sVECTOR3D{MaxX, MinY, MinZ};
271 	}
272 
273 	Chunks[ChunkNum].Rotation = NewRotation;
274 }
275 
276 /*
277  * Set location.
278  */
SetLocation(const sVECTOR3D & NewLocation)279 void cObject3D::SetLocation(const sVECTOR3D &NewLocation)
280 {
281 	PrevLocation = Location;
282 	Location = NewLocation;
283 }
284 
285 /*
286  * Set rotation.
287  */
SetRotation(const sVECTOR3D & NewRotation)288 void cObject3D::SetRotation(const sVECTOR3D &NewRotation)
289 {
290 	OldRotationInv = sVECTOR3D{-Rotation.x, -Rotation.y, -Rotation.z};
291 	Rotation += NewRotation;
292 
293 	memcpy(OldInvRotationMat, CurrentRotationMat, 9 * sizeof(CurrentRotationMat[0]));
294 	vw_Matrix33InverseRotate(OldInvRotationMat);
295 	vw_Matrix33CreateRotate(CurrentRotationMat, Rotation);
296 
297 	vw_Matrix33CalcPoint(Orientation, OldInvRotationMat);
298 	vw_Matrix33CalcPoint(Orientation, CurrentRotationMat);
299 
300 	if (!HitBB.empty()) {
301 		for (unsigned int i = 0; i < Chunks.size(); i++) {
302 			vw_Matrix33CalcPoint(HitBB[i].Location, OldInvRotationMat);
303 			vw_Matrix33CalcPoint(HitBB[i].Location, CurrentRotationMat);
304 
305 			for (int j = 0; j < 8; j++) {
306 				vw_Matrix33CalcPoint(HitBB[i].Box[j], OldInvRotationMat);
307 				vw_Matrix33CalcPoint(HitBB[i].Box[j], CurrentRotationMat);
308 			}
309 		}
310 	}
311 
312 	vw_Matrix33CalcPoint(OBB.Location, OldInvRotationMat);
313 	vw_Matrix33CalcPoint(OBB.Location, CurrentRotationMat);
314 	for (int j = 0; j < 8; j++) {
315 		vw_Matrix33CalcPoint(OBB.Box[j], OldInvRotationMat);
316 		vw_Matrix33CalcPoint(OBB.Box[j], CurrentRotationMat);
317 	}
318 
319 	float MinX = 10000.0f;
320 	float MaxX = -10000.0f;
321 	float MinY = 10000.0f;
322 	float MaxY = -10000.0f;
323 	float MinZ = 10000.0f;
324 	float MaxZ = -10000.0f;
325 	for (int j = 0; j < 8; j++) {
326 		if (MinX > OBB.Box[j].x + OBB.Location.x)
327 			MinX = OBB.Box[j].x + OBB.Location.x;
328 		if (MinY > OBB.Box[j].y + OBB.Location.y)
329 			MinY = OBB.Box[j].y + OBB.Location.y;
330 		if (MinZ > OBB.Box[j].z + OBB.Location.z)
331 			MinZ = OBB.Box[j].z + OBB.Location.z;
332 		if (MaxX < OBB.Box[j].x + OBB.Location.x)
333 			MaxX = OBB.Box[j].x + OBB.Location.x;
334 		if (MaxY < OBB.Box[j].y + OBB.Location.y)
335 			MaxY = OBB.Box[j].y + OBB.Location.y;
336 		if (MaxZ < OBB.Box[j].z + OBB.Location.z)
337 			MaxZ = OBB.Box[j].z + OBB.Location.z;
338 	}
339 	AABB[0] = sVECTOR3D{MaxX, MaxY, MaxZ};
340 	AABB[1] = sVECTOR3D{MinX, MaxY, MaxZ};
341 	AABB[2] = sVECTOR3D{MinX, MaxY, MinZ};
342 	AABB[3] = sVECTOR3D{MaxX, MaxY, MinZ};
343 	AABB[4] = sVECTOR3D{MaxX, MinY, MaxZ};
344 	AABB[5] = sVECTOR3D{MinX, MinY, MaxZ};
345 	AABB[6] = sVECTOR3D{MinX, MinY, MinZ};
346 	AABB[7] = sVECTOR3D{MaxX, MinY, MinZ};
347 
348 }
349 
350 /*
351  * Draw line.
352  * Since this is debug only code, no need in future rendering optimization (vbo/ibo/vao, etc...).
353  */
DrawLine(const sVECTOR3D & Point1,const sVECTOR3D & Point2,const sRGBCOLOR & Color)354 static void DrawLine(const sVECTOR3D &Point1, const sVECTOR3D &Point2, const sRGBCOLOR &Color)
355 {
356 	constexpr float Alpha{1.0f};
357 
358 	// 2 points for line with RI_3f_XYZ | RI_4f_COLOR = 2 * (3 + 4) = 14
359 	float DrawArray[14]{Point1.x, Point1.y, Point1.z, Color.r, Color.g, Color.b, Alpha,
360 			    Point2.x, Point2.y, Point2.z, Color.r, Color.g, Color.b, Alpha};
361 
362 	vw_Draw3D(ePrimitiveType::LINES, 2, RI_3f_XYZ | RI_4f_COLOR, DrawArray, 7 * sizeof(DrawArray[0]));
363 }
364 
365 /*
366  * Draw box lines.
367  * Since this is debug only code, no need in future rendering optimization (vbo/ibo/vao, etc...).
368  */
DrawBoxLines(const bounding_box & Box,const sVECTOR3D & LocalLocation,const sRGBCOLOR & Color)369 static void DrawBoxLines(const bounding_box &Box, const sVECTOR3D &LocalLocation, const sRGBCOLOR &Color)
370 {
371 	vw_PushMatrix();
372 	vw_Translate(LocalLocation);
373 
374 	DrawLine(Box[0], Box[1], Color);
375 	DrawLine(Box[1], Box[2], Color);
376 	DrawLine(Box[2], Box[3], Color);
377 	DrawLine(Box[3], Box[0], Color);
378 
379 	DrawLine(Box[4], Box[5], Color);
380 	DrawLine(Box[5], Box[6], Color);
381 	DrawLine(Box[6], Box[7], Color);
382 	DrawLine(Box[7], Box[4], Color);
383 
384 	DrawLine(Box[0], Box[4], Color);
385 	DrawLine(Box[1], Box[5], Color);
386 	DrawLine(Box[2], Box[6], Color);
387 	DrawLine(Box[3], Box[7], Color);
388 
389 	vw_PopMatrix();
390 }
391 
392 /*
393  * Draw bounding boxes.
394  */
DrawBoundingBoxes(const sVECTOR3D & Location,const bounding_box & AABB,const sOBB & OBB,const std::vector<sHitBB> & HitBB)395 static void DrawBoundingBoxes(const sVECTOR3D &Location, const bounding_box &AABB,
396 			      const sOBB &OBB, const std::vector<sHitBB> &HitBB)
397 {
398 	if (BBRenderMode == eRenderBoundingBoxes::None)
399 		return;
400 
401 	static const sRGBCOLOR Red{eRGBCOLOR::red};
402 	DrawBoxLines(AABB, Location, Red);
403 
404 	if (BBRenderMode == eRenderBoundingBoxes::AABB_Only)
405 		return;
406 
407 	static const sRGBCOLOR Green{eRGBCOLOR::green};
408 	DrawBoxLines(OBB.Box, Location + OBB.Location, Green);
409 
410 	if ((BBRenderMode == eRenderBoundingBoxes::AABB_And_OBB) || HitBB.empty())
411 		return;
412 
413 	static const sRGBCOLOR Blue{eRGBCOLOR::blue};
414 	for (const auto &tmpHitBB : HitBB) {
415 		DrawBoxLines(tmpHitBB.Box, Location + tmpHitBB.Location, Blue);
416 	}
417 }
418 
419 /*
420  * Set bounding boxes render mode.
421  */
SetObjectsBBRenderMode(eRenderBoundingBoxes Mode)422 void SetObjectsBBRenderMode(eRenderBoundingBoxes Mode)
423 {
424 	BBRenderMode = Mode;
425 }
426 
427 /*
428  * Fill status draw array for line.
429  */
FillStatusDrawArray(float SizeXStart,float SizeXEnd,float SizeY,const sRGBCOLOR & Color,float (& DrawArray)[28])430 static void FillStatusDrawArray(float SizeXStart, float SizeXEnd, float SizeY,
431 				const sRGBCOLOR &Color, float (&DrawArray)[28])
432 {
433 	unsigned int DrawArrayIndex{0};
434 	constexpr float Alpha{1.0f};
435 
436 	auto FillVertex = [&] (float X, float Y) {
437 		DrawArray[DrawArrayIndex++] = X;
438 		DrawArray[DrawArrayIndex++] = Y;
439 		DrawArray[DrawArrayIndex++] = 0.0f; // Z
440 		DrawArray[DrawArrayIndex++] = Color.r;
441 		DrawArray[DrawArrayIndex++] = Color.g;
442 		DrawArray[DrawArrayIndex++] = Color.b;
443 		DrawArray[DrawArrayIndex++] = Alpha;
444 	};
445 
446 	FillVertex(SizeXStart, -SizeY);
447 	FillVertex(SizeXStart, SizeY);
448 	FillVertex(SizeXEnd, -SizeY);
449 	FillVertex(SizeXEnd, SizeY);
450 }
451 
452 /*
453  * Draw status near 3d model in space.
454  */
DrawObjectStatus(const sVECTOR3D & Location,float Width,const sRGBCOLOR & Color,float CurrentValue,float InitialValue)455 static void DrawObjectStatus(const sVECTOR3D &Location, float Width, const sRGBCOLOR &Color,
456 			     float CurrentValue, float InitialValue)
457 {
458 	vw_PushMatrix();
459 	sVECTOR3D CurrentCameraRotation;
460 	vw_GetCameraRotation(&CurrentCameraRotation);
461 	vw_Translate(Location);
462 	vw_Rotate(-180 + CurrentCameraRotation.y, 0.0f, 1.0f, 0.0f);
463 	vw_Rotate(-CurrentCameraRotation.x, 1.0f, 0.0f, 0.0f);
464 
465 	// TRIANGLE_STRIP, RI_3f_XYZ | RI_4f_COLOR = 4 * (3 + 4) = 28
466 	float DrawArray[28];
467 
468 	// background
469 	float SizeX = Width / 2.5f + 0.1f;
470 	FillStatusDrawArray(-SizeX, SizeX, 0.35f, sRGBCOLOR{eRGBCOLOR::black}, DrawArray);
471 	vw_Draw3D(ePrimitiveType::TRIANGLE_STRIP, 4, RI_3f_XYZ | RI_4f_COLOR, DrawArray, 7 * sizeof(DrawArray[0]));
472 
473 	// status
474 	float SizeXStart = Width / 2.5f - 0.8f * Width * CurrentValue / InitialValue;
475 	float SizeXEnd = Width / 2.5f;
476 	FillStatusDrawArray(SizeXStart, SizeXEnd, 0.25f, Color, DrawArray);
477 	vw_Draw3D(ePrimitiveType::TRIANGLE_STRIP, 4, RI_3f_XYZ | RI_4f_COLOR, DrawArray, 7 * sizeof(DrawArray[0]));
478 
479 	vw_PopMatrix();
480 }
481 
482 /*
483  * Draw.
484  */
Draw(bool VertexOnlyPass,bool ShadowMap)485 void cObject3D::Draw(bool VertexOnlyPass, bool ShadowMap)
486 {
487 	if (Chunks.empty())
488 		return;
489 
490 	bool NeedOnePieceDraw{false};
491 	if (PromptDrawDist2 >= 0.0f) {
492 		sVECTOR3D CurrentCameraLocation;
493 		vw_GetCameraLocation(&CurrentCameraLocation);
494 		float PromptDrawRealDist2 = (Location.x - CurrentCameraLocation.x) * (Location.x - CurrentCameraLocation.x) +
495 					    (Location.y - CurrentCameraLocation.y) * (Location.y - CurrentCameraLocation.y) +
496 					    (Location.z - CurrentCameraLocation.z) * (Location.z - CurrentCameraLocation.z);
497 
498 		int LightsCount = vw_CalculateAllPointLightsAttenuation(Location, Radius * Radius, nullptr);
499 
500 		if (PromptDrawRealDist2 > PromptDrawDist2) {
501 			if (LightsCount <= GameConfig().MaxPointLights)
502 				NeedOnePieceDraw = true;
503 			else
504 				NeedOnePieceDraw = false;
505 
506 			if (InternalLights >= LightsCount)
507 				NeedOnePieceDraw = true;
508 		} else {
509 			if (LightsCount <= GameConfig().MaxPointLights)
510 				NeedOnePieceDraw = true;
511 		}
512 	}
513 
514 	// fast rendering for shadows map generation, without textures, shaders, etc
515 	// make sure, we call this one _before_ any camera/frustum checks, since not visible
516 	// for us 3D model could also drop the shadow on visible for us part of scene
517 	if (VertexOnlyPass) {
518 		vw_PushMatrix();
519 
520 		vw_Translate(Location);
521 		vw_Rotate(Rotation.z, 0.0f, 0.0f, 1.0f);
522 		vw_Rotate(Rotation.y, 0.0f, 1.0f, 0.0f);
523 		vw_Rotate(Rotation.x, 1.0f, 0.0f, 0.0f);
524 
525 		if (NeedOnePieceDraw) {
526 			unsigned DrawVertexCount{GlobalIndexArrayCount};
527 			if (!DrawVertexCount)
528 				DrawVertexCount = GlobalVertexArrayCount;
529 
530 			vw_Draw3D(ePrimitiveType::TRIANGLES, DrawVertexCount, RI_3f_XYZ, GlobalVertexArray.get(),
531 				  Chunks[0].VertexStride * sizeof(float), GlobalVBO, 0,
532 				  GlobalIndexArray.get(), GlobalIBO, GlobalVAO);
533 		} else {
534 			for (auto &tmpChunk : Chunks) {
535 				vw_PushMatrix();
536 
537 				vw_Translate(tmpChunk.Location);
538 				vw_Rotate(tmpChunk.Rotation.z, 0.0f, 0.0f, 1.0f);
539 				vw_Rotate(tmpChunk.Rotation.y, 0.0f, 1.0f, 0.0f);
540 				vw_Rotate(tmpChunk.Rotation.x, 1.0f, 0.0f, 0.0f);
541 
542 				if (tmpChunk.NeedGeometryAnimation) {
543 					vw_Rotate(tmpChunk.GeometryAnimation.z, 0.0f, 0.0f, 1.0f);
544 					vw_Rotate(tmpChunk.GeometryAnimation.y, 0.0f, 1.0f, 0.0f);
545 					vw_Rotate(tmpChunk.GeometryAnimation.x, 1.0f, 0.0f, 0.0f);
546 				}
547 
548 				if (tmpChunk.ShaderType == 2) {
549 					if (auto sharedGLSL = GLSLShaderType2.lock()) {
550 						vw_UseShaderProgram(sharedGLSL);
551 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 0), 0);
552 						vw_Uniform1f(vw_GetShaderUniformLocation(sharedGLSL, 1), Chunks[0].ShaderData[0]);
553 						vw_Uniform1f(vw_GetShaderUniformLocation(sharedGLSL, 2), Chunks[0].ShaderData[1]);
554 					}
555 				}
556 
557 				vw_Draw3D(ePrimitiveType::TRIANGLES, tmpChunk.VertexQuantity, RI_3f_XYZ, tmpChunk.VertexArray.get(),
558 					  tmpChunk.VertexStride * sizeof(float), tmpChunk.VBO,
559 					  tmpChunk.RangeStart, tmpChunk.IndexArray.get(), tmpChunk.IBO, tmpChunk.VAO);
560 
561 				if ((tmpChunk.ShaderType == 2) &&
562 				    !GLSLShaderType2.expired())
563 					vw_StopShaderProgram();
564 
565 				vw_PopMatrix();
566 			}
567 		}
568 
569 		vw_PopMatrix();
570 		return;
571 	}
572 
573 	if (!vw_BoxInFrustum(Location + AABB[6], Location + AABB[0])) {
574 		if (DeleteAfterLeaveScene == eDeleteAfterLeaveScene::showed)
575 			DeleteAfterLeaveScene = eDeleteAfterLeaveScene::need_delete;
576 
577 		return;
578 	}
579 
580 	if (DeleteAfterLeaveScene == eDeleteAfterLeaveScene::enabled)
581 		DeleteAfterLeaveScene = eDeleteAfterLeaveScene::showed;
582 	if (DeleteAfterLeaveScene == eDeleteAfterLeaveScene::wait_delay) {
583 		DeleteAfterLeaveScene = eDeleteAfterLeaveScene::showed;
584 		Lifetime = -1.0f;
585 	}
586 
587 	GLtexture CurrentNormalMap{0};
588 	std::weak_ptr<cGLSL> CurrentGLSL{};
589 	int NeedNormalMapping{0};
590 	float Matrix[16];
591 	vw_GetMatrix(eMatrixPname::MODELVIEW, Matrix);
592 
593 	vw_PushMatrix();
594 
595 	vw_Translate(Location);
596 	vw_Rotate(Rotation.z, 0.0f, 0.0f, 1.0f);
597 	vw_Rotate(Rotation.y, 0.0f, 1.0f, 0.0f);
598 	vw_Rotate(Rotation.x, 1.0f, 0.0f, 0.0f);
599 
600 	bool N1 = false;
601 	for (auto &tmpChunk : Chunks) {
602 		if (tmpChunk.DrawType == eModel3DDrawType::Blend)
603 			N1 = true;
604 	}
605 
606 	vw_MaterialV(eMaterialParameter::DIFFUSE, Diffuse);
607 	vw_MaterialV(eMaterialParameter::AMBIENT, Ambient);
608 	vw_MaterialV(eMaterialParameter::SPECULAR, Specular);
609 	vw_MaterialV(eMaterialParameter::SHININESS, Power);
610 
611 	if (!NeedCullFaces)
612 		vw_CullFace(eCullFace::NONE);
613 	if (NeedAlphaTest)
614 		vw_SetTextureAlphaTest(true, eCompareFunc::GREATER, 0.4f);
615 
616 	if (NeedOnePieceDraw) {
617 		vw_BindTexture(0, Texture[0]);
618 
619 		if (!TextureIllum.empty() && TextureIllum[0]) {
620 			vw_BindTexture(1, TextureIllum[0]);
621 			vw_SetTextureEnvMode(eTextureEnvMode::COMBINE);
622 			vw_SetTextureBlendMode(eTextureCombinerName::COMBINE_RGB, eTextureCombinerOp::ADD);
623 		}
624 
625 		NeedNormalMapping = 0;
626 		if (GameConfig().UseGLSL120 &&
627 		    !NormalMap.empty() && NormalMap[0]) {
628 			NeedNormalMapping = 1;
629 			CurrentNormalMap = NormalMap[0];
630 			vw_BindTexture(3, CurrentNormalMap);
631 		}
632 
633 		// FIXME we don't need LightType1 and LightType2 any more, revise vw_CheckAndActivateAllLights()
634 		int LightType1, LightType2;
635 		vw_CheckAndActivateAllLights(LightType1, LightType2, Location, Radius*Radius, 1, GameConfig().MaxPointLights, Matrix);
636 
637 		if (GameConfig().UseGLSL120 && (Chunks[0].ShaderType >= 0)) {
638 			std::weak_ptr<cGLSL> CurrentObject3DGLSL{};
639 
640 			// FIXME we know what exactly we have, why we need shaders setup in this way?
641 			if ((Chunks[0].ShaderType == 1) && ShadowMap)
642 				Chunks[0].ShaderType = 3;
643 			if ((Chunks[0].ShaderType == 3) && !ShadowMap)
644 				Chunks[0].ShaderType = 1;
645 
646 			switch (Chunks[0].ShaderType) {
647 			case 1:
648 				CurrentObject3DGLSL = GLSLShaderType1;
649 				break;
650 			case 2:
651 				CurrentObject3DGLSL = GLSLShaderType2;
652 				break;
653 			case 3:
654 				CurrentObject3DGLSL = GLSLShaderType3;
655 				break;
656 			}
657 
658 			if (CurrentGLSL.lock() != CurrentObject3DGLSL.lock()) {
659 				if (!CurrentGLSL.expired())
660 					vw_StopShaderProgram();
661 
662 				CurrentGLSL = CurrentObject3DGLSL;
663 
664 				if (!CurrentGLSL.expired())
665 					vw_UseShaderProgram(CurrentGLSL);
666 			}
667 
668 			if (auto sharedGLSL = CurrentObject3DGLSL.lock()) {
669 				switch (Chunks[0].ShaderType) {
670 				case 1: // per pixel light
671 					vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 0), 0);
672 					vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 1), 1);
673 					if (!TextureIllum.empty() && TextureIllum[0])
674 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 2), 1);
675 					else
676 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 2), 0);
677 
678 					vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 3), 3);
679 					vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 4), NeedNormalMapping);
680 					break;
681 
682 				case 2: // explosion
683 					vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 0), 0);
684 					vw_Uniform1f(vw_GetShaderUniformLocation(sharedGLSL, 1), Chunks[0].ShaderData[0]);
685 					vw_Uniform1f(vw_GetShaderUniformLocation(sharedGLSL, 2), Chunks[0].ShaderData[1]);
686 					break;
687 
688 				case 3: // shadow map
689 					vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 0), 0);
690 					vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 1), 1);
691 					if (!TextureIllum.empty() && TextureIllum[0])
692 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 2), 1);
693 					else
694 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 2), 0);
695 
696 					vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 3), 2);
697 					vw_Uniform1f(vw_GetShaderUniformLocation(sharedGLSL, 4), ShadowMap_Get_xPixelOffset());
698 					vw_Uniform1f(vw_GetShaderUniformLocation(sharedGLSL, 5), ShadowMap_Get_yPixelOffset());
699 					vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 6), 3);
700 					vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 7), NeedNormalMapping);
701 					break;
702 				}
703 			}
704 		}
705 
706 		unsigned DrawVertexCount{GlobalIndexArrayCount};
707 		if (!DrawVertexCount)
708 			DrawVertexCount = GlobalVertexArrayCount;
709 
710 		vw_Draw3D(ePrimitiveType::TRIANGLES, DrawVertexCount, Chunks[0].VertexFormat, GlobalVertexArray.get(),
711 			  Chunks[0].VertexStride * sizeof(float), GlobalVBO, 0,
712 			  GlobalIndexArray.get(), GlobalIBO, GlobalVAO);
713 
714 		vw_DeActivateAllLights();
715 	} else {
716 		GLtexture CurrentTexture{0};
717 
718 		for (unsigned int i = 0; i < Chunks.size(); i++) {
719 			if (!HitBB.empty()) {
720 				sVECTOR3D Min, Max;
721 				Min.x = Max.x = HitBB[i].Box[0].x + HitBB[i].Location.x;
722 				Min.y = Max.y = HitBB[i].Box[0].y + HitBB[i].Location.y;
723 				Min.z = Max.z = HitBB[i].Box[0].z + HitBB[i].Location.z;
724 				for (int j = 0; j < 8; j++) {
725 					if (Min.x > HitBB[i].Box[j].x + HitBB[i].Location.x)
726 						Min.x = HitBB[i].Box[j].x + HitBB[i].Location.x;
727 					if (Min.y > HitBB[i].Box[j].y + HitBB[i].Location.y)
728 						Min.y = HitBB[i].Box[j].y + HitBB[i].Location.y;
729 					if (Min.z > HitBB[i].Box[j].z + HitBB[i].Location.z)
730 						Min.z = HitBB[i].Box[j].z + HitBB[i].Location.z;
731 					if (Max.x < HitBB[i].Box[j].x + HitBB[i].Location.x)
732 						Max.x = HitBB[i].Box[j].x + HitBB[i].Location.x;
733 					if (Max.y < HitBB[i].Box[j].y + HitBB[i].Location.y)
734 						Max.y = HitBB[i].Box[j].y + HitBB[i].Location.y;
735 					if (Max.z < HitBB[i].Box[j].z + HitBB[i].Location.z)
736 						Max.z = HitBB[i].Box[j].z + HitBB[i].Location.z;
737 				}
738 
739 				if (!vw_BoxInFrustum(Location + Min, Location + Max))
740 					continue;
741 			}
742 
743 			if (CurrentTexture != Texture[i]) {
744 				vw_BindTexture(0, Texture[i]);
745 
746 				if (Chunks[i].NeedTextureAnimation) {
747 					vw_MatrixMode(eMatrixMode::TEXTURE);
748 					vw_LoadIdentity();
749 					vw_Translate(Chunks[i].TextureAnimation);
750 					vw_MatrixMode(eMatrixMode::MODELVIEW);
751 				}
752 
753 				if ((TextureIllum.size() > static_cast<unsigned>(i)) && TextureIllum[i]) {
754 					vw_BindTexture(1, TextureIllum[i]);
755 					vw_SetTextureEnvMode(eTextureEnvMode::COMBINE);
756 					vw_SetTextureBlendMode(eTextureCombinerName::COMBINE_RGB, eTextureCombinerOp::ADD);
757 				}
758 
759 				NeedNormalMapping = 0;
760 				if (GameConfig().UseGLSL120 &&
761 				    (NormalMap.size() > static_cast<unsigned>(i))) {
762 					if (NormalMap[i]) {
763 						NeedNormalMapping = 1;
764 						CurrentNormalMap = NormalMap[i];
765 						vw_BindTexture(3, CurrentNormalMap);
766 					} else if (CurrentNormalMap) {
767 						vw_BindTexture(3, 0);
768 						CurrentNormalMap = 0;
769 					}
770 				}
771 
772 				CurrentTexture = Texture[i];
773 			}
774 
775 			vw_PushMatrix();
776 
777 			vw_Translate(Chunks[i].Location);
778 			vw_Rotate(Chunks[i].Rotation.z, 0.0f, 0.0f, 1.0f);
779 			vw_Rotate(Chunks[i].Rotation.y, 0.0f, 1.0f, 0.0f);
780 			vw_Rotate(Chunks[i].Rotation.x, 1.0f, 0.0f, 0.0f);
781 
782 			if (Chunks[i].NeedGeometryAnimation) {
783 				vw_Rotate(Chunks[i].GeometryAnimation.z, 0.0f, 0.0f, 1.0f);
784 				vw_Rotate(Chunks[i].GeometryAnimation.y, 0.0f, 1.0f, 0.0f);
785 				vw_Rotate(Chunks[i].GeometryAnimation.x, 1.0f, 0.0f, 0.0f);
786 			}
787 
788 			// FIXME we don't need LightType1 and LightType2 any more, revise vw_CheckAndActivateAllLights()
789 			int LightType1, LightType2;
790 			if (!HitBB.empty())
791 				vw_CheckAndActivateAllLights(LightType1, LightType2, Location + HitBB[i].Location, HitBB[i].Radius2, 1, GameConfig().MaxPointLights, Matrix);
792 			else
793 				vw_CheckAndActivateAllLights(LightType1, LightType2, Location, Radius * Radius, 1, GameConfig().MaxPointLights, Matrix);
794 
795 			// for planet's clouds
796 			if (N1)
797 				vw_PolygonOffset(true, 2.0f, 2.0f);
798 
799 			if (Chunks[i].DrawType == eModel3DDrawType::Blend) {
800 				vw_SetTextureAlphaTest(true, eCompareFunc::GREATER, 0.01f);
801 				vw_SetTextureBlend(true, eTextureBlendFactor::SRC_ALPHA, eTextureBlendFactor::ONE);
802 				vw_PolygonOffset(true, 1.0f, 1.0f);
803 			}
804 
805 			if (GameConfig().UseGLSL120 &&
806 			    (Chunks[i].ShaderType >= 0)) {
807 				std::weak_ptr<cGLSL> CurrentObject3DGLSL{};
808 
809 				// FIXME we know what exactly we have, why we need shaders setup in this way?
810 				if ((Chunks[i].ShaderType == 1) && ShadowMap)
811 					Chunks[i].ShaderType = 3;
812 				if ((Chunks[i].ShaderType == 3) && !ShadowMap)
813 					Chunks[i].ShaderType = 1;
814 
815 				switch (Chunks[i].ShaderType) {
816 				case 1:
817 					CurrentObject3DGLSL = GLSLShaderType1;
818 					break;
819 				case 2:
820 					CurrentObject3DGLSL = GLSLShaderType2;
821 					break;
822 				case 3:
823 					CurrentObject3DGLSL = GLSLShaderType3;
824 					break;
825 				}
826 
827 				if (CurrentGLSL.lock() != CurrentObject3DGLSL.lock()) {
828 					if (!CurrentGLSL.expired())
829 						vw_StopShaderProgram();
830 
831 					CurrentGLSL = CurrentObject3DGLSL;
832 
833 					if (!CurrentGLSL.expired())
834 						vw_UseShaderProgram(CurrentGLSL);
835 				}
836 
837 				if (auto sharedGLSL = CurrentObject3DGLSL.lock()) {
838 					switch (Chunks[i].ShaderType) {
839 					case 1: // per pixel light
840 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 0), 0);
841 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 1), 1);
842 						if (!TextureIllum.empty() && TextureIllum[0])
843 							vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 2), 1);
844 						else
845 							vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 2), 0);
846 
847 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 3), 3);
848 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 4), NeedNormalMapping);
849 						break;
850 
851 					case 2: // explosion
852 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 0), 0);
853 						vw_Uniform1f(vw_GetShaderUniformLocation(sharedGLSL, 1), Chunks[0].ShaderData[0]);
854 						vw_Uniform1f(vw_GetShaderUniformLocation(sharedGLSL, 2), Chunks[0].ShaderData[1]);
855 						break;
856 
857 					case 3: // shadow map
858 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 0), 0);
859 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 1), 1);
860 						if (!TextureIllum.empty() && TextureIllum[0])
861 							vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 2), 1);
862 						else
863 							vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 2), 0);
864 
865 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 3), 2);
866 						vw_Uniform1f(vw_GetShaderUniformLocation(sharedGLSL, 4), ShadowMap_Get_xPixelOffset());
867 						vw_Uniform1f(vw_GetShaderUniformLocation(sharedGLSL, 5), ShadowMap_Get_yPixelOffset());
868 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 6), 3);
869 						vw_Uniform1i(vw_GetShaderUniformLocation(sharedGLSL, 7), NeedNormalMapping);
870 						break;
871 					}
872 				}
873 			}
874 
875 			vw_Draw3D(ePrimitiveType::TRIANGLES, Chunks[i].VertexQuantity, Chunks[i].VertexFormat, Chunks[i].VertexArray.get(),
876 				  Chunks[i].VertexStride * sizeof(float), Chunks[i].VBO,
877 				  Chunks[i].RangeStart, Chunks[i].IndexArray.get(), Chunks[i].IBO, Chunks[i].VAO);
878 
879 			if (Chunks[i].DrawType == eModel3DDrawType::Blend) {
880 				vw_SetTextureAlphaTest(false, eCompareFunc::ALWAYS, 0);
881 				vw_SetTextureBlend(false, eTextureBlendFactor::ONE, eTextureBlendFactor::ZERO);
882 				vw_PolygonOffset(false, 0.0f, 0.0f);
883 			}
884 
885 			vw_DeActivateAllLights();
886 
887 			if (Chunks[i].NeedTextureAnimation) {
888 				vw_BindTexture(0, 0);
889 				vw_MatrixMode(eMatrixMode::TEXTURE);
890 				vw_LoadIdentity();
891 				vw_MatrixMode(eMatrixMode::MODELVIEW);
892 				CurrentTexture = 0;
893 			}
894 
895 			vw_PopMatrix();
896 		}
897 	}
898 
899 	if (GameConfig().UseGLSL120)
900 		vw_StopShaderProgram();
901 
902 	if (CurrentNormalMap)
903 		vw_BindTexture(3, 0);
904 	vw_BindTexture(1, 0);
905 	vw_BindTexture(0, 0);
906 	if (NeedAlphaTest)
907 		vw_SetTextureAlphaTest(false, eCompareFunc::ALWAYS, 0);
908 	if (!NeedCullFaces)
909 		vw_CullFace(eCullFace::BACK);
910 	vw_PopMatrix();
911 
912 #ifndef NDEBUG
913 	// debug info, line number in script file
914 	if (!ScriptLineNumberUTF32.empty())
915 		vw_DrawText3DUTF32(Location.x, Location.y + AABB[0].y, Location.z, ScriptLineNumberUTF32);
916 #endif // NDEBUG
917 
918 	DrawBoundingBoxes(Location, AABB, OBB, HitBB);
919 
920 	// TODO why we need ShowStatus if we could use ArmorInitialStatus < 0.0f for this?
921 	if (!ShowStatus ||
922 	    (ArmorInitialStatus <= 0.0f) ||
923 	    ((ArmorCurrentStatus == ArmorInitialStatus) &&
924 	     (ShieldCurrentStatus == ShieldInitialStatus) &&
925 	     !ShowStatusAllTime))
926 		return;
927 
928 	// even if shield recharged - don't hide object's status any more
929 	ShowStatusAllTime = true;
930 
931 	DrawObjectStatus(sVECTOR3D{Location.x, Location.y + AABB[0].y + 0.7f, Location.z},
932 			 Width, sRGBCOLOR{eRGBCOLOR::red}, ArmorCurrentStatus, ArmorInitialStatus);
933 	if (ShieldInitialStatus > 0.0f)
934 		DrawObjectStatus(sVECTOR3D{Location.x, Location.y + AABB[0].y + 1.75f, Location.z},
935 				 Width, sRGBCOLOR{0.1f, 0.7f, 1.0f}, ShieldCurrentStatus, ShieldInitialStatus);
936 	vw_BindTexture(0, 0);
937 }
938 
939 /*
940  * Update.
941  */
Update(float Time)942 bool cObject3D::Update(float Time)
943 {
944 	if (TimeLastUpdate == -1.0f) {
945 		TimeLastUpdate = Time;
946 		return true;
947 	}
948 
949 	if (DeleteAfterLeaveScene == eDeleteAfterLeaveScene::need_delete) {
950 		Lifetime = DeleteAfterLeaveSceneDelay;
951 		DeleteAfterLeaveScene = eDeleteAfterLeaveScene::wait_delay;
952 	}
953 
954 	TimeDelta = Time - TimeLastUpdate;
955 	if (TimeDelta == 0.0f)
956 		return true;
957 
958 	TimeLastUpdate = Time;
959 
960 	if (Lifetime > -1.0f) {
961 		Lifetime -= TimeDelta;
962 		if (Lifetime <= 0.0f)
963 			return false;
964 	}
965 
966 	if ((ShieldInitialStatus > 0.0f) &&
967 	    (ShieldCurrentStatus < ShieldInitialStatus)) {
968 		ShieldCurrentStatus += ShieldRechargeRate * TimeDelta;
969 		if (ShieldCurrentStatus > ShieldInitialStatus)
970 			ShieldCurrentStatus = ShieldInitialStatus;
971 	}
972 
973 	// if we have TimeSheet with actions and this is not a cycled entry
974 	if (!TimeSheetList.empty() &&
975 	    (TimeSheetList.front().Time > -1.0f)) {
976 		TimeSheetList.front().Time -= TimeDelta;
977 		// if this entry is out of time, remove it
978 		if (TimeSheetList.front().Time <= 0.0f) {
979 			// correct time delta
980 			if (TimeSheetList.front().Time < 0.0f)
981 				TimeDelta += TimeSheetList.front().Time;
982 			TimeSheetList.pop_front();
983 		}
984 	}
985 	// should be unpacked
986 	if (!TimeSheetList.empty() &&
987 	    (TimeSheetList.front().AI_Mode != 0)) {
988 		InterAIMode(TimeSheetList);
989 		// since we already unpack this entry, remove it
990 		TimeSheetList.pop_front();
991 	}
992 
993 	return true;
994 }
995 
996 } // astromenace namespace
997 } // viewizard namespace
998