1 /*
2 * This file is part of the Colobot: Gold Edition source code
3 * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4 * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see http://gnu.org/licenses
18 */
19
20
21 #include "graphics/engine/engine.h"
22
23 #include "app/app.h"
24 #include "app/input.h"
25
26 #include "common/image.h"
27 #include "common/key.h"
28 #include "common/logger.h"
29 #include "common/make_unique.h"
30 #include "common/profiler.h"
31 #include "common/stringutils.h"
32
33 #include "common/system/system.h"
34
35 #include "common/thread/resource_owning_thread.h"
36
37 #include "graphics/core/device.h"
38 #include "graphics/core/framebuffer.h"
39
40 #include "graphics/engine/camera.h"
41 #include "graphics/engine/cloud.h"
42 #include "graphics/engine/lightman.h"
43 #include "graphics/engine/lightning.h"
44 #include "graphics/engine/oldmodelmanager.h"
45 #include "graphics/engine/particle.h"
46 #include "graphics/engine/planet.h"
47 #include "graphics/engine/pyro_manager.h"
48 #include "graphics/engine/terrain.h"
49 #include "graphics/engine/text.h"
50 #include "graphics/engine/water.h"
51
52 #include "graphics/model/model_mesh.h"
53 #include "graphics/model/model_shadow_spot.h"
54
55 #include "level/robotmain.h"
56
57 #include "math/geometry.h"
58
59 #include "sound/sound.h"
60
61 #include "ui/controls/interface.h"
62
63 #include <iomanip>
64 #include <SDL_surface.h>
65 #include <SDL_thread.h>
66
67 // Graphics module namespace
68 namespace Gfx
69 {
70
71 /**
72 * \struct EngineMouse
73 * \brief Information about mouse cursor
74 */
75 struct EngineMouse
76 {
77 //! Index of texture element for 1st image
78 int icon1;
79 //! Index of texture element for 2nd image
80 int icon2;
81 //! Shadow texture part
82 int iconShadow;
83 //! Mode to render 1st image in
84 EngineRenderState mode1;
85 //! Mode to render 2nd image in
86 EngineRenderState mode2;
87 //! Hot point
88 Math::IntPoint hotPoint;
89
EngineMouseGfx::EngineMouse90 EngineMouse(int icon1 = -1,
91 int icon2 = -1,
92 int iconShadow = -1,
93 EngineRenderState mode1 = ENG_RSTATE_NORMAL,
94 EngineRenderState mode2 = ENG_RSTATE_NORMAL,
95 Math::IntPoint hotPoint = Math::IntPoint())
96 : icon1(icon1)
97 , icon2(icon2)
98 , iconShadow(iconShadow)
99 , mode1(mode1)
100 , mode2(mode2)
101 , hotPoint(hotPoint)
102 {}
103 };
104
105 const Math::IntPoint MOUSE_SIZE(32, 32);
106 const std::map<EngineMouseType, EngineMouse> MOUSE_TYPES = {
107 {{ENG_MOUSE_NORM}, {EngineMouse( 0, 1, 32, ENG_RSTATE_TTEXTURE_WHITE, ENG_RSTATE_TTEXTURE_BLACK, Math::IntPoint( 1, 1))}},
108 {{ENG_MOUSE_WAIT}, {EngineMouse( 2, 3, 33, ENG_RSTATE_TTEXTURE_WHITE, ENG_RSTATE_TTEXTURE_BLACK, Math::IntPoint( 8, 12))}},
109 {{ENG_MOUSE_HAND}, {EngineMouse( 4, 5, 34, ENG_RSTATE_TTEXTURE_WHITE, ENG_RSTATE_TTEXTURE_BLACK, Math::IntPoint( 7, 2))}},
110 {{ENG_MOUSE_NO}, {EngineMouse( 6, 7, 35, ENG_RSTATE_TTEXTURE_WHITE, ENG_RSTATE_TTEXTURE_BLACK, Math::IntPoint(10, 10))}},
111 {{ENG_MOUSE_EDIT}, {EngineMouse( 8, 9, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint( 6, 10))}},
112 {{ENG_MOUSE_CROSS}, {EngineMouse(10, 11, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint(10, 10))}},
113 {{ENG_MOUSE_MOVEV}, {EngineMouse(12, 13, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint( 5, 11))}},
114 {{ENG_MOUSE_MOVEH}, {EngineMouse(14, 15, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint(11, 5))}},
115 {{ENG_MOUSE_MOVED}, {EngineMouse(16, 17, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint( 9, 9))}},
116 {{ENG_MOUSE_MOVEI}, {EngineMouse(18, 19, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint( 9, 9))}},
117 {{ENG_MOUSE_MOVE}, {EngineMouse(20, 21, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint(11, 11))}},
118 {{ENG_MOUSE_TARGET}, {EngineMouse(22, 23, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint(15, 15))}},
119 {{ENG_MOUSE_SCROLLL}, {EngineMouse(24, 25, 43, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint( 2, 9))}},
120 {{ENG_MOUSE_SCROLLR}, {EngineMouse(26, 27, 44, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint(17, 9))}},
121 {{ENG_MOUSE_SCROLLU}, {EngineMouse(28, 29, 45, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint( 9, 2))}},
122 {{ENG_MOUSE_SCROLLD}, {EngineMouse(30, 31, 46, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::IntPoint( 9, 17))}},
123 };
124
CEngine(CApplication * app,CSystemUtils * systemUtils)125 CEngine::CEngine(CApplication *app, CSystemUtils* systemUtils)
126 : m_app(app),
127 m_systemUtils(systemUtils),
128 m_ambientColor(),
129 m_fogColor(),
130 m_deepView(),
131 m_fogStart(),
132 m_highlightRank()
133 {
134 m_device = nullptr;
135
136 m_lightMan = nullptr;
137 m_text = nullptr;
138 m_particle = nullptr;
139 m_water = nullptr;
140 m_cloud = nullptr;
141 m_lightning = nullptr;
142 m_planet = nullptr;
143 m_sound = nullptr;
144 m_terrain = nullptr;
145
146 m_showStats = false;
147
148 m_focus = 0.75f;
149 m_hfov = 2.0f * atan((640.f/480.f) * tan(m_focus / 2.0f));
150
151 m_rankView = 0;
152
153 m_ambientColor[0] = Color(0.5f, 0.5f, 0.5f, 0.5f);
154 m_ambientColor[1] = Color(0.5f, 0.5f, 0.5f, 0.5f);
155 m_fogColor[0] = Color(1.0f, 1.0f, 1.0f, 1.0f);
156 m_fogColor[1] = Color(1.0f, 1.0f, 1.0f, 1.0f);
157 m_deepView[0] = 1000.0f;
158 m_deepView[1] = 1000.0f;
159 m_fogStart[0] = 0.75f;
160 m_fogStart[1] = 0.75f;
161 m_waterAddColor = Color(0.0f, 0.0f, 0.0f, 0.0f);
162
163 m_render = true;
164 m_renderInterface = true;
165 m_screenshotMode = false;
166 m_dirty = true;
167 m_fog = true;
168 m_secondTex = "";
169 m_eyeDirH = 0.0f;
170 m_eyeDirV = 0.0f;
171 m_backgroundName = ""; // no background image
172 m_backgroundColorUp = Color();
173 m_backgroundColorDown = Color();
174 m_backgroundCloudUp = Color();
175 m_backgroundCloudDown = Color();
176 m_backgroundFull = false;
177 m_backgroundScale = false;
178 m_overFront = true;
179 m_overColor = Color();
180 m_overMode = ENG_RSTATE_TCOLOR_BLACK;
181 m_highlight = false;
182 std::fill_n(m_highlightRank, 100, -1);
183 m_highlightTime = 0.0f;
184 m_eyePt = Math::Vector(0.0f, 0.0f, 0.0f);
185 m_lookatPt = Math::Vector(0.0f, 0.0f, 1.0f);
186 m_drawWorld = true;
187 m_drawFront = false;
188 m_particleDensity = 1.0f;
189 m_clippingDistance = 1.0f;
190 m_terrainVision = 1000.0f;
191 m_textureMipmapLevel = 1;
192 m_textureAnisotropy = 1;
193 m_shadowMapping = true;
194 m_offscreenShadowRendering = true;
195 m_offscreenShadowRenderingResolution = 1024;
196 m_qualityShadows = true;
197 m_terrainShadows = false;
198 m_shadowRange = 0.0f;
199 m_multisample = 2;
200 m_vsync = 0;
201
202 m_backForce = true;
203 m_lightMode = true;
204 m_editIndentMode = true;
205 m_editIndentValue = 4;
206 m_tracePrecision = 1.0f;
207 m_pauseBlurEnabled = true;
208
209
210 m_updateGeometry = false;
211 m_updateStaticBuffers = false;
212
213 m_interfaceMode = false;
214
215 m_debugLights = false;
216 m_debugDumpLights = false;
217
218 m_mouseType = ENG_MOUSE_NORM;
219
220 m_fpsCounter = 0;
221 m_lastFrameTime = m_systemUtils->CreateTimeStamp();
222 m_currentFrameTime = m_systemUtils->CreateTimeStamp();
223
224 m_shadowColor = 0.5f;
225
226 m_defaultTexParams.format = TEX_IMG_AUTO;
227 m_defaultTexParams.filter = TEX_FILTER_BILINEAR;
228
229 m_terrainTexParams.format = TEX_IMG_AUTO;
230 m_terrainTexParams.filter = TEX_FILTER_BILINEAR;
231
232 // Compute bias matrix for shadow mapping
233 Math::Matrix temp1, temp2;
234 Math::LoadScaleMatrix(temp1, Math::Vector(0.5f, 0.5f, 0.5f));
235 Math::LoadTranslationMatrix(temp2, Math::Vector(1.0f, 1.0f, 1.0f));
236 m_shadowBias = Math::MultiplyMatrices(temp1, temp2);
237
238 m_lastState = -1;
239 m_statisticTriangle = 0;
240 m_fps = 0.0f;
241 m_firstGroundSpot = false;
242 }
243
~CEngine()244 CEngine::~CEngine()
245 {
246 m_systemUtils->DestroyTimeStamp(m_lastFrameTime);
247 m_lastFrameTime = nullptr;
248 m_systemUtils->DestroyTimeStamp(m_currentFrameTime);
249 m_currentFrameTime = nullptr;
250 }
251
SetDevice(CDevice * device)252 void CEngine::SetDevice(CDevice *device)
253 {
254 m_device = device;
255 }
256
GetDevice()257 CDevice* CEngine::GetDevice()
258 {
259 return m_device;
260 }
261
GetModelManager()262 COldModelManager* CEngine::GetModelManager()
263 {
264 return m_modelManager.get();
265 }
266
GetPyroManager()267 CPyroManager* CEngine::GetPyroManager()
268 {
269 return m_pyroManager.get();
270 }
271
GetText()272 CText* CEngine::GetText()
273 {
274 return m_text.get();
275 }
276
GetLightManager()277 CLightManager* CEngine::GetLightManager()
278 {
279 return m_lightMan.get();
280 }
281
GetParticle()282 CParticle* CEngine::GetParticle()
283 {
284 return m_particle.get();
285 }
286
GetTerrain()287 CTerrain* CEngine::GetTerrain()
288 {
289 return m_terrain;
290 }
291
GetWater()292 CWater* CEngine::GetWater()
293 {
294 return m_water.get();
295 }
296
GetLightning()297 CLightning* CEngine::GetLightning()
298 {
299 return m_lightning.get();
300 }
301
GetPlanet()302 CPlanet* CEngine::GetPlanet()
303 {
304 return m_planet.get();
305 }
306
GetCloud()307 CCloud* CEngine::GetCloud()
308 {
309 return m_cloud.get();
310 }
311
SetTerrain(CTerrain * terrain)312 void CEngine::SetTerrain(CTerrain* terrain)
313 {
314 m_terrain = terrain;
315 }
316
Create()317 bool CEngine::Create()
318 {
319 m_size = m_app->GetVideoConfig().size;
320
321 // Use the setters to set defaults, because they automatically disable what is not supported
322 SetShadowMapping(m_shadowMapping);
323 SetShadowMappingQuality(m_qualityShadows);
324 SetShadowMappingOffscreen(m_offscreenShadowRendering);
325 SetShadowMappingOffscreenResolution(m_offscreenShadowRenderingResolution);
326 SetMultiSample(m_multisample);
327 SetVSync(m_vsync);
328
329 m_modelManager = MakeUnique<COldModelManager>(this);
330 m_pyroManager = MakeUnique<CPyroManager>();
331 m_lightMan = MakeUnique<CLightManager>(this);
332 m_text = MakeUnique<CText>(this);
333 m_particle = MakeUnique<CParticle>(this);
334 m_water = MakeUnique<CWater>(this);
335 m_cloud = MakeUnique<CCloud>(this);
336 m_lightning = MakeUnique<CLightning>(this);
337 m_planet = MakeUnique<CPlanet>(this);
338
339 m_lightMan->SetDevice(m_device);
340 m_particle->SetDevice(m_device);
341
342 m_text->SetDevice(m_device);
343 if (! m_text->Create())
344 {
345 std::string error = m_text->GetError();
346 GetLogger()->Error("Error creating CText: %s\n", error.c_str());
347 return false;
348 }
349
350 m_device->SetClearColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
351 m_device->SetShadeModel(SHADE_SMOOTH);
352 m_device->SetFillMode(FILL_POLY);
353
354 SetFocus(m_focus);
355
356 m_matWorldInterface.LoadIdentity();
357 m_matViewInterface.LoadIdentity();
358
359 Math::LoadOrthoProjectionMatrix(m_matProjInterface, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
360
361 TextureCreateParams params;
362 params.format = TEX_IMG_AUTO;
363 params.filter = TEX_FILTER_NEAREST;
364 params.mipmap = false;
365 m_miceTexture = LoadTexture("textures/interface/mouse.png", params);
366
367 m_systemUtils->GetCurrentTimeStamp(m_currentFrameTime);
368 m_systemUtils->GetCurrentTimeStamp(m_lastFrameTime);
369
370 return true;
371 }
372
Destroy()373 void CEngine::Destroy()
374 {
375 m_text->Destroy();
376
377 if (m_shadowMap.id != 0)
378 {
379 if (m_offscreenShadowRendering)
380 m_device->DeleteFramebuffer("shadow");
381 else
382 m_device->DestroyTexture(m_shadowMap);
383
384 m_shadowMap = Texture();
385 }
386
387 m_lightMan.reset();
388 m_text.reset();
389 m_particle.reset();
390 m_water.reset();
391 m_cloud.reset();
392 m_lightning.reset();
393 m_planet.reset();
394 }
395
ResetAfterVideoConfigChanged()396 void CEngine::ResetAfterVideoConfigChanged()
397 {
398 m_size = m_app->GetVideoConfig().size;
399
400 // Update the camera projection matrix for new aspect ratio
401 ApplyChange();
402
403 // This needs to be recreated on resolution change
404 m_device->DeleteFramebuffer("multisample");
405 }
406
ReloadAllTextures()407 void CEngine::ReloadAllTextures()
408 {
409 FlushTextureCache();
410 m_text->FlushCache();
411 m_text->ReloadFonts();
412
413 m_app->GetEventQueue()->AddEvent(Event(EVENT_RELOAD_TEXTURES));
414 UpdateGroundSpotTextures();
415 // LoadAllTextures() is called from CRobotMain on EVENT_RELOAD_TEXTURES
416 // This is required because all dynamic textures need to be loaded first
417
418 // recapture 3D scene
419 if (m_worldCaptured)
420 {
421 m_captureWorld = true;
422 m_worldCaptured = false;
423 }
424 }
425
ProcessEvent(const Event & event)426 bool CEngine::ProcessEvent(const Event &event)
427 {
428 if (event.type == EVENT_RESOLUTION_CHANGED)
429 {
430 ResetAfterVideoConfigChanged();
431 }
432
433 if (event.type == EVENT_KEY_DOWN)
434 {
435 auto data = event.GetData<KeyEventData>();
436
437 if (data->key == KEY(F11) || data->key == KEY(F12))
438 {
439 m_showStats = !m_showStats;
440 return false;
441 }
442 }
443
444 // By default, pass on all events
445 return true;
446 }
447
FrameUpdate()448 void CEngine::FrameUpdate()
449 {
450 float rTime = m_app->GetRelTime();
451
452 m_lightMan->UpdateProgression(rTime);
453
454 CProfiler::StartPerformanceCounter(PCNT_UPDATE_PARTICLE);
455 m_particle->FrameParticle(rTime);
456 CProfiler::StopPerformanceCounter(PCNT_UPDATE_PARTICLE);
457
458 ComputeDistance();
459 UpdateGeometry();
460 UpdateStaticBuffers();
461
462 m_highlightTime = m_app->GetAbsTime();
463
464 if (m_groundMark.draw)
465 {
466 if (m_groundMark.phase == ENG_GR_MARK_PHASE_INC) // growing?
467 {
468 m_groundMark.intensity += rTime*(1.0f/m_groundMark.delay[0]);
469 if (m_groundMark.intensity >= 1.0f)
470 {
471 m_groundMark.intensity = 1.0f;
472 m_groundMark.fix = 0.0f;
473 m_groundMark.phase = ENG_GR_MARK_PHASE_FIX;
474 }
475 }
476 else if (m_groundMark.phase == ENG_GR_MARK_PHASE_FIX) // fixed?
477 {
478 m_groundMark.fix += rTime*(1.0f/m_groundMark.delay[1]);
479 if (m_groundMark.fix >= 1.0f)
480 m_groundMark.phase = ENG_GR_MARK_PHASE_DEC;
481 }
482 else if (m_groundMark.phase == ENG_GR_MARK_PHASE_DEC) // decay?
483 {
484 m_groundMark.intensity -= rTime*(1.0f/m_groundMark.delay[2]);
485 if (m_groundMark.intensity < 0.0f)
486 {
487 m_groundMark.intensity = 0.0f;
488 m_groundMark.phase = ENG_GR_MARK_PHASE_NULL;
489 m_groundMark.draw = false;
490 }
491 }
492 }
493 }
494
WriteScreenShot(const std::string & fileName)495 void CEngine::WriteScreenShot(const std::string& fileName)
496 {
497 auto data = MakeUnique<WriteScreenShotData>();
498 data->img = MakeUnique<CImage>(Math::IntPoint(m_size.x, m_size.y));
499
500 auto pixels = m_device->GetFrameBufferPixels();
501 data->img->SetDataPixels(pixels->GetPixelsData());
502 data->img->FlipVertically();
503
504 data->fileName = fileName;
505
506 CResourceOwningThread<WriteScreenShotData> thread(CEngine::WriteScreenShotThread, std::move(data), "WriteScreenShot thread");
507 thread.Start();
508 }
509
WriteScreenShotThread(std::unique_ptr<WriteScreenShotData> data)510 void CEngine::WriteScreenShotThread(std::unique_ptr<WriteScreenShotData> data)
511 {
512 if ( data->img->SavePNG(data->fileName.c_str()) )
513 {
514 GetLogger()->Debug("Save screenshot saved successfully\n");
515 }
516 else
517 {
518 GetLogger()->Error("%s!\n", data->img->GetError().c_str());
519 }
520
521 CApplication::GetInstancePointer()->GetEventQueue()->AddEvent(Event(EVENT_WRITE_SCENE_FINISHED));
522 }
523
SetPause(bool pause)524 void CEngine::SetPause(bool pause)
525 {
526 m_pause = pause;
527 }
528
GetPause()529 bool CEngine::GetPause()
530 {
531 return m_pause;
532 }
533
SetShowStats(bool show)534 void CEngine::SetShowStats(bool show)
535 {
536 m_showStats = show;
537 }
538
GetShowStats()539 bool CEngine::GetShowStats()
540 {
541 return m_showStats;
542 }
543
SetRenderEnable(bool enable)544 void CEngine::SetRenderEnable(bool enable)
545 {
546 m_render = enable;
547 }
548
SetRenderInterface(bool enable)549 void CEngine::SetRenderInterface(bool enable)
550 {
551 m_renderInterface = enable;
552 }
553
GetRenderInterface()554 bool CEngine::GetRenderInterface()
555 {
556 return m_renderInterface;
557 }
558
SetScreenshotMode(bool screenshotMode)559 void CEngine::SetScreenshotMode(bool screenshotMode)
560 {
561 m_screenshotMode = screenshotMode;
562 }
563
GetScreenshotMode()564 bool CEngine::GetScreenshotMode()
565 {
566 return m_screenshotMode;
567 }
568
GetWindowSize()569 Math::IntPoint CEngine::GetWindowSize()
570 {
571 return m_size;
572 }
573
WindowToInterfaceCoords(Math::IntPoint pos)574 Math::Point CEngine::WindowToInterfaceCoords(Math::IntPoint pos)
575 {
576 return Math::Point( static_cast<float>(pos.x) / static_cast<float>(m_size.x),
577 1.0f - static_cast<float>(pos.y) / static_cast<float>(m_size.y) );
578 }
579
InterfaceToWindowCoords(Math::Point pos)580 Math::IntPoint CEngine::InterfaceToWindowCoords(Math::Point pos)
581 {
582 return Math::IntPoint(static_cast<int>(pos.x * m_size.x),
583 static_cast<int>((1.0f - pos.y) * m_size.y));
584 }
585
WindowToInterfaceSize(Math::IntPoint size)586 Math::Point CEngine::WindowToInterfaceSize(Math::IntPoint size)
587 {
588 return Math::Point(static_cast<float>(size.x) / static_cast<float>(m_size.x),
589 static_cast<float>(size.y) / static_cast<float>(m_size.y));
590 }
591
InterfaceToWindowSize(Math::Point size)592 Math::IntPoint CEngine::InterfaceToWindowSize(Math::Point size)
593 {
594 return Math::IntPoint(static_cast<int>(size.x * m_size.x),
595 static_cast<int>(size.y * m_size.y));
596 }
597
AddStatisticTriangle(int count)598 void CEngine::AddStatisticTriangle(int count)
599 {
600 m_statisticTriangle += count;
601 }
602
GetStatisticTriangle()603 int CEngine::GetStatisticTriangle()
604 {
605 return m_statisticTriangle;
606 }
607
SetStatisticPos(Math::Vector pos)608 void CEngine::SetStatisticPos(Math::Vector pos)
609 {
610 m_statisticPos = pos;
611 }
612
SetTimerDisplay(const std::string & text)613 void CEngine::SetTimerDisplay(const std::string& text)
614 {
615 m_timerText = text;
616 }
617
618
619
620 /*******************************************************
621 Object management
622 *******************************************************/
623
AddLevel2(EngineBaseObject & p1,const std::string & tex1Name,const std::string & tex2Name)624 EngineBaseObjTexTier& CEngine::AddLevel2(EngineBaseObject& p1, const std::string& tex1Name, const std::string& tex2Name)
625 {
626 for (int i = 0; i < static_cast<int>( p1.next.size() ); i++)
627 {
628 if (p1.next[i].tex1Name == tex1Name && p1.next[i].tex2Name == tex2Name)
629 return p1.next[i];
630 }
631
632 p1.next.push_back(EngineBaseObjTexTier(tex1Name, tex2Name));
633 return p1.next.back();
634 }
635
AddLevel3(EngineBaseObjTexTier & p3,EngineTriangleType type,const Material & material,int state)636 EngineBaseObjDataTier& CEngine::AddLevel3(EngineBaseObjTexTier& p3, EngineTriangleType type,
637 const Material& material, int state)
638 {
639 for (int i = 0; i < static_cast<int>( p3.next.size() ); i++)
640 {
641 if ( (p3.next[i].type == type) && (p3.next[i].material == material) && (p3.next[i].state == state) )
642 return p3.next[i];
643 }
644
645 p3.next.push_back(EngineBaseObjDataTier(type, material, state));
646 return p3.next.back();
647 }
648
CreateBaseObject()649 int CEngine::CreateBaseObject()
650 {
651 int baseObjRank = 0;
652 for ( ; baseObjRank < static_cast<int>( m_baseObjects.size() ); baseObjRank++)
653 {
654 if (! m_baseObjects[baseObjRank].used)
655 {
656 m_baseObjects[baseObjRank].LoadDefault();
657 break;
658 }
659 }
660
661 if (baseObjRank == static_cast<int>( m_baseObjects.size() ))
662 m_baseObjects.push_back(EngineBaseObject());
663 else
664 m_baseObjects[baseObjRank].LoadDefault();
665
666
667 m_baseObjects[baseObjRank].used = true;
668
669 return baseObjRank;
670 }
671
DeleteBaseObject(int baseObjRank)672 void CEngine::DeleteBaseObject(int baseObjRank)
673 {
674 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
675
676 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
677
678 if (! p1.used)
679 return;
680
681 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
682 {
683 EngineBaseObjTexTier& p2 = p1.next[l2];
684
685 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
686 {
687 EngineBaseObjDataTier& p3 = p2.next[l3];
688 m_device->DestroyStaticBuffer(p3.staticBufferId);
689 p3.staticBufferId = 0;
690 }
691 }
692
693 p1.next.clear();
694
695 p1.used = false;
696 }
697
DeleteAllBaseObjects()698 void CEngine::DeleteAllBaseObjects()
699 {
700 for (int baseObjRank = 0; baseObjRank < static_cast<int>( m_baseObjects.size() ); baseObjRank++)
701 {
702 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
703 if (! p1.used)
704 continue;
705
706 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
707 {
708 EngineBaseObjTexTier& p2 = p1.next[l2];
709
710 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
711 {
712 EngineBaseObjDataTier& p3 = p2.next[l3];
713 m_device->DestroyStaticBuffer(p3.staticBufferId);
714 p3.staticBufferId = 0;
715 }
716 }
717 }
718
719 m_baseObjects.clear();
720 }
721
CopyBaseObject(int sourceBaseObjRank,int destBaseObjRank)722 void CEngine::CopyBaseObject(int sourceBaseObjRank, int destBaseObjRank)
723 {
724 assert(sourceBaseObjRank >= 0 && sourceBaseObjRank < static_cast<int>( m_baseObjects.size() ));
725 assert(destBaseObjRank >= 0 && destBaseObjRank < static_cast<int>( m_baseObjects.size() ));
726
727 m_baseObjects[destBaseObjRank] = m_baseObjects[sourceBaseObjRank];
728
729 EngineBaseObject& p1 = m_baseObjects[destBaseObjRank];
730
731 if (! p1.used)
732 return;
733
734 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
735 {
736 EngineBaseObjTexTier& p2 = p1.next[l2];
737
738 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
739 {
740 EngineBaseObjDataTier& p3 = p2.next[l3];
741 p3.staticBufferId = 0;
742 }
743 }
744 }
745
AddBaseObjTriangles(int baseObjRank,const std::vector<VertexTex2> & vertices,const Material & material,int state,std::string tex1Name,std::string tex2Name)746 void CEngine::AddBaseObjTriangles(int baseObjRank, const std::vector<VertexTex2>& vertices,
747 const Material& material, int state,
748 std::string tex1Name, std::string tex2Name)
749 {
750 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
751
752 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
753 EngineBaseObjTexTier& p2 = AddLevel2(p1, tex1Name, tex2Name);
754 EngineBaseObjDataTier& p3 = AddLevel3(p2, ENG_TRIANGLE_TYPE_TRIANGLES, material, state);
755
756 p3.vertices.insert(p3.vertices.end(), vertices.begin(), vertices.end());
757
758 p3.updateStaticBuffer = true;
759 m_updateStaticBuffers = true;
760
761 for (int i = 0; i < static_cast<int>( vertices.size() ); i++)
762 {
763 p1.bboxMin.x = Math::Min(vertices[i].coord.x, p1.bboxMin.x);
764 p1.bboxMin.y = Math::Min(vertices[i].coord.y, p1.bboxMin.y);
765 p1.bboxMin.z = Math::Min(vertices[i].coord.z, p1.bboxMin.z);
766 p1.bboxMax.x = Math::Max(vertices[i].coord.x, p1.bboxMax.x);
767 p1.bboxMax.y = Math::Max(vertices[i].coord.y, p1.bboxMax.y);
768 p1.bboxMax.z = Math::Max(vertices[i].coord.z, p1.bboxMax.z);
769 }
770
771 p1.boundingSphere = Math::BoundingSphereForBox(p1.bboxMin, p1.bboxMax);
772
773 p1.totalTriangles += vertices.size() / 3;
774 }
775
AddBaseObjQuick(int baseObjRank,const EngineBaseObjDataTier & buffer,std::string tex1Name,std::string tex2Name,bool globalUpdate)776 void CEngine::AddBaseObjQuick(int baseObjRank, const EngineBaseObjDataTier& buffer,
777 std::string tex1Name, std::string tex2Name,
778 bool globalUpdate)
779 {
780 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
781
782 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
783 EngineBaseObjTexTier& p2 = AddLevel2(p1, tex1Name, tex2Name);
784
785 p2.next.push_back(buffer);
786
787 EngineBaseObjDataTier& p3 = p2.next.back();
788
789 UpdateStaticBuffer(p3);
790
791 if (globalUpdate)
792 {
793 m_updateGeometry = true;
794 }
795 else
796 {
797 for (int i = 0; i < static_cast<int>( p3.vertices.size() ); i++)
798 {
799 p1.bboxMin.x = Math::Min(p3.vertices[i].coord.x, p1.bboxMin.x);
800 p1.bboxMin.y = Math::Min(p3.vertices[i].coord.y, p1.bboxMin.y);
801 p1.bboxMin.z = Math::Min(p3.vertices[i].coord.z, p1.bboxMin.z);
802 p1.bboxMax.x = Math::Max(p3.vertices[i].coord.x, p1.bboxMax.x);
803 p1.bboxMax.y = Math::Max(p3.vertices[i].coord.y, p1.bboxMax.y);
804 p1.bboxMax.z = Math::Max(p3.vertices[i].coord.z, p1.bboxMax.z);
805 }
806
807 p1.boundingSphere = Math::BoundingSphereForBox(p1.bboxMin, p1.bboxMax);
808 }
809
810 if (p3.type == ENG_TRIANGLE_TYPE_TRIANGLES)
811 p1.totalTriangles += p3.vertices.size() / 3;
812 else if (p3.type == ENG_TRIANGLE_TYPE_SURFACE)
813 p1.totalTriangles += p3.vertices.size() - 2;
814 }
815
DebugObject(int objRank)816 void CEngine::DebugObject(int objRank)
817 {
818 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
819
820 CLogger* l = GetLogger();
821
822 l->Debug("Debug object: %d\n", objRank);
823 if (! m_objects[objRank].used)
824 {
825 l->Debug(" not used\n");
826 return;
827 }
828
829 l->Debug(" baseObjRank = %d\n", m_objects[objRank].baseObjRank);
830 l->Debug(" visible = %s\n", m_objects[objRank].visible ? "true" : "false");
831 l->Debug(" drawWorld = %s\n", m_objects[objRank].drawWorld ? "true" : "false");
832 l->Debug(" drawFront = %s\n", m_objects[objRank].drawFront ? "true" : "false");
833 l->Debug(" type = %d\n", m_objects[objRank].type);
834 l->Debug(" distance = %f\n", m_objects[objRank].distance);
835 l->Debug(" shadowRank = %d\n", m_objects[objRank].shadowRank);
836 l->Debug(" transparency = %f\n", m_objects[objRank].transparency);
837
838 l->Debug(" baseObj:\n");
839 int baseObjRank = m_objects[objRank].baseObjRank;
840 if (baseObjRank == -1)
841 {
842 l->Debug(" null\n");
843 return;
844 }
845
846 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
847
848 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
849 if (!p1.used)
850 {
851 l->Debug(" not used\n");
852 return;
853 }
854
855 std::string vecStr;
856
857 vecStr = p1.bboxMin.ToString();
858 l->Debug(" bboxMin: %s\n", vecStr.c_str());
859 vecStr = p1.bboxMax.ToString();
860 l->Debug(" bboxMax: %s\n", vecStr.c_str());
861 l->Debug(" totalTriangles: %d\n", p1.totalTriangles);
862 l->Debug(" radius: %f\n", p1.boundingSphere.radius);
863
864 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
865 {
866 EngineBaseObjTexTier& p2 = p1.next[l2];
867 l->Debug(" l2:\n");
868
869 l->Debug(" tex1: %s (id: %u)\n", p2.tex1Name.c_str(), p2.tex1.id);
870 l->Debug(" tex2: %s (id: %u)\n", p2.tex2Name.c_str(), p2.tex2.id);
871
872 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
873 {
874 EngineBaseObjDataTier& p3 = p2.next[l3];
875
876 l->Debug(" l3:\n");
877 l->Debug(" type: %d\n", p3.type);
878 l->Debug(" state: %d\n", p3.state);
879 l->Debug(" staticBufferId: %u\n", p3.staticBufferId);
880 l->Debug(" updateStaticBuffer: %s\n", p3.updateStaticBuffer ? "true" : "false");
881 }
882 }
883 }
884
CreateObject()885 int CEngine::CreateObject()
886 {
887 int objRank = 0;
888 for ( ; objRank < static_cast<int>( m_objects.size() ); objRank++)
889 {
890 if (! m_objects[objRank].used)
891 {
892 m_objects[objRank].LoadDefault();
893 break;
894 }
895 }
896
897 if (objRank == static_cast<int>( m_objects.size() ))
898 m_objects.push_back(EngineObject());
899
900
901 m_objects[objRank].used = true;
902
903 Math::Matrix mat;
904 mat.LoadIdentity();
905 SetObjectTransform(objRank, mat);
906
907 m_objects[objRank].drawWorld = true;
908 m_objects[objRank].distance = 0.0f;
909 m_objects[objRank].baseObjRank = -1;
910 m_objects[objRank].shadowRank = -1;
911
912 return objRank;
913 }
914
DeleteAllObjects()915 void CEngine::DeleteAllObjects()
916 {
917 m_objects.clear();
918 m_shadowSpots.clear();
919
920 DeleteAllGroundSpots();
921 }
922
DeleteObject(int objRank)923 void CEngine::DeleteObject(int objRank)
924 {
925 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
926
927 // Mark object as deleted
928 m_objects[objRank].used = false;
929
930 // Delete associated shadows
931 DeleteShadowSpot(objRank);
932 }
933
SetObjectBaseRank(int objRank,int baseObjRank)934 void CEngine::SetObjectBaseRank(int objRank, int baseObjRank)
935 {
936 assert(objRank == -1 || (objRank >= 0 && objRank < static_cast<int>( m_objects.size() )));
937
938 m_objects[objRank].baseObjRank = baseObjRank;
939 }
940
GetObjectBaseRank(int objRank)941 int CEngine::GetObjectBaseRank(int objRank)
942 {
943 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
944
945 return m_objects[objRank].baseObjRank;
946 }
947
SetObjectType(int objRank,EngineObjectType type)948 void CEngine::SetObjectType(int objRank, EngineObjectType type)
949 {
950 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
951
952 m_objects[objRank].type = type;
953 }
954
GetObjectType(int objRank)955 EngineObjectType CEngine::GetObjectType(int objRank)
956 {
957 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
958
959 return m_objects[objRank].type;
960 }
961
962
SetObjectTransform(int objRank,const Math::Matrix & transform)963 void CEngine::SetObjectTransform(int objRank, const Math::Matrix& transform)
964 {
965 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
966
967 m_objects[objRank].transform = transform;
968 }
969
GetObjectTransform(int objRank,Math::Matrix & transform)970 void CEngine::GetObjectTransform(int objRank, Math::Matrix& transform)
971 {
972 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
973
974 transform = m_objects[objRank].transform;
975 }
976
SetObjectDrawWorld(int objRank,bool draw)977 void CEngine::SetObjectDrawWorld(int objRank, bool draw)
978 {
979 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
980
981 m_objects[objRank].drawWorld = draw;
982 }
983
SetObjectDrawFront(int objRank,bool draw)984 void CEngine::SetObjectDrawFront(int objRank, bool draw)
985 {
986 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
987
988 m_objects[objRank].drawFront = draw;
989 }
990
SetObjectTransparency(int objRank,float value)991 void CEngine::SetObjectTransparency(int objRank, float value)
992 {
993 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
994
995 m_objects[objRank].transparency = value;
996 }
997
GetObjectBBox(int objRank,Math::Vector & min,Math::Vector & max)998 void CEngine::GetObjectBBox(int objRank, Math::Vector& min, Math::Vector& max)
999 {
1000 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1001
1002 int baseObjRank = m_objects[objRank].baseObjRank;
1003 if (baseObjRank == -1)
1004 return;
1005
1006 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
1007
1008 min = m_baseObjects[baseObjRank].bboxMin;
1009 max = m_baseObjects[baseObjRank].bboxMax;
1010 }
1011
1012
GetObjectTotalTriangles(int objRank)1013 int CEngine::GetObjectTotalTriangles(int objRank)
1014 {
1015 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1016
1017 int baseObjRank = m_objects[objRank].baseObjRank;
1018 if (baseObjRank == -1)
1019 return 0;
1020
1021 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
1022
1023 return m_baseObjects[baseObjRank].totalTriangles;
1024 }
1025
FindTriangles(int objRank,const Material & material,int state,std::string tex1Name,std::string tex2Name)1026 EngineBaseObjDataTier* CEngine::FindTriangles(int objRank, const Material& material,
1027 int state, std::string tex1Name,
1028 std::string tex2Name)
1029 {
1030 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1031
1032 int baseObjRank = m_objects[objRank].baseObjRank;
1033 if (baseObjRank == -1)
1034 return nullptr;
1035
1036 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
1037
1038 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
1039
1040 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
1041 {
1042 EngineBaseObjTexTier& p2 = p1.next[l2];
1043
1044 if (p2.tex1Name != tex1Name)
1045 continue;
1046
1047 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
1048 {
1049 EngineBaseObjDataTier& p3 = p2.next[l3];
1050
1051 if ( (p3.state & (~(ENG_RSTATE_DUAL_BLACK|ENG_RSTATE_DUAL_WHITE))) != state ||
1052 p3.material != material )
1053 continue;
1054
1055 return &p3;
1056 }
1057 }
1058
1059 return nullptr;
1060 }
1061
GetPartialTriangles(int objRank,float percent,int maxCount,std::vector<EngineTriangle> & triangles)1062 int CEngine::GetPartialTriangles(int objRank, float percent, int maxCount,
1063 std::vector<EngineTriangle>& triangles)
1064 {
1065 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1066
1067 int baseObjRank = m_objects[objRank].baseObjRank;
1068 if (baseObjRank == -1)
1069 return 0;
1070
1071 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
1072
1073 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
1074
1075 int total = p1.totalTriangles;
1076 int expectedCount = static_cast<int>(percent * total);
1077 triangles.reserve(Math::Min(maxCount, expectedCount));
1078
1079 int actualCount = 0;
1080
1081 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
1082 {
1083 EngineBaseObjTexTier& p2 = p1.next[l2];
1084
1085 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
1086 {
1087 EngineBaseObjDataTier& p3 = p2.next[l3];
1088
1089 if (p3.type == ENG_TRIANGLE_TYPE_TRIANGLES)
1090 {
1091 for (int i = 0; i < static_cast<int>( p3.vertices.size() ); i += 3)
1092 {
1093 if (static_cast<float>(actualCount) / total >= percent)
1094 break;
1095
1096 if (actualCount >= maxCount)
1097 break;
1098
1099 EngineTriangle t;
1100 t.triangle[0] = p3.vertices[i];
1101 t.triangle[1] = p3.vertices[i+1];
1102 t.triangle[2] = p3.vertices[i+2];
1103 t.material = p3.material;
1104 t.state = p3.state;
1105 t.tex1Name = p2.tex1Name;
1106 t.tex2Name = p2.tex2Name;
1107
1108 triangles.push_back(t);
1109
1110 ++actualCount;
1111 }
1112 }
1113 else if (p3.type == ENG_TRIANGLE_TYPE_SURFACE)
1114 {
1115 for (int i = 0; i < static_cast<int>( p3.vertices.size() ); i += 1)
1116 {
1117 if (static_cast<float>(actualCount) / total >= percent)
1118 break;
1119
1120 if (actualCount >= maxCount)
1121 break;
1122
1123 EngineTriangle t;
1124 t.triangle[0] = p3.vertices[i];
1125 t.triangle[1] = p3.vertices[i+1];
1126 t.triangle[2] = p3.vertices[i+2];
1127 t.material = p3.material;
1128 t.state = p3.state;
1129 t.tex1Name = p2.tex1Name;
1130 t.tex2Name = p2.tex2Name;
1131
1132 triangles.push_back(t);
1133
1134 ++actualCount;
1135 }
1136 }
1137 }
1138 }
1139
1140 return actualCount;
1141 }
1142
ChangeSecondTexture(int objRank,const std::string & tex2Name)1143 void CEngine::ChangeSecondTexture(int objRank, const std::string& tex2Name)
1144 {
1145 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1146
1147 int baseObjRank = m_objects[objRank].baseObjRank;
1148 if (baseObjRank == -1)
1149 return;
1150
1151 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
1152
1153 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
1154
1155 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
1156 {
1157 if (p1.next[l2].tex2Name == tex2Name)
1158 continue; // already new
1159
1160 std::string tex1Name = p1.next[l2].tex1Name;
1161 EngineBaseObjTexTier& newP2 = AddLevel2(p1, tex1Name, tex2Name);
1162 newP2.next.insert(newP2.next.end(), p1.next[l2].next.begin(), p1.next[l2].next.end());
1163 p1.next[l2].next.clear();
1164
1165 if (!newP2.tex1.Valid() && !newP2.tex1Name.empty())
1166 newP2.tex1 = LoadTexture("textures/"+newP2.tex1Name);
1167
1168 if (!newP2.tex2.Valid() && !newP2.tex2Name.empty())
1169 newP2.tex2 = LoadTexture("textures/"+newP2.tex2Name);
1170 }
1171 }
1172
ChangeTextureMapping(int objRank,const Material & mat,int state,const std::string & tex1Name,const std::string & tex2Name,EngineTextureMapping mode,float au,float bu,float av,float bv)1173 void CEngine::ChangeTextureMapping(int objRank, const Material& mat, int state,
1174 const std::string& tex1Name, const std::string& tex2Name,
1175 EngineTextureMapping mode,
1176 float au, float bu, float av, float bv)
1177 {
1178 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1179
1180 EngineBaseObjDataTier* p4 = FindTriangles(objRank, mat, state, tex1Name, tex2Name);
1181 if (p4 == nullptr)
1182 return;
1183
1184 int nb = p4->vertices.size();
1185
1186 if (mode == ENG_TEX_MAPPING_X)
1187 {
1188 for (int i = 0; i < nb; i++)
1189 {
1190 p4->vertices[i].texCoord.x = p4->vertices[i].coord.z * au + bu;
1191 p4->vertices[i].texCoord.y = p4->vertices[i].coord.y * av + bv;
1192 }
1193 }
1194 else if (mode == ENG_TEX_MAPPING_Y)
1195 {
1196 for (int i = 0; i < nb; i++)
1197 {
1198 p4->vertices[i].texCoord.x = p4->vertices[i].coord.x * au + bu;
1199 p4->vertices[i].texCoord.y = p4->vertices[i].coord.z * av + bv;
1200 }
1201 }
1202 else if (mode == ENG_TEX_MAPPING_Z)
1203 {
1204 for (int i = 0; i < nb; i++)
1205 {
1206 p4->vertices[i].texCoord.x = p4->vertices[i].coord.x * au + bu;
1207 p4->vertices[i].texCoord.y = p4->vertices[i].coord.y * av + bv;
1208 }
1209 }
1210 else if (mode == ENG_TEX_MAPPING_1X)
1211 {
1212 for (int i = 0; i < nb; i++)
1213 {
1214 p4->vertices[i].texCoord.x = p4->vertices[i].coord.x * au + bu;
1215 }
1216 }
1217 else if (mode == ENG_TEX_MAPPING_1Y)
1218 {
1219 for (int i = 0; i < nb; i++)
1220 {
1221 p4->vertices[i].texCoord.y = p4->vertices[i].coord.y * au + bu;
1222 }
1223 }
1224 else if (mode == ENG_TEX_MAPPING_1Z)
1225 {
1226 for (int i = 0; i < nb; i++)
1227 {
1228 p4->vertices[i].texCoord.x = p4->vertices[i].coord.z * au + bu;
1229 }
1230 }
1231
1232 UpdateStaticBuffer(*p4);
1233 }
1234
TrackTextureMapping(int objRank,const Material & mat,int state,const std::string & tex1Name,const std::string & tex2Name,EngineTextureMapping mode,float pos,float factor,float tl,float ts,float tt)1235 void CEngine::TrackTextureMapping(int objRank, const Material& mat, int state,
1236 const std::string& tex1Name, const std::string& tex2Name,
1237 EngineTextureMapping mode,
1238 float pos, float factor, float tl, float ts, float tt)
1239 {
1240 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1241
1242 EngineBaseObjDataTier* p4 = FindTriangles(objRank, mat, state, tex1Name, tex2Name);
1243 if (p4 == nullptr)
1244 return;
1245
1246 int tNum = p4->vertices.size();
1247 if (tNum < 12 || tNum % 6 != 0)
1248 return;
1249
1250 std::vector<Gfx::VertexTex2>& vs = p4->vertices;
1251
1252 while (pos < 0.0f)
1253 pos += 1.0f; // never negative!
1254
1255 Math::Vector current;
1256
1257 for (int i = 0; i < 6; i++)
1258 {
1259 for (int j = 0; j < 6; j++)
1260 {
1261 if (Math::IsEqual(vs[i].coord.x, vs[j+6].coord.x) &&
1262 Math::IsEqual(vs[i].coord.y, vs[j+6].coord.y))
1263 {
1264 current.x = vs[i].coord.x; // position end link
1265 current.y = vs[i].coord.y;
1266 break;
1267 }
1268 }
1269 }
1270
1271 float ps = 0.0f; // start position on the periphery
1272 float pe = 0.0f;
1273 int is[6] = { 0 }, ie[6] = { 0 };
1274
1275 int tBase = 0;
1276 for (int ti = 0; ti < tNum / 6; ti++)
1277 {
1278 int s = 0;
1279 int e = 0;
1280
1281 for (int i = 0; i < 6; i++)
1282 {
1283 if (Math::IsEqual(vs[tBase + i].coord.x, current.x, 0.0001f) &&
1284 Math::IsEqual(vs[tBase + i].coord.y, current.y, 0.0001f))
1285 {
1286 ie[e++] = i;
1287 }
1288 else
1289 {
1290 is[s++] = i;
1291 }
1292 }
1293 if (s == 3 && e == 3)
1294 {
1295 pe = ps + Math::Point(vs[tBase + is[0]].coord.x - vs[tBase + ie[0]].coord.x,
1296 vs[tBase + is[0]].coord.y - vs[tBase + ie[0]].coord.y).Length() / factor; // end position on the periphery
1297
1298 float pps = ps + pos;
1299 float ppe = pe + pos;
1300 int offset = static_cast<int>(pps);
1301 ppe -= offset;
1302 pps -= offset;
1303
1304 for (int i = 0; i < 3; i++)
1305 {
1306 vs[tBase + is[i]].texCoord.x = ((pps * tl) + ts) / tt;
1307 vs[tBase + ie[i]].texCoord.x = ((ppe * tl) + ts) / tt;
1308 }
1309 }
1310
1311 if (ti >= (tNum / 6) - 1)
1312 break;
1313
1314 for (int i = 0; i < 6; i++)
1315 {
1316 if (!Math::IsEqual(vs[tBase + i+6].coord.x, current.x, 0.0001f) ||
1317 !Math::IsEqual(vs[tBase + i+6].coord.y, current.y, 0.0001f))
1318 {
1319 current.x = vs[tBase + i+6].coord.x; // end next link
1320 current.y = vs[tBase + i+6].coord.y;
1321 break;
1322 }
1323 }
1324 ps = pe; // following start position on the periphery
1325 tBase += 6;
1326 }
1327
1328 UpdateStaticBuffer(*p4);
1329 }
1330
1331
CreateShadowSpot(int objRank)1332 void CEngine::CreateShadowSpot(int objRank)
1333 {
1334 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1335
1336 // Already allocated?
1337 if (m_objects[objRank].shadowRank != -1)
1338 return;
1339
1340 int index = 0;
1341 for ( ; index < static_cast<int>( m_shadowSpots.size() ); index++)
1342 {
1343 if (! m_shadowSpots[index].used)
1344 {
1345 m_shadowSpots[index].LoadDefault();
1346 break;
1347 }
1348 }
1349
1350 if (index == static_cast<int>( m_shadowSpots.size() ))
1351 m_shadowSpots.push_back(EngineShadow());
1352
1353 m_shadowSpots[index].used = true;
1354 m_shadowSpots[index].objRank = objRank;
1355 m_shadowSpots[index].height = 0.0f;
1356
1357 m_objects[objRank].shadowRank = index;
1358 }
1359
DeleteShadowSpot(int objRank)1360 void CEngine::DeleteShadowSpot(int objRank)
1361 {
1362 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1363
1364 int shadowRank = m_objects[objRank].shadowRank;
1365 if (shadowRank == -1)
1366 return;
1367
1368 assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
1369
1370 m_shadowSpots[shadowRank].used = false;
1371 m_shadowSpots[shadowRank].objRank = -1;
1372
1373 m_objects[objRank].shadowRank = -1;
1374 }
1375
SetObjectShadowSpotHide(int objRank,bool hide)1376 void CEngine::SetObjectShadowSpotHide(int objRank, bool hide)
1377 {
1378 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1379
1380 int shadowRank = m_objects[objRank].shadowRank;
1381 if (shadowRank == -1)
1382 return;
1383
1384 assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
1385
1386 m_shadowSpots[shadowRank].hide = hide;
1387 }
1388
SetObjectShadowSpotType(int objRank,EngineShadowType type)1389 void CEngine::SetObjectShadowSpotType(int objRank, EngineShadowType type)
1390 {
1391 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1392
1393 int shadowRank = m_objects[objRank].shadowRank;
1394 if (shadowRank == -1)
1395 return;
1396
1397 assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
1398
1399 m_shadowSpots[shadowRank].type = type;
1400 }
1401
SetObjectShadowSpotPos(int objRank,const Math::Vector & pos)1402 void CEngine::SetObjectShadowSpotPos(int objRank, const Math::Vector& pos)
1403 {
1404 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1405
1406 int shadowRank = m_objects[objRank].shadowRank;
1407 if (shadowRank == -1)
1408 return;
1409
1410 assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
1411
1412 m_shadowSpots[shadowRank].pos = pos;
1413 }
1414
SetObjectShadowSpotAngle(int objRank,float angle)1415 void CEngine::SetObjectShadowSpotAngle(int objRank, float angle)
1416 {
1417 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1418
1419 int shadowRank = m_objects[objRank].shadowRank;
1420 if (shadowRank == -1)
1421 return;
1422
1423 assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
1424
1425 m_shadowSpots[shadowRank].angle = angle;
1426 }
1427
SetObjectShadowSpotRadius(int objRank,float radius)1428 void CEngine::SetObjectShadowSpotRadius(int objRank, float radius)
1429 {
1430 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1431
1432 int shadowRank = m_objects[objRank].shadowRank;
1433 if (shadowRank == -1)
1434 return;
1435
1436 assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
1437
1438 m_shadowSpots[shadowRank].radius = radius;
1439 }
1440
SetObjectShadowSpotIntensity(int objRank,float intensity)1441 void CEngine::SetObjectShadowSpotIntensity(int objRank, float intensity)
1442 {
1443 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1444
1445 int shadowRank = m_objects[objRank].shadowRank;
1446 if (shadowRank == -1)
1447 return;
1448
1449 assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
1450
1451 m_shadowSpots[shadowRank].intensity = intensity;
1452 }
1453
SetObjectShadowSpotHeight(int objRank,float height)1454 void CEngine::SetObjectShadowSpotHeight(int objRank, float height)
1455 {
1456 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1457
1458 int shadowRank = m_objects[objRank].shadowRank;
1459 if (shadowRank == -1)
1460 return;
1461
1462 assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
1463
1464 m_shadowSpots[shadowRank].height = height;
1465 }
1466
GetHighlight(Math::Point & p1,Math::Point & p2)1467 bool CEngine::GetHighlight(Math::Point &p1, Math::Point &p2)
1468 {
1469 p1 = m_highlightP1;
1470 p2 = m_highlightP2;
1471 return m_highlight;
1472 }
1473
SetHighlightRank(int * rankList)1474 void CEngine::SetHighlightRank(int *rankList)
1475 {
1476 int i = 0;
1477 while ( *rankList != -1 )
1478 {
1479 m_highlightRank[i++] = *rankList++;
1480 }
1481 m_highlightRank[i] = -1; // terminator
1482 }
1483
GetBBox2D(int objRank,Math::Point & min,Math::Point & max)1484 bool CEngine::GetBBox2D(int objRank, Math::Point &min, Math::Point &max)
1485 {
1486 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
1487
1488 min.x = 1000000.0f;
1489 min.y = 1000000.0f;
1490 max.x = -1000000.0f;
1491 max.y = -1000000.0f;
1492
1493 int baseObjRank = m_objects[objRank].baseObjRank;
1494 if (baseObjRank == -1)
1495 return false;
1496
1497 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
1498
1499 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
1500
1501 for (int i = 0; i < 8; i++)
1502 {
1503 Math::Vector p;
1504
1505 if ( i & (1<<0) ) p.x = p1.bboxMin.x;
1506 else p.x = p1.bboxMax.x;
1507 if ( i & (1<<1) ) p.y = p1.bboxMin.y;
1508 else p.y = p1.bboxMax.y;
1509 if ( i & (1<<2) ) p.z = p1.bboxMin.z;
1510 else p.z = p1.bboxMax.z;
1511
1512 Math::Vector pp;
1513 if (TransformPoint(pp, objRank, p))
1514 {
1515 if (pp.x < min.x) min.x = pp.x;
1516 if (pp.x > max.x) max.x = pp.x;
1517 if (pp.y < min.y) min.y = pp.y;
1518 if (pp.y > max.y) max.y = pp.y;
1519 }
1520 }
1521
1522 if (min.x == 1000000.0f ||
1523 min.y == 1000000.0f ||
1524 max.x == -1000000.0f ||
1525 max.y == -1000000.0f)
1526 return false;
1527
1528 return true;
1529 }
1530
DeleteAllGroundSpots()1531 void CEngine::DeleteAllGroundSpots()
1532 {
1533 m_groundSpots.clear();
1534 m_firstGroundSpot = true;
1535
1536 for (int s = 0; s < 16; s++)
1537 {
1538 CImage shadowImg(Math::IntPoint(256, 256));
1539 shadowImg.Fill(Gfx::IntColor(255, 255, 255, 255));
1540
1541 std::stringstream str;
1542 str << "shadow" << std::setfill('0') << std::setw(2) << s << ".png";
1543 std::string texName = str.str();
1544
1545 CreateOrUpdateTexture(texName, &shadowImg);
1546 }
1547 }
1548
CreateGroundSpot()1549 int CEngine::CreateGroundSpot()
1550 {
1551 int index = 0;
1552 for ( ; index < static_cast<int>( m_groundSpots.size() ); index++)
1553 {
1554 if (! m_groundSpots[index].used)
1555 {
1556 m_groundSpots[index].LoadDefault();
1557 break;
1558 }
1559 }
1560
1561 m_groundSpots.push_back(EngineGroundSpot());
1562
1563 m_groundSpots[index].used = true;
1564 m_groundSpots[index].smooth = 1.0f;
1565
1566 return index;
1567 }
1568
DeleteGroundSpot(int rank)1569 void CEngine::DeleteGroundSpot(int rank)
1570 {
1571 assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
1572
1573 m_groundSpots[rank].used = false;
1574 m_groundSpots[rank].pos = Math::Vector(0.0f, 0.0f, 0.0f);
1575 }
1576
SetObjectGroundSpotPos(int rank,const Math::Vector & pos)1577 void CEngine::SetObjectGroundSpotPos(int rank, const Math::Vector& pos)
1578 {
1579 assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
1580
1581 m_groundSpots[rank].pos = pos;
1582 }
1583
SetObjectGroundSpotRadius(int rank,float radius)1584 void CEngine::SetObjectGroundSpotRadius(int rank, float radius)
1585 {
1586 assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
1587
1588 m_groundSpots[rank].radius = radius;
1589 }
1590
SetObjectGroundSpotColor(int rank,const Color & color)1591 void CEngine::SetObjectGroundSpotColor(int rank, const Color& color)
1592 {
1593 assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
1594
1595 m_groundSpots[rank].color = color;
1596 }
1597
SetObjectGroundSpotMinMax(int rank,float min,float max)1598 void CEngine::SetObjectGroundSpotMinMax(int rank, float min, float max)
1599 {
1600 assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
1601
1602 m_groundSpots[rank].min = min;
1603 m_groundSpots[rank].max = max;
1604 }
1605
SetObjectGroundSpotSmooth(int rank,float smooth)1606 void CEngine::SetObjectGroundSpotSmooth(int rank, float smooth)
1607 {
1608 assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
1609
1610 m_groundSpots[rank].smooth = smooth;
1611 }
1612
CreateGroundMark(Math::Vector pos,float radius,float delay1,float delay2,float delay3,int dx,int dy,char * table)1613 void CEngine::CreateGroundMark(Math::Vector pos, float radius,
1614 float delay1, float delay2, float delay3,
1615 int dx, int dy, char* table)
1616 {
1617 m_groundMark.LoadDefault();
1618
1619 m_groundMark.draw = true;
1620 m_groundMark.phase = ENG_GR_MARK_PHASE_INC;
1621 m_groundMark.delay[0] = delay1;
1622 m_groundMark.delay[1] = delay2;
1623 m_groundMark.delay[2] = delay3;
1624 m_groundMark.pos = pos;
1625 m_groundMark.radius = radius;
1626 m_groundMark.intensity = 0.0f;
1627 m_groundMark.dx = dx;
1628 m_groundMark.dy = dy;
1629 m_groundMark.table = table;
1630 }
1631
DeleteGroundMark(int rank)1632 void CEngine::DeleteGroundMark(int rank)
1633 {
1634 m_groundMark.LoadDefault();
1635 }
1636
ComputeDistance()1637 void CEngine::ComputeDistance()
1638 {
1639 for (int i = 0; i < static_cast<int>( m_objects.size() ); i++)
1640 {
1641 if (! m_objects[i].used)
1642 continue;
1643
1644 Math::Vector v;
1645 v.x = m_eyePt.x - m_objects[i].transform.Get(1, 4);
1646 v.y = m_eyePt.y - m_objects[i].transform.Get(2, 4);
1647 v.z = m_eyePt.z - m_objects[i].transform.Get(3, 4);
1648 m_objects[i].distance = v.Length();
1649 }
1650 }
1651
UpdateGeometry()1652 void CEngine::UpdateGeometry()
1653 {
1654 if (! m_updateGeometry)
1655 return;
1656
1657 for (int baseObjRank = 0; baseObjRank < static_cast<int>( m_baseObjects.size() ); baseObjRank++)
1658 {
1659 EngineBaseObject &p1 = m_baseObjects[baseObjRank];
1660 if (! p1.used)
1661 continue;
1662
1663 p1.bboxMin.LoadZero();
1664 p1.bboxMax.LoadZero();
1665
1666 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
1667 {
1668 EngineBaseObjTexTier& p2 = p1.next[l2];
1669
1670 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
1671 {
1672 EngineBaseObjDataTier& p3 = p2.next[l3];
1673
1674 for (int i = 0; i < static_cast<int>( p3.vertices.size() ); i++)
1675 {
1676 p1.bboxMin.x = Math::Min(p3.vertices[i].coord.x, p1.bboxMin.x);
1677 p1.bboxMin.y = Math::Min(p3.vertices[i].coord.y, p1.bboxMin.y);
1678 p1.bboxMin.z = Math::Min(p3.vertices[i].coord.z, p1.bboxMin.z);
1679 p1.bboxMax.x = Math::Max(p3.vertices[i].coord.x, p1.bboxMax.x);
1680 p1.bboxMax.y = Math::Max(p3.vertices[i].coord.y, p1.bboxMax.y);
1681 p1.bboxMax.z = Math::Max(p3.vertices[i].coord.z, p1.bboxMax.z);
1682 }
1683 }
1684 }
1685
1686 p1.boundingSphere = Math::BoundingSphereForBox(p1.bboxMin, p1.bboxMax);
1687 }
1688
1689 m_updateGeometry = false;
1690 }
1691
UpdateStaticBuffer(EngineBaseObjDataTier & p4)1692 void CEngine::UpdateStaticBuffer(EngineBaseObjDataTier& p4)
1693 {
1694 PrimitiveType type;
1695 if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES)
1696 type = PRIMITIVE_TRIANGLES;
1697 else
1698 type = PRIMITIVE_TRIANGLE_STRIP;
1699
1700 if (p4.staticBufferId == 0)
1701 p4.staticBufferId = m_device->CreateStaticBuffer(type, &p4.vertices[0], p4.vertices.size());
1702 else
1703 m_device->UpdateStaticBuffer(p4.staticBufferId, type, &p4.vertices[0], p4.vertices.size());
1704
1705 p4.updateStaticBuffer = false;
1706 }
1707
UpdateStaticBuffers()1708 void CEngine::UpdateStaticBuffers()
1709 {
1710 if (!m_updateStaticBuffers)
1711 return;
1712
1713 m_updateStaticBuffers = false;
1714
1715 for (int baseObjRank = 0; baseObjRank < static_cast<int>( m_baseObjects.size() ); baseObjRank++)
1716 {
1717 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
1718 if (! p1.used)
1719 continue;
1720
1721 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
1722 {
1723 EngineBaseObjTexTier& p2 = p1.next[l2];
1724
1725 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
1726 {
1727 EngineBaseObjDataTier& p3 = p2.next[l3];
1728
1729 if (! p3.updateStaticBuffer)
1730 continue;
1731
1732 UpdateStaticBuffer(p3);
1733 }
1734 }
1735 }
1736 }
1737
Update()1738 void CEngine::Update()
1739 {
1740 ComputeDistance();
1741 UpdateGeometry();
1742 UpdateStaticBuffers();
1743 }
1744
DetectBBox(int objRank,Math::Point mouse)1745 bool CEngine::DetectBBox(int objRank, Math::Point mouse)
1746 {
1747 assert(objRank >= 0 && objRank < static_cast<int>(m_objects.size()));
1748
1749 int baseObjRank = m_objects[objRank].baseObjRank;
1750 if (baseObjRank == -1)
1751 return false;
1752
1753 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
1754
1755 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
1756
1757 Math::Point min, max;
1758 min.x = 1000000.0f;
1759 min.y = 1000000.0f;
1760 max.x = -1000000.0f;
1761 max.y = -1000000.0f;
1762
1763 for (int i = 0; i < 8; i++)
1764 {
1765 Math::Vector p;
1766
1767 if ( i & (1<<0) ) p.x = p1.bboxMin.x;
1768 else p.x = p1.bboxMax.x;
1769 if ( i & (1<<1) ) p.y = p1.bboxMin.y;
1770 else p.y = p1.bboxMax.y;
1771 if ( i & (1<<2) ) p.z = p1.bboxMin.z;
1772 else p.z = p1.bboxMax.z;
1773
1774 Math::Vector pp;
1775 if ( TransformPoint(pp, objRank, p) )
1776 {
1777 if (pp.x < min.x) min.x = pp.x;
1778 if (pp.x > max.x) max.x = pp.x;
1779 if (pp.y < min.y) min.y = pp.y;
1780 if (pp.y > max.y) max.y = pp.y;
1781 }
1782 }
1783
1784 return ( mouse.x >= min.x &&
1785 mouse.x <= max.x &&
1786 mouse.y >= min.y &&
1787 mouse.y <= max.y );
1788 }
1789
DetectObject(Math::Point mouse,Math::Vector & targetPos,bool terrain)1790 int CEngine::DetectObject(Math::Point mouse, Math::Vector& targetPos, bool terrain)
1791 {
1792 float min = 1000000.0f;
1793 int nearest = -1;
1794 Math::Vector pos;
1795
1796 for (int objRank = 0; objRank < static_cast<int>( m_objects.size() ); objRank++)
1797 {
1798 if (! m_objects[objRank].used)
1799 continue;
1800
1801 if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN && !terrain)
1802 continue;
1803
1804 if (! DetectBBox(objRank, mouse))
1805 continue;
1806
1807 int baseObjRank = m_objects[objRank].baseObjRank;
1808 if (baseObjRank == -1)
1809 continue;
1810
1811 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
1812
1813 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
1814 if (! p1.used)
1815 continue;
1816
1817 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
1818 {
1819 EngineBaseObjTexTier& p2 = p1.next[l2];
1820
1821 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
1822 {
1823 EngineBaseObjDataTier& p3 = p2.next[l3];
1824
1825 if (p3.type == ENG_TRIANGLE_TYPE_TRIANGLES)
1826 {
1827 for (int i = 0; i < static_cast<int>( p3.vertices.size() ); i += 3)
1828 {
1829 float dist = 0.0f;
1830 if (DetectTriangle(mouse, &p3.vertices[i], objRank, dist, pos) && dist < min)
1831 {
1832 min = dist;
1833 nearest = objRank;
1834 targetPos = pos;
1835 }
1836 }
1837 }
1838 else if (p3.type == ENG_TRIANGLE_TYPE_SURFACE)
1839 {
1840 for (int i = 0; i < static_cast<int>( p3.vertices.size() ) - 2; i += 1)
1841 {
1842 float dist = 0.0f;
1843 if (DetectTriangle(mouse, &p3.vertices[i], objRank, dist, pos) && dist < min)
1844 {
1845 min = dist;
1846 nearest = objRank;
1847 targetPos = pos;
1848 }
1849 }
1850 }
1851 }
1852 }
1853 }
1854
1855 return nearest;
1856 }
1857
DetectTriangle(Math::Point mouse,VertexTex2 * triangle,int objRank,float & dist,Math::Vector & pos)1858 bool CEngine::DetectTriangle(Math::Point mouse, VertexTex2* triangle, int objRank, float& dist, Math::Vector& pos)
1859 {
1860 assert(objRank >= 0 && objRank < static_cast<int>(m_objects.size()));
1861
1862 Math::Vector p2D[3], p3D;
1863
1864 for (int i = 0; i < 3; i++)
1865 {
1866 p3D.x = triangle[i].coord.x;
1867 p3D.y = triangle[i].coord.y;
1868 p3D.z = triangle[i].coord.z;
1869
1870 if (! TransformPoint(p2D[i], objRank, p3D))
1871 return false;
1872 }
1873
1874 if (mouse.x < p2D[0].x &&
1875 mouse.x < p2D[1].x &&
1876 mouse.x < p2D[2].x)
1877 return false;
1878
1879 if (mouse.x > p2D[0].x &&
1880 mouse.x > p2D[1].x &&
1881 mouse.x > p2D[2].x)
1882 return false;
1883
1884 if (mouse.y < p2D[0].y &&
1885 mouse.y < p2D[1].y &&
1886 mouse.y < p2D[2].y)
1887 return false;
1888
1889 if (mouse.y > p2D[0].y &&
1890 mouse.y > p2D[1].y &&
1891 mouse.y > p2D[2].y)
1892 return false;
1893
1894 Math::Point a, b, c;
1895 a.x = p2D[0].x;
1896 a.y = p2D[0].y;
1897 b.x = p2D[1].x;
1898 b.y = p2D[1].y;
1899 c.x = p2D[2].x;
1900 c.y = p2D[2].y;
1901
1902 if (! Math::IsInsideTriangle(a, b, c, mouse))
1903 return false;
1904
1905 Math::Vector a2 = Math::Transform(m_objects[objRank].transform, triangle[0].coord);
1906 Math::Vector b2 = Math::Transform(m_objects[objRank].transform, triangle[1].coord);
1907 Math::Vector c2 = Math::Transform(m_objects[objRank].transform, triangle[2].coord);
1908 Math::Vector e = Math::Transform(m_matView.Inverse(), Math::Vector(0.0f, 0.0f, -1.0f));
1909 Math::Vector f = Math::Transform(m_matView.Inverse(), Math::Vector(
1910 (mouse.x*2.0f-1.0f)*m_matProj.Inverse().Get(1,1),
1911 (mouse.y*2.0f-1.0f)*m_matProj.Inverse().Get(2,2),
1912 0.0f));
1913 Math::Intersect(a2, b2, c2, e, f, pos);
1914
1915 dist = (p2D[0].z + p2D[1].z + p2D[2].z) / 3.0f;
1916 return true;
1917 }
1918
1919 //! Use only after world transform already set
IsVisible(int objRank)1920 bool CEngine::IsVisible(int objRank)
1921 {
1922 assert(objRank >= 0 && objRank < static_cast<int>(m_objects.size()));
1923
1924 int baseObjRank = m_objects[objRank].baseObjRank;
1925 if (baseObjRank == -1)
1926 return false;
1927
1928 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
1929
1930 const auto& sphere = m_baseObjects[baseObjRank].boundingSphere;
1931 if (m_device->ComputeSphereVisibility(sphere.pos, sphere.radius) == Gfx::FRUSTUM_PLANE_ALL)
1932 {
1933 m_objects[objRank].visible = true;
1934 return true;
1935 }
1936
1937 m_objects[objRank].visible = false;
1938 return false;
1939 }
1940
TransformPoint(Math::Vector & p2D,int objRank,Math::Vector p3D)1941 bool CEngine::TransformPoint(Math::Vector& p2D, int objRank, Math::Vector p3D)
1942 {
1943 assert(objRank >= 0 && objRank < static_cast<int>(m_objects.size()));
1944
1945 p3D = Math::Transform(m_objects[objRank].transform, p3D);
1946 p3D = Math::Transform(m_matView, p3D);
1947
1948 if (p3D.z < 2.0f)
1949 return false; // behind?
1950
1951 p2D.x = (p3D.x/p3D.z)*m_matProj.Get(1,1);
1952 p2D.y = (p3D.y/p3D.z)*m_matProj.Get(2,2);
1953 p2D.z = p3D.z;
1954
1955 p2D.x = (p2D.x+1.0f)/2.0f; // [-1..1] -> [0..1]
1956 p2D.y = (p2D.y+1.0f)/2.0f;
1957
1958 return true;
1959 }
1960
1961
1962
1963 /*******************************************************
1964 Mode setting
1965 *******************************************************/
1966
1967
1968
SetState(int state,const Color & color)1969 void CEngine::SetState(int state, const Color& color)
1970 {
1971 if (state == m_lastState && color == m_lastColor)
1972 return;
1973
1974 m_lastState = state;
1975 m_lastColor = color;
1976
1977 if (state & ENG_RSTATE_TTEXTURE_BLACK) // transparent black texture?
1978 {
1979 m_device->SetRenderState(RENDER_STATE_FOG, false);
1980 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
1981 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
1982
1983 m_device->SetRenderState(RENDER_STATE_BLENDING, true);
1984 m_device->SetBlendFunc(BLEND_ONE, BLEND_INV_SRC_COLOR);
1985
1986 TextureStageParams params;
1987 params.colorOperation = TEX_MIX_OPER_MODULATE;
1988 params.colorArg1 = TEX_MIX_ARG_TEXTURE;
1989 params.colorArg2 = TEX_MIX_ARG_FACTOR;
1990 params.alphaOperation = TEX_MIX_OPER_DEFAULT;
1991 params.factor = color;
1992
1993 m_device->SetTextureEnabled(0, true);
1994 m_device->SetTextureStageParams(0, params);
1995 }
1996 else if (state & ENG_RSTATE_TTEXTURE_WHITE) // transparent white texture?
1997 {
1998 m_device->SetRenderState(RENDER_STATE_FOG, false);
1999 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
2000 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
2001
2002 m_device->SetRenderState(RENDER_STATE_BLENDING, true);
2003 m_device->SetBlendFunc(BLEND_DST_COLOR, BLEND_ZERO);
2004
2005 TextureStageParams params;
2006 params.colorOperation = TEX_MIX_OPER_ADD;
2007 params.colorArg1 = TEX_MIX_ARG_TEXTURE;
2008 params.colorArg2 = TEX_MIX_ARG_FACTOR;
2009 params.alphaOperation = TEX_MIX_OPER_DEFAULT;
2010 params.factor = color.Inverse();
2011
2012 m_device->SetTextureEnabled(0, true);
2013 m_device->SetTextureStageParams(0, params);
2014 }
2015 else if (state & ENG_RSTATE_TCOLOR_BLACK) // transparent black color?
2016 {
2017 m_device->SetRenderState(RENDER_STATE_FOG, false);
2018 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
2019 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
2020
2021 m_device->SetRenderState(RENDER_STATE_BLENDING, true);
2022 m_device->SetBlendFunc(BLEND_ONE, BLEND_INV_SRC_COLOR);
2023
2024 m_device->SetTextureEnabled(0, false);
2025 }
2026 else if (state & ENG_RSTATE_TCOLOR_WHITE) // transparent white color?
2027 {
2028 m_device->SetRenderState(RENDER_STATE_FOG, false);
2029 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
2030 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
2031
2032 m_device->SetRenderState(RENDER_STATE_BLENDING, true);
2033 m_device->SetBlendFunc(BLEND_DST_COLOR, BLEND_ZERO);
2034
2035 m_device->SetTextureEnabled(0, false);
2036 }
2037 else if (state & ENG_RSTATE_TDIFFUSE) // diffuse color as transparent?
2038 {
2039 m_device->SetRenderState(RENDER_STATE_FOG, false);
2040 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
2041 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
2042
2043 m_device->SetRenderState(RENDER_STATE_BLENDING, true);
2044 m_device->SetBlendFunc(BLEND_SRC_ALPHA, BLEND_DST_ALPHA);
2045
2046 TextureStageParams params;
2047 params.colorOperation = TEX_MIX_OPER_REPLACE;
2048 params.colorArg1 = TEX_MIX_ARG_TEXTURE;
2049 params.alphaOperation = TEX_MIX_OPER_DEFAULT;
2050
2051 m_device->SetTextureEnabled(0, true);
2052 m_device->SetTextureStageParams(0, params);
2053 }
2054 else if (state & ENG_RSTATE_OPAQUE_TEXTURE) // opaque texture ?
2055 {
2056 m_device->SetRenderState(RENDER_STATE_FOG, false);
2057 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
2058 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
2059 m_device->SetRenderState(RENDER_STATE_BLENDING, false);
2060
2061 m_device->SetTextureEnabled(0, true);
2062 m_device->SetTextureStageParams(0, TextureStageParams()); // default operation
2063 }
2064 else if (state & ENG_RSTATE_OPAQUE_COLOR) // opaque color ?
2065 {
2066 m_device->SetRenderState(RENDER_STATE_FOG, false);
2067 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
2068 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
2069 m_device->SetRenderState(RENDER_STATE_BLENDING, false);
2070
2071 m_device->SetTextureEnabled(0, false);
2072 }
2073 else if (state & ENG_RSTATE_TEXT) // font rendering?
2074 {
2075 m_device->SetRenderState(RENDER_STATE_FOG, false);
2076 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
2077 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
2078
2079 m_device->SetRenderState(RENDER_STATE_BLENDING, true);
2080 m_device->SetBlendFunc(BLEND_SRC_ALPHA, BLEND_INV_SRC_ALPHA);
2081
2082 m_device->SetTextureEnabled(0, true);
2083 m_device->SetTextureStageParams(0, TextureStageParams()); // default operation
2084 }
2085 else if (state & ENG_RSTATE_ALPHA) // image with alpha channel?
2086 {
2087 m_device->SetRenderState(RENDER_STATE_BLENDING, true);
2088 m_device->SetBlendFunc(BLEND_SRC_ALPHA, BLEND_INV_SRC_ALPHA);
2089
2090 m_device->SetRenderState(RENDER_STATE_FOG, true);
2091 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true);
2092
2093 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, true);
2094
2095 m_device->SetAlphaTestFunc(COMP_FUNC_GREATER, 0.5f);
2096
2097 TextureStageParams params;
2098 params.colorOperation = TEX_MIX_OPER_MODULATE;
2099 params.colorArg1 = TEX_MIX_ARG_TEXTURE;
2100 params.colorArg2 = TEX_MIX_ARG_SRC_COLOR;
2101 params.alphaOperation = TEX_MIX_OPER_REPLACE;
2102 params.alphaArg1 = TEX_MIX_ARG_TEXTURE;
2103 params.factor = color;
2104
2105 m_device->SetTextureEnabled(0, true);
2106 m_device->SetTextureStageParams(0, params);
2107 }
2108 else if (state & ENG_RSTATE_TTEXTURE_ALPHA) // texture with alpha channel?
2109 {
2110 m_device->SetRenderState(RENDER_STATE_FOG, false);
2111 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
2112
2113 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, true);
2114 m_device->SetAlphaTestFunc(COMP_FUNC_GREATER, 0.0f);
2115
2116 m_device->SetRenderState(RENDER_STATE_BLENDING, true);
2117 m_device->SetBlendFunc(BLEND_DST_COLOR, BLEND_INV_SRC_ALPHA);
2118
2119 TextureStageParams params;
2120 params.colorOperation = TEX_MIX_OPER_MODULATE;
2121 params.colorArg1 = TEX_MIX_ARG_TEXTURE;
2122 params.colorArg2 = TEX_MIX_ARG_SRC_COLOR;
2123 params.alphaOperation = TEX_MIX_OPER_MODULATE;
2124 params.alphaArg1 = TEX_MIX_ARG_TEXTURE;
2125 params.alphaArg2 = TEX_MIX_ARG_FACTOR;
2126 params.factor = color;
2127
2128 m_device->SetTextureEnabled(0, true);
2129 m_device->SetTextureStageParams(0, params);
2130 }
2131 else if (state & ENG_RSTATE_TCOLOR_ALPHA)
2132 {
2133 m_device->SetRenderState(RENDER_STATE_FOG, false);
2134 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
2135 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
2136
2137 m_device->SetRenderState(RENDER_STATE_BLENDING, true);
2138 m_device->SetBlendFunc(BLEND_DST_COLOR, BLEND_INV_SRC_ALPHA);
2139
2140 m_device->SetTextureEnabled(0, false);
2141 }
2142 else // normal ?
2143 {
2144 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
2145 m_device->SetRenderState(RENDER_STATE_BLENDING, false);
2146
2147 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true);
2148 m_device->SetRenderState(RENDER_STATE_FOG, true);
2149
2150 TextureStageParams params;
2151 params.colorOperation = TEX_MIX_OPER_DEFAULT; // default modulate
2152 params.alphaOperation = TEX_MIX_OPER_DEFAULT;
2153
2154 m_device->SetTextureEnabled(0, true);
2155 m_device->SetTextureStageParams(0, params);
2156 }
2157
2158 if (state & ENG_RSTATE_FOG)
2159 m_device->SetRenderState(RENDER_STATE_FOG, true);
2160
2161
2162 bool second = m_dirty;
2163
2164 // TODO: I'm pretty sure this is reversed and should be m_dirty instead of !m_dirty ~krzys_h
2165 if ( !m_dirty && (state & ENG_RSTATE_SECOND) == 0 ) second = false;
2166
2167 if ((state & ENG_RSTATE_DUAL_BLACK) && second)
2168 {
2169 TextureStageParams params;
2170 params.colorOperation = TEX_MIX_OPER_MODULATE;
2171 params.colorArg1 = TEX_MIX_ARG_TEXTURE;
2172 params.colorArg2 = TEX_MIX_ARG_COMPUTED_COLOR;
2173 params.alphaOperation = TEX_MIX_OPER_DEFAULT;
2174 m_device->SetTextureEnabled(1, true);
2175 m_device->SetTextureStageParams(1, params);
2176 }
2177 else if ((state & ENG_RSTATE_DUAL_WHITE) && second)
2178 {
2179 TextureStageParams params;
2180 params.colorOperation = TEX_MIX_OPER_ADD;
2181 params.colorArg1 = TEX_MIX_ARG_TEXTURE;
2182 params.colorArg2 = TEX_MIX_ARG_COMPUTED_COLOR;
2183 params.alphaOperation = TEX_MIX_OPER_DEFAULT;
2184 m_device->SetTextureEnabled(1, true);
2185 m_device->SetTextureStageParams(1, params);
2186 }
2187 else
2188 {
2189 m_device->SetTextureEnabled(1, false);
2190 }
2191
2192 if (state & ENG_RSTATE_WRAP)
2193 {
2194 m_device->SetTextureStageWrap(0, TEX_WRAP_REPEAT, TEX_WRAP_REPEAT);
2195 m_device->SetTextureStageWrap(1, TEX_WRAP_REPEAT, TEX_WRAP_REPEAT);
2196 }
2197 else if (state & ENG_RSTATE_CLAMP)
2198 {
2199 m_device->SetTextureStageWrap(0, TEX_WRAP_CLAMP, TEX_WRAP_CLAMP);
2200 m_device->SetTextureStageWrap(1, TEX_WRAP_CLAMP, TEX_WRAP_CLAMP);
2201 }
2202
2203 if (state & ENG_RSTATE_2FACE)
2204 {
2205 m_device->SetRenderState(RENDER_STATE_CULLING, false);
2206 }
2207 else
2208 {
2209 m_device->SetRenderState(RENDER_STATE_CULLING, true);
2210 m_device->SetCullMode(CULL_CCW);
2211 }
2212
2213 if (state & ENG_RSTATE_LIGHT)
2214 m_device->SetGlobalAmbient(Color(1.0f, 1.0f, 1.0f, 1.0f));
2215 else
2216 m_device->SetGlobalAmbient(m_ambientColor[m_rankView]);
2217
2218
2219 // In interface mode, disable lighting
2220 if (m_interfaceMode)
2221 m_device->SetRenderState(RENDER_STATE_LIGHTING, false);
2222 }
2223
SetMaterial(const Material & mat)2224 void CEngine::SetMaterial(const Material& mat)
2225 {
2226 m_lastMaterial = mat;
2227 m_device->SetMaterial(mat);
2228 }
2229
SetViewParams(const Math::Vector & eyePt,const Math::Vector & lookatPt,const Math::Vector & upVec)2230 void CEngine::SetViewParams(const Math::Vector &eyePt, const Math::Vector &lookatPt, const Math::Vector &upVec)
2231 {
2232 m_eyePt = eyePt;
2233 m_lookatPt = lookatPt;
2234 m_eyeDirH = Math::RotateAngle(eyePt.x - lookatPt.x, eyePt.z - lookatPt.z);
2235 m_eyeDirV = Math::RotateAngle(Math::DistanceProjected(eyePt, lookatPt), eyePt.y - lookatPt.y);
2236
2237 Math::LoadViewMatrix(m_matView, eyePt, lookatPt, upVec);
2238
2239 if (m_sound == nullptr)
2240 m_sound = m_app->GetSound();
2241
2242 if (m_sound != nullptr)
2243 m_sound->SetListener(eyePt, lookatPt);
2244 }
2245
CreateTexture(const std::string & texName,const TextureCreateParams & params,CImage * image)2246 Texture CEngine::CreateTexture(const std::string& texName, const TextureCreateParams& params, CImage* image)
2247 {
2248 if (texName.empty())
2249 return Texture(); // invalid texture
2250
2251 if (m_texBlacklist.find(texName) != m_texBlacklist.end())
2252 return Texture(); // invalid texture
2253
2254 Texture tex;
2255 CImage img;
2256
2257 if (image == nullptr)
2258 {
2259 if (!img.Load(texName))
2260 {
2261 std::string error = img.GetError();
2262 GetLogger()->Error("Couldn't load texture '%s': %s, blacklisting\n", texName.c_str(), error.c_str());
2263 m_texBlacklist.insert(texName);
2264 return Texture(); // invalid texture
2265 }
2266
2267 image = &img;
2268 }
2269
2270 tex = m_device->CreateTexture(image, params);
2271
2272 if (! tex.Valid())
2273 {
2274 GetLogger()->Error("Couldn't load texture '%s', blacklisting\n", texName.c_str());
2275 m_texBlacklist.insert(texName);
2276 return tex;
2277 }
2278
2279 m_texNameMap[texName] = tex;
2280 m_revTexNameMap[tex] = texName;
2281
2282 return tex;
2283 }
2284
LoadTexture(const std::string & name)2285 Texture CEngine::LoadTexture(const std::string& name)
2286 {
2287 return LoadTexture(name, m_defaultTexParams);
2288 }
2289
LoadTexture(const std::string & name,CImage * image)2290 Texture CEngine::LoadTexture(const std::string& name, CImage* image)
2291 {
2292 Texture tex = CreateTexture(name, m_defaultTexParams, image);
2293 return tex;
2294 }
2295
LoadTexture(const std::string & name,const TextureCreateParams & params)2296 Texture CEngine::LoadTexture(const std::string& name, const TextureCreateParams& params)
2297 {
2298 if (m_texBlacklist.find(name) != m_texBlacklist.end())
2299 return Texture();
2300
2301 std::map<std::string, Texture>::iterator it = m_texNameMap.find(name);
2302 if (it != m_texNameMap.end())
2303 return (*it).second;
2304
2305 return CreateTexture(name, params);
2306 }
2307
LoadAllTextures()2308 bool CEngine::LoadAllTextures()
2309 {
2310 m_miceTexture = LoadTexture("textures/interface/mouse.png");
2311 LoadTexture("textures/interface/button1.png");
2312 LoadTexture("textures/interface/button2.png");
2313 LoadTexture("textures/interface/button3.png");
2314 LoadTexture("textures/interface/button4.png");
2315 LoadTexture("textures/effect00.png");
2316 LoadTexture("textures/effect01.png");
2317 LoadTexture("textures/effect02.png");
2318 LoadTexture("textures/effect03.png");
2319
2320 if (! m_backgroundName.empty())
2321 {
2322 TextureCreateParams params = m_defaultTexParams;
2323 params.padToNearestPowerOfTwo = true;
2324 m_backgroundTex = LoadTexture(m_backgroundName, params);
2325 }
2326 else
2327 m_backgroundTex.SetInvalid();
2328
2329 if (! m_foregroundName.empty())
2330 m_foregroundTex = LoadTexture(m_foregroundName);
2331 else
2332 m_foregroundTex.SetInvalid();
2333
2334 m_planet->LoadTexture();
2335
2336 bool ok = true;
2337
2338 for (int objRank = 0; objRank < static_cast<int>( m_objects.size() ); objRank++)
2339 {
2340 if (! m_objects[objRank].used)
2341 continue;
2342
2343 bool terrain = false;
2344 if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN)
2345 terrain = true;
2346
2347 int baseObjRank = m_objects[objRank].baseObjRank;
2348 if (baseObjRank == -1)
2349 continue;
2350
2351 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
2352
2353 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
2354 if (! p1.used)
2355 continue;
2356
2357 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
2358 {
2359 EngineBaseObjTexTier& p2 = p1.next[l2];
2360
2361 if (! p2.tex1Name.empty())
2362 {
2363 if (terrain)
2364 p2.tex1 = LoadTexture("textures/"+p2.tex1Name, m_terrainTexParams);
2365 else
2366 p2.tex1 = LoadTexture("textures/"+p2.tex1Name);
2367
2368 if (! p2.tex1.Valid())
2369 ok = false;
2370 }
2371
2372 if (! p2.tex2Name.empty())
2373 {
2374 if (terrain)
2375 p2.tex2 = LoadTexture("textures/"+p2.tex2Name, m_terrainTexParams);
2376 else
2377 p2.tex2 = LoadTexture("textures/"+p2.tex2Name);
2378
2379 if (! p2.tex2.Valid())
2380 ok = false;
2381 }
2382 }
2383 }
2384
2385 return ok;
2386 }
2387
IsExcludeColor(Math::Point * exclude,int x,int y)2388 static bool IsExcludeColor(Math::Point *exclude, int x, int y)
2389 {
2390 int i = 0;
2391 while ( exclude[i+0].x != 0.0f || exclude[i+0].y != 0.0f ||
2392 exclude[i+1].y != 0.0f || exclude[i+1].y != 0.0f )
2393 {
2394 if ( x >= static_cast<int>(exclude[i+0].x*256.0f) &&
2395 x < static_cast<int>(exclude[i+1].x*256.0f) &&
2396 y >= static_cast<int>(exclude[i+0].y*256.0f) &&
2397 y < static_cast<int>(exclude[i+1].y*256.0f) )
2398 return true; // exclude
2399
2400 i += 2;
2401 }
2402
2403 return false; // point to include
2404 }
2405
2406
ChangeTextureColor(const std::string & texName,const std::string & srcName,Color colorRef1,Color colorNew1,Color colorRef2,Color colorNew2,float tolerance1,float tolerance2,Math::Point ts,Math::Point ti,Math::Point * exclude,float shift,bool hsv)2407 bool CEngine::ChangeTextureColor(const std::string& texName,
2408 const std::string& srcName,
2409 Color colorRef1, Color colorNew1,
2410 Color colorRef2, Color colorNew2,
2411 float tolerance1, float tolerance2,
2412 Math::Point ts, Math::Point ti,
2413 Math::Point *exclude, float shift, bool hsv)
2414 {
2415 CImage img;
2416 if (!img.Load(srcName))
2417 {
2418 std::string error = img.GetError();
2419 GetLogger()->Error("Couldn't load texture '%s': %s, blacklisting\n", srcName.c_str(), error.c_str());
2420 m_texBlacklist.insert(srcName);
2421 return false;
2422 }
2423
2424 bool changeColorsNeeded = true;
2425
2426 if (colorRef1.r == colorNew1.r &&
2427 colorRef1.g == colorNew1.g &&
2428 colorRef1.b == colorNew1.b &&
2429 colorRef2.r == colorNew2.r &&
2430 colorRef2.g == colorNew2.g &&
2431 colorRef2.b == colorNew2.b)
2432 {
2433 changeColorsNeeded = false;
2434 }
2435
2436
2437 int dx = img.GetSize().x;
2438 int dy = img.GetSize().y;
2439
2440 int sx = static_cast<int>(Math::Max(ts.x*dx, 0));
2441 int sy = static_cast<int>(Math::Max(ts.y*dy, 0));
2442
2443 int ex = static_cast<int>(Math::Min(ti.x*dx, dx));
2444 int ey = static_cast<int>(Math::Min(ti.y*dy, dy));
2445
2446
2447 ColorHSV cr1 = RGB2HSV(colorRef1);
2448 ColorHSV cn1 = RGB2HSV(colorNew1);
2449 ColorHSV cr2 = RGB2HSV(colorRef2);
2450 ColorHSV cn2 = RGB2HSV(colorNew2);
2451
2452 if (changeColorsNeeded)
2453 {
2454 for (int y = sy; y < ey; y++)
2455 {
2456 for (int x = sx; x < ex; x++)
2457 {
2458 if (exclude != nullptr && IsExcludeColor(exclude, x,y) )
2459 continue;
2460
2461 Color color = img.GetPixel(Math::IntPoint(x, y));
2462
2463 if (hsv)
2464 {
2465 ColorHSV c = RGB2HSV(color);
2466 if (c.s > 0.01f && fabs(c.h - cr1.h) < tolerance1)
2467 {
2468 c.h += cn1.h - cr1.h;
2469 c.s += cn1.s - cr1.s;
2470 c.v += cn1.v - cr1.v;
2471 if (c.h < 0.0f) c.h -= 1.0f;
2472 if (c.h > 1.0f) c.h += 1.0f;
2473 color = HSV2RGB(c);
2474 color.r = Math::Norm(color.r + shift);
2475 color.g = Math::Norm(color.g + shift);
2476 color.b = Math::Norm(color.b + shift);
2477 img.SetPixel(Math::IntPoint(x, y), color);
2478 }
2479 else if (tolerance2 != -1.0f &&
2480 c.s > 0.01f && fabs(c.h - cr2.h) < tolerance2)
2481 {
2482 c.h += cn2.h - cr2.h;
2483 c.s += cn2.s - cr2.s;
2484 c.v += cn2.v - cr2.v;
2485 if (c.h < 0.0f) c.h -= 1.0f;
2486 if (c.h > 1.0f) c.h += 1.0f;
2487 color = HSV2RGB(c);
2488 color.r = Math::Norm(color.r + shift);
2489 color.g = Math::Norm(color.g + shift);
2490 color.b = Math::Norm(color.b + shift);
2491 img.SetPixel(Math::IntPoint(x, y), color);
2492 }
2493 }
2494 else
2495 {
2496 if ( fabs(color.r - colorRef1.r) +
2497 fabs(color.g - colorRef1.g) +
2498 fabs(color.b - colorRef1.b) < tolerance1 * 3.0f)
2499 {
2500 color.r = Math::Norm(colorNew1.r + color.r - colorRef1.r + shift);
2501 color.g = Math::Norm(colorNew1.g + color.g - colorRef1.g + shift);
2502 color.b = Math::Norm(colorNew1.b + color.b - colorRef1.b + shift);
2503 img.SetPixel(Math::IntPoint(x, y), color);
2504 }
2505 else if (tolerance2 != -1 &&
2506 fabs(color.r - colorRef2.r) +
2507 fabs(color.g - colorRef2.g) +
2508 fabs(color.b - colorRef2.b) < tolerance2 * 3.0f)
2509 {
2510 color.r = Math::Norm(colorNew2.r + color.r - colorRef2.r + shift);
2511 color.g = Math::Norm(colorNew2.g + color.g - colorRef2.g + shift);
2512 color.b = Math::Norm(colorNew2.b + color.b - colorRef2.b + shift);
2513 img.SetPixel(Math::IntPoint(x, y), color);
2514 }
2515 }
2516 }
2517 }
2518 }
2519
2520 CreateOrUpdateTexture(texName, &img);
2521
2522 return true;
2523 }
2524
ChangeTextureColor(const std::string & texName,Color colorRef1,Color colorNew1,Color colorRef2,Color colorNew2,float tolerance1,float tolerance2,Math::Point ts,Math::Point ti,Math::Point * exclude,float shift,bool hsv)2525 bool CEngine::ChangeTextureColor(const std::string& texName,
2526 Color colorRef1, Color colorNew1,
2527 Color colorRef2, Color colorNew2,
2528 float tolerance1, float tolerance2,
2529 Math::Point ts, Math::Point ti,
2530 Math::Point *exclude, float shift, bool hsv)
2531 {
2532 return ChangeTextureColor(texName, texName, colorRef1, colorNew1, colorRef2, colorNew2, tolerance1, tolerance2, ts, ti, exclude, shift, hsv);
2533 }
2534
DeleteTexture(const std::string & texName)2535 void CEngine::DeleteTexture(const std::string& texName)
2536 {
2537 auto it = m_texNameMap.find(texName);
2538 if (it == m_texNameMap.end())
2539 return;
2540
2541 auto revIt = m_revTexNameMap.find((*it).second);
2542
2543 m_device->DestroyTexture((*it).second);
2544
2545 m_revTexNameMap.erase(revIt);
2546 m_texNameMap.erase(it);
2547 }
2548
DeleteTexture(const Texture & tex)2549 void CEngine::DeleteTexture(const Texture& tex)
2550 {
2551 if (! tex.Valid())
2552 return;
2553
2554 auto revIt = m_revTexNameMap.find(tex);
2555 if (revIt == m_revTexNameMap.end())
2556 return;
2557
2558 m_device->DestroyTexture(tex);
2559
2560 auto it = m_texNameMap.find((*revIt).second);
2561
2562 m_revTexNameMap.erase(revIt);
2563 m_texNameMap.erase(it);
2564 }
2565
CreateOrUpdateTexture(const std::string & texName,CImage * img)2566 void CEngine::CreateOrUpdateTexture(const std::string& texName, CImage* img)
2567 {
2568 auto it = m_texNameMap.find(texName);
2569 if (it == m_texNameMap.end())
2570 {
2571 LoadTexture(texName, img);
2572 }
2573 else
2574 {
2575 m_device->UpdateTexture((*it).second, Math::IntPoint(0, 0), img->GetData(), m_defaultTexParams.format);
2576 }
2577 }
2578
FlushTextureCache()2579 void CEngine::FlushTextureCache()
2580 {
2581 m_device->DestroyAllTextures();
2582
2583 m_backgroundTex.SetInvalid();
2584 m_foregroundTex.SetInvalid();
2585
2586 m_texNameMap.clear();
2587 m_revTexNameMap.clear();
2588 m_texBlacklist.clear();
2589
2590 m_firstGroundSpot = true;
2591 }
2592
SetTexture(const std::string & name,int stage)2593 bool CEngine::SetTexture(const std::string& name, int stage)
2594 {
2595 auto it = m_texNameMap.find(name);
2596 if (it != m_texNameMap.end())
2597 {
2598 m_device->SetTexture(stage, (*it).second);
2599 return true;
2600 }
2601
2602 if (! LoadTexture(name).Valid())
2603 {
2604 m_device->SetTexture(stage, 0); // invalid texture
2605 return false;
2606 }
2607
2608 it = m_texNameMap.find(name);
2609 if (it != m_texNameMap.end())
2610 {
2611 m_device->SetTexture(stage, (*it).second);
2612 return true;
2613 }
2614
2615 m_device->SetTexture(stage, 0); // invalid texture
2616 return false; // should not happen normally
2617 }
2618
SetTexture(const Texture & tex,int stage)2619 void CEngine::SetTexture(const Texture& tex, int stage)
2620 {
2621 m_device->SetTexture(stage, tex);
2622 }
2623
SetTerrainVision(float vision)2624 void CEngine::SetTerrainVision(float vision)
2625 {
2626 m_terrainVision = vision;
2627 }
2628
SetFocus(float focus)2629 void CEngine::SetFocus(float focus)
2630 {
2631 m_focus = focus;
2632 m_size = m_app->GetVideoConfig().size;
2633
2634 float farPlane = m_deepView[0] * m_clippingDistance;
2635
2636 float aspect = static_cast<float>(m_size.x) / static_cast<float>(m_size.y);
2637
2638 // Compute H-FoV from V-FoV and aspect ratio.
2639 m_hfov = 2.0f * atan(aspect * tan(focus / 2.0f));
2640
2641 Math::LoadProjectionMatrix(m_matProj, m_focus, aspect, 0.5f, farPlane);
2642 }
2643
GetFocus()2644 float CEngine::GetFocus()
2645 {
2646 return m_focus;
2647 }
2648
GetVFovAngle()2649 float CEngine::GetVFovAngle()
2650 {
2651 return m_focus;
2652 }
2653
GetHFovAngle()2654 float CEngine::GetHFovAngle()
2655 {
2656 return m_hfov;
2657 }
2658
SetShadowColor(float value)2659 void CEngine::SetShadowColor(float value)
2660 {
2661 m_shadowColor = value;
2662 }
2663
GetShadowColor()2664 float CEngine::GetShadowColor()
2665 {
2666 return m_shadowColor;
2667 }
2668
SetShadowRange(float value)2669 void CEngine::SetShadowRange(float value)
2670 {
2671 m_shadowRange = value;
2672 }
2673
GetShadowRange()2674 float CEngine::GetShadowRange()
2675 {
2676 return m_shadowRange;
2677 }
2678
SetMultiSample(int value)2679 void CEngine::SetMultiSample(int value)
2680 {
2681 if(value == m_multisample) return;
2682 m_multisample = value;
2683 m_device->DeleteFramebuffer("multisample");
2684 }
2685
GetMultiSample()2686 int CEngine::GetMultiSample()
2687 {
2688 return m_multisample;
2689 }
2690
SetDirty(bool mode)2691 void CEngine::SetDirty(bool mode)
2692 {
2693 m_dirty = mode;
2694 }
2695
GetDirty()2696 bool CEngine::GetDirty()
2697 {
2698 return m_dirty;
2699 }
2700
SetFog(bool mode)2701 void CEngine::SetFog(bool mode)
2702 {
2703 m_fog = mode;
2704 }
2705
GetFog()2706 bool CEngine::GetFog()
2707 {
2708 return m_fog;
2709 }
2710
SetSecondTexture(const std::string & texNum)2711 void CEngine::SetSecondTexture(const std::string& texNum)
2712 {
2713 m_secondTex = texNum;
2714 }
2715
GetSecondTexture()2716 const std::string& CEngine::GetSecondTexture()
2717 {
2718 return m_secondTex;
2719 }
2720
SetRankView(int rank)2721 void CEngine::SetRankView(int rank)
2722 {
2723 if (rank < 0) rank = 0;
2724 if (rank > 1) rank = 1;
2725
2726 if (m_rankView == 0 && rank == 1) // enters the water?
2727 m_lightMan->AdaptLightColor(m_waterAddColor, +1.0f);
2728
2729 if (m_rankView == 1 && rank == 0) // out of the water?
2730 m_lightMan->AdaptLightColor(m_waterAddColor, -1.0f);
2731
2732 m_rankView = rank;
2733 }
2734
GetRankView()2735 int CEngine::GetRankView()
2736 {
2737 return m_rankView;
2738 }
2739
SetDrawWorld(bool draw)2740 void CEngine::SetDrawWorld(bool draw)
2741 {
2742 m_drawWorld = draw;
2743 }
2744
SetDrawFront(bool draw)2745 void CEngine::SetDrawFront(bool draw)
2746 {
2747 m_drawFront = draw;
2748 }
2749
SetAmbientColor(const Color & color,int rank)2750 void CEngine::SetAmbientColor(const Color& color, int rank)
2751 {
2752 m_ambientColor[rank] = color;
2753 }
2754
GetAmbientColor(int rank)2755 Color CEngine::GetAmbientColor(int rank)
2756 {
2757 return m_ambientColor[rank];
2758 }
2759
SetWaterAddColor(const Color & color)2760 void CEngine::SetWaterAddColor(const Color& color)
2761 {
2762 m_waterAddColor = color;
2763 }
2764
GetWaterAddColor()2765 Color CEngine::GetWaterAddColor()
2766 {
2767 return m_waterAddColor;
2768 }
2769
SetFogColor(const Color & color,int rank)2770 void CEngine::SetFogColor(const Color& color, int rank)
2771 {
2772 m_fogColor[rank] = color;
2773 }
2774
GetFogColor(int rank)2775 Color CEngine::GetFogColor(int rank)
2776 {
2777 return m_fogColor[rank];
2778 }
2779
SetDeepView(float length,int rank,bool ref)2780 void CEngine::SetDeepView(float length, int rank, bool ref)
2781 {
2782 if (ref)
2783 length *= m_clippingDistance;
2784
2785 m_deepView[rank] = length;
2786 }
2787
GetDeepView(int rank)2788 float CEngine::GetDeepView(int rank)
2789 {
2790 return m_deepView[rank];
2791 }
2792
SetFogStart(float start,int rank)2793 void CEngine::SetFogStart(float start, int rank)
2794 {
2795 if (start < 0.0f)
2796 m_fogStart[rank] = 0.0f;
2797 else
2798 m_fogStart[rank] = start;
2799 }
2800
GetFogStart(int rank)2801 float CEngine::GetFogStart(int rank)
2802 {
2803 return m_fogStart[rank];
2804 }
2805
SetBackground(const std::string & name,Color up,Color down,Color cloudUp,Color cloudDown,bool full,bool scale)2806 void CEngine::SetBackground(const std::string& name, Color up, Color down,
2807 Color cloudUp, Color cloudDown, bool full, bool scale)
2808 {
2809 if (m_backgroundTex.Valid() && name != m_backgroundName)
2810 {
2811 DeleteTexture(m_backgroundTex);
2812 m_backgroundTex.SetInvalid();
2813 }
2814
2815 m_backgroundName = name;
2816 m_backgroundColorUp = up;
2817 m_backgroundColorDown = down;
2818 m_backgroundCloudUp = cloudUp;
2819 m_backgroundCloudDown = cloudDown;
2820 m_backgroundFull = full;
2821 m_backgroundScale = scale;
2822
2823 if (! m_backgroundName.empty() && !m_backgroundTex.Valid())
2824 {
2825 TextureCreateParams params = m_defaultTexParams;
2826 params.padToNearestPowerOfTwo = true;
2827 m_backgroundTex = LoadTexture(m_backgroundName, params);
2828 }
2829 }
2830
GetBackground(std::string & name,Color & up,Color & down,Color & cloudUp,Color & cloudDown,bool & full,bool & scale)2831 void CEngine::GetBackground(std::string& name, Color& up, Color& down,
2832 Color& cloudUp, Color& cloudDown, bool &full, bool &scale)
2833 {
2834 name = m_backgroundName;
2835 up = m_backgroundColorUp;
2836 down = m_backgroundColorDown;
2837 cloudUp = m_backgroundCloudUp;
2838 cloudDown = m_backgroundCloudDown;
2839 full = m_backgroundFull;
2840 scale = m_backgroundScale;
2841 }
2842
SetForegroundName(const std::string & name)2843 void CEngine::SetForegroundName(const std::string& name)
2844 {
2845 if (m_foregroundTex.Valid() && name != m_foregroundName)
2846 {
2847 DeleteTexture(m_foregroundTex);
2848 m_foregroundTex.SetInvalid();
2849 }
2850
2851 m_foregroundName = name;
2852
2853 if (! m_foregroundName.empty() && !m_foregroundTex.Valid())
2854 m_foregroundTex = LoadTexture(m_foregroundName);
2855 }
2856
SetOverFront(bool front)2857 void CEngine::SetOverFront(bool front)
2858 {
2859 m_overFront = front;
2860 }
2861
SetOverColor(const Color & color,int mode)2862 void CEngine::SetOverColor(const Color& color, int mode)
2863 {
2864 m_overColor = color;
2865 m_overMode = mode;
2866 }
2867
SetParticleDensity(float value)2868 void CEngine::SetParticleDensity(float value)
2869 {
2870 if (value < 0.0f) value = 0.0f;
2871 if (value > 2.0f) value = 2.0f;
2872 m_particleDensity = value;
2873 }
2874
GetParticleDensity()2875 float CEngine::GetParticleDensity()
2876 {
2877 return m_particleDensity;
2878 }
2879
ParticleAdapt(float factor)2880 float CEngine::ParticleAdapt(float factor)
2881 {
2882 if (m_particleDensity == 0.0f)
2883 return 1000000.0f;
2884
2885 return factor / m_particleDensity;
2886 }
2887
SetClippingDistance(float value)2888 void CEngine::SetClippingDistance(float value)
2889 {
2890 if (value < 0.5f) value = 0.5f;
2891 if (value > 2.0f) value = 2.0f;
2892 m_clippingDistance = value;
2893 }
2894
GetClippingDistance()2895 float CEngine::GetClippingDistance()
2896 {
2897 return m_clippingDistance;
2898 }
2899
SetTextureFilterMode(TexFilter value)2900 void CEngine::SetTextureFilterMode(TexFilter value)
2901 {
2902 if(m_defaultTexParams.filter == value && m_terrainTexParams.filter == value) return;
2903
2904 m_defaultTexParams.filter = m_terrainTexParams.filter = value;
2905 m_defaultTexParams.mipmap = m_terrainTexParams.mipmap = (value == TEX_FILTER_TRILINEAR);
2906 ReloadAllTextures();
2907 }
2908
GetTextureFilterMode()2909 TexFilter CEngine::GetTextureFilterMode()
2910 {
2911 return m_terrainTexParams.filter;
2912 }
2913
SetTextureMipmapLevel(int value)2914 void CEngine::SetTextureMipmapLevel(int value)
2915 {
2916 if (value < 1) value = 1;
2917 if (value > 16) value = 16;
2918 if(m_textureMipmapLevel == value) return;
2919
2920 m_textureMipmapLevel = value;
2921 ReloadAllTextures();
2922 }
2923
GetTextureMipmapLevel()2924 int CEngine::GetTextureMipmapLevel()
2925 {
2926 return m_textureMipmapLevel;
2927 }
2928
SetTextureAnisotropyLevel(int value)2929 void CEngine::SetTextureAnisotropyLevel(int value)
2930 {
2931 if (value < 1) value = 1;
2932 if (value > 16) value = 16;
2933
2934 if(m_textureAnisotropy == value) return;
2935
2936 m_textureAnisotropy = value;
2937 ReloadAllTextures();
2938 }
2939
GetTextureAnisotropyLevel()2940 int CEngine::GetTextureAnisotropyLevel()
2941 {
2942 return m_textureAnisotropy;
2943 }
2944
IsShadowMappingSupported()2945 bool CEngine::IsShadowMappingSupported()
2946 {
2947 return m_device->IsShadowMappingSupported() && m_device->GetMaxTextureStageCount() >= 3;
2948 }
2949
SetShadowMapping(bool value)2950 void CEngine::SetShadowMapping(bool value)
2951 {
2952 if(!IsShadowMappingSupported()) value = false;
2953 if(value == m_shadowMapping) return;
2954 m_shadowMapping = value;
2955 if(!value)
2956 {
2957 m_device->DeleteFramebuffer("shadow");
2958 m_device->DestroyTexture(m_shadowMap);
2959 m_shadowMap.id = 0;
2960 }
2961 }
2962
GetShadowMapping()2963 bool CEngine::GetShadowMapping()
2964 {
2965 return m_shadowMapping;
2966 }
2967
SetShadowMappingOffscreen(bool value)2968 void CEngine::SetShadowMappingOffscreen(bool value)
2969 {
2970 if(!m_device->IsFramebufferSupported()) value = false;
2971 if(value == m_offscreenShadowRendering) return;
2972 m_offscreenShadowRendering = value;
2973 if(value)
2974 {
2975 m_device->DestroyTexture(m_shadowMap);
2976 m_shadowMap.id = 0;
2977 }
2978 else
2979 {
2980 m_device->DeleteFramebuffer("shadow");
2981 m_shadowMap.id = 0;
2982 }
2983 }
2984
GetShadowMappingOffscreen()2985 bool CEngine::GetShadowMappingOffscreen()
2986 {
2987 return m_offscreenShadowRendering;
2988 }
2989
SetShadowMappingOffscreenResolution(int resolution)2990 void CEngine::SetShadowMappingOffscreenResolution(int resolution)
2991 {
2992 resolution = Math::Min(resolution, m_device->GetMaxTextureSize());
2993 if(resolution == m_offscreenShadowRenderingResolution) return;
2994 m_offscreenShadowRenderingResolution = resolution;
2995 m_device->DeleteFramebuffer("shadow");
2996 m_shadowMap.id = 0;
2997 }
2998
GetShadowMappingOffscreenResolution()2999 int CEngine::GetShadowMappingOffscreenResolution()
3000 {
3001 return m_offscreenShadowRenderingResolution;
3002 }
3003
IsShadowMappingQualitySupported()3004 bool CEngine::IsShadowMappingQualitySupported()
3005 {
3006 return IsShadowMappingSupported() && m_device->GetMaxTextureStageCount() >= 3;
3007 }
3008
SetShadowMappingQuality(bool value)3009 void CEngine::SetShadowMappingQuality(bool value)
3010 {
3011 if(!IsShadowMappingQualitySupported()) value = false;
3012 m_qualityShadows = value;
3013 }
3014
GetShadowMappingQuality()3015 bool CEngine::GetShadowMappingQuality()
3016 {
3017 return m_qualityShadows;
3018 }
3019
SetTerrainShadows(bool value)3020 void CEngine::SetTerrainShadows(bool value)
3021 {
3022 m_terrainShadows = value;
3023 }
3024
GetTerrainShadows()3025 bool CEngine::GetTerrainShadows()
3026 {
3027 return m_terrainShadows;
3028 }
3029
SetVSync(int value)3030 void CEngine::SetVSync(int value)
3031 {
3032 if (value < -1) value = -1;
3033 if (value > 1) value = 1;
3034 if(m_vsync == value) return;
3035 m_vsync = value;
3036 }
3037
GetVSync()3038 int CEngine::GetVSync()
3039 {
3040 return m_vsync;
3041 }
3042
SetBackForce(bool present)3043 void CEngine::SetBackForce(bool present)
3044 {
3045 m_backForce = present;
3046 }
3047
GetBackForce()3048 bool CEngine::GetBackForce()
3049 {
3050 return m_backForce;
3051 }
3052
SetLightMode(bool present)3053 void CEngine::SetLightMode(bool present)
3054 {
3055 m_lightMode = present;
3056 }
3057
GetLightMode()3058 bool CEngine::GetLightMode()
3059 {
3060 return m_lightMode;
3061 }
3062
SetEditIndentMode(bool autoIndent)3063 void CEngine::SetEditIndentMode(bool autoIndent)
3064 {
3065 m_editIndentMode = autoIndent;
3066 }
3067
GetEditIndentMode()3068 bool CEngine::GetEditIndentMode()
3069 {
3070 return m_editIndentMode;
3071 }
3072
SetEditIndentValue(int value)3073 void CEngine::SetEditIndentValue(int value)
3074 {
3075 m_editIndentValue = value;
3076 }
3077
GetEditIndentValue()3078 int CEngine::GetEditIndentValue()
3079 {
3080 return m_editIndentValue;
3081 }
3082
SetTracePrecision(float factor)3083 void CEngine::SetTracePrecision(float factor)
3084 {
3085 m_tracePrecision = factor;
3086 }
3087
GetTracePrecision()3088 float CEngine::GetTracePrecision()
3089 {
3090 return m_tracePrecision;
3091 }
3092
SetMouseType(EngineMouseType type)3093 void CEngine::SetMouseType(EngineMouseType type)
3094 {
3095 m_mouseType = type;
3096 }
3097
GetMouseType()3098 EngineMouseType CEngine::GetMouseType()
3099 {
3100 return m_mouseType;
3101 }
3102
SetPauseBlurEnabled(bool enable)3103 void CEngine::SetPauseBlurEnabled(bool enable)
3104 {
3105 m_pauseBlurEnabled = enable;
3106 }
3107
GetPauseBlurEnabled()3108 bool CEngine::GetPauseBlurEnabled()
3109 {
3110 return m_pauseBlurEnabled;
3111 }
3112
GetMatView()3113 const Math::Matrix& CEngine::GetMatView()
3114 {
3115 return m_matView;
3116 }
3117
GetMatProj()3118 const Math::Matrix& CEngine::GetMatProj()
3119 {
3120 return m_matProj;
3121 }
3122
GetEyePt()3123 Math::Vector CEngine::GetEyePt()
3124 {
3125 return m_eyePt;
3126 }
3127
GetLookatPt()3128 Math::Vector CEngine::GetLookatPt()
3129 {
3130 return m_lookatPt;
3131 }
3132
GetEyeDirH()3133 float CEngine::GetEyeDirH()
3134 {
3135 return m_eyeDirH;
3136 }
3137
GetEyeDirV()3138 float CEngine::GetEyeDirV()
3139 {
3140 return m_eyeDirV;
3141 }
3142
IsVisiblePoint(const Math::Vector & pos)3143 bool CEngine::IsVisiblePoint(const Math::Vector &pos)
3144 {
3145 return Math::Distance(m_eyePt, pos) <= (m_deepView[0] * m_clippingDistance);
3146 }
3147
UpdateMatProj()3148 void CEngine::UpdateMatProj()
3149 {
3150 m_device->SetTransform(TRANSFORM_PROJECTION, m_matProj);
3151 }
3152
ApplyChange()3153 void CEngine::ApplyChange()
3154 {
3155 SetFocus(m_focus);
3156
3157 // recapture 3D scene
3158 if (m_worldCaptured)
3159 {
3160 m_captureWorld = true;
3161 m_worldCaptured = false;
3162 }
3163 }
3164
3165 /*******************************************************
3166 Rendering
3167 *******************************************************/
3168
3169
3170
3171 /**
3172 This function sets up render states, clears the
3173 viewport, and renders the scene. */
Render()3174 void CEngine::Render()
3175 {
3176 m_fpsCounter++;
3177
3178 m_systemUtils->GetCurrentTimeStamp(m_currentFrameTime);
3179 float diff = m_systemUtils->TimeStampDiff(m_lastFrameTime, m_currentFrameTime, STU_SEC);
3180 if (diff > 1.0f)
3181 {
3182 m_systemUtils->CopyTimeStamp(m_lastFrameTime, m_currentFrameTime);
3183
3184 m_fps = m_fpsCounter / diff;
3185 m_fpsCounter = 0;
3186 }
3187
3188 if (! m_render)
3189 return;
3190
3191 m_statisticTriangle = 0;
3192 m_lastState = -1;
3193 m_lastColor = Color(-1.0f);
3194 m_lastMaterial = Material();
3195
3196 m_lightMan->UpdateLights();
3197
3198 Color color;
3199 if (m_cloud->GetLevel() != 0.0f) // clouds?
3200 color = m_backgroundCloudDown;
3201 else
3202 color = m_backgroundColorDown;
3203
3204 m_device->SetClearColor(color);
3205
3206 // Begin the scene
3207 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true);
3208
3209 m_device->BeginScene();
3210
3211 // use currently captured scene for world
3212 if (m_worldCaptured && !m_captureWorld)
3213 {
3214 DrawCaptured3DScene();
3215 }
3216 else
3217 {
3218 // Render shadow map
3219 if (m_drawWorld && m_shadowMapping)
3220 RenderShadowMap();
3221
3222 UseMSAA(true);
3223
3224 DrawBackground(); // draws the background
3225
3226 if (m_drawWorld)
3227 Draw3DScene();
3228
3229 UseMSAA(false);
3230
3231 // marked to capture currently rendered world
3232 if (m_captureWorld)
3233 {
3234 Capture3DScene();
3235 m_device->Clear();
3236 DrawCaptured3DScene();
3237 }
3238 }
3239
3240 CProfiler::StartPerformanceCounter(PCNT_RENDER_INTERFACE);
3241 DrawInterface();
3242 CProfiler::StopPerformanceCounter(PCNT_RENDER_INTERFACE);
3243
3244 // End the scene
3245 m_device->EndScene();
3246 }
3247
Draw3DScene()3248 void CEngine::Draw3DScene()
3249 {
3250 if (!m_worldCaptured)
3251 {
3252 if (m_capturedWorldTexture.Valid())
3253 {
3254 m_device->DestroyTexture(m_capturedWorldTexture);
3255 m_capturedWorldTexture = Texture();
3256 }
3257 }
3258
3259 m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false);
3260
3261 UpdateGroundSpotTextures();
3262
3263 DrawPlanet(); // draws the planets
3264 m_cloud->Draw(); // draws the clouds
3265
3266 // Display the objects
3267
3268 m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true);
3269 m_device->SetRenderState(RENDER_STATE_LIGHTING, true);
3270 m_device->SetRenderState(RENDER_STATE_FOG, true);
3271
3272 float fogStart = m_deepView[m_rankView] * m_fogStart[m_rankView] * m_clippingDistance;
3273 float fogEnd = m_deepView[m_rankView] * m_clippingDistance;
3274 m_device->SetFogParams(FOG_LINEAR, m_fogColor[m_rankView], fogStart, fogEnd, 1.0f);
3275
3276 m_device->SetTransform(TRANSFORM_PROJECTION, m_matProj);
3277 m_device->SetTransform(TRANSFORM_VIEW, m_matView);
3278
3279 // TODO: This causes a rendering artifact and I can't see anything that breaks if you just comment it out
3280 // So I'll just leave it like that for now ~krzys_h
3281 //m_water->DrawBack(); // draws water background
3282
3283 CProfiler::StartPerformanceCounter(PCNT_RENDER_TERRAIN);
3284
3285 // Draw terrain
3286
3287 m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN);
3288
3289 UseShadowMapping(true);
3290
3291 for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
3292 {
3293 if (! m_objects[objRank].used)
3294 continue;
3295
3296 if (m_objects[objRank].type != ENG_OBJTYPE_TERRAIN)
3297 continue;
3298
3299 if (! m_objects[objRank].drawWorld)
3300 continue;
3301
3302 m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform);
3303
3304 if (! IsVisible(objRank))
3305 continue;
3306
3307 int baseObjRank = m_objects[objRank].baseObjRank;
3308 if (baseObjRank == -1)
3309 continue;
3310
3311 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
3312
3313 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
3314 if (! p1.used)
3315 continue;
3316
3317 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
3318 {
3319 EngineBaseObjTexTier& p2 = p1.next[l2];
3320
3321 SetTexture(p2.tex1, 0);
3322 SetTexture(p2.tex2, 1);
3323
3324 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
3325 {
3326 EngineBaseObjDataTier& p3 = p2.next[l3];
3327
3328 SetMaterial(p3.material);
3329 SetState(p3.state);
3330
3331 DrawObject(p3);
3332 }
3333 }
3334 }
3335
3336 if (!m_qualityShadows)
3337 UseShadowMapping(false);
3338
3339 // Draws the old-style shadow spots, if shadow mapping disabled
3340 if (!m_shadowMapping)
3341 DrawShadowSpots();
3342
3343 CProfiler::StopPerformanceCounter(PCNT_RENDER_TERRAIN);
3344
3345 // Draw other objects
3346
3347 CProfiler::StartPerformanceCounter(PCNT_RENDER_OBJECTS);
3348
3349 bool transparent = false;
3350
3351 for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
3352 {
3353 if (! m_objects[objRank].used)
3354 continue;
3355
3356 if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN)
3357 continue;
3358
3359 if (! m_objects[objRank].drawWorld)
3360 continue;
3361
3362 m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform);
3363
3364 if (! IsVisible(objRank))
3365 continue;
3366
3367 int baseObjRank = m_objects[objRank].baseObjRank;
3368 if (baseObjRank == -1)
3369 continue;
3370
3371 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
3372
3373 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
3374 if (! p1.used)
3375 continue;
3376
3377 m_lightMan->UpdateDeviceLights(m_objects[objRank].type);
3378
3379 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
3380 {
3381 EngineBaseObjTexTier& p2 = p1.next[l2];
3382
3383 SetTexture(p2.tex1, 0);
3384 SetTexture(p2.tex2, 1);
3385
3386 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
3387 {
3388 EngineBaseObjDataTier& p3 = p2.next[l3];
3389
3390 if (m_objects[objRank].transparency != 0.0f) // transparent ?
3391 {
3392 transparent = true;
3393 continue;
3394 }
3395
3396 SetMaterial(p3.material);
3397 SetState(p3.state);
3398
3399 DrawObject(p3);
3400 }
3401 }
3402 }
3403
3404 UseShadowMapping(false);
3405
3406 // Draw transparent objects
3407
3408 if (transparent)
3409 {
3410 int tState = ENG_RSTATE_TTEXTURE_BLACK | ENG_RSTATE_2FACE;
3411 Color tColor = Color(68.0f / 255.0f, 68.0f / 255.0f, 68.0f / 255.0f, 68.0f / 255.0f);
3412
3413 for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
3414 {
3415 if (! m_objects[objRank].used)
3416 continue;
3417
3418 if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN)
3419 continue;
3420
3421 if (! m_objects[objRank].drawWorld)
3422 continue;
3423
3424 m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform);
3425
3426 if (! IsVisible(objRank))
3427 continue;
3428
3429 int baseObjRank = m_objects[objRank].baseObjRank;
3430 if (baseObjRank == -1)
3431 continue;
3432
3433 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
3434
3435 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
3436 if (! p1.used)
3437 continue;
3438
3439 m_lightMan->UpdateDeviceLights(m_objects[objRank].type);
3440
3441 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
3442 {
3443 EngineBaseObjTexTier& p2 = p1.next[l2];
3444
3445 SetTexture(p2.tex1, 0);
3446 SetTexture(p2.tex2, 1);
3447
3448 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
3449 {
3450 EngineBaseObjDataTier& p3 = p2.next[l3];
3451
3452 if (m_objects[objRank].transparency == 0.0f)
3453 continue;
3454
3455 SetMaterial(p3.material);
3456 SetState(tState, tColor);
3457
3458 DrawObject(p3);
3459 }
3460 }
3461 }
3462 }
3463
3464 CProfiler::StopPerformanceCounter(PCNT_RENDER_OBJECTS);
3465
3466 m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN);
3467
3468 if (m_debugLights)
3469 m_device->DebugLights();
3470
3471 if (m_debugDumpLights)
3472 {
3473 m_debugDumpLights = false;
3474 m_lightMan->DebugDumpLights();
3475 }
3476
3477 CProfiler::StartPerformanceCounter(PCNT_RENDER_WATER);
3478 m_water->DrawSurf(); // draws water surface
3479 CProfiler::StopPerformanceCounter(PCNT_RENDER_WATER);
3480
3481 m_device->SetRenderState(RENDER_STATE_LIGHTING, false);
3482
3483 RenderPendingDebugDraws();
3484
3485 if (m_debugGoto)
3486 {
3487 Math::Matrix worldMatrix;
3488 worldMatrix.LoadIdentity();
3489 m_device->SetTransform(TRANSFORM_WORLD, worldMatrix);
3490
3491 SetState(ENG_RSTATE_OPAQUE_COLOR);
3492
3493 for (const auto& line : m_displayGoto)
3494 {
3495 m_device->DrawPrimitive(PRIMITIVE_LINE_STRIP, line.data(), line.size());
3496 }
3497 }
3498 m_displayGoto.clear();
3499
3500 CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE_WORLD);
3501 m_particle->DrawParticle(SH_WORLD); // draws the particles of the 3D world
3502 CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE_WORLD);
3503
3504 m_device->SetRenderState(RENDER_STATE_LIGHTING, true);
3505
3506 m_lightning->Draw(); // draws lightning
3507
3508 DrawForegroundImage(); // draws the foreground
3509
3510 if (! m_overFront) DrawOverColor(); // draws the foreground color
3511 }
3512
Capture3DScene()3513 void CEngine::Capture3DScene()
3514 {
3515 // destroy existing texture
3516 if (m_capturedWorldTexture.Valid())
3517 {
3518 m_device->DestroyTexture(m_capturedWorldTexture);
3519 m_capturedWorldTexture = Texture();
3520 }
3521
3522 // obtain pixels from screen
3523 int width = m_size.x;
3524 int height = m_size.y;
3525
3526 auto pixels = m_device->GetFrameBufferPixels();
3527 unsigned char* data = reinterpret_cast<unsigned char*>(pixels->GetPixelsData());
3528
3529 // calculate 2nd mipmap
3530 int newWidth = width / 4;
3531 int newHeight = height / 4;
3532 std::unique_ptr<unsigned char[]> mipmap = MakeUniqueArray<unsigned char>(4 * newWidth * newHeight);
3533
3534 for (int x = 0; x < newWidth; x++)
3535 {
3536 for (int y = 0; y < newHeight; y++)
3537 {
3538 float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
3539
3540 for (int i = 0; i < 4; i++)
3541 {
3542 for (int j = 0; j < 4; j++)
3543 {
3544 int index = 4 * ((4 * x + i) + width * (4 * y + j));
3545
3546 for (int k = 0; k < 4; k++)
3547 color[k] += data[index + k];
3548 }
3549 }
3550
3551 int index = 4 * (x + newWidth * y);
3552
3553 for (int k = 0; k < 4; k++)
3554 {
3555 mipmap[index + k] = static_cast<unsigned char>(color[k] * (1.0f / 16.0f));
3556 }
3557 }
3558 }
3559
3560 // calculate Gaussian blur
3561 std::unique_ptr<unsigned char[]> blured = MakeUniqueArray<unsigned char>(4 * newWidth * newHeight);
3562
3563 float matrix[7][7] =
3564 {
3565 { 0.00000067f, 0.00002292f, 0.00019117f, 0.00038771f, 0.00019117f, 0.00002292f, 0.00000067f },
3566 { 0.00002292f, 0.00078634f, 0.00655965f, 0.01330373f, 0.00655965f, 0.00078633f, 0.00002292f },
3567 { 0.00019117f, 0.00655965f, 0.05472157f, 0.11098164f, 0.05472157f, 0.00655965f, 0.00019117f },
3568 { 0.00038771f, 0.01330373f, 0.11098164f, 0.22508352f, 0.11098164f, 0.01330373f, 0.00038771f },
3569 { 0.00019117f, 0.00655965f, 0.05472157f, 0.11098164f, 0.05472157f, 0.00655965f, 0.00019117f },
3570 { 0.00002292f, 0.00078633f, 0.00655965f, 0.01330373f, 0.00655965f, 0.00078633f, 0.00002292f },
3571 { 0.00000067f, 0.00002292f, 0.00019117f, 0.00038771f, 0.00019117f, 0.00002292f, 0.00000067f }
3572 };
3573
3574 for (int x = 0; x < newWidth; x++)
3575 {
3576 for (int y = 0; y < newHeight; y++)
3577 {
3578 float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
3579
3580 for (int i = -3; i <= 3; i++)
3581 {
3582 for (int j = -3; j <= 3; j++)
3583 {
3584 int xp = Math::Clamp(x + i, 0, newWidth - 1);
3585 int yp = Math::Clamp(y + j, 0, newHeight - 1);
3586
3587 float weight = matrix[i + 3][j + 3];
3588
3589 int index = 4 * (newWidth * yp + xp);
3590
3591 for (int k = 0; k < 4; k++)
3592 color[k] += weight * mipmap[index + k];
3593 }
3594 }
3595
3596 int index = 4 * (newWidth * y + x);
3597
3598 for (int k = 0; k < 4; k++)
3599 {
3600 float value = Math::Clamp(color[k], 0.0f, 255.0f);
3601 blured[index + k] = static_cast<unsigned char>(value);
3602 }
3603 }
3604 }
3605
3606 // create SDL surface and final texture
3607 ImageData image;
3608 image.surface = SDL_CreateRGBSurfaceFrom(blured.get(), newWidth, newHeight, 32, 0, 0, 0, 0, 0xFF000000);
3609
3610 TextureCreateParams params;
3611 params.filter = TEX_FILTER_BILINEAR;
3612 params.format = TEX_IMG_RGBA;
3613 params.mipmap = false;
3614
3615 m_capturedWorldTexture = m_device->CreateTexture(&image, params);
3616
3617 SDL_FreeSurface(image.surface);
3618
3619 m_captureWorld = false;
3620 m_worldCaptured = true;
3621 }
3622
DrawCaptured3DScene()3623 void CEngine::DrawCaptured3DScene()
3624 {
3625 Math::Matrix identity;
3626
3627 m_device->SetTransform(TRANSFORM_PROJECTION, identity);
3628 m_device->SetTransform(TRANSFORM_VIEW, identity);
3629 m_device->SetTransform(TRANSFORM_WORLD, identity);
3630
3631 m_device->SetRenderState(RENDER_STATE_BLENDING, false);
3632 m_device->SetRenderState(RENDER_STATE_CULLING, false);
3633
3634 Vertex vertices[4];
3635
3636 vertices[0] = Vertex(Math::Vector(-1.0f, -1.0f, 0.0f), Math::Vector(0.0f, 1.0f, 0.0f), Math::Point(0.0f, 0.0f));
3637 vertices[1] = Vertex(Math::Vector(1.0f, -1.0f, 0.0f), Math::Vector(0.0f, 1.0f, 0.0f), Math::Point(1.0f, 0.0f));
3638 vertices[2] = Vertex(Math::Vector(-1.0f, 1.0f, 0.0f), Math::Vector(0.0f, 1.0f, 0.0f), Math::Point(0.0f, 1.0f));
3639 vertices[3] = Vertex(Math::Vector(1.0f, 1.0f, 0.0f), Math::Vector(0.0f, 1.0f, 0.0f), Math::Point(1.0f, 1.0f));
3640
3641 m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false);
3642
3643 m_device->SetTexture(TEXTURE_PRIMARY, m_capturedWorldTexture);
3644 m_device->SetTextureEnabled(TEXTURE_PRIMARY, true);
3645 m_device->SetTextureEnabled(TEXTURE_SECONDARY, false);
3646
3647 m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertices, 4);
3648 }
3649
RenderDebugSphere(const Math::Sphere & sphere,const Math::Matrix & transform,const Gfx::Color & color)3650 void CEngine::RenderDebugSphere(const Math::Sphere& sphere, const Math::Matrix& transform, const Gfx::Color& color)
3651 {
3652 static constexpr int LONGITUDE_DIVISIONS = 16;
3653 static constexpr int LATITUDE_DIVISIONS = 8;
3654 static constexpr int NUM_LINE_STRIPS = 2 + LONGITUDE_DIVISIONS + LATITUDE_DIVISIONS;
3655 static constexpr int VERTS_IN_LINE_STRIP = 32;
3656
3657 static std::array<Math::Vector, NUM_LINE_STRIPS * VERTS_IN_LINE_STRIP> verticesTemplate = []
3658 {
3659 std::array<Math::Vector, NUM_LINE_STRIPS * VERTS_IN_LINE_STRIP> vertices;
3660
3661 auto SpherePoint = [&](float latitude, float longitude)
3662 {
3663 float latitudeAngle = (latitude - 0.5f) * 2.0f * Math::PI;
3664 float longitudeAngle = longitude * 2.0f * Math::PI;
3665 return Math::Vector(sinf(latitudeAngle) * cosf(longitudeAngle),
3666 cosf(latitudeAngle),
3667 sinf(latitudeAngle) * sinf(longitudeAngle));
3668 };
3669
3670 auto vert = vertices.begin();
3671
3672 for (int longitudeDivision = 0; longitudeDivision <= LONGITUDE_DIVISIONS; ++longitudeDivision)
3673 {
3674 for (int segment = 0; segment < VERTS_IN_LINE_STRIP; ++segment)
3675 {
3676 float latitude = static_cast<float>(segment) / VERTS_IN_LINE_STRIP;
3677 float longitude = static_cast<float>(longitudeDivision) / (LONGITUDE_DIVISIONS);
3678 *vert++ = SpherePoint(latitude, longitude);
3679 }
3680 }
3681
3682 for (int latitudeDivision = 0; latitudeDivision <= LATITUDE_DIVISIONS; ++latitudeDivision)
3683 {
3684 for (int segment = 0; segment < VERTS_IN_LINE_STRIP; ++segment)
3685 {
3686 float latitude = static_cast<float>(latitudeDivision + 1) / (LATITUDE_DIVISIONS + 2);
3687 float longitude = static_cast<float>(segment) / VERTS_IN_LINE_STRIP;
3688 *vert++ = SpherePoint(latitude, longitude);
3689 }
3690 }
3691 return vertices;
3692 }();
3693
3694
3695 const int firstDraw = m_pendingDebugDraws.firsts.size();
3696 const int firstVert = m_pendingDebugDraws.vertices.size();
3697
3698 m_pendingDebugDraws.firsts.resize(m_pendingDebugDraws.firsts.size() + NUM_LINE_STRIPS);
3699 m_pendingDebugDraws.counts.resize(m_pendingDebugDraws.counts.size() + NUM_LINE_STRIPS);
3700 m_pendingDebugDraws.vertices.resize(m_pendingDebugDraws.vertices.size() + verticesTemplate.size());
3701
3702 for (int i = 0; i < NUM_LINE_STRIPS; ++i)
3703 {
3704 m_pendingDebugDraws.firsts[i + firstDraw] = firstVert + i * VERTS_IN_LINE_STRIP;
3705 }
3706
3707 for (int i = 0; i < NUM_LINE_STRIPS; ++i)
3708 {
3709 m_pendingDebugDraws.counts[i + firstDraw] = VERTS_IN_LINE_STRIP;
3710 }
3711
3712 for (std::size_t i = 0; i < verticesTemplate.size(); ++i)
3713 {
3714 auto pos = Math::MatrixVectorMultiply(transform, sphere.pos + verticesTemplate[i] * sphere.radius);
3715 m_pendingDebugDraws.vertices[i + firstVert] = VertexCol{pos, color};
3716 }
3717 }
3718
RenderDebugBox(const Math::Vector & mins,const Math::Vector & maxs,const Math::Matrix & transform,const Gfx::Color & color)3719 void CEngine::RenderDebugBox(const Math::Vector& mins, const Math::Vector& maxs, const Math::Matrix& transform, const Gfx::Color& color)
3720 {
3721 static constexpr int NUM_LINE_STRIPS = 4;
3722 static constexpr int VERTS_IN_LINE_STRIP = 4;
3723
3724 const int firstDraw = m_pendingDebugDraws.firsts.size();
3725 const int firstVert = m_pendingDebugDraws.vertices.size();
3726
3727 m_pendingDebugDraws.firsts.resize(m_pendingDebugDraws.firsts.size() + NUM_LINE_STRIPS);
3728 m_pendingDebugDraws.counts.resize(m_pendingDebugDraws.counts.size() + NUM_LINE_STRIPS);
3729 m_pendingDebugDraws.vertices.resize(m_pendingDebugDraws.vertices.size() + NUM_LINE_STRIPS * VERTS_IN_LINE_STRIP);
3730
3731 for (int i = 0; i < NUM_LINE_STRIPS; ++i)
3732 {
3733 m_pendingDebugDraws.firsts[i + firstDraw] = firstVert + (i * VERTS_IN_LINE_STRIP);
3734 }
3735
3736 for (int i = 0; i < NUM_LINE_STRIPS; ++i)
3737 {
3738 m_pendingDebugDraws.counts[i + firstDraw] = NUM_LINE_STRIPS;
3739 }
3740
3741 auto vert = m_pendingDebugDraws.vertices.begin() + firstVert;
3742
3743 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{mins.x, mins.y, mins.z}), color};
3744 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{maxs.x, mins.y, mins.z}), color};
3745 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{maxs.x, maxs.y, mins.z}), color};
3746 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{maxs.x, maxs.y, maxs.z}), color};
3747
3748 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{mins.x, mins.y, maxs.z}), color};
3749 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{mins.x, mins.y, mins.z}), color};
3750 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{mins.x, maxs.y, mins.z}), color};
3751 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{maxs.x, maxs.y, mins.z}), color};
3752
3753 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{maxs.x, mins.y, maxs.z}), color};
3754 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{mins.x, mins.y, maxs.z}), color};
3755 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{mins.x, maxs.y, maxs.z}), color};
3756 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{mins.x, maxs.y, mins.z}), color};
3757
3758 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{maxs.x, mins.y, mins.z}), color};
3759 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{maxs.x, mins.y, maxs.z}), color};
3760 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{maxs.x, maxs.y, maxs.z}), color};
3761 *vert++ = VertexCol{Math::MatrixVectorMultiply(transform, Math::Vector{mins.x, maxs.y, maxs.z}), color};
3762 }
3763
RenderPendingDebugDraws()3764 void CEngine::RenderPendingDebugDraws()
3765 {
3766 if (m_pendingDebugDraws.firsts.empty()) return;
3767
3768 m_device->SetTransform(TRANSFORM_WORLD, Math::Matrix{});
3769
3770 SetState(ENG_RSTATE_OPAQUE_COLOR);
3771
3772 m_device->DrawPrimitives(PRIMITIVE_LINE_STRIP,
3773 m_pendingDebugDraws.vertices.data(),
3774 m_pendingDebugDraws.firsts.data(),
3775 m_pendingDebugDraws.counts.data(),
3776 m_pendingDebugDraws.firsts.size());
3777
3778 m_pendingDebugDraws.firsts.clear();
3779 m_pendingDebugDraws.counts.clear();
3780 m_pendingDebugDraws.vertices.clear();
3781 }
3782
RenderShadowMap()3783 void CEngine::RenderShadowMap()
3784 {
3785 m_shadowMapping = m_shadowMapping && m_device->IsShadowMappingSupported();
3786 m_offscreenShadowRendering = m_offscreenShadowRendering && m_device->IsFramebufferSupported();
3787 m_offscreenShadowRenderingResolution = Math::Min(m_offscreenShadowRenderingResolution, m_device->GetMaxTextureSize());
3788
3789 if (!m_shadowMapping) return;
3790
3791 CProfiler::StartPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
3792
3793 // If no shadow map texture exists, create it
3794 if (m_shadowMap.id == 0)
3795 {
3796 int width = 256, height = 256;
3797 int depth;
3798
3799 if (m_offscreenShadowRendering)
3800 {
3801 width = height = m_offscreenShadowRenderingResolution;
3802
3803 FramebufferParams params;
3804 params.width = params.height = width;
3805 params.depth = depth = 32;
3806 params.colorAttachment = FramebufferParams::AttachmentType::None;
3807 params.depthAttachment = FramebufferParams::AttachmentType::Texture;
3808
3809 CFramebuffer *framebuffer = m_device->CreateFramebuffer("shadow", params);
3810 if (framebuffer == nullptr)
3811 {
3812 GetLogger()->Error("Could not create shadow mapping framebuffer, disabling dynamic shadows\n");
3813 m_shadowMapping = false;
3814 m_offscreenShadowRendering = false;
3815 m_qualityShadows = false;
3816 CProfiler::StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
3817 return;
3818 }
3819
3820 m_shadowMap.id = framebuffer->GetDepthTexture();
3821 m_shadowMap.size = Math::IntPoint(width, height);
3822 }
3823 else
3824 {
3825 int min = Math::Min(m_size.x, m_size.y);
3826
3827 for (int i = 0; i < 16; i++)
3828 {
3829 if (min < (1 << i)) break;
3830
3831 width = height = 1 << i;
3832 }
3833
3834 depth = m_app->GetInstance().GetVideoConfig().depthSize;
3835
3836 m_shadowMap = m_device->CreateDepthTexture(width, height, depth);
3837 }
3838
3839 GetLogger()->Info("Created shadow map texture: %dx%d, depth %d\n", width, height, depth);
3840 }
3841
3842 if (m_offscreenShadowRendering)
3843 {
3844 m_device->GetFramebuffer("shadow")->Bind();
3845 }
3846
3847 m_device->SetRenderMode(RENDER_MODE_SHADOW);
3848 m_device->Clear();
3849
3850 // change state to rendering shadow maps
3851 m_device->SetColorMask(false, false, false, false);
3852 m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true);
3853 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true);
3854 m_device->SetRenderState(RENDER_STATE_BLENDING, false);
3855 m_device->SetRenderState(RENDER_STATE_LIGHTING, false);
3856 m_device->SetRenderState(RENDER_STATE_FOG, false);
3857 m_device->SetRenderState(RENDER_STATE_CULLING, false);
3858 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, true);
3859 m_device->SetAlphaTestFunc(COMP_FUNC_GREATER, 0.5f);
3860 m_device->SetRenderState(RENDER_STATE_DEPTH_BIAS, true);
3861 m_device->SetDepthBias(2.0f, 8.0f);
3862 m_device->SetViewport(0, 0, m_shadowMap.size.x, m_shadowMap.size.y);
3863
3864 // recompute matrices
3865 Math::Vector worldUp(0.0f, 1.0f, 0.0f);
3866 Math::Vector lightDir = Math::Vector(1.0f, 2.0f, -1.0f);
3867 Math::Vector dir = m_lookatPt - m_eyePt;
3868 dir.y = 0.0f;
3869 dir.Normalize();
3870
3871 float dist = m_shadowRange;
3872 float depth = 200.0f;
3873
3874 if (dist < 0.5f)
3875 {
3876 float scale = log(m_shadowMap.size.x) / log(2.0f) - 6.5f;
3877 dist = 75.0f * scale;
3878 }
3879
3880 Math::Vector pos = m_lookatPt + 0.25f * dist * dir;
3881
3882 {
3883 // To prevent 'shadow shimmering', we ensure that the position only moves in texel-sized
3884 // increments. To do this we transform the position to a space where the light's forward/right/up
3885 // axes are aligned with the x/y/z axes (not necessarily in that order, and +/- signs don't matter).
3886 Math::Matrix lightRotation;
3887 Math::LoadViewMatrix(lightRotation, Math::Vector{}, lightDir, worldUp);
3888 pos = Math::MatrixVectorMultiply(lightRotation, pos);
3889 // ...then we round to the nearest worldUnitsPerTexel:
3890 const float worldUnitsPerTexel = (dist * 2.0f) / m_shadowMap.size.x;
3891 pos /= worldUnitsPerTexel;
3892 pos.x = round(pos.x);
3893 pos.y = round(pos.y);
3894 pos.z = round(pos.z);
3895 pos *= worldUnitsPerTexel;
3896 // ...and convert back to world space.
3897 pos = Math::MatrixVectorMultiply(lightRotation.Inverse(), pos);
3898 }
3899
3900 Math::Vector lookAt = pos - lightDir;
3901
3902 Math::LoadOrthoProjectionMatrix(m_shadowProjMat, -dist, dist, -dist, dist, -depth, depth);
3903 Math::LoadViewMatrix(m_shadowViewMat, pos, lookAt, worldUp);
3904
3905 Math::Matrix scaleMat;
3906 Math::LoadScaleMatrix(scaleMat, Math::Vector(1.0f, 1.0f, -1.0f));
3907 m_shadowViewMat = Math::MultiplyMatrices(scaleMat, m_shadowViewMat);
3908
3909 Math::Matrix temporary = Math::MultiplyMatrices(m_shadowProjMat, m_shadowViewMat);
3910 m_shadowTextureMat = Math::MultiplyMatrices(m_shadowBias, temporary);
3911
3912 m_shadowViewMat = Math::MultiplyMatrices(scaleMat, m_shadowViewMat);
3913
3914 m_device->SetTransform(TRANSFORM_PROJECTION, m_shadowProjMat);
3915 m_device->SetTransform(TRANSFORM_VIEW, m_shadowViewMat);
3916
3917 m_device->SetTexture(0, 0);
3918 m_device->SetTexture(1, 0);
3919 m_device->SetTexture(2, 0);
3920
3921 // render objects into shadow map
3922 for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
3923 {
3924 if (!m_objects[objRank].used)
3925 continue;
3926
3927 bool terrain = (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN);
3928
3929 if (terrain)
3930 {
3931 if (m_terrainShadows)
3932 {
3933 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
3934 m_device->SetRenderState(RENDER_STATE_CULLING, true);
3935 m_device->SetCullMode(CULL_CCW);
3936 }
3937 else
3938 continue;
3939 }
3940 else
3941 {
3942 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, true);
3943 m_device->SetRenderState(RENDER_STATE_CULLING, false);
3944 }
3945
3946 m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform);
3947
3948 if (!IsVisible(objRank))
3949 continue;
3950
3951 int baseObjRank = m_objects[objRank].baseObjRank;
3952 if (baseObjRank == -1)
3953 continue;
3954
3955 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
3956
3957 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
3958 if (!p1.used)
3959 continue;
3960
3961 for (int l2 = 0; l2 < static_cast<int>(p1.next.size()); l2++)
3962 {
3963 EngineBaseObjTexTier& p2 = p1.next[l2];
3964
3965 SetTexture(p2.tex1, 0);
3966
3967 for (int l3 = 0; l3 < static_cast<int>(p2.next.size()); l3++)
3968 {
3969 EngineBaseObjDataTier& p3 = p2.next[l3];
3970
3971 DrawObject(p3);
3972 }
3973 }
3974 }
3975
3976 m_device->SetRenderState(RENDER_STATE_DEPTH_BIAS, false);
3977 m_device->SetDepthBias(0.0f, 0.0f);
3978 m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false);
3979 m_device->SetRenderState(RENDER_STATE_CULLING, false);
3980 m_device->SetCullMode(CULL_CW);
3981
3982 if (m_offscreenShadowRendering) // shadow map texture already have depth information, just unbind it
3983 {
3984 m_device->GetFramebuffer("shadow")->Unbind();
3985 }
3986 else // copy depth buffer to shadow map
3987 {
3988 m_device->CopyFramebufferToTexture(m_shadowMap, 0, 0, 0, 0, m_shadowMap.size.x, m_shadowMap.size.y);
3989 }
3990
3991 // restore default state
3992 m_device->SetViewport(0, 0, m_size.x, m_size.y);
3993
3994 m_device->SetColorMask(true, true, true, true);
3995 m_device->Clear();
3996
3997 CProfiler::StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
3998
3999 m_device->SetRenderMode(RENDER_MODE_NORMAL);
4000 m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false);
4001 }
4002
UseShadowMapping(bool enable)4003 void CEngine::UseShadowMapping(bool enable)
4004 {
4005 if (!m_shadowMapping) return;
4006 if (m_shadowMap.id == 0) return;
4007
4008 if (enable)
4009 {
4010 m_device->SetShadowColor(m_shadowColor);
4011 m_device->SetTransform(TRANSFORM_SHADOW, m_shadowTextureMat);
4012 m_device->SetTexture(TEXTURE_SHADOW, m_shadowMap);
4013 m_device->SetTextureStageWrap(TEXTURE_SHADOW, TEX_WRAP_CLAMP_TO_BORDER, TEX_WRAP_CLAMP_TO_BORDER);
4014 m_device->SetRenderState(RENDER_STATE_SHADOW_MAPPING, true);
4015 }
4016 else
4017 {
4018 m_device->SetRenderState(RENDER_STATE_SHADOW_MAPPING, false);
4019 m_device->SetTexture(TEXTURE_SHADOW, 0);
4020 }
4021 }
4022
UseMSAA(bool enable)4023 void CEngine::UseMSAA(bool enable)
4024 {
4025 m_multisample = Math::Min(m_device->GetMaxSamples(), m_multisample);
4026 if (m_multisample < 2) return;
4027
4028 if (enable)
4029 {
4030 if (m_multisample > 1)
4031 {
4032 CFramebuffer* framebuffer = m_device->GetFramebuffer("multisample");
4033
4034 if (framebuffer == nullptr)
4035 {
4036 CFramebuffer* screen = m_device->GetFramebuffer("default");
4037
4038 FramebufferParams params;
4039 params.width = screen->GetWidth();
4040 params.height = screen->GetHeight();
4041 params.depth = 24;
4042 params.samples = m_multisample;
4043
4044 framebuffer = m_device->CreateFramebuffer("multisample", params);
4045
4046 if (framebuffer == nullptr)
4047 {
4048 GetLogger()->Error("Could not create MSAA framebuffer, disabling MSAA\n");
4049 m_multisample = 1;
4050 }
4051 }
4052
4053 if (framebuffer != nullptr)
4054 {
4055 framebuffer->Bind();
4056 }
4057
4058 m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true);
4059 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true);
4060
4061 m_device->Clear();
4062 }
4063 }
4064 else
4065 {
4066 if (m_multisample > 1)
4067 {
4068 CFramebuffer* framebuffer = m_device->GetFramebuffer("multisample");
4069 framebuffer->Unbind();
4070
4071 CFramebuffer* screen = m_device->GetFramebuffer("default");
4072
4073 int width = screen->GetWidth();
4074 int height = screen->GetHeight();
4075
4076 framebuffer->CopyToScreen(0, 0, width, height, 0, 0, width, height);
4077 }
4078 }
4079 }
4080
DrawObject(const EngineBaseObjDataTier & p4)4081 void CEngine::DrawObject(const EngineBaseObjDataTier& p4)
4082 {
4083 if (p4.staticBufferId != 0)
4084 {
4085 m_device->DrawStaticBuffer(p4.staticBufferId);
4086
4087 if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES)
4088 m_statisticTriangle += p4.vertices.size() / 3;
4089 else
4090 m_statisticTriangle += p4.vertices.size() - 2;
4091 }
4092 else
4093 {
4094 if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES)
4095 {
4096 m_device->DrawPrimitive(PRIMITIVE_TRIANGLES, &p4.vertices[0], p4.vertices.size());
4097 m_statisticTriangle += p4.vertices.size() / 3;
4098 }
4099 else
4100 {
4101 m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, &p4.vertices[0], p4.vertices.size() );
4102 m_statisticTriangle += p4.vertices.size() - 2;
4103 }
4104 }
4105 }
4106
DrawInterface()4107 void CEngine::DrawInterface()
4108 {
4109 m_device->SetRenderMode(RENDER_MODE_INTERFACE);
4110
4111 m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false);
4112 m_device->SetRenderState(RENDER_STATE_LIGHTING, false);
4113 m_device->SetRenderState(RENDER_STATE_FOG, false);
4114
4115 SetInterfaceCoordinates();
4116
4117 // Force new state to disable lighting
4118 m_interfaceMode = true;
4119 m_lastState = -1;
4120 SetState(Gfx::ENG_RSTATE_NORMAL);
4121
4122 // Draw the entire interface
4123 Ui::CInterface* interface = CRobotMain::GetInstancePointer()->GetInterface();
4124 if (interface != nullptr && m_renderInterface)
4125 {
4126 interface->Draw();
4127 }
4128
4129 m_interfaceMode = false;
4130 m_lastState = -1;
4131 SetState(Gfx::ENG_RSTATE_NORMAL);
4132
4133 if (!m_screenshotMode && m_renderInterface)
4134 {
4135 CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE_IFACE);
4136 m_particle->DrawParticle(SH_INTERFACE); // draws the particles of the interface
4137 CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE_IFACE);
4138 }
4139
4140 // 3D objects drawn in front of interface
4141 if (m_drawFront)
4142 {
4143 m_device->SetRenderMode(RENDER_MODE_NORMAL);
4144
4145 // Display the objects
4146 m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true);
4147
4148 m_device->SetTransform(TRANSFORM_PROJECTION, m_matProj);
4149
4150 m_device->SetGlobalAmbient(m_ambientColor[m_rankView]);
4151 m_device->SetRenderState(RENDER_STATE_LIGHTING, true);
4152
4153 m_device->SetRenderState(RENDER_STATE_FOG, true);
4154
4155 float fogStart = m_deepView[m_rankView] * m_fogStart[m_rankView] * m_clippingDistance;
4156 float fogEnd = m_deepView[m_rankView] * m_clippingDistance;
4157 m_device->SetFogParams(FOG_LINEAR, m_fogColor[m_rankView], fogStart, fogEnd, 1.0f);
4158
4159 m_device->SetTransform(TRANSFORM_VIEW, m_matView);
4160
4161 for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
4162 {
4163 if (! m_objects[objRank].used)
4164 continue;
4165
4166 if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN)
4167 continue;
4168
4169 if (! m_objects[objRank].drawFront)
4170 continue;
4171
4172 m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform);
4173
4174 if (! IsVisible(objRank))
4175 continue;
4176
4177 int baseObjRank = m_objects[objRank].baseObjRank;
4178 if (baseObjRank == -1)
4179 continue;
4180
4181 assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
4182
4183 EngineBaseObject& p1 = m_baseObjects[baseObjRank];
4184 if (! p1.used)
4185 continue;
4186
4187 m_lightMan->UpdateDeviceLights(m_objects[objRank].type);
4188
4189 for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
4190 {
4191 EngineBaseObjTexTier& p2 = p1.next[l2];
4192
4193 SetTexture(p2.tex1, 0);
4194 SetTexture(p2.tex2, 1);
4195
4196 for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
4197 {
4198 EngineBaseObjDataTier& p3 = p2.next[l3];
4199
4200 SetMaterial(p3.material);
4201 SetState(p3.state);
4202
4203 DrawObject(p3);
4204 }
4205 }
4206 }
4207
4208 m_particle->DrawParticle(SH_FRONT); // draws the particles of the 3D world
4209
4210 m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false);
4211 m_device->SetRenderState(RENDER_STATE_LIGHTING, false);
4212 m_device->SetRenderState(RENDER_STATE_FOG, false);
4213
4214 m_device->SetRenderMode(RENDER_MODE_INTERFACE);
4215
4216 SetInterfaceCoordinates();
4217 }
4218
4219 // Draw foreground color
4220 if (m_overFront)
4221 DrawOverColor();
4222
4223 // At the end to not overlap
4224 if (m_renderInterface)
4225 DrawHighlight();
4226
4227 DrawTimer();
4228 DrawStats();
4229
4230 if (m_renderInterface)
4231 DrawMouse();
4232
4233 m_device->SetRenderMode(RENDER_MODE_NORMAL);
4234 }
4235
UpdateGroundSpotTextures()4236 void CEngine::UpdateGroundSpotTextures()
4237 {
4238 if (!m_firstGroundSpot &&
4239 m_groundMark.drawPos.x == m_groundMark.pos.x &&
4240 m_groundMark.drawPos.z == m_groundMark.pos.z &&
4241 m_groundMark.drawRadius == m_groundMark.radius &&
4242 m_groundMark.drawIntensity == m_groundMark.intensity)
4243 return;
4244
4245 for (int s = 0; s < 16; s++)
4246 {
4247 Math::Point min, max;
4248 min.x = (s%4) * 254.0f - 1.0f; // 1 pixel cover
4249 min.y = (s/4) * 254.0f - 1.0f;
4250 max.x = min.x + 254.0f + 2.0f;
4251 max.y = min.y + 254.0f + 2.0f;
4252
4253 bool clear = false;
4254 bool set = false;
4255
4256 // Calculate the area to be erased.
4257 int dot = static_cast<int>(m_groundMark.drawRadius/2.0f);
4258
4259 float tu, tv;
4260 float cx, cy;
4261
4262 tu = (m_groundMark.drawPos.x+1600.0f)/3200.0f;
4263 tv = (m_groundMark.drawPos.z+1600.0f)/3200.0f; // 0..1
4264
4265 cx = (tu*254.0f*4.0f)-0.5f;
4266 cy = (tv*254.0f*4.0f)-0.5f;
4267
4268 if (dot == 0)
4269 {
4270 cx += 0.5f;
4271 cy += 0.5f;
4272 }
4273
4274 float px = cx-Math::Mod(cx, 1.0f);
4275 float py = cy-Math::Mod(cy, 1.0f); // multiple of 1
4276
4277 if (m_firstGroundSpot ||
4278 (m_groundMark.drawRadius != 0.0f &&
4279 px+dot >= min.x && py+dot >= min.y &&
4280 px-dot <= max.x && py-dot <= max.y))
4281 {
4282 clear = true;
4283 }
4284
4285 // Calculate the area to draw.
4286 dot = static_cast<int>(m_groundMark.radius/2.0f);
4287
4288 tu = (m_groundMark.pos.x+1600.0f)/3200.0f;
4289 tv = (m_groundMark.pos.z+1600.0f)/3200.0f; // 0..1
4290
4291 cx = (tu*254.0f*4.0f)-0.5f;
4292 cy = (tv*254.0f*4.0f)-0.5f;
4293
4294 if ( dot == 0 )
4295 {
4296 cx += 0.5f;
4297 cy += 0.5f;
4298 }
4299
4300 px = cx - Math::Mod(cx, 1.0f);
4301 py = cy - Math::Mod(cy, 1.0f); // multiple of 1
4302
4303 if (m_groundMark.draw &&
4304 px+dot >= min.x && py+dot >= min.y &&
4305 px-dot <= max.x && py-dot <= max.y)
4306 {
4307 set = true;
4308 }
4309
4310 if (clear || set || m_debugResources || m_displayGotoImage != nullptr)
4311 {
4312 CImage shadowImg(Math::IntPoint(256, 256));
4313 shadowImg.Fill(Gfx::IntColor(255, 255, 255, 255));
4314
4315 // Draw the new shadows.
4316 for (int i = 0; i < static_cast<int>( m_groundSpots.size() ); i++)
4317 {
4318 if (m_groundSpots[i].used == false ||
4319 m_groundSpots[i].radius == 0.0f)
4320 continue;
4321
4322 if (m_groundSpots[i].min == 0.0f &&
4323 m_groundSpots[i].max == 0.0f)
4324 {
4325 dot = static_cast<int>(m_groundSpots[i].radius/2.0f);
4326
4327 tu = (m_groundSpots[i].pos.x+1600.0f)/3200.0f;
4328 tv = (m_groundSpots[i].pos.z+1600.0f)/3200.0f; // 0..1
4329
4330 cx = (tu*254.0f*4.0f) - 0.5f;
4331 cy = (tv*254.0f*4.0f) - 0.5f;
4332
4333 if (dot == 0)
4334 {
4335 cx += 0.5f;
4336 cy += 0.5f;
4337 }
4338
4339 px = cx-Math::Mod(cx, 1.0f);
4340 py = cy-Math::Mod(cy, 1.0f); // multiple of 1
4341
4342 if (px+dot < min.x || py+dot < min.y ||
4343 px-dot > max.x || py-dot > max.y)
4344 continue;
4345
4346 for (int iy = -dot; iy <= dot; iy++)
4347 {
4348 for (int ix =- dot; ix <= dot; ix++)
4349 {
4350 float ppx = px+ix;
4351 float ppy = py+iy;
4352
4353 if (ppx < min.x || ppy < min.y ||
4354 ppx >= max.x || ppy >= max.y)
4355 continue;
4356
4357 float intensity;
4358 if (dot == 0)
4359 intensity = 0.0f;
4360 else
4361 intensity = Math::Point(ppx-cx, ppy-cy).Length()/dot;
4362
4363 ppx -= min.x; // on the texture
4364 ppy -= min.y;
4365 Math::IntPoint pp(ppx, ppy);
4366
4367 Gfx::Color color = shadowImg.GetPixel(pp);
4368 color.r *= Math::Norm(m_groundSpots[i].color.r+intensity);
4369 color.g *= Math::Norm(m_groundSpots[i].color.g+intensity);
4370 color.b *= Math::Norm(m_groundSpots[i].color.b+intensity);
4371 shadowImg.SetPixel(pp, color);
4372 }
4373 }
4374 }
4375 else
4376 {
4377 for (int iy = 0; iy < 256; iy++)
4378 {
4379 for (int ix = 0; ix < 256; ix++)
4380 {
4381 Math::Vector pos;
4382 pos.x = (256.0f * (s%4) + ix) * 3200.0f/1024.0f - 1600.0f;
4383 pos.z = (256.0f * (s/4) + iy) * 3200.0f/1024.0f - 1600.0f;
4384 pos.y = 0.0f;
4385
4386 float level = m_terrain->GetFloorLevel(pos, true);
4387 if (level < m_groundSpots[i].min ||
4388 level > m_groundSpots[i].max)
4389 continue;
4390
4391 float intensity;
4392 if (level > (m_groundSpots[i].max+m_groundSpots[i].min)/2.0f)
4393 intensity = 1.0f - (m_groundSpots[i].max-level) / m_groundSpots[i].smooth;
4394 else
4395 intensity = 1.0f - (level-m_groundSpots[i].min) / m_groundSpots[i].smooth;
4396
4397 if (intensity < 0.0f) intensity = 0.0f;
4398
4399 Math::IntPoint pp(ix, iy);
4400
4401 Gfx::Color color = shadowImg.GetPixel(pp);
4402 color.r *= Math::Norm(m_groundSpots[i].color.r+intensity);
4403 color.g *= Math::Norm(m_groundSpots[i].color.g+intensity);
4404 color.b *= Math::Norm(m_groundSpots[i].color.b+intensity);
4405 shadowImg.SetPixel(pp, color);
4406 }
4407 }
4408 }
4409 }
4410
4411 if (set)
4412 {
4413 dot = static_cast<int>(m_groundMark.radius/2.0f);
4414
4415 tu = (m_groundMark.pos.x + 1600.0f) / 3200.0f;
4416 tv = (m_groundMark.pos.z + 1600.0f) / 3200.0f; // 0..1
4417
4418 cx = (tu*254.0f*4.0f)-0.5f;
4419 cy = (tv*254.0f*4.0f)-0.5f;
4420
4421 if (dot == 0)
4422 {
4423 cx += 0.5f;
4424 cy += 0.5f;
4425 }
4426
4427 px = cx-Math::Mod(cx, 1.0f);
4428 py = cy-Math::Mod(cy, 1.0f); // multiple of 1
4429
4430 for (int iy = -dot; iy <= dot; iy++)
4431 {
4432 for (int ix = -dot; ix <= dot; ix++)
4433 {
4434 float ppx = px+ix;
4435 float ppy = py+iy;
4436
4437 if (ppx < min.x || ppy < min.y ||
4438 ppx >= max.x || ppy >= max.y)
4439 continue;
4440
4441 ppx -= min.x; // on the texture
4442 ppy -= min.y;
4443
4444 float intensity = 1.0f - Math::Point(ix, iy).Length() / dot;
4445 if (intensity <= 0.0f)
4446 continue;
4447
4448 intensity *= m_groundMark.intensity;
4449
4450 int j = (ix+dot) + (iy+dot) * m_groundMark.dx;
4451 if (m_groundMark.table[j] == 1) // green ?
4452 {
4453 Math::IntPoint pp(ppx, ppy);
4454 Gfx::Color color = shadowImg.GetPixel(pp);
4455 color.r *= Math::Norm(1.0f-intensity);
4456 color.g *= 1.0f;
4457 color.b *= Math::Norm(1.0f-intensity);
4458 shadowImg.SetPixel(pp, color);
4459 }
4460 if (m_groundMark.table[j] == 2) // red ?
4461 {
4462 Math::IntPoint pp(ppx, ppy);
4463 Gfx::Color color = shadowImg.GetPixel(pp);
4464 color.r *= 1.0f;
4465 color.g *= Math::Norm(1.0f-intensity);
4466 color.b *= Math::Norm(1.0f-intensity);
4467 shadowImg.SetPixel(pp, color);
4468 }
4469 }
4470 }
4471 }
4472
4473 if (m_debugResources)
4474 {
4475 for (float x = min.x; x < max.x; x += 1.0f)
4476 {
4477 for (float y = min.y; y < max.y; y += 1.0f)
4478 {
4479 Math::Vector pos(
4480 x / 4.0f / 254.0f * 3200.0f - 1600.0f,
4481 0.0f,
4482 y / 4.0f / 254.0f * 3200.0f - 1600.0f
4483 );
4484 TerrainRes res = m_terrain->GetResource(pos);
4485 Math::IntPoint p(x-min.x, y-min.y);
4486 if (res == TR_NULL)
4487 {
4488 shadowImg.SetPixel(p, Gfx::Color(0.5f, 0.5f, 0.5f));
4489 continue;
4490 }
4491 shadowImg.SetPixelInt(p, ResourceToColor(res));
4492 }
4493 }
4494 }
4495
4496 if (m_displayGotoImage != nullptr)
4497 {
4498 Math::IntPoint size = m_displayGotoImage->GetSize();
4499 for (float x = min.x; x < max.x; x += 1.0f)
4500 {
4501 for (float y = min.y; y < max.y; y += 1.0f)
4502 {
4503 int px = x / 4.0f / 254.0f * size.x;
4504 int py = y / 4.0f / 254.0f * size.y;
4505 // This can happen because the shadow??.png textures have a 1 pixel margin around them
4506 if (px < 0 || px >= size.x || py < 0 || py >= size.y)
4507 continue;
4508 shadowImg.SetPixelInt(Math::IntPoint(x-min.x, y-min.y), m_displayGotoImage->GetPixelInt(Math::IntPoint(px, py)));
4509 }
4510 }
4511 }
4512
4513 std::stringstream str;
4514 str << "textures/shadow" << std::setfill('0') << std::setw(2) << s << ".png";
4515 std::string texName = str.str();
4516
4517 CreateOrUpdateTexture(texName, &shadowImg);
4518 }
4519 }
4520
4521 for (int i = 0; i < static_cast<int>( m_groundSpots.size() ); i++)
4522 {
4523 if (m_groundSpots[i].used == false ||
4524 m_groundSpots[i].radius == 0.0f)
4525 {
4526 m_groundSpots[i].drawRadius = 0.0f;
4527 }
4528 else
4529 {
4530 m_groundSpots[i].drawPos = m_groundSpots[i].pos;
4531 m_groundSpots[i].drawRadius = m_groundSpots[i].radius;
4532 }
4533 }
4534
4535 m_groundMark.drawPos = m_groundMark.pos;
4536 m_groundMark.drawRadius = m_groundMark.radius;
4537 m_groundMark.drawIntensity = m_groundMark.intensity;
4538
4539 m_firstGroundSpot = false;
4540 }
4541
DrawShadowSpots()4542 void CEngine::DrawShadowSpots()
4543 {
4544 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
4545 m_device->SetRenderState(RENDER_STATE_LIGHTING, false);
4546
4547 Math::Matrix matrix;
4548 matrix.LoadIdentity();
4549 m_device->SetTransform(TRANSFORM_WORLD, matrix);
4550
4551
4552 Material material;
4553 material.diffuse = Color(1.0f, 1.0f, 1.0f);
4554 material.ambient = Color(0.5f, 0.5f, 0.5f);
4555 SetMaterial(material);
4556
4557 // TODO: create a separate texture
4558 SetTexture("textures/effect03.png");
4559
4560 Math::Point ts, ti;
4561
4562 float dp = 0.5f/256.0f;
4563 ts.y = 192.0f/256.0f;
4564 ti.y = 224.0f/256.0f;
4565 ts.y += dp;
4566 ti.y -= dp;
4567
4568 Math::Vector n(0.0f, 1.0f, 0.0f);
4569
4570 float startDeepView = m_deepView[m_rankView] * m_fogStart[m_rankView] * m_clippingDistance;
4571 float endDeepView = m_deepView[m_rankView] * m_clippingDistance;
4572
4573 float lastIntensity = -1.0f;
4574 for (int i = 0; i < static_cast<int>( m_shadowSpots.size() ); i++)
4575 {
4576 if (m_shadowSpots[i].hide || !m_shadowSpots[i].used)
4577 continue;
4578
4579 Math::Vector pos = m_shadowSpots[i].pos; // pos = center of the shadow on the ground
4580
4581 if (m_eyePt.y == pos.y)
4582 continue; // camera at the same level?
4583
4584 float d = 0.0f;
4585 float D = 0.0f;
4586
4587 // h is the height above the ground to which the shadow
4588 // will be drawn.
4589 if (m_eyePt.y > pos.y) // camera on?
4590 {
4591 float height = m_eyePt.y-pos.y;
4592 float h = m_shadowSpots[i].radius;
4593 float max = height*0.5f;
4594 if ( h > max ) h = max;
4595 if ( h > 4.0f ) h = 4.0f;
4596
4597 D = Math::Distance(m_eyePt, pos);
4598 if (D >= endDeepView)
4599 continue;
4600
4601 d = D*h/height;
4602
4603 pos.x += (m_eyePt.x-pos.x)*d/D;
4604 pos.z += (m_eyePt.z-pos.z)*d/D;
4605 pos.y += h;
4606 }
4607 else // camera underneath?
4608 {
4609 float height = pos.y-m_eyePt.y;
4610 float h = m_shadowSpots[i].radius;
4611 float max = height*0.1f;
4612 if ( h > max ) h = max;
4613 if ( h > 4.0f ) h = 4.0f;
4614
4615 D = Math::Distance(m_eyePt, pos);
4616 if (D >= endDeepView)
4617 continue;
4618
4619 d = D*h/height;
4620
4621 pos.x += (m_eyePt.x-pos.x)*d/D;
4622 pos.z += (m_eyePt.z-pos.z)*d/D;
4623 pos.y -= h;
4624 }
4625
4626 // The hFactor decreases the intensity and size increases more
4627 // the object is high relative to the ground.
4628 float hFactor = m_shadowSpots[i].height/20.0f;
4629 if ( hFactor < 0.0f ) hFactor = 0.0f;
4630 if ( hFactor > 1.0f ) hFactor = 1.0f;
4631 hFactor = powf(1.0f-hFactor, 2.0f);
4632 if ( hFactor < 0.2f ) hFactor = 0.2f;
4633
4634 float radius = m_shadowSpots[i].radius*1.5f;
4635 radius *= 2.0f-hFactor; // greater if high
4636 radius *= 1.0f-d/D; // smaller if close
4637
4638
4639 Math::Vector corner[4];
4640
4641 if (m_shadowSpots[i].type == ENG_SHADOW_NORM)
4642 {
4643 corner[0].x = +radius;
4644 corner[0].z = +radius;
4645 corner[0].y = 0.0f;
4646
4647 corner[1].x = -radius;
4648 corner[1].z = +radius;
4649 corner[1].y = 0.0f;
4650
4651 corner[2].x = +radius;
4652 corner[2].z = -radius;
4653 corner[2].y = 0.0f;
4654
4655 corner[3].x = -radius;
4656 corner[3].z = -radius;
4657 corner[3].y = 0.0f;
4658
4659 ts.x = 64.0f/256.0f;
4660 ti.x = 96.0f/256.0f;
4661 }
4662 else
4663 {
4664 Math::Point rot;
4665
4666 rot = Math::RotatePoint(-m_shadowSpots[i].angle, Math::Point(radius, radius));
4667 corner[0].x = rot.x;
4668 corner[0].z = rot.y;
4669 corner[0].y = 0.0f;
4670
4671 rot = Math::RotatePoint(-m_shadowSpots[i].angle, Math::Point(-radius, radius));
4672 corner[1].x = rot.x;
4673 corner[1].z = rot.y;
4674 corner[1].y = 0.0f;
4675
4676 rot = Math::RotatePoint(-m_shadowSpots[i].angle, Math::Point(radius, -radius));
4677 corner[2].x = rot.x;
4678 corner[2].z = rot.y;
4679 corner[2].y = 0.0f;
4680
4681 rot = Math::RotatePoint(-m_shadowSpots[i].angle, Math::Point(-radius, -radius));
4682 corner[3].x = rot.x;
4683 corner[3].z = rot.y;
4684 corner[3].y = 0.0f;
4685
4686 if (m_shadowSpots[i].type == ENG_SHADOW_WORM)
4687 {
4688 ts.x = 96.0f/256.0f;
4689 ti.x = 128.0f/256.0f;
4690 }
4691 else
4692 {
4693 ts.x = 64.0f/256.0f;
4694 ti.x = 96.0f/256.0f;
4695 }
4696 }
4697
4698 corner[0] = Math::CrossProduct(corner[0], m_shadowSpots[i].normal);
4699 corner[1] = Math::CrossProduct(corner[1], m_shadowSpots[i].normal);
4700 corner[2] = Math::CrossProduct(corner[2], m_shadowSpots[i].normal);
4701 corner[3] = Math::CrossProduct(corner[3], m_shadowSpots[i].normal);
4702
4703 corner[0] += pos;
4704 corner[1] += pos;
4705 corner[2] += pos;
4706 corner[3] += pos;
4707
4708 ts.x += dp;
4709 ti.x -= dp;
4710
4711 Vertex vertex[4] =
4712 {
4713 Vertex(corner[1], n, Math::Point(ts.x, ts.y)),
4714 Vertex(corner[0], n, Math::Point(ti.x, ts.y)),
4715 Vertex(corner[3], n, Math::Point(ts.x, ti.y)),
4716 Vertex(corner[2], n, Math::Point(ti.x, ti.y))
4717 };
4718
4719 float intensity = (0.5f+m_shadowSpots[i].intensity*0.5f)*hFactor;
4720
4721 // Decreases the intensity of the shade if you're in the area
4722 // between the beginning and the end of the fog.
4723 if ( D > startDeepView )
4724 intensity *= 1.0f-(D-startDeepView)/(endDeepView-startDeepView);
4725
4726 if (intensity == 0.0f)
4727 continue;
4728
4729 if (lastIntensity != intensity) // intensity changed?
4730 {
4731 lastIntensity = intensity;
4732 SetState(ENG_RSTATE_TTEXTURE_WHITE, Color(intensity, intensity, intensity, intensity));
4733 }
4734
4735 m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
4736 AddStatisticTriangle(2);
4737 }
4738
4739 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true);
4740 m_device->SetRenderState(RENDER_STATE_LIGHTING, true);
4741 }
4742
DrawBackground()4743 void CEngine::DrawBackground()
4744 {
4745 m_device->SetRenderMode(RENDER_MODE_INTERFACE);
4746
4747 if (m_cloud->GetLevel() != 0.0f) // clouds ?
4748 {
4749 if (m_backgroundCloudUp != m_backgroundCloudDown) // degraded?
4750 DrawBackgroundGradient(m_backgroundCloudUp, m_backgroundCloudDown);
4751 }
4752 else
4753 {
4754 if (m_backgroundColorUp != m_backgroundColorDown) // degraded?
4755 DrawBackgroundGradient(m_backgroundColorUp, m_backgroundColorDown);
4756 }
4757
4758 if (m_backForce || !m_backgroundName.empty() )
4759 {
4760 DrawBackgroundImage(); // image
4761 }
4762
4763 m_device->SetRenderMode(RENDER_MODE_NORMAL);
4764 }
4765
DrawBackgroundGradient(const Color & up,const Color & down)4766 void CEngine::DrawBackgroundGradient(const Color& up, const Color& down)
4767 {
4768 Math::Point p1(0.0f, 0.5f);
4769 Math::Point p2(1.0f, 1.0f);
4770
4771 Color color[3] =
4772 {
4773 up,
4774 down,
4775 Color(0.0f, 0.0f, 0.0f, 0.0f)
4776 };
4777
4778 SetState(ENG_RSTATE_OPAQUE_COLOR);
4779
4780 m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface);
4781 m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface);
4782 m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface);
4783
4784 VertexCol vertex[4] =
4785 {
4786 VertexCol(Math::Vector(p1.x, p1.y, 0.0f), color[1]),
4787 VertexCol(Math::Vector(p1.x, p2.y, 0.0f), color[0]),
4788 VertexCol(Math::Vector(p2.x, p1.y, 0.0f), color[1]),
4789 VertexCol(Math::Vector(p2.x, p2.y, 0.0f), color[0])
4790 };
4791
4792 m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
4793 AddStatisticTriangle(2);
4794 }
4795
DrawBackgroundImage()4796 void CEngine::DrawBackgroundImage()
4797 {
4798 Math::Point p1, p2;
4799 p1.x = 0.0f;
4800 p1.y = 0.0f;
4801 p2.x = 1.0f;
4802 p2.y = 1.0f;
4803
4804 Math::Vector n = Math::Vector(0.0f, 0.0f, -1.0f); // normal
4805
4806 float u1, u2, v1, v2;
4807 if (m_backgroundFull)
4808 {
4809 u1 = 0.0f;
4810 v1 = 0.0f;
4811 u2 = 1.0f;
4812 v2 = 1.0f;
4813 }
4814 else
4815 {
4816 float h = 0.5f; // visible area vertically (1=all)
4817 float a = m_eyeDirV-Math::PI*0.15f;
4818 if (a > Math::PI ) a -= Math::PI*2.0f; // a = -Math::PI..Math::PI
4819 if (a > Math::PI/4.0f) a = Math::PI/4.0f;
4820 if (a < -Math::PI/4.0f) a = -Math::PI/4.0f;
4821
4822 // Note the background covers Math::PI radians, i.e. it repeats twice per rotation!
4823 u1 = (-m_eyeDirH - GetHFovAngle()/2.0f) / Math::PI;
4824 u2 = u1 + (GetHFovAngle() / Math::PI);
4825
4826 v1 = (1.0f-h)*(0.5f+a/(2.0f*Math::PI/4.0f))+0.1f;
4827 v2 = v1+h;
4828 }
4829
4830 Math::Point backgroundScale;
4831 backgroundScale.x = static_cast<float>(m_backgroundTex.originalSize.x) / static_cast<float>(m_backgroundTex.size.x);
4832 backgroundScale.y = static_cast<float>(m_backgroundTex.originalSize.y) / static_cast<float>(m_backgroundTex.size.y);
4833
4834 u2 *= backgroundScale.x;
4835 v2 *= backgroundScale.y;
4836
4837 if (m_backgroundScale)
4838 {
4839 Math::Point scale;
4840 scale.x = static_cast<float>(m_size.x) / static_cast<float>(m_backgroundTex.originalSize.x);
4841 scale.y = static_cast<float>(m_size.y) / static_cast<float>(m_backgroundTex.originalSize.y);
4842 if (scale.x > scale.y)
4843 {
4844 scale.y /= scale.x;
4845 scale.x = 1;
4846 }
4847 else
4848 {
4849 scale.x /= scale.y;
4850 scale.y = 1;
4851 }
4852 float margin_u = (1-scale.x)/2;
4853 float margin_v = (1-scale.y)/2;
4854 margin_u *= backgroundScale.x;
4855 margin_v *= backgroundScale.y;
4856 u1 += margin_u;
4857 v1 += margin_v;
4858 u2 -= margin_u;
4859 v2 -= margin_v;
4860 }
4861
4862 SetTexture(m_backgroundTex);
4863 SetState(ENG_RSTATE_OPAQUE_TEXTURE | ENG_RSTATE_WRAP);
4864
4865 m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface);
4866 m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface);
4867 m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface);
4868
4869 Vertex vertex[4] =
4870 {
4871 Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(u1, v2)),
4872 Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(u1, v1)),
4873 Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(u2, v2)),
4874 Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(u2, v1))
4875 };
4876
4877 m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
4878 AddStatisticTriangle(2);
4879 }
4880
DrawPlanet()4881 void CEngine::DrawPlanet()
4882 {
4883 if (! m_planet->PlanetExist())
4884 return;
4885
4886 m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false);
4887 m_device->SetRenderState(RENDER_STATE_LIGHTING, false);
4888 m_device->SetRenderState(RENDER_STATE_FOG, false);
4889
4890 m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface);
4891 m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface);
4892 m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface);
4893
4894 m_planet->Draw(); // draws the planets
4895 }
4896
DrawForegroundImage()4897 void CEngine::DrawForegroundImage()
4898 {
4899 if (m_foregroundName.empty())
4900 return;
4901
4902 Math::Vector n = Math::Vector(0.0f, 0.0f, -1.0f); // normal
4903
4904 Math::Point p1(0.0f, 0.0f);
4905 Math::Point p2(1.0f, 1.0f);
4906
4907 float u1 = -m_eyeDirH/(Math::PI*0.6f)+Math::PI*0.5f;
4908 float u2 = u1+0.50f;
4909
4910 float v1 = 0.2f;
4911 float v2 = 1.0f;
4912
4913
4914 Vertex vertex[4] =
4915 {
4916 Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(u1, v2)),
4917 Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(u1, v1)),
4918 Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(u2, v2)),
4919 Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(u2, v1))
4920 };
4921
4922 SetTexture(m_foregroundTex);
4923 SetState(ENG_RSTATE_CLAMP | ENG_RSTATE_TTEXTURE_BLACK);
4924
4925 m_device->SetRenderMode(RENDER_MODE_INTERFACE);
4926
4927 m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface);
4928 m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface);
4929 m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface);
4930
4931 m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
4932 AddStatisticTriangle(2);
4933
4934 m_device->SetRenderMode(RENDER_MODE_NORMAL);
4935 }
4936
DrawOverColor()4937 void CEngine::DrawOverColor()
4938 {
4939 if ((m_overColor == Color(0.0f, 0.0f, 0.0f, 0.0f) && m_overMode == ENG_RSTATE_TCOLOR_BLACK) ||
4940 (m_overColor == Color(1.0f, 1.0f, 1.0f, 1.0f) && m_overMode == ENG_RSTATE_TCOLOR_WHITE))
4941 return;
4942
4943 Math::Point p1(0.0f, 0.0f);
4944 Math::Point p2(1.0f, 1.0f);
4945
4946 Color color[3] =
4947 {
4948 m_overColor,
4949 m_overColor,
4950 Color(0.0f, 0.0f, 0.0f, 0.0f)
4951 };
4952
4953 m_device->SetRenderMode(RENDER_MODE_INTERFACE);
4954
4955 SetState(m_overMode);
4956
4957 m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface);
4958 m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface);
4959 m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface);
4960 m_device->SetRenderState(RENDER_STATE_LIGHTING, false);
4961
4962 VertexCol vertex[4] =
4963 {
4964 VertexCol(Math::Vector(p1.x, p1.y, 0.0f), color[1]),
4965 VertexCol(Math::Vector(p1.x, p2.y, 0.0f), color[0]),
4966 VertexCol(Math::Vector(p2.x, p1.y, 0.0f), color[1]),
4967 VertexCol(Math::Vector(p2.x, p2.y, 0.0f), color[0])
4968 };
4969
4970 m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
4971 AddStatisticTriangle(2);
4972
4973 m_device->SetRenderMode(RENDER_MODE_NORMAL);
4974 }
4975
DrawHighlight()4976 void CEngine::DrawHighlight()
4977 {
4978 Math::Point min, max;
4979 min.x = 1000000.0f;
4980 min.y = 1000000.0f;
4981 max.x = -1000000.0f;
4982 max.y = -1000000.0f;
4983
4984 int i = 0;
4985 while (m_highlightRank[i] != -1)
4986 {
4987 Math::Point omin, omax;
4988 if (GetBBox2D(m_highlightRank[i++], omin, omax))
4989 {
4990 min.x = Math::Min(min.x, omin.x);
4991 min.y = Math::Min(min.y, omin.y);
4992 max.x = Math::Max(max.x, omax.x);
4993 max.y = Math::Max(max.y, omax.y);
4994 }
4995 }
4996
4997 if (min.x == 1000000.0f ||
4998 min.y == 1000000.0f ||
4999 max.x == -1000000.0f ||
5000 max.y == -1000000.0f)
5001 {
5002 m_highlight = false; // not highlighted
5003 }
5004 else
5005 {
5006 m_highlightP1 = min;
5007 m_highlightP2 = max;
5008 m_highlight = true;
5009 }
5010
5011 if (!m_highlight)
5012 return;
5013
5014 Math::Point p1 = m_highlightP1;
5015 Math::Point p2 = m_highlightP2;
5016
5017 int nbOut = 0;
5018 if (p1.x < 0.0f || p1.x > 1.0f) nbOut++;
5019 if (p1.y < 0.0f || p1.y > 1.0f) nbOut++;
5020 if (p2.x < 0.0f || p2.x > 1.0f) nbOut++;
5021 if (p2.y < 0.0f || p2.y > 1.0f) nbOut++;
5022 if (nbOut > 2)
5023 return;
5024
5025 SetState(ENG_RSTATE_OPAQUE_COLOR);
5026
5027 float d = 0.5f + sinf(m_highlightTime * 6.0f) * 0.5f;
5028 d *= (p2.x - p1.x) * 0.1f;
5029 p1.x += d;
5030 p1.y += d;
5031 p2.x -= d;
5032 p2.y -= d;
5033
5034 Color color(1.0f, 1.0f, 0.0f); // yellow
5035
5036 VertexCol line[3] =
5037 {
5038 VertexCol(Math::Vector(), color),
5039 VertexCol(Math::Vector(), color),
5040 VertexCol(Math::Vector(), color)
5041 };
5042
5043 float dx = (p2.x - p1.x) / 5.0f;
5044 float dy = (p2.y - p1.y) / 5.0f;
5045
5046 line[0].coord = Math::Vector(p1.x, p1.y + dy, 0.0f);
5047 line[1].coord = Math::Vector(p1.x, p1.y, 0.0f);
5048 line[2].coord = Math::Vector(p1.x + dx, p1.y, 0.0f);
5049 m_device->DrawPrimitive(PRIMITIVE_LINE_STRIP, line, 3);
5050
5051 line[0].coord = Math::Vector(p2.x - dx, p1.y, 0.0f);
5052 line[1].coord = Math::Vector(p2.x, p1.y, 0.0f);
5053 line[2].coord = Math::Vector(p2.x, p1.y + dy, 0.0f);
5054 m_device->DrawPrimitive(PRIMITIVE_LINE_STRIP, line, 3);
5055
5056 line[0].coord = Math::Vector(p2.x, p2.y - dy, 0.0f);
5057 line[1].coord = Math::Vector(p2.x, p2.y, 0.0f);
5058 line[2].coord = Math::Vector(p2.x - dx, p2.y, 0.0f);
5059 m_device->DrawPrimitive(PRIMITIVE_LINE_STRIP, line, 3);
5060
5061 line[0].coord = Math::Vector(p1.x + dx, p2.y, 0.0f);
5062 line[1].coord = Math::Vector(p1.x, p2.y, 0.0f);
5063 line[2].coord = Math::Vector(p1.x, p2.y - dy, 0.0f);
5064 m_device->DrawPrimitive(PRIMITIVE_LINE_STRIP, line, 3);
5065 }
5066
DrawMouse()5067 void CEngine::DrawMouse()
5068 {
5069 MouseMode mode = m_app->GetMouseMode();
5070 if (mode != MOUSE_ENGINE && mode != MOUSE_BOTH)
5071 return;
5072
5073 SetWindowCoordinates();
5074
5075 Material material;
5076 material.diffuse = Color(1.0f, 1.0f, 1.0f);
5077 material.ambient = Color(0.5f, 0.5f, 0.5f);
5078
5079 m_device->SetMaterial(material);
5080 m_device->SetTexture(0, m_miceTexture);
5081
5082 Math::Point mousePos = CInput::GetInstancePointer()->GetMousePos();
5083 Math::IntPoint pos(mousePos.x * m_size.x, m_size.y - mousePos.y * m_size.y);
5084 pos.x -= MOUSE_TYPES.at(m_mouseType).hotPoint.x;
5085 pos.y -= MOUSE_TYPES.at(m_mouseType).hotPoint.y;
5086
5087 Math::IntPoint shadowPos;
5088 shadowPos.x = pos.x + 4;
5089 shadowPos.y = pos.y - 3;
5090
5091 SetState(ENG_RSTATE_TCOLOR_WHITE);
5092 DrawMouseSprite(shadowPos, MOUSE_SIZE, MOUSE_TYPES.at(m_mouseType).iconShadow);
5093
5094 SetState(MOUSE_TYPES.at(m_mouseType).mode1);
5095 DrawMouseSprite(pos, MOUSE_SIZE, MOUSE_TYPES.at(m_mouseType).icon1);
5096
5097 SetState(MOUSE_TYPES.at(m_mouseType).mode2);
5098 DrawMouseSprite(pos, MOUSE_SIZE, MOUSE_TYPES.at(m_mouseType).icon2);
5099
5100 SetInterfaceCoordinates();
5101 }
5102
DrawMouseSprite(Math::IntPoint pos,Math::IntPoint size,int icon)5103 void CEngine::DrawMouseSprite(Math::IntPoint pos, Math::IntPoint size, int icon)
5104 {
5105 if (icon == -1)
5106 return;
5107
5108 Math::IntPoint p1 = pos;
5109 Math::IntPoint p2 = p1 + size;
5110
5111 float u1 = (32.0f / 256.0f) * (icon % 8);
5112 float v1 = (32.0f / 256.0f) * (icon / 8);
5113 float u2 = u1 + (32.0f / 256.0f);
5114 float v2 = v1 + (32.0f / 256.0f);
5115
5116 float dp = 0.5f / 256.0f;
5117 u1 += dp;
5118 v1 += dp;
5119 u2 -= dp;
5120 v2 -= dp;
5121
5122 Math::Vector normal(0.0f, 0.0f, -1.0f);
5123
5124 Vertex vertex[4] =
5125 {
5126 Vertex(Math::Vector(p1.x, p2.y, 0.0f), normal, Math::Point(u1, v2)),
5127 Vertex(Math::Vector(p1.x, p1.y, 0.0f), normal, Math::Point(u1, v1)),
5128 Vertex(Math::Vector(p2.x, p2.y, 0.0f), normal, Math::Point(u2, v2)),
5129 Vertex(Math::Vector(p2.x, p1.y, 0.0f), normal, Math::Point(u2, v1))
5130 };
5131
5132 m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
5133 AddStatisticTriangle(2);
5134 }
5135
DrawStats()5136 void CEngine::DrawStats()
5137 {
5138 if (!m_showStats)
5139 return;
5140
5141 float height = m_text->GetAscent(FONT_COMMON, 13.0f);
5142 float width = 0.4f;
5143 const int TOTAL_LINES = 22;
5144
5145 Math::Point pos(0.05f * m_size.x/m_size.y, 0.05f + TOTAL_LINES * height);
5146
5147 SetState(ENG_RSTATE_TCOLOR_ALPHA);
5148
5149 Gfx::Color black(0.0f, 0.0f, 0.0f, 0.75f);
5150
5151 Math::Point margin = Math::Point(5.f / m_size.x, 5.f / m_size.y);
5152
5153 VertexCol vertex[4] =
5154 {
5155 VertexCol(Math::Vector(pos.x - margin.x, pos.y - (TOTAL_LINES + 1) * height - margin.y, 0.0f), black),
5156 VertexCol(Math::Vector(pos.x - margin.x, pos.y + height + margin.y, 0.0f), black),
5157 VertexCol(Math::Vector(pos.x + width + margin.x, pos.y - (TOTAL_LINES + 1) * height - margin.y, 0.0f), black),
5158 VertexCol(Math::Vector(pos.x + width + margin.x, pos.y + height + margin.y, 0.0f), black)
5159 };
5160
5161 m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
5162
5163 SetState(ENG_RSTATE_TEXT);
5164
5165 auto drawStatsLine = [&](const std::string& name, const std::string& value, const std::string& value2)
5166 {
5167 if (!name.empty())
5168 m_text->DrawText(name+":", FONT_COMMON, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
5169 pos.x += 0.25f;
5170 if (!value.empty())
5171 m_text->DrawText(value, FONT_COMMON, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
5172 pos.x += 0.15f;
5173 if (!value2.empty())
5174 m_text->DrawText(value2, FONT_COMMON, 12.0f, pos, 1.0f, TEXT_ALIGN_RIGHT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
5175 pos.x -= 0.4f;
5176 pos.y -= height;
5177 };
5178
5179 auto drawStatsValue = [&](const std::string& name, long long time)
5180 {
5181 float value = static_cast<float>(time)/CProfiler::GetPerformanceCounterTime(PCNT_ALL);
5182 drawStatsLine(name, StrUtils::Format("%.2f", value), StrUtils::Format("%.2f ms", time/1e6f));
5183 };
5184
5185 auto drawStatsCounter = [&](const std::string& name, PerformanceCounter counter)
5186 {
5187 drawStatsValue(name, CProfiler::GetPerformanceCounterTime(counter));
5188 };
5189
5190 // TODO: Find a more generic way to calculate these in CProfiler
5191
5192 long long engineUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ENGINE) -
5193 CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_PARTICLE);
5194
5195 long long gameUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_GAME) -
5196 CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_CBOT);
5197
5198 long long otherUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ALL) -
5199 CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ENGINE) -
5200 CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_GAME);
5201
5202 long long otherRender = CProfiler::GetPerformanceCounterTime(PCNT_RENDER_ALL) -
5203 CProfiler::GetPerformanceCounterTime(PCNT_RENDER_PARTICLE_WORLD) -
5204 CProfiler::GetPerformanceCounterTime(PCNT_RENDER_WATER) -
5205 CProfiler::GetPerformanceCounterTime(PCNT_RENDER_TERRAIN) -
5206 CProfiler::GetPerformanceCounterTime(PCNT_RENDER_OBJECTS) -
5207 CProfiler::GetPerformanceCounterTime(PCNT_RENDER_INTERFACE) -
5208 CProfiler::GetPerformanceCounterTime(PCNT_RENDER_SHADOW_MAP);
5209
5210 drawStatsCounter("Event processing", PCNT_EVENT_PROCESSING);
5211 drawStatsLine( "", "", "");
5212 drawStatsCounter("Frame update", PCNT_UPDATE_ALL);
5213 drawStatsValue (" Engine update", engineUpdate);
5214 drawStatsCounter(" Particle update", PCNT_UPDATE_PARTICLE);
5215 drawStatsValue (" Game update", gameUpdate);
5216 drawStatsCounter(" CBot programs", PCNT_UPDATE_CBOT);
5217 drawStatsValue( " Other update", otherUpdate);
5218 drawStatsLine( "", "", "");
5219 drawStatsCounter("Frame render", PCNT_RENDER_ALL);
5220 drawStatsCounter(" Particle render", PCNT_RENDER_PARTICLE_WORLD);
5221 drawStatsCounter(" Water render", PCNT_RENDER_WATER);
5222 drawStatsCounter(" Terrain render", PCNT_RENDER_TERRAIN);
5223 drawStatsCounter(" Objects render", PCNT_RENDER_OBJECTS);
5224 drawStatsCounter(" UI render", PCNT_RENDER_INTERFACE);
5225 drawStatsCounter(" particles", PCNT_RENDER_PARTICLE_IFACE);
5226 drawStatsCounter(" Shadow map render", PCNT_RENDER_SHADOW_MAP);
5227 drawStatsValue( " Other render", otherRender);
5228 drawStatsCounter("Swap buffers & VSync", PCNT_SWAP_BUFFERS);
5229 drawStatsLine( "", "", "");
5230 drawStatsLine( "Triangles", StrUtils::ToString<int>(m_statisticTriangle), "");
5231 drawStatsLine( "FPS", StrUtils::Format("%.3f", m_fps), "");
5232 drawStatsLine( "", "", "");
5233 std::stringstream str;
5234 str << std::fixed << std::setprecision(2) << m_statisticPos.x << "; " << m_statisticPos.z;
5235 drawStatsLine( "Position", str.str(), "");
5236 }
5237
DrawTimer()5238 void CEngine::DrawTimer()
5239 {
5240 SetState(ENG_RSTATE_TEXT);
5241
5242 Math::Point pos(0.98f, 0.98f-m_text->GetAscent(FONT_COMMON, 15.0f));
5243 m_text->DrawText(m_timerText, FONT_COMMON, 15.0f, pos, 1.0f, TEXT_ALIGN_RIGHT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
5244 }
5245
AddBaseObjTriangles(int baseObjRank,const std::vector<Gfx::ModelTriangle> & triangles)5246 void CEngine::AddBaseObjTriangles(int baseObjRank, const std::vector<Gfx::ModelTriangle>& triangles)
5247 {
5248 std::vector<VertexTex2> vs(3, VertexTex2());
5249
5250 for (const auto& triangle : triangles)
5251 {
5252 vs[0] = triangle.p1;
5253 vs[1] = triangle.p2;
5254 vs[2] = triangle.p3;
5255
5256 Material material;
5257 material.ambient = triangle.ambient;
5258 material.diffuse = triangle.diffuse;
5259 material.specular = triangle.specular;
5260
5261 int state = GetEngineState(triangle);
5262
5263 std::string tex1Name;
5264 if (!triangle.tex1Name.empty())
5265 tex1Name = "objects/" + triangle.tex1Name;
5266
5267 std::string tex2Name;
5268 if (triangle.variableTex2)
5269 tex2Name = GetSecondTexture();
5270 else
5271 tex2Name = triangle.tex2Name;
5272
5273 AddBaseObjTriangles(baseObjRank, vs, material, state, tex1Name, tex2Name);
5274 }
5275 }
5276
GetEngineState(const ModelTriangle & triangle)5277 int CEngine::GetEngineState(const ModelTriangle& triangle)
5278 {
5279 int state = 0;
5280
5281 if (!triangle.tex2Name.empty() || triangle.variableTex2)
5282 state |= ENG_RSTATE_DUAL_BLACK;
5283
5284 switch (triangle.transparentMode)
5285 {
5286 case ModelTransparentMode::None:
5287 break;
5288
5289 case ModelTransparentMode::AlphaChannel:
5290 state |= ENG_RSTATE_ALPHA;
5291 break;
5292
5293 case ModelTransparentMode::MapBlackToAlpha:
5294 state |= ENG_RSTATE_TTEXTURE_BLACK;
5295 break;
5296
5297 case ModelTransparentMode::MapWhiteToAlpha:
5298 state |= ENG_RSTATE_TTEXTURE_WHITE;
5299 break;
5300 }
5301
5302 switch (triangle.specialMark)
5303 {
5304 case ModelSpecialMark::None:
5305 break;
5306
5307 case ModelSpecialMark::Part1:
5308 state |= ENG_RSTATE_PART1;
5309 break;
5310
5311 case ModelSpecialMark::Part2:
5312 state |= ENG_RSTATE_PART2;
5313 break;
5314
5315 case ModelSpecialMark::Part3:
5316 state |= ENG_RSTATE_PART3;
5317 break;
5318 }
5319
5320 if (triangle.doubleSided)
5321 state |= ENG_RSTATE_2FACE;
5322
5323 return state;
5324 }
5325
UpdateObjectShadowSpotNormal(int objRank)5326 void CEngine::UpdateObjectShadowSpotNormal(int objRank)
5327 {
5328 assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
5329
5330 int shadowRank = m_objects[objRank].shadowRank;
5331 if (shadowRank == -1)
5332 return;
5333
5334 assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
5335
5336 // Calculating the normal to the ground in nine strategic locations,
5337 // then perform a weighted average (the dots in the center are more important).
5338
5339 Math::Vector pos = m_shadowSpots[shadowRank].pos;
5340 float radius = m_shadowSpots[shadowRank].radius;
5341
5342 Math::Vector n[20];
5343 Math::Vector norm;
5344 int i = 0;
5345
5346 m_terrain->GetNormal(norm, pos);
5347 n[i++] = norm;
5348 n[i++] = norm;
5349 n[i++] = norm;
5350
5351 Math::Vector shPos = pos;
5352 shPos.x += radius*0.6f;
5353 shPos.z += radius*0.6f;
5354 m_terrain->GetNormal(norm, shPos);
5355 n[i++] = norm;
5356 n[i++] = norm;
5357
5358 shPos = pos;
5359 shPos.x -= radius*0.6f;
5360 shPos.z += radius*0.6f;
5361 m_terrain->GetNormal(norm, shPos);
5362 n[i++] = norm;
5363 n[i++] = norm;
5364
5365 shPos = pos;
5366 shPos.x += radius*0.6f;
5367 shPos.z -= radius*0.6f;
5368 m_terrain->GetNormal(norm, shPos);
5369 n[i++] = norm;
5370 n[i++] = norm;
5371
5372 shPos = pos;
5373 shPos.x -= radius*0.6f;
5374 shPos.z -= radius*0.6f;
5375 m_terrain->GetNormal(norm, shPos);
5376 n[i++] = norm;
5377 n[i++] = norm;
5378
5379 shPos = pos;
5380 shPos.x += radius;
5381 shPos.z += radius;
5382 m_terrain->GetNormal(norm, shPos);
5383 n[i++] = norm;
5384
5385 shPos = pos;
5386 shPos.x -= radius;
5387 shPos.z += radius;
5388 m_terrain->GetNormal(norm, shPos);
5389 n[i++] = norm;
5390
5391 shPos = pos;
5392 shPos.x += radius;
5393 shPos.z -= radius;
5394 m_terrain->GetNormal(norm, shPos);
5395 n[i++] = norm;
5396
5397 shPos = pos;
5398 shPos.x -= radius;
5399 shPos.z -= radius;
5400 m_terrain->GetNormal(norm, shPos);
5401 n[i++] = norm;
5402
5403 norm.LoadZero();
5404 for (int j = 0; j < i; j++)
5405 {
5406 norm += n[j];
5407 }
5408 norm /= static_cast<float>(i); // average vector
5409
5410 m_shadowSpots[shadowRank].normal = norm;
5411 }
5412
AddStaticMesh(const std::string & key,const CModelMesh * mesh,const Math::Matrix & worldMatrix)5413 int CEngine::AddStaticMesh(const std::string& key, const CModelMesh* mesh, const Math::Matrix& worldMatrix)
5414 {
5415 int baseObjRank = -1;
5416
5417 auto it = m_staticMeshBaseObjects.find(key);
5418 if (it == m_staticMeshBaseObjects.end())
5419 {
5420 baseObjRank = CreateBaseObject();
5421 AddBaseObjTriangles(baseObjRank, mesh->GetTriangles());
5422 m_staticMeshBaseObjects[key] = baseObjRank;
5423 }
5424 else
5425 {
5426 baseObjRank = it->second;
5427 }
5428
5429 int objRank = CreateObject();
5430 SetObjectBaseRank(objRank, baseObjRank);
5431 SetObjectTransform(objRank, worldMatrix);
5432 SetObjectType(objRank, ENG_OBJTYPE_FIX);
5433
5434 return objRank;
5435 }
5436
AddStaticMeshShadowSpot(int meshHandle,const ModelShadowSpot & shadowSpot)5437 void CEngine::AddStaticMeshShadowSpot(int meshHandle, const ModelShadowSpot& shadowSpot)
5438 {
5439 int objRank = meshHandle;
5440
5441 CreateShadowSpot(objRank);
5442 SetObjectShadowSpotRadius(objRank, shadowSpot.radius);
5443 SetObjectShadowSpotIntensity(objRank, shadowSpot.intensity);
5444 SetObjectShadowSpotType(objRank, ENG_SHADOW_NORM);
5445 SetObjectShadowSpotHeight(objRank, 0.0f);
5446 SetObjectShadowSpotAngle(objRank, 0.0f);
5447 UpdateObjectShadowSpotNormal(objRank);
5448 }
5449
DeleteStaticMesh(int meshHandle)5450 void CEngine::DeleteStaticMesh(int meshHandle)
5451 {
5452 int objRank = meshHandle;
5453
5454 DeleteShadowSpot(objRank);
5455 DeleteObject(objRank);
5456 }
5457
GetStaticMeshWorldMatrix(int meshHandle)5458 const Math::Matrix& CEngine::GetStaticMeshWorldMatrix(int meshHandle)
5459 {
5460 int objRank = meshHandle;
5461 return m_objects[objRank].transform;
5462 }
5463
SetStaticMeshTransparency(int meshHandle,float value)5464 void CEngine::SetStaticMeshTransparency(int meshHandle, float value)
5465 {
5466 int objRank = meshHandle;
5467 SetObjectTransparency(objRank, value);
5468 }
5469
SetDebugLights(bool debugLights)5470 void CEngine::SetDebugLights(bool debugLights)
5471 {
5472 m_debugLights = debugLights;
5473 }
5474
GetDebugLights()5475 bool CEngine::GetDebugLights()
5476 {
5477 return m_debugLights;
5478 }
5479
DebugDumpLights()5480 void CEngine::DebugDumpLights()
5481 {
5482 m_debugDumpLights = true;
5483 }
5484
SetDebugResources(bool debugResources)5485 void CEngine::SetDebugResources(bool debugResources)
5486 {
5487 m_debugResources = debugResources;
5488 m_firstGroundSpot = true; // Force a refresh of ground spot textures
5489 UpdateGroundSpotTextures();
5490 }
5491
GetDebugResources()5492 bool CEngine::GetDebugResources()
5493 {
5494 return m_debugResources;
5495 }
5496
SetDebugGoto(bool debugGoto)5497 void CEngine::SetDebugGoto(bool debugGoto)
5498 {
5499 m_debugGoto = debugGoto;
5500 if (!m_debugGoto)
5501 {
5502 m_displayGotoImage.reset();
5503 }
5504 }
5505
GetDebugGoto()5506 bool CEngine::GetDebugGoto()
5507 {
5508 return m_debugGoto;
5509 }
5510
AddDebugGotoLine(std::vector<Gfx::VertexCol> line)5511 void CEngine::AddDebugGotoLine(std::vector<Gfx::VertexCol> line)
5512 {
5513 m_displayGoto.push_back(line);
5514 }
5515
SetDebugGotoBitmap(std::unique_ptr<CImage> debugImage)5516 void CEngine::SetDebugGotoBitmap(std::unique_ptr<CImage> debugImage)
5517 {
5518 m_displayGotoImage = std::move(debugImage);
5519 m_firstGroundSpot = true; // Force ground spot texture reload
5520 UpdateGroundSpotTextures();
5521 }
5522
SetInterfaceCoordinates()5523 void CEngine::SetInterfaceCoordinates()
5524 {
5525 m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface);
5526 m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface);
5527 m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface);
5528 }
5529
EnablePauseBlur()5530 void CEngine::EnablePauseBlur()
5531 {
5532 if (!m_pauseBlurEnabled) return;
5533
5534 m_captureWorld = true;
5535 m_worldCaptured = false;
5536 }
5537
DisablePauseBlur()5538 void CEngine::DisablePauseBlur()
5539 {
5540 m_captureWorld = false;
5541 m_worldCaptured = false;
5542 }
5543
SetWindowCoordinates()5544 void CEngine::SetWindowCoordinates()
5545 {
5546 Math::Matrix matWorldWindow;
5547 matWorldWindow.LoadIdentity();
5548
5549 Math::Matrix matViewWindow;
5550 matViewWindow.LoadIdentity();
5551
5552 Math::Matrix matProjWindow;
5553 Math::LoadOrthoProjectionMatrix(matProjWindow, 0.0f, m_size.x, m_size.y, 0.0f, -1.0f, 1.0f);
5554
5555 m_device->SetTransform(TRANSFORM_VIEW, matViewWindow);
5556 m_device->SetTransform(TRANSFORM_PROJECTION, matProjWindow);
5557 m_device->SetTransform(TRANSFORM_WORLD, matWorldWindow);
5558 }
5559
5560 } // namespace Gfx
5561