Asset pipeline · Üretim → runtime köprüsü
glTF Yükleme Yüzeyi: Loader, Decoder ve Sahne Kökü
Runtime'da glTF «şema» değil yükleme yüzeyi olarak yaşar: hangi loader, hangi decoder eklentisi, progress ve hata sözleşmesi. Bu sayfa accessor veya bufferView matematiğini tekrar etmez; loader'ın etrafındaki disiplini anlatır.
glTF dosya yapısı glTF · genel bakış izleğindedir. Framework tarafında Suspense ve hook dispose R3F · yükleme & asset yaprağındadır. Burada vanilla Three + GLTFLoader köprüsü ele alınır: Draco/meshopt yüzey olarak, extension şeması olarak değil — decoder yalnızca «bağlanması gereken bir bağımlılık» olarak görülür.
Bir model yolculuğu sözleşmesi hazır olduğunda (önceki yaprak), runtime'ın işi
«bu paketi güvenle aç» olur. Ama tek satır loader.load() çağrısı yeterli
değildir: decoder eklenmemişse yükleme sessizce takılır, progress bağlanmamışsa
kullanıcı donmuş bir ekran görür, iptal yoksa route değişiminde yarış durumu oluşur.
Bu yaprak tam olarak bu üç boşluğu kapatan yüzeyi tanımlar.
Sayfayı bitirdiğinizde şu cümleyi kurabilmelisiniz: «GLTFLoader tek giriş noktasıdır; decoder konfigürasyon meselesidir; progress ve iptal olmadan yükleme kullanıcıya karşı sözleşmesizdir; sahne kökü doğrudan scene'e değil bir assetsRoot'a bağlanır.»
Bu sayfanın sınırı
Bu sayfa şunları anlatır:
- GLTFLoader kurulumu ve yükleme yaşam döngüsü
- Decoder eklentisi yüzeyi (Draco / meshopt)
- Progress, hata, iptal ve sahne kökü sözleşmesi
Bilinçli olarak dışarıda bırakılan konular:
- Accessor / bufferView şeması → glTF format
- useLoader / Suspense → Framework R3F
- Draco sıkıştırma kararı → Draco karar noktası
- Export ayarları → Modelleme izleği
Kısa ayrım: Bu sayfa «loader yüzeyi» · glTF format «byte şeması» · Framework «deklaratif yükleme».
GLTFLoader: tek giriş noktası
Holodepth: tüm harici modeller GLTFLoader (veya proje sarmalayıcısı) üzerinden girer. Doğrudan ObjectLoader veya elle JSON parse ile sahne kurmak borçtur — test, hata yakalama ve dispose yolu kaybolur, her yeni geliştirici kendi kısayolunu icat eder.
Tek giriş noktası, aynı zamanda gözlemlenebilirlik demektir: tüm yükleme hataları, progress olayları ve decoder ayarları bir yerde toplanır. Loader'ı sarmalayan ince bir fonksiyon (loadGltf(url, options)) proje büyüdükçe log, metrik ve retry mantığını tek noktadan eklemenizi sağlar.
Decoder yüzeyi: extension değil konfigürasyon
Draco veya meshopt sıkıştırması export'ta seçilir; runtime'da DRACOLoader / MeshoptDecoder bağlanır. Extension byte düzeni glTF izleğinde; burada yalnızca «decoder set edilmezse yükleme sessizce başarısız olur veya konsola belirsiz bir hata düşer» uyarısı önemlidir.
Decoder dosyaları (wasm/js) kendi başına bir asset'tir ve CDN yolu ayrıca yönetilmelidir. Holodepth önerisi: decoder yolu ortam değişkeninden okunur, sahne kodunda hard-code edilmez — böylece decoder sürümü yükseltildiğinde tek yerden değiştirilir. Hangi sıkıştırma seçileceği kararı ayrı bir yaprakta derinleşir.
Progress ve iptal sözleşmesi
LoadingManager veya loader callback ile byte ilerlemesi UI'a bağlanır. Route değişiminde veya sahne unload'da yükleme iptal edilebilir olmalıdır — aksi halde geç gelen model yanlış sahneye eklenir ve kullanıcı beklemediği bir asset'in aniden belirdiğini görür.
Progress değeri her zaman doğru total ile gelmez — Content-Length başlığı eksikse tarayıcı toplam boyutu bilemez ve yüzde hesaplanamaz. Bu durumda belirsiz bir yükleme göstergesi (spinner) tercih edilmeli, sahte bir yüzde uydurulmamalıdır.
Sahne kökü ve graph yerleşimi
gltf.scene genelde kök bir Group'tur. Holodepth: kökü doğrudan scene'e eklemek yerine bir assetsRoot altına toplamak dispose ve katman yönetimini kolaylaştırır — tek bir assetsRoot.clear() çağrısı sahne değişiminde bütün dinamik içeriği temizler.
Kamera, ışık ve statik çevre gibi «sahnenin sabit kısmı» ile dinamik olarak yüklenen modelleri aynı düğüm altında tutmamak, hangi kısmın hangi yaşam döngüsüne sahip olduğunu görünür kılar.
Loader tekilleştirme: bir sayfa, bir instance
Her yükleme için yeni bir GLTFLoader ve yeni bir DRACOLoader oluşturmak, decoder'ın (özellikle wasm modülünün) tekrar tekrar başlatılmasına yol açar. Holodepth önerisi: loader ve decoder modül seviyesinde bir kez oluşturulur, tüm yükleme çağrıları aynı instance'ı paylaşır.
Paylaşılan instance, aynı zamanda setPath gibi konfigürasyonun tek noktadan yönetilmesini sağlar. Tek istisna: birbirinden tamamen izole çalışması gereken test ortamları — orada bilinçli olarak ayrı instance kurulabilir.
Hata sözleşmesi: reject, fallback, retry
Ağ hatası, 404 veya bozuk dosya — loader'ın onError callback'i bu üçünü ayırt etmez, sadece bir hata verir. Runtime katmanı bu hatayı üç şekilde ele alabilir: kullanıcıya görünür bir fallback mesh göstermek, otomatik bir yeniden deneme (retry) tetiklemek veya hatayı üst katmana (route/sahne yöneticisi) fırlatmak.
Hangi stratejinin seçileceği projeye bağlıdır, ama sessiz kalmamak ortak kural olmalıdır — konsola düşen bir hata mesajı, kullanıcının hiçbir şey görmeden boş bir sahnede kalmasından daha iyidir.
Eşzamanlı yüklemeler ve sıralama
Aynı asset için art arda iki yükleme isteği (örneğin kullanıcı hızla iki route arasında geçtiğinde) iki ayrı gltf.scene üretebilir ve ikisi de sahneye eklenmeye çalışabilir. Holodepth: her yükleme isteğine bir kimlik verilir; yalnızca «son isteğin» sonucu sahneye eklenir, diğerleri dispose edilir.
Bu sıralama disiplini, aşağıdaki simüle kodda signal ile iptal mekanizmasına bağlanır — AbortController deseni, fetch tabanlı yüklemelerde de aynı mantıkla çalışır.
Yanlış yükleme kalıpları
- Decoder unutulmuş sıkıştırılmış GLB
- Her mesh için ayrı loader instance (decoder şişer)
- Progress yok → donmuş UI hissi
- İptal yok → route değişiminde yarış durumu
- Hata sessizce yutulmuş, kullanıcı boş sahnede kalmış
- Accessor dersini loader sayfasında beklemek
Loader yüzeyi «çalışıyor mu» sorusuna değil, «her koşulda doğru davranıyor mu» sorusuna cevap vermelidir.
Simüle: loader yüzeyi
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'
const draco = new DRACOLoader()
draco.setDecoderPath('/vendor/draco/')
const loader = new GLTFLoader()
loader.setDRACOLoader(draco)
let activeRequestId = 0
export function loadGltf(url, { onProgress, signal } = {}) {
const requestId = ++activeRequestId
return new Promise((resolve, reject) => {
loader.load(
url,
(gltf) => {
if (requestId !== activeRequestId) {
// Daha yeni bir istek geldi - bu sonucu sessizce ele
disposeGltfScene(gltf.scene)
return
}
resolve(gltf)
},
(evt) => {
if (evt.total > 0) onProgress?.(evt.loaded / evt.total)
},
(err) => reject(err),
)
signal?.addEventListener('abort', () => {
// Three r152+ - loader abort politikasi projeye gore genisletilebilir
requestId !== activeRequestId
})
})
}
function disposeGltfScene(root) {
root.traverse((obj) => obj.geometry?.dispose?.())
}
Simüle · assetsRoot yerleşimi ve dispose.
export function mountGltf(scene, assetsRoot, gltf) {
assetsRoot.add(gltf.scene)
return {
root: gltf.scene,
animations: gltf.animations,
dispose() {
assetsRoot.remove(gltf.scene)
gltf.scene.traverse((obj) => {
obj.geometry?.dispose()
if (obj.material) {
const mats = Array.isArray(obj.material) ? obj.material : [obj.material]
mats.forEach((m) => m.dispose())
}
})
},
}
}
Üç yükleme senaryosu
-
01 · Yükleme
Tekil model
- Decoder
- Opsiyonel
- İptal
- Genelde gerekmez
- Risk
- Basit ama unutulan hata yolu
-
02 · Yükleme
Karakter + varyant
- Decoder
- Draco tekil instance
- Sıralama
- Son istek kazanır
- Risk
- Eski varyant sahnede kalır
-
03 · Yükleme
Sahne seti (çoklu glb)
- Progress
- Toplam ağırlıklı
- İptal
- Zorunlu (route)
- Risk
- Kısmi yükleme, eksik sahne
Holodepth perspektifleri
Loader, format izleğinin runtime karşılığı
Şemayı bilmek yeterli değildir; decoder, progress ve iptal de yazılmalıdır. Format bilgisi statiktir, loader disiplini davranışsaldır.
Tekilleştirme sessiz bir performans kazancıdır
Paylaşılan loader/decoder instance'ı, kullanıcı hiçbir kod değişikliği görmeden decoder başlatma maliyetini bir kereye indirir.
Hata, sessizlikten daha iyidir
Bir yükleme başarısız olduğunda kullanıcıya hiçbir şey göstermemek, yanlış bir şey göstermekten daha kötüdür — en azından teşhis edilebilir bir iz kalmaz.
- Tüm yüklemeler tek loader sarmalayıcıdan mı geçiyor?
- Decoder yolu ortam değişkeninden mi okunuyor?
- Progress belirsizse sahte yüzde mi üretiliyor?
- Route değişiminde eski istek iptal ediliyor mu?
- Hata her zaman görünür bir sonuca mı bağlanıyor?
- Sahne kökü assetsRoot altında mı, doğrudan scene'de mi?
- Eşzamanlı isteklerde «son istek kazanır» kuralı var mı?
Holodepth içgörüsü
Yükleme yüzeyi oturunca sıradaki durak: versiyon ve önbellek mührü — aynı URL'in her zaman aynı byte'ı taşıdığından emin olma disiplini.
Sonraki kapı
Loader sözleşmesi netse versiyon & önbellek mührü yaprağında hash, immutable URL ve stale risk ele alınır.