#include "fiko/kutuk/zbuffer6_motor.hpp" #include #include #include namespace fiko::kutuk { namespace { inline bool sonluMu(f64 v) { return std::isfinite(v); } inline bool sonluMu(const Vec3d& v) { return sonluMu(v.x) && sonluMu(v.y) && sonluMu(v.z); } inline f64 bilesen(const Vec3d& v, int e) { return e == 0 ? v.x : (e == 1 ? v.y : v.z); } // Gecerli (birim) eksen; NaN/sifir -> (0,0,1) (spec §4/§5 guard). Vec3d gecerliEksen(Vec3d e) { if (!sonluMu(e)) return {0, 0, 1}; const f64 u2 = e.uzunlukKare(); if (u2 < 1e-12) return {0, 0, 1}; return e / std::sqrt(u2); } } // namespace // --------------------------------------------------------------------------- // DepthMap::kur // --------------------------------------------------------------------------- void ZBuffer6Motoru::DepthMap::kur(int isinE, bool poz, u32 nuS, u32 nvS) { isinEksen = isinE; pozitif = poz; int a = 0, b = 1; if (isinE == 0) { a = 1; b = 2; } // isin X -> u=Y,v=Z else if (isinE == 1) { a = 0; b = 2; } // isin Y -> u=X,v=Z else { a = 0; b = 1; } // isin Z -> u=X,v=Y uEksen = a; vEksen = b; nu = nuS; nv = nvS; const size_t n = static_cast(nu) * nv; near.assign(n, 0.0f); far.assign(n, 0.0f); dolu.assign(n, 0); } // --------------------------------------------------------------------------- // takimSolidIci — Dexel3 ile BIREBIR ayni profil (Duz/Kuresel/Torus) // ---------------------------------------------------------------------------- // Takim solid: eksen T (birim), uc konumu poz, yaricap R, boy. Govde silindiri // [0,boy] + uc profili. √ oncesi max(0,·) guard (spec §1 flat/ball/bull notu). // --------------------------------------------------------------------------- bool ZBuffer6Motoru::takimSolidIci(const Vec3d& p, Vec3d poz, const TakimGeo& takim) { const f64 R = takim.cap * 0.5; if (R <= 0) return false; const f64 R2 = R * R; const f64 boy = takim.boy; const Vec3d T = takim.eksen; // birim (cagiranda garanti) const bool kuresel = takim.tip == TakimGeo::Tip::Kuresel; const bool torus = takim.tip == TakimGeo::Tip::Torus; const f64 rc = std::clamp(takim.koseR, 0.0, R); const f64 flatR = torus ? std::max(0.0, R - rc) : 0.0; const Vec3d rp = p - poz; const f64 ax = rp.nokta(T); // eksen boyunca (uc=0) const f64 rad2 = std::max(0.0, rp.uzunlukKare() - ax * ax); if (ax >= 0.0 && ax <= boy) return rad2 <= R2; // govde silindiri if (ax > boy) return false; // uc ustu // ax<0: uc profili if (kuresel) return rp.uzunlukKare() <= R2; // yarimkure if (torus) { if (-ax > rc) return false; const f64 rad = std::sqrt(rad2); if (rad <= flatR) return true; const f64 dd = rad - flatR; return dd * dd + ax * ax <= rc * rc; // kose torus kesiti } return false; // duz uc: ax<0 dis } // --------------------------------------------------------------------------- // haritaDoldur — stok dolu koridoru (near/far tam bbox veya silindir maske) // --------------------------------------------------------------------------- void ZBuffer6Motoru::haritaDoldur(DepthMap& dm) { if (dm.nu == 0 || dm.nv == 0) return; const int a = dm.uEksen, b = dm.vEksen; const bool silindir = silindirYaricap_ >= 0.0; const f64 rad2 = silindirYaricap_ * silindirYaricap_; for (u32 iv = 0; iv < dm.nv; ++iv) { const f64 vc = dm.v0 + (iv + 0.5) * dm.hucreV; for (u32 iu = 0; iu < dm.nu; ++iu) { const f64 uc = dm.u0 + (iu + 0.5) * dm.hucreU; const u32 i = dm.idx(iu, iv); if (silindir) { // u,v ekseninden dunya X/Y bilesenlerini turet (isin eksenine gore). f64 wx = merkez_.x, wy = merkez_.y; if (a == 0) wx = uc; else if (a == 1) wy = uc; if (b == 0) wx = vc; else if (b == 1) wy = vc; const f64 dx = wx - merkez_.x, dy = wy - merkez_.y; if (dx * dx + dy * dy > rad2) { dm.dolu[i] = 0; continue; } // bos piksel } dm.near[i] = static_cast(dm.w0); dm.far[i] = static_cast(dm.w1); dm.dolu[i] = 1; // tam koridor (w0..w1) } } } // --------------------------------------------------------------------------- // kutukKur // --------------------------------------------------------------------------- bool ZBuffer6Motoru::kutukKur(const cam::Stok& stok, Aabbf sinir, f64 cozunurluk) { sonHata_.clear(); kuruldu_ = false; supurmeCapUyarisi_ = false; kaldirilanHacim_ = 0.0; stok_ = stok; if (!sinir.gecerliMi() || !sonluMu(Vec3d{sinir.asgari.x, sinir.asgari.y, sinir.asgari.z}) || !sonluMu(Vec3d{sinir.azami.x, sinir.azami.y, sinir.azami.z})) { sonHata_ = "Gecersiz kutuk siniri (min>max veya NaN)."; return false; } if (!(cozunurluk > 1e-9) || !sonluMu(cozunurluk)) { sonHata_ = "Gecersiz cozunurluk."; return false; } const Vec3f boyutF = sinir.boyut(); const f64 ext[3] = {boyutF.x, boyutF.y, boyutF.z}; sinir_ = sinir; cozunurluk_ = cozunurluk; const Vec3f merkF = sinir.merkez(); merkez_ = {merkF.x, merkF.y, merkF.z}; silindirYaricap_ = (stok.tip == cam::StokTip::Silindir) ? stok.boyut.x * 0.5 : -1.0; const Vec3d mn{sinir.asgari.x, sinir.asgari.y, sinir.asgari.z}; const Vec3d mx{sinir.azami.x, sinir.azami.y, sinir.azami.z}; // Bos/dejenerre kutuk -> bos haritalar (NaN uretme). Basari ama malzeme yok. if (ext[0] <= 1e-9 || ext[1] <= 1e-9 || ext[2] <= 1e-9) { for (int y = 0; y < 6; ++y) haritalar_[y].kur(y / 2, (y % 2) == 0, 0, 0); kuruldu_ = true; return true; } auto hucreSayi = [&](f64 uzunluk) -> u32 { return std::max(1, static_cast(std::ceil(uzunluk / cozunurluk))); }; // 6 harita: yon = 0..5 -> isinEksen=yon/2 (0=X,1=Y,2=Z), pozitif=(yon%2==0). for (int y = 0; y < 6; ++y) { const int e = y / 2; // isin ekseni const bool poz = (y % 2) == 0; // +/- int a, b; if (e == 0) { a = 1; b = 2; } else if (e == 1) { a = 0; b = 2; } else { a = 0; b = 1; } DepthMap& dm = haritalar_[y]; dm.kur(e, poz, hucreSayi(ext[a]), hucreSayi(ext[b])); dm.u0 = bilesen(mn, a); dm.v0 = bilesen(mn, b); dm.w0 = bilesen(mn, e); dm.w1 = bilesen(mx, e); dm.hucreU = ext[a] / dm.nu; dm.hucreV = ext[b] / dm.nv; haritaDoldur(dm); } kuruldu_ = true; return true; } // --------------------------------------------------------------------------- // sifirla — dolu kutuk haline geri don // --------------------------------------------------------------------------- void ZBuffer6Motoru::sifirla() { if (!kuruldu_) return; kaldirilanHacim_ = 0.0; for (auto& dm : haritalar_) haritaDoldur(dm); } // --------------------------------------------------------------------------- // haritaKes — GERCEK DAIRESEL kesit projeksiyon (web dikdortgen yaklasimini duzeltir) // ---------------------------------------------------------------------------- // Her piksel icin isin (w) boyunca ince adimlarla takim solid'ine giris/cikis // bulunur (dairesel/kapsul footprint dogal olarak ortaya cikar — dikdortgen degil). // Koridor [near,far] icinden cikarilan aralik near/far'i gunceller: // - kesim near tarafini kapatiyorsa near yukari (+ yon: newNear=max(near,cikW)) // - far tarafini kapatiyorsa far asagi // - delinme (kesim tum koridoru kapsar) guard -> piksel bosalt (dolu=0). // Hacim yalniz takim ekseni ile en paralel harita GRUBUNDAN sayilir (cift sayim yok). // --------------------------------------------------------------------------- void ZBuffer6Motoru::haritaKes(DepthMap& dm, Vec3d poz, const TakimGeo& takim) { if (dm.nu == 0 || dm.nv == 0) return; const f64 R = takim.cap * 0.5; if (R <= 0) return; const f64 boy = takim.boy; const Vec3d T = takim.eksen; const int a = dm.uEksen, b = dm.vEksen, w = dm.isinEksen; const f64 wEkseniParalel = std::fabs(bilesen(T, w)); const bool hacimHaritasi = wEkseniParalel > 0.5 && dm.pozitif; // tek grup sayar // Uc alti profil derinligi (eksen boyunca en fazla asagi ne kadar iner). const bool kuresel = takim.tip == TakimGeo::Tip::Kuresel; const bool torus = takim.tip == TakimGeo::Tip::Torus; const f64 rc = std::clamp(takim.koseR, 0.0, R); const f64 ucDerinlik = kuresel ? R : (torus ? rc : 0.0); // Etki-kutusu yaricapi (uv duzleminde) — konservatif R + boy izdusumu. const f64 kutuR = R + boy * std::sqrt(std::max(0.0, 1.0 - wEkseniParalel * wEkseniParalel)); const f64 pu = bilesen(poz, a); const f64 pv = bilesen(poz, b); auto aralik = [&](f64 merkez, f64 o0, f64 hucre, u32 n, u32& i0, u32& i1) { const f64 lo = (merkez - kutuR - o0) / hucre; const f64 hi = (merkez + kutuR - o0) / hucre; if (hi < 0 || lo > n) { i0 = 1; i1 = 0; return; } i0 = static_cast(std::max(0.0, std::floor(lo))); i1 = static_cast(std::min(n - 1, std::ceil(hi))); // grid-disi clamp }; u32 iu0, iu1, iv0, iv1; aralik(pu, dm.u0, dm.hucreU, dm.nu, iu0, iu1); aralik(pv, dm.v0, dm.hucreV, dm.nv, iv0, iv1); if (iu0 > iu1 || iv0 > iv1) return; // Isin taramasi footprint'i (hot path daraltma). const f64 pw = bilesen(poz, w); const f64 wYar = R + boy; const f64 orn = std::max(cozunurluk_ * 0.5, (dm.w1 - dm.w0) / 4096.0); const f64 pikselAlan = dm.hucreU * dm.hucreV; for (u32 iv = iv0; iv <= iv1; ++iv) { const f64 vc = dm.v0 + (iv + 0.5) * dm.hucreV; for (u32 iu = iu0; iu <= iu1; ++iu) { const u32 i = dm.idx(iu, iv); if (!dm.dolu[i]) continue; // bos piksel -> atla (guard) const f64 uc = dm.u0 + (iu + 0.5) * dm.hucreU; // Isin boyunca solid giris/cikis (dairesel kesit dogal): footprint tarama. const f64 wLo = std::max(static_cast(dm.near[i]), pw - wYar - ucDerinlik); const f64 wHi = std::min(static_cast(dm.far[i]), pw + wYar); if (wHi <= wLo) continue; f64 girW = 1e300, cikW = -1e300; for (f64 wv = wLo; wv <= wHi + 1e-9; wv += orn) { f64 xyz[3]; xyz[a] = uc; xyz[b] = vc; xyz[w] = wv; const Vec3d p{xyz[0], xyz[1], xyz[2]}; if (takimSolidIci(p, poz, takim)) { girW = std::min(girW, wv); cikW = std::max(cikW, wv); } } if (girW > cikW) continue; // bu piksel takim disi const f64 cutMin = std::max(dm.near[i], girW - orn * 0.5); const f64 cutMax = std::min(dm.far[i], cikW + orn * 0.5); if (cutMin >= cutMax) continue; const f64 near0 = dm.near[i], far0 = dm.far[i]; // Delinme: kesim tum koridoru kapsar -> piksel bosalt. if (cutMin <= near0 + 1e-6 && cutMax >= far0 - 1e-6) { if (hacimHaritasi) kaldirilanHacim_ += (far0 - near0) * pikselAlan; dm.near[i] = 0; dm.far[i] = 0; dm.dolu[i] = 0; // near>=far -> bosalt continue; } // 2.5D tek-koridor: near tarafini asindir (kesim near'a bitisikse) veya // far tarafini asindir. Ic delik (ortada) tek-degerli modelde temsil // edilemez -> en yakin ucu asindir (Vericut kabul; undercut icin Dexel3). const f64 nearMesafe = cutMin - near0; // kesimin near'dan uzakligi const f64 farMesafe = far0 - cutMax; f64 kald; if (nearMesafe <= farMesafe) { // near tarafina daha yakin -> near yukari (koridoru kisalt) kald = cutMax - near0; dm.near[i] = static_cast(cutMax); } else { kald = far0 - cutMin; dm.far[i] = static_cast(cutMin); } if (dm.near[i] >= dm.far[i]) { dm.dolu[i] = 0; } // near>=far guard if (hacimHaritasi) kaldirilanHacim_ += std::max(0.0, kald) * pikselAlan; } } } // --------------------------------------------------------------------------- // takimUygula // --------------------------------------------------------------------------- void ZBuffer6Motoru::takimUygula(Vec3d poz, const TakimGeo& takim) { if (!kuruldu_) return; if (!sonluMu(poz)) return; TakimGeo t = takim; t.eksen = gecerliEksen(takim.eksen); if (!(t.cap > 0)) return; for (auto& dm : haritalar_) haritaKes(dm, poz, t); } // --------------------------------------------------------------------------- // sweptUygula — adaptif adim // --------------------------------------------------------------------------- void ZBuffer6Motoru::sweptUygula(Vec3d a, Vec3d b, const TakimGeo& takim) { if (!kuruldu_) return; if (!sonluMu(a) || !sonluMu(b)) return; const Vec3d d = b - a; const f64 uz = d.uzunluk(); if (uz < 0.01) { takimUygula(a, takim); return; } const f64 adim = std::max(cozunurluk_ * 0.5, 1e-3); const f64 oranliCap = uz / adim; const u32 ustSinir = static_cast(std::min(oranliCap, 100000.0)); u32 n = static_cast(std::ceil(uz / adim)); if (n > ustSinir) { n = ustSinir; supurmeCapUyarisi_ = true; } if (n < 1) n = 1; for (u32 i = 0; i <= n; ++i) { const f64 t = static_cast(i) / n; takimUygula(a + d * t, takim); } } // --------------------------------------------------------------------------- // noktaIcerideMi — +Z haritasi koridoru (2.5D) + silindir/bbox maske // ---------------------------------------------------------------------------- // Z-buffer tek-degerli koridor modeli: nokta, +Z haritasindaki [near,far] // aralninda ve (silindir ise) XY maske icinde ise malzeme icinde sayilir. // (Dexel3 3-grid AND'e gore daha kaba — Z-buffer'in dogasi geregi.) // --------------------------------------------------------------------------- bool ZBuffer6Motoru::noktaIcerideMi(Vec3d p) const { if (!kuruldu_ || !sonluMu(p)) return false; const DepthMap& dm = haritalar_[4]; // +Z if (dm.nu == 0 || dm.nv == 0) return false; // bos kutuk // Silindir XY maske. if (silindirYaricap_ >= 0.0) { const f64 dx = p.x - merkez_.x, dy = p.y - merkez_.y; if (dx * dx + dy * dy > silindirYaricap_ * silindirYaricap_) return false; } const f64 pu = bilesen(p, dm.uEksen); const f64 pv = bilesen(p, dm.vEksen); const f64 pw = bilesen(p, dm.isinEksen); const f64 fu = (pu - dm.u0) / dm.hucreU; const f64 fv = (pv - dm.v0) / dm.hucreV; if (fu < 0 || fv < 0 || fu >= dm.nu || fv >= dm.nv) return false; const u32 iu = static_cast(fu); const u32 iv = static_cast(fv); const u32 i = dm.idx(iu, iv); if (!dm.dolu[i]) return false; return pw >= dm.near[i] && pw <= dm.far[i]; } // --------------------------------------------------------------------------- // meshCikar — +Z near/far IKISI (ust+alt) + 4 yan duvar (Dexel3 kalibi) // ---------------------------------------------------------------------------- // WEB HATA DUZELTMESI (b): web mesh yalniz +Z heightfield'di (yalniz ust yuzey). // Burada far=ust yuzey + near=alt yuzey iki quad + komsu farkina gore yan/ic // duvar seridi. Bos piksel -> delik (quad yok). NaN bbox uretmez (bos kutuk guard). // --------------------------------------------------------------------------- void ZBuffer6Motoru::meshCikar(MeshVeri& cikti) { cikti.temizle(); const DepthMap& dm = haritalar_[4]; // +Z if (dm.nu == 0 || dm.nv == 0) return; // bos kutuk -> bos mesh (NaN uretme) const u32 nu = dm.nu, nv = dm.nv; const f64 x0 = dm.u0, y0 = dm.v0; const f64 cu = dm.hucreU, cv = dm.hucreV; auto dolu = [&](u32 iu, u32 iv) { return dm.dolu[dm.idx(iu, iv)] != 0; }; // +Z haritasi: near = alt (kucuk z), far = ust (buyuk z). auto ustZ = [&](u32 iu, u32 iv) { return dm.far[dm.idx(iu, iv)]; }; auto altZ = [&](u32 iu, u32 iv) { return dm.near[dm.idx(iu, iv)]; }; auto ekleVertex = [&](f32 x, f32 y, f32 z, f32 nx, f32 ny, f32 nz) -> u32 { const u32 vi = cikti.vertexSayi(); cikti.pozisyon.push_back(x); cikti.pozisyon.push_back(y); cikti.pozisyon.push_back(z); cikti.normal.push_back(nx); cikti.normal.push_back(ny); cikti.normal.push_back(nz); cikti.sinir.genislet({x, y, z}); return vi; }; // Ust yuzey normali (central-diff, far/ust yukseklik farkindan). auto ustNormal = [&](u32 iu, u32 iv) -> Vec3d { const f32 h = ustZ(iu, iv); if (!std::isfinite(h)) return {0, 0, 1}; const u32 il = iu > 0 ? iu - 1 : iu; const u32 ir = iu + 1 < nu ? iu + 1 : iu; const u32 ib = iv > 0 ? iv - 1 : iv; const u32 it = iv + 1 < nv ? iv + 1 : iv; const f32 hl = dolu(il, iv) ? ustZ(il, iv) : h; const f32 hr = dolu(ir, iv) ? ustZ(ir, iv) : h; const f32 hb = dolu(iu, ib) ? ustZ(iu, ib) : h; const f32 ht = dolu(iu, it) ? ustZ(iu, it) : h; const f64 dzx = hr - hl, dzy = ht - hb; Vec3d nrm{-dzx / (2 * cu), -dzy / (2 * cv), 1.0}; return nrm.birim(); }; const f32 esik = static_cast(std::max(cu, cv)); for (u32 iv = 0; iv < nv; ++iv) { for (u32 iu = 0; iu < nu; ++iu) { if (!dolu(iu, iv)) continue; const f32 hu = ustZ(iu, iv), ha = altZ(iu, iv); const f32 xa = static_cast(x0 + iu * cu), xb = static_cast(x0 + (iu + 1) * cu); const f32 ya = static_cast(y0 + iv * cv), yb = static_cast(y0 + (iv + 1) * cv); const Vec3d nu3 = ustNormal(iu, iv); const f32 nx = static_cast(nu3.x), ny = static_cast(nu3.y), nz = static_cast(nu3.z); // ust quad (far, yukari bakan) const u32 a0 = ekleVertex(xa, ya, hu, nx, ny, nz); const u32 a1 = ekleVertex(xb, ya, hu, nx, ny, nz); const u32 a2 = ekleVertex(xb, yb, hu, nx, ny, nz); const u32 a3 = ekleVertex(xa, yb, hu, nx, ny, nz); cikti.indeks.insert(cikti.indeks.end(), {a0, a1, a2, a0, a2, a3}); // alt quad (near, asagi bakan) const u32 b0 = ekleVertex(xa, ya, ha, 0, 0, -1); const u32 b1 = ekleVertex(xb, ya, ha, 0, 0, -1); const u32 b2 = ekleVertex(xb, yb, ha, 0, 0, -1); const u32 b3 = ekleVertex(xa, yb, ha, 0, 0, -1); cikti.indeks.insert(cikti.indeks.end(), {b0, b2, b1, b0, b3, b2}); // 4 yan duvar: komsu bos VEYA ust yukseklik farki > esik. struct Kom { int dux, dvy; f32 wx0, wy0, wx1, wy1; f32 nx, ny; }; const Kom komsular[4] = { {-1, 0, xa, ya, xa, yb, -1, 0}, { 1, 0, xb, yb, xb, ya, 1, 0}, { 0,-1, xb, ya, xa, ya, 0,-1}, { 0, 1, xa, yb, xb, yb, 0, 1}, }; for (const Kom& k : komsular) { const int ju = static_cast(iu) + k.dux; const int jv = static_cast(iv) + k.dvy; f32 komHu = ha; bool duvar = true; if (ju >= 0 && jv >= 0 && ju < static_cast(nu) && jv < static_cast(nv) && dolu(static_cast(ju), static_cast(jv))) { komHu = ustZ(static_cast(ju), static_cast(jv)); duvar = std::fabs(hu - komHu) > esik; } if (!duvar) continue; const f32 ust = hu, alt = (komHu < hu) ? komHu : ha; if (ust <= alt) continue; const u32 v0 = ekleVertex(k.wx0, k.wy0, ust, k.nx, k.ny, 0); const u32 v1 = ekleVertex(k.wx1, k.wy1, ust, k.nx, k.ny, 0); const u32 v2 = ekleVertex(k.wx1, k.wy1, alt, k.nx, k.ny, 0); const u32 v3 = ekleVertex(k.wx0, k.wy0, alt, k.nx, k.ny, 0); cikti.indeks.insert(cikti.indeks.end(), {v0, v1, v2, v0, v2, v3}); } } } } } // namespace fiko::kutuk