#pragma once // CATIA tarzı navigasyon "ghost" overlay — orta/sağ tuş navigasyonu sırasında // pivot (kamera odağı) etrafında yarı-saydam DÜNYA teli belirir: enlem/boylam // yayları kamera yönelimiyle döner → "küreyi elinle çeviriyormuşsun" hissi. // Pan'de 4-yön ok, zoom'da dikey çift-ok. Navigasyon bitince yumuşak solar. // Salt görsel: girdi tüketmez, seçim/pick'e dokunmaz. #include #include #include #include "fiko/render/orbit_kamera.hpp" #include "imgui.h" namespace fiko { struct NavGhostDurum { // ---- Ayar (merkezi Ayarlar sayfasına bağlanır) ---- bool aktif = true; // ghost gösterilsin mi f32 boyut = 1.0f; // küre yarıçap çarpanı (ekran-göreli) f32 opaklik = 1.0f; // genel saydamlık çarpanı (0.2–1) // ---- Çalışma anı (kare-arası animasyon) ---- f32 yogunluk = 0.0f; // 0→1 belirme/solma int sonKip = -1; // en son etkin kip (fade-out sırasında glifi korumak için) int oncekiKip = -1; // crossfade kaynağı (kip değişince eski glif buraya taşınır) f32 gecis = 1.0f; // 0→1 oncekiKip→sonKip morf ilerlemesi (1 = tamamlandı) f32 zoomFlas = 0.0f; // tekerlek zoom'u için zoom glifini kısa süre zorla göster (sn) }; // Tek örnek: hem çizim (main) hem ayar kaydı buradan erişir (ort yapısına dokunmadan). inline NavGhostDurum& navGhostGlobal() { static NavGhostDurum g; return g; } namespace navghost_ic { inline ImVec2 projekte(const Mat4f& vp, Vec3f p, f32 mx, f32 my, f32 w, f32 h) { const Vec4f c = vp * Vec4f{p, 1.0f}; const f32 iw = 1.0f / (std::abs(c.w) < 1e-6f ? 1e-6f : c.w); return ImVec2(mx + (c.x * iw * 0.5f + 0.5f) * w, my + (c.y * iw * 0.5f + 0.5f) * h); } inline ImU32 beyaz(f32 a) { const int A = (int)std::clamp(a * 255.0f, 0.0f, 255.0f); return IM_COL32(220, 238, 255, A); } inline ImU32 mavi(f32 a) { const int A = (int)std::clamp(a * 255.0f, 0.0f, 255.0f); return IM_COL32(90, 175, 255, A); } } // namespace navghost_ic // etkinKip: 0=döndür, 1=zoom, 2=pan, -1=yok. naviAktif: bir nav tuşu basılı mı. // tekerlek: bu kareki fare tekerleği deltası (≠0 ise zoom glifi kısa süre flaşlar — // zoom çoğunlukla tekerlekle yapılır ve basılı tuş olmadığından ghost aksi halde çıkmaz). inline void navGhostCizVeIsle(NavGhostDurum& g, OrbitKamera& kamera, ImDrawList* dl, int etkinKip, bool naviAktif, f32 dt, f32 mx, f32 my, f32 vw, f32 vh, f32 enBoy, f32 tekerlek = 0.0f) { using namespace navghost_ic; // Tekerlek zoom flaşı: tekerlek döndükçe zoom glifini ~0.5s zorla göster. if (tekerlek != 0.0f) g.zoomFlas = 0.5f; else if (g.zoomFlas > 0.0f) g.zoomFlas = std::max(0.0f, g.zoomFlas - dt); int kipEff = etkinKip; bool naviEff = naviAktif; if (etkinKip < 0 && g.zoomFlas > 0.0f) { kipEff = 1; naviEff = true; } // tekerlek → zoom // Belirme/solma: navigasyonda hızlı belir (~0.10s), bırakınca yumuşak sol (~0.30s). const bool goster = g.aktif && naviEff && kipEff >= 0; const f32 hedef = goster ? 1.0f : 0.0f; const f32 hiz = goster ? 12.0f : 4.5f; g.yogunluk += (hedef - g.yogunluk) * (1.0f - std::exp(-hiz * std::max(dt, 1e-4f))); if (goster && kipEff != g.sonKip) { // Kip değişti → crossfade başlat (eski glif solarken yeni belirir). İlk // belirmede (sonKip<0) geçiş yok; anında yeni glif. g.oncekiKip = g.sonKip; g.sonKip = kipEff; g.gecis = g.oncekiKip >= 0 ? 0.0f : 1.0f; } if (g.gecis < 1.0f) g.gecis = std::min(1.0f, g.gecis + dt * 6.0f); // ~0.16s morf if (g.yogunluk < 0.01f) { g.yogunluk = 0.0f; return; } const f32 a = std::clamp(g.yogunluk, 0.0f, 1.0f) * std::clamp(g.opaklik, 0.2f, 1.0f); const Mat4f vp = kamera.izdusum(enBoy) * kamera.gorunum(); const Vec3f C = kamera.odak(); const ImVec2 pc = projekte(vp, C, mx, my, vw, vh); const f32 R = std::min(vw, vh) * 0.34f * std::clamp(g.boyut, 0.3f, 2.5f); if (naviAktif) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); // ---- Dış çember + hafif dolgu (her kipte ortak zemin) ---- dl->AddCircleFilled(pc, R, IM_COL32(30, 46, 66, (int)(26 * a)), 64); dl->AddCircle(pc, R, mavi(0.55f * a), 64, 1.6f); // Tek glif çizimi (ga = bu glifin kendi saydamlığı; crossfade'de iki kez çağrılır). auto cizGlif = [&](int glifKip, f32 ga) { if (ga < 0.004f) return; if (glifKip == 0) { // ---- DÖNDÜR: dünya-uzaylı tel küre (kamera yönelimiyle döner) ---- const f32 fov = kamera.gorusAcisiDeg() * (f32)std::numbers::pi / 180.0f; const f32 wPerPix = (kamera.mesafe() * std::tan(fov * 0.5f)) / std::max(vh * 0.5f, 1.0f); const f32 wR = R * wPerPix; const Vec3f cam = kamera.konum(); auto depthA = [&](Vec3f p) { // kameraya yakın = parlak const f32 d = (p - cam).uzunluk(); const f32 t = std::clamp((kamera.mesafe() - d) / std::max(wR, 1e-4f), -1.0f, 1.0f); return 0.30f + 0.70f * (0.5f + 0.5f * t); }; const Vec3f eX{1, 0, 0}, eY{0, 1, 0}, eZ{0, 0, 1}; auto kureNok = [&](f32 fi, f32 te) { return C + (eX * (std::cos(fi) * std::cos(te)) + eY * (std::cos(fi) * std::sin(te)) + eZ * std::sin(fi)) * wR; }; // Enlem halkaları (ekvator vurgulu) const f32 enlemler[5] = {-1.0472f, -0.5236f, 0.0f, 0.5236f, 1.0472f}; // ±60,±30,0 for (int li = 0; li < 5; ++li) { const f32 fi = enlemler[li]; const bool ekv = (li == 2); ImVec2 onceki{}; f32 onA = 0; for (int s = 0; s <= 40; ++s) { const f32 te = (f32)s / 40.0f * 6.2831853f; const Vec3f pw = kureNok(fi, te); const ImVec2 ps = projekte(vp, pw, mx, my, vw, vh); const f32 da = depthA(pw); if (s > 0) { const f32 la = 0.5f * (onA + da) * ga * (ekv ? 1.0f : 0.7f); dl->AddLine(onceki, ps, ekv ? mavi(la) : beyaz(la * 0.9f), ekv ? 2.0f : 1.3f); } onceki = ps; onA = da; } } // Boylam yayları (kutuptan kutba) for (int mi = 0; mi < 6; ++mi) { const f32 te = (f32)mi / 6.0f * (f32)std::numbers::pi; ImVec2 onceki{}; f32 onA = 0; for (int s = 0; s <= 40; ++s) { const f32 fi = -1.5707963f + (f32)s / 40.0f * 3.1415927f; const Vec3f pw = kureNok(fi, te); const ImVec2 ps = projekte(vp, pw, mx, my, vw, vh); const f32 da = depthA(pw); if (s > 0) dl->AddLine(onceki, ps, beyaz(0.5f * (onA + da) * ga * 0.7f), 1.3f); onceki = ps; onA = da; } } // Pivot noktası dl->AddCircleFilled(pc, 4.0f, mavi(ga), 16); dl->AddCircle(pc, 4.0f, beyaz(ga), 16, 1.2f); } else if (glifKip == 2) { // ---- PAN: 4-yön ok ---- const f32 L = R * 0.62f, hb = 7.0f; const ImVec2 yon[4] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; for (auto d : yon) { const ImVec2 uc{pc.x + d.x * L, pc.y + d.y * L}; dl->AddLine(pc, uc, beyaz(0.9f * ga), 2.2f); const ImVec2 pn{-d.y, d.x}; dl->AddTriangleFilled(uc, ImVec2(uc.x - d.x * hb + pn.x * hb, uc.y - d.y * hb + pn.y * hb), ImVec2(uc.x - d.x * hb - pn.x * hb, uc.y - d.y * hb - pn.y * hb), beyaz(0.9f * ga)); } dl->AddCircleFilled(pc, 3.5f, mavi(ga), 12); } else { // ---- ZOOM: dikey çift-ok + büyüteç halkası ---- const f32 L = R * 0.60f, hb = 8.0f; for (f32 s : {-1.0f, 1.0f}) { const ImVec2 uc{pc.x, pc.y + s * L}; dl->AddLine(pc, uc, beyaz(0.9f * ga), 2.2f); dl->AddTriangleFilled(uc, ImVec2(uc.x - hb, uc.y - s * hb), ImVec2(uc.x + hb, uc.y - s * hb), beyaz(0.9f * ga)); } dl->AddCircle(pc, R * 0.30f, mavi(0.9f * ga), 32, 1.8f); } }; // Crossfade: kip geçişi sürerken eski glif solarken yeni belirir; aksi halde tek glif. if (g.gecis < 1.0f && g.oncekiKip >= 0 && g.oncekiKip != g.sonKip) { cizGlif(g.oncekiKip, a * (1.0f - g.gecis)); cizGlif(g.sonKip, a * g.gecis); } else { cizGlif(g.sonKip, a); } } } // namespace fiko