#include "fiko/dokum/voxel_gorsel.hpp" #include #include #include "fiko/dokum/voxelize.hpp" // noktaUcgenEnYakin (küp köşesi → gerçek yüzey) namespace fiko::dokum { namespace { // renkRampa.glsl rampaJet ikizi (aynı formül; kesintisiz). Vec3f jet(f32 t) { t = std::clamp(t, 0.0f, 1.0f); const f32 r = std::clamp(1.5f - std::fabs(4.0f * t - 3.0f), 0.0f, 1.0f); const f32 g = std::clamp(1.5f - std::fabs(4.0f * t - 2.0f), 0.0f, 1.0f); const f32 b = std::clamp(1.5f - std::fabs(4.0f * t - 1.0f), 0.0f, 1.0f); return {r, g, b}; } // renkRampa.glsl rampaTurbo ikizi (Google Turbo polinom yaklaşımı). Vec3f turbo(f32 t) { t = std::clamp(t, 0.0f, 1.0f); const Vec3f c0{0.1140f, 0.0628f, 0.2248f}; const Vec3f c1{6.7164f, 3.1822f, 7.5715f}; const Vec3f c2{-66.094f, -4.9279f, -10.089f}; const Vec3f c3{228.766f, 25.005f, -91.541f}; const Vec3f c4{-334.83f, -69.315f, 288.586f}; const Vec3f c5{218.766f, 67.523f, -305.20f}; const Vec3f c6{-52.889f, -21.549f, 110.514f}; const f32 t2 = t * t, t3 = t2 * t, t4 = t2 * t2, t5 = t4 * t, t6 = t4 * t2; Vec3f c = c0 + c1 * t + c2 * t2 + c3 * t3 + c4 * t4 + c5 * t5 + c6 * t6; return {std::clamp(c.x, 0.0f, 1.0f), std::clamp(c.y, 0.0f, 1.0f), std::clamp(c.z, 0.0f, 1.0f)}; } // Matplotlib "inferno" polinom yaklaşımı (mattz, shadertoy WlfXRN, CC0) — // algısal-düzgün, gri tonlamada da okunur, renk-körü dostu "akkor" palet. Vec3f inferno(f32 t) { t = std::clamp(t, 0.0f, 1.0f); const Vec3f c0{0.00021894f, 0.00165100f, -0.01948090f}; const Vec3f c1{0.10651342f, 0.56395644f, 3.93271239f}; const Vec3f c2{11.60249308f, -3.97285397f, -15.94239411f}; const Vec3f c3{-41.70399613f, 17.43639888f, 44.35414520f}; const Vec3f c4{77.16293570f, -33.40235894f, -81.80730926f}; const Vec3f c5{-71.31942824f, 32.62606426f, 73.20951986f}; const Vec3f c6{25.13112622f, -12.24266895f, -23.07032500f}; const Vec3f c = c0 + (c1 + (c2 + (c3 + (c4 + (c5 + c6 * t) * t) * t) * t) * t) * t; return {std::clamp(c.x, 0.0f, 1.0f), std::clamp(c.y, 0.0f, 1.0f), std::clamp(c.z, 0.0f, 1.0f)}; } // [min,max] → [0,1] güvenli normalizasyon (renkRampa.glsl alanNorm ikizi). f32 norm(f32 v, f32 mn, f32 mx) { const f32 d = mx - mn; return d > 1e-6f ? std::clamp((v - mn) / d, 0.0f, 1.0f) : 0.0f; } // Kademeli (banded) kip: t'yi band merkezine kuantala (MAGMA 15'li lejant). f32 bandla(const VoxelGorselAyar& a, f32 t) { if (!a.bandli || a.bandSayi < 2) return t; const f32 n = (f32)a.bandSayi; return (std::floor(std::min(t, 0.9999f) * n) + 0.5f) / n; } // Kesit süpürme: eksen boyunca düzlem hücre indeksi (kesitOran·(n−1)). // Dönüş: -1 = kesit kapalı; ≥0 = bu indeksten büyük hücreler atlanır. int kesitIndeks(const VoxelGorselAyar& a, u32 nx, u32 ny, u32 nz) { if (a.kesitEksen < 0 || a.kesitEksen > 2 || a.kesitOran >= 0.999f) return -1; const u32 n = a.kesitEksen == 0 ? nx : a.kesitEksen == 1 ? ny : nz; return (int)(std::clamp(a.kesitOran, 0.0f, 1.0f) * (f32)(n - 1)); } inline bool kesitDisi(const VoxelGorselAyar& a, int kIdx, u32 i, u32 j, u32 k) { if (kIdx < 0) return false; const u32 e = a.kesitEksen == 0 ? i : a.kesitEksen == 1 ? j : k; return (int)e > kIdx; } // Küpün 6 yüzü — CCW dış-normal (model pipeline BACK cull; analiz NONE). // maske: hangi yüzler üretilecek (bit f → aşağıdaki yüz sırası). Minecraft // tarzı yüz-ayıklama: komşusu da çizilen hücreye bakan İÇ yüzler atlanır → // boşluksuz kusursuz blok yüzeyi + 5-20× az üçgen (perf). // Yüz sırası (acikYuzler ile eş): 0:-z 1:+z 2:-y 3:+y 4:+x 5:-x. // (Gövde parcaSdfOrnek olarak dosya SONUNDA — namespace dışı, test erişebilsin.) f32 sdfOrnek(const VoxelKafes& k, const Vec3d& p) { const u32 nx = k.nx(), ny = k.ny(), nz = k.nz(); const Vec3d m0 = k.merkez(0, 0, 0); const f64 h = nx > 1 ? std::fabs(k.merkez(1, 0, 0).x - m0.x) : 1.0; if (!(h > 0)) return 0.0f; // Merkez-uzayında sürekli indeks. const f64 fx = (p.x - m0.x) / h, fy = (p.y - m0.y) / h, fz = (p.z - m0.z) / h; const f64 cx = std::clamp(fx, 0.0, (f64)nx - 1.0); const f64 cy = std::clamp(fy, 0.0, (f64)ny - 1.0); const f64 cz = std::clamp(fz, 0.0, (f64)nz - 1.0); const u32 i0 = (u32)cx, j0 = (u32)cy, k0 = (u32)cz; const u32 i1 = std::min(i0 + 1, nx - 1), j1 = std::min(j0 + 1, ny - 1), k1 = std::min(k0 + 1, nz - 1); const f64 tx = cx - i0, ty = cy - j0, tz = cz - k0; auto s = [&](u32 i, u32 j, u32 kk) { return (f64)k.parcaSdf[k.indeks(i, j, kk)]; }; const f64 c00 = s(i0, j0, k0) * (1 - tx) + s(i1, j0, k0) * tx; const f64 c10 = s(i0, j1, k0) * (1 - tx) + s(i1, j1, k0) * tx; const f64 c01 = s(i0, j0, k1) * (1 - tx) + s(i1, j0, k1) * tx; const f64 c11 = s(i0, j1, k1) * (1 - tx) + s(i1, j1, k1) * tx; const f64 c0 = c00 * (1 - ty) + c10 * ty, c1 = c01 * (1 - ty) + c11 * ty; return (f32)(c0 * (1 - tz) + c1 * tz); } // Köşeyi parça yüzeyine geri it (yalnız DIŞARIDAysa). Köşeler hücre kafesinde // ORTAK → komşu küpler aynı köşeyi aynı yere iter → SU GEÇİRMEZ (çatlak yok). // Bu, küp-düzlem kırpmasına (Sutherland-Hodgman) tercih edildi: o yöntem komşu // kapakları hizalamaz ve hücreler arası çatlak bırakır. Vec3d koseyiYuzeyeIt(const VoxelKafes& k, Vec3d p, f64 hMesh) { const f32 d = sdfOrnek(k, p); // Trilineer SDF yalnız UCUZ ELEME için: derin içerideyse kesin dokunma. // KARAR için kullanılamaz — içbükey köşede yanılır ("içeride" der, oysa dışarıda; // L profilinde 208 vertex 1.000mm dışarıda kalmıştı). if (d < -1.5f) return p; // TERCİH EDİLEN: gerçek çorbaya en-yakın nokta. Keskin köşede sonuç parçanın // KÖŞE VERTEX'i → o köşeye komşu tüm küp köşeleri AYNI noktaya oturur = SİVRİ. if (!k.parcaUcgen.empty()) { if (noktaIcindeHam(k.parcaUcgen, p)) return p; // EXACT: gerçekten içeride // Doğrudan üçgenler üzerinde: UcgenCorba KOPYALAMA (kare başına megabaytlar). // Arama yarıçapı: yalnız sınır küpleri itilir → köşe en fazla ~1 hücre dışarıda // → 2h güvenli üst sınır, ön-eleme de hızlı kalır. const f64 ara = 2.0 * hMesh; const size_t nUc = k.parcaUcgen.size() / 3; Vec3d enIyi = p; f64 enIyiD2 = 1e300; for (size_t t = 0; t < nUc; ++t) { const Vec3d& a = k.parcaUcgen[3 * t]; const Vec3d& b = k.parcaUcgen[3 * t + 1]; const Vec3d& c = k.parcaUcgen[3 * t + 2]; const f64 mnx = std::min({a.x, b.x, c.x}), mxx = std::max({a.x, b.x, c.x}); if (p.x < mnx - ara || p.x > mxx + ara) continue; const f64 mny = std::min({a.y, b.y, c.y}), mxy = std::max({a.y, b.y, c.y}); if (p.y < mny - ara || p.y > mxy + ara) continue; const f64 mnz = std::min({a.z, b.z, c.z}), mxz = std::max({a.z, b.z, c.z}); if (p.z < mnz - ara || p.z > mxz + ara) continue; const Vec3d q = noktaUcgenEnYakin(p, a, b, c); const f64 d2 = (p - q).uzunlukKare(); if (d2 < enIyiD2) { enIyiD2 = d2; enIyi = q; } } if (enIyiD2 < (4.0 * hMesh) * (4.0 * hMesh)) return enIyi; // Ön-eleme her şeyi eledi (aday yok) → gradyan yoluna düş. } if (!(d > 0.0f)) return p; // çorba yok + SDF içeride diyor → dokunma // YEDEK (çorba yok): gradyanla it. Keskin köşeyi YUVARLAR — bilinçli taviz. const f64 e = 0.5 * hMesh; const Vec3d g{sdfOrnek(k, {p.x + e, p.y, p.z}) - sdfOrnek(k, {p.x - e, p.y, p.z}), sdfOrnek(k, {p.x, p.y + e, p.z}) - sdfOrnek(k, {p.x, p.y - e, p.z}), sdfOrnek(k, {p.x, p.y, p.z + e}) - sdfOrnek(k, {p.x, p.y, p.z - e})}; const f64 gl = g.uzunluk(); if (!(gl > 1e-9)) return p; // düz alan (gradyan yok) → güvenli: dokunma // d hücre biriminde → mesh birimine çevir. Yüzey d·hMesh kadar ĝ yönünde geride. return p - g * ((d * hMesh) / gl); } // kirp != nullptr → küp köşeleri bu AABB'ye eksen-bazlı clamp'lenir (parçayı // aşan dış hücreler yüzeye oturur; tam iç hücreler etkilenmez → no-op). // sdfK != nullptr → köşeler AYRICA gerçek parça yüzeyine itilir (AABB kutu-olmayan // parçada YETMEZ: L profilinde iç yüzeyler bounding box'ın İÇİNDE kalır, ölçüldü: // L'de iç hücrelerin %55'inin tam küpü yüzeyi 1.000mm'ye kadar aşıyordu). void kupEkle(MeshVeri& m, const Vec3d& mrk, f64 yari, const Vec3f& renk, u8 maske = 0x3F, const Aabbd* kirp = nullptr, const VoxelKafes* sdfK = nullptr, f64 hMesh = 0.0) { if (maske == 0) return; // tamamen iç hücre — hiç yüz üretme f64 x0 = mrk.x - yari, x1 = mrk.x + yari; f64 y0 = mrk.y - yari, y1 = mrk.y + yari; f64 z0 = mrk.z - yari, z1 = mrk.z + yari; if (kirp) { x0 = std::clamp(x0, kirp->asgari.x, kirp->azami.x); x1 = std::clamp(x1, kirp->asgari.x, kirp->azami.x); y0 = std::clamp(y0, kirp->asgari.y, kirp->azami.y); y1 = std::clamp(y1, kirp->asgari.y, kirp->azami.y); z0 = std::clamp(z0, kirp->asgari.z, kirp->azami.z); z1 = std::clamp(z1, kirp->asgari.z, kirp->azami.z); // Tamamen dışarı düşen hücre → sıfır hacim; üçgen üretme (dejenere yüz). if (!(x1 > x0) || !(y1 > y0) || !(z1 > z0)) return; } Vec3d kd[8] = {{x0, y0, z0}, {x1, y0, z0}, {x1, y1, z0}, {x0, y1, z0}, {x0, y0, z1}, {x1, y0, z1}, {x1, y1, z1}, {x0, y1, z1}}; bool bozuldu = false; // köşe itildi mi → normal yeniden hesaplanmalı if (sdfK && sdfK->parcaSdf.size() == sdfK->hucreSayi() && hMesh > 0) for (Vec3d& c : kd) { const Vec3d y = koseyiYuzeyeIt(*sdfK, c, hMesh); if (!bozuldu && (y - c).uzunlukKare() > 1e-18) bozuldu = true; c = y; } const Vec3f k[8] = { {(f32)kd[0].x, (f32)kd[0].y, (f32)kd[0].z}, {(f32)kd[1].x, (f32)kd[1].y, (f32)kd[1].z}, {(f32)kd[2].x, (f32)kd[2].y, (f32)kd[2].z}, {(f32)kd[3].x, (f32)kd[3].y, (f32)kd[3].z}, {(f32)kd[4].x, (f32)kd[4].y, (f32)kd[4].z}, {(f32)kd[5].x, (f32)kd[5].y, (f32)kd[5].z}, {(f32)kd[6].x, (f32)kd[6].y, (f32)kd[6].z}, {(f32)kd[7].x, (f32)kd[7].y, (f32)kd[7].z}}; // Yüz: 4 köşe (CCW dıştan bakışta) + dış normal. static const int yuz[6][4] = {{0, 3, 2, 1}, {4, 5, 6, 7}, {0, 1, 5, 4}, {2, 3, 7, 6}, {1, 2, 6, 5}, {0, 4, 7, 3}}; static const Vec3f nrm[6] = {{0, 0, -1}, {0, 0, 1}, {0, -1, 0}, {0, 1, 0}, {1, 0, 0}, {-1, 0, 0}}; for (int f = 0; f < 6; ++f) { if (!(maske & (1 << f))) continue; // Köşeler yüzeye itildiyse yüz artık eksen-hizalı DEĞİL → sabit nrm[] yanlış // aydınlatır (kırık gölge). Normali deforme dörtgenin kendisinden türet. Vec3f n = nrm[f]; if (bozuldu) { const Vec3f& a = k[yuz[f][0]]; const Vec3f& b = k[yuz[f][1]]; const Vec3f& c2 = k[yuz[f][2]]; const Vec3f u{b.x - a.x, b.y - a.y, b.z - a.z}; const Vec3f w{c2.x - a.x, c2.y - a.y, c2.z - a.z}; const Vec3f cr{u.y * w.z - u.z * w.y, u.z * w.x - u.x * w.z, u.x * w.y - u.y * w.x}; const f32 len = std::sqrt(cr.x * cr.x + cr.y * cr.y + cr.z * cr.z); // Dejenere (sıfır alan) → eski eksen normaline düş. if (len > 1e-12f) n = {cr.x / len, cr.y / len, cr.z / len}; } const u32 v0 = (u32)m.vertexler.size(); for (int c = 0; c < 4; ++c) { Vertex vv{}; vv.konum[0] = k[yuz[f][c]].x; vv.konum[1] = k[yuz[f][c]].y; vv.konum[2] = k[yuz[f][c]].z; vv.normal[0] = n.x; vv.normal[1] = n.y; vv.normal[2] = n.z; vv.renk[0] = renk.x; vv.renk[1] = renk.y; vv.renk[2] = renk.z; vv.altKimlik = 0; m.vertexler.push_back(vv); } m.indeksler.push_back(v0); m.indeksler.push_back(v0 + 1); m.indeksler.push_back(v0 + 2); m.indeksler.push_back(v0); m.indeksler.push_back(v0 + 2); m.indeksler.push_back(v0 + 3); } } // Çizilen-hücre kümesine göre açık yüz maskesi (kupEkle yüz sırası). // Komşu grid içinde ve çiziliyorsa o yüz İÇ yüzdür → üretilmez. u8 acikYuzler(const VoxelKafes& kafes, const std::vector& ciz, u32 i, u32 j, u32 k) { static const int d[6][3] = {{0, 0, -1}, {0, 0, 1}, {0, -1, 0}, {0, 1, 0}, {1, 0, 0}, {-1, 0, 0}}; const int nx = (int)kafes.nx(), ny = (int)kafes.ny(), nz = (int)kafes.nz(); u8 m = 0; for (int f = 0; f < 6; ++f) { const int ni = (int)i + d[f][0], nj = (int)j + d[f][1], nk = (int)k + d[f][2]; const bool komsuCiz = ni >= 0 && nj >= 0 && nk >= 0 && ni < nx && nj < ny && nk < nz && ciz[kafes.indeks((u32)ni, (u32)nj, (u32)nk)]; if (!komsuCiz) m |= (u8)(1 << f); } return m; } } // namespace f32 parcaSdfOrnek(const VoxelKafes& k, const Vec3d& p) { if (k.parcaSdf.size() != k.hucreSayi() || !k.gecerliMi()) return 0.0f; return sdfOrnek(k, p); } Vec3f voxelRampaRenk(VoxelRampa rampa, f32 t) { switch (rampa) { case VoxelRampa::Jet: return jet(t); case VoxelRampa::Inferno: return inferno(t); default: return turbo(t); } } bool gorselKabukMu(const VoxelKafes& kafes, u32 i, u32 j, u32 k) { if (!gorselMetalMi(kafes, kafes.indeks(i, j, k))) return false; static const int d[6][3] = {{0, 0, -1}, {0, 0, 1}, {0, -1, 0}, {0, 1, 0}, {1, 0, 0}, {-1, 0, 0}}; const int nx = (int)kafes.nx(), ny = (int)kafes.ny(), nz = (int)kafes.nz(); for (auto& f : d) { const int ni = (int)i + f[0], nj = (int)j + f[1], nk = (int)k + f[2]; if (ni < 0 || nj < 0 || nk < 0 || ni >= nx || nj >= ny || nk >= nz) return true; if (!gorselMetalMi(kafes, kafes.indeks((u32)ni, (u32)nj, (u32)nk))) return true; } return false; } bool voxelAlanDeger(const VoxelKafes& kafes, const KatilasmaSonuc& sonuc, VoxelAlan alan, u32 idx, f64& deger) { switch (alan) { case VoxelAlan::Sicaklik: if (idx >= kafes.T.size()) return false; deger = kafes.T[idx]; return true; case VoxelAlan::KatiOran: if (idx >= kafes.fs.size()) return false; deger = kafes.fs[idx]; return true; case VoxelAlan::KatilasmaSuresi: if (idx >= sonuc.katilasmaSuresi.size()) return false; deger = sonuc.katilasmaSuresi[idx]; return deger > 0.0; // 0 = hiç katılaşmadı / dolgu değil → atla case VoxelAlan::Niyama: if (idx >= sonuc.niyama.size()) return false; deger = sonuc.niyama[idx]; return deger > 0.0; default: return false; } } VoxelGorselSonuc voxelMeshKur(const VoxelKafes& kafes, const KatilasmaSonuc& sonuc, const VoxelGorselAyar& ayar) { VoxelGorselSonuc r; if (ayar.alan == VoxelAlan::Kapali || !kafes.gecerliMi() || !sonuc.hazir) return r; const u32 nx = kafes.nx(), ny = kafes.ny(), nz = kafes.nz(); const f64 hMesh = kafes.hucreHacim() > 0 ? std::cbrt(kafes.hucreHacim()) : 0.0; // merkez() mesh birimindedir → hücre boyutu da mesh biriminde (fizik ölçeği DEĞİL). const Vec3d m0 = kafes.merkez(0, 0, 0), m1 = kafes.merkez(1, 0, 0); const f64 hcMesh = nx > 1 ? std::fabs(m1.x - m0.x) : (hMesh > 0 ? hMesh : 1.0); const bool blok = ayar.kupOlcek >= 0.99f; // 1=aralıksız blok, <1=küçük bağımsız küpler const f64 yari = 0.5 * hcMesh * (blok ? 1.0 : (f64)std::max(0.2f, ayar.kupOlcek)); if (!(hcMesh > 0)) return r; // 1. geçiş: aday hücreleri + değerlerini topla (dolgu + geçerli alan değeri). struct Hucre { u32 i, j, k; f32 v; }; std::vector aday; aday.reserve(kafes.hucreSayi() / 4 + 1); f32 mn = 3.0e38f, mx = -3.0e38f; for (u32 k = 0; k < nz; ++k) for (u32 j = 0; j < ny; ++j) for (u32 i = 0; i < nx; ++i) { const u32 idx = kafes.indeks(i, j, k); if (!gorselMetalMi(kafes, idx)) continue; // Sinir = parça-dışı kabuk (taşma) if (ayar.yalnizSinir && !gorselKabukMu(kafes, i, j, k)) continue; f64 deger; if (!voxelAlanDeger(kafes, sonuc, ayar.alan, idx, deger)) continue; const f32 v = (f32)deger; aday.push_back({i, j, k, v}); mn = std::min(mn, v); mx = std::max(mx, v); } if (aday.empty()) return r; if (mx <= mn) mx = mn + 1.0f; // tekdüze alan → düz renk (norm güvenli) const f32 amin = ayar.otoMinMax ? mn : ayar.elleMin; const f32 amax = ayar.otoMinMax ? mx : ayar.elleMax; // 2. geçiş: yalnız AÇIK yüzleri üret (renk = rampa(norm(değer))). Tek mesh. std::vector ciz(kafes.hucreSayi(), 0); for (const Hucre& h : aday) ciz[kafes.indeks(h.i, h.j, h.k)] = 1; r.mesh.vertexler.reserve(aday.size() * 8); r.mesh.indeksler.reserve(aday.size() * 12); // Parçaya kırpma referansı (yalnız istenirse + ham sınır geçerliyse). // AABB = kaba kafes (kutu parçada yeterli); parcaSdf = GERÇEK yüzey (kutu // olmayan parçada ŞART — L profilinde iç yüzeyler AABB'nin içinde kalır). const Aabbd& ham = kafes.sinirHam(); const Aabbd* kirp = (ayar.kirpParcaya && ham.gecerliMi()) ? &ham : nullptr; const VoxelKafes* sdfK = (ayar.kirpParcaya && kafes.parcaSdf.size() == kafes.hucreSayi()) ? &kafes : nullptr; bool ilk = true; for (const Hucre& h : aday) { const f32 t = bandla(ayar, norm(h.v, amin, amax)); const Vec3f renk = voxelRampaRenk(ayar.rampa, t); const Vec3d mrk = kafes.merkez(h.i, h.j, h.k); kupEkle(r.mesh, mrk, yari, renk, blok ? acikYuzler(kafes, ciz, h.i, h.j, h.k) : 0x3F, kirp, sdfK, hcMesh); const f64 y = yari; Vec3d lod{mrk.x - y, mrk.y - y, mrk.z - y}; Vec3d hid{mrk.x + y, mrk.y + y, mrk.z + y}; if (kirp) { // AABB de kırpılmış geometriyi yansıtmalı (yoksa şişik kalır) lod = {std::clamp(lod.x, ham.asgari.x, ham.azami.x), std::clamp(lod.y, ham.asgari.y, ham.azami.y), std::clamp(lod.z, ham.asgari.z, ham.azami.z)}; hid = {std::clamp(hid.x, ham.asgari.x, ham.azami.x), std::clamp(hid.y, ham.asgari.y, ham.azami.y), std::clamp(hid.z, ham.asgari.z, ham.azami.z)}; } const Vec3f lo{(f32)lod.x, (f32)lod.y, (f32)lod.z}; const Vec3f hi{(f32)hid.x, (f32)hid.y, (f32)hid.z}; if (ilk) { r.mesh.sinir.asgari = lo; r.mesh.sinir.azami = hi; ilk = false; } r.mesh.sinir.genislet(lo); r.mesh.sinir.genislet(hi); } r.mesh.yuzSayi = 0; r.alanMin = amin; r.alanMax = amax; r.hucreSayi = (u32)aday.size(); r.gecerli = !r.mesh.vertexler.empty(); return r; } VoxelGorselSonuc dolumMeshKur(const VoxelKafes& kafes, const DolumSonuc& dolum, const VoxelGorselAyar& ayar, f64 zamanKesim) { VoxelGorselSonuc r; if (!kafes.gecerliMi() || !dolum.hazir) return r; const u32 N = kafes.hucreSayi(); if (dolum.varisZamani.size() != N) return r; const u32 nx = kafes.nx(), ny = kafes.ny(), nz = kafes.nz(); const Vec3d m0 = kafes.merkez(0, 0, 0), m1 = kafes.merkez(1, 0, 0); const f64 hcMesh = nx > 1 ? std::fabs(m1.x - m0.x) : (kafes.hucreHacim() > 0 ? std::cbrt(kafes.hucreHacim()) : 1.0); if (!(hcMesh > 0)) return r; const bool blok = ayar.kupOlcek >= 0.99f; // 1=aralıksız blok, <1=küçük bağımsız küpler const f64 yari = 0.5 * hcMesh * (blok ? 1.0 : (f64)std::max(0.2f, ayar.kupOlcek)); // Aday hücreler: dolan (varış≥0) + zaman-kesim altı (animasyon). Renk aralığı // TÜM dolum süresine göre normalize (kesim ilerlerken renk-tutarlı). struct Hucre { u32 i, j, k; f32 v; }; std::vector aday; f32 mn = 3.0e38f, mx = -3.0e38f; for (u32 k = 0; k < nz; ++k) for (u32 j = 0; j < ny; ++j) for (u32 i = 0; i < nx; ++i) { const u32 idx = kafes.indeks(i, j, k); const f64 tz = dolum.varisZamani[idx]; if (tz < 0.0) continue; // dolmadı / metal değil if (!gorselMetalMi(kafes, idx)) continue; // Sinir kabuk çizilmez (taşma) if (ayar.yalnizSinir && !gorselKabukMu(kafes, i, j, k)) continue; const f32 v = (f32)tz; mn = std::min(mn, v); mx = std::max(mx, v); if (zamanKesim >= 0.0 && tz > zamanKesim) continue; // henüz dolmadı (anim) aday.push_back({i, j, k, v}); } if (mx <= mn) mx = mn + 1.0f; if (aday.empty()) { r.alanMin = mn; r.alanMax = mx; return r; } const f32 amin = ayar.otoMinMax ? mn : ayar.elleMin; const f32 amax = ayar.otoMinMax ? mx : ayar.elleMax; std::vector ciz(N, 0); for (const Hucre& hc : aday) ciz[kafes.indeks(hc.i, hc.j, hc.k)] = 1; r.mesh.vertexler.reserve(aday.size() * 8); r.mesh.indeksler.reserve(aday.size() * 12); // Kırpma referansları — voxelMeshKur/alanMeshKur ile AYNI (bu döngü `hc` adını // kullandığı için önceki toplu düzenlemeler onu ATLAMIŞTI; "Voxel küpler (hızlı)" // dolum görünümü buradan çizilir ve kırpmasız taşıyordu). const Aabbd& ham = kafes.sinirHam(); const Aabbd* kirp = (ayar.kirpParcaya && ham.gecerliMi()) ? &ham : nullptr; const VoxelKafes* sdfK = (ayar.kirpParcaya && kafes.parcaSdf.size() == kafes.hucreSayi()) ? &kafes : nullptr; bool ilk = true; for (const Hucre& hc : aday) { const f32 t = bandla(ayar, norm(hc.v, amin, amax)); const Vec3f renk = voxelRampaRenk(ayar.rampa, t); const Vec3d mrk = kafes.merkez(hc.i, hc.j, hc.k); kupEkle(r.mesh, mrk, yari, renk, blok ? acikYuzler(kafes, ciz, hc.i, hc.j, hc.k) : 0x3F, kirp, sdfK, hcMesh); const f64 y = yari; Vec3d lod{mrk.x - y, mrk.y - y, mrk.z - y}; Vec3d hid{mrk.x + y, mrk.y + y, mrk.z + y}; if (kirp) { lod = {std::clamp(lod.x, ham.asgari.x, ham.azami.x), std::clamp(lod.y, ham.asgari.y, ham.azami.y), std::clamp(lod.z, ham.asgari.z, ham.azami.z)}; hid = {std::clamp(hid.x, ham.asgari.x, ham.azami.x), std::clamp(hid.y, ham.asgari.y, ham.azami.y), std::clamp(hid.z, ham.asgari.z, ham.azami.z)}; } const Vec3f lo{(f32)lod.x, (f32)lod.y, (f32)lod.z}; const Vec3f hi{(f32)hid.x, (f32)hid.y, (f32)hid.z}; if (ilk) { r.mesh.sinir.asgari = lo; r.mesh.sinir.azami = hi; ilk = false; } r.mesh.sinir.genislet(lo); r.mesh.sinir.genislet(hi); } r.mesh.yuzSayi = 0; r.alanMin = amin; r.alanMax = amax; r.hucreSayi = (u32)aday.size(); r.gecerli = !r.mesh.vertexler.empty(); return r; } VoxelGorselSonuc alanMeshKur(const VoxelKafes& kafes, const std::vector& alan, const VoxelGorselAyar& ayar, f32 esik) { VoxelGorselSonuc r; if (!kafes.gecerliMi() || alan.size() != kafes.hucreSayi()) return r; const u32 nx = kafes.nx(), ny = kafes.ny(), nz = kafes.nz(); const Vec3d m0 = kafes.merkez(0, 0, 0), m1 = kafes.merkez(1, 0, 0); const f64 hcMesh = nx > 1 ? std::fabs(m1.x - m0.x) : (kafes.hucreHacim() > 0 ? std::cbrt(kafes.hucreHacim()) : 1.0); if (!(hcMesh > 0)) return r; const bool blok = ayar.kupOlcek >= 0.99f; // 1=aralıksız blok, <1=küçük bağımsız küpler const f64 yari = 0.5 * hcMesh * (blok ? 1.0 : (f64)std::max(0.2f, ayar.kupOlcek)); struct Hucre { u32 i, j, k; f32 v; }; std::vector aday; f32 mn = 3.0e38f, mx = -3.0e38f; const int kIdx = kesitIndeks(ayar, nx, ny, nz); // voxel-dilim kesit süpürme for (u32 k = 0; k < nz; ++k) for (u32 j = 0; j < ny; ++j) for (u32 i = 0; i < nx; ++i) { const u32 idx = kafes.indeks(i, j, k); if (!gorselMetalMi(kafes, idx)) continue; // Sinir kabuk çizilmez (taşma) if (ayar.yalnizSinir && !gorselKabukMu(kafes, i, j, k)) continue; const f32 v = alan[idx]; if (!(v > esik)) continue; // Lejant aralığı kesitten BAĞIMSIZ (tüm gövde) → süpürmede renk sabit. mn = std::min(mn, v); mx = std::max(mx, v); if (kesitDisi(ayar, kIdx, i, j, k)) continue; // düzlem gerisi (kesit) aday.push_back({i, j, k, v}); } if (aday.empty()) return r; if (mx <= mn) mx = mn + 1.0f; const f32 amin = ayar.otoMinMax ? mn : ayar.elleMin; const f32 amax = ayar.otoMinMax ? mx : ayar.elleMax; std::vector ciz(kafes.hucreSayi(), 0); for (const Hucre& h : aday) ciz[kafes.indeks(h.i, h.j, h.k)] = 1; r.mesh.vertexler.reserve(aday.size() * 8); r.mesh.indeksler.reserve(aday.size() * 12); // Parçaya kırpma referansı (yalnız istenirse + ham sınır geçerliyse). // AABB = kaba kafes (kutu parçada yeterli); parcaSdf = GERÇEK yüzey (kutu // olmayan parçada ŞART — L profilinde iç yüzeyler AABB'nin içinde kalır). const Aabbd& ham = kafes.sinirHam(); const Aabbd* kirp = (ayar.kirpParcaya && ham.gecerliMi()) ? &ham : nullptr; const VoxelKafes* sdfK = (ayar.kirpParcaya && kafes.parcaSdf.size() == kafes.hucreSayi()) ? &kafes : nullptr; bool ilk = true; for (const Hucre& h : aday) { const f32 t = bandla(ayar, norm(h.v, amin, amax)); const Vec3f renk = voxelRampaRenk(ayar.rampa, t); const Vec3d mrk = kafes.merkez(h.i, h.j, h.k); kupEkle(r.mesh, mrk, yari, renk, blok ? acikYuzler(kafes, ciz, h.i, h.j, h.k) : 0x3F, kirp, sdfK, hcMesh); const f64 y = yari; Vec3d lod{mrk.x - y, mrk.y - y, mrk.z - y}; Vec3d hid{mrk.x + y, mrk.y + y, mrk.z + y}; if (kirp) { // AABB de kırpılmış geometriyi yansıtmalı (yoksa şişik kalır) lod = {std::clamp(lod.x, ham.asgari.x, ham.azami.x), std::clamp(lod.y, ham.asgari.y, ham.azami.y), std::clamp(lod.z, ham.asgari.z, ham.azami.z)}; hid = {std::clamp(hid.x, ham.asgari.x, ham.azami.x), std::clamp(hid.y, ham.asgari.y, ham.azami.y), std::clamp(hid.z, ham.asgari.z, ham.azami.z)}; } const Vec3f lo{(f32)lod.x, (f32)lod.y, (f32)lod.z}; const Vec3f hi{(f32)hid.x, (f32)hid.y, (f32)hid.z}; if (ilk) { r.mesh.sinir.asgari = lo; r.mesh.sinir.azami = hi; ilk = false; } r.mesh.sinir.genislet(lo); r.mesh.sinir.genislet(hi); } r.mesh.yuzSayi = 0; r.alanMin = amin; r.alanMax = amax; r.hucreSayi = (u32)aday.size(); r.gecerli = !r.mesh.vertexler.empty(); return r; } VoxelGorselSonuc partikulMeshKur(const VoxelKafes& kafes, const std::vector& partikuller, const PartikulGorselAyar& ayar) { VoxelGorselSonuc r; if (partikuller.empty()) return r; // Oto yarıçap: hücre boyu (mesh birimi) × oran. Partikül konumu zaten mesh // biriminde → kafes yalnız ölçek referansı; geçersiz kafeste elle yarıçap şart. f64 yari = ayar.yariCap; if (!(yari > 0)) { f64 hcMesh = 0; if (kafes.gecerliMi()) { const Vec3d m0 = kafes.merkez(0, 0, 0); if (kafes.nx() > 1) hcMesh = std::fabs(kafes.merkez(1, 0, 0).x - m0.x); else if (kafes.hucreHacim() > 0) hcMesh = std::cbrt(kafes.hucreHacim()); } yari = hcMesh * std::max(0.02f, ayar.yariCapOran); } if (!(yari > 0)) return r; const u32 adim = std::max(1u, ayar.altOrnek); // Renk aralığı: sabit lejant kilidi yoksa kareden türet (min/max tarama). f32 amin = ayar.renkSabitMin, amax = ayar.renkSabitMax; if (!ayar.renkSabit) { amin = 1e30f; amax = -1e30f; for (size_t p = 0; p < partikuller.size(); p += adim) { const f32 v = ayar.hizRenk ? partikuller[p].hiz : partikuller[p].sicaklik; amin = std::min(amin, v); amax = std::max(amax, v); } } if (!(amax > amin)) amax = amin + 1.0f; // Oktahedron: 6 köşe / 8 üçgen — partikül başına en ucuz "yuvarlak" blob. // Normal = merkezden köşeye birim yön → düzgün gölge (küre izlenimi). static const Vec3f kYon[6] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}}; static const int kUcgen[8][3] = {{0, 2, 4}, {2, 1, 4}, {1, 3, 4}, {3, 0, 4}, {2, 0, 5}, {1, 2, 5}, {3, 1, 5}, {0, 3, 5}}; const size_t adet = (partikuller.size() + adim - 1) / adim; r.mesh.vertexler.reserve(adet * 6); r.mesh.indeksler.reserve(adet * 24); bool ilk = true; u32 ciz = 0; for (size_t p = 0; p < partikuller.size(); p += adim) { const SphPartikul& sp = partikuller[p]; const f32 ham = ayar.hizRenk ? sp.hiz : sp.sicaklik; f32 t = norm(ham, amin, amax); if (ayar.bandli && ayar.bandSayi >= 2) { const f32 n = (f32)ayar.bandSayi; t = (std::floor(std::min(t, 0.9999f) * n) + 0.5f) / n; } const Vec3f renk = voxelRampaRenk(ayar.rampa, t); const u32 v0 = (u32)r.mesh.vertexler.size(); for (const Vec3f& y : kYon) { Vertex vv{}; vv.konum[0] = sp.konum.x + y.x * (f32)yari; vv.konum[1] = sp.konum.y + y.y * (f32)yari; vv.konum[2] = sp.konum.z + y.z * (f32)yari; vv.normal[0] = y.x; vv.normal[1] = y.y; vv.normal[2] = y.z; vv.renk[0] = renk.x; vv.renk[1] = renk.y; vv.renk[2] = renk.z; vv.altKimlik = 0; r.mesh.vertexler.push_back(vv); } for (const auto& u : kUcgen) { r.mesh.indeksler.push_back(v0 + (u32)u[0]); r.mesh.indeksler.push_back(v0 + (u32)u[1]); r.mesh.indeksler.push_back(v0 + (u32)u[2]); } const Vec3f lo{sp.konum.x - (f32)yari, sp.konum.y - (f32)yari, sp.konum.z - (f32)yari}; const Vec3f hi{sp.konum.x + (f32)yari, sp.konum.y + (f32)yari, sp.konum.z + (f32)yari}; if (ilk) { r.mesh.sinir.asgari = lo; r.mesh.sinir.azami = hi; ilk = false; } r.mesh.sinir.genislet(lo); r.mesh.sinir.genislet(hi); ++ciz; } r.mesh.yuzSayi = 0; r.alanMin = amin; r.alanMax = amax; r.hucreSayi = ciz; // partikül adedi (görsel sözleşme: çizilen öğe sayısı) r.gecerli = !r.mesh.vertexler.empty(); return r; } } // namespace fiko::dokum