├── .gitignore ├── README.md ├── src ├── VariantPtr.h ├── OsgCameraForm.h ├── MainWindow.h ├── OsgTreeView.h ├── osgtree.pro ├── OsgCameraForm.cpp ├── main.cpp ├── OsgTreeForm.h ├── OsgTreeView.cpp ├── MainWindow.cpp ├── OsgTreeForm.ui ├── OsgItemModel.h ├── OsgCameraForm.ui ├── Osg3dView.h ├── MainWindow.ui ├── OSGWidget.h ├── OsgTreeForm.cpp ├── Osg3dView.cpp ├── ViewingCore.h ├── OSGWidget.cpp ├── OsgItemModel.cpp └── ViewingCore.cpp └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | build* 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # osgtree 2 | OpenSceneGraph Qt application with Tree view of scenegraph 3 | -------------------------------------------------------------------------------- /src/VariantPtr.h: -------------------------------------------------------------------------------- 1 | #ifndef VARIANPTR_H 2 | #define VARIANPTR_H 3 | #include 4 | 5 | template class VariantPtr 6 | { 7 | 8 | public: 9 | 10 | static T* asPtr(QVariant v) 11 | { 12 | return (T *) v.value(); 13 | } 14 | 15 | static QVariant asQVariant(T* ptr) 16 | { 17 | return QVariant::fromValue((void *) ptr); 18 | } 19 | }; 20 | #endif // VARIANPTR_H 21 | -------------------------------------------------------------------------------- /src/OsgCameraForm.h: -------------------------------------------------------------------------------- 1 | #ifndef OSGCAMERAFORM_H 2 | #define OSGCAMERAFORM_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class OsgCameraForm; 8 | } 9 | 10 | class OsgCameraForm : public QWidget 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit OsgCameraForm(QWidget *parent = 0); 16 | ~OsgCameraForm(); 17 | public slots: 18 | void updateFromCamera(); 19 | 20 | private: 21 | Ui::OsgCameraForm *ui; 22 | }; 23 | 24 | #endif // OSGCAMERAFORM_H 25 | -------------------------------------------------------------------------------- /src/MainWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include "OsgItemModel.h" 6 | 7 | namespace Ui { 8 | class MainWindow; 9 | } 10 | 11 | class MainWindow : public QMainWindow 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit MainWindow(QWidget *parent = 0); 17 | ~MainWindow(); 18 | 19 | public slots: 20 | void on_actionFileOpen_triggered(); 21 | void on_actionFileSave_triggered(); 22 | void on_actionFileSaveAs_triggered(); 23 | 24 | private: 25 | Ui::MainWindow *ui; 26 | 27 | OsgItemModel m_itemModel; 28 | }; 29 | 30 | #endif // MAINWINDOW_H 31 | -------------------------------------------------------------------------------- /src/OsgTreeView.h: -------------------------------------------------------------------------------- 1 | #ifndef OSGTREEVIEW_H 2 | #define OSGTREEVIEW_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | class OsgTreeView : public QTreeView 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit OsgTreeView(QWidget *parent = 0); 15 | 16 | signals: 17 | void osgObjectActivated(osg::ref_ptr object); 18 | 19 | public slots: 20 | void resizeColumnsToFit(); 21 | void customMenuRequested(QPoint pos); 22 | private slots: 23 | void announceObject(const QModelIndex & index); 24 | 25 | private: 26 | QMenu popupMenu; 27 | }; 28 | 29 | #endif // OSGTREEVIEW_H 30 | -------------------------------------------------------------------------------- /src/osgtree.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2015-09-08T16:12:57 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = osgtree 12 | TEMPLATE = app 13 | LIBS += -losg -losgDB -losgUtil -losgViewer -losgGA 14 | 15 | SOURCES += main.cpp\ 16 | MainWindow.cpp \ 17 | OsgItemModel.cpp \ 18 | OsgTreeView.cpp \ 19 | OsgTreeForm.cpp \ 20 | ViewingCore.cpp \ 21 | Osg3dView.cpp \ 22 | OsgCameraForm.cpp 23 | 24 | HEADERS += MainWindow.h \ 25 | OsgItemModel.h \ 26 | OsgTreeView.h \ 27 | VariantPtr.h \ 28 | OsgTreeForm.h \ 29 | ViewingCore.h \ 30 | Osg3dView.h \ 31 | OsgCameraForm.h 32 | 33 | FORMS += MainWindow.ui \ 34 | OsgTreeForm.ui \ 35 | OsgCameraForm.ui 36 | -------------------------------------------------------------------------------- /src/OsgCameraForm.cpp: -------------------------------------------------------------------------------- 1 | #include "OsgCameraForm.h" 2 | #include "ui_OsgCameraForm.h" 3 | #include "Osg3dView.h" 4 | #include "ViewingCore.h" 5 | 6 | OsgCameraForm::OsgCameraForm(QWidget *parent) : 7 | QWidget(parent), 8 | ui(new Ui::OsgCameraForm) 9 | { 10 | ui->setupUi(this); 11 | } 12 | 13 | OsgCameraForm::~OsgCameraForm() 14 | { 15 | delete ui; 16 | } 17 | 18 | void OsgCameraForm::updateFromCamera() 19 | { 20 | Osg3dView *osgWin = dynamic_cast(sender()); 21 | if (!osgWin) 22 | return; 23 | 24 | osg::ref_ptr viewingCore = osgWin->getViewingCore(); 25 | 26 | osg::Vec3d eyePos = viewingCore->getEyePosition(); 27 | ui->eyePositionEdit->setText(QString::asprintf("%g %g %g", eyePos.x(), eyePos.y(), eyePos.z())); 28 | 29 | osg::Vec3d viewCenter = viewingCore->getViewCenter(); 30 | ui->viewCenterEdit->setText(QString::asprintf("%g %g %g", viewCenter.x(), viewCenter.y(), viewCenter.z())); 31 | 32 | ui->fovyEdit->setText(QString::asprintf("%g", viewingCore->getFovy())); 33 | } 34 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.h" 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | void appSetup(const QString organizationName) 8 | { 9 | // set up application name 10 | QFileInfo applicationFile(QApplication::applicationFilePath()); 11 | 12 | // These allow us to simply construct a "QSettings" object without arguments 13 | qApp->setOrganizationDomain("mil.army.arl"); 14 | qApp->setApplicationName(applicationFile.baseName()); 15 | qApp->setOrganizationName(organizationName); 16 | qApp->setApplicationVersion(__DATE__ __TIME__); 17 | 18 | // Look up the last directory where the user opened files. 19 | // set it if it hasn't been set. 20 | QSettings settings; 21 | if (!settings.allKeys().contains("currentDirectory")) 22 | settings.setValue("currentDirectory", 23 | applicationFile.path()); 24 | } 25 | 26 | int main(int argc, char *argv[]) 27 | { 28 | QApplication a(argc, argv); 29 | appSetup("Army Research Laboratory"); 30 | MainWindow w; 31 | w.show(); 32 | 33 | return a.exec(); 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 iraytrace 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/OsgTreeForm.h: -------------------------------------------------------------------------------- 1 | #ifndef OSGTREEFORM_H 2 | #define OSGTREEFORM_H 3 | 4 | #include 5 | 6 | 7 | #include "OsgItemModel.h" 8 | 9 | class QTableWidgetItem; 10 | class QTableWidget; 11 | #include 12 | #include 13 | 14 | namespace Ui { 15 | class OsgTreeForm; 16 | } 17 | 18 | class OsgTreeForm : public QWidget 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit OsgTreeForm(QWidget *parent = 0); 24 | ~OsgTreeForm(); 25 | 26 | void setModel(OsgItemModel *model); 27 | private slots: 28 | void osgObjectActivated(osg::ref_ptr object); 29 | void itemClicked(QTableWidgetItem * item); 30 | 31 | private: 32 | void setTableValuesObject(osg::ref_ptr object); 33 | void setTableValuesNode(osg::Node * node); 34 | void setTableValuesGroup(osg::Group *group); 35 | void setTableValuesGeode(osg::Geode *geode); 36 | void setTableValuesDrawable(osg::Drawable *drawable); 37 | 38 | void setTableValuesGeometry(osg::Geometry *geometry); 39 | 40 | QTableWidgetItem *getOrCreateWidgetItem(QTableWidget *tw, int row, int col); 41 | QTableWidgetItem *itemForKey(const QString key); 42 | void setTextForKey(const QString key, const QString value = QString("")); 43 | 44 | Ui::OsgTreeForm *ui; 45 | QTableWidgetItem *setKeyChecked(const QString key, const bool value); 46 | }; 47 | 48 | #endif // OSGTREEFORM_H 49 | -------------------------------------------------------------------------------- /src/OsgTreeView.cpp: -------------------------------------------------------------------------------- 1 | #include "OsgTreeView.h" 2 | #include 3 | #include "OsgItemModel.h" 4 | 5 | OsgTreeView::OsgTreeView(QWidget *parent) : QTreeView(parent) 6 | { 7 | setContextMenuPolicy(Qt::CustomContextMenu); 8 | 9 | connect(this, SIGNAL(customContextMenuRequested(QPoint)), 10 | this, SLOT(customMenuRequested(QPoint))); 11 | 12 | connect(this, SIGNAL(expanded(QModelIndex)), 13 | this, SLOT(resizeColumnsToFit())); 14 | connect(this, SIGNAL(collapsed(QModelIndex)), 15 | this, SLOT(resizeColumnsToFit())); 16 | 17 | connect(this, SIGNAL(activated(QModelIndex)), 18 | this, SLOT(announceObject(QModelIndex))); 19 | connect(this, SIGNAL(clicked(QModelIndex)), 20 | this, SLOT(announceObject(QModelIndex))); 21 | 22 | popupMenu.addAction(new QAction("Copy", this)); 23 | popupMenu.addAction(new QAction("Cut", this)); 24 | popupMenu.addAction(new QAction("Paste", this)); 25 | 26 | } 27 | 28 | 29 | void OsgTreeView::resizeColumnsToFit() 30 | { 31 | this->resizeColumnToContents(0); 32 | this->resizeColumnToContents(1); 33 | } 34 | 35 | void OsgTreeView::customMenuRequested(QPoint pos) 36 | { 37 | popupMenu.popup(this->viewport()->mapToGlobal(pos)); 38 | } 39 | 40 | void OsgTreeView::announceObject(const QModelIndex &index) 41 | { 42 | OsgItemModel *model = dynamic_cast(this->model()); 43 | 44 | if (!model) 45 | return; 46 | 47 | emit osgObjectActivated(model->getObjectFromModelIndex(index)); 48 | } 49 | -------------------------------------------------------------------------------- /src/MainWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.h" 2 | #include "ui_MainWindow.h" 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | MainWindow::MainWindow(QWidget *parent) : 9 | QMainWindow(parent), 10 | ui(new Ui::MainWindow) 11 | { 12 | ui->setupUi(this); 13 | 14 | ui->osgTreeForm->setModel(&m_itemModel); 15 | ui->osg3dView->setScene(&m_itemModel); 16 | 17 | connect(ui->osg3dView, SIGNAL(updated()), 18 | ui->osgCameraView, SLOT(updateFromCamera())); 19 | } 20 | 21 | MainWindow::~MainWindow() 22 | { 23 | delete ui; 24 | } 25 | 26 | void MainWindow::on_actionFileOpen_triggered() 27 | { 28 | QSettings settings; 29 | settings.value("currentDirectory"); 30 | QString fileName = QFileDialog::getOpenFileName(this, 31 | "Select File", 32 | settings.value("currentDirectory").toString(), 33 | "OpenSceneGraph (*.osg *.ive *.osgt *.osgb *.obj)"); 34 | if (fileName.isEmpty()) 35 | return; 36 | 37 | settings.setValue("currentDirectory", QVariant(QFileInfo(fileName).path())); 38 | 39 | m_itemModel.importFileByName(fileName); 40 | settings.setValue("recentFile", fileName); 41 | } 42 | 43 | void MainWindow::on_actionFileSave_triggered() 44 | { 45 | QSettings settings; 46 | 47 | QString fileName = settings.value("recentFile").toString(); 48 | 49 | if (m_itemModel.saveToFileByName(fileName)) 50 | qDebug("yes"); 51 | else 52 | qDebug("no"); 53 | } 54 | 55 | void MainWindow::on_actionFileSaveAs_triggered() 56 | { 57 | QSettings settings; 58 | settings.value("currentDirectory"); 59 | QString fileName = QFileDialog::getSaveFileName(this, 60 | "Select File", 61 | settings.value("currentDirectory").toString(), 62 | "OpenSceneGraph (*.osgt *.osgb *.osgx)"); 63 | qDebug("save file<%s>", qPrintable(fileName)); 64 | if (m_itemModel.saveToFileByName(fileName)) 65 | qDebug("yes"); 66 | else 67 | qDebug("no"); 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/OsgTreeForm.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | OsgTreeForm 4 | 5 | 6 | 7 | 0 8 | 0 9 | 571 10 | 639 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 1 19 | 20 | 21 | 1 22 | 23 | 24 | 1 25 | 26 | 27 | 1 28 | 29 | 30 | 31 | 32 | Qt::Vertical 33 | 34 | 35 | 36 | 37 | 1 38 | 39 | 40 | true 41 | 42 | 43 | false 44 | 45 | 46 | 47 | 48 | Name 49 | 50 | 51 | 52 | 53 | Value 54 | 55 | 56 | 57 | 58 | ObjName 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | ItemIsSelectable|ItemIsEditable|ItemIsDragEnabled|ItemIsEnabled 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | OsgTreeView 77 | QTreeView 78 |
OsgTreeView.h
79 |
80 |
81 | 82 | 83 |
84 | -------------------------------------------------------------------------------- /src/OsgItemModel.h: -------------------------------------------------------------------------------- 1 | #ifndef OSGITEMMODEL_H 2 | #define OSGITEMMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class OsgItemModel : public QAbstractItemModel 9 | { 10 | public: 11 | OsgItemModel(QObject * parent = 0); 12 | 13 | //////////////////// Start QAbstractItemModel methods ////////////////////// 14 | int columnCount(const QModelIndex &parent) const; 15 | QVariant data(const QModelIndex &index, int role) const; 16 | Qt::ItemFlags flags(const QModelIndex &index) const; 17 | bool hasChildren(const QModelIndex &parent=QModelIndex()) const; 18 | QVariant headerData(int section, 19 | Qt::Orientation orientation, 20 | int role) const; 21 | QModelIndex index(int row, 22 | int column, 23 | const QModelIndex & parent = QModelIndex()) const; 24 | 25 | QModelIndex parent(const QModelIndex &index) const; 26 | int rowCount(const QModelIndex &parent = QModelIndex()) const; 27 | bool setData(const QModelIndex &index, 28 | const QVariant &value, 29 | int role = Qt::EditRole); 30 | //////////////////// End QAbstractItemModel methods //////////////////////// 31 | 32 | void importFileByName(const QString fileName); ///< load a file into the "root" 33 | bool saveToFileByName(const QString fileName); 34 | 35 | /// print the entire heirarchy on stderr. Mostly for debugging. 36 | void printTree() const; 37 | 38 | /// print a node of the tree and all its children 39 | void printNode(osg::ref_ptr n, 40 | const int level) const; 41 | 42 | void insertNode(osg::ref_ptr parent, 43 | osg::ref_ptr newChild, 44 | int childPositionInParent, 45 | int row); 46 | 47 | osg::ref_ptr getObjectFromModelIndex(const QModelIndex &index) const; 48 | 49 | // The only thing that should call this is OsgView::setScene() 50 | osg::ref_ptr getRoot() const { return m_root; } 51 | 52 | private: 53 | 54 | QString maskToString(const osg::Node::NodeMask mask) const; 55 | QModelIndex modelIndexFromNode(osg::ref_ptr ptr, 56 | int column) const; 57 | QModelIndex parentOfNode(osg::Node *childNode) const; 58 | QModelIndex parentOfDrawable(osg::Drawable *childDrawable) const; 59 | osg::ref_ptr m_root; 60 | osg::ref_ptr m_loadedModel; 61 | osg::ref_ptr m_clipBoard; 62 | bool setObjectMask(const QModelIndex &index, const QVariant &value); 63 | bool setObjectName(const QModelIndex &index, const QVariant &value); 64 | }; 65 | 66 | #endif // OSGITEMMODEL_H 67 | -------------------------------------------------------------------------------- /src/OsgCameraForm.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | OsgCameraForm 4 | 5 | 6 | 7 | 0 8 | 0 9 | 371 10 | 569 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | eyePosition 21 | 22 | 23 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | viewCenter 34 | 35 | 36 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Fovy 47 | 48 | 49 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | mode 60 | 61 | 62 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 63 | 64 | 65 | 66 | 67 | 68 | 69 | Qt::Vertical 70 | 71 | 72 | 73 | 20 74 | 397 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | true 83 | 84 | 85 | 86 | 87 | 88 | 89 | Perspective 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/Osg3dView.h: -------------------------------------------------------------------------------- 1 | #ifndef OSGVIEW_H 2 | #define OSGVIEW_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "ViewingCore.h" 11 | 12 | class OsgItemModel; 13 | 14 | class Osg3dView : public QOpenGLWidget, public osgViewer::Viewer 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | Osg3dView(QWidget * parent = 0); 20 | 21 | enum MouseMode { 22 | MM_ORBIT = (1<<1), 23 | MM_PAN = (1<<2), 24 | MM_ZOOM = (1<<3), 25 | MM_ROTATE = (1<<4), 26 | MM_PICK_CENTER = (1<<5) 27 | }; 28 | enum StandardView { 29 | V_TOP = (1<<0), 30 | V_BOTTOM = (1<<1), 31 | V_FRONT = (1<<2), 32 | V_BACK = (1<<3), 33 | V_RIGHT = (1<<4), 34 | V_LEFT = (1<<5) 35 | }; 36 | enum Projection { 37 | P_ORTHO = (1<<0), 38 | P_PERSP = (1<<1) 39 | }; 40 | enum DrawMode { 41 | D_FACET = (1<<1), 42 | D_WIRE = (1<<2), 43 | D_POINT = (1<<3) 44 | }; 45 | 46 | /// Let others tell what scene graph we should be drawing 47 | void setScene(OsgItemModel *model); 48 | 49 | osg::ref_ptr getViewingCore() const { return m_viewingCore; } 50 | 51 | public slots: 52 | void paintGL(); 53 | void resizeGL(int w, int h); 54 | void hello(); 55 | /// Set the m_mouseMode variable 56 | void setMouseMode(MouseMode mode); 57 | void setMouseMode(); 58 | void setStandardView(); 59 | void setDrawMode(); 60 | void setProjection(); 61 | 62 | void mousePressEvent( QMouseEvent* event ); 63 | void mouseReleaseEvent(QMouseEvent* event); 64 | void mouseMoveEvent(QMouseEvent* event); 65 | void wheelEvent(QWheelEvent *event); 66 | void customMenuRequested(const QPoint &pos); 67 | 68 | void fitScreenTopView(const QModelIndex & parent, int first, int last); 69 | 70 | void dataChanged(const QModelIndex & topLeft, 71 | const QModelIndex & bottomRight, 72 | const QVector & roles = QVector ()); 73 | 74 | signals: 75 | /// Let the rest of the world (OsgView) know the current MouseMode 76 | void mouseModeChanged(Osg3dView::MouseMode); 77 | void updated(); 78 | 79 | private: 80 | osg::Vec2d getNormalized(const int ix, const int iy); 81 | 82 | /// OSG uses singleSided drawing/display by default. 83 | /// This is annoying when you are "inside" something and the back wall of 84 | /// it simply disappears. This gets called to set up to draw both front and 85 | /// back facing polygons so that the world behaves in a more normal fashion. 86 | /// Yes it's a performance hit. 87 | void setLightingTwoSided(); 88 | 89 | void buildPopupMenu(); 90 | 91 | QMenu m_popupMenu; 92 | 93 | /// OSG graphics window 94 | osg::ref_ptr m_osgGraphicsWindow; 95 | 96 | /// Camera manager 97 | osg::ref_ptr m_viewingCore; 98 | 99 | /// Current mouse mode 100 | MouseMode m_mouseMode; 101 | 102 | osg::Vec2d m_savedEventNDCoords; 103 | }; 104 | 105 | #endif // OSGVIEW_H 106 | -------------------------------------------------------------------------------- /src/MainWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 753 10 | 604 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | Qt::Horizontal 22 | 23 | 24 | 25 | 26 | 512 27 | 512 28 | 29 | 30 | 31 | 32 | 33 | 0 34 | 35 | 36 | 37 | Tree 38 | 39 | 40 | 41 | 1 42 | 43 | 44 | 1 45 | 46 | 47 | 1 48 | 49 | 50 | 1 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Camera 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 0 76 | 0 77 | 753 78 | 33 79 | 80 | 81 | 82 | 83 | File 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | TopToolBarArea 97 | 98 | 99 | false 100 | 101 | 102 | 103 | 104 | 105 | Open... 106 | 107 | 108 | Ctrl+O 109 | 110 | 111 | 112 | 113 | Quit 114 | 115 | 116 | Ctrl+Q 117 | 118 | 119 | 120 | 121 | Save 122 | 123 | 124 | 125 | 126 | SaveAs... 127 | 128 | 129 | 130 | 131 | 132 | 133 | OsgTreeForm 134 | QWidget 135 |
OsgTreeForm.h
136 | 1 137 |
138 | 139 | Osg3dView 140 | QOpenGLWidget 141 |
Osg3dView.h
142 |
143 | 144 | OsgCameraForm 145 | QWidget 146 |
OsgCameraForm.h
147 | 1 148 |
149 |
150 | 151 | 152 | 153 | actionQuit 154 | triggered() 155 | MainWindow 156 | close() 157 | 158 | 159 | -1 160 | -1 161 | 162 | 163 | 199 164 | 149 165 | 166 | 167 | 168 | 169 |
170 | -------------------------------------------------------------------------------- /src/OSGWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef OSGWIDGET_H 2 | #define OSGWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "OsgView/ViewingCore.h" 10 | 11 | class OSGWidget : public QGLWidget, 12 | public osgViewer::Viewer 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | 18 | OSGWidget(QWidget *parent); 19 | 20 | // Helper types //////////////////////////////////////////////////////////// 21 | enum MouseMode { 22 | MM_ORBIT = (1<<1), 23 | MM_PAN = (1<<2), 24 | MM_ZOOM = (1<<3), 25 | MM_ROTATE = (1<<4), 26 | MM_PICK_CENTER = (1<<5) 27 | }; 28 | 29 | // Overridden from QGLWidget ////////////////////////////////////////////// 30 | 31 | // Qt Event handlers 32 | //void keyPressEvent(QKeyEvent* event); 33 | //void keyReleaseEvent(QKeyEvent* event); 34 | void mousePressEvent( QMouseEvent* event ); 35 | void mouseReleaseEvent(QMouseEvent* event); 36 | void mouseMoveEvent(QMouseEvent* event); 37 | void wheelEvent(QWheelEvent *event); 38 | 39 | // Overridden from osgViewer::Viewer ////////////////////////////////////// 40 | 41 | /// Let others tell what scene graph we should be drawing 42 | void setScene(osg::Node *); 43 | 44 | /// Render one frame 45 | void paintGL(); 46 | public slots: 47 | /// Set the view of the camera 48 | void viewTop() { 49 | m_viewingCore->viewTop(); 50 | } 51 | void viewbottom() { 52 | m_viewingCore->viewBottom(); 53 | } 54 | void viewRight() { 55 | m_viewingCore->viewRight(); 56 | } 57 | void viewLeft() { 58 | m_viewingCore->viewLeft(); 59 | } 60 | void viewFront() { 61 | m_viewingCore->viewFront(); 62 | } 63 | void viewBack() { 64 | m_viewingCore->viewBack(); 65 | } 66 | void resetView() { 67 | m_viewingCore->computeInitialView(); 68 | } 69 | void fitToScreen() { 70 | m_viewingCore->fitToScreen(); 71 | } 72 | 73 | /// Set the m_mouseMode variable 74 | void setMouseMode(MouseMode mode); 75 | private slots: 76 | 77 | /// Handle camera animation for 'tossing' the geometry 78 | void animateCameraThrow(); 79 | 80 | signals: 81 | 82 | /// Let the rest of the world (OsgView) know the current MouseMode 83 | void mouseModeChanged(OSGWidget::MouseMode); 84 | 85 | private: 86 | 87 | // Helper types /////////////////////////////////////////////////////////// 88 | 89 | struct stashState { 90 | MouseMode mm; 91 | ViewingCore::ViewingCoreMode vcm; 92 | }; 93 | 94 | // Helper functions /////////////////////////////////////////////////////// 95 | 96 | /// OSG uses singleSided drawing/display by default. 97 | /// This is annoying when you are "inside" something and the back wall of 98 | /// it simply disappears. This gets called to set up to draw both front and 99 | /// back facing polygons so that the world behaves in a more normal fashion. 100 | /// Yes it's a performance hit. 101 | void setLightingTwoSided(); 102 | 103 | /// Invoked whenever the window size changes so that the OpenGL viewport 104 | /// gets updated appropriately 105 | void resizeGL( int width, int height ); 106 | 107 | /// Get the Normalized Device Coordinates for a given X,Y pixel location 108 | osg::Vec2d getNormalized(const int ix, const int iy); 109 | 110 | 111 | /// compute the distance (in inches) between m_lastMouseEventNDCCoords and 112 | /// the parmeter mouse event. 113 | float inchesToLastMouseEvent(QMouseEvent* thisMouseEvent); 114 | 115 | /// stash the current mouse behaviour for later 116 | void pushState(); 117 | 118 | /// restore a previously saved mouse behavior 119 | void popState(); 120 | 121 | // Private data /////////////////////////////////////////////////////////// 122 | 123 | 124 | 125 | /// The currently pressed mouse buttons 126 | int m_mouseButtons; 127 | 128 | /// The currently active modifiers (from the mousePress event) 129 | int m_mouseModifiers; 130 | 131 | /// Current mouse mode 132 | MouseMode m_mouseMode; 133 | 134 | /// OSG graphics window 135 | osg::ref_ptr m_osgGraphicsWindow; 136 | 137 | /// Viewing Core --> controls the camera of the osgViewer 138 | osg::ref_ptr< ViewingCore > m_viewingCore; 139 | 140 | /// Redraw the OSG window every time this timer signals 141 | QTimer m_redrawTimer; 142 | 143 | /// When this ticks update the camera position to support animation 144 | /// If the time to draw a frame is large then multiple of these will be 145 | /// accumulated at once and arguably should only be done once. However if 146 | /// the redraw time is very small compared to this timer then the angular 147 | /// difference frame to frame might become very small and induce floating 148 | /// point error. 149 | QTimer m_animateTimer; 150 | 151 | /// The position (in Normalized Device Coordinates) of the last mouse event 152 | osg::Vec2d m_lastMouseEventNDCoords; 153 | 154 | /// The time at which the last mouse event occured. 155 | QTime m_lastMouseEventTime; 156 | 157 | QList m_stashState; 158 | 159 | // Throwing support ////////////////////////////////////////////////// 160 | 161 | /// flag: the last mouse event was a release 162 | bool m_mouseReleaseJustHappened; 163 | 164 | /// if a mouse move event happens in less time than this after a mouse 165 | /// release event then we are possibly throwing the display 166 | int m_throwTimeTolerance; 167 | 168 | /// If the mouse moves more than this distance (inches) from the time 169 | /// of the mouse is released to the next mouse move event, we are throwing 170 | float m_throwDistTolerance; ///< if moues moves more than this din 171 | 172 | /// These three Vec2d are NDC coordinates used for doing animation. 173 | 174 | /// The location of the event prior to the start of animation 175 | /// Initialized from m_lastMouseEventNDCoords 176 | osg::Vec2d m_animateStart; 177 | 178 | /// delta from m_aimateStart to event that causes animation to start 179 | osg::Vec2d m_animateDelta; 180 | 181 | /// prediction of location of next mouse event 182 | osg::Vec2d m_predictedPos; 183 | }; 184 | 185 | #endif // OSGVIEW_H 186 | -------------------------------------------------------------------------------- /src/OsgTreeForm.cpp: -------------------------------------------------------------------------------- 1 | #include "OsgTreeForm.h" 2 | #include "ui_OsgTreeForm.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #include "VariantPtr.h" 8 | 9 | OsgTreeForm::OsgTreeForm(QWidget *parent) : 10 | QWidget(parent), 11 | ui(new Ui::OsgTreeForm) 12 | { 13 | ui->setupUi(this); 14 | ui->splitter->setStretchFactor(0, 3); 15 | ui->splitter->setStretchFactor(1, 1); 16 | ui->osgTableWidget->resizeColumnsToContents(); 17 | ui->osgTableWidget->horizontalHeader()->setStretchLastSection(true); 18 | 19 | 20 | connect(ui->osgTreeView, SIGNAL(osgObjectActivated(osg::ref_ptr)), 21 | this, SLOT(osgObjectActivated(osg::ref_ptr))); 22 | 23 | connect(ui->osgTableWidget, SIGNAL(itemClicked(QTableWidgetItem*)), 24 | this, SLOT(itemClicked(QTableWidgetItem *))); 25 | 26 | // hide all rows 27 | for (int i=0 ; i < ui->osgTableWidget->rowCount() ; i++) { 28 | ui->osgTableWidget->hideRow(i); 29 | } 30 | 31 | 32 | } 33 | 34 | OsgTreeForm::~OsgTreeForm() 35 | { 36 | delete ui; 37 | } 38 | 39 | void OsgTreeForm::setModel(OsgItemModel *model) 40 | { 41 | ui->osgTreeView->setModel(model); 42 | 43 | connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), 44 | ui->osgTreeView, SLOT(resizeColumnsToFit())); 45 | } 46 | 47 | QTableWidgetItem * OsgTreeForm::getOrCreateWidgetItem(QTableWidget *tw, int row, int col) 48 | { 49 | QTableWidgetItem *twi = tw->item(row, col); 50 | if (!twi) { 51 | twi = new QTableWidgetItem; 52 | tw->setItem(row, col, twi); 53 | } 54 | return twi; 55 | } 56 | void OsgTreeForm::setTextForKey(const QString key, const QString value) 57 | { 58 | QTableWidgetItem *twi = itemForKey(key); 59 | 60 | twi->setText(value); 61 | 62 | twi->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); 63 | ui->osgTableWidget->showRow(twi->row()); 64 | } 65 | 66 | QTableWidgetItem * OsgTreeForm::setKeyChecked(const QString key, const bool value) 67 | { 68 | QTableWidgetItem *twi = itemForKey(key); 69 | 70 | twi->setText(""); 71 | 72 | if (value) 73 | twi->setCheckState(Qt::Checked); 74 | else 75 | twi->setCheckState(Qt::Unchecked); 76 | 77 | twi->setFlags(Qt::ItemIsEnabled); 78 | ui->osgTableWidget->showRow(twi->row()); 79 | 80 | return twi; 81 | } 82 | 83 | QTableWidgetItem * OsgTreeForm::itemForKey(const QString key) 84 | { 85 | QList twilist = ui->osgTableWidget->findItems(key, Qt::MatchFixedString); 86 | QTableWidgetItem *twi; 87 | 88 | if (twilist.size() == 0) { 89 | int currentRowCount = ui->osgTableWidget->rowCount(); 90 | ui->osgTableWidget->setRowCount(currentRowCount+1); 91 | twi = getOrCreateWidgetItem(ui->osgTableWidget, currentRowCount, 0); 92 | twi->setText(key); 93 | } else 94 | twi = twilist.at(0); 95 | 96 | return getOrCreateWidgetItem(ui->osgTableWidget, twi->row(), twi->column()+1); 97 | } 98 | 99 | void OsgTreeForm::osgObjectActivated(osg::ref_ptr object) 100 | { 101 | qDebug("activated(%s)", object->getName().c_str()); 102 | 103 | 104 | for (int i=0 ; i < ui->osgTableWidget->rowCount() ; i++) { 105 | ui->osgTableWidget->hideRow(i); 106 | } 107 | 108 | setTableValuesObject(object); 109 | setTableValuesNode(dynamic_cast(object.get())); 110 | setTableValuesDrawable(dynamic_cast(object.get())); 111 | 112 | ui->osgTableWidget->resizeColumnsToContents(); 113 | ui->osgTableWidget->horizontalHeader()->setStretchLastSection(true); 114 | } 115 | 116 | void OsgTreeForm::itemClicked(QTableWidgetItem *item) 117 | { 118 | if ( ! (item->flags() & Qt::ItemIsUserCheckable) ) 119 | return; 120 | 121 | QVariant v = item->data(Qt::UserRole); 122 | 123 | qDebug("clicked %d %d %s", item->row(), item->column(), qPrintable(item->text())); 124 | if (v.isValid() && !v.isNull()) { 125 | osg::Object *obj = VariantPtr::asPtr(v); 126 | 127 | if (osg::Node *n = dynamic_cast(obj)) { 128 | qDebug("Node"); 129 | } 130 | if (osg::Drawable *d = dynamic_cast(obj)) { 131 | qDebug("Drawable"); 132 | } 133 | 134 | } 135 | } 136 | 137 | void OsgTreeForm::setTableValuesObject(osg::ref_ptr object) 138 | { 139 | setTextForKey("Name", QString::fromStdString( object->getName() )); 140 | 141 | osg::Object::DataVariance dataVariance = object->getDataVariance(); 142 | QString key = "DataVariance"; 143 | switch (dataVariance) { 144 | case osg::Object::DYNAMIC: 145 | setTextForKey(key, "DYNAMIC"); 146 | break; 147 | case osg::Object::STATIC: 148 | setTextForKey(key, "STATIC"); 149 | break; 150 | case osg::Object::UNSPECIFIED: 151 | setTextForKey(key, "UNSPECIFIED"); 152 | break; 153 | } 154 | } 155 | 156 | void OsgTreeForm::setTableValuesNode(osg::Node *node) 157 | { 158 | if (!node) 159 | return; 160 | 161 | setTextForKey("NodeMask", QString::asprintf("%08x", (unsigned)node->getNodeMask())); 162 | 163 | setKeyChecked("CullingActive", node->isCullingActive()); 164 | 165 | setTextForKey("Descriptions", QString::asprintf("%d", node->getNumDescriptions())); 166 | 167 | setTableValuesGroup(node->asGroup()); 168 | setTableValuesGeode(node->asGeode()); 169 | } 170 | void OsgTreeForm::setTableValuesGroup(osg::Group *group) 171 | { 172 | if(!group) return; 173 | setTextForKey("NumChildren", QString::asprintf("%d", group->getNumChildren())); 174 | } 175 | 176 | void OsgTreeForm::setTableValuesGeode(osg::Geode *geode) 177 | { 178 | if(!geode) return; 179 | 180 | setTextForKey("NumDrawables", QString::asprintf("%d", geode->getNumDrawables())); 181 | 182 | osg::BoundingSpheref bs = geode->computeBound(); 183 | setTextForKey("GeodeBoundingSphere", QString::asprintf("radius:%g @(%g %g %g)", 184 | bs.radius(), 185 | bs.center().x(), 186 | bs.center().y(), 187 | bs.center().z() 188 | )); 189 | } 190 | 191 | void OsgTreeForm::setTableValuesDrawable(osg::Drawable *drawable) 192 | { 193 | if (!drawable) return; 194 | 195 | setKeyChecked("UseVertexBuffer", drawable->getUseVertexBufferObjects()); 196 | setKeyChecked("UseDisplayList", drawable->getUseDisplayList()); 197 | 198 | setTableValuesGeometry(drawable->asGeometry()); 199 | } 200 | 201 | void OsgTreeForm::setTableValuesGeometry(osg::Geometry *geometry) 202 | { 203 | if (!geometry) return; 204 | 205 | osg::Array *array = geometry->getVertexArray(); 206 | setTextForKey("PrimitiveSets", 207 | QString::asprintf("%d", geometry->getNumPrimitiveSets() )); 208 | 209 | if (array) 210 | setTextForKey("VertexCount", QString::asprintf("%d", array->getNumElements())); 211 | 212 | 213 | array = geometry->getNormalArray(); 214 | if (array) 215 | setTextForKey("NormalCount", QString::asprintf("%d", array->getNumElements())); 216 | 217 | 218 | array = geometry->getColorArray(); 219 | if (array) 220 | setTextForKey("ColorCount", QString::asprintf("%d", array->getNumElements())); 221 | 222 | setTextForKey("TextCoordArrayCount", 223 | QString::asprintf("%d", geometry->getNumTexCoordArrays())); 224 | 225 | } 226 | -------------------------------------------------------------------------------- /src/Osg3dView.cpp: -------------------------------------------------------------------------------- 1 | #include "Osg3dView.h" 2 | 3 | #include 4 | #include "OsgItemModel.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | static bool debugView = false; 11 | #define vDebug if (debugView) qDebug 12 | 13 | Osg3dView::Osg3dView(QWidget *parent) 14 | : QOpenGLWidget(parent) 15 | , m_viewingCore(new ViewingCore) 16 | , m_mouseMode(MM_ORBIT) 17 | { 18 | setContextMenuPolicy(Qt::CustomContextMenu); 19 | connect(this, SIGNAL(customContextMenuRequested(QPoint)), 20 | this, SLOT(customMenuRequested(QPoint))); 21 | buildPopupMenu(); 22 | 23 | // Construct the embedded graphics window 24 | m_osgGraphicsWindow = new osgViewer::GraphicsWindowEmbedded(0,0,width(),height()); 25 | getCamera()->setGraphicsContext(m_osgGraphicsWindow); 26 | 27 | // Set up the camera 28 | getCamera()->setViewport(new osg::Viewport(0,0,width(),height())); 29 | getCamera()->setProjectionMatrixAsPerspective(30.0f, 30 | static_cast(width())/static_cast(height()), 31 | 1.0f, 32 | 10000.0f); 33 | // By default draw everthing that has even 1 bit set in the nodemask 34 | getCamera()->setCullMask( (unsigned)~0 ); 35 | getCamera()->setDataVariance(osg::Object::DYNAMIC); 36 | 37 | // As of July 2010 there wasn't really a good way to multi-thread OSG 38 | // under Qt so just set the threading model to be SingleThreaded 39 | setThreadingModel(osgViewer::Viewer::SingleThreaded); 40 | 41 | // draw both sides of polygons 42 | setLightingTwoSided(); 43 | 44 | update(); 45 | } 46 | 47 | void Osg3dView::setScene(OsgItemModel *model) 48 | { 49 | connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), 50 | this, SLOT(fitScreenTopView(QModelIndex,int,int))); 51 | connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), 52 | this, SLOT(update())); 53 | 54 | osg::ref_ptr root = model->getRoot(); 55 | this->setSceneData(root); 56 | m_viewingCore->setSceneData(root); 57 | } 58 | 59 | void Osg3dView::paintGL() 60 | { 61 | vDebug("paintGL"); 62 | 63 | // Update the camera 64 | osg::Camera *cam = this->getCamera(); 65 | const osg::Viewport* vp = cam->getViewport(); 66 | 67 | m_viewingCore->setAspect(vp->width() / vp->height()); 68 | osg::Node *n = this->getSceneData(); 69 | 70 | int i; 71 | if (n->getUserValue("fred", i)) { 72 | qDebug("fred is %d", i); 73 | } else { 74 | qDebug("fred not set"); 75 | } 76 | cam->setViewMatrix(m_viewingCore->getInverseMatrix()); 77 | cam->setProjectionMatrix(m_viewingCore->computeProjection()); 78 | 79 | // Invoke the OSG traversal pipeline 80 | frame(); 81 | 82 | emit updated(); 83 | } 84 | 85 | void Osg3dView::resizeGL(int w, int h) 86 | { 87 | vDebug("resizeGL"); 88 | 89 | m_osgGraphicsWindow->getEventQueue()->windowResize(9, 0, w, h); 90 | m_osgGraphicsWindow->resized(0,0,w,h); 91 | } 92 | 93 | void Osg3dView::hello() 94 | { 95 | qDebug("hello"); 96 | } 97 | 98 | void Osg3dView::setMouseMode(Osg3dView::MouseMode mode) 99 | { 100 | m_mouseMode = mode; 101 | 102 | if(mode == MM_ROTATE) 103 | m_viewingCore->setViewingCoreMode( ViewingCore::FIRST_PERSON ); 104 | else { 105 | m_viewingCore->setViewingCoreMode( ViewingCore::THIRD_PERSON ); 106 | } 107 | 108 | emit mouseModeChanged(mode); 109 | } 110 | 111 | osg::Vec2d Osg3dView::getNormalized(const int ix, const int iy) 112 | { 113 | int x, y, width, height; 114 | osg::Vec2d ndc; 115 | 116 | // we don't really need the x, y values but the width/height are important 117 | m_osgGraphicsWindow->getWindowRectangle(x, y, width, height); 118 | int center = width/2; 119 | ndc[0] = ((double)ix - (double)center) / (double)center; 120 | if (ndc[0] > 1.0) ndc[0] = 1.0; 121 | 122 | center = height/2; 123 | int invertedY = height - iy; 124 | ndc[1] = ((double)invertedY - (double)center) / (double)center; 125 | if (ndc[1] > 1.0) ndc[1] = 1.0; 126 | 127 | return ndc; 128 | } 129 | 130 | void Osg3dView::mousePressEvent(QMouseEvent *event) 131 | { 132 | vDebug("mousePressEvent"); 133 | 134 | if (event->button() == Qt::LeftButton) { 135 | m_savedEventNDCoords = getNormalized(event->x(), event->y()); 136 | 137 | // Do the job asked 138 | if (m_mouseMode & (MM_PAN|MM_ROTATE|MM_ORBIT|MM_ZOOM) ) 139 | m_viewingCore->setPanStart( m_savedEventNDCoords.x(), 140 | m_savedEventNDCoords.y()); 141 | else if (m_mouseMode & MM_PICK_CENTER) { 142 | m_viewingCore->pickCenter(m_savedEventNDCoords.x(), 143 | m_savedEventNDCoords.y() ); 144 | } 145 | } 146 | } 147 | 148 | void Osg3dView::mouseMoveEvent(QMouseEvent *event) 149 | { 150 | vDebug("mouseMoveEvent"); 151 | osg::Vec2d currentNDC = getNormalized(event->x(), event->y()); 152 | osg::Vec2d delta = currentNDC - m_savedEventNDCoords; 153 | 154 | switch (m_mouseMode) { 155 | case MM_ORBIT: 156 | m_viewingCore->rotate( m_savedEventNDCoords, delta); 157 | break; 158 | case MM_PAN: 159 | m_viewingCore->pan(delta.x(), delta.y()); 160 | break; 161 | case MM_ZOOM: { 162 | double tempScale = m_viewingCore->getFovyScale(); 163 | m_viewingCore->setFovyScale(1.03); 164 | 165 | if(delta.y() > 0) 166 | m_viewingCore->fovyScaleDown(); 167 | 168 | if(delta.y() < 0) 169 | m_viewingCore->fovyScaleUp(); 170 | 171 | m_viewingCore->setFovyScale(tempScale); 172 | break; 173 | } 174 | case MM_ROTATE: 175 | m_viewingCore->rotate( m_savedEventNDCoords, delta ); 176 | break; 177 | default: 178 | break; 179 | } 180 | 181 | m_savedEventNDCoords = currentNDC; 182 | update(); 183 | } 184 | 185 | void Osg3dView::mouseReleaseEvent(QMouseEvent *event) 186 | { 187 | vDebug("mouseReleaseEvent"); 188 | m_savedEventNDCoords = getNormalized(event->x(), event->y()); 189 | } 190 | 191 | 192 | void Osg3dView::wheelEvent(QWheelEvent *event) 193 | { 194 | if(event->delta() > 0) 195 | m_viewingCore->dolly(0.5); 196 | else 197 | m_viewingCore->dolly(-0.5); 198 | update(); 199 | } 200 | 201 | void Osg3dView::buildPopupMenu() 202 | { 203 | QAction *a; 204 | QMenu *sub = m_popupMenu.addMenu("MouseMode..."); 205 | 206 | a = sub->addAction("Orbit", this, SLOT(setMouseMode())); 207 | a->setData(QVariant(MM_ORBIT)); 208 | a = sub->addAction("Pan", this, SLOT(setMouseMode())); 209 | a->setData(QVariant(MM_PAN)); 210 | a = sub->addAction("Rotate", this, SLOT(setMouseMode())); 211 | a->setData(QVariant(MM_ROTATE)); 212 | a = sub->addAction("Zoom", this, SLOT(setMouseMode())); 213 | a->setData(QVariant(MM_ZOOM)); 214 | a = sub->addAction("Pick Center", this, SLOT(setMouseMode())); 215 | a->setData(QVariant(MM_PICK_CENTER)); 216 | 217 | sub = m_popupMenu.addMenu("Std View..."); 218 | a = sub->addAction("Top", this, SLOT(setStandardView())); 219 | a->setData(V_TOP); 220 | a = sub->addAction("Bottom", this, SLOT(setStandardView())); 221 | a->setData(V_BOTTOM); 222 | a = sub->addAction("Front", this, SLOT(setStandardView())); 223 | a->setData(V_FRONT); 224 | a = sub->addAction("Back", this, SLOT(setStandardView())); 225 | a->setData(V_BACK); 226 | a = sub->addAction("Right", this, SLOT(setStandardView())); 227 | a->setData(V_RIGHT); 228 | a = sub->addAction("Left", this, SLOT(setStandardView())); 229 | a->setData(V_LEFT); 230 | 231 | sub = m_popupMenu.addMenu("Projection..."); 232 | a = sub->addAction("Orthographic", this, SLOT(setProjection())); 233 | a->setData(P_ORTHO); 234 | a = sub->addAction("Perspective", this, SLOT(setProjection())); 235 | a->setData(P_PERSP); 236 | 237 | sub = m_popupMenu.addMenu("DrawMode..."); 238 | a = sub->addAction("Facets", this, SLOT(setDrawMode())); 239 | a->setData(D_FACET); 240 | a = sub->addAction("Wireframe", this, SLOT(setDrawMode())); 241 | a->setData(D_WIRE); 242 | a = sub->addAction("Points", this, SLOT(setDrawMode())); 243 | a->setData(D_POINT); 244 | } 245 | 246 | void Osg3dView::customMenuRequested(const QPoint &pos) 247 | { 248 | vDebug("customMenu %d %d", pos.x(), pos.y()); 249 | 250 | m_popupMenu.popup(this->mapToGlobal(pos)); 251 | } 252 | 253 | void Osg3dView::fitScreenTopView(const QModelIndex &parent, int first, int last) 254 | { 255 | vDebug("rowsInserted"); 256 | m_viewingCore->viewTop(); 257 | m_viewingCore->fitToScreen(); 258 | update(); 259 | } 260 | 261 | void Osg3dView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) 262 | { 263 | vDebug("dataChanged"); 264 | update(); 265 | } 266 | 267 | void Osg3dView::setLightingTwoSided() 268 | { 269 | osg::ref_ptr lm = new osg::LightModel; 270 | lm->setTwoSided(true); 271 | lm->setAmbientIntensity(osg::Vec4(0.1f,0.1f,0.1f,1.0f)); 272 | 273 | osg::StateSet *ss; 274 | 275 | for (int i=0 ; i < 2 ; i++ ) { 276 | ss = ((osgViewer::Renderer *)getCamera() 277 | ->getRenderer())->getSceneView(i)->getGlobalStateSet(); 278 | 279 | ss->setAttributeAndModes(lm, osg::StateAttribute::ON); 280 | } 281 | } 282 | 283 | 284 | void Osg3dView::setMouseMode() 285 | { 286 | QAction *a = dynamic_cast(sender()); 287 | if (!a) 288 | return; 289 | 290 | MouseMode mode = static_cast(a->data().toUInt()); 291 | setMouseMode(mode); 292 | } 293 | 294 | void Osg3dView::setStandardView() 295 | { 296 | QAction *a = dynamic_cast(sender()); 297 | if (!a) 298 | return; 299 | 300 | StandardView view = static_cast(a->data().toUInt()); 301 | switch (view) { 302 | case V_TOP: m_viewingCore->viewTop(); break; 303 | case V_BOTTOM: m_viewingCore->viewBottom(); break; 304 | case V_FRONT: m_viewingCore->viewFront(); break; 305 | case V_BACK: m_viewingCore->viewBack(); break; 306 | case V_RIGHT: m_viewingCore->viewRight(); break; 307 | case V_LEFT: m_viewingCore->viewLeft(); break; 308 | } 309 | update(); 310 | } 311 | 312 | void Osg3dView::setDrawMode() 313 | { 314 | QAction *a = dynamic_cast(sender()); 315 | if (!a) 316 | return; 317 | 318 | DrawMode drawMode = static_cast(a->data().toUInt()); 319 | switch (drawMode) { 320 | case D_FACET: 321 | break; 322 | case D_WIRE: 323 | break; 324 | case D_POINT: 325 | break; 326 | } 327 | } 328 | 329 | void Osg3dView::setProjection() 330 | { 331 | QAction *a = dynamic_cast(sender()); 332 | if (!a) 333 | return; 334 | 335 | Projection projType = static_cast(a->data().toUInt()); 336 | switch (projType) { 337 | case P_ORTHO: 338 | m_viewingCore->setOrtho(true); 339 | break; 340 | case P_PERSP: 341 | m_viewingCore->setOrtho(false); 342 | break; 343 | } 344 | update(); 345 | } 346 | -------------------------------------------------------------------------------- /src/ViewingCore.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Skew Matrix Software LLC. All rights reserved. 2 | 3 | #ifndef __VIEWING_CORE_H__ 4 | #define __VIEWING_CORE_H__ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | //#include // this should get replaced by a C++11 13 | #include 14 | #include 15 | 16 | /** \brief A GUI-independent class for maintaining view and projection matrix parameters. 17 | * 18 | * This camera model primarily replaces OpenSceneGraph's camera model with a 19 | * better one. In particular, it allows switching between Orthographic and 20 | * Perspective modes with reasonable correspondence of the view. It allows 21 | * better interaction and inspection of camera parameters, and provides for 22 | * rotation about both the view center and the eye position. 23 | */ 24 | class ViewingCore : public osg::Object 25 | { 26 | public: 27 | ViewingCore(); 28 | ViewingCore( const ViewingCore& rhs, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY ); 29 | 30 | META_Object(osgwTools,ViewingCore) 31 | 32 | 33 | /** Specify the scene. Used to create appropriate view volume boundaries 34 | when switching from perspective to ortho in the setOrtho() function. Used 35 | by computeProjection() to compute appropriate zNear and zFar planes. */ 36 | void setSceneData( osg::Node* scene ); 37 | 38 | void computeInitialView(); 39 | 40 | 41 | /** Return a matrix using \c _viewDistance and \c _viewCenter as a translation, 42 | and \c _viewDir and \c _viewUp as an orientation basis. */ 43 | osg::Matrixd getMatrix() const; 44 | /** Just orientation, without the translation. */ 45 | osg::Matrixd getOrientationMatrix() const; 46 | /** Return the inverse of the getMatrixui->osgv->saveView()() function. */ 47 | osg::Matrixd getInverseMatrix() const; 48 | 49 | /** Causes the view to rotate. How the code changes the view depends on 50 | the viewing mode (setViewingCoreMode()). 51 | 52 | In FIRST_PERSON mode, the view direction changes while the view position 53 | remains constant. The \c start parameter is ignored, \c dir[0] is taken as 54 | a delta left/right angle in radians, and \c dir[1] is taken as a delta 55 | up/down angle in radians. 56 | 57 | In THIRD_PERSON mode, the code rotates the view position around a 58 | constant center point (selected with pickCenter()). Rotation emulates a 59 | trackball. The \c start parameter indicates a normalized location on the 60 | trackball surface, with (0.,0.) corresponding to the lower left window 61 | corner and (1.,1.) to the upper right. \c dir indicates the direction of 62 | rotation in normalized window space. Rotation alters the view up vector. 63 | */ 64 | void rotate( osg::Vec2d start, osg::Vec2d dir ); 65 | 66 | /** Pass in a delta motion vector. pan() creates a viewer coordinate 67 | system in which +x to the right and +y up, then changes 68 | the view center by \c deltaMovement in this coordinate system. */ 69 | void pan( const double ndcX, const double ndcY ); 70 | 71 | /** Dolly the camera forwards and backwards. Changes the view distance. 72 | This function is a no-op for orthographic projections. */ 73 | void dolly( const double deltaMovement ); 74 | 75 | /** Set the initial location for panning. The UI typically calls this function for 76 | a click event, then calls \c pan() for subsequent drag events. 77 | 78 | This function creates a pan plane that is used by \c pan() to calculate the world 79 | coordinate motion. To define the pan plane, this function performs an intersection 80 | test with the scene. If an intersection occurs, the picked point is used to define 81 | the plane. Otherwise, \c _viewCenter is used. The plane created passes through the 82 | point (either the intersection point or \c _viewCenter and uses \c _viewDir as the 83 | plane normal. */ 84 | void setPanStart( const double ndcX, const double ndcY ); 85 | 86 | typedef enum { 87 | FIRST_PERSON, 88 | THIRD_PERSON 89 | } ViewingCoreMode; 90 | /** Specify FIRST_PERSON or the THIRD_PERSON viewing modes. The viewing mode 91 | is THIRD_PERSON by default. The viewing mode affects interpretation of 92 | the rotate() parameters. See rotate() for more information. */ 93 | void setViewingCoreMode( ViewingCoreMode mode ); 94 | ViewingCoreMode getViewingCoreMode() const { 95 | return( _mode ); 96 | } 97 | 98 | /** Select a new view center. This function back-transforms the input NDC xy values 99 | into world space and uses them to create a LineSegmentIntersector to pick a new 100 | \c _center for the view matrix. */ 101 | void pickCenter( const double ndcX, const double ndcY ); 102 | 103 | /** Get the current eye position. */ 104 | inline osg::Vec3d getEyePosition() const { 105 | return( _viewCenter - ( _viewDir * _viewDistance ) ); 106 | } 107 | 108 | /** When the viewing mode is THIRD_PERSON, the code emulates a trackball 109 | in which mouse motion along the window boundary rolls the current view. 110 | The application can use this function to adjust the sensitivity of the 111 | emulated trackball. Smaller values produui->osgv->saveView()ce less of a roll effect, while 112 | larger values produce more pronounced roll effects. 113 | 114 | \c rollSensitivity is literally the maximum angle of the trackball's 115 | rotation axis relative to the view plane. This value is scaled by 116 | the mouse's normalized distance from the window center. 117 | \param rollSensitivity Clamped to range 0.0 to pi/2. Default is 1.3. */ 118 | void setTrackballRollSensitivity( double rollSensitivity ); 119 | 120 | /** Set world space vectors used to compute yaw/pitch/roll (see 121 | getYawPitchRoll() ). The \c up vector corresponds to pitch (pi/2) 122 | and defines a horison plane corresponding to pitch 0.0. The \c north 123 | vector corresponds to yaw 0.0. */ 124 | void setBaseUpNorth( const osg::Vec3d& up, const osg::Vec3d& north ); 125 | 126 | /** Get current yaw/pitch/roll angles for the current view. 127 | Values are computed relative to the base up and north vectors 128 | (see setBaseUpNorth() ). All return values are in degrees. 129 | \param yaw Heading value. 0.0 <= yaw < 360.0. 130 | \param pitch Elevation value. -(pi/2.0) <= pitch <= (pi/2.0). 131 | \param roll Twist value. 0.0 <= roll < 360.0. 132 | \param rightHanded Use a right-handed coordinate system to compute 133 | yaw and roll angles. Turning left increases the yaw angle when rightHanded=true, 134 | and decreases the yaw angle when rightHanded=false. Likewise, counter-clockwise 135 | roll increases the roll angle when rightHanded=true, but decreases the roll angle 136 | when rightHanded=false. */ 137 | void getYawPitchRoll( double& yaw, double& pitch, double& roll, bool rightHanded=false ) const; 138 | 139 | 140 | /** Specify whether or not to use an orthographic projection. 141 | Specify \c true to enable orthographic mode, and false to disable 142 | orthographic mode (and use perspective instead). The default is 143 | false (perspective). */ 144 | void setOrtho( bool ortho ); 145 | bool getOrtho() const { 146 | return( _ortho ); 147 | } 148 | 149 | /** Set the aspect ratio. This value is used in the computation of the projection 150 | matrix. */ 151 | void setAspect( double aspect ) { 152 | _aspect = aspect; 153 | } 154 | 155 | /** Modify \c proj so that it used \c _fovy for its field of view in y, 156 | maintaining the same aspect ratio, and near and far plane values. This 157 | function works for both symmetrical and assymetrical view volumes. */ 158 | void updateFovy( osg::Matrixd& proj ) const; 159 | /** Conpute a projection matrix from specified aspect and fovy. Creates 160 | a symmetrical projection matrix. zNear and zFar planes are computed from 161 | the proximity of view position to scene data. */ 162 | osg::Matrixd computeProjection() const; 163 | 164 | /** Set the field of view in y (fovy) in degrees. Default is 30 degrees. */ 165 | void setFovy( double fovy ); 166 | double getFovy() const { 167 | return( _fovy ); 168 | } 169 | double getFovyRadians() const; 170 | 171 | /** Scale fovy up or down, using the scale value set with setFovyScale(). */ 172 | void fovyScaleUp(); 173 | void fovyScaleDown(); 174 | 175 | /** Percentage to increase the fovy in a fovyScaleUp() call. 176 | For example, to increase fovy by 120% in that call, pass 1.2. Default is 177 | 1.1 (110%). The inverse (1.0 / fovyScale) is used in the fovyScaleDown() 178 | call. */ 179 | void setFovyScale( double fovyScale ) { 180 | _fovyScale = fovyScale; 181 | } 182 | double getFovyScale() const { 183 | return( _fovyScale ); 184 | } 185 | 186 | /** Default is to clamp fovy scaling to the range (1.0,160.0). Pass \c false 187 | as first paramter to disable clamping. */ 188 | void setClampFovyScale( bool clamp, osg::Vec2d clampFovyRange=osg::Vec2d(1.0,140.0) ); 189 | osg::Vec2d getClampFovyScale() { 190 | return _clampFovyRange; 191 | } 192 | 193 | void viewTop(); 194 | void viewBottom(); 195 | void viewRight(); 196 | void viewLeft(); 197 | void viewFront(); 198 | void viewBack(); 199 | 200 | //Used for moving the wand 201 | osg::Vec3d findDeltaOnPanPlane(const double ndcX1, const double ndcY1, 202 | const double ndcX2, const double ndcY2); 203 | 204 | osg::Vec3d getViewDir() { 205 | return _viewDir; 206 | } 207 | 208 | void getZNearZFarProj(double &zNear, double &zFar, osg::Matrixd &projMat); 209 | 210 | void fitToScreen(); 211 | 212 | void saveView(std::stringstream &stream); 213 | void loadView(std::stringstream &stream); 214 | 215 | osg::Vec3d getViewCenter() const { 216 | return _viewCenter; 217 | } 218 | protected: 219 | ~ViewingCore(); 220 | 221 | 222 | bool intersect( osg::Vec3d& result, const osg::Vec3d& farPoint ); 223 | 224 | /** OSG doesn't appear to have a utility function to intersect a plane and a ray. 225 | TBD This should probably go in osgWorks. */ 226 | bool intersectPlaneRay( osg::Vec3d& result, const osg::Vec4d& plane, const osg::Vec3d& p0, const osg::Vec3d& p1 ); 227 | 228 | double _trackballRollSensitivity; 229 | osg::Vec3d _baseUp, _baseNorth; 230 | osg::Vec3d _lastCross, _viewUp, _viewDir, _viewCenter; 231 | double _viewDistance; 232 | double _initialViewDistance; 233 | 234 | osg::Vec4d _panPlane; 235 | 236 | ViewingCoreMode _mode; 237 | 238 | 239 | // Projection matrix and field of view support. 240 | bool _ortho; 241 | osg::ref_ptr< osg::Node > _scene; 242 | double _aspect; 243 | 244 | double _fovy; 245 | double _fovyScale; 246 | bool _clampFovyScale; 247 | osg::Vec2d _clampFovyRange; 248 | double _orthoBottom, _orthoTop; 249 | }; 250 | 251 | 252 | 253 | 254 | 255 | // __VIEWING_CORE_H__ 256 | #endif 257 | -------------------------------------------------------------------------------- /src/OSGWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "OSGWidget.h" 2 | #include "OsgDocument.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | OSGWidget::OSGWidget(QWidget *parent) 15 | : QGLWidget(parent) 16 | , m_mouseButtons(Qt::NoButton) 17 | , m_mouseModifiers(Qt::NoModifier) 18 | , m_mouseMode(MM_ORBIT) 19 | , m_viewingCore(new ViewingCore) 20 | , m_lastMouseEventTime(QTime::currentTime()) 21 | , m_mouseReleaseJustHappened(false) 22 | , m_throwTimeTolerance(100) 23 | , m_throwDistTolerance(0.1) 24 | { 25 | setObjectName("osgv"); 26 | // Strong focus policy needs to be set to capture keyboard events 27 | setFocusPolicy(Qt::StrongFocus); 28 | setMouseTracking(true); // get mouse move events even without button press 29 | 30 | // Construct the embedded graphics window 31 | m_osgGraphicsWindow = new osgViewer::GraphicsWindowEmbedded(0,0,width(),height()); 32 | getCamera()->setGraphicsContext(m_osgGraphicsWindow); 33 | 34 | // Set up the camera 35 | getCamera()->setViewport(new osg::Viewport(0,0,width(),height())); 36 | getCamera()->setProjectionMatrixAsPerspective(30.0f, 37 | static_cast(width())/static_cast(height()), 38 | 1.0f, 39 | 10000.0f); 40 | 41 | // By default draw everthing that has even 1 bit set in the nodemask 42 | getCamera()->setCullMask( (unsigned)~0 ); 43 | getCamera()->setDataVariance(osg::Object::DYNAMIC); 44 | 45 | // As of July 2010 there wasn't really a good way to multi-thread OSG 46 | // under Qt so just set the threading model to be SingleThreaded 47 | setThreadingModel(osgViewer::Viewer::SingleThreaded); 48 | 49 | // draw both sides of polygons 50 | setLightingTwoSided(); 51 | 52 | // wake up and re-draw the window every so often 53 | connect(&m_redrawTimer, SIGNAL(timeout()), this, SLOT(updateGL())); 54 | m_redrawTimer.setSingleShot(true); 55 | m_redrawTimer.start(20); 56 | //m_frameTimer.start(); 57 | 58 | // Signal-slot to handle animating the camera for 'tossing' the geometry 59 | connect(&m_animateTimer, SIGNAL(timeout()), 60 | this, SLOT(animateCameraThrow())); 61 | 62 | // Set the minimum size for this viewer window 63 | setMinimumSize(200, 200); 64 | } 65 | 66 | 67 | //#define DEBUG_MOUSE 1 68 | 69 | void OSGWidget::mousePressEvent( QMouseEvent* event ) 70 | { 71 | m_animateTimer.stop(); 72 | 73 | m_mouseReleaseJustHappened = false; 74 | 75 | m_lastMouseEventNDCoords = getNormalized(event->x(), event->y()); 76 | 77 | m_mouseButtons |= event->buttons(); 78 | 79 | m_mouseModifiers = event->modifiers(); 80 | 81 | // handle overrides 82 | if (m_mouseButtons == Qt::LeftButton) { 83 | if (m_mouseModifiers == Qt::ShiftModifier) { 84 | pushState(); 85 | m_viewingCore->setViewingCoreMode(ViewingCore::THIRD_PERSON); 86 | setMouseMode(MM_PAN); 87 | } else if (m_mouseModifiers == Qt::ControlModifier) { 88 | pushState(); 89 | m_viewingCore->setViewingCoreMode(ViewingCore::THIRD_PERSON); 90 | setMouseMode(MM_ORBIT); 91 | } else if ((unsigned)m_mouseModifiers == (unsigned)(Qt::ControlModifier | Qt::ShiftModifier) ) { 92 | pushState(); 93 | m_viewingCore->setViewingCoreMode(ViewingCore::THIRD_PERSON); 94 | setMouseMode(MM_ZOOM); 95 | } 96 | } else if (m_mouseButtons == Qt::RightButton) { 97 | m_viewingCore->pickCenter(m_lastMouseEventNDCoords.x(), 98 | m_lastMouseEventNDCoords.y() ); 99 | } 100 | 101 | // Do the job asked 102 | if (m_mouseMode & (MM_PAN|MM_ROTATE|MM_ORBIT|MM_ZOOM) ) 103 | m_viewingCore->setPanStart( m_lastMouseEventNDCoords.x(), m_lastMouseEventNDCoords.y() ); 104 | else if (m_mouseMode & MM_PICK_CENTER) { 105 | m_viewingCore->pickCenter(m_lastMouseEventNDCoords.x(), 106 | m_lastMouseEventNDCoords.y() ); 107 | } 108 | 109 | m_lastMouseEventTime.start(); 110 | } 111 | 112 | 113 | void OSGWidget::mouseReleaseEvent( QMouseEvent* event ) 114 | { 115 | m_mouseReleaseJustHappened = true; 116 | 117 | m_lastMouseEventNDCoords = getNormalized(event->x(), event->y()); 118 | 119 | m_mouseButtons &= event->buttons(); 120 | 121 | if (m_mouseModifiers != Qt::NoModifier) { 122 | popState(); 123 | } 124 | 125 | m_mouseModifiers = Qt::NoModifier; 126 | m_lastMouseEventTime.start(); 127 | } 128 | 129 | void OSGWidget::mouseMoveEvent( QMouseEvent* event ) 130 | { 131 | // either a button is pressed or a mouse release just happened 132 | 133 | osg::Vec2d currentNDC = getNormalized(event->x(), event->y()); 134 | 135 | if ( m_mouseReleaseJustHappened) { 136 | // We are potentially starting a throw of the camera 137 | // Look to see if we meet the tolerance criteria for space and time 138 | // for starting camera animation 139 | 140 | int deltaT = m_lastMouseEventTime.elapsed(); 141 | 142 | if (deltaT < m_throwTimeTolerance) { 143 | // compute the motion of the cursor 144 | 145 | float distance = inchesToLastMouseEvent(event); 146 | if (distance > m_throwDistTolerance) { 147 | // start animation 148 | 149 | m_animateStart = m_lastMouseEventNDCoords; 150 | 151 | osg::Vec2d kalmanNDC = (currentNDC + m_predictedPos) * 0.5; 152 | m_animateDelta = kalmanNDC - m_animateStart; 153 | 154 | m_animateTimer.start(deltaT); 155 | } 156 | } 157 | m_mouseReleaseJustHappened = false; 158 | return; 159 | } 160 | 161 | 162 | if (m_mouseButtons == Qt::NoButton) 163 | return; 164 | 165 | osg::Vec2d delta = currentNDC - m_lastMouseEventNDCoords; 166 | 167 | switch (m_mouseMode) { 168 | case MM_ORBIT: 169 | m_viewingCore->rotate( m_lastMouseEventNDCoords, delta ); 170 | break; 171 | case MM_PAN: 172 | m_viewingCore->pan(delta.x(), delta.y()); 173 | break; 174 | case MM_ZOOM: { 175 | double tempScale = m_viewingCore->getFovyScale(); 176 | m_viewingCore->setFovyScale(1.03); 177 | 178 | if(delta.y() > 0) 179 | m_viewingCore->fovyScaleDown(); 180 | 181 | if(delta.y() < 0) 182 | m_viewingCore->fovyScaleUp(); 183 | 184 | m_viewingCore->setFovyScale(tempScale); 185 | break; 186 | } 187 | case MM_ROTATE: 188 | m_viewingCore->rotate( m_lastMouseEventNDCoords, delta ); 189 | break; 190 | default: 191 | break; 192 | } 193 | 194 | m_predictedPos.set(currentNDC.x() + delta.x(), currentNDC.y() + delta.y()); 195 | 196 | m_lastMouseEventNDCoords = currentNDC; 197 | m_lastMouseEventTime.start(); 198 | } 199 | 200 | void OSGWidget::wheelEvent(QWheelEvent *event) 201 | { 202 | if(event->delta() > 0) 203 | m_viewingCore->dolly(0.5); 204 | else 205 | m_viewingCore->dolly(-0.5); 206 | } 207 | 208 | void OSGWidget::paintGL() 209 | { 210 | // Update the camera 211 | osg::Camera *cam = this->getCamera(); 212 | const osg::Viewport* vp = cam->getViewport(); 213 | 214 | m_viewingCore->setAspect(vp->width() / vp->height()); 215 | 216 | cam->setViewMatrix(m_viewingCore->getInverseMatrix()); 217 | cam->setProjectionMatrix(m_viewingCore->computeProjection()); 218 | 219 | // Invoke the OSG traversal pipeline 220 | frame(); 221 | 222 | // Start the timer again 223 | m_redrawTimer.start(); 224 | } 225 | 226 | 227 | 228 | void OSGWidget::animateCameraThrow() 229 | { 230 | switch (m_mouseMode) { 231 | case MM_ORBIT: 232 | m_viewingCore->rotate( m_animateStart, m_animateDelta ); 233 | break; 234 | case MM_PAN: 235 | m_viewingCore->pan(m_animateDelta.x(), m_animateDelta.y()); 236 | break; 237 | case MM_ZOOM: { 238 | double tempScale = m_viewingCore->getFovyScale(); 239 | m_viewingCore->setFovyScale(1.03); 240 | 241 | if(m_animateDelta.y() > 0) 242 | m_viewingCore->fovyScaleDown(); 243 | else 244 | m_viewingCore->fovyScaleUp(); 245 | 246 | m_viewingCore->setFovyScale(tempScale); 247 | break; 248 | } 249 | case MM_ROTATE: 250 | m_viewingCore->rotate( m_animateStart, m_animateDelta ); 251 | break; 252 | default: 253 | break; 254 | } 255 | } 256 | 257 | 258 | 259 | void OSGWidget::setScene(osg::Node *root) 260 | { 261 | this->setSceneData(root); 262 | m_viewingCore->setSceneData(root); 263 | m_viewingCore->fitToScreen(); 264 | } 265 | 266 | void OSGWidget::setLightingTwoSided() 267 | { 268 | osg::ref_ptr lm = new osg::LightModel; 269 | lm->setTwoSided(true); 270 | lm->setAmbientIntensity(osg::Vec4(0.1f,0.1f,0.1f,1.0f)); 271 | 272 | osg::StateSet *ss; 273 | 274 | for (int i=0 ; i < 2 ; i++ ) { 275 | ss = ((osgViewer::Renderer *)getCamera() 276 | ->getRenderer())->getSceneView(i)->getGlobalStateSet(); 277 | 278 | ss->setAttributeAndModes(lm, osg::StateAttribute::ON); 279 | } 280 | } 281 | 282 | void OSGWidget::resizeGL(int width, int height) 283 | { 284 | m_osgGraphicsWindow->getEventQueue()->windowResize(0, 0, width, height ); 285 | m_osgGraphicsWindow->resized(0,0,width,height); 286 | } 287 | 288 | osg::Vec2d OSGWidget::getNormalized(const int ix, const int iy) 289 | { 290 | int x, y, width, height; 291 | osg::Vec2d ndc; 292 | 293 | // we don't really need the x, y values but the width/height are important 294 | m_osgGraphicsWindow->getWindowRectangle(x, y, width, height); 295 | int center = width/2; 296 | ndc[0] = ((double)ix - (double)center) / (double)center; 297 | if (ndc[0] > 1.0) ndc[0] = 1.0; 298 | 299 | center = height/2; 300 | int invertedY = height - iy; 301 | ndc[1] = ((double)invertedY - (double)center) / (double)center; 302 | if (ndc[1] > 1.0) ndc[1] = 1.0; 303 | 304 | return ndc; 305 | } 306 | 307 | void OSGWidget::setMouseMode(MouseMode mode) 308 | { 309 | m_mouseMode = mode; 310 | 311 | if(mode == MM_ROTATE) 312 | m_viewingCore->setViewingCoreMode( ViewingCore::FIRST_PERSON ); 313 | else { 314 | m_viewingCore->setViewingCoreMode( ViewingCore::THIRD_PERSON ); 315 | } 316 | 317 | emit mouseModeChanged(mode); 318 | m_animateTimer.stop(); 319 | } 320 | 321 | 322 | 323 | float OSGWidget::inchesToLastMouseEvent(QMouseEvent* thisMouseEvent) 324 | { 325 | // we don't really need the x, y values but the width/height are important 326 | int x, y, width, height; 327 | m_osgGraphicsWindow->getWindowRectangle(x, y, width, height); 328 | 329 | // un-normalize last event 330 | float center = width/2.0; 331 | int oldX = (m_lastMouseEventNDCoords.x() * center) + center; 332 | 333 | center = height/2.0; 334 | int oldY = (m_lastMouseEventNDCoords.y() * center) + center; 335 | 336 | float deltaX = thisMouseEvent->x() - oldX; 337 | float deltaY = (height - thisMouseEvent->y()) - oldY; 338 | 339 | float distXinches = deltaX / (float)qApp->desktop()->logicalDpiX(); 340 | float distYinches = deltaY / (float)qApp->desktop()->logicalDpiY(); 341 | 342 | float distSquared = distXinches * distXinches + 343 | distYinches * distYinches; 344 | 345 | return sqrt(distSquared); 346 | } 347 | 348 | 349 | void OSGWidget::pushState() 350 | { 351 | stashState state; 352 | state.mm = m_mouseMode; 353 | state.vcm = m_viewingCore->getViewingCoreMode(); 354 | m_stashState.push_back(state); 355 | } 356 | 357 | 358 | void OSGWidget::popState() 359 | { 360 | if (m_stashState.size() < 1) 361 | return; 362 | 363 | stashState state = m_stashState.last(); 364 | m_stashState.pop_back(); 365 | m_viewingCore->setViewingCoreMode(state.vcm); 366 | setMouseMode(state.mm); 367 | } 368 | 369 | -------------------------------------------------------------------------------- /src/OsgItemModel.cpp: -------------------------------------------------------------------------------- 1 | #include "OsgItemModel.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | static bool debugModel = false; 11 | #define modelDebug if (debugModel) qDebug 12 | 13 | static QString stringFromRole(const int role); 14 | 15 | OsgItemModel::OsgItemModel(QObject * parent) 16 | : QAbstractItemModel(parent) 17 | , m_root(new osg::Group) 18 | , m_loadedModel(new osg::MatrixTransform) 19 | , m_clipBoard(new osg::Group) 20 | { 21 | m_root->setName("__root"); 22 | m_loadedModel->setName("__loadedModel"); 23 | 24 | m_root->addChild(m_loadedModel); 25 | m_root->setUserValue("fred", 10); 26 | 27 | m_clipBoard->setName("__clipBoard"); 28 | } 29 | 30 | int OsgItemModel::columnCount(const QModelIndex & parent) const 31 | { 32 | int numberOfColumns = 1; 33 | if (!parent.isValid()) { 34 | // looking at root node 35 | if (m_loadedModel->getNumChildren() > 0) 36 | numberOfColumns = 3; 37 | } else { 38 | numberOfColumns = 3; // XXX how to tell the real number of columns? 39 | } 40 | 41 | modelDebug("columnCount(%d,%d) = %d", 42 | parent.row(), parent.column(), numberOfColumns); 43 | 44 | return numberOfColumns; 45 | } 46 | 47 | static void setObjectNameFromParent(osg::ref_ptr object) 48 | { 49 | if (object->getName().size() > 0) 50 | return; 51 | 52 | QString newName; 53 | 54 | if (osg::Drawable *drawable = dynamic_cast(object.get())) { 55 | if (drawable->getNumParents() == 0) { 56 | newName = QString::asprintf("Lonely_%s", object->className()); 57 | } else { 58 | setObjectNameFromParent(drawable->getParent(0)); // make sure parent is named 59 | newName = QString::asprintf("%s_%s", 60 | drawable->getParent(0)->getName().c_str(), 61 | object->className() ); 62 | } 63 | } else if (osg::Node *node = dynamic_cast(object.get())) { 64 | if (node->getNumParents() == 0) { 65 | newName = QString::asprintf("Lonely_%s", object->className()); 66 | } else { 67 | setObjectNameFromParent(node->getParent(0)); // make sure parent is named 68 | newName = QString::asprintf("%s_%s", 69 | node->getParent(0)->getName().c_str(), 70 | object->className() ); 71 | } 72 | } 73 | object->setName(qPrintable(newName)); 74 | } 75 | 76 | QVariant OsgItemModel::data(const QModelIndex &index, int role) const 77 | { 78 | QVariant variant; 79 | if (!index.isValid()) { 80 | modelDebug("data() on invalid\n"); 81 | return QVariant(); 82 | } 83 | 84 | osg::ref_ptr object = getObjectFromModelIndex(index); 85 | 86 | #if 0 87 | modelDebug("data(%d %d %s) ", index.row(), index.column(), 88 | qPrintable(stringFromRole(role))); 89 | #else 90 | if (role == Qt::DisplayRole) 91 | modelDebug("data(%d %d %s) ", index.row(), index.column(), object->getName().c_str()); 92 | #endif 93 | 94 | switch (role) { 95 | case Qt::DisplayRole: { 96 | 97 | switch (index.column()) { 98 | case 0: 99 | if (object->getName().size() == 0) 100 | setObjectNameFromParent(object); 101 | 102 | variant = QVariant(QString::fromStdString(object->getName())); 103 | break; 104 | case 1: 105 | variant = QVariant(QString(object->className())); break; 106 | 107 | case 2: { 108 | osg::Node *node = dynamic_cast(object.get()); 109 | if (node) { 110 | variant = QVariant(maskToString(node->getNodeMask())); 111 | } 112 | break; 113 | } 114 | default: 115 | break; 116 | } 117 | break; 118 | } 119 | default: 120 | variant = QVariant(); 121 | break; 122 | } 123 | return variant; 124 | } 125 | 126 | Qt::ItemFlags OsgItemModel::flags(const QModelIndex &index) const 127 | { 128 | osg::ref_ptr object = getObjectFromModelIndex(index); 129 | 130 | modelDebug("flags %d %d \"%s\"", 131 | index.row(), index.column(), 132 | object->getName().c_str()); 133 | 134 | Qt::ItemFlags flags = Qt::ItemIsEnabled ; 135 | if (index.column() == 0 || index.column() == 2) { 136 | flags |= Qt::ItemIsEditable | Qt::ItemIsSelectable; 137 | } 138 | 139 | return flags; 140 | } 141 | 142 | osg::ref_ptr OsgItemModel::getObjectFromModelIndex(const QModelIndex &index) const 143 | { 144 | if (index.isValid()) { 145 | osg::ref_ptr obj = static_cast(index.internalPointer()); 146 | if (obj) 147 | return obj; 148 | } 149 | return m_loadedModel; 150 | } 151 | 152 | 153 | bool OsgItemModel::hasChildren ( const QModelIndex & parent ) const 154 | { 155 | osg::ref_ptr object = getObjectFromModelIndex(parent); 156 | 157 | modelDebug("hasChildren(%s %d %d) ... ", 158 | object->getName().c_str(), 159 | parent.row(), parent.column()); 160 | 161 | unsigned numberOfChildren = 0; 162 | 163 | if (osg::Geode *geode = dynamic_cast(object.get())) { 164 | numberOfChildren = geode->getNumDrawables(); 165 | } else if (osg::Group *group = dynamic_cast(object.get())) { 166 | numberOfChildren = group->getNumChildren(); 167 | } else if (dynamic_cast(object.get())) { 168 | numberOfChildren = 0; 169 | } else { 170 | abort(); 171 | } 172 | 173 | modelDebug("hasChildren(%s %d %d) = %u", 174 | object->getName().c_str(), 175 | parent.row(), 176 | parent.column(), 177 | numberOfChildren); 178 | 179 | return (numberOfChildren > 0); 180 | } 181 | 182 | 183 | QVariant OsgItemModel::headerData(int section, Qt::Orientation orientation, int role) const 184 | { 185 | modelDebug("headerData(%d, , )\n", section); 186 | if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 187 | switch (section) { 188 | case 0: return QVariant(QString("Name")); 189 | case 1: return QVariant(QString("Type")); 190 | case 2: return QVariant(QString("mask")); 191 | default: return QVariant(QString("col %1").arg(section)); 192 | } 193 | } 194 | return QVariant(); 195 | } 196 | 197 | QModelIndex OsgItemModel::index(int row, int column, const QModelIndex &parent) const 198 | { 199 | qDebug("parent row:%d col:%d Valid: %s", 200 | parent.row(), parent.column(), parent.isValid()?"true":"false"); 201 | 202 | osg::ref_ptr parentItem = getObjectFromModelIndex(parent); 203 | 204 | if (osg::Group *group = dynamic_cast(parentItem.get())) { 205 | if (group->getNumChildren() > (unsigned)row) 206 | return createIndex(row, column, group->getChild(row)); 207 | } else if (osg::Geode *geode = dynamic_cast(parentItem.get())) { 208 | if (geode->getNumDrawables() > (unsigned)row) { 209 | return createIndex(row, column, geode->getDrawable(row)); 210 | } 211 | } 212 | 213 | return QModelIndex(); 214 | } 215 | 216 | QModelIndex OsgItemModel::modelIndexFromNode(osg::ref_ptr ptr, 217 | int column) const 218 | { 219 | if (!ptr.valid() || ptr == m_loadedModel || ptr->getNumParents() <= 0) 220 | return QModelIndex(); 221 | 222 | osg::ref_ptr parent = ptr->getParent(0); 223 | int idx = parent->getChildIndex(ptr); 224 | return createIndex(idx, column, ptr); 225 | } 226 | 227 | void OsgItemModel::insertNode(osg::ref_ptr parent, 228 | osg::ref_ptr newChild, 229 | int childPositionInParent, 230 | int row) 231 | { 232 | if (!parent.valid()) abort(); 233 | 234 | QModelIndex pIndex = this->modelIndexFromNode(parent, 0); 235 | beginInsertRows(pIndex, row, row); 236 | parent->insertChild(childPositionInParent, newChild); 237 | endInsertRows(); 238 | } 239 | 240 | QModelIndex OsgItemModel::parentOfNode(osg::Node *childNode) const 241 | { 242 | if (childNode == m_loadedModel) 243 | return QModelIndex(); 244 | 245 | // get the osg parent 246 | // XXX this assumes one parent 247 | osg::ref_ptr parent = childNode->getParent(0); 248 | 249 | return modelIndexFromNode(parent, 0); 250 | } 251 | 252 | QModelIndex OsgItemModel::parentOfDrawable(osg::Drawable *childDrawable) const 253 | { 254 | if (childDrawable->getNumParents() == 0) 255 | return QModelIndex(); 256 | 257 | osg::ref_ptr parentNode = childDrawable->getParent(0); 258 | 259 | return modelIndexFromNode(parentNode, 0); 260 | } 261 | 262 | 263 | QModelIndex OsgItemModel::parent(const QModelIndex &index) const 264 | { 265 | if (! index.isValid()) 266 | return QModelIndex(); 267 | 268 | osg::ref_ptr childObject = getObjectFromModelIndex(index); 269 | 270 | if (osg::Node *node = dynamic_cast(childObject.get())) 271 | return parentOfNode(node); 272 | 273 | osg::Drawable *drawable = dynamic_cast(childObject.get()); 274 | if (!drawable) { 275 | abort(); 276 | } 277 | return parentOfDrawable(drawable); 278 | } 279 | 280 | void OsgItemModel::printNode(osg::ref_ptr n, const int level) const 281 | { 282 | modelDebug("%*s%s %s\n", level, "", n->getName().c_str(), qPrintable(maskToString(n->getNodeMask()))); 283 | 284 | osg::ref_ptr g = n->asGroup(); 285 | if (g) { 286 | int kids = g->getNumChildren(); 287 | for (int kid=0 ; kid < kids ; kid++) { 288 | osg::ref_ptr child = g->getChild(kid); 289 | printNode(child, level+2); 290 | } 291 | return; 292 | } 293 | } 294 | 295 | QString OsgItemModel::maskToString(const osg::Node::NodeMask mask) const 296 | { 297 | QString maskString = QString::asprintf("%08x", (unsigned)mask); 298 | modelDebug("maskToString %s", qPrintable(maskString)); 299 | return maskString; 300 | } 301 | void OsgItemModel::printTree() const 302 | { 303 | printNode(m_loadedModel, 2); 304 | } 305 | 306 | 307 | int OsgItemModel::rowCount(const QModelIndex &parent) const 308 | { 309 | osg::ref_ptr object = getObjectFromModelIndex(parent); 310 | int kids = 0; 311 | 312 | if (osg::Group *group = dynamic_cast(object.get())) { 313 | kids = group->getNumChildren(); 314 | } else if (osg::Geode *geode = dynamic_cast(object.get())) { 315 | kids = geode->getNumDrawables(); 316 | } 317 | 318 | modelDebug("rowCount %d %d %s == %d\n", 319 | parent.row(), 320 | parent.column(), 321 | object->getName().c_str(), kids); 322 | 323 | return kids; 324 | } 325 | 326 | bool OsgItemModel::setObjectName(const QModelIndex & index, 327 | const QVariant & value) 328 | { 329 | getObjectFromModelIndex(index)->setName(qPrintable(value.toString())); 330 | return true; 331 | } 332 | bool OsgItemModel::setObjectMask(const QModelIndex & index, 333 | const QVariant & value) 334 | { 335 | bool ok = false; 336 | osg::ref_ptr object = getObjectFromModelIndex(index); 337 | if (osg::Node *node = dynamic_cast(object.get())) { 338 | 339 | int number = value.toString().toUInt(&ok, 16); 340 | if (ok) { 341 | node->setNodeMask(number); 342 | } else { 343 | qDebug("set mask: %s %d", qPrintable(value.toString()), number); 344 | } 345 | } 346 | return ok; 347 | } 348 | 349 | bool OsgItemModel::setData(const QModelIndex & index, 350 | const QVariant & value, 351 | int role ) 352 | { 353 | qDebug("setData(%d %d %s) = \"%s\"", 354 | index.row(), index.column(), 355 | qPrintable(stringFromRole(role)), 356 | qPrintable(value.toString())); 357 | 358 | if (role != Qt::EditRole || value.toString().size() <= 0) 359 | return false; 360 | 361 | bool dataWasSet = false; 362 | 363 | switch (index.column()) { 364 | case 0: dataWasSet = setObjectName(index, value); break; 365 | case 2: dataWasSet = setObjectMask(index, value); break; 366 | default: 367 | break; 368 | } 369 | 370 | if (dataWasSet) 371 | emit dataChanged(index, index); 372 | 373 | return dataWasSet; 374 | } 375 | 376 | void OsgItemModel::importFileByName(const QString fileName) 377 | { 378 | osg::Node *loaded = osgDB::readNodeFile(fileName.toStdString()); 379 | 380 | 381 | if (!loaded) 382 | return; 383 | 384 | 385 | if (loaded->getName().size() == 0) 386 | loaded->setName(basename(qPrintable(fileName))); 387 | 388 | int childNumber = m_loadedModel->getNumChildren(); 389 | loaded->setUserValue("childIndex", childNumber); 390 | 391 | if (childNumber == 0) { 392 | beginInsertColumns(createIndex(-1, -1), 1, 2); 393 | insertNode(m_loadedModel, loaded, m_loadedModel->getNumChildren(), 0); 394 | endInsertColumns(); 395 | } else { 396 | insertNode(m_loadedModel, loaded, m_loadedModel->getNumChildren(), 0); 397 | } 398 | } 399 | 400 | bool OsgItemModel::saveToFileByName(const QString fileName) 401 | { 402 | if (m_loadedModel->getNumChildren() == 1) { 403 | return osgDB::writeNodeFile(*m_loadedModel->getChild(0), qPrintable(fileName)); 404 | } 405 | 406 | osg::Group *writeGroup = new osg::Group(*m_loadedModel); 407 | writeGroup->setName(qPrintable(fileName)); 408 | return osgDB::writeNodeFile(*writeGroup, fileName.toStdString().c_str()); 409 | } 410 | 411 | static QString stringFromRole(const int role) 412 | { 413 | switch (role) { 414 | case Qt::DisplayRole: return QString("DisplayRole"); break; 415 | case Qt::DecorationRole: return QString("DecorationRole"); break; 416 | case Qt::EditRole: return QString("EditRole"); break; 417 | case Qt::ToolTipRole: return QString("ToolTipRole"); break; 418 | case Qt::StatusTipRole: return QString("StatusTipRole"); break; 419 | case Qt::WhatsThisRole: return QString("WhatsThisRole"); break; 420 | case Qt::SizeHintRole: return QString("SizeHintRole"); break; 421 | case Qt::FontRole: return QString("FontRole"); break; 422 | case Qt::TextAlignmentRole: return QString("TextAlignmentRole"); break; 423 | case Qt::BackgroundRole: return QString("BackgroundRole"); break; 424 | case Qt::ForegroundRole: return QString("ForegroundRole"); break; 425 | case Qt::CheckStateRole: return QString("CheckStateRole"); break; 426 | case Qt::InitialSortOrderRole: return QString("InitialSortOrderRole"); break; 427 | case Qt::AccessibleTextRole: return QString("AccessibleTextRole"); break; 428 | case Qt::AccessibleDescriptionRole: return QString("AccessibleDescriptionRole"); break; 429 | } 430 | 431 | if (role >= Qt::UserRole) 432 | return QString("UserRole"); 433 | 434 | return QString("invalidRole"); 435 | } 436 | -------------------------------------------------------------------------------- /src/ViewingCore.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Skew Matrix Software LLC. All rights reserved. 2 | 3 | 4 | #include "ViewingCore.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | ViewingCore::ViewingCore() 19 | : _trackballRollSensitivity( 1.3 ), 20 | _baseUp( osg::Vec3d( 0., 0., 1. ) ), 21 | _baseNorth( osg::Vec3d( 0., 1., 0. ) ), 22 | _lastCross( osg::Vec3d( 1., 0., 0. ) ), 23 | _viewUp( osg::Vec3d( 0., 0., 1. ) ), 24 | _viewDir( osg::Vec3d( 0., 1., 0. ) ), 25 | _viewCenter( osg::Vec3d( 0., 0., 0. ) ), 26 | _viewDistance( 1. ), 27 | _panPlane( osg::Vec4d( _viewDir, 0. ) ), 28 | _mode( THIRD_PERSON ), 29 | _ortho( false ), 30 | _scene( NULL ), 31 | _aspect( 1.0 ), 32 | _fovy( 30.0 ), 33 | _fovyScale( 1.1 ), 34 | _clampFovyScale( true ), 35 | _clampFovyRange( osg::Vec2d( 5.0, 160.0 ) ), 36 | _orthoBottom( 0.0 ), 37 | _orthoTop( 0.0 ) 38 | { 39 | } 40 | 41 | ViewingCore::ViewingCore( const ViewingCore& rhs, const osg::CopyOp& copyop ) 42 | : Object( rhs, copyop ), 43 | _trackballRollSensitivity( rhs._trackballRollSensitivity ), 44 | _baseUp( rhs._baseUp ), 45 | _baseNorth( rhs._baseNorth ), 46 | _lastCross( rhs._lastCross ), 47 | _viewUp( rhs._viewUp ), 48 | _viewDir( rhs._viewDir ), 49 | _viewCenter( rhs._viewCenter ), 50 | _viewDistance( rhs._viewDistance ), 51 | _panPlane( rhs._panPlane ), 52 | _mode( rhs._mode ), 53 | _ortho( rhs._ortho ), 54 | _scene( rhs._scene ), 55 | _aspect( rhs._aspect ), 56 | _fovy( rhs._fovy ), 57 | _fovyScale( rhs._fovyScale ), 58 | _clampFovyScale( rhs._clampFovyScale ), 59 | _clampFovyRange( rhs._clampFovyRange ), 60 | _orthoBottom( rhs._orthoBottom ), 61 | _orthoTop( rhs._orthoTop ) 62 | { 63 | } 64 | 65 | ViewingCore::~ViewingCore() 66 | { 67 | } 68 | 69 | void ViewingCore::setSceneData( osg::Node* scene ) 70 | { 71 | _scene = scene; 72 | 73 | const osg::BoundingSphere& bs = _scene->getBound(); 74 | _viewCenter = bs._center; 75 | } 76 | 77 | void ViewingCore::computeInitialView() 78 | { 79 | _viewUp = _baseUp; 80 | _viewDir = _baseNorth; 81 | 82 | _viewCenter = _scene->getBound().center(); 83 | 84 | // tan( fovy/2. ) = bs.radius / distance 85 | // Solve for distance: 86 | // distance = bs.radius / tan( fovy/2. ) 87 | _fovy = 30; 88 | float distance = _scene->getBound().radius() / 89 | tan( osg::DegreesToRadians( _fovy/2. ) ); 90 | 91 | _viewDistance = _initialViewDistance = distance; 92 | 93 | _orthoTop = tan( getFovyRadians() * 0.5 ) * _viewDistance; 94 | _orthoBottom = -_orthoTop; 95 | } 96 | 97 | void ViewingCore::fitToScreen() 98 | { 99 | _viewCenter = _scene->getBound().center(); 100 | 101 | // tan( fovy/2. ) = bs.radius / distance 102 | // Solve for distance: 103 | // distance = bs.radius / tan( fovy/2. ) 104 | _fovy = 30; 105 | float distance = _scene->getBound().radius() / 106 | tan( osg::DegreesToRadians( _fovy/2. ) ); 107 | 108 | _viewDistance = _initialViewDistance = distance; 109 | 110 | _orthoTop = tan( getFovyRadians() * 0.5 ) * _viewDistance; 111 | _orthoBottom = -_orthoTop; 112 | } 113 | 114 | 115 | // 116 | // View matrix support 117 | // 118 | 119 | osg::Matrixd ViewingCore::getMatrix() const 120 | { 121 | const osg::Vec3d& d = _viewDir; 122 | const osg::Vec3d& u = _viewUp; 123 | osg::Vec3d r = d ^ u; 124 | const osg::Vec3d p = getEyePosition(); 125 | 126 | osg::Matrixd m = osg::Matrixd( 127 | r[0], r[1], r[2], 0.0, 128 | u[0], u[1], u[2], 0.0, 129 | -d[0], -d[1], -d[2], 0.0, 130 | p[0], p[1], p[2], 1.0 ); 131 | return( m ); 132 | } 133 | osg::Matrixd ViewingCore::getOrientationMatrix() const 134 | { 135 | const osg::Vec3d& d = _viewDir; 136 | const osg::Vec3d& u = _viewUp; 137 | osg::Vec3d r = d ^ u; 138 | 139 | osg::Matrixd m = osg::Matrixd( 140 | r[0], r[1], r[2], 0.0, 141 | u[0], u[1], u[2], 0.0, 142 | -d[0], -d[1], -d[2], 0.0, 143 | 0.0, 0.0, 0.0, 1.0 ); 144 | return( m ); 145 | } 146 | osg::Matrixd ViewingCore::getInverseMatrix() const 147 | { 148 | osg::Matrixd m; 149 | m.invert( getMatrix() ); 150 | return( m ); 151 | } 152 | 153 | void ViewingCore::rotate( osg::Vec2d start, osg::Vec2d dir ) 154 | { 155 | if( dir.length2() == 0. ) 156 | // No motion 157 | return; 158 | 159 | if( _mode == FIRST_PERSON ) { 160 | // Position is constant in 1st person view. Obtain it (for later use) 161 | // *before* we alter the _viewDir. 162 | const osg::Vec3d position = getEyePosition(); 163 | 164 | // Compute rotation matrix. 165 | osg::Vec3d cross = _viewDir ^ _viewUp; 166 | osg::Matrix m = osg::Matrix::rotate( dir[ 0 ], _viewUp ) * 167 | osg::Matrix::rotate( -dir[ 1 ], cross ); 168 | 169 | // Re-orient the basis. 170 | _viewDir = _viewDir * m; 171 | _viewUp = _viewUp * m; 172 | // Orthonormalize. 173 | cross = _viewDir ^ _viewUp; 174 | _viewUp = cross ^ _viewDir; 175 | _viewDir.normalize(); 176 | _viewUp.normalize(); 177 | 178 | // Compute the new view center. 179 | _viewCenter = position + ( _viewDir * _viewDistance ); 180 | } 181 | 182 | else { // THIRD_PERSON 183 | const osg::Matrixd orientMat = getOrientationMatrix(); 184 | 185 | // Take the spin direction 'dir' and rotate it 90 degrees 186 | // to get our base axis (still in the window plane). 187 | // Simultaneously convert to current view space. 188 | osg::Vec2d screenAxis( -dir[ 1 ], dir[ 0 ] ); 189 | const osg::Vec3d baseAxis = osg::Vec3d( screenAxis[ 0 ], screenAxis[ 1 ], 0. ) * orientMat; 190 | osg::Vec3d dir3 = osg::Vec3d( dir[ 0 ], dir[ 1 ], 0. ) * orientMat; 191 | dir3.normalize(); 192 | 193 | // The distance from center, along with the roll sensitivity, 194 | // tells us how much to rotate the baseAxis (ballTouchAngle) to get 195 | // the actual ballAxis. 196 | const double distance = start.length(); 197 | const double rotationDir( ( screenAxis * start > 0. ) ? -1. : 1. ); 198 | const double ballTouchAngle = rotationDir * _trackballRollSensitivity * distance; 199 | osg::Vec3d ballAxis = baseAxis * osg::Matrixd::rotate( ballTouchAngle, dir3 ); 200 | ballAxis.normalize(); 201 | 202 | osg::Matrixd m = osg::Matrixd::rotate( -( dir.length() ), ballAxis ); 203 | 204 | // Re-orient the basis. 205 | _viewDir = _viewDir * m; 206 | _viewUp = _viewUp * m; 207 | // Orthonormalize. 208 | osg::Vec3d cross = _viewDir ^ _viewUp; 209 | _viewUp = cross ^ _viewDir; 210 | _viewDir.normalize(); 211 | _viewUp.normalize(); 212 | } 213 | } 214 | 215 | void ViewingCore::getZNearZFarProj(double &zNear, double &zFar, osg::Matrixd &projMat) 216 | { 217 | projMat = computeProjection(); 218 | 219 | if( getOrtho() ) { 220 | double l, r, b, t; 221 | projMat.getOrtho( l, r, b, t, zNear, zFar ); 222 | } else { 223 | double fovy, aspect; 224 | projMat.getPerspective( fovy, aspect, zNear, zFar ); 225 | } 226 | } 227 | 228 | void ViewingCore::pan( const double ndcX, const double ndcY ) 229 | { 230 | // Get the view volume far plane value, and the distance from 231 | // the near to far plane. 232 | double zNear, zFar; 233 | osg::Matrixd p; 234 | 235 | getZNearZFarProj(zNear, zFar, p); 236 | 237 | const double distance = zFar - zNear; 238 | 239 | // Create two points, both in NDC space, and lying on the far plane at the back 240 | // of the view volume. One is the xy origin, the other with the passed xy parameters. 241 | osg::Vec4d farPoint0 = osg::Vec4d( 0., 0., 1., 1. ); 242 | osg::Vec4d farPoint1 = osg::Vec4d( ndcX, ndcY, 1., 1. ); 243 | if( !getOrtho() ) { 244 | // Not ortho, so w != 1.0. Multiply by the far plane distance. 245 | // This yields values in clip coordinates. 246 | farPoint0 *= zFar; 247 | farPoint1 *= zFar; 248 | } 249 | 250 | // Get inverse view & proj matrices to back-transform the 251 | // two clip coord far points into world space. 252 | osg::Matrixd v = getMatrix(); 253 | p.invert( p ); 254 | osg::Vec4d wc0 = farPoint0 * p * v; 255 | osg::Vec4d wc1 = farPoint1 * p * v; 256 | 257 | // Intersect the two world coord points with the pan plane. 258 | osg::Vec3d result0, result1; 259 | osg::Vec3d p1( wc0.x(), wc0.y(), wc0.z() ); 260 | osg::Vec3d p0 = getOrtho() ? p1 - ( _viewDir * distance ) : getEyePosition(); 261 | intersectPlaneRay( result0, _panPlane, p0, p1 ); 262 | p1 = osg::Vec3d( wc1.x(), wc1.y(), wc1.z() ); 263 | p0 = getOrtho() ? p1 - ( _viewDir * distance ) : getEyePosition(); 264 | intersectPlaneRay( result1, _panPlane, p0, p1 ); 265 | 266 | // Subtract the two plane intersection points to get the delta world coord 267 | // motion and move the view center accordingly. 268 | osg::Vec3d delta = result1 - result0; 269 | osg::notify( osg::DEBUG_FP ) << " delta " << delta << std::endl; 270 | _viewCenter -= delta; 271 | } 272 | 273 | osg::Vec3d ViewingCore::findDeltaOnPanPlane(double ndcX1, double ndcY1, double ndcX2, double ndcY2) 274 | { 275 | // Get the view volume far plane value, and the distance from 276 | // the near to far plane. 277 | double zNear, zFar; 278 | osg::Matrixd p = computeProjection(); 279 | if( getOrtho() ) { 280 | double l, r, b, t; 281 | p.getOrtho( l, r, b, t, zNear, zFar ); 282 | } else { 283 | double fovy, aspect; 284 | p.getPerspective( fovy, aspect, zNear, zFar ); 285 | } 286 | const double distance = zFar - zNear; 287 | 288 | // Create two points, both in NDC space, and lying on the far plane at the back 289 | // of the view volume. One is the xy origin, the other with the passed xy parameters. 290 | osg::Vec4d farPoint0 = osg::Vec4d( ndcX1, ndcY1, 1., 1. ); 291 | osg::Vec4d farPoint1 = osg::Vec4d( ndcX2, ndcY2, 1., 1. ); 292 | if( !getOrtho() ) { 293 | // Not ortho, so w != 1.0. Multiply by the far plane distance. 294 | // This yields values in clip coordinates. 295 | farPoint0 *= zFar; 296 | farPoint1 *= zFar; 297 | } 298 | 299 | // Get inverse view & proj matrices to back-transform the 300 | // two clip coord far points into world space. 301 | osg::Matrixd v = getMatrix(); 302 | p.invert( p ); 303 | osg::Vec4d wc0 = farPoint0 * p * v; 304 | osg::Vec4d wc1 = farPoint1 * p * v; 305 | 306 | // Intersect the two world coord points with the pan plane. 307 | osg::Vec3d result0, result1; 308 | osg::Vec3d p1( wc0.x(), wc0.y(), wc0.z() ); 309 | osg::Vec3d p0 = getOrtho() ? p1 - ( _viewDir * distance ) : getEyePosition(); 310 | intersectPlaneRay( result0, _panPlane, p0, p1 ); 311 | p1 = osg::Vec3d( wc1.x(), wc1.y(), wc1.z() ); 312 | p0 = getOrtho() ? p1 - ( _viewDir * distance ) : getEyePosition(); 313 | intersectPlaneRay( result1, _panPlane, p0, p1 ); 314 | 315 | // Subtract the two plane intersection points to get the delta world coord 316 | // motion return 317 | return result1 - result0; 318 | } 319 | 320 | void ViewingCore::dolly( const double deltaMovement ) 321 | { 322 | if( getOrtho() ) 323 | // No dolly in ortho mode 324 | return; 325 | 326 | double scale( 1.0 ); 327 | if( !( _scene.valid() ) ) { 328 | osg::notify( osg::WARN ) << "ViewingCore::dolly: _scene == NULL." << std::endl; 329 | } else { 330 | // Scale based on model size. TBD this should be under 331 | // app control so that it can be disabled if desired. 332 | const osg::BoundingSphere& bs = _scene->getBound(); 333 | scale = bs._radius * .5; 334 | if( _viewDistance > bs._radius ) 335 | scale *= ( _viewDistance / bs._radius ); 336 | } 337 | 338 | _viewDistance += ( deltaMovement * scale ); 339 | if( _viewDistance < 0. ) { 340 | double centerDistanceToMove = 1 - _viewDistance; 341 | _viewCenter = _viewCenter + (_viewDir * centerDistanceToMove); 342 | _viewDistance = 1.; 343 | } 344 | } 345 | 346 | 347 | void ViewingCore::setPanStart( const double ndcX, const double ndcY ) 348 | { 349 | osg::Matrixd p = computeProjection(); 350 | 351 | // Assume ortho, where ndc far plane == 1 and w always == 1. 352 | osg::Vec4d farPoint = osg::Vec4d( ndcX, ndcY, 1., 1. ); 353 | if( !getOrtho() ) { 354 | // Not ortho, so w != 1.0. Multiply by the far plane distance. 355 | // This yields a value in clip coords. 356 | double fovy, aspect, zNear, zFar; 357 | p.getPerspective( fovy, aspect, zNear, zFar ); 358 | farPoint *= zFar; 359 | } 360 | 361 | // Get inverse view & proj matrices to back-transform the clip coord far point. 362 | osg::Matrixd v = getMatrix(); 363 | p.invert( p ); 364 | 365 | osg::Vec4d wc = farPoint * p * v; 366 | osg::Vec3d wcFarPoint( wc.x(), wc.y(), wc.z() ); 367 | 368 | // Define world coord plane orthogonal to view, which contains the picked point. 369 | osg::Vec3d pickPoint; 370 | if( !( intersect( pickPoint, wcFarPoint ) ) ) { 371 | // Intersection failed, probably user clicked on background. 372 | // Use _viewCenter to compute plane distance value. 373 | pickPoint = _viewCenter; 374 | osg::notify( osg::DEBUG_FP ) << "Intersection failed. "; 375 | } 376 | 377 | _panPlane = osg::Vec4d( _viewDir, -( pickPoint * _viewDir ) ); 378 | osg::notify( osg::DEBUG_FP ) << "Pick point " << pickPoint << std::endl; 379 | osg::notify( osg::DEBUG_FP ) << " Plane " << _panPlane << std::endl; 380 | } 381 | 382 | void ViewingCore::setViewingCoreMode( ViewingCoreMode mode ) 383 | { 384 | if( _mode == mode ) 385 | return; 386 | _mode = mode; 387 | } 388 | 389 | void ViewingCore::pickCenter( const double ndcX, const double ndcY ) 390 | { 391 | // Preserve the view direction. 392 | const osg::Vec3d lastPosition = getEyePosition(); 393 | 394 | osg::Matrixd p = computeProjection(); 395 | 396 | osg::Vec4d ccFarPoint( ndcX, ndcY, 1., 1. ); 397 | if( !getOrtho() ) { 398 | // Not ortho, so w != 1.0. Multiply by the far plane distance. 399 | // This yields a value in clip coords. 400 | double fovy, aspect, zNear, zFar; 401 | p.getPerspective( fovy, aspect, zNear, zFar ); 402 | ccFarPoint *= zFar; 403 | } 404 | 405 | // Get inverse view & proj matrices to back-transform the clip coord point. 406 | osg::Matrixd v = getMatrix(); 407 | p.invert( p ); 408 | 409 | osg::Vec4d wc = ccFarPoint * p * v; 410 | osg::Vec3d farPoint( wc.x(), wc.y(), wc.z() ); 411 | 412 | if( !( intersect( _viewCenter, farPoint ) ) ) 413 | osg::notify( osg::WARN ) << "ViewingCore::pickCenter: No intersections." << std::endl; 414 | 415 | _viewDistance = ( lastPosition - _viewCenter ).length(); 416 | } 417 | 418 | 419 | void ViewingCore::setTrackballRollSensitivity( double sollSensitivity ) 420 | { 421 | _trackballRollSensitivity = sollSensitivity; 422 | osg::clampBetween< double >( _trackballRollSensitivity, 0., osg::PI_2 ); 423 | } 424 | 425 | void ViewingCore::setBaseUpNorth( const osg::Vec3d& up, const osg::Vec3d& north ) 426 | { 427 | _baseUp = up; 428 | _baseNorth = north; 429 | _baseUp.normalize(); 430 | _baseNorth.normalize(); 431 | 432 | if( osg::absolute< double >( _baseUp * _baseNorth ) > 0.01 ) 433 | osg::notify( osg::WARN ) << "ViewingCore::setBaseUpNorth: Vectors are not orthogonal. Results are undefined." << std::endl; 434 | } 435 | void ViewingCore::getYawPitchRoll( double& yaw, double& pitch, double& roll, bool rightHanded ) const 436 | { 437 | // Temp var for cross products. 438 | osg::Vec3d right; 439 | 440 | const osg::Vec3d viewDirXBaseUp( _viewDir ^ _baseUp ); 441 | const double twoPi( 2. * osg::PI ); 442 | 443 | 444 | // Yaw 445 | 446 | // Compute view direction, projected into plane defined by base up. 447 | // TBD what if _viewDir and _baseUp are coincident? 448 | osg::Vec3d projectedDir = _baseUp ^ viewDirXBaseUp; 449 | projectedDir.normalize(); 450 | // Is the vector pointing to the left of north, or to the right? 451 | right = _baseNorth ^ _baseUp; 452 | const double dotDirRight = projectedDir * right; 453 | // Dot product of two unit vectors is the cosine of the angle between them. 454 | const double dotDirNorth = projectedDir * _baseNorth; 455 | double yawRad = acos( dotDirNorth ); 456 | if( dotDirRight > 0. ) 457 | yawRad = osg::PI + ( osg::PI - yawRad ); 458 | if( !rightHanded ) 459 | yawRad = twoPi - yawRad; 460 | if( yawRad == twoPi ) 461 | yawRad = 0.; 462 | yaw = osg::RadiansToDegrees( yawRad ); 463 | 464 | 465 | // Pitch 466 | 467 | const double dotDirUp = _viewDir * _baseUp; 468 | const double dotUpUp = _viewUp * _baseUp; 469 | double pitchRad = acos( osg::absolute< double >( dotUpUp ) ); 470 | if( dotDirUp < 0. ) 471 | pitchRad *= -1.; 472 | pitch = osg::RadiansToDegrees( pitchRad ); 473 | 474 | 475 | // Roll 476 | 477 | // Compute base up projected onto plane defined by view direction. 478 | // TBD what if _viewDir and _baseUp are coincident? 479 | osg::Vec3d projectedBaseUp = viewDirXBaseUp ^ _viewDir; 480 | projectedBaseUp.normalize(); 481 | // Is the view up vector pointing to the left of the projected base up, or to the right? 482 | right = _viewDir ^ projectedBaseUp; 483 | const double dotUpRight = _viewUp * right; 484 | // Dot product of two unit vectors is the cosine of the angle between them. 485 | const double dotUp = projectedBaseUp * _viewUp; 486 | double rollRad = acos( dotUp ); 487 | if( dotUpRight > 0. ) 488 | rollRad = osg::PI + ( osg::PI - rollRad ); 489 | if( !rightHanded ) 490 | rollRad = twoPi - rollRad; 491 | if( rollRad == twoPi ) 492 | rollRad = 0.; 493 | roll = osg::RadiansToDegrees( rollRad ); 494 | } 495 | 496 | 497 | // 498 | // Projection / FOB support 499 | // 500 | 501 | void ViewingCore::setOrtho( bool ortho ) 502 | { 503 | _ortho = ortho; 504 | /*if( _ortho ) 505 | { 506 | if( !( _scene.valid() ) ) 507 | { 508 | osg::notify( osg::WARN ) << "ViewingCore::setOrtho: _scene == NULL." << std::endl; 509 | _orthoTop = 1.0; 510 | _orthoBottom = -_orthoTop; 511 | return; 512 | } 513 | 514 | // tan (fovy/2) = a / e2c.len 515 | _orthoTop = tan( getFovyRadians() * .5 ) * _viewDistance; 516 | _orthoBottom = -_orthoTop; 517 | }*/ 518 | } 519 | 520 | void ViewingCore::updateFovy( osg::Matrixd& proj ) const 521 | { 522 | if( _ortho ) { 523 | osg::notify( osg::WARN ) << "ViewingCore::updateFovy: Ortho is not yet implemented. TBD." << std::endl; 524 | } else { 525 | double left, right, bottom, top, near, far; 526 | proj.getFrustum( left, right, bottom, top, near, far ); 527 | 528 | //const double fovLeft = atan( left / near ); 529 | //const double fovRight = atan( right / near ); 530 | const double fovBottom = atan( bottom / near ); 531 | const double fovTop = atan( top / near ); 532 | 533 | const double fovyRatio = getFovyRadians() / 534 | ( osg::absolute< double >( fovBottom ) + osg::absolute< double >( fovTop ) ); 535 | 536 | const double newBottom = tan( fovBottom * fovyRatio ) * near; 537 | const double newTop = tan( fovTop * fovyRatio ) * near; 538 | const double xScale = newTop / top; 539 | left *= xScale; 540 | right *= xScale; 541 | proj = osg::Matrixd::frustum( left, right, newBottom, newTop, near, far ); 542 | } 543 | } 544 | osg::Matrixd ViewingCore::computeProjection() const 545 | { 546 | if( !( _scene.valid() ) ) { 547 | osg::notify( osg::WARN ) << "ViewingCore::computeProjection: _scene == NULL." << std::endl; 548 | return( osg::Matrixd::identity() ); 549 | } 550 | 551 | // TBD do we really want eyeToCenter to be a vector 552 | // to the *bound* center, or to the *view* center? 553 | const osg::BoundingSphere& bs = _scene->getBound(); 554 | const osg::Vec3d eyeToCenter( bs._center - getEyePosition() ); 555 | if( _ortho ) { 556 | double zNear = eyeToCenter.length() - bs._radius; 557 | double zFar = eyeToCenter.length() + bs._radius; 558 | 559 | const double xRange = _aspect * ( _orthoTop - _orthoBottom ); 560 | const double right = xRange * .5; 561 | 562 | return( osg::Matrixd::ortho( -right, right, _orthoBottom, _orthoTop, zNear, zFar ) ); 563 | } else { 564 | double zNear = eyeToCenter.length() - bs._radius; 565 | double zFar = zNear + ( bs._radius * 2. ); 566 | if( zNear < 0. ) { 567 | zNear = zFar / 2000.; // Default z ratio. 568 | } 569 | return( osg::Matrixd::perspective( _fovy, _aspect, zNear, zFar ) ); 570 | } 571 | } 572 | 573 | void ViewingCore::setFovy( double fovy ) 574 | { 575 | const double ratio = fovy / _fovy; 576 | _orthoBottom *= ratio; 577 | _orthoTop *= ratio; 578 | _fovy = fovy; 579 | } 580 | double ViewingCore::getFovyRadians() const 581 | { 582 | return( osg::DegreesToRadians( _fovy ) ); 583 | } 584 | void ViewingCore::fovyScaleUp() 585 | { 586 | _fovy *= _fovyScale; 587 | if( _clampFovyScale ) { 588 | _fovy = osg::clampBelow< double >( _fovy, _clampFovyRange.y() ); 589 | } 590 | 591 | _orthoBottom *= _fovyScale; 592 | _orthoTop *= _fovyScale; 593 | } 594 | void ViewingCore::fovyScaleDown() 595 | { 596 | const double factor( 1.0 / _fovyScale ); 597 | _fovy *= factor; 598 | if( _clampFovyScale ) { 599 | _fovy = osg::clampAbove< double >( _fovy, _clampFovyRange.x() ); 600 | } 601 | 602 | _orthoBottom *= factor; 603 | _orthoTop *= factor; 604 | } 605 | void ViewingCore::setClampFovyScale( bool clamp, osg::Vec2d clampFovyRange ) 606 | { 607 | _clampFovyScale = clamp; 608 | _clampFovyRange = clampFovyRange; 609 | if( _clampFovyScale ) { 610 | _fovy = osg::clampBetween< double >( _fovy, _clampFovyRange.x(), _clampFovyRange.y() ); 611 | } 612 | } 613 | 614 | 615 | bool ViewingCore::intersect( osg::Vec3d& result, const osg::Vec3d& farPoint ) 616 | { 617 | if( !( _scene.valid() ) ) { 618 | osg::notify( osg::WARN ) << "ViewingCore::intersect: _scene == NULL." << std::endl; 619 | return( false ); 620 | } 621 | 622 | const osg::BoundingSphere& bs = _scene->getBound(); 623 | const double distance = _viewDistance + bs._radius; 624 | 625 | osg::Vec3d startPoint = getOrtho() ? farPoint - ( _viewDir * distance * 2. ) : getEyePosition(); 626 | osgUtil::LineSegmentIntersector* intersector = new osgUtil::LineSegmentIntersector( 627 | startPoint, farPoint ); 628 | osgUtil::IntersectionVisitor intersectVisitor( intersector, NULL ); 629 | _scene->accept( intersectVisitor ); 630 | 631 | osgUtil::LineSegmentIntersector::Intersections& intersections = intersector->getIntersections(); 632 | if( intersections.empty() ) 633 | return( false ); 634 | 635 | const osgUtil::LineSegmentIntersector::Intersection& intersection = *( intersections.begin() ); 636 | result = intersection.getWorldIntersectPoint(); 637 | return( true ); 638 | } 639 | 640 | bool ViewingCore::intersectPlaneRay( osg::Vec3d& result, const osg::Vec4d& plane, const osg::Vec3d& p0, const osg::Vec3d& p1 ) 641 | { 642 | osg::Vec3d planeNormal = osg::Vec3d( plane[ 0 ], plane[ 1 ], plane[ 2 ] ); 643 | 644 | osg::notify( osg::DEBUG_FP ) << " p0 " << p0 << std::endl; 645 | osg::notify( osg::DEBUG_FP ) << " p1 " << p1 << std::endl; 646 | const osg::Vec3d vDir = p1 - p0; 647 | const double dotVd = vDir * planeNormal; 648 | osg::notify( osg::DEBUG_FP ) << " dotVd " << dotVd << std::endl; 649 | if( dotVd == 0. ) { 650 | osg::notify( osg::WARN ) << "ViewingCore::intersectPlaneRay: No plane intersection." << std::endl; 651 | return( false ); 652 | } 653 | double length = -( planeNormal * p0 + plane[ 3 ] ) / dotVd; 654 | osg::notify( osg::DEBUG_FP ) << " length " << length << std::endl; 655 | result = p0 + ( vDir * length ); 656 | osg::notify( osg::DEBUG_FP ) << " intersection point " << result << std::endl; 657 | return( true ); 658 | } 659 | 660 | void ViewingCore::viewTop() 661 | { 662 | computeInitialView(); 663 | _viewUp = osg::Vec3d(0., 1., 0.); 664 | _viewDir = osg::Vec3d(0., 0., -1.); 665 | } 666 | 667 | void ViewingCore::viewBottom() 668 | { 669 | computeInitialView(); 670 | _viewUp = osg::Vec3d(0., -1., 0.); 671 | _viewDir = osg::Vec3d(0., 0., 1.); 672 | } 673 | 674 | void ViewingCore::viewRight() 675 | { 676 | computeInitialView(); 677 | _viewUp = osg::Vec3d(0., 0., 1.); 678 | _viewDir = osg::Vec3d(0., 1., 0.); 679 | } 680 | 681 | void ViewingCore::viewLeft() 682 | { 683 | computeInitialView(); 684 | _viewUp = osg::Vec3d(0., 0., 1.); 685 | _viewDir = osg::Vec3d(0., -1., 0.); 686 | } 687 | 688 | void ViewingCore::viewFront() 689 | { 690 | computeInitialView(); 691 | _viewUp = osg::Vec3d(0., 0., 1.); 692 | _viewDir = osg::Vec3d(-1., 0., 0.); 693 | } 694 | 695 | void ViewingCore::viewBack() 696 | { 697 | computeInitialView(); 698 | _viewUp = osg::Vec3d(0., 0., 1.); 699 | _viewDir = osg::Vec3d(1., 0., 0.); 700 | } 701 | 702 | void ViewingCore::saveView(std::stringstream &stream) 703 | { 704 | stream << _viewUp.x() << " " << _viewUp.y() << " " << _viewUp.z() << " "; 705 | stream << _viewDir.x() << " " << _viewDir.y() << " " << _viewDir.z() << " "; 706 | stream << _viewCenter.x() << " " << _viewCenter.y() << " " << _viewCenter.z() << " "; 707 | stream << _viewDistance << " "; 708 | stream << _panPlane.x() << " " << _panPlane.y() << " " << _panPlane.z() << " " << _panPlane.w() << " "; 709 | 710 | if(_ortho) 711 | stream << "true" << " "; 712 | else 713 | stream << "false" << " "; 714 | 715 | stream << _orthoBottom << " "; 716 | stream << _orthoTop << " "; 717 | stream << _fovy << " "; 718 | stream << _fovyScale << " "; 719 | 720 | if(_clampFovyScale) 721 | stream << "true" << " "; 722 | else 723 | stream << "false" << " "; 724 | 725 | stream << _clampFovyRange.x() << " " << _clampFovyRange.y(); 726 | } 727 | 728 | void ViewingCore::loadView(std::stringstream &stream) 729 | { 730 | 731 | stream >> _viewUp.x() >> _viewUp.y() >> _viewUp.z(); 732 | stream >> _viewDir.x() >> _viewDir.y() >> _viewDir.z(); 733 | stream >> _viewCenter.x() >> _viewCenter.y() >> _viewCenter.z(); 734 | stream >> _viewDistance; 735 | stream >> _panPlane.x() >> _panPlane.y() >> _panPlane.z() >> _panPlane.w(); 736 | 737 | std::string ortho; 738 | stream >> ortho; 739 | if(ortho == "true") 740 | _ortho = true; 741 | else 742 | _ortho = false; 743 | 744 | stream >> _orthoBottom; 745 | stream >> _orthoTop; 746 | stream >> _fovy; 747 | stream >> _fovyScale; 748 | 749 | std::string clampFovyScale; 750 | stream >> clampFovyScale; 751 | if(clampFovyScale == "true") 752 | _clampFovyScale = true; 753 | else 754 | _clampFovyScale = false; 755 | 756 | stream >> _clampFovyRange.x() >> _clampFovyRange.y(); 757 | } 758 | --------------------------------------------------------------------------------