2 poin oleh GN⁺ 2024-05-03 | 1 komentar | Bagikan ke WhatsApp
  • ShapeUp lahir dari tantangan Wheel Reinvention Jam untuk “melihat kembali software yang sudah ada dari sudut pandang baru”, dan selesai sebagai 3D modeler yang dilengkapi demo yang berjalan di browser serta ekspor .obj
  • Kunci yang membuat alat 3D bisa dibuat dalam seminggu adalah ray marched SDF; dibanding renderer berbasis segitiga, ini memungkinkan implementasi scene dengan warna, bayangan halus, dan ambient occlusion secara lebih cepat
  • Implementasinya dijaga tetap sederhana dengan berpusat pada satu file C, dan model menyimpan hingga 100 Shape dalam array statis untuk mengurangi beban manajemen memori
  • raylib membantu menampilkan jendela OpenGL dengan cepat, tetapi karena API yang berpusat pada int, ketiadaan validasi parameter, dependensi GLFW, dan keterbatasan raygui, pengembang harus memakai OpenGL langsung atau membuat ulang fitur sendiri
  • Hasil akhirnya berukuran 2024 baris C dan 250 baris GLSL, total sekitar 2300 baris, serta mendukung buka/simpan file, berjalan di berbagai platform, dan ekspor .obj

Proses ShapeUp Berubah Menjadi 3D Modeler

  • Wheel Reinvention Jam adalah acara pemrograman selama seminggu untuk meninjau kembali sistem software yang sudah ada dari sudut pandang baru
  • Target awalnya berangkat dari ketidakpuasan terhadap compiler TypeScript yang lambat, yaitu membuat subset TypeScript yang lebih cepat daripada tsc
    • Dengan menjadikan parser TypeScript milik esbuild atau Bun sebagai titik awal, hal itu tampak memungkinkan
    • Namun demo suksesnya hanya berupa “satu perintah terminal selesai lebih cepat daripada perintah lain”, sehingga kurang menarik secara visual, dan akhirnya arahnya dialihkan ke 3D
  • ShapeUp dibuat sebagai 3D modeler untuk mengedit bentuk dengan mouse
    • Pengembang sudah pernah menulis shader SDF sebelumnya, tetapi cara memodelkan dengan mengubah kode langsung terasa tidak alami
    • Tujuannya adalah memungkinkan pengeditan bentuk berbasis SDF dengan mouse

Mengapa SDF Memungkinkan Proyek Seminggu Ini

  • Basis rendering ShapeUp adalah ray marched signed distance fields (SDFs)
  • Scene SDF, bahkan dengan warna, bayangan halus, dan ambient occlusion, bisa diimplementasikan lebih cepat daripada renderer berbasis segitiga
  • Contoh Inigo Quilez membuat karakter bergaya Pixar secara langsung dengan SDF menjadi acuan arah teknis
  • ShapeUp menangani pemodelan SDF dengan cara memanipulasi bentuk secara langsung, bukan mengedit kode

Implementasi C dan Struktur Data

  • ShapeUp ditulis dalam C dan menggunakan raylib untuk membuat jendela OpenGL
  • C dipilih karena kompilasinya cepat, sintaksnya tidak menyembunyikan perilaku kompleks, sudah familier, dan memungkinkan kompilasi ke native maupun WebAssembly
  • Model tersusun dari kumpulan struct Shape
    • Setiap Shape memiliki posisi, ukuran, sudut, radius sudut, tingkat blob, warna, sumbu mirroring, dan status subtract
  • Daftar Shape dikelola dengan array statis, bukan alokasi dinamis
    • MAX_SHAPE_COUNT bernilai 100
    • Status dikelola dengan Shape shapes[MAX_SHAPE_COUNT], shape_count, dan selected_shape
    • Pendekatan ini menghilangkan kemungkinan gagal alokasi dan kebocoran memori
  • Batas 100 Shape tidak menjadi masalah besar dalam penggunaan nyata
    • Karena waktu optimasi renderer terbatas, frame rate sudah turun lebih dulu sebelum mencapai 100 Shape
    • Jika ada waktu, rencananya model akan dibagi menjadi unit bata kecil dan ray marching dilakukan di dalam tiap bata

Cara Penggunaan Memori

  • ShapeUp hanya memakai alokasi memori dinamis di 3 tempat
    • Penyimpanan: mengalokasikan buffer yang dapat memuat seluruh dokumen
    • Ekspor .OBJ: mengalokasikan buffer yang dapat memuat semua vertex
    • Pembuatan shader GLSL: mengalokasikan buffer untuk source shader
  • Pada tiap kasus, free hanya dipanggil sekali di akhir fungsi
  • Bisa saja setiap Shape di-malloc satu per satu lalu pointer-nya disimpan di array dinamis, tetapi struktur seperti itu tidak diperlukan untuk proyek ini
  • C memiliki keunggulan karena memungkinkan kontrol langsung atas layout memori
  • Jika membutuhkan array dinamis atau hashmap, alat seperti stb_ds.h dapat digunakan

Cara Implementasi UI

  • UI diimplementasikan dengan pendekatan immediate mode user interface (IMGUI)
  • Keunggulan IMGUI adalah mudah di-debug, dan posisi elemen dapat ditentukan dengan bahasa pemrograman nyata alih-alih CSS, constraints, atau SwiftUI
  • Elemen yang fokus dan aksi mouse dilacak dengan enum Control
    • Status operasi seperti posisi, ukuran, sudut, warna, pemindahan, rotasi, skala, rotasi kamera, dan tingkat blob direpresentasikan sebagai nilai enum
    • focused_control dan mouse_action menyimpan status UI saat ini

Titik Buntu dengan raylib dan raygui

  • raylib berguna untuk menampilkan jendela OpenGL dengan cepat, tetapi seiring waktu menjadi faktor yang memperlambat pengembangan
  • Bagian yang paling merepotkan dari API raylib adalah kurangnya informasi tipe
    • int digunakan bahkan di tempat yang mengharapkan tipe enum, sehingga compiler tidak dapat melakukan pemeriksaan tipe
    • Makna parameter tidak terlihat jelas hanya dari signature fungsi
    • Misalnya, gesture pada IsGestureDetected(unsigned int gesture) tampak seperti ID gesture yang terdaftar, padahal sebenarnya enum Gesture
    • Karena dokumentasinya berpusat pada file header, pengembang harus melihat implementasi untuk memastikan int mana yang sebenarnya enum
  • Desain yang tidak melakukan validasi parameter dasar juga memperbesar masalah
    • LoadFileData(const char *fileName, int * dataSize) menyebabkan segfault jika dataSize bernilai NULL
    • Header tidak menunjukkan apakah dataSize adalah parameter output atau apakah nilainya tidak boleh null
    • Ketiadaan validasi membuat masalah sederhana sulit dilacak, dan dalam beberapa kasus bisa menyebabkan perilaku aneh secara diam-diam
  • Penanganan dependensi juga berbeda dari harapan
    • Ada masalah di GLFW yang tidak diakali oleh raylib dan patch-nya juga tidak diajukan
    • Pengguna akhir lebih peduli apakah fitur raylib berjalan dengan benar daripada detail implementasi internal cara pembuatan jendela
  • Library UI raygui memiliki terlalu banyak keterbatasan untuk proyek ini
    • Tidak bisa menampilkan angka floating-point, sehingga field teks float harus dibuat sendiri
    • Tidak dapat menangani routing event mouse untuk elemen yang saling menimpa atau terpotong
    • Tidak mendukung sudut membulat yang umum di UI
    • Sulit ditata agar terlihat bagus
  • Bug juga mengganggu alur pengembangan
    • Karena bug pada tool raygui, font default yang terlalu bergaya tidak bisa diganti
    • Fungsi gambar seperti DrawCircle(...) tidak berbagi vertex antarsegitiga, sehingga jika matrix saat ini memiliki skala atau rotasi, error floating-point menimbulkan celah piksel
  • Selama beberapa waktu masalah yang ditemukan dilaporkan, tetapi sebagian besar ditutup sebagai “wont fix”, sehingga pelaporan akhirnya dihentikan
  • Cara mengakalinya adalah memakai fungsi OpenGL langsung atau mengimplementasikan fitur yang dibutuhkan dari awal
  • Ke depan, rencananya akan memakai sokol sebagai pengganti raylib

Empat Hal yang Harus Diselesaikan dalam 6 Hari

  • ShapeUp harus menyelesaikan empat bagian besar dalam 6 hari
    • Antarmuka pengguna: gizmo 3D, shortcut keyboard, sidebar, game controller
    • Generator shader GLSL dan renderer ray marching
    • Seleksi mouse berbasis GPU
    • Marching cubes untuk ekspor
  • Kesulitannya bukan pada masing-masing fitur itu sendiri, melainkan pada menjaga prioritas
  • Masalah yang sulit atau memakan waktu lama dihindari dengan mengubah desain, atau ditangani dengan solusi sederhana yang bekerja untuk 90% kasus
  • Untuk beberapa fitur, solusinya muncul setelah dibiarkan tertunda selama sehari
  • Cara kerjanya adalah selalu mempertahankan 3D modeler yang berfungsi, lalu meningkatkannya secara bertahap sejauh waktu memungkinkan
    • Ini dianalogikan bukan sebagai cara yang baru menjadi piramida hanya pada saat selesai, melainkan cara yang di tahap mana pun jika berhenti tetap menjadi piramida kecil yang utuh

Hasil Akhir

  • Pada akhir minggu, ShapeUp sudah bisa membuat model 3D yang bermakna dan mengekspornya sebagai file .obj
  • Program berjalan di berbagai platform, serta mendukung buka dan simpan file
  • Ukuran kodenya adalah 2024 baris C dan 250 baris GLSL
  • Fakta bahwa 3D modeler yang cukup berguna dapat dibuat dalam sekitar 2300 baris merupakan hasil yang menonjol
  • Proyeknya sendiri relatif sederhana, tetapi kemampuan memilih apa yang akan dibuat, pengetahuan untuk membuatnya, dan disiplin untuk menyelesaikannya dalam seminggu berperan penting

1 komentar

 
GN⁺ 2024-05-03
Komentar Hacker News
  • Saya sepenuhnya sependapat dengan penulis soal keterbatasan Raylib. Saat ini saya sedang membuat gim bergaya tower defense yang dimulai dengan Raylib, dan saya mengalami banyak keterbatasan yang sama serta masalah lainnya
    Misalnya, peralihan layar penuh tidak berfungsi konsisten di tiap platform, mode layar tidak bisa dienumerasi, sulit menyalakan/mematikan fitur rendering saat berjalan, ada masalah menyimpan shader yang sudah dikompilasi, dan sebagainya
    Meski begitu, saya menghargai kerja yang Ray curahkan ke library ini dan berniat terus mendukungnya. Raylib bagus untuk membuat prototipe dengan cepat, tetapi tidak mudah untuk melangkah lebih jauh dari itu kecuali rela menerima batasan yang berat
    Jelas ada banyak hal yang saya pelajari, tetapi pengembangan sudah terlalu jauh untuk merombak semua kode terkait Raylib dan menggantinya dengan sesuatu seperti SDL

    • Menarik juga detail bahwa Raylib berasal dari nama pembuatnya, Ray, bukan dari ray tracing
      Things Unexpectedly Named After People: https://notes.rolandcrosby.com/posts/unexpectedly-eponymous/
    • Raylib mudah untuk mulai digunakan, tetapi begitu proyek menjadi sedikit kompleks, ia justru mulai menggigit balik. Sebaliknya, SDL membutuhkan waktu lebih lama untuk setup awal, tetapi skalanya sangat baik ketika proyek membesar. Kualitas kodenya juga luar biasa bagus
    • Raylib punya banyak masalah yang tampaknya tidak akan diperbaiki ke depannya, tetapi sulit juga menyalahkan Raylib saja untuk masalah layar penuh. Layar penuh di Windows praktis sudah rusak sampai nyaris tidak bisa dipakai selama puluhan tahun, dan kemungkinan platform lain juga serupa
      Strategi sekarang adalah memakai mode jendela tanpa bingkai saja dan berpura-pura layar penuh sungguhan tidak ada
    • Rasanya mirip. Sekitar 2 bulan lalu saya memulai proyek dan memilih Raylib; hal-hal dasar memang berjalan sangat mudah, tetapi makin sering dipakai makin sering bertemu ketidaknyamanan kecil yang acak. Sekarang investasi saya di proyek sudah terlalu besar untuk membatalkan penggunaan Raylib
      Masalah terbesar saat ini adalah penanganan font dan rendering teks. Sepertinya saya harus beralih dari font TTF ke font bitmap yang sudah di-bake sebelumnya, tetapi nanti saat lokalisasi ini mungkin akan cukup menyakitkan
      Dua fitur yang paling saya rindukan setelah pindah dari Love2D adalah merender teks multiwarna dengan mudah, serta memotong tekstur lalu mengulang atau menyusunnya sebagai tile dengan mudah. Di Raylib, teks harus dipecah sendiri berdasarkan markup warna, diberi offset lebar, lalu fungsi gambar dipanggil untuk tiap potongan sambil tetap memperhitungkan line break
      Jika menggambar banyak teks di layar, FPS juga tampaknya turun drastis; mungkin batching draw call untuk teksnya rusak. Dulu ada fungsi untuk menggambar tekstur tile, tetapi entah kenapa dihapus
    • Ini membuat saya ingin melihat raylib. Ada contoh-contoh lucu yang berjalan dengan WebAssembly: https://www.raylib.com/examples.html
      Hal yang selalu mengganggu saya di Wasm dan grafis 3D/2D browser adalah masalah kecil seperti scrolling sering terlihat. Lihat contoh “Background scrolling & parallax” di sini: https://www.raylib.com/examples.html
      Saya mengujinya di beberapa perangkat, dan kalau mata saya tidak keliru, itu jelas bukan scrolling yang mulus. Rasanya sulit dipercaya bahwa pada 2024, scrolling 2D yang mulus masih belum menjadi masalah yang terselesaikan
  • “Shape disimpan dalam array yang dialokasikan secara statis. Tidak ada kegagalan alokasi, tidak ada kebocoran, tidak ada embel-embel. Menyenangkan. Batas 100 Shape ternyata bukan kendala dalam praktiknya. Karena hampir tidak ada waktu untuk mengoptimalkan renderer, frame rate mungkin sudah turun sebelum mencapai 100.”
    Ini salah satu contoh terbaik menghindari optimasi prematur yang saya lihat belakangan ini

    • Menurut saya justru hampir kebalikannya. Itu menghindari abstraksi dan generalisasi yang prematur
    • Ini contoh terbaik yang menunjukkan perbedaan antara orang yang benar-benar membuat sesuatu dan orang yang hanya duduk berdebat soal bagaimana sesuatu seharusnya dibuat
  • Artikel yang sangat menarik, dan saya suka karena membahas berbagai keputusan seperti cara menangani memori serta masalah yang ditemui di raylib. Kebetulan saya sedang mengulang C lagi sambil masuk ke bagian 2 Crafting Interpreters, jadi menyenangkan diingatkan kembali pada hal-hal yang dikerjakan C dengan baik

  • Demo real-time di videonya benar-benar bagus. Mengesampingkan pembuatan aplikasinya, kalau saya yang mencoba, mungkin videonya saja tidak akan selesai dalam seminggu

    • Membuat videonya memakan waktu lebih lama daripada aplikasinya. Saya tidak tahu bagaimana para YouTuber bisa melakukannya sekonsisten itu
  • Dulu sekali saya pernah mengerjakan sistem operasi untuk telepon meja. RAM-nya hanya 64K, jadi sama sekali tidak ada manajemen memori dinamis, dan kami banyak memakai variabel statis agar compiler menata semuanya pada waktu kompilasi
    Mudah terlupakan bahwa banyak aplikasi mungkin sama sekali tidak membutuhkan manajemen memori dinamis. Sering kali cukup dengan mengalokasikan beberapa buffer berukuran tetap, lalu menangani kasus pengecualian ketika buffer itu penuh dengan rapi
    Dalam konteks seperti itu, C sebenarnya jauh lebih aman. Tidak ada memory leak, dan yang perlu dikhawatirkan hanya buffer overflow. Jika semua variabel dialokasikan secara statis, ini bisa dikelola dengan memakai sizeof secara hati-hati
    Bukan berarti Rust dan Go bukan pilihan bagus saat ini, tetapi C lama yang sederhana juga masih bekerja dengan baik dan tidak harus serumit mimpi buruk

  • Sedikit keluar topik, tetapi saya senang karena ini pertama kalinya saya melihat antarmuka WebAssembly yang teksnya tidak tampak buram. Benar-benar pertama kali
    Jika diperluas ke program dan beberapa sistem operasi, misalnya sampai Windows, dalam beberapa tahun terakhir cara rasterisasi teks telah menjadi tren umum sekaligus pengaturan default, dan itu menimbulkan masalah secara keseluruhan
    Sayangnya, pengguna sering kali tidak bisa mematikan anti-aliasing untuk mendapatkan teks yang tajam, dan meskipun kadang ada opsi, antarmuka seperti menu tetap diberi anti-aliasing

    • Teks tajam itu tidak terlalu mengesankan. Tipografinya tidak punya kurva dan tidak ada smoothing. Pada resolusi apa pun, itu akan terlihat sebagai teks yang tajam dan kotak-kotak seperti blok
    • Saya penasaran apa hubungan antara WebAssembly dan rasterisasi. Ini benar-benar terlihat menarik
  • Saya sangat suka proyek seperti ini. Saya masih menyukai sifat low-level C. Saat ini saya banyak memakai Rust dan Elixir/Erlang, tetapi saya sering merindukan kesederhanaan dan keeksplisitan C
    Karena itu saya juga banyak memakai Zig; bahasanya mempertahankan banyak filosofi C sambil memperbaikinya dengan cukup baik

    • Erlang juga bahasa yang cukup sederhana
  • Saya sangat setuju dengan penilaiannya tentang C. Terutama bagian bahwa “sintaksnya tidak menyembunyikan perilaku yang kompleks. Bahasanya cukup sederhana sehingga tidak perlu terus-menerus dicari tahu”, dan lebih jauh lagi, ketika perlu mencari sesuatu tentang C pun prosesnya sangat mudah dan bermanfaat
    Bahasa yang sederhana dan tua memang punya kelebihannya sendiri

  • Jika setiap Shape dialokasikan terpisah dengan malloc lalu pointer-pointer itu disimpan dalam array dinamis, memang jelas kita bisa membuat pekerjaan sendiri jadi lebih sulit. Ada pernyataan bahwa bahasa seperti C# memaksakan struktur alokasi semacam itu, tetapi saya penasaran apa yang sebenarnya mencegah penggunaan array struct berukuran tetap di C# seperti yang dilakukan penulis di C

    • Tidak ada yang mencegah. Di C# pun penggunaan array struct seperti ini bukan hal yang langka
  • Saya berharap ada yang terus melanjutkan proyek ini. Dengan beberapa bulan pemolesan lagi, untuk penggunaan tertentu ini bisa menjadi alternatif serius bagi Blender atau FreeCAD, dan kurva belajarnya juga tampak jauh lebih landai

    • Ada baiknya juga melihat MagicaCSG, versi yang lebih canggih dan tetap gratis: https://ephtracy.github.io/index.html?page=magicacsg#ss-caro...
    • Koreksi: aha, program ini sudah mendukung ekspor mesh dengan marching cubes. Lihat video YouTube di situsnya. Saya tidak tahu itu
      Namun karena pada dasarnya bekerja dengan SDF, pengalaman pemodelannya berbeda dari mesh tradisional yang memakai segitiga, vertex, dan sebagainya, dan data yang disimpannya pun berbeda
      Mengubah SDF menjadi mesh bisa dilakukan dengan metode seperti marching cubes, tetapi data seperti itu kemungkinan besar tetap harus dirapikan lagi nantinya di aplikasi semacam Blender
      Jika renderer-nya juga berbasis SDF, maka SDF sangat bagus. Namun kebanyakan tidak begitu
      Maaf kalau ini sudah diketahui
    • Jika menyukai SDF, Womp cukup bagus sebagai titik awal. Tinkercad juga cukup lumayan sebagai CAD untuk pemula
    • Ada baiknya juga melihat Dune3D dan Salome-Platform
    • Ini bisa menjadi alternatif Blender, tetapi sayangnya tampaknya bukan alternatif untuk pekerjaan CAD