/* * This file is part of the Colobot: Gold Edition source code * Copyright (C) 2001-2023, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsitec.ch; http://colobot.info; http://github.com/colobot * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://gnu.org/licenses */ #include "ui/screen/screen_appearance.h" #include "app/app.h" #include "common/restext.h" #include "graphics/engine/camera.h" #include "graphics/engine/engine.h" #include "level/player_profile.h" #include "level/robotmain.h" #include "math/geometry.h" #include "ui/controls/button.h" #include "ui/controls/color.h" #include "ui/controls/interface.h" #include "ui/controls/label.h" #include "ui/controls/slider.h" #include "ui/controls/window.h" namespace Ui { const int PERSO_COLOR[3*10*3] = { // hair: 193, 221, 226, // white 255, 255, 181, // yellow 204, 155, 84, // blond 165, 48, 10, // red 140, 75, 84, // brown 83, 64, 51, // brown 90, 95, 85, // black 85, 48, 9, // brown 60, 0, 23, // black 0, 0, 0, // // spacesuit: 203, 206, 204, // dirty white 0, 205, 203, // bluish 108, 176, 0, // greenish 207, 207, 32, // yellow 170, 141, 0, // orange 108, 84, 0, // brown 0, 84, 136, // bluish 56, 61, 146, // bluish 56, 56, 56, // black 0, 0, 0, // // strips: 255, 255, 255, // white 255, 255, 0, // yellow 255, 132, 1, // orange 255, 0, 255, // magenta 255, 0, 0, // red 0, 255, 0, // green 0, 255, 255, // cyan 0, 0, 255, // blue 70, 51, 84, // dark 0, 0, 0, // }; CScreenAppearance::CScreenAppearance() : m_appearanceTab(0), m_appearanceAngle(0.0f) { } void CScreenAppearance::CreateInterface() { CWindow* pw; CLabel* pl; CButton* pb; CColor* pco; CSlider* psl; glm::vec2 pos, ddim; std::string name; pos.x = 0.10f; pos.y = 0.10f; ddim.x = 0.80f; ddim.y = 0.80f; pw = m_interface->CreateWindows(pos, ddim, 12, EVENT_WINDOW5); GetResource(RES_TEXT, RT_TITLE_PERSO, name); pw->SetName(name); pos.x = 0.10f; pos.y = 0.40f; ddim.x = 0.50f; ddim.y = 0.50f; pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // orange corner pos.x = 0.40f; pos.y = 0.10f; ddim.x = 0.50f; ddim.y = 0.50f; pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // blue corner pos.x = 95.0f/640.0f; pos.y = 108.0f/480.0f; ddim.x = 220.0f/640.0f; ddim.y = 274.0f/480.0f; pw->CreateGroup(pos, ddim, 17, EVENT_NULL); // frame pos.x = 100.0f/640.0f; pos.y = 364.0f/480.0f; ddim.x = 210.0f/640.0f; ddim.y = 14.0f/480.0f; pw->CreateGroup(pos, ddim, 3, EVENT_NULL); // transparent -> gray pos.x = 120.0f/640.0f; pos.y = 364.0f/480.0f; ddim.x = 80.0f/640.0f; ddim.y = 28.0f/480.0f; pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PHEAD); pb->SetState(STATE_SHADOW); pb->SetState(STATE_CARD); pos.x = 210.0f/640.0f; pos.y = 364.0f/480.0f; ddim.x = 80.0f/640.0f; ddim.y = 28.0f/480.0f; pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PBODY); pb->SetState(STATE_SHADOW); pb->SetState(STATE_CARD); pos.x = 100.0f/640.0f; pos.y = 354.0f/480.0f; ddim.x = 210.0f/640.0f; ddim.y = 10.0f/480.0f; pw->CreateGroup(pos, ddim, 1, EVENT_INTERFACE_GLINTb); // orange bar pos.x = 100.0f/640.0f; pos.y = 154.0f/480.0f; ddim.x = 210.0f/640.0f; ddim.y = 200.0f/480.0f; pw->CreateGroup(pos, ddim, 2, EVENT_INTERFACE_GLINTu); // orange -> transparent // Face pos.x = 340.0f/640.0f; pos.y = 356.0f/480.0f; ddim.x = 200.0f/640.0f; ddim.y = 16.0f/480.0f; pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL11, ""); pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); pos.x = 340.0f/640.0f; pos.y = 312.0f/480.0f; ddim.x = 44.0f/640.0f; ddim.y = 44.0f/480.0f; pb = pw->CreateButton(pos, ddim, 43, EVENT_INTERFACE_PFACE1); pb->SetState(STATE_SHADOW); pos.x += 50.0f/640.0f; pb = pw->CreateButton(pos, ddim, 44, EVENT_INTERFACE_PFACE2); pb->SetState(STATE_SHADOW); pos.x += 50.0f/640.0f; pb = pw->CreateButton(pos, ddim, 45, EVENT_INTERFACE_PFACE3); pb->SetState(STATE_SHADOW); pos.x += 50.0f/640.0f; pb = pw->CreateButton(pos, ddim, 46, EVENT_INTERFACE_PFACE4); pb->SetState(STATE_SHADOW); // Glasses pos.x = 340.0f/640.0f; pos.y = 270.0f/480.0f; ddim.x = 200.0f/640.0f; ddim.y = 16.0f/480.0f; pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL12, ""); pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); pos.x = 340.0f/640.0f; pos.y = 240.0f/480.0f; ddim.x = 30.0f/640.0f; ddim.y = 30.0f/480.0f; for ( int i=0 ; i<6 ; i++ ) { int ti[6] = {11, 179, 180, 181, 182, 183}; pb = pw->CreateButton(pos, ddim, ti[i], static_cast<EventType>(EVENT_INTERFACE_PGLASS0+i)); pb->SetState(STATE_SHADOW); pos.x += (30.0f+2.8f)/640.0f; } // Color A pos.x = 340.0f/640.0f; pos.y = 300.0f/480.0f; ddim.x = 200.0f/640.0f; ddim.y = 16.0f/480.0f; pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL14, ""); pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); pos.y = 282.0f/480.0f; ddim.x = 18.0f/640.0f; ddim.y = 18.0f/480.0f; for ( int j=0 ; j<3 ; j++ ) { pos.x = 340.0f/640.0f; for ( int i=0 ; i<3 ; i++ ) { pco = pw->CreateColor(pos, ddim, -1, static_cast<EventType>(EVENT_INTERFACE_PC0a+j*3+i)); pco->SetState(STATE_SHADOW); pos.x += 20.0f/640.0f; } pos.y -= 20.0f/480.0f; } pos.x = 420.0f/640.0f; pos.y = 282.0f/480.0f; ddim.x = 100.0f/640.0f; ddim.y = 18.0f/480.0f; for ( int i=0 ; i<3 ; i++ ) { psl = pw->CreateSlider(pos, ddim, 0, static_cast<EventType>(EVENT_INTERFACE_PCRa+i)); psl->SetState(STATE_SHADOW); psl->SetLimit(0.0f, 255.0f); psl->SetArrowStep(16.0f); pos.y -= 20.0f/480.0f; } // Color B pos.x = 340.0f/640.0f; pos.y = 192.0f/480.0f; ddim.x = 200.0f/640.0f; ddim.y = 16.0f/480.0f; pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL13, ""); pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); pos.y = 174.0f/480.0f; ddim.x = 18.0f/640.0f; ddim.y = 18.0f/480.0f; for ( int j=0 ; j<3 ; j++ ) { pos.x = 340.0f/640.0f; for ( int i=0 ; i<3 ; i++ ) { pco = pw->CreateColor(pos, ddim, -1, static_cast<EventType>(EVENT_INTERFACE_PC0b+j*3+i)); pco->SetState(STATE_SHADOW); pos.x += 20.0f/640.0f; } pos.y -= 20.0f/480.0f; } pos.x = 420.0f/640.0f; pos.y = 174.0f/480.0f; ddim.x = 100.0f/640.0f; ddim.y = 18.0f/480.0f; for ( int i=0 ; i<3 ; i++ ) { psl = pw->CreateSlider(pos, ddim, 0, static_cast<EventType>(EVENT_INTERFACE_PCRb+i)); psl->SetState(STATE_SHADOW); psl->SetLimit(0.0f, 255.0f); psl->SetArrowStep(16.0f); pos.y -= 20.0f/480.0f; } // Rotation pos.x = 100.0f/640.0f; pos.y = 113.0f/480.0f; ddim.x = 20.0f/640.0f; ddim.y = 20.0f/480.0f; pb = pw->CreateButton(pos, ddim, 55, EVENT_INTERFACE_PLROT); // < pb->SetState(STATE_SHADOW); pb->SetRepeat(true); pos.x = 290.0f/640.0f; pos.y = 113.0f/480.0f; ddim.x = 20.0f/640.0f; ddim.y = 20.0f/480.0f; pb = pw->CreateButton(pos, ddim, 48, EVENT_INTERFACE_PRROT); // > pb->SetState(STATE_SHADOW); pb->SetRepeat(true); pos.x = 100.0f/640.0f; pos.y = 70.0f/480.0f; ddim.x = 100.0f/640.0f; ddim.y = 32.0f/480.0f; pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_POK); pb->SetState(STATE_SHADOW); pos.x = 210.0f/640.0f; pos.y = 70.0f/480.0f; ddim.x =100.0f/640.0f; ddim.y = 32.0f/480.0f; pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PCANCEL); pb->SetState(STATE_SHADOW); pos.x = 340.0f/640.0f; pos.y = 70.0f/480.0f; ddim.x =194.0f/640.0f; ddim.y = 32.0f/480.0f; pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PDEF); pb->SetState(STATE_SHADOW); m_appearanceTab = 0; m_appearanceAngle = -0.6f; m_main->GetPlayerProfile()->LoadAppearance(); UpdatePerso(); m_main->ScenePerso(); CameraPerso(); } bool CScreenAppearance::EventProcess(const Event &event) { PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance(); switch( event.type ) { case EVENT_KEY_DOWN: { auto data = event.GetData<KeyEventData>(); if (data->key == KEY(RETURN)) { m_main->ChangePhase(PHASE_MAIN_MENU); } if (data->key == KEY(ESCAPE)) { m_main->ChangePhase(PHASE_PLAYER_SELECT); } break; } case EVENT_UPDINTERFACE: CameraPerso(); break; case EVENT_INTERFACE_PHEAD: m_appearanceTab = 0; UpdatePerso(); m_main->ScenePerso(); CameraPerso(); break; case EVENT_INTERFACE_PBODY: m_appearanceTab = 1; UpdatePerso(); m_main->ScenePerso(); CameraPerso(); break; case EVENT_INTERFACE_PFACE1: case EVENT_INTERFACE_PFACE2: case EVENT_INTERFACE_PFACE3: case EVENT_INTERFACE_PFACE4: appearance.face = event.type-EVENT_INTERFACE_PFACE1; appearance.DefHairColor(); UpdatePerso(); m_main->ScenePerso(); break; case EVENT_INTERFACE_PGLASS0: case EVENT_INTERFACE_PGLASS1: case EVENT_INTERFACE_PGLASS2: case EVENT_INTERFACE_PGLASS3: case EVENT_INTERFACE_PGLASS4: case EVENT_INTERFACE_PGLASS5: case EVENT_INTERFACE_PGLASS6: case EVENT_INTERFACE_PGLASS7: case EVENT_INTERFACE_PGLASS8: case EVENT_INTERFACE_PGLASS9: appearance.glasses = event.type-EVENT_INTERFACE_PGLASS0; UpdatePerso(); m_main->ScenePerso(); break; case EVENT_INTERFACE_PC0a: case EVENT_INTERFACE_PC1a: case EVENT_INTERFACE_PC2a: case EVENT_INTERFACE_PC3a: case EVENT_INTERFACE_PC4a: case EVENT_INTERFACE_PC5a: case EVENT_INTERFACE_PC6a: case EVENT_INTERFACE_PC7a: case EVENT_INTERFACE_PC8a: case EVENT_INTERFACE_PC9a: FixPerso(event.type-EVENT_INTERFACE_PC0a, 0); UpdatePerso(); m_main->ScenePerso(); break; case EVENT_INTERFACE_PC0b: case EVENT_INTERFACE_PC1b: case EVENT_INTERFACE_PC2b: case EVENT_INTERFACE_PC3b: case EVENT_INTERFACE_PC4b: case EVENT_INTERFACE_PC5b: case EVENT_INTERFACE_PC6b: case EVENT_INTERFACE_PC7b: case EVENT_INTERFACE_PC8b: case EVENT_INTERFACE_PC9b: FixPerso(event.type-EVENT_INTERFACE_PC0b, 1); UpdatePerso(); m_main->ScenePerso(); break; case EVENT_INTERFACE_PCRa: case EVENT_INTERFACE_PCGa: case EVENT_INTERFACE_PCBa: case EVENT_INTERFACE_PCRb: case EVENT_INTERFACE_PCGb: case EVENT_INTERFACE_PCBb: ColorPerso(); UpdatePerso(); m_main->ScenePerso(); break; case EVENT_INTERFACE_PDEF: appearance.DefPerso(); UpdatePerso(); m_main->ScenePerso(); break; case EVENT_INTERFACE_PLROT: m_appearanceAngle += 0.2f; break; case EVENT_INTERFACE_PRROT: m_appearanceAngle -= 0.2f; break; case EVENT_INTERFACE_POK: m_main->GetPlayerProfile()->SaveAppearance(); m_main->ChangePhase(PHASE_MAIN_MENU); break; case EVENT_INTERFACE_PCANCEL: m_main->GetPlayerProfile()->LoadAppearance(); // reload appearance from file m_main->ChangePhase(PHASE_PLAYER_SELECT); break; default: return true; } return false; } bool CScreenAppearance::GetGamerOnlyHead() { return m_appearanceTab == 0; } float CScreenAppearance::GetPersoAngle() { return m_appearanceAngle; } // Tests whether two colors are equal or nearly are. static bool EqColor(const Gfx::Color &c1, const Gfx::Color &c2) { return (fabs(c1.r-c2.r) < 0.01f && fabs(c1.g-c2.g) < 0.01f && fabs(c1.b-c2.b) < 0.01f ); } // Updates all the buttons for the character. void CScreenAppearance::UpdatePerso() { CWindow* pw; CLabel* pl; CButton* pb; CColor* pc; CSlider* ps; Gfx::Color color; std::string name; int i; PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance(); pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return; pb = static_cast<CButton*>(pw->SearchControl(EVENT_INTERFACE_PHEAD)); if ( pb != nullptr ) { pb->SetState(STATE_CHECK, m_appearanceTab==0); } pb = static_cast<CButton*>(pw->SearchControl(EVENT_INTERFACE_PBODY)); if ( pb != nullptr ) { pb->SetState(STATE_CHECK, m_appearanceTab==1); } pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL11)); if ( pl != nullptr ) { if ( m_appearanceTab == 0 ) { pl->SetState(STATE_VISIBLE); GetResource(RES_TEXT, RT_PERSO_FACE, name); pl->SetName(name); } else { pl->ClearState(STATE_VISIBLE); } } pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL12)); if ( pl != nullptr ) { if ( m_appearanceTab == 0 ) { pl->SetState(STATE_VISIBLE); GetResource(RES_TEXT, RT_PERSO_GLASSES, name); pl->SetName(name); } else { pl->ClearState(STATE_VISIBLE); } } pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL13)); if ( pl != nullptr ) { if ( m_appearanceTab == 0 ) GetResource(RES_TEXT, RT_PERSO_HAIR, name); else GetResource(RES_TEXT, RT_PERSO_BAND, name); pl->SetName(name); } pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL14)); if ( pl != nullptr ) { if ( m_appearanceTab == 0 ) { pl->ClearState(STATE_VISIBLE); } else { pl->SetState(STATE_VISIBLE); GetResource(RES_TEXT, RT_PERSO_COMBI, name); pl->SetName(name); } } for ( i=0 ; i<4 ; i++ ) { pb = static_cast<CButton*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PFACE1+i))); if ( pb == nullptr ) break; pb->SetState(STATE_VISIBLE, m_appearanceTab==0); pb->SetState(STATE_CHECK, i==appearance.face); } for ( i=0 ; i<10 ; i++ ) { pb = static_cast<CButton*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PGLASS0+i))); if ( pb == nullptr ) break; pb->SetState(STATE_VISIBLE, m_appearanceTab==0); pb->SetState(STATE_CHECK, i==appearance.glasses); } for ( i=0 ; i<3*3 ; i++ ) { pc = static_cast<CColor*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PC0a+i))); if ( pc == nullptr ) break; if ( m_appearanceTab == 0 ) { pc->ClearState(STATE_VISIBLE); } else { pc->SetState(STATE_VISIBLE); color.r = PERSO_COLOR[3*10*1+3*i+0]/255.0f; color.g = PERSO_COLOR[3*10*1+3*i+1]/255.0f; color.b = PERSO_COLOR[3*10*1+3*i+2]/255.0f; color.a = 0.0f; pc->SetColor(color); pc->SetState(STATE_CHECK, EqColor(color, appearance.colorCombi)); } pc = static_cast<CColor*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PC0b+i))); if ( pc == nullptr ) break; color.r = PERSO_COLOR[3*10*2*m_appearanceTab+3*i+0]/255.0f; color.g = PERSO_COLOR[3*10*2*m_appearanceTab+3*i+1]/255.0f; color.b = PERSO_COLOR[3*10*2*m_appearanceTab+3*i+2]/255.0f; color.a = 0.0f; pc->SetColor(color); pc->SetState(STATE_CHECK, EqColor(color, m_appearanceTab?appearance.colorBand:appearance.colorHair)); } for ( i=0 ; i<3 ; i++ ) { ps = static_cast<CSlider*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PCRa+i))); if ( ps == nullptr ) break; ps->SetState(STATE_VISIBLE, m_appearanceTab==1); } if ( m_appearanceTab == 1 ) { color = appearance.colorCombi; ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRa)); if ( ps != nullptr ) ps->SetVisibleValue(color.r*255.0f); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCGa)); if ( ps != nullptr ) ps->SetVisibleValue(color.g*255.0f); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCBa)); if ( ps != nullptr ) ps->SetVisibleValue(color.b*255.0f); } if ( m_appearanceTab == 0 ) color = appearance.colorHair; else color = appearance.colorBand; ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRb)); if ( ps != nullptr ) ps->SetVisibleValue(color.r*255.0f); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCGb)); if ( ps != nullptr ) ps->SetVisibleValue(color.g*255.0f); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCBb)); if ( ps != nullptr ) ps->SetVisibleValue(color.b*255.0f); } // Updates the camera for the character. void CScreenAppearance::CameraPerso() { if ( m_appearanceTab == 0 ) { SetCamera(0.325f, -0.15f, 5.0f); } else { SetCamera(0.325f, 0.3f, 18.0f); } } // Sets a fixed color. void CScreenAppearance::FixPerso(int rank, int index) { PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance(); if ( m_appearanceTab == 0 ) { if ( index == 1 ) { appearance.colorHair.r = PERSO_COLOR[3*10*0+rank*3+0]/255.0f; appearance.colorHair.g = PERSO_COLOR[3*10*0+rank*3+1]/255.0f; appearance.colorHair.b = PERSO_COLOR[3*10*0+rank*3+2]/255.0f; } } if ( m_appearanceTab == 1 ) { if ( index == 0 ) { appearance.colorCombi.r = PERSO_COLOR[3*10*1+rank*3+0]/255.0f; appearance.colorCombi.g = PERSO_COLOR[3*10*1+rank*3+1]/255.0f; appearance.colorCombi.b = PERSO_COLOR[3*10*1+rank*3+2]/255.0f; } if ( index == 1 ) { appearance.colorBand.r = PERSO_COLOR[3*10*2+rank*3+0]/255.0f; appearance.colorBand.g = PERSO_COLOR[3*10*2+rank*3+1]/255.0f; appearance.colorBand.b = PERSO_COLOR[3*10*2+rank*3+2]/255.0f; } } } // Updates the color of the character. void CScreenAppearance::ColorPerso() { CWindow* pw; CSlider* ps; Gfx::Color color; PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance(); pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return; color.a = 0.0f; ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRa)); if ( ps != nullptr ) color.r = ps->GetVisibleValue()/255.0f; ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCGa)); if ( ps != nullptr ) color.g = ps->GetVisibleValue()/255.0f; ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCBa)); if ( ps != nullptr ) color.b = ps->GetVisibleValue()/255.0f; if ( m_appearanceTab == 1 ) appearance.colorCombi = color; ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRb)); if ( ps != nullptr ) color.r = ps->GetVisibleValue()/255.0f; ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCGb)); if ( ps != nullptr ) color.g = ps->GetVisibleValue()/255.0f; ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCBb)); if ( ps != nullptr ) color.b = ps->GetVisibleValue()/255.0f; if ( m_appearanceTab == 0 ) appearance.colorHair = color; else appearance.colorBand = color; } void CScreenAppearance::SetCamera(float x, float y, float cameraDistance) { Gfx::CCamera* camera = m_main->GetCamera(); Gfx::CEngine* engine = Gfx::CEngine::GetInstancePointer(); camera->SetType(Gfx::CAM_TYPE_SCRIPT); glm::vec3 p2D(x, y, cameraDistance); glm::vec3 p3D{}; glm::mat4 matView; glm::mat4 matProj = engine->GetMatProj(); Math::LoadViewMatrix(matView, glm::vec3(0.0f, 0.0f, -cameraDistance), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); p2D.x = p2D.x * 2.0f - 1.0f; // [0..1] -> [-1..1] p2D.y = p2D.y * 2.0f - 1.0f; p3D.x = p2D.x * p2D.z / matProj[0][0]; p3D.y = p2D.y * p2D.z / matProj[1][1]; p3D.z = p2D.z; p3D = Math::Transform(glm::inverse(matView), p3D); p3D = -p3D; camera->SetScriptCamera(glm::vec3(cameraDistance, p3D.y, p3D.x), glm::vec3(0.0f, p3D.y, p3D.x)); } } // namespace Ui