colobot/src/graphics/engine/engine.cpp

4849 lines
138 KiB
C++
Raw Normal View History

/*
* This file is part of the Colobot: Gold Edition source code
2021-09-11 13:52:34 +00:00
* Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam
2015-08-22 14:40:02 +00:00
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "graphics/engine/engine.h"
#include "app/app.h"
#include "app/input.h"
2012-09-19 21:50:28 +00:00
#include "common/image.h"
#include "common/key.h"
#include "common/logger.h"
#include "common/profiler.h"
2015-08-09 13:20:35 +00:00
#include "common/stringutils.h"
2012-09-19 21:50:28 +00:00
#include "common/system/system.h"
#include "graphics/core/device.h"
#include "graphics/core/framebuffer.h"
#include "graphics/core/material.h"
#include "graphics/core/renderers.h"
#include "graphics/core/triangle.h"
#include "graphics/engine/camera.h"
#include "graphics/engine/cloud.h"
#include "graphics/engine/lightman.h"
#include "graphics/engine/lightning.h"
#include "graphics/engine/oldmodelmanager.h"
#include "graphics/engine/particle.h"
#include "graphics/engine/planet.h"
#include "graphics/engine/pyro_manager.h"
#include "graphics/engine/terrain.h"
#include "graphics/engine/text.h"
#include "graphics/engine/water.h"
2012-09-19 21:50:28 +00:00
#include "graphics/model/model_mesh.h"
#include "graphics/model/model_shadow_spot.h"
#include "level/robotmain.h"
#include "math/geometry.h"
2012-09-19 21:50:28 +00:00
#include "sound/sound.h"
2012-09-19 21:50:28 +00:00
#include "ui/controls/interface.h"
#include <iomanip>
#include <SDL_surface.h>
#include <SDL_thread.h>
#include <thread>
using TimeUtils::TimeUnit;
2012-09-19 21:50:28 +00:00
// Graphics module namespace
2015-08-02 09:40:47 +00:00
namespace Gfx
{
2012-09-19 21:50:28 +00:00
/**
* \struct EngineBaseObjDataTier
* \brief Tier 3 of object tree (data)
*/
struct EngineBaseObjDataTier
{
EngineTriangleType type = EngineTriangleType::TRIANGLES;
Material material = {};
std::vector<Vertex3D> vertices;
CVertexBuffer* buffer = nullptr;
bool updateStaticBuffer = false;
Texture albedoTexture;
Texture emissiveTexture;
Texture materialTexture;
Texture normalTexture;
Texture detailTexture;
glm::vec2 uvOffset = { 0.0f, 0.0f };
glm::vec2 uvScale = { 1.0f, 1.0f };
};
/**
* \struct BaseEngineObject
* \brief Base (template) object - geometry for engine objects
*
* This is also the tier 1 of base object tree.
*/
struct EngineBaseObject
{
//! If true, base object is valid in objects vector
bool used = false;
//! Number of triangles
int totalTriangles = 0;
//! Bounding box min (origin 0,0,0 always included)
glm::vec3 bboxMin{ 0, 0, 0 };
//! bounding box max (origin 0,0,0 always included)
glm::vec3 bboxMax{ 0, 0, 0 };
//! A bounding sphere that contains all the vertices in this EngineBaseObject
Math::Sphere boundingSphere;
//! Next tier
std::vector<EngineBaseObjDataTier> next;
inline void LoadDefault()
{
*this = EngineBaseObject();
}
};
2016-06-21 11:07:40 +00:00
/**
* \struct EngineMouse
* \brief Information about mouse cursor
*/
struct EngineMouse
{
//! Index of texture element for 1st image
int icon1;
//! Index of texture element for 2nd image
int icon2;
//! Shadow texture part
int iconShadow;
//! Mode to render 1st image in
TransparencyMode mode1;
2016-06-21 11:07:40 +00:00
//! Mode to render 2nd image in
TransparencyMode mode2;
2016-06-21 11:07:40 +00:00
//! Hot point
glm::ivec2 hotPoint;
2016-06-21 11:07:40 +00:00
EngineMouse(int icon1 = -1,
int icon2 = -1,
int iconShadow = -1,
TransparencyMode mode1 = TransparencyMode::NONE,
TransparencyMode mode2 = TransparencyMode::NONE,
glm::ivec2 hotPoint = { 0, 0 })
2016-06-21 11:07:40 +00:00
: icon1(icon1)
, icon2(icon2)
, iconShadow(iconShadow)
, mode1(mode1)
, mode2(mode2)
, hotPoint(hotPoint)
{}
};
constexpr glm::ivec2 MOUSE_SIZE(32, 32);
2016-06-21 11:07:40 +00:00
const std::map<EngineMouseType, EngineMouse> MOUSE_TYPES = {
{{ENG_MOUSE_NORM}, {EngineMouse( 0, 1, 32, TransparencyMode::WHITE, TransparencyMode::BLACK, glm::ivec2( 1, 1))}},
{{ENG_MOUSE_WAIT}, {EngineMouse( 2, 3, 33, TransparencyMode::WHITE, TransparencyMode::BLACK, glm::ivec2( 8, 12))}},
{{ENG_MOUSE_HAND}, {EngineMouse( 4, 5, 34, TransparencyMode::WHITE, TransparencyMode::BLACK, glm::ivec2( 7, 2))}},
{{ENG_MOUSE_NO}, {EngineMouse( 6, 7, 35, TransparencyMode::WHITE, TransparencyMode::BLACK, glm::ivec2(10, 10))}},
{{ENG_MOUSE_EDIT}, {EngineMouse( 8, 9, -1, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2( 6, 10))}},
{{ENG_MOUSE_CROSS}, {EngineMouse(10, 11, -1, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2(10, 10))}},
{{ENG_MOUSE_MOVEV}, {EngineMouse(12, 13, -1, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2( 5, 11))}},
{{ENG_MOUSE_MOVEH}, {EngineMouse(14, 15, -1, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2(11, 5))}},
{{ENG_MOUSE_MOVED}, {EngineMouse(16, 17, -1, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2( 9, 9))}},
{{ENG_MOUSE_MOVEI}, {EngineMouse(18, 19, -1, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2( 9, 9))}},
{{ENG_MOUSE_MOVE}, {EngineMouse(20, 21, -1, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2(11, 11))}},
{{ENG_MOUSE_TARGET}, {EngineMouse(22, 23, -1, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2(15, 15))}},
{{ENG_MOUSE_SCROLLL}, {EngineMouse(24, 25, 43, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2( 2, 9))}},
{{ENG_MOUSE_SCROLLR}, {EngineMouse(26, 27, 44, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2(17, 9))}},
{{ENG_MOUSE_SCROLLU}, {EngineMouse(28, 29, 45, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2( 9, 2))}},
{{ENG_MOUSE_SCROLLD}, {EngineMouse(30, 31, 46, TransparencyMode::BLACK, TransparencyMode::WHITE, glm::ivec2( 9, 17))}},
2016-06-21 11:07:40 +00:00
};
CEngine::CEngine(CApplication *app, CSystemUtils* systemUtils)
: m_app(app),
m_systemUtils(systemUtils),
m_ambientColor(),
m_fogColor(),
m_deepView(),
m_fogStart(),
2016-06-21 11:07:40 +00:00
m_highlightRank()
{
m_device = nullptr;
m_lightMan = nullptr;
m_text = nullptr;
m_particle = nullptr;
m_water = nullptr;
m_cloud = nullptr;
m_lightning = nullptr;
m_planet = nullptr;
m_sound = nullptr;
m_terrain = nullptr;
m_showStats = false;
m_focus = 0.75f;
2018-04-19 23:31:11 +00:00
m_hfov = 2.0f * atan((640.f/480.f) * tan(m_focus / 2.0f));
m_rankView = 0;
2012-09-19 21:50:28 +00:00
m_ambientColor[0] = Color(0.5f, 0.5f, 0.5f, 0.5f);
m_ambientColor[1] = Color(0.5f, 0.5f, 0.5f, 0.5f);
m_fogColor[0] = Color(1.0f, 1.0f, 1.0f, 1.0f);
m_fogColor[1] = Color(1.0f, 1.0f, 1.0f, 1.0f);
m_deepView[0] = 1000.0f;
m_deepView[1] = 1000.0f;
m_fogStart[0] = 0.75f;
m_fogStart[1] = 0.75f;
2012-09-19 21:50:28 +00:00
m_waterAddColor = Color(0.0f, 0.0f, 0.0f, 0.0f);
m_render = true;
2015-09-04 17:16:21 +00:00
m_renderInterface = true;
m_screenshotMode = false;
m_triplanarMode = false;
m_triplanarScale = 0.2f;
m_dirty = true;
m_fog = true;
m_secondTex = "";
m_eyeDirH = 0.0f;
m_eyeDirV = 0.0f;
m_backgroundName = ""; // no background image
2012-09-19 21:50:28 +00:00
m_backgroundColorUp = Color();
m_backgroundColorDown = Color();
m_backgroundCloudUp = Color();
m_backgroundCloudDown = Color();
m_backgroundFull = false;
m_backgroundScale = false;
m_overFront = true;
2012-09-19 21:50:28 +00:00
m_overColor = Color();
m_overMode = TransparencyMode::BLACK;
m_highlight = false;
std::fill_n(m_highlightRank, 100, -1);
m_highlightTime = 0.0f;
m_eyePt = glm::vec3(0.0f, 0.0f, 0.0f);
m_lookatPt = glm::vec3(0.0f, 0.0f, 1.0f);
m_drawWorld = true;
m_drawFront = false;
m_particleDensity = 1.0f;
m_clippingDistance = 1.0f;
m_terrainVision = 1000.0f;
m_textureMipmapLevel = 1;
m_textureAnisotropy = 1;
m_shadowMapping = true;
m_offscreenShadowRendering = true;
m_offscreenShadowRenderingResolution = 1024;
m_qualityShadows = true;
m_terrainShadows = false;
2015-06-15 16:41:31 +00:00
m_shadowRange = 0.0f;
m_multisample = 2;
2018-07-05 17:43:56 +00:00
m_vsync = 0;
2015-06-21 22:56:47 +00:00
m_backForce = true;
m_lightMode = true;
m_editIndentMode = true;
m_editIndentValue = 4;
m_tracePrecision = 1.0f;
m_pauseBlurEnabled = true;
m_updateGeometry = false;
m_updateStaticBuffers = false;
m_interfaceMode = false;
m_debugLights = false;
m_debugDumpLights = false;
2012-09-19 21:50:28 +00:00
m_mouseType = ENG_MOUSE_NORM;
m_fpsCounter = 0;
2015-06-20 18:02:06 +00:00
m_shadowColor = 0.5f;
2012-09-19 21:50:28 +00:00
m_defaultTexParams.format = TEX_IMG_AUTO;
m_defaultTexParams.filter = TEX_FILTER_BILINEAR;
m_terrainTexParams.format = TEX_IMG_AUTO;
m_terrainTexParams.filter = TEX_FILTER_BILINEAR;
// Compute bias matrix for shadow mapping
glm::mat4 temp1, temp2;
Math::LoadScaleMatrix(temp1, glm::vec3(0.5f, 0.5f, 0.5f));
Math::LoadTranslationMatrix(temp2, glm::vec3(1.0f, 1.0f, 1.0f));
m_shadowBias = temp1 * temp2;
m_statisticTriangle = 0;
m_fps = 0.0f;
m_firstGroundSpot = false;
}
2012-09-19 21:50:28 +00:00
CEngine::~CEngine()
{
}
2012-09-19 21:50:28 +00:00
void CEngine::SetDevice(CDevice *device)
{
m_device = device;
}
2012-09-19 21:50:28 +00:00
CDevice* CEngine::GetDevice()
{
return m_device;
}
COldModelManager* CEngine::GetModelManager()
{
return m_modelManager.get();
}
CPyroManager* CEngine::GetPyroManager()
{
return m_pyroManager.get();
}
CText* CEngine::GetText()
{
return m_text.get();
}
CLightManager* CEngine::GetLightManager()
{
return m_lightMan.get();
}
CParticle* CEngine::GetParticle()
{
return m_particle.get();
}
CTerrain* CEngine::GetTerrain()
{
return m_terrain;
}
CWater* CEngine::GetWater()
{
return m_water.get();
}
CLightning* CEngine::GetLightning()
{
return m_lightning.get();
}
CPlanet* CEngine::GetPlanet()
{
return m_planet.get();
}
CCloud* CEngine::GetCloud()
{
return m_cloud.get();
}
void CEngine::SetTerrain(CTerrain* terrain)
{
m_terrain = terrain;
}
2012-09-19 21:50:28 +00:00
bool CEngine::Create()
{
2013-01-27 10:43:53 +00:00
m_size = m_app->GetVideoConfig().size;
// Use the setters to set defaults, because they automatically disable what is not supported
SetShadowMapping(m_shadowMapping);
SetShadowMappingQuality(m_qualityShadows);
SetShadowMappingOffscreen(m_offscreenShadowRendering);
SetShadowMappingOffscreenResolution(m_offscreenShadowRenderingResolution);
SetMultiSample(m_multisample);
2018-07-05 17:43:56 +00:00
SetVSync(m_vsync);
m_modelManager = std::make_unique<COldModelManager>(this);
m_pyroManager = std::make_unique<CPyroManager>();
m_lightMan = std::make_unique<CLightManager>(this);
m_text = std::make_unique<CText>(this);
m_particle = std::make_unique<CParticle>(this);
m_water = std::make_unique<CWater>(this);
m_cloud = std::make_unique<CCloud>(this);
m_lightning = std::make_unique<CLightning>(this);
m_planet = std::make_unique<CPlanet>(this);
2012-08-26 23:02:25 +00:00
m_lightMan->SetDevice(m_device);
2012-10-05 16:59:49 +00:00
m_particle->SetDevice(m_device);
2012-08-26 23:02:25 +00:00
m_text->SetDevice(m_device);
if (! m_text->Create())
{
std::string error = m_text->GetError();
GetLogger()->Error("Error creating CText: %s\n", error.c_str());
return false;
}
2012-09-19 21:50:28 +00:00
m_device->SetClearColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
SetFocus(m_focus);
m_matWorldInterface = glm::mat4(1.0f);
m_matViewInterface = glm::mat4(1.0f);
auto renderer = m_device->GetUIRenderer();
renderer->SetProjection(0.0f, 1.0f, 0.0f, 1.0f);
Math::LoadOrthoProjectionMatrix(m_matProjInterface, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
2012-09-19 21:50:28 +00:00
TextureCreateParams params;
params.format = TEX_IMG_AUTO;
params.filter = TEX_FILTER_NEAREST;
params.mipmap = false;
m_miceTexture = LoadTexture("textures/interface/mouse.png", params);
m_currentFrameTime = m_systemUtils->GetCurrentTimeStamp();
m_lastFrameTime = m_systemUtils->GetCurrentTimeStamp();
return true;
}
2012-09-19 21:50:28 +00:00
void CEngine::Destroy()
{
m_text->Destroy();
if (m_shadowMap.id != 0)
{
if (m_offscreenShadowRendering)
m_device->DeleteFramebuffer("shadow");
else
m_device->DestroyTexture(m_shadowMap);
m_shadowMap = Texture();
}
m_lightMan.reset();
m_text.reset();
m_particle.reset();
m_water.reset();
m_cloud.reset();
m_lightning.reset();
m_planet.reset();
}
2015-09-25 09:11:35 +00:00
void CEngine::ResetAfterVideoConfigChanged()
{
m_size = m_app->GetVideoConfig().size;
2013-01-03 23:29:19 +00:00
2015-09-25 09:11:35 +00:00
// Update the camera projection matrix for new aspect ratio
ApplyChange();
2015-09-25 09:11:35 +00:00
// This needs to be recreated on resolution change
m_device->DeleteFramebuffer("multisample");
}
2015-09-25 09:11:35 +00:00
void CEngine::ReloadAllTextures()
{
FlushTextureCache();
2015-09-25 09:11:35 +00:00
m_text->FlushCache();
2020-07-19 14:07:27 +00:00
m_text->ReloadFonts();
m_app->GetEventQueue()->AddEvent(Event(EVENT_RELOAD_TEXTURES));
UpdateGroundSpotTextures();
// LoadAllTextures() is called from CRobotMain on EVENT_RELOAD_TEXTURES
// This is required because all dynamic textures need to be loaded first
// recapture 3D scene
if (m_worldCaptured)
{
m_captureWorld = true;
m_worldCaptured = false;
}
}
2012-09-19 21:50:28 +00:00
bool CEngine::ProcessEvent(const Event &event)
{
if (event.type == EVENT_RESOLUTION_CHANGED)
{
ResetAfterVideoConfigChanged();
}
if (event.type == EVENT_KEY_DOWN)
{
auto data = event.GetData<KeyEventData>();
2018-07-25 21:45:15 +00:00
if (data->key == KEY(F11) || data->key == KEY(F12))
{
m_showStats = !m_showStats;
return false;
}
}
// By default, pass on all events
return true;
}
2012-09-19 21:50:28 +00:00
void CEngine::FrameUpdate()
{
float rTime = m_app->GetRelTime();
m_lightMan->UpdateProgression(rTime);
2012-10-25 21:29:49 +00:00
CProfiler::StartPerformanceCounter(PCNT_UPDATE_PARTICLE);
m_particle->FrameParticle(rTime);
CProfiler::StopPerformanceCounter(PCNT_UPDATE_PARTICLE);
2012-10-25 21:29:49 +00:00
ComputeDistance();
UpdateGeometry();
UpdateStaticBuffers();
m_highlightTime = m_app->GetAbsTime();
if (m_groundMark.draw)
{
2012-09-19 21:50:28 +00:00
if (m_groundMark.phase == ENG_GR_MARK_PHASE_INC) // growing?
{
m_groundMark.intensity += rTime*(1.0f/m_groundMark.delay[0]);
if (m_groundMark.intensity >= 1.0f)
{
m_groundMark.intensity = 1.0f;
m_groundMark.fix = 0.0f;
2012-09-19 21:50:28 +00:00
m_groundMark.phase = ENG_GR_MARK_PHASE_FIX;
}
}
2012-09-19 21:50:28 +00:00
else if (m_groundMark.phase == ENG_GR_MARK_PHASE_FIX) // fixed?
{
m_groundMark.fix += rTime*(1.0f/m_groundMark.delay[1]);
if (m_groundMark.fix >= 1.0f)
2012-09-19 21:50:28 +00:00
m_groundMark.phase = ENG_GR_MARK_PHASE_DEC;
}
2012-09-19 21:50:28 +00:00
else if (m_groundMark.phase == ENG_GR_MARK_PHASE_DEC) // decay?
{
m_groundMark.intensity -= rTime*(1.0f/m_groundMark.delay[2]);
if (m_groundMark.intensity < 0.0f)
{
m_groundMark.intensity = 0.0f;
2012-09-19 21:50:28 +00:00
m_groundMark.phase = ENG_GR_MARK_PHASE_NULL;
2012-10-25 21:29:49 +00:00
m_groundMark.draw = false;
}
}
}
}
void CEngine::WriteScreenShot(const std::string& fileName)
{
auto data = std::make_unique<WriteScreenShotData>();
data->img = std::make_unique<CImage>(glm::ivec2(m_size.x, m_size.y));
auto pixels = m_device->GetFrameBufferPixels();
data->img->SetDataPixels(pixels->GetPixelsData());
data->img->FlipVertically();
data->fileName = fileName;
std::thread{&CEngine::WriteScreenShotThread, std::move(data)}.detach();
}
void CEngine::WriteScreenShotThread(std::unique_ptr<WriteScreenShotData> data)
{
if ( data->img->SavePNG(data->fileName.c_str()) )
2015-07-16 18:47:15 +00:00
{
GetLogger()->Debug("Save screenshot saved successfully\n");
}
2015-07-16 18:47:15 +00:00
else
{
GetLogger()->Error("%s!\n", data->img->GetError().c_str());
}
CApplication::GetInstancePointer()->GetEventQueue()->AddEvent(Event(EVENT_WRITE_SCENE_FINISHED));
}
2015-10-01 16:55:41 +00:00
void CEngine::SetPause(bool pause)
{
m_pause = pause;
}
2012-09-19 21:50:28 +00:00
bool CEngine::GetPause()
{
2015-10-01 16:55:41 +00:00
return m_pause;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetShowStats(bool show)
{
m_showStats = show;
}
2012-09-19 21:50:28 +00:00
bool CEngine::GetShowStats()
{
return m_showStats;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetRenderEnable(bool enable)
{
m_render = enable;
}
2015-09-04 17:16:21 +00:00
void CEngine::SetRenderInterface(bool enable)
{
m_renderInterface = enable;
}
bool CEngine::GetRenderInterface()
{
return m_renderInterface;
}
void CEngine::SetScreenshotMode(bool screenshotMode)
{
m_screenshotMode = screenshotMode;
}
bool CEngine::GetScreenshotMode()
{
return m_screenshotMode;
}
glm::ivec2 CEngine::GetWindowSize()
{
return m_size;
}
2021-12-24 19:25:38 +00:00
glm::vec2 CEngine::WindowToInterfaceCoords(const glm::ivec2& pos)
{
2021-12-24 19:25:38 +00:00
return { static_cast<float>(pos.x) / static_cast<float>(m_size.x),
1.0f - static_cast<float>(pos.y) / static_cast<float>(m_size.y) };
}
2021-12-24 19:25:38 +00:00
glm::ivec2 CEngine::InterfaceToWindowCoords(const glm::vec2& pos)
{
return { static_cast<int>(pos.x * m_size.x),
static_cast<int>((1.0f - pos.y) * m_size.y) };
}
2021-12-24 19:25:38 +00:00
glm::vec2 CEngine::WindowToInterfaceSize(const glm::ivec2& size)
{
2021-12-24 19:25:38 +00:00
return { static_cast<float>(size.x) / static_cast<float>(m_size.x),
static_cast<float>(size.y) / static_cast<float>(m_size.y) };
}
2021-12-24 19:25:38 +00:00
glm::ivec2 CEngine::InterfaceToWindowSize(const glm::vec2& size)
{
return { static_cast<int>(size.x * m_size.x),
static_cast<int>(size.y * m_size.y) };
}
2012-09-19 21:50:28 +00:00
void CEngine::AddStatisticTriangle(int count)
{
m_statisticTriangle += count;
}
2012-09-19 21:50:28 +00:00
int CEngine::GetStatisticTriangle()
{
return m_statisticTriangle;
}
void CEngine::SetStatisticPos(glm::vec3 pos)
{
m_statisticPos = pos;
}
2014-10-29 16:53:46 +00:00
void CEngine::SetTimerDisplay(const std::string& text)
{
m_timerText = text;
}
/*******************************************************
Object management
*******************************************************/
EngineBaseObjDataTier& CEngine::AddLevel(EngineBaseObject& p3, EngineTriangleType type, const Material& material)
{
for (size_t i = 0; i < p3.next.size(); i++)
{
if ( (p3.next[i].type == type) && (p3.next[i].material == material) )
return p3.next[i];
}
p3.next.push_back(EngineBaseObjDataTier{ type, material });
return p3.next.back();
}
int CEngine::CreateBaseObject()
{
size_t baseObjRank = 0;
for (; baseObjRank < m_baseObjects.size(); baseObjRank++)
{
if (!m_baseObjects[baseObjRank].used)
{
m_baseObjects[baseObjRank] = {};
break;
}
}
if (baseObjRank == m_baseObjects.size())
m_baseObjects.push_back(EngineBaseObject());
else
m_baseObjects[baseObjRank] = {};
2012-12-27 13:18:16 +00:00
m_baseObjects[baseObjRank].used = true;
return static_cast<int>(baseObjRank);
}
void CEngine::DeleteBaseObject(int baseObjRank)
{
2012-12-27 13:18:16 +00:00
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (! p1.used)
return;
for (auto& data : p1.next)
{
m_device->DestroyVertexBuffer(data.buffer);
data.buffer = nullptr;
}
2012-12-27 13:18:16 +00:00
p1.next.clear();
p1.used = false;
}
void CEngine::DeleteAllBaseObjects()
{
for (auto& object : m_baseObjects)
2013-10-13 12:44:12 +00:00
{
if (!object.used)
2013-10-13 12:44:12 +00:00
continue;
for (auto& data : object.next)
2013-10-13 12:44:12 +00:00
{
m_device->DestroyVertexBuffer(data.buffer);
data.buffer = nullptr;
2013-10-13 12:44:12 +00:00
}
}
m_baseObjects.clear();
}
void CEngine::CopyBaseObject(int sourceBaseObjRank, int destBaseObjRank)
{
assert(sourceBaseObjRank >= 0 && sourceBaseObjRank < static_cast<int>( m_baseObjects.size() ));
assert(destBaseObjRank >= 0 && destBaseObjRank < static_cast<int>( m_baseObjects.size() ));
m_baseObjects[destBaseObjRank] = m_baseObjects[sourceBaseObjRank];
EngineBaseObject& p1 = m_baseObjects[destBaseObjRank];
if (! p1.used)
return;
for (auto& data : p1.next)
{
data.buffer = nullptr;
data.updateStaticBuffer = true;
}
m_updateStaticBuffers = true;
}
void CEngine::AddBaseObjTriangles(int baseObjRank, const std::vector<Vertex3D>& vertices,
const Material& material, EngineTriangleType type)
{
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
2012-12-27 13:18:16 +00:00
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
EngineBaseObjDataTier& p3 = AddLevel(p1, type, material);
p3.vertices.insert(p3.vertices.end(), vertices.begin(), vertices.end());
if (p3.buffer)
m_device->DestroyVertexBuffer(p3.buffer);
p3.buffer = nullptr;
p3.updateStaticBuffer = true;
m_updateStaticBuffers = true;
for (size_t i = 0; i < vertices.size(); i++)
{
p1.bboxMin.x = Math::Min(vertices[i].position.x, p1.bboxMin.x);
p1.bboxMin.y = Math::Min(vertices[i].position.y, p1.bboxMin.y);
p1.bboxMin.z = Math::Min(vertices[i].position.z, p1.bboxMin.z);
p1.bboxMax.x = Math::Max(vertices[i].position.x, p1.bboxMax.x);
p1.bboxMax.y = Math::Max(vertices[i].position.y, p1.bboxMax.y);
p1.bboxMax.z = Math::Max(vertices[i].position.z, p1.bboxMax.z);
}
p1.boundingSphere = Math::BoundingSphereForBox(p1.bboxMin, p1.bboxMax);
p1.totalTriangles += vertices.size() / 3;
}
void CEngine::DebugObject(int objRank)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
CLogger* l = GetLogger();
l->Debug("Debug object: %d\n", objRank);
if (! m_objects[objRank].used)
{
l->Debug(" not used\n");
return;
}
l->Debug(" baseObjRank = %d\n", m_objects[objRank].baseObjRank);
l->Debug(" visible = %s\n", m_objects[objRank].visible ? "true" : "false");
l->Debug(" drawWorld = %s\n", m_objects[objRank].drawWorld ? "true" : "false");
l->Debug(" drawFront = %s\n", m_objects[objRank].drawFront ? "true" : "false");
l->Debug(" type = %d\n", m_objects[objRank].type);
l->Debug(" distance = %f\n", m_objects[objRank].distance);
l->Debug(" shadowRank = %d\n", m_objects[objRank].shadowRank);
2022-01-24 20:57:56 +00:00
l->Debug(" ghost = %s\n", m_objects[objRank].ghost ? "true" : "false");
l->Debug(" baseObj:\n");
int baseObjRank = m_objects[objRank].baseObjRank;
if (baseObjRank == -1)
{
l->Debug(" null\n");
return;
}
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (!p1.used)
{
l->Debug(" not used\n");
return;
}
std::string vecStr;
vecStr = Math::ToString(p1.bboxMin);
l->Debug(" bboxMin: %s\n", vecStr.c_str());
vecStr = Math::ToString(p1.bboxMax);
l->Debug(" bboxMax: %s\n", vecStr.c_str());
l->Debug(" totalTriangles: %d\n", p1.totalTriangles);
l->Debug(" radius: %f\n", p1.boundingSphere.radius);
for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
{
EngineBaseObjDataTier& p2 = p1.next[l2];
l->Debug(" l2:\n");
/*
l->Debug(" tex1: %s (id: %u)\n", p2.tex1Name.c_str(), p2.tex1.id);
l->Debug(" tex2: %s (id: %u)\n", p2.tex2Name.c_str(), p2.tex2.id);
for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
{
EngineBaseObjDataTier& p3 = p2.next[l3];
l->Debug(" l3:\n");
l->Debug(" type: %d\n", p3.type);
l->Debug(" state: %d\n", p3.state);
l->Debug(" staticBufferId: %u\n", p3.buffer);
l->Debug(" updateStaticBuffer: %s\n", p3.updateStaticBuffer ? "true" : "false");
}
// */
}
}
int CEngine::CreateObject()
{
2012-12-27 13:18:16 +00:00
int objRank = 0;
for ( ; objRank < static_cast<int>( m_objects.size() ); objRank++)
{
2012-12-27 13:18:16 +00:00
if (! m_objects[objRank].used)
{
2012-12-27 13:18:16 +00:00
m_objects[objRank].LoadDefault();
break;
}
}
2012-12-27 13:18:16 +00:00
if (objRank == static_cast<int>( m_objects.size() ))
m_objects.push_back(EngineObject());
2012-12-27 13:18:16 +00:00
m_objects[objRank].used = true;
glm::mat4 mat = glm::mat4(1.0f);
2012-12-27 13:18:16 +00:00
SetObjectTransform(objRank, mat);
2012-12-27 13:18:16 +00:00
m_objects[objRank].drawWorld = true;
m_objects[objRank].distance = 0.0f;
2013-10-13 12:44:12 +00:00
m_objects[objRank].baseObjRank = -1;
2012-12-27 13:18:16 +00:00
m_objects[objRank].shadowRank = -1;
2012-12-27 13:18:16 +00:00
return objRank;
}
void CEngine::DeleteAllObjects()
{
m_objects.clear();
m_shadowSpots.clear();
DeleteAllGroundSpots();
}
void CEngine::DeleteObject(int objRank)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
// Mark object as deleted
m_objects[objRank].used = false;
// Delete associated shadows
DeleteShadowSpot(objRank);
}
void CEngine::SetObjectBaseRank(int objRank, int baseObjRank)
{
2012-12-28 12:23:49 +00:00
assert(objRank == -1 || (objRank >= 0 && objRank < static_cast<int>( m_objects.size() )));
m_objects[objRank].baseObjRank = baseObjRank;
}
int CEngine::GetObjectBaseRank(int objRank)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
return m_objects[objRank].baseObjRank;
}
void CEngine::SetObjectType(int objRank, EngineObjectType type)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
m_objects[objRank].type = type;
}
EngineObjectType CEngine::GetObjectType(int objRank)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
return m_objects[objRank].type;
}
void CEngine::SetObjectTransform(int objRank, const glm::mat4& transform)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
m_objects[objRank].transform = transform;
}
void CEngine::GetObjectTransform(int objRank, glm::mat4& transform)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
transform = m_objects[objRank].transform;
}
void CEngine::SetObjectDrawWorld(int objRank, bool draw)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
m_objects[objRank].drawWorld = draw;
}
void CEngine::SetObjectDrawFront(int objRank, bool draw)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
m_objects[objRank].drawFront = draw;
}
2022-01-24 20:57:56 +00:00
void CEngine::SetObjectGhostMode(int objRank, bool enabled)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
2022-01-24 20:57:56 +00:00
m_objects[objRank].ghost = enabled;
}
void CEngine::GetObjectBBox(int objRank, glm::vec3& min, glm::vec3& max)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
return;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
min = m_baseObjects[baseObjRank].bboxMin;
max = m_baseObjects[baseObjRank].bboxMax;
}
int CEngine::GetObjectTotalTriangles(int objRank)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
return 0;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
return m_baseObjects[baseObjRank].totalTriangles;
}
int CEngine::GetPartialTriangles(int objRank, float percent, int maxCount,
std::vector<EngineTriangle>& triangles)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
return 0;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
2012-12-27 13:18:16 +00:00
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
int total = p1.totalTriangles;
int expectedCount = static_cast<int>(percent * total);
triangles.reserve(Math::Min(maxCount, expectedCount));
int actualCount = 0;
2012-12-27 13:18:16 +00:00
for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
{
EngineBaseObjDataTier& p3 = p1.next[l2];
if (p3.type == EngineTriangleType::TRIANGLES)
{
for (size_t i = 0; i < p3.vertices.size(); i += 3)
{
if (static_cast<float>(actualCount) / total >= percent)
break;
if (actualCount >= maxCount)
break;
EngineTriangle t;
t.triangle[0] = p3.vertices[i];
t.triangle[1] = p3.vertices[i + 1];
t.triangle[2] = p3.vertices[i + 2];
t.material = p3.material;
triangles.push_back(t);
++actualCount;
}
}
else if (p3.type == EngineTriangleType::SURFACE)
{
for (size_t i = 0; i < p3.vertices.size(); i += 1)
{
if (static_cast<float>(actualCount) / total >= percent)
break;
if (actualCount >= maxCount)
break;
EngineTriangle t;
t.triangle[0] = p3.vertices[i];
t.triangle[1] = p3.vertices[i + 1];
t.triangle[2] = p3.vertices[i + 2];
t.material = p3.material;
triangles.push_back(t);
++actualCount;
}
}
}
return actualCount;
}
void CEngine::ChangeSecondTexture(int objRank, const std::string& tex2Name)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
return;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
2012-12-27 13:18:16 +00:00
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
for (auto& data : p1.next)
{
if (data.material.detailTexture == tex2Name)
continue; // already new
data.material.detailTexture = tex2Name;
data.detailTexture = LoadTexture("textures/" + tex2Name);
}
}
void CEngine::SetUVTransform(int objRank, const std::string& tag, const glm::vec2& offset, const glm::vec2& scale)
{
assert(objRank >= 0 && objRank < static_cast<int>(m_objects.size()));
2012-10-19 20:37:11 +00:00
int baseObjRank = m_objects[objRank].baseObjRank;
if (baseObjRank == -1)
return;
2012-10-19 20:37:11 +00:00
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
2012-10-19 20:37:11 +00:00
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
2012-10-19 20:37:11 +00:00
for (auto& data : p1.next)
2012-10-19 20:37:11 +00:00
{
if (data.material.tag == tag)
2012-10-19 20:37:11 +00:00
{
data.uvOffset = offset;
data.uvScale = scale;
2012-10-19 20:37:11 +00:00
}
}
}
void CEngine::CreateShadowSpot(int objRank)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
// Already allocated?
if (m_objects[objRank].shadowRank != -1)
return;
int index = 0;
for ( ; index < static_cast<int>( m_shadowSpots.size() ); index++)
{
if (! m_shadowSpots[index].used)
{
m_shadowSpots[index].LoadDefault();
break;
}
}
if (index == static_cast<int>( m_shadowSpots.size() ))
m_shadowSpots.push_back(EngineShadow());
m_shadowSpots[index].used = true;
m_shadowSpots[index].objRank = objRank;
m_shadowSpots[index].height = 0.0f;
m_objects[objRank].shadowRank = index;
}
void CEngine::DeleteShadowSpot(int objRank)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int shadowRank = m_objects[objRank].shadowRank;
if (shadowRank == -1)
return;
assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
m_shadowSpots[shadowRank].used = false;
m_shadowSpots[shadowRank].objRank = -1;
m_objects[objRank].shadowRank = -1;
}
void CEngine::SetObjectShadowSpotHide(int objRank, bool hide)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int shadowRank = m_objects[objRank].shadowRank;
if (shadowRank == -1)
return;
assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
m_shadowSpots[shadowRank].hide = hide;
}
void CEngine::SetObjectShadowSpotType(int objRank, EngineShadowType type)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int shadowRank = m_objects[objRank].shadowRank;
if (shadowRank == -1)
return;
assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
m_shadowSpots[shadowRank].type = type;
}
void CEngine::SetObjectShadowSpotPos(int objRank, const glm::vec3& pos)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int shadowRank = m_objects[objRank].shadowRank;
if (shadowRank == -1)
return;
assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
m_shadowSpots[shadowRank].pos = pos;
}
void CEngine::SetObjectShadowSpotAngle(int objRank, float angle)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int shadowRank = m_objects[objRank].shadowRank;
if (shadowRank == -1)
return;
assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
m_shadowSpots[shadowRank].angle = angle;
}
void CEngine::SetObjectShadowSpotRadius(int objRank, float radius)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int shadowRank = m_objects[objRank].shadowRank;
if (shadowRank == -1)
return;
assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
m_shadowSpots[shadowRank].radius = radius;
}
void CEngine::SetObjectShadowSpotIntensity(int objRank, float intensity)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int shadowRank = m_objects[objRank].shadowRank;
if (shadowRank == -1)
return;
assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
m_shadowSpots[shadowRank].intensity = intensity;
}
void CEngine::SetObjectShadowSpotHeight(int objRank, float height)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int shadowRank = m_objects[objRank].shadowRank;
if (shadowRank == -1)
return;
assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
m_shadowSpots[shadowRank].height = height;
}
2021-12-24 19:25:38 +00:00
bool CEngine::GetHighlight(glm::vec2& p1, glm::vec2& p2)
{
p1 = m_highlightP1;
p2 = m_highlightP2;
return m_highlight;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetHighlightRank(int *rankList)
{
int i = 0;
while ( *rankList != -1 )
{
m_highlightRank[i++] = *rankList++;
}
m_highlightRank[i] = -1; // terminator
}
2021-12-24 19:25:38 +00:00
bool CEngine::GetBBox2D(int objRank, glm::vec2& min, glm::vec2& max)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
min.x = 1000000.0f;
min.y = 1000000.0f;
max.x = -1000000.0f;
max.y = -1000000.0f;
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
return false;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
2012-12-27 13:18:16 +00:00
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
for (int i = 0; i < 8; i++)
{
glm::vec3 p{ 0, 0, 0 };
2012-12-27 13:18:16 +00:00
if ( i & (1<<0) ) p.x = p1.bboxMin.x;
else p.x = p1.bboxMax.x;
if ( i & (1<<1) ) p.y = p1.bboxMin.y;
else p.y = p1.bboxMax.y;
if ( i & (1<<2) ) p.z = p1.bboxMin.z;
else p.z = p1.bboxMax.z;
glm::vec3 pp{ 0, 0, 0 };
if (TransformPoint(pp, objRank, p))
{
if (pp.x < min.x) min.x = pp.x;
if (pp.x > max.x) max.x = pp.x;
if (pp.y < min.y) min.y = pp.y;
if (pp.y > max.y) max.y = pp.y;
}
}
if (min.x == 1000000.0f ||
min.y == 1000000.0f ||
max.x == -1000000.0f ||
max.y == -1000000.0f)
return false;
return true;
}
void CEngine::DeleteAllGroundSpots()
{
m_groundSpots.clear();
m_firstGroundSpot = true;
for (int s = 0; s < 16; s++)
{
CImage shadowImg(glm::ivec2(256, 256));
shadowImg.Fill(Gfx::IntColor(255, 255, 255, 255));
std::stringstream str;
str << "shadow" << std::setfill('0') << std::setw(2) << s << ".png";
std::string texName = str.str();
CreateOrUpdateTexture(texName, &shadowImg);
}
}
2012-09-19 21:50:28 +00:00
int CEngine::CreateGroundSpot()
{
int index = 0;
for ( ; index < static_cast<int>( m_groundSpots.size() ); index++)
{
if (! m_groundSpots[index].used)
{
m_groundSpots[index].LoadDefault();
break;
}
}
2012-09-19 21:50:28 +00:00
m_groundSpots.push_back(EngineGroundSpot());
m_groundSpots[index].used = true;
m_groundSpots[index].smooth = 1.0f;
return index;
}
2012-09-19 21:50:28 +00:00
void CEngine::DeleteGroundSpot(int rank)
{
assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
m_groundSpots[rank].used = false;
m_groundSpots[rank].pos = glm::vec3(0.0f, 0.0f, 0.0f);
}
void CEngine::SetObjectGroundSpotPos(int rank, const glm::vec3& pos)
{
assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
m_groundSpots[rank].pos = pos;
}
void CEngine::SetObjectGroundSpotRadius(int rank, float radius)
{
assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
m_groundSpots[rank].radius = radius;
}
void CEngine::SetObjectGroundSpotColor(int rank, const Color& color)
{
assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
m_groundSpots[rank].color = color;
}
void CEngine::SetObjectGroundSpotMinMax(int rank, float min, float max)
{
assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
m_groundSpots[rank].min = min;
m_groundSpots[rank].max = max;
}
void CEngine::SetObjectGroundSpotSmooth(int rank, float smooth)
{
assert(rank >= 0 && rank < static_cast<int>( m_groundSpots.size() ));
m_groundSpots[rank].smooth = smooth;
}
void CEngine::CreateGroundMark(glm::vec3 pos, float radius,
float delay1, float delay2, float delay3,
int dx, int dy, char* table)
{
m_groundMark.LoadDefault();
2012-10-20 14:34:22 +00:00
m_groundMark.draw = true;
2012-09-19 21:50:28 +00:00
m_groundMark.phase = ENG_GR_MARK_PHASE_INC;
m_groundMark.delay[0] = delay1;
m_groundMark.delay[1] = delay2;
m_groundMark.delay[2] = delay3;
m_groundMark.pos = pos;
m_groundMark.radius = radius;
m_groundMark.intensity = 0.0f;
m_groundMark.dx = dx;
m_groundMark.dy = dy;
m_groundMark.table = table;
}
2012-09-19 21:50:28 +00:00
void CEngine::DeleteGroundMark(int rank)
{
m_groundMark.LoadDefault();
}
2012-09-19 21:50:28 +00:00
void CEngine::ComputeDistance()
{
for (int i = 0; i < static_cast<int>( m_objects.size() ); i++)
{
if (! m_objects[i].used)
continue;
glm::vec3 v{};
v.x = m_eyePt.x - m_objects[i].transform[3][0];
v.y = m_eyePt.y - m_objects[i].transform[3][1];
v.z = m_eyePt.z - m_objects[i].transform[3][2];
m_objects[i].distance = glm::length(v);
}
}
2012-09-19 21:50:28 +00:00
void CEngine::UpdateGeometry()
{
if (! m_updateGeometry)
return;
for (auto& object : m_baseObjects)
{
if (!object.used)
continue;
object.bboxMin = { 0, 0, 0 };
object.bboxMax = { 0, 0, 0 };
for (auto& data : object.next)
{
for (const auto& vertex : data.vertices)
{
object.bboxMin.x = Math::Min(vertex.position.x, object.bboxMin.x);
object.bboxMin.y = Math::Min(vertex.position.y, object.bboxMin.y);
object.bboxMin.z = Math::Min(vertex.position.z, object.bboxMin.z);
object.bboxMax.x = Math::Max(vertex.position.x, object.bboxMax.x);
object.bboxMax.y = Math::Max(vertex.position.y, object.bboxMax.y);
object.bboxMax.z = Math::Max(vertex.position.z, object.bboxMax.z);
}
}
object.boundingSphere = Math::BoundingSphereForBox(object.bboxMin, object.bboxMax);
}
m_updateGeometry = false;
}
void CEngine::UpdateStaticBuffer(EngineBaseObjDataTier& p4)
{
PrimitiveType type;
if (p4.type == EngineTriangleType::TRIANGLES)
2021-12-05 11:26:34 +00:00
type = PrimitiveType::TRIANGLES;
else
2021-12-05 11:26:34 +00:00
type = PrimitiveType::TRIANGLE_STRIP;
if (p4.buffer == nullptr)
{
p4.buffer = m_device->CreateVertexBuffer(type, p4.vertices.data(), p4.vertices.size());
}
else
{
p4.buffer->SetType(type);
if (p4.buffer->Size() != p4.vertices.size())
p4.buffer->Resize(p4.vertices.size());
p4.buffer->SetData(p4.vertices.data(), 0, p4.vertices.size());
p4.buffer->Update();
}
p4.updateStaticBuffer = false;
}
void CEngine::UpdateStaticBuffers()
{
if (!m_updateStaticBuffers)
return;
m_updateStaticBuffers = false;
for (auto& object : m_baseObjects)
{
if (!object.used)
continue;
for (auto& data : object.next)
{
if (data.updateStaticBuffer)
UpdateStaticBuffer(data);
}
}
}
2012-09-19 21:50:28 +00:00
void CEngine::Update()
{
ComputeDistance();
UpdateGeometry();
UpdateStaticBuffers();
}
2021-12-24 19:25:38 +00:00
bool CEngine::DetectBBox(int objRank, const glm::vec2& mouse)
{
assert(objRank >= 0 && objRank < static_cast<int>(m_objects.size()));
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
return false;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
2012-12-27 13:18:16 +00:00
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
2021-12-24 19:25:38 +00:00
glm::vec2 min, max;
min.x = 1000000.0f;
min.y = 1000000.0f;
max.x = -1000000.0f;
max.y = -1000000.0f;
for (int i = 0; i < 8; i++)
{
glm::vec3 p{ 0, 0, 0 };
2012-12-27 13:18:16 +00:00
if ( i & (1<<0) ) p.x = p1.bboxMin.x;
else p.x = p1.bboxMax.x;
if ( i & (1<<1) ) p.y = p1.bboxMin.y;
else p.y = p1.bboxMax.y;
if ( i & (1<<2) ) p.z = p1.bboxMin.z;
else p.z = p1.bboxMax.z;
glm::vec3 pp{ 0, 0, 0 };
if ( TransformPoint(pp, objRank, p) )
{
if (pp.x < min.x) min.x = pp.x;
if (pp.x > max.x) max.x = pp.x;
if (pp.y < min.y) min.y = pp.y;
if (pp.y > max.y) max.y = pp.y;
}
}
return ( mouse.x >= min.x &&
mouse.x <= max.x &&
mouse.y >= min.y &&
mouse.y <= max.y );
}
int CEngine::DetectObject(const glm::vec2& mouse, glm::vec3& targetPos, bool terrain)
{
float min = 1000000.0f;
int nearest = -1;
glm::vec3 pos{ 0, 0, 0 };
for (int objRank = 0; objRank < static_cast<int>( m_objects.size() ); objRank++)
{
if (! m_objects[objRank].used)
continue;
2016-03-28 11:51:39 +00:00
if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN && !terrain)
continue;
if (! DetectBBox(objRank, mouse))
continue;
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
continue;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (! p1.used)
continue;
for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
{
EngineBaseObjDataTier& data = p1.next[l2];
if (data.type == EngineTriangleType::TRIANGLES)
{
for (int i = 0; i < static_cast<int>(data.vertices.size()); i += 3)
{
float dist = 0.0f;
if (DetectTriangle(mouse, &data.vertices[i], objRank, dist, pos) && dist < min)
{
min = dist;
nearest = objRank;
targetPos = pos;
}
}
}
else if (data.type == EngineTriangleType::SURFACE)
{
for (int i = 0; i < static_cast<int>(data.vertices.size()) - 2; i += 1)
{
float dist = 0.0f;
if (DetectTriangle(mouse, &data.vertices[i], objRank, dist, pos) && dist < min)
{
min = dist;
nearest = objRank;
targetPos = pos;
}
}
}
}
}
return nearest;
}
bool CEngine::DetectTriangle(const glm::vec2& mouse, Vertex3D* triangle, int objRank, float& dist, glm::vec3& pos)
{
assert(objRank >= 0 && objRank < static_cast<int>(m_objects.size()));
glm::vec3 p2D[3], p3D{ 0, 0, 0 };
for (int i = 0; i < 3; i++)
{
p3D.x = triangle[i].position.x;
p3D.y = triangle[i].position.y;
p3D.z = triangle[i].position.z;
if (! TransformPoint(p2D[i], objRank, p3D))
return false;
}
if (mouse.x < p2D[0].x &&
mouse.x < p2D[1].x &&
mouse.x < p2D[2].x)
return false;
if (mouse.x > p2D[0].x &&
mouse.x > p2D[1].x &&
mouse.x > p2D[2].x)
return false;
if (mouse.y < p2D[0].y &&
mouse.y < p2D[1].y &&
mouse.y < p2D[2].y)
return false;
if (mouse.y > p2D[0].y &&
mouse.y > p2D[1].y &&
mouse.y > p2D[2].y)
return false;
2021-12-24 19:25:38 +00:00
glm::vec2 a, b, c;
a.x = p2D[0].x;
a.y = p2D[0].y;
b.x = p2D[1].x;
b.y = p2D[1].y;
c.x = p2D[2].x;
c.y = p2D[2].y;
if (! Math::IsInsideTriangle(a, b, c, mouse))
return false;
auto matViewInverse = glm::inverse(m_matView);
auto matProjInverse = glm::inverse(m_matProj);
glm::vec3 a2 = Math::Transform(m_objects[objRank].transform, triangle[0].position);
glm::vec3 b2 = Math::Transform(m_objects[objRank].transform, triangle[1].position);
glm::vec3 c2 = Math::Transform(m_objects[objRank].transform, triangle[2].position);
glm::vec3 e = Math::Transform(matViewInverse, glm::vec3(0.0f, 0.0f, -1.0f));
glm::vec3 f = Math::Transform(matViewInverse, glm::vec3(
(mouse.x*2.0f-1.0f) * matProjInverse[0][0],
(mouse.y*2.0f-1.0f) * matProjInverse[1][1],
2016-03-28 11:51:39 +00:00
0.0f));
Math::Intersect(a2, b2, c2, e, f, pos);
dist = (p2D[0].z + p2D[1].z + p2D[2].z) / 3.0f;
return true;
}
//! Use only after world transform already set
bool CEngine::IsVisible(const glm::mat4& matrix, int objRank)
{
assert(objRank >= 0 && objRank < static_cast<int>(m_objects.size()));
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
return false;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
const auto& sphere = m_baseObjects[baseObjRank].boundingSphere;
if (ComputeSphereVisibility(matrix, sphere.pos, sphere.radius) == Gfx::FRUSTUM_PLANE_ALL)
{
m_objects[objRank].visible = true;
return true;
}
m_objects[objRank].visible = false;
return false;
}
int CEngine::ComputeSphereVisibility(const glm::mat4& m, const glm::vec3& center, float radius)
{
glm::vec3 vec[6];
float originPlane[6];
// Left plane
vec[0].x = m[0][3] + m[0][0];
vec[0].y = m[1][3] + m[1][0];
vec[0].z = m[2][3] + m[2][0];
float l1 = glm::length(vec[0]);
vec[0] = glm::normalize(vec[0]);
originPlane[0] = (m[3][3] + m[3][0]) / l1;
// Right plane
vec[1].x = m[0][3] - m[0][0];
vec[1].y = m[1][3] - m[1][0];
vec[1].z = m[2][3] - m[2][0];
float l2 = glm::length(vec[1]);
vec[1] = glm::normalize(vec[1]);
originPlane[1] = (m[3][3] - m[3][0]) / l2;
// Bottom plane
vec[2].x = m[0][3] + m[1][0];
vec[2].y = m[1][3] + m[1][1];
vec[2].z = m[3][2] + m[1][2];
float l3 = glm::length(vec[2]);
vec[2] = glm::normalize(vec[2]);
originPlane[2] = (m[3][3] + m[3][1]) / l3;
// Top plane
vec[3].x = m[0][3] - m[0][1];
vec[3].y = m[1][3] - m[1][1];
vec[3].z = m[2][3] - m[2][1];
float l4 = glm::length(vec[3]);
vec[3] = glm::normalize(vec[3]);
originPlane[3] = (m[3][3] - m[3][1]) / l4;
// Front plane
vec[4].x = m[0][3] + m[0][2];
vec[4].y = m[1][3] + m[1][2];
vec[4].z = m[2][3] + m[2][2];
float l5 = glm::length(vec[4]);
vec[4] = glm::normalize(vec[4]);
originPlane[4] = (m[3][3] + m[3][2]) / l5;
// Back plane
vec[5].x = m[0][3] - m[0][2];
vec[5].y = m[1][3] - m[1][2];
vec[5].z = m[2][3] - m[2][2];
float l6 = glm::length(vec[5]);
vec[5] = glm::normalize(vec[5]);
originPlane[5] = (m[3][3] - m[3][2]) / l6;
int result = 0;
if (InPlane(vec[0], originPlane[0], center, radius))
result |= FRUSTUM_PLANE_LEFT;
if (InPlane(vec[1], originPlane[1], center, radius))
result |= FRUSTUM_PLANE_RIGHT;
if (InPlane(vec[2], originPlane[2], center, radius))
result |= FRUSTUM_PLANE_BOTTOM;
if (InPlane(vec[3], originPlane[3], center, radius))
result |= FRUSTUM_PLANE_TOP;
if (InPlane(vec[4], originPlane[4], center, radius))
result |= FRUSTUM_PLANE_FRONT;
if (InPlane(vec[5], originPlane[5], center, radius))
result |= FRUSTUM_PLANE_BACK;
return result;
}
bool CEngine::InPlane(glm::vec3 normal, float originPlane, glm::vec3 center, float radius)
{
float distance = originPlane + glm::dot(normal, center);
if (distance < -radius)
return false;
return true;
}
bool CEngine::TransformPoint(glm::vec3& p2D, int objRank, glm::vec3 p3D)
{
assert(objRank >= 0 && objRank < static_cast<int>(m_objects.size()));
p3D = Math::Transform(m_objects[objRank].transform, p3D);
p3D = Math::Transform(m_matView, p3D);
if (p3D.z < 2.0f)
return false; // behind?
p2D.x = (p3D.x/p3D.z)*m_matProj[0][0];
p2D.y = (p3D.y/p3D.z)*m_matProj[1][1];
p2D.z = p3D.z;
p2D.x = (p2D.x+1.0f)/2.0f; // [-1..1] -> [0..1]
p2D.y = (p2D.y+1.0f)/2.0f;
return true;
}
/*******************************************************
Mode setting
*******************************************************/
void CEngine::SetViewParams(const glm::vec3 &eyePt, const glm::vec3 &lookatPt, const glm::vec3 &upVec)
{
m_eyePt = eyePt;
m_lookatPt = lookatPt;
m_eyeDirH = Math::RotateAngle(eyePt.x - lookatPt.x, eyePt.z - lookatPt.z);
m_eyeDirV = Math::RotateAngle(Math::DistanceProjected(eyePt, lookatPt), eyePt.y - lookatPt.y);
Math::LoadViewMatrix(m_matView, eyePt, lookatPt, upVec);
if (m_sound == nullptr)
m_sound = m_app->GetSound();
if (m_sound != nullptr)
m_sound->SetListener(eyePt, lookatPt);
}
Texture CEngine::CreateTexture(const std::string& texName, const TextureCreateParams& params, CImage* image)
{
if (texName.empty())
return Texture(); // invalid texture
if (m_texBlacklist.find(texName) != m_texBlacklist.end())
2012-09-19 21:50:28 +00:00
return Texture(); // invalid texture
2012-09-29 22:23:26 +00:00
Texture tex;
CImage img;
2012-09-29 22:23:26 +00:00
if (image == nullptr)
{
if (!img.Load(texName))
2012-09-29 22:23:26 +00:00
{
2014-05-18 10:12:47 +00:00
std::string error = img.GetError();
GetLogger()->Error("Couldn't load texture '%s': %s, blacklisting\n", texName.c_str(), error.c_str());
m_texBlacklist.insert(texName);
return Texture(); // invalid texture
}
image = &img;
2012-09-29 22:23:26 +00:00
}
tex = m_device->CreateTexture(image, params);
if (! tex.Valid())
{
GetLogger()->Error("Couldn't load texture '%s', blacklisting\n", texName.c_str());
m_texBlacklist.insert(texName);
return tex;
}
m_texNameMap[texName] = tex;
m_revTexNameMap[tex] = texName;
return tex;
}
2012-09-19 21:50:28 +00:00
Texture CEngine::LoadTexture(const std::string& name)
{
return LoadTexture(name, m_defaultTexParams);
}
2012-09-29 22:23:26 +00:00
Texture CEngine::LoadTexture(const std::string& name, CImage* image)
{
Texture tex = CreateTexture(name, m_defaultTexParams, image);
return tex;
}
2012-09-19 21:50:28 +00:00
Texture CEngine::LoadTexture(const std::string& name, const TextureCreateParams& params)
{
if (m_texBlacklist.find(name) != m_texBlacklist.end())
2012-09-19 21:50:28 +00:00
return Texture();
2012-09-19 21:50:28 +00:00
std::map<std::string, Texture>::iterator it = m_texNameMap.find(name);
if (it != m_texNameMap.end())
return (*it).second;
return CreateTexture(name, params);
}
2012-09-19 21:50:28 +00:00
bool CEngine::LoadAllTextures()
{
m_miceTexture = LoadTexture("textures/interface/mouse.png");
LoadTexture("textures/interface/button1.png");
LoadTexture("textures/interface/button2.png");
LoadTexture("textures/interface/button3.png");
LoadTexture("textures/interface/button4.png");
LoadTexture("textures/effect00.png");
LoadTexture("textures/effect01.png");
LoadTexture("textures/effect02.png");
2015-08-06 21:06:36 +00:00
LoadTexture("textures/effect03.png");
if (! m_backgroundName.empty())
{
TextureCreateParams params = m_defaultTexParams;
params.padToNearestPowerOfTwo = true;
m_backgroundTex = LoadTexture(m_backgroundName, params);
}
else
m_backgroundTex.SetInvalid();
if (! m_foregroundName.empty())
m_foregroundTex = LoadTexture(m_foregroundName);
else
m_foregroundTex.SetInvalid();
m_planet->LoadTexture();
bool ok = true;
for (int objRank = 0; objRank < static_cast<int>( m_objects.size() ); objRank++)
{
if (! m_objects[objRank].used)
continue;
bool terrain = false;
if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN)
terrain = true;
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
continue;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (! p1.used)
continue;
for (auto& data : p1.next)
{
if (!data.material.albedoTexture.empty())
{
if (terrain)
data.albedoTexture = LoadTexture("textures/" + data.material.albedoTexture, m_terrainTexParams);
else
data.albedoTexture = LoadTexture("textures/" + data.material.albedoTexture);
if (!data.albedoTexture.Valid())
ok = false;
}
if (!data.material.detailTexture.empty())
{
if (terrain)
data.detailTexture = LoadTexture("textures/" + data.material.detailTexture, m_terrainTexParams);
else
data.detailTexture = LoadTexture("textures/" + data.material.detailTexture);
if (!data.detailTexture.Valid())
ok = false;
}
}
}
return ok;
}
2021-12-24 19:25:38 +00:00
static bool IsExcludeColor(glm::vec2* exclude, int x, int y)
2012-09-29 21:37:38 +00:00
{
int i = 0;
while ( exclude[i+0].x != 0.0f || exclude[i+0].y != 0.0f ||
exclude[i+1].y != 0.0f || exclude[i+1].y != 0.0f )
{
if ( x >= static_cast<int>(exclude[i+0].x*256.0f) &&
x < static_cast<int>(exclude[i+1].x*256.0f) &&
y >= static_cast<int>(exclude[i+0].y*256.0f) &&
y < static_cast<int>(exclude[i+1].y*256.0f) )
return true; // exclude
2012-09-29 21:37:38 +00:00
i += 2;
}
return false; // point to include
}
2012-09-19 21:50:28 +00:00
void CEngine::DeleteTexture(const std::string& texName)
{
auto it = m_texNameMap.find(texName);
if (it == m_texNameMap.end())
return;
auto revIt = m_revTexNameMap.find((*it).second);
m_device->DestroyTexture((*it).second);
m_revTexNameMap.erase(revIt);
m_texNameMap.erase(it);
}
2012-09-19 21:50:28 +00:00
void CEngine::DeleteTexture(const Texture& tex)
{
if (! tex.Valid())
return;
auto revIt = m_revTexNameMap.find(tex);
if (revIt == m_revTexNameMap.end())
return;
m_device->DestroyTexture(tex);
auto it = m_texNameMap.find((*revIt).second);
m_revTexNameMap.erase(revIt);
m_texNameMap.erase(it);
}
void CEngine::CreateOrUpdateTexture(const std::string& texName, CImage* img)
{
auto it = m_texNameMap.find(texName);
if (it == m_texNameMap.end())
{
LoadTexture(texName, img);
}
else
{
m_device->UpdateTexture((*it).second, { 0, 0 }, img->GetData(), m_defaultTexParams.format);
}
}
void CEngine::FlushTextureCache()
{
m_device->DestroyAllTextures();
2015-08-31 10:06:14 +00:00
m_backgroundTex.SetInvalid();
m_foregroundTex.SetInvalid();
m_texNameMap.clear();
m_revTexNameMap.clear();
m_texBlacklist.clear();
m_firstGroundSpot = true;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetTerrainVision(float vision)
{
m_terrainVision = vision;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetFocus(float focus)
{
m_focus = focus;
m_size = m_app->GetVideoConfig().size;
2016-01-30 22:58:26 +00:00
float farPlane = m_deepView[0] * m_clippingDistance;
float aspect = static_cast<float>(m_size.x) / static_cast<float>(m_size.y);
2017-08-05 07:45:08 +00:00
// Compute H-FoV from V-FoV and aspect ratio.
m_hfov = 2.0f * atan(aspect * tan(focus / 2.0f));
2016-01-30 22:58:26 +00:00
Math::LoadProjectionMatrix(m_matProj, m_focus, aspect, 0.5f, farPlane);
}
2012-09-19 21:50:28 +00:00
float CEngine::GetFocus()
{
return m_focus;
}
2017-08-05 07:45:08 +00:00
float CEngine::GetVFovAngle()
{
return m_focus;
}
float CEngine::GetHFovAngle()
{
return m_hfov;
}
2015-05-27 20:12:02 +00:00
void CEngine::SetShadowColor(float value)
{
m_shadowColor = value;
}
float CEngine::GetShadowColor()
2015-05-27 20:12:02 +00:00
{
return m_shadowColor;
}
2015-06-15 16:41:31 +00:00
void CEngine::SetShadowRange(float value)
{
m_shadowRange = value;
}
float CEngine::GetShadowRange()
2015-06-15 16:41:31 +00:00
{
return m_shadowRange;
}
2015-06-21 22:56:47 +00:00
void CEngine::SetMultiSample(int value)
{
if(value == m_multisample) return;
2015-06-21 22:56:47 +00:00
m_multisample = value;
m_device->DeleteFramebuffer("multisample");
2015-06-21 22:56:47 +00:00
}
int CEngine::GetMultiSample()
2015-06-21 22:56:47 +00:00
{
return m_multisample;
}
void CEngine::SetTriplanarMode(bool enabled)
{
m_triplanarMode = enabled;
}
bool CEngine::GetTriplanarMode()
{
return m_triplanarMode;
}
void CEngine::SetTriplanarScale(float scale)
{
m_triplanarScale = scale;
}
float CEngine::GetTriplanarScale()
{
return m_triplanarScale;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetDirty(bool mode)
{
m_dirty = mode;
}
2012-09-19 21:50:28 +00:00
bool CEngine::GetDirty()
{
return m_dirty;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetFog(bool mode)
{
m_fog = mode;
}
2012-09-19 21:50:28 +00:00
bool CEngine::GetFog()
{
return m_fog;
}
void CEngine::SetSecondTexture(const std::string& texNum)
{
m_secondTex = texNum;
}
const std::string& CEngine::GetSecondTexture()
{
return m_secondTex;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetRankView(int rank)
{
if (rank < 0) rank = 0;
if (rank > 1) rank = 1;
if (m_rankView == 0 && rank == 1) // enters the water?
m_lightMan->AdaptLightColor(m_waterAddColor, +1.0f);
if (m_rankView == 1 && rank == 0) // out of the water?
m_lightMan->AdaptLightColor(m_waterAddColor, -1.0f);
m_rankView = rank;
}
2012-09-19 21:50:28 +00:00
int CEngine::GetRankView()
{
return m_rankView;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetDrawWorld(bool draw)
{
m_drawWorld = draw;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetDrawFront(bool draw)
{
m_drawFront = draw;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetAmbientColor(const Color& color, int rank)
{
m_ambientColor[rank] = color;
}
2012-09-19 21:50:28 +00:00
Color CEngine::GetAmbientColor(int rank)
{
return m_ambientColor[rank];
}
2012-09-19 21:50:28 +00:00
void CEngine::SetWaterAddColor(const Color& color)
{
m_waterAddColor = color;
}
2012-09-19 21:50:28 +00:00
Color CEngine::GetWaterAddColor()
{
return m_waterAddColor;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetFogColor(const Color& color, int rank)
{
m_fogColor[rank] = color;
}
2012-09-19 21:50:28 +00:00
Color CEngine::GetFogColor(int rank)
{
return m_fogColor[rank];
}
2012-09-19 21:50:28 +00:00
void CEngine::SetDeepView(float length, int rank, bool ref)
{
if (ref)
length *= m_clippingDistance;
m_deepView[rank] = length;
}
2012-09-19 21:50:28 +00:00
float CEngine::GetDeepView(int rank)
{
return m_deepView[rank];
}
2012-09-19 21:50:28 +00:00
void CEngine::SetFogStart(float start, int rank)
{
if (start < 0.0f)
m_fogStart[rank] = 0.0f;
else
m_fogStart[rank] = start;
}
2012-09-19 21:50:28 +00:00
float CEngine::GetFogStart(int rank)
{
return m_fogStart[rank];
}
2012-09-19 21:50:28 +00:00
void CEngine::SetBackground(const std::string& name, Color up, Color down,
Color cloudUp, Color cloudDown, bool full, bool scale)
{
if (m_backgroundTex.Valid() && name != m_backgroundName)
{
DeleteTexture(m_backgroundTex);
m_backgroundTex.SetInvalid();
}
m_backgroundName = name;
m_backgroundColorUp = up;
m_backgroundColorDown = down;
m_backgroundCloudUp = cloudUp;
m_backgroundCloudDown = cloudDown;
m_backgroundFull = full;
m_backgroundScale = scale;
if (! m_backgroundName.empty() && !m_backgroundTex.Valid())
{
TextureCreateParams params = m_defaultTexParams;
params.padToNearestPowerOfTwo = true;
m_backgroundTex = LoadTexture(m_backgroundName, params);
}
}
2012-09-19 21:50:28 +00:00
void CEngine::GetBackground(std::string& name, Color& up, Color& down,
Color& cloudUp, Color& cloudDown, bool &full, bool &scale)
{
name = m_backgroundName;
up = m_backgroundColorUp;
down = m_backgroundColorDown;
cloudUp = m_backgroundCloudUp;
cloudDown = m_backgroundCloudDown;
full = m_backgroundFull;
scale = m_backgroundScale;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetForegroundName(const std::string& name)
{
if (m_foregroundTex.Valid() && name != m_foregroundName)
{
DeleteTexture(m_foregroundTex);
m_foregroundTex.SetInvalid();
}
m_foregroundName = name;
if (! m_foregroundName.empty() && !m_foregroundTex.Valid())
m_foregroundTex = LoadTexture(m_foregroundName);
}
2012-09-19 21:50:28 +00:00
void CEngine::SetOverFront(bool front)
{
m_overFront = front;
}
void CEngine::SetOverColor(const Color& color, TransparencyMode mode)
{
m_overColor = color;
m_overMode = mode;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetParticleDensity(float value)
{
if (value < 0.0f) value = 0.0f;
if (value > 2.0f) value = 2.0f;
m_particleDensity = value;
}
2012-09-19 21:50:28 +00:00
float CEngine::GetParticleDensity()
{
return m_particleDensity;
}
2012-09-19 21:50:28 +00:00
float CEngine::ParticleAdapt(float factor)
{
if (m_particleDensity == 0.0f)
return 1000000.0f;
return factor / m_particleDensity;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetClippingDistance(float value)
{
if (value < 0.5f) value = 0.5f;
if (value > 2.0f) value = 2.0f;
m_clippingDistance = value;
}
2012-09-19 21:50:28 +00:00
float CEngine::GetClippingDistance()
{
return m_clippingDistance;
}
void CEngine::SetTextureFilterMode(TexFilter value)
{
if(m_defaultTexParams.filter == value && m_terrainTexParams.filter == value) return;
m_defaultTexParams.filter = m_terrainTexParams.filter = value;
m_defaultTexParams.mipmap = m_terrainTexParams.mipmap = (value == TEX_FILTER_TRILINEAR);
2015-09-25 09:11:35 +00:00
ReloadAllTextures();
}
TexFilter CEngine::GetTextureFilterMode()
{
return m_terrainTexParams.filter;
}
void CEngine::SetTextureMipmapLevel(int value)
{
if (value < 1) value = 1;
if (value > 16) value = 16;
if(m_textureMipmapLevel == value) return;
m_textureMipmapLevel = value;
2015-09-25 09:11:35 +00:00
ReloadAllTextures();
}
int CEngine::GetTextureMipmapLevel()
{
return m_textureMipmapLevel;
}
void CEngine::SetTextureAnisotropyLevel(int value)
{
if (value < 1) value = 1;
if (value > 16) value = 16;
if(m_textureAnisotropy == value) return;
m_textureAnisotropy = value;
2015-09-25 09:11:35 +00:00
ReloadAllTextures();
}
int CEngine::GetTextureAnisotropyLevel()
{
return m_textureAnisotropy;
}
bool CEngine::IsShadowMappingSupported()
{
return true;
}
void CEngine::SetShadowMapping(bool value)
{
if(!IsShadowMappingSupported()) value = false;
if(value == m_shadowMapping) return;
m_shadowMapping = value;
if(!value)
{
m_device->DeleteFramebuffer("shadow");
m_device->DestroyTexture(m_shadowMap);
m_shadowMap.id = 0;
}
}
bool CEngine::GetShadowMapping()
{
return m_shadowMapping;
}
void CEngine::SetShadowMappingOffscreen(bool value)
{
if(!m_device->IsFramebufferSupported()) value = false;
if(value == m_offscreenShadowRendering) return;
m_offscreenShadowRendering = value;
if(value)
{
m_device->DestroyTexture(m_shadowMap);
m_shadowMap.id = 0;
}
else
{
m_device->DeleteFramebuffer("shadow");
m_shadowMap.id = 0;
}
}
bool CEngine::GetShadowMappingOffscreen()
{
return m_offscreenShadowRendering;
}
void CEngine::SetShadowMappingOffscreenResolution(int resolution)
{
resolution = Math::Min(resolution, m_device->GetMaxTextureSize());
if(resolution == m_offscreenShadowRenderingResolution) return;
m_offscreenShadowRenderingResolution = resolution;
m_device->DeleteFramebuffer("shadow");
m_shadowMap.id = 0;
}
int CEngine::GetShadowMappingOffscreenResolution()
{
return m_offscreenShadowRenderingResolution;
}
bool CEngine::IsShadowMappingQualitySupported()
{
return true;
}
void CEngine::SetShadowMappingQuality(bool value)
{
if(!IsShadowMappingQualitySupported()) value = false;
m_qualityShadows = value;
}
bool CEngine::GetShadowMappingQuality()
{
return m_qualityShadows;
}
void CEngine::SetTerrainShadows(bool value)
{
m_terrainShadows = value;
}
bool CEngine::GetTerrainShadows()
{
return m_terrainShadows;
}
2018-07-05 17:43:56 +00:00
void CEngine::SetVSync(int value)
{
if (value < -1) value = -1;
if (value > 1) value = 1;
if(m_vsync == value) return;
m_vsync = value;
}
int CEngine::GetVSync()
{
return m_vsync;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetBackForce(bool present)
{
m_backForce = present;
}
2012-09-19 21:50:28 +00:00
bool CEngine::GetBackForce()
{
return m_backForce;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetLightMode(bool present)
{
m_lightMode = present;
}
2012-09-19 21:50:28 +00:00
bool CEngine::GetLightMode()
{
return m_lightMode;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetEditIndentMode(bool autoIndent)
{
m_editIndentMode = autoIndent;
}
2012-09-19 21:50:28 +00:00
bool CEngine::GetEditIndentMode()
{
return m_editIndentMode;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetEditIndentValue(int value)
{
m_editIndentValue = value;
}
2012-09-19 21:50:28 +00:00
int CEngine::GetEditIndentValue()
{
return m_editIndentValue;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetTracePrecision(float factor)
{
m_tracePrecision = factor;
}
2012-09-19 21:50:28 +00:00
float CEngine::GetTracePrecision()
{
return m_tracePrecision;
}
2012-09-19 21:50:28 +00:00
void CEngine::SetMouseType(EngineMouseType type)
{
m_mouseType = type;
}
2012-09-19 21:50:28 +00:00
EngineMouseType CEngine::GetMouseType()
{
return m_mouseType;
}
void CEngine::SetPauseBlurEnabled(bool enable)
{
m_pauseBlurEnabled = enable;
}
bool CEngine::GetPauseBlurEnabled()
{
return m_pauseBlurEnabled;
}
const glm::mat4& CEngine::GetMatView()
{
return m_matView;
}
const glm::mat4& CEngine::GetMatProj()
{
return m_matProj;
}
glm::vec3 CEngine::GetEyePt()
{
return m_eyePt;
}
glm::vec3 CEngine::GetLookatPt()
{
return m_lookatPt;
}
2012-09-19 21:50:28 +00:00
float CEngine::GetEyeDirH()
{
return m_eyeDirH;
}
2012-09-19 21:50:28 +00:00
float CEngine::GetEyeDirV()
{
return m_eyeDirV;
}
bool CEngine::IsVisiblePoint(const glm::vec3 &pos)
{
return glm::distance(m_eyePt, pos) <= (m_deepView[0] * m_clippingDistance);
}
2012-09-19 21:50:28 +00:00
void CEngine::ApplyChange()
{
SetFocus(m_focus);
// recapture 3D scene
if (m_worldCaptured)
{
m_captureWorld = true;
m_worldCaptured = false;
}
}
/*******************************************************
Rendering
*******************************************************/
/**
This function sets up render states, clears the
viewport, and renders the scene. */
2012-09-19 21:50:28 +00:00
void CEngine::Render()
{
2015-08-09 13:20:35 +00:00
m_fpsCounter++;
m_currentFrameTime = m_systemUtils->GetCurrentTimeStamp();
float diff = TimeUtils::Diff(m_lastFrameTime, m_currentFrameTime, TimeUnit::SECONDS);
2015-08-09 13:20:35 +00:00
if (diff > 1.0f)
{
m_lastFrameTime = m_currentFrameTime;
2015-08-09 13:20:35 +00:00
m_fps = m_fpsCounter / diff;
m_fpsCounter = 0;
}
if (! m_render)
return;
m_statisticTriangle = 0;
m_lightMan->UpdateLights();
2012-09-19 21:50:28 +00:00
Color color;
if (m_cloud->GetLevel() != 0.0f) // clouds?
color = m_backgroundCloudDown;
else
color = m_backgroundColorDown;
m_device->SetClearColor(color);
2015-06-21 22:56:47 +00:00
// Begin the scene
m_device->SetDepthMask(true);
2015-06-25 09:47:42 +00:00
m_device->BeginScene();
// use currently captured scene for world
if (m_worldCaptured && !m_captureWorld)
{
DrawCaptured3DScene();
}
else
{
// Render shadow map
if (m_drawWorld && m_shadowMapping)
RenderShadowMap();
UseMSAA(true);
DrawBackground(); // draws the background
if (m_drawWorld)
Draw3DScene();
UseMSAA(false);
// marked to capture currently rendered world
if (m_captureWorld)
{
Capture3DScene();
m_device->Clear();
DrawCaptured3DScene();
}
}
2016-06-19 09:45:56 +00:00
CProfiler::StartPerformanceCounter(PCNT_RENDER_INTERFACE);
2015-09-12 09:51:03 +00:00
DrawInterface();
CProfiler::StopPerformanceCounter(PCNT_RENDER_INTERFACE);
// End the scene
m_device->EndScene();
}
void CEngine::Draw3DScene()
2016-06-19 09:45:56 +00:00
{
if (!m_worldCaptured)
2016-06-19 09:45:56 +00:00
{
if (m_capturedWorldTexture.Valid())
2016-06-19 09:45:56 +00:00
{
m_device->DestroyTexture(m_capturedWorldTexture);
m_capturedWorldTexture = Texture();
2016-06-19 09:45:56 +00:00
}
}
m_device->SetDepthTest(false);
2015-06-25 09:47:42 +00:00
UpdateGroundSpotTextures();
DrawPlanet(); // draws the planets
m_cloud->Draw(); // draws the clouds
// Display the objects
//m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true);
//m_device->SetRenderState(RENDER_STATE_LIGHTING, true);
//m_device->SetRenderState(RENDER_STATE_FOG, true);
2016-01-30 23:38:24 +00:00
float fogStart = m_deepView[m_rankView] * m_fogStart[m_rankView] * m_clippingDistance;
float fogEnd = m_deepView[m_rankView] * m_clippingDistance;
2017-05-18 17:03:19 +00:00
// TODO: This causes a rendering artifact and I can't see anything that breaks if you just comment it out
// So I'll just leave it like that for now ~krzys_h
//m_water->DrawBack(); // draws water background
CProfiler::StartPerformanceCounter(PCNT_RENDER_TERRAIN);
2012-10-25 21:29:49 +00:00
// Draw terrain
//m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN);
Gfx::ShadowParam shadowParams[4];
for (int i = 0; i < m_shadowRegions; i++)
{
shadowParams[i].matrix = m_shadowParams[i].transform;
shadowParams[i].uv_offset = m_shadowParams[i].offset;
shadowParams[i].uv_scale = m_shadowParams[i].scale;
}
auto terrainRenderer = m_device->GetTerrainRenderer();
terrainRenderer->Begin();
terrainRenderer->SetProjectionMatrix(m_matProj);
terrainRenderer->SetViewMatrix(m_matView);
terrainRenderer->SetShadowMap(m_shadowMap);
terrainRenderer->SetLight(glm::vec4(1.0, 1.0, -1.0, 0.0), 1.0f, glm::vec3(1.0));
if (m_shadowMapping)
terrainRenderer->SetShadowParams(m_shadowRegions, shadowParams);
else
terrainRenderer->SetShadowParams(0, nullptr);
Color fogColor = m_fogColor[m_rankView];
terrainRenderer->SetFog(fogStart, fogEnd, { fogColor.r, fogColor.g, fogColor.b });
glm::mat4 scale = glm::mat4(1.0f);
scale[2][2] = -1.0f;
auto projectionViewMatrix = m_matProj * scale;
projectionViewMatrix = projectionViewMatrix * m_matView;
for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
{
if (! m_objects[objRank].used)
continue;
2012-08-26 23:02:25 +00:00
if (m_objects[objRank].type != ENG_OBJTYPE_TERRAIN)
continue;
if (! m_objects[objRank].drawWorld)
continue;
auto combinedMatrix = projectionViewMatrix * m_objects[objRank].transform;
if (! IsVisible(combinedMatrix, objRank))
continue;
int baseObjRank = m_objects[objRank].baseObjRank;
if (baseObjRank == -1)
continue;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
2012-12-28 12:23:49 +00:00
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (! p1.used)
continue;
2012-12-27 13:18:16 +00:00
for (auto& data : p1.next)
{
terrainRenderer->SetPrimaryTexture(data.albedoTexture);
terrainRenderer->SetSecondaryTexture(data.detailTexture);
terrainRenderer->DrawObject(m_objects[objRank].transform, data.buffer);
}
}
terrainRenderer->End();
2015-08-14 11:36:23 +00:00
// Draws the old-style shadow spots, if shadow mapping disabled
if (!m_shadowMapping)
DrawShadowSpots();
CProfiler::StopPerformanceCounter(PCNT_RENDER_TERRAIN);
2012-10-25 21:29:49 +00:00
// Draw other objects
2012-10-25 21:29:49 +00:00
CProfiler::StartPerformanceCounter(PCNT_RENDER_OBJECTS);
auto objectRenderer = m_device->GetObjectRenderer();
objectRenderer->Begin();
objectRenderer->SetProjectionMatrix(m_matProj);
objectRenderer->SetViewMatrix(m_matView);
objectRenderer->SetShadowMap(m_shadowMap);
objectRenderer->SetLighting(true);
objectRenderer->SetLight(glm::vec4(1.0, 1.0, -1.0, 0.0), 0.8f, glm::vec3(1.0));
objectRenderer->SetSky(Color(1.0, 1.0, 1.0), 0.5f);
objectRenderer->SetTransparency(TransparencyMode::NONE);
objectRenderer->SetFog(fogStart, fogEnd, { fogColor.r, fogColor.g, fogColor.b });
objectRenderer->SetAlphaScissor(0.0f);
if (m_shadowMapping && m_qualityShadows)
objectRenderer->SetShadowParams(m_shadowRegions, shadowParams);
else
objectRenderer->SetShadowParams(0, nullptr);
objectRenderer->SetTriplanarMode(m_triplanarMode);
objectRenderer->SetTriplanarScale(m_triplanarScale);
bool transparent = false;
for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
{
if (! m_objects[objRank].used)
continue;
if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN)
continue;
if (! m_objects[objRank].drawWorld)
continue;
auto combinedMatrix = projectionViewMatrix * m_objects[objRank].transform;
if (! IsVisible(combinedMatrix, objRank))
continue;
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
continue;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (! p1.used)
continue;
objectRenderer->SetModelMatrix(m_objects[objRank].transform);
//m_lightMan->UpdateDeviceLights(m_objects[objRank].type);
for (auto& data : p1.next)
{
if (m_objects[objRank].ghost) // transparent ?
{
transparent = true;
continue;
}
if (data.material.alphaMode != AlphaMode::NONE)
{
objectRenderer->SetAlphaScissor(data.material.alphaThreshold);
}
else
{
objectRenderer->SetAlphaScissor(0.0f);
}
objectRenderer->SetAlbedoColor(data.material.albedoColor);
objectRenderer->SetAlbedoTexture(data.albedoTexture);
objectRenderer->SetDetailTexture(data.detailTexture);
objectRenderer->SetEmissiveColor(data.material.emissiveColor);
objectRenderer->SetEmissiveTexture(data.emissiveTexture);
objectRenderer->SetMaterialParams(data.material.roughness, data.material.metalness);
objectRenderer->SetMaterialTexture(data.materialTexture);
objectRenderer->SetCullFace(data.material.cullFace);
objectRenderer->SetUVTransform(data.uvOffset, data.uvScale);
objectRenderer->DrawObject(data.buffer);
}
}
objectRenderer->End();
objectRenderer->Begin();
objectRenderer->SetLighting(false);
objectRenderer->SetDepthMask(false);
2022-01-19 16:14:27 +00:00
objectRenderer->SetTransparency(TransparencyMode::BLACK);
objectRenderer->SetAlphaScissor(0.0f);
objectRenderer->SetCullFace(CullFace::NONE);
// Draw transparent objects
if (transparent)
{
Color tColor = Color(68.0f / 255.0f, 68.0f / 255.0f, 68.0f / 255.0f, 255.0f);
for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
{
if (! m_objects[objRank].used)
continue;
if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN)
continue;
if (! m_objects[objRank].drawWorld)
continue;
if (!m_objects[objRank].ghost)
continue;
auto combinedMatrix = projectionViewMatrix * m_objects[objRank].transform;
if (! IsVisible(combinedMatrix, objRank))
continue;
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
continue;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (! p1.used)
continue;
objectRenderer->SetModelMatrix(m_objects[objRank].transform);
for (auto& data : p1.next)
{
objectRenderer->SetAlbedoColor(tColor);
objectRenderer->SetAlbedoTexture(data.albedoTexture);
objectRenderer->SetDetailTexture(data.detailTexture);
objectRenderer->SetUVTransform(data.uvOffset, data.uvScale);
objectRenderer->DrawObject(data.buffer);
}
}
}
objectRenderer->End();
CProfiler::StopPerformanceCounter(PCNT_RENDER_OBJECTS);
2012-10-25 21:29:49 +00:00
m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN);
if (m_debugDumpLights)
{
m_debugDumpLights = false;
m_lightMan->DebugDumpLights();
}
CProfiler::StartPerformanceCounter(PCNT_RENDER_WATER);
objectRenderer->Begin();
objectRenderer->SetProjectionMatrix(m_matProj);
objectRenderer->SetViewMatrix(m_matView);
objectRenderer->SetShadowMap(m_shadowMap);
objectRenderer->SetLighting(true);
objectRenderer->SetLight(glm::vec4(1.0, 1.0, -1.0, 0.0), 1.0f, glm::vec3(1.0));
objectRenderer->SetTransparency(TransparencyMode::NONE);
objectRenderer->SetFog(fogStart, fogEnd, { fogColor.r, fogColor.g, fogColor.b });
objectRenderer->SetAlphaScissor(0.0f);
objectRenderer->SetShadowParams(m_shadowRegions, shadowParams);
m_water->DrawSurf(); // draws water surface
CProfiler::StopPerformanceCounter(PCNT_RENDER_WATER);
RenderPendingDebugDraws();
2016-03-28 18:25:07 +00:00
if (m_debugGoto)
{
glm::mat4 worldMatrix = glm::mat4(1.0f);
objectRenderer->SetTransparency(TransparencyMode::NONE);
objectRenderer->SetLighting(false);
objectRenderer->SetModelMatrix(glm::mat4(1.0f));
2016-03-28 18:25:07 +00:00
for (const auto& line : m_displayGoto)
{
objectRenderer->DrawPrimitive(PrimitiveType::LINE_STRIP, line.size(), line.data());
2016-03-28 18:25:07 +00:00
}
}
m_displayGoto.clear();
objectRenderer->End();
auto particleRenderer = m_device->GetParticleRenderer();
particleRenderer->Begin();
particleRenderer->SetProjectionMatrix(m_matProj);
particleRenderer->SetViewMatrix(m_matView);
CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE_WORLD);
2012-09-19 21:50:28 +00:00
m_particle->DrawParticle(SH_WORLD); // draws the particles of the 3D world
CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE_WORLD);
2012-10-25 21:29:49 +00:00
m_lightning->Draw(); // draws lightning
2012-09-20 22:01:03 +00:00
particleRenderer->End();
DrawForegroundImage(); // draws the foreground
2012-09-20 22:01:03 +00:00
if (! m_overFront) DrawOverColor(); // draws the foreground color
}
void CEngine::Capture3DScene()
{
// destroy existing texture
if (m_capturedWorldTexture.Valid())
{
m_device->DestroyTexture(m_capturedWorldTexture);
m_capturedWorldTexture = Texture();
}
// obtain pixels from screen
int width = m_size.x;
int height = m_size.y;
auto pixels = m_device->GetFrameBufferPixels();
unsigned char* data = reinterpret_cast<unsigned char*>(pixels->GetPixelsData());
// calculate 2nd mipmap
int newWidth = width / 4;
int newHeight = height / 4;
std::unique_ptr<unsigned char[]> mipmap = std::make_unique<unsigned char[]>(4 * newWidth * newHeight);
for (int x = 0; x < newWidth; x++)
{
for (int y = 0; y < newHeight; y++)
{
float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
int index = 4 * ((4 * x + i) + width * (4 * y + j));
for (int k = 0; k < 4; k++)
color[k] += data[index + k];
}
}
int index = 4 * (x + newWidth * y);
for (int k = 0; k < 4; k++)
{
mipmap[index + k] = static_cast<unsigned char>(color[k] * (1.0f / 16.0f));
}
}
}
// calculate Gaussian blur
std::unique_ptr<unsigned char[]> blured = std::make_unique<unsigned char[]>(4 * newWidth * newHeight);
float matrix[7][7] =
{
{ 0.00000067f, 0.00002292f, 0.00019117f, 0.00038771f, 0.00019117f, 0.00002292f, 0.00000067f },
{ 0.00002292f, 0.00078634f, 0.00655965f, 0.01330373f, 0.00655965f, 0.00078633f, 0.00002292f },
{ 0.00019117f, 0.00655965f, 0.05472157f, 0.11098164f, 0.05472157f, 0.00655965f, 0.00019117f },
{ 0.00038771f, 0.01330373f, 0.11098164f, 0.22508352f, 0.11098164f, 0.01330373f, 0.00038771f },
{ 0.00019117f, 0.00655965f, 0.05472157f, 0.11098164f, 0.05472157f, 0.00655965f, 0.00019117f },
{ 0.00002292f, 0.00078633f, 0.00655965f, 0.01330373f, 0.00655965f, 0.00078633f, 0.00002292f },
{ 0.00000067f, 0.00002292f, 0.00019117f, 0.00038771f, 0.00019117f, 0.00002292f, 0.00000067f }
};
for (int x = 0; x < newWidth; x++)
{
for (int y = 0; y < newHeight; y++)
{
float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
for (int i = -3; i <= 3; i++)
{
for (int j = -3; j <= 3; j++)
{
int xp = glm::clamp(x + i, 0, newWidth - 1);
int yp = glm::clamp(y + j, 0, newHeight - 1);
float weight = matrix[i + 3][j + 3];
int index = 4 * (newWidth * yp + xp);
for (int k = 0; k < 4; k++)
color[k] += weight * mipmap[index + k];
}
}
int index = 4 * (newWidth * y + x);
for (int k = 0; k < 4; k++)
{
float value = glm::clamp(color[k], 0.0f, 255.0f);
blured[index + k] = static_cast<unsigned char>(value);
}
}
}
// create SDL surface and final texture
ImageData image;
image.surface = SDL_CreateRGBSurfaceFrom(blured.get(), newWidth, newHeight, 32, 0, 0, 0, 0, 0xFF000000);
TextureCreateParams params;
params.filter = TEX_FILTER_BILINEAR;
params.format = TEX_IMG_RGBA;
params.mipmap = false;
m_capturedWorldTexture = m_device->CreateTexture(&image, params);
SDL_FreeSurface(image.surface);
m_captureWorld = false;
m_worldCaptured = true;
}
void CEngine::DrawCaptured3DScene()
{
m_device->SetDepthTest(false);
auto renderer = m_device->GetUIRenderer();
renderer->SetTexture(m_capturedWorldTexture);
renderer->SetTransparency(TransparencyMode::NONE);
auto vertices = renderer->BeginPrimitive(Gfx::PrimitiveType::TRIANGLE_STRIP, 4);
vertices[0] = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
vertices[1] = { { 1.0f, 0.0f }, { 1.0f, 0.0f } };
vertices[2] = { { 0.0f, 1.0f }, { 0.0f, 1.0f } };
vertices[3] = { { 1.0f, 1.0f }, { 1.0f, 1.0f } };
renderer->EndPrimitive();
}
void CEngine::RenderDebugSphere(const Math::Sphere& sphere, const glm::mat4& transform, const Gfx::Color& color)
{
static constexpr int LONGITUDE_DIVISIONS = 16;
static constexpr int LATITUDE_DIVISIONS = 8;
static constexpr int NUM_LINE_STRIPS = 2 + LONGITUDE_DIVISIONS + LATITUDE_DIVISIONS;
static constexpr int VERTS_IN_LINE_STRIP = 32;
2016-03-20 18:17:15 +00:00
static std::array<glm::vec3, NUM_LINE_STRIPS * VERTS_IN_LINE_STRIP> verticesTemplate = []
{
std::array<glm::vec3, NUM_LINE_STRIPS * VERTS_IN_LINE_STRIP> vertices;
auto SpherePoint = [&](float latitude, float longitude)
{
float latitudeAngle = (latitude - 0.5f) * 2.0f * Math::PI;
float longitudeAngle = longitude * 2.0f * Math::PI;
return glm::vec3(sinf(latitudeAngle) * cosf(longitudeAngle),
cosf(latitudeAngle),
sinf(latitudeAngle) * sinf(longitudeAngle));
};
auto vert = vertices.begin();
2016-03-20 18:17:15 +00:00
for (int longitudeDivision = 0; longitudeDivision <= LONGITUDE_DIVISIONS; ++longitudeDivision)
{
for (int segment = 0; segment < VERTS_IN_LINE_STRIP; ++segment)
{
float latitude = static_cast<float>(segment) / VERTS_IN_LINE_STRIP;
float longitude = static_cast<float>(longitudeDivision) / (LONGITUDE_DIVISIONS);
*vert++ = SpherePoint(latitude, longitude);
}
}
for (int latitudeDivision = 0; latitudeDivision <= LATITUDE_DIVISIONS; ++latitudeDivision)
{
for (int segment = 0; segment < VERTS_IN_LINE_STRIP; ++segment)
{
float latitude = static_cast<float>(latitudeDivision + 1) / (LATITUDE_DIVISIONS + 2);
float longitude = static_cast<float>(segment) / VERTS_IN_LINE_STRIP;
*vert++ = SpherePoint(latitude, longitude);
}
}
return vertices;
}();
const int firstDraw = m_pendingDebugDraws.counts.size();
const int firstVert = m_pendingDebugDraws.vertices.size();
m_pendingDebugDraws.counts.resize(m_pendingDebugDraws.counts.size() + NUM_LINE_STRIPS);
m_pendingDebugDraws.vertices.resize(m_pendingDebugDraws.vertices.size() + verticesTemplate.size());
2016-03-20 18:17:15 +00:00
for (int i = 0; i < NUM_LINE_STRIPS; ++i)
{
m_pendingDebugDraws.counts[i + firstDraw] = VERTS_IN_LINE_STRIP;
}
2016-03-20 18:17:15 +00:00
for (std::size_t i = 0; i < verticesTemplate.size(); ++i)
{
auto pos = Math::Transform(transform, sphere.pos + verticesTemplate[i] * sphere.radius);
m_pendingDebugDraws.vertices[i + firstVert] = Vertex3D{ pos, {}, color };
}
}
void CEngine::RenderDebugBox(const glm::vec3& mins, const glm::vec3& maxs, const glm::mat4& transform, const Gfx::Color& color)
{
static constexpr int NUM_LINE_STRIPS = 4;
static constexpr int VERTS_IN_LINE_STRIP = 4;
const int firstDraw = m_pendingDebugDraws.counts.size();
const int firstVert = m_pendingDebugDraws.vertices.size();
m_pendingDebugDraws.counts.resize(m_pendingDebugDraws.counts.size() + NUM_LINE_STRIPS);
m_pendingDebugDraws.vertices.resize(m_pendingDebugDraws.vertices.size() + NUM_LINE_STRIPS * VERTS_IN_LINE_STRIP);
for (int i = 0; i < NUM_LINE_STRIPS; ++i)
{
m_pendingDebugDraws.counts[i + firstDraw] = NUM_LINE_STRIPS;
}
auto vert = m_pendingDebugDraws.vertices.begin() + firstVert;
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{mins.x, mins.y, mins.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{maxs.x, mins.y, mins.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{maxs.x, maxs.y, mins.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{maxs.x, maxs.y, maxs.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{mins.x, mins.y, maxs.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{mins.x, mins.y, mins.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{mins.x, maxs.y, mins.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{maxs.x, maxs.y, mins.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{maxs.x, mins.y, maxs.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{mins.x, mins.y, maxs.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{mins.x, maxs.y, maxs.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{mins.x, maxs.y, mins.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{maxs.x, mins.y, mins.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{maxs.x, mins.y, maxs.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{maxs.x, maxs.y, maxs.z}), {}, color };
*vert++ = Vertex3D{ Math::Transform(transform, glm::vec3{mins.x, maxs.y, maxs.z}), {}, color };
}
void CEngine::RenderPendingDebugDraws()
{
if (m_pendingDebugDraws.counts.empty()) return;
auto renderer = m_device->GetObjectRenderer();
renderer->SetTransparency(TransparencyMode::NONE);
renderer->SetLighting(false);
renderer->SetModelMatrix(glm::mat4(1.0f));
renderer->SetAlbedoTexture(Texture{});
renderer->SetDetailTexture(Texture{});
renderer->DrawPrimitives(PrimitiveType::LINE_STRIP,
m_pendingDebugDraws.counts.size(),
m_pendingDebugDraws.counts.data(),
m_pendingDebugDraws.vertices.data());
m_pendingDebugDraws.counts.clear();
m_pendingDebugDraws.vertices.clear();
}
void CEngine::RenderShadowMap()
{
m_shadowMapping = m_shadowMapping && m_device->IsShadowMappingSupported();
m_offscreenShadowRendering = m_offscreenShadowRendering && m_device->IsFramebufferSupported();
m_offscreenShadowRenderingResolution = Math::Min(m_offscreenShadowRenderingResolution, m_device->GetMaxTextureSize());
if (!m_shadowMapping) return;
CProfiler::StartPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
m_shadowRegions = 4;
2015-06-21 16:48:31 +00:00
m_shadowParams[0].range = 32.0;
m_shadowParams[0].offset = { 0.0, 0.0 };
m_shadowParams[0].scale = { 0.5, 0.5 };
2015-06-21 16:48:31 +00:00
m_shadowParams[1].range = 96.0;
m_shadowParams[1].offset = { 0.5, 0.0 };
m_shadowParams[1].scale = { 0.5, 0.5 };
m_shadowParams[2].range = 256.0;
m_shadowParams[2].offset = { 0.0, 0.5 };
m_shadowParams[2].scale = { 0.5, 0.5 };
m_shadowParams[3].range = 1024.0;
m_shadowParams[3].offset = { 0.5, 0.5 };
m_shadowParams[3].scale = { 0.5, 0.5 };
// If no shadow map texture exists, create it
if (m_shadowMap.id == 0)
{
m_shadowMap = m_device->CreateDepthTexture(
m_offscreenShadowRenderingResolution,
m_offscreenShadowRenderingResolution,
32);
GetLogger()->Info("Created shadow map texture: %dx%d, depth %d\n",
m_shadowMap.size.x, m_shadowMap.size.y, 32);
}
auto renderer = m_device->GetShadowRenderer();
renderer->Begin();
renderer->SetShadowMap(m_shadowMap);
renderer->SetShadowRegion({ 0.0, 0.0 }, { 1.0, 1.0 });
m_device->Clear();
for (int region = 0; region < m_shadowRegions; region++)
2015-06-15 16:41:31 +00:00
{
renderer->SetShadowRegion(
m_shadowParams[region].offset,
m_shadowParams[region].scale);
// recompute matrices
glm::vec3 worldUp(0.0f, 1.0f, 0.0f);
glm::vec3 lightDir = glm::vec3(1.0f, 2.0f, -1.0f);
glm::vec3 dir = m_lookatPt - m_eyePt;
dir.y = 0.0f;
dir = glm::normalize(dir);
float range = m_shadowParams[region].range;
float dist = range;
float depth = 200.0f;
2015-05-21 16:18:46 +00:00
if (dist < 0.5f)
{
float scale = log(m_shadowMap.size.x) / log(2.0f) - 6.5f;
dist = 75.0f * scale;
}
glm::vec3 pos = m_lookatPt + 0.25f * dist * dir;
{
// To prevent 'shadow shimmering', we ensure that the position only moves in texel-sized
// increments. To do this we transform the position to a space where the light's forward/right/up
// axes are aligned with the x/y/z axes (not necessarily in that order, and +/- signs don't matter).
glm::mat4 lightRotation;
Math::LoadViewMatrix(lightRotation, glm::vec3{0, 0, 0}, lightDir, worldUp);
pos = Math::Transform(lightRotation, pos);
// ...then we round to the nearest worldUnitsPerTexel:
const float worldUnitsPerTexel = (dist * 2.0f) / m_shadowMap.size.x;
pos /= worldUnitsPerTexel;
pos.x = round(pos.x);
pos.y = round(pos.y);
pos.z = round(pos.z);
pos *= worldUnitsPerTexel;
// ...and convert back to world space.
pos = Math::Transform(glm::inverse(lightRotation), pos);
}
glm::vec3 lookAt = pos - lightDir;
Math::LoadOrthoProjectionMatrix(m_shadowProjMat, -dist, dist, -dist, dist, -depth, depth);
Math::LoadViewMatrix(m_shadowViewMat, pos, lookAt, worldUp);
glm::mat4 scaleMat;
Math::LoadScaleMatrix(scaleMat, glm::vec3(1.0f, 1.0f, -1.0f));
m_shadowViewMat = scaleMat * m_shadowViewMat;
glm::mat4 temporary = m_shadowProjMat * m_shadowViewMat;
m_shadowTextureMat = m_shadowBias * temporary;
m_shadowViewMat = scaleMat * m_shadowViewMat;
auto projectionViewMatrix = m_shadowProjMat * m_shadowViewMat;
m_shadowParams[region].transform = m_shadowTextureMat;
renderer->SetProjectionMatrix(m_shadowProjMat);
renderer->SetViewMatrix(m_shadowViewMat);
// render objects into shadow map
for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
{
if (!m_objects[objRank].used)
continue;
bool terrain = (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN);
if (terrain && !m_terrainShadows) continue;
auto combinedMatrix = projectionViewMatrix * m_objects[objRank].transform;
if (!IsVisible(combinedMatrix, objRank))
continue;
int baseObjRank = m_objects[objRank].baseObjRank;
if (baseObjRank == -1)
continue;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (!p1.used)
continue;
renderer->SetModelMatrix(m_objects[objRank].transform);
for (auto& data : p1.next)
{
renderer->SetTexture(data.albedoTexture);
renderer->DrawObject(data.buffer, true);
}
}
}
renderer->End();
// restore default state
m_device->SetViewport(0, 0, m_size.x, m_size.y);
m_device->SetColorMask(true, true, true, true);
m_device->Clear();
CProfiler::StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
m_device->SetDepthTest(false);
}
void CEngine::UseMSAA(bool enable)
{
m_multisample = Math::Min(m_device->GetMaxSamples(), m_multisample);
if (m_multisample < 2) return;
if (enable)
{
if (m_multisample > 1)
{
CFramebuffer* framebuffer = m_device->GetFramebuffer("multisample");
if (framebuffer == nullptr)
{
CFramebuffer* screen = m_device->GetFramebuffer("default");
FramebufferParams params;
params.width = screen->GetWidth();
params.height = screen->GetHeight();
params.depth = 24;
params.samples = m_multisample;
framebuffer = m_device->CreateFramebuffer("multisample", params);
if (framebuffer == nullptr)
{
GetLogger()->Error("Could not create MSAA framebuffer, disabling MSAA\n");
m_multisample = 1;
}
}
2019-01-29 19:39:46 +00:00
if (framebuffer != nullptr)
{
2018-10-13 08:12:41 +00:00
framebuffer->Bind();
}
m_device->SetDepthTest(true);
m_device->SetDepthMask(true);
m_device->Clear();
}
}
else
{
if (m_multisample > 1)
{
CFramebuffer* framebuffer = m_device->GetFramebuffer("multisample");
framebuffer->Unbind();
CFramebuffer* screen = m_device->GetFramebuffer("default");
int width = screen->GetWidth();
int height = screen->GetHeight();
framebuffer->CopyToScreen(0, 0, width, height, 0, 0, width, height);
}
}
}
2012-09-19 21:50:28 +00:00
void CEngine::DrawInterface()
{
m_device->SetDepthTest(false);
m_device->SetTransparency(TransparencyMode::NONE);
SetInterfaceCoordinates();
// Force new state to disable lighting
m_interfaceMode = true;
// Draw the entire interface
Ui::CInterface* interface = CRobotMain::GetInstancePointer()->GetInterface();
2015-09-12 09:51:03 +00:00
if (interface != nullptr && m_renderInterface)
{
interface->Draw();
}
m_interfaceMode = false;
2015-09-12 09:51:03 +00:00
if (!m_screenshotMode && m_renderInterface)
{
auto particleRenderer = m_device->GetParticleRenderer();
particleRenderer->Begin();
particleRenderer->SetProjectionMatrix(m_matProjInterface);
particleRenderer->SetViewMatrix(m_matViewInterface);
particleRenderer->SetModelMatrix(m_matWorldInterface);
CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE_IFACE);
m_particle->DrawParticle(SH_INTERFACE); // draws the particles of the interface
CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE_IFACE);
particleRenderer->End();
}
// 3D objects drawn in front of interface
if (m_drawFront)
{
2016-01-30 23:38:24 +00:00
float fogStart = m_deepView[m_rankView] * m_fogStart[m_rankView] * m_clippingDistance;
float fogEnd = m_deepView[m_rankView] * m_clippingDistance;
Color fogColor = m_fogColor[m_rankView];
auto renderer = m_device->GetObjectRenderer();
renderer->Begin();
renderer->SetProjectionMatrix(m_matProj);
renderer->SetViewMatrix(m_matView);
renderer->SetFog(fogStart, fogEnd, { fogColor.r, fogColor.g, fogColor.b });
renderer->SetLighting(false);
renderer->SetLight(glm::vec4(1.0, 1.0, -1.0, 0.0), 1.0f, glm::vec3(1.0));
renderer->SetTransparency(TransparencyMode::NONE);
renderer->SetAlphaScissor(0.0f);
renderer->SetShadowParams(0, nullptr);
renderer->SetAlbedoColor(Color{ 1.0f, 1.0f, 1.0f, 1.0f });
renderer->SetCullFace(CullFace::BACK);
renderer->SetTriplanarMode(m_triplanarMode);
renderer->SetTriplanarScale(m_triplanarScale);
auto projectionViewMatrix = m_matProj * m_matView;
for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
{
if (! m_objects[objRank].used)
continue;
2015-08-14 11:36:23 +00:00
if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN)
continue;
if (! m_objects[objRank].drawFront)
continue;
auto combinedMatrix = projectionViewMatrix * m_objects[objRank].transform;
//if (! IsVisible(combinedMatrix, objRank))
// continue;
int baseObjRank = m_objects[objRank].baseObjRank;
2012-12-28 12:23:49 +00:00
if (baseObjRank == -1)
continue;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>( m_baseObjects.size() ));
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (! p1.used)
continue;
renderer->SetModelMatrix(m_objects[objRank].transform);
//m_lightMan->UpdateDeviceLights(m_objects[objRank].type);
for (auto& data : p1.next)
{
renderer->SetAlbedoTexture(data.albedoTexture);
renderer->SetDetailTexture(data.detailTexture);
renderer->DrawObject(data.buffer);
}
}
renderer->End();
auto particleRenderer = m_device->GetParticleRenderer();
particleRenderer->Begin();
particleRenderer->SetProjectionMatrix(m_matProj);
particleRenderer->SetViewMatrix(m_matView);
2012-09-19 21:50:28 +00:00
m_particle->DrawParticle(SH_FRONT); // draws the particles of the 3D world
particleRenderer->End();
m_device->SetDepthTest(false);
SetInterfaceCoordinates();
}
// Draw foreground color
if (m_overFront)
DrawOverColor();
// At the end to not overlap
2015-09-12 09:51:03 +00:00
if (m_renderInterface)
DrawHighlight();
2014-10-29 16:53:46 +00:00
DrawTimer();
2015-08-09 13:20:35 +00:00
DrawStats();
2015-09-12 09:51:03 +00:00
if (m_renderInterface)
DrawMouse();
}
2012-09-19 21:50:28 +00:00
void CEngine::UpdateGroundSpotTextures()
{
2012-10-20 14:34:22 +00:00
if (!m_firstGroundSpot &&
m_groundMark.drawPos.x == m_groundMark.pos.x &&
m_groundMark.drawPos.z == m_groundMark.pos.z &&
m_groundMark.drawRadius == m_groundMark.radius &&
m_groundMark.drawIntensity == m_groundMark.intensity)
return;
2012-10-20 14:34:22 +00:00
for (int s = 0; s < 16; s++)
{
2021-12-24 19:25:38 +00:00
glm::vec2 min, max;
2012-10-20 14:34:22 +00:00
min.x = (s%4) * 254.0f - 1.0f; // 1 pixel cover
min.y = (s/4) * 254.0f - 1.0f;
max.x = min.x + 254.0f + 2.0f;
max.y = min.y + 254.0f + 2.0f;
bool clear = false;
bool set = false;
// Calculate the area to be erased.
int dot = static_cast<int>(m_groundMark.drawRadius/2.0f);
float tu, tv;
float cx, cy;
tu = (m_groundMark.drawPos.x+1600.0f)/3200.0f;
tv = (m_groundMark.drawPos.z+1600.0f)/3200.0f; // 0..1
cx = (tu*254.0f*4.0f)-0.5f;
cy = (tv*254.0f*4.0f)-0.5f;
if (dot == 0)
{
cx += 0.5f;
cy += 0.5f;
}
float px = cx-Math::Mod(cx, 1.0f);
float py = cy-Math::Mod(cy, 1.0f); // multiple of 1
if (m_firstGroundSpot ||
(m_groundMark.drawRadius != 0.0f &&
px+dot >= min.x && py+dot >= min.y &&
px-dot <= max.x && py-dot <= max.y))
{
clear = true;
}
// Calculate the area to draw.
dot = static_cast<int>(m_groundMark.radius/2.0f);
tu = (m_groundMark.pos.x+1600.0f)/3200.0f;
tv = (m_groundMark.pos.z+1600.0f)/3200.0f; // 0..1
cx = (tu*254.0f*4.0f)-0.5f;
cy = (tv*254.0f*4.0f)-0.5f;
if ( dot == 0 )
{
cx += 0.5f;
cy += 0.5f;
}
px = cx - Math::Mod(cx, 1.0f);
py = cy - Math::Mod(cy, 1.0f); // multiple of 1
if (m_groundMark.draw &&
px+dot >= min.x && py+dot >= min.y &&
px-dot <= max.x && py-dot <= max.y)
{
set = true;
}
2016-03-28 19:42:25 +00:00
if (clear || set || m_debugResources || m_displayGotoImage != nullptr)
2012-10-20 14:34:22 +00:00
{
CImage shadowImg(glm::ivec2(256, 256));
2012-10-20 14:34:22 +00:00
shadowImg.Fill(Gfx::IntColor(255, 255, 255, 255));
// Draw the new shadows.
for (int i = 0; i < static_cast<int>( m_groundSpots.size() ); i++)
{
if (m_groundSpots[i].used == false ||
m_groundSpots[i].radius == 0.0f)
continue;
2012-10-20 14:34:22 +00:00
if (m_groundSpots[i].min == 0.0f &&
m_groundSpots[i].max == 0.0f)
2012-10-20 14:34:22 +00:00
{
dot = static_cast<int>(m_groundSpots[i].radius/2.0f);
tu = (m_groundSpots[i].pos.x+1600.0f)/3200.0f;
tv = (m_groundSpots[i].pos.z+1600.0f)/3200.0f; // 0..1
cx = (tu*254.0f*4.0f) - 0.5f;
cy = (tv*254.0f*4.0f) - 0.5f;
if (dot == 0)
{
cx += 0.5f;
cy += 0.5f;
}
px = cx-Math::Mod(cx, 1.0f);
py = cy-Math::Mod(cy, 1.0f); // multiple of 1
if (px+dot < min.x || py+dot < min.y ||
px-dot > max.x || py-dot > max.y)
continue;
2012-10-20 14:34:22 +00:00
for (int iy = -dot; iy <= dot; iy++)
{
for (int ix =- dot; ix <= dot; ix++)
{
float ppx = px+ix;
float ppy = py+iy;
if (ppx < min.x || ppy < min.y ||
ppx >= max.x || ppy >= max.y)
continue;
2012-10-20 14:34:22 +00:00
float intensity;
if (dot == 0)
intensity = 0.0f;
else
intensity = glm::length(glm::vec2(ppx-cx, ppy-cy)) / dot;
2012-10-20 14:34:22 +00:00
ppx -= min.x; // on the texture
ppy -= min.y;
glm::ivec2 pp(ppx, ppy);
2012-10-20 14:34:22 +00:00
2016-11-11 16:06:53 +00:00
Gfx::Color color = shadowImg.GetPixel(pp);
color.r *= Math::Norm(m_groundSpots[i].color.r+intensity);
color.g *= Math::Norm(m_groundSpots[i].color.g+intensity);
color.b *= Math::Norm(m_groundSpots[i].color.b+intensity);
shadowImg.SetPixel(pp, color);
2012-10-20 14:34:22 +00:00
}
}
}
else
{
for (int iy = 0; iy < 256; iy++)
{
for (int ix = 0; ix < 256; ix++)
{
glm::vec3 pos{};
2012-10-20 14:34:22 +00:00
pos.x = (256.0f * (s%4) + ix) * 3200.0f/1024.0f - 1600.0f;
pos.z = (256.0f * (s/4) + iy) * 3200.0f/1024.0f - 1600.0f;
pos.y = 0.0f;
float level = m_terrain->GetFloorLevel(pos, true);
if (level < m_groundSpots[i].min ||
level > m_groundSpots[i].max)
continue;
2012-10-20 14:34:22 +00:00
float intensity;
if (level > (m_groundSpots[i].max+m_groundSpots[i].min)/2.0f)
intensity = 1.0f - (m_groundSpots[i].max-level) / m_groundSpots[i].smooth;
else
intensity = 1.0f - (level-m_groundSpots[i].min) / m_groundSpots[i].smooth;
if (intensity < 0.0f) intensity = 0.0f;
glm::ivec2 pp(ix, iy);
2012-10-20 14:34:22 +00:00
2016-11-11 16:06:53 +00:00
Gfx::Color color = shadowImg.GetPixel(pp);
color.r *= Math::Norm(m_groundSpots[i].color.r+intensity);
color.g *= Math::Norm(m_groundSpots[i].color.g+intensity);
color.b *= Math::Norm(m_groundSpots[i].color.b+intensity);
shadowImg.SetPixel(pp, color);
2012-10-20 14:34:22 +00:00
}
}
}
}
if (set)
{
dot = static_cast<int>(m_groundMark.radius/2.0f);
tu = (m_groundMark.pos.x + 1600.0f) / 3200.0f;
tv = (m_groundMark.pos.z + 1600.0f) / 3200.0f; // 0..1
cx = (tu*254.0f*4.0f)-0.5f;
cy = (tv*254.0f*4.0f)-0.5f;
if (dot == 0)
{
cx += 0.5f;
cy += 0.5f;
}
px = cx-Math::Mod(cx, 1.0f);
py = cy-Math::Mod(cy, 1.0f); // multiple of 1
for (int iy = -dot; iy <= dot; iy++)
{
for (int ix = -dot; ix <= dot; ix++)
{
float ppx = px+ix;
float ppy = py+iy;
if (ppx < min.x || ppy < min.y ||
ppx >= max.x || ppy >= max.y)
continue;
2012-10-20 14:34:22 +00:00
ppx -= min.x; // on the texture
ppy -= min.y;
float intensity = 1.0f - glm::length(glm::vec2(ix, iy)) / dot;
if (intensity <= 0.0f)
continue;
2012-10-20 14:34:22 +00:00
intensity *= m_groundMark.intensity;
int j = (ix+dot) + (iy+dot) * m_groundMark.dx;
if (m_groundMark.table[j] == 1) // green ?
{
glm::ivec2 pp(ppx, ppy);
2016-11-11 16:06:53 +00:00
Gfx::Color color = shadowImg.GetPixel(pp);
color.r *= Math::Norm(1.0f-intensity);
color.g *= 1.0f;
color.b *= Math::Norm(1.0f-intensity);
shadowImg.SetPixel(pp, color);
2012-10-20 14:34:22 +00:00
}
if (m_groundMark.table[j] == 2) // red ?
{
glm::ivec2 pp(ppx, ppy);
2016-11-11 16:06:53 +00:00
Gfx::Color color = shadowImg.GetPixel(pp);
color.r *= 1.0f;
color.g *= Math::Norm(1.0f-intensity);
color.b *= Math::Norm(1.0f-intensity);
shadowImg.SetPixel(pp, color);
2012-10-20 14:34:22 +00:00
}
}
}
}
2016-03-28 15:57:41 +00:00
if (m_debugResources)
{
for (float x = min.x; x < max.x; x += 1.0f)
{
for (float y = min.y; y < max.y; y += 1.0f)
{
glm::vec3 pos(
2016-03-28 15:57:41 +00:00
x / 4.0f / 254.0f * 3200.0f - 1600.0f,
0.0f,
y / 4.0f / 254.0f * 3200.0f - 1600.0f
);
TerrainRes res = m_terrain->GetResource(pos);
glm::ivec2 p(x-min.x, y-min.y);
2016-03-28 15:57:41 +00:00
if (res == TR_NULL)
{
shadowImg.SetPixel(p, Gfx::Color(0.5f, 0.5f, 0.5f));
continue;
}
shadowImg.SetPixelInt(p, ResourceToColor(res));
}
}
}
2016-03-28 19:42:25 +00:00
if (m_displayGotoImage != nullptr)
{
glm::ivec2 size = m_displayGotoImage->GetSize();
2016-03-28 19:42:25 +00:00
for (float x = min.x; x < max.x; x += 1.0f)
{
for (float y = min.y; y < max.y; y += 1.0f)
{
int px = x / 4.0f / 254.0f * size.x;
int py = y / 4.0f / 254.0f * size.y;
// This can happen because the shadow??.png textures have a 1 pixel margin around them
if (px < 0 || px >= size.x || py < 0 || py >= size.y)
continue;
auto color = m_displayGotoImage->GetPixelInt({ px, py });
shadowImg.SetPixelInt({ x - min.x, y - min.y }, color);
2016-03-28 19:42:25 +00:00
}
}
}
2012-10-20 14:34:22 +00:00
std::stringstream str;
str << "textures/shadow" << std::setfill('0') << std::setw(2) << s << ".png";
2012-10-20 14:34:22 +00:00
std::string texName = str.str();
CreateOrUpdateTexture(texName, &shadowImg);
2012-10-20 14:34:22 +00:00
}
}
for (int i = 0; i < static_cast<int>( m_groundSpots.size() ); i++)
{
if (m_groundSpots[i].used == false ||
m_groundSpots[i].radius == 0.0f)
{
m_groundSpots[i].drawRadius = 0.0f;
}
else
{
m_groundSpots[i].drawPos = m_groundSpots[i].pos;
m_groundSpots[i].drawRadius = m_groundSpots[i].radius;
}
}
m_groundMark.drawPos = m_groundMark.pos;
m_groundMark.drawRadius = m_groundMark.radius;
m_groundMark.drawIntensity = m_groundMark.intensity;
m_firstGroundSpot = false;
}
2015-08-14 11:36:23 +00:00
void CEngine::DrawShadowSpots()
{
m_device->SetDepthMask(false);
glm::mat4 matrix = glm::mat4(1.0f);
//m_device->SetTransform(TRANSFORM_WORLD, matrix);
// TODO: create a separate texture
//SetTexture("textures/effect03.png");
2021-12-24 19:25:38 +00:00
glm::vec2 ts, ti;
float dp = 0.5f/256.0f;
ts.y = 192.0f/256.0f;
ti.y = 224.0f/256.0f;
ts.y += dp;
ti.y -= dp;
glm::vec3 n(0.0f, 1.0f, 0.0f);
2016-01-30 23:38:24 +00:00
float startDeepView = m_deepView[m_rankView] * m_fogStart[m_rankView] * m_clippingDistance;
float endDeepView = m_deepView[m_rankView] * m_clippingDistance;
float lastIntensity = -1.0f;
for (int i = 0; i < static_cast<int>( m_shadowSpots.size() ); i++)
{
if (m_shadowSpots[i].hide || !m_shadowSpots[i].used)
continue;
glm::vec3 pos = m_shadowSpots[i].pos; // pos = center of the shadow on the ground
if (m_eyePt.y == pos.y)
continue; // camera at the same level?
float d = 0.0f;
float D = 0.0f;
// h is the height above the ground to which the shadow
// will be drawn.
if (m_eyePt.y > pos.y) // camera on?
{
float height = m_eyePt.y-pos.y;
float h = m_shadowSpots[i].radius;
float max = height*0.5f;
if ( h > max ) h = max;
if ( h > 4.0f ) h = 4.0f;
D = glm::distance(m_eyePt, pos);
if (D >= endDeepView)
continue;
d = D*h/height;
pos.x += (m_eyePt.x-pos.x)*d/D;
pos.z += (m_eyePt.z-pos.z)*d/D;
pos.y += h;
}
else // camera underneath?
{
float height = pos.y-m_eyePt.y;
float h = m_shadowSpots[i].radius;
float max = height*0.1f;
if ( h > max ) h = max;
if ( h > 4.0f ) h = 4.0f;
D = glm::distance(m_eyePt, pos);
if (D >= endDeepView)
continue;
d = D*h/height;
pos.x += (m_eyePt.x-pos.x)*d/D;
pos.z += (m_eyePt.z-pos.z)*d/D;
pos.y -= h;
}
// The hFactor decreases the intensity and size increases more
// the object is high relative to the ground.
float hFactor = m_shadowSpots[i].height/20.0f;
if ( hFactor < 0.0f ) hFactor = 0.0f;
if ( hFactor > 1.0f ) hFactor = 1.0f;
hFactor = powf(1.0f-hFactor, 2.0f);
if ( hFactor < 0.2f ) hFactor = 0.2f;
float radius = m_shadowSpots[i].radius*1.5f;
radius *= 2.0f-hFactor; // greater if high
radius *= 1.0f-d/D; // smaller if close
glm::vec3 corner[4];
if (m_shadowSpots[i].type == EngineShadowType::NORMAL)
{
corner[0].x = +radius;
corner[0].z = +radius;
corner[0].y = 0.0f;
corner[1].x = -radius;
corner[1].z = +radius;
corner[1].y = 0.0f;
corner[2].x = +radius;
corner[2].z = -radius;
corner[2].y = 0.0f;
corner[3].x = -radius;
corner[3].z = -radius;
corner[3].y = 0.0f;
ts.x = 64.0f/256.0f;
ti.x = 96.0f/256.0f;
}
else
{
2021-12-24 19:25:38 +00:00
glm::vec2 rot;
2021-12-24 19:25:38 +00:00
rot = Math::RotatePoint(-m_shadowSpots[i].angle, { radius, radius });
corner[0].x = rot.x;
corner[0].z = rot.y;
corner[0].y = 0.0f;
2021-12-24 19:25:38 +00:00
rot = Math::RotatePoint(-m_shadowSpots[i].angle, { -radius, radius });
corner[1].x = rot.x;
corner[1].z = rot.y;
corner[1].y = 0.0f;
2021-12-24 19:25:38 +00:00
rot = Math::RotatePoint(-m_shadowSpots[i].angle, { radius, -radius });
corner[2].x = rot.x;
corner[2].z = rot.y;
corner[2].y = 0.0f;
2021-12-24 19:25:38 +00:00
rot = Math::RotatePoint(-m_shadowSpots[i].angle, { -radius, -radius });
corner[3].x = rot.x;
corner[3].z = rot.y;
corner[3].y = 0.0f;
if (m_shadowSpots[i].type == EngineShadowType::WORM)
{
ts.x = 96.0f/256.0f;
ti.x = 128.0f/256.0f;
}
else
{
ts.x = 64.0f/256.0f;
ti.x = 96.0f/256.0f;
}
}
corner[0] = glm::cross(corner[0], m_shadowSpots[i].normal);
corner[1] = glm::cross(corner[1], m_shadowSpots[i].normal);
corner[2] = glm::cross(corner[2], m_shadowSpots[i].normal);
corner[3] = glm::cross(corner[3], m_shadowSpots[i].normal);
corner[0] += pos;
corner[1] += pos;
corner[2] += pos;
corner[3] += pos;
ts.x += dp;
ti.x -= dp;
IntColor white(255, 255, 255, 255);
Vertex3D vertex[4] =
{
{ corner[1], white, { ts.x, ts.y } },
{ corner[0], white, { ti.x, ts.y } },
{ corner[3], white, { ts.x, ti.y } },
{ corner[2], white, { ti.x, ti.y } }
};
float intensity = (0.5f+m_shadowSpots[i].intensity*0.5f)*hFactor;
// Decreases the intensity of the shade if you're in the area
// between the beginning and the end of the fog.
if ( D > startDeepView )
intensity *= 1.0f-(D-startDeepView)/(endDeepView-startDeepView);
if (intensity == 0.0f)
continue;
if (lastIntensity != intensity) // intensity changed?
{
lastIntensity = intensity;
//SetState(ENG_RSTATE_TTEXTURE_WHITE, Color(intensity, intensity, intensity, intensity));
}
//m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, 4);
AddStatisticTriangle(2);
}
m_device->SetDepthMask(true);
}
2012-09-19 21:50:28 +00:00
void CEngine::DrawBackground()
{
if (m_cloud->GetLevel() != 0.0f) // clouds ?
{
if (m_backgroundCloudUp != m_backgroundCloudDown) // degraded?
DrawBackgroundGradient(m_backgroundCloudUp, m_backgroundCloudDown);
}
else
{
if (m_backgroundColorUp != m_backgroundColorDown) // degraded?
DrawBackgroundGradient(m_backgroundColorUp, m_backgroundColorDown);
}
if (m_backForce || !m_backgroundName.empty() )
{
DrawBackgroundImage(); // image
}
}
2012-09-19 21:50:28 +00:00
void CEngine::DrawBackgroundGradient(const Color& up, const Color& down)
{
glm::vec2 p1(0.0f, 0.0f);
2021-12-24 19:25:38 +00:00
glm::vec2 p2(1.0f, 1.0f);
glm::u8vec4 color[3] =
{
{ up.r, up.g, up.b, up.a },
{ down.r, down.g, down.b, down.a },
{ 0, 0, 0, 0 }
};
auto renderer = m_device->GetUIRenderer();
renderer->SetTexture(Texture{});
renderer->SetTransparency(TransparencyMode::NONE);
auto vertices = renderer->BeginPrimitive(PrimitiveType::TRIANGLE_STRIP, 4);
vertices[0] = { { p1.x, p1.y }, {}, color[1] };
vertices[1] = { { p1.x, p2.y }, {}, color[0] };
vertices[2] = { { p2.x, p1.y }, {}, color[1] };
vertices[3] = { { p2.x, p2.y }, {}, color[0] };
renderer->EndPrimitive();
AddStatisticTriangle(2);
}
2012-09-19 21:50:28 +00:00
void CEngine::DrawBackgroundImage()
{
2021-12-24 19:25:38 +00:00
glm::vec2 p1, p2;
p1.x = 0.0f;
p1.y = 0.0f;
p2.x = 1.0f;
p2.y = 1.0f;
glm::vec3 n = glm::vec3(0.0f, 0.0f, -1.0f); // normal
float u1, u2, v1, v2;
if (m_backgroundFull)
{
u1 = 0.0f;
v1 = 0.0f;
u2 = 1.0f;
v2 = 1.0f;
}
else
{
float h = 0.5f; // visible area vertically (1=all)
float a = m_eyeDirV-Math::PI*0.15f;
if (a > Math::PI ) a -= Math::PI*2.0f; // a = -Math::PI..Math::PI
if (a > Math::PI/4.0f) a = Math::PI/4.0f;
if (a < -Math::PI/4.0f) a = -Math::PI/4.0f;
// Note the background covers Math::PI radians, i.e. it repeats twice per rotation!
u1 = (-m_eyeDirH - GetHFovAngle()/2.0f) / Math::PI;
u2 = u1 + (GetHFovAngle() / Math::PI);
v1 = (1.0f-h)*(0.5f+a/(2.0f*Math::PI/4.0f))+0.1f;
v2 = v1+h;
}
2021-12-24 19:25:38 +00:00
glm::vec2 backgroundScale;
backgroundScale.x = static_cast<float>(m_backgroundTex.originalSize.x) / static_cast<float>(m_backgroundTex.size.x);
backgroundScale.y = static_cast<float>(m_backgroundTex.originalSize.y) / static_cast<float>(m_backgroundTex.size.y);
u2 *= backgroundScale.x;
v2 *= backgroundScale.y;
if (m_backgroundScale)
{
2021-12-24 19:25:38 +00:00
glm::vec2 scale;
scale.x = static_cast<float>(m_size.x) / static_cast<float>(m_backgroundTex.originalSize.x);
scale.y = static_cast<float>(m_size.y) / static_cast<float>(m_backgroundTex.originalSize.y);
2015-08-02 09:40:47 +00:00
if (scale.x > scale.y)
{
scale.y /= scale.x;
scale.x = 1;
}
else
{
scale.x /= scale.y;
scale.y = 1;
}
float margin_u = (1-scale.x)/2;
float margin_v = (1-scale.y)/2;
margin_u *= backgroundScale.x;
margin_v *= backgroundScale.y;
u1 += margin_u;
v1 += margin_v;
u2 -= margin_u;
v2 -= margin_v;
}
auto renderer = m_device->GetUIRenderer();
renderer->SetTexture(m_backgroundTex);
renderer->SetTransparency(TransparencyMode::NONE);
auto vertices = renderer->BeginPrimitive(PrimitiveType::TRIANGLE_STRIP, 4);
vertices[0] = { { p1.x, p1.y }, { u1, v2 } };
vertices[1] = { { p1.x, p2.y }, { u1, v1 } };
vertices[2] = { { p2.x, p1.y }, { u2, v2 } };
vertices[3] = { { p2.x, p2.y }, { u2, v1 } };
renderer->EndPrimitive();
AddStatisticTriangle(2);
}
2012-09-19 21:50:28 +00:00
void CEngine::DrawPlanet()
{
if (! m_planet->PlanetExist())
return;
auto renderer = m_device->GetObjectRenderer();
renderer->Begin();
renderer->SetProjectionMatrix(m_matProjInterface);
renderer->SetViewMatrix(m_matViewInterface);
renderer->SetModelMatrix(m_matWorldInterface);
renderer->SetFog(1e+6, 1e+6, {});
renderer->SetLighting(false);
renderer->SetDepthTest(false);
renderer->SetDepthMask(false);
m_planet->Draw(); // draws the planets
renderer->End();
}
2012-09-19 21:50:28 +00:00
void CEngine::DrawForegroundImage()
{
if (m_foregroundName.empty())
return;
glm::vec3 n = glm::vec3(0.0f, 0.0f, -1.0f); // normal
2021-12-24 19:25:38 +00:00
glm::vec2 p1(0.0f, 0.0f);
glm::vec2 p2(1.0f, 1.0f);
float u1 = -m_eyeDirH/(Math::PI*0.6f)+Math::PI*0.5f;
float u2 = u1+0.50f;
float v1 = 0.2f;
float v2 = 1.0f;
auto renderer = m_device->GetUIRenderer();
renderer->SetTexture(m_foregroundTex);
renderer->SetTransparency(TransparencyMode::BLACK);
auto vertices = renderer->BeginPrimitive(PrimitiveType::TRIANGLE_STRIP, 4);
vertices[0] = { { p1.x, p1.y }, { u1, v2 } };
vertices[1] = { { p1.x, p2.y }, { u1, v1 } };
vertices[2] = { { p2.x, p1.y }, { u2, v2 } };
vertices[3] = { { p2.x, p2.y }, { u2, v1 } };
renderer->EndPrimitive();
AddStatisticTriangle(2);
}
2012-09-19 21:50:28 +00:00
void CEngine::DrawOverColor()
{
if ((m_overColor == Color(0.0f, 0.0f, 0.0f, 0.0f) && m_overMode == TransparencyMode::BLACK) ||
(m_overColor == Color(1.0f, 1.0f, 1.0f, 1.0f) && m_overMode == TransparencyMode::WHITE))
return;
2021-12-24 19:25:38 +00:00
glm::vec2 p1(0.0f, 0.0f);
glm::vec2 p2(1.0f, 1.0f);
auto color = Gfx::ColorToIntColor(m_overColor);
glm::u8vec4 colors[3] =
{
{ color.r, color.g, color.b, color.a },
{ color.r, color.g, color.b, color.a },
{ 0, 0, 0, 0 }
};
auto renderer = m_device->GetUIRenderer();
renderer->SetTexture(Texture{});
renderer->SetTransparency(m_overMode);
auto vertices = renderer->BeginPrimitive(PrimitiveType::TRIANGLE_STRIP, 4);
vertices[0] = { { p1.x, p1.y }, {}, colors[1] };
vertices[1] = { { p1.x, p2.y }, {}, colors[0] };
vertices[2] = { { p2.x, p1.y }, {}, colors[1] };
vertices[3] = { { p2.x, p2.y }, {}, colors[0] };
renderer->EndPrimitive();
AddStatisticTriangle(2);
}
2012-09-19 21:50:28 +00:00
void CEngine::DrawHighlight()
{
2021-12-24 19:25:38 +00:00
glm::vec2 min, max;
2016-06-21 11:07:40 +00:00
min.x = 1000000.0f;
min.y = 1000000.0f;
max.x = -1000000.0f;
max.y = -1000000.0f;
int i = 0;
while (m_highlightRank[i] != -1)
{
2021-12-24 19:25:38 +00:00
glm::vec2 omin, omax;
if (GetBBox2D(m_highlightRank[i++], omin, omax))
{
min.x = Math::Min(min.x, omin.x);
min.y = Math::Min(min.y, omin.y);
max.x = Math::Max(max.x, omax.x);
max.y = Math::Max(max.y, omax.y);
}
}
2016-06-21 11:07:40 +00:00
if (min.x == 1000000.0f ||
min.y == 1000000.0f ||
max.x == -1000000.0f ||
max.y == -1000000.0f)
{
m_highlight = false; // not highlighted
}
else
{
m_highlightP1 = min;
m_highlightP2 = max;
m_highlight = true;
}
2016-06-21 11:07:40 +00:00
if (!m_highlight)
return;
2021-12-24 19:25:38 +00:00
glm::vec2 p1 = m_highlightP1;
glm::vec2 p2 = m_highlightP2;
int nbOut = 0;
if (p1.x < 0.0f || p1.x > 1.0f) nbOut++;
if (p1.y < 0.0f || p1.y > 1.0f) nbOut++;
if (p2.x < 0.0f || p2.x > 1.0f) nbOut++;
if (p2.y < 0.0f || p2.y > 1.0f) nbOut++;
if (nbOut > 2)
return;
2016-06-21 11:07:40 +00:00
float d = 0.5f + sinf(m_highlightTime * 6.0f) * 0.5f;
d *= (p2.x - p1.x) * 0.1f;
p1.x += d;
p1.y += d;
p2.x -= d;
p2.y -= d;
glm::u8vec4 color(255, 255, 0, 255); // yellow
auto renderer = m_device->GetUIRenderer();
renderer->SetTransparency(TransparencyMode::NONE);
renderer->SetTexture(Texture{});
float dx = (p2.x - p1.x) / 5.0f;
float dy = (p2.y - p1.y) / 5.0f;
auto line = renderer->BeginPrimitive(PrimitiveType::LINE_STRIP, 3);
line[0] = { { p1.x, p1.y + dy }, {}, color };
line[1] = { { p1.x, p1.y }, {}, color };
line[2] = { { p1.x + dx, p1.y }, {}, color };
renderer->EndPrimitive();
line = renderer->BeginPrimitive(PrimitiveType::LINE_STRIP, 3);
line[0] = { { p2.x - dx, p1.y }, {}, color };
line[1] = { { p2.x, p1.y }, {}, color };
line[2] = { { p2.x, p1.y + dy }, {}, color };
renderer->EndPrimitive();
line = renderer->BeginPrimitive(PrimitiveType::LINE_STRIP, 3);
line[0] = { { p2.x, p2.y - dy }, {}, color };
line[1] = { { p2.x, p2.y }, {}, color };
line[2] = { { p2.x - dx, p2.y }, {}, color };
renderer->EndPrimitive();
line = renderer->BeginPrimitive(PrimitiveType::LINE_STRIP, 3);
line[0] = { { p1.x + dx, p2.y }, {}, color };
line[1] = { { p1.x, p2.y }, {}, color };
line[2] = { { p1.x, p2.y - dy }, {}, color };
renderer->EndPrimitive();
}
2012-09-19 21:50:28 +00:00
void CEngine::DrawMouse()
{
MouseMode mode = m_app->GetMouseMode();
if (mode != MOUSE_ENGINE && mode != MOUSE_BOTH)
return;
2016-06-21 11:07:40 +00:00
SetWindowCoordinates();
2021-12-24 19:25:38 +00:00
glm::vec2 mousePos = CInput::GetInstancePointer()->GetMousePos();
glm::ivec2 pos(mousePos.x * m_size.x, m_size.y - mousePos.y * m_size.y);
2016-06-21 11:07:40 +00:00
pos.x -= MOUSE_TYPES.at(m_mouseType).hotPoint.x;
pos.y -= MOUSE_TYPES.at(m_mouseType).hotPoint.y;
glm::ivec2 shadowPos = { pos.x + 4, pos.y - 3 };
auto renderer = m_device->GetUIRenderer();
renderer->SetTexture(m_miceTexture);
DrawMouseSprite(shadowPos, MOUSE_SIZE, MOUSE_TYPES.at(m_mouseType).iconShadow, TransparencyMode::WHITE);
DrawMouseSprite(pos, MOUSE_SIZE, MOUSE_TYPES.at(m_mouseType).icon1, MOUSE_TYPES.at(m_mouseType).mode1);
DrawMouseSprite(pos, MOUSE_SIZE, MOUSE_TYPES.at(m_mouseType).icon2, MOUSE_TYPES.at(m_mouseType).mode2);
2016-06-21 11:07:40 +00:00
SetInterfaceCoordinates();
}
void CEngine::DrawMouseSprite(const glm::ivec2& pos, const glm::ivec2& size, int icon, TransparencyMode mode)
{
if (icon == -1)
return;
glm::ivec2 p1 = pos;
glm::ivec2 p2 = p1 + size;
float u1 = (32.0f / 256.0f) * (icon % 8);
float v1 = (32.0f / 256.0f) * (icon / 8);
float u2 = u1 + (32.0f / 256.0f);
float v2 = v1 + (32.0f / 256.0f);
float dp = 0.5f / 256.0f;
u1 += dp;
v1 += dp;
u2 -= dp;
v2 -= dp;
auto renderer = m_device->GetUIRenderer();
renderer->SetTransparency(mode);
auto vertices = renderer->BeginPrimitive(PrimitiveType::TRIANGLE_STRIP, 4);
vertices[0] = { { p1.x, p2.y }, { u1, v2 } };
vertices[1] = { { p1.x, p1.y }, { u1, v1 } };
vertices[2] = { { p2.x, p2.y }, { u2, v2 } };
vertices[3] = { { p2.x, p1.y }, { u2, v1 } };
renderer->EndPrimitive();
AddStatisticTriangle(2);
}
2012-09-19 21:50:28 +00:00
void CEngine::DrawStats()
{
if (!m_showStats)
return;
2017-10-11 15:10:04 +00:00
float height = m_text->GetAscent(FONT_COMMON, 13.0f);
float width = 0.4f;
const int TOTAL_LINES = 22;
2021-12-24 19:25:38 +00:00
glm::vec2 pos(0.05f * m_size.x/m_size.y, 0.05f + TOTAL_LINES * height);
auto renderer = m_device->GetUIRenderer();
renderer->SetTransparency(TransparencyMode::ALPHA);
renderer->SetTexture(Texture{});
2015-08-09 13:20:35 +00:00
glm::u8vec4 black = { 0, 0, 0, 192 };
2021-12-24 19:25:38 +00:00
glm::vec2 margin = { 5.f / m_size.x, 5.f / m_size.y };
auto vertices = renderer->BeginPrimitive(PrimitiveType::TRIANGLE_STRIP, 4);
vertices[0] = { { pos.x - margin.x, pos.y - (TOTAL_LINES + 1) * height - margin.y }, {}, black };
vertices[1] = { { pos.x - margin.x, pos.y + height + margin.y }, {}, black };
vertices[2] = { { pos.x + width + margin.x, pos.y - (TOTAL_LINES + 1) * height - margin.y }, {}, black };
vertices[3] = { { pos.x + width + margin.x, pos.y + height + margin.y }, {}, black };
renderer->EndPrimitive();
renderer->SetTransparency(TransparencyMode::ALPHA);
auto drawStatsLine = [&](const std::string& name, const std::string& value, const std::string& value2)
2015-08-09 13:20:35 +00:00
{
if (!name.empty())
2017-10-11 15:10:04 +00:00
m_text->DrawText(name+":", FONT_COMMON, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
pos.x += 0.25f;
if (!value.empty())
2017-10-11 15:10:04 +00:00
m_text->DrawText(value, FONT_COMMON, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
pos.x += 0.15f;
if (!value2.empty())
2017-10-11 15:10:04 +00:00
m_text->DrawText(value2, FONT_COMMON, 12.0f, pos, 1.0f, TEXT_ALIGN_RIGHT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
pos.x -= 0.4f;
2015-08-09 13:20:35 +00:00
pos.y -= height;
};
2012-10-25 21:29:49 +00:00
auto drawStatsValue = [&](const std::string& name, long long time)
2015-08-09 13:20:35 +00:00
{
float value = static_cast<float>(time)/CProfiler::GetPerformanceCounterTime(PCNT_ALL);
drawStatsLine(name, StrUtils::Format("%.2f", value), StrUtils::Format("%.2f ms", time/1e6f));
2015-08-09 13:20:35 +00:00
};
2012-10-25 21:29:49 +00:00
2015-08-09 13:20:35 +00:00
auto drawStatsCounter = [&](const std::string& name, PerformanceCounter counter)
{
drawStatsValue(name, CProfiler::GetPerformanceCounterTime(counter));
2015-08-09 13:20:35 +00:00
};
2012-10-25 21:29:49 +00:00
// TODO: Find a more generic way to calculate these in CProfiler
2012-10-25 21:29:49 +00:00
long long engineUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ENGINE) -
CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_PARTICLE);
2016-07-24 14:36:13 +00:00
long long gameUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_GAME) -
CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_CBOT);
long long otherUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ALL) -
2016-07-24 14:36:13 +00:00
CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ENGINE) -
CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_GAME);
long long otherRender = CProfiler::GetPerformanceCounterTime(PCNT_RENDER_ALL) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_PARTICLE_WORLD) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_WATER) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_TERRAIN) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_OBJECTS) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_INTERFACE) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_SHADOW_MAP);
2012-10-25 21:29:49 +00:00
2015-08-09 13:20:35 +00:00
drawStatsCounter("Event processing", PCNT_EVENT_PROCESSING);
drawStatsLine( "", "", "");
2015-08-09 13:20:35 +00:00
drawStatsCounter("Frame update", PCNT_UPDATE_ALL);
drawStatsValue (" Engine update", engineUpdate);
drawStatsCounter(" Particle update", PCNT_UPDATE_PARTICLE);
2016-07-24 14:36:13 +00:00
drawStatsValue (" Game update", gameUpdate);
drawStatsCounter(" CBot programs", PCNT_UPDATE_CBOT);
2015-08-09 13:20:35 +00:00
drawStatsValue( " Other update", otherUpdate);
drawStatsLine( "", "", "");
2015-08-09 13:20:35 +00:00
drawStatsCounter("Frame render", PCNT_RENDER_ALL);
drawStatsCounter(" Particle render", PCNT_RENDER_PARTICLE_WORLD);
2015-08-09 13:20:35 +00:00
drawStatsCounter(" Water render", PCNT_RENDER_WATER);
drawStatsCounter(" Terrain render", PCNT_RENDER_TERRAIN);
drawStatsCounter(" Objects render", PCNT_RENDER_OBJECTS);
drawStatsCounter(" UI render", PCNT_RENDER_INTERFACE);
drawStatsCounter(" particles", PCNT_RENDER_PARTICLE_IFACE);
2015-08-09 13:20:35 +00:00
drawStatsCounter(" Shadow map render", PCNT_RENDER_SHADOW_MAP);
drawStatsValue( " Other render", otherRender);
drawStatsCounter("Swap buffers & VSync", PCNT_SWAP_BUFFERS);
drawStatsLine( "", "", "");
drawStatsLine( "Triangles", StrUtils::ToString<int>(m_statisticTriangle), "");
drawStatsLine( "FPS", StrUtils::Format("%.3f", m_fps), "");
drawStatsLine( "", "", "");
std::stringstream str;
2015-08-15 12:02:07 +00:00
str << std::fixed << std::setprecision(2) << m_statisticPos.x << "; " << m_statisticPos.z;
drawStatsLine( "Position", str.str(), "");
}
2014-10-29 16:53:46 +00:00
void CEngine::DrawTimer()
{
2021-12-24 19:25:38 +00:00
glm::vec2 pos(0.98f, 0.98f-m_text->GetAscent(FONT_COMMON, 15.0f));
2017-10-11 15:10:04 +00:00
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));
2014-10-29 16:53:46 +00:00
}
void CEngine::AddBaseObjTriangles(int baseObjRank, const std::vector<Gfx::ModelTriangle>& triangles)
{
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
std::array<Vertex3D, 3> vertices;
for (const auto& triangle : triangles)
{
vertices[0] = triangle.p1;
vertices[1] = triangle.p2;
vertices[2] = triangle.p3;
2022-02-25 19:59:31 +00:00
Material material = triangle.material;
material.albedoTexture = "objects/" + material.albedoTexture;
2022-02-25 19:59:31 +00:00
if (material.variableDetail)
material.detailTexture = GetSecondTexture();
EngineBaseObjDataTier& data = AddLevel(p1, EngineTriangleType::TRIANGLES, material);
data.vertices.insert(data.vertices.end(), vertices.begin(), vertices.end());
data.updateStaticBuffer = true;
for (size_t i = 0; i < vertices.size(); i++)
{
p1.bboxMin.x = Math::Min(vertices[i].position.x, p1.bboxMin.x);
p1.bboxMin.y = Math::Min(vertices[i].position.y, p1.bboxMin.y);
p1.bboxMin.z = Math::Min(vertices[i].position.z, p1.bboxMin.z);
p1.bboxMax.x = Math::Max(vertices[i].position.x, p1.bboxMax.x);
p1.bboxMax.y = Math::Max(vertices[i].position.y, p1.bboxMax.y);
p1.bboxMax.z = Math::Max(vertices[i].position.z, p1.bboxMax.z);
}
p1.boundingSphere = Math::BoundingSphereForBox(p1.bboxMin, p1.bboxMax);
p1.totalTriangles += vertices.size() / 3;
}
2022-02-25 19:59:31 +00:00
m_updateStaticBuffers = true;
}
void CEngine::UpdateObjectShadowSpotNormal(int objRank)
{
assert(objRank >= 0 && objRank < static_cast<int>( m_objects.size() ));
int shadowRank = m_objects[objRank].shadowRank;
if (shadowRank == -1)
return;
assert(shadowRank >= 0 && shadowRank < static_cast<int>( m_shadowSpots.size() ));
// Calculating the normal to the ground in nine strategic locations,
// then perform a weighted average (the dots in the center are more important).
glm::vec3 pos = m_shadowSpots[shadowRank].pos;
float radius = m_shadowSpots[shadowRank].radius;
glm::vec3 n[20];
glm::vec3 norm = { 0, 0, 0 };
int i = 0;
m_terrain->GetNormal(norm, pos);
n[i++] = norm;
n[i++] = norm;
n[i++] = norm;
glm::vec3 shPos = pos;
shPos.x += radius*0.6f;
shPos.z += radius*0.6f;
m_terrain->GetNormal(norm, shPos);
n[i++] = norm;
n[i++] = norm;
shPos = pos;
shPos.x -= radius*0.6f;
shPos.z += radius*0.6f;
m_terrain->GetNormal(norm, shPos);
n[i++] = norm;
n[i++] = norm;
shPos = pos;
shPos.x += radius*0.6f;
shPos.z -= radius*0.6f;
m_terrain->GetNormal(norm, shPos);
n[i++] = norm;
n[i++] = norm;
shPos = pos;
shPos.x -= radius*0.6f;
shPos.z -= radius*0.6f;
m_terrain->GetNormal(norm, shPos);
n[i++] = norm;
n[i++] = norm;
shPos = pos;
shPos.x += radius;
shPos.z += radius;
m_terrain->GetNormal(norm, shPos);
n[i++] = norm;
shPos = pos;
shPos.x -= radius;
shPos.z += radius;
m_terrain->GetNormal(norm, shPos);
n[i++] = norm;
shPos = pos;
shPos.x += radius;
shPos.z -= radius;
m_terrain->GetNormal(norm, shPos);
n[i++] = norm;
shPos = pos;
shPos.x -= radius;
shPos.z -= radius;
m_terrain->GetNormal(norm, shPos);
n[i++] = norm;
norm = { 0, 0, 0 };
for (int j = 0; j < i; j++)
{
norm += n[j];
}
norm /= static_cast<float>(i); // average vector
m_shadowSpots[shadowRank].normal = norm;
}
int CEngine::AddStaticMesh(const std::string& key, const CModelMesh* mesh, const glm::mat4& worldMatrix)
{
int baseObjRank = -1;
auto it = m_staticMeshBaseObjects.find(key);
if (it == m_staticMeshBaseObjects.end())
{
baseObjRank = CreateBaseObject();
AddBaseObjTriangles(baseObjRank, mesh->GetTriangles());
m_staticMeshBaseObjects[key] = baseObjRank;
}
else
{
baseObjRank = it->second;
}
int objRank = CreateObject();
SetObjectBaseRank(objRank, baseObjRank);
SetObjectTransform(objRank, worldMatrix);
SetObjectType(objRank, ENG_OBJTYPE_FIX);
return objRank;
}
void CEngine::AddStaticMeshShadowSpot(int meshHandle, const ModelShadowSpot& shadowSpot)
{
int objRank = meshHandle;
CreateShadowSpot(objRank);
SetObjectShadowSpotRadius(objRank, shadowSpot.radius);
SetObjectShadowSpotIntensity(objRank, shadowSpot.intensity);
SetObjectShadowSpotType(objRank, EngineShadowType::NORMAL);
SetObjectShadowSpotHeight(objRank, 0.0f);
SetObjectShadowSpotAngle(objRank, 0.0f);
UpdateObjectShadowSpotNormal(objRank);
}
void CEngine::DeleteStaticMesh(int meshHandle)
{
int objRank = meshHandle;
DeleteShadowSpot(objRank);
DeleteObject(objRank);
}
const glm::mat4& CEngine::GetStaticMeshWorldMatrix(int meshHandle)
{
int objRank = meshHandle;
return m_objects[objRank].transform;
}
2022-01-24 20:57:56 +00:00
void CEngine::SetStaticMeshGhostMode(int meshHandle, bool enabled)
{
int objRank = meshHandle;
2022-01-24 20:57:56 +00:00
SetObjectGhostMode(objRank, enabled);
}
2012-09-19 21:50:28 +00:00
2016-03-28 11:51:39 +00:00
void CEngine::SetDebugLights(bool debugLights)
{
m_debugLights = debugLights;
}
bool CEngine::GetDebugLights()
{
return m_debugLights;
}
void CEngine::DebugDumpLights()
{
m_debugDumpLights = true;
}
2016-03-28 15:57:41 +00:00
void CEngine::SetDebugResources(bool debugResources)
{
m_debugResources = debugResources;
m_firstGroundSpot = true; // Force a refresh of ground spot textures
UpdateGroundSpotTextures();
}
bool CEngine::GetDebugResources()
{
return m_debugResources;
}
2016-03-28 18:25:07 +00:00
void CEngine::SetDebugGoto(bool debugGoto)
{
m_debugGoto = debugGoto;
2016-03-28 19:42:25 +00:00
if (!m_debugGoto)
{
m_displayGotoImage.reset();
}
2016-03-28 18:25:07 +00:00
}
bool CEngine::GetDebugGoto()
{
return m_debugGoto;
}
void CEngine::AddDebugGotoLine(std::vector<Gfx::Vertex3D> line)
2016-03-28 18:25:07 +00:00
{
m_displayGoto.push_back(line);
}
2016-03-28 19:42:25 +00:00
void CEngine::SetDebugGotoBitmap(std::unique_ptr<CImage> debugImage)
{
m_displayGotoImage = std::move(debugImage);
m_firstGroundSpot = true; // Force ground spot texture reload
UpdateGroundSpotTextures();
}
void CEngine::SetInterfaceCoordinates()
{
auto renderer = m_device->GetUIRenderer();
renderer->SetProjection(0.0f, 1.0f, 0.0f, 1.0f);
}
void CEngine::EnablePauseBlur()
{
if (!m_pauseBlurEnabled) return;
m_captureWorld = true;
m_worldCaptured = false;
}
void CEngine::DisablePauseBlur()
{
m_captureWorld = false;
m_worldCaptured = false;
}
void CEngine::SetWindowCoordinates()
{
auto renderer = m_device->GetUIRenderer();
renderer->SetProjection(0.0f, m_size.x, m_size.y, 0.0f);
}
2012-09-19 21:50:28 +00:00
} // namespace Gfx