
Pendahuluan
Sebagai aturan, ketika bekerja dengan parameter rendering, OpenGL bertindak sebagai mesin status. Status render adalah kumpulan atribut status, seperti sumber cahaya, bahan, tekstur, dan mode tampilan, dihidupkan dan dimatikan oleh fungsi glEnable () dan glDisable (). Ketika negara tertentu diatur, ia tetap berlaku sampai beberapa fungsi lain mengubahnya. Pipa OpenGL mendukung tumpukan keadaan untuk menyimpan dan memulihkan keadaan pada waktu tertentu. Mesin negara memberi pengembang kendali penuh atas status rendering saat ini dan yang disimpan pada stack.
Namun, pendekatan ini tidak nyaman ketika bekerja dengan OSG. Untuk alasan ini, mesin status OpenGL dienkapsulasi oleh kelas osg :: StateSet, yang menangani masalah stack negara dan mengaturnya dalam proses melintasi grafik adegan.
Sebuah instance dari kelas osg :: StateSet berisi subset dari berbagai status rendering dan dapat menerapkannya ke osg :: Node adegan node dan osg :: Objek geometris yang dapat digambar menggunakan metode setStateSet ()
osg::StateSet *stateset = new osg::StateSet; node->setStateSet(stateset);
Cara yang lebih aman adalah dengan menggunakan metode getOrCreateStateSet (), yang menjamin kembalinya keadaan yang benar dan lampirannya ke node atau objek yang dapat digambar
osg::StateSet *stateset = node->getOrCreateStateSet();
Osg :: Node dan osg :: Kelas Drawable mengontrol variabel anggota osg :: StateSet melalui penunjuk pintar osg :: ref_ptr <>. Ini berarti bahwa satu set negara dapat dibagi antara beberapa objek dalam adegan dan akan dihancurkan hanya ketika semua objek ini dihancurkan.
1. Atribut dan mode
OSG mendefinisikan kelas osg :: StateAttribute untuk menyimpan atribut rendering. Ini adalah kelas dasar virtual yang diwarisi oleh berbagai atribut render seperti cahaya, material, dan kabut.
Mode rendering berfungsi seperti sakelar yang dapat dihidupkan dan dimatikan. Selain itu, mereka terkait dengan enumerator, yang digunakan untuk menunjukkan jenis mode OpenGL. Terkadang mode rendering dikaitkan dengan atribut, misalnya, mode GL_LIGHTING mencakup variabel untuk sumber cahaya yang dikirim ke pipa OpenGL ketika dihidupkan, dan mematikan pencahayaan jika tidak.
Kelas osg :: StateSet membagi atribut dan mode menjadi dua kelompok: tekstur dan non-tekstur. Ini memiliki beberapa metode publik untuk menambahkan atribut dan mode non-tekstur ke serangkaian negara:
- setAttribute () - Menambahkan objek bertipe osg :: StateAttribute ke set state. Atribut dengan tipe yang sama tidak dapat hidup berdampingan di set status yang sama. Setpoint sebelumnya akan ditimpa dengan yang baru.
- setMode () - melampirkan enumerator mode ke set negara dan menetapkan nilainya ke osg :: StateAttribute :: ON atau osg :: StateAttribute :: OFF, yang berarti mengaktifkan atau menonaktifkan mode.
- setAttributeAndModes () - melampirkan atribut render dan mode yang terkait dan menetapkan nilai switch (default ke ON). Harus diingat bahwa tidak setiap atribut memiliki mode yang sesuai, tetapi Anda dapat menggunakan metode ini dalam hal apa pun.
Untuk mengatur atribut dan mode yang terkait, Anda dapat menggunakan kode ini
stateset->setAttributeAndModes(attr, osg::StateAttribute::ON);
Untuk mengatur atribut tekstur, parameter tambahan harus diberikan untuk menunjukkan tekstur yang harus diterapkan. Osg :: StateSet menyediakan beberapa metode publik lainnya untuk ini, seperti setTextureAttribute (), setTextureMode (), dan setTextureAttributeAndModes ()
stateset->setTextureAttributeAndModes(0, textattr, osg::StateAttribute::ON);
menerapkan atribut textattr ke tekstur dengan pengidentifikasi 0.
2. Mengatur mode tampilan poligon untuk node pemandangan
Kami menggambarkan teori di atas dengan contoh praktis - mengubah mode rasterisasi poligon OpenGL menggunakan kelas osg :: PolygonMode, yang diturunkan dari osg :: StateAttribute. Kelas ini mengenkapsulasi fungsi glPolygonMode () dan menyediakan antarmuka untuk mengatur mode tampilan poligon untuk node adegan tertentu.
Contoh polygonmodemain.h #ifndef MAIN_H #define MAIN_H #include <osg/PolygonMode> #include <osg/MatrixTransform> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::translate(-25.0f, 0.0f, 0.0f)); transform1->addChild(model.get()); osg::ref_ptr<osg::MatrixTransform> transform2 = new osg::MatrixTransform; transform2->setMatrix(osg::Matrix::translate(25.0f, 0.0f, 0.0f)); transform2->addChild(model.get()); osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode; pm->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); transform1->getOrCreateStateSet()->setAttribute(pm.get()); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(transform1.get()); root->addChild(transform2.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Di sini kita mengunggah model cessna kita tercinta dan menerapkan transformasi padanya, kita mendapatkan dua contoh model cessna. Untuk salah satu dari mereka, di sebelah kiri, kami menerapkan atribut yang mengatur mode tampilan wireframe poligon
osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode; pm->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); transform1->getOrCreateStateSet()->setAttribute(pm.get());

Jika kita beralih ke spesifikasi OpenGL, kita dapat dengan mudah membayangkan opsi tampilan poligon apa yang akan tersedia bagi kita ketika menggunakan setMode () dalam kasus khusus ini. Parameter pertama dapat mengambil nilai osg :: PolygonMode :: FRONT, BACK dan FRONT_AND_BACK, sesuai dengan enumerator OpenGL GL_FRONT, GL_BACK, GL_FRONT_AND_BACK. Parameter kedua dapat mengambil nilai osg :: PolygonMode :: POINT, LINE dan FILL, yang sesuai dengan GL_POINT, GL_LINE dan GL_FILL. Tidak ada trik lain, seperti yang sering terjadi ketika mengembangkan OpenGL murni, tidak diperlukan di sini - OSG menangani sebagian besar pekerjaan. Mode tampilan poligon tidak memiliki mode terkait dan tidak perlu memanggil pasangan glEnable () / glDisable (). Metode setAttributeAndModes () akan berfungsi dengan baik dalam kasus ini juga, tetapi nilai parameter ketiga tidak akan berguna.
3. Warisan status render. Menerapkan Atribut dan Mode
Himpunan status simpul mempengaruhi simpul saat ini dan semua anak-anaknya. Misalnya, atribut osg :: PolygonMode yang ditetapkan untuk transform1 dari contoh sebelumnya akan diterapkan ke semua anak dari simpul ini. Namun, simpul anak dapat mengesampingkan atribut induk, yaitu, keadaan rendering akan diwarisi dari simpul induk jika simpul anak tidak mengubah perilaku.
Terkadang Anda perlu mendefinisikan kembali perilaku simpul dalam hal penggunaan atribut. Misalnya, di sebagian besar editor 3D, pengguna dapat memuat beberapa model dan mengubah mode tampilan untuk semua model yang dimuat pada waktu yang sama, terlepas dari bagaimana mereka ditampilkan sebelumnya. Dengan kata lain, semua model dalam editor harus mewarisi atribut tunggal, terlepas dari bagaimana mereka ditetapkan sebelumnya untuk masing-masing model. Di OSG, ini dapat diimplementasikan menggunakan flag osg :: StateAttribute :: OVERRIDE, misalnya
stateset->StateAttribute(attr, osg::StateAttribute::OVERRIDE);
Saat mengatur mode dan mode dengan atribut, operator ATAU bitwise digunakan
stateset->StateAttributeAndModes(attr, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
Selain itu, atribut juga dapat dilindungi dari penggantian - untuk ini, atribut harus ditandai dengan osg :: StateAttribute :: flag PROTECTED.
Ada flag ketiga, osg :: StateAttribute :: INHERIT, yang digunakan untuk menunjukkan bahwa atribut ini harus diwarisi dari kumpulan state dari node induk.
Berikut adalah contoh singkat menggunakan flag OVERRIDE dan PROTECTED. Node root akan diatur ke OVERRIDE untuk memaksa semua node turunan mewarisi atribut dan modenya. Dalam kasus ini, child node akan mencoba mengubah statusnya dengan atau tanpa bantuan flag PROTECTED, yang akan menghasilkan hasil yang berbeda.
Mewarisi contoh teksmain.h #ifndef MAIN_H #define MAIN_H #include <osg/PolygonMode> #include <osg/MatrixTransform> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/glider.osg"); osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::translate(-0.5f, 0.0f, 0.0f)); transform1->addChild(model.get()); osg::ref_ptr<osg::MatrixTransform> transform2 = new osg::MatrixTransform; transform2->setMatrix(osg::Matrix::translate(0.5f, 0.0f, 0.0f)); transform2->addChild(model.get()); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(transform1.get()); root->addChild(transform2.get()); transform1->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); transform2->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED); root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }

Untuk memahami apa yang terjadi, Anda perlu melihat seperti apa tampilan glider yang biasanya menyala dengan mengunduh osgviewer OSG penuh waktu
$ osgviewer glider.osg
Dalam contoh ini, kami mencoba mengubah mode pencahayaan untuk node transform1 dan transform2 dengan sepenuhnya mematikan pencahayaan.
transform1->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); transform2->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
Dalam hal ini, kita menyalakan mode pencahayaan untuk simpul root, dan menggunakan bendera OVERRIDE untuk semua simpul turunannya, sehingga mereka mewarisi keadaan simpul akar. Namun, trnsform2 menggunakan flag PROTECTED untuk mencegah pengaturan simpul root mempengaruhi.
transform2->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
Akibatnya, terlepas dari kenyataan bahwa kami mematikan pencahayaan di node transform1, hang glider kiri masih menyala, karena pengaturan root scene memblokir upaya kami untuk mematikan pencahayaan untuk itu. Glider hang kanan ditampilkan tanpa pencahayaan (kelihatannya lebih cerah hanya karena dibanjiri dengan warna yang sederhana tanpa memberikan pencahayaan), karena transform2 dilindungi dari mewarisi atribut dari node root.
4. Daftar Atribut OpenGL yang Didukung di OpenSceneGraph
OSG mendukung hampir semua atribut dan mode rendering yang didukung oleh OpenGL melalui kelas-kelas yang diturunkan dari osg :: StateAttribute. Tabel menunjukkan semua parameter mesin status OpenGL yang tersedia dari engine.
ID jenis atribut | Nama kelas | Mode terkait | Fungsi OpenGL Setara |
---|
ALPHEFUNC | osg :: AlphaFunc | GL_ALPHA_TEST | glAlphaFunc () |
BLENDFUNC | osg :: BlendFunc | GL_BLEND | glBlendFunc () dan glBlendFuncSeparate () |
KLIPPLAN | osg :: ClipPlane | GL_CLIP_PLANEi (i dari 1 hingga 5) | glClipPlane () |
Colormask | osg :: ColorMask | - | glColorMask () |
CULLFACE | osg :: CullFace | GL_CULLFACE | glCullFace () |
Kedalaman | osg :: Kedalaman | GL_DEPTH_TEST | glDepthFunc (), glDepthRange () dan glDepthMask () |
KABUT | osg :: Kabut | GL_FOG | glFog () |
FRONTFACE | osg :: FrontFace | - | glFrontFace () |
Ringan | osg :: Cahaya | GL_LIGHTi (i dari 1 hingga 7) | glLight () |
LIGHTMODEL | osg :: LightModel | - | glLightModel () |
LINESTRIPPLE | osg :: LineStripple | GL_LINE_STRIPPLE | glLineStripple () |
LINEWIDTH | osg :: LineWidth | - | glLineWidht () |
LOGICOP | osg :: LogicOp | GL_COLOR_LOGIC_OP | glLogicOp () |
BAHAN | osg :: Bahan | - | glMaterial () dan glColorMaterial () |
TITIK | osg :: Poin | GL_POINT_SMOOTH | glPointParameter () |
POINTSPRITE | osg :: PointSprite | GL_POINT_SPRITE_ARB | Fungsi untuk bekerja dengan sprite OpenGL |
POLYGONMODE | osg :: PolygonMode | - | glPolygonMode () |
POLYGONOFFSET | osg :: PolygonOffset | GL_POLYGON_OFFSET_POINT | glPolygonOffset () |
POLYGONSTRIPPLE | osg :: PolygonStripple | GL_POLYGON_STRIPPLE | glPolygonStripple () |
GUNTING | osg :: Gunting | GL_SCISSOR_TEST | glScissor () |
SHADEMODEL | osg :: ShadeModel | - | glShadeModel () |
STENCIL | osg :: Stensil | GL_STENCIL_TEST | glStencilFunc (), glStencilOp () dan glStencilMask () |
Texenv | osg :: TexEnv | - | glTexEnv () |
Texgen | osg :: TexGen | GL_TEXTURE_GEN_S | glTexGen () |
Kolom ID tipe atribut menunjukkan pengidentifikasi OSG spesifik yang mengidentifikasi atribut ini di enumerator dari osg :: StateAttribute class. Ini dapat digunakan dalam metode getAttribute untuk mendapatkan nilai atribut tertentu.
osg::PolygonMode *pm = dynamic_cast<osg::PolygonMode *>(stateset->getAttribute(osg::StateAttribute::POLYGONMODE));
Pointer yang valid menunjukkan bahwa atribut telah ditetapkan sebelumnya. Jika tidak, metode ini akan berbohong NULL. Anda juga bisa mendapatkan nilai mode saat ini menggunakan panggilan
osg::StateAttribute::GLModeValue value = stateset->getMode(GL_LIGHTING);
Di sini enumerator GL_LIGHTING digunakan untuk mengaktifkan / menonaktifkan pencahayaan di seluruh adegan.
5. Menerapkan kabut ke model di tempat kejadian
Mari kita kutip efek kabut sebagai cara ideal untuk menunjukkan cara bekerja dengan berbagai atribut dan mode rendering. OpenGL menggunakan satu persamaan linear dan dua eksponensial yang menggambarkan model kabut, didukung oleh kelas osg :: Fog.
Contoh kabut teksmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Fog> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Fog> fog = new osg::Fog; fog->setMode(osg::Fog::LINEAR); fog->setStart(500.0f); fog->setEnd(2500.0f); fog->setColor(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/lz.osg"); model->getOrCreateStateSet()->setAttributeAndModes(fog.get()); osgViewer::Viewer viewer; viewer.setSceneData(model.get()); return viewer.run(); }
Pertama, buat atribut kabut. Kami menggunakan model linier, sesuaikan rentang tampilan kabut dengan jarak ke model
osg::ref_ptr<osg::Fog> fog = new osg::Fog; fog->setMode(osg::Fog::LINEAR); fog->setStart(500.0f); fog->setEnd(2500.0f); fog->setColor(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));
Kami memuat sampel lansekap lz.osg dan menerapkan atribut ini padanya
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/lz.osg"); model->getOrCreateStateSet()->setAttributeAndModes(fog.get());
Di jendela penampil, kita melihat lanskap buram, dan kita bisa melihat bagaimana kepadatan kabut berubah tergantung pada jarak ke model



6. Bekerja dengan sumber cahaya dan pencahayaan
Seperti halnya OpenGL, OSG mendukung hingga delapan sumber cahaya untuk secara langsung bertemu objek pemandangan. Seperti OpenGL, OSG tidak secara otomatis menghitung bayangan. Sinar cahaya datang dari sumber-sumber dalam garis lurus, dipantulkan dari objek dan disebarkan oleh mereka, setelah itu mereka dilihat oleh mata penonton. Untuk pemrosesan pencahayaan berkualitas tinggi, perlu mengatur sifat material, geometri objek normal, dll.
Osg :: Light class menyediakan metode untuk mengendalikan sumber cahaya, termasuk: setLightNum () dan getLightNum () - untuk bekerja dengan jumlah sumber; setAmbient () dan getAmbient () untuk mengontrol komponen di sekitarnya; setDiffuse () dan getDiffuse () - untuk bekerja dengan komponen yang tersebar, dll.
OSG juga menjelaskan kelas osg :: LightSource untuk menambahkan sumber cahaya ke TKP. Ini memberikan metode setLight () dan merupakan simpul daun dari grafik adegan dengan atribut tunggal. Semua node lain dari grafik adegan dipengaruhi oleh sumber cahaya jika mode yang sesuai untuk GL_LIGHTi diatur. Sebagai contoh:
Solusi lain yang lebih nyaman adalah metode setStateSetModes (), dengan mana sumber cahaya dengan nomor yang diinginkan secara otomatis dilampirkan ke node root
root->addChild( lightSource.get() ); lightSource->setStateSetModes( root->getOrCreateStateSet(), osg::StateAttribute::ON );
Anda dapat menambahkan node anak ke sumber cahaya, tetapi ini tidak berarti sama sekali, Anda akan menerangi subgraph yang terkait dengannya entah bagaimana dengan cara yang khusus. Ini akan diproses sebagai geometri, diwakili oleh bentuk fisik sumber cahaya.
Osg :: LightSource node dapat dilampirkan ke node transformasi, dan, misalnya, sumber cahaya titik dapat dipindahkan di ruang angkasa. Ini dapat dinonaktifkan dengan mengatur sistem koordinat absolut untuk sumber cahaya.
lightSource->setReferenceFrame( osg::LightSource::ABSOLUTE_RF );
7. Membuat sumber cahaya di tempat kejadian
Secara default, OSG secara otomatis mengatur sumber cahaya ke angka 0, yang memancarkan cahaya arah seragam ke tempat kejadian. Namun, kapan saja Anda dapat menambahkan beberapa sumber cahaya tambahan, dan bahkan mengendalikannya menggunakan node transformasi koordinat. Hanya sumber posisi (sumber titik) yang dapat dipindahkan. Cahaya directional hanya memiliki arah (aliran sinar paralel yang datang dari tak terbatas) dan tidak terikat pada posisi tertentu di atas panggung. OpenGL dan OSG menggunakan komponen keempat dari parameter posisi untuk menentukan jenis sumber cahaya. Jika 0, maka cahaya dianggap sebagai diarahkan; dengan nilai 1 - posisi.
Pertimbangkan contoh kecil bekerja dengan pencahayaan.
Judul spoilermain.h #ifndef MAIN_H #define MAIN_H #include <osg/MatrixTransform> #include <osg/LightSource> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Untuk membuat sumber cahaya, kami memiliki fungsi terpisah
osg::Node *createLightSource(int num, const osg::Vec3 &trans, const osg::Vec4 &color) { osg::ref_ptr<osg::Light> light = new osg::Light; light->setLightNum(num); light->setDiffuse(color); light->setPosition(osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource; lightSource->setLight(light); osg::ref_ptr<osg::MatrixTransform> sourceTrans = new osg::MatrixTransform; sourceTrans->setMatrix(osg::Matrix::translate(trans)); sourceTrans->addChild(lightSource.get()); return sourceTrans.release(); }
Dalam fungsi ini, pertama-tama kita menentukan parameter pencahayaan yang diberikan oleh sumber, sehingga menciptakan atribut GL_LIGHTi
osg::ref_ptr<osg::Light> light = new osg::Light;
Setelah itu, sumber cahaya dibuat dimana atribut ini diberikan.
osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource; lightSource->setLight(light);
Kami membuat dan mengkonfigurasi node transformasi, melewati sumber cahaya kami sebagai node anak
osg::ref_ptr<osg::MatrixTransform> sourceTrans = new osg::MatrixTransform; sourceTrans->setMatrix(osg::Matrix::translate(trans)); sourceTrans->addChild(lightSource.get());
Kembalikan pointer ke node transformasi
return sourceTrans.release();
Dalam tubuh program utama, kami memuat model tiga dimensi (sekali lagi, cessna favorit kami)
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(model.get());
Kami membuat dua sumber cahaya dengan angka 0 dan 1. Yang pertama akan bersinar kuning, yang kedua - biru-hijau
osg::Node *light0 = createLightSource(0, osg::Vec3(-20.0f, 0.0f, 0.0f), osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)); osg::Node *light1 = createLightSource(1, osg::Vec3(0.0f, -20.0f, 0.0f), osg::Vec4(0.0f, 1.0f, 1.0f, 1.0f));
Kami memberi tahu mesin status OpenGL bahwa perlu menyalakan 0 dan 1 sumber cahaya dan menambahkan sumber yang kami buat ke tempat kejadian
root->getOrCreateStateSet()->setMode(GL_LIGHT0, osg::StateAttribute::ON); root->getOrCreateStateSet()->setMode(GL_LIGHT1, osg::StateAttribute::ON); root->addChild(light0); root->addChild(light1);
Setelah menginisialisasi dan memulai penampil, kami mendapatkan gambar

Kesimpulan
Sangat tersentuh oleh perhatian orang-orang yang tertarik pada siklus ini. Tindakan ini tidak dimulai dengan banyak, tetapi saya merasa bahwa masyarakat membutuhkan artikel. Terima kasih atas semua jenis umpan balik positif.
Hari ini saya mencoba mempertimbangkan, sekali lagi, hal-hal yang agak mendasar dari mesin OSG. Tidak yakin apa yang keluar keren. Tetapi sejauh ini saya menetapkan hal-hal yang sangat primitif, di mana saya sendiri memahaminya. Saya memeriksa semua contoh secara pribadi, repositori saya tersedia di
sini . Terima kasih, kawan-kawan terkasih, saya akan berusaha menjaga agar kisah ini
terus berlanjut ...