Ashenfall SF | Devlog

[Devlog] Birleşik İkili Harita Formatı (.ashenfallmap): 1.5 Milyon Satır Çöpün Temizliği, Sıfır Gecikmeli Bellek Eşleme ve Deterministik Arazi Hattı​

Ashenfall motorunun dosya sistemi ve arazi hattında, 2004 yılından miras kalan ilkel ve parçalanmış map yükleme/yazma mimarisini tamamen çöpe attım. Klasörler dolusu yüz binlerce başıboş dosyanın ana thread üzerinde senkronize I/O darboğazı oluşturduğu, kamera her hareket ettiğinde diski kilitleyen ve komşu harita sınırlarında dikiş izleri (seam drift) yaratan 20 yıllık eski kafayı projeden söküp attım.
136 harita klasörünü, 10.823 adet başıboş dosyayı ve diskteki 1.5 milyon satırlık metin/veri çöpünü tek hamlede temizledim. Yerine, hem istemcinin hem de WorldEditor'ün sıfır dinamik bellek kopyalamasıyla doğrudan okuyup yazabildiği birleşik ikili kapsayıcı mimarimi (.ashenfallmap) sıfırdan geliştirdim.

Eski Yöntem Neydi ve Neden Terk Edildi?​

Geleneksel harita yapısında tek bir açık dünya haritası; yüzlerce 000000/, 000001/ alt klasörüne bölünmüş height.raw, tile.raw, attr.atr, water.wtr, minimap.dds ve metin tabanlı areadata.txt, setting.txt gibi binlerce başıboş dosyadan oluşuyordu.
Bu ilkel düzen şu devasa problemleri beraberinde getiriyordu:
  • Senkronize CPU ve I/O Darboğazı: Karakter haritada yürürken 3×3'lük chunk penceresi her güncellendiğinde ana thread üzerinde onlarca metin dosyası satır satır ayrıştırılıyor (LoadMultipleTextData), LZO ile sıkıştırılmış küçük dosyalar için pack kilitleri (lock) tetikleniyor ve anlık mikro takılmalar (stutter / hitch) kaçınılmaz oluyordu.
  • Sınır Kayması (Border Drift) ve Dikiş İzleri: Komşu arazilerin yükseklik sınırları her iki parça tarafından ayrı ayrı saklanıyordu. Yıllar içinde bu veriler birbirinden kopmuştu; örneğin lonca haritasında (guild_war4) iki komşu chunk arasında 50 metrelik uçurumlar ve uyuşmayan 370'in üzerinde kenar noktası mevcuttu.
  • Yanlış Klasörlenen Objeler: 46.000'den fazla dünya nesnesinin 549 tanesi üzerinde durduğu arazinin değil, tamamen farklı koordinatlardaki klasörlerin içine yazılmıştı. Bu durum, kameranın görüş açısına göre ağaçların ve binaların aniden havadan belirmesine ya da erken kaybolmasına (pop-in) sebep oluyordu.
  • Çapraz Harita Doku Bozulması: Editörde tek bir haritanın zeminini düzenleyip kaydettiğinizde, ortak kullanılan paylaşımlı textureset dosyası ezildiği için alakasız diğer 10 haritanın zemin kaplamaları geri dönülemez şekilde bozuluyordu.

Yeni Sistem Nasıl Çalışıyor? (Teknik & Mimari Altyapı)​

Yeni mimaride artık klasörler, metin dosyaları ve parça parça raw array'ler yok. Harita başına yalnızca tek bir deterministik binary kapsayıcı (.ashenfallmap) bulunuyor.
Kod:
Genişlet Daralt Kopyala
┌────────────────────────────────────────────────────────────────────────┐
│                        .ashenfallmap MİMARİSİ                             │
├───────────────────┬────────────────────────────────────────────────────┤
│ 64-Bayt Header    │ PNG-stili Sihirli Baytlar (Magic), Sürüm, CRC-32   │
│ Section Table        │ 32-Bayt Hizalı Bölüm Başlıkları (Tag, Offset, CRC) │
│ Sabit Katmanlar   │ META (Ayarlar), STRS (Dizgi Havuzu), LAYR, CHNK     │
│ Raster Verileri   │ HGHT (129² uint16), TILE (256²), ATTR, WATR, MINI  │
│ Varlık Tabloları  │ INST (48-Bayt POD), AMBI (24-Bayt POD)             │
└───────────────────┴────────────────────────────────────────────────────┘

1. Sıfır Kopyalamalı Bellek Eşleme (Zero-Copy Memory-Mapped I/O)​

Dosya mimarisi mmap ve doğrudan işaretçi (pointer) aritmetiği için 16-bayt sınırlarına hizalandı. Harita arşivi istemciye EPack32 Type 0 (Stored / Sıkıştırmasız) olarak paketleniyor.
İstemci haritayı açtığında diski belleğe tek bir sanal adres uzayı olarak eşliyor (CEterPack::Get). Pack arşivi açılırken CPU üzerinde hiçbir global kilit (lock) oluşmuyor; sayfalar (pages) belleğe yalnızca dokunuldukça (page fault mantığıyla) işletim sistemi tarafından sıfır gecikmeyle çekiliyor.

2. Katman Bazlı Yükseklik Delta Filtreleme & Blob DEFLATE​

Harita geometrisi ve raster katmanları dosya içinde katı bir determinizmle organize edildi:
  • Filtrelenmiş Yükseklik Verisi (Height Delta Planes): 129×129'luk ham yükseklikler komşu piksel delta farklarına ayrılarak düşük ve yüksek bayt düzlemlerine (planes) bölündü. Bu filtreleme, salt DEFLATE sıkıştırmasına kıyasla yükseklik verisinde fazladan %27 boyut tasarrufu sağladı.
  • Türetilebilir Verilerin Tasfiyesi: Komşu arazilerin sınır halkaları (rings), yüzey normalleri ve splat haritaları dosyada saklanmıyor; yükleme anında bitişik komşulardan dikişsiz olarak türetiliyor (AssembleHeightRing). Kenar yükseklikleri matematiksel olarak eşitlendi ve drift problemi tamamen ortadan kaldırıldı.
  • Gömülü Doku Paleti (LAYR): Her harita kendi zemin katmanlarını ve UV ölçeklerini kendi içinde taşıyor. Editörde bir haritayı düzenlemek artık başka hiçbir haritanın zeminini etkileyemiyor.

3. 48-Bayt Sabit POD Nesne Dizilimi​

Metin tabanlı areadata.txt ayrıştırması tamamen kaldırıldı. Binalar, zindan blokları, ağaçlar ve efektler harita koordinatlarına göre sıralanmış 48 baytlık sabit POD (Plain Old Data) struct'ları olarak saklanıyor. Yanlış klasörlenen 549 nesne matematiksel olarak üzerinde durdukları doğru chunk sınırlarına taşındı.

Performans & Optimizasyon Kazanımları​

Bütün projedeki 136 haritanın (106 bağımsız harita, 30 hafif alias) tamamı yeni formata geçirildiğinde elde edilen net mühendislik verileri:
  • 1.5 Milyon Satır ve 10.823 Dosya Çöpe Gitti: +1.321 / -1.502.382 satırlık diff ile kaynak koddan tüm ilkel loader'lar, metin parser'lar ve başıboş klasörler kalıcı olarak kazındı.
  • %78 Disk Ayak İzi Küçülmesi: Eski sistemde 313 MB tutan dağınık harita verisi, yeni mimariyle birlikte kayıpsız olarak 67.4 MB boyutuna indi. En büyük 6×6 kar haritasının (map_n_snowm_01) tüm zemin ve nesne geometrisi yalnızca 2.37 MB.
  • 26 Milisaniyede 136 Harita: 136 haritanın tamamını açıp başlık ve CRC doğrulamalarını tamamlamak C++ motorunda toplamda yalnızca 26 ms sürdü.
  • 0.06 ms Chunk Açılma Hızı: Karakter arazide ilerlerken yeni bir parçanın bellekten çekilip çözülmesi 0.06 milisaniyeye (60 mikrosaniye) düştü. Chunk geçişlerindeki mikro takılmalar, anlık FPS düşüşleri ve disk I/O beklemesi tamamen sıfırlandı.
  • Determinizm ve Pre-Commit Güvenliği: Yazılan MapDocument mimarisi sayesinde, harita WorldEditor ile açılıp hiçbir düzenleme yapılmadan kaydedildiğinde bile diskteki dosyanın tek bir baytı dahi kaymıyor; byte-identical determinizm korunuyor. Repoya eski tip harita dosyalarının girmesi ise CI/CD hattına eklediğim pre-commit gate kontrolüyle engellendi.
Gelecekte araziyi doğrudan GPU üzerinde Compute Shader veya Indirect Draw ile tek geçişte çizeceğimiz Full-Map Terrain Renderer mimarisine geçiş için gereken temiz, contigous ve modern veri altyapısını böylece tamamlamış oldum.

​

[Devlog] Bölüm II: Sunucu Tarafının Tasfiyesi, Tek Gerçeklik Kaynağı (.ashenfallworld) ve Modern Editör Altyapısı​

İstemci ve WorldEditor tarafında .ashenfallmap formatına geçip disk I/O darboğazını sıfırladıktan sonra masada devasa bir kambur kalmıştı: Sunucu çekirdeği (game).
20 yıldır istemci dosyalarından bağımsız yaşayan, sunucu tarafında yüzlerce klasörde tutulan Setting.txt, Town.txt, LZO sıkıştırmalı server_attr, regen.txt, npc.txt ve boss.txt dosyaları hala hayattaydı. İstemcide olmayan dosyaların sunucuda aranması, sunucuda olan koordinatların istemcideki atlasinfo.txt ile uyuşmaması, büyük/küçük harf tutarsızlıkları (devilsCatacomb, CapeDragonHead) ve monkey dungeon gibi kopya haritaların diskte yüzlerce megabaytlık gereksiz veri tekrarı yaratması tam bir krizdi.
Bu ikinci fazda sunucunun tüm eski harita yükleyicilerini kalıcı olarak projeden kazıdım. Artık istemci, editör ve sunucunun tamamı aynı ikili dosyalardan ve tek bir global dünya tanımından besleniyor.

Eski Sunucu Mimarisinde Neler Vardı ve Neden Çöpe Gitti?​

  1. İki Ayrı Dünyanın Senkronizasyon Çilesi:
    İstemci hangi haritaya ışınlanacağını root/atlasinfo.txt üzerinden metin okuyarak çözerken, sunucu share/locale/.../map/index dosyasını tarıyordu. Yıllar içinde bu iki dosya birbirinden koptuğu için 22 haritaya (season1/..., season2/..., map_a2) oyuncu asla ışınlanamıyor ya da yanlış yere düşüyordu.
  2. Kopya Haritaların Veri Yükü:
    Üç maymun zindanı veya lonca köyleri gibi haritalar aynı arazi geometrisine sahip olmasına rağmen, sunucu tarafında her biri için ayrı ayrı server_attr ve metin dosyaları tutuluyordu.
  3. 20 Yıllık Ölü Kod ve Verisiz Sistemler:
    Sunucu boot olurken hiçbir haritada bulunmayan hayalet dungeon.txt dosyalarını arıyor, içi boş MonsterAreaInfo yapıları parse ediliyor ve event_flag bağımlı sahte event attribute bitleri için gereksiz CPU döngüsü harcanıyordu.

Yeni Sistem Nasıl Çalışıyor? (Format 1.1 & .ashenfallworld)​

Sunucuya .ashenfallmap okuma kabiliyetini doğrudan C++20 standardında ekledim (Shared/Map/MapFile.cpp).
Kod:
Genişlet Daralt Kopyala
┌────────────────────────────────────────────────────────────────────────┐
│                   BİRLEŞİK SUNUCU/DÜNYA MİMARİSİ                       │
├───────────────────┬────────────────────────────────────────────────────┤
│ maps/world...     │ .ashenfallworld: Global koordinatlar, Server ID,   │
│ (PLCE + STRS)     │ Harita boyutları, çakışma kuralları (Single-Source)│
├───────────────────┼────────────────────────────────────────────────────┤
│ Format 1.1 Ekleri │ TOWN (Doğma/İmparatorluk noktaları, 36-Bayt POD)   │
│ (Opsiyonel)       │ SETS (İsimli spawn setleri: boot, boss, event)     │
│                   │ SPWN (36-Bayt sabit POD spawn kayıtları)           │
└───────────────────┴────────────────────────────────────────────────────┘

1. Tek Dünya Tanımı:​

Eski atlasinfo.txt ve sunucunun index dosyasını tamamen sildim. Yerine tüm haritaların global dünya üzerindeki $(X, Y)$ taban koordinatlarını, sunucu ID'lerini ve boyutlarını tutan ikili world.ashenfallworld dosyasını getirdim.
  • Sıfır Veri Tekrarı (Aliasing): Maymun zindanları ve lonca köyleri artık diskte sadece birer ufak alias dosyasından ibaret. Zemin geometrisi ve çarpışma verisi ana araziden okunuyor; her kopya sadece kendi TOWN ve SPWN verisini saklıyor.
  • Yetkili Koordinat Sistemi: Harita dosyaları (.ashenfallmap) artık kendi içinde base koordinatı tutmuyor (her zaman 0). Haritayı dünyanın neresine koyacağına yalnızca dünya dosyası karar veriyor.

2. Sabit 36-Bayt POD Spawn Altyapısı (​

Metin tabanlı regen.txt, npc.txt ve boss.txt dosyalarının tamamı ikili formatın içine taşındı:
  • Spawnlar; türü (m, g, r, s), agresiflik bayrakları, yönü, koordinatları ve süreleri 36 baytlık sabit POD struct'ları olarak dosyada yer alıyor.
  • İsimli Setler (SETS): Haritanın açılışta doğuracağı canavarlar (SET_BOOT), zindan fonksiyonlarının tetikleyeceği boss/özel gruplar (instance_regen, npc) isimli setler altında gruplandı. Sunucu artık diskten dosya yolu aramak yerine hafızadaki haritadan ismiyle seti çekiyor (GetSpawnSet("instance_regen")).

3.​

Sunucunun harita açılışında okuduğu LZO sıkıştırmalı server_attr dosyalarının tamamı silindi. SECTREE_MANAGER, sectree bloklarını ve çarpışma bitlerini doğrudan .ashenfallmap içerisindeki ATTR raster katmanından derleme anında oluşturuyor (100 birimlik grid 50 birimlik 2x2 sunucu hücrelerine genişletiliyor).

4. WorldEditor: MonsterAreaInfo Tasfiyesi ve Yeni Spawns Katmanı​

Eski MFC/DirectX 9 döneminden kalma, arayüzü kilitleyen ve server_map klasörlerine bağımlı olan MonsterAreaInfo kodlarının tamamı çekirdekten silindi.
  • Editöre modern ImGui tabanlı bağımsız bir Spawns paneli inşa edildi.
  • Canavar isimleri doğrudan mob_proto ve sunucunun group.txt / group_group.txt hiyerarşisinden okunarak harita üzerinde gerçek isimleriyle ("Yabani Köpek x3" vb.) görselleştirildi.
  • Sahne üzerinde kutular, yön okları, kasaba ve 3 imparatorluğun dirilme noktaları (Town/Empire markers) milimetrik ızgarada doğrudan düzenlenebilir, boyutlandırılabilir ve anında haritanın ikili formatına kaydedilebilir hale getirildi.
  • 64 adımlık hafif prosedürel bir geri alma (Undo/Redo) mimarisi kuruldu.

Doğrulama ve Net Kazanımlar​

  • 680 Sunucu Dosyası Çöpe Gitti: Server/Binary/share/locale/english/map klasörünün tamamı (Setting.txt, Town.txt, server_attr ve metin tabanlı spawnlar dahil 680 dosya) silindi. Sunucu artık tüm harita verilerini tek bir MAP_FOLDER parametresiyle istemcinin ikili paket klasöründen okuyor.
  • Tam Determinizm (--verify-maps): Geliştirilen geçici debug doğrulayıcı ile 65 harita, 12.704 sectree, 208 milyon attribute hücresi ve 20.969 spawn kaydı eski metin okuyucularıyla bayt bayt kıyaslandı; bilinen tasarım kararları dışında 0 hata / 0 uyumsuzluk ile doğrulandı. Fuzzing testlerinde üretilen 16.672 bozuk dosya senaryosunun tamamı motor tarafından anında reddedildi.
  • Sıfır Uyarı, Sıfır Hata: Hem SV-Debug hem SV-Release derlemeleri 0 hata ve 0 yeni uyarı ile inşa edildi.
  • Ölü Kodların Temizliği: Verisi olmayan dungeon.txt ayrıştırıcıları, d.spawn gibi hayalet fonksiyonlar, sahte event attribute taramaları ve mob drop tablolarındaki gizli kalmış tanımsız bellek gölgeleme hataları tamamen ayıklandı.
İstemci, sunucu ve editör artık aynı binary dilden konuşuyor. Yıllardır harita düzenlerken yaşanan "sunucuya ayrı at, istemciye ayrı paketle, atlasinfo ile index'i eşitle" çilesi tamamen bitti; harita hattı profesyonel modern oyun motorları standardında tek kaynak (single source of truth) haline getirildi.
 
​


[Devlog] WorldEditor: Kuşbakışı Dünya Haritası ve Kafayı Yedirtmeyen Undo/Redo!​

1. Globe: Artık Harita İsimleriyle Boğuşmak Yok​

Eskiden editörde başka bir haritaya geçmek için dosya listelerinde kayboluyorduk. Hangi bölge neredeydi, haritalar yan yana nasıl duruyordu tam bir muammaydı.
  • Yeni Globe sekmesiyle birlikte tüm oyun dünyasını tek bir devasa tuvalde, sunucudaki gerçek koordinatlarıyla kuşbakışı görüyoruz.
  • Çift tıkla istediğin haritanın içine anında ışınlanabiliyorsun.
  • Bölgeleri ızgara üzerinde cetvel gibi hizalamak artık tek tık.

2. Ctrl+Z Yapınca Çöken Editör Tarih Oldu​

Eski editörü kullananlar bilir: Fırçayı yere 3 kere dokundursan program bellekte şişer, Ctrl+Z bastığında ya objelerin seçimi düşer ya da dokular sapıtırdı.
  • Hafıza Tüketimi Sıfırlandı: Eskiden her fırça darbesinde etraftaki 9 arazinin kopyasını alıp ram'e yığıyordu (~1.3 MB). Şimdi sadece fırçanın değdiği minik kareyi tutuyor (~2 KB). Yani 700 kat daha hafif.
  • Attr ve Su da Artık Geri Alınabiliyor: Yıllardır Attr (blokaj) veya su boyarken hata yapınca geri alma şansımız yoktu, artık ikisi de tıkır tıkır geri alınıyor.
  • Objelerin Kafası Karışmıyor: Eskiden toplu ağaç taşıyıp geri aldığında objeler birbirinin yerine geçerdi. Artık her objenin kalıcı bir kimliği (ID) var; 50 tane ağacı aynı anda sağa çekip Ctrl+Z yapsan bile hepsi milimetrik olarak kendi eski yerine oturuyor.
 
Editorler cok guzel ve kullanisli duruyor elinize saglik. Aslinda terrain sistemini de sifirdan yazip ustune Unreal Unity'dekine benzer bi tooling insa edilebilir.

Daha da guzeli birbirinde ayri mapler uzerinden calisan bi oyun yerine tamamen open world bi dunya olurdu. Yani oyuncu hic bir loading screen gormeden mavi bayrak 1.koyden sari bayrak 1.koye yuruyebilse. Gerci gunumuz fileslari goz onune alindiginda hic bi oyuncunun umursamayacagi bir ozellik olurdu muhtemelen.

World Editor gibi item editor, mob editor toolari da aslinda yazilip hepsi tek bir program icerisine gomulse oyun icerigini olusturmak ve yonetmek cok kolay bi hale gelebilir. Ilerisi icin boyle bir dusunce var mi?
 
Editorler cok guzel ve kullanisli duruyor elinize saglik. Aslinda terrain sistemini de sifirdan yazip ustune Unreal Unity'dekine benzer bi tooling insa edilebilir.

Daha da guzeli birbirinde ayri mapler uzerinden calisan bi oyun yerine tamamen open world bi dunya olurdu. Yani oyuncu hic bir loading screen gormeden mavi bayrak 1.koyden sari bayrak 1.koye yuruyebilse. Gerci gunumuz fileslari goz onune alindiginda hic bi oyuncunun umursamayacagi bir ozellik olurdu muhtemelen.

World Editor gibi item editor, mob editor toolari da aslinda yazilip hepsi tek bir program icerisine gomulse oyun icerigini olusturmak ve yonetmek cok kolay bi hale gelebilir. Ilerisi icin boyle bir dusunce var mi?
Şu anda tam olarak terraine rework atıyordum bunu yazdınız. Bu yazdıklarınızı yapay zekayla taş çatlasın 5 dakika önce tartıştım. Sanki yorumunuzu tam şu an ekranıma bakıp yazmışsınız gibi. Şu anda ufuk çizgisine kadar aralıksız terrain bile çizsek, 1FPS bile zararı dokunmayacak gelişmiş bir terrain sistemi yazıyorum.

1791041659304.webp
 
"great minds think alike" diyelim o zaman hocam :D.

Buradaki temel problem client tarafinda cikmaz ama server tarafini boyle bir yapiya gecirmek epey zorlu olabilir. Client kendisi tum haritayi cell'lere ayirip oyuncunun pozisyonuna gore etrafindaki celleri yonetebilir. Statik objeler icin is kolay ama mevzu bahis server tarafindan yonetilen oyunculari yuklemeye geldiginde is biraz daha karisiyo. Server tarafinin da bu cell yapisina hakim olup, client'a "bu cell'in etrafinda bu objeler bulunuyor, bunlari yukle" demesi lazim.

Tabi client tarafinda da bazi zor kisimlar var. Mesela oyuncu bir dagin tepesine cikip ufka baktiginda cok uzaktaki cell'leri gorebilir ama oyuncunun gordugu her cell'in memory'e yuklenmesi performans acisindan inanilmaz sakincali bir durum. Orada da Unreal'in HLOD sistemine benzer sistemler lazim. Burada WoW gibi open world mmo'lar nasil cozmus bu problemleri onlari arastirmak lazim.
 
"great minds think alike" diyelim o zaman hocam :D.

Buradaki temel problem client tarafinda cikmaz ama server tarafini boyle bir yapiya gecirmek epey zorlu olabilir. Client kendisi tum haritayi cell'lere ayirip oyuncunun pozisyonuna gore etrafindaki celleri yonetebilir. Statik objeler icin is kolay ama mevzu bahis server tarafindan yonetilen oyunculari yuklemeye geldiginde is biraz daha karisiyo. Server tarafinin da bu cell yapisina hakim olup, client'a "bu cell'in etrafinda bu objeler bulunuyor, bunlari yukle" demesi lazim.

Tabi client tarafinda da bazi zor kisimlar var. Mesela oyuncu bir dagin tepesine cikip ufka baktiginda cok uzaktaki cell'leri gorebilir ama oyuncunun gordugu her cell'in memory'e yuklenmesi performans acisindan inanilmaz sakincali bir durum. Orada da Unreal'in HLOD sistemine benzer sistemler lazim. Burada WoW gibi open world mmo'lar nasil cozmus bu problemleri onlari arastirmak lazim.
open worldlerin bazılarında pasaj ya da ince geçt gibi yerden player yürürken geçtiği mapi yükleme mantığı koyan oyunlar var
 
fullterrain1.webp

fullterrain2.webp
[Devlog] GPU-Driven CDLOD Arazi Motoru: 20 Yıllık CPU Yama İskelesinin Tasfiyesi, İki Aşamalı Hi-Z Görünürlük ve Sıfır Draw Call Darboğazı
Ashenfall motorunun grafik hattında, Direct3D 8/9 döneminden miras kalan ve açık dünya arazisini CPU üzerinde 3x3'lük chunk pencerelerine hapsederek görünürlüğü 256 metrede bir sis perdesiyle kesen ilkel arazi işleme mimarisini tamamen tarihe gömdüm.
Bir önceki devlog'da temelini attığımız deterministik ikili harita formatının (.ashenfallmap) sunduğu saf contiguous bellek yapısını arkamıza alarak, arazinin seçimini, seviye geçişlerini (LOD) ve görünürlük testlerini doğrudan GPU'ya devrettiğimiz GPU-Driven CDLOD (Continuous Distance-Dependent Level of Detail) ve Two-Phase Hi-Z Occlusion Culling mimarisini tamamladım.
Artık CPU harita arazisi için tek bir vertex veya index kopyalamıyor, tek bir bounding box test etmiyor; tüm harita ufka kadar tekil bir indirect komutla GPU tarafından seçilip çiziliyor.

Eski Yöntem Neydi ve Neden Çöpe Atıldı?​

2004 yılından kalan klasik açık dünya arazi motoru, araziyi CPU üzerinde 16x16'lık yamalara (CTerrainPatch) bölüyor ve her karede şu ilkel döngüyü işletiyordu:
  1. CPU Frustum Culling Darboğazı: Kamera her döndüğünde ana thread, yüzlerce yamanın küresel sınırlarını tek tek dolaşıp frustum testi yapıyordu.
  2. 3x3 Chunk Sınırı ve Sis Maskesi: CPU ve D3D9 API çağrı maliyetleri yüzünden harita hiçbir zaman bütüncül olarak ekrana verilemiyor; 3x3'lük (veya biraz genişletilmiş) bir pencerenin dışı yapay bir FarPlane ve yoğun sis ile acımasızca kesiliyordu.
  3. Parçalanmış Draw Call Patlaması: Her yama ve her katman için CPU'dan GPU'ya sürekli vertex buffer / index buffer set ediliyor, yüzlerce gereksiz API çağrısı (draw call) ile sürücü kilitleniyordu.
  4. T-Junction ve Çatlaklar (Cracking): Farklı detay seviyelerine sahip komşu parçalar arasında yükseklik farkları oluştuğundan, kenarlarda siyah çatlaklar beliriyor; bunu gizlemek için CPU'da karmaşık ve pahalı dikiş (stitching) algoritmaları koşturuluyordu.

Yeni Mimari: Tam GPU-Driven CDLOD ve İki Aşamalı Hi-Z​

Yeni nesil arazi hattı (RC::Terrain), arazinin tüm geometrisini harita yüklendiği anda GPU belleğinde tekil dokular ve tamponlar halinde (Texture2DArray splat maskeleri, R16_UINT yükseklik ve normal rasters) yerleşik (resident) tutuyor. Çizim süreci ise tamamen compute shader'lar üzerinden yürüyor.
Kod:
Genişlet Daralt Kopyala
┌────────────────────────────────────────────────────────────────────────┐
│                   GPU-DRIVEN CDLOD & HI-Z PIPELINE                     │
├───────────────────┬────────────────────────────────────────────────────┤
│ 1. Frame Open     │ GPU'da tekil Indirect Args sıfırlama (Upload ring) │
│ 2. Compute Select │ Dörtlü ağaç seçimi & GPU tabanlı CDLOD             │
│ 3. Phase 1 Cull   │ Önceki karenin Hi-Z derinlik piramidine karşı test │
│ 4. Depth Prepass  │ Görünen parçaların derinliği tekil ExecuteIndirect │
│ 5. Hi-Z Pyramid   │ Prepass sonu derinlikten mip zinciri inşası (Hi-Z) │
│ 6. Phase 2 Cull   │ Elenen parçaları prepass sonu anında kurtarma      │
│ 7. Lit Pass       │ Shading: Tek geçiş splatting, DXR ışınları, 0 over │
└───────────────────┴────────────────────────────────────────────────────┘

1. GPU Compute Selection & Kesintisiz Morfoloji​

Her kare BeginRayScene aşamasında tek bir compute dispatch çalışır. Tüm haritanın quadtree hiyerarşisi GPU üzerinde taranır:
  • Piksel Başına Hata Hedefi (T): Ekrandaki hiçbir arazi dörtgeninin belirlenen piksel boyutunu (varsayılan T = 8 px) aşmaması garanti edilir.
  • Continuous Morphing: İki detay seviyesi (LOD) arasındaki geçiş, vertex shader içinde precise matematiksel morfolojiyle (k = 0.25) pürüzsüzce birbirine bağlanır. Geometride tek bir çatlak, dikiş izi ya da sıçrama (popping) oluşmaz.
  • Görüş Alanı (Reverse-Z & Sonsuz Projeksiyon): Matrisimiz _33 = 0 sonsuz ters-Z projeksiyonuna oturtulduğu için arazinin sonu diye bir sınır yoktur; harita en uç sınırına kadar eksiksiz çizilir.

2. Çift Aşamalı Hiyerarşik Derinlik Elemesi (Two-Phase Hi-Z Occlusion Culling)​

Tüm haritayı çizmek devasa bir piksel yükü getirebilirdi; ancak bunu Haar & Aaltonen modeli iki aşamalı donanımsal derinlik piramidiyle tamamen etkisiz hale getirdik:
  • Phase 1 (Geçmiş Kare Hi-Z): Seçilen arazi parçalarının eksen hizalı sınır kutuları (AABB), bir önceki karenin oluşturduğu ekran derinlik piramidine karşı test edilir. Önünde tepe veya dağ olan kilometrelerce arazi daha ilk aşamada GPU tarafından elenir.
  • Prepass Sonu Kancası & Phase 2: Depth Prepass tamamlandığı anda güncel ekran derinliğinden hiyerarşik mip piramidi çıkarılır. Kamera aniden döndüğünde (hızlı hareketlerde) Phase 1'in haksız yere elediği parçalar mikro saniyeler içinde kurtarılır (Phase 2 draw) ve sahneye eklenir. Ekranda sıfır yırtılma, sıfır gecikme!
  • Doğrulama (Culled Quarters View): Debug paneline eklediğimiz görünüm moduyla, elenen tüm parçalar magenta renkle test edildi; kameranın gördüğü alanda tek bir hatalı eleme olmadığı matematiksel olarak kanıtlandı.

3. Tek Geçişte Premultiplied Splatting & DXR Entegrasyonu​

Klasik çoklu geçişli (multi-pass) zemin boyama çöpe atıldı. Piksel altındaki katmanlar bindless doku dizisinden okunarak tek geçişte harmanlanır (8 katmana kadar sıfır ek maliyet).
Ayrıca her chunk için compute kuyruğunda oluşturulan tam çözünürlüklü statik BLAS (Bottom-Level Acceleration Structure) sayesinde, DXR 1.1 ışın izleme hattımız arazinin en ince kıvrımlarından ve kayalarından kusursuz donanımsal gölgeler üretir.

Ekran Görüntüleri Ne Anlatıyor?​

Paylaştığım iki ekran görüntüsü, mimarinin sahadaki somut gücünü sergiliyor:

  1. []Tam Harita Görünürlüğü (Görsel 1): Eski motorun 256 metre sonra kestiği tüm vadi ve dağ silsilesi, kameranın açısından haritanın en uç sınırına kadar eksiksiz işleniyor. Ufuk çizgisine kadar hiçbir sis perdesi veya yapay kesilme yok; arazinin tamamı sahnede.
    []CDLOD Seviye Dağılımı - Görselleştirme (Görsel 2):
    Geliştirici arayüzündeki seviye görünümünde arazinin detay ayrımı net bir şekilde görülüyor:
    • Gri (Level 0): Kameraya en yakın, 512 üçgenlik en yüksek detay seviyesi.
    • Yeşil (Level 1): Orta mesafede morfolojik olarak bağlanan ara detay.
    • Kırmızı (Level 2): Ufka doğru uzanan, piksel hatasını T <= 8 px sınırında tutan en optimize seviye.
      Geçişler compute shader tarafından tam dinamik olarak hesaplanıyor, poligon israfı sıfıra indiriliyor.

Performans ve Mühendislik Kazanımları​

  • Sıfır CPU Arazi Maliyeti: Harita ne kadar büyük olursa olsun, CPU arazi çizimi için 0.00 ms harcar. CTerrainPatch, yama listeleri ve CPU frustum kontrolleri tamamen silindi.
  • Tekil Indirect Çizim (ExecuteIndirect): Bütün dünya tek bir komutla GPU'dan çizdirilir; CPU-GPU komut kuyruğu darboğazı sıfırlanmıştır.
  • Yüzde 60'a Varan Geometri Tasarrufu: Engebeli ve kapalı haritalarda (örneğin Devils Catacomb / Zindan haritaları) 42 arazinin 26'sı Hi-Z culling ile daha piksel aşamasına gelmeden doğrudan GPU'da elenir.
  • Modern Entegrasyon: Anlık hedef piksel boyutu (T), seviye zorlamaları ve GPU bellek istatistikleri çalışma anında dinamik olarak izlenip ayarlanabilir.
Arazinin GPU üzerindeki bu kusursuz ve sınırsız işleyişi tamamlandı. Sırada, arazinin üzerinde duran statik nesneleri, binaları ve ağaçları da aynı GPU Instance & Hi-Z mimarisine bağlayacağımız Unified GPU Scene aşaması var!
 
Son düzenleme:
1791461223126.webp
[Devlog] MapGround Mimarisi: 20 Yıllık 3x3 Chunk Streaming İllüzyonunun Sonu, O(1) Contiguous Zemin Belleği ve CTerrain Tasfiyesi

Önceki devlog'da arazinin ekran kartındaki çizimini GPU-Driven CDLOD ve Hi-Z mimarisine emanet ederek GPU render darboğazını sıfırlamıştık. Ancak motorun derinliklerinde çözülmesi gereken devasa bir "mimari borç" daha vardı:

GPU haritanın tamamını ufka kadar tek seferde çizerken; oyunun CPU fiziği, ayak basma yüksekliği (GetHeight), duvara çarpma engelleri (isAttrOn), tıkla-git ışınları (Pick), balıkçılık sorguları ve hatta mini harita hâlâ 2004 yılından miras kalan ilkel bir 3x3'lük chunk streaming penceresine mahkumdu. Karakter her 128 metre yürüdüğünde ana iş parçacığında (main thread) diskten senkronize arazi yükleniyor, kare hızında mikro donmalar (hitch/stutter) yaşanıyor ve 3x3 pencerenin dışına yapılan tüm sorgular zemini "yok (0)" sanıyordu.

Bu devlog ile birlikte; zemin ve su hattını birbirinden tamamen kopararak tüm harita arazisini tek parça, biti bitine doğrulanmış bir bellek deposuna (GameLib/MapGround) taşıdık. CTerrain sınıf ailesini, chunk yükleme kuyruklarını ve 3x3 streaming illüzyonunu motor kodundan ebediyen sildik.



Eski Sistemin Mimari Çıkmazları ve Kök Hatalar

Arazi kodunun derinliklerine indiğimizde karşılaştığımız tablo tam bir teknik borç yumağıydı:

  • Chunk Sınırlarında Main-Thread Takılmaları: Oyuncu haritada gezerken her chunk sınırında CPU senkron olarak arazi verisini diskten çözüyor, normal hesaplıyor ve belleğe basıyordu. Bu durum kare zamanında (frame interval) belirgin sıçramalara (spike) neden oluyordu.
  • Çift Köşegen Uyuşmazlığı (Görsel ve Fizik Ayrımı): 20 yıldır motorda var olan trajikomik bir geometri hatası: Ekran kartı ve DXR BLAS arazi hücrelerini bir köşegenden ikiye bölerken, CPU tarafındaki GetHeight tam ters köşegenden interpolasyon yapıyordu. Sonuç: Dik yokuşlarda karakterlerin ve yere düşen eşyaların bir ayağının toprağa gömülmesi veya havada asılı kalması!
  • Kaba ve Sınırlı Işın Testi (Picking): Fareyle tıkladığınızda arazi 5, 10 ve 100 birimlik kör adımlarla en fazla 1.000 metre taranabiliyordu. Ufuktaki bir dağa tıkladığınızda ışın ya boşluğa düşüyor ya da yanlış noktaya sapıyordu.
  • Suni Okyanus Hataları (Water Coupling): Haritadaki su yüzeyleri bağımsız değildi; arazinin ömrüne bağlıydı. İçinde küçük bir gölet veya kuyu olan 20 farklı kapalı harita (Catacomb, Arena, Battle Royale vb.), harita kenarında deniz olmamasına rağmen "en yaygın su seviyesini" okyanus kabul edip haritanın etrafına devasa bir su eteği (skirt) çiziyordu.
  • Çift/Üç Kat Dosya Çözme İsrafı: Harita yüklenirken GPU için zemin, CPU için fizik ve su motoru aynı verileri dosyadan tekrar tekrar decompress edip belleği şişiriyordu.



Yeni Mimari: MapGround ve Tam Tasfiye

Kod:
Genişlet Daralt Kopyala
┌────────────────────────────────────────────────────────────────────────┐
│                   MAPGROUND CPU UNIFIED ARCHITECTURE                   │
├───────────────────┬────────────────────────────────────────────────────┤
│ 1. Map Load       │ .ashenfallmap tek seferde MapGround belleğine açılır│
│ 2. Single Decode  │ GPU (TerrainMap) & Su (MapWater) direkt buradan alır│
│ 3. O(1) Queries   │ Yükseklik, Attr, Normal, Su: Slot araması YOK     │
│ 4. Bounds Raycast │ Quadtree hızlandırmalı sınırsız Pick (Amanatides)  │
│ 5. Object Sync    │ Statik objeler & ağaçlar 3x3 pencereden bağımsız    │
└───────────────────┴────────────────────────────────────────────────────┘

1. Tekil, Harita Çapında Depo (GameLib/MapGround):
Harita açıldığı an tüm haritanın yükseklikleri (HGHT), öznitelikleri/yürüme engelleri (ATTR) ve su katmanları (WATR) harita boyutunda tek parça, contiguous bir belleğe alınır.
Artık GetHeight, isAttrOn ve WaterLevel sorguları 9 slotluk matrislerde chunk aramaz; $O(1)$ matris indeksiyle anında yanıt verir.

2. Çizilen Geometri ile Birebir Köşegen Eşitliği (Rw3):
CPU zemin yüksekliği algoritması, GPU CDLOD ve DXR Ray Tracing BLAS'ın çizdiği üçgen köşegeniyle birebir eşitlendi. Karakterin bastığı nokta ile poligonun eğimi artık pikseli pikseline aynı yüzeydedir.

3. Quadtree Hızlandırmalı Analitik Picking (D3):
Eski kör adımlı ışın döngüsü silindi. Haritanın yükseklik sınırları (Quadtree Bounds) üzerinden çalışan ve Amanatides & Woo grid yürüyüşünü kullanan analitik bir ray-picker yazıldı. Artık haritanın öbür ucundaki 20 kilometrelik bir dağ zirvesine tıkladığınızda bile ışın anında doğru üçgene oturur; mesafe sınırı yoktur.

4. Su Ayrımı ve Perimeter 1/8 Deniz Kuralı (Q1 & D7):
Su yaşam döngüsü araziden tamamen koparıldı (MapWater). Haritanın dışına deniz eteği çizilme şartı, haritanın dış çerçeve hücrelerinin (perimeter) en az 1/8'inin açık deniz seviyesinde olması kuralına bağlandı. Kapalı zindanlar ve iç gölleri olan haritalar sahte okyanuslardan kurtuldu.

5. WorldEditor Entegrasyonu ve Dikiş İzsiz Fırçalar (D5):
Editördeki 20 yıllık CTerrainAccessor silindi. Yeni MapBrushes mimarisi doğrudan MapGround tamponlarına yazar. İki chunk'ın sınırına fırça vurulduğunda artık dikiş hesapları veya kenar eşitleme (ShareEdges) algoritmaları koşturulmaz; zemin tek parça olduğu için doğal olarak kusursuz boyanır.

6. Statik Objelerin (CArea) Tasfiyesi ve glTF Entegrasyonu (W7):
Arazinin özgürleşmesiyle birlikte, araziye bağlı duran binalar, zindan blokları ve SpeedTree ağaçları da eski 3x3 penceresinden ve yapay 768 metre gölge/ışın kesme kısıtlamalarından kurtarılarak glTF / ModelLib sahnesine doğrudan bağlandı. Zemin snapping sorguları doğrudan MapGround'a kenetlendi.



Doğrulama ve Test Süreci

Bu kadar köklü bir omurga ameliyatını güvenle tamamlamak için geçici bir test harness'ı (MapGroundCheck) kurduk:

Test Edilen HaritaTest Noktası SayısıBit-Level UyuşmazlıkRaycast (Pick) Testi
metin2_map_a1~2.6 Milyon021.000 Işın (Kusursuz)
devilscatacomb~2.7 Milyon015.000 Işın (Kusursuz)
battleroyale~5.0 Milyon016.000 Işın (Kusursuz)

Toplamda 10.3 milyondan fazla rastgele koordinatta eski sistem ile yeni MapGround bit-seviyesinde kıyaslandı ve sıfır hata ile doğrulandı.



Büyük Tasfiye (The Purge) ve Sonuç

Doğrulama bittikten sonra temizlik tamamlandı:
  • CTerrain, CTerrainImpl, CTerrainAccessor ve TerrainType.h dosyaları tamamen silindi.
  • CMapOutdoor içindeki arazi vektörleri, 60 saniyelik silme havuzları (TerrainDeleteVector) söküldü.

Sonuç olarak: Chunk sınırlarında yaşanan takılmalar bitti, fizik/yükseklik hesapları modern D3D12 BLAS geometrisiyle eşitlendi, bellek kullanımı hafifledi ve zemin mimarimiz modern bir oyun motoruna yakışır sadeliğe kavuştu!
 
Son düzenleme:
1791512303453.webp

1791512877851.webp

1791513135180.webp

Dev-Log: Bir Devrin Sonu: Granny ve SpeedTree'ye Güle Güle — glTF 2.0 Geçişi & Server Single Source of Truth​

Projenin başından beri sırtımızdaki en büyük teknik kambur olan **Granny** ve **SpeedTreeRT** kütüphanelerini motordan tamamen söktüm. Bu kütüphanelerin getirdiği tescilli dosya formatları, D3D9 uyumluluk kalıntıları ve binlerce parçalı metin script'i artık projede yer almıyor.

Bütün varlık (asset) ekosistemini doğrudan modern GPU pipeline'larına uygun **glTF 2.0 Binary (.glb)** standardına geçirdim. Üstelik bu dönüşümü sadece istemciyle sınırlı tutmayıp, sunucuyu da (game core) doğrudan aynı ikili paketlere bağlayarak tam anlamıyla **"Single Source of Truth"** mimarisini kurdum.



1. Granny 3D (.gr2 / .msa) Yerine Tekil glTF 2.0 Binary (.glb)

Granny tabanlı eski mimaride tek bir canavarı veya karakteri motora yüklemek için disk üzerinde 5-6 farklı dosya türünü tek tek aramak, metin (ASCII) parse etmek ve bunları kod tarafında birleştirmek gerekiyordu:
  • Geometri ve iskelet için .gr2
  • Parça/iskelet montajı için .msm
  • Her bir animasyon döngüsü ve skill için ayrı ayrı onlarca .msa
  • Animasyon ses tetikleyicileri için .mss
  • Klasör indeksleri için motlist.txt
  • Yüzlerce bağımsız, dağınık doku dosyası

Bu dosya karmaşasının tamamına son verdim. Artık tüm modelleri tek bir .glb konteyneri içinde topladım:
  • Mesh, İskelet ve Animasyonlar: Modelin tüm kemik hiyerarşisi, LOD seviyeleri ve animasyon kanalları standart glTF yapısında doğrudan ikili (binary) olarak dosyaya gömülü.
  • Materyal ve Dokular: Modern PBR doku standartları doğrudan buffer seviyesinde yapıya dahil.
  • Metadata ve Eventler: Animasyonların hangi karesinde hangi sesin çalacağı veya hangi efektin tetikleneceği bilgisini extras JSON düğümleri altında tuttum. Oyun açılırken binlerce minik metin dosyasını diskten okuma ve string parse etme maliyetini sıfırladım.



2. Sunucuda "Single Source of Truth": Tüm Server Script'leri Çöpe

Ymir'in eski mimarisinde sunucu; canavarların hareket hızlarını, saldırı sürelerini, vuruş anlarını (hit data), combo pencerelerini ve çarpışma yarıçaplarını share/data/monster altındaki metin tabanlı .msa, .msm ve motlist.txt kopyalarından okuyordu. Bu durum istemci ile sunucu arasında sürekli bir sayısal desync (senkronizasyon kaybı) ve korkunç bir veri tekrarı yaratıyordu.

  • Ortak Model Okuyucusu (Shared/Model/ModelGameData): game çekirdeğine, doğrudan fastgltf / simdjson üzerinden çalışan hafif bir model metaveri okuyucusu ekledim.
  • Doğrudan Pack'ten Okuma: Tıpkı harita sisteminde (ashenfallmap) yaptığım gibi sunucuya PACK_FOLDER yapılandırmasını getirdim. Sunucu artık canavarların ve karakterlerin tüm verilerini (hareket mesafesi, saldırı süreleri, combo pencereleri, splash etki alanları) diske saçılmış metin dosyalarından değil, istemcinin render ettiği pack/Models ve pack/Actors altındaki .glb modellerinin kendi içinden okuyor.
  • Gerçek Root Motion: Sunucunun yürüme/koşma hızlarını eski .msa dosyalarındaki bayat ve elle girilmiş accumulation değerlerinden kurtardım. Sunucu artık istemcinin karaktere gerçekten uyguladığı ölçülmüş rootMotion vektörünü baz alıyor; böylece istemci ile sunucu konumu arasında en ufak bir sapma ihtimali kalmadı.
  • Doğrulama (Zero-Tolerance): Yazdığım test harness'ı ile 4.211 farklı animasyon/hareket değerini (64 ırk hareketi, 768 kombo penceresi, 2.098 canavar hareketi, 1.176 normal saldırı ve 105 skill) tek tek doğruladım; tek bir uyumsuzluk kalmadı.



3. SpeedTreeRT Kaldırıldı: Tiago Sousa (GPU Gems 3) Rüzgar Fiziği

20 yıllık SpeedTree 1.6 kütüphanesini (SpeedTreeRT.dll ve SpeedTreeLib) tamamen sildim. Ağaçları da artık standart birer ModelLib instance'ı olarak yüklüyorum.

  • GPU Bükülme Fiziği (Tiago Sousa / GPU Gems 3): SpeedTree'nin CPU tarafındaki eski dallanma mantığı yerine, Crytek'in Tiago Sousa formülünü doğrudan GPU Compute ve Vertex Shader katmanına bağladım.
  • Doğal Ağaç Salınımı: Ağaç gövdeleri ve dallar, global rüzgar yönü ve hızına göre hiyerarşik bir parabolik eğriyle (x²) esniyor. Her ağacın dünya koordinatına bağlı bir faz farkı (dot(worldPos, 1)) bulunduğu için tüm orman senkronize bir robot gibi değil, dalga dalga ve doğal bir salınımla hareket ediyor.
  • Canlı Ayar Desteği: Rüzgar kuvveti, dal salınım genliği ve frekans katsayılarını shader koduna çakmadım; RenderDefaults::Wind üzerinden canlı olarak düzenlenebiliyor.



4. Gömülü BVH ile 0-Bit Kayıplı Çarpışma (Collision & Height)

Ağaçların, binaların ve dungeon bloklarının zemin yükseklik ve tıklama (picking) verileri eskiden harici .mdatr dosyalarında ham üçgenler halinde tutuluyordu.

  • Yükseklik taşıyan 362 modelin tamamını, bake aşamasında kendi BVH (Bounding Volume Hierarchy) hızlandırma ağacıyla derledim ve doğrudan model dosyasına gömdüm.
  • Yaptığım doğrulama testlerinde 16.08 milyon yükseklik sorgusu ve 5.48 milyon ray-pick işlemi koştum; eski sistem ile yeni BVH arasında 0 bit fark ile birebir matematiksel eşleşme sağladım.
  • Artık CPU sahnede yükseklik ararken binlerce üçgeni tek tek lineer taramak yerine mikro-saniyeler içinde doğrudan BVH kutularına dallanıyor.



5. Silinen Dosyalar ve Büyük Temizlik

Granny ve SpeedTree'yi projeden tamamen söküp hem istemciyi hem de sunucuyu tekil ikili (.glb) yapıya geçirmemle birlikte repoda devasa bir temizlik yaptım:

  • 43.048 Dosya Silindi: İstemci paketlerindeki 37.453 dosyanın üzerine, sunucu tarafındaki data/monster, data/pc ve data/pc2 dizinlerinde çürüyen 5.595 dosyayı daha kökten temizledim.
  • Sunucu Tamamen Arındı: Sunucudaki 5.285 adet .msa, 292 adet motlist.txt, 288 adet .msm ve sunucuya kazara atılmış tüm yetim istemci dosyaları (.mse, dokular vb.) tarihe karıştı.
  • ~1.5 GB Veri Tasarrufu: Ölü modelleri, artık kullanılmayan dokuları ve on binlerce metin script'ini silerek toplam proje boyutunu ciddi oranda küçülttüm.
  • ~430.000 Satırdan fazla text silindi EterGrnLib, SpeedTreeLib, SpeedTreeRT, eski metin parser'ları, D3D9 arayüzleri ve sunucudaki eski motion text okuyucuları tamamen kalktı.

Sildiğim Dosyaların Dökümü (Client + Server):
Kod:
Genişlet Daralt Kopyala
.msa (Motion Script - Client + Server) : 14,033 adet
.gr2 (Granny Mesh/Skeleton)            : 11,801 adet
Model Dokuları                         :  6,000+ adet
.mss (Motion Sound Script)             :  5,011 adet
.msm (Model/Armor Script)              :  3,805 adet
.mdatr (Collision Data)                :  1,560 adet
motlist.txt (Client + Server)          :  1,009 adet
.spt (SpeedTree Modelleri)             :    118 adet

Motor genelinde hem Client hem de WorldEditor ve Server (game) tarafında tüm konfigürasyonları (Debug/Release) sıfır uyarı ve sıfır hata ile derledim. Eski kod tabanının kazara geri girmesini engellemek için CI gate kurallarını kilitledim. Varlık hattı artık baştan aşağı modern, hızlı ve tek bir kaynaktan besleniyor.
 
Son düzenleme:
1791863487923.webp


1791863531080.webp

​

[Devlog] WorldEditor 2.0: Dahili Asset Health Paneli, Otomatik LOD Üretimi ve Karakter Referanslı Metrik Ölçek​

Birtakım büyük mimari geçişlerin planlamasını yaparken arada boş durmadım; WorldEditor üzerinde içerik üretirken elimizi ayağımızı rahatlatacak, bizi harici script'lerden kurtaracak birkaç kritik aracı aradan çıkardım.

Bu sefer teknik ayrıntılara boğulmadan; model kontrolünü, ölçek tutarlılığını ve LOD üretimini doğrudan editörün içine taşıyan bu yenilikleri ekran görüntüleriyle özet geçiyorum.

1. Asset Health: 3.600+ Modelin Saniyeler İçinde Denetimi ve Dahili LOD Üretimi​

Eskiden bir modelde kural ihlali var mı, poligon bütçesini aşıyor mu ya da dokusu doğru formatta mı anlamak için terminalden python script'leri koşturmak gerekiyordu.

Model Inspector'a eklediğim Asset Health paneliyle bu işi tamamen editörün içine aldım:
  • Tek Ekranda Tüm Depo Taraması: Projedeki 3.603 modelin tamamını yalnızca 7.1 saniyede tarayarak; hata, uyarı, doku sıkıştırmaları (BC1/DXT1), en büyük primitive boyutları ve poligon sayılarını filtreli bir liste halinde önümüze döküyor.
  • Otomatik LOD Üretimi (Make Levels): Artık eksik LOD'ları üretmek için Blender'a dönüp poligon indirip tekrar export etmeye gerek yok. Eksik basamağı olan modelleri listeden seçip "Make levels" butonuna bastığınız anda WorldEditor arka planda `meshoptimizer` ile LOD1, LOD2 ve LOD3 kademelerini kendi içinde otomatik olarak türetiyor ve dosyaya işliyor.

2. Viewport 3D XYZ Yönelim Gizmosu​

Modeli incelerken kameranın dünya eksenlerine göre nerede durduğunu, orijin noktasını ve bakış açısını anlık olarak takip edebilmek için viewport'un sağ üst köşesine interaktif bir 3D XYZ yönelim gizmosu ekledim. Kamerayı çevirdikçe eksenlerin nereye baktığı net bir şekilde görülüyor, serbest açıda çalışırken yön duygusunu kaybetmenin önüne geçiyor.

3. Scale Reference: Savaşçı Modeli ve Dinamik Metre Cetveli​

Eski varlıklarda en büyük dertlerden biri ölçek tutarsızlığıydı. Editörde normal görünen bir kapı veya bina, oyuna yerleştirildiğinde ya cüce gibi kalıyor ya da devasa bir kuleye dönüşüyordu.

Üst menüye eklediğim Scale Reference butonu tek tıkla devreye giriyor:
  • 1:1 Savaşçı Karakter Referansı: Sahneye hemen modelin zemin hizasına gerçek boyutlu savaşçı karakterini yerleştiriyor. Örneğin fotoğraftaki taş kapıyı (`stone_door.glb`) tek başına incelerken boyutunu kestirmek zorken, yanına savaşçıyı koyduğunuz anda kapının heybeti kendini gösteriyor.
  • Metrik Boyut Cetveli: Karakterin hemen arkasında modelin net yüksekliğini gösteren bir metre cetveli yükseliyor. Göz kararı tahmin yürütmek yerine kapının tam olarak 8.09 metre olduğunu doğrudan ekranda görüyoruz.

Editörün bu tarz pratik özelliklerle kendi kendine yeten modern bir araca dönüşmesi, arkada yapacağımız asıl büyük rendering revizyonlarının zeminini çok daha sağlam kılıyor. Büyük işlere devam.
 

​


[Devlog] Ashenfall: Dinamik Atlas Motoru, Zindan Haritaları, Zoom/Takip ve Legacy Tasfiyesi​

Metin2'nin 20 yıllık statik atlas altyapısını tamamen kaldırdım. WorldEditor Globe sekmesinde kurduğumuz dinamik doku mimarisini doğrudan oyun istemcisine (runtime) taşıyarak harita sistemini baştan aşağı modernize ettim. Geliştirmenin oynanış videosunu yukarı bırakıyorum; lafı uzatmadan neydi, ne gitti, ne oldu ve ne eklendi şeklinde özetliyorum.(Videoyu buraya ekleyebilirsiniz)

1. Neydi & Ne Gitti? (Legacy DDS Çöplüğü ve 12.3 MB Tasfiye)​

  • Neydi: Haritalar elle çizilmiş/aktarılmış, düşük çözünürlüklü statik _atlas.dds ve .sub dosyalarından okunuyordu. 64 haritanın yalnızca 26'sında atlas vardı; zindanlar ve kat haritaları "Büyük harita gösterilemiyor" uyarısı veriyordu. Üstelik resim boyutları harita ölçeğiyle birebir uyuşmadığı için (örn. 4x5 chunk'lık A1'in 256x256 resme gerilmesi) tıklanan piksel ile dünya koordinatı arasında metrelerce kayma yaşanıyordu.
  • Ne Gitti: Pack içerisindeki kullanılmayan ve kopyalanmış tüm varyantlar dahil 304 adet legacy atlas dosyası (12.3 MB) tamamen silindi. WorldEditor'deki eski BMP atlas dışa aktarma (SaveAtlas) kodları çöpe atıldı ve harita kontrol kapısına (check_maps.py) statik atlas girişini yasaklayan kural eklendi.

2. Ne Oldu? (Dinamik GPU MINI Verisi ve 4 Kat Netlik)​

  • Canlı Birleştirme: Artık harici hiçbir görsel okunmuyor. Harita açılırken .ashenfallmap dosyalarının içinde gömülü olan chunk MINI (BC1) blokları doğrudan GPU yüzeyinde anlık olarak birleştirilip atlas dokusuna dönüştürülüyor (~30-60 ms).
  • 4 Kat Çözünürlük: Eski atlaslara kıyasla 4 kat daha yüksek çözünürlük elde edildi. Harita üzerinde yapılan her zemin/nesne değişikliği atlasta anında güncel kalıyor.
  • 64/64 Tam Kapsama (Otomatik Zindan Atlasları): Şeytan Kulesi, Örümcek Zindanı, Katakomb ve arena haritaları dahil oyundaki 64 bölgenin tamamı artık otomatik olarak kristal netliğinde tam ekran haritaya sahip.

3. Ne Eklendi? (Zoom, Pan, Takip Modu ve 1:1 Hassasiyet)​

  • Kademeli Zoom (Powers of Two): Fare tekerleğiyle haritayı bulanıklaşma olmadan (25 ile 400 unit/piksel adımlarında) yakınlaştırıp uzaklaştırabiliyoruz.
  • Dinamik Çerçeve & Pan Clamping: Atlas penceresi her haritanın en-boy oranına göre açılıyor; etraftaki siyah boşluklar tamamen sıfırlandı. Harita dışına, siyah boşluğa doğru sürükleme (pan) yapılması engellendi.
  • "Beni Takip Et" Modu: Alt bara eklenen takip kutusu aktif edildiğinde harita karakteri merkezine alıp koşarken canlı olarak takip ediyor. Haritayı fareyle elle kaydırdığınız anda takip modu kullanıcı girdisine direnmeden kendini kapatıyor.
  • 1:1 Matematiksel Doğruluk: 1 texel tam olarak 1 metreye eşitlendi. Atlasta tıklanan bir pikselin dünya koordinat karşılığı ($X, Y$) sıfır sapmayla alınıyor.
Bu geçiş yalnızca görsel bir iyileştirme değil; atlasta tıklanan herhangi bir noktaya karakterin NavMesh üzerinden engellere takılmadan otomatik koşmasını sağlayacak olan Full-Map Auto-Pathfinding sisteminin kusursuz çalışması için temel yapı taşıydı. Bir sonraki adımda görüşmek üzere.
 


1791959821145.webp

​


[Devlog] 3D Tiled NavMesh ve Atlas Otopilotu: 2D Grid Prangasından Kurtuluş, Dilimlenmiş (Sliced) Detour A* ve Sıfır Takılmalı Çapraz Harita Navigasyonu

Ashenfall motorunda grafik hattından sonra oyunun can damarı olan hareket ve navigasyon mimarisinde de çağ atladık.

2004 yılından miras kalan, dünyayı yükseklikten habersiz 2 boyutlu kaba bir ızgaraya (attr.atr / server_attr) hapseden ilkel sistemi tamamen çöpe attım. Yerine hem WorldEditor'e hem de oyun istemcisine entegre, modern oyun motoru standartlarında çalışan deterministik bir 3D Tiled NavMesh (Recast/Detour) altyapısı kurdum.

Geçtiğimiz günlerde tamamladığımız dinamik GPU atlas mimarisinin 1:1 matematiksel koordinat hassasiyetini arkamıza alarak, haritanın bir ucundan diğer ucuna engellerden süzülerek otomatik koşan Atlas Click-to-Move (Otopilot) sistemini hayata geçirdim.

Geliştirmenin oynanış videosunu ve WorldEditor NavMesh panelinin ekran görüntüsünü aşağıya bırakıyorum; lafı uzatmadan mimarinin detaylarına geçelim.

Eski Yöntem Neydi ve Neden Çöpe Atıldı?​


Klasik Metin2'nin hareket ve engel kontrolü 20 yıllık köhne bir yaklaşıma dayanıyordu:
  1. 2D Izgara ve Yükseklik (Z Ekseni) Körlüğü: Harita sadece 100x100 cm'lik düz hücrelerden oluşan 2 boyutlu bir matristi. Motor 3. boyuttan (yükseklik) habersiz olduğu için bir köprünün altından mı geçiyorsunuz yoksa üstünde mi yürüyorsunuz ayırt edemiyordu.
  2. Kaba ve Titrek Çarpışmalar: Karakterler organik çevre geometrisi yerine yapay kare kutulara çarpıyor; köşelere sürtünürken takılıyor, titriyor veya iç içe geçiyordu.
  3. Harita Boyu Tıkla-Yürü İmkansızlığı: Eski NEW_MoveAlongPath sistemi maksimum 256 poligonluk sığ bir koridora ve 4.096 düğümlük tekil bir A aramasına kilitliydi. Ekranda görünmeyen uzak bir noktaya rota çizmek imkansızdı; zorlandığında ise ana thread'i kitleyip oyunu donduruyordu (frame hitch).

Yeni Mimari: 3D Tiled NavMesh & WorldEditor Entegrasyonu​


Artık sunucu ve istemci dünyayı aynı 3 boyutlu yürünebilir poligon ağları (walkable mesh) üzerinden okuyor. Tüm zemin eğimleri, kayalar, rampalar ve binalar milimetrik poligon sınırlarıyla taranıyor.

Kod:
Genişlet Daralt Kopyala
┌────────────────────────────────────────────────────────────────────────┐
│                   ASHENFALL 3D NAVIGATION PIPELINE                     │
├───────────────────┬────────────────────────────────────────────────────┤
│ 1. NavBake (WE)   │ Worker thread'lerde arka plan karo (tile) inşası   │
│ 2. .ashenfallmap  │ Deterministic NAVP & NAVT ikili katmanları         │
│ 3. TileCache      │ Dinamik engeller (lonca binaları) için canlı mesh  │
│ 4. Sliced Query   │ Bütçeli (2.048 iterasyon/kare) asenkron A* araması │
│ 5. Funnel Pass    │ Koridordan düzleştirilmiş en kısa köşe noktaları   │
│ 6. Liang-Barsky   │ Atlas vizörüne donanımsal segment kırpma ve çizim  │
│ 7. Path Runner    │ Seri numaralı (m_uPathSerial) güvenli hareket      │
└───────────────────┴────────────────────────────────────────────────────┘

1. WorldEditor Navigation Sekmesi ve Arka Plan Bake​

Harita tasarımcısının hayatını kolaylaştırmak için NavMesh üretimini harici araçlardan kurtarıp doğrudan WorldEditor'ün içine aldım:
  • Asenkron Karo Üretimi (Tiled Bake): Haritadaki araziler 25.6 metrelik bağımsız karolara (tiles) ayrıldı. WorldEditor, sahneyi dondurmadan arka plandaki worker thread'lerde NavMesh'i otomatik olarak örüyor.
  • Deterministik İkili Katman (NAVP & NAVT): Üretilen NavMesh verisi .ashenfallmap ikili dosyasının içine doğrudan gömülüyor. Hash doğrulaması (digest) sayesinde haritada bir taşın yeri değiştiğinde yalnızca o karonun NavMesh'i güncelleniyor.
  • TileCache ile Canlı Engeller: Lonca arazilerine sonradan dikilen binalar veya ağaçlar için tüm haritayı yeniden örmüyoruz; Detour TileCache katmanı sayesinde haritanın o karosu oyun anında dinamik olarak kesilip yeniden deliniyor.

2. Atlas Otopilotu: Dilimlenmiş (Sliced) Detour A* Algoritması​

Atlas üzerinde bir noktaya tıkladığınızda karakterin haritanın diğer ucuna donma yaşamadan koşabilmesi için arama motorunu akıllı bir bütçe sistemine bağladık:
  • Sıfır Frame Drop (Sliced Pathfinding): Sohan Dağı gibi devasa bir haritada 2.8 kilometrelik çapraz bir rota aramak 22.000'den fazla poligonun taranmasını gerektirebiliyor. Bunu tek karede hesaplayıp 5-6 ms'lik takılma (stutter) yaratmak yerine, Detour'un dilimlenmiş arama API'sini (initSlicedFindPath, updateSlicedFindPath) kullandık. Arama kare başına 2.048 iterasyon (~0.5 ms) bütçeyle çalışıyor; en uzak rotalar bile 7-8 kare içinde oyuncu fark etmeden pürüzsüzce çözülüyor.
  • İzole Sorgu Nesnesi (Isolated Node Pool): Uzun mesafe araması için 65.535 düğümlük bağımsız ikinci bir sorgu nesnesi tahsis ettik. Böylece harita otopilotu hesaplanırken sahnedeki diğer slotların veya karakterlerin anlık hareket sorguları devam eden aramayı bozamıyor.
  • Funnel Algoritması & Liang-Barsky Kırpma: Çözülen poligon koridoru düzleştirilerek en kestirme köşe noktaları çıkarılıyor (findStraightPath). Kalan rota atlasta şık bir çizgiyle çizilirken, pencere dışına taşan kısımlar Liang-Barsky algoritmasıyla vizör sınırlarında donanımsal olarak kırpılıyor.

3. Kusursuz Kontrol ve İptal Mekanizmaları​

Karakterin otopilotta koşarken kontrolden çıkmasını engellemek için tüm hareket hattını seri numaralı (m_uPathSerial) bir durum makinesine bağladık:
  • Anında Tepki: Karakter koşarken WASD/yön tuşlarına basıldığı, 3D dünyada zemine tıklandığı, bir canavara saldırıldığı veya bir skill basıldığı anda rota anında iptal edilir ve kontrol oyuncuya geçer.
  • Darbe ve İtilme Koruması: Slotların karakteri geriye itmesi (pushback), devirmesi (knockdown) veya sersemletmesi (stun) durumunda eski rotanın saçmalaması engellendi; rota anında kendini temizler.
  • Akıllı Takılma Koruması (Stall & Re-plan): Önüne aniden bir engel çıkan karakter 1 saniye boyunca 50 cm'den az ilerleme kaydederse bulunduğu noktadan otomatik olarak tek seferlik yeni bir rota dener; yine tıkalıysa oyuncuyu yormadan durur.

Performans ve Mühendislik Kazanımları​

  • Takılmasız Asenkron Navigasyon: 2.8 km'lik en ağır rotada bile kare başına harcanan CPU süresi yalnızca 0.74 ms; harita ne kadar büyük olursa olsun tek bir kare kaybı yaşanmıyor.
  • Hafıza Tasarrufu & Determinizm: 2D statik sunucu ızgaraları (server_attr) tamamen kaldırıldı; hem sunucu hem istemci tekil ve sıkıştırılmış .ashenfallmap ikili NavMesh katmanından besleniyor.
  • Tam 3D Hareket Özgürlüğü: Köprüler, tüneller, dikey zindan katları ve çok katmanlı yapılar artık oyun motoru için doğal birer yürüyüş alanına dönüştü.
 
Son düzenleme:
Geri
Üst