- 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
esbuildatauBunsebagai 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
- Dengan menjadikan parser TypeScript milik
- 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_COUNTbernilai 100- Status dikelola dengan
Shape shapes[MAX_SHAPE_COUNT],shape_count, danselected_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,
freehanya dipanggil sekali di akhir fungsi - Bisa saja setiap Shape di-
mallocsatu 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.hdapat 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_controldanmouse_actionmenyimpan 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
intdigunakan bahkan di tempat yang mengharapkan tipe enum, sehingga compiler tidak dapat melakukan pemeriksaan tipe- Makna parameter tidak terlihat jelas hanya dari signature fungsi
- Misalnya,
gesturepadaIsGestureDetected(unsigned int gesture)tampak seperti ID gesture yang terdaftar, padahal sebenarnya enumGesture - Karena dokumentasinya berpusat pada file header, pengembang harus melihat implementasi untuk memastikan
intmana yang sebenarnya enum
- Desain yang tidak melakukan validasi parameter dasar juga memperbesar masalah
LoadFileData(const char *fileName, int * dataSize)menyebabkan segfault jikadataSizebernilaiNULL- Header tidak menunjukkan apakah
dataSizeadalah 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
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
Things Unexpectedly Named After People: https://notes.rolandcrosby.com/posts/unexpectedly-eponymous/
Strategi sekarang adalah memakai mode jendela tanpa bingkai saja dan berpura-pura layar penuh sungguhan tidak ada
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
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
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
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
sizeofsecara hati-hatiBukan 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
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
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
malloclalu 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 CSaya 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
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