/* * This file is part of the Colobot: Gold Edition source code * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsiteс.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 "common/image.h" #include "common/key.h" #include "common/logger.h" #include "graphics/core/device.h" #include "graphics/engine/camera.h" #include "graphics/engine/cloud.h" #include "graphics/engine/lightman.h" #include "graphics/engine/lightning.h" #include "graphics/engine/particle.h" #include "graphics/engine/planet.h" #include "graphics/engine/pyro.h" #include "graphics/engine/terrain.h" #include "graphics/engine/text.h" #include "graphics/engine/water.h" #include "math/geometry.h" #include "sound/sound.h" #include "ui/interface.h" #include template<> Gfx::CEngine* CSingleton::m_instance = nullptr; // Graphics module namespace namespace Gfx { CEngine::CEngine(CApplication *app) { m_app = app; 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_pause = nullptr; m_showStats = false; m_focus = 0.75f; m_rankView = 0; 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; m_waterAddColor = Color(0.0f, 0.0f, 0.0f, 0.0f); m_render = true; m_movieLock = false; m_shadowVisible = true; m_groundSpotVisible = true; m_dirty = true; m_fog = true; m_secondTexNum = 0; m_eyeDirH = 0.0f; m_eyeDirV = 0.0f; m_backgroundName = ""; // no background image m_backgroundColorUp = Color(); m_backgroundColorDown = Color(); m_backgroundCloudUp = Color(); m_backgroundCloudDown = Color(); m_backgroundFull = false; m_overFront = true; m_overColor = Color(); m_overMode = ENG_RSTATE_TCOLOR_BLACK; m_highlightRank[0] = -1; // empty list m_highlightTime = 0.0f; m_eyePt = Math::Vector(0.0f, 0.0f, 0.0f); m_lookatPt = Math::Vector(0.0f, 0.0f, 1.0f); m_drawWorld = true; m_drawFront = false; m_particleDensity = 1.0f; m_lastClippingDistance = m_clippingDistance = 1.0f; m_objectDetail = 1.0f; m_terrainVision = 1000.0f; m_gadgetQuantity = 1.0f; m_textureQuality = 1; m_totoMode = true; m_lensMode = true; m_waterMode = true; m_skyMode = true; m_backForce = true; m_planetMode = true; m_lightMode = true; m_editIndentMode = true; m_editIndentValue = 4; m_tracePrecision = 1.0f; m_updateGeometry = false; m_updateStaticBuffers = false; m_interfaceMode = false; m_debugLights = false; m_debugDumpLights = false; m_mice[ENG_MOUSE_NORM] = EngineMouse( 0, 1, 32, ENG_RSTATE_TTEXTURE_WHITE, ENG_RSTATE_TTEXTURE_BLACK, Math::Point( 1.0f, 1.0f)); m_mice[ENG_MOUSE_WAIT] = EngineMouse( 2, 3, 33, ENG_RSTATE_TTEXTURE_WHITE, ENG_RSTATE_TTEXTURE_BLACK, Math::Point( 8.0f, 12.0f)); m_mice[ENG_MOUSE_HAND] = EngineMouse( 4, 5, 34, ENG_RSTATE_TTEXTURE_WHITE, ENG_RSTATE_TTEXTURE_BLACK, Math::Point( 7.0f, 2.0f)); m_mice[ENG_MOUSE_NO] = EngineMouse( 6, 7, 35, ENG_RSTATE_TTEXTURE_WHITE, ENG_RSTATE_TTEXTURE_BLACK, Math::Point(10.0f, 10.0f)); m_mice[ENG_MOUSE_EDIT] = EngineMouse( 8, 9, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 6.0f, 10.0f)); m_mice[ENG_MOUSE_CROSS] = EngineMouse(10, 11, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point(10.0f, 10.0f)); m_mice[ENG_MOUSE_MOVEV] = EngineMouse(12, 13, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 5.0f, 11.0f)); m_mice[ENG_MOUSE_MOVEH] = EngineMouse(14, 15, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point(11.0f, 5.0f)); m_mice[ENG_MOUSE_MOVED] = EngineMouse(16, 17, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 9.0f, 9.0f)); m_mice[ENG_MOUSE_MOVEI] = EngineMouse(18, 19, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 9.0f, 9.0f)); m_mice[ENG_MOUSE_MOVE] = EngineMouse(20, 21, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point(11.0f, 11.0f)); m_mice[ENG_MOUSE_TARGET] = EngineMouse(22, 23, -1, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point(15.0f, 15.0f)); m_mice[ENG_MOUSE_SCROLLL] = EngineMouse(24, 25, 43, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 2.0f, 9.0f)); m_mice[ENG_MOUSE_SCROLLR] = EngineMouse(26, 27, 44, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point(17.0f, 9.0f)); m_mice[ENG_MOUSE_SCROLLU] = EngineMouse(28, 29, 45, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 9.0f, 2.0f)); m_mice[ENG_MOUSE_SCROLLD] = EngineMouse(30, 31, 46, ENG_RSTATE_TTEXTURE_BLACK, ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 9.0f, 17.0f)); m_mouseSize = Math::Point(0.04f, 0.04f * (800.0f / 600.0f)); m_mouseType = ENG_MOUSE_NORM; m_fpsCounter = 0; m_lastFrameTime = GetSystemUtils()->CreateTimeStamp(); m_currentFrameTime = GetSystemUtils()->CreateTimeStamp(); m_defaultTexParams.format = TEX_IMG_AUTO; m_defaultTexParams.mipmap = true; m_defaultTexParams.minFilter = TEX_MIN_FILTER_LINEAR_MIPMAP_LINEAR; m_defaultTexParams.magFilter = TEX_MAG_FILTER_LINEAR; m_terrainTexParams.format = TEX_IMG_AUTO; m_terrainTexParams.mipmap = false; m_terrainTexParams.minFilter = TEX_MIN_FILTER_LINEAR; m_terrainTexParams.magFilter = TEX_MAG_FILTER_LINEAR; } CEngine::~CEngine() { m_app = nullptr; m_sound = nullptr; m_device = nullptr; m_text = nullptr; m_lightMan = nullptr; m_particle = nullptr; m_water = nullptr; m_cloud = nullptr; m_lightning = nullptr; m_planet = nullptr; m_terrain = nullptr; m_pause = nullptr; GetSystemUtils()->DestroyTimeStamp(m_lastFrameTime); m_lastFrameTime = nullptr; GetSystemUtils()->DestroyTimeStamp(m_currentFrameTime); m_currentFrameTime = nullptr; } void CEngine::SetDevice(CDevice *device) { m_device = device; } CDevice* CEngine::GetDevice() { return m_device; } CText* CEngine::GetText() { return m_text; } CLightManager* CEngine::GetLightManager() { return m_lightMan; } CParticle* CEngine::GetParticle() { return m_particle; } CTerrain* CEngine::GetTerrain() { return m_terrain; } CWater* CEngine::GetWater() { return m_water; } CLightning* CEngine::GetLightning() { return m_lightning; } CPlanet* CEngine::GetPlanet() { return m_planet; } CCloud* CEngine::GetCloud() { return m_cloud; } void CEngine::SetTerrain(CTerrain* terrain) { m_terrain = terrain; } bool CEngine::Create() { m_size = m_app->GetVideoConfig().size; m_lightMan = new CLightManager(this); m_text = new CText(this); m_particle = new CParticle(this); m_water = new CWater(this); m_cloud = new CCloud(this); m_lightning = new CLightning(this); m_planet = new CPlanet(this); m_pause = new CPauseManager(); m_lightMan->SetDevice(m_device); m_particle->SetDevice(m_device); 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; } m_device->SetClearColor(Color(0.0f, 0.0f, 0.0f, 0.0f)); m_device->SetShadeModel(SHADE_SMOOTH); m_device->SetFillMode(FILL_POLY); SetFocus(m_focus); m_matWorldInterface.LoadIdentity(); m_matViewInterface.LoadIdentity(); Math::LoadOrthoProjectionMatrix(m_matProjInterface, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); TextureCreateParams params; params.format = TEX_IMG_AUTO; params.minFilter = TEX_MIN_FILTER_NEAREST; params.magFilter = TEX_MAG_FILTER_NEAREST; params.mipmap = false; m_miceTexture = LoadTexture("textures/interface/mouse.png", params); GetSystemUtils()->GetCurrentTimeStamp(m_currentFrameTime); GetSystemUtils()->GetCurrentTimeStamp(m_lastFrameTime); return true; } void CEngine::Destroy() { m_text->Destroy(); delete m_pause; m_pause = nullptr; delete m_lightMan; m_lightMan = nullptr; delete m_text; m_text = nullptr; delete m_particle; m_particle = nullptr; delete m_water; m_water = nullptr; delete m_cloud; m_cloud = nullptr; delete m_lightning; m_lightning = nullptr; delete m_planet; m_planet = nullptr; } void CEngine::ResetAfterDeviceChanged() { m_size = m_app->GetVideoConfig().size; m_text->FlushCache(); FlushTextureCache(); LoadAllTextures(); } bool CEngine::ProcessEvent(const Event &event) { if (event.type == EVENT_KEY_DOWN) { if (event.key.key == KEY(F12)) { m_showStats = !m_showStats; return false; } if (event.key.key == KEY(F11)) { m_debugLights = !m_debugLights; return false; } if (event.key.key == KEY(F10)) { m_debugDumpLights = true; return false; } } // By default, pass on all events return true; } void CEngine::FrameUpdate() { m_fpsCounter++; GetSystemUtils()->GetCurrentTimeStamp(m_currentFrameTime); float diff = GetSystemUtils()->TimeStampDiff(m_lastFrameTime, m_currentFrameTime, STU_SEC); if (diff > 1.0f) { GetSystemUtils()->CopyTimeStamp(m_lastFrameTime, m_currentFrameTime); m_fps = m_fpsCounter / diff; m_fpsCounter = 0; if (m_showStats) { std::stringstream str; str << "FPS: "; str.precision(2); str.setf(std::ios_base::fixed); str << m_fps; m_fpsText = str.str(); } } float rTime = m_app->GetRelTime(); m_lightMan->UpdateProgression(rTime); m_app->StartPerformanceCounter(PCNT_UPDATE_PARTICLE); m_particle->FrameParticle(rTime); m_app->StopPerformanceCounter(PCNT_UPDATE_PARTICLE); ComputeDistance(); UpdateGeometry(); UpdateStaticBuffers(); m_highlightTime = m_app->GetAbsTime(); if (m_groundMark.draw) { 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; m_groundMark.phase = ENG_GR_MARK_PHASE_FIX; } } 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) m_groundMark.phase = ENG_GR_MARK_PHASE_DEC; } 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; m_groundMark.phase = ENG_GR_MARK_PHASE_NULL; m_groundMark.draw = false; } } } } bool CEngine::WriteScreenShot(const std::string& fileName, int width, int height) { void *pixels = m_device->GetFrameBufferPixels(); CImage img({width,height}); img.SetDataPixels(pixels); img.flipVertically(); if ( img.SavePNG(fileName.c_str()) ){ GetLogger()->Info("Save SceenShot Saved Successfully!\n"); return true; } else{ GetLogger()->Error("%s!\n",img.GetError().c_str()); return false; } } bool CEngine::GetPause() { return m_pause->GetPause(); } void CEngine::SetMovieLock(bool lock) { m_movieLock = lock; } bool CEngine::GetMovieLock() { return m_movieLock; } void CEngine::SetShowStats(bool show) { m_showStats = show; } bool CEngine::GetShowStats() { return m_showStats; } void CEngine::SetRenderEnable(bool enable) { m_render = enable; } Math::IntPoint CEngine::GetWindowSize() { return m_size; } Math::Point CEngine::WindowToInterfaceCoords(Math::IntPoint pos) { return Math::Point( static_cast(pos.x) / static_cast(m_size.x), 1.0f - static_cast(pos.y) / static_cast(m_size.y) ); } Math::IntPoint CEngine::InterfaceToWindowCoords(Math::Point pos) { return Math::IntPoint(static_cast(pos.x * m_size.x), static_cast((1.0f - pos.y) * m_size.y)); } Math::Point CEngine::WindowToInterfaceSize(Math::IntPoint size) { return Math::Point(static_cast(size.x) / static_cast(m_size.x), static_cast(size.y) / static_cast(m_size.y)); } Math::IntPoint CEngine::InterfaceToWindowSize(Math::Point size) { return Math::IntPoint(static_cast(size.x * m_size.x), static_cast(size.y * m_size.y)); } void CEngine::AddStatisticTriangle(int count) { m_statisticTriangle += count; } int CEngine::GetStatisticTriangle() { return m_statisticTriangle; } /******************************************************* Object management *******************************************************/ EngineBaseObjTexTier& CEngine::AddLevel2(EngineBaseObject& p1, const std::string& tex1Name, const std::string& tex2Name) { for (int i = 0; i < static_cast( p1.next.size() ); i++) { if (p1.next[i].tex1Name == tex1Name && p1.next[i].tex2Name == tex2Name) return p1.next[i]; } p1.next.push_back(EngineBaseObjTexTier(tex1Name, tex2Name)); return p1.next.back(); } EngineBaseObjLODTier& CEngine::AddLevel3(EngineBaseObjTexTier& p2, LODLevel lodLevel) { for (int i = 0; i < static_cast( p2.next.size() ); i++) { if (p2.next[i].lodLevel == lodLevel) return p2.next[i]; } p2.next.push_back(EngineBaseObjLODTier(lodLevel)); return p2.next.back(); } EngineBaseObjDataTier& CEngine::AddLevel4(EngineBaseObjLODTier& p3, EngineTriangleType type, const Material& material, int state) { for (int i = 0; i < static_cast( p3.next.size() ); i++) { if ( (p3.next[i].type == type) && (p3.next[i].material == material) && (p3.next[i].state == state) ) return p3.next[i]; } p3.next.push_back(EngineBaseObjDataTier(type, material, state)); return p3.next.back(); } int CEngine::CreateBaseObject() { int baseObjRank = 0; for ( ; baseObjRank < static_cast( m_baseObjects.size() ); baseObjRank++) { if (! m_baseObjects[baseObjRank].used) { m_baseObjects[baseObjRank].LoadDefault(); break; } } if (baseObjRank == static_cast( m_baseObjects.size() )) m_baseObjects.push_back(EngineBaseObject()); else m_baseObjects[baseObjRank].LoadDefault(); m_baseObjects[baseObjRank].used = true; return baseObjRank; } void CEngine::DeleteBaseObject(int baseObjRank) { assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; if (! p1.used) return; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; m_device->DestroyStaticBuffer(p4.staticBufferId); p4.staticBufferId = 0; } } } p1.next.clear(); p1.used = false; } void CEngine::DeleteAllBaseObjects() { for (int baseObjRank = 0; baseObjRank < static_cast( m_baseObjects.size() ); baseObjRank++) { EngineBaseObject& p1 = m_baseObjects[baseObjRank]; if (! p1.used) continue; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; m_device->DestroyStaticBuffer(p4.staticBufferId); p4.staticBufferId = 0; } } } } m_baseObjects.clear(); } void CEngine::CopyBaseObject(int sourceBaseObjRank, int destBaseObjRank) { assert(sourceBaseObjRank >= 0 && sourceBaseObjRank < static_cast( m_baseObjects.size() )); assert(destBaseObjRank >= 0 && destBaseObjRank < static_cast( m_baseObjects.size() )); m_baseObjects[destBaseObjRank] = m_baseObjects[sourceBaseObjRank]; EngineBaseObject& p1 = m_baseObjects[destBaseObjRank]; if (! p1.used) return; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; p4.staticBufferId = 0; } } } } void CEngine::AddBaseObjTriangles(int baseObjRank, const std::vector& vertices, EngineTriangleType triangleType, const Material& material, int state, std::string tex1Name, std::string tex2Name, LODLevel lodLevel, bool globalUpdate) { assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; EngineBaseObjTexTier& p2 = AddLevel2(p1, tex1Name, tex2Name); EngineBaseObjLODTier& p3 = AddLevel3(p2, lodLevel); EngineBaseObjDataTier& p4 = AddLevel4(p3, triangleType, material, state); p4.vertices.insert(p4.vertices.end(), vertices.begin(), vertices.end()); p4.updateStaticBuffer = true; m_updateStaticBuffers = true; if (globalUpdate) { m_updateGeometry = true; } else { for (int i = 0; i < static_cast( vertices.size() ); i++) { p1.bboxMin.x = Math::Min(vertices[i].coord.x, p1.bboxMin.x); p1.bboxMin.y = Math::Min(vertices[i].coord.y, p1.bboxMin.y); p1.bboxMin.z = Math::Min(vertices[i].coord.z, p1.bboxMin.z); p1.bboxMax.x = Math::Max(vertices[i].coord.x, p1.bboxMax.x); p1.bboxMax.y = Math::Max(vertices[i].coord.y, p1.bboxMax.y); p1.bboxMax.z = Math::Max(vertices[i].coord.z, p1.bboxMax.z); } p1.radius = Math::Max(p1.bboxMin.Length(), p1.bboxMax.Length()); } if (triangleType == ENG_TRIANGLE_TYPE_TRIANGLES) p1.totalTriangles += vertices.size() / 3; else p1.totalTriangles += vertices.size() - 2; } void CEngine::AddBaseObjQuick(int baseObjRank, const EngineBaseObjDataTier& buffer, std::string tex1Name, std::string tex2Name, LODLevel lodLevel, bool globalUpdate) { assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; EngineBaseObjTexTier& p2 = AddLevel2(p1, tex1Name, tex2Name); EngineBaseObjLODTier& p3 = AddLevel3(p2, lodLevel); p3.next.push_back(buffer); EngineBaseObjDataTier& p4 = p3.next.back(); UpdateStaticBuffer(p4); if (globalUpdate) { m_updateGeometry = true; } else { for (int i = 0; i < static_cast( p4.vertices.size() ); i++) { p1.bboxMin.x = Math::Min(p4.vertices[i].coord.x, p1.bboxMin.x); p1.bboxMin.y = Math::Min(p4.vertices[i].coord.y, p1.bboxMin.y); p1.bboxMin.z = Math::Min(p4.vertices[i].coord.z, p1.bboxMin.z); p1.bboxMax.x = Math::Max(p4.vertices[i].coord.x, p1.bboxMax.x); p1.bboxMax.y = Math::Max(p4.vertices[i].coord.y, p1.bboxMax.y); p1.bboxMax.z = Math::Max(p4.vertices[i].coord.z, p1.bboxMax.z); } p1.radius = Math::Max(p1.bboxMin.Length(), p1.bboxMax.Length()); } if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) p1.totalTriangles += p4.vertices.size() / 3; else if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) p1.totalTriangles += p4.vertices.size() - 2; } void CEngine::DebugObject(int objRank) { assert(objRank >= 0 && objRank < static_cast( 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); l->Debug(" transparency = %f\n", m_objects[objRank].transparency); l->Debug(" baseObj:\n"); int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) { l->Debug(" null\n"); return; } assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; if (!p1.used) { l->Debug(" not used\n"); return; } std::string vecStr; vecStr = p1.bboxMin.ToString(); l->Debug(" bboxMin: %s\n", vecStr.c_str()); vecStr = p1.bboxMax.ToString(); l->Debug(" bboxMax: %s\n", vecStr.c_str()); l->Debug(" totalTriangles: %d\n", p1.totalTriangles); l->Debug(" radius: %f\n", p1.radius); for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& 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( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; l->Debug(" l3:\n"); l->Debug(" lodLevel: %d\n", p3.lodLevel); for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; l->Debug(" l4:\n"); l->Debug(" type: %d\n", p4.type); l->Debug(" state: %d\n", p4.state); l->Debug(" staticBufferId: %u\n", p4.staticBufferId); l->Debug(" updateStaticBuffer: %s\n", p4.updateStaticBuffer ? "true" : "false"); } } } } int CEngine::CreateObject() { int objRank = 0; for ( ; objRank < static_cast( m_objects.size() ); objRank++) { if (! m_objects[objRank].used) { m_objects[objRank].LoadDefault(); break; } } if (objRank == static_cast( m_objects.size() )) m_objects.push_back(EngineObject()); m_objects[objRank].used = true; Math::Matrix mat; mat.LoadIdentity(); SetObjectTransform(objRank, mat); m_objects[objRank].drawWorld = true; m_objects[objRank].distance = 0.0f; m_objects[objRank].baseObjRank = -1; m_objects[objRank].shadowRank = -1; return objRank; } void CEngine::DeleteAllObjects() { m_objects.clear(); m_shadows.clear(); DeleteAllGroundSpots(); } void CEngine::DeleteObject(int objRank) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); // Mark object as deleted m_objects[objRank].used = false; // Delete associated shadows DeleteShadow(objRank); } void CEngine::SetObjectBaseRank(int objRank, int baseObjRank) { assert(objRank == -1 || (objRank >= 0 && objRank < static_cast( m_objects.size() ))); m_objects[objRank].baseObjRank = baseObjRank; } int CEngine::GetObjectBaseRank(int objRank) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); return m_objects[objRank].baseObjRank; } void CEngine::SetObjectType(int objRank, EngineObjectType type) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); m_objects[objRank].type = type; } EngineObjectType CEngine::GetObjectType(int objRank) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); return m_objects[objRank].type; } void CEngine::SetObjectTransform(int objRank, const Math::Matrix& transform) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); m_objects[objRank].transform = transform; } void CEngine::GetObjectTransform(int objRank, Math::Matrix& transform) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); transform = m_objects[objRank].transform; } void CEngine::SetObjectDrawWorld(int objRank, bool draw) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); m_objects[objRank].drawWorld = draw; } void CEngine::SetObjectDrawFront(int objRank, bool draw) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); m_objects[objRank].drawFront = draw; } void CEngine::SetObjectTransparency(int objRank, float value) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); m_objects[objRank].transparency = value; } void CEngine::GetObjectBBox(int objRank, Math::Vector& min, Math::Vector& max) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) return; assert(baseObjRank >= 0 && baseObjRank < static_cast(m_baseObjects.size())); min = m_baseObjects[baseObjRank].bboxMin; max = m_baseObjects[baseObjRank].bboxMax; } int CEngine::GetObjectTotalTriangles(int objRank) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) return 0; assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); return m_baseObjects[baseObjRank].totalTriangles; } EngineBaseObjDataTier* CEngine::FindTriangles(int objRank, const Material& material, int state, std::string tex1Name, std::string tex2Name, int lodLevelMask) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) return nullptr; assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; if (p2.tex1Name != tex1Name) continue; for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; if ((p3.lodLevel & lodLevelMask) == 0) continue; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; if ( (p4.state & (~(ENG_RSTATE_DUAL_BLACK|ENG_RSTATE_DUAL_WHITE))) != state || p4.material != material ) continue; return &p4; } } } return nullptr; } int CEngine::GetPartialTriangles(int objRank, int lodLevelMask, float percent, int maxCount, std::vector& triangles) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) return 0; assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; int total = p1.totalTriangles; int expectedCount = static_cast(percent * total); triangles.reserve(Math::Min(maxCount, expectedCount)); int actualCount = 0; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; if ((p3.lodLevel & lodLevelMask) == 0) continue; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) { for (int i = 0; i < static_cast( p4.vertices.size() ); i += 3) { if (static_cast(actualCount) / total >= percent) break; if (actualCount >= maxCount) break; EngineTriangle t; t.triangle[0] = p4.vertices[i]; t.triangle[1] = p4.vertices[i+1]; t.triangle[2] = p4.vertices[i+2]; t.material = p4.material; t.state = p4.state; t.tex1Name = p2.tex1Name; t.tex2Name = p2.tex2Name; triangles.push_back(t); ++actualCount; } } else if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) { for (int i = 0; i < static_cast( p4.vertices.size() ); i += 1) { if (static_cast(actualCount) / total >= percent) break; if (actualCount >= maxCount) break; EngineTriangle t; t.triangle[0] = p4.vertices[i]; t.triangle[1] = p4.vertices[i+1]; t.triangle[2] = p4.vertices[i+2]; t.material = p4.material; t.state = p4.state; t.tex1Name = p2.tex1Name; t.tex2Name = p2.tex2Name; triangles.push_back(t); ++actualCount; } } } } } return actualCount; } void CEngine::ChangeSecondTexture(int objRank, const std::string& tex2Name) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) return; assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { if (p1.next[l2].tex2Name == tex2Name) continue; // already new std::string tex1Name = p1.next[l2].tex1Name; EngineBaseObjTexTier& newP2 = AddLevel2(p1, tex1Name, tex2Name); newP2.next.insert(newP2.next.end(), p1.next[l2].next.begin(), p1.next[l2].next.end()); p1.next[l2].next.clear(); if (!newP2.tex1.Valid()) newP2.tex1 = LoadTexture("textures/"+newP2.tex1Name); if (!newP2.tex2.Valid()) newP2.tex2 = LoadTexture("textures/"+newP2.tex2Name); } } void CEngine::ChangeTextureMapping(int objRank, const Material& mat, int state, const std::string& tex1Name, const std::string& tex2Name, int lodLevelMask, EngineTextureMapping mode, float au, float bu, float av, float bv) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); EngineBaseObjDataTier* p4 = FindTriangles(objRank, mat, state, tex1Name, tex2Name, lodLevelMask); if (p4 == nullptr) return; int nb = p4->vertices.size(); if (mode == ENG_TEX_MAPPING_X) { for (int i = 0; i < nb; i++) { p4->vertices[i].texCoord.x = p4->vertices[i].coord.z * au + bu; p4->vertices[i].texCoord.y = p4->vertices[i].coord.y * av + bv; } } else if (mode == ENG_TEX_MAPPING_Y) { for (int i = 0; i < nb; i++) { p4->vertices[i].texCoord.x = p4->vertices[i].coord.x * au + bu; p4->vertices[i].texCoord.y = p4->vertices[i].coord.z * av + bv; } } else if (mode == ENG_TEX_MAPPING_Z) { for (int i = 0; i < nb; i++) { p4->vertices[i].texCoord.x = p4->vertices[i].coord.x * au + bu; p4->vertices[i].texCoord.y = p4->vertices[i].coord.y * av + bv; } } else if (mode == ENG_TEX_MAPPING_1X) { for (int i = 0; i < nb; i++) { p4->vertices[i].texCoord.x = p4->vertices[i].coord.x * au + bu; } } else if (mode == ENG_TEX_MAPPING_1Y) { for (int i = 0; i < nb; i++) { p4->vertices[i].texCoord.y = p4->vertices[i].coord.y * au + bu; } } else if (mode == ENG_TEX_MAPPING_1Z) { for (int i = 0; i < nb; i++) { p4->vertices[i].texCoord.x = p4->vertices[i].coord.z * au + bu; } } UpdateStaticBuffer(*p4); } void CEngine::TrackTextureMapping(int objRank, const Material& mat, int state, const std::string& tex1Name, const std::string& tex2Name, int lodLevelMask, EngineTextureMapping mode, float pos, float factor, float tl, float ts, float tt) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); EngineBaseObjDataTier* p4 = FindTriangles(objRank, mat, state, tex1Name, tex2Name, lodLevelMask); if (p4 == nullptr) return; int tNum = p4->vertices.size(); if (tNum < 12 || tNum % 6 != 0) return; std::vector& vs = p4->vertices; while (pos < 0.0f) pos += 1.0f; // never negative! Math::Vector current; for (int i = 0; i < 6; i++) { for (int j = 0; j < 6; j++) { if (Math::IsEqual(vs[i].coord.x, vs[j+6].coord.x) && Math::IsEqual(vs[i].coord.y, vs[j+6].coord.y)) { current.x = vs[i].coord.x; // position end link current.y = vs[i].coord.y; break; } } } float ps = 0.0f; // start position on the periphery float pe = 0.0f; int is[6] = { 0 }, ie[6] = { 0 }; int tBase = 0; for (int ti = 0; ti < tNum / 6; ti++) { int s = 0; int e = 0; for (int i = 0; i < 6; i++) { if (Math::IsEqual(vs[tBase + i].coord.x, current.x, 0.0001f) && Math::IsEqual(vs[tBase + i].coord.y, current.y, 0.0001f)) { ie[e++] = i; } else { is[s++] = i; } } if (s == 3 && e == 3) { pe = ps + Math::Point(vs[tBase + is[0]].coord.x - vs[tBase + ie[0]].coord.x, vs[tBase + is[0]].coord.y - vs[tBase + ie[0]].coord.y).Length() / factor; // end position on the periphery float pps = ps + pos; float ppe = pe + pos; int offset = static_cast(pps); ppe -= offset; pps -= offset; for (int i = 0; i < 3; i++) { vs[tBase + is[i]].texCoord.x = ((pps * tl) + ts) / tt; vs[tBase + ie[i]].texCoord.x = ((ppe * tl) + ts) / tt; } } if (ti >= (tNum / 6) - 1) break; for (int i = 0; i < 6; i++) { if (!Math::IsEqual(vs[tBase + i+6].coord.x, current.x, 0.0001f) || !Math::IsEqual(vs[tBase + i+6].coord.y, current.y, 0.0001f)) { current.x = vs[tBase + i+6].coord.x; // end next link current.y = vs[tBase + i+6].coord.y; break; } } ps = pe; // following start position on the periphery tBase += 6; } UpdateStaticBuffer(*p4); } void CEngine::CreateShadow(int objRank) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); // Already allocated? if (m_objects[objRank].shadowRank != -1) return; int index = 0; for ( ; index < static_cast( m_shadows.size() ); index++) { if (! m_shadows[index].used) { m_shadows[index].LoadDefault(); break; } } if (index == static_cast( m_shadows.size() )) m_shadows.push_back(EngineShadow()); m_shadows[index].used = true; m_shadows[index].objRank = objRank; m_shadows[index].height = 0.0f; m_objects[objRank].shadowRank = index; } void CEngine::DeleteShadow(int objRank) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int shadowRank = m_objects[objRank].shadowRank; if (shadowRank == -1) return; assert(shadowRank >= 0 && shadowRank < static_cast( m_shadows.size() )); m_shadows[shadowRank].used = false; m_shadows[shadowRank].objRank = -1; m_objects[objRank].shadowRank = -1; } void CEngine::SetObjectShadowHide(int objRank, bool hide) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int shadowRank = m_objects[objRank].shadowRank; if (shadowRank == -1) return; assert(shadowRank >= 0 && shadowRank < static_cast( m_shadows.size() )); m_shadows[shadowRank].hide = hide; } void CEngine::SetObjectShadowType(int objRank, EngineShadowType type) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int shadowRank = m_objects[objRank].shadowRank; if (shadowRank == -1) return; assert(shadowRank >= 0 && shadowRank < static_cast( m_shadows.size() )); m_shadows[shadowRank].type = type; } void CEngine::SetObjectShadowPos(int objRank, const Math::Vector& pos) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int shadowRank = m_objects[objRank].shadowRank; if (shadowRank == -1) return; assert(shadowRank >= 0 && shadowRank < static_cast( m_shadows.size() )); m_shadows[shadowRank].pos = pos; } void CEngine::SetObjectShadowNormal(int objRank, const Math::Vector& normal) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int shadowRank = m_objects[objRank].shadowRank; if (shadowRank == -1) return; assert(shadowRank >= 0 && shadowRank < static_cast( m_shadows.size() )); m_shadows[shadowRank].normal = normal; } void CEngine::SetObjectShadowAngle(int objRank, float angle) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int shadowRank = m_objects[objRank].shadowRank; if (shadowRank == -1) return; assert(shadowRank >= 0 && shadowRank < static_cast( m_shadows.size() )); m_shadows[shadowRank].angle = angle; } void CEngine::SetObjectShadowRadius(int objRank, float radius) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int shadowRank = m_objects[objRank].shadowRank; if (shadowRank == -1) return; assert(shadowRank >= 0 && shadowRank < static_cast( m_shadows.size() )); m_shadows[shadowRank].radius = radius; } void CEngine::SetObjectShadowIntensity(int objRank, float intensity) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int shadowRank = m_objects[objRank].shadowRank; if (shadowRank == -1) return; assert(shadowRank >= 0 && shadowRank < static_cast( m_shadows.size() )); m_shadows[shadowRank].intensity = intensity; } void CEngine::SetObjectShadowHeight(int objRank, float height) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int shadowRank = m_objects[objRank].shadowRank; if (shadowRank == -1) return; assert(shadowRank >= 0 && shadowRank < static_cast( m_shadows.size() )); m_shadows[shadowRank].height = height; } float CEngine::GetObjectShadowRadius(int objRank) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); int shadowRank = m_objects[objRank].shadowRank; if (shadowRank == -1) return 0.0f; assert(shadowRank >= 0 && shadowRank < static_cast( m_shadows.size() )); return m_shadows[shadowRank].radius; } bool CEngine::GetHighlight(Math::Point &p1, Math::Point &p2) { p1 = m_highlightP1; p2 = m_highlightP2; return m_highlight; } void CEngine::SetHighlightRank(int *rankList) { int i = 0; while ( *rankList != -1 ) { m_highlightRank[i++] = *rankList++; } m_highlightRank[i] = -1; // terminator } bool CEngine::GetBBox2D(int objRank, Math::Point &min, Math::Point &max) { assert(objRank >= 0 && objRank < static_cast( 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; if (baseObjRank == -1) return false; assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; for (int i = 0; i < 8; i++) { Math::Vector p; 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; Math::Vector pp; 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(Math::IntPoint(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(); DeleteTexture(texName); Gfx::Texture tex = m_device->CreateTexture(&shadowImg, m_defaultTexParams); m_texNameMap[texName] = tex; m_revTexNameMap[tex] = texName; } } int CEngine::CreateGroundSpot() { int index = 0; for ( ; index < static_cast( m_groundSpots.size() ); index++) { if (! m_groundSpots[index].used) { m_groundSpots[index].LoadDefault(); break; } } m_groundSpots.push_back(EngineGroundSpot()); m_groundSpots[index].used = true; m_groundSpots[index].smooth = 1.0f; return index; } void CEngine::DeleteGroundSpot(int rank) { assert(rank >= 0 && rank < static_cast( m_groundSpots.size() )); m_groundSpots[rank].used = false; m_groundSpots[rank].pos = Math::Vector(0.0f, 0.0f, 0.0f); } void CEngine::SetObjectGroundSpotPos(int rank, const Math::Vector& pos) { assert(rank >= 0 && rank < static_cast( m_groundSpots.size() )); m_groundSpots[rank].pos = pos; } void CEngine::SetObjectGroundSpotRadius(int rank, float radius) { assert(rank >= 0 && rank < static_cast( m_groundSpots.size() )); m_groundSpots[rank].radius = radius; } void CEngine::SetObjectGroundSpotColor(int rank, const Color& color) { assert(rank >= 0 && rank < static_cast( m_groundSpots.size() )); m_groundSpots[rank].color = color; } void CEngine::SetObjectGroundSpotMinMax(int rank, float min, float max) { assert(rank >= 0 && rank < static_cast( 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( m_groundSpots.size() )); m_groundSpots[rank].smooth = smooth; } void CEngine::CreateGroundMark(Math::Vector pos, float radius, float delay1, float delay2, float delay3, int dx, int dy, char* table) { m_groundMark.LoadDefault(); m_groundMark.draw = true; 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; } void CEngine::DeleteGroundMark(int rank) { m_groundMark.LoadDefault(); } void CEngine::ComputeDistance() { for (int i = 0; i < static_cast( m_objects.size() ); i++) { if (! m_objects[i].used) continue; Math::Vector v; v.x = m_eyePt.x - m_objects[i].transform.Get(1, 4); v.y = m_eyePt.y - m_objects[i].transform.Get(2, 4); v.z = m_eyePt.z - m_objects[i].transform.Get(3, 4); m_objects[i].distance = v.Length(); } } void CEngine::UpdateGeometry() { if (! m_updateGeometry) return; for (int baseObjRank = 0; baseObjRank < static_cast( m_baseObjects.size() ); baseObjRank++) { EngineBaseObject &p1 = m_baseObjects[baseObjRank]; if (! p1.used) continue; p1.bboxMin.LoadZero(); p1.bboxMax.LoadZero(); p1.radius = 0; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; for (int i = 0; i < static_cast( p4.vertices.size() ); i++) { p1.bboxMin.x = Math::Min(p4.vertices[i].coord.x, p1.bboxMin.x); p1.bboxMin.y = Math::Min(p4.vertices[i].coord.y, p1.bboxMin.y); p1.bboxMin.z = Math::Min(p4.vertices[i].coord.z, p1.bboxMin.z); p1.bboxMax.x = Math::Max(p4.vertices[i].coord.x, p1.bboxMax.x); p1.bboxMax.y = Math::Max(p4.vertices[i].coord.y, p1.bboxMax.y); p1.bboxMax.z = Math::Max(p4.vertices[i].coord.z, p1.bboxMax.z); } p1.radius = Math::Max(p1.bboxMin.Length(), p1.bboxMax.Length()); } } } } m_updateGeometry = false; } void CEngine::UpdateStaticBuffer(EngineBaseObjDataTier& p4) { PrimitiveType type; if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) type = PRIMITIVE_TRIANGLES; else type = PRIMITIVE_TRIANGLE_STRIP; if (p4.staticBufferId == 0) p4.staticBufferId = m_device->CreateStaticBuffer(type, &p4.vertices[0], p4.vertices.size()); else m_device->UpdateStaticBuffer(p4.staticBufferId, type, &p4.vertices[0], p4.vertices.size()); p4.updateStaticBuffer = false; } void CEngine::UpdateStaticBuffers() { if (!m_updateStaticBuffers) return; m_updateStaticBuffers = false; for (int baseObjRank = 0; baseObjRank < static_cast( m_baseObjects.size() ); baseObjRank++) { EngineBaseObject& p1 = m_baseObjects[baseObjRank]; if (! p1.used) continue; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; if (! p4.updateStaticBuffer) continue; UpdateStaticBuffer(p4); } } } } } void CEngine::Update() { ComputeDistance(); UpdateGeometry(); UpdateStaticBuffers(); } bool CEngine::DetectBBox(int objRank, Math::Point mouse) { assert(objRank >= 0 && objRank < static_cast(m_objects.size())); int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) return false; assert(baseObjRank >= 0 && baseObjRank < static_cast(m_baseObjects.size())); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; Math::Point 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++) { Math::Vector p; 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; Math::Vector pp; 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(Math::Point mouse) { float min = 1000000.0f; int nearest = -1; for (int objRank = 0; objRank < static_cast( m_objects.size() ); objRank++) { if (! m_objects[objRank].used) continue; if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN) continue; if (! DetectBBox(objRank, mouse)) continue; int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) continue; assert(baseObjRank >= 0 && baseObjRank < static_cast(m_baseObjects.size())); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; if (! p1.used) continue; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; if (p3.lodLevel != LOD_Constant && p3.lodLevel != LOD_High) continue; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) { for (int i = 0; i < static_cast( p4.vertices.size() ); i += 3) { float dist = 0.0f; if (DetectTriangle(mouse, &p4.vertices[i], objRank, dist) && dist < min) { min = dist; nearest = objRank; } } } else if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) { for (int i = 0; i < static_cast( p4.vertices.size() ) - 2; i += 1) { float dist = 0.0f; if (DetectTriangle(mouse, &p4.vertices[i], objRank, dist) && dist < min) { min = dist; nearest = objRank; } } } } } } } return nearest; } bool CEngine::DetectTriangle(Math::Point mouse, VertexTex2* triangle, int objRank, float& dist) { assert(objRank >= 0 && objRank < static_cast(m_objects.size())); Math::Vector p2D[3], p3D; for (int i = 0; i < 3; i++) { p3D.x = triangle[i].coord.x; p3D.y = triangle[i].coord.y; p3D.z = triangle[i].coord.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; Math::Point 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; dist = (p2D[0].z + p2D[1].z + p2D[2].z) / 3.0f; return true; } //! Use only after world transform already set bool CEngine::IsVisible(int objRank) { assert(objRank >= 0 && objRank < static_cast(m_objects.size())); int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) return false; assert(baseObjRank >= 0 && baseObjRank < static_cast(m_baseObjects.size())); float radius = m_baseObjects[baseObjRank].radius; Math::Vector center(0.0f, 0.0f, 0.0f); if (m_device->ComputeSphereVisibility(center, radius) == Gfx::FRUSTUM_PLANE_ALL) { m_objects[objRank].visible = true; return true; } m_objects[objRank].visible = false; return false; } bool CEngine::IsWithinLODLimit(float distance, LODLevel lodLevel) { float min = 0.0f, max = 0.0f; if (lodLevel == LOD_Constant) { min = 0.0f; max = m_terrainVision * m_clippingDistance; } else { if (lodLevel == LOD_High) { min = 0.0f; max = 100.0f; } else if (lodLevel == LOD_Medium) { min = 100.0f; max = 200.0f; } else if (lodLevel == LOD_Low) { min = 100.0f; max = 1000000.0f; } min *= m_size.x / 640.0f; min *= 1.0f+m_objectDetail*2.0f; max *= m_size.x / 640.0f; max *= 1.0f+m_objectDetail*2.0f; } return distance >= min && distance < max; } bool CEngine::TransformPoint(Math::Vector& p2D, int objRank, Math::Vector p3D) { assert(objRank >= 0 && objRank < static_cast(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.Get(1,1); p2D.y = (p3D.y/p3D.z)*m_matProj.Get(2,2); 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::SetState(int state, const Color& color) { if (state == m_lastState && color == m_lastColor) return; m_lastState = state; m_lastColor = color; if (state & ENG_RSTATE_TTEXTURE_BLACK) // transparent black texture? { m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false); m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false); m_device->SetRenderState(RENDER_STATE_BLENDING, true); m_device->SetBlendFunc(BLEND_ONE, BLEND_INV_SRC_COLOR); TextureStageParams params; params.colorOperation = TEX_MIX_OPER_MODULATE; params.colorArg1 = TEX_MIX_ARG_TEXTURE; params.colorArg2 = TEX_MIX_ARG_FACTOR; params.alphaOperation = TEX_MIX_OPER_DEFAULT; params.factor = color; m_device->SetTextureEnabled(0, true); m_device->SetTextureStageParams(0, params); } else if (state & ENG_RSTATE_TTEXTURE_WHITE) // transparent white texture? { m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false); m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false); m_device->SetRenderState(RENDER_STATE_BLENDING, true); m_device->SetBlendFunc(BLEND_DST_COLOR, BLEND_ZERO); TextureStageParams params; params.colorOperation = TEX_MIX_OPER_ADD; params.colorArg1 = TEX_MIX_ARG_TEXTURE; params.colorArg2 = TEX_MIX_ARG_FACTOR; params.alphaOperation = TEX_MIX_OPER_DEFAULT; params.factor = color.Inverse(); m_device->SetTextureEnabled(0, true); m_device->SetTextureStageParams(0, params); } else if (state & ENG_RSTATE_TCOLOR_BLACK) // transparent black color? { m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false); m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false); m_device->SetRenderState(RENDER_STATE_BLENDING, true); m_device->SetBlendFunc(BLEND_ONE, BLEND_INV_SRC_COLOR); m_device->SetTextureEnabled(0, false); } else if (state & ENG_RSTATE_TCOLOR_WHITE) // transparent white color? { m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false); m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false); m_device->SetRenderState(RENDER_STATE_BLENDING, true); m_device->SetBlendFunc(BLEND_DST_COLOR, BLEND_ZERO); m_device->SetTextureEnabled(0, false); } else if (state & ENG_RSTATE_TDIFFUSE) // diffuse color as transparent? { m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false); m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false); m_device->SetRenderState(RENDER_STATE_BLENDING, true); m_device->SetBlendFunc(BLEND_SRC_ALPHA, BLEND_DST_ALPHA); TextureStageParams params; params.colorOperation = TEX_MIX_OPER_REPLACE; params.colorArg1 = TEX_MIX_ARG_TEXTURE; params.alphaOperation = TEX_MIX_OPER_DEFAULT; m_device->SetTextureEnabled(0, true); m_device->SetTextureStageParams(0, params); } else if (state & ENG_RSTATE_OPAQUE_TEXTURE) // opaque texture ? { m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false); m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false); m_device->SetRenderState(RENDER_STATE_BLENDING, false); m_device->SetTextureEnabled(0, true); m_device->SetTextureStageParams(0, TextureStageParams()); // default operation } else if (state & ENG_RSTATE_OPAQUE_COLOR) // opaque color ? { m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false); m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false); m_device->SetRenderState(RENDER_STATE_BLENDING, false); m_device->SetTextureEnabled(0, false); } else if (state & ENG_RSTATE_TEXT) // font rendering? { m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false); m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false); m_device->SetRenderState(RENDER_STATE_BLENDING, true); m_device->SetBlendFunc(BLEND_SRC_ALPHA, BLEND_INV_SRC_ALPHA); m_device->SetTextureEnabled(0, true); m_device->SetTextureStageParams(0, TextureStageParams()); // default operation } else if (state & ENG_RSTATE_ALPHA) // image with alpha channel? { m_device->SetRenderState(RENDER_STATE_BLENDING, false); m_device->SetRenderState(RENDER_STATE_FOG, true); m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true); m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, true); m_device->SetAlphaTestFunc(COMP_FUNC_GREATER, 0.5f); TextureStageParams params; params.colorOperation = TEX_MIX_OPER_MODULATE; params.colorArg1 = TEX_MIX_ARG_TEXTURE; params.colorArg2 = TEX_MIX_ARG_SRC_COLOR; params.alphaOperation = TEX_MIX_OPER_REPLACE; params.alphaArg1 = TEX_MIX_ARG_TEXTURE; params.factor = color; m_device->SetTextureEnabled(0, true); m_device->SetTextureStageParams(0, params); } else // normal ? { m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, false); m_device->SetRenderState(RENDER_STATE_BLENDING, false); m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true); m_device->SetRenderState(RENDER_STATE_FOG, true); TextureStageParams params; params.colorOperation = TEX_MIX_OPER_DEFAULT; // default modulate params.alphaOperation = TEX_MIX_OPER_DEFAULT; m_device->SetTextureEnabled(0, true); m_device->SetTextureStageParams(0, params); } if (state & ENG_RSTATE_FOG) m_device->SetRenderState(RENDER_STATE_FOG, true); bool second = m_groundSpotVisible || m_dirty; if ( !m_groundSpotVisible && (state & ENG_RSTATE_SECOND) != 0 ) second = false; if ( !m_dirty && (state & ENG_RSTATE_SECOND) == 0 ) second = false; if ((state & ENG_RSTATE_DUAL_BLACK) && second) { TextureStageParams params; params.colorOperation = TEX_MIX_OPER_MODULATE; params.colorArg1 = TEX_MIX_ARG_TEXTURE; params.colorArg2 = TEX_MIX_ARG_COMPUTED_COLOR; params.alphaOperation = TEX_MIX_OPER_DEFAULT; m_device->SetTextureEnabled(1, true); m_device->SetTextureStageParams(1, params); } else if ((state & ENG_RSTATE_DUAL_WHITE) && second) { TextureStageParams params; params.colorOperation = TEX_MIX_OPER_ADD; params.colorArg1 = TEX_MIX_ARG_TEXTURE; params.colorArg2 = TEX_MIX_ARG_COMPUTED_COLOR; params.alphaOperation = TEX_MIX_OPER_DEFAULT; m_device->SetTextureEnabled(1, true); m_device->SetTextureStageParams(1, params); } else { m_device->SetTextureEnabled(1, false); } if (state & ENG_RSTATE_WRAP) { m_device->SetTextureStageWrap(0, TEX_WRAP_REPEAT, TEX_WRAP_REPEAT); m_device->SetTextureStageWrap(1, TEX_WRAP_REPEAT, TEX_WRAP_REPEAT); } else // if (state & ENG_RSTATE_CLAMP) or otherwise { m_device->SetTextureStageWrap(0, TEX_WRAP_CLAMP, TEX_WRAP_CLAMP); m_device->SetTextureStageWrap(1, TEX_WRAP_CLAMP, TEX_WRAP_CLAMP); } if (state & ENG_RSTATE_2FACE) { m_device->SetRenderState(RENDER_STATE_CULLING, false); } else { m_device->SetRenderState(RENDER_STATE_CULLING, true); m_device->SetCullMode(CULL_CCW); } if (state & ENG_RSTATE_LIGHT) m_device->SetGlobalAmbient(Color(1.0f, 1.0f, 1.0f, 1.0f)); else m_device->SetGlobalAmbient(m_ambientColor[m_rankView]); // In interface mode, disable lighting if (m_interfaceMode) m_device->SetRenderState(RENDER_STATE_LIGHTING, false); } void CEngine::SetMaterial(const Material& mat) { m_lastMaterial = mat; m_device->SetMaterial(mat); } void CEngine::SetViewParams(const Math::Vector& eyePt, const Math::Vector& lookatPt, const Math::Vector& upVec, float eyeDistance) { 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()) return Texture(); // invalid texture Texture tex; CImage img; if (image == nullptr) { if (!img.Load(texName)) { 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; } 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; } Texture CEngine::LoadTexture(const std::string& name) { return LoadTexture(name, m_defaultTexParams); } Texture CEngine::LoadTexture(const std::string& name, CImage* image) { Texture tex = CreateTexture(name, m_defaultTexParams, image); return tex; } Texture CEngine::LoadTexture(const std::string& name, const TextureCreateParams& params) { if (m_texBlacklist.find(name) != m_texBlacklist.end()) return Texture(); std::map::iterator it = m_texNameMap.find(name); if (it != m_texNameMap.end()) return (*it).second; return CreateTexture(name, params); } bool CEngine::LoadAllTextures() { LoadTexture("textures/interface/text.png"); m_miceTexture = LoadTexture("textures/interface/mouse.png"); LoadTexture("textures/interface/button1.png"); LoadTexture("textures/interface/button2.png"); LoadTexture("textures/interface/button3.png"); LoadTexture("textures/effect00.png"); LoadTexture("textures/effect01.png"); LoadTexture("textures/effect02.png"); LoadTexture("textures/interface/map.png"); if (! m_backgroundName.empty()) { TextureCreateParams params = m_defaultTexParams; params.padToNearestPowerOfTwo = true; m_backgroundTex = LoadTexture("textures/"+m_backgroundName, params); } else m_backgroundTex.SetInvalid(); if (! m_foregroundName.empty()) m_foregroundTex = LoadTexture("textures/"+m_foregroundName); else m_foregroundTex.SetInvalid(); m_planet->LoadTexture(); bool ok = true; for (int objRank = 0; objRank < static_cast( 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; if (baseObjRank == -1) continue; assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; if (! p1.used) continue; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; if (! p2.tex1Name.empty()) { if (terrain) p2.tex1 = LoadTexture("textures/"+p2.tex1Name, m_terrainTexParams); else p2.tex1 = LoadTexture("textures/"+p2.tex1Name); if (! p2.tex1.Valid()) ok = false; } if (! p2.tex2Name.empty()) { if (terrain) p2.tex2 = LoadTexture("textures/"+p2.tex2Name, m_terrainTexParams); else p2.tex2 = LoadTexture("textures/"+p2.tex2Name); if (! p2.tex2.Valid()) ok = false; } } } return ok; } bool IsExcludeColor(Math::Point *exclude, int x, int y) { 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(exclude[i+0].x*256.0f) && x < static_cast(exclude[i+1].x*256.0f) && y >= static_cast(exclude[i+0].y*256.0f) && y < static_cast(exclude[i+1].y*256.0f) ) return true; // exclude i += 2; } return false; // point to include } bool CEngine::ChangeTextureColor(const std::string& texName, Color colorRef1, Color colorNew1, Color colorRef2, Color colorNew2, float tolerance1, float tolerance2, Math::Point ts, Math::Point ti, Math::Point *exclude, float shift, bool hsv) { if (colorRef1.r == colorNew1.r && colorRef1.g == colorNew1.g && colorRef1.b == colorNew1.b && colorRef2.r == colorNew2.r && colorRef2.g == colorNew2.g && colorRef2.b == colorNew2.b) return true; DeleteTexture(texName); CImage img; if (!img.Load(texName)) { 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 false; } int dx = img.GetSize().x; int dy = img.GetSize().y; int sx = static_cast(Math::Max(ts.x*dx, 0)); int sy = static_cast(Math::Max(ts.y*dy, 0)); int ex = static_cast(Math::Min(ti.x*dx, dx)); int ey = static_cast(Math::Min(ti.y*dy, dy)); ColorHSV cr1 = RGB2HSV(colorRef1); ColorHSV cn1 = RGB2HSV(colorNew1); ColorHSV cr2 = RGB2HSV(colorRef2); ColorHSV cn2 = RGB2HSV(colorNew2); for (int y = sy; y < ey; y++) { for (int x = sx; x < ex; x++) { if (exclude != nullptr && IsExcludeColor(exclude, x,y) ) continue; Color color = img.GetPixel(Math::IntPoint(x, y)); if (hsv) { ColorHSV c = RGB2HSV(color); if (c.s > 0.01f && fabs(c.h - cr1.h) < tolerance1) { c.h += cn1.h - cr1.h; c.s += cn1.s - cr1.s; c.v += cn1.v - cr1.v; if (c.h < 0.0f) c.h -= 1.0f; if (c.h > 1.0f) c.h += 1.0f; color = HSV2RGB(c); color.r = Math::Norm(color.r + shift); color.g = Math::Norm(color.g + shift); color.b = Math::Norm(color.b + shift); img.SetPixel(Math::IntPoint(x, y), color); } else if (tolerance2 != -1.0f && c.s > 0.01f && fabs(c.h - cr2.h) < tolerance2) { c.h += cn2.h - cr2.h; c.s += cn2.s - cr2.s; c.v += cn2.v - cr2.v; if (c.h < 0.0f) c.h -= 1.0f; if (c.h > 1.0f) c.h += 1.0f; color = HSV2RGB(c); color.r = Math::Norm(color.r + shift); color.g = Math::Norm(color.g + shift); color.b = Math::Norm(color.b + shift); img.SetPixel(Math::IntPoint(x, y), color); } } else { if ( fabs(color.r - colorRef1.r) + fabs(color.g - colorRef1.g) + fabs(color.b - colorRef1.b) < tolerance1 * 3.0f) { color.r = Math::Norm(colorNew1.r + color.r - colorRef1.r + shift); color.g = Math::Norm(colorNew1.g + color.g - colorRef1.g + shift); color.b = Math::Norm(colorNew1.b + color.b - colorRef1.b + shift); img.SetPixel(Math::IntPoint(x, y), color); } else if (tolerance2 != -1 && fabs(color.r - colorRef2.r) + fabs(color.g - colorRef2.g) + fabs(color.b - colorRef2.b) < tolerance2 * 3.0f) { color.r = Math::Norm(colorNew2.r + color.r - colorRef2.r + shift); color.g = Math::Norm(colorNew2.g + color.g - colorRef2.g + shift); color.b = Math::Norm(colorNew2.b + color.b - colorRef2.b + shift); img.SetPixel(Math::IntPoint(x, y), color); } } } } Texture tex = m_device->CreateTexture(&img, m_defaultTexParams); if (! tex.Valid()) { GetLogger()->Error("Couldn't load texture '%s', blacklisting\n", texName.c_str()); m_texBlacklist.insert(texName); return false; } m_texNameMap[texName] = tex; m_revTexNameMap[tex] = texName; return true; } 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); } 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::FlushTextureCache() { m_texNameMap.clear(); m_revTexNameMap.clear(); m_texBlacklist.clear(); } bool CEngine::SetTexture(const std::string& name, int stage) { auto it = m_texNameMap.find(name); if (it != m_texNameMap.end()) { m_device->SetTexture(stage, (*it).second); return true; } if (! LoadTexture(name).Valid()) { m_device->SetTexture(stage, 0); // invalid texture return false; } it = m_texNameMap.find(name); if (it != m_texNameMap.end()) { m_device->SetTexture(stage, (*it).second); return true; } m_device->SetTexture(stage, 0); // invalid texture return false; // should not happen normally } void CEngine::SetTexture(const Texture& tex, int stage) { m_device->SetTexture(stage, tex); } void CEngine::SetTerrainVision(float vision) { m_terrainVision = vision; } void CEngine::SetFocus(float focus) { m_focus = focus; m_size = m_app->GetVideoConfig().size; float aspect = (static_cast(m_size.x)) / m_size.y; Math::LoadProjectionMatrix(m_matProj, m_focus, aspect, 0.5f, m_deepView[0]); } float CEngine::GetFocus() { return m_focus; } void CEngine::SetGroundSpot(bool mode) { m_groundSpotVisible = mode; } bool CEngine::GetGroundSpot() { return m_groundSpotVisible; } void CEngine::SetShadow(bool mode) { m_shadowVisible = mode; } bool CEngine::GetShadow() { return m_shadowVisible; } void CEngine::SetDirty(bool mode) { m_dirty = mode; } bool CEngine::GetDirty() { return m_dirty; } void CEngine::SetFog(bool mode) { m_fog = mode; } bool CEngine::GetFog() { return m_fog; } void CEngine::SetSecondTexture(int texNum) { m_secondTexNum = texNum; } int CEngine::GetSecondTexture() { return m_secondTexNum; } 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; } int CEngine::GetRankView() { return m_rankView; } void CEngine::SetDrawWorld(bool draw) { m_drawWorld = draw; } void CEngine::SetDrawFront(bool draw) { m_drawFront = draw; } void CEngine::SetAmbientColor(const Color& color, int rank) { m_ambientColor[rank] = color; } Color CEngine::GetAmbientColor(int rank) { return m_ambientColor[rank]; } void CEngine::SetWaterAddColor(const Color& color) { m_waterAddColor = color; } Color CEngine::GetWaterAddColor() { return m_waterAddColor; } void CEngine::SetFogColor(const Color& color, int rank) { m_fogColor[rank] = color; } Color CEngine::GetFogColor(int rank) { return m_fogColor[rank]; } void CEngine::SetDeepView(float length, int rank, bool ref) { if (ref) length *= m_clippingDistance; m_deepView[rank] = length; } float CEngine::GetDeepView(int rank) { return m_deepView[rank]; } void CEngine::SetFogStart(float start, int rank) { m_fogStart[rank] = start; } float CEngine::GetFogStart(int rank) { return m_fogStart[rank]; } void CEngine::SetBackground(const std::string& name, Color up, Color down, Color cloudUp, Color cloudDown, bool full) { if (m_backgroundTex.Valid()) { DeleteTexture(m_backgroundTex); m_backgroundTex.SetInvalid(); } m_backgroundName = name; m_backgroundColorUp = up; m_backgroundColorDown = down; m_backgroundCloudUp = cloudUp; m_backgroundCloudDown = cloudDown; m_backgroundFull = full; if (! m_backgroundName.empty()) { TextureCreateParams params = m_defaultTexParams; params.padToNearestPowerOfTwo = true; m_backgroundTex = LoadTexture("textures/"+m_backgroundName, params); } } void CEngine::GetBackground(std::string& name, Color& up, Color& down, Color& cloudUp, Color& cloudDown, bool &full) { name = m_backgroundName; up = m_backgroundColorUp; down = m_backgroundColorDown; cloudUp = m_backgroundCloudUp; cloudDown = m_backgroundCloudDown; full = m_backgroundFull; } void CEngine::SetForegroundName(const std::string& name) { if (m_foregroundTex.Valid()) { DeleteTexture(m_foregroundTex); m_foregroundTex.SetInvalid(); } m_foregroundName = name; if (! m_foregroundName.empty()) m_foregroundTex = LoadTexture("textures/"+m_foregroundName); } void CEngine::SetOverFront(bool front) { m_overFront = front; } void CEngine::SetOverColor(const Color& color, int mode) { m_overColor = color; m_overMode = mode; } void CEngine::SetParticleDensity(float value) { if (value < 0.0f) value = 0.0f; if (value > 2.0f) value = 2.0f; m_particleDensity = value; } float CEngine::GetParticleDensity() { return m_particleDensity; } float CEngine::ParticleAdapt(float factor) { if (m_particleDensity == 0.0f) return 1000000.0f; return factor / m_particleDensity; } void CEngine::SetClippingDistance(float value) { if (value < 0.5f) value = 0.5f; if (value > 2.0f) value = 2.0f; m_lastClippingDistance = m_clippingDistance; m_clippingDistance = value; } float CEngine::GetClippingDistance() { return m_clippingDistance; } void CEngine::SetObjectDetail(float value) { if ( value < 0.0f ) value = 0.0f; if ( value > 2.0f ) value = 2.0f; m_objectDetail = value; } float CEngine::GetObjectDetail() { return m_objectDetail; } void CEngine::SetGadgetQuantity(float value) { if (value < 0.0f) value = 0.0f; if (value > 1.0f) value = 1.0f; m_gadgetQuantity = value; } float CEngine::GetGadgetQuantity() { return m_gadgetQuantity; } void CEngine::SetTextureQuality(int value) { if (value < 0) value = 0; if (value > 2) value = 2; if (value != m_textureQuality) { m_textureQuality = value; LoadAllTextures(); } } int CEngine::GetTextureQuality() { return m_textureQuality; } void CEngine::SetTotoMode(bool present) { m_totoMode = present; } bool CEngine::GetTotoMode() { return m_totoMode; } void CEngine::SetLensMode(bool present) { m_lensMode = present; } bool CEngine::GetLensMode() { return m_lensMode; } void CEngine::SetWaterMode(bool present) { m_waterMode = present; } bool CEngine::GetWaterMode() { return m_waterMode; } void CEngine::SetLightingMode(bool present) { m_lightMode = present; } bool CEngine::GetLightingMode() { return m_lightMode; } void CEngine::SetSkyMode(bool present) { m_skyMode = present; } bool CEngine::GetSkyMode() { return m_skyMode; } void CEngine::SetBackForce(bool present) { m_backForce = present; } bool CEngine::GetBackForce() { return m_backForce; } void CEngine::SetPlanetMode(bool present) { m_planetMode = present; } bool CEngine::GetPlanetMode() { return m_planetMode; } void CEngine::SetLightMode(bool present) { m_lightMode = present; } bool CEngine::GetLightMode() { return m_lightMode; } void CEngine::SetEditIndentMode(bool autoIndent) { m_editIndentMode = autoIndent; } bool CEngine::GetEditIndentMode() { return m_editIndentMode; } void CEngine::SetEditIndentValue(int value) { m_editIndentValue = value; } int CEngine::GetEditIndentValue() { return m_editIndentValue; } void CEngine::SetTracePrecision(float factor) { m_tracePrecision = factor; } float CEngine::GetTracePrecision() { return m_tracePrecision; } void CEngine::SetMouseType(EngineMouseType type) { m_mouseType = type; } EngineMouseType CEngine::GetMouseType() { return m_mouseType; } const Math::Matrix& CEngine::GetMatView() { return m_matView; } Math::Vector CEngine::GetEyePt() { return m_eyePt; } Math::Vector CEngine::GetLookatPt() { return m_lookatPt; } float CEngine::GetEyeDirH() { return m_eyeDirH; } float CEngine::GetEyeDirV() { return m_eyeDirV; } bool CEngine::IsVisiblePoint(const Math::Vector &pos) { return Math::Distance(m_eyePt, pos) <= m_deepView[0]; } void CEngine::UpdateMatProj() { m_device->SetTransform(TRANSFORM_PROJECTION, m_matProj); } void CEngine::ApplyChange() { m_deepView[0] /= m_lastClippingDistance; m_deepView[1] /= m_lastClippingDistance; SetFocus(m_focus); m_deepView[0] *= m_clippingDistance; m_deepView[1] *= m_clippingDistance; } /******************************************************* Rendering *******************************************************/ /** This function sets up render states, clears the viewport, and renders the scene. */ void CEngine::Render() { if (! m_render) return; m_statisticTriangle = 0; m_lastState = -1; m_lastColor = Color(-1.0f); m_lastMaterial = Material(); m_lightMan->UpdateLights(); Color color; if (m_skyMode && m_cloud->GetLevel() != 0.0f) // clouds? color = m_backgroundCloudDown; else color = m_backgroundColorDown; m_device->SetClearColor(color); // Begin the scene m_device->BeginScene(); if (m_drawWorld) Draw3DScene(); m_app->StartPerformanceCounter(PCNT_RENDER_INTERFACE); DrawInterface(); m_app->StopPerformanceCounter(PCNT_RENDER_INTERFACE); // End the scene m_device->EndScene(); } void CEngine::Draw3DScene() { if (m_groundSpotVisible) UpdateGroundSpotTextures(); DrawBackground(); // draws the background if (m_planetMode) DrawPlanet(); // draws the planets if (m_skyMode) 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); float fogStart = m_deepView[m_rankView]*m_fogStart[m_rankView]; float fogEnd = m_deepView[m_rankView]; m_device->SetFogParams(FOG_LINEAR, m_fogColor[m_rankView], fogStart, fogEnd, 1.0f); m_device->SetTransform(TRANSFORM_PROJECTION, m_matProj); m_device->SetTransform(TRANSFORM_VIEW, m_matView); if (m_waterMode) m_water->DrawBack(); // draws water background m_app->StartPerformanceCounter(PCNT_RENDER_TERRAIN); // Draw terrain m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN); for (int objRank = 0; objRank < static_cast(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; m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform); if (! IsVisible(objRank)) continue; int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) continue; assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; if (! p1.used) continue; for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; SetTexture(p2.tex1, 0); SetTexture(p2.tex2, 1); for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; if (! IsWithinLODLimit(m_objects[objRank].distance, p3.lodLevel)) continue; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; SetMaterial(p4.material); SetState(p4.state); DrawObject(p4); } } } } // Draws the shadows , if shadows enabled if (m_shadowVisible) DrawShadow(); m_app->StopPerformanceCounter(PCNT_RENDER_TERRAIN); // Draw other objects (and if shadows disabled, also terrain) m_app->StartPerformanceCounter(PCNT_RENDER_OBJECTS); bool transparent = false; for (int objRank = 0; objRank < static_cast(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; m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform); if (! IsVisible(objRank)) continue; int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) continue; assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; if (! p1.used) continue; m_lightMan->UpdateDeviceLights(m_objects[objRank].type); for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; SetTexture(p2.tex1, 0); SetTexture(p2.tex2, 1); for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; if (! IsWithinLODLimit(m_objects[objRank].distance, p3.lodLevel)) continue; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; if (m_objects[objRank].transparency != 0.0f) // transparent ? { transparent = true; continue; } SetMaterial(p4.material); SetState(p4.state); DrawObject(p4); } } } } // Draw transparent objects if (transparent) { int tState = ENG_RSTATE_TTEXTURE_BLACK | ENG_RSTATE_2FACE; Color tColor = Color(68.0f / 255.0f, 68.0f / 255.0f, 68.0f / 255.0f, 68.0f / 255.0f); for (int objRank = 0; objRank < static_cast(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; m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform); if (! IsVisible(objRank)) continue; int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) continue; assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; if (! p1.used) continue; m_lightMan->UpdateDeviceLights(m_objects[objRank].type); for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; SetTexture(p2.tex1, 0); SetTexture(p2.tex2, 1); for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; if (! IsWithinLODLimit(m_objects[objRank].distance, p3.lodLevel)) continue; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; if (m_objects[objRank].transparency == 0.0f) continue; SetMaterial(p4.material); SetState(tState, tColor); DrawObject(p4); } } } } } m_app->StopPerformanceCounter(PCNT_RENDER_OBJECTS); m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN); if (m_debugLights) m_device->DebugLights(); if (m_debugDumpLights) { m_debugDumpLights = false; m_lightMan->DebugDumpLights(); } if (m_waterMode) { m_app->StartPerformanceCounter(PCNT_RENDER_WATER); m_water->DrawSurf(); // draws water surface m_app->StopPerformanceCounter(PCNT_RENDER_WATER); } m_app->StartPerformanceCounter(PCNT_RENDER_PARTICLE); m_particle->DrawParticle(SH_WORLD); // draws the particles of the 3D world m_app->StopPerformanceCounter(PCNT_RENDER_PARTICLE); m_lightning->Draw(); // draws lightning if (m_lensMode) DrawForegroundImage(); // draws the foreground if (! m_overFront) DrawOverColor(); // draws the foreground color } void CEngine::DrawObject(const EngineBaseObjDataTier& p4) { if (p4.staticBufferId != 0) { m_device->DrawStaticBuffer(p4.staticBufferId); if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) m_statisticTriangle += p4.vertices.size() / 3; else m_statisticTriangle += p4.vertices.size() - 2; } else { if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) { m_device->DrawPrimitive(PRIMITIVE_TRIANGLES, &p4.vertices[0], p4.vertices.size()); m_statisticTriangle += p4.vertices.size() / 3; } else { m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, &p4.vertices[0], p4.vertices.size() ); m_statisticTriangle += p4.vertices.size() - 2; } } } void CEngine::DrawInterface() { m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false); m_device->SetRenderState(RENDER_STATE_LIGHTING, false); m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface); m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface); m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface); // Force new state to disable lighting m_interfaceMode = true; m_lastState = -1; SetState(Gfx::ENG_RSTATE_NORMAL); // Draw the entire interface Ui::CInterface* interface = CRobotMain::GetInstancePointer()->GetInterface(); if (interface != nullptr) { interface->Draw(); } m_interfaceMode = false; m_lastState = -1; SetState(Gfx::ENG_RSTATE_NORMAL); m_particle->DrawParticle(SH_INTERFACE); // draws the particles of the interface // 3D objects drawn in front of interface if (m_drawFront) { // Display the objects m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true); m_device->SetTransform(TRANSFORM_PROJECTION, m_matProj); m_device->SetGlobalAmbient(m_ambientColor[m_rankView]); m_device->SetRenderState(RENDER_STATE_LIGHTING, true); m_device->SetRenderState(RENDER_STATE_FOG, true); float fogStart = m_deepView[m_rankView]*m_fogStart[m_rankView]; float fogEnd = m_deepView[m_rankView]; m_device->SetFogParams(FOG_LINEAR, m_fogColor[m_rankView], fogStart, fogEnd, 1.0f); m_device->SetTransform(TRANSFORM_VIEW, m_matView); for (int objRank = 0; objRank < static_cast(m_objects.size()); objRank++) { if (! m_objects[objRank].used) continue; if (m_shadowVisible && m_objects[objRank].type == ENG_OBJTYPE_TERRAIN) continue; if (! m_objects[objRank].drawFront) continue; m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform); if (! IsVisible(objRank)) continue; int baseObjRank = m_objects[objRank].baseObjRank; if (baseObjRank == -1) continue; assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; if (! p1.used) continue; m_lightMan->UpdateDeviceLights(m_objects[objRank].type); for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; SetTexture(p2.tex1, 0); SetTexture(p2.tex2, 1); for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { EngineBaseObjLODTier& p3 = p2.next[l3]; if (! IsWithinLODLimit(m_objects[objRank].distance, p3.lodLevel)) continue; for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) { EngineBaseObjDataTier& p4 = p3.next[l4]; SetMaterial(p4.material); SetState(p4.state); DrawObject(p4); } } } } m_particle->DrawParticle(SH_FRONT); // draws the particles of the 3D world m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false); m_device->SetRenderState(RENDER_STATE_LIGHTING, false); m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface); m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface); m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface); } // Draw foreground color if (m_overFront) DrawOverColor(); // At the end to not overlap DrawMouse(); DrawHighlight(); DrawStats(); } void CEngine::UpdateGroundSpotTextures() { 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; for (int s = 0; s < 16; s++) { Math::Point min, max; 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(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(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; } if (clear || set) { CImage shadowImg(Math::IntPoint(256, 256)); shadowImg.Fill(Gfx::IntColor(255, 255, 255, 255)); // Draw the new shadows. for (int i = 0; i < static_cast( m_groundSpots.size() ); i++) { if (m_groundSpots[i].used == false || m_groundSpots[i].radius == 0.0f) continue; if (m_groundSpots[i].min == 0.0f && m_groundSpots[i].max == 0.0f) { dot = static_cast(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; 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; float intensity; if (dot == 0) intensity = 0.0f; else intensity = Math::Point(ppx-cx, ppy-cy).Length()/dot; Gfx::Color color; 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); ppx -= min.x; // on the texture ppy -= min.y; shadowImg.SetPixel(Math::IntPoint(ppx, ppy), color); } } } else { for (int iy = 0; iy < 256; iy++) { for (int ix = 0; ix < 256; ix++) { Math::Vector pos; 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; 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; Gfx::Color color; 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(Math::IntPoint(ix, iy), color); } } } } if (set) { dot = static_cast(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; ppx -= min.x; // on the texture ppy -= min.y; float intensity = 1.0f - Math::Point(ix, iy).Length() / dot; if (intensity <= 0.0f) continue; intensity *= m_groundMark.intensity; int j = (ix+dot) + (iy+dot) * m_groundMark.dx; if (m_groundMark.table[j] == 1) // green ? { Gfx::Color color; color.r = Math::Norm(1.0f-intensity); color.g = 1.0f; color.b = Math::Norm(1.0f-intensity); shadowImg.SetPixel(Math::IntPoint(ppx, ppy), color); } if (m_groundMark.table[j] == 2) // red ? { Gfx::Color color; color.r = 1.0f; color.g = Math::Norm(1.0f-intensity); color.b = Math::Norm(1.0f-intensity); shadowImg.SetPixel(Math::IntPoint(ppx, ppy), color); } } } } std::stringstream str; str << "textures/shadow" << std::setfill('0') << std::setw(2) << s << ".png"; std::string texName = str.str(); DeleteTexture(texName); Gfx::Texture tex = m_device->CreateTexture(&shadowImg, m_defaultTexParams); m_texNameMap[texName] = tex; m_revTexNameMap[tex] = texName; } } for (int i = 0; i < static_cast( 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; } void CEngine::DrawShadow() { m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false); m_device->SetRenderState(RENDER_STATE_LIGHTING, false); Math::Matrix matrix; matrix.LoadIdentity(); m_device->SetTransform(TRANSFORM_WORLD, matrix); Material material; material.diffuse = Color(1.0f, 1.0f, 1.0f); material.ambient = Color(0.5f, 0.5f, 0.5f); SetMaterial(material); // TODO: create a separate texture SetTexture("textures/interface/text.png"); Math::Point 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; Math::Vector n(0.0f, 1.0f, 0.0f); float startDeepView = m_deepView[m_rankView]*m_fogStart[m_rankView]; float endDeepView = m_deepView[m_rankView]; float lastIntensity = -1.0f; for (int i = 0; i < static_cast( m_shadows.size() ); i++) { if (m_shadows[i].hide || !m_shadows[i].used) continue; Math::Vector pos = m_shadows[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_shadows[i].radius; float max = height*0.5f; if ( h > max ) h = max; if ( h > 4.0f ) h = 4.0f; D = Math::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_shadows[i].radius; float max = height*0.1f; if ( h > max ) h = max; if ( h > 4.0f ) h = 4.0f; D = Math::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_shadows[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_shadows[i].radius*1.5f; radius *= 2.0f-hFactor; // greater if high radius *= 1.0f-d/D; // smaller if close Math::Vector corner[4]; if (m_shadows[i].type == ENG_SHADOW_NORM) { 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 { Math::Point rot; rot = Math::RotatePoint(-m_shadows[i].angle, Math::Point(radius, radius)); corner[0].x = rot.x; corner[0].z = rot.y; corner[0].y = 0.0f; rot = Math::RotatePoint(-m_shadows[i].angle, Math::Point(-radius, radius)); corner[1].x = rot.x; corner[1].z = rot.y; corner[1].y = 0.0f; rot = Math::RotatePoint(-m_shadows[i].angle, Math::Point(radius, -radius)); corner[2].x = rot.x; corner[2].z = rot.y; corner[2].y = 0.0f; rot = Math::RotatePoint(-m_shadows[i].angle, Math::Point(-radius, -radius)); corner[3].x = rot.x; corner[3].z = rot.y; corner[3].y = 0.0f; if (m_shadows[i].type == ENG_SHADOW_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] = Math::CrossProduct(corner[0], m_shadows[i].normal); corner[1] = Math::CrossProduct(corner[1], m_shadows[i].normal); corner[2] = Math::CrossProduct(corner[2], m_shadows[i].normal); corner[3] = Math::CrossProduct(corner[3], m_shadows[i].normal); corner[0] += pos; corner[1] += pos; corner[2] += pos; corner[3] += pos; ts.x += dp; ti.x -= dp; Vertex vertex[4] = { Vertex(corner[1], n, Math::Point(ts.x, ts.y)), Vertex(corner[0], n, Math::Point(ti.x, ts.y)), Vertex(corner[3], n, Math::Point(ts.x, ti.y)), Vertex(corner[2], n, Math::Point(ti.x, ti.y)) }; float intensity = (0.5f+m_shadows[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(PRIMITIVE_TRIANGLE_STRIP, vertex, 4); AddStatisticTriangle(2); } m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true); m_device->SetRenderState(RENDER_STATE_LIGHTING, true); } void CEngine::DrawBackground() { if (m_skyMode && 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_skyMode && !m_backgroundName.empty()) ) { DrawBackgroundImage(); // image } } void CEngine::DrawBackgroundGradient(const Color& up, const Color& down) { Math::Point p1(0.0f, 0.5f); Math::Point p2(1.0f, 1.0f); Color color[3] = { up, down, Color(0.0f, 0.0f, 0.0f, 0.0f) }; SetState(ENG_RSTATE_OPAQUE_COLOR); m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface); m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface); m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface); VertexCol vertex[4] = { VertexCol(Math::Vector(p1.x, p1.y, 0.0f), color[1]), VertexCol(Math::Vector(p1.x, p2.y, 0.0f), color[0]), VertexCol(Math::Vector(p2.x, p1.y, 0.0f), color[1]), VertexCol(Math::Vector(p2.x, p2.y, 0.0f), color[0]) }; m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4); AddStatisticTriangle(2); } void CEngine::DrawBackgroundImage() { Math::Point p1, p2; p1.x = 0.0f; p1.y = 0.0f; p2.x = 1.0f; p2.y = 1.0f; Math::Vector n = Math::Vector(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; u1 = -m_eyeDirH/Math::PI; u2 = u1+1.0f/Math::PI; v1 = (1.0f-h)*(0.5f+a/(2.0f*Math::PI/4.0f))+0.1f; v2 = v1+h; } Math::Point backgroundScale; backgroundScale.x = static_cast(m_backgroundTex.originalSize.x) / static_cast(m_backgroundTex.size.x); backgroundScale.y = static_cast(m_backgroundTex.originalSize.y) / static_cast(m_backgroundTex.size.y); u2 *= backgroundScale.x; v2 *= backgroundScale.y; SetTexture(m_backgroundTex); SetState(ENG_RSTATE_OPAQUE_TEXTURE | ENG_RSTATE_WRAP); m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface); m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface); m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface); Vertex vertex[4] = { Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(u1, v2)), Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(u1, v1)), Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(u2, v2)), Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(u2, v1)) }; m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4); AddStatisticTriangle(2); } void CEngine::DrawPlanet() { if (! m_planet->PlanetExist()) return; m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, false); m_device->SetRenderState(RENDER_STATE_LIGHTING, false); m_device->SetRenderState(RENDER_STATE_FOG, false); m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface); m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface); m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface); m_planet->Draw(); // draws the planets } void CEngine::DrawForegroundImage() { if (m_foregroundName.empty()) return; Math::Vector n = Math::Vector(0.0f, 0.0f, -1.0f); // normal Math::Point p1(0.0f, 0.0f); Math::Point 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; Vertex vertex[4] = { Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(u1, v2)), Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(u1, v1)), Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(u2, v2)), Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(u2, v1)) }; SetTexture(m_foregroundTex); SetState(ENG_RSTATE_CLAMP | ENG_RSTATE_TTEXTURE_BLACK); m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface); m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface); m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface); m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4); AddStatisticTriangle(2); } void CEngine::DrawOverColor() { if ((m_overColor == Color(0.0f, 0.0f, 0.0f, 0.0f) && m_overMode == ENG_RSTATE_TCOLOR_BLACK) || (m_overColor == Color(1.0f, 1.0f, 1.0f, 1.0f) && m_overMode == ENG_RSTATE_TCOLOR_WHITE)) return; Math::Point p1(0.0f, 0.0f); Math::Point p2(1.0f, 1.0f); Color color[3] = { m_overColor, m_overColor, Color(0.0f, 0.0f, 0.0f, 0.0f) }; SetState(m_overMode); m_device->SetTransform(TRANSFORM_VIEW, m_matViewInterface); m_device->SetTransform(TRANSFORM_PROJECTION, m_matProjInterface); m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface); VertexCol vertex[4] = { VertexCol(Math::Vector(p1.x, p1.y, 0.0f), color[1]), VertexCol(Math::Vector(p1.x, p2.y, 0.0f), color[0]), VertexCol(Math::Vector(p2.x, p1.y, 0.0f), color[1]), VertexCol(Math::Vector(p2.x, p2.y, 0.0f), color[0]) }; m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4); AddStatisticTriangle(2); } void CEngine::DrawHighlight() { Math::Point min, max; min.x = 1000000.0f; min.y = 1000000.0f; max.x = -1000000.0f; max.y = -1000000.0f; int i = 0; while (m_highlightRank[i] != -1) { Math::Point 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); } } 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; } if (! m_highlight) return; Math::Point p1 = m_highlightP1; Math::Point 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; SetState(ENG_RSTATE_OPAQUE_COLOR); 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; Color color(1.0f, 1.0f, 0.0f); // yellow VertexCol line[3] = { VertexCol(Math::Vector(), color), VertexCol(Math::Vector(), color), VertexCol(Math::Vector(), color) }; float dx = (p2.x - p1.x) / 5.0f; float dy = (p2.y - p1.y) / 5.0f; line[0].coord = Math::Vector(p1.x, p1.y + dy, 0.0f); line[1].coord = Math::Vector(p1.x, p1.y, 0.0f); line[2].coord = Math::Vector(p1.x + dx, p1.y, 0.0f); m_device->DrawPrimitive(PRIMITIVE_LINE_STRIP, line, 3); line[0].coord = Math::Vector(p2.x - dx, p1.y, 0.0f); line[1].coord = Math::Vector(p2.x, p1.y, 0.0f); line[2].coord = Math::Vector(p2.x, p1.y + dy, 0.0f); m_device->DrawPrimitive(PRIMITIVE_LINE_STRIP, line, 3); line[0].coord = Math::Vector(p2.x, p2.y - dy, 0.0f); line[1].coord = Math::Vector(p2.x, p2.y, 0.0f); line[2].coord = Math::Vector(p2.x - dx, p2.y, 0.0f); m_device->DrawPrimitive(PRIMITIVE_LINE_STRIP, line, 3); line[0].coord = Math::Vector(p1.x + dx, p2.y, 0.0f); line[1].coord = Math::Vector(p1.x, p2.y, 0.0f); line[2].coord = Math::Vector(p1.x, p2.y - dy, 0.0f); m_device->DrawPrimitive(PRIMITIVE_LINE_STRIP, line, 3); } void CEngine::DrawMouse() { MouseMode mode = m_app->GetMouseMode(); if (mode != MOUSE_ENGINE && mode != MOUSE_BOTH) return; Material material; material.diffuse = Color(1.0f, 1.0f, 1.0f); material.ambient = Color(0.5f, 0.5f, 0.5f); m_device->SetMaterial(material); m_device->SetTexture(0, m_miceTexture); int index = static_cast(m_mouseType); Math::Point pos = m_app->GetMousePos(); pos.x = pos.x - (m_mice[index].hotPoint.x * m_mouseSize.x) / 32.0f; pos.y = pos.y - ((32.0f - m_mice[index].hotPoint.y) * m_mouseSize.y) / 32.0f; Math::Point shadowPos; shadowPos.x = pos.x + (4.0f/800.0f); shadowPos.y = pos.y - (3.0f/600.0f); SetState(ENG_RSTATE_TCOLOR_WHITE); DrawMouseSprite(shadowPos, m_mouseSize, m_mice[index].iconShadow); SetState(m_mice[index].mode1); DrawMouseSprite(pos, m_mouseSize, m_mice[index].icon1); SetState(m_mice[index].mode2); DrawMouseSprite(pos, m_mouseSize, m_mice[index].icon2); } void CEngine::DrawMouseSprite(Math::Point pos, Math::Point size, int icon) { if (icon == -1) return; Math::Point p1 = pos; Math::Point 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; Math::Vector normal(0.0f, 0.0f, -1.0f); Vertex vertex[4] = { Vertex(Math::Vector(p1.x, p1.y, 0.0f), normal, Math::Point(u1, v2)), Vertex(Math::Vector(p1.x, p2.y, 0.0f), normal, Math::Point(u1, v1)), Vertex(Math::Vector(p2.x, p1.y, 0.0f), normal, Math::Point(u2, v2)), Vertex(Math::Vector(p2.x, p2.y, 0.0f), normal, Math::Point(u2, v1)) }; m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4); AddStatisticTriangle(2); } void CEngine::DrawStats() { if (!m_showStats) return; float height = m_text->GetAscent(FONT_COLOBOT, 12.0f); float width = 0.2f; Math::Point pos(0.04f, 0.04f + 17 * height); SetState(ENG_RSTATE_OPAQUE_COLOR); Gfx::Color black(0.0f, 0.0f, 0.0f, 0.0f); VertexCol vertex[4] = { VertexCol(Math::Vector(pos.x , pos.y - 17 * height, 0.0f), black), VertexCol(Math::Vector(pos.x , pos.y + height, 0.0f), black), VertexCol(Math::Vector(pos.x + width, pos.y - 17 * height, 0.0f), black), VertexCol(Math::Vector(pos.x + width, pos.y + height, 0.0f), black) }; m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertex, 4); SetState(ENG_RSTATE_TEXT); std::stringstream str; str.str(""); str << "Event processing: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_EVENT_PROCESSING); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; pos.y -= height; str.str(""); str << "Frame update: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_UPDATE_ALL); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; str.str(""); str << "Engine update: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_UPDATE_ENGINE); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; str.str(""); str << "Particle update: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_UPDATE_PARTICLE); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; str.str(""); str << "Game update: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_UPDATE_GAME); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; float otherUpdate = Math::Max(0.0f, m_app->GetPerformanceCounterData(PCNT_UPDATE_ALL) - m_app->GetPerformanceCounterData(PCNT_UPDATE_ENGINE) - m_app->GetPerformanceCounterData(PCNT_UPDATE_PARTICLE) - m_app->GetPerformanceCounterData(PCNT_UPDATE_GAME)); str.str(""); str << "Other update: " << std::fixed << std::setprecision(2) << otherUpdate; m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; pos.y -= height; str.str(""); str << "Frame render: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_RENDER_ALL); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; str.str(""); str << "Particle render: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_RENDER_PARTICLE); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; str.str(""); str << "Water render: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_RENDER_WATER); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; str.str(""); str << "Terrain render: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_RENDER_TERRAIN); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; str.str(""); str << "Objects render: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_RENDER_OBJECTS); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; str.str(""); str << "UI render: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_RENDER_INTERFACE); m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; float otherRender = m_app->GetPerformanceCounterData(PCNT_RENDER_ALL) - m_app->GetPerformanceCounterData(PCNT_RENDER_PARTICLE) - m_app->GetPerformanceCounterData(PCNT_RENDER_WATER) - m_app->GetPerformanceCounterData(PCNT_RENDER_TERRAIN) - m_app->GetPerformanceCounterData(PCNT_RENDER_OBJECTS) - m_app->GetPerformanceCounterData(PCNT_RENDER_INTERFACE); str.str(""); str << "Other render: " << std::fixed << std::setprecision(2) << otherRender; m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; pos.y -= height; str.str(""); str << "Triangles: " << m_statisticTriangle; m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); pos.y -= height; m_text->DrawText(m_fpsText, FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); } } // namespace Gfx