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 add initialization via XML file, hard coded initialization should be removed
29 // TODO remove GetPreloadedTextureAsset() call from main loop
30 
31 // TODO probably, we could use GL_EXT_draw_instanced here in future
32 //      render 'space dust' instanced (send position and size in texture), instead of render 3 layer
33 
34 /*
35 In order to show movement and more 'live' space, star system render "space dust" with 3 tile-animated layers:
36 1. Small and far space dust, looks more like 'clouds'. This layer rendered first, before all objects in the foreground.
37 2. Far 'big' space dust. This layer rendered second with higher speed, before all objects in the foreground.
38 3. Close space dust. This layer rendered with highest speed, after all 3D objects.
39 */
40 
41 #include "../config/config.h"
42 #include "../assets/texture.h"
43 #include "../object3d/space_object/space_object.h"
44 #include "skybox.h"
45 #include "../game/camera.h"
46 
47 // NOTE switch to nested namespace definition (namespace A::B::C { ... }) (since C++17)
48 namespace viewizard {
49 namespace astromenace {
50 
51 namespace {
52 
53 // StarSystem initialization status
54 bool StarSystem_InitedAll{false};
55 bool StarSystem_Inited{false};
56 // StarSystem rotation
57 sVECTOR3D StarSystem_BaseRotation(0.0f, 0.0f, 0.0f);
58 
59 // particle system for space dust
60 std::weak_ptr<cParticleSystem> psSpace;
61 
62 // tile animation for space dust (2 layers)
63 float StarsTile{0.0f};
64 float StarsTile2{0.0f};
65 float StarsTileUpdateTime{0.0f};
66 float StarsTileUpdateTime2{0.0f};
67 float StarsTileStartTransparentLayer1{0.0f};
68 float StarsTileEndTransparentLayer1{0.0f};
69 float StarsTileStartTransparentLayer2{0.0f};
70 float StarsTileEndTransparentLayer2{0.0f};
71 
72 sVECTOR3D InGameInitialLocation{0, 10, 250};
73 
74 // vertex array for rendering
75 float VertexArray[36]; // 4 * 9 = 4 vertices * (RI_3f_XYZ | RI_4f_COLOR | RI_1_TEX);
76 unsigned int VertexArrayPosition{0};
77 
78 } // unnamed namespace
79 
80 
81 /*
82  * Star system initialization.
83  */
StarSystemInit(int Num,sVECTOR3D SetBaseRotation)84 void StarSystemInit(int Num, sVECTOR3D SetBaseRotation)
85 {
86 	// SkyBox setup
87 	switch (Num) {
88 	case 1:
89 		SkyBoxCreate(0.0f, 0.0f, 0.0f, 100.0f, 100.0f, 100.0f);
90 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/1/skybox_back6.tga"), eSide::BACK);
91 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/1/skybox_bottom4.tga"), eSide::BOTTOM);
92 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/1/skybox_front5.tga"), eSide::FRONT);
93 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/1/skybox_left2.tga"), eSide::LEFT);
94 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/1/skybox_right1.tga"), eSide::RIGHT);
95 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/1/skybox_top3.tga"), eSide::TOP);
96 		StarSystem_Inited = true;
97 		break;
98 	case 2:
99 		SkyBoxCreate(0.0f, 0.0f, 0.0f, 100.0f, 100.0f, 100.0f);
100 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/2/skybox_back6.tga"), eSide::BACK);
101 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/2/skybox_bottom4.tga"), eSide::BOTTOM);
102 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/2/skybox_front5.tga"), eSide::FRONT);
103 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/2/skybox_left2.tga"), eSide::LEFT);
104 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/2/skybox_right1.tga"), eSide::RIGHT);
105 		SkyBoxSetTexture(GetPreloadedTextureAsset("skybox/2/skybox_top3.tga"), eSide::TOP);
106 		StarSystem_Inited = true;
107 		break;
108 	default:
109 		std::cerr << __func__ << "(): " << "wrong Num.\n";
110 		break;
111 	}
112 
113 	// StarSystem setup
114 	StarSystem_InitedAll = true;
115 	StarSystem_BaseRotation = SetBaseRotation;
116 }
117 
118 /*
119  * Star system initialization by game's part type (menu/game).
120  */
StarSystemInitByType(eDrawType DrawType)121 void StarSystemInitByType(eDrawType DrawType)
122 {
123 	StarsTileStartTransparentLayer1 = 0.2f;
124 	StarsTileEndTransparentLayer1 = 0.7f;
125 	StarsTileStartTransparentLayer2 = 0.9f;
126 	StarsTileEndTransparentLayer2 = 0.7f;
127 
128 	switch (DrawType) {
129 	case eDrawType::MENU:
130 		StarsTileUpdateTime = vw_GetTimeThread(0);
131 		StarsTileUpdateTime2 = vw_GetTimeThread(0);
132 		psSpace = vw_CreateParticleSystem();
133 		if (auto sharedSpace = psSpace.lock()) {
134 			sharedSpace->ColorStart.r = 0.80f;
135 			sharedSpace->ColorStart.g = 0.80f;
136 			sharedSpace->ColorStart.b = 1.00f;
137 			sharedSpace->ColorEnd.r = 0.70f;
138 			sharedSpace->ColorEnd.g = 0.70f;
139 			sharedSpace->ColorEnd.b = 1.00f;
140 			sharedSpace->AlphaStart = 1.00f;
141 			sharedSpace->AlphaEnd   = 0.00f;
142 			sharedSpace->SizeStart = 0.10f;
143 			sharedSpace->SizeVar = 0.05f;
144 			sharedSpace->SizeEnd = 0.30f;
145 			sharedSpace->Speed = 4.00f;
146 			sharedSpace->SpeedVar = 8.00f;
147 			sharedSpace->Theta = 0.00f;
148 			sharedSpace->Life = 10.00f;
149 			sharedSpace->LifeVar = 0.00f;
150 			sharedSpace->CreationType = eParticleCreationType::Cube;
151 			sharedSpace->CreationSize = sVECTOR3D{2.0f, 50.0f, 30.0f};
152 			sharedSpace->ParticlesPerSec = 140;
153 			sharedSpace->Texture = GetPreloadedTextureAsset("gfx/flare3.tga");
154 			sharedSpace->Direction = sVECTOR3D{1.0f, 0.0f, 0.0f};
155 			sharedSpace->CameraDistResize = 0.1f;
156 			sharedSpace->SetStartLocation(sVECTOR3D{-50.0f, 10.0f, -20.0f});
157 
158 			// emulate time flow, particles should fill the screen
159 			float Time = sharedSpace->TimeLastUpdate;
160 			for (float i = Time; i < (Time + 20); i += 1.0f) {
161 				sharedSpace->Update(i);
162 			}
163 			sharedSpace->TimeLastUpdate = Time;
164 		}
165 	break;
166 	case eDrawType::GAME:
167 		StarsTileUpdateTime = vw_GetTimeThread(1);
168 		StarsTileUpdateTime2 = vw_GetTimeThread(1);
169 		psSpace = vw_CreateParticleSystem();
170 		if (auto sharedSpace = psSpace.lock()) {
171 			sharedSpace->ColorStart.r = 0.80f;
172 			sharedSpace->ColorStart.g = 0.80f;
173 			sharedSpace->ColorStart.b = 1.00f;
174 			sharedSpace->ColorEnd.r = 0.70f;
175 			sharedSpace->ColorEnd.g = 0.70f;
176 			sharedSpace->ColorEnd.b = 1.00f;
177 			sharedSpace->AlphaStart = 0.50f;
178 			sharedSpace->AlphaEnd = 1.00f;
179 			sharedSpace->SizeStart = 0.40f;
180 			sharedSpace->SizeEnd = 0.05f;
181 			sharedSpace->Speed = 25.00f;
182 			sharedSpace->SpeedVar = 5.00f;
183 			sharedSpace->Theta = 0.00f;
184 			sharedSpace->Life = 14.00f;
185 			sharedSpace->LifeVar = 0.00f;
186 			sharedSpace->CreationType = eParticleCreationType::Cube;
187 			sharedSpace->CreationSize = sVECTOR3D{200.0f, 30.0f, 10.0f};
188 			sharedSpace->ParticlesPerSec = 100;
189 			sharedSpace->Texture = GetPreloadedTextureAsset("gfx/flare3.tga");
190 			sharedSpace->Direction = sVECTOR3D{0.0f, 0.0f, -1.0f};
191 			sharedSpace->SetStartLocation(InGameInitialLocation);
192 
193 			// emulate time flow, particles should fill the screen
194 			float Time = sharedSpace->TimeLastUpdate;
195 			for (float i = Time; i < (Time + 25); i += 1.0f) {
196 				sharedSpace->Update(i);
197 			}
198 			sharedSpace->TimeLastUpdate = Time;
199 		}
200 	break;
201 	}
202 }
203 
204 /*
205  * Setup first and second layers transparency.
206  */
StarSystemLayer1Transp(float Start,float End)207 void StarSystemLayer1Transp(float Start, float End)
208 {
209 	StarsTileStartTransparentLayer1 = Start;
210 	StarsTileEndTransparentLayer1 = End;
211 }
212 
213 /*
214  * Setup third layer transparency.
215  */
StarSystemLayer3Transp(float Start,float End)216 void StarSystemLayer3Transp(float Start, float End)
217 {
218 	StarsTileStartTransparentLayer2 = Start;
219 	StarsTileEndTransparentLayer2 = End;
220 }
221 
222 /*
223  * Release star system.
224  */
StarSystemRelease()225 void StarSystemRelease()
226 {
227 	for (unsigned i = 0; i < static_cast<unsigned>(eSide::size); i++)
228 		SkyBoxSetTexture(0, static_cast<eSide>(i));
229 
230 	StarSystem_Inited = false;
231 }
232 
233 /*
234  * Draw local vertex array.
235  */
DrawVertexArray()236 static inline void DrawVertexArray()
237 {
238 	vw_Draw3D(ePrimitiveType::TRIANGLE_STRIP, 4,
239 		  RI_3f_XYZ | RI_4f_COLOR | RI_1_TEX,
240 		  VertexArray, 9 * sizeof(VertexArray[0]));
241 }
242 
243 /*
244  * Add vertex to  to local vertex array.
245  */
AddToVertexArray(float CoordX,float CoordY,float CoordZ,sRGBCOLOR Color,float Alpha,float TextureU,float TextureV)246 static inline void AddToVertexArray(float CoordX, float CoordY, float CoordZ,
247 				    sRGBCOLOR Color, float Alpha,
248 				    float TextureU, float TextureV)
249 {
250 	VertexArray[VertexArrayPosition++] = CoordX;
251 	VertexArray[VertexArrayPosition++] = CoordY;
252 	VertexArray[VertexArrayPosition++] = CoordZ;
253 	VertexArray[VertexArrayPosition++] = Color.r;
254 	VertexArray[VertexArrayPosition++] = Color.g;
255 	VertexArray[VertexArrayPosition++] = Color.b;
256 	VertexArray[VertexArrayPosition++] = Alpha;
257 	VertexArray[VertexArrayPosition++] = TextureU;
258 	VertexArray[VertexArrayPosition++] = TextureV;
259 }
260 
261 /*
262  * Draw star system.
263  */
StarSystemDraw(eDrawType DrawType)264 void StarSystemDraw(eDrawType DrawType)
265 {
266 	if (!StarSystem_InitedAll) return;
267 
268 	// current camera location
269 	sVECTOR3D CurrentCameraLocation;
270 	vw_GetCameraLocation(&CurrentCameraLocation);
271 
272 	if (StarSystem_Inited) {
273 		vw_DepthTest(false, eCompareFunc::LESS);
274 
275 		// SkyBox
276 		vw_PushMatrix();
277 		vw_Translate(CurrentCameraLocation);
278 		vw_Rotate(StarSystem_BaseRotation.x, 1.0f, 0.0f, 0.0f);
279 		vw_Rotate(StarSystem_BaseRotation.y, 0.0f, 1.0f, 0.0f);
280 		vw_Rotate(StarSystem_BaseRotation.z, 0.0f, 0.0f, 1.0f);
281 		SkyBoxDraw();
282 		vw_PopMatrix();
283 
284 		vw_DepthTest(true, eCompareFunc::LEQUAL);
285 	}
286 
287 	// FIXME should be moved to 'space_object' code
288 	// planets and big asteroids should be rendered before 'space dust'
289 	ForEachSpaceObject([&] (cSpaceObject &tmpSpace) {
290 		if (tmpSpace.ObjectType == eObjectType::Planet) {
291 			if (DrawType == eDrawType::GAME) {
292 				vw_PushMatrix();
293 				vw_Translate(sVECTOR3D{CurrentCameraLocation.x * 0.90f - GetCameraShake() * 4.0f,
294 						       GetCameraShake() * 2.0f,
295 						       0.0f});
296 			}
297 			tmpSpace.Draw(false);
298 			if (DrawType == eDrawType::GAME)
299 				vw_PopMatrix();
300 		}
301 	});
302 
303 	// planetoids much more closer to player, than planets, clear the depth buffer
304 	vw_Clear(RI_DEPTH_BUFFER);
305 
306 	ForEachSpaceObject([&] (cSpaceObject &tmpSpace) {
307 		if (tmpSpace.ObjectType == eObjectType::Planetoid) {
308 			if (DrawType == eDrawType::GAME) {
309 				vw_PushMatrix();
310 				vw_Translate(sVECTOR3D{CurrentCameraLocation.x * 0.70f - GetCameraShake() * 4.0f,
311 						       GetCameraShake() * 2.0f,
312 						       0.0f});
313 			}
314 			tmpSpace.Draw(false);
315 			if (DrawType == eDrawType::GAME)
316 				vw_PopMatrix();
317 		}
318 	});
319 
320 	// 'space dust' much more closer to player, than planets and planetoids, clear the depth buffer
321 	vw_Clear(RI_DEPTH_BUFFER);
322 
323 	float width_2{0.0f};
324 	float heigh_2{110.0f};
325 	float length_2{110.0f};
326 	float x{GetCameraCoveredDistance().x};
327 	float y{GetCameraCoveredDistance().y};
328 	float z{GetCameraCoveredDistance().z};
329 	float StartTransparentLayer1{0.7f};
330 	float EndTransparentLayer1{0.7f};
331 	sRGBCOLOR Color{1.0f, 1.0f, 1.0f};
332 
333 	if (DrawType == eDrawType::GAME) {
334 		width_2 = length_2 = 175.0f;
335 		heigh_2 = 0.0f;
336 
337 		x = GetCameraCoveredDistance().x + GetCameraShake() + CurrentCameraLocation.x * 0.8f;
338 		y = GetCameraCoveredDistance().y - GetCameraShake() * 0.5f;
339 		z = GetCameraCoveredDistance().z + 25.0f;
340 
341 		StartTransparentLayer1 = StarsTileStartTransparentLayer1;
342 		EndTransparentLayer1 = StarsTileEndTransparentLayer1;
343 	}
344 
345 	// first 'space dust' layer (clouds)
346 	VertexArrayPosition = 0;
347 	AddToVertexArray(x + width_2, y + heigh_2, z + length_2 + length_2 / 2.0f,
348 			 Color, StartTransparentLayer1,
349 			 1.0f, 0.0f + StarsTile / 3.0f);
350 	AddToVertexArray(x + width_2, y + heigh_2, z - length_2 / 2.0f,
351 			 Color, EndTransparentLayer1,
352 			 1.0f, 1.0f + StarsTile / 3.0f);
353 	AddToVertexArray(x - width_2, y - heigh_2, z + length_2 + length_2 / 2.0f,
354 			 Color, StartTransparentLayer1,
355 			 0.0f, 0.0f + StarsTile / 3.0f);
356 	AddToVertexArray(x - width_2, y - heigh_2, z - length_2/2,
357 			 Color, EndTransparentLayer1,
358 			 0.0f, 1.0f + StarsTile / 3.0f);
359 
360 	if (DrawType == eDrawType::MENU) {
361 		StarsTile -= 0.015f * (vw_GetTimeThread(0) - StarsTileUpdateTime);
362 		StarsTileUpdateTime = vw_GetTimeThread(0);
363 	} else {
364 		StarsTile -= 0.035f * (vw_GetTimeThread(1) - StarsTileUpdateTime);
365 		StarsTileUpdateTime = vw_GetTimeThread(1);
366 	}
367 
368 	if (StarsTile < -3.0f)
369 		StarsTile += 3.0f;
370 
371 	GLtexture TileTexture = GetPreloadedTextureAsset("skybox/tile_back.tga");
372 	vw_BindTexture(0, TileTexture);
373 	vw_SetTextureBlend(true, eTextureBlendFactor::SRC_ALPHA, eTextureBlendFactor::ONE);
374 
375 	vw_DepthTest(false, eCompareFunc::LESS);
376 
377 	if (DrawType == eDrawType::MENU) {
378 		vw_PushMatrix();
379 		vw_Rotate(-20.0f, 0.0f, 0.0f, 1.0f);
380 		vw_Rotate(-45.0f, 0.0f, 1.0f, 0.0f);
381 		vw_Rotate(30.0f, 1.0f, 0.0f, 0.0f);
382 	}
383 
384 	DrawVertexArray();
385 
386 	// second 'space dust' layer
387 	VertexArrayPosition = 0;
388 	AddToVertexArray(x + width_2, y + heigh_2, z + length_2 + length_2 / 2.0f,
389 			 Color, StartTransparentLayer1,
390 			 3.0f, 0.0f + StarsTile);
391 	AddToVertexArray(x + width_2, y + heigh_2, z - length_2 / 2.0f,
392 			 Color, EndTransparentLayer1,
393 			 3.0f, 3.0f + StarsTile);
394 	AddToVertexArray(x - width_2, y - heigh_2, z + length_2 + length_2 / 2.0f,
395 			 Color, StartTransparentLayer1,
396 			 0.0f, 0.0f + StarsTile);
397 	AddToVertexArray(x - width_2, y - heigh_2, z - length_2/2,
398 			 Color, EndTransparentLayer1,
399 			 0.0f, 3.0f + StarsTile);
400 
401 	vw_BindTexture(0, GetPreloadedTextureAsset("skybox/tile_stars.tga"));
402 
403 	DrawVertexArray();
404 
405 	if (DrawType == eDrawType::MENU)
406 		vw_PopMatrix();
407 
408 	vw_DepthTest(true, eCompareFunc::LEQUAL);
409 
410 	vw_SetTextureBlend(false, eTextureBlendFactor::ONE, eTextureBlendFactor::ZERO);
411 	vw_BindTexture(0, 0);
412 
413 	// care about in-game camera movements
414 	if (DrawType == eDrawType::GAME) {
415 		if (auto sharedSpace = psSpace.lock()) {
416 			sharedSpace->SetStartLocation(sharedSpace->GetLocation());
417 			sharedSpace->MoveSystemLocation(InGameInitialLocation + GetCameraCoveredDistance());
418 		}
419 	}
420 }
421 
422 /*
423  * Draw third layer of space dust.
424  */
StarSystemDrawThirdLayer(eDrawType DrawType)425 void StarSystemDrawThirdLayer(eDrawType DrawType)
426 {
427 	// VisualEffectsQuality is inverted (0 - all effects, 2 - minimum effects)
428 	if (GameConfig().VisualEffectsQuality > 1)
429 		return;
430 
431 	float width_2{0.0f};
432 	float heigh_2{110.0f};
433 	float length_2{110.0f};
434 	float x{GetCameraCoveredDistance().x};
435 	float y{GetCameraCoveredDistance().y};
436 	float z{GetCameraCoveredDistance().z};
437 	float StartTransparentLayer2{0.9f};
438 	float EndTransparentLayer2{0.7f};
439 	sRGBCOLOR Color{1.0f, 1.0f, 1.0f};
440 
441 	if (DrawType == eDrawType::GAME) {
442 		width_2 = length_2 = 175.0f;
443 		heigh_2 = 0.0f;
444 
445 		sVECTOR3D CurrentCameraLocation;
446 		vw_GetCameraLocation(&CurrentCameraLocation);
447 
448 		x = GetCameraCoveredDistance().x + GetCameraShake() * 2.0f + CurrentCameraLocation.x * 0.5f;
449 		y = GetCameraCoveredDistance().y - GetCameraShake();
450 		z = GetCameraCoveredDistance().z + 25.0f;
451 
452 		StartTransparentLayer2 = StarsTileStartTransparentLayer2;
453 		EndTransparentLayer2 = StarsTileEndTransparentLayer2;
454 	}
455 
456 	VertexArrayPosition = 0;
457 	AddToVertexArray(x + width_2, y + heigh_2, z + length_2 + length_2/ 2.0f,
458 			 Color, StartTransparentLayer2,
459 			 3.2f, 0.0f + StarsTile2);
460 	AddToVertexArray(x + width_2, y + heigh_2, z - length_2 / 2.0f,
461 			 Color, EndTransparentLayer2,
462 			 3.2f, 3.0f + StarsTile2);
463 	AddToVertexArray(x - width_2, y - heigh_2, z + length_2 + length_2 / 2.0f,
464 			 Color, StartTransparentLayer2,
465 			 0.2f, 0.0f + StarsTile2);
466 	AddToVertexArray(x - width_2, y - heigh_2, z - length_2 / 2.0f,
467 			 Color, EndTransparentLayer2,
468 			 0.2f, 3.0f + StarsTile2);
469 
470 	if (DrawType == eDrawType::MENU) {
471 		StarsTile2 -= 0.04f * (vw_GetTimeThread(0) - StarsTileUpdateTime2);
472 		StarsTileUpdateTime2 = vw_GetTimeThread(0);
473 	} else {
474 		StarsTile2 -= 0.12f * (vw_GetTimeThread(1) - StarsTileUpdateTime2);
475 		StarsTileUpdateTime2 = vw_GetTimeThread(1);
476 	}
477 	if (StarsTile2 < -3.0f)
478 		StarsTile2 += 3.0f;
479 
480 	GLtexture TileTexture = GetPreloadedTextureAsset("skybox/tile_stars.tga");
481 	vw_BindTexture(0, TileTexture);
482 	vw_SetTextureBlend(true, eTextureBlendFactor::SRC_ALPHA, eTextureBlendFactor::ONE);
483 	vw_DepthTest(false, eCompareFunc::LESS);
484 
485 	if (DrawType == eDrawType::MENU) {
486 		vw_PushMatrix();
487 		vw_Rotate(-20.0f, 0.0f, 0.0f, 1.0f);
488 		vw_Rotate(-45.0f, 0.0f, 1.0f, 0.0f);
489 		vw_Rotate(30.0f, 1.0f, 0.0f, 0.0f);
490 	}
491 
492 	DrawVertexArray();
493 
494 	if (DrawType == eDrawType::MENU)
495 		vw_PopMatrix();
496 
497 	vw_DepthTest(true, eCompareFunc::LEQUAL);
498 	vw_SetTextureBlend(false, eTextureBlendFactor::ONE, eTextureBlendFactor::ZERO);
499 	vw_BindTexture(0, 0);
500 }
501 
502 } // astromenace namespace
503 } // viewizard namespace
504