2 poin oleh GN⁺ 2024-05-03 | 1 komentar | Bagikan ke WhatsApp

Membuat modeler 3D dengan C dalam seminggu

  • Pada musim gugur tahun lalu, saya mengikuti acara pemrograman seminggu bernama "Wheel Reinvention Jam".
  • Tujuannya adalah melihat ulang sistem perangkat lunak yang sudah ada dari perspektif baru.
  • Saya membuat 3D modeler bernama "ShapeUp", dan menonton demo ShapeUp terlebih dahulu sebelum membaca artikel ini sangat membantu pemahaman.
  • ShapeUp dapat dicoba langsung di browser.

Pemilihan bahasa: C

  • Saya ikut jam ini karena tidak puas dengan compiler TypeScript yang lambat.
  • Jika dimulai dari parser TypeScript esbuild atau Bun, proyek yang mengimplementasikan subset cepat TypeScript tampak memungkinkan.
  • Tapi perbandingan kecepatan eksekusi perintah terminal dirasa tidak akan menghasilkan demo yang menarik, sehingga arah berubah ke proyek 3D.
  • Dengan teknik ray marching signed distance fields (SDF), tampak mungkin membuat proyek 3D dari awal dalam satu minggu.
  • Rendering dengan SDF bisa dibangun jauh lebih cepat daripada renderer berbasis segitiga yang setara.
  • Saya pernah menulis shader SDF sebelumnya, tetapi hanya pada level sangat dasar, dan mengedit model lewat kode terasa tidak alami.
  • Saya ingin mengedit bentuk dengan mouse; jam ini saya anggap peluang untuk mewujudkannya.
  • Saya menamai proyek ini ShapeUp.

Kelebihan menggunakan C

  • C adalah bahasa yang sangat sederhana dan mendasar, jadi saya menganggap akan banyak waktu terbuang untuk mengatasi kurangnya struktur data bawaan serta memperbaiki bug pointer.
  • Tapi kesederhanaan C justru jadi kelebihan:
    • Kompilasi cepat.
    • Sintaks tidak menyembunyikan operasi yang kompleks.
    • Sederhana, jadi tidak perlu terus-menerus mencari-cari sintaks.
    • Mudah dikompilasi ke native maupun WebAssembly.
  • Kekurangan C dapat dihindari berkat kebiasaan yang saya bangun dari penggunaan 22 tahun.
  • ShapeUp sangat sederhana dan hanya terdiri dari satu file C kecil.

Struktur data ShapeUp

  • Model terdiri dari array dari struktur bernama Shapes.
  • Shapes disimpan dalam array dengan alokasi statis:
    • Risiko alokasi gagal atau kebocoran memori nyaris tidak ada.
    • Batas 100 Shape ternyata tidak terlalu membatasi dalam praktik.
    • Karena waktu optimasi renderer terbatas, framerate kemungkinan sudah turun sebelum mencapai 100.
    • Jika ada waktu, saya akan membagi model menjadi blok-blok kecil lalu melakukan raymarching di dalam tiap blok.
  • Alokasi memori dinamis hanya dipanggil di tiga tempat:
    • Penyimpanan (mengalokasikan buffer sebesar yang cukup untuk seluruh dokumen)
    • Ekspor OBJ (mengalokasikan buffer sebesar yang cukup untuk seluruh vertex)
    • Pembuatan shader GLSL (buffer untuk sumber shader)
  • Setiap kasus punya satu free tunggal di akhir fungsi.
  • Contoh bahwa manajemen memori di C bisa dibuat sederhana.
  • Bahasa seperti C#, JavaScript, dan Python memaksa struktur alokasi yang memanggil malloc per Shape dan menyimpan pointer-pointernya dalam array dinamis.
  • C enak karena memberi kendali penuh terhadap tata letak memori.

Antarmuka pengguna

  • Diimplementasikan dengan immediate mode user interface (IMGUI)
  • Saya suka UI bergaya IMGUI karena:
    • Debugging sangat mudah.
    • Penempatan elemen memakai bahasa pemrograman sungguhan (berbeda dengan CSS, constraints, dan SwiftUI).
  • Seperti sebagian besar IMGUI, saya melacak elemen yang difokuskan atau tindakan mouse yang sedang berjalan menggunakan enum.
  • Proyek ini tidak butuh array dinamis atau hashmap, tapi kalau butuh saya akan memakai sesuatu seperti stb_ds.h.

Masalah pada pustaka raylib

  • Keputusan memakai C bagus, tetapi raylib malah menjadi masalah.
  • Ada beberapa pilihan desain aneh yang membuat developer experience buruk:
    • Menggunakan int di tempat tipe enum, sehingga pemeriksaan tipe kompilator hilang dan fungsi tidak self-documenting.
    • Tidak melakukan validasi parameter bawaan (sebagai keputusan desain).
    • Tidak bertanggung jawab atas dependensi (tidak menyelesaikan issue GLFW atau mengirim patch).
  • Pustaka UI raygui terasa seperti mainan:
    • Tidak bisa menampilkan angka floating point.
    • Tidak menangani routing event mouse untuk elemen yang tumpang tindih atau terpotong.
    • Tidak bisa membuat sudut membulat.
    • Tidak bisa memberi gaya tampil yang rapi.
  • Ada juga bug:
    • Bug yang mencegah perubahan font.
    • Fungsi gambar tidak membagi vertex bersama antar segitiga sehingga ada celah piksel.
  • Saya melaporkan tiap masalah yang ditemukan, tetapi kebanyakan ditutup sebagai "won't fix", lalu saya menyerah karena menulis bug report terlalu menyita waktu.
  • Bagus karena menyediakan window OpenGL, tapi saya membayar mahal untuk kenyamanan itu.
  • Untungnya saya menemukan jalan keluar: menggunakan fungsi OpenGL secara langsung atau mengimplementasikan fitur dari awal.
  • Ke depan, saya berencana memakai sokol.

Proses pengembangan selama seminggu

  • ShapeUp terdiri dari empat bagian utama yang harus selesai dalam 6 hari:
    1. Antarmuka pengguna (alat 3D, shortcut keyboard, sidebar, gamepad)
    2. Generator shader GLSL + renderer ray marching
    3. Seleksi mouse berbasis GPU
    4. Marching cubes untuk ekspor
  • Masing-masing memang tidak sulit, tetapi tantangannya adalah menetapkan prioritas dengan benar dan tidak menyimpang.
  • Untuk masalah yang rumit atau memakan waktu, membantu saat diselesaikan lewat desain, atau memakai solusi bodoh yang tetap bekerja pada 90% kasus.
  • Terkadang menunda fitur sekitar satu hari membuat solusi muncul secara tak sadar.
  • Saya selalu punya modeler 3D yang berfungsi, dan berusaha memperbaikinya secara bertahap saat waktu mengizinkan.
  • Saya membayangkannya seperti membangun piramida: meski tidak bisa selesai sampai langkah terakhir, ia tetap bisa menjadi piramida utuh di setiap tahap berhenti.

Hasil proyek

  • Setelah seminggu, saya punya program 3D yang bisa membuat model bermakna dan mengekspor ke file .obj.
  • Berjalan lintas platform, dengan fitur buka dan simpan berkas.
  • Proyek ini berisi 2.024 baris kode C dan 250 baris GLSL.
  • Menarik bahwa sekitar 2.300 baris sudah bisa mewakili 3D modeler yang lumayan berguna.
  • Saya diminta menampilkan demo ShapeUp saat rangkuman Jam dan di konferensi Handmade Seattle.
  • Orang-orang tampak terkesan, walau sepertinya itu bukan capaian besar; ini proyek yang relatif sederhana.
  • Jika ada yang istimewa dari apa yang saya kerjakan, itu adalah insting memilih apa yang akan dibuat, pengetahuan untuk membuatnya, dan disiplin menyelesaikan dalam seminggu.

Opini GN⁺

  • Proyek ini menarik dan menunjukkan keunggulan dari kesederhanaan serta kecepatan C. Tapi C yang tingkat abstraksinya rendah tampak sulit dipakai langsung untuk proyek komersial. Mengimplementasikan semua fitur alat pemodelan 3D modern dalam C kemungkinan butuh upaya yang sangat besar.
  • Sangat mengesankan bisa menyelesaikan program yang bisa berjalan dalam seminggu. Tapi untuk sudut pandang jangka panjang, agar pemeliharaan dan perluasan fitur lebih baik, mungkin bahasa seperti C++ atau Rust lebih tepat dipilih.
  • Teknik rendering berbasis SDF cepat dan sederhana, tetapi tampak memiliki batasan pada kebebasan dan kualitas pemodelan. Alat pemodelan komersial umumnya memakai teknik pemodelan permukaan seperti SubD atau NURBS. Untuk game atau demo yang menuntut real-time, rendering SDF tetap terlihat sangat bernilai.
  • Ini contoh yang baik tentang sulitnya memilih pustaka open source: perlu menilai dokumentasi, kualitas kode, dan dukungan secara cermat sebelum memutuskan. Implementasi mandiri juga bisa jadi alternatif yang baik.
  • Pendekatan membuat program yang berfungsi dulu lalu mengoptimalkan secara bertahap juga sangat berguna dalam kerja nyata. Untuk itu, penting menata prioritas agar fitur inti selesai dulu lalu detailnya ditingkatkan secara bertahap.

1 komentar

 
GN⁺ 2024-05-03
Komentar Hacker News
  • Saya sejalan sepenuhnya dengan penulis soal keterbatasan Raylib.
    • Saya sedang mengembangkan game gaya tower defense yang dimulai dengan Raylib, namun menghadapi pembatasan yang serupa.
    • Ada masalah seperti ketidaksesuaian perpindahan layar penuh lintas platform, tidak bisa mendaftarkan mode layar, tidak bisa mengubah fitur rendering saat runtime, dan tidak bisa menyimpan shader yang sudah dikompilasi.
    • Raylib bagus untuk membuat prototipe, tetapi sulit maju lebih jauh kecuali menerima keterbatasan yang sangat berat.
    • Pengembangan sudah terlalu lanjut, jadi sekarang terlambat untuk menggantinya dengan SDL atau sejenisnya.
  • Menyimpan bentuk dalam array yang dialokasikan secara statis adalah pendekatan yang cantik karena tidak punya risiko alokasi gagal atau kebocoran memori. Nyatanya, batasan 100 bentuk tidak jadi hambatan.
  • Saya berharap proyek ini terus berkembang. Beberapa bulan lagi bisa menjadi alternatif serius untuk kasus penggunaan tertentu dari Blender/FreeCAD dengan kurva pembelajaran yang jauh lebih landai.
  • Saya sangat suka demo langsung di videonya. Belum lagi pembuatan aplikasi, apalagi menyusun video sejenis itu dalam waktu 1 minggu.
  • Menarik membaca tulisan yang membahas berbagai keputusan terkait memori dan lain-lain. Saat saya mulai mempelajari lagi C lewat bagian 2 of Crafting Interpreters, hal ini mengingatkan saya akan apa yang C kerjakan dengan baik.
  • Terima kasih atas usaha yang membuat sesuatu yang ironis menjadi mungkin dengan 2.024 baris kode C :)
  • Ada kekuatan yang sungguh besar saat membuat sesuatu yang keren hanya dengan alat yang sudah kita kuasai. Tulisan yang bagus.
  • Saya sangat setuju dengan argumen tentang C. Terutama sangat selaras dengan "bahwa sintaks tidak menyembunyikan operasi yang kompleks; sederhana sehingga tak perlu terus-menerus dicari." Selain itu, jika harus mempelajari sesuatu tentang C, itu sangat mudah dan berguna. Itulah keunggulan bahasa yang sederhana dan klasik.
  • Terkadang C terasa sebagai segalanya yang kita butuhkan.
  • Perkembangan pengerjaan yang mengagumkan. Penjelasan di videonya juga sangat menyenangkan untuk ditonton.