Aset 3D pada aplikasi web sering menjadi penyebab utama lonjakan First Contentful Paint (FCP) dan memory leak di WebGL context. Model 3D hasil ekspor dari software modeling atau visual web modeler sering membawa geometri unindexed, resolusi tekstur berlebih (misal 4K), serta struktur node yang tidak efisien. Memeriksa aset ini secara manual memperlambat proses deployment dan rawan human error.

Solusinya adalah mengintegrasikan CI/CD asset 3D web untuk menjalankan validasi integritas struktur file dan kompresi aset secara otomatis sebelum model masuk ke production storage atau repository statis.

Arsitektur Pipeline CI/CD untuk Asset 3D

Pipeline validasi dan optimasi aset 3D berjalan dalam alur berikut:

  1. Deteksi Berkas: Mengidentifikasi file .gltf atau .glb yang berubah pada pull request.
  2. Validasi Spesifikasi Khronos: Memeriksa integritas struktur file menggunakan gltf-validator resmi. Jika terjadi error sintaks, pipeline otomatis fail.
  3. Optimasi Geometri & Tekstur: Menjalankan serangkaian transformasi dengan @gltf-transform/cli, seperti deduping node, normalisasi ukuran tekstur, dan kompresi geometri.
  4. Enforcement Performance Budget: Memverifikasi ukuran akhir berkas. Jika ukuran melebihi batas batas ambang yang ditentukan (misalnya 5 MB), PR check akan diblokir.

Tooling CLI Utama

Pipeline ini menggunakan dua utility berbasis Node.js:

  • gltf-validator: Engine resmi dari Khronos Group untuk mendeteksi malformed glTF, missing buffer, invalid animation sampler, dan memory alignment issue.
  • @gltf-transform/cli: Toolkit untuk mutasi non-destruktif: kompresi mesh (Draco atau Meshopt), konversi tekstur, instance de-duplication, dan pruning atribut yang tidak terpakai.

Draco vs Meshopt

Pilih algoritma kompresi berdasarkan kebutuhan runtime target:

  • Draco (Google): Rasio kompresi sangat tinggi, ukuran berkas transfer minimal. Kelemahan: Beban dekompresi CPU di browser tinggi, decoding via Web Assembly membutuhkan waktu tambahan sebelum render awal.
  • Meshopt (Meshoptimizer): Rasio kompresi sedikit lebih rendah dibanding Draco, namun dekompresi berjalan sangat cepat dengan instruksi SIMD di WASM/GPU. Cocok untuk aplikasi 3D interaktif yang menuntut frame rate tinggi saat inisiasi.

Implementasi GitHub Actions Workflow

Simpan konfigurasi pipeline berikut pada path .github/workflows/3d-asset-pipeline.yml:

name: 3D Asset CI/CD Pipeline

on:
  pull_request:
    paths:
      - 'assets/**/*.glb'
      - 'assets/**/*.gltf'

jobs:
  validate-and-optimize:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install 3D Processing CLI Tools
        run: |
          npm install --global gltf-validator @gltf-transform/cli

      - name: Identify Changed Models
        id: files
        run: |
          # Ambil list file model yang ditambah atau diubah pada PR ini
          CHANGED_FILES=$(git diff --name-only --diff-filter=d HEAD~1 HEAD | grep -E '\.(glb|gltf)$' || true)
          echo "models=$CHANGED_FILES" >> $GITHUB_OUTPUT

      - name: Run Integrity Validation
        if: steps.files.outputs.models != ''
        run: |
          for file in ${{ steps.files.outputs.models }}; do
            echo "Validating: $file"
            gltf-validator -p -a -r "$file"
          done

      - name: Optimize Meshes and Textures
        if: steps.files.outputs.models != ''
        run: |
          mkdir -p dist/assets
          for file in ${{ steps.files.outputs.models }}; do
            filename=$(basename "$file")
            target_path="dist/assets/$filename"

            echo "Optimizing: $file -> $target_path"

            # Pipeline optimasi:
            # 1. dedup: Hapus duplikat mesh/material
            # 2. prune: Hapus accessor & vertex attributes yang tidak digunakan
            # 3. resize: Batasi resolusi tekstur maksimal 2048x2048
            # 4. meshopt: Kompresi mesh menggunakan meshoptimizer
            gltf-transform optimize "$file" "$target_path" \
              --compress meshopt \
              --texture-size 2048
          done

      - name: Enforce Performance Budget
        if: steps.files.outputs.models != ''
        run: |
          # Performance budget batas atas: 5 MB per model
          MAX_SIZE_KB=5120
          FAILED=0

          for optimized_file in dist/assets/*; do
            FILE_SIZE_KB=$(du -k "$optimized_file" | cut -f1)
            echo "File: $optimized_file - Size: ${FILE_SIZE_KB} KB (Max: ${MAX_SIZE_KB} KB)"

            if [ "$FILE_SIZE_KB" -gt "$MAX_SIZE_KB" ]; then
              echo "::error file=$optimized_file::Model melebihi budget performa (${FILE_SIZE_KB} KB > ${MAX_SIZE_KB} KB)!"
              FAILED=1
            fi
          done

          if [ "$FAILED" -eq 1 ]; then
            echo "Pull Request diblokir: Optimasi aset gagal memenuhi kriteria performa web."
            exit 1
          fi

      - name: Upload Optimized Artifacts
        if: steps.files.outputs.models != ''
        uses: actions/upload-artifact@v4
        with:
          name: optimized-3d-assets
          path: dist/assets/

Detail Langkah Optimasi

1. Deteksi Error Integrity

Command gltf-validator -p -a -r menghasilkan output status validasi dalam format JSON/teks. Flag -p memverifikasi pointer URI, flag -a memastikan seluruh buffer binari di-decode untuk mencari nilai NaN atau floating-point overflow pada vertex position.

2. Texture Downsampling

Beban GPU WebGL paling besar sering kali bukan polycount, melainkan VRAM yang dikonsumsi oleh tekstur uncompressed. Opsi --texture-size 2048 pada gltf-transform secara rekursif mereduksi resolusi diffuse, roughness, dan normal map ke resolusi maksimum 2048x2048, menjaga rasio aspek sembari menghemat memory footprint.

3. Performance Budget Blocker

Langkah budget check berfungsi sebagai quality gate. Menghentikan pipeline dengan status exit code 1 otomatis menandai status commit check PR menjadi failed di GitHub. Ini mencegah tim developer atau artis 3D me-merge model berukuran puluhan megabyte yang dapat merusak user experience di mobile browser.

Troubleshooting Masalah Umum

  • Texture Degradation: Jika normal map terlihat rusak pasca kompresi, pastikan filtering map tidak diubah menjadi format lossy ekstrem. Tambahkan flag --keep-materials atau batasi kompresi tekstur ke format KTX2/Basis Universal untuk mempertahankan visual fidelity.
  • Missing Skinning/Bones: Flag prune terkadang menghapus skin node jika animator menggunakan empty bone helper. Gunakan parameter preserve bones jika model memiliki animasi skeletal rigging kompleks.