
Pendahuluan
Artikel ini tidak akan terlalu fokus pada grafik seperti bagaimana aplikasi menggunakannya harus diatur, dengan mempertimbangkan spesifikasi mesin OpenSceneGraph dan perangkat lunak yang disediakannya.
Bukan rahasia lagi bahwa kunci keberhasilan setiap produk perangkat lunak adalah arsitektur yang dirancang dengan baik yang menyediakan kemampuan untuk mempertahankan dan memperluas kode tertulis. Dalam hal ini, mesin yang kami pertimbangkan berada pada level yang cukup tinggi, menyediakan pengembang dengan toolkit yang sangat luas, menyediakan konstruksi arsitektur modular yang fleksibel.
Artikel ini cukup panjang dan mencakup tinjauan berbagai alat dan teknik (pola desain, jika Anda mau) yang disediakan oleh mesin pengembang. Semua bagian artikel diberikan dengan contoh, kode yang dapat diambil di
repositori saya .
1. Opsi baris perintah parsing
Dalam C / C ++, parameter baris perintah dilewatkan melalui argumen ke fungsi main (). Dalam contoh sebelumnya, kami dengan hati-hati menandai parameter ini sebagai tidak terpakai, sekarang kami akan menggunakannya untuk memberi tahu kami beberapa data saat program dimulai.
OSG memiliki alat penguraian baris perintah bawaan.
Buat contoh berikut
Contoh baris perintahmain.h#ifndef MAIN_H #define MAIN_H #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif // MAIN_H
main.cpp #include "main.h" int main(int argc, char *argv[]) { osg::ArgumentParser args(&argc, argv); std::string filename; args.read("--model", filename); osg::ref_ptr<osg::Node> root = osgDB::readNodeFile(filename); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Atur parameter startup program di QtCreator

Menjalankan program untuk dieksekusi, kami mendapatkan hasilnya (model truk
diambil dari OpenSceneGraph-Data yang sama )

Sekarang mari kita lihat contoh baris demi baris
osg::ArgumentParser args(&argc, argv);
membuat turunan dari kelas parser baris perintah osg :: ArgumentParser. Ketika dibuat, konstruktor kelas melewati argumen yang diterima oleh fungsi utama () dari sistem operasi.
std::string filename; args.read("--model", filename);
kami menganalisis argumen, yaitu, kami mencari kunci "βmodel" di antara mereka, menempatkan nilainya dalam nama file string. Dengan demikian, menggunakan kunci ini, kami mentransfer nama file dengan model tiga dimensi ke program. Selanjutnya kita memuat model ini dan menampilkannya
osg::ref_ptr<osg::Node> root = osgDB::readNodeFile(filename); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run();
Metode read () pada kelas osg :: ArgumentParser memiliki banyak kelebihan, yang memungkinkan Anda membaca tidak hanya nilai string dari baris perintah, tetapi juga bilangan bulat, angka titik mengambang, vektor, dll. Misalnya, Anda dapat membaca parameter tipe float tertentu
float size = 0.0f; args.read("--size", size);
Jika parameter ini tidak ditampilkan pada baris perintah, maka nilainya akan tetap seperti setelah variabel ukuran diinisialisasi.
2. Mekanisme pemberitahuan dan pencatatan
OpenSceneGraph memiliki mekanisme pemberitahuan yang memungkinkan Anda untuk menampilkan pesan debug selama proses rendering, serta diprakarsai oleh pengembang. Ini sangat membantu ketika melacak dan men-debug program. Sistem pemberitahuan OSG mendukung keluaran informasi diagnostik (kesalahan, peringatan, pemberitahuan) di tingkat inti mesin dan plug-in-nya. Pengembang dapat menampilkan pesan diagnostik selama operasi program menggunakan fungsi osg :: notify ().
Fungsi ini berfungsi sebagai aliran keluaran standar dari pustaka C ++ standar melalui overloading operator <<. Dibutuhkan tingkat pesan sebagai argumen: SELALU, FATAL, PERINGATAN, PEMBERITAHUAN, INFO, DEBUG_INFO, dan DEBUG_FP. Sebagai contoh
osg::notify(osg::WARN) << "Some warning message" << std::endl;
menampilkan peringatan dengan teks yang ditentukan pengguna.
Pemberitahuan OSG dapat berisi informasi penting tentang status program, ekstensi subsistem grafis komputer, kemungkinan masalah dengan mesin.
Dalam beberapa kasus, ini diperlukan untuk menampilkan data ini bukan ke konsol, tetapi untuk dapat mengarahkan output ini ke file (dalam bentuk log) atau ke antarmuka lain, termasuk widget grafis. Mesin berisi osg kelas khusus :: NotifyHandler yang menyediakan pengalihan notifikasi ke aliran output yang dibutuhkan pengembang.
Menggunakan contoh sederhana, pertimbangkan bagaimana Anda dapat mengarahkan output pemberitahuan, katakanlah, ke file log teks. Tulis kode berikut
Beri tahu contohmain.h #ifndef MAIN_H #define MAIN_H #include <osgDB/ReadFile> #include <osgViewer/Viewer> #include <fstream> #endif // MAIN_H
main.cpp #include "main.h" class LogFileHandler : public osg::NotifyHandler { public: LogFileHandler(const std::string &file) { _log.open(file.c_str()); } virtual ~LogFileHandler() { _log.close(); } virtual void notify(osg::NotifySeverity severity, const char *msg) { _log << msg; } protected: std::ofstream _log; }; int main(int argc, char *argv[]) { osg::setNotifyLevel(osg::INFO); osg::setNotifyHandler(new LogFileHandler("../logs/log.txt")); osg::ArgumentParser args(&argc, argv); osg::ref_ptr<osg::Node> root = osgDB::readNodeFiles(args); if (!root) { OSG_FATAL << args.getApplicationName() << ": No data loaded." << std::endl; return -1; } osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Untuk mengarahkan kembali output, kita menulis kelas LogFileHandler, yang merupakan penerus dari osg :: NotifyHandler. Konstruktor dan destruktor dari kelas ini mengontrol pembukaan dan penutupan aliran keluaran _log yang dikaitkan dengan file teks. Metode notify () adalah metode kelas dasar yang serupa yang kami definisikan ulang untuk menghasilkan pemberitahuan file yang dikirim oleh OSG selama operasi melalui parameter msg.
Kelas LogFileHandler class LogFileHandler : public osg::NotifyHandler { public: LogFileHandler(const std::string &file) { _log.open(file.c_str()); } virtual ~LogFileHandler() { _log.close(); } virtual void notify(osg::NotifySeverity severity, const char *msg) { _log << msg; } protected: std::ofstream _log; };
Selanjutnya, di program utama, lakukan pengaturan yang diperlukan
osg::setNotifyLevel(osg::INFO);
atur tingkat pemberitahuan INFO, yaitu, output ke log semua informasi tentang operasi mesin, termasuk pemberitahuan saat ini tentang operasi normal.
osg::setNotifyHandler(new LogFileHandler("../logs/log.txt"));
pasang handler notifikasi. Selanjutnya, kami memproses argumen baris perintah di mana jalur ke model yang dimuat dilewatkan
osg::ArgumentParser args(&argc, argv); osg::ref_ptr<osg::Node> root = osgDB::readNodeFiles(args); if (!root) { OSG_FATAL << args.getApplicationName() << ": No data loaded." << std::endl; return -1; }
Pada saat yang sama, kami menangani situasi kurangnya data pada baris perintah, menampilkan pesan dalam mode manual masuk menggunakan makro OSG_FATAL. Jalankan program dengan argumen berikut

mendapatkan output ke file log seperti ini
Contoh Log OSG Opened DynamicLibrary osgPlugins-3.7.0/mingw_osgdb_osgd.dll CullSettings::readEnvironmentalVariables() CullSettings::readEnvironmentalVariables() Opened DynamicLibrary osgPlugins-3.7.0/mingw_osgdb_deprecated_osgd.dll OSGReaderWriter wrappers loaded OK CullSettings::readEnvironmentalVariables() void StateSet::setGlobalDefaults() void StateSet::setGlobalDefaults() ShaderPipeline disabled. StateSet::setGlobalDefaults() Setting up GL2 compatible shaders CullSettings::readEnvironmentalVariables() CullSettings::readEnvironmentalVariables() CullSettings::readEnvironmentalVariables() CullSettings::readEnvironmentalVariables() ShaderComposer::ShaderComposer() 0xa5ce8f0 CullSettings::readEnvironmentalVariables() ShaderComposer::ShaderComposer() 0xa5ce330 View::setSceneData() Reusing existing scene0xa514220 CameraManipulator::computeHomePosition(0, 0) boundingSphere.center() = (-6.40034 1.96225 0.000795364) boundingSphere.radius() = 16.6002 CameraManipulator::computeHomePosition(0xa52f138, 0) boundingSphere.center() = (-6.40034 1.96225 0.000795364) boundingSphere.radius() = 16.6002 Viewer::realize() - No valid contexts found, setting up view across all screens. Applying osgViewer::ViewConfig : AcrossAllScreens . . . . ShaderComposer::~ShaderComposer() 0xa5ce330 ShaderComposer::~ShaderComposer() 0xa5ce8f0 ShaderComposer::~ShaderComposer() 0xa5d6228 close(0x1)0xa5d3e50 close(0)0xa5d3e50 ContextData::unregisterGraphicsContext 0xa5d3e50 DatabasePager::RequestQueue::~RequestQueue() Destructing queue. DatabasePager::RequestQueue::~RequestQueue() Destructing queue. DatabasePager::RequestQueue::~RequestQueue() Destructing queue. DatabasePager::RequestQueue::~RequestQueue() Destructing queue. ShaderComposer::~ShaderComposer() 0xa5de4e0 close(0x1)0xa5ddba0 close(0)0xa5ddba0 ContextData::unregisterGraphicsContext 0xa5ddba0 Done destructing osg::View DatabasePager::RequestQueue::~RequestQueue() Destructing queue. DatabasePager::RequestQueue::~RequestQueue() Destructing queue. DatabasePager::RequestQueue::~RequestQueue() Destructing queue. DatabasePager::RequestQueue::~RequestQueue() Destructing queue. Closing DynamicLibrary osgPlugins-3.7.0/mingw_osgdb_osgd.dll Closing DynamicLibrary osgPlugins-3.7.0/mingw_osgdb_deprecated_osgd.dll
Tidak masalah bahwa saat ini informasi ini mungkin tidak berguna bagi Anda - di masa depan, kesimpulan seperti itu dapat membantu men-debug kesalahan dalam program Anda.
Secara default, OSG mengirim pesan ke std :: cout keluaran standar dan pesan kesalahan ke std :: cerr stream. Namun, dengan mengabaikan penangan notifikasi, seperti yang ditunjukkan dalam contoh, output ini dapat dialihkan ke aliran output apa pun, termasuk elemen GUI.
Ingatlah bahwa ketika mengatur pemberitahuan tingkat tinggi (misalnya, FATAL), sistem mengabaikan semua pemberitahuan dari tingkat yang lebih rendah. Misalnya, dalam kasus serupa
osg::setNotifyLevel(osg::FATAL); . . . osg::notify(osg::WARN) << "Some message." << std::endl;
pesan khusus tidak akan ditampilkan.
3. Intersepsi atribut geometris
Kelas osg :: Geometry mengelola seperangkat data yang menggambarkan simpul dan menampilkan mesh poligon menggunakan seperangkat primitif yang dipesan. Namun, kelas ini tidak memiliki gagasan tentang elemen topologi model seperti wajah, tepi dan hubungan di antara mereka. Nuansa ini mencegah implementasi hal-hal seperti menggerakkan wajah-wajah tertentu, misalnya saat menganimasikan model. OSG saat ini tidak mendukung fungsi ini.
Namun, mesin mengimplementasikan sejumlah fungsi yang memungkinkan Anda membaca kembali atribut geometri objek apa pun dan menggunakannya untuk memodelkan topologi mesh poligonal. Dalam C ++, functor adalah sebuah konstruk yang memungkinkan Anda untuk menggunakan objek sebagai fungsi.
Osg :: Kelas Drawable menyediakan pengembang dengan empat jenis fungsi:
- osg :: Drawable :: AttributeFunctor - membaca atribut simpul sebagai array dari pointer. Ini memiliki sejumlah metode virtual untuk menerapkan atribut vertex dari berbagai tipe data. Untuk menggunakan functor ini, Anda harus mendeskripsikan kelas dan menimpa satu atau lebih metode, di mana tindakan yang diperlukan oleh pengembang dilakukan
virtual void apply( osg::Drawable::AttributeType type, unsigned int size, osg::Vec3* ptr ) {
- osg :: Drawable :: ConstAttributeFunctor - versi read-only dari functor sebelumnya: pointer ke array vektor dilewatkan sebagai parameter konstan
- osg :: PrimitiveFunctor - meniru proses rendering objek OpenGL. Di bawah kedok rendering objek, metode functor yang ditimpa oleh pengembang disebut. Functor ini memiliki dua subkelas templat penting: osg :: TemplatePrimitiveFunctor <> dan osg :: TriangleFunctor <>. Kelas-kelas ini menerima simpul primitif sebagai parameter dan meneruskannya ke metode pengguna menggunakan operator () operator.
- osg :: PrimitiveIndexFunctor - melakukan tindakan yang sama dengan functor sebelumnya, tetapi menerima indeks titik primitif sebagai parameter.
Kelas-kelas yang diturunkan dari osg :: Drawable, seperti osg :: ShapeDrawable dan osg :: Geometry, memiliki metode accept () untuk menerapkan berbagai fungsi.
4. Contoh penggunaan functor primitif
Kami menggambarkan fungsionalitas yang dijelaskan menggunakan contoh pengumpulan informasi tentang wajah segitiga dan titik-titik beberapa geometri yang sebelumnya kami tentukan.
Contoh fungsimain.h #ifndef MAIN_H #define MAIN_H #include <osg/Geode> #include <osg/Geometry> #include <osg/TriangleFunctor> #include <osgViewer/Viewer> #include <iostream> #endif
main.cpp #include "main.h" std::string vec2str(const osg::Vec3 &v) { std::string tmp = std::to_string(vx()); tmp += " "; tmp += std::to_string(vy()); tmp += " "; tmp += std::to_string(vz()); return tmp; } struct FaceCollector { void operator()(const osg::Vec3 &v1, const osg::Vec3 &v2, const osg::Vec3 &v3) { std::cout << "Face vertices: " << vec2str(v1) << "; " << vec2str(v2) << "; " << vec2str(v3) << std::endl; } }; int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( osg::Vec3(0.0f, 0.0f, 0.0f) ); vertices->push_back( osg::Vec3(0.0f, 0.0f, 1.0f) ); vertices->push_back( osg::Vec3(1.0f, 0.0f, 0.0f) ); vertices->push_back( osg::Vec3(1.0f, 0.0f, 1.5f) ); vertices->push_back( osg::Vec3(2.0f, 0.0f, 0.0f) ); vertices->push_back( osg::Vec3(2.0f, 0.0f, 1.0f) ); vertices->push_back( osg::Vec3(3.0f, 0.0f, 0.0f) ); vertices->push_back( osg::Vec3(3.0f, 0.0f, 1.5f) ); vertices->push_back( osg::Vec3(4.0f, 0.0f, 0.0f) ); vertices->push_back( osg::Vec3(4.0f, 0.0f, 1.0f) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back( osg::Vec3(0.0f, -1.0f, 0.0f) ); osg::ref_ptr<osg::Geometry> geom = new osg::Geometry; geom->setVertexArray(vertices.get()); geom->setNormalArray(normals.get()); geom->setNormalBinding(osg::Geometry::BIND_OVERALL); geom->addPrimitiveSet(new osg::DrawArrays(GL_QUAD_STRIP, 0, 10)); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(geom.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); osg::TriangleFunctor<FaceCollector> functor; geom->accept(functor); return viewer.run(); }
Menghilangkan proses pembuatan geometri yang kami pertimbangkan berkali-kali, mari kita perhatikan yang berikut ini. Kami mendefinisikan struktur FaceCollector yang kami mendefinisikan ulang operator () sebagai berikut
struct FaceCollector { void operator()(const osg::Vec3 &v1, const osg::Vec3 &v2, const osg::Vec3 &v3) { std::cout << "Face vertices: " << vec2str(v1) << "; " << vec2str(v2) << "; " << vec2str(v3) << std::endl; } };
Operator ini, ketika dipanggil, akan menampilkan koordinat dari tiga simpul yang dikirimkan kepadanya oleh mesin. Fungsi vec2str diperlukan untuk menerjemahkan komponen-komponen dari vektor osg :: Vec3 ke std :: string. Untuk memanggil functor, buat instance dan berikan ke objek geometri melalui metode accept ()
osg::TriangleFunctor<FaceCollector> functor; geom->accept(functor);
Panggilan ini, seperti yang disebutkan di atas, meniru rendering geometri, mengganti gambar itu sendiri dengan memanggil metode functor yang ditimpa. Dalam hal ini, itu akan dipanggil selama "menggambar" dari masing-masing segitiga yang membentuk geometri contoh.
Di layar kita mendapatkan geometri seperti itu

dan seperti knalpot ke konsol
Face vertices: 0.000000 0.000000 0.000000; 0.000000 0.000000 1.000000; 1.000000 0.000000 0.000000 Face vertices: 0.000000 0.000000 1.000000; 1.000000 0.000000 1.500000; 1.000000 0.000000 0.000000 Face vertices: 1.000000 0.000000 0.000000; 1.000000 0.000000 1.500000; 2.000000 0.000000 0.000000 Face vertices: 1.000000 0.000000 1.500000; 2.000000 0.000000 1.000000; 2.000000 0.000000 0.000000 Face vertices: 2.000000 0.000000 0.000000; 2.000000 0.000000 1.000000; 3.000000 0.000000 0.000000 Face vertices: 2.000000 0.000000 1.000000; 3.000000 0.000000 1.500000; 3.000000 0.000000 0.000000 Face vertices: 3.000000 0.000000 0.000000; 3.000000 0.000000 1.500000; 4.000000 0.000000 0.000000 Face vertices: 3.000000 0.000000 1.500000; 4.000000 0.000000 1.000000; 4.000000 0.000000 0.000000
Bahkan, ketika memanggil geom-> accept (...), segitiga tidak dirender, panggilan OpenGL disimulasikan, dan alih-alih data tentang simpul segitiga, rendering yang disimulasikan

Kelas osg :: TemplatePrimitiveFunctor mengumpulkan data tidak hanya tentang segitiga, tetapi juga tentang primitif OpenGL lainnya. Untuk menerapkan pemrosesan data ini, Anda harus mengganti operator berikut dalam argumen templat
5. Pola Pengunjung
Pola pengunjung digunakan untuk mengakses operasi untuk mengubah elemen grafik adegan tanpa mengubah kelas elemen ini. Kelas pengunjung mengimplementasikan semua fungsi virtual yang relevan untuk menerapkannya ke berbagai jenis elemen melalui mekanisme pengiriman ganda. Dengan menggunakan mekanisme ini, pengembang dapat membuat instance pengunjungnya sendiri dengan mengimplementasikan fungsionalitas yang dibutuhkannya dengan bantuan operator khusus dan mengikat pengunjung ke berbagai jenis elemen grafik adegan dengan cepat, tanpa mengubah fungsionalitas elemen itu sendiri. Ini adalah cara yang bagus untuk memperluas fungsionalitas suatu elemen tanpa mendefinisikan subclass dari elemen-elemen ini.
Untuk mengimplementasikan mekanisme ini dalam OSG, kelas osg :: NodeVisitor didefinisikan. Kelas yang diwarisi dari osg :: NodeVisitor bergerak di sekitar grafik adegan, mengunjungi setiap node dan menerapkan operasi yang ditentukan oleh pengembang. Ini adalah kelas utama yang digunakan untuk campur tangan dalam proses memperbarui node dan kliping node tak terlihat, serta menerapkan beberapa operasi lain yang terkait dengan memodifikasi geometri adegan adegan, seperti osgUtil :: SmoothingVisitor, osgUtil :: Simplifier dan osgUtil :: TriStripVisitor.
Untuk mensubclass pengunjung, kita harus mengganti satu atau lebih metode apply () virtual berlebih yang disediakan oleh kelas dasar osg :: NodeVisitor. Sebagian besar tipe simpul OSG utama memiliki metode ini. Pengunjung akan secara otomatis memanggil metode apply () untuk setiap node yang dikunjungi ketika melintasi grafik adegan adegan. Pengembang mengabaikan metode apply () untuk setiap jenis simpul yang ia butuhkan.
Dalam implementasi metode apply (), pengembang, pada saat yang tepat, harus memanggil metode traverse () dari kelas dasar osg :: NodeVisitor. Ini memulai transisi pengunjung ke simpul berikutnya, baik anak atau tetangga di tingkat hierarki, jika simpul saat ini tidak memiliki simpul anak yang dapat dibuat transisi. Tidak adanya panggilan untuk melintasi () berarti menghentikan traversal grafik adegan dan sisa grafik adegan diabaikan.
Kelebihan metode apply () memiliki format terpadu
virtual void apply( osg::Node& ); virtual void apply( osg::Geode& ); virtual void apply( osg::Group& ); virtual void apply( osg::Transform& );
Untuk memotong subgraph dari node saat ini untuk objek pengunjung, Anda harus mengatur mode crawl, misalnya,
ExampleVisitor visitor; visitor->setTraversalMode( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ); node->accept( visitor );
Mode Bypass diatur oleh beberapa enumerator
- TRAVERSE_ALL_CHILDREN - bergerak melalui semua node anak.
- TRAVERSE_PARENTS - kembali dari simpul saat ini, tidak mencapai simpul akar
- TRAVERSE_ACTIVE_CHILDREN - memotong node yang aktif secara eksklusif, yaitu mereka yang visibilitasnya diaktifkan melalui osg :: Switch node.
6. Analisis struktur cessna yang terbakar
Pengembang selalu dapat menganalisis bagian grafik adegan yang dihasilkan oleh model yang diambil dari file.
Contoh fungsimain.h #ifndef MAIN_H #define MAIN_H #include <osgDB/ReadFile> #include <osgViewer/Viewer> #include <iostream> #endif
main.cpp #include "main.h"
Kami membuat kelas InfoVisitor, mewarisinya dari osg :: NodeVisitor
class InfoVisitor : public osg::NodeVisitor { public: InfoVisitor() : _level(0) { setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN); } std::string spaces() { return std::string(_level * 2, ' '); } virtual void apply(osg::Node &node); virtual void apply(osg::Geode &geode); protected: unsigned int _level; };
Properti _level yang dilindungi akan menunjuk ke tingkat grafik adegan di mana kelas pengunjung kami saat ini berada. Di konstruktor, inisialisasi penghitung level dan atur mode node traversal - untuk mem-bypass semua node anak.
Sekarang mendefinisikan kembali metode apply () untuk node
void InfoVisitor::apply(osg::Node &node) { std::cout << spaces() << node.libraryName() << "::" << node.className() << std::endl; _level++; traverse(node); _level--; }
Di sini kita akan menampilkan tipe node saat ini. Metode libraryName () untuk node menampilkan nama perpustakaan OSG di mana node ini diterapkan, dan metode className menampilkan nama kelas node. Metode ini diimplementasikan melalui penggunaan makro dalam kode pustaka OSG.
std::cout << spaces() << node.libraryName() << "::" << node.className() << std::endl;
Setelah itu, kami meningkatkan penghitung tingkat grafik dan memanggil metode traverse (), memulai transisi ke tingkat yang lebih tinggi, ke simpul anak. Setelah kembali dari traverse (), kami kembali mengurangi nilai penghitung. Sangat mudah untuk menebak bahwa traverse () memulai panggilan berulang ke metode apply (), traverse berulang () sudah untuk subgraph mulai dari node saat ini. Kami mendapatkan eksekusi pengunjung rekursif sampai kami mencapai titik akhir dari grafik adegan.
Untuk simpul akhir dari tipe osg :: Geode, kelebihannya dari metode apply () diganti
void InfoVisitor::apply(osg::Geode &geode) { std::cout << spaces() << geode.libraryName() << "::" << geode.className() << std::endl; _level++; for (unsigned int i = 0; i < geode.getNumDrawables(); ++i) { osg::Drawable *drawable = geode.getDrawable(i); std::cout << spaces() << drawable->libraryName() << "::" << drawable->className() << std::endl; } traverse(geode); _level--; }
dengan kode kerja yang sama, kecuali bahwa kami menampilkan data pada semua objek geometrik yang melekat pada simpul geometrik saat ini
for (unsigned int i = 0; i < geode.getNumDrawables(); ++i) { osg::Drawable *drawable = geode.getDrawable(i); std::cout << spaces() << drawable->libraryName() << "::" << drawable->className() << std::endl; }
Dalam fungsi utama (), kami memproses argumen baris perintah di mana kami melewati daftar model yang dimuat ke dalam adegan dan membentuk adegan
osg::ArgumentParser args(&argc, argv); osg::ref_ptr<osg::Node> root = osgDB::readNodeFiles(args); if (!root.valid()) { OSG_FATAL << args.getApplicationName() << ": No data leaded. " << std::endl; return -1; }
Pada saat yang sama, kami memproses kesalahan terkait dengan tidak adanya nama file model pada baris perintah. Sekarang kita membuat kelas pengunjung dan meneruskannya ke grafik adegan untuk dieksekusi
InfoVisitor infoVisitor; root->accept(infoVisitor);
Berikutnya adalah langkah-langkah untuk meluncurkan penampil, yang telah kami lakukan berkali-kali. Setelah memulai program dengan parameter
$ visitor ../data/cessnafire.osg
kita akan melihat output berikut ke konsol
osg::Group osg::MatrixTransform osg::Geode osg::Geometry osg::Geometry osg::MatrixTransform osgParticle::ModularEmitter osgParticle::ModularEmitter osgParticle::ParticleSystemUpdater osg::Geode osgParticle::ParticleSystem osgParticle::ParticleSystem osgParticle::ParticleSystem osgParticle::ParticleSystem
Bahkan, kami mendapat pohon lengkap dari adegan yang dimuat. Maaf, di mana ada begitu banyak node? Semuanya sangat sederhana - model format * .osg sendiri adalah wadah yang menyimpan tidak hanya geometri model, tetapi juga informasi lain tentang strukturnya dalam bentuk subgraf adegan OSG. Geometri model, transformasi, efek partikel yang menyadari asap dan api adalah semua simpul dari grafik adegan OSG.
Adegan apa pun dapat diunduh dari * .osg atau dibongkar dari pemirsa ke format * .osg.Ini adalah contoh sederhana menerapkan mekanisme pengunjung. Bahkan, di dalam pengunjung, Anda dapat melakukan banyak operasi untuk memodifikasi node ketika program sedang berjalan.7. Mengontrol perilaku node dalam grafik adegan dengan mengganti metode traverse ()
Cara penting untuk bekerja dengan OSG adalah mengganti metode traverse (). Metode ini disebut setiap kali bingkai digambar. Mereka menerima parameter tipe osg :: NodeVisitor & yang melaporkan bagian mana dari grafik adegan yang saat ini dilakukan (memperbarui, memproses acara, atau kliping). Sebagian besar host OSG mengganti metode ini untuk mengimplementasikan fungsionalitasnya.Harus diingat bahwa menimpa metode traverse () bisa berbahaya, karena memengaruhi proses melintasi grafik adegan dan dapat menyebabkan tampilan adegan yang salah. Ini juga merepotkan jika Anda ingin menambahkan fungsionalitas baru ke beberapa jenis node. Dalam hal ini, callback node digunakan, percakapan tentang yang akan sedikit lebih rendah., osg::Switch , . , , , .
animswitchmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Switch> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
. AnimatingSwitch, osg::Switch.
class AnimatingSwitch : public osg::Switch { public: AnimatingSwitch() : osg::Switch(), _count(0) {} AnimatingSwitch(const AnimatingSwitch ©, const osg::CopyOp ©op = osg::CopyOp::SHALLOW_COPY) : osg::Switch(copy, copyop), _count(copy._count) {} META_Node(osg, AnimatingSwitch); virtual void traverse(osg::NodeVisitor &nv); protected: unsigned int _count; }; void AnimatingSwitch::traverse(osg::NodeVisitor &nv) { if (!((++_count) % 60) ) { setValue(0, !getValue(0)); setValue(1, !getValue(1)); } osg::Switch::traverse(nv); }
-
AnimatingSwitch() : osg::Switch(), _count(0) {}
, OSG
AnimatingSwitch(const AnimatingSwitch ©, const osg::CopyOp ©op = osg::CopyOp::SHALLOW_COPY) : osg::Switch(copy, copyop), _count(copy._count) {}
: , osg::CopyOp, .
META_Node(osg, AnimatingSwitch);
Ini adalah makro yang membentuk struktur yang diperlukan untuk keturunan kelas yang berasal dari osg :: Node. Sampai kita mementingkan makro ini, penting bahwa itu harus ada ketika mewarisi dari osg :: Beralih ketika mendefinisikan semua kelas turunan. Kelas berisi field yang dilindungi _count - penghitung yang didasarkan pada mana kita beralih. Kami menerapkan pengalihan ketika mengganti metode traverse () void AnimatingSwitch::traverse(osg::NodeVisitor &nv) { if (!((++_count) % 60) ) { setValue(0, !getValue(0)); setValue(1, !getValue(1)); } osg::Switch::traverse(nv); }
Mengganti status tampilan node akan terjadi setiap kali nilai penghitung (menambah setiap panggilan metode) adalah kelipatan 60. Kami mengkompilasi contoh dan menjalankannyaKarena metode traverse () secara konstan didefinisikan ulang untuk berbagai jenis node, metode ini harus menyediakan mekanisme untuk memperoleh matriks transformasi dan membuat status untuk digunakan lebih lanjut oleh algoritma kelebihan beban mereka. Parameter input osg :: NodeVisitor adalah kunci untuk berbagai operasi dengan node. Secara khusus, ini menunjukkan jenis traversal grafik adegan saat ini, seperti memperbarui, memproses peristiwa, dan memotong wajah yang tidak terlihat. Dua yang pertama terkait dengan panggilan balik simpul dan akan dipertimbangkan saat mempelajari animasi.Passing klip dapat diidentifikasi dengan mengonversi objek osg :: NodeVisitor ke osg :: CullVisitor osgUtil::CullVisitor *cv = dynamic_cast<osgUtil::CullVisitor *>(&nv); if (cv) {
8. Mekanisme panggilan balik
, . , . , .
. , osg::NodeCallback , osg::Drawable::UpdateCallback, osg::Drawable::EventCallback osg::Drawable:CullCallback β , .
osg::NodeCallback operator(), . , , setUpdateCallback() addUpdateCallback(). operator() .
, OSG
| | | |
---|
| osg::NodeCallback | operator() | osg::Node::setUpdateCallback() |
| osg::NodeCallback | operator() | osg::Node::setEventCallback() |
| osg::NodeCallback | operator() | osg::Node::setCullCallback() |
| osg::Drawable::UpdateCallback | update() | osg::Drawable::setUpdateCallback() |
| osg::Drawable::EventCallback | event() | osg::Drawable::setEventCallback() |
| osg::Drawable::CullCallback | cull() | osg::Drawable::setCullCallback() |
| osg::StateAttributeCallback | operator() | osg::StateAttribute::setUpdateCallback() |
| osg::StateAttributeCallback | operator() | osg::StateAttribute::setEventCallback() |
| osg::Uniform::Callback | operator() | osg::Uniform::setUpdateCallback() |
| osg::Uniform::Callback | operator() | osg::Uniform::setEvevtCallback() |
| osg::Camera::DrawCallback | operator() | osg::Camera::PreDrawCallback() |
| osg::Camera::DrawCallback | operator() | osg::Camera::PostDrawCallback() |
9. osg::Switch
Sedikit lebih tinggi, kami menulis contoh dengan mengganti dua model pesawat. Sekarang kita akan mengulangi contoh ini, tetapi kita akan melakukan semuanya dengan benar menggunakan mekanisme panggilan balik OSG.Panggil kembali dengan contohmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Switch> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Anda harus membuat kelas yang diwarisi dari osg :: NodeCallback, yang mengontrol osg :: Switch node class SwitchingCallback : public osg::NodeCallback { public: SwitchingCallback() : _count(0) {} virtual void operator()(osg::Node *node, osg::NodeVisitor *nv); protected: unsigned int _count; };
Penghitung _count akan mengontrol peralihan osg :: Alihkan node dari memetakan satu simpul anak ke lainnya, tergantung pada nilainya. Di konstruktor, kami menginisialisasi penghitung, dan mendefinisikan kembali metode operator virtual () void SwitchingCallback::operator()(osg::Node *node, osg::NodeVisitor *nv) { osg::Switch *switchNode = static_cast<osg::Switch *>(node); if ( !((++_count) % 60) && switchNode ) { switchNode->setValue(0, !switchNode->getValue(0)); switchNode->setValue(1, !switchNode->getValue(0)); } traverse(node, nv); }
Node tempat panggilan berfungsi diteruskan ke parameter node. Karena kita tahu pasti bahwa ini akan menjadi simpul dari tipe osg :: Switch, kita melakukan cast statis dari pointer ke node ke pointer ke node switch osg::Switch *switchNode = static_cast<osg::Switch *>(node);
Kami akan mengganti node anak yang ditampilkan dengan nilai valid dari pointer ini, dan ketika nilai counter adalah kelipatan 60 if ( !((++_count) % 60) && switchNode ) { switchNode->setValue(0, !switchNode->getValue(0)); switchNode->setValue(1, !switchNode->getValue(0)); }
Jangan lupa untuk memanggil metode traverse () untuk melanjutkan traversal rekursif dari grafik adegan traverse(node, nv);
Sisa kode program adalah sepele, kecuali untuk baris root->setUpdateCallback( new SwitchingCallback );
tempat kami menetapkan panggilan balik yang kami buat ke simpul akar dari tipe osg :: Switch. Program bekerja mirip dengan contoh sebelumnyatraverse() : - osg::NodeVisitor, .
traverse(), . , traverse() .
addUpdateCallback(). setUpdateCallback() . .
Kesimpulan
Kami memeriksa teknik dasar yang digunakan dalam mengembangkan aplikasi menggunakan mesin grafis OpenSceneGraph. Namun, ini jauh dari semua poin yang ingin saya sentuh (terlepas dari kenyataan bahwa artikel tersebut ternyata cukup panjang), oleh karena ituDilanjutkan ...